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