use of org.apache.calcite.rex.RexCall in project druid by druid-io.
the class Expressions method toMathExpression.
/**
* Translate a row-expression to a Druid math expression. One day, when possible, this could be folded into
* {@link #toRowExtraction(DruidOperatorTable, PlannerContext, List, RexNode)}.
*
* @param rowOrder order of fields in the Druid rows to be extracted from
* @param expression expression meant to be applied on top of the rows
*
* @return expression referring to fields in rowOrder, or null if not possible
*/
public static String toMathExpression(final List<String> rowOrder, final RexNode expression) {
final SqlKind kind = expression.getKind();
final SqlTypeName sqlTypeName = expression.getType().getSqlTypeName();
if (kind == SqlKind.INPUT_REF) {
// Translate field references.
final RexInputRef ref = (RexInputRef) expression;
final String columnName = rowOrder.get(ref.getIndex());
if (columnName == null) {
throw new ISE("WTF?! Expression referred to nonexistent index[%d]", ref.getIndex());
}
return String.format("\"%s\"", escape(columnName));
} else if (kind == SqlKind.CAST || kind == SqlKind.REINTERPRET) {
// Translate casts.
final RexNode operand = ((RexCall) expression).getOperands().get(0);
final String operandExpression = toMathExpression(rowOrder, operand);
if (operandExpression == null) {
return null;
}
final ExprType fromType = MATH_TYPES.get(operand.getType().getSqlTypeName());
final ExprType toType = MATH_TYPES.get(sqlTypeName);
if (fromType != toType) {
return String.format("CAST(%s, '%s')", operandExpression, toType.toString());
} else {
return operandExpression;
}
} else if (kind == SqlKind.TIMES || kind == SqlKind.DIVIDE || kind == SqlKind.PLUS || kind == SqlKind.MINUS) {
// Translate simple arithmetic.
final List<RexNode> operands = ((RexCall) expression).getOperands();
final String lhsExpression = toMathExpression(rowOrder, operands.get(0));
final String rhsExpression = toMathExpression(rowOrder, operands.get(1));
if (lhsExpression == null || rhsExpression == null) {
return null;
}
final String op = ImmutableMap.of(SqlKind.TIMES, "*", SqlKind.DIVIDE, "/", SqlKind.PLUS, "+", SqlKind.MINUS, "-").get(kind);
return String.format("(%s %s %s)", lhsExpression, op, rhsExpression);
} else if (kind == SqlKind.OTHER_FUNCTION) {
final String calciteFunction = ((RexCall) expression).getOperator().getName();
final String druidFunction = MATH_FUNCTIONS.get(calciteFunction);
final List<String> functionArgs = Lists.newArrayList();
for (final RexNode operand : ((RexCall) expression).getOperands()) {
final String operandExpression = toMathExpression(rowOrder, operand);
if (operandExpression == null) {
return null;
}
functionArgs.add(operandExpression);
}
if ("MOD".equals(calciteFunction)) {
// Special handling for MOD, which is a function in Calcite but a binary operator in Druid.
Preconditions.checkState(functionArgs.size() == 2, "WTF?! Expected 2 args for MOD.");
return String.format("(%s %s %s)", functionArgs.get(0), "%", functionArgs.get(1));
}
if (druidFunction == null) {
return null;
}
return String.format("%s(%s)", druidFunction, Joiner.on(", ").join(functionArgs));
} else if (kind == SqlKind.LITERAL) {
// Translate literal.
if (SqlTypeName.NUMERIC_TYPES.contains(sqlTypeName)) {
// Include literal numbers as-is.
return String.valueOf(RexLiteral.value(expression));
} else if (SqlTypeName.STRING_TYPES.contains(sqlTypeName)) {
// Quote literal strings.
return "\'" + escape(RexLiteral.stringValue(expression)) + "\'";
} else {
// Can't translate other literals.
return null;
}
} else {
// Can't translate other kinds of expressions.
return null;
}
}
use of org.apache.calcite.rex.RexCall in project druid by druid-io.
the class Expressions method toPostAggregator.
/**
* Translate a Calcite row-expression to a Druid PostAggregator. One day, when possible, this could be folded
* into {@link #toRowExtraction(DruidOperatorTable, PlannerContext, List, RexNode)} .
*
* @param name name of the PostAggregator
* @param rowOrder order of fields in the Druid rows to be extracted from
* @param finalizingPostAggregatorFactories post-aggregators that should be used for specific entries in rowOrder.
* May be empty, and individual values may be null. Missing or null values
* will lead to creation of {@link FieldAccessPostAggregator}.
* @param expression expression meant to be applied on top of the rows
*
* @return PostAggregator or null if not possible
*/
public static PostAggregator toPostAggregator(final String name, final List<String> rowOrder, final List<PostAggregatorFactory> finalizingPostAggregatorFactories, final RexNode expression) {
final PostAggregator retVal;
if (expression.getKind() == SqlKind.INPUT_REF) {
final RexInputRef ref = (RexInputRef) expression;
final PostAggregatorFactory finalizingPostAggregatorFactory = finalizingPostAggregatorFactories.get(ref.getIndex());
retVal = finalizingPostAggregatorFactory != null ? finalizingPostAggregatorFactory.factorize(name) : new FieldAccessPostAggregator(name, rowOrder.get(ref.getIndex()));
} else if (expression.getKind() == SqlKind.CAST) {
// Ignore CAST when translating to PostAggregators and hope for the best. They are really loosey-goosey with
// types internally and there isn't much we can do to respect
// TODO(gianm): Probably not a good idea to ignore CAST like this.
final RexNode operand = ((RexCall) expression).getOperands().get(0);
retVal = toPostAggregator(name, rowOrder, finalizingPostAggregatorFactories, operand);
} else if (expression.getKind() == SqlKind.LITERAL && SqlTypeName.NUMERIC_TYPES.contains(expression.getType().getSqlTypeName())) {
retVal = new ConstantPostAggregator(name, (Number) RexLiteral.value(expression));
} else if (expression.getKind() == SqlKind.TIMES || expression.getKind() == SqlKind.DIVIDE || expression.getKind() == SqlKind.PLUS || expression.getKind() == SqlKind.MINUS) {
final String fnName = ImmutableMap.<SqlKind, String>builder().put(SqlKind.TIMES, "*").put(SqlKind.DIVIDE, "quotient").put(SqlKind.PLUS, "+").put(SqlKind.MINUS, "-").build().get(expression.getKind());
final List<PostAggregator> operands = Lists.newArrayList();
for (RexNode operand : ((RexCall) expression).getOperands()) {
final PostAggregator translatedOperand = toPostAggregator(null, rowOrder, finalizingPostAggregatorFactories, operand);
if (translatedOperand == null) {
return null;
}
operands.add(translatedOperand);
}
retVal = new ArithmeticPostAggregator(name, fnName, operands);
} else {
// Try converting to a math expression.
final String mathExpression = Expressions.toMathExpression(rowOrder, expression);
if (mathExpression == null) {
retVal = null;
} else {
retVal = new ExpressionPostAggregator(name, mathExpression);
}
}
if (retVal != null && name != null && !name.equals(retVal.getName())) {
throw new ISE("WTF?! Was about to return a PostAggregator with bad name, [%s] != [%s]", name, retVal.getName());
}
return retVal;
}
use of org.apache.calcite.rex.RexCall in project druid by druid-io.
the class Expressions method toRowExtraction.
/**
* Translate a Calcite row-expression to a Druid row extraction. Note that this signature will probably need to
* change once we support extractions from multiple columns.
*
* @param plannerContext SQL planner context
* @param rowOrder order of fields in the Druid rows to be extracted from
* @param expression expression meant to be applied on top of the rows
*
* @return RowExtraction or null if not possible
*/
public static RowExtraction toRowExtraction(final DruidOperatorTable operatorTable, final PlannerContext plannerContext, final List<String> rowOrder, final RexNode expression) {
if (expression.getKind() == SqlKind.INPUT_REF) {
final RexInputRef ref = (RexInputRef) expression;
final String columnName = rowOrder.get(ref.getIndex());
if (columnName == null) {
throw new ISE("WTF?! Expression referred to nonexistent index[%d]", ref.getIndex());
}
return RowExtraction.of(columnName, null);
} else if (expression.getKind() == SqlKind.CAST) {
final RexNode operand = ((RexCall) expression).getOperands().get(0);
if (expression.getType().getSqlTypeName() == SqlTypeName.DATE && operand.getType().getSqlTypeName() == SqlTypeName.TIMESTAMP) {
// Handling casting TIMESTAMP to DATE by flooring to DAY.
return FloorExtractionOperator.applyTimestampFloor(toRowExtraction(operatorTable, plannerContext, rowOrder, operand), TimeUnits.toQueryGranularity(TimeUnitRange.DAY, plannerContext.getTimeZone()));
} else {
// TODO(gianm): Probably not a good idea to ignore other CASTs like this.
return toRowExtraction(operatorTable, plannerContext, rowOrder, ((RexCall) expression).getOperands().get(0));
}
} else {
// Try conversion using a SqlExtractionOperator.
final RowExtraction retVal;
if (expression instanceof RexCall) {
final SqlExtractionOperator extractionOperator = operatorTable.lookupExtractionOperator(expression.getKind(), ((RexCall) expression).getOperator().getName());
retVal = extractionOperator != null ? extractionOperator.convert(operatorTable, plannerContext, rowOrder, expression) : null;
} else {
retVal = null;
}
return retVal;
}
}
use of org.apache.calcite.rex.RexCall in project druid by druid-io.
the class FloorExtractionOperator method convert.
@Override
public RowExtraction convert(final DruidOperatorTable operatorTable, final PlannerContext plannerContext, final List<String> rowOrder, final RexNode expression) {
final RexCall call = (RexCall) expression;
final RexNode arg = call.getOperands().get(0);
final RowExtraction rex = Expressions.toRowExtraction(operatorTable, plannerContext, rowOrder, arg);
if (rex == null) {
return null;
} else if (call.getOperands().size() == 1) {
// FLOOR(expr)
return RowExtraction.of(rex.getColumn(), ExtractionFns.compose(new BucketExtractionFn(1.0, 0.0), rex.getExtractionFn()));
} else if (call.getOperands().size() == 2) {
// FLOOR(expr TO timeUnit)
final RexLiteral flag = (RexLiteral) call.getOperands().get(1);
final TimeUnitRange timeUnit = (TimeUnitRange) flag.getValue();
return applyTimestampFloor(rex, TimeUnits.toQueryGranularity(timeUnit, plannerContext.getTimeZone()));
} else {
// WTF? FLOOR with 3 arguments?
return null;
}
}
use of org.apache.calcite.rex.RexCall in project calcite by apache.
the class RelMdColumnUniqueness method areColumnsUnique.
public Boolean areColumnsUnique(Project rel, RelMetadataQuery mq, ImmutableBitSet columns, boolean ignoreNulls) {
// LogicalProject maps a set of rows to a different set;
// Without knowledge of the mapping function(whether it
// preserves uniqueness), it is only safe to derive uniqueness
// info from the child of a project when the mapping is f(a) => a.
//
// Also need to map the input column set to the corresponding child
// references
List<RexNode> projExprs = rel.getProjects();
ImmutableBitSet.Builder childColumns = ImmutableBitSet.builder();
for (int bit : columns) {
RexNode projExpr = projExprs.get(bit);
if (projExpr instanceof RexInputRef) {
childColumns.set(((RexInputRef) projExpr).getIndex());
} else if (projExpr instanceof RexCall && ignoreNulls) {
// If the expression is a cast such that the types are the same
// except for the nullability, then if we're ignoring nulls,
// it doesn't matter whether the underlying column reference
// is nullable. Check that the types are the same by making a
// nullable copy of both types and then comparing them.
RexCall call = (RexCall) projExpr;
if (call.getOperator() != SqlStdOperatorTable.CAST) {
continue;
}
RexNode castOperand = call.getOperands().get(0);
if (!(castOperand instanceof RexInputRef)) {
continue;
}
RelDataTypeFactory typeFactory = rel.getCluster().getTypeFactory();
RelDataType castType = typeFactory.createTypeWithNullability(projExpr.getType(), true);
RelDataType origType = typeFactory.createTypeWithNullability(castOperand.getType(), true);
if (castType.equals(origType)) {
childColumns.set(((RexInputRef) castOperand).getIndex());
}
} else {
// projection, then skip it.
continue;
}
}
// If no columns can affect uniqueness, then return unknown
if (childColumns.cardinality() == 0) {
return null;
}
return mq.areColumnsUnique(rel.getInput(), childColumns.build(), ignoreNulls);
}
Aggregations