[CALCITE-1690] Calcite timestamp literals cannot express precision above millisecond, TIMESTAMP(3)
Project: http://git-wip-us.apache.org/repos/asf/calcite/repo Commit: http://git-wip-us.apache.org/repos/asf/calcite/commit/205af813 Tree: http://git-wip-us.apache.org/repos/asf/calcite/tree/205af813 Diff: http://git-wip-us.apache.org/repos/asf/calcite/diff/205af813 Branch: refs/heads/master Commit: 205af8134857ba312415b9a5b4a48020fe0ce888 Parents: 915c218 Author: Julian Hyde <[email protected]> Authored: Mon May 1 21:53:12 2017 -0700 Committer: Julian Hyde <[email protected]> Committed: Wed May 31 08:17:28 2017 -0700 ---------------------------------------------------------------------- core/src/main/codegen/templates/Parser.jj | 73 +---- .../adapter/enumerable/RexToLixTranslator.java | 40 +-- .../apache/calcite/interpreter/Interpreter.java | 2 +- .../apache/calcite/interpreter/SortNode.java | 5 +- .../java/org/apache/calcite/plan/Strong.java | 2 +- .../calcite/rel/metadata/RelMdCollation.java | 8 +- .../rel/metadata/RelMdColumnUniqueness.java | 4 +- .../apache/calcite/rel/metadata/RelMdSize.java | 6 +- .../apache/calcite/rel/metadata/RelMdUtil.java | 19 +- .../calcite/rel/rel2sql/SqlImplementor.java | 25 +- .../calcite/rel/rules/DateRangeRules.java | 7 +- .../java/org/apache/calcite/rex/RexBuilder.java | 124 +++++---- .../java/org/apache/calcite/rex/RexLiteral.java | 275 +++++++++++++++---- .../org/apache/calcite/rex/RexSimplify.java | 10 +- .../calcite/rex/RexToSqlNodeConverterImpl.java | 18 +- .../calcite/sql/SqlAbstractDateTimeLiteral.java | 81 +----- .../org/apache/calcite/sql/SqlDateLiteral.java | 23 +- .../java/org/apache/calcite/sql/SqlLiteral.java | 96 ++++++- .../org/apache/calcite/sql/SqlTimeLiteral.java | 48 +--- .../apache/calcite/sql/SqlTimestampLiteral.java | 49 +--- .../calcite/sql/parser/SqlParserUtil.java | 85 ++++++ .../calcite/sql/type/SqlTypeFactoryImpl.java | 8 + .../apache/calcite/sql/type/SqlTypeName.java | 15 +- .../calcite/sql/validate/SqlValidatorImpl.java | 2 +- .../sql2rel/SqlNodeToRexConverterImpl.java | 35 +-- .../org/apache/calcite/util/BasicDatetime.java | 63 ----- .../org/apache/calcite/util/DateString.java | 98 +++++++ .../org/apache/calcite/util/TimeString.java | 192 +++++++++++++ .../apache/calcite/util/TimestampString.java | 252 +++++++++++++++++ .../main/java/org/apache/calcite/util/Util.java | 20 +- .../org/apache/calcite/util/ZonelessDate.java | 135 --------- .../apache/calcite/util/ZonelessDatetime.java | 199 -------------- .../org/apache/calcite/util/ZonelessTime.java | 175 ------------ .../apache/calcite/util/ZonelessTimestamp.java | 160 ----------- .../rel/rel2sql/RelToSqlConverterTest.java | 20 ++ .../org/apache/calcite/rex/RexBuilderTest.java | 209 +++++++++++++- .../org/apache/calcite/rex/RexExecutorTest.java | 16 +- .../calcite/sql/parser/SqlParserTest.java | 11 +- .../calcite/sql/test/SqlOperatorBaseTest.java | 6 +- .../calcite/test/RexImplicationCheckerTest.java | 34 +-- .../org/apache/calcite/test/RexProgramTest.java | 10 +- core/src/test/resources/sql/misc.iq | 2 +- .../adapter/druid/DruidDateTimeUtils.java | 57 ++-- .../calcite/test/DruidDateRangeRulesTest.java | 14 +- 44 files changed, 1505 insertions(+), 1228 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/calcite/blob/205af813/core/src/main/codegen/templates/Parser.jj ---------------------------------------------------------------------- diff --git a/core/src/main/codegen/templates/Parser.jj b/core/src/main/codegen/templates/Parser.jj index b776e92..3543da6 100644 --- a/core/src/main/codegen/templates/Parser.jj +++ b/core/src/main/codegen/templates/Parser.jj @@ -411,63 +411,6 @@ JAVACODE void checkNonQueryExpression(ExprContext exprContext) } } -// The date/time parse utilities have to live here, instead of in the -// SqlParserUtil class because ParseException is ambiguous, and -// CommonParser has to live in multiple packages. - -JAVACODE SqlDateLiteral parseDateLiteral(String s, SqlParserPos pos) { - String dateStr = SqlParserUtil.parseString(s); - Calendar cal = DateTimeUtils.parseDateFormat( - dateStr, DateTimeUtils.DATE_FORMAT_STRING, DateTimeUtils.UTC_ZONE); - if (null == cal) { - throw SqlUtil.newContextException(pos, - RESOURCE.illegalLiteral("DATE", s, - RESOURCE.badFormat(DateTimeUtils.DATE_FORMAT_STRING).str())); - } - return SqlLiteral.createDate(cal, pos); -} - -JAVACODE SqlTimeLiteral parseTimeLiteral(String s, SqlParserPos pos) { - String dateStr = SqlParserUtil.parseString(s); - DateTimeUtils.PrecisionTime pt = - DateTimeUtils.parsePrecisionDateTimeLiteral( - dateStr, DateTimeUtils.TIME_FORMAT_STRING, DateTimeUtils.UTC_ZONE); - if (null == pt) { - throw SqlUtil.newContextException(pos, - RESOURCE.illegalLiteral("TIME", s, - RESOURCE.badFormat(DateTimeUtils.TIME_FORMAT_STRING).str())); - } - return SqlLiteral.createTime(pt.getCalendar(), pt.getPrecision(), pos); -} - -JAVACODE SqlTimestampLiteral parseTimestampLiteral(String s, SqlParserPos pos) { - String dateStr = SqlParserUtil.parseString(s); - DateTimeUtils.PrecisionTime pt = - DateTimeUtils.parsePrecisionDateTimeLiteral( - dateStr, DateTimeUtils.TIMESTAMP_FORMAT_STRING, DateTimeUtils.UTC_ZONE); - if (null == pt) { - throw SqlUtil.newContextException(pos, - RESOURCE.illegalLiteral("TIMESTAMP", s, - RESOURCE.badFormat(DateTimeUtils.TIMESTAMP_FORMAT_STRING).str())); - } - return SqlLiteral.createTimestamp(pt.getCalendar(), pt.getPrecision(), pos); -} - -JAVACODE SqlIntervalLiteral parseIntervalLiteral( - SqlParserPos pos, - int sign, - String s, - SqlIntervalQualifier intervalQualifier) throws ParseException -{ - String intervalStr = SqlParserUtil.parseString(s); - if ("".equals(intervalStr)) { - throw new ParseException( - RESOURCE.illegalIntervalLiteral(s + " " - + intervalQualifier.toString(), pos.toString()).str()); - } - return SqlLiteral.createInterval(sign, intervalStr, intervalQualifier, pos); -} - /** * Converts a ParseException (local to this particular instantiation * of the parser) into a SqlParseException (common to all parsers). @@ -3693,33 +3636,33 @@ SqlLiteral DateTimeLiteral() : p = token.image; } <RBRACE> { - return parseDateLiteral(p, getPos()); + return SqlParserUtil.parseDateLiteral(p, getPos()); } | <LBRACE_T> <QUOTED_STRING> { p = token.image; } <RBRACE> { - return parseTimeLiteral(p, getPos()); + return SqlParserUtil.parseTimeLiteral(p, getPos()); } | <LBRACE_TS> { s = span(); } <QUOTED_STRING> { p = token.image; } <RBRACE> { - return parseTimestampLiteral(p, s.end(this)); + return SqlParserUtil.parseTimestampLiteral(p, s.end(this)); } | <DATE> { s = span(); } <QUOTED_STRING> { - return parseDateLiteral(token.image, s.end(this)); + return SqlParserUtil.parseDateLiteral(token.image, s.end(this)); } | <TIME> { s = span(); } <QUOTED_STRING> { - return parseTimeLiteral(token.image, s.end(this)); + return SqlParserUtil.parseTimeLiteral(token.image, s.end(this)); } | <TIMESTAMP> { s = span(); } <QUOTED_STRING> { - return parseTimestampLiteral(token.image, s.end(this)); + return SqlParserUtil.parseTimestampLiteral(token.image, s.end(this)); } } @@ -3862,8 +3805,8 @@ SqlLiteral IntervalLiteral() : ] <QUOTED_STRING> { p = token.image; } intervalQualifier = IntervalQualifier() { - return parseIntervalLiteral(s.end(intervalQualifier), sign, p, - intervalQualifier); + return SqlParserUtil.parseIntervalLiteral(s.end(intervalQualifier), + sign, p, intervalQualifier); } } http://git-wip-us.apache.org/repos/asf/calcite/blob/205af813/core/src/main/java/org/apache/calcite/adapter/enumerable/RexToLixTranslator.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/RexToLixTranslator.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/RexToLixTranslator.java index 7d5ef75..10e969f 100644 --- a/core/src/main/java/org/apache/calcite/adapter/enumerable/RexToLixTranslator.java +++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/RexToLixTranslator.java @@ -48,7 +48,6 @@ import org.apache.calcite.sql.SqlOperator; import org.apache.calcite.sql.type.SqlTypeUtil; import org.apache.calcite.util.BuiltInMethod; import org.apache.calcite.util.ControlFlowException; -import org.apache.calcite.util.NlsString; import org.apache.calcite.util.Pair; import org.apache.calcite.util.Util; @@ -59,7 +58,6 @@ import java.lang.reflect.Modifier; import java.lang.reflect.Type; import java.math.BigDecimal; import java.util.ArrayList; -import java.util.Calendar; import java.util.Collections; import java.util.LinkedHashMap; import java.util.List; @@ -588,8 +586,7 @@ public class RexToLixTranslator { RelDataType type, JavaTypeFactory typeFactory, RexImpTable.NullAs nullAs) { - final Comparable value = literal.getValue(); - if (value == null) { + if (literal.isNull()) { switch (nullAs) { case TRUE: case IS_NULL: @@ -615,26 +612,22 @@ public class RexToLixTranslator { final Object value2; switch (literal.getType().getSqlTypeName()) { case DECIMAL: + final BigDecimal bd = literal.getValueAs(BigDecimal.class); if (javaClass == float.class) { - return Expressions.constant(value, javaClass); + return Expressions.constant(bd, javaClass); } assert javaClass == BigDecimal.class; return Expressions.new_(BigDecimal.class, - Expressions.constant(value.toString())); + Expressions.constant(bd.toString())); case DATE: - value2 = (int) - (((Calendar) value).getTimeInMillis() / DateTimeUtils.MILLIS_PER_DAY); - javaClass = int.class; - break; case TIME: - value2 = (int) - (((Calendar) value).getTimeInMillis() % DateTimeUtils.MILLIS_PER_DAY); + case INTERVAL_YEAR: + case INTERVAL_YEAR_MONTH: + case INTERVAL_MONTH: + value2 = literal.getValueAs(Integer.class); javaClass = int.class; break; case TIMESTAMP: - value2 = ((Calendar) value).getTimeInMillis(); - javaClass = long.class; - break; case INTERVAL_DAY: case INTERVAL_DAY_HOUR: case INTERVAL_DAY_MINUTE: @@ -645,32 +638,27 @@ public class RexToLixTranslator { case INTERVAL_MINUTE: case INTERVAL_MINUTE_SECOND: case INTERVAL_SECOND: - value2 = ((BigDecimal) value).longValue(); + value2 = literal.getValueAs(Long.class); javaClass = long.class; break; - case INTERVAL_YEAR: - case INTERVAL_YEAR_MONTH: - case INTERVAL_MONTH: - value2 = ((BigDecimal) value).intValue(); - javaClass = int.class; - break; case CHAR: case VARCHAR: - value2 = ((NlsString) value).getValue(); + value2 = literal.getValueAs(String.class); break; case BINARY: case VARBINARY: return Expressions.new_( ByteString.class, Expressions.constant( - ((ByteString) value).getBytes(), + literal.getValueAs(byte[].class), byte[].class)); case SYMBOL: - value2 = value; - javaClass = value.getClass(); + value2 = literal.getValueAs(Enum.class); + javaClass = value2.getClass(); break; default: final Primitive primitive = Primitive.ofBoxOr(javaClass); + final Comparable value = literal.getValueAs(Comparable.class); if (primitive != null && value instanceof Number) { value2 = primitive.number((Number) value); } else { http://git-wip-us.apache.org/repos/asf/calcite/blob/205af813/core/src/main/java/org/apache/calcite/interpreter/Interpreter.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/interpreter/Interpreter.java b/core/src/main/java/org/apache/calcite/interpreter/Interpreter.java index 51aa082..16b70de 100644 --- a/core/src/main/java/org/apache/calcite/interpreter/Interpreter.java +++ b/core/src/main/java/org/apache/calcite/interpreter/Interpreter.java @@ -238,7 +238,7 @@ public class Interpreter extends AbstractEnumerable<Object[]> public Object execute(Context context) { switch (node.getKind()) { case LITERAL: - return ((RexLiteral) node).getValue(); + return ((RexLiteral) node).getValueAs(Comparable.class); case INPUT_REF: return context.values[((RexInputRef) node).getIndex()]; default: http://git-wip-us.apache.org/repos/asf/calcite/blob/205af813/core/src/main/java/org/apache/calcite/interpreter/SortNode.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/interpreter/SortNode.java b/core/src/main/java/org/apache/calcite/interpreter/SortNode.java index e2c0eeb..5eaaaf6 100644 --- a/core/src/main/java/org/apache/calcite/interpreter/SortNode.java +++ b/core/src/main/java/org/apache/calcite/interpreter/SortNode.java @@ -25,7 +25,6 @@ import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import com.google.common.collect.Ordering; -import java.math.BigDecimal; import java.util.Collections; import java.util.Comparator; import java.util.List; @@ -43,11 +42,11 @@ public class SortNode extends AbstractSingleNode<Sort> { final int offset = rel.offset == null ? 0 - : ((BigDecimal) ((RexLiteral) rel.offset).getValue()).intValue(); + : ((RexLiteral) rel.offset).getValueAs(Integer.class); final int fetch = rel.fetch == null ? -1 - : ((BigDecimal) ((RexLiteral) rel.fetch).getValue()).intValue(); + : ((RexLiteral) rel.fetch).getValueAs(Integer.class); // In pure limit mode. No sort required. Row row; loop: http://git-wip-us.apache.org/repos/asf/calcite/blob/205af813/core/src/main/java/org/apache/calcite/plan/Strong.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/plan/Strong.java b/core/src/main/java/org/apache/calcite/plan/Strong.java index 99a1b4e..e631d90 100644 --- a/core/src/main/java/org/apache/calcite/plan/Strong.java +++ b/core/src/main/java/org/apache/calcite/plan/Strong.java @@ -118,7 +118,7 @@ public class Strong { switch (node.getKind()) { case LITERAL: - return ((RexLiteral) node).getValue() == null; + return ((RexLiteral) node).isNull(); // We can only guarantee AND to return NULL if both inputs are NULL (similar for OR) // AND(NULL, FALSE) = FALSE case AND: http://git-wip-us.apache.org/repos/asf/calcite/blob/205af813/core/src/main/java/org/apache/calcite/rel/metadata/RelMdCollation.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdCollation.java b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdCollation.java index 2867657..32596ed 100644 --- a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdCollation.java +++ b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdCollation.java @@ -335,16 +335,16 @@ public class RelMdCollation case ASCENDING: return new Ordering<List<RexLiteral>>() { public int compare(List<RexLiteral> o1, List<RexLiteral> o2) { - final Comparable c1 = o1.get(x).getValue(); - final Comparable c2 = o2.get(x).getValue(); + final Comparable c1 = o1.get(x).getValueAs(Comparable.class); + final Comparable c2 = o2.get(x).getValueAs(Comparable.class); return RelFieldCollation.compare(c1, c2, nullComparison); } }; default: return new Ordering<List<RexLiteral>>() { public int compare(List<RexLiteral> o1, List<RexLiteral> o2) { - final Comparable c1 = o1.get(x).getValue(); - final Comparable c2 = o2.get(x).getValue(); + final Comparable c1 = o1.get(x).getValueAs(Comparable.class); + final Comparable c2 = o2.get(x).getValueAs(Comparable.class); return RelFieldCollation.compare(c2, c1, -nullComparison); } }; http://git-wip-us.apache.org/repos/asf/calcite/blob/205af813/core/src/main/java/org/apache/calcite/rel/metadata/RelMdColumnUniqueness.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdColumnUniqueness.java b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdColumnUniqueness.java index 9b5d38c..e7032c6 100644 --- a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdColumnUniqueness.java +++ b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdColumnUniqueness.java @@ -333,7 +333,9 @@ public class RelMdColumnUniqueness final List<Comparable> values = new ArrayList<>(); for (ImmutableList<RexLiteral> tuple : rel.tuples) { for (RexLiteral literal : tuple) { - values.add(NullSentinel.mask(literal.getValue())); + values.add(literal.isNull() + ? NullSentinel.INSTANCE + : literal.getValueAs(Comparable.class)); } if (!set.add(ImmutableList.copyOf(values))) { return false; http://git-wip-us.apache.org/repos/asf/calcite/blob/205af813/core/src/main/java/org/apache/calcite/rel/metadata/RelMdSize.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdSize.java b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdSize.java index 744d241..bd12347 100644 --- a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdSize.java +++ b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdSize.java @@ -145,7 +145,8 @@ public class RelMdSize implements MetadataHandler<BuiltInMetadata.Size> { } else { d = 0; for (ImmutableList<RexLiteral> literals : rel.getTuples()) { - d += typeValueSize(field.getType(), literals.get(i).getValue()); + d += typeValueSize(field.getType(), + literals.get(i).getValueAs(Comparable.class)); } d /= rel.getTuples().size(); } @@ -372,7 +373,8 @@ public class RelMdSize implements MetadataHandler<BuiltInMetadata.Size> { case INPUT_REF: return inputColumnSizes.get(((RexInputRef) node).getIndex()); case LITERAL: - return typeValueSize(node.getType(), ((RexLiteral) node).getValue()); + return typeValueSize(node.getType(), + ((RexLiteral) node).getValueAs(Comparable.class)); default: if (node instanceof RexCall) { RexCall call = (RexCall) node; http://git-wip-us.apache.org/repos/asf/calcite/blob/205af813/core/src/main/java/org/apache/calcite/rel/metadata/RelMdUtil.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdUtil.java b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdUtil.java index 7b63ac9..6a896ad 100644 --- a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdUtil.java +++ b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdUtil.java @@ -86,24 +86,23 @@ public class RelMdUtil { RexBuilder rexBuilder = rel.getCluster().getRexBuilder(); double selectivity = computeSemiJoinSelectivity(mq, rel.getLeft(), rel.getRight(), rel); - RexNode selec = - rexBuilder.makeApproxLiteral(new BigDecimal(selectivity)); - return rexBuilder.makeCall(ARTIFICIAL_SELECTIVITY_FUNC, selec); + return rexBuilder.makeCall(ARTIFICIAL_SELECTIVITY_FUNC, + rexBuilder.makeApproxLiteral(new BigDecimal(selectivity))); } /** - * Returns the selectivity value stored in the rexnode + * Returns the selectivity value stored in a call. * - * @param artificialSelecFuncNode rexnode containing the selectivity value + * @param artificialSelectivityFuncNode Call containing the selectivity value * @return selectivity value */ - public static double getSelectivityValue(RexNode artificialSelecFuncNode) { - assert artificialSelecFuncNode instanceof RexCall; - RexCall call = (RexCall) artificialSelecFuncNode; + public static double getSelectivityValue( + RexNode artificialSelectivityFuncNode) { + assert artificialSelectivityFuncNode instanceof RexCall; + RexCall call = (RexCall) artificialSelectivityFuncNode; assert call.getOperator() == ARTIFICIAL_SELECTIVITY_FUNC; RexNode operand = call.getOperands().get(0); - BigDecimal bd = (BigDecimal) ((RexLiteral) operand).getValue(); - return bd.doubleValue(); + return ((RexLiteral) operand).getValueAs(Double.class); } /** http://git-wip-us.apache.org/repos/asf/calcite/blob/205af813/core/src/main/java/org/apache/calcite/rel/rel2sql/SqlImplementor.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/rel/rel2sql/SqlImplementor.java b/core/src/main/java/org/apache/calcite/rel/rel2sql/SqlImplementor.java index 826cebc..1e477d2 100644 --- a/core/src/main/java/org/apache/calcite/rel/rel2sql/SqlImplementor.java +++ b/core/src/main/java/org/apache/calcite/rel/rel2sql/SqlImplementor.java @@ -67,6 +67,9 @@ import org.apache.calcite.sql.type.SqlTypeFamily; import org.apache.calcite.sql.type.SqlTypeName; import org.apache.calcite.sql.type.SqlTypeUtil; import org.apache.calcite.sql.validate.SqlValidatorUtil; +import org.apache.calcite.util.DateString; +import org.apache.calcite.util.TimeString; +import org.apache.calcite.util.TimestampString; import org.apache.calcite.util.Util; import com.google.common.base.Preconditions; @@ -78,9 +81,9 @@ import com.google.common.collect.Iterables; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.math.BigDecimal; import java.util.AbstractList; import java.util.ArrayList; -import java.util.Calendar; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; @@ -481,7 +484,8 @@ public abstract class SqlImplementor { || node instanceof SqlMatchRecognize || node instanceof SqlCall && (((SqlCall) node).getOperator() instanceof SqlSetOperator - || ((SqlCall) node).getOperator() == SqlStdOperatorTable.AS) + || ((SqlCall) node).getOperator() == SqlStdOperatorTable.AS + || ((SqlCall) node).getOperator() == SqlStdOperatorTable.VALUES) : node; return new SqlSelect(POS, SqlNodeList.EMPTY, null, node, null, null, null, SqlNodeList.EMPTY, null, null, null); @@ -542,20 +546,23 @@ public abstract class SqlImplementor { return SqlLiteral.createCharString((String) literal.getValue2(), POS); case NUMERIC: case EXACT_NUMERIC: - return SqlLiteral.createExactNumeric(literal.getValue().toString(), - POS); + return SqlLiteral.createExactNumeric( + literal.getValueAs(BigDecimal.class).toString(), POS); case APPROXIMATE_NUMERIC: return SqlLiteral.createApproxNumeric( - literal.getValue().toString(), POS); + literal.getValueAs(BigDecimal.class).toString(), POS); case BOOLEAN: - return SqlLiteral.createBoolean((Boolean) literal.getValue(), POS); + return SqlLiteral.createBoolean(literal.getValueAs(Boolean.class), + POS); case DATE: - return SqlLiteral.createDate((Calendar) literal.getValue(), POS); + return SqlLiteral.createDate(literal.getValueAs(DateString.class), + POS); case TIME: - return SqlLiteral.createTime((Calendar) literal.getValue(), + return SqlLiteral.createTime(literal.getValueAs(TimeString.class), literal.getType().getPrecision(), POS); case TIMESTAMP: - return SqlLiteral.createTimestamp((Calendar) literal.getValue(), + return SqlLiteral.createTimestamp( + literal.getValueAs(TimestampString.class), literal.getType().getPrecision(), POS); case ANY: case NULL: http://git-wip-us.apache.org/repos/asf/calcite/blob/205af813/core/src/main/java/org/apache/calcite/rel/rules/DateRangeRules.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/rel/rules/DateRangeRules.java b/core/src/main/java/org/apache/calcite/rel/rules/DateRangeRules.java index 1bfb813..2a185a1 100644 --- a/core/src/main/java/org/apache/calcite/rel/rules/DateRangeRules.java +++ b/core/src/main/java/org/apache/calcite/rel/rules/DateRangeRules.java @@ -35,6 +35,7 @@ import org.apache.calcite.sql.fun.SqlStdOperatorTable; import org.apache.calcite.tools.RelBuilder; import org.apache.calcite.tools.RelBuilderFactory; import org.apache.calcite.util.Bug; +import org.apache.calcite.util.DateString; import org.apache.calcite.util.Util; import com.google.common.base.Predicate; @@ -355,7 +356,8 @@ public abstract class DateRangeRules { : SqlStdOperatorTable.GREATER_THAN; nodes.add( rexBuilder.makeCall(op, operand, - rexBuilder.makeDateLiteral(r.lowerEndpoint()))); + rexBuilder.makeDateLiteral( + DateString.fromCalendarFields(r.lowerEndpoint())))); } if (r.hasUpperBound()) { final SqlBinaryOperator op = r.upperBoundType() == BoundType.CLOSED @@ -363,7 +365,8 @@ public abstract class DateRangeRules { : SqlStdOperatorTable.LESS_THAN; nodes.add( rexBuilder.makeCall(op, operand, - rexBuilder.makeDateLiteral(r.upperEndpoint()))); + rexBuilder.makeDateLiteral( + DateString.fromCalendarFields(r.upperEndpoint())))); } return RexUtil.composeConjunction(rexBuilder, nodes, false); } http://git-wip-us.apache.org/repos/asf/calcite/blob/205af813/core/src/main/java/org/apache/calcite/rex/RexBuilder.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/rex/RexBuilder.java b/core/src/main/java/org/apache/calcite/rex/RexBuilder.java index 1906227..adf7fee 100644 --- a/core/src/main/java/org/apache/calcite/rex/RexBuilder.java +++ b/core/src/main/java/org/apache/calcite/rex/RexBuilder.java @@ -28,7 +28,6 @@ import org.apache.calcite.rel.type.RelDataType; import org.apache.calcite.rel.type.RelDataTypeFactory; import org.apache.calcite.rel.type.RelDataTypeField; import org.apache.calcite.runtime.FlatLists; -import org.apache.calcite.runtime.SqlFunctions; import org.apache.calcite.sql.SqlAggFunction; import org.apache.calcite.sql.SqlIntervalQualifier; import org.apache.calcite.sql.SqlKind; @@ -43,8 +42,11 @@ import org.apache.calcite.sql.type.MultisetSqlType; import org.apache.calcite.sql.type.SqlTypeFamily; import org.apache.calcite.sql.type.SqlTypeName; import org.apache.calcite.sql.type.SqlTypeUtil; +import org.apache.calcite.util.DateString; import org.apache.calcite.util.NlsString; import org.apache.calcite.util.Pair; +import org.apache.calcite.util.TimeString; +import org.apache.calcite.util.TimestampString; import org.apache.calcite.util.Util; import com.google.common.base.Function; @@ -477,21 +479,6 @@ public class RexBuilder { } /** - * Rounds the time part of a TIME or TIMESTAMP value to the given precision. - * - * @param timestamp The value to be rounded, will change in place - * @param precision the desired precision - */ - private void roundTime(Calendar timestamp, long precision) { - if (precision == RelDataType.PRECISION_NOT_SPECIFIED) { - precision = 0; - } - final long pow = DateTimeUtils.powerX(10, 3 - precision); - final long timeMs = SqlFunctions.round(timestamp.getTimeInMillis(), pow); - timestamp.setTimeInMillis(timeMs); - } - - /** * Creates a call to the CAST operator, expanding if possible, and optionally * also preserving nullability. * @@ -512,7 +499,7 @@ public class RexBuilder { final SqlTypeName sqlType = type.getSqlTypeName(); if (exp instanceof RexLiteral) { RexLiteral literal = (RexLiteral) exp; - Comparable value = literal.getValue(); + Comparable value = literal.getValueAs(Comparable.class); SqlTypeName typeName = literal.getTypeName(); if (canRemoveCastFromLiteral(type, value, typeName)) { switch (typeName) { @@ -836,7 +823,7 @@ public class RexBuilder { /** * Internal method to create a call to a literal. Code outside this package * should call one of the type-specific methods such as - * {@link #makeDateLiteral(Calendar)}, {@link #makeLiteral(boolean)}, + * {@link #makeDateLiteral(DateString)}, {@link #makeLiteral(boolean)}, * {@link #makeLiteral(String)}. * * @param o Value of literal, must be appropriate for the type @@ -850,6 +837,7 @@ public class RexBuilder { SqlTypeName typeName) { // All literals except NULL have NOT NULL types. type = typeFactory.createTypeWithNullability(type, o == null); + int p; switch (typeName) { case CHAR: // Character literals must have a charset and collation. Populate @@ -868,8 +856,21 @@ public class RexBuilder { } break; case TIME: + assert o instanceof TimeString; + p = type.getPrecision(); + if (p == RelDataType.PRECISION_NOT_SPECIFIED) { + p = 0; + } + o = ((TimeString) o).round(p); + break; case TIMESTAMP: - roundTime((Calendar) o, type.getPrecision()); + assert o instanceof TimestampString; + p = type.getPrecision(); + if (p == RelDataType.PRECISION_NOT_SPECIFIED) { + p = 0; + } + o = ((TimestampString) o).round(p); + break; } return new RexLiteral(o, type, typeName); } @@ -1054,37 +1055,48 @@ public class RexBuilder { return makeLiteral(str, type, SqlTypeName.CHAR); } + /** @deprecated Use {@link #makeDateLiteral(DateString)}. */ + @Deprecated // to be removed before 2.0 + public RexLiteral makeDateLiteral(Calendar calendar) { + return makeDateLiteral(DateString.fromCalendarFields(calendar)); + } + /** * Creates a Date literal. */ - public RexLiteral makeDateLiteral(Calendar date) { - assert date != null; - return makeLiteral( - date, typeFactory.createSqlType(SqlTypeName.DATE), SqlTypeName.DATE); + public RexLiteral makeDateLiteral(DateString date) { + return makeLiteral(Preconditions.checkNotNull(date), + typeFactory.createSqlType(SqlTypeName.DATE), SqlTypeName.DATE); + } + + /** @deprecated Use {@link #makeTimeLiteral(TimeString, int)}. */ + @Deprecated // to be removed before 2.0 + public RexLiteral makeTimeLiteral(Calendar calendar, int precision) { + return makeTimeLiteral(TimeString.fromCalendarFields(calendar), precision); } /** * Creates a Time literal. */ - public RexLiteral makeTimeLiteral( - Calendar time, - int precision) { - assert time != null; - return makeLiteral( - time, + public RexLiteral makeTimeLiteral(TimeString time, int precision) { + return makeLiteral(Preconditions.checkNotNull(time), typeFactory.createSqlType(SqlTypeName.TIME, precision), SqlTypeName.TIME); } + /** @deprecated Use {@link #makeTimestampLiteral(TimestampString, int)}. */ + @Deprecated // to be removed before 2.0 + public RexLiteral makeTimestampLiteral(Calendar calendar, int precision) { + return makeTimestampLiteral(TimestampString.fromCalendarFields(calendar), + precision); + } + /** * Creates a Timestamp literal. */ - public RexLiteral makeTimestampLiteral( - Calendar timestamp, + public RexLiteral makeTimestampLiteral(TimestampString timestamp, int precision) { - assert timestamp != null; - return makeLiteral( - timestamp, + return makeLiteral(Preconditions.checkNotNull(timestamp), typeFactory.createSqlType(SqlTypeName.TIMESTAMP, precision), SqlTypeName.TIMESTAMP); } @@ -1275,11 +1287,11 @@ public class RexBuilder { case BOOLEAN: return (Boolean) value ? booleanTrue : booleanFalse; case TIME: - return makeTimeLiteral((Calendar) value, type.getPrecision()); + return makeTimeLiteral((TimeString) value, type.getPrecision()); case DATE: - return makeDateLiteral((Calendar) value); + return makeDateLiteral((DateString) value); case TIMESTAMP: - return makeTimestampLiteral((Calendar) value, type.getPrecision()); + return makeTimestampLiteral((TimestampString) value, type.getPrecision()); case INTERVAL_YEAR: case INTERVAL_YEAR_MONTH: case INTERVAL_MONTH: @@ -1357,7 +1369,6 @@ public class RexBuilder { if (o == null) { return null; } - final Calendar calendar; switch (type.getSqlTypeName()) { case TINYINT: case SMALLINT: @@ -1402,27 +1413,38 @@ public class RexBuilder { return new NlsString((String) o, type.getCharset().name(), type.getCollation()); case TIME: - if (o instanceof Calendar) { + if (o instanceof TimeString) { return o; + } else if (o instanceof Calendar) { + if (!((Calendar) o).getTimeZone().equals(DateTimeUtils.UTC_ZONE)) { + throw new AssertionError(); + } + return TimeString.fromCalendarFields((Calendar) o); + } else { + return TimeString.fromMillisOfDay((Integer) o); } - calendar = Util.calendar(); - calendar.setTimeInMillis((Integer) o); - return calendar; case DATE: - if (o instanceof Calendar) { + if (o instanceof DateString) { return o; + } else if (o instanceof Calendar) { + if (!((Calendar) o).getTimeZone().equals(DateTimeUtils.UTC_ZONE)) { + throw new AssertionError(); + } + return DateString.fromCalendarFields((Calendar) o); + } else { + return DateString.fromDaysSinceEpoch((Integer) o); } - calendar = Util.calendar(); - calendar.setTimeInMillis(0); - calendar.add(Calendar.DAY_OF_YEAR, (Integer) o); - return calendar; case TIMESTAMP: - if (o instanceof Calendar) { + if (o instanceof TimestampString) { return o; + } else if (o instanceof Calendar) { + if (!((Calendar) o).getTimeZone().equals(DateTimeUtils.UTC_ZONE)) { + throw new AssertionError(); + } + return TimestampString.fromCalendarFields((Calendar) o); + } else { + return TimestampString.fromMillisSinceEpoch((Long) o); } - calendar = Util.calendar(); - calendar.setTimeInMillis((Long) o); - return calendar; default: return o; } http://git-wip-us.apache.org/repos/asf/calcite/blob/205af813/core/src/main/java/org/apache/calcite/rex/RexLiteral.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/rex/RexLiteral.java b/core/src/main/java/org/apache/calcite/rex/RexLiteral.java index f9be2bb..97e3055 100644 --- a/core/src/main/java/org/apache/calcite/rex/RexLiteral.java +++ b/core/src/main/java/org/apache/calcite/rex/RexLiteral.java @@ -26,14 +26,13 @@ import org.apache.calcite.sql.fun.SqlStdOperatorTable; import org.apache.calcite.sql.parser.SqlParserUtil; import org.apache.calcite.sql.type.SqlTypeName; import org.apache.calcite.util.ConversionUtil; +import org.apache.calcite.util.DateString; import org.apache.calcite.util.Litmus; import org.apache.calcite.util.NlsString; import org.apache.calcite.util.SaffronProperties; +import org.apache.calcite.util.TimeString; +import org.apache.calcite.util.TimestampString; import org.apache.calcite.util.Util; -import org.apache.calcite.util.ZonelessDate; -import org.apache.calcite.util.ZonelessDatetime; -import org.apache.calcite.util.ZonelessTime; -import org.apache.calcite.util.ZonelessTimestamp; import com.google.common.base.Preconditions; @@ -42,9 +41,11 @@ import java.io.StringWriter; import java.math.BigDecimal; import java.nio.ByteBuffer; import java.nio.charset.Charset; +import java.text.SimpleDateFormat; import java.util.AbstractList; import java.util.Calendar; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.Objects; import java.util.TimeZone; @@ -68,7 +69,7 @@ import java.util.TimeZone; * <caption>Allowable types for RexLiteral instances</caption> * <tr> * <th>TypeName</th> - * <th>Meaing</th> + * <th>Meaning</th> * <th>Value type</th> * </tr> * <tr> @@ -96,32 +97,63 @@ import java.util.TimeZone; * <tr> * <td>{@link SqlTypeName#DATE}</td> * <td>Date, for example <code>DATE '1969-04'29'</code></td> - * <td>{@link Calendar}</td> + * <td>{@link Calendar}; + * also {@link Calendar} (UTC time zone) + * and {@link Integer} (days since POSIX epoch)</td> * </tr> * <tr> * <td>{@link SqlTypeName#TIME}</td> * <td>Time, for example <code>TIME '18:37:42.567'</code></td> - * <td>{@link Calendar}</td> + * <td>{@link Calendar}; + * also {@link Calendar} (UTC time zone) + * and {@link Integer} (milliseconds since midnight)</td> * </tr> * <tr> * <td>{@link SqlTypeName#TIMESTAMP}</td> * <td>Timestamp, for example <code>TIMESTAMP '1969-04-29 * 18:37:42.567'</code></td> - * <td>{@link Calendar}</td> + * <td>{@link TimestampString}; + * also {@link Calendar} (UTC time zone) + * and {@link Long} (milliseconds since POSIX epoch)</td> + * </tr> + * <tr> + * <td>{@link SqlTypeName#INTERVAL_DAY}, + * {@link SqlTypeName#INTERVAL_DAY_HOUR}, + * {@link SqlTypeName#INTERVAL_DAY_MINUTE}, + * {@link SqlTypeName#INTERVAL_DAY_SECOND}, + * {@link SqlTypeName#INTERVAL_HOUR}, + * {@link SqlTypeName#INTERVAL_HOUR_MINUTE}, + * {@link SqlTypeName#INTERVAL_HOUR_SECOND}, + * {@link SqlTypeName#INTERVAL_MINUTE}, + * {@link SqlTypeName#INTERVAL_MINUTE_SECOND}, + * {@link SqlTypeName#INTERVAL_SECOND}</td> + * <td>Interval, for example <code>INTERVAL '4:3:2' HOUR TO SECOND</code></td> + * <td>{@link BigDecimal}; + * also {@link Long} (milliseconds)</td> + * </tr> + * <tr> + * <td>{@link SqlTypeName#INTERVAL_YEAR}, + * {@link SqlTypeName#INTERVAL_YEAR_MONTH}, + * {@link SqlTypeName#INTERVAL_MONTH}</td> + * <td>Interval, for example <code>INTERVAL '2-3' YEAR TO MONTH</code></td> + * <td>{@link BigDecimal}; + * also {@link Integer} (months)</td> * </tr> * <tr> * <td>{@link SqlTypeName#CHAR}</td> * <td>Character constant, for example <code>'Hello, world!'</code>, <code> * ''</code>, <code>_N'Bonjour'</code>, <code>_ISO-8859-1'It''s superman!' * COLLATE SHIFT_JIS$ja_JP$2</code>. These are always CHAR, never VARCHAR.</td> - * <td>{@link NlsString}</td> + * <td>{@link NlsString}; + * also {@link String}</td> * </tr> * <tr> * <td>{@link SqlTypeName#BINARY}</td> * <td>Binary constant, for example <code>X'7F34'</code>. (The number of hexits * must be even; see above.) These constants are always BINARY, never * VARBINARY.</td> - * <td>{@link ByteBuffer}</td> + * <td>{@link ByteBuffer}; + * also {@code byte[]}</td> * </tr> * <tr> * <td>{@link SqlTypeName#SYMBOL}</td> @@ -213,10 +245,11 @@ public class RexLiteral extends RexNode { case BIGINT: return value instanceof BigDecimal; case DATE: + return value instanceof DateString; case TIME: + return value instanceof TimeString; case TIMESTAMP: - return value instanceof Calendar - && ((Calendar) value).getTimeZone().equals(DateTimeUtils.UTC_ZONE); + return value instanceof TimestampString; case INTERVAL_YEAR: case INTERVAL_YEAR_MONTH: case INTERVAL_MONTH: @@ -391,13 +424,16 @@ public class RexLiteral extends RexNode { pw.print(")"); break; case DATE: - printDatetime(pw, new ZonelessDate(), value); + assert value instanceof DateString; + pw.print(value); break; case TIME: - printDatetime(pw, new ZonelessTime(), value); + assert value instanceof TimeString; + pw.print(value); break; case TIMESTAMP: - printDatetime(pw, new ZonelessTimestamp(), value); + assert value instanceof TimestampString; + pw.print(value); break; case INTERVAL_YEAR: case INTERVAL_YEAR_MONTH: @@ -439,16 +475,6 @@ public class RexLiteral extends RexNode { } } - private static void printDatetime( - PrintWriter pw, - ZonelessDatetime datetime, - Comparable value) { - assert value instanceof Calendar; - datetime.setZonelessTime( - ((Calendar) value).getTimeInMillis()); - pw.print(datetime); - } - /** * Converts a Jdbc string into a RexLiteral. This method accepts a string, * as returned by the Jdbc method ResultSet.getString(), and restores the @@ -521,25 +547,44 @@ public class RexLiteral extends RexNode { case TIMESTAMP: String format = getCalendarFormat(typeName); TimeZone tz = DateTimeUtils.UTC_ZONE; - Calendar cal = null; - if (typeName == SqlTypeName.DATE) { - cal = - DateTimeUtils.parseDateFormat(literal, format, tz); - } else { + final Comparable v; + switch (typeName) { + case DATE: + final Calendar cal = DateTimeUtils.parseDateFormat(literal, + new SimpleDateFormat(format, Locale.ROOT), + tz); + if (cal == null) { + throw new AssertionError("fromJdbcString: invalid date/time value '" + + literal + "'"); + } + v = DateString.fromCalendarFields(cal); + break; + default: // Allow fractional seconds for times and timestamps - DateTimeUtils.PrecisionTime ts = - DateTimeUtils.parsePrecisionDateTimeLiteral(literal, format, tz); - if (ts != null) { - cal = ts.getCalendar(); + assert format != null; + final DateTimeUtils.PrecisionTime ts = + DateTimeUtils.parsePrecisionDateTimeLiteral(literal, + new SimpleDateFormat(format, Locale.ROOT), tz, -1); + if (ts == null) { + throw new AssertionError("fromJdbcString: invalid date/time value '" + + literal + "'"); + } + switch (typeName) { + case TIMESTAMP: + v = TimestampString.fromCalendarFields(ts.getCalendar()) + .withFraction(ts.getFraction()); + break; + case TIME: + v = TimeString.fromCalendarFields(ts.getCalendar()) + .withFraction(ts.getFraction()); + break; + default: + throw new AssertionError(); } } - if (cal == null) { - throw new AssertionError("fromJdbcString: invalid date/time value '" - + literal + "'"); - } - return new RexLiteral(cal, type, typeName); - case SYMBOL: + return new RexLiteral(v, type, typeName); + case SYMBOL: // Symbols are for internal use default: throw new AssertionError("fromJdbcString: unsupported type"); @@ -572,11 +617,31 @@ public class RexLiteral extends RexNode { } /** + * Returns whether this literal's value is null. + */ + public boolean isNull() { + return value == null; + } + + /** * Returns the value of this literal. + * + * <p>For backwards compatibility, returns DATE. TIME and TIMESTAMP as a + * {@link Calendar} value in UTC time zone. */ public Comparable getValue() { assert valueMatchesType(value, typeName, true) : value; - return value; + if (value == null) { + return null; + } + switch (typeName) { + case TIME: + case DATE: + case TIMESTAMP: + return getValueAs(Calendar.class); + default: + return value; + } } /** @@ -589,17 +654,13 @@ public class RexLiteral extends RexNode { } switch (typeName) { case CHAR: - return ((NlsString) value).getValue(); + return getValueAs(String.class); case DECIMAL: - return ((BigDecimal) value).unscaledValue().longValue(); + case TIMESTAMP: + return getValueAs(Long.class); case DATE: - return (int) (((Calendar) value).getTimeInMillis() - / DateTimeUtils.MILLIS_PER_DAY); case TIME: - return (int) (((Calendar) value).getTimeInMillis() - % DateTimeUtils.MILLIS_PER_DAY); - case TIMESTAMP: - return ((Calendar) value).getTimeInMillis(); + return getValueAs(Integer.class); default: return value; } @@ -619,6 +680,122 @@ public class RexLiteral extends RexNode { } } + /** Returns the value of this literal as an instance of the specified class. + * + * <p>The following SQL types allow more than one form: + * + * <ul> + * <li>CHAR as {@link NlsString} or {@link String} + * <li>TIME as {@link TimeString}, + * {@link Integer} (milliseconds since midnight), + * {@link Calendar} (in UTC) + * <li>DATE as {@link DateString}, + * {@link Integer} (days since 1970-01-01), + * {@link Calendar} + * <li>TIMESTAMP as {@link TimestampString}, + * {@link Long} (milliseconds since 1970-01-01 00:00:00), + * {@link Calendar} + * <li>DECIMAL as {@link BigDecimal} or {@link Long} + * </ul> + * + * <p>Called with {@code clazz} = {@link Comparable}, returns the value in + * its native form. + * + * @param clazz Desired return type + * @param <T> Return type + * @return Value of this literal in the desired type + */ + public <T> T getValueAs(Class<T> clazz) { + if (value == null || clazz.isInstance(value)) { + return clazz.cast(value); + } + switch (typeName) { + case BINARY: + if (clazz == byte[].class) { + return clazz.cast(((ByteString) value).getBytes()); + } + break; + case CHAR: + if (clazz == String.class) { + return clazz.cast(((NlsString) value).getValue()); + } + break; + case DECIMAL: + if (clazz == Long.class) { + return clazz.cast(((BigDecimal) value).unscaledValue().longValue()); + } + // fall through + case BIGINT: + case INTEGER: + case SMALLINT: + case TINYINT: + case DOUBLE: + case REAL: + case FLOAT: + if (clazz == Long.class) { + return clazz.cast(((BigDecimal) value).longValue()); + } else if (clazz == Integer.class) { + return clazz.cast(((BigDecimal) value).intValue()); + } else if (clazz == Short.class) { + return clazz.cast(((BigDecimal) value).shortValue()); + } else if (clazz == Byte.class) { + return clazz.cast(((BigDecimal) value).byteValue()); + } else if (clazz == Double.class) { + return clazz.cast(((BigDecimal) value).doubleValue()); + } else if (clazz == Float.class) { + return clazz.cast(((BigDecimal) value).floatValue()); + } + break; + case DATE: + if (clazz == Integer.class) { + return clazz.cast(((DateString) value).getDaysSinceEpoch()); + } else if (clazz == Calendar.class) { + return clazz.cast(((DateString) value).toCalendar()); + } + break; + case TIME: + if (clazz == Integer.class) { + return clazz.cast(((TimeString) value).getMillisOfDay()); + } else if (clazz == Calendar.class) { + // Note: Nanos are ignored + return clazz.cast(((TimeString) value).toCalendar()); + } + break; + case TIMESTAMP: + if (clazz == Long.class) { + // Milliseconds since 1970-01-01 00:00:00 + return clazz.cast(((TimestampString) value).getMillisSinceEpoch()); + } else if (clazz == Calendar.class) { + // Note: Nanos are ignored + return clazz.cast(((TimestampString) value).toCalendar()); + } + break; + case INTERVAL_YEAR: + case INTERVAL_YEAR_MONTH: + case INTERVAL_MONTH: + if (clazz == Integer.class) { + return clazz.cast(((BigDecimal) value).intValue()); + } + break; + case INTERVAL_DAY: + case INTERVAL_DAY_HOUR: + case INTERVAL_DAY_MINUTE: + case INTERVAL_DAY_SECOND: + case INTERVAL_HOUR: + case INTERVAL_HOUR_MINUTE: + case INTERVAL_HOUR_SECOND: + case INTERVAL_MINUTE: + case INTERVAL_MINUTE_SECOND: + case INTERVAL_SECOND: + if (clazz == Long.class) { + return clazz.cast(((BigDecimal) value).longValue()); + } + break; + } + throw new AssertionError("cannot convert " + typeName + + " literal to " + clazz); + } + public static boolean booleanValue(RexNode node) { return (Boolean) ((RexLiteral) node).value; } http://git-wip-us.apache.org/repos/asf/calcite/blob/205af813/core/src/main/java/org/apache/calcite/rex/RexSimplify.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/rex/RexSimplify.java b/core/src/main/java/org/apache/calcite/rex/RexSimplify.java index 9ac69f7..34f01a0 100644 --- a/core/src/main/java/org/apache/calcite/rex/RexSimplify.java +++ b/core/src/main/java/org/apache/calcite/rex/RexSimplify.java @@ -177,8 +177,8 @@ public class RexSimplify { if (o0.isA(SqlKind.LITERAL) && o1.isA(SqlKind.LITERAL) && o0.getType().equals(o1.getType())) { - final Comparable v0 = ((RexLiteral) o0).getValue(); - final Comparable v1 = ((RexLiteral) o1).getValue(); + final Comparable v0 = ((RexLiteral) o0).getValueAs(Comparable.class); + final Comparable v1 = ((RexLiteral) o1).getValueAs(Comparable.class); if (v0 == null || v1 == null) { return unknownAsFalse ? rexBuilder.makeLiteral(false) @@ -358,7 +358,7 @@ public class RexSimplify { case CUSTOM: switch (a.getKind()) { case LITERAL: - return rexBuilder.makeLiteral(((RexLiteral) a).getValue() != null); + return rexBuilder.makeLiteral(!((RexLiteral) a).isNull()); default: throw new AssertionError("every CUSTOM policy needs a handler, " + a.getKind()); @@ -793,7 +793,7 @@ public class RexSimplify { switch (operand.getKind()) { case LITERAL: final RexLiteral literal = (RexLiteral) operand; - final Comparable value = literal.getValue(); + final Comparable value = literal.getValueAs(Comparable.class); final SqlTypeName typeName = literal.getTypeName(); // First, try to remove the cast without changing the value. @@ -834,7 +834,7 @@ public class RexSimplify { private static RexNode processRange(RexBuilder rexBuilder, List<RexNode> terms, Map<String, Pair<Range, List<RexNode>>> rangeTerms, RexNode term, RexNode ref, RexLiteral constant, SqlKind comparison) { - final Comparable v0 = constant.getValue(); + final Comparable v0 = constant.getValueAs(Comparable.class); Pair<Range, List<RexNode>> p = rangeTerms.get(ref.toString()); if (p == null) { Range r; http://git-wip-us.apache.org/repos/asf/calcite/blob/205af813/core/src/main/java/org/apache/calcite/rex/RexToSqlNodeConverterImpl.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/rex/RexToSqlNodeConverterImpl.java b/core/src/main/java/org/apache/calcite/rex/RexToSqlNodeConverterImpl.java index 16b4203..f9a8932 100644 --- a/core/src/main/java/org/apache/calcite/rex/RexToSqlNodeConverterImpl.java +++ b/core/src/main/java/org/apache/calcite/rex/RexToSqlNodeConverterImpl.java @@ -20,9 +20,10 @@ import org.apache.calcite.sql.SqlLiteral; import org.apache.calcite.sql.SqlNode; import org.apache.calcite.sql.parser.SqlParserPos; import org.apache.calcite.sql.type.SqlTypeFamily; +import org.apache.calcite.util.DateString; import org.apache.calcite.util.NlsString; - -import java.util.Calendar; +import org.apache.calcite.util.TimeString; +import org.apache.calcite.util.TimestampString; /** * Standard implementation of {@link RexToSqlNodeConverter}. @@ -83,7 +84,7 @@ public class RexToSqlNodeConverterImpl implements RexToSqlNodeConverter { if (SqlTypeFamily.TIMESTAMP.getTypeNames().contains( literal.getTypeName())) { return SqlLiteral.createTimestamp( - (Calendar) literal.getValue(), + literal.getValueAs(TimestampString.class), 0, SqlParserPos.ZERO); } @@ -92,7 +93,16 @@ public class RexToSqlNodeConverterImpl implements RexToSqlNodeConverter { if (SqlTypeFamily.DATE.getTypeNames().contains( literal.getTypeName())) { return SqlLiteral.createDate( - (Calendar) literal.getValue(), + literal.getValueAs(DateString.class), + SqlParserPos.ZERO); + } + + // Time + if (SqlTypeFamily.TIME.getTypeNames().contains( + literal.getTypeName())) { + return SqlLiteral.createTime( + literal.getValueAs(TimeString.class), + 0, SqlParserPos.ZERO); } http://git-wip-us.apache.org/repos/asf/calcite/blob/205af813/core/src/main/java/org/apache/calcite/sql/SqlAbstractDateTimeLiteral.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/sql/SqlAbstractDateTimeLiteral.java b/core/src/main/java/org/apache/calcite/sql/SqlAbstractDateTimeLiteral.java index 324b799..59dfc69 100644 --- a/core/src/main/java/org/apache/calcite/sql/SqlAbstractDateTimeLiteral.java +++ b/core/src/main/java/org/apache/calcite/sql/SqlAbstractDateTimeLiteral.java @@ -20,12 +20,7 @@ import org.apache.calcite.rel.type.RelDataType; import org.apache.calcite.rel.type.RelDataTypeFactory; import org.apache.calcite.sql.parser.SqlParserPos; import org.apache.calcite.sql.type.SqlTypeName; -import org.apache.calcite.util.ZonelessDate; -import org.apache.calcite.util.ZonelessTime; -import org.apache.calcite.util.ZonelessTimestamp; - -import java.util.Calendar; -import java.util.TimeZone; +import org.apache.calcite.util.TimestampString; /** * A SQL literal representing a DATE, TIME or TIMESTAMP value. @@ -42,57 +37,29 @@ abstract class SqlAbstractDateTimeLiteral extends SqlLiteral { //~ Instance fields -------------------------------------------------------- protected final boolean hasTimeZone; - protected final String formatString; protected final int precision; //~ Constructors ----------------------------------------------------------- /** - * Constructs a datetime literal based on a Calendar. If the literal is to - * represent a Timestamp, the Calendar is expected to follow java.sql - * semantics. If the Calendar is to represent a Time or Date, the Calendar - * is expected to follow {@link org.apache.calcite.util.ZonelessTime} - * and {@link org.apache.calcite.util.ZonelessDate} - * semantics. + * Constructs a datetime literal. */ - protected SqlAbstractDateTimeLiteral( - Calendar d, - boolean tz, - SqlTypeName typeName, - int precision, - String formatString, - SqlParserPos pos) { + protected SqlAbstractDateTimeLiteral(Object d, boolean tz, + SqlTypeName typeName, int precision, SqlParserPos pos) { super(d, typeName, pos); this.hasTimeZone = tz; this.precision = precision; - this.formatString = formatString; } //~ Methods ---------------------------------------------------------------- - public int getPrec() { - return precision; - } - - public String toValue() { - return Long.toString(getCal().getTimeInMillis()); - } - - public Calendar getCal() { - return (Calendar) value; + /** Converts this literal to a {@link TimestampString}. */ + protected TimestampString getTimestamp() { + return (TimestampString) value; } - /** - * Returns time zone component of this literal. Technically, a SQL date - * doesn't come with a tz, but time and ts inherit this, and the calendar - * object has one, so it seems harmless. - * - * @return time zone - */ - public TimeZone getTimeZone() { - assert hasTimeZone : "Attempt to get time zone on Literal date: " - + getCal() + ", which has no time zone"; - return getCal().getTimeZone(); + public int getPrec() { + return precision; } /** @@ -117,36 +84,6 @@ abstract class SqlAbstractDateTimeLiteral extends SqlLiteral { int rightPrec) { writer.literal(this.toString()); } - - /** - * Converts this literal to a - * {@link org.apache.calcite.util.ZonelessDate} object. - */ - protected ZonelessDate getDate() { - ZonelessDate zd = new ZonelessDate(); - zd.setZonelessTime(getCal().getTimeInMillis()); - return zd; - } - - /** - * Converts this literal to a - * {@link org.apache.calcite.util.ZonelessTime} object. - */ - protected ZonelessTime getTime() { - ZonelessTime zt = new ZonelessTime(); - zt.setZonelessTime(getCal().getTimeInMillis()); - return zt; - } - - /** - * Converts this literal to a - * {@link org.apache.calcite.util.ZonelessTimestamp} object. - */ - protected ZonelessTimestamp getTimestamp() { - ZonelessTimestamp zt = new ZonelessTimestamp(); - zt.setZonelessTime(getCal().getTimeInMillis()); - return zt; - } } // End SqlAbstractDateTimeLiteral.java http://git-wip-us.apache.org/repos/asf/calcite/blob/205af813/core/src/main/java/org/apache/calcite/sql/SqlDateLiteral.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/sql/SqlDateLiteral.java b/core/src/main/java/org/apache/calcite/sql/SqlDateLiteral.java index 9a7aed9..1a757f6 100644 --- a/core/src/main/java/org/apache/calcite/sql/SqlDateLiteral.java +++ b/core/src/main/java/org/apache/calcite/sql/SqlDateLiteral.java @@ -16,13 +16,11 @@ */ package org.apache.calcite.sql; -import org.apache.calcite.avatica.util.DateTimeUtils; import org.apache.calcite.rel.type.RelDataType; import org.apache.calcite.rel.type.RelDataTypeFactory; import org.apache.calcite.sql.parser.SqlParserPos; import org.apache.calcite.sql.type.SqlTypeName; - -import java.util.Calendar; +import org.apache.calcite.util.DateString; /** * A SQL literal representing a DATE value, such as <code>DATE @@ -33,21 +31,22 @@ import java.util.Calendar; public class SqlDateLiteral extends SqlAbstractDateTimeLiteral { //~ Constructors ----------------------------------------------------------- - SqlDateLiteral(Calendar d, SqlParserPos pos) { - super(d, false, SqlTypeName.DATE, 0, DateTimeUtils.DATE_FORMAT_STRING, pos); - } - - SqlDateLiteral(Calendar d, String format, SqlParserPos pos) { - super(d, false, SqlTypeName.DATE, 0, format, pos); + SqlDateLiteral(DateString d, SqlParserPos pos) { + super(d, false, SqlTypeName.DATE, 0, pos); } //~ Methods ---------------------------------------------------------------- + /** Converts this literal to a {@link DateString}. */ + protected DateString getDate() { + return (DateString) value; + } + public SqlNode clone(SqlParserPos pos) { - return new SqlDateLiteral((Calendar) value, pos); + return new SqlDateLiteral((DateString) value, pos); } - public String toString() { + @Override public String toString() { return "DATE '" + toFormattedString() + "'"; } @@ -55,7 +54,7 @@ public class SqlDateLiteral extends SqlAbstractDateTimeLiteral { * Returns e.g. '1969-07-21'. */ public String toFormattedString() { - return getDate().toString(formatString); + return getDate().toString(); } public RelDataType createSqlType(RelDataTypeFactory typeFactory) { http://git-wip-us.apache.org/repos/asf/calcite/blob/205af813/core/src/main/java/org/apache/calcite/sql/SqlLiteral.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/sql/SqlLiteral.java b/core/src/main/java/org/apache/calcite/sql/SqlLiteral.java index 228abfc..4fcafb9 100644 --- a/core/src/main/java/org/apache/calcite/sql/SqlLiteral.java +++ b/core/src/main/java/org/apache/calcite/sql/SqlLiteral.java @@ -31,8 +31,11 @@ import org.apache.calcite.sql.validate.SqlMonotonicity; import org.apache.calcite.sql.validate.SqlValidator; import org.apache.calcite.sql.validate.SqlValidatorScope; import org.apache.calcite.util.BitString; +import org.apache.calcite.util.DateString; import org.apache.calcite.util.Litmus; import org.apache.calcite.util.NlsString; +import org.apache.calcite.util.TimeString; +import org.apache.calcite.util.TimestampString; import org.apache.calcite.util.Util; import java.math.BigDecimal; @@ -195,9 +198,11 @@ public class SqlLiteral extends SqlNode { case DOUBLE: return value instanceof BigDecimal; case DATE: + return value instanceof DateString; case TIME: + return value instanceof TimeString; case TIMESTAMP: - return value instanceof Calendar; + return value instanceof TimestampString; case INTERVAL_YEAR: case INTERVAL_YEAR_MONTH: case INTERVAL_MONTH: @@ -251,6 +256,61 @@ public class SqlLiteral extends SqlNode { return value; } + public <T> T getValueAs(Class<T> clazz) { + if (clazz.isInstance(value)) { + return clazz.cast(value); + } + switch (typeName) { + case DATE: + if (clazz == Calendar.class) { + return clazz.cast(((DateString) value).toCalendar()); + } + break; + case TIME: + if (clazz == Calendar.class) { + return clazz.cast(((TimeString) value).toCalendar()); + } + break; + case TIMESTAMP: + if (clazz == Calendar.class) { + return clazz.cast(((TimestampString) value).toCalendar()); + } + break; + case INTERVAL_YEAR: + case INTERVAL_YEAR_MONTH: + case INTERVAL_MONTH: + if (clazz == Long.class) { + final SqlIntervalLiteral.IntervalValue valMonth = + (SqlIntervalLiteral.IntervalValue) value; + return clazz.cast(valMonth.getSign() + * SqlParserUtil.intervalToMonths(valMonth)); + } else if (clazz == BigDecimal.class) { + return clazz.cast(BigDecimal.valueOf(getValueAs(Long.class))); + } + break; + case INTERVAL_DAY: + case INTERVAL_DAY_HOUR: + case INTERVAL_DAY_MINUTE: + case INTERVAL_DAY_SECOND: + case INTERVAL_HOUR: + case INTERVAL_HOUR_MINUTE: + case INTERVAL_HOUR_SECOND: + case INTERVAL_MINUTE: + case INTERVAL_MINUTE_SECOND: + case INTERVAL_SECOND: + if (clazz == Long.class) { + final SqlIntervalLiteral.IntervalValue valTime = + (SqlIntervalLiteral.IntervalValue) value; + return clazz.cast(valTime.getSign() + * SqlParserUtil.intervalToMillis(valTime)); + } else if (clazz == BigDecimal.class) { + return clazz.cast(BigDecimal.valueOf(getValueAs(Long.class))); + } + break; + } + throw new AssertionError("cannot cast " + value + " as " + clazz); + } + /** Returns the value as a symbol. */ @Deprecated // to be removed before 2.0 public <E extends Enum<E>> E symbolValue_() { @@ -363,12 +423,12 @@ public class SqlLiteral extends SqlNode { if (node instanceof SqlLiteral) { SqlLiteral literal = (SqlLiteral) node; assert SqlTypeUtil.inCharFamily(literal.getTypeName()); - return literal.toValue(); + return literal.value.toString(); } else if (SqlUtil.isLiteralChain(node)) { final SqlLiteral literal = SqlLiteralChainOperator.concatenateOperands((SqlCall) node); assert SqlTypeUtil.inCharFamily(literal.getTypeName()); - return literal.toValue(); + return literal.value.toString(); } else if (node instanceof SqlCall && ((SqlCall) node).getOperator() == SqlStdOperatorTable.CAST) { //noinspection deprecation @@ -680,24 +740,48 @@ public class SqlLiteral extends SqlNode { } } + @Deprecated // to be removed before 2.0 public static SqlDateLiteral createDate( Calendar calendar, SqlParserPos pos) { - return new SqlDateLiteral(calendar, pos); + return createDate(DateString.fromCalendarFields(calendar), pos); + } + + public static SqlDateLiteral createDate( + DateString date, + SqlParserPos pos) { + return new SqlDateLiteral(date, pos); } + @Deprecated // to be removed before 2.0 public static SqlTimestampLiteral createTimestamp( Calendar calendar, int precision, SqlParserPos pos) { - return new SqlTimestampLiteral(calendar, precision, false, pos); + return createTimestamp(TimestampString.fromCalendarFields(calendar), + precision, pos); + } + + public static SqlTimestampLiteral createTimestamp( + TimestampString ts, + int precision, + SqlParserPos pos) { + return new SqlTimestampLiteral(ts, precision, false, pos); } + @Deprecated // to be removed before 2.0 public static SqlTimeLiteral createTime( Calendar calendar, int precision, SqlParserPos pos) { - return new SqlTimeLiteral(calendar, precision, false, pos); + return createTime(TimeString.fromCalendarFields(calendar), precision, pos); + } + + public static SqlTimeLiteral createTime( + TimeString t, + int precision, + SqlParserPos pos) { + return new SqlTimeLiteral(t, precision, false, pos); } /** http://git-wip-us.apache.org/repos/asf/calcite/blob/205af813/core/src/main/java/org/apache/calcite/sql/SqlTimeLiteral.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/sql/SqlTimeLiteral.java b/core/src/main/java/org/apache/calcite/sql/SqlTimeLiteral.java index 79166c3..4327e2a 100644 --- a/core/src/main/java/org/apache/calcite/sql/SqlTimeLiteral.java +++ b/core/src/main/java/org/apache/calcite/sql/SqlTimeLiteral.java @@ -16,14 +16,12 @@ */ package org.apache.calcite.sql; -import org.apache.calcite.avatica.util.DateTimeUtils; import org.apache.calcite.sql.parser.SqlParserPos; import org.apache.calcite.sql.type.SqlTypeName; +import org.apache.calcite.util.TimeString; import com.google.common.base.Preconditions; -import java.util.Calendar; - /** * A SQL literal representing a TIME value, for example <code>TIME * '14:33:44.567'</code>. @@ -33,33 +31,21 @@ import java.util.Calendar; public class SqlTimeLiteral extends SqlAbstractDateTimeLiteral { //~ Constructors ----------------------------------------------------------- - SqlTimeLiteral( - Calendar t, - int precision, - boolean hasTimeZone, - SqlParserPos pos) { - this(t, precision, hasTimeZone, DateTimeUtils.TIME_FORMAT_STRING, pos); - } - - SqlTimeLiteral( - Calendar t, - int precision, - boolean hasTimeZone, - String format, + SqlTimeLiteral(TimeString t, int precision, boolean hasTimeZone, SqlParserPos pos) { - super(t, hasTimeZone, SqlTypeName.TIME, precision, format, pos); - Preconditions.checkArgument(this.precision >= 0 && this.precision <= 3); + super(t, hasTimeZone, SqlTypeName.TIME, precision, pos); + Preconditions.checkArgument(this.precision >= 0); } //~ Methods ---------------------------------------------------------------- + /** Converts this literal to a {@link TimeString}. */ + protected TimeString getTime() { + return (TimeString) value; + } + public SqlNode clone(SqlParserPos pos) { - return new SqlTimeLiteral( - (Calendar) value, - precision, - hasTimeZone, - formatString, - pos); + return new SqlTimeLiteral((TimeString) value, precision, hasTimeZone, pos); } public String toString() { @@ -70,19 +56,7 @@ public class SqlTimeLiteral extends SqlAbstractDateTimeLiteral { * Returns e.g. '03:05:67.456'. */ public String toFormattedString() { - String result = getTime().toString(formatString); - final Calendar cal = getCal(); - if (precision > 0) { - // get the millisecond count. millisecond => at most 3 digits. - String digits = Long.toString(cal.getTimeInMillis()); - result = - result + "." - + digits.substring(digits.length() - 3, - digits.length() - 3 + precision); - } else { - assert 0 == cal.get(Calendar.MILLISECOND); - } - return result; + return getTime().toString(precision); } } http://git-wip-us.apache.org/repos/asf/calcite/blob/205af813/core/src/main/java/org/apache/calcite/sql/SqlTimestampLiteral.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/sql/SqlTimestampLiteral.java b/core/src/main/java/org/apache/calcite/sql/SqlTimestampLiteral.java index a620595..cc659d5 100644 --- a/core/src/main/java/org/apache/calcite/sql/SqlTimestampLiteral.java +++ b/core/src/main/java/org/apache/calcite/sql/SqlTimestampLiteral.java @@ -16,14 +16,12 @@ */ package org.apache.calcite.sql; -import org.apache.calcite.avatica.util.DateTimeUtils; import org.apache.calcite.sql.parser.SqlParserPos; import org.apache.calcite.sql.type.SqlTypeName; +import org.apache.calcite.util.TimestampString; import com.google.common.base.Preconditions; -import java.util.Calendar; - /** * A SQL literal representing a TIMESTAMP value, for example <code>TIMESTAMP * '1969-07-21 03:15 GMT'</code>. @@ -33,34 +31,17 @@ import java.util.Calendar; public class SqlTimestampLiteral extends SqlAbstractDateTimeLiteral { //~ Constructors ----------------------------------------------------------- - public SqlTimestampLiteral( - Calendar cal, - int precision, - boolean hasTimeZone, - SqlParserPos pos) { - this(cal, precision, hasTimeZone, DateTimeUtils.TIMESTAMP_FORMAT_STRING, - pos); - } - - public SqlTimestampLiteral( - Calendar cal, - int precision, - boolean hasTimeZone, - String format, - SqlParserPos pos) { - super(cal, hasTimeZone, SqlTypeName.TIMESTAMP, precision, format, pos); - Preconditions.checkArgument(this.precision >= 0 && this.precision <= 3); + SqlTimestampLiteral(TimestampString ts, int precision, + boolean hasTimeZone, SqlParserPos pos) { + super(ts, hasTimeZone, SqlTypeName.TIMESTAMP, precision, pos); + Preconditions.checkArgument(this.precision >= 0); } //~ Methods ---------------------------------------------------------------- public SqlNode clone(SqlParserPos pos) { - return new SqlTimestampLiteral( - (Calendar) value, - precision, - hasTimeZone, - formatString, - pos); + return new SqlTimestampLiteral((TimestampString) value, precision, + hasTimeZone, pos); } public String toString() { @@ -71,21 +52,11 @@ public class SqlTimestampLiteral extends SqlAbstractDateTimeLiteral { * Returns e.g. '03:05:67.456'. */ public String toFormattedString() { - String result = getTimestamp().toString(formatString); - final Calendar cal = getCal(); + TimestampString ts = getTimestamp(); if (precision > 0) { - assert precision <= 3; - - // get the millisecond count. millisecond => at most 3 digits. - String digits = Long.toString(cal.getTimeInMillis()); - result = - result + "." - + digits.substring(digits.length() - 3, - digits.length() - 3 + precision); - } else { - assert 0 == cal.get(Calendar.MILLISECOND); + ts = ts.round(precision); } - return result; + return ts.toString(precision); } public void unparse( http://git-wip-us.apache.org/repos/asf/calcite/blob/205af813/core/src/main/java/org/apache/calcite/sql/parser/SqlParserUtil.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/sql/parser/SqlParserUtil.java b/core/src/main/java/org/apache/calcite/sql/parser/SqlParserUtil.java index d4b7237..e82841a 100644 --- a/core/src/main/java/org/apache/calcite/sql/parser/SqlParserUtil.java +++ b/core/src/main/java/org/apache/calcite/sql/parser/SqlParserUtil.java @@ -17,10 +17,12 @@ package org.apache.calcite.sql.parser; import org.apache.calcite.avatica.util.Casing; +import org.apache.calcite.avatica.util.DateTimeUtils; import org.apache.calcite.rel.type.RelDataTypeSystem; import org.apache.calcite.runtime.CalciteContextException; import org.apache.calcite.runtime.PredicateImpl; import org.apache.calcite.sql.SqlBinaryOperator; +import org.apache.calcite.sql.SqlDateLiteral; import org.apache.calcite.sql.SqlIntervalLiteral; import org.apache.calcite.sql.SqlIntervalQualifier; import org.apache.calcite.sql.SqlKind; @@ -32,9 +34,15 @@ import org.apache.calcite.sql.SqlOperator; import org.apache.calcite.sql.SqlPostfixOperator; import org.apache.calcite.sql.SqlPrefixOperator; import org.apache.calcite.sql.SqlSpecialOperator; +import org.apache.calcite.sql.SqlTimeLiteral; +import org.apache.calcite.sql.SqlTimestampLiteral; +import org.apache.calcite.sql.SqlUtil; import org.apache.calcite.sql.fun.SqlStdOperatorTable; +import org.apache.calcite.util.DateString; import org.apache.calcite.util.PrecedenceClimbingParser; import org.apache.calcite.util.SaffronProperties; +import org.apache.calcite.util.TimeString; +import org.apache.calcite.util.TimestampString; import org.apache.calcite.util.Util; import org.apache.calcite.util.trace.CalciteTrace; @@ -46,8 +54,10 @@ import org.slf4j.Logger; import java.math.BigDecimal; import java.math.BigInteger; import java.nio.charset.Charset; +import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.ArrayList; +import java.util.Calendar; import java.util.List; import java.util.Locale; import java.util.StringTokenizer; @@ -129,6 +139,63 @@ public final class SqlParserUtil { return java.sql.Timestamp.valueOf(s); } + public static SqlDateLiteral parseDateLiteral(String s, SqlParserPos pos) { + final String dateStr = parseString(s); + final Calendar cal = + DateTimeUtils.parseDateFormat(dateStr, Format.PER_THREAD.get().date, + DateTimeUtils.UTC_ZONE); + if (cal == null) { + throw SqlUtil.newContextException(pos, + RESOURCE.illegalLiteral("DATE", s, + RESOURCE.badFormat(DateTimeUtils.DATE_FORMAT_STRING).str())); + } + final DateString d = DateString.fromCalendarFields(cal); + return SqlLiteral.createDate(d, pos); + } + + public static SqlTimeLiteral parseTimeLiteral(String s, SqlParserPos pos) { + final String dateStr = parseString(s); + final DateTimeUtils.PrecisionTime pt = + DateTimeUtils.parsePrecisionDateTimeLiteral(dateStr, + Format.PER_THREAD.get().time, DateTimeUtils.UTC_ZONE, -1); + if (pt == null) { + throw SqlUtil.newContextException(pos, + RESOURCE.illegalLiteral("TIME", s, + RESOURCE.badFormat(DateTimeUtils.TIME_FORMAT_STRING).str())); + } + final TimeString t = TimeString.fromCalendarFields(pt.getCalendar()) + .withFraction(pt.getFraction()); + return SqlLiteral.createTime(t, pt.getPrecision(), pos); + } + + public static SqlTimestampLiteral parseTimestampLiteral(String s, + SqlParserPos pos) { + final String dateStr = parseString(s); + final DateTimeUtils.PrecisionTime pt = + DateTimeUtils.parsePrecisionDateTimeLiteral(dateStr, + Format.PER_THREAD.get().timestamp, DateTimeUtils.UTC_ZONE, -1); + if (pt == null) { + throw SqlUtil.newContextException(pos, + RESOURCE.illegalLiteral("TIMESTAMP", s, + RESOURCE.badFormat(DateTimeUtils.TIMESTAMP_FORMAT_STRING).str())); + } + final TimestampString ts = + TimestampString.fromCalendarFields(pt.getCalendar()) + .withFraction(pt.getFraction()); + return SqlLiteral.createTimestamp(ts, pt.getPrecision(), pos); + } + + public static SqlIntervalLiteral parseIntervalLiteral(SqlParserPos pos, + int sign, String s, SqlIntervalQualifier intervalQualifier) { + final String intervalStr = parseString(s); + if (intervalStr.equals("")) { + throw SqlUtil.newContextException(pos, + RESOURCE.illegalIntervalLiteral(s + " " + + intervalQualifier.toString(), pos.toString())); + } + return SqlLiteral.createInterval(sign, intervalStr, intervalQualifier, pos); + } + /** * Checks if the date/time format is valid * @@ -875,6 +942,24 @@ public final class SqlParserUtil { SqlParserUtil.replaceSublist(list, start, end, e); } } + + /** Pre-initialized {@link DateFormat} objects, to be used within the current + * thread, because {@code DateFormat} is not thread-safe. */ + private static class Format { + private static final ThreadLocal<Format> PER_THREAD = + new ThreadLocal<Format>() { + @Override protected Format initialValue() { + return new Format(); + } + }; + final DateFormat timestamp = + new SimpleDateFormat(DateTimeUtils.TIMESTAMP_FORMAT_STRING, + Locale.ROOT); + final DateFormat time = + new SimpleDateFormat(DateTimeUtils.TIME_FORMAT_STRING, Locale.ROOT); + final DateFormat date = + new SimpleDateFormat(DateTimeUtils.DATE_FORMAT_STRING, Locale.ROOT); + } } // End SqlParserUtil.java http://git-wip-us.apache.org/repos/asf/calcite/blob/205af813/core/src/main/java/org/apache/calcite/sql/type/SqlTypeFactoryImpl.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/sql/type/SqlTypeFactoryImpl.java b/core/src/main/java/org/apache/calcite/sql/type/SqlTypeFactoryImpl.java index bbe0801..9f7dc51 100644 --- a/core/src/main/java/org/apache/calcite/sql/type/SqlTypeFactoryImpl.java +++ b/core/src/main/java/org/apache/calcite/sql/type/SqlTypeFactoryImpl.java @@ -56,6 +56,10 @@ public class SqlTypeFactoryImpl extends RelDataTypeFactoryImpl { public RelDataType createSqlType( SqlTypeName typeName, int precision) { + final int maxPrecision = typeSystem.getMaxPrecision(typeName); + if (maxPrecision >= 0 && precision > maxPrecision) { + precision = maxPrecision; + } if (typeName.allowsScale()) { return createSqlType(typeName, precision, typeName.getDefaultScale()); } @@ -75,6 +79,10 @@ public class SqlTypeFactoryImpl extends RelDataTypeFactoryImpl { assertBasic(typeName); assert (precision >= 0) || (precision == RelDataType.PRECISION_NOT_SPECIFIED); + final int maxPrecision = typeSystem.getMaxPrecision(typeName); + if (maxPrecision >= 0 && precision > maxPrecision) { + precision = maxPrecision; + } RelDataType newType = new BasicSqlType(typeSystem, typeName, precision, scale); newType = SqlTypeUtil.addCharsetAndCollation(newType, this); http://git-wip-us.apache.org/repos/asf/calcite/blob/205af813/core/src/main/java/org/apache/calcite/sql/type/SqlTypeName.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/sql/type/SqlTypeName.java b/core/src/main/java/org/apache/calcite/sql/type/SqlTypeName.java index f34ea11..f1b4e6a 100644 --- a/core/src/main/java/org/apache/calcite/sql/type/SqlTypeName.java +++ b/core/src/main/java/org/apache/calcite/sql/type/SqlTypeName.java @@ -19,6 +19,9 @@ package org.apache.calcite.sql.type; import org.apache.calcite.avatica.util.TimeUnit; import org.apache.calcite.sql.SqlLiteral; import org.apache.calcite.sql.parser.SqlParserPos; +import org.apache.calcite.util.DateString; +import org.apache.calcite.util.TimeString; +import org.apache.calcite.util.TimestampString; import org.apache.calcite.util.Util; import com.google.common.collect.ImmutableList; @@ -911,11 +914,17 @@ public enum SqlTypeName { case BINARY: return SqlLiteral.createBinaryString((byte[]) o, pos); case DATE: - return SqlLiteral.createDate((Calendar) o, pos); + return SqlLiteral.createDate(o instanceof Calendar + ? DateString.fromCalendarFields((Calendar) o) + : (DateString) o, pos); case TIME: - return SqlLiteral.createTime((Calendar) o, 0 /* todo */, pos); + return SqlLiteral.createTime(o instanceof Calendar + ? TimeString.fromCalendarFields((Calendar) o) + : (TimeString) o, 0 /* todo */, pos); case TIMESTAMP: - return SqlLiteral.createTimestamp((Calendar) o, 0 /* todo */, pos); + return SqlLiteral.createTimestamp(o instanceof Calendar + ? TimestampString.fromCalendarFields((Calendar) o) + : (TimestampString) o, 0 /* todo */, pos); default: throw Util.unexpected(this); } http://git-wip-us.apache.org/repos/asf/calcite/blob/205af813/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorImpl.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorImpl.java b/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorImpl.java index b8cddb0..46cd711 100644 --- a/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorImpl.java +++ b/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorImpl.java @@ -2821,7 +2821,7 @@ public class SqlValidatorImpl implements SqlValidatorWithHints { case DATE: case TIME: case TIMESTAMP: - Calendar calendar = (Calendar) literal.getValue(); + Calendar calendar = literal.getValueAs(Calendar.class); final int year = calendar.get(Calendar.YEAR); final int era = calendar.get(Calendar.ERA); if (year < 1 || era == GregorianCalendar.BC || year > 9999) { http://git-wip-us.apache.org/repos/asf/calcite/blob/205af813/core/src/main/java/org/apache/calcite/sql2rel/SqlNodeToRexConverterImpl.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/sql2rel/SqlNodeToRexConverterImpl.java b/core/src/main/java/org/apache/calcite/sql2rel/SqlNodeToRexConverterImpl.java index 3f7b5cb..9f398d6 100644 --- a/core/src/main/java/org/apache/calcite/sql2rel/SqlNodeToRexConverterImpl.java +++ b/core/src/main/java/org/apache/calcite/sql2rel/SqlNodeToRexConverterImpl.java @@ -31,13 +31,15 @@ import org.apache.calcite.sql.SqlTimestampLiteral; import org.apache.calcite.sql.type.SqlTypeName; import org.apache.calcite.sql.validate.SqlValidator; import org.apache.calcite.util.BitString; +import org.apache.calcite.util.DateString; import org.apache.calcite.util.NlsString; +import org.apache.calcite.util.TimeString; +import org.apache.calcite.util.TimestampString; import org.apache.calcite.util.Util; import com.google.common.base.Preconditions; import java.math.BigDecimal; -import java.util.Calendar; /** * Standard implementation of {@link SqlNodeToRexConverter}. @@ -80,8 +82,7 @@ public class SqlNodeToRexConverterImpl implements SqlNodeToRexConverter { RexBuilder rexBuilder = cx.getRexBuilder(); RelDataTypeFactory typeFactory = cx.getTypeFactory(); SqlValidator validator = cx.getValidator(); - final Object value = literal.getValue(); - if (value == null) { + if (literal.getValue() == null) { // Since there is no eq. RexLiteral of SqlLiteral.Unknown we // treat it as a cast(null as boolean) RelDataType type; @@ -102,23 +103,23 @@ public class SqlNodeToRexConverterImpl implements SqlNodeToRexConverter { switch (literal.getTypeName()) { case DECIMAL: - // exact number - BigDecimal bd = (BigDecimal) value; + BigDecimal bd = literal.getValueAs(BigDecimal.class); return rexBuilder.makeExactLiteral( bd, literal.createSqlType(typeFactory)); - case DOUBLE: + case DOUBLE: // approximate type // TODO: preserve fixed-point precision and large integers - return rexBuilder.makeApproxLiteral((BigDecimal) value); + return rexBuilder.makeApproxLiteral(literal.getValueAs(BigDecimal.class)); + case CHAR: - return rexBuilder.makeCharLiteral((NlsString) value); + return rexBuilder.makeCharLiteral(literal.getValueAs(NlsString.class)); case BOOLEAN: - return rexBuilder.makeLiteral(((Boolean) value).booleanValue()); + return rexBuilder.makeLiteral(literal.getValueAs(Boolean.class)); case BINARY: - bitString = (BitString) value; + bitString = literal.getValueAs(BitString.class); Preconditions.checkArgument((bitString.getBitCount() % 8) == 0, "incomplete octet"); @@ -127,17 +128,17 @@ public class SqlNodeToRexConverterImpl implements SqlNodeToRexConverter { ByteString byteString = new ByteString(bitString.getAsByteArray()); return rexBuilder.makeBinaryLiteral(byteString); case SYMBOL: - return rexBuilder.makeFlag((Enum) value); + return rexBuilder.makeFlag(literal.getValueAs(Enum.class)); case TIMESTAMP: return rexBuilder.makeTimestampLiteral( - (Calendar) value, + literal.getValueAs(TimestampString.class), ((SqlTimestampLiteral) literal).getPrec()); case TIME: return rexBuilder.makeTimeLiteral( - (Calendar) value, + literal.getValueAs(TimeString.class), ((SqlTimeLiteral) literal).getPrec()); case DATE: - return rexBuilder.makeDateLiteral((Calendar) value); + return rexBuilder.makeDateLiteral(literal.getValueAs(DateString.class)); case INTERVAL_YEAR: case INTERVAL_YEAR_MONTH: @@ -153,10 +154,10 @@ public class SqlNodeToRexConverterImpl implements SqlNodeToRexConverter { case INTERVAL_MINUTE_SECOND: case INTERVAL_SECOND: SqlIntervalQualifier sqlIntervalQualifier = - ((SqlIntervalLiteral.IntervalValue) value).getIntervalQualifier(); - l = (long) SqlLiteral.value(literal); + literal.getValueAs(SqlIntervalLiteral.IntervalValue.class) + .getIntervalQualifier(); return rexBuilder.makeIntervalLiteral( - BigDecimal.valueOf(l), + literal.getValueAs(BigDecimal.class), sqlIntervalQualifier); default: throw Util.unexpected(literal.getTypeName()); http://git-wip-us.apache.org/repos/asf/calcite/blob/205af813/core/src/main/java/org/apache/calcite/util/BasicDatetime.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/util/BasicDatetime.java b/core/src/main/java/org/apache/calcite/util/BasicDatetime.java deleted file mode 100644 index 0d38bc0..0000000 --- a/core/src/main/java/org/apache/calcite/util/BasicDatetime.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to you under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.calcite.util; - -import java.util.TimeZone; - -/** - * BasicDatetime is an interface for dates, times, or timestamps that can be - * assigned from a long value. The value to be assigned may either be a zoneless - * time, or it may be a zoned time. - * - * <p>A zoneless time is based on milliseconds. It may contain date and/or time - * components as follows: - * - * <pre> - * The time component = value % milliseconds in a day - * The date component = value / milliseconds in a day - * </pre> - * - * If a date component is specified, it is relative to the epoch (1970-01-01). - * - * <p>A zoned time represents a time that was created in a particular time zone. - * It may contain date and/or time components that are valid when interpreted - * relative to a specified time zone, according to a - * {@link java.util.Calendar Calendar}. Jdbc types, such as - * {@link java.sql.Date} typically contain zoned times. - */ -public interface BasicDatetime { - //~ Methods ---------------------------------------------------------------- - - /** - * Gets the internal value of this datetime - */ - long getTime(); - - /** - * Sets this datetime via a zoneless time value. See class comments for more - * information. - */ - void setZonelessTime(long value); - - /** - * Sets this datetime via a zoned time value. See class comments for more - * information. - */ - void setZonedTime(long value, TimeZone zone); -} - -// End BasicDatetime.java
