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