use of org.apache.calcite.rex.RexCall in project beam by apache.
the class CEPUtils method getCEPPatternFromPattern.
/**
* Construct a list of {@code CEPPattern}s from a {@code RexNode}.
*/
public static ArrayList<CEPPattern> getCEPPatternFromPattern(Schema upStreamSchema, RexNode call, Map<String, RexNode> patternDefs) {
ArrayList<CEPPattern> patternList = new ArrayList<>();
if (call.getClass() == RexLiteral.class) {
String p = ((RexLiteral) call).getValueAs(String.class);
RexNode pd = patternDefs.get(p);
patternList.add(CEPPattern.of(upStreamSchema, p, (RexCall) pd, Quantifier.NONE));
} else {
RexCall patCall = (RexCall) call;
SqlOperator operator = patCall.getOperator();
List<RexNode> operands = patCall.getOperands();
// check if the node has quantifier
if (operator.getKind() == SqlKind.PATTERN_QUANTIFIER) {
String p = ((RexLiteral) operands.get(0)).getValueAs(String.class);
RexNode pd = patternDefs.get(p);
int start = ((RexLiteral) operands.get(1)).getValueAs(Integer.class);
int end = ((RexLiteral) operands.get(2)).getValueAs(Integer.class);
boolean isReluctant = ((RexLiteral) operands.get(3)).getValueAs(Boolean.class);
patternList.add(CEPPattern.of(upStreamSchema, p, (RexCall) pd, getQuantifier(start, end, isReluctant)));
} else {
for (RexNode i : operands) {
patternList.addAll(getCEPPatternFromPattern(upStreamSchema, i, patternDefs));
}
}
}
return patternList;
}
use of org.apache.calcite.rex.RexCall in project beam by apache.
the class MongoDbTable method translateRexNodeToBson.
/**
* Recursively translates a single RexNode to MongoDB Bson filter. Supports simple comparison
* operations, negation, and nested conjunction/disjunction. Boolean fields are translated as an
* `$eq` operation with a boolean `true`.
*
* @param node {@code RexNode} to translate.
* @return {@code Bson} filter.
*/
private Bson translateRexNodeToBson(RexNode node) {
final IntFunction<String> fieldIdToName = i -> getSchema().getField(i).getName();
// Supported operations are described in MongoDbFilter#isSupported
if (node instanceof RexCall) {
RexCall compositeNode = (RexCall) node;
List<RexLiteral> literals = new ArrayList<>();
List<RexInputRef> inputRefs = new ArrayList<>();
for (RexNode operand : compositeNode.getOperands()) {
if (operand instanceof RexLiteral) {
literals.add((RexLiteral) operand);
} else if (operand instanceof RexInputRef) {
inputRefs.add((RexInputRef) operand);
}
}
// Operation is a comparison, since one of the operands in a field reference.
if (inputRefs.size() == 1) {
RexInputRef inputRef = inputRefs.get(0);
String inputFieldName = fieldIdToName.apply(inputRef.getIndex());
if (literals.size() > 0) {
// Convert literal value to the same Java type as the field we are comparing to.
Object literal = convertToExpectedType(inputRef, literals.get(0));
switch(node.getKind()) {
case IN:
return Filters.in(inputFieldName, convertToExpectedType(inputRef, literals));
case EQUALS:
return Filters.eq(inputFieldName, literal);
case NOT_EQUALS:
return Filters.not(Filters.eq(inputFieldName, literal));
case LESS_THAN:
return Filters.lt(inputFieldName, literal);
case GREATER_THAN:
return Filters.gt(inputFieldName, literal);
case GREATER_THAN_OR_EQUAL:
return Filters.gte(inputFieldName, literal);
case LESS_THAN_OR_EQUAL:
return Filters.lte(inputFieldName, literal);
default:
// Encountered an unexpected node kind, RuntimeException below.
break;
}
} else if (node.getKind().equals(SqlKind.NOT)) {
// Ex: `where not boolean_field`
return Filters.not(translateRexNodeToBson(inputRef));
} else {
throw new RuntimeException("Cannot create a filter for an unsupported node: " + node.toString());
}
} else {
// Operation is a conjunction/disjunction.
switch(node.getKind()) {
case AND:
// Recursively construct filter for each operand of conjunction.
return Filters.and(compositeNode.getOperands().stream().map(this::translateRexNodeToBson).collect(Collectors.toList()));
case OR:
// Recursively construct filter for each operand of disjunction.
return Filters.or(compositeNode.getOperands().stream().map(this::translateRexNodeToBson).collect(Collectors.toList()));
default:
// Encountered an unexpected node kind, RuntimeException below.
break;
}
}
throw new RuntimeException("Encountered an unexpected node kind: " + node.getKind().toString());
} else if (node instanceof RexInputRef && node.getType().getSqlTypeName().equals(SqlTypeName.BOOLEAN)) {
// Boolean field, must be true. Ex: `select * from table where bool_field`
return Filters.eq(fieldIdToName.apply(((RexInputRef) node).getIndex()), true);
}
throw new RuntimeException("Was expecting a RexCall or a boolean RexInputRef, but received: " + node.getClass().getSimpleName());
}
use of org.apache.calcite.rex.RexCall in project beam by apache.
the class BigQueryFilter method isSupported.
/**
* Check whether a {@code RexNode} is supported. As of right now BigQuery supports: 1. Complex
* predicates (both conjunction and disjunction). 2. Comparison between a column and a literal.
*
* <p>TODO: Check if comparison between two columns is supported. Also over a boolean field.
*
* @param node A node to check for predicate push-down support.
* @return A pair containing a boolean whether an expression is supported and the number of input
* references used by the expression.
*/
private Pair<Boolean, Integer> isSupported(RexNode node) {
int numberOfInputRefs = 0;
boolean isSupported = true;
if (node instanceof RexCall) {
RexCall compositeNode = (RexCall) node;
// CAST, TRIM? and REVERSE? should be supported as well.
if (!node.getKind().belongsTo(SUPPORTED_OPS)) {
isSupported = false;
} else {
for (RexNode operand : compositeNode.getOperands()) {
// All operands must be supported for a parent node to be supported.
Pair<Boolean, Integer> childSupported = isSupported(operand);
// (OR).
if (!node.getKind().belongsTo(ImmutableSet.of(AND, OR))) {
numberOfInputRefs += childSupported.getRight();
}
// Predicate functions, where more than one field is involved are unsupported.
isSupported = numberOfInputRefs < 2 && childSupported.getLeft();
}
}
} else if (node instanceof RexInputRef) {
numberOfInputRefs = 1;
} else if (node instanceof RexLiteral) {
// RexLiterals are expected, but no action is needed.
} else {
throw new UnsupportedOperationException("Encountered an unexpected node type: " + node.getClass().getSimpleName());
}
return Pair.of(isSupported, numberOfInputRefs);
}
use of org.apache.calcite.rex.RexCall in project druid by druid-io.
the class FilterJoinExcludePushToChildRule method getConjunctions.
/**
* Copied from {@link FilterJoinRule#getConjunctions}. Method is exactly the same as original.
*/
private List<RexNode> getConjunctions(Filter filter) {
List<RexNode> conjunctions = RelOptUtil.conjunctions(filter.getCondition());
RexBuilder rexBuilder = filter.getCluster().getRexBuilder();
for (int i = 0; i < conjunctions.size(); i++) {
RexNode node = conjunctions.get(i);
if (node instanceof RexCall) {
conjunctions.set(i, RelOptUtil.collapseExpandedIsNotDistinctFromExpr((RexCall) node, rexBuilder));
}
}
return conjunctions;
}
use of org.apache.calcite.rex.RexCall in project druid by druid-io.
the class DruidJoinRule method analyzeCondition.
/**
* If this condition is an AND of some combination of (1) literals; (2) equality conditions of the form
* {@code f(LeftRel) = RightColumn}, then return a {@link ConditionAnalysis}.
*/
private Optional<ConditionAnalysis> analyzeCondition(final RexNode condition, final RelDataType leftRowType, DruidRel<?> right) {
final List<RexNode> subConditions = decomposeAnd(condition);
final List<Pair<RexNode, RexInputRef>> equalitySubConditions = new ArrayList<>();
final List<RexLiteral> literalSubConditions = new ArrayList<>();
final int numLeftFields = leftRowType.getFieldCount();
final Set<RexInputRef> rightColumns = new HashSet<>();
for (RexNode subCondition : subConditions) {
if (RexUtil.isLiteral(subCondition, true)) {
if (subCondition.isA(SqlKind.CAST)) {
// This is CAST(literal) which is always OK.
// We know that this is CAST(literal) as it passed the check from RexUtil.isLiteral
RexCall call = (RexCall) subCondition;
// are different, then skipping the cast might change the meaning of the subcondition.
if (call.getType().getSqlTypeName().equals(call.getOperands().get(0).getType().getSqlTypeName())) {
// If the types are the same, unwrap the cast and use the underlying literal.
literalSubConditions.add((RexLiteral) call.getOperands().get(0));
} else {
// If the types are not the same, return Optional.empty() indicating the condition is not supported.
return Optional.empty();
}
} else {
// Literals are always OK.
literalSubConditions.add((RexLiteral) subCondition);
}
continue;
}
if (!subCondition.isA(SqlKind.EQUALS)) {
// If it's not EQUALS, it's not supported.
plannerContext.setPlanningError("SQL requires a join with '%s' condition that is not supported.", subCondition.getKind());
return Optional.empty();
}
final List<RexNode> operands = ((RexCall) subCondition).getOperands();
Preconditions.checkState(operands.size() == 2, "Expected 2 operands, got[%,d]", operands.size());
if (isLeftExpression(operands.get(0), numLeftFields) && isRightInputRef(operands.get(1), numLeftFields)) {
equalitySubConditions.add(Pair.of(operands.get(0), (RexInputRef) operands.get(1)));
rightColumns.add((RexInputRef) operands.get(1));
} else if (isRightInputRef(operands.get(0), numLeftFields) && isLeftExpression(operands.get(1), numLeftFields)) {
equalitySubConditions.add(Pair.of(operands.get(1), (RexInputRef) operands.get(0)));
rightColumns.add((RexInputRef) operands.get(0));
} else {
// Cannot handle this condition.
plannerContext.setPlanningError("SQL is resulting in a join that has unsupported operand types.");
return Optional.empty();
}
}
// thereby allowing join conditions on both k and v columns of the lookup
if (right != null && !DruidJoinQueryRel.computeRightRequiresSubquery(DruidJoinQueryRel.getSomeDruidChild(right)) && right instanceof DruidQueryRel) {
DruidQueryRel druidQueryRel = (DruidQueryRel) right;
if (druidQueryRel.getDruidTable().getDataSource() instanceof LookupDataSource) {
long distinctRightColumns = rightColumns.stream().map(RexSlot::getIndex).distinct().count();
if (distinctRightColumns > 1) {
// it means that the join's right side is lookup and the join condition contains both key and value columns of lookup.
// currently, the lookup datasource in the native engine doesn't support using value column in the join condition.
plannerContext.setPlanningError("SQL is resulting in a join involving lookup where value column is used in the condition.");
return Optional.empty();
}
}
}
return Optional.of(new ConditionAnalysis(numLeftFields, equalitySubConditions, literalSubConditions));
}
Aggregations