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 const char* tok =
176 reinterpret_cast<const char*>(sqlite3_value_text(argv[i]));
177 auto idx = c->table->ColumnIdxFromName(tok);
178 if (!idx) {
179 base::StackString<128> err("column '%s' does not exist in table",
180 sqlite3_value_text(argv[i]));
181 return sqlite::utils::SetError(c->pVtab, err.c_str());
182 }
183 c->bound_col_to_table_index[c->col_count++] = *idx;
184 }
185 c->iterator = c->table->IterateRows();
186 return SQLITE_OK;
187 }
188
Next(sqlite3_vtab_cursor * cur)189 int TablePointerModule::Next(sqlite3_vtab_cursor* cur) {
190 auto* c = GetCursor(cur);
191 ++(*c->iterator);
192 return SQLITE_OK;
193 }
194
Eof(sqlite3_vtab_cursor * cur)195 int TablePointerModule::Eof(sqlite3_vtab_cursor* cur) {
196 return !*GetCursor(cur)->iterator;
197 }
198
Column(sqlite3_vtab_cursor * cur,sqlite3_context * ctx,int raw_n)199 int TablePointerModule::Column(sqlite3_vtab_cursor* cur,
200 sqlite3_context* ctx,
201 int raw_n) {
202 auto* c = GetCursor(cur);
203 auto N = static_cast<uint32_t>(raw_n);
204 if (PERFETTO_UNLIKELY(N >= c->col_count)) {
205 return sqlite::utils::SetError(c->pVtab,
206 "Asking for value of non bound column");
207 }
208 uint32_t table_index = c->bound_col_to_table_index[N];
209 sqlite::utils::ReportSqlValue(ctx, c->iterator->Get(table_index));
210 return SQLITE_OK;
211 }
212
Rowid(sqlite3_vtab_cursor *,sqlite_int64 *)213 int TablePointerModule::Rowid(sqlite3_vtab_cursor*, sqlite_int64*) {
214 return SQLITE_ERROR;
215 }
216
FindFunction(sqlite3_vtab *,int,const char * name,FindFunctionFn ** fn,void **)217 int TablePointerModule::FindFunction(sqlite3_vtab*,
218 int,
219 const char* name,
220 FindFunctionFn** fn,
221 void**) {
222 if (base::CaseInsensitiveEqual(name, "__intrinsic_table_ptr_bind")) {
223 *fn = [](sqlite3_context* ctx, int, sqlite3_value**) {
224 return sqlite::result::Error(ctx, "Should not be called.");
225 };
226 return SQLITE_INDEX_CONSTRAINT_FUNCTION + 1;
227 }
228 return SQLITE_OK;
229 }
230
231 } // namespace perfetto::trace_processor
232