duckdb 0.3.2-dev788.0 → 0.3.2-dev841.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 +451 -33
- package/src/duckdb.hpp +31 -16
- package/src/parquet-amalgamation.cpp +30917 -29877
- package/src/parquet-amalgamation.hpp +57 -68
package/src/duckdb.cpp
CHANGED
|
@@ -21139,6 +21139,7 @@ public:
|
|
|
21139
21139
|
|
|
21140
21140
|
int64_t GetFileSize(FileHandle &handle) override;
|
|
21141
21141
|
|
|
21142
|
+
void Reset(FileHandle &handle) override;
|
|
21142
21143
|
bool OnDiskFile(FileHandle &handle) override {
|
|
21143
21144
|
return false;
|
|
21144
21145
|
};
|
|
@@ -21181,6 +21182,10 @@ int64_t PipeFile::WriteChunk(void *buffer, int64_t nr_bytes) {
|
|
|
21181
21182
|
return child_handle->Write(buffer, nr_bytes);
|
|
21182
21183
|
}
|
|
21183
21184
|
|
|
21185
|
+
void PipeFileSystem::Reset(FileHandle &handle) {
|
|
21186
|
+
throw InternalException("Cannot reset pipe file system");
|
|
21187
|
+
}
|
|
21188
|
+
|
|
21184
21189
|
int64_t PipeFileSystem::Read(FileHandle &handle, void *buffer, int64_t nr_bytes) {
|
|
21185
21190
|
auto &pipe = (PipeFile &)handle;
|
|
21186
21191
|
return pipe.ReadChunk(buffer, nr_bytes);
|
|
@@ -56617,6 +56622,125 @@ string PhysicalTopN::ParamsToString() const {
|
|
|
56617
56622
|
|
|
56618
56623
|
namespace duckdb {
|
|
56619
56624
|
|
|
56625
|
+
struct CSVFileHandle {
|
|
56626
|
+
public:
|
|
56627
|
+
explicit CSVFileHandle(unique_ptr<FileHandle> file_handle_p) : file_handle(move(file_handle_p)) {
|
|
56628
|
+
can_seek = file_handle->CanSeek();
|
|
56629
|
+
plain_file_source = file_handle->OnDiskFile() && can_seek;
|
|
56630
|
+
file_size = file_handle->GetFileSize();
|
|
56631
|
+
}
|
|
56632
|
+
|
|
56633
|
+
bool CanSeek() {
|
|
56634
|
+
return can_seek;
|
|
56635
|
+
}
|
|
56636
|
+
void Seek(idx_t position) {
|
|
56637
|
+
if (!can_seek) {
|
|
56638
|
+
throw InternalException("Cannot seek in this file");
|
|
56639
|
+
}
|
|
56640
|
+
file_handle->Seek(position);
|
|
56641
|
+
}
|
|
56642
|
+
idx_t SeekPosition() {
|
|
56643
|
+
if (!can_seek) {
|
|
56644
|
+
throw InternalException("Cannot seek in this file");
|
|
56645
|
+
}
|
|
56646
|
+
return file_handle->SeekPosition();
|
|
56647
|
+
}
|
|
56648
|
+
void Reset() {
|
|
56649
|
+
if (plain_file_source) {
|
|
56650
|
+
file_handle->Reset();
|
|
56651
|
+
} else {
|
|
56652
|
+
if (!reset_enabled) {
|
|
56653
|
+
throw InternalException("Reset called but reset is not enabled for this CSV Handle");
|
|
56654
|
+
}
|
|
56655
|
+
read_position = 0;
|
|
56656
|
+
}
|
|
56657
|
+
}
|
|
56658
|
+
bool PlainFileSource() {
|
|
56659
|
+
return plain_file_source;
|
|
56660
|
+
}
|
|
56661
|
+
|
|
56662
|
+
idx_t FileSize() {
|
|
56663
|
+
return file_size;
|
|
56664
|
+
}
|
|
56665
|
+
|
|
56666
|
+
idx_t Read(void *buffer, idx_t nr_bytes) {
|
|
56667
|
+
if (!plain_file_source) {
|
|
56668
|
+
// not a plain file source: we need to do some bookkeeping around the reset functionality
|
|
56669
|
+
idx_t result_offset = 0;
|
|
56670
|
+
if (read_position < buffer_size) {
|
|
56671
|
+
// we need to read from our cached buffer
|
|
56672
|
+
auto buffer_read_count = MinValue<idx_t>(nr_bytes, buffer_size - read_position);
|
|
56673
|
+
memcpy(buffer, cached_buffer.get() + read_position, buffer_read_count);
|
|
56674
|
+
result_offset += buffer_read_count;
|
|
56675
|
+
read_position += buffer_read_count;
|
|
56676
|
+
if (result_offset == nr_bytes) {
|
|
56677
|
+
return nr_bytes;
|
|
56678
|
+
}
|
|
56679
|
+
} else if (!reset_enabled && cached_buffer) {
|
|
56680
|
+
// reset is disabled but we still have cached data
|
|
56681
|
+
// we can remove any cached data
|
|
56682
|
+
cached_buffer.reset();
|
|
56683
|
+
buffer_size = 0;
|
|
56684
|
+
buffer_capacity = 0;
|
|
56685
|
+
read_position = 0;
|
|
56686
|
+
}
|
|
56687
|
+
// we have data left to read from the file
|
|
56688
|
+
// read directly into the buffer
|
|
56689
|
+
auto bytes_read = file_handle->Read((char *)buffer + result_offset, nr_bytes - result_offset);
|
|
56690
|
+
read_position += bytes_read;
|
|
56691
|
+
if (reset_enabled) {
|
|
56692
|
+
// if reset caching is enabled, we need to cache the bytes that we have read
|
|
56693
|
+
if (buffer_size + bytes_read >= buffer_capacity) {
|
|
56694
|
+
// no space; first enlarge the buffer
|
|
56695
|
+
buffer_capacity = MaxValue<idx_t>(NextPowerOfTwo(buffer_size + bytes_read), buffer_capacity * 2);
|
|
56696
|
+
|
|
56697
|
+
auto new_buffer = unique_ptr<data_t[]>(new data_t[buffer_capacity]);
|
|
56698
|
+
if (buffer_size > 0) {
|
|
56699
|
+
memcpy(new_buffer.get(), cached_buffer.get(), buffer_size);
|
|
56700
|
+
}
|
|
56701
|
+
cached_buffer = move(new_buffer);
|
|
56702
|
+
}
|
|
56703
|
+
memcpy(cached_buffer.get() + buffer_size, (char *)buffer + result_offset, bytes_read);
|
|
56704
|
+
buffer_size += bytes_read;
|
|
56705
|
+
}
|
|
56706
|
+
|
|
56707
|
+
return result_offset + bytes_read;
|
|
56708
|
+
} else {
|
|
56709
|
+
return file_handle->Read(buffer, nr_bytes);
|
|
56710
|
+
}
|
|
56711
|
+
}
|
|
56712
|
+
|
|
56713
|
+
string ReadLine() {
|
|
56714
|
+
string result;
|
|
56715
|
+
char buffer[1];
|
|
56716
|
+
while (true) {
|
|
56717
|
+
idx_t tuples_read = Read(buffer, 1);
|
|
56718
|
+
if (tuples_read == 0 || buffer[0] == '\n') {
|
|
56719
|
+
return result;
|
|
56720
|
+
}
|
|
56721
|
+
if (buffer[0] != '\r') {
|
|
56722
|
+
result += buffer[0];
|
|
56723
|
+
}
|
|
56724
|
+
}
|
|
56725
|
+
}
|
|
56726
|
+
|
|
56727
|
+
void DisableReset() {
|
|
56728
|
+
this->reset_enabled = false;
|
|
56729
|
+
}
|
|
56730
|
+
|
|
56731
|
+
private:
|
|
56732
|
+
unique_ptr<FileHandle> file_handle;
|
|
56733
|
+
bool reset_enabled = true;
|
|
56734
|
+
bool can_seek = false;
|
|
56735
|
+
bool plain_file_source = false;
|
|
56736
|
+
idx_t file_size = 0;
|
|
56737
|
+
// reset support
|
|
56738
|
+
unique_ptr<data_t[]> cached_buffer;
|
|
56739
|
+
idx_t read_position = 0;
|
|
56740
|
+
idx_t buffer_size = 0;
|
|
56741
|
+
idx_t buffer_capacity = 0;
|
|
56742
|
+
};
|
|
56743
|
+
|
|
56620
56744
|
void BufferedCSVReaderOptions::SetDelimiter(const string &input) {
|
|
56621
56745
|
this->delimiter = StringUtil::Replace(input, "\\t", "\t");
|
|
56622
56746
|
this->has_delimiter = true;
|
|
@@ -56749,6 +56873,13 @@ BufferedCSVReader::BufferedCSVReader(ClientContext &context, BufferedCSVReaderOp
|
|
|
56749
56873
|
requested_types) {
|
|
56750
56874
|
}
|
|
56751
56875
|
|
|
56876
|
+
BufferedCSVReader::~BufferedCSVReader() {
|
|
56877
|
+
}
|
|
56878
|
+
|
|
56879
|
+
idx_t BufferedCSVReader::GetFileSize() {
|
|
56880
|
+
return file_handle ? file_handle->FileSize() : 0;
|
|
56881
|
+
}
|
|
56882
|
+
|
|
56752
56883
|
void BufferedCSVReader::Initialize(const vector<LogicalType> &requested_types) {
|
|
56753
56884
|
PrepareComplexParser();
|
|
56754
56885
|
if (options.auto_detect) {
|
|
@@ -56765,6 +56896,9 @@ void BufferedCSVReader::Initialize(const vector<LogicalType> &requested_types) {
|
|
|
56765
56896
|
SkipRowsAndReadHeader(options.skip_rows, options.header);
|
|
56766
56897
|
}
|
|
56767
56898
|
InitParseChunk(sql_types.size());
|
|
56899
|
+
// we only need reset support during the automatic CSV type detection
|
|
56900
|
+
// since reset support might require caching (in the case of streams), we disable it for the remainder
|
|
56901
|
+
file_handle->DisableReset();
|
|
56768
56902
|
}
|
|
56769
56903
|
|
|
56770
56904
|
void BufferedCSVReader::PrepareComplexParser() {
|
|
@@ -56773,12 +56907,10 @@ void BufferedCSVReader::PrepareComplexParser() {
|
|
|
56773
56907
|
quote_search = TextSearchShiftArray(options.quote);
|
|
56774
56908
|
}
|
|
56775
56909
|
|
|
56776
|
-
unique_ptr<
|
|
56777
|
-
auto
|
|
56778
|
-
|
|
56779
|
-
|
|
56780
|
-
file_size = result->GetFileSize();
|
|
56781
|
-
return result;
|
|
56910
|
+
unique_ptr<CSVFileHandle> BufferedCSVReader::OpenCSV(const BufferedCSVReaderOptions &options) {
|
|
56911
|
+
auto file_handle = fs.OpenFile(options.file_path.c_str(), FileFlags::FILE_FLAGS_READ, FileLockType::NO_LOCK,
|
|
56912
|
+
options.compression, this->opener);
|
|
56913
|
+
return make_unique<CSVFileHandle>(move(file_handle));
|
|
56782
56914
|
}
|
|
56783
56915
|
|
|
56784
56916
|
// Helper function to generate column names
|
|
@@ -56944,7 +57076,7 @@ bool BufferedCSVReader::JumpToNextSample() {
|
|
|
56944
57076
|
// assess if it makes sense to jump, based on size of the first chunk relative to size of the entire file
|
|
56945
57077
|
if (sample_chunk_idx == 0) {
|
|
56946
57078
|
idx_t bytes_first_chunk = bytes_in_chunk;
|
|
56947
|
-
double chunks_fit = (
|
|
57079
|
+
double chunks_fit = (file_handle->FileSize() / (double)bytes_first_chunk);
|
|
56948
57080
|
jumping_samples = chunks_fit >= options.sample_chunks;
|
|
56949
57081
|
|
|
56950
57082
|
// jump back to the beginning
|
|
@@ -56959,7 +57091,7 @@ bool BufferedCSVReader::JumpToNextSample() {
|
|
|
56959
57091
|
|
|
56960
57092
|
// if we deal with any other sources than plaintext files, jumping_samples can be tricky. In that case
|
|
56961
57093
|
// we just read x continuous chunks from the stream TODO: make jumps possible for zipfiles.
|
|
56962
|
-
if (!
|
|
57094
|
+
if (!file_handle->PlainFileSource() || !jumping_samples) {
|
|
56963
57095
|
sample_chunk_idx++;
|
|
56964
57096
|
return true;
|
|
56965
57097
|
}
|
|
@@ -56969,13 +57101,13 @@ bool BufferedCSVReader::JumpToNextSample() {
|
|
|
56969
57101
|
bytes_per_line_avg = ((bytes_per_line_avg * (sample_chunk_idx)) + bytes_per_line) / (sample_chunk_idx + 1);
|
|
56970
57102
|
|
|
56971
57103
|
// if none of the previous conditions were met, we can jump
|
|
56972
|
-
idx_t partition_size = (idx_t)round(
|
|
57104
|
+
idx_t partition_size = (idx_t)round(file_handle->FileSize() / (double)options.sample_chunks);
|
|
56973
57105
|
|
|
56974
57106
|
// calculate offset to end of the current partition
|
|
56975
57107
|
int64_t offset = partition_size - bytes_in_chunk - remaining_bytes_in_buffer;
|
|
56976
57108
|
auto current_pos = file_handle->SeekPosition();
|
|
56977
57109
|
|
|
56978
|
-
if (current_pos + offset <
|
|
57110
|
+
if (current_pos + offset < file_handle->FileSize()) {
|
|
56979
57111
|
// set position in stream and clear failure bits
|
|
56980
57112
|
file_handle->Seek(current_pos + offset);
|
|
56981
57113
|
|
|
@@ -56986,10 +57118,10 @@ bool BufferedCSVReader::JumpToNextSample() {
|
|
|
56986
57118
|
// seek backwards from the end in last chunk and hope to catch the end of the file
|
|
56987
57119
|
// TODO: actually it would be good to make sure that the end of file is being reached, because
|
|
56988
57120
|
// messy end-lines are quite common. For this case, however, we first need a skip_end detection anyways.
|
|
56989
|
-
file_handle->Seek(
|
|
57121
|
+
file_handle->Seek(file_handle->FileSize() - bytes_in_chunk);
|
|
56990
57122
|
|
|
56991
57123
|
// estimate linenr
|
|
56992
|
-
linenr = (idx_t)round((
|
|
57124
|
+
linenr = (idx_t)round((file_handle->FileSize() - bytes_in_chunk) / bytes_per_line_avg);
|
|
56993
57125
|
linenr_estimated = true;
|
|
56994
57126
|
}
|
|
56995
57127
|
|
|
@@ -57143,6 +57275,7 @@ void BufferedCSVReader::DetectDialect(const vector<LogicalType> &requested_types
|
|
|
57143
57275
|
|
|
57144
57276
|
JumpToBeginning(original_options.skip_rows);
|
|
57145
57277
|
sniffed_column_counts.clear();
|
|
57278
|
+
|
|
57146
57279
|
if (!TryParseCSV(ParserMode::SNIFFING_DIALECT)) {
|
|
57147
57280
|
continue;
|
|
57148
57281
|
}
|
|
@@ -88587,9 +88720,9 @@ static void GenerateUUIDFunction(DataChunk &args, ExpressionState &state, Vector
|
|
|
88587
88720
|
}
|
|
88588
88721
|
|
|
88589
88722
|
void UUIDFun::RegisterFunction(BuiltinFunctions &set) {
|
|
88723
|
+
ScalarFunction uuid_function({}, LogicalType::UUID, GenerateUUIDFunction, true, UUIDRandomBind);
|
|
88590
88724
|
// generate a random uuid
|
|
88591
|
-
set.AddFunction(
|
|
88592
|
-
ScalarFunction("gen_random_uuid", {}, LogicalType::UUID, GenerateUUIDFunction, true, UUIDRandomBind));
|
|
88725
|
+
set.AddFunction({"uuid", "gen_random_uuid"}, uuid_function);
|
|
88593
88726
|
}
|
|
88594
88727
|
|
|
88595
88728
|
} // namespace duckdb
|
|
@@ -91542,7 +91675,7 @@ static unique_ptr<FunctionOperatorData> ReadCSVInit(ClientContext &context, cons
|
|
|
91542
91675
|
result->csv_reader = make_unique<BufferedCSVReader>(context, bind_data.options, bind_data.sql_types);
|
|
91543
91676
|
}
|
|
91544
91677
|
bind_data.bytes_read = 0;
|
|
91545
|
-
bind_data.file_size = result->csv_reader->
|
|
91678
|
+
bind_data.file_size = result->csv_reader->GetFileSize();
|
|
91546
91679
|
result->file_index = 1;
|
|
91547
91680
|
return move(result);
|
|
91548
91681
|
}
|
|
@@ -98287,8 +98420,8 @@ string ClientContext::VerifyQuery(ClientContextLock &lock, const string &query,
|
|
|
98287
98420
|
} catch (std::exception &ex) {
|
|
98288
98421
|
original_result->error = ex.what();
|
|
98289
98422
|
original_result->success = false;
|
|
98290
|
-
interrupted = false;
|
|
98291
98423
|
}
|
|
98424
|
+
interrupted = false;
|
|
98292
98425
|
|
|
98293
98426
|
// check explain, only if q does not already contain EXPLAIN
|
|
98294
98427
|
if (original_result->success) {
|
|
@@ -98308,8 +98441,8 @@ string ClientContext::VerifyQuery(ClientContextLock &lock, const string &query,
|
|
|
98308
98441
|
} catch (std::exception &ex) {
|
|
98309
98442
|
copied_result->error = ex.what();
|
|
98310
98443
|
copied_result->success = false;
|
|
98311
|
-
interrupted = false;
|
|
98312
98444
|
}
|
|
98445
|
+
interrupted = false;
|
|
98313
98446
|
// now execute the deserialized statement
|
|
98314
98447
|
try {
|
|
98315
98448
|
auto result = RunStatementInternal(lock, query, move(deserialized_stmt), false, false);
|
|
@@ -98317,8 +98450,8 @@ string ClientContext::VerifyQuery(ClientContextLock &lock, const string &query,
|
|
|
98317
98450
|
} catch (std::exception &ex) {
|
|
98318
98451
|
deserialized_result->error = ex.what();
|
|
98319
98452
|
deserialized_result->success = false;
|
|
98320
|
-
interrupted = false;
|
|
98321
98453
|
}
|
|
98454
|
+
interrupted = false;
|
|
98322
98455
|
// now execute the unoptimized statement
|
|
98323
98456
|
config.enable_optimizer = false;
|
|
98324
98457
|
try {
|
|
@@ -98327,8 +98460,8 @@ string ClientContext::VerifyQuery(ClientContextLock &lock, const string &query,
|
|
|
98327
98460
|
} catch (std::exception &ex) {
|
|
98328
98461
|
unoptimized_result->error = ex.what();
|
|
98329
98462
|
unoptimized_result->success = false;
|
|
98330
|
-
interrupted = false;
|
|
98331
98463
|
}
|
|
98464
|
+
interrupted = false;
|
|
98332
98465
|
config.enable_optimizer = true;
|
|
98333
98466
|
|
|
98334
98467
|
if (profiling_is_enabled) {
|
|
@@ -112849,7 +112982,10 @@ void ExpressionRewriter::VisitExpression(unique_ptr<Expression> *expression) {
|
|
|
112849
112982
|
|
|
112850
112983
|
|
|
112851
112984
|
|
|
112985
|
+
|
|
112986
|
+
|
|
112852
112987
|
#include <functional>
|
|
112988
|
+
#include <map>
|
|
112853
112989
|
|
|
112854
112990
|
namespace duckdb {
|
|
112855
112991
|
|
|
@@ -112887,6 +113023,37 @@ private:
|
|
|
112887
113023
|
idx_t GetEquivalenceSet(Expression *expr);
|
|
112888
113024
|
FilterResult AddConstantComparison(vector<ExpressionValueInformation> &info_list, ExpressionValueInformation info);
|
|
112889
113025
|
|
|
113026
|
+
//! Functions used to push and generate OR Filters
|
|
113027
|
+
void LookUpConjunctions(Expression *expr);
|
|
113028
|
+
bool BFSLookUpConjunctions(BoundConjunctionExpression *conjunction);
|
|
113029
|
+
void VerifyOrsToPush(Expression &expr);
|
|
113030
|
+
|
|
113031
|
+
bool UpdateConjunctionFilter(BoundComparisonExpression *comparison_expr);
|
|
113032
|
+
bool UpdateFilterByColumn(BoundColumnRefExpression *column_ref, BoundComparisonExpression *comparison_expr);
|
|
113033
|
+
void GenerateORFilters(TableFilterSet &table_filter, vector<idx_t> &column_ids);
|
|
113034
|
+
|
|
113035
|
+
template <typename CONJUNCTION_TYPE>
|
|
113036
|
+
void GenerateConjunctionFilter(BoundConjunctionExpression *conjunction, ConjunctionFilter *last_conj_filter) {
|
|
113037
|
+
auto new_filter = NextConjunctionFilter<CONJUNCTION_TYPE>(conjunction);
|
|
113038
|
+
auto conj_filter_ptr = (ConjunctionFilter *)new_filter.get();
|
|
113039
|
+
last_conj_filter->child_filters.push_back(move(new_filter));
|
|
113040
|
+
last_conj_filter = conj_filter_ptr;
|
|
113041
|
+
}
|
|
113042
|
+
|
|
113043
|
+
template <typename CONJUNCTION_TYPE>
|
|
113044
|
+
unique_ptr<TableFilter> NextConjunctionFilter(BoundConjunctionExpression *conjunction) {
|
|
113045
|
+
unique_ptr<ConjunctionFilter> conj_filter = make_unique<CONJUNCTION_TYPE>();
|
|
113046
|
+
for (auto &expr : conjunction->children) {
|
|
113047
|
+
auto comp_expr = (BoundComparisonExpression *)expr.get();
|
|
113048
|
+
auto &const_expr =
|
|
113049
|
+
(comp_expr->left->type == ExpressionType::VALUE_CONSTANT) ? *comp_expr->left : *comp_expr->right;
|
|
113050
|
+
auto const_value = ExpressionExecutor::EvaluateScalar(const_expr);
|
|
113051
|
+
auto const_filter = make_unique<ConstantFilter>(comp_expr->type, const_value);
|
|
113052
|
+
conj_filter->child_filters.push_back(move(const_filter));
|
|
113053
|
+
}
|
|
113054
|
+
return move(conj_filter);
|
|
113055
|
+
}
|
|
113056
|
+
|
|
112890
113057
|
private:
|
|
112891
113058
|
vector<unique_ptr<Expression>> remaining_filters;
|
|
112892
113059
|
|
|
@@ -112895,6 +113062,26 @@ private:
|
|
|
112895
113062
|
unordered_map<idx_t, vector<ExpressionValueInformation>> constant_values;
|
|
112896
113063
|
unordered_map<idx_t, vector<Expression *>> equivalence_map;
|
|
112897
113064
|
idx_t set_index = 0;
|
|
113065
|
+
|
|
113066
|
+
//! Structures used for OR Filters
|
|
113067
|
+
|
|
113068
|
+
struct ConjunctionsToPush {
|
|
113069
|
+
BoundConjunctionExpression *root_or;
|
|
113070
|
+
|
|
113071
|
+
// only preserve AND if there is a single column in the expression
|
|
113072
|
+
bool preserve_and = true;
|
|
113073
|
+
|
|
113074
|
+
// conjunction chain for this column
|
|
113075
|
+
vector<unique_ptr<BoundConjunctionExpression>> conjunctions;
|
|
113076
|
+
};
|
|
113077
|
+
|
|
113078
|
+
expression_map_t<vector<unique_ptr<ConjunctionsToPush>>> map_col_conjunctions;
|
|
113079
|
+
vector<BoundColumnRefExpression *> vec_colref_insertion_order;
|
|
113080
|
+
|
|
113081
|
+
BoundConjunctionExpression *cur_root_or;
|
|
113082
|
+
BoundConjunctionExpression *cur_conjunction;
|
|
113083
|
+
|
|
113084
|
+
BoundColumnRefExpression *cur_colref_to_push;
|
|
112898
113085
|
};
|
|
112899
113086
|
|
|
112900
113087
|
} // namespace duckdb
|
|
@@ -112916,6 +113103,7 @@ private:
|
|
|
112916
113103
|
|
|
112917
113104
|
|
|
112918
113105
|
|
|
113106
|
+
|
|
112919
113107
|
namespace duckdb {
|
|
112920
113108
|
|
|
112921
113109
|
using ExpressionValueInformation = FilterCombiner::ExpressionValueInformation;
|
|
@@ -112978,6 +113166,7 @@ FilterResult FilterCombiner::AddConstantComparison(vector<ExpressionValueInforma
|
|
|
112978
113166
|
}
|
|
112979
113167
|
|
|
112980
113168
|
FilterResult FilterCombiner::AddFilter(unique_ptr<Expression> expr) {
|
|
113169
|
+
LookUpConjunctions(expr.get());
|
|
112981
113170
|
// try to push the filter into the combiner
|
|
112982
113171
|
auto result = AddFilter(expr.get());
|
|
112983
113172
|
if (result == FilterResult::UNSUPPORTED) {
|
|
@@ -113450,6 +113639,8 @@ TableFilterSet FilterCombiner::GenerateTableScanFilters(vector<idx_t> &column_id
|
|
|
113450
113639
|
}
|
|
113451
113640
|
}
|
|
113452
113641
|
|
|
113642
|
+
GenerateORFilters(table_filters, column_ids);
|
|
113643
|
+
|
|
113453
113644
|
return table_filters;
|
|
113454
113645
|
}
|
|
113455
113646
|
|
|
@@ -113887,6 +114078,176 @@ ValueComparisonResult CompareValueInformation(ExpressionValueInformation &left,
|
|
|
113887
114078
|
}
|
|
113888
114079
|
}
|
|
113889
114080
|
|
|
114081
|
+
void FilterCombiner::LookUpConjunctions(Expression *expr) {
|
|
114082
|
+
if (expr->GetExpressionType() == ExpressionType::CONJUNCTION_OR) {
|
|
114083
|
+
auto root_or_expr = (BoundConjunctionExpression *)expr;
|
|
114084
|
+
for (const auto &entry : map_col_conjunctions) {
|
|
114085
|
+
for (const auto &conjs_to_push : entry.second) {
|
|
114086
|
+
if (conjs_to_push->root_or->Equals(root_or_expr)) {
|
|
114087
|
+
return;
|
|
114088
|
+
}
|
|
114089
|
+
}
|
|
114090
|
+
}
|
|
114091
|
+
|
|
114092
|
+
cur_root_or = root_or_expr;
|
|
114093
|
+
cur_conjunction = root_or_expr;
|
|
114094
|
+
cur_colref_to_push = nullptr;
|
|
114095
|
+
if (!BFSLookUpConjunctions(cur_root_or)) {
|
|
114096
|
+
if (cur_colref_to_push) {
|
|
114097
|
+
auto entry = map_col_conjunctions.find(cur_colref_to_push);
|
|
114098
|
+
auto &vec_conjs_to_push = entry->second;
|
|
114099
|
+
if (vec_conjs_to_push.size() == 1) {
|
|
114100
|
+
map_col_conjunctions.erase(entry);
|
|
114101
|
+
return;
|
|
114102
|
+
}
|
|
114103
|
+
vec_conjs_to_push.pop_back();
|
|
114104
|
+
}
|
|
114105
|
+
}
|
|
114106
|
+
return;
|
|
114107
|
+
}
|
|
114108
|
+
|
|
114109
|
+
// Verify if the expression has a column already pushed down by other OR expression
|
|
114110
|
+
VerifyOrsToPush(*expr);
|
|
114111
|
+
}
|
|
114112
|
+
|
|
114113
|
+
bool FilterCombiner::BFSLookUpConjunctions(BoundConjunctionExpression *conjunction) {
|
|
114114
|
+
vector<BoundConjunctionExpression *> conjunctions_to_visit;
|
|
114115
|
+
|
|
114116
|
+
for (auto &child : conjunction->children) {
|
|
114117
|
+
switch (child->GetExpressionClass()) {
|
|
114118
|
+
case ExpressionClass::BOUND_CONJUNCTION: {
|
|
114119
|
+
auto child_conjunction = (BoundConjunctionExpression *)child.get();
|
|
114120
|
+
conjunctions_to_visit.emplace_back(child_conjunction);
|
|
114121
|
+
break;
|
|
114122
|
+
}
|
|
114123
|
+
case ExpressionClass::BOUND_COMPARISON: {
|
|
114124
|
+
if (!UpdateConjunctionFilter((BoundComparisonExpression *)child.get())) {
|
|
114125
|
+
return false;
|
|
114126
|
+
}
|
|
114127
|
+
break;
|
|
114128
|
+
}
|
|
114129
|
+
default: {
|
|
114130
|
+
return false;
|
|
114131
|
+
}
|
|
114132
|
+
}
|
|
114133
|
+
}
|
|
114134
|
+
|
|
114135
|
+
for (auto child_conjunction : conjunctions_to_visit) {
|
|
114136
|
+
cur_conjunction = child_conjunction;
|
|
114137
|
+
// traverse child conjuction
|
|
114138
|
+
if (!BFSLookUpConjunctions(child_conjunction)) {
|
|
114139
|
+
return false;
|
|
114140
|
+
}
|
|
114141
|
+
}
|
|
114142
|
+
return true;
|
|
114143
|
+
}
|
|
114144
|
+
|
|
114145
|
+
void FilterCombiner::VerifyOrsToPush(Expression &expr) {
|
|
114146
|
+
if (expr.type == ExpressionType::BOUND_COLUMN_REF) {
|
|
114147
|
+
auto colref = (BoundColumnRefExpression *)&expr;
|
|
114148
|
+
auto entry = map_col_conjunctions.find(colref);
|
|
114149
|
+
if (entry == map_col_conjunctions.end()) {
|
|
114150
|
+
return;
|
|
114151
|
+
}
|
|
114152
|
+
map_col_conjunctions.erase(entry);
|
|
114153
|
+
}
|
|
114154
|
+
ExpressionIterator::EnumerateChildren(expr, [&](Expression &child) { VerifyOrsToPush(child); });
|
|
114155
|
+
}
|
|
114156
|
+
|
|
114157
|
+
bool FilterCombiner::UpdateConjunctionFilter(BoundComparisonExpression *comparison_expr) {
|
|
114158
|
+
bool left_is_scalar = comparison_expr->left->IsFoldable();
|
|
114159
|
+
bool right_is_scalar = comparison_expr->right->IsFoldable();
|
|
114160
|
+
|
|
114161
|
+
Expression *non_scalar_expr;
|
|
114162
|
+
if (left_is_scalar || right_is_scalar) {
|
|
114163
|
+
// only support comparison with scalar
|
|
114164
|
+
non_scalar_expr = left_is_scalar ? comparison_expr->right.get() : comparison_expr->left.get();
|
|
114165
|
+
|
|
114166
|
+
if (non_scalar_expr->GetExpressionType() == ExpressionType::BOUND_COLUMN_REF) {
|
|
114167
|
+
return UpdateFilterByColumn((BoundColumnRefExpression *)non_scalar_expr, comparison_expr);
|
|
114168
|
+
}
|
|
114169
|
+
}
|
|
114170
|
+
|
|
114171
|
+
return false;
|
|
114172
|
+
}
|
|
114173
|
+
|
|
114174
|
+
bool FilterCombiner::UpdateFilterByColumn(BoundColumnRefExpression *column_ref,
|
|
114175
|
+
BoundComparisonExpression *comparison_expr) {
|
|
114176
|
+
if (cur_colref_to_push == nullptr) {
|
|
114177
|
+
cur_colref_to_push = column_ref;
|
|
114178
|
+
|
|
114179
|
+
auto or_conjunction = make_unique<BoundConjunctionExpression>(ExpressionType::CONJUNCTION_OR);
|
|
114180
|
+
or_conjunction->children.emplace_back(comparison_expr->Copy());
|
|
114181
|
+
|
|
114182
|
+
unique_ptr<ConjunctionsToPush> conjs_to_push = make_unique<ConjunctionsToPush>();
|
|
114183
|
+
conjs_to_push->conjunctions.emplace_back(move(or_conjunction));
|
|
114184
|
+
conjs_to_push->root_or = cur_root_or;
|
|
114185
|
+
|
|
114186
|
+
auto &&vec_col_conjs = map_col_conjunctions[column_ref];
|
|
114187
|
+
vec_col_conjs.emplace_back(move(conjs_to_push));
|
|
114188
|
+
vec_colref_insertion_order.emplace_back(column_ref);
|
|
114189
|
+
return true;
|
|
114190
|
+
}
|
|
114191
|
+
|
|
114192
|
+
auto entry = map_col_conjunctions.find(cur_colref_to_push);
|
|
114193
|
+
D_ASSERT(entry != map_col_conjunctions.end());
|
|
114194
|
+
auto &conjunctions_to_push = entry->second.back();
|
|
114195
|
+
|
|
114196
|
+
if (!cur_colref_to_push->Equals(column_ref)) {
|
|
114197
|
+
// check for multiple colunms in the same root OR node
|
|
114198
|
+
if (cur_root_or == cur_conjunction) {
|
|
114199
|
+
return false;
|
|
114200
|
+
}
|
|
114201
|
+
// found an AND using a different column, we should stop the look up
|
|
114202
|
+
if (cur_conjunction->GetExpressionType() == ExpressionType::CONJUNCTION_AND) {
|
|
114203
|
+
return false;
|
|
114204
|
+
}
|
|
114205
|
+
|
|
114206
|
+
// found a different column, AND conditions cannot be preserved anymore
|
|
114207
|
+
conjunctions_to_push->preserve_and = false;
|
|
114208
|
+
return true;
|
|
114209
|
+
}
|
|
114210
|
+
|
|
114211
|
+
auto &last_conjunction = conjunctions_to_push->conjunctions.back();
|
|
114212
|
+
if (cur_conjunction->GetExpressionType() == last_conjunction->GetExpressionType()) {
|
|
114213
|
+
last_conjunction->children.emplace_back(comparison_expr->Copy());
|
|
114214
|
+
} else {
|
|
114215
|
+
auto new_conjunction = make_unique<BoundConjunctionExpression>(cur_conjunction->GetExpressionType());
|
|
114216
|
+
new_conjunction->children.emplace_back(comparison_expr->Copy());
|
|
114217
|
+
conjunctions_to_push->conjunctions.emplace_back(move(new_conjunction));
|
|
114218
|
+
}
|
|
114219
|
+
return true;
|
|
114220
|
+
}
|
|
114221
|
+
|
|
114222
|
+
void FilterCombiner::GenerateORFilters(TableFilterSet &table_filter, vector<idx_t> &column_ids) {
|
|
114223
|
+
for (const auto colref : vec_colref_insertion_order) {
|
|
114224
|
+
auto column_index = column_ids[colref->binding.column_index];
|
|
114225
|
+
if (column_index == COLUMN_IDENTIFIER_ROW_ID) {
|
|
114226
|
+
break;
|
|
114227
|
+
}
|
|
114228
|
+
|
|
114229
|
+
for (const auto &conjunctions_to_push : map_col_conjunctions[colref]) {
|
|
114230
|
+
// root OR filter to push into the TableFilter
|
|
114231
|
+
auto root_or_filter = make_unique<ConjunctionOrFilter>();
|
|
114232
|
+
// variable to hold the last conjuntion filter pointer
|
|
114233
|
+
// the next filter will be added into it, i.e., we create a chain of conjunction filters
|
|
114234
|
+
ConjunctionFilter *last_conj_filter = root_or_filter.get();
|
|
114235
|
+
|
|
114236
|
+
for (auto &conjunction : conjunctions_to_push->conjunctions) {
|
|
114237
|
+
if (conjunction->GetExpressionType() == ExpressionType::CONJUNCTION_AND &&
|
|
114238
|
+
conjunctions_to_push->preserve_and) {
|
|
114239
|
+
GenerateConjunctionFilter<ConjunctionAndFilter>(conjunction.get(), last_conj_filter);
|
|
114240
|
+
} else {
|
|
114241
|
+
GenerateConjunctionFilter<ConjunctionOrFilter>(conjunction.get(), last_conj_filter);
|
|
114242
|
+
}
|
|
114243
|
+
}
|
|
114244
|
+
table_filter.PushFilter(column_index, move(root_or_filter));
|
|
114245
|
+
}
|
|
114246
|
+
}
|
|
114247
|
+
map_col_conjunctions.clear();
|
|
114248
|
+
vec_colref_insertion_order.clear();
|
|
114249
|
+
}
|
|
114250
|
+
|
|
113890
114251
|
} // namespace duckdb
|
|
113891
114252
|
//===----------------------------------------------------------------------===//
|
|
113892
114253
|
// DuckDB
|
|
@@ -119023,6 +119384,10 @@ FilterPropagateResult StatisticsPropagator::PropagateComparison(BaseStatistics &
|
|
|
119023
119384
|
// only handle numerics for now
|
|
119024
119385
|
switch (left.type.InternalType()) {
|
|
119025
119386
|
case PhysicalType::BOOL:
|
|
119387
|
+
case PhysicalType::UINT8:
|
|
119388
|
+
case PhysicalType::UINT16:
|
|
119389
|
+
case PhysicalType::UINT32:
|
|
119390
|
+
case PhysicalType::UINT64:
|
|
119026
119391
|
case PhysicalType::INT8:
|
|
119027
119392
|
case PhysicalType::INT16:
|
|
119028
119393
|
case PhysicalType::INT32:
|
|
@@ -119150,6 +119515,10 @@ namespace duckdb {
|
|
|
119150
119515
|
unique_ptr<BaseStatistics> StatisticsPropagator::StatisticsFromValue(const Value &input) {
|
|
119151
119516
|
switch (input.type().InternalType()) {
|
|
119152
119517
|
case PhysicalType::BOOL:
|
|
119518
|
+
case PhysicalType::UINT8:
|
|
119519
|
+
case PhysicalType::UINT16:
|
|
119520
|
+
case PhysicalType::UINT32:
|
|
119521
|
+
case PhysicalType::UINT64:
|
|
119153
119522
|
case PhysicalType::INT8:
|
|
119154
119523
|
case PhysicalType::INT16:
|
|
119155
119524
|
case PhysicalType::INT32:
|
|
@@ -149839,7 +150208,7 @@ void ExpressionIterator::EnumerateQueryNodeChildren(BoundQueryNode &node,
|
|
|
149839
150208
|
|
|
149840
150209
|
namespace duckdb {
|
|
149841
150210
|
|
|
149842
|
-
ConjunctionOrFilter::ConjunctionOrFilter() :
|
|
150211
|
+
ConjunctionOrFilter::ConjunctionOrFilter() : ConjunctionFilter(TableFilterType::CONJUNCTION_OR) {
|
|
149843
150212
|
}
|
|
149844
150213
|
|
|
149845
150214
|
FilterPropagateResult ConjunctionOrFilter::CheckStatistics(BaseStatistics &stats) {
|
|
@@ -149868,7 +150237,7 @@ string ConjunctionOrFilter::ToString(const string &column_name) {
|
|
|
149868
150237
|
}
|
|
149869
150238
|
|
|
149870
150239
|
bool ConjunctionOrFilter::Equals(const TableFilter &other_p) const {
|
|
149871
|
-
if (!
|
|
150240
|
+
if (!ConjunctionFilter::Equals(other_p)) {
|
|
149872
150241
|
return false;
|
|
149873
150242
|
}
|
|
149874
150243
|
auto &other = (ConjunctionOrFilter &)other_p;
|
|
@@ -149883,11 +150252,11 @@ bool ConjunctionOrFilter::Equals(const TableFilter &other_p) const {
|
|
|
149883
150252
|
return true;
|
|
149884
150253
|
}
|
|
149885
150254
|
|
|
149886
|
-
ConjunctionAndFilter::ConjunctionAndFilter() :
|
|
150255
|
+
ConjunctionAndFilter::ConjunctionAndFilter() : ConjunctionFilter(TableFilterType::CONJUNCTION_AND) {
|
|
149887
150256
|
}
|
|
149888
150257
|
|
|
149889
150258
|
FilterPropagateResult ConjunctionAndFilter::CheckStatistics(BaseStatistics &stats) {
|
|
149890
|
-
// the
|
|
150259
|
+
// the AND filter is true if ALL of the children is true
|
|
149891
150260
|
D_ASSERT(!child_filters.empty());
|
|
149892
150261
|
auto result = FilterPropagateResult::FILTER_ALWAYS_TRUE;
|
|
149893
150262
|
for (auto &filter : child_filters) {
|
|
@@ -149913,7 +150282,7 @@ string ConjunctionAndFilter::ToString(const string &column_name) {
|
|
|
149913
150282
|
}
|
|
149914
150283
|
|
|
149915
150284
|
bool ConjunctionAndFilter::Equals(const TableFilter &other_p) const {
|
|
149916
|
-
if (!
|
|
150285
|
+
if (!ConjunctionFilter::Equals(other_p)) {
|
|
149917
150286
|
return false;
|
|
149918
150287
|
}
|
|
149919
150288
|
auto &other = (ConjunctionAndFilter &)other_p;
|
|
@@ -160130,6 +160499,14 @@ FilterPropagateResult NumericStatistics::CheckZonemap(ExpressionType comparison_
|
|
|
160130
160499
|
} else {
|
|
160131
160500
|
return FilterPropagateResult::FILTER_ALWAYS_FALSE;
|
|
160132
160501
|
}
|
|
160502
|
+
case ExpressionType::COMPARE_NOTEQUAL:
|
|
160503
|
+
if (constant < min || constant > max) {
|
|
160504
|
+
return FilterPropagateResult::FILTER_ALWAYS_TRUE;
|
|
160505
|
+
} else if (min == max && min == constant) {
|
|
160506
|
+
// corner case of a cluster with one numeric equal to the target constant
|
|
160507
|
+
return FilterPropagateResult::FILTER_ALWAYS_FALSE;
|
|
160508
|
+
}
|
|
160509
|
+
return FilterPropagateResult::NO_PRUNING_POSSIBLE;
|
|
160133
160510
|
case ExpressionType::COMPARE_GREATERTHANOREQUALTO:
|
|
160134
160511
|
// X >= C
|
|
160135
160512
|
// this can be true only if max(X) >= C
|
|
@@ -160326,7 +160703,6 @@ unique_ptr<BaseStatistics> StringStatistics::Copy() {
|
|
|
160326
160703
|
memcpy(stats->max, max, MAX_STRING_MINMAX_SIZE);
|
|
160327
160704
|
stats->has_unicode = has_unicode;
|
|
160328
160705
|
stats->max_string_length = max_string_length;
|
|
160329
|
-
stats->max_string_length = max_string_length;
|
|
160330
160706
|
if (validity_stats) {
|
|
160331
160707
|
stats->validity_stats = validity_stats->Copy();
|
|
160332
160708
|
}
|
|
@@ -160430,6 +160806,11 @@ FilterPropagateResult StringStatistics::CheckZonemap(ExpressionType comparison_t
|
|
|
160430
160806
|
} else {
|
|
160431
160807
|
return FilterPropagateResult::FILTER_ALWAYS_FALSE;
|
|
160432
160808
|
}
|
|
160809
|
+
case ExpressionType::COMPARE_NOTEQUAL:
|
|
160810
|
+
if (min_comp < 0 || max_comp > 0) {
|
|
160811
|
+
return FilterPropagateResult::FILTER_ALWAYS_TRUE;
|
|
160812
|
+
}
|
|
160813
|
+
return FilterPropagateResult::NO_PRUNING_POSSIBLE;
|
|
160433
160814
|
case ExpressionType::COMPARE_GREATERTHANOREQUALTO:
|
|
160434
160815
|
case ExpressionType::COMPARE_GREATERTHAN:
|
|
160435
160816
|
if (max_comp <= 0) {
|
|
@@ -162553,6 +162934,16 @@ static void FilterSelectionSwitch(T *vec, T *predicate, SelectionVector &sel, id
|
|
|
162553
162934
|
}
|
|
162554
162935
|
break;
|
|
162555
162936
|
}
|
|
162937
|
+
case ExpressionType::COMPARE_NOTEQUAL: {
|
|
162938
|
+
if (mask.AllValid()) {
|
|
162939
|
+
approved_tuple_count =
|
|
162940
|
+
TemplatedFilterSelection<T, NotEquals, false>(vec, predicate, sel, approved_tuple_count, mask, new_sel);
|
|
162941
|
+
} else {
|
|
162942
|
+
approved_tuple_count =
|
|
162943
|
+
TemplatedFilterSelection<T, NotEquals, true>(vec, predicate, sel, approved_tuple_count, mask, new_sel);
|
|
162944
|
+
}
|
|
162945
|
+
break;
|
|
162946
|
+
}
|
|
162556
162947
|
case ExpressionType::COMPARE_LESSTHAN: {
|
|
162557
162948
|
if (mask.AllValid()) {
|
|
162558
162949
|
approved_tuple_count =
|
|
@@ -162622,15 +163013,44 @@ static idx_t TemplatedNullSelection(SelectionVector &sel, idx_t approved_tuple_c
|
|
|
162622
163013
|
}
|
|
162623
163014
|
}
|
|
162624
163015
|
|
|
162625
|
-
|
|
162626
|
-
|
|
163016
|
+
idx_t ColumnSegment::FilterSelection(SelectionVector &sel, Vector &result, const TableFilter &filter,
|
|
163017
|
+
idx_t &approved_tuple_count, ValidityMask &mask) {
|
|
162627
163018
|
switch (filter.filter_type) {
|
|
163019
|
+
case TableFilterType::CONJUNCTION_OR: {
|
|
163020
|
+
// similar to the CONJUNCTION_AND, but we need to take care of the SelectionVectors (OR all of them)
|
|
163021
|
+
idx_t count_total = 0;
|
|
163022
|
+
SelectionVector result_sel(approved_tuple_count);
|
|
163023
|
+
auto &conjunction_or = (ConjunctionOrFilter &)filter;
|
|
163024
|
+
for (auto &child_filter : conjunction_or.child_filters) {
|
|
163025
|
+
SelectionVector temp_sel;
|
|
163026
|
+
temp_sel.Initialize(sel);
|
|
163027
|
+
idx_t temp_tuple_count = approved_tuple_count;
|
|
163028
|
+
idx_t temp_count = FilterSelection(temp_sel, result, *child_filter, temp_tuple_count, mask);
|
|
163029
|
+
// tuples passed, move them into the actual result vector
|
|
163030
|
+
for (idx_t i = 0; i < temp_count; i++) {
|
|
163031
|
+
auto new_idx = temp_sel.get_index(i);
|
|
163032
|
+
bool is_new_idx = true;
|
|
163033
|
+
for (idx_t res_idx = 0; res_idx < count_total; res_idx++) {
|
|
163034
|
+
if (result_sel.get_index(res_idx) == new_idx) {
|
|
163035
|
+
is_new_idx = false;
|
|
163036
|
+
break;
|
|
163037
|
+
}
|
|
163038
|
+
}
|
|
163039
|
+
if (is_new_idx) {
|
|
163040
|
+
result_sel.set_index(count_total++, new_idx);
|
|
163041
|
+
}
|
|
163042
|
+
}
|
|
163043
|
+
}
|
|
163044
|
+
sel.Initialize(result_sel);
|
|
163045
|
+
approved_tuple_count = count_total;
|
|
163046
|
+
return approved_tuple_count;
|
|
163047
|
+
}
|
|
162628
163048
|
case TableFilterType::CONJUNCTION_AND: {
|
|
162629
163049
|
auto &conjunction_and = (ConjunctionAndFilter &)filter;
|
|
162630
163050
|
for (auto &child_filter : conjunction_and.child_filters) {
|
|
162631
163051
|
FilterSelection(sel, result, *child_filter, approved_tuple_count, mask);
|
|
162632
163052
|
}
|
|
162633
|
-
|
|
163053
|
+
return approved_tuple_count;
|
|
162634
163054
|
}
|
|
162635
163055
|
case TableFilterType::CONSTANT_COMPARISON: {
|
|
162636
163056
|
auto &constant_filter = (ConstantFilter &)filter;
|
|
@@ -162743,14 +163163,12 @@ void ColumnSegment::FilterSelection(SelectionVector &sel, Vector &result, const
|
|
|
162743
163163
|
default:
|
|
162744
163164
|
throw InvalidTypeException(result.GetType(), "Invalid type for filter pushed down to table comparison");
|
|
162745
163165
|
}
|
|
162746
|
-
|
|
163166
|
+
return approved_tuple_count;
|
|
162747
163167
|
}
|
|
162748
163168
|
case TableFilterType::IS_NULL:
|
|
162749
|
-
TemplatedNullSelection<true>(sel, approved_tuple_count, mask);
|
|
162750
|
-
break;
|
|
163169
|
+
return TemplatedNullSelection<true>(sel, approved_tuple_count, mask);
|
|
162751
163170
|
case TableFilterType::IS_NOT_NULL:
|
|
162752
|
-
TemplatedNullSelection<false>(sel, approved_tuple_count, mask);
|
|
162753
|
-
break;
|
|
163171
|
+
return TemplatedNullSelection<false>(sel, approved_tuple_count, mask);
|
|
162754
163172
|
default:
|
|
162755
163173
|
throw InternalException("FIXME: unsupported type for filter selection");
|
|
162756
163174
|
}
|