• 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 #ifndef SRC_TRACE_PROCESSOR_DB_COLUMN_H_
18 #define SRC_TRACE_PROCESSOR_DB_COLUMN_H_
19 
20 #include <stdint.h>
21 
22 #include "perfetto/base/logging.h"
23 #include "perfetto/ext/base/optional.h"
24 #include "perfetto/trace_processor/basic_types.h"
25 #include "src/trace_processor/containers/nullable_vector.h"
26 #include "src/trace_processor/containers/row_map.h"
27 #include "src/trace_processor/containers/string_pool.h"
28 #include "src/trace_processor/db/compare.h"
29 
30 namespace perfetto {
31 namespace trace_processor {
32 
33 // Id type which can be used as a base for strongly typed ids.
34 // TypedColumn has support for storing descendents of BaseId seamlessly
35 // in a Column.
36 struct BaseId {
37   BaseId() = default;
BaseIdBaseId38   explicit constexpr BaseId(uint32_t v) : value(v) {}
39 
40   bool operator==(const BaseId& o) const { return o.value == value; }
41   bool operator<(const BaseId& o) const { return value < o.value; }
42 
43   uint32_t value;
44 };
45 
46 // Represents the possible filter operations on a column.
47 enum class FilterOp {
48   kEq,
49   kNe,
50   kGt,
51   kLt,
52   kGe,
53   kLe,
54   kIsNull,
55   kIsNotNull,
56 };
57 
58 // Represents a constraint on a column.
59 struct Constraint {
60   uint32_t col_idx;
61   FilterOp op;
62   SqlValue value;
63 };
64 
65 // Represents an order by operation on a column.
66 struct Order {
67   uint32_t col_idx;
68   bool desc;
69 };
70 
71 // Represents a column which is to be joined on.
72 struct JoinKey {
73   uint32_t col_idx;
74 };
75 
76 class Table;
77 
78 // Represents a named, strongly typed list of data.
79 class Column {
80  public:
81   // Flags which indicate properties of the data in the column. These features
82   // are used to speed up column methods like filtering/sorting.
83   enum Flag : uint32_t {
84     // Indicates that this column has no special properties.
85     kNoFlag = 0,
86 
87     // Indicates the data in the column is sorted. This can be used to speed
88     // up filtering and skip sorting.
89     kSorted = 1 << 0,
90 
91     // Indicates the data in the column is non-null. That is, the NullableVector
92     // passed in will never have any null entries. This is only used for
93     // numeric columns (string columns and id columns both have special
94     // handling which ignores this flag).
95     //
96     // This is used to speed up filters as we can safely index NullableVector
97     // directly if this flag is set.
98     kNonNull = 1 << 1,
99 
100     // Indicates that the data in the column is "hidden". This can by used to
101     // hint to users of Table and Column that this column should not be
102     // displayed to the user as it is part of the internal implementation
103     // details of the table.
104     kHidden = 1 << 2,
105 
106     // Indicates that the data in this column is stored densely. This
107     // allows for fast Set calls to change the data in the column.
108     //
109     // This flag is only meaningful for nullable columns has no effect for
110     // non-null columns.
111     kDense = 1 << 3,
112   };
113 
114   // Flags specified for an id column.
115   static constexpr uint32_t kIdFlags = Flag::kSorted | Flag::kNonNull;
116 
117   template <typename T>
Column(const char * name,NullableVector<T> * storage,uint32_t flags,Table * table,uint32_t col_idx_in_table,uint32_t row_map_idx)118   Column(const char* name,
119          NullableVector<T>* storage,
120          /* Flag */ uint32_t flags,
121          Table* table,
122          uint32_t col_idx_in_table,
123          uint32_t row_map_idx)
124       : Column(name,
125                ToColumnType<T>(),
126                flags,
127                table,
128                col_idx_in_table,
129                row_map_idx,
130                storage,
131                nullptr) {}
132 
133   // Create a Column has the same name and is backed by the same data as
134   // |column| but is associated to a different table.
135   Column(const Column& column,
136          Table* table,
137          uint32_t col_idx_in_table,
138          uint32_t row_map_idx);
139 
140   // Columns are movable but not copyable.
141   Column(Column&&) noexcept = default;
142   Column& operator=(Column&&) = default;
143 
144   template <typename T>
WithOwnedStorage(const char * name,std::unique_ptr<NullableVector<T>> storage,uint32_t flags,Table * table,uint32_t col_idx_in_table,uint32_t row_map_idx)145   static Column WithOwnedStorage(const char* name,
146                                  std::unique_ptr<NullableVector<T>> storage,
147                                  /* Flag */ uint32_t flags,
148                                  Table* table,
149                                  uint32_t col_idx_in_table,
150                                  uint32_t row_map_idx) {
151     NullableVector<T>* ptr = storage.get();
152     return Column(name, ToColumnType<T>(), flags, table, col_idx_in_table,
153                   row_map_idx, ptr, std::move(storage));
154   }
155 
156   // Creates a Column which returns the index as the value of the row.
157   static Column IdColumn(Table* table,
158                          uint32_t col_idx_in_table,
159                          uint32_t row_map_idx);
160 
161   // Gets the value of the Column at the given |row|.
Get(uint32_t row)162   SqlValue Get(uint32_t row) const { return GetAtIdx(row_map().Get(row)); }
163 
164   // Returns the row containing the given value in the Column.
IndexOf(SqlValue value)165   base::Optional<uint32_t> IndexOf(SqlValue value) const {
166     switch (type_) {
167       // TODO(lalitm): investigate whether we could make this more efficient
168       // by first checking the type of the column and comparing explicitly
169       // based on that type.
170       case ColumnType::kInt32:
171       case ColumnType::kUint32:
172       case ColumnType::kInt64:
173       case ColumnType::kDouble:
174       case ColumnType::kString: {
175         for (uint32_t i = 0; i < row_map().size(); i++) {
176           if (compare::SqlValue(Get(i), value) == 0)
177             return i;
178         }
179         return base::nullopt;
180       }
181       case ColumnType::kId: {
182         if (value.type != SqlValue::Type::kLong)
183           return base::nullopt;
184         return row_map().IndexOf(static_cast<uint32_t>(value.long_value));
185       }
186     }
187     PERFETTO_FATAL("For GCC");
188   }
189 
190   // Sets the value of the column at the given |row|.
Set(uint32_t row,SqlValue value)191   void Set(uint32_t row, SqlValue value) {
192     PERFETTO_CHECK(value.type == type());
193     switch (type_) {
194       case ColumnType::kInt32: {
195         mutable_nullable_vector<int32_t>()->Set(
196             row, static_cast<int32_t>(value.long_value));
197         break;
198       }
199       case ColumnType::kUint32: {
200         mutable_nullable_vector<uint32_t>()->Set(
201             row, static_cast<uint32_t>(value.long_value));
202         break;
203       }
204       case ColumnType::kInt64: {
205         mutable_nullable_vector<int64_t>()->Set(
206             row, static_cast<int64_t>(value.long_value));
207         break;
208       }
209       case ColumnType::kDouble: {
210         mutable_nullable_vector<double>()->Set(row, value.double_value);
211         break;
212       }
213       case ColumnType::kString: {
214         PERFETTO_FATAL(
215             "Setting a generic value on a string column is not implemented");
216       }
217       case ColumnType::kId: {
218         PERFETTO_FATAL("Cannot set value on a id column");
219       }
220     }
221   }
222 
223   // Sorts |idx| in ascending or descending order (determined by |desc|) based
224   // on the contents of this column.
225   void StableSort(bool desc, std::vector<uint32_t>* idx) const;
226 
227   // Updates the given RowMap by only keeping rows where this column meets the
228   // given filter constraint.
FilterInto(FilterOp op,SqlValue value,RowMap * rm)229   void FilterInto(FilterOp op, SqlValue value, RowMap* rm) const {
230     if (IsId() && op == FilterOp::kEq) {
231       // If this is an equality constraint on an id column, try and find the
232       // single row with the id (if it exists).
233       auto opt_idx = IndexOf(value);
234       if (opt_idx) {
235         rm->Intersect(RowMap::SingleRow(*opt_idx));
236       } else {
237         rm->Intersect(RowMap());
238       }
239       return;
240     }
241 
242     if (IsSorted() && value.type == type()) {
243       // If the column is sorted and the value has the same type as the column,
244       // we should be able to just do a binary search to find the range of rows
245       // instead of a full table scan.
246       bool handled = FilterIntoSorted(op, value, rm);
247       if (handled)
248         return;
249     }
250 
251     FilterIntoSlow(op, value, rm);
252   }
253 
254   // Returns the minimum value in this column. Returns nullopt if this column
255   // is empty.
Min()256   base::Optional<SqlValue> Min() const {
257     if (row_map().empty())
258       return base::nullopt;
259 
260     if (IsSorted())
261       return Get(0);
262 
263     Iterator b(this, 0);
264     Iterator e(this, row_map().size());
265     return *std::min_element(b, e, &compare::SqlValueComparator);
266   }
267 
268   // Returns the minimum value in this column. Returns nullopt if this column
269   // is empty.
Max()270   base::Optional<SqlValue> Max() const {
271     if (row_map().empty())
272       return base::nullopt;
273 
274     if (IsSorted())
275       return Get(row_map().size() - 1);
276 
277     Iterator b(this, 0);
278     Iterator e(this, row_map().size());
279     return *std::max_element(b, e, &compare::SqlValueComparator);
280   }
281 
282   // Returns true if this column is considered an id column.
IsId()283   bool IsId() const { return type_ == ColumnType::kId; }
284 
285   // Returns true if this column is a nullable column.
IsNullable()286   bool IsNullable() const { return (flags_ & Flag::kNonNull) == 0; }
287 
288   // Returns true if this column is a sorted column.
IsSorted()289   bool IsSorted() const { return (flags_ & Flag::kSorted) != 0; }
290 
291   // Returns true if this column is a dense column.
IsDense()292   bool IsDense() const { return (flags_ & Flag::kDense) != 0; }
293 
294   // Returns the backing RowMap for this Column.
295   // This function is defined out of line because of a circular dependency
296   // between |Table| and |Column|.
297   const RowMap& row_map() const;
298 
299   // Returns the name of the column.
name()300   const char* name() const { return name_; }
301 
302   // Returns the type of this Column in terms of SqlValue::Type.
type()303   SqlValue::Type type() const { return ToSqlValueType(type_); }
304 
305   // Test the type of this Column.
306   template <typename T>
IsColumnType()307   bool IsColumnType() const {
308     return ToColumnType<T>() == type_;
309   }
310 
311   // Returns the index of the current column in the containing table.
index_in_table()312   uint32_t index_in_table() const { return col_idx_in_table_; }
313 
314   // Returns a Constraint for each type of filter operation for this Column.
eq_value(SqlValue value)315   Constraint eq_value(SqlValue value) const {
316     return Constraint{col_idx_in_table_, FilterOp::kEq, value};
317   }
gt_value(SqlValue value)318   Constraint gt_value(SqlValue value) const {
319     return Constraint{col_idx_in_table_, FilterOp::kGt, value};
320   }
lt_value(SqlValue value)321   Constraint lt_value(SqlValue value) const {
322     return Constraint{col_idx_in_table_, FilterOp::kLt, value};
323   }
ne_value(SqlValue value)324   Constraint ne_value(SqlValue value) const {
325     return Constraint{col_idx_in_table_, FilterOp::kNe, value};
326   }
ge_value(SqlValue value)327   Constraint ge_value(SqlValue value) const {
328     return Constraint{col_idx_in_table_, FilterOp::kGe, value};
329   }
le_value(SqlValue value)330   Constraint le_value(SqlValue value) const {
331     return Constraint{col_idx_in_table_, FilterOp::kLe, value};
332   }
is_not_null()333   Constraint is_not_null() const {
334     return Constraint{col_idx_in_table_, FilterOp::kIsNotNull, SqlValue()};
335   }
is_null()336   Constraint is_null() const {
337     return Constraint{col_idx_in_table_, FilterOp::kIsNull, SqlValue()};
338   }
339 
340   // Returns an Order for each Order type for this Column.
ascending()341   Order ascending() const { return Order{col_idx_in_table_, false}; }
descending()342   Order descending() const { return Order{col_idx_in_table_, true}; }
343 
344   // Returns the JoinKey for this Column.
join_key()345   JoinKey join_key() const { return JoinKey{col_idx_in_table_}; }
346 
347  protected:
348   // Returns the backing sparse vector cast to contain data of type T.
349   // Should only be called when |type_| == ToColumnType<T>().
350   template <typename T>
mutable_nullable_vector()351   NullableVector<T>* mutable_nullable_vector() {
352     PERFETTO_DCHECK(ToColumnType<T>() == type_);
353     return static_cast<NullableVector<T>*>(nullable_vector_);
354   }
355 
356   // Returns the backing sparse vector cast to contain data of type T.
357   // Should only be called when |type_| == ToColumnType<T>().
358   template <typename T>
nullable_vector()359   const NullableVector<T>& nullable_vector() const {
360     PERFETTO_DCHECK(ToColumnType<T>() == type_);
361     return *static_cast<const NullableVector<T>*>(nullable_vector_);
362   }
363 
364   // Returns the type of this Column in terms of SqlValue::Type.
365   template <typename T>
ToSqlValueType()366   static SqlValue::Type ToSqlValueType() {
367     return ToSqlValueType(ToColumnType<T>());
368   }
369 
string_pool()370   const StringPool& string_pool() const { return *string_pool_; }
371 
372  private:
373   enum class ColumnType {
374     // Standard primitive types.
375     kInt32,
376     kUint32,
377     kInt64,
378     kDouble,
379     kString,
380 
381     // Types generated on the fly.
382     kId,
383   };
384 
385   // Iterator over a column which conforms to std iterator interface
386   // to allow using std algorithms (e.g. upper_bound, lower_bound etc.).
387   class Iterator {
388    public:
389     using iterator_category = std::random_access_iterator_tag;
390     using value_type = SqlValue;
391     using difference_type = uint32_t;
392     using pointer = uint32_t*;
393     using reference = uint32_t&;
394 
Iterator(const Column * col,uint32_t row)395     Iterator(const Column* col, uint32_t row) : col_(col), row_(row) {}
396 
397     Iterator(const Iterator&) = default;
398     Iterator& operator=(const Iterator&) = default;
399 
400     bool operator==(const Iterator& other) const { return other.row_ == row_; }
401     bool operator!=(const Iterator& other) const { return !(*this == other); }
402     bool operator<(const Iterator& other) const { return other.row_ < row_; }
403     bool operator>(const Iterator& other) const { return other < *this; }
404     bool operator<=(const Iterator& other) const { return !(other < *this); }
405     bool operator>=(const Iterator& other) const { return !(*this < other); }
406 
407     SqlValue operator*() const { return col_->Get(row_); }
408     Iterator& operator++() {
409       row_++;
410       return *this;
411     }
412     Iterator& operator--() {
413       row_--;
414       return *this;
415     }
416 
417     Iterator& operator+=(uint32_t diff) {
418       row_ += diff;
419       return *this;
420     }
421     uint32_t operator-(const Iterator& other) { return row_ - other.row_; }
422 
423    private:
424     const Column* col_ = nullptr;
425     uint32_t row_ = 0;
426   };
427 
428   friend class Table;
429 
430   // Base constructor for this class which all other constructors call into.
431   Column(const char* name,
432          ColumnType type,
433          uint32_t flags,
434          Table* table,
435          uint32_t col_idx_in_table,
436          uint32_t row_map_idx,
437          NullableVectorBase* nullable_vector,
438          std::shared_ptr<NullableVectorBase> owned_nullable_vector);
439 
440   Column(const Column&) = delete;
441   Column& operator=(const Column&) = delete;
442 
443   // Gets the value of the Column at the given |row|.
GetAtIdx(uint32_t idx)444   SqlValue GetAtIdx(uint32_t idx) const {
445     switch (type_) {
446       case ColumnType::kInt32: {
447         auto opt_value = nullable_vector<int32_t>().Get(idx);
448         return opt_value ? SqlValue::Long(*opt_value) : SqlValue();
449       }
450       case ColumnType::kUint32: {
451         auto opt_value = nullable_vector<uint32_t>().Get(idx);
452         return opt_value ? SqlValue::Long(*opt_value) : SqlValue();
453       }
454       case ColumnType::kInt64: {
455         auto opt_value = nullable_vector<int64_t>().Get(idx);
456         return opt_value ? SqlValue::Long(*opt_value) : SqlValue();
457       }
458       case ColumnType::kDouble: {
459         auto opt_value = nullable_vector<double>().Get(idx);
460         return opt_value ? SqlValue::Double(*opt_value) : SqlValue();
461       }
462       case ColumnType::kString: {
463         auto str = GetStringPoolStringAtIdx(idx).c_str();
464         return str == nullptr ? SqlValue() : SqlValue::String(str);
465       }
466       case ColumnType::kId:
467         return SqlValue::Long(idx);
468     }
469     PERFETTO_FATAL("For GCC");
470   }
471 
472   // Optimized filter method for sorted columns.
473   // Returns whether the constraint was handled by the method.
FilterIntoSorted(FilterOp op,SqlValue value,RowMap * rm)474   bool FilterIntoSorted(FilterOp op, SqlValue value, RowMap* rm) const {
475     PERFETTO_DCHECK(IsSorted());
476     PERFETTO_DCHECK(value.type == type());
477 
478     Iterator b(this, 0);
479     Iterator e(this, row_map().size());
480     switch (op) {
481       case FilterOp::kEq: {
482         uint32_t beg = std::distance(
483             b, std::lower_bound(b, e, value, &compare::SqlValueComparator));
484         uint32_t end = std::distance(
485             b, std::upper_bound(b, e, value, &compare::SqlValueComparator));
486         rm->Intersect(RowMap(beg, end));
487         return true;
488       }
489       case FilterOp::kLe: {
490         uint32_t end = std::distance(
491             b, std::upper_bound(b, e, value, &compare::SqlValueComparator));
492         rm->Intersect(RowMap(0, end));
493         return true;
494       }
495       case FilterOp::kLt: {
496         uint32_t end = std::distance(
497             b, std::lower_bound(b, e, value, &compare::SqlValueComparator));
498         rm->Intersect(RowMap(0, end));
499         return true;
500       }
501       case FilterOp::kGe: {
502         uint32_t beg = std::distance(
503             b, std::lower_bound(b, e, value, &compare::SqlValueComparator));
504         rm->Intersect(RowMap(beg, row_map().size()));
505         return true;
506       }
507       case FilterOp::kGt: {
508         uint32_t beg = std::distance(
509             b, std::upper_bound(b, e, value, &compare::SqlValueComparator));
510         rm->Intersect(RowMap(beg, row_map().size()));
511         return true;
512       }
513       case FilterOp::kNe:
514       case FilterOp::kIsNull:
515       case FilterOp::kIsNotNull:
516         break;
517     }
518     return false;
519   }
520 
521   // Slow path filter method which will perform a full table scan.
522   void FilterIntoSlow(FilterOp op, SqlValue value, RowMap* rm) const;
523 
524   // Slow path filter method for numerics which will perform a full table scan.
525   template <typename T, bool is_nullable>
526   void FilterIntoNumericSlow(FilterOp op, SqlValue value, RowMap* rm) const;
527 
528   // Slow path filter method for numerics with a comparator which will perform a
529   // full table scan.
530   template <typename T, bool is_nullable, typename Comparator = int(T)>
531   void FilterIntoNumericWithComparatorSlow(FilterOp op,
532                                            RowMap* rm,
533                                            Comparator cmp) const;
534 
535   // Slow path filter method for strings which will perform a full table scan.
536   void FilterIntoStringSlow(FilterOp op, SqlValue value, RowMap* rm) const;
537 
538   // Slow path filter method for ids which will perform a full table scan.
539   void FilterIntoIdSlow(FilterOp op, SqlValue value, RowMap* rm) const;
540 
541   // Stable sorts this column storing the result in |out|.
542   template <bool desc>
543   void StableSort(std::vector<uint32_t>* out) const;
544 
545   // Stable sorts this column storing the result in |out|.
546   // |T| and |is_nullable| should match the type and nullability of this column.
547   template <bool desc, typename T, bool is_nullable>
548   void StableSortNumeric(std::vector<uint32_t>* out) const;
549 
550   template <typename T>
ToColumnType()551   static ColumnType ToColumnType() {
552     if (std::is_same<T, uint32_t>::value) {
553       return ColumnType::kUint32;
554     } else if (std::is_same<T, int64_t>::value) {
555       return ColumnType::kInt64;
556     } else if (std::is_same<T, int32_t>::value) {
557       return ColumnType::kInt32;
558     } else if (std::is_same<T, StringPool::Id>::value) {
559       return ColumnType::kString;
560     } else if (std::is_same<T, double>::value) {
561       return ColumnType::kDouble;
562     } else {
563       PERFETTO_FATAL("Unsupported type of column");
564     }
565   }
566 
ToSqlValueType(ColumnType type)567   static SqlValue::Type ToSqlValueType(ColumnType type) {
568     switch (type) {
569       case ColumnType::kInt32:
570       case ColumnType::kUint32:
571       case ColumnType::kInt64:
572       case ColumnType::kId:
573         return SqlValue::Type::kLong;
574       case ColumnType::kDouble:
575         return SqlValue::Type::kDouble;
576       case ColumnType::kString:
577         return SqlValue::Type::kString;
578     }
579     PERFETTO_FATAL("For GCC");
580   }
581 
582   // Returns the string at the index |idx|.
583   // Should only be called when |type_| == ColumnType::kString.
GetStringPoolStringAtIdx(uint32_t idx)584   NullTermStringView GetStringPoolStringAtIdx(uint32_t idx) const {
585     PERFETTO_DCHECK(type_ == ColumnType::kString);
586     return string_pool_->Get(nullable_vector<StringPool::Id>().GetNonNull(idx));
587   }
588 
589   // Only filled for columns which own the data inside them. Generally this is
590   // only true for columns which are dynamically generated at runtime.
591   // Keep this before |nullable_vector_|.
592   std::shared_ptr<NullableVectorBase> owned_nullable_vector_;
593 
594   // type_ is used to cast nullable_vector_ to the correct type.
595   ColumnType type_ = ColumnType::kInt64;
596   NullableVectorBase* nullable_vector_ = nullptr;
597 
598   const char* name_ = nullptr;
599   uint32_t flags_ = Flag::kNoFlag;
600   const Table* table_ = nullptr;
601   uint32_t col_idx_in_table_ = 0;
602   uint32_t row_map_idx_ = 0;
603   const StringPool* string_pool_ = nullptr;
604 };
605 
606 }  // namespace trace_processor
607 }  // namespace perfetto
608 
609 #endif  // SRC_TRACE_PROCESSOR_DB_COLUMN_H_
610