duckdb 0.6.2-dev1070.0 → 0.6.2-dev1092.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (24) hide show
  1. package/package.json +1 -1
  2. package/src/duckdb/src/execution/index/art/art.cpp +18 -54
  3. package/src/duckdb/src/execution/index/art/leaf.cpp +10 -3
  4. package/src/duckdb/src/execution/index/art/node.cpp +41 -36
  5. package/src/duckdb/src/execution/index/art/node16.cpp +13 -14
  6. package/src/duckdb/src/execution/index/art/node256.cpp +10 -15
  7. package/src/duckdb/src/execution/index/art/node4.cpp +13 -14
  8. package/src/duckdb/src/execution/index/art/node48.cpp +10 -17
  9. package/src/duckdb/src/execution/index/art/prefix.cpp +0 -4
  10. package/src/duckdb/src/execution/operator/schema/physical_create_index.cpp +27 -72
  11. package/src/duckdb/src/execution/physical_plan/plan_create_index.cpp +60 -10
  12. package/src/duckdb/src/function/table/version/pragma_version.cpp +2 -2
  13. package/src/duckdb/src/include/duckdb/execution/index/art/art.hpp +3 -3
  14. package/src/duckdb/src/include/duckdb/execution/index/art/node.hpp +5 -3
  15. package/src/duckdb/src/include/duckdb/execution/index/art/node16.hpp +2 -2
  16. package/src/duckdb/src/include/duckdb/execution/index/art/node256.hpp +2 -2
  17. package/src/duckdb/src/include/duckdb/execution/index/art/node4.hpp +2 -2
  18. package/src/duckdb/src/include/duckdb/execution/index/art/node48.hpp +2 -2
  19. package/src/duckdb/src/include/duckdb/execution/index/art/prefix.hpp +4 -2
  20. package/src/duckdb/src/include/duckdb/execution/operator/schema/physical_create_index.hpp +7 -9
  21. package/src/duckdb/src/include/duckdb/main/prepared_statement.hpp +2 -0
  22. package/src/duckdb/src/include/duckdb/storage/index.hpp +2 -4
  23. package/src/duckdb/src/main/prepared_statement.cpp +13 -0
  24. package/src/duckdb/src/storage/index.cpp +2 -0
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "duckdb",
3
3
  "main": "./lib/duckdb.js",
4
4
  "types": "./lib/duckdb.d.ts",
5
- "version": "0.6.2-dev1070.0",
5
+ "version": "0.6.2-dev1092.0",
6
6
  "description": "DuckDB node.js API",
7
7
  "gypfile": true,
8
8
  "dependencies": {
@@ -15,16 +15,20 @@ namespace duckdb {
15
15
  ART::ART(const vector<column_t> &column_ids, TableIOManager &table_io_manager,
16
16
  const vector<unique_ptr<Expression>> &unbound_expressions, IndexConstraintType constraint_type,
17
17
  AttachedDatabase &db, idx_t block_id, idx_t block_offset)
18
+
18
19
  : Index(IndexType::ART, table_io_manager, column_ids, unbound_expressions, constraint_type), db(db),
19
20
  estimated_art_size(0), estimated_key_size(16) {
21
+
20
22
  if (!Radix::IsLittleEndian()) {
21
23
  throw NotImplementedException("ART indexes are not supported on big endian architectures");
22
24
  }
25
+
23
26
  if (block_id != DConstants::INVALID_INDEX) {
24
27
  tree = Node::Deserialize(*this, block_id, block_offset);
25
28
  } else {
26
29
  tree = nullptr;
27
30
  }
31
+
28
32
  serialized_data_pointer = BlockPointer(block_id, block_offset);
29
33
  for (idx_t i = 0; i < types.size(); i++) {
30
34
  switch (types[i]) {
@@ -272,11 +276,17 @@ void Construct(vector<Key> &keys, row_t *row_ids, Node *&node, KeySection &key_s
272
276
  auto num_row_ids = key_section.end - key_section.start + 1;
273
277
 
274
278
  // check for possible constraint violation
275
- if (has_constraint && num_row_ids != 1) {
279
+ auto single_row_id = num_row_ids == 1;
280
+ if (has_constraint && !single_row_id) {
276
281
  throw ConstraintException("New data contains duplicates on indexed column(s)");
277
282
  }
278
283
 
284
+ if (single_row_id) {
285
+ node = Leaf::New(start_key, prefix_start, row_ids[key_section.start]);
286
+ return;
287
+ }
279
288
  node = Leaf::New(start_key, prefix_start, row_ids + key_section.start, num_row_ids);
289
+
280
290
  } else { // create a new node and recurse
281
291
 
282
292
  // we will find at least two child entries of this node, otherwise we'd have reached a leaf
@@ -298,61 +308,15 @@ void Construct(vector<Key> &keys, row_t *row_ids, Node *&node, KeySection &key_s
298
308
  }
299
309
  }
300
310
 
301
- void ART::ConstructAndMerge(IndexLock &lock, PayloadScanner &scanner, Allocator &allocator) {
302
-
303
- auto payload_types = logical_types;
304
- payload_types.emplace_back(LogicalType::ROW_TYPE);
305
-
306
- ArenaAllocator arena_allocator(BufferAllocator::Get(db));
307
- vector<Key> keys(STANDARD_VECTOR_SIZE);
308
-
309
- auto temp_art = make_unique<ART>(this->column_ids, this->table_io_manager, this->unbound_expressions,
310
- this->constraint_type, this->db);
311
+ void ART::ConstructFromSorted(idx_t count, vector<Key> &keys, Vector &row_identifiers) {
311
312
 
312
- for (;;) {
313
- DataChunk ordered_chunk;
314
- ordered_chunk.Initialize(allocator, payload_types);
315
- ordered_chunk.SetCardinality(0);
316
- scanner.Scan(ordered_chunk);
317
- if (ordered_chunk.size() == 0) {
318
- break;
319
- }
320
-
321
- // get the key chunk and the row_identifiers vector
322
- DataChunk row_id_chunk;
323
- ordered_chunk.Split(row_id_chunk, ordered_chunk.ColumnCount() - 1);
324
- auto &row_identifiers = row_id_chunk.data[0];
325
-
326
- D_ASSERT(row_identifiers.GetType().InternalType() == ROW_TYPE);
327
- D_ASSERT(logical_types[0] == ordered_chunk.data[0].GetType());
328
-
329
- // generate the keys for the given input
330
- arena_allocator.Reset();
331
- GenerateKeys(arena_allocator, ordered_chunk, keys);
332
-
333
- // prepare the row_identifiers
334
- row_identifiers.Flatten(ordered_chunk.size());
335
- auto row_ids = FlatVector::GetData<row_t>(row_identifiers);
313
+ // prepare the row_identifiers
314
+ row_identifiers.Flatten(count);
315
+ auto row_ids = FlatVector::GetData<row_t>(row_identifiers);
336
316
 
337
- // construct the ART of this chunk
338
- auto art = make_unique<ART>(this->column_ids, this->table_io_manager, this->unbound_expressions,
339
- this->constraint_type, this->db);
340
- auto key_section = KeySection(0, ordered_chunk.size() - 1, 0, 0);
341
- auto has_constraint = IsUnique();
342
- Construct(keys, row_ids, art->tree, key_section, has_constraint);
343
-
344
- // merge art into temp_art
345
- if (!temp_art->MergeIndexes(lock, art.get())) {
346
- throw ConstraintException("Data contains duplicates on indexed column(s)");
347
- }
348
- }
349
-
350
- // NOTE: currently this code is only used for index creation, so we can assume that there are no
351
- // duplicate violations between the existing index and the new data,
352
- // so we do not need to revert any changes
353
- if (!this->MergeIndexes(lock, temp_art.get())) {
354
- throw ConstraintException("Data contains duplicates on indexed column(s)");
355
- }
317
+ auto key_section = KeySection(0, count - 1, 0, 0);
318
+ auto has_constraint = IsUnique();
319
+ Construct(keys, row_ids, this->tree, key_section, has_constraint);
356
320
  }
357
321
 
358
322
  bool ART::Insert(IndexLock &lock, DataChunk &input, Vector &row_ids) {
@@ -158,10 +158,17 @@ void Leaf::Merge(Node *&l_node, Node *&r_node) {
158
158
  Leaf *l_n = (Leaf *)l_node;
159
159
  Leaf *r_n = (Leaf *)r_node;
160
160
 
161
- // append non-duplicate row_ids to l_n
162
- for (idx_t i = 0; i < r_n->count; i++) {
163
- l_n->Insert(r_n->GetRowId(i));
161
+ auto l_capacity = l_n->GetCapacity();
162
+ auto l_row_ids = l_n->GetRowIds();
163
+ auto r_row_ids = r_n->GetRowIds();
164
+
165
+ if (l_n->count + r_n->count > l_capacity) {
166
+ auto new_capacity = NextPowerOfTwo(l_n->count + r_n->count);
167
+ l_row_ids = l_n->Resize(l_row_ids, l_n->count, new_capacity);
164
168
  }
169
+
170
+ memcpy(l_row_ids + l_n->count, r_row_ids, r_n->count * sizeof(row_t));
171
+ l_n->count += r_n->count;
165
172
  }
166
173
 
167
174
  BlockPointer Leaf::Serialize(duckdb::MetaBlockWriter &writer) {
@@ -314,6 +314,9 @@ void UpdateParentsOfNodes(Node *&l_node, Node *&r_node, ParentsOfNodes &parents)
314
314
  }
315
315
  }
316
316
 
317
+ // forward declaration
318
+ bool ResolvePrefixesAndMerge(MergeInfo &info, idx_t depth, ParentsOfNodes &parents);
319
+
317
320
  bool Merge(MergeInfo &info, idx_t depth, ParentsOfNodes &parents) {
318
321
 
319
322
  // always try to merge the smaller node into the bigger node
@@ -327,16 +330,7 @@ bool Merge(MergeInfo &info, idx_t depth, ParentsOfNodes &parents) {
327
330
  UpdateParentsOfNodes(info.l_node, info.r_node, parents);
328
331
  }
329
332
 
330
- switch (info.r_node->type) {
331
- case NodeType::N256:
332
- return Node256::Merge(info, depth, parents.l_parent, parents.l_pos);
333
- case NodeType::N48:
334
- return Node48::Merge(info, depth, parents.l_parent, parents.l_pos);
335
- case NodeType::N16:
336
- return Node16::Merge(info, depth, parents.l_parent, parents.l_pos);
337
- case NodeType::N4:
338
- return Node4::Merge(info, depth, parents.l_parent, parents.l_pos);
339
- case NodeType::NLeaf:
333
+ if (info.r_node->type == NodeType::NLeaf) {
340
334
  D_ASSERT(info.l_node->type == NodeType::NLeaf);
341
335
  D_ASSERT(info.r_node->type == NodeType::NLeaf);
342
336
  if (info.l_art->IsUnique()) {
@@ -345,7 +339,37 @@ bool Merge(MergeInfo &info, idx_t depth, ParentsOfNodes &parents) {
345
339
  Leaf::Merge(info.l_node, info.r_node);
346
340
  return true;
347
341
  }
348
- throw InternalException("Invalid node type for right node in merge.");
342
+
343
+ uint8_t key_byte;
344
+ idx_t r_child_pos = DConstants::INVALID_INDEX;
345
+
346
+ while (true) {
347
+ r_child_pos = info.r_node->GetNextPosAndByte(r_child_pos, key_byte);
348
+ if (r_child_pos == DConstants::INVALID_INDEX) {
349
+ break;
350
+ }
351
+ auto r_child = info.r_node->GetChild(*info.r_art, r_child_pos);
352
+ auto l_child_pos = info.l_node->GetChildPos(key_byte);
353
+
354
+ if (l_child_pos == DConstants::INVALID_INDEX) {
355
+ // insert child at empty position
356
+ Node::InsertChild(info.l_node, key_byte, r_child);
357
+ if (parents.l_parent) {
358
+ parents.l_parent->ReplaceChildPointer(parents.l_pos, info.l_node);
359
+ }
360
+ info.r_node->ReplaceChildPointer(r_child_pos, nullptr);
361
+
362
+ } else {
363
+ // recurse
364
+ auto l_child = info.l_node->GetChild(*info.l_art, l_child_pos);
365
+ MergeInfo child_info(info.l_art, info.r_art, l_child, r_child);
366
+ ParentsOfNodes child_parents(info.l_node, l_child_pos, info.r_node, r_child_pos);
367
+ if (!ResolvePrefixesAndMerge(child_info, depth + 1, child_parents)) {
368
+ return false;
369
+ }
370
+ }
371
+ }
372
+ return true;
349
373
  }
350
374
 
351
375
  bool ResolvePrefixesAndMerge(MergeInfo &info, idx_t depth, ParentsOfNodes &parents) {
@@ -355,22 +379,25 @@ bool ResolvePrefixesAndMerge(MergeInfo &info, idx_t depth, ParentsOfNodes &paren
355
379
 
356
380
  // NOTE: we always merge into the left ART
357
381
  D_ASSERT(l_node);
382
+ auto l_prefix_size = l_node->prefix.Size();
383
+ auto r_prefix_size = r_node->prefix.Size();
358
384
 
359
385
  // make sure that r_node has the longer (or equally long) prefix
360
- if (l_node->prefix.Size() > r_node->prefix.Size()) {
386
+ if (l_prefix_size > r_prefix_size) {
361
387
  swap(info.l_art, info.r_art);
362
388
  swap(l_node, r_node);
389
+ swap(l_prefix_size, r_prefix_size);
363
390
  UpdateParentsOfNodes(l_node, r_node, parents);
364
391
  }
365
392
 
366
393
  auto mismatch_pos = l_node->prefix.MismatchPosition(r_node->prefix);
367
394
 
368
395
  // both nodes have no prefix or the same prefix
369
- if (mismatch_pos == l_node->prefix.Size() && l_node->prefix.Size() == r_node->prefix.Size()) {
396
+ if (mismatch_pos == l_prefix_size && l_prefix_size == r_prefix_size) {
370
397
  return Merge(info, depth + mismatch_pos, parents);
371
398
  }
372
399
 
373
- if (mismatch_pos == l_node->prefix.Size()) {
400
+ if (mismatch_pos == l_prefix_size) {
374
401
  // r_node's prefix contains l_node's prefix
375
402
  // l_node cannot be a leaf, otherwise the key represented by l_node would be a subset of another key
376
403
  // which is not possible by our construction
@@ -418,28 +445,6 @@ bool ResolvePrefixesAndMerge(MergeInfo &info, idx_t depth, ParentsOfNodes &paren
418
445
  return true;
419
446
  }
420
447
 
421
- bool Node::MergeAtByte(MergeInfo &info, idx_t depth, idx_t &l_child_pos, idx_t &r_pos, uint8_t &key_byte,
422
- Node *&l_parent, idx_t l_pos) {
423
-
424
- auto r_child = info.r_node->GetChild(*info.r_art, r_pos);
425
-
426
- // insert child at empty position
427
- if (l_child_pos == DConstants::INVALID_INDEX) {
428
- Node::InsertChild(info.l_node, key_byte, r_child);
429
- if (l_parent) {
430
- l_parent->ReplaceChildPointer(l_pos, info.l_node);
431
- }
432
- info.r_node->ReplaceChildPointer(r_pos, nullptr);
433
- return true;
434
- }
435
-
436
- // recurse
437
- auto l_child = info.l_node->GetChild(*info.l_art, l_child_pos);
438
- MergeInfo child_info(info.l_art, info.r_art, l_child, r_child);
439
- ParentsOfNodes child_parents(info.l_node, l_child_pos, info.r_node, r_pos);
440
- return ResolvePrefixesAndMerge(child_info, depth + 1, child_parents);
441
- }
442
-
443
448
  bool Node::MergeARTs(ART *l_art, ART *r_art) {
444
449
 
445
450
  Node *null_parent = nullptr;
@@ -47,6 +47,19 @@ idx_t Node16::GetNextPos(idx_t pos) {
47
47
  return pos < count ? pos : DConstants::INVALID_INDEX;
48
48
  }
49
49
 
50
+ idx_t Node16::GetNextPosAndByte(idx_t pos, uint8_t &byte) {
51
+ if (pos == DConstants::INVALID_INDEX) {
52
+ byte = key[0];
53
+ return 0;
54
+ }
55
+ pos++;
56
+ if (pos < count) {
57
+ byte = key[pos];
58
+ return pos;
59
+ }
60
+ return DConstants::INVALID_INDEX;
61
+ }
62
+
50
63
  Node *Node16::GetChild(ART &art, idx_t pos) {
51
64
  D_ASSERT(pos < count);
52
65
  return children[pos].Unswizzle(art);
@@ -124,20 +137,6 @@ void Node16::EraseChild(Node *&node, int pos, ART &art) {
124
137
  }
125
138
  }
126
139
 
127
- bool Node16::Merge(MergeInfo &info, idx_t depth, Node *&l_parent, idx_t l_pos) {
128
-
129
- Node16 *r_n = (Node16 *)info.r_node;
130
-
131
- for (idx_t i = 0; i < info.r_node->count; i++) {
132
-
133
- auto l_child_pos = info.l_node->GetChildPos(r_n->key[i]);
134
- if (!Node::MergeAtByte(info, depth, l_child_pos, i, r_n->key[i], l_parent, l_pos)) {
135
- return false;
136
- }
137
- }
138
- return true;
139
- }
140
-
141
140
  idx_t Node16::GetSize() {
142
141
  return 16;
143
142
  }
@@ -47,6 +47,16 @@ idx_t Node256::GetNextPos(idx_t pos) {
47
47
  return Node::GetNextPos(pos);
48
48
  }
49
49
 
50
+ idx_t Node256::GetNextPosAndByte(idx_t pos, uint8_t &byte) {
51
+ for (pos == DConstants::INVALID_INDEX ? pos = 0 : pos++; pos < 256; pos++) {
52
+ if (children[pos]) {
53
+ byte = uint8_t(pos);
54
+ return pos;
55
+ }
56
+ }
57
+ return Node::GetNextPos(pos);
58
+ }
59
+
50
60
  Node *Node256::GetChild(ART &art, idx_t pos) {
51
61
  return children[pos].Unswizzle(art);
52
62
  }
@@ -82,21 +92,6 @@ void Node256::EraseChild(Node *&node, int pos, ART &art) {
82
92
  }
83
93
  }
84
94
 
85
- bool Node256::Merge(MergeInfo &info, idx_t depth, Node *&l_parent, idx_t l_pos) {
86
-
87
- for (idx_t i = 0; i < 256; i++) {
88
- if (info.r_node->GetChildPos(i) != DConstants::INVALID_INDEX) {
89
-
90
- auto l_child_pos = info.l_node->GetChildPos(i);
91
- auto key_byte = (uint8_t)i;
92
- if (!Node::MergeAtByte(info, depth, l_child_pos, i, key_byte, l_parent, l_pos)) {
93
- return false;
94
- }
95
- }
96
- }
97
- return true;
98
- }
99
-
100
95
  idx_t Node256::GetSize() {
101
96
  return 256;
102
97
  }
@@ -45,6 +45,19 @@ idx_t Node4::GetNextPos(idx_t pos) {
45
45
  return pos < count ? pos : DConstants::INVALID_INDEX;
46
46
  }
47
47
 
48
+ idx_t Node4::GetNextPosAndByte(idx_t pos, uint8_t &byte) {
49
+ if (pos == DConstants::INVALID_INDEX) {
50
+ byte = key[0];
51
+ return 0;
52
+ }
53
+ pos++;
54
+ if (pos < count) {
55
+ byte = key[pos];
56
+ return pos;
57
+ }
58
+ return DConstants::INVALID_INDEX;
59
+ }
60
+
48
61
  Node *Node4::GetChild(ART &art, idx_t pos) {
49
62
  D_ASSERT(pos < count);
50
63
  return children[pos].Unswizzle(art);
@@ -117,20 +130,6 @@ void Node4::EraseChild(Node *&node, int pos, ART &art) {
117
130
  }
118
131
  }
119
132
 
120
- bool Node4::Merge(MergeInfo &info, idx_t depth, Node *&l_parent, idx_t l_pos) {
121
-
122
- Node4 *r_n = (Node4 *)info.r_node;
123
-
124
- for (idx_t i = 0; i < info.r_node->count; i++) {
125
-
126
- auto l_child_pos = info.l_node->GetChildPos(r_n->key[i]);
127
- if (!Node::MergeAtByte(info, depth, l_child_pos, i, r_n->key[i], l_parent, l_pos)) {
128
- return false;
129
- }
130
- }
131
- return true;
132
- }
133
-
134
133
  idx_t Node4::GetSize() {
135
134
  return 4;
136
135
  }
@@ -51,6 +51,16 @@ idx_t Node48::GetNextPos(idx_t pos) {
51
51
  return Node::GetNextPos(pos);
52
52
  }
53
53
 
54
+ idx_t Node48::GetNextPosAndByte(idx_t pos, uint8_t &byte) {
55
+ for (pos == DConstants::INVALID_INDEX ? pos = 0 : pos++; pos < 256; pos++) {
56
+ if (child_index[pos] != Node::EMPTY_MARKER) {
57
+ byte = uint8_t(pos);
58
+ return pos;
59
+ }
60
+ }
61
+ return Node::GetNextPos(pos);
62
+ }
63
+
54
64
  Node *Node48::GetChild(ART &art, idx_t pos) {
55
65
  D_ASSERT(child_index[pos] != Node::EMPTY_MARKER);
56
66
  return children[child_index[pos]].Unswizzle(art);
@@ -114,23 +124,6 @@ void Node48::EraseChild(Node *&node, int pos, ART &art) {
114
124
  }
115
125
  }
116
126
 
117
- bool Node48::Merge(MergeInfo &info, idx_t depth, Node *&l_parent, idx_t l_pos) {
118
-
119
- Node48 *r_n = (Node48 *)info.r_node;
120
-
121
- for (idx_t i = 0; i < 256; i++) {
122
- if (r_n->child_index[i] != Node::EMPTY_MARKER) {
123
-
124
- auto l_child_pos = info.l_node->GetChildPos(i);
125
- auto key_byte = (uint8_t)i;
126
- if (!Node::MergeAtByte(info, depth, l_child_pos, i, key_byte, l_parent, l_pos)) {
127
- return false;
128
- }
129
- }
130
- }
131
- return true;
132
- }
133
-
134
127
  idx_t Node48::GetSize() {
135
128
  return 48;
136
129
  }
@@ -2,10 +2,6 @@
2
2
 
3
3
  namespace duckdb {
4
4
 
5
- uint32_t Prefix::Size() const {
6
- return size;
7
- }
8
-
9
5
  bool Prefix::IsInlined() const {
10
6
  return size <= PREFIX_INLINE_BYTES;
11
7
  }
@@ -1,9 +1,6 @@
1
1
  #include "duckdb/execution/operator/schema/physical_create_index.hpp"
2
2
 
3
3
  #include "duckdb/catalog/catalog_entry/index_catalog_entry.hpp"
4
- #include "duckdb/catalog/catalog_entry/schema_catalog_entry.hpp"
5
- #include "duckdb/catalog/catalog_entry/table_catalog_entry.hpp"
6
- #include "duckdb/execution/expression_executor.hpp"
7
4
  #include "duckdb/main/client_context.hpp"
8
5
  #include "duckdb/storage/storage_manager.hpp"
9
6
  #include "duckdb/main/database_manager.hpp"
@@ -22,20 +19,13 @@ public:
22
19
 
23
20
  class CreateIndexLocalSinkState : public LocalSinkState {
24
21
  public:
25
- explicit CreateIndexLocalSinkState(const vector<unique_ptr<Expression>> &expressions) : executor(expressions) {
26
- }
22
+ explicit CreateIndexLocalSinkState(ClientContext &context) : arena_allocator(Allocator::Get(context)) {};
27
23
 
28
- //! Local indexes build from chunks of the scanned data
29
24
  unique_ptr<Index> local_index;
30
-
25
+ ArenaAllocator arena_allocator;
26
+ vector<Key> keys;
31
27
  DataChunk key_chunk;
32
- unique_ptr<GlobalSortState> global_sort_state;
33
- LocalSortState local_sort_state;
34
-
35
- RowLayout payload_layout;
36
- vector<LogicalType> payload_types;
37
-
38
- ExpressionExecutor executor;
28
+ vector<column_t> key_column_ids;
39
29
  };
40
30
 
41
31
  unique_ptr<GlobalSinkState> PhysicalCreateIndex::GetGlobalSinkState(ClientContext &context) const {
@@ -51,14 +41,12 @@ unique_ptr<GlobalSinkState> PhysicalCreateIndex::GetGlobalSinkState(ClientContex
51
41
  default:
52
42
  throw InternalException("Unimplemented index type");
53
43
  }
54
-
55
44
  return (std::move(state));
56
45
  }
57
46
 
58
47
  unique_ptr<LocalSinkState> PhysicalCreateIndex::GetLocalSinkState(ExecutionContext &context) const {
59
- auto &allocator = Allocator::Get(table.storage->db);
60
48
 
61
- auto state = make_unique<CreateIndexLocalSinkState>(expressions);
49
+ auto state = make_unique<CreateIndexLocalSinkState>(context.client);
62
50
 
63
51
  // create the local index
64
52
  switch (info->index_type) {
@@ -70,82 +58,49 @@ unique_ptr<LocalSinkState> PhysicalCreateIndex::GetLocalSinkState(ExecutionConte
70
58
  default:
71
59
  throw InternalException("Unimplemented index type");
72
60
  }
61
+ state->keys = vector<Key>(STANDARD_VECTOR_SIZE);
62
+ state->key_chunk.Initialize(Allocator::Get(context.client), state->local_index->logical_types);
73
63
 
74
- state->key_chunk.Initialize(allocator, state->local_index->logical_types);
75
-
76
- // ordering of the entries of the index
77
- vector<BoundOrderByNode> orders;
78
- for (idx_t i = 0; i < state->local_index->logical_types.size(); i++) {
79
- auto col_expr = make_unique_base<Expression, BoundReferenceExpression>(state->local_index->logical_types[i], i);
80
- orders.emplace_back(OrderType::ASCENDING, OrderByNullType::NULLS_FIRST, std::move(col_expr));
64
+ for (idx_t i = 0; i < state->key_chunk.ColumnCount(); i++) {
65
+ state->key_column_ids.push_back(i);
81
66
  }
82
-
83
- // row layout of the global sort state
84
- state->payload_types = state->local_index->logical_types;
85
- state->payload_types.emplace_back(LogicalType::ROW_TYPE);
86
- state->payload_layout.Initialize(state->payload_types);
87
-
88
- // initialize global and local sort state
89
- auto &buffer_manager = BufferManager::GetBufferManager(table.storage->db);
90
- state->global_sort_state = make_unique<GlobalSortState>(buffer_manager, orders, state->payload_layout);
91
- state->local_sort_state.Initialize(*state->global_sort_state, buffer_manager);
92
-
93
67
  return std::move(state);
94
68
  }
95
69
 
96
70
  SinkResultType PhysicalCreateIndex::Sink(ExecutionContext &context, GlobalSinkState &gstate_p, LocalSinkState &lstate_p,
97
71
  DataChunk &input) const {
98
72
 
99
- // here, we sink all incoming data into the local_sink_state after executing the expression executor
73
+ D_ASSERT(input.ColumnCount() >= 2);
100
74
  auto &lstate = (CreateIndexLocalSinkState &)lstate_p;
75
+ auto &row_identifiers = input.data[input.ColumnCount() - 1];
101
76
 
102
- // resolve the expressions for this chunk
103
- D_ASSERT(!lstate.executor.HasContext());
104
- lstate.key_chunk.Reset();
105
- lstate.executor.Execute(input, lstate.key_chunk);
106
-
107
- // create the payload chunk
108
- DataChunk payload_chunk;
109
- payload_chunk.InitializeEmpty(lstate.payload_types);
110
- for (idx_t i = 0; i < lstate.local_index->logical_types.size(); i++) {
111
- payload_chunk.data[i].Reference(lstate.key_chunk.data[i]);
112
- }
113
- payload_chunk.data[lstate.payload_types.size() - 1].Reference(input.data[input.ColumnCount() - 1]);
114
- payload_chunk.SetCardinality(input.size());
77
+ // generate the keys for the given input
78
+ lstate.key_chunk.ReferenceColumns(input, lstate.key_column_ids);
79
+ lstate.arena_allocator.Reset();
80
+ ART::GenerateKeys(lstate.arena_allocator, lstate.key_chunk, lstate.keys);
115
81
 
116
- // sink the chunks into the local sort state
117
- lstate.local_sort_state.SinkChunk(lstate.key_chunk, payload_chunk);
82
+ auto art = make_unique<ART>(lstate.local_index->column_ids, lstate.local_index->table_io_manager,
83
+ lstate.local_index->unbound_expressions, lstate.local_index->constraint_type,
84
+ table.storage->db);
85
+ art->ConstructFromSorted(lstate.key_chunk.size(), lstate.keys, row_identifiers);
118
86
 
87
+ // merge into the local ART
88
+ {
89
+ IndexLock local_lock;
90
+ lstate.local_index->InitializeLock(local_lock);
91
+ if (!lstate.local_index->MergeIndexes(local_lock, art.get())) {
92
+ throw ConstraintException("Data contains duplicates on indexed column(s)");
93
+ }
94
+ }
119
95
  return SinkResultType::NEED_MORE_INPUT;
120
96
  }
121
97
 
122
98
  void PhysicalCreateIndex::Combine(ExecutionContext &context, GlobalSinkState &gstate_p,
123
99
  LocalSinkState &lstate_p) const {
124
100
 
125
- // here, we take the sunk data chunks and sort them
126
- // then, we scan the sorted data and build a local index from it
127
- // finally, we merge the local index into the global index
128
-
129
101
  auto &gstate = (CreateIndexGlobalSinkState &)gstate_p;
130
102
  auto &lstate = (CreateIndexLocalSinkState &)lstate_p;
131
103
 
132
- auto &allocator = Allocator::Get(table.storage->db);
133
-
134
- // add local state to global state, which sorts the data
135
- lstate.global_sort_state->AddLocalState(lstate.local_sort_state);
136
- lstate.global_sort_state->PrepareMergePhase();
137
-
138
- // scan the sorted row data and construct the index from it
139
- {
140
- IndexLock local_lock;
141
- lstate.local_index->InitializeLock(local_lock);
142
- if (!lstate.global_sort_state->sorted_blocks.empty()) {
143
- PayloadScanner scanner(*lstate.global_sort_state->sorted_blocks[0]->payload_data,
144
- *lstate.global_sort_state);
145
- lstate.local_index->ConstructAndMerge(local_lock, scanner, allocator);
146
- }
147
- }
148
-
149
104
  // merge the local index into the global index
150
105
  gstate.global_index->MergeIndexes(lstate.local_index.get());
151
106
  }
@@ -1,7 +1,9 @@
1
1
  #include "duckdb/catalog/catalog_entry/table_catalog_entry.hpp"
2
+ #include "duckdb/execution/operator/projection/physical_projection.hpp"
2
3
  #include "duckdb/execution/operator/filter/physical_filter.hpp"
3
4
  #include "duckdb/execution/operator/scan/physical_table_scan.hpp"
4
5
  #include "duckdb/execution/operator/schema/physical_create_index.hpp"
6
+ #include "duckdb/execution/operator/order/physical_order.hpp"
5
7
  #include "duckdb/execution/physical_plan_generator.hpp"
6
8
  #include "duckdb/function/table/table_scan.hpp"
7
9
  #include "duckdb/planner/filter/null_filter.hpp"
@@ -12,9 +14,26 @@ namespace duckdb {
12
14
 
13
15
  unique_ptr<PhysicalOperator> PhysicalPlanGenerator::CreatePlan(LogicalCreateIndex &op) {
14
16
 
17
+ // generate a physical plan for the parallel index creation which consists of the following operators
18
+ // table scan - projection (for expression execution) - filter (NOT NULL) - order - create index
19
+
15
20
  D_ASSERT(op.children.empty());
16
21
 
22
+ // validate that all expressions contain valid scalar functions
23
+ // e.g. get_current_timestamp(), random(), and sequence values are not allowed as ART keys
24
+ // because they make deletions and lookups unfeasible
25
+ for (idx_t i = 0; i < op.unbound_expressions.size(); i++) {
26
+ auto &expr = op.unbound_expressions[i];
27
+ if (expr->expression_class == ExpressionClass::BOUND_FUNCTION) {
28
+ auto &func_expr = (BoundFunctionExpression &)*expr;
29
+ if (func_expr.function.side_effects == FunctionSideEffects::HAS_SIDE_EFFECTS) {
30
+ throw BinderException("Index keys cannot contain the \"%s\" function.", func_expr.function.name);
31
+ }
32
+ }
33
+ }
34
+
17
35
  // table scan operator for index key columns and row IDs
36
+
18
37
  unique_ptr<TableFilterSet> table_filters;
19
38
  op.info->column_ids.emplace_back(COLUMN_IDENTIFIER_ROW_ID);
20
39
 
@@ -31,30 +50,61 @@ unique_ptr<PhysicalOperator> PhysicalPlanGenerator::CreatePlan(LogicalCreateInde
31
50
  D_ASSERT(op.info->scan_types.size() - 1 <= op.info->names.size());
32
51
  D_ASSERT(op.info->scan_types.size() - 1 <= op.info->column_ids.size());
33
52
 
53
+ // projection to execute expressions on the key columns
54
+
55
+ vector<LogicalType> new_column_types;
56
+ vector<unique_ptr<Expression>> select_list;
57
+ for (idx_t i = 0; i < op.expressions.size(); i++) {
58
+ new_column_types.push_back(op.expressions[i]->return_type);
59
+ select_list.push_back(std::move(op.expressions[i]));
60
+ }
61
+ new_column_types.emplace_back(LogicalType::ROW_TYPE);
62
+ select_list.push_back(make_unique<BoundReferenceExpression>(LogicalType::ROW_TYPE, op.info->scan_types.size() - 1));
63
+
64
+ auto projection =
65
+ make_unique<PhysicalProjection>(new_column_types, std::move(select_list), op.estimated_cardinality);
66
+ projection->children.push_back(std::move(table_scan));
67
+
34
68
  // filter operator for IS_NOT_NULL on each key column
69
+
35
70
  vector<LogicalType> filter_types;
36
71
  vector<unique_ptr<Expression>> filter_select_list;
37
72
 
38
- for (idx_t i = 0; i < op.info->scan_types.size() - 1; i++) {
39
- filter_types.push_back(op.info->scan_types[i]);
73
+ for (idx_t i = 0; i < new_column_types.size() - 1; i++) {
74
+ filter_types.push_back(new_column_types[i]);
40
75
  auto is_not_null_expr =
41
76
  make_unique<BoundOperatorExpression>(ExpressionType::OPERATOR_IS_NOT_NULL, LogicalType::BOOLEAN);
42
- auto bound_ref =
43
- make_unique<BoundReferenceExpression>(op.info->names[op.info->column_ids[i]], op.info->scan_types[i], i);
77
+ auto bound_ref = make_unique<BoundReferenceExpression>(new_column_types[i], i);
44
78
  is_not_null_expr->children.push_back(std::move(bound_ref));
45
79
  filter_select_list.push_back(std::move(is_not_null_expr));
46
80
  }
47
81
 
48
82
  auto null_filter =
49
- make_unique<PhysicalFilter>(std::move(filter_types), std::move(filter_select_list), STANDARD_VECTOR_SIZE);
83
+ make_unique<PhysicalFilter>(std::move(filter_types), std::move(filter_select_list), op.estimated_cardinality);
50
84
  null_filter->types.emplace_back(LogicalType::ROW_TYPE);
51
- null_filter->children.push_back(std::move(table_scan));
85
+ null_filter->children.push_back(std::move(projection));
86
+
87
+ // order operator
88
+
89
+ vector<BoundOrderByNode> orders;
90
+ vector<idx_t> projections;
91
+ for (idx_t i = 0; i < new_column_types.size() - 1; i++) {
92
+ auto col_expr = make_unique_base<Expression, BoundReferenceExpression>(new_column_types[i], i);
93
+ orders.emplace_back(OrderType::ASCENDING, OrderByNullType::NULLS_FIRST, std::move(col_expr));
94
+ projections.emplace_back(i);
95
+ }
96
+ projections.emplace_back(new_column_types.size() - 1);
97
+
98
+ auto physical_order = make_unique<PhysicalOrder>(new_column_types, std::move(orders), std::move(projections),
99
+ op.estimated_cardinality);
100
+ physical_order->children.push_back(std::move(null_filter));
52
101
 
53
102
  // actual physical create index operator
54
- auto physical_create_index = make_unique<PhysicalCreateIndex>(
55
- op, op.table, op.info->column_ids, std::move(op.expressions), std::move(op.info),
56
- std::move(op.unbound_expressions), op.estimated_cardinality);
57
- physical_create_index->children.push_back(std::move(null_filter));
103
+
104
+ auto physical_create_index =
105
+ make_unique<PhysicalCreateIndex>(op, op.table, op.info->column_ids, std::move(op.info),
106
+ std::move(op.unbound_expressions), op.estimated_cardinality);
107
+ physical_create_index->children.push_back(std::move(physical_order));
58
108
  return std::move(physical_create_index);
59
109
  }
60
110
 
@@ -1,8 +1,8 @@
1
1
  #ifndef DUCKDB_VERSION
2
- #define DUCKDB_VERSION "0.6.2-dev1070"
2
+ #define DUCKDB_VERSION "0.6.2-dev1092"
3
3
  #endif
4
4
  #ifndef DUCKDB_SOURCE_ID
5
- #define DUCKDB_SOURCE_ID "e0cff6a3dd"
5
+ #define DUCKDB_SOURCE_ID "123b6e7bbd"
6
6
  #endif
7
7
  #include "duckdb/function/table/system_functions.hpp"
8
8
  #include "duckdb/main/database.hpp"
@@ -49,6 +49,7 @@ enum VerifyExistenceType : uint8_t {
49
49
 
50
50
  class ART : public Index {
51
51
  public:
52
+ //! Constructs an ART containing the bound expressions, which are resolved during index construction
52
53
  ART(const vector<column_t> &column_ids, TableIOManager &table_io_manager,
53
54
  const vector<unique_ptr<Expression>> &unbound_expressions, IndexConstraintType constraint_type,
54
55
  AttachedDatabase &db, idx_t block_id = DConstants::INVALID_INDEX,
@@ -57,7 +58,7 @@ public:
57
58
 
58
59
  //! Root of the tree
59
60
  Node *tree;
60
-
61
+ //! Attached database
61
62
  AttachedDatabase &db;
62
63
 
63
64
  public:
@@ -88,8 +89,7 @@ public:
88
89
  //! Insert data into the index.
89
90
  bool Insert(IndexLock &lock, DataChunk &data, Vector &row_ids) override;
90
91
 
91
- //! Construct ARTs from sorted chunks and merge them.
92
- void ConstructAndMerge(IndexLock &lock, PayloadScanner &scanner, Allocator &allocator) override;
92
+ void ConstructFromSorted(idx_t count, vector<Key> &keys, Vector &row_identifiers);
93
93
 
94
94
  //! Search Equal and fetches the row IDs
95
95
  bool SearchEqual(Key &key, idx_t max_count, vector<row_t> &result_ids);
@@ -88,6 +88,11 @@ public:
88
88
  virtual idx_t GetNextPos(idx_t pos) {
89
89
  return DConstants::INVALID_INDEX;
90
90
  }
91
+ //! Get the next position and byte in the node, or DConstants::INVALID_INDEX if there is no next position. if pos ==
92
+ //! DConstants::INVALID_INDEX, then the first valid position in the node is returned
93
+ virtual idx_t GetNextPosAndByte(idx_t pos, uint8_t &byte) {
94
+ return DConstants::INVALID_INDEX;
95
+ }
91
96
  //! Get the child at the specified position in the node. pos should be between [0, count). Throws an assertion if
92
97
  //! the element is not found
93
98
  virtual Node *GetChild(ART &art, idx_t pos);
@@ -110,9 +115,6 @@ public:
110
115
 
111
116
  //! Deserialize this node
112
117
  static Node *Deserialize(ART &art, idx_t block_id, idx_t offset);
113
- //! Merge r_node into l_node at the specified byte
114
- static bool MergeAtByte(MergeInfo &info, idx_t depth, idx_t &l_child_pos, idx_t &r_pos, uint8_t &key_byte,
115
- Node *&l_parent, idx_t l_pos);
116
118
  //! Merge two ART
117
119
  static bool MergeARTs(ART *l_art, ART *r_art);
118
120
 
@@ -29,6 +29,8 @@ public:
29
29
  idx_t GetMin() override;
30
30
  //! Get the next position in the node, or DConstants::INVALID_INDEX if there is no next position
31
31
  idx_t GetNextPos(idx_t pos) override;
32
+ //! Get the next position in the node, or DConstants::INVALID_INDEX if there is no next position
33
+ idx_t GetNextPosAndByte(idx_t pos, uint8_t &byte) override;
32
34
  //! Get Node16 child
33
35
  Node *GetChild(ART &art, idx_t pos) override;
34
36
  //! Replace child pointer
@@ -38,8 +40,6 @@ public:
38
40
  static void InsertChild(Node *&node, uint8_t key_byte, Node *new_child);
39
41
  //! Erase the child at pos and (if necessary) shrink to Node4
40
42
  static void EraseChild(Node *&node, int pos, ART &art);
41
- //! Merge Node16 into l_node
42
- static bool Merge(MergeInfo &info, idx_t depth, Node *&l_parent, idx_t l_pos);
43
43
  //! Returns the size (maximum capacity) of the Node16
44
44
  static idx_t GetSize();
45
45
  };
@@ -28,6 +28,8 @@ public:
28
28
  idx_t GetMin() override;
29
29
  //! Get the next position in the node, or DConstants::INVALID_INDEX if there is no next position
30
30
  idx_t GetNextPos(idx_t pos) override;
31
+ //! Get the next position in the node, or DConstants::INVALID_INDEX if there is no next position
32
+ idx_t GetNextPosAndByte(idx_t pos, uint8_t &byte) override;
31
33
  //! Get Node256 child
32
34
  Node *GetChild(ART &art, idx_t pos) override;
33
35
  //! Replace child pointer
@@ -37,8 +39,6 @@ public:
37
39
  static void InsertChild(Node *&node, uint8_t key_byte, Node *new_child);
38
40
  //! Erase the child at pos and (if necessary) shrink to Node48
39
41
  static void EraseChild(Node *&node, int pos, ART &art);
40
- //! Merge Node256 into l_node
41
- static bool Merge(MergeInfo &info, idx_t depth, Node *&l_parent, idx_t l_pos);
42
42
  //! Returns the size (maximum capacity) of the Node256
43
43
  static idx_t GetSize();
44
44
  };
@@ -31,6 +31,8 @@ public:
31
31
  idx_t GetMin() override;
32
32
  //! Get the next position in the node, or DConstants::INVALID_INDEX if there is no next position
33
33
  idx_t GetNextPos(idx_t pos) override;
34
+ //! Get the next position in the node, or DConstants::INVALID_INDEX if there is no next position
35
+ idx_t GetNextPosAndByte(idx_t pos, uint8_t &byte) override;
34
36
  //! Get Node4 child
35
37
  Node *GetChild(ART &art, idx_t pos) override;
36
38
  //! Replace child pointer
@@ -40,8 +42,6 @@ public:
40
42
  static void InsertChild(Node *&node, uint8_t key_byte, Node *new_child);
41
43
  //! Erase the child at pos and (if necessary) merge with last child
42
44
  static void EraseChild(Node *&node, int pos, ART &art);
43
- //! Merge Node4 into l_node
44
- static bool Merge(MergeInfo &info, idx_t depth, Node *&l_parent, idx_t l_pos);
45
45
  //! Returns the size (maximum capacity) of the Node4
46
46
  static idx_t GetSize();
47
47
  };
@@ -29,6 +29,8 @@ public:
29
29
  idx_t GetMin() override;
30
30
  //! Get the next position in the node, or DConstants::INVALID_INDEX if there is no next position
31
31
  idx_t GetNextPos(idx_t pos) override;
32
+ //! Get the next position in the node, or DConstants::INVALID_INDEX if there is no next position
33
+ idx_t GetNextPosAndByte(idx_t pos, uint8_t &byte) override;
32
34
  //! Get Node48 child
33
35
  Node *GetChild(ART &art, idx_t pos) override;
34
36
  //! Replace child pointer
@@ -38,8 +40,6 @@ public:
38
40
  static void InsertChild(Node *&node, uint8_t key_byte, Node *new_child);
39
41
  //! Erase the child at pos and (if necessary) shrink to Node16
40
42
  static void EraseChild(Node *&node, int pos, ART &art);
41
- //! Merge Node48 into l_node
42
- static bool Merge(MergeInfo &info, idx_t depth, Node *&l_parent, idx_t l_pos);
43
43
  //! Returns the size (maximum capacity) of the Node48
44
44
  static idx_t GetSize();
45
45
  };
@@ -23,8 +23,10 @@ public:
23
23
  Prefix(Prefix &other_prefix, uint32_t size);
24
24
  ~Prefix();
25
25
 
26
- // Returns the Prefix's size
27
- uint32_t Size() const;
26
+ //! Returns the Prefix's size
27
+ inline uint32_t Size() const {
28
+ return size;
29
+ }
28
30
  //! Return a pointer to the prefix data
29
31
  uint8_t *GetPrefixData();
30
32
  const uint8_t *GetPrefixData() const;
@@ -18,15 +18,14 @@
18
18
 
19
19
  namespace duckdb {
20
20
 
21
- //! Physically CREATE INDEX statement
21
+ //! Physical CREATE (UNIQUE) INDEX statement
22
22
  class PhysicalCreateIndex : public PhysicalOperator {
23
23
  public:
24
24
  PhysicalCreateIndex(LogicalOperator &op, TableCatalogEntry &table, vector<column_t> column_ids,
25
- vector<unique_ptr<Expression>> expressions, unique_ptr<CreateIndexInfo> info,
26
- vector<unique_ptr<Expression>> unbound_expressions, idx_t estimated_cardinality)
25
+ unique_ptr<CreateIndexInfo> info, vector<unique_ptr<Expression>> unbound_expressions,
26
+ idx_t estimated_cardinality)
27
27
  : PhysicalOperator(PhysicalOperatorType::CREATE_INDEX, op.types, estimated_cardinality), table(table),
28
- expressions(std::move(expressions)), info(std::move(info)),
29
- unbound_expressions(std::move(unbound_expressions)) {
28
+ info(std::move(info)), unbound_expressions(std::move(unbound_expressions)) {
30
29
 
31
30
  // convert virtual column ids to storage column ids
32
31
  for (auto &column_id : column_ids) {
@@ -38,21 +37,20 @@ public:
38
37
  TableCatalogEntry &table;
39
38
  //! The list of column IDs required for the index
40
39
  vector<column_t> storage_ids;
41
- //! Set of expressions to index by
42
- vector<unique_ptr<Expression>> expressions;
43
40
  //! Info for index creation
44
41
  unique_ptr<CreateIndexInfo> info;
45
42
  //! Unbound expressions to be used in the optimizer
46
43
  vector<unique_ptr<Expression>> unbound_expressions;
47
44
 
48
45
  public:
49
- // Source interface
46
+ //! Source interface, NOP for this operator
50
47
  void GetData(ExecutionContext &context, DataChunk &chunk, GlobalSourceState &gstate,
51
48
  LocalSourceState &lstate) const override;
52
49
 
53
50
  public:
54
- // Sink interface
51
+ //! Sink interface, thread-local sink states
55
52
  unique_ptr<LocalSinkState> GetLocalSinkState(ExecutionContext &context) const override;
53
+ //! Sink interface, global sink state
56
54
  unique_ptr<GlobalSinkState> GetGlobalSinkState(ClientContext &context) const override;
57
55
 
58
56
  SinkResultType Sink(ExecutionContext &context, GlobalSinkState &gstate_p, LocalSinkState &lstate_p,
@@ -60,6 +60,8 @@ public:
60
60
  DUCKDB_API const vector<LogicalType> &GetTypes();
61
61
  //! Returns the result names of the prepared statement
62
62
  DUCKDB_API const vector<string> &GetNames();
63
+ //! Returns the map of parameter index to the expected type of parameter
64
+ DUCKDB_API vector<LogicalType> GetExpectedParameterTypes() const;
63
65
 
64
66
  //! Create a pending query result of the prepared statement with the given set of arguments
65
67
  template <typename... Args>
@@ -39,7 +39,7 @@ public:
39
39
  TableIOManager &table_io_manager;
40
40
  //! Column identifiers to extract from the base table
41
41
  vector<column_t> column_ids;
42
- //! unordered_set of column_ids used by the index
42
+ //! Unordered_set of column_ids used by the index
43
43
  unordered_set<column_t> column_id_set;
44
44
  //! Unbound expressions used by the index
45
45
  vector<unique_ptr<Expression>> unbound_expressions;
@@ -47,7 +47,7 @@ public:
47
47
  vector<PhysicalType> types;
48
48
  //! The logical types of the expressions
49
49
  vector<LogicalType> logical_types;
50
- //! constraint type
50
+ //! Index constraint type (primary key, foreign key, ...)
51
51
  IndexConstraintType constraint_type;
52
52
 
53
53
  public:
@@ -83,8 +83,6 @@ public:
83
83
 
84
84
  //! Insert data into the index. Does not lock the index.
85
85
  virtual bool Insert(IndexLock &lock, DataChunk &input, Vector &row_identifiers) = 0;
86
- //! Construct an index from sorted chunks of keys.
87
- virtual void ConstructAndMerge(IndexLock &lock, PayloadScanner &scanner, Allocator &allocator) = 0;
88
86
 
89
87
  //! Merge other_index into this index.
90
88
  virtual bool MergeIndexes(IndexLock &state, Index *other_index) = 0;
@@ -52,6 +52,19 @@ const vector<string> &PreparedStatement::GetNames() {
52
52
  return data->names;
53
53
  }
54
54
 
55
+ vector<LogicalType> PreparedStatement::GetExpectedParameterTypes() const {
56
+ D_ASSERT(data);
57
+ vector<LogicalType> expected_types(data->value_map.size());
58
+ for (auto &it : data->value_map) {
59
+ D_ASSERT(it.first >= 1);
60
+ idx_t param_index = it.first - 1;
61
+ D_ASSERT(param_index < expected_types.size());
62
+ D_ASSERT(it.second);
63
+ expected_types[param_index] = it.second->value.type();
64
+ }
65
+ return expected_types;
66
+ }
67
+
55
68
  unique_ptr<QueryResult> PreparedStatement::Execute(vector<Value> &values, bool allow_stream_result) {
56
69
  auto pending = PendingQuery(values, allow_stream_result);
57
70
  if (pending->HasError()) {
@@ -10,7 +10,9 @@ namespace duckdb {
10
10
 
11
11
  Index::Index(IndexType type, TableIOManager &table_io_manager, const vector<column_t> &column_ids_p,
12
12
  const vector<unique_ptr<Expression>> &unbound_expressions, IndexConstraintType constraint_type_p)
13
+
13
14
  : type(type), table_io_manager(table_io_manager), column_ids(column_ids_p), constraint_type(constraint_type_p) {
15
+
14
16
  for (auto &expr : unbound_expressions) {
15
17
  types.push_back(expr->return_type.InternalType());
16
18
  logical_types.push_back(expr->return_type);