SqlInternalOperators.java
/*
* 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;
import org.apache.calcite.rex.RexCall;
import org.apache.calcite.sql.SqlAggFunction;
import org.apache.calcite.sql.SqlCall;
import org.apache.calcite.sql.SqlInternalOperator;
import org.apache.calcite.sql.SqlKind;
import org.apache.calcite.sql.SqlMeasureOperator;
import org.apache.calcite.sql.SqlNode;
import org.apache.calcite.sql.SqlNodeList;
import org.apache.calcite.sql.SqlOperator;
import org.apache.calcite.sql.SqlOperatorTable;
import org.apache.calcite.sql.SqlSyntax;
import org.apache.calcite.sql.SqlWriter;
import org.apache.calcite.sql.type.InferTypes;
import org.apache.calcite.sql.type.OperandTypes;
import org.apache.calcite.sql.type.ReturnTypes;
import org.apache.calcite.sql.type.SqlTypeTransforms;
import org.apache.calcite.util.Litmus;
import org.checkerframework.checker.nullness.qual.Nullable;
import java.util.List;
/**
* Contains internal operators.
*
* <p>These operators are always created directly, not by looking up a function
* or operator by name or syntax, and therefore this class does not implement
* interface {@link SqlOperatorTable}.
*/
public abstract class SqlInternalOperators {
private SqlInternalOperators() {
}
/** Similar to {@link SqlStdOperatorTable#ROW}, but does not print "ROW".
*
* <p>For arguments [1, TRUE], ROW would print "{@code ROW (1, TRUE)}",
* but this operator prints "{@code (1, TRUE)}". */
public static final SqlRowOperator ANONYMOUS_ROW =
new SqlRowOperator("$ANONYMOUS_ROW") {
@Override public void unparse(SqlWriter writer, SqlCall call,
int leftPrec, int rightPrec) {
@SuppressWarnings("assignment.type.incompatible")
List<@Nullable SqlNode> operandList = call.getOperandList();
writer.list(SqlWriter.FrameTypeEnum.PARENTHESES, SqlWriter.COMMA,
SqlNodeList.of(call.getParserPosition(), operandList));
}
};
/** Similar to {@link #ANONYMOUS_ROW}, but does not print "ROW" or
* parentheses.
*
* <p>For arguments [1, TRUE], prints "{@code 1, TRUE}". It is used in
* contexts where parentheses have been printed (because we thought we were
* about to print "{@code (ROW (1, TRUE))}") and we wish we had not. */
public static final SqlRowOperator ANONYMOUS_ROW_NO_PARENTHESES =
new SqlRowOperator("$ANONYMOUS_ROW_NO_PARENTHESES") {
@Override public void unparse(SqlWriter writer, SqlCall call,
int leftPrec, int rightPrec) {
final SqlWriter.Frame frame =
writer.startList(SqlWriter.FrameTypeEnum.FUN_CALL);
for (SqlNode operand : call.getOperandList()) {
writer.sep(",");
operand.unparse(writer, leftPrec, rightPrec);
}
writer.endList(frame);
}
};
/** "$THROW_UNLESS(condition, message)" throws an error with the given message
* if condition is not TRUE, otherwise returns TRUE. */
public static final SqlInternalOperator THROW_UNLESS =
new SqlInternalOperator("$THROW_UNLESS", SqlKind.OTHER);
/** <code>MEASURE</code> operator wraps an expression in the SELECT clause
* that is a measure. It always occurs inside a call to "AS". */
public static final SqlMeasureOperator MEASURE =
new SqlMeasureOperator();
/** {@code V2M} operator converts a measure to a value. */
public static final SqlOperator M2V =
new SqlInternalOperator("M2V", SqlKind.M2V, 2, true,
ReturnTypes.ARG0.andThen(SqlTypeTransforms.FROM_MEASURE), null,
OperandTypes.ANY);
/** {@code V2M} operator converts a value to a measure. */
public static final SqlOperator V2M =
new SqlInternalOperator("V2M", SqlKind.V2M, 2, true,
ReturnTypes.ARG0.andThen(SqlTypeTransforms.TO_MEASURE), null,
OperandTypes.ANY);
/** {@code M2X} operator evaluates an expression in a context. As for
* {@link #V2M}, the expression may involve aggregate functions, so that it
* can be evaluated in any aggregation context. */
public static final SqlOperator M2X =
new SqlInternalOperator("M2X", SqlKind.M2X, 2, true,
ReturnTypes.ARG0.andThen(SqlTypeTransforms.FROM_MEASURE), null,
OperandTypes.MEASURE_BOOLEAN);
/** {@code AGG_M2M} aggregate function takes a measure as its argument and
* returns a measure. It is used to propagate measures through the
* {@code Aggregate} relational operator.
*
* @see SqlLibraryOperators#AGGREGATE */
public static final SqlAggFunction AGG_M2M =
SqlBasicAggFunction.create(SqlKind.AGG_M2M, ReturnTypes.ARG0,
OperandTypes.ANY);
/** {@code AGG_M2V} aggregate function takes a measure as its argument and
* returns value. */
public static final SqlAggFunction AGG_M2V =
SqlBasicAggFunction.create(SqlKind.AGG_M2V,
ReturnTypes.ARG0.andThen(SqlTypeTransforms.FROM_MEASURE),
OperandTypes.ANY);
/** {@code SAME_PARTITION} operator takes a number of expressions and returns
* whether the values of those expressions in current row are all the same as
* the values of those expressions in the 'anchor' row of a call to
* {@link #M2X}.
*
* <p>Its role in {@code M2X} is the same as the {@code PARTITION BY} clause
* of a windowed aggregate. For example,
*
* <pre>{@code
* SUM(sal) OVER (PARTITION BY deptno, job)
* }</pre>
*
* <p>is equivalent to
*
* <pre>{@code
* M2X(SUM(sal), SAME_PARTITION(deptno, job))
* }</pre>
*
* <p>You may think of it as expanding to a {@code BOOLEAN} expression in
* terms of the {@code ANCHOR} record; for example,
*
* <pre>{@code
* SAME_PARTITION(deptno, job)
* }</pre>
*
* <p>expands to
*
* <pre>{@code
* deptno IS NOT DISTINCT FROM anchor.deptno
* AND job IS NOT DISTINCT FROM anchor.job
* AND GROUPING(deptno, job) = GROUPING(anchor.deptno, anchor.job)
* }</pre>
*
* <p>But we prefer to leave it intact for easier matching and eventual
* elimination in transformation rules. */
public static final SqlOperator SAME_PARTITION =
new SqlInternalOperator("SAME_PARTITION", SqlKind.SAME_PARTITION, 2, true,
ReturnTypes.BOOLEAN, InferTypes.ANY_NULLABLE, OperandTypes.VARIADIC);
/** An IN operator for Druid.
*
* <p>Unlike the regular
* {@link SqlStdOperatorTable#IN} operator it may
* be used in {@link RexCall}. It does not require that
* its operands have consistent types. */
public static final SqlInOperator DRUID_IN =
new SqlInOperator(SqlKind.DRUID_IN);
/** A NOT IN operator for Druid, analogous to {@link #DRUID_IN}. */
public static final SqlInOperator DRUID_NOT_IN =
new SqlInOperator(SqlKind.DRUID_NOT_IN);
/** A BETWEEN operator for Druid, analogous to {@link #DRUID_IN}. */
public static final SqlBetweenOperator DRUID_BETWEEN =
new SqlBetweenOperator(SqlBetweenOperator.Flag.SYMMETRIC, false) {
@Override public SqlKind getKind() {
return SqlKind.DRUID_BETWEEN;
}
@Override public boolean validRexOperands(int count, Litmus litmus) {
return litmus.succeed();
}
};
/** Separator expression inside GROUP_CONCAT, e.g. '{@code SEPARATOR ','}'. */
public static final SqlOperator SEPARATOR =
new SqlInternalOperator("SEPARATOR", SqlKind.SEPARATOR, 20, false,
ReturnTypes.ARG0, InferTypes.RETURN_TYPE, OperandTypes.ANY);
/** {@code DISTINCT} operator, occurs within {@code GROUP BY} clause. */
public static final SqlInternalOperator GROUP_BY_DISTINCT =
new SqlRollupOperator("GROUP BY DISTINCT", SqlKind.GROUP_BY_DISTINCT);
/** Fetch operator is ONLY used for its precedence during unparsing. */
public static final SqlOperator FETCH =
SqlBasicOperator.create("FETCH")
.withPrecedence(SqlStdOperatorTable.UNION.getLeftPrec() - 2, true);
/** 2-argument form of the special minus-date operator
* to be used with BigQuery subtraction functions. It differs from
* the standard MINUS_DATE operator in that it has 2 arguments,
* and subtracts an interval from a datetime. */
public static final SqlDatetimeSubtractionOperator MINUS_DATE2 =
new SqlDatetimeSubtractionOperator("MINUS_DATE2", ReturnTypes.ARG0_NULLABLE);
/** Offset operator is ONLY used for its precedence during unparsing. */
public static final SqlOperator OFFSET =
SqlBasicOperator.create("OFFSET")
.withPrecedence(SqlStdOperatorTable.UNION.getLeftPrec() - 2, true);
/** Aggregate function that always returns a given literal. */
public static final SqlAggFunction LITERAL_AGG =
SqlLiteralAggFunction.INSTANCE;
/** CAST NOT NULL operator used for cast expression to make it non-nullable in SqlNode.
*
* <p>For example:
* <pre>{@code COALESCE(a,b)
* }</pre>
*
* <p>is converted to
*
* <pre>{@code CASE WHEN a is not null THEN a ELSE b
* }</pre>
* by {@code SqlCoalesceFunction#rewriteCall}.
*
* <p>When a is nullable and b is non-nullable, the {@code COALESCE(a,b)} data type will be
* non-nullable and {@code CASE WHEN a is not null THEN a ELSE b} data type will be nullable.
* The validator will throw an exception about failing to preserve the data type.
*
* <p>Add CAST NOT NULL operator to {@code CASE WHEN a is not null THEN a ELSE b},
* making it becomes
* <pre>{@code CASE WHEN a is not null THEN CAST NOT NULL(a) ELSE b}
* </pre>
*
* <p>Then the validator knows this data type is non-nullable.
* So we can keep the types consistent before and after conversion.
* */
public static final SqlOperator CAST_NOT_NULL =
new SqlInternalOperator("CAST NOT NULL", SqlKind.CAST_NOT_NULL, 2, true,
ReturnTypes.ARG0.andThen(SqlTypeTransforms.TO_NOT_NULLABLE), null,
OperandTypes.ANY) {
// This is an internal operator, which should not be unparsed to sql.
@Override public void unparse(SqlWriter writer, SqlCall call, int leftPrec, int rightPrec) {
call.operand(0).unparse(writer, leftPrec, rightPrec);
}
};
/** Subject to change. */
private static class SqlBasicOperator extends SqlOperator {
@Override public SqlSyntax getSyntax() {
return SqlSyntax.SPECIAL;
}
/** Private constructor. Use {@link #create}. */
private SqlBasicOperator(String name, int leftPrecedence, int rightPrecedence) {
super(name, SqlKind.OTHER, leftPrecedence, rightPrecedence,
ReturnTypes.BOOLEAN, InferTypes.RETURN_TYPE, OperandTypes.ANY);
}
static SqlBasicOperator create(String name) {
return new SqlBasicOperator(name, 0, 0);
}
SqlBasicOperator withPrecedence(int prec, boolean leftAssoc) {
return new SqlBasicOperator(getName(), leftPrec(prec, leftAssoc),
rightPrec(prec, leftAssoc));
}
}
}