You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@calcite.apache.org by jh...@apache.org on 2017/11/08 17:52:55 UTC

[1/3] calcite git commit: [CALCITE-2035] Allow approximate aggregate functions, and add APPROX_COUNT_DISTINCT

Repository: calcite
Updated Branches:
  refs/heads/master e07e8ad9f -> 77a354917


[CALCITE-2035] Allow approximate aggregate functions, and add APPROX_COUNT_DISTINCT

There are changes to RelBuilder and AggregateCall APIs, but extensions
to SQL grammar will not happen until [CALCITE-1588].

In Druid adapter, HyperLogLog will be used if the call is to
APPROX_COUNT_DISTINCT or if approximateDistinctCount=true in connection
properties.


Project: http://git-wip-us.apache.org/repos/asf/calcite/repo
Commit: http://git-wip-us.apache.org/repos/asf/calcite/commit/fe3529d9
Tree: http://git-wip-us.apache.org/repos/asf/calcite/tree/fe3529d9
Diff: http://git-wip-us.apache.org/repos/asf/calcite/diff/fe3529d9

Branch: refs/heads/master
Commit: fe3529d9a6d4d826c6e77fc4c0d7d18bd681bb85
Parents: e07e8ad
Author: Julian Hyde <jh...@apache.org>
Authored: Mon Nov 6 16:03:13 2017 -0800
Committer: Julian Hyde <jh...@apache.org>
Committed: Wed Nov 8 08:41:32 2017 -0800

----------------------------------------------------------------------
 .../org/apache/calcite/plan/RelOptUtil.java     |  6 +-
 .../calcite/plan/SubstitutionVisitor.java       |  2 +-
 .../apache/calcite/rel/core/AggregateCall.java  | 60 +++++++++++++++-----
 .../java/org/apache/calcite/rel/core/Match.java |  4 +-
 .../org/apache/calcite/rel/core/Window.java     |  5 +-
 .../calcite/rel/externalize/RelJsonReader.java  |  2 +-
 .../rel/rules/AbstractMaterializedViewRule.java | 22 +++----
 .../AggregateExpandDistinctAggregatesRule.java  | 25 ++++----
 .../rel/rules/AggregateFilterTransposeRule.java |  4 +-
 .../rel/rules/AggregateReduceFunctionsRule.java | 26 +++++----
 .../rel/rules/AggregateStarTableRule.java       |  6 +-
 .../rel/rules/AggregateUnionTransposeRule.java  |  1 +
 .../calcite/rel/rules/SubQueryRemoveRule.java   |  8 +--
 .../calcite/sql/SqlSplittableAggFunction.java   |  8 +--
 .../calcite/sql/fun/SqlCountAggFunction.java    | 13 +----
 .../calcite/sql/fun/SqlStdOperatorTable.java    |  8 ++-
 .../apache/calcite/sql2rel/RelFieldTrimmer.java |  3 +-
 .../calcite/sql2rel/SqlToRelConverter.java      | 15 +++--
 .../org/apache/calcite/tools/PigRelBuilder.java |  3 +-
 .../org/apache/calcite/tools/RelBuilder.java    | 46 ++++++++++-----
 .../org/apache/calcite/plan/RelWriterTest.java  |  6 +-
 .../plan/volcano/TraitPropagationTest.java      |  2 +-
 .../calcite/sql/test/SqlOperatorBaseTest.java   | 45 +++++++++++++++
 .../org/apache/calcite/test/RelBuilderTest.java | 34 +++++------
 .../apache/calcite/test/RelMetadataTest.java    |  4 +-
 .../calcite/adapter/druid/DruidQuery.java       |  2 +-
 .../calcite/adapter/druid/DruidRules.java       |  6 +-
 .../calcite/adapter/druid/DruidTable.java       | 12 ++--
 .../org/apache/calcite/test/DruidAdapterIT.java | 35 +++++++-----
 site/_docs/algebra.md                           |  2 +-
 site/_docs/reference.md                         |  6 +-
 31 files changed, 275 insertions(+), 146 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/calcite/blob/fe3529d9/core/src/main/java/org/apache/calcite/plan/RelOptUtil.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/plan/RelOptUtil.java b/core/src/main/java/org/apache/calcite/plan/RelOptUtil.java
index 625fc73..5021569 100644
--- a/core/src/main/java/org/apache/calcite/plan/RelOptUtil.java
+++ b/core/src/main/java/org/apache/calcite/plan/RelOptUtil.java
@@ -500,6 +500,7 @@ public abstract class RelOptUtil {
       final AggregateCall aggCall =
           AggregateCall.create(SqlStdOperatorTable.MIN,
               false,
+              false,
               ImmutableList.of(0),
               -1,
               0,
@@ -578,6 +579,7 @@ public abstract class RelOptUtil {
     final AggregateCall aggCall =
         AggregateCall.create(SqlStdOperatorTable.MIN,
             false,
+            false,
             ImmutableList.of(projectedKeyCount),
             -1,
             projectedKeyCount,
@@ -781,8 +783,8 @@ public abstract class RelOptUtil {
     for (int i = 0; i < aggCallCnt; i++) {
       aggCalls.add(
           AggregateCall.create(
-              SqlStdOperatorTable.SINGLE_VALUE, false, ImmutableList.of(i), -1,
-              0, rel, null, null));
+              SqlStdOperatorTable.SINGLE_VALUE, false, false,
+              ImmutableList.of(i), -1, 0, rel, null, null));
     }
 
     return LogicalAggregate.create(rel, ImmutableBitSet.of(), null, aggCalls);

http://git-wip-us.apache.org/repos/asf/calcite/blob/fe3529d9/core/src/main/java/org/apache/calcite/plan/SubstitutionVisitor.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/plan/SubstitutionVisitor.java b/core/src/main/java/org/apache/calcite/plan/SubstitutionVisitor.java
index ffc036a..7129ccb 100644
--- a/core/src/main/java/org/apache/calcite/plan/SubstitutionVisitor.java
+++ b/core/src/main/java/org/apache/calcite/plan/SubstitutionVisitor.java
@@ -1264,7 +1264,7 @@ public class SubstitutionVisitor {
         }
         aggregateCalls.add(
             AggregateCall.create(getRollup(aggregateCall.getAggregation()),
-                aggregateCall.isDistinct(),
+                aggregateCall.isDistinct(), aggregateCall.isApproximate(),
                 ImmutableList.of(target.groupSet.cardinality() + i), -1,
                 aggregateCall.type, aggregateCall.name));
       }

http://git-wip-us.apache.org/repos/asf/calcite/blob/fe3529d9/core/src/main/java/org/apache/calcite/rel/core/AggregateCall.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/core/AggregateCall.java b/core/src/main/java/org/apache/calcite/rel/core/AggregateCall.java
index 501aba6..f7194f4 100644
--- a/core/src/main/java/org/apache/calcite/rel/core/AggregateCall.java
+++ b/core/src/main/java/org/apache/calcite/rel/core/AggregateCall.java
@@ -40,6 +40,7 @@ public class AggregateCall {
   private final SqlAggFunction aggFunction;
 
   private final boolean distinct;
+  private final boolean approximate;
   public final RelDataType type;
   public final String name;
 
@@ -66,7 +67,7 @@ public class AggregateCall {
       List<Integer> argList,
       RelDataType type,
       String name) {
-    this(aggFunction, distinct, argList, -1, type, name);
+    this(aggFunction, distinct, false, argList, -1, type, name);
   }
 
   /**
@@ -74,6 +75,7 @@ public class AggregateCall {
    *
    * @param aggFunction Aggregate function
    * @param distinct    Whether distinct
+   * @param approximate Whether approximate
    * @param argList     List of ordinals of arguments
    * @param filterArg   Ordinal of filter argument, or -1
    * @param type        Result type
@@ -82,6 +84,7 @@ public class AggregateCall {
   private AggregateCall(
       SqlAggFunction aggFunction,
       boolean distinct,
+      boolean approximate,
       List<Integer> argList,
       int filterArg,
       RelDataType type,
@@ -92,23 +95,32 @@ public class AggregateCall {
     this.argList = ImmutableList.copyOf(argList);
     this.filterArg = filterArg;
     this.distinct = distinct;
+    this.approximate = approximate;
   }
 
   //~ Methods ----------------------------------------------------------------
 
-  /** Creates an AggregateCall, inferring its type if {@code type} is null. */
   @Deprecated // to be removed before 2.0
   public static AggregateCall create(SqlAggFunction aggFunction,
       boolean distinct, List<Integer> argList, int groupCount, RelNode input,
       RelDataType type, String name) {
-    return create(aggFunction, distinct, argList, -1, groupCount, input, type,
-        name);
+    return create(aggFunction, distinct, false, argList, -1, groupCount, input,
+        type, name);
   }
 
-  /** Creates an AggregateCall, inferring its type if {@code type} is null. */
+  @Deprecated // to be removed before 2.0
   public static AggregateCall create(SqlAggFunction aggFunction,
       boolean distinct, List<Integer> argList, int filterArg, int groupCount,
       RelNode input, RelDataType type, String name) {
+    return create(aggFunction, distinct, false, argList, -1, groupCount, input,
+        type, name);
+  }
+
+  /** Creates an AggregateCall, inferring its type if {@code type} is null. */
+  public static AggregateCall create(SqlAggFunction aggFunction,
+      boolean distinct, boolean approximate, List<Integer> argList,
+      int filterArg, int groupCount,
+      RelNode input, RelDataType type, String name) {
     if (type == null) {
       final RelDataTypeFactory typeFactory =
           input.getCluster().getTypeFactory();
@@ -119,15 +131,23 @@ public class AggregateCall {
               groupCount, filterArg >= 0);
       type = aggFunction.inferReturnType(callBinding);
     }
-    return create(aggFunction, distinct, argList, filterArg, type, name);
+    return create(aggFunction, distinct, approximate, argList, filterArg, type,
+        name);
   }
 
-  /** Creates an AggregateCall. */
+  @Deprecated // to be removed before 2.0
   public static AggregateCall create(SqlAggFunction aggFunction,
       boolean distinct, List<Integer> argList, int filterArg, RelDataType type,
       String name) {
-    return new AggregateCall(aggFunction, distinct, argList, filterArg, type,
-        name);
+    return create(aggFunction, distinct, false, argList, filterArg, type, name);
+  }
+
+  /** Creates an AggregateCall. */
+  public static AggregateCall create(SqlAggFunction aggFunction,
+      boolean distinct, boolean approximate, List<Integer> argList,
+      int filterArg, RelDataType type, String name) {
+    return new AggregateCall(aggFunction, distinct, approximate, argList,
+        filterArg, type, name);
   }
 
   /**
@@ -141,6 +161,16 @@ public class AggregateCall {
   }
 
   /**
+   * Returns whether this AggregateCall is approximate, as in <code>
+   * APPROX_COUNT_DISTINCT(empno)</code>.
+   *
+   * @return whether approximate
+   */
+  public final boolean isApproximate() {
+    return approximate;
+  }
+
+  /**
    * Returns the aggregate function.
    *
    * @return aggregate function
@@ -187,8 +217,8 @@ public class AggregateCall {
     if (Objects.equals(this.name, name)) {
       return this;
     }
-    return new AggregateCall(aggFunction, distinct, argList, filterArg, type,
-        name);
+    return new AggregateCall(aggFunction, distinct, approximate, argList,
+        filterArg, type, name);
   }
 
   public String toString() {
@@ -257,8 +287,8 @@ public class AggregateCall {
    * @return AggregateCall that suits new inputs and GROUP BY columns
    */
   public AggregateCall copy(List<Integer> args, int filterArg) {
-    return new AggregateCall(aggFunction, distinct, args, filterArg, type,
-        name);
+    return new AggregateCall(aggFunction, distinct, approximate, args,
+        filterArg, type, name);
   }
 
   @Deprecated // to be removed before 2.0
@@ -287,8 +317,8 @@ public class AggregateCall {
             && filterArg == this.filterArg
             ? type
             : null;
-    return create(aggFunction, distinct, argList, filterArg, newGroupKeyCount,
-        input, newType, getName());
+    return create(aggFunction, distinct, approximate, argList, filterArg,
+        newGroupKeyCount, input, newType, getName());
   }
 
   /** Creates a copy of this aggregate call, applying a mapping to its

http://git-wip-us.apache.org/repos/asf/calcite/blob/fe3529d9/core/src/main/java/org/apache/calcite/rel/core/Match.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/core/Match.java b/core/src/main/java/org/apache/calcite/rel/core/Match.java
index 2278954..2ebafec 100644
--- a/core/src/main/java/org/apache/calcite/rel/core/Match.java
+++ b/core/src/main/java/org/apache/calcite/rel/core/Match.java
@@ -28,8 +28,8 @@ import org.apache.calcite.rex.RexNode;
 import org.apache.calcite.rex.RexPatternFieldRef;
 import org.apache.calcite.rex.RexVisitorImpl;
 import org.apache.calcite.sql.SqlAggFunction;
-import org.apache.calcite.sql.fun.SqlCountAggFunction;
 import org.apache.calcite.sql.fun.SqlMinMaxAggFunction;
+import org.apache.calcite.sql.fun.SqlStdOperatorTable;
 import org.apache.calcite.sql.fun.SqlSumAggFunction;
 import org.apache.calcite.sql.fun.SqlSumEmptyIsZeroAggFunction;
 
@@ -248,7 +248,7 @@ public abstract class Match extends SingleRel {
         aggFunction = new SqlMinMaxAggFunction(call.getKind());
         break;
       case COUNT:
-        aggFunction = new SqlCountAggFunction();
+        aggFunction = SqlStdOperatorTable.COUNT;
         break;
       default:
         for (RexNode rex : call.getOperands()) {

http://git-wip-us.apache.org/repos/asf/calcite/blob/fe3529d9/core/src/main/java/org/apache/calcite/rel/core/Window.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/core/Window.java b/core/src/main/java/org/apache/calcite/rel/core/Window.java
index b9d92ac..1e00bba 100644
--- a/core/src/main/java/org/apache/calcite/rel/core/Window.java
+++ b/core/src/main/java/org/apache/calcite/rel/core/Window.java
@@ -313,8 +313,9 @@ public abstract class Window extends SingleRel {
 
         public AggregateCall get(int index) {
           final RexWinAggCall aggCall = aggCalls.get(index);
-          return AggregateCall.create((SqlAggFunction) aggCall.getOperator(),
-              aggCall.distinct, getProjectOrdinals(aggCall.getOperands()), -1,
+          final SqlAggFunction op = (SqlAggFunction) aggCall.getOperator();
+          return AggregateCall.create(op, aggCall.distinct,
+              false, getProjectOrdinals(aggCall.getOperands()), -1,
               aggCall.getType(), fieldNames.get(aggCall.ordinal));
         }
       };

http://git-wip-us.apache.org/repos/asf/calcite/blob/fe3529d9/core/src/main/java/org/apache/calcite/rel/externalize/RelJsonReader.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/externalize/RelJsonReader.java b/core/src/main/java/org/apache/calcite/rel/externalize/RelJsonReader.java
index a3d6dd4..695ab0b 100644
--- a/core/src/main/java/org/apache/calcite/rel/externalize/RelJsonReader.java
+++ b/core/src/main/java/org/apache/calcite/rel/externalize/RelJsonReader.java
@@ -279,7 +279,7 @@ public class RelJsonReader {
     final Integer filterOperand = (Integer) jsonAggCall.get("filter");
     final RelDataType type =
         relJson.toType(cluster.getTypeFactory(), jsonAggCall.get("type"));
-    return AggregateCall.create(aggregation, distinct, operands,
+    return AggregateCall.create(aggregation, distinct, false, operands,
         filterOperand == null ? -1 : filterOperand, type, null);
   }
 

http://git-wip-us.apache.org/repos/asf/calcite/blob/fe3529d9/core/src/main/java/org/apache/calcite/rel/rules/AbstractMaterializedViewRule.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/rules/AbstractMaterializedViewRule.java b/core/src/main/java/org/apache/calcite/rel/rules/AbstractMaterializedViewRule.java
index d563f8b..e2ea81d 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/AbstractMaterializedViewRule.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/AbstractMaterializedViewRule.java
@@ -1048,12 +1048,10 @@ public abstract class AbstractMaterializedViewRule extends RelOptRule {
         aggregateCalls.add(
             relBuilder.aggregateCall(
                 SubstitutionVisitor.getRollup(aggCall.getAggregation()),
-                aggCall.isDistinct(),
-                null,
+                aggCall.isDistinct(), aggCall.isApproximate(), null,
                 aggCall.name,
-                ImmutableList.of(
-                    rexBuilder.makeInputRef(
-                        relBuilder.peek(), aggregate.getGroupCount() + i))));
+                rexBuilder.makeInputRef(relBuilder.peek(),
+                    aggregate.getGroupCount() + i)));
       }
       RelNode result = relBuilder
           .aggregate(relBuilder.groupKey(groupSet, null), aggregateCalls)
@@ -1251,10 +1249,9 @@ public abstract class AbstractMaterializedViewRule extends RelOptRule {
                 aggregateCalls.add(
                     relBuilder.aggregateCall(
                         SubstitutionVisitor.getRollup(queryAggCall.getAggregation()),
-                        queryAggCall.isDistinct(),
-                        null,
-                        queryAggCall.name,
-                        ImmutableList.of(rexBuilder.makeInputRef(input, k))));
+                        queryAggCall.isDistinct(), queryAggCall.isApproximate(),
+                        null, queryAggCall.name,
+                        rexBuilder.makeInputRef(input, k)));
                 rewritingMapping.set(k, sourceIdx);
                 added = true;
                 break;
@@ -1268,10 +1265,9 @@ public abstract class AbstractMaterializedViewRule extends RelOptRule {
             aggregateCalls.add(
                 relBuilder.aggregateCall(
                     SubstitutionVisitor.getRollup(queryAggCall.getAggregation()),
-                    queryAggCall.isDistinct(),
-                    null,
-                    queryAggCall.name,
-                    ImmutableList.of(rexBuilder.makeInputRef(input, targetIdx))));
+                    queryAggCall.isDistinct(), queryAggCall.isApproximate(),
+                    null, queryAggCall.name,
+                    rexBuilder.makeInputRef(input, targetIdx)));
             rewritingMapping.set(targetIdx, sourceIdx);
           }
         }

http://git-wip-us.apache.org/repos/asf/calcite/blob/fe3529d9/core/src/main/java/org/apache/calcite/rel/rules/AggregateExpandDistinctAggregatesRule.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/rules/AggregateExpandDistinctAggregatesRule.java b/core/src/main/java/org/apache/calcite/rel/rules/AggregateExpandDistinctAggregatesRule.java
index ca95bcc..53068d0 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/AggregateExpandDistinctAggregatesRule.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/AggregateExpandDistinctAggregatesRule.java
@@ -293,7 +293,7 @@ public final class AggregateExpandDistinctAggregatesRule extends RelOptRule {
       if (!aggCall.isDistinct()) {
         final AggregateCall newCall =
             AggregateCall.create(aggCall.getAggregation(), false,
-                aggCall.getArgList(), -1,
+                aggCall.isApproximate(), aggCall.getArgList(), -1,
                 ImmutableBitSet.of(bottomGroupSet).cardinality(),
                 relBuilder.peek(), null, aggCall.name);
         bottomAggregateCalls.add(newCall);
@@ -318,9 +318,9 @@ public final class AggregateExpandDistinctAggregatesRule extends RelOptRule {
           newArgList.add(bottomGroupSet.headSet(arg).size());
         }
         newCall =
-            AggregateCall.create(
-                aggCall.getAggregation(),
+            AggregateCall.create(aggCall.getAggregation(),
                 false,
+                aggCall.isApproximate(),
                 newArgList,
                 -1,
                 originalGroupSet.cardinality(),
@@ -334,12 +334,14 @@ public final class AggregateExpandDistinctAggregatesRule extends RelOptRule {
             Lists.newArrayList(bottomGroupSet.size() + nonDistinctAggCallProcessedSoFar);
         if (aggCall.getAggregation().getKind() == SqlKind.COUNT) {
           newCall =
-              AggregateCall.create(new SqlSumEmptyIsZeroAggFunction(), false, newArgs, -1,
+              AggregateCall.create(new SqlSumEmptyIsZeroAggFunction(), false,
+                  aggCall.isApproximate(), newArgs, -1,
                   originalGroupSet.cardinality(), relBuilder.peek(),
                   aggCall.getType(), aggCall.getName());
         } else {
           newCall =
-              AggregateCall.create(aggCall.getAggregation(), false, newArgs, -1,
+              AggregateCall.create(aggCall.getAggregation(), false,
+                  aggCall.isApproximate(), newArgs, -1,
                   originalGroupSet.cardinality(),
                   relBuilder.peek(), aggCall.getType(), aggCall.name);
         }
@@ -400,7 +402,7 @@ public final class AggregateExpandDistinctAggregatesRule extends RelOptRule {
     final Map<ImmutableBitSet, Integer> filters = new LinkedHashMap<>();
     final int z = groupCount + distinctAggCalls.size();
     distinctAggCalls.add(
-        AggregateCall.create(SqlStdOperatorTable.GROUPING, false,
+        AggregateCall.create(SqlStdOperatorTable.GROUPING, false, false,
             ImmutableIntList.copyOf(fullGroupSet), -1, groupSets.size(),
             relBuilder.peek(), null, "$g"));
     for (Ord<ImmutableBitSet> groupSet : Ord.zip(groupSets)) {
@@ -446,8 +448,9 @@ public final class AggregateExpandDistinctAggregatesRule extends RelOptRule {
                     .union(aggregate.getGroupSet()));
       }
       final AggregateCall newCall =
-          AggregateCall.create(aggregation, false, newArgList, newFilterArg,
-              aggregate.getGroupCount(), distinct, null, aggCall.name);
+          AggregateCall.create(aggregation, false, aggCall.isApproximate(),
+              newArgList, newFilterArg, aggregate.getGroupCount(), distinct,
+              null, aggCall.name);
       newCalls.add(newCall);
     }
 
@@ -655,7 +658,8 @@ public final class AggregateExpandDistinctAggregatesRule extends RelOptRule {
       final int newFilterArg =
           aggCall.filterArg >= 0 ? sourceOf.get(aggCall.filterArg) : -1;
       final AggregateCall newAggCall =
-          AggregateCall.create(aggCall.getAggregation(), false, newArgs,
+          AggregateCall.create(aggCall.getAggregation(), false,
+              aggCall.isApproximate(), newArgs,
               newFilterArg, aggCall.getType(), aggCall.getName());
       assert refs.get(i) == null;
       if (n == 0) {
@@ -743,7 +747,8 @@ public final class AggregateExpandDistinctAggregatesRule extends RelOptRule {
         newArgs.add(sourceOf.get(arg));
       }
       final AggregateCall newAggCall =
-          AggregateCall.create(aggCall.getAggregation(), false, newArgs, -1,
+          AggregateCall.create(aggCall.getAggregation(), false,
+              aggCall.isApproximate(), newArgs, -1,
               aggCall.getType(), aggCall.getName());
       newAggCalls.set(i, newAggCall);
     }

http://git-wip-us.apache.org/repos/asf/calcite/blob/fe3529d9/core/src/main/java/org/apache/calcite/rel/rules/AggregateFilterTransposeRule.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/rules/AggregateFilterTransposeRule.java b/core/src/main/java/org/apache/calcite/rel/rules/AggregateFilterTransposeRule.java
index dfe28f5..abe17bc 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/AggregateFilterTransposeRule.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/AggregateFilterTransposeRule.java
@@ -147,8 +147,8 @@ public class AggregateFilterTransposeRule extends RelOptRule {
         }
         topAggCallList.add(
             AggregateCall.create(rollup, aggregateCall.isDistinct(),
-                ImmutableList.of(i++), -1, aggregateCall.type,
-                aggregateCall.name));
+                aggregateCall.isApproximate(), ImmutableList.of(i++), -1,
+                aggregateCall.type, aggregateCall.name));
       }
       final Aggregate topAggregate =
           aggregate.copy(aggregate.getTraitSet(), newFilter,

http://git-wip-us.apache.org/repos/asf/calcite/blob/fe3529d9/core/src/main/java/org/apache/calcite/rel/rules/AggregateReduceFunctionsRule.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/rules/AggregateReduceFunctionsRule.java b/core/src/main/java/org/apache/calcite/rel/rules/AggregateReduceFunctionsRule.java
index a9bdb84..746e1f4 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/AggregateReduceFunctionsRule.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/AggregateReduceFunctionsRule.java
@@ -267,6 +267,7 @@ public class AggregateReduceFunctionsRule extends RelOptRule {
             oldCall.filterArg >= 0);
     return AggregateCall.create(aggFunction,
         oldCall.isDistinct(),
+        oldCall.isApproximate(),
         ImmutableIntList.of(argOrdinal),
         oldCall.filterArg,
         aggFunction.inferReturnType(binding),
@@ -287,9 +288,9 @@ public class AggregateReduceFunctionsRule extends RelOptRule {
             oldAggRel.getInput(),
             iAvgInput);
     final AggregateCall sumCall =
-        AggregateCall.create(
-            SqlStdOperatorTable.SUM,
+        AggregateCall.create(SqlStdOperatorTable.SUM,
             oldCall.isDistinct(),
+            oldCall.isApproximate(),
             oldCall.getArgList(),
             oldCall.filterArg,
             oldAggRel.getGroupCount(),
@@ -297,9 +298,9 @@ public class AggregateReduceFunctionsRule extends RelOptRule {
             null,
             null);
     final AggregateCall countCall =
-        AggregateCall.create(
-            SqlStdOperatorTable.COUNT,
+        AggregateCall.create(SqlStdOperatorTable.COUNT,
             oldCall.isDistinct(),
+            oldCall.isApproximate(),
             oldCall.getArgList(),
             oldCall.filterArg,
             oldAggRel.getGroupCount(),
@@ -347,12 +348,13 @@ public class AggregateReduceFunctionsRule extends RelOptRule {
             arg);
     final AggregateCall sumZeroCall =
         AggregateCall.create(SqlStdOperatorTable.SUM0, oldCall.isDistinct(),
-            oldCall.getArgList(), oldCall.filterArg, oldAggRel.getGroupCount(),
-            oldAggRel.getInput(), null, oldCall.name);
+            oldCall.isApproximate(), oldCall.getArgList(), oldCall.filterArg,
+            oldAggRel.getGroupCount(), oldAggRel.getInput(), null,
+            oldCall.name);
     final AggregateCall countCall =
-        AggregateCall.create(
-            SqlStdOperatorTable.COUNT,
+        AggregateCall.create(SqlStdOperatorTable.COUNT,
             oldCall.isDistinct(),
+            oldCall.isApproximate(),
             oldCall.getArgList(),
             oldCall.filterArg,
             oldAggRel.getGroupCount(),
@@ -441,9 +443,9 @@ public class AggregateReduceFunctionsRule extends RelOptRule {
             ImmutableList.of(sumArgSquaredAggCall.getType()));
 
     final AggregateCall sumArgAggCall =
-        AggregateCall.create(
-            SqlStdOperatorTable.SUM,
+        AggregateCall.create(SqlStdOperatorTable.SUM,
             oldCall.isDistinct(),
+            oldCall.isApproximate(),
             ImmutableIntList.of(argOrdinal),
             oldCall.filterArg,
             oldAggRel.getGroupCount(),
@@ -464,9 +466,9 @@ public class AggregateReduceFunctionsRule extends RelOptRule {
             SqlStdOperatorTable.MULTIPLY, sumArgCast, sumArgCast);
 
     final AggregateCall countArgAggCall =
-        AggregateCall.create(
-            SqlStdOperatorTable.COUNT,
+        AggregateCall.create(SqlStdOperatorTable.COUNT,
             oldCall.isDistinct(),
+            oldCall.isApproximate(),
             oldCall.getArgList(),
             oldCall.filterArg,
             oldAggRel.getGroupCount(),

http://git-wip-us.apache.org/repos/asf/calcite/blob/fe3529d9/core/src/main/java/org/apache/calcite/rel/rules/AggregateStarTableRule.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/rules/AggregateStarTableRule.java b/core/src/main/java/org/apache/calcite/rel/rules/AggregateStarTableRule.java
index 6391a98..51ae97f 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/AggregateStarTableRule.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/AggregateStarTableRule.java
@@ -213,7 +213,8 @@ public class AggregateStarTableRule extends RelOptRule {
       if (roll == null) {
         break tryRoll;
       }
-      return AggregateCall.create(roll, false, ImmutableList.of(offset + i), -1,
+      return AggregateCall.create(roll, false,
+          aggregateCall.isApproximate(), ImmutableList.of(offset + i), -1,
           groupCount, relBuilder.peek(), null, aggregateCall.name);
     }
 
@@ -228,7 +229,8 @@ public class AggregateStarTableRule extends RelOptRule {
         }
         newArgs.add(z);
       }
-      return AggregateCall.create(aggregation, false, newArgs, -1,
+      return AggregateCall.create(aggregation, false,
+          aggregateCall.isApproximate(), newArgs, -1,
           groupCount, relBuilder.peek(), null, aggregateCall.name);
     }
 

http://git-wip-us.apache.org/repos/asf/calcite/blob/fe3529d9/core/src/main/java/org/apache/calcite/rel/rules/AggregateUnionTransposeRule.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/rules/AggregateUnionTransposeRule.java b/core/src/main/java/org/apache/calcite/rel/rules/AggregateUnionTransposeRule.java
index 0e07256..abc5fbe 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/AggregateUnionTransposeRule.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/AggregateUnionTransposeRule.java
@@ -170,6 +170,7 @@ public class AggregateUnionTransposeRule extends RelOptRule {
       }
       AggregateCall newCall =
           AggregateCall.create(aggFun, origCall.isDistinct(),
+              origCall.isApproximate(),
               ImmutableList.of(groupCount + ord.i), -1, groupCount, input,
               aggType, origCall.getName());
       newCalls.add(newCall);

http://git-wip-us.apache.org/repos/asf/calcite/blob/fe3529d9/core/src/main/java/org/apache/calcite/rel/rules/SubQueryRemoveRule.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/rules/SubQueryRemoveRule.java b/core/src/main/java/org/apache/calcite/rel/rules/SubQueryRemoveRule.java
index 1961999..3bed2b3 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/SubQueryRemoveRule.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/SubQueryRemoveRule.java
@@ -165,8 +165,8 @@ public abstract class SubQueryRemoveRule extends RelOptRule {
           ImmutableBitSet.of());
       if (unique == null || !unique) {
         builder.aggregate(builder.groupKey(),
-            builder.aggregateCall(SqlStdOperatorTable.SINGLE_VALUE, false, null,
-                null, builder.field(0)));
+            builder.aggregateCall(SqlStdOperatorTable.SINGLE_VALUE, false,
+                false, null, null, builder.field(0)));
       }
       builder.join(JoinRelType.LEFT, builder.literal(true), variablesSet);
       return field(builder, inputCount, offset);
@@ -292,8 +292,8 @@ public abstract class SubQueryRemoveRule extends RelOptRule {
         }
         builder.aggregate(builder.groupKey(),
             builder.count(false, "c"),
-            builder.aggregateCall(SqlStdOperatorTable.COUNT, false, null, "ck",
-                builder.fields()));
+            builder.aggregateCall(SqlStdOperatorTable.COUNT, false, false, null,
+                "ck", builder.fields()));
         builder.as("ct");
         if (!variablesSet.isEmpty()) {
           builder.join(JoinRelType.LEFT, builder.literal(true), variablesSet);

http://git-wip-us.apache.org/repos/asf/calcite/blob/fe3529d9/core/src/main/java/org/apache/calcite/sql/SqlSplittableAggFunction.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/SqlSplittableAggFunction.java b/core/src/main/java/org/apache/calcite/sql/SqlSplittableAggFunction.java
index d776847..6e8e4fe 100644
--- a/core/src/main/java/org/apache/calcite/sql/SqlSplittableAggFunction.java
+++ b/core/src/main/java/org/apache/calcite/sql/SqlSplittableAggFunction.java
@@ -117,7 +117,7 @@ public interface SqlSplittableAggFunction {
     }
 
     public AggregateCall other(RelDataTypeFactory typeFactory, AggregateCall e) {
-      return AggregateCall.create(SqlStdOperatorTable.COUNT, false,
+      return AggregateCall.create(SqlStdOperatorTable.COUNT, false, false,
           ImmutableIntList.of(), -1,
           typeFactory.createSqlType(SqlTypeName.BIGINT), null);
     }
@@ -146,7 +146,7 @@ public interface SqlSplittableAggFunction {
         throw new AssertionError("unexpected count " + merges);
       }
       int ordinal = extra.register(node);
-      return AggregateCall.create(SqlStdOperatorTable.SUM0, false,
+      return AggregateCall.create(SqlStdOperatorTable.SUM0, false, false,
           ImmutableList.of(ordinal), -1, aggregateCall.type,
           aggregateCall.name);
     }
@@ -229,7 +229,7 @@ public interface SqlSplittableAggFunction {
     }
 
     public AggregateCall other(RelDataTypeFactory typeFactory, AggregateCall e) {
-      return AggregateCall.create(SqlStdOperatorTable.COUNT, false,
+      return AggregateCall.create(SqlStdOperatorTable.COUNT, false, false,
           ImmutableIntList.of(), -1,
           typeFactory.createSqlType(SqlTypeName.BIGINT), null);
     }
@@ -260,7 +260,7 @@ public interface SqlSplittableAggFunction {
         throw new AssertionError("unexpected count " + merges);
       }
       int ordinal = extra.register(node);
-      return AggregateCall.create(SqlStdOperatorTable.SUM, false,
+      return AggregateCall.create(SqlStdOperatorTable.SUM, false, false,
           ImmutableList.of(ordinal), -1, aggregateCall.type,
           aggregateCall.name);
     }

http://git-wip-us.apache.org/repos/asf/calcite/blob/fe3529d9/core/src/main/java/org/apache/calcite/sql/fun/SqlCountAggFunction.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/fun/SqlCountAggFunction.java b/core/src/main/java/org/apache/calcite/sql/fun/SqlCountAggFunction.java
index ebfa084..e053294 100644
--- a/core/src/main/java/org/apache/calcite/sql/fun/SqlCountAggFunction.java
+++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlCountAggFunction.java
@@ -44,19 +44,12 @@ import java.util.List;
 public class SqlCountAggFunction extends SqlAggFunction {
   //~ Constructors -----------------------------------------------------------
 
-  public SqlCountAggFunction() {
-    super(
-        "COUNT",
-        null,
-        SqlKind.COUNT,
-        ReturnTypes.BIGINT,
-        null,
+  public SqlCountAggFunction(String name) {
+    super(name, null, SqlKind.COUNT, ReturnTypes.BIGINT, null,
         SqlValidator.STRICT
             ? OperandTypes.ANY
             : OperandTypes.ONE_OR_MORE,
-        SqlFunctionCategory.NUMERIC,
-        false,
-        false);
+        SqlFunctionCategory.NUMERIC, false, false);
   }
 
   //~ Methods ----------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/calcite/blob/fe3529d9/core/src/main/java/org/apache/calcite/sql/fun/SqlStdOperatorTable.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/fun/SqlStdOperatorTable.java b/core/src/main/java/org/apache/calcite/sql/fun/SqlStdOperatorTable.java
index 9b5273a..f6a762b 100644
--- a/core/src/main/java/org/apache/calcite/sql/fun/SqlStdOperatorTable.java
+++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlStdOperatorTable.java
@@ -828,7 +828,13 @@ public class SqlStdOperatorTable extends ReflectiveSqlOperatorTable {
   /**
    * <code>COUNT</code> aggregate function.
    */
-  public static final SqlAggFunction COUNT = new SqlCountAggFunction();
+  public static final SqlAggFunction COUNT = new SqlCountAggFunction("COUNT");
+
+  /**
+   * <code>APPROX_COUNT_DISTINCT</code> aggregate function.
+   */
+  public static final SqlAggFunction APPROX_COUNT_DISTINCT =
+      new SqlCountAggFunction("APPROX_COUNT_DISTINCT");
 
   /**
    * <code>MIN</code> aggregate function.

http://git-wip-us.apache.org/repos/asf/calcite/blob/fe3529d9/core/src/main/java/org/apache/calcite/sql2rel/RelFieldTrimmer.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql2rel/RelFieldTrimmer.java b/core/src/main/java/org/apache/calcite/sql2rel/RelFieldTrimmer.java
index 0b7af6f..2d7ec59 100644
--- a/core/src/main/java/org/apache/calcite/sql2rel/RelFieldTrimmer.java
+++ b/core/src/main/java/org/apache/calcite/sql2rel/RelFieldTrimmer.java
@@ -866,7 +866,8 @@ public class RelFieldTrimmer implements ReflectiveVisitor {
             : relBuilder.field(Mappings.apply(inputMapping, aggCall.filterArg));
         RelBuilder.AggCall newAggCall =
             relBuilder.aggregateCall(aggCall.getAggregation(),
-                aggCall.isDistinct(), filterArg, aggCall.name, args);
+                aggCall.isDistinct(), aggCall.isApproximate(),
+                filterArg, aggCall.name, args);
         mapping.set(j, groupCount + indicatorCount + newAggCallList.size());
         newAggCallList.add(newAggCall);
       }

http://git-wip-us.apache.org/repos/asf/calcite/blob/fe3529d9/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java b/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java
index 752743b..bbe7b68 100644
--- a/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java
+++ b/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java
@@ -1101,9 +1101,9 @@ public class SqlToRelConverter {
             LogicalAggregate.create(seek, ImmutableBitSet.of(), null,
                 ImmutableList.of(
                     AggregateCall.create(SqlStdOperatorTable.COUNT, false,
-                        ImmutableList.<Integer>of(), -1, longType, null),
+                        false, ImmutableList.<Integer>of(), -1, longType, null),
                     AggregateCall.create(SqlStdOperatorTable.COUNT, false,
-                        args, -1, longType, null)));
+                        false, args, -1, longType, null)));
         LogicalJoin join =
             LogicalJoin.create(bb.root, aggregate, rexBuilder.makeLiteral(true),
                 ImmutableSet.<CorrelationId>of(), JoinRelType.INNER);
@@ -4879,19 +4879,26 @@ public class SqlToRelConverter {
         bb.agg = this;
       }
 
-      final SqlAggFunction aggFunction =
+      SqlAggFunction aggFunction =
           (SqlAggFunction) call.getOperator();
-      RelDataType type = validator.deriveType(bb.scope, call);
+      final RelDataType type = validator.deriveType(bb.scope, call);
       boolean distinct = false;
       SqlLiteral quantifier = call.getFunctionQuantifier();
       if ((null != quantifier)
           && (quantifier.getValue() == SqlSelectKeyword.DISTINCT)) {
         distinct = true;
       }
+      boolean approximate = false;
+      if (aggFunction == SqlStdOperatorTable.APPROX_COUNT_DISTINCT) {
+        aggFunction = SqlStdOperatorTable.COUNT;
+        distinct = true;
+        approximate = true;
+      }
       final AggregateCall aggCall =
           AggregateCall.create(
               aggFunction,
               distinct,
+              approximate,
               args,
               filterArg,
               type,

http://git-wip-us.apache.org/repos/asf/calcite/blob/fe3529d9/core/src/main/java/org/apache/calcite/tools/PigRelBuilder.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/tools/PigRelBuilder.java b/core/src/main/java/org/apache/calcite/tools/PigRelBuilder.java
index 4451f5c..dcac18b 100644
--- a/core/src/main/java/org/apache/calcite/tools/PigRelBuilder.java
+++ b/core/src/main/java/org/apache/calcite/tools/PigRelBuilder.java
@@ -146,7 +146,8 @@ public class PigRelBuilder extends RelBuilder {
           cluster.getRexBuilder().makeCall(peek(1, 0).getRowType(),
               SqlStdOperatorTable.ROW, fields());
       aggregate(groupKey.e,
-          aggregateCall(SqlStdOperatorTable.COLLECT, false, null, getAlias(), row));
+          aggregateCall(SqlStdOperatorTable.COLLECT, false, false, null,
+              getAlias(), row));
       if (groupKey.i < n - 1) {
         push(r);
         List<RexNode> predicates = new ArrayList<>();

http://git-wip-us.apache.org/repos/asf/calcite/blob/fe3529d9/core/src/main/java/org/apache/calcite/tools/RelBuilder.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/tools/RelBuilder.java b/core/src/main/java/org/apache/calcite/tools/RelBuilder.java
index c131f8e..0b871f9 100644
--- a/core/src/main/java/org/apache/calcite/tools/RelBuilder.java
+++ b/core/src/main/java/org/apache/calcite/tools/RelBuilder.java
@@ -727,16 +727,30 @@ public class RelBuilder {
     return groupKey(nodes, indicator, nodeLists);
   }
 
-  /** Creates a call to an aggregate function. */
+  @Deprecated // to be removed before 2.0
   public AggCall aggregateCall(SqlAggFunction aggFunction, boolean distinct,
       RexNode filter, String alias, RexNode... operands) {
-    return aggregateCall(aggFunction, distinct, filter, alias,
+    return aggregateCall(aggFunction, distinct, false, filter, alias,
         ImmutableList.copyOf(operands));
   }
 
   /** Creates a call to an aggregate function. */
   public AggCall aggregateCall(SqlAggFunction aggFunction, boolean distinct,
+      boolean approximate, RexNode filter, String alias, RexNode... operands) {
+    return aggregateCall(aggFunction, distinct, approximate, filter, alias,
+        ImmutableList.copyOf(operands));
+  }
+
+  @Deprecated // to be removed before 2.0
+  public AggCall aggregateCall(SqlAggFunction aggFunction, boolean distinct,
       RexNode filter, String alias, Iterable<? extends RexNode> operands) {
+    return aggregateCall(aggFunction, distinct, false, filter, alias, operands);
+  }
+
+  /** Creates a call to an aggregate function. */
+  public AggCall aggregateCall(SqlAggFunction aggFunction, boolean distinct,
+      boolean approximate, RexNode filter, String alias,
+      Iterable<? extends RexNode> operands) {
     if (filter != null) {
       if (filter.getType().getSqlTypeName() != SqlTypeName.BOOLEAN) {
         throw RESOURCE.filterMustBeBoolean().ex();
@@ -745,41 +759,43 @@ public class RelBuilder {
         filter = call(SqlStdOperatorTable.IS_TRUE, filter);
       }
     }
-    return new AggCallImpl(aggFunction, distinct, filter, alias,
+    return new AggCallImpl(aggFunction, distinct, approximate, filter, alias,
         ImmutableList.copyOf(operands));
   }
 
   /** Creates a call to the COUNT aggregate function. */
   public AggCall count(boolean distinct, String alias, RexNode... operands) {
-    return aggregateCall(SqlStdOperatorTable.COUNT, distinct, null, alias,
-        operands);
+    return aggregateCall(SqlStdOperatorTable.COUNT, distinct, false, null,
+        alias, operands);
   }
 
   /** Creates a call to the COUNT(*) aggregate function. */
   public AggCall countStar(String alias) {
-    return aggregateCall(SqlStdOperatorTable.COUNT, false, null, alias);
+    return aggregateCall(SqlStdOperatorTable.COUNT, false, false, null, alias);
   }
 
   /** Creates a call to the SUM aggregate function. */
   public AggCall sum(boolean distinct, String alias, RexNode operand) {
-    return aggregateCall(SqlStdOperatorTable.SUM, distinct, null, alias,
+    return aggregateCall(SqlStdOperatorTable.SUM, distinct, false, null, alias,
         operand);
   }
 
   /** Creates a call to the AVG aggregate function. */
   public AggCall avg(boolean distinct, String alias, RexNode operand) {
-    return aggregateCall(
-        SqlStdOperatorTable.AVG, distinct, null, alias, operand);
+    return aggregateCall(SqlStdOperatorTable.AVG, distinct, false, null, alias,
+        operand);
   }
 
   /** Creates a call to the MIN aggregate function. */
   public AggCall min(String alias, RexNode operand) {
-    return aggregateCall(SqlStdOperatorTable.MIN, false, null, alias, operand);
+    return aggregateCall(SqlStdOperatorTable.MIN, false, false, null, alias,
+        operand);
   }
 
   /** Creates a call to the MAX aggregate function. */
   public AggCall max(String alias, RexNode operand) {
-    return aggregateCall(SqlStdOperatorTable.MAX, false, null, alias, operand);
+    return aggregateCall(SqlStdOperatorTable.MAX, false, false, null, alias,
+        operand);
   }
 
   // Methods for patterns
@@ -1235,7 +1251,8 @@ public class RelBuilder {
           throw new IllegalArgumentException("FILTER not allowed");
         }
         aggregateCall =
-            AggregateCall.create(aggCall1.aggFunction, aggCall1.distinct, args,
+            AggregateCall.create(aggCall1.aggFunction, aggCall1.distinct,
+                aggCall1.approximate, args,
                 filterArg, groupSet.cardinality(), r, null, aggCall1.alias);
       } else {
         aggregateCall = ((AggCallImpl2) aggCall).aggregateCall;
@@ -1933,14 +1950,17 @@ public class RelBuilder {
   private static class AggCallImpl implements AggCall {
     private final SqlAggFunction aggFunction;
     private final boolean distinct;
+    private final boolean approximate;
     private final RexNode filter;
     private final String alias;
     private final ImmutableList<RexNode> operands;
 
-    AggCallImpl(SqlAggFunction aggFunction, boolean distinct, RexNode filter,
+    AggCallImpl(SqlAggFunction aggFunction, boolean distinct,
+        boolean approximate, RexNode filter,
         String alias, ImmutableList<RexNode> operands) {
       this.aggFunction = aggFunction;
       this.distinct = distinct;
+      this.approximate = approximate;
       this.filter = filter;
       this.alias = alias;
       this.operands = operands;

http://git-wip-us.apache.org/repos/asf/calcite/blob/fe3529d9/core/src/test/java/org/apache/calcite/plan/RelWriterTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/plan/RelWriterTest.java b/core/src/test/java/org/apache/calcite/plan/RelWriterTest.java
index 9749b6d..4f4829b 100644
--- a/core/src/test/java/org/apache/calcite/plan/RelWriterTest.java
+++ b/core/src/test/java/org/apache/calcite/plan/RelWriterTest.java
@@ -141,10 +141,10 @@ public class RelWriterTest {
                     LogicalAggregate.create(filter, ImmutableBitSet.of(0), null,
                         ImmutableList.of(
                             AggregateCall.create(SqlStdOperatorTable.COUNT,
-                                true, ImmutableList.of(1), -1, bigIntType,
-                                "c"),
+                                true, false, ImmutableList.of(1), -1,
+                                bigIntType, "c"),
                             AggregateCall.create(SqlStdOperatorTable.COUNT,
-                                false, ImmutableList.<Integer>of(), -1,
+                                false, false, ImmutableList.<Integer>of(), -1,
                                 bigIntType, "d")));
                 aggregate.explain(writer);
                 return writer.asString();

http://git-wip-us.apache.org/repos/asf/calcite/blob/fe3529d9/core/src/test/java/org/apache/calcite/plan/volcano/TraitPropagationTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/plan/volcano/TraitPropagationTest.java b/core/src/test/java/org/apache/calcite/plan/volcano/TraitPropagationTest.java
index 74e675c..8847f93 100644
--- a/core/src/test/java/org/apache/calcite/plan/volcano/TraitPropagationTest.java
+++ b/core/src/test/java/org/apache/calcite/plan/volcano/TraitPropagationTest.java
@@ -166,7 +166,7 @@ public class TraitPropagationTest {
 
       // aggregate on s, count
       AggregateCall aggCall = AggregateCall.create(SqlStdOperatorTable.COUNT,
-          false, Collections.singletonList(1), -1, sqlBigInt, "cnt");
+          false, false, Collections.singletonList(1), -1, sqlBigInt, "cnt");
       RelNode agg = new LogicalAggregate(cluster,
           cluster.traitSetOf(Convention.NONE), project, false,
           ImmutableBitSet.of(0), null, Collections.singletonList(aggCall));

http://git-wip-us.apache.org/repos/asf/calcite/blob/fe3529d9/core/src/test/java/org/apache/calcite/sql/test/SqlOperatorBaseTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/sql/test/SqlOperatorBaseTest.java b/core/src/test/java/org/apache/calcite/sql/test/SqlOperatorBaseTest.java
index 7c0be3f..8bd922a 100644
--- a/core/src/test/java/org/apache/calcite/sql/test/SqlOperatorBaseTest.java
+++ b/core/src/test/java/org/apache/calcite/sql/test/SqlOperatorBaseTest.java
@@ -6203,6 +6203,51 @@ public abstract class SqlOperatorBaseTest {
     tester.checkAgg("COUNT(DISTINCT 123)", stringValues, 1, (double) 0);
   }
 
+  @Test public void testApproxCountDistinctFunc() {
+    tester.setFor(SqlStdOperatorTable.COUNT, VM_EXPAND);
+    tester.checkFails("approx_count_distinct(^*^)", "Unknown identifier '\\*'",
+        false);
+    tester.checkType("approx_count_distinct('name')", "BIGINT NOT NULL");
+    tester.checkType("approx_count_distinct(1)", "BIGINT NOT NULL");
+    tester.checkType("approx_count_distinct(1.2)", "BIGINT NOT NULL");
+    tester.checkType("APPROX_COUNT_DISTINCT(DISTINCT 'x')", "BIGINT NOT NULL");
+    tester.checkFails("^APPROX_COUNT_DISTINCT()^",
+        "Invalid number of arguments to function 'APPROX_COUNT_DISTINCT'. "
+            + "Was expecting 1 arguments",
+        false);
+    tester.checkType("approx_count_distinct(1, 2)", "BIGINT NOT NULL");
+    tester.checkType("approx_count_distinct(1, 2, 'x', 'y')",
+        "BIGINT NOT NULL");
+    final String[] values = {"0", "CAST(null AS INTEGER)", "1", "0"};
+    // currently APPROX_COUNT_DISTINCT(x) returns the same as COUNT(DISTINCT x)
+    tester.checkAgg(
+        "APPROX_COUNT_DISTINCT(x)",
+        values,
+        2,
+        (double) 0);
+    tester.checkAgg(
+        "APPROX_COUNT_DISTINCT(CASE x WHEN 0 THEN NULL ELSE -1 END)",
+        values,
+        1,
+        (double) 0);
+    // DISTINCT keyword is allowed but has no effect
+    tester.checkAgg(
+        "APPROX_COUNT_DISTINCT(DISTINCT x)",
+        values,
+        2,
+        (double) 0);
+
+    // string values -- note that empty string is not null
+    final String[] stringValues = {
+        "'a'", "CAST(NULL AS VARCHAR(1))", "''"
+    };
+    tester.checkAgg("APPROX_COUNT_DISTINCT(x)", stringValues, 2, (double) 0);
+    tester.checkAgg("APPROX_COUNT_DISTINCT(DISTINCT x)", stringValues, 2,
+        (double) 0);
+    tester.checkAgg("APPROX_COUNT_DISTINCT(DISTINCT 123)", stringValues, 1,
+        (double) 0);
+  }
+
   @Test public void testSumFunc() {
     tester.setFor(SqlStdOperatorTable.SUM, VM_EXPAND);
     tester.checkFails(

http://git-wip-us.apache.org/repos/asf/calcite/blob/fe3529d9/core/src/test/java/org/apache/calcite/test/RelBuilderTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/test/RelBuilderTest.java b/core/src/test/java/org/apache/calcite/test/RelBuilderTest.java
index a17a154..4858841 100644
--- a/core/src/test/java/org/apache/calcite/test/RelBuilderTest.java
+++ b/core/src/test/java/org/apache/calcite/test/RelBuilderTest.java
@@ -467,7 +467,7 @@ public class RelBuilderTest {
                     builder.literal(20)))
             .aggregate(builder.groupKey(0, 1, 2),
                 builder.aggregateCall(SqlStdOperatorTable.SUM,
-                    false, null, null,
+                    false, false, null, null,
                     builder.field(0)))
             .project(builder.field("c"),
                 builder.field("a"))
@@ -623,8 +623,8 @@ public class RelBuilderTest {
     RelNode root =
         builder.scan("EMP")
             .aggregate(builder.groupKey(),
-                builder.aggregateCall(SqlStdOperatorTable.COUNT, true, null,
-                    "C", builder.field("DEPTNO")))
+                builder.aggregateCall(SqlStdOperatorTable.COUNT, true, false,
+                    null, "C", builder.field("DEPTNO")))
             .build();
     assertThat(str(root),
         is("LogicalAggregate(group=[{}], C=[COUNT(DISTINCT $7)])\n"
@@ -645,9 +645,10 @@ public class RelBuilderTest {
                         builder.field(4),
                         builder.field(3)),
                     builder.field(1)),
-                builder.aggregateCall(SqlStdOperatorTable.COUNT, false, null,
-                    "C"),
-                builder.aggregateCall(SqlStdOperatorTable.SUM, false, null, "S",
+                builder.aggregateCall(SqlStdOperatorTable.COUNT, false, false,
+                    null, "C"),
+                builder.aggregateCall(SqlStdOperatorTable.SUM, false, false,
+                    null, "S",
                     builder.call(SqlStdOperatorTable.PLUS, builder.field(3),
                         builder.literal(1))))
             .build();
@@ -670,7 +671,7 @@ public class RelBuilderTest {
                 builder.groupKey(ImmutableBitSet.of(7),
                     ImmutableList.of(ImmutableBitSet.of(7),
                         ImmutableBitSet.of())),
-                builder.aggregateCall(SqlStdOperatorTable.COUNT, false,
+                builder.aggregateCall(SqlStdOperatorTable.COUNT, false, false,
                     builder.call(SqlStdOperatorTable.GREATER_THAN,
                         builder.field("EMPNO"), builder.literal(100)), "C"))
             .build();
@@ -692,7 +693,7 @@ public class RelBuilderTest {
           builder.scan("EMP")
               .aggregate(
                   builder.groupKey(builder.field("DEPTNO")),
-                  builder.aggregateCall(SqlStdOperatorTable.SUM, false,
+                  builder.aggregateCall(SqlStdOperatorTable.SUM, false, false,
                       builder.field("COMM"), "C", builder.field("SAL")))
               .build();
       fail("expected error, got " + root);
@@ -712,7 +713,7 @@ public class RelBuilderTest {
         builder.scan("EMP")
             .aggregate(
                 builder.groupKey(builder.field("DEPTNO")),
-                builder.aggregateCall(SqlStdOperatorTable.SUM, false,
+                builder.aggregateCall(SqlStdOperatorTable.SUM, false, false,
                     builder.call(SqlStdOperatorTable.LESS_THAN,
                         builder.field("COMM"), builder.literal(100)), "C",
                     builder.field("SAL")))
@@ -819,8 +820,8 @@ public class RelBuilderTest {
     RelNode root =
         builder.scan("EMP")
             .aggregate(builder.groupKey(6, 7),
-                builder.aggregateCall(SqlStdOperatorTable.GROUPING, false, null,
-                    "g", builder.field("DEPTNO")))
+                builder.aggregateCall(SqlStdOperatorTable.GROUPING, false,
+                    false, null, "g", builder.field("DEPTNO")))
             .build();
     final String expected = ""
         + "LogicalAggregate(group=[{6, 7}], g=[GROUPING($7)])\n"
@@ -834,8 +835,8 @@ public class RelBuilderTest {
       RelNode root =
           builder.scan("EMP")
               .aggregate(builder.groupKey(6, 7),
-                  builder.aggregateCall(SqlStdOperatorTable.GROUPING, true, null,
-                      "g", builder.field("DEPTNO")))
+                  builder.aggregateCall(SqlStdOperatorTable.GROUPING, true,
+                      false, null, "g", builder.field("DEPTNO")))
               .build();
       fail("expected error, got " + root);
     } catch (IllegalArgumentException e) {
@@ -850,7 +851,8 @@ public class RelBuilderTest {
           builder.scan("EMP")
               .aggregate(builder.groupKey(6, 7),
                   builder.aggregateCall(SqlStdOperatorTable.GROUPING, false,
-                      builder.literal(true), "g", builder.field("DEPTNO")))
+                      false, builder.literal(true), "g",
+                      builder.field("DEPTNO")))
               .build();
       fail("expected error, got " + root);
     } catch (IllegalArgumentException e) {
@@ -1339,8 +1341,8 @@ public class RelBuilderTest {
             .project(builder.field("DEPTNO"),
                 builder.literal(20))
             .aggregate(builder.groupKey(builder.field("EMP_alias", "DEPTNO")),
-                builder.aggregateCall(SqlStdOperatorTable.SUM, false, null,
-                    null, builder.field(1)))
+                builder.aggregateCall(SqlStdOperatorTable.SUM, false, false,
+                    null, null, builder.field(1)))
             .project(builder.alias(builder.field(1), "sum"),
                 builder.field("EMP_alias", "DEPTNO"))
             .build();

http://git-wip-us.apache.org/repos/asf/calcite/blob/fe3529d9/core/src/test/java/org/apache/calcite/test/RelMetadataTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/test/RelMetadataTest.java b/core/src/test/java/org/apache/calcite/test/RelMetadataTest.java
index f2bed82..a09de16 100644
--- a/core/src/test/java/org/apache/calcite/test/RelMetadataTest.java
+++ b/core/src/test/java/org/apache/calcite/test/RelMetadataTest.java
@@ -1283,8 +1283,8 @@ public class RelMetadataTest extends SqlToRelTestBase {
         LogicalAggregate.create(join, ImmutableBitSet.of(2, 0),
             ImmutableList.<ImmutableBitSet>of(),
             ImmutableList.of(
-                AggregateCall.create(
-                    SqlStdOperatorTable.COUNT, false, ImmutableIntList.of(),
+                AggregateCall.create(SqlStdOperatorTable.COUNT,
+                    false, false, ImmutableIntList.of(),
                     -1, 2, join, null, null)));
     rowSize = mq.getAverageRowSize(aggregate);
     columnSizes = mq.getAverageColumnSizes(aggregate);

http://git-wip-us.apache.org/repos/asf/calcite/blob/fe3529d9/druid/src/main/java/org/apache/calcite/adapter/druid/DruidQuery.java
----------------------------------------------------------------------
diff --git a/druid/src/main/java/org/apache/calcite/adapter/druid/DruidQuery.java b/druid/src/main/java/org/apache/calcite/adapter/druid/DruidQuery.java
index 012d272..4cfee48 100644
--- a/druid/src/main/java/org/apache/calcite/adapter/druid/DruidQuery.java
+++ b/druid/src/main/java/org/apache/calcite/adapter/druid/DruidQuery.java
@@ -860,7 +860,7 @@ public class DruidQuery extends AbstractRelNode implements BindableRel {
     switch (aggCall.getAggregation().getKind()) {
     case COUNT:
       if (aggCall.isDistinct()) {
-        if (config.approximateDistinctCount()) {
+        if (aggCall.isApproximate() || config.approximateDistinctCount()) {
           if (complexMetric == null) {
             aggregation = new JsonCardinalityAggregation("cardinality", name, list);
           } else {

http://git-wip-us.apache.org/repos/asf/calcite/blob/fe3529d9/druid/src/main/java/org/apache/calcite/adapter/druid/DruidRules.java
----------------------------------------------------------------------
diff --git a/druid/src/main/java/org/apache/calcite/adapter/druid/DruidRules.java b/druid/src/main/java/org/apache/calcite/adapter/druid/DruidRules.java
index 0f4d8b7..baf5e41 100644
--- a/druid/src/main/java/org/apache/calcite/adapter/druid/DruidRules.java
+++ b/druid/src/main/java/org/apache/calcite/adapter/druid/DruidRules.java
@@ -142,8 +142,10 @@ public class DruidRules {
                       node, query)) {
                 return true;
               }
-              if ((config.approximateDistinctCount() && aggregateCall.isDistinct())
-                      || aggregateCall.getArgList().isEmpty()) {
+              if ((aggregateCall.isDistinct()
+                      && (aggregateCall.isApproximate()
+                          || config.approximateDistinctCount()))
+                  || aggregateCall.getArgList().isEmpty()) {
                 continue;
               }
               return true;

http://git-wip-us.apache.org/repos/asf/calcite/blob/fe3529d9/druid/src/main/java/org/apache/calcite/adapter/druid/DruidTable.java
----------------------------------------------------------------------
diff --git a/druid/src/main/java/org/apache/calcite/adapter/druid/DruidTable.java b/druid/src/main/java/org/apache/calcite/adapter/druid/DruidTable.java
index 1993789..3789a36 100644
--- a/druid/src/main/java/org/apache/calcite/adapter/druid/DruidTable.java
+++ b/druid/src/main/java/org/apache/calcite/adapter/druid/DruidTable.java
@@ -34,6 +34,7 @@ import org.apache.calcite.sql.SqlCall;
 import org.apache.calcite.sql.SqlKind;
 import org.apache.calcite.sql.SqlNode;
 import org.apache.calcite.sql.SqlSelectKeyword;
+import org.apache.calcite.sql.fun.SqlStdOperatorTable;
 import org.apache.calcite.sql.type.SqlTypeName;
 
 import com.google.common.base.Preconditions;
@@ -178,11 +179,12 @@ public class DruidTable extends AbstractTable implements TranslatableTable {
     assert isRolledUp(column);
     // Our rolled up columns are only allowed in COUNT(DISTINCT ...) aggregate functions.
     // We only allow this when approximate results are acceptable.
-    return config != null
-            && config.approximateDistinctCount()
-            && isCountDistinct(call)
-            && call.getOperandList().size() == 1 // for COUNT(a_1, a_2, ... a_n). n should be 1
-            && isValidParentKind(parent);
+    return ((config != null
+                && config.approximateDistinctCount()
+                && isCountDistinct(call))
+            || call.getOperator() == SqlStdOperatorTable.APPROX_COUNT_DISTINCT)
+        && call.getOperandList().size() == 1 // for COUNT(a_1, a_2, ... a_n). n should be 1
+        && isValidParentKind(parent);
   }
 
   private boolean isValidParentKind(SqlNode node) {

http://git-wip-us.apache.org/repos/asf/calcite/blob/fe3529d9/druid/src/test/java/org/apache/calcite/test/DruidAdapterIT.java
----------------------------------------------------------------------
diff --git a/druid/src/test/java/org/apache/calcite/test/DruidAdapterIT.java b/druid/src/test/java/org/apache/calcite/test/DruidAdapterIT.java
index 36c866e..30e1438 100644
--- a/druid/src/test/java/org/apache/calcite/test/DruidAdapterIT.java
+++ b/druid/src/test/java/org/apache/calcite/test/DruidAdapterIT.java
@@ -3137,19 +3137,28 @@ public class DruidAdapterIT {
                     "month=November; avg$=34.51727744987063",
                     "month=December; avg$=33.62788702774498");
 
-    wikiApprox("select (count(distinct \"user_id\") + 100) - "
-            + "(count(distinct \"user_id\") * 2) from \"wiki\"")
-            .queryContains(
-                    druidChecker("'aggregations':[{'type':'hyperUnique','name':'$f0',"
-                            + "'fieldName':'user_unique'}],'postAggregations':[{'type':"
-                            + "'arithmetic','name':'postagg#0','fn':'-','fields':[{'type':"
-                            + "'arithmetic','name':'','fn':'+','fields':[{'type':"
-                            + "'hyperUniqueCardinality','name':'','fieldName':'$f0'},"
-                            + "{'type':'constant','name':'','value':100.0}]},{'type':"
-                            + "'arithmetic','name':'','fn':'*','fields':[{'type':"
-                            + "'hyperUniqueCardinality','name':'','fieldName':'$f0'},"
-                            + "{'type':'constant','name':'','value':2.0}]}]}]"))
-            .returnsUnordered("EXPR$0=-10590");
+    final String druid = "'aggregations':[{'type':'hyperUnique','name':'$f0',"
+        + "'fieldName':'user_unique'}],'postAggregations':[{'type':"
+        + "'arithmetic','name':'postagg#0','fn':'-','fields':[{'type':"
+        + "'arithmetic','name':'','fn':'+','fields':[{'type':"
+        + "'hyperUniqueCardinality','name':'','fieldName':'$f0'},"
+        + "{'type':'constant','name':'','value':100.0}]},{'type':"
+        + "'arithmetic','name':'','fn':'*','fields':[{'type':"
+        + "'hyperUniqueCardinality','name':'','fieldName':'$f0'},"
+        + "{'type':'constant','name':'','value':2.0}]}]}]";
+    final String sql = "select (count(distinct \"user_id\") + 100) - "
+        + "(count(distinct \"user_id\") * 2) from \"wiki\"";
+    wikiApprox(sql)
+        .queryContains(druidChecker(druid))
+        .returnsUnordered("EXPR$0=-10590");
+
+    // Change COUNT(DISTINCT ...) to APPROX_COUNT_DISTINCT(...) and get
+    // same result even if approximation is off by default.
+    final String sql2 = "select (approx_count_distinct(\"user_id\") + 100) - "
+        + "(approx_count_distinct(\"user_id\") * 2) from \"wiki\"";
+    sql(sql2, WIKI)
+        .queryContains(druidChecker(druid))
+        .returnsUnordered("EXPR$0=-10590");
   }
 
   /**

http://git-wip-us.apache.org/repos/asf/calcite/blob/fe3529d9/site/_docs/algebra.md
----------------------------------------------------------------------
diff --git a/site/_docs/algebra.md b/site/_docs/algebra.md
index 527b2e1..920e71a 100644
--- a/site/_docs/algebra.md
+++ b/site/_docs/algebra.md
@@ -389,7 +389,7 @@ The following methods return an
 
 | Method              | Description
 |:------------------- |:-----------
-| `aggregateCall(op, distinct, filter, alias, expr...)`<br/>`aggregateCall(op, distinct, filter, alias, exprList)` | Creates a call to a given aggregate function, with an optional filter expression
+| `aggregateCall(op, distinct, approximate, filter, alias, expr...)`<br/>`aggregateCall(op, distinct, approximate, filter, alias, exprList)` | Creates a call to a given aggregate function, with an optional filter expression
 | `count(distinct, alias, expr...)` | Creates a call to the COUNT aggregate function
 | `countStar(alias)` | Creates a call to the COUNT(*) aggregate function
 | `sum(distinct, alias, expr)` | Creates a call to the SUM aggregate function

http://git-wip-us.apache.org/repos/asf/calcite/blob/fe3529d9/site/_docs/reference.md
----------------------------------------------------------------------
diff --git a/site/_docs/reference.md b/site/_docs/reference.md
index 8f0b4e8..ea6e983 100644
--- a/site/_docs/reference.md
+++ b/site/_docs/reference.md
@@ -1480,6 +1480,7 @@ passed to the aggregate function.
 | COLLECT( [ ALL &#124; DISTINCT ] value)       | Returns a multiset of the values
 | COUNT( [ ALL &#124; DISTINCT ] value [, value ]*) | Returns the number of input rows for which *value* is not null (wholly not null if *value* is composite)
 | COUNT(*)                           | Returns the number of input rows
+| APPROX_COUNT_DISTINCT(value [, value ]*)      | Returns the approximate number of distinct values of *value*; the database is allowed to use an approximation but is not required to
 | AVG( [ ALL &#124; DISTINCT ] numeric)         | Returns the average (arithmetic mean) of *numeric* across all input values
 | SUM( [ ALL &#124; DISTINCT ] numeric)         | Returns the sum of *numeric* across all input values
 | MAX( [ ALL &#124; DISTINCT ] value)           | Returns the maximum value of *value* across all input values
@@ -1507,7 +1508,7 @@ Not implemented:
 
 | Operator syntax                           | Description
 |:----------------------------------------- |:-----------
-| COUNT(value [, value ]*) OVER window     | Returns the number of rows in *window* for which *value* is not null (wholly not null if *value* is composite)
+| COUNT(value [, value ]*) OVER window      | Returns the number of rows in *window* for which *value* is not null (wholly not null if *value* is composite)
 | COUNT(*) OVER window                      | Returns the number of rows in *window*
 | AVG(numeric) OVER window                  | Returns the average (arithmetic mean) of *numeric* across all values in *window*
 | SUM(numeric) OVER window                  | Returns the sum of *numeric* across all values in *window*
@@ -1524,7 +1525,8 @@ Not implemented:
 
 Not implemented:
 
-* COUNT(DISTINCT value) OVER window
+* COUNT(DISTINCT value [, value ]*) OVER window
+* APPROX_COUNT_DISTINCT(value [, value ]*) OVER window
 * FIRST_VALUE(value) IGNORE NULLS OVER window
 * LAST_VALUE(value) IGNORE NULLS OVER window
 * PERCENT_RANK(value) OVER window


[3/3] calcite git commit: [CALCITE-1876] In CSV example, tweak cost to ensure that Project is pushed through Aggregate (Luis Fernando Kauer)

Posted by jh...@apache.org.
[CALCITE-1876] In CSV example, tweak cost to ensure that Project is pushed through Aggregate (Luis Fernando Kauer)

Solved by implementing CsvTableScan.computeSelfCost.

Smooth the function by adding 2 to top and bottom of fraction (Julian Hyde).

Close apache/calcite#562


Project: http://git-wip-us.apache.org/repos/asf/calcite/repo
Commit: http://git-wip-us.apache.org/repos/asf/calcite/commit/77a35491
Tree: http://git-wip-us.apache.org/repos/asf/calcite/tree/77a35491
Diff: http://git-wip-us.apache.org/repos/asf/calcite/diff/77a35491

Branch: refs/heads/master
Commit: 77a35491794724c32ad2fb7b3333744b84f1e18f
Parents: 5a0403c
Author: Luis Fernando Kauer <lf...@yahoo.com.br>
Authored: Tue Nov 7 17:29:26 2017 -0200
Committer: Julian Hyde <jh...@apache.org>
Committed: Wed Nov 8 08:41:33 2017 -0800

----------------------------------------------------------------------
 .../calcite/adapter/csv/CsvTableScan.java       | 16 +++++++++
 .../java/org/apache/calcite/test/CsvTest.java   | 35 ++++++++++++++++++++
 2 files changed, 51 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/calcite/blob/77a35491/example/csv/src/main/java/org/apache/calcite/adapter/csv/CsvTableScan.java
----------------------------------------------------------------------
diff --git a/example/csv/src/main/java/org/apache/calcite/adapter/csv/CsvTableScan.java b/example/csv/src/main/java/org/apache/calcite/adapter/csv/CsvTableScan.java
index a667a44..e55655c 100644
--- a/example/csv/src/main/java/org/apache/calcite/adapter/csv/CsvTableScan.java
+++ b/example/csv/src/main/java/org/apache/calcite/adapter/csv/CsvTableScan.java
@@ -25,12 +25,14 @@ import org.apache.calcite.linq4j.tree.Blocks;
 import org.apache.calcite.linq4j.tree.Expressions;
 import org.apache.calcite.linq4j.tree.Primitive;
 import org.apache.calcite.plan.RelOptCluster;
+import org.apache.calcite.plan.RelOptCost;
 import org.apache.calcite.plan.RelOptPlanner;
 import org.apache.calcite.plan.RelOptTable;
 import org.apache.calcite.plan.RelTraitSet;
 import org.apache.calcite.rel.RelNode;
 import org.apache.calcite.rel.RelWriter;
 import org.apache.calcite.rel.core.TableScan;
+import org.apache.calcite.rel.metadata.RelMetadataQuery;
 import org.apache.calcite.rel.type.RelDataType;
 import org.apache.calcite.rel.type.RelDataTypeFactory;
 import org.apache.calcite.rel.type.RelDataTypeField;
@@ -79,6 +81,20 @@ public class CsvTableScan extends TableScan implements EnumerableRel {
     planner.addRule(CsvProjectTableScanRule.INSTANCE);
   }
 
+  @Override public RelOptCost computeSelfCost(RelOptPlanner planner,
+      RelMetadataQuery mq) {
+    // Multiply the cost by a factor that makes a scan more attractive if it
+    // has significantly fewer fields than the original scan.
+    //
+    // The "+ 2D" on top and bottom keeps the function fairly smooth.
+    //
+    // For example, if table has 3 fields, project has 1 field,
+    // then factor = (1 + 2) / (3 + 2) = 0.6
+    return super.computeSelfCost(planner, mq)
+        .multiplyBy(((double) fields.length + 2D)
+            / ((double) table.getRowType().getFieldCount() + 2D));
+  }
+
   public Result implement(EnumerableRelImplementor implementor, Prefer pref) {
     PhysType physType =
         PhysTypeImpl.of(

http://git-wip-us.apache.org/repos/asf/calcite/blob/77a35491/example/csv/src/test/java/org/apache/calcite/test/CsvTest.java
----------------------------------------------------------------------
diff --git a/example/csv/src/test/java/org/apache/calcite/test/CsvTest.java b/example/csv/src/test/java/org/apache/calcite/test/CsvTest.java
index 785688b..af9d997 100644
--- a/example/csv/src/test/java/org/apache/calcite/test/CsvTest.java
+++ b/example/csv/src/test/java/org/apache/calcite/test/CsvTest.java
@@ -241,6 +241,41 @@ public class CsvTest {
         .ok();
   }
 
+  @Test public void testPushDownProjectAggregate() throws SQLException {
+    final String sql = "explain plan for\n"
+        + "select gender, count(*) from EMPS group by gender";
+    final String expected = "PLAN="
+        + "EnumerableAggregate(group=[{0}], EXPR$1=[COUNT()])\n"
+        + "  CsvTableScan(table=[[SALES, EMPS]], fields=[[3]])\n";
+    sql("smart", sql).returns(expected).ok();
+  }
+
+  @Test public void testPushDownProjectAggregateWithFilter() throws SQLException {
+    final String sql = "explain plan for\n"
+        + "select max(empno) from EMPS where gender='F'";
+    final String expected = "PLAN="
+        + "EnumerableAggregate(group=[{}], EXPR$0=[MAX($0)])\n"
+        + "  EnumerableCalc(expr#0..1=[{inputs}], expr#2=['F'], "
+        + "expr#3=[=($t1, $t2)], proj#0..1=[{exprs}], $condition=[$t3])\n"
+        + "    CsvTableScan(table=[[SALES, EMPS]], fields=[[0, 3]])\n";
+    sql("smart", sql).returns(expected).ok();
+  }
+
+  @Test public void testPushDownProjectAggregateNested() throws SQLException {
+    final String sql = "explain plan for\n"
+        + "select gender, max(qty)\n"
+        + "from (\n"
+        + "  select name, gender, count(*) qty\n"
+        + "  from EMPS\n"
+        + "  group by name, gender) t\n"
+        + "group by gender";
+    final String expected = "PLAN="
+        + "EnumerableAggregate(group=[{1}], EXPR$1=[MAX($2)])\n"
+        + "  EnumerableAggregate(group=[{0, 1}], QTY=[COUNT()])\n"
+        + "    CsvTableScan(table=[[SALES, EMPS]], fields=[[1, 3]])\n";
+    sql("smart", sql).returns(expected).ok();
+  }
+
   @Test public void testFilterableSelect() throws SQLException {
     sql("filterable-model", "select name from EMPS").ok();
   }


[2/3] calcite git commit: [CALCITE-2037] Modify parser template to allow sub-projects to override SqlStmt syntax (Roman Kulyk)

Posted by jh...@apache.org.
[CALCITE-2037] Modify parser template to allow sub-projects to override SqlStmt syntax (Roman Kulyk)

Move additional (custom) statements on the top of the SqlStmt() method in Parser.jj.

Add test to "parserextensiontesting" parser (Julian Hyde).

Close apache/calcite#561


Project: http://git-wip-us.apache.org/repos/asf/calcite/repo
Commit: http://git-wip-us.apache.org/repos/asf/calcite/commit/5a0403cf
Tree: http://git-wip-us.apache.org/repos/asf/calcite/tree/5a0403cf
Diff: http://git-wip-us.apache.org/repos/asf/calcite/diff/5a0403cf

Branch: refs/heads/master
Commit: 5a0403cf7a31aa80d4de179f75b8154a7b0c7a58
Parents: fe3529d
Author: Roman Kulyk <ro...@gmail.com>
Authored: Tue Nov 7 14:48:08 2017 +0000
Committer: Julian Hyde <jh...@apache.org>
Committed: Wed Nov 8 08:41:33 2017 -0800

----------------------------------------------------------------------
 core/src/main/codegen/templates/Parser.jj                | 11 +++++------
 core/src/test/codegen/config.fmpp                        |  1 +
 core/src/test/codegen/includes/parserImpls.ftl           |  9 +++++++++
 .../parserextensiontesting/ExtensionSqlParserTest.java   |  9 +++++++++
 4 files changed, 24 insertions(+), 6 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/calcite/blob/5a0403cf/core/src/main/codegen/templates/Parser.jj
----------------------------------------------------------------------
diff --git a/core/src/main/codegen/templates/Parser.jj b/core/src/main/codegen/templates/Parser.jj
index 0aef85b..a4f7119 100644
--- a/core/src/main/codegen/templates/Parser.jj
+++ b/core/src/main/codegen/templates/Parser.jj
@@ -890,6 +890,11 @@ SqlNode SqlStmt() :
 }
 {
     (
+<#-- Add methods to parse additional statements here -->
+<#list parser.statementParserMethods as method>
+        stmt = ${method}
+    |
+</#list>
         stmt = SqlSetOption(Span.of(), null)
     |
         stmt = SqlAlter()
@@ -913,12 +918,6 @@ SqlNode SqlStmt() :
         stmt = SqlMerge()
     |
         stmt = SqlProcedureCall()
-
-<#-- Add methods to parse additional statements here -->
-<#list parser.statementParserMethods as method>
-    |
-        stmt = ${method}
-</#list>
     )
     {
         return stmt;

http://git-wip-us.apache.org/repos/asf/calcite/blob/5a0403cf/core/src/test/codegen/config.fmpp
----------------------------------------------------------------------
diff --git a/core/src/test/codegen/config.fmpp b/core/src/test/codegen/config.fmpp
index bd168b1..2f747e2 100644
--- a/core/src/test/codegen/config.fmpp
+++ b/core/src/test/codegen/config.fmpp
@@ -38,6 +38,7 @@ data: {
 
       # List of methods for parsing custom SQL statements.
       statementParserMethods: [
+        "SqlDescribeSpacePower()"
       ]
 
       # List of methods for parsing custom literals.

http://git-wip-us.apache.org/repos/asf/calcite/blob/5a0403cf/core/src/test/codegen/includes/parserImpls.ftl
----------------------------------------------------------------------
diff --git a/core/src/test/codegen/includes/parserImpls.ftl b/core/src/test/codegen/includes/parserImpls.ftl
index cea63a4..754f876 100644
--- a/core/src/test/codegen/includes/parserImpls.ftl
+++ b/core/src/test/codegen/includes/parserImpls.ftl
@@ -47,4 +47,13 @@ SqlCreate SqlCreateTable(Span s, boolean replace) :
     }
 }
 
+SqlNode SqlDescribeSpacePower() :
+{
+}
+{
+    <DESCRIBE> <SPACE> <POWER> {
+        return null;
+    }
+}
+
 // End parserImpls.ftl

http://git-wip-us.apache.org/repos/asf/calcite/blob/5a0403cf/core/src/test/java/org/apache/calcite/sql/parser/parserextensiontesting/ExtensionSqlParserTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/sql/parser/parserextensiontesting/ExtensionSqlParserTest.java b/core/src/test/java/org/apache/calcite/sql/parser/parserextensiontesting/ExtensionSqlParserTest.java
index 1b58a4c..825d072 100644
--- a/core/src/test/java/org/apache/calcite/sql/parser/parserextensiontesting/ExtensionSqlParserTest.java
+++ b/core/src/test/java/org/apache/calcite/sql/parser/parserextensiontesting/ExtensionSqlParserTest.java
@@ -16,10 +16,12 @@
  */
 package org.apache.calcite.sql.parser.parserextensiontesting;
 
+import org.apache.calcite.sql.SqlNode;
 import org.apache.calcite.sql.parser.SqlParseException;
 import org.apache.calcite.sql.parser.SqlParserImplFactory;
 import org.apache.calcite.sql.parser.SqlParserTest;
 
+import org.hamcrest.core.IsNull;
 import org.junit.Test;
 
 /**
@@ -49,6 +51,13 @@ public class ExtensionSqlParserTest extends SqlParserTest {
     sql("CREATE TABLE foo.baz(i INTEGER, j VARCHAR(10) NOT NULL)")
         .ok("CREATE TABLE `FOO`.`BAZ` (`I` INTEGER, `J` VARCHAR(10) NOT NULL)");
   }
+
+  @Test public void testExtendedSqlStmt() {
+    sql("DESCRIBE SPACE POWER")
+        .node(new IsNull<SqlNode>());
+    sql("DESCRIBE SEA ^POWER^")
+        .fails("(?s)Encountered \"POWER\" at line 1, column 14..*");
+  }
 }
 
 // End ExtensionSqlParserTest.java