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