use of io.trino.sql.tree.FunctionCall in project trino by trinodb.
the class ExtractSpatialJoins method tryCreateSpatialJoin.
private static Result tryCreateSpatialJoin(Context context, JoinNode joinNode, Expression filter, PlanNodeId nodeId, List<Symbol> outputSymbols, FunctionCall spatialFunction, Optional<Expression> radius, PlannerContext plannerContext, SplitManager splitManager, PageSourceManager pageSourceManager, TypeAnalyzer typeAnalyzer) {
// TODO Add support for distributed left spatial joins
Optional<String> spatialPartitioningTableName = joinNode.getType() == INNER ? getSpatialPartitioningTableName(context.getSession()) : Optional.empty();
Optional<KdbTree> kdbTree = spatialPartitioningTableName.map(tableName -> loadKdbTree(tableName, context.getSession(), plannerContext.getMetadata(), splitManager, pageSourceManager));
List<Expression> arguments = spatialFunction.getArguments();
verify(arguments.size() == 2);
Expression firstArgument = arguments.get(0);
Expression secondArgument = arguments.get(1);
Type sphericalGeographyType = plannerContext.getTypeManager().getType(SPHERICAL_GEOGRAPHY_TYPE_SIGNATURE);
if (typeAnalyzer.getType(context.getSession(), context.getSymbolAllocator().getTypes(), firstArgument).equals(sphericalGeographyType) || typeAnalyzer.getType(context.getSession(), context.getSymbolAllocator().getTypes(), secondArgument).equals(sphericalGeographyType)) {
return Result.empty();
}
Set<Symbol> firstSymbols = extractUnique(firstArgument);
Set<Symbol> secondSymbols = extractUnique(secondArgument);
if (firstSymbols.isEmpty() || secondSymbols.isEmpty()) {
return Result.empty();
}
Optional<Symbol> newFirstSymbol = newGeometrySymbol(context, firstArgument, plannerContext.getTypeManager());
Optional<Symbol> newSecondSymbol = newGeometrySymbol(context, secondArgument, plannerContext.getTypeManager());
PlanNode leftNode = joinNode.getLeft();
PlanNode rightNode = joinNode.getRight();
PlanNode newLeftNode;
PlanNode newRightNode;
// Check if the order of arguments of the spatial function matches the order of join sides
int alignment = checkAlignment(joinNode, firstSymbols, secondSymbols);
if (alignment > 0) {
newLeftNode = newFirstSymbol.map(symbol -> addProjection(context, leftNode, symbol, firstArgument)).orElse(leftNode);
newRightNode = newSecondSymbol.map(symbol -> addProjection(context, rightNode, symbol, secondArgument)).orElse(rightNode);
} else if (alignment < 0) {
newLeftNode = newSecondSymbol.map(symbol -> addProjection(context, leftNode, symbol, secondArgument)).orElse(leftNode);
newRightNode = newFirstSymbol.map(symbol -> addProjection(context, rightNode, symbol, firstArgument)).orElse(rightNode);
} else {
return Result.empty();
}
Expression newFirstArgument = toExpression(newFirstSymbol, firstArgument);
Expression newSecondArgument = toExpression(newSecondSymbol, secondArgument);
Optional<Symbol> leftPartitionSymbol = Optional.empty();
Optional<Symbol> rightPartitionSymbol = Optional.empty();
if (kdbTree.isPresent()) {
leftPartitionSymbol = Optional.of(context.getSymbolAllocator().newSymbol("pid", INTEGER));
rightPartitionSymbol = Optional.of(context.getSymbolAllocator().newSymbol("pid", INTEGER));
if (alignment > 0) {
newLeftNode = addPartitioningNodes(plannerContext, context, newLeftNode, leftPartitionSymbol.get(), kdbTree.get(), newFirstArgument, Optional.empty());
newRightNode = addPartitioningNodes(plannerContext, context, newRightNode, rightPartitionSymbol.get(), kdbTree.get(), newSecondArgument, radius);
} else {
newLeftNode = addPartitioningNodes(plannerContext, context, newLeftNode, leftPartitionSymbol.get(), kdbTree.get(), newSecondArgument, Optional.empty());
newRightNode = addPartitioningNodes(plannerContext, context, newRightNode, rightPartitionSymbol.get(), kdbTree.get(), newFirstArgument, radius);
}
}
Expression newSpatialFunction = FunctionCallBuilder.resolve(context.getSession(), plannerContext.getMetadata()).setName(spatialFunction.getName()).addArgument(GEOMETRY_TYPE_SIGNATURE, newFirstArgument).addArgument(GEOMETRY_TYPE_SIGNATURE, newSecondArgument).build();
Expression newFilter = replaceExpression(filter, ImmutableMap.of(spatialFunction, newSpatialFunction));
return Result.ofPlanNode(new SpatialJoinNode(nodeId, SpatialJoinNode.Type.fromJoinNodeType(joinNode.getType()), newLeftNode, newRightNode, outputSymbols, newFilter, leftPartitionSymbol, rightPartitionSymbol, kdbTree.map(KdbTreeUtils::toJson)));
}
use of io.trino.sql.tree.FunctionCall in project trino by trinodb.
the class ExtractSpatialJoins method addPartitioningNodes.
private static PlanNode addPartitioningNodes(PlannerContext plannerContext, Context context, PlanNode node, Symbol partitionSymbol, KdbTree kdbTree, Expression geometry, Optional<Expression> radius) {
Assignments.Builder projections = Assignments.builder();
for (Symbol outputSymbol : node.getOutputSymbols()) {
projections.putIdentity(outputSymbol);
}
TypeSignature typeSignature = new TypeSignature(KDB_TREE_TYPENAME);
FunctionCallBuilder spatialPartitionsCall = FunctionCallBuilder.resolve(context.getSession(), plannerContext.getMetadata()).setName(QualifiedName.of("spatial_partitions")).addArgument(typeSignature, new Cast(new StringLiteral(KdbTreeUtils.toJson(kdbTree)), toSqlType(plannerContext.getTypeManager().getType(typeSignature)))).addArgument(GEOMETRY_TYPE_SIGNATURE, geometry);
radius.ifPresent(value -> spatialPartitionsCall.addArgument(DOUBLE, value));
FunctionCall partitioningFunction = spatialPartitionsCall.build();
Symbol partitionsSymbol = context.getSymbolAllocator().newSymbol(partitioningFunction, new ArrayType(INTEGER));
projections.put(partitionsSymbol, partitioningFunction);
return new UnnestNode(context.getIdAllocator().getNextId(), new ProjectNode(context.getIdAllocator().getNextId(), node, projections.build()), node.getOutputSymbols(), ImmutableList.of(new UnnestNode.Mapping(partitionsSymbol, ImmutableList.of(partitionSymbol))), Optional.empty(), INNER, Optional.empty());
}
use of io.trino.sql.tree.FunctionCall 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.FunctionCall in project trino by trinodb.
the class DynamicFilters method getDescriptor.
public static Optional<Descriptor> getDescriptor(Expression expression) {
if (!(expression instanceof FunctionCall)) {
return Optional.empty();
}
FunctionCall functionCall = (FunctionCall) expression;
if (!isDynamicFilterFunction(functionCall)) {
return Optional.empty();
}
List<Expression> arguments = functionCall.getArguments();
checkArgument(arguments.size() == 4, "invalid arguments count: %s", arguments.size());
Expression probeSymbol = arguments.get(0);
Expression operatorExpression = arguments.get(1);
checkArgument(operatorExpression instanceof StringLiteral, "operatorExpression is expected to be an instance of StringLiteral: %s", operatorExpression.getClass().getSimpleName());
String operatorExpressionString = ((StringLiteral) operatorExpression).getValue();
ComparisonExpression.Operator operator = ComparisonExpression.Operator.valueOf(operatorExpressionString);
Expression idExpression = arguments.get(2);
checkArgument(idExpression instanceof StringLiteral, "id is expected to be an instance of StringLiteral: %s", idExpression.getClass().getSimpleName());
String id = ((StringLiteral) idExpression).getValue();
Expression nullAllowedExpression = arguments.get(3);
checkArgument(nullAllowedExpression instanceof BooleanLiteral, "nullAllowedExpression is expected to be an instance of BooleanLiteral: %s", nullAllowedExpression.getClass().getSimpleName());
boolean nullAllowed = ((BooleanLiteral) nullAllowedExpression).getValue();
return Optional.of(new Descriptor(new DynamicFilterId(id), probeSymbol, operator, nullAllowed));
}
use of io.trino.sql.tree.FunctionCall in project trino by trinodb.
the class TestSetTimeZoneTask method testSetTimeZoneIntervalDayTimeTypeInvalidFunctionCall.
@Test
public void testSetTimeZoneIntervalDayTimeTypeInvalidFunctionCall() {
QueryStateMachine stateMachine = createQueryStateMachine("SET TIME ZONE parse_duration('3601s')");
SetTimeZone setTimeZone = new SetTimeZone(new NodeLocation(1, 1), Optional.of(new FunctionCall(new NodeLocation(1, 24), QualifiedName.of(ImmutableList.of(new Identifier(new NodeLocation(1, 24), "parse_duration", false))), ImmutableList.of(new StringLiteral(new NodeLocation(1, 39), "3601s")))));
assertThatThrownBy(() -> executeSetTimeZone(setTimeZone, stateMachine)).isInstanceOf(TrinoException.class).hasMessage("Invalid time zone offset interval: interval contains seconds");
}
Aggregations