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 <stdint.h>
22 #include <functional>
23 #include <memory>
24 #include <type_traits>
25
26 #include "perfetto/base/status.h"
27 #include "perfetto/ext/base/flat_hash_map.h"
28 #include "perfetto/ext/base/hash.h"
29 #include "src/trace_processor/db/table.h"
30 #include "src/trace_processor/prelude/functions/sql_function.h"
31 #include "src/trace_processor/prelude/table_functions/table_function.h"
32 #include "src/trace_processor/sqlite/query_cache.h"
33 #include "src/trace_processor/sqlite/scoped_db.h"
34 #include "src/trace_processor/sqlite/sqlite_table.h"
35 #include "src/trace_processor/sqlite/sqlite_utils.h"
36
37 namespace perfetto {
38 namespace trace_processor {
39
40 // Wrapper class around SQLite C API.
41 //
42 // The goal of this class is to provide a one-stop-shop mechanism to use SQLite.
43 // Benefits of this include:
44 // 1) It allows us to add code which intercepts registration of functions
45 // and tables and keeps track of this for later lookup.
46 // 2) Allows easily auditing the SQLite APIs we use making it easy to determine
47 // what functionality we rely on.
48 class SqliteEngine {
49 public:
50 SqliteEngine();
51 ~SqliteEngine();
52
53 // Registers a trace processor C++ table with SQLite with an SQL name of
54 // |name|.
55 void RegisterTable(const Table& table, const std::string& name);
56
57 // Registers a trace processor C++ function to be runnable from SQL.
58 //
59 // The format of the function is given by the |SqlFunction|.
60 //
61 // |db|: sqlite3 database object
62 // |name|: name of the function in SQL
63 // |argc|: number of arguments for this function. This can be -1 if
64 // the number of arguments is variable.
65 // |ctx|: context object for the function (see SqlFunction::Run
66 // above);
67 // this object *must* outlive the function so should likely be
68 // either static or scoped to the lifetime of TraceProcessor.
69 // |determistic|: whether this function has deterministic output given the
70 // same set of arguments.
71 template <typename Function = SqlFunction>
72 base::Status RegisterSqlFunction(const char* name,
73 int argc,
74 typename Function::Context* ctx,
75 bool deterministic = true);
76
77 // Registers a trace processor C++ function to be runnable from SQL.
78 //
79 // This function is the same as the above except allows a unique_ptr to be
80 // passed for the context; this allows for SQLite to manage the lifetime of
81 // this pointer instead of the essentially static requirement of the context
82 // pointer above.
83 template <typename Function>
84 base::Status RegisterSqlFunction(
85 const char* name,
86 int argc,
87 std::unique_ptr<typename Function::Context> ctx,
88 bool deterministic = true);
89
90 // Registers a trace processor C++ table function with SQLite.
91 void RegisterTableFunction(std::unique_ptr<TableFunction> fn);
92
93 // Registers a SQLite virtual table module with the given name.
94 //
95 // This API only exists for internal/legacy use: most callers should use
96 // one of the RegisterTable* APIs above.
97 template <typename Vtab, typename Context>
98 void RegisterVirtualTableModule(const std::string& module_name,
99 Context ctx,
100 SqliteTable::TableType table_type,
101 bool updatable);
102
103 // Declares a virtual table with SQLite.
104 //
105 // This API only exists for internal use. Most callers should never call this
106 // directly: instead use one of the RegisterTable* APIs above.
107 base::Status DeclareVirtualTable(const std::string& create_stmt);
108
109 // Saves a SQLite table across a pair of xDisconnect/xConnect callbacks.
110 //
111 // This API only exists for internal use. Most callers should never call this
112 // directly.
113 base::Status SaveSqliteTable(const std::string& table_name,
114 std::unique_ptr<SqliteTable>);
115
116 // Restores a SQLite table across a pair of xDisconnect/xConnect callbacks.
117 //
118 // This API only exists for internal use. Most callers should never call this
119 // directly.
120 base::StatusOr<std::unique_ptr<SqliteTable>> RestoreSqliteTable(
121 const std::string& table_name);
122
123 // Gets the context for a registered SQL function.
124 //
125 // This API only exists for internal use. Most callers should never call this
126 // directly.
127 void* GetFunctionContext(const std::string& name, int argc);
128
db()129 sqlite3* db() const { return db_.get(); }
130
131 private:
132 struct FnHasher {
operatorFnHasher133 size_t operator()(const std::pair<std::string, int>& x) const {
134 base::Hasher hasher;
135 hasher.Update(x.first);
136 hasher.Update(x.second);
137 return static_cast<size_t>(hasher.digest());
138 }
139 };
140
141 std::unique_ptr<QueryCache> query_cache_;
142 base::FlatHashMap<std::string, std::unique_ptr<SqliteTable>> saved_tables_;
143 base::FlatHashMap<std::pair<std::string, int>, void*, FnHasher> fn_ctx_;
144
145 ScopedDb db_;
146 };
147
148 } // namespace trace_processor
149 } // namespace perfetto
150
151 // The rest of this file is just implementation details which we need
152 // in the header file because it is templated code. We separate it out
153 // like this to keep the API people actually care about easy to read.
154
155 namespace perfetto {
156 namespace trace_processor {
157 namespace sqlite_internal {
158
159 // RAII type to call Function::Cleanup when destroyed.
160 template <typename Function>
161 struct ScopedCleanup {
162 typename Function::Context* ctx;
~ScopedCleanupScopedCleanup163 ~ScopedCleanup() { Function::Cleanup(ctx); }
164 };
165
166 template <typename Function>
WrapSqlFunction(sqlite3_context * ctx,int argc,sqlite3_value ** argv)167 void WrapSqlFunction(sqlite3_context* ctx, int argc, sqlite3_value** argv) {
168 using Context = typename Function::Context;
169 Context* ud = static_cast<Context*>(sqlite3_user_data(ctx));
170
171 ScopedCleanup<Function> scoped_cleanup{ud};
172 SqlValue value{};
173 SqlFunction::Destructors destructors{};
174 base::Status status =
175 Function::Run(ud, static_cast<size_t>(argc), argv, value, destructors);
176 if (!status.ok()) {
177 sqlite3_result_error(ctx, status.c_message(), -1);
178 return;
179 }
180
181 if (Function::kVoidReturn) {
182 if (!value.is_null()) {
183 sqlite3_result_error(ctx, "void SQL function returned value", -1);
184 return;
185 }
186
187 // If the function doesn't want to return anything, set the "VOID"
188 // pointer type to a non-null value. Note that because of the weird
189 // way |sqlite3_value_pointer| works, we need to set some value even
190 // if we don't actually read it - just set it to a pointer to an empty
191 // string for this reason.
192 static char kVoidValue[] = "";
193 sqlite3_result_pointer(ctx, kVoidValue, "VOID", nullptr);
194 } else {
195 sqlite_utils::ReportSqlValue(ctx, value, destructors.string_destructor,
196 destructors.bytes_destructor);
197 }
198
199 status = Function::VerifyPostConditions(ud);
200 if (!status.ok()) {
201 sqlite3_result_error(ctx, status.c_message(), -1);
202 return;
203 }
204 }
205
206 } // namespace sqlite_internal
207
208 template <typename Function>
RegisterSqlFunction(const char * name,int argc,typename Function::Context * ctx,bool deterministic)209 base::Status SqliteEngine::RegisterSqlFunction(const char* name,
210 int argc,
211 typename Function::Context* ctx,
212 bool deterministic) {
213 int flags = SQLITE_UTF8 | (deterministic ? SQLITE_DETERMINISTIC : 0);
214 int ret = sqlite3_create_function_v2(
215 db_.get(), name, static_cast<int>(argc), flags, ctx,
216 sqlite_internal::WrapSqlFunction<Function>, nullptr, nullptr, nullptr);
217 if (ret != SQLITE_OK) {
218 return base::ErrStatus("Unable to register function with name %s", name);
219 }
220 *fn_ctx_.Insert(std::make_pair(name, argc), ctx).first = ctx;
221 return base::OkStatus();
222 }
223
224 template <typename Function>
RegisterSqlFunction(const char * name,int argc,std::unique_ptr<typename Function::Context> user_data,bool deterministic)225 base::Status SqliteEngine::RegisterSqlFunction(
226 const char* name,
227 int argc,
228 std::unique_ptr<typename Function::Context> user_data,
229 bool deterministic) {
230 int flags = SQLITE_UTF8 | (deterministic ? SQLITE_DETERMINISTIC : 0);
231 void* fn_ctx = user_data.get();
232 int ret = sqlite3_create_function_v2(
233 db_.get(), name, static_cast<int>(argc), flags, user_data.release(),
234 sqlite_internal::WrapSqlFunction<Function>, nullptr, nullptr,
235 [](void* ptr) { delete static_cast<typename Function::Context*>(ptr); });
236 if (ret != SQLITE_OK) {
237 return base::ErrStatus("Unable to register function with name %s", name);
238 }
239 *fn_ctx_.Insert(std::make_pair(name, argc), fn_ctx).first = fn_ctx;
240 return base::OkStatus();
241 }
242
243 template <typename Vtab, typename Context>
RegisterVirtualTableModule(const std::string & module_name,Context ctx,SqliteTable::TableType table_type,bool updatable)244 void SqliteEngine::RegisterVirtualTableModule(const std::string& module_name,
245 Context ctx,
246 SqliteTable::TableType table_type,
247 bool updatable) {
248 static_assert(std::is_base_of_v<SqliteTable, Vtab>,
249 "Must subclass TypedSqliteTable");
250
251 auto module_arg =
252 Vtab::CreateModuleArg(this, std::move(ctx), table_type, updatable);
253 sqlite3_module* module = &module_arg->module;
254 int res = sqlite3_create_module_v2(
255 db_.get(), module_name.c_str(), module, module_arg.release(),
256 [](void* arg) { delete static_cast<typename Vtab::ModuleArg*>(arg); });
257 PERFETTO_CHECK(res == SQLITE_OK);
258 }
259
260 } // namespace trace_processor
261 } // namespace perfetto
262
263 #endif // SRC_TRACE_PROCESSOR_SQLITE_SQLITE_ENGINE_H_
264