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