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