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