blob: fdac4d9091858368ce22e1b00a78592fcb642e0f [file] [log] [blame]
/*
* 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;
import org.apache.calcite.avatica.util.DateTimeUtils;
import org.apache.calcite.avatica.util.TimeUnit;
import org.apache.calcite.avatica.util.TimeUnitRange;
import org.apache.calcite.plan.RelOptUtil;
import org.apache.calcite.rel.RelCollation;
import org.apache.calcite.rel.type.RelDataType;
import org.apache.calcite.rel.type.RelDataTypeFactory;
import org.apache.calcite.rel.type.RelDataTypeFamily;
import org.apache.calcite.rex.RexBuilder;
import org.apache.calcite.rex.RexCall;
import org.apache.calcite.rex.RexCallBinding;
import org.apache.calcite.rex.RexLiteral;
import org.apache.calcite.rex.RexNode;
import org.apache.calcite.rex.RexRangeRef;
import org.apache.calcite.rex.RexUtil;
import org.apache.calcite.sql.SqlAggFunction;
import org.apache.calcite.sql.SqlBasicCall;
import org.apache.calcite.sql.SqlBinaryOperator;
import org.apache.calcite.sql.SqlCall;
import org.apache.calcite.sql.SqlDataTypeSpec;
import org.apache.calcite.sql.SqlFunction;
import org.apache.calcite.sql.SqlFunctionCategory;
import org.apache.calcite.sql.SqlIdentifier;
import org.apache.calcite.sql.SqlIntervalLiteral;
import org.apache.calcite.sql.SqlIntervalQualifier;
import org.apache.calcite.sql.SqlJdbcFunctionCall;
import org.apache.calcite.sql.SqlKind;
import org.apache.calcite.sql.SqlLiteral;
import org.apache.calcite.sql.SqlNode;
import org.apache.calcite.sql.SqlNodeList;
import org.apache.calcite.sql.SqlNumericLiteral;
import org.apache.calcite.sql.SqlOperator;
import org.apache.calcite.sql.SqlUtil;
import org.apache.calcite.sql.fun.OracleSqlOperatorTable;
import org.apache.calcite.sql.fun.SqlArrayValueConstructor;
import org.apache.calcite.sql.fun.SqlBetweenOperator;
import org.apache.calcite.sql.fun.SqlCase;
import org.apache.calcite.sql.fun.SqlDatetimeSubtractionOperator;
import org.apache.calcite.sql.fun.SqlExtractFunction;
import org.apache.calcite.sql.fun.SqlLiteralChainOperator;
import org.apache.calcite.sql.fun.SqlMapValueConstructor;
import org.apache.calcite.sql.fun.SqlMultisetQueryConstructor;
import org.apache.calcite.sql.fun.SqlMultisetValueConstructor;
import org.apache.calcite.sql.fun.SqlOverlapsOperator;
import org.apache.calcite.sql.fun.SqlQuarterFunction;
import org.apache.calcite.sql.fun.SqlRowOperator;
import org.apache.calcite.sql.fun.SqlSequenceValueOperator;
import org.apache.calcite.sql.fun.SqlStdOperatorTable;
import org.apache.calcite.sql.fun.SqlTrimFunction;
import org.apache.calcite.sql.parser.SqlParserPos;
import org.apache.calcite.sql.type.SqlOperandTypeChecker;
import org.apache.calcite.sql.type.SqlTypeFamily;
import org.apache.calcite.sql.type.SqlTypeName;
import org.apache.calcite.sql.type.SqlTypeUtil;
import org.apache.calcite.util.Util;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
/**
* Standard implementation of {@link SqlRexConvertletTable}.
*/
public class StandardConvertletTable extends ReflectiveConvertletTable {
/** Singleton instance. */
public static final StandardConvertletTable INSTANCE =
new StandardConvertletTable();
//~ Constructors -----------------------------------------------------------
private StandardConvertletTable() {
super();
// Register aliases (operators which have a different name but
// identical behavior to other operators).
addAlias(
SqlStdOperatorTable.CHARACTER_LENGTH,
SqlStdOperatorTable.CHAR_LENGTH);
addAlias(
SqlStdOperatorTable.IS_UNKNOWN,
SqlStdOperatorTable.IS_NULL);
addAlias(
SqlStdOperatorTable.IS_NOT_UNKNOWN,
SqlStdOperatorTable.IS_NOT_NULL);
// Register convertlets for specific objects.
registerOp(
SqlStdOperatorTable.CAST,
new SqlRexConvertlet() {
public RexNode convertCall(SqlRexContext cx, SqlCall call) {
return convertCast(cx, call);
}
});
registerOp(
SqlStdOperatorTable.IS_DISTINCT_FROM,
new SqlRexConvertlet() {
public RexNode convertCall(SqlRexContext cx, SqlCall call) {
return convertIsDistinctFrom(cx, call, false);
}
});
registerOp(
SqlStdOperatorTable.IS_NOT_DISTINCT_FROM,
new SqlRexConvertlet() {
public RexNode convertCall(SqlRexContext cx, SqlCall call) {
return convertIsDistinctFrom(cx, call, true);
}
});
registerOp(
SqlStdOperatorTable.PLUS,
new SqlRexConvertlet() {
public RexNode convertCall(SqlRexContext cx, SqlCall call) {
return convertPlus(cx, call);
}
});
registerOp(SqlStdOperatorTable.MINUS,
new SqlRexConvertlet() {
public RexNode convertCall(SqlRexContext cx, SqlCall call) {
final RexCall e =
(RexCall) StandardConvertletTable.this.convertCall(cx, call,
call.getOperator());
switch (e.getOperands().get(0).getType().getSqlTypeName()) {
case DATE:
case TIME:
case TIMESTAMP:
return convertDatetimeMinus(cx, SqlStdOperatorTable.MINUS_DATE,
call);
default:
return e;
}
}
});
registerOp(OracleSqlOperatorTable.LTRIM,
new TrimConvertlet(SqlTrimFunction.Flag.LEADING));
registerOp(OracleSqlOperatorTable.RTRIM,
new TrimConvertlet(SqlTrimFunction.Flag.TRAILING));
registerOp(OracleSqlOperatorTable.GREATEST, new GreatestConvertlet());
registerOp(OracleSqlOperatorTable.LEAST, new GreatestConvertlet());
registerOp(OracleSqlOperatorTable.NVL,
new SqlRexConvertlet() {
public RexNode convertCall(SqlRexContext cx, SqlCall call) {
final RexBuilder rexBuilder = cx.getRexBuilder();
final RexNode operand0 =
cx.convertExpression(call.getOperandList().get(0));
final RexNode operand1 =
cx.convertExpression(call.getOperandList().get(1));
final RelDataType type =
cx.getValidator().getValidatedNodeType(call);
return rexBuilder.makeCall(type, SqlStdOperatorTable.CASE,
ImmutableList.of(
rexBuilder.makeCall(SqlStdOperatorTable.IS_NOT_NULL,
operand0),
rexBuilder.makeCast(type, operand0),
rexBuilder.makeCast(type, operand1)));
}
});
registerOp(OracleSqlOperatorTable.DECODE,
new SqlRexConvertlet() {
public RexNode convertCall(SqlRexContext cx, SqlCall call) {
final RexBuilder rexBuilder = cx.getRexBuilder();
final List<RexNode> operands = convertExpressionList(cx,
call.getOperandList(), SqlOperandTypeChecker.Consistency.NONE);
final RelDataType type =
cx.getValidator().getValidatedNodeType(call);
final List<RexNode> exprs = new ArrayList<>();
for (int i = 1; i < operands.size() - 1; i += 2) {
exprs.add(
RelOptUtil.isDistinctFrom(rexBuilder, operands.get(0),
operands.get(i), true));
exprs.add(operands.get(i + 1));
}
if (operands.size() % 2 == 0) {
exprs.add(Util.last(operands));
} else {
exprs.add(rexBuilder.makeNullLiteral(type.getSqlTypeName()));
}
return rexBuilder.makeCall(type, SqlStdOperatorTable.CASE, exprs);
}
});
// Expand "x NOT LIKE y" into "NOT (x LIKE y)"
registerOp(
SqlStdOperatorTable.NOT_LIKE,
new SqlRexConvertlet() {
public RexNode convertCall(SqlRexContext cx, SqlCall call) {
final SqlCall expanded =
SqlStdOperatorTable.NOT.createCall(
SqlParserPos.ZERO,
SqlStdOperatorTable.LIKE.createCall(
SqlParserPos.ZERO,
call.getOperandList()));
return cx.convertExpression(expanded);
}
});
// Expand "x NOT SIMILAR y" into "NOT (x SIMILAR y)"
registerOp(
SqlStdOperatorTable.NOT_SIMILAR_TO,
new SqlRexConvertlet() {
public RexNode convertCall(SqlRexContext cx, SqlCall call) {
final SqlCall expanded =
SqlStdOperatorTable.NOT.createCall(
SqlParserPos.ZERO,
SqlStdOperatorTable.SIMILAR_TO.createCall(
SqlParserPos.ZERO,
call.getOperandList()));
return cx.convertExpression(expanded);
}
});
// Unary "+" has no effect, so expand "+ x" into "x".
registerOp(
SqlStdOperatorTable.UNARY_PLUS,
new SqlRexConvertlet() {
public RexNode convertCall(SqlRexContext cx, SqlCall call) {
SqlNode expanded = call.operand(0);
return cx.convertExpression(expanded);
}
});
// "AS" has no effect, so expand "x AS id" into "x".
registerOp(
SqlStdOperatorTable.AS,
new SqlRexConvertlet() {
public RexNode convertCall(SqlRexContext cx, SqlCall call) {
SqlNode expanded = call.operand(0);
return cx.convertExpression(expanded);
}
});
// "SQRT(x)" is equivalent to "POWER(x, .5)"
registerOp(
SqlStdOperatorTable.SQRT,
new SqlRexConvertlet() {
public RexNode convertCall(SqlRexContext cx, SqlCall call) {
SqlNode expanded =
SqlStdOperatorTable.POWER.createCall(
SqlParserPos.ZERO,
call.operand(0),
SqlLiteral.createExactNumeric(
"0.5", SqlParserPos.ZERO));
return cx.convertExpression(expanded);
}
});
// REVIEW jvs 24-Apr-2006: This only seems to be working from within a
// windowed agg. I have added an optimizer rule
// org.apache.calcite.rel.rules.AggregateReduceFunctionsRule which handles
// other cases post-translation. The reason I did that was to defer the
// implementation decision; e.g. we may want to push it down to a foreign
// server directly rather than decomposed; decomposition is easier than
// recognition.
// Convert "avg(<expr>)" to "cast(sum(<expr>) / count(<expr>) as
// <type>)". We don't need to handle the empty set specially, because
// the SUM is already supposed to come out as NULL in cases where the
// COUNT is zero, so the null check should take place first and prevent
// division by zero. We need the cast because SUM and COUNT may use
// different types, say BIGINT.
//
// Similarly STDDEV_POP and STDDEV_SAMP, VAR_POP and VAR_SAMP.
registerOp(
SqlStdOperatorTable.AVG,
new AvgVarianceConvertlet(SqlKind.AVG));
registerOp(
SqlStdOperatorTable.STDDEV_POP,
new AvgVarianceConvertlet(SqlKind.STDDEV_POP));
registerOp(
SqlStdOperatorTable.STDDEV_SAMP,
new AvgVarianceConvertlet(SqlKind.STDDEV_SAMP));
registerOp(
SqlStdOperatorTable.VAR_POP,
new AvgVarianceConvertlet(SqlKind.VAR_POP));
registerOp(
SqlStdOperatorTable.VAR_SAMP,
new AvgVarianceConvertlet(SqlKind.VAR_SAMP));
final SqlRexConvertlet floorCeilConvertlet = new FloorCeilConvertlet();
registerOp(SqlStdOperatorTable.FLOOR, floorCeilConvertlet);
registerOp(SqlStdOperatorTable.CEIL, floorCeilConvertlet);
registerOp(SqlStdOperatorTable.TIMESTAMP_ADD, new TimestampAddConvertlet());
registerOp(SqlStdOperatorTable.TIMESTAMP_DIFF,
new TimestampDiffConvertlet());
// Convert "element(<expr>)" to "$element_slice(<expr>)", if the
// expression is a multiset of scalars.
if (false) {
registerOp(
SqlStdOperatorTable.ELEMENT,
new SqlRexConvertlet() {
public RexNode convertCall(SqlRexContext cx, SqlCall call) {
assert call.operandCount() == 1;
final SqlNode operand = call.operand(0);
final RelDataType type =
cx.getValidator().getValidatedNodeType(operand);
if (!type.getComponentType().isStruct()) {
return cx.convertExpression(
SqlStdOperatorTable.ELEMENT_SLICE.createCall(
SqlParserPos.ZERO,
operand));
}
// fallback on default behavior
return StandardConvertletTable.this.convertCall(
cx,
call);
}
});
}
// Convert "$element_slice(<expr>)" to "element(<expr>).field#0"
if (false) {
registerOp(
SqlStdOperatorTable.ELEMENT_SLICE,
new SqlRexConvertlet() {
public RexNode convertCall(SqlRexContext cx, SqlCall call) {
assert call.operandCount() == 1;
final SqlNode operand = call.operand(0);
final RexNode expr =
cx.convertExpression(
SqlStdOperatorTable.ELEMENT.createCall(
SqlParserPos.ZERO,
operand));
return cx.getRexBuilder().makeFieldAccess(
expr,
0);
}
});
}
}
//~ Methods ----------------------------------------------------------------
/**
* Converts a CASE expression.
*/
public RexNode convertCase(
SqlRexContext cx,
SqlCase call) {
SqlNodeList whenList = call.getWhenOperands();
SqlNodeList thenList = call.getThenOperands();
assert whenList.size() == thenList.size();
final List<RexNode> exprList = new ArrayList<>();
for (int i = 0; i < whenList.size(); i++) {
exprList.add(cx.convertExpression(whenList.get(i)));
exprList.add(cx.convertExpression(thenList.get(i)));
}
exprList.add(cx.convertExpression(call.getElseOperand()));
RexBuilder rexBuilder = cx.getRexBuilder();
RelDataType type =
rexBuilder.deriveReturnType(call.getOperator(), exprList);
for (int i : elseArgs(exprList.size())) {
exprList.set(i,
rexBuilder.ensureType(type, exprList.get(i), false));
}
return rexBuilder.makeCall(type, SqlStdOperatorTable.CASE, exprList);
}
public RexNode convertMultiset(
SqlRexContext cx,
SqlMultisetValueConstructor op,
SqlCall call) {
final RelDataType originalType =
cx.getValidator().getValidatedNodeType(call);
RexRangeRef rr = cx.getSubqueryExpr(call);
assert rr != null;
RelDataType msType = rr.getType().getFieldList().get(0).getType();
RexNode expr =
cx.getRexBuilder().makeInputRef(
msType,
rr.getOffset());
assert msType.getComponentType().isStruct();
if (!originalType.getComponentType().isStruct()) {
// If the type is not a struct, the multiset operator will have
// wrapped the type as a record. Add a call to the $SLICE operator
// to compensate. For example,
// if '<ms>' has type 'RECORD (INTEGER x) MULTISET',
// then '$SLICE(<ms>) has type 'INTEGER MULTISET'.
// This will be removed as the expression is translated.
expr =
cx.getRexBuilder().makeCall(originalType, SqlStdOperatorTable.SLICE,
ImmutableList.of(expr));
}
return expr;
}
public RexNode convertArray(
SqlRexContext cx,
SqlArrayValueConstructor op,
SqlCall call) {
return convertCall(cx, call);
}
public RexNode convertMap(
SqlRexContext cx,
SqlMapValueConstructor op,
SqlCall call) {
return convertCall(cx, call);
}
public RexNode convertMultisetQuery(
SqlRexContext cx,
SqlMultisetQueryConstructor op,
SqlCall call) {
final RelDataType originalType =
cx.getValidator().getValidatedNodeType(call);
RexRangeRef rr = cx.getSubqueryExpr(call);
assert rr != null;
RelDataType msType = rr.getType().getFieldList().get(0).getType();
RexNode expr =
cx.getRexBuilder().makeInputRef(
msType,
rr.getOffset());
assert msType.getComponentType().isStruct();
if (!originalType.getComponentType().isStruct()) {
// If the type is not a struct, the multiset operator will have
// wrapped the type as a record. Add a call to the $SLICE operator
// to compensate. For example,
// if '<ms>' has type 'RECORD (INTEGER x) MULTISET',
// then '$SLICE(<ms>) has type 'INTEGER MULTISET'.
// This will be removed as the expression is translated.
expr =
cx.getRexBuilder().makeCall(SqlStdOperatorTable.SLICE, expr);
}
return expr;
}
public RexNode convertJdbc(
SqlRexContext cx,
SqlJdbcFunctionCall op,
SqlCall call) {
// Yuck!! The function definition contains arguments!
// TODO: adopt a more conventional definition/instance structure
final SqlCall convertedCall = op.getLookupCall();
return cx.convertExpression(convertedCall);
}
protected RexNode convertCast(
SqlRexContext cx,
final SqlCall call) {
RelDataTypeFactory typeFactory = cx.getTypeFactory();
assert call.getKind() == SqlKind.CAST;
final SqlNode left = call.operand(0);
final SqlNode right = call.operand(1);
if (right instanceof SqlIntervalQualifier) {
final SqlIntervalQualifier intervalQualifier =
(SqlIntervalQualifier) right;
if (left instanceof SqlIntervalLiteral) {
RexLiteral sourceInterval =
(RexLiteral) cx.convertExpression(left);
BigDecimal sourceValue =
(BigDecimal) sourceInterval.getValue();
RexLiteral castedInterval =
cx.getRexBuilder().makeIntervalLiteral(sourceValue,
intervalQualifier);
return castToValidatedType(cx, call, castedInterval);
} else if (left instanceof SqlNumericLiteral) {
RexLiteral sourceInterval =
(RexLiteral) cx.convertExpression(left);
BigDecimal sourceValue =
(BigDecimal) sourceInterval.getValue();
final BigDecimal multiplier = intervalQualifier.getUnit().multiplier;
sourceValue = sourceValue.multiply(multiplier);
RexLiteral castedInterval =
cx.getRexBuilder().makeIntervalLiteral(
sourceValue,
intervalQualifier);
return castToValidatedType(cx, call, castedInterval);
}
return castToValidatedType(cx, call, cx.convertExpression(left));
}
SqlDataTypeSpec dataType = (SqlDataTypeSpec) right;
if (SqlUtil.isNullLiteral(left, false)) {
return cx.convertExpression(left);
}
RexNode arg = cx.convertExpression(left);
RelDataType type = dataType.deriveType(typeFactory);
if (arg.getType().isNullable()) {
type = typeFactory.createTypeWithNullability(type, true);
}
if (null != dataType.getCollectionsTypeName()) {
final RelDataType argComponentType =
arg.getType().getComponentType();
final RelDataType componentType = type.getComponentType();
if (argComponentType.isStruct()
&& !componentType.isStruct()) {
RelDataType tt =
typeFactory.builder()
.add(
argComponentType.getFieldList().get(0).getName(),
componentType)
.build();
tt = typeFactory.createTypeWithNullability(
tt,
componentType.isNullable());
boolean isn = type.isNullable();
type = typeFactory.createMultisetType(tt, -1);
type = typeFactory.createTypeWithNullability(type, isn);
}
}
return cx.getRexBuilder().makeCast(type, arg);
}
protected RexNode convertFloorCeil(SqlRexContext cx, SqlCall call) {
final boolean floor = call.getKind() == SqlKind.FLOOR;
// Rewrite floor, ceil of interval
if (call.operandCount() == 1
&& call.operand(0) instanceof SqlIntervalLiteral) {
final SqlIntervalLiteral literal = call.operand(0);
SqlIntervalLiteral.IntervalValue interval =
(SqlIntervalLiteral.IntervalValue) literal.getValue();
BigDecimal val =
interval.getIntervalQualifier().getStartUnit().multiplier;
RexNode rexInterval = cx.convertExpression(literal);
RexNode res;
final RexBuilder rexBuilder = cx.getRexBuilder();
RexNode zero = rexBuilder.makeExactLiteral(BigDecimal.valueOf(0));
RexNode cond =
rexBuilder.makeCall(
SqlStdOperatorTable.GREATER_THAN_OR_EQUAL,
rexInterval,
zero);
RexNode pad =
rexBuilder.makeExactLiteral(val.subtract(BigDecimal.ONE));
RexNode cast = rexBuilder.makeReinterpretCast(
rexInterval.getType(), pad, rexBuilder.makeLiteral(false));
SqlOperator op =
floor ? SqlStdOperatorTable.MINUS
: SqlStdOperatorTable.PLUS;
RexNode sum = rexBuilder.makeCall(op, rexInterval, cast);
RexNode kase =
floor
? rexBuilder.makeCall(SqlStdOperatorTable.CASE,
cond, rexInterval, sum)
: rexBuilder.makeCall(SqlStdOperatorTable.CASE,
cond, sum, rexInterval);
RexNode factor = rexBuilder.makeExactLiteral(val);
RexNode div =
rexBuilder.makeCall(
SqlStdOperatorTable.DIVIDE_INTEGER,
kase,
factor);
RexNode mult =
rexBuilder.makeCall(
SqlStdOperatorTable.MULTIPLY,
div,
factor);
res = mult;
return res;
}
// normal floor, ceil function
return convertFunction(cx, (SqlFunction) call.getOperator(), call);
}
/**
* Converts a call to the {@code EXTRACT} function.
*
* <p>Called automatically via reflection.
*/
public RexNode convertExtract(
SqlRexContext cx,
SqlExtractFunction op,
SqlCall call) {
final RexBuilder rexBuilder = cx.getRexBuilder();
final List<SqlNode> operands = call.getOperandList();
final List<RexNode> exprs = convertExpressionList(cx, operands,
SqlOperandTypeChecker.Consistency.NONE);
// TODO: Will need to use decimal type for seconds with precision
RelDataType resType =
cx.getTypeFactory().createSqlType(SqlTypeName.BIGINT);
resType =
cx.getTypeFactory().createTypeWithNullability(
resType,
exprs.get(1).getType().isNullable());
RexNode res = rexBuilder.makeReinterpretCast(
resType, exprs.get(1), rexBuilder.makeLiteral(false));
final TimeUnit unit =
((SqlIntervalQualifier) operands.get(0)).getStartUnit();
final SqlTypeName sqlTypeName = exprs.get(1).getType().getSqlTypeName();
switch (unit) {
case YEAR:
case MONTH:
case DAY:
switch (sqlTypeName) {
case INTERVAL_YEAR:
case INTERVAL_YEAR_MONTH:
case INTERVAL_MONTH:
case INTERVAL_DAY:
case INTERVAL_DAY_HOUR:
case INTERVAL_DAY_MINUTE:
case INTERVAL_DAY_SECOND:
case INTERVAL_HOUR:
case INTERVAL_HOUR_MINUTE:
case INTERVAL_HOUR_SECOND:
case INTERVAL_MINUTE:
case INTERVAL_MINUTE_SECOND:
case INTERVAL_SECOND:
break;
case TIMESTAMP:
res = divide(rexBuilder, res, TimeUnit.DAY.multiplier);
// fall through
case DATE:
return rexBuilder.makeCall(resType, SqlStdOperatorTable.EXTRACT_DATE,
ImmutableList.of(exprs.get(0), res));
default:
throw new AssertionError("unexpected " + sqlTypeName);
}
break;
case MILLENNIUM:
case CENTURY:
case DECADE:
switch (sqlTypeName) {
case TIMESTAMP:
res = divide(rexBuilder, res, TimeUnit.DAY.multiplier);
// fall through
case DATE:
res = rexBuilder.makeCall(resType, SqlStdOperatorTable.EXTRACT_DATE,
ImmutableList.of(rexBuilder.makeFlag(TimeUnitRange.YEAR), res));
return divide(rexBuilder, res, unit.multiplier.divide(TimeUnit.YEAR.multiplier));
}
break;
case QUARTER:
switch (sqlTypeName) {
case TIMESTAMP:
res = divide(rexBuilder, res, TimeUnit.DAY.multiplier);
// fall through
case DATE:
res = rexBuilder.makeCall(resType, SqlStdOperatorTable.EXTRACT_DATE,
ImmutableList.of(rexBuilder.makeFlag(TimeUnitRange.MONTH), res));
res = rexBuilder.makeCall(SqlStdOperatorTable.MINUS, res,
rexBuilder.makeExactLiteral(BigDecimal.ONE));
res = divide(rexBuilder, res, unit.multiplier);
return rexBuilder.makeCall(SqlStdOperatorTable.PLUS, res,
rexBuilder.makeExactLiteral(BigDecimal.ONE));
}
break;
case EPOCH:
switch (sqlTypeName) {
case DATE:
// convert to milliseconds
res = rexBuilder.makeCall(resType, SqlStdOperatorTable.MULTIPLY,
ImmutableList.of(res, rexBuilder.makeExactLiteral(TimeUnit.DAY.multiplier)));
// fall through
case TIMESTAMP:
// convert to seconds
return divide(rexBuilder, res, TimeUnit.SECOND.multiplier);
case INTERVAL_YEAR:
case INTERVAL_YEAR_MONTH:
case INTERVAL_MONTH:
case INTERVAL_DAY:
case INTERVAL_DAY_HOUR:
case INTERVAL_DAY_MINUTE:
case INTERVAL_DAY_SECOND:
case INTERVAL_HOUR:
case INTERVAL_HOUR_MINUTE:
case INTERVAL_HOUR_SECOND:
case INTERVAL_MINUTE:
case INTERVAL_MINUTE_SECOND:
case INTERVAL_SECOND:
// no convertlet conversion, pass it as extract
return convertFunction(cx, (SqlFunction) call.getOperator(), call);
}
break;
case DOW:
case DOY:
case WEEK:
switch (sqlTypeName) {
case INTERVAL_YEAR:
case INTERVAL_YEAR_MONTH:
case INTERVAL_MONTH:
case INTERVAL_DAY:
case INTERVAL_DAY_HOUR:
case INTERVAL_DAY_MINUTE:
case INTERVAL_DAY_SECOND:
case INTERVAL_HOUR:
case INTERVAL_HOUR_MINUTE:
case INTERVAL_HOUR_SECOND:
case INTERVAL_MINUTE:
case INTERVAL_MINUTE_SECOND:
case INTERVAL_SECOND:
// TODO: is this check better to do in validation phase?
// Currently there is parameter on TimeUnit to identify these type of units.
throw new IllegalArgumentException("Extract " + unit + " from "
+ sqlTypeName + " type data is not supported");
case TIMESTAMP: // fall through
case DATE:
// no convertlet conversion, pass it as extract
return convertFunction(cx, (SqlFunction) call.getOperator(), call);
}
}
res = mod(rexBuilder, resType, res, getFactor(unit));
if (unit == TimeUnit.QUARTER) {
res = rexBuilder.makeCall(SqlStdOperatorTable.MINUS, res,
rexBuilder.makeExactLiteral(BigDecimal.ONE));
}
res = divide(rexBuilder, res, unit.multiplier);
if (unit == TimeUnit.QUARTER) {
res = rexBuilder.makeCall(SqlStdOperatorTable.PLUS, res,
rexBuilder.makeExactLiteral(BigDecimal.ONE));
}
return res;
}
/**
* Converts a call to the {@code QUARTER} function.
*
* <p>Called automatically via reflection.
*/
public RexNode convertQuarter(
SqlRexContext cx,
SqlQuarterFunction op,
SqlCall call) {
final List<SqlNode> operands = call.getOperandList();
assert operands.size() == 1;
RexNode x = cx.convertExpression(operands.get(0));
final RexBuilder rexBuilder = cx.getRexBuilder();
final RelDataTypeFactory typeFactory = cx.getTypeFactory();
final RelDataType resType =
typeFactory.createTypeWithNullability(
typeFactory.createSqlType(SqlTypeName.BIGINT),
x.getType().isNullable());
RexNode res =
rexBuilder.makeCall(
resType,
SqlStdOperatorTable.EXTRACT_DATE,
ImmutableList.of(rexBuilder.makeFlag(TimeUnitRange.MONTH), x));
res = rexBuilder.makeCall(SqlStdOperatorTable.MINUS, res,
rexBuilder.makeExactLiteral(BigDecimal.ONE));
res = divide(rexBuilder, res, TimeUnit.QUARTER.multiplier);
res = rexBuilder.makeCall(SqlStdOperatorTable.PLUS, res,
rexBuilder.makeExactLiteral(BigDecimal.ONE));
return res;
}
private static BigDecimal getFactor(TimeUnit unit) {
switch (unit) {
case DAY:
return BigDecimal.ONE;
case HOUR:
return TimeUnit.DAY.multiplier;
case MINUTE:
return TimeUnit.HOUR.multiplier;
case SECOND:
return TimeUnit.MINUTE.multiplier;
case MONTH:
return TimeUnit.YEAR.multiplier;
case QUARTER:
return TimeUnit.YEAR.multiplier;
case YEAR:
case DECADE:
case CENTURY:
case MILLENNIUM:
return BigDecimal.ONE;
default:
throw Util.unexpected(unit);
}
}
private RexNode mod(RexBuilder rexBuilder, RelDataType resType, RexNode res,
BigDecimal val) {
if (val.equals(BigDecimal.ONE)) {
return res;
}
return rexBuilder.makeCall(SqlStdOperatorTable.MOD, res,
rexBuilder.makeExactLiteral(val, resType));
}
private static RexNode divide(RexBuilder rexBuilder, RexNode res,
BigDecimal val) {
if (val.equals(BigDecimal.ONE)) {
return res;
}
// If val is between 0 and 1, rather than divide by val, multiply by its
// reciprocal. For example, rather than divide by 0.001 multiply by 1000.
if (val.compareTo(BigDecimal.ONE) < 0
&& val.signum() == 1) {
try {
final BigDecimal reciprocal =
BigDecimal.ONE.divide(val, BigDecimal.ROUND_UNNECESSARY);
return rexBuilder.makeCall(SqlStdOperatorTable.MULTIPLY, res,
rexBuilder.makeExactLiteral(reciprocal));
} catch (ArithmeticException e) {
// ignore - reciprocal is not an integer
}
}
return rexBuilder.makeCall(SqlStdOperatorTable.DIVIDE_INTEGER, res,
rexBuilder.makeExactLiteral(val));
}
public RexNode convertDatetimeMinus(
SqlRexContext cx,
SqlDatetimeSubtractionOperator op,
SqlCall call) {
// Rewrite datetime minus
final RexBuilder rexBuilder = cx.getRexBuilder();
final List<SqlNode> operands = call.getOperandList();
final List<RexNode> exprs = convertExpressionList(cx, operands,
SqlOperandTypeChecker.Consistency.NONE);
RelDataType int8Type =
cx.getTypeFactory().createSqlType(SqlTypeName.BIGINT);
final RexNode[] casts = new RexNode[2];
casts[0] =
rexBuilder.makeCast(
cx.getTypeFactory().createTypeWithNullability(
int8Type,
exprs.get(0).getType().isNullable()),
exprs.get(0));
casts[1] =
rexBuilder.makeCast(
cx.getTypeFactory().createTypeWithNullability(
int8Type,
exprs.get(1).getType().isNullable()),
exprs.get(1));
final RelDataType resType =
cx.getValidator().getValidatedNodeType(call);
return rexBuilder.makeCall(resType, op, exprs.subList(0, 2));
}
public RexNode convertFunction(
SqlRexContext cx,
SqlFunction fun,
SqlCall call) {
final List<SqlNode> operands = call.getOperandList();
final List<RexNode> exprs = convertExpressionList(cx, operands,
SqlOperandTypeChecker.Consistency.NONE);
if (fun.getFunctionType() == SqlFunctionCategory.USER_DEFINED_CONSTRUCTOR) {
return makeConstructorCall(cx, fun, exprs);
}
RelDataType returnType =
cx.getValidator().getValidatedNodeTypeIfKnown(call);
if (returnType == null) {
returnType = cx.getRexBuilder().deriveReturnType(fun, exprs);
}
return cx.getRexBuilder().makeCall(returnType, fun, exprs);
}
public RexNode convertSequenceValue(
SqlRexContext cx,
SqlSequenceValueOperator fun,
SqlCall call) {
final List<SqlNode> operands = call.getOperandList();
assert operands.size() == 1;
assert operands.get(0) instanceof SqlIdentifier;
final SqlIdentifier id = (SqlIdentifier) operands.get(0);
final String key = Util.listToString(id.names);
RelDataType returnType =
cx.getValidator().getValidatedNodeType(call);
return cx.getRexBuilder().makeCall(returnType, fun,
ImmutableList.<RexNode>of(cx.getRexBuilder().makeLiteral(key)));
}
public RexNode convertAggregateFunction(
SqlRexContext cx,
SqlAggFunction fun,
SqlCall call) {
final List<SqlNode> operands = call.getOperandList();
final List<RexNode> exprs;
if (call.isCountStar()) {
exprs = ImmutableList.of();
} else {
exprs = convertExpressionList(cx, operands,
SqlOperandTypeChecker.Consistency.NONE);
}
RelDataType returnType =
cx.getValidator().getValidatedNodeTypeIfKnown(call);
final int groupCount = cx.getGroupCount();
if (returnType == null) {
RexCallBinding binding =
new RexCallBinding(cx.getTypeFactory(), fun, exprs, ImmutableList.<RelCollation>of()) {
@Override public int getGroupCount() {
return groupCount;
}
};
returnType = fun.inferReturnType(binding);
}
return cx.getRexBuilder().makeCall(returnType, fun, exprs);
}
private static RexNode makeConstructorCall(
SqlRexContext cx,
SqlFunction constructor,
List<RexNode> exprs) {
final RexBuilder rexBuilder = cx.getRexBuilder();
RelDataType type = rexBuilder.deriveReturnType(constructor, exprs);
int n = type.getFieldCount();
ImmutableList.Builder<RexNode> initializationExprs =
ImmutableList.builder();
for (int i = 0; i < n; ++i) {
initializationExprs.add(
cx.getDefaultValueFactory().newAttributeInitializer(
type, constructor, i, exprs));
}
List<RexNode> defaultCasts =
RexUtil.generateCastExpressions(
rexBuilder,
type,
initializationExprs.build());
return rexBuilder.makeNewInvocation(type, defaultCasts);
}
/**
* Converts a call to an operator into a {@link RexCall} to the same
* operator.
*
* <p>Called automatically via reflection.
*
* @param cx Context
* @param call Call
* @return Rex call
*/
public RexNode convertCall(
SqlRexContext cx,
SqlCall call) {
return convertCall(cx, call, call.getOperator());
}
/** Converts a {@link SqlCall} to a {@link RexCall} with a perhaps different
* operator. */
private RexNode convertCall(
SqlRexContext cx,
SqlCall call,
SqlOperator op) {
final List<SqlNode> operands = call.getOperandList();
final RexBuilder rexBuilder = cx.getRexBuilder();
final SqlOperandTypeChecker.Consistency consistency =
op.getOperandTypeChecker() == null
? SqlOperandTypeChecker.Consistency.NONE
: op.getOperandTypeChecker().getConsistency();
final List<RexNode> exprs =
convertExpressionList(cx, operands, consistency);
RelDataType type = rexBuilder.deriveReturnType(op, exprs);
return rexBuilder.makeCall(type, op, RexUtil.flatten(exprs, op));
}
private List<Integer> elseArgs(int count) {
// If list is odd, e.g. [0, 1, 2, 3, 4] we get [1, 3, 4]
// If list is even, e.g. [0, 1, 2, 3, 4, 5] we get [2, 4, 5]
final List<Integer> list = new ArrayList<>();
for (int i = count % 2;;) {
list.add(i);
i += 2;
if (i >= count) {
list.add(i - 1);
break;
}
}
return list;
}
private static List<RexNode> convertExpressionList(SqlRexContext cx,
List<SqlNode> nodes, SqlOperandTypeChecker.Consistency consistency) {
final List<RexNode> exprs = Lists.newArrayList();
for (SqlNode node : nodes) {
exprs.add(cx.convertExpression(node));
}
if (exprs.size() > 1) {
final RelDataType type =
consistentType(cx, consistency, RexUtil.types(exprs));
if (type != null) {
final List<RexNode> oldExprs = Lists.newArrayList(exprs);
exprs.clear();
for (RexNode expr : oldExprs) {
exprs.add(cx.getRexBuilder().ensureType(type, expr, true));
}
}
}
return exprs;
}
private static RelDataType consistentType(SqlRexContext cx,
SqlOperandTypeChecker.Consistency consistency, List<RelDataType> types) {
switch (consistency) {
case COMPARE:
final Set<RelDataTypeFamily> families =
Sets.newHashSet(RexUtil.families(types));
if (families.size() < 2) {
// All arguments are of same family. No need for explicit casts.
return null;
}
final List<RelDataType> nonCharacterTypes = Lists.newArrayList();
for (RelDataType type : types) {
if (type.getFamily() != SqlTypeFamily.CHARACTER) {
nonCharacterTypes.add(type);
}
}
if (!nonCharacterTypes.isEmpty()) {
final int typeCount = types.size();
types = nonCharacterTypes;
if (nonCharacterTypes.size() < typeCount) {
final RelDataTypeFamily family =
nonCharacterTypes.get(0).getFamily();
if (family instanceof SqlTypeFamily) {
// The character arguments might be larger than the numeric
// argument. Give ourselves some headroom.
switch ((SqlTypeFamily) family) {
case INTEGER:
case NUMERIC:
nonCharacterTypes.add(
cx.getTypeFactory().createSqlType(SqlTypeName.BIGINT));
}
}
}
}
// fall through
case LEAST_RESTRICTIVE:
return cx.getTypeFactory().leastRestrictive(types);
default:
return null;
}
}
private RexNode convertPlus(SqlRexContext cx, SqlCall call) {
final RexNode rex = convertCall(cx, call);
switch (rex.getType().getSqlTypeName()) {
case DATE:
case TIME:
case TIMESTAMP:
// Use special "+" operator for datetime + interval.
// Re-order operands, if necessary, so that interval is second.
final RexBuilder rexBuilder = cx.getRexBuilder();
List<RexNode> operands = ((RexCall) rex).getOperands();
if (operands.size() == 2) {
final SqlTypeName sqlTypeName = operands.get(0).getType().getSqlTypeName();
switch (sqlTypeName) {
case INTERVAL_YEAR:
case INTERVAL_YEAR_MONTH:
case INTERVAL_MONTH:
case INTERVAL_DAY:
case INTERVAL_DAY_HOUR:
case INTERVAL_DAY_MINUTE:
case INTERVAL_DAY_SECOND:
case INTERVAL_HOUR:
case INTERVAL_HOUR_MINUTE:
case INTERVAL_HOUR_SECOND:
case INTERVAL_MINUTE:
case INTERVAL_MINUTE_SECOND:
case INTERVAL_SECOND:
operands = ImmutableList.of(operands.get(1), operands.get(0));
}
}
return rexBuilder.makeCall(rex.getType(),
SqlStdOperatorTable.DATETIME_PLUS, operands);
default:
return rex;
}
}
private RexNode convertIsDistinctFrom(
SqlRexContext cx,
SqlCall call,
boolean neg) {
RexNode op0 = cx.convertExpression(call.operand(0));
RexNode op1 = cx.convertExpression(call.operand(1));
return RelOptUtil.isDistinctFrom(
cx.getRexBuilder(), op0, op1, neg);
}
/**
* Converts a BETWEEN expression.
*
* <p>Called automatically via reflection.
*/
public RexNode convertBetween(
SqlRexContext cx,
SqlBetweenOperator op,
SqlCall call) {
final List<RexNode> list =
convertExpressionList(cx, call.getOperandList(),
op.getOperandTypeChecker().getConsistency());
final RexNode x = list.get(SqlBetweenOperator.VALUE_OPERAND);
final RexNode y = list.get(SqlBetweenOperator.LOWER_OPERAND);
final RexNode z = list.get(SqlBetweenOperator.UPPER_OPERAND);
final RexBuilder rexBuilder = cx.getRexBuilder();
RexNode ge1 =
rexBuilder.makeCall(
SqlStdOperatorTable.GREATER_THAN_OR_EQUAL, x, y);
RexNode le1 =
rexBuilder.makeCall(
SqlStdOperatorTable.LESS_THAN_OR_EQUAL,
x,
z);
RexNode and1 =
rexBuilder.makeCall(
SqlStdOperatorTable.AND,
ge1,
le1);
RexNode res;
final SqlBetweenOperator.Flag symmetric = op.flag;
switch (symmetric) {
case ASYMMETRIC:
res = and1;
break;
case SYMMETRIC:
RexNode ge2 =
rexBuilder.makeCall(
SqlStdOperatorTable.GREATER_THAN_OR_EQUAL,
x,
z);
RexNode le2 =
rexBuilder.makeCall(
SqlStdOperatorTable.LESS_THAN_OR_EQUAL,
x,
y);
RexNode and2 =
rexBuilder.makeCall(
SqlStdOperatorTable.AND,
ge2,
le2);
res =
rexBuilder.makeCall(
SqlStdOperatorTable.OR,
and1,
and2);
break;
default:
throw Util.unexpected(symmetric);
}
final SqlBetweenOperator betweenOp =
(SqlBetweenOperator) call.getOperator();
if (betweenOp.isNegated()) {
res = rexBuilder.makeCall(SqlStdOperatorTable.NOT, res);
}
return res;
}
/**
* Converts a LiteralChain expression: that is, concatenates the operands
* immediately, to produce a single literal string.
*
* <p>Called automatically via reflection.
*/
public RexNode convertLiteralChain(
SqlRexContext cx,
SqlLiteralChainOperator op,
SqlCall call) {
Util.discard(cx);
SqlLiteral sum = SqlLiteralChainOperator.concatenateOperands(call);
return cx.convertLiteral(sum);
}
/**
* Converts a ROW.
*
* <p>Called automatically via reflection.
*/
public RexNode convertRow(
SqlRexContext cx,
SqlRowOperator op,
SqlCall call) {
if (cx.getValidator().getValidatedNodeType(call).getSqlTypeName()
!= SqlTypeName.COLUMN_LIST) {
return convertCall(cx, call);
}
final RexBuilder rexBuilder = cx.getRexBuilder();
final List<RexNode> columns = new ArrayList<>();
for (SqlNode operand : call.getOperandList()) {
columns.add(
rexBuilder.makeLiteral(
((SqlIdentifier) operand).getSimple()));
}
final RelDataType type =
rexBuilder.deriveReturnType(SqlStdOperatorTable.COLUMN_LIST, columns);
return rexBuilder.makeCall(type, SqlStdOperatorTable.COLUMN_LIST, columns);
}
/**
* Converts a call to OVERLAPS.
*
* <p>Called automatically via reflection.
*/
public RexNode convertOverlaps(
SqlRexContext cx,
SqlOverlapsOperator op,
SqlCall call) {
// for intervals [t0, t1] overlaps [t2, t3], we can find if the
// intervals overlaps by: ~(t1 < t2 or t3 < t0)
final SqlNode[] operands = ((SqlBasicCall) call).getOperands();
assert operands.length == 4;
if (operands[1] instanceof SqlIntervalLiteral) {
// make t1 = t0 + t1 when t1 is an interval.
SqlOperator op1 = SqlStdOperatorTable.PLUS;
SqlNode[] second = new SqlNode[2];
second[0] = operands[0];
second[1] = operands[1];
operands[1] =
op1.createCall(
call.getParserPosition(),
second);
}
if (operands[3] instanceof SqlIntervalLiteral) {
// make t3 = t2 + t3 when t3 is an interval.
SqlOperator op1 = SqlStdOperatorTable.PLUS;
SqlNode[] four = new SqlNode[2];
four[0] = operands[2];
four[1] = operands[3];
operands[3] =
op1.createCall(
call.getParserPosition(),
four);
}
// This captures t1 >= t2
SqlOperator op1 = SqlStdOperatorTable.GREATER_THAN_OR_EQUAL;
SqlNode[] left = new SqlNode[2];
left[0] = operands[1];
left[1] = operands[2];
SqlCall call1 =
op1.createCall(
call.getParserPosition(),
left);
// This captures t3 >= t0
SqlOperator op2 = SqlStdOperatorTable.GREATER_THAN_OR_EQUAL;
SqlNode[] right = new SqlNode[2];
right[0] = operands[3];
right[1] = operands[0];
SqlCall call2 =
op2.createCall(
call.getParserPosition(),
right);
// This captures t1 >= t2 and t3 >= t0
SqlOperator and = SqlStdOperatorTable.AND;
SqlNode[] overlaps = new SqlNode[2];
overlaps[0] = call1;
overlaps[1] = call2;
SqlCall call3 =
and.createCall(
call.getParserPosition(),
overlaps);
return cx.convertExpression(call3);
}
/**
* Casts a RexNode value to the validated type of a SqlCall. If the value
* was already of the validated type, then the value is returned without an
* additional cast.
*/
public RexNode castToValidatedType(
SqlRexContext cx,
SqlCall call,
RexNode value) {
final RelDataType resType =
cx.getValidator().getValidatedNodeType(call);
if (value.getType() == resType) {
return value;
}
return cx.getRexBuilder().makeCast(resType, value);
}
/** Convertlet that handles {@code AVG} and {@code VARIANCE}
* windowed aggregate functions. */
private static class AvgVarianceConvertlet implements SqlRexConvertlet {
private final SqlKind kind;
public AvgVarianceConvertlet(SqlKind kind) {
this.kind = kind;
}
public RexNode convertCall(SqlRexContext cx, SqlCall call) {
assert call.operandCount() == 1;
final SqlNode arg = call.operand(0);
final SqlNode expr;
switch (kind) {
case AVG:
expr = expandAvg(arg);
break;
case STDDEV_POP:
expr = expandVariance(arg, true, true);
break;
case STDDEV_SAMP:
expr = expandVariance(arg, false, true);
break;
case VAR_POP:
expr = expandVariance(arg, true, false);
break;
case VAR_SAMP:
expr = expandVariance(arg, false, false);
break;
default:
throw Util.unexpected(kind);
}
RelDataType type =
cx.getValidator().getValidatedNodeType(call);
RexNode rex = cx.convertExpression(expr);
return cx.getRexBuilder().ensureType(type, rex, true);
}
private SqlNode expandAvg(
final SqlNode arg) {
final SqlParserPos pos = SqlParserPos.ZERO;
final SqlNode sum =
SqlStdOperatorTable.SUM.createCall(pos, arg);
final SqlNode count =
SqlStdOperatorTable.COUNT.createCall(pos, arg);
return SqlStdOperatorTable.DIVIDE.createCall(
pos, sum, count);
}
private SqlNode expandVariance(
final SqlNode arg,
boolean biased,
boolean sqrt) {
// stddev_pop(x) ==>
// power(
// (sum(x * x) - sum(x) * sum(x) / count(x))
// / count(x),
// .5)
//
// stddev_samp(x) ==>
// power(
// (sum(x * x) - sum(x) * sum(x) / count(x))
// / (count(x) - 1),
// .5)
//
// var_pop(x) ==>
// (sum(x * x) - sum(x) * sum(x) / count(x))
// / count(x)
//
// var_samp(x) ==>
// (sum(x * x) - sum(x) * sum(x) / count(x))
// / (count(x) - 1)
final SqlParserPos pos = SqlParserPos.ZERO;
final SqlNode argSquared =
SqlStdOperatorTable.MULTIPLY.createCall(pos, arg, arg);
final SqlNode sumArgSquared =
SqlStdOperatorTable.SUM.createCall(pos, argSquared);
final SqlNode sum =
SqlStdOperatorTable.SUM.createCall(pos, arg);
final SqlNode sumSquared =
SqlStdOperatorTable.MULTIPLY.createCall(pos, sum, sum);
final SqlNode count =
SqlStdOperatorTable.COUNT.createCall(pos, arg);
final SqlNode avgSumSquared =
SqlStdOperatorTable.DIVIDE.createCall(
pos, sumSquared, count);
final SqlNode diff =
SqlStdOperatorTable.MINUS.createCall(
pos, sumArgSquared, avgSumSquared);
final SqlNode denominator;
if (biased) {
denominator = count;
} else {
final SqlNumericLiteral one =
SqlLiteral.createExactNumeric("1", pos);
denominator =
SqlStdOperatorTable.MINUS.createCall(
pos, count, one);
}
final SqlNode div =
SqlStdOperatorTable.DIVIDE.createCall(
pos, diff, denominator);
SqlNode result = div;
if (sqrt) {
final SqlNumericLiteral half =
SqlLiteral.createExactNumeric("0.5", pos);
result =
SqlStdOperatorTable.POWER.createCall(pos, div, half);
}
return result;
}
}
/** Convertlet that converts {@code LTRIM} and {@code RTRIM} to
* {@code TRIM}. */
private static class TrimConvertlet implements SqlRexConvertlet {
private final SqlTrimFunction.Flag flag;
public TrimConvertlet(SqlTrimFunction.Flag flag) {
this.flag = flag;
}
public RexNode convertCall(SqlRexContext cx, SqlCall call) {
final RexBuilder rexBuilder = cx.getRexBuilder();
final RexNode operand =
cx.convertExpression(call.getOperandList().get(0));
return rexBuilder.makeCall(SqlStdOperatorTable.TRIM,
rexBuilder.makeFlag(flag), rexBuilder.makeLiteral(" "), operand);
}
}
/** Convertlet that converts {@code GREATEST} and {@code LEAST}. */
private static class GreatestConvertlet implements SqlRexConvertlet {
public RexNode convertCall(SqlRexContext cx, SqlCall call) {
// Translate
// GREATEST(a, b, c, d)
// to
// CASE
// WHEN a IS NULL OR b IS NULL OR c IS NULL OR d IS NULL
// THEN NULL
// WHEN a > b AND a > c AND a > d
// THEN a
// WHEN b > c AND b > d
// THEN b
// WHEN c > d
// THEN c
// ELSE d
// END
final RexBuilder rexBuilder = cx.getRexBuilder();
final RelDataType type =
cx.getValidator().getValidatedNodeType(call);
final SqlBinaryOperator op;
switch (call.getKind()) {
case GREATEST:
op = SqlStdOperatorTable.GREATER_THAN;
break;
case LEAST:
op = SqlStdOperatorTable.LESS_THAN;
break;
default:
throw new AssertionError();
}
final List<RexNode> exprs = convertExpressionList(cx,
call.getOperandList(), SqlOperandTypeChecker.Consistency.NONE);
final List<RexNode> list = new ArrayList<>();
final List<RexNode> orList = new ArrayList<>();
for (RexNode expr : exprs) {
orList.add(rexBuilder.makeCall(SqlStdOperatorTable.IS_NULL, expr));
}
list.add(RexUtil.composeDisjunction(rexBuilder, orList, false));
list.add(rexBuilder.makeNullLiteral(type.getSqlTypeName()));
for (int i = 0; i < exprs.size() - 1; i++) {
RexNode expr = exprs.get(i);
final List<RexNode> andList = new ArrayList<>();
for (int j = i + 1; j < exprs.size(); j++) {
final RexNode expr2 = exprs.get(j);
andList.add(rexBuilder.makeCall(op, expr, expr2));
}
list.add(RexUtil.composeConjunction(rexBuilder, andList, false));
list.add(expr);
}
list.add(exprs.get(exprs.size() - 1));
return rexBuilder.makeCall(type, SqlStdOperatorTable.CASE, list);
}
}
/** Convertlet that handles {@code FLOOR} and {@code CEIL} functions. */
private class FloorCeilConvertlet implements SqlRexConvertlet {
public RexNode convertCall(SqlRexContext cx, SqlCall call) {
return convertFloorCeil(cx, call);
}
}
/** Convertlet that handles the {@code TIMESTAMPADD} function. */
private static class TimestampAddConvertlet implements SqlRexConvertlet {
public RexNode convertCall(SqlRexContext cx, SqlCall call) {
// TIMESTAMPADD(unit, count, timestamp)
// => timestamp + count * INTERVAL '1' UNIT
final RexBuilder rexBuilder = cx.getRexBuilder();
final SqlLiteral unitLiteral = call.operand(0);
final TimeUnit unit = unitLiteral.symbolValue(TimeUnit.class);
return rexBuilder.makeCall(SqlStdOperatorTable.DATETIME_PLUS,
cx.convertExpression(call.operand(2)),
rexBuilder.makeCall(SqlStdOperatorTable.MULTIPLY,
rexBuilder.makeIntervalLiteral(unit.multiplier,
new SqlIntervalQualifier(unit, null,
unitLiteral.getParserPosition())),
cx.convertExpression(call.operand(1))));
}
}
/** Convertlet that handles the {@code TIMESTAMPDIFF} function. */
private static class TimestampDiffConvertlet implements SqlRexConvertlet {
public RexNode convertCall(SqlRexContext cx, SqlCall call) {
// TIMESTAMPDIFF(unit, t1, t2)
// => (t2 - t1) UNIT
final RexBuilder rexBuilder = cx.getRexBuilder();
final SqlLiteral unitLiteral = call.operand(0);
TimeUnit unit = unitLiteral.symbolValue(TimeUnit.class);
BigDecimal multiplier = BigDecimal.ONE;
BigDecimal divider = BigDecimal.ONE;
switch (unit) {
case MICROSECOND:
case MILLISECOND:
case WEEK:
multiplier = BigDecimal.valueOf(DateTimeUtils.MILLIS_PER_SECOND);
divider = unit.multiplier;
unit = TimeUnit.SECOND;
break;
case QUARTER:
divider = unit.multiplier;
unit = TimeUnit.MONTH;
break;
}
final SqlIntervalQualifier qualifier =
new SqlIntervalQualifier(unit, null, SqlParserPos.ZERO);
final RexNode op2 = cx.convertExpression(call.operand(2));
final RexNode op1 = cx.convertExpression(call.operand(1));
final RelDataType intervalType =
cx.getTypeFactory().createTypeWithNullability(
cx.getTypeFactory().createSqlIntervalType(qualifier),
op1.getType().isNullable() || op2.getType().isNullable());
final RexCall rexCall = (RexCall) rexBuilder.makeCall(
intervalType, SqlStdOperatorTable.MINUS_DATE,
ImmutableList.of(op2, op1));
final RelDataType intType =
cx.getTypeFactory().createTypeWithNullability(
cx.getTypeFactory().createSqlType(SqlTypeName.INTEGER),
SqlTypeUtil.containsNullable(rexCall.getType()));
RexNode e = rexBuilder.makeCast(intType, rexCall);
return rexBuilder.multiplyDivide(e, multiplier, divider);
}
}
}
// End StandardConvertletTable.java