• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #include "src/trace_processor/perfetto_sql/engine/perfetto_sql_engine.h"
18 
19 #include <algorithm>
20 #include <array>
21 #include <cctype>
22 #include <cstddef>
23 #include <cstdint>
24 #include <cstring>
25 #include <memory>
26 #include <optional>
27 #include <string>
28 #include <string_view>
29 #include <utility>
30 #include <variant>
31 #include <vector>
32 
33 #include "perfetto/base/logging.h"
34 #include "perfetto/base/status.h"
35 #include "perfetto/ext/base/flat_hash_map.h"
36 #include "perfetto/ext/base/status_or.h"
37 #include "perfetto/ext/base/string_utils.h"
38 #include "perfetto/ext/base/string_view.h"
39 #include "perfetto/trace_processor/basic_types.h"
40 #include "src/trace_processor/containers/string_pool.h"
41 #include "src/trace_processor/db/runtime_table.h"
42 #include "src/trace_processor/db/table.h"
43 #include "src/trace_processor/perfetto_sql/engine/created_function.h"
44 #include "src/trace_processor/perfetto_sql/engine/runtime_table_function.h"
45 #include "src/trace_processor/perfetto_sql/intrinsics/table_functions/static_table_function.h"
46 #include "src/trace_processor/perfetto_sql/parser/function_util.h"
47 #include "src/trace_processor/perfetto_sql/parser/perfetto_sql_parser.h"
48 #include "src/trace_processor/perfetto_sql/preprocessor/perfetto_sql_preprocessor.h"
49 #include "src/trace_processor/sqlite/db_sqlite_table.h"
50 #include "src/trace_processor/sqlite/scoped_db.h"
51 #include "src/trace_processor/sqlite/sql_source.h"
52 #include "src/trace_processor/sqlite/sqlite_engine.h"
53 #include "src/trace_processor/sqlite/sqlite_utils.h"
54 #include "src/trace_processor/tp_metatrace.h"
55 #include "src/trace_processor/util/sql_argument.h"
56 #include "src/trace_processor/util/sql_modules.h"
57 #include "src/trace_processor/util/status_macros.h"
58 
59 #include "protos/perfetto/trace_processor/metatrace_categories.pbzero.h"
60 
61 // Implementation details
62 // ----------------------
63 //
64 // The execution of PerfettoSQL statements is the joint responsibility of
65 // several classes which all are linked together in the following way:
66 //
67 //  PerfettoSqlEngine -> PerfettoSqlParser -> PerfettoSqlPreprocessor
68 //
69 // The responsibility of each of these classes is as follows:
70 //
71 // * PerfettoSqlEngine: this class is responsible for the end-to-end processing
72 //   of statements. It calls into PerfettoSqlParser to incrementally receive
73 //   parsed SQL statements and then executes them. If the statement is a
74 //   PerfettoSQL-only statement, the execution happens entirely in this class.
75 //   Otherwise, if the statement is a valid SQLite statement, SQLite is called
76 //   into to perform the execution.
77 // * PerfettoSqlParser: this class is responsible for taking a chunk of SQL and
78 //   incrementally converting them into parsed SQL statement. The parser calls
79 //   into the PerfettoSqlPreprocessor to split the SQL chunk into a statement
80 //   and perform any macro expansion. It then tries to parse any
81 //   PerfettoSQL-only statements into their component parts and leaves SQLite
82 //   statements as-is for execution by SQLite.
83 // * PerfettoSqlPreprocessor: this class is responsible for taking a chunk of
84 //   SQL and breaking them into statements, while also expanding any macros
85 //   which might be present inside.
86 namespace perfetto::trace_processor {
87 namespace {
88 
IncrementCountForStmt(const SqliteEngine::PreparedStatement & p_stmt,PerfettoSqlEngine::ExecutionStats * res)89 void IncrementCountForStmt(const SqliteEngine::PreparedStatement& p_stmt,
90                            PerfettoSqlEngine::ExecutionStats* res) {
91   res->statement_count++;
92 
93   // If the stmt is already done, it clearly didn't have any output.
94   if (p_stmt.IsDone())
95     return;
96 
97   sqlite3_stmt* stmt = p_stmt.sqlite_stmt();
98   if (sqlite3_column_count(stmt) == 1) {
99     sqlite3_value* value = sqlite3_column_value(stmt, 0);
100 
101     // If the "VOID" pointer associated to the return value is not null,
102     // that means this is a function which is forced to return a value
103     // (because all functions in SQLite have to) but doesn't actually
104     // wait to (i.e. it wants to be treated like CREATE TABLE or similar).
105     // Because of this, ignore the return value of this function.
106     // See |WrapSqlFunction| for where this is set.
107     if (sqlite3_value_pointer(value, "VOID") != nullptr) {
108       return;
109     }
110 
111     // If the statement only has a single column and that column is named
112     // "suppress_query_output", treat it as a statement without output for
113     // accounting purposes. This allows an escape hatch for cases where the
114     // user explicitly wants to ignore functions as having output.
115     if (strcmp(sqlite3_column_name(stmt, 0), "suppress_query_output") == 0) {
116       return;
117     }
118   }
119 
120   // Otherwise, the statement has output and so increment the count.
121   res->statement_count_with_output++;
122 }
123 
AddTracebackIfNeeded(base::Status status,const SqlSource & source)124 base::Status AddTracebackIfNeeded(base::Status status,
125                                   const SqlSource& source) {
126   if (status.ok()) {
127     return status;
128   }
129   if (status.GetPayload("perfetto.dev/has_traceback") == "true") {
130     return status;
131   }
132   // Since the error is with the statement as a whole, just pass zero so the
133   // traceback points to the start of the statement.
134   std::string traceback = source.AsTraceback(0);
135   status = base::ErrStatus("%s%s", traceback.c_str(), status.c_message());
136   status.SetPayload("perfetto.dev/has_traceback", "true");
137   return status;
138 }
139 
140 // This function is used when the PerfettoSQL has been fully executed by the
141 // PerfettoSqlEngine and a SqlSoruce is needed for SQLite to execute.
RewriteToDummySql(const SqlSource & source)142 SqlSource RewriteToDummySql(const SqlSource& source) {
143   return source.RewriteAllIgnoreExisting(
144       SqlSource::FromTraceProcessorImplementation("SELECT 0 WHERE 0"));
145 }
146 
147 base::StatusOr<std::vector<sql_argument::ArgumentDefinition>>
ValidateAndGetEffectiveSchema(const std::vector<std::string> & column_names,const std::vector<sql_argument::ArgumentDefinition> & schema,const char * tag)148 ValidateAndGetEffectiveSchema(
149     const std::vector<std::string>& column_names,
150     const std::vector<sql_argument::ArgumentDefinition>& schema,
151     const char* tag) {
152   std::vector<std::string> duplicate_columns;
153   for (auto it = column_names.begin(); it != column_names.end(); ++it) {
154     if (std::count(it + 1, column_names.end(), *it) > 0) {
155       duplicate_columns.push_back(*it);
156     }
157   }
158   if (!duplicate_columns.empty()) {
159     return base::ErrStatus("%s: multiple columns are named: %s", tag,
160                            base::Join(duplicate_columns, ", ").c_str());
161   }
162 
163   // If the user has not provided a schema, we have nothing further to validate.
164   if (schema.empty()) {
165     return schema;
166   }
167 
168   std::vector<std::string> columns_missing_from_query;
169   std::vector<std::string> columns_missing_from_schema;
170 
171   std::vector<sql_argument::ArgumentDefinition> effective_schema;
172 
173   for (const std::string& name : column_names) {
174     auto it =
175         std::find_if(schema.begin(), schema.end(), [&name](const auto& arg) {
176           return arg.name() == base::StringView(name);
177         });
178     bool present = it != schema.end();
179     if (present) {
180       effective_schema.push_back(*it);
181     } else {
182       columns_missing_from_schema.push_back(name);
183     }
184   }
185 
186   for (const auto& arg : schema) {
187     bool present = std::find_if(column_names.begin(), column_names.end(),
188                                 [&arg](const std::string& name) {
189                                   return arg.name() == base::StringView(name);
190                                 }) != column_names.end();
191     if (!present) {
192       columns_missing_from_query.push_back(arg.name().ToStdString());
193     }
194   }
195 
196   if (!columns_missing_from_query.empty() &&
197       !columns_missing_from_schema.empty()) {
198     return base::ErrStatus(
199         "%s: the following columns are declared in the schema, but do not "
200         "exist: "
201         "%s; and the folowing columns exist, but are not declared: %s",
202         tag, base::Join(columns_missing_from_query, ", ").c_str(),
203         base::Join(columns_missing_from_schema, ", ").c_str());
204   }
205 
206   if (!columns_missing_from_schema.empty()) {
207     return base::ErrStatus(
208         "%s: the following columns are missing from the schema: %s", tag,
209         base::Join(columns_missing_from_schema, ", ").c_str());
210   }
211 
212   if (!columns_missing_from_query.empty()) {
213     return base::ErrStatus(
214         "%s: the following columns are declared in the schema, but do not "
215         "exist: %s",
216         tag, base::Join(columns_missing_from_query, ", ").c_str());
217   }
218 
219   return effective_schema;
220 }
221 
GetColumnNamesFromSelectStatement(const SqliteEngine::PreparedStatement & stmt,const char * tag)222 base::StatusOr<std::vector<std::string>> GetColumnNamesFromSelectStatement(
223     const SqliteEngine::PreparedStatement& stmt,
224     const char* tag) {
225   auto columns =
226       static_cast<uint32_t>(sqlite3_column_count(stmt.sqlite_stmt()));
227   std::vector<std::string> column_names;
228   for (uint32_t i = 0; i < columns; ++i) {
229     std::string col_name =
230         sqlite3_column_name(stmt.sqlite_stmt(), static_cast<int>(i));
231     if (col_name.empty()) {
232       return base::ErrStatus("%s: column %u: name must not be empty", tag, i);
233     }
234     if (!std::isalpha(col_name.front())) {
235       return base::ErrStatus(
236           "%s: Column %u: name '%s' has to start with a letter.", tag, i,
237           col_name.c_str());
238     }
239     if (!sql_argument::IsValidName(base::StringView(col_name))) {
240       return base::ErrStatus(
241           "%s: Column %u: name '%s' has to contain only alphanumeric "
242           "characters and underscores.",
243           tag, i, col_name.c_str());
244     }
245     column_names.push_back(col_name);
246   }
247   return column_names;
248 }
249 
250 constexpr std::array<std::string_view, 6> kTokensAllowedInMacro{
251     "_ColumnNameList",
252     "_ProjectionFragment",
253     "_TableNameList",
254     "ColumnName",
255     "Expr",
256     "TableOrSubquery",
257 };
258 
IsTokenAllowedInMacro(const std::string & str)259 bool IsTokenAllowedInMacro(const std::string& str) {
260   base::StringView view = base::StringView{str};
261   return std::any_of(kTokensAllowedInMacro.begin(), kTokensAllowedInMacro.end(),
262                      [&view](const auto& allowed_token) {
263                        return view.CaseInsensitiveEq(base::StringView{
264                            allowed_token.data(), allowed_token.size()});
265                      });
266 }
267 
GetTokenNamesAllowedInMacro()268 std::string GetTokenNamesAllowedInMacro() {
269   std::vector<std::string> result;
270   result.reserve(kTokensAllowedInMacro.size());
271   for (auto token : kTokensAllowedInMacro) {
272     result.emplace_back(token);
273   }
274   return base::Join(result, ", ");
275 }
276 
277 }  // namespace
278 
PerfettoSqlEngine(StringPool * pool,bool enable_extra_checks)279 PerfettoSqlEngine::PerfettoSqlEngine(StringPool* pool, bool enable_extra_checks)
280     : pool_(pool),
281       enable_extra_checks_(enable_extra_checks),
282       engine_(new SqliteEngine()) {
283   // Initialize `perfetto_tables` table, which will contain the names of all of
284   // the registered tables.
285   char* errmsg_raw = nullptr;
286   int err =
287       sqlite3_exec(engine_->db(), "CREATE TABLE perfetto_tables(name STRING);",
288                    nullptr, nullptr, &errmsg_raw);
289   ScopedSqliteString errmsg(errmsg_raw);
290   if (err != SQLITE_OK) {
291     PERFETTO_FATAL("Failed to initialize perfetto_tables: %s", errmsg_raw);
292   }
293 
294   {
295     auto ctx = std::make_unique<RuntimeTableFunctionModule::Context>();
296     runtime_table_fn_context_ = ctx.get();
297     engine_->RegisterVirtualTableModule<RuntimeTableFunctionModule>(
298         "runtime_table_function", std::move(ctx));
299   }
300   {
301     auto ctx = std::make_unique<DbSqliteModule::Context>();
302     runtime_table_context_ = ctx.get();
303     engine_->RegisterVirtualTableModule<DbSqliteModule>("runtime_table",
304                                                         std::move(ctx));
305   }
306   {
307     auto ctx = std::make_unique<DbSqliteModule::Context>();
308     static_table_context_ = ctx.get();
309     engine_->RegisterVirtualTableModule<DbSqliteModule>("static_table",
310                                                         std::move(ctx));
311   }
312   {
313     auto ctx = std::make_unique<DbSqliteModule::Context>();
314     static_table_fn_context_ = ctx.get();
315     engine_->RegisterVirtualTableModule<DbSqliteModule>("static_table_function",
316                                                         std::move(ctx));
317   }
318 }
319 
320 base::StatusOr<SqliteEngine::PreparedStatement>
PrepareSqliteStatement(SqlSource sql_source)321 PerfettoSqlEngine::PrepareSqliteStatement(SqlSource sql_source) {
322   PerfettoSqlParser parser(std::move(sql_source), macros_);
323   if (!parser.Next()) {
324     return base::ErrStatus("No statement found to prepare");
325   }
326   const auto* sqlite =
327       std::get_if<PerfettoSqlParser::SqliteSql>(&parser.statement());
328   if (!sqlite) {
329     return base::ErrStatus("Statement was not a valid SQLite statement");
330   }
331   SqliteEngine::PreparedStatement stmt =
332       engine_->PrepareStatement(parser.statement_sql());
333   if (parser.Next()) {
334     return base::ErrStatus("Too many statements found to prepare");
335   }
336   return std::move(stmt);
337 }
338 
RegisterStaticTable(Table * table,const std::string & table_name,Table::Schema schema)339 void PerfettoSqlEngine::RegisterStaticTable(Table* table,
340                                             const std::string& table_name,
341                                             Table::Schema schema) {
342   // Make sure we didn't accidentally leak a state from a previous table
343   // creation.
344   PERFETTO_CHECK(!static_table_context_->temporary_create_state);
345   static_table_context_->temporary_create_state =
346       std::make_unique<DbSqliteModule::State>(table, std::move(schema));
347 
348   base::StackString<1024> sql(
349       R"(
350         CREATE VIRTUAL TABLE %s USING static_table;
351         INSERT INTO perfetto_tables(name) VALUES('%s');
352       )",
353       table_name.c_str(), table_name.c_str());
354   auto status =
355       Execute(SqlSource::FromTraceProcessorImplementation(sql.ToStdString()));
356   if (!status.ok()) {
357     PERFETTO_FATAL("%s", status.status().c_message());
358   }
359 
360   PERFETTO_CHECK(!static_table_context_->temporary_create_state);
361 }
362 
RegisterStaticTableFunction(std::unique_ptr<StaticTableFunction> fn)363 void PerfettoSqlEngine::RegisterStaticTableFunction(
364     std::unique_ptr<StaticTableFunction> fn) {
365   std::string name = fn->TableName();
366 
367   // Make sure we didn't accidentally leak a state from a previous table
368   // creation.
369   PERFETTO_CHECK(!static_table_fn_context_->temporary_create_state);
370   static_table_fn_context_->temporary_create_state =
371       std::make_unique<DbSqliteModule::State>(std::move(fn));
372 
373   base::StackString<1024> sql(
374       "CREATE VIRTUAL TABLE %s USING static_table_function;", name.c_str());
375   auto status =
376       Execute(SqlSource::FromTraceProcessorImplementation(sql.ToStdString()));
377   if (!status.ok()) {
378     PERFETTO_FATAL("%s", status.status().c_message());
379   }
380 
381   PERFETTO_CHECK(!static_table_fn_context_->temporary_create_state);
382 }
383 
Execute(SqlSource sql)384 base::StatusOr<PerfettoSqlEngine::ExecutionStats> PerfettoSqlEngine::Execute(
385     SqlSource sql) {
386   auto res = ExecuteUntilLastStatement(std::move(sql));
387   RETURN_IF_ERROR(res.status());
388   if (res->stmt.IsDone()) {
389     return res->stats;
390   }
391   while (res->stmt.Step()) {
392   }
393   RETURN_IF_ERROR(res->stmt.status());
394   return res->stats;
395 }
396 
397 base::StatusOr<PerfettoSqlEngine::ExecutionResult>
ExecuteUntilLastStatement(SqlSource sql_source)398 PerfettoSqlEngine::ExecuteUntilLastStatement(SqlSource sql_source) {
399   // A SQL string can contain several statements. Some of them might be comment
400   // only, e.g. "SELECT 1; /* comment */; SELECT 2;". Some statements can also
401   // be PerfettoSQL statements which we need to transpile before execution or
402   // execute without delegating to SQLite.
403   //
404   // The logic here is the following:
405   //  - We parse the statement as a PerfettoSQL statement.
406   //  - If the statement is something we can execute, execute it instantly and
407   //    prepare a dummy SQLite statement so the rest of the code continues to
408   //    work correctly.
409   //  - If the statement is actually an SQLite statement, we invoke PrepareStmt.
410   //  - We step once to make sure side effects take effect (e.g. for CREATE
411   //    TABLE statements, tables are created).
412   //  - If we encounter a valid statement afterwards, we step internally through
413   //    all rows of the previous one. This ensures that any further side effects
414   //    take hold *before* we step into the next statement.
415   //  - Once no further statements are encountered, we return the prepared
416   //    statement for the last valid statement.
417   std::optional<SqliteEngine::PreparedStatement> res;
418   ExecutionStats stats;
419   PerfettoSqlParser parser(std::move(sql_source), macros_);
420   while (parser.Next()) {
421     std::optional<SqlSource> source;
422     if (const auto* cf = std::get_if<PerfettoSqlParser::CreateFunction>(
423             &parser.statement())) {
424       RETURN_IF_ERROR(AddTracebackIfNeeded(ExecuteCreateFunction(*cf),
425                                            parser.statement_sql()));
426       source = RewriteToDummySql(parser.statement_sql());
427     } else if (const auto* cst = std::get_if<PerfettoSqlParser::CreateTable>(
428                    &parser.statement())) {
429       RETURN_IF_ERROR(AddTracebackIfNeeded(ExecuteCreateTable(*cst),
430                                            parser.statement_sql()));
431       source = RewriteToDummySql(parser.statement_sql());
432     } else if (const auto* create_view =
433                    std::get_if<PerfettoSqlParser::CreateView>(
434                        &parser.statement())) {
435       RETURN_IF_ERROR(AddTracebackIfNeeded(ExecuteCreateView(*create_view),
436                                            parser.statement_sql()));
437       source = RewriteToDummySql(parser.statement_sql());
438     } else if (const auto* include = std::get_if<PerfettoSqlParser::Include>(
439                    &parser.statement())) {
440       RETURN_IF_ERROR(ExecuteInclude(*include, parser));
441       source = RewriteToDummySql(parser.statement_sql());
442     } else if (const auto* macro = std::get_if<PerfettoSqlParser::CreateMacro>(
443                    &parser.statement())) {
444       auto sql = macro->sql;
445       RETURN_IF_ERROR(ExecuteCreateMacro(*macro));
446       source = RewriteToDummySql(sql);
447     } else if (const auto* create_index =
448                    std::get_if<PerfettoSqlParser::CreateIndex>(
449                        &parser.statement())) {
450       RETURN_IF_ERROR(ExecuteCreateIndex(*create_index));
451       source = RewriteToDummySql(parser.statement_sql());
452     } else if (const auto* drop_index =
453                    std::get_if<PerfettoSqlParser::DropIndex>(
454                        &parser.statement())) {
455       RETURN_IF_ERROR(ExecuteDropIndex(*drop_index));
456       source = RewriteToDummySql(parser.statement_sql());
457     } else {
458       // If none of the above matched, this must just be an SQL statement
459       // directly executable by SQLite.
460       const auto* sql =
461           std::get_if<PerfettoSqlParser::SqliteSql>(&parser.statement());
462       PERFETTO_CHECK(sql);
463       source = parser.statement_sql();
464     }
465 
466     // Try to get SQLite to prepare the statement.
467     std::optional<SqliteEngine::PreparedStatement> cur_stmt;
468     {
469       PERFETTO_TP_TRACE(metatrace::Category::QUERY_TIMELINE, "QUERY_PREPARE");
470       auto stmt = engine_->PrepareStatement(std::move(*source));
471       RETURN_IF_ERROR(stmt.status());
472       cur_stmt = std::move(stmt);
473     }
474 
475     // The only situation where we'd have an ok status but also no prepared
476     // statement is if the SQL was a pure comment. However, the PerfettoSQL
477     // parser should filter out such statements so this should never happen.
478     PERFETTO_DCHECK(cur_stmt->sqlite_stmt());
479 
480     // Before stepping into |cur_stmt|, we need to finish iterating through
481     // the previous statement so we don't have two clashing statements (e.g.
482     // SELECT * FROM v and DROP VIEW v) partially stepped into.
483     if (res && !res->IsDone()) {
484       PERFETTO_TP_TRACE(metatrace::Category::QUERY_TIMELINE,
485                         "STMT_STEP_UNTIL_DONE",
486                         [&res](metatrace::Record* record) {
487                           record->AddArg("Original SQL", res->original_sql());
488                           record->AddArg("Executed SQL", res->sql());
489                         });
490       while (res->Step()) {
491       }
492       RETURN_IF_ERROR(res->status());
493     }
494 
495     // Propogate the current statement to the next iteration.
496     res = std::move(cur_stmt);
497 
498     // Step the newly prepared statement once. This is considered to be
499     // "executing" the statement.
500     {
501       PERFETTO_TP_TRACE(metatrace::Category::QUERY_TIMELINE, "STMT_FIRST_STEP",
502                         [&res](metatrace::Record* record) {
503                           record->AddArg("Original SQL", res->original_sql());
504                           record->AddArg("Executed SQL", res->sql());
505                         });
506       res->Step();
507       RETURN_IF_ERROR(res->status());
508     }
509 
510     // Increment the neecessary counts for the statement.
511     IncrementCountForStmt(*res, &stats);
512   }
513   RETURN_IF_ERROR(parser.status());
514 
515   // If we didn't manage to prepare a single statement, that means everything
516   // in the SQL was treated as a comment.
517   if (!res)
518     return base::ErrStatus("No valid SQL to run");
519 
520   // Update the output statement and column count.
521   stats.column_count =
522       static_cast<uint32_t>(sqlite3_column_count(res->sqlite_stmt()));
523   return ExecutionResult{std::move(*res), stats};
524 }
525 
RegisterRuntimeFunction(bool replace,const FunctionPrototype & prototype,sql_argument::Type return_type,SqlSource sql)526 base::Status PerfettoSqlEngine::RegisterRuntimeFunction(
527     bool replace,
528     const FunctionPrototype& prototype,
529     sql_argument::Type return_type,
530     SqlSource sql) {
531   int created_argc = static_cast<int>(prototype.arguments.size());
532   auto* ctx = static_cast<CreatedFunction::Context*>(
533       sqlite_engine()->GetFunctionContext(prototype.function_name,
534                                           created_argc));
535   if (ctx) {
536     if (CreatedFunction::IsValid(ctx) && !replace) {
537       return base::ErrStatus(
538           "CREATE PERFETTO FUNCTION[prototype=%s]: function already exists",
539           prototype.ToString().c_str());
540     }
541     CreatedFunction::Reset(ctx, this);
542   } else {
543     // We register the function with SQLite before we prepare the statement so
544     // the statement can reference the function itself, enabling recursive
545     // calls.
546     std::unique_ptr<CreatedFunction::Context> created_fn_ctx =
547         CreatedFunction::MakeContext(this);
548     ctx = created_fn_ctx.get();
549     RETURN_IF_ERROR(RegisterFunctionWithSqlite<CreatedFunction>(
550         prototype.function_name.c_str(), created_argc,
551         std::move(created_fn_ctx)));
552     runtime_function_count_++;
553   }
554   return CreatedFunction::Prepare(ctx, prototype, return_type, std::move(sql));
555 }
556 
557 base::StatusOr<std::unique_ptr<RuntimeTable>>
CreateTableImpl(const char * tag,const std::string & name,SqliteEngine::PreparedStatement source,const std::vector<std::string> & column_names,const std::vector<sql_argument::ArgumentDefinition> & schema,CreateTableType create_table_type)558 PerfettoSqlEngine::CreateTableImpl(
559     const char* tag,
560     const std::string& name,
561     SqliteEngine::PreparedStatement source,
562     const std::vector<std::string>& column_names,
563     const std::vector<sql_argument::ArgumentDefinition>& schema,
564     CreateTableType create_table_type) {
565   size_t column_count = column_names.size();
566   RuntimeTable::Builder builder(pool_, column_names);
567   uint32_t rows = 0;
568 
569   int res;
570   for (res = sqlite3_step(source.sqlite_stmt()); res == SQLITE_ROW;
571        ++rows, res = sqlite3_step(source.sqlite_stmt())) {
572     for (uint32_t i = 0; i < column_count; ++i) {
573       int int_i = static_cast<int>(i);
574       switch (sqlite3_column_type(source.sqlite_stmt(), int_i)) {
575         case SQLITE_NULL:
576           RETURN_IF_ERROR(builder.AddNull(i));
577           break;
578         case SQLITE_INTEGER:
579           RETURN_IF_ERROR(builder.AddInteger(
580               i, sqlite3_column_int64(source.sqlite_stmt(), int_i)));
581           break;
582         case SQLITE_FLOAT:
583           RETURN_IF_ERROR(builder.AddFloat(
584               i, sqlite3_column_double(source.sqlite_stmt(), int_i)));
585           break;
586         case SQLITE_TEXT: {
587           RETURN_IF_ERROR(builder.AddText(
588               i, reinterpret_cast<const char*>(
589                      sqlite3_column_text(source.sqlite_stmt(), int_i))));
590           break;
591         }
592         case SQLITE_BLOB:
593           if (create_table_type == CreateTableType::kValidateOnly) {
594             RETURN_IF_ERROR(builder.AddNull(i));
595             break;
596           } else {
597             return base::ErrStatus(
598                 "%s on column '%s' in table '%s': bytes "
599                 "columns are not supported",
600                 tag, sqlite3_column_name(source.sqlite_stmt(), int_i),
601                 name.c_str());
602           }
603       }
604     }
605   }
606   if (res != SQLITE_DONE) {
607     return base::ErrStatus(
608         "%s: SQLite error while creating body for table '%s': %s", tag,
609         name.c_str(), sqlite3_errmsg(engine_->db()));
610   }
611 
612   ASSIGN_OR_RETURN(auto table, std::move(builder).Build(rows));
613 
614   std::vector<std::string> errors;
615 
616   // Validate the column types.
617   if (!schema.empty()) {
618     auto actual_schema = table->schema();
619     for (size_t i = 0; i < column_count; ++i) {
620       SqlValue::Type type = actual_schema.columns[i].type;
621       sql_argument::Type declared_type = schema[i].type();
622       SqlValue::Type effective_declared_type =
623           sql_argument::TypeToSqlValueType(declared_type);
624       if (type != SqlValue::kNull && type != effective_declared_type) {
625         errors.push_back(
626             base::StackString<1024>(
627                 "column '%s' declared as %s (%s) in the "
628                 "schema, but %s found",
629                 column_names[i].c_str(),
630                 sql_argument::TypeToHumanFriendlyString(declared_type),
631                 sqlite::utils::SqlValueTypeToString(effective_declared_type),
632                 sqlite::utils::SqlValueTypeToString(type))
633                 .ToStdString());
634       }
635     }
636   }
637 
638   // It's really annoying to have errors one-by-one when multiple columns have
639   // incorrect types, so we emit all errors together here.
640   if (!errors.empty()) {
641     if (errors.size() == 1) {
642       return base::ErrStatus("%s(%s): %s", tag, name.c_str(),
643                              errors.front().c_str());
644     }
645     for (std::string& error : errors) {
646       error = "  " + error;
647     }
648     return base::ErrStatus("%s(%s): %zu errors\n%s", tag, name.c_str(),
649                            errors.size(), base::Join(errors, "\n").c_str());
650   }
651 
652   return std::move(table);
653 }
654 
ExecuteCreateTable(const PerfettoSqlParser::CreateTable & create_table)655 base::Status PerfettoSqlEngine::ExecuteCreateTable(
656     const PerfettoSqlParser::CreateTable& create_table) {
657   PERFETTO_TP_TRACE(metatrace::Category::QUERY_TIMELINE,
658                     "CREATE PERFETTO TABLE",
659                     [&create_table](metatrace::Record* record) {
660                       record->AddArg("table_name", create_table.name);
661                     });
662 
663   auto stmt_or = engine_->PrepareStatement(create_table.sql);
664   RETURN_IF_ERROR(stmt_or.status());
665   SqliteEngine::PreparedStatement stmt = std::move(stmt_or);
666 
667   base::StatusOr<std::vector<std::string>> maybe_column_names =
668       GetColumnNamesFromSelectStatement(stmt, "CREATE PERFETTO TABLE");
669   RETURN_IF_ERROR(maybe_column_names.status());
670   const std::vector<std::string>& column_names = *maybe_column_names;
671 
672   base::StatusOr<std::vector<sql_argument::ArgumentDefinition>>
673       effective_schema = ValidateAndGetEffectiveSchema(
674           column_names, create_table.schema, "CREATE PERFETTO TABLE");
675   RETURN_IF_ERROR(effective_schema.status());
676 
677   ASSIGN_OR_RETURN(
678       auto table,
679       CreateTableImpl("CREATE PERFETTO TABLE", create_table.name,
680                       std::move(stmt), column_names, *effective_schema,
681                       CreateTableType::kCreateTable));
682 
683   // TODO(lalitm): unfortunately, in the (very unlikely) event that there is a
684   // sqlite3_interrupt call between the DROP and CREATE, we can end up with the
685   // non-atomic query execution. Fixing this is extremely difficult as it
686   // involves telling SQLite that we want the drop/create to be atomic.
687   //
688   // We would need to do with the transaction API but given we have no usage of
689   // this until now, investigating that needs some proper work.
690   if (create_table.replace) {
691     base::StackString<1024> drop("DROP TABLE IF EXISTS %s",
692                                  create_table.name.c_str());
693     auto drop_res = Execute(
694         SqlSource::FromTraceProcessorImplementation(drop.ToStdString()));
695     RETURN_IF_ERROR(drop_res.status());
696   }
697 
698   base::StackString<1024> create("CREATE VIRTUAL TABLE %s USING runtime_table",
699                                  create_table.name.c_str());
700 
701   // Make sure we didn't accidentally leak a state from a previous function
702   // creation.
703   PERFETTO_CHECK(!runtime_table_context_->temporary_create_state);
704 
705   // Move the state into the context so that it will be picked up in xCreate
706   // of RuntimeTableFunctionModule.
707   runtime_table_context_->temporary_create_state =
708       std::make_unique<DbSqliteModule::State>(std::move(table));
709   auto status =
710       Execute(SqlSource::FromTraceProcessorImplementation(create.ToStdString()))
711           .status();
712 
713   // If an error happened, it's possible that the state was not picked up.
714   // Therefore, always reset the state just in case. OTOH if the creation
715   // succeeded, the state should always have been captured.
716   if (status.ok()) {
717     PERFETTO_CHECK(!runtime_table_context_->temporary_create_state);
718   } else {
719     runtime_table_context_->temporary_create_state.reset();
720   }
721   return status;
722 }
723 
ExecuteCreateView(const PerfettoSqlParser::CreateView & create_view)724 base::Status PerfettoSqlEngine::ExecuteCreateView(
725     const PerfettoSqlParser::CreateView& create_view) {
726   PERFETTO_TP_TRACE(metatrace::Category::QUERY_TIMELINE, "CREATE PERFETTO VIEW",
727                     [&create_view](metatrace::Record* record) {
728                       record->AddArg("view_name", create_view.name);
729                     });
730 
731   // Verify that the underlying SQL statement is valid.
732   auto stmt = sqlite_engine()->PrepareStatement(create_view.sql);
733   RETURN_IF_ERROR(stmt.status());
734 
735   if (create_view.replace) {
736     base::StackString<1024> drop_if_exists("DROP VIEW IF EXISTS %s",
737                                            create_view.name.c_str());
738     RETURN_IF_ERROR(Execute(SqlSource::FromTraceProcessorImplementation(
739                                 drop_if_exists.ToStdString()))
740                         .status());
741   }
742 
743   // If the schema is specified, verify that the column names match it.
744   if (!create_view.schema.empty()) {
745     base::StatusOr<std::vector<std::string>> maybe_column_names =
746         GetColumnNamesFromSelectStatement(stmt, "CREATE PERFETTO VIEW");
747     RETURN_IF_ERROR(maybe_column_names.status());
748     const std::vector<std::string>& column_names = *maybe_column_names;
749 
750     base::StatusOr<std::vector<sql_argument::ArgumentDefinition>>
751         effective_schema = ValidateAndGetEffectiveSchema(
752             column_names, create_view.schema, "CREATE PERFETTO VIEW");
753     RETURN_IF_ERROR(effective_schema.status());
754 
755     if (enable_extra_checks_) {
756       // If extra checks are enabled, materialize the view to ensure that its
757       // values are correct.
758       base::StatusOr<std::unique_ptr<RuntimeTable>> materialized =
759           CreateTableImpl("CREATE PERFETTO VIEW", create_view.name,
760                           std::move(stmt), column_names, *effective_schema,
761                           CreateTableType::kValidateOnly);
762       RETURN_IF_ERROR(materialized.status());
763     }
764   }
765 
766   RETURN_IF_ERROR(Execute(create_view.create_view_sql).status());
767   return base::OkStatus();
768 }
769 
EnableSqlFunctionMemoization(const std::string & name)770 base::Status PerfettoSqlEngine::EnableSqlFunctionMemoization(
771     const std::string& name) {
772   constexpr size_t kSupportedArgCount = 1;
773   auto* ctx = static_cast<CreatedFunction::Context*>(
774       sqlite_engine()->GetFunctionContext(name, kSupportedArgCount));
775   if (!ctx) {
776     return base::ErrStatus(
777         "EXPERIMENTAL_MEMOIZE: Function '%s'(INT) does not exist",
778         name.c_str());
779   }
780   return CreatedFunction::EnableMemoization(ctx);
781 }
782 
ExecuteInclude(const PerfettoSqlParser::Include & include,const PerfettoSqlParser & parser)783 base::Status PerfettoSqlEngine::ExecuteInclude(
784     const PerfettoSqlParser::Include& include,
785     const PerfettoSqlParser& parser) {
786   PERFETTO_TP_TRACE(
787       metatrace::Category::QUERY_TIMELINE, "INCLUDE PERFETTO MODULE",
788       [&](metatrace::Record* r) { r->AddArg("include", include.key); });
789 
790   const std::string& key = include.key;
791   if (key == "*") {
792     for (auto package = packages_.GetIterator(); package; ++package) {
793       RETURN_IF_ERROR(IncludePackageImpl(package.value(), key, parser));
794     }
795     return base::OkStatus();
796   }
797 
798   std::string package_name = sql_modules::GetPackageName(key);
799 
800   auto* package = FindPackage(package_name);
801   if (!package) {
802     if (package_name == "common") {
803       return base::ErrStatus(
804           "INCLUDE: Package `common` has been removed and most of the "
805           "functionality has been moved to other packages. Check "
806           "`slices.with_context` for replacement for `common.slices` and "
807           "`time.conversion` for replacement for `common.timestamps`. The "
808           "documentation for Perfetto standard library can be found at "
809           "https://perfetto.dev/docs/analysis/stdlib-docs.");
810     }
811     return base::ErrStatus("INCLUDE: Package '%s' not found", key.c_str());
812   }
813   return IncludePackageImpl(*package, key, parser);
814 }
815 
ExecuteCreateIndex(const PerfettoSqlParser::CreateIndex & index)816 base::Status PerfettoSqlEngine::ExecuteCreateIndex(
817     const PerfettoSqlParser::CreateIndex& index) {
818   PERFETTO_TP_TRACE(metatrace::Category::QUERY_TIMELINE,
819                     "CREATE PERFETTO INDEX",
820                     [&index](metatrace::Record* record) {
821                       record->AddArg("index_name", index.name);
822                       record->AddArg("table_name", index.table_name);
823                       record->AddArg("cols", base::Join(index.col_names, ", "));
824                     });
825 
826   Table* t = GetTableOrNull(index.table_name);
827   if (!t) {
828     return base::ErrStatus("CREATE PERFETTO INDEX: Table '%s' not found",
829                            index.table_name.c_str());
830   }
831   std::vector<uint32_t> col_idxs;
832   for (const std::string& col_name : index.col_names) {
833     const std::optional<uint32_t> opt_col = t->ColumnIdxFromName(col_name);
834     if (!opt_col) {
835       return base::ErrStatus(
836           "CREATE PERFETTO INDEX: Column '%s' not found in table '%s'",
837           col_name.c_str(), index.table_name.c_str());
838     }
839     col_idxs.push_back(*opt_col);
840   }
841   RETURN_IF_ERROR(
842       t->CreateIndex(index.name, std::move(col_idxs), index.replace));
843   return base::OkStatus();
844 }
845 
ExecuteDropIndex(const PerfettoSqlParser::DropIndex & index)846 base::Status PerfettoSqlEngine::ExecuteDropIndex(
847     const PerfettoSqlParser::DropIndex& index) {
848   PERFETTO_TP_TRACE(metatrace::Category::QUERY_TIMELINE, "DROP PERFETTO INDEX",
849                     [&index](metatrace::Record* record) {
850                       record->AddArg("index_name", index.name);
851                       record->AddArg("table_name", index.table_name);
852                     });
853 
854   Table* t = GetTableOrNull(index.table_name);
855   if (!t) {
856     return base::ErrStatus("DROP PERFETTO INDEX: Table '%s' not found",
857                            index.table_name.c_str());
858   }
859   RETURN_IF_ERROR(t->DropIndex(index.name));
860   return base::OkStatus();
861 }
862 
IncludePackageImpl(sql_modules::RegisteredPackage & package,const std::string & include_key,const PerfettoSqlParser & parser)863 base::Status PerfettoSqlEngine::IncludePackageImpl(
864     sql_modules::RegisteredPackage& package,
865     const std::string& include_key,
866     const PerfettoSqlParser& parser) {
867   if (!include_key.empty() && include_key.back() == '*') {
868     // If the key ends with a wildcard, iterate through all the keys in the
869     // module and include matching ones.
870     std::string prefix = include_key.substr(0, include_key.size() - 1);
871     for (auto module = package.modules.GetIterator(); module; ++module) {
872       if (!base::StartsWith(module.key(), prefix))
873         continue;
874       PERFETTO_TP_TRACE(
875           metatrace::Category::QUERY_TIMELINE,
876           "Include (expanded from wildcard)",
877           [&](metatrace::Record* r) { r->AddArg("Module", module.key()); });
878       RETURN_IF_ERROR(IncludeModuleImpl(module.value(), module.key(), parser));
879     }
880     return base::OkStatus();
881   }
882   auto* module_file = package.modules.Find(include_key);
883   if (!module_file) {
884     return base::ErrStatus("INCLUDE: unknown module '%s'", include_key.c_str());
885   }
886   return IncludeModuleImpl(*module_file, include_key, parser);
887 }
888 
IncludeModuleImpl(sql_modules::RegisteredPackage::ModuleFile & file,const std::string & key,const PerfettoSqlParser & parser)889 base::Status PerfettoSqlEngine::IncludeModuleImpl(
890     sql_modules::RegisteredPackage::ModuleFile& file,
891     const std::string& key,
892     const PerfettoSqlParser& parser) {
893   // INCLUDE is noop for already included files.
894   if (file.included) {
895     return base::OkStatus();
896   }
897 
898   auto it = Execute(SqlSource::FromModuleInclude(file.sql, key));
899   if (!it.status().ok()) {
900     return base::ErrStatus("%s%s",
901                            parser.statement_sql().AsTraceback(0).c_str(),
902                            it.status().c_message());
903   }
904   if (it->statement_count_with_output > 0)
905     return base::ErrStatus("INCLUDE: Included module returning values.");
906   file.included = true;
907   return base::OkStatus();
908 }
909 
ExecuteCreateFunction(const PerfettoSqlParser::CreateFunction & cf)910 base::Status PerfettoSqlEngine::ExecuteCreateFunction(
911     const PerfettoSqlParser::CreateFunction& cf) {
912   PERFETTO_TP_TRACE(metatrace::Category::QUERY_TIMELINE,
913                     "CREATE PERFETTO FUNCTION",
914                     [&cf](metatrace::Record* record) {
915                       record->AddArg("name", cf.prototype.function_name);
916                       record->AddArg("prototype", cf.prototype.ToString());
917                     });
918 
919   if (!cf.returns.is_table) {
920     return RegisterRuntimeFunction(cf.replace, cf.prototype,
921                                    cf.returns.scalar_type, cf.sql);
922   }
923 
924   auto state = std::make_unique<RuntimeTableFunctionModule::State>(
925       RuntimeTableFunctionModule::State{
926           this,
927           cf.sql,
928           cf.prototype,
929           cf.returns.table_columns,
930           std::nullopt,
931       });
932 
933   // Verify that the provided SQL prepares to a statement correctly.
934   auto stmt = sqlite_engine()->PrepareStatement(cf.sql);
935   RETURN_IF_ERROR(stmt.status());
936 
937   // Verify that every argument name in the function appears in the
938   // argument list.
939   //
940   // We intentionally loop from 1 to |used_param_count| because SQL
941   // parameters are 1-indexed *not* 0-indexed.
942   int used_param_count = sqlite3_bind_parameter_count(stmt.sqlite_stmt());
943   for (int i = 1; i <= used_param_count; ++i) {
944     const char* name = sqlite3_bind_parameter_name(stmt.sqlite_stmt(), i);
945 
946     if (!name) {
947       return base::ErrStatus(
948           "%s: \"Nameless\" SQL parameters cannot be used in the SQL "
949           "statements of view functions.",
950           state->prototype.function_name.c_str());
951     }
952 
953     if (!base::StringView(name).StartsWith("$")) {
954       return base::ErrStatus(
955           "%s: invalid parameter name %s used in the SQL definition of "
956           "the view function: all parameters must be prefixed with '$' not "
957           "':' or '@'.",
958           state->prototype.function_name.c_str(), name);
959     }
960 
961     auto it = std::find_if(state->prototype.arguments.begin(),
962                            state->prototype.arguments.end(),
963                            [name](const sql_argument::ArgumentDefinition& arg) {
964                              return arg.dollar_name() == name;
965                            });
966     if (it == state->prototype.arguments.end()) {
967       return base::ErrStatus(
968           "%s: parameter %s does not appear in the list of arguments in the "
969           "prototype of the view function.",
970           state->prototype.function_name.c_str(), name);
971     }
972   }
973 
974   // Verify that the prepared statement column count matches the return
975   // count.
976   auto col_count =
977       static_cast<uint32_t>(sqlite3_column_count(stmt.sqlite_stmt()));
978   if (col_count != state->return_values.size()) {
979     return base::ErrStatus(
980         "%s: number of return values %u does not match SQL statement column "
981         "count %zu.",
982         state->prototype.function_name.c_str(), col_count,
983         state->return_values.size());
984   }
985 
986   // Verify that the return names matches the prepared statment column names.
987   for (uint32_t i = 0; i < col_count; ++i) {
988     const char* name =
989         sqlite3_column_name(stmt.sqlite_stmt(), static_cast<int>(i));
990     if (name != state->return_values[i].name()) {
991       return base::ErrStatus(
992           "%s: column %s at index %u does not match return value name %s.",
993           state->prototype.function_name.c_str(), name, i,
994           state->return_values[i].name().c_str());
995     }
996   }
997   state->temporary_create_stmt = std::move(stmt);
998 
999   // TODO(lalitm): this suffers the same non-atomic DROP/CREATE problem as
1000   // CREATE PERFETTO TABLE implementation above: see the comment there for
1001   // more info on this.
1002   if (cf.replace) {
1003     base::StackString<1024> drop("DROP TABLE IF EXISTS %s",
1004                                  state->prototype.function_name.c_str());
1005     auto res = Execute(
1006         SqlSource::FromTraceProcessorImplementation(drop.ToStdString()));
1007     RETURN_IF_ERROR(res.status());
1008   }
1009 
1010   base::StackString<1024> create(
1011       "CREATE VIRTUAL TABLE %s USING runtime_table_function",
1012       state->prototype.function_name.c_str());
1013 
1014   // Make sure we didn't accidentally leak a state from a previous function
1015   // creation.
1016   PERFETTO_CHECK(!runtime_table_fn_context_->temporary_create_state);
1017 
1018   // Move the state into the context so that it will be picked up in xCreate
1019   // of RuntimeTableFunctionModule.
1020   runtime_table_fn_context_->temporary_create_state = std::move(state);
1021   auto status = Execute(cf.sql.RewriteAllIgnoreExisting(
1022                             SqlSource::FromTraceProcessorImplementation(
1023                                 create.ToStdString())))
1024                     .status();
1025 
1026   // If an error happened, it's possible that the state was not picked up.
1027   // Therefore, always reset the state just in case. OTOH if the creation
1028   // succeeded, the state should always have been captured.
1029   if (status.ok()) {
1030     PERFETTO_CHECK(!runtime_table_fn_context_->temporary_create_state);
1031   } else {
1032     runtime_table_fn_context_->temporary_create_state.reset();
1033   }
1034   return status;
1035 }
1036 
ExecuteCreateMacro(const PerfettoSqlParser::CreateMacro & create_macro)1037 base::Status PerfettoSqlEngine::ExecuteCreateMacro(
1038     const PerfettoSqlParser::CreateMacro& create_macro) {
1039   PERFETTO_TP_TRACE(metatrace::Category::QUERY_TIMELINE,
1040                     "CREATE PERFETTO MACRO",
1041                     [&create_macro](metatrace::Record* record) {
1042                       record->AddArg("name", create_macro.name.sql());
1043                     });
1044 
1045   // Check that the argument types is one of the allowed types.
1046   for (const auto& [name, type] : create_macro.args) {
1047     if (!IsTokenAllowedInMacro(type.sql())) {
1048       // TODO(lalitm): add a link to create macro documentation.
1049       return base::ErrStatus(
1050           "%sMacro '%s' argument '%s' is unknown type '%s'. Allowed types: %s",
1051           type.AsTraceback(0).c_str(), create_macro.name.sql().c_str(),
1052           name.sql().c_str(), type.sql().c_str(),
1053           GetTokenNamesAllowedInMacro().c_str());
1054     }
1055   }
1056   if (!IsTokenAllowedInMacro(create_macro.returns.sql())) {
1057     // TODO(lalitm): add a link to create macro documentation.
1058     return base::ErrStatus(
1059         "%sMacro %s return type %s is unknown. Allowed types: %s",
1060         create_macro.returns.AsTraceback(0).c_str(),
1061         create_macro.name.sql().c_str(), create_macro.returns.sql().c_str(),
1062         GetTokenNamesAllowedInMacro().c_str());
1063   }
1064 
1065   std::vector<std::string> args;
1066   args.reserve(create_macro.args.size());
1067   for (const auto& arg : create_macro.args) {
1068     args.push_back(arg.first.sql());
1069   }
1070   PerfettoSqlPreprocessor::Macro macro{
1071       create_macro.replace,
1072       create_macro.name.sql(),
1073       std::move(args),
1074       create_macro.sql,
1075   };
1076   if (auto* it = macros_.Find(create_macro.name.sql()); it) {
1077     if (!create_macro.replace) {
1078       // TODO(lalitm): add a link to create macro documentation.
1079       return base::ErrStatus("%sMacro already exists",
1080                              create_macro.name.AsTraceback(0).c_str());
1081     }
1082     *it = std::move(macro);
1083     return base::OkStatus();
1084   }
1085   std::string name = macro.name;
1086   auto it_and_inserted = macros_.Insert(std::move(name), std::move(macro));
1087   PERFETTO_CHECK(it_and_inserted.second);
1088   return base::OkStatus();
1089 }
1090 
GetRuntimeTableOrNull(std::string_view name) const1091 const RuntimeTable* PerfettoSqlEngine::GetRuntimeTableOrNull(
1092     std::string_view name) const {
1093   auto* state = runtime_table_context_->manager.FindStateByName(name);
1094   return state ? state->runtime_table.get() : nullptr;
1095 }
1096 
GetRuntimeTableOrNull(std::string_view name)1097 RuntimeTable* PerfettoSqlEngine::GetRuntimeTableOrNull(std::string_view name) {
1098   auto* state = runtime_table_context_->manager.FindStateByName(name);
1099   return state ? state->runtime_table.get() : nullptr;
1100 }
1101 
GetStaticTableOrNull(std::string_view name) const1102 const Table* PerfettoSqlEngine::GetStaticTableOrNull(
1103     std::string_view name) const {
1104   auto* state = static_table_context_->manager.FindStateByName(name);
1105   return state ? state->static_table : nullptr;
1106 }
1107 
GetStaticTableOrNull(std::string_view name)1108 Table* PerfettoSqlEngine::GetStaticTableOrNull(std::string_view name) {
1109   auto* state = static_table_context_->manager.FindStateByName(name);
1110   return state ? state->static_table : nullptr;
1111 }
1112 
1113 }  // namespace perfetto::trace_processor
1114