[FLINK-6409] [table] TUMBLE/HOP/SESSION_START/END do not resolve time field correctly
This closes #3799. Project: http://git-wip-us.apache.org/repos/asf/flink/repo Commit: http://git-wip-us.apache.org/repos/asf/flink/commit/2d33c0be Tree: http://git-wip-us.apache.org/repos/asf/flink/tree/2d33c0be Diff: http://git-wip-us.apache.org/repos/asf/flink/diff/2d33c0be Branch: refs/heads/table-retraction Commit: 2d33c0bead3f13417acff0f18f78a5c2c5bef22e Parents: 464d6f5 Author: twalthr <twal...@apache.org> Authored: Fri Apr 28 17:17:30 2017 +0200 Committer: twalthr <twal...@apache.org> Committed: Tue May 2 15:31:35 2017 +0200 ---------------------------------------------------------------------- .../calcite/sql/fun/SqlGroupFunction.java | 103 + .../calcite/sql/fun/SqlStdOperatorTable.java | 2133 +++++++ .../apache/calcite/sql/validate/AggChecker.java | 225 + .../calcite/sql2rel/AuxiliaryConverter.java | 79 + .../calcite/sql2rel/SqlToRelConverter.java | 5356 ++++++++++++++++++ .../scala/batch/sql/WindowAggregateTest.scala | 31 + tools/maven/suppressions.xml | 2 + 7 files changed, 7929 insertions(+) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/flink/blob/2d33c0be/flink-libraries/flink-table/src/main/java/org/apache/calcite/sql/fun/SqlGroupFunction.java ---------------------------------------------------------------------- diff --git a/flink-libraries/flink-table/src/main/java/org/apache/calcite/sql/fun/SqlGroupFunction.java b/flink-libraries/flink-table/src/main/java/org/apache/calcite/sql/fun/SqlGroupFunction.java new file mode 100644 index 0000000..a57cf10 --- /dev/null +++ b/flink-libraries/flink-table/src/main/java/org/apache/calcite/sql/fun/SqlGroupFunction.java @@ -0,0 +1,103 @@ +/* + * 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.sql.fun; + +/* + * THIS FILE HAS BEEN COPIED FROM THE APACHE CALCITE PROJECT UNTIL CALCITE-1761 IS FIXED. + */ + +import org.apache.calcite.sql.SqlFunction; +import org.apache.calcite.sql.SqlFunctionCategory; +import org.apache.calcite.sql.SqlKind; +import org.apache.calcite.sql.SqlOperatorBinding; +import org.apache.calcite.sql.type.ReturnTypes; +import org.apache.calcite.sql.type.SqlOperandTypeChecker; +import org.apache.calcite.sql.validate.SqlMonotonicity; + +import com.google.common.collect.ImmutableList; + +import java.util.List; + +/** + * SQL function that computes keys by which rows can be partitioned and + * aggregated. + * + * <p>Grouped window functions always occur in the GROUP BY clause. They often + * have auxiliary functions that access information about the group. For + * example, {@code HOP} is a group function, and its auxiliary functions are + * {@code HOP_START} and {@code HOP_END}. Here they are used in a streaming + * query: + * + * <blockquote><pre> + * SELECT STREAM HOP_START(rowtime, INTERVAL '1' HOUR), + * HOP_END(rowtime, INTERVAL '1' HOUR), + * MIN(unitPrice) + * FROM Orders + * GROUP BY HOP(rowtime, INTERVAL '1' HOUR), productId + * </pre></blockquote> + */ +class SqlGroupFunction extends SqlFunction { + /** The grouped function, if this an auxiliary function; null otherwise. */ + final SqlGroupFunction groupFunction; + + /** Creates a SqlGroupFunction. + * + * @param kind Kind; also determines function name + * @param groupFunction Group function, if this is an auxiliary; + * null, if this is a group function + * @param operandTypeChecker Operand type checker + */ + SqlGroupFunction(SqlKind kind, SqlGroupFunction groupFunction, + SqlOperandTypeChecker operandTypeChecker) { + super(kind.name(), kind, ReturnTypes.ARG0, null, + operandTypeChecker, SqlFunctionCategory.SYSTEM); + this.groupFunction = groupFunction; + if (groupFunction != null) { + assert groupFunction.groupFunction == null; + } + } + + /** Creates an auxiliary function from this grouped window function. */ + SqlGroupFunction auxiliary(SqlKind kind) { + return new SqlGroupFunction(kind, this, getOperandTypeChecker()); + } + + /** Returns a list of this grouped window function's auxiliary functions. */ + List<SqlGroupFunction> getAuxiliaryFunctions() { + return ImmutableList.of(); + } + + @Override public boolean isGroup() { + // Auxiliary functions are not group functions + return groupFunction == null; + } + + @Override public boolean isGroupAuxiliary() { + return groupFunction != null; + } + + @Override public SqlMonotonicity getMonotonicity(SqlOperatorBinding call) { + // Monotonic iff its first argument is, but not strict. + // + // Note: This strategy happens to works for all current group functions + // (HOP, TUMBLE, SESSION). When there are exceptions to this rule, we'll + // make the method abstract. + return call.getOperandMonotonicity(0).unstrict(); + } +} + +// End SqlGroupFunction.java http://git-wip-us.apache.org/repos/asf/flink/blob/2d33c0be/flink-libraries/flink-table/src/main/java/org/apache/calcite/sql/fun/SqlStdOperatorTable.java ---------------------------------------------------------------------- diff --git a/flink-libraries/flink-table/src/main/java/org/apache/calcite/sql/fun/SqlStdOperatorTable.java b/flink-libraries/flink-table/src/main/java/org/apache/calcite/sql/fun/SqlStdOperatorTable.java new file mode 100644 index 0000000..6faf8e8 --- /dev/null +++ b/flink-libraries/flink-table/src/main/java/org/apache/calcite/sql/fun/SqlStdOperatorTable.java @@ -0,0 +1,2133 @@ +/* + * 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.sql.fun; + +/* + * THIS FILE HAS BEEN COPIED FROM THE APACHE CALCITE PROJECT UNTIL CALCITE-1761 IS FIXED. + */ + +import org.apache.calcite.avatica.util.TimeUnit; +import org.apache.calcite.rel.type.RelDataType; +import org.apache.calcite.rel.type.RelDataTypeFactory; +import org.apache.calcite.sql.SqlAggFunction; +import org.apache.calcite.sql.SqlAsOperator; +import org.apache.calcite.sql.SqlBasicCall; +import org.apache.calcite.sql.SqlBinaryOperator; +import org.apache.calcite.sql.SqlCall; +import org.apache.calcite.sql.SqlFilterOperator; +import org.apache.calcite.sql.SqlFunction; +import org.apache.calcite.sql.SqlFunctionCategory; +import org.apache.calcite.sql.SqlInternalOperator; +import org.apache.calcite.sql.SqlKind; +import org.apache.calcite.sql.SqlLateralOperator; +import org.apache.calcite.sql.SqlLiteral; +import org.apache.calcite.sql.SqlNode; +import org.apache.calcite.sql.SqlNumericLiteral; +import org.apache.calcite.sql.SqlOperandCountRange; +import org.apache.calcite.sql.SqlOperator; +import org.apache.calcite.sql.SqlOperatorBinding; +import org.apache.calcite.sql.SqlOverOperator; +import org.apache.calcite.sql.SqlPostfixOperator; +import org.apache.calcite.sql.SqlPrefixOperator; +import org.apache.calcite.sql.SqlProcedureCallOperator; +import org.apache.calcite.sql.SqlRankFunction; +import org.apache.calcite.sql.SqlSampleSpec; +import org.apache.calcite.sql.SqlSetOperator; +import org.apache.calcite.sql.SqlSpecialOperator; +import org.apache.calcite.sql.SqlUnnestOperator; +import org.apache.calcite.sql.SqlUtil; +import org.apache.calcite.sql.SqlValuesOperator; +import org.apache.calcite.sql.SqlWindow; +import org.apache.calcite.sql.SqlWriter; +import org.apache.calcite.sql.type.InferTypes; +import org.apache.calcite.sql.type.IntervalSqlType; +import org.apache.calcite.sql.type.OperandTypes; +import org.apache.calcite.sql.type.ReturnTypes; +import org.apache.calcite.sql.type.SqlOperandCountRanges; +import org.apache.calcite.sql.type.SqlTypeName; +import org.apache.calcite.sql.util.ReflectiveSqlOperatorTable; +import org.apache.calcite.sql.validate.SqlModality; +import org.apache.calcite.sql2rel.AuxiliaryConverter; +import org.apache.calcite.util.Litmus; +import org.apache.calcite.util.Pair; + +import com.google.common.collect.ImmutableList; + +import java.util.List; + +/** + * Implementation of {@link org.apache.calcite.sql.SqlOperatorTable} containing + * the standard operators and functions. + */ +public class SqlStdOperatorTable extends ReflectiveSqlOperatorTable { + //~ Static fields/initializers --------------------------------------------- + + /** + * The standard operator table. + */ + private static SqlStdOperatorTable instance; + + //------------------------------------------------------------- + // SET OPERATORS + //------------------------------------------------------------- + // The set operators can be compared to the arithmetic operators + // UNION -> + + // EXCEPT -> - + // INTERSECT -> * + // which explains the different precedence values + public static final SqlSetOperator UNION = + new SqlSetOperator("UNION", SqlKind.UNION, 14, false); + + public static final SqlSetOperator UNION_ALL = + new SqlSetOperator("UNION ALL", SqlKind.UNION, 14, true); + + public static final SqlSetOperator EXCEPT = + new SqlSetOperator("EXCEPT", SqlKind.EXCEPT, 14, false); + + public static final SqlSetOperator EXCEPT_ALL = + new SqlSetOperator("EXCEPT ALL", SqlKind.EXCEPT, 14, true); + + public static final SqlSetOperator INTERSECT = + new SqlSetOperator("INTERSECT", SqlKind.INTERSECT, 18, false); + + public static final SqlSetOperator INTERSECT_ALL = + new SqlSetOperator("INTERSECT ALL", SqlKind.INTERSECT, 18, true); + + /** + * The "MULTISET UNION" operator. + */ + public static final SqlMultisetSetOperator MULTISET_UNION = + new SqlMultisetSetOperator("MULTISET UNION", 14, false); + + /** + * The "MULTISET UNION ALL" operator. + */ + public static final SqlMultisetSetOperator MULTISET_UNION_ALL = + new SqlMultisetSetOperator("MULTISET UNION ALL", 14, true); + + /** + * The "MULTISET EXCEPT" operator. + */ + public static final SqlMultisetSetOperator MULTISET_EXCEPT = + new SqlMultisetSetOperator("MULTISET EXCEPT", 14, false); + + /** + * The "MULTISET EXCEPT ALL" operator. + */ + public static final SqlMultisetSetOperator MULTISET_EXCEPT_ALL = + new SqlMultisetSetOperator("MULTISET EXCEPT ALL", 14, true); + + /** + * The "MULTISET INTERSECT" operator. + */ + public static final SqlMultisetSetOperator MULTISET_INTERSECT = + new SqlMultisetSetOperator("MULTISET INTERSECT", 18, false); + + /** + * The "MULTISET INTERSECT ALL" operator. + */ + public static final SqlMultisetSetOperator MULTISET_INTERSECT_ALL = + new SqlMultisetSetOperator("MULTISET INTERSECT ALL", 18, true); + + //------------------------------------------------------------- + // BINARY OPERATORS + //------------------------------------------------------------- + + /** + * Logical <code>AND</code> operator. + */ + public static final SqlBinaryOperator AND = + new SqlBinaryOperator( + "AND", + SqlKind.AND, + 24, + true, + ReturnTypes.BOOLEAN_NULLABLE_OPTIMIZED, + InferTypes.BOOLEAN, + OperandTypes.BOOLEAN_BOOLEAN); + + /** + * <code>AS</code> operator associates an expression in the SELECT clause + * with an alias. + */ + public static final SqlAsOperator AS = new SqlAsOperator(); + + /** + * <code>ARGUMENT_ASSIGNMENT</code> operator (<code>=<</code>) + * assigns an argument to a function call to a particular named parameter. + */ + public static final SqlSpecialOperator ARGUMENT_ASSIGNMENT = + new SqlArgumentAssignmentOperator(); + + /** + * <code>DEFAULT</code> operator indicates that an argument to a function call + * is to take its default value.. + */ + public static final SqlSpecialOperator DEFAULT = new SqlDefaultOperator(); + + /** <code>FILTER</code> operator filters which rows are included in an + * aggregate function. */ + public static final SqlFilterOperator FILTER = new SqlFilterOperator(); + + /** {@code CUBE} operator, occurs within {@code GROUP BY} clause + * or nested within a {@code GROUPING SETS}. */ + public static final SqlInternalOperator CUBE = + new SqlRollupOperator("CUBE", SqlKind.CUBE); + + /** {@code ROLLUP} operator, occurs within {@code GROUP BY} clause + * or nested within a {@code GROUPING SETS}. */ + public static final SqlInternalOperator ROLLUP = + new SqlRollupOperator("ROLLUP", SqlKind.ROLLUP); + + /** {@code GROUPING SETS} operator, occurs within {@code GROUP BY} clause + * or nested within a {@code GROUPING SETS}. */ + public static final SqlInternalOperator GROUPING_SETS = + new SqlRollupOperator("GROUPING SETS", SqlKind.GROUPING_SETS); + + /** {@code GROUPING} function. Occurs in similar places to an aggregate + * function ({@code SELECT}, {@code HAVING} clause, etc. of an aggregate + * query), but not technically an aggregate function. */ + public static final SqlGroupingFunction GROUPING = + new SqlGroupingFunction("GROUPING"); + + /** {@code GROUP_ID} function. */ + public static final SqlGroupIdFunction GROUP_ID = + new SqlGroupIdFunction(); + + /** {@code GROUP_ID} function is a synonym for {@code GROUPING}. + * + * <p>Some history. The {@code GROUPING} function is in the SQL standard, + * and originally supported only one argument. The {@code GROUP_ID} is not + * standard (though supported in Oracle and SQL Server) and supports zero or + * more arguments. + * + * <p>The SQL standard has changed to allow {@code GROUPING} to have multiple + * arguments. It is now equivalent to {@code GROUP_ID}, so we made + * {@code GROUP_ID} a synonym for {@code GROUPING}. */ + public static final SqlGroupingFunction GROUPING_ID = + new SqlGroupingFunction("GROUPING_ID"); + + /** {@code EXTEND} operator. */ + public static final SqlInternalOperator EXTEND = new SqlExtendOperator(); + + /** + * String concatenation operator, '<code>||</code>'. + */ + public static final SqlBinaryOperator CONCAT = + new SqlBinaryOperator( + "||", + SqlKind.OTHER, + 60, + true, + ReturnTypes.DYADIC_STRING_SUM_PRECISION_NULLABLE, + null, + OperandTypes.STRING_SAME_SAME); + + /** + * Arithmetic division operator, '<code>/</code>'. + */ + public static final SqlBinaryOperator DIVIDE = + new SqlBinaryOperator( + "/", + SqlKind.DIVIDE, + 60, + true, + ReturnTypes.QUOTIENT_NULLABLE, + InferTypes.FIRST_KNOWN, + OperandTypes.DIVISION_OPERATOR); + + /** The {@code RAND_INTEGER([seed, ] bound)} function, which yields a random + * integer, optionally with seed. */ + public static final SqlRandIntegerFunction RAND_INTEGER = + new SqlRandIntegerFunction(); + + /** The {@code RAND([seed])} function, which yields a random double, + * optionally with seed. */ + public static final SqlRandFunction RAND = new SqlRandFunction(); + + /** + * Internal integer arithmetic division operator, '<code>/INT</code>'. This + * is only used to adjust scale for numerics. We distinguish it from + * user-requested division since some personalities want a floating-point + * computation, whereas for the internal scaling use of division, we always + * want integer division. + */ + public static final SqlBinaryOperator DIVIDE_INTEGER = + new SqlBinaryOperator( + "/INT", + SqlKind.DIVIDE, + 60, + true, + ReturnTypes.INTEGER_QUOTIENT_NULLABLE, + InferTypes.FIRST_KNOWN, + OperandTypes.DIVISION_OPERATOR); + + /** + * Dot operator, '<code>.</code>', used for referencing fields of records. + */ + public static final SqlBinaryOperator DOT = + new SqlBinaryOperator( + ".", + SqlKind.DOT, + 80, + true, + null, + null, + OperandTypes.ANY_ANY); + + /** + * Logical equals operator, '<code>=</code>'. + */ + public static final SqlBinaryOperator EQUALS = + new SqlBinaryOperator( + "=", + SqlKind.EQUALS, + 30, + true, + ReturnTypes.BOOLEAN_NULLABLE, + InferTypes.FIRST_KNOWN, + OperandTypes.COMPARABLE_UNORDERED_COMPARABLE_UNORDERED); + + /** + * Logical greater-than operator, '<code>></code>'. + */ + public static final SqlBinaryOperator GREATER_THAN = + new SqlBinaryOperator( + ">", + SqlKind.GREATER_THAN, + 30, + true, + ReturnTypes.BOOLEAN_NULLABLE, + InferTypes.FIRST_KNOWN, + OperandTypes.COMPARABLE_ORDERED_COMPARABLE_ORDERED); + + /** + * <code>IS DISTINCT FROM</code> operator. + */ + public static final SqlBinaryOperator IS_DISTINCT_FROM = + new SqlBinaryOperator( + "IS DISTINCT FROM", + SqlKind.IS_DISTINCT_FROM, + 30, + true, + ReturnTypes.BOOLEAN, + InferTypes.FIRST_KNOWN, + OperandTypes.COMPARABLE_UNORDERED_COMPARABLE_UNORDERED); + + /** + * <code>IS NOT DISTINCT FROM</code> operator. Is equivalent to <code>NOT(x + * IS DISTINCT FROM y)</code> + */ + public static final SqlBinaryOperator IS_NOT_DISTINCT_FROM = + new SqlBinaryOperator( + "IS NOT DISTINCT FROM", + SqlKind.IS_NOT_DISTINCT_FROM, + 30, + true, + ReturnTypes.BOOLEAN, + InferTypes.FIRST_KNOWN, + OperandTypes.COMPARABLE_UNORDERED_COMPARABLE_UNORDERED); + + /** + * The internal <code>$IS_DIFFERENT_FROM</code> operator is the same as the + * user-level {@link #IS_DISTINCT_FROM} in all respects except that + * the test for equality on character datatypes treats trailing spaces as + * significant. + */ + public static final SqlBinaryOperator IS_DIFFERENT_FROM = + new SqlBinaryOperator( + "$IS_DIFFERENT_FROM", + SqlKind.OTHER, + 30, + true, + ReturnTypes.BOOLEAN, + InferTypes.FIRST_KNOWN, + OperandTypes.COMPARABLE_UNORDERED_COMPARABLE_UNORDERED); + + /** + * Logical greater-than-or-equal operator, '<code>>=</code>'. + */ + public static final SqlBinaryOperator GREATER_THAN_OR_EQUAL = + new SqlBinaryOperator( + ">=", + SqlKind.GREATER_THAN_OR_EQUAL, + 30, + true, + ReturnTypes.BOOLEAN_NULLABLE, + InferTypes.FIRST_KNOWN, + OperandTypes.COMPARABLE_ORDERED_COMPARABLE_ORDERED); + + /** + * <code>IN</code> operator tests for a value's membership in a sub-query or + * a list of values. + */ + public static final SqlBinaryOperator IN = new SqlInOperator(false); + + /** + * <code>NOT IN</code> operator tests for a value's membership in a sub-query + * or a list of values. + */ + public static final SqlBinaryOperator NOT_IN = + new SqlInOperator(true); + + /** + * Logical less-than operator, '<code><</code>'. + */ + public static final SqlBinaryOperator LESS_THAN = + new SqlBinaryOperator( + "<", + SqlKind.LESS_THAN, + 30, + true, + ReturnTypes.BOOLEAN_NULLABLE, + InferTypes.FIRST_KNOWN, + OperandTypes.COMPARABLE_ORDERED_COMPARABLE_ORDERED); + + /** + * Logical less-than-or-equal operator, '<code><=</code>'. + */ + public static final SqlBinaryOperator LESS_THAN_OR_EQUAL = + new SqlBinaryOperator( + "<=", + SqlKind.LESS_THAN_OR_EQUAL, + 30, + true, + ReturnTypes.BOOLEAN_NULLABLE, + InferTypes.FIRST_KNOWN, + OperandTypes.COMPARABLE_ORDERED_COMPARABLE_ORDERED); + + /** + * Infix arithmetic minus operator, '<code>-</code>'. + * + * <p>Its precedence is less than the prefix {@link #UNARY_PLUS +} + * and {@link #UNARY_MINUS -} operators. + */ + public static final SqlBinaryOperator MINUS = + new SqlMonotonicBinaryOperator( + "-", + SqlKind.MINUS, + 40, + true, + + // Same type inference strategy as sum + ReturnTypes.NULLABLE_SUM, + InferTypes.FIRST_KNOWN, + OperandTypes.MINUS_OPERATOR); + + /** + * Arithmetic multiplication operator, '<code>*</code>'. + */ + public static final SqlBinaryOperator MULTIPLY = + new SqlMonotonicBinaryOperator( + "*", + SqlKind.TIMES, + 60, + true, + ReturnTypes.PRODUCT_NULLABLE, + InferTypes.FIRST_KNOWN, + OperandTypes.MULTIPLY_OPERATOR); + + /** + * Logical not-equals operator, '<code><></code>'. + */ + public static final SqlBinaryOperator NOT_EQUALS = + new SqlBinaryOperator( + "<>", + SqlKind.NOT_EQUALS, + 30, + true, + ReturnTypes.BOOLEAN_NULLABLE, + InferTypes.FIRST_KNOWN, + OperandTypes.COMPARABLE_UNORDERED_COMPARABLE_UNORDERED); + + /** + * Logical <code>OR</code> operator. + */ + public static final SqlBinaryOperator OR = + new SqlBinaryOperator( + "OR", + SqlKind.OR, + 22, + true, + ReturnTypes.BOOLEAN_NULLABLE_OPTIMIZED, + InferTypes.BOOLEAN, + OperandTypes.BOOLEAN_BOOLEAN); + + /** + * Infix arithmetic plus operator, '<code>+</code>'. + */ + public static final SqlBinaryOperator PLUS = + new SqlMonotonicBinaryOperator( + "+", + SqlKind.PLUS, + 40, + true, + ReturnTypes.NULLABLE_SUM, + InferTypes.FIRST_KNOWN, + OperandTypes.PLUS_OPERATOR); + + /** + * Infix datetime plus operator, '<code>DATETIME + INTERVAL</code>'. + */ + public static final SqlSpecialOperator DATETIME_PLUS = + new SqlSpecialOperator("DATETIME_PLUS", SqlKind.PLUS, 40, true, null, + InferTypes.FIRST_KNOWN, OperandTypes.PLUS_OPERATOR) { + @Override public RelDataType + inferReturnType(SqlOperatorBinding opBinding) { + final RelDataTypeFactory typeFactory = opBinding.getTypeFactory(); + final RelDataType leftType = opBinding.getOperandType(0); + final IntervalSqlType unitType = + (IntervalSqlType) opBinding.getOperandType(1); + switch (unitType.getIntervalQualifier().getStartUnit()) { + case HOUR: + case MINUTE: + case SECOND: + case MILLISECOND: + case MICROSECOND: + return typeFactory.createTypeWithNullability( + typeFactory.createSqlType(SqlTypeName.TIMESTAMP), + leftType.isNullable() || unitType.isNullable()); + default: + return leftType; + } + } + }; + + /** + * Multiset {@code MEMBER OF}, which returns whether a element belongs to a + * multiset. + * + * <p>For example, the following returns <code>false</code>: + * + * <blockquote> + * <code>'green' MEMBER OF MULTISET ['red','almost green','blue']</code> + * </blockquote> + */ + public static final SqlBinaryOperator MEMBER_OF = + new SqlMultisetMemberOfOperator(); + + /** + * Submultiset. Checks to see if an multiset is a sub-set of another + * multiset. + * + * <p>For example, the following returns <code>false</code>: + * + * <blockquote> + * <code>MULTISET ['green'] SUBMULTISET OF + * MULTISET['red', 'almost green', 'blue']</code> + * </blockquote> + * + * <p>The following returns <code>true</code>, in part because multisets are + * order-independent: + * + * <blockquote> + * <code>MULTISET ['blue', 'red'] SUBMULTISET OF + * MULTISET ['red', 'almost green', 'blue']</code> + * </blockquote> + */ + public static final SqlBinaryOperator SUBMULTISET_OF = + + // TODO: check if precedence is correct + new SqlBinaryOperator( + "SUBMULTISET OF", + SqlKind.OTHER, + 30, + true, + ReturnTypes.BOOLEAN_NULLABLE, + null, + OperandTypes.MULTISET_MULTISET); + + //------------------------------------------------------------- + // POSTFIX OPERATORS + //------------------------------------------------------------- + public static final SqlPostfixOperator DESC = + new SqlPostfixOperator( + "DESC", + SqlKind.DESCENDING, + 20, + ReturnTypes.ARG0, + InferTypes.RETURN_TYPE, + OperandTypes.ANY); + + public static final SqlPostfixOperator NULLS_FIRST = + new SqlPostfixOperator( + "NULLS FIRST", + SqlKind.NULLS_FIRST, + 18, + ReturnTypes.ARG0, + InferTypes.RETURN_TYPE, + OperandTypes.ANY); + + public static final SqlPostfixOperator NULLS_LAST = + new SqlPostfixOperator( + "NULLS LAST", + SqlKind.NULLS_LAST, + 18, + ReturnTypes.ARG0, + InferTypes.RETURN_TYPE, + OperandTypes.ANY); + + public static final SqlPostfixOperator IS_NOT_NULL = + new SqlPostfixOperator( + "IS NOT NULL", + SqlKind.IS_NOT_NULL, + 28, + ReturnTypes.BOOLEAN_NOT_NULL, + InferTypes.VARCHAR_1024, + OperandTypes.ANY); + + public static final SqlPostfixOperator IS_NULL = + new SqlPostfixOperator( + "IS NULL", + SqlKind.IS_NULL, + 28, + ReturnTypes.BOOLEAN_NOT_NULL, + InferTypes.VARCHAR_1024, + OperandTypes.ANY); + + public static final SqlPostfixOperator IS_NOT_TRUE = + new SqlPostfixOperator( + "IS NOT TRUE", + SqlKind.IS_NOT_TRUE, + 28, + ReturnTypes.BOOLEAN_NOT_NULL, + InferTypes.BOOLEAN, + OperandTypes.BOOLEAN); + + public static final SqlPostfixOperator IS_TRUE = + new SqlPostfixOperator( + "IS TRUE", + SqlKind.IS_TRUE, + 28, + ReturnTypes.BOOLEAN_NOT_NULL, + InferTypes.BOOLEAN, + OperandTypes.BOOLEAN); + + public static final SqlPostfixOperator IS_NOT_FALSE = + new SqlPostfixOperator( + "IS NOT FALSE", + SqlKind.IS_NOT_FALSE, + 28, + ReturnTypes.BOOLEAN_NOT_NULL, + InferTypes.BOOLEAN, + OperandTypes.BOOLEAN); + + public static final SqlPostfixOperator IS_FALSE = + new SqlPostfixOperator( + "IS FALSE", + SqlKind.IS_FALSE, + 28, + ReturnTypes.BOOLEAN_NOT_NULL, + InferTypes.BOOLEAN, + OperandTypes.BOOLEAN); + + public static final SqlPostfixOperator IS_NOT_UNKNOWN = + new SqlPostfixOperator( + "IS NOT UNKNOWN", + SqlKind.IS_NOT_NULL, + 28, + ReturnTypes.BOOLEAN_NOT_NULL, + InferTypes.BOOLEAN, + OperandTypes.BOOLEAN); + + public static final SqlPostfixOperator IS_UNKNOWN = + new SqlPostfixOperator( + "IS UNKNOWN", + SqlKind.IS_NULL, + 28, + ReturnTypes.BOOLEAN_NOT_NULL, + InferTypes.BOOLEAN, + OperandTypes.BOOLEAN); + + public static final SqlPostfixOperator IS_A_SET = + new SqlPostfixOperator( + "IS A SET", + SqlKind.OTHER, + 28, + ReturnTypes.BOOLEAN, + null, + OperandTypes.MULTISET); + + //------------------------------------------------------------- + // PREFIX OPERATORS + //------------------------------------------------------------- + public static final SqlPrefixOperator EXISTS = + new SqlPrefixOperator( + "EXISTS", + SqlKind.EXISTS, + 40, + ReturnTypes.BOOLEAN, + null, + OperandTypes.ANY) { + public boolean argumentMustBeScalar(int ordinal) { + return false; + } + + @Override public boolean validRexOperands(int count, Litmus litmus) { + if (count != 0) { + return litmus.fail("wrong operand count {} for {}", count, this); + } + return litmus.succeed(); + } + }; + + public static final SqlPrefixOperator NOT = + new SqlPrefixOperator( + "NOT", + SqlKind.NOT, + 26, + ReturnTypes.ARG0, + InferTypes.BOOLEAN, + OperandTypes.BOOLEAN); + + /** + * Prefix arithmetic minus operator, '<code>-</code>'. + * + * <p>Its precedence is greater than the infix '{@link #PLUS +}' and + * '{@link #MINUS -}' operators. + */ + public static final SqlPrefixOperator UNARY_MINUS = + new SqlPrefixOperator( + "-", + SqlKind.MINUS_PREFIX, + 80, + ReturnTypes.ARG0, + InferTypes.RETURN_TYPE, + OperandTypes.NUMERIC_OR_INTERVAL); + + /** + * Prefix arithmetic plus operator, '<code>+</code>'. + * + * <p>Its precedence is greater than the infix '{@link #PLUS +}' and + * '{@link #MINUS -}' operators. + */ + public static final SqlPrefixOperator UNARY_PLUS = + new SqlPrefixOperator( + "+", + SqlKind.PLUS_PREFIX, + 80, + ReturnTypes.ARG0, + InferTypes.RETURN_TYPE, + OperandTypes.NUMERIC_OR_INTERVAL); + + /** + * Keyword which allows an identifier to be explicitly flagged as a table. + * For example, <code>select * from (TABLE t)</code> or <code>TABLE + * t</code>. See also {@link #COLLECTION_TABLE}. + */ + public static final SqlPrefixOperator EXPLICIT_TABLE = + new SqlPrefixOperator( + "TABLE", + SqlKind.EXPLICIT_TABLE, + 2, + null, + null, + null); + + //------------------------------------------------------------- + // AGGREGATE OPERATORS + //------------------------------------------------------------- + /** + * <code>SUM</code> aggregate function. + */ + public static final SqlAggFunction SUM = new SqlSumAggFunction(null); + + /** + * <code>COUNT</code> aggregate function. + */ + public static final SqlAggFunction COUNT = new SqlCountAggFunction(); + + /** + * <code>MIN</code> aggregate function. + */ + public static final SqlAggFunction MIN = + new SqlMinMaxAggFunction(SqlKind.MIN); + + /** + * <code>MAX</code> aggregate function. + */ + public static final SqlAggFunction MAX = + new SqlMinMaxAggFunction(SqlKind.MAX); + + /** + * <code>LAST_VALUE</code> aggregate function. + */ + public static final SqlAggFunction LAST_VALUE = + new SqlFirstLastValueAggFunction(SqlKind.LAST_VALUE); + + /** + * <code>FIRST_VALUE</code> aggregate function. + */ + public static final SqlAggFunction FIRST_VALUE = + new SqlFirstLastValueAggFunction(SqlKind.FIRST_VALUE); + + /** + * <code>LEAD</code> aggregate function. + */ + public static final SqlAggFunction LEAD = + new SqlLeadLagAggFunction(SqlKind.LEAD); + + /** + * <code>LAG</code> aggregate function. + */ + public static final SqlAggFunction LAG = + new SqlLeadLagAggFunction(SqlKind.LAG); + + /** + * <code>NTILE</code> aggregate function. + */ + public static final SqlAggFunction NTILE = + new SqlNtileAggFunction(); + + /** + * <code>SINGLE_VALUE</code> aggregate function. + */ + public static final SqlAggFunction SINGLE_VALUE = + new SqlSingleValueAggFunction(null); + + /** + * <code>AVG</code> aggregate function. + */ + public static final SqlAggFunction AVG = + new SqlAvgAggFunction(SqlKind.AVG); + + /** + * <code>STDDEV_POP</code> aggregate function. + */ + public static final SqlAggFunction STDDEV_POP = + new SqlAvgAggFunction(SqlKind.STDDEV_POP); + + /** + * <code>REGR_SXX</code> aggregate function. + */ + public static final SqlAggFunction REGR_SXX = + new SqlCovarAggFunction(SqlKind.REGR_SXX); + + /** + * <code>REGR_SYY</code> aggregate function. + */ + public static final SqlAggFunction REGR_SYY = + new SqlCovarAggFunction(SqlKind.REGR_SYY); + + /** + * <code>COVAR_POP</code> aggregate function. + */ + public static final SqlAggFunction COVAR_POP = + new SqlCovarAggFunction(SqlKind.COVAR_POP); + + /** + * <code>COVAR_SAMP</code> aggregate function. + */ + public static final SqlAggFunction COVAR_SAMP = + new SqlCovarAggFunction(SqlKind.COVAR_SAMP); + + /** + * <code>STDDEV_SAMP</code> aggregate function. + */ + public static final SqlAggFunction STDDEV_SAMP = + new SqlAvgAggFunction(SqlKind.STDDEV_SAMP); + + /** + * <code>VAR_POP</code> aggregate function. + */ + public static final SqlAggFunction VAR_POP = + new SqlAvgAggFunction(SqlKind.VAR_POP); + + /** + * <code>VAR_SAMP</code> aggregate function. + */ + public static final SqlAggFunction VAR_SAMP = + new SqlAvgAggFunction(SqlKind.VAR_SAMP); + + //------------------------------------------------------------- + // WINDOW Aggregate Functions + //------------------------------------------------------------- + /** + * <code>HISTOGRAM</code> aggregate function support. Used by window + * aggregate versions of MIN/MAX + */ + public static final SqlAggFunction HISTOGRAM_AGG = + new SqlHistogramAggFunction(null); + + /** + * <code>HISTOGRAM_MIN</code> window aggregate function. + */ + public static final SqlFunction HISTOGRAM_MIN = + new SqlFunction( + "$HISTOGRAM_MIN", + SqlKind.OTHER_FUNCTION, + ReturnTypes.ARG0_NULLABLE, + null, + OperandTypes.NUMERIC_OR_STRING, + SqlFunctionCategory.NUMERIC); + + /** + * <code>HISTOGRAM_MAX</code> window aggregate function. + */ + public static final SqlFunction HISTOGRAM_MAX = + new SqlFunction( + "$HISTOGRAM_MAX", + SqlKind.OTHER_FUNCTION, + ReturnTypes.ARG0_NULLABLE, + null, + OperandTypes.NUMERIC_OR_STRING, + SqlFunctionCategory.NUMERIC); + + /** + * <code>HISTOGRAM_FIRST_VALUE</code> window aggregate function. + */ + public static final SqlFunction HISTOGRAM_FIRST_VALUE = + new SqlFunction( + "$HISTOGRAM_FIRST_VALUE", + SqlKind.OTHER_FUNCTION, + ReturnTypes.ARG0_NULLABLE, + null, + OperandTypes.NUMERIC_OR_STRING, + SqlFunctionCategory.NUMERIC); + + /** + * <code>HISTOGRAM_LAST_VALUE</code> window aggregate function. + */ + public static final SqlFunction HISTOGRAM_LAST_VALUE = + new SqlFunction( + "$HISTOGRAM_LAST_VALUE", + SqlKind.OTHER_FUNCTION, + ReturnTypes.ARG0_NULLABLE, + null, + OperandTypes.NUMERIC_OR_STRING, + SqlFunctionCategory.NUMERIC); + + /** + * <code>SUM0</code> aggregate function. + */ + public static final SqlAggFunction SUM0 = + new SqlSumEmptyIsZeroAggFunction(); + + //------------------------------------------------------------- + // WINDOW Rank Functions + //------------------------------------------------------------- + /** + * <code>CUME_DIST</code> window function. + */ + public static final SqlRankFunction CUME_DIST = + new SqlRankFunction(true, SqlKind.CUME_DIST); + + /** + * <code>DENSE_RANK</code> window function. + */ + public static final SqlRankFunction DENSE_RANK = + new SqlRankFunction(true, SqlKind.DENSE_RANK); + + /** + * <code>PERCENT_RANK</code> window function. + */ + public static final SqlRankFunction PERCENT_RANK = + new SqlRankFunction(true, SqlKind.PERCENT_RANK); + + /** + * <code>RANK</code> window function. + */ + public static final SqlRankFunction RANK = + new SqlRankFunction(true, SqlKind.RANK); + + /** + * <code>ROW_NUMBER</code> window function. + */ + public static final SqlRankFunction ROW_NUMBER = + new SqlRankFunction(false, SqlKind.ROW_NUMBER); + + //------------------------------------------------------------- + // SPECIAL OPERATORS + //------------------------------------------------------------- + public static final SqlRowOperator ROW = new SqlRowOperator("ROW"); + + /** + * A special operator for the subtraction of two DATETIMEs. The format of + * DATETIME subtraction is: + * + * <blockquote><code>"(" <datetime> "-" <datetime> ")" + * <interval qualifier></code></blockquote> + * + * <p>This operator is special since it needs to hold the + * additional interval qualifier specification.</p> + */ + public static final SqlDatetimeSubtractionOperator MINUS_DATE = + new SqlDatetimeSubtractionOperator(); + + /** + * The MULTISET Value Constructor. e.g. "<code>MULTISET[1,2,3]</code>". + */ + public static final SqlMultisetValueConstructor MULTISET_VALUE = + new SqlMultisetValueConstructor(); + + /** + * The MULTISET Query Constructor. e.g. "<code>SELECT dname, MULTISET(SELECT + * FROM emp WHERE deptno = dept.deptno) FROM dept</code>". + */ + public static final SqlMultisetQueryConstructor MULTISET_QUERY = + new SqlMultisetQueryConstructor(); + + /** + * The ARRAY Query Constructor. e.g. "<code>SELECT dname, ARRAY(SELECT + * FROM emp WHERE deptno = dept.deptno) FROM dept</code>". + */ + public static final SqlMultisetQueryConstructor ARRAY_QUERY = + new SqlArrayQueryConstructor(); + + /** + * The MAP Query Constructor. e.g. "<code>MAP(SELECT empno, deptno + * FROM emp)</code>". + */ + public static final SqlMultisetQueryConstructor MAP_QUERY = + new SqlMapQueryConstructor(); + + /** + * The CURSOR constructor. e.g. "<code>SELECT * FROM + * TABLE(DEDUP(CURSOR(SELECT * FROM EMPS), 'name'))</code>". + */ + public static final SqlCursorConstructor CURSOR = + new SqlCursorConstructor(); + + /** + * The COLUMN_LIST constructor. e.g. the ROW() call in "<code>SELECT * FROM + * TABLE(DEDUP(CURSOR(SELECT * FROM EMPS), ROW(name, empno)))</code>". + */ + public static final SqlColumnListConstructor COLUMN_LIST = + new SqlColumnListConstructor(); + + /** + * The <code>UNNEST</code> operator. + */ + public static final SqlUnnestOperator UNNEST = + new SqlUnnestOperator(false); + + /** + * The <code>UNNEST WITH ORDINALITY</code> operator. + */ + public static final SqlUnnestOperator UNNEST_WITH_ORDINALITY = + new SqlUnnestOperator(true); + + /** + * The <code>LATERAL</code> operator. + */ + public static final SqlSpecialOperator LATERAL = + new SqlLateralOperator(SqlKind.LATERAL); + + /** + * The "table function derived table" operator, which a table-valued + * function into a relation, e.g. "<code>SELECT * FROM + * TABLE(ramp(5))</code>". + * + * <p>This operator has function syntax (with one argument), whereas + * {@link #EXPLICIT_TABLE} is a prefix operator. + */ + public static final SqlSpecialOperator COLLECTION_TABLE = + new SqlCollectionTableOperator("TABLE", SqlModality.RELATION); + + public static final SqlOverlapsOperator OVERLAPS = + new SqlOverlapsOperator(); + + public static final SqlSpecialOperator VALUES = + new SqlValuesOperator(); + + public static final SqlLiteralChainOperator LITERAL_CHAIN = + new SqlLiteralChainOperator(); + + public static final SqlThrowOperator THROW = new SqlThrowOperator(); + + public static final SqlBetweenOperator BETWEEN = + new SqlBetweenOperator( + SqlBetweenOperator.Flag.ASYMMETRIC, + false); + + public static final SqlBetweenOperator SYMMETRIC_BETWEEN = + new SqlBetweenOperator( + SqlBetweenOperator.Flag.SYMMETRIC, + false); + + public static final SqlBetweenOperator NOT_BETWEEN = + new SqlBetweenOperator( + SqlBetweenOperator.Flag.ASYMMETRIC, + true); + + public static final SqlBetweenOperator SYMMETRIC_NOT_BETWEEN = + new SqlBetweenOperator( + SqlBetweenOperator.Flag.SYMMETRIC, + true); + + public static final SqlSpecialOperator NOT_LIKE = + new SqlLikeOperator("NOT LIKE", SqlKind.LIKE, true); + + public static final SqlSpecialOperator LIKE = + new SqlLikeOperator("LIKE", SqlKind.LIKE, false); + + public static final SqlSpecialOperator NOT_SIMILAR_TO = + new SqlLikeOperator("NOT SIMILAR TO", SqlKind.SIMILAR, true); + + public static final SqlSpecialOperator SIMILAR_TO = + new SqlLikeOperator("SIMILAR TO", SqlKind.SIMILAR, false); + + /** + * Internal operator used to represent the ESCAPE clause of a LIKE or + * SIMILAR TO expression. + */ + public static final SqlSpecialOperator ESCAPE = + new SqlSpecialOperator("ESCAPE", SqlKind.ESCAPE, 0); + + public static final SqlCaseOperator CASE = SqlCaseOperator.INSTANCE; + + public static final SqlOperator PROCEDURE_CALL = + new SqlProcedureCallOperator(); + + public static final SqlOperator NEW = new SqlNewOperator(); + + /** + * The <code>OVER</code> operator, which applies an aggregate functions to a + * {@link SqlWindow window}. + * + * <p>Operands are as follows: + * + * <ol> + * <li>name of window function ({@link org.apache.calcite.sql.SqlCall})</li> + * <li>window name ({@link org.apache.calcite.sql.SqlLiteral}) or window + * in-line specification (@link SqlWindowOperator})</li> + * </ol> + */ + public static final SqlBinaryOperator OVER = new SqlOverOperator(); + + /** + * An <code>REINTERPRET</code> operator is internal to the planner. When the + * physical storage of two types is the same, this operator may be used to + * reinterpret values of one type as the other. This operator is similar to + * a cast, except that it does not alter the data value. Like a regular cast + * it accepts one operand and stores the target type as the return type. It + * performs an overflow check if it has <i>any</i> second operand, whether + * true or not. + */ + public static final SqlSpecialOperator REINTERPRET = + new SqlSpecialOperator("Reinterpret", SqlKind.REINTERPRET) { + public SqlOperandCountRange getOperandCountRange() { + return SqlOperandCountRanges.between(1, 2); + } + }; + + /** Internal operator that extracts time periods (year, month, date) from a + * date in internal format (number of days since epoch). */ + public static final SqlSpecialOperator EXTRACT_DATE = + new SqlSpecialOperator("EXTRACT_DATE", SqlKind.EXTRACT); + + //------------------------------------------------------------- + // FUNCTIONS + //------------------------------------------------------------- + + /** + * The character substring function: <code>SUBSTRING(string FROM start [FOR + * length])</code>. + * + * <p>If the length parameter is a constant, the length of the result is the + * minimum of the length of the input and that length. Otherwise it is the + * length of the input. + */ + public static final SqlFunction SUBSTRING = new SqlSubstringFunction(); + + /** The {@code REPLACE(string, search, replace)} function. Not standard SQL, + * but in Oracle and Postgres. */ + public static final SqlFunction REPLACE = + new SqlFunction("REPLACE", SqlKind.OTHER_FUNCTION, + ReturnTypes.ARG0_NULLABLE_VARYING, null, + OperandTypes.STRING_STRING_STRING, SqlFunctionCategory.STRING); + + public static final SqlFunction CONVERT = + new SqlConvertFunction("CONVERT"); + + /** + * The <code>TRANSLATE(<i>char_value</i> USING <i>translation_name</i>)</code> function + * alters the character set of a string value from one base character set to another. + * + * <p>It is defined in the SQL standard. See also non-standard + * {@link OracleSqlOperatorTable#TRANSLATE3}. + */ + public static final SqlFunction TRANSLATE = + new SqlConvertFunction("TRANSLATE"); + + public static final SqlFunction OVERLAY = new SqlOverlayFunction(); + + /** The "TRIM" function. */ + public static final SqlFunction TRIM = SqlTrimFunction.INSTANCE; + + public static final SqlFunction POSITION = new SqlPositionFunction(); + + public static final SqlFunction CHAR_LENGTH = + new SqlFunction( + "CHAR_LENGTH", + SqlKind.OTHER_FUNCTION, + ReturnTypes.INTEGER_NULLABLE, + null, + OperandTypes.CHARACTER, + SqlFunctionCategory.NUMERIC); + + public static final SqlFunction CHARACTER_LENGTH = + new SqlFunction( + "CHARACTER_LENGTH", + SqlKind.OTHER_FUNCTION, + ReturnTypes.INTEGER_NULLABLE, + null, + OperandTypes.CHARACTER, + SqlFunctionCategory.NUMERIC); + + public static final SqlFunction UPPER = + new SqlFunction( + "UPPER", + SqlKind.OTHER_FUNCTION, + ReturnTypes.ARG0_NULLABLE, + null, + OperandTypes.CHARACTER, + SqlFunctionCategory.STRING); + + public static final SqlFunction LOWER = + new SqlFunction( + "LOWER", + SqlKind.OTHER_FUNCTION, + ReturnTypes.ARG0_NULLABLE, + null, + OperandTypes.CHARACTER, + SqlFunctionCategory.STRING); + + public static final SqlFunction INITCAP = + new SqlFunction( + "INITCAP", + SqlKind.OTHER_FUNCTION, + ReturnTypes.ARG0_NULLABLE, + null, + OperandTypes.CHARACTER, + SqlFunctionCategory.STRING); + + /** + * Uses SqlOperatorTable.useDouble for its return type since we don't know + * what the result type will be by just looking at the operand types. For + * example POW(int, int) can return a non integer if the second operand is + * negative. + */ + public static final SqlFunction POWER = + new SqlFunction( + "POWER", + SqlKind.OTHER_FUNCTION, + ReturnTypes.DOUBLE_NULLABLE, + null, + OperandTypes.NUMERIC_NUMERIC, + SqlFunctionCategory.NUMERIC); + + public static final SqlFunction SQRT = + new SqlFunction( + "SQRT", + SqlKind.OTHER_FUNCTION, + ReturnTypes.DOUBLE_NULLABLE, + null, + OperandTypes.NUMERIC, + SqlFunctionCategory.NUMERIC); + + public static final SqlFunction MOD = + // Return type is same as divisor (2nd operand) + // SQL2003 Part2 Section 6.27, Syntax Rules 9 + new SqlFunction( + "MOD", + SqlKind.OTHER_FUNCTION, + ReturnTypes.ARG1_NULLABLE, + null, + OperandTypes.EXACT_NUMERIC_EXACT_NUMERIC, + SqlFunctionCategory.NUMERIC); + + public static final SqlFunction LN = + new SqlFunction( + "LN", + SqlKind.OTHER_FUNCTION, + ReturnTypes.DOUBLE_NULLABLE, + null, + OperandTypes.NUMERIC, + SqlFunctionCategory.NUMERIC); + + public static final SqlFunction LOG10 = + new SqlFunction( + "LOG10", + SqlKind.OTHER_FUNCTION, + ReturnTypes.DOUBLE_NULLABLE, + null, + OperandTypes.NUMERIC, + SqlFunctionCategory.NUMERIC); + + public static final SqlFunction ABS = + new SqlFunction( + "ABS", + SqlKind.OTHER_FUNCTION, + ReturnTypes.ARG0, + null, + OperandTypes.NUMERIC_OR_INTERVAL, + SqlFunctionCategory.NUMERIC); + + public static final SqlFunction ACOS = + new SqlFunction( + "ACOS", + SqlKind.OTHER_FUNCTION, + ReturnTypes.DOUBLE_NULLABLE, + null, + OperandTypes.NUMERIC, + SqlFunctionCategory.NUMERIC); + + public static final SqlFunction ASIN = + new SqlFunction( + "ASIN", + SqlKind.OTHER_FUNCTION, + ReturnTypes.DOUBLE_NULLABLE, + null, + OperandTypes.NUMERIC, + SqlFunctionCategory.NUMERIC); + + public static final SqlFunction ATAN = + new SqlFunction( + "ATAN", + SqlKind.OTHER_FUNCTION, + ReturnTypes.DOUBLE_NULLABLE, + null, + OperandTypes.NUMERIC, + SqlFunctionCategory.NUMERIC); + + public static final SqlFunction ATAN2 = + new SqlFunction( + "ATAN2", + SqlKind.OTHER_FUNCTION, + ReturnTypes.DOUBLE_NULLABLE, + null, + OperandTypes.NUMERIC_NUMERIC, + SqlFunctionCategory.NUMERIC); + + public static final SqlFunction COS = + new SqlFunction( + "COS", + SqlKind.OTHER_FUNCTION, + ReturnTypes.DOUBLE_NULLABLE, + null, + OperandTypes.NUMERIC, + SqlFunctionCategory.NUMERIC); + + public static final SqlFunction COT = + new SqlFunction( + "COT", + SqlKind.OTHER_FUNCTION, + ReturnTypes.DOUBLE_NULLABLE, + null, + OperandTypes.NUMERIC, + SqlFunctionCategory.NUMERIC); + + public static final SqlFunction DEGREES = + new SqlFunction( + "DEGREES", + SqlKind.OTHER_FUNCTION, + ReturnTypes.DOUBLE_NULLABLE, + null, + OperandTypes.NUMERIC, + SqlFunctionCategory.NUMERIC); + + public static final SqlFunction EXP = + new SqlFunction( + "EXP", + SqlKind.OTHER_FUNCTION, + ReturnTypes.DOUBLE_NULLABLE, + null, + OperandTypes.NUMERIC, + SqlFunctionCategory.NUMERIC); + + public static final SqlFunction RADIANS = + new SqlFunction( + "RADIANS", + SqlKind.OTHER_FUNCTION, + ReturnTypes.DOUBLE_NULLABLE, + null, + OperandTypes.NUMERIC, + SqlFunctionCategory.NUMERIC); + + public static final SqlFunction ROUND = + new SqlFunction( + "ROUND", + SqlKind.OTHER_FUNCTION, + ReturnTypes.ARG0, + null, + OperandTypes.NUMERIC_INTEGER, + SqlFunctionCategory.NUMERIC); + + public static final SqlFunction SIGN = + new SqlFunction( + "SIGN", + SqlKind.OTHER_FUNCTION, + ReturnTypes.ARG0, + null, + OperandTypes.NUMERIC, + SqlFunctionCategory.NUMERIC); + + public static final SqlFunction SIN = + new SqlFunction( + "SIN", + SqlKind.OTHER_FUNCTION, + ReturnTypes.DOUBLE_NULLABLE, + null, + OperandTypes.NUMERIC, + SqlFunctionCategory.NUMERIC); + + + public static final SqlFunction TAN = + new SqlFunction( + "TAN", + SqlKind.OTHER_FUNCTION, + ReturnTypes.DOUBLE_NULLABLE, + null, + OperandTypes.NUMERIC, + SqlFunctionCategory.NUMERIC); + + public static final SqlFunction TRUNCATE = + new SqlFunction( + "TRUNCATE", + SqlKind.OTHER_FUNCTION, + ReturnTypes.ARG0, + null, + OperandTypes.NUMERIC_INTEGER, + SqlFunctionCategory.NUMERIC); + + public static final SqlFunction PI = + new SqlBaseContextVariable("PI", ReturnTypes.DOUBLE, + SqlFunctionCategory.NUMERIC); + + /** {@code FINAL} function to be used within {@code MATCH_RECOGNIZE}. */ + public static final SqlFunction FINAL = + new SqlFunction("FINAL", SqlKind.FINAL, ReturnTypes.ARG0_NULLABLE, null, + OperandTypes.ANY, SqlFunctionCategory.MATCH_RECOGNIZE); + + /** {@code RUNNING} function to be used within {@code MATCH_RECOGNIZE}. */ + public static final SqlFunction RUNNING = + new SqlFunction("RUNNING", SqlKind.RUNNING, ReturnTypes.ARG0_NULLABLE, + null, OperandTypes.ANY, SqlFunctionCategory.MATCH_RECOGNIZE); + + /** {@code FIRST} function to be used within {@code MATCH_RECOGNIZE}. */ + public static final SqlFunction FIRST = + new SqlFunction("FIRST", SqlKind.FIRST, ReturnTypes.ARG0_NULLABLE, + null, OperandTypes.ANY_NUMERIC, SqlFunctionCategory.MATCH_RECOGNIZE); + + /** {@code LAST} function to be used within {@code MATCH_RECOGNIZE}. */ + public static final SqlFunction LAST = + new SqlFunction("LAST", SqlKind.LAST, ReturnTypes.ARG0_NULLABLE, + null, OperandTypes.ANY_NUMERIC, SqlFunctionCategory.MATCH_RECOGNIZE); + + /** {@code PREV} function to be used within {@code MATCH_RECOGNIZE}. */ + public static final SqlFunction PREV = + new SqlFunction("PREV", SqlKind.PREV, ReturnTypes.ARG0_NULLABLE, + null, OperandTypes.ANY_NUMERIC, SqlFunctionCategory.MATCH_RECOGNIZE); + + /** {@code NEXT} function to be used within {@code MATCH_RECOGNIZE}. */ + public static final SqlFunction NEXT = + new SqlFunction("NEXT", SqlKind.NEXT, ReturnTypes.ARG0_NULLABLE, null, + OperandTypes.ANY_NUMERIC, SqlFunctionCategory.MATCH_RECOGNIZE); + + public static final SqlFunction NULLIF = new SqlNullifFunction(); + + /** + * The COALESCE builtin function. + */ + public static final SqlFunction COALESCE = new SqlCoalesceFunction(); + + /** + * The <code>FLOOR</code> function. + */ + public static final SqlFunction FLOOR = new SqlFloorFunction(SqlKind.FLOOR); + + /** + * The <code>CEIL</code> function. + */ + public static final SqlFunction CEIL = new SqlFloorFunction(SqlKind.CEIL); + + /** + * The <code>USER</code> function. + */ + public static final SqlFunction USER = + new SqlStringContextVariable("USER"); + + /** + * The <code>CURRENT_USER</code> function. + */ + public static final SqlFunction CURRENT_USER = + new SqlStringContextVariable("CURRENT_USER"); + + /** + * The <code>SESSION_USER</code> function. + */ + public static final SqlFunction SESSION_USER = + new SqlStringContextVariable("SESSION_USER"); + + /** + * The <code>SYSTEM_USER</code> function. + */ + public static final SqlFunction SYSTEM_USER = + new SqlStringContextVariable("SYSTEM_USER"); + + /** + * The <code>CURRENT_PATH</code> function. + */ + public static final SqlFunction CURRENT_PATH = + new SqlStringContextVariable("CURRENT_PATH"); + + /** + * The <code>CURRENT_ROLE</code> function. + */ + public static final SqlFunction CURRENT_ROLE = + new SqlStringContextVariable("CURRENT_ROLE"); + + /** + * The <code>CURRENT_CATALOG</code> function. + */ + public static final SqlFunction CURRENT_CATALOG = + new SqlStringContextVariable("CURRENT_CATALOG"); + + /** + * The <code>CURRENT_SCHEMA</code> function. + */ + public static final SqlFunction CURRENT_SCHEMA = + new SqlStringContextVariable("CURRENT_SCHEMA"); + + /** + * The <code>LOCALTIME [(<i>precision</i>)]</code> function. + */ + public static final SqlFunction LOCALTIME = + new SqlAbstractTimeFunction("LOCALTIME", SqlTypeName.TIME); + + /** + * The <code>LOCALTIMESTAMP [(<i>precision</i>)]</code> function. + */ + public static final SqlFunction LOCALTIMESTAMP = + new SqlAbstractTimeFunction("LOCALTIMESTAMP", SqlTypeName.TIMESTAMP); + + /** + * The <code>CURRENT_TIME [(<i>precision</i>)]</code> function. + */ + public static final SqlFunction CURRENT_TIME = + new SqlAbstractTimeFunction("CURRENT_TIME", SqlTypeName.TIME); + + /** + * The <code>CURRENT_TIMESTAMP [(<i>precision</i>)]</code> function. + */ + public static final SqlFunction CURRENT_TIMESTAMP = + new SqlAbstractTimeFunction("CURRENT_TIMESTAMP", SqlTypeName.TIMESTAMP); + + /** + * The <code>CURRENT_DATE</code> function. + */ + public static final SqlFunction CURRENT_DATE = + new SqlCurrentDateFunction(); + + /** The <code>TIMESTAMPADD</code> function. */ + public static final SqlFunction TIMESTAMP_ADD = new SqlTimestampAddFunction(); + + /** The <code>TIMESTAMPDIFF</code> function. */ + public static final SqlFunction TIMESTAMP_DIFF = new SqlTimestampDiffFunction(); + + /** + * Use of the <code>IN_FENNEL</code> operator forces the argument to be + * evaluated in Fennel. Otherwise acts as identity function. + */ + public static final SqlFunction IN_FENNEL = + new SqlMonotonicUnaryFunction( + "IN_FENNEL", + SqlKind.OTHER_FUNCTION, + ReturnTypes.ARG0, + null, + OperandTypes.ANY, + SqlFunctionCategory.SYSTEM); + + /** + * The SQL <code>CAST</code> operator. + * + * <p>The SQL syntax is + * + * <blockquote><code>CAST(<i>expression</i> AS <i>type</i>)</code> + * </blockquote> + * + * <p>When the CAST operator is applies as a {@link SqlCall}, it has two + * arguments: the expression and the type. The type must not include a + * constraint, so <code>CAST(x AS INTEGER NOT NULL)</code>, for instance, is + * invalid.</p> + * + * <p>When the CAST operator is applied as a <code>RexCall</code>, the + * target type is simply stored as the return type, not an explicit operand. + * For example, the expression <code>CAST(1 + 2 AS DOUBLE)</code> will + * become a call to <code>CAST</code> with the expression <code>1 + 2</code> + * as its only operand.</p> + * + * <p>The <code>RexCall</code> form can also have a type which contains a + * <code>NOT NULL</code> constraint. When this expression is implemented, if + * the value is NULL, an exception will be thrown.</p> + */ + public static final SqlFunction CAST = new SqlCastFunction(); + + /** + * The SQL <code>EXTRACT</code> operator. Extracts a specified field value + * from a DATETIME or an INTERVAL. E.g.<br> + * <code>EXTRACT(HOUR FROM INTERVAL '364 23:59:59')</code> returns <code> + * 23</code> + */ + public static final SqlFunction EXTRACT = new SqlExtractFunction(); + + /** + * The SQL <code>YEAR</code> operator. Returns the Year + * from a DATETIME E.g.<br> + * <code>YEAR(date '2008-9-23')</code> returns <code> + * 2008</code> + */ + public static final SqlDatePartFunction YEAR = + new SqlDatePartFunction("YEAR", TimeUnit.YEAR); + + /** + * The SQL <code>QUARTER</code> operator. Returns the Quarter + * from a DATETIME E.g.<br> + * <code>QUARTER(date '2008-9-23')</code> returns <code> + * 3</code> + */ + public static final SqlDatePartFunction QUARTER = + new SqlDatePartFunction("QUARTER", TimeUnit.QUARTER); + + /** + * The SQL <code>MONTH</code> operator. Returns the Month + * from a DATETIME E.g.<br> + * <code>MONTH(date '2008-9-23')</code> returns <code> + * 9</code> + */ + public static final SqlDatePartFunction MONTH = + new SqlDatePartFunction("MONTH", TimeUnit.MONTH); + + /** + * The SQL <code>WEEK</code> operator. Returns the Week + * from a DATETIME E.g.<br> + * <code>WEEK(date '2008-9-23')</code> returns <code> + * 39</code> + */ + public static final SqlDatePartFunction WEEK = + new SqlDatePartFunction("WEEK", TimeUnit.WEEK); + + /** + * The SQL <code>DAYOFYEAR</code> operator. Returns the DOY + * from a DATETIME E.g.<br> + * <code>DAYOFYEAR(date '2008-9-23')</code> returns <code> + * 267</code> + */ + public static final SqlDatePartFunction DAYOFYEAR = + new SqlDatePartFunction("DAYOFYEAR", TimeUnit.DOY); + + /** + * The SQL <code>DAYOFMONTH</code> operator. Returns the Day + * from a DATETIME E.g.<br> + * <code>DAYOFMONTH(date '2008-9-23')</code> returns <code> + * 23</code> + */ + public static final SqlDatePartFunction DAYOFMONTH = + new SqlDatePartFunction("DAYOFMONTH", TimeUnit.DAY); + + /** + * The SQL <code>DAYOFWEEK</code> operator. Returns the DOW + * from a DATETIME E.g.<br> + * <code>DAYOFWEEK(date '2008-9-23')</code> returns <code> + * 2</code> + */ + public static final SqlDatePartFunction DAYOFWEEK = + new SqlDatePartFunction("DAYOFWEEK", TimeUnit.DOW); + + /** + * The SQL <code>HOUR</code> operator. Returns the Hour + * from a DATETIME E.g.<br> + * <code>HOUR(timestamp '2008-9-23 01:23:45')</code> returns <code> + * 1</code> + */ + public static final SqlDatePartFunction HOUR = + new SqlDatePartFunction("HOUR", TimeUnit.HOUR); + + /** + * The SQL <code>MINUTE</code> operator. Returns the Minute + * from a DATETIME E.g.<br> + * <code>MINUTE(timestamp '2008-9-23 01:23:45')</code> returns <code> + * 23</code> + */ + public static final SqlDatePartFunction MINUTE = + new SqlDatePartFunction("MINUTE", TimeUnit.MINUTE); + + /** + * The SQL <code>SECOND</code> operator. Returns the Second + * from a DATETIME E.g.<br> + * <code>SECOND(timestamp '2008-9-23 01:23:45')</code> returns <code> + * 45</code> + */ + public static final SqlDatePartFunction SECOND = + new SqlDatePartFunction("SECOND", TimeUnit.SECOND); + + /** + * The ELEMENT operator, used to convert a multiset with only one item to a + * "regular" type. Example ... log(ELEMENT(MULTISET[1])) ... + */ + public static final SqlFunction ELEMENT = + new SqlFunction( + "ELEMENT", + SqlKind.OTHER_FUNCTION, + ReturnTypes.MULTISET_ELEMENT_NULLABLE, + null, + OperandTypes.COLLECTION, + SqlFunctionCategory.SYSTEM); + + /** + * The item operator {@code [ ... ]}, used to access a given element of an + * array or map. For example, {@code myArray[3]} or {@code "myMap['foo']"}. + * + * <p>The SQL standard calls the ARRAY variant a + * <array element reference>. Index is 1-based. The standard says + * to raise "data exception - array element error" but we currently return + * null.</p> + * + * <p>MAP is not standard SQL.</p> + */ + public static final SqlOperator ITEM = new SqlItemOperator(); + + /** + * The ARRAY Value Constructor. e.g. "<code>ARRAY[1, 2, 3]</code>". + */ + public static final SqlArrayValueConstructor ARRAY_VALUE_CONSTRUCTOR = + new SqlArrayValueConstructor(); + + /** + * The MAP Value Constructor, + * e.g. "<code>MAP['washington', 1, 'obama', 44]</code>". + */ + public static final SqlMapValueConstructor MAP_VALUE_CONSTRUCTOR = + new SqlMapValueConstructor(); + + /** + * The internal "$SLICE" operator takes a multiset of records and returns a + * multiset of the first column of those records. + * + * <p>It is introduced when multisets of scalar types are created, in order + * to keep types consistent. For example, <code>MULTISET [5]</code> has type + * <code>INTEGER MULTISET</code> but is translated to an expression of type + * <code>RECORD(INTEGER EXPR$0) MULTISET</code> because in our internal + * representation of multisets, every element must be a record. Applying the + * "$SLICE" operator to this result converts the type back to an <code> + * INTEGER MULTISET</code> multiset value. + * + * <p><code>$SLICE</code> is often translated away when the multiset type is + * converted back to scalar values. + */ + public static final SqlInternalOperator SLICE = + new SqlInternalOperator( + "$SLICE", + SqlKind.OTHER, + 0, + false, + ReturnTypes.MULTISET_PROJECT0, + null, + OperandTypes.RECORD_COLLECTION) { + }; + + /** + * The internal "$ELEMENT_SLICE" operator returns the first field of the + * only element of a multiset. + * + * <p>It is introduced when multisets of scalar types are created, in order + * to keep types consistent. For example, <code>ELEMENT(MULTISET [5])</code> + * is translated to <code>$ELEMENT_SLICE(MULTISET (VALUES ROW (5 + * EXPR$0))</code> It is translated away when the multiset type is converted + * back to scalar values.</p> + * + * <p>NOTE: jhyde, 2006/1/9: Usages of this operator are commented out, but + * I'm not deleting the operator, because some multiset tests are disabled, + * and we may need this operator to get them working!</p> + */ + public static final SqlInternalOperator ELEMENT_SLICE = + new SqlInternalOperator( + "$ELEMENT_SLICE", + SqlKind.OTHER, + 0, + false, + ReturnTypes.MULTISET_RECORD, + null, + OperandTypes.MULTISET) { + public void unparse( + SqlWriter writer, + SqlCall call, + int leftPrec, + int rightPrec) { + SqlUtil.unparseFunctionSyntax( + this, + writer, call); + } + }; + + /** + * The internal "$SCALAR_QUERY" operator returns a scalar value from a + * record type. It assumes the record type only has one field, and returns + * that field as the output. + */ + public static final SqlInternalOperator SCALAR_QUERY = + new SqlInternalOperator( + "$SCALAR_QUERY", + SqlKind.SCALAR_QUERY, + 0, + false, + ReturnTypes.RECORD_TO_SCALAR, + null, + OperandTypes.RECORD_TO_SCALAR) { + public void unparse( + SqlWriter writer, + SqlCall call, + int leftPrec, + int rightPrec) { + final SqlWriter.Frame frame = writer.startList("(", ")"); + call.operand(0).unparse(writer, 0, 0); + writer.endList(frame); + } + + public boolean argumentMustBeScalar(int ordinal) { + // Obvious, really. + return false; + } + }; + + /** + * The CARDINALITY operator, used to retrieve the number of elements in a + * MULTISET, ARRAY or MAP. + */ + public static final SqlFunction CARDINALITY = + new SqlFunction( + "CARDINALITY", + SqlKind.OTHER_FUNCTION, + ReturnTypes.INTEGER_NULLABLE, + null, + OperandTypes.COLLECTION_OR_MAP, + SqlFunctionCategory.SYSTEM); + + /** + * The COLLECT operator. Multiset aggregator function. + */ + public static final SqlAggFunction COLLECT = + new SqlAggFunction("COLLECT", + null, + SqlKind.COLLECT, + ReturnTypes.TO_MULTISET, + null, + OperandTypes.ANY, + SqlFunctionCategory.SYSTEM, false, false) { + }; + + /** + * The FUSION operator. Multiset aggregator function. + */ + public static final SqlFunction FUSION = + new SqlAggFunction("FUSION", null, + SqlKind.FUSION, + ReturnTypes.ARG0, + null, + OperandTypes.MULTISET, + SqlFunctionCategory.SYSTEM, false, false) { + }; + + /** + * The sequence next value function: <code>NEXT VALUE FOR sequence</code> + */ + public static final SqlOperator NEXT_VALUE = + new SqlSequenceValueOperator(SqlKind.NEXT_VALUE); + + /** + * The sequence current value function: <code>CURRENT VALUE FOR + * sequence</code> + */ + public static final SqlOperator CURRENT_VALUE = + new SqlSequenceValueOperator(SqlKind.CURRENT_VALUE); + + /** + * The <code>TABLESAMPLE</code> operator. + * + * <p>Examples: + * + * <ul> + * <li><code><query> TABLESAMPLE SUBSTITUTE('sampleName')</code> + * (non-standard) + * <li><code><query> TABLESAMPLE BERNOULLI(<percent>) + * [REPEATABLE(<seed>)]</code> (standard, but not implemented for FTRS + * yet) + * <li><code><query> TABLESAMPLE SYSTEM(<percent>) + * [REPEATABLE(<seed>)]</code> (standard, but not implemented for FTRS + * yet) + * </ul> + * + * <p>Operand #0 is a query or table; Operand #1 is a {@link SqlSampleSpec} + * wrapped in a {@link SqlLiteral}. + */ + public static final SqlSpecialOperator TABLESAMPLE = + new SqlSpecialOperator( + "TABLESAMPLE", + SqlKind.TABLESAMPLE, + 20, + true, + ReturnTypes.ARG0, + null, + OperandTypes.VARIADIC) { + public void unparse( + SqlWriter writer, + SqlCall call, + int leftPrec, + int rightPrec) { + call.operand(0).unparse(writer, leftPrec, 0); + writer.keyword("TABLESAMPLE"); + call.operand(1).unparse(writer, 0, rightPrec); + } + }; + + /** The {@code TUMBLE} group function. */ + public static final SqlGroupFunction TUMBLE = + new SqlGroupFunction(SqlKind.TUMBLE, null, + OperandTypes.or(OperandTypes.DATETIME_INTERVAL, + OperandTypes.DATETIME_INTERVAL_TIME)) { + @Override List<SqlGroupFunction> getAuxiliaryFunctions() { + return ImmutableList.of(TUMBLE_START, TUMBLE_END); + } + }; + + /** The {@code TUMBLE_START} auxiliary function of + * the {@code TUMBLE} group function. */ + public static final SqlGroupFunction TUMBLE_START = + TUMBLE.auxiliary(SqlKind.TUMBLE_START); + + /** The {@code TUMBLE_END} auxiliary function of + * the {@code TUMBLE} group function. */ + public static final SqlGroupFunction TUMBLE_END = + TUMBLE.auxiliary(SqlKind.TUMBLE_END); + + /** The {@code HOP} group function. */ + public static final SqlGroupFunction HOP = + new SqlGroupFunction(SqlKind.HOP, null, + OperandTypes.or(OperandTypes.DATETIME_INTERVAL_INTERVAL, + OperandTypes.DATETIME_INTERVAL_INTERVAL_TIME)) { + @Override List<SqlGroupFunction> getAuxiliaryFunctions() { + return ImmutableList.of(HOP_START, HOP_END); + } + }; + + /** The {@code HOP_START} auxiliary function of + * the {@code HOP} group function. */ + public static final SqlGroupFunction HOP_START = + HOP.auxiliary(SqlKind.HOP_START); + + /** The {@code HOP_END} auxiliary function of + * the {@code HOP} group function. */ + public static final SqlGroupFunction HOP_END = + HOP.auxiliary(SqlKind.HOP_END); + + /** The {@code SESSION} group function. */ + public static final SqlGroupFunction SESSION = + new SqlGroupFunction(SqlKind.SESSION, null, + OperandTypes.or(OperandTypes.DATETIME_INTERVAL, + OperandTypes.DATETIME_INTERVAL_TIME)) { + @Override List<SqlGroupFunction> getAuxiliaryFunctions() { + return ImmutableList.of(SESSION_START, SESSION_END); + } + }; + + /** The {@code SESSION_START} auxiliary function of + * the {@code SESSION} group function. */ + public static final SqlGroupFunction SESSION_START = + SESSION.auxiliary(SqlKind.SESSION_START); + + /** The {@code SESSION_END} auxiliary function of + * the {@code SESSION} group function. */ + public static final SqlGroupFunction SESSION_END = + SESSION.auxiliary(SqlKind.SESSION_END); + + /** {@code |} operator to create alternate patterns + * within {@code MATCH_RECOGNIZE}. + * + * <p>If {@code p1} and {@code p2} are patterns then {@code p1 | p2} is a + * pattern that matches {@code p1} or {@code p2}. */ + public static final SqlBinaryOperator PATTERN_ALTER = + new SqlBinaryOperator("|", SqlKind.PATTERN_ALTER, 70, true, null, null, null); + + /** Operator to concatenate patterns within {@code MATCH_RECOGNIZE}. + * + * <p>If {@code p1} and {@code p2} are patterns then {@code p1 p2} is a + * pattern that matches {@code p1} followed by {@code p2}. */ + public static final SqlBinaryOperator PATTERN_CONCAT = + new SqlBinaryOperator("", SqlKind.PATTERN_CONCAT, 80, true, null, null, null); + + /** Operator to quantify patterns within {@code MATCH_RECOGNIZE}. + * + * <p>If {@code p} is a pattern then {@code p{3, 5}} is a + * pattern that matches between 3 and 5 occurrences of {@code p}. */ + public static final SqlSpecialOperator PATTERN_QUANTIFIER = + new SqlSpecialOperator("PATTERN_QUANTIFIER", SqlKind.PATTERN_QUANTIFIER, + 90) { + @Override public void unparse(SqlWriter writer, SqlCall call, + int leftPrec, int rightPrec) { + call.operand(0).unparse(writer, this.getLeftPrec(), this.getRightPrec()); + int startNum = ((SqlNumericLiteral) call.operand(1)).intValue(true); + SqlNumericLiteral endRepNum = call.operand(2); + boolean isReluctant = ((SqlLiteral) call.operand(3)).booleanValue(); + int endNum = endRepNum.intValue(true); + if (startNum == endNum) { + writer.keyword("{ " + startNum + " }"); + } else { + if (endNum == -1) { + if (startNum == 0) { + writer.keyword("*"); + } else if (startNum == 1) { + writer.keyword("+"); + } else { + writer.keyword("{ " + startNum + ", }"); + } + } else { + if (startNum == 0 && endNum == 1) { + writer.keyword("?"); + } else if (startNum == -1) { + writer.keyword("{ , " + endNum + " }"); + } else { + writer.keyword("{ " + startNum + ", " + endNum + " }"); + } + } + if (isReluctant) { + writer.keyword("?"); + } + } + } + }; + + /** {@code PERMUTE} operator to combine patterns within + * {@code MATCH_RECOGNIZE}. + * + * <p>If {@code p1} and {@code p2} are patterns then {@code PERMUTE (p1, p2)} + * is a pattern that matches all permutations of {@code p1} and + * {@code p2}. */ + public static final SqlSpecialOperator PATTERN_PERMUTE = + new SqlSpecialOperator("PATTERN_PERMUTE", SqlKind.PATTERN_PERMUTE, 100) { + @Override public void unparse(SqlWriter writer, SqlCall call, + int leftPrec, int rightPrec) { + writer.keyword("PERMUTE"); + SqlWriter.Frame frame = writer.startList("(", ")"); + for (int i = 0; i < call.getOperandList().size(); i++) { + SqlNode pattern = call.getOperandList().get(i); + pattern.unparse(writer, 0, 0); + if (i != call.getOperandList().size() - 1) { + writer.print(","); + } + } + writer.endList(frame); + } + }; + + /** {@code EXCLUDE} operator within {@code MATCH_RECOGNIZE}. + * + * <p>If {@code p} is a pattern then {@code {- p -} }} is a + * pattern that excludes {@code p} from the output. */ + public static final SqlSpecialOperator PATTERN_EXCLUDE = + new SqlSpecialOperator("PATTERN_EXCLUDE", SqlKind.PATTERN_EXCLUDED, + 100) { + @Override public void unparse(SqlWriter writer, SqlCall call, + int leftPrec, int rightPrec) { + SqlWriter.Frame frame = writer.startList("{-", "-}"); + SqlNode node = call.getOperandList().get(0); + node.unparse(writer, 0, 0); + writer.endList(frame); + } + }; + + //~ Methods ---------------------------------------------------------------- + + /** + * Returns the standard operator table, creating it if necessary. + */ + public static synchronized SqlStdOperatorTable instance() { + if (instance == null) { + // Creates and initializes the standard operator table. + // Uses two-phase construction, because we can't initialize the + // table until the constructor of the sub-class has completed. + instance = new SqlStdOperatorTable(); + instance.init(); + } + return instance; + } + + /** Returns the group function for which a given kind is an auxiliary + * function, or null if it is not an auxiliary function. */ + public static SqlGroupFunction auxiliaryToGroup(SqlKind kind) { + switch (kind) { + case TUMBLE_START: + case TUMBLE_END: + return TUMBLE; + case HOP_START: + case HOP_END: + return HOP; + case SESSION_START: + case SESSION_END: + return SESSION; + default: + return null; + } + } + + /** Converts a call to a grouped auxiliary function + * to a call to the grouped window function. For other calls returns null. + * + * <p>For example, converts {@code TUMBLE_START(rowtime, INTERVAL '1' HOUR))} + * to {@code TUMBLE(rowtime, INTERVAL '1' HOUR))}. */ + public static SqlCall convertAuxiliaryToGroupCall(SqlCall call) { + final SqlOperator op = call.getOperator(); + if (op instanceof SqlGroupFunction + && op.isGroupAuxiliary()) { + return copy(call, ((SqlGroupFunction) op).groupFunction); + } + return null; + } + + /** Converts a call to a grouped window function to a call to its auxiliary + * window function(s). For other calls returns null. + * + * <p>For example, converts {@code TUMBLE_START(rowtime, INTERVAL '1' HOUR))} + * to {@code TUMBLE(rowtime, INTERVAL '1' HOUR))}. */ + public static List<Pair<SqlNode, AuxiliaryConverter>> + convertGroupToAuxiliaryCalls(SqlCall call) { + final SqlOperator op = call.getOperator(); + if (op instanceof SqlGroupFunction + && op.isGroup()) { + ImmutableList.Builder<Pair<SqlNode, AuxiliaryConverter>> builder = + ImmutableList.builder(); + for (final SqlGroupFunction f + : ((SqlGroupFunction) op).getAuxiliaryFunctions()) { + builder.add( + Pair.<SqlNode, AuxiliaryConverter>of(copy(call, f), + new AuxiliaryConverter.Impl(f))); + } + return builder.build(); + } + return ImmutableList.of(); + } + + /** Creates a copy of a call with a new operator. */ + private static SqlCall copy(SqlCall call, SqlOperator operator) { + final List<SqlNode> list = call.getOperandList(); + return new SqlBasicCall(operator, list.toArray(new SqlNode[list.size()]), + call.getParserPosition()); + } + +} + +// End SqlStdOperatorTable.java http://git-wip-us.apache.org/repos/asf/flink/blob/2d33c0be/flink-libraries/flink-table/src/main/java/org/apache/calcite/sql/validate/AggChecker.java ---------------------------------------------------------------------- diff --git a/flink-libraries/flink-table/src/main/java/org/apache/calcite/sql/validate/AggChecker.java b/flink-libraries/flink-table/src/main/java/org/apache/calcite/sql/validate/AggChecker.java new file mode 100644 index 0000000..56a364f --- /dev/null +++ b/flink-libraries/flink-table/src/main/java/org/apache/calcite/sql/validate/AggChecker.java @@ -0,0 +1,225 @@ +/* + * 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.sql.validate; + +/* + * THIS FILE HAS BEEN COPIED FROM THE APACHE CALCITE PROJECT UNTIL CALCITE-1761 IS FIXED. + */ + +import org.apache.calcite.sql.SqlCall; +import org.apache.calcite.sql.SqlIdentifier; +import org.apache.calcite.sql.SqlKind; +import org.apache.calcite.sql.SqlNode; +import org.apache.calcite.sql.SqlNodeList; +import org.apache.calcite.sql.SqlSelect; +import org.apache.calcite.sql.SqlUtil; +import org.apache.calcite.sql.SqlWindow; +import org.apache.calcite.sql.fun.SqlStdOperatorTable; +import org.apache.calcite.sql.util.SqlBasicVisitor; +import org.apache.calcite.util.Litmus; + +import java.util.ArrayDeque; +import java.util.Deque; +import java.util.List; + +import static org.apache.calcite.util.Static.RESOURCE; + +/** + * Visitor which throws an exception if any component of the expression is not a + * group expression. + */ +class AggChecker extends SqlBasicVisitor<Void> { + //~ Instance fields -------------------------------------------------------- + + private final Deque<SqlValidatorScope> scopes = new ArrayDeque<>(); + private final List<SqlNode> extraExprs; + private final List<SqlNode> groupExprs; + private boolean distinct; + private SqlValidatorImpl validator; + + //~ Constructors ----------------------------------------------------------- + + /** + * Creates an AggChecker. + * + * @param validator Validator + * @param scope Scope + * @param groupExprs Expressions in GROUP BY (or SELECT DISTINCT) clause, + * that are therefore available + * @param distinct Whether aggregation checking is because of a SELECT + * DISTINCT clause + */ + AggChecker( + SqlValidatorImpl validator, + AggregatingScope scope, + List<SqlNode> extraExprs, + List<SqlNode> groupExprs, + boolean distinct) { + this.validator = validator; + this.extraExprs = extraExprs; + this.groupExprs = groupExprs; + this.distinct = distinct; + this.scopes.push(scope); + } + + //~ Methods ---------------------------------------------------------------- + + boolean isGroupExpr(SqlNode expr) { + for (SqlNode groupExpr : groupExprs) { + if (groupExpr.equalsDeep(expr, Litmus.IGNORE)) { + return true; + } + } + + for (SqlNode extraExpr : extraExprs) { + if (extraExpr.equalsDeep(expr, Litmus.IGNORE)) { + return true; + } + } + return false; + } + + public Void visit(SqlIdentifier id) { + if (isGroupExpr(id) || id.isStar()) { + // Star may validly occur in "SELECT COUNT(*) OVER w" + return null; + } + + // Is it a call to a parentheses-free function? + SqlCall call = + SqlUtil.makeCall( + validator.getOperatorTable(), + id); + if (call != null) { + return call.accept(this); + } + + // Didn't find the identifier in the group-by list as is, now find + // it fully-qualified. + // TODO: It would be better if we always compared fully-qualified + // to fully-qualified. + final SqlQualified fqId = scopes.peek().fullyQualify(id); + if (isGroupExpr(fqId.identifier)) { + return null; + } + SqlNode originalExpr = validator.getOriginal(id); + final String exprString = originalExpr.toString(); + throw validator.newValidationError(originalExpr, + distinct + ? RESOURCE.notSelectDistinctExpr(exprString) + : RESOURCE.notGroupExpr(exprString)); + } + + public Void visit(SqlCall call) { + final SqlValidatorScope scope = scopes.peek(); + if (call.getOperator().isAggregator()) { + if (distinct) { + if (scope instanceof AggregatingSelectScope) { + SqlNodeList selectList = + ((SqlSelect) scope.getNode()).getSelectList(); + + // Check if this aggregation function is just an element in the select + for (SqlNode sqlNode : selectList) { + if (sqlNode.getKind() == SqlKind.AS) { + sqlNode = ((SqlCall) sqlNode).operand(0); + } + + if (validator.expand(sqlNode, scope) + .equalsDeep(call, Litmus.IGNORE)) { + return null; + } + } + } + + // Cannot use agg fun in ORDER BY clause if have SELECT DISTINCT. + SqlNode originalExpr = validator.getOriginal(call); + final String exprString = originalExpr.toString(); + throw validator.newValidationError(call, + RESOURCE.notSelectDistinctExpr(exprString)); + } + + // For example, 'sum(sal)' in 'SELECT sum(sal) FROM emp GROUP + // BY deptno' + return null; + } + if (call.getKind() == SqlKind.FILTER) { + call.operand(0).accept(this); + return null; + } + // Visit the operand in window function + if (call.getKind() == SqlKind.OVER) { + for (SqlNode operand : call.<SqlCall>operand(0).getOperandList()) { + operand.accept(this); + } + // Check the OVER clause + final SqlNode over = call.operand(1); + if (over instanceof SqlCall) { + over.accept(this); + } else if (over instanceof SqlIdentifier) { + // Check the corresponding SqlWindow in WINDOW clause + final SqlWindow window = + scope.lookupWindow(((SqlIdentifier) over).getSimple()); + window.getPartitionList().accept(this); + window.getOrderList().accept(this); + } + } + if (isGroupExpr(call)) { + // This call matches an expression in the GROUP BY clause. + return null; + } + + final SqlCall groupCall = + SqlStdOperatorTable.convertAuxiliaryToGroupCall(call); + if (groupCall != null) { + if (isGroupExpr(groupCall)) { + // This call is an auxiliary function that matches a group call in the + // GROUP BY clause. + // + // For example TUMBLE_START is an auxiliary of the TUMBLE + // group function, and + // TUMBLE_START(rowtime, INTERVAL '1' HOUR) + // matches + // TUMBLE(rowtime, INTERVAL '1' HOUR') + return null; + } + throw validator.newValidationError(groupCall, + RESOURCE.auxiliaryWithoutMatchingGroupCall( + call.getOperator().getName(), groupCall.getOperator().getName())); + } + + if (call.isA(SqlKind.QUERY)) { + // Allow queries for now, even though they may contain + // references to forbidden columns. + return null; + } + + // Switch to new scope. + SqlValidatorScope newScope = scope.getOperandScope(call); + scopes.push(newScope); + + // Visit the operands (only expressions). + call.getOperator() + .acceptCall(this, call, true, ArgHandlerImpl.<Void>instance()); + + // Restore scope. + scopes.pop(); + return null; + } + +} + +// End AggChecker.java http://git-wip-us.apache.org/repos/asf/flink/blob/2d33c0be/flink-libraries/flink-table/src/main/java/org/apache/calcite/sql2rel/AuxiliaryConverter.java ---------------------------------------------------------------------- diff --git a/flink-libraries/flink-table/src/main/java/org/apache/calcite/sql2rel/AuxiliaryConverter.java b/flink-libraries/flink-table/src/main/java/org/apache/calcite/sql2rel/AuxiliaryConverter.java new file mode 100644 index 0000000..98e1199 --- /dev/null +++ b/flink-libraries/flink-table/src/main/java/org/apache/calcite/sql2rel/AuxiliaryConverter.java @@ -0,0 +1,79 @@ +/* + * 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.sql2rel; + +/* + * THIS FILE HAS BEEN COPIED FROM THE APACHE CALCITE PROJECT UNTIL CALCITE-1761 IS FIXED. + */ + +import org.apache.calcite.rex.RexBuilder; +import org.apache.calcite.rex.RexNode; +import org.apache.calcite.sql.SqlFunction; +import org.apache.calcite.sql.fun.SqlStdOperatorTable; + +/** Converts an expression for a group window function (e.g. TUMBLE) + * into an expression for an auxiliary group function (e.g. TUMBLE_START). + * + * @see SqlStdOperatorTable#TUMBLE + */ +public interface AuxiliaryConverter { + /** Converts an expression. + * + * @param rexBuilder Rex builder + * @param groupCall Call to the group function, e.g. "TUMBLE($2, 36000)" + * @param e Expression holding result of the group function, e.g. "$0" + * + * @return Expression for auxiliary function, e.g. "$0 + 36000" converts + * the result of TUMBLE to the result of TUMBLE_END + */ + RexNode convert(RexBuilder rexBuilder, RexNode groupCall, RexNode e); + + /** Simple implementation of {@link AuxiliaryConverter}. */ + class Impl implements AuxiliaryConverter { + private final SqlFunction f; + + public Impl(SqlFunction f) { + this.f = f; + } + + public RexNode convert(RexBuilder rexBuilder, RexNode groupCall, + RexNode e) { + return rexBuilder.makeCall(this.f, e); + // FLINK QUICK FIX + // we do not use this logic right now +// switch (f.getKind()) { +// case TUMBLE_START: +// case HOP_START: +// case SESSION_START: +// case SESSION_END: // TODO: ? +// return e; +// case TUMBLE_END: +// return rexBuilder.makeCall( +// SqlStdOperatorTable.PLUS, e, +// ((RexCall) groupCall).operands.get(1)); +// case HOP_END: +// return rexBuilder.makeCall( +// SqlStdOperatorTable.PLUS, e, +// ((RexCall) groupCall).operands.get(2)); +// default: +// throw new AssertionError("unknown: " + f); +// } + } + } +} + +// End AuxiliaryConverter.java