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.*
+====