1 /*
2 * Copyright (C) 2024 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/table_pointer_module.h"
18
19 #include <sqlite3.h>
20 #include <algorithm>
21 #include <array>
22 #include <cstdint>
23 #include <iterator>
24 #include <memory>
25 #include <string>
26 #include <string_view>
27 #include <vector>
28
29 #include "perfetto/ext/base/string_utils.h"
30 #include "perfetto/public/compiler.h"
31 #include "src/trace_processor/db/column.h"
32 #include "src/trace_processor/db/table.h"
33 #include "src/trace_processor/sqlite/bindings/sqlite_result.h"
34 #include "src/trace_processor/sqlite/sqlite_utils.h"
35
36 namespace perfetto::trace_processor {
37
Connect(sqlite3 * db,void *,int,const char * const *,sqlite3_vtab ** vtab,char **)38 int TablePointerModule::Connect(sqlite3* db,
39 void*,
40 int,
41 const char* const*,
42 sqlite3_vtab** vtab,
43 char**) {
44 // Specify a dynamic list of columns as our schema which can be later be bound
45 // to specific columns in the table. Only the columns which are bound can be
46 // accessed - all others will throw an error.
47 static constexpr char kSchema[] = R"(
48 CREATE TABLE x(
49 c0 ANY,
50 c1 ANY,
51 c2 ANY,
52 c3 ANY,
53 c4 ANY,
54 c5 ANY,
55 c6 ANY,
56 c7 ANY,
57 c8 ANY,
58 c9 ANY,
59 c10 ANY,
60 c11 ANY,
61 c12 ANY,
62 c13 ANY,
63 c14 ANY,
64 c15 ANY,
65 tab BLOB HIDDEN,
66 row INTEGER HIDDEN,
67 PRIMARY KEY(row)
68 ) WITHOUT ROWID
69 )";
70 if (int ret = sqlite3_declare_vtab(db, kSchema); ret != SQLITE_OK) {
71 return ret;
72 }
73 std::unique_ptr<Vtab> res = std::make_unique<Vtab>();
74 *vtab = res.release();
75 return SQLITE_OK;
76 }
77
Disconnect(sqlite3_vtab * vtab)78 int TablePointerModule::Disconnect(sqlite3_vtab* vtab) {
79 delete GetVtab(vtab);
80 return SQLITE_OK;
81 }
82
BestIndex(sqlite3_vtab * tab,sqlite3_index_info * info)83 int TablePointerModule::BestIndex(sqlite3_vtab* tab, sqlite3_index_info* info) {
84 std::array<bool, kBindableColumnCount> bound_cols{};
85 uint32_t bound_cols_count = 0;
86 bool seen_tab_eq = false;
87 for (int i = 0; i < info->nConstraint; ++i) {
88 auto& in = info->aConstraint[i];
89 auto& out = info->aConstraintUsage[i];
90 // Ignore any unusable constraints.
91 if (!in.usable) {
92 continue;
93 }
94 // Disallow row constraints.
95 if (in.iColumn == kRowColumnIndex) {
96 return sqlite::utils::SetError(tab, "Constraint on row not allowed");
97 }
98 // Bind constraints.
99 if (in.op == kBindConstraint) {
100 if (in.iColumn >= kBindableColumnCount) {
101 return sqlite::utils::SetError(tab, "Invalid bound column");
102 }
103 bool& bound = bound_cols[static_cast<uint32_t>(in.iColumn)];
104 if (bound) {
105 return sqlite::utils::SetError(tab, "Duplicate bound column");
106 }
107 // TODO(lalitm): all of the values here should be constants which should
108 // be accessed with sqlite3_rhs_value. Doing this would require having to
109 // serialize and deserialize the constants though so let's not do it for
110 // now.
111 out.argvIndex = kBoundColumnArgvOffset + in.iColumn;
112 out.omit = true;
113 bound = true;
114 bound_cols_count++;
115 continue;
116 }
117 // Constraint on tab.
118 if (in.iColumn == kTableColumnIndex) {
119 if (in.op != SQLITE_INDEX_CONSTRAINT_EQ) {
120 return sqlite::utils::SetError(
121 tab, "tab only supports equality constraints");
122 }
123 out.argvIndex = kTableArgvIndex;
124 out.omit = true;
125 seen_tab_eq = true;
126 continue;
127 }
128 // Any other constraints on the columns.
129 // TODO(lalitm): implement support for passing these down.
130 }
131 if (!seen_tab_eq) {
132 return sqlite::utils::SetError(tab, "table must be bound");
133 }
134 if (bound_cols_count == 0) {
135 return sqlite::utils::SetError(tab, "At least one column must be bound");
136 }
137 for (uint32_t i = 0; i < bound_cols_count; ++i) {
138 if (!bound_cols[i]) {
139 return sqlite::utils::SetError(tab, "Bound columns are not dense");
140 }
141 }
142 return SQLITE_OK;
143 }
144
Open(sqlite3_vtab *,sqlite3_vtab_cursor ** cursor)145 int TablePointerModule::Open(sqlite3_vtab*, sqlite3_vtab_cursor** cursor) {
146 std::unique_ptr<Cursor> c = std::make_unique<Cursor>();
147 *cursor = c.release();
148 return SQLITE_OK;
149 }
150
Close(sqlite3_vtab_cursor * cursor)151 int TablePointerModule::Close(sqlite3_vtab_cursor* cursor) {
152 delete GetCursor(cursor);
153 return SQLITE_OK;
154 }
155
Filter(sqlite3_vtab_cursor * cur,int,const char *,int argc,sqlite3_value ** argv)156 int TablePointerModule::Filter(sqlite3_vtab_cursor* cur,
157 int,
158 const char*,
159 int argc,
160 sqlite3_value** argv) {
161 auto* c = GetCursor(cur);
162 if (argc == 0) {
163 return sqlite::utils::SetError(c->pVtab, "tab parameter is not set");
164 }
165 c->table = static_cast<const Table*>(sqlite3_value_pointer(argv[0], "TABLE"));
166 if (!c->table) {
167 return sqlite::utils::SetError(c->pVtab, "tab parameter is NULL");
168 }
169 c->col_count = 0;
170 for (int i = 1; i < argc; ++i) {
171 if (sqlite3_value_type(argv[i]) != SQLITE_TEXT) {
172 return sqlite::utils::SetError(c->pVtab, "Column name is not text");
173 }
174
175 std::string_view tok(
176 reinterpret_cast<const char*>(sqlite3_value_text(argv[i])));
177 auto it = std::find_if(
178 c->table->columns().begin(), c->table->columns().end(),
179 [&tok](const ColumnLegacy& col) { return col.name() == tok; });
180 if (it == c->table->columns().end()) {
181 return sqlite::utils::SetError(c->pVtab,
182 "column does not exist in table");
183 }
184 c->bound_col_to_table_index[c->col_count++] =
185 static_cast<uint32_t>(std::distance(c->table->columns().begin(), it));
186 }
187 c->iterator = c->table->IterateRows();
188 return SQLITE_OK;
189 }
190
Next(sqlite3_vtab_cursor * cur)191 int TablePointerModule::Next(sqlite3_vtab_cursor* cur) {
192 auto* c = GetCursor(cur);
193 ++(*c->iterator);
194 return SQLITE_OK;
195 }
196
Eof(sqlite3_vtab_cursor * cur)197 int TablePointerModule::Eof(sqlite3_vtab_cursor* cur) {
198 return !*GetCursor(cur)->iterator;
199 }
200
Column(sqlite3_vtab_cursor * cur,sqlite3_context * ctx,int raw_n)201 int TablePointerModule::Column(sqlite3_vtab_cursor* cur,
202 sqlite3_context* ctx,
203 int raw_n) {
204 auto* c = GetCursor(cur);
205 auto N = static_cast<uint32_t>(raw_n);
206 if (PERFETTO_UNLIKELY(N >= c->col_count)) {
207 return sqlite::utils::SetError(c->pVtab,
208 "Asking for value of non bound column");
209 }
210 uint32_t table_index = c->bound_col_to_table_index[N];
211 sqlite::utils::ReportSqlValue(ctx, c->iterator->Get(table_index));
212 return SQLITE_OK;
213 }
214
Rowid(sqlite3_vtab_cursor *,sqlite_int64 *)215 int TablePointerModule::Rowid(sqlite3_vtab_cursor*, sqlite_int64*) {
216 return SQLITE_ERROR;
217 }
218
FindFunction(sqlite3_vtab *,int,const char * name,FindFunctionFn ** fn,void **)219 int TablePointerModule::FindFunction(sqlite3_vtab*,
220 int,
221 const char* name,
222 FindFunctionFn** fn,
223 void**) {
224 if (base::CaseInsensitiveEqual(name, "__intrinsic_table_ptr_bind")) {
225 *fn = [](sqlite3_context* ctx, int, sqlite3_value**) {
226 return sqlite::result::Error(ctx, "Should not be called.");
227 };
228 return SQLITE_INDEX_CONSTRAINT_FUNCTION + 1;
229 }
230 return SQLITE_OK;
231 }
232
233 } // namespace perfetto::trace_processor
234