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 <cstdint>
20 #include <variant>
21 #include <vector>
22
23 #include "perfetto/base/logging.h"
24 #include "perfetto/ext/base/status_or.h"
25 #include "src/trace_processor/perfetto_sql/parser/perfetto_sql_test_utils.h"
26 #include "src/trace_processor/sqlite/sql_source.h"
27 #include "test/gtest_and_gmock.h"
28
29 namespace perfetto {
30 namespace trace_processor {
31
32 using Result = PerfettoSqlParser::Statement;
33 using Statement = PerfettoSqlParser::Statement;
34 using SqliteSql = PerfettoSqlParser::SqliteSql;
35 using CreateFn = PerfettoSqlParser::CreateFunction;
36 using CreateTable = PerfettoSqlParser::CreateTable;
37 using CreateView = PerfettoSqlParser::CreateView;
38 using Include = PerfettoSqlParser::Include;
39 using CreateMacro = PerfettoSqlParser::CreateMacro;
40 using CreateIndex = PerfettoSqlParser::CreateIndex;
41
42 namespace {
43
44 class PerfettoSqlParserTest : public ::testing::Test {
45 protected:
Parse(SqlSource sql)46 base::StatusOr<std::vector<PerfettoSqlParser::Statement>> Parse(
47 SqlSource sql) {
48 PerfettoSqlParser parser(sql, macros_);
49 std::vector<PerfettoSqlParser::Statement> results;
50 while (parser.Next()) {
51 results.push_back(std::move(parser.statement()));
52 }
53 if (!parser.status().ok()) {
54 return parser.status();
55 }
56 return results;
57 }
58
59 base::FlatHashMap<std::string, PerfettoSqlPreprocessor::Macro> macros_;
60 };
61
TEST_F(PerfettoSqlParserTest,Empty)62 TEST_F(PerfettoSqlParserTest, Empty) {
63 ASSERT_THAT(*Parse(SqlSource::FromExecuteQuery("")), testing::IsEmpty());
64 }
65
TEST_F(PerfettoSqlParserTest,SemiColonTerminatedStatement)66 TEST_F(PerfettoSqlParserTest, SemiColonTerminatedStatement) {
67 SqlSource res = SqlSource::FromExecuteQuery("SELECT * FROM slice;");
68 PerfettoSqlParser parser(res, macros_);
69 ASSERT_TRUE(parser.Next());
70 ASSERT_EQ(parser.statement(), Statement{SqliteSql{}});
71 ASSERT_EQ(parser.statement_sql(), FindSubstr(res, "SELECT * FROM slice;"));
72 }
73
TEST_F(PerfettoSqlParserTest,MultipleStmts)74 TEST_F(PerfettoSqlParserTest, MultipleStmts) {
75 auto res =
76 SqlSource::FromExecuteQuery("SELECT * FROM slice; SELECT * FROM s");
77 PerfettoSqlParser parser(res, macros_);
78 ASSERT_TRUE(parser.Next());
79 ASSERT_EQ(parser.statement(), Statement{SqliteSql{}});
80 ASSERT_EQ(parser.statement_sql().sql(),
81 FindSubstr(res, "SELECT * FROM slice;").sql());
82 ASSERT_TRUE(parser.Next());
83 ASSERT_EQ(parser.statement(), Statement{SqliteSql{}});
84 ASSERT_EQ(parser.statement_sql().sql(),
85 FindSubstr(res, "SELECT * FROM s").sql());
86 }
87
TEST_F(PerfettoSqlParserTest,IgnoreOnlySpace)88 TEST_F(PerfettoSqlParserTest, IgnoreOnlySpace) {
89 auto res = SqlSource::FromExecuteQuery(" ; SELECT * FROM s; ; ;");
90 PerfettoSqlParser parser(res, macros_);
91 ASSERT_TRUE(parser.Next());
92 ASSERT_EQ(parser.statement(), Statement{SqliteSql{}});
93 ASSERT_EQ(parser.statement_sql().sql(),
94 FindSubstr(res, "SELECT * FROM s;").sql());
95 }
96
TEST_F(PerfettoSqlParserTest,CreatePerfettoFunctionScalar)97 TEST_F(PerfettoSqlParserTest, CreatePerfettoFunctionScalar) {
98 auto res = SqlSource::FromExecuteQuery(
99 "create perfetto function foo() returns INT as select 1");
100 ASSERT_THAT(*Parse(res), testing::ElementsAre(CreateFn{
101 false, FunctionPrototype{"foo", {}}, "INT",
102 FindSubstr(res, "select 1"), false}));
103
104 res = SqlSource::FromExecuteQuery(
105 "create perfetto function bar(x INT, y LONG) returns STRING as "
106 "select 'foo'");
107 ASSERT_THAT(*Parse(res),
108 testing::ElementsAre(
109 CreateFn{false,
110 FunctionPrototype{
111 "bar",
112 {
113 {"$x", sql_argument::Type::kInt},
114 {"$y", sql_argument::Type::kLong},
115 },
116 },
117 "STRING", FindSubstr(res, "select 'foo'"), false}));
118
119 res = SqlSource::FromExecuteQuery(
120 "CREATE perfetto FuNcTiOn bar(x INT, y LONG) returnS STRING As "
121 "select 'foo'");
122 ASSERT_THAT(*Parse(res),
123 testing::ElementsAre(
124 CreateFn{false,
125 FunctionPrototype{
126 "bar",
127 {
128 {"$x", sql_argument::Type::kInt},
129 {"$y", sql_argument::Type::kLong},
130 },
131 },
132 "STRING", FindSubstr(res, "select 'foo'"), false}));
133 }
134
TEST_F(PerfettoSqlParserTest,CreateOrReplacePerfettoFunctionScalar)135 TEST_F(PerfettoSqlParserTest, CreateOrReplacePerfettoFunctionScalar) {
136 auto res = SqlSource::FromExecuteQuery(
137 "create or replace perfetto function foo() returns INT as select 1");
138 ASSERT_THAT(*Parse(res), testing::ElementsAre(CreateFn{
139 true, FunctionPrototype{"foo", {}}, "INT",
140 FindSubstr(res, "select 1"), false}));
141 }
142
TEST_F(PerfettoSqlParserTest,CreatePerfettoFunctionScalarError)143 TEST_F(PerfettoSqlParserTest, CreatePerfettoFunctionScalarError) {
144 auto res = SqlSource::FromExecuteQuery(
145 "create perfetto function foo( returns INT as select 1");
146 ASSERT_FALSE(Parse(res).status().ok());
147
148 res = SqlSource::FromExecuteQuery(
149 "create perfetto function foo(x INT) as select 1");
150 ASSERT_FALSE(Parse(res).status().ok());
151
152 res = SqlSource::FromExecuteQuery(
153 "create perfetto function foo(x INT) returns INT");
154 ASSERT_FALSE(Parse(res).status().ok());
155 }
156
TEST_F(PerfettoSqlParserTest,CreatePerfettoFunctionAndOther)157 TEST_F(PerfettoSqlParserTest, CreatePerfettoFunctionAndOther) {
158 auto res = SqlSource::FromExecuteQuery(
159 "create perfetto function foo() returns INT as select 1; select foo()");
160 PerfettoSqlParser parser(res, macros_);
161 ASSERT_TRUE(parser.Next());
162 CreateFn fn{false, FunctionPrototype{"foo", {}}, "INT",
163 FindSubstr(res, "select 1"), false};
164 ASSERT_EQ(parser.statement(), Statement{fn});
165 ASSERT_EQ(
166 parser.statement_sql().sql(),
167 FindSubstr(res, "create perfetto function foo() returns INT as select 1")
168 .sql());
169 ASSERT_TRUE(parser.Next());
170 ASSERT_EQ(parser.statement(), Statement{SqliteSql{}});
171 ASSERT_EQ(parser.statement_sql().sql(),
172 FindSubstr(res, "select foo()").sql());
173 }
174
TEST_F(PerfettoSqlParserTest,IncludePerfettoTrivial)175 TEST_F(PerfettoSqlParserTest, IncludePerfettoTrivial) {
176 auto res =
177 SqlSource::FromExecuteQuery("include perfetto module cheese.bre_ad;");
178 ASSERT_THAT(*Parse(res), testing::ElementsAre(Include{"cheese.bre_ad"}));
179 }
180
TEST_F(PerfettoSqlParserTest,IncludePerfettoErrorAdditionalChars)181 TEST_F(PerfettoSqlParserTest, IncludePerfettoErrorAdditionalChars) {
182 auto res = SqlSource::FromExecuteQuery(
183 "include perfetto module cheese.bre_ad blabla;");
184 ASSERT_FALSE(Parse(res).status().ok());
185 }
186
TEST_F(PerfettoSqlParserTest,IncludePerfettoErrorWrongModuleName)187 TEST_F(PerfettoSqlParserTest, IncludePerfettoErrorWrongModuleName) {
188 auto res =
189 SqlSource::FromExecuteQuery("include perfetto module chees*e.bre_ad;");
190 ASSERT_FALSE(Parse(res).status().ok());
191 }
192
TEST_F(PerfettoSqlParserTest,CreatePerfettoMacro)193 TEST_F(PerfettoSqlParserTest, CreatePerfettoMacro) {
194 auto res = SqlSource::FromExecuteQuery(
195 "create perfetto macro foo(a1 Expr, b1 TableOrSubquery,c3_d "
196 "TableOrSubquery2 ) returns TableOrSubquery3 as random sql snippet");
197 PerfettoSqlParser parser(res, macros_);
198 ASSERT_TRUE(parser.Next());
199 ASSERT_EQ(
200 parser.statement(),
201 Statement(CreateMacro{
202 false,
203 FindSubstr(res, "foo"),
204 {
205 {FindSubstr(res, "a1"), FindSubstr(res, "Expr")},
206 {FindSubstr(res, "b1"), FindSubstr(res, "TableOrSubquery")},
207 {FindSubstr(res, "c3_d"), FindSubstr(res, "TableOrSubquery2")},
208 },
209 FindSubstr(res, "TableOrSubquery3"),
210 FindSubstr(res, "random sql snippet")}));
211 ASSERT_FALSE(parser.Next());
212 }
213
TEST_F(PerfettoSqlParserTest,CreateOrReplacePerfettoMacro)214 TEST_F(PerfettoSqlParserTest, CreateOrReplacePerfettoMacro) {
215 auto res = SqlSource::FromExecuteQuery(
216 "create or replace perfetto macro foo() returns Expr as 1");
217 PerfettoSqlParser parser(res, macros_);
218 ASSERT_TRUE(parser.Next());
219 ASSERT_EQ(parser.statement(), Statement(CreateMacro{true,
220 FindSubstr(res, "foo"),
221 {},
222 FindSubstr(res, "Expr"),
223 FindSubstr(res, "1")}));
224 ASSERT_FALSE(parser.Next());
225 }
226
TEST_F(PerfettoSqlParserTest,CreatePerfettoMacroAndOther)227 TEST_F(PerfettoSqlParserTest, CreatePerfettoMacroAndOther) {
228 auto res = SqlSource::FromExecuteQuery(
229 "create perfetto macro foo() returns sql1 as random sql snippet; "
230 "select 1");
231 PerfettoSqlParser parser(res, macros_);
232 ASSERT_TRUE(parser.Next());
233 ASSERT_EQ(parser.statement(), Statement(CreateMacro{
234 false,
235 FindSubstr(res, "foo"),
236 {},
237 FindSubstr(res, "sql1"),
238 FindSubstr(res, "random sql snippet"),
239 }));
240 ASSERT_TRUE(parser.Next());
241 ASSERT_EQ(parser.statement(), Statement(SqliteSql{}));
242 ASSERT_EQ(parser.statement_sql(), FindSubstr(res, "select 1"));
243 ASSERT_FALSE(parser.Next());
244 }
245
TEST_F(PerfettoSqlParserTest,CreatePerfettoTable)246 TEST_F(PerfettoSqlParserTest, CreatePerfettoTable) {
247 auto res = SqlSource::FromExecuteQuery(
248 "CREATE PERFETTO TABLE foo AS SELECT 42 AS bar");
249 PerfettoSqlParser parser(res, macros_);
250 ASSERT_TRUE(parser.Next());
251 ASSERT_EQ(parser.statement(),
252 Statement(CreateTable{
253 false, "foo", FindSubstr(res, "SELECT 42 AS bar"), {}}));
254 ASSERT_FALSE(parser.Next());
255 }
256
TEST_F(PerfettoSqlParserTest,CreateOrReplacePerfettoTable)257 TEST_F(PerfettoSqlParserTest, CreateOrReplacePerfettoTable) {
258 auto res = SqlSource::FromExecuteQuery(
259 "CREATE OR REPLACE PERFETTO TABLE foo AS SELECT 42 AS bar");
260 PerfettoSqlParser parser(res, macros_);
261 ASSERT_TRUE(parser.Next());
262 ASSERT_EQ(parser.statement(),
263 Statement(CreateTable{
264 true, "foo", FindSubstr(res, "SELECT 42 AS bar"), {}}));
265 ASSERT_FALSE(parser.Next());
266 }
267
TEST_F(PerfettoSqlParserTest,CreatePerfettoTableWithSchema)268 TEST_F(PerfettoSqlParserTest, CreatePerfettoTableWithSchema) {
269 auto res = SqlSource::FromExecuteQuery(
270 "CREATE PERFETTO TABLE foo(bar INT) AS SELECT 42 AS bar");
271 PerfettoSqlParser parser(res, macros_);
272 ASSERT_TRUE(parser.Next());
273 ASSERT_EQ(parser.statement(), Statement(CreateTable{
274 false,
275 "foo",
276 FindSubstr(res, "SELECT 42 AS bar"),
277 {{"$bar", sql_argument::Type::kInt}},
278 }));
279 ASSERT_FALSE(parser.Next());
280 }
281
TEST_F(PerfettoSqlParserTest,CreatePerfettoTableAndOther)282 TEST_F(PerfettoSqlParserTest, CreatePerfettoTableAndOther) {
283 auto res = SqlSource::FromExecuteQuery(
284 "CREATE PERFETTO TABLE foo AS SELECT 42 AS bar; select 1");
285 PerfettoSqlParser parser(res, macros_);
286 ASSERT_TRUE(parser.Next());
287 ASSERT_EQ(parser.statement(),
288 Statement(CreateTable{
289 false, "foo", FindSubstr(res, "SELECT 42 AS bar"), {}}));
290 ASSERT_TRUE(parser.Next());
291 ASSERT_EQ(parser.statement(), Statement(SqliteSql{}));
292 ASSERT_EQ(parser.statement_sql(), FindSubstr(res, "select 1"));
293 ASSERT_FALSE(parser.Next());
294 }
295
TEST_F(PerfettoSqlParserTest,CreatePerfettoView)296 TEST_F(PerfettoSqlParserTest, CreatePerfettoView) {
297 auto res = SqlSource::FromExecuteQuery(
298 "CREATE PERFETTO VIEW foo AS SELECT 42 AS bar");
299 PerfettoSqlParser parser(res, macros_);
300 ASSERT_TRUE(parser.Next());
301 ASSERT_EQ(
302 parser.statement(),
303 Statement(CreateView{
304 false,
305 "foo",
306 SqlSource::FromExecuteQuery("SELECT 42 AS bar"),
307 SqlSource::FromExecuteQuery("CREATE VIEW foo AS SELECT 42 AS bar"),
308 {}}));
309 ASSERT_FALSE(parser.Next());
310 }
311
TEST_F(PerfettoSqlParserTest,CreateOrReplacePerfettoView)312 TEST_F(PerfettoSqlParserTest, CreateOrReplacePerfettoView) {
313 auto res = SqlSource::FromExecuteQuery(
314 "CREATE OR REPLACE PERFETTO VIEW foo AS SELECT 42 AS bar");
315 PerfettoSqlParser parser(res, macros_);
316 ASSERT_TRUE(parser.Next());
317 ASSERT_EQ(
318 parser.statement(),
319 Statement(CreateView{
320 true,
321 "foo",
322 SqlSource::FromExecuteQuery("SELECT 42 AS bar"),
323 SqlSource::FromExecuteQuery("CREATE VIEW foo AS SELECT 42 AS bar"),
324 {}}));
325 ASSERT_FALSE(parser.Next());
326 }
327
TEST_F(PerfettoSqlParserTest,CreatePerfettoViewAndOther)328 TEST_F(PerfettoSqlParserTest, CreatePerfettoViewAndOther) {
329 auto res = SqlSource::FromExecuteQuery(
330 "CREATE PERFETTO VIEW foo AS SELECT 42 AS bar; select 1");
331 PerfettoSqlParser parser(res, macros_);
332 ASSERT_TRUE(parser.Next());
333 ASSERT_EQ(
334 parser.statement(),
335 Statement(CreateView{
336 false,
337 "foo",
338 SqlSource::FromExecuteQuery("SELECT 42 AS bar"),
339 SqlSource::FromExecuteQuery("CREATE VIEW foo AS SELECT 42 AS bar"),
340 {}}));
341 ASSERT_TRUE(parser.Next());
342 ASSERT_EQ(parser.statement(), Statement(SqliteSql{}));
343 ASSERT_EQ(parser.statement_sql(), FindSubstr(res, "select 1"));
344 ASSERT_FALSE(parser.Next());
345 }
346
TEST_F(PerfettoSqlParserTest,CreatePerfettoViewWithSchema)347 TEST_F(PerfettoSqlParserTest, CreatePerfettoViewWithSchema) {
348 auto res = SqlSource::FromExecuteQuery(
349 "CREATE PERFETTO VIEW foo(foo STRING, bar INT) AS SELECT 'a' as foo, 42 "
350 "AS bar");
351 PerfettoSqlParser parser(res, macros_);
352 ASSERT_TRUE(parser.Next());
353 ASSERT_EQ(parser.statement(),
354 Statement(CreateView{
355 false,
356 "foo",
357 SqlSource::FromExecuteQuery("SELECT 'a' as foo, 42 AS bar"),
358 SqlSource::FromExecuteQuery(
359 "CREATE VIEW foo AS SELECT 'a' as foo, 42 AS bar"),
360 {{"$foo", sql_argument::Type::kString},
361 {"$bar", sql_argument::Type::kInt}},
362 }));
363 ASSERT_FALSE(parser.Next());
364 }
365
TEST_F(PerfettoSqlParserTest,ParseComplexArgumentType)366 TEST_F(PerfettoSqlParserTest, ParseComplexArgumentType) {
367 auto res = SqlSource::FromExecuteQuery(
368 "CREATE PERFETTO VIEW foo(foo JOINID(foo.bar), bar LONG) AS SELECT "
369 "'a' as foo, 42 "
370 "AS bar");
371 PerfettoSqlParser parser(res, macros_);
372 ASSERT_TRUE(parser.Next());
373 ASSERT_EQ(parser.statement(),
374 Statement(CreateView{
375 false,
376 "foo",
377 SqlSource::FromExecuteQuery("SELECT 'a' as foo, 42 AS bar"),
378 SqlSource::FromExecuteQuery(
379 "CREATE VIEW foo AS SELECT 'a' as foo, 42 AS bar"),
380 {{"$foo", sql_argument::Type::kLong},
381 {"$bar", sql_argument::Type::kLong}},
382 }));
383 ASSERT_FALSE(parser.Next());
384 }
385
386 } // namespace
387 } // namespace trace_processor
388 } // namespace perfetto
389