duckdb 0.3.3-dev1096.0 → 0.3.3-dev1133.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.
- package/package.json +1 -1
- package/src/duckdb.cpp +1586 -40
- package/src/duckdb.hpp +36 -33
- package/src/parquet-amalgamation.cpp +18872 -18872
package/src/duckdb.cpp
CHANGED
|
@@ -6393,6 +6393,8 @@ string PhysicalOperatorToString(PhysicalOperatorType type) {
|
|
|
6393
6393
|
return "INDEX_JOIN";
|
|
6394
6394
|
case PhysicalOperatorType::PIECEWISE_MERGE_JOIN:
|
|
6395
6395
|
return "PIECEWISE_MERGE_JOIN";
|
|
6396
|
+
case PhysicalOperatorType::IE_JOIN:
|
|
6397
|
+
return "IE_JOIN";
|
|
6396
6398
|
case PhysicalOperatorType::CROSS_PRODUCT:
|
|
6397
6399
|
return "CROSS_PRODUCT";
|
|
6398
6400
|
case PhysicalOperatorType::UNION:
|
|
@@ -27978,7 +27980,26 @@ public:
|
|
|
27978
27980
|
//! Used to scan the data into DataChunks after sorting
|
|
27979
27981
|
struct PayloadScanner {
|
|
27980
27982
|
public:
|
|
27981
|
-
PayloadScanner(SortedData &sorted_data, GlobalSortState &global_sort_state);
|
|
27983
|
+
PayloadScanner(SortedData &sorted_data, GlobalSortState &global_sort_state, bool flush = true);
|
|
27984
|
+
explicit PayloadScanner(GlobalSortState &global_sort_state, bool flush = true);
|
|
27985
|
+
|
|
27986
|
+
//! Scan a single block
|
|
27987
|
+
PayloadScanner(GlobalSortState &global_sort_state, idx_t block_idx);
|
|
27988
|
+
|
|
27989
|
+
//! The type layout of the payload
|
|
27990
|
+
inline const vector<LogicalType> &GetPayloadTypes() const {
|
|
27991
|
+
return sorted_data.layout.GetTypes();
|
|
27992
|
+
}
|
|
27993
|
+
|
|
27994
|
+
//! The number of rows scanned so far
|
|
27995
|
+
inline idx_t Scanned() const {
|
|
27996
|
+
return total_scanned;
|
|
27997
|
+
}
|
|
27998
|
+
|
|
27999
|
+
//! The number of remaining rows
|
|
28000
|
+
inline idx_t Remaining() const {
|
|
28001
|
+
return total_count - total_scanned;
|
|
28002
|
+
}
|
|
27982
28003
|
|
|
27983
28004
|
//! Scans the next data chunk from the sorted data
|
|
27984
28005
|
void Scan(DataChunk &chunk);
|
|
@@ -27996,6 +28017,8 @@ private:
|
|
|
27996
28017
|
Vector addresses = Vector(LogicalType::POINTER);
|
|
27997
28018
|
//! The number of rows scanned so far
|
|
27998
28019
|
idx_t total_scanned;
|
|
28020
|
+
//! Whether to flush the blocks after scanning
|
|
28021
|
+
const bool flush;
|
|
27999
28022
|
};
|
|
28000
28023
|
|
|
28001
28024
|
} // namespace duckdb
|
|
@@ -30249,21 +30272,34 @@ void SBScanState::SetIndices(idx_t block_idx_to, idx_t entry_idx_to) {
|
|
|
30249
30272
|
entry_idx = entry_idx_to;
|
|
30250
30273
|
}
|
|
30251
30274
|
|
|
30252
|
-
PayloadScanner::PayloadScanner(SortedData &sorted_data, GlobalSortState &global_sort_state)
|
|
30275
|
+
PayloadScanner::PayloadScanner(SortedData &sorted_data, GlobalSortState &global_sort_state, bool flush_p)
|
|
30253
30276
|
: sorted_data(sorted_data), read_state(global_sort_state.buffer_manager, global_sort_state),
|
|
30254
|
-
total_count(sorted_data.Count()), global_sort_state(global_sort_state), total_scanned(0) {
|
|
30277
|
+
total_count(sorted_data.Count()), global_sort_state(global_sort_state), total_scanned(0), flush(flush_p) {
|
|
30278
|
+
}
|
|
30279
|
+
|
|
30280
|
+
PayloadScanner::PayloadScanner(GlobalSortState &global_sort_state, bool flush_p)
|
|
30281
|
+
: PayloadScanner(*global_sort_state.sorted_blocks[0]->payload_data, global_sort_state, flush_p) {
|
|
30282
|
+
}
|
|
30283
|
+
|
|
30284
|
+
PayloadScanner::PayloadScanner(GlobalSortState &global_sort_state, idx_t block_idx)
|
|
30285
|
+
: sorted_data(*global_sort_state.sorted_blocks[0]->payload_data),
|
|
30286
|
+
read_state(global_sort_state.buffer_manager, global_sort_state),
|
|
30287
|
+
total_count(sorted_data.data_blocks[block_idx].count), global_sort_state(global_sort_state), total_scanned(0),
|
|
30288
|
+
flush(false) {
|
|
30289
|
+
read_state.SetIndices(block_idx, 0);
|
|
30255
30290
|
}
|
|
30256
30291
|
|
|
30257
30292
|
void PayloadScanner::Scan(DataChunk &chunk) {
|
|
30258
30293
|
auto count = MinValue((idx_t)STANDARD_VECTOR_SIZE, total_count - total_scanned);
|
|
30259
30294
|
if (count == 0) {
|
|
30260
|
-
D_ASSERT(read_state.block_idx == sorted_data.data_blocks.size());
|
|
30261
30295
|
chunk.SetCardinality(count);
|
|
30262
30296
|
return;
|
|
30263
30297
|
}
|
|
30264
30298
|
// Eagerly delete references to blocks that we've passed
|
|
30265
|
-
|
|
30266
|
-
|
|
30299
|
+
if (flush) {
|
|
30300
|
+
for (idx_t i = 0; i < read_state.block_idx; i++) {
|
|
30301
|
+
sorted_data.data_blocks[i].block = nullptr;
|
|
30302
|
+
}
|
|
30267
30303
|
}
|
|
30268
30304
|
const idx_t &row_width = sorted_data.layout.GetRowWidth();
|
|
30269
30305
|
// Set up a batch of pointers to scan data from
|
|
@@ -33142,6 +33178,7 @@ void DataChunk::Split(DataChunk &other, idx_t split_idx) {
|
|
|
33142
33178
|
vector_caches.pop_back();
|
|
33143
33179
|
}
|
|
33144
33180
|
other.SetCardinality(*this);
|
|
33181
|
+
other.SetCapacity(*this);
|
|
33145
33182
|
}
|
|
33146
33183
|
|
|
33147
33184
|
void DataChunk::Fuse(DataChunk &other) {
|
|
@@ -43887,6 +43924,32 @@ bool VectorOperations::HasNull(Vector &input, idx_t count) {
|
|
|
43887
43924
|
}
|
|
43888
43925
|
}
|
|
43889
43926
|
|
|
43927
|
+
idx_t VectorOperations::CountNotNull(Vector &input, const idx_t count) {
|
|
43928
|
+
idx_t valid = 0;
|
|
43929
|
+
|
|
43930
|
+
VectorData vdata;
|
|
43931
|
+
input.Orrify(count, vdata);
|
|
43932
|
+
if (vdata.validity.AllValid()) {
|
|
43933
|
+
return count;
|
|
43934
|
+
}
|
|
43935
|
+
switch (input.GetVectorType()) {
|
|
43936
|
+
case VectorType::FLAT_VECTOR:
|
|
43937
|
+
valid += vdata.validity.CountValid(count);
|
|
43938
|
+
break;
|
|
43939
|
+
case VectorType::CONSTANT_VECTOR:
|
|
43940
|
+
valid += vdata.validity.CountValid(1) * count;
|
|
43941
|
+
break;
|
|
43942
|
+
default:
|
|
43943
|
+
for (idx_t i = 0; i < count; ++i) {
|
|
43944
|
+
const auto row_idx = vdata.sel->get_index(i);
|
|
43945
|
+
valid += int(vdata.validity.RowIsValid(row_idx));
|
|
43946
|
+
}
|
|
43947
|
+
break;
|
|
43948
|
+
}
|
|
43949
|
+
|
|
43950
|
+
return valid;
|
|
43951
|
+
}
|
|
43952
|
+
|
|
43890
43953
|
} // namespace duckdb
|
|
43891
43954
|
//===--------------------------------------------------------------------===//
|
|
43892
43955
|
// numeric_inplace_operators.cpp
|
|
@@ -57374,6 +57437,1481 @@ void PhysicalHashJoin::GetData(ExecutionContext &context, DataChunk &chunk, Glob
|
|
|
57374
57437
|
sink.hash_table->ScanFullOuter(chunk, state.ht_scan_state);
|
|
57375
57438
|
}
|
|
57376
57439
|
|
|
57440
|
+
} // namespace duckdb
|
|
57441
|
+
//===----------------------------------------------------------------------===//
|
|
57442
|
+
// DuckDB
|
|
57443
|
+
//
|
|
57444
|
+
// duckdb/execution/operator/join/physical_piecewise_merge_join.hpp
|
|
57445
|
+
//
|
|
57446
|
+
//
|
|
57447
|
+
//===----------------------------------------------------------------------===//
|
|
57448
|
+
|
|
57449
|
+
|
|
57450
|
+
|
|
57451
|
+
|
|
57452
|
+
|
|
57453
|
+
|
|
57454
|
+
namespace duckdb {
|
|
57455
|
+
|
|
57456
|
+
class IEJoinSortedTable;
|
|
57457
|
+
|
|
57458
|
+
//! PhysicalIEJoin represents a two inequality range join between
|
|
57459
|
+
//! two tables
|
|
57460
|
+
class PhysicalIEJoin : public PhysicalComparisonJoin {
|
|
57461
|
+
public:
|
|
57462
|
+
PhysicalIEJoin(LogicalOperator &op, unique_ptr<PhysicalOperator> left, unique_ptr<PhysicalOperator> right,
|
|
57463
|
+
vector<JoinCondition> cond, JoinType join_type, idx_t estimated_cardinality);
|
|
57464
|
+
|
|
57465
|
+
vector<LogicalType> join_key_types;
|
|
57466
|
+
vector<vector<BoundOrderByNode>> lhs_orders;
|
|
57467
|
+
vector<vector<BoundOrderByNode>> rhs_orders;
|
|
57468
|
+
|
|
57469
|
+
public:
|
|
57470
|
+
// Operator Interface
|
|
57471
|
+
OperatorResultType Execute(ExecutionContext &context, DataChunk &input, DataChunk &chunk,
|
|
57472
|
+
OperatorState &state) const override;
|
|
57473
|
+
|
|
57474
|
+
public:
|
|
57475
|
+
// Source interface
|
|
57476
|
+
unique_ptr<LocalSourceState> GetLocalSourceState(ExecutionContext &context,
|
|
57477
|
+
GlobalSourceState &gstate) const override;
|
|
57478
|
+
unique_ptr<GlobalSourceState> GetGlobalSourceState(ClientContext &context) const override;
|
|
57479
|
+
void GetData(ExecutionContext &context, DataChunk &chunk, GlobalSourceState &gstate,
|
|
57480
|
+
LocalSourceState &lstate) const override;
|
|
57481
|
+
|
|
57482
|
+
bool IsSource() const override {
|
|
57483
|
+
return true;
|
|
57484
|
+
}
|
|
57485
|
+
bool ParallelSource() const override {
|
|
57486
|
+
return true;
|
|
57487
|
+
}
|
|
57488
|
+
|
|
57489
|
+
public:
|
|
57490
|
+
// Sink Interface
|
|
57491
|
+
unique_ptr<GlobalSinkState> GetGlobalSinkState(ClientContext &context) const override;
|
|
57492
|
+
unique_ptr<LocalSinkState> GetLocalSinkState(ExecutionContext &context) const override;
|
|
57493
|
+
SinkResultType Sink(ExecutionContext &context, GlobalSinkState &state, LocalSinkState &lstate,
|
|
57494
|
+
DataChunk &input) const override;
|
|
57495
|
+
void Combine(ExecutionContext &context, GlobalSinkState &gstate, LocalSinkState &lstate) const override;
|
|
57496
|
+
SinkFinalizeType Finalize(Pipeline &pipeline, Event &event, ClientContext &context,
|
|
57497
|
+
GlobalSinkState &gstate) const override;
|
|
57498
|
+
|
|
57499
|
+
//! Schedules tasks to merge sort the current child's data during a Finalize phase
|
|
57500
|
+
static void ScheduleMergeTasks(Pipeline &pipeline, Event &event, IEJoinSortedTable &table);
|
|
57501
|
+
|
|
57502
|
+
bool IsSink() const override {
|
|
57503
|
+
return true;
|
|
57504
|
+
}
|
|
57505
|
+
bool ParallelSink() const override {
|
|
57506
|
+
return true;
|
|
57507
|
+
}
|
|
57508
|
+
|
|
57509
|
+
private:
|
|
57510
|
+
// resolve joins that can potentially output N*M elements (INNER, LEFT, FULL)
|
|
57511
|
+
void ResolveComplexJoin(ExecutionContext &context, DataChunk &result, LocalSourceState &state) const;
|
|
57512
|
+
};
|
|
57513
|
+
|
|
57514
|
+
} // namespace duckdb
|
|
57515
|
+
|
|
57516
|
+
|
|
57517
|
+
|
|
57518
|
+
|
|
57519
|
+
|
|
57520
|
+
|
|
57521
|
+
|
|
57522
|
+
|
|
57523
|
+
|
|
57524
|
+
|
|
57525
|
+
|
|
57526
|
+
|
|
57527
|
+
|
|
57528
|
+
#include <thread>
|
|
57529
|
+
|
|
57530
|
+
namespace duckdb {
|
|
57531
|
+
|
|
57532
|
+
PhysicalIEJoin::PhysicalIEJoin(LogicalOperator &op, unique_ptr<PhysicalOperator> left,
|
|
57533
|
+
unique_ptr<PhysicalOperator> right, vector<JoinCondition> cond, JoinType join_type,
|
|
57534
|
+
idx_t estimated_cardinality)
|
|
57535
|
+
: PhysicalComparisonJoin(op, PhysicalOperatorType::IE_JOIN, move(cond), join_type, estimated_cardinality) {
|
|
57536
|
+
// Reorder the conditions so that ranges are at the front.
|
|
57537
|
+
// TODO: use stats to improve the choice?
|
|
57538
|
+
// TODO: Prefer fixed length types?
|
|
57539
|
+
auto conditions_p = std::move(conditions);
|
|
57540
|
+
conditions.resize(conditions_p.size());
|
|
57541
|
+
idx_t range_position = 0;
|
|
57542
|
+
idx_t other_position = conditions_p.size();
|
|
57543
|
+
for (idx_t i = 0; i < conditions_p.size(); ++i) {
|
|
57544
|
+
switch (conditions_p[i].comparison) {
|
|
57545
|
+
case ExpressionType::COMPARE_LESSTHAN:
|
|
57546
|
+
case ExpressionType::COMPARE_LESSTHANOREQUALTO:
|
|
57547
|
+
case ExpressionType::COMPARE_GREATERTHAN:
|
|
57548
|
+
case ExpressionType::COMPARE_GREATERTHANOREQUALTO:
|
|
57549
|
+
conditions[range_position++] = std::move(conditions_p[i]);
|
|
57550
|
+
break;
|
|
57551
|
+
case ExpressionType::COMPARE_NOTEQUAL:
|
|
57552
|
+
case ExpressionType::COMPARE_DISTINCT_FROM:
|
|
57553
|
+
// Allowed in multi-predicate joins, but can't be first/sort.
|
|
57554
|
+
conditions[--other_position] = std::move(conditions_p[i]);
|
|
57555
|
+
break;
|
|
57556
|
+
default:
|
|
57557
|
+
// COMPARE EQUAL not supported with iejoin join
|
|
57558
|
+
throw NotImplementedException("Unimplemented join type for IEJoin");
|
|
57559
|
+
}
|
|
57560
|
+
}
|
|
57561
|
+
|
|
57562
|
+
// IEJoin requires at least two comparisons.
|
|
57563
|
+
D_ASSERT(range_position > 1);
|
|
57564
|
+
|
|
57565
|
+
// 1. let L1 (resp. L2) be the array of column X (resp. Y)
|
|
57566
|
+
D_ASSERT(conditions.size() >= 2);
|
|
57567
|
+
lhs_orders.resize(2);
|
|
57568
|
+
rhs_orders.resize(2);
|
|
57569
|
+
for (idx_t i = 0; i < 2; ++i) {
|
|
57570
|
+
auto &cond = conditions[i];
|
|
57571
|
+
D_ASSERT(cond.left->return_type == cond.right->return_type);
|
|
57572
|
+
join_key_types.push_back(cond.left->return_type);
|
|
57573
|
+
|
|
57574
|
+
// Convert the conditions to sort orders
|
|
57575
|
+
auto left = cond.left->Copy();
|
|
57576
|
+
auto right = cond.right->Copy();
|
|
57577
|
+
auto sense = OrderType::INVALID;
|
|
57578
|
+
|
|
57579
|
+
// 2. if (op1 ∈ {>, ≥}) sort L1 in descending order
|
|
57580
|
+
// 3. else if (op1 ∈ {<, ≤}) sort L1 in ascending order
|
|
57581
|
+
// 4. if (op2 ∈ {>, ≥}) sort L2 in ascending order
|
|
57582
|
+
// 5. else if (op2 ∈ {<, ≤}) sort L2 in descending order
|
|
57583
|
+
switch (cond.comparison) {
|
|
57584
|
+
case ExpressionType::COMPARE_GREATERTHAN:
|
|
57585
|
+
case ExpressionType::COMPARE_GREATERTHANOREQUALTO:
|
|
57586
|
+
sense = i ? OrderType::ASCENDING : OrderType::DESCENDING;
|
|
57587
|
+
break;
|
|
57588
|
+
default:
|
|
57589
|
+
sense = i ? OrderType::DESCENDING : OrderType::ASCENDING;
|
|
57590
|
+
break;
|
|
57591
|
+
}
|
|
57592
|
+
lhs_orders[i].emplace_back(BoundOrderByNode(sense, OrderByNullType::NULLS_LAST, move(left)));
|
|
57593
|
+
rhs_orders[i].emplace_back(BoundOrderByNode(sense, OrderByNullType::NULLS_LAST, move(right)));
|
|
57594
|
+
}
|
|
57595
|
+
|
|
57596
|
+
for (idx_t i = 2; i < conditions.size(); ++i) {
|
|
57597
|
+
auto &cond = conditions[i];
|
|
57598
|
+
D_ASSERT(cond.left->return_type == cond.right->return_type);
|
|
57599
|
+
join_key_types.push_back(cond.left->return_type);
|
|
57600
|
+
}
|
|
57601
|
+
|
|
57602
|
+
children.push_back(move(left));
|
|
57603
|
+
children.push_back(move(right));
|
|
57604
|
+
}
|
|
57605
|
+
|
|
57606
|
+
//===--------------------------------------------------------------------===//
|
|
57607
|
+
// Sink
|
|
57608
|
+
//===--------------------------------------------------------------------===//
|
|
57609
|
+
class IEJoinLocalState : public LocalSinkState {
|
|
57610
|
+
public:
|
|
57611
|
+
explicit IEJoinLocalState(const vector<JoinCondition> &conditions, const idx_t child) : has_null(0), count(0) {
|
|
57612
|
+
// Initialize order clause expression executor and key DataChunk
|
|
57613
|
+
vector<LogicalType> types;
|
|
57614
|
+
for (const auto &cond : conditions) {
|
|
57615
|
+
comparisons.emplace_back(cond.comparison);
|
|
57616
|
+
|
|
57617
|
+
const auto &expr = child ? cond.right : cond.left;
|
|
57618
|
+
executor.AddExpression(*expr);
|
|
57619
|
+
|
|
57620
|
+
types.push_back(expr->return_type);
|
|
57621
|
+
}
|
|
57622
|
+
keys.Initialize(types);
|
|
57623
|
+
}
|
|
57624
|
+
|
|
57625
|
+
//! The local sort state
|
|
57626
|
+
LocalSortState local_sort_state;
|
|
57627
|
+
//! Local copy of the sorting expression executor
|
|
57628
|
+
ExpressionExecutor executor;
|
|
57629
|
+
//! Holds a vector of incoming sorting columns
|
|
57630
|
+
DataChunk keys;
|
|
57631
|
+
//! The comparison list (for null merging)
|
|
57632
|
+
vector<ExpressionType> comparisons;
|
|
57633
|
+
//! The number of NULL values
|
|
57634
|
+
idx_t has_null;
|
|
57635
|
+
//! The total number of rows
|
|
57636
|
+
idx_t count;
|
|
57637
|
+
|
|
57638
|
+
idx_t MergeKeyNulls();
|
|
57639
|
+
|
|
57640
|
+
void Sink(DataChunk &input, GlobalSortState &global_sort_state) {
|
|
57641
|
+
// Initialize local state (if necessary)
|
|
57642
|
+
if (!local_sort_state.initialized) {
|
|
57643
|
+
local_sort_state.Initialize(global_sort_state, global_sort_state.buffer_manager);
|
|
57644
|
+
}
|
|
57645
|
+
|
|
57646
|
+
// Obtain sorting columns
|
|
57647
|
+
keys.Reset();
|
|
57648
|
+
executor.Execute(input, keys);
|
|
57649
|
+
|
|
57650
|
+
// Count the NULLs so we can exclude them later
|
|
57651
|
+
has_null += MergeKeyNulls();
|
|
57652
|
+
count += keys.size();
|
|
57653
|
+
|
|
57654
|
+
// Sink the data into the local sort state
|
|
57655
|
+
D_ASSERT(keys.ColumnCount() > 1);
|
|
57656
|
+
// Only sort the primary key
|
|
57657
|
+
DataChunk join_head;
|
|
57658
|
+
join_head.data.emplace_back(Vector(keys.data[0]));
|
|
57659
|
+
join_head.SetCardinality(keys.size());
|
|
57660
|
+
|
|
57661
|
+
local_sort_state.SinkChunk(join_head, input);
|
|
57662
|
+
}
|
|
57663
|
+
|
|
57664
|
+
void Sort(GlobalSortState &gss) {
|
|
57665
|
+
local_sort_state.Sort(gss, true);
|
|
57666
|
+
}
|
|
57667
|
+
void Reset() {
|
|
57668
|
+
has_null = 0;
|
|
57669
|
+
count = 0;
|
|
57670
|
+
}
|
|
57671
|
+
};
|
|
57672
|
+
|
|
57673
|
+
idx_t IEJoinLocalState::MergeKeyNulls() {
|
|
57674
|
+
// Merge the validity masks of the comparison keys into the primary
|
|
57675
|
+
// Return the number of NULLs in the resulting chunk
|
|
57676
|
+
D_ASSERT(keys.ColumnCount() > 0);
|
|
57677
|
+
const auto count = keys.size();
|
|
57678
|
+
|
|
57679
|
+
size_t all_constant = 0;
|
|
57680
|
+
for (auto &v : keys.data) {
|
|
57681
|
+
all_constant += int(v.GetVectorType() == VectorType::CONSTANT_VECTOR);
|
|
57682
|
+
}
|
|
57683
|
+
|
|
57684
|
+
auto &primary = keys.data[0];
|
|
57685
|
+
if (all_constant == keys.data.size()) {
|
|
57686
|
+
// Either all NULL or no NULLs
|
|
57687
|
+
for (auto &v : keys.data) {
|
|
57688
|
+
if (ConstantVector::IsNull(v)) {
|
|
57689
|
+
ConstantVector::SetNull(primary, true);
|
|
57690
|
+
return count;
|
|
57691
|
+
}
|
|
57692
|
+
}
|
|
57693
|
+
return 0;
|
|
57694
|
+
} else if (keys.ColumnCount() > 1) {
|
|
57695
|
+
// Normalify the primary, as it will need to merge arbitrary validity masks
|
|
57696
|
+
primary.Normalify(count);
|
|
57697
|
+
auto &pvalidity = FlatVector::Validity(primary);
|
|
57698
|
+
D_ASSERT(keys.ColumnCount() == comparisons.size());
|
|
57699
|
+
for (size_t c = 1; c < keys.data.size(); ++c) {
|
|
57700
|
+
// Skip comparisons that accept NULLs
|
|
57701
|
+
if (comparisons[c] == ExpressionType::COMPARE_DISTINCT_FROM) {
|
|
57702
|
+
continue;
|
|
57703
|
+
}
|
|
57704
|
+
// Orrify the rest, as the sort code will do this anyway.
|
|
57705
|
+
auto &v = keys.data[c];
|
|
57706
|
+
VectorData vdata;
|
|
57707
|
+
v.Orrify(count, vdata);
|
|
57708
|
+
auto &vvalidity = vdata.validity;
|
|
57709
|
+
if (vvalidity.AllValid()) {
|
|
57710
|
+
continue;
|
|
57711
|
+
}
|
|
57712
|
+
pvalidity.EnsureWritable();
|
|
57713
|
+
auto pmask = pvalidity.GetData();
|
|
57714
|
+
if (v.GetVectorType() == VectorType::FLAT_VECTOR) {
|
|
57715
|
+
// Merge entire entries
|
|
57716
|
+
const auto entry_count = pvalidity.EntryCount(count);
|
|
57717
|
+
for (idx_t entry_idx = 0; entry_idx < entry_count; ++entry_idx) {
|
|
57718
|
+
pmask[entry_idx] &= vvalidity.GetValidityEntry(entry_idx);
|
|
57719
|
+
}
|
|
57720
|
+
}
|
|
57721
|
+
}
|
|
57722
|
+
return count - pvalidity.CountValid(count);
|
|
57723
|
+
} else {
|
|
57724
|
+
return count - VectorOperations::CountNotNull(primary, count);
|
|
57725
|
+
}
|
|
57726
|
+
}
|
|
57727
|
+
|
|
57728
|
+
class IEJoinSortedTable {
|
|
57729
|
+
public:
|
|
57730
|
+
IEJoinSortedTable(ClientContext &context, const vector<BoundOrderByNode> &orders, RowLayout &payload_layout)
|
|
57731
|
+
: global_sort_state(BufferManager::GetBufferManager(context), orders, payload_layout), has_null(0), count(0),
|
|
57732
|
+
memory_per_thread(0) {
|
|
57733
|
+
D_ASSERT(orders.size() == 1);
|
|
57734
|
+
|
|
57735
|
+
// Set external (can be force with the PRAGMA)
|
|
57736
|
+
auto &config = ClientConfig::GetConfig(context);
|
|
57737
|
+
global_sort_state.external = config.force_external;
|
|
57738
|
+
// Memory usage per thread should scale with max mem / num threads
|
|
57739
|
+
// We take 1/4th of this, to be conservative
|
|
57740
|
+
idx_t max_memory = global_sort_state.buffer_manager.GetMaxMemory();
|
|
57741
|
+
idx_t num_threads = TaskScheduler::GetScheduler(context).NumberOfThreads();
|
|
57742
|
+
memory_per_thread = (max_memory / num_threads) / 4;
|
|
57743
|
+
}
|
|
57744
|
+
|
|
57745
|
+
inline idx_t Count() const {
|
|
57746
|
+
return count;
|
|
57747
|
+
}
|
|
57748
|
+
|
|
57749
|
+
inline idx_t BlockCount() const {
|
|
57750
|
+
if (global_sort_state.sorted_blocks.empty()) {
|
|
57751
|
+
return 0;
|
|
57752
|
+
}
|
|
57753
|
+
D_ASSERT(global_sort_state.sorted_blocks.size() == 1);
|
|
57754
|
+
return global_sort_state.sorted_blocks[0]->radix_sorting_data.size();
|
|
57755
|
+
}
|
|
57756
|
+
|
|
57757
|
+
inline idx_t BlockSize(idx_t i) const {
|
|
57758
|
+
return global_sort_state.sorted_blocks[0]->radix_sorting_data[i].count;
|
|
57759
|
+
}
|
|
57760
|
+
|
|
57761
|
+
inline void Combine(IEJoinLocalState &lstate) {
|
|
57762
|
+
global_sort_state.AddLocalState(lstate.local_sort_state);
|
|
57763
|
+
has_null += lstate.has_null;
|
|
57764
|
+
count += lstate.count;
|
|
57765
|
+
}
|
|
57766
|
+
|
|
57767
|
+
inline void IntializeMatches() {
|
|
57768
|
+
found_match = unique_ptr<bool[]>(new bool[Count()]);
|
|
57769
|
+
memset(found_match.get(), 0, sizeof(bool) * Count());
|
|
57770
|
+
}
|
|
57771
|
+
|
|
57772
|
+
void Print() {
|
|
57773
|
+
PayloadScanner scanner(global_sort_state, false);
|
|
57774
|
+
DataChunk chunk;
|
|
57775
|
+
chunk.Initialize(scanner.GetPayloadTypes());
|
|
57776
|
+
for (;;) {
|
|
57777
|
+
scanner.Scan(chunk);
|
|
57778
|
+
const auto count = chunk.size();
|
|
57779
|
+
if (!count) {
|
|
57780
|
+
break;
|
|
57781
|
+
}
|
|
57782
|
+
chunk.Print();
|
|
57783
|
+
}
|
|
57784
|
+
}
|
|
57785
|
+
|
|
57786
|
+
GlobalSortState global_sort_state;
|
|
57787
|
+
//! Whether or not the RHS has NULL values
|
|
57788
|
+
atomic<idx_t> has_null;
|
|
57789
|
+
//! The total number of rows in the RHS
|
|
57790
|
+
atomic<idx_t> count;
|
|
57791
|
+
//! A bool indicating for each tuple in the RHS if they found a match (only used in FULL OUTER JOIN)
|
|
57792
|
+
unique_ptr<bool[]> found_match;
|
|
57793
|
+
//! Memory usage per thread
|
|
57794
|
+
idx_t memory_per_thread;
|
|
57795
|
+
};
|
|
57796
|
+
|
|
57797
|
+
class IEJoinGlobalState : public GlobalSinkState {
|
|
57798
|
+
public:
|
|
57799
|
+
IEJoinGlobalState(ClientContext &context, const PhysicalIEJoin &op) : child(0) {
|
|
57800
|
+
tables.resize(2);
|
|
57801
|
+
RowLayout lhs_layout;
|
|
57802
|
+
lhs_layout.Initialize(op.children[0]->types);
|
|
57803
|
+
vector<BoundOrderByNode> lhs_order;
|
|
57804
|
+
lhs_order.emplace_back(op.lhs_orders[0][0].Copy());
|
|
57805
|
+
tables[0] = make_unique<IEJoinSortedTable>(context, lhs_order, lhs_layout);
|
|
57806
|
+
|
|
57807
|
+
RowLayout rhs_layout;
|
|
57808
|
+
rhs_layout.Initialize(op.children[1]->types);
|
|
57809
|
+
vector<BoundOrderByNode> rhs_order;
|
|
57810
|
+
rhs_order.emplace_back(op.rhs_orders[0][0].Copy());
|
|
57811
|
+
tables[1] = make_unique<IEJoinSortedTable>(context, rhs_order, rhs_layout);
|
|
57812
|
+
}
|
|
57813
|
+
|
|
57814
|
+
IEJoinGlobalState(IEJoinGlobalState &prev)
|
|
57815
|
+
: GlobalSinkState(prev), tables(move(prev.tables)), child(prev.child + 1) {
|
|
57816
|
+
}
|
|
57817
|
+
|
|
57818
|
+
void Sink(DataChunk &input, IEJoinLocalState &lstate) {
|
|
57819
|
+
auto &table = *tables[child];
|
|
57820
|
+
auto &global_sort_state = table.global_sort_state;
|
|
57821
|
+
auto &local_sort_state = lstate.local_sort_state;
|
|
57822
|
+
|
|
57823
|
+
// Sink the data into the local sort state
|
|
57824
|
+
lstate.Sink(input, global_sort_state);
|
|
57825
|
+
|
|
57826
|
+
// When sorting data reaches a certain size, we sort it
|
|
57827
|
+
if (local_sort_state.SizeInBytes() >= table.memory_per_thread) {
|
|
57828
|
+
local_sort_state.Sort(global_sort_state, true);
|
|
57829
|
+
}
|
|
57830
|
+
}
|
|
57831
|
+
|
|
57832
|
+
vector<unique_ptr<IEJoinSortedTable>> tables;
|
|
57833
|
+
size_t child;
|
|
57834
|
+
};
|
|
57835
|
+
|
|
57836
|
+
unique_ptr<GlobalSinkState> PhysicalIEJoin::GetGlobalSinkState(ClientContext &context) const {
|
|
57837
|
+
D_ASSERT(!sink_state);
|
|
57838
|
+
return make_unique<IEJoinGlobalState>(context, *this);
|
|
57839
|
+
}
|
|
57840
|
+
|
|
57841
|
+
unique_ptr<LocalSinkState> PhysicalIEJoin::GetLocalSinkState(ExecutionContext &context) const {
|
|
57842
|
+
idx_t sink_child = 0;
|
|
57843
|
+
if (sink_state) {
|
|
57844
|
+
const auto &ie_sink = (IEJoinGlobalState &)*sink_state;
|
|
57845
|
+
sink_child = ie_sink.child;
|
|
57846
|
+
}
|
|
57847
|
+
return make_unique<IEJoinLocalState>(conditions, sink_child);
|
|
57848
|
+
}
|
|
57849
|
+
|
|
57850
|
+
SinkResultType PhysicalIEJoin::Sink(ExecutionContext &context, GlobalSinkState &gstate_p, LocalSinkState &lstate_p,
|
|
57851
|
+
DataChunk &input) const {
|
|
57852
|
+
auto &gstate = (IEJoinGlobalState &)gstate_p;
|
|
57853
|
+
auto &lstate = (IEJoinLocalState &)lstate_p;
|
|
57854
|
+
|
|
57855
|
+
gstate.Sink(input, lstate);
|
|
57856
|
+
|
|
57857
|
+
return SinkResultType::NEED_MORE_INPUT;
|
|
57858
|
+
}
|
|
57859
|
+
|
|
57860
|
+
void PhysicalIEJoin::Combine(ExecutionContext &context, GlobalSinkState &gstate_p, LocalSinkState &lstate_p) const {
|
|
57861
|
+
auto &gstate = (IEJoinGlobalState &)gstate_p;
|
|
57862
|
+
auto &lstate = (IEJoinLocalState &)lstate_p;
|
|
57863
|
+
gstate.tables[gstate.child]->Combine(lstate);
|
|
57864
|
+
auto &client_profiler = QueryProfiler::Get(context.client);
|
|
57865
|
+
|
|
57866
|
+
context.thread.profiler.Flush(this, &lstate.executor, gstate.child ? "rhs_executor" : "lhs_executor", 1);
|
|
57867
|
+
client_profiler.Flush(context.thread.profiler);
|
|
57868
|
+
}
|
|
57869
|
+
|
|
57870
|
+
//===--------------------------------------------------------------------===//
|
|
57871
|
+
// Finalize
|
|
57872
|
+
//===--------------------------------------------------------------------===//
|
|
57873
|
+
class IEJoinFinalizeTask : public ExecutorTask {
|
|
57874
|
+
public:
|
|
57875
|
+
IEJoinFinalizeTask(shared_ptr<Event> event_p, ClientContext &context, IEJoinSortedTable &table)
|
|
57876
|
+
: ExecutorTask(context), event(move(event_p)), context(context), table(table) {
|
|
57877
|
+
}
|
|
57878
|
+
|
|
57879
|
+
TaskExecutionResult ExecuteTask(TaskExecutionMode mode) override {
|
|
57880
|
+
// Initialize iejoin sorted and iterate until done
|
|
57881
|
+
auto &global_sort_state = table.global_sort_state;
|
|
57882
|
+
MergeSorter merge_sorter(global_sort_state, BufferManager::GetBufferManager(context));
|
|
57883
|
+
merge_sorter.PerformInMergeRound();
|
|
57884
|
+
event->FinishTask();
|
|
57885
|
+
|
|
57886
|
+
return TaskExecutionResult::TASK_FINISHED;
|
|
57887
|
+
}
|
|
57888
|
+
|
|
57889
|
+
private:
|
|
57890
|
+
shared_ptr<Event> event;
|
|
57891
|
+
ClientContext &context;
|
|
57892
|
+
IEJoinSortedTable &table;
|
|
57893
|
+
};
|
|
57894
|
+
|
|
57895
|
+
class IEJoinFinalizeEvent : public Event {
|
|
57896
|
+
public:
|
|
57897
|
+
IEJoinFinalizeEvent(IEJoinSortedTable &table_p, Pipeline &pipeline_p)
|
|
57898
|
+
: Event(pipeline_p.executor), table(table_p), pipeline(pipeline_p) {
|
|
57899
|
+
}
|
|
57900
|
+
|
|
57901
|
+
IEJoinSortedTable &table;
|
|
57902
|
+
Pipeline &pipeline;
|
|
57903
|
+
|
|
57904
|
+
public:
|
|
57905
|
+
void Schedule() override {
|
|
57906
|
+
auto &context = pipeline.GetClientContext();
|
|
57907
|
+
|
|
57908
|
+
// Schedule tasks equal to the number of threads, which will each iejoin multiple partitions
|
|
57909
|
+
auto &ts = TaskScheduler::GetScheduler(context);
|
|
57910
|
+
idx_t num_threads = ts.NumberOfThreads();
|
|
57911
|
+
|
|
57912
|
+
vector<unique_ptr<Task>> iejoin_tasks;
|
|
57913
|
+
for (idx_t tnum = 0; tnum < num_threads; tnum++) {
|
|
57914
|
+
iejoin_tasks.push_back(make_unique<IEJoinFinalizeTask>(shared_from_this(), context, table));
|
|
57915
|
+
}
|
|
57916
|
+
SetTasks(move(iejoin_tasks));
|
|
57917
|
+
}
|
|
57918
|
+
|
|
57919
|
+
void FinishEvent() override {
|
|
57920
|
+
auto &global_sort_state = table.global_sort_state;
|
|
57921
|
+
|
|
57922
|
+
global_sort_state.CompleteMergeRound(true);
|
|
57923
|
+
if (global_sort_state.sorted_blocks.size() > 1) {
|
|
57924
|
+
// Multiple blocks remaining: Schedule the next round
|
|
57925
|
+
PhysicalIEJoin::ScheduleMergeTasks(pipeline, *this, table);
|
|
57926
|
+
}
|
|
57927
|
+
}
|
|
57928
|
+
};
|
|
57929
|
+
|
|
57930
|
+
void PhysicalIEJoin::ScheduleMergeTasks(Pipeline &pipeline, Event &event, IEJoinSortedTable &table) {
|
|
57931
|
+
// Initialize global sort state for a round of merging
|
|
57932
|
+
table.global_sort_state.InitializeMergeRound();
|
|
57933
|
+
auto new_event = make_shared<IEJoinFinalizeEvent>(table, pipeline);
|
|
57934
|
+
event.InsertEvent(move(new_event));
|
|
57935
|
+
}
|
|
57936
|
+
|
|
57937
|
+
SinkFinalizeType PhysicalIEJoin::Finalize(Pipeline &pipeline, Event &event, ClientContext &context,
|
|
57938
|
+
GlobalSinkState &gstate_p) const {
|
|
57939
|
+
auto &gstate = (IEJoinGlobalState &)gstate_p;
|
|
57940
|
+
auto &table = *gstate.tables[gstate.child];
|
|
57941
|
+
auto &global_sort_state = table.global_sort_state;
|
|
57942
|
+
|
|
57943
|
+
if ((gstate.child == 1 && IsRightOuterJoin(join_type)) || (gstate.child == 0 && IsLeftOuterJoin(join_type))) {
|
|
57944
|
+
// for FULL/LEFT/RIGHT OUTER JOIN, initialize found_match to false for every tuple
|
|
57945
|
+
table.IntializeMatches();
|
|
57946
|
+
}
|
|
57947
|
+
if (gstate.child == 1 && global_sort_state.sorted_blocks.empty() && EmptyResultIfRHSIsEmpty()) {
|
|
57948
|
+
// Empty input!
|
|
57949
|
+
return SinkFinalizeType::NO_OUTPUT_POSSIBLE;
|
|
57950
|
+
}
|
|
57951
|
+
|
|
57952
|
+
// Prepare for child sort phase
|
|
57953
|
+
global_sort_state.PrepareMergePhase();
|
|
57954
|
+
|
|
57955
|
+
// Start the iejoin phase or finish if a iejoin is not necessary
|
|
57956
|
+
if (global_sort_state.sorted_blocks.size() > 1) {
|
|
57957
|
+
PhysicalIEJoin::ScheduleMergeTasks(pipeline, event, table);
|
|
57958
|
+
}
|
|
57959
|
+
|
|
57960
|
+
++gstate.child;
|
|
57961
|
+
|
|
57962
|
+
return SinkFinalizeType::READY;
|
|
57963
|
+
}
|
|
57964
|
+
|
|
57965
|
+
//===--------------------------------------------------------------------===//
|
|
57966
|
+
// Operator
|
|
57967
|
+
//===--------------------------------------------------------------------===//
|
|
57968
|
+
struct SBIterator {
|
|
57969
|
+
static int ComparisonValue(ExpressionType comparison) {
|
|
57970
|
+
switch (comparison) {
|
|
57971
|
+
case ExpressionType::COMPARE_LESSTHAN:
|
|
57972
|
+
case ExpressionType::COMPARE_GREATERTHAN:
|
|
57973
|
+
return -1;
|
|
57974
|
+
case ExpressionType::COMPARE_LESSTHANOREQUALTO:
|
|
57975
|
+
case ExpressionType::COMPARE_GREATERTHANOREQUALTO:
|
|
57976
|
+
return 0;
|
|
57977
|
+
default:
|
|
57978
|
+
throw InternalException("Unimplemented comparison type for IEJoin!");
|
|
57979
|
+
}
|
|
57980
|
+
}
|
|
57981
|
+
|
|
57982
|
+
explicit SBIterator(GlobalSortState &gss, ExpressionType comparison, idx_t entry_idx_p = 0)
|
|
57983
|
+
: sort_layout(gss.sort_layout), block_count(gss.sorted_blocks[0]->radix_sorting_data.size()),
|
|
57984
|
+
block_capacity(gss.block_capacity), cmp_size(sort_layout.comparison_size), entry_size(sort_layout.entry_size),
|
|
57985
|
+
all_constant(sort_layout.all_constant), external(gss.external), cmp(ComparisonValue(comparison)),
|
|
57986
|
+
scan(gss.buffer_manager, gss), block_ptr(nullptr), entry_ptr(nullptr) {
|
|
57987
|
+
|
|
57988
|
+
scan.sb = gss.sorted_blocks[0].get();
|
|
57989
|
+
scan.block_idx = block_count;
|
|
57990
|
+
SetIndex(entry_idx_p);
|
|
57991
|
+
}
|
|
57992
|
+
|
|
57993
|
+
inline idx_t GetIndex() const {
|
|
57994
|
+
return entry_idx;
|
|
57995
|
+
}
|
|
57996
|
+
|
|
57997
|
+
inline void SetIndex(idx_t entry_idx_p) {
|
|
57998
|
+
const auto new_block_idx = entry_idx_p / block_capacity;
|
|
57999
|
+
if (new_block_idx != scan.block_idx) {
|
|
58000
|
+
scan.SetIndices(new_block_idx, 0);
|
|
58001
|
+
if (new_block_idx < block_count) {
|
|
58002
|
+
scan.PinRadix(scan.block_idx);
|
|
58003
|
+
block_ptr = scan.RadixPtr();
|
|
58004
|
+
if (!all_constant) {
|
|
58005
|
+
scan.PinData(*scan.sb->blob_sorting_data);
|
|
58006
|
+
}
|
|
58007
|
+
}
|
|
58008
|
+
}
|
|
58009
|
+
|
|
58010
|
+
scan.entry_idx = entry_idx_p % block_capacity;
|
|
58011
|
+
entry_ptr = block_ptr + scan.entry_idx * entry_size;
|
|
58012
|
+
entry_idx = entry_idx_p;
|
|
58013
|
+
}
|
|
58014
|
+
|
|
58015
|
+
inline SBIterator &operator++() {
|
|
58016
|
+
if (++scan.entry_idx < block_capacity) {
|
|
58017
|
+
entry_ptr += entry_size;
|
|
58018
|
+
++entry_idx;
|
|
58019
|
+
} else {
|
|
58020
|
+
SetIndex(entry_idx + 1);
|
|
58021
|
+
}
|
|
58022
|
+
|
|
58023
|
+
return *this;
|
|
58024
|
+
}
|
|
58025
|
+
|
|
58026
|
+
inline SBIterator &operator--() {
|
|
58027
|
+
if (scan.entry_idx) {
|
|
58028
|
+
--scan.entry_idx;
|
|
58029
|
+
--entry_idx;
|
|
58030
|
+
entry_ptr -= entry_size;
|
|
58031
|
+
} else {
|
|
58032
|
+
SetIndex(entry_idx - 1);
|
|
58033
|
+
}
|
|
58034
|
+
|
|
58035
|
+
return *this;
|
|
58036
|
+
}
|
|
58037
|
+
|
|
58038
|
+
inline bool Compare(const SBIterator &other) const {
|
|
58039
|
+
int comp_res;
|
|
58040
|
+
if (all_constant) {
|
|
58041
|
+
comp_res = FastMemcmp(entry_ptr, other.entry_ptr, cmp_size);
|
|
58042
|
+
} else {
|
|
58043
|
+
comp_res = Comparators::CompareTuple(scan, other.scan, entry_ptr, other.entry_ptr, sort_layout, external);
|
|
58044
|
+
}
|
|
58045
|
+
|
|
58046
|
+
return comp_res <= cmp;
|
|
58047
|
+
}
|
|
58048
|
+
|
|
58049
|
+
// Fixed comparison parameters
|
|
58050
|
+
const SortLayout &sort_layout;
|
|
58051
|
+
const idx_t block_count;
|
|
58052
|
+
const idx_t block_capacity;
|
|
58053
|
+
const size_t cmp_size;
|
|
58054
|
+
const size_t entry_size;
|
|
58055
|
+
const bool all_constant;
|
|
58056
|
+
const bool external;
|
|
58057
|
+
const int cmp;
|
|
58058
|
+
|
|
58059
|
+
// Iteration state
|
|
58060
|
+
SBScanState scan;
|
|
58061
|
+
idx_t entry_idx;
|
|
58062
|
+
data_ptr_t block_ptr;
|
|
58063
|
+
data_ptr_t entry_ptr;
|
|
58064
|
+
};
|
|
58065
|
+
|
|
58066
|
+
struct IEJoinUnion {
|
|
58067
|
+
using SortedTable = IEJoinSortedTable;
|
|
58068
|
+
|
|
58069
|
+
static idx_t AppendKey(SortedTable &table, ExpressionExecutor &executor, SortedTable &marked, int64_t increment,
|
|
58070
|
+
int64_t base, const idx_t block_idx);
|
|
58071
|
+
|
|
58072
|
+
static void Sort(SortedTable &table) {
|
|
58073
|
+
auto &global_sort_state = table.global_sort_state;
|
|
58074
|
+
global_sort_state.PrepareMergePhase();
|
|
58075
|
+
while (global_sort_state.sorted_blocks.size() > 1) {
|
|
58076
|
+
global_sort_state.InitializeMergeRound();
|
|
58077
|
+
MergeSorter merge_sorter(global_sort_state, global_sort_state.buffer_manager);
|
|
58078
|
+
merge_sorter.PerformInMergeRound();
|
|
58079
|
+
global_sort_state.CompleteMergeRound(true);
|
|
58080
|
+
}
|
|
58081
|
+
}
|
|
58082
|
+
|
|
58083
|
+
template <typename T>
|
|
58084
|
+
static vector<T> ExtractColumn(SortedTable &table, idx_t col_idx) {
|
|
58085
|
+
vector<T> result;
|
|
58086
|
+
result.reserve(table.count);
|
|
58087
|
+
|
|
58088
|
+
auto &gstate = table.global_sort_state;
|
|
58089
|
+
auto &blocks = *gstate.sorted_blocks[0]->payload_data;
|
|
58090
|
+
PayloadScanner scanner(blocks, gstate, false);
|
|
58091
|
+
|
|
58092
|
+
DataChunk payload;
|
|
58093
|
+
payload.Initialize(gstate.payload_layout.GetTypes());
|
|
58094
|
+
for (;;) {
|
|
58095
|
+
scanner.Scan(payload);
|
|
58096
|
+
const auto count = payload.size();
|
|
58097
|
+
if (!count) {
|
|
58098
|
+
break;
|
|
58099
|
+
}
|
|
58100
|
+
|
|
58101
|
+
const auto data_ptr = FlatVector::GetData<T>(payload.data[col_idx]);
|
|
58102
|
+
result.insert(result.end(), data_ptr, data_ptr + count);
|
|
58103
|
+
}
|
|
58104
|
+
|
|
58105
|
+
return result;
|
|
58106
|
+
}
|
|
58107
|
+
|
|
58108
|
+
IEJoinUnion(ClientContext &context, const PhysicalIEJoin &op, SortedTable &t1, const idx_t b1, SortedTable &t2,
|
|
58109
|
+
const idx_t b2);
|
|
58110
|
+
|
|
58111
|
+
bool NextRow();
|
|
58112
|
+
|
|
58113
|
+
//! Inverted loop
|
|
58114
|
+
idx_t JoinComplexBlocks(SelectionVector &lsel, SelectionVector &rsel);
|
|
58115
|
+
|
|
58116
|
+
//! L1
|
|
58117
|
+
unique_ptr<SortedTable> l1;
|
|
58118
|
+
//! L2
|
|
58119
|
+
unique_ptr<SortedTable> l2;
|
|
58120
|
+
|
|
58121
|
+
//! Li
|
|
58122
|
+
vector<int64_t> li;
|
|
58123
|
+
//! P
|
|
58124
|
+
vector<idx_t> p;
|
|
58125
|
+
|
|
58126
|
+
//! B
|
|
58127
|
+
vector<validity_t> bit_array;
|
|
58128
|
+
ValidityMask bit_mask;
|
|
58129
|
+
|
|
58130
|
+
//! Bloom Filter
|
|
58131
|
+
static constexpr idx_t BLOOM_CHUNK_BITS = 1024;
|
|
58132
|
+
idx_t bloom_count;
|
|
58133
|
+
vector<validity_t> bloom_array;
|
|
58134
|
+
ValidityMask bloom_filter;
|
|
58135
|
+
|
|
58136
|
+
//! Iteration state
|
|
58137
|
+
idx_t n;
|
|
58138
|
+
idx_t i;
|
|
58139
|
+
unique_ptr<SBIterator> op1;
|
|
58140
|
+
unique_ptr<SBIterator> off1;
|
|
58141
|
+
unique_ptr<SBIterator> op2;
|
|
58142
|
+
unique_ptr<SBIterator> off2;
|
|
58143
|
+
int64_t lrid;
|
|
58144
|
+
};
|
|
58145
|
+
|
|
58146
|
+
idx_t IEJoinUnion::AppendKey(SortedTable &table, ExpressionExecutor &executor, SortedTable &marked, int64_t increment,
|
|
58147
|
+
int64_t base, const idx_t block_idx) {
|
|
58148
|
+
LocalSortState local_sort_state;
|
|
58149
|
+
local_sort_state.Initialize(marked.global_sort_state, marked.global_sort_state.buffer_manager);
|
|
58150
|
+
|
|
58151
|
+
// Reading
|
|
58152
|
+
const auto valid = table.count - table.has_null;
|
|
58153
|
+
auto &gstate = table.global_sort_state;
|
|
58154
|
+
PayloadScanner scanner(gstate, block_idx);
|
|
58155
|
+
auto table_idx = block_idx * gstate.block_capacity;
|
|
58156
|
+
|
|
58157
|
+
DataChunk scanned;
|
|
58158
|
+
scanned.Initialize(scanner.GetPayloadTypes());
|
|
58159
|
+
|
|
58160
|
+
// Writing
|
|
58161
|
+
auto types = local_sort_state.sort_layout->logical_types;
|
|
58162
|
+
const idx_t payload_idx = types.size();
|
|
58163
|
+
|
|
58164
|
+
const auto &payload_types = local_sort_state.payload_layout->GetTypes();
|
|
58165
|
+
types.insert(types.end(), payload_types.begin(), payload_types.end());
|
|
58166
|
+
const idx_t rid_idx = types.size() - 1;
|
|
58167
|
+
|
|
58168
|
+
DataChunk keys;
|
|
58169
|
+
DataChunk payload;
|
|
58170
|
+
keys.Initialize(types);
|
|
58171
|
+
|
|
58172
|
+
idx_t inserted = 0;
|
|
58173
|
+
for (auto rid = base; table_idx < valid;) {
|
|
58174
|
+
scanner.Scan(scanned);
|
|
58175
|
+
|
|
58176
|
+
// NULLs are at the end, so stop when we reach them
|
|
58177
|
+
auto scan_count = scanned.size();
|
|
58178
|
+
if (table_idx + scan_count > valid) {
|
|
58179
|
+
scan_count = valid - table_idx;
|
|
58180
|
+
scanned.SetCardinality(scan_count);
|
|
58181
|
+
}
|
|
58182
|
+
if (scan_count == 0) {
|
|
58183
|
+
break;
|
|
58184
|
+
}
|
|
58185
|
+
table_idx += scan_count;
|
|
58186
|
+
|
|
58187
|
+
// Compute the input columns from the payload
|
|
58188
|
+
keys.Reset();
|
|
58189
|
+
keys.Split(payload, rid_idx);
|
|
58190
|
+
executor.Execute(scanned, keys);
|
|
58191
|
+
|
|
58192
|
+
// Mark the rid column
|
|
58193
|
+
payload.data[0].Sequence(rid, increment);
|
|
58194
|
+
payload.SetCardinality(scan_count);
|
|
58195
|
+
keys.Fuse(payload);
|
|
58196
|
+
rid += increment * scan_count;
|
|
58197
|
+
|
|
58198
|
+
// Sort on the sort columns (which will no longer be needed)
|
|
58199
|
+
keys.Split(payload, payload_idx);
|
|
58200
|
+
local_sort_state.SinkChunk(keys, payload);
|
|
58201
|
+
inserted += scan_count;
|
|
58202
|
+
keys.Fuse(payload);
|
|
58203
|
+
|
|
58204
|
+
// Flush when we have enough data
|
|
58205
|
+
if (local_sort_state.SizeInBytes() >= marked.memory_per_thread) {
|
|
58206
|
+
local_sort_state.Sort(marked.global_sort_state, true);
|
|
58207
|
+
}
|
|
58208
|
+
}
|
|
58209
|
+
marked.global_sort_state.AddLocalState(local_sort_state);
|
|
58210
|
+
marked.count += inserted;
|
|
58211
|
+
|
|
58212
|
+
return inserted;
|
|
58213
|
+
}
|
|
58214
|
+
|
|
58215
|
+
IEJoinUnion::IEJoinUnion(ClientContext &context, const PhysicalIEJoin &op, SortedTable &t1, const idx_t b1,
|
|
58216
|
+
SortedTable &t2, const idx_t b2)
|
|
58217
|
+
: n(0), i(0) {
|
|
58218
|
+
// input : query Q with 2 join predicates t1.X op1 t2.X' and t1.Y op2 t2.Y', tables T, T' of sizes m and n resp.
|
|
58219
|
+
// output: a list of tuple pairs (ti , tj)
|
|
58220
|
+
// Note that T/T' are already sorted on X/X' and contain the payload data
|
|
58221
|
+
// We only join the two block numbers and use the sizes of the blocks as the counts
|
|
58222
|
+
|
|
58223
|
+
// 0. Filter out tables with no overlap
|
|
58224
|
+
if (!t1.BlockSize(b1) || !t2.BlockSize(b2)) {
|
|
58225
|
+
return;
|
|
58226
|
+
}
|
|
58227
|
+
|
|
58228
|
+
const auto &cmp1 = op.conditions[0].comparison;
|
|
58229
|
+
SBIterator bounds1(t1.global_sort_state, cmp1);
|
|
58230
|
+
SBIterator bounds2(t2.global_sort_state, cmp1);
|
|
58231
|
+
|
|
58232
|
+
// t1.X[0] op1 t2.X'[-1]
|
|
58233
|
+
bounds1.SetIndex(bounds1.block_capacity * b1);
|
|
58234
|
+
bounds2.SetIndex(bounds2.block_capacity * b2 + t2.BlockSize(b2) - 1);
|
|
58235
|
+
if (!bounds1.Compare(bounds2)) {
|
|
58236
|
+
return;
|
|
58237
|
+
}
|
|
58238
|
+
|
|
58239
|
+
// 1. let L1 (resp. L2) be the array of column X (resp. Y )
|
|
58240
|
+
const auto &order1 = op.lhs_orders[0][0];
|
|
58241
|
+
const auto &order2 = op.lhs_orders[1][0];
|
|
58242
|
+
|
|
58243
|
+
// 2. if (op1 ∈ {>, ≥}) sort L1 in descending order
|
|
58244
|
+
// 3. else if (op1 ∈ {<, ≤}) sort L1 in ascending order
|
|
58245
|
+
|
|
58246
|
+
// For the union algorithm, we make a unified table with the keys and the rids as the payload:
|
|
58247
|
+
// X/X', Y/Y', R/R'/Li
|
|
58248
|
+
// The first position is the sort key.
|
|
58249
|
+
vector<LogicalType> types;
|
|
58250
|
+
types.emplace_back(order2.expression->return_type);
|
|
58251
|
+
types.emplace_back(LogicalType::BIGINT);
|
|
58252
|
+
RowLayout payload_layout;
|
|
58253
|
+
payload_layout.Initialize(types);
|
|
58254
|
+
|
|
58255
|
+
// Sort on the first expression
|
|
58256
|
+
auto ref = make_unique<BoundReferenceExpression>(order1.expression->return_type, 0);
|
|
58257
|
+
vector<BoundOrderByNode> orders;
|
|
58258
|
+
orders.emplace_back(BoundOrderByNode(order1.type, order1.null_order, move(ref)));
|
|
58259
|
+
|
|
58260
|
+
l1 = make_unique<SortedTable>(context, orders, payload_layout);
|
|
58261
|
+
|
|
58262
|
+
// LHS has positive rids
|
|
58263
|
+
ExpressionExecutor l_executor;
|
|
58264
|
+
l_executor.AddExpression(*order1.expression);
|
|
58265
|
+
l_executor.AddExpression(*order2.expression);
|
|
58266
|
+
AppendKey(t1, l_executor, *l1, 1, 1, b1);
|
|
58267
|
+
|
|
58268
|
+
// RHS has negative rids
|
|
58269
|
+
ExpressionExecutor r_executor;
|
|
58270
|
+
r_executor.AddExpression(*op.rhs_orders[0][0].expression);
|
|
58271
|
+
r_executor.AddExpression(*op.rhs_orders[1][0].expression);
|
|
58272
|
+
AppendKey(t2, r_executor, *l1, -1, -1, b2);
|
|
58273
|
+
|
|
58274
|
+
Sort(*l1);
|
|
58275
|
+
|
|
58276
|
+
op1 = make_unique<SBIterator>(l1->global_sort_state, cmp1);
|
|
58277
|
+
off1 = make_unique<SBIterator>(l1->global_sort_state, cmp1);
|
|
58278
|
+
|
|
58279
|
+
// We don't actually need the L1 column, just its sort key, which is in the sort blocks
|
|
58280
|
+
li = ExtractColumn<int64_t>(*l1, types.size() - 1);
|
|
58281
|
+
|
|
58282
|
+
// 4. if (op2 ∈ {>, ≥}) sort L2 in ascending order
|
|
58283
|
+
// 5. else if (op2 ∈ {<, ≤}) sort L2 in descending order
|
|
58284
|
+
|
|
58285
|
+
// We sort on Y/Y' to obtain the sort keys and the permutation array.
|
|
58286
|
+
// For this we just need a two-column table of Y, P
|
|
58287
|
+
types.clear();
|
|
58288
|
+
types.emplace_back(LogicalType::BIGINT);
|
|
58289
|
+
payload_layout.Initialize(types);
|
|
58290
|
+
|
|
58291
|
+
// Sort on the first expression
|
|
58292
|
+
orders.clear();
|
|
58293
|
+
ref = make_unique<BoundReferenceExpression>(order2.expression->return_type, 0);
|
|
58294
|
+
orders.emplace_back(BoundOrderByNode(order2.type, order2.null_order, move(ref)));
|
|
58295
|
+
|
|
58296
|
+
ExpressionExecutor executor;
|
|
58297
|
+
executor.AddExpression(*orders[0].expression);
|
|
58298
|
+
|
|
58299
|
+
l2 = make_unique<SortedTable>(context, orders, payload_layout);
|
|
58300
|
+
for (idx_t base = 0, block_idx = 0; block_idx < l1->BlockCount(); ++block_idx) {
|
|
58301
|
+
base += AppendKey(*l1, executor, *l2, 1, base, block_idx);
|
|
58302
|
+
}
|
|
58303
|
+
|
|
58304
|
+
Sort(*l2);
|
|
58305
|
+
|
|
58306
|
+
// We don't actually need the L2 column, just its sort key, which is in the sort blocks
|
|
58307
|
+
|
|
58308
|
+
// 6. compute the permutation array P of L2 w.r.t. L1
|
|
58309
|
+
p = ExtractColumn<idx_t>(*l2, types.size() - 1);
|
|
58310
|
+
|
|
58311
|
+
// 7. initialize bit-array B (|B| = n), and set all bits to 0
|
|
58312
|
+
n = l2->count.load();
|
|
58313
|
+
bit_array.resize(ValidityMask::EntryCount(n), 0);
|
|
58314
|
+
bit_mask.Initialize(bit_array.data());
|
|
58315
|
+
|
|
58316
|
+
// Bloom filter
|
|
58317
|
+
bloom_count = (n + (BLOOM_CHUNK_BITS - 1)) / BLOOM_CHUNK_BITS;
|
|
58318
|
+
bloom_array.resize(ValidityMask::EntryCount(bloom_count), 0);
|
|
58319
|
+
bloom_filter.Initialize(bloom_array.data());
|
|
58320
|
+
|
|
58321
|
+
// 11. for(i←1 to n) do
|
|
58322
|
+
const auto &cmp2 = op.conditions[1].comparison;
|
|
58323
|
+
op2 = make_unique<SBIterator>(l2->global_sort_state, cmp2);
|
|
58324
|
+
off2 = make_unique<SBIterator>(l2->global_sort_state, cmp2);
|
|
58325
|
+
i = 0;
|
|
58326
|
+
(void)NextRow();
|
|
58327
|
+
}
|
|
58328
|
+
|
|
58329
|
+
bool IEJoinUnion::NextRow() {
|
|
58330
|
+
for (; i < n; ++i) {
|
|
58331
|
+
// 12. pos ← P[i]
|
|
58332
|
+
auto pos = p[i];
|
|
58333
|
+
lrid = li[pos];
|
|
58334
|
+
if (lrid < 0) {
|
|
58335
|
+
continue;
|
|
58336
|
+
}
|
|
58337
|
+
|
|
58338
|
+
// 16. B[pos] ← 1
|
|
58339
|
+
op2->SetIndex(i);
|
|
58340
|
+
for (; off2->GetIndex() < n; ++(*off2)) {
|
|
58341
|
+
if (!off2->Compare(*op2)) {
|
|
58342
|
+
break;
|
|
58343
|
+
}
|
|
58344
|
+
const auto p2 = p[off2->GetIndex()];
|
|
58345
|
+
bit_mask.SetValid(p2);
|
|
58346
|
+
bloom_filter.SetValid(p2 / BLOOM_CHUNK_BITS);
|
|
58347
|
+
}
|
|
58348
|
+
|
|
58349
|
+
// 9. if (op1 ∈ {≤,≥} and op2 ∈ {≤,≥}) eqOff = 0
|
|
58350
|
+
// 10. else eqOff = 1
|
|
58351
|
+
// No, because there could be more than one equal value.
|
|
58352
|
+
// Scan the neighborhood instead
|
|
58353
|
+
op1->SetIndex(pos);
|
|
58354
|
+
off1->SetIndex(pos);
|
|
58355
|
+
for (; off1->GetIndex() > 0 && op1->Compare(*off1); --(*off1)) {
|
|
58356
|
+
continue;
|
|
58357
|
+
}
|
|
58358
|
+
for (; off1->GetIndex() < n && !op1->Compare(*off1); ++(*off1)) {
|
|
58359
|
+
continue;
|
|
58360
|
+
}
|
|
58361
|
+
|
|
58362
|
+
return true;
|
|
58363
|
+
}
|
|
58364
|
+
return false;
|
|
58365
|
+
}
|
|
58366
|
+
|
|
58367
|
+
static idx_t NextValid(const ValidityMask &bits, idx_t j, const idx_t n) {
|
|
58368
|
+
if (j >= n) {
|
|
58369
|
+
return n;
|
|
58370
|
+
}
|
|
58371
|
+
|
|
58372
|
+
// We can do a first approximation by checking entries one at a time
|
|
58373
|
+
// which gives 64:1.
|
|
58374
|
+
idx_t entry_idx, idx_in_entry;
|
|
58375
|
+
bits.GetEntryIndex(j, entry_idx, idx_in_entry);
|
|
58376
|
+
auto entry = bits.GetValidityEntry(entry_idx++);
|
|
58377
|
+
|
|
58378
|
+
// Trim the bits before the start position
|
|
58379
|
+
entry &= (ValidityMask::ValidityBuffer::MAX_ENTRY << idx_in_entry);
|
|
58380
|
+
|
|
58381
|
+
// Check the non-ragged entries
|
|
58382
|
+
for (const auto entry_count = bits.EntryCount(n); entry_idx < entry_count; ++entry_idx) {
|
|
58383
|
+
if (entry) {
|
|
58384
|
+
for (; idx_in_entry < bits.BITS_PER_VALUE; ++idx_in_entry, ++j) {
|
|
58385
|
+
if (bits.RowIsValid(entry, idx_in_entry)) {
|
|
58386
|
+
return j;
|
|
58387
|
+
}
|
|
58388
|
+
}
|
|
58389
|
+
} else {
|
|
58390
|
+
j += bits.BITS_PER_VALUE - idx_in_entry;
|
|
58391
|
+
}
|
|
58392
|
+
|
|
58393
|
+
entry = bits.GetValidityEntry(entry_idx);
|
|
58394
|
+
idx_in_entry = 0;
|
|
58395
|
+
}
|
|
58396
|
+
|
|
58397
|
+
// Check the final entry
|
|
58398
|
+
for (; j < n; ++idx_in_entry, ++j) {
|
|
58399
|
+
if (bits.RowIsValid(entry, idx_in_entry)) {
|
|
58400
|
+
return j;
|
|
58401
|
+
}
|
|
58402
|
+
}
|
|
58403
|
+
|
|
58404
|
+
return j;
|
|
58405
|
+
}
|
|
58406
|
+
|
|
58407
|
+
idx_t IEJoinUnion::JoinComplexBlocks(SelectionVector &lsel, SelectionVector &rsel) {
|
|
58408
|
+
// 8. initialize join result as an empty list for tuple pairs
|
|
58409
|
+
idx_t result_count = 0;
|
|
58410
|
+
|
|
58411
|
+
// 11. for(i←1 to n) do
|
|
58412
|
+
while (i < n) {
|
|
58413
|
+
// 13. for (j ← pos+eqOff to n) do
|
|
58414
|
+
for (;;) {
|
|
58415
|
+
// 14. if B[j] = 1 then
|
|
58416
|
+
auto j = off1->GetIndex();
|
|
58417
|
+
|
|
58418
|
+
// Use the Bloom filter to find candidate blocks
|
|
58419
|
+
while (j < n) {
|
|
58420
|
+
auto bloom_begin = NextValid(bloom_filter, j / BLOOM_CHUNK_BITS, bloom_count) * BLOOM_CHUNK_BITS;
|
|
58421
|
+
auto bloom_end = MinValue<idx_t>(n, bloom_begin + BLOOM_CHUNK_BITS);
|
|
58422
|
+
|
|
58423
|
+
j = MaxValue<idx_t>(j, bloom_begin);
|
|
58424
|
+
j = NextValid(bit_mask, j, bloom_end);
|
|
58425
|
+
if (j < bloom_end) {
|
|
58426
|
+
break;
|
|
58427
|
+
}
|
|
58428
|
+
}
|
|
58429
|
+
|
|
58430
|
+
if (j >= n) {
|
|
58431
|
+
break;
|
|
58432
|
+
}
|
|
58433
|
+
off1->SetIndex(j + 1);
|
|
58434
|
+
|
|
58435
|
+
// Filter out tuples with the same sign (they come from the same table)
|
|
58436
|
+
const auto rrid = li[j];
|
|
58437
|
+
|
|
58438
|
+
// 15. add tuples w.r.t. (L1[j], L1[i]) to join result
|
|
58439
|
+
if (lrid > 0 && rrid < 0) {
|
|
58440
|
+
lsel.set_index(result_count, sel_t(+lrid - 1));
|
|
58441
|
+
rsel.set_index(result_count, sel_t(-rrid - 1));
|
|
58442
|
+
++result_count;
|
|
58443
|
+
if (result_count == STANDARD_VECTOR_SIZE) {
|
|
58444
|
+
// out of space!
|
|
58445
|
+
return result_count;
|
|
58446
|
+
}
|
|
58447
|
+
}
|
|
58448
|
+
}
|
|
58449
|
+
++i;
|
|
58450
|
+
|
|
58451
|
+
if (!NextRow()) {
|
|
58452
|
+
break;
|
|
58453
|
+
}
|
|
58454
|
+
}
|
|
58455
|
+
|
|
58456
|
+
return result_count;
|
|
58457
|
+
}
|
|
58458
|
+
|
|
58459
|
+
class IEJoinState : public OperatorState {
|
|
58460
|
+
public:
|
|
58461
|
+
explicit IEJoinState(const PhysicalIEJoin &op) : local_left(op.conditions, 0) {};
|
|
58462
|
+
|
|
58463
|
+
IEJoinLocalState local_left;
|
|
58464
|
+
};
|
|
58465
|
+
|
|
58466
|
+
static void SliceSortedPayload(DataChunk &payload, GlobalSortState &state, const idx_t block_idx,
|
|
58467
|
+
const SelectionVector &result, const idx_t result_count, const idx_t left_cols = 0) {
|
|
58468
|
+
// There should only be one sorted block if they have been sorted
|
|
58469
|
+
D_ASSERT(state.sorted_blocks.size() == 1);
|
|
58470
|
+
SBScanState read_state(state.buffer_manager, state);
|
|
58471
|
+
read_state.sb = state.sorted_blocks[0].get();
|
|
58472
|
+
auto &sorted_data = *read_state.sb->payload_data;
|
|
58473
|
+
|
|
58474
|
+
read_state.SetIndices(block_idx, 0);
|
|
58475
|
+
read_state.PinData(sorted_data);
|
|
58476
|
+
const auto data_ptr = read_state.DataPtr(sorted_data);
|
|
58477
|
+
|
|
58478
|
+
// Set up a batch of pointers to scan data from
|
|
58479
|
+
Vector addresses(LogicalType::POINTER, result_count);
|
|
58480
|
+
auto data_pointers = FlatVector::GetData<data_ptr_t>(addresses);
|
|
58481
|
+
|
|
58482
|
+
// Set up the data pointers for the values that are actually referenced
|
|
58483
|
+
const idx_t &row_width = sorted_data.layout.GetRowWidth();
|
|
58484
|
+
|
|
58485
|
+
auto prev_idx = result.get_index(0);
|
|
58486
|
+
SelectionVector gsel(result_count);
|
|
58487
|
+
idx_t addr_count = 0;
|
|
58488
|
+
gsel.set_index(0, addr_count);
|
|
58489
|
+
data_pointers[addr_count] = data_ptr + prev_idx * row_width;
|
|
58490
|
+
for (idx_t i = 1; i < result_count; ++i) {
|
|
58491
|
+
const auto row_idx = result.get_index(i);
|
|
58492
|
+
if (row_idx != prev_idx) {
|
|
58493
|
+
data_pointers[++addr_count] = data_ptr + row_idx * row_width;
|
|
58494
|
+
prev_idx = row_idx;
|
|
58495
|
+
}
|
|
58496
|
+
gsel.set_index(i, addr_count);
|
|
58497
|
+
}
|
|
58498
|
+
++addr_count;
|
|
58499
|
+
|
|
58500
|
+
// Unswizzle the offsets back to pointers (if needed)
|
|
58501
|
+
if (!sorted_data.layout.AllConstant() && state.external) {
|
|
58502
|
+
RowOperations::UnswizzlePointers(sorted_data.layout, data_ptr, read_state.payload_heap_handle->Ptr(),
|
|
58503
|
+
addr_count);
|
|
58504
|
+
}
|
|
58505
|
+
|
|
58506
|
+
// Deserialize the payload data
|
|
58507
|
+
auto sel = FlatVector::IncrementalSelectionVector();
|
|
58508
|
+
for (idx_t col_idx = 0; col_idx < sorted_data.layout.ColumnCount(); col_idx++) {
|
|
58509
|
+
const auto col_offset = sorted_data.layout.GetOffsets()[col_idx];
|
|
58510
|
+
auto &col = payload.data[left_cols + col_idx];
|
|
58511
|
+
RowOperations::Gather(addresses, *sel, col, *sel, addr_count, col_offset, col_idx);
|
|
58512
|
+
col.Slice(gsel, result_count);
|
|
58513
|
+
}
|
|
58514
|
+
}
|
|
58515
|
+
|
|
58516
|
+
class IEJoinLocalSourceState : public LocalSourceState {
|
|
58517
|
+
public:
|
|
58518
|
+
explicit IEJoinLocalSourceState(const PhysicalIEJoin &op)
|
|
58519
|
+
: op(op), true_sel(STANDARD_VECTOR_SIZE), left_matches(nullptr), right_matches(nullptr) {
|
|
58520
|
+
|
|
58521
|
+
if (op.conditions.size() < 3) {
|
|
58522
|
+
return;
|
|
58523
|
+
}
|
|
58524
|
+
|
|
58525
|
+
vector<LogicalType> left_types;
|
|
58526
|
+
vector<LogicalType> right_types;
|
|
58527
|
+
for (idx_t i = 2; i < op.conditions.size(); ++i) {
|
|
58528
|
+
const auto &cond = op.conditions[i];
|
|
58529
|
+
|
|
58530
|
+
left_types.push_back(cond.left->return_type);
|
|
58531
|
+
left_executor.AddExpression(*cond.left);
|
|
58532
|
+
|
|
58533
|
+
right_types.push_back(cond.left->return_type);
|
|
58534
|
+
right_executor.AddExpression(*cond.right);
|
|
58535
|
+
}
|
|
58536
|
+
|
|
58537
|
+
left_keys.Initialize(left_types);
|
|
58538
|
+
right_keys.Initialize(right_types);
|
|
58539
|
+
}
|
|
58540
|
+
|
|
58541
|
+
idx_t SelectJoinTail(const ExpressionType &condition, Vector &left, Vector &right, const SelectionVector *sel,
|
|
58542
|
+
idx_t count);
|
|
58543
|
+
|
|
58544
|
+
idx_t SelectOuterRows(bool *matches) {
|
|
58545
|
+
idx_t count = 0;
|
|
58546
|
+
for (; outer_idx < outer_count; ++outer_idx) {
|
|
58547
|
+
if (!matches[outer_idx]) {
|
|
58548
|
+
true_sel.set_index(count++, outer_idx);
|
|
58549
|
+
if (count >= STANDARD_VECTOR_SIZE) {
|
|
58550
|
+
break;
|
|
58551
|
+
}
|
|
58552
|
+
}
|
|
58553
|
+
}
|
|
58554
|
+
|
|
58555
|
+
return count;
|
|
58556
|
+
}
|
|
58557
|
+
|
|
58558
|
+
const PhysicalIEJoin &op;
|
|
58559
|
+
|
|
58560
|
+
// Joining
|
|
58561
|
+
unique_ptr<IEJoinUnion> joiner;
|
|
58562
|
+
|
|
58563
|
+
idx_t left_base;
|
|
58564
|
+
idx_t left_block_index;
|
|
58565
|
+
|
|
58566
|
+
idx_t right_base;
|
|
58567
|
+
idx_t right_block_index;
|
|
58568
|
+
|
|
58569
|
+
// Trailing predicates
|
|
58570
|
+
SelectionVector true_sel;
|
|
58571
|
+
|
|
58572
|
+
ExpressionExecutor left_executor;
|
|
58573
|
+
DataChunk left_keys;
|
|
58574
|
+
|
|
58575
|
+
ExpressionExecutor right_executor;
|
|
58576
|
+
DataChunk right_keys;
|
|
58577
|
+
|
|
58578
|
+
// Outer joins
|
|
58579
|
+
idx_t outer_idx;
|
|
58580
|
+
idx_t outer_count;
|
|
58581
|
+
bool *left_matches;
|
|
58582
|
+
bool *right_matches;
|
|
58583
|
+
};
|
|
58584
|
+
|
|
58585
|
+
idx_t IEJoinLocalSourceState::SelectJoinTail(const ExpressionType &condition, Vector &left, Vector &right,
|
|
58586
|
+
const SelectionVector *sel, idx_t count) {
|
|
58587
|
+
switch (condition) {
|
|
58588
|
+
case ExpressionType::COMPARE_NOTEQUAL:
|
|
58589
|
+
return VectorOperations::NotEquals(left, right, sel, count, &true_sel, nullptr);
|
|
58590
|
+
case ExpressionType::COMPARE_LESSTHAN:
|
|
58591
|
+
return VectorOperations::LessThan(left, right, sel, count, &true_sel, nullptr);
|
|
58592
|
+
case ExpressionType::COMPARE_GREATERTHAN:
|
|
58593
|
+
return VectorOperations::GreaterThan(left, right, sel, count, &true_sel, nullptr);
|
|
58594
|
+
case ExpressionType::COMPARE_LESSTHANOREQUALTO:
|
|
58595
|
+
return VectorOperations::LessThanEquals(left, right, sel, count, &true_sel, nullptr);
|
|
58596
|
+
case ExpressionType::COMPARE_GREATERTHANOREQUALTO:
|
|
58597
|
+
return VectorOperations::GreaterThanEquals(left, right, sel, count, &true_sel, nullptr);
|
|
58598
|
+
case ExpressionType::COMPARE_DISTINCT_FROM:
|
|
58599
|
+
return VectorOperations::DistinctFrom(left, right, sel, count, &true_sel, nullptr);
|
|
58600
|
+
case ExpressionType::COMPARE_NOT_DISTINCT_FROM:
|
|
58601
|
+
case ExpressionType::COMPARE_EQUAL:
|
|
58602
|
+
default:
|
|
58603
|
+
throw InternalException("Unsupported comparison type for PhysicalIEJoin");
|
|
58604
|
+
}
|
|
58605
|
+
|
|
58606
|
+
return count;
|
|
58607
|
+
}
|
|
58608
|
+
|
|
58609
|
+
void PhysicalIEJoin::ResolveComplexJoin(ExecutionContext &context, DataChunk &chunk, LocalSourceState &state_p) const {
|
|
58610
|
+
auto &state = (IEJoinLocalSourceState &)state_p;
|
|
58611
|
+
auto &ie_sink = (IEJoinGlobalState &)*sink_state;
|
|
58612
|
+
auto &left_table = *ie_sink.tables[0];
|
|
58613
|
+
auto &right_table = *ie_sink.tables[1];
|
|
58614
|
+
|
|
58615
|
+
const auto left_cols = children[0]->GetTypes().size();
|
|
58616
|
+
do {
|
|
58617
|
+
SelectionVector lsel(STANDARD_VECTOR_SIZE);
|
|
58618
|
+
SelectionVector rsel(STANDARD_VECTOR_SIZE);
|
|
58619
|
+
auto result_count = state.joiner->JoinComplexBlocks(lsel, rsel);
|
|
58620
|
+
if (result_count == 0) {
|
|
58621
|
+
// exhausted this pair
|
|
58622
|
+
return;
|
|
58623
|
+
}
|
|
58624
|
+
|
|
58625
|
+
// found matches: extract them
|
|
58626
|
+
chunk.Reset();
|
|
58627
|
+
SliceSortedPayload(chunk, left_table.global_sort_state, state.left_block_index, lsel, result_count, 0);
|
|
58628
|
+
SliceSortedPayload(chunk, right_table.global_sort_state, state.right_block_index, rsel, result_count,
|
|
58629
|
+
left_cols);
|
|
58630
|
+
chunk.SetCardinality(result_count);
|
|
58631
|
+
|
|
58632
|
+
auto sel = FlatVector::IncrementalSelectionVector();
|
|
58633
|
+
if (conditions.size() > 2) {
|
|
58634
|
+
// If there are more expressions to compute,
|
|
58635
|
+
// split the result chunk into the left and right halves
|
|
58636
|
+
// so we can compute the values for comparison.
|
|
58637
|
+
const auto tail_cols = conditions.size() - 2;
|
|
58638
|
+
|
|
58639
|
+
DataChunk right_chunk;
|
|
58640
|
+
chunk.Split(right_chunk, left_cols);
|
|
58641
|
+
state.left_executor.SetChunk(chunk);
|
|
58642
|
+
state.right_executor.SetChunk(right_chunk);
|
|
58643
|
+
|
|
58644
|
+
auto tail_count = result_count;
|
|
58645
|
+
for (size_t cmp_idx = 0; cmp_idx < tail_cols; ++cmp_idx) {
|
|
58646
|
+
auto &left = state.left_keys.data[cmp_idx];
|
|
58647
|
+
state.left_executor.ExecuteExpression(cmp_idx, left);
|
|
58648
|
+
|
|
58649
|
+
auto &right = state.right_keys.data[cmp_idx];
|
|
58650
|
+
state.right_executor.ExecuteExpression(cmp_idx, right);
|
|
58651
|
+
|
|
58652
|
+
if (tail_count < result_count) {
|
|
58653
|
+
left.Slice(*sel, tail_count);
|
|
58654
|
+
right.Slice(*sel, tail_count);
|
|
58655
|
+
}
|
|
58656
|
+
tail_count = state.SelectJoinTail(conditions[cmp_idx + 2].comparison, left, right, sel, tail_count);
|
|
58657
|
+
sel = &state.true_sel;
|
|
58658
|
+
}
|
|
58659
|
+
chunk.Fuse(right_chunk);
|
|
58660
|
+
|
|
58661
|
+
if (tail_count < result_count) {
|
|
58662
|
+
result_count = tail_count;
|
|
58663
|
+
chunk.Slice(*sel, result_count);
|
|
58664
|
+
}
|
|
58665
|
+
}
|
|
58666
|
+
|
|
58667
|
+
// found matches: mark the found matches if required
|
|
58668
|
+
if (left_table.found_match) {
|
|
58669
|
+
for (idx_t i = 0; i < result_count; i++) {
|
|
58670
|
+
left_table.found_match[state.left_base + lsel[sel->get_index(i)]] = true;
|
|
58671
|
+
}
|
|
58672
|
+
}
|
|
58673
|
+
if (right_table.found_match) {
|
|
58674
|
+
for (idx_t i = 0; i < result_count; i++) {
|
|
58675
|
+
right_table.found_match[state.right_base + rsel[sel->get_index(i)]] = true;
|
|
58676
|
+
}
|
|
58677
|
+
}
|
|
58678
|
+
chunk.Verify();
|
|
58679
|
+
} while (chunk.size() == 0);
|
|
58680
|
+
}
|
|
58681
|
+
|
|
58682
|
+
OperatorResultType PhysicalIEJoin::Execute(ExecutionContext &context, DataChunk &input, DataChunk &chunk,
|
|
58683
|
+
OperatorState &state) const {
|
|
58684
|
+
return OperatorResultType::FINISHED;
|
|
58685
|
+
}
|
|
58686
|
+
|
|
58687
|
+
//===--------------------------------------------------------------------===//
|
|
58688
|
+
// Source
|
|
58689
|
+
//===--------------------------------------------------------------------===//
|
|
58690
|
+
class IEJoinGlobalSourceState : public GlobalSourceState {
|
|
58691
|
+
public:
|
|
58692
|
+
explicit IEJoinGlobalSourceState(const PhysicalIEJoin &op)
|
|
58693
|
+
: op(op), initialized(false), next_pair(0), completed(0), left_outers(0), next_left(0), right_outers(0),
|
|
58694
|
+
next_right(0) {
|
|
58695
|
+
}
|
|
58696
|
+
|
|
58697
|
+
void Initialize(IEJoinGlobalState &sink_state) {
|
|
58698
|
+
lock_guard<mutex> initializing(lock);
|
|
58699
|
+
if (initialized) {
|
|
58700
|
+
return;
|
|
58701
|
+
}
|
|
58702
|
+
|
|
58703
|
+
// Compute the starting row for reach block
|
|
58704
|
+
// (In theory these are all the same size, but you never know...)
|
|
58705
|
+
auto &left_table = *sink_state.tables[0];
|
|
58706
|
+
const auto left_blocks = left_table.BlockCount();
|
|
58707
|
+
idx_t left_base = 0;
|
|
58708
|
+
|
|
58709
|
+
for (size_t lhs = 0; lhs < left_blocks; ++lhs) {
|
|
58710
|
+
left_bases.emplace_back(left_base);
|
|
58711
|
+
left_base += left_table.BlockSize(lhs);
|
|
58712
|
+
}
|
|
58713
|
+
|
|
58714
|
+
auto &right_table = *sink_state.tables[1];
|
|
58715
|
+
const auto right_blocks = right_table.BlockCount();
|
|
58716
|
+
idx_t right_base = 0;
|
|
58717
|
+
for (size_t rhs = 0; rhs < right_blocks; ++rhs) {
|
|
58718
|
+
right_bases.emplace_back(right_base);
|
|
58719
|
+
right_base += right_table.BlockSize(rhs);
|
|
58720
|
+
}
|
|
58721
|
+
|
|
58722
|
+
// Outer join block counts
|
|
58723
|
+
if (left_table.found_match) {
|
|
58724
|
+
left_outers = left_blocks;
|
|
58725
|
+
}
|
|
58726
|
+
|
|
58727
|
+
if (right_table.found_match) {
|
|
58728
|
+
right_outers = right_blocks;
|
|
58729
|
+
}
|
|
58730
|
+
|
|
58731
|
+
// Ready for action
|
|
58732
|
+
initialized = true;
|
|
58733
|
+
}
|
|
58734
|
+
|
|
58735
|
+
public:
|
|
58736
|
+
idx_t MaxThreads() override {
|
|
58737
|
+
// We can't leverage any more threads than block pairs.
|
|
58738
|
+
const auto &sink_state = ((IEJoinGlobalState &)*op.sink_state);
|
|
58739
|
+
return sink_state.tables[0]->BlockCount() * sink_state.tables[1]->BlockCount();
|
|
58740
|
+
}
|
|
58741
|
+
|
|
58742
|
+
void GetNextPair(ClientContext &client, IEJoinGlobalState &gstate, IEJoinLocalSourceState &lstate) {
|
|
58743
|
+
auto &left_table = *gstate.tables[0];
|
|
58744
|
+
auto &right_table = *gstate.tables[1];
|
|
58745
|
+
|
|
58746
|
+
const auto left_blocks = left_table.BlockCount();
|
|
58747
|
+
const auto right_blocks = right_table.BlockCount();
|
|
58748
|
+
const auto pair_count = left_blocks * right_blocks;
|
|
58749
|
+
|
|
58750
|
+
// Regular block
|
|
58751
|
+
const auto i = next_pair++;
|
|
58752
|
+
if (i < pair_count) {
|
|
58753
|
+
const auto b1 = i / right_blocks;
|
|
58754
|
+
const auto b2 = i % right_blocks;
|
|
58755
|
+
|
|
58756
|
+
lstate.left_block_index = b1;
|
|
58757
|
+
lstate.left_base = left_bases[b1];
|
|
58758
|
+
|
|
58759
|
+
lstate.right_block_index = b2;
|
|
58760
|
+
lstate.right_base = right_bases[b2];
|
|
58761
|
+
|
|
58762
|
+
lstate.joiner = make_unique<IEJoinUnion>(client, op, left_table, b1, right_table, b2);
|
|
58763
|
+
return;
|
|
58764
|
+
} else {
|
|
58765
|
+
--next_pair;
|
|
58766
|
+
}
|
|
58767
|
+
|
|
58768
|
+
// Outer joins
|
|
58769
|
+
if (!left_outers && !right_outers) {
|
|
58770
|
+
return;
|
|
58771
|
+
}
|
|
58772
|
+
|
|
58773
|
+
// Spin wait for regular blocks to finish(!)
|
|
58774
|
+
while (completed < pair_count) {
|
|
58775
|
+
std::this_thread::yield();
|
|
58776
|
+
}
|
|
58777
|
+
|
|
58778
|
+
// Left outer blocks
|
|
58779
|
+
const auto l = next_left++;
|
|
58780
|
+
if (l < left_outers) {
|
|
58781
|
+
lstate.left_block_index = l;
|
|
58782
|
+
lstate.left_base = left_bases[l];
|
|
58783
|
+
|
|
58784
|
+
lstate.left_matches = left_table.found_match.get() + lstate.left_base;
|
|
58785
|
+
lstate.outer_idx = 0;
|
|
58786
|
+
lstate.outer_count = left_table.BlockSize(l);
|
|
58787
|
+
return;
|
|
58788
|
+
} else {
|
|
58789
|
+
lstate.left_matches = nullptr;
|
|
58790
|
+
--next_left;
|
|
58791
|
+
}
|
|
58792
|
+
|
|
58793
|
+
// Right outer block
|
|
58794
|
+
const auto r = next_right++;
|
|
58795
|
+
if (r < right_outers) {
|
|
58796
|
+
lstate.right_block_index = r;
|
|
58797
|
+
lstate.right_base = right_bases[r];
|
|
58798
|
+
|
|
58799
|
+
lstate.right_matches = right_table.found_match.get() + lstate.right_base;
|
|
58800
|
+
lstate.outer_idx = 0;
|
|
58801
|
+
lstate.outer_count = right_table.BlockSize(r);
|
|
58802
|
+
return;
|
|
58803
|
+
} else {
|
|
58804
|
+
lstate.right_matches = nullptr;
|
|
58805
|
+
--next_right;
|
|
58806
|
+
}
|
|
58807
|
+
}
|
|
58808
|
+
|
|
58809
|
+
void PairCompleted(ClientContext &client, IEJoinGlobalState &gstate, IEJoinLocalSourceState &lstate) {
|
|
58810
|
+
lstate.joiner.reset();
|
|
58811
|
+
++completed;
|
|
58812
|
+
GetNextPair(client, gstate, lstate);
|
|
58813
|
+
}
|
|
58814
|
+
|
|
58815
|
+
const PhysicalIEJoin &op;
|
|
58816
|
+
|
|
58817
|
+
mutex lock;
|
|
58818
|
+
bool initialized;
|
|
58819
|
+
|
|
58820
|
+
// Join queue state
|
|
58821
|
+
std::atomic<size_t> next_pair;
|
|
58822
|
+
std::atomic<size_t> completed;
|
|
58823
|
+
|
|
58824
|
+
// Block base row number
|
|
58825
|
+
vector<idx_t> left_bases;
|
|
58826
|
+
vector<idx_t> right_bases;
|
|
58827
|
+
|
|
58828
|
+
// Outer joins
|
|
58829
|
+
idx_t left_outers;
|
|
58830
|
+
std::atomic<idx_t> next_left;
|
|
58831
|
+
|
|
58832
|
+
idx_t right_outers;
|
|
58833
|
+
std::atomic<idx_t> next_right;
|
|
58834
|
+
};
|
|
58835
|
+
|
|
58836
|
+
unique_ptr<GlobalSourceState> PhysicalIEJoin::GetGlobalSourceState(ClientContext &context) const {
|
|
58837
|
+
return make_unique<IEJoinGlobalSourceState>(*this);
|
|
58838
|
+
}
|
|
58839
|
+
|
|
58840
|
+
unique_ptr<LocalSourceState> PhysicalIEJoin::GetLocalSourceState(ExecutionContext &context,
|
|
58841
|
+
GlobalSourceState &gstate) const {
|
|
58842
|
+
return make_unique<IEJoinLocalSourceState>(*this);
|
|
58843
|
+
}
|
|
58844
|
+
|
|
58845
|
+
void PhysicalIEJoin::GetData(ExecutionContext &context, DataChunk &result, GlobalSourceState &gstate,
|
|
58846
|
+
LocalSourceState &lstate) const {
|
|
58847
|
+
auto &ie_sink = (IEJoinGlobalState &)*sink_state;
|
|
58848
|
+
auto &ie_gstate = (IEJoinGlobalSourceState &)gstate;
|
|
58849
|
+
auto &ie_lstate = (IEJoinLocalSourceState &)lstate;
|
|
58850
|
+
|
|
58851
|
+
ie_gstate.Initialize(ie_sink);
|
|
58852
|
+
|
|
58853
|
+
if (!ie_lstate.joiner) {
|
|
58854
|
+
ie_gstate.GetNextPair(context.client, ie_sink, ie_lstate);
|
|
58855
|
+
}
|
|
58856
|
+
|
|
58857
|
+
// Process INNER results
|
|
58858
|
+
while (ie_lstate.joiner) {
|
|
58859
|
+
ResolveComplexJoin(context, result, ie_lstate);
|
|
58860
|
+
|
|
58861
|
+
if (result.size()) {
|
|
58862
|
+
return;
|
|
58863
|
+
}
|
|
58864
|
+
|
|
58865
|
+
ie_gstate.PairCompleted(context.client, ie_sink, ie_lstate);
|
|
58866
|
+
}
|
|
58867
|
+
|
|
58868
|
+
// Process LEFT OUTER results
|
|
58869
|
+
const auto left_cols = children[0]->GetTypes().size();
|
|
58870
|
+
while (ie_lstate.left_matches) {
|
|
58871
|
+
const idx_t count = ie_lstate.SelectOuterRows(ie_lstate.left_matches);
|
|
58872
|
+
if (!count) {
|
|
58873
|
+
ie_gstate.GetNextPair(context.client, ie_sink, ie_lstate);
|
|
58874
|
+
continue;
|
|
58875
|
+
}
|
|
58876
|
+
|
|
58877
|
+
SliceSortedPayload(result, ie_sink.tables[0]->global_sort_state, ie_lstate.left_base, ie_lstate.true_sel,
|
|
58878
|
+
count);
|
|
58879
|
+
|
|
58880
|
+
// Fill in NULLs to the right
|
|
58881
|
+
for (auto col_idx = left_cols; col_idx < result.ColumnCount(); ++col_idx) {
|
|
58882
|
+
result.data[col_idx].SetVectorType(VectorType::CONSTANT_VECTOR);
|
|
58883
|
+
ConstantVector::SetNull(result.data[col_idx], true);
|
|
58884
|
+
}
|
|
58885
|
+
|
|
58886
|
+
result.SetCardinality(count);
|
|
58887
|
+
result.Verify();
|
|
58888
|
+
|
|
58889
|
+
return;
|
|
58890
|
+
}
|
|
58891
|
+
|
|
58892
|
+
// Process RIGHT OUTER results
|
|
58893
|
+
while (ie_lstate.right_matches) {
|
|
58894
|
+
const idx_t count = ie_lstate.SelectOuterRows(ie_lstate.right_matches);
|
|
58895
|
+
if (!count) {
|
|
58896
|
+
ie_gstate.GetNextPair(context.client, ie_sink, ie_lstate);
|
|
58897
|
+
}
|
|
58898
|
+
|
|
58899
|
+
SliceSortedPayload(result, ie_sink.tables[1]->global_sort_state, ie_lstate.right_base, ie_lstate.true_sel,
|
|
58900
|
+
count, left_cols);
|
|
58901
|
+
|
|
58902
|
+
// Fill in NULLs to the left
|
|
58903
|
+
for (idx_t col_idx = 0; col_idx < left_cols; ++col_idx) {
|
|
58904
|
+
result.data[col_idx].SetVectorType(VectorType::CONSTANT_VECTOR);
|
|
58905
|
+
ConstantVector::SetNull(result.data[col_idx], true);
|
|
58906
|
+
}
|
|
58907
|
+
|
|
58908
|
+
result.SetCardinality(count);
|
|
58909
|
+
result.Verify();
|
|
58910
|
+
|
|
58911
|
+
return;
|
|
58912
|
+
}
|
|
58913
|
+
}
|
|
58914
|
+
|
|
57377
58915
|
} // namespace duckdb
|
|
57378
58916
|
//===----------------------------------------------------------------------===//
|
|
57379
58917
|
// DuckDB
|
|
@@ -58478,32 +60016,6 @@ unique_ptr<GlobalSinkState> PhysicalPiecewiseMergeJoin::GetGlobalSinkState(Clien
|
|
|
58478
60016
|
return move(state);
|
|
58479
60017
|
}
|
|
58480
60018
|
|
|
58481
|
-
static idx_t CountValid(Vector &v, const idx_t count) {
|
|
58482
|
-
idx_t valid = 0;
|
|
58483
|
-
|
|
58484
|
-
VectorData vdata;
|
|
58485
|
-
v.Orrify(count, vdata);
|
|
58486
|
-
if (vdata.validity.AllValid()) {
|
|
58487
|
-
return count;
|
|
58488
|
-
}
|
|
58489
|
-
switch (v.GetVectorType()) {
|
|
58490
|
-
case VectorType::FLAT_VECTOR:
|
|
58491
|
-
valid += vdata.validity.CountValid(count);
|
|
58492
|
-
break;
|
|
58493
|
-
case VectorType::CONSTANT_VECTOR:
|
|
58494
|
-
valid += vdata.validity.CountValid(1) * count;
|
|
58495
|
-
break;
|
|
58496
|
-
default:
|
|
58497
|
-
for (idx_t i = 0; i < count; ++i) {
|
|
58498
|
-
const auto row_idx = vdata.sel->get_index(i);
|
|
58499
|
-
valid += int(vdata.validity.RowIsValid(row_idx));
|
|
58500
|
-
}
|
|
58501
|
-
break;
|
|
58502
|
-
}
|
|
58503
|
-
|
|
58504
|
-
return valid;
|
|
58505
|
-
}
|
|
58506
|
-
|
|
58507
60019
|
class MergeJoinLocalState : public LocalSinkState {
|
|
58508
60020
|
public:
|
|
58509
60021
|
explicit MergeJoinLocalState() : rhs_has_null(0), rhs_count(0) {
|
|
@@ -58604,7 +60116,7 @@ static idx_t PiecewiseMergeNulls(DataChunk &keys, const vector<JoinCondition> &c
|
|
|
58604
60116
|
}
|
|
58605
60117
|
return count - pvalidity.CountValid(count);
|
|
58606
60118
|
} else {
|
|
58607
|
-
return count -
|
|
60119
|
+
return count - VectorOperations::CountNotNull(primary, count);
|
|
58608
60120
|
}
|
|
58609
60121
|
}
|
|
58610
60122
|
|
|
@@ -66022,6 +67534,7 @@ unique_ptr<PhysicalOperator> PhysicalPlanGenerator::CreatePlan(LogicalChunkGet &
|
|
|
66022
67534
|
|
|
66023
67535
|
|
|
66024
67536
|
|
|
67537
|
+
|
|
66025
67538
|
namespace duckdb {
|
|
66026
67539
|
|
|
66027
67540
|
static bool CanPlanIndexJoin(Transaction &transaction, TableScanBindData *bind_data, PhysicalTableScan &scan) {
|
|
@@ -66180,8 +67693,7 @@ unique_ptr<PhysicalOperator> PhysicalPlanGenerator::CreatePlan(LogicalComparison
|
|
|
66180
67693
|
|
|
66181
67694
|
bool has_equality = false;
|
|
66182
67695
|
// bool has_inequality = false;
|
|
66183
|
-
|
|
66184
|
-
bool has_null_equal_conditions = false;
|
|
67696
|
+
size_t has_range = 0;
|
|
66185
67697
|
for (size_t c = 0; c < op.conditions.size(); ++c) {
|
|
66186
67698
|
auto &cond = op.conditions[c];
|
|
66187
67699
|
switch (cond.comparison) {
|
|
@@ -66193,7 +67705,7 @@ unique_ptr<PhysicalOperator> PhysicalPlanGenerator::CreatePlan(LogicalComparison
|
|
|
66193
67705
|
case ExpressionType::COMPARE_GREATERTHAN:
|
|
66194
67706
|
case ExpressionType::COMPARE_LESSTHANOREQUALTO:
|
|
66195
67707
|
case ExpressionType::COMPARE_GREATERTHANOREQUALTO:
|
|
66196
|
-
has_range
|
|
67708
|
+
++has_range;
|
|
66197
67709
|
break;
|
|
66198
67710
|
case ExpressionType::COMPARE_NOTEQUAL:
|
|
66199
67711
|
case ExpressionType::COMPARE_DISTINCT_FROM:
|
|
@@ -66203,12 +67715,10 @@ unique_ptr<PhysicalOperator> PhysicalPlanGenerator::CreatePlan(LogicalComparison
|
|
|
66203
67715
|
throw NotImplementedException("Unimplemented comparison join");
|
|
66204
67716
|
}
|
|
66205
67717
|
if (cond.null_values_are_equal) {
|
|
66206
|
-
has_null_equal_conditions = true;
|
|
66207
67718
|
D_ASSERT(cond.comparison == ExpressionType::COMPARE_EQUAL ||
|
|
66208
67719
|
cond.comparison == ExpressionType::COMPARE_DISTINCT_FROM);
|
|
66209
67720
|
}
|
|
66210
67721
|
}
|
|
66211
|
-
(void)has_null_equal_conditions;
|
|
66212
67722
|
|
|
66213
67723
|
unique_ptr<PhysicalOperator> plan;
|
|
66214
67724
|
if (has_equality) {
|
|
@@ -66237,17 +67747,22 @@ unique_ptr<PhysicalOperator> PhysicalPlanGenerator::CreatePlan(LogicalComparison
|
|
|
66237
67747
|
op.estimated_cardinality, perfect_join_stats);
|
|
66238
67748
|
|
|
66239
67749
|
} else {
|
|
66240
|
-
bool can_merge = has_range;
|
|
67750
|
+
bool can_merge = has_range > 0;
|
|
67751
|
+
bool can_iejoin = has_range >= 2 && rec_ctes.empty();
|
|
66241
67752
|
switch (op.join_type) {
|
|
66242
67753
|
case JoinType::SEMI:
|
|
66243
67754
|
case JoinType::ANTI:
|
|
66244
67755
|
case JoinType::MARK:
|
|
66245
67756
|
can_merge = can_merge && op.conditions.size() == 1;
|
|
67757
|
+
can_iejoin = false;
|
|
66246
67758
|
break;
|
|
66247
67759
|
default:
|
|
66248
67760
|
break;
|
|
66249
67761
|
}
|
|
66250
|
-
if (
|
|
67762
|
+
if (can_iejoin) {
|
|
67763
|
+
plan = make_unique<PhysicalIEJoin>(op, move(left), move(right), move(op.conditions), op.join_type,
|
|
67764
|
+
op.estimated_cardinality);
|
|
67765
|
+
} else if (can_merge) {
|
|
66251
67766
|
// range join: use piecewise merge join
|
|
66252
67767
|
plan = make_unique<PhysicalPiecewiseMergeJoin>(op, move(left), move(right), move(op.conditions),
|
|
66253
67768
|
op.join_type, op.estimated_cardinality);
|
|
@@ -115711,6 +117226,7 @@ bool QueryProfiler::OperatorRequiresProfiling(PhysicalOperatorType op_type) {
|
|
|
115711
117226
|
case PhysicalOperatorType::HASH_JOIN:
|
|
115712
117227
|
case PhysicalOperatorType::CROSS_PRODUCT:
|
|
115713
117228
|
case PhysicalOperatorType::PIECEWISE_MERGE_JOIN:
|
|
117229
|
+
case PhysicalOperatorType::IE_JOIN:
|
|
115714
117230
|
case PhysicalOperatorType::DELIM_JOIN:
|
|
115715
117231
|
case PhysicalOperatorType::UNION:
|
|
115716
117232
|
case PhysicalOperatorType::RECURSIVE_CTE:
|
|
@@ -128568,6 +130084,7 @@ void Event::SetTasks(vector<unique_ptr<Task>> tasks) {
|
|
|
128568
130084
|
|
|
128569
130085
|
|
|
128570
130086
|
|
|
130087
|
+
|
|
128571
130088
|
//===----------------------------------------------------------------------===//
|
|
128572
130089
|
// DuckDB
|
|
128573
130090
|
//
|
|
@@ -129243,6 +130760,35 @@ void Executor::BuildPipelines(PhysicalOperator *op, Pipeline *current) {
|
|
|
129243
130760
|
}
|
|
129244
130761
|
BuildPipelines(op->children[0].get(), current);
|
|
129245
130762
|
break;
|
|
130763
|
+
case PhysicalOperatorType::IE_JOIN: {
|
|
130764
|
+
D_ASSERT(op->children.size() == 2);
|
|
130765
|
+
if (recursive_cte) {
|
|
130766
|
+
throw NotImplementedException("IEJoins are not supported in recursive CTEs yet");
|
|
130767
|
+
}
|
|
130768
|
+
|
|
130769
|
+
// Build the LHS
|
|
130770
|
+
auto lhs_pipeline = make_shared<Pipeline>(*this);
|
|
130771
|
+
lhs_pipeline->sink = op;
|
|
130772
|
+
D_ASSERT(op->children[0].get());
|
|
130773
|
+
BuildPipelines(op->children[0].get(), lhs_pipeline.get());
|
|
130774
|
+
|
|
130775
|
+
// Build the RHS
|
|
130776
|
+
auto rhs_pipeline = make_shared<Pipeline>(*this);
|
|
130777
|
+
rhs_pipeline->sink = op;
|
|
130778
|
+
D_ASSERT(op->children[1].get());
|
|
130779
|
+
BuildPipelines(op->children[1].get(), rhs_pipeline.get());
|
|
130780
|
+
|
|
130781
|
+
// RHS => LHS => current
|
|
130782
|
+
current->AddDependency(rhs_pipeline);
|
|
130783
|
+
rhs_pipeline->AddDependency(lhs_pipeline);
|
|
130784
|
+
|
|
130785
|
+
pipelines.emplace_back(move(lhs_pipeline));
|
|
130786
|
+
pipelines.emplace_back(move(rhs_pipeline));
|
|
130787
|
+
|
|
130788
|
+
// Now build both and scan
|
|
130789
|
+
current->source = op;
|
|
130790
|
+
return;
|
|
130791
|
+
}
|
|
129246
130792
|
case PhysicalOperatorType::DELIM_JOIN: {
|
|
129247
130793
|
// duplicate eliminated join
|
|
129248
130794
|
// for delim joins, recurse into the actual join
|