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 2015/11/21 02:54:59 UTC

[1/5] calcite git commit: Tests for [CALCITE-974]; can now convert INSERT, UPDATE, DELETE

Repository: calcite
Updated Branches:
  refs/heads/master 8cd8ad925 -> 3e7efce6e


Tests for [CALCITE-974]; can now convert INSERT, UPDATE, DELETE


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

Branch: refs/heads/master
Commit: 4e61eae560f5e2e46ca96a1002522cd1a3317a4e
Parents: 737846e
Author: Julian Hyde <jh...@apache.org>
Authored: Thu Nov 19 15:01:28 2015 -0800
Committer: Julian Hyde <jh...@apache.org>
Committed: Thu Nov 19 17:08:20 2015 -0800

----------------------------------------------------------------------
 .../calcite/test/SqlToRelConverterTest.java     | 41 +++++++++++
 .../calcite/test/SqlToRelConverterTest.xml      | 74 ++++++++++++++++++++
 2 files changed, 115 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/calcite/blob/4e61eae5/core/src/test/java/org/apache/calcite/test/SqlToRelConverterTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/test/SqlToRelConverterTest.java b/core/src/test/java/org/apache/calcite/test/SqlToRelConverterTest.java
index dbd8a63..10cdb4e 100644
--- a/core/src/test/java/org/apache/calcite/test/SqlToRelConverterTest.java
+++ b/core/src/test/java/org/apache/calcite/test/SqlToRelConverterTest.java
@@ -1182,6 +1182,47 @@ public class SqlToRelConverterTest extends SqlToRelTestBase {
         .convertsTo("${plan}");
   }
 
+  @Test public void testDelete() {
+    sql("delete from emp")
+        .convertsTo("${plan}");
+  }
+
+  @Test public void testDeleteWhere() {
+    sql("delete from emp where deptno = 10")
+        .convertsTo("${plan}");
+  }
+
+  @Test public void testUpdate() {
+    sql("update emp set empno = empno + 1")
+        .convertsTo("${plan}");
+  }
+
+  @Test public void testUpdateSubQuery() {
+    final String sql = "update emp\n"
+        + "set empno = (\n"
+        + "  select min(empno) from emp as e where e.deptno = emp.deptno)";
+    sql(sql)
+        .convertsTo("${plan}");
+  }
+
+  @Test public void testUpdateWhere() {
+    sql("update emp set empno = empno + 1 where deptno = 10")
+        .convertsTo("${plan}");
+  }
+
+  @Ignore("CALCITE-985")
+  @Test public void testMerge() {
+    final String sql = "merge into emp as target\n"
+        + "using (select * from emp where deptno = 30) as source\n"
+        + "on target.empno = source.empno\n"
+        + "when matched then\n"
+        + "  update set sal = sal + source.sal\n"
+        + "when not matched then\n"
+        + "  insert (empno, deptno, sal)\n"
+        + "  values (source.empno, source.deptno, source.sal)";
+    sql(sql).convertsTo("${plan}");
+  }
+
   @Test public void testSelectView() {
     // translated condition: deptno = 20 and sal > 1000 and empno > 100
     sql("select * from emp_20 where empno > 100")

http://git-wip-us.apache.org/repos/asf/calcite/blob/4e61eae5/core/src/test/resources/org/apache/calcite/test/SqlToRelConverterTest.xml
----------------------------------------------------------------------
diff --git a/core/src/test/resources/org/apache/calcite/test/SqlToRelConverterTest.xml b/core/src/test/resources/org/apache/calcite/test/SqlToRelConverterTest.xml
index e145812..4500149 100644
--- a/core/src/test/resources/org/apache/calcite/test/SqlToRelConverterTest.xml
+++ b/core/src/test/resources/org/apache/calcite/test/SqlToRelConverterTest.xml
@@ -2557,4 +2557,78 @@ LogicalProject(A=[$0], *=[$1])
 ]]>
         </Resource>
     </TestCase>
+    <TestCase name="testDeleteWhere">
+        <Resource name="sql">
+            <![CDATA[delete from emp where deptno = 10]]>
+        </Resource>
+        <Resource name="plan">
+            <![CDATA[
+LogicalTableModify(table=[[CATALOG, SALES, EMP]], operation=[DELETE], updateColumnList=[[]], flattened=[true])
+  LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8])
+    LogicalFilter(condition=[=($7, 10)])
+      LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+]]>
+        </Resource>
+    </TestCase>
+    <TestCase name="testUpdateWhere">
+        <Resource name="sql">
+            <![CDATA[update emp set empno = empno + 1 where deptno = 10]]>
+        </Resource>
+        <Resource name="plan">
+            <![CDATA[
+LogicalTableModify(table=[[CATALOG, SALES, EMP]], operation=[UPDATE], updateColumnList=[[EMPNO]], flattened=[true])
+  LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8], EXPR$0=[+($0, 1)])
+    LogicalFilter(condition=[=($7, 10)])
+      LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+]]>
+        </Resource>
+    </TestCase>
+    <TestCase name="testDelete">
+        <Resource name="sql">
+            <![CDATA[delete from emp]]>
+        </Resource>
+        <Resource name="plan">
+            <![CDATA[
+LogicalTableModify(table=[[CATALOG, SALES, EMP]], operation=[DELETE], updateColumnList=[[]], flattened=[true])
+  LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8])
+    LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+]]>
+        </Resource>
+    </TestCase>
+    <TestCase name="testUpdate">
+        <Resource name="sql">
+            <![CDATA[update emp set empno = empno + 1]]>
+        </Resource>
+        <Resource name="plan">
+            <![CDATA[
+LogicalTableModify(table=[[CATALOG, SALES, EMP]], operation=[UPDATE], updateColumnList=[[EMPNO]], flattened=[true])
+  LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8], EXPR$0=[+($0, 1)])
+    LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+]]>
+        </Resource>
+    </TestCase>
+    <TestCase name="testUpdateSubQuery">
+        <Resource name="sql">
+            <![CDATA[update emp
+set empno = (
+  select min(empno) from emp as e where e.deptno = emp.deptno)]]>
+        </Resource>
+        <Resource name="plan">
+            <![CDATA[
+LogicalTableModify(table=[[CATALOG, SALES, EMP]], operation=[UPDATE], updateColumnList=[[EMPNO]], flattened=[true])
+  LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8], EXPR$0=[$9])
+    LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8], EXPR$0=[$10])
+      LogicalJoin(condition=[=($7, $9)], joinType=[left])
+        LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+        LogicalAggregate(group=[{0}], EXPR$0=[MIN($1)])
+          LogicalProject(DEPTNO0=[$1], EMPNO=[$0])
+            LogicalProject(EMPNO=[$0], DEPTNO0=[$9])
+              LogicalJoin(condition=[=($7, $9)], joinType=[inner])
+                LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+                LogicalAggregate(group=[{0}])
+                  LogicalProject(DEPTNO=[$7])
+                    LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+]]>
+        </Resource>
+    </TestCase>
 </Root>


[3/5] calcite git commit: Factor user-defined function tests from JdbcTest to UdfTest, and classes into Smalls

Posted by jh...@apache.org.
http://git-wip-us.apache.org/repos/asf/calcite/blob/16478ee1/core/src/test/java/org/apache/calcite/util/Smalls.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/util/Smalls.java b/core/src/test/java/org/apache/calcite/util/Smalls.java
new file mode 100644
index 0000000..3941eda
--- /dev/null
+++ b/core/src/test/java/org/apache/calcite/util/Smalls.java
@@ -0,0 +1,543 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.calcite.util;
+
+import org.apache.calcite.DataContext;
+import org.apache.calcite.adapter.java.AbstractQueryableTable;
+import org.apache.calcite.linq4j.BaseQueryable;
+import org.apache.calcite.linq4j.Enumerable;
+import org.apache.calcite.linq4j.Enumerator;
+import org.apache.calcite.linq4j.Linq4j;
+import org.apache.calcite.linq4j.QueryProvider;
+import org.apache.calcite.linq4j.Queryable;
+import org.apache.calcite.linq4j.function.Function1;
+import org.apache.calcite.linq4j.function.Function2;
+import org.apache.calcite.linq4j.function.Parameter;
+import org.apache.calcite.linq4j.tree.Types;
+import org.apache.calcite.rel.type.RelDataType;
+import org.apache.calcite.rel.type.RelDataTypeFactory;
+import org.apache.calcite.rel.type.RelProtoDataType;
+import org.apache.calcite.rex.RexLiteral;
+import org.apache.calcite.schema.QueryableTable;
+import org.apache.calcite.schema.ScannableTable;
+import org.apache.calcite.schema.SchemaPlus;
+import org.apache.calcite.schema.TranslatableTable;
+import org.apache.calcite.schema.impl.AbstractTable;
+import org.apache.calcite.schema.impl.ViewTable;
+import org.apache.calcite.sql.SqlDialect;
+import org.apache.calcite.sql.type.SqlTypeName;
+
+import com.google.common.collect.ImmutableList;
+
+import java.lang.reflect.Method;
+import java.util.AbstractList;
+import java.util.List;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+
+/**
+ * Holder for various classes and functions used in tests as user-defined
+ * functions and so forth.
+ */
+public class Smalls {
+  public static final Method GENERATE_STRINGS_METHOD =
+      Types.lookupMethod(Smalls.class, "generateStrings", Integer.class);
+  public static final Method MAZE_METHOD =
+      Types.lookupMethod(MazeTable.class, "generate", int.class, int.class,
+          int.class);
+  public static final Method MAZE2_METHOD =
+      Types.lookupMethod(MazeTable.class, "generate2", int.class, int.class,
+          Integer.class);
+  public static final Method MAZE3_METHOD =
+      Types.lookupMethod(MazeTable.class, "generate3", String.class);
+  public static final Method MULTIPLICATION_TABLE_METHOD =
+      Types.lookupMethod(Smalls.class, "multiplicationTable", int.class,
+        int.class, Integer.class);
+  public static final Method VIEW_METHOD =
+      Types.lookupMethod(Smalls.class, "view", String.class);
+  public static final Method STR_METHOD =
+      Types.lookupMethod(Smalls.class, "str", Object.class, Object.class);
+  public static final Method STRING_UNION_METHOD =
+      Types.lookupMethod(Smalls.class, "stringUnion", Queryable.class,
+          Queryable.class);
+  public static final Method PROCESS_CURSOR_METHOD =
+      Types.lookupMethod(Smalls.class, "processCursor",
+          int.class, Enumerable.class);
+  public static final Method PROCESS_CURSORS_METHOD =
+      Types.lookupMethod(Smalls.class, "processCursors",
+          int.class, Enumerable.class, Enumerable.class);
+
+  private Smalls() {}
+
+  private static QueryableTable oneThreePlus(String s) {
+    List<Integer> items;
+    // Argument is null in case SQL contains function call with expression.
+    // Then the engine calls a function with null arguments to get getRowType.
+    if (s == null) {
+      items = ImmutableList.of();
+    } else {
+      Integer latest = Integer.parseInt(s.substring(1, s.length() - 1));
+      items = ImmutableList.of(1, 3, latest);
+    }
+    final Enumerable<Integer> enumerable = Linq4j.asEnumerable(items);
+    return new AbstractQueryableTable(Integer.class) {
+      public <E> Queryable<E> asQueryable(
+          QueryProvider queryProvider, SchemaPlus schema, String tableName) {
+        //noinspection unchecked
+        return (Queryable<E>) enumerable.asQueryable();
+      }
+
+      public RelDataType getRowType(RelDataTypeFactory typeFactory) {
+        return typeFactory.builder().add("c", SqlTypeName.INTEGER).build();
+      }
+    };
+  }
+
+  public static <T> Queryable<T> stringUnion(
+      Queryable<T> q0, Queryable<T> q1) {
+    return q0.concat(q1);
+  }
+
+  /** A function that generates a table that generates a sequence of
+   * {@link IntString} values. */
+  public static QueryableTable generateStrings(final Integer count) {
+    return new AbstractQueryableTable(IntString.class) {
+      public RelDataType getRowType(RelDataTypeFactory typeFactory) {
+        return typeFactory.createJavaType(IntString.class);
+      }
+
+      public <T> Queryable<T> asQueryable(QueryProvider queryProvider,
+          SchemaPlus schema, String tableName) {
+        BaseQueryable<IntString> queryable =
+            new BaseQueryable<IntString>(null, IntString.class, null) {
+              public Enumerator<IntString> enumerator() {
+                return new Enumerator<IntString>() {
+                  static final String Z = "abcdefghijklm";
+
+                  int i = 0;
+                  int curI;
+                  String curS;
+
+                  public IntString current() {
+                    return new IntString(curI, curS);
+                  }
+
+                  public boolean moveNext() {
+                    if (i < count) {
+                      curI = i;
+                      curS = Z.substring(0, i % Z.length());
+                      ++i;
+                      return true;
+                    } else {
+                      return false;
+                    }
+                  }
+
+                  public void reset() {
+                    i = 0;
+                  }
+
+                  public void close() {
+                  }
+                };
+              }
+            };
+        //noinspection unchecked
+        return (Queryable<T>) queryable;
+      }
+    };
+  }
+
+  /** A function that generates multiplication table of {@code ncol} columns x
+   * {@code nrow} rows. */
+  public static QueryableTable multiplicationTable(final int ncol,
+      final int nrow, Integer offset) {
+    final int offs = offset == null ? 0 : offset;
+    return new AbstractQueryableTable(Object[].class) {
+      public RelDataType getRowType(RelDataTypeFactory typeFactory) {
+        final RelDataTypeFactory.FieldInfoBuilder builder =
+            typeFactory.builder();
+        builder.add("row_name", typeFactory.createJavaType(String.class));
+        final RelDataType int_ = typeFactory.createJavaType(int.class);
+        for (int i = 1; i <= ncol; i++) {
+          builder.add("c" + i, int_);
+        }
+        return builder.build();
+      }
+
+      public Queryable<Object[]> asQueryable(QueryProvider queryProvider,
+          SchemaPlus schema, String tableName) {
+        final List<Object[]> table = new AbstractList<Object[]>() {
+          @Override public Object[] get(int index) {
+            Object[] cur = new Object[ncol + 1];
+            cur[0] = "row " + index;
+            for (int j = 1; j <= ncol; j++) {
+              cur[j] = j * (index + 1) + offs;
+            }
+            return cur;
+          }
+
+          @Override public int size() {
+            return nrow;
+          }
+        };
+        return Linq4j.asEnumerable(table).asQueryable();
+      }
+    };
+  }
+
+  /**
+   * A function that adds a number to the first column of input cursor
+   */
+  public static QueryableTable processCursor(final int offset,
+      final Enumerable<Object[]> a) {
+    return new AbstractQueryableTable(Object[].class) {
+      public RelDataType getRowType(RelDataTypeFactory typeFactory) {
+        return typeFactory.builder()
+            .add("result", SqlTypeName.INTEGER)
+            .build();
+      }
+
+      public <T> Queryable<T> asQueryable(QueryProvider queryProvider,
+          SchemaPlus schema, String tableName) {
+        final Enumerable<Integer> enumerable =
+            a.select(new Function1<Object[], Integer>() {
+              public Integer apply(Object[] a0) {
+                return offset + ((Integer) a0[0]);
+              }
+            });
+        //noinspection unchecked
+        return (Queryable) enumerable.asQueryable();
+      }
+    };
+  }
+
+  /**
+   * A function that sums the second column of first input cursor, second
+   * column of first input and the given int.
+   */
+  public static QueryableTable processCursors(final int offset,
+      final Enumerable<Object[]> a, final Enumerable<IntString> b) {
+    return new AbstractQueryableTable(Object[].class) {
+      public RelDataType getRowType(RelDataTypeFactory typeFactory) {
+        return typeFactory.builder()
+            .add("result", SqlTypeName.INTEGER)
+            .build();
+      }
+
+      public <T> Queryable<T> asQueryable(QueryProvider queryProvider,
+          SchemaPlus schema, String tableName) {
+        final Enumerable<Integer> enumerable =
+            a.zip(b, new Function2<Object[], IntString, Integer>() {
+              public Integer apply(Object[] v0, IntString v1) {
+                return ((Integer) v0[1]) + v1.n + offset;
+              }
+            });
+        //noinspection unchecked
+        return (Queryable) enumerable.asQueryable();
+      }
+    };
+  }
+
+  public static TranslatableTable view(String s) {
+    return new ViewTable(Object.class,
+        new RelProtoDataType() {
+          public RelDataType apply(RelDataTypeFactory typeFactory) {
+            return typeFactory.builder().add("c", SqlTypeName.INTEGER)
+                .build();
+          }
+        }, "values (1), (3), " + s, ImmutableList.<String>of());
+  }
+
+  public static TranslatableTable str(Object o, Object p) {
+    assertThat(RexLiteral.validConstant(o, Litmus.THROW), is(true));
+    assertThat(RexLiteral.validConstant(p, Litmus.THROW), is(true));
+    return new ViewTable(Object.class,
+        new RelProtoDataType() {
+          public RelDataType apply(RelDataTypeFactory typeFactory) {
+            return typeFactory.builder().add("c", SqlTypeName.VARCHAR, 100)
+                .build();
+          }
+        },
+        "values " + SqlDialect.CALCITE.quoteStringLiteral(o.toString())
+            + ", " + SqlDialect.CALCITE.quoteStringLiteral(p.toString()),
+        ImmutableList.<String>of());
+  }
+
+  /** Class with int and String fields. */
+  public static class IntString {
+    public final int n;
+    public final String s;
+
+    public IntString(int n, String s) {
+      this.n = n;
+      this.s = s;
+    }
+
+    public String toString() {
+      return "{n=" + n + ", s=" + s + "}";
+    }
+  }
+
+  /** Example of a UDF with a non-static {@code eval} method,
+   * and named parameters. */
+  public static class MyPlusFunction {
+    public int eval(@Parameter(name = "x") int x, @Parameter(name = "y") int y) {
+      return x + y;
+    }
+  }
+
+  /** Example of a UDF with named parameters. */
+  public static class MyLeftFunction {
+    public String eval(@Parameter(name = "s") String s,
+        @Parameter(name = "n") int n) {
+      return s.substring(0, n);
+    }
+  }
+
+  /** Example of a UDF with named parameters, some of them optional. */
+  public static class MyAbcdeFunction {
+    public String eval(@Parameter(name = "A", optional = false) Integer a,
+        @Parameter(name = "B", optional = true) Integer b,
+        @Parameter(name = "C", optional = false) Integer c,
+        @Parameter(name = "D", optional = true) Integer d,
+        @Parameter(name = "E", optional = true) Integer e) {
+      return "{a: " + a + ", b: " + b +  ", c: " + c +  ", d: " + d  + ", e: "
+          + e + "}";
+    }
+  }
+
+  /** Example of a non-strict UDF. (Does something useful when passed NULL.) */
+  public static class MyToStringFunction {
+    public static String eval(@Parameter(name = "o") Object o) {
+      if (o == null) {
+        return "<null>";
+      }
+      return "<" + o.toString() + ">";
+    }
+  }
+
+  /** Example of a UDF with a static {@code eval} method. Class is abstract,
+   * but code-generator should not need to instantiate it. */
+  public abstract static class MyDoubleFunction {
+    private MyDoubleFunction() {
+    }
+
+    public static int eval(int x) {
+      return x * 2;
+    }
+  }
+
+  /** User-defined function with two arguments. */
+  public static class MyIncrement {
+    public float eval(int x, int y) {
+      return x + x * y / 100;
+    }
+  }
+
+  /** Example of a UDF that has overloaded UDFs (same name, different args). */
+  public abstract static class CountArgs0Function {
+    private CountArgs0Function() {}
+
+    public static int eval() {
+      return 0;
+    }
+  }
+
+  /** See {@link CountArgs0Function}. */
+  public abstract static class CountArgs1Function {
+    private CountArgs1Function() {}
+
+    public static int eval(int x) {
+      return 1;
+    }
+  }
+
+  /** See {@link CountArgs0Function}. */
+  public abstract static class CountArgs2Function {
+    private CountArgs2Function() {}
+
+    public static int eval(int x, int y) {
+      return 2;
+    }
+  }
+
+  /** Example of a UDF class that needs to be instantiated but cannot be. */
+  public abstract static class AwkwardFunction {
+    private AwkwardFunction() {
+    }
+
+    public int eval(int x) {
+      return 0;
+    }
+  }
+
+  /** UDF class that has multiple methods, some overloaded. */
+  public static class MultipleFunction {
+    private MultipleFunction() {}
+
+    // Three overloads
+    public static String fun1(String x) { return x.toLowerCase(); }
+    public static int fun1(int x) { return x * 2; }
+    public static int fun1(int x, int y) { return x + y; }
+
+    // Another method
+    public static int fun2(int x) { return x * 3; }
+
+    // Non-static method cannot be used because constructor is private
+    public int nonStatic(int x) { return x * 3; }
+  }
+
+  /** Example of a user-defined aggregate function (UDAF). */
+  public static class MySumFunction {
+    public MySumFunction() {
+    }
+    public long init() {
+      return 0L;
+    }
+    public long add(long accumulator, int v) {
+      return accumulator + v;
+    }
+    public long merge(long accumulator0, long accumulator1) {
+      return accumulator0 + accumulator1;
+    }
+    public long result(long accumulator) {
+      return accumulator;
+    }
+  }
+
+  /** Example of a user-defined aggregate function (UDAF), whose methods are
+   * static. */
+  public static class MyStaticSumFunction {
+    public static long init() {
+      return 0L;
+    }
+    public static long add(long accumulator, int v) {
+      return accumulator + v;
+    }
+    public static long merge(long accumulator0, long accumulator1) {
+      return accumulator0 + accumulator1;
+    }
+    public static long result(long accumulator) {
+      return accumulator;
+    }
+  }
+
+  /** User-defined function. */
+  public static class SumFunctionBadIAdd {
+    public long init() {
+      return 0L;
+    }
+    public long add(short accumulator, int v) {
+      return accumulator + v;
+    }
+  }
+
+  /** User-defined table-macro function. */
+  public static class TableMacroFunction {
+    public TranslatableTable eval(String s) {
+      return view(s);
+    }
+  }
+
+  /** User-defined table-macro function whose eval method is static. */
+  public static class StaticTableMacroFunction {
+    public static TranslatableTable eval(String s) {
+      return view(s);
+    }
+  }
+
+  /** User-defined table-macro function with named and optional parameters. */
+  public static class TableMacroFunctionWithNamedParameters {
+    public TranslatableTable eval(
+        @Parameter(name = "R", optional = true) String r,
+        @Parameter(name = "S") String s,
+        @Parameter(name = "T", optional = true) Integer t) {
+      final StringBuilder sb = new StringBuilder();
+      abc(sb, r);
+      abc(sb, s);
+      abc(sb, t);
+      return view(sb.toString());
+    }
+
+    private void abc(StringBuilder sb, Object s) {
+      if (s != null) {
+        if (sb.length() > 0) {
+          sb.append(", ");
+        }
+        sb.append('(').append(s).append(')');
+      }
+    }
+  }
+
+  /** A table function that returns a {@link QueryableTable}. */
+  public static class MyTableFunction {
+    public QueryableTable eval(String s) {
+      return oneThreePlus(s);
+    }
+  }
+
+  /** A table function that returns a {@link QueryableTable} via a
+   * static method. */
+  public static class TestStaticTableFunction {
+    public static QueryableTable eval(String s) {
+      return oneThreePlus(s);
+    }
+  }
+
+  /** The real MazeTable may be found in example/function. This is a cut-down
+   * version to support a test. */
+  public static class MazeTable extends AbstractTable
+      implements ScannableTable {
+
+    private final String content;
+
+    public MazeTable(String content) {
+      this.content = content;
+    }
+
+    public static ScannableTable generate(int width, int height, int seed) {
+      return new MazeTable(String.format("generate(w=%d, h=%d, s=%d)", width, height, seed));
+    }
+
+    public static ScannableTable generate2(
+        @Parameter(name = "WIDTH") int width,
+        @Parameter(name = "HEIGHT") int height,
+        @Parameter(name = "SEED", optional = true) Integer seed) {
+      return new MazeTable(String.format("generate2(w=%d, h=%d, s=%d)", width, height, seed));
+    }
+
+    public static ScannableTable generate3(
+        @Parameter(name = "FOO") String foo) {
+      return new MazeTable(String.format("generate3(foo=%s)", foo));
+    }
+
+    public RelDataType getRowType(RelDataTypeFactory typeFactory) {
+      return typeFactory.builder()
+          .add("S", SqlTypeName.VARCHAR, 12)
+          .build();
+    }
+
+    public Enumerable<Object[]> scan(DataContext root) {
+      Object[][] rows = {{"abcde"}, {"xyz"}, {content}};
+      return Linq4j.asEnumerable(rows);
+    }
+  }
+}
+
+// End Smalls.java


[2/5] calcite git commit: [CALCITE-974] Exception while validating DELETE (Yuri Au Yong)

Posted by jh...@apache.org.
[CALCITE-974] Exception while validating DELETE (Yuri Au Yong)


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

Branch: refs/heads/master
Commit: 737846ee992cbeb6513075fdda5d99b42b6cf515
Parents: 8cd8ad9
Author: Yuri Au Yong <yu...@persistent.co.in>
Authored: Thu Nov 19 14:59:46 2015 -0800
Committer: Julian Hyde <jh...@apache.org>
Committed: Thu Nov 19 17:08:20 2015 -0800

----------------------------------------------------------------------
 .../apache/calcite/sql/validate/SqlValidatorImpl.java    | 11 +++++------
 1 file changed, 5 insertions(+), 6 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/calcite/blob/737846ee/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorImpl.java b/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorImpl.java
index cac83c1..0390c07 100644
--- a/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorImpl.java
+++ b/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorImpl.java
@@ -1021,7 +1021,7 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
       } else {
         final SqlNodeList selectList =
             new SqlNodeList(SqlParserPos.ZERO);
-        selectList.add(new SqlIdentifier("*", SqlParserPos.ZERO));
+        selectList.add(SqlIdentifier.star(SqlParserPos.ZERO));
         return new SqlSelect(node.getParserPosition(), null, selectList, node,
             null, null, null, null, null, null, null);
       }
@@ -1156,7 +1156,7 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
     } else {
       // otherwise, just use select *
       selectList = new SqlNodeList(SqlParserPos.ZERO);
-      selectList.add(new SqlIdentifier("*", SqlParserPos.ZERO));
+      selectList.add(SqlIdentifier.star(SqlParserPos.ZERO));
     }
     SqlNode targetTable = call.getTargetTable();
     if (call.getAlias() != null) {
@@ -1194,8 +1194,7 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
     // select on the values row constructor; so we need to extract
     // that via the from clause on the select
     if (insertCall != null) {
-      SqlSelect valuesSelect = (SqlSelect) insertCall.getSource();
-      SqlCall valuesCall = (SqlCall) valuesSelect.getFrom();
+      SqlCall valuesCall = (SqlCall) insertCall.getSource();
       SqlCall rowCall = valuesCall.operand(0);
       selectList =
           new SqlNodeList(
@@ -1307,7 +1306,7 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
    */
   protected SqlSelect createSourceSelectForUpdate(SqlUpdate call) {
     final SqlNodeList selectList = new SqlNodeList(SqlParserPos.ZERO);
-    selectList.add(new SqlIdentifier("*", SqlParserPos.ZERO));
+    selectList.add(SqlIdentifier.star(SqlParserPos.ZERO));
     int ordinal = 0;
     for (SqlNode exp : call.getSourceExpressionList()) {
       // Force unique aliases to avoid a duplicate for Y with
@@ -1336,7 +1335,7 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
    */
   protected SqlSelect createSourceSelectForDelete(SqlDelete call) {
     final SqlNodeList selectList = new SqlNodeList(SqlParserPos.ZERO);
-    selectList.add(new SqlIdentifier("*", SqlParserPos.ZERO));
+    selectList.add(SqlIdentifier.star(SqlParserPos.ZERO));
     SqlNode sourceTable = call.getTargetTable();
     if (call.getAlias() != null) {
       sourceTable =


[5/5] calcite git commit: [CALCITE-986] User-defined function with Date or Timestamp parameters

Posted by jh...@apache.org.
[CALCITE-986] User-defined function with Date or Timestamp parameters


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

Branch: refs/heads/master
Commit: 3e7efce6e9eafd1c3fcaaac5ce9b1c2e483e8446
Parents: 16478ee
Author: Julian Hyde <jh...@apache.org>
Authored: Fri Nov 20 14:30:35 2015 -0800
Committer: Julian Hyde <jh...@apache.org>
Committed: Fri Nov 20 14:30:35 2015 -0800

----------------------------------------------------------------------
 .../ReflectiveCallNotNullImplementor.java       | 35 ++++++++++++++++++++
 .../apache/calcite/runtime/SqlFunctions.java    | 31 +++++++++++++++++
 .../java/org/apache/calcite/test/UdfTest.java   | 30 +++++++++++++++++
 .../java/org/apache/calcite/util/Smalls.java    |  9 +++++
 4 files changed, 105 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/calcite/blob/3e7efce6/core/src/main/java/org/apache/calcite/adapter/enumerable/ReflectiveCallNotNullImplementor.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/ReflectiveCallNotNullImplementor.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/ReflectiveCallNotNullImplementor.java
index bf35032..6414e03 100644
--- a/core/src/main/java/org/apache/calcite/adapter/enumerable/ReflectiveCallNotNullImplementor.java
+++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/ReflectiveCallNotNullImplementor.java
@@ -16,13 +16,16 @@
  */
 package org.apache.calcite.adapter.enumerable;
 
+import org.apache.calcite.linq4j.tree.ConstantUntypedNull;
 import org.apache.calcite.linq4j.tree.Expression;
 import org.apache.calcite.linq4j.tree.Expressions;
 import org.apache.calcite.linq4j.tree.NewExpression;
 import org.apache.calcite.rex.RexCall;
+import org.apache.calcite.runtime.SqlFunctions;
 
 import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
+import java.util.ArrayList;
 import java.util.List;
 
 /**
@@ -46,6 +49,7 @@ public class ReflectiveCallNotNullImplementor implements NotNullImplementor {
 
   public Expression implement(RexToLixTranslator translator,
       RexCall call, List<Expression> translatedOperands) {
+    translatedOperands = fromInternal(translatedOperands);
     if ((method.getModifiers() & Modifier.STATIC) != 0) {
       return Expressions.call(method, translatedOperands);
     } else {
@@ -56,6 +60,37 @@ public class ReflectiveCallNotNullImplementor implements NotNullImplementor {
       return Expressions.call(target, method, translatedOperands);
     }
   }
+
+  protected List<Expression> fromInternal(List<Expression> expressions) {
+    final List<Expression> list = new ArrayList<>();
+    final Class[] types = method.getParameterTypes();
+    for (int i = 0; i < expressions.size(); i++) {
+      list.add(fromInternal(expressions.get(i), types[i]));
+    }
+    return list;
+  }
+
+  protected Expression fromInternal(Expression e, Class<?> targetType) {
+    if (e == ConstantUntypedNull.INSTANCE) {
+      return e;
+    }
+    if (!(e.getType() instanceof Class)) {
+      return e;
+    }
+    if (targetType.isAssignableFrom((Class) e.getType())) {
+      return e;
+    }
+    if (targetType == java.sql.Date.class) {
+      return Expressions.call(SqlFunctions.class, "internalToDate", e);
+    }
+    if (targetType == java.sql.Time.class) {
+      return Expressions.call(SqlFunctions.class, "internalToTime", e);
+    }
+    if (targetType == java.sql.Timestamp.class) {
+      return Expressions.call(SqlFunctions.class, "internalToTimestamp", e);
+    }
+    return e;
+  }
 }
 
 // End ReflectiveCallNotNullImplementor.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/3e7efce6/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java b/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java
index d2659c3..22b0aaa 100644
--- a/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java
+++ b/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java
@@ -1173,6 +1173,37 @@ public class SqlFunctions {
         : toBigDecimal(o.toString());
   }
 
+  /** Converts the internal representation of a SQL DATE (int) to the Java
+   * type used for UDF parameters ({@link java.sql.Date}). */
+  public static java.sql.Date internalToDate(int x) {
+    return new java.sql.Date(x * DateTimeUtils.MILLIS_PER_DAY);
+  }
+
+  /** As {@link #internalToDate(int)} but allows nulls. */
+  public static java.sql.Date internalToDate(Integer x) {
+    return x == null ? null : internalToDate(x.intValue());
+  }
+
+  /** Converts the internal representation of a SQL TIME (int) to the Java
+   * type used for UDF parameters ({@link java.sql.Time}). */
+  public static java.sql.Time internalToTime(int x) {
+    return new java.sql.Time(x);
+  }
+
+  public static java.sql.Time internalToTime(Integer x) {
+    return x == null ? null : internalToTime(x.intValue());
+  }
+
+  /** Converts the internal representation of a SQL TIMESTAMP (long) to the Java
+   * type used for UDF parameters ({@link java.sql.Timestamp}). */
+  public static java.sql.Timestamp internalToTimestamp(long x) {
+    return new java.sql.Timestamp(x);
+  }
+
+  public static java.sql.Timestamp internalToTimestamp(Long x) {
+    return x == null ? null : internalToTimestamp(x.longValue());
+  }
+
   // Don't need shortValueOf etc. - Short.valueOf is sufficient.
 
   /** Helper for CAST(... AS VARCHAR(maxLength)). */

http://git-wip-us.apache.org/repos/asf/calcite/blob/3e7efce6/core/src/test/java/org/apache/calcite/test/UdfTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/test/UdfTest.java b/core/src/test/java/org/apache/calcite/test/UdfTest.java
index 2e8126d..7d707fe 100644
--- a/core/src/test/java/org/apache/calcite/test/UdfTest.java
+++ b/core/src/test/java/org/apache/calcite/test/UdfTest.java
@@ -120,6 +120,12 @@ public class UdfTest {
         + Smalls.MultipleFunction.class.getName()
         + "',\n"
         + "           methodName: '*'\n"
+        + "         },\n"
+        + "         {\n"
+        + "           className: '"
+        + Smalls.AllTypesFunction.class.getName()
+        + "',\n"
+        + "           methodName: '*'\n"
         + "         }\n"
         + "       ]\n"
         + "     }\n"
@@ -556,6 +562,30 @@ public class UdfTest {
     adhoc3.query("values \"adhoc\".MY_PLUS(1, 1)").returns(res);
   }
 
+  /** Test case for
+   * <a href="https://issues.apache.org/jira/browse/CALCITE-986">[CALCITE-986]
+   * User-defined function with Date or Timestamp parameters</a>. */
+  @Test public void testDate() {
+    final CalciteAssert.AssertThat with = withUdf();
+    with.query("values \"adhoc\".\"dateFun\"(DATE '1970-01-01')")
+        .returnsValue("0");
+    with.query("values \"adhoc\".\"dateFun\"(DATE '1970-01-02')")
+        .returnsValue("86400000");
+    with.query("values \"adhoc\".\"dateFun\"(cast(null as date))")
+        .returnsValue("-1");
+    with.query("values \"adhoc\".\"timeFun\"(TIME '00:00:00')")
+        .returnsValue("0");
+    with.query("values \"adhoc\".\"timeFun\"(TIME '00:01:30')")
+        .returnsValue("90000");
+    with.query("values \"adhoc\".\"timeFun\"(cast(null as time))")
+        .returnsValue("-1");
+    with.query("values \"adhoc\".\"timestampFun\"(TIMESTAMP '1970-01-01 00:00:00')")
+        .returnsValue("0");
+    with.query("values \"adhoc\".\"timestampFun\"(TIMESTAMP '1970-01-02 00:01:30')")
+        .returnsValue("86490000");
+    with.query("values \"adhoc\".\"timestampFun\"(cast(null as timestamp))")
+        .returnsValue("-1");
+  }
 }
 
 // End UdfTest.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/3e7efce6/core/src/test/java/org/apache/calcite/util/Smalls.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/util/Smalls.java b/core/src/test/java/org/apache/calcite/util/Smalls.java
index 3941eda..fe04094 100644
--- a/core/src/test/java/org/apache/calcite/util/Smalls.java
+++ b/core/src/test/java/org/apache/calcite/util/Smalls.java
@@ -403,6 +403,15 @@ public class Smalls {
     public int nonStatic(int x) { return x * 3; }
   }
 
+  /** UDF class that provides user-defined functions for each data type. */
+  public static class AllTypesFunction {
+    private AllTypesFunction() {}
+
+    public static long dateFun(java.sql.Date x) { return x == null ? -1L : x.getTime(); }
+    public static long timestampFun(java.sql.Timestamp x) { return x == null ? -1L : x.getTime(); }
+    public static long timeFun(java.sql.Time x) { return x == null ? -1L : x.getTime(); }
+  }
+
   /** Example of a user-defined aggregate function (UDAF). */
   public static class MySumFunction {
     public MySumFunction() {


[4/5] calcite git commit: Factor user-defined function tests from JdbcTest to UdfTest, and classes into Smalls

Posted by jh...@apache.org.
Factor user-defined function tests from JdbcTest to UdfTest, and classes into Smalls

No functionality changes.


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

Branch: refs/heads/master
Commit: 16478ee19e53bd20c249170de2a5e7e83a68b1d3
Parents: 4e61eae
Author: Julian Hyde <jh...@apache.org>
Authored: Fri Nov 20 12:05:09 2015 -0800
Committer: Julian Hyde <jh...@apache.org>
Committed: Fri Nov 20 13:28:15 2015 -0800

----------------------------------------------------------------------
 .../org/apache/calcite/test/CalciteSuite.java   |    1 +
 .../java/org/apache/calcite/test/JdbcTest.java  | 1051 +-----------------
 .../calcite/test/ReflectiveSchemaTest.java      |    5 +-
 .../java/org/apache/calcite/test/UdfTest.java   |  561 ++++++++++
 .../java/org/apache/calcite/util/Smalls.java    |  543 +++++++++
 5 files changed, 1130 insertions(+), 1031 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/calcite/blob/16478ee1/core/src/test/java/org/apache/calcite/test/CalciteSuite.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/test/CalciteSuite.java b/core/src/test/java/org/apache/calcite/test/CalciteSuite.java
index c78c0cc..95bc225 100644
--- a/core/src/test/java/org/apache/calcite/test/CalciteSuite.java
+++ b/core/src/test/java/org/apache/calcite/test/CalciteSuite.java
@@ -112,6 +112,7 @@ import org.junit.runners.Suite;
     EnumerableCorrelateTest.class,
 
     // slow tests (above 1s)
+    UdfTest.class,
     PlannerTest.class,
     RelBuilderTest.class,
     PigRelBuilderTest.class,

http://git-wip-us.apache.org/repos/asf/calcite/blob/16478ee1/core/src/test/java/org/apache/calcite/test/JdbcTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/test/JdbcTest.java b/core/src/test/java/org/apache/calcite/test/JdbcTest.java
index 70259b9..3709eba 100644
--- a/core/src/test/java/org/apache/calcite/test/JdbcTest.java
+++ b/core/src/test/java/org/apache/calcite/test/JdbcTest.java
@@ -16,7 +16,6 @@
  */
 package org.apache.calcite.test;
 
-import org.apache.calcite.DataContext;
 import org.apache.calcite.adapter.clone.CloneSchema;
 import org.apache.calcite.adapter.generate.RangeTable;
 import org.apache.calcite.adapter.java.AbstractQueryableTable;
@@ -37,18 +36,12 @@ import org.apache.calcite.jdbc.CalciteMetaImpl;
 import org.apache.calcite.jdbc.CalcitePrepare;
 import org.apache.calcite.jdbc.CalciteSchema;
 import org.apache.calcite.jdbc.Driver;
-import org.apache.calcite.linq4j.BaseQueryable;
-import org.apache.calcite.linq4j.Enumerable;
 import org.apache.calcite.linq4j.Enumerator;
 import org.apache.calcite.linq4j.Linq4j;
 import org.apache.calcite.linq4j.Ord;
 import org.apache.calcite.linq4j.QueryProvider;
 import org.apache.calcite.linq4j.Queryable;
 import org.apache.calcite.linq4j.function.Function0;
-import org.apache.calcite.linq4j.function.Function1;
-import org.apache.calcite.linq4j.function.Function2;
-import org.apache.calcite.linq4j.function.Parameter;
-import org.apache.calcite.linq4j.tree.Types;
 import org.apache.calcite.plan.RelOptCluster;
 import org.apache.calcite.plan.RelOptTable;
 import org.apache.calcite.plan.RelOptUtil;
@@ -59,8 +52,6 @@ import org.apache.calcite.rel.core.TableModify;
 import org.apache.calcite.rel.logical.LogicalTableModify;
 import org.apache.calcite.rel.type.RelDataType;
 import org.apache.calcite.rel.type.RelDataTypeFactory;
-import org.apache.calcite.rel.type.RelProtoDataType;
-import org.apache.calcite.rex.RexLiteral;
 import org.apache.calcite.runtime.Hook;
 import org.apache.calcite.runtime.SqlFunctions;
 import org.apache.calcite.schema.ModifiableTable;
@@ -78,7 +69,6 @@ import org.apache.calcite.schema.TranslatableTable;
 import org.apache.calcite.schema.impl.AbstractSchema;
 import org.apache.calcite.schema.impl.AbstractTable;
 import org.apache.calcite.schema.impl.AbstractTableQueryable;
-import org.apache.calcite.schema.impl.ScalarFunctionImpl;
 import org.apache.calcite.schema.impl.TableFunctionImpl;
 import org.apache.calcite.schema.impl.TableMacroImpl;
 import org.apache.calcite.schema.impl.ViewTable;
@@ -99,8 +89,8 @@ import org.apache.calcite.sql.parser.impl.SqlParserImpl;
 import org.apache.calcite.sql.type.SqlTypeName;
 import org.apache.calcite.util.Bug;
 import org.apache.calcite.util.JsonBuilder;
-import org.apache.calcite.util.Litmus;
 import org.apache.calcite.util.Pair;
+import org.apache.calcite.util.Smalls;
 import org.apache.calcite.util.Util;
 
 import com.google.common.base.Function;
@@ -121,7 +111,6 @@ import java.io.FileReader;
 import java.io.FileWriter;
 import java.io.IOException;
 import java.io.Reader;
-import java.lang.reflect.Method;
 import java.math.BigDecimal;
 import java.net.URL;
 import java.sql.Array;
@@ -136,7 +125,6 @@ import java.sql.ResultSetMetaData;
 import java.sql.SQLException;
 import java.sql.Statement;
 import java.sql.Timestamp;
-import java.util.AbstractList;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Calendar;
@@ -174,41 +162,6 @@ import static org.junit.Assert.fail;
  * Tests for using Calcite via JDBC.
  */
 public class JdbcTest {
-  public static final Method GENERATE_STRINGS_METHOD =
-      Types.lookupMethod(JdbcTest.class, "generateStrings", Integer.class);
-
-  public static final Method MAZE_METHOD =
-      Types.lookupMethod(MazeTable.class, "generate", int.class, int.class,
-          int.class);
-
-  public static final Method MAZE2_METHOD =
-      Types.lookupMethod(MazeTable.class, "generate2", int.class, int.class,
-          Integer.class);
-
-  public static final Method MAZE3_METHOD =
-      Types.lookupMethod(MazeTable.class, "generate3", String.class);
-
-  public static final Method MULTIPLICATION_TABLE_METHOD =
-      Types.lookupMethod(JdbcTest.class, "multiplicationTable", int.class,
-        int.class, Integer.class);
-
-  public static final Method VIEW_METHOD =
-      Types.lookupMethod(JdbcTest.class, "view", String.class);
-
-  public static final Method STR_METHOD =
-      Types.lookupMethod(JdbcTest.class, "str", Object.class, Object.class);
-
-  public static final Method STRING_UNION_METHOD =
-      Types.lookupMethod(
-          JdbcTest.class, "stringUnion", Queryable.class, Queryable.class);
-
-  public static final Method PROCESS_CURSOR_METHOD =
-      Types.lookupMethod(JdbcTest.class, "processCursor",
-          int.class, Enumerable.class);
-
-  public static final Method PROCESS_CURSORS_METHOD =
-      Types.lookupMethod(JdbcTest.class, "processCursors",
-          int.class, Enumerable.class, Enumerable.class);
 
   public static final String FOODMART_SCHEMA = "     {\n"
       + "       type: 'jdbc',\n"
@@ -461,7 +414,7 @@ public class JdbcTest {
     SchemaPlus rootSchema = calciteConnection.getRootSchema();
     SchemaPlus schema = rootSchema.add("s", new AbstractSchema());
     final TableFunction table =
-        TableFunctionImpl.create(GENERATE_STRINGS_METHOD);
+        TableFunctionImpl.create(Smalls.GENERATE_STRINGS_METHOD);
     schema.add("GenerateStrings", table);
     ResultSet resultSet = connection.createStatement().executeQuery("select *\n"
         + "from table(\"s\".\"GenerateStrings\"(5)) as t(n, c)\n"
@@ -481,7 +434,7 @@ public class JdbcTest {
         connection.unwrap(CalciteConnection.class);
     SchemaPlus rootSchema = calciteConnection.getRootSchema();
     SchemaPlus schema = rootSchema.add("s", new AbstractSchema());
-    final TableFunction table = TableFunctionImpl.create(MAZE_METHOD);
+    final TableFunction table = TableFunctionImpl.create(Smalls.MAZE_METHOD);
     schema.add("Maze", table);
     final String sql = "select *\n"
         + "from table(\"s\".\"Maze\"(5, 3, 1))";
@@ -500,7 +453,7 @@ public class JdbcTest {
         connection.unwrap(CalciteConnection.class);
     SchemaPlus rootSchema = calciteConnection.getRootSchema();
     SchemaPlus schema = rootSchema.add("s", new AbstractSchema());
-    final TableFunction table = TableFunctionImpl.create(MAZE2_METHOD);
+    final TableFunction table = TableFunctionImpl.create(Smalls.MAZE2_METHOD);
     schema.add("Maze", table);
     final String sql = "select *\n"
         + "from table(\"s\".\"Maze\"(5, 3, 1))";
@@ -533,11 +486,11 @@ public class JdbcTest {
         connection.unwrap(CalciteConnection.class);
     SchemaPlus rootSchema = calciteConnection.getRootSchema();
     SchemaPlus schema = rootSchema.add("s", new AbstractSchema());
-    final TableFunction table1 = TableFunctionImpl.create(MAZE_METHOD);
+    final TableFunction table1 = TableFunctionImpl.create(Smalls.MAZE_METHOD);
     schema.add("Maze", table1);
-    final TableFunction table2 = TableFunctionImpl.create(MAZE2_METHOD);
+    final TableFunction table2 = TableFunctionImpl.create(Smalls.MAZE2_METHOD);
     schema.add("Maze", table2);
-    final TableFunction table3 = TableFunctionImpl.create(MAZE3_METHOD);
+    final TableFunction table3 = TableFunctionImpl.create(Smalls.MAZE3_METHOD);
     schema.add("Maze", table3);
     final String sql = "select *\n"
         + "from table(\"s\".\"Maze\"(5, 3, 1))";
@@ -619,7 +572,7 @@ public class JdbcTest {
     SchemaPlus rootSchema = calciteConnection.getRootSchema();
     SchemaPlus schema = rootSchema.add("s", new AbstractSchema());
     final TableFunction table =
-        TableFunctionImpl.create(MULTIPLICATION_TABLE_METHOD);
+        TableFunctionImpl.create(Smalls.MULTIPLICATION_TABLE_METHOD);
     schema.add("multiplication", table);
     return connection;
   }
@@ -638,10 +591,10 @@ public class JdbcTest {
     SchemaPlus rootSchema = calciteConnection.getRootSchema();
     SchemaPlus schema = rootSchema.add("s", new AbstractSchema());
     final TableFunction table =
-        TableFunctionImpl.create(GENERATE_STRINGS_METHOD);
+        TableFunctionImpl.create(Smalls.GENERATE_STRINGS_METHOD);
     schema.add("GenerateStrings", table);
     final TableFunction add =
-        TableFunctionImpl.create(PROCESS_CURSOR_METHOD);
+        TableFunctionImpl.create(Smalls.PROCESS_CURSOR_METHOD);
     schema.add("process", add);
     final PreparedStatement ps = connection.prepareStatement("select *\n"
         + "from table(\"s\".\"process\"(2,\n"
@@ -672,10 +625,10 @@ public class JdbcTest {
     SchemaPlus rootSchema = calciteConnection.getRootSchema();
     SchemaPlus schema = rootSchema.getSubSchema("s");
     final TableFunction table =
-        TableFunctionImpl.create(GENERATE_STRINGS_METHOD);
+        TableFunctionImpl.create(Smalls.GENERATE_STRINGS_METHOD);
     schema.add("GenerateStrings", table);
     final TableFunction add =
-        TableFunctionImpl.create(PROCESS_CURSORS_METHOD);
+        TableFunctionImpl.create(Smalls.PROCESS_CURSORS_METHOD);
     schema.add("process", add);
     final PreparedStatement ps = connection.prepareStatement("select *\n"
         + "from table(\"s\".\"process\"(2,\n"
@@ -760,7 +713,7 @@ public class JdbcTest {
   /**
    * Tests a relation that is accessed via method syntax.
    *
-   * <p>The function ({@link #view(String)} has a return type
+   * <p>The function ({@link Smalls#view(String)} has a return type
    * {@link Table} and the actual returned value implements
    * {@link org.apache.calcite.schema.TranslatableTable}.
    */
@@ -772,7 +725,7 @@ public class JdbcTest {
         connection.unwrap(CalciteConnection.class);
     SchemaPlus rootSchema = calciteConnection.getRootSchema();
     SchemaPlus schema = rootSchema.add("s", new AbstractSchema());
-    final TableMacro tableMacro = TableMacroImpl.create(VIEW_METHOD);
+    final TableMacro tableMacro = TableMacroImpl.create(Smalls.VIEW_METHOD);
     schema.add("View", tableMacro);
     ResultSet resultSet = connection.createStatement().executeQuery("select *\n"
         + "from table(\"s\".\"View\"('(10), (20)')) as t(n)\n"
@@ -798,7 +751,7 @@ public class JdbcTest {
         connection.unwrap(CalciteConnection.class);
     SchemaPlus rootSchema = calciteConnection.getRootSchema();
     SchemaPlus schema = rootSchema.add("s", new AbstractSchema());
-    final TableMacro tableMacro = TableMacroImpl.create(STR_METHOD);
+    final TableMacro tableMacro = TableMacroImpl.create(Smalls.STR_METHOD);
     schema.add("Str", tableMacro);
     ResultSet resultSet = connection.createStatement().executeQuery("select *\n"
         + "from table(\"s\".\"Str\"(MAP['a', 1, 'baz', 2],\n"
@@ -814,7 +767,7 @@ public class JdbcTest {
   @Test public void testTableMacroWithNamedParameters() throws Exception {
     // View(String r optional, String s, int t optional)
     final CalciteAssert.AssertThat with =
-        assertWithMacro(TableMacroFunctionWithNamedParameters.class);
+        assertWithMacro(Smalls.TableMacroFunctionWithNamedParameters.class);
     with.query("select * from table(\"adhoc\".\"View\"('(5)'))")
         .throws_("No match found for function signature View(<CHARACTER>)");
     final String expected1 = "c=1\n"
@@ -845,25 +798,25 @@ public class JdbcTest {
   /** Tests a JDBC connection that provides a model that contains a table
    *  macro. */
   @Test public void testTableMacroInModel() throws Exception {
-    checkTableMacroInModel(TableMacroFunction.class);
+    checkTableMacroInModel(Smalls.TableMacroFunction.class);
   }
 
   /** Tests a JDBC connection that provides a model that contains a table
    *  macro defined as a static method. */
   @Test public void testStaticTableMacroInModel() throws Exception {
-    checkTableMacroInModel(StaticTableMacroFunction.class);
+    checkTableMacroInModel(Smalls.StaticTableMacroFunction.class);
   }
 
   /** Tests a JDBC connection that provides a model that contains a table
    *  function. */
   @Test public void testTableFunctionInModel() throws Exception {
-    checkTableFunctionInModel(MyTableFunction.class);
+    checkTableFunctionInModel(Smalls.MyTableFunction.class);
   }
 
   /** Tests a JDBC connection that provides a model that contains a table
    *  function defined as a static method. */
   @Test public void testStaticTableFunctionInModel() throws Exception {
-    checkTableFunctionInModel(TestStaticTableFunction.class);
+    checkTableFunctionInModel(Smalls.TestStaticTableFunction.class);
   }
 
   private CalciteAssert.AssertThat assertWithMacro(Class clazz) {
@@ -913,150 +866,6 @@ public class JdbcTest {
             "A=30; B=30");
   }
 
-  public static <T> Queryable<T> stringUnion(
-      Queryable<T> q0, Queryable<T> q1) {
-    return q0.concat(q1);
-  }
-
-  /** A function that generates a table that generates a sequence of
-   * {@link org.apache.calcite.test.JdbcTest.IntString} values. */
-  public static QueryableTable generateStrings(final Integer count) {
-    return new AbstractQueryableTable(IntString.class) {
-      public RelDataType getRowType(RelDataTypeFactory typeFactory) {
-        return typeFactory.createJavaType(IntString.class);
-      }
-
-      public <T> Queryable<T> asQueryable(QueryProvider queryProvider,
-          SchemaPlus schema, String tableName) {
-        BaseQueryable<IntString> queryable =
-            new BaseQueryable<IntString>(null, IntString.class, null) {
-              public Enumerator<IntString> enumerator() {
-                return new Enumerator<IntString>() {
-                  static final String Z = "abcdefghijklm";
-
-                  int i = 0;
-                  int curI;
-                  String curS;
-
-                  public IntString current() {
-                    return new IntString(curI, curS);
-                  }
-
-                  public boolean moveNext() {
-                    if (i < count) {
-                      curI = i;
-                      curS = Z.substring(0, i % Z.length());
-                      ++i;
-                      return true;
-                    } else {
-                      return false;
-                    }
-                  }
-
-                  public void reset() {
-                    i = 0;
-                  }
-
-                  public void close() {
-                  }
-                };
-              }
-            };
-        //noinspection unchecked
-        return (Queryable<T>) queryable;
-      }
-    };
-  }
-
-  /** A function that generates multiplication table of {@code ncol} columns x
-   * {@code nrow} rows. */
-  public static QueryableTable multiplicationTable(final int ncol,
-      final int nrow, Integer offset) {
-    final int offs = offset == null ? 0 : offset;
-    return new AbstractQueryableTable(Object[].class) {
-      public RelDataType getRowType(RelDataTypeFactory typeFactory) {
-        final RelDataTypeFactory.FieldInfoBuilder builder =
-          typeFactory.builder();
-        builder.add("row_name", typeFactory.createJavaType(String.class));
-        final RelDataType int_ = typeFactory.createJavaType(int.class);
-        for (int i = 1; i <= ncol; i++) {
-          builder.add("c" + i, int_);
-        }
-        return builder.build();
-      }
-
-      public Queryable<Object[]> asQueryable(QueryProvider queryProvider,
-          SchemaPlus schema, String tableName) {
-        final List<Object[]> table = new AbstractList<Object[]>() {
-          @Override public Object[] get(int index) {
-            Object[] cur = new Object[ncol + 1];
-            cur[0] = "row " + index;
-            for (int j = 1; j <= ncol; j++) {
-              cur[j] = j * (index + 1) + offs;
-            }
-            return cur;
-          }
-
-          @Override public int size() {
-            return nrow;
-          }
-        };
-        return Linq4j.asEnumerable(table).asQueryable();
-      }
-    };
-  }
-
-  /**
-   * A function that adds a number to the first column of input cursor
-   */
-  public static QueryableTable processCursor(final int offset,
-    final Enumerable<Object[]> a) {
-    return new AbstractQueryableTable(Object[].class) {
-      public RelDataType getRowType(RelDataTypeFactory typeFactory) {
-        return typeFactory.builder()
-            .add("result", SqlTypeName.INTEGER)
-            .build();
-      }
-
-      public Queryable<Integer> asQueryable(QueryProvider queryProvider,
-          SchemaPlus schema, String tableName) {
-        final Enumerable<Integer> enumerable =
-            a.select(new Function1<Object[], Integer>() {
-              public Integer apply(Object[] a0) {
-                return offset + ((Integer) a0[0]);
-              }
-            });
-        return enumerable.asQueryable();
-      }
-    };
-  }
-
-  /**
-   * A function that sums the second column of first input cursor, second
-   * column of first input and the given int.
-   */
-  public static QueryableTable processCursors(final int offset,
-      final Enumerable<Object[]> a, final Enumerable<IntString> b) {
-    return new AbstractQueryableTable(Object[].class) {
-      public RelDataType getRowType(RelDataTypeFactory typeFactory) {
-        return typeFactory.builder()
-            .add("result", SqlTypeName.INTEGER)
-            .build();
-      }
-
-      public Queryable<Integer> asQueryable(QueryProvider queryProvider,
-          SchemaPlus schema, String tableName) {
-        final Enumerable<Integer> enumerable =
-            a.zip(b, new Function2<Object[], IntString, Integer>() {
-              public Integer apply(Object[] v0, IntString v1) {
-                return ((Integer) v0[1]) + v1.n + offset;
-              }
-            });
-        return enumerable.asQueryable();
-      }
-    };
-  }
-
   /** Tests {@link org.apache.calcite.avatica.Handler#onConnectionClose}
    * and  {@link org.apache.calcite.avatica.Handler#onStatementClose}. */
   @Test public void testOnConnectionClose() throws Exception {
@@ -5407,514 +5216,6 @@ public class JdbcTest {
             });
   }
 
-  private CalciteAssert.AssertThat withUdf() {
-    return CalciteAssert.model("{\n"
-        + "  version: '1.0',\n"
-        + "   schemas: [\n"
-        + "     {\n"
-        + "       name: 'adhoc',\n"
-        + "       tables: [\n"
-        + "         {\n"
-        + "           name: 'EMPLOYEES',\n"
-        + "           type: 'custom',\n"
-        + "           factory: '"
-        + EmpDeptTableFactory.class.getName()
-        + "',\n"
-        + "           operand: {'foo': true, 'bar': 345}\n"
-        + "         }\n"
-        + "       ],\n"
-        + "       functions: [\n"
-        + "         {\n"
-        + "           name: 'MY_PLUS',\n"
-        + "           className: '"
-        + MyPlusFunction.class.getName()
-        + "'\n"
-        + "         },\n"
-        + "         {\n"
-        + "           name: 'MY_LEFT',\n"
-        + "           className: '"
-        + MyLeftFunction.class.getName()
-        + "'\n"
-        + "         },\n"
-        + "         {\n"
-        + "           name: 'ABCDE',\n"
-        + "           className: '"
-        + MyAbcdeFunction.class.getName()
-        + "'\n"
-        + "         },\n"
-        + "         {\n"
-        + "           name: 'MY_STR',\n"
-        + "           className: '"
-        + MyToStringFunction.class.getName()
-        + "'\n"
-        + "         },\n"
-        + "         {\n"
-        + "           name: 'MY_DOUBLE',\n"
-        + "           className: '"
-        + MyDoubleFunction.class.getName()
-        + "'\n"
-        + "         },\n"
-        + "         {\n"
-        + "           name: 'COUNT_ARGS',\n"
-        + "           className: '"
-        + CountArgs0Function.class.getName()
-        + "'\n"
-        + "         },\n"
-        + "         {\n"
-        + "           name: 'COUNT_ARGS',\n"
-        + "           className: '"
-        + CountArgs1Function.class.getName()
-        + "'\n"
-        + "         },\n"
-        + "         {\n"
-        + "           name: 'COUNT_ARGS',\n"
-        + "           className: '"
-        + CountArgs2Function.class.getName()
-        + "'\n"
-        + "         },\n"
-        + "         {\n"
-        + "           name: 'MY_ABS',\n"
-        + "           className: '"
-        + java.lang.Math.class.getName()
-        + "',\n"
-        + "           methodName: 'abs'\n"
-        + "         },\n"
-        + "         {\n"
-        + "           className: '"
-        + MultipleFunction.class.getName()
-        + "',\n"
-        + "           methodName: '*'\n"
-        + "         }\n"
-        + "       ]\n"
-        + "     }\n"
-        + "   ]\n"
-        + "}");
-  }
-
-  /** Tests user-defined function. */
-  @Test public void testUserDefinedFunction() throws Exception {
-    final CalciteAssert.AssertThat with = withUdf();
-    with.query(
-        "select \"adhoc\".my_plus(\"deptno\", 100) as p from \"adhoc\".EMPLOYEES")
-        .returns("P=110\n"
-            + "P=120\n"
-            + "P=110\n"
-            + "P=110\n");
-    with.query(
-        "select \"adhoc\".my_double(\"deptno\") as p from \"adhoc\".EMPLOYEES")
-        .returns("P=20\n"
-            + "P=40\n"
-            + "P=20\n"
-            + "P=20\n");
-  }
-
-  /** Test case for
-   * <a href="https://issues.apache.org/jira/browse/CALCITE-937">[CALCITE-937]
-   * User-defined function within view</a>. */
-  @Test public void testUserDefinedFunctionInView() throws Exception {
-    Class.forName("org.apache.calcite.jdbc.Driver");
-    Connection connection = DriverManager.getConnection("jdbc:calcite:");
-    CalciteConnection calciteConnection =
-        connection.unwrap(CalciteConnection.class);
-    SchemaPlus rootSchema = calciteConnection.getRootSchema();
-    rootSchema.add("hr", new ReflectiveSchema(new HrSchema()));
-
-    SchemaPlus post = rootSchema.add("POST", new AbstractSchema());
-    post.add("MY_INCREMENT",
-        ScalarFunctionImpl.create(MyIncrement.class, "eval"));
-
-    final String viewSql = "select \"empid\" as EMPLOYEE_ID,\n"
-        + "  \"name\" || ' ' || \"name\" as EMPLOYEE_NAME,\n"
-        + "  \"salary\" as EMPLOYEE_SALARY,\n"
-        + "  POST.MY_INCREMENT(\"empid\", 10) as INCREMENTED_SALARY\n"
-        + "from \"hr\".\"emps\"";
-    post.add("V_EMP",
-        ViewTable.viewMacro(post, viewSql, ImmutableList.<String>of(), null));
-
-    final String result = ""
-        + "EMPLOYEE_ID=100; EMPLOYEE_NAME=Bill Bill; EMPLOYEE_SALARY=10000.0; INCREMENTED_SALARY=110.0\n"
-        + "EMPLOYEE_ID=200; EMPLOYEE_NAME=Eric Eric; EMPLOYEE_SALARY=8000.0; INCREMENTED_SALARY=220.0\n"
-        + "EMPLOYEE_ID=150; EMPLOYEE_NAME=Sebastian Sebastian; EMPLOYEE_SALARY=7000.0; INCREMENTED_SALARY=165.0\n"
-        + "EMPLOYEE_ID=110; EMPLOYEE_NAME=Theodore Theodore; EMPLOYEE_SALARY=11500.0; INCREMENTED_SALARY=121.0\n";
-
-    Statement statement = connection.createStatement();
-    ResultSet resultSet = statement.executeQuery(viewSql);
-    assertThat(CalciteAssert.toString(resultSet), is(result));
-    resultSet.close();
-
-    ResultSet viewResultSet =
-        statement.executeQuery("select * from \"POST\".\"V_EMP\"");
-    assertThat(CalciteAssert.toString(viewResultSet), is(result));
-    statement.close();
-    connection.close();
-  }
-
-  /**
-   * Tests that IS NULL/IS NOT NULL is properly implemented for non-strict
-   * functions.
-   */
-  @Test public void testNotNullImplementor() {
-    final CalciteAssert.AssertThat with = withUdf();
-    with.query(
-        "select upper(\"adhoc\".my_str(\"name\")) as p from \"adhoc\".EMPLOYEES")
-        .returns("P=<BILL>\n"
-            + "P=<ERIC>\n"
-            + "P=<SEBASTIAN>\n"
-            + "P=<THEODORE>\n");
-    with.query("select \"name\" as p from \"adhoc\".EMPLOYEES\n"
-        + "where \"adhoc\".my_str(\"name\") is not null")
-        .returns("P=Bill\n"
-            + "P=Eric\n"
-            + "P=Sebastian\n"
-            + "P=Theodore\n");
-    with.query("select \"name\" as p from \"adhoc\".EMPLOYEES\n"
-        + "where \"adhoc\".my_str(upper(\"name\")) is not null")
-        .returns("P=Bill\n"
-            + "P=Eric\n"
-            + "P=Sebastian\n"
-            + "P=Theodore\n");
-    with.query("select \"name\" as p from \"adhoc\".EMPLOYEES\n"
-        + "where upper(\"adhoc\".my_str(\"name\")) is not null")
-        .returns("P=Bill\n"
-            + "P=Eric\n"
-            + "P=Sebastian\n"
-            + "P=Theodore\n");
-    with.query("select \"name\" as p from \"adhoc\".EMPLOYEES\n"
-        + "where \"adhoc\".my_str(\"name\") is null")
-        .returns("");
-    with.query("select \"name\" as p from \"adhoc\".EMPLOYEES\n"
-        + "where \"adhoc\".my_str(upper(\"adhoc\".my_str(\"name\")"
-        + ")) ='8'")
-        .returns("");
-  }
-
-  /** Tests derived return type of user-defined function. */
-  @Test public void testUdfDerivedReturnType() {
-    final CalciteAssert.AssertThat with = withUdf();
-    with.query(
-        "select max(\"adhoc\".my_double(\"deptno\")) as p from \"adhoc\".EMPLOYEES")
-        .returns("P=40\n");
-    with.query("select max(\"adhoc\".my_str(\"name\")) as p\n"
-        + "from \"adhoc\".EMPLOYEES\n"
-        + "where \"adhoc\".my_str(\"name\") is null")
-        .returns("P=null\n");
-  }
-
-  /** Tests a user-defined function that has multiple overloads. */
-  @Test public void testUdfOverloaded() {
-    final CalciteAssert.AssertThat with = withUdf();
-    with.query("values (\"adhoc\".count_args(),\n"
-        + " \"adhoc\".count_args(0),\n"
-        + " \"adhoc\".count_args(0, 0))")
-        .returns("EXPR$0=0; EXPR$1=1; EXPR$2=2\n");
-    with.query("select max(\"adhoc\".count_args()) as p0,\n"
-        + " min(\"adhoc\".count_args(0)) as p1,\n"
-        + " max(\"adhoc\".count_args(0, 0)) as p2\n"
-        + "from \"adhoc\".EMPLOYEES limit 1")
-        .returns("P0=0; P1=1; P2=2\n");
-  }
-
-  /** Tests passing parameters to user-defined function by name. */
-  @Test public void testUdfArgumentName() {
-    final CalciteAssert.AssertThat with = withUdf();
-    // arguments in physical order
-    with.query("values (\"adhoc\".my_left(\"s\" => 'hello', \"n\" => 3))")
-        .returns("EXPR$0=hel\n");
-    // arguments in reverse order
-    with.query("values (\"adhoc\".my_left(\"n\" => 3, \"s\" => 'hello'))")
-        .returns("EXPR$0=hel\n");
-    with.query("values (\"adhoc\".my_left(\"n\" => 1 + 2, \"s\" => 'hello'))")
-        .returns("EXPR$0=hel\n");
-    // duplicate argument names
-    with.query("values (\"adhoc\".my_left(\"n\" => 3, \"n\" => 2, \"s\" => 'hello'))")
-        .throws_("Duplicate argument name 'n'");
-    // invalid argument names
-    with.query("values (\"adhoc\".my_left(\"n\" => 3, \"m\" => 2, \"s\" => 'h'))")
-        .throws_("No match found for function signature "
-            + "MY_LEFT(n => <NUMERIC>, m => <NUMERIC>, s => <CHARACTER>)");
-    // missing arguments
-    with.query("values (\"adhoc\".my_left(\"n\" => 3))")
-        .throws_("No match found for function signature MY_LEFT(n => <NUMERIC>)");
-    with.query("values (\"adhoc\".my_left(\"s\" => 'hello'))")
-        .throws_("No match found for function signature MY_LEFT(s => <CHARACTER>)");
-    // arguments of wrong type
-    with.query("values (\"adhoc\".my_left(\"n\" => 'hello', \"s\" => 'x'))")
-        .throws_("No match found for function signature "
-            + "MY_LEFT(n => <CHARACTER>, s => <CHARACTER>)");
-    with.query("values (\"adhoc\".my_left(\"n\" => 1, \"s\" => 0))")
-        .throws_("No match found for function signature "
-            + "MY_LEFT(n => <NUMERIC>, s => <NUMERIC>)");
-  }
-
-  /** Tests calling a user-defined function some of whose parameters are
-   * optional. */
-  @Test public void testUdfArgumentOptional() {
-    final CalciteAssert.AssertThat with = withUdf();
-    with.query("values (\"adhoc\".abcde(a=>1,b=>2,c=>3,d=>4,e=>5))")
-        .returns("EXPR$0={a: 1, b: 2, c: 3, d: 4, e: 5}\n");
-    with.query("values (\"adhoc\".abcde(1,2,3,4,CAST(NULL AS INTEGER)))")
-        .returns("EXPR$0={a: 1, b: 2, c: 3, d: 4, e: null}\n");
-    with.query("values (\"adhoc\".abcde(a=>1,b=>2,c=>3,d=>4))")
-        .returns("EXPR$0={a: 1, b: 2, c: 3, d: 4, e: null}\n");
-    with.query("values (\"adhoc\".abcde(a=>1,b=>2,c=>3))")
-        .returns("EXPR$0={a: 1, b: 2, c: 3, d: null, e: null}\n");
-    with.query("values (\"adhoc\".abcde(a=>1,e=>5,c=>3))")
-        .returns("EXPR$0={a: 1, b: null, c: 3, d: null, e: 5}\n");
-    with.query("values (\"adhoc\".abcde(1,2,3))")
-        .returns("EXPR$0={a: 1, b: 2, c: 3, d: null, e: null}\n");
-    with.query("values (\"adhoc\".abcde(1,2,3,4))")
-        .returns("EXPR$0={a: 1, b: 2, c: 3, d: 4, e: null}\n");
-    with.query("values (\"adhoc\".abcde(1,2,3,4,5))")
-        .returns("EXPR$0={a: 1, b: 2, c: 3, d: 4, e: 5}\n");
-    with.query("values (\"adhoc\".abcde(1,2))")
-        .throws_("No match found for function signature ABCDE(<NUMERIC>, <NUMERIC>)");
-    with.query("values (\"adhoc\".abcde(1,DEFAULT,3))")
-        .returns("EXPR$0={a: 1, b: null, c: 3, d: null, e: null}\n");
-    with.query("values (\"adhoc\".abcde(1,DEFAULT,'abcde'))")
-        .throws_("No match found for function signature ABCDE(<NUMERIC>, <ANY>, <CHARACTER>)");
-    with.query("values (\"adhoc\".abcde(true))")
-        .throws_("No match found for function signature ABCDE(<BOOLEAN>)");
-    with.query("values (\"adhoc\".abcde(true,DEFAULT))")
-        .throws_("No match found for function signature ABCDE(<BOOLEAN>, <ANY>)");
-    with.query("values (\"adhoc\".abcde(1,DEFAULT,3,DEFAULT))")
-        .returns("EXPR$0={a: 1, b: null, c: 3, d: null, e: null}\n");
-    with.query("values (\"adhoc\".abcde(1,2,DEFAULT))")
-        .throws_("DEFAULT is only allowed for optional parameters");
-    with.query("values (\"adhoc\".abcde(a=>1,b=>2,c=>DEFAULT))")
-        .throws_("DEFAULT is only allowed for optional parameters");
-    with.query("values (\"adhoc\".abcde(a=>1,b=>DEFAULT,c=>3))")
-        .returns("EXPR$0={a: 1, b: null, c: 3, d: null, e: null}\n");
-  }
-
-  /** Test for
-   * {@link org.apache.calcite.runtime.CalciteResource#requireDefaultConstructor(String)}. */
-  @Test public void testUserDefinedFunction2() throws Exception {
-    withBadUdf(AwkwardFunction.class)
-        .connectThrows(
-            "Declaring class 'org.apache.calcite.test.JdbcTest$AwkwardFunction' of non-static user-defined function must have a public constructor with zero parameters");
-  }
-
-  /** Tests user-defined function, with multiple methods per class. */
-  @Test public void testUserDefinedFunctionWithMethodName() throws Exception {
-    // java.lang.Math has abs(int) and abs(double).
-    final CalciteAssert.AssertThat with = withUdf();
-    with.query("values abs(-4)").returnsValue("4");
-    with.query("values abs(-4.5)").returnsValue("4.5");
-
-    // 3 overloads of "fun1", another method "fun2", but method "nonStatic"
-    // cannot be used as a function
-    with.query("values \"adhoc\".\"fun1\"(2)").returnsValue("4");
-    with.query("values \"adhoc\".\"fun1\"(2, 3)").returnsValue("5");
-    with.query("values \"adhoc\".\"fun1\"('Foo Bar')").returnsValue("foo bar");
-    with.query("values \"adhoc\".\"fun2\"(10)").returnsValue("30");
-    with.query("values \"adhoc\".\"nonStatic\"(2)")
-        .throws_("No match found for function signature nonStatic(<NUMERIC>)");
-  }
-
-  /** Tests user-defined aggregate function. */
-  @Test public void testUserDefinedAggregateFunction() throws Exception {
-    final String empDept = EmpDeptTableFactory.class.getName();
-    final String sum = MyStaticSumFunction.class.getName();
-    final String sum2 = MySumFunction.class.getName();
-    final CalciteAssert.AssertThat with = CalciteAssert.model("{\n"
-        + "  version: '1.0',\n"
-        + "   schemas: [\n"
-        + "     {\n"
-        + "       name: 'adhoc',\n"
-        + "       tables: [\n"
-        + "         {\n"
-        + "           name: 'EMPLOYEES',\n"
-        + "           type: 'custom',\n"
-        + "           factory: '" + empDept + "',\n"
-        + "           operand: {'foo': true, 'bar': 345}\n"
-        + "         }\n"
-        + "       ],\n"
-        + "       functions: [\n"
-        + "         {\n"
-        + "           name: 'MY_SUM',\n"
-        + "           className: '" + sum + "'\n"
-        + "         },\n"
-        + "         {\n"
-        + "           name: 'MY_SUM2',\n"
-        + "           className: '" + sum2 + "'\n"
-        + "         }\n"
-        + "       ]\n"
-        + "     }\n"
-        + "   ]\n"
-        + "}")
-        .withDefaultSchema("adhoc");
-    with.withDefaultSchema(null)
-        .query(
-            "select \"adhoc\".my_sum(\"deptno\") as p from \"adhoc\".EMPLOYEES\n")
-        .returns("P=50\n");
-    with.query("select my_sum(\"empid\"), \"deptno\" as p from EMPLOYEES\n")
-        .throws_(
-            "Expression 'deptno' is not being grouped");
-    with.query("select my_sum(\"deptno\") as p from EMPLOYEES\n")
-        .returns("P=50\n");
-    with.query("select my_sum(\"name\") as p from EMPLOYEES\n")
-        .throws_(
-            "Cannot apply 'MY_SUM' to arguments of type 'MY_SUM(<JAVATYPE(CLASS JAVA.LANG.STRING)>)'. Supported form(s): 'MY_SUM(<NUMERIC>)");
-    with.query("select my_sum(\"deptno\", 1) as p from EMPLOYEES\n")
-        .throws_(
-            "No match found for function signature MY_SUM(<NUMERIC>, <NUMERIC>)");
-    with.query("select my_sum() as p from EMPLOYEES\n")
-        .throws_(
-            "No match found for function signature MY_SUM()");
-    with.query("select \"deptno\", my_sum(\"deptno\") as p from EMPLOYEES\n"
-        + "group by \"deptno\"")
-        .returnsUnordered(
-            "deptno=20; P=20",
-            "deptno=10; P=30");
-    with.query("select \"deptno\", my_sum2(\"deptno\") as p from EMPLOYEES\n"
-        + "group by \"deptno\"")
-        .returnsUnordered("deptno=20; P=20", "deptno=10; P=30");
-  }
-
-  /** Test for
-   * {@link org.apache.calcite.runtime.CalciteResource#firstParameterOfAdd(String)}. */
-  @Test public void testUserDefinedAggregateFunction3() throws Exception {
-    withBadUdf(SumFunctionBadIAdd.class).connectThrows(
-        "Caused by: java.lang.RuntimeException: In user-defined aggregate class 'org.apache.calcite.test.JdbcTest$SumFunctionBadIAdd', first parameter to 'add' method must be the accumulator (the return type of the 'init' method)");
-  }
-
-  private static CalciteAssert.AssertThat withBadUdf(Class clazz) {
-    final String empDept = EmpDeptTableFactory.class.getName();
-    final String className = clazz.getName();
-    return CalciteAssert.model("{\n"
-        + "  version: '1.0',\n"
-        + "   schemas: [\n"
-        + "     {\n"
-        + "       name: 'adhoc',\n"
-        + "       tables: [\n"
-        + "         {\n"
-        + "           name: 'EMPLOYEES',\n"
-        + "           type: 'custom',\n"
-        + "           factory: '" + empDept + "',\n"
-        + "           operand: {'foo': true, 'bar': 345}\n"
-        + "         }\n"
-        + "       ],\n"
-        + "       functions: [\n"
-        + "         {\n"
-        + "           name: 'AWKWARD',\n"
-        + "           className: '" + className + "'\n"
-        + "         }\n"
-        + "       ]\n"
-        + "     }\n"
-        + "   ]\n"
-        + "}")
-        .withDefaultSchema("adhoc");
-  }
-
-  /** Tests user-defined aggregate function with FILTER.
-   *
-   * <p>Also tests that we do not try to push ADAF to JDBC source. */
-  @Test public void testUserDefinedAggregateFunctionWithFilter() throws Exception {
-    final String sum = MyStaticSumFunction.class.getName();
-    final String sum2 = MySumFunction.class.getName();
-    final CalciteAssert.AssertThat with = CalciteAssert.model("{\n"
-        + "  version: '1.0',\n"
-        + "   schemas: [\n"
-        + SCOTT_SCHEMA
-        + ",\n"
-        + "     {\n"
-        + "       name: 'adhoc',\n"
-        + "       functions: [\n"
-        + "         {\n"
-        + "           name: 'MY_SUM',\n"
-        + "           className: '" + sum + "'\n"
-        + "         },\n"
-        + "         {\n"
-        + "           name: 'MY_SUM2',\n"
-        + "           className: '" + sum2 + "'\n"
-        + "         }\n"
-        + "       ]\n"
-        + "     }\n"
-        + "   ]\n"
-        + "}")
-        .withDefaultSchema("adhoc");
-    with.query("select deptno, \"adhoc\".my_sum(deptno) as p\n"
-            + "from scott.emp\n"
-            + "group by deptno\n")
-        .returns(
-            "DEPTNO=20; P=100\n"
-                + "DEPTNO=10; P=30\n"
-                + "DEPTNO=30; P=180\n");
-
-    with.query("select deptno,\n"
-            + "  \"adhoc\".my_sum(deptno) filter (where job = 'CLERK') as c,\n"
-            + "  \"adhoc\".my_sum(deptno) filter (where job = 'XXX') as x\n"
-            + "from scott.emp\n"
-            + "group by deptno\n")
-        .returns(
-            "DEPTNO=20; C=40; X=0\n"
-                + "DEPTNO=10; C=10; X=0\n"
-                + "DEPTNO=30; C=30; X=0\n");
-  }
-
-  /** Tests resolution of functions using schema paths. */
-  @Test public void testPath() throws Exception {
-    final String name = MyPlusFunction.class.getName();
-    final CalciteAssert.AssertThat with = CalciteAssert.model("{\n"
-        + "  version: '1.0',\n"
-        + "   schemas: [\n"
-        + "     {\n"
-        + "       name: 'adhoc',\n"
-        + "       functions: [\n"
-        + "         {\n"
-        + "           name: 'MY_PLUS',\n"
-        + "           className: '" + name + "'\n"
-        + "         }\n"
-        + "       ]\n"
-        + "     },\n"
-        + "     {\n"
-        + "       name: 'adhoc2',\n"
-        + "       functions: [\n"
-        + "         {\n"
-        + "           name: 'MY_PLUS2',\n"
-        + "           className: '" + name + "'\n"
-        + "         }\n"
-        + "       ]\n"
-        + "     },\n"
-        + "     {\n"
-        + "       name: 'adhoc3',\n"
-        + "       path: ['adhoc2','adhoc3'],\n"
-        + "       functions: [\n"
-        + "         {\n"
-        + "           name: 'MY_PLUS3',\n"
-        + "           className: '" + name + "'\n"
-        + "         }\n"
-        + "       ]\n"
-        + "     }\n"
-        + "   ]\n"
-        + "}");
-
-    final String err = "No match found for function signature";
-    final String res = "EXPR$0=2\n";
-
-    // adhoc can see own function MY_PLUS but not adhoc2.MY_PLUS2 unless
-    // qualified
-    final CalciteAssert.AssertThat adhoc = with.withDefaultSchema("adhoc");
-    adhoc.query("values MY_PLUS(1, 1)").returns(res);
-    adhoc.query("values MY_PLUS2(1, 1)").throws_(err);
-    adhoc.query("values \"adhoc2\".MY_PLUS(1, 1)").throws_(err);
-    adhoc.query("values \"adhoc2\".MY_PLUS2(1, 1)").returns(res);
-
-    // adhoc2 can see own function MY_PLUS2 but not adhoc2.MY_PLUS unless
-    // qualified
-    final CalciteAssert.AssertThat adhoc2 = with.withDefaultSchema("adhoc2");
-    adhoc2.query("values MY_PLUS2(1, 1)").returns(res);
-    adhoc2.query("values MY_PLUS(1, 1)").throws_(err);
-    adhoc2.query("values \"adhoc\".MY_PLUS(1, 1)").returns(res);
-
-    // adhoc3 can see own adhoc2.MY_PLUS2 because in path, with or without
-    // qualification, but can only see adhoc.MY_PLUS with qualification
-    final CalciteAssert.AssertThat adhoc3 = with.withDefaultSchema("adhoc3");
-    adhoc3.query("values MY_PLUS2(1, 1)").returns(res);
-    adhoc3.query("values MY_PLUS(1, 1)").throws_(err);
-    adhoc3.query("values \"adhoc\".MY_PLUS(1, 1)").returns(res);
-  }
-
   @Test public void testExplain() {
     final CalciteAssert.AssertThat with =
         CalciteAssert.that().with(CalciteAssert.Config.FOODMART_CLONE);
@@ -6903,39 +6204,14 @@ public class JdbcTest {
     };
 
     public QueryableTable foo(int count) {
-      return generateStrings(count);
+      return Smalls.generateStrings(count);
     }
 
     public TranslatableTable view(String s) {
-      return JdbcTest.view(s);
+      return Smalls.view(s);
     }
   }
 
-  public static TranslatableTable view(String s) {
-    return new ViewTable(Object.class,
-        new RelProtoDataType() {
-          public RelDataType apply(RelDataTypeFactory typeFactory) {
-            return typeFactory.builder().add("c", SqlTypeName.INTEGER)
-                .build();
-          }
-        }, "values (1), (3), " + s, ImmutableList.<String>of());
-  }
-
-  public static TranslatableTable str(Object o, Object p) {
-    assertThat(RexLiteral.validConstant(o, Litmus.THROW), is(true));
-    assertThat(RexLiteral.validConstant(p, Litmus.THROW), is(true));
-    return new ViewTable(Object.class,
-        new RelProtoDataType() {
-          public RelDataType apply(RelDataTypeFactory typeFactory) {
-            return typeFactory.builder().add("c", SqlTypeName.VARCHAR, 100)
-                .build();
-          }
-        },
-        "values " + SqlDialect.CALCITE.quoteStringLiteral(o.toString())
-            + ", " + SqlDialect.CALCITE.quoteStringLiteral(p.toString()),
-        ImmutableList.<String>of());
-  }
-
   public static class Employee {
     public final int empid;
     public final int deptno;
@@ -7061,21 +6337,6 @@ public class JdbcTest {
 
   //CHECKSTYLE: ON
 
-  /** Class with int and String fields. */
-  public static class IntString {
-    public final int n;
-    public final String s;
-
-    public IntString(int n, String s) {
-      this.n = n;
-      this.s = s;
-    }
-
-    public String toString() {
-      return "{n=" + n + ", s=" + s + "}";
-    }
-  }
-
   /** Abstract base class for implementations of {@link ModifiableTable}. */
   public abstract static class AbstractModifiableTable
       extends AbstractTable implements ModifiableTable {
@@ -7277,274 +6538,6 @@ public class JdbcTest {
     public MyTable2[] mytable2 = { new MyTable2() };
   }
 
-  /** Example of a UDF with a non-static {@code eval} method,
-   * and named parameters. */
-  public static class MyPlusFunction {
-    public int eval(@Parameter(name = "x") int x, @Parameter(name = "y") int y) {
-      return x + y;
-    }
-  }
-
-  /** Example of a UDF with named parameters. */
-  public static class MyLeftFunction {
-    public String eval(@Parameter(name = "s") String s,
-        @Parameter(name = "n") int n) {
-      return s.substring(0, n);
-    }
-  }
-
-  /** Example of a UDF with named parameters, some of them optional. */
-  public static class MyAbcdeFunction {
-    public String eval(@Parameter(name = "A", optional = false) Integer a,
-        @Parameter(name = "B", optional = true) Integer b,
-        @Parameter(name = "C", optional = false) Integer c,
-        @Parameter(name = "D", optional = true) Integer d,
-        @Parameter(name = "E", optional = true) Integer e) {
-      return "{a: " + a + ", b: " + b +  ", c: " + c +  ", d: " + d  + ", e: "
-          + e + "}";
-    }
-  }
-
-  /** Example of a non-strict UDF. (Does something useful when passed NULL.) */
-  public static class MyToStringFunction {
-    public static String eval(@Parameter(name = "o") Object o) {
-      if (o == null) {
-        return "<null>";
-      }
-      return "<" + o.toString() + ">";
-    }
-  }
-
-  /** Example of a UDF with a static {@code eval} method. Class is abstract,
-   * but code-generator should not need to instantiate it. */
-  public abstract static class MyDoubleFunction {
-    private MyDoubleFunction() {
-    }
-
-    public static int eval(int x) {
-      return x * 2;
-    }
-  }
-
-  /** User-defined function with two arguments. */
-  public static class MyIncrement {
-    public float eval(int x, int y) {
-      return x + x * y / 100;
-    }
-  }
-
-  /** Example of a UDF that has overloaded UDFs (same name, different args). */
-  public abstract static class CountArgs0Function {
-    private CountArgs0Function() {}
-
-    public static int eval() {
-      return 0;
-    }
-  }
-
-  /** See {@link CountArgs0Function}. */
-  public abstract static class CountArgs1Function {
-    private CountArgs1Function() {}
-
-    public static int eval(int x) {
-      return 1;
-    }
-  }
-
-  /** See {@link CountArgs0Function}. */
-  public abstract static class CountArgs2Function {
-    private CountArgs2Function() {}
-
-    public static int eval(int x, int y) {
-      return 2;
-    }
-  }
-
-  /** Example of a UDF class that needs to be instantiated but cannot be. */
-  public abstract static class AwkwardFunction {
-    private AwkwardFunction() {
-    }
-
-    public int eval(int x) {
-      return 0;
-    }
-  }
-
-  /** UDF class that has multiple methods, some overloaded. */
-  public static class MultipleFunction {
-    private MultipleFunction() {}
-
-    // Three overloads
-    public static String fun1(String x) { return x.toLowerCase(); }
-    public static int fun1(int x) { return x * 2; }
-    public static int fun1(int x, int y) { return x + y; }
-
-    // Another method
-    public static int fun2(int x) { return x * 3; }
-
-    // Non-static method cannot be used because constructor is private
-    public int nonStatic(int x) { return x * 3; }
-  }
-
-  /** Example of a user-defined aggregate function (UDAF). */
-  public static class MySumFunction {
-    public MySumFunction() {
-    }
-    public long init() {
-      return 0L;
-    }
-    public long add(long accumulator, int v) {
-      return accumulator + v;
-    }
-    public long merge(long accumulator0, long accumulator1) {
-      return accumulator0 + accumulator1;
-    }
-    public long result(long accumulator) {
-      return accumulator;
-    }
-  }
-
-  /** Example of a user-defined aggregate function (UDAF), whose methods are
-   * static. */
-  public static class MyStaticSumFunction {
-    public static long init() {
-      return 0L;
-    }
-    public static long add(long accumulator, int v) {
-      return accumulator + v;
-    }
-    public static long merge(long accumulator0, long accumulator1) {
-      return accumulator0 + accumulator1;
-    }
-    public static long result(long accumulator) {
-      return accumulator;
-    }
-  }
-
-  /** User-defined function. */
-  public static class SumFunctionBadIAdd {
-    public long init() {
-      return 0L;
-    }
-    public long add(short accumulator, int v) {
-      return accumulator + v;
-    }
-  }
-
-  /** User-defined table-macro function. */
-  public static class TableMacroFunction {
-    public TranslatableTable eval(String s) {
-      return view(s);
-    }
-  }
-
-  /** User-defined table-macro function whose eval method is static. */
-  public static class StaticTableMacroFunction {
-    public static TranslatableTable eval(String s) {
-      return view(s);
-    }
-  }
-
-  /** User-defined table-macro function with named and optional parameters. */
-  public static class TableMacroFunctionWithNamedParameters {
-    public TranslatableTable eval(
-        @Parameter(name = "R", optional = true) String r,
-        @Parameter(name = "S") String s,
-        @Parameter(name = "T", optional = true) Integer t) {
-      final StringBuilder sb = new StringBuilder();
-      abc(sb, r);
-      abc(sb, s);
-      abc(sb, t);
-      return view(sb.toString());
-    }
-
-    private void abc(StringBuilder sb, Object s) {
-      if (s != null) {
-        if (sb.length() > 0) {
-          sb.append(", ");
-        }
-        sb.append('(').append(s).append(')');
-      }
-    }
-  }
-
-  private static QueryableTable oneThreePlus(String s) {
-    List<Integer> items;
-    // Argument is null in case SQL contains function call with expression.
-    // Then the engine calls a function with null arguments to get getRowType.
-    if (s == null) {
-      items = ImmutableList.of();
-    } else {
-      Integer latest = Integer.parseInt(s.substring(1, s.length() - 1));
-      items = ImmutableList.of(1, 3, latest);
-    }
-    final Enumerable<Integer> enumerable = Linq4j.asEnumerable(items);
-    return new AbstractQueryableTable(Integer.class) {
-      public <E> Queryable<E> asQueryable(
-          QueryProvider queryProvider, SchemaPlus schema, String tableName) {
-        //noinspection unchecked
-        return (Queryable<E>) enumerable.asQueryable();
-      }
-
-      public RelDataType getRowType(RelDataTypeFactory typeFactory) {
-        return typeFactory.builder().add("c", SqlTypeName.INTEGER).build();
-      }
-    };
-  }
-
-  /** A table function that returns a {@link QueryableTable}. */
-  public static class MyTableFunction {
-    public QueryableTable eval(String s) {
-      return oneThreePlus(s);
-    }
-  }
-
-  /** A table function that returns a {@link QueryableTable} via a
-   * static method. */
-  public static class TestStaticTableFunction {
-    public static QueryableTable eval(String s) {
-      return oneThreePlus(s);
-    }
-  }
-
-  /** The real MazeTable may be found in example/function. This is a cut-down
-   * version to support a test. */
-  public static class MazeTable extends AbstractTable
-      implements ScannableTable {
-
-    private final String content;
-
-    public MazeTable(String content) {
-      this.content = content;
-    }
-
-    public static ScannableTable generate(int width, int height, int seed) {
-      return new MazeTable(String.format("generate(w=%d, h=%d, s=%d)", width, height, seed));
-    }
-
-    public static ScannableTable generate2(
-        @Parameter(name = "WIDTH") int width,
-        @Parameter(name = "HEIGHT") int height,
-        @Parameter(name = "SEED", optional = true) Integer seed) {
-      return new MazeTable(String.format("generate2(w=%d, h=%d, s=%d)", width, height, seed));
-    }
-
-    public static ScannableTable generate3(
-        @Parameter(name = "FOO") String foo) {
-      return new MazeTable(String.format("generate3(foo=%s)", foo));
-    }
-
-    public RelDataType getRowType(RelDataTypeFactory typeFactory) {
-      return typeFactory.builder()
-          .add("S", SqlTypeName.VARCHAR, 12)
-          .build();
-    }
-
-    public Enumerable<Object[]> scan(DataContext root) {
-      Object[][] rows = {{"abcde"}, {"xyz"}, {content}};
-      return Linq4j.asEnumerable(rows);
-    }
-  }
 }
 
 // End JdbcTest.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/16478ee1/core/src/test/java/org/apache/calcite/test/ReflectiveSchemaTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/test/ReflectiveSchemaTest.java b/core/src/test/java/org/apache/calcite/test/ReflectiveSchemaTest.java
index c950ef7..95b15d6 100644
--- a/core/src/test/java/org/apache/calcite/test/ReflectiveSchemaTest.java
+++ b/core/src/test/java/org/apache/calcite/test/ReflectiveSchemaTest.java
@@ -34,6 +34,7 @@ import org.apache.calcite.schema.SchemaPlus;
 import org.apache.calcite.schema.impl.AbstractSchema;
 import org.apache.calcite.schema.impl.TableMacroImpl;
 import org.apache.calcite.schema.impl.ViewTable;
+import org.apache.calcite.util.Smalls;
 import org.apache.calcite.util.Util;
 
 import com.google.common.base.Function;
@@ -172,9 +173,9 @@ public class ReflectiveSchemaTest {
     SchemaPlus rootSchema = calciteConnection.getRootSchema();
     SchemaPlus schema = rootSchema.add("s", new AbstractSchema());
     schema.add("GenerateStrings",
-        TableMacroImpl.create(JdbcTest.GENERATE_STRINGS_METHOD));
+        TableMacroImpl.create(Smalls.GENERATE_STRINGS_METHOD));
     schema.add("StringUnion",
-        TableMacroImpl.create(JdbcTest.STRING_UNION_METHOD));
+        TableMacroImpl.create(Smalls.STRING_UNION_METHOD));
     rootSchema.add("hr", new ReflectiveSchema(new JdbcTest.HrSchema()));
     ResultSet resultSet = connection.createStatement().executeQuery(
         "select *\n"

http://git-wip-us.apache.org/repos/asf/calcite/blob/16478ee1/core/src/test/java/org/apache/calcite/test/UdfTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/test/UdfTest.java b/core/src/test/java/org/apache/calcite/test/UdfTest.java
new file mode 100644
index 0000000..2e8126d
--- /dev/null
+++ b/core/src/test/java/org/apache/calcite/test/UdfTest.java
@@ -0,0 +1,561 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.calcite.test;
+
+import org.apache.calcite.adapter.java.ReflectiveSchema;
+import org.apache.calcite.jdbc.CalciteConnection;
+import org.apache.calcite.schema.SchemaPlus;
+import org.apache.calcite.schema.impl.AbstractSchema;
+import org.apache.calcite.schema.impl.ScalarFunctionImpl;
+import org.apache.calcite.schema.impl.ViewTable;
+import org.apache.calcite.util.Smalls;
+
+import com.google.common.collect.ImmutableList;
+
+import org.junit.Test;
+
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.ResultSet;
+import java.sql.Statement;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+
+/**
+ * Tests for user-defined functions (including user-defined table functions
+ * and user-defined aggregate functions).
+ *
+ * @see Smalls
+ */
+public class UdfTest {
+  private CalciteAssert.AssertThat withUdf() {
+    final String model = "{\n"
+        + "  version: '1.0',\n"
+        + "   schemas: [\n"
+        + "     {\n"
+        + "       name: 'adhoc',\n"
+        + "       tables: [\n"
+        + "         {\n"
+        + "           name: 'EMPLOYEES',\n"
+        + "           type: 'custom',\n"
+        + "           factory: '"
+        + JdbcTest.EmpDeptTableFactory.class.getName()
+        + "',\n"
+        + "           operand: {'foo': true, 'bar': 345}\n"
+        + "         }\n"
+        + "       ],\n"
+        + "       functions: [\n"
+        + "         {\n"
+        + "           name: 'MY_PLUS',\n"
+        + "           className: '"
+        + Smalls.MyPlusFunction.class.getName()
+        + "'\n"
+        + "         },\n"
+        + "         {\n"
+        + "           name: 'MY_LEFT',\n"
+        + "           className: '"
+        + Smalls.MyLeftFunction.class.getName()
+        + "'\n"
+        + "         },\n"
+        + "         {\n"
+        + "           name: 'ABCDE',\n"
+        + "           className: '"
+        + Smalls.MyAbcdeFunction.class.getName()
+        + "'\n"
+        + "         },\n"
+        + "         {\n"
+        + "           name: 'MY_STR',\n"
+        + "           className: '"
+        + Smalls.MyToStringFunction.class.getName()
+        + "'\n"
+        + "         },\n"
+        + "         {\n"
+        + "           name: 'MY_DOUBLE',\n"
+        + "           className: '"
+        + Smalls.MyDoubleFunction.class.getName()
+        + "'\n"
+        + "         },\n"
+        + "         {\n"
+        + "           name: 'COUNT_ARGS',\n"
+        + "           className: '"
+        + Smalls.CountArgs0Function.class.getName()
+        + "'\n"
+        + "         },\n"
+        + "         {\n"
+        + "           name: 'COUNT_ARGS',\n"
+        + "           className: '"
+        + Smalls.CountArgs1Function.class.getName()
+        + "'\n"
+        + "         },\n"
+        + "         {\n"
+        + "           name: 'COUNT_ARGS',\n"
+        + "           className: '"
+        + Smalls.CountArgs2Function.class.getName()
+        + "'\n"
+        + "         },\n"
+        + "         {\n"
+        + "           name: 'MY_ABS',\n"
+        + "           className: '"
+        + java.lang.Math.class.getName()
+        + "',\n"
+        + "           methodName: 'abs'\n"
+        + "         },\n"
+        + "         {\n"
+        + "           className: '"
+        + Smalls.MultipleFunction.class.getName()
+        + "',\n"
+        + "           methodName: '*'\n"
+        + "         }\n"
+        + "       ]\n"
+        + "     }\n"
+        + "   ]\n"
+        + "}";
+    return CalciteAssert.model(model);
+  }
+
+  /** Tests user-defined function. */
+  @Test public void testUserDefinedFunction() throws Exception {
+    final String sql = "select \"adhoc\".my_plus(\"deptno\", 100) as p\n"
+        + "from \"adhoc\".EMPLOYEES";
+    final String expected = "P=110\n"
+            + "P=120\n"
+            + "P=110\n"
+            + "P=110\n";
+    withUdf().query(sql).returns(expected);
+  }
+
+  @Test public void testUserDefinedFunctionB() throws Exception {
+    final String sql = "select \"adhoc\".my_double(\"deptno\") as p\n"
+        + "from \"adhoc\".EMPLOYEES";
+    final String expected = "P=20\n"
+        + "P=40\n"
+        + "P=20\n"
+        + "P=20\n";
+    withUdf().query(sql).returns(expected);
+  }
+
+  /** Test case for
+   * <a href="https://issues.apache.org/jira/browse/CALCITE-937">[CALCITE-937]
+   * User-defined function within view</a>. */
+  @Test public void testUserDefinedFunctionInView() throws Exception {
+    Class.forName("org.apache.calcite.jdbc.Driver");
+    Connection connection = DriverManager.getConnection("jdbc:calcite:");
+    CalciteConnection calciteConnection =
+        connection.unwrap(CalciteConnection.class);
+    SchemaPlus rootSchema = calciteConnection.getRootSchema();
+    rootSchema.add("hr", new ReflectiveSchema(new JdbcTest.HrSchema()));
+
+    SchemaPlus post = rootSchema.add("POST", new AbstractSchema());
+    post.add("MY_INCREMENT",
+        ScalarFunctionImpl.create(Smalls.MyIncrement.class, "eval"));
+
+    final String viewSql = "select \"empid\" as EMPLOYEE_ID,\n"
+        + "  \"name\" || ' ' || \"name\" as EMPLOYEE_NAME,\n"
+        + "  \"salary\" as EMPLOYEE_SALARY,\n"
+        + "  POST.MY_INCREMENT(\"empid\", 10) as INCREMENTED_SALARY\n"
+        + "from \"hr\".\"emps\"";
+    post.add("V_EMP",
+        ViewTable.viewMacro(post, viewSql, ImmutableList.<String>of(), null));
+
+    final String result = ""
+        + "EMPLOYEE_ID=100; EMPLOYEE_NAME=Bill Bill; EMPLOYEE_SALARY=10000.0; INCREMENTED_SALARY=110.0\n"
+        + "EMPLOYEE_ID=200; EMPLOYEE_NAME=Eric Eric; EMPLOYEE_SALARY=8000.0; INCREMENTED_SALARY=220.0\n"
+        + "EMPLOYEE_ID=150; EMPLOYEE_NAME=Sebastian Sebastian; EMPLOYEE_SALARY=7000.0; INCREMENTED_SALARY=165.0\n"
+        + "EMPLOYEE_ID=110; EMPLOYEE_NAME=Theodore Theodore; EMPLOYEE_SALARY=11500.0; INCREMENTED_SALARY=121.0\n";
+
+    Statement statement = connection.createStatement();
+    ResultSet resultSet = statement.executeQuery(viewSql);
+    assertThat(CalciteAssert.toString(resultSet), is(result));
+    resultSet.close();
+
+    ResultSet viewResultSet =
+        statement.executeQuery("select * from \"POST\".\"V_EMP\"");
+    assertThat(CalciteAssert.toString(viewResultSet), is(result));
+    statement.close();
+    connection.close();
+  }
+
+  /**
+   * Tests that IS NULL/IS NOT NULL is properly implemented for non-strict
+   * functions.
+   */
+  @Test public void testNotNullImplementor() {
+    final CalciteAssert.AssertThat with = withUdf();
+    with.query(
+        "select upper(\"adhoc\".my_str(\"name\")) as p from \"adhoc\".EMPLOYEES")
+        .returns("P=<BILL>\n"
+            + "P=<ERIC>\n"
+            + "P=<SEBASTIAN>\n"
+            + "P=<THEODORE>\n");
+    with.query("select \"name\" as p from \"adhoc\".EMPLOYEES\n"
+        + "where \"adhoc\".my_str(\"name\") is not null")
+        .returns("P=Bill\n"
+            + "P=Eric\n"
+            + "P=Sebastian\n"
+            + "P=Theodore\n");
+    with.query("select \"name\" as p from \"adhoc\".EMPLOYEES\n"
+        + "where \"adhoc\".my_str(upper(\"name\")) is not null")
+        .returns("P=Bill\n"
+            + "P=Eric\n"
+            + "P=Sebastian\n"
+            + "P=Theodore\n");
+    with.query("select \"name\" as p from \"adhoc\".EMPLOYEES\n"
+        + "where upper(\"adhoc\".my_str(\"name\")) is not null")
+        .returns("P=Bill\n"
+            + "P=Eric\n"
+            + "P=Sebastian\n"
+            + "P=Theodore\n");
+    with.query("select \"name\" as p from \"adhoc\".EMPLOYEES\n"
+        + "where \"adhoc\".my_str(\"name\") is null")
+        .returns("");
+    with.query("select \"name\" as p from \"adhoc\".EMPLOYEES\n"
+        + "where \"adhoc\".my_str(upper(\"adhoc\".my_str(\"name\")"
+        + ")) ='8'")
+        .returns("");
+  }
+
+  /** Tests derived return type of user-defined function. */
+  @Test public void testUdfDerivedReturnType() {
+    final CalciteAssert.AssertThat with = withUdf();
+    with.query(
+        "select max(\"adhoc\".my_double(\"deptno\")) as p from \"adhoc\".EMPLOYEES")
+        .returns("P=40\n");
+    with.query("select max(\"adhoc\".my_str(\"name\")) as p\n"
+        + "from \"adhoc\".EMPLOYEES\n"
+        + "where \"adhoc\".my_str(\"name\") is null")
+        .returns("P=null\n");
+  }
+
+  /** Tests a user-defined function that has multiple overloads. */
+  @Test public void testUdfOverloaded() {
+    final CalciteAssert.AssertThat with = withUdf();
+    with.query("values (\"adhoc\".count_args(),\n"
+        + " \"adhoc\".count_args(0),\n"
+        + " \"adhoc\".count_args(0, 0))")
+        .returns("EXPR$0=0; EXPR$1=1; EXPR$2=2\n");
+    with.query("select max(\"adhoc\".count_args()) as p0,\n"
+        + " min(\"adhoc\".count_args(0)) as p1,\n"
+        + " max(\"adhoc\".count_args(0, 0)) as p2\n"
+        + "from \"adhoc\".EMPLOYEES limit 1")
+        .returns("P0=0; P1=1; P2=2\n");
+  }
+
+  /** Tests passing parameters to user-defined function by name. */
+  @Test public void testUdfArgumentName() {
+    final CalciteAssert.AssertThat with = withUdf();
+    // arguments in physical order
+    with.query("values (\"adhoc\".my_left(\"s\" => 'hello', \"n\" => 3))")
+        .returns("EXPR$0=hel\n");
+    // arguments in reverse order
+    with.query("values (\"adhoc\".my_left(\"n\" => 3, \"s\" => 'hello'))")
+        .returns("EXPR$0=hel\n");
+    with.query("values (\"adhoc\".my_left(\"n\" => 1 + 2, \"s\" => 'hello'))")
+        .returns("EXPR$0=hel\n");
+    // duplicate argument names
+    with.query("values (\"adhoc\".my_left(\"n\" => 3, \"n\" => 2, \"s\" => 'hello'))")
+        .throws_("Duplicate argument name 'n'");
+    // invalid argument names
+    with.query("values (\"adhoc\".my_left(\"n\" => 3, \"m\" => 2, \"s\" => 'h'))")
+        .throws_("No match found for function signature "
+            + "MY_LEFT(n => <NUMERIC>, m => <NUMERIC>, s => <CHARACTER>)");
+    // missing arguments
+    with.query("values (\"adhoc\".my_left(\"n\" => 3))")
+        .throws_("No match found for function signature MY_LEFT(n => <NUMERIC>)");
+    with.query("values (\"adhoc\".my_left(\"s\" => 'hello'))")
+        .throws_("No match found for function signature MY_LEFT(s => <CHARACTER>)");
+    // arguments of wrong type
+    with.query("values (\"adhoc\".my_left(\"n\" => 'hello', \"s\" => 'x'))")
+        .throws_("No match found for function signature "
+            + "MY_LEFT(n => <CHARACTER>, s => <CHARACTER>)");
+    with.query("values (\"adhoc\".my_left(\"n\" => 1, \"s\" => 0))")
+        .throws_("No match found for function signature "
+            + "MY_LEFT(n => <NUMERIC>, s => <NUMERIC>)");
+  }
+
+  /** Tests calling a user-defined function some of whose parameters are
+   * optional. */
+  @Test public void testUdfArgumentOptional() {
+    final CalciteAssert.AssertThat with = withUdf();
+    with.query("values (\"adhoc\".abcde(a=>1,b=>2,c=>3,d=>4,e=>5))")
+        .returns("EXPR$0={a: 1, b: 2, c: 3, d: 4, e: 5}\n");
+    with.query("values (\"adhoc\".abcde(1,2,3,4,CAST(NULL AS INTEGER)))")
+        .returns("EXPR$0={a: 1, b: 2, c: 3, d: 4, e: null}\n");
+    with.query("values (\"adhoc\".abcde(a=>1,b=>2,c=>3,d=>4))")
+        .returns("EXPR$0={a: 1, b: 2, c: 3, d: 4, e: null}\n");
+    with.query("values (\"adhoc\".abcde(a=>1,b=>2,c=>3))")
+        .returns("EXPR$0={a: 1, b: 2, c: 3, d: null, e: null}\n");
+    with.query("values (\"adhoc\".abcde(a=>1,e=>5,c=>3))")
+        .returns("EXPR$0={a: 1, b: null, c: 3, d: null, e: 5}\n");
+    with.query("values (\"adhoc\".abcde(1,2,3))")
+        .returns("EXPR$0={a: 1, b: 2, c: 3, d: null, e: null}\n");
+    with.query("values (\"adhoc\".abcde(1,2,3,4))")
+        .returns("EXPR$0={a: 1, b: 2, c: 3, d: 4, e: null}\n");
+    with.query("values (\"adhoc\".abcde(1,2,3,4,5))")
+        .returns("EXPR$0={a: 1, b: 2, c: 3, d: 4, e: 5}\n");
+    with.query("values (\"adhoc\".abcde(1,2))")
+        .throws_("No match found for function signature ABCDE(<NUMERIC>, <NUMERIC>)");
+    with.query("values (\"adhoc\".abcde(1,DEFAULT,3))")
+        .returns("EXPR$0={a: 1, b: null, c: 3, d: null, e: null}\n");
+    with.query("values (\"adhoc\".abcde(1,DEFAULT,'abcde'))")
+        .throws_("No match found for function signature ABCDE(<NUMERIC>, <ANY>, <CHARACTER>)");
+    with.query("values (\"adhoc\".abcde(true))")
+        .throws_("No match found for function signature ABCDE(<BOOLEAN>)");
+    with.query("values (\"adhoc\".abcde(true,DEFAULT))")
+        .throws_("No match found for function signature ABCDE(<BOOLEAN>, <ANY>)");
+    with.query("values (\"adhoc\".abcde(1,DEFAULT,3,DEFAULT))")
+        .returns("EXPR$0={a: 1, b: null, c: 3, d: null, e: null}\n");
+    with.query("values (\"adhoc\".abcde(1,2,DEFAULT))")
+        .throws_("DEFAULT is only allowed for optional parameters");
+    with.query("values (\"adhoc\".abcde(a=>1,b=>2,c=>DEFAULT))")
+        .throws_("DEFAULT is only allowed for optional parameters");
+    with.query("values (\"adhoc\".abcde(a=>1,b=>DEFAULT,c=>3))")
+        .returns("EXPR$0={a: 1, b: null, c: 3, d: null, e: null}\n");
+  }
+
+  /** Test for
+   * {@link org.apache.calcite.runtime.CalciteResource#requireDefaultConstructor(String)}. */
+  @Test public void testUserDefinedFunction2() throws Exception {
+    withBadUdf(Smalls.AwkwardFunction.class)
+        .connectThrows(
+            "Declaring class 'org.apache.calcite.util.Smalls$AwkwardFunction' of non-static user-defined function must have a public constructor with zero parameters");
+  }
+
+  /** Tests user-defined function, with multiple methods per class. */
+  @Test public void testUserDefinedFunctionWithMethodName() throws Exception {
+    // java.lang.Math has abs(int) and abs(double).
+    final CalciteAssert.AssertThat with = withUdf();
+    with.query("values abs(-4)").returnsValue("4");
+    with.query("values abs(-4.5)").returnsValue("4.5");
+
+    // 3 overloads of "fun1", another method "fun2", but method "nonStatic"
+    // cannot be used as a function
+    with.query("values \"adhoc\".\"fun1\"(2)").returnsValue("4");
+    with.query("values \"adhoc\".\"fun1\"(2, 3)").returnsValue("5");
+    with.query("values \"adhoc\".\"fun1\"('Foo Bar')").returnsValue("foo bar");
+    with.query("values \"adhoc\".\"fun2\"(10)").returnsValue("30");
+    with.query("values \"adhoc\".\"nonStatic\"(2)")
+        .throws_("No match found for function signature nonStatic(<NUMERIC>)");
+  }
+
+  /** Tests user-defined aggregate function. */
+  @Test public void testUserDefinedAggregateFunction() throws Exception {
+    final String empDept = JdbcTest.EmpDeptTableFactory.class.getName();
+    final String sum = Smalls.MyStaticSumFunction.class.getName();
+    final String sum2 = Smalls.MySumFunction.class.getName();
+    final CalciteAssert.AssertThat with = CalciteAssert.model("{\n"
+        + "  version: '1.0',\n"
+        + "   schemas: [\n"
+        + "     {\n"
+        + "       name: 'adhoc',\n"
+        + "       tables: [\n"
+        + "         {\n"
+        + "           name: 'EMPLOYEES',\n"
+        + "           type: 'custom',\n"
+        + "           factory: '" + empDept + "',\n"
+        + "           operand: {'foo': true, 'bar': 345}\n"
+        + "         }\n"
+        + "       ],\n"
+        + "       functions: [\n"
+        + "         {\n"
+        + "           name: 'MY_SUM',\n"
+        + "           className: '" + sum + "'\n"
+        + "         },\n"
+        + "         {\n"
+        + "           name: 'MY_SUM2',\n"
+        + "           className: '" + sum2 + "'\n"
+        + "         }\n"
+        + "       ]\n"
+        + "     }\n"
+        + "   ]\n"
+        + "}")
+        .withDefaultSchema("adhoc");
+    with.withDefaultSchema(null)
+        .query(
+            "select \"adhoc\".my_sum(\"deptno\") as p from \"adhoc\".EMPLOYEES\n")
+        .returns("P=50\n");
+    with.query("select my_sum(\"empid\"), \"deptno\" as p from EMPLOYEES\n")
+        .throws_(
+            "Expression 'deptno' is not being grouped");
+    with.query("select my_sum(\"deptno\") as p from EMPLOYEES\n")
+        .returns("P=50\n");
+    with.query("select my_sum(\"name\") as p from EMPLOYEES\n")
+        .throws_(
+            "Cannot apply 'MY_SUM' to arguments of type 'MY_SUM(<JAVATYPE(CLASS JAVA.LANG.STRING)>)'. Supported form(s): 'MY_SUM(<NUMERIC>)");
+    with.query("select my_sum(\"deptno\", 1) as p from EMPLOYEES\n")
+        .throws_(
+            "No match found for function signature MY_SUM(<NUMERIC>, <NUMERIC>)");
+    with.query("select my_sum() as p from EMPLOYEES\n")
+        .throws_(
+            "No match found for function signature MY_SUM()");
+    with.query("select \"deptno\", my_sum(\"deptno\") as p from EMPLOYEES\n"
+        + "group by \"deptno\"")
+        .returnsUnordered(
+            "deptno=20; P=20",
+            "deptno=10; P=30");
+    with.query("select \"deptno\", my_sum2(\"deptno\") as p from EMPLOYEES\n"
+        + "group by \"deptno\"")
+        .returnsUnordered("deptno=20; P=20", "deptno=10; P=30");
+  }
+
+  /** Test for
+   * {@link org.apache.calcite.runtime.CalciteResource#firstParameterOfAdd(String)}. */
+  @Test public void testUserDefinedAggregateFunction3() throws Exception {
+    withBadUdf(Smalls.SumFunctionBadIAdd.class).connectThrows(
+        "Caused by: java.lang.RuntimeException: In user-defined aggregate class 'org.apache.calcite.util.Smalls$SumFunctionBadIAdd', first parameter to 'add' method must be the accumulator (the return type of the 'init' method)");
+  }
+
+  private static CalciteAssert.AssertThat withBadUdf(Class clazz) {
+    final String empDept = JdbcTest.EmpDeptTableFactory.class.getName();
+    final String className = clazz.getName();
+    return CalciteAssert.model("{\n"
+        + "  version: '1.0',\n"
+        + "   schemas: [\n"
+        + "     {\n"
+        + "       name: 'adhoc',\n"
+        + "       tables: [\n"
+        + "         {\n"
+        + "           name: 'EMPLOYEES',\n"
+        + "           type: 'custom',\n"
+        + "           factory: '" + empDept + "',\n"
+        + "           operand: {'foo': true, 'bar': 345}\n"
+        + "         }\n"
+        + "       ],\n"
+        + "       functions: [\n"
+        + "         {\n"
+        + "           name: 'AWKWARD',\n"
+        + "           className: '" + className + "'\n"
+        + "         }\n"
+        + "       ]\n"
+        + "     }\n"
+        + "   ]\n"
+        + "}")
+        .withDefaultSchema("adhoc");
+  }
+
+  /** Tests user-defined aggregate function with FILTER.
+   *
+   * <p>Also tests that we do not try to push ADAF to JDBC source. */
+  @Test public void testUserDefinedAggregateFunctionWithFilter() throws Exception {
+    final String sum = Smalls.MyStaticSumFunction.class.getName();
+    final String sum2 = Smalls.MySumFunction.class.getName();
+    final CalciteAssert.AssertThat with = CalciteAssert.model("{\n"
+        + "  version: '1.0',\n"
+        + "   schemas: [\n"
+        + JdbcTest.SCOTT_SCHEMA
+        + ",\n"
+        + "     {\n"
+        + "       name: 'adhoc',\n"
+        + "       functions: [\n"
+        + "         {\n"
+        + "           name: 'MY_SUM',\n"
+        + "           className: '" + sum + "'\n"
+        + "         },\n"
+        + "         {\n"
+        + "           name: 'MY_SUM2',\n"
+        + "           className: '" + sum2 + "'\n"
+        + "         }\n"
+        + "       ]\n"
+        + "     }\n"
+        + "   ]\n"
+        + "}")
+        .withDefaultSchema("adhoc");
+    with.query("select deptno, \"adhoc\".my_sum(deptno) as p\n"
+        + "from scott.emp\n"
+        + "group by deptno\n")
+        .returns(
+            "DEPTNO=20; P=100\n"
+                + "DEPTNO=10; P=30\n"
+                + "DEPTNO=30; P=180\n");
+
+    with.query("select deptno,\n"
+        + "  \"adhoc\".my_sum(deptno) filter (where job = 'CLERK') as c,\n"
+        + "  \"adhoc\".my_sum(deptno) filter (where job = 'XXX') as x\n"
+        + "from scott.emp\n"
+        + "group by deptno\n")
+        .returns(
+            "DEPTNO=20; C=40; X=0\n"
+                + "DEPTNO=10; C=10; X=0\n"
+                + "DEPTNO=30; C=30; X=0\n");
+  }
+
+  /** Tests resolution of functions using schema paths. */
+  @Test public void testPath() throws Exception {
+    final String name = Smalls.MyPlusFunction.class.getName();
+    final CalciteAssert.AssertThat with = CalciteAssert.model("{\n"
+        + "  version: '1.0',\n"
+        + "   schemas: [\n"
+        + "     {\n"
+        + "       name: 'adhoc',\n"
+        + "       functions: [\n"
+        + "         {\n"
+        + "           name: 'MY_PLUS',\n"
+        + "           className: '" + name + "'\n"
+        + "         }\n"
+        + "       ]\n"
+        + "     },\n"
+        + "     {\n"
+        + "       name: 'adhoc2',\n"
+        + "       functions: [\n"
+        + "         {\n"
+        + "           name: 'MY_PLUS2',\n"
+        + "           className: '" + name + "'\n"
+        + "         }\n"
+        + "       ]\n"
+        + "     },\n"
+        + "     {\n"
+        + "       name: 'adhoc3',\n"
+        + "       path: ['adhoc2','adhoc3'],\n"
+        + "       functions: [\n"
+        + "         {\n"
+        + "           name: 'MY_PLUS3',\n"
+        + "           className: '" + name + "'\n"
+        + "         }\n"
+        + "       ]\n"
+        + "     }\n"
+        + "   ]\n"
+        + "}");
+
+    final String err = "No match found for function signature";
+    final String res = "EXPR$0=2\n";
+
+    // adhoc can see own function MY_PLUS but not adhoc2.MY_PLUS2 unless
+    // qualified
+    final CalciteAssert.AssertThat adhoc = with.withDefaultSchema("adhoc");
+    adhoc.query("values MY_PLUS(1, 1)").returns(res);
+    adhoc.query("values MY_PLUS2(1, 1)").throws_(err);
+    adhoc.query("values \"adhoc2\".MY_PLUS(1, 1)").throws_(err);
+    adhoc.query("values \"adhoc2\".MY_PLUS2(1, 1)").returns(res);
+
+    // adhoc2 can see own function MY_PLUS2 but not adhoc2.MY_PLUS unless
+    // qualified
+    final CalciteAssert.AssertThat adhoc2 = with.withDefaultSchema("adhoc2");
+    adhoc2.query("values MY_PLUS2(1, 1)").returns(res);
+    adhoc2.query("values MY_PLUS(1, 1)").throws_(err);
+    adhoc2.query("values \"adhoc\".MY_PLUS(1, 1)").returns(res);
+
+    // adhoc3 can see own adhoc2.MY_PLUS2 because in path, with or without
+    // qualification, but can only see adhoc.MY_PLUS with qualification
+    final CalciteAssert.AssertThat adhoc3 = with.withDefaultSchema("adhoc3");
+    adhoc3.query("values MY_PLUS2(1, 1)").returns(res);
+    adhoc3.query("values MY_PLUS(1, 1)").throws_(err);
+    adhoc3.query("values \"adhoc\".MY_PLUS(1, 1)").returns(res);
+  }
+
+}
+
+// End UdfTest.java