// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "impeller/archivist/archive_statement.h" #include #include "flutter/fml/logging.h" #include "third_party/sqlite/sqlite3.h" namespace impeller { struct ArchiveStatement::Handle { Handle(void* db, const std::string& statememt) { if (db == nullptr) { return; } ::sqlite3_stmt* handle = nullptr; if (::sqlite3_prepare_v2(reinterpret_cast(db), // statememt.c_str(), // static_cast(statememt.size()), // &handle, // nullptr) == SQLITE_OK) { handle_ = handle; } } ~Handle() { if (handle_ == nullptr) { return; } auto res = ::sqlite3_finalize(handle_); FML_CHECK(res == SQLITE_OK) << "Unable to finalize the archive."; } bool IsValid() const { return handle_ != nullptr; } ::sqlite3_stmt* Get() const { return handle_; } private: ::sqlite3_stmt* handle_ = nullptr; FML_DISALLOW_COPY_AND_ASSIGN(Handle); }; ArchiveStatement::ArchiveStatement(void* db, const std::string& statememt) : statement_handle_(std::make_unique(db, statememt)) { if (!statement_handle_->IsValid()) { statement_handle_.reset(); } } ArchiveStatement::~ArchiveStatement() = default; bool ArchiveStatement::IsValid() const { return statement_handle_ != nullptr; } bool ArchiveStatement::Reset() { if (!IsValid()) { return false; } if (::sqlite3_reset(statement_handle_->Get()) != SQLITE_OK) { return false; } if (::sqlite3_clear_bindings(statement_handle_->Get()) != SQLITE_OK) { return false; } return true; } static constexpr int ToParam(size_t index) { /* * sqlite parameters begin from 1 */ return static_cast(index + 1); } static constexpr int ToColumn(size_t index) { /* * sqlite columns begin from 0 */ return static_cast(index); } size_t ArchiveStatement::GetColumnCount() { if (!IsValid()) { return 0u; } return ::sqlite3_column_count(statement_handle_->Get()); } /* * Bind Variants */ bool ArchiveStatement::WriteValue(size_t index, const std::string& item) { if (!IsValid()) { return false; } return ::sqlite3_bind_text(statement_handle_->Get(), // ToParam(index), // item.data(), // static_cast(item.size()), // SQLITE_TRANSIENT) == SQLITE_OK; } bool ArchiveStatement::BindIntegral(size_t index, int64_t item) { if (!IsValid()) { return false; } return ::sqlite3_bind_int64(statement_handle_->Get(), // ToParam(index), // item) == SQLITE_OK; } bool ArchiveStatement::WriteValue(size_t index, double item) { if (!IsValid()) { return false; } return ::sqlite3_bind_double(statement_handle_->Get(), // ToParam(index), // item) == SQLITE_OK; } bool ArchiveStatement::WriteValue(size_t index, const Allocation& item) { if (!IsValid()) { return false; } return ::sqlite3_bind_blob(statement_handle_->Get(), // ToParam(index), // item.GetBuffer(), // static_cast(item.GetLength()), // SQLITE_TRANSIENT) == SQLITE_OK; } /* * Column Variants */ bool ArchiveStatement::ColumnIntegral(size_t index, int64_t& item) { if (!IsValid()) { return false; } item = ::sqlite3_column_int64(statement_handle_->Get(), ToColumn(index)); return true; } bool ArchiveStatement::ReadValue(size_t index, double& item) { if (!IsValid()) { return false; } item = ::sqlite3_column_double(statement_handle_->Get(), ToColumn(index)); return true; } /* * For cases where byte sizes of column data is necessary, the * recommendations in https://www.sqlite.org/c3ref/column_blob.html regarding * type conversions are followed. * * TL;DR: Access blobs then bytes. */ bool ArchiveStatement::ReadValue(size_t index, std::string& item) { if (!IsValid()) { return false; } /* * Get the character data */ auto chars = reinterpret_cast( ::sqlite3_column_text(statement_handle_->Get(), ToColumn(index))); /* * Get the length of the string (in bytes) */ size_t textByteSize = ::sqlite3_column_bytes(statement_handle_->Get(), ToColumn(index)); std::string text(chars, textByteSize); item.swap(text); return true; } bool ArchiveStatement::ReadValue(size_t index, Allocation& item) { if (!IsValid()) { return false; } /* * Get a blob pointer */ auto blob = reinterpret_cast( ::sqlite3_column_blob(statement_handle_->Get(), ToColumn(index))); /* * Decode the number of bytes in the blob */ size_t byteSize = ::sqlite3_column_bytes(statement_handle_->Get(), ToColumn(index)); /* * Reszie the host allocation and move the blob contents into it */ if (!item.Truncate(byteSize, false /* npot */)) { return false; } memmove(item.GetBuffer(), blob, byteSize); return true; } ArchiveStatement::Result ArchiveStatement::Execute() { if (!IsValid()) { return Result::kFailure; } switch (::sqlite3_step(statement_handle_->Get())) { case SQLITE_DONE: return Result::kDone; case SQLITE_ROW: return Result::kRow; default: return Result::kFailure; } } } // namespace impeller