• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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