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