| /* |
| * Copyright (C) 2018 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #ifndef SRC_TRACE_PROCESSOR_SQLITE_SQLITE_TABLE_H_ |
| #define SRC_TRACE_PROCESSOR_SQLITE_SQLITE_TABLE_H_ |
| |
| #include <sqlite3.h> |
| |
| #include <functional> |
| #include <limits> |
| #include <memory> |
| #include <optional> |
| #include <string> |
| #include <vector> |
| |
| #include "perfetto/base/status.h" |
| #include "perfetto/ext/base/flat_hash_map.h" |
| #include "perfetto/ext/base/status_or.h" |
| #include "perfetto/ext/base/utils.h" |
| #include "perfetto/trace_processor/basic_types.h" |
| #include "src/trace_processor/db/table.h" |
| #include "src/trace_processor/sqlite/query_constraints.h" |
| |
| namespace perfetto { |
| namespace trace_processor { |
| |
| class SqliteEngine; |
| class TypedSqliteTableBase; |
| |
| // Abstract base class representing a SQLite virtual table. Implements the |
| // common bookeeping required across all tables and allows subclasses to |
| // implement a friendlier API than that required by SQLite. |
| class SqliteTable : public sqlite3_vtab { |
| public: |
| // Custom opcodes used by subclasses of SqliteTable. |
| // Stored here as we need a central repository of opcodes to prevent clashes |
| // between different sub-classes. |
| enum CustomFilterOpcode { |
| kSourceGeqOpCode = SQLITE_INDEX_CONSTRAINT_FUNCTION + 1, |
| }; |
| // Describes a column of this table. |
| class Column { |
| public: |
| Column(size_t idx, |
| std::string name, |
| SqlValue::Type type, |
| bool hidden = false); |
| |
| size_t index() const { return index_; } |
| const std::string& name() const { return name_; } |
| SqlValue::Type type() const { return type_; } |
| |
| bool hidden() const { return hidden_; } |
| void set_hidden(bool hidden) { hidden_ = hidden; } |
| |
| private: |
| size_t index_ = 0; |
| std::string name_; |
| SqlValue::Type type_ = SqlValue::Type::kNull; |
| bool hidden_ = false; |
| }; |
| |
| // Abstract base class representing an SQLite Cursor. Presents a friendlier |
| // API for subclasses to implement. |
| class BaseCursor : public sqlite3_vtab_cursor { |
| public: |
| // Enum for the history of calls to Filter. |
| enum class FilterHistory : uint32_t { |
| // Indicates that constraint set passed is the different to the |
| // previous Filter call. |
| kDifferent = 0, |
| |
| // Indicates that the constraint set passed is the same as the previous |
| // Filter call. |
| // This can be useful for subclasses to perform optimizations on repeated |
| // nested subqueries. |
| kSame = 1, |
| }; |
| |
| explicit BaseCursor(SqliteTable* table); |
| virtual ~BaseCursor(); |
| |
| // Methods to be implemented by derived table classes. |
| // Note: these methods are intentionally not virtual for performance |
| // reasons. As these methods are not defined, there will be compile errors |
| // thrown if any of these methods are missing. |
| |
| // Called to intialise the cursor with the constraints of the query. |
| base::Status Filter(const QueryConstraints& qc, |
| sqlite3_value**, |
| FilterHistory); |
| |
| // Called to forward the cursor to the next row in the table. |
| base::Status Next(); |
| |
| // Called to check if the cursor has reached eof. Column will be called iff |
| // this method returns true. |
| bool Eof(); |
| |
| // Used to extract the value from the column at index |N|. |
| base::Status Column(sqlite3_context* context, int N); |
| |
| SqliteTable* table() const { return table_; } |
| |
| protected: |
| BaseCursor(BaseCursor&) = delete; |
| BaseCursor& operator=(const BaseCursor&) = delete; |
| |
| BaseCursor(BaseCursor&&) noexcept = default; |
| BaseCursor& operator=(BaseCursor&&) = default; |
| |
| private: |
| SqliteTable* table_ = nullptr; |
| }; |
| |
| // The schema of the table. Created by subclasses to allow the table class to |
| // do filtering and inform SQLite about the CREATE table statement. |
| class Schema { |
| public: |
| Schema(); |
| Schema(std::vector<Column>, std::vector<size_t> primary_keys); |
| |
| // This class is explicitly copiable. |
| Schema(const Schema&); |
| Schema& operator=(const Schema& t); |
| |
| std::string ToCreateTableStmt() const; |
| |
| const std::vector<Column>& columns() const { return columns_; } |
| std::vector<Column>* mutable_columns() { return &columns_; } |
| |
| const std::vector<size_t> primary_keys() { return primary_keys_; } |
| |
| private: |
| // The names and types of the columns of the table. |
| std::vector<Column> columns_; |
| |
| // The primary keys of the table given by an offset into |columns|. |
| std::vector<size_t> primary_keys_; |
| }; |
| |
| enum TableType { |
| // A table which automatically exists in the main schema and cannot be |
| // created with CREATE VIRTUAL TABLE. |
| // Note: the name value here matches the naming in the vtable docs of |
| // SQLite. |
| kEponymousOnly, |
| |
| // A table which must be explicitly created using a CREATE VIRTUAL TABLE |
| // statement (i.e. does exist automatically). |
| kExplicitCreate, |
| }; |
| |
| // Public for unique_ptr destructor calls. |
| virtual ~SqliteTable(); |
| |
| // When set it logs all BestIndex and Filter actions on the console. |
| static bool debug; |
| |
| protected: |
| // Populated by a BestIndex call to allow subclasses to tweak SQLite's |
| // handling of sets of constraints. |
| struct BestIndexInfo { |
| // Contains bools which indicate whether SQLite should omit double checking |
| // the constraint at that index. |
| // |
| // If there are no constraints, SQLite will be told it can omit checking for |
| // the whole query. |
| std::vector<bool> sqlite_omit_constraint; |
| |
| // Indicates that SQLite should not double check the result of the order by |
| // clause. |
| // |
| // If there are no order by clauses, this value will be ignored and SQLite |
| // will be told that it can omit double checking (i.e. this value will |
| // implicitly be taken to be true). |
| bool sqlite_omit_order_by = false; |
| |
| // Stores the estimated cost of this query. |
| double estimated_cost = 0; |
| |
| // Estimated row count. |
| int64_t estimated_rows = 0; |
| }; |
| |
| SqliteTable(); |
| |
| // Methods to be implemented by derived table classes. |
| virtual base::Status Init(int argc, const char* const* argv, Schema*) = 0; |
| virtual std::unique_ptr<BaseCursor> CreateCursor() = 0; |
| virtual int BestIndex(const QueryConstraints& qc, BestIndexInfo* info) = 0; |
| |
| // Optional metods to implement. |
| using FindFunctionFn = void (*)(sqlite3_context*, int, sqlite3_value**); |
| virtual base::Status ModifyConstraints(QueryConstraints* qc); |
| virtual int FindFunction(const char* name, FindFunctionFn* fn, void** args); |
| |
| // At registration time, the function should also pass true for |read_write|. |
| virtual base::Status Update(int, sqlite3_value**, sqlite3_int64*); |
| |
| bool ReadConstraints(int idxNum, const char* idxStr, int argc); |
| |
| const Schema& schema() const { return schema_; } |
| const std::string& module_name() const { return module_name_; } |
| const std::string& name() const { return name_; } |
| |
| private: |
| template <typename, typename> |
| friend class TypedSqliteTable; |
| friend class TypedSqliteTableBase; |
| |
| SqliteTable(const SqliteTable&) = delete; |
| SqliteTable& operator=(const SqliteTable&) = delete; |
| |
| // The engine class this table is registered with. Used for restoring/saving |
| // the table. |
| SqliteEngine* engine_ = nullptr; |
| |
| // This name of the table. For tables created using CREATE VIRTUAL TABLE, this |
| // will be the name of the table specified by the query. For automatically |
| // created tables, this will be the same as the module name passed to |
| // RegisterTable. |
| std::string name_; |
| |
| // The module name is the name passed to RegisterTable. This is differs from |
| // the table name (|name_|) where the table was created using CREATE VIRTUAL |
| // TABLE. |
| std::string module_name_; |
| |
| Schema schema_; |
| |
| QueryConstraints qc_cache_; |
| int qc_hash_ = 0; |
| int best_index_num_ = 0; |
| }; |
| |
| class TypedSqliteTableBase : public SqliteTable { |
| protected: |
| struct BaseModuleArg { |
| sqlite3_module module; |
| SqliteEngine* engine; |
| TableType table_type; |
| }; |
| |
| ~TypedSqliteTableBase() override; |
| |
| static int xDestroy(sqlite3_vtab*); |
| static int xDestroyFatal(sqlite3_vtab*); |
| |
| static int xConnectRestoreTable(sqlite3* xdb, |
| void* arg, |
| int argc, |
| const char* const* argv, |
| sqlite3_vtab** tab, |
| char** pzErr); |
| static int xDisconnectSaveTable(sqlite3_vtab*); |
| |
| static int xOpen(sqlite3_vtab*, sqlite3_vtab_cursor**); |
| static int xBestIndex(sqlite3_vtab*, sqlite3_index_info*); |
| |
| static base::Status DeclareAndAssignVtab(std::unique_ptr<SqliteTable> table, |
| sqlite3_vtab** tab); |
| |
| base::Status InitInternal(SqliteEngine* engine, |
| int argc, |
| const char* const* argv); |
| |
| int SetStatusAndReturn(base::Status status) { |
| if (!status.ok()) { |
| sqlite3_free(zErrMsg); |
| zErrMsg = sqlite3_mprintf("%s", status.c_message()); |
| return SQLITE_ERROR; |
| } |
| return SQLITE_OK; |
| } |
| }; |
| |
| template <typename SubTable, typename Context> |
| class TypedSqliteTable : public TypedSqliteTableBase { |
| public: |
| struct ModuleArg : public BaseModuleArg { |
| Context context; |
| }; |
| |
| static std::unique_ptr<ModuleArg> CreateModuleArg(SqliteEngine* engine, |
| Context ctx, |
| TableType table_type, |
| bool updatable) { |
| auto arg = std::make_unique<ModuleArg>(); |
| arg->module = CreateModule(table_type, updatable); |
| arg->engine = engine; |
| arg->table_type = table_type; |
| arg->context = std::move(ctx); |
| return arg; |
| } |
| |
| private: |
| static constexpr sqlite3_module CreateModule(TableType table_type, |
| bool updatable) { |
| sqlite3_module module; |
| memset(&module, 0, sizeof(sqlite3_module)); |
| switch (table_type) { |
| case TableType::kEponymousOnly: |
| // Neither xCreate nor xDestroy should ever be called for |
| // eponymous-only tables. |
| module.xCreate = nullptr; |
| module.xDestroy = &xDestroyFatal; |
| |
| // xConnect and xDisconnect will automatically be called with |
| // |module_name| == |name|. |
| module.xConnect = &xCreate; |
| module.xDisconnect = &xDestroy; |
| break; |
| case TableType::kExplicitCreate: |
| // xConnect and xDestroy will be called when the table is CREATE-ed and |
| // DROP-ed respectively. |
| module.xCreate = &xCreate; |
| module.xDestroy = &xDestroy; |
| |
| // xConnect and xDisconnect can be called at any time. |
| module.xConnect = &xConnectRestoreTable; |
| module.xDisconnect = &xDisconnectSaveTable; |
| break; |
| } |
| module.xOpen = &xOpen; |
| module.xClose = &xClose; |
| module.xBestIndex = &xBestIndex; |
| module.xFindFunction = &xFindFunction; |
| module.xFilter = &xFilter; |
| module.xNext = &xNext; |
| module.xEof = &xEof; |
| module.xColumn = &xColumn; |
| module.xRowid = &xRowid; |
| if (updatable) { |
| module.xUpdate = &xUpdate; |
| } |
| return module; |
| } |
| |
| static int xCreate(sqlite3* xdb, |
| void* arg, |
| int argc, |
| const char* const* argv, |
| sqlite3_vtab** tab, |
| char** pzErr) { |
| auto* xdesc = static_cast<ModuleArg*>(arg); |
| std::unique_ptr<SubTable> table( |
| new SubTable(xdb, std::move(xdesc->context))); |
| SubTable* table_ptr = table.get(); |
| base::Status status = table->InitInternal(xdesc->engine, argc, argv); |
| if (!status.ok()) { |
| *pzErr = sqlite3_mprintf("%s", status.c_message()); |
| return SQLITE_ERROR; |
| } |
| status = DeclareAndAssignVtab(std::move(table), tab); |
| if (!status.ok()) { |
| *pzErr = sqlite3_mprintf("%s", status.c_message()); |
| return SQLITE_ERROR; |
| } |
| xdesc->engine->OnSqliteTableCreated(table_ptr->name(), xdesc->table_type); |
| return SQLITE_OK; |
| } |
| static int xClose(sqlite3_vtab_cursor* c) { |
| delete static_cast<typename SubTable::Cursor*>(c); |
| return SQLITE_OK; |
| } |
| static int xFindFunction(sqlite3_vtab* t, |
| int, |
| const char* name, |
| void (**fn)(sqlite3_context*, int, sqlite3_value**), |
| void** args) { |
| return static_cast<SubTable*>(t)->FindFunction(name, fn, args); |
| } |
| static int xFilter(sqlite3_vtab_cursor* vc, |
| int i, |
| const char* s, |
| int a, |
| sqlite3_value** v) { |
| auto* cursor = static_cast<typename SubTable::Cursor*>(vc); |
| bool is_cached = cursor->table()->ReadConstraints(i, s, a); |
| auto history = is_cached ? BaseCursor::FilterHistory::kSame |
| : BaseCursor::FilterHistory::kDifferent; |
| auto* table = static_cast<SubTable*>(cursor->table()); |
| return table->SetStatusAndReturn( |
| cursor->Filter(cursor->table()->qc_cache_, v, history)); |
| } |
| static int xNext(sqlite3_vtab_cursor* c) { |
| auto* cursor = static_cast<typename SubTable::Cursor*>(c); |
| auto* table = static_cast<SubTable*>(cursor->table()); |
| return table->SetStatusAndReturn(cursor->Next()); |
| } |
| static int xEof(sqlite3_vtab_cursor* c) { |
| return static_cast<int>(static_cast<typename SubTable::Cursor*>(c)->Eof()); |
| } |
| static int xColumn(sqlite3_vtab_cursor* c, sqlite3_context* a, int b) { |
| auto* cursor = static_cast<typename SubTable::Cursor*>(c); |
| auto* table = static_cast<SubTable*>(cursor->table()); |
| return table->SetStatusAndReturn(cursor->Column(a, b)); |
| } |
| static int xRowid(sqlite3_vtab_cursor*, sqlite3_int64*) { |
| return SQLITE_ERROR; |
| } |
| static int xUpdate(sqlite3_vtab* t, |
| int a, |
| sqlite3_value** v, |
| sqlite3_int64* r) { |
| auto* table = static_cast<SubTable*>(t); |
| return table->SetStatusAndReturn(table->Update(a, v, r)); |
| } |
| }; |
| |
| } // namespace trace_processor |
| } // namespace perfetto |
| |
| #endif // SRC_TRACE_PROCESSOR_SQLITE_SQLITE_TABLE_H_ |