You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@impala.apache.org by st...@apache.org on 2023/03/27 23:01:56 UTC
[impala] 02/17: IMPALA-11707: Fix global runtime IN-list filter of numeric types are AlwaysFalse
This is an automated email from the ASF dual-hosted git repository.
stigahuang pushed a commit to branch branch-4.1.2
in repository https://gitbox.apache.org/repos/asf/impala.git
commit 4f284b0f15ef4db3abc24027fe3c09e6eaf870c3
Author: stiga-huang <hu...@gmail.com>
AuthorDate: Mon Nov 7 20:44:52 2022 +0800
IMPALA-11707: Fix global runtime IN-list filter of numeric types are AlwaysFalse
Global runtime filters are published to the coordinator and then
distributed to all executors that need it. The filter is serialized and
deserialized using protobuf. While deserializing a global runtime filter
of numeric type from protobuf, the InsertBatch() method forgot to update
the total_entries_ counter. The filter is then considered as an empty
list, which will reject any files/rows.
This patch adds the missing update of total_entries_. Some DCHECKs are
added to make sure total_entries_ is consistent with the actual size of
the value set. This patch also fixes a type error (long_val -> int_val)
in ToProtobuf() of Date type IN-list filter.
Tests:
- Added BE tests to verify the filter cloned from protobuf has the same
behavior as the original one.
- Added e2e regression tests
- Run TestInListFilters 200 times.
Change-Id: Ie90b2bce5e5ec6f6906ce9d2090b0ab19d48cc78
Reviewed-on: http://gerrit.cloudera.org:8080/19220
Tested-by: Impala Public Jenkins <im...@cloudera.com>
Reviewed-by: Qifan Chen <qf...@hotmail.com>
---
be/src/runtime/runtime-filter-bank.h | 20 +--
be/src/util/in-list-filter-ir.cc | 6 +-
be/src/util/in-list-filter-test.cc | 141 +++++++++++++++------
be/src/util/in-list-filter.cc | 37 +++---
be/src/util/in-list-filter.h | 25 +++-
.../queries/QueryTest/in_list_filters.test | 95 +++++++++++++-
6 files changed, 248 insertions(+), 76 deletions(-)
diff --git a/be/src/runtime/runtime-filter-bank.h b/be/src/runtime/runtime-filter-bank.h
index 7a66ec6c9..f97d43dd1 100644
--- a/be/src/runtime/runtime-filter-bank.h
+++ b/be/src/runtime/runtime-filter-bank.h
@@ -74,10 +74,11 @@ struct FilterRegistration {
///
/// All producers and consumers of filters must register via RegisterProducer() and
/// RegisterConsumer(). Local plan fragments update the filters by calling
-/// UpdateFilterFromLocal(), with either a bloom filter or a min-max filter, depending
-/// on the filter's type. The 'bloom_filter' or 'min_max_filter' that is passed into
-/// UpdateFilterFromLocal() must have been allocated by AllocateScratch*Filter(); this
-/// allows RuntimeFilterBank to manage all memory associated with filters.
+/// UpdateFilterFromLocal(), with either a bloom filter, a min-max filter, or an in-list
+/// filter, depending on the filter's type. The 'bloom_filter', 'min_max_filter' or
+/// 'in_list_filter' that is passed into UpdateFilterFromLocal() must have been allocated
+/// by AllocateScratch*Filter(); this allows RuntimeFilterBank to manage all memory
+/// associated with filters.
///
/// Filters are aggregated, first locally in this RuntimeFilterBank, if there are multiple
/// producers, and then made available to consumers after PublishGlobalFilter() has been
@@ -85,10 +86,11 @@ struct FilterRegistration {
/// of time so that RuntimeFilterBank knows when the filter is complete.
///
/// After PublishGlobalFilter() has been called (at most once per filter_id), the
-/// RuntimeFilter object associated with filter_id will have a valid bloom_filter or
-/// min_max_filter, and may be used for filter evaluation. This operation occurs
-/// without synchronisation, and neither the thread that calls PublishGlobalFilter()
-/// nor the thread that may call RuntimeFilter::Eval() need to coordinate in any way.
+/// RuntimeFilter object associated with filter_id will have a valid bloom_filter,
+/// min_max_filter or in_list_filter, and may be used for filter evaluation. This
+/// operation occurs without synchronisation, and neither the thread that calls
+/// PublishGlobalFilter() nor the thread that may call RuntimeFilter::Eval() need to
+/// coordinate in any way.
class RuntimeFilterBank {
public:
/// 'filters': contains an entry for every filter produced or consumed on this backend.
@@ -127,7 +129,7 @@ class RuntimeFilterBank {
void UpdateFilterFromLocal(int32_t filter_id, BloomFilter* bloom_filter,
MinMaxFilter* min_max_filter, InListFilter* in_list_filter);
- /// Makes a bloom_filter (aggregated globally from all producer fragments) available for
+ /// Makes a filter (aggregated globally from all producer fragments) available for
/// consumption by operators that wish to use it for filtering.
void PublishGlobalFilter(
const PublishFilterParamsPB& params, kudu::rpc::RpcContext* context);
diff --git a/be/src/util/in-list-filter-ir.cc b/be/src/util/in-list-filter-ir.cc
index 9e532b418..b166829e3 100644
--- a/be/src/util/in-list-filter-ir.cc
+++ b/be/src/util/in-list-filter-ir.cc
@@ -40,6 +40,7 @@ int32_t InListFilterImpl<int32_t, TYPE_DATE>::GetValue(const void* val) {
Reset(); \
} \
} \
+ DCHECK_EQ(total_entries_, values_.size()); \
} \
\
template<> \
@@ -72,14 +73,15 @@ StringValue InListFilterImpl<StringValue, TYPE_CHAR>::GetValue(const void* val,
} \
StringValue s = GetValue(val, type_len_); \
if (!values_.find(s)) { \
- bool res = newly_inserted_values_.insert(s); \
- if (res) { \
+ const auto& res = newly_inserted_values_.insert(s); \
+ if (res.second) { \
++total_entries_; \
uint32_t str_total_len = values_.total_len + newly_inserted_values_.total_len; \
if (UNLIKELY(total_entries_ > entry_limit_ \
|| str_total_len >= STRING_SET_MAX_TOTAL_LENGTH)) { \
Reset(); \
} \
+ DCHECK_EQ(total_entries_, values_.size() + newly_inserted_values_.size()); \
} \
} \
} \
diff --git a/be/src/util/in-list-filter-test.cc b/be/src/util/in-list-filter-test.cc
index 9db92486a..e274c16d1 100644
--- a/be/src/util/in-list-filter-test.cc
+++ b/be/src/util/in-list-filter-test.cc
@@ -23,6 +23,32 @@
using namespace impala;
+template<typename T>
+void VerifyItems(InListFilter* f, ColumnType col_type, T min_value, T max_value,
+ bool contains_null) {
+ int num_items = max_value - min_value + 1;
+ if (contains_null) ++num_items;
+ EXPECT_EQ(num_items, f->NumItems());
+ EXPECT_EQ(contains_null, f->ContainsNull());
+ for (T v = min_value; v <= max_value; ++v) {
+ EXPECT_TRUE(f->Find(&v, col_type)) << v << " not found in " << f->DebugString();
+ }
+ T i = min_value - 1;
+ EXPECT_FALSE(f->Find(&i, col_type));
+ i = max_value + 1;
+ EXPECT_FALSE(f->Find(&i, col_type));
+
+ EXPECT_FALSE(f->AlwaysFalse());
+ EXPECT_FALSE(f->AlwaysTrue());
+}
+
+InListFilter* CloneFromProtobuf(InListFilter* filter, ColumnType col_type,
+ uint32_t entry_limit, ObjectPool* pool, MemTracker* mem_tracker) {
+ InListFilterPB pb;
+ InListFilter::ToProtobuf(filter, &pb);
+ return InListFilter::Create(pb, col_type, entry_limit, pool, mem_tracker);
+}
+
template<typename T, PrimitiveType SLOT_TYPE>
void TestNumericInListFilter() {
MemTracker mem_tracker;
@@ -32,35 +58,40 @@ void TestNumericInListFilter() {
EXPECT_TRUE(filter->AlwaysFalse());
EXPECT_FALSE(filter->AlwaysTrue());
+ // Insert 20 values
for (T v = -10; v < 10; ++v) {
filter->Insert(&v);
}
- // Insert duplicated values again
+ // Insert some duplicated values again
for (T v = 9; v >= 0; --v) {
filter->Insert(&v);
}
- EXPECT_EQ(20, filter->NumItems());
- EXPECT_FALSE(filter->ContainsNull());
- filter->Insert(nullptr);
- EXPECT_TRUE(filter->ContainsNull());
- EXPECT_EQ(21, filter->NumItems());
+ VerifyItems<T>(filter, col_type, -10, 9, false);
- for (T v = -10; v < 10; ++v) {
- EXPECT_TRUE(filter->Find(&v, col_type));
- }
- T i = -11;
- EXPECT_FALSE(filter->Find(&i, col_type));
- i = 10;
- EXPECT_FALSE(filter->Find(&i, col_type));
+ // Copy the filter through protobuf for testing InsertBatch()
+ InListFilter* filter2 = CloneFromProtobuf(filter, col_type, 20, &obj_pool,
+ &mem_tracker);
+ VerifyItems<T>(filter2, col_type, -10, 9, false);
- EXPECT_FALSE(filter->AlwaysFalse());
- EXPECT_FALSE(filter->AlwaysTrue());
+ // Insert NULL
+ filter->Insert(nullptr);
+ VerifyItems<T>(filter, col_type, -10, 9, true);
+
+ filter2 = CloneFromProtobuf(filter, col_type, 20, &obj_pool, &mem_tracker);
+ VerifyItems<T>(filter2, col_type, -10, 9, true);
// Test falling back to an always_true filter when #items exceeds the limit
- filter->Insert(&i);
- EXPECT_FALSE(filter->AlwaysFalse());
- EXPECT_TRUE(filter->AlwaysTrue());
- EXPECT_EQ(0, filter->NumItems());
+ T value = 10;
+ filter->Insert(&value);
+ filter2 = CloneFromProtobuf(filter, col_type, 20, &obj_pool, &mem_tracker);
+
+ int i = 0;
+ for (InListFilter* f : {filter, filter2}) {
+ EXPECT_FALSE(f->AlwaysFalse());
+ EXPECT_TRUE(f->AlwaysTrue());
+ EXPECT_EQ(0, f->NumItems()) << "Error in filter " << i;
+ ++i;
+ }
}
TEST(InListFilterTest, TestTinyint) {
@@ -101,15 +132,23 @@ TEST(InListFilterTest, TestDate) {
}
EXPECT_EQ(5, filter->NumItems());
EXPECT_FALSE(filter->ContainsNull());
+ InListFilter* filter2 = CloneFromProtobuf(filter, col_type, 20, &obj_pool,
+ &mem_tracker);
+ EXPECT_EQ(5, filter2->NumItems());
+ EXPECT_FALSE(filter2->ContainsNull());
+
filter->Insert(nullptr);
- EXPECT_TRUE(filter->ContainsNull());
- EXPECT_EQ(6, filter->NumItems());
+ filter2 = CloneFromProtobuf(filter, col_type, 20, &obj_pool, &mem_tracker);
- for (const auto& v : values) {
- EXPECT_TRUE(filter->Find(&v, col_type));
+ for (InListFilter* f : {filter, filter2}) {
+ EXPECT_TRUE(f->ContainsNull());
+ EXPECT_EQ(6, f->NumItems());
+ for (const auto& v : values) {
+ EXPECT_TRUE(f->Find(&v, col_type));
+ }
+ DateValue d(60000);
+ EXPECT_FALSE(f->Find(&d, col_type));
}
- DateValue d(60000);
- EXPECT_FALSE(filter->Find(&d, col_type));
}
void TestStringInListFilter(const ColumnType& col_type) {
@@ -135,21 +174,30 @@ void TestStringInListFilter(const ColumnType& col_type) {
filter->Insert(&s);
}
filter->MaterializeValues();
-
EXPECT_EQ(5, filter->NumItems());
EXPECT_FALSE(filter->ContainsNull());
+
+ InListFilter* filter2 = CloneFromProtobuf(filter, col_type, 20, &obj_pool,
+ &mem_tracker);
+ EXPECT_EQ(5, filter2->NumItems());
+ EXPECT_FALSE(filter2->ContainsNull());
+ filter2->Close();
+
filter->Insert(nullptr);
- EXPECT_TRUE(filter->ContainsNull());
- EXPECT_EQ(6, filter->NumItems());
+ filter2 = CloneFromProtobuf(filter, col_type, 20, &obj_pool, &mem_tracker);
// Merge ss2 to ss1
ss1.insert(ss1.end(), ss2.begin(), ss2.end());
- for (const StringValue& s : ss1) {
- EXPECT_TRUE(filter->Find(&s, col_type));
+ for (InListFilter* f : {filter, filter2}) {
+ EXPECT_TRUE(f->ContainsNull());
+ EXPECT_EQ(6, f->NumItems());
+ for (const StringValue& s : ss1) {
+ EXPECT_TRUE(f->Find(&s, col_type));
+ }
+ StringValue d("d");
+ EXPECT_FALSE(f->Find(&d, col_type));
+ f->Close();
}
- StringValue d("d");
- EXPECT_FALSE(filter->Find(&d, col_type));
- filter->Close();
}
TEST(InListFilterTest, TestString) {
@@ -188,16 +236,25 @@ TEST(InListFilterTest, TestChar) {
EXPECT_EQ(5, filter->NumItems());
EXPECT_FALSE(filter->ContainsNull());
+ InListFilter* filter2 = CloneFromProtobuf(filter, col_type, 20, &obj_pool,
+ &mem_tracker);
+ EXPECT_EQ(5, filter2->NumItems());
+ EXPECT_FALSE(filter2->ContainsNull());
+ filter2->Close();
+
filter->Insert(nullptr);
- EXPECT_TRUE(filter->ContainsNull());
- EXPECT_EQ(6, filter->NumItems());
+ filter2 = CloneFromProtobuf(filter, col_type, 20, &obj_pool, &mem_tracker);
+ for (InListFilter* f : {filter, filter2}) {
+ EXPECT_TRUE(f->ContainsNull());
+ EXPECT_EQ(6, f->NumItems());
- ptr = str_buffer;
- for (int i = 0; i < 5; ++i) {
- EXPECT_TRUE(filter->Find(ptr, col_type));
- ptr += 2;
+ ptr = str_buffer;
+ for (int i = 0; i < 5; ++i) {
+ EXPECT_TRUE(f->Find(ptr, col_type));
+ ptr += 2;
+ }
+ ptr = "gg";
+ EXPECT_FALSE(f->Find(ptr, col_type));
+ f->Close();
}
- ptr = "gg";
- EXPECT_FALSE(filter->Find(ptr, col_type));
- filter->Close();
}
\ No newline at end of file
diff --git a/be/src/util/in-list-filter.cc b/be/src/util/in-list-filter.cc
index 8749deda2..1c1a914a0 100644
--- a/be/src/util/in-list-filter.cc
+++ b/be/src/util/in-list-filter.cc
@@ -132,10 +132,7 @@ void InListFilterImpl<StringValue, SLOT_TYPE>::MaterializeValues() {
VLOG_QUERY << "Not enough memory in materializing string IN-list filters. "
<< "Fallback to always true. New string batch size: "
<< newly_inserted_values_.total_len << "\n" << mem_pool_.DebugString();
- always_true_ = true;
- values_.clear();
- newly_inserted_values_.clear();
- total_entries_ = 0;
+ Reset();
return;
}
// Transfer values to the finial set. Don't need to update total_entries_ since it's
@@ -148,23 +145,31 @@ void InListFilterImpl<StringValue, SLOT_TYPE>::MaterializeValues() {
newly_inserted_values_.clear();
}
-#define IN_LIST_FILTER_INSERT_BATCH(TYPE, SLOT_TYPE, PB_VAL_METHOD) \
+#define IN_LIST_FILTER_INSERT_BATCH(TYPE, SLOT_TYPE, PB_VAL_METHOD, SET_VAR) \
template<> \
void InListFilterImpl<TYPE, SLOT_TYPE>::InsertBatch(const ColumnValueBatchPB& batch) { \
for (const ColumnValuePB& v : batch) { \
- DCHECK(v.has_##PB_VAL_METHOD()); \
- values_.insert(v.PB_VAL_METHOD()); \
+ DCHECK(v.has_##PB_VAL_METHOD()) << v.ShortDebugString(); \
+ const auto& res = SET_VAR.insert(v.PB_VAL_METHOD()); \
+ if (res.second) { \
+ ++total_entries_; \
+ if (UNLIKELY(total_entries_ > entry_limit_)) { \
+ Reset(); \
+ break; \
+ } \
+ } \
} \
+ DCHECK_EQ(total_entries_, SET_VAR.size()); \
}
-IN_LIST_FILTER_INSERT_BATCH(int8_t, TYPE_TINYINT, byte_val)
-IN_LIST_FILTER_INSERT_BATCH(int16_t, TYPE_SMALLINT, short_val)
-IN_LIST_FILTER_INSERT_BATCH(int32_t, TYPE_INT, int_val)
-IN_LIST_FILTER_INSERT_BATCH(int64_t, TYPE_BIGINT, long_val)
-IN_LIST_FILTER_INSERT_BATCH(int32_t, TYPE_DATE, int_val)
-IN_LIST_FILTER_INSERT_BATCH(StringValue, TYPE_STRING, string_val)
-IN_LIST_FILTER_INSERT_BATCH(StringValue, TYPE_VARCHAR, string_val)
-IN_LIST_FILTER_INSERT_BATCH(StringValue, TYPE_CHAR, string_val)
+IN_LIST_FILTER_INSERT_BATCH(int8_t, TYPE_TINYINT, byte_val, values_)
+IN_LIST_FILTER_INSERT_BATCH(int16_t, TYPE_SMALLINT, short_val, values_)
+IN_LIST_FILTER_INSERT_BATCH(int32_t, TYPE_INT, int_val, values_)
+IN_LIST_FILTER_INSERT_BATCH(int64_t, TYPE_BIGINT, long_val, values_)
+IN_LIST_FILTER_INSERT_BATCH(int32_t, TYPE_DATE, int_val, values_)
+IN_LIST_FILTER_INSERT_BATCH(StringValue, TYPE_STRING, string_val, newly_inserted_values_)
+IN_LIST_FILTER_INSERT_BATCH(StringValue, TYPE_VARCHAR, string_val, newly_inserted_values_)
+IN_LIST_FILTER_INSERT_BATCH(StringValue, TYPE_CHAR, string_val, newly_inserted_values_)
#define NUMERIC_IN_LIST_FILTER_TO_PROTOBUF(TYPE, SLOT_TYPE, PB_VAL_METHOD) \
template<> \
@@ -182,7 +187,7 @@ NUMERIC_IN_LIST_FILTER_TO_PROTOBUF(int8_t, TYPE_TINYINT, byte_val)
NUMERIC_IN_LIST_FILTER_TO_PROTOBUF(int16_t, TYPE_SMALLINT, short_val)
NUMERIC_IN_LIST_FILTER_TO_PROTOBUF(int32_t, TYPE_INT, int_val)
NUMERIC_IN_LIST_FILTER_TO_PROTOBUF(int64_t, TYPE_BIGINT, long_val)
-NUMERIC_IN_LIST_FILTER_TO_PROTOBUF(int32_t, TYPE_DATE, long_val)
+NUMERIC_IN_LIST_FILTER_TO_PROTOBUF(int32_t, TYPE_DATE, int_val)
#define STRING_IN_LIST_FILTER_TO_PROTOBUF(SLOT_TYPE) \
template<> \
diff --git a/be/src/util/in-list-filter.h b/be/src/util/in-list-filter.h
index cb2e7c6b2..c2e36cab9 100644
--- a/be/src/util/in-list-filter.h
+++ b/be/src/util/in-list-filter.h
@@ -136,20 +136,29 @@ class InListFilterImpl : public InListFilter {
std::unordered_set<T> values_;
};
+/// String set that wraps a boost::unordered_set<StringValue> and tracks the total length
+/// of strings in the set. Exposes the same methods of boost::unordered_set that are used
+/// in InListFilters.
struct StringSetWithTotalLen {
boost::unordered_set<StringValue> values;
uint32_t total_len = 0;
- inline bool insert(StringValue v) {
- const auto& res = values.insert(v);
+ typedef typename boost::unordered_set<StringValue>::iterator iterator;
+
+ /// Inserts a new StringValue. Returns a pair consisting of an iterator to the element
+ /// in the set, and a bool denoting whether the insertion took place (true if insertion
+ /// happened, false if it did not, i.e. already exists).
+ inline pair<iterator, bool> insert(StringValue v) {
+ const auto& res = values.emplace(v);
total_len += (res.second? v.len : 0);
- return res.second;
+ return res;
}
- inline bool insert(const string& s) {
+ /// Same as the above one but inserts a value of std::string
+ inline pair<iterator, bool> insert(const string& s) {
const auto& res = values.emplace(s);
total_len += (res.second ? s.length() : 0);
- return res.second;
+ return res;
}
inline bool find(StringValue v) const {
@@ -160,6 +169,10 @@ struct StringSetWithTotalLen {
values.clear();
total_len = 0;
}
+
+ inline size_t size() const {
+ return values.size();
+ }
};
template<PrimitiveType SLOT_TYPE>
@@ -196,7 +209,7 @@ class InListFilterImpl<StringValue, SLOT_TYPE> : public InListFilter {
MemPool mem_pool_;
StringSetWithTotalLen values_;
/// Temp set used to insert new values. They will be transferred to values_ in
- /// MaterializeValues().
+ /// MaterializeValues(). Values should always be inserted into this set first.
StringSetWithTotalLen newly_inserted_values_;
/// Type len for CHAR type.
int type_len_;
diff --git a/testdata/workloads/functional-query/queries/QueryTest/in_list_filters.test b/testdata/workloads/functional-query/queries/QueryTest/in_list_filters.test
index cfe71f19a..919c2d850 100644
--- a/testdata/workloads/functional-query/queries/QueryTest/in_list_filters.test
+++ b/testdata/workloads/functional-query/queries/QueryTest/in_list_filters.test
@@ -170,4 +170,97 @@ select STRAIGHT_JOIN count(*) from alltypes a
row_regex: .*Filter 0 arrival with 0 items.*
row_regex: .*RowsRead: 2.43K \(2433\).*
====
-
+---- QUERY
+# IMPALA-11707: Regression test on global IN-list filter
+# Final filter table shown below. Filter 0 is the global filter
+# generated by the build side of scanning b, and is applied to
+# the scan node that scans a.
+#
+# ID Src. Node Tgt. Node(s) Target type Partition filter Pending (Expected) First arrived Completed Enabled Bloom Size Est fpp Min value Max value In-list size
+#------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+# 1 3 0 LOCAL false 0 (3) N/A N/A true IN_LIST PartialUpdates
+# 0 4 1 REMOTE false 0 (3) 431.952ms 431.953ms true IN_LIST 1
+select count(*) from alltypes t, alltypestiny a, alltypestiny b
+where t.id = a.id and a.tinyint_col = b.tinyint_col and b.id = 0;
+---- RESULTS
+4
+---- RUNTIME_PROFILE
+row_regex: .*0\s+4\s+1\s+REMOTE\s+false.*IN_LIST\s+1
+row_regex: .*Filter 0 arrival with 1 items.*
+====
+---- QUERY
+# IMPALA-11707: Regression test on global IN-list filter
+select count(*) from alltypes t, alltypestiny a, alltypestiny b
+where t.id = a.id and a.smallint_col = b.smallint_col and b.id = 0;
+---- RESULTS
+4
+---- RUNTIME_PROFILE
+row_regex: .*0\s+4\s+1\s+REMOTE\s+false.*IN_LIST\s+1
+row_regex: .*Filter 0 arrival with 1 items.*
+====
+---- QUERY
+# IMPALA-11707: Regression test on global IN-list filter
+select count(*) from alltypes t, alltypestiny a, alltypestiny b
+where t.id = a.id and a.int_col = b.int_col and b.id = 0;
+---- RESULTS
+4
+---- RUNTIME_PROFILE
+row_regex: .*0\s+4\s+1\s+REMOTE\s+false.*IN_LIST\s+1
+row_regex: .*Filter 0 arrival with 1 items.*
+====
+---- QUERY
+# IMPALA-11707: Regression test on global IN-list filter
+select count(*) from alltypes t, alltypestiny a, alltypestiny b
+where t.id = a.id and a.bigint_col = b.bigint_col and b.id = 0;
+---- RESULTS
+4
+---- RUNTIME_PROFILE
+row_regex: .*0\s+4\s+1\s+REMOTE\s+false.*IN_LIST\s+1
+row_regex: .*Filter 0 arrival with 1 items.*
+====
+---- QUERY
+# IMPALA-11707: Regression test on global IN-list filter
+select count(*) from alltypes t, alltypestiny a, alltypestiny b
+where t.id = a.id and a.string_col = b.string_col and b.id = 0;
+---- RESULTS
+4
+---- RUNTIME_PROFILE
+row_regex: .*0\s+4\s+1\s+REMOTE\s+false.*IN_LIST\s+1
+row_regex: .*Filter 0 arrival with 1 items.*
+====
+---- QUERY
+# IMPALA-11707: Regression test on global IN-list filter on DATE type
+# Final filter table:
+# ID Src. Node Tgt. Node(s) Target type Partition filter Pending (Expected) First arrived Completed Enabled Bloom Size Est fpp Min value Max value In-list size
+#------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+# 1 3 0 LOCAL false 0 (3) N/A N/A true IN_LIST PartialUpdates
+# 0 4 1 REMOTE false 0 (3) 427.938ms 427.947ms true IN_LIST 5
+select STRAIGHT_JOIN count(*)
+from date_tbl t
+join [BROADCAST] date_tbl a on t.id_col = a.id_col
+join [BROADCAST] date_tbl b on a.date_col = b.date_col
+where b.id_col < 5;
+---- RESULTS
+7
+---- RUNTIME_PROFILE
+row_regex: .*0\s+4\s+1\s+REMOTE\s+false.*IN_LIST\s+5
+row_regex: .*Filter 0 arrival with 5 items.*
+====
+---- QUERY
+# IMPALA-11707: Regression test on global IN-list filter
+# Final filter table:
+# ID Src. Node Tgt. Node(s) Target type Partition filter Pending (Expected) First arrived Completed Enabled Bloom Size Est fpp Min value Max value In-list size
+#------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+# 1 3 0 LOCAL false 0 (1) N/A N/A true IN_LIST PartialUpdates
+# 0 4 1 REMOTE false 0 (1) 87.270ms 87.271ms true IN_LIST 1
+select count(*)
+from tpch_orc_def.supplier, tpch_orc_def.nation, tpch_orc_def.region
+where s_nationkey = n_nationkey
+ and n_regionkey = r_regionkey
+ and r_name = 'EUROPE';
+---- RESULTS
+1987
+---- RUNTIME_PROFILE
+row_regex: .*0\s+4\s+1\s+REMOTE\s+false.*IN_LIST\s+1
+row_regex: .*Filter 0 arrival with 1 items.*
+====