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