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