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/db_sqlite_table.h"
18
19 #include "perfetto/ext/base/string_writer.h"
20 #include "src/trace_processor/containers/bit_vector.h"
21 #include "src/trace_processor/sqlite/query_cache.h"
22 #include "src/trace_processor/sqlite/sqlite_utils.h"
23 #include "src/trace_processor/tp_metatrace.h"
24
25 namespace perfetto {
26 namespace trace_processor {
27
28 namespace {
29
SqliteOpToFilterOp(int sqlite_op)30 base::Optional<FilterOp> SqliteOpToFilterOp(int sqlite_op) {
31 switch (sqlite_op) {
32 case SQLITE_INDEX_CONSTRAINT_EQ:
33 case SQLITE_INDEX_CONSTRAINT_IS:
34 return FilterOp::kEq;
35 case SQLITE_INDEX_CONSTRAINT_GT:
36 return FilterOp::kGt;
37 case SQLITE_INDEX_CONSTRAINT_LT:
38 return FilterOp::kLt;
39 case SQLITE_INDEX_CONSTRAINT_ISNOT:
40 case SQLITE_INDEX_CONSTRAINT_NE:
41 return FilterOp::kNe;
42 case SQLITE_INDEX_CONSTRAINT_GE:
43 return FilterOp::kGe;
44 case SQLITE_INDEX_CONSTRAINT_LE:
45 return FilterOp::kLe;
46 case SQLITE_INDEX_CONSTRAINT_ISNULL:
47 return FilterOp::kIsNull;
48 case SQLITE_INDEX_CONSTRAINT_ISNOTNULL:
49 return FilterOp::kIsNotNull;
50 case SQLITE_INDEX_CONSTRAINT_LIKE:
51 case SQLITE_INDEX_CONSTRAINT_GLOB:
52 return base::nullopt;
53 #if SQLITE_VERSION_NUMBER >= 3038000
54 // LIMIT and OFFSET constraints were introduced in 3.38 but we
55 // still build for older versions in most places. We still need
56 // to handle this here as Chrome is very good at staying up to date
57 // with SQLite versions and crashes if we don't have this.
58 case SQLITE_INDEX_CONSTRAINT_LIMIT:
59 case SQLITE_INDEX_CONSTRAINT_OFFSET:
60 return base::nullopt;
61 #endif
62 default:
63 PERFETTO_FATAL("Currently unsupported constraint");
64 }
65 }
66
SqliteValueToSqlValue(sqlite3_value * sqlite_val)67 SqlValue SqliteValueToSqlValue(sqlite3_value* sqlite_val) {
68 auto col_type = sqlite3_value_type(sqlite_val);
69 SqlValue value;
70 switch (col_type) {
71 case SQLITE_INTEGER:
72 value.type = SqlValue::kLong;
73 value.long_value = sqlite3_value_int64(sqlite_val);
74 break;
75 case SQLITE_TEXT:
76 value.type = SqlValue::kString;
77 value.string_value =
78 reinterpret_cast<const char*>(sqlite3_value_text(sqlite_val));
79 break;
80 case SQLITE_FLOAT:
81 value.type = SqlValue::kDouble;
82 value.double_value = sqlite3_value_double(sqlite_val);
83 break;
84 case SQLITE_BLOB:
85 value.type = SqlValue::kBytes;
86 value.bytes_value = sqlite3_value_blob(sqlite_val);
87 value.bytes_count = static_cast<size_t>(sqlite3_value_bytes(sqlite_val));
88 break;
89 case SQLITE_NULL:
90 value.type = SqlValue::kNull;
91 break;
92 }
93 return value;
94 }
95
ColsUsedBitVector(uint64_t sqlite_cols_used,size_t col_count)96 BitVector ColsUsedBitVector(uint64_t sqlite_cols_used, size_t col_count) {
97 return BitVector::Range(
98 0, static_cast<uint32_t>(col_count), [sqlite_cols_used](uint32_t idx) {
99 // If the lowest bit of |sqlite_cols_used| is set, the first column is
100 // used. The second lowest bit corresponds to the second column etc. If
101 // the most significant bit of |sqlite_cols_used| is set, that means
102 // that any column after the first 63 columns could be used.
103 return sqlite_cols_used & (1ull << std::min(idx, 63u));
104 });
105 }
106
107 } // namespace
108
DbSqliteTable(sqlite3 *,Context context)109 DbSqliteTable::DbSqliteTable(sqlite3*, Context context)
110 : cache_(context.cache),
111 schema_(std::move(context.schema)),
112 computation_(context.computation),
113 static_table_(context.static_table),
114 generator_(std::move(context.generator)) {}
115 DbSqliteTable::~DbSqliteTable() = default;
116
RegisterTable(sqlite3 * db,QueryCache * cache,Table::Schema schema,const Table * table,const std::string & name)117 void DbSqliteTable::RegisterTable(sqlite3* db,
118 QueryCache* cache,
119 Table::Schema schema,
120 const Table* table,
121 const std::string& name) {
122 Context context{cache, schema, TableComputation::kStatic, table, nullptr};
123 SqliteTable::Register<DbSqliteTable, Context>(db, std::move(context), name);
124 }
125
RegisterTable(sqlite3 * db,QueryCache * cache,std::unique_ptr<DynamicTableGenerator> generator)126 void DbSqliteTable::RegisterTable(
127 sqlite3* db,
128 QueryCache* cache,
129 std::unique_ptr<DynamicTableGenerator> generator) {
130 Table::Schema schema = generator->CreateSchema();
131 std::string name = generator->TableName();
132
133 // Figure out if the table needs explicit args (in the form of constraints
134 // on hidden columns) passed to it in order to make the query valid.
135 base::Status status = generator->ValidateConstraints(
136 QueryConstraints(std::numeric_limits<uint64_t>::max()));
137 bool requires_args = !status.ok();
138
139 Context context{cache, std::move(schema), TableComputation::kDynamic, nullptr,
140 std::move(generator)};
141 SqliteTable::Register<DbSqliteTable, Context>(db, std::move(context), name,
142 false, requires_args);
143 }
144
Init(int,const char * const *,Schema * schema)145 base::Status DbSqliteTable::Init(int, const char* const*, Schema* schema) {
146 *schema = ComputeSchema(schema_, name().c_str());
147 return base::OkStatus();
148 }
149
ComputeSchema(const Table::Schema & schema,const char * table_name)150 SqliteTable::Schema DbSqliteTable::ComputeSchema(const Table::Schema& schema,
151 const char* table_name) {
152 std::vector<SqliteTable::Column> schema_cols;
153 for (uint32_t i = 0; i < schema.columns.size(); ++i) {
154 const auto& col = schema.columns[i];
155 schema_cols.emplace_back(i, col.name, col.type, col.is_hidden);
156 }
157
158 // TODO(lalitm): this is hardcoded to be the id column but change this to be
159 // more generic in the future.
160 auto it = std::find_if(
161 schema.columns.begin(), schema.columns.end(),
162 [](const Table::Schema::Column& c) { return c.name == "id"; });
163 if (it == schema.columns.end()) {
164 PERFETTO_FATAL(
165 "id column not found in %s. Currently all db Tables need to contain an "
166 "id column; this constraint will be relaxed in the future.",
167 table_name);
168 }
169
170 std::vector<size_t> primary_keys;
171 primary_keys.emplace_back(std::distance(schema.columns.begin(), it));
172 return Schema(std::move(schema_cols), std::move(primary_keys));
173 }
174
BestIndex(const QueryConstraints & qc,BestIndexInfo * info)175 int DbSqliteTable::BestIndex(const QueryConstraints& qc, BestIndexInfo* info) {
176 switch (computation_) {
177 case TableComputation::kStatic:
178 BestIndex(schema_, static_table_->row_count(), qc, info);
179 break;
180 case TableComputation::kDynamic:
181 base::Status status = generator_->ValidateConstraints(qc);
182 if (!status.ok())
183 return SQLITE_CONSTRAINT;
184 BestIndex(schema_, generator_->EstimateRowCount(), qc, info);
185 break;
186 }
187 return SQLITE_OK;
188 }
189
BestIndex(const Table::Schema & schema,uint32_t row_count,const QueryConstraints & qc,BestIndexInfo * info)190 void DbSqliteTable::BestIndex(const Table::Schema& schema,
191 uint32_t row_count,
192 const QueryConstraints& qc,
193 BestIndexInfo* info) {
194 auto cost_and_rows = EstimateCost(schema, row_count, qc);
195 info->estimated_cost = cost_and_rows.cost;
196 info->estimated_rows = cost_and_rows.rows;
197
198 const auto& cs = qc.constraints();
199 for (uint32_t i = 0; i < cs.size(); ++i) {
200 // SqliteOpToFilterOp will return nullopt for any constraint which we don't
201 // support filtering ourselves. Only omit filtering by SQLite when we can
202 // handle filtering.
203 base::Optional<FilterOp> opt_op = SqliteOpToFilterOp(cs[i].op);
204 info->sqlite_omit_constraint[i] = opt_op.has_value();
205 }
206
207 // We can sort on any column correctly.
208 info->sqlite_omit_order_by = true;
209 }
210
ModifyConstraints(QueryConstraints * qc)211 int DbSqliteTable::ModifyConstraints(QueryConstraints* qc) {
212 ModifyConstraints(schema_, qc);
213 return SQLITE_OK;
214 }
215
ModifyConstraints(const Table::Schema & schema,QueryConstraints * qc)216 void DbSqliteTable::ModifyConstraints(const Table::Schema& schema,
217 QueryConstraints* qc) {
218 using C = QueryConstraints::Constraint;
219
220 // Reorder constraints to consider the constraints on columns which are
221 // cheaper to filter first.
222 auto* cs = qc->mutable_constraints();
223 std::sort(cs->begin(), cs->end(), [&schema](const C& a, const C& b) {
224 uint32_t a_idx = static_cast<uint32_t>(a.column);
225 uint32_t b_idx = static_cast<uint32_t>(b.column);
226 const auto& a_col = schema.columns[a_idx];
227 const auto& b_col = schema.columns[b_idx];
228
229 // Id columns are always very cheap to filter on so try and get them
230 // first.
231 if (a_col.is_id && !b_col.is_id)
232 return true;
233
234 // Sorted columns are also quite cheap to filter so order them after
235 // any id columns.
236 if (a_col.is_sorted && !b_col.is_sorted)
237 return true;
238
239 // TODO(lalitm): introduce more orderings here based on empirical data.
240 return false;
241 });
242
243 // Remove any order by constraints which also have an equality constraint.
244 auto* ob = qc->mutable_order_by();
245 {
246 auto p = [&cs](const QueryConstraints::OrderBy& o) {
247 auto inner_p = [&o](const QueryConstraints::Constraint& c) {
248 return c.column == o.iColumn && sqlite_utils::IsOpEq(c.op);
249 };
250 return std::any_of(cs->begin(), cs->end(), inner_p);
251 };
252 auto remove_it = std::remove_if(ob->begin(), ob->end(), p);
253 ob->erase(remove_it, ob->end());
254 }
255
256 // Go through the order by constraints in reverse order and eliminate
257 // constraints until the first non-sorted column or the first order by in
258 // descending order.
259 {
260 auto p = [&schema](const QueryConstraints::OrderBy& o) {
261 const auto& col = schema.columns[static_cast<uint32_t>(o.iColumn)];
262 return o.desc || !col.is_sorted;
263 };
264 auto first_non_sorted_it = std::find_if(ob->rbegin(), ob->rend(), p);
265 auto pop_count = std::distance(ob->rbegin(), first_non_sorted_it);
266 ob->resize(ob->size() - static_cast<uint32_t>(pop_count));
267 }
268 }
269
EstimateCost(const Table::Schema & schema,uint32_t row_count,const QueryConstraints & qc)270 DbSqliteTable::QueryCost DbSqliteTable::EstimateCost(
271 const Table::Schema& schema,
272 uint32_t row_count,
273 const QueryConstraints& qc) {
274 // Currently our cost estimation algorithm is quite simplistic but is good
275 // enough for the simplest cases.
276 // TODO(lalitm): replace hardcoded constants with either more heuristics
277 // based on the exact type of constraint or profiling the queries themselves.
278
279 // We estimate the fixed cost of set-up and tear-down of a query in terms of
280 // the number of rows scanned.
281 constexpr double kFixedQueryCost = 1000.0;
282
283 // Setup the variables for estimating the number of rows we will have at the
284 // end of filtering. Note that |current_row_count| should always be at least 1
285 // unless we are absolutely certain that we will return no rows as otherwise
286 // SQLite can make some bad choices.
287 uint32_t current_row_count = row_count;
288
289 // If the table is empty, any constraint set only pays the fixed cost. Also we
290 // can return 0 as the row count as we are certain that we will return no
291 // rows.
292 if (current_row_count == 0)
293 return QueryCost{kFixedQueryCost, 0};
294
295 // Setup the variables for estimating the cost of filtering.
296 double filter_cost = 0.0;
297 const auto& cs = qc.constraints();
298 for (const auto& c : cs) {
299 if (current_row_count < 2)
300 break;
301 const auto& col_schema = schema.columns[static_cast<uint32_t>(c.column)];
302 if (sqlite_utils::IsOpEq(c.op) && col_schema.is_id) {
303 // If we have an id equality constraint, we can very efficiently filter
304 // down to a single row in C++. However, if we're joining with another
305 // table, SQLite will do this once per row which can be extremely
306 // expensive because of all the virtual table (which is implemented using
307 // virtual function calls) machinery. Indicate this by saying that an
308 // entire filter call is ~10x the cost of iterating a single row.
309 filter_cost += 10;
310 current_row_count = 1;
311 } else if (sqlite_utils::IsOpEq(c.op)) {
312 // If there is only a single equality constraint, we have special logic
313 // to sort by that column and then binary search if we see the constraint
314 // set often. Model this by dividing by the log of the number of rows as
315 // a good approximation. Otherwise, we'll need to do a full table scan.
316 // Alternatively, if the column is sorted, we can use the same binary
317 // search logic so we have the same low cost (even better because we don't
318 // have to sort at all).
319 filter_cost += cs.size() == 1 || col_schema.is_sorted
320 ? log2(current_row_count)
321 : current_row_count;
322
323 // As an extremely rough heuristic, assume that an equalty constraint will
324 // cut down the number of rows by approximately double log of the number
325 // of rows.
326 double estimated_rows = current_row_count / (2 * log2(current_row_count));
327 current_row_count = std::max(static_cast<uint32_t>(estimated_rows), 1u);
328 } else if (col_schema.is_sorted &&
329 (sqlite_utils::IsOpLe(c.op) || sqlite_utils::IsOpLt(c.op) ||
330 sqlite_utils::IsOpGt(c.op) || sqlite_utils::IsOpGe(c.op))) {
331 // On a sorted column, if we see any partition constraints, we can do this
332 // filter very efficiently. Model this using the log of the number of
333 // rows as a good approximation.
334 filter_cost += log2(current_row_count);
335
336 // As an extremely rough heuristic, assume that an partition constraint
337 // will cut down the number of rows by approximately double log of the
338 // number of rows.
339 double estimated_rows = current_row_count / (2 * log2(current_row_count));
340 current_row_count = std::max(static_cast<uint32_t>(estimated_rows), 1u);
341 } else {
342 // Otherwise, we will need to do a full table scan and we estimate we will
343 // maybe (at best) halve the number of rows.
344 filter_cost += current_row_count;
345 current_row_count = std::max(current_row_count / 2u, 1u);
346 }
347 }
348
349 // Now, to figure out the cost of sorting, multiply the final row count
350 // by |qc.order_by().size()| * log(row count). This should act as a crude
351 // estimation of the cost.
352 double sort_cost =
353 static_cast<double>(qc.order_by().size() * current_row_count) *
354 log2(current_row_count);
355
356 // The cost of iterating rows is more expensive than just filtering the rows
357 // so multiply by an appropriate factor.
358 double iteration_cost = current_row_count * 2.0;
359
360 // To get the final cost, add up all the individual components.
361 double final_cost =
362 kFixedQueryCost + filter_cost + sort_cost + iteration_cost;
363 return QueryCost{final_cost, current_row_count};
364 }
365
CreateCursor()366 std::unique_ptr<SqliteTable::Cursor> DbSqliteTable::CreateCursor() {
367 return std::unique_ptr<Cursor>(new Cursor(this, cache_));
368 }
369
Cursor(DbSqliteTable * sqlite_table,QueryCache * cache)370 DbSqliteTable::Cursor::Cursor(DbSqliteTable* sqlite_table, QueryCache* cache)
371 : SqliteTable::Cursor(sqlite_table),
372 db_sqlite_table_(sqlite_table),
373 cache_(cache) {}
374
TryCacheCreateSortedTable(const QueryConstraints & qc,FilterHistory history)375 void DbSqliteTable::Cursor::TryCacheCreateSortedTable(
376 const QueryConstraints& qc,
377 FilterHistory history) {
378 // Check if we have a cache. Some subclasses (e.g. the flamegraph table) may
379 // pass nullptr to disable caching.
380 if (!cache_)
381 return;
382
383 if (history == FilterHistory::kDifferent) {
384 repeated_cache_count_ = 0;
385
386 // Check if the new constraint set is cached by another cursor.
387 sorted_cache_table_ =
388 cache_->GetIfCached(upstream_table_, qc.constraints());
389 return;
390 }
391
392 PERFETTO_DCHECK(history == FilterHistory::kSame);
393
394 // TODO(lalitm): all of the caching policy below should live in QueryCache and
395 // not here. This is only here temporarily to allow migration of sched without
396 // regressing UI performance and should be removed ASAP.
397
398 // Only try and create the cached table on exactly the third time we see this
399 // constraint set.
400 constexpr uint32_t kRepeatedThreshold = 3;
401 if (sorted_cache_table_ || repeated_cache_count_++ != kRepeatedThreshold)
402 return;
403
404 // If we have more than one constraint, we can't cache the table using
405 // this method.
406 if (qc.constraints().size() != 1)
407 return;
408
409 // If the constraing is not an equality constraint, there's little
410 // benefit to caching
411 const auto& c = qc.constraints().front();
412 if (!sqlite_utils::IsOpEq(c.op))
413 return;
414
415 // If the column is already sorted, we don't need to cache at all.
416 uint32_t col = static_cast<uint32_t>(c.column);
417 if (upstream_table_->GetColumn(col).IsSorted())
418 return;
419
420 // Try again to get the result or start caching it.
421 sorted_cache_table_ =
422 cache_->GetOrCache(upstream_table_, qc.constraints(), [this, col]() {
423 return upstream_table_->Sort({Order{col, false}});
424 });
425 }
426
Filter(const QueryConstraints & qc,sqlite3_value ** argv,FilterHistory history)427 int DbSqliteTable::Cursor::Filter(const QueryConstraints& qc,
428 sqlite3_value** argv,
429 FilterHistory history) {
430 // Clear out the iterator before filtering to ensure the destructor is run
431 // before the table's destructor.
432 iterator_ = base::nullopt;
433
434 // We reuse this vector to reduce memory allocations on nested subqueries.
435 constraints_.resize(qc.constraints().size());
436 uint32_t constraints_pos = 0;
437 for (size_t i = 0; i < qc.constraints().size(); ++i) {
438 const auto& cs = qc.constraints()[i];
439 uint32_t col = static_cast<uint32_t>(cs.column);
440
441 // If we get a nullopt FilterOp, that means we should allow SQLite
442 // to handle the constraint.
443 base::Optional<FilterOp> opt_op = SqliteOpToFilterOp(cs.op);
444 if (!opt_op)
445 continue;
446
447 SqlValue value = SqliteValueToSqlValue(argv[i]);
448 constraints_[constraints_pos++] = Constraint{col, *opt_op, value};
449 }
450 constraints_.resize(constraints_pos);
451
452 // We reuse this vector to reduce memory allocations on nested subqueries.
453 orders_.resize(qc.order_by().size());
454 for (size_t i = 0; i < qc.order_by().size(); ++i) {
455 const auto& ob = qc.order_by()[i];
456 uint32_t col = static_cast<uint32_t>(ob.iColumn);
457 orders_[i] = Order{col, static_cast<bool>(ob.desc)};
458 }
459
460 // Setup the upstream table based on the computation state.
461 switch (db_sqlite_table_->computation_) {
462 case TableComputation::kStatic:
463 // If we have a static table, just set the upstream table to be the static
464 // table.
465 upstream_table_ = db_sqlite_table_->static_table_;
466
467 // Tries to create a sorted cached table which can be used to speed up
468 // filters below.
469 TryCacheCreateSortedTable(qc, history);
470 break;
471 case TableComputation::kDynamic: {
472 PERFETTO_TP_TRACE("DYNAMIC_TABLE_GENERATE", [this](metatrace::Record* r) {
473 r->AddArg("Table", db_sqlite_table_->name());
474 });
475 // If we have a dynamically created table, regenerate the table based on
476 // the new constraints.
477 std::unique_ptr<Table> computed_table;
478 BitVector cols_used_bv = ColsUsedBitVector(
479 qc.cols_used(), db_sqlite_table_->schema_.columns.size());
480 auto status = db_sqlite_table_->generator_->ComputeTable(
481 constraints_, orders_, cols_used_bv, computed_table);
482
483 if (!status.ok()) {
484 auto* sqlite_err = sqlite3_mprintf(
485 "%s: %s", db_sqlite_table_->name().c_str(), status.c_message());
486 db_sqlite_table_->SetErrorMessage(sqlite_err);
487 return SQLITE_CONSTRAINT;
488 }
489 PERFETTO_DCHECK(computed_table);
490 dynamic_table_ = std::move(computed_table);
491 upstream_table_ = dynamic_table_.get();
492 break;
493 }
494 }
495
496 PERFETTO_TP_TRACE("DB_TABLE_FILTER_AND_SORT", [this](metatrace::Record* r) {
497 const Table* source = SourceTable();
498 char buffer[2048];
499 for (const Constraint& c : constraints_) {
500 base::StringWriter writer(buffer, sizeof(buffer));
501 writer.AppendString(source->GetColumn(c.col_idx).name());
502
503 writer.AppendChar(' ');
504 switch (c.op) {
505 case FilterOp::kEq:
506 writer.AppendString("=");
507 break;
508 case FilterOp::kGe:
509 writer.AppendString(">=");
510 break;
511 case FilterOp::kGt:
512 writer.AppendString(">");
513 break;
514 case FilterOp::kLe:
515 writer.AppendString("<=");
516 break;
517 case FilterOp::kLt:
518 writer.AppendString("<");
519 break;
520 case FilterOp::kNe:
521 writer.AppendString("!=");
522 break;
523 case FilterOp::kIsNull:
524 writer.AppendString("IS");
525 break;
526 case FilterOp::kIsNotNull:
527 writer.AppendString("IS NOT");
528 break;
529 }
530 writer.AppendChar(' ');
531
532 switch (c.value.type) {
533 case SqlValue::kString:
534 writer.AppendString(c.value.AsString());
535 break;
536 case SqlValue::kBytes:
537 writer.AppendString("<bytes>");
538 break;
539 case SqlValue::kNull:
540 writer.AppendString("<null>");
541 break;
542 case SqlValue::kDouble: {
543 writer.AppendDouble(c.value.AsDouble());
544 break;
545 }
546 case SqlValue::kLong: {
547 writer.AppendInt(c.value.AsLong());
548 break;
549 }
550 }
551 r->AddArg("Table", db_sqlite_table_->name());
552 r->AddArg("Constraint", writer.GetStringView());
553 }
554
555 for (const auto& o : orders_) {
556 base::StringWriter writer(buffer, sizeof(buffer));
557 writer.AppendString(source->GetColumn(o.col_idx).name());
558 if (o.desc)
559 writer.AppendString(" desc");
560 r->AddArg("Order by", writer.GetStringView());
561 }
562 });
563
564 // Attempt to filter into a RowMap first - weall figure out whether to apply
565 // this to the table or we should use the RowMap directly. Also, if we are
566 // going to sort on the RowMap, it makes sense that we optimize for lookup
567 // speed so our sorting is not super slow.
568 RowMap::OptimizeFor optimize_for = orders_.empty()
569 ? RowMap::OptimizeFor::kMemory
570 : RowMap::OptimizeFor::kLookupSpeed;
571 RowMap filter_map = SourceTable()->FilterToRowMap(constraints_, optimize_for);
572
573 // If we have no order by constraints and it's cheap for us to use the
574 // RowMap, just use the RowMap directoy.
575 if (filter_map.IsRange() && filter_map.size() <= 1) {
576 // Currently, our criteria where we have a special fast path is if it's
577 // a single ranged row. We have tihs fast path for joins on id columns
578 // where we get repeated queries filtering down to a single row. The
579 // other path performs allocations when creating the new table as well
580 // as the iterator on the new table whereas this path only uses a single
581 // number and lives entirely on the stack.
582
583 // TODO(lalitm): investigate some other criteria where it is beneficial
584 // to have a fast path and expand to them.
585 mode_ = Mode::kSingleRow;
586 single_row_ = filter_map.size() == 1
587 ? base::make_optional(filter_map.Get(0))
588 : base::nullopt;
589 eof_ = !single_row_.has_value();
590 } else {
591 mode_ = Mode::kTable;
592
593 db_table_ = SourceTable()->Apply(std::move(filter_map));
594 if (!orders_.empty())
595 db_table_ = db_table_->Sort(orders_);
596
597 iterator_ = db_table_->IterateRows();
598
599 eof_ = !*iterator_;
600 }
601
602 return SQLITE_OK;
603 }
604
Next()605 int DbSqliteTable::Cursor::Next() {
606 if (mode_ == Mode::kSingleRow) {
607 eof_ = true;
608 } else {
609 iterator_->Next();
610 eof_ = !*iterator_;
611 }
612 return SQLITE_OK;
613 }
614
Eof()615 int DbSqliteTable::Cursor::Eof() {
616 return eof_;
617 }
618
Column(sqlite3_context * ctx,int raw_col)619 int DbSqliteTable::Cursor::Column(sqlite3_context* ctx, int raw_col) {
620 uint32_t column = static_cast<uint32_t>(raw_col);
621 SqlValue value = mode_ == Mode::kSingleRow
622 ? SourceTable()->GetColumn(column).Get(*single_row_)
623 : iterator_->Get(column);
624 // We can say kSqliteStatic for strings because all strings are expected to
625 // come from the string pool and thus will be valid for the lifetime
626 // of trace processor.
627 // Similarily for bytes we can also use kSqliteStatic because for our iterator
628 // will hold onto the pointer as long as we don't call Next() but that only
629 // happens with Next() is called on the Cursor itself at which point
630 // SQLite no longer cares about the bytes pointer.
631 sqlite_utils::ReportSqlValue(ctx, value, sqlite_utils::kSqliteStatic,
632 sqlite_utils::kSqliteStatic);
633 return SQLITE_OK;
634 }
635
636 DbSqliteTable::DynamicTableGenerator::~DynamicTableGenerator() = default;
637
638 } // namespace trace_processor
639 } // namespace perfetto
640