in core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java [2556:2716]
protected void convertMatchRecognize(Blackboard bb,
SqlMatchRecognize matchRecognize) {
final SqlValidatorNamespace ns = getNamespace(matchRecognize);
final SqlValidatorScope scope = validator().getMatchRecognizeScope(matchRecognize);
final Blackboard matchBb = createBlackboard(scope, null, false);
final RelDataType rowType = ns.getRowType();
// convert inner query, could be a table name or a derived table
SqlNode expr = matchRecognize.getTableRef();
convertFrom(matchBb, expr);
final RelNode input = matchBb.root();
// PARTITION BY
final SqlNodeList partitionList = matchRecognize.getPartitionList();
final ImmutableBitSet partitionKeys = buildPartitionKeys(matchBb, partitionList);
// ORDER BY
// TODO combine with buildCollation method after support NULLS_FIRST/NULLS_LAST
final SqlNodeList orderList = matchRecognize.getOrderList();
final List<RelFieldCollation> orderKeys = new ArrayList<>();
for (SqlNode order : orderList) {
final RelFieldCollation.Direction direction;
switch (order.getKind()) {
case DESCENDING:
direction = RelFieldCollation.Direction.DESCENDING;
order = ((SqlCall) order).operand(0);
break;
case NULLS_FIRST:
case NULLS_LAST:
throw new AssertionError();
default:
direction = RelFieldCollation.Direction.ASCENDING;
break;
}
final RelFieldCollation.NullDirection nullDirection =
validator().config().defaultNullCollation().last(desc(direction))
? RelFieldCollation.NullDirection.LAST
: RelFieldCollation.NullDirection.FIRST;
RexNode e = matchBb.convertExpression(order);
orderKeys.add(
new RelFieldCollation(((RexInputRef) e).getIndex(), direction,
nullDirection));
}
final RelCollation orders = cluster.traitSet().canonize(RelCollations.of(orderKeys));
// convert pattern
final Set<String> patternVarsSet = new HashSet<>();
SqlNode pattern = matchRecognize.getPattern();
final SqlBasicVisitor<@Nullable RexNode> patternVarVisitor =
new SqlBasicVisitor<@Nullable RexNode>() {
@Override public RexNode visit(SqlCall call) {
List<SqlNode> operands = call.getOperandList();
List<RexNode> newOperands = new ArrayList<>();
for (SqlNode node : operands) {
RexNode arg = requireNonNull(node.accept(this), node::toString);
newOperands.add(arg);
}
return rexBuilder.makeCall(call.getParserPosition(),
validator().getUnknownType(), call.getOperator(), newOperands);
}
@Override public RexNode visit(SqlIdentifier id) {
assert id.isSimple();
patternVarsSet.add(id.getSimple());
return rexBuilder.makeLiteral(id.getSimple());
}
@Override public RexNode visit(SqlLiteral literal) {
if (literal instanceof SqlNumericLiteral) {
return rexBuilder.makeExactLiteral(BigDecimal.valueOf(literal.intValue(true)));
} else {
return rexBuilder.makeLiteral(literal.booleanValue());
}
}
};
final RexNode patternNode = pattern.accept(patternVarVisitor);
if (patternNode == null) {
throw new AssertionError("pattern is not found in " + pattern);
}
SqlLiteral interval = matchRecognize.getInterval();
RexNode intervalNode = null;
if (interval != null) {
intervalNode = matchBb.convertLiteral(interval);
}
// convert subset
final SqlNodeList subsets = matchRecognize.getSubsetList();
final Map<String, TreeSet<String>> subsetMap = new HashMap<>();
for (SqlNode node : subsets) {
List<SqlNode> operands = ((SqlCall) node).getOperandList();
SqlIdentifier left = (SqlIdentifier) operands.get(0);
patternVarsSet.add(left.getSimple());
final SqlNodeList rights = (SqlNodeList) operands.get(1);
final TreeSet<String> list =
new TreeSet<>(SqlIdentifier.simpleNames(rights));
subsetMap.put(left.getSimple(), list);
}
SqlNode afterMatch = matchRecognize.getAfter();
if (afterMatch == null) {
afterMatch =
SqlMatchRecognize.AfterOption.SKIP_TO_NEXT_ROW.symbol(SqlParserPos.ZERO);
}
final RexNode after;
if (afterMatch instanceof SqlCall) {
final SqlCall afterCall = (SqlCall) afterMatch;
List<SqlNode> operands = afterCall.getOperandList();
SqlOperator operator = afterCall.getOperator();
assert operands.size() == 1;
SqlIdentifier id = (SqlIdentifier) operands.get(0);
assert patternVarsSet.contains(id.getSimple())
: id.getSimple() + " not defined in pattern";
RexNode rex = rexBuilder.makeLiteral(id.getSimple());
after =
rexBuilder.makeCall(afterCall.getParserPosition(), validator().getUnknownType(), operator,
ImmutableList.of(rex));
} else {
after = matchBb.convertExpression(afterMatch);
}
matchBb.setPatternVarRef(true);
// convert measures
final ImmutableMap.Builder<String, RexNode> measureNodes =
ImmutableMap.builder();
for (SqlNode measure : matchRecognize.getMeasureList()) {
List<SqlNode> operands = ((SqlCall) measure).getOperandList();
String alias = ((SqlIdentifier) operands.get(1)).getSimple();
RexNode rex = matchBb.convertExpression(operands.get(0));
measureNodes.put(alias, rex);
}
// convert definitions
final ImmutableMap.Builder<String, RexNode> definitionNodes =
ImmutableMap.builder();
for (SqlNode def : matchRecognize.getPatternDefList()) {
replaceSubQueries(matchBb, def, RelOptUtil.Logic.UNKNOWN_AS_FALSE);
List<SqlNode> operands = ((SqlCall) def).getOperandList();
String alias = ((SqlIdentifier) operands.get(1)).getSimple();
RexNode rex = matchBb.convertExpression(operands.get(0));
definitionNodes.put(alias, rex);
}
final SqlLiteral rowsPerMatch = matchRecognize.getRowsPerMatch();
final boolean allRows = rowsPerMatch != null
&& rowsPerMatch.getValue() == SqlMatchRecognize.RowsPerMatchOption.ALL_ROWS;
matchBb.setPatternVarRef(false);
final RelFactories.MatchFactory factory =
RelFactories.DEFAULT_MATCH_FACTORY;
final RelNode rel =
factory.createMatch(input, patternNode,
rowType, matchRecognize.getStrictStart().booleanValue(),
matchRecognize.getStrictEnd().booleanValue(),
definitionNodes.build(), measureNodes.build(), after, subsetMap,
allRows, partitionKeys, orders, intervalNode);
bb.setRoot(rel, false);
}