|  | // 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 <string> | 
|  |  | 
|  | #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<sqlite3*>(db),      // | 
|  | statememt.c_str(),                   // | 
|  | static_cast<int>(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<Handle>(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<int>(index + 1); | 
|  | } | 
|  |  | 
|  | static constexpr int ToColumn(size_t index) { | 
|  | /* | 
|  | *  sqlite columns begin from 0 | 
|  | */ | 
|  | return static_cast<int>(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<int>(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<int>(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<const char*>( | 
|  | ::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<const uint8_t*>( | 
|  | ::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 |