You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@calcite.apache.org by jh...@apache.org on 2014/09/19 03:03:02 UTC

[1/2] [OPTIQ-413] Add RelDataTypeSystem plugin, allowing different max precision of a DECIMAL

Repository: incubator-optiq
Updated Branches:
  refs/heads/master a2cc356cf -> 0fd4e3d9e


http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/0fd4e3d9/core/src/main/java/org/eigenbase/sql/type/SqlTypeName.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/eigenbase/sql/type/SqlTypeName.java b/core/src/main/java/org/eigenbase/sql/type/SqlTypeName.java
index 65edb45..41745f5 100644
--- a/core/src/main/java/org/eigenbase/sql/type/SqlTypeName.java
+++ b/core/src/main/java/org/eigenbase/sql/type/SqlTypeName.java
@@ -22,6 +22,7 @@ import java.sql.*;
 
 import java.util.*;
 
+import org.eigenbase.reltype.RelDataTypeSystem;
 import org.eigenbase.sql.*;
 import org.eigenbase.sql.parser.*;
 import org.eigenbase.util.*;
@@ -87,10 +88,6 @@ public enum SqlTypeName {
       SqlTypeFamily.COLUMN_LIST);
 
   public static final int MAX_DATETIME_PRECISION = 3;
-  public static final int MAX_NUMERIC_PRECISION = 19;
-  public static final int MAX_NUMERIC_SCALE = 19;
-  public static final int MAX_CHAR_LENGTH = 65536;
-  public static final int MAX_BINARY_LENGTH = 65536;
 
   // Minimum and default interval precisions are  defined by SQL2003
   // Maximum interval precisions are implementation dependent,
@@ -301,31 +298,15 @@ public enum SqlTypeName {
   }
 
   /**
-   * @return default precision for this type if supported, otherwise -1 if
-   * precision is either unsupported or must be specified explicitly
+   * Returns the default precision for this type if supported, otherwise -1 if
+   * precision is either unsupported or must be specified explicitly.
+   *
+   * @deprecated Use
+   * {@link org.eigenbase.reltype.RelDataTypeSystem#getDefaultPrecision(SqlTypeName)};
+   * will be removed after optiq-0.9.1.
    */
   public int getDefaultPrecision() {
-    switch (this) {
-    case CHAR:
-    case BINARY:
-    case VARCHAR:
-    case VARBINARY:
-      return 1;
-    case TIME:
-      return 0;
-    case TIMESTAMP:
-
-      // TODO jvs 26-July-2004:  should be 6 for microseconds,
-      // but we can't support that yet
-      return 0;
-    case DECIMAL:
-      return MAX_NUMERIC_PRECISION;
-    case INTERVAL_DAY_TIME:
-    case INTERVAL_YEAR_MONTH:
-      return DEFAULT_INTERVAL_START_PRECISION;
-    default:
-      return -1;
-    }
+    return RelDataTypeSystem.DEFAULT.getDefaultPrecision(this);
   }
 
   /**
@@ -698,26 +679,13 @@ public enum SqlTypeName {
    * precision/length are not applicable for this type.
    *
    * @return Maximum allowed precision
+   *
+   * @deprecated Use
+   * {@link org.eigenbase.reltype.RelDataTypeSystem#getMaxScale(SqlTypeName)};
+   * will be removed after optiq-0.9.1.
    */
   public int getMaxPrecision() {
-    switch (this) {
-    case DECIMAL:
-      return MAX_NUMERIC_PRECISION;
-    case VARCHAR:
-    case CHAR:
-      return MAX_CHAR_LENGTH;
-    case VARBINARY:
-    case BINARY:
-      return MAX_BINARY_LENGTH;
-    case TIME:
-    case TIMESTAMP:
-      return MAX_DATETIME_PRECISION;
-    case INTERVAL_DAY_TIME:
-    case INTERVAL_YEAR_MONTH:
-      return MAX_INTERVAL_START_PRECISION;
-    default:
-      return -1;
-    }
+    return RelDataTypeSystem.DEFAULT.getMaxPrecision(this);
   }
 
   /**
@@ -726,17 +694,13 @@ public enum SqlTypeName {
    * applicable for this type.
    *
    * @return Maximum allowed scale
+   *
+   * @deprecated Use
+   * {@link org.eigenbase.reltype.RelDataTypeSystem#getMaxScale(SqlTypeName)};
+   * will be removed after optiq-0.9.1.
    */
   public int getMaxScale() {
-    switch (this) {
-    case DECIMAL:
-      return MAX_NUMERIC_SCALE;
-    case INTERVAL_DAY_TIME:
-    case INTERVAL_YEAR_MONTH:
-      return MAX_INTERVAL_FRACTIONAL_SECOND_PRECISION;
-    default:
-      return -1;
-    }
+    return RelDataTypeSystem.DEFAULT.getMaxScale(this);
   }
 
   /**

http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/0fd4e3d9/core/src/main/java/org/eigenbase/sql/validate/SqlValidatorImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/eigenbase/sql/validate/SqlValidatorImpl.java b/core/src/main/java/org/eigenbase/sql/validate/SqlValidatorImpl.java
index b0d2901..631ccf6 100644
--- a/core/src/main/java/org/eigenbase/sql/validate/SqlValidatorImpl.java
+++ b/core/src/main/java/org/eigenbase/sql/validate/SqlValidatorImpl.java
@@ -2500,7 +2500,7 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
         String intervalStr = interval.getIntervalLiteral();
         // throws EigenbaseContextException if string is invalid
         int[] values = intervalQualifier.evaluateIntervalLiteral(intervalStr,
-            literal.getParserPosition());
+            literal.getParserPosition(), typeFactory.getTypeSystem());
         Util.discard(values);
       }
       break;
@@ -2525,43 +2525,41 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
     assert qualifier != null;
     boolean startPrecisionOutOfRange = false;
     boolean fractionalSecondPrecisionOutOfRange = false;
-
+    final RelDataTypeSystem typeSystem = typeFactory.getTypeSystem();
+
+    final int startPrecision = qualifier.getStartPrecision(typeSystem);
+    final int fracPrecision =
+        qualifier.getFractionalSecondPrecision(typeSystem);
+    final int maxPrecision = typeSystem.getMaxPrecision(qualifier.typeName());
+    final int minPrecision = qualifier.typeName().getMinPrecision();
+    final int minScale = qualifier.typeName().getMinScale();
+    final int maxScale = typeSystem.getMaxScale(qualifier.typeName());
     if (qualifier.isYearMonth()) {
-      if ((qualifier.getStartPrecision()
-          < SqlTypeName.INTERVAL_YEAR_MONTH.getMinPrecision())
-          || (qualifier.getStartPrecision()
-          > SqlTypeName.INTERVAL_YEAR_MONTH.getMaxPrecision())) {
+      if (startPrecision < minPrecision || startPrecision > maxPrecision) {
         startPrecisionOutOfRange = true;
-      } else if (
-          (qualifier.getFractionalSecondPrecision()
-              < SqlTypeName.INTERVAL_YEAR_MONTH.getMinScale())
-              || (qualifier.getFractionalSecondPrecision()
-              > SqlTypeName.INTERVAL_YEAR_MONTH.getMaxScale())) {
-        fractionalSecondPrecisionOutOfRange = true;
+      } else {
+        if (fracPrecision < minScale || fracPrecision > maxScale) {
+          fractionalSecondPrecisionOutOfRange = true;
+        }
       }
     } else {
-      if ((qualifier.getStartPrecision()
-          < SqlTypeName.INTERVAL_DAY_TIME.getMinPrecision())
-          || (qualifier.getStartPrecision()
-          > SqlTypeName.INTERVAL_DAY_TIME.getMaxPrecision())) {
+      if (startPrecision < minPrecision || startPrecision > maxPrecision) {
         startPrecisionOutOfRange = true;
-      } else if (
-          (qualifier.getFractionalSecondPrecision()
-              < SqlTypeName.INTERVAL_DAY_TIME.getMinScale())
-              || (qualifier.getFractionalSecondPrecision()
-              > SqlTypeName.INTERVAL_DAY_TIME.getMaxScale())) {
-        fractionalSecondPrecisionOutOfRange = true;
+      } else {
+        if (fracPrecision < minScale || fracPrecision > maxScale) {
+          fractionalSecondPrecisionOutOfRange = true;
+        }
       }
     }
 
     if (startPrecisionOutOfRange) {
       throw newValidationError(qualifier,
-          RESOURCE.intervalStartPrecisionOutOfRange(
-              qualifier.getStartPrecision(), "INTERVAL " + qualifier));
+          RESOURCE.intervalStartPrecisionOutOfRange(startPrecision,
+              "INTERVAL " + qualifier));
     } else if (fractionalSecondPrecisionOutOfRange) {
       throw newValidationError(qualifier,
           RESOURCE.intervalFractionalSecondPrecisionOutOfRange(
-              qualifier.getFractionalSecondPrecision(),
+              fracPrecision,
               "INTERVAL " + qualifier));
     }
   }

http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/0fd4e3d9/core/src/main/java/org/eigenbase/sql2rel/SqlToRelConverter.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/eigenbase/sql2rel/SqlToRelConverter.java b/core/src/main/java/org/eigenbase/sql2rel/SqlToRelConverter.java
index 6694895..c940b6c 100644
--- a/core/src/main/java/org/eigenbase/sql2rel/SqlToRelConverter.java
+++ b/core/src/main/java/org/eigenbase/sql2rel/SqlToRelConverter.java
@@ -4746,12 +4746,11 @@ public class SqlToRelConverter {
      */
     private RelDataType computeHistogramType(RelDataType type) {
       if (SqlTypeUtil.isExactNumeric(type)
-          && (type.getSqlTypeName() != SqlTypeName.BIGINT)) {
-        return new BasicSqlType(SqlTypeName.BIGINT);
-      } else if (
-          SqlTypeUtil.isApproximateNumeric(type)
-              && (type.getSqlTypeName() != SqlTypeName.DOUBLE)) {
-        return new BasicSqlType(SqlTypeName.DOUBLE);
+          && type.getSqlTypeName() != SqlTypeName.BIGINT) {
+        return typeFactory.createSqlType(SqlTypeName.BIGINT);
+      } else if (SqlTypeUtil.isApproximateNumeric(type)
+          && type.getSqlTypeName() != SqlTypeName.DOUBLE) {
+        return typeFactory.createSqlType(SqlTypeName.DOUBLE);
       } else {
         return type;
       }

http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/0fd4e3d9/core/src/test/java/net/hydromatic/optiq/impl/clone/ArrayTableTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/net/hydromatic/optiq/impl/clone/ArrayTableTest.java b/core/src/test/java/net/hydromatic/optiq/impl/clone/ArrayTableTest.java
index b2f4f59..534e9c2 100644
--- a/core/src/test/java/net/hydromatic/optiq/impl/clone/ArrayTableTest.java
+++ b/core/src/test/java/net/hydromatic/optiq/impl/clone/ArrayTableTest.java
@@ -23,6 +23,7 @@ import net.hydromatic.optiq.jdbc.JavaTypeFactoryImpl;
 
 import org.eigenbase.reltype.RelDataType;
 import org.eigenbase.reltype.RelDataTypeImpl;
+import org.eigenbase.reltype.RelDataTypeSystem;
 
 import org.junit.Test;
 
@@ -281,7 +282,8 @@ public class ArrayTableTest {
   }
 
   @Test public void testLoadSorted() {
-    final JavaTypeFactoryImpl typeFactory = new JavaTypeFactoryImpl();
+    final JavaTypeFactoryImpl typeFactory =
+        new JavaTypeFactoryImpl(RelDataTypeSystem.DEFAULT);
     final RelDataType rowType =
         typeFactory.builder()
             .add("empid", typeFactory.createType(int.class))
@@ -316,7 +318,8 @@ public class ArrayTableTest {
    * column #0. The algorithm needs to go back and permute the values of
    * column #0 after it discovers that column #1 is unique and sorts by it. */
   @Test public void testLoadSorted2() {
-    final JavaTypeFactoryImpl typeFactory = new JavaTypeFactoryImpl();
+    final JavaTypeFactoryImpl typeFactory =
+        new JavaTypeFactoryImpl(RelDataTypeSystem.DEFAULT);
     final RelDataType rowType =
         typeFactory.builder()
             .add("deptno", typeFactory.createType(int.class))

http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/0fd4e3d9/core/src/test/java/net/hydromatic/optiq/test/MaterializationTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/net/hydromatic/optiq/test/MaterializationTest.java b/core/src/test/java/net/hydromatic/optiq/test/MaterializationTest.java
index 314a919..3b4629d 100644
--- a/core/src/test/java/net/hydromatic/optiq/test/MaterializationTest.java
+++ b/core/src/test/java/net/hydromatic/optiq/test/MaterializationTest.java
@@ -22,6 +22,7 @@ import net.hydromatic.optiq.prepare.Prepare;
 
 import org.eigenbase.relopt.SubstitutionVisitor;
 import org.eigenbase.reltype.RelDataType;
+import org.eigenbase.reltype.RelDataTypeSystem;
 import org.eigenbase.rex.*;
 import org.eigenbase.sql.fun.SqlStdOperatorTable;
 
@@ -46,7 +47,8 @@ public class MaterializationTest {
       OptiqAssert.checkResultContains(
           "EnumerableTableAccessRel(table=[[hr, m0]])");
 
-  final JavaTypeFactoryImpl typeFactory = new JavaTypeFactoryImpl();
+  final JavaTypeFactoryImpl typeFactory =
+      new JavaTypeFactoryImpl(RelDataTypeSystem.DEFAULT);
   final RexBuilder rexBuilder = new RexBuilder(typeFactory);
 
   @Test public void testFilter() {

http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/0fd4e3d9/core/src/test/java/net/hydromatic/optiq/tools/FrameworksTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/net/hydromatic/optiq/tools/FrameworksTest.java b/core/src/test/java/net/hydromatic/optiq/tools/FrameworksTest.java
index 1f0cb33..ed4f381 100644
--- a/core/src/test/java/net/hydromatic/optiq/tools/FrameworksTest.java
+++ b/core/src/test/java/net/hydromatic/optiq/tools/FrameworksTest.java
@@ -21,16 +21,21 @@ import net.hydromatic.optiq.Table;
 import net.hydromatic.optiq.impl.AbstractTable;
 import net.hydromatic.optiq.rules.java.EnumerableConvention;
 import net.hydromatic.optiq.rules.java.JavaRules;
+import net.hydromatic.optiq.server.OptiqServerStatement;
 
 import org.eigenbase.rel.FilterRel;
 import org.eigenbase.rel.RelNode;
 import org.eigenbase.relopt.*;
 import org.eigenbase.reltype.RelDataType;
 import org.eigenbase.reltype.RelDataTypeFactory;
+import org.eigenbase.reltype.RelDataTypeSystem;
+import org.eigenbase.reltype.RelDataTypeSystemImpl;
 import org.eigenbase.rex.RexBuilder;
+import org.eigenbase.rex.RexLiteral;
 import org.eigenbase.rex.RexNode;
 import org.eigenbase.sql.SqlExplainLevel;
 import org.eigenbase.sql.fun.SqlStdOperatorTable;
+import org.eigenbase.sql.type.SqlTypeName;
 import org.eigenbase.util.Util;
 
 import org.junit.Test;
@@ -110,6 +115,69 @@ public class FrameworksTest {
     SchemaPlus rootSchema = Frameworks.createRootSchema(false);
     assertThat(rootSchema.getSubSchemaNames().size(), equalTo(0));
   }
+
+  /** Tests that validation (specifically, inferring the result of adding
+   * two DECIMAL(19, 0) values together) happens differently with a type system
+   * that allows a larger maximum precision for decimals.
+   *
+   * <p>Test case for
+   * <a href="https://issues.apache.org/jira/browse/OPTIQ-413">OPTIQ-413</a>,
+   * "Add RelDataTypeSystem plugin, allowing different max precision of a
+   * DECIMAL".
+   *
+   * <p>Also tests the plugin system, by specifying implementations of a
+   * plugin interface with public and private constructors. */
+  @Test public void testTypeSystem() {
+    checkTypeSystem(19, Frameworks.newConfigBuilder().build());
+    checkTypeSystem(25, Frameworks.newConfigBuilder()
+        .typeSystem(HiveLikeTypeSystem.INSTANCE).build());
+    checkTypeSystem(31, Frameworks.newConfigBuilder()
+        .typeSystem(new HiveLikeTypeSystem2()).build());
+  }
+
+  private void checkTypeSystem(final int expected, FrameworkConfig config) {
+    Frameworks.withPrepare(
+        new Frameworks.PrepareAction<Void>(config) {
+          @Override public Void apply(RelOptCluster cluster,
+              RelOptSchema relOptSchema, SchemaPlus rootSchema,
+              OptiqServerStatement statement) {
+            final RelDataType type =
+                cluster.getTypeFactory()
+                    .createSqlType(SqlTypeName.DECIMAL, 30, 2);
+            final RexLiteral literal =
+                cluster.getRexBuilder().makeExactLiteral(BigDecimal.ONE, type);
+            final RexNode call =
+                cluster.getRexBuilder().makeCall(SqlStdOperatorTable.PLUS,
+                    literal,
+                    literal);
+            assertEquals(expected, call.getType().getPrecision());
+            return null;
+          }
+        });
+  }
+
+  /** Dummy type system, similar to Hive's, accessed via an INSTANCE member. */
+  public static class HiveLikeTypeSystem extends RelDataTypeSystemImpl {
+    public static final RelDataTypeSystem INSTANCE = new HiveLikeTypeSystem();
+
+    private HiveLikeTypeSystem() {}
+
+    @Override public int getMaxNumericPrecision() {
+      assert super.getMaxNumericPrecision() == 19;
+      return 25;
+    }
+  }
+
+  /** Dummy type system, similar to Hive's, accessed via a public default
+   * constructor. */
+  public static class HiveLikeTypeSystem2 extends RelDataTypeSystemImpl {
+    public HiveLikeTypeSystem2() {}
+
+    @Override public int getMaxNumericPrecision() {
+      assert super.getMaxNumericPrecision() == 19;
+      return 38;
+    }
+  }
 }
 
 // End FrameworksTest.java

http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/0fd4e3d9/core/src/test/java/org/eigenbase/relopt/RelOptUtilTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/eigenbase/relopt/RelOptUtilTest.java b/core/src/test/java/org/eigenbase/relopt/RelOptUtilTest.java
index f08c14c..65e8b5f 100644
--- a/core/src/test/java/org/eigenbase/relopt/RelOptUtilTest.java
+++ b/core/src/test/java/org/eigenbase/relopt/RelOptUtilTest.java
@@ -36,7 +36,8 @@ public class RelOptUtilTest {
   //~ Methods ----------------------------------------------------------------
 
   @Test public void testTypeDump() {
-    RelDataTypeFactory typeFactory = new SqlTypeFactoryImpl();
+    RelDataTypeFactory typeFactory =
+        new SqlTypeFactoryImpl(RelDataTypeSystem.DEFAULT);
     RelDataType t1 =
         typeFactory.builder()
             .add("f0", SqlTypeName.DECIMAL, 5, 2)

http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/0fd4e3d9/core/src/test/java/org/eigenbase/relopt/volcano/VolcanoPlannerTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/eigenbase/relopt/volcano/VolcanoPlannerTest.java b/core/src/test/java/org/eigenbase/relopt/volcano/VolcanoPlannerTest.java
index 0a060ef..125fdb5 100644
--- a/core/src/test/java/org/eigenbase/relopt/volcano/VolcanoPlannerTest.java
+++ b/core/src/test/java/org/eigenbase/relopt/volcano/VolcanoPlannerTest.java
@@ -60,7 +60,8 @@ public class VolcanoPlannerTest {
 
   static RelOptCluster newCluster(VolcanoPlanner planner) {
     RelOptQuery query = new RelOptQuery(planner);
-    RelDataTypeFactory typeFactory = new SqlTypeFactoryImpl();
+    RelDataTypeFactory typeFactory =
+        new SqlTypeFactoryImpl(RelDataTypeSystem.DEFAULT);
     return query.createCluster(
         typeFactory,
         new RexBuilder(typeFactory));

http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/0fd4e3d9/core/src/test/java/org/eigenbase/sql/test/DefaultSqlTestFactory.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/eigenbase/sql/test/DefaultSqlTestFactory.java b/core/src/test/java/org/eigenbase/sql/test/DefaultSqlTestFactory.java
index a83a61b..c2538f5 100644
--- a/core/src/test/java/org/eigenbase/sql/test/DefaultSqlTestFactory.java
+++ b/core/src/test/java/org/eigenbase/sql/test/DefaultSqlTestFactory.java
@@ -17,6 +17,7 @@
 package org.eigenbase.sql.test;
 
 import org.eigenbase.reltype.RelDataTypeFactory;
+import org.eigenbase.reltype.RelDataTypeSystem;
 import org.eigenbase.sql.SqlOperatorTable;
 import org.eigenbase.sql.advise.SqlAdvisor;
 import org.eigenbase.sql.fun.SqlStdOperatorTable;
@@ -74,7 +75,8 @@ public class DefaultSqlTestFactory implements SqlTestFactory {
   public SqlValidator getValidator(SqlTestFactory factory) {
     final SqlOperatorTable operatorTable = factory.createOperatorTable();
     final boolean caseSensitive = (Boolean) factory.get("caseSensitive");
-    final RelDataTypeFactory typeFactory = new SqlTypeFactoryImpl();
+    final RelDataTypeFactory typeFactory =
+        new SqlTypeFactoryImpl(RelDataTypeSystem.DEFAULT);
     return SqlValidatorUtil.newValidator(operatorTable,
         new MockCatalogReader(typeFactory, caseSensitive).init(),
         typeFactory);

http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/0fd4e3d9/core/src/test/java/org/eigenbase/sql/test/SqlAdvisorTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/eigenbase/sql/test/SqlAdvisorTest.java b/core/src/test/java/org/eigenbase/sql/test/SqlAdvisorTest.java
index 2f0797b..a707234 100644
--- a/core/src/test/java/org/eigenbase/sql/test/SqlAdvisorTest.java
+++ b/core/src/test/java/org/eigenbase/sql/test/SqlAdvisorTest.java
@@ -1176,9 +1176,9 @@ public class SqlAdvisorTest extends SqlValidatorTestCase {
     }
 
     @Override
-    public SqlValidator getValidator(
-        SqlTestFactory factory) {
-      final RelDataTypeFactory typeFactory = new SqlTypeFactoryImpl();
+    public SqlValidator getValidator(SqlTestFactory factory) {
+      final RelDataTypeFactory typeFactory =
+          new SqlTypeFactoryImpl(RelDataTypeSystem.DEFAULT);
       final SqlConformance conformance = (SqlConformance) get("conformance");
       final boolean caseSensitive = (Boolean) factory.get("caseSensitive");
       return new SqlAdvisorValidator(

http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/0fd4e3d9/core/src/test/java/org/eigenbase/sql/test/SqlOperatorBaseTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/eigenbase/sql/test/SqlOperatorBaseTest.java b/core/src/test/java/org/eigenbase/sql/test/SqlOperatorBaseTest.java
index 6f3cc18..6ef09a4 100644
--- a/core/src/test/java/org/eigenbase/sql/test/SqlOperatorBaseTest.java
+++ b/core/src/test/java/org/eigenbase/sql/test/SqlOperatorBaseTest.java
@@ -24,6 +24,7 @@ import java.text.*;
 import java.util.*;
 import java.util.regex.*;
 
+import org.eigenbase.reltype.RelDataType;
 import org.eigenbase.sql.*;
 import org.eigenbase.sql.fun.*;
 import org.eigenbase.sql.parser.*;
@@ -4815,8 +4816,10 @@ public abstract class SqlOperatorBaseTest {
     if (!enable) {
       return;
     }
-    for (BasicSqlType type : SqlLimitsTest.getTypes()) {
-      for (Object o : getValues(type, true)) {
+    final List<RelDataType> types =
+        SqlLimitsTest.getTypes(tester.getValidator().getTypeFactory());
+    for (RelDataType type : types) {
+      for (Object o : getValues((BasicSqlType) type, true)) {
         SqlLiteral literal =
             type.getSqlTypeName().createLiteral(o, SqlParserPos.ZERO);
         SqlString literalString =
@@ -4863,8 +4866,10 @@ public abstract class SqlOperatorBaseTest {
    */
   @Test public void testLiteralBeyondLimit() {
     tester.setFor(SqlStdOperatorTable.CAST);
-    for (BasicSqlType type : SqlLimitsTest.getTypes()) {
-      for (Object o : getValues(type, false)) {
+    final List<RelDataType> types =
+        SqlLimitsTest.getTypes(tester.getValidator().getTypeFactory());
+    for (RelDataType type : types) {
+      for (Object o : getValues((BasicSqlType) type, false)) {
         SqlLiteral literal =
             type.getSqlTypeName().createLiteral(o, SqlParserPos.ZERO);
         SqlString literalString =

http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/0fd4e3d9/core/src/test/java/org/eigenbase/test/RexProgramTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/eigenbase/test/RexProgramTest.java b/core/src/test/java/org/eigenbase/test/RexProgramTest.java
index 184f1e6..c21168c 100644
--- a/core/src/test/java/org/eigenbase/test/RexProgramTest.java
+++ b/core/src/test/java/org/eigenbase/test/RexProgramTest.java
@@ -61,7 +61,7 @@ public class RexProgramTest {
 
   @Before
   public void setUp() {
-    typeFactory = new JavaTypeFactoryImpl();
+    typeFactory = new JavaTypeFactoryImpl(RelDataTypeSystem.DEFAULT);
     rexBuilder = new RexBuilder(typeFactory);
   }
 

http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/0fd4e3d9/core/src/test/java/org/eigenbase/test/RexTransformerTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/eigenbase/test/RexTransformerTest.java b/core/src/test/java/org/eigenbase/test/RexTransformerTest.java
index 788c859..95f0a58 100644
--- a/core/src/test/java/org/eigenbase/test/RexTransformerTest.java
+++ b/core/src/test/java/org/eigenbase/test/RexTransformerTest.java
@@ -47,7 +47,7 @@ public class RexTransformerTest {
 
   @Before
   public void setUp() {
-    typeFactory = new JavaTypeFactoryImpl();
+    typeFactory = new JavaTypeFactoryImpl(RelDataTypeSystem.DEFAULT);
     rexBuilder = new RexBuilder(typeFactory);
     boolRelDataType = typeFactory.createSqlType(SqlTypeName.BOOLEAN);
 

http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/0fd4e3d9/core/src/test/java/org/eigenbase/test/SargTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/eigenbase/test/SargTest.java b/core/src/test/java/org/eigenbase/test/SargTest.java
index 5f77c03..57230ad 100644
--- a/core/src/test/java/org/eigenbase/test/SargTest.java
+++ b/core/src/test/java/org/eigenbase/test/SargTest.java
@@ -76,7 +76,8 @@ public class SargTest {
   public void setUp() {
     // create some reusable fixtures
 
-    RelDataTypeFactory typeFactory = new SqlTypeFactoryImpl();
+    RelDataTypeFactory typeFactory =
+        new SqlTypeFactoryImpl(RelDataTypeSystem.DEFAULT);
     intType = typeFactory.createSqlType(SqlTypeName.INTEGER);
     intType = typeFactory.createTypeWithNullability(intType, true);
     stringType = typeFactory.createSqlType(SqlTypeName.VARCHAR, 20);

http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/0fd4e3d9/core/src/test/java/org/eigenbase/test/SqlLimitsTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/eigenbase/test/SqlLimitsTest.java b/core/src/test/java/org/eigenbase/test/SqlLimitsTest.java
index e122148..cbc2ce2 100644
--- a/core/src/test/java/org/eigenbase/test/SqlLimitsTest.java
+++ b/core/src/test/java/org/eigenbase/test/SqlLimitsTest.java
@@ -20,10 +20,15 @@ import java.io.*;
 import java.text.*;
 import java.util.*;
 
+import org.eigenbase.reltype.RelDataType;
+import org.eigenbase.reltype.RelDataTypeFactory;
+import org.eigenbase.reltype.RelDataTypeSystem;
 import org.eigenbase.sql.*;
 import org.eigenbase.sql.parser.*;
 import org.eigenbase.sql.type.*;
 
+import net.hydromatic.optiq.jdbc.JavaTypeFactoryImpl;
+
 import com.google.common.collect.ImmutableList;
 
 import org.junit.BeforeClass;
@@ -35,33 +40,6 @@ import org.junit.Test;
 public class SqlLimitsTest {
   //~ Static fields/initializers ---------------------------------------------
 
-  private static final List<BasicSqlType> TYPE_LIST =
-      ImmutableList.of(
-          new BasicSqlType(SqlTypeName.BOOLEAN),
-          new BasicSqlType(SqlTypeName.TINYINT),
-          new BasicSqlType(SqlTypeName.SMALLINT),
-          new BasicSqlType(SqlTypeName.INTEGER),
-          new BasicSqlType(SqlTypeName.BIGINT),
-          new BasicSqlType(SqlTypeName.DECIMAL),
-          new BasicSqlType(SqlTypeName.DECIMAL, 5),
-          new BasicSqlType(SqlTypeName.DECIMAL, 6, 2),
-          new BasicSqlType(SqlTypeName.DECIMAL,
-              SqlTypeName.DECIMAL.getMaxPrecision(), 0),
-          new BasicSqlType(SqlTypeName.DECIMAL,
-              SqlTypeName.DECIMAL.getMaxPrecision(), 5),
-
-          // todo: test IntervalDayTime and IntervalYearMonth
-          // todo: test Float, Real, Double
-
-          new BasicSqlType(SqlTypeName.CHAR, 5),
-          new BasicSqlType(SqlTypeName.VARCHAR, 1),
-          new BasicSqlType(SqlTypeName.VARCHAR, 20),
-          new BasicSqlType(SqlTypeName.BINARY, 3),
-          new BasicSqlType(SqlTypeName.VARBINARY, 4),
-          new BasicSqlType(SqlTypeName.DATE),
-          new BasicSqlType(SqlTypeName.TIME, 0),
-          new BasicSqlType(SqlTypeName.TIMESTAMP, 0));
-
   //~ Constructors -----------------------------------------------------------
 
   public SqlLimitsTest() {
@@ -73,11 +51,33 @@ public class SqlLimitsTest {
     return DiffRepository.lookup(SqlLimitsTest.class);
   }
 
-  /**
-   * Returns a list of typical types.
-   */
-  public static List<BasicSqlType> getTypes() {
-    return TYPE_LIST;
+  /** Returns a list of typical types. */
+  public static List<RelDataType> getTypes(RelDataTypeFactory typeFactory) {
+    final int maxPrecision =
+        typeFactory.getTypeSystem().getMaxPrecision(SqlTypeName.DECIMAL);
+    return ImmutableList.of(
+        typeFactory.createSqlType(SqlTypeName.BOOLEAN),
+        typeFactory.createSqlType(SqlTypeName.TINYINT),
+        typeFactory.createSqlType(SqlTypeName.SMALLINT),
+        typeFactory.createSqlType(SqlTypeName.INTEGER),
+        typeFactory.createSqlType(SqlTypeName.BIGINT),
+        typeFactory.createSqlType(SqlTypeName.DECIMAL),
+        typeFactory.createSqlType(SqlTypeName.DECIMAL, 5),
+        typeFactory.createSqlType(SqlTypeName.DECIMAL, 6, 2),
+        typeFactory.createSqlType(SqlTypeName.DECIMAL, maxPrecision, 0),
+        typeFactory.createSqlType(SqlTypeName.DECIMAL, maxPrecision, 5),
+
+        // todo: test IntervalDayTime and IntervalYearMonth
+        // todo: test Float, Real, Double
+
+        typeFactory.createSqlType(SqlTypeName.CHAR, 5),
+        typeFactory.createSqlType(SqlTypeName.VARCHAR, 1),
+        typeFactory.createSqlType(SqlTypeName.VARCHAR, 20),
+        typeFactory.createSqlType(SqlTypeName.BINARY, 3),
+        typeFactory.createSqlType(SqlTypeName.VARBINARY, 4),
+        typeFactory.createSqlType(SqlTypeName.DATE),
+        typeFactory.createSqlType(SqlTypeName.TIME, 0),
+        typeFactory.createSqlType(SqlTypeName.TIMESTAMP, 0));
   }
 
   @BeforeClass public static void setUSLocale() {
@@ -89,7 +89,9 @@ public class SqlLimitsTest {
   @Test public void testPrintLimits() {
     StringWriter sw = new StringWriter();
     PrintWriter pw = new PrintWriter(sw);
-    for (BasicSqlType type : TYPE_LIST) {
+    final List<RelDataType> types =
+        getTypes(new JavaTypeFactoryImpl(RelDataTypeSystem.DEFAULT));
+    for (RelDataType type : types) {
       pw.println(type.toString());
       printLimit(
           pw,
@@ -163,11 +165,11 @@ public class SqlLimitsTest {
   private void printLimit(
       PrintWriter pw,
       String desc,
-      BasicSqlType type,
+      RelDataType type,
       boolean sign,
       SqlTypeName.Limit limit,
       boolean beyond) {
-    Object o = type.getLimit(sign, limit, beyond);
+    Object o = ((BasicSqlType) type).getLimit(sign, limit, beyond);
     if (o == null) {
       return;
     }

http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/0fd4e3d9/core/src/test/java/org/eigenbase/test/SqlToRelTestBase.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/eigenbase/test/SqlToRelTestBase.java b/core/src/test/java/org/eigenbase/test/SqlToRelTestBase.java
index 9c22703..52cec3b 100644
--- a/core/src/test/java/org/eigenbase/test/SqlToRelTestBase.java
+++ b/core/src/test/java/org/eigenbase/test/SqlToRelTestBase.java
@@ -480,7 +480,7 @@ public abstract class SqlToRelTestBase {
     }
 
     protected RelDataTypeFactory createTypeFactory() {
-      return new SqlTypeFactoryImpl();
+      return new SqlTypeFactoryImpl(RelDataTypeSystem.DEFAULT);
     }
 
     protected final RelOptPlanner getPlanner() {

http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/0fd4e3d9/core/src/test/java/org/eigenbase/test/SqlValidatorFeatureTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/eigenbase/test/SqlValidatorFeatureTest.java b/core/src/test/java/org/eigenbase/test/SqlValidatorFeatureTest.java
index e4e58f0..bcc553e 100644
--- a/core/src/test/java/org/eigenbase/test/SqlValidatorFeatureTest.java
+++ b/core/src/test/java/org/eigenbase/test/SqlValidatorFeatureTest.java
@@ -124,7 +124,8 @@ public class SqlValidatorFeatureTest extends SqlValidatorTestCase {
 
     @Override
     public SqlValidator getValidator(SqlTestFactory factory) {
-      final RelDataTypeFactory typeFactory = new SqlTypeFactoryImpl();
+      final RelDataTypeFactory typeFactory =
+          new SqlTypeFactoryImpl(RelDataTypeSystem.DEFAULT);
       SqlConformance conformance = (SqlConformance) get("conformance");
       final boolean caseSensitive = (Boolean) get("caseSensitive");
       return new FeatureValidator(

http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/0fd4e3d9/core/src/test/java/org/eigenbase/test/SqlValidatorTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/eigenbase/test/SqlValidatorTest.java b/core/src/test/java/org/eigenbase/test/SqlValidatorTest.java
index 3bb6beb..1cb6108 100644
--- a/core/src/test/java/org/eigenbase/test/SqlValidatorTest.java
+++ b/core/src/test/java/org/eigenbase/test/SqlValidatorTest.java
@@ -22,6 +22,7 @@ import java.util.List;
 import java.util.Locale;
 import java.util.logging.*;
 
+import org.eigenbase.reltype.RelDataTypeSystem;
 import org.eigenbase.sql.*;
 import org.eigenbase.sql.fun.SqlStdOperatorTable;
 import org.eigenbase.sql.test.SqlTester;
@@ -3409,6 +3410,8 @@ public class SqlValidatorTest extends SqlValidatorTestCase {
     // First check that min, max, and defaults are what we expect
     // (values used in subtests depend on these being true to
     // accurately test bounds)
+    final RelDataTypeSystem typeSystem =
+        getTester().getValidator().getTypeFactory().getTypeSystem();
     assertTrue(
         SqlTypeName.INTERVAL_YEAR_MONTH.getMinPrecision() == 1);
     assertTrue(
@@ -3417,18 +3420,16 @@ public class SqlValidatorTest extends SqlValidatorTestCase {
         SqlTypeName.INTERVAL_YEAR_MONTH.getMaxPrecision() == 10);
     assertTrue(
         SqlTypeName.INTERVAL_DAY_TIME.getMaxPrecision() == 10);
-    assertTrue(
-        SqlTypeName.INTERVAL_YEAR_MONTH.getDefaultPrecision() == 2);
-    assertTrue(
-        SqlTypeName.INTERVAL_DAY_TIME.getDefaultPrecision() == 2);
+    assertEquals(2,
+        typeSystem.getDefaultPrecision(SqlTypeName.INTERVAL_YEAR_MONTH));
+    assertEquals(2,
+        typeSystem.getDefaultPrecision(SqlTypeName.INTERVAL_DAY_TIME));
     assertTrue(
         SqlTypeName.INTERVAL_YEAR_MONTH.getMinScale() == 1);
     assertTrue(
         SqlTypeName.INTERVAL_DAY_TIME.getMinScale() == 1);
-    assertTrue(
-        SqlTypeName.INTERVAL_YEAR_MONTH.getMaxScale() == 9);
-    assertTrue(
-        SqlTypeName.INTERVAL_DAY_TIME.getMaxScale() == 9);
+    assertEquals(9, typeSystem.getMaxScale(SqlTypeName.INTERVAL_YEAR_MONTH));
+    assertEquals(9, typeSystem.getMaxScale(SqlTypeName.INTERVAL_DAY_TIME));
     assertTrue(
         SqlTypeName.INTERVAL_YEAR_MONTH.getDefaultScale() == 6);
     assertTrue(

http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/0fd4e3d9/core/src/test/resources/org/eigenbase/test/SqlLimitsTest.xml
----------------------------------------------------------------------
diff --git a/core/src/test/resources/org/eigenbase/test/SqlLimitsTest.xml b/core/src/test/resources/org/eigenbase/test/SqlLimitsTest.xml
index 0ea34eb..956e51f 100644
--- a/core/src/test/resources/org/eigenbase/test/SqlLimitsTest.xml
+++ b/core/src/test/resources/org/eigenbase/test/SqlLimitsTest.xml
@@ -16,9 +16,9 @@ See the License for the specific language governing permissions and
 limitations under the License.
 -->
 <Root>
-  <TestCase name="testPrintLimits">
-    <Resource name="output">
-      <![CDATA[BOOLEAN
+    <TestCase name="testPrintLimits">
+        <Resource name="output">
+            <![CDATA[BOOLEAN
   zero:                   false; as SQL: FALSE
   max:                    true; as SQL: TRUE
 
@@ -58,7 +58,7 @@ BIGINT
   max:                    9223372036854775807; as SQL: 9223372036854775807
   max + epsilon:          9223372036854775808; as SQL: 9223372036854775808
 
-DECIMAL
+DECIMAL(19, 0)
   min - epsilon:          -9223372036854775809; as SQL: -9223372036854775809
   min:                    -9223372036854775808; as SQL: -9223372036854775808
   zero - delta:           -1; as SQL: -1
@@ -67,7 +67,7 @@ DECIMAL
   max:                    9223372036854775807; as SQL: 9223372036854775807
   max + epsilon:          9223372036854775808; as SQL: 9223372036854775808
 
-DECIMAL(5)
+DECIMAL(5, 0)
   min - epsilon:          -100000; as SQL: -100000
   min:                    -99999; as SQL: -99999
   zero - delta:           -1; as SQL: -1
@@ -148,6 +148,6 @@ TIMESTAMP(0)
   max:                    Dec 31, 9999 11:59:59 PM; as SQL: TIMESTAMP '9999-12-31 23:59:59'
 
 ]]>
-    </Resource>
-  </TestCase>
+        </Resource>
+    </TestCase>
 </Root>


[2/2] git commit: [OPTIQ-413] Add RelDataTypeSystem plugin, allowing different max precision of a DECIMAL

Posted by jh...@apache.org.
[OPTIQ-413] Add RelDataTypeSystem plugin, allowing different max precision of a DECIMAL


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

Branch: refs/heads/master
Commit: 0fd4e3d9ea63fa67a97ea8ba73e46a12e31af2b1
Parents: a2cc356
Author: Julian Hyde <jh...@apache.org>
Authored: Thu Sep 18 12:05:22 2014 -0700
Committer: Julian Hyde <jh...@apache.org>
Committed: Thu Sep 18 17:33:14 2014 -0700

----------------------------------------------------------------------
 .../avatica/ConnectionConfigImpl.java           |  59 +++++
 .../hydromatic/avatica/ConnectionProperty.java  |   4 +-
 .../main/codegen/templates/CombinedParser.jj    |   5 +-
 .../optiq/config/OptiqConnectionConfig.java     |   2 +
 .../optiq/config/OptiqConnectionConfigImpl.java |   5 +
 .../optiq/config/OptiqConnectionProperty.java   |   7 +-
 .../hydromatic/optiq/impl/jdbc/JdbcSchema.java  |   3 +-
 .../optiq/jdbc/JavaTypeFactoryImpl.java         |  10 +-
 .../optiq/jdbc/OptiqConnectionImpl.java         |  12 +-
 .../optiq/rules/java/RexToLixTranslator.java    |   4 +-
 .../hydromatic/optiq/tools/FrameworkConfig.java |   8 +
 .../net/hydromatic/optiq/tools/Frameworks.java  |  28 +-
 .../eigenbase/rel/rules/ReduceDecimalsRule.java |  44 +++-
 .../eigenbase/reltype/RelDataTypeFactory.java   |   5 +
 .../reltype/RelDataTypeFactoryImpl.java         |  30 ++-
 .../eigenbase/reltype/RelDataTypeSystem.java    |  60 +++++
 .../reltype/RelDataTypeSystemImpl.java          |  98 +++++++
 .../main/java/org/eigenbase/rex/RexBuilder.java |   9 +-
 .../java/org/eigenbase/rex/RexExecutorImpl.java |   3 +-
 .../org/eigenbase/sql/SqlIntervalQualifier.java | 258 ++++++++++---------
 .../eigenbase/sql/SqlUnresolvedFunction.java    |   6 +-
 .../org/eigenbase/sql/parser/SqlParserUtil.java |   5 +-
 .../org/eigenbase/sql/type/BasicSqlType.java    |  41 +--
 .../org/eigenbase/sql/type/IntervalSqlType.java |  45 ++--
 .../org/eigenbase/sql/type/ReturnTypes.java     |  18 +-
 .../eigenbase/sql/type/SqlTypeFactoryImpl.java  |  32 ++-
 .../org/eigenbase/sql/type/SqlTypeName.java     |  72 ++----
 .../sql/validate/SqlValidatorImpl.java          |  48 ++--
 .../eigenbase/sql2rel/SqlToRelConverter.java    |  11 +-
 .../optiq/impl/clone/ArrayTableTest.java        |   7 +-
 .../optiq/test/MaterializationTest.java         |   4 +-
 .../hydromatic/optiq/tools/FrameworksTest.java  |  68 +++++
 .../org/eigenbase/relopt/RelOptUtilTest.java    |   3 +-
 .../relopt/volcano/VolcanoPlannerTest.java      |   3 +-
 .../sql/test/DefaultSqlTestFactory.java         |   4 +-
 .../org/eigenbase/sql/test/SqlAdvisorTest.java  |   6 +-
 .../eigenbase/sql/test/SqlOperatorBaseTest.java |  13 +-
 .../java/org/eigenbase/test/RexProgramTest.java |   2 +-
 .../org/eigenbase/test/RexTransformerTest.java  |   2 +-
 .../test/java/org/eigenbase/test/SargTest.java  |   3 +-
 .../java/org/eigenbase/test/SqlLimitsTest.java  |  72 +++---
 .../org/eigenbase/test/SqlToRelTestBase.java    |   2 +-
 .../eigenbase/test/SqlValidatorFeatureTest.java |   3 +-
 .../org/eigenbase/test/SqlValidatorTest.java    |  17 +-
 .../org/eigenbase/test/SqlLimitsTest.xml        |  14 +-
 45 files changed, 775 insertions(+), 380 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/0fd4e3d9/avatica/src/main/java/net/hydromatic/avatica/ConnectionConfigImpl.java
----------------------------------------------------------------------
diff --git a/avatica/src/main/java/net/hydromatic/avatica/ConnectionConfigImpl.java b/avatica/src/main/java/net/hydromatic/avatica/ConnectionConfigImpl.java
index 89066eb..f406da2 100644
--- a/avatica/src/main/java/net/hydromatic/avatica/ConnectionConfigImpl.java
+++ b/avatica/src/main/java/net/hydromatic/avatica/ConnectionConfigImpl.java
@@ -16,6 +16,7 @@
  */
 package net.hydromatic.avatica;
 
+import java.lang.reflect.Field;
 import java.util.LinkedHashMap;
 import java.util.Map;
 import java.util.Properties;
@@ -123,6 +124,31 @@ public class ConnectionConfigImpl implements ConnectionConfig {
       //noinspection unchecked
       return get_(enumConverter(enumClass), defaultValue.name());
     }
+
+    /** Returns an instance of a plugin.
+     *
+     * <p>Throws if not set and no default.
+     * Also throws if the class does not implement the required interface,
+     * or if it does not have a public default constructor or an public static
+     * field called {@code #INSTANCE}. */
+    public <T> T getPlugin(Class<T> pluginClass, T defaultInstance) {
+      return getPlugin(pluginClass, (String) property.defaultValue(),
+          defaultInstance);
+    }
+
+    /** Returns an instance of a plugin, using a given class name if none is
+     * set.
+     *
+     * <p>Throws if not set and no default.
+     * Also throws if the class does not implement the required interface,
+     * or if it does not have a public default constructor or an public static
+     * field called {@code #INSTANCE}. */
+    public <T> T getPlugin(Class<T> pluginClass, String defaultClassName,
+        T defaultInstance) {
+      assert property.type() == ConnectionProperty.Type.PLUGIN;
+      return get_(pluginConverter(pluginClass, defaultInstance),
+          defaultClassName);
+    }
   }
 
   /** Callback to parse a property from string to its native type. */
@@ -165,6 +191,39 @@ public class ConnectionConfigImpl implements ConnectionConfig {
       }
     };
   }
+
+  public static <T> Converter<T> pluginConverter(final Class<T> pluginClass,
+      final T defaultInstance) {
+    return new Converter<T>() {
+      public T apply(ConnectionProperty connectionProperty, String s) {
+        if (s == null) {
+          if (defaultInstance != null) {
+            return defaultInstance;
+          }
+          throw new RuntimeException("Required property '"
+              + connectionProperty.camelName() + "' not specified");
+        }
+        // First look for a C.INSTANCE field, then do new C().
+        try {
+          //noinspection unchecked
+          final Class<T> clazz = (Class) Class.forName(s);
+          assert pluginClass.isAssignableFrom(clazz);
+          try {
+            // We assume that if there is an INSTANCE field it is static and
+            // has the right type.
+            final Field field = clazz.getField("INSTANCE");
+            return pluginClass.cast(field.get(null));
+          } catch (NoSuchFieldException e) {
+            // ignore
+          }
+          return clazz.newInstance();
+        } catch (Exception e) {
+          throw new RuntimeException("Property '" + s
+              + "' not valid for plugin type " + pluginClass.getName(), e);
+        }
+      }
+    };
+  }
 }
 
 // End ConnectionConfigImpl.java

http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/0fd4e3d9/avatica/src/main/java/net/hydromatic/avatica/ConnectionProperty.java
----------------------------------------------------------------------
diff --git a/avatica/src/main/java/net/hydromatic/avatica/ConnectionProperty.java b/avatica/src/main/java/net/hydromatic/avatica/ConnectionProperty.java
index 2dda49a..5fcdc50 100644
--- a/avatica/src/main/java/net/hydromatic/avatica/ConnectionProperty.java
+++ b/avatica/src/main/java/net/hydromatic/avatica/ConnectionProperty.java
@@ -46,13 +46,15 @@ public interface ConnectionProperty {
   enum Type {
     BOOLEAN,
     STRING,
-    ENUM;
+    ENUM,
+    PLUGIN;
 
     public boolean valid(Object defaultValue) {
       switch (this) {
       case BOOLEAN:
         return defaultValue instanceof Boolean;
       case STRING:
+      case PLUGIN:
         return defaultValue instanceof String;
       default:
         return defaultValue instanceof Enum;

http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/0fd4e3d9/core/src/main/codegen/templates/CombinedParser.jj
----------------------------------------------------------------------
diff --git a/core/src/main/codegen/templates/CombinedParser.jj b/core/src/main/codegen/templates/CombinedParser.jj
index 6e3101b..2ccad91 100644
--- a/core/src/main/codegen/templates/CombinedParser.jj
+++ b/core/src/main/codegen/templates/CombinedParser.jj
@@ -33,6 +33,7 @@ package ${parser.package};
 import ${importStr};
 </#list>
 
+import org.eigenbase.reltype.RelDataType;
 import org.eigenbase.sql.*;
 import org.eigenbase.sql.parser.*;
 import org.eigenbase.sql.fun.*;
@@ -3118,8 +3119,8 @@ SqlIntervalQualifier IntervalQualifier() :
 {
     SqlIntervalQualifier.TimeUnit start;
     SqlIntervalQualifier.TimeUnit end = null;
-    int startPrec = SqlIntervalQualifier.getDefaultPrecisionId();
-    int secondFracPrec = SqlIntervalQualifier.getDefaultPrecisionId();
+    int startPrec = RelDataType.PRECISION_NOT_SPECIFIED;
+    int secondFracPrec = RelDataType.PRECISION_NOT_SPECIFIED;
 }
 {
     (

http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/0fd4e3d9/core/src/main/java/net/hydromatic/optiq/config/OptiqConnectionConfig.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/net/hydromatic/optiq/config/OptiqConnectionConfig.java b/core/src/main/java/net/hydromatic/optiq/config/OptiqConnectionConfig.java
index a8a49ab..304a3cb 100644
--- a/core/src/main/java/net/hydromatic/optiq/config/OptiqConnectionConfig.java
+++ b/core/src/main/java/net/hydromatic/optiq/config/OptiqConnectionConfig.java
@@ -44,6 +44,8 @@ public interface OptiqConnectionConfig extends ConnectionConfig {
   boolean caseSensitive();
   /** @see net.hydromatic.optiq.config.OptiqConnectionProperty#SPARK */
   boolean spark();
+  /** @see net.hydromatic.optiq.config.OptiqConnectionProperty#TYPE_SYSTEM */
+  <T> T typeSystem(Class<T> typeSystemClass, T defaultTypeSystem);
 }
 
 // End OptiqConnectionConfig.java

http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/0fd4e3d9/core/src/main/java/net/hydromatic/optiq/config/OptiqConnectionConfigImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/net/hydromatic/optiq/config/OptiqConnectionConfigImpl.java b/core/src/main/java/net/hydromatic/optiq/config/OptiqConnectionConfigImpl.java
index 719f33b..f08cc7c 100644
--- a/core/src/main/java/net/hydromatic/optiq/config/OptiqConnectionConfigImpl.java
+++ b/core/src/main/java/net/hydromatic/optiq/config/OptiqConnectionConfigImpl.java
@@ -82,6 +82,11 @@ public class OptiqConnectionConfigImpl extends ConnectionConfigImpl
   public boolean spark() {
     return OptiqConnectionProperty.SPARK.wrap(properties).getBoolean();
   }
+
+  public <T> T typeSystem(Class<T> typeSystemClass, T defaultTypeSystem) {
+    return OptiqConnectionProperty.TYPE_SYSTEM.wrap(properties)
+        .getPlugin(typeSystemClass, defaultTypeSystem);
+  }
 }
 
 // End OptiqConnectionConfigImpl.java

http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/0fd4e3d9/core/src/main/java/net/hydromatic/optiq/config/OptiqConnectionProperty.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/net/hydromatic/optiq/config/OptiqConnectionProperty.java b/core/src/main/java/net/hydromatic/optiq/config/OptiqConnectionProperty.java
index ba3089c..5f2d85c 100644
--- a/core/src/main/java/net/hydromatic/optiq/config/OptiqConnectionProperty.java
+++ b/core/src/main/java/net/hydromatic/optiq/config/OptiqConnectionProperty.java
@@ -68,7 +68,12 @@ public enum OptiqConnectionProperty implements ConnectionProperty {
   SPARK("spark", Type.BOOLEAN, false),
 
   /** Timezone, for example 'gmt-3'. Default is the JVM's time zone. */
-  TIMEZONE("timezone", Type.STRING, null);
+  TIMEZONE("timezone", Type.STRING, null),
+
+  /** Type system. The name of a class that implements
+   * {@code org.eigenbase.reltype.RelDataTypeSystem} and has a public default
+   * constructor or an {@code INSTANCE} constant. */
+  TYPE_SYSTEM("typeSystem", Type.PLUGIN, null);
 
   private final String camelName;
   private final Type type;

http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/0fd4e3d9/core/src/main/java/net/hydromatic/optiq/impl/jdbc/JdbcSchema.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/net/hydromatic/optiq/impl/jdbc/JdbcSchema.java b/core/src/main/java/net/hydromatic/optiq/impl/jdbc/JdbcSchema.java
index b1940df..0301489 100644
--- a/core/src/main/java/net/hydromatic/optiq/impl/jdbc/JdbcSchema.java
+++ b/core/src/main/java/net/hydromatic/optiq/impl/jdbc/JdbcSchema.java
@@ -240,7 +240,8 @@ public class JdbcSchema implements Schema {
     // Temporary type factory, just for the duration of this method. Allowable
     // because we're creating a proto-type, not a type; before being used, the
     // proto-type will be copied into a real type factory.
-    final RelDataTypeFactory typeFactory = new SqlTypeFactoryImpl();
+    final RelDataTypeFactory typeFactory =
+        new SqlTypeFactoryImpl(RelDataTypeSystem.DEFAULT);
     final RelDataTypeFactory.FieldInfoBuilder fieldInfo = typeFactory.builder();
     while (resultSet.next()) {
       final String columnName = resultSet.getString(4);

http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/0fd4e3d9/core/src/main/java/net/hydromatic/optiq/jdbc/JavaTypeFactoryImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/net/hydromatic/optiq/jdbc/JavaTypeFactoryImpl.java b/core/src/main/java/net/hydromatic/optiq/jdbc/JavaTypeFactoryImpl.java
index 3202221..67fb093 100644
--- a/core/src/main/java/net/hydromatic/optiq/jdbc/JavaTypeFactoryImpl.java
+++ b/core/src/main/java/net/hydromatic/optiq/jdbc/JavaTypeFactoryImpl.java
@@ -53,6 +53,14 @@ public class JavaTypeFactoryImpl
   syntheticTypes =
       new HashMap<List<Pair<Type, Boolean>>, SyntheticRecordType>();
 
+  public JavaTypeFactoryImpl() {
+    this(RelDataTypeSystem.DEFAULT);
+  }
+
+  public JavaTypeFactoryImpl(RelDataTypeSystem typeSystem) {
+    super(typeSystem);
+  }
+
   public RelDataType createStructType(Class type) {
     List<RelDataTypeField> list = new ArrayList<RelDataTypeField>();
     for (Field field : type.getFields()) {
@@ -178,7 +186,7 @@ public class JavaTypeFactoryImpl
     }
     if (type instanceof JavaType) {
       return createTypeWithNullability(
-          new BasicSqlType(type.getSqlTypeName()),
+          createSqlType(type.getSqlTypeName()),
           type.isNullable());
     }
     return type;

http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/0fd4e3d9/core/src/main/java/net/hydromatic/optiq/jdbc/OptiqConnectionImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/net/hydromatic/optiq/jdbc/OptiqConnectionImpl.java b/core/src/main/java/net/hydromatic/optiq/jdbc/OptiqConnectionImpl.java
index d5c8592..80d77d3 100644
--- a/core/src/main/java/net/hydromatic/optiq/jdbc/OptiqConnectionImpl.java
+++ b/core/src/main/java/net/hydromatic/optiq/jdbc/OptiqConnectionImpl.java
@@ -35,6 +35,7 @@ import net.hydromatic.optiq.runtime.Hook;
 import net.hydromatic.optiq.server.OptiqServer;
 import net.hydromatic.optiq.server.OptiqServerStatement;
 
+import org.eigenbase.reltype.RelDataTypeSystem;
 import org.eigenbase.sql.advise.SqlAdvisor;
 import org.eigenbase.sql.advise.SqlAdvisorValidator;
 import org.eigenbase.sql.fun.SqlStdOperatorTable;
@@ -83,13 +84,18 @@ abstract class OptiqConnectionImpl
       String url, Properties info, OptiqRootSchema rootSchema,
       JavaTypeFactory typeFactory) {
     super(driver, factory, url, info);
+    OptiqConnectionConfig cfg = new OptiqConnectionConfigImpl(info);
     this.prepareFactory = driver.prepareFactory;
-    this.typeFactory =
-        typeFactory != null ? typeFactory : new JavaTypeFactoryImpl();
+    if (typeFactory != null) {
+      this.typeFactory = typeFactory;
+    } else {
+      final RelDataTypeSystem typeSystem =
+          cfg.typeSystem(RelDataTypeSystem.class, RelDataTypeSystem.DEFAULT);
+      this.typeFactory = new JavaTypeFactoryImpl(typeSystem);
+    }
     this.rootSchema =
         rootSchema != null ? rootSchema : OptiqSchema.createRootSchema(true);
 
-    OptiqConnectionConfig cfg = new OptiqConnectionConfigImpl(info);
     this.properties.put(InternalProperty.CASE_SENSITIVE, cfg.caseSensitive());
     this.properties.put(InternalProperty.UNQUOTED_CASING, cfg.unquotedCasing());
     this.properties.put(InternalProperty.QUOTED_CASING, cfg.quotedCasing());

http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/0fd4e3d9/core/src/main/java/net/hydromatic/optiq/rules/java/RexToLixTranslator.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/net/hydromatic/optiq/rules/java/RexToLixTranslator.java b/core/src/main/java/net/hydromatic/optiq/rules/java/RexToLixTranslator.java
index 369f9b1..4c13cfd 100644
--- a/core/src/main/java/net/hydromatic/optiq/rules/java/RexToLixTranslator.java
+++ b/core/src/main/java/net/hydromatic/optiq/rules/java/RexToLixTranslator.java
@@ -251,7 +251,9 @@ public class RexToLixTranslator {
                 BuiltinMethod.INTERVAL_DAY_TIME_TO_STRING.method,
                 operand,
                 Expressions.constant(interval.foo()),
-                Expressions.constant(interval.getFractionalSecondPrecision())));
+                Expressions.constant(
+                    interval.getFractionalSecondPrecision(
+                        typeFactory.getTypeSystem()))));
         break;
       case BOOLEAN:
         convert = RexImpTable.optimize2(

http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/0fd4e3d9/core/src/main/java/net/hydromatic/optiq/tools/FrameworkConfig.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/net/hydromatic/optiq/tools/FrameworkConfig.java b/core/src/main/java/net/hydromatic/optiq/tools/FrameworkConfig.java
index 4e9cd84..98d0739 100644
--- a/core/src/main/java/net/hydromatic/optiq/tools/FrameworkConfig.java
+++ b/core/src/main/java/net/hydromatic/optiq/tools/FrameworkConfig.java
@@ -22,6 +22,7 @@ import net.hydromatic.optiq.config.Lex;
 import org.eigenbase.relopt.Context;
 import org.eigenbase.relopt.RelOptCostFactory;
 import org.eigenbase.relopt.RelTraitDef;
+import org.eigenbase.reltype.RelDataTypeSystem;
 import org.eigenbase.sql.SqlOperatorTable;
 import org.eigenbase.sql.parser.SqlParserImplFactory;
 import org.eigenbase.sql2rel.SqlRexConvertletTable;
@@ -31,6 +32,8 @@ import com.google.common.collect.ImmutableList;
 /**
  * Interface that describes how to configure planning sessions generated
  * using the Frameworks tools.
+ *
+ * @see Frameworks#newConfigBuilder()
  */
 public interface FrameworkConfig {
   /**
@@ -107,6 +110,11 @@ public interface FrameworkConfig {
    * calling {@link org.eigenbase.relopt.RelOptPlanner#getContext()}.
    */
   Context getContext();
+
+  /**
+   * Returns the type system.
+   */
+  RelDataTypeSystem getTypeSystem();
 }
 
 // End FrameworkConfig.java

http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/0fd4e3d9/core/src/main/java/net/hydromatic/optiq/tools/Frameworks.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/net/hydromatic/optiq/tools/Frameworks.java b/core/src/main/java/net/hydromatic/optiq/tools/Frameworks.java
index d08c29c..62b6f3f 100644
--- a/core/src/main/java/net/hydromatic/optiq/tools/Frameworks.java
+++ b/core/src/main/java/net/hydromatic/optiq/tools/Frameworks.java
@@ -18,6 +18,7 @@ package net.hydromatic.optiq.tools;
 
 import net.hydromatic.optiq.SchemaPlus;
 import net.hydromatic.optiq.config.Lex;
+import net.hydromatic.optiq.config.OptiqConnectionProperty;
 import net.hydromatic.optiq.jdbc.OptiqConnection;
 import net.hydromatic.optiq.jdbc.OptiqSchema;
 import net.hydromatic.optiq.prepare.OptiqPrepareImpl;
@@ -29,6 +30,7 @@ import org.eigenbase.relopt.RelOptCluster;
 import org.eigenbase.relopt.RelOptCostFactory;
 import org.eigenbase.relopt.RelOptSchema;
 import org.eigenbase.relopt.RelTraitDef;
+import org.eigenbase.reltype.RelDataTypeSystem;
 import org.eigenbase.sql.SqlOperatorTable;
 import org.eigenbase.sql.fun.SqlStdOperatorTable;
 import org.eigenbase.sql.parser.SqlParserImplFactory;
@@ -42,6 +44,7 @@ import com.google.common.collect.ImmutableList;
 import java.sql.Connection;
 import java.sql.DriverManager;
 import java.util.List;
+import java.util.Properties;
 
 /**
  * Tools for invoking Optiq functionality without initializing a container /
@@ -134,8 +137,13 @@ public class Frameworks {
   public static <R> R withPrepare(PrepareAction<R> action) {
     try {
       Class.forName("net.hydromatic.optiq.jdbc.Driver");
+      final Properties info = new Properties();
+      if (action.config.getTypeSystem() != RelDataTypeSystem.DEFAULT) {
+        info.setProperty(OptiqConnectionProperty.TYPE_SYSTEM.camelName(),
+            action.config.getTypeSystem().getClass().getName());
+      }
       Connection connection =
-          DriverManager.getConnection("jdbc:optiq:");
+          DriverManager.getConnection("jdbc:optiq:", info);
       OptiqConnection optiqConnection =
           connection.unwrap(OptiqConnection.class);
       final OptiqServerStatement statement =
@@ -175,12 +183,14 @@ public class Frameworks {
     private SchemaPlus defaultSchema;
     private RelOptCostFactory costFactory;
     private SqlParserImplFactory parserFactory = SqlParserImpl.FACTORY;
+    private RelDataTypeSystem typeSystem = RelDataTypeSystem.DEFAULT;
 
     private ConfigBuilder() {}
 
     public FrameworkConfig build() {
       return new StdFrameworkConfig(context, convertletTable, operatorTable,
-          programs, traitDefs, lex, defaultSchema, costFactory, parserFactory);
+          programs, traitDefs, lex, defaultSchema, costFactory, parserFactory,
+          typeSystem);
     }
 
     public ConfigBuilder context(Context c) {
@@ -250,6 +260,11 @@ public class Frameworks {
       this.parserFactory = Preconditions.checkNotNull(parserFactory);
       return this;
     }
+
+    public ConfigBuilder typeSystem(RelDataTypeSystem typeSystem) {
+      this.typeSystem = Preconditions.checkNotNull(typeSystem);
+      return this;
+    }
   }
 
   /**
@@ -266,6 +281,7 @@ public class Frameworks {
     private final SchemaPlus defaultSchema;
     private final RelOptCostFactory costFactory;
     private final SqlParserImplFactory parserFactory;
+    private final RelDataTypeSystem typeSystem;
 
     public StdFrameworkConfig(Context context,
         SqlRexConvertletTable convertletTable,
@@ -275,7 +291,8 @@ public class Frameworks {
         Lex lex,
         SchemaPlus defaultSchema,
         RelOptCostFactory costFactory,
-        SqlParserImplFactory parserFactory) {
+        SqlParserImplFactory parserFactory,
+        RelDataTypeSystem typeSystem) {
       this.context = context;
       this.convertletTable = convertletTable;
       this.operatorTable = operatorTable;
@@ -285,6 +302,7 @@ public class Frameworks {
       this.defaultSchema = defaultSchema;
       this.costFactory = costFactory;
       this.parserFactory = parserFactory;
+      this.typeSystem = typeSystem;
     }
 
     public Lex getLex() {
@@ -322,6 +340,10 @@ public class Frameworks {
     public SqlOperatorTable getOperatorTable() {
       return operatorTable;
     }
+
+    public RelDataTypeSystem getTypeSystem() {
+      return typeSystem;
+    }
   }
 }
 

http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/0fd4e3d9/core/src/main/java/org/eigenbase/rel/rules/ReduceDecimalsRule.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/eigenbase/rel/rules/ReduceDecimalsRule.java b/core/src/main/java/org/eigenbase/rel/rules/ReduceDecimalsRule.java
index 1a9325d..d89561d 100644
--- a/core/src/main/java/org/eigenbase/rel/rules/ReduceDecimalsRule.java
+++ b/core/src/main/java/org/eigenbase/rel/rules/ReduceDecimalsRule.java
@@ -336,7 +336,9 @@ public class ReduceDecimalsRule extends RelOptRule {
      * @return 10^scale as an exact numeric value
      */
     protected RexNode makeScaleFactor(int scale) {
-      assert (scale > 0) && (scale < SqlTypeName.MAX_NUMERIC_PRECISION);
+      assert scale > 0;
+      assert scale
+          < builder.getTypeFactory().getTypeSystem().getMaxNumericPrecision();
       return makeExactLiteral(powerOfTen(scale));
     }
 
@@ -364,7 +366,9 @@ public class ReduceDecimalsRule extends RelOptRule {
      * @return 10^scale / 2 as an exact numeric value
      */
     protected RexNode makeRoundFactor(int scale) {
-      assert (scale > 0) && (scale < SqlTypeName.MAX_NUMERIC_PRECISION);
+      assert scale > 0;
+      assert scale
+          < builder.getTypeFactory().getTypeSystem().getMaxNumericPrecision();
       return makeExactLiteral(powerOfTen(scale) / 2);
     }
 
@@ -372,7 +376,9 @@ public class ReduceDecimalsRule extends RelOptRule {
      * Calculates a power of ten, as a long value
      */
     protected long powerOfTen(int scale) {
-      assert (scale >= 0) && (scale < SqlTypeName.MAX_NUMERIC_PRECISION);
+      assert scale >= 0;
+      assert scale
+          < builder.getTypeFactory().getTypeSystem().getMaxNumericPrecision();
       return BigInteger.TEN.pow(scale).longValue();
     }
 
@@ -400,7 +406,9 @@ public class ReduceDecimalsRule extends RelOptRule {
      * @return value * 10^scale as an exact numeric value
      */
     protected RexNode scaleUp(RexNode value, int scale) {
-      assert (scale >= 0) && (scale < SqlTypeName.MAX_NUMERIC_PRECISION);
+      assert scale >= 0;
+      assert scale
+          < builder.getTypeFactory().getTypeSystem().getMaxNumericPrecision();
       if (scale == 0) {
         return value;
       }
@@ -422,8 +430,9 @@ public class ReduceDecimalsRule extends RelOptRule {
      * exact numeric value
      */
     protected RexNode scaleDown(RexNode value, int scale) {
-      int maxPrecision = SqlTypeName.MAX_NUMERIC_PRECISION;
-      assert (scale >= 0) && (scale <= maxPrecision);
+      final int maxPrecision =
+          builder.getTypeFactory().getTypeSystem().getMaxNumericPrecision();
+      assert scale >= 0 && scale <= maxPrecision;
       if (scale == 0) {
         return value;
       }
@@ -462,12 +471,13 @@ public class ReduceDecimalsRule extends RelOptRule {
      * double precision arithmetic.
      *
      * @param value the integer representation of a decimal
-     * @param scale a value from zero to {@link
-     *              SqlTypeName#MAX_NUMERIC_PRECISION MAX_NUMERIC_PRECISION}
+     * @param scale a value from zero to max precision
      * @return value/10^scale as a double precision value
      */
     protected RexNode scaleDownDouble(RexNode value, int scale) {
-      assert (scale >= 0) && (scale <= SqlTypeName.MAX_NUMERIC_PRECISION);
+      assert scale >= 0;
+      assert scale
+          <= builder.getTypeFactory().getTypeSystem().getMaxNumericPrecision();
       RexNode cast = ensureType(real8, value);
       if (scale == 0) {
         return cast;
@@ -495,8 +505,10 @@ public class ReduceDecimalsRule extends RelOptRule {
      * corresponding to the input value
      */
     protected RexNode ensureScale(RexNode value, int scale, int required) {
-      int maxPrecision = SqlTypeName.MAX_NUMERIC_PRECISION;
-      assert (scale <= maxPrecision) && (required <= maxPrecision);
+      final RelDataTypeSystem typeSystem =
+          builder.getTypeFactory().getTypeSystem();
+      final int maxPrecision = typeSystem.getMaxNumericPrecision();
+      assert scale <= maxPrecision && required <= maxPrecision;
       assert required >= scale;
       if (scale == required) {
         return value;
@@ -509,7 +521,7 @@ public class ReduceDecimalsRule extends RelOptRule {
       }
 
       // TODO: make a validator exception for this
-      if (scaleDiff >= SqlTypeName.MAX_NUMERIC_PRECISION) {
+      if (scaleDiff >= maxPrecision) {
         throw Util.needToImplement(
             "Source type with scale " + scale
             + " cannot be converted to target type with scale "
@@ -1002,11 +1014,13 @@ public class ReduceDecimalsRule extends RelOptRule {
       RexNode decValue = call.operands.get(0);
       int scale = decValue.getType().getScale();
       RexNode value = decodeValue(decValue);
+      final RelDataTypeSystem typeSystem =
+          builder.getTypeFactory().getTypeSystem();
 
       RexNode rewrite;
       if (scale == 0) {
         rewrite = decValue;
-      } else if (scale == SqlTypeName.MAX_NUMERIC_PRECISION) {
+      } else if (scale == typeSystem.getMaxNumericPrecision()) {
         rewrite =
             makeCase(
                 makeIsNegative(value),
@@ -1049,11 +1063,13 @@ public class ReduceDecimalsRule extends RelOptRule {
       RexNode decValue = call.operands.get(0);
       int scale = decValue.getType().getScale();
       RexNode value = decodeValue(decValue);
+      final RelDataTypeSystem typeSystem =
+          builder.getTypeFactory().getTypeSystem();
 
       RexNode rewrite;
       if (scale == 0) {
         rewrite = decValue;
-      } else if (scale == SqlTypeName.MAX_NUMERIC_PRECISION) {
+      } else if (scale == typeSystem.getMaxNumericPrecision()) {
         rewrite =
             makeCase(
                 makeIsPositive(value),

http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/0fd4e3d9/core/src/main/java/org/eigenbase/reltype/RelDataTypeFactory.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/eigenbase/reltype/RelDataTypeFactory.java b/core/src/main/java/org/eigenbase/reltype/RelDataTypeFactory.java
index ab26365..0d9fd06 100644
--- a/core/src/main/java/org/eigenbase/reltype/RelDataTypeFactory.java
+++ b/core/src/main/java/org/eigenbase/reltype/RelDataTypeFactory.java
@@ -40,6 +40,11 @@ public interface RelDataTypeFactory {
   //~ Methods ----------------------------------------------------------------
 
   /**
+   * Returns the type system.
+   */
+  RelDataTypeSystem getTypeSystem();
+
+  /**
    * Creates a type which corresponds to a Java class.
    *
    * @param clazz the Java class used to define the type

http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/0fd4e3d9/core/src/main/java/org/eigenbase/reltype/RelDataTypeFactoryImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/eigenbase/reltype/RelDataTypeFactoryImpl.java b/core/src/main/java/org/eigenbase/reltype/RelDataTypeFactoryImpl.java
index db9ceda..80d066c 100644
--- a/core/src/main/java/org/eigenbase/reltype/RelDataTypeFactoryImpl.java
+++ b/core/src/main/java/org/eigenbase/reltype/RelDataTypeFactoryImpl.java
@@ -88,13 +88,30 @@ public abstract class RelDataTypeFactoryImpl implements RelDataTypeFactory {
           .put(Timestamp.class, SqlTypeFamily.TIMESTAMP)
           .build();
 
+  protected final RelDataTypeSystem typeSystem;
+
   //~ Constructors -----------------------------------------------------------
 
+  /** Creates a type factory with default type system.
+   *
+   * @deprecated Will be removed after optiq-0.9.1.
+   */
   protected RelDataTypeFactoryImpl() {
+    this(RelDataTypeSystem.DEFAULT);
+    Bug.upgrade("optiq-0.9.1");
+  }
+
+  /** Creates a type factory. */
+  protected RelDataTypeFactoryImpl(RelDataTypeSystem typeSystem) {
+    this.typeSystem = Preconditions.checkNotNull(typeSystem);
   }
 
   //~ Methods ----------------------------------------------------------------
 
+  public RelDataTypeSystem getTypeSystem() {
+    return typeSystem;
+  }
+
   // implement RelDataTypeFactory
   public RelDataType createJavaType(Class clazz) {
     final JavaType javaType =
@@ -450,12 +467,12 @@ public abstract class RelDataTypeFactoryImpl implements RelDataTypeFactory {
         int s2 = type2.getScale();
 
         int scale = s1 + s2;
-        scale = Math.min(scale, SqlTypeName.MAX_NUMERIC_SCALE);
+        scale = Math.min(scale, typeSystem.getMaxNumericScale());
         int precision = p1 + p2;
         precision =
             Math.min(
                 precision,
-                SqlTypeName.MAX_NUMERIC_PRECISION);
+                typeSystem.getMaxNumericPrecision());
 
         RelDataType ret;
         ret =
@@ -511,20 +528,21 @@ public abstract class RelDataTypeFactoryImpl implements RelDataTypeFactory {
         int s1 = type1.getScale();
         int s2 = type2.getScale();
 
+        final int maxNumericPrecision = typeSystem.getMaxNumericPrecision();
         int dout =
             Math.min(
                 p1 - s1 + s2,
-                SqlTypeName.MAX_NUMERIC_PRECISION);
+                maxNumericPrecision);
 
         int scale = Math.max(6, s1 + p2 + 1);
         scale =
             Math.min(
                 scale,
-                SqlTypeName.MAX_NUMERIC_PRECISION - dout);
-        scale = Math.min(scale, SqlTypeName.MAX_NUMERIC_SCALE);
+                maxNumericPrecision - dout);
+        scale = Math.min(scale, getTypeSystem().getMaxNumericScale());
 
         int precision = dout + scale;
-        assert precision <= SqlTypeName.MAX_NUMERIC_PRECISION;
+        assert precision <= maxNumericPrecision;
         assert precision > 0;
 
         RelDataType ret;

http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/0fd4e3d9/core/src/main/java/org/eigenbase/reltype/RelDataTypeSystem.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/eigenbase/reltype/RelDataTypeSystem.java b/core/src/main/java/org/eigenbase/reltype/RelDataTypeSystem.java
new file mode 100644
index 0000000..e72722f
--- /dev/null
+++ b/core/src/main/java/org/eigenbase/reltype/RelDataTypeSystem.java
@@ -0,0 +1,60 @@
+/*
+ * 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.eigenbase.reltype;
+
+import org.eigenbase.sql.type.SqlTypeName;
+
+/**
+ * Type system.
+ *
+ * <p>Provides behaviors concerning type limits and behaviors. For example,
+ * in the default system, a DECIMAL can have maximum precision 19, but Hive
+ * overrides to 38.
+ *
+ * <p>The default implementation is {@link #DEFAULT}.
+ */
+public interface RelDataTypeSystem {
+  /** Default type system. */
+  RelDataTypeSystem DEFAULT = new RelDataTypeSystemImpl() { };
+
+  /** Returns the maximum scale of a given type. */
+  int getMaxScale(SqlTypeName typeName);
+
+  /**
+   * Returns default precision for this type if supported, otherwise -1 if
+   * precision is either unsupported or must be specified explicitly.
+   *
+   * @return Default precision
+   */
+  int getDefaultPrecision(SqlTypeName typeName);
+
+  /**
+   * Returns the maximum precision (or length) allowed for this type, or -1 if
+   * precision/length are not applicable for this type.
+   *
+   * @return Maximum allowed precision
+   */
+  int getMaxPrecision(SqlTypeName typeName);
+
+  /** Returns the maximum scale of a NUMERIC or DECIMAL type. */
+  int getMaxNumericScale();
+
+  /** Returns the maximum precision of a NUMERIC or DECIMAL type. */
+  int getMaxNumericPrecision();
+}
+
+// End RelDataTypeFactory.java

http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/0fd4e3d9/core/src/main/java/org/eigenbase/reltype/RelDataTypeSystemImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/eigenbase/reltype/RelDataTypeSystemImpl.java b/core/src/main/java/org/eigenbase/reltype/RelDataTypeSystemImpl.java
new file mode 100644
index 0000000..b158974
--- /dev/null
+++ b/core/src/main/java/org/eigenbase/reltype/RelDataTypeSystemImpl.java
@@ -0,0 +1,98 @@
+/*
+ * 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.eigenbase.reltype;
+
+import org.eigenbase.sql.type.SqlTypeName;
+
+/** Default implementation of {@link org.eigenbase.reltype.RelDataTypeSystem},
+ * providing parameters from the SQL standard.
+ *
+ * <p>To implement other type systems, create a derived class and override
+ * values as needed.
+ *
+ * <table border='1'>
+ *   <tr><td>Parameter</td>        <td>Value</td></tr>
+ *   <tr><td>MAX_NUMERIC_SCALE</td><td>19</td></tr>
+ * </table>
+ */
+public abstract class RelDataTypeSystemImpl implements RelDataTypeSystem {
+  public int getMaxScale(SqlTypeName typeName) {
+    switch (typeName) {
+    case DECIMAL:
+      return getMaxNumericScale();
+    case INTERVAL_DAY_TIME:
+    case INTERVAL_YEAR_MONTH:
+      return SqlTypeName.MAX_INTERVAL_FRACTIONAL_SECOND_PRECISION;
+    default:
+      return -1;
+    }
+  }
+
+  public int getDefaultPrecision(SqlTypeName typeName) {
+    switch (typeName) {
+    case CHAR:
+    case BINARY:
+    case VARCHAR:
+    case VARBINARY:
+      return 1;
+    case TIME:
+      return 0;
+    case TIMESTAMP:
+      // TODO jvs 26-July-2004:  should be 6 for microseconds,
+      // but we can't support that yet
+      return 0;
+    case DECIMAL:
+      return getMaxNumericPrecision();
+    case INTERVAL_DAY_TIME:
+    case INTERVAL_YEAR_MONTH:
+      return SqlTypeName.DEFAULT_INTERVAL_START_PRECISION;
+    default:
+      return -1;
+    }
+  }
+
+  public int getMaxPrecision(SqlTypeName typeName) {
+    switch (typeName) {
+    case DECIMAL:
+      return getMaxNumericPrecision();
+    case VARCHAR:
+    case CHAR:
+      return 65536;
+    case VARBINARY:
+    case BINARY:
+      return 65536;
+    case TIME:
+    case TIMESTAMP:
+      return SqlTypeName.MAX_DATETIME_PRECISION;
+    case INTERVAL_DAY_TIME:
+    case INTERVAL_YEAR_MONTH:
+      return SqlTypeName.MAX_INTERVAL_START_PRECISION;
+    default:
+      return -1;
+    }
+  }
+
+  public int getMaxNumericScale() {
+    return 19;
+  }
+
+  public int getMaxNumericPrecision() {
+    return 19;
+  }
+}
+
+// End RelDataTypeSystemImpl.java

http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/0fd4e3d9/core/src/main/java/org/eigenbase/rex/RexBuilder.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/eigenbase/rex/RexBuilder.java b/core/src/main/java/org/eigenbase/rex/RexBuilder.java
index d393a16..251af45 100644
--- a/core/src/main/java/org/eigenbase/rex/RexBuilder.java
+++ b/core/src/main/java/org/eigenbase/rex/RexBuilder.java
@@ -529,7 +529,8 @@ public class RexBuilder {
     if (endUnit == TimeUnit.SECOND) {
       scale = Math.min(
           intervalType.getIntervalQualifier()
-              .getFractionalSecondPrecision(), 3);
+              .getFractionalSecondPrecision(typeFactory.getTypeSystem()),
+          3);
     }
     BigDecimal multiplier = BigDecimal.valueOf(endUnit.multiplier)
         .divide(BigDecimal.TEN.pow(scale));
@@ -560,7 +561,8 @@ public class RexBuilder {
     if (endUnit == TimeUnit.SECOND) {
       scale = Math.min(
           intervalType.getIntervalQualifier()
-              .getFractionalSecondPrecision(), 3);
+              .getFractionalSecondPrecision(typeFactory.getTypeSystem()),
+          3);
     }
     BigDecimal multiplier = BigDecimal.valueOf(endUnit.multiplier)
         .divide(BigDecimal.TEN.pow(scale));
@@ -798,7 +800,8 @@ public class RexBuilder {
     RelDataType relType;
     int scale = bd.scale();
     long l = bd.unscaledValue().longValue();
-    assert (scale >= 0) && (scale <= SqlTypeName.MAX_NUMERIC_SCALE);
+    assert scale >= 0;
+    assert scale <= typeFactory.getTypeSystem().getMaxNumericScale() : scale;
     assert BigDecimal.valueOf(l, scale).equals(bd);
     if (scale == 0) {
       if ((l >= Integer.MIN_VALUE) && (l <= Integer.MAX_VALUE)) {

http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/0fd4e3d9/core/src/main/java/org/eigenbase/rex/RexExecutorImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/eigenbase/rex/RexExecutorImpl.java b/core/src/main/java/org/eigenbase/rex/RexExecutorImpl.java
index b187344..da34a55 100644
--- a/core/src/main/java/org/eigenbase/rex/RexExecutorImpl.java
+++ b/core/src/main/java/org/eigenbase/rex/RexExecutorImpl.java
@@ -61,7 +61,8 @@ public class RexExecutorImpl implements RelOptPlanner.Executor {
       programBuilder.addProject(
           node, "c" + programBuilder.getProjectList().size());
     }
-    final JavaTypeFactoryImpl javaTypeFactory = new JavaTypeFactoryImpl();
+    final JavaTypeFactoryImpl javaTypeFactory =
+        new JavaTypeFactoryImpl(rexBuilder.getTypeFactory().getTypeSystem());
     final BlockBuilder blockBuilder = new BlockBuilder();
     final ParameterExpression root0_ =
         Expressions.parameter(Object.class, "root0");

http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/0fd4e3d9/core/src/main/java/org/eigenbase/sql/SqlIntervalQualifier.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/eigenbase/sql/SqlIntervalQualifier.java b/core/src/main/java/org/eigenbase/sql/SqlIntervalQualifier.java
index 2d3eb7c..406ed57 100644
--- a/core/src/main/java/org/eigenbase/sql/SqlIntervalQualifier.java
+++ b/core/src/main/java/org/eigenbase/sql/SqlIntervalQualifier.java
@@ -20,6 +20,8 @@ import java.math.BigDecimal;
 import java.util.*;
 import java.util.regex.*;
 
+import org.eigenbase.reltype.RelDataType;
+import org.eigenbase.reltype.RelDataTypeSystem;
 import org.eigenbase.sql.parser.*;
 import org.eigenbase.sql.type.*;
 import org.eigenbase.sql.util.*;
@@ -29,6 +31,7 @@ import org.eigenbase.util14.DateTimeUtil;
 
 import net.hydromatic.optiq.runtime.SqlFunctions;
 
+import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableMap;
 
 import static org.eigenbase.util.Static.RESOURCE;
@@ -83,7 +86,6 @@ import static org.eigenbase.util.Static.RESOURCE;
 public class SqlIntervalQualifier extends SqlNode {
   //~ Static fields/initializers ---------------------------------------------
 
-  private static final int USE_DEFAULT_PRECISION = -1;
   private static final BigDecimal ZERO = BigDecimal.ZERO;
   private static final BigDecimal THOUSAND = BigDecimal.valueOf(1000);
   private static final BigDecimal INT_MAX_VALUE_PLUS_ONE =
@@ -204,9 +206,6 @@ public class SqlIntervalQualifier extends SqlNode {
   private final TimeUnitRange timeUnitRange;
   private final int fractionalSecondPrecision;
 
-  private final boolean useDefaultStartPrecision;
-  private final boolean useDefaultFractionalSecondPrecision;
-
   //~ Constructors -----------------------------------------------------------
 
   public SqlIntervalQualifier(
@@ -216,39 +215,10 @@ public class SqlIntervalQualifier extends SqlNode {
       int fractionalSecondPrecision,
       SqlParserPos pos) {
     super(pos);
-    assert null != startUnit;
-
-    this.timeUnitRange = TimeUnitRange.of(startUnit, endUnit);
-
-    // if unspecified, start precision = 2
-    if (startPrecision == USE_DEFAULT_PRECISION) {
-      useDefaultStartPrecision = true;
-      if (this.isYearMonth()) {
-        this.startPrecision =
-            SqlTypeName.INTERVAL_YEAR_MONTH.getDefaultPrecision();
-      } else {
-        this.startPrecision =
-            SqlTypeName.INTERVAL_DAY_TIME.getDefaultPrecision();
-      }
-    } else {
-      useDefaultStartPrecision = false;
-      this.startPrecision = startPrecision;
-    }
-
-    // unspecified fractional second precision = 6
-    if (fractionalSecondPrecision == USE_DEFAULT_PRECISION) {
-      useDefaultFractionalSecondPrecision = true;
-      if (this.isYearMonth()) {
-        this.fractionalSecondPrecision =
-            SqlTypeName.INTERVAL_YEAR_MONTH.getDefaultScale();
-      } else {
-        this.fractionalSecondPrecision =
-            SqlTypeName.INTERVAL_DAY_TIME.getDefaultScale();
-      }
-    } else {
-      useDefaultFractionalSecondPrecision = false;
-      this.fractionalSecondPrecision = fractionalSecondPrecision;
-    }
+    this.timeUnitRange =
+        TimeUnitRange.of(Preconditions.checkNotNull(startUnit), endUnit);
+    this.startPrecision = startPrecision;
+    this.fractionalSecondPrecision = fractionalSecondPrecision;
   }
 
   public SqlIntervalQualifier(
@@ -257,14 +227,20 @@ public class SqlIntervalQualifier extends SqlNode {
       SqlParserPos pos) {
     this(
         startUnit,
-        USE_DEFAULT_PRECISION,
+        RelDataType.PRECISION_NOT_SPECIFIED,
         endUnit,
-        USE_DEFAULT_PRECISION,
+        RelDataType.PRECISION_NOT_SPECIFIED,
         pos);
   }
 
   //~ Methods ----------------------------------------------------------------
 
+  public SqlTypeName typeName() {
+    return isYearMonth()
+        ? SqlTypeName.INTERVAL_YEAR_MONTH
+        : SqlTypeName.INTERVAL_DAY_TIME;
+  }
+
   public SqlFunctions.TimeUnitRange foo() {
     return SqlFunctions.TimeUnitRange.valueOf(timeUnitRange.name());
   }
@@ -289,33 +265,34 @@ public class SqlIntervalQualifier extends SqlNode {
     return true;
   }
 
-  public static int getDefaultPrecisionId() {
-    return USE_DEFAULT_PRECISION;
+  public int getStartPrecision(RelDataTypeSystem typeSystem) {
+    if (startPrecision == RelDataType.PRECISION_NOT_SPECIFIED) {
+      return typeSystem.getDefaultPrecision(typeName());
+    } else {
+      return startPrecision;
+    }
   }
 
-  public int getStartPrecision() {
+  public int getStartPrecisionPreservingDefault() {
     return startPrecision;
   }
 
-  public int getStartPrecisionPreservingDefault() {
-    if (useDefaultStartPrecision) {
-      return USE_DEFAULT_PRECISION;
-    } else {
-      return startPrecision;
-    }
+  private boolean useDefaultStartPrecision() {
+    return startPrecision == RelDataType.PRECISION_NOT_SPECIFIED;
   }
 
   public static int combineStartPrecisionPreservingDefault(
+      RelDataTypeSystem typeSystem,
       SqlIntervalQualifier qual1,
       SqlIntervalQualifier qual2) {
-    if (qual1.getStartPrecision()
-        > qual2.getStartPrecision()) {
+    final int start1 = qual1.getStartPrecision(typeSystem);
+    final int start2 = qual2.getStartPrecision(typeSystem);
+    if (start1 > start2) {
       // qual1 is more precise, but if it has the default indicator
       // set, we need to return that indicator so result will also
       // use default
       return qual1.getStartPrecisionPreservingDefault();
-    } else if (qual1.getStartPrecision()
-        < qual2.getStartPrecision()) {
+    } else if (start1 < start2) {
       // qual2 is more precise, but if it has the default indicator
       // set, we need to return that indicator so result will also
       // use default
@@ -323,39 +300,47 @@ public class SqlIntervalQualifier extends SqlNode {
     } else {
       // they are equal.  return default if both are default,
       // otherwise return exact precision
-      if (qual1.useDefaultStartPrecision
-          && qual2.useDefaultStartPrecision) {
+      if (qual1.useDefaultStartPrecision()
+          && qual2.useDefaultStartPrecision()) {
         return qual1.getStartPrecisionPreservingDefault();
       } else {
-        return qual1.getStartPrecision();
+        return start1;
       }
     }
   }
 
-  public int getFractionalSecondPrecision() {
-    return fractionalSecondPrecision;
+  public int getFractionalSecondPrecision(RelDataTypeSystem typeSystem) {
+    if (fractionalSecondPrecision == RelDataType.PRECISION_NOT_SPECIFIED) {
+      return typeName().getDefaultScale();
+    } else {
+      return fractionalSecondPrecision;
+    }
   }
 
   public int getFractionalSecondPrecisionPreservingDefault() {
-    if (useDefaultFractionalSecondPrecision) {
-      return USE_DEFAULT_PRECISION;
+    if (useDefaultFractionalSecondPrecision()) {
+      return RelDataType.PRECISION_NOT_SPECIFIED;
     } else {
       return startPrecision;
     }
   }
 
+  private boolean useDefaultFractionalSecondPrecision() {
+    return fractionalSecondPrecision == RelDataType.PRECISION_NOT_SPECIFIED;
+  }
+
   public static int combineFractionalSecondPrecisionPreservingDefault(
+      RelDataTypeSystem typeSystem,
       SqlIntervalQualifier qual1,
       SqlIntervalQualifier qual2) {
-    if (qual1.getFractionalSecondPrecision()
-        > qual2.getFractionalSecondPrecision()) {
+    final int p1 = qual1.getFractionalSecondPrecision(typeSystem);
+    final int p2 = qual2.getFractionalSecondPrecision(typeSystem);
+    if (p1 > p2) {
       // qual1 is more precise, but if it has the default indicator
       // set, we need to return that indicator so result will also
       // use default
       return qual1.getFractionalSecondPrecisionPreservingDefault();
-    } else if (
-        qual1.getFractionalSecondPrecision()
-            < qual2.getFractionalSecondPrecision()) {
+    } else if (p1 < p2) {
       // qual2 is more precise, but if it has the default indicator
       // set, we need to return that indicator so result will also
       // use default
@@ -363,11 +348,11 @@ public class SqlIntervalQualifier extends SqlNode {
     } else {
       // they are equal.  return default if both are default,
       // otherwise return exact precision
-      if (qual1.useDefaultFractionalSecondPrecision
-          && qual2.useDefaultFractionalSecondPrecision) {
+      if (qual1.useDefaultFractionalSecondPrecision()
+          && qual2.useDefaultFractionalSecondPrecision()) {
         return qual1.getFractionalSecondPrecisionPreservingDefault();
       } else {
-        return qual1.getFractionalSecondPrecision();
+        return p1;
       }
     }
   }
@@ -381,29 +366,30 @@ public class SqlIntervalQualifier extends SqlNode {
   }
 
   public SqlNode clone(SqlParserPos pos) {
-    return new SqlIntervalQualifier(
-        timeUnitRange.startUnit,
-        useDefaultStartPrecision ? USE_DEFAULT_PRECISION
-            : startPrecision,
-        timeUnitRange.endUnit,
-        useDefaultFractionalSecondPrecision ? USE_DEFAULT_PRECISION
-            : fractionalSecondPrecision,
-        pos);
+    return new SqlIntervalQualifier(timeUnitRange.startUnit, startPrecision,
+        timeUnitRange.endUnit, fractionalSecondPrecision, pos);
   }
 
   public void unparse(
       SqlWriter writer,
       int leftPrec,
       int rightPrec) {
+    unparse(RelDataTypeSystem.DEFAULT, writer);
+  }
+
+  public void unparse(RelDataTypeSystem typeSystem, SqlWriter writer) {
     final String start = timeUnitRange.startUnit.name();
+    final int fractionalSecondPrecision =
+        getFractionalSecondPrecision(typeSystem);
+    final int startPrecision = getStartPrecision(typeSystem);
     if (timeUnitRange.startUnit == TimeUnit.SECOND) {
-      if (!useDefaultFractionalSecondPrecision) {
+      if (!useDefaultFractionalSecondPrecision()) {
         final SqlWriter.Frame frame = writer.startFunCall(start);
         writer.print(startPrecision);
         writer.sep(",", true);
-        writer.print(fractionalSecondPrecision);
+        writer.print(getFractionalSecondPrecision(typeSystem));
         writer.endList(frame);
-      } else if (!useDefaultStartPrecision) {
+      } else if (!useDefaultStartPrecision()) {
         final SqlWriter.Frame frame = writer.startFunCall(start);
         writer.print(startPrecision);
         writer.endList(frame);
@@ -411,7 +397,7 @@ public class SqlIntervalQualifier extends SqlNode {
         writer.keyword(start);
       }
     } else {
-      if (!useDefaultStartPrecision) {
+      if (!useDefaultStartPrecision()) {
         final SqlWriter.Frame frame = writer.startFunCall(start);
         writer.print(startPrecision);
         writer.endList(frame);
@@ -423,7 +409,7 @@ public class SqlIntervalQualifier extends SqlNode {
         writer.keyword("TO");
         final String end = timeUnitRange.endUnit.name();
         if ((TimeUnit.SECOND == timeUnitRange.endUnit)
-            && (!useDefaultFractionalSecondPrecision)) {
+            && (!useDefaultFractionalSecondPrecision())) {
           final SqlWriter.Frame frame = writer.startFunCall(end);
           writer.print(fractionalSecondPrecision);
           writer.endList(frame);
@@ -474,21 +460,23 @@ public class SqlIntervalQualifier extends SqlNode {
     return unsignedValue;
   }
 
-  private boolean isLeadFieldInRange(BigDecimal value, TimeUnit unit) {
+  private boolean isLeadFieldInRange(RelDataTypeSystem typeSystem,
+      BigDecimal value, TimeUnit unit) {
     // we should never get handed a negative field value
     assert value.compareTo(ZERO) >= 0;
 
     // Leading fields are only restricted by startPrecision.
+    final int startPrecision = getStartPrecision(typeSystem);
     return startPrecision < POWERS10.length
         ? value.compareTo(POWERS10[startPrecision]) < 0
         : value.compareTo(INT_MAX_VALUE_PLUS_ONE) < 0;
   }
 
-  private void checkLeadFieldInRange(
-      int sign, BigDecimal value, TimeUnit unit, SqlParserPos pos) {
-    if (!isLeadFieldInRange(value, unit)) {
+  private void checkLeadFieldInRange(RelDataTypeSystem typeSystem, int sign,
+      BigDecimal value, TimeUnit unit, SqlParserPos pos) {
+    if (!isLeadFieldInRange(typeSystem, value, unit)) {
       throw fieldExceedsPrecisionException(
-          pos, sign, value, unit, startPrecision);
+          pos, sign, value, unit, getStartPrecision(typeSystem));
     }
   }
 
@@ -581,7 +569,7 @@ public class SqlIntervalQualifier extends SqlNode {
    * @throws EigenbaseContextException if the interval value is illegal.
    */
   private int[] evaluateIntervalLiteralAsYear(
-      int sign,
+      RelDataTypeSystem typeSystem, int sign,
       String value,
       String originalValue,
       SqlParserPos pos) {
@@ -600,7 +588,7 @@ public class SqlIntervalQualifier extends SqlNode {
       }
 
       // Validate individual fields
-      checkLeadFieldInRange(sign, year, TimeUnit.YEAR, pos);
+      checkLeadFieldInRange(typeSystem, sign, year, TimeUnit.YEAR, pos);
 
       // package values up for return
       return fillIntervalValueArray(sign, year, ZERO);
@@ -615,7 +603,7 @@ public class SqlIntervalQualifier extends SqlNode {
    * @throws EigenbaseContextException if the interval value is illegal.
    */
   private int[] evaluateIntervalLiteralAsYearToMonth(
-      int sign,
+      RelDataTypeSystem typeSystem, int sign,
       String value,
       String originalValue,
       SqlParserPos pos) {
@@ -636,7 +624,7 @@ public class SqlIntervalQualifier extends SqlNode {
       }
 
       // Validate individual fields
-      checkLeadFieldInRange(sign, year, TimeUnit.YEAR, pos);
+      checkLeadFieldInRange(typeSystem, sign, year, TimeUnit.YEAR, pos);
       if (!(isSecondaryFieldInRange(month, TimeUnit.MONTH))) {
         throw invalidValueException(pos, originalValue);
       }
@@ -654,7 +642,7 @@ public class SqlIntervalQualifier extends SqlNode {
    * @throws EigenbaseContextException if the interval value is illegal.
    */
   private int[] evaluateIntervalLiteralAsMonth(
-      int sign,
+      RelDataTypeSystem typeSystem, int sign,
       String value,
       String originalValue,
       SqlParserPos pos) {
@@ -673,7 +661,7 @@ public class SqlIntervalQualifier extends SqlNode {
       }
 
       // Validate individual fields
-      checkLeadFieldInRange(sign, month, TimeUnit.MONTH, pos);
+      checkLeadFieldInRange(typeSystem, sign, month, TimeUnit.MONTH, pos);
 
       // package values up for return
       return fillIntervalValueArray(sign, ZERO, month);
@@ -688,7 +676,7 @@ public class SqlIntervalQualifier extends SqlNode {
    * @throws EigenbaseContextException if the interval value is illegal.
    */
   private int[] evaluateIntervalLiteralAsDay(
-      int sign,
+      RelDataTypeSystem typeSystem, int sign,
       String value,
       String originalValue,
       SqlParserPos pos) {
@@ -707,7 +695,7 @@ public class SqlIntervalQualifier extends SqlNode {
       }
 
       // Validate individual fields
-      checkLeadFieldInRange(sign, day, TimeUnit.DAY, pos);
+      checkLeadFieldInRange(typeSystem, sign, day, TimeUnit.DAY, pos);
 
       // package values up for return
       return fillIntervalValueArray(sign, day, ZERO, ZERO, ZERO, ZERO);
@@ -722,7 +710,7 @@ public class SqlIntervalQualifier extends SqlNode {
    * @throws EigenbaseContextException if the interval value is illegal.
    */
   private int[] evaluateIntervalLiteralAsDayToHour(
-      int sign,
+      RelDataTypeSystem typeSystem, int sign,
       String value,
       String originalValue,
       SqlParserPos pos) {
@@ -743,7 +731,7 @@ public class SqlIntervalQualifier extends SqlNode {
       }
 
       // Validate individual fields
-      checkLeadFieldInRange(sign, day, TimeUnit.DAY, pos);
+      checkLeadFieldInRange(typeSystem, sign, day, TimeUnit.DAY, pos);
       if (!(isSecondaryFieldInRange(hour, TimeUnit.HOUR))) {
         throw invalidValueException(pos, originalValue);
       }
@@ -761,7 +749,7 @@ public class SqlIntervalQualifier extends SqlNode {
    * @throws EigenbaseContextException if the interval value is illegal.
    */
   private int[] evaluateIntervalLiteralAsDayToMinute(
-      int sign,
+      RelDataTypeSystem typeSystem, int sign,
       String value,
       String originalValue,
       SqlParserPos pos) {
@@ -784,7 +772,7 @@ public class SqlIntervalQualifier extends SqlNode {
       }
 
       // Validate individual fields
-      checkLeadFieldInRange(sign, day, TimeUnit.DAY, pos);
+      checkLeadFieldInRange(typeSystem, sign, day, TimeUnit.DAY, pos);
       if (!(isSecondaryFieldInRange(hour, TimeUnit.HOUR))
           || !(isSecondaryFieldInRange(minute, TimeUnit.MINUTE))) {
         throw invalidValueException(pos, originalValue);
@@ -803,7 +791,7 @@ public class SqlIntervalQualifier extends SqlNode {
    * @throws EigenbaseContextException if the interval value is illegal.
    */
   private int[] evaluateIntervalLiteralAsDayToSecond(
-      int sign,
+      RelDataTypeSystem typeSystem, int sign,
       String value,
       String originalValue,
       SqlParserPos pos) {
@@ -817,6 +805,8 @@ public class SqlIntervalQualifier extends SqlNode {
     // validate as DAY(startPrecision) TO MINUTE,
     // e.g. 'DD HH:MM:SS' or 'DD HH:MM:SS.SSS'
     // Note: must check two patterns, since fractional second is optional
+    final int fractionalSecondPrecision =
+        getFractionalSecondPrecision(typeSystem);
     String intervalPatternWithFracSec =
         "(\\d+) (\\d{1,2}):(\\d{1,2}):(\\d{1,2})\\.(\\d{1,"
         + fractionalSecondPrecision + "})";
@@ -849,7 +839,7 @@ public class SqlIntervalQualifier extends SqlNode {
       }
 
       // Validate individual fields
-      checkLeadFieldInRange(sign, day, TimeUnit.DAY, pos);
+      checkLeadFieldInRange(typeSystem, sign, day, TimeUnit.DAY, pos);
       if (!(isSecondaryFieldInRange(hour, TimeUnit.HOUR))
           || !(isSecondaryFieldInRange(minute, TimeUnit.MINUTE))
           || !(isSecondaryFieldInRange(second, TimeUnit.SECOND))
@@ -876,7 +866,7 @@ public class SqlIntervalQualifier extends SqlNode {
    * @throws EigenbaseContextException if the interval value is illegal.
    */
   private int[] evaluateIntervalLiteralAsHour(
-      int sign,
+      RelDataTypeSystem typeSystem, int sign,
       String value,
       String originalValue,
       SqlParserPos pos) {
@@ -895,7 +885,7 @@ public class SqlIntervalQualifier extends SqlNode {
       }
 
       // Validate individual fields
-      checkLeadFieldInRange(sign, hour, TimeUnit.HOUR, pos);
+      checkLeadFieldInRange(typeSystem, sign, hour, TimeUnit.HOUR, pos);
 
       // package values up for return
       return fillIntervalValueArray(sign, ZERO, hour, ZERO, ZERO, ZERO);
@@ -911,7 +901,7 @@ public class SqlIntervalQualifier extends SqlNode {
    * @throws EigenbaseContextException if the interval value is illegal.
    */
   private int[] evaluateIntervalLiteralAsHourToMinute(
-      int sign,
+      RelDataTypeSystem typeSystem, int sign,
       String value,
       String originalValue,
       SqlParserPos pos) {
@@ -932,7 +922,7 @@ public class SqlIntervalQualifier extends SqlNode {
       }
 
       // Validate individual fields
-      checkLeadFieldInRange(sign, hour, TimeUnit.HOUR, pos);
+      checkLeadFieldInRange(typeSystem, sign, hour, TimeUnit.HOUR, pos);
       if (!(isSecondaryFieldInRange(minute, TimeUnit.MINUTE))) {
         throw invalidValueException(pos, originalValue);
       }
@@ -951,7 +941,7 @@ public class SqlIntervalQualifier extends SqlNode {
    * @throws EigenbaseContextException if the interval value is illegal.
    */
   private int[] evaluateIntervalLiteralAsHourToSecond(
-      int sign,
+      RelDataTypeSystem typeSystem, int sign,
       String value,
       String originalValue,
       SqlParserPos pos) {
@@ -964,6 +954,8 @@ public class SqlIntervalQualifier extends SqlNode {
     // validate as HOUR(startPrecision) TO SECOND,
     // e.g. 'HH:MM:SS' or 'HH:MM:SS.SSS'
     // Note: must check two patterns, since fractional second is optional
+    final int fractionalSecondPrecision =
+        getFractionalSecondPrecision(typeSystem);
     String intervalPatternWithFracSec =
         "(\\d+):(\\d{1,2}):(\\d{1,2})\\.(\\d{1,"
         + fractionalSecondPrecision + "})";
@@ -995,7 +987,7 @@ public class SqlIntervalQualifier extends SqlNode {
       }
 
       // Validate individual fields
-      checkLeadFieldInRange(sign, hour, TimeUnit.HOUR, pos);
+      checkLeadFieldInRange(typeSystem, sign, hour, TimeUnit.HOUR, pos);
       if (!(isSecondaryFieldInRange(minute, TimeUnit.MINUTE))
           || !(isSecondaryFieldInRange(second, TimeUnit.SECOND))
           || !(isFractionalSecondFieldInRange(secondFrac))) {
@@ -1021,7 +1013,7 @@ public class SqlIntervalQualifier extends SqlNode {
    * @throws EigenbaseContextException if the interval value is illegal.
    */
   private int[] evaluateIntervalLiteralAsMinute(
-      int sign,
+      RelDataTypeSystem typeSystem, int sign,
       String value,
       String originalValue,
       SqlParserPos pos) {
@@ -1040,7 +1032,7 @@ public class SqlIntervalQualifier extends SqlNode {
       }
 
       // Validate individual fields
-      checkLeadFieldInRange(sign, minute, TimeUnit.MINUTE, pos);
+      checkLeadFieldInRange(typeSystem, sign, minute, TimeUnit.MINUTE, pos);
 
       // package values up for return
       return fillIntervalValueArray(sign, ZERO, ZERO, minute, ZERO, ZERO);
@@ -1056,7 +1048,7 @@ public class SqlIntervalQualifier extends SqlNode {
    * @throws EigenbaseContextException if the interval value is illegal.
    */
   private int[] evaluateIntervalLiteralAsMinuteToSecond(
-      int sign,
+      RelDataTypeSystem typeSystem, int sign,
       String value,
       String originalValue,
       SqlParserPos pos) {
@@ -1068,6 +1060,8 @@ public class SqlIntervalQualifier extends SqlNode {
     // validate as MINUTE(startPrecision) TO SECOND,
     // e.g. 'MM:SS' or 'MM:SS.SSS'
     // Note: must check two patterns, since fractional second is optional
+    final int fractionalSecondPrecision =
+        getFractionalSecondPrecision(typeSystem);
     String intervalPatternWithFracSec =
         "(\\d+):(\\d{1,2})\\.(\\d{1," + fractionalSecondPrecision + "})";
     String intervalPatternWithoutFracSec =
@@ -1097,7 +1091,7 @@ public class SqlIntervalQualifier extends SqlNode {
       }
 
       // Validate individual fields
-      checkLeadFieldInRange(sign, minute, TimeUnit.MINUTE, pos);
+      checkLeadFieldInRange(typeSystem, sign, minute, TimeUnit.MINUTE, pos);
       if (!(isSecondaryFieldInRange(second, TimeUnit.SECOND))
           || !(isFractionalSecondFieldInRange(secondFrac))) {
         throw invalidValueException(pos, originalValue);
@@ -1122,6 +1116,7 @@ public class SqlIntervalQualifier extends SqlNode {
    * @throws EigenbaseContextException if the interval value is illegal.
    */
   private int[] evaluateIntervalLiteralAsSecond(
+      RelDataTypeSystem typeSystem,
       int sign,
       String value,
       String originalValue,
@@ -1133,6 +1128,8 @@ public class SqlIntervalQualifier extends SqlNode {
     // validate as SECOND(startPrecision, fractionalSecondPrecision)
     // e.g. 'SS' or 'SS.SSS'
     // Note: must check two patterns, since fractional second is optional
+    final int fractionalSecondPrecision =
+        getFractionalSecondPrecision(typeSystem);
     String intervalPatternWithFracSec =
         "(\\d+)\\.(\\d{1," + fractionalSecondPrecision + "})";
     String intervalPatternWithoutFracSec =
@@ -1161,7 +1158,7 @@ public class SqlIntervalQualifier extends SqlNode {
       }
 
       // Validate individual fields
-      checkLeadFieldInRange(sign, second, TimeUnit.SECOND, pos);
+      checkLeadFieldInRange(typeSystem, sign, second, TimeUnit.SECOND, pos);
       if (!(isFractionalSecondFieldInRange(secondFrac))) {
         throw invalidValueException(pos, originalValue);
       }
@@ -1183,7 +1180,8 @@ public class SqlIntervalQualifier extends SqlNode {
    * @return field values, never null
    * @throws EigenbaseContextException if the interval value is illegal
    */
-  public int[] evaluateIntervalLiteral(String value, SqlParserPos pos) {
+  public int[] evaluateIntervalLiteral(String value, SqlParserPos pos,
+      RelDataTypeSystem typeSystem) {
     // save original value for if we have to throw
     final String value0 = value;
 
@@ -1207,31 +1205,43 @@ public class SqlIntervalQualifier extends SqlNode {
     // well as explicit or implicit precision and range.
     switch (timeUnitRange) {
     case YEAR:
-      return evaluateIntervalLiteralAsYear(sign, value, value0, pos);
+      return evaluateIntervalLiteralAsYear(typeSystem, sign, value, value0,
+          pos);
     case YEAR_TO_MONTH:
-      return evaluateIntervalLiteralAsYearToMonth(sign, value, value0, pos);
+      return evaluateIntervalLiteralAsYearToMonth(typeSystem, sign, value,
+          value0, pos);
     case MONTH:
-      return evaluateIntervalLiteralAsMonth(sign, value, value0, pos);
+      return evaluateIntervalLiteralAsMonth(typeSystem, sign, value, value0,
+          pos);
     case DAY:
-      return evaluateIntervalLiteralAsDay(sign, value, value0, pos);
+      return evaluateIntervalLiteralAsDay(typeSystem, sign, value, value0, pos);
     case DAY_TO_HOUR:
-      return evaluateIntervalLiteralAsDayToHour(sign, value, value0, pos);
+      return evaluateIntervalLiteralAsDayToHour(typeSystem, sign, value, value0,
+          pos);
     case DAY_TO_MINUTE:
-      return evaluateIntervalLiteralAsDayToMinute(sign, value, value0, pos);
+      return evaluateIntervalLiteralAsDayToMinute(typeSystem, sign, value,
+          value0, pos);
     case DAY_TO_SECOND:
-      return evaluateIntervalLiteralAsDayToSecond(sign, value, value0, pos);
+      return evaluateIntervalLiteralAsDayToSecond(typeSystem, sign, value,
+          value0, pos);
     case HOUR:
-      return evaluateIntervalLiteralAsHour(sign, value, value0, pos);
+      return evaluateIntervalLiteralAsHour(typeSystem, sign, value, value0,
+          pos);
     case HOUR_TO_MINUTE:
-      return evaluateIntervalLiteralAsHourToMinute(sign, value, value0, pos);
+      return evaluateIntervalLiteralAsHourToMinute(typeSystem, sign, value,
+          value0, pos);
     case HOUR_TO_SECOND:
-      return evaluateIntervalLiteralAsHourToSecond(sign, value, value0, pos);
+      return evaluateIntervalLiteralAsHourToSecond(typeSystem, sign, value,
+          value0, pos);
     case MINUTE:
-      return evaluateIntervalLiteralAsMinute(sign, value, value0, pos);
+      return evaluateIntervalLiteralAsMinute(typeSystem, sign, value, value0,
+          pos);
     case MINUTE_TO_SECOND:
-      return evaluateIntervalLiteralAsMinuteToSecond(sign, value, value0, pos);
+      return evaluateIntervalLiteralAsMinuteToSecond(typeSystem, sign, value,
+          value0, pos);
     case SECOND:
-      return evaluateIntervalLiteralAsSecond(sign, value, value0, pos);
+      return evaluateIntervalLiteralAsSecond(typeSystem, sign, value, value0,
+          pos);
     default:
       throw invalidValueException(pos, value0);
     }

http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/0fd4e3d9/core/src/main/java/org/eigenbase/sql/SqlUnresolvedFunction.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/eigenbase/sql/SqlUnresolvedFunction.java b/core/src/main/java/org/eigenbase/sql/SqlUnresolvedFunction.java
index a8a5ac6..a607022 100644
--- a/core/src/main/java/org/eigenbase/sql/SqlUnresolvedFunction.java
+++ b/core/src/main/java/org/eigenbase/sql/SqlUnresolvedFunction.java
@@ -19,7 +19,6 @@ package org.eigenbase.sql;
 import java.util.List;
 
 import org.eigenbase.reltype.RelDataType;
-import org.eigenbase.sql.type.BasicSqlType;
 import org.eigenbase.sql.type.SqlOperandTypeChecker;
 import org.eigenbase.sql.type.SqlOperandTypeInference;
 import org.eigenbase.sql.type.SqlReturnTypeInference;
@@ -64,9 +63,8 @@ public class SqlUnresolvedFunction extends SqlFunction {
    * fail.
    */
   @Override
-  public RelDataType inferReturnType(
-      SqlOperatorBinding opBinding) {
-    return new BasicSqlType(SqlTypeName.ANY);
+  public RelDataType inferReturnType(SqlOperatorBinding opBinding) {
+    return opBinding.getTypeFactory().createSqlType(SqlTypeName.ANY);
   }
 }
 

http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/0fd4e3d9/core/src/main/java/org/eigenbase/sql/parser/SqlParserUtil.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/eigenbase/sql/parser/SqlParserUtil.java b/core/src/main/java/org/eigenbase/sql/parser/SqlParserUtil.java
index 30fe3d4..1297f35 100644
--- a/core/src/main/java/org/eigenbase/sql/parser/SqlParserUtil.java
+++ b/core/src/main/java/org/eigenbase/sql/parser/SqlParserUtil.java
@@ -22,6 +22,7 @@ import java.text.*;
 import java.util.*;
 import java.util.logging.*;
 
+import org.eigenbase.reltype.RelDataTypeSystem;
 import org.eigenbase.sql.*;
 import org.eigenbase.trace.*;
 import org.eigenbase.util.*;
@@ -135,7 +136,7 @@ public final class SqlParserUtil {
     int[] ret;
     try {
       ret = intervalQualifier.evaluateIntervalLiteral(literal,
-          intervalQualifier.getParserPosition());
+          intervalQualifier.getParserPosition(), RelDataTypeSystem.DEFAULT);
       assert ret != null;
     } catch (EigenbaseContextException e) {
       throw Util.newInternal(
@@ -177,7 +178,7 @@ public final class SqlParserUtil {
     int[] ret;
     try {
       ret = intervalQualifier.evaluateIntervalLiteral(literal,
-          intervalQualifier.getParserPosition());
+          intervalQualifier.getParserPosition(), RelDataTypeSystem.DEFAULT);
       assert ret != null;
     } catch (EigenbaseContextException e) {
       throw Util.newInternal(

http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/0fd4e3d9/core/src/main/java/org/eigenbase/sql/type/BasicSqlType.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/eigenbase/sql/type/BasicSqlType.java b/core/src/main/java/org/eigenbase/sql/type/BasicSqlType.java
index ab79e8f..796f45b 100644
--- a/core/src/main/java/org/eigenbase/sql/type/BasicSqlType.java
+++ b/core/src/main/java/org/eigenbase/sql/type/BasicSqlType.java
@@ -18,6 +18,7 @@ package org.eigenbase.sql.type;
 
 import java.nio.charset.*;
 
+import org.eigenbase.reltype.RelDataTypeSystem;
 import org.eigenbase.sql.*;
 import org.eigenbase.util.*;
 
@@ -30,8 +31,9 @@ public class BasicSqlType extends AbstractSqlType {
 
   //~ Instance fields --------------------------------------------------------
 
-  private int precision;
-  private int scale;
+  private final int precision;
+  private final int scale;
+  private final RelDataTypeSystem typeSystem;
   private SqlCollation collation;
   private SerializableCharset wrappedCharset;
 
@@ -43,12 +45,11 @@ public class BasicSqlType extends AbstractSqlType {
    *
    * @param typeName Type name
    */
-  public BasicSqlType(SqlTypeName typeName) {
-    super(typeName, false, null);
+  public BasicSqlType(RelDataTypeSystem typeSystem, SqlTypeName typeName) {
+    this(typeSystem, typeName, false, PRECISION_NOT_SPECIFIED,
+        SCALE_NOT_SPECIFIED);
     assert typeName.allowsPrecScale(false, false)
         : "typeName.allowsPrecScale(false,false), typeName=" + typeName.name();
-    this.precision = PRECISION_NOT_SPECIFIED;
-    this.scale = SCALE_NOT_SPECIFIED;
     computeDigest();
   }
 
@@ -57,14 +58,11 @@ public class BasicSqlType extends AbstractSqlType {
    *
    * @param typeName Type name
    */
-  public BasicSqlType(
-      SqlTypeName typeName,
+  public BasicSqlType(RelDataTypeSystem typeSystem, SqlTypeName typeName,
       int precision) {
-    super(typeName, false, null);
+    this(typeSystem, typeName, false, precision, SCALE_NOT_SPECIFIED);
     assert typeName.allowsPrecScale(true, false)
         : "typeName.allowsPrecScale(true, false)";
-    this.precision = precision;
-    this.scale = SCALE_NOT_SPECIFIED;
     computeDigest();
   }
 
@@ -73,15 +71,24 @@ public class BasicSqlType extends AbstractSqlType {
    *
    * @param typeName Type name
    */
-  public BasicSqlType(
+  public BasicSqlType(RelDataTypeSystem typeSystem, SqlTypeName typeName,
+      int precision, int scale) {
+    this(typeSystem, typeName, false, precision, scale);
+    assert typeName.allowsPrecScale(true, true);
+    computeDigest();
+  }
+
+  /** Internal constructor. */
+  private BasicSqlType(
+      RelDataTypeSystem typeSystem,
       SqlTypeName typeName,
+      boolean nullable,
       int precision,
       int scale) {
-    super(typeName, false, null);
-    assert typeName.allowsPrecScale(true, true);
+    super(typeName, nullable, null);
+    this.typeSystem = typeSystem;
     this.precision = precision;
     this.scale = scale;
-    computeDigest();
   }
 
   //~ Methods ----------------------------------------------------------------
@@ -137,7 +144,7 @@ public class BasicSqlType extends AbstractSqlType {
       case BIGINT:
         return 19;
       case DECIMAL:
-        return SqlTypeName.MAX_NUMERIC_PRECISION;
+        return RelDataTypeSystem.DEFAULT.getMaxNumericPrecision(); // FIXME
       case REAL:
         return 7;
       case FLOAT:
@@ -205,7 +212,7 @@ public class BasicSqlType extends AbstractSqlType {
     // since (for instance) TIME is equivalent to TIME(0).
     if (withDetail) {
       // -1 means there is no default value for precision
-      if (typeName.getDefaultPrecision() > -1) {
+      if (typeSystem.getDefaultPrecision(typeName) > -1) {
         printPrecision = true;
       }
       if (typeName.getDefaultScale() > -1) {

http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/0fd4e3d9/core/src/main/java/org/eigenbase/sql/type/IntervalSqlType.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/eigenbase/sql/type/IntervalSqlType.java b/core/src/main/java/org/eigenbase/sql/type/IntervalSqlType.java
index c3141f7..c82671c 100644
--- a/core/src/main/java/org/eigenbase/sql/type/IntervalSqlType.java
+++ b/core/src/main/java/org/eigenbase/sql/type/IntervalSqlType.java
@@ -19,6 +19,8 @@ package org.eigenbase.sql.type;
 import org.eigenbase.reltype.*;
 import org.eigenbase.sql.*;
 import org.eigenbase.sql.parser.*;
+import org.eigenbase.sql.pretty.SqlPrettyWriter;
+import org.eigenbase.sql.util.SqlString;
 
 /**
  * IntervalSqlType represents a standard SQL datetime interval type.
@@ -26,6 +28,7 @@ import org.eigenbase.sql.parser.*;
 public class IntervalSqlType extends AbstractSqlType {
   //~ Instance fields --------------------------------------------------------
 
+  private final RelDataTypeSystem typeSystem;
   private SqlIntervalQualifier intervalQualifier;
 
   //~ Constructors -----------------------------------------------------------
@@ -34,14 +37,15 @@ public class IntervalSqlType extends AbstractSqlType {
    * Constructs an IntervalSqlType. This should only be called from a factory
    * method.
    */
-  public IntervalSqlType(
+  public IntervalSqlType(RelDataTypeSystem typeSystem,
       SqlIntervalQualifier intervalQualifier,
       boolean isNullable) {
-    super(
-        intervalQualifier.isYearMonth() ? SqlTypeName.INTERVAL_YEAR_MONTH
+    super(intervalQualifier.isYearMonth()
+            ? SqlTypeName.INTERVAL_YEAR_MONTH
             : SqlTypeName.INTERVAL_DAY_TIME,
         isNullable,
         null);
+    this.typeSystem = typeSystem;
     this.intervalQualifier = intervalQualifier;
     computeDigest();
   }
@@ -51,7 +55,15 @@ public class IntervalSqlType extends AbstractSqlType {
   // implement RelDataTypeImpl
   protected void generateTypeString(StringBuilder sb, boolean withDetail) {
     sb.append("INTERVAL ");
-    sb.append(intervalQualifier.toString());
+    SqlDialect dialect = null;
+    dialect = SqlDialect.DUMMY;
+    SqlPrettyWriter writer = new SqlPrettyWriter(dialect);
+    writer.setAlwaysUseParentheses(false);
+    writer.setSelectListItemsOnSeparateLines(false);
+    writer.setIndentation(0);
+    intervalQualifier.unparse(writer, 0, 0);
+    final String sql = writer.toString();
+    sb.append(new SqlString(dialect, sql).getSql());
   }
 
   // implement RelDataType
@@ -88,10 +100,10 @@ public class IntervalSqlType extends AbstractSqlType {
     int secondPrec =
         this.intervalQualifier.getStartPrecisionPreservingDefault();
     int fracPrec =
-        SqlIntervalQualifier
-            .combineFractionalSecondPrecisionPreservingDefault(
-                this.intervalQualifier,
-                that.intervalQualifier);
+        SqlIntervalQualifier.combineFractionalSecondPrecisionPreservingDefault(
+            typeSystem,
+            this.intervalQualifier,
+            that.intervalQualifier);
 
     if (thisStart.ordinal() > thatStart.ordinal()) {
       thisEnd = thisStart;
@@ -101,17 +113,15 @@ public class IntervalSqlType extends AbstractSqlType {
     } else if (thisStart.ordinal() == thatStart.ordinal()) {
       secondPrec =
           SqlIntervalQualifier.combineStartPrecisionPreservingDefault(
+              typeFactory.getTypeSystem(),
               this.intervalQualifier,
               that.intervalQualifier);
-    } else if (
-        (null == thisEnd)
-            || (thisEnd.ordinal() < thatStart.ordinal())) {
+    } else if (null == thisEnd || thisEnd.ordinal() < thatStart.ordinal()) {
       thisEnd = thatStart;
     }
 
     if (null != thatEnd) {
-      if ((null == thisEnd)
-          || (thisEnd.ordinal() < thatEnd.ordinal())) {
+      if (null == thisEnd || thisEnd.ordinal() < thatEnd.ordinal()) {
         thisEnd = thatEnd;
       }
     }
@@ -131,17 +141,14 @@ public class IntervalSqlType extends AbstractSqlType {
     return (IntervalSqlType) intervalType;
   }
 
-  // implement RelDataType
-  public int getPrecision() {
-    return intervalQualifier.getStartPrecision();
+  @Override public int getPrecision() {
+    return intervalQualifier.getStartPrecision(typeSystem);
   }
 
   @Override
   public int getScale() {
-    // TODO Auto-generated method stub
-    return intervalQualifier.getFractionalSecondPrecision();
+    return intervalQualifier.getFractionalSecondPrecision(typeSystem);
   }
-
 }
 
 // End IntervalSqlType.java

http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/0fd4e3d9/core/src/main/java/org/eigenbase/sql/type/ReturnTypes.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/eigenbase/sql/type/ReturnTypes.java b/core/src/main/java/org/eigenbase/sql/type/ReturnTypes.java
index 573c36c..55c2893 100644
--- a/core/src/main/java/org/eigenbase/sql/type/ReturnTypes.java
+++ b/core/src/main/java/org/eigenbase/sql/type/ReturnTypes.java
@@ -468,23 +468,21 @@ public abstract class ReturnTypes {
               int s1 = type1.getScale();
               int s2 = type2.getScale();
 
+              final RelDataTypeFactory typeFactory = opBinding.getTypeFactory();
               int scale = Math.max(s1, s2);
-              assert scale <= SqlTypeName.MAX_NUMERIC_SCALE;
+              final RelDataTypeSystem typeSystem = typeFactory.getTypeSystem();
+              assert scale <= typeSystem.getMaxNumericScale();
               int precision = Math.max(p1 - s1, p2 - s2) + scale + 1;
               precision =
                   Math.min(
                       precision,
-                      SqlTypeName.MAX_NUMERIC_PRECISION);
+                      typeSystem.getMaxNumericPrecision());
               assert precision > 0;
 
-              RelDataType ret;
-              ret =
-                  opBinding.getTypeFactory().createSqlType(
-                      SqlTypeName.DECIMAL,
-                      precision,
-                      scale);
-
-              return ret;
+              return typeFactory.createSqlType(
+                  SqlTypeName.DECIMAL,
+                  precision,
+                  scale);
             }
           }
 

http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/0fd4e3d9/core/src/main/java/org/eigenbase/sql/type/SqlTypeFactoryImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/eigenbase/sql/type/SqlTypeFactoryImpl.java b/core/src/main/java/org/eigenbase/sql/type/SqlTypeFactoryImpl.java
index 71ccbf9..04110b8 100644
--- a/core/src/main/java/org/eigenbase/sql/type/SqlTypeFactoryImpl.java
+++ b/core/src/main/java/org/eigenbase/sql/type/SqlTypeFactoryImpl.java
@@ -24,13 +24,14 @@ import org.eigenbase.sql.*;
 import org.eigenbase.util.*;
 
 /**
- * SqlTypeFactoryImpl provides a default implementation of {@link
- * RelDataTypeFactory} which supports SQL types.
+ * SqlTypeFactoryImpl provides a default implementation of
+ * {@link RelDataTypeFactory} which supports SQL types.
  */
 public class SqlTypeFactoryImpl extends RelDataTypeFactoryImpl {
   //~ Constructors -----------------------------------------------------------
 
-  public SqlTypeFactoryImpl() {
+  public SqlTypeFactoryImpl(RelDataTypeSystem typeSystem) {
+    super(typeSystem);
   }
 
   //~ Methods ----------------------------------------------------------------
@@ -38,10 +39,10 @@ public class SqlTypeFactoryImpl extends RelDataTypeFactoryImpl {
   // implement RelDataTypeFactory
   public RelDataType createSqlType(SqlTypeName typeName) {
     if (typeName.allowsPrec()) {
-      return createSqlType(typeName, typeName.getDefaultPrecision());
+      return createSqlType(typeName, typeSystem.getDefaultPrecision(typeName));
     }
     assertBasic(typeName);
-    RelDataType newType = new BasicSqlType(typeName);
+    RelDataType newType = new BasicSqlType(typeSystem, typeName);
     return canonize(newType);
   }
 
@@ -55,7 +56,7 @@ public class SqlTypeFactoryImpl extends RelDataTypeFactoryImpl {
     assertBasic(typeName);
     assert (precision >= 0)
         || (precision == RelDataType.PRECISION_NOT_SPECIFIED);
-    RelDataType newType = new BasicSqlType(typeName, precision);
+    RelDataType newType = new BasicSqlType(typeSystem, typeName, precision);
     newType = SqlTypeUtil.addCharsetAndCollation(newType, this);
     return canonize(newType);
   }
@@ -68,7 +69,8 @@ public class SqlTypeFactoryImpl extends RelDataTypeFactoryImpl {
     assertBasic(typeName);
     assert (precision >= 0)
         || (precision == RelDataType.PRECISION_NOT_SPECIFIED);
-    RelDataType newType = new BasicSqlType(typeName, precision, scale);
+    RelDataType newType =
+        new BasicSqlType(typeSystem, typeName, precision, scale);
     newType = SqlTypeUtil.addCharsetAndCollation(newType, this);
     return canonize(newType);
   }
@@ -100,7 +102,8 @@ public class SqlTypeFactoryImpl extends RelDataTypeFactoryImpl {
   // implement RelDataTypeFactory
   public RelDataType createSqlIntervalType(
       SqlIntervalQualifier intervalQualifier) {
-    RelDataType newType = new IntervalSqlType(intervalQualifier, false);
+    RelDataType newType =
+        new IntervalSqlType(typeSystem, intervalQualifier, false);
     return canonize(newType);
   }
 
@@ -339,23 +342,24 @@ public class SqlTypeFactoryImpl extends RelDataTypeFactoryImpl {
               int p2 = type.getPrecision();
               int s1 = resultType.getScale();
               int s2 = type.getScale();
+              final int maxPrecision = typeSystem.getMaxNumericPrecision();
+              final int maxScale = typeSystem.getMaxNumericScale();
 
               int dout = Math.max(p1 - s1, p2 - s2);
               dout =
                   Math.min(
                       dout,
-                      SqlTypeName.MAX_NUMERIC_PRECISION);
+                      maxPrecision);
 
               int scale = Math.max(s1, s2);
               scale =
                   Math.min(
                       scale,
-                      SqlTypeName.MAX_NUMERIC_PRECISION - dout);
-              scale =
-                  Math.min(scale, SqlTypeName.MAX_NUMERIC_SCALE);
+                      maxPrecision - dout);
+              scale = Math.min(scale, maxScale);
 
               int precision = dout + scale;
-              assert precision <= SqlTypeName.MAX_NUMERIC_PRECISION;
+              assert precision <= maxPrecision;
               assert precision > 0;
 
               resultType =
@@ -461,7 +465,7 @@ public class SqlTypeFactoryImpl extends RelDataTypeFactoryImpl {
   }
 
   private RelDataType copyIntervalType(RelDataType type, boolean nullable) {
-    return new IntervalSqlType(
+    return new IntervalSqlType(typeSystem,
         type.getIntervalQualifier(),
         nullable);
   }