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