[CALCITE-6015] AssertionError during optimization of EXTRACT expression
Signed-off-by: Mihai Budiu <mbudiu@feldera.com>
diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java
index 9cabfcb..6bf8f18 100644
--- a/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java
+++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java
@@ -3128,7 +3128,8 @@
case MILLISECOND:
case MICROSECOND:
case NANOSECOND:
- if (sqlTypeName == SqlTypeName.DATE) {
+ if (sqlTypeName == SqlTypeName.DATE
+ || SqlTypeName.YEAR_INTERVAL_TYPES.contains(sqlTypeName)) {
return Expressions.constant(0L);
}
operand = mod(operand, TimeUnit.MINUTE.multiplier.longValue(), !isIntervalType);
@@ -3155,9 +3156,6 @@
translator.getRoot()));
return Expressions.divide(operand,
Expressions.constant(TimeUnit.SECOND.multiplier.longValue()));
- case INTERVAL_YEAR:
- case INTERVAL_YEAR_MONTH:
- case INTERVAL_MONTH:
case INTERVAL_DAY:
case INTERVAL_DAY_HOUR:
case INTERVAL_DAY_MINUTE:
@@ -3168,6 +3166,11 @@
case INTERVAL_MINUTE:
case INTERVAL_MINUTE_SECOND:
case INTERVAL_SECOND:
+ return Expressions.divide(operand,
+ Expressions.constant(TimeUnit.SECOND.multiplier.longValue()));
+ case INTERVAL_YEAR:
+ case INTERVAL_YEAR_MONTH:
+ case INTERVAL_MONTH:
// no convertlet conversion, pass it as extract
throw new AssertionError("unexpected " + sqlTypeName);
default:
diff --git a/core/src/main/java/org/apache/calcite/sql/SqlCall.java b/core/src/main/java/org/apache/calcite/sql/SqlCall.java
index 9eba738..617fa46 100755
--- a/core/src/main/java/org/apache/calcite/sql/SqlCall.java
+++ b/core/src/main/java/org/apache/calcite/sql/SqlCall.java
@@ -191,7 +191,7 @@
* Returns a string describing the actual argument types of a call, e.g.
* "SUBSTR(VARCHAR(12), NUMBER(3,2), INTEGER)".
*/
- protected String getCallSignature(
+ public String getCallSignature(
SqlValidator validator,
@Nullable SqlValidatorScope scope) {
List<String> signatureList = new ArrayList<>();
diff --git a/core/src/main/java/org/apache/calcite/sql/fun/SqlExtractFunction.java b/core/src/main/java/org/apache/calcite/sql/fun/SqlExtractFunction.java
index 07789aa..1c8a20b 100644
--- a/core/src/main/java/org/apache/calcite/sql/fun/SqlExtractFunction.java
+++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlExtractFunction.java
@@ -17,6 +17,7 @@
package org.apache.calcite.sql.fun;
import org.apache.calcite.avatica.util.TimeUnitRange;
+import org.apache.calcite.rel.type.RelDataType;
import org.apache.calcite.sql.SqlCall;
import org.apache.calcite.sql.SqlFunction;
import org.apache.calcite.sql.SqlFunctionCategory;
@@ -26,12 +27,16 @@
import org.apache.calcite.sql.SqlWriter;
import org.apache.calcite.sql.type.OperandTypes;
import org.apache.calcite.sql.type.ReturnTypes;
+import org.apache.calcite.sql.type.SqlTypeName;
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.Util;
+import com.google.common.collect.ImmutableSet;
+
import static org.apache.calcite.sql.validate.SqlNonNullableAccessors.getOperandLiteralValueOrThrow;
+import static org.apache.calcite.util.Static.RESOURCE;
/**
* The SQL <code>EXTRACT</code> operator. Extracts a specified field value from
@@ -70,6 +75,59 @@
writer.endFunCall(frame);
}
+ // List of types that support EXTRACT(X, ...) where X is MONTH or larger
+ private static final ImmutableSet<SqlTypeName> MONTH_AND_ABOVE_TYPES =
+ new ImmutableSet.Builder<SqlTypeName>()
+ .add(SqlTypeName.DATE)
+ .add(SqlTypeName.TIMESTAMP)
+ .add(SqlTypeName.TIMESTAMP_WITH_LOCAL_TIME_ZONE)
+ .addAll(SqlTypeName.YEAR_INTERVAL_TYPES)
+ .build();
+
+ // List of types that support EXTRACT(X, ...) where X is between DAY and WEEK
+ private static final ImmutableSet<SqlTypeName> DAY_TO_WEEK_TYPES =
+ new ImmutableSet.Builder<SqlTypeName>()
+ .add(SqlTypeName.DATE)
+ .add(SqlTypeName.TIMESTAMP)
+ .add(SqlTypeName.TIMESTAMP_WITH_LOCAL_TIME_ZONE)
+ .build();
+
+ // List of types that support EXTRACT(EPOCH, ...)
+ private static final ImmutableSet<SqlTypeName> EPOCH_TYPES =
+ new ImmutableSet.Builder<SqlTypeName>()
+ .add(SqlTypeName.DATE)
+ .add(SqlTypeName.TIMESTAMP)
+ .add(SqlTypeName.TIMESTAMP_WITH_LOCAL_TIME_ZONE)
+ .addAll(SqlTypeName.YEAR_INTERVAL_TYPES)
+ .addAll(SqlTypeName.DAY_INTERVAL_TYPES)
+ .build();
+
+ // List of types that support EXTRACT(DAY, ...)
+ private static final ImmutableSet<SqlTypeName> DAY_TYPES =
+ new ImmutableSet.Builder<SqlTypeName>()
+ .add(SqlTypeName.DATE)
+ .add(SqlTypeName.TIMESTAMP)
+ .add(SqlTypeName.TIMESTAMP_WITH_LOCAL_TIME_ZONE)
+ .add(SqlTypeName.INTERVAL_DAY)
+ .add(SqlTypeName.INTERVAL_DAY_HOUR)
+ .add(SqlTypeName.INTERVAL_DAY_MINUTE)
+ .add(SqlTypeName.INTERVAL_DAY_SECOND)
+ .addAll(SqlTypeName.YEAR_INTERVAL_TYPES)
+ .build();
+
+ // List of types that support EXTRACT(X, ...) where X is
+ // between HOUR and NANOSECOND
+ private static final ImmutableSet<SqlTypeName> HOUR_TO_NANOSECOND_TYPES =
+ new ImmutableSet.Builder<SqlTypeName>()
+ .add(SqlTypeName.DATE)
+ .add(SqlTypeName.TIMESTAMP)
+ .add(SqlTypeName.TIMESTAMP_WITH_LOCAL_TIME_ZONE)
+ .add(SqlTypeName.TIME)
+ .add(SqlTypeName.TIME_WITH_LOCAL_TIME_ZONE)
+ .addAll(SqlTypeName.YEAR_INTERVAL_TYPES)
+ .addAll(SqlTypeName.DAY_INTERVAL_TYPES)
+ .build();
+
@Override public void validateCall(SqlCall call, SqlValidator validator,
SqlValidatorScope scope, SqlValidatorScope operandScope) {
super.validateCall(call, validator, scope, operandScope);
@@ -83,8 +141,65 @@
// startUnit = EPOCH and timeFrameName = 'MINUTE15'.
//
// If the latter, check that timeFrameName is valid.
- validator.validateTimeFrame(
- (SqlIntervalQualifier) call.getOperandList().get(0));
+ SqlIntervalQualifier qualifier = call.operand(0);
+ validator.validateTimeFrame(qualifier);
+ TimeUnitRange range = qualifier.timeUnitRange;
+
+ RelDataType type = validator.getValidatedNodeTypeIfKnown(call.operand(1));
+ if (type == null) {
+ return;
+ }
+
+ SqlTypeName typeName = type.getSqlTypeName();
+ boolean legal;
+ switch (range) {
+ case YEAR:
+ case MONTH:
+ case ISOYEAR:
+ case QUARTER:
+ case DECADE:
+ case CENTURY:
+ case MILLENNIUM:
+ legal = MONTH_AND_ABOVE_TYPES.contains(typeName);
+ break;
+ case WEEK:
+ case DOW:
+ case ISODOW:
+ case DOY:
+ legal = DAY_TO_WEEK_TYPES.contains(typeName);
+ break;
+ case EPOCH:
+ legal = EPOCH_TYPES.contains(typeName);
+ break;
+ case DAY:
+ legal = DAY_TYPES.contains(typeName);
+ break;
+ case HOUR:
+ case MINUTE:
+ case SECOND:
+ case MILLISECOND:
+ case MICROSECOND:
+ case NANOSECOND:
+ legal = HOUR_TO_NANOSECOND_TYPES.contains(typeName);
+ break;
+ case YEAR_TO_MONTH:
+ case DAY_TO_HOUR:
+ case DAY_TO_MINUTE:
+ case DAY_TO_SECOND:
+ case HOUR_TO_MINUTE:
+ case HOUR_TO_SECOND:
+ case MINUTE_TO_SECOND:
+ default:
+ legal = false;
+ break;
+ }
+
+ if (!legal) {
+ throw validator.newValidationError(call,
+ RESOURCE.canNotApplyOp2Type(call.getOperator().getName(),
+ call.getCallSignature(validator, scope),
+ call.getOperator().getAllowedSignatures()));
+ }
}
@Override public SqlMonotonicity getMonotonicity(SqlOperatorBinding call) {
diff --git a/core/src/main/java/org/apache/calcite/sql/type/OperandTypes.java b/core/src/main/java/org/apache/calcite/sql/type/OperandTypes.java
index 67367ba..c7a85cd 100644
--- a/core/src/main/java/org/apache/calcite/sql/type/OperandTypes.java
+++ b/core/src/main/java/org/apache/calcite/sql/type/OperandTypes.java
@@ -1100,7 +1100,8 @@
family(SqlTypeFamily.DATETIME_INTERVAL, SqlTypeFamily.DATETIME);
public static final SqlSingleOperandTypeChecker INTERVALINTERVAL_INTERVALDATETIME =
- INTERVAL_SAME_SAME.or(INTERVAL_DATETIME);
+ INTERVAL_SAME_SAME.or(INTERVAL_DATETIME)
+ .or(family(SqlTypeFamily.INTERVAL_DAY_TIME, SqlTypeFamily.INTERVAL_YEAR_MONTH));
// TODO: datetime+interval checking missing
// TODO: interval+datetime checking missing
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 8612da7..9572cf4 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
@@ -1869,7 +1869,7 @@
}
final SqlNode original = originalExprs.get(node);
if (original != null && original != node) {
- return getValidatedNodeType(original);
+ return getValidatedNodeTypeIfKnown(original);
}
if (node instanceof SqlIdentifier) {
return getCatalogReader().getNamedType((SqlIdentifier) node);
diff --git a/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java b/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java
index 21e2e91..11c2edf 100644
--- a/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java
+++ b/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java
@@ -7067,9 +7067,8 @@
.columnType("BIGINT NOT NULL");
expr("extract(minute from interval '1.1' second)").ok();
expr("extract(year from DATE '2008-2-2')").ok();
+ expr("extract(minute from interval '11' month)").ok();
- wholeExpr("extract(minute from interval '11' month)")
- .fails("(?s).*Cannot apply.*");
wholeExpr("extract(year from interval '11' second)")
.fails("(?s).*Cannot apply.*");
}
diff --git a/site/_docs/history.md b/site/_docs/history.md
index 553ca1c..f3140e7 100644
--- a/site/_docs/history.md
+++ b/site/_docs/history.md
@@ -43,6 +43,10 @@
#### Breaking Changes
{: #breaking-1-37-0}
+* In the context of [CALCITE-6015] the visibility of the method
+`SqlCall.getCallSignature` has been converted from `protected` to `public`.
+ Any subclass overriding it will need to be adjusted accordingly.
+
Compatibility: This release is tested on Linux, macOS, Microsoft Windows;
using JDK/OpenJDK versions 8 to 19;
Guava versions 21.0 to 32.1.3-jre;
diff --git a/testkit/src/main/java/org/apache/calcite/sql/test/SqlOperatorFixture.java b/testkit/src/main/java/org/apache/calcite/sql/test/SqlOperatorFixture.java
index f87208c..af4264e 100644
--- a/testkit/src/main/java/org/apache/calcite/sql/test/SqlOperatorFixture.java
+++ b/testkit/src/main/java/org/apache/calcite/sql/test/SqlOperatorFixture.java
@@ -84,14 +84,6 @@
// TODO: Change message
String BAD_DATETIME_MESSAGE = "(?s).*";
- // Error messages when an invalid time unit is given as
- // input to extract for a particular input type.
- String INVALID_EXTRACT_UNIT_CONVERTLET_ERROR =
- "Was not expecting value '.*' for enumeration.*";
-
- String INVALID_EXTRACT_UNIT_VALIDATION_ERROR =
- "Cannot apply 'EXTRACT' to arguments of type .*'\n.*";
-
String LITERAL_OUT_OF_RANGE_MESSAGE =
"(?s).*Numeric literal.*out of range.*";
diff --git a/testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java b/testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java
index 5a772c8..023de7c 100644
--- a/testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java
+++ b/testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java
@@ -129,8 +129,6 @@
import static org.apache.calcite.sql.test.SqlOperatorFixture.DIVISION_BY_ZERO_MESSAGE;
import static org.apache.calcite.sql.test.SqlOperatorFixture.INVALID_ARGUMENTS_NUMBER;
import static org.apache.calcite.sql.test.SqlOperatorFixture.INVALID_CHAR_MESSAGE;
-import static org.apache.calcite.sql.test.SqlOperatorFixture.INVALID_EXTRACT_UNIT_CONVERTLET_ERROR;
-import static org.apache.calcite.sql.test.SqlOperatorFixture.INVALID_EXTRACT_UNIT_VALIDATION_ERROR;
import static org.apache.calcite.sql.test.SqlOperatorFixture.LITERAL_OUT_OF_RANGE_MESSAGE;
import static org.apache.calcite.sql.test.SqlOperatorFixture.OUT_OF_RANGE_MESSAGE;
import static org.apache.calcite.sql.test.SqlOperatorFixture.WRONG_FORMAT_MESSAGE;
@@ -10626,64 +10624,45 @@
f.setFor(SqlStdOperatorTable.EXTRACT, VM_FENNEL, VM_JAVA);
if (TODO) {
- // Not supported, fails in type validation because the extract
- // unit is not YearMonth interval type.
-
f.checkScalar("extract(epoch from interval '4-2' year to month)",
// number of seconds elapsed since timestamp
// '1970-01-01 00:00:00' + input interval
"131328000", "BIGINT NOT NULL");
-
- f.checkScalar("extract(second from interval '4-2' year to month)",
- "0", "BIGINT NOT NULL");
-
- f.checkScalar("extract(millisecond from "
- + "interval '4-2' year to month)", "0", "BIGINT NOT NULL");
-
- f.checkScalar("extract(microsecond "
- + "from interval '4-2' year to month)", "0", "BIGINT NOT NULL");
-
- f.checkScalar("extract(nanosecond from "
- + "interval '4-2' year to month)", "0", "BIGINT NOT NULL");
-
- f.checkScalar("extract(minute from interval '4-2' year to month)",
- "0", "BIGINT NOT NULL");
-
- f.checkScalar("extract(hour from interval '4-2' year to month)",
- "0", "BIGINT NOT NULL");
-
- f.checkScalar("extract(day from interval '4-2' year to month)",
- "0", "BIGINT NOT NULL");
}
+ f.checkScalar("extract(second from interval '4-2' year to month)",
+ "0", "BIGINT NOT NULL");
+ f.checkScalar("extract(millisecond from "
+ + "interval '4-2' year to month)", "0", "BIGINT NOT NULL");
+ f.checkScalar("extract(microsecond "
+ + "from interval '4-2' year to month)", "0", "BIGINT NOT NULL");
+ f.checkScalar("extract(nanosecond from "
+ + "interval '4-2' year to month)", "0", "BIGINT NOT NULL");
+ f.checkScalar("extract(minute from interval '4-2' year to month)",
+ "0", "BIGINT NOT NULL");
+ f.checkScalar("extract(hour from interval '4-2' year to month)",
+ "0", "BIGINT NOT NULL");
+ f.checkScalar("extract(day from interval '4-2' year to month)",
+ "0", "BIGINT NOT NULL");
- // Postgres doesn't support DOW, ISODOW, DOY and WEEK on INTERVAL YEAR MONTH type.
- // SQL standard doesn't have extract units for DOW, ISODOW, DOY and WEEK.
- if (Bug.CALCITE_2539_FIXED) {
- f.checkFails("extract(doy from interval '4-2' year to month)",
- INVALID_EXTRACT_UNIT_VALIDATION_ERROR, false);
- f.checkFails("^extract(dow from interval '4-2' year to month)^",
- INVALID_EXTRACT_UNIT_VALIDATION_ERROR, false);
- f.checkFails("^extract(week from interval '4-2' year to month)^",
- INVALID_EXTRACT_UNIT_VALIDATION_ERROR, false);
- f.checkFails("^extract(isodow from interval '4-2' year to month)^",
- INVALID_EXTRACT_UNIT_VALIDATION_ERROR, false);
- }
+ final String fail = "Cannot apply 'EXTRACT' to arguments of type 'EXTRACT\\(<.*> "
+ + "FROM <INTERVAL YEAR TO MONTH>\\)'\\. Supported form\\(s\\): "
+ + ".*\\n.*\\n.*";
+
+ f.checkFails("^extract(doy from interval '4-2' year to month)^", fail, false);
+ f.checkFails("^extract(dow from interval '4-2' year to month)^", fail, false);
+ f.checkFails("^extract(isodow from interval '4-2' year to month)^", fail, false);
+ f.checkFails("^extract(week from interval '4-2' year to month)^", fail, false);
f.checkScalar("extract(month from interval '4-2' year to month)",
"2", "BIGINT NOT NULL");
-
f.checkScalar("extract(quarter from interval '4-2' year to month)",
"1", "BIGINT NOT NULL");
-
f.checkScalar("extract(year from interval '4-2' year to month)",
"4", "BIGINT NOT NULL");
-
f.checkScalar("extract(decade from "
+ "interval '426-3' year(3) to month)", "42", "BIGINT NOT NULL");
-
f.checkScalar("extract(century from "
+ "interval '426-3' year(3) to month)", "4", "BIGINT NOT NULL");
-
f.checkScalar("extract(millennium from "
+ "interval '2005-3' year(4) to month)", "2", "BIGINT NOT NULL");
}
@@ -10692,15 +10671,11 @@
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.EXTRACT, VM_FENNEL, VM_JAVA);
- if (TODO) {
- // Not implemented in operator test
- f.checkScalar("extract(epoch from "
- + "interval '2 3:4:5.678' day to second)",
- // number of seconds elapsed since timestamp
- // '1970-01-01 00:00:00' + input interval
- "183845.678",
- "BIGINT NOT NULL");
- }
+ f.checkScalar("extract(epoch from interval '2 3:4:5.678' day to second)",
+ // number of seconds elapsed since timestamp
+ // '1970-01-01 00:00:00' + input interval
+ "183845",
+ "BIGINT NOT NULL");
f.checkScalar("extract(millisecond from "
+ "interval '2 3:4:5.678' day to second)",
@@ -10737,46 +10712,19 @@
"2",
"BIGINT NOT NULL");
- // Postgres doesn't support DOW, ISODOW, DOY and WEEK on INTERVAL DAY TIME type.
- // SQL standard doesn't have extract units for DOW, ISODOW, DOY and WEEK.
- f.checkFails("extract(doy from interval '2 3:4:5.678' day to second)",
- INVALID_EXTRACT_UNIT_CONVERTLET_ERROR, true);
- f.checkFails("extract(dow from interval '2 3:4:5.678' day to second)",
- INVALID_EXTRACT_UNIT_CONVERTLET_ERROR, true);
- f.checkFails("extract(week from interval '2 3:4:5.678' day to second)",
- INVALID_EXTRACT_UNIT_CONVERTLET_ERROR, true);
- f.checkFails("extract(isodow from interval '2 3:4:5.678' day to second)",
- INVALID_EXTRACT_UNIT_CONVERTLET_ERROR, true);
+ final String fail = "Cannot apply 'EXTRACT' to arguments of type 'EXTRACT\\(<.*> "
+ + "FROM <INTERVAL DAY TO SECOND>\\)'\\. Supported form\\(s\\): .*\\n.*\\n.*";
- f.checkFails("^extract(month from interval '2 3:4:5.678' day to second)^",
- "(?s)Cannot apply 'EXTRACT' to arguments of type 'EXTRACT\\(<INTERVAL "
- + "MONTH> FROM <INTERVAL DAY TO SECOND>\\)'\\. Supported "
- + "form\\(s\\):.*",
- false);
-
- f.checkFails("^extract(quarter from interval '2 3:4:5.678' day to second)^",
- "(?s)Cannot apply 'EXTRACT' to arguments of type 'EXTRACT\\(<INTERVAL "
- + "QUARTER> FROM <INTERVAL DAY TO SECOND>\\)'\\. Supported "
- + "form\\(s\\):.*",
- false);
-
- f.checkFails("^extract(year from interval '2 3:4:5.678' day to second)^",
- "(?s)Cannot apply 'EXTRACT' to arguments of type 'EXTRACT\\(<INTERVAL "
- + "YEAR> FROM <INTERVAL DAY TO SECOND>\\)'\\. Supported "
- + "form\\(s\\):.*",
- false);
-
- f.checkFails("^extract(isoyear from interval '2 3:4:5.678' day to second)^",
- "(?s)Cannot apply 'EXTRACT' to arguments of type 'EXTRACT\\(<INTERVAL "
- + "ISOYEAR> FROM <INTERVAL DAY TO SECOND>\\)'\\. Supported "
- + "form\\(s\\):.*",
- false);
-
- f.checkFails("^extract(century from interval '2 3:4:5.678' day to second)^",
- "(?s)Cannot apply 'EXTRACT' to arguments of type 'EXTRACT\\(<INTERVAL "
- + "CENTURY> FROM <INTERVAL DAY TO SECOND>\\)'\\. Supported "
- + "form\\(s\\):.*",
- false);
+ f.checkFails("^extract(doy from interval '2 3:4:5.678' day to second)^", fail, false);
+ f.checkFails("^extract(dow from interval '2 3:4:5.678' day to second)^", fail, false);
+ f.checkFails("^extract(week from interval '2 3:4:5.678' day to second)^", fail, false);
+ f.checkFails("^extract(isodow from interval '2 3:4:5.678' day to second)^", fail, false);
+ f.checkFails("^extract(month from interval '2 3:4:5.678' day to second)^", fail, false);
+ f.checkFails("^extract(quarter from interval '2 3:4:5.678' day to second)^", fail, false);
+ f.checkFails("^extract(year from interval '2 3:4:5.678' day to second)^", fail, false);
+ f.checkFails("^extract(isoyear from interval '2 3:4:5.678' day to second)^", fail, false);
+ f.checkFails("^extract(century from interval '2 3:4:5.678' day to second)^", fail, false);
+ f.checkFails("^extract(millennium from interval '2 3:4:5.678' day to second)^", fail, false);
}
@Test void testExtractDate() {
@@ -10862,6 +10810,44 @@
"3", "BIGINT NOT NULL");
}
+ @Test void testExtractTime() {
+ final SqlOperatorFixture f = fixture();
+ f.setFor(SqlStdOperatorTable.EXTRACT, VM_FENNEL, VM_JAVA);
+
+ final String fail = "Cannot apply 'EXTRACT' to arguments of type 'EXTRACT\\(<.*> "
+ + "FROM <TIME\\(0\\)>\\)'\\. "
+ + "Supported form\\(s\\): .*\\n.*\\n.*";
+
+ f.checkFails("extract(^a^ from time '12:34:56')",
+ "'A' is not a valid time frame", false);
+ f.checkFails("^extract(epoch from time '12:34:56')^",
+ fail, false);
+
+ f.checkScalar("extract(second from time '12:34:56')",
+ "56", "BIGINT NOT NULL");
+ f.checkScalar("extract(millisecond from time '12:34:56')",
+ "56000", "BIGINT NOT NULL");
+ f.checkScalar("extract(microsecond from time '12:34:56')",
+ "56000000", "BIGINT NOT NULL");
+ f.checkScalar("extract(nanosecond from time '12:34:56')",
+ "56000000000", "BIGINT NOT NULL");
+ f.checkScalar("extract(minute from time '12:34:56')",
+ "34", "BIGINT NOT NULL");
+ f.checkScalar("extract(hour from time '12:34:56')",
+ "12", "BIGINT NOT NULL");
+ f.checkFails("^extract(day from time '12:34:56')^", fail, false);
+ f.checkFails("^extract(month from time '12:34:56')^", fail, false);
+ f.checkFails("^extract(quarter from time '12:34:56')^", fail, false);
+ f.checkFails("^extract(year from time '12:34:56')^", fail, false);
+ f.checkFails("^extract(isoyear from time '12:34:56')^", fail, false);
+ f.checkFails("^extract(doy from time '12:34:56')^", fail, false);
+ f.checkFails("^extract(dow from time '12:34:56')^", fail, false);
+ f.checkFails("^extract(week from time '12:34:56')^", fail, false);
+ f.checkFails("^extract(decade from time '12:34:56')^", fail, false);
+ f.checkFails("^extract(century from time '12:34:56')^", fail, false);
+ f.checkFails("^extract(millennium from time '12:34:56')^", fail, false);
+ }
+
@Test void testExtractTimestamp() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.EXTRACT, VM_FENNEL, VM_JAVA);