• 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     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