Repository: calcite Updated Branches: refs/heads/master 0ced3b7f5 -> 707f4de9c
Clean up SqlTypeAssignmentRules Project: http://git-wip-us.apache.org/repos/asf/calcite/repo Commit: http://git-wip-us.apache.org/repos/asf/calcite/commit/becb6dfb Tree: http://git-wip-us.apache.org/repos/asf/calcite/tree/becb6dfb Diff: http://git-wip-us.apache.org/repos/asf/calcite/diff/becb6dfb Branch: refs/heads/master Commit: becb6dfb85fad583d75aeaba13ac71d03bb5d4a8 Parents: fd68f25 Author: Julian Hyde <[email protected]> Authored: Mon Jan 8 19:58:41 2018 -0800 Committer: Julian Hyde <[email protected]> Committed: Fri Feb 16 10:18:01 2018 -0800 ---------------------------------------------------------------------- .../sql/type/SqlTypeAssignmentRules.java | 464 ++++++++++--------- .../apache/calcite/sql/type/SqlTypeUtil.java | 4 +- .../org/apache/calcite/test/RexProgramTest.java | 17 +- 3 files changed, 259 insertions(+), 226 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/calcite/blob/becb6dfb/core/src/main/java/org/apache/calcite/sql/type/SqlTypeAssignmentRules.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/sql/type/SqlTypeAssignmentRules.java b/core/src/main/java/org/apache/calcite/sql/type/SqlTypeAssignmentRules.java index cd46c39..038ead3 100644 --- a/core/src/main/java/org/apache/calcite/sql/type/SqlTypeAssignmentRules.java +++ b/core/src/main/java/org/apache/calcite/sql/type/SqlTypeAssignmentRules.java @@ -16,80 +16,96 @@ */ package org.apache.calcite.sql.type; +import com.google.common.base.Preconditions; +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Sets; + import java.util.EnumSet; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; +import java.util.concurrent.ExecutionException; +import javax.annotation.Nonnull; /** - * Class to hold rules to determine if a type is assignable from another type. - * - * <p>REVIEW 7/05/04 Wael: We should split this up in Cast rules, symmetric and - * asymmetric assignable rules + * Rules that determine whether a type is assignable from another type. */ public class SqlTypeAssignmentRules { //~ Static fields/initializers --------------------------------------------- - private static SqlTypeAssignmentRules instance = null; + private static final SqlTypeAssignmentRules INSTANCE; + private static final SqlTypeAssignmentRules COERCE_INSTANCE; - private final Map<SqlTypeName, Set<SqlTypeName>> rules; - private final Map<SqlTypeName, Set<SqlTypeName>> coerceRules; + private final Map<SqlTypeName, ImmutableSet<SqlTypeName>> map; //~ Constructors ----------------------------------------------------------- - private SqlTypeAssignmentRules() { - rules = new HashMap<>(); + private SqlTypeAssignmentRules( + Map<SqlTypeName, ImmutableSet<SqlTypeName>> map) { + this.map = ImmutableMap.copyOf(map); + } + + static { + final Builder rules = new Builder(); - Set<SqlTypeName> rule; + final Set<SqlTypeName> rule = new HashSet<>(); // IntervalYearMonth is assignable from... for (SqlTypeName interval : SqlTypeName.YEAR_INTERVAL_TYPES) { - rules.put(interval, SqlTypeName.YEAR_INTERVAL_TYPES); + rules.add(interval, SqlTypeName.YEAR_INTERVAL_TYPES); } for (SqlTypeName interval : SqlTypeName.DAY_INTERVAL_TYPES) { - rules.put(interval, SqlTypeName.DAY_INTERVAL_TYPES); + rules.add(interval, SqlTypeName.DAY_INTERVAL_TYPES); + } + for (SqlTypeName interval : SqlTypeName.DAY_INTERVAL_TYPES) { + final Set<SqlTypeName> dayIntervalTypes = SqlTypeName.DAY_INTERVAL_TYPES; + rules.add(interval, dayIntervalTypes); } - // Multiset is assignable from... - rules.put(SqlTypeName.MULTISET, EnumSet.of(SqlTypeName.MULTISET)); + // MULTISET is assignable from... + rules.add(SqlTypeName.MULTISET, EnumSet.of(SqlTypeName.MULTISET)); - // Tinyint is assignable from... - rules.put(SqlTypeName.TINYINT, EnumSet.of(SqlTypeName.TINYINT)); + // TINYINT is assignable from... + rules.add(SqlTypeName.TINYINT, EnumSet.of(SqlTypeName.TINYINT)); - // Smallint is assignable from... - rule = new HashSet<>(); + // SMALLINT is assignable from... + rule.clear(); rule.add(SqlTypeName.TINYINT); rule.add(SqlTypeName.SMALLINT); - rules.put(SqlTypeName.SMALLINT, rule); + rules.add(SqlTypeName.SMALLINT, rule); - // Int is assignable from... - rule = new HashSet<>(); + // INTEGER is assignable from... + rule.clear(); rule.add(SqlTypeName.TINYINT); rule.add(SqlTypeName.SMALLINT); rule.add(SqlTypeName.INTEGER); - rules.put(SqlTypeName.INTEGER, rule); + rules.add(SqlTypeName.INTEGER, rule); - // BigInt is assignable from... - rule = new HashSet<>(); + // BIGINT is assignable from... + rule.clear(); rule.add(SqlTypeName.TINYINT); rule.add(SqlTypeName.SMALLINT); rule.add(SqlTypeName.INTEGER); rule.add(SqlTypeName.BIGINT); - rules.put(SqlTypeName.BIGINT, rule); + rules.add(SqlTypeName.BIGINT, rule); - // Float is assignable from... - rule = new HashSet<>(); + // FLOAT (up to 64 bit floating point) is assignable from... + rule.clear(); rule.add(SqlTypeName.TINYINT); rule.add(SqlTypeName.SMALLINT); rule.add(SqlTypeName.INTEGER); rule.add(SqlTypeName.BIGINT); rule.add(SqlTypeName.DECIMAL); rule.add(SqlTypeName.FLOAT); - rules.put(SqlTypeName.FLOAT, rule); + rules.add(SqlTypeName.FLOAT, rule); - // Real is assignable from... - rule = new HashSet<>(); + // REAL (32 bit floating point) is assignable from... + rule.clear(); rule.add(SqlTypeName.TINYINT); rule.add(SqlTypeName.SMALLINT); rule.add(SqlTypeName.INTEGER); @@ -97,10 +113,10 @@ public class SqlTypeAssignmentRules { rule.add(SqlTypeName.DECIMAL); rule.add(SqlTypeName.FLOAT); rule.add(SqlTypeName.REAL); - rules.put(SqlTypeName.REAL, rule); + rules.add(SqlTypeName.REAL, rule); - // Double is assignable from... - rule = new HashSet<>(); + // DOUBLE is assignable from... + rule.clear(); rule.add(SqlTypeName.TINYINT); rule.add(SqlTypeName.SMALLINT); rule.add(SqlTypeName.INTEGER); @@ -109,10 +125,10 @@ public class SqlTypeAssignmentRules { rule.add(SqlTypeName.FLOAT); rule.add(SqlTypeName.REAL); rule.add(SqlTypeName.DOUBLE); - rules.put(SqlTypeName.DOUBLE, rule); + rules.add(SqlTypeName.DOUBLE, rule); - // Decimal is assignable from... - rule = new HashSet<>(); + // DECIMAL is assignable from... + rule.clear(); rule.add(SqlTypeName.TINYINT); rule.add(SqlTypeName.SMALLINT); rule.add(SqlTypeName.INTEGER); @@ -120,63 +136,63 @@ public class SqlTypeAssignmentRules { rule.add(SqlTypeName.REAL); rule.add(SqlTypeName.DOUBLE); rule.add(SqlTypeName.DECIMAL); - rules.put(SqlTypeName.DECIMAL, rule); + rules.add(SqlTypeName.DECIMAL, rule); - // VarBinary is assignable from... - rule = new HashSet<>(); + // VARBINARY is assignable from... + rule.clear(); rule.add(SqlTypeName.VARBINARY); rule.add(SqlTypeName.BINARY); - rules.put(SqlTypeName.VARBINARY, rule); + rules.add(SqlTypeName.VARBINARY, rule); - // Char is assignable from... - rules.put(SqlTypeName.CHAR, EnumSet.of(SqlTypeName.CHAR)); + // CHAR is assignable from... + rules.add(SqlTypeName.CHAR, EnumSet.of(SqlTypeName.CHAR)); - // VarChar is assignable from... - rule = new HashSet<>(); + // VARCHAR is assignable from... + rule.clear(); rule.add(SqlTypeName.CHAR); rule.add(SqlTypeName.VARCHAR); - rules.put(SqlTypeName.VARCHAR, rule); + rules.add(SqlTypeName.VARCHAR, rule); - // Boolean is assignable from... - rules.put(SqlTypeName.BOOLEAN, EnumSet.of(SqlTypeName.BOOLEAN)); + // BOOLEAN is assignable from... + rules.add(SqlTypeName.BOOLEAN, EnumSet.of(SqlTypeName.BOOLEAN)); - // Binary is assignable from... - rule = new HashSet<>(); + // BINARY is assignable from... + rule.clear(); rule.add(SqlTypeName.BINARY); rule.add(SqlTypeName.VARBINARY); - rules.put(SqlTypeName.BINARY, rule); + rules.add(SqlTypeName.BINARY, rule); - // Date is assignable from ... - rule = new HashSet<>(); + // DATE is assignable from... + rule.clear(); rule.add(SqlTypeName.DATE); rule.add(SqlTypeName.TIMESTAMP); - rules.put(SqlTypeName.DATE, rule); + rules.add(SqlTypeName.DATE, rule); - // Time is assignable from ... - rule = new HashSet<>(); + // TIME is assignable from... + rule.clear(); rule.add(SqlTypeName.TIME); rule.add(SqlTypeName.TIMESTAMP); - rules.put(SqlTypeName.TIME, rule); + rules.add(SqlTypeName.TIME, rule); - // Time with local time-zone is assignable from ... - rules.put(SqlTypeName.TIME_WITH_LOCAL_TIME_ZONE, + // TIME WITH LOCAL TIME ZONE is assignable from... + rules.add(SqlTypeName.TIME_WITH_LOCAL_TIME_ZONE, EnumSet.of(SqlTypeName.TIME_WITH_LOCAL_TIME_ZONE)); - // Timestamp is assignable from ... - rules.put(SqlTypeName.TIMESTAMP, EnumSet.of(SqlTypeName.TIMESTAMP)); + // TIMESTAMP is assignable from ... + rules.add(SqlTypeName.TIMESTAMP, EnumSet.of(SqlTypeName.TIMESTAMP)); - // Timestamp with local time-zone is assignable from ... - rules.put(SqlTypeName.TIMESTAMP_WITH_LOCAL_TIME_ZONE, + // TIMESTAMP WITH LOCAL TIME ZONE is assignable from... + rules.add(SqlTypeName.TIMESTAMP_WITH_LOCAL_TIME_ZONE, EnumSet.of(SqlTypeName.TIMESTAMP_WITH_LOCAL_TIME_ZONE)); - // Geometry is assignable from ... - rules.put(SqlTypeName.GEOMETRY, EnumSet.of(SqlTypeName.GEOMETRY)); + // GEOMETRY is assignable from ... + rules.add(SqlTypeName.GEOMETRY, EnumSet.of(SqlTypeName.GEOMETRY)); - // Array is assignable from ... - rules.put(SqlTypeName.ARRAY, EnumSet.of(SqlTypeName.ARRAY)); + // ARRAY is assignable from ... + rules.add(SqlTypeName.ARRAY, EnumSet.of(SqlTypeName.ARRAY)); - // Any is assignable from ... - rule = new HashSet<>(); + // ANY is assignable from ... + rule.clear(); rule.add(SqlTypeName.TINYINT); rule.add(SqlTypeName.SMALLINT); rule.add(SqlTypeName.INTEGER); @@ -187,14 +203,14 @@ public class SqlTypeAssignmentRules { rule.add(SqlTypeName.TIME); rule.add(SqlTypeName.DATE); rule.add(SqlTypeName.TIMESTAMP); - rules.put(SqlTypeName.ANY, rule); + rules.add(SqlTypeName.ANY, rule); // we use coerceRules when we're casting - coerceRules = copy(rules); + final Builder coerceRules = new Builder(rules); - // Make numbers symmetrical and - // make varchar/char castable to/from numbers - rule = new HashSet<>(); + // Make numbers symmetrical, + // and make VARCHAR and CHAR castable to/from numbers + rule.clear(); rule.add(SqlTypeName.TINYINT); rule.add(SqlTypeName.SMALLINT); rule.add(SqlTypeName.INTEGER); @@ -207,148 +223,156 @@ public class SqlTypeAssignmentRules { rule.add(SqlTypeName.CHAR); rule.add(SqlTypeName.VARCHAR); - coerceRules.put( - SqlTypeName.TINYINT, - copy(rule)); - coerceRules.put( - SqlTypeName.SMALLINT, - copy(rule)); - coerceRules.put( - SqlTypeName.INTEGER, - copy(rule)); - coerceRules.put( - SqlTypeName.BIGINT, - copy(rule)); - coerceRules.put( - SqlTypeName.FLOAT, - copy(rule)); - coerceRules.put( - SqlTypeName.REAL, - copy(rule)); - coerceRules.put( - SqlTypeName.DECIMAL, - copy(rule)); - coerceRules.put( - SqlTypeName.DOUBLE, - copy(rule)); - coerceRules.put( - SqlTypeName.CHAR, - copy(rule)); - coerceRules.put( - SqlTypeName.VARCHAR, - copy(rule)); - - // Exact Numerics are castable from intervals + coerceRules.add(SqlTypeName.TINYINT, rule); + coerceRules.add(SqlTypeName.SMALLINT, rule); + coerceRules.add(SqlTypeName.INTEGER, rule); + coerceRules.add(SqlTypeName.BIGINT, rule); + coerceRules.add(SqlTypeName.FLOAT, rule); + coerceRules.add(SqlTypeName.REAL, rule); + coerceRules.add(SqlTypeName.DECIMAL, rule); + coerceRules.add(SqlTypeName.DOUBLE, rule); + coerceRules.add(SqlTypeName.CHAR, rule); + coerceRules.add(SqlTypeName.VARCHAR, rule); + + // Exact numeric types are castable from intervals for (SqlTypeName exactType : SqlTypeName.EXACT_TYPES) { - rule = coerceRules.get(exactType); - rule.addAll(SqlTypeName.INTERVAL_TYPES); + coerceRules.add(exactType, + coerceRules.copyValues(exactType) + .addAll(SqlTypeName.INTERVAL_TYPES) + .build()); } - // intervals are castable from Exact Numeric + // Intervals are castable from exact numeric for (SqlTypeName typeName : SqlTypeName.INTERVAL_TYPES) { - rule = coerceRules.get(typeName); - rule.add(SqlTypeName.TINYINT); - rule.add(SqlTypeName.SMALLINT); - rule.add(SqlTypeName.INTEGER); - rule.add(SqlTypeName.BIGINT); - rule.add(SqlTypeName.DECIMAL); - rule.add(SqlTypeName.VARCHAR); + coerceRules.add(typeName, + coerceRules.copyValues(typeName) + .add(SqlTypeName.TINYINT) + .add(SqlTypeName.SMALLINT) + .add(SqlTypeName.INTEGER) + .add(SqlTypeName.BIGINT) + .add(SqlTypeName.DECIMAL) + .add(SqlTypeName.VARCHAR) + .build()); } - // varchar is castable from Boolean, Date, time, timestamp, numbers and + // VARCHAR is castable from BOOLEAN, DATE, TIMESTAMP, numeric types and // intervals - rule = coerceRules.get(SqlTypeName.VARCHAR); - rule.add(SqlTypeName.BOOLEAN); - rule.add(SqlTypeName.DATE); - rule.add(SqlTypeName.TIME); - rule.add(SqlTypeName.TIMESTAMP); - rule.addAll(SqlTypeName.INTERVAL_TYPES); - - // char is castable from Boolean, Date, time and timestamp and numbers - rule = coerceRules.get(SqlTypeName.CHAR); - rule.add(SqlTypeName.BOOLEAN); - rule.add(SqlTypeName.DATE); - rule.add(SqlTypeName.TIME); - rule.add(SqlTypeName.TIMESTAMP); - rule.addAll(SqlTypeName.INTERVAL_TYPES); - - // Boolean is castable from char and varchar - rule = coerceRules.get(SqlTypeName.BOOLEAN); - rule.add(SqlTypeName.CHAR); - rule.add(SqlTypeName.VARCHAR); - - // Date, time, and timestamp are castable from - // char and varchar - // Date is castable from ... - rule = new HashSet<>(); - rule.add(SqlTypeName.DATE); - rule.add(SqlTypeName.TIMESTAMP); - rule.add(SqlTypeName.TIMESTAMP_WITH_LOCAL_TIME_ZONE); - rule.add(SqlTypeName.CHAR); - rule.add(SqlTypeName.VARCHAR); - coerceRules.put(SqlTypeName.DATE, rule); - - // Time is castable from ... - rule = new HashSet<>(); - rule.add(SqlTypeName.TIME); - rule.add(SqlTypeName.TIME_WITH_LOCAL_TIME_ZONE); - rule.add(SqlTypeName.TIMESTAMP); - rule.add(SqlTypeName.TIMESTAMP_WITH_LOCAL_TIME_ZONE); - rule.add(SqlTypeName.CHAR); - rule.add(SqlTypeName.VARCHAR); - coerceRules.put(SqlTypeName.TIME, rule); - - // Time with local time-zone is castable from ... - rule = new HashSet<>(); - rule.add(SqlTypeName.TIME); - rule.add(SqlTypeName.TIME_WITH_LOCAL_TIME_ZONE); - rule.add(SqlTypeName.TIMESTAMP); - rule.add(SqlTypeName.TIMESTAMP_WITH_LOCAL_TIME_ZONE); - rule.add(SqlTypeName.CHAR); - rule.add(SqlTypeName.VARCHAR); - coerceRules.put(SqlTypeName.TIME_WITH_LOCAL_TIME_ZONE, rule); - - // Timestamp is castable from ... - rule = new HashSet<>(); - rule.add(SqlTypeName.TIMESTAMP); - rule.add(SqlTypeName.TIMESTAMP_WITH_LOCAL_TIME_ZONE); - rule.add(SqlTypeName.DATE); - rule.add(SqlTypeName.TIME); - rule.add(SqlTypeName.TIME_WITH_LOCAL_TIME_ZONE); - rule.add(SqlTypeName.CHAR); - rule.add(SqlTypeName.VARCHAR); - coerceRules.put(SqlTypeName.TIMESTAMP, rule); - - // Timestamp with local time-zone is castable from ... - rule = new HashSet<>(); - rule.add(SqlTypeName.TIMESTAMP); - rule.add(SqlTypeName.TIMESTAMP_WITH_LOCAL_TIME_ZONE); - rule.add(SqlTypeName.DATE); - rule.add(SqlTypeName.TIME); - rule.add(SqlTypeName.TIME_WITH_LOCAL_TIME_ZONE); - rule.add(SqlTypeName.CHAR); - rule.add(SqlTypeName.VARCHAR); - coerceRules.put(SqlTypeName.TIMESTAMP_WITH_LOCAL_TIME_ZONE, rule); + coerceRules.add(SqlTypeName.VARCHAR, + coerceRules.copyValues(SqlTypeName.VARCHAR) + .add(SqlTypeName.BOOLEAN) + .add(SqlTypeName.DATE) + .add(SqlTypeName.TIME) + .add(SqlTypeName.TIMESTAMP) + .addAll(SqlTypeName.INTERVAL_TYPES) + .build()); + + // CHAR is castable from BOOLEAN, DATE, TIME, TIMESTAMP and numeric types + coerceRules.add(SqlTypeName.CHAR, + coerceRules.copyValues(SqlTypeName.CHAR) + .add(SqlTypeName.BOOLEAN) + .add(SqlTypeName.DATE) + .add(SqlTypeName.TIME) + .add(SqlTypeName.TIMESTAMP) + .addAll(SqlTypeName.INTERVAL_TYPES) + .build()); + + // BOOLEAN is castable from CHAR and VARCHAR + coerceRules.add(SqlTypeName.BOOLEAN, + coerceRules.copyValues(SqlTypeName.BOOLEAN) + .add(SqlTypeName.CHAR) + .add(SqlTypeName.VARCHAR) + .build()); + + // DATE, TIME, and TIMESTAMP are castable from + // CHAR and VARCHAR. + + // DATE is castable from... + coerceRules.add(SqlTypeName.DATE, + coerceRules.copyValues(SqlTypeName.DATE) + .add(SqlTypeName.DATE) + .add(SqlTypeName.TIMESTAMP) + .add(SqlTypeName.TIMESTAMP_WITH_LOCAL_TIME_ZONE) + .add(SqlTypeName.CHAR) + .add(SqlTypeName.VARCHAR) + .build()); + + // TIME is castable from... + coerceRules.add(SqlTypeName.TIME, + coerceRules.copyValues(SqlTypeName.TIME) + .add(SqlTypeName.TIME) + .add(SqlTypeName.TIME_WITH_LOCAL_TIME_ZONE) + .add(SqlTypeName.TIMESTAMP) + .add(SqlTypeName.TIMESTAMP_WITH_LOCAL_TIME_ZONE) + .add(SqlTypeName.CHAR) + .add(SqlTypeName.VARCHAR) + .build()); + + // TIME WITH LOCAL TIME ZONE is castable from... + coerceRules.add(SqlTypeName.TIME_WITH_LOCAL_TIME_ZONE, + coerceRules.copyValues(SqlTypeName.TIME_WITH_LOCAL_TIME_ZONE) + .add(SqlTypeName.TIME) + .add(SqlTypeName.TIME_WITH_LOCAL_TIME_ZONE) + .add(SqlTypeName.TIMESTAMP) + .add(SqlTypeName.TIMESTAMP_WITH_LOCAL_TIME_ZONE) + .add(SqlTypeName.CHAR) + .add(SqlTypeName.VARCHAR) + .build()); + + // TIMESTAMP is castable from... + coerceRules.add(SqlTypeName.TIMESTAMP, + coerceRules.copyValues(SqlTypeName.TIMESTAMP) + .add(SqlTypeName.TIMESTAMP) + .add(SqlTypeName.TIMESTAMP_WITH_LOCAL_TIME_ZONE) + .add(SqlTypeName.DATE) + .add(SqlTypeName.TIME) + .add(SqlTypeName.TIME_WITH_LOCAL_TIME_ZONE) + .add(SqlTypeName.CHAR) + .add(SqlTypeName.VARCHAR) + .build()); + + // TIMESTAMP WITH LOCAL TIME ZONE is castable from... + coerceRules.add(SqlTypeName.TIMESTAMP_WITH_LOCAL_TIME_ZONE, + coerceRules.copyValues(SqlTypeName.TIMESTAMP_WITH_LOCAL_TIME_ZONE) + .add(SqlTypeName.TIMESTAMP) + .add(SqlTypeName.TIMESTAMP_WITH_LOCAL_TIME_ZONE) + .add(SqlTypeName.DATE) + .add(SqlTypeName.TIME) + .add(SqlTypeName.TIME_WITH_LOCAL_TIME_ZONE) + .add(SqlTypeName.CHAR) + .add(SqlTypeName.VARCHAR) + .build()); + + INSTANCE = new SqlTypeAssignmentRules(rules.map); + COERCE_INSTANCE = new SqlTypeAssignmentRules(coerceRules.map); } //~ Methods ---------------------------------------------------------------- + /** Returns an instance that does not coerce. */ public static synchronized SqlTypeAssignmentRules instance() { - if (instance == null) { - instance = new SqlTypeAssignmentRules(); - } - return instance; + return instance(false); + } + + /** Returns an instance. */ + public static synchronized SqlTypeAssignmentRules instance(boolean coerce) { + return coerce ? COERCE_INSTANCE : INSTANCE; } + @Deprecated public boolean canCastFrom( SqlTypeName to, SqlTypeName from, boolean coerce) { - assert to != null; - assert from != null; + return instance(coerce).canCastFrom(to, from); + } - Map<SqlTypeName, Set<SqlTypeName>> ruleset = - coerce ? coerceRules : rules; + /** Returns whether it is valid to cast a value of from type {@code from} to + * type {@code to}. */ + public boolean canCastFrom( + SqlTypeName to, + SqlTypeName from) { + Preconditions.checkNotNull(to); + Preconditions.checkNotNull(from); if (to == SqlTypeName.NULL) { return false; @@ -356,7 +380,7 @@ public class SqlTypeAssignmentRules { return true; } - final Set<SqlTypeName> rule = ruleset.get(to); + final Set<SqlTypeName> rule = map.get(to); if (rule == null) { // if you hit this assert, see the constructor of this class on how // to add new rule @@ -366,21 +390,43 @@ public class SqlTypeAssignmentRules { return rule.contains(from); } - @SuppressWarnings("unchecked") - private static <K, V> Map<K, V> copy(Map<K, V> map) { - Map<K, V> copy = new HashMap<>(); - for (Map.Entry<K, V> e : map.entrySet()) { - if (e.getValue() instanceof Set) { - copy.put(e.getKey(), (V) copy((Set) e.getValue())); - } else { - copy.put(e.getKey(), e.getValue()); + + /** Keeps state while maps are building build. */ + private static class Builder { + final Map<SqlTypeName, ImmutableSet<SqlTypeName>> map; + final LoadingCache<Set<SqlTypeName>, ImmutableSet<SqlTypeName>> sets; + + /** Creates an empty Builder. */ + Builder() { + this.map = new HashMap<>(); + this.sets = + CacheBuilder.newBuilder().build( + new CacheLoader<Set<SqlTypeName>, ImmutableSet<SqlTypeName>>() { + public ImmutableSet<SqlTypeName> load( + @Nonnull Set<SqlTypeName> key) { + return Sets.immutableEnumSet(key); + } + }); + } + + /** Creates a Builder as a copy of another Builder. */ + Builder(Builder builder) { + this.map = new HashMap<>(builder.map); + this.sets = builder.sets; // share the same canonical sets + } + + void add(SqlTypeName fromType, Set<SqlTypeName> toTypes) { + try { + map.put(fromType, sets.get(toTypes)); + } catch (ExecutionException e) { + throw new RuntimeException("populating SqlTypeAssignmentRules", e); } } - return copy; - } - private static <T> HashSet<T> copy(Set<T> set) { - return new HashSet<T>(set); + ImmutableSet.Builder<SqlTypeName> copyValues(SqlTypeName typeName) { + return ImmutableSet.<SqlTypeName>builder() + .addAll(map.get(typeName)); + } } } http://git-wip-us.apache.org/repos/asf/calcite/blob/becb6dfb/core/src/main/java/org/apache/calcite/sql/type/SqlTypeUtil.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/sql/type/SqlTypeUtil.java b/core/src/main/java/org/apache/calcite/sql/type/SqlTypeUtil.java index 9113dd8..c8fbe03 100644 --- a/core/src/main/java/org/apache/calcite/sql/type/SqlTypeUtil.java +++ b/core/src/main/java/org/apache/calcite/sql/type/SqlTypeUtil.java @@ -859,8 +859,8 @@ public abstract class SqlTypeUtil { // where internally a cast across character repertoires is OK. Should // probably clean that up. - SqlTypeAssignmentRules rules = SqlTypeAssignmentRules.instance(); - return rules.canCastFrom(toTypeName, fromTypeName, coerce); + SqlTypeAssignmentRules rules = SqlTypeAssignmentRules.instance(coerce); + return rules.canCastFrom(toTypeName, fromTypeName); } /** http://git-wip-us.apache.org/repos/asf/calcite/blob/becb6dfb/core/src/test/java/org/apache/calcite/test/RexProgramTest.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/org/apache/calcite/test/RexProgramTest.java b/core/src/test/java/org/apache/calcite/test/RexProgramTest.java index 9501959..94eb792 100644 --- a/core/src/test/java/org/apache/calcite/test/RexProgramTest.java +++ b/core/src/test/java/org/apache/calcite/test/RexProgramTest.java @@ -69,7 +69,6 @@ import org.junit.Test; import java.math.BigDecimal; import java.util.ArrayList; import java.util.Arrays; -import java.util.Calendar; import java.util.List; import java.util.Map; import java.util.TimeZone; @@ -1597,8 +1596,8 @@ public class RexProgramTest { for (RelDataType fromType : types) { for (RelDataType toType : types) { - if (SqlTypeAssignmentRules.instance().canCastFrom( - toType.getSqlTypeName(), fromType.getSqlTypeName(), false)) { + if (SqlTypeAssignmentRules.instance(false) + .canCastFrom(toType.getSqlTypeName(), fromType.getSqlTypeName())) { for (RexLiteral literal : map.get(fromType.getSqlTypeName())) { final RexNode cast = rexBuilder.makeCast(toType, literal); if (cast instanceof RexLiteral) { @@ -1824,17 +1823,6 @@ public class RexProgramTest { RexUtil.retainDeterministic(RelOptUtil.conjunctions(n)).size()); } - private Calendar cal(int y, int m, int d, int h, int mm, int s) { - final Calendar c = Util.calendar(); - c.set(Calendar.YEAR, y); - c.set(Calendar.MONTH, m); - c.set(Calendar.DAY_OF_MONTH, d); - c.set(Calendar.HOUR_OF_DAY, h); - c.set(Calendar.MINUTE, mm); - c.set(Calendar.SECOND, s); - return c; - } - @Test public void testConstantMap() { final RelDataType intType = typeFactory.createSqlType(SqlTypeName.INTEGER); final RelDataType rowType = typeFactory.builder() @@ -1865,7 +1853,6 @@ public class RexProgramTest { // Contradictory constraints yield no constants final RexNode ref0 = rexBuilder.makeInputRef(rowType, 0); - final RexNode ref1 = rexBuilder.makeInputRef(rowType, 1); final ImmutableMap<RexNode, RexNode> map2 = RexUtil.predicateConstants(RexNode.class, rexBuilder, ImmutableList.of(eq(ref0, literal1),
