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