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