/*
 * 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.config.CalciteConnectionConfig;
import org.apache.calcite.linq4j.AbstractEnumerable;
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.Deterministic;
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.function.SemiStrict;
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.runtime.SqlFunctions;
import org.apache.calcite.schema.QueryableTable;
import org.apache.calcite.schema.ScannableTable;
import org.apache.calcite.schema.Schema;
import org.apache.calcite.schema.SchemaPlus;
import org.apache.calcite.schema.Statistic;
import org.apache.calcite.schema.Statistics;
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.SqlCall;
import org.apache.calcite.sql.SqlNode;
import org.apache.calcite.sql.dialect.CalciteSqlDialect;
import org.apache.calcite.sql.type.SqlTypeName;

import com.google.common.collect.ImmutableList;

import java.lang.reflect.Method;
import java.sql.Date;
import java.sql.Time;
import java.sql.Timestamp;
import java.util.AbstractList;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import java.util.concurrent.atomic.AtomicInteger;

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 FIBONACCI_TABLE_METHOD =
      Types.lookupMethod(Smalls.class, "fibonacciTable");
  public static final Method FIBONACCI2_TABLE_METHOD =
      Types.lookupMethod(Smalls.class, "fibonacciTableWithLimit", long.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.Builder 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 generates the Fibonacci sequence.
   * Interesting because it has one column and no arguments. */
  public static ScannableTable fibonacciTable() {
    return fibonacciTableWithLimit(-1L);
  }

  /** A function that generates the Fibonacci sequence.
   * Interesting because it has one column and no arguments. */
  public static ScannableTable fibonacciTableWithLimit(final long limit) {
    return new ScannableTable() {
      public RelDataType getRowType(RelDataTypeFactory typeFactory) {
        return typeFactory.builder().add("N", SqlTypeName.BIGINT).build();
      }

      public Enumerable<Object[]> scan(DataContext root) {
        return new AbstractEnumerable<Object[]>() {
          public Enumerator<Object[]> enumerator() {
            return new Enumerator<Object[]>() {
              private long prev = 1;
              private long current = 0;

              public Object[] current() {
                return new Object[] {current};
              }

              public boolean moveNext() {
                final long next = current + prev;
                if (limit >= 0 && next > limit) {
                  return false;
                }
                prev = current;
                current = next;
                return true;
              }

              public void reset() {
                prev = 0;
                current = 1;
              }

              public void close() {
              }
            };
          }
        };
      }

      public Statistic getStatistic() {
        return Statistics.UNKNOWN;
      }

      public Schema.TableType getJdbcTableType() {
        return Schema.TableType.TABLE;
      }

      public boolean isRolledUp(String column) {
        return false;
      }

      public boolean rolledUpColumnValidInsideAgg(String column, SqlCall call,
          SqlNode parent, CalciteConnectionConfig config) {
        return true;
      }
    };
  }

  /**
   * 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(), Arrays.asList("view"));
  }

  public static TranslatableTable strView(String s) {
    return new ViewTable(Object.class,
        new RelProtoDataType() {
          public RelDataType apply(RelDataTypeFactory typeFactory) {
            return typeFactory.builder().add("c", SqlTypeName.VARCHAR, 100)
                    .build();
          }
        }, "values (" + CalciteSqlDialect.DEFAULT.quoteStringLiteral(s) + ")",
        ImmutableList.<String>of(), Arrays.asList("view"));
  }

  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 " + CalciteSqlDialect.DEFAULT.quoteStringLiteral(o.toString())
            + ", " + CalciteSqlDialect.DEFAULT.quoteStringLiteral(p.toString()),
        ImmutableList.<String>of(), Arrays.asList("view"));
  }

  /** 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 static final AtomicInteger INSTANCE_COUNT = new AtomicInteger(0);

    // Note: Not marked @Deterministic
    public MyPlusFunction() {
      INSTANCE_COUNT.incrementAndGet();
    }

    public int eval(@Parameter(name = "x") int x,
        @Parameter(name = "y") int y) {
      return x + y;
    }
  }

  /** As {@link MyPlusFunction} but declared to be deterministic. */
  public static class MyDeterministicPlusFunction {
    public static final AtomicInteger INSTANCE_COUNT = new AtomicInteger(0);

    @Deterministic public MyDeterministicPlusFunction() {
      INSTANCE_COUNT.incrementAndGet();
    }

    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 semi-strict UDF.
   * (Returns null if its parameter is null or if its length is 4.) */
  public static class Null4Function {
    @SemiStrict public static String eval(@Parameter(name = "s") String s) {
      if (s == null || s.length() == 4) {
        return null;
      }
      return s;
    }
  }

  /** Example of a picky, semi-strict UDF.
   * Throws {@link NullPointerException} if argument is null.
   * Returns null if its argument's length is 8. */
  public static class Null8Function {
    @SemiStrict public static String eval(@Parameter(name = "s") String s) {
      if (s.length() == 8) {
        return null;
      }
      return s;
    }
  }

  /** 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 CountArgs1NullableFunction {
    private CountArgs1NullableFunction() {}

    public static int eval(Short 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(Locale.ROOT);
    }
    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;
    }
  }

  /** UDF class that provides user-defined functions for each data type. */
  @Deterministic
  public static class AllTypesFunction {
    private AllTypesFunction() {}

    // We use SqlFunctions.toLong(Date) ratter than Date.getTime(),
    // and SqlFunctions.internalToTimestamp(long) rather than new Date(long),
    // because the contract of JDBC (also used by UDFs) is to represent
    // date-time values in the LOCAL time zone.

    public static long dateFun(java.sql.Date v) {
      return v == null ? -1L : SqlFunctions.toLong(v);
    }
    public static long timestampFun(java.sql.Timestamp v) {
      return v == null ? -1L : SqlFunctions.toLong(v);
    }
    public static long timeFun(java.sql.Time v) {
      return v == null ? -1L : SqlFunctions.toLong(v);
    }

    /** Overloaded, in a challenging way, with {@link #toDateFun(Long)}. */
    public static java.sql.Date toDateFun(int v) {
      return SqlFunctions.internalToDate(v);
    }

    public static java.sql.Date toDateFun(Long v) {
      return v == null ? null : SqlFunctions.internalToDate(v.intValue());
    }
    public static java.sql.Timestamp toTimestampFun(Long v) {
      return SqlFunctions.internalToTimestamp(v);
    }
    public static java.sql.Time toTimeFun(Long v) {
      return v == null ? null : SqlFunctions.internalToTime(v.intValue());
    }

    public static List arrayAppendFun(List v, Integer i) {
      if (v == null || i == null) {
        return null;
      } else {
        v.add(i);
        return v;
      }
    }

    /** Overloaded functions with DATE, TIMESTAMP and TIME arguments. */
    public static long toLong(Date date) {
      return date == null ? 0 : SqlFunctions.toLong(date);
    }

    public static long toLong(Timestamp timestamp) {
      return timestamp == null ? 0 : SqlFunctions.toLong(timestamp);
    }

    public static long toLong(Time time) {
      return time == null ? 0 : SqlFunctions.toLong(time);
    }

  }

  /** 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;
    }
  }

  /** A generic interface for defining user defined aggregate functions
   *
   * @param <A> accumulator type
   * @param <V> value type
   * @param <R> result type */
  private interface MyGenericAggFunction<A, V, R> {
    A init();

    A add(A accumulator, V val);

    A merge(A accumulator1, A accumulator2);

    R result(A accumulator);
  }

  /** Example of a user-defined aggregate function that implements a generic
   * interface. */
  public static class MySum3
      implements MyGenericAggFunction<Integer, Integer, Integer> {
    public Integer init() {
      return 0;
    }

    public Integer add(Integer accumulator, Integer val) {
      return accumulator + val;
    }

    public Integer merge(Integer accumulator1, Integer accumulator2) {
      return accumulator1 + accumulator2;
    }

    public Integer result(Integer 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;
    }
  }

  /** Example of a user-defined aggregate function (UDAF). */
  public static class MyTwoParamsSumFunctionFilter1 {
    public MyTwoParamsSumFunctionFilter1() {
    }
    public int init() {
      return 0;
    }
    public int add(int accumulator, int v1, int v2) {
      if (v1 > v2) {
        return accumulator + v1;
      }
      return accumulator;
    }
    public int merge(int accumulator0, int accumulator1) {
      return accumulator0 + accumulator1;
    }
    public int result(int accumulator) {
      return accumulator;
    }
  }

  /** Example of a user-defined aggregate function (UDAF). */
  public static class MyTwoParamsSumFunctionFilter2 {
    public MyTwoParamsSumFunctionFilter2() {
    }
    public long init() {
      return 0L;
    }
    public long add(long accumulator, int v1, String v2) {
      if (v2.equals("Eric")) {
        return accumulator + v1;
      }
      return accumulator;
    }
    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 MyThreeParamsSumFunctionWithFilter1 {
    public static long init() {
      return 0L;
    }
    public static long add(long accumulator, int v1, String v2, String v3) {
      if (v2.equals(v3)) {
        return accumulator + v1;
      }
      return accumulator;
    }
    public static long merge(long accumulator0, long accumulator1) {
      return accumulator0 + accumulator1;
    }
    public static long result(long accumulator) {
      return accumulator;
    }
  }

  /** Example of a user-defined aggregate function (UDAF), whose methods are
   * static. olny for validate to get exact function by calcite*/
  public static class MyThreeParamsSumFunctionWithFilter2 {
    public static long init() {
      return 0L;
    }
    public static long add(long accumulator, int v1, int v2, int v3) {
      if (v3 > 250) {
        return accumulator + v1 + v2;
      }
      return accumulator;
    }
    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(Locale.ROOT, "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(Locale.ROOT, "generate2(w=%d, h=%d, s=%d)", width,
              height, seed));
    }

    public static ScannableTable generate3(
        @Parameter(name = "FOO") String foo) {
      return new MazeTable(
          String.format(Locale.ROOT, "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);
    }
  }

  /** Schema containing a {@code prod} table with a lot of columns. */
  public static class WideSaleSchema {
    @Override public String toString() {
      return "WideSaleSchema";
    }

    @SuppressWarnings("unused")
    public final WideProductSale[] prod = {
        new WideProductSale(100, 10)
    };
  }

  /** Table with a lot of columns. */
  @SuppressWarnings("unused")
  public static class WideProductSale {
    public final int prodId;
    public final double sale0;
    public final double sale1 = 10;
    public final double sale2 = 10;
    public final double sale3 = 10;
    public final double sale4 = 10;
    public final double sale5 = 10;
    public final double sale6 = 10;
    public final double sale7 = 10;
    public final double sale8 = 10;
    public final double sale9 = 10;
    public final double sale10 = 10;
    public final double sale11 = 10;
    public final double sale12 = 10;
    public final double sale13 = 10;
    public final double sale14 = 10;
    public final double sale15 = 10;
    public final double sale16 = 10;
    public final double sale17 = 10;
    public final double sale18 = 10;
    public final double sale19 = 10;
    public final double sale20 = 10;
    public final double sale21 = 10;
    public final double sale22 = 10;
    public final double sale23 = 10;
    public final double sale24 = 10;
    public final double sale25 = 10;
    public final double sale26 = 10;
    public final double sale27 = 10;
    public final double sale28 = 10;
    public final double sale29 = 10;
    public final double sale30 = 10;
    public final double sale31 = 10;
    public final double sale32 = 10;
    public final double sale33 = 10;
    public final double sale34 = 10;
    public final double sale35 = 10;
    public final double sale36 = 10;
    public final double sale37 = 10;
    public final double sale38 = 10;
    public final double sale39 = 10;
    public final double sale40 = 10;
    public final double sale41 = 10;
    public final double sale42 = 10;
    public final double sale43 = 10;
    public final double sale44 = 10;
    public final double sale45 = 10;
    public final double sale46 = 10;
    public final double sale47 = 10;
    public final double sale48 = 10;
    public final double sale49 = 10;
    public final double sale50 = 10;
    public final double sale51 = 10;
    public final double sale52 = 10;
    public final double sale53 = 10;
    public final double sale54 = 10;
    public final double sale55 = 10;
    public final double sale56 = 10;
    public final double sale57 = 10;
    public final double sale58 = 10;
    public final double sale59 = 10;
    public final double sale60 = 10;
    public final double sale61 = 10;
    public final double sale62 = 10;
    public final double sale63 = 10;
    public final double sale64 = 10;
    public final double sale65 = 10;
    public final double sale66 = 10;
    public final double sale67 = 10;
    public final double sale68 = 10;
    public final double sale69 = 10;
    public final double sale70 = 10;
    public final double sale71 = 10;
    public final double sale72 = 10;
    public final double sale73 = 10;
    public final double sale74 = 10;
    public final double sale75 = 10;
    public final double sale76 = 10;
    public final double sale77 = 10;
    public final double sale78 = 10;
    public final double sale79 = 10;
    public final double sale80 = 10;
    public final double sale81 = 10;
    public final double sale82 = 10;
    public final double sale83 = 10;
    public final double sale84 = 10;
    public final double sale85 = 10;
    public final double sale86 = 10;
    public final double sale87 = 10;
    public final double sale88 = 10;
    public final double sale89 = 10;
    public final double sale90 = 10;
    public final double sale91 = 10;
    public final double sale92 = 10;
    public final double sale93 = 10;
    public final double sale94 = 10;
    public final double sale95 = 10;
    public final double sale96 = 10;
    public final double sale97 = 10;
    public final double sale98 = 10;
    public final double sale99 = 10;
    public final double sale100 = 10;
    public final double sale101 = 10;
    public final double sale102 = 10;
    public final double sale103 = 10;
    public final double sale104 = 10;
    public final double sale105 = 10;
    public final double sale106 = 10;
    public final double sale107 = 10;
    public final double sale108 = 10;
    public final double sale109 = 10;
    public final double sale110 = 10;
    public final double sale111 = 10;
    public final double sale112 = 10;
    public final double sale113 = 10;
    public final double sale114 = 10;
    public final double sale115 = 10;
    public final double sale116 = 10;
    public final double sale117 = 10;
    public final double sale118 = 10;
    public final double sale119 = 10;
    public final double sale120 = 10;
    public final double sale121 = 10;
    public final double sale122 = 10;
    public final double sale123 = 10;
    public final double sale124 = 10;
    public final double sale125 = 10;
    public final double sale126 = 10;
    public final double sale127 = 10;
    public final double sale128 = 10;
    public final double sale129 = 10;
    public final double sale130 = 10;
    public final double sale131 = 10;
    public final double sale132 = 10;
    public final double sale133 = 10;
    public final double sale134 = 10;
    public final double sale135 = 10;
    public final double sale136 = 10;
    public final double sale137 = 10;
    public final double sale138 = 10;
    public final double sale139 = 10;
    public final double sale140 = 10;
    public final double sale141 = 10;
    public final double sale142 = 10;
    public final double sale143 = 10;
    public final double sale144 = 10;
    public final double sale145 = 10;
    public final double sale146 = 10;
    public final double sale147 = 10;
    public final double sale148 = 10;
    public final double sale149 = 10;
    public final double sale150 = 10;
    public final double sale151 = 10;
    public final double sale152 = 10;
    public final double sale153 = 10;
    public final double sale154 = 10;
    public final double sale155 = 10;
    public final double sale156 = 10;
    public final double sale157 = 10;
    public final double sale158 = 10;
    public final double sale159 = 10;
    public final double sale160 = 10;
    public final double sale161 = 10;
    public final double sale162 = 10;
    public final double sale163 = 10;
    public final double sale164 = 10;
    public final double sale165 = 10;
    public final double sale166 = 10;
    public final double sale167 = 10;
    public final double sale168 = 10;
    public final double sale169 = 10;
    public final double sale170 = 10;
    public final double sale171 = 10;
    public final double sale172 = 10;
    public final double sale173 = 10;
    public final double sale174 = 10;
    public final double sale175 = 10;
    public final double sale176 = 10;
    public final double sale177 = 10;
    public final double sale178 = 10;
    public final double sale179 = 10;
    public final double sale180 = 10;
    public final double sale181 = 10;
    public final double sale182 = 10;
    public final double sale183 = 10;
    public final double sale184 = 10;
    public final double sale185 = 10;
    public final double sale186 = 10;
    public final double sale187 = 10;
    public final double sale188 = 10;
    public final double sale189 = 10;
    public final double sale190 = 10;
    public final double sale191 = 10;
    public final double sale192 = 10;
    public final double sale193 = 10;
    public final double sale194 = 10;
    public final double sale195 = 10;
    public final double sale196 = 10;
    public final double sale197 = 10;
    public final double sale198 = 10;
    public final double sale199 = 10;

    public WideProductSale(int prodId, double sale) {
      this.prodId = prodId;
      this.sale0 = sale;
    }
  }
}

// End Smalls.java
