/* * 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.eigenbase.sql2rel; import java.math.*; import java.util.*; import org.eigenbase.relopt.*; import org.eigenbase.reltype.*; import org.eigenbase.rex.*; import org.eigenbase.sql.*; import org.eigenbase.sql.fun.*; import org.eigenbase.sql.parser.*; import org.eigenbase.sql.type.*; import org.eigenbase.util.*; import org.eigenbase.util14.DateTimeUtil; import com.google.common.collect.ImmutableList; /** * 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); } }); // 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.eigenbase.rel.rules.ReduceAggregatesRule 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(SqlAvgAggFunction.Subtype.AVG)); registerOp( SqlStdOperatorTable.STDDEV_POP, new AvgVarianceConvertlet(SqlAvgAggFunction.Subtype.STDDEV_POP)); registerOp( SqlStdOperatorTable.STDDEV_SAMP, new AvgVarianceConvertlet(SqlAvgAggFunction.Subtype.STDDEV_SAMP)); registerOp( SqlStdOperatorTable.VAR_POP, new AvgVarianceConvertlet(SqlAvgAggFunction.Subtype.VAR_POP)); registerOp( SqlStdOperatorTable.VAR_SAMP, new AvgVarianceConvertlet(SqlAvgAggFunction.Subtype.VAR_SAMP)); registerOp( SqlStdOperatorTable.FLOOR, new FloorCeilConvertlet(true)); registerOp( SqlStdOperatorTable.CEIL, new FloorCeilConvertlet(false)); // 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<RexNode>(); 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 || left instanceof SqlNumericLiteral) { RexLiteral sourceInterval = (RexLiteral) cx.convertExpression(left); BigDecimal sourceValue = (BigDecimal) sourceInterval.getValue(); RexLiteral castedInterval = cx.getRexBuilder().makeIntervalLiteral( sourceValue.multiply( BigDecimal.valueOf( intervalQualifier.getStartUnit().multiplier), MathContext.UNLIMITED), 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, boolean 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(); long 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(BigDecimal.valueOf(val - 1)); 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(BigDecimal.valueOf(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); } 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); // 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 SqlIntervalQualifier.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_MONTH: case INTERVAL_DAY_TIME: break; case TIMESTAMP: res = divide(rexBuilder, res, DateTimeUtil.MILLIS_PER_DAY); // fall through case DATE: return rexBuilder.makeCall(resType, SqlStdOperatorTable.EXTRACT_DATE, ImmutableList.of(exprs.get(0), res)); default: throw new AssertionError("unexpected " + sqlTypeName); } } res = mod(rexBuilder, resType, res, getFactor(unit)); res = divide(rexBuilder, res, unit.multiplier); return res; } private static long getFactor(SqlIntervalQualifier.TimeUnit unit) { switch (unit) { case DAY: return 1; case HOUR: return SqlIntervalQualifier.TimeUnit.DAY.multiplier; case MINUTE: return SqlIntervalQualifier.TimeUnit.HOUR.multiplier; case SECOND: return SqlIntervalQualifier.TimeUnit.MINUTE.multiplier; case YEAR: return 1; case MONTH: return SqlIntervalQualifier.TimeUnit.YEAR.multiplier; default: throw Util.unexpected(unit); } } private RexNode mod(RexBuilder rexBuilder, RelDataType resType, RexNode res, long val) { if (val == 1L) { return res; } return rexBuilder.makeCall(SqlStdOperatorTable.MOD, res, rexBuilder.makeExactLiteral(BigDecimal.valueOf(val), resType)); } private RexNode divide(RexBuilder rexBuilder, RexNode res, long val) { if (val == 1L) { return res; } return rexBuilder.makeCall(SqlStdOperatorTable.DIVIDE_INTEGER, res, rexBuilder.makeExactLiteral(BigDecimal.valueOf(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); // TODO: Handle year month interval (represented in months) for (RexNode expr : exprs) { if (SqlTypeName.INTERVAL_YEAR_MONTH == expr.getType().getSqlTypeName()) { Util.needToImplement( "Datetime subtraction of year month interval"); } } 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 RexNode minus = rexBuilder.makeCall( SqlStdOperatorTable.MINUS, casts); final RelDataType resType = cx.getValidator().getValidatedNodeType(call); return rexBuilder.makeReinterpretCast( resType, minus, rexBuilder.makeLiteral(false)); } public RexNode convertFunction( SqlRexContext cx, SqlFunction fun, SqlCall call) { final List<SqlNode> operands = call.getOperandList(); final List<RexNode> exprs = convertExpressionList(cx, operands); 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 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); } RelDataType returnType = cx.getValidator().getValidatedNodeTypeIfKnown(call); final int groupCount = cx.getGroupCount(); if (returnType == null) { RexCallBinding binding = new RexCallBinding(cx.getTypeFactory(), fun, exprs) { @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 List<RexNode> exprs = convertExpressionList(cx, operands); if (op.getOperandTypeChecker() == OperandTypes.COMPARABLE_UNORDERED_COMPARABLE_UNORDERED) { ensureSameType(cx, exprs); } 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] List<Integer> list = new ArrayList<Integer>(); for (int i = count % 2;;) { list.add(i); i += 2; if (i >= count) { list.add(i - 1); break; } } return list; } private void ensureSameType(SqlRexContext cx, final List<RexNode> exprs) { RelDataType type = cx.getTypeFactory().leastRestrictive( new AbstractList<RelDataType>() { public RelDataType get(int index) { return exprs.get(index).getType(); } public int size() { return exprs.size(); } }); for (int i = 0; i < exprs.size(); i++) { // REVIEW: assigning to a list that may be immutable? exprs.set( i, cx.getRexBuilder().ensureType(type, exprs.get(i), true)); } } private static List<RexNode> convertExpressionList( SqlRexContext cx, List<SqlNode> nodes) { final ArrayList<RexNode> exprs = new ArrayList<RexNode>(); for (SqlNode node : nodes) { exprs.add(cx.convertExpression(node)); } return exprs; } private RexNode convertPlus(SqlRexContext cx, SqlCall call) { final RexNode rex = convertCall(cx, call); switch (rex.getType().getSqlTypeName()) { case DATE: case TIME: case TIMESTAMP: return convertCall(cx, call, SqlStdOperatorTable.DATETIME_PLUS); 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 SqlNode value = call.operand(SqlBetweenOperator.VALUE_OPERAND); RexNode x = cx.convertExpression(value); final SqlBetweenOperator.Flag symmetric = op.flag; final SqlNode lower = call.operand(SqlBetweenOperator.LOWER_OPERAND); RexNode y = cx.convertExpression(lower); final SqlNode upper = call.operand(SqlBetweenOperator.UPPER_OPERAND); RexNode z = cx.convertExpression(upper); 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; 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<RexNode>(); 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); } private static class AvgVarianceConvertlet implements SqlRexConvertlet { private final SqlAvgAggFunction.Subtype subtype; public AvgVarianceConvertlet(SqlAvgAggFunction.Subtype subtype) { this.subtype = subtype; } public RexNode convertCall(SqlRexContext cx, SqlCall call) { assert call.operandCount() == 1; final SqlNode arg = call.operand(0); final SqlNode expr; switch (subtype) { 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(subtype); } 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; } } private class FloorCeilConvertlet implements SqlRexConvertlet { private final boolean floor; public FloorCeilConvertlet(boolean floor) { this.floor = floor; } public RexNode convertCall(SqlRexContext cx, SqlCall call) { return convertFloorCeil(cx, call, floor); } } } // End StandardConvertletTable.java