xref: /aosp_15_r20/external/perfetto/src/trace_processor/perfetto_sql/engine/perfetto_sql_engine.cc (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
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