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 2014/06/28 00:27:48 UTC

[1/7] git commit: [OPTIQ-309] WITH ... ORDER BY query gives AssertionError

Repository: incubator-optiq
Updated Branches:
  refs/heads/master 90d0d79ec -> 99f396828


[OPTIQ-309] WITH ... ORDER BY query gives AssertionError


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

Branch: refs/heads/master
Commit: a1557c2927b446feebc1895208fc7a84451e2e95
Parents: 90d0d79
Author: Vladimir Sitnikov <si...@gmail.com>
Authored: Mon Jun 23 00:42:57 2014 +0400
Committer: Julian Hyde <ju...@gmail.com>
Committed: Sun Jun 22 21:41:04 2014 -0700

----------------------------------------------------------------------
 .../org/eigenbase/sql/validate/SqlValidatorImpl.java  |  3 +++
 .../java/org/eigenbase/sql2rel/SqlToRelConverter.java |  4 ++++
 .../test/java/net/hydromatic/optiq/test/JdbcTest.java | 14 ++++++++++++++
 3 files changed, 21 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/a1557c29/core/src/main/java/org/eigenbase/sql/validate/SqlValidatorImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/eigenbase/sql/validate/SqlValidatorImpl.java b/core/src/main/java/org/eigenbase/sql/validate/SqlValidatorImpl.java
index 938ea56..54febe5 100644
--- a/core/src/main/java/org/eigenbase/sql/validate/SqlValidatorImpl.java
+++ b/core/src/main/java/org/eigenbase/sql/validate/SqlValidatorImpl.java
@@ -1812,6 +1812,9 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
     case WITH:
     case UNNEST:
     case OTHER_FUNCTION:
+      if (alias == null) {
+        alias = deriveAlias(node, nextGeneratedId++);
+      }
       registerQuery(
           parentScope,
           usingScope,

http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/a1557c29/core/src/main/java/org/eigenbase/sql2rel/SqlToRelConverter.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/eigenbase/sql2rel/SqlToRelConverter.java b/core/src/main/java/org/eigenbase/sql2rel/SqlToRelConverter.java
index 7514518..07f4650 100644
--- a/core/src/main/java/org/eigenbase/sql2rel/SqlToRelConverter.java
+++ b/core/src/main/java/org/eigenbase/sql2rel/SqlToRelConverter.java
@@ -1651,6 +1651,10 @@ public class SqlToRelConverter {
       convertFrom(bb, ((SqlWithItem) from).query);
       return;
 
+    case WITH:
+      convertFrom(bb, ((SqlWith) from).body);
+      return;
+
     case TABLESAMPLE:
       operands = ((SqlBasicCall) from).getOperands();
       SqlSampleSpec sampleSpec = SqlLiteral.sampleValue(operands[1]);

http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/a1557c29/core/src/test/java/net/hydromatic/optiq/test/JdbcTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/net/hydromatic/optiq/test/JdbcTest.java b/core/src/test/java/net/hydromatic/optiq/test/JdbcTest.java
index 04803f5..dcbe60c 100644
--- a/core/src/test/java/net/hydromatic/optiq/test/JdbcTest.java
+++ b/core/src/test/java/net/hydromatic/optiq/test/JdbcTest.java
@@ -2847,6 +2847,20 @@ public class JdbcTest {
             "deptno=10");
   }
 
+  @Test public void testWithOrderBy() {
+    OptiqAssert.that()
+        .with(OptiqAssert.Config.REGULAR)
+        .query(
+            "with emp2 as (select * from \"hr\".\"emps\")\n"
+                + "select * from emp2\n"
+                + "order by \"deptno\" desc, \"empid\" desc")
+        .returns(
+            "empid=200; deptno=20; name=Eric; salary=8000.0; commission=500\n"
+            + "empid=150; deptno=10; name=Sebastian; salary=7000.0; commission=null\n"
+            + "empid=110; deptno=10; name=Theodore; salary=11500.0; commission=250\n"
+            + "empid=100; deptno=10; name=Bill; salary=10000.0; commission=1000\n");
+  }
+
   /** Tests windowed aggregation. */
   @Test public void testWinAgg() {
     OptiqAssert.that()


[3/7] git commit: Reduce count(not null) to count()

Posted by jh...@apache.org.
Reduce count(not null) to count()


Project: http://git-wip-us.apache.org/repos/asf/incubator-optiq/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-optiq/commit/4621db7e
Tree: http://git-wip-us.apache.org/repos/asf/incubator-optiq/tree/4621db7e
Diff: http://git-wip-us.apache.org/repos/asf/incubator-optiq/diff/4621db7e

Branch: refs/heads/master
Commit: 4621db7e4ee80dd84b14d448d05c093abb8a82f1
Parents: 347eabd
Author: Vladimir Sitnikov <si...@gmail.com>
Authored: Mon Jun 23 09:53:11 2014 +0400
Committer: Julian Hyde <ju...@gmail.com>
Committed: Sun Jun 22 23:54:38 2014 -0700

----------------------------------------------------------------------
 .../rel/rules/ReduceAggregatesRule.java         | 42 ++++++++++++++-----
 .../main/java/org/eigenbase/rex/RexBuilder.java | 44 ++++++++++++++++++++
 .../eigenbase/sql2rel/SqlToRelConverter.java    | 10 ++++-
 .../net/hydromatic/optiq/test/JdbcTest.java     | 23 ++++++++++
 .../org/eigenbase/test/RelMetadataTest.java     | 13 ++++--
 .../org/eigenbase/test/RelOptRulesTest.xml      |  2 +-
 .../eigenbase/test/SqlToRelConverterTest.xml    |  4 +-
 7 files changed, 119 insertions(+), 19 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/4621db7e/core/src/main/java/org/eigenbase/rel/rules/ReduceAggregatesRule.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/eigenbase/rel/rules/ReduceAggregatesRule.java b/core/src/main/java/org/eigenbase/rel/rules/ReduceAggregatesRule.java
index 4dc29b9..be9020b 100644
--- a/core/src/main/java/org/eigenbase/rel/rules/ReduceAggregatesRule.java
+++ b/core/src/main/java/org/eigenbase/rel/rules/ReduceAggregatesRule.java
@@ -26,8 +26,11 @@ import org.eigenbase.reltype.*;
 import org.eigenbase.rex.*;
 import org.eigenbase.sql.*;
 import org.eigenbase.sql.fun.*;
+import org.eigenbase.sql.type.SqlTypeUtil;
 import org.eigenbase.util.*;
 
+import com.google.common.collect.ImmutableList;
+
 /**
  * Rule to reduce aggregates to simpler forms. Currently only AVG(x) to
  * SUM(x)/COUNT(x), but eventually will handle others such as STDDEV.
@@ -49,11 +52,18 @@ public class ReduceAggregatesRule extends RelOptRule {
 
   //~ Methods ----------------------------------------------------------------
 
+  @Override
+  public boolean matches(RelOptRuleCall call) {
+    if (!super.matches(call)) {
+      return false;
+    }
+    AggregateRelBase oldAggRel = (AggregateRelBase) call.rels[0];
+    return containsAvgStddevVarCall(oldAggRel.getAggCallList());
+  }
+
   public void onMatch(RelOptRuleCall ruleCall) {
     AggregateRelBase oldAggRel = (AggregateRelBase) ruleCall.rels[0];
-    if (containsAvgStddevVarCall(oldAggRel.getAggCallList())) {
-      reduceAggs(ruleCall, oldAggRel);
-    }
+    reduceAggs(ruleCall, oldAggRel);
   }
 
   /**
@@ -200,11 +210,14 @@ public class ReduceAggregatesRule extends RelOptRule {
       // anything else:  preserve original call
       RexBuilder rexBuilder = oldAggRel.getCluster().getRexBuilder();
       final int nGroups = oldAggRel.getGroupCount();
+      List<RelDataType> oldArgTypes = SqlTypeUtil
+          .projectTypes(oldAggRel.getRowType(), oldCall.getArgList());
       return rexBuilder.addAggCall(
           oldCall,
           nGroups,
           newCalls,
-          aggCallMapping);
+          aggCallMapping,
+          oldArgTypes);
     }
   }
 
@@ -251,13 +264,15 @@ public class ReduceAggregatesRule extends RelOptRule {
             sumCall,
             nGroups,
             newCalls,
-            aggCallMapping);
+            aggCallMapping,
+            ImmutableList.of(avgInputType));
     RexNode denominatorRef =
         rexBuilder.addAggCall(
             countCall,
             nGroups,
             newCalls,
-            aggCallMapping);
+            aggCallMapping,
+            ImmutableList.of(avgInputType));
     final RexNode divideRef =
         rexBuilder.makeCall(
             SqlStdOperatorTable.DIVIDE,
@@ -309,7 +324,8 @@ public class ReduceAggregatesRule extends RelOptRule {
             sumZeroCall,
             nGroups,
             newCalls,
-            aggCallMapping);
+            aggCallMapping,
+            ImmutableList.of(argType));
     if (!oldCall.getType().isNullable()) {
       // If SUM(x) is not nullable, the validator must have determined that
       // nulls are impossible (because the group is never empty and x is never
@@ -321,7 +337,8 @@ public class ReduceAggregatesRule extends RelOptRule {
             countCall,
             nGroups,
             newCalls,
-            aggCallMapping);
+            aggCallMapping,
+            ImmutableList.of(argType));
     return rexBuilder.makeCall(SqlStdOperatorTable.CASE,
         rexBuilder.makeCall(SqlStdOperatorTable.EQUALS,
             countRef, rexBuilder.makeExactLiteral(BigDecimal.ZERO)),
@@ -382,7 +399,8 @@ public class ReduceAggregatesRule extends RelOptRule {
             sumArgSquaredAggCall,
             nGroups,
             newCalls,
-            aggCallMapping);
+            aggCallMapping,
+            ImmutableList.of(argType));
 
     final AggregateCall sumArgAggCall =
         new AggregateCall(
@@ -396,7 +414,8 @@ public class ReduceAggregatesRule extends RelOptRule {
             sumArgAggCall,
             nGroups,
             newCalls,
-            aggCallMapping);
+            aggCallMapping,
+            ImmutableList.of(argType));
 
     final RexNode sumSquaredArg =
         rexBuilder.makeCall(
@@ -416,7 +435,8 @@ public class ReduceAggregatesRule extends RelOptRule {
             countArgAggCall,
             nGroups,
             newCalls,
-            aggCallMapping);
+            aggCallMapping,
+            ImmutableList.of(argType));
 
     final RexNode avgSumSquaredArg =
         rexBuilder.makeCall(

http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/4621db7e/core/src/main/java/org/eigenbase/rex/RexBuilder.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/eigenbase/rex/RexBuilder.java b/core/src/main/java/org/eigenbase/rex/RexBuilder.java
index 6a848bc..a05df07 100644
--- a/core/src/main/java/org/eigenbase/rex/RexBuilder.java
+++ b/core/src/main/java/org/eigenbase/rex/RexBuilder.java
@@ -262,12 +262,56 @@ public class RexBuilder {
 
   /**
    * Creates a reference to an aggregate call, checking for repeated calls.
+   * @param aggCall aggregate call to be added
+   * @param groupCount number of groups in the aggregate relation
+   * @param aggCalls destination list of aggregate calls
+   * @param aggCallMapping the dictionary of already added calls
+   * @return Rex expression for the given aggregate call
+   * @deprecated Use {@link #addAggCall(org.eigenbase.rel.AggregateCall, int, java.util.List, java.util.Map, java.util.List)}
+   * Will be removed before optiq-0.9.
    */
+  @Deprecated
   public RexNode addAggCall(
       AggregateCall aggCall,
       int groupCount,
       List<AggregateCall> aggCalls,
       Map<AggregateCall, RexNode> aggCallMapping) {
+    Bug.upgrade("remove before optiq-0.9");
+    return addAggCall(aggCall, groupCount, aggCalls, aggCallMapping, null);
+  }
+
+  /**
+   * Creates a reference to an aggregate call, checking for repeated calls.
+   * Argument types help to optimize for repeated aggregates.
+   * For instance count(42) is equivalent to count(*)
+   * @param aggCall aggregate call to be added
+   * @param groupCount number of groups in the aggregate relation
+   * @param aggCalls destination list of aggregate calls
+   * @param aggCallMapping the dictionary of already added calls
+   * @return Rex expression for the given aggregate call
+   */
+  public RexNode addAggCall(
+      AggregateCall aggCall,
+      int groupCount,
+      List<AggregateCall> aggCalls,
+      Map<AggregateCall, RexNode> aggCallMapping,
+      List<RelDataType> aggArgTypes) {
+    if (aggCall.getAggregation() instanceof SqlCountAggFunction
+        && aggArgTypes != null && !aggArgTypes.isEmpty()
+        && !aggCall.isDistinct()) {
+      boolean hasNotNullArg = false;
+      for (RelDataType type : aggArgTypes) {
+        if (!type.isNullable()) {
+          hasNotNullArg = true;
+          break;
+        }
+      }
+      if (hasNotNullArg) {
+        aggCall = new AggregateCall(aggCall.getAggregation(),
+            aggCall.isDistinct(), ImmutableList.<Integer>of(),
+            aggCall.getType(), aggCall.getName());
+      }
+    }
     RexNode rex = aggCallMapping.get(aggCall);
     if (rex == null) {
       int index = aggCalls.size() + groupCount;

http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/4621db7e/core/src/main/java/org/eigenbase/sql2rel/SqlToRelConverter.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/eigenbase/sql2rel/SqlToRelConverter.java b/core/src/main/java/org/eigenbase/sql2rel/SqlToRelConverter.java
index 07f4650..682914d 100644
--- a/core/src/main/java/org/eigenbase/sql2rel/SqlToRelConverter.java
+++ b/core/src/main/java/org/eigenbase/sql2rel/SqlToRelConverter.java
@@ -4556,6 +4556,10 @@ public class SqlToRelConverter {
       if (call.getOperator().isAggregator()) {
         assert bb.agg == this;
         List<Integer> args = new ArrayList<Integer>();
+        List<RelDataType> argTypes =
+            call.getOperator() instanceof SqlCountAggFunction
+            ? new ArrayList<RelDataType>(call.getOperandList().size())
+            : null;
         try {
           // switch out of agg mode
           bb.agg = null;
@@ -4573,6 +4577,9 @@ public class SqlToRelConverter {
             }
             convertedExpr = bb.convertExpression(operand);
             assert convertedExpr != null;
+            if (argTypes != null) {
+              argTypes.add(convertedExpr.getType());
+            }
             args.add(lookupOrCreateGroupExpr(convertedExpr));
           }
         } finally {
@@ -4601,7 +4608,8 @@ public class SqlToRelConverter {
                 aggCall,
                 groupExprs.size(),
                 aggCalls,
-                aggCallMapping);
+                aggCallMapping,
+                argTypes);
         aggMapping.put(call, rex);
       } else if (call instanceof SqlSelect) {
         // rchen 2006-10-17:

http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/4621db7e/core/src/test/java/net/hydromatic/optiq/test/JdbcTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/net/hydromatic/optiq/test/JdbcTest.java b/core/src/test/java/net/hydromatic/optiq/test/JdbcTest.java
index 0fdf109..e02c19d 100644
--- a/core/src/test/java/net/hydromatic/optiq/test/JdbcTest.java
+++ b/core/src/test/java/net/hydromatic/optiq/test/JdbcTest.java
@@ -2800,9 +2800,32 @@ public class JdbcTest {
             + " avg(\"deptno\") as a\n"
             + "from \"hr\".\"emps\"\n"
             + "where \"deptno\" < 0")
+        .explainContains(
+            "PLAN=EnumerableCalcRel(expr#0..2=[{inputs}], expr#3=[0], expr#4=[=($t0, $t3)], expr#5=[null], expr#6=[CASE($t4, $t5, $t1)], expr#7=[/($t2, $t0)], expr#8=[CAST($t7):JavaType(class java.lang.Integer)], CS=[$t0], C=[$t0], S=[$t6], A=[$t8])\n"
+            + "  EnumerableAggregateRel(group=[{}], CS=[COUNT()], agg#1=[$SUM0($0)], agg#2=[SUM($0)])\n"
+            + "    EnumerableCalcRel(expr#0..4=[{inputs}], expr#5=[0], expr#6=[<($t1, $t5)], deptno=[$t1], $condition=[$t6])\n"
+            + "      EnumerableTableAccessRel(table=[[hr, emps]])\n")
         .returns("CS=0; C=0; S=null; A=null\n");
   }
 
+  /** Tests that count(deptno) is reduced to count(). */
+  @Test public void testReduceCountNotNullable() {
+    OptiqAssert.that()
+        .with(OptiqAssert.Config.REGULAR)
+        .query(
+            "select\n"
+            + " count(\"deptno\") as cs,\n"
+            + " count(*) as cs2\n"
+            + "from \"hr\".\"emps\"\n"
+            + "where \"deptno\" < 0")
+        .explainContains(
+            "PLAN=EnumerableCalcRel(expr#0=[{inputs}], CS=[$t0], CS2=[$t0])\n"
+            + "  EnumerableAggregateRel(group=[{}], CS=[COUNT()])\n"
+            + "    EnumerableCalcRel(expr#0..4=[{inputs}], expr#5=[0], expr#6=[<($t1, $t5)], DUMMY=[$t5], $condition=[$t6])\n"
+            + "      EnumerableTableAccessRel(table=[[hr, emps]])\n")
+        .returns("CS=0; CS2=0\n");
+  }
+
   /** Tests sorting by a column that is already sorted. */
   @Test public void testOrderByOnSortedTable() {
     OptiqAssert.that()

http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/4621db7e/core/src/test/java/org/eigenbase/test/RelMetadataTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/eigenbase/test/RelMetadataTest.java b/core/src/test/java/org/eigenbase/test/RelMetadataTest.java
index 7b587fe..63be2f9 100644
--- a/core/src/test/java/org/eigenbase/test/RelMetadataTest.java
+++ b/core/src/test/java/org/eigenbase/test/RelMetadataTest.java
@@ -330,11 +330,16 @@ public class RelMetadataTest extends SqlToRelTestBase {
         false);
   }
 
-  @Test public void testColumnOriginsAggMeasure() {
+  @Test public void testColumnOriginsAggReduced() {
+    checkNoColumnOrigin(
+        "select count(deptno),name from dept group by name");
+  }
+
+  @Test public void testColumnOriginsAggCountNullable() {
     checkSingleColumnOrigin(
-        "select count(deptno),name from dept group by name",
-        "DEPT",
-        "DEPTNO",
+        "select count(mgr),ename from emp group by ename",
+        "EMP",
+        "MGR",
         true);
   }
 

http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/4621db7e/core/src/test/resources/org/eigenbase/test/RelOptRulesTest.xml
----------------------------------------------------------------------
diff --git a/core/src/test/resources/org/eigenbase/test/RelOptRulesTest.xml b/core/src/test/resources/org/eigenbase/test/RelOptRulesTest.xml
index 5976cc7..68ba466 100644
--- a/core/src/test/resources/org/eigenbase/test/RelOptRulesTest.xml
+++ b/core/src/test/resources/org/eigenbase/test/RelOptRulesTest.xml
@@ -107,7 +107,7 @@ AggregateRel(group=[{0}], EXPR$1=[MAX($0)], EXPR$2=[AVG($1)], EXPR$3=[MIN($0)])
             <![CDATA[
 ProjectRel(NAME=[$0], EXPR$1=[$1], EXPR$2=[CAST(/($2, $3)):INTEGER NOT NULL], EXPR$3=[$4])
   ProjectRel(NAME=[$0], EXPR$1=[$1], $f2=[$2], $f3=[$3], EXPR$3=[$4])
-    AggregateRel(group=[{0}], EXPR$1=[MAX($0)], agg#1=[$SUM0($1)], agg#2=[COUNT($1)], EXPR$3=[MIN($0)])
+    AggregateRel(group=[{0}], EXPR$1=[MAX($0)], agg#1=[$SUM0($1)], agg#2=[COUNT()], EXPR$3=[MIN($0)])
       ProjectRel(NAME=[$1], DEPTNO=[$0])
         TableAccessRel(table=[[CATALOG, SALES, DEPT]])
 ]]>

http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/4621db7e/core/src/test/resources/org/eigenbase/test/SqlToRelConverterTest.xml
----------------------------------------------------------------------
diff --git a/core/src/test/resources/org/eigenbase/test/SqlToRelConverterTest.xml b/core/src/test/resources/org/eigenbase/test/SqlToRelConverterTest.xml
index c7bbdd5..c2cc97d 100644
--- a/core/src/test/resources/org/eigenbase/test/SqlToRelConverterTest.xml
+++ b/core/src/test/resources/org/eigenbase/test/SqlToRelConverterTest.xml
@@ -63,7 +63,7 @@ ProjectRel(DEPTNO=[$0], EXPR$1=[$1])
         <Resource name="plan">
             <![CDATA[
 ProjectRel(EXPR$0=[+($0, 4)], EXPR$1=[$1], EXPR$2=[$2], EXPR$3=[*(2, $3)])
-  AggregateRel(group=[{0}], EXPR$1=[SUM($1)], EXPR$2=[SUM($2)], agg#2=[COUNT($1)])
+  AggregateRel(group=[{0}], EXPR$1=[SUM($1)], EXPR$2=[SUM($2)], agg#2=[COUNT()])
     ProjectRel(DEPTNO=[$7], SAL=[$5], $f2=[+(3, $5)])
       TableAccessRel(table=[[CATALOG, SALES, EMP]])
 ]]>
@@ -130,7 +130,7 @@ ProjectRel(NAME=[$0])
             <![CDATA[
 ProjectRel(NAME=[$1], FOO=[$2])
   ProjectRel(DEPTNO=[$1], NAME=[$0], FOO=[$2])
-    AggregateRel(group=[{0, 1}], FOO=[COUNT($1)])
+    AggregateRel(group=[{0, 1}], FOO=[COUNT()])
       ProjectRel(NAME=[$1], DEPTNO=[$0])
         TableAccessRel(table=[[CATALOG, SALES, DEPT]])
 ]]>


[2/7] git commit: More tests for WITH ... ORDER BY.

Posted by jh...@apache.org.
More tests for WITH ... ORDER BY.


Project: http://git-wip-us.apache.org/repos/asf/incubator-optiq/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-optiq/commit/347eabdd
Tree: http://git-wip-us.apache.org/repos/asf/incubator-optiq/tree/347eabdd
Diff: http://git-wip-us.apache.org/repos/asf/incubator-optiq/diff/347eabdd

Branch: refs/heads/master
Commit: 347eabdd6ea600bcd17df607bdae90ea57ea221a
Parents: a1557c2
Author: Julian Hyde <ju...@gmail.com>
Authored: Sun Jun 22 21:44:29 2014 -0700
Committer: Julian Hyde <ju...@gmail.com>
Committed: Sun Jun 22 21:44:29 2014 -0700

----------------------------------------------------------------------
 .../net/hydromatic/optiq/test/JdbcTest.java     | 10 +++---
 .../eigenbase/test/SqlToRelConverterTest.java   | 18 ++++++++++
 .../eigenbase/test/SqlToRelConverterTest.xml    | 38 ++++++++++++++++++++
 .../hydromatic/optiq/impl/tpcds/TpcdsTest.java  | 12 +++----
 4 files changed, 67 insertions(+), 11 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/347eabdd/core/src/test/java/net/hydromatic/optiq/test/JdbcTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/net/hydromatic/optiq/test/JdbcTest.java b/core/src/test/java/net/hydromatic/optiq/test/JdbcTest.java
index dcbe60c..0fdf109 100644
--- a/core/src/test/java/net/hydromatic/optiq/test/JdbcTest.java
+++ b/core/src/test/java/net/hydromatic/optiq/test/JdbcTest.java
@@ -2852,8 +2852,8 @@ public class JdbcTest {
         .with(OptiqAssert.Config.REGULAR)
         .query(
             "with emp2 as (select * from \"hr\".\"emps\")\n"
-                + "select * from emp2\n"
-                + "order by \"deptno\" desc, \"empid\" desc")
+            + "select * from emp2\n"
+            + "order by \"deptno\" desc, \"empid\" desc")
         .returns(
             "empid=200; deptno=20; name=Eric; salary=8000.0; commission=500\n"
             + "empid=150; deptno=10; name=Sebastian; salary=7000.0; commission=null\n"
@@ -4524,7 +4524,8 @@ public class JdbcTest {
         .returns("P0=0; P1=1; P2=2\n");
   }
 
-  /** Test for {@link EigenbaseNewResource#requireDefaultConstructor(String)}. */
+  /** Test for
+   * {@link org.eigenbase.resource.EigenbaseNewResource#requireDefaultConstructor(String)}. */
   @Test public void testUserDefinedFunction2() throws Exception {
     withBadUdf(AwkwardFunction.class)
         .connectThrows(
@@ -4595,7 +4596,8 @@ public class JdbcTest {
         .returnsUnordered("deptno=20; P=20", "deptno=10; P=30");
   }
 
-  /** Test for {@link EigenbaseNewResource#firstParameterOfAdd(String)}. */
+  /** Test for
+   * {@link org.eigenbase.resource.EigenbaseNewResource#firstParameterOfAdd(String)}. */
   @Test public void testUserDefinedAggregateFunction3() throws Exception {
     withBadUdf(SumFunctionBadIAdd.class).connectThrows(
         "Caused by: java.lang.RuntimeException: In user-defined aggregate class 'net.hydromatic.optiq.test.JdbcTest$SumFunctionBadIAdd', first parameter to 'add' method must be the accumulator (the return type of the 'init' method)");

http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/347eabdd/core/src/test/java/org/eigenbase/test/SqlToRelConverterTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/eigenbase/test/SqlToRelConverterTest.java b/core/src/test/java/org/eigenbase/test/SqlToRelConverterTest.java
index f474b1f..03600c4 100644
--- a/core/src/test/java/org/eigenbase/test/SqlToRelConverterTest.java
+++ b/core/src/test/java/org/eigenbase/test/SqlToRelConverterTest.java
@@ -436,6 +436,24 @@ public class SqlToRelConverterTest extends SqlToRelTestBase {
         "${plan}");
   }
 
+  /** Test case for
+   * <a href="https://issues.apache.org/jira/browse/OPTIQ-309">[OPTIQ-309]
+   * WITH ... ORDER BY query gives AssertionError</a>. */
+  @Test public void testWithOrder() {
+    check("with emp2 as (select * from emp)\n"
+        + "select * from emp2 order by deptno",
+        "${plan}");
+  }
+
+  @Test public void testWithUnionOrder() {
+    check("with emp2 as (select empno, deptno as x from emp)\n"
+        + "select * from emp2\n"
+        + "union all\n"
+        + "select * from emp2\n"
+        + "order by empno + x",
+        "${plan}");
+  }
+
   @Test public void testWithUnion() {
     check("with emp2 as (select * from emp where deptno > 10)\n"
       + "select empno from emp2 where deptno < 30 union all select deptno from emp",

http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/347eabdd/core/src/test/resources/org/eigenbase/test/SqlToRelConverterTest.xml
----------------------------------------------------------------------
diff --git a/core/src/test/resources/org/eigenbase/test/SqlToRelConverterTest.xml b/core/src/test/resources/org/eigenbase/test/SqlToRelConverterTest.xml
index 56733e6..c7bbdd5 100644
--- a/core/src/test/resources/org/eigenbase/test/SqlToRelConverterTest.xml
+++ b/core/src/test/resources/org/eigenbase/test/SqlToRelConverterTest.xml
@@ -1807,4 +1807,42 @@ UnionRel(all=[true])
 ]]>
         </Resource>
     </TestCase>
+    <TestCase name="testWithOrder">
+        <Resource name="sql">
+            <![CDATA[with emp2 as (select * from emp)
+select * from emp2 order by deptno]]>
+        </Resource>
+        <Resource name="plan">
+            <![CDATA[
+SortRel(sort0=[$7], dir0=[ASC])
+  ProjectRel(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8])
+    ProjectRel(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8])
+      ProjectRel(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8])
+        TableAccessRel(table=[[CATALOG, SALES, EMP]])
+]]>
+        </Resource>
+    </TestCase>
+    <TestCase name="testWithUnionOrder">
+        <Resource name="sql">
+            <![CDATA[with emp2 as (select empno, deptno as x from emp)
+select * from emp2
+union all
+select * from emp2
+order by empno + x]]>
+        </Resource>
+        <Resource name="plan">
+            <![CDATA[
+ProjectRel(EMPNO=[$0], X=[$1])
+  SortRel(sort0=[$2], dir0=[ASC])
+    ProjectRel(EMPNO=[$0], X=[$1], EXPR$2=[+($0, $1)])
+      UnionRel(all=[true])
+        ProjectRel(EMPNO=[$0], X=[$1])
+          ProjectRel(EMPNO=[$0], X=[$7])
+            TableAccessRel(table=[[CATALOG, SALES, EMP]])
+        ProjectRel(EMPNO=[$0], X=[$1])
+          ProjectRel(EMPNO=[$0], X=[$7])
+            TableAccessRel(table=[[CATALOG, SALES, EMP]])
+]]>
+        </Resource>
+    </TestCase>
 </Root>

http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/347eabdd/plus/src/test/java/net/hydromatic/optiq/impl/tpcds/TpcdsTest.java
----------------------------------------------------------------------
diff --git a/plus/src/test/java/net/hydromatic/optiq/impl/tpcds/TpcdsTest.java b/plus/src/test/java/net/hydromatic/optiq/impl/tpcds/TpcdsTest.java
index fb90a87..17319e5 100644
--- a/plus/src/test/java/net/hydromatic/optiq/impl/tpcds/TpcdsTest.java
+++ b/plus/src/test/java/net/hydromatic/optiq/impl/tpcds/TpcdsTest.java
@@ -83,17 +83,16 @@ public class TpcdsTest {
         .returnsCount(750000);
   }
 
-  @Ignore("assert fail in registerQuery")
   @Test public void testQuery01() {
-    checkQuery(1);
+    checkQuery(1).runs();
   }
 
   @Ignore("takes too long to optimize")
   @Test public void testQuery72() {
-    checkQuery(72);
+    checkQuery(72).runs();
   }
 
-  private void checkQuery(int i) {
+  private OptiqAssert.AssertQuery checkQuery(int i) {
     final Query query = Query.of(i);
     String sql = query.sql(-1, new Random(0));
     switch (i) {
@@ -101,9 +100,8 @@ public class TpcdsTest {
       // Work around OPTIQ-304: Support '<DATE> + <INTEGER>'.
       sql = sql.replace("+ 5", "+ interval '5' day");
     }
-    with()
-        .query(sql.replaceAll("tpcds\\.", "tpcds_01."))
-        .runs();
+    return with()
+        .query(sql.replaceAll("tpcds\\.", "tpcds_01."));
   }
 }
 


[7/7] git commit: [OPTIQ-310] Implement LEAD, LAG and NTILE windowed aggregates.

Posted by jh...@apache.org.
[OPTIQ-310] Implement LEAD, LAG and NTILE windowed aggregates.

Refactor aggregate implementation to interfaces.
Add rowInFrame, and rowInPartition, and needCacheWhenFrameIntact APIs for
windowed aggregates.


Project: http://git-wip-us.apache.org/repos/asf/incubator-optiq/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-optiq/commit/99f39682
Tree: http://git-wip-us.apache.org/repos/asf/incubator-optiq/tree/99f39682
Diff: http://git-wip-us.apache.org/repos/asf/incubator-optiq/diff/99f39682

Branch: refs/heads/master
Commit: 99f3968285d5e7c7c50d6d1fc01e3f3aa5486e62
Parents: e34e6e1
Author: Vladimir Sitnikov <si...@gmail.com>
Authored: Tue Jun 24 11:07:29 2014 +0400
Committer: Julian Hyde <ju...@gmail.com>
Committed: Tue Jun 24 20:17:26 2014 -0600

----------------------------------------------------------------------
 REFERENCE.md                                    |   4 +
 .../optiq/rules/java/AggAddContext.java         |  30 +-
 .../optiq/rules/java/AggResetContext.java       |  22 +-
 .../optiq/rules/java/AggResultContext.java      |  18 +-
 .../hydromatic/optiq/rules/java/JavaRules.java  | 266 +++++++--------
 .../optiq/rules/java/NestedBlockBuilder.java    |  57 +---
 .../rules/java/NestedBlockBuilderImpl.java      | 117 +++++++
 .../optiq/rules/java/RexImpTable.java           | 144 +++++++-
 .../optiq/rules/java/RexToLixTranslator.java    |  10 +-
 .../optiq/rules/java/StrictAggImplementor.java  |   4 +-
 .../rules/java/StrictWinAggImplementor.java     |   4 +
 .../optiq/rules/java/WinAggAddContext.java      |  38 +--
 .../optiq/rules/java/WinAggContext.java         |  27 ++
 .../optiq/rules/java/WinAggFrameContext.java    |  73 +++++
 .../rules/java/WinAggFrameResultContext.java    |  69 ++++
 .../optiq/rules/java/WinAggImplementor.java     | 106 +-----
 .../optiq/rules/java/WinAggResetContext.java    |  60 +---
 .../optiq/rules/java/WinAggResultContext.java   |  41 ++-
 .../rules/java/impl/AggAddContextImpl.java      |  39 +++
 .../rules/java/impl/AggResetContextImpl.java    |  51 +++
 .../rules/java/impl/AggResultContextImpl.java   |  44 +++
 .../rules/java/impl/WinAggAddContextImpl.java   |  53 +++
 .../rules/java/impl/WinAggResetContextImpl.java |  89 +++++
 .../java/impl/WinAggResultContextImpl.java      | 108 ++++++
 .../optiq/rules/java/impl/package-info.java     |  24 ++
 .../sql/fun/SqlLeadLagAggFunction.java          |  94 ++++++
 .../eigenbase/sql/fun/SqlNtileAggFunction.java  |  58 ++++
 .../eigenbase/sql/fun/SqlStdOperatorTable.java  |  18 +
 .../sql/type/CompositeOperandTypeChecker.java   |   6 +
 .../sql/type/SqlSingleOperandTypeChecker.java   |   2 +-
 .../net/hydromatic/optiq/test/JdbcTest.java     | 328 ++++++++++++++-----
 31 files changed, 1438 insertions(+), 566 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/99f39682/REFERENCE.md
----------------------------------------------------------------------
diff --git a/REFERENCE.md b/REFERENCE.md
index 5fe4fdb..92f54eb 100644
--- a/REFERENCE.md
+++ b/REFERENCE.md
@@ -434,8 +434,12 @@ Not implemented:
 | ROW_NUMBER() OVER window
 | FIRST_VALUE(value) OVER window
 | LAST_VALUE(value) OVER window
+| LEAD(value, offset, default) OVER window
+| LAG(value, offset, default) OVER window
+| NTILE(value) OVER window
 
 Not implemented:
+* COUNT(DISTINCT value) OVER window
 * FIRST_VALUE(value) IGNORE NULLS OVER window
 * LAST_VALUE(value) IGNORE NULLS OVER window
 * PERCENT_RANK(value) OVER window

http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/99f39682/core/src/main/java/net/hydromatic/optiq/rules/java/AggAddContext.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/net/hydromatic/optiq/rules/java/AggAddContext.java b/core/src/main/java/net/hydromatic/optiq/rules/java/AggAddContext.java
index 88a88c3..cd1aca3 100644
--- a/core/src/main/java/net/hydromatic/optiq/rules/java/AggAddContext.java
+++ b/core/src/main/java/net/hydromatic/optiq/rules/java/AggAddContext.java
@@ -17,7 +17,6 @@
 */
 package net.hydromatic.optiq.rules.java;
 
-import net.hydromatic.linq4j.expressions.BlockBuilder;
 import net.hydromatic.linq4j.expressions.Expression;
 
 import org.eigenbase.rex.RexNode;
@@ -25,22 +24,18 @@ import org.eigenbase.rex.RexNode;
 import java.util.List;
 
 /**
- * Information for a call to {@link AggImplementor#implementAdd(AggContext, AggAddContext)}.
+ * Information for a call to {@link net.hydromatic.optiq.rules.java.AggImplementor#implementAdd(AggContext, AggAddContext)}.
  * Typically, the aggregation implementation will use {@link #arguments()}
  * or {@link #rexArguments()} to update aggregate value.
  */
-public abstract class AggAddContext extends AggResultContext {
-  public AggAddContext(BlockBuilder block, List<Expression> accumulator) {
-    super(block, accumulator);
-  }
-
+public interface AggAddContext extends AggResultContext {
   /**
-   * Returns {@link RexNode} representation of arguments.
+   * Returns {@link org.eigenbase.rex.RexNode} representation of arguments.
    * This can be useful for manual translation of required arguments with
-   * different {@link net.hydromatic.optiq.rules.java.NullPolicy}.
-   * @return {@link RexNode} representation of arguments
+   * different {@link NullPolicy}.
+   * @return {@link org.eigenbase.rex.RexNode} representation of arguments
    */
-  public abstract List<RexNode> rexArguments();
+  List<RexNode> rexArguments();
 
   /**
    * Returns Linq4j form of arguments.
@@ -49,14 +44,13 @@ public abstract class AggAddContext extends AggResultContext {
    * This is handy if you need just operate on argument.
    * @return Linq4j form of arguments.
    */
-  public final List<Expression> arguments() {
-    return rowTranslator().translateList(rexArguments());
-  }
+  List<Expression> arguments();
 
   /**
-   * Returns {@link RexToLixTranslator} suitable to transform the arguments.
-   * @return {@link RexToLixTranslator} suitable to transform the arguments.
+   * Returns {@link net.hydromatic.optiq.rules.java.RexToLixTranslator} suitable to transform the arguments.
+   * @return {@link net.hydromatic.optiq.rules.java.RexToLixTranslator} suitable to transform the arguments.
    */
-  public abstract RexToLixTranslator rowTranslator();
-
+  RexToLixTranslator rowTranslator();
 }
+
+// End AggAddContext.java

http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/99f39682/core/src/main/java/net/hydromatic/optiq/rules/java/AggResetContext.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/net/hydromatic/optiq/rules/java/AggResetContext.java b/core/src/main/java/net/hydromatic/optiq/rules/java/AggResetContext.java
index 22276a1..b6faada 100644
--- a/core/src/main/java/net/hydromatic/optiq/rules/java/AggResetContext.java
+++ b/core/src/main/java/net/hydromatic/optiq/rules/java/AggResetContext.java
@@ -17,7 +17,6 @@
 */
 package net.hydromatic.optiq.rules.java;
 
-import net.hydromatic.linq4j.expressions.BlockBuilder;
 import net.hydromatic.linq4j.expressions.Expression;
 
 import java.util.List;
@@ -27,30 +26,15 @@ import java.util.List;
  * {@link AggResetContext} provides access to the accumulator variables
  * that should be reset.
  */
-public class AggResetContext extends NestedBlockBuilder {
-  private final List<Expression> accumulator;
-
-  /**
-   * Creates aggregate reset context
-   * @param block code block that will contain the added initialization
-   * @param accumulator accumulator variables that store the intermediate
-   *                    aggregate state
-   */
-  public AggResetContext(BlockBuilder block, List<Expression> accumulator) {
-    super(block);
-    this.accumulator = accumulator;
-  }
-
+public interface AggResetContext extends NestedBlockBuilder {
   /**
    * Returns accumulator variables that should be reset.
    * There MUST be an assignment even if you just assign the default value.
    * @return accumulator variables that should be reset or empty list when no
    *   accumulator variables are used by the aggregate implementation.
-   * @see net.hydromatic.optiq.rules.java.AggImplementor#getStateType(AggContext)
+   * @see AggImplementor#getStateType(net.hydromatic.optiq.rules.java.AggContext)
    */
-  public List<Expression> accumulator() {
-    return accumulator;
-  }
+  List<Expression> accumulator();
 }
 
 // End AggResetContext.java

http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/99f39682/core/src/main/java/net/hydromatic/optiq/rules/java/AggResultContext.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/net/hydromatic/optiq/rules/java/AggResultContext.java b/core/src/main/java/net/hydromatic/optiq/rules/java/AggResultContext.java
index 3c096df..71ab4af 100644
--- a/core/src/main/java/net/hydromatic/optiq/rules/java/AggResultContext.java
+++ b/core/src/main/java/net/hydromatic/optiq/rules/java/AggResultContext.java
@@ -17,27 +17,13 @@
 */
 package net.hydromatic.optiq.rules.java;
 
-import net.hydromatic.linq4j.expressions.BlockBuilder;
-import net.hydromatic.linq4j.expressions.Expression;
-
-import java.util.List;
-
 /**
  * Information for a call to {@link AggImplementor#implementResult(AggContext, AggResultContext)}
  * Typically, the aggregation implementation will convert {@link #accumulator()}
  * to the resulting value of the aggregation.
- * The implementation MUST NOT destroy the cotents of {@link #accumulator()}.
+ * The implementation MUST NOT destroy the contents of {@link #accumulator()}.
  */
-public class AggResultContext extends AggResetContext {
-  /**
-   * Creates aggregate result context
-   * @param block code block that will contain the result calculation statements
-   * @param accumulator accumulator variables that store the intermediate
-   *                    aggregate state
-   */
-  public AggResultContext(BlockBuilder block, List<Expression> accumulator) {
-    super(block, accumulator);
-  }
+public interface AggResultContext extends NestedBlockBuilder, AggResetContext {
 }
 
 // End AggResultContext.java

http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/99f39682/core/src/main/java/net/hydromatic/optiq/rules/java/JavaRules.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/net/hydromatic/optiq/rules/java/JavaRules.java b/core/src/main/java/net/hydromatic/optiq/rules/java/JavaRules.java
index cf8ce44..266a7b4 100644
--- a/core/src/main/java/net/hydromatic/optiq/rules/java/JavaRules.java
+++ b/core/src/main/java/net/hydromatic/optiq/rules/java/JavaRules.java
@@ -26,6 +26,7 @@ import net.hydromatic.optiq.*;
 import net.hydromatic.optiq.impl.java.JavaTypeFactory;
 import net.hydromatic.optiq.prepare.OptiqPrepareImpl;
 import net.hydromatic.optiq.prepare.Prepare;
+import net.hydromatic.optiq.rules.java.impl.*;
 import net.hydromatic.optiq.runtime.SortedMultiMap;
 import net.hydromatic.optiq.util.BitSets;
 
@@ -1049,7 +1050,7 @@ public class JavaRules {
         agg.state = decls;
         initExpressions.addAll(decls);
         agg.implementor.implementReset(agg.context,
-            new AggResultContext(initBlock, decls));
+            new AggResultContextImpl(initBlock, decls));
       }
 
       final PhysType accPhysType =
@@ -1094,7 +1095,7 @@ public class JavaRules {
         stateOffset += stateSize;
 
         AggAddContext addContext =
-            new AggAddContext(builder2, accumulator) {
+            new AggAddContextImpl(builder2, accumulator) {
               public List<RexNode> rexArguments() {
                 List<RelDataTypeField> inputTypes =
                     inputPhysType.getRowType().getFieldList();
@@ -1149,7 +1150,7 @@ public class JavaRules {
       for (final AggImpState agg : aggs) {
         results.add(agg.implementor.implementResult(
             agg.context,
-            new AggResultContext(resultBlock, agg.state)));
+            new AggResultContextImpl(resultBlock, agg.state)));
       }
       resultBlock.add(physType.record(results));
       if (keyArity == 0) {
@@ -2256,10 +2257,14 @@ public class JavaRules {
         declareAndResetState(typeFactory, builder, result, windowIdx, aggs,
             outputPhysType, outputRow);
 
+        // There are assumptions that minX==0. If ever change this, look for
+        // frameRowCount, bounds checking, etc
         final Expression minX = Expressions.constant(0);
+        final Expression partitionRowCount =
+            builder3.append("partRows", Expressions.field(rows_, "length"));
         final Expression maxX = builder3.append("maxX",
             Expressions.subtract(
-                Expressions.field(rows_, "length"), Expressions.constant(1)));
+                partitionRowCount, Expressions.constant(1)));
 
         final Expression startUnchecked = builder4.append("start",
             translateBound(translator, i_, row_, minX, maxX, rows_,
@@ -2323,14 +2328,14 @@ public class JavaRules {
             startX == minX ? endX : Expressions.subtract(endX, startX),
             Expressions.constant(1));
 
-        final Expression partitionRowCount;
+        final Expression frameRowCount;
 
         if (hasRows.equals(Expressions.constant(true))) {
-          partitionRowCount =
-              builder3.append("totalRows", rowCountWhenNonEmpty);
+          frameRowCount =
+              builder4.append("totalRows", rowCountWhenNonEmpty);
         } else {
-          partitionRowCount =
-              builder5.append("totalRows", Expressions.condition(hasRows,
+          frameRowCount =
+              builder4.append("totalRows", Expressions.condition(hasRows,
                   rowCountWhenNonEmpty, Expressions.constant(0)));
         }
 
@@ -2343,8 +2348,8 @@ public class JavaRules {
 
         for (final AggImpState agg : aggs) {
           agg.implementor.implementReset(agg.context,
-              new WinAggResetContext(builder6, agg.state, i_, startX, endX,
-                  hasRows, partitionRowCount));
+              new WinAggResetContextImpl(builder6, agg.state, i_, startX, endX,
+                  hasRows, partitionRowCount, frameRowCount));
         }
 
         Expression lowerBoundCanChange =
@@ -2381,10 +2386,12 @@ public class JavaRules {
             Expressions.declare(0, "j", actualStart);
 
         final PhysType inputPhysTypeFinal = inputPhysType;
-        final Function<BlockBuilder, WinAggImplementor.WinAggFrameResultContext>
+        final Function<BlockBuilder, WinAggFrameResultContext>
             resultContextBuilder =
             getBlockBuilderWinAggFrameResultContextFunction(typeFactory, result,
                 translatedConstants, comparator_, rows_, i_, startX, endX,
+                minX, maxX,
+                hasRows, frameRowCount, partitionRowCount,
                 jDecl, inputPhysTypeFinal);
 
         final Function<AggImpState, List<RexNode>> rexArguments =
@@ -2406,8 +2413,7 @@ public class JavaRules {
               }
             };
 
-        implementAdd(aggs, i_, startX, endX, hasRows, partitionRowCount,
-            builder7, jDecl, resultContextBuilder, rexArguments);
+        implementAdd(aggs, builder7, resultContextBuilder, rexArguments, jDecl);
 
         BlockStatement forBlock = builder7.toBlock();
         if (!forBlock.statements.isEmpty()) {
@@ -2423,13 +2429,15 @@ public class JavaRules {
           builder5.add(forAggLoop);
         }
 
-        implementResult(aggs, i_, startX, endX, hasRows, builder5,
-            partitionRowCount,
-            resultContextBuilder, rexArguments);
+        if (implementResult(aggs, builder5, resultContextBuilder, rexArguments,
+                true)) {
+          builder4.add(Expressions.ifThen(Expressions.orElse(
+              lowerBoundCanChange,
+              Expressions.notEqual(endX, prevEnd)), builder5.toBlock()));
+        }
 
-        builder4.add(Expressions.ifThen(Expressions.orElse(
-            lowerBoundCanChange,
-            Expressions.notEqual(endX, prevEnd)), builder5.toBlock()));
+        implementResult(aggs, builder4, resultContextBuilder, rexArguments,
+            false);
 
         builder4.add(
             Expressions.statement(
@@ -2476,25 +2484,28 @@ public class JavaRules {
       return implementor.result(inputPhysType, builder.toBlock());
     }
 
-    private Function<BlockBuilder, WinAggImplementor.WinAggFrameResultContext>
+    private Function<BlockBuilder, WinAggFrameResultContext>
     getBlockBuilderWinAggFrameResultContextFunction(
         final JavaTypeFactory typeFactory, final Result result,
         final List<Expression> translatedConstants,
         final Expression comparator_,
         final Expression rows_, final ParameterExpression i_,
-        final Expression startX,
-        final Expression endX, final DeclarationStatement jDecl,
-        final PhysType inputPhysTypeFinal) {
+        final Expression startX, final Expression endX,
+        final Expression minX, final Expression maxX,
+        final Expression hasRows, final Expression frameRowCount,
+        final Expression partitionRowCount,
+        final DeclarationStatement jDecl,
+        final PhysType inputPhysType) {
       return new Function<BlockBuilder,
-          WinAggImplementor.WinAggFrameResultContext>() {
-        public WinAggImplementor.WinAggFrameResultContext apply(
+          WinAggFrameResultContext>() {
+        public WinAggFrameResultContext apply(
             final BlockBuilder block) {
-          return new WinAggImplementor.WinAggFrameResultContext() {
+          return new WinAggFrameResultContext() {
             public RexToLixTranslator rowTranslator(Expression rowIndex) {
               Expression row =
                   getRow(rowIndex);
               final RexToLixTranslator.InputGetter inputGetter =
-                  new WindowRelInputGetter(row, inputPhysTypeFinal,
+                  new WindowRelInputGetter(row, inputPhysType,
                       result.physType.getRowType().getFieldCount(),
                       translatedConstants);
 
@@ -2502,16 +2513,6 @@ public class JavaRules {
                   block, inputGetter);
             }
 
-            public List<RexNode> rexArguments() {
-              throw new UnsupportedOperationException(
-                  "Should not be used");
-            }
-
-            public List<Expression> arguments(Expression rowIndex) {
-              throw new UnsupportedOperationException(
-                  "Should not be used");
-            }
-
             public Expression computeIndex(Expression offset,
                 WinAggImplementor.SeekType seekType) {
               Expression index;
@@ -2528,15 +2529,35 @@ public class JavaRules {
                     + " is not supported");
               }
               if (!Expressions.constant(0).equals(offset)) {
-                index = Expressions.add(index, offset);
-                index = Expressions.call(
-                    BuiltinMethod.MATH_MIN.method, index, endX);
-                index = Expressions.call(
-                    BuiltinMethod.MATH_MAX.method, index, startX);
+                index = block.append("idx", Expressions.add(index, offset));
               }
               return index;
             }
 
+            private Expression checkBounds(Expression rowIndex,
+                Expression minIndex, Expression maxIndex) {
+              if (rowIndex == i_ || rowIndex == startX || rowIndex == endX) {
+                // No additional bounds check required
+                return hasRows;
+              }
+
+              //noinspection UnnecessaryLocalVariable
+              Expression res = block.append("rowInFrame", Expressions.foldAnd(
+                  ImmutableList.of(hasRows,
+                      Expressions.greaterThanOrEqual(rowIndex, minIndex),
+                      Expressions.lessThanOrEqual(rowIndex, maxIndex))));
+
+              return res;
+            }
+
+            public Expression rowInFrame(Expression rowIndex) {
+              return checkBounds(rowIndex, startX, endX);
+            }
+
+            public Expression rowInPartition(Expression rowIndex) {
+              return checkBounds(rowIndex, minX, maxX);
+            }
+
             public Expression compareRows(Expression a, Expression b) {
               return Expressions.call(comparator_,
                   BuiltinMethod.COMPARATOR_COMPARE.method,
@@ -2548,7 +2569,31 @@ public class JavaRules {
                   "jRow",
                   RexToLixTranslator.convert(
                       Expressions.arrayIndex(rows_, rowIndex),
-                      inputPhysTypeFinal.getJavaRowType()));
+                      inputPhysType.getJavaRowType()));
+            }
+
+            public Expression index() {
+              return i_;
+            }
+
+            public Expression startIndex() {
+              return startX;
+            }
+
+            public Expression endIndex() {
+              return endX;
+            }
+
+            public Expression hasRows() {
+              return hasRows;
+            }
+
+            public Expression getFrameRowCount() {
+              return frameRowCount;
+            }
+
+            public Expression getPartitionRowCount() {
+              return partitionRowCount;
             }
           };
         }
@@ -2668,7 +2713,7 @@ public class JavaRules {
         List<Expression> outputRow) {
       for (final AggImpState agg: aggs) {
         agg.context =
-            new WinAggImplementor.WinAggContext() {
+            new WinAggContext() {
               public Aggregation aggregation() {
                 return agg.call.getAggregation();
               }
@@ -2727,68 +2772,23 @@ public class JavaRules {
         agg.result = aggRes;
         outputRow.add(aggRes);
         agg.implementor.implementReset(agg.context,
-            new WinAggResetContext(builder, agg.state,
-                null, null, null, null, null));
+            new WinAggResetContextImpl(builder, agg.state,
+                null, null, null, null, null, null));
       }
     }
 
     private void implementAdd(List<AggImpState> aggs,
-        final ParameterExpression i_,
-        final Expression startX, final Expression endX,
-        final Expression hasRows,
-        final Expression partitionRowCount, final BlockBuilder builder7,
-        final DeclarationStatement jDecl,
-        final Function<BlockBuilder, WinAggImplementor
-            .WinAggFrameResultContext> resultContextBuilder,
-        final Function<AggImpState, List<RexNode>> rexArguments) {
+        final BlockBuilder builder7,
+        final Function<BlockBuilder, WinAggFrameResultContext> frame,
+        final Function<AggImpState, List<RexNode>> rexArguments,
+        final DeclarationStatement jDecl) {
       for (final AggImpState agg : aggs) {
         final WinAggAddContext addContext =
-            new WinAggAddContext(builder7, agg.state) {
-              public Expression computeIndex(Expression offset,
-                  WinAggImplementor.SeekType seekType) {
-                WinAggImplementor.WinAggFrameResultContext context =
-                    resultContextBuilder.apply(currentBlock());
-                return context.computeIndex(offset, seekType);
-              }
-
-              public RexToLixTranslator rowTranslator(Expression rowIndex) {
-                WinAggImplementor.WinAggFrameResultContext context =
-                    resultContextBuilder.apply(currentBlock());
-                return context.rowTranslator(rowIndex)
-                    .setNullable(currentNullables());
-              }
-
-              public Expression compareRows(Expression a, Expression b) {
-                WinAggImplementor.WinAggFrameResultContext context =
-                    resultContextBuilder.apply(currentBlock());
-                return context.compareRows(a, b);
-              }
-
+            new WinAggAddContextImpl(builder7, agg.state, frame) {
               public Expression currentPosition() {
                 return jDecl.parameter;
               }
 
-              public Expression index() {
-                return i_;
-              }
-
-              public Expression startIndex() {
-                return startX;
-              }
-
-              public Expression endIndex() {
-                return endX;
-              }
-
-              public Expression hasRows() {
-                return hasRows;
-              }
-
-              public Expression getPartitionRowCount() {
-                return partitionRowCount;
-              }
-
-              @Override
               public List<RexNode> rexArguments() {
                 return rexArguments.apply(agg);
               }
@@ -2797,67 +2797,37 @@ public class JavaRules {
       }
     }
 
-    private void implementResult(List<AggImpState> aggs,
-        final ParameterExpression i_,
-        final Expression startX, final Expression endX,
-        final Expression hasRows,
-        final BlockBuilder builder5, final Expression partitionRowCount,
-        final Function<BlockBuilder, WinAggImplementor
-                    .WinAggFrameResultContext> resultContextBuilder,
-        final Function<AggImpState, List<RexNode>> rexArguments) {
+    private boolean implementResult(List<AggImpState> aggs,
+        final BlockBuilder builder,
+        final Function<BlockBuilder, WinAggFrameResultContext> frame,
+        final Function<AggImpState, List<RexNode>> rexArguments,
+        boolean cachedBlock) {
+      boolean nonEmpty = false;
       for (final AggImpState agg : aggs) {
+        boolean needCache = true;
+        if (agg.implementor instanceof WinAggImplementor) {
+          WinAggImplementor imp = (WinAggImplementor) agg.implementor;
+          needCache = imp.needCacheWhenFrameIntact();
+        }
+        if (needCache ^ cachedBlock) {
+          // Regular aggregates do not change when the windowing frame keeps
+          // the same. Ths
+          continue;
+        }
+        nonEmpty = true;
         Expression res = agg.implementor.implementResult(agg.context,
-            new WinAggResultContext(builder5, agg.state) {
+            new WinAggResultContextImpl(builder, agg.state, frame) {
               public List<RexNode> rexArguments() {
                 return rexArguments.apply(agg);
               }
-
-              public Expression computeIndex(Expression offset,
-                  WinAggImplementor.SeekType seekType) {
-                WinAggImplementor.WinAggFrameResultContext context =
-                    resultContextBuilder.apply(currentBlock());
-                return context.computeIndex(offset, seekType);
-              }
-
-              public RexToLixTranslator rowTranslator(Expression rowIndex) {
-                WinAggImplementor.WinAggFrameResultContext context =
-                    resultContextBuilder.apply(currentBlock());
-                return context.rowTranslator(rowIndex)
-                    .setNullable(currentNullables());
-              }
-
-              public Expression compareRows(Expression a, Expression b) {
-                WinAggImplementor.WinAggFrameResultContext context =
-                    resultContextBuilder.apply(currentBlock());
-                return context.compareRows(a, b);
-              }
-
-              public Expression index() {
-                return i_;
-              }
-
-              public Expression startIndex() {
-                return startX;
-              }
-
-              public Expression endIndex() {
-                return endX;
-              }
-
-              public Expression hasRows() {
-                return hasRows;
-              }
-
-              public Expression getPartitionRowCount() {
-                return partitionRowCount;
-              }
             });
         // Several count(a) and count(b) might share the result
-        Expression aggRes = builder5.append("a" + agg.aggIdx + "res",
+        Expression aggRes = builder.append("a" + agg.aggIdx + "res",
             RexToLixTranslator.convert(res, agg.result.getType()));
-        builder5.add(Expressions.statement(
+        builder.add(Expressions.statement(
             Expressions.assign(agg.result, aggRes)));
       }
+      return nonEmpty;
     }
 
     private Expression translateBound(RexToLixTranslator translator,

http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/99f39682/core/src/main/java/net/hydromatic/optiq/rules/java/NestedBlockBuilder.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/net/hydromatic/optiq/rules/java/NestedBlockBuilder.java b/core/src/main/java/net/hydromatic/optiq/rules/java/NestedBlockBuilder.java
index dac5f59..7371e93 100644
--- a/core/src/main/java/net/hydromatic/optiq/rules/java/NestedBlockBuilder.java
+++ b/core/src/main/java/net/hydromatic/optiq/rules/java/NestedBlockBuilder.java
@@ -21,37 +21,21 @@ import net.hydromatic.linq4j.expressions.BlockBuilder;
 
 import org.eigenbase.rex.RexNode;
 
-import java.util.*;
+import java.util.Map;
 
 /**
  * Allows to build nested code blocks with tracking of current context and
  * the nullability of particular {@link org.eigenbase.rex.RexNode} expressions.
  * @see net.hydromatic.optiq.rules.java.StrictAggImplementor#implementAdd(AggContext, AggAddContext)
  */
-public class NestedBlockBuilder {
-  private final List<BlockBuilder> blocks = new ArrayList<BlockBuilder>();
-  private final List<Map<RexNode, Boolean>> nullables =
-      new ArrayList<Map<RexNode, Boolean>>();
-
-  /**
-   * Constructs nested block builders starting of a given code block.
-   * @param block root code block
-   */
-  public NestedBlockBuilder(BlockBuilder block) {
-    nestBlock(block);
-  }
-
+public interface NestedBlockBuilder {
   /**
    * Starts nested code block. The resulting block can optimize expressions
    * and reuse already calculated values from the parent blocks.
    * @return new code block that can optimize expressions and reuse already
    * calculated values from the parent blocks.
    */
-  public final BlockBuilder nestBlock() {
-    BlockBuilder block = new BlockBuilder(true, currentBlock());
-    nestBlock(block, Collections.<RexNode, Boolean>emptyMap());
-    return block;
-  }
+  BlockBuilder nestBlock();
 
   /**
    * Uses given block as the new code context.
@@ -59,9 +43,7 @@ public class NestedBlockBuilder {
    * @param block new code block
    * @see #exitBlock()
    */
-  public final void nestBlock(BlockBuilder block) {
-    nestBlock(block, Collections.<RexNode, Boolean>emptyMap());
-  }
+  void nestBlock(BlockBuilder block);
 
   /**
    * Uses given block as the new code context and the map of nullability.
@@ -70,48 +52,27 @@ public class NestedBlockBuilder {
    * @param nullables map of expression to its nullability state
    * @see #exitBlock()
    */
-  public final void nestBlock(BlockBuilder block,
-      Map<RexNode, Boolean> nullables) {
-    blocks.add(block);
-    Map<RexNode, Boolean> prev = this.nullables.isEmpty()
-        ? Collections.<RexNode, Boolean>emptyMap()
-        : this.nullables.get(this.nullables.size() - 1);
-    Map<RexNode, Boolean> next;
-    if (nullables == null || nullables.isEmpty()) {
-      next = prev;
-    } else {
-      next = new HashMap<RexNode, Boolean>(nullables);
-      next.putAll(prev);
-      next = Collections.unmodifiableMap(next);
-    }
-    this.nullables.add(next);
-  }
+  void nestBlock(BlockBuilder block,
+      Map<RexNode, Boolean> nullables);
 
   /**
    * Returns the current code block
    * @return current code block
    */
-  public final BlockBuilder currentBlock() {
-    return blocks.get(blocks.size() - 1);
-  }
+  BlockBuilder currentBlock();
 
   /**
    * Returns the current nullability state of rex nodes.
    * The resulting value is the summary of all the maps in the block hierarchy.
    * @return current nullability state of rex nodes
    */
-  public final Map<RexNode, Boolean> currentNullables() {
-    return nullables.get(nullables.size() - 1);
-  }
+  Map<RexNode, Boolean> currentNullables();
 
   /**
    * Leaves the current code block.
    * @see #nestBlock()
    */
-  public final void exitBlock() {
-    blocks.remove(blocks.size() - 1);
-    nullables.remove(nullables.size() - 1);
-  }
+  void exitBlock();
 }
 
 // End NestedBlockBuilder.java

http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/99f39682/core/src/main/java/net/hydromatic/optiq/rules/java/NestedBlockBuilderImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/net/hydromatic/optiq/rules/java/NestedBlockBuilderImpl.java b/core/src/main/java/net/hydromatic/optiq/rules/java/NestedBlockBuilderImpl.java
new file mode 100644
index 0000000..0fee5ed
--- /dev/null
+++ b/core/src/main/java/net/hydromatic/optiq/rules/java/NestedBlockBuilderImpl.java
@@ -0,0 +1,117 @@
+/*
+// Licensed to Julian Hyde under one or more contributor license
+// agreements. See the NOTICE file distributed with this work for
+// additional information regarding copyright ownership.
+//
+// Julian Hyde 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 net.hydromatic.optiq.rules.java;
+
+import net.hydromatic.linq4j.expressions.BlockBuilder;
+
+import org.eigenbase.rex.RexNode;
+
+import java.util.*;
+
+/**
+ * Allows to build nested code blocks with tracking of current context and
+ * the nullability of particular {@link org.eigenbase.rex.RexNode} expressions.
+ * @see net.hydromatic.optiq.rules.java.StrictAggImplementor#implementAdd(AggContext, AggAddContext)
+ */
+public class NestedBlockBuilderImpl implements NestedBlockBuilder {
+  private final List<BlockBuilder> blocks = new ArrayList<BlockBuilder>();
+  private final List<Map<RexNode, Boolean>> nullables =
+      new ArrayList<Map<RexNode, Boolean>>();
+
+  /**
+   * Constructs nested block builders starting of a given code block.
+   * @param block root code block
+   */
+  public NestedBlockBuilderImpl(BlockBuilder block) {
+    nestBlock(block);
+  }
+
+  /**
+   * Starts nested code block. The resulting block can optimize expressions
+   * and reuse already calculated values from the parent blocks.
+   * @return new code block that can optimize expressions and reuse already
+   * calculated values from the parent blocks.
+   */
+  public final BlockBuilder nestBlock() {
+    BlockBuilder block = new BlockBuilder(true, currentBlock());
+    nestBlock(block, Collections.<RexNode, Boolean>emptyMap());
+    return block;
+  }
+
+  /**
+   * Uses given block as the new code context.
+   * The current block will be restored after {@link #exitBlock()} call.
+   * @param block new code block
+   * @see #exitBlock()
+   */
+  public final void nestBlock(BlockBuilder block) {
+    nestBlock(block, Collections.<RexNode, Boolean>emptyMap());
+  }
+
+  /**
+   * Uses given block as the new code context and the map of nullability.
+   * The current block will be restored after {@link #exitBlock()} call.
+   * @param block new code block
+   * @param nullables map of expression to its nullability state
+   * @see #exitBlock()
+   */
+  public final void nestBlock(BlockBuilder block,
+      Map<RexNode, Boolean> nullables) {
+    blocks.add(block);
+    Map<RexNode, Boolean> prev = this.nullables.isEmpty()
+        ? Collections.<RexNode, Boolean>emptyMap()
+        : this.nullables.get(this.nullables.size() - 1);
+    Map<RexNode, Boolean> next;
+    if (nullables == null || nullables.isEmpty()) {
+      next = prev;
+    } else {
+      next = new HashMap<RexNode, Boolean>(nullables);
+      next.putAll(prev);
+      next = Collections.unmodifiableMap(next);
+    }
+    this.nullables.add(next);
+  }
+
+  /**
+   * Returns the current code block
+   * @return current code block
+   */
+  public final BlockBuilder currentBlock() {
+    return blocks.get(blocks.size() - 1);
+  }
+
+  /**
+   * Returns the current nullability state of rex nodes.
+   * The resulting value is the summary of all the maps in the block hierarchy.
+   * @return current nullability state of rex nodes
+   */
+  public final Map<RexNode, Boolean> currentNullables() {
+    return nullables.get(nullables.size() - 1);
+  }
+
+  /**
+   * Leaves the current code block.
+   * @see #nestBlock()
+   */
+  public final void exitBlock() {
+    blocks.remove(blocks.size() - 1);
+    nullables.remove(nullables.size() - 1);
+  }
+}
+
+// End NestedBlockBuilder.java

http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/99f39682/core/src/main/java/net/hydromatic/optiq/rules/java/RexImpTable.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/net/hydromatic/optiq/rules/java/RexImpTable.java b/core/src/main/java/net/hydromatic/optiq/rules/java/RexImpTable.java
index e80eeb0..45fc0a0 100644
--- a/core/src/main/java/net/hydromatic/optiq/rules/java/RexImpTable.java
+++ b/core/src/main/java/net/hydromatic/optiq/rules/java/RexImpTable.java
@@ -194,6 +194,9 @@ public class RexImpTable {
     winAggMap.put(FIRST_VALUE,
         constructorSupplier(FirstValueImplementor.class));
     winAggMap.put(LAST_VALUE, constructorSupplier(LastValueImplementor.class));
+    winAggMap.put(LEAD, constructorSupplier(LeadImplementor.class));
+    winAggMap.put(LAG, constructorSupplier(LagImplementor.class));
+    winAggMap.put(NTILE, constructorSupplier(NtileImplementor.class));
     winAggMap.put(COUNT, constructorSupplier(CountWinImplementor.class));
   }
 
@@ -774,7 +777,7 @@ public class RexImpTable {
   }
 
   static class CountWinImplementor extends StrictWinAggImplementor {
-    boolean justPartitionRowCount;
+    boolean justFrameRowCount;
 
     @Override
     public List<Type> getNotNullState(WinAggContext info) {
@@ -786,7 +789,7 @@ public class RexImpTable {
         }
       }
       if (!hasNullable) {
-        justPartitionRowCount = true;
+        justFrameRowCount = true;
         return Collections.emptyList();
       }
       return super.getNotNullState(info);
@@ -794,7 +797,7 @@ public class RexImpTable {
 
     @Override
     public void implementNotNullAdd(WinAggContext info, WinAggAddContext add) {
-      if (justPartitionRowCount) {
+      if (justFrameRowCount) {
         return;
       }
       add.currentBlock().add(Expressions.statement(
@@ -804,8 +807,8 @@ public class RexImpTable {
     @Override
     protected Expression implementNotNullResult(WinAggContext info,
         WinAggResultContext result) {
-      if (justPartitionRowCount) {
-        return result.getPartitionRowCount();
+      if (justFrameRowCount) {
+        return result.getFrameRowCount();
       }
       return super.implementNotNullResult(info, result);
     }
@@ -1034,13 +1037,18 @@ public class RexImpTable {
       // no op
     }
 
+    public boolean needCacheWhenFrameIntact() {
+      return true;
+    }
+
     public Expression implementResult(AggContext info,
         AggResultContext result) {
       WinAggResultContext winResult = (WinAggResultContext) result;
+
       return Expressions.condition(winResult.hasRows(),
-          RexToLixTranslator.convert(
-              winResult.arguments(winResult.computeIndex(
-              Expressions.constant(0), seekType)).get(0), info.returnType()),
+          winResult.rowTranslator(winResult.computeIndex(
+              Expressions.constant(0), seekType)).translate(
+              winResult.rexArguments().get(0), info.returnType()),
           getDefaultValue(info.returnType()));
     }
   }
@@ -1057,6 +1065,126 @@ public class RexImpTable {
     }
   }
 
+  static class LeadLagImplementor implements WinAggImplementor {
+    private final boolean isLead;
+
+    protected LeadLagImplementor(boolean isLead) {
+      this.isLead = isLead;
+    }
+
+    public List<Type> getStateType(AggContext info) {
+      return Collections.emptyList();
+    }
+
+    public void implementReset(AggContext info, AggResetContext reset) {
+      // no op
+    }
+
+    public void implementAdd(AggContext info, AggAddContext add) {
+      // no op
+    }
+
+    public boolean needCacheWhenFrameIntact() {
+      return false;
+    }
+
+    public Expression implementResult(AggContext info,
+        AggResultContext result) {
+      WinAggResultContext winResult = (WinAggResultContext) result;
+
+      List<RexNode> rexArgs = winResult.rexArguments();
+
+      ParameterExpression res = Expressions.parameter(0, info.returnType(),
+          result.currentBlock().newName(isLead ? "lead" : "lag"));
+
+      Expression offset;
+      RexToLixTranslator currentRowTranslator =
+          winResult.rowTranslator(winResult.computeIndex(
+              Expressions.constant(0), SeekType.SET));
+      if (rexArgs.size() >= 2) {
+        // lead(x, offset) or lead(x, offset, default)
+        offset = currentRowTranslator.translate(
+            rexArgs.get(1), int.class);
+      } else {
+        offset = Expressions.constant(1);
+      }
+      if (!isLead) {
+        offset = Expressions.negate(offset);
+      }
+      Expression dstIndex = winResult.computeIndex(offset, SeekType.SET);
+
+      Expression rowInRange = winResult.rowInPartition(dstIndex);
+
+      BlockBuilder thenBlock = result.nestBlock();
+      Expression lagResult = winResult.rowTranslator(dstIndex).translate(
+          rexArgs.get(0), res.type);
+      thenBlock.add(Expressions.statement(Expressions.assign(res, lagResult)));
+      result.exitBlock();
+      BlockStatement thenBranch = thenBlock.toBlock();
+
+      Expression defaultValue = rexArgs.size() == 3
+          ? currentRowTranslator.translate(rexArgs.get(2), res.type)
+          : getDefaultValue(res.type);
+
+      result.currentBlock().add(Expressions.declare(0, res, null));
+      result.currentBlock().add(Expressions.ifThenElse(rowInRange, thenBranch,
+          Expressions.statement(Expressions.assign(res, defaultValue))));
+      return res;
+    }
+  }
+
+  public static class LeadImplementor extends LeadLagImplementor {
+    protected LeadImplementor() {
+      super(true);
+    }
+  }
+
+  public static class LagImplementor extends LeadLagImplementor {
+    protected LagImplementor() {
+      super(false);
+    }
+  }
+
+  static class NtileImplementor implements WinAggImplementor {
+    public List<Type> getStateType(AggContext info) {
+      return Collections.emptyList();
+    }
+
+    public void implementReset(AggContext info, AggResetContext reset) {
+      // no op
+    }
+
+    public void implementAdd(AggContext info, AggAddContext add) {
+      // no op
+    }
+
+    public boolean needCacheWhenFrameIntact() {
+      return false;
+    }
+
+    public Expression implementResult(AggContext info,
+        AggResultContext result) {
+      WinAggResultContext winResult = (WinAggResultContext) result;
+
+      List<RexNode> rexArgs = winResult.rexArguments();
+
+      Expression tiles =
+          winResult.rowTranslator(winResult.index()).translate(
+              rexArgs.get(0), int.class);
+
+      Expression ntile =
+          Expressions.add(Expressions.constant(1),
+              Expressions.divide(
+                  Expressions.multiply(
+                      tiles,
+                      Expressions.subtract(
+                          winResult.index(), winResult.startIndex())),
+                  winResult.getPartitionRowCount()));
+
+      return ntile;
+    }
+  }
+
   static class RowNumberImplementor extends StrictWinAggImplementor {
     @Override
     public List<Type> getNotNullState(WinAggContext info) {

http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/99f39682/core/src/main/java/net/hydromatic/optiq/rules/java/RexToLixTranslator.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/net/hydromatic/optiq/rules/java/RexToLixTranslator.java b/core/src/main/java/net/hydromatic/optiq/rules/java/RexToLixTranslator.java
index d15e140..34cf83e 100644
--- a/core/src/main/java/net/hydromatic/optiq/rules/java/RexToLixTranslator.java
+++ b/core/src/main/java/net/hydromatic/optiq/rules/java/RexToLixTranslator.java
@@ -328,6 +328,12 @@ public class RexToLixTranslator {
       Expression x = inputGetter.field(list, index, storageType);
 
       Expression input = list.append("inp" + index + "_", x); // safe to share
+      if (nullAs == RexImpTable.NullAs.NOT_POSSIBLE
+          && input.type.equals(storageType)) {
+        // When we asked for not null input that would be stored as box, avoid
+        // unboxing via nullAs.handle below.
+        return input;
+      }
       Expression nullHandled = nullAs.handle(input);
 
       // If we get ConstantExpression, just return it (i.e. primitive false)
@@ -525,7 +531,7 @@ public class RexToLixTranslator {
    *
    * @return translated expressions
    */
-  List<Expression> translateList(List<? extends RexNode> operandList) {
+  public List<Expression> translateList(List<? extends RexNode> operandList) {
     return translateList(operandList, null);
   }
 
@@ -543,7 +549,7 @@ public class RexToLixTranslator {
    *
    * @return translated expressions
    */
-  List<Expression> translateList(List<? extends RexNode> operandList,
+  public List<Expression> translateList(List<? extends RexNode> operandList,
       List<? extends Type> storageTypes) {
     final List<Expression> list = new ArrayList<Expression>(operandList.size());
 

http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/99f39682/core/src/main/java/net/hydromatic/optiq/rules/java/StrictAggImplementor.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/net/hydromatic/optiq/rules/java/StrictAggImplementor.java b/core/src/main/java/net/hydromatic/optiq/rules/java/StrictAggImplementor.java
index c973e56..d1e96ae 100644
--- a/core/src/main/java/net/hydromatic/optiq/rules/java/StrictAggImplementor.java
+++ b/core/src/main/java/net/hydromatic/optiq/rules/java/StrictAggImplementor.java
@@ -64,9 +64,7 @@ public abstract class StrictAggImplementor implements AggImplementor {
         break;
       }
     }
-    trackNullsPerRow =
-        !(info instanceof WinAggImplementor.WinAggContext)
-            || hasNullableArgs;
+    trackNullsPerRow = !(info instanceof WinAggContext) || hasNullableArgs;
 
     List<Type> res = new ArrayList<Type>(subState.size() + 1);
     res.addAll(subState);

http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/99f39682/core/src/main/java/net/hydromatic/optiq/rules/java/StrictWinAggImplementor.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/net/hydromatic/optiq/rules/java/StrictWinAggImplementor.java b/core/src/main/java/net/hydromatic/optiq/rules/java/StrictWinAggImplementor.java
index 805b557..56257c6 100644
--- a/core/src/main/java/net/hydromatic/optiq/rules/java/StrictWinAggImplementor.java
+++ b/core/src/main/java/net/hydromatic/optiq/rules/java/StrictWinAggImplementor.java
@@ -78,4 +78,8 @@ public abstract class StrictWinAggImplementor extends StrictAggImplementor
     return implementNotNullResult((WinAggContext) info,
         (WinAggResultContext) result);
   }
+
+  public boolean needCacheWhenFrameIntact() {
+    return true;
+  }
 }

http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/99f39682/core/src/main/java/net/hydromatic/optiq/rules/java/WinAggAddContext.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/net/hydromatic/optiq/rules/java/WinAggAddContext.java b/core/src/main/java/net/hydromatic/optiq/rules/java/WinAggAddContext.java
index 41cf6c6..e00587f 100644
--- a/core/src/main/java/net/hydromatic/optiq/rules/java/WinAggAddContext.java
+++ b/core/src/main/java/net/hydromatic/optiq/rules/java/WinAggAddContext.java
@@ -17,51 +17,27 @@
 */
 package net.hydromatic.optiq.rules.java;
 
-import net.hydromatic.linq4j.expressions.BlockBuilder;
 import net.hydromatic.linq4j.expressions.Expression;
-import net.hydromatic.linq4j.expressions.Expressions;
-
-import java.util.List;
 
 /**
  * Information for a call to {@link AggImplementor#implementAdd(AggContext, AggAddContext)}.
  * {@link WinAggAddContext} is used when implementing windowed aggregate.
- * Note: logically, {@link WinAggAddContext} should extend {@link WinAggResultContext},
- * however this would prohibit usage of the same {@link AggImplementor} for both
- * regular aggregate and window aggregate.
  * Typically, the aggregation implementation will use {@link #arguments()}
  * or {@link #rexArguments()} to update aggregate value.
- * @see net.hydromatic.optiq.rules.java.AggAddContext
+ * @see AggAddContext
  */
-public abstract class WinAggAddContext
-    extends AggAddContext
-    implements WinAggImplementor.WinAggFrameContext,
-      WinAggImplementor.WinAggFrameResultContext {
-  public WinAggAddContext(BlockBuilder block, List<Expression> accumulator) {
-    super(block, accumulator);
-  }
-
+public interface WinAggAddContext extends AggAddContext, WinAggResultContext {
   /**
    * Returns current position inside for-loop of window aggregate.
-   * Note, the position is relative to {@link WinAggImplementor.WinAggFrameContext#startIndex()}.
+   * Note, the position is relative to {@link WinAggFrameContext#startIndex()}.
    * This is NOT current row as in "rows between current row".
    * If you need to know the relative index of the current row in the partition,
-   * use {@link WinAggImplementor.WinAggFrameContext#index()}.
+   * use {@link WinAggFrameContext#index()}.
    * @return current position inside for-loop of window aggregate.
-   * @see WinAggImplementor.WinAggFrameContext#index()
-   * @see WinAggImplementor.WinAggFrameContext#startIndex()
+   * @see WinAggFrameContext#index()
+   * @see WinAggFrameContext#startIndex()
    */
-  public abstract Expression currentPosition();
-
-  public List<Expression> arguments(Expression rowIndex) {
-    return rowTranslator(rowIndex).translateList(rexArguments());
-  }
-
-  @Override
-  public final RexToLixTranslator rowTranslator() {
-    return rowTranslator(computeIndex(Expressions.constant(0),
-        WinAggImplementor.SeekType.AGG_INDEX));
-  }
+  Expression currentPosition();
 }
 
 // End WinAggAddContext.java

http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/99f39682/core/src/main/java/net/hydromatic/optiq/rules/java/WinAggContext.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/net/hydromatic/optiq/rules/java/WinAggContext.java b/core/src/main/java/net/hydromatic/optiq/rules/java/WinAggContext.java
new file mode 100644
index 0000000..281aade
--- /dev/null
+++ b/core/src/main/java/net/hydromatic/optiq/rules/java/WinAggContext.java
@@ -0,0 +1,27 @@
+/*
+// Licensed to Julian Hyde under one or more contributor license
+// agreements. See the NOTICE file distributed with this work for
+// additional information regarding copyright ownership.
+//
+// Julian Hyde 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 net.hydromatic.optiq.rules.java;
+
+/**
+ * Marker interface to allow {@link net.hydromatic.optiq.rules.java.AggImplementor} to tell if it is used in
+ * regular or windowed context.
+ */
+public interface WinAggContext extends AggContext {
+}
+
+// End WinAggContext.java

http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/99f39682/core/src/main/java/net/hydromatic/optiq/rules/java/WinAggFrameContext.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/net/hydromatic/optiq/rules/java/WinAggFrameContext.java b/core/src/main/java/net/hydromatic/optiq/rules/java/WinAggFrameContext.java
new file mode 100644
index 0000000..c5db249
--- /dev/null
+++ b/core/src/main/java/net/hydromatic/optiq/rules/java/WinAggFrameContext.java
@@ -0,0 +1,73 @@
+/*
+// Licensed to Julian Hyde under one or more contributor license
+// agreements. See the NOTICE file distributed with this work for
+// additional information regarding copyright ownership.
+//
+// Julian Hyde 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 net.hydromatic.optiq.rules.java;
+
+import net.hydromatic.linq4j.expressions.Expression;
+
+/**
+ * Provides information on the current window.
+ * All the indexes are ready to be used in {@link WinAggFrameResultContext#arguments(net.hydromatic.linq4j.expressions.Expression)},
+ * {@link WinAggFrameResultContext#rowTranslator(net.hydromatic.linq4j.expressions.Expression)} and similar methods.
+ */
+public interface WinAggFrameContext {
+  /**
+   * Returns the index of the current row in the partition.
+   * In other words, it is close to ~ROWS BETWEEN CURRENT ROW.
+   * Note to use {@link #startIndex()} when you need zero-based row position.
+   * @return the index of the very first row in partition
+   */
+  Expression index();
+
+  /**
+   * Returns the index of the very first row in partition.
+   * @return index of the very first row in partition
+   */
+  Expression startIndex();
+
+  /**
+   * Returns the index of the very last row in partition.
+   * @return index of the very last row in partition
+   */
+  Expression endIndex();
+
+  /**
+   * Returns the boolean expression that tells if the partition has rows.
+   * The partition might lack rows in cases like ROWS BETWEEN 1000 PRECEDING
+   * AND 900 PRECEDING.
+   * @return boolean expression that tells if the partition has rows
+   */
+  Expression hasRows();
+
+  /**
+   * Returns the number of rows in the current frame (subject to framing
+   * clause).
+   * @return number of rows in the current partition or 0 if the partition
+   *   is empty
+   */
+  Expression getFrameRowCount();
+
+  /**
+   * Returns the number of rows in the current partition (as determined by
+   * PARTITION BY clause).
+   * @return number of rows in the current partition or 0 if the partition
+   *   is empty
+   */
+  Expression getPartitionRowCount();
+}
+
+// End WinAggFrameContext.java

http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/99f39682/core/src/main/java/net/hydromatic/optiq/rules/java/WinAggFrameResultContext.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/net/hydromatic/optiq/rules/java/WinAggFrameResultContext.java b/core/src/main/java/net/hydromatic/optiq/rules/java/WinAggFrameResultContext.java
new file mode 100644
index 0000000..d648fb4
--- /dev/null
+++ b/core/src/main/java/net/hydromatic/optiq/rules/java/WinAggFrameResultContext.java
@@ -0,0 +1,69 @@
+/*
+// Licensed to Julian Hyde under one or more contributor license
+// agreements. See the NOTICE file distributed with this work for
+// additional information regarding copyright ownership.
+//
+// Julian Hyde 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 net.hydromatic.optiq.rules.java;
+
+import net.hydromatic.linq4j.expressions.Expression;
+
+/**
+ * Provides information on the current window when computing the result of
+ * the aggregation.
+ */
+public interface WinAggFrameResultContext extends WinAggFrameContext {
+  /**
+   * Converts absolute index position of the given relative position.
+   * @param offset offset of the requested row
+   * @param seekType the type of offset (start of window, end of window, etc)
+   * @return absolute position of the requested row
+   */
+  Expression computeIndex(Expression offset,
+      WinAggImplementor.SeekType seekType);
+
+  /**
+   * Returns boolean the expression that checks if the given index is in
+   * the frame bounds.
+   * @param rowIndex index if the row to check
+   * @return expression that validates frame bounds for the given index
+   */
+  Expression rowInFrame(Expression rowIndex);
+
+  /**
+   * Returns boolean the expression that checks if the given index is in
+   * the partition bounds.
+   * @param rowIndex index if the row to check
+   * @return expression that validates partition bounds for the given index
+   */
+  Expression rowInPartition(Expression rowIndex);
+
+  /**
+   * Returns row translator for given absolute row position.
+   * @param rowIndex absolute index of the row.
+   * @return translator for the requested row
+   */
+  RexToLixTranslator rowTranslator(Expression rowIndex);
+
+  /**
+   * Compares two rows given by absolute positions according to the order
+   * collation of the current window.
+   * @param a absolute index of the first row
+   * @param b absolute index of the second row
+   * @return result of comparison as as in {@link Comparable#compareTo}
+   */
+  Expression compareRows(Expression a, Expression b);
+}
+
+// End WinAggFrameResultContext.java

http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/99f39682/core/src/main/java/net/hydromatic/optiq/rules/java/WinAggImplementor.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/net/hydromatic/optiq/rules/java/WinAggImplementor.java b/core/src/main/java/net/hydromatic/optiq/rules/java/WinAggImplementor.java
index 5190a30..34f8389 100644
--- a/core/src/main/java/net/hydromatic/optiq/rules/java/WinAggImplementor.java
+++ b/core/src/main/java/net/hydromatic/optiq/rules/java/WinAggImplementor.java
@@ -17,18 +17,14 @@
 */
 package net.hydromatic.optiq.rules.java;
 
-import net.hydromatic.linq4j.expressions.Expression;
-
-import org.eigenbase.rex.RexNode;
-
-import java.util.List;
-
 /**
  * Implements a windowed aggregate function by generating expressions to
  * initialize, add to, and get a result from, an accumulator.
  * Windowed aggregate is more powerful than regular aggregate since it can
  * access rows in the current partition by row indices.
  * Regular aggregate can be used to implement windowed aggregate.
+ * <p>This interface does not define new methods: window-specific
+ * sub-interfaces are passed when implementing window aggregate.
  *
  * @see net.hydromatic.optiq.rules.java.StrictWinAggImplementor
  * @see net.hydromatic.optiq.rules.java.RexImpTable.FirstLastValueImplementor
@@ -64,103 +60,7 @@ public interface WinAggImplementor extends AggImplementor {
     END
   }
 
-  /**
-   * Marker interface to allow {@link AggImplementor} to tell if it is used in
-   * regular or windowed context.
-   */
-  public interface WinAggContext extends AggContext {
-  }
-
-  /**
-   * Provides information on the current window.
-   * All the indexes are ready to be used in {@link WinAggFrameResultContext#arguments(Expression)},
-   * {@link WinAggFrameResultContext#rowTranslator(Expression)} and similar methods.
-   */
-  public interface WinAggFrameContext {
-    /**
-     * Returns the index of the current row in the partition.
-     * In other words, it is close to ~ROWS BETWEEN CURRENT ROW.
-     * Note to use {@link #startIndex()} when you need zero-based row position.
-     * @return the index of the very first row in partition
-     */
-    Expression index();
-
-    /**
-     * Returns the index of the very first row in partition.
-     * @return index of the very first row in partition
-     */
-    Expression startIndex();
-
-    /**
-     * Returns the index of the very last row in partition.
-     * @return index of the very last row in partition
-     */
-    Expression endIndex();
-
-    /**
-     * Returns the boolean expression that tells if the partition has rows.
-     * The partition might lack rows in cases like ROWS BETWEEN 1000 PRECEDING
-     * AND 900 PRECEDING.
-     * @return boolean expression that tells if the partition has rows
-     */
-    Expression hasRows();
-
-    /**
-     * Returns the number of rows in the current partition.
-     * @return number of rows in the current partition or 0 if the partition
-     *   is empty
-     */
-    Expression getPartitionRowCount();
-  }
-
-  /**
-   * Provides information on the current window when computing the result of
-   * the aggregation.
-   */
-  public interface WinAggFrameResultContext {
-    /**
-     * Returns {@link RexNode} representation of arguments.
-     * This can be useful for manual translation of required arguments with
-     * different {@link net.hydromatic.optiq.rules.java.NullPolicy}.
-     * @return {@link RexNode} representation of arguments
-     */
-    List<RexNode> rexArguments();
-
-    /**
-     * Returns Linq4j form of arguments.
-     * The resulting value is equivalent to
-     * {@code rowTranslator().translateList(rexArguments())}.
-     * This is handy if you need just operate on argument.
-     * @param rowIndex index of the requested row. The index must be in range
-     *                 of partition's startIndex and endIndex.
-     * @return Linq4j form of arguments of the particular row
-     */
-    List<Expression> arguments(Expression rowIndex);
-
-    /**
-     * Converts absolute index position of the given relative position.
-     * @param offset offset of the requested row
-     * @param seekType the type of offset (start of window, end of window, etc)
-     * @return absolute position of the requested row
-     */
-    Expression computeIndex(Expression offset, SeekType seekType);
-
-    /**
-     * Returns row translator for given absolute row position.
-     * @param rowIndex absolute index of the row.
-     * @return translator for the requested row
-     */
-    RexToLixTranslator rowTranslator(Expression rowIndex);
-
-    /**
-     * Compares two rows given by absolute positions according to the order
-     * collation of the current window.
-     * @param a absolute index of the first row
-     * @param b absolute index of the second row
-     * @return result of comparison as as in {@link Comparable#compareTo}
-     */
-    Expression compareRows(Expression a, Expression b);
-  }
+  boolean needCacheWhenFrameIntact();
 }
 
 // End WinAggImplementor.java

http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/99f39682/core/src/main/java/net/hydromatic/optiq/rules/java/WinAggResetContext.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/net/hydromatic/optiq/rules/java/WinAggResetContext.java b/core/src/main/java/net/hydromatic/optiq/rules/java/WinAggResetContext.java
index bfbe5ab..0baff8b 100644
--- a/core/src/main/java/net/hydromatic/optiq/rules/java/WinAggResetContext.java
+++ b/core/src/main/java/net/hydromatic/optiq/rules/java/WinAggResetContext.java
@@ -17,11 +17,6 @@
 */
 package net.hydromatic.optiq.rules.java;
 
-import net.hydromatic.linq4j.expressions.BlockBuilder;
-import net.hydromatic.linq4j.expressions.Expression;
-
-import java.util.List;
-
 /**
  * Information for a call to {@link AggImplementor#implementReset(AggContext, AggResetContext)}.
  * {@link AggResetContext} provides access to the accumulator variables
@@ -31,57 +26,8 @@ import java.util.List;
  * In other words, the implementation should treat indices and partition row
  * count as a hint to pre-size the collections.
  */
-public class WinAggResetContext extends AggResetContext
-    implements WinAggImplementor.WinAggFrameContext {
-  private final Expression index;
-  private final Expression startIndex;
-  private final Expression endIndex;
-  private final Expression partitionRowCount;
-  private final Expression hasRows;
-
-  /**
-   * Creates window aggregate reset context.
-   * @param block code block that will contain the added initialization
-   * @param accumulator accumulator variables that store the intermediate
-   *                    aggregate state
-   * @param index index of the current row in the partition
-   * @param startIndex index of the very first row in partition
-   * @param endIndex index of the very last row in partition
-   * @param hasRows boolean expression that tells if the partition has rows
-   * @param partitionRowCount number of rows in the current partition or
-   *                          0 if the partition is empty
-   *
-   */
-  public WinAggResetContext(BlockBuilder block,
-      List<Expression> accumulator, Expression index,
-      Expression startIndex, Expression endIndex,
-      Expression hasRows, Expression partitionRowCount) {
-    super(block, accumulator);
-    this.index = index;
-    this.startIndex = startIndex;
-    this.endIndex = endIndex;
-    this.partitionRowCount = partitionRowCount;
-    this.hasRows = hasRows;
-  }
-
-  public Expression index() {
-    return index;
-  }
-
-  public Expression startIndex() {
-    return startIndex;
-  }
-
-  public Expression endIndex() {
-    return endIndex;
-  }
-
-  public Expression hasRows() {
-    return hasRows;
-  }
-
-  public Expression getPartitionRowCount() {
-    return partitionRowCount;
-  }
+public interface WinAggResetContext
+    extends AggResetContext, WinAggFrameContext {
 }
 
+// End WinAggResetContext.java

http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/99f39682/core/src/main/java/net/hydromatic/optiq/rules/java/WinAggResultContext.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/net/hydromatic/optiq/rules/java/WinAggResultContext.java b/core/src/main/java/net/hydromatic/optiq/rules/java/WinAggResultContext.java
index 9aa5a68..7d4131f 100644
--- a/core/src/main/java/net/hydromatic/optiq/rules/java/WinAggResultContext.java
+++ b/core/src/main/java/net/hydromatic/optiq/rules/java/WinAggResultContext.java
@@ -17,39 +17,38 @@
 */
 package net.hydromatic.optiq.rules.java;
 
-import net.hydromatic.linq4j.expressions.BlockBuilder;
 import net.hydromatic.linq4j.expressions.Expression;
 
+import org.eigenbase.rex.RexNode;
+
 import java.util.List;
 
 /**
  * Information for a call to {@link AggImplementor#implementResult(AggContext, AggResultContext)}
  * Typically, the aggregation implementation will convert {@link #accumulator()}
  * to the resulting value of the aggregation.
- * The implementation MUST NOT destroy the cotents of {@link #accumulator()}.
- * Note: logically, {@link WinAggResultContext} should extend {@link WinAggResetContext},
- * however this would prohibit usage of the same {@link AggImplementor} for both
- * regular aggregate and window aggregate.
+ * The implementation MUST NOT destroy the contents of {@link #accumulator()}.
  */
-public abstract class WinAggResultContext
-    extends AggResultContext
-    implements WinAggImplementor.WinAggFrameContext,
-      WinAggImplementor.WinAggFrameResultContext {
-
+public interface WinAggResultContext extends AggResultContext,
+    WinAggFrameResultContext {
   /**
-   * Creates window aggregate result context.
-   * @param block code block that will contain the added initialization
-   * @param accumulator accumulator variables that store the intermediate
-   *                    aggregate state
+   * Returns {@link org.eigenbase.rex.RexNode} representation of arguments.
+   * This can be useful for manual translation of required arguments with
+   * different {@link NullPolicy}.
+   * @return {@link org.eigenbase.rex.RexNode} representation of arguments
    */
-  public WinAggResultContext(BlockBuilder block,
-      List<Expression> accumulator) {
-    super(block, accumulator);
-  }
+  List<RexNode> rexArguments();
 
-  public final List<Expression> arguments(Expression rowIndex) {
-    return rowTranslator(rowIndex).translateList(rexArguments());
-  }
+  /**
+   * Returns Linq4j form of arguments.
+   * The resulting value is equivalent to
+   * {@code rowTranslator().translateList(rexArguments())}.
+   * This is handy if you need just operate on argument.
+   * @param rowIndex index of the requested row. The index must be in range
+   *                 of partition's startIndex and endIndex.
+   * @return Linq4j form of arguments of the particular row
+   */
+  List<Expression> arguments(Expression rowIndex);
 }
 
 // End WinAggResultContext.java

http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/99f39682/core/src/main/java/net/hydromatic/optiq/rules/java/impl/AggAddContextImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/net/hydromatic/optiq/rules/java/impl/AggAddContextImpl.java b/core/src/main/java/net/hydromatic/optiq/rules/java/impl/AggAddContextImpl.java
new file mode 100644
index 0000000..a88ce18
--- /dev/null
+++ b/core/src/main/java/net/hydromatic/optiq/rules/java/impl/AggAddContextImpl.java
@@ -0,0 +1,39 @@
+/*
+// Licensed to Julian Hyde under one or more contributor license
+// agreements. See the NOTICE file distributed with this work for
+// additional information regarding copyright ownership.
+//
+// Julian Hyde 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 net.hydromatic.optiq.rules.java.impl;
+
+import net.hydromatic.linq4j.expressions.BlockBuilder;
+import net.hydromatic.linq4j.expressions.Expression;
+
+import net.hydromatic.optiq.rules.java.AggAddContext;
+
+import java.util.List;
+
+/**
+ * Implementation of {@link net.hydromatic.optiq.rules.java.AggAddContext}.
+ */
+public abstract class AggAddContextImpl extends AggResultContextImpl
+    implements AggAddContext {
+  public AggAddContextImpl(BlockBuilder block, List<Expression> accumulator) {
+    super(block, accumulator);
+  }
+
+  public final List<Expression> arguments() {
+    return rowTranslator().translateList(rexArguments());
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/99f39682/core/src/main/java/net/hydromatic/optiq/rules/java/impl/AggResetContextImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/net/hydromatic/optiq/rules/java/impl/AggResetContextImpl.java b/core/src/main/java/net/hydromatic/optiq/rules/java/impl/AggResetContextImpl.java
new file mode 100644
index 0000000..93d518f
--- /dev/null
+++ b/core/src/main/java/net/hydromatic/optiq/rules/java/impl/AggResetContextImpl.java
@@ -0,0 +1,51 @@
+/*
+// Licensed to Julian Hyde under one or more contributor license
+// agreements. See the NOTICE file distributed with this work for
+// additional information regarding copyright ownership.
+//
+// Julian Hyde 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 net.hydromatic.optiq.rules.java.impl;
+
+import net.hydromatic.linq4j.expressions.BlockBuilder;
+import net.hydromatic.linq4j.expressions.Expression;
+
+import net.hydromatic.optiq.rules.java.AggResetContext;
+import net.hydromatic.optiq.rules.java.NestedBlockBuilderImpl;
+
+import java.util.List;
+
+/**
+ * Implementation of {@link net.hydromatic.optiq.rules.java.AggResetContext}
+ */
+public class AggResetContextImpl extends NestedBlockBuilderImpl
+    implements AggResetContext {
+  private final List<Expression> accumulator;
+
+  /**
+   * Creates aggregate reset context
+   * @param block code block that will contain the added initialization
+   * @param accumulator accumulator variables that store the intermediate
+   *                    aggregate state
+   */
+  public AggResetContextImpl(BlockBuilder block, List<Expression> accumulator) {
+    super(block);
+    this.accumulator = accumulator;
+  }
+
+  public List<Expression> accumulator() {
+    return accumulator;
+  }
+}
+
+// End AggResetContext.java

http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/99f39682/core/src/main/java/net/hydromatic/optiq/rules/java/impl/AggResultContextImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/net/hydromatic/optiq/rules/java/impl/AggResultContextImpl.java b/core/src/main/java/net/hydromatic/optiq/rules/java/impl/AggResultContextImpl.java
new file mode 100644
index 0000000..43d9190
--- /dev/null
+++ b/core/src/main/java/net/hydromatic/optiq/rules/java/impl/AggResultContextImpl.java
@@ -0,0 +1,44 @@
+/*
+// Licensed to Julian Hyde under one or more contributor license
+// agreements. See the NOTICE file distributed with this work for
+// additional information regarding copyright ownership.
+//
+// Julian Hyde 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 net.hydromatic.optiq.rules.java.impl;
+
+import net.hydromatic.linq4j.expressions.BlockBuilder;
+import net.hydromatic.linq4j.expressions.Expression;
+
+import net.hydromatic.optiq.rules.java.AggResultContext;
+
+import java.util.List;
+
+/**
+ * Implementation of {@link net.hydromatic.optiq.rules.java.AggResultContext}
+ */
+public class AggResultContextImpl extends AggResetContextImpl
+    implements AggResultContext {
+  /**
+   * Creates aggregate result context
+   * @param block code block that will contain the result calculation statements
+   * @param accumulator accumulator variables that store the intermediate
+   *                    aggregate state
+   */
+  public AggResultContextImpl(BlockBuilder block,
+      List<Expression> accumulator) {
+    super(block, accumulator);
+  }
+}
+
+// End AggResultContext.java

http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/99f39682/core/src/main/java/net/hydromatic/optiq/rules/java/impl/WinAggAddContextImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/net/hydromatic/optiq/rules/java/impl/WinAggAddContextImpl.java b/core/src/main/java/net/hydromatic/optiq/rules/java/impl/WinAggAddContextImpl.java
new file mode 100644
index 0000000..1533415
--- /dev/null
+++ b/core/src/main/java/net/hydromatic/optiq/rules/java/impl/WinAggAddContextImpl.java
@@ -0,0 +1,53 @@
+/*
+// Licensed to Julian Hyde under one or more contributor license
+// agreements. See the NOTICE file distributed with this work for
+// additional information regarding copyright ownership.
+//
+// Julian Hyde 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 net.hydromatic.optiq.rules.java.impl;
+
+import net.hydromatic.linq4j.expressions.BlockBuilder;
+import net.hydromatic.linq4j.expressions.Expression;
+import net.hydromatic.linq4j.expressions.Expressions;
+
+import net.hydromatic.optiq.rules.java.RexToLixTranslator;
+import net.hydromatic.optiq.rules.java.WinAggAddContext;
+import net.hydromatic.optiq.rules.java.WinAggFrameResultContext;
+import net.hydromatic.optiq.rules.java.WinAggImplementor;
+
+import com.google.common.base.Function;
+
+import java.util.List;
+
+/**
+ * Implementation of {@link net.hydromatic.optiq.rules.java.WinAggAddContext}.
+ */
+public abstract class WinAggAddContextImpl extends WinAggResultContextImpl
+    implements WinAggAddContext {
+  public WinAggAddContextImpl(BlockBuilder block, List<Expression> accumulator,
+      Function<BlockBuilder, WinAggFrameResultContext> frame) {
+    super(block, accumulator, frame);
+  }
+
+  public final RexToLixTranslator rowTranslator() {
+    return rowTranslator(computeIndex(Expressions.constant(0),
+        WinAggImplementor.SeekType.AGG_INDEX));
+  }
+
+  public final List<Expression> arguments() {
+    return rowTranslator().translateList(rexArguments());
+  }
+}
+
+// End WinAggAddContext.java

http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/99f39682/core/src/main/java/net/hydromatic/optiq/rules/java/impl/WinAggResetContextImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/net/hydromatic/optiq/rules/java/impl/WinAggResetContextImpl.java b/core/src/main/java/net/hydromatic/optiq/rules/java/impl/WinAggResetContextImpl.java
new file mode 100644
index 0000000..15ed040
--- /dev/null
+++ b/core/src/main/java/net/hydromatic/optiq/rules/java/impl/WinAggResetContextImpl.java
@@ -0,0 +1,89 @@
+/*
+// Licensed to Julian Hyde under one or more contributor license
+// agreements. See the NOTICE file distributed with this work for
+// additional information regarding copyright ownership.
+//
+// Julian Hyde 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 net.hydromatic.optiq.rules.java.impl;
+
+import net.hydromatic.linq4j.expressions.BlockBuilder;
+import net.hydromatic.linq4j.expressions.Expression;
+
+import net.hydromatic.optiq.rules.java.WinAggResetContext;
+
+import java.util.List;
+
+/**
+ * Implementation of {@link net.hydromatic.optiq.rules.java.WinAggResetContext}.
+ */
+public class WinAggResetContextImpl extends AggResetContextImpl
+    implements WinAggResetContext {
+  private final Expression index;
+  private final Expression startIndex;
+  private final Expression endIndex;
+  private final Expression frameRowCount;
+  private final Expression partitionRowCount;
+  private final Expression hasRows;
+
+  /**
+   * Creates window aggregate reset context.
+   * @param block code block that will contain the added initialization
+   * @param accumulator accumulator variables that store the intermediate
+   *                    aggregate state
+   * @param index index of the current row in the partition
+   * @param startIndex index of the very first row in partition
+   * @param endIndex index of the very last row in partition
+   * @param hasRows boolean expression that tells if the partition has rows
+   * @param frameRowCount number of rows in the current frame
+   * @param partitionRowCount number of rows in the current partition
+   */
+  public WinAggResetContextImpl(BlockBuilder block,
+      List<Expression> accumulator, Expression index,
+      Expression startIndex, Expression endIndex,
+      Expression hasRows,
+      Expression frameRowCount, Expression partitionRowCount) {
+    super(block, accumulator);
+    this.index = index;
+    this.startIndex = startIndex;
+    this.endIndex = endIndex;
+    this.frameRowCount = frameRowCount;
+    this.partitionRowCount = partitionRowCount;
+    this.hasRows = hasRows;
+  }
+
+  public Expression index() {
+    return index;
+  }
+
+  public Expression startIndex() {
+    return startIndex;
+  }
+
+  public Expression endIndex() {
+    return endIndex;
+  }
+
+  public Expression hasRows() {
+    return hasRows;
+  }
+
+  public Expression getFrameRowCount() {
+    return frameRowCount;
+  }
+
+  public Expression getPartitionRowCount() {
+    return partitionRowCount;
+  }
+}
+

http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/99f39682/core/src/main/java/net/hydromatic/optiq/rules/java/impl/WinAggResultContextImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/net/hydromatic/optiq/rules/java/impl/WinAggResultContextImpl.java b/core/src/main/java/net/hydromatic/optiq/rules/java/impl/WinAggResultContextImpl.java
new file mode 100644
index 0000000..f29cbd4
--- /dev/null
+++ b/core/src/main/java/net/hydromatic/optiq/rules/java/impl/WinAggResultContextImpl.java
@@ -0,0 +1,108 @@
+/*
+// Licensed to Julian Hyde under one or more contributor license
+// agreements. See the NOTICE file distributed with this work for
+// additional information regarding copyright ownership.
+//
+// Julian Hyde 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 net.hydromatic.optiq.rules.java.impl;
+
+import net.hydromatic.linq4j.expressions.BlockBuilder;
+import net.hydromatic.linq4j.expressions.Expression;
+
+import net.hydromatic.optiq.rules.java.RexToLixTranslator;
+import net.hydromatic.optiq.rules.java.WinAggFrameResultContext;
+import net.hydromatic.optiq.rules.java.WinAggImplementor;
+import net.hydromatic.optiq.rules.java.WinAggResultContext;
+
+import com.google.common.base.Function;
+
+import java.util.List;
+
+/**
+ * Implementation of {@link net.hydromatic.optiq.rules.java.WinAggResultContext}.
+ */
+public abstract class WinAggResultContextImpl extends AggResultContextImpl
+    implements WinAggResultContext {
+
+  private final Function<BlockBuilder, WinAggFrameResultContext> frame;
+
+  /**
+   * Creates window aggregate result context.
+   * @param block code block that will contain the added initialization
+   * @param accumulator accumulator variables that store the intermediate
+   *                    aggregate state
+   */
+  public WinAggResultContextImpl(BlockBuilder block,
+      List<Expression> accumulator,
+      Function<BlockBuilder, WinAggFrameResultContext> frameContextBuilder) {
+    super(block, accumulator);
+    this.frame = frameContextBuilder;
+  }
+
+  private WinAggFrameResultContext getFrame() {
+    return frame.apply(currentBlock());
+  }
+
+  public final List<Expression> arguments(Expression rowIndex) {
+    return rowTranslator(rowIndex).translateList(rexArguments());
+  }
+
+  public Expression computeIndex(Expression offset,
+      WinAggImplementor.SeekType seekType) {
+    return getFrame().computeIndex(offset, seekType);
+  }
+
+  public Expression rowInFrame(Expression rowIndex) {
+    return getFrame().rowInFrame(rowIndex);
+  }
+
+  public Expression rowInPartition(Expression rowIndex) {
+    return getFrame().rowInPartition(rowIndex);
+  }
+
+  public RexToLixTranslator rowTranslator(Expression rowIndex) {
+    return getFrame().rowTranslator(rowIndex)
+        .setNullable(currentNullables());
+  }
+
+  public Expression compareRows(Expression a, Expression b) {
+    return getFrame().compareRows(a, b);
+  }
+
+  public Expression index() {
+    return getFrame().index();
+  }
+
+  public Expression startIndex() {
+    return getFrame().startIndex();
+  }
+
+  public Expression endIndex() {
+    return getFrame().endIndex();
+  }
+
+  public Expression hasRows() {
+    return getFrame().hasRows();
+  }
+
+  public Expression getFrameRowCount() {
+    return getFrame().getFrameRowCount();
+  }
+
+  public Expression getPartitionRowCount() {
+    return getFrame().getPartitionRowCount();
+  }
+}
+
+// End WinAggResultContext.java

http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/99f39682/core/src/main/java/net/hydromatic/optiq/rules/java/impl/package-info.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/net/hydromatic/optiq/rules/java/impl/package-info.java b/core/src/main/java/net/hydromatic/optiq/rules/java/impl/package-info.java
new file mode 100644
index 0000000..1c019c8
--- /dev/null
+++ b/core/src/main/java/net/hydromatic/optiq/rules/java/impl/package-info.java
@@ -0,0 +1,24 @@
+/*
+// Licensed to Julian Hyde under one or more contributor license
+// agreements. See the NOTICE file distributed with this work for
+// additional information regarding copyright ownership.
+//
+// Julian Hyde 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.
+*/
+
+/**
+ * Optiq-specific classes for implementation of regular and window aggregates.
+ */
+package net.hydromatic.optiq.rules.java.impl;
+
+// End package-info.java


[4/7] git commit: Add test case for [OPTIQ-307]

Posted by jh...@apache.org.
Add test case for [OPTIQ-307]


Project: http://git-wip-us.apache.org/repos/asf/incubator-optiq/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-optiq/commit/548cf39b
Tree: http://git-wip-us.apache.org/repos/asf/incubator-optiq/tree/548cf39b
Diff: http://git-wip-us.apache.org/repos/asf/incubator-optiq/diff/548cf39b

Branch: refs/heads/master
Commit: 548cf39b1895bb1c50323cb265076757e80119f0
Parents: 4621db7
Author: julianhyde <ju...@gmail.com>
Authored: Fri Jun 20 15:39:02 2014 -0700
Committer: julianhyde <ju...@gmail.com>
Committed: Mon Jun 23 01:06:25 2014 -0700

----------------------------------------------------------------------
 .../net/hydromatic/optiq/test/JdbcTest.java     |  4 ++
 core/src/test/resources/sql/misc.oq             | 64 ++++++++++++++++++++
 2 files changed, 68 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/548cf39b/core/src/test/java/net/hydromatic/optiq/test/JdbcTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/net/hydromatic/optiq/test/JdbcTest.java b/core/src/test/java/net/hydromatic/optiq/test/JdbcTest.java
index e02c19d..26f72bd 100644
--- a/core/src/test/java/net/hydromatic/optiq/test/JdbcTest.java
+++ b/core/src/test/java/net/hydromatic/optiq/test/JdbcTest.java
@@ -3855,6 +3855,10 @@ public class JdbcTest {
     checkRun("sql/winagg.oq");
   }
 
+  @Test public void testRunMisc() throws Exception {
+    checkRun("sql/misc.oq");
+  }
+
   private void checkRun(String path) throws Exception {
     // e.g. "file:/home/fred/optiq/core/target/test-classes/sql/outer.oq"
     final URL inUrl = JdbcTest.class.getResource("/" + path);

http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/548cf39b/core/src/test/resources/sql/misc.oq
----------------------------------------------------------------------
diff --git a/core/src/test/resources/sql/misc.oq b/core/src/test/resources/sql/misc.oq
new file mode 100644
index 0000000..c41560e
--- /dev/null
+++ b/core/src/test/resources/sql/misc.oq
@@ -0,0 +1,64 @@
+# misc.oq - Miscellaneous queries
+!use post
+!set outputformat mysql
+
+# OPTIQ-307 CAST(timestamp AS DATE) gives ClassCastException
+# Based on DRILL-1051
+!if (false) {
+with data(c_row, c_timestamp) as (select * from (values
+    (1, TIMESTAMP '1997-01-02 03:04:05'),
+    (2, TIMESTAMP '1997-01-02 00:00:00'),
+    (3, TIMESTAMP '2001-09-22 18:19:20'),
+    (4, TIMESTAMP '1997-02-10 17:32:01'),
+    (5, TIMESTAMP '1997-02-10 17:32:00'),
+    (6, TIMESTAMP '1997-02-11 17:32:01'),
+    (7, TIMESTAMP '1997-02-12 17:32:01'),
+    (8, TIMESTAMP '1997-02-13 17:32:01'),
+    (9, TIMESTAMP '1997-02-14 17:32:01'),
+    (10, TIMESTAMP '1997-02-15 17:32:01'),
+    (11, TIMESTAMP '1997-02-16 17:32:01'),
+    (13, TIMESTAMP '0097-02-16 17:32:01'),
+    (14, TIMESTAMP '0597-02-16 17:32:01'),
+    (15, TIMESTAMP '1097-02-16 17:32:01'),
+    (16, TIMESTAMP '1697-02-16 17:32:01'),
+    (17, TIMESTAMP '1797-02-16 17:32:01'),
+    (18, TIMESTAMP '1897-02-16 17:32:01'),
+    (19, TIMESTAMP '1997-02-16 17:32:01'),
+    (20, TIMESTAMP '2097-02-16 17:32:01'),
+    (21, TIMESTAMP '1996-02-28 17:32:01'),
+    (22, TIMESTAMP '1996-02-29 17:32:01'),
+    (23, TIMESTAMP '1996-03-01 17:32:01')))
+select cast(c_timestamp as varchar(20)), cast(c_timestamp as date) from data where c_row <> 12;
+
++------------+------------+
+|   EXPR$0   |   EXPR$1   |
++------------+------------+
+| 1997-01-02 03:04:05 | 1997-01-02 |
+| 1997-01-02 00:00:00 | 1997-01-02 |
+| 2001-09-22 18:19:20 | 2001-09-22 |
+| 1997-02-10 17:32:01 | 1997-02-10 |
+| 1997-02-10 17:32:00 | 1997-02-10 |
+| 1997-02-11 17:32:01 | 1997-02-11 |
+| 1997-02-12 17:32:01 | 1997-02-12 |
+| 1997-02-13 17:32:01 | 1997-02-13 |
+| 1997-02-14 17:32:01 | 1997-02-14 |
+| 1997-02-15 17:32:01 | 1997-02-15 |
+| 1997-02-16 17:32:01 | 1997-02-16 |
+| 0097-02-16 17:32:01 | 0097-02-17 |
+| 0597-02-16 17:32:01 | 0597-02-13 |
+| 1097-02-16 17:32:01 | 1097-02-09 |
+| 1697-02-16 17:32:01 | 1697-02-15 |
+| 1797-02-16 17:32:01 | 1797-02-15 |
+| 1897-02-16 17:32:01 | 1897-02-16 |
+| 1997-02-16 17:32:01 | 1997-02-16 |
+| 2097-02-16 17:32:01 | 2097-02-16 |
+| 1996-02-28 17:32:01 | 1996-02-28 |
+| 1996-02-29 17:32:01 | 1996-02-29 |
+| 1996-03-01 17:32:01 | 1996-03-01 |
++------------+------------+
+22 rows selected
+
+!ok
+!}
+
+# End misc.oq


[5/7] git commit: Re-organize planner initialization, to make it easier to use heuristic join order.

Posted by jh...@apache.org.
Re-organize planner initialization, to make it easier to use heuristic join order.


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

Branch: refs/heads/master
Commit: e34e6e12006b7a6f6fa16c610961089b3c3c3778
Parents: 548cf39
Author: julianhyde <ju...@gmail.com>
Authored: Mon Jun 23 01:14:03 2014 -0700
Committer: julianhyde <ju...@gmail.com>
Committed: Mon Jun 23 01:14:03 2014 -0700

----------------------------------------------------------------------
 .../net/hydromatic/optiq/jdbc/OptiqPrepare.java |  12 +-
 .../optiq/prepare/OptiqPrepareImpl.java         |  91 ++++++++++------
 .../net/hydromatic/optiq/prepare/Prepare.java   | 109 +++++++++----------
 .../net/hydromatic/optiq/tools/Programs.java    |  81 ++++++++++++--
 .../net/hydromatic/optiq/tools/PlannerTest.java |  99 +++++------------
 .../hydromatic/optiq/impl/tpcds/TpcdsTest.java  |   5 +
 .../optiq/impl/spark/SparkHandlerImpl.java      |   7 +-
 7 files changed, 226 insertions(+), 178 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/e34e6e12/core/src/main/java/net/hydromatic/optiq/jdbc/OptiqPrepare.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/net/hydromatic/optiq/jdbc/OptiqPrepare.java b/core/src/main/java/net/hydromatic/optiq/jdbc/OptiqPrepare.java
index 3e9b15b..41f44f6 100644
--- a/core/src/main/java/net/hydromatic/optiq/jdbc/OptiqPrepare.java
+++ b/core/src/main/java/net/hydromatic/optiq/jdbc/OptiqPrepare.java
@@ -31,7 +31,7 @@ import net.hydromatic.optiq.runtime.*;
 
 import org.eigenbase.rel.RelNode;
 import org.eigenbase.relopt.RelOptPlanner;
-import org.eigenbase.relopt.volcano.VolcanoPlanner;
+import org.eigenbase.relopt.RelOptRule;
 import org.eigenbase.reltype.RelDataType;
 import org.eigenbase.reltype.RelDataTypeFactory;
 import org.eigenbase.sql.SqlNode;
@@ -96,13 +96,19 @@ public interface OptiqPrepare {
     RelNode flattenTypes(RelOptPlanner planner, RelNode rootRel,
         boolean restructure);
 
-    void registerRules(VolcanoPlanner planner);
+    void registerRules(RuleSetBuilder builder);
 
     boolean enabled();
 
     Bindable compile(ClassDeclaration expr, String s);
 
     Object sparkContext();
+
+    /** Allows Spark to declare the rules it needs. */
+    interface RuleSetBuilder {
+      void addRule(RelOptRule rule);
+      void removeRule(RelOptRule rule);
+    }
   }
 
   /** Namespace that allows us to define non-abstract methods inside an
@@ -160,7 +166,7 @@ public interface OptiqPrepare {
         return rootRel;
       }
 
-      public void registerRules(VolcanoPlanner planner) {
+      public void registerRules(RuleSetBuilder builder) {
       }
 
       public boolean enabled() {

http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/e34e6e12/core/src/main/java/net/hydromatic/optiq/prepare/OptiqPrepareImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/net/hydromatic/optiq/prepare/OptiqPrepareImpl.java b/core/src/main/java/net/hydromatic/optiq/prepare/OptiqPrepareImpl.java
index d19024a..b11780d 100644
--- a/core/src/main/java/net/hydromatic/optiq/prepare/OptiqPrepareImpl.java
+++ b/core/src/main/java/net/hydromatic/optiq/prepare/OptiqPrepareImpl.java
@@ -98,6 +98,46 @@ public class OptiqPrepareImpl implements OptiqPrepare {
           "values 1",
           "VALUES 1");
 
+  private static final List<RelOptRule> DEFAULT_RULES =
+      ImmutableList.of(
+          JavaRules.ENUMERABLE_JOIN_RULE,
+          JavaRules.ENUMERABLE_PROJECT_RULE,
+          JavaRules.ENUMERABLE_FILTER_RULE,
+          JavaRules.ENUMERABLE_AGGREGATE_RULE,
+          JavaRules.ENUMERABLE_SORT_RULE,
+          JavaRules.ENUMERABLE_LIMIT_RULE,
+          JavaRules.ENUMERABLE_COLLECT_RULE,
+          JavaRules.ENUMERABLE_UNCOLLECT_RULE,
+          JavaRules.ENUMERABLE_UNION_RULE,
+          JavaRules.ENUMERABLE_INTERSECT_RULE,
+          JavaRules.ENUMERABLE_MINUS_RULE,
+          JavaRules.ENUMERABLE_TABLE_MODIFICATION_RULE,
+          JavaRules.ENUMERABLE_VALUES_RULE,
+          JavaRules.ENUMERABLE_WINDOW_RULE,
+          JavaRules.ENUMERABLE_ONE_ROW_RULE,
+          JavaRules.ENUMERABLE_EMPTY_RULE,
+          JavaRules.ENUMERABLE_TABLE_FUNCTION_RULE,
+          TableAccessRule.INSTANCE,
+          MergeProjectRule.INSTANCE,
+          PushFilterPastProjectRule.INSTANCE,
+          PushFilterPastJoinRule.FILTER_ON_JOIN,
+          RemoveDistinctAggregateRule.INSTANCE,
+          ReduceAggregatesRule.INSTANCE,
+          SwapJoinRule.INSTANCE,
+          PushJoinThroughJoinRule.RIGHT,
+          PushJoinThroughJoinRule.LEFT,
+          PushSortPastProjectRule.INSTANCE);
+
+  private static final List<RelOptRule> CONSTANT_REDUCTION_RULES =
+      ImmutableList.of(
+          ReduceExpressionsRule.PROJECT_INSTANCE,
+          ReduceExpressionsRule.FILTER_INSTANCE,
+          ReduceExpressionsRule.CALC_INSTANCE,
+          ReduceExpressionsRule.JOIN_INSTANCE,
+          ReduceValuesRule.FILTER_INSTANCE,
+          ReduceValuesRule.PROJECT_FILTER_INSTANCE,
+          ReduceValuesRule.PROJECT_INSTANCE);
+
   public OptiqPrepareImpl() {
   }
 
@@ -157,48 +197,29 @@ public class OptiqPrepareImpl implements OptiqPrepare {
       planner.registerAbstractRelationalRules();
     }
     RelOptUtil.registerAbstractRels(planner);
-    planner.addRule(JavaRules.ENUMERABLE_JOIN_RULE);
-    planner.addRule(JavaRules.ENUMERABLE_PROJECT_RULE);
-    planner.addRule(JavaRules.ENUMERABLE_FILTER_RULE);
-    planner.addRule(JavaRules.ENUMERABLE_AGGREGATE_RULE);
-    planner.addRule(JavaRules.ENUMERABLE_SORT_RULE);
-    planner.addRule(JavaRules.ENUMERABLE_LIMIT_RULE);
-    planner.addRule(JavaRules.ENUMERABLE_COLLECT_RULE);
-    planner.addRule(JavaRules.ENUMERABLE_UNCOLLECT_RULE);
-    planner.addRule(JavaRules.ENUMERABLE_UNION_RULE);
-    planner.addRule(JavaRules.ENUMERABLE_INTERSECT_RULE);
-    planner.addRule(JavaRules.ENUMERABLE_MINUS_RULE);
-    planner.addRule(JavaRules.ENUMERABLE_TABLE_MODIFICATION_RULE);
-    planner.addRule(JavaRules.ENUMERABLE_VALUES_RULE);
-    planner.addRule(JavaRules.ENUMERABLE_WINDOW_RULE);
-    planner.addRule(JavaRules.ENUMERABLE_ONE_ROW_RULE);
-    planner.addRule(JavaRules.ENUMERABLE_EMPTY_RULE);
-    planner.addRule(JavaRules.ENUMERABLE_TABLE_FUNCTION_RULE);
-    planner.addRule(TableAccessRule.INSTANCE);
-    planner.addRule(MergeProjectRule.INSTANCE);
-    planner.addRule(PushFilterPastProjectRule.INSTANCE);
-    planner.addRule(PushFilterPastJoinRule.FILTER_ON_JOIN);
-    planner.addRule(RemoveDistinctAggregateRule.INSTANCE);
-    planner.addRule(ReduceAggregatesRule.INSTANCE);
-    planner.addRule(SwapJoinRule.INSTANCE);
-    planner.addRule(PushJoinThroughJoinRule.RIGHT);
-    planner.addRule(PushJoinThroughJoinRule.LEFT);
-    planner.addRule(PushSortPastProjectRule.INSTANCE);
+    for (RelOptRule rule : DEFAULT_RULES) {
+      planner.addRule(rule);
+    }
 
     // Change the below to enable constant-reduction.
     if (false) {
-      planner.addRule(ReduceExpressionsRule.PROJECT_INSTANCE);
-      planner.addRule(ReduceExpressionsRule.FILTER_INSTANCE);
-      planner.addRule(ReduceExpressionsRule.CALC_INSTANCE);
-      planner.addRule(ReduceExpressionsRule.JOIN_INSTANCE);
-      planner.addRule(ReduceValuesRule.FILTER_INSTANCE);
-      planner.addRule(ReduceValuesRule.PROJECT_FILTER_INSTANCE);
-      planner.addRule(ReduceValuesRule.PROJECT_INSTANCE);
+      for (RelOptRule rule : CONSTANT_REDUCTION_RULES) {
+        planner.addRule(rule);
+      }
     }
 
     final SparkHandler spark = context.spark();
     if (spark.enabled()) {
-      spark.registerRules(planner);
+      spark.registerRules(
+          new SparkHandler.RuleSetBuilder() {
+          public void addRule(RelOptRule rule) {
+            // TODO:
+          }
+
+          public void removeRule(RelOptRule rule) {
+            // TODO:
+          }
+        });
     }
     return planner;
   }

http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/e34e6e12/core/src/main/java/net/hydromatic/optiq/prepare/Prepare.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/net/hydromatic/optiq/prepare/Prepare.java b/core/src/main/java/net/hydromatic/optiq/prepare/Prepare.java
index aad8a01..026f436 100644
--- a/core/src/main/java/net/hydromatic/optiq/prepare/Prepare.java
+++ b/core/src/main/java/net/hydromatic/optiq/prepare/Prepare.java
@@ -17,8 +17,6 @@
 */
 package net.hydromatic.optiq.prepare;
 
-import net.hydromatic.linq4j.function.Functions;
-
 import net.hydromatic.optiq.DataContext;
 import net.hydromatic.optiq.impl.StarTable;
 import net.hydromatic.optiq.jdbc.OptiqPrepare;
@@ -26,12 +24,13 @@ import net.hydromatic.optiq.jdbc.OptiqSchema;
 import net.hydromatic.optiq.rules.java.JavaRules;
 import net.hydromatic.optiq.runtime.Bindable;
 import net.hydromatic.optiq.runtime.Typed;
+import net.hydromatic.optiq.tools.Program;
+import net.hydromatic.optiq.tools.Programs;
 
 import org.eigenbase.rel.*;
 import org.eigenbase.rel.metadata.*;
 import org.eigenbase.rel.rules.*;
 import org.eigenbase.relopt.*;
-import org.eigenbase.relopt.hep.*;
 import org.eigenbase.reltype.RelDataType;
 import org.eigenbase.rex.RexBuilder;
 import org.eigenbase.rex.RexExecutorImpl;
@@ -41,7 +40,7 @@ import org.eigenbase.sql2rel.SqlToRelConverter;
 import org.eigenbase.trace.EigenbaseTimingTracer;
 import org.eigenbase.trace.EigenbaseTrace;
 
-import com.google.common.collect.Lists;
+import com.google.common.collect.ImmutableList;
 
 import java.lang.reflect.Type;
 import java.util.*;
@@ -55,6 +54,24 @@ import java.util.logging.Logger;
 public abstract class Prepare {
   protected static final Logger LOGGER = EigenbaseTrace.getStatementTracer();
 
+  private static final ImmutableList<RelOptRule> CALC_RULES =
+      ImmutableList.of(
+          JavaRules.ENUMERABLE_CALC_RULE,
+          JavaRules.ENUMERABLE_FILTER_TO_CALC_RULE,
+          JavaRules.ENUMERABLE_PROJECT_TO_CALC_RULE,
+          MergeCalcRule.INSTANCE,
+          MergeFilterOntoCalcRule.INSTANCE,
+          MergeProjectOntoCalcRule.INSTANCE,
+          FilterToCalcRule.INSTANCE,
+          ProjectToCalcRule.INSTANCE,
+          MergeCalcRule.INSTANCE,
+
+          // REVIEW jvs 9-Apr-2006: Do we still need these two?  Doesn't the
+          // combination of MergeCalcRule, FilterToCalcRule, and
+          // ProjectToCalcRule have the same effect?
+          MergeFilterOntoCalcRule.INSTANCE,
+          MergeProjectOntoCalcRule.INSTANCE);
+
   protected final OptiqPrepare.Context context;
   protected final CatalogReader catalogReader;
   protected String queryString = null;
@@ -93,64 +110,46 @@ public abstract class Prepare {
    * @param materializations Tables known to be populated with a given query
    * @return an equivalent optimized relational expression
    */
-  protected RelNode optimize(RelDataType logicalRowType, RelNode rootRel,
-      List<Materialization> materializations) {
+  protected RelNode optimize(RelDataType logicalRowType, final RelNode rootRel,
+      final List<Materialization> materializations) {
     final RelOptPlanner planner = rootRel.getCluster().getPlanner();
 
-    final DataContext dataContext = context.getDataContext();
-    planner.setExecutor(new RexExecutorImpl(dataContext));
-
     planner.setRoot(rootRel);
-    for (Materialization materialization : materializations) {
-      planner.addMaterialization(
-          new RelOptMaterialization(materialization.tableRel,
-              materialization.queryRel, materialization.starRelOptTable));
-    }
-
-    RelTraitSet desiredTraits = getDesiredRootTraitSet(rootRel);
-
-    final RelNode rootRel2 = planner.changeTraits(rootRel, desiredTraits);
-    assert rootRel2 != null;
 
-    planner.setRoot(rootRel2);
-    final RelOptPlanner planner2 = planner.chooseDelegate();
-    final RelNode rootRel3 = planner2.findBestExp();
-    assert rootRel3 != null : "could not implement exp";
+    final RelTraitSet desiredTraits = getDesiredRootTraitSet(rootRel);
+    final Program program1 =
+        new Program() {
+          public RelNode run(RelOptPlanner planner, RelNode rel,
+              RelTraitSet requiredOutputTraits) {
+            final DataContext dataContext = context.getDataContext();
+            planner.setExecutor(new RexExecutorImpl(dataContext));
+
+            for (Materialization materialization : materializations) {
+              planner.addMaterialization(
+                  new RelOptMaterialization(materialization.tableRel,
+                      materialization.queryRel,
+                      materialization.starRelOptTable));
+            }
+
+            final RelNode rootRel2 =
+                planner.changeTraits(rel, requiredOutputTraits);
+            assert rootRel2 != null;
+
+            planner.setRoot(rootRel2);
+            final RelOptPlanner planner2 = planner.chooseDelegate();
+            final RelNode rootRel3 = planner2.findBestExp();
+            assert rootRel3 != null : "could not implement exp";
+            return rootRel3;
+          }
+        };
+
+    final RelNode rootRel3 = program1.run(planner, rootRel, desiredTraits);
 
     // Second planner pass to do physical "tweaks". This the first time that
     // EnumerableCalcRel is introduced.
-    final HepProgram program = HepProgram.builder()
-        .addRuleInstance(JavaRules.ENUMERABLE_CALC_RULE)
-        .addRuleInstance(JavaRules.ENUMERABLE_FILTER_TO_CALC_RULE)
-        .addRuleInstance(JavaRules.ENUMERABLE_PROJECT_TO_CALC_RULE)
-        .addRuleInstance(MergeCalcRule.INSTANCE)
-        .addRuleInstance(MergeFilterOntoCalcRule.INSTANCE)
-        .addRuleInstance(MergeProjectOntoCalcRule.INSTANCE)
-        .addRuleInstance(FilterToCalcRule.INSTANCE)
-        .addRuleInstance(ProjectToCalcRule.INSTANCE)
-        .addRuleInstance(MergeCalcRule.INSTANCE)
-
-            // REVIEW jvs 9-Apr-2006: Do we still need these two?  Doesn't the
-            // combination of MergeCalcRule, FilterToCalcRule, and
-            // ProjectToCalcRule have the same effect?
-        .addRuleInstance(MergeFilterOntoCalcRule.INSTANCE)
-        .addRuleInstance(MergeProjectOntoCalcRule.INSTANCE)
-        .build();
-    final HepPlanner planner3 =
-        new HepPlanner(program, true,
-            Functions.<RelNode, RelNode, Void>ignore2(),
-            RelOptCostImpl.FACTORY);
-    List<RelMetadataProvider> list = Lists.newArrayList();
-    DefaultRelMetadataProvider defaultProvider =
-        new DefaultRelMetadataProvider();
-    list.add(defaultProvider);
-    planner3.registerMetadataProviders(list);
-    RelMetadataProvider plannerChain =
-        ChainedRelMetadataProvider.of(list);
-    rootRel3.getCluster().setMetadataProvider(plannerChain);
-    planner3.setRoot(rootRel3);
-
-    final RelNode rootRel4 = planner3.findBestExp();
+    final Program program2 =
+        Programs.hep(CALC_RULES, true, new DefaultRelMetadataProvider());
+    final RelNode rootRel4 = program2.run(null, rootRel3, null);
     if (LOGGER.isLoggable(Level.FINE)) {
       LOGGER.fine(
           "Plan after physical tweaks: "

http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/e34e6e12/core/src/main/java/net/hydromatic/optiq/tools/Programs.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/net/hydromatic/optiq/tools/Programs.java b/core/src/main/java/net/hydromatic/optiq/tools/Programs.java
index 42f4ed6..4996fda 100644
--- a/core/src/main/java/net/hydromatic/optiq/tools/Programs.java
+++ b/core/src/main/java/net/hydromatic/optiq/tools/Programs.java
@@ -18,19 +18,17 @@
 package net.hydromatic.optiq.tools;
 
 import org.eigenbase.rel.RelNode;
-import org.eigenbase.relopt.RelOptPlanner;
-import org.eigenbase.relopt.RelOptRule;
-import org.eigenbase.relopt.RelTraitSet;
-import org.eigenbase.relopt.hep.HepPlanner;
-import org.eigenbase.relopt.hep.HepProgram;
+import org.eigenbase.rel.metadata.ChainedRelMetadataProvider;
+import org.eigenbase.rel.metadata.RelMetadataProvider;
+import org.eigenbase.rel.rules.*;
+import org.eigenbase.relopt.*;
+import org.eigenbase.relopt.hep.*;
 
 import com.google.common.base.Function;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Lists;
 
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.List;
+import java.util.*;
 
 /**
  * Utilities for creating {@link Program}s.
@@ -71,18 +69,81 @@ public class Programs {
     return new SequenceProgram(ImmutableList.copyOf(programs));
   }
 
+  /** Creates a program that executes a list of rules in a HEP planner. */
+  public static Program hep(ImmutableList<RelOptRule> rules, boolean noDag,
+      RelMetadataProvider metadataProvider) {
+    final HepProgramBuilder builder = HepProgram.builder();
+    for (RelOptRule rule : rules) {
+      builder.addRuleInstance(rule);
+    }
+    return of(builder.build(), noDag, metadataProvider);
+  }
+
   /** Creates a program that executes a {@link HepProgram}. */
-  public static Program of(final HepProgram hepProgram) {
+  public static Program of(final HepProgram hepProgram, final boolean noDag,
+      final RelMetadataProvider metadataProvider) {
     return new Program() {
       public RelNode run(RelOptPlanner planner, RelNode rel,
           RelTraitSet requiredOutputTraits) {
-        final HepPlanner hepPlanner = new HepPlanner(hepProgram);
+        final HepPlanner hepPlanner = new HepPlanner(hepProgram,
+            noDag, null, RelOptCostImpl.FACTORY);
+
+        if (metadataProvider != null) {
+          List<RelMetadataProvider> list = Lists.newArrayList();
+          list.add(metadataProvider);
+          hepPlanner.registerMetadataProviders(list);
+          RelMetadataProvider plannerChain =
+              ChainedRelMetadataProvider.of(list);
+          rel.getCluster().setMetadataProvider(plannerChain);
+        }
+
         hepPlanner.setRoot(rel);
         return hepPlanner.findBestExp();
       }
     };
   }
 
+  /** Creates a program that invokes heuristic join-order optimization
+   * (via {@link org.eigenbase.rel.rules.ConvertMultiJoinRule},
+   * {@link org.eigenbase.rel.rules.MultiJoinRel} and
+   * {@link org.eigenbase.rel.rules.LoptOptimizeJoinRule})
+   * if there are 6 or more joins (7 or more relations). */
+  public static Program heuristicJoinOrder(final Collection<RelOptRule> rules) {
+    return new Program() {
+      public RelNode run(RelOptPlanner planner, RelNode rel,
+          RelTraitSet requiredOutputTraits) {
+        final int joinCount = RelOptUtil.countJoins(rel);
+        final Program program;
+        if (joinCount < 6) {
+          program = ofRules(rules);
+        } else {
+          // Create a program that gathers together joins as a MultiJoinRel.
+          final HepProgram hep = new HepProgramBuilder()
+              .addRuleInstance(PushFilterPastJoinRule.FILTER_ON_JOIN)
+              .addMatchOrder(HepMatchOrder.BOTTOM_UP)
+              .addRuleInstance(ConvertMultiJoinRule.INSTANCE)
+              .build();
+          final Program program1 = of(hep, false, null);
+
+          // Create a program that contains a rule to expand a MultiJoinRel
+          // into heuristically ordered joins.
+          // We use the rule set passed in, but remove SwapJoinRule and
+          // PushJoinThroughJoinRule, because they cause exhaustive search.
+          final List<RelOptRule> list = new ArrayList<RelOptRule>(rules);
+          list.removeAll(
+              ImmutableList.of(SwapJoinRule.INSTANCE,
+                  PushJoinThroughJoinRule.LEFT,
+                  PushJoinThroughJoinRule.RIGHT));
+          list.add(LoptOptimizeJoinRule.INSTANCE);
+          final Program program2 = ofRules(list);
+
+          program = sequence(program1, program2);
+        }
+        return program.run(planner, rel, requiredOutputTraits);
+      }
+    };
+  }
+
   /** Program backed by a {@link RuleSet}. */
   static class RuleSetProgram implements Program {
     final RuleSet ruleSet;

http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/e34e6e12/core/src/test/java/net/hydromatic/optiq/tools/PlannerTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/net/hydromatic/optiq/tools/PlannerTest.java b/core/src/test/java/net/hydromatic/optiq/tools/PlannerTest.java
index eb244af..354f9e6 100644
--- a/core/src/test/java/net/hydromatic/optiq/tools/PlannerTest.java
+++ b/core/src/test/java/net/hydromatic/optiq/tools/PlannerTest.java
@@ -32,7 +32,6 @@ import org.eigenbase.rel.*;
 import org.eigenbase.rel.convert.ConverterRule;
 import org.eigenbase.rel.rules.*;
 import org.eigenbase.relopt.*;
-import org.eigenbase.relopt.hep.*;
 import org.eigenbase.reltype.RelDataType;
 import org.eigenbase.reltype.RelDataTypeFactory;
 import org.eigenbase.sql.*;
@@ -49,7 +48,6 @@ import org.eigenbase.util.Util;
 
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Iterables;
 
 import org.junit.Test;
 
@@ -442,7 +440,7 @@ public class PlannerTest {
           .append(i).append(".\"deptno\" = d")
           .append(i - 1).append(".\"deptno\"");
     }
-    Planner planner = getPlanner(null, adaptiveJoinProgram(RULE_SET));
+    Planner planner = getPlanner(null, Programs.heuristicJoinOrder(RULE_SET));
     SqlNode parse = planner.parse(buf.toString());
 
     SqlNode validate = planner.validate(parse);
@@ -454,40 +452,6 @@ public class PlannerTest {
         "EnumerableJoinRel(condition=[=($3, $0)], joinType=[inner])"));
   }
 
-  /** Creates a program that invokes heuristic join-order optimization
-   * (via {@link org.eigenbase.rel.rules.ConvertMultiJoinRule},
-   * {@link org.eigenbase.rel.rules.MultiJoinRel} and
-   * {@link org.eigenbase.rel.rules.LoptOptimizeJoinRule})
-   * if there are 6 or more joins (7 or more relations). */
-  private static Program adaptiveJoinProgram(final RuleSet ruleSet) {
-    return new Program() {
-      public RelNode run(RelOptPlanner planner, RelNode rel,
-          RelTraitSet requiredOutputTraits) {
-        final int joinCount = RelOptUtil.countJoins(rel);
-        final Program program;
-        if (joinCount < 6) {
-          program = Programs.of(ruleSet);
-        } else {
-          final HepProgram hep = new HepProgramBuilder()
-              .addRuleInstance(PushFilterPastJoinRule.FILTER_ON_JOIN)
-              .addMatchOrder(HepMatchOrder.BOTTOM_UP)
-              .addRuleInstance(ConvertMultiJoinRule.INSTANCE)
-              .build();
-          final List<RelOptRule> list = new ArrayList<RelOptRule>();
-          Iterables.addAll(list, ruleSet);
-          list.removeAll(
-              ImmutableList.of(SwapJoinRule.INSTANCE,
-                  PushJoinThroughJoinRule.LEFT,
-                  PushJoinThroughJoinRule.RIGHT));
-          list.add(LoptOptimizeJoinRule.INSTANCE);
-          program =
-              Programs.sequence(Programs.of(hep), Programs.ofRules(list));
-        }
-        return program.run(planner, rel, requiredOutputTraits);
-      }
-    };
-  }
-
   /**
    * Rule to convert a {@link EnumerableProjectRel} to an
    * {@link JdbcProjectRel}.
@@ -560,39 +524,32 @@ public class PlannerTest {
     }
   }
 
-  private static final RuleSet RULE_SET =
-      new RuleSet() {
-        final Set<RelOptRule> setOfRules =
-            ImmutableSet.of(
-                JavaRules.ENUMERABLE_JOIN_RULE,
-                JavaRules.ENUMERABLE_PROJECT_RULE,
-                JavaRules.ENUMERABLE_FILTER_RULE,
-                JavaRules.ENUMERABLE_AGGREGATE_RULE,
-                JavaRules.ENUMERABLE_SORT_RULE,
-                JavaRules.ENUMERABLE_LIMIT_RULE,
-                JavaRules.ENUMERABLE_UNION_RULE,
-                JavaRules.ENUMERABLE_INTERSECT_RULE,
-                JavaRules.ENUMERABLE_MINUS_RULE,
-                JavaRules.ENUMERABLE_TABLE_MODIFICATION_RULE,
-                JavaRules.ENUMERABLE_VALUES_RULE,
-                JavaRules.ENUMERABLE_WINDOW_RULE,
-                JavaRules.ENUMERABLE_ONE_ROW_RULE,
-                JavaRules.ENUMERABLE_EMPTY_RULE,
-                TableAccessRule.INSTANCE,
-                MergeProjectRule.INSTANCE,
-                PushFilterPastProjectRule.INSTANCE,
-                PushFilterPastJoinRule.FILTER_ON_JOIN,
-                RemoveDistinctAggregateRule.INSTANCE,
-                ReduceAggregatesRule.INSTANCE,
-                SwapJoinRule.INSTANCE,
-                PushJoinThroughJoinRule.RIGHT,
-                PushJoinThroughJoinRule.LEFT,
-                PushSortPastProjectRule.INSTANCE);
-
-        public Iterator<RelOptRule> iterator() {
-          return setOfRules.iterator();
-        }
-      };
+  private static final ImmutableSet<RelOptRule> RULE_SET =
+      ImmutableSet.of(
+          JavaRules.ENUMERABLE_JOIN_RULE,
+          JavaRules.ENUMERABLE_PROJECT_RULE,
+          JavaRules.ENUMERABLE_FILTER_RULE,
+          JavaRules.ENUMERABLE_AGGREGATE_RULE,
+          JavaRules.ENUMERABLE_SORT_RULE,
+          JavaRules.ENUMERABLE_LIMIT_RULE,
+          JavaRules.ENUMERABLE_UNION_RULE,
+          JavaRules.ENUMERABLE_INTERSECT_RULE,
+          JavaRules.ENUMERABLE_MINUS_RULE,
+          JavaRules.ENUMERABLE_TABLE_MODIFICATION_RULE,
+          JavaRules.ENUMERABLE_VALUES_RULE,
+          JavaRules.ENUMERABLE_WINDOW_RULE,
+          JavaRules.ENUMERABLE_ONE_ROW_RULE,
+          JavaRules.ENUMERABLE_EMPTY_RULE,
+          TableAccessRule.INSTANCE,
+          MergeProjectRule.INSTANCE,
+          PushFilterPastProjectRule.INSTANCE,
+          PushFilterPastJoinRule.FILTER_ON_JOIN,
+          RemoveDistinctAggregateRule.INSTANCE,
+          ReduceAggregatesRule.INSTANCE,
+          SwapJoinRule.INSTANCE,
+          PushJoinThroughJoinRule.RIGHT,
+          PushJoinThroughJoinRule.LEFT,
+          PushSortPastProjectRule.INSTANCE);
 
   /**
    * Test to determine whether de-correlation correctly removes CorrelatorRel.
@@ -622,7 +579,7 @@ public class PlannerTest {
             new ReflectiveSchema(new TpchSchema()));
 
     Planner p = Frameworks.getPlanner(Lex.MYSQL, schema,
-        SqlStdOperatorTable.instance(), RULE_SET);
+        SqlStdOperatorTable.instance(), RuleSets.ofList(RULE_SET));
     SqlNode n = p.parse(tpchTestQuery);
     n = p.validate(n);
     RelNode r = p.convert(n);

http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/e34e6e12/plus/src/test/java/net/hydromatic/optiq/impl/tpcds/TpcdsTest.java
----------------------------------------------------------------------
diff --git a/plus/src/test/java/net/hydromatic/optiq/impl/tpcds/TpcdsTest.java b/plus/src/test/java/net/hydromatic/optiq/impl/tpcds/TpcdsTest.java
index 17319e5..dbae507 100644
--- a/plus/src/test/java/net/hydromatic/optiq/impl/tpcds/TpcdsTest.java
+++ b/plus/src/test/java/net/hydromatic/optiq/impl/tpcds/TpcdsTest.java
@@ -92,6 +92,11 @@ public class TpcdsTest {
     checkQuery(72).runs();
   }
 
+  @Ignore("work in progress")
+  @Test public void testQuery72Plan() {
+    checkQuery(72).planContains("xx");
+  }
+
   private OptiqAssert.AssertQuery checkQuery(int i) {
     final Query query = Query.of(i);
     String sql = query.sql(-1, new Random(0));

http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/e34e6e12/spark/src/main/java/net/hydromatic/optiq/impl/spark/SparkHandlerImpl.java
----------------------------------------------------------------------
diff --git a/spark/src/main/java/net/hydromatic/optiq/impl/spark/SparkHandlerImpl.java b/spark/src/main/java/net/hydromatic/optiq/impl/spark/SparkHandlerImpl.java
index 3e2e7c2..b2c0c5a 100644
--- a/spark/src/main/java/net/hydromatic/optiq/impl/spark/SparkHandlerImpl.java
+++ b/spark/src/main/java/net/hydromatic/optiq/impl/spark/SparkHandlerImpl.java
@@ -27,7 +27,6 @@ import net.hydromatic.optiq.runtime.Typed;
 import org.eigenbase.javac.JaninoCompiler;
 import org.eigenbase.rel.RelNode;
 import org.eigenbase.relopt.*;
-import org.eigenbase.relopt.volcano.VolcanoPlanner;
 
 import org.apache.spark.api.java.JavaSparkContext;
 
@@ -89,11 +88,11 @@ public class SparkHandlerImpl implements OptiqPrepare.SparkHandler {
     return planner.changeTraits(root2, rootRel.getTraitSet());
   }
 
-  public void registerRules(VolcanoPlanner planner) {
+  public void registerRules(RuleSetBuilder builder) {
     for (RelOptRule rule : SparkRules.rules()) {
-      planner.addRule(rule);
+      builder.addRule(rule);
     }
-    planner.removeRule(JavaRules.ENUMERABLE_VALUES_RULE);
+    builder.removeRule(JavaRules.ENUMERABLE_VALUES_RULE);
   }
 
   public Object sparkContext() {


[6/7] [OPTIQ-310] Implement LEAD, LAG and NTILE windowed aggregates.

Posted by jh...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/99f39682/core/src/main/java/org/eigenbase/sql/fun/SqlLeadLagAggFunction.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/eigenbase/sql/fun/SqlLeadLagAggFunction.java b/core/src/main/java/org/eigenbase/sql/fun/SqlLeadLagAggFunction.java
new file mode 100644
index 0000000..f996300
--- /dev/null
+++ b/core/src/main/java/org/eigenbase/sql/fun/SqlLeadLagAggFunction.java
@@ -0,0 +1,94 @@
+/*
+// Licensed to Julian Hyde under one or more contributor license
+// agreements. See the NOTICE file distributed with this work for
+// additional information regarding copyright ownership.
+//
+// Julian Hyde 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.eigenbase.sql.fun;
+
+import java.util.List;
+
+import org.eigenbase.reltype.RelDataType;
+import org.eigenbase.reltype.RelDataTypeFactory;
+import org.eigenbase.sql.*;
+import org.eigenbase.sql.type.*;
+
+import com.google.common.collect.ImmutableList;
+
+/**
+ * <code>LEAD</code> and <code>LAG</code> aggregate functions
+ * return the value of given expression evaluated at given offset.
+ */
+public class SqlLeadLagAggFunction extends SqlAggFunction {
+  private static final SqlSingleOperandTypeChecker OPERAND_TYPES =
+      OperandTypes.or(
+          OperandTypes.ANY,
+          OperandTypes.family(SqlTypeFamily.ANY, SqlTypeFamily.NUMERIC),
+          OperandTypes.and(
+              OperandTypes.family(
+                  SqlTypeFamily.ANY, SqlTypeFamily.NUMERIC, SqlTypeFamily.ANY)
+              // Arguments 1 and 3 must have same type
+              , new SameOperandTypeChecker(3) {
+                @Override
+                protected List<Integer> getOperandList(int operandCount) {
+                  return ImmutableList.of(0, 2);
+                }
+              }));
+
+  private static final SqlReturnTypeInference RETURN_TYPE =
+      ReturnTypes.cascade(ReturnTypes.ARG0, new SqlTypeTransform() {
+        public RelDataType transformType(SqlOperatorBinding binding,
+            RelDataType type) {
+          // Result is NOT NULL if NOT NULL default value is provided
+          SqlTypeTransform transform;
+          if (binding.getOperandCount() < 3) {
+            transform = SqlTypeTransforms.FORCE_NULLABLE;
+          } else {
+            RelDataType defValueType = binding.getOperandType(2);
+            transform = defValueType.isNullable()
+                ? SqlTypeTransforms.FORCE_NULLABLE
+                : SqlTypeTransforms.TO_NOT_NULLABLE;
+          }
+          return transform.transformType(binding, type);
+        }
+      });
+
+  public SqlLeadLagAggFunction(boolean isLead) {
+    super(
+        isLead ? "LEAD" : "LAG",
+        SqlKind.OTHER_FUNCTION,
+        RETURN_TYPE,
+        null,
+        OPERAND_TYPES,
+        SqlFunctionCategory.NUMERIC);
+  }
+
+  @Override public boolean requiresOrder() {
+    return true;
+  }
+
+  @Override public boolean allowsFraming() {
+    return false;
+  }
+
+  public List<RelDataType> getParameterTypes(RelDataTypeFactory typeFactory) {
+    throw new UnsupportedOperationException("remove before optiq-0.9");
+  }
+
+  public RelDataType getReturnType(RelDataTypeFactory typeFactory) {
+    throw new UnsupportedOperationException("remove before optiq-0.9");
+  }
+}
+
+// End SqlLeadLagAggFunction.java

http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/99f39682/core/src/main/java/org/eigenbase/sql/fun/SqlNtileAggFunction.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/eigenbase/sql/fun/SqlNtileAggFunction.java b/core/src/main/java/org/eigenbase/sql/fun/SqlNtileAggFunction.java
new file mode 100644
index 0000000..ed13608
--- /dev/null
+++ b/core/src/main/java/org/eigenbase/sql/fun/SqlNtileAggFunction.java
@@ -0,0 +1,58 @@
+/*
+// Licensed to Julian Hyde under one or more contributor license
+// agreements. See the NOTICE file distributed with this work for
+// additional information regarding copyright ownership.
+//
+// Julian Hyde 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.eigenbase.sql.fun;
+
+import java.util.List;
+
+import org.eigenbase.reltype.RelDataType;
+import org.eigenbase.reltype.RelDataTypeFactory;
+import org.eigenbase.sql.SqlAggFunction;
+import org.eigenbase.sql.SqlFunctionCategory;
+import org.eigenbase.sql.SqlKind;
+import org.eigenbase.sql.type.OperandTypes;
+import org.eigenbase.sql.type.ReturnTypes;
+
+/**
+ * <code>NTILE</code> aggregate function
+ * return the value of given expression evaluated at given offset.
+ */
+public class SqlNtileAggFunction extends SqlAggFunction {
+  public SqlNtileAggFunction() {
+    super(
+        "NTILE",
+        SqlKind.OTHER_FUNCTION,
+        ReturnTypes.INTEGER,
+        null,
+        OperandTypes.POSITIVE_INTEGER_LITERAL,
+        SqlFunctionCategory.NUMERIC);
+  }
+
+  @Override public boolean requiresOrder() {
+    return true;
+  }
+
+  public List<RelDataType> getParameterTypes(RelDataTypeFactory typeFactory) {
+    throw new UnsupportedOperationException("remove before optiq-0.9");
+  }
+
+  public RelDataType getReturnType(RelDataTypeFactory typeFactory) {
+    throw new UnsupportedOperationException("remove before optiq-0.9");
+  }
+}
+
+// End SqlNtileAggFunction.java

http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/99f39682/core/src/main/java/org/eigenbase/sql/fun/SqlStdOperatorTable.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/eigenbase/sql/fun/SqlStdOperatorTable.java b/core/src/main/java/org/eigenbase/sql/fun/SqlStdOperatorTable.java
index afb062d..87f9177 100644
--- a/core/src/main/java/org/eigenbase/sql/fun/SqlStdOperatorTable.java
+++ b/core/src/main/java/org/eigenbase/sql/fun/SqlStdOperatorTable.java
@@ -624,6 +624,24 @@ public class SqlStdOperatorTable extends ReflectiveSqlOperatorTable {
       new SqlFirstLastValueAggFunction(true);
 
   /**
+   * <code>LEAD</code> aggregate function.
+   */
+  public static final SqlAggFunction LEAD =
+      new SqlLeadLagAggFunction(true);
+
+  /**
+   * <code>LAG</code> aggregate function.
+   */
+  public static final SqlAggFunction LAG =
+      new SqlLeadLagAggFunction(false);
+
+  /**
+   * <code>NTILE</code> aggregate function.
+   */
+  public static final SqlAggFunction NTILE =
+      new SqlNtileAggFunction();
+
+  /**
    * <code>SINGLE_VALUE</code> aggregate function.
    */
   public static final SqlAggFunction SINGLE_VALUE =

http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/99f39682/core/src/main/java/org/eigenbase/sql/type/CompositeOperandTypeChecker.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/eigenbase/sql/type/CompositeOperandTypeChecker.java b/core/src/main/java/org/eigenbase/sql/type/CompositeOperandTypeChecker.java
index 7bb4996..e1e3f9e 100644
--- a/core/src/main/java/org/eigenbase/sql/type/CompositeOperandTypeChecker.java
+++ b/core/src/main/java/org/eigenbase/sql/type/CompositeOperandTypeChecker.java
@@ -294,6 +294,12 @@ public class CompositeOperandTypeChecker
       default:
         if (!rule.checkOperandTypes(callBinding, false)) {
           typeErrorCount++;
+          if (composition == Composition.AND) {
+            // Avoid trying other rules in AND if the first one fails.
+            break label;
+          }
+        } else if (composition == Composition.OR) {
+          break label; // true OR any == true, just break
         }
         break;
       }

http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/99f39682/core/src/main/java/org/eigenbase/sql/type/SqlSingleOperandTypeChecker.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/eigenbase/sql/type/SqlSingleOperandTypeChecker.java b/core/src/main/java/org/eigenbase/sql/type/SqlSingleOperandTypeChecker.java
index 323d7b0..f81c916 100644
--- a/core/src/main/java/org/eigenbase/sql/type/SqlSingleOperandTypeChecker.java
+++ b/core/src/main/java/org/eigenbase/sql/type/SqlSingleOperandTypeChecker.java
@@ -21,7 +21,7 @@ import org.eigenbase.sql.*;
 
 /**
  * SqlSingleOperandTypeChecker is an extension of {@link SqlOperandTypeChecker}
- * for implementations which are cabable of checking the type of a single
+ * for implementations which are capable of checking the type of a single
  * operand in isolation. This isn't meaningful for all type-checking rules (e.g.
  * SameOperandTypeChecker requires two operands to have matching types, so
  * checking one in isolation is meaningless).

http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/99f39682/core/src/test/java/net/hydromatic/optiq/test/JdbcTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/net/hydromatic/optiq/test/JdbcTest.java b/core/src/test/java/net/hydromatic/optiq/test/JdbcTest.java
index 26f72bd..76d54a2 100644
--- a/core/src/test/java/net/hydromatic/optiq/test/JdbcTest.java
+++ b/core/src/test/java/net/hydromatic/optiq/test/JdbcTest.java
@@ -146,7 +146,7 @@ public class JdbcTest {
 
 
   public static final String START_OF_GROUP_DATA =
-      "values"
+      "(values"
       + "(1,0,1),\n"
       + "(2,0,1),\n"
       + "(3,1,2),\n"
@@ -3142,20 +3142,16 @@ public class JdbcTest {
             "deptno=20; R=4"); // 4 for rank and 2 for dense_rank
   }
 
-  /**
-   * Tests start_of_group approach for grouping of adjacent intervals.
-   * This is a step1, implemented as last_value.
-   * http://timurakhmadeev.wordpress.com/2013/07/21/start_of_group/
-   */
-  @Test public void testStartOfGroupLastValueStep1() {
+  private void startOfGroupStep1(String startOfGroup) {
     OptiqAssert.that()
         .with(OptiqAssert.Config.REGULAR)
         .query(
-                "select t.*\n"
+            "select t.*\n"
                 + "  from (\n"
                 + "       select  t.*,\n"
-                + "               case when val = last_value(val) over (order by rn rows between 1 preceding and 1 preceding) then 0 else 1 end start_of_group\n"
-                + "         from ("
+                + "               case when " + startOfGroup
+                + " then 0 else 1 end start_of_group\n"
+                + "         from "
                 + START_OF_GROUP_DATA
                 + ") t\n")
         .typeIs(
@@ -3169,25 +3165,20 @@ public class JdbcTest {
             "RN=6; VAL=0; EXPECTED=3; START_OF_GROUP=0",
             "RN=7; VAL=1; EXPECTED=4; START_OF_GROUP=1",
             "RN=8; VAL=1; EXPECTED=4; START_OF_GROUP=0");
-
   }
 
-  /**
-   * Tests start_of_group approach for grouping of adjacent intervals.
-   * This is a step2, that gets the final group numbers
-   * http://timurakhmadeev.wordpress.com/2013/07/21/start_of_group/
-   */
-  @Test public void testStartOfGroupLastValueStep2() {
+  private void startOfGroupStep2(String startOfGroup) {
     OptiqAssert.that()
         .with(OptiqAssert.Config.REGULAR)
         .query(
-                "select t.*\n"
+            "select t.*\n"
                 // current row is assumed, group_id should be NOT NULL
                 + "       ,sum(start_of_group) over (order by rn rows unbounded preceding) group_id\n"
                 + "  from (\n"
                 + "       select  t.*,\n"
-                + "               case when val = last_value(val) over (order by rn rows between 1 preceding and 1 preceding) then 0 else 1 end start_of_group\n"
-                + "         from ("
+                + "               case when " + startOfGroup
+                + " then 0 else 1 end start_of_group\n"
+                + "         from "
                 + START_OF_GROUP_DATA
                 + ") t\n")
         .typeIs(
@@ -3201,27 +3192,22 @@ public class JdbcTest {
             "RN=6; VAL=0; EXPECTED=3; START_OF_GROUP=0; GROUP_ID=3",
             "RN=7; VAL=1; EXPECTED=4; START_OF_GROUP=1; GROUP_ID=4",
             "RN=8; VAL=1; EXPECTED=4; START_OF_GROUP=0; GROUP_ID=4");
-
   }
 
-  /**
-   * Tests start_of_group approach for grouping of adjacent intervals.
-   * This is a step3, that aggregates the computed groups
-   * http://timurakhmadeev.wordpress.com/2013/07/21/start_of_group/
-   */
-  @Test public void testStartOfGroupLastValueStep3() {
+  private void startOfGroupStep3(String startOfGroup) {
     OptiqAssert.that()
         .with(OptiqAssert.Config.REGULAR)
         .query(
-                "select group_id, min(rn) min_rn, max(rn) max_rn, count(rn) cnt_rn, avg(val) avg_val"
+            "select group_id, min(rn) min_rn, max(rn) max_rn, count(rn) cnt_rn, avg(val) avg_val"
                 + " from (\n"
                 + "select t.*\n"
                 // current row is assumed, group_id should be NOT NULL
                 + "       ,sum(start_of_group) over (order by rn rows unbounded preceding) group_id\n"
                 + "  from (\n"
                 + "       select  t.*,\n"
-                + "               case when val = last_value(val) over (order by rn rows between 1 preceding and 1 preceding) then 0 else 1 end start_of_group\n"
-                + "         from ("
+                + "               case when " + startOfGroup
+                + " then 0 else 1 end start_of_group\n"
+                + "         from "
                 + START_OF_GROUP_DATA
                 + ") t\n"
                 + ") group by group_id\n")
@@ -3232,7 +3218,36 @@ public class JdbcTest {
             "GROUP_ID=2; MIN_RN=3; MAX_RN=3; CNT_RN=1; AVG_VAL=1",
             "GROUP_ID=3; MIN_RN=4; MAX_RN=6; CNT_RN=3; AVG_VAL=0",
             "GROUP_ID=4; MIN_RN=7; MAX_RN=8; CNT_RN=2; AVG_VAL=1");
+  }
+
+  /**
+   * Tests start_of_group approach for grouping of adjacent intervals.
+   * This is a step1, implemented as last_value.
+   * http://timurakhmadeev.wordpress.com/2013/07/21/start_of_group/
+   */
+  @Test public void testStartOfGroupLastValueStep1() {
+    startOfGroupStep1(
+        "val = last_value(val) over (order by rn rows between 1 preceding and 1 preceding)");
+  }
 
+  /**
+   * Tests start_of_group approach for grouping of adjacent intervals.
+   * This is a step2, that gets the final group numbers
+   * http://timurakhmadeev.wordpress.com/2013/07/21/start_of_group/
+   */
+  @Test public void testStartOfGroupLastValueStep2() {
+    startOfGroupStep2(
+        "val = last_value(val) over (order by rn rows between 1 preceding and 1 preceding)");
+  }
+
+  /**
+   * Tests start_of_group approach for grouping of adjacent intervals.
+   * This is a step3, that aggregates the computed groups
+   * http://timurakhmadeev.wordpress.com/2013/07/21/start_of_group/
+   */
+  @Test public void testStartOfGroupLastValueStep3() {
+    startOfGroupStep3(
+        "val = last_value(val) over (order by rn rows between 1 preceding and 1 preceding)");
   }
 
   /**
@@ -3240,94 +3255,225 @@ public class JdbcTest {
    * This is a step1, implemented as last_value.
    * http://timurakhmadeev.wordpress.com/2013/07/21/start_of_group/
    */
-  @Ignore("LEAD/LAG is not implemented yet")
   @Test public void testStartOfGroupLagStep1() {
+    startOfGroupStep1("val = lag(val) over (order by rn)");
+  }
+
+  /**
+   * Tests start_of_group approach for grouping of adjacent intervals.
+   * This is a step2, that gets the final group numbers
+   * http://timurakhmadeev.wordpress.com/2013/07/21/start_of_group/
+   */
+  @Test public void testStartOfGroupLagValueStep2() {
+    startOfGroupStep2("val = lag(val) over (order by rn)");
+  }
+
+  /**
+   * Tests start_of_group approach for grouping of adjacent intervals.
+   * This is a step3, that aggregates the computed groups
+   * http://timurakhmadeev.wordpress.com/2013/07/21/start_of_group/
+   */
+  @Test public void testStartOfGroupLagStep3() {
+    startOfGroupStep3("val = lag(val) over (order by rn)");
+  }
+
+  /**
+   * Tests start_of_group approach for grouping of adjacent intervals.
+   * This is a step1, implemented as last_value.
+   * http://timurakhmadeev.wordpress.com/2013/07/21/start_of_group/
+   */
+  @Test public void testStartOfGroupLeadStep1() {
+    startOfGroupStep1("val = lead(val, -1) over (order by rn)");
+  }
+
+  /**
+   * Tests start_of_group approach for grouping of adjacent intervals.
+   * This is a step2, that gets the final group numbers
+   * http://timurakhmadeev.wordpress.com/2013/07/21/start_of_group/
+   */
+  @Test public void testStartOfGroupLeadValueStep2() {
+    startOfGroupStep2("val = lead(val, -1) over (order by rn)");
+  }
+
+  /**
+   * Tests start_of_group approach for grouping of adjacent intervals.
+   * This is a step3, that aggregates the computed groups
+   * http://timurakhmadeev.wordpress.com/2013/07/21/start_of_group/
+   */
+  @Test public void testStartOfGroupLeadStep3() {
+    startOfGroupStep3("val = lead(val, -1) over (order by rn)");
+  }
+
+  /**
+   * Tests default value of LAG function.
+   */
+  @Test public void testLagDefaultValue() {
     OptiqAssert.that()
         .with(OptiqAssert.Config.REGULAR)
         .query(
-                "select t.*\n"
-                + "  from (\n"
-                + "       select  t.*,\n"
-                + "               case when val = lag(val) over (order by rn) then 0 else 1 end start_of_group\n"
-                + "         from ("
-                + START_OF_GROUP_DATA
-                + ") t\n")
+            "select t.*, lag(rn+expected,1,42) over (order by rn) l\n"
+            + " from " + START_OF_GROUP_DATA)
         .typeIs(
-            "[RN INTEGER NOT NULL, VAL INTEGER NOT NULL, EXPECTED INTEGER NOT NULL, START_OF_GROUP INTEGER NOT NULL]")
+            "[RN INTEGER NOT NULL, VAL INTEGER NOT NULL, EXPECTED INTEGER NOT NULL, L INTEGER NOT NULL]")
         .returnsUnordered(
-            "RN=1; VAL=0; EXPECTED=1; START_OF_GROUP=1",
-            "RN=2; VAL=0; EXPECTED=1; START_OF_GROUP=0",
-            "RN=3; VAL=1; EXPECTED=2; START_OF_GROUP=1",
-            "RN=4; VAL=0; EXPECTED=3; START_OF_GROUP=1",
-            "RN=5; VAL=0; EXPECTED=3; START_OF_GROUP=0",
-            "RN=6; VAL=0; EXPECTED=3; START_OF_GROUP=0",
-            "RN=7; VAL=1; EXPECTED=4; START_OF_GROUP=1",
-            "RN=8; VAL=1; EXPECTED=4; START_OF_GROUP=0");
+            "RN=1; VAL=0; EXPECTED=1; L=42",
+            "RN=2; VAL=0; EXPECTED=1; L=2",
+            "RN=3; VAL=1; EXPECTED=2; L=3",
+            "RN=4; VAL=0; EXPECTED=3; L=5",
+            "RN=5; VAL=0; EXPECTED=3; L=7",
+            "RN=6; VAL=0; EXPECTED=3; L=8",
+            "RN=7; VAL=1; EXPECTED=4; L=9",
+            "RN=8; VAL=1; EXPECTED=4; L=11");
+  }
 
+  /**
+   * Tests default value of LEAD function.
+   */
+  @Test public void testLeadDefaultValue() {
+    OptiqAssert.that()
+        .with(OptiqAssert.Config.REGULAR)
+        .query(
+            "select t.*, lead(rn+expected,1,42) over (order by rn) l\n"
+            + " from " + START_OF_GROUP_DATA)
+        .typeIs(
+            "[RN INTEGER NOT NULL, VAL INTEGER NOT NULL, EXPECTED INTEGER NOT NULL, L INTEGER NOT NULL]")
+        .returnsUnordered(
+            "RN=1; VAL=0; EXPECTED=1; L=3",
+            "RN=2; VAL=0; EXPECTED=1; L=5",
+            "RN=3; VAL=1; EXPECTED=2; L=7",
+            "RN=4; VAL=0; EXPECTED=3; L=8",
+            "RN=5; VAL=0; EXPECTED=3; L=9",
+            "RN=6; VAL=0; EXPECTED=3; L=11",
+            "RN=7; VAL=1; EXPECTED=4; L=12",
+            "RN=8; VAL=1; EXPECTED=4; L=42");
   }
 
   /**
-   * Tests start_of_group approach for grouping of adjacent intervals.
-   * This is a step2, that gets the final group numbers
-   * http://timurakhmadeev.wordpress.com/2013/07/21/start_of_group/
+   * Tests expression in offset value of LAG function.
    */
-  @Ignore("LEAD/LAG is not implemented yet")
-  @Test public void testStartOfGroupLagValueStep2() {
+  @Test public void testLagExpressionOffset() {
     OptiqAssert.that()
         .with(OptiqAssert.Config.REGULAR)
         .query(
-                "select t.*\n"
-                + "       ,sum(start_of_group) over (order by rn rows between unbounded preceding and current row) group_id\n"
-                + "  from (\n"
-                + "       select  t.*,\n"
-                + "               case when val = lag(val) over (order by rn) then 0 else 1 end start_of_group\n"
-                + "         from ("
-                + START_OF_GROUP_DATA
-                + ") t\n")
+            "select t.*, lag(rn, expected, 42) over (order by rn) l\n"
+            + " from " + START_OF_GROUP_DATA)
         .typeIs(
-            "[RN INTEGER NOT NULL, VAL INTEGER NOT NULL, EXPECTED INTEGER NOT NULL, START_OF_GROUP INTEGER NOT NULL, GROUP_ID INTEGER NOT NULL]")
+            "[RN INTEGER NOT NULL, VAL INTEGER NOT NULL, EXPECTED INTEGER NOT NULL, L INTEGER NOT NULL]")
         .returnsUnordered(
-            "RN=1; VAL=0; EXPECTED=1; START_OF_GROUP=1; GROUP_ID=1",
-            "RN=2; VAL=0; EXPECTED=1; START_OF_GROUP=0; GROUP_ID=1",
-            "RN=3; VAL=1; EXPECTED=2; START_OF_GROUP=1; GROUP_ID=2",
-            "RN=4; VAL=0; EXPECTED=3; START_OF_GROUP=1; GROUP_ID=3",
-            "RN=5; VAL=0; EXPECTED=3; START_OF_GROUP=0; GROUP_ID=3",
-            "RN=6; VAL=0; EXPECTED=3; START_OF_GROUP=0; GROUP_ID=3",
-            "RN=7; VAL=1; EXPECTED=4; START_OF_GROUP=1; GROUP_ID=4",
-            "RN=8; VAL=1; EXPECTED=4; START_OF_GROUP=0; GROUP_ID=4");
+            "RN=1; VAL=0; EXPECTED=1; L=42",
+            "RN=2; VAL=0; EXPECTED=1; L=1",
+            "RN=3; VAL=1; EXPECTED=2; L=1",
+            "RN=4; VAL=0; EXPECTED=3; L=1",
+            "RN=5; VAL=0; EXPECTED=3; L=2",
+            "RN=6; VAL=0; EXPECTED=3; L=3",
+            "RN=7; VAL=1; EXPECTED=4; L=3",
+            "RN=8; VAL=1; EXPECTED=4; L=4");
+  }
 
+  /**
+   * Tests DATE as offset argument of LAG function.
+   */
+  @Test public void testLagInvalidOffsetArgument() {
+    OptiqAssert.that()
+        .with(OptiqAssert.Config.REGULAR)
+        .query(
+            "select t.*, lag(rn, DATE '2014-06-20', 42) over (order by rn) l\n"
+            + " from " + START_OF_GROUP_DATA)
+        .throws_(
+            "Cannot apply 'LAG' to arguments of type 'LAG(<INTEGER>, <DATE>, <INTEGER>)'");
   }
 
   /**
-   * Tests start_of_group approach for grouping of adjacent intervals.
-   * This is a step3, that aggregates the computed groups
-   * http://timurakhmadeev.wordpress.com/2013/07/21/start_of_group/
+   * Tests NTILE(2).
    */
-  @Ignore("LEAD/LAG is not implemented yet")
-  @Test public void testStartOfGroupLagStep3() {
+  @Test public void testNtile1() {
     OptiqAssert.that()
         .with(OptiqAssert.Config.REGULAR)
         .query(
-            "select group_id, min(rn) min_rn, max(rn) max_rn, count(rn) cnt_rn, avg(val) avg_val"
-                + " from (\n"
-                + "select t.*\n"
-                // current row is assumed, group_id should be NOT NULL
-                + "       ,sum(start_of_group) over (order by rn rows unbounded preceding) group_id\n"
-                + "  from (\n"
-                + "       select  t.*,\n"
-                + "               case when val = lag(val) over (order by rn) then 0 else 1 end start_of_group\n"
-                + "         from ("
-                + START_OF_GROUP_DATA
-                + ") t\n"
-                + ") group by group_id\n")
+            "select rn, ntile(1) over (order by rn) l\n"
+                + " from " + START_OF_GROUP_DATA)
         .typeIs(
-            "[GROUP_ID INTEGER NOT NULL, MIN_RN INTEGER NOT NULL, MAX_RN INTEGER NOT NULL, CNT_RN BIGINT NOT NULL, AVG_VAL INTEGER NOT NULL]")
+            "[RN INTEGER NOT NULL, L INTEGER NOT NULL]")
         .returnsUnordered(
-            "GROUP_ID=1; MIN_RN=1; MAX_RN=2; CNT_RN=2; AVG_VAL=0",
-            "GROUP_ID=2; MIN_RN=3; MAX_RN=3; CNT_RN=1; AVG_VAL=1",
-            "GROUP_ID=3; MIN_RN=4; MAX_RN=6; CNT_RN=3; AVG_VAL=0",
-            "GROUP_ID=4; MIN_RN=7; MAX_RN=8; CNT_RN=2; AVG_VAL=1");
+            "RN=1; L=1",
+            "RN=2; L=1",
+            "RN=3; L=1",
+            "RN=4; L=1",
+            "RN=5; L=1",
+            "RN=6; L=1",
+            "RN=7; L=1",
+            "RN=8; L=1");
+  }
 
+  /**
+   * Tests NTILE(2).
+   */
+  @Test public void testNtile2() {
+    OptiqAssert.that()
+        .with(OptiqAssert.Config.REGULAR)
+        .query(
+            "select rn, ntile(2) over (order by rn) l\n"
+                + " from " + START_OF_GROUP_DATA)
+        .typeIs(
+            "[RN INTEGER NOT NULL, L INTEGER NOT NULL]")
+        .returnsUnordered(
+            "RN=1; L=1",
+            "RN=2; L=1",
+            "RN=3; L=1",
+            "RN=4; L=1",
+            "RN=5; L=2",
+            "RN=6; L=2",
+            "RN=7; L=2",
+            "RN=8; L=2");
+  }
+
+  /**
+   * Tests expression in offset value of LAG function.
+   */
+  @Ignore("Have no idea how to validate that expression is constant")
+  @Test public void testNtileConstantArgs() {
+    OptiqAssert.that()
+        .with(OptiqAssert.Config.REGULAR)
+        .query(
+            "select rn, ntile(1+1) over (order by rn) l\n"
+                + " from " + START_OF_GROUP_DATA)
+        .typeIs(
+            "[RN INTEGER NOT NULL, VAL INTEGER NOT NULL, EXPECTED INTEGER NOT NULL, L INTEGER NOT NULL]")
+        .returnsUnordered(
+            "RN=1; L=1",
+            "RN=2; L=1",
+            "RN=3; L=1",
+            "RN=4; L=1",
+            "RN=5; L=2",
+            "RN=6; L=2",
+            "RN=7; L=2",
+            "RN=8; L=2");
+  }
+
+  /**
+   * Tests expression in offset value of LAG function.
+   */
+  @Test public void testNtileNegativeArg() {
+    OptiqAssert.that()
+        .with(OptiqAssert.Config.REGULAR)
+        .query(
+            "select rn, ntile(-1) over (order by rn) l\n"
+                + " from " + START_OF_GROUP_DATA)
+        .throws_(
+            "Argument to function 'NTILE' must be a positive integer literal");
+  }
+
+  /**
+   * Tests expression in offset value of LAG function.
+   */
+  @Test public void testNtileDecimalArg() {
+    OptiqAssert.that()
+        .with(OptiqAssert.Config.REGULAR)
+        .query(
+            "select rn, ntile(3.141592653) over (order by rn) l\n"
+                + " from " + START_OF_GROUP_DATA)
+        .throws_(
+            "Cannot apply 'NTILE' to arguments of type 'NTILE(<DECIMAL(10, 9)>)'");
   }
 
   /** Tests for FIRST_VALUE */