xref: /aosp_15_r20/external/perfetto/src/trace_processor/perfetto_sql/engine/runtime_table_function.cc (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
1 /*
2  * Copyright (C) 2019 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 #include "src/trace_processor/perfetto_sql/engine/runtime_table_function.h"
18 
19 #include <sqlite3.h>
20 #include <cstddef>
21 #include <cstdint>
22 #include <memory>
23 #include <optional>
24 #include <string>
25 #include <utility>
26 #include <vector>
27 
28 #include "perfetto/base/logging.h"
29 #include "perfetto/base/status.h"
30 #include "perfetto/ext/base/string_utils.h"
31 #include "perfetto/public/compiler.h"
32 #include "src/trace_processor/perfetto_sql/engine/perfetto_sql_engine.h"
33 #include "src/trace_processor/perfetto_sql/parser/function_util.h"
34 #include "src/trace_processor/sqlite/bindings/sqlite_result.h"
35 #include "src/trace_processor/sqlite/module_lifecycle_manager.h"
36 #include "src/trace_processor/sqlite/sqlite_utils.h"
37 #include "src/trace_processor/tp_metatrace.h"
38 #include "src/trace_processor/util/sql_argument.h"
39 
40 namespace perfetto::trace_processor {
41 
42 namespace {
43 
ResetStatement(sqlite3_stmt * stmt)44 void ResetStatement(sqlite3_stmt* stmt) {
45   sqlite3_reset(stmt);
46   sqlite3_clear_bindings(stmt);
47 }
48 
CreateTableStrFromState(RuntimeTableFunctionModule::State * state)49 auto CreateTableStrFromState(RuntimeTableFunctionModule::State* state) {
50   std::vector<std::string> columns;
51   columns.reserve(state->return_values.size());
52   for (const auto& ret : state->return_values) {
53     columns.emplace_back(ret.name().ToStdString() + " " +
54                          sqlite::utils::SqlValueTypeToSqliteTypeName(
55                              sql_argument::TypeToSqlValueType(ret.type())));
56   }
57   for (const auto& arg : state->prototype.arguments) {
58     // Add the "in_" prefix to every argument param to avoid clashes between the
59     // output and input parameters.
60     columns.emplace_back("in_" + arg.name().ToStdString() + " " +
61                          sqlite::utils::SqlValueTypeToSqliteTypeName(
62                              sql_argument::TypeToSqlValueType(arg.type())) +
63                          " HIDDEN");
64   }
65   columns.emplace_back("_primary_key BIGINT HIDDEN");
66 
67   std::string cols = base::Join(columns, ",");
68   return base::StackString<1024>(
69       R"(CREATE TABLE x(%s, PRIMARY KEY(_primary_key)) WITHOUT ROWID)",
70       cols.c_str());
71 }
72 
73 }  // namespace
74 
Create(sqlite3 * db,void * ctx,int,const char * const * argv,sqlite3_vtab ** vtab,char **)75 int RuntimeTableFunctionModule::Create(sqlite3* db,
76                                        void* ctx,
77                                        int,
78                                        const char* const* argv,
79                                        sqlite3_vtab** vtab,
80                                        char**) {
81   auto* context = GetContext(ctx);
82   auto state = std::move(context->temporary_create_state);
83 
84   auto create_table_str = CreateTableStrFromState(state.get());
85   if (int ret = sqlite3_declare_vtab(db, create_table_str.c_str());
86       ret != SQLITE_OK) {
87     return ret;
88   }
89   std::unique_ptr<Vtab> res = std::make_unique<Vtab>();
90   res->reusable_stmt = std::move(state->temporary_create_stmt);
91   state->temporary_create_stmt = std::nullopt;
92   res->state = context->manager.OnCreate(argv, std::move(state));
93   *vtab = res.release();
94   return SQLITE_OK;
95 }
96 
Destroy(sqlite3_vtab * vtab)97 int RuntimeTableFunctionModule::Destroy(sqlite3_vtab* vtab) {
98   std::unique_ptr<Vtab> tab(GetVtab(vtab));
99   sqlite::ModuleStateManager<RuntimeTableFunctionModule>::OnDestroy(tab->state);
100   return SQLITE_OK;
101 }
102 
Connect(sqlite3 * db,void * ctx,int,const char * const *,sqlite3_vtab ** vtab,char ** argv)103 int RuntimeTableFunctionModule::Connect(sqlite3* db,
104                                         void* ctx,
105                                         int,
106                                         const char* const*,
107                                         sqlite3_vtab** vtab,
108                                         char** argv) {
109   auto* context = GetContext(ctx);
110 
111   std::unique_ptr<Vtab> res = std::make_unique<Vtab>();
112   res->state = context->manager.OnConnect(argv);
113 
114   auto create_table_str = CreateTableStrFromState(
115       sqlite::ModuleStateManager<RuntimeTableFunctionModule>::GetState(
116           res->state));
117   if (int ret = sqlite3_declare_vtab(db, create_table_str.c_str());
118       ret != SQLITE_OK) {
119     // If the registration happens to fail, make sure to disconnect the state
120     // again.
121     sqlite::ModuleStateManager<RuntimeTableFunctionModule>::OnDisconnect(
122         res->state);
123     return ret;
124   }
125   *vtab = res.release();
126   return SQLITE_OK;
127 }
128 
Disconnect(sqlite3_vtab * vtab)129 int RuntimeTableFunctionModule::Disconnect(sqlite3_vtab* vtab) {
130   std::unique_ptr<Vtab> tab(GetVtab(vtab));
131   sqlite::ModuleStateManager<RuntimeTableFunctionModule>::OnDisconnect(
132       tab->state);
133   return SQLITE_OK;
134 }
135 
BestIndex(sqlite3_vtab * tab,sqlite3_index_info * info)136 int RuntimeTableFunctionModule::BestIndex(sqlite3_vtab* tab,
137                                           sqlite3_index_info* info) {
138   auto* t = GetVtab(tab);
139   auto* s = sqlite::ModuleStateManager<RuntimeTableFunctionModule>::GetState(
140       t->state);
141 
142   // Don't deal with any constraints on the output parameters for simplicty.
143   // TODO(lalitm): reconsider this decision to allow more efficient queries:
144   // we would need to wrap the query in a SELECT * FROM (...) WHERE constraint
145   // like we do for SPAN JOIN.
146   base::Status status = sqlite::utils::ValidateFunctionArguments(
147       info, s->prototype.arguments.size(),
148       [s](size_t c) { return s->IsArgumentColumn(c); });
149   if (!status.ok()) {
150     return SQLITE_CONSTRAINT;
151   }
152   return SQLITE_OK;
153 }
154 
Open(sqlite3_vtab * tab,sqlite3_vtab_cursor ** cursor)155 int RuntimeTableFunctionModule::Open(sqlite3_vtab* tab,
156                                      sqlite3_vtab_cursor** cursor) {
157   auto* t = GetVtab(tab);
158   std::unique_ptr<Cursor> c = std::make_unique<Cursor>();
159   if (t->reusable_stmt) {
160     c->stmt = std::move(t->reusable_stmt);
161     t->reusable_stmt = std::nullopt;
162   }
163   *cursor = c.release();
164   return SQLITE_OK;
165 }
166 
Close(sqlite3_vtab_cursor * cursor)167 int RuntimeTableFunctionModule::Close(sqlite3_vtab_cursor* cursor) {
168   std::unique_ptr<Cursor> c(GetCursor(cursor));
169   auto* t = GetVtab(c->pVtab);
170   if (!t->reusable_stmt && c->stmt) {
171     ResetStatement(c->stmt->sqlite_stmt());
172     t->reusable_stmt = std::move(c->stmt);
173   }
174   return SQLITE_OK;
175 }
176 
Filter(sqlite3_vtab_cursor * cur,int,const char *,int argc,sqlite3_value ** argv)177 int RuntimeTableFunctionModule::Filter(sqlite3_vtab_cursor* cur,
178                                        int,
179                                        const char*,
180                                        int argc,
181                                        sqlite3_value** argv) {
182   auto* c = GetCursor(cur);
183   auto* t = GetVtab(cur->pVtab);
184   auto* s = sqlite::ModuleStateManager<RuntimeTableFunctionModule>::GetState(
185       t->state);
186 
187   PERFETTO_CHECK(static_cast<size_t>(argc) == s->prototype.arguments.size());
188   PERFETTO_TP_TRACE(metatrace::Category::FUNCTION_CALL, "TABLE_FUNCTION_CALL",
189                     [s](metatrace::Record* r) {
190                       r->AddArg("Function", s->prototype.function_name.c_str());
191                     });
192 
193   // Prepare the SQL definition as a statement using SQLite.
194   // TODO(lalitm): measure and implement whether it would be a good idea to
195   // forward constraints here when we build the nested query.
196   if (c->stmt) {
197     // Filter can be called multiple times for the same cursor, so if we
198     // already have a statement, reset and reuse it. Otherwise, create a
199     // new one.
200     ResetStatement(c->stmt->sqlite_stmt());
201   } else {
202     auto stmt = s->engine->sqlite_engine()->PrepareStatement(s->sql_defn_str);
203     c->stmt = std::move(stmt);
204     if (const auto& status = c->stmt->status(); !status.ok()) {
205       return sqlite::utils::SetError(t, status.c_message());
206     }
207   }
208 
209   // Bind all the arguments to the appropriate places in the function.
210   for (uint32_t i = 0; i < static_cast<uint32_t>(argc); ++i) {
211     const auto& arg = s->prototype.arguments[i];
212     base::Status status = MaybeBindArgument(
213         c->stmt->sqlite_stmt(), s->prototype.function_name, arg, argv[i]);
214     if (!status.ok()) {
215       return sqlite::utils::SetError(t, status.c_message());
216     }
217   }
218 
219   // Reset the next call count - this is necessary because the same cursor
220   // can be used for multiple filter operations.
221   c->next_call_count = 0;
222   return Next(cur);
223 }
224 
Next(sqlite3_vtab_cursor * cur)225 int RuntimeTableFunctionModule::Next(sqlite3_vtab_cursor* cur) {
226   auto* c = GetCursor(cur);
227   c->is_eof = !c->stmt->Step();
228   c->next_call_count++;
229   if (const auto& status = c->stmt->status(); !status.ok()) {
230     return sqlite::utils::SetError(cur->pVtab, status.c_message());
231   }
232   return SQLITE_OK;
233 }
234 
Eof(sqlite3_vtab_cursor * cur)235 int RuntimeTableFunctionModule::Eof(sqlite3_vtab_cursor* cur) {
236   return GetCursor(cur)->is_eof;
237 }
238 
Column(sqlite3_vtab_cursor * cur,sqlite3_context * ctx,int N)239 int RuntimeTableFunctionModule::Column(sqlite3_vtab_cursor* cur,
240                                        sqlite3_context* ctx,
241                                        int N) {
242   auto* c = GetCursor(cur);
243   auto* t = GetVtab(cur->pVtab);
244   auto* s = sqlite::ModuleStateManager<RuntimeTableFunctionModule>::GetState(
245       t->state);
246 
247   auto idx = static_cast<size_t>(N);
248   if (PERFETTO_LIKELY(s->IsReturnValueColumn(idx))) {
249     sqlite::result::Value(ctx, sqlite3_column_value(c->stmt->sqlite_stmt(), N));
250     return SQLITE_OK;
251   }
252 
253   if (PERFETTO_LIKELY(s->IsArgumentColumn(idx))) {
254     // TODO(lalitm): it may be more appropriate to keep a note of the arguments
255     // which we passed in and return them here. Not doing this to because it
256     // doesn't seem necessary for any useful thing but something which may need
257     // to be changed in the future.
258     sqlite::result::Null(ctx);
259     return SQLITE_OK;
260   }
261 
262   PERFETTO_DCHECK(s->IsPrimaryKeyColumn(idx));
263   sqlite::result::Long(ctx, c->next_call_count);
264   return SQLITE_OK;
265 }
266 
Rowid(sqlite3_vtab_cursor *,sqlite_int64 *)267 int RuntimeTableFunctionModule::Rowid(sqlite3_vtab_cursor*, sqlite_int64*) {
268   return SQLITE_ERROR;
269 }
270 
271 }  // namespace perfetto::trace_processor
272