• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 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/sqlite/create_view_function.h"
18 
19 #include <numeric>
20 
21 #include "perfetto/base/status.h"
22 #include "perfetto/ext/base/string_utils.h"
23 #include "perfetto/ext/base/string_view.h"
24 #include "perfetto/trace_processor/basic_types.h"
25 #include "src/trace_processor/sqlite/create_function_internal.h"
26 #include "src/trace_processor/sqlite/scoped_db.h"
27 #include "src/trace_processor/sqlite/sqlite_table.h"
28 #include "src/trace_processor/sqlite/sqlite_utils.h"
29 #include "src/trace_processor/util/status_macros.h"
30 
31 namespace perfetto {
32 namespace trace_processor {
33 
34 namespace {
35 
36 class CreatedViewFunction : public SqliteTable {
37  public:
38   class Cursor : public SqliteTable::Cursor {
39    public:
40     explicit Cursor(CreatedViewFunction* table);
41     ~Cursor() override;
42 
43     int Filter(const QueryConstraints& qc,
44                sqlite3_value**,
45                FilterHistory) override;
46     int Next() override;
47     int Eof() override;
48     int Column(sqlite3_context* context, int N) override;
49 
50    private:
51     sqlite3_stmt* stmt_ = nullptr;
52     CreatedViewFunction* table_ = nullptr;
53     bool is_eof_ = false;
54   };
55 
56   CreatedViewFunction(sqlite3*, CreateViewFunction::State* state);
57   ~CreatedViewFunction() override;
58 
59   base::Status Init(int argc, const char* const* argv, Schema*) override;
60   std::unique_ptr<SqliteTable::Cursor> CreateCursor() override;
61   int BestIndex(const QueryConstraints& qc, BestIndexInfo* info) override;
62 
Register(sqlite3 * db,CreateViewFunction::State * state)63   static void Register(sqlite3* db, CreateViewFunction::State* state) {
64     SqliteTable::Register<CreatedViewFunction>(
65         db, state, "internal_view_function_impl", false, true);
66   }
67 
68  private:
69   Schema CreateSchema();
70 
71   sqlite3* db_ = nullptr;
72 
73   Prototype prototype_;
74   std::vector<Prototype::Argument> return_values_;
75 
76   std::string prototype_str_;
77   std::string sql_defn_str_;
78 
79   CreateViewFunction::State* state_;
80 };
81 
CreatedViewFunction(sqlite3 * db,CreateViewFunction::State * state)82 CreatedViewFunction::CreatedViewFunction(sqlite3* db,
83                                          CreateViewFunction::State* state)
84     : db_(db), state_(state) {}
85 CreatedViewFunction::~CreatedViewFunction() = default;
86 
Init(int argc,const char * const * argv,Schema * schema)87 base::Status CreatedViewFunction::Init(int argc,
88                                        const char* const* argv,
89                                        Schema* schema) {
90   // The first three args are SQLite ones which we ignore.
91   PERFETTO_CHECK(argc == 6);
92 
93   prototype_str_ = argv[3];
94   std::string return_prototype_str = argv[4];
95   sql_defn_str_ = argv[5];
96 
97   // SQLite gives us strings with quotes included (i.e. 'string'). Strip these
98   // from the front and back.
99   prototype_str_ = prototype_str_.substr(1, prototype_str_.size() - 2);
100   return_prototype_str =
101       return_prototype_str.substr(1, return_prototype_str.size() - 2);
102   sql_defn_str_ = sql_defn_str_.substr(1, sql_defn_str_.size() - 2);
103 
104   // Parse all the arguments into a more friendly form.
105   base::Status status =
106       ParsePrototype(base::StringView(prototype_str_), prototype_);
107   if (!status.ok()) {
108     return base::ErrStatus("CREATE_VIEW_FUNCTION[prototype=%s]: %s",
109                            prototype_str_.c_str(), status.c_message());
110   }
111 
112   // Parse the return type into a enum format.
113   status = ParseArgs(return_prototype_str, return_values_);
114   if (!status.ok()) {
115     return base::ErrStatus(
116         "CREATE_VIEW_FUNCTION[prototype=%s, return=%s]: unknown return type "
117         "specified",
118         prototype_str_.c_str(), return_prototype_str.c_str());
119   }
120 
121   // Now we've parsed prototype and return values, create the schema.
122   *schema = CreateSchema();
123 
124   return base::OkStatus();
125 }
126 
CreateSchema()127 SqliteTable::Schema CreatedViewFunction::CreateSchema() {
128   std::vector<Column> columns;
129   for (size_t i = 0; i < return_values_.size(); ++i) {
130     const auto& ret = return_values_[i];
131     columns.push_back(Column(columns.size(), ret.name, ret.type, false));
132   }
133   for (size_t i = 0; i < prototype_.arguments.size(); ++i) {
134     const auto& arg = prototype_.arguments[i];
135     columns.push_back(Column(columns.size(), arg.name, arg.type, true));
136   }
137 
138   std::vector<size_t> primary_keys(return_values_.size());
139   std::iota(primary_keys.begin(), primary_keys.end(), 0);
140 
141   return SqliteTable::Schema(std::move(columns), std::move(primary_keys));
142 }
143 
CreateCursor()144 std::unique_ptr<SqliteTable::Cursor> CreatedViewFunction::CreateCursor() {
145   return std::unique_ptr<Cursor>(new Cursor(this));
146 }
147 
BestIndex(const QueryConstraints & qc,BestIndexInfo * info)148 int CreatedViewFunction::BestIndex(const QueryConstraints& qc,
149                                    BestIndexInfo* info) {
150   for (size_t i = 0; i < info->sqlite_omit_constraint.size(); ++i) {
151     size_t col = static_cast<size_t>(qc.constraints()[i].column);
152     if (schema().columns()[col].hidden()) {
153       info->sqlite_omit_constraint[i] = true;
154     }
155   }
156   return SQLITE_OK;
157 }
158 
Cursor(CreatedViewFunction * table)159 CreatedViewFunction::Cursor::Cursor(CreatedViewFunction* table)
160     : SqliteTable::Cursor(table), table_(table) {}
161 
162 CreatedViewFunction::Cursor::~Cursor() = default;
163 
Filter(const QueryConstraints & qc,sqlite3_value ** argv,FilterHistory)164 int CreatedViewFunction::Cursor::Filter(const QueryConstraints& qc,
165                                         sqlite3_value** argv,
166                                         FilterHistory) {
167   auto col_to_arg_idx = [this](int col) {
168     return static_cast<size_t>(col) - table_->return_values_.size();
169   };
170 
171   size_t seen_hidden_constraints = 0;
172   for (size_t i = 0; i < qc.constraints().size(); ++i) {
173     const auto& cs = qc.constraints()[i];
174 
175     // Only consider hidden columns (i.e. input parameters) as we're delegating
176     // the rest to SQLite.
177     if (!table_->schema().columns()[static_cast<size_t>(cs.column)].hidden())
178       continue;
179 
180     // We only support equality constraints as we're expecting "input arguments"
181     // to our "function".
182     if (!sqlite_utils::IsOpEq(cs.op)) {
183       table_->SetErrorMessage(
184           sqlite3_mprintf("%s: non-equality constraint passed",
185                           table_->prototype_.function_name.c_str()));
186       return SQLITE_ERROR;
187     }
188 
189     SqlValue::Type type =
190         sqlite_utils::SqliteTypeToSqlValueType(sqlite3_value_type(argv[i]));
191     if (type != table_->prototype_.arguments[col_to_arg_idx(cs.column)].type) {
192       table_->SetErrorMessage(
193           sqlite3_mprintf("%s: type of input argument does not match",
194                           table_->prototype_.function_name.c_str()));
195       return SQLITE_ERROR;
196     }
197 
198     seen_hidden_constraints++;
199   }
200 
201   // Verify that we saw one valid constriant for every input argument.
202   if (seen_hidden_constraints < table_->prototype_.arguments.size()) {
203     table_->SetErrorMessage(
204         sqlite3_mprintf("%s: missing value for input argument",
205                         table_->prototype_.function_name.c_str()));
206     return SQLITE_ERROR;
207   }
208 
209   // Prepare the SQL definition as a statement using SQLite.
210   // TODO(lalitm): see if we can reuse this prepared statement rather than
211   // creating it very time.
212   // TODO(lalitm): measure and implement whether it would be a good idea to
213   // forward constraints here when we build the nested query.
214   ScopedStmt stmt;
215   int ret = sqlite3_prepare_v2(table_->db_, table_->sql_defn_str_.data(),
216                                static_cast<int>(table_->sql_defn_str_.size()),
217                                &stmt_, nullptr);
218   stmt.reset(stmt_);
219   if (ret != SQLITE_OK) {
220     table_->SetErrorMessage(sqlite3_mprintf(
221         "%s: SQLite error when preparing statement %s",
222         table_->prototype_.function_name.c_str(), sqlite3_errmsg(table_->db_)));
223     return SQLITE_ERROR;
224   }
225 
226   // Bind all the arguments to the appropriate places in the function.
227   for (size_t i = 0; i < qc.constraints().size(); ++i) {
228     const auto& cs = qc.constraints()[i];
229     const auto& arg = table_->prototype_.arguments[col_to_arg_idx(cs.column)];
230     auto status = MaybeBindArgument(stmt_, table_->prototype_.function_name,
231                                     arg, argv[i]);
232     if (!status.ok()) {
233       table_->SetErrorMessage(sqlite3_mprintf("%s", status.c_message()));
234       return SQLITE_ERROR;
235     }
236   }
237 
238   ret = Next();
239   if (ret != SQLITE_OK)
240     return ret;
241 
242   // Keep track of the scoped statements in the stmts vector so we can clean
243   // all these up before destroying trace processor.
244   table_->state_->erase(table_->prototype_.function_name);
245   table_->state_->emplace(table_->prototype_.function_name, std::move(stmt));
246 
247   return SQLITE_OK;
248 }
249 
Next()250 int CreatedViewFunction::Cursor::Next() {
251   int ret = sqlite3_step(stmt_);
252   is_eof_ = ret == SQLITE_DONE;
253   if (ret != SQLITE_ROW && ret != SQLITE_DONE) {
254     table_->SetErrorMessage(sqlite3_mprintf(
255         "%s: SQLite error while stepping statement: %s",
256         table_->prototype_.function_name.c_str(), sqlite3_errmsg(table_->db_)));
257     return ret;
258   }
259   return SQLITE_OK;
260 }
261 
Eof()262 int CreatedViewFunction::Cursor::Eof() {
263   return is_eof_;
264 }
265 
Column(sqlite3_context * ctx,int i)266 int CreatedViewFunction::Cursor::Column(sqlite3_context* ctx, int i) {
267   size_t idx = static_cast<size_t>(i);
268   if (idx < table_->return_values_.size()) {
269     sqlite3_result_value(ctx, sqlite3_column_value(stmt_, i));
270   } else {
271     sqlite3_result_null(ctx);
272   }
273   return SQLITE_OK;
274 }
275 
276 }  // namespace
277 
Run(CreateViewFunction::Context * ctx,size_t argc,sqlite3_value ** argv,SqlValue &,Destructors &)278 base::Status CreateViewFunction::Run(CreateViewFunction::Context* ctx,
279                                      size_t argc,
280                                      sqlite3_value** argv,
281                                      SqlValue&,
282                                      Destructors&) {
283   if (argc != 3) {
284     return base::ErrStatus(
285         "CREATE_VIEW_FUNCTION: invalid number of args; expected %u, received "
286         "%zu",
287         3u, argc);
288   }
289 
290   sqlite3_value* prototype_value = argv[0];
291   sqlite3_value* return_prototype_value = argv[1];
292   sqlite3_value* sql_defn_value = argv[2];
293 
294   // Type check all the arguments.
295   {
296     auto type_check = [prototype_value](sqlite3_value* value,
297                                         SqlValue::Type type, const char* desc) {
298       base::Status status = TypeCheckSqliteValue(value, type);
299       if (!status.ok()) {
300         return base::ErrStatus("CREATE_VIEW_FUNCTION[prototype=%s]: %s %s",
301                                sqlite3_value_text(prototype_value), desc,
302                                status.c_message());
303       }
304       return base::OkStatus();
305     };
306 
307     RETURN_IF_ERROR(type_check(prototype_value, SqlValue::Type::kString,
308                                "function prototype (first argument)"));
309     RETURN_IF_ERROR(type_check(return_prototype_value, SqlValue::Type::kString,
310                                "return prototype (second argument)"));
311     RETURN_IF_ERROR(type_check(sql_defn_value, SqlValue::Type::kString,
312                                "SQL definition (third argument)"));
313   }
314 
315   // Extract the arguments from the value wrappers.
316   auto extract_string = [](sqlite3_value* value) -> const char* {
317     return reinterpret_cast<const char*>(sqlite3_value_text(value));
318   };
319   const char* prototype_str = extract_string(prototype_value);
320   const char* return_prototype_str = extract_string(return_prototype_value);
321   const char* sql_defn_str = extract_string(sql_defn_value);
322 
323   base::StringView function_name;
324   RETURN_IF_ERROR(ParseFunctionName(prototype_str, function_name));
325 
326   base::StackString<1024> sql(
327       "CREATE OR REPLACE VIRTUAL TABLE %s USING "
328       "INTERNAL_VIEW_FUNCTION_IMPL('%s', '%s', '%s');",
329       function_name.ToStdString().c_str(), prototype_str, return_prototype_str,
330       sql_defn_str);
331 
332   ScopedSqliteString errmsg;
333   char* errmsg_raw = nullptr;
334   int ret = sqlite3_exec(ctx->db, sql.c_str(), nullptr, nullptr, &errmsg_raw);
335   errmsg.reset(errmsg_raw);
336   if (ret != SQLITE_OK)
337     return base::ErrStatus("%s", errmsg.get());
338 
339   // CREATE_VIEW_FUNCTION doesn't have a return value so just don't sent |out|.
340   return base::OkStatus();
341 }
342 
RegisterTable(sqlite3 * db,CreateViewFunction::State * state)343 void CreateViewFunction::RegisterTable(sqlite3* db,
344                                        CreateViewFunction::State* state) {
345   CreatedViewFunction::Register(db, state);
346 }
347 
348 }  // namespace trace_processor
349 }  // namespace perfetto
350