use of io.trino.sql.tree.PatternRecognitionRelation in project trino by trinodb.
the class PatternRecognitionAnalyzer method analyze.
public static PatternRecognitionAnalysis analyze(List<SubsetDefinition> subsets, List<VariableDefinition> variableDefinitions, List<MeasureDefinition> measures, RowPattern pattern, Optional<SkipTo> skipTo) {
// extract label names (Identifiers) from PATTERN and SUBSET clauses. create labels respecting SQL identifier semantics
Set<String> primaryLabels = extractExpressions(ImmutableList.of(pattern), Identifier.class).stream().map(PatternRecognitionAnalyzer::label).collect(toImmutableSet());
List<String> unionLabels = subsets.stream().map(SubsetDefinition::getName).map(PatternRecognitionAnalyzer::label).collect(toImmutableList());
// analyze SUBSET
Set<String> unique = new HashSet<>();
for (SubsetDefinition subset : subsets) {
String label = label(subset.getName());
if (primaryLabels.contains(label)) {
throw semanticException(INVALID_LABEL, subset.getName(), "union pattern variable name: %s is a duplicate of primary pattern variable name", subset.getName());
}
if (!unique.add(label)) {
throw semanticException(INVALID_LABEL, subset.getName(), "union pattern variable name: %s is declared twice", subset.getName());
}
for (Identifier element : subset.getIdentifiers()) {
// TODO can there be repetitions in the list of subset elements? (currently repetitions are supported)
if (!primaryLabels.contains(label(element))) {
throw semanticException(INVALID_LABEL, element, "subset element: %s is not a primary pattern variable", element);
}
}
}
// analyze DEFINE
unique = new HashSet<>();
for (VariableDefinition definition : variableDefinitions) {
String label = label(definition.getName());
if (!primaryLabels.contains(label)) {
throw semanticException(INVALID_LABEL, definition.getName(), "defined variable: %s is not a primary pattern variable", definition.getName());
}
if (!unique.add(label)) {
throw semanticException(INVALID_LABEL, definition.getName(), "pattern variable with name: %s is defined twice", definition.getName());
}
// DEFINE clause only supports RUNNING semantics which is default
Expression expression = definition.getExpression();
extractExpressions(ImmutableList.of(expression), FunctionCall.class).stream().filter(functionCall -> functionCall.getProcessingMode().map(mode -> mode.getMode() == FINAL).orElse(false)).findFirst().ifPresent(functionCall -> {
throw semanticException(INVALID_PROCESSING_MODE, functionCall.getProcessingMode().get(), "FINAL semantics is not supported in DEFINE clause");
});
}
// record primary labels without definitions. they are implicitly associated with `true` condition
Set<String> undefinedLabels = Sets.difference(primaryLabels, unique);
// validate pattern quantifiers
ImmutableMap.Builder<NodeRef<RangeQuantifier>, Range> ranges = ImmutableMap.builder();
preOrder(pattern).filter(RangeQuantifier.class::isInstance).map(RangeQuantifier.class::cast).forEach(quantifier -> {
Optional<Long> atLeast = quantifier.getAtLeast().map(LongLiteral::getValue);
atLeast.ifPresent(value -> {
if (value < 0) {
throw semanticException(NUMERIC_VALUE_OUT_OF_RANGE, quantifier, "Pattern quantifier lower bound must be greater than or equal to 0");
}
if (value > Integer.MAX_VALUE) {
throw semanticException(NUMERIC_VALUE_OUT_OF_RANGE, quantifier, "Pattern quantifier lower bound must not exceed " + Integer.MAX_VALUE);
}
});
Optional<Long> atMost = quantifier.getAtMost().map(LongLiteral::getValue);
atMost.ifPresent(value -> {
if (value < 1) {
throw semanticException(NUMERIC_VALUE_OUT_OF_RANGE, quantifier, "Pattern quantifier upper bound must be greater than or equal to 1");
}
if (value > Integer.MAX_VALUE) {
throw semanticException(NUMERIC_VALUE_OUT_OF_RANGE, quantifier, "Pattern quantifier upper bound must not exceed " + Integer.MAX_VALUE);
}
});
if (atLeast.isPresent() && atMost.isPresent()) {
if (atLeast.get() > atMost.get()) {
throw semanticException(INVALID_RANGE, quantifier, "Pattern quantifier lower bound must not exceed upper bound");
}
}
ranges.put(NodeRef.of(quantifier), new Range(atLeast.map(Math::toIntExact), atMost.map(Math::toIntExact)));
});
// validate AFTER MATCH SKIP
Set<String> allLabels = ImmutableSet.<String>builder().addAll(primaryLabels).addAll(unionLabels).build();
skipTo.flatMap(SkipTo::getIdentifier).ifPresent(identifier -> {
String label = label(identifier);
if (!allLabels.contains(label)) {
throw semanticException(INVALID_LABEL, identifier, "%s is not a primary or union pattern variable", identifier);
}
});
// check no prohibited nesting: cannot nest one row pattern recognition within another
List<Expression> expressions = Streams.concat(measures.stream().map(MeasureDefinition::getExpression), variableDefinitions.stream().map(VariableDefinition::getExpression)).collect(toImmutableList());
expressions.forEach(expression -> preOrder(expression).filter(child -> child instanceof PatternRecognitionRelation || child instanceof RowPattern).findFirst().ifPresent(nested -> {
throw semanticException(NESTED_ROW_PATTERN_RECOGNITION, nested, "nested row pattern recognition in row pattern recognition");
}));
return new PatternRecognitionAnalysis(allLabels, undefinedLabels, ranges.buildOrThrow());
}
use of io.trino.sql.tree.PatternRecognitionRelation in project trino by trinodb.
the class AstBuilder method visitPatternRecognition.
@Override
public Node visitPatternRecognition(SqlBaseParser.PatternRecognitionContext context) {
Relation child = (Relation) visit(context.aliasedRelation());
if (context.MATCH_RECOGNIZE() == null) {
return child;
}
Optional<OrderBy> orderBy = Optional.empty();
if (context.ORDER() != null) {
orderBy = Optional.of(new OrderBy(getLocation(context.ORDER()), visit(context.sortItem(), SortItem.class)));
}
Optional<PatternSearchMode> searchMode = Optional.empty();
if (context.INITIAL() != null) {
searchMode = Optional.of(new PatternSearchMode(getLocation(context.INITIAL()), INITIAL));
} else if (context.SEEK() != null) {
searchMode = Optional.of(new PatternSearchMode(getLocation(context.SEEK()), SEEK));
}
PatternRecognitionRelation relation = new PatternRecognitionRelation(getLocation(context), child, visit(context.partition, Expression.class), orderBy, visit(context.measureDefinition(), MeasureDefinition.class), getRowsPerMatch(context.rowsPerMatch()), visitIfPresent(context.skipTo(), SkipTo.class), searchMode, (RowPattern) visit(context.rowPattern()), visit(context.subsetDefinition(), SubsetDefinition.class), visit(context.variableDefinition(), VariableDefinition.class));
if (context.identifier() == null) {
return relation;
}
List<Identifier> aliases = null;
if (context.columnAliases() != null) {
aliases = visit(context.columnAliases().identifier(), Identifier.class);
}
return new AliasedRelation(getLocation(context), relation, (Identifier) visit(context.identifier()), aliases);
}
use of io.trino.sql.tree.PatternRecognitionRelation in project trino by trinodb.
the class RelationPlanner method visitPatternRecognitionRelation.
@Override
protected RelationPlan visitPatternRecognitionRelation(PatternRecognitionRelation node, Void context) {
RelationPlan subPlan = process(node.getInput(), context);
// Pre-project inputs for PARTITION BY and ORDER BY
List<Expression> inputs = ImmutableList.<Expression>builder().addAll(node.getPartitionBy()).addAll(getSortItemsFromOrderBy(node.getOrderBy()).stream().map(SortItem::getSortKey).collect(toImmutableList())).build();
PlanBuilder planBuilder = newPlanBuilder(subPlan, analysis, lambdaDeclarationToSymbolMap);
// no handleSubqueries because subqueries are not allowed here
planBuilder = planBuilder.appendProjections(inputs, symbolAllocator, idAllocator);
ImmutableList.Builder<Symbol> outputLayout = ImmutableList.builder();
boolean oneRowOutput = node.getRowsPerMatch().isEmpty() || node.getRowsPerMatch().get().isOneRow();
WindowNode.Specification specification = planWindowSpecification(node.getPartitionBy(), node.getOrderBy(), planBuilder::translate);
outputLayout.addAll(specification.getPartitionBy());
if (!oneRowOutput) {
getSortItemsFromOrderBy(node.getOrderBy()).stream().map(SortItem::getSortKey).map(planBuilder::translate).forEach(outputLayout::add);
}
planBuilder = subqueryPlanner.handleSubqueries(planBuilder, extractPatternRecognitionExpressions(node.getVariableDefinitions(), node.getMeasures()), analysis.getSubqueries(node));
PatternRecognitionComponents components = planPatternRecognitionComponents(planBuilder::rewrite, node.getSubsets(), node.getMeasures(), node.getAfterMatchSkipTo(), node.getPatternSearchMode(), node.getPattern(), node.getVariableDefinitions());
outputLayout.addAll(components.getMeasureOutputs());
if (!oneRowOutput) {
Set<Symbol> inputSymbolsOnOutput = ImmutableSet.copyOf(outputLayout.build());
subPlan.getFieldMappings().stream().filter(symbol -> !inputSymbolsOnOutput.contains(symbol)).forEach(outputLayout::add);
}
PatternRecognitionNode planNode = new PatternRecognitionNode(idAllocator.getNextId(), planBuilder.getRoot(), specification, Optional.empty(), ImmutableSet.of(), 0, ImmutableMap.of(), components.getMeasures(), Optional.empty(), node.getRowsPerMatch().orElse(ONE), components.getSkipToLabel(), components.getSkipToPosition(), components.isInitial(), components.getPattern(), components.getSubsets(), components.getVariableDefinitions());
return new RelationPlan(planNode, analysis.getScope(node), outputLayout.build(), outerContext);
}
Aggregations