• 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/parser/perfetto_sql_parser.h"
18 
19 #include <cctype>
20 #include <cstdio>
21 #include <cstdlib>
22 #include <memory>
23 #include <optional>
24 #include <string>
25 #include <string_view>
26 #include <utility>
27 #include <vector>
28 
29 #include "perfetto/base/logging.h"
30 #include "perfetto/base/status.h"
31 #include "perfetto/ext/base/flat_hash_map.h"
32 #include "perfetto/ext/base/string_utils.h"
33 #include "perfetto/ext/base/string_view.h"
34 #include "perfetto/ext/base/utils.h"
35 #include "src/trace_processor/perfetto_sql/grammar/perfettosql_grammar_interface.h"
36 #include "src/trace_processor/perfetto_sql/parser/function_util.h"
37 #include "src/trace_processor/perfetto_sql/preprocessor/perfetto_sql_preprocessor.h"
38 #include "src/trace_processor/perfetto_sql/tokenizer/sqlite_tokenizer.h"
39 #include "src/trace_processor/sqlite/sql_source.h"
40 #include "src/trace_processor/util/sql_argument.h"
41 
42 namespace perfetto::trace_processor {
43 
44 namespace {
45 
46 using Token = SqliteTokenizer::Token;
47 using Statement = PerfettoSqlParser::Statement;
48 
TokenToPerfettoSqlToken(const Token & token)49 PerfettoSqlToken TokenToPerfettoSqlToken(const Token& token) {
50   return PerfettoSqlToken{token.str.data(), token.str.size()};
51 }
52 
PerfettoSqlTokenToToken(const PerfettoSqlToken & token)53 Token PerfettoSqlTokenToToken(const PerfettoSqlToken& token) {
54   return Token{std::string_view(token.ptr, token.n), 0};
55 }
56 
57 }  // namespace
58 
59 // Grammar interface implementation
60 extern "C" {
61 
62 struct PerfettoSqlParserState {
PerfettoSqlParserStateperfetto::trace_processor::PerfettoSqlParserState63   explicit PerfettoSqlParserState(
64       SqlSource source,
65       const base::FlatHashMap<std::string, PerfettoSqlPreprocessor::Macro>&
66           macros)
67       : tokenizer{SqlSource::FromTraceProcessorImplementation("")},
68         preprocessor(std::move(source), macros) {}
69 
ErrorAtTokenperfetto::trace_processor::PerfettoSqlParserState70   void ErrorAtToken(const char* msg, const PerfettoSqlToken& token) {
71     status = base::ErrStatus(
72         "%s%s", tokenizer.AsTraceback(PerfettoSqlTokenToToken(token)).c_str(),
73         msg);
74   }
75 
76   // Current statement being built
77   std::optional<PerfettoSqlParser::Statement> current_statement;
78 
79   // Tokenizer for the current statement
80   SqliteTokenizer tokenizer;
81 
82   // Preprocessor for handling SQL statements
83   PerfettoSqlPreprocessor preprocessor;
84 
85   // Error handling
86   base::Status status;
87 };
88 
89 struct PerfettoSqlArgumentList {
90   std::vector<sql_argument::ArgumentDefinition> inner;
91 };
92 
93 struct PerfettoSqlIndexedColumnList {
94   std::vector<std::string> cols;
95 };
96 
97 struct PerfettoSqlMacroArgumentList {
98   std::vector<std::pair<SqlSource, SqlSource>> args;
99 };
100 
101 struct PerfettoSqlFnReturnType {
102   bool is_table;
103   sql_argument::Type scalar_type;
104   std::vector<sql_argument::ArgumentDefinition> table_columns;
105 };
106 
107 struct PerfettoSqlTableSchema {
108   std::vector<sql_argument::ArgumentDefinition> columns;
109   std::string description;
110 };
111 
OnPerfettoSqlCreateOrAppendArgument(PerfettoSqlParserState * state,PerfettoSqlArgumentList * list,PerfettoSqlToken * name,PerfettoSqlToken * type)112 PerfettoSqlArgumentList* OnPerfettoSqlCreateOrAppendArgument(
113     PerfettoSqlParserState* state,
114     PerfettoSqlArgumentList* list,
115     PerfettoSqlToken* name,
116     PerfettoSqlToken* type) {
117   std::unique_ptr<PerfettoSqlArgumentList> owned_list(list);
118   if (!owned_list) {
119     owned_list = std::make_unique<PerfettoSqlArgumentList>();
120   }
121   auto parsed = sql_argument::ParseType(base::StringView(type->ptr, type->n));
122   if (!parsed) {
123     state->ErrorAtToken("Failed to parse type", *type);
124     return nullptr;
125   }
126   owned_list->inner.emplace_back("$" + std::string(name->ptr, name->n),
127                                  *parsed);
128   return owned_list.release();
129 }
130 
OnPerfettoSqlFreeArgumentList(PerfettoSqlParserState *,PerfettoSqlArgumentList * args)131 void OnPerfettoSqlFreeArgumentList(PerfettoSqlParserState*,
132                                    PerfettoSqlArgumentList* args) {
133   std::unique_ptr<PerfettoSqlArgumentList> args_deleter(args);
134 }
135 
OnPerfettoSqlCreateOrAppendIndexedColumn(PerfettoSqlIndexedColumnList * list,PerfettoSqlToken * col)136 PerfettoSqlIndexedColumnList* OnPerfettoSqlCreateOrAppendIndexedColumn(
137     PerfettoSqlIndexedColumnList* list,
138     PerfettoSqlToken* col) {
139   std::unique_ptr<PerfettoSqlIndexedColumnList> owned_list(list);
140   if (!owned_list) {
141     owned_list = std::make_unique<PerfettoSqlIndexedColumnList>();
142   }
143   owned_list->cols.emplace_back(col->ptr, col->n);
144   return owned_list.release();
145 }
146 
OnPerfettoSqlFreeIndexedColumnList(PerfettoSqlParserState *,PerfettoSqlIndexedColumnList * cols)147 void OnPerfettoSqlFreeIndexedColumnList(PerfettoSqlParserState*,
148                                         PerfettoSqlIndexedColumnList* cols) {
149   std::unique_ptr<PerfettoSqlIndexedColumnList> cols_deleter(cols);
150 }
151 
OnPerfettoSqlCreateOrAppendMacroArgument(PerfettoSqlParserState * state,PerfettoSqlMacroArgumentList * list,PerfettoSqlToken * name,PerfettoSqlToken * type)152 PerfettoSqlMacroArgumentList* OnPerfettoSqlCreateOrAppendMacroArgument(
153     PerfettoSqlParserState* state,
154     PerfettoSqlMacroArgumentList* list,
155     PerfettoSqlToken* name,
156     PerfettoSqlToken* type) {
157   std::unique_ptr<PerfettoSqlMacroArgumentList> owned_list(list);
158   if (!owned_list) {
159     owned_list = std::make_unique<PerfettoSqlMacroArgumentList>();
160   }
161   owned_list->args.emplace_back(
162       state->tokenizer.SubstrToken(PerfettoSqlTokenToToken(*name)),
163       state->tokenizer.SubstrToken(PerfettoSqlTokenToToken(*type)));
164   return owned_list.release();
165 }
166 
OnPerfettoSqlFreeMacroArgumentList(PerfettoSqlParserState *,PerfettoSqlMacroArgumentList * list)167 void OnPerfettoSqlFreeMacroArgumentList(PerfettoSqlParserState*,
168                                         PerfettoSqlMacroArgumentList* list) {
169   std::unique_ptr<PerfettoSqlMacroArgumentList> list_deleter(list);
170 }
171 
OnPerfettoSqlSyntaxError(PerfettoSqlParserState * state,PerfettoSqlToken * token)172 void OnPerfettoSqlSyntaxError(PerfettoSqlParserState* state,
173                               PerfettoSqlToken* token) {
174   if (token->n == 0) {
175     state->ErrorAtToken("incomplete input", *token);
176   } else {
177     state->ErrorAtToken("syntax error", *token);
178   }
179 }
180 
OnPerfettoSqlCreateScalarReturnType(PerfettoSqlToken * type)181 PerfettoSqlFnReturnType* OnPerfettoSqlCreateScalarReturnType(
182     PerfettoSqlToken* type) {
183   auto res = std::make_unique<PerfettoSqlFnReturnType>();
184   res->is_table = false;
185   auto parsed = sql_argument::ParseType(base::StringView(type->ptr, type->n));
186   if (!parsed) {
187     return nullptr;
188   }
189   res->scalar_type = *parsed;
190   return res.release();
191 }
192 
OnPerfettoSqlCreateTableReturnType(PerfettoSqlArgumentList * args)193 PerfettoSqlFnReturnType* OnPerfettoSqlCreateTableReturnType(
194     PerfettoSqlArgumentList* args) {
195   std::unique_ptr<PerfettoSqlArgumentList> args_deleter(args);
196   auto res = std::make_unique<PerfettoSqlFnReturnType>();
197   res->is_table = true;
198   res->table_columns = std::move(args->inner);
199   return res.release();
200 }
201 
OnPerfettoSqlFnFreeReturnType(PerfettoSqlParserState *,PerfettoSqlFnReturnType * type)202 void OnPerfettoSqlFnFreeReturnType(PerfettoSqlParserState*,
203                                    PerfettoSqlFnReturnType* type) {
204   std::unique_ptr<PerfettoSqlFnReturnType> type_deleter(type);
205 }
206 
OnPerfettoSqlCreateFunction(PerfettoSqlParserState * state,int replace,PerfettoSqlToken * name,PerfettoSqlArgumentList * args,PerfettoSqlFnReturnType * returns,PerfettoSqlToken * body_start,PerfettoSqlToken * body_end)207 void OnPerfettoSqlCreateFunction(PerfettoSqlParserState* state,
208                                  int replace,
209                                  PerfettoSqlToken* name,
210                                  PerfettoSqlArgumentList* args,
211                                  PerfettoSqlFnReturnType* returns,
212                                  PerfettoSqlToken* body_start,
213                                  PerfettoSqlToken* body_end) {
214   std::unique_ptr<PerfettoSqlArgumentList> args_deleter(args);
215   std::unique_ptr<PerfettoSqlFnReturnType> returns_deleter(returns);
216 
217   // Convert the return type
218   PerfettoSqlParser::CreateFunction::Returns returns_res;
219   returns_res.is_table = returns->is_table;
220   if (returns->is_table) {
221     returns_res.table_columns = std::move(returns->table_columns);
222   } else {
223     returns_res.scalar_type = returns->scalar_type;
224   }
225 
226   // Create a new CreateFunction statement
227   state->current_statement = PerfettoSqlParser::CreateFunction{
228       replace != 0,
229       FunctionPrototype{
230           std::string(name->ptr, name->n),
231           args ? std::move(args->inner)
232                : std::vector<sql_argument::ArgumentDefinition>{},
233       },
234       std::move(returns_res),
235       state->tokenizer.Substr(PerfettoSqlTokenToToken(*body_start),
236                               PerfettoSqlTokenToToken(*body_end),
237                               SqliteTokenizer::EndToken::kInclusive),
238       "",
239   };
240 }
241 
OnPerfettoSqlCreateTable(PerfettoSqlParserState * state,int replace,PerfettoSqlToken * name,PerfettoSqlArgumentList * args,PerfettoSqlToken * body_start,PerfettoSqlToken * body_end)242 void OnPerfettoSqlCreateTable(PerfettoSqlParserState* state,
243                               int replace,
244                               PerfettoSqlToken* name,
245                               PerfettoSqlArgumentList* args,
246                               PerfettoSqlToken* body_start,
247                               PerfettoSqlToken* body_end) {
248   std::unique_ptr<PerfettoSqlArgumentList> args_deleter(args);
249   state->current_statement = PerfettoSqlParser::CreateTable{
250       replace != 0,
251       std::string(name->ptr, name->n),
252       args ? std::move(args->inner)
253            : std::vector<sql_argument::ArgumentDefinition>{},
254       state->tokenizer.Substr(PerfettoSqlTokenToToken(*body_start),
255                               PerfettoSqlTokenToToken(*body_end)),
256   };
257 }
258 
OnPerfettoSqlCreateView(PerfettoSqlParserState * state,int replace,PerfettoSqlToken * create_token,PerfettoSqlToken * name,PerfettoSqlArgumentList * args,PerfettoSqlToken * body_start,PerfettoSqlToken * body_end)259 void OnPerfettoSqlCreateView(PerfettoSqlParserState* state,
260                              int replace,
261                              PerfettoSqlToken* create_token,
262                              PerfettoSqlToken* name,
263                              PerfettoSqlArgumentList* args,
264                              PerfettoSqlToken* body_start,
265                              PerfettoSqlToken* body_end) {
266   std::unique_ptr<PerfettoSqlArgumentList> args_deleter(args);
267 
268   SqlSource header = SqlSource::FromTraceProcessorImplementation(
269       "CREATE VIEW " + std::string(name->ptr, name->n) + " AS ");
270   SqlSource::Rewriter rewriter(state->preprocessor.statement());
271   state->tokenizer.Rewrite(rewriter, PerfettoSqlTokenToToken(*create_token),
272                            PerfettoSqlTokenToToken(*body_start), header);
273 
274   state->current_statement = PerfettoSqlParser::CreateView{
275       replace != 0,
276       std::string(name->ptr, name->n),
277       args ? std::move(args->inner)
278            : std::vector<sql_argument::ArgumentDefinition>(),
279       state->tokenizer.Substr(PerfettoSqlTokenToToken(*body_start),
280                               PerfettoSqlTokenToToken(*body_end)),
281       std::move(rewriter).Build(),
282   };
283 }
284 
OnPerfettoSqlCreateIndex(PerfettoSqlParserState * state,int replace,PerfettoSqlToken * create_token,PerfettoSqlToken * name,PerfettoSqlToken * table_name,PerfettoSqlIndexedColumnList * cols)285 void OnPerfettoSqlCreateIndex(PerfettoSqlParserState* state,
286                               int replace,
287                               PerfettoSqlToken* create_token,
288                               PerfettoSqlToken* name,
289                               PerfettoSqlToken* table_name,
290                               PerfettoSqlIndexedColumnList* cols) {
291   std::unique_ptr<PerfettoSqlIndexedColumnList> cols_deleter(cols);
292 
293   SqlSource header = SqlSource::FromTraceProcessorImplementation(
294       "CREATE INDEX " + std::string(name->ptr, name->n));
295   SqlSource::Rewriter rewriter(state->preprocessor.statement());
296   state->tokenizer.Rewrite(rewriter, PerfettoSqlTokenToToken(*create_token),
297                            PerfettoSqlTokenToToken(*create_token), header,
298                            SqliteTokenizer::EndToken::kExclusive);
299 
300   state->current_statement = PerfettoSqlParser::CreateIndex{
301       replace != 0,
302       std::string(name->ptr, name->n),
303       std::string(table_name->ptr, table_name->n),
304       std::move(cols->cols),
305   };
306 }
307 
OnPerfettoSqlDropIndex(PerfettoSqlParserState * state,PerfettoSqlToken * name,PerfettoSqlToken * table_name)308 void OnPerfettoSqlDropIndex(PerfettoSqlParserState* state,
309                             PerfettoSqlToken* name,
310                             PerfettoSqlToken* table_name) {
311   state->current_statement = PerfettoSqlParser::DropIndex{
312       std::string(name->ptr, name->n),
313       std::string(table_name->ptr, table_name->n),
314   };
315 }
316 
OnPerfettoSqlCreateMacro(PerfettoSqlParserState * state,int replace,PerfettoSqlToken * name,PerfettoSqlMacroArgumentList * args,PerfettoSqlToken * returns,PerfettoSqlToken * body_start,PerfettoSqlToken * body_end)317 void OnPerfettoSqlCreateMacro(PerfettoSqlParserState* state,
318                               int replace,
319                               PerfettoSqlToken* name,
320                               PerfettoSqlMacroArgumentList* args,
321                               PerfettoSqlToken* returns,
322                               PerfettoSqlToken* body_start,
323                               PerfettoSqlToken* body_end) {
324   std::unique_ptr<PerfettoSqlMacroArgumentList> args_deleter(args);
325 
326   state->current_statement = PerfettoSqlParser::CreateMacro{
327       replace != 0,
328       state->tokenizer.SubstrToken(PerfettoSqlTokenToToken(*name)),
329       args ? std::move(args->args)
330            : std::vector<std::pair<SqlSource, SqlSource>>{},
331 
332       state->tokenizer.SubstrToken(PerfettoSqlTokenToToken(*returns)),
333       state->tokenizer.Substr(PerfettoSqlTokenToToken(*body_start),
334                               PerfettoSqlTokenToToken(*body_end)),
335   };
336 }
337 
OnPerfettoSqlInclude(PerfettoSqlParserState * state,PerfettoSqlToken * module_name)338 void OnPerfettoSqlInclude(PerfettoSqlParserState* state,
339                           PerfettoSqlToken* module_name) {
340   state->current_statement =
341       PerfettoSqlParser::Include{std::string(module_name->ptr, module_name->n)};
342 }
343 
344 }  // extern "C"
345 
PerfettoSqlParser(SqlSource source,const base::FlatHashMap<std::string,PerfettoSqlPreprocessor::Macro> & macros)346 PerfettoSqlParser::PerfettoSqlParser(
347     SqlSource source,
348     const base::FlatHashMap<std::string, PerfettoSqlPreprocessor::Macro>&
349         macros)
350     : parser_state_(std::make_unique<PerfettoSqlParserState>(std::move(source),
351                                                              macros)) {}
352 
353 PerfettoSqlParser::~PerfettoSqlParser() = default;
354 
Next()355 bool PerfettoSqlParser::Next() {
356   PERFETTO_DCHECK(parser_state_->status.ok());
357 
358   parser_state_->current_statement = std::nullopt;
359   statement_sql_ = std::nullopt;
360 
361   if (!parser_state_->preprocessor.NextStatement()) {
362     parser_state_->status = parser_state_->preprocessor.status();
363     return false;
364   }
365   parser_state_->tokenizer.Reset(parser_state_->preprocessor.statement());
366 
367   auto* parser = PerfettoSqlParseAlloc(malloc, parser_state_.get());
368   auto guard = base::OnScopeExit([&]() { PerfettoSqlParseFree(parser, free); });
369 
370   enum { kEof, kSemicolon, kNone } eof = kNone;
371   for (Token token = parser_state_->tokenizer.Next();;
372        token = parser_state_->tokenizer.Next()) {
373     if (!parser_state_->status.ok()) {
374       return false;
375     }
376     if (token.IsTerminal()) {
377       if (eof == kNone) {
378         PerfettoSqlParse(parser, TK_SEMI, TokenToPerfettoSqlToken(token));
379         eof = kSemicolon;
380         continue;
381       }
382       if (eof == kSemicolon) {
383         PerfettoSqlParse(parser, 0, TokenToPerfettoSqlToken(token));
384         eof = kEof;
385         continue;
386       }
387       if (!parser_state_->current_statement) {
388         parser_state_->current_statement = SqliteSql{};
389       }
390       statement_sql_ = parser_state_->preprocessor.statement();
391       return true;
392     }
393     if (token.token_type == TK_SPACE) {
394       continue;
395     }
396     PerfettoSqlParse(parser, token.token_type, TokenToPerfettoSqlToken(token));
397   }
398 }
399 
statement() const400 const Statement& PerfettoSqlParser::statement() const {
401   PERFETTO_DCHECK(parser_state_->current_statement.has_value());
402   return *parser_state_->current_statement;
403 }
404 
status() const405 const base::Status& PerfettoSqlParser::status() const {
406   return parser_state_->status;
407 }
408 
409 }  // namespace perfetto::trace_processor
410