This is an automated email from the ASF dual-hosted git repository.

cwylie pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/druid.git


The following commit(s) were added to refs/heads/master by this push:
     new df5bcd13670 fix bugs with expression virtual column indexes for 
expression virtual columns which refer to other virtual columns (#15633)
df5bcd13670 is described below

commit df5bcd1367054e3e7e449df65339d334b26d3094
Author: Clint Wylie <[email protected]>
AuthorDate: Mon Jan 8 13:10:11 2024 -0800

    fix bugs with expression virtual column indexes for expression virtual 
columns which refer to other virtual columns (#15633)
    
    changes:
    * ColumnIndexSelector now extends ColumnSelector. The only real 
implementation of ColumnIndexSelector, ColumnSelectorColumnIndexSelector, 
already has a ColumnSelector, so this isn't very disruptive
    * removed getColumnNames from ColumnSelector since it was not used
    * VirtualColumns and VirtualColumn getIndexSupplier method now needs 
argument of ColumnIndexSelector instead of ColumnSelector, which allows 
expression virtual columns to correctly recognize other virtual columns, fixing 
an issue which would incorrectly handle other virtual columns as non-existent 
columns instead
    * fixed a bug with sql planner incorrectly not using expression filter for 
equality filters on columns with extractionFn and no virtual column registry
---
 .../druid/benchmark/MockColumnIndexSelector.java   |  8 +++
 .../main/java/org/apache/druid/math/expr/Expr.java | 17 ++---
 .../org/apache/druid/math/expr/IdentifierExpr.java | 15 ++---
 .../druid/query/filter/ColumnIndexSelector.java    |  4 +-
 .../java/org/apache/druid/segment/ColumnCache.java |  7 --
 .../org/apache/druid/segment/ColumnSelector.java   |  7 --
 .../segment/ColumnSelectorColumnIndexSelector.java | 16 ++++-
 .../DeprecatedQueryableIndexColumnSelector.java    |  7 --
 .../org/apache/druid/segment/VirtualColumn.java    | 12 ++--
 .../org/apache/druid/segment/VirtualColumns.java   |  8 ++-
 .../segment/virtual/ExpressionVirtualColumn.java   |  9 ++-
 .../segment/virtual/FallbackVirtualColumn.java     |  9 ++-
 .../segment/virtual/ListFilteredVirtualColumn.java | 17 ++---
 .../segment/virtual/NestedFieldVirtualColumn.java  |  5 +-
 .../apache/druid/segment/TestColumnSelector.java   |  8 ---
 .../druid/segment/filter/BaseFilterTest.java       |  3 +
 .../druid/segment/filter/EqualityFilterTests.java  | 43 ++++++++++++
 .../segment/filter/ExtractionDimFilterTest.java    |  8 +++
 .../druid/segment/filter/NullFilterTests.java      | 27 ++++++++
 .../segment/virtual/DummyStringVirtualColumn.java  | 10 +--
 .../segment/virtual/FallbackVirtualColumnTest.java | 16 +++--
 .../druid/sql/calcite/expression/Expressions.java  | 15 ++++-
 .../druid/sql/calcite/CalciteArraysQueryTest.java  | 77 ++++++++++++++++++++++
 23 files changed, 259 insertions(+), 89 deletions(-)

diff --git 
a/benchmarks/src/test/java/org/apache/druid/benchmark/MockColumnIndexSelector.java
 
b/benchmarks/src/test/java/org/apache/druid/benchmark/MockColumnIndexSelector.java
index 9e6bdc57eef..33b824649bf 100644
--- 
a/benchmarks/src/test/java/org/apache/druid/benchmark/MockColumnIndexSelector.java
+++ 
b/benchmarks/src/test/java/org/apache/druid/benchmark/MockColumnIndexSelector.java
@@ -22,6 +22,7 @@ package org.apache.druid.benchmark;
 import org.apache.druid.collections.bitmap.BitmapFactory;
 import org.apache.druid.query.filter.ColumnIndexSelector;
 import org.apache.druid.segment.column.ColumnCapabilities;
+import org.apache.druid.segment.column.ColumnHolder;
 import org.apache.druid.segment.column.ColumnIndexSupplier;
 
 import javax.annotation.Nullable;
@@ -58,6 +59,13 @@ public class MockColumnIndexSelector implements 
ColumnIndexSelector
     return indexSupplier;
   }
 
+  @Nullable
+  @Override
+  public ColumnHolder getColumnHolder(String columnName)
+  {
+    return null;
+  }
+
   @Nullable
   @Override
   public ColumnCapabilities getColumnCapabilities(String column)
diff --git a/processing/src/main/java/org/apache/druid/math/expr/Expr.java 
b/processing/src/main/java/org/apache/druid/math/expr/Expr.java
index 3511abb758a..9471bb24eb5 100644
--- a/processing/src/main/java/org/apache/druid/math/expr/Expr.java
+++ b/processing/src/main/java/org/apache/druid/math/expr/Expr.java
@@ -28,9 +28,8 @@ import org.apache.druid.java.util.common.Cacheable;
 import org.apache.druid.java.util.common.ISE;
 import org.apache.druid.math.expr.vector.ExprVectorProcessor;
 import org.apache.druid.query.cache.CacheKeyBuilder;
-import org.apache.druid.segment.ColumnSelector;
+import org.apache.druid.query.filter.ColumnIndexSelector;
 import org.apache.druid.segment.column.ColumnCapabilities;
-import org.apache.druid.segment.column.ColumnHolder;
 import org.apache.druid.segment.column.ColumnIndexSupplier;
 import org.apache.druid.segment.column.ColumnType;
 import org.apache.druid.segment.index.semantic.DictionaryEncodedValueIndex;
@@ -194,7 +193,10 @@ public interface Expr extends Cacheable
   }
 
   @Nullable
-  default ColumnIndexSupplier asColumnIndexSupplier(ColumnSelector 
columnSelector, @Nullable ColumnType outputType)
+  default ColumnIndexSupplier asColumnIndexSupplier(
+      ColumnIndexSelector columnIndexSelector,
+      @Nullable ColumnType outputType
+  )
   {
     final Expr.BindingAnalysis details = analyzeInputs();
     if (details.getRequiredBindings().size() == 1) {
@@ -202,13 +204,7 @@ public interface Expr extends Cacheable
       // map over the values of the index.
       final String column = 
Iterables.getOnlyElement(details.getRequiredBindings());
 
-      final ColumnHolder holder = columnSelector.getColumnHolder(column);
-      if (holder == null) {
-        // column doesn't exist, no index supplier
-        return null;
-      }
-      final ColumnCapabilities capabilities = holder.getCapabilities();
-      final ColumnIndexSupplier delegateIndexSupplier = 
holder.getIndexSupplier();
+      final ColumnIndexSupplier delegateIndexSupplier = 
columnIndexSelector.getIndexSupplier(column);
       if (delegateIndexSupplier == null) {
         return null;
       }
@@ -216,6 +212,7 @@ public interface Expr extends Cacheable
           DictionaryEncodedValueIndex.class
       );
 
+      final ColumnCapabilities capabilities = 
columnIndexSelector.getColumnCapabilities(column);
       final ExpressionType inputType = 
ExpressionType.fromColumnTypeStrict(capabilities);
       final ColumnType outType;
       if (outputType == null) {
diff --git 
a/processing/src/main/java/org/apache/druid/math/expr/IdentifierExpr.java 
b/processing/src/main/java/org/apache/druid/math/expr/IdentifierExpr.java
index 9a16acbe92b..ef222cda61f 100644
--- a/processing/src/main/java/org/apache/druid/math/expr/IdentifierExpr.java
+++ b/processing/src/main/java/org/apache/druid/math/expr/IdentifierExpr.java
@@ -23,8 +23,7 @@ import org.apache.commons.lang.StringEscapeUtils;
 import org.apache.druid.java.util.common.StringUtils;
 import org.apache.druid.math.expr.vector.ExprVectorProcessor;
 import org.apache.druid.math.expr.vector.VectorProcessors;
-import org.apache.druid.segment.ColumnSelector;
-import org.apache.druid.segment.column.ColumnHolder;
+import org.apache.druid.query.filter.ColumnIndexSelector;
 import org.apache.druid.segment.column.ColumnIndexSupplier;
 import org.apache.druid.segment.column.ColumnType;
 
@@ -158,15 +157,13 @@ class IdentifierExpr implements Expr
 
   @Nullable
   @Override
-  public ColumnIndexSupplier asColumnIndexSupplier(ColumnSelector 
columnSelector, @Nullable ColumnType outputType)
+  public ColumnIndexSupplier asColumnIndexSupplier(
+      ColumnIndexSelector indexSelector,
+      @Nullable ColumnType outputType
+  )
   {
     // identifier just wraps a column, we can return its index supplier 
directly if the column exists
-    final ColumnHolder holder = columnSelector.getColumnHolder(binding);
-    if (holder == null) {
-      // column doesn't exist, no index supplier
-      return null;
-    }
-    return holder.getIndexSupplier();
+    return indexSelector.getIndexSupplier(binding);
   }
 
   @Override
diff --git 
a/processing/src/main/java/org/apache/druid/query/filter/ColumnIndexSelector.java
 
b/processing/src/main/java/org/apache/druid/query/filter/ColumnIndexSelector.java
index c0eb6a4ac49..2a9fea71870 100644
--- 
a/processing/src/main/java/org/apache/druid/query/filter/ColumnIndexSelector.java
+++ 
b/processing/src/main/java/org/apache/druid/query/filter/ColumnIndexSelector.java
@@ -20,14 +20,14 @@
 package org.apache.druid.query.filter;
 
 import org.apache.druid.collections.bitmap.BitmapFactory;
-import org.apache.druid.segment.ColumnInspector;
+import org.apache.druid.segment.ColumnSelector;
 import org.apache.druid.segment.column.ColumnIndexSupplier;
 
 import javax.annotation.Nullable;
 
 /**
  */
-public interface ColumnIndexSelector extends ColumnInspector
+public interface ColumnIndexSelector extends ColumnSelector
 {
   int getNumRows();
 
diff --git a/processing/src/main/java/org/apache/druid/segment/ColumnCache.java 
b/processing/src/main/java/org/apache/druid/segment/ColumnCache.java
index fb285fa282a..8ec7ced4ae7 100644
--- a/processing/src/main/java/org/apache/druid/segment/ColumnCache.java
+++ b/processing/src/main/java/org/apache/druid/segment/ColumnCache.java
@@ -28,7 +28,6 @@ import 
org.apache.druid.segment.selector.settable.SettableColumnValueSelector;
 
 import javax.annotation.Nullable;
 import java.util.HashMap;
-import java.util.List;
 
 public class ColumnCache implements ColumnSelector
 {
@@ -45,12 +44,6 @@ public class ColumnCache implements ColumnSelector
   }
 
 
-  @Override
-  public List<String> getColumnNames()
-  {
-    return index.getColumnNames();
-  }
-
   @Nullable
   @Override
   public ColumnHolder getColumnHolder(String columnName)
diff --git 
a/processing/src/main/java/org/apache/druid/segment/ColumnSelector.java 
b/processing/src/main/java/org/apache/druid/segment/ColumnSelector.java
index 32a3e95fb40..49319a6713a 100644
--- a/processing/src/main/java/org/apache/druid/segment/ColumnSelector.java
+++ b/processing/src/main/java/org/apache/druid/segment/ColumnSelector.java
@@ -23,18 +23,11 @@ import org.apache.druid.segment.column.ColumnCapabilities;
 import org.apache.druid.segment.column.ColumnHolder;
 
 import javax.annotation.Nullable;
-import java.util.List;
 
 /**
  */
 public interface ColumnSelector extends ColumnInspector
 {
-  /**
-   * This method is apparently no longer used anymore, so deprecating it.
-   */
-  @Deprecated
-  List<String> getColumnNames();
-
   @Nullable
   ColumnHolder getColumnHolder(String columnName);
 
diff --git 
a/processing/src/main/java/org/apache/druid/segment/ColumnSelectorColumnIndexSelector.java
 
b/processing/src/main/java/org/apache/druid/segment/ColumnSelectorColumnIndexSelector.java
index 184138b0ea8..b509d8d1a8b 100644
--- 
a/processing/src/main/java/org/apache/druid/segment/ColumnSelectorColumnIndexSelector.java
+++ 
b/processing/src/main/java/org/apache/druid/segment/ColumnSelectorColumnIndexSelector.java
@@ -77,7 +77,7 @@ public class ColumnSelectorColumnIndexSelector implements 
ColumnIndexSelector
   {
     final ColumnIndexSupplier indexSupplier;
     if (isVirtualColumn(column)) {
-      indexSupplier = virtualColumns.getIndexSupplier(column, columnSelector);
+      indexSupplier = virtualColumns.getIndexSupplier(column, this);
     } else {
       final ColumnHolder columnHolder = columnSelector.getColumnHolder(column);
       // for missing columns we return null here. This allows callers to 
fabricate an 'all true' or 'all false'
@@ -90,9 +90,14 @@ public class ColumnSelectorColumnIndexSelector implements 
ColumnIndexSelector
     return indexSupplier;
   }
 
-  private boolean isVirtualColumn(final String columnName)
+  @Nullable
+  @Override
+  public ColumnHolder getColumnHolder(String columnName)
   {
-    return virtualColumns.getVirtualColumn(columnName) != null;
+    if (isVirtualColumn(columnName)) {
+      return null;
+    }
+    return columnSelector.getColumnHolder(columnName);
   }
 
   @Nullable
@@ -101,4 +106,9 @@ public class ColumnSelectorColumnIndexSelector implements 
ColumnIndexSelector
   {
     return virtualColumns.getColumnCapabilitiesWithFallback(columnSelector, 
column);
   }
+
+  private boolean isVirtualColumn(final String columnName)
+  {
+    return virtualColumns.getVirtualColumn(columnName) != null;
+  }
 }
diff --git 
a/processing/src/main/java/org/apache/druid/segment/DeprecatedQueryableIndexColumnSelector.java
 
b/processing/src/main/java/org/apache/druid/segment/DeprecatedQueryableIndexColumnSelector.java
index 0eb0180ea0c..73863eef27c 100644
--- 
a/processing/src/main/java/org/apache/druid/segment/DeprecatedQueryableIndexColumnSelector.java
+++ 
b/processing/src/main/java/org/apache/druid/segment/DeprecatedQueryableIndexColumnSelector.java
@@ -22,7 +22,6 @@ package org.apache.druid.segment;
 import org.apache.druid.segment.column.ColumnHolder;
 
 import javax.annotation.Nullable;
-import java.util.List;
 
 /**
  * It likely looks weird that we are creating a new instance of ColumnSelector 
here that begins its life deprecated
@@ -51,12 +50,6 @@ public class DeprecatedQueryableIndexColumnSelector 
implements ColumnSelector
     this.index = index;
   }
 
-  @Override
-  public List<String> getColumnNames()
-  {
-    return index.getColumnNames();
-  }
-
   @Nullable
   @Override
   public ColumnHolder getColumnHolder(String columnName)
diff --git 
a/processing/src/main/java/org/apache/druid/segment/VirtualColumn.java 
b/processing/src/main/java/org/apache/druid/segment/VirtualColumn.java
index e79baa00b1b..3698a8a731b 100644
--- a/processing/src/main/java/org/apache/druid/segment/VirtualColumn.java
+++ b/processing/src/main/java/org/apache/druid/segment/VirtualColumn.java
@@ -23,6 +23,7 @@ import com.fasterxml.jackson.annotation.JsonSubTypes;
 import com.fasterxml.jackson.annotation.JsonTypeInfo;
 import org.apache.druid.java.util.common.Cacheable;
 import org.apache.druid.query.dimension.DimensionSpec;
+import org.apache.druid.query.filter.ColumnIndexSelector;
 import org.apache.druid.segment.column.ColumnCapabilities;
 import org.apache.druid.segment.column.ColumnIndexSupplier;
 import org.apache.druid.segment.data.ReadableOffset;
@@ -299,13 +300,16 @@ public interface VirtualColumn extends Cacheable
 
   /**
    * Get the {@link ColumnIndexSupplier} for the specified virtual column, 
with the assistance of a
-   * {@link ColumnSelector} to allow reading things from segments. If the 
virtual column has no indexes, this method
-   * will return null, or may also return a non-null supplier whose methods 
may return null values - having a supplier
-   * is no guarantee that the column has indexes.
+   * {@link ColumnIndexSelector} to allow reading things from segments. If the 
virtual column has no indexes, this
+   * method will return null, or may also return a non-null supplier whose 
methods may return null values - having a
+   * supplier is no guarantee that the column has indexes.
    */
   @SuppressWarnings("unused")
   @Nullable
-  default ColumnIndexSupplier getIndexSupplier(String columnName, 
ColumnSelector columnSelector)
+  default ColumnIndexSupplier getIndexSupplier(
+      String columnName,
+      ColumnIndexSelector columnIndexSelector
+  )
   {
     return NoIndexesColumnIndexSupplier.getInstance();
   }
diff --git 
a/processing/src/main/java/org/apache/druid/segment/VirtualColumns.java 
b/processing/src/main/java/org/apache/druid/segment/VirtualColumns.java
index 5560d10d4ed..583f0425c2a 100644
--- a/processing/src/main/java/org/apache/druid/segment/VirtualColumns.java
+++ b/processing/src/main/java/org/apache/druid/segment/VirtualColumns.java
@@ -33,6 +33,7 @@ import org.apache.druid.java.util.common.Pair;
 import org.apache.druid.query.Query;
 import org.apache.druid.query.cache.CacheKeyBuilder;
 import org.apache.druid.query.dimension.DimensionSpec;
+import org.apache.druid.query.filter.ColumnIndexSelector;
 import org.apache.druid.segment.column.ColumnCapabilities;
 import org.apache.druid.segment.column.ColumnHolder;
 import org.apache.druid.segment.column.ColumnIndexSupplier;
@@ -185,10 +186,13 @@ public class VirtualColumns implements Cacheable
    * is no guarantee that the column has indexes.
    */
   @Nullable
-  public ColumnIndexSupplier getIndexSupplier(String columnName, 
ColumnSelector columnSelector)
+  public ColumnIndexSupplier getIndexSupplier(
+      String columnName,
+      ColumnIndexSelector columnIndexSelector
+  )
   {
     final VirtualColumn virtualColumn = 
getVirtualColumnForSelector(columnName);
-    return virtualColumn.getIndexSupplier(columnName, columnSelector);
+    return virtualColumn.getIndexSupplier(columnName, columnIndexSelector);
   }
 
   /**
diff --git 
a/processing/src/main/java/org/apache/druid/segment/virtual/ExpressionVirtualColumn.java
 
b/processing/src/main/java/org/apache/druid/segment/virtual/ExpressionVirtualColumn.java
index f8ace2ca2f2..58952061239 100644
--- 
a/processing/src/main/java/org/apache/druid/segment/virtual/ExpressionVirtualColumn.java
+++ 
b/processing/src/main/java/org/apache/druid/segment/virtual/ExpressionVirtualColumn.java
@@ -34,8 +34,8 @@ import org.apache.druid.math.expr.ExpressionProcessing;
 import org.apache.druid.math.expr.Parser;
 import org.apache.druid.query.cache.CacheKeyBuilder;
 import org.apache.druid.query.dimension.DimensionSpec;
+import org.apache.druid.query.filter.ColumnIndexSelector;
 import org.apache.druid.segment.ColumnInspector;
-import org.apache.druid.segment.ColumnSelector;
 import org.apache.druid.segment.ColumnSelectorFactory;
 import org.apache.druid.segment.ColumnValueSelector;
 import org.apache.druid.segment.DimensionSelector;
@@ -244,9 +244,12 @@ public class ExpressionVirtualColumn implements 
VirtualColumn
 
   @Nullable
   @Override
-  public ColumnIndexSupplier getIndexSupplier(String columnName, 
ColumnSelector columnSelector)
+  public ColumnIndexSupplier getIndexSupplier(
+      String columnName,
+      ColumnIndexSelector columnIndexSelector
+  )
   {
-    return getParsedExpression().get().asColumnIndexSupplier(columnSelector, 
outputType);
+    return 
getParsedExpression().get().asColumnIndexSupplier(columnIndexSelector, 
outputType);
   }
 
   @Override
diff --git 
a/processing/src/main/java/org/apache/druid/segment/virtual/FallbackVirtualColumn.java
 
b/processing/src/main/java/org/apache/druid/segment/virtual/FallbackVirtualColumn.java
index 461a6b68dff..26ce21b98ee 100644
--- 
a/processing/src/main/java/org/apache/druid/segment/virtual/FallbackVirtualColumn.java
+++ 
b/processing/src/main/java/org/apache/druid/segment/virtual/FallbackVirtualColumn.java
@@ -25,8 +25,8 @@ import org.apache.druid.java.util.common.IAE;
 import org.apache.druid.java.util.common.Pair;
 import org.apache.druid.query.cache.CacheKeyBuilder;
 import org.apache.druid.query.dimension.DimensionSpec;
+import org.apache.druid.query.filter.ColumnIndexSelector;
 import org.apache.druid.segment.ColumnInspector;
-import org.apache.druid.segment.ColumnSelector;
 import org.apache.druid.segment.ColumnSelectorFactory;
 import org.apache.druid.segment.ColumnValueSelector;
 import org.apache.druid.segment.DimensionSelector;
@@ -189,9 +189,12 @@ public class FallbackVirtualColumn implements VirtualColumn
 
   @Nullable
   @Override
-  public ColumnIndexSupplier getIndexSupplier(String columnName, 
ColumnSelector columnSelector)
+  public ColumnIndexSupplier getIndexSupplier(
+      String columnName,
+      ColumnIndexSelector indexSelector
+  )
   {
-    final ColumnHolder columnHolder = 
columnSelector.getColumnHolder(columnToUse(columnSelector).getDimension());
+    final ColumnHolder columnHolder = 
indexSelector.getColumnHolder(columnToUse(indexSelector).getDimension());
     if (columnHolder == null) {
       return null;
     }
diff --git 
a/processing/src/main/java/org/apache/druid/segment/virtual/ListFilteredVirtualColumn.java
 
b/processing/src/main/java/org/apache/druid/segment/virtual/ListFilteredVirtualColumn.java
index 741db001961..9262e8a1730 100644
--- 
a/processing/src/main/java/org/apache/druid/segment/virtual/ListFilteredVirtualColumn.java
+++ 
b/processing/src/main/java/org/apache/druid/segment/virtual/ListFilteredVirtualColumn.java
@@ -34,11 +34,11 @@ import org.apache.druid.query.cache.CacheKeyBuilder;
 import org.apache.druid.query.dimension.DefaultDimensionSpec;
 import org.apache.druid.query.dimension.DimensionSpec;
 import org.apache.druid.query.dimension.ListFilteredDimensionSpec;
+import org.apache.druid.query.filter.ColumnIndexSelector;
 import org.apache.druid.query.filter.DruidObjectPredicate;
 import org.apache.druid.query.filter.DruidPredicateFactory;
 import org.apache.druid.query.ordering.StringComparators;
 import org.apache.druid.segment.ColumnInspector;
-import org.apache.druid.segment.ColumnSelector;
 import org.apache.druid.segment.ColumnSelectorFactory;
 import org.apache.druid.segment.ColumnValueSelector;
 import org.apache.druid.segment.DimensionSelector;
@@ -183,7 +183,10 @@ public class ListFilteredVirtualColumn implements 
VirtualColumn
 
   @Nullable
   @Override
-  public ColumnIndexSupplier getIndexSupplier(String columnName, 
ColumnSelector columnSelector)
+  public ColumnIndexSupplier getIndexSupplier(
+      String columnName,
+      ColumnIndexSelector indexSelector
+  )
   {
     if (delegate.getExtractionFn() != null) {
       return NoIndexesColumnIndexSupplier.getInstance();
@@ -194,13 +197,7 @@ public class ListFilteredVirtualColumn implements 
VirtualColumn
       @Override
       public <T> T as(Class<T> clazz)
       {
-
-        final ColumnHolder holder = 
columnSelector.getColumnHolder(delegate.getDimension());
-        if (holder == null) {
-          return null;
-        }
-
-        ColumnIndexSupplier indexSupplier = holder.getIndexSupplier();
+        ColumnIndexSupplier indexSupplier = 
indexSelector.getIndexSupplier(delegate.getDimension());
         if (indexSupplier == null) {
           return null;
         }
@@ -229,7 +226,7 @@ public class ListFilteredVirtualColumn implements 
VirtualColumn
         }
 
         // someday maybe we can have a better way to get row count..
-        final ColumnHolder time = 
columnSelector.getColumnHolder(ColumnHolder.TIME_COLUMN_NAME);
+        final ColumnHolder time = 
indexSelector.getColumnHolder(ColumnHolder.TIME_COLUMN_NAME);
         final int numRows = time.getLength();
         final Supplier<ImmutableBitmap> nullValueBitmapSupplier = 
Suppliers.memoize(
             () -> underlyingIndex.getBitmapFactory().complement(
diff --git 
a/processing/src/main/java/org/apache/druid/segment/virtual/NestedFieldVirtualColumn.java
 
b/processing/src/main/java/org/apache/druid/segment/virtual/NestedFieldVirtualColumn.java
index 0a282f5e4bc..63b8598ef63 100644
--- 
a/processing/src/main/java/org/apache/druid/segment/virtual/NestedFieldVirtualColumn.java
+++ 
b/processing/src/main/java/org/apache/druid/segment/virtual/NestedFieldVirtualColumn.java
@@ -34,6 +34,7 @@ import org.apache.druid.math.expr.ExpressionType;
 import org.apache.druid.query.cache.CacheKeyBuilder;
 import org.apache.druid.query.dimension.DimensionSpec;
 import org.apache.druid.query.extraction.ExtractionFn;
+import org.apache.druid.query.filter.ColumnIndexSelector;
 import org.apache.druid.query.filter.DruidPredicateFactory;
 import org.apache.druid.query.filter.ValueMatcher;
 import org.apache.druid.query.monomorphicprocessing.RuntimeShapeInspector;
@@ -1158,10 +1159,10 @@ public class NestedFieldVirtualColumn implements 
VirtualColumn
   @Override
   public ColumnIndexSupplier getIndexSupplier(
       String columnName,
-      ColumnSelector selector
+      ColumnIndexSelector indexSelector
   )
   {
-    ColumnHolder holder = selector.getColumnHolder(this.columnName);
+    ColumnHolder holder = indexSelector.getColumnHolder(this.columnName);
     if (holder == null) {
       return null;
     }
diff --git 
a/processing/src/test/java/org/apache/druid/segment/TestColumnSelector.java 
b/processing/src/test/java/org/apache/druid/segment/TestColumnSelector.java
index ea8f85391f5..244213588e8 100644
--- a/processing/src/test/java/org/apache/druid/segment/TestColumnSelector.java
+++ b/processing/src/test/java/org/apache/druid/segment/TestColumnSelector.java
@@ -19,14 +19,12 @@
 
 package org.apache.druid.segment;
 
-import com.google.common.collect.Lists;
 import org.apache.druid.java.util.common.UOE;
 import org.apache.druid.segment.column.ColumnCapabilities;
 import org.apache.druid.segment.column.ColumnHolder;
 
 import javax.annotation.Nullable;
 import java.util.LinkedHashMap;
-import java.util.List;
 import java.util.Map;
 
 public class TestColumnSelector implements ColumnSelector
@@ -46,12 +44,6 @@ public class TestColumnSelector implements ColumnSelector
     return this;
   }
 
-  @Override
-  public List<String> getColumnNames()
-  {
-    return Lists.newArrayList(holders.keySet());
-  }
-
   @Nullable
   @Override
   public ColumnHolder getColumnHolder(String columnName)
diff --git 
a/processing/src/test/java/org/apache/druid/segment/filter/BaseFilterTest.java 
b/processing/src/test/java/org/apache/druid/segment/filter/BaseFilterTest.java
index 6bab72f2cdb..906c5cedc72 100644
--- 
a/processing/src/test/java/org/apache/druid/segment/filter/BaseFilterTest.java
+++ 
b/processing/src/test/java/org/apache/druid/segment/filter/BaseFilterTest.java
@@ -143,6 +143,9 @@ public abstract class BaseFilterTest extends 
InitializedNullHandlingTest
           new ExpressionVirtualColumn("vd0-add-sub", "d0 + (d0 - d0)", 
ColumnType.DOUBLE, TestExprMacroTable.INSTANCE),
           new ExpressionVirtualColumn("vf0-add-sub", "f0 + (f0 - f0)", 
ColumnType.FLOAT, TestExprMacroTable.INSTANCE),
           new ExpressionVirtualColumn("vl0-add-sub", "l0 + (l0 - l0)", 
ColumnType.LONG, TestExprMacroTable.INSTANCE),
+          new ExpressionVirtualColumn("double-vd0-add-sub", "vd0 + (vd0 - 
vd0)", ColumnType.DOUBLE, TestExprMacroTable.INSTANCE),
+          new ExpressionVirtualColumn("double-vf0-add-sub", "vf0 + (vf0 - 
vf0)", ColumnType.FLOAT, TestExprMacroTable.INSTANCE),
+          new ExpressionVirtualColumn("double-vl0-add-sub", "vl0 + (vl0 - 
vl0)", ColumnType.LONG, TestExprMacroTable.INSTANCE),
           new ExpressionVirtualColumn("vdim3-concat", "dim3 + dim3", 
ColumnType.LONG, TestExprMacroTable.INSTANCE),
           new ExpressionVirtualColumn("nestedArrayLong", "array(arrayLong)", 
ColumnType.ofArray(ColumnType.LONG_ARRAY), TestExprMacroTable.INSTANCE),
           new ListFilteredVirtualColumn("allow-dim0", 
DefaultDimensionSpec.of("dim0"), ImmutableSet.of("3", "4"), true),
diff --git 
a/processing/src/test/java/org/apache/druid/segment/filter/EqualityFilterTests.java
 
b/processing/src/test/java/org/apache/druid/segment/filter/EqualityFilterTests.java
index 01a7b2d43ed..3a141d63758 100644
--- 
a/processing/src/test/java/org/apache/druid/segment/filter/EqualityFilterTests.java
+++ 
b/processing/src/test/java/org/apache/druid/segment/filter/EqualityFilterTests.java
@@ -649,6 +649,23 @@ public class EqualityFilterTests
             NotDimFilter.of(new EqualityFilter("vd0-add-sub", 
ColumnType.DOUBLE, 0.0, null)),
             ImmutableList.of("1", "3", "4", "5")
         );
+
+        // virtual column that refers to another virtual column
+        assertFilterMatches(new EqualityFilter("double-vf0-add-sub", 
ColumnType.STRING, "0", null), ImmutableList.of("0", "4"));
+        assertFilterMatches(new EqualityFilter("double-vd0-add-sub", 
ColumnType.STRING, "0", null), ImmutableList.of("0", "2"));
+        assertFilterMatches(new EqualityFilter("double-vl0-add-sub", 
ColumnType.STRING, "0", null), ImmutableList.of("0", "3"));
+
+        assertFilterMatches(new EqualityFilter("double-vf0-add-sub", 
ColumnType.FLOAT, 0f, null), ImmutableList.of("0", "4"));
+        assertFilterMatches(
+            NotDimFilter.of(new EqualityFilter("double-vf0-add-sub", 
ColumnType.FLOAT, 0f, null)),
+            ImmutableList.of("1", "2", "3", "5")
+        );
+        assertFilterMatches(new EqualityFilter("double-vd0-add-sub", 
ColumnType.DOUBLE, 0.0, null), ImmutableList.of("0", "2"));
+        assertFilterMatches(
+            NotDimFilter.of(new EqualityFilter("double-vd0-add-sub", 
ColumnType.DOUBLE, 0.0, null)),
+            ImmutableList.of("1", "3", "4", "5")
+        );
+
         assertFilterMatches(new EqualityFilter("vl0", ColumnType.LONG, 0L, 
null), ImmutableList.of("0", "3"));
         assertFilterMatches(
             NotDimFilter.of(new EqualityFilter("vl0", ColumnType.LONG, 0L, 
null)),
@@ -713,6 +730,32 @@ public class EqualityFilterTests
           assertFilterMatches(new EqualityFilter("vf0-add-sub", 
ColumnType.STRING, "0", null), ImmutableList.of("0"));
           assertFilterMatches(new EqualityFilter("vd0-add-sub", 
ColumnType.STRING, "0", null), ImmutableList.of("0"));
           assertFilterMatches(new EqualityFilter("vl0-add-sub", 
ColumnType.STRING, "0", null), ImmutableList.of("0"));
+
+          assertFilterMatches(new EqualityFilter("double-vf0-add-sub", 
ColumnType.FLOAT, 0f, null), ImmutableList.of("0"));
+          assertFilterMatches(
+              NotDimFilter.of(new EqualityFilter("double-vf0-add-sub", 
ColumnType.FLOAT, 0f, null)),
+              NullHandling.sqlCompatible()
+              ? ImmutableList.of("1", "2", "3", "5")
+              : ImmutableList.of("1", "2", "3", "4", "5")
+          );
+          assertFilterMatches(new EqualityFilter("double-vd0-add-sub", 
ColumnType.DOUBLE, 0.0, null), ImmutableList.of("0"));
+          assertFilterMatches(
+              NotDimFilter.of(new EqualityFilter("double-vd0-add-sub", 
ColumnType.DOUBLE, 0.0, null)),
+              NullHandling.sqlCompatible()
+              ? ImmutableList.of("1", "3", "4", "5")
+              : ImmutableList.of("1", "2", "3", "4", "5")
+          );
+          assertFilterMatches(new EqualityFilter("double-vl0-add-sub", 
ColumnType.LONG, 0L, null), ImmutableList.of("0"));
+          assertFilterMatches(
+              NotDimFilter.of(new EqualityFilter("double-vl0-add-sub", 
ColumnType.LONG, 0L, null)),
+              NullHandling.sqlCompatible()
+              ? ImmutableList.of("1", "2", "4", "5")
+              : ImmutableList.of("1", "2", "3", "4", "5")
+          );
+
+          assertFilterMatches(new EqualityFilter("double-vf0-add-sub", 
ColumnType.STRING, "0", null), ImmutableList.of("0"));
+          assertFilterMatches(new EqualityFilter("double-vd0-add-sub", 
ColumnType.STRING, "0", null), ImmutableList.of("0"));
+          assertFilterMatches(new EqualityFilter("double-vl0-add-sub", 
ColumnType.STRING, "0", null), ImmutableList.of("0"));
         }
       }
     }
diff --git 
a/processing/src/test/java/org/apache/druid/segment/filter/ExtractionDimFilterTest.java
 
b/processing/src/test/java/org/apache/druid/segment/filter/ExtractionDimFilterTest.java
index 692a2d499f4..b890e666d36 100644
--- 
a/processing/src/test/java/org/apache/druid/segment/filter/ExtractionDimFilterTest.java
+++ 
b/processing/src/test/java/org/apache/druid/segment/filter/ExtractionDimFilterTest.java
@@ -36,6 +36,7 @@ import org.apache.druid.query.filter.Filter;
 import org.apache.druid.query.filter.SelectorDimFilter;
 import org.apache.druid.segment.column.ColumnCapabilities;
 import org.apache.druid.segment.column.ColumnCapabilitiesImpl;
+import org.apache.druid.segment.column.ColumnHolder;
 import org.apache.druid.segment.column.ColumnIndexSupplier;
 import org.apache.druid.segment.column.ColumnType;
 import org.apache.druid.segment.data.BitmapSerdeFactory;
@@ -88,6 +89,13 @@ public class ExtractionDimFilterTest extends 
InitializedNullHandlingTest
 
   private final ColumnIndexSelector BITMAP_INDEX_SELECTOR = new 
ColumnIndexSelector()
   {
+    @Nullable
+    @Override
+    public ColumnHolder getColumnHolder(String columnName)
+    {
+      return null;
+    }
+
     @Nullable
     @Override
     public ColumnCapabilities getColumnCapabilities(String column)
diff --git 
a/processing/src/test/java/org/apache/druid/segment/filter/NullFilterTests.java 
b/processing/src/test/java/org/apache/druid/segment/filter/NullFilterTests.java
index 443f655baae..9cb64f75d78 100644
--- 
a/processing/src/test/java/org/apache/druid/segment/filter/NullFilterTests.java
+++ 
b/processing/src/test/java/org/apache/druid/segment/filter/NullFilterTests.java
@@ -248,6 +248,24 @@ public class NullFilterTests
             NotDimFilter.of(NullFilter.forColumn("vl0-add-sub")),
             ImmutableList.of("0", "1", "2", "3", "4", "5")
         );
+
+        assertFilterMatches(NullFilter.forColumn("double-vf0-add-sub"), 
ImmutableList.of());
+        assertFilterMatches(
+            NotDimFilter.of(NullFilter.forColumn("double-vf0-add-sub")),
+            ImmutableList.of("0", "1", "2", "3", "4", "5")
+        );
+
+        assertFilterMatches(NullFilter.forColumn("double-vd0-add-sub"), 
ImmutableList.of());
+        assertFilterMatches(
+            NotDimFilter.of(NullFilter.forColumn("double-vd0-add-sub")),
+            ImmutableList.of("0", "1", "2", "3", "4", "5")
+        );
+
+        assertFilterMatches(NullFilter.forColumn("double-vl0-add-sub"), 
ImmutableList.of());
+        assertFilterMatches(
+            NotDimFilter.of(NullFilter.forColumn("double-vl0-add-sub")),
+            ImmutableList.of("0", "1", "2", "3", "4", "5")
+        );
       } else {
         assertFilterMatches(NullFilter.forColumn("vf0"), 
ImmutableList.of("4"));
         assertFilterMatches(NotDimFilter.of(NullFilter.forColumn("vf0")), 
ImmutableList.of("0", "1", "2", "3", "5"));
@@ -269,6 +287,15 @@ public class NullFilterTests
 
           assertFilterMatches(NullFilter.forColumn("vl0-add-sub"), 
ImmutableList.of("3"));
           
assertFilterMatches(NotDimFilter.of(NullFilter.forColumn("vl0-add-sub")), 
ImmutableList.of("0", "1", "2", "4", "5"));
+
+          assertFilterMatches(NullFilter.forColumn("double-vf0-add-sub"), 
ImmutableList.of("4"));
+          
assertFilterMatches(NotDimFilter.of(NullFilter.forColumn("double-vf0-add-sub")),
 ImmutableList.of("0", "1", "2", "3", "5"));
+
+          assertFilterMatches(NullFilter.forColumn("double-vd0-add-sub"), 
ImmutableList.of("2"));
+          
assertFilterMatches(NotDimFilter.of(NullFilter.forColumn("double-vd0-add-sub")),
 ImmutableList.of("0", "1", "3", "4", "5"));
+
+          assertFilterMatches(NullFilter.forColumn("vl0-add-sub"), 
ImmutableList.of("3"));
+          
assertFilterMatches(NotDimFilter.of(NullFilter.forColumn("double-vl0-add-sub")),
 ImmutableList.of("0", "1", "2", "4", "5"));
         }
       }
     }
diff --git 
a/processing/src/test/java/org/apache/druid/segment/virtual/DummyStringVirtualColumn.java
 
b/processing/src/test/java/org/apache/druid/segment/virtual/DummyStringVirtualColumn.java
index 27f5113eea2..817fb8446a3 100644
--- 
a/processing/src/test/java/org/apache/druid/segment/virtual/DummyStringVirtualColumn.java
+++ 
b/processing/src/test/java/org/apache/druid/segment/virtual/DummyStringVirtualColumn.java
@@ -22,6 +22,7 @@ package org.apache.druid.segment.virtual;
 import org.apache.druid.java.util.common.IAE;
 import org.apache.druid.query.dimension.DefaultDimensionSpec;
 import org.apache.druid.query.dimension.DimensionSpec;
+import org.apache.druid.query.filter.ColumnIndexSelector;
 import org.apache.druid.query.filter.DruidPredicateFactory;
 import org.apache.druid.query.filter.ValueMatcher;
 import org.apache.druid.query.monomorphicprocessing.RuntimeShapeInspector;
@@ -169,7 +170,7 @@ public class DummyStringVirtualColumn implements 
VirtualColumn
   @Override
   public ColumnIndexSupplier getIndexSupplier(
       String columnName,
-      ColumnSelector columnSelector
+      ColumnIndexSelector indexSelector
   )
   {
     return new ColumnIndexSupplier()
@@ -180,12 +181,11 @@ public class DummyStringVirtualColumn implements 
VirtualColumn
       public <T> T as(Class<T> clazz)
       {
         if (enableBitmaps) {
-          ColumnHolder holder = columnSelector.getColumnHolder(baseColumnName);
-          if (holder == null) {
+          ColumnIndexSupplier supplier = 
indexSelector.getIndexSupplier(baseColumnName);
+          if (supplier == null) {
             return null;
           }
-
-          return holder.getIndexSupplier().as(clazz);
+          return supplier.as(clazz);
         } else {
           return null;
         }
diff --git 
a/processing/src/test/java/org/apache/druid/segment/virtual/FallbackVirtualColumnTest.java
 
b/processing/src/test/java/org/apache/druid/segment/virtual/FallbackVirtualColumnTest.java
index 72de5466ff6..9a04b906e91 100644
--- 
a/processing/src/test/java/org/apache/druid/segment/virtual/FallbackVirtualColumnTest.java
+++ 
b/processing/src/test/java/org/apache/druid/segment/virtual/FallbackVirtualColumnTest.java
@@ -19,16 +19,19 @@
 
 package org.apache.druid.segment.virtual;
 
+import org.apache.druid.collections.bitmap.RoaringBitmapFactory;
 import org.apache.druid.common.config.NullHandling;
 import org.apache.druid.query.cache.CacheKeyBuilder;
 import org.apache.druid.query.dimension.DefaultDimensionSpec;
 import org.apache.druid.query.dimension.DimensionSpec;
 import org.apache.druid.query.extraction.ExtractionFn;
+import org.apache.druid.segment.ColumnSelectorColumnIndexSelector;
 import org.apache.druid.segment.ConstantDimensionSelector;
 import org.apache.druid.segment.DimensionSelector;
 import org.apache.druid.segment.IdLookup;
 import org.apache.druid.segment.TestColumnSelector;
 import org.apache.druid.segment.TestColumnSelectorFactory;
+import org.apache.druid.segment.VirtualColumns;
 import org.apache.druid.segment.column.BaseColumn;
 import org.apache.druid.segment.column.ColumnCapabilities;
 import org.apache.druid.segment.column.ColumnCapabilitiesImpl;
@@ -317,17 +320,22 @@ public class FallbackVirtualColumnTest
         .addCapabilities("colA", ColumnCapabilitiesImpl.createDefault())
         .addCapabilities("colB", ColumnCapabilitiesImpl.createDefault())
         .addCapabilities("colC", ColumnCapabilitiesImpl.createDefault());
+    final ColumnSelectorColumnIndexSelector columnIndexSelector = new 
ColumnSelectorColumnIndexSelector(
+        RoaringBitmapFactory.INSTANCE,
+        VirtualColumns.EMPTY,
+        selectorFactory
+    );
 
-    Assert.assertSame(colA, col.getIndexSupplier("abcd", selectorFactory));
+    Assert.assertSame(colA, col.getIndexSupplier("abcd", columnIndexSelector));
 
     selectorFactory.addCapabilities("colA", null);
-    Assert.assertSame(colB, col.getIndexSupplier("abcd", selectorFactory));
+    Assert.assertSame(colB, col.getIndexSupplier("abcd", columnIndexSelector));
 
     selectorFactory.addCapabilities("colB", null);
-    Assert.assertSame(colC, col.getIndexSupplier("abcd", selectorFactory));
+    Assert.assertSame(colC, col.getIndexSupplier("abcd", columnIndexSelector));
 
     selectorFactory.addCapabilities("colC", null);
-    Assert.assertSame(colA, col.getIndexSupplier("abcd", selectorFactory));
+    Assert.assertSame(colA, col.getIndexSupplier("abcd", columnIndexSelector));
 
   }
 
diff --git 
a/sql/src/main/java/org/apache/druid/sql/calcite/expression/Expressions.java 
b/sql/src/main/java/org/apache/druid/sql/calcite/expression/Expressions.java
index 538663395d0..3da2b5fc2f2 100644
--- a/sql/src/main/java/org/apache/druid/sql/calcite/expression/Expressions.java
+++ b/sql/src/main/java/org/apache/druid/sql/calcite/expression/Expressions.java
@@ -562,9 +562,17 @@ public class Expressions
               druidExpression.getSimpleExtraction().getExtractionFn()
           );
         } else {
-          if (virtualColumnRegistry != null && 
druidExpression.getSimpleExtraction().getExtractionFn() != null) {
-            String column = 
virtualColumnRegistry.getOrCreateVirtualColumnForExpression(druidExpression, 
druidExpression.getDruidType());
-            equalFilter = NullFilter.forColumn(column);
+          if (druidExpression.getSimpleExtraction().getExtractionFn() != null) 
{
+            if (virtualColumnRegistry != null) {
+              String column = 
virtualColumnRegistry.getOrCreateVirtualColumnForExpression(
+                  druidExpression,
+                  druidExpression.getDruidType()
+              );
+              equalFilter = NullFilter.forColumn(column);
+            } else {
+              // virtual column registry unavailable, fallback to expression 
filter
+              return null;
+            }
           } else {
             equalFilter = 
NullFilter.forColumn(druidExpression.getDirectColumn());
           }
@@ -770,6 +778,7 @@ public class Expressions
             );
           } else {
             // if this happens for some reason, bail and use an expression 
filter
+            return null;
           }
         }
 
diff --git 
a/sql/src/test/java/org/apache/druid/sql/calcite/CalciteArraysQueryTest.java 
b/sql/src/test/java/org/apache/druid/sql/calcite/CalciteArraysQueryTest.java
index 54e28f0b01f..e4f609ef9ff 100644
--- a/sql/src/test/java/org/apache/druid/sql/calcite/CalciteArraysQueryTest.java
+++ b/sql/src/test/java/org/apache/druid/sql/calcite/CalciteArraysQueryTest.java
@@ -7291,4 +7291,81 @@ public class CalciteArraysQueryTest extends 
BaseCalciteQueryTest
                     .build()
     );
   }
+
+  @Test
+  public void testUnnestExtractionFn()
+  {
+    skipVectorize();
+    cannotVectorize();
+    testQuery(
+        "SELECT substring(d3,1) FROM druid.numfoo, UNNEST(MV_TO_ARRAY(dim3)) 
as unnested (d3) WHERE substring(d3,1) <> 'b'",
+        QUERY_CONTEXT_UNNEST,
+        ImmutableList.of(
+            Druids.newScanQueryBuilder()
+                  .dataSource(UnnestDataSource.create(
+                      new TableDataSource(CalciteTests.DATASOURCE3),
+                      expressionVirtualColumn("j0.unnest", "\"dim3\"", 
ColumnType.STRING),
+                      NullHandling.sqlCompatible()
+                      ? expressionFilter("(substring(\"j0.unnest\", 0, -1) != 
'b')")
+                      : not(selector("j0.unnest", "b", new 
SubstringDimExtractionFn(0, null)))
+                  ))
+                  .virtualColumns(expressionVirtualColumn("v0", 
"substring(\"j0.unnest\", 0, -1)", ColumnType.STRING))
+                  .intervals(querySegmentSpec(Filtration.eternity()))
+                  
.resultFormat(ScanQuery.ResultFormat.RESULT_FORMAT_COMPACTED_LIST)
+                  .legacy(false)
+                  .context(QUERY_CONTEXT_UNNEST)
+                  .columns(ImmutableList.of("v0"))
+                  .build()
+        ),
+        useDefault ?
+        ImmutableList.of(
+            new Object[]{"a"},
+            new Object[]{"c"},
+            new Object[]{"d"},
+            new Object[]{""},
+            new Object[]{""},
+            new Object[]{""}
+        ) :
+        ImmutableList.of(
+            new Object[]{"a"},
+            new Object[]{"c"},
+            new Object[]{"d"}
+        )
+    );
+  }
+
+  @Test
+  public void testUnnestExtractionFnNull()
+  {
+    skipVectorize();
+    cannotVectorize();
+    testQuery(
+        "SELECT substring(d3,1) FROM druid.numfoo, UNNEST(MV_TO_ARRAY(dim3)) 
as unnested (d3) WHERE substring(d3,1) is not null",
+        QUERY_CONTEXT_UNNEST,
+        ImmutableList.of(
+            Druids.newScanQueryBuilder()
+                  .dataSource(UnnestDataSource.create(
+                      new TableDataSource(CalciteTests.DATASOURCE3),
+                      expressionVirtualColumn("j0.unnest", "\"dim3\"", 
ColumnType.STRING),
+                      NullHandling.sqlCompatible()
+                      ? expressionFilter("notnull(substring(\"j0.unnest\", 0, 
-1))")
+                      : not(selector("j0.unnest", null, new 
SubstringDimExtractionFn(0, null)))
+                  ))
+                  .virtualColumns(expressionVirtualColumn("v0", 
"substring(\"j0.unnest\", 0, -1)", ColumnType.STRING))
+                  .intervals(querySegmentSpec(Filtration.eternity()))
+                  
.resultFormat(ScanQuery.ResultFormat.RESULT_FORMAT_COMPACTED_LIST)
+                  .legacy(false)
+                  .context(QUERY_CONTEXT_UNNEST)
+                  .columns(ImmutableList.of("v0"))
+                  .build()
+        ),
+        ImmutableList.of(
+            new Object[]{"a"},
+            new Object[]{"b"},
+            new Object[]{"b"},
+            new Object[]{"c"},
+            new Object[]{"d"}
+        )
+    );
+  }
 }


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]


Reply via email to