• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2019 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_function.h"
18 
19 #include "perfetto/base/status.h"
20 #include "perfetto/trace_processor/basic_types.h"
21 #include "src/trace_processor/sqlite/create_function_internal.h"
22 #include "src/trace_processor/sqlite/scoped_db.h"
23 #include "src/trace_processor/sqlite/sqlite_utils.h"
24 #include "src/trace_processor/util/status_macros.h"
25 
26 namespace perfetto {
27 namespace trace_processor {
28 
29 namespace {
30 
31 struct CreatedFunction : public SqlFunction {
32   struct Context {
33     sqlite3* db;
34     Prototype prototype;
35     SqlValue::Type return_type;
36     std::string sql;
37     sqlite3_stmt* stmt;
38   };
39 
40   static base::Status Run(Context* ctx,
41                           size_t argc,
42                           sqlite3_value** argv,
43                           SqlValue& out,
44                           Destructors&);
45   static base::Status Cleanup(Context*);
46 };
47 
Run(CreatedFunction::Context * ctx,size_t argc,sqlite3_value ** argv,SqlValue & out,Destructors &)48 base::Status CreatedFunction::Run(CreatedFunction::Context* ctx,
49                                   size_t argc,
50                                   sqlite3_value** argv,
51                                   SqlValue& out,
52                                   Destructors&) {
53   if (argc != ctx->prototype.arguments.size()) {
54     return base::ErrStatus(
55         "%s: invalid number of args; expected %zu, received %zu",
56         ctx->prototype.function_name.c_str(), ctx->prototype.arguments.size(),
57         argc);
58   }
59 
60   // Type check all the arguments.
61   for (size_t i = 0; i < argc; ++i) {
62     sqlite3_value* arg = argv[i];
63     base::Status status =
64         TypeCheckSqliteValue(arg, ctx->prototype.arguments[i].type);
65     if (!status.ok()) {
66       return base::ErrStatus("%s[arg=%s]: argument %zu %s",
67                              ctx->prototype.function_name.c_str(),
68                              sqlite3_value_text(arg), i, status.c_message());
69     }
70   }
71 
72   // Bind all the arguments to the appropriate places in the function.
73   for (size_t i = 0; i < argc; ++i) {
74     RETURN_IF_ERROR(MaybeBindArgument(ctx->stmt, ctx->prototype.function_name,
75                                       ctx->prototype.arguments[i], argv[i]));
76   }
77 
78   int ret = sqlite3_step(ctx->stmt);
79   RETURN_IF_ERROR(
80       SqliteRetToStatus(ctx->db, ctx->prototype.function_name, ret));
81   if (ret == SQLITE_DONE)
82     // No return value means we just return don't set |out|.
83     return base::OkStatus();
84 
85   PERFETTO_DCHECK(ret == SQLITE_ROW);
86   size_t col_count = static_cast<size_t>(sqlite3_column_count(ctx->stmt));
87   if (col_count != 1) {
88     return base::ErrStatus(
89         "%s: SQL definition should only return one column: returned %zu "
90         "columns",
91         ctx->prototype.function_name.c_str(), col_count);
92   }
93 
94   out = sqlite_utils::SqliteValueToSqlValue(sqlite3_column_value(ctx->stmt, 0));
95   return base::OkStatus();
96 }
97 
Cleanup(CreatedFunction::Context * ctx)98 base::Status CreatedFunction::Cleanup(CreatedFunction::Context* ctx) {
99   int ret = sqlite3_step(ctx->stmt);
100   RETURN_IF_ERROR(
101       SqliteRetToStatus(ctx->db, ctx->prototype.function_name, ret));
102   if (ret == SQLITE_ROW) {
103     return base::ErrStatus(
104         "%s: multiple values were returned when executing function body",
105         ctx->prototype.function_name.c_str());
106   }
107   PERFETTO_DCHECK(ret == SQLITE_DONE);
108 
109   // Make sure to reset the statement to remove any bindings.
110   ret = sqlite3_reset(ctx->stmt);
111   if (ret != SQLITE_OK) {
112     return base::ErrStatus("%s: error while resetting metric",
113                            ctx->prototype.function_name.c_str());
114   }
115   return base::OkStatus();
116 }
117 
118 }  // namespace
119 
operator ()(const NameAndArgc & s) const120 size_t CreateFunction::NameAndArgc::Hasher::operator()(
121     const NameAndArgc& s) const noexcept {
122   base::Hash hash;
123   hash.Update(s.name.data(), s.name.size());
124   hash.Update(s.argc);
125   return static_cast<size_t>(hash.digest());
126 }
127 
Run(CreateFunction::Context * ctx,size_t argc,sqlite3_value ** argv,SqlValue &,Destructors &)128 base::Status CreateFunction::Run(CreateFunction::Context* ctx,
129                                  size_t argc,
130                                  sqlite3_value** argv,
131                                  SqlValue&,
132                                  Destructors&) {
133   if (argc != 3) {
134     return base::ErrStatus(
135         "CREATE_FUNCTION: invalid number of args; expected %u, received %zu",
136         3u, argc);
137   }
138 
139   sqlite3_value* prototype_value = argv[0];
140   sqlite3_value* return_type_value = argv[1];
141   sqlite3_value* sql_defn_value = argv[2];
142 
143   // Type check all the arguments.
144   {
145     auto type_check = [prototype_value](sqlite3_value* value,
146                                         SqlValue::Type type, const char* desc) {
147       base::Status status = TypeCheckSqliteValue(value, type);
148       if (!status.ok()) {
149         return base::ErrStatus("CREATE_FUNCTION[prototype=%s]: %s %s",
150                                sqlite3_value_text(prototype_value), desc,
151                                status.c_message());
152       }
153       return base::OkStatus();
154     };
155 
156     RETURN_IF_ERROR(type_check(prototype_value, SqlValue::Type::kString,
157                                "function prototype (first argument)"));
158     RETURN_IF_ERROR(type_check(return_type_value, SqlValue::Type::kString,
159                                "return type (second argument)"));
160     RETURN_IF_ERROR(type_check(sql_defn_value, SqlValue::Type::kString,
161                                "SQL definition (third argument)"));
162   }
163 
164   // Extract the arguments from the value wrappers.
165   auto extract_string = [](sqlite3_value* value) -> base::StringView {
166     return reinterpret_cast<const char*>(sqlite3_value_text(value));
167   };
168   base::StringView prototype_str = extract_string(prototype_value);
169   base::StringView return_type_str = extract_string(return_type_value);
170   std::string sql_defn_str = extract_string(sql_defn_value).ToStdString();
171 
172   // Parse all the arguments into a more friendly form.
173   Prototype prototype;
174   base::Status status = ParsePrototype(prototype_str, prototype);
175   if (!status.ok()) {
176     return base::ErrStatus("CREATE_FUNCTION[prototype=%s]: %s",
177                            prototype_str.ToStdString().c_str(),
178                            status.c_message());
179   }
180 
181   // Parse the return type into a enum format.
182   auto opt_return_type = ParseType(return_type_str);
183   if (!opt_return_type) {
184     return base::ErrStatus(
185         "CREATE_FUNCTION[prototype=%s, return=%s]: unknown return type "
186         "specified",
187         prototype_str.ToStdString().c_str(),
188         return_type_str.ToStdString().c_str());
189   }
190   SqlValue::Type return_type = *opt_return_type;
191 
192   int created_argc = static_cast<int>(prototype.arguments.size());
193   NameAndArgc key{prototype.function_name, created_argc};
194   auto it = ctx->state->find(key);
195   if (it != ctx->state->end()) {
196     // If the function already exists, just verify that the prototype, return
197     // type and SQL matches exactly with what we already had registered. By
198     // doing this, we can avoid the problem plaguing C++ macros where macro
199     // ordering determines which one gets run.
200     auto* created_ctx = static_cast<CreatedFunction::Context*>(
201         it->second.created_functon_context);
202 
203     if (created_ctx->prototype != prototype) {
204       return base::ErrStatus(
205           "CREATE_FUNCTION[prototype=%s]: function prototype changed",
206           prototype_str.ToStdString().c_str());
207     }
208 
209     if (created_ctx->return_type != return_type) {
210       return base::ErrStatus(
211           "CREATE_FUNCTION[prototype=%s]: return type changed from %s to %s",
212           prototype_str.ToStdString().c_str(),
213           SqliteTypeToFriendlyString(created_ctx->return_type),
214           return_type_str.ToStdString().c_str());
215     }
216 
217     if (created_ctx->sql != sql_defn_str) {
218       return base::ErrStatus(
219           "CREATE_FUNCTION[prototype=%s]: function SQL changed from %s to %s",
220           prototype_str.ToStdString().c_str(), created_ctx->sql.c_str(),
221           sql_defn_str.c_str());
222     }
223     return base::OkStatus();
224   }
225 
226   // Prepare the SQL definition as a statement using SQLite.
227   ScopedStmt stmt;
228   sqlite3_stmt* stmt_raw = nullptr;
229   int ret = sqlite3_prepare_v2(ctx->db, sql_defn_str.data(),
230                                static_cast<int>(sql_defn_str.size()), &stmt_raw,
231                                nullptr);
232   if (ret != SQLITE_OK) {
233     return base::ErrStatus(
234         "CREATE_FUNCTION[prototype=%s]: SQLite error when preparing "
235         "statement "
236         "%s",
237         prototype_str.ToStdString().c_str(), sqlite3_errmsg(ctx->db));
238   }
239   stmt.reset(stmt_raw);
240 
241   std::unique_ptr<CreatedFunction::Context> created(
242       new CreatedFunction::Context{ctx->db, std::move(prototype), return_type,
243                                    std::move(sql_defn_str), stmt.get()});
244   CreatedFunction::Context* created_ptr = created.get();
245   RETURN_IF_ERROR(RegisterSqlFunction<CreatedFunction>(
246       ctx->db, key.name.c_str(), created_argc, std::move(created)));
247   ctx->state->emplace(key, PerFunctionState{std::move(stmt), created_ptr});
248 
249   // CREATE_FUNCTION doesn't have a return value so just don't sent |out|.
250   return base::OkStatus();
251 }
252 
253 }  // namespace trace_processor
254 }  // namespace perfetto
255