1 /*
2 * Copyright (C) 2023 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #ifndef SRC_TRACE_PROCESSOR_SQLITE_SQLITE_ENGINE_H_
18 #define SRC_TRACE_PROCESSOR_SQLITE_SQLITE_ENGINE_H_
19
20 #include <sqlite3.h>
21 #include <cstddef>
22 #include <cstdint>
23 #include <memory>
24 #include <optional>
25 #include <string>
26 #include <type_traits>
27 #include <utility>
28
29 #include "perfetto/base/logging.h"
30 #include "perfetto/base/status.h"
31 #include "perfetto/ext/base/flat_hash_map.h"
32 #include "perfetto/ext/base/hash.h"
33 #include "src/trace_processor/sqlite/bindings/sqlite_module.h"
34 #include "src/trace_processor/sqlite/scoped_db.h"
35 #include "src/trace_processor/sqlite/sql_source.h"
36
37 namespace perfetto::trace_processor {
38
39 // Wrapper class around SQLite C API.
40 //
41 // The goal of this class is to provide a one-stop-shop mechanism to use SQLite.
42 // Benefits of this include:
43 // 1) It allows us to add code which intercepts registration of functions
44 // and tables and keeps track of this for later lookup.
45 // 2) Allows easily auditing the SQLite APIs we use making it easy to determine
46 // what functionality we rely on.
47 class SqliteEngine {
48 public:
49 using Fn = void(sqlite3_context* ctx, int argc, sqlite3_value** argv);
50 using AggregateFnStep = void(sqlite3_context* ctx,
51 int argc,
52 sqlite3_value** argv);
53 using AggregateFnFinal = void(sqlite3_context* ctx);
54 using WindowFnStep = void(sqlite3_context* ctx,
55 int argc,
56 sqlite3_value** argv);
57 using WindowFnInverse = void(sqlite3_context* ctx,
58 int argc,
59 sqlite3_value** argv);
60 using WindowFnValue = void(sqlite3_context* ctx);
61 using WindowFnFinal = void(sqlite3_context* ctx);
62 using FnCtxDestructor = void(void*);
63
64 // Wrapper class for SQLite's |sqlite3_stmt| struct and associated functions.
65 struct PreparedStatement {
66 public:
67 bool Step();
68 bool IsDone() const;
69
70 const char* original_sql() const;
71 const char* sql() const;
72
statusPreparedStatement73 const base::Status& status() const { return status_; }
sqlite_stmtPreparedStatement74 sqlite3_stmt* sqlite_stmt() const { return stmt_.get(); }
75
76 private:
77 friend class SqliteEngine;
78
79 explicit PreparedStatement(ScopedStmt, SqlSource);
80
81 ScopedStmt stmt_;
82 ScopedSqliteString expanded_sql_;
83 SqlSource sql_source_;
84 base::Status status_ = base::OkStatus();
85 };
86
87 SqliteEngine();
88 ~SqliteEngine();
89
90 SqliteEngine(SqliteEngine&&) noexcept = delete;
91 SqliteEngine& operator=(SqliteEngine&&) = delete;
92
93 // Prepares a SQLite statement for the given SQL.
94 PreparedStatement PrepareStatement(SqlSource);
95
96 // Registers a C++ function to be runnable from SQL.
97 base::Status RegisterFunction(const char* name,
98 int argc,
99 Fn* fn,
100 void* ctx,
101 FnCtxDestructor* ctx_destructor,
102 bool deterministic);
103
104 // Registers a C++ aggregate function to be runnable from SQL.
105 base::Status RegisterAggregateFunction(const char* name,
106 int argc,
107 AggregateFnStep* step,
108 AggregateFnFinal* final,
109 void* ctx,
110 FnCtxDestructor* ctx_destructor,
111 bool deterministic);
112
113 // Registers a C++ window function to be runnable from SQL.
114 base::Status RegisterWindowFunction(const char* name,
115 int argc,
116 WindowFnStep* step,
117 WindowFnInverse* inverse,
118 WindowFnValue* value,
119 WindowFnFinal* final,
120 void* ctx,
121 FnCtxDestructor* ctx_destructor,
122 bool deterministic);
123
124 // Unregisters a C++ function from SQL.
125 base::Status UnregisterFunction(const char* name, int argc);
126
127 // Registers a SQLite virtual table module with the given name.
128 template <typename Module>
129 void RegisterVirtualTableModule(const std::string& module_name,
130 typename Module::Context* ctx);
131
132 // Registers a SQLite virtual table module with the given name.
133 template <typename Module>
134 void RegisterVirtualTableModule(const std::string& module_name,
135 std::unique_ptr<typename Module::Context>);
136
137 // Declares a virtual table with SQLite.
138 base::Status DeclareVirtualTable(const std::string& create_stmt);
139
140 // Gets the context for a registered SQL function.
141 void* GetFunctionContext(const std::string& name, int argc);
142
db()143 sqlite3* db() const { return db_.get(); }
144
145 private:
146 struct FnHasher {
operatorFnHasher147 size_t operator()(const std::pair<std::string, int>& x) const {
148 base::Hasher hasher;
149 hasher.Update(x.first);
150 hasher.Update(x.second);
151 return static_cast<size_t>(hasher.digest());
152 }
153 };
154
155 std::optional<uint32_t> GetErrorOffset() const;
156
157 base::FlatHashMap<std::pair<std::string, int>, void*, FnHasher> fn_ctx_;
158 ScopedDb db_;
159 };
160
161 } // namespace perfetto::trace_processor
162
163 // The rest of this file is just implementation details which we need
164 // in the header file because it is templated code. We separate it out
165 // like this to keep the API people actually care about easy to read.
166
167 namespace perfetto::trace_processor {
168
169 template <typename Module>
RegisterVirtualTableModule(const std::string & module_name,typename Module::Context * ctx)170 void SqliteEngine::RegisterVirtualTableModule(const std::string& module_name,
171 typename Module::Context* ctx) {
172 static_assert(std::is_base_of_v<sqlite::Module<Module>, Module>,
173 "Must subclass sqlite::Module");
174 int res = sqlite3_create_module_v2(db_.get(), module_name.c_str(),
175 &Module::kModule, ctx, nullptr);
176 PERFETTO_CHECK(res == SQLITE_OK);
177 }
178
179 template <typename Module>
RegisterVirtualTableModule(const std::string & module_name,std::unique_ptr<typename Module::Context> ctx)180 void SqliteEngine::RegisterVirtualTableModule(
181 const std::string& module_name,
182 std::unique_ptr<typename Module::Context> ctx) {
183 static_assert(std::is_base_of_v<sqlite::Module<Module>, Module>,
184 "Must subclass sqlite::Module");
185 int res = sqlite3_create_module_v2(
186 db_.get(), module_name.c_str(), &Module::kModule, ctx.release(),
187 [](void* arg) { delete static_cast<typename Module::Context*>(arg); });
188 PERFETTO_CHECK(res == SQLITE_OK);
189 }
190
191 } // namespace perfetto::trace_processor
192
193 #endif // SRC_TRACE_PROCESSOR_SQLITE_SQLITE_ENGINE_H_
194