1 | //===----------------------------------------------------------------------===// |
2 | // DuckDB |
3 | // |
4 | // duckdb/execution/index/art/art.hpp |
5 | // |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | |
9 | #pragma once |
10 | |
11 | #include "duckdb/storage/index.hpp" |
12 | |
13 | namespace duckdb { |
14 | |
15 | // classes |
16 | enum class VerifyExistenceType : uint8_t { |
17 | APPEND = 0, // appends to a table |
18 | APPEND_FK = 1, // appends to a table that has a foreign key |
19 | DELETE_FK = 2 // delete from a table that has a foreign key |
20 | }; |
21 | class ConflictManager; |
22 | class Node; |
23 | class ARTKey; |
24 | class FixedSizeAllocator; |
25 | |
26 | // structs |
27 | struct ARTIndexScanState; |
28 | struct ARTFlags { |
29 | vector<bool> vacuum_flags; |
30 | vector<idx_t> merge_buffer_counts; |
31 | }; |
32 | |
33 | class ART : public Index { |
34 | public: |
35 | //! Constructs an ART |
36 | ART(const vector<column_t> &column_ids, TableIOManager &table_io_manager, |
37 | const vector<unique_ptr<Expression>> &unbound_expressions, const IndexConstraintType constraint_type, |
38 | AttachedDatabase &db, const idx_t block_id = DConstants::INVALID_INDEX, |
39 | const idx_t block_offset = DConstants::INVALID_INDEX); |
40 | ~ART() override; |
41 | |
42 | //! Root of the tree |
43 | unique_ptr<Node> tree; |
44 | //! Fixed-size allocators holding the ART nodes |
45 | vector<unique_ptr<FixedSizeAllocator>> allocators; |
46 | |
47 | public: |
48 | //! Initialize a single predicate scan on the index with the given expression and column IDs |
49 | unique_ptr<IndexScanState> InitializeScanSinglePredicate(const Transaction &transaction, const Value &value, |
50 | const ExpressionType expression_type) override; |
51 | //! Initialize a two predicate scan on the index with the given expression and column IDs |
52 | unique_ptr<IndexScanState> InitializeScanTwoPredicates(const Transaction &transaction, const Value &low_value, |
53 | const ExpressionType low_expression_type, |
54 | const Value &high_value, |
55 | const ExpressionType high_expression_type) override; |
56 | //! Performs a lookup on the index, fetching up to max_count result IDs. Returns true if all row IDs were fetched, |
57 | //! and false otherwise |
58 | bool Scan(const Transaction &transaction, const DataTable &table, IndexScanState &state, const idx_t max_count, |
59 | vector<row_t> &result_ids) override; |
60 | |
61 | //! Called when data is appended to the index. The lock obtained from InitializeLock must be held |
62 | PreservedError Append(IndexLock &lock, DataChunk &entries, Vector &row_identifiers) override; |
63 | //! Verify that data can be appended to the index without a constraint violation |
64 | void VerifyAppend(DataChunk &chunk) override; |
65 | //! Verify that data can be appended to the index without a constraint violation using the conflict manager |
66 | void VerifyAppend(DataChunk &chunk, ConflictManager &conflict_manager) override; |
67 | //! Delete a chunk of entries from the index. The lock obtained from InitializeLock must be held |
68 | void Delete(IndexLock &lock, DataChunk &entries, Vector &row_identifiers) override; |
69 | //! Insert a chunk of entries into the index |
70 | PreservedError Insert(IndexLock &lock, DataChunk &data, Vector &row_ids) override; |
71 | |
72 | //! Construct an ART from a vector of sorted keys |
73 | bool ConstructFromSorted(idx_t count, vector<ARTKey> &keys, Vector &row_identifiers); |
74 | |
75 | //! Search equal values and fetches the row IDs |
76 | bool SearchEqual(ARTKey &key, idx_t max_count, vector<row_t> &result_ids); |
77 | //! Search equal values used for joins that do not need to fetch data |
78 | void SearchEqualJoinNoFetch(ARTKey &key, idx_t &result_size); |
79 | |
80 | //! Serializes the index and returns the pair of block_id offset positions |
81 | BlockPointer Serialize(MetaBlockWriter &writer) override; |
82 | |
83 | //! Merge another index into this index. The lock obtained from InitializeLock must be held, and the other |
84 | //! index must also be locked during the merge |
85 | bool MergeIndexes(IndexLock &state, Index &other_index) override; |
86 | |
87 | //! Traverses an ART and vacuums the qualifying nodes. The lock obtained from InitializeLock must be held |
88 | void Vacuum(IndexLock &state) override; |
89 | |
90 | //! Generate ART keys for an input chunk |
91 | static void GenerateKeys(ArenaAllocator &allocator, DataChunk &input, vector<ARTKey> &keys); |
92 | |
93 | //! Generate a string containing all the expressions and their respective values that violate a constraint |
94 | string GenerateErrorKeyName(DataChunk &input, idx_t row); |
95 | //! Generate the matching error message for a constraint violation |
96 | string GenerateConstraintErrorMessage(VerifyExistenceType verify_type, const string &key_name); |
97 | //! Performs constraint checking for a chunk of input data |
98 | void CheckConstraintsForChunk(DataChunk &input, ConflictManager &conflict_manager) override; |
99 | |
100 | //! Returns the string representation of the ART, or only traverses and verifies the index |
101 | string VerifyAndToString(IndexLock &state, const bool only_verify) override; |
102 | |
103 | //! Find the node with a matching key, or return nullptr if not found |
104 | Node Lookup(Node node, const ARTKey &key, idx_t depth); |
105 | |
106 | private: |
107 | //! Insert a row ID into a leaf |
108 | bool InsertToLeaf(Node &leaf_node, const row_t &row_id); |
109 | //! Insert a key into the tree |
110 | bool Insert(Node &node, const ARTKey &key, idx_t depth, const row_t &row_id); |
111 | //! Erase a key from the tree (if a leaf has more than one value) or erase the leaf itself |
112 | void Erase(Node &node, const ARTKey &key, idx_t depth, const row_t &row_id); |
113 | |
114 | //! Returns all row IDs belonging to a key greater (or equal) than the search key |
115 | bool SearchGreater(ARTIndexScanState &state, ARTKey &key, bool inclusive, idx_t max_count, |
116 | vector<row_t> &result_ids); |
117 | //! Returns all row IDs belonging to a key less (or equal) than the upper_bound |
118 | bool SearchLess(ARTIndexScanState &state, ARTKey &upper_bound, bool inclusive, idx_t max_count, |
119 | vector<row_t> &result_ids); |
120 | //! Returns all row IDs belonging to a key within the range of lower_bound and upper_bound |
121 | bool SearchCloseRange(ARTIndexScanState &state, ARTKey &lower_bound, ARTKey &upper_bound, bool left_inclusive, |
122 | bool right_inclusive, idx_t max_count, vector<row_t> &result_ids); |
123 | |
124 | //! Initializes a merge operation by returning a set containing the buffer count of each fixed-size allocator |
125 | void InitializeMerge(ARTFlags &flags); |
126 | |
127 | //! Initializes a vacuum operation by calling the initialize operation of the respective |
128 | //! node allocator, and returns a vector containing either true, if the allocator at |
129 | //! the respective position qualifies, or false, if not |
130 | void InitializeVacuum(ARTFlags &flags); |
131 | //! Finalizes a vacuum operation by calling the finalize operation of all qualifying |
132 | //! fixed size allocators |
133 | void FinalizeVacuum(const ARTFlags &flags); |
134 | |
135 | //! Internal function to return the string representation of the ART, |
136 | //! or only traverses and verifies the index |
137 | string VerifyAndToStringInternal(const bool only_verify); |
138 | }; |
139 | |
140 | } // namespace duckdb |
141 | |