You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@druid.apache.org by fj...@apache.org on 2019/03/11 18:38:04 UTC

[incubator-druid] branch master updated: sql, filters, and virtual columns (#6902)

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

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


The following commit(s) were added to refs/heads/master by this push:
     new d7ba19d  sql, filters, and virtual columns  (#6902)
d7ba19d is described below

commit d7ba19d477732578fc310831ed28365b36332752
Author: Clint Wylie <cj...@gmail.com>
AuthorDate: Mon Mar 11 11:37:58 2019 -0700

    sql, filters, and virtual columns  (#6902)
    
    * refactor sql planning to re-use expression virtual columns when possible when constructing a DruidQuery, allowing virtual columns to be defined in filter expressions, and making resulting native druid queries more concise. also minor refactor of built-in sql aggregators to maximize code re-use
    
    * fix it
    
    * fix it in the right place
    
    * fixup for base64 stuff
    
    * fixup tests
    
    * fix merge conflict on import order
    
    * fixup
    
    * fix imports
    
    * fix tests
    
    * review comments
    
    * refactor
    
    * re-arrange
    
    * better javadoc
    
    * fixup merge
    
    * fixup tests
    
    * fix accidental changes
---
 .../hll/sql/HllSketchSqlAggregator.java            |  13 +-
 .../quantiles/sql/DoublesSketchSqlAggregator.java  |  13 +-
 .../theta/sql/ThetaSketchSqlAggregator.java        |  13 +-
 .../hll/sql/HllSketchSqlAggregatorTest.java        |  14 +-
 .../sql/DoublesSketchSqlAggregatorTest.java        |   4 +-
 .../theta/sql/ThetaSketchSqlAggregatorTest.java    |  14 +-
 .../bloom/sql/BloomFilterSqlAggregator.java        |  12 +-
 .../filter/sql/BloomFilterOperatorConversion.java  |  24 +-
 .../bloom/sql/BloomFilterSqlAggregatorTest.java    |  12 +-
 .../query/filter/sql/BloomDimFilterSqlTest.java    |  50 +-
 ...FixedBucketsHistogramQuantileSqlAggregator.java |  13 +-
 .../histogram/sql/QuantileSqlAggregator.java       |  11 +-
 ...dBucketsHistogramQuantileSqlAggregatorTest.java |   4 +-
 .../histogram/sql/QuantileSqlAggregatorTest.java   |   4 +-
 .../druid/sql/calcite/aggregation/Aggregation.java |  28 +-
 .../calcite/aggregation/DimensionExpression.java   |  23 +-
 .../sql/calcite/aggregation/SqlAggregator.java     |   9 +-
 .../builtin/ApproxCountDistinctSqlAggregator.java  |  18 +-
 .../aggregation/builtin/AvgSqlAggregator.java      |  55 +-
 .../aggregation/builtin/CountSqlAggregator.java    |  19 +-
 .../aggregation/builtin/MaxSqlAggregator.java      |  54 +-
 .../aggregation/builtin/MinSqlAggregator.java      |  54 +-
 ...SqlAggregator.java => SimpleSqlAggregator.java} |  53 +-
 .../aggregation/builtin/SumSqlAggregator.java      |  54 +-
 .../druid/sql/calcite/expression/Expressions.java  | 142 ++--
 .../calcite/expression/SqlOperatorConversion.java  |   9 +-
 .../expression/builtin/LikeOperatorConversion.java |  39 +-
 .../filtration/ConvertBoundsToSelectors.java       |  16 +-
 .../druid/sql/calcite/filtration/Filtration.java   |  14 +-
 .../apache/druid/sql/calcite/rel/DruidQuery.java   | 227 +++---
 .../druid/sql/calcite/rel/DruidQuerySignature.java | 148 ++++
 .../druid/sql/calcite/rule/GroupByRules.java       |  19 +-
 .../druid/sql/calcite/BaseCalciteQueryTest.java    |  16 +-
 .../apache/druid/sql/calcite/CalciteQueryTest.java | 770 +++++++++++----------
 .../sql/calcite/filtration/FiltrationTest.java     |   5 +-
 35 files changed, 1030 insertions(+), 943 deletions(-)

diff --git a/extensions-core/datasketches/src/main/java/org/apache/druid/query/aggregation/datasketches/hll/sql/HllSketchSqlAggregator.java b/extensions-core/datasketches/src/main/java/org/apache/druid/query/aggregation/datasketches/hll/sql/HllSketchSqlAggregator.java
index 13147ff..93760ff 100644
--- a/extensions-core/datasketches/src/main/java/org/apache/druid/query/aggregation/datasketches/hll/sql/HllSketchSqlAggregator.java
+++ b/extensions-core/datasketches/src/main/java/org/apache/druid/query/aggregation/datasketches/hll/sql/HllSketchSqlAggregator.java
@@ -42,13 +42,13 @@ import org.apache.druid.query.dimension.DefaultDimensionSpec;
 import org.apache.druid.query.dimension.DimensionSpec;
 import org.apache.druid.segment.VirtualColumn;
 import org.apache.druid.segment.column.ValueType;
-import org.apache.druid.segment.virtual.ExpressionVirtualColumn;
 import org.apache.druid.sql.calcite.aggregation.Aggregation;
 import org.apache.druid.sql.calcite.aggregation.SqlAggregator;
 import org.apache.druid.sql.calcite.expression.DruidExpression;
 import org.apache.druid.sql.calcite.expression.Expressions;
 import org.apache.druid.sql.calcite.planner.Calcites;
 import org.apache.druid.sql.calcite.planner.PlannerContext;
+import org.apache.druid.sql.calcite.rel.DruidQuerySignature;
 import org.apache.druid.sql.calcite.table.RowSignature;
 
 import javax.annotation.Nullable;
@@ -71,7 +71,7 @@ public class HllSketchSqlAggregator implements SqlAggregator
   @Override
   public Aggregation toDruidAggregation(
       PlannerContext plannerContext,
-      RowSignature rowSignature,
+      DruidQuerySignature querySignature,
       RexBuilder rexBuilder,
       String name,
       AggregateCall aggregateCall,
@@ -80,6 +80,7 @@ public class HllSketchSqlAggregator implements SqlAggregator
       boolean finalizeAggregations
   )
   {
+    final RowSignature rowSignature = querySignature.getRowSignature();
     // Don't use Aggregations.getArgumentsForSimpleAggregator, since it won't let us use direct column access
     // for string columns.
     final RexNode columnRexNode = Expressions.fromFieldAccess(
@@ -147,10 +148,10 @@ public class HllSketchSqlAggregator implements SqlAggregator
       if (columnArg.isDirectColumnAccess()) {
         dimensionSpec = columnArg.getSimpleExtraction().toDimensionSpec(null, inputType);
       } else {
-        final ExpressionVirtualColumn virtualColumn = columnArg.toVirtualColumn(
-            Calcites.makePrefixedName(name, "v"),
-            inputType,
-            plannerContext.getExprMacroTable()
+        VirtualColumn virtualColumn = querySignature.getOrCreateVirtualColumnForExpression(
+            plannerContext,
+            columnArg,
+            sqlTypeName
         );
         dimensionSpec = new DefaultDimensionSpec(virtualColumn.getOutputName(), null, inputType);
         virtualColumns.add(virtualColumn);
diff --git a/extensions-core/datasketches/src/main/java/org/apache/druid/query/aggregation/datasketches/quantiles/sql/DoublesSketchSqlAggregator.java b/extensions-core/datasketches/src/main/java/org/apache/druid/query/aggregation/datasketches/quantiles/sql/DoublesSketchSqlAggregator.java
index 0a0a05b..96e4c81 100644
--- a/extensions-core/datasketches/src/main/java/org/apache/druid/query/aggregation/datasketches/quantiles/sql/DoublesSketchSqlAggregator.java
+++ b/extensions-core/datasketches/src/main/java/org/apache/druid/query/aggregation/datasketches/quantiles/sql/DoublesSketchSqlAggregator.java
@@ -38,13 +38,13 @@ import org.apache.druid.query.aggregation.datasketches.quantiles.DoublesSketchAg
 import org.apache.druid.query.aggregation.datasketches.quantiles.DoublesSketchToQuantilePostAggregator;
 import org.apache.druid.query.aggregation.post.FieldAccessPostAggregator;
 import org.apache.druid.segment.VirtualColumn;
-import org.apache.druid.segment.column.ValueType;
 import org.apache.druid.segment.virtual.ExpressionVirtualColumn;
 import org.apache.druid.sql.calcite.aggregation.Aggregation;
 import org.apache.druid.sql.calcite.aggregation.SqlAggregator;
 import org.apache.druid.sql.calcite.expression.DruidExpression;
 import org.apache.druid.sql.calcite.expression.Expressions;
 import org.apache.druid.sql.calcite.planner.PlannerContext;
+import org.apache.druid.sql.calcite.rel.DruidQuerySignature;
 import org.apache.druid.sql.calcite.table.RowSignature;
 
 import javax.annotation.Nullable;
@@ -66,7 +66,7 @@ public class DoublesSketchSqlAggregator implements SqlAggregator
   @Override
   public Aggregation toDruidAggregation(
       final PlannerContext plannerContext,
-      final RowSignature rowSignature,
+      final DruidQuerySignature querySignature,
       final RexBuilder rexBuilder,
       final String name,
       final AggregateCall aggregateCall,
@@ -75,6 +75,7 @@ public class DoublesSketchSqlAggregator implements SqlAggregator
       final boolean finalizeAggregations
   )
   {
+    final RowSignature rowSignature = querySignature.getRowSignature();
     final DruidExpression input = Expressions.toDruidExpression(
         plannerContext,
         rowSignature,
@@ -178,10 +179,10 @@ public class DoublesSketchSqlAggregator implements SqlAggregator
           k
       );
     } else {
-      final ExpressionVirtualColumn virtualColumn = input.toVirtualColumn(
-          StringUtils.format("%s:v", name),
-          ValueType.FLOAT,
-          plannerContext.getExprMacroTable()
+      VirtualColumn virtualColumn = querySignature.getOrCreateVirtualColumnForExpression(
+          plannerContext,
+          input,
+          SqlTypeName.FLOAT
       );
       virtualColumns.add(virtualColumn);
       aggregatorFactory = new DoublesSketchAggregatorFactory(
diff --git a/extensions-core/datasketches/src/main/java/org/apache/druid/query/aggregation/datasketches/theta/sql/ThetaSketchSqlAggregator.java b/extensions-core/datasketches/src/main/java/org/apache/druid/query/aggregation/datasketches/theta/sql/ThetaSketchSqlAggregator.java
index f202c1a..339f169 100644
--- a/extensions-core/datasketches/src/main/java/org/apache/druid/query/aggregation/datasketches/theta/sql/ThetaSketchSqlAggregator.java
+++ b/extensions-core/datasketches/src/main/java/org/apache/druid/query/aggregation/datasketches/theta/sql/ThetaSketchSqlAggregator.java
@@ -41,13 +41,13 @@ import org.apache.druid.query.dimension.DefaultDimensionSpec;
 import org.apache.druid.query.dimension.DimensionSpec;
 import org.apache.druid.segment.VirtualColumn;
 import org.apache.druid.segment.column.ValueType;
-import org.apache.druid.segment.virtual.ExpressionVirtualColumn;
 import org.apache.druid.sql.calcite.aggregation.Aggregation;
 import org.apache.druid.sql.calcite.aggregation.SqlAggregator;
 import org.apache.druid.sql.calcite.expression.DruidExpression;
 import org.apache.druid.sql.calcite.expression.Expressions;
 import org.apache.druid.sql.calcite.planner.Calcites;
 import org.apache.druid.sql.calcite.planner.PlannerContext;
+import org.apache.druid.sql.calcite.rel.DruidQuerySignature;
 import org.apache.druid.sql.calcite.table.RowSignature;
 
 import javax.annotation.Nullable;
@@ -70,7 +70,7 @@ public class ThetaSketchSqlAggregator implements SqlAggregator
   @Override
   public Aggregation toDruidAggregation(
       PlannerContext plannerContext,
-      RowSignature rowSignature,
+      DruidQuerySignature querySignature,
       RexBuilder rexBuilder,
       String name,
       AggregateCall aggregateCall,
@@ -79,6 +79,7 @@ public class ThetaSketchSqlAggregator implements SqlAggregator
       boolean finalizeAggregations
   )
   {
+    final RowSignature rowSignature = querySignature.getRowSignature();
     // Don't use Aggregations.getArgumentsForSimpleAggregator, since it won't let us use direct column access
     // for string columns.
     final RexNode columnRexNode = Expressions.fromFieldAccess(
@@ -135,10 +136,10 @@ public class ThetaSketchSqlAggregator implements SqlAggregator
       if (columnArg.isDirectColumnAccess()) {
         dimensionSpec = columnArg.getSimpleExtraction().toDimensionSpec(null, inputType);
       } else {
-        final ExpressionVirtualColumn virtualColumn = columnArg.toVirtualColumn(
-            Calcites.makePrefixedName(name, "v"),
-            inputType,
-            plannerContext.getExprMacroTable()
+        VirtualColumn virtualColumn = querySignature.getOrCreateVirtualColumnForExpression(
+            plannerContext,
+            columnArg,
+            sqlTypeName
         );
         dimensionSpec = new DefaultDimensionSpec(virtualColumn.getOutputName(), null, inputType);
         virtualColumns.add(virtualColumn);
diff --git a/extensions-core/datasketches/src/test/java/org/apache/druid/query/aggregation/datasketches/hll/sql/HllSketchSqlAggregatorTest.java b/extensions-core/datasketches/src/test/java/org/apache/druid/query/aggregation/datasketches/hll/sql/HllSketchSqlAggregatorTest.java
index da70643..70cccba 100644
--- a/extensions-core/datasketches/src/test/java/org/apache/druid/query/aggregation/datasketches/hll/sql/HllSketchSqlAggregatorTest.java
+++ b/extensions-core/datasketches/src/test/java/org/apache/druid/query/aggregation/datasketches/hll/sql/HllSketchSqlAggregatorTest.java
@@ -246,13 +246,13 @@ public class HllSketchSqlAggregatorTest extends CalciteTestBase
               .granularity(Granularities.ALL)
               .virtualColumns(
                   new ExpressionVirtualColumn(
-                      "a3:v",
+                      "v0",
                       "substring(\"dim2\", 0, 1)",
                       ValueType.STRING,
                       TestExprMacroTable.INSTANCE
                   ),
                   new ExpressionVirtualColumn(
-                      "a4:v",
+                      "v1",
                       "concat(substring(\"dim2\", 0, 1),'x')",
                       ValueType.STRING,
                       TestExprMacroTable.INSTANCE
@@ -278,13 +278,13 @@ public class HllSketchSqlAggregatorTest extends CalciteTestBase
                       ),
                       new HllSketchBuildAggregatorFactory(
                           "a3",
-                          "a3:v",
+                          "v0",
                           null,
                           null
                       ),
                       new HllSketchBuildAggregatorFactory(
                           "a4",
-                          "a4:v",
+                          "v1",
                           null,
                           null
                       ),
@@ -330,7 +330,7 @@ public class HllSketchSqlAggregatorTest extends CalciteTestBase
                                                      .setGranularity(Granularities.ALL)
                                                      .setVirtualColumns(
                                                          new ExpressionVirtualColumn(
-                                                             "d0:v",
+                                                             "v0",
                                                              "timestamp_floor(\"__time\",'P1D',null,'UTC')",
                                                              ValueType.LONG,
                                                              TestExprMacroTable.INSTANCE
@@ -339,8 +339,8 @@ public class HllSketchSqlAggregatorTest extends CalciteTestBase
                                                      .setDimensions(
                                                          Collections.singletonList(
                                                              new DefaultDimensionSpec(
-                                                                 "d0:v",
-                                                                 "d0",
+                                                                 "v0",
+                                                                 "v0",
                                                                  ValueType.LONG
                                                              )
                                                          )
diff --git a/extensions-core/datasketches/src/test/java/org/apache/druid/query/aggregation/datasketches/quantiles/sql/DoublesSketchSqlAggregatorTest.java b/extensions-core/datasketches/src/test/java/org/apache/druid/query/aggregation/datasketches/quantiles/sql/DoublesSketchSqlAggregatorTest.java
index 2b4e5e0..e51c2de 100644
--- a/extensions-core/datasketches/src/test/java/org/apache/druid/query/aggregation/datasketches/quantiles/sql/DoublesSketchSqlAggregatorTest.java
+++ b/extensions-core/datasketches/src/test/java/org/apache/druid/query/aggregation/datasketches/quantiles/sql/DoublesSketchSqlAggregatorTest.java
@@ -228,7 +228,7 @@ public class DoublesSketchSqlAggregatorTest extends CalciteTestBase
               .granularity(Granularities.ALL)
               .virtualColumns(
                   new ExpressionVirtualColumn(
-                      "a4:v",
+                      "v0",
                       "(\"m1\" * 2)",
                       ValueType.FLOAT,
                       TestExprMacroTable.INSTANCE
@@ -238,7 +238,7 @@ public class DoublesSketchSqlAggregatorTest extends CalciteTestBase
                   new DoublesSketchAggregatorFactory("a0:agg", "m1", null),
                   new DoublesSketchAggregatorFactory("a1:agg", "m1", 64),
                   new DoublesSketchAggregatorFactory("a2:agg", "m1", 256),
-                  new DoublesSketchAggregatorFactory("a4:agg", "a4:v", null),
+                  new DoublesSketchAggregatorFactory("a4:agg", "v0", null),
                   new FilteredAggregatorFactory(
                       new DoublesSketchAggregatorFactory("a5:agg", "m1", null),
                       new SelectorDimFilter("dim1", "abc", null)
diff --git a/extensions-core/datasketches/src/test/java/org/apache/druid/query/aggregation/datasketches/theta/sql/ThetaSketchSqlAggregatorTest.java b/extensions-core/datasketches/src/test/java/org/apache/druid/query/aggregation/datasketches/theta/sql/ThetaSketchSqlAggregatorTest.java
index 939e396..af418f3 100644
--- a/extensions-core/datasketches/src/test/java/org/apache/druid/query/aggregation/datasketches/theta/sql/ThetaSketchSqlAggregatorTest.java
+++ b/extensions-core/datasketches/src/test/java/org/apache/druid/query/aggregation/datasketches/theta/sql/ThetaSketchSqlAggregatorTest.java
@@ -246,13 +246,13 @@ public class ThetaSketchSqlAggregatorTest extends CalciteTestBase
               .granularity(Granularities.ALL)
               .virtualColumns(
                   new ExpressionVirtualColumn(
-                      "a3:v",
+                      "v0",
                       "substring(\"dim2\", 0, 1)",
                       ValueType.STRING,
                       TestExprMacroTable.INSTANCE
                   ),
                   new ExpressionVirtualColumn(
-                      "a4:v",
+                      "v1",
                       "concat(substring(\"dim2\", 0, 1),'x')",
                       ValueType.STRING,
                       TestExprMacroTable.INSTANCE
@@ -282,7 +282,7 @@ public class ThetaSketchSqlAggregatorTest extends CalciteTestBase
                       ),
                       new SketchMergeAggregatorFactory(
                           "a3",
-                          "a3:v",
+                          "v0",
                           null,
                           null,
                           null,
@@ -290,7 +290,7 @@ public class ThetaSketchSqlAggregatorTest extends CalciteTestBase
                       ),
                       new SketchMergeAggregatorFactory(
                           "a4",
-                          "a4:v",
+                          "v1",
                           null,
                           null,
                           null,
@@ -337,7 +337,7 @@ public class ThetaSketchSqlAggregatorTest extends CalciteTestBase
                                                      .setGranularity(Granularities.ALL)
                                                      .setVirtualColumns(
                                                          new ExpressionVirtualColumn(
-                                                             "d0:v",
+                                                             "v0",
                                                              "timestamp_floor(\"__time\",'P1D',null,'UTC')",
                                                              ValueType.LONG,
                                                              TestExprMacroTable.INSTANCE
@@ -346,8 +346,8 @@ public class ThetaSketchSqlAggregatorTest extends CalciteTestBase
                                                      .setDimensions(
                                                          Collections.singletonList(
                                                              new DefaultDimensionSpec(
-                                                                 "d0:v",
-                                                                 "d0",
+                                                                 "v0",
+                                                                 "v0",
                                                                  ValueType.LONG
                                                              )
                                                          )
diff --git a/extensions-core/druid-bloom-filter/src/main/java/org/apache/druid/query/aggregation/bloom/sql/BloomFilterSqlAggregator.java b/extensions-core/druid-bloom-filter/src/main/java/org/apache/druid/query/aggregation/bloom/sql/BloomFilterSqlAggregator.java
index 0a37dad..48415d6 100644
--- a/extensions-core/druid-bloom-filter/src/main/java/org/apache/druid/query/aggregation/bloom/sql/BloomFilterSqlAggregator.java
+++ b/extensions-core/druid-bloom-filter/src/main/java/org/apache/druid/query/aggregation/bloom/sql/BloomFilterSqlAggregator.java
@@ -46,6 +46,7 @@ import org.apache.druid.sql.calcite.expression.DruidExpression;
 import org.apache.druid.sql.calcite.expression.Expressions;
 import org.apache.druid.sql.calcite.planner.Calcites;
 import org.apache.druid.sql.calcite.planner.PlannerContext;
+import org.apache.druid.sql.calcite.rel.DruidQuerySignature;
 import org.apache.druid.sql.calcite.table.RowSignature;
 
 import javax.annotation.Nullable;
@@ -67,7 +68,7 @@ public class BloomFilterSqlAggregator implements SqlAggregator
   @Override
   public Aggregation toDruidAggregation(
       PlannerContext plannerContext,
-      RowSignature rowSignature,
+      DruidQuerySignature querySignature,
       RexBuilder rexBuilder,
       String name,
       AggregateCall aggregateCall,
@@ -76,6 +77,7 @@ public class BloomFilterSqlAggregator implements SqlAggregator
       boolean finalizeAggregations
   )
   {
+    final RowSignature rowSignature = querySignature.getRowSignature();
     final RexNode inputOperand = Expressions.fromFieldAccess(
         rowSignature,
         project,
@@ -166,10 +168,10 @@ public class BloomFilterSqlAggregator implements SqlAggregator
           input.getSimpleExtraction().getExtractionFn()
       );
     } else {
-      final ExpressionVirtualColumn virtualColumn = input.toVirtualColumn(
-          StringUtils.format("%s:v", aggName),
-          valueType,
-          plannerContext.getExprMacroTable()
+      VirtualColumn virtualColumn = querySignature.getOrCreateVirtualColumnForExpression(
+          plannerContext,
+          input,
+          inputOperand.getType().getSqlTypeName()
       );
       virtualColumns.add(virtualColumn);
       spec = new DefaultDimensionSpec(virtualColumn.getOutputName(), virtualColumn.getOutputName());
diff --git a/extensions-core/druid-bloom-filter/src/main/java/org/apache/druid/query/filter/sql/BloomFilterOperatorConversion.java b/extensions-core/druid-bloom-filter/src/main/java/org/apache/druid/query/filter/sql/BloomFilterOperatorConversion.java
index 794d0d8..a27ec4f 100644
--- a/extensions-core/druid-bloom-filter/src/main/java/org/apache/druid/query/filter/sql/BloomFilterOperatorConversion.java
+++ b/extensions-core/druid-bloom-filter/src/main/java/org/apache/druid/query/filter/sql/BloomFilterOperatorConversion.java
@@ -33,12 +33,13 @@ import org.apache.druid.query.filter.BloomDimFilter;
 import org.apache.druid.query.filter.BloomKFilter;
 import org.apache.druid.query.filter.BloomKFilterHolder;
 import org.apache.druid.query.filter.DimFilter;
+import org.apache.druid.segment.VirtualColumn;
 import org.apache.druid.sql.calcite.expression.DirectOperatorConversion;
 import org.apache.druid.sql.calcite.expression.DruidExpression;
 import org.apache.druid.sql.calcite.expression.Expressions;
 import org.apache.druid.sql.calcite.expression.OperatorConversions;
 import org.apache.druid.sql.calcite.planner.PlannerContext;
-import org.apache.druid.sql.calcite.table.RowSignature;
+import org.apache.druid.sql.calcite.rel.DruidQuerySignature;
 
 import javax.annotation.Nullable;
 import java.io.IOException;
@@ -67,17 +68,17 @@ public class BloomFilterOperatorConversion extends DirectOperatorConversion
   @Override
   public DimFilter toDruidFilter(
       final PlannerContext plannerContext,
-      final RowSignature rowSignature,
+      final DruidQuerySignature querySignature,
       final RexNode rexNode
   )
   {
     final List<RexNode> operands = ((RexCall) rexNode).getOperands();
     final DruidExpression druidExpression = Expressions.toDruidExpression(
         plannerContext,
-        rowSignature,
+        querySignature.getRowSignature(),
         operands.get(0)
     );
-    if (druidExpression == null || !druidExpression.isSimpleExtraction()) {
+    if (druidExpression == null) {
       return null;
     }
 
@@ -100,8 +101,19 @@ public class BloomFilterOperatorConversion extends DirectOperatorConversion
           druidExpression.getSimpleExtraction().getExtractionFn()
       );
     } else {
-      // expression virtual columns not currently supported
-      return null;
+      VirtualColumn virtualColumn = querySignature.getOrCreateVirtualColumnForExpression(
+          plannerContext,
+          druidExpression,
+          operands.get(0).getType().getSqlTypeName()
+      );
+      if (virtualColumn == null) {
+        return null;
+      }
+      return new BloomDimFilter(
+          virtualColumn.getOutputName(),
+          holder,
+          null
+      );
     }
   }
 }
diff --git a/extensions-core/druid-bloom-filter/src/test/java/org/apache/druid/query/aggregation/bloom/sql/BloomFilterSqlAggregatorTest.java b/extensions-core/druid-bloom-filter/src/test/java/org/apache/druid/query/aggregation/bloom/sql/BloomFilterSqlAggregatorTest.java
index 6b218bb..ceb8c42 100644
--- a/extensions-core/druid-bloom-filter/src/test/java/org/apache/druid/query/aggregation/bloom/sql/BloomFilterSqlAggregatorTest.java
+++ b/extensions-core/druid-bloom-filter/src/test/java/org/apache/druid/query/aggregation/bloom/sql/BloomFilterSqlAggregatorTest.java
@@ -493,7 +493,7 @@ public class BloomFilterSqlAggregatorTest
               .granularity(Granularities.ALL)
               .virtualColumns(
                   new ExpressionVirtualColumn(
-                    "a0:agg:v",
+                    "v0",
                     "(\"l1\" * 2)",
                     ValueType.LONG,
                     TestExprMacroTable.INSTANCE
@@ -503,7 +503,7 @@ public class BloomFilterSqlAggregatorTest
                   ImmutableList.of(
                     new BloomFilterAggregatorFactory(
                         "a0:agg",
-                        new DefaultDimensionSpec("a0:agg:v", "a0:agg:v"),
+                        new DefaultDimensionSpec("v0", "v0"),
                         TEST_NUM_ENTRIES
                     )
                 )
@@ -556,7 +556,7 @@ public class BloomFilterSqlAggregatorTest
               .granularity(Granularities.ALL)
               .virtualColumns(
                   new ExpressionVirtualColumn(
-                    "a0:agg:v",
+                    "v0",
                     "(\"f1\" * 2)",
                     ValueType.FLOAT,
                     TestExprMacroTable.INSTANCE
@@ -566,7 +566,7 @@ public class BloomFilterSqlAggregatorTest
                   ImmutableList.of(
                     new BloomFilterAggregatorFactory(
                         "a0:agg",
-                        new DefaultDimensionSpec("a0:agg:v", "a0:agg:v"),
+                        new DefaultDimensionSpec("v0", "v0"),
                         TEST_NUM_ENTRIES
                     )
                 )
@@ -619,7 +619,7 @@ public class BloomFilterSqlAggregatorTest
               .granularity(Granularities.ALL)
               .virtualColumns(
                   new ExpressionVirtualColumn(
-                    "a0:agg:v",
+                    "v0",
                     "(\"d1\" * 2)",
                     ValueType.DOUBLE,
                     TestExprMacroTable.INSTANCE
@@ -629,7 +629,7 @@ public class BloomFilterSqlAggregatorTest
                   ImmutableList.of(
                     new BloomFilterAggregatorFactory(
                         "a0:agg",
-                        new DefaultDimensionSpec("a0:agg:v", "a0:agg:v"),
+                        new DefaultDimensionSpec("v0", "v0"),
                         TEST_NUM_ENTRIES
                     )
                 )
diff --git a/extensions-core/druid-bloom-filter/src/test/java/org/apache/druid/query/filter/sql/BloomDimFilterSqlTest.java b/extensions-core/druid-bloom-filter/src/test/java/org/apache/druid/query/filter/sql/BloomDimFilterSqlTest.java
index 335599a..c8243dc 100644
--- a/extensions-core/druid-bloom-filter/src/test/java/org/apache/druid/query/filter/sql/BloomDimFilterSqlTest.java
+++ b/extensions-core/druid-bloom-filter/src/test/java/org/apache/druid/query/filter/sql/BloomDimFilterSqlTest.java
@@ -46,6 +46,7 @@ import org.apache.druid.query.filter.ExpressionDimFilter;
 import org.apache.druid.query.filter.OrDimFilter;
 import org.apache.druid.query.lookup.LookupReferencesManager;
 import org.apache.druid.segment.TestHelper;
+import org.apache.druid.segment.column.ValueType;
 import org.apache.druid.server.security.AuthenticationResult;
 import org.apache.druid.sql.calcite.BaseCalciteQueryTest;
 import org.apache.druid.sql.calcite.filtration.Filtration;
@@ -130,7 +131,7 @@ public class BloomDimFilterSqlTest extends BaseCalciteQueryTest
   }
 
   @Test
-  public void testBloomFilterVirtualColumn() throws Exception
+  public void testBloomFilterExprFilter() throws Exception
   {
     BloomKFilter filter = new BloomKFilter(1500);
     filter.addString("a-foo");
@@ -141,17 +142,17 @@ public class BloomDimFilterSqlTest extends BaseCalciteQueryTest
     byte[] bytes = BloomFilterSerializersModule.bloomKFilterToBytes(filter);
     String base64 = StringUtils.encodeBase64String(bytes);
 
+    // fool the planner to make an expression virtual column to test bloom filter Druid expression
     testQuery(
-        StringUtils.format("SELECT COUNT(*) FROM druid.foo WHERE bloom_filter_test(concat(dim2, '-foo'), '%s')", base64),
+        StringUtils.format("SELECT COUNT(*) FROM druid.foo WHERE bloom_filter_test(concat(dim2, '-foo'), '%s') = TRUE", base64),
         ImmutableList.of(
             Druids.newTimeseriesQueryBuilder()
                   .dataSource(CalciteTests.DATASOURCE1)
                   .intervals(querySegmentSpec(Filtration.eternity()))
                   .granularity(Granularities.ALL)
-                  .virtualColumns()
                   .filters(
                       new ExpressionDimFilter(
-                          StringUtils.format("bloom_filter_test(concat(\"dim2\",'-foo'),'%s')", base64),
+                          StringUtils.format("(bloom_filter_test(concat(\"dim2\",'-foo'),'%s') == 1)", base64),
                           createExprMacroTable()
                       )
                   )
@@ -166,10 +167,40 @@ public class BloomDimFilterSqlTest extends BaseCalciteQueryTest
   }
 
   @Test
+  public void testBloomFilterVirtualColumn() throws Exception
+  {
+    BloomKFilter filter = new BloomKFilter(1500);
+    filter.addString("def-foo");
+    byte[] bytes = BloomFilterSerializersModule.bloomKFilterToBytes(filter);
+    String base64 = StringUtils.encodeBase64String(bytes);
+
+    testQuery(
+        StringUtils.format("SELECT COUNT(*) FROM druid.foo WHERE bloom_filter_test(concat(dim1, '-foo'), '%s')", base64),
+        ImmutableList.of(
+            Druids.newTimeseriesQueryBuilder()
+                  .dataSource(CalciteTests.DATASOURCE1)
+                  .intervals(querySegmentSpec(Filtration.eternity()))
+                  .granularity(Granularities.ALL)
+                  .virtualColumns(expressionVirtualColumn("v0", "concat(\"dim1\",'-foo')", ValueType.STRING))
+                  .filters(
+                      new BloomDimFilter("v0", BloomKFilterHolder.fromBloomKFilter(filter), null)
+                  )
+                  .aggregators(aggregators(new CountAggregatorFactory("a0")))
+                  .context(TIMESERIES_CONTEXT_DEFAULT)
+                  .build()
+        ),
+        ImmutableList.of(
+            new Object[]{1L}
+        )
+    );
+  }
+
+
+  @Test
   public void testBloomFilterVirtualColumnNumber() throws Exception
   {
     BloomKFilter filter = new BloomKFilter(1500);
-    filter.addDouble(20.2);
+    filter.addFloat(20.2f);
     byte[] bytes = BloomFilterSerializersModule.bloomKFilterToBytes(filter);
     String base64 = StringUtils.encodeBase64String(bytes);
 
@@ -180,12 +211,11 @@ public class BloomDimFilterSqlTest extends BaseCalciteQueryTest
                   .dataSource(CalciteTests.DATASOURCE1)
                   .intervals(querySegmentSpec(Filtration.eternity()))
                   .granularity(Granularities.ALL)
-                  .virtualColumns()
+                  .virtualColumns(
+                      expressionVirtualColumn("v0", "(2 * CAST(\"dim1\", 'DOUBLE'))", ValueType.FLOAT)
+                  )
                   .filters(
-                      new ExpressionDimFilter(
-                          StringUtils.format("bloom_filter_test((2 * CAST(\"dim1\", 'DOUBLE')),'%s')", base64),
-                          createExprMacroTable()
-                      )
+                      new BloomDimFilter("v0", BloomKFilterHolder.fromBloomKFilter(filter), null)
                   )
                   .aggregators(aggregators(new CountAggregatorFactory("a0")))
                   .context(TIMESERIES_CONTEXT_DEFAULT)
diff --git a/extensions-core/histogram/src/main/java/org/apache/druid/query/aggregation/histogram/sql/FixedBucketsHistogramQuantileSqlAggregator.java b/extensions-core/histogram/src/main/java/org/apache/druid/query/aggregation/histogram/sql/FixedBucketsHistogramQuantileSqlAggregator.java
index 3a28eda..4c92082 100644
--- a/extensions-core/histogram/src/main/java/org/apache/druid/query/aggregation/histogram/sql/FixedBucketsHistogramQuantileSqlAggregator.java
+++ b/extensions-core/histogram/src/main/java/org/apache/druid/query/aggregation/histogram/sql/FixedBucketsHistogramQuantileSqlAggregator.java
@@ -38,13 +38,13 @@ import org.apache.druid.query.aggregation.histogram.FixedBucketsHistogram;
 import org.apache.druid.query.aggregation.histogram.FixedBucketsHistogramAggregatorFactory;
 import org.apache.druid.query.aggregation.histogram.QuantilePostAggregator;
 import org.apache.druid.segment.VirtualColumn;
-import org.apache.druid.segment.column.ValueType;
 import org.apache.druid.segment.virtual.ExpressionVirtualColumn;
 import org.apache.druid.sql.calcite.aggregation.Aggregation;
 import org.apache.druid.sql.calcite.aggregation.SqlAggregator;
 import org.apache.druid.sql.calcite.expression.DruidExpression;
 import org.apache.druid.sql.calcite.expression.Expressions;
 import org.apache.druid.sql.calcite.planner.PlannerContext;
+import org.apache.druid.sql.calcite.rel.DruidQuerySignature;
 import org.apache.druid.sql.calcite.table.RowSignature;
 
 import javax.annotation.Nullable;
@@ -66,7 +66,7 @@ public class FixedBucketsHistogramQuantileSqlAggregator implements SqlAggregator
   @Override
   public Aggregation toDruidAggregation(
       PlannerContext plannerContext,
-      RowSignature rowSignature,
+      DruidQuerySignature querySignature,
       RexBuilder rexBuilder,
       String name,
       AggregateCall aggregateCall,
@@ -75,6 +75,7 @@ public class FixedBucketsHistogramQuantileSqlAggregator implements SqlAggregator
       boolean finalizeAggregations
   )
   {
+    final RowSignature rowSignature = querySignature.getRowSignature();
     final DruidExpression input = Expressions.toDruidExpression(
         plannerContext,
         rowSignature,
@@ -233,10 +234,10 @@ public class FixedBucketsHistogramQuantileSqlAggregator implements SqlAggregator
           outlierHandlingMode
       );
     } else {
-      final ExpressionVirtualColumn virtualColumn = input.toVirtualColumn(
-          StringUtils.format("%s:v", name),
-          ValueType.FLOAT,
-          plannerContext.getExprMacroTable()
+      VirtualColumn virtualColumn = querySignature.getOrCreateVirtualColumnForExpression(
+          plannerContext,
+          input,
+          SqlTypeName.FLOAT
       );
       virtualColumns.add(virtualColumn);
       aggregatorFactory = new FixedBucketsHistogramAggregatorFactory(
diff --git a/extensions-core/histogram/src/main/java/org/apache/druid/query/aggregation/histogram/sql/QuantileSqlAggregator.java b/extensions-core/histogram/src/main/java/org/apache/druid/query/aggregation/histogram/sql/QuantileSqlAggregator.java
index 26ec44d..c00ad49 100644
--- a/extensions-core/histogram/src/main/java/org/apache/druid/query/aggregation/histogram/sql/QuantileSqlAggregator.java
+++ b/extensions-core/histogram/src/main/java/org/apache/druid/query/aggregation/histogram/sql/QuantileSqlAggregator.java
@@ -46,6 +46,7 @@ import org.apache.druid.sql.calcite.aggregation.SqlAggregator;
 import org.apache.druid.sql.calcite.expression.DruidExpression;
 import org.apache.druid.sql.calcite.expression.Expressions;
 import org.apache.druid.sql.calcite.planner.PlannerContext;
+import org.apache.druid.sql.calcite.rel.DruidQuerySignature;
 import org.apache.druid.sql.calcite.table.RowSignature;
 
 import javax.annotation.Nullable;
@@ -67,7 +68,7 @@ public class QuantileSqlAggregator implements SqlAggregator
   @Override
   public Aggregation toDruidAggregation(
       final PlannerContext plannerContext,
-      final RowSignature rowSignature,
+      DruidQuerySignature querySignature,
       final RexBuilder rexBuilder,
       final String name,
       final AggregateCall aggregateCall,
@@ -76,6 +77,7 @@ public class QuantileSqlAggregator implements SqlAggregator
       final boolean finalizeAggregations
   )
   {
+    final RowSignature rowSignature = querySignature.getRowSignature();
     final DruidExpression input = Expressions.toDruidExpression(
         plannerContext,
         rowSignature,
@@ -193,11 +195,8 @@ public class QuantileSqlAggregator implements SqlAggregator
         );
       }
     } else {
-      final ExpressionVirtualColumn virtualColumn = input.toVirtualColumn(
-          StringUtils.format("%s:v", name),
-          ValueType.FLOAT,
-          plannerContext.getExprMacroTable()
-      );
+      final VirtualColumn virtualColumn =
+          querySignature.getOrCreateVirtualColumnForExpression(plannerContext, input, SqlTypeName.FLOAT);
       virtualColumns.add(virtualColumn);
       aggregatorFactory = new ApproximateHistogramAggregatorFactory(
           histogramName,
diff --git a/extensions-core/histogram/src/test/java/org/apache/druid/query/aggregation/histogram/sql/FixedBucketsHistogramQuantileSqlAggregatorTest.java b/extensions-core/histogram/src/test/java/org/apache/druid/query/aggregation/histogram/sql/FixedBucketsHistogramQuantileSqlAggregatorTest.java
index 2bac9e0..c8f6b3e 100644
--- a/extensions-core/histogram/src/test/java/org/apache/druid/query/aggregation/histogram/sql/FixedBucketsHistogramQuantileSqlAggregatorTest.java
+++ b/extensions-core/histogram/src/test/java/org/apache/druid/query/aggregation/histogram/sql/FixedBucketsHistogramQuantileSqlAggregatorTest.java
@@ -231,7 +231,7 @@ public class FixedBucketsHistogramQuantileSqlAggregatorTest extends CalciteTestB
                            .granularity(Granularities.ALL)
                            .virtualColumns(
                                new ExpressionVirtualColumn(
-                                   "a4:v",
+                                   "v0",
                                    "(\"m1\" * 2)",
                                    ValueType.FLOAT,
                                    TestExprMacroTable.INSTANCE
@@ -242,7 +242,7 @@ public class FixedBucketsHistogramQuantileSqlAggregatorTest extends CalciteTestB
                                    "a0:agg", "m1", 20, 0.0d, 10.0d, FixedBucketsHistogram.OutlierHandlingMode.IGNORE
                                ),
                                new FixedBucketsHistogramAggregatorFactory(
-                                   "a4:agg", "a4:v", 40, 0.0d, 20.0d, FixedBucketsHistogram.OutlierHandlingMode.IGNORE
+                                   "a4:agg", "v0", 40, 0.0d, 20.0d, FixedBucketsHistogram.OutlierHandlingMode.IGNORE
                                ),
                                new FilteredAggregatorFactory(
                                    new FixedBucketsHistogramAggregatorFactory(
diff --git a/extensions-core/histogram/src/test/java/org/apache/druid/query/aggregation/histogram/sql/QuantileSqlAggregatorTest.java b/extensions-core/histogram/src/test/java/org/apache/druid/query/aggregation/histogram/sql/QuantileSqlAggregatorTest.java
index ee354e5..7ba5a5c 100644
--- a/extensions-core/histogram/src/test/java/org/apache/druid/query/aggregation/histogram/sql/QuantileSqlAggregatorTest.java
+++ b/extensions-core/histogram/src/test/java/org/apache/druid/query/aggregation/histogram/sql/QuantileSqlAggregatorTest.java
@@ -232,7 +232,7 @@ public class QuantileSqlAggregatorTest extends CalciteTestBase
               .granularity(Granularities.ALL)
               .virtualColumns(
                   new ExpressionVirtualColumn(
-                      "a4:v",
+                      "v0",
                       "(\"m1\" * 2)",
                       ValueType.FLOAT,
                       TestExprMacroTable.INSTANCE
@@ -241,7 +241,7 @@ public class QuantileSqlAggregatorTest extends CalciteTestBase
               .aggregators(ImmutableList.of(
                   new ApproximateHistogramAggregatorFactory("a0:agg", "m1", null, null, null, null),
                   new ApproximateHistogramAggregatorFactory("a2:agg", "m1", 200, null, null, null),
-                  new ApproximateHistogramAggregatorFactory("a4:agg", "a4:v", null, null, null, null),
+                  new ApproximateHistogramAggregatorFactory("a4:agg", "v0", null, null, null, null),
                   new FilteredAggregatorFactory(
                       new ApproximateHistogramAggregatorFactory("a5:agg", "m1", null, null, null, null),
                       new SelectorDimFilter("dim1", "abc", null)
diff --git a/sql/src/main/java/org/apache/druid/sql/calcite/aggregation/Aggregation.java b/sql/src/main/java/org/apache/druid/sql/calcite/aggregation/Aggregation.java
index 559af3e..c987aed 100644
--- a/sql/src/main/java/org/apache/druid/sql/calcite/aggregation/Aggregation.java
+++ b/sql/src/main/java/org/apache/druid/sql/calcite/aggregation/Aggregation.java
@@ -31,7 +31,7 @@ import org.apache.druid.query.filter.AndDimFilter;
 import org.apache.druid.query.filter.DimFilter;
 import org.apache.druid.segment.VirtualColumn;
 import org.apache.druid.sql.calcite.filtration.Filtration;
-import org.apache.druid.sql.calcite.table.RowSignature;
+import org.apache.druid.sql.calcite.rel.DruidQuerySignature;
 
 import javax.annotation.Nullable;
 import java.util.ArrayList;
@@ -80,12 +80,6 @@ public class Aggregation
                         ? postAggregator.getName()
                         : Iterables.getOnlyElement(aggregatorFactories).getName();
 
-    for (VirtualColumn virtualColumn : virtualColumns) {
-      if (!virtualColumn.getOutputName().startsWith(name)) {
-        throw new IAE("VirtualColumn[%s] not prefixed under[%s]", virtualColumn.getOutputName(), name);
-      }
-    }
-
     for (AggregatorFactory aggregatorFactory : aggregatorFactories) {
       if (!aggregatorFactory.getName().startsWith(name)) {
         throw new IAE("Aggregator[%s] not prefixed under[%s]", aggregatorFactory.getName(), name);
@@ -156,12 +150,13 @@ public class Aggregation
            : Iterables.getOnlyElement(aggregatorFactories).getName();
   }
 
-  public Aggregation filter(final RowSignature sourceRowSignature, final DimFilter filter)
+  public Aggregation filter(final DruidQuerySignature querySignature, final DimFilter filter)
   {
     if (filter == null) {
       return this;
     }
 
+
     if (postAggregator != null) {
       // Verify that this Aggregation contains all input to its postAggregator.
       // If not, this "filter" call won't work right.
@@ -178,18 +173,29 @@ public class Aggregation
     }
 
     final DimFilter baseOptimizedFilter = Filtration.create(filter)
-                                                    .optimizeFilterOnly(sourceRowSignature)
+                                                    .optimizeFilterOnly(querySignature)
                                                     .getDimFilter();
 
+    Set<VirtualColumn> aggVirtualColumnsPlusFilterColumns = new HashSet<>(virtualColumns);
+    for (String column : baseOptimizedFilter.getRequiredColumns()) {
+      if (querySignature.isVirtualColumnDefined(column)) {
+        aggVirtualColumnsPlusFilterColumns.add(querySignature.getVirtualColumn(column));
+      }
+    }
     final List<AggregatorFactory> newAggregators = new ArrayList<>();
     for (AggregatorFactory agg : aggregatorFactories) {
       if (agg instanceof FilteredAggregatorFactory) {
         final FilteredAggregatorFactory filteredAgg = (FilteredAggregatorFactory) agg;
+        for (String column : filteredAgg.getFilter().getRequiredColumns()) {
+          if (querySignature.isVirtualColumnDefined(column)) {
+            aggVirtualColumnsPlusFilterColumns.add(querySignature.getVirtualColumn(column));
+          }
+        }
         newAggregators.add(
             new FilteredAggregatorFactory(
                 filteredAgg.getAggregator(),
                 Filtration.create(new AndDimFilter(ImmutableList.of(filteredAgg.getFilter(), baseOptimizedFilter)))
-                          .optimizeFilterOnly(sourceRowSignature)
+                          .optimizeFilterOnly(querySignature)
                           .getDimFilter()
             )
         );
@@ -198,7 +204,7 @@ public class Aggregation
       }
     }
 
-    return new Aggregation(virtualColumns, newAggregators, postAggregator);
+    return new Aggregation(new ArrayList<>(aggVirtualColumnsPlusFilterColumns), newAggregators, postAggregator);
   }
 
   @Override
diff --git a/sql/src/main/java/org/apache/druid/sql/calcite/aggregation/DimensionExpression.java b/sql/src/main/java/org/apache/druid/sql/calcite/aggregation/DimensionExpression.java
index 7fba517..72f296e 100644
--- a/sql/src/main/java/org/apache/druid/sql/calcite/aggregation/DimensionExpression.java
+++ b/sql/src/main/java/org/apache/druid/sql/calcite/aggregation/DimensionExpression.java
@@ -19,17 +19,11 @@
 
 package org.apache.druid.sql.calcite.aggregation;
 
-import com.google.common.collect.ImmutableList;
-import org.apache.druid.math.expr.ExprMacroTable;
 import org.apache.druid.query.dimension.DefaultDimensionSpec;
 import org.apache.druid.query.dimension.DimensionSpec;
-import org.apache.druid.segment.VirtualColumn;
 import org.apache.druid.segment.column.ValueType;
 import org.apache.druid.sql.calcite.expression.DruidExpression;
-import org.apache.druid.sql.calcite.planner.Calcites;
 
-import javax.annotation.Nullable;
-import java.util.List;
 import java.util.Objects;
 
 public class DimensionExpression
@@ -64,25 +58,10 @@ public class DimensionExpression
     if (expression.isSimpleExtraction()) {
       return expression.getSimpleExtraction().toDimensionSpec(outputName, outputType);
     } else {
-      return new DefaultDimensionSpec(getVirtualColumnName(), getOutputName(), outputType);
+      return new DefaultDimensionSpec(getOutputName(), getOutputName(), outputType);
     }
   }
 
-  public List<VirtualColumn> getVirtualColumns(final ExprMacroTable macroTable)
-  {
-    if (expression.isSimpleExtraction()) {
-      return ImmutableList.of();
-    } else {
-      return ImmutableList.of(expression.toVirtualColumn(getVirtualColumnName(), outputType, macroTable));
-    }
-  }
-
-  @Nullable
-  public String getVirtualColumnName()
-  {
-    return expression.isSimpleExtraction() ? null : Calcites.makePrefixedName(outputName, "v");
-  }
-
   @Override
   public boolean equals(final Object o)
   {
diff --git a/sql/src/main/java/org/apache/druid/sql/calcite/aggregation/SqlAggregator.java b/sql/src/main/java/org/apache/druid/sql/calcite/aggregation/SqlAggregator.java
index 23240c6..1c15474 100644
--- a/sql/src/main/java/org/apache/druid/sql/calcite/aggregation/SqlAggregator.java
+++ b/sql/src/main/java/org/apache/druid/sql/calcite/aggregation/SqlAggregator.java
@@ -24,7 +24,7 @@ import org.apache.calcite.rel.core.Project;
 import org.apache.calcite.rex.RexBuilder;
 import org.apache.calcite.sql.SqlAggFunction;
 import org.apache.druid.sql.calcite.planner.PlannerContext;
-import org.apache.druid.sql.calcite.table.RowSignature;
+import org.apache.druid.sql.calcite.rel.DruidQuerySignature;
 
 import javax.annotation.Nullable;
 import java.util.List;
@@ -46,7 +46,7 @@ public interface SqlAggregator
    * they will be applied to your aggregator in a later step.
    *
    * @param plannerContext       SQL planner context
-   * @param rowSignature         signature of the rows being aggregated
+   * @param querySignature       signature of the rows row signature and re-usable virtual column references
    * @param rexBuilder           a rexBuilder, in case you need one
    * @param name                 desired output name of the aggregation
    * @param aggregateCall        aggregate call object
@@ -62,10 +62,9 @@ public interface SqlAggregator
   @Nullable
   Aggregation toDruidAggregation(
       PlannerContext plannerContext,
-      RowSignature rowSignature,
+      DruidQuerySignature querySignature,
       RexBuilder rexBuilder,
-      String name,
-      AggregateCall aggregateCall,
+      String name, AggregateCall aggregateCall,
       Project project,
       List<Aggregation> existingAggregations,
       boolean finalizeAggregations
diff --git a/sql/src/main/java/org/apache/druid/sql/calcite/aggregation/builtin/ApproxCountDistinctSqlAggregator.java b/sql/src/main/java/org/apache/druid/sql/calcite/aggregation/builtin/ApproxCountDistinctSqlAggregator.java
index df9f9e5..bc59a32 100644
--- a/sql/src/main/java/org/apache/druid/sql/calcite/aggregation/builtin/ApproxCountDistinctSqlAggregator.java
+++ b/sql/src/main/java/org/apache/druid/sql/calcite/aggregation/builtin/ApproxCountDistinctSqlAggregator.java
@@ -41,13 +41,13 @@ import org.apache.druid.query.dimension.DefaultDimensionSpec;
 import org.apache.druid.query.dimension.DimensionSpec;
 import org.apache.druid.segment.VirtualColumn;
 import org.apache.druid.segment.column.ValueType;
-import org.apache.druid.segment.virtual.ExpressionVirtualColumn;
 import org.apache.druid.sql.calcite.aggregation.Aggregation;
 import org.apache.druid.sql.calcite.aggregation.SqlAggregator;
 import org.apache.druid.sql.calcite.expression.DruidExpression;
 import org.apache.druid.sql.calcite.expression.Expressions;
 import org.apache.druid.sql.calcite.planner.Calcites;
 import org.apache.druid.sql.calcite.planner.PlannerContext;
+import org.apache.druid.sql.calcite.rel.DruidQuerySignature;
 import org.apache.druid.sql.calcite.table.RowSignature;
 
 import javax.annotation.Nullable;
@@ -70,7 +70,7 @@ public class ApproxCountDistinctSqlAggregator implements SqlAggregator
   @Override
   public Aggregation toDruidAggregation(
       final PlannerContext plannerContext,
-      final RowSignature rowSignature,
+      DruidQuerySignature querySignature,
       final RexBuilder rexBuilder,
       final String name,
       final AggregateCall aggregateCall,
@@ -79,6 +79,7 @@ public class ApproxCountDistinctSqlAggregator implements SqlAggregator
       final boolean finalizeAggregations
   )
   {
+    final RowSignature rowSignature = querySignature.getRowSignature();
     // Don't use Aggregations.getArgumentsForSimpleAggregator, since it won't let us use direct column access
     // for string columns.
     final RexNode rexNode = Expressions.fromFieldAccess(
@@ -92,7 +93,7 @@ public class ApproxCountDistinctSqlAggregator implements SqlAggregator
       return null;
     }
 
-    final List<VirtualColumn> virtualColumns = new ArrayList<>();
+    final List<VirtualColumn> myvirtualColumns = new ArrayList<>();
     final AggregatorFactory aggregatorFactory;
     final String aggregatorName = finalizeAggregations ? Calcites.makePrefixedName(name, "a") : name;
 
@@ -110,13 +111,10 @@ public class ApproxCountDistinctSqlAggregator implements SqlAggregator
       if (arg.isSimpleExtraction()) {
         dimensionSpec = arg.getSimpleExtraction().toDimensionSpec(null, inputType);
       } else {
-        final ExpressionVirtualColumn virtualColumn = arg.toVirtualColumn(
-            Calcites.makePrefixedName(name, "v"),
-            inputType,
-            plannerContext.getExprMacroTable()
-        );
+        VirtualColumn virtualColumn =
+            querySignature.getOrCreateVirtualColumnForExpression(plannerContext, arg, sqlTypeName);
         dimensionSpec = new DefaultDimensionSpec(virtualColumn.getOutputName(), null, inputType);
-        virtualColumns.add(virtualColumn);
+        myvirtualColumns.add(virtualColumn);
       }
 
       aggregatorFactory = new CardinalityAggregatorFactory(
@@ -129,7 +127,7 @@ public class ApproxCountDistinctSqlAggregator implements SqlAggregator
     }
 
     return Aggregation.create(
-        virtualColumns,
+        myvirtualColumns,
         Collections.singletonList(aggregatorFactory),
         finalizeAggregations ? new HyperUniqueFinalizingPostAggregator(name, aggregatorFactory.getName()) : null
     );
diff --git a/sql/src/main/java/org/apache/druid/sql/calcite/aggregation/builtin/AvgSqlAggregator.java b/sql/src/main/java/org/apache/druid/sql/calcite/aggregation/builtin/AvgSqlAggregator.java
index 30b7029..2761d0c 100644
--- a/sql/src/main/java/org/apache/druid/sql/calcite/aggregation/builtin/AvgSqlAggregator.java
+++ b/sql/src/main/java/org/apache/druid/sql/calcite/aggregation/builtin/AvgSqlAggregator.java
@@ -20,10 +20,7 @@
 package org.apache.druid.sql.calcite.aggregation.builtin;
 
 import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Iterables;
 import org.apache.calcite.rel.core.AggregateCall;
-import org.apache.calcite.rel.core.Project;
-import org.apache.calcite.rex.RexBuilder;
 import org.apache.calcite.sql.SqlAggFunction;
 import org.apache.calcite.sql.fun.SqlStdOperatorTable;
 import org.apache.calcite.sql.type.SqlTypeName;
@@ -34,17 +31,9 @@ import org.apache.druid.query.aggregation.post.ArithmeticPostAggregator;
 import org.apache.druid.query.aggregation.post.FieldAccessPostAggregator;
 import org.apache.druid.segment.column.ValueType;
 import org.apache.druid.sql.calcite.aggregation.Aggregation;
-import org.apache.druid.sql.calcite.aggregation.Aggregations;
-import org.apache.druid.sql.calcite.aggregation.SqlAggregator;
-import org.apache.druid.sql.calcite.expression.DruidExpression;
 import org.apache.druid.sql.calcite.planner.Calcites;
-import org.apache.druid.sql.calcite.planner.PlannerContext;
-import org.apache.druid.sql.calcite.table.RowSignature;
 
-import javax.annotation.Nullable;
-import java.util.List;
-
-public class AvgSqlAggregator implements SqlAggregator
+public class AvgSqlAggregator extends SimpleSqlAggregator
 {
   @Override
   public SqlAggFunction calciteFunction()
@@ -52,37 +41,16 @@ public class AvgSqlAggregator implements SqlAggregator
     return SqlStdOperatorTable.AVG;
   }
 
-  @Nullable
   @Override
-  public Aggregation toDruidAggregation(
-      final PlannerContext plannerContext,
-      final RowSignature rowSignature,
-      final RexBuilder rexBuilder,
+  Aggregation getAggregation(
       final String name,
       final AggregateCall aggregateCall,
-      final Project project,
-      final List<Aggregation> existingAggregations,
-      final boolean finalizeAggregations
+      final ExprMacroTable macroTable,
+      final String fieldName,
+      final String expression
   )
   {
-    if (aggregateCall.isDistinct()) {
-      return null;
-    }
-
-    final List<DruidExpression> arguments = Aggregations.getArgumentsForSimpleAggregator(
-        plannerContext,
-        rowSignature,
-        aggregateCall,
-        project
-    );
-
-    if (arguments == null) {
-      return null;
-    }
-
-    final DruidExpression arg = Iterables.getOnlyElement(arguments);
     final ValueType sumType;
-
     // Use 64-bit sum regardless of the type of the AVG aggregator.
     if (SqlTypeName.INT_TYPES.contains(aggregateCall.getType().getSqlTypeName())) {
       sumType = ValueType.LONG;
@@ -90,19 +58,6 @@ public class AvgSqlAggregator implements SqlAggregator
       sumType = ValueType.DOUBLE;
     }
 
-    final ExprMacroTable macroTable = plannerContext.getExprMacroTable();
-
-    final String fieldName;
-    final String expression;
-
-    if (arg.isDirectColumnAccess()) {
-      fieldName = arg.getDirectColumn();
-      expression = null;
-    } else {
-      fieldName = null;
-      expression = arg.getExpression();
-    }
-
     final String sumName = Calcites.makePrefixedName(name, "sum");
     final String countName = Calcites.makePrefixedName(name, "count");
     final AggregatorFactory sum = SumSqlAggregator.createSumAggregatorFactory(
diff --git a/sql/src/main/java/org/apache/druid/sql/calcite/aggregation/builtin/CountSqlAggregator.java b/sql/src/main/java/org/apache/druid/sql/calcite/aggregation/builtin/CountSqlAggregator.java
index e98ec32..6ab7a16 100644
--- a/sql/src/main/java/org/apache/druid/sql/calcite/aggregation/builtin/CountSqlAggregator.java
+++ b/sql/src/main/java/org/apache/druid/sql/calcite/aggregation/builtin/CountSqlAggregator.java
@@ -36,7 +36,7 @@ import org.apache.druid.sql.calcite.aggregation.SqlAggregator;
 import org.apache.druid.sql.calcite.expression.DruidExpression;
 import org.apache.druid.sql.calcite.expression.Expressions;
 import org.apache.druid.sql.calcite.planner.PlannerContext;
-import org.apache.druid.sql.calcite.table.RowSignature;
+import org.apache.druid.sql.calcite.rel.DruidQuerySignature;
 
 import javax.annotation.Nullable;
 import java.util.List;
@@ -55,7 +55,7 @@ public class CountSqlAggregator implements SqlAggregator
   @Override
   public Aggregation toDruidAggregation(
       final PlannerContext plannerContext,
-      final RowSignature rowSignature,
+      final DruidQuerySignature querySignature,
       final RexBuilder rexBuilder,
       final String name,
       final AggregateCall aggregateCall,
@@ -66,7 +66,7 @@ public class CountSqlAggregator implements SqlAggregator
   {
     final List<DruidExpression> args = Aggregations.getArgumentsForSimpleAggregator(
         plannerContext,
-        rowSignature,
+        querySignature.getRowSignature(),
         aggregateCall,
         project
     );
@@ -83,12 +83,9 @@ public class CountSqlAggregator implements SqlAggregator
       if (plannerContext.getPlannerConfig().isUseApproximateCountDistinct()) {
         return APPROX_COUNT_DISTINCT.toDruidAggregation(
             plannerContext,
-            rowSignature,
+            querySignature,
             rexBuilder,
-            name,
-            aggregateCall,
-            project,
-            existingAggregations,
+            name, aggregateCall, project, existingAggregations,
             finalizeAggregations
         );
       } else {
@@ -99,7 +96,7 @@ public class CountSqlAggregator implements SqlAggregator
 
       // COUNT(x) should count all non-null values of x.
       final RexNode rexNode = Expressions.fromFieldAccess(
-          rowSignature,
+          querySignature.getRowSignature(),
           project,
           Iterables.getOnlyElement(aggregateCall.getArgList())
       );
@@ -107,7 +104,7 @@ public class CountSqlAggregator implements SqlAggregator
       if (rexNode.getType().isNullable()) {
         final DimFilter nonNullFilter = Expressions.toFilter(
             plannerContext,
-            rowSignature,
+            querySignature,
             rexBuilder.makeCall(SqlStdOperatorTable.IS_NOT_NULL, ImmutableList.of(rexNode))
         );
 
@@ -116,7 +113,7 @@ public class CountSqlAggregator implements SqlAggregator
           throw new ISE("Could not create not-null filter for rexNode[%s]", rexNode);
         }
 
-        return Aggregation.create(new CountAggregatorFactory(name)).filter(rowSignature, nonNullFilter);
+        return Aggregation.create(new CountAggregatorFactory(name)).filter(querySignature, nonNullFilter);
       } else {
         return Aggregation.create(new CountAggregatorFactory(name));
       }
diff --git a/sql/src/main/java/org/apache/druid/sql/calcite/aggregation/builtin/MaxSqlAggregator.java b/sql/src/main/java/org/apache/druid/sql/calcite/aggregation/builtin/MaxSqlAggregator.java
index 923b022..c555330 100644
--- a/sql/src/main/java/org/apache/druid/sql/calcite/aggregation/builtin/MaxSqlAggregator.java
+++ b/sql/src/main/java/org/apache/druid/sql/calcite/aggregation/builtin/MaxSqlAggregator.java
@@ -19,10 +19,7 @@
 
 package org.apache.druid.sql.calcite.aggregation.builtin;
 
-import com.google.common.collect.Iterables;
 import org.apache.calcite.rel.core.AggregateCall;
-import org.apache.calcite.rel.core.Project;
-import org.apache.calcite.rex.RexBuilder;
 import org.apache.calcite.sql.SqlAggFunction;
 import org.apache.calcite.sql.fun.SqlStdOperatorTable;
 import org.apache.druid.java.util.common.ISE;
@@ -33,17 +30,9 @@ import org.apache.druid.query.aggregation.FloatMaxAggregatorFactory;
 import org.apache.druid.query.aggregation.LongMaxAggregatorFactory;
 import org.apache.druid.segment.column.ValueType;
 import org.apache.druid.sql.calcite.aggregation.Aggregation;
-import org.apache.druid.sql.calcite.aggregation.Aggregations;
-import org.apache.druid.sql.calcite.aggregation.SqlAggregator;
-import org.apache.druid.sql.calcite.expression.DruidExpression;
 import org.apache.druid.sql.calcite.planner.Calcites;
-import org.apache.druid.sql.calcite.planner.PlannerContext;
-import org.apache.druid.sql.calcite.table.RowSignature;
 
-import javax.annotation.Nullable;
-import java.util.List;
-
-public class MaxSqlAggregator implements SqlAggregator
+public class MaxSqlAggregator extends SimpleSqlAggregator
 {
   @Override
   public SqlAggFunction calciteFunction()
@@ -51,49 +40,16 @@ public class MaxSqlAggregator implements SqlAggregator
     return SqlStdOperatorTable.MAX;
   }
 
-  @Nullable
   @Override
-  public Aggregation toDruidAggregation(
-      final PlannerContext plannerContext,
-      final RowSignature rowSignature,
-      final RexBuilder rexBuilder,
+  Aggregation getAggregation(
       final String name,
       final AggregateCall aggregateCall,
-      final Project project,
-      final List<Aggregation> existingAggregations,
-      final boolean finalizeAggregations
+      final ExprMacroTable macroTable,
+      final String fieldName,
+      final String expression
   )
   {
-    if (aggregateCall.isDistinct()) {
-      return null;
-    }
-
-    final List<DruidExpression> arguments = Aggregations.getArgumentsForSimpleAggregator(
-        plannerContext,
-        rowSignature,
-        aggregateCall,
-        project
-    );
-
-    if (arguments == null) {
-      return null;
-    }
-
-    final DruidExpression arg = Iterables.getOnlyElement(arguments);
     final ValueType valueType = Calcites.getValueTypeForSqlTypeName(aggregateCall.getType().getSqlTypeName());
-    final ExprMacroTable macroTable = plannerContext.getExprMacroTable();
-
-    final String fieldName;
-    final String expression;
-
-    if (arg.isDirectColumnAccess()) {
-      fieldName = arg.getDirectColumn();
-      expression = null;
-    } else {
-      fieldName = null;
-      expression = arg.getExpression();
-    }
-
     return Aggregation.create(createMaxAggregatorFactory(valueType, name, fieldName, expression, macroTable));
   }
 
diff --git a/sql/src/main/java/org/apache/druid/sql/calcite/aggregation/builtin/MinSqlAggregator.java b/sql/src/main/java/org/apache/druid/sql/calcite/aggregation/builtin/MinSqlAggregator.java
index 9e8abe9..d1d2d73 100644
--- a/sql/src/main/java/org/apache/druid/sql/calcite/aggregation/builtin/MinSqlAggregator.java
+++ b/sql/src/main/java/org/apache/druid/sql/calcite/aggregation/builtin/MinSqlAggregator.java
@@ -19,10 +19,7 @@
 
 package org.apache.druid.sql.calcite.aggregation.builtin;
 
-import com.google.common.collect.Iterables;
 import org.apache.calcite.rel.core.AggregateCall;
-import org.apache.calcite.rel.core.Project;
-import org.apache.calcite.rex.RexBuilder;
 import org.apache.calcite.sql.SqlAggFunction;
 import org.apache.calcite.sql.fun.SqlStdOperatorTable;
 import org.apache.druid.java.util.common.ISE;
@@ -33,17 +30,9 @@ import org.apache.druid.query.aggregation.FloatMinAggregatorFactory;
 import org.apache.druid.query.aggregation.LongMinAggregatorFactory;
 import org.apache.druid.segment.column.ValueType;
 import org.apache.druid.sql.calcite.aggregation.Aggregation;
-import org.apache.druid.sql.calcite.aggregation.Aggregations;
-import org.apache.druid.sql.calcite.aggregation.SqlAggregator;
-import org.apache.druid.sql.calcite.expression.DruidExpression;
 import org.apache.druid.sql.calcite.planner.Calcites;
-import org.apache.druid.sql.calcite.planner.PlannerContext;
-import org.apache.druid.sql.calcite.table.RowSignature;
 
-import javax.annotation.Nullable;
-import java.util.List;
-
-public class MinSqlAggregator implements SqlAggregator
+public class MinSqlAggregator extends SimpleSqlAggregator
 {
   @Override
   public SqlAggFunction calciteFunction()
@@ -51,49 +40,16 @@ public class MinSqlAggregator implements SqlAggregator
     return SqlStdOperatorTable.MIN;
   }
 
-  @Nullable
   @Override
-  public Aggregation toDruidAggregation(
-      final PlannerContext plannerContext,
-      final RowSignature rowSignature,
-      final RexBuilder rexBuilder,
+  Aggregation getAggregation(
       final String name,
       final AggregateCall aggregateCall,
-      final Project project,
-      final List<Aggregation> existingAggregations,
-      final boolean finalizeAggregations
+      final ExprMacroTable macroTable,
+      final String fieldName,
+      final String expression
   )
   {
-    if (aggregateCall.isDistinct()) {
-      return null;
-    }
-
-    final List<DruidExpression> arguments = Aggregations.getArgumentsForSimpleAggregator(
-        plannerContext,
-        rowSignature,
-        aggregateCall,
-        project
-    );
-
-    if (arguments == null) {
-      return null;
-    }
-
-    final DruidExpression arg = Iterables.getOnlyElement(arguments);
     final ValueType valueType = Calcites.getValueTypeForSqlTypeName(aggregateCall.getType().getSqlTypeName());
-    final ExprMacroTable macroTable = plannerContext.getExprMacroTable();
-
-    final String fieldName;
-    final String expression;
-
-    if (arg.isDirectColumnAccess()) {
-      fieldName = arg.getDirectColumn();
-      expression = null;
-    } else {
-      fieldName = null;
-      expression = arg.getExpression();
-    }
-
     return Aggregation.create(createMinAggregatorFactory(valueType, name, fieldName, expression, macroTable));
   }
 
diff --git a/sql/src/main/java/org/apache/druid/sql/calcite/aggregation/builtin/SumSqlAggregator.java b/sql/src/main/java/org/apache/druid/sql/calcite/aggregation/builtin/SimpleSqlAggregator.java
similarity index 61%
copy from sql/src/main/java/org/apache/druid/sql/calcite/aggregation/builtin/SumSqlAggregator.java
copy to sql/src/main/java/org/apache/druid/sql/calcite/aggregation/builtin/SimpleSqlAggregator.java
index 0d80932..b98450b 100644
--- a/sql/src/main/java/org/apache/druid/sql/calcite/aggregation/builtin/SumSqlAggregator.java
+++ b/sql/src/main/java/org/apache/druid/sql/calcite/aggregation/builtin/SimpleSqlAggregator.java
@@ -23,39 +23,28 @@ import com.google.common.collect.Iterables;
 import org.apache.calcite.rel.core.AggregateCall;
 import org.apache.calcite.rel.core.Project;
 import org.apache.calcite.rex.RexBuilder;
-import org.apache.calcite.sql.SqlAggFunction;
-import org.apache.calcite.sql.fun.SqlStdOperatorTable;
-import org.apache.druid.java.util.common.ISE;
 import org.apache.druid.math.expr.ExprMacroTable;
-import org.apache.druid.query.aggregation.AggregatorFactory;
-import org.apache.druid.query.aggregation.DoubleSumAggregatorFactory;
-import org.apache.druid.query.aggregation.FloatSumAggregatorFactory;
-import org.apache.druid.query.aggregation.LongSumAggregatorFactory;
-import org.apache.druid.segment.column.ValueType;
 import org.apache.druid.sql.calcite.aggregation.Aggregation;
 import org.apache.druid.sql.calcite.aggregation.Aggregations;
 import org.apache.druid.sql.calcite.aggregation.SqlAggregator;
 import org.apache.druid.sql.calcite.expression.DruidExpression;
-import org.apache.druid.sql.calcite.planner.Calcites;
 import org.apache.druid.sql.calcite.planner.PlannerContext;
+import org.apache.druid.sql.calcite.rel.DruidQuerySignature;
 import org.apache.druid.sql.calcite.table.RowSignature;
 
 import javax.annotation.Nullable;
 import java.util.List;
 
-public class SumSqlAggregator implements SqlAggregator
+/**
+ * Abstraction for single column, single argument simple aggregators like sum, avg, min, max
+ */
+public abstract class SimpleSqlAggregator implements SqlAggregator
 {
-  @Override
-  public SqlAggFunction calciteFunction()
-  {
-    return SqlStdOperatorTable.SUM;
-  }
-
   @Nullable
   @Override
   public Aggregation toDruidAggregation(
       final PlannerContext plannerContext,
-      final RowSignature rowSignature,
+      final DruidQuerySignature querySignature,
       final RexBuilder rexBuilder,
       final String name,
       final AggregateCall aggregateCall,
@@ -67,6 +56,7 @@ public class SumSqlAggregator implements SqlAggregator
     if (aggregateCall.isDistinct()) {
       return null;
     }
+    final RowSignature rowSignature = querySignature.getRowSignature();
 
     final List<DruidExpression> arguments = Aggregations.getArgumentsForSimpleAggregator(
         plannerContext,
@@ -80,7 +70,6 @@ public class SumSqlAggregator implements SqlAggregator
     }
 
     final DruidExpression arg = Iterables.getOnlyElement(arguments);
-    final ValueType valueType = Calcites.getValueTypeForSqlTypeName(aggregateCall.getType().getSqlTypeName());
     final ExprMacroTable macroTable = plannerContext.getExprMacroTable();
 
     final String fieldName;
@@ -94,26 +83,14 @@ public class SumSqlAggregator implements SqlAggregator
       expression = arg.getExpression();
     }
 
-    return Aggregation.create(createSumAggregatorFactory(valueType, name, fieldName, expression, macroTable));
+    return getAggregation(name, aggregateCall, macroTable, fieldName, expression);
   }
 
-  static AggregatorFactory createSumAggregatorFactory(
-      final ValueType aggregationType,
-      final String name,
-      final String fieldName,
-      final String expression,
-      final ExprMacroTable macroTable
-  )
-  {
-    switch (aggregationType) {
-      case LONG:
-        return new LongSumAggregatorFactory(name, fieldName, expression, macroTable);
-      case FLOAT:
-        return new FloatSumAggregatorFactory(name, fieldName, expression, macroTable);
-      case DOUBLE:
-        return new DoubleSumAggregatorFactory(name, fieldName, expression, macroTable);
-      default:
-        throw new ISE("Cannot create aggregator factory for type[%s]", aggregationType);
-    }
-  }
+  abstract Aggregation getAggregation(
+      String name,
+      AggregateCall aggregateCall,
+      ExprMacroTable macroTable,
+      String fieldName,
+      String expression
+  );
 }
diff --git a/sql/src/main/java/org/apache/druid/sql/calcite/aggregation/builtin/SumSqlAggregator.java b/sql/src/main/java/org/apache/druid/sql/calcite/aggregation/builtin/SumSqlAggregator.java
index 0d80932..c96d74e 100644
--- a/sql/src/main/java/org/apache/druid/sql/calcite/aggregation/builtin/SumSqlAggregator.java
+++ b/sql/src/main/java/org/apache/druid/sql/calcite/aggregation/builtin/SumSqlAggregator.java
@@ -19,10 +19,7 @@
 
 package org.apache.druid.sql.calcite.aggregation.builtin;
 
-import com.google.common.collect.Iterables;
 import org.apache.calcite.rel.core.AggregateCall;
-import org.apache.calcite.rel.core.Project;
-import org.apache.calcite.rex.RexBuilder;
 import org.apache.calcite.sql.SqlAggFunction;
 import org.apache.calcite.sql.fun.SqlStdOperatorTable;
 import org.apache.druid.java.util.common.ISE;
@@ -33,17 +30,9 @@ import org.apache.druid.query.aggregation.FloatSumAggregatorFactory;
 import org.apache.druid.query.aggregation.LongSumAggregatorFactory;
 import org.apache.druid.segment.column.ValueType;
 import org.apache.druid.sql.calcite.aggregation.Aggregation;
-import org.apache.druid.sql.calcite.aggregation.Aggregations;
-import org.apache.druid.sql.calcite.aggregation.SqlAggregator;
-import org.apache.druid.sql.calcite.expression.DruidExpression;
 import org.apache.druid.sql.calcite.planner.Calcites;
-import org.apache.druid.sql.calcite.planner.PlannerContext;
-import org.apache.druid.sql.calcite.table.RowSignature;
 
-import javax.annotation.Nullable;
-import java.util.List;
-
-public class SumSqlAggregator implements SqlAggregator
+public class SumSqlAggregator extends SimpleSqlAggregator
 {
   @Override
   public SqlAggFunction calciteFunction()
@@ -51,49 +40,16 @@ public class SumSqlAggregator implements SqlAggregator
     return SqlStdOperatorTable.SUM;
   }
 
-  @Nullable
   @Override
-  public Aggregation toDruidAggregation(
-      final PlannerContext plannerContext,
-      final RowSignature rowSignature,
-      final RexBuilder rexBuilder,
+  Aggregation getAggregation(
       final String name,
       final AggregateCall aggregateCall,
-      final Project project,
-      final List<Aggregation> existingAggregations,
-      final boolean finalizeAggregations
+      final ExprMacroTable macroTable,
+      final String fieldName,
+      final String expression
   )
   {
-    if (aggregateCall.isDistinct()) {
-      return null;
-    }
-
-    final List<DruidExpression> arguments = Aggregations.getArgumentsForSimpleAggregator(
-        plannerContext,
-        rowSignature,
-        aggregateCall,
-        project
-    );
-
-    if (arguments == null) {
-      return null;
-    }
-
-    final DruidExpression arg = Iterables.getOnlyElement(arguments);
     final ValueType valueType = Calcites.getValueTypeForSqlTypeName(aggregateCall.getType().getSqlTypeName());
-    final ExprMacroTable macroTable = plannerContext.getExprMacroTable();
-
-    final String fieldName;
-    final String expression;
-
-    if (arg.isDirectColumnAccess()) {
-      fieldName = arg.getDirectColumn();
-      expression = null;
-    } else {
-      fieldName = null;
-      expression = arg.getExpression();
-    }
-
     return Aggregation.create(createSumAggregatorFactory(valueType, name, fieldName, expression, macroTable));
   }
 
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 fd2c1d8..ef8764c 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
@@ -50,6 +50,7 @@ import org.apache.druid.query.filter.OrDimFilter;
 import org.apache.druid.query.filter.SelectorDimFilter;
 import org.apache.druid.query.ordering.StringComparator;
 import org.apache.druid.query.ordering.StringComparators;
+import org.apache.druid.segment.VirtualColumn;
 import org.apache.druid.segment.column.ColumnHolder;
 import org.apache.druid.segment.column.ValueType;
 import org.apache.druid.sql.calcite.filtration.BoundRefKey;
@@ -57,6 +58,7 @@ import org.apache.druid.sql.calcite.filtration.Bounds;
 import org.apache.druid.sql.calcite.filtration.Filtration;
 import org.apache.druid.sql.calcite.planner.Calcites;
 import org.apache.druid.sql.calcite.planner.PlannerContext;
+import org.apache.druid.sql.calcite.rel.DruidQuerySignature;
 import org.apache.druid.sql.calcite.table.RowSignature;
 import org.joda.time.Interval;
 
@@ -207,27 +209,35 @@ public class Expressions
    * Translates "condition" to a Druid filter, or returns null if we cannot translate the condition.
    *
    * @param plannerContext planner context
-   * @param rowSignature   row signature of the dataSource to be filtered
+   * @param querySignature   row signature of the dataSource to be filtered
    * @param expression     Calcite row expression
    */
   @Nullable
   public static DimFilter toFilter(
       final PlannerContext plannerContext,
-      final RowSignature rowSignature,
+      final DruidQuerySignature querySignature,
       final RexNode expression
   )
   {
     final SqlKind kind = expression.getKind();
 
     if (kind == SqlKind.IS_TRUE || kind == SqlKind.IS_NOT_FALSE) {
-      return toFilter(plannerContext, rowSignature, Iterables.getOnlyElement(((RexCall) expression).getOperands()));
+      return toFilter(
+          plannerContext,
+          querySignature,
+          Iterables.getOnlyElement(((RexCall) expression).getOperands())
+      );
     } else if (kind == SqlKind.IS_FALSE || kind == SqlKind.IS_NOT_TRUE) {
       return new NotDimFilter(
-          toFilter(plannerContext, rowSignature, Iterables.getOnlyElement(((RexCall) expression).getOperands()))
+          toFilter(
+              plannerContext,
+              querySignature,
+              Iterables.getOnlyElement(((RexCall) expression).getOperands())
+          )
       );
     } else if (kind == SqlKind.CAST && expression.getType().getSqlTypeName() == SqlTypeName.BOOLEAN) {
       // Calcite sometimes leaves errant, useless cast-to-booleans inside filters. Strip them and continue.
-      return toFilter(plannerContext, rowSignature, Iterables.getOnlyElement(((RexCall) expression).getOperands()));
+      return toFilter(plannerContext, querySignature, Iterables.getOnlyElement(((RexCall) expression).getOperands()));
     } else if (kind == SqlKind.AND
                || kind == SqlKind.OR
                || kind == SqlKind.NOT) {
@@ -235,7 +245,7 @@ public class Expressions
       for (final RexNode rexNode : ((RexCall) expression).getOperands()) {
         final DimFilter nextFilter = toFilter(
             plannerContext,
-            rowSignature,
+            querySignature,
             rexNode
         );
         if (nextFilter == null) {
@@ -254,7 +264,7 @@ public class Expressions
       }
     } else {
       // Handle filter conditions on everything else.
-      return toLeafFilter(plannerContext, rowSignature, expression);
+      return toLeafFilter(plannerContext, querySignature, expression);
     }
   }
 
@@ -263,13 +273,13 @@ public class Expressions
    * if we cannot translate the condition.
    *
    * @param plannerContext planner context
-   * @param rowSignature   row signature of the dataSource to be filtered
+   * @param querySignature   row signature of the dataSource to be filtered
    * @param rexNode        Calcite row expression
    */
   @Nullable
   private static DimFilter toLeafFilter(
       final PlannerContext plannerContext,
-      final RowSignature rowSignature,
+      final DruidQuerySignature querySignature,
       final RexNode rexNode
   )
   {
@@ -279,17 +289,23 @@ public class Expressions
       return Filtration.matchNothing();
     }
 
-    final DimFilter simpleFilter = toSimpleLeafFilter(plannerContext, rowSignature, rexNode);
-    return simpleFilter != null ? simpleFilter : toExpressionLeafFilter(plannerContext, rowSignature, rexNode);
+    final DimFilter simpleFilter = toSimpleLeafFilter(
+        plannerContext,
+        querySignature,
+        rexNode
+    );
+    return simpleFilter != null
+           ? simpleFilter
+           : toExpressionLeafFilter(plannerContext, querySignature.getRowSignature(), rexNode);
   }
 
   /**
-   * Translates to a simple leaf filter, meaning one that hits just a single column and is not an expression filter.
+   * Translates to a simple leaf filter, i.e. is not an expression filter.
    */
   @Nullable
   private static DimFilter toSimpleLeafFilter(
       final PlannerContext plannerContext,
-      final RowSignature rowSignature,
+      final DruidQuerySignature querySignature,
       final RexNode rexNode
   )
   {
@@ -298,14 +314,14 @@ public class Expressions
     if (kind == SqlKind.IS_TRUE || kind == SqlKind.IS_NOT_FALSE) {
       return toSimpleLeafFilter(
           plannerContext,
-          rowSignature,
+          querySignature,
           Iterables.getOnlyElement(((RexCall) rexNode).getOperands())
       );
     } else if (kind == SqlKind.IS_FALSE || kind == SqlKind.IS_NOT_TRUE) {
       return new NotDimFilter(
           toSimpleLeafFilter(
               plannerContext,
-              rowSignature,
+              querySignature,
               Iterables.getOnlyElement(((RexCall) rexNode).getOperands())
           )
       );
@@ -313,16 +329,35 @@ public class Expressions
       final RexNode operand = Iterables.getOnlyElement(((RexCall) rexNode).getOperands());
 
       // operand must be translatable to a SimpleExtraction to be simple-filterable
-      final DruidExpression druidExpression = toDruidExpression(plannerContext, rowSignature, operand);
-      if (druidExpression == null || !druidExpression.isSimpleExtraction()) {
+      final DruidExpression druidExpression =
+          toDruidExpression(plannerContext, querySignature.getRowSignature(), operand);
+
+      if (druidExpression == null) {
         return null;
       }
 
-      final DimFilter equalFilter = new SelectorDimFilter(
-          druidExpression.getSimpleExtraction().getColumn(),
-          NullHandling.defaultStringValue(),
-          druidExpression.getSimpleExtraction().getExtractionFn()
-      );
+      final DimFilter equalFilter;
+      if (druidExpression.isSimpleExtraction()) {
+        equalFilter = new SelectorDimFilter(
+            druidExpression.getSimpleExtraction().getColumn(),
+            NullHandling.defaultStringValue(),
+            druidExpression.getSimpleExtraction().getExtractionFn()
+        );
+      } else {
+        final VirtualColumn virtualColumn = querySignature.getOrCreateVirtualColumnForExpression(
+            plannerContext,
+            druidExpression,
+            operand.getType().getSqlTypeName()
+        );
+        if (virtualColumn == null) {
+          return null;
+        }
+        equalFilter = new SelectorDimFilter(
+            virtualColumn.getOutputName(),
+            NullHandling.defaultStringValue(),
+            null
+        );
+      }
 
       return kind == SqlKind.IS_NOT_NULL ? new NotDimFilter(equalFilter) : equalFilter;
     } else if (kind == SqlKind.EQUALS
@@ -379,7 +414,7 @@ public class Expressions
       }
 
       // Translate lhs to a DruidExpression.
-      final DruidExpression lhsExpression = toDruidExpression(plannerContext, rowSignature, lhs);
+      final DruidExpression lhsExpression = toDruidExpression(plannerContext, querySignature.getRowSignature(), lhs);
       if (lhsExpression == null) {
         return null;
       }
@@ -392,14 +427,24 @@ public class Expressions
         return buildTimeFloorFilter(ColumnHolder.TIME_COLUMN_NAME, queryGranularity, flippedKind, rhsMillis);
       }
 
-      // In the general case, lhs must be translatable to a SimpleExtraction to be simple-filterable.
-      if (!lhsExpression.isSimpleExtraction()) {
-        return null;
+      final String column;
+      final ExtractionFn extractionFn;
+      if (lhsExpression.isSimpleExtraction()) {
+        column = lhsExpression.getSimpleExtraction().getColumn();
+        extractionFn = lhsExpression.getSimpleExtraction().getExtractionFn();
+      } else {
+        VirtualColumn virtualLhs = querySignature.getOrCreateVirtualColumnForExpression(
+            plannerContext,
+            lhsExpression,
+            lhs.getType().getSqlTypeName()
+        );
+        if (virtualLhs == null) {
+          return null;
+        }
+        column = virtualLhs.getOutputName();
+        extractionFn = null;
       }
 
-      final String column = lhsExpression.getSimpleExtraction().getColumn();
-      final ExtractionFn extractionFn = lhsExpression.getSimpleExtraction().getExtractionFn();
-
       if (column.equals(ColumnHolder.TIME_COLUMN_NAME) && extractionFn instanceof TimeFormatExtractionFn) {
         // Check if we can strip the extractionFn and convert the filter to a direct filter on __time.
         // This allows potential conversion to query-level "intervals" later on, which is ideal for Druid queries.
@@ -477,19 +522,32 @@ public class Expressions
       if (conversion == null) {
         return null;
       } else {
-        DimFilter filter = conversion.toDruidFilter(plannerContext, rowSignature, rexNode);
+        DimFilter filter =
+            conversion.toDruidFilter(plannerContext, querySignature, rexNode);
         if (filter != null) {
           return filter;
         }
-        DruidExpression expression = conversion.toDruidExpression(plannerContext, rowSignature, rexNode);
-        if (expression != null) {
-          return new ExpressionDimFilter(expression.getExpression(), plannerContext.getExprMacroTable());
-        }
+        return null;
       }
     }
     return null;
   }
 
+  /**
+   * Translates to an "expression" type leaf filter. Used as a fallback if we can't use a simple leaf filter.
+   */
+  @Nullable
+  private static DimFilter toExpressionLeafFilter(
+      final PlannerContext plannerContext,
+      final RowSignature rowSignature,
+      final RexNode rexNode
+  )
+  {
+    final DruidExpression druidExpression = toDruidExpression(plannerContext, rowSignature, rexNode);
+    return druidExpression != null
+           ? new ExpressionDimFilter(druidExpression.getExpression(), plannerContext.getExprMacroTable())
+           : null;
+  }
 
   public static ExprType exprTypeForValueType(final ValueType valueType)
   {
@@ -507,22 +565,6 @@ public class Expressions
   }
 
   /**
-   * Translates to an "expression" type leaf filter. Used as a fallback if we can't use a simple leaf filter.
-   */
-  @Nullable
-  private static DimFilter toExpressionLeafFilter(
-      final PlannerContext plannerContext,
-      final RowSignature rowSignature,
-      final RexNode rexNode
-  )
-  {
-    final DruidExpression druidExpression = toDruidExpression(plannerContext, rowSignature, rexNode);
-    return druidExpression == null
-           ? null
-           : new ExpressionDimFilter(druidExpression.getExpression(), plannerContext.getExprMacroTable());
-  }
-
-  /**
    * Converts an expression to a Granularity, if possible. This is possible if, and only if, the expression
    * is a timestamp_floor function on the __time column with literal parameters for period, origin, and timeZone.
    *
diff --git a/sql/src/main/java/org/apache/druid/sql/calcite/expression/SqlOperatorConversion.java b/sql/src/main/java/org/apache/druid/sql/calcite/expression/SqlOperatorConversion.java
index 684ce58..dc85fdb 100644
--- a/sql/src/main/java/org/apache/druid/sql/calcite/expression/SqlOperatorConversion.java
+++ b/sql/src/main/java/org/apache/druid/sql/calcite/expression/SqlOperatorConversion.java
@@ -23,6 +23,7 @@ import org.apache.calcite.rex.RexNode;
 import org.apache.calcite.sql.SqlOperator;
 import org.apache.druid.query.filter.DimFilter;
 import org.apache.druid.sql.calcite.planner.PlannerContext;
+import org.apache.druid.sql.calcite.rel.DruidQuerySignature;
 import org.apache.druid.sql.calcite.table.RowSignature;
 
 import javax.annotation.Nullable;
@@ -53,16 +54,16 @@ public interface SqlOperatorConversion
   /**
    * Returns a Druid Aggregation corresponding to a SQL {@link SqlOperator} used to filter rows
    *
-   * @param plannerContext       SQL planner context
-   * @param rowSignature         signature of the rows being aggregated
-   * @param rexNode           a rexBuilder, in case you need one
+   * @param plannerContext   SQL planner context
+   * @param querySignature   signature of the rows being aggregated and expression column references
+   * @param rexNode          filter expression rex node
    *
    * @return filter, or null if the call cannot be translated
    */
   @Nullable
   default DimFilter toDruidFilter(
       PlannerContext plannerContext,
-      RowSignature rowSignature,
+      DruidQuerySignature querySignature,
       RexNode rexNode
   )
   {
diff --git a/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/LikeOperatorConversion.java b/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/LikeOperatorConversion.java
index 09bb341..b1b7d9d 100644
--- a/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/LikeOperatorConversion.java
+++ b/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/LikeOperatorConversion.java
@@ -26,11 +26,12 @@ import org.apache.calcite.sql.SqlOperator;
 import org.apache.calcite.sql.fun.SqlStdOperatorTable;
 import org.apache.druid.query.filter.DimFilter;
 import org.apache.druid.query.filter.LikeDimFilter;
+import org.apache.druid.segment.VirtualColumn;
 import org.apache.druid.sql.calcite.expression.DirectOperatorConversion;
 import org.apache.druid.sql.calcite.expression.DruidExpression;
 import org.apache.druid.sql.calcite.expression.Expressions;
 import org.apache.druid.sql.calcite.planner.PlannerContext;
-import org.apache.druid.sql.calcite.table.RowSignature;
+import org.apache.druid.sql.calcite.rel.DruidQuerySignature;
 
 import javax.annotation.Nullable;
 import java.util.List;
@@ -54,24 +55,42 @@ public class LikeOperatorConversion extends DirectOperatorConversion
   @Override
   public DimFilter toDruidFilter(
       PlannerContext plannerContext,
-      RowSignature rowSignature,
+      DruidQuerySignature querySignature,
       RexNode rexNode
   )
   {
     final List<RexNode> operands = ((RexCall) rexNode).getOperands();
     final DruidExpression druidExpression = Expressions.toDruidExpression(
         plannerContext,
-        rowSignature,
+        querySignature.getRowSignature(),
         operands.get(0)
     );
-    if (druidExpression == null || !druidExpression.isSimpleExtraction()) {
+    if (druidExpression == null) {
       return null;
     }
-    return new LikeDimFilter(
-        druidExpression.getSimpleExtraction().getColumn(),
-        RexLiteral.stringValue(operands.get(1)),
-        operands.size() > 2 ? RexLiteral.stringValue(operands.get(2)) : null,
-        druidExpression.getSimpleExtraction().getExtractionFn()
-    );
+
+    if (druidExpression.isSimpleExtraction()) {
+      return new LikeDimFilter(
+          druidExpression.getSimpleExtraction().getColumn(),
+          RexLiteral.stringValue(operands.get(1)),
+          operands.size() > 2 ? RexLiteral.stringValue(operands.get(2)) : null,
+          druidExpression.getSimpleExtraction().getExtractionFn()
+      );
+    } else {
+      VirtualColumn v = querySignature.getOrCreateVirtualColumnForExpression(
+          plannerContext,
+          druidExpression,
+          operands.get(0).getType().getSqlTypeName()
+      );
+      if (v == null) {
+        return null;
+      }
+      return new LikeDimFilter(
+          v.getOutputName(),
+          RexLiteral.stringValue(operands.get(1)),
+          operands.size() > 2 ? RexLiteral.stringValue(operands.get(2)) : null,
+          null
+      );
+    }
   }
 }
diff --git a/sql/src/main/java/org/apache/druid/sql/calcite/filtration/ConvertBoundsToSelectors.java b/sql/src/main/java/org/apache/druid/sql/calcite/filtration/ConvertBoundsToSelectors.java
index 3eb918e..e75fe14 100644
--- a/sql/src/main/java/org/apache/druid/sql/calcite/filtration/ConvertBoundsToSelectors.java
+++ b/sql/src/main/java/org/apache/druid/sql/calcite/filtration/ConvertBoundsToSelectors.java
@@ -24,20 +24,20 @@ import org.apache.druid.query.filter.DimFilter;
 import org.apache.druid.query.filter.SelectorDimFilter;
 import org.apache.druid.query.ordering.StringComparator;
 import org.apache.druid.sql.calcite.expression.SimpleExtraction;
-import org.apache.druid.sql.calcite.table.RowSignature;
+import org.apache.druid.sql.calcite.rel.DruidQuerySignature;
 
 public class ConvertBoundsToSelectors extends BottomUpTransform
 {
-  private final RowSignature sourceRowSignature;
+  private final DruidQuerySignature querySignature;
 
-  private ConvertBoundsToSelectors(final RowSignature sourceRowSignature)
+  private ConvertBoundsToSelectors(final DruidQuerySignature querySignature)
   {
-    this.sourceRowSignature = sourceRowSignature;
+    this.querySignature = querySignature;
   }
 
-  public static ConvertBoundsToSelectors create(final RowSignature sourceRowSignature)
+  public static ConvertBoundsToSelectors create(final DruidQuerySignature querySignature)
   {
-    return new ConvertBoundsToSelectors(sourceRowSignature);
+    return new ConvertBoundsToSelectors(querySignature);
   }
 
   @Override
@@ -45,7 +45,7 @@ public class ConvertBoundsToSelectors extends BottomUpTransform
   {
     if (filter instanceof BoundDimFilter) {
       final BoundDimFilter bound = (BoundDimFilter) filter;
-      final StringComparator naturalStringComparator = sourceRowSignature.naturalStringComparator(
+      final StringComparator comparator = querySignature.getRowSignature().naturalStringComparator(
           SimpleExtraction.of(bound.getDimension(), bound.getExtractionFn())
       );
 
@@ -54,7 +54,7 @@ public class ConvertBoundsToSelectors extends BottomUpTransform
           && bound.getUpper().equals(bound.getLower())
           && !bound.isUpperStrict()
           && !bound.isLowerStrict()
-          && bound.getOrdering().equals(naturalStringComparator)) {
+          && (querySignature.isVirtualColumnDefined(bound.getDimension()) || bound.getOrdering().equals(comparator))) {
         return new SelectorDimFilter(
             bound.getDimension(),
             bound.getUpper(),
diff --git a/sql/src/main/java/org/apache/druid/sql/calcite/filtration/Filtration.java b/sql/src/main/java/org/apache/druid/sql/calcite/filtration/Filtration.java
index 25232a5..1d891d2 100644
--- a/sql/src/main/java/org/apache/druid/sql/calcite/filtration/Filtration.java
+++ b/sql/src/main/java/org/apache/druid/sql/calcite/filtration/Filtration.java
@@ -28,7 +28,7 @@ import org.apache.druid.query.filter.DimFilter;
 import org.apache.druid.query.filter.ExpressionDimFilter;
 import org.apache.druid.query.spec.MultipleIntervalSegmentSpec;
 import org.apache.druid.query.spec.QuerySegmentSpec;
-import org.apache.druid.sql.calcite.table.RowSignature;
+import org.apache.druid.sql.calcite.rel.DruidQuerySignature;
 import org.joda.time.Interval;
 
 import java.util.List;
@@ -108,15 +108,15 @@ public class Filtration
    *
    * @return equivalent Filtration
    */
-  public Filtration optimize(final RowSignature sourceRowSignature)
+  public Filtration optimize(final DruidQuerySignature querySignature)
   {
     return transform(
         this,
         ImmutableList.of(
             CombineAndSimplifyBounds.instance(),
             MoveTimeFiltersToIntervals.instance(),
-            ConvertBoundsToSelectors.create(sourceRowSignature),
-            ConvertSelectorsToIns.create(sourceRowSignature),
+            ConvertBoundsToSelectors.create(querySignature),
+            ConvertSelectorsToIns.create(querySignature.getRowSignature()),
             MoveMarkerFiltersToIntervals.instance(),
             ValidateNoMarkerFiltersRemain.instance()
         )
@@ -128,7 +128,7 @@ public class Filtration
    *
    * @return equivalent Filtration
    */
-  public Filtration optimizeFilterOnly(final RowSignature sourceRowSignature)
+  public Filtration optimizeFilterOnly(final DruidQuerySignature querySignature)
   {
     if (!intervals.equals(ImmutableList.of(eternity()))) {
       throw new ISE("Cannot optimizeFilterOnly when intervals are set");
@@ -138,8 +138,8 @@ public class Filtration
         this,
         ImmutableList.of(
             CombineAndSimplifyBounds.instance(),
-            ConvertBoundsToSelectors.create(sourceRowSignature),
-            ConvertSelectorsToIns.create(sourceRowSignature)
+            ConvertBoundsToSelectors.create(querySignature),
+            ConvertSelectorsToIns.create(querySignature.getRowSignature())
         )
     );
 
diff --git a/sql/src/main/java/org/apache/druid/sql/calcite/rel/DruidQuery.java b/sql/src/main/java/org/apache/druid/sql/calcite/rel/DruidQuery.java
index 647e213..832c202 100644
--- a/sql/src/main/java/org/apache/druid/sql/calcite/rel/DruidQuery.java
+++ b/sql/src/main/java/org/apache/druid/sql/calcite/rel/DruidQuery.java
@@ -44,7 +44,6 @@ import org.apache.calcite.util.ImmutableBitSet;
 import org.apache.druid.java.util.common.ISE;
 import org.apache.druid.java.util.common.granularity.Granularities;
 import org.apache.druid.java.util.common.granularity.Granularity;
-import org.apache.druid.math.expr.ExprMacroTable;
 import org.apache.druid.math.expr.ExprType;
 import org.apache.druid.math.expr.Parser;
 import org.apache.druid.query.DataSource;
@@ -84,11 +83,15 @@ import org.apache.druid.sql.calcite.planner.PlannerContext;
 import org.apache.druid.sql.calcite.rule.GroupByRules;
 import org.apache.druid.sql.calcite.table.RowSignature;
 
+import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
 import java.util.ArrayList;
+import java.util.Comparator;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import java.util.TreeSet;
 import java.util.stream.Collectors;
 
@@ -99,7 +102,6 @@ import java.util.stream.Collectors;
 public class DruidQuery
 {
   private final DataSource dataSource;
-  private final RowSignature sourceRowSignature;
   private final PlannerContext plannerContext;
 
   @Nullable
@@ -118,13 +120,14 @@ public class DruidQuery
   private final DefaultLimitSpec limitSpec;
 
   @Nullable
-  private final RowSignature outputRowSignature;
-
-  @Nullable
   private final RelDataType outputRowType;
 
   private final Query query;
 
+  private final DruidQuerySignature sourceQuerySignature;
+
+  private final RowSignature outputRowSignature;
+
   public DruidQuery(
       final PartialDruidQuery partialQuery,
       final DataSource dataSource,
@@ -135,17 +138,16 @@ public class DruidQuery
   )
   {
     this.dataSource = dataSource;
-    this.sourceRowSignature = sourceRowSignature;
     this.outputRowType = partialQuery.leafRel().getRowType();
+    this.sourceQuerySignature = new DruidQuerySignature(sourceRowSignature);
     this.plannerContext = plannerContext;
 
     // Now the fun begins.
-    this.filter = computeWhereFilter(partialQuery, sourceRowSignature, plannerContext);
-    this.selectProjection = computeSelectProjection(partialQuery, plannerContext, sourceRowSignature);
-    this.grouping = computeGrouping(partialQuery, plannerContext, sourceRowSignature, rexBuilder, finalizeAggregations);
+    this.filter = computeWhereFilter(partialQuery, plannerContext, sourceQuerySignature);
+    this.selectProjection = computeSelectProjection(partialQuery, plannerContext, sourceQuerySignature);
+    this.grouping = computeGrouping(partialQuery, plannerContext, sourceQuerySignature, rexBuilder, finalizeAggregations);
 
     final RowSignature sortingInputRowSignature;
-
     if (this.selectProjection != null) {
       sortingInputRowSignature = this.selectProjection.getOutputRowSignature();
     } else if (this.grouping != null) {
@@ -156,7 +158,6 @@ public class DruidQuery
 
     this.sortProject = computeSortProject(partialQuery, plannerContext, sortingInputRowSignature);
 
-    // outputRowSignature is used only for scan and select query, and thus sort and grouping must be null
     this.outputRowSignature = sortProject == null ? sortingInputRowSignature : sortProject.getOutputRowSignature();
 
     this.limitSpec = computeLimitSpec(partialQuery, sortingInputRowSignature);
@@ -166,8 +167,8 @@ public class DruidQuery
   @Nullable
   private static DimFilter computeWhereFilter(
       final PartialDruidQuery partialQuery,
-      final RowSignature sourceRowSignature,
-      final PlannerContext plannerContext
+      final PlannerContext plannerContext,
+      final DruidQuerySignature querySignature
   )
   {
     final Filter whereFilter = partialQuery.getWhereFilter();
@@ -176,14 +177,40 @@ public class DruidQuery
       return null;
     }
 
-    final RexNode condition = whereFilter.getCondition();
+    return getDimFilter(plannerContext, querySignature, whereFilter);
+  }
+
+  @Nullable
+  private static DimFilter computeHavingFilter(
+      final PartialDruidQuery partialQuery,
+      final PlannerContext plannerContext,
+      final DruidQuerySignature querySignature
+  )
+  {
+    final Filter havingFilter = partialQuery.getHavingFilter();
+
+    if (havingFilter == null) {
+      return null;
+    }
+
+    return getDimFilter(plannerContext, querySignature, havingFilter);
+  }
+
+  @Nonnull
+  private static DimFilter getDimFilter(
+      final PlannerContext plannerContext,
+      final DruidQuerySignature querySignature,
+      Filter filter
+  )
+  {
+    final RexNode condition = filter.getCondition();
     final DimFilter dimFilter = Expressions.toFilter(
         plannerContext,
-        sourceRowSignature,
+        querySignature,
         condition
     );
     if (dimFilter == null) {
-      throw new CannotBuildQueryException(whereFilter, condition);
+      throw new CannotBuildQueryException(filter, condition);
     } else {
       return dimFilter;
     }
@@ -193,7 +220,7 @@ public class DruidQuery
   private static SelectProjection computeSelectProjection(
       final PartialDruidQuery partialQuery,
       final PlannerContext plannerContext,
-      final RowSignature sourceRowSignature
+      final DruidQuerySignature queryColumns
   )
   {
     final Project project = partialQuery.getSelectProject();
@@ -207,7 +234,7 @@ public class DruidQuery
     for (final RexNode rexNode : project.getChildExps()) {
       final DruidExpression expression = Expressions.toDruidExpression(
           plannerContext,
-          sourceRowSignature,
+          queryColumns.getRowSignature(),
           rexNode
       );
 
@@ -219,41 +246,37 @@ public class DruidQuery
     }
 
     final List<String> directColumns = new ArrayList<>();
-    final List<VirtualColumn> virtualColumns = new ArrayList<>();
+    final Set<VirtualColumn> virtualColumns = new HashSet<>();
     final List<String> rowOrder = new ArrayList<>();
 
-    final String virtualColumnPrefix = Calcites.findUnusedPrefix(
-        "v",
-        new TreeSet<>(sourceRowSignature.getRowOrder())
-    );
-    int virtualColumnNameCounter = 0;
-
     for (int i = 0; i < expressions.size(); i++) {
       final DruidExpression expression = expressions.get(i);
       if (expression.isDirectColumnAccess()) {
         directColumns.add(expression.getDirectColumn());
         rowOrder.add(expression.getDirectColumn());
       } else {
-        final String virtualColumnName = virtualColumnPrefix + virtualColumnNameCounter++;
-        virtualColumns.add(
-            expression.toVirtualColumn(
-                virtualColumnName,
-                Calcites.getValueTypeForSqlTypeName(project.getChildExps().get(i).getType().getSqlTypeName()),
-                plannerContext.getExprMacroTable()
-            )
+        VirtualColumn virtualColumn = queryColumns.getOrCreateVirtualColumnForExpression(
+            plannerContext,
+            expression,
+            project.getChildExps().get(i).getType().getSqlTypeName()
         );
-        rowOrder.add(virtualColumnName);
+        virtualColumns.add(virtualColumn);
+        rowOrder.add(virtualColumn.getOutputName());
       }
     }
 
-    return new SelectProjection(directColumns, virtualColumns, RowSignature.from(rowOrder, project.getRowType()));
+    return new SelectProjection(
+        directColumns,
+        ImmutableList.copyOf(virtualColumns),
+        RowSignature.from(rowOrder, project.getRowType())
+    );
   }
 
   @Nullable
   private static Grouping computeGrouping(
       final PartialDruidQuery partialQuery,
       final PlannerContext plannerContext,
-      final RowSignature sourceRowSignature,
+      final DruidQuerySignature queryColumns,
       final RexBuilder rexBuilder,
       final boolean finalizeAggregations
   )
@@ -265,11 +288,11 @@ public class DruidQuery
       return null;
     }
 
-    final List<DimensionExpression> dimensions = computeDimensions(partialQuery, plannerContext, sourceRowSignature);
+    final List<DimensionExpression> dimensions = computeDimensions(partialQuery, plannerContext, queryColumns);
     final List<Aggregation> aggregations = computeAggregations(
         partialQuery,
         plannerContext,
-        sourceRowSignature,
+        queryColumns,
         rexBuilder,
         finalizeAggregations
     );
@@ -284,10 +307,11 @@ public class DruidQuery
         aggregate.getRowType()
     );
 
+    DruidQuerySignature aggregateSignature = queryColumns.asAggregateSignature(aggregateRowSignature);
     final DimFilter havingFilter = computeHavingFilter(
         partialQuery,
-        aggregateRowSignature,
-        plannerContext
+        plannerContext,
+        aggregateSignature
     );
 
     if (aggregateProject == null) {
@@ -414,7 +438,7 @@ public class DruidQuery
    *
    * @param partialQuery       partial query
    * @param plannerContext     planner context
-   * @param sourceRowSignature source row signature
+   * @param querySignature     source row signature and re-usable virtual column references
    *
    * @return dimensions
    *
@@ -423,21 +447,20 @@ public class DruidQuery
   private static List<DimensionExpression> computeDimensions(
       final PartialDruidQuery partialQuery,
       final PlannerContext plannerContext,
-      final RowSignature sourceRowSignature
+      final DruidQuerySignature querySignature
   )
   {
     final Aggregate aggregate = Preconditions.checkNotNull(partialQuery.getAggregate());
     final List<DimensionExpression> dimensions = new ArrayList<>();
-    final String outputNamePrefix = Calcites.findUnusedPrefix("d", new TreeSet<>(sourceRowSignature.getRowOrder()));
+    final String outputNamePrefix = Calcites.findUnusedPrefix("d", new TreeSet<>(querySignature.getRowSignature().getRowOrder()));
     int outputNameCounter = 0;
 
     for (int i : aggregate.getGroupSet()) {
       // Dimension might need to create virtual columns. Avoid giving it a name that would lead to colliding columns.
-      final String dimOutputName = outputNamePrefix + outputNameCounter++;
-      final RexNode rexNode = Expressions.fromFieldAccess(sourceRowSignature, partialQuery.getSelectProject(), i);
+      final RexNode rexNode = Expressions.fromFieldAccess(querySignature.getRowSignature(), partialQuery.getSelectProject(), i);
       final DruidExpression druidExpression = Expressions.toDruidExpression(
           plannerContext,
-          sourceRowSignature,
+          querySignature.getRowSignature(),
           rexNode
       );
       if (druidExpression == null) {
@@ -451,6 +474,16 @@ public class DruidQuery
         throw new CannotBuildQueryException(aggregate, rexNode);
       }
 
+      final VirtualColumn virtualColumn;
+
+      final String dimOutputName;
+      if (!druidExpression.isSimpleExtraction()) {
+        virtualColumn = querySignature.getOrCreateVirtualColumnForExpression(plannerContext, druidExpression, sqlTypeName);
+        dimOutputName = virtualColumn.getOutputName();
+      } else {
+        dimOutputName = outputNamePrefix + outputNameCounter++;
+      }
+
       dimensions.add(new DimensionExpression(dimOutputName, druidExpression, outputType));
     }
 
@@ -462,7 +495,7 @@ public class DruidQuery
    *
    * @param partialQuery         partial query
    * @param plannerContext       planner context
-   * @param sourceRowSignature   source row signature
+   * @param querySignature       source row signature and re-usable virtual column references
    * @param rexBuilder           calcite RexBuilder
    * @param finalizeAggregations true if this query should include explicit finalization for all of its
    *                             aggregators, where required. Useful for subqueries where Druid's native query layer
@@ -475,26 +508,26 @@ public class DruidQuery
   private static List<Aggregation> computeAggregations(
       final PartialDruidQuery partialQuery,
       final PlannerContext plannerContext,
-      final RowSignature sourceRowSignature,
+      final DruidQuerySignature querySignature,
       final RexBuilder rexBuilder,
       final boolean finalizeAggregations
   )
   {
     final Aggregate aggregate = Preconditions.checkNotNull(partialQuery.getAggregate());
     final List<Aggregation> aggregations = new ArrayList<>();
-    final String outputNamePrefix = Calcites.findUnusedPrefix("a", new TreeSet<>(sourceRowSignature.getRowOrder()));
+    final String outputNamePrefix = Calcites.findUnusedPrefix("a", new TreeSet<>(querySignature.getRowSignature().getRowOrder()));
 
     for (int i = 0; i < aggregate.getAggCallList().size(); i++) {
       final String aggName = outputNamePrefix + i;
       final AggregateCall aggCall = aggregate.getAggCallList().get(i);
       final Aggregation aggregation = GroupByRules.translateAggregateCall(
           plannerContext,
-          sourceRowSignature,
+          querySignature,
           rexBuilder,
           partialQuery.getSelectProject(),
-          aggCall,
           aggregations,
           aggName,
+          aggCall,
           finalizeAggregations
       );
 
@@ -509,32 +542,6 @@ public class DruidQuery
   }
 
   @Nullable
-  private static DimFilter computeHavingFilter(
-      final PartialDruidQuery partialQuery,
-      final RowSignature outputRowSignature,
-      final PlannerContext plannerContext
-  )
-  {
-    final Filter havingFilter = partialQuery.getHavingFilter();
-
-    if (havingFilter == null) {
-      return null;
-    }
-
-    final RexNode condition = havingFilter.getCondition();
-    final DimFilter dimFilter = Expressions.toFilter(
-        plannerContext,
-        outputRowSignature,
-        condition
-    );
-    if (dimFilter == null) {
-      throw new CannotBuildQueryException(havingFilter, condition);
-    } else {
-      return dimFilter;
-    }
-  }
-
-  @Nullable
   private static DefaultLimitSpec computeLimitSpec(
       final PartialDruidQuery partialQuery,
       final RowSignature outputRowSignature
@@ -629,27 +636,43 @@ public class DruidQuery
     return toExprType.equals(fromExprType);
   }
 
-  public VirtualColumns getVirtualColumns(final ExprMacroTable macroTable, final boolean includeDimensions)
+  public VirtualColumns getVirtualColumns(final boolean includeDimensions)
   {
-    final List<VirtualColumn> retVal = new ArrayList<>();
-
+    // 'sourceRowSignature' could provide a list of all defined virtual columns while constructing a query, but we
+    // still want to collect the set of VirtualColumns this way to ensure we only add what is still being used after
+    // the various transforms and optimizations
+    Set<VirtualColumn> virtualColumns = new HashSet<>();
+
+    // we always want to add any virtual columns used by the query level DimFilter
+    if (filter != null) {
+      for (String columnName : filter.getRequiredColumns()) {
+        if (sourceQuerySignature.isVirtualColumnDefined(columnName)) {
+          virtualColumns.add(sourceQuerySignature.getVirtualColumn(columnName));
+        }
+      }
+    }
     if (selectProjection != null) {
-      retVal.addAll(selectProjection.getVirtualColumns());
+      virtualColumns.addAll(selectProjection.getVirtualColumns());
     } else {
       if (grouping != null) {
         if (includeDimensions) {
-          for (DimensionExpression dimensionExpression : grouping.getDimensions()) {
-            retVal.addAll(dimensionExpression.getVirtualColumns(macroTable));
+          for (DimensionExpression expression : grouping.getDimensions()) {
+            if (sourceQuerySignature.isVirtualColumnDefined(expression.getOutputName())) {
+              virtualColumns.add(sourceQuerySignature.getVirtualColumn(expression.getOutputName()));
+            }
           }
         }
 
         for (Aggregation aggregation : grouping.getAggregations()) {
-          retVal.addAll(aggregation.getVirtualColumns());
+          virtualColumns.addAll(aggregation.getVirtualColumns());
         }
       }
     }
 
-    return VirtualColumns.create(retVal);
+    // sort for predictable output
+    List<VirtualColumn> columns = new ArrayList<>(virtualColumns);
+    columns.sort(Comparator.comparing(VirtualColumn::getOutputName));
+    return VirtualColumns.create(columns);
   }
 
   public Grouping getGrouping()
@@ -672,11 +695,6 @@ public class DruidQuery
     return outputRowType;
   }
 
-  public RowSignature getSourceRowSignature()
-  {
-    return sourceRowSignature;
-  }
-
   public RowSignature getOutputRowSignature()
   {
     return outputRowSignature;
@@ -794,7 +812,7 @@ public class DruidQuery
       return null;
     }
 
-    final Filtration filtration = Filtration.create(filter).optimize(sourceRowSignature);
+    final Filtration filtration = Filtration.create(filter).optimize(sourceQuerySignature);
 
     final List<PostAggregator> postAggregators = new ArrayList<>(grouping.getPostAggregators());
     if (sortProject != null) {
@@ -808,7 +826,7 @@ public class DruidQuery
         dataSource,
         filtration.getQuerySegmentSpec(),
         descending,
-        getVirtualColumns(plannerContext.getExprMacroTable(), false),
+        getVirtualColumns(false),
         filtration.getDimFilter(),
         queryGranularity,
         grouping.getAggregatorFactories(),
@@ -870,7 +888,7 @@ public class DruidQuery
       return null;
     }
 
-    final Filtration filtration = Filtration.create(filter).optimize(sourceRowSignature);
+    final Filtration filtration = Filtration.create(filter).optimize(sourceQuerySignature);
 
     final List<PostAggregator> postAggregators = new ArrayList<>(grouping.getPostAggregators());
     if (sortProject != null) {
@@ -879,7 +897,7 @@ public class DruidQuery
 
     return new TopNQuery(
         dataSource,
-        getVirtualColumns(plannerContext.getExprMacroTable(), true),
+        getVirtualColumns(true),
         dimensionSpec,
         topNMetricSpec,
         limitSpec.getLimit(),
@@ -904,8 +922,17 @@ public class DruidQuery
       return null;
     }
 
-    final Filtration filtration = Filtration.create(filter).optimize(sourceRowSignature);
+    final Filtration filtration = Filtration.create(filter).optimize(sourceQuerySignature);
 
+    final DimFilterHavingSpec havingSpec;
+    if (grouping.getHavingFilter() != null) {
+      havingSpec = new DimFilterHavingSpec(
+          Filtration.create(grouping.getHavingFilter()).optimizeFilterOnly(sourceQuerySignature).getDimFilter(),
+          true
+      );
+    } else {
+      havingSpec = null;
+    }
     final List<PostAggregator> postAggregators = new ArrayList<>(grouping.getPostAggregators());
     if (sortProject != null) {
       postAggregators.addAll(sortProject.getPostAggregators());
@@ -914,13 +941,13 @@ public class DruidQuery
     return new GroupByQuery(
         dataSource,
         filtration.getQuerySegmentSpec(),
-        getVirtualColumns(plannerContext.getExprMacroTable(), true),
+        getVirtualColumns(true),
         filtration.getDimFilter(),
         Granularities.ALL,
         grouping.getDimensionSpecs(),
         grouping.getAggregatorFactories(),
         postAggregators,
-        grouping.getHavingFilter() != null ? new DimFilterHavingSpec(grouping.getHavingFilter(), true) : null,
+        havingSpec,
         limitSpec,
         null,
         ImmutableSortedMap.copyOf(plannerContext.getQueryContext())
@@ -950,7 +977,7 @@ public class DruidQuery
       throw new ISE("WTF?! Attempting to convert to Scan query without any columns?");
     }
 
-    final Filtration filtration = Filtration.create(filter).optimize(sourceRowSignature);
+    final Filtration filtration = Filtration.create(filter).optimize(sourceQuerySignature);
 
     // DefaultLimitSpec (which we use to "remember" limits) is int typed, and Integer.MAX_VALUE means "no limit".
     final long scanLimit = limitSpec == null || limitSpec.getLimit() == Integer.MAX_VALUE
@@ -983,7 +1010,7 @@ public class DruidQuery
       return null;
     }
 
-    final Filtration filtration = Filtration.create(filter).optimize(sourceRowSignature);
+    final Filtration filtration = Filtration.create(filter).optimize(sourceQuerySignature);
     final boolean descending;
     final int threshold;
 
@@ -1011,7 +1038,7 @@ public class DruidQuery
 
     // We need to ask for dummy columns to prevent Select from returning all of them.
     String dummyColumn = "dummy";
-    while (sourceRowSignature.getColumnType(dummyColumn) != null
+    while (sourceQuerySignature.getRowSignature().getColumnType(dummyColumn) != null
            || outputRowSignature.getRowOrder().contains(dummyColumn)) {
       dummyColumn = dummyColumn + "_";
     }
@@ -1044,7 +1071,7 @@ public class DruidQuery
         Granularities.ALL,
         ImmutableList.of(new DefaultDimensionSpec(dummyColumn, dummyColumn)),
         metrics.stream().sorted().distinct().collect(Collectors.toList()),
-        getVirtualColumns(plannerContext.getExprMacroTable(), true),
+        getVirtualColumns(true),
         pagingSpec,
         ImmutableSortedMap.copyOf(plannerContext.getQueryContext())
     );
diff --git a/sql/src/main/java/org/apache/druid/sql/calcite/rel/DruidQuerySignature.java b/sql/src/main/java/org/apache/druid/sql/calcite/rel/DruidQuerySignature.java
new file mode 100644
index 0000000..2b320ec
--- /dev/null
+++ b/sql/src/main/java/org/apache/druid/sql/calcite/rel/DruidQuerySignature.java
@@ -0,0 +1,148 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.druid.sql.calcite.rel;
+
+import org.apache.calcite.sql.type.SqlTypeName;
+import org.apache.druid.segment.VirtualColumn;
+import org.apache.druid.sql.calcite.expression.DruidExpression;
+import org.apache.druid.sql.calcite.planner.Calcites;
+import org.apache.druid.sql.calcite.planner.PlannerContext;
+import org.apache.druid.sql.calcite.table.RowSignature;
+
+import javax.annotation.Nullable;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.TreeSet;
+
+/**
+ * Wraps a {@link RowSignature} and provides facilities to re-use {@link VirtualColumn} definitions for dimensions,
+ * filters, and filtered aggregators while constructing a {@link DruidQuery}
+ */
+public class DruidQuerySignature
+{
+  private final RowSignature rowSignature;
+  private final boolean isAggregateSignature;
+
+  private final Map<String, VirtualColumn> virtualColumnsByExpression;
+  private final Map<String, VirtualColumn> virtualColumnsByName;
+  private final String virtualColumnPrefix;
+  private int virtualColumnCounter;
+
+  public DruidQuerySignature(RowSignature rowSignature)
+  {
+    this.isAggregateSignature = false;
+    this.rowSignature = rowSignature;
+    this.virtualColumnPrefix = rowSignature == null ? "v" : Calcites.findUnusedPrefix(
+        "v",
+        new TreeSet<>(rowSignature.getRowOrder())
+    );
+    this.virtualColumnsByExpression = new HashMap<>();
+    this.virtualColumnsByName = new HashMap<>();
+  }
+
+  private DruidQuerySignature(
+      RowSignature rowSignature,
+      String prefix,
+      Map<String, VirtualColumn> virtualColumnsByExpression,
+      Map<String, VirtualColumn> virtualColumnsByName,
+      boolean isAggregateSignature
+  )
+  {
+    this.isAggregateSignature = isAggregateSignature;
+    this.rowSignature = rowSignature;
+    this.virtualColumnPrefix = prefix;
+    this.virtualColumnsByExpression = virtualColumnsByExpression;
+    this.virtualColumnsByName = virtualColumnsByName;
+  }
+
+  /**
+   * Get {@link RowSignature} of {@link DruidQuery} under construction
+   */
+  public RowSignature getRowSignature()
+  {
+    return rowSignature;
+  }
+
+  /**
+   * Check if a {@link VirtualColumn} is defined by column name
+   */
+  public boolean isVirtualColumnDefined(String virtualColumnName)
+  {
+    return virtualColumnsByName.containsKey(virtualColumnName);
+  }
+
+
+  /**
+   * Get existing or create new (if not {@link DruidQuerySignature#isAggregateSignature}) {@link VirtualColumn} for a given
+   * {@link DruidExpression}
+   */
+  @Nullable
+  public VirtualColumn getOrCreateVirtualColumnForExpression(
+      PlannerContext plannerContext,
+      DruidExpression expression,
+      SqlTypeName typeName
+  )
+  {
+    if (!isAggregateSignature && !virtualColumnsByExpression.containsKey(expression.getExpression())) {
+      final String virtualColumnName = virtualColumnPrefix + virtualColumnCounter++;
+      final VirtualColumn virtualColumn = expression.toVirtualColumn(
+          virtualColumnName,
+          Calcites.getValueTypeForSqlTypeName(typeName),
+          plannerContext.getExprMacroTable()
+      );
+      virtualColumnsByExpression.put(
+          expression.getExpression(),
+          virtualColumn
+      );
+      virtualColumnsByName.put(
+          virtualColumnName,
+          virtualColumn
+      );
+    }
+
+    return virtualColumnsByExpression.get(expression.getExpression());
+  }
+
+  /**
+   * Get existing virtual column by column name
+   */
+  @Nullable
+  public VirtualColumn getVirtualColumn(String virtualColumnName)
+  {
+    return virtualColumnsByName.getOrDefault(virtualColumnName, null);
+  }
+
+  /**
+   * Create as an "immutable" "aggregate" signature for a grouping, so that post aggregations and having filters
+   * can not define new virtual columns
+   * @param sourceSignature
+   * @return
+   */
+  public DruidQuerySignature asAggregateSignature(RowSignature sourceSignature)
+  {
+    return new DruidQuerySignature(
+        sourceSignature,
+        virtualColumnPrefix,
+        virtualColumnsByExpression,
+        virtualColumnsByName,
+        true
+    );
+  }
+}
diff --git a/sql/src/main/java/org/apache/druid/sql/calcite/rule/GroupByRules.java b/sql/src/main/java/org/apache/druid/sql/calcite/rule/GroupByRules.java
index a3c35c0..5989152 100644
--- a/sql/src/main/java/org/apache/druid/sql/calcite/rule/GroupByRules.java
+++ b/sql/src/main/java/org/apache/druid/sql/calcite/rule/GroupByRules.java
@@ -31,7 +31,7 @@ import org.apache.druid.sql.calcite.aggregation.SqlAggregator;
 import org.apache.druid.sql.calcite.expression.Expressions;
 import org.apache.druid.sql.calcite.filtration.Filtration;
 import org.apache.druid.sql.calcite.planner.PlannerContext;
-import org.apache.druid.sql.calcite.table.RowSignature;
+import org.apache.druid.sql.calcite.rel.DruidQuerySignature;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -52,12 +52,12 @@ public class GroupByRules
    */
   public static Aggregation translateAggregateCall(
       final PlannerContext plannerContext,
-      final RowSignature sourceRowSignature,
+      final DruidQuerySignature querySignature,
       final RexBuilder rexBuilder,
       final Project project,
-      final AggregateCall call,
       final List<Aggregation> existingAggregations,
       final String name,
+      final AggregateCall call,
       final boolean finalizeAggregations
   )
   {
@@ -71,11 +71,11 @@ public class GroupByRules
       }
 
       final RexNode expression = project.getChildExps().get(call.filterArg);
-      final DimFilter nonOptimizedFilter = Expressions.toFilter(plannerContext, sourceRowSignature, expression);
+      final DimFilter nonOptimizedFilter = Expressions.toFilter(plannerContext, querySignature, expression);
       if (nonOptimizedFilter == null) {
         return null;
       } else {
-        filter = Filtration.create(nonOptimizedFilter).optimizeFilterOnly(sourceRowSignature).getDimFilter();
+        filter = Filtration.create(nonOptimizedFilter).optimizeFilterOnly(querySignature).getDimFilter();
       }
     } else {
       filter = null;
@@ -121,12 +121,9 @@ public class GroupByRules
 
     final Aggregation retVal = sqlAggregator.toDruidAggregation(
         plannerContext,
-        sourceRowSignature,
+        querySignature,
         rexBuilder,
-        name,
-        call,
-        project,
-        existingAggregationsWithSameFilter,
+        name, call, project, existingAggregationsWithSameFilter,
         finalizeAggregations
     );
 
@@ -137,7 +134,7 @@ public class GroupByRules
       if (isUsingExistingAggregation(retVal, existingAggregationsWithSameFilter)) {
         return retVal;
       } else {
-        return retVal.filter(sourceRowSignature, filter);
+        return retVal.filter(querySignature, filter);
       }
     }
   }
diff --git a/sql/src/test/java/org/apache/druid/sql/calcite/BaseCalciteQueryTest.java b/sql/src/test/java/org/apache/druid/sql/calcite/BaseCalciteQueryTest.java
index 4ee3b1f..29247b5 100644
--- a/sql/src/test/java/org/apache/druid/sql/calcite/BaseCalciteQueryTest.java
+++ b/sql/src/test/java/org/apache/druid/sql/calcite/BaseCalciteQueryTest.java
@@ -243,22 +243,22 @@ public class BaseCalciteQueryTest extends CalciteTestBase
   }
 
   // Generate timestamps for expected results
-  public static long t(final String timeString)
+  public static long timestamp(final String timeString)
   {
     return Calcites.jodaToCalciteTimestamp(DateTimes.of(timeString), DateTimeZone.UTC);
   }
 
   // Generate timestamps for expected results
-  public static long t(final String timeString, final String timeZoneString)
+  public static long timestamp(final String timeString, final String timeZoneString)
   {
     final DateTimeZone timeZone = DateTimes.inferTzFromString(timeZoneString);
     return Calcites.jodaToCalciteTimestamp(new DateTime(timeString, timeZone), timeZone);
   }
 
   // Generate day numbers for expected results
-  public static int d(final String dayString)
+  public static int day(final String dayString)
   {
-    return (int) (Intervals.utc(t("1970"), t(dayString)).toDurationMillis() / (86400L * 1000L));
+    return (int) (Intervals.utc(timestamp("1970"), timestamp(dayString)).toDurationMillis() / (86400L * 1000L));
   }
 
   public static QuerySegmentSpec querySegmentSpec(final Interval... intervals)
@@ -296,7 +296,7 @@ public class BaseCalciteQueryTest extends CalciteTestBase
     return new ExpressionDimFilter(expression, CalciteTests.createExprMacroTable());
   }
 
-  public static DimFilter numeric_Selector(
+  public static DimFilter numericSelector(
       final String fieldName,
       final String value,
       final ExtractionFn extractionFn
@@ -339,7 +339,7 @@ public class BaseCalciteQueryTest extends CalciteTestBase
     return new CascadeExtractionFn(fns);
   }
 
-  public static List<DimensionSpec> dimensionSpec(final DimensionSpec... dimensionSpecs)
+  public static List<DimensionSpec> dimensions(final DimensionSpec... dimensionSpecs)
   {
     return Arrays.asList(dimensionSpecs);
   }
@@ -354,7 +354,7 @@ public class BaseCalciteQueryTest extends CalciteTestBase
     return new DimFilterHavingSpec(filter, true);
   }
 
-  public static ExpressionVirtualColumn expression_Virtual_Column(
+  public static ExpressionVirtualColumn expressionVirtualColumn(
       final String name,
       final String expression,
       final ValueType outputType
@@ -363,7 +363,7 @@ public class BaseCalciteQueryTest extends CalciteTestBase
     return new ExpressionVirtualColumn(name, expression, outputType, CalciteTests.createExprMacroTable());
   }
 
-  public static ExpressionPostAggregator expresionPostAgg(final String name, final String expression)
+  public static ExpressionPostAggregator expressionPostAgg(final String name, final String expression)
   {
     return new ExpressionPostAggregator(name, expression, null, CalciteTests.createExprMacroTable());
   }
diff --git a/sql/src/test/java/org/apache/druid/sql/calcite/CalciteQueryTest.java b/sql/src/test/java/org/apache/druid/sql/calcite/CalciteQueryTest.java
index 839d892..f18c276 100644
--- a/sql/src/test/java/org/apache/druid/sql/calcite/CalciteQueryTest.java
+++ b/sql/src/test/java/org/apache/druid/sql/calcite/CalciteQueryTest.java
@@ -109,7 +109,7 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
             newScanQueryBuilder()
                 .dataSource(CalciteTests.DATASOURCE1)
                 .intervals(querySegmentSpec(Filtration.eternity()))
-                .virtualColumns(expression_Virtual_Column("v0", "2", ValueType.LONG))
+                .virtualColumns(expressionVirtualColumn("v0", "2", ValueType.LONG))
                 .columns("dim1", "v0")
                 .resultFormat(ScanQuery.RESULT_FORMAT_COMPACTED_LIST)
                 .limit(1)
@@ -141,7 +141,7 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                                    new DoubleSumAggregatorFactory("a1", "m2")
                                ))
                                .postAggregators(
-                                   expresionPostAgg("p0", "(exp(\"a0\") + 10)")
+                                   expressionPostAgg("p0", "(exp(\"a0\") + 10)")
                                )
                                .context(QUERY_CONTEXT_DONT_SKIP_EMPTY_BUCKETS)
                                .build()),
@@ -164,7 +164,7 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                                    new DoubleSumAggregatorFactory("a1", "m2")
                                ))
                                .postAggregators(
-                                   expresionPostAgg("p0", "(exp(\"a0\") + 10)")
+                                   expressionPostAgg("p0", "(exp(\"a0\") + 10)")
                                )
                                .context(QUERY_CONTEXT_DONT_SKIP_EMPTY_BUCKETS)
                                .build()),
@@ -216,17 +216,17 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                   .granularity(Granularities.ALL)
                   .aggregators(aggregators(new CountAggregatorFactory("a0")))
                   .postAggregators(
-                      expresionPostAgg("p0", "'foo'"),
-                      expresionPostAgg("p1", "'xfoo'"),
-                      expresionPostAgg("p2", "'foo'"),
-                      expresionPostAgg("p3", "' foo'"),
-                      expresionPostAgg("p4", "'foo'"),
-                      expresionPostAgg("p5", "'foo'"),
-                      expresionPostAgg("p6", "'foo'"),
-                      expresionPostAgg("p7", "'foo '"),
-                      expresionPostAgg("p8", "'foox'"),
-                      expresionPostAgg("p9", "' foo'"),
-                      expresionPostAgg("p10", "'xfoo'")
+                      expressionPostAgg("p0", "'foo'"),
+                      expressionPostAgg("p1", "'xfoo'"),
+                      expressionPostAgg("p2", "'foo'"),
+                      expressionPostAgg("p3", "' foo'"),
+                      expressionPostAgg("p4", "'foo'"),
+                      expressionPostAgg("p5", "'foo'"),
+                      expressionPostAgg("p6", "'foo'"),
+                      expressionPostAgg("p7", "'foo '"),
+                      expressionPostAgg("p8", "'foox'"),
+                      expressionPostAgg("p9", "' foo'"),
+                      expressionPostAgg("p10", "'xfoo'")
                   )
                   .context(TIMESERIES_CONTEXT_DEFAULT)
                   .build()
@@ -436,14 +436,14 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                 .build()
         ),
         ImmutableList.of(
-            new Object[]{t("2000-01-01"), 1L, "", "a", "[\"a\",\"b\"]", 1f, 1.0, hyperLogLogCollectorClassName},
+            new Object[]{timestamp("2000-01-01"), 1L, "", "a", "[\"a\",\"b\"]", 1f, 1.0, hyperLogLogCollectorClassName},
             new Object[]{
-                t("2000-01-02"), 1L, "10.1", NULL_VALUE, "[\"b\",\"c\"]", 2f, 2.0, hyperLogLogCollectorClassName
+                timestamp("2000-01-02"), 1L, "10.1", NULL_VALUE, "[\"b\",\"c\"]", 2f, 2.0, hyperLogLogCollectorClassName
             },
-            new Object[]{t("2000-01-03"), 1L, "2", "", "d", 3f, 3.0, hyperLogLogCollectorClassName},
-            new Object[]{t("2001-01-01"), 1L, "1", "a", "", 4f, 4.0, hyperLogLogCollectorClassName},
-            new Object[]{t("2001-01-02"), 1L, "def", "abc", NULL_VALUE, 5f, 5.0, hyperLogLogCollectorClassName},
-            new Object[]{t("2001-01-03"), 1L, "abc", NULL_VALUE, NULL_VALUE, 6f, 6.0, hyperLogLogCollectorClassName}
+            new Object[]{timestamp("2000-01-03"), 1L, "2", "", "d", 3f, 3.0, hyperLogLogCollectorClassName},
+            new Object[]{timestamp("2001-01-01"), 1L, "1", "a", "", 4f, 4.0, hyperLogLogCollectorClassName},
+            new Object[]{timestamp("2001-01-02"), 1L, "def", "abc", NULL_VALUE, 5f, 5.0, hyperLogLogCollectorClassName},
+            new Object[]{timestamp("2001-01-03"), 1L, "abc", NULL_VALUE, NULL_VALUE, 6f, 6.0, hyperLogLogCollectorClassName}
         )
     );
   }
@@ -471,7 +471,7 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
         ),
         ImmutableList.of(
             new Object[]{
-                t("2000-01-01"),
+                timestamp("2000-01-01"),
                 1L,
                 "forbidden",
                 "abcd",
@@ -536,8 +536,8 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                 .build()
         ),
         ImmutableList.of(
-            new Object[]{t("2000-01-01"), 1L, "", "a", "[\"a\",\"b\"]", 1.0f, 1.0, HLLC_STRING},
-            new Object[]{t("2000-01-02"), 1L, "10.1", NULL_VALUE, "[\"b\",\"c\"]", 2.0f, 2.0, HLLC_STRING}
+            new Object[]{timestamp("2000-01-01"), 1L, "", "a", "[\"a\",\"b\"]", 1.0f, 1.0, HLLC_STRING},
+            new Object[]{timestamp("2000-01-02"), 1L, "10.1", NULL_VALUE, "[\"b\",\"c\"]", 2.0f, 2.0, HLLC_STRING}
         )
     );
   }
@@ -552,7 +552,7 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                 .dataSource(CalciteTests.DATASOURCE1)
                 .intervals(querySegmentSpec(Filtration.eternity()))
                 .virtualColumns(
-                    expression_Virtual_Column("v0", "substring(\"dim2\", 0, 1)", ValueType.STRING)
+                    expressionVirtualColumn("v0", "substring(\"dim2\", 0, 1)", ValueType.STRING)
                 )
                 .columns("v0")
                 .limit(2)
@@ -588,8 +588,8 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                   .build()
         ),
         ImmutableList.of(
-            new Object[]{t("2001-01-03"), 1L, "abc", NULL_VALUE, NULL_VALUE, 6f, 6d, HLLC_STRING},
-            new Object[]{t("2001-01-02"), 1L, "def", "abc", NULL_VALUE, 5f, 5d, HLLC_STRING}
+            new Object[]{timestamp("2001-01-03"), 1L, "abc", NULL_VALUE, NULL_VALUE, 6f, 6d, HLLC_STRING},
+            new Object[]{timestamp("2001-01-02"), 1L, "def", "abc", NULL_VALUE, 5f, 5d, HLLC_STRING}
         )
     );
   }
@@ -631,12 +631,12 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                   .build()
         ),
         ImmutableList.of(
-            new Object[]{t("2000-01-01"), 1L, "", "a", "[\"a\",\"b\"]", 1f, 1.0, HLLC_STRING},
-            new Object[]{t("2000-01-02"), 1L, "10.1", NULL_VALUE, "[\"b\",\"c\"]", 2f, 2.0, HLLC_STRING},
-            new Object[]{t("2000-01-03"), 1L, "2", "", "d", 3f, 3.0, HLLC_STRING},
-            new Object[]{t("2001-01-01"), 1L, "1", "a", "", 4f, 4.0, HLLC_STRING},
-            new Object[]{t("2001-01-02"), 1L, "def", "abc", NULL_VALUE, 5f, 5.0, HLLC_STRING},
-            new Object[]{t("2001-01-03"), 1L, "abc", NULL_VALUE, NULL_VALUE, 6f, 6.0, HLLC_STRING}
+            new Object[]{timestamp("2000-01-01"), 1L, "", "a", "[\"a\",\"b\"]", 1f, 1.0, HLLC_STRING},
+            new Object[]{timestamp("2000-01-02"), 1L, "10.1", NULL_VALUE, "[\"b\",\"c\"]", 2f, 2.0, HLLC_STRING},
+            new Object[]{timestamp("2000-01-03"), 1L, "2", "", "d", 3f, 3.0, HLLC_STRING},
+            new Object[]{timestamp("2001-01-01"), 1L, "1", "a", "", 4f, 4.0, HLLC_STRING},
+            new Object[]{timestamp("2001-01-02"), 1L, "def", "abc", NULL_VALUE, 5f, 5.0, HLLC_STRING},
+            new Object[]{timestamp("2001-01-03"), 1L, "abc", NULL_VALUE, NULL_VALUE, 6f, 6.0, HLLC_STRING}
         )
     );
   }
@@ -672,7 +672,7 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
             Druids.newSelectQueryBuilder()
                   .dataSource(CalciteTests.DATASOURCE1)
                   .intervals(querySegmentSpec(Filtration.eternity()))
-                  .dimensionSpecs(dimensionSpec(new DefaultDimensionSpec("dim1", "d1")))
+                  .dimensionSpecs(dimensions(new DefaultDimensionSpec("dim1", "d1")))
                   .granularity(Granularities.ALL)
                   .descending(true)
                   .dimensions(ImmutableList.of("dummy"))
@@ -699,7 +699,7 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
             new GroupByQuery.Builder()
                 .setDataSource(CalciteTests.DATASOURCE1)
                 .setInterval(querySegmentSpec(Filtration.eternity()))
-                .setDimensions(dimensionSpec(new DefaultDimensionSpec("dim1", "d0")))
+                .setDimensions(dimensions(new DefaultDimensionSpec("dim1", "d0")))
                 .setGranularity(Granularities.ALL)
                 .setLimitSpec(
                     new DefaultLimitSpec(
@@ -798,7 +798,7 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                         .setDataSource(CalciteTests.DATASOURCE1)
                         .setInterval(querySegmentSpec(Filtration.eternity()))
                         .setGranularity(Granularities.ALL)
-                        .setDimensions(dimensionSpec(new DefaultDimensionSpec("cnt", "d0", ValueType.LONG)))
+                        .setDimensions(dimensions(new DefaultDimensionSpec("cnt", "d0", ValueType.LONG)))
                         .setAggregatorSpecs(aggregators(new CountAggregatorFactory("a0")))
                         .setContext(QUERY_CONTEXT_DEFAULT)
                         .build()
@@ -819,7 +819,7 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                         .setDataSource(CalciteTests.DATASOURCE1)
                         .setInterval(querySegmentSpec(Filtration.eternity()))
                         .setGranularity(Granularities.ALL)
-                        .setDimensions(dimensionSpec(new DefaultDimensionSpec("cnt", "d0", ValueType.LONG)))
+                        .setDimensions(dimensions(new DefaultDimensionSpec("cnt", "d0", ValueType.LONG)))
                         .setAggregatorSpecs(aggregators(new CountAggregatorFactory("a0")))
                         .setContext(QUERY_CONTEXT_DEFAULT)
                         .build()
@@ -841,7 +841,7 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                         .setDataSource(CalciteTests.DATASOURCE1)
                         .setInterval(querySegmentSpec(Filtration.eternity()))
                         .setGranularity(Granularities.ALL)
-                        .setDimensions(dimensionSpec(new DefaultDimensionSpec("cnt", "d0", ValueType.LONG)))
+                        .setDimensions(dimensions(new DefaultDimensionSpec("cnt", "d0", ValueType.LONG)))
                         .setAggregatorSpecs(aggregators(new CountAggregatorFactory("a0")))
                         .setLimitSpec(
                             new DefaultLimitSpec(
@@ -883,8 +883,8 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                   .build()
         ),
         ImmutableList.of(
-            new Object[]{t("2000-01-01"), 3L},
-            new Object[]{t("2001-01-01"), 3L}
+            new Object[]{timestamp("2000-01-01"), 3L},
+            new Object[]{timestamp("2001-01-01"), 3L}
         )
     );
   }
@@ -899,7 +899,7 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                         .setDataSource(CalciteTests.DATASOURCE1)
                         .setInterval(querySegmentSpec(Filtration.eternity()))
                         .setGranularity(Granularities.ALL)
-                        .setDimensions(dimensionSpec(new DefaultDimensionSpec("cnt", "d0", ValueType.LONG)))
+                        .setDimensions(dimensions(new DefaultDimensionSpec("cnt", "d0", ValueType.LONG)))
                         .setAggregatorSpecs(aggregators(new CountAggregatorFactory("a0")))
                         .setLimitSpec(
                             new DefaultLimitSpec(
@@ -932,7 +932,7 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                         .setDataSource(CalciteTests.DATASOURCE1)
                         .setInterval(querySegmentSpec(Filtration.eternity()))
                         .setGranularity(Granularities.ALL)
-                        .setDimensions(dimensionSpec(new DefaultDimensionSpec("m1", "d0", ValueType.FLOAT)))
+                        .setDimensions(dimensions(new DefaultDimensionSpec("m1", "d0", ValueType.FLOAT)))
                         .setAggregatorSpecs(aggregators(new CountAggregatorFactory("a0")))
                         .setContext(QUERY_CONTEXT_DEFAULT)
                         .build()
@@ -958,7 +958,7 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                         .setDataSource(CalciteTests.DATASOURCE1)
                         .setInterval(querySegmentSpec(Filtration.eternity()))
                         .setGranularity(Granularities.ALL)
-                        .setDimensions(dimensionSpec(new DefaultDimensionSpec("m2", "d0", ValueType.DOUBLE)))
+                        .setDimensions(dimensions(new DefaultDimensionSpec("m2", "d0", ValueType.DOUBLE)))
                         .setAggregatorSpecs(aggregators(new CountAggregatorFactory("a0")))
                         .setContext(QUERY_CONTEXT_DEFAULT)
                         .build()
@@ -1027,7 +1027,7 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                         .setInterval(querySegmentSpec(Filtration.eternity()))
                         .setGranularity(Granularities.ALL)
                         .setAggregatorSpecs(aggregators(new DoubleSumAggregatorFactory("a0", "m1")))
-                        .setHavingSpec(having(numeric_Selector("a0", "21", null)))
+                        .setHavingSpec(having(selector("a0", "21", null)))
                         .setContext(QUERY_CONTEXT_DEFAULT)
                         .build()
         ),
@@ -1047,7 +1047,7 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                         .setDataSource(CalciteTests.DATASOURCE1)
                         .setInterval(querySegmentSpec(Filtration.eternity()))
                         .setGranularity(Granularities.ALL)
-                        .setDimensions(dimensionSpec(new DefaultDimensionSpec("dim1", "d0")))
+                        .setDimensions(dimensions(new DefaultDimensionSpec("dim1", "d0")))
                         .setAggregatorSpecs(aggregators(new DoubleSumAggregatorFactory("a0", "m1")))
                         .setHavingSpec(
                             having(
@@ -1086,7 +1086,7 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                         .setDataSource(CalciteTests.DATASOURCE1)
                         .setInterval(querySegmentSpec(Filtration.eternity()))
                         .setGranularity(Granularities.ALL)
-                        .setDimensions(dimensionSpec(new DefaultDimensionSpec("dim2", "d0")))
+                        .setDimensions(dimensions(new DefaultDimensionSpec("dim2", "d0")))
                         .setAggregatorSpecs(
                             aggregators(
                                 new CardinalityAggregatorFactory(
@@ -1144,7 +1144,7 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                                             .setInterval(querySegmentSpec(Filtration.eternity()))
                                             .setGranularity(Granularities.ALL)
                                             .setDimensions(
-                                                dimensionSpec(
+                                                dimensions(
                                                     new DefaultDimensionSpec("dim2", "d0", ValueType.STRING),
                                                     new DefaultDimensionSpec("m1", "d1", ValueType.FLOAT)
                                                 )
@@ -1155,7 +1155,7 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                         )
                         .setInterval(querySegmentSpec(Filtration.eternity()))
                         .setGranularity(Granularities.ALL)
-                        .setDimensions(dimensionSpec(new DefaultDimensionSpec("d0", "_d0", ValueType.STRING)))
+                        .setDimensions(dimensions(new DefaultDimensionSpec("d0", "_d0", ValueType.STRING)))
                         .setAggregatorSpecs(aggregators(new CountAggregatorFactory("a0")))
                         .setHavingSpec(
                             having(
@@ -1190,14 +1190,14 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
   {
     testQuery(
         PLANNER_CONFIG_FALLBACK,
-        "SELECT dim1, CASt(SUM(m1) AS FLOAT) AS m1_sum FROM druid.foo GROUP BY dim1 HAVING CAST(SUM(m1) AS FLOAT) > 1",
+        "SELECT dim1, CAST(SUM(m1) AS FLOAT) AS m1_sum FROM druid.foo GROUP BY dim1 HAVING CAST(SUM(m1) AS FLOAT) > 1",
         CalciteTests.REGULAR_USER_AUTH_RESULT,
         ImmutableList.of(
             GroupByQuery.builder()
                         .setDataSource(CalciteTests.DATASOURCE1)
                         .setInterval(querySegmentSpec(Filtration.eternity()))
                         .setGranularity(Granularities.ALL)
-                        .setDimensions(dimensionSpec(new DefaultDimensionSpec("dim1", "d0")))
+                        .setDimensions(dimensions(new DefaultDimensionSpec("dim1", "d0")))
                         .setAggregatorSpecs(aggregators(new DoubleSumAggregatorFactory("a0", "m1")))
                         .setHavingSpec(
                             having(
@@ -1237,7 +1237,7 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                         .setInterval(querySegmentSpec(Filtration.eternity()))
                         .setGranularity(Granularities.ALL)
                         .setDimFilter(expressionFilter("((\"m1\" - 1) == \"dim1\")"))
-                        .setDimensions(dimensionSpec(
+                        .setDimensions(dimensions(
                             new DefaultDimensionSpec("dim1", "d0"),
                             new DefaultDimensionSpec("m1", "d1", ValueType.FLOAT)
                         ))
@@ -1273,7 +1273,7 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                         .setDataSource(CalciteTests.DATASOURCE1)
                         .setInterval(querySegmentSpec(Filtration.eternity()))
                         .setGranularity(Granularities.ALL)
-                        .setDimensions(dimensionSpec(new DefaultDimensionSpec("dim1", "d0")))
+                        .setDimensions(dimensions(new DefaultDimensionSpec("dim1", "d0")))
                         .setAggregatorSpecs(aggregators(
                             new FilteredAggregatorFactory(
                                 new CountAggregatorFactory("a0"),
@@ -1282,7 +1282,7 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                             new CountAggregatorFactory("a1")
                         ))
                         .setPostAggregatorSpecs(ImmutableList.of(
-                            expresionPostAgg("p0", "(\"a0\" / \"a1\")")
+                            expressionPostAgg("p0", "(\"a0\" / \"a1\")")
                         ))
                         .setHavingSpec(having(expressionFilter("((\"a0\" / \"a1\") == 1)")))
                         .setContext(QUERY_CONTEXT_DEFAULT)
@@ -1311,9 +1311,9 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                         .setDataSource(CalciteTests.DATASOURCE1)
                         .setInterval(querySegmentSpec(Filtration.eternity()))
                         .setGranularity(Granularities.ALL)
-                        .setDimensions(dimensionSpec(new DefaultDimensionSpec("dim1", "d0")))
+                        .setDimensions(dimensions(new DefaultDimensionSpec("dim1", "d0")))
                         .setPostAggregatorSpecs(ImmutableList.of(
-                            expresionPostAgg("p0", "substring(\"d0\", 1, -1)")
+                            expressionPostAgg("p0", "substring(\"d0\", 1, -1)")
                         ))
                         .setContext(QUERY_CONTEXT_DEFAULT)
                         .build()
@@ -1344,10 +1344,10 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                         .setDataSource(CalciteTests.DATASOURCE1)
                         .setInterval(querySegmentSpec(Filtration.eternity()))
                         .setGranularity(Granularities.ALL)
-                        .setDimensions(dimensionSpec(new DefaultDimensionSpec("dim1", "d0")))
+                        .setDimensions(dimensions(new DefaultDimensionSpec("dim1", "d0")))
                         .setPostAggregatorSpecs(ImmutableList.of(
-                            expresionPostAgg("p0", "substring(\"d0\", 1, -1)"),
-                            expresionPostAgg("p1", "strlen(\"d0\")")
+                            expressionPostAgg("p0", "substring(\"d0\", 1, -1)"),
+                            expressionPostAgg("p1", "strlen(\"d0\")")
                         ))
                         .setLimitSpec(new DefaultLimitSpec(
                             ImmutableList.of(
@@ -1395,7 +1395,7 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                 .granularity(Granularities.ALL)
                 .dimension(new DefaultDimensionSpec("dim1", "d0"))
                 .postAggregators(ImmutableList.of(
-                    expresionPostAgg("p0", "substring(\"d0\", 1, -1)")
+                    expressionPostAgg("p0", "substring(\"d0\", 1, -1)")
                 ))
                 .metric(new DimensionTopNMetricSpec(null, StringComparators.LEXICOGRAPHIC))
                 .threshold(10)
@@ -1432,8 +1432,8 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                 .granularity(Granularities.ALL)
                 .dimension(new DefaultDimensionSpec("dim1", "d0"))
                 .postAggregators(ImmutableList.of(
-                    expresionPostAgg("p0", "substring(\"d0\", 1, -1)"),
-                    expresionPostAgg("p1", "strlen(\"d0\")")
+                    expressionPostAgg("p0", "substring(\"d0\", 1, -1)"),
+                    expressionPostAgg("p1", "strlen(\"d0\")")
                 ))
                 .metric(new NumericTopNMetricSpec("p1"))
                 .threshold(10)
@@ -1555,7 +1555,7 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                   .intervals(querySegmentSpec(Filtration.eternity()))
                   .granularity(Granularities.ALL)
                   .aggregators(aggregators(new DoubleSumAggregatorFactory("a0", "m1")))
-                  .postAggregators(ImmutableList.of(expresionPostAgg("p0", "(\"a0\" / 10)")))
+                  .postAggregators(ImmutableList.of(expressionPostAgg("p0", "(\"a0\" / 10)")))
                   .context(TIMESERIES_CONTEXT_DEFAULT)
                   .build()
         ),
@@ -1583,7 +1583,7 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                         .setInterval(querySegmentSpec(Filtration.eternity()))
                         .setGranularity(Granularities.ALL)
                         .setAggregatorSpecs(aggregators(new DoubleSumAggregatorFactory("a0", "m1")))
-                        .setHavingSpec(having(numeric_Selector("a0", "21", null)))
+                        .setHavingSpec(having(selector("a0", "21", null)))
                         .setContext(QUERY_CONTEXT_DEFAULT)
                         .build()
         ),
@@ -1615,8 +1615,8 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                         .setInterval(querySegmentSpec(Filtration.eternity()))
                         .setGranularity(Granularities.ALL)
                         .setVirtualColumns(
-                            expression_Virtual_Column(
-                                "d0:v",
+                            expressionVirtualColumn(
+                                "v0",
                                 "case_searched("
                                 + "(CAST(timestamp_extract(\"__time\",'DAY','UTC'), 'DOUBLE') == \"m1\"),"
                                 + "'match-m1',"
@@ -1628,7 +1628,7 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                                 ValueType.STRING
                             )
                         )
-                        .setDimensions(dimensionSpec(new DefaultDimensionSpec("d0:v", "d0")))
+                        .setDimensions(dimensions(new DefaultDimensionSpec("v0", "v0")))
                         .setAggregatorSpecs(aggregators(new CountAggregatorFactory("a0")))
                         .setContext(QUERY_CONTEXT_DEFAULT)
                         .build()
@@ -1656,13 +1656,13 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                         .setInterval(querySegmentSpec(Filtration.eternity()))
                         .setGranularity(Granularities.ALL)
                         .setVirtualColumns(
-                            expression_Virtual_Column(
-                                "d0:v",
+                            expressionVirtualColumn(
+                                "v0",
                                 "case_searched(((\"m1\" > 1) && (\"m1\" < 5) && (\"cnt\" == 1)),'x',null)",
                                 ValueType.STRING
                             )
                         )
-                        .setDimensions(dimensionSpec(new DefaultDimensionSpec("d0:v", "d0")))
+                        .setDimensions(dimensions(new DefaultDimensionSpec("v0", "v0")))
                         .setAggregatorSpecs(aggregators(new CountAggregatorFactory("a0")))
                         .setContext(QUERY_CONTEXT_DEFAULT)
                         .build()
@@ -1772,13 +1772,13 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                         .setInterval(querySegmentSpec(Filtration.eternity()))
                         .setGranularity(Granularities.ALL)
                         .setVirtualColumns(
-                            expression_Virtual_Column(
-                                "d0:v",
+                            expressionVirtualColumn(
+                                "v0",
                                 "case_searched(notnull(\"dim2\"),\"dim2\",\"dim1\")",
                                 ValueType.STRING
                             )
                         )
-                        .setDimensions(dimensionSpec(new DefaultDimensionSpec("d0:v", "d0", ValueType.STRING)))
+                        .setDimensions(dimensions(new DefaultDimensionSpec("v0", "v0", ValueType.STRING)))
                         .setAggregatorSpecs(aggregators(new CountAggregatorFactory("a0")))
                         .setContext(QUERY_CONTEXT_DEFAULT)
                         .build()
@@ -1884,9 +1884,9 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                 .build()
         ),
         ImmutableList.of(
-            new Object[]{t("2000-01-01"), 1L, "", "a", "[\"a\",\"b\"]", 1.0f, 1.0d, HLLC_STRING},
-            new Object[]{t("2001-01-01"), 1L, "1", "a", "", 4.0f, 4.0d, HLLC_STRING},
-            new Object[]{t("2001-01-02"), 1L, "def", "abc", NULL_VALUE, 5.0f, 5.0d, HLLC_STRING}
+            new Object[]{timestamp("2000-01-01"), 1L, "", "a", "[\"a\",\"b\"]", 1.0f, 1.0d, HLLC_STRING},
+            new Object[]{timestamp("2001-01-01"), 1L, "1", "a", "", 4.0f, 4.0d, HLLC_STRING},
+            new Object[]{timestamp("2001-01-02"), 1L, "def", "abc", NULL_VALUE, 5.0f, 5.0d, HLLC_STRING}
         )
     );
   }
@@ -2019,14 +2019,17 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                   .dataSource(CalciteTests.DATASOURCE1)
                   .intervals(querySegmentSpec(Filtration.eternity()))
                   .granularity(Granularities.ALL)
+                  .virtualColumns(
+                      expressionVirtualColumn(
+                          "v0",
+                          "case_searched((\"dim2\" == 'abc'),'yes',(\"dim2\" == 'def'),'yes'," + DruidExpression.nullLiteral() + ")",
+                          ValueType.STRING
+                      )
+                  )
                   .aggregators(aggregators(
                       new FilteredAggregatorFactory(
                           new CountAggregatorFactory("a0"),
-                          expressionFilter(
-                              "notnull(case_searched((\"dim2\" == 'abc'),'yes',(\"dim2\" == 'def'),'yes',"
-                              + DruidExpression.nullLiteral()
-                              + "))"
-                          )
+                          not(selector("v0", NullHandling.defaultStringValue(), null))
                       )
                   ))
                   .context(TIMESERIES_CONTEXT_DEFAULT)
@@ -2295,12 +2298,18 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                         .setDataSource(CalciteTests.DATASOURCE1)
                         .setInterval(querySegmentSpec(Filtration.eternity()))
                         .setGranularity(Granularities.ALL)
-                        .setDimensions(dimensionSpec(new DefaultDimensionSpec("dim1", "d0")))
+                        .setDimensions(dimensions(new DefaultDimensionSpec("dim1", "d0")))
+                        .setVirtualColumns(
+                            expressionVirtualColumn(
+                                "v0",
+                                "floor(CAST(\"dim1\", 'DOUBLE'))",
+                                ValueType.DOUBLE)
+                        )
                         .setDimFilter(
                             or(
                                 selector("dim1", "10", null),
                                 and(
-                                    expressionFilter("(floor(CAST(\"dim1\", 'DOUBLE')) == 10.00)"),
+                                    selector("v0", "10.00", null),
                                     bound("dim1", "9", "10.5", true, false, null, StringComparators.NUMERIC)
                                 )
                             )
@@ -2351,7 +2360,7 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                               new FieldAccessPostAggregator(null, "a2:count")
                           )
                       ),
-                      expresionPostAgg("p0", "((\"a3\" + \"a4\") + \"a5\")")
+                      expressionPostAgg("p0", "((\"a3\" + \"a4\") + \"a5\")")
                   )
                   .context(TIMESERIES_CONTEXT_DEFAULT)
                   .build()
@@ -2386,7 +2395,7 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                 ))
                 .postAggregators(
                     ImmutableList.of(
-                        expresionPostAgg("p0", "(\"a0\" + \"a1\")")
+                        expressionPostAgg("p0", "(\"a0\" + \"a1\")")
                     )
                 )
                 .threshold(3)
@@ -2415,12 +2424,12 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                         .setDataSource(CalciteTests.DATASOURCE1)
                         .setInterval(querySegmentSpec(Filtration.eternity()))
                         .setGranularity(Granularities.ALL)
-                        .setDimensions(dimensionSpec(new DefaultDimensionSpec("dim1", "d0")))
+                        .setDimensions(dimensions(new DefaultDimensionSpec("dim1", "d0")))
                         .setAggregatorSpecs(
                             new FloatMinAggregatorFactory("a0", "m1"),
                             new FloatMaxAggregatorFactory("a1", "m1")
                         )
-                        .setPostAggregatorSpecs(ImmutableList.of(expresionPostAgg("p0", "(\"a0\" + \"a1\")")))
+                        .setPostAggregatorSpecs(ImmutableList.of(expressionPostAgg("p0", "(\"a0\" + \"a1\")")))
                         .setLimitSpec(
                             new DefaultLimitSpec(
                                 ImmutableList.of(
@@ -2459,14 +2468,14 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                         .setDataSource(CalciteTests.DATASOURCE1)
                         .setInterval(querySegmentSpec(Filtration.eternity()))
                         .setGranularity(Granularities.ALL)
-                        .setDimensions(dimensionSpec(new DefaultDimensionSpec("dim1", "d0")))
+                        .setDimensions(dimensions(new DefaultDimensionSpec("dim1", "d0")))
                         .setAggregatorSpecs(
                             new FloatMinAggregatorFactory("a0", "m1"),
                             new FloatMaxAggregatorFactory("a1", "m1")
                         )
                         .setPostAggregatorSpecs(
                             ImmutableList.of(
-                                expresionPostAgg("p0", "(\"a0\" + \"a1\")")
+                                expressionPostAgg("p0", "(\"a0\" + \"a1\")")
                             )
                         )
                         .setLimitSpec(
@@ -2566,7 +2575,7 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                           new CardinalityAggregatorFactory(
                               "a10",
                               null,
-                              dimensionSpec(new DefaultDimensionSpec("m1", "m1", ValueType.FLOAT)),
+                              dimensions(new DefaultDimensionSpec("m1", "m1", ValueType.FLOAT)),
                               false,
                               true
                           ),
@@ -2604,7 +2613,7 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                         .setDataSource(CalciteTests.DATASOURCE1)
                         .setInterval(querySegmentSpec(Filtration.eternity()))
                         .setGranularity(Granularities.ALL)
-                        .setDimensions(dimensionSpec(new DefaultDimensionSpec("cnt", "d0", ValueType.LONG)))
+                        .setDimensions(dimensions(new DefaultDimensionSpec("cnt", "d0", ValueType.LONG)))
                         .setAggregatorSpecs(aggregators(
                             new FilteredAggregatorFactory(
                                 new CountAggregatorFactory("a0"),
@@ -2612,7 +2621,7 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                             ),
                             new LongSumAggregatorFactory("a1", "cnt")
                         ))
-                        .setPostAggregatorSpecs(ImmutableList.of(expresionPostAgg("p0", "(\"a0\" + \"a1\")")))
+                        .setPostAggregatorSpecs(ImmutableList.of(expressionPostAgg("p0", "(\"a0\" + \"a1\")")))
                         .setContext(QUERY_CONTEXT_DEFAULT)
                         .build()
         ),
@@ -2689,8 +2698,8 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                       new DoubleMaxAggregatorFactory("a4", null, "(strlen(\"dim2\") + log(\"m1\"))", macroTable)
                   ))
                   .postAggregators(
-                      expresionPostAgg("p0", "log((\"a1\" + \"a2\"))"),
-                      expresionPostAgg("p1", "(\"a1\" % 4)")
+                      expressionPostAgg("p0", "log((\"a1\" + \"a2\"))"),
+                      expressionPostAgg("p1", "(\"a1\" % 4)")
                   )
                   .context(TIMESERIES_CONTEXT_DEFAULT)
                   .build()
@@ -2718,16 +2727,16 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                         .setInterval(querySegmentSpec(Filtration.eternity()))
                         .setGranularity(Granularities.ALL)
                         .setVirtualColumns(
-                            expression_Virtual_Column("d0:v", "(floor((\"m1\" / 2)) * 2)", ValueType.FLOAT)
+                            expressionVirtualColumn("v0", "(floor((\"m1\" / 2)) * 2)", ValueType.FLOAT)
                         )
-                        .setDimFilter(expressionFilter("((floor((\"m1\" / 2)) * 2) > -1)"))
-                        .setDimensions(dimensionSpec(new DefaultDimensionSpec("d0:v", "d0", ValueType.FLOAT)))
+                        .setDimFilter(bound("v0", "-1", null, true, false, null, StringComparators.NUMERIC))
+                        .setDimensions(dimensions(new DefaultDimensionSpec("v0", "v0", ValueType.FLOAT)))
                         .setAggregatorSpecs(aggregators(new CountAggregatorFactory("a0")))
                         .setLimitSpec(
                             new DefaultLimitSpec(
                                 ImmutableList.of(
                                     new OrderByColumnSpec(
-                                        "d0",
+                                        "v0",
                                         OrderByColumnSpec.Direction.DESCENDING,
                                         StringComparators.NUMERIC
                                     )
@@ -2764,18 +2773,18 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                         .setInterval(querySegmentSpec(Filtration.eternity()))
                         .setGranularity(Granularities.ALL)
                         .setVirtualColumns(
-                            expression_Virtual_Column("d0:v", "((CAST(\"m1\", 'LONG') / 2) * 2)", ValueType.LONG)
+                            expressionVirtualColumn("v0", "((CAST(\"m1\", 'LONG') / 2) * 2)", ValueType.LONG)
                         )
                         .setDimFilter(
-                            expressionFilter("(((CAST(\"m1\", 'LONG') / 2) * 2) > -1)")
+                            bound("v0", "-1", null, true, false, null, StringComparators.NUMERIC)
                         )
-                        .setDimensions(dimensionSpec(new DefaultDimensionSpec("d0:v", "d0", ValueType.LONG)))
+                        .setDimensions(dimensions(new DefaultDimensionSpec("v0", "v0", ValueType.LONG)))
                         .setAggregatorSpecs(aggregators(new CountAggregatorFactory("a0")))
                         .setLimitSpec(
                             new DefaultLimitSpec(
                                 ImmutableList.of(
                                     new OrderByColumnSpec(
-                                        "d0",
+                                        "v0",
                                         OrderByColumnSpec.Direction.DESCENDING,
                                         StringComparators.NUMERIC
                                     )
@@ -2812,22 +2821,22 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                         .setInterval(querySegmentSpec(Filtration.eternity()))
                         .setGranularity(Granularities.ALL)
                         .setVirtualColumns(
-                            expression_Virtual_Column(
-                                "d0:v",
+                            expressionVirtualColumn(
+                                "v0",
                                 "(floor((CAST(\"dim1\", 'DOUBLE') / 2)) * 2)",
                                 ValueType.FLOAT
                             )
                         )
                         .setDimFilter(
-                            expressionFilter("((floor((CAST(\"dim1\", 'DOUBLE') / 2)) * 2) > -1)")
+                            bound("v0", "-1", null, true, false, null, StringComparators.NUMERIC)
                         )
-                        .setDimensions(dimensionSpec(new DefaultDimensionSpec("d0:v", "d0", ValueType.FLOAT)))
+                        .setDimensions(dimensions(new DefaultDimensionSpec("v0", "v0", ValueType.FLOAT)))
                         .setAggregatorSpecs(aggregators(new CountAggregatorFactory("a0")))
                         .setLimitSpec(
                             new DefaultLimitSpec(
                                 ImmutableList.of(
                                     new OrderByColumnSpec(
-                                        "d0",
+                                        "v0",
                                         OrderByColumnSpec.Direction.DESCENDING,
                                         StringComparators.NUMERIC
                                     )
@@ -2862,7 +2871,7 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                         .setDataSource(CalciteTests.DATASOURCE1)
                         .setInterval(querySegmentSpec(Filtration.eternity()))
                         .setGranularity(Granularities.ALL)
-                        .setDimensions(dimensionSpec(new DefaultDimensionSpec("dim1", "d0")))
+                        .setDimensions(dimensions(new DefaultDimensionSpec("dim1", "d0")))
                         .setDimFilter(new InDimFilter("dim1", ImmutableList.of("abc", "def", "ghi"), null))
                         .setAggregatorSpecs(
                             aggregators(
@@ -2901,7 +2910,7 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                         .setDataSource(CalciteTests.DATASOURCE1)
                         .setInterval(querySegmentSpec(Filtration.eternity()))
                         .setGranularity(Granularities.ALL)
-                        .setDimensions(dimensionSpec(new DefaultDimensionSpec("dim1", "d0")))
+                        .setDimensions(dimensions(new DefaultDimensionSpec("dim1", "d0")))
                         .setDimFilter(new InDimFilter("dim1", elements, null))
                         .setAggregatorSpecs(
                             aggregators(
@@ -3030,7 +3039,7 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                   .dataSource(CalciteTests.DATASOURCE1)
                   .intervals(querySegmentSpec(Filtration.eternity()))
                   .granularity(Granularities.ALL)
-                  .filters(numeric_Selector("dim1", "2", null))
+                  .filters(numericSelector("dim1", "2", null))
                   .aggregators(aggregators(new CountAggregatorFactory("a0")))
                   .context(TIMESERIES_CONTEXT_DEFAULT)
                   .build()
@@ -3364,8 +3373,8 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                           not(selector("dim2", "a", null)),
                           bound(
                               "__time",
-                              String.valueOf(t("2000-01-01")),
-                              String.valueOf(t("2000-12-31T23:59:59.999")),
+                              String.valueOf(timestamp("2000-01-01")),
+                              String.valueOf(timestamp("2000-12-31T23:59:59.999")),
                               false,
                               false,
                               null,
@@ -3557,7 +3566,7 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                 .setInterval(querySegmentSpec(Filtration.eternity()))
                 .setGranularity(Granularities.ALL)
                 .setVirtualColumns(
-                    expression_Virtual_Column("d0:v", "timestamp_floor(\"cnt\",'P1Y',null,'UTC')", ValueType.LONG)
+                    expressionVirtualColumn("v0", "timestamp_floor(\"cnt\",'P1Y',null,'UTC')", ValueType.LONG)
                 )
                 .setDimFilter(
                     bound(
@@ -3570,13 +3579,13 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                         StringComparators.NUMERIC
                     )
                 )
-                .setDimensions(dimensionSpec(new DefaultDimensionSpec("d0:v", "d0", ValueType.LONG)))
+                .setDimensions(dimensions(new DefaultDimensionSpec("v0", "v0", ValueType.LONG)))
                 .setAggregatorSpecs(aggregators(new CountAggregatorFactory("a0")))
                 .setContext(QUERY_CONTEXT_DEFAULT)
                 .build()
         ),
         ImmutableList.of(
-            new Object[]{t("1970-01-01"), 6L}
+            new Object[]{timestamp("1970-01-01"), 6L}
         )
     );
   }
@@ -3591,7 +3600,7 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                         .setDataSource(CalciteTests.DATASOURCE1)
                         .setInterval(querySegmentSpec(Filtration.eternity()))
                         .setGranularity(Granularities.ALL)
-                        .setDimensions(dimensionSpec(new DefaultDimensionSpec("dim1", "d0")))
+                        .setDimensions(dimensions(new DefaultDimensionSpec("dim1", "d0")))
                         .setDimFilter(
                             or(
                                 selector(
@@ -3627,11 +3636,15 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                         .setDataSource(CalciteTests.DATASOURCE1)
                         .setInterval(querySegmentSpec(Filtration.eternity()))
                         .setGranularity(Granularities.ALL)
-                        .setDimensions(dimensionSpec(new DefaultDimensionSpec("dim1", "d0")))
+                        .setVirtualColumns(
+                            expressionVirtualColumn("v0", "strlen(\"dim1\")", ValueType.LONG),
+                            expressionVirtualColumn("v1", "CAST(strlen(\"dim1\"), 'STRING')", ValueType.STRING)
+                        )
+                        .setDimensions(dimensions(new DefaultDimensionSpec("dim1", "d0")))
                         .setDimFilter(
                             or(
-                                expressionFilter("(strlen(\"dim1\") == 3)"),
-                                expressionFilter("(CAST(strlen(\"dim1\"), 'STRING') == 3)")
+                                selector("v0", "3", null),
+                                selector("v1", "3", null)
                             )
                         )
                         .setContext(QUERY_CONTEXT_DEFAULT)
@@ -3798,7 +3811,7 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                           new CardinalityAggregatorFactory(
                               "a1",
                               null,
-                              dimensionSpec(new DefaultDimensionSpec("dim2", null)),
+                              dimensions(new DefaultDimensionSpec("dim2", null)),
                               false,
                               true
                           ),
@@ -3882,7 +3895,7 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                                             .setDataSource(CalciteTests.DATASOURCE1)
                                             .setInterval(querySegmentSpec(Filtration.eternity()))
                                             .setGranularity(Granularities.ALL)
-                                            .setDimensions(dimensionSpec(new DefaultDimensionSpec("dim2", "d0")))
+                                            .setDimensions(dimensions(new DefaultDimensionSpec("dim2", "d0")))
                                             .setContext(QUERY_CONTEXT_DEFAULT)
                                             .build()
                             )
@@ -3923,7 +3936,7 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                           new CardinalityAggregatorFactory(
                               "a0",
                               null,
-                              dimensionSpec(new DefaultDimensionSpec("dim2", null)),
+                              dimensions(new DefaultDimensionSpec("dim2", null)),
                               false,
                               true
                           )
@@ -3955,7 +3968,7 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                                             .setDataSource(CalciteTests.DATASOURCE1)
                                             .setInterval(querySegmentSpec(Filtration.eternity()))
                                             .setGranularity(Granularities.ALL)
-                                            .setDimensions(dimensionSpec(
+                                            .setDimensions(dimensions(
                                                 new DefaultDimensionSpec("dim2", "d0"),
                                                 new DefaultDimensionSpec("dim1", "d1")
                                             ))
@@ -3966,7 +3979,7 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                         )
                         .setInterval(querySegmentSpec(Filtration.eternity()))
                         .setGranularity(Granularities.ALL)
-                        .setDimensions(dimensionSpec(new DefaultDimensionSpec("d0", "_d0")))
+                        .setDimensions(dimensions(new DefaultDimensionSpec("d0", "_d0")))
                         .setAggregatorSpecs(aggregators(
                             new LongSumAggregatorFactory("_a0", "a0"),
                             new FilteredAggregatorFactory(
@@ -4010,7 +4023,7 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                   .intervals(querySegmentSpec(Filtration.eternity()))
                   .granularity(Granularities.ALL)
                   .virtualColumns(
-                      expression_Virtual_Column("a4:v", "concat(substring(\"dim2\", 0, 1),'x')", ValueType.STRING)
+                      expressionVirtualColumn("v0", "concat(substring(\"dim2\", 0, 1),'x')", ValueType.STRING)
                   )
                   .aggregators(
                       aggregators(
@@ -4018,7 +4031,7 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                           new CardinalityAggregatorFactory(
                               "a1",
                               null,
-                              dimensionSpec(new DefaultDimensionSpec("dim2", "dim2")),
+                              dimensions(new DefaultDimensionSpec("dim2", "dim2")),
                               false,
                               true
                           ),
@@ -4026,7 +4039,7 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                               new CardinalityAggregatorFactory(
                                   "a2",
                                   null,
-                                  dimensionSpec(new DefaultDimensionSpec("dim2", "dim2")),
+                                  dimensions(new DefaultDimensionSpec("dim2", "dim2")),
                                   false,
                                   true
                               ),
@@ -4035,7 +4048,7 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                           new CardinalityAggregatorFactory(
                               "a3",
                               null,
-                              dimensionSpec(
+                              dimensions(
                                   new ExtractionDimensionSpec(
                                       "dim2",
                                       "dim2",
@@ -4049,7 +4062,7 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                           new CardinalityAggregatorFactory(
                               "a4",
                               null,
-                              dimensionSpec(new DefaultDimensionSpec("a4:v", "a4:v", ValueType.STRING)),
+                              dimensions(new DefaultDimensionSpec("v0", "v0", ValueType.STRING)),
                               false,
                               true
                           ),
@@ -4095,7 +4108,7 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                                         .setDataSource(CalciteTests.DATASOURCE1)
                                         .setInterval(querySegmentSpec(Filtration.eternity()))
                                         .setGranularity(Granularities.ALL)
-                                        .setDimensions(dimensionSpec(
+                                        .setDimensions(dimensions(
                                             new DefaultDimensionSpec("m2", "d0", ValueType.DOUBLE),
                                             new DefaultDimensionSpec("dim1", "d1")
                                         ))
@@ -4107,15 +4120,15 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                         .setInterval(querySegmentSpec(Filtration.eternity()))
                         .setGranularity(Granularities.ALL)
                         .setVirtualColumns(
-                            expression_Virtual_Column(
-                                "_d0:v",
+                            expressionVirtualColumn(
+                                "v0",
                                 "timestamp_floor(\"a0\",'PT1H',null,'UTC')",
                                 ValueType.LONG
                             )
                         )
-                        .setDimensions(dimensionSpec(
-                            new DefaultDimensionSpec("_d0:v", "_d0", ValueType.LONG),
-                            new DefaultDimensionSpec("d1", "_d1", ValueType.STRING)
+                        .setDimensions(dimensions(
+                            new DefaultDimensionSpec("v0", "v0", ValueType.LONG),
+                            new DefaultDimensionSpec("d1", "_d0", ValueType.STRING)
                         ))
                         .setAggregatorSpecs(aggregators(
                             new CountAggregatorFactory("_a0")
@@ -4153,7 +4166,7 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                                                         .setDataSource(CalciteTests.DATASOURCE1)
                                                         .setInterval(querySegmentSpec(Filtration.eternity()))
                                                         .setGranularity(Granularities.ALL)
-                                                        .setDimensions(dimensionSpec(
+                                                        .setDimensions(dimensions(
                                                             new DefaultDimensionSpec("dim1", "d0"),
                                                             new DefaultDimensionSpec("dim2", "d1")
                                                         ))
@@ -4163,7 +4176,7 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                                         )
                                         .setInterval(querySegmentSpec(Filtration.eternity()))
                                         .setGranularity(Granularities.ALL)
-                                        .setDimensions(dimensionSpec(new DefaultDimensionSpec("d1", "_d0")))
+                                        .setDimensions(dimensions(new DefaultDimensionSpec("d1", "_d0")))
                                         .setAggregatorSpecs(aggregators(new LongSumAggregatorFactory("_a0", "a0")))
                                         .setContext(QUERY_CONTEXT_DEFAULT)
                                         .build()
@@ -4232,7 +4245,7 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                                             .setDataSource(CalciteTests.DATASOURCE1)
                                             .setInterval(querySegmentSpec(Filtration.eternity()))
                                             .setGranularity(Granularities.ALL)
-                                            .setDimensions(dimensionSpec(new DefaultDimensionSpec("dim2", "d0")))
+                                            .setDimensions(dimensions(new DefaultDimensionSpec("dim2", "d0")))
                                             .setAggregatorSpecs(aggregators(new LongSumAggregatorFactory("a0", "cnt")))
                                             .setContext(QUERY_CONTEXT_DEFAULT)
                                             .build()
@@ -4277,13 +4290,13 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                                             .setInterval(querySegmentSpec(Filtration.eternity()))
                                             .setGranularity(Granularities.ALL)
                                             .setVirtualColumns(
-                                                expression_Virtual_Column(
-                                                    "d0:v",
+                                                expressionVirtualColumn(
+                                                    "v0",
                                                     "timestamp_floor(\"__time\",'P1D',null,'UTC')",
                                                     ValueType.LONG
                                                 )
                                             )
-                                            .setDimensions(dimensionSpec(new DefaultDimensionSpec("d0:v", "d0", ValueType.LONG)))
+                                            .setDimensions(dimensions(new DefaultDimensionSpec("v0", "v0", ValueType.LONG)))
                                             .setAggregatorSpecs(aggregators(new CountAggregatorFactory("a0")))
                                             .setContext(QUERY_CONTEXT_DEFAULT)
                                             .build()
@@ -4296,7 +4309,7 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                             new LongMinAggregatorFactory("_a1", "a0"),
                             new LongSumAggregatorFactory("_a2:sum", "a0"),
                             new CountAggregatorFactory("_a2:count"),
-                            new LongMaxAggregatorFactory("_a3", "d0"),
+                            new LongMaxAggregatorFactory("_a3", "v0"),
                             new CountAggregatorFactory("_a4")
                         ))
                         .setPostAggregatorSpecs(
@@ -4309,7 +4322,7 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                                         new FieldAccessPostAggregator(null, "_a2:count")
                                     )
                                 ),
-                                expresionPostAgg("s0", "timestamp_extract(\"_a3\",'EPOCH','UTC')")
+                                expressionPostAgg("s0", "timestamp_extract(\"_a3\",'EPOCH','UTC')")
                             )
                         )
                         .setLimit(1)
@@ -4336,19 +4349,19 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                                             .setInterval(querySegmentSpec(Filtration.eternity()))
                                             .setGranularity(Granularities.ALL)
                                             .setVirtualColumns(
-                                                expression_Virtual_Column(
-                                                    "d0:v",
+                                                expressionVirtualColumn(
+                                                    "v0",
                                                     "timestamp_floor(\"__time\",'P1D',null,'UTC')",
                                                     ValueType.LONG
                                                 )
                                             )
-                                            .setDimensions(dimensionSpec(new DefaultDimensionSpec("d0:v", "d0", ValueType.LONG)))
+                                            .setDimensions(dimensions(new DefaultDimensionSpec("v0", "v0", ValueType.LONG)))
                                             .setAggregatorSpecs(
                                                 aggregators(
                                                     new CardinalityAggregatorFactory(
                                                         "a0:a",
                                                         null,
-                                                        dimensionSpec(new DefaultDimensionSpec("cnt", "cnt", ValueType.LONG)),
+                                                        dimensions(new DefaultDimensionSpec("cnt", "cnt", ValueType.LONG)),
                                                         false,
                                                         true
                                                     )
@@ -4425,7 +4438,7 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                         .setInterval(querySegmentSpec(Filtration.eternity()))
                         .setGranularity(Granularities.ALL)
                         .setDimFilter(filter)
-                        .setDimensions(dimensionSpec(new DefaultDimensionSpec("dim1", "d0")))
+                        .setDimensions(dimensions(new DefaultDimensionSpec("dim1", "d0")))
                         .setAggregatorSpecs(aggregators(new LongSumAggregatorFactory("a0", "cnt")))
                         .setLimitSpec(
                             new DefaultLimitSpec(
@@ -4492,7 +4505,7 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                         .setInterval(querySegmentSpec(Filtration.eternity()))
                         .setGranularity(Granularities.ALL)
                         .setDimFilter(in("dim2", ImmutableList.of("", "a"), null))
-                        .setDimensions(dimensionSpec(new DefaultDimensionSpec("dim1", "d0")))
+                        .setDimensions(dimensions(new DefaultDimensionSpec("dim1", "d0")))
                         .setAggregatorSpecs(aggregators(new LongSumAggregatorFactory("a0", "cnt")))
                         .setLimitSpec(
                             new DefaultLimitSpec(
@@ -4543,7 +4556,7 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                         .setDataSource(CalciteTests.DATASOURCE1)
                         .setInterval(querySegmentSpec(Filtration.eternity()))
                         .setGranularity(Granularities.ALL)
-                        .setDimensions(dimensionSpec(new DefaultDimensionSpec("dim1", "d0")))
+                        .setDimensions(dimensions(new DefaultDimensionSpec("dim1", "d0")))
                         .setAggregatorSpecs(aggregators(new LongSumAggregatorFactory("a0", "cnt")))
                         .setLimitSpec(
                             new DefaultLimitSpec(
@@ -4589,7 +4602,7 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                         .setInterval(querySegmentSpec(Filtration.eternity()))
                         .setGranularity(Granularities.ALL)
                         .setDimFilter(not(selector("dim1", "", null)))
-                        .setDimensions(dimensionSpec(new ExtractionDimensionSpec(
+                        .setDimensions(dimensions(new ExtractionDimensionSpec(
                             "dim1",
                             "d0",
                             new SubstringDimExtractionFn(0, 1)
@@ -4608,7 +4621,7 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                                                 ImmutableList.of("1", "2", "a", "d"),
                                                 new SubstringDimExtractionFn(0, 1)
                                             ))
-                                            .setDimensions(dimensionSpec(new DefaultDimensionSpec("dim2", "d0")))
+                                            .setDimensions(dimensions(new DefaultDimensionSpec("dim2", "d0")))
                                             .setContext(QUERY_CONTEXT_DEFAULT)
                                             .build()
                             )
@@ -4685,7 +4698,7 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                                             .setInterval(querySegmentSpec(Filtration.eternity()))
                                             .setDimFilter(not(selector("dim2", "", null)))
                                             .setGranularity(Granularities.ALL)
-                                            .setDimensions(dimensionSpec(new DefaultDimensionSpec("dim2", "d0")))
+                                            .setDimensions(dimensions(new DefaultDimensionSpec("dim2", "d0")))
                                             .setAggregatorSpecs(aggregators(new LongSumAggregatorFactory("a0", "cnt")))
                                             .setContext(QUERY_CONTEXT_DEFAULT)
                                             .build()
@@ -4724,7 +4737,7 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                                             .setInterval(querySegmentSpec(Filtration.eternity()))
                                             .setDimFilter(not(selector("dim2", null, null)))
                                             .setGranularity(Granularities.ALL)
-                                            .setDimensions(dimensionSpec(new DefaultDimensionSpec("dim2", "d0")))
+                                            .setDimensions(dimensions(new DefaultDimensionSpec("dim2", "d0")))
                                             .setAggregatorSpecs(aggregators(new LongSumAggregatorFactory("a0", "cnt")))
                                             .setContext(QUERY_CONTEXT_DEFAULT)
                                             .build()
@@ -4766,7 +4779,7 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                                             .setDataSource(CalciteTests.DATASOURCE1)
                                             .setInterval(querySegmentSpec(Filtration.eternity()))
                                             .setGranularity(Granularities.ALL)
-                                            .setDimensions(dimensionSpec(new DefaultDimensionSpec("dim2", "d0")))
+                                            .setDimensions(dimensions(new DefaultDimensionSpec("dim2", "d0")))
                                             .setAggregatorSpecs(aggregators(new LongSumAggregatorFactory("a0", "cnt")))
                                             .setLimit(1)
                                             .setContext(QUERY_CONTEXT_DEFAULT)
@@ -4811,7 +4824,7 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                                             .setInterval(querySegmentSpec(Filtration.eternity()))
                                             .setGranularity(Granularities.ALL)
                                             .setDimFilter(not(selector("dim1", "", null)))
-                                            .setDimensions(dimensionSpec(new DefaultDimensionSpec("dim1", "d0")))
+                                            .setDimensions(dimensions(new DefaultDimensionSpec("dim1", "d0")))
                                             .setContext(QUERY_CONTEXT_DEFAULT)
                                             .build()
                             )
@@ -4823,14 +4836,14 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                             new CardinalityAggregatorFactory(
                                 "a1",
                                 null,
-                                dimensionSpec(new DefaultDimensionSpec("d0", null)),
+                                dimensions(new DefaultDimensionSpec("d0", null)),
                                 false,
                                 true
                             )
                         ))
                         .setPostAggregatorSpecs(
                             ImmutableList.of(
-                                expresionPostAgg("p0", "((1 - (\"a1\" / \"a0\")) * 100)")
+                                expressionPostAgg("p0", "((1 - (\"a1\" / \"a0\")) * 100)")
                             )
                         )
                         .setContext(QUERY_CONTEXT_DEFAULT)
@@ -4859,7 +4872,7 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                                             .setDataSource(CalciteTests.DATASOURCE1)
                                             .setInterval(querySegmentSpec(Filtration.eternity()))
                                             .setGranularity(Granularities.ALL)
-                                            .setDimensions(dimensionSpec(new DefaultDimensionSpec("dim2", "d0")))
+                                            .setDimensions(dimensions(new DefaultDimensionSpec("dim2", "d0")))
                                             .setAggregatorSpecs(aggregators(new LongSumAggregatorFactory("a0", "cnt")))
                                             .setContext(QUERY_CONTEXT_DEFAULT)
                                             .build()
@@ -4867,7 +4880,7 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                         )
                         .setInterval(querySegmentSpec(Filtration.eternity()))
                         .setGranularity(Granularities.ALL)
-                        .setDimensions(dimensionSpec(new DefaultDimensionSpec("a0", "_d0")))
+                        .setDimensions(dimensions(new DefaultDimensionSpec("a0", "_d0")))
                         .setAggregatorSpecs(aggregators(
                             new CountAggregatorFactory("_a0")
                         ))
@@ -4904,7 +4917,7 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                                             .setDataSource(CalciteTests.DATASOURCE1)
                                             .setInterval(querySegmentSpec(Filtration.eternity()))
                                             .setGranularity(Granularities.ALL)
-                                            .setDimensions(dimensionSpec(new DefaultDimensionSpec("dim2", "d0")))
+                                            .setDimensions(dimensions(new DefaultDimensionSpec("dim2", "d0")))
                                             .setAggregatorSpecs(aggregators(new LongSumAggregatorFactory("a0", "cnt")))
                                             .setContext(QUERY_CONTEXT_DEFAULT)
                                             .build()
@@ -4912,7 +4925,7 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                         )
                         .setInterval(querySegmentSpec(Filtration.eternity()))
                         .setGranularity(Granularities.ALL)
-                        .setDimensions(dimensionSpec(new DefaultDimensionSpec("a0", "_d0")))
+                        .setDimensions(dimensions(new DefaultDimensionSpec("a0", "_d0")))
                         .setAggregatorSpecs(aggregators(
                             new CountAggregatorFactory("_a0")
                         ))
@@ -4964,17 +4977,17 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                           new CardinalityAggregatorFactory(
                               "a1",
                               null,
-                              dimensionSpec(new DefaultDimensionSpec("dim2", null)),
+                              dimensions(new DefaultDimensionSpec("dim2", null)),
                               false,
                               true
                           )
                       )
                   )
                   .postAggregators(
-                      expresionPostAgg("p0", "CAST(\"a1\", 'DOUBLE')"),
-                      expresionPostAgg("p1", "(\"a0\" / \"a1\")"),
-                      expresionPostAgg("p2", "((\"a0\" / \"a1\") + 3)"),
-                      expresionPostAgg("p3", "((CAST(\"a0\", 'DOUBLE') / CAST(\"a1\", 'DOUBLE')) + 3)")
+                      expressionPostAgg("p0", "CAST(\"a1\", 'DOUBLE')"),
+                      expressionPostAgg("p1", "(\"a0\" / \"a1\")"),
+                      expressionPostAgg("p2", "((\"a0\" / \"a1\") + 3)"),
+                      expressionPostAgg("p3", "((CAST(\"a0\", 'DOUBLE') / CAST(\"a1\", 'DOUBLE')) + 3)")
                   )
                   .context(TIMESERIES_CONTEXT_DEFAULT)
                   .build()
@@ -5001,7 +5014,7 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                           new CardinalityAggregatorFactory(
                               "a0",
                               null,
-                              dimensionSpec(
+                              dimensions(
                                   new ExtractionDimensionSpec(
                                       "dim1",
                                       null,
@@ -5033,16 +5046,15 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
             Druids.newTimeseriesQueryBuilder()
                   .dataSource(CalciteTests.DATASOURCE1)
                   .intervals(querySegmentSpec(Filtration.eternity()))
-                  .filters(not(selector("dim1", "", null)))
                   .granularity(Granularities.ALL)
-                  .virtualColumns(expression_Virtual_Column("a0:v", "trim(\"dim1\",' ')", ValueType.STRING))
-                  .filters(expressionFilter("(trim(\"dim1\",' ') != '')"))
+                  .virtualColumns(expressionVirtualColumn("v0", "trim(\"dim1\",' ')", ValueType.STRING))
+                  .filters(not(selector("v0", NullHandling.emptyToNullIfNeeded(""), null)))
                   .aggregators(
                       aggregators(
                           new CardinalityAggregatorFactory(
                               "a0",
                               null,
-                              dimensionSpec(new DefaultDimensionSpec("a0:v", "a0:v", ValueType.STRING)),
+                              dimensions(new DefaultDimensionSpec("v0", "v0", ValueType.STRING)),
                               false,
                               true
                           )
@@ -5071,12 +5083,12 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                         .setDataSource(CalciteTests.DATASOURCE1)
                         .setInterval(querySegmentSpec(Filtration.eternity()))
                         .setGranularity(Granularities.ALL)
-                        .setVirtualColumns(expression_Virtual_Column(
-                            "d0:v",
+                        .setVirtualColumns(expressionVirtualColumn(
+                            "v0",
                             "(((timestamp_extract(\"__time\",'MONTH','UTC') - 1) / 3) + 1)",
                             ValueType.LONG
                         ))
-                        .setDimensions(dimensionSpec(new DefaultDimensionSpec("d0:v", "d0", ValueType.LONG)))
+                        .setDimensions(dimensions(new DefaultDimensionSpec("v0", "v0", ValueType.LONG)))
                         .setAggregatorSpecs(aggregators(new CountAggregatorFactory("a0")))
                         .setContext(QUERY_CONTEXT_DEFAULT)
                         .build()
@@ -5110,7 +5122,7 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                             ))
                         )
                         .setDimensions(
-                            dimensionSpec(
+                            dimensions(
                                 new ExtractionDimensionSpec(
                                     "dim1",
                                     "d0",
@@ -5148,7 +5160,7 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                         .setInterval(querySegmentSpec(Filtration.eternity()))
                         .setGranularity(Granularities.ALL)
                         .setDimensions(
-                            dimensionSpec(
+                            dimensions(
                                 new DefaultDimensionSpec("dim2", "d0"),
                                 new DefaultDimensionSpec("dim1", "d1")
                             )
@@ -5195,7 +5207,7 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                         .setInterval(querySegmentSpec(Filtration.eternity()))
                         .setGranularity(Granularities.ALL)
                         .setDimensions(
-                            dimensionSpec(
+                            dimensions(
                                 new DefaultDimensionSpec("dim1", "d0"),
                                 new DefaultDimensionSpec("dim2", "d1")
                             )
@@ -5213,7 +5225,7 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                                 4
                             )
                         )
-                        .setHavingSpec(having(numeric_Selector("a0", "1", null)))
+                        .setHavingSpec(having(selector("a0", "1", null)))
                         .setContext(QUERY_CONTEXT_DEFAULT)
                         .build()
         ),
@@ -5289,7 +5301,7 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
         CalciteTests.REGULAR_USER_AUTH_RESULT,
         ImmutableList.of(),
         ImmutableList.of(
-            new Object[]{t("2000-01-01T00Z", LOS_ANGELES), d("1999-12-31"), d("2000-01-01")}
+            new Object[]{timestamp("2000-01-01T00Z", LOS_ANGELES), day("1999-12-31"), day("2000-01-01")}
         )
     );
   }
@@ -5445,11 +5457,15 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                   .dataSource(CalciteTests.DATASOURCE1)
                   .intervals(querySegmentSpec(Filtration.eternity()))
                   .granularity(Granularities.ALL)
+                  .virtualColumns(
+                      expressionVirtualColumn("v0", "timestamp_extract(\"__time\",'YEAR','UTC')", ValueType.LONG),
+                      expressionVirtualColumn("v1", "timestamp_extract(\"__time\",'MONTH','UTC')", ValueType.LONG)
+                  )
                   .aggregators(aggregators(new CountAggregatorFactory("a0")))
                   .filters(
                       and(
-                          expressionFilter("(timestamp_extract(\"__time\",'YEAR','UTC') == 2000)"),
-                          expressionFilter("(timestamp_extract(\"__time\",'MONTH','UTC') == 1)")
+                          selector("v0", "2000", null),
+                          selector("v1", "1", null)
                       )
                   )
                   .context(TIMESERIES_CONTEXT_DEFAULT)
@@ -5473,15 +5489,23 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                   .dataSource(CalciteTests.DATASOURCE1)
                   .intervals(querySegmentSpec(Filtration.eternity()))
                   .granularity(Granularities.ALL)
+                  .virtualColumns(
+                      expressionVirtualColumn(
+                          "v0",
+                          "timestamp_extract(\"__time\",'YEAR','UTC')",
+                          ValueType.LONG
+                      ),
+                      expressionVirtualColumn(
+                          "v1",
+                          "timestamp_extract(\"__time\",'DAY','UTC')",
+                          ValueType.LONG
+                      )
+                  )
                   .aggregators(aggregators(new CountAggregatorFactory("a0")))
                   .filters(
                       and(
-                          expressionFilter("(timestamp_extract(\"__time\",'YEAR','UTC') == 2000)"),
-                          or(
-                              expressionFilter("(timestamp_extract(\"__time\",'DAY','UTC') == 2)"),
-                              expressionFilter("(timestamp_extract(\"__time\",'DAY','UTC') == 3)"),
-                              expressionFilter("(timestamp_extract(\"__time\",'DAY','UTC') == 5)")
-                          )
+                          selector("v0", "2000", null),
+                          in("v1", ImmutableList.of("2", "3", "5"), null)
                       )
                   )
                   .context(TIMESERIES_CONTEXT_DEFAULT)
@@ -5525,9 +5549,9 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                         .setInterval(querySegmentSpec(Filtration.eternity()))
                         .setGranularity(Granularities.ALL)
                         .setVirtualColumns(
-                            expression_Virtual_Column("d0:v", "floor(CAST(\"dim1\", 'DOUBLE'))", ValueType.FLOAT)
+                            expressionVirtualColumn("v0", "floor(CAST(\"dim1\", 'DOUBLE'))", ValueType.FLOAT)
                         )
-                        .setDimensions(dimensionSpec(new DefaultDimensionSpec("d0:v", "d0", ValueType.FLOAT)))
+                        .setDimensions(dimensions(new DefaultDimensionSpec("v0", "v0", ValueType.FLOAT)))
                         .setAggregatorSpecs(aggregators(new CountAggregatorFactory("a0")))
                         .setContext(QUERY_CONTEXT_DEFAULT)
                         .build()
@@ -5552,17 +5576,17 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                         .setInterval(querySegmentSpec(Filtration.eternity()))
                         .setGranularity(Granularities.ALL)
                         .setVirtualColumns(
-                            expression_Virtual_Column(
-                                "d0:v",
+                            expressionVirtualColumn(
+                                "v0",
                                 "floor(CAST(\"dim1\", 'DOUBLE'))",
                                 ValueType.FLOAT
                             )
                         )
                         .setDimensions(
-                            dimensionSpec(
+                            dimensions(
                                 new DefaultDimensionSpec(
-                                    "d0:v",
-                                    "d0",
+                                    "v0",
+                                    "v0",
                                     ValueType.FLOAT
                                 )
                             )
@@ -5572,7 +5596,7 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                             new DefaultLimitSpec(
                                 ImmutableList.of(
                                     new OrderByColumnSpec(
-                                        "d0",
+                                        "v0",
                                         OrderByColumnSpec.Direction.DESCENDING,
                                         StringComparators.NUMERIC
                                     )
@@ -5606,16 +5630,16 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                         .setInterval(querySegmentSpec(Filtration.eternity()))
                         .setGranularity(Granularities.ALL)
                         .setVirtualColumns(
-                            expression_Virtual_Column(
-                                "d0:v",
+                            expressionVirtualColumn(
+                                "v0",
                                 "timestamp_floor(\"__time\",'P1Y',null,'UTC')",
                                 ValueType.LONG
                             )
                         )
                         .setDimensions(
-                            dimensionSpec(
-                                new DefaultDimensionSpec("d0:v", "d0", ValueType.LONG),
-                                new DefaultDimensionSpec("dim2", "d1")
+                            dimensions(
+                                new DefaultDimensionSpec("v0", "v0", ValueType.LONG),
+                                new DefaultDimensionSpec("dim2", "d0")
                             )
                         )
                         .setAggregatorSpecs(
@@ -5627,12 +5651,12 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                             new DefaultLimitSpec(
                                 ImmutableList.of(
                                     new OrderByColumnSpec(
-                                        "d0",
+                                        "v0",
                                         OrderByColumnSpec.Direction.ASCENDING,
                                         StringComparators.NUMERIC
                                     ),
                                     new OrderByColumnSpec(
-                                        "d1",
+                                        "d0",
                                         OrderByColumnSpec.Direction.ASCENDING,
                                         StringComparators.LEXICOGRAPHIC
                                     ),
@@ -5650,19 +5674,19 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
         ),
         NullHandling.replaceWithDefault() ?
         ImmutableList.of(
-            new Object[]{t("2000"), "", 2L},
-            new Object[]{t("2000"), "a", 1L},
-            new Object[]{t("2001"), "", 1L},
-            new Object[]{t("2001"), "a", 1L},
-            new Object[]{t("2001"), "abc", 1L}
+            new Object[]{timestamp("2000"), "", 2L},
+            new Object[]{timestamp("2000"), "a", 1L},
+            new Object[]{timestamp("2001"), "", 1L},
+            new Object[]{timestamp("2001"), "a", 1L},
+            new Object[]{timestamp("2001"), "abc", 1L}
         ) :
         ImmutableList.of(
-            new Object[]{t("2000"), null, 1L},
-            new Object[]{t("2000"), "", 1L},
-            new Object[]{t("2000"), "a", 1L},
-            new Object[]{t("2001"), null, 1L},
-            new Object[]{t("2001"), "a", 1L},
-            new Object[]{t("2001"), "abc", 1L}
+            new Object[]{timestamp("2000"), null, 1L},
+            new Object[]{timestamp("2000"), "", 1L},
+            new Object[]{timestamp("2000"), "a", 1L},
+            new Object[]{timestamp("2001"), null, 1L},
+            new Object[]{timestamp("2001"), "a", 1L},
+            new Object[]{timestamp("2001"), "abc", 1L}
         )
     );
   }
@@ -5677,8 +5701,8 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                         .setDataSource(CalciteTests.DATASOURCE1)
                         .setInterval(querySegmentSpec(Filtration.eternity()))
                         .setGranularity(Granularities.ALL)
-                        .setVirtualColumns(expression_Virtual_Column("d0:v", "strlen(\"dim1\")", ValueType.LONG))
-                        .setDimensions(dimensionSpec(new DefaultDimensionSpec("d0:v", "d0", ValueType.LONG)))
+                        .setVirtualColumns(expressionVirtualColumn("v0", "strlen(\"dim1\")", ValueType.LONG))
+                        .setDimensions(dimensions(new DefaultDimensionSpec("v0", "v0", ValueType.LONG)))
                         .setAggregatorSpecs(aggregators(new CountAggregatorFactory("a0")))
                         .setContext(QUERY_CONTEXT_DEFAULT)
                         .build()
@@ -5722,7 +5746,7 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                             ))
                         )
                         .setDimensions(
-                            dimensionSpec(
+                            dimensions(
                                 new ExtractionDimensionSpec(
                                     "dim1",
                                     "d0",
@@ -5803,8 +5827,8 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                   .build()
         ),
         ImmutableList.of(
-            new Object[]{3L, t("2000-01-01")},
-            new Object[]{3L, t("2001-01-01")}
+            new Object[]{3L, timestamp("2000-01-01")},
+            new Object[]{3L, timestamp("2001-01-01")}
         )
     );
   }
@@ -5832,8 +5856,8 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                           new LongSumAggregatorFactory("a0", "cnt"),
                           bound(
                               "__time",
-                              String.valueOf(t("2000-01-01")),
-                              String.valueOf(t("2000-02-01")),
+                              String.valueOf(timestamp("2000-01-01")),
+                              String.valueOf(timestamp("2000-02-01")),
                               false,
                               true,
                               null,
@@ -5844,8 +5868,8 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                           new LongSumAggregatorFactory("a1", "cnt"),
                           bound(
                               "__time",
-                              String.valueOf(t("2001-01-01")),
-                              String.valueOf(t("2001-02-01")),
+                              String.valueOf(timestamp("2001-01-01")),
+                              String.valueOf(timestamp("2001-02-01")),
                               false,
                               true,
                               null,
@@ -5885,10 +5909,10 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                   .build()
         ),
         ImmutableList.of(
-            new Object[]{1L, t("1999-12-01", LOS_ANGELES)},
-            new Object[]{2L, t("2000-01-01", LOS_ANGELES)},
-            new Object[]{1L, t("2000-12-01", LOS_ANGELES)},
-            new Object[]{2L, t("2001-01-01", LOS_ANGELES)}
+            new Object[]{1L, timestamp("1999-12-01", LOS_ANGELES)},
+            new Object[]{2L, timestamp("2000-01-01", LOS_ANGELES)},
+            new Object[]{1L, timestamp("2000-12-01", LOS_ANGELES)},
+            new Object[]{2L, timestamp("2001-01-01", LOS_ANGELES)}
         )
     );
   }
@@ -5916,10 +5940,10 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                   .build()
         ),
         ImmutableList.of(
-            new Object[]{1L, t("1999-12-01", LOS_ANGELES)},
-            new Object[]{2L, t("2000-01-01", LOS_ANGELES)},
-            new Object[]{1L, t("2000-12-01", LOS_ANGELES)},
-            new Object[]{2L, t("2001-01-01", LOS_ANGELES)}
+            new Object[]{1L, timestamp("1999-12-01", LOS_ANGELES)},
+            new Object[]{2L, timestamp("2000-01-01", LOS_ANGELES)},
+            new Object[]{1L, timestamp("2000-12-01", LOS_ANGELES)},
+            new Object[]{2L, timestamp("2001-01-01", LOS_ANGELES)}
         )
     );
   }
@@ -5944,8 +5968,8 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                   .build()
         ),
         ImmutableList.of(
-            new Object[]{3L, t("2000-01-01")},
-            new Object[]{3L, t("2001-01-01")}
+            new Object[]{3L, timestamp("2000-01-01")},
+            new Object[]{3L, timestamp("2001-01-01")}
         )
     );
   }
@@ -5955,7 +5979,7 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
   {
     testQuery(
         "SELECT SUM(cnt), gran FROM (\n"
-        + "  SELECT TIME_FLOOR(TIME_SHIFT(__time, 'P1D', -1), 'P1M') AS gran,\n"
+        + "  SELECT TIME_FLOOR(TIME_SHIFt(__time, 'P1D', -1), 'P1M') AS gran,\n"
         + "  cnt FROM druid.foo\n"
         + ") AS x\n"
         + "GROUP BY gran\n"
@@ -5966,19 +5990,19 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                         .setInterval(querySegmentSpec(Filtration.eternity()))
                         .setGranularity(Granularities.ALL)
                         .setVirtualColumns(
-                            expression_Virtual_Column(
-                                "d0:v",
+                            expressionVirtualColumn(
+                                "v0",
                                 "timestamp_floor(timestamp_shift(\"__time\",'P1D',-1),'P1M',null,'UTC')",
                                 ValueType.LONG
                             )
                         )
-                        .setDimensions(dimensionSpec(new DefaultDimensionSpec("d0:v", "d0", ValueType.LONG)))
+                        .setDimensions(dimensions(new DefaultDimensionSpec("v0", "v0", ValueType.LONG)))
                         .setAggregatorSpecs(aggregators(new LongSumAggregatorFactory("a0", "cnt")))
                         .setLimitSpec(
                             new DefaultLimitSpec(
                                 ImmutableList.of(
                                     new OrderByColumnSpec(
-                                        "d0",
+                                        "v0",
                                         OrderByColumnSpec.Direction.ASCENDING,
                                         StringComparators.NUMERIC
                                     )
@@ -5990,10 +6014,10 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                         .build()
         ),
         ImmutableList.of(
-            new Object[]{1L, t("1999-12-01")},
-            new Object[]{2L, t("2000-01-01")},
-            new Object[]{1L, t("2000-12-01")},
-            new Object[]{2L, t("2001-01-01")}
+            new Object[]{1L, timestamp("1999-12-01")},
+            new Object[]{2L, timestamp("2000-01-01")},
+            new Object[]{1L, timestamp("2000-12-01")},
+            new Object[]{2L, timestamp("2001-01-01")}
         )
     );
   }
@@ -6014,19 +6038,19 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                         .setInterval(querySegmentSpec(Filtration.eternity()))
                         .setGranularity(Granularities.ALL)
                         .setVirtualColumns(
-                            expression_Virtual_Column(
-                                "d0:v",
+                            expressionVirtualColumn(
+                                "v0",
                                 "timestamp_floor((\"__time\" + -86400000),'P1M',null,'UTC')",
                                 ValueType.LONG
                             )
                         )
-                        .setDimensions(dimensionSpec(new DefaultDimensionSpec("d0:v", "d0", ValueType.LONG)))
+                        .setDimensions(dimensions(new DefaultDimensionSpec("v0", "v0", ValueType.LONG)))
                         .setAggregatorSpecs(aggregators(new LongSumAggregatorFactory("a0", "cnt")))
                         .setLimitSpec(
                             new DefaultLimitSpec(
                                 ImmutableList.of(
                                     new OrderByColumnSpec(
-                                        "d0",
+                                        "v0",
                                         OrderByColumnSpec.Direction.ASCENDING,
                                         StringComparators.NUMERIC
                                     )
@@ -6038,10 +6062,10 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                         .build()
         ),
         ImmutableList.of(
-            new Object[]{1L, t("1999-12-01")},
-            new Object[]{2L, t("2000-01-01")},
-            new Object[]{1L, t("2000-12-01")},
-            new Object[]{2L, t("2001-01-01")}
+            new Object[]{1L, timestamp("1999-12-01")},
+            new Object[]{2L, timestamp("2000-01-01")},
+            new Object[]{1L, timestamp("2000-12-01")},
+            new Object[]{2L, timestamp("2001-01-01")}
         )
     );
   }
@@ -6072,10 +6096,10 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                   .build()
         ),
         ImmutableList.of(
-            new Object[]{1L, t("1999-12-01T01:02:03")},
-            new Object[]{2L, t("2000-01-01T01:02:03")},
-            new Object[]{1L, t("2000-12-01T01:02:03")},
-            new Object[]{2L, t("2001-01-01T01:02:03")}
+            new Object[]{1L, timestamp("1999-12-01T01:02:03")},
+            new Object[]{2L, timestamp("2000-01-01T01:02:03")},
+            new Object[]{1L, timestamp("2000-12-01T01:02:03")},
+            new Object[]{2L, timestamp("2001-01-01T01:02:03")}
         )
     );
   }
@@ -6100,10 +6124,10 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                   .build()
         ),
         ImmutableList.of(
-            new Object[]{1L, t("1999-12-01T08")},
-            new Object[]{2L, t("2000-01-01T08")},
-            new Object[]{1L, t("2000-12-01T08")},
-            new Object[]{2L, t("2001-01-01T08")}
+            new Object[]{1L, timestamp("1999-12-01T08")},
+            new Object[]{2L, timestamp("2000-01-01T08")},
+            new Object[]{1L, timestamp("2000-12-01T08")},
+            new Object[]{2L, timestamp("2001-01-01T08")}
         )
     );
   }
@@ -6131,10 +6155,10 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                   .build()
         ),
         ImmutableList.of(
-            new Object[]{1L, t("1999-12-01", LOS_ANGELES)},
-            new Object[]{2L, t("2000-01-01", LOS_ANGELES)},
-            new Object[]{1L, t("2000-12-01", LOS_ANGELES)},
-            new Object[]{2L, t("2001-01-01", LOS_ANGELES)}
+            new Object[]{1L, timestamp("1999-12-01", LOS_ANGELES)},
+            new Object[]{2L, timestamp("2000-01-01", LOS_ANGELES)},
+            new Object[]{1L, timestamp("2000-12-01", LOS_ANGELES)},
+            new Object[]{2L, timestamp("2001-01-01", LOS_ANGELES)}
         )
     );
   }
@@ -6164,30 +6188,30 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                   .build()
         ),
         ImmutableList.<Object[]>builder()
-            .add(new Object[]{1L, t("2000-01-01")})
-            .add(new Object[]{defaultVal, t("2000-01-01T01")})
-            .add(new Object[]{defaultVal, t("2000-01-01T02")})
-            .add(new Object[]{defaultVal, t("2000-01-01T03")})
-            .add(new Object[]{defaultVal, t("2000-01-01T04")})
-            .add(new Object[]{defaultVal, t("2000-01-01T05")})
-            .add(new Object[]{defaultVal, t("2000-01-01T06")})
-            .add(new Object[]{defaultVal, t("2000-01-01T07")})
-            .add(new Object[]{defaultVal, t("2000-01-01T08")})
-            .add(new Object[]{defaultVal, t("2000-01-01T09")})
-            .add(new Object[]{defaultVal, t("2000-01-01T10")})
-            .add(new Object[]{defaultVal, t("2000-01-01T11")})
-            .add(new Object[]{defaultVal, t("2000-01-01T12")})
-            .add(new Object[]{defaultVal, t("2000-01-01T13")})
-            .add(new Object[]{defaultVal, t("2000-01-01T14")})
-            .add(new Object[]{defaultVal, t("2000-01-01T15")})
-            .add(new Object[]{defaultVal, t("2000-01-01T16")})
-            .add(new Object[]{defaultVal, t("2000-01-01T17")})
-            .add(new Object[]{defaultVal, t("2000-01-01T18")})
-            .add(new Object[]{defaultVal, t("2000-01-01T19")})
-            .add(new Object[]{defaultVal, t("2000-01-01T20")})
-            .add(new Object[]{defaultVal, t("2000-01-01T21")})
-            .add(new Object[]{defaultVal, t("2000-01-01T22")})
-            .add(new Object[]{defaultVal, t("2000-01-01T23")})
+            .add(new Object[]{1L, timestamp("2000-01-01")})
+            .add(new Object[]{defaultVal, timestamp("2000-01-01T01")})
+            .add(new Object[]{defaultVal, timestamp("2000-01-01T02")})
+            .add(new Object[]{defaultVal, timestamp("2000-01-01T03")})
+            .add(new Object[]{defaultVal, timestamp("2000-01-01T04")})
+            .add(new Object[]{defaultVal, timestamp("2000-01-01T05")})
+            .add(new Object[]{defaultVal, timestamp("2000-01-01T06")})
+            .add(new Object[]{defaultVal, timestamp("2000-01-01T07")})
+            .add(new Object[]{defaultVal, timestamp("2000-01-01T08")})
+            .add(new Object[]{defaultVal, timestamp("2000-01-01T09")})
+            .add(new Object[]{defaultVal, timestamp("2000-01-01T10")})
+            .add(new Object[]{defaultVal, timestamp("2000-01-01T11")})
+            .add(new Object[]{defaultVal, timestamp("2000-01-01T12")})
+            .add(new Object[]{defaultVal, timestamp("2000-01-01T13")})
+            .add(new Object[]{defaultVal, timestamp("2000-01-01T14")})
+            .add(new Object[]{defaultVal, timestamp("2000-01-01T15")})
+            .add(new Object[]{defaultVal, timestamp("2000-01-01T16")})
+            .add(new Object[]{defaultVal, timestamp("2000-01-01T17")})
+            .add(new Object[]{defaultVal, timestamp("2000-01-01T18")})
+            .add(new Object[]{defaultVal, timestamp("2000-01-01T19")})
+            .add(new Object[]{defaultVal, timestamp("2000-01-01T20")})
+            .add(new Object[]{defaultVal, timestamp("2000-01-01T21")})
+            .add(new Object[]{defaultVal, timestamp("2000-01-01T22")})
+            .add(new Object[]{defaultVal, timestamp("2000-01-01T23")})
             .build()
     );
   }
@@ -6212,12 +6236,12 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                   .build()
         ),
         ImmutableList.of(
-            new Object[]{1L, d("2000-01-01")},
-            new Object[]{1L, d("2000-01-02")},
-            new Object[]{1L, d("2000-01-03")},
-            new Object[]{1L, d("2001-01-01")},
-            new Object[]{1L, d("2001-01-02")},
-            new Object[]{1L, d("2001-01-03")}
+            new Object[]{1L, day("2000-01-01")},
+            new Object[]{1L, day("2000-01-02")},
+            new Object[]{1L, day("2000-01-03")},
+            new Object[]{1L, day("2001-01-01")},
+            new Object[]{1L, day("2001-01-02")},
+            new Object[]{1L, day("2001-01-03")}
         )
     );
   }
@@ -6227,7 +6251,7 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
   {
     testQuery(
         "SELECT SUM(cnt), dt FROM (\n"
-        + "  SELECT CASt(FLOOR(__time TO QUARTER) AS DATE) AS dt,\n"
+        + "  SELECT CAST(FLOOR(__time TO QUARTER) AS DATE) AS dt,\n"
         + "  cnt FROM druid.foo\n"
         + ") AS x\n"
         + "GROUP BY dt\n"
@@ -6242,8 +6266,8 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                   .build()
         ),
         ImmutableList.of(
-            new Object[]{3L, d("2000-01-01")},
-            new Object[]{3L, d("2001-01-01")}
+            new Object[]{3L, day("2000-01-01")},
+            new Object[]{3L, day("2001-01-01")}
         )
     );
   }
@@ -6269,8 +6293,8 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                   .build()
         ),
         ImmutableList.of(
-            new Object[]{t("2001-01-01"), 3L},
-            new Object[]{t("2000-01-01"), 3L}
+            new Object[]{timestamp("2001-01-01"), 3L},
+            new Object[]{timestamp("2000-01-01"), 3L}
         )
     );
   }
@@ -6291,19 +6315,19 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                         .setInterval(querySegmentSpec(Filtration.eternity()))
                         .setGranularity(Granularities.ALL)
                         .setVirtualColumns(
-                            expression_Virtual_Column(
-                                "d0:v",
+                            expressionVirtualColumn(
+                                "v0",
                                 "timestamp_extract(\"__time\",'YEAR','UTC')",
                                 ValueType.LONG
                             )
                         )
-                        .setDimensions(dimensionSpec(new DefaultDimensionSpec("d0:v", "d0", ValueType.LONG)))
+                        .setDimensions(dimensions(new DefaultDimensionSpec("v0", "v0", ValueType.LONG)))
                         .setAggregatorSpecs(aggregators(new LongSumAggregatorFactory("a0", "cnt")))
                         .setLimitSpec(
                             new DefaultLimitSpec(
                                 ImmutableList.of(
                                     new OrderByColumnSpec(
-                                        "d0",
+                                        "v0",
                                         OrderByColumnSpec.Direction.ASCENDING,
                                         StringComparators.NUMERIC
                                     )
@@ -6326,10 +6350,10 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
   {
     testQuery(
         "SELECT\n"
-        + "  TIME_FORMAT(__time, 'yyyy MM') AS \"year\",\n"
+        + "  TIME_FORMAt(__time, 'yyyy MM') AS \"year\",\n"
         + "  SUM(cnt)\n"
         + "FROM druid.foo\n"
-        + "GROUP BY TIME_FORMAT(__time, 'yyyy MM')\n"
+        + "GROUP BY TIME_FORMAt(__time, 'yyyy MM')\n"
         + "ORDER BY 1",
         ImmutableList.of(
             GroupByQuery.builder()
@@ -6337,19 +6361,19 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                         .setInterval(querySegmentSpec(Filtration.eternity()))
                         .setGranularity(Granularities.ALL)
                         .setVirtualColumns(
-                            expression_Virtual_Column(
-                                "d0:v",
+                            expressionVirtualColumn(
+                                "v0",
                                 "timestamp_format(\"__time\",'yyyy MM','UTC')",
                                 ValueType.STRING
                             )
                         )
-                        .setDimensions(dimensionSpec(new DefaultDimensionSpec("d0:v", "d0", ValueType.STRING)))
+                        .setDimensions(dimensions(new DefaultDimensionSpec("v0", "v0", ValueType.STRING)))
                         .setAggregatorSpecs(aggregators(new LongSumAggregatorFactory("a0", "cnt")))
                         .setLimitSpec(
                             new DefaultLimitSpec(
                                 ImmutableList.of(
                                     new OrderByColumnSpec(
-                                        "d0",
+                                        "v0",
                                         OrderByColumnSpec.Direction.ASCENDING,
                                         StringComparators.LEXICOGRAPHIC
                                     )
@@ -6381,13 +6405,13 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                         .setInterval(querySegmentSpec(Filtration.eternity()))
                         .setGranularity(Granularities.ALL)
                         .setVirtualColumns(
-                            expression_Virtual_Column(
-                                "d0:v",
+                            expressionVirtualColumn(
+                                "v0",
                                 "timestamp_extract(timestamp_floor(\"__time\",'P1Y',null,'UTC'),'YEAR','UTC')",
                                 ValueType.LONG
                             )
                         )
-                        .setDimensions(dimensionSpec(new DefaultDimensionSpec("d0:v", "d0", ValueType.LONG)))
+                        .setDimensions(dimensions(new DefaultDimensionSpec("v0", "v0", ValueType.LONG)))
                         .setAggregatorSpecs(aggregators(new LongSumAggregatorFactory("a0", "cnt")))
                         .setContext(QUERY_CONTEXT_DEFAULT)
                         .build()
@@ -6416,13 +6440,13 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                         .setInterval(querySegmentSpec(Filtration.eternity()))
                         .setGranularity(Granularities.ALL)
                         .setVirtualColumns(
-                            expression_Virtual_Column(
-                                "d0:v",
+                            expressionVirtualColumn(
+                                "v0",
                                 "timestamp_extract(timestamp_floor(\"__time\",'P1Y',null,'America/Los_Angeles'),'YEAR','America/Los_Angeles')",
                                 ValueType.LONG
                             )
                         )
-                        .setDimensions(dimensionSpec(new DefaultDimensionSpec("d0:v", "d0", ValueType.LONG)))
+                        .setDimensions(dimensions(new DefaultDimensionSpec("v0", "v0", ValueType.LONG)))
                         .setAggregatorSpecs(aggregators(new LongSumAggregatorFactory("a0", "cnt")))
                         .setContext(QUERY_CONTEXT_LOS_ANGELES)
                         .build()
@@ -6460,7 +6484,7 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                   .build()
         ),
         ImmutableList.of(
-            new Object[]{t("2000-01-01"), 3L}
+            new Object[]{timestamp("2000-01-01"), 3L}
         )
     );
   }
@@ -6487,7 +6511,7 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                   .build()
         ),
         ImmutableList.of(
-            new Object[]{t("2000-01-01"), 3L}
+            new Object[]{timestamp("2000-01-01"), 3L}
         )
     );
   }
@@ -6515,7 +6539,7 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                   .build()
         ),
         ImmutableList.of(
-            new Object[]{t("2000-01-01"), 3L}
+            new Object[]{timestamp("2000-01-01"), 3L}
         )
     );
   }
@@ -6534,16 +6558,16 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                         .setInterval(querySegmentSpec(Filtration.eternity()))
                         .setGranularity(Granularities.ALL)
                         .setVirtualColumns(
-                            expression_Virtual_Column(
-                                "d1:v",
+                            expressionVirtualColumn(
+                                "v0",
                                 "timestamp_floor(\"__time\",'P1M',null,'UTC')",
                                 ValueType.LONG
                             )
                         )
                         .setDimensions(
-                            dimensionSpec(
+                            dimensions(
                                 new DefaultDimensionSpec("dim2", "d0"),
-                                new DefaultDimensionSpec("d1:v", "d1", ValueType.LONG)
+                                new DefaultDimensionSpec("v0", "v0", ValueType.LONG)
                             )
                         )
                         .setAggregatorSpecs(aggregators(new LongSumAggregatorFactory("a0", "cnt")))
@@ -6552,7 +6576,7 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                                 ImmutableList.of(
                                     new OrderByColumnSpec("d0", OrderByColumnSpec.Direction.ASCENDING),
                                     new OrderByColumnSpec(
-                                        "d1",
+                                        "v0",
                                         OrderByColumnSpec.Direction.ASCENDING,
                                         StringComparators.NUMERIC
                                     )
@@ -6565,19 +6589,19 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
         ),
         NullHandling.replaceWithDefault() ?
         ImmutableList.of(
-            new Object[]{"", t("2000-01-01"), 2L},
-            new Object[]{"", t("2001-01-01"), 1L},
-            new Object[]{"a", t("2000-01-01"), 1L},
-            new Object[]{"a", t("2001-01-01"), 1L},
-            new Object[]{"abc", t("2001-01-01"), 1L}
+            new Object[]{"", timestamp("2000-01-01"), 2L},
+            new Object[]{"", timestamp("2001-01-01"), 1L},
+            new Object[]{"a", timestamp("2000-01-01"), 1L},
+            new Object[]{"a", timestamp("2001-01-01"), 1L},
+            new Object[]{"abc", timestamp("2001-01-01"), 1L}
         ) :
         ImmutableList.of(
-            new Object[]{null, t("2000-01-01"), 1L},
-            new Object[]{null, t("2001-01-01"), 1L},
-            new Object[]{"", t("2000-01-01"), 1L},
-            new Object[]{"a", t("2000-01-01"), 1L},
-            new Object[]{"a", t("2001-01-01"), 1L},
-            new Object[]{"abc", t("2001-01-01"), 1L}
+            new Object[]{null, timestamp("2000-01-01"), 1L},
+            new Object[]{null, timestamp("2001-01-01"), 1L},
+            new Object[]{"", timestamp("2000-01-01"), 1L},
+            new Object[]{"a", timestamp("2000-01-01"), 1L},
+            new Object[]{"a", timestamp("2001-01-01"), 1L},
+            new Object[]{"abc", timestamp("2001-01-01"), 1L}
         )
     );
   }
@@ -6598,7 +6622,7 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                         .setInterval(querySegmentSpec(Filtration.eternity()))
                         .setGranularity(Granularities.ALL)
                         .setDimFilter(not(selector("dim1", "", null)))
-                        .setDimensions(dimensionSpec(new DefaultDimensionSpec("dim1", "d0")))
+                        .setDimensions(dimensions(new DefaultDimensionSpec("dim1", "d0")))
                         .setContext(QUERY_CONTEXT_DEFAULT)
                         .build(),
             GroupByQuery.builder()
@@ -6612,7 +6636,7 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                             )
                         )
                         .setDimensions(
-                            dimensionSpec(
+                            dimensions(
                                 new DefaultDimensionSpec("dim1", "d0"),
                                 new DefaultDimensionSpec("dim2", "d1")
                             )
@@ -6647,7 +6671,7 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
         + "        BindableJoin(condition=[true], joinType=[inner])\n"
         + "          DruidQueryRel(query=[{\"queryType\":\"scan\",\"dataSource\":{\"type\":\"table\",\"name\":\"foo\"},\"intervals\":{\"type\":\"intervals\",\"intervals\":[\"-146136543-09-08T08:23:32.096Z/146140482-04-24T15:36:27.903Z\"]},\"virtualColumns\":[],\"resultFormat\":\"compactedList\",\"batchSize\":20480,\"limit\":9223372036854775807,\"filter\":null,\"columns\":[\"dim1\",\"dim2\"],\"legacy\":false,\"context\":{\"defaultTimeout\":300000,\"maxScatterGatherBytes\":9223372036854775 [...]
         + "          DruidQueryRel(query=[{\"queryType\":\"timeseries\",\"dataSource\":{\"type\":\"table\",\"name\":\"foo\"},\"intervals\":{\"type\":\"intervals\",\"intervals\":[\"-146136543-09-08T08:23:32.096Z/146140482-04-24T15:36:27.903Z\"]},\"descending\":false,\"virtualColumns\":[],\"filter\":{\"type\":\"like\",\"dimension\":\"dim1\",\"pattern\":\"%bc\",\"escape\":null,\"extractionFn\":null},\"granularity\":{\"type\":\"all\"},\"aggregations\":[{\"type\":\"count\",\"name\":\"a0\"}],\ [...]
-        + "        DruidQueryRel(query=[{\"queryType\":\"groupBy\",\"dataSource\":{\"type\":\"table\",\"name\":\"foo\"},\"intervals\":{\"type\":\"intervals\",\"intervals\":[\"-146136543-09-08T08:23:32.096Z/146140482-04-24T15:36:27.903Z\"]},\"virtualColumns\":[{\"type\":\"expression\",\"name\":\"d1:v\",\"expression\":\"1\",\"outputType\":\"LONG\"}],\"filter\":{\"type\":\"like\",\"dimension\":\"dim1\",\"pattern\":\"%bc\",\"escape\":null,\"extractionFn\":null},\"granularity\":{\"type\":\"al [...]
+        + "        DruidQueryRel(query=[{\"queryType\":\"groupBy\",\"dataSource\":{\"type\":\"table\",\"name\":\"foo\"},\"intervals\":{\"type\":\"intervals\",\"intervals\":[\"-146136543-09-08T08:23:32.096Z/146140482-04-24T15:36:27.903Z\"]},\"virtualColumns\":[{\"type\":\"expression\",\"name\":\"v0\",\"expression\":\"1\",\"outputType\":\"LONG\"}],\"filter\":{\"type\":\"like\",\"dimension\":\"dim1\",\"pattern\":\"%bc\",\"escape\":null,\"extractionFn\":null},\"granularity\":{\"type\":\"all\ [...]
 
     final String theQuery = "SELECT dim1, dim2, COUNT(*) FROM druid.foo\n"
                             + "WHERE dim1 = 'xxx' OR dim2 IN (SELECT dim1 FROM druid.foo WHERE dim1 LIKE '%bc')\n"
@@ -6696,12 +6720,12 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                         .setInterval(querySegmentSpec(Filtration.eternity()))
                         .setGranularity(Granularities.ALL)
                         .setDimFilter(selector("dim2", "abc", null))
-                        .setDimensions(dimensionSpec(
+                        .setDimensions(dimensions(
                             new DefaultDimensionSpec("dim1", "d0"),
                             new DefaultDimensionSpec("dim2", "d1")
                         ))
                         .setAggregatorSpecs(aggregators(new CountAggregatorFactory("a0")))
-                        .setHavingSpec(having(numeric_Selector("a0", "1", null)))
+                        .setHavingSpec(having(selector("a0", "1", null)))
                         .setContext(QUERY_CONTEXT_DEFAULT)
                         .build(),
             newScanQueryBuilder()
@@ -6720,7 +6744,7 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                 .build()
         ),
         ImmutableList.of(
-            new Object[]{t("2001-01-02"), 1L, "def", "abc"}
+            new Object[]{timestamp("2001-01-02"), 1L, "def", "abc"}
         )
     );
   }
@@ -6744,7 +6768,7 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                         .setDataSource(CalciteTests.DATASOURCE1)
                         .setInterval(querySegmentSpec(Filtration.eternity()))
                         .setGranularity(Granularities.ALL)
-                        .setDimensions(dimensionSpec(new DefaultDimensionSpec("dim2", "d0")))
+                        .setDimensions(dimensions(new DefaultDimensionSpec("dim2", "d0")))
                         .setLimitSpec(
                             new DefaultLimitSpec(
                                 ImmutableList.of(
@@ -6800,7 +6824,7 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                         .setDataSource(CalciteTests.DATASOURCE1)
                         .setInterval(querySegmentSpec(Filtration.eternity()))
                         .setGranularity(Granularities.ALL)
-                        .setDimensions(dimensionSpec(new DefaultDimensionSpec("dim2", "d0")))
+                        .setDimensions(dimensions(new DefaultDimensionSpec("dim2", "d0")))
                         .setDimFilter(selector("dim1", "def", null))
                         .setContext(QUERY_CONTEXT_DEFAULT)
                         .build(),
@@ -6808,7 +6832,7 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                 .dataSource(CalciteTests.DATASOURCE1)
                 .intervals(querySegmentSpec(Filtration.eternity()))
                 .virtualColumns(
-                    expression_Virtual_Column("v0", "timestamp_extract(\"__time\",'MONTH','UTC')", ValueType.LONG)
+                    expressionVirtualColumn("v0", "timestamp_extract(\"__time\",'MONTH','UTC')", ValueType.LONG)
                 )
                 .filters(
                     and(
@@ -6844,7 +6868,7 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                 .setDataSource(CalciteTests.DATASOURCE1)
                 .setInterval(querySegmentSpec(Filtration.eternity()))
                 .setGranularity(Granularities.ALL)
-                .setDimensions(dimensionSpec(new DefaultDimensionSpec("dim2", "d0")))
+                .setDimensions(dimensions(new DefaultDimensionSpec("dim2", "d0")))
                 .setDimFilter(selector("dim1", "def", null))
                 .setContext(QUERY_CONTEXT_DEFAULT)
                 .build(),
@@ -6852,7 +6876,7 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                 .builder()
                 .setDataSource(CalciteTests.DATASOURCE1)
                 .setVirtualColumns(
-                    expression_Virtual_Column("d0:v", "timestamp_extract(\"__time\",'MONTH','UTC')", ValueType.LONG)
+                    expressionVirtualColumn("v0", "timestamp_extract(\"__time\",'MONTH','UTC')", ValueType.LONG)
                 )
                 .setDimFilter(
                     and(
@@ -6860,7 +6884,7 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                         selector("dim2", "abc", null)
                     )
                 )
-                .setDimensions(dimensionSpec(new DefaultDimensionSpec("d0:v", "d0", ValueType.LONG)))
+                .setDimensions(dimensions(new DefaultDimensionSpec("v0", "v0", ValueType.LONG)))
                 .setInterval(querySegmentSpec(Filtration.eternity()))
                 .setGranularity(Granularities.ALL)
                 .setAggregatorSpecs(
@@ -6880,7 +6904,7 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                     new DefaultLimitSpec(
                         ImmutableList.of(
                             new OrderByColumnSpec(
-                                "d0",
+                                "v0",
                                 OrderByColumnSpec.Direction.ASCENDING,
                                 StringComparators.NUMERIC
                             )
@@ -6911,7 +6935,7 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                         .setGranularity(Granularities.ALL)
                         .setDimFilter(not(selector("dim1", "", null)))
                         .setDimensions(
-                            dimensionSpec(new ExtractionDimensionSpec("dim1", "d0", new SubstringDimExtractionFn(0, 1)))
+                            dimensions(new ExtractionDimensionSpec("dim1", "d0", new SubstringDimExtractionFn(0, 1)))
                         )
                         .setContext(QUERY_CONTEXT_DEFAULT)
                         .build(),
@@ -6924,7 +6948,7 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                             ImmutableList.of("1", "2", "a", "d"),
                             new SubstringDimExtractionFn(0, 1)
                         ))
-                        .setDimensions(dimensionSpec(new DefaultDimensionSpec("dim2", "d0")))
+                        .setDimensions(dimensions(new DefaultDimensionSpec("dim2", "d0")))
                         .setAggregatorSpecs(aggregators(new CountAggregatorFactory("a0")))
                         .setContext(QUERY_CONTEXT_DEFAULT)
                         .build()
@@ -6958,7 +6982,7 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                             new LikeDimFilter("dim1", "דר%", null, null),
                             new SelectorDimFilter("dim1", "друид", null)
                         ))
-                        .setDimensions(dimensionSpec(
+                        .setDimensions(dimensions(
                             new DefaultDimensionSpec("dim1", "d0"),
                             new DefaultDimensionSpec("dim2", "d1")
                         ))
@@ -6984,7 +7008,7 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                         .setInterval(querySegmentSpec(Filtration.eternity()))
                         .setGranularity(Granularities.ALL)
                         .setDimensions(
-                            dimensionSpec(
+                            dimensions(
                                 new DefaultDimensionSpec("dim1", "d0"),
                                 new DefaultDimensionSpec("dim2", "d1")
                             )
@@ -7023,7 +7047,7 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                         .setInterval(querySegmentSpec(Filtration.eternity()))
                         .setGranularity(Granularities.ALL)
                         .setDimensions(
-                            dimensionSpec(
+                            dimensions(
                                 new DefaultDimensionSpec("dim1", "d0"),
                                 new DefaultDimensionSpec("dim2", "d1")
                             )
@@ -7031,7 +7055,7 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                         .setAggregatorSpecs(
                             aggregators(new CountAggregatorFactory("a0"), new DoubleSumAggregatorFactory("a1", "m2"))
                         )
-                        .setPostAggregatorSpecs(Collections.singletonList(expresionPostAgg(
+                        .setPostAggregatorSpecs(Collections.singletonList(expressionPostAgg(
                             "s0",
                             "(\"a1\" / \"a0\")"
                         )))
@@ -7068,7 +7092,7 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                         .setInterval(querySegmentSpec(Filtration.eternity()))
                         .setGranularity(Granularities.ALL)
                         .setDimensions(
-                            dimensionSpec(
+                            dimensions(
                                 new DefaultDimensionSpec("dim1", "d0")
                             )
                         )
@@ -7124,7 +7148,7 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                                         .setDataSource(CalciteTests.DATASOURCE1)
                                         .setInterval(querySegmentSpec(Filtration.eternity()))
                                         .setGranularity(Granularities.ALL)
-                                        .setDimensions(dimensionSpec(
+                                        .setDimensions(dimensions(
                                             new DefaultDimensionSpec("__time", "d0", ValueType.LONG),
                                             new DefaultDimensionSpec("m2", "d1", ValueType.DOUBLE),
                                             new DefaultDimensionSpec("dim1", "d2")
@@ -7134,7 +7158,7 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                         )
                         .setInterval(querySegmentSpec(Filtration.eternity()))
                         .setGranularity(Granularities.ALL)
-                        .setDimensions(dimensionSpec(
+                        .setDimensions(dimensions(
                             new DefaultDimensionSpec("d0", "_d0", ValueType.LONG),
                             new DefaultDimensionSpec("d2", "_d1", ValueType.STRING)
                         ))
@@ -7190,7 +7214,7 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                       )
                   )
                   .postAggregators(
-                      expresionPostAgg("p0", "(\"a0\" + \"a1\")")
+                      expressionPostAgg("p0", "(\"a0\" + \"a1\")")
                   )
                   .descending(true)
                   .context(TIMESERIES_CONTEXT_DEFAULT)
@@ -7240,7 +7264,7 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                                 new FieldAccessPostAggregator(null, "a0:count")
                             )
                         ),
-                        expresionPostAgg("p0", "(\"a1\" + \"a2\")")
+                        expressionPostAgg("p0", "(\"a1\" + \"a2\")")
                     )
                 )
                 .metric(new DimensionTopNMetricSpec(null, StringComparators.NUMERIC))
@@ -7259,12 +7283,12 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
   public void testConcat() throws Exception
   {
     testQuery(
-        "SELECT CONCAT(dim1, '-', dim1, '_', dim1) as dimX FROM foo",
+        "SELECT CONCAt(dim1, '-', dim1, '_', dim1) as dimX FROM foo",
         ImmutableList.of(
             newScanQueryBuilder()
                 .dataSource(CalciteTests.DATASOURCE1)
                 .intervals(querySegmentSpec(Filtration.eternity()))
-                .virtualColumns(expression_Virtual_Column(
+                .virtualColumns(expressionVirtualColumn(
                     "v0",
                     "concat(\"dim1\",'-',\"dim1\",'_',\"dim1\")",
                     ValueType.STRING
@@ -7285,12 +7309,12 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
     );
 
     testQuery(
-        "SELECT CONCAT(dim1, CONCAT(dim2,'x'), m2, 9999, dim1) as dimX FROM foo",
+        "SELECT CONCAt(dim1, CONCAt(dim2,'x'), m2, 9999, dim1) as dimX FROM foo",
         ImmutableList.of(
             newScanQueryBuilder()
                 .dataSource(CalciteTests.DATASOURCE1)
                 .intervals(querySegmentSpec(Filtration.eternity()))
-                .virtualColumns(expression_Virtual_Column(
+                .virtualColumns(expressionVirtualColumn(
                     "v0",
                     "concat(\"dim1\",concat(\"dim2\",'x'),\"m2\",9999,\"dim1\")",
                     ValueType.STRING
@@ -7320,7 +7344,7 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
             newScanQueryBuilder()
                 .dataSource(CalciteTests.DATASOURCE1)
                 .intervals(querySegmentSpec(Filtration.eternity()))
-                .virtualColumns(expression_Virtual_Column("v0", "concat(\"dim1\",\"dim1\")", ValueType.STRING))
+                .virtualColumns(expressionVirtualColumn("v0", "concat(\"dim1\",\"dim1\")", ValueType.STRING))
                 .columns("v0")
                 .resultFormat(ScanQuery.RESULT_FORMAT_COMPACTED_LIST)
                 .context(QUERY_CONTEXT_DEFAULT)
@@ -7342,7 +7366,7 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
             newScanQueryBuilder()
                 .dataSource(CalciteTests.DATASOURCE1)
                 .intervals(querySegmentSpec(Filtration.eternity()))
-                .virtualColumns(expression_Virtual_Column(
+                .virtualColumns(expressionVirtualColumn(
                     "v0",
                     "concat(\"dim1\",CAST(\"m2\", 'STRING'))",
                     ValueType.STRING
@@ -7387,8 +7411,8 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                   .build()
         ),
         ImmutableList.of(
-            new Object[]{3L, t("2000-01-01")},
-            new Object[]{3L, t("2001-01-01")}
+            new Object[]{3L, timestamp("2000-01-01")},
+            new Object[]{3L, timestamp("2001-01-01")}
         )
     );
 
@@ -7411,7 +7435,7 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                                                 JodaUtils.MAX_INSTANT
                                             )))
                                             .setGranularity(Granularities.ALL)
-                                            .setDimensions(dimensionSpec(new DefaultDimensionSpec("dim2", "d0")))
+                                            .setDimensions(dimensions(new DefaultDimensionSpec("dim2", "d0")))
                                             .setAggregatorSpecs(aggregators(new LongSumAggregatorFactory("a0", "cnt")))
                                             .setContext(QUERY_CONTEXT_DEFAULT)
                                             .build()
@@ -7450,7 +7474,7 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                         .setInterval(querySegmentSpec(Intervals.utc(DateTimes.of("2000-01-01").getMillis(), JodaUtils.MAX_INSTANT)))
                         .setGranularity(Granularities.ALL)
                         .setDimFilter(not(selector("dim1", "", null)))
-                        .setDimensions(dimensionSpec(new ExtractionDimensionSpec(
+                        .setDimensions(dimensions(new ExtractionDimensionSpec(
                             "dim1",
                             "d0",
                             new SubstringDimExtractionFn(0, 1)
@@ -7618,15 +7642,15 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
                                    new CountAggregatorFactory("a0")
                                ))
                                .postAggregators(
-                                   expresionPostAgg("p0", "(exp(\"a0\") + 10)"),
-                                   expresionPostAgg("p1", "sin((pi() / 6))"),
-                                   expresionPostAgg("p2", "cos((pi() / 6))"),
-                                   expresionPostAgg("p3", "tan((pi() / 6))"),
-                                   expresionPostAgg("p4", "cot((pi() / 6))"),
-                                   expresionPostAgg("p5", "asin((exp(\"a0\") / 2))"),
-                                   expresionPostAgg("p6", "acos((exp(\"a0\") / 2))"),
-                                   expresionPostAgg("p7", "atan((exp(\"a0\") / 2))"),
-                                   expresionPostAgg("p8", "atan2(exp(\"a0\"),1)")
+                                   expressionPostAgg("p0", "(exp(\"a0\") + 10)"),
+                                   expressionPostAgg("p1", "sin((pi() / 6))"),
+                                   expressionPostAgg("p2", "cos((pi() / 6))"),
+                                   expressionPostAgg("p3", "tan((pi() / 6))"),
+                                   expressionPostAgg("p4", "cot((pi() / 6))"),
+                                   expressionPostAgg("p5", "asin((exp(\"a0\") / 2))"),
+                                   expressionPostAgg("p6", "acos((exp(\"a0\") / 2))"),
+                                   expressionPostAgg("p7", "atan((exp(\"a0\") / 2))"),
+                                   expressionPostAgg("p8", "atan2(exp(\"a0\"),1)")
                                )
                                .context(QUERY_CONTEXT_DONT_SKIP_EMPTY_BUCKETS)
                                .build()),
diff --git a/sql/src/test/java/org/apache/druid/sql/calcite/filtration/FiltrationTest.java b/sql/src/test/java/org/apache/druid/sql/calcite/filtration/FiltrationTest.java
index b85eae1..9d91a42 100644
--- a/sql/src/test/java/org/apache/druid/sql/calcite/filtration/FiltrationTest.java
+++ b/sql/src/test/java/org/apache/druid/sql/calcite/filtration/FiltrationTest.java
@@ -24,6 +24,9 @@ import org.apache.druid.java.util.common.Intervals;
 import org.apache.druid.query.filter.IntervalDimFilter;
 import org.apache.druid.query.filter.NotDimFilter;
 import org.apache.druid.segment.column.ColumnHolder;
+import org.apache.druid.segment.column.ValueType;
+import org.apache.druid.sql.calcite.rel.DruidQuerySignature;
+import org.apache.druid.sql.calcite.table.RowSignature;
 import org.apache.druid.sql.calcite.util.CalciteTestBase;
 import org.junit.Assert;
 import org.junit.Test;
@@ -42,7 +45,7 @@ public class FiltrationTest extends CalciteTestBase
             )
         ),
         null
-    ).optimize(null);
+    ).optimize(new DruidQuerySignature(RowSignature.builder().add(ColumnHolder.TIME_COLUMN_NAME, ValueType.LONG).build()));
 
     Assert.assertEquals(
         ImmutableList.of(Filtration.eternity()),


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@druid.apache.org
For additional commands, e-mail: commits-help@druid.apache.org