use of io.trino.sql.analyzer.Analysis in project trino by trinodb.
the class CreateViewTask method execute.
@Override
public ListenableFuture<Void> execute(CreateView statement, QueryStateMachine stateMachine, List<Expression> parameters, WarningCollector warningCollector) {
Session session = stateMachine.getSession();
QualifiedObjectName name = createQualifiedObjectName(session, statement, statement.getName());
accessControl.checkCanCreateView(session.toSecurityContext(), name);
if (metadata.isMaterializedView(session, name)) {
throw semanticException(TABLE_ALREADY_EXISTS, statement, "Materialized view already exists: '%s'", name);
}
if (metadata.isView(session, name)) {
if (!statement.isReplace()) {
throw semanticException(TABLE_ALREADY_EXISTS, statement, "View already exists: '%s'", name);
}
} else if (metadata.getTableHandle(session, name).isPresent()) {
throw semanticException(TABLE_ALREADY_EXISTS, statement, "Table already exists: '%s'", name);
}
String sql = getFormattedSql(statement.getQuery(), sqlParser);
Analysis analysis = analyzerFactory.createAnalyzer(session, parameters, parameterExtractor(statement, parameters), stateMachine.getWarningCollector()).analyze(statement);
List<ViewColumn> columns = analysis.getOutputDescriptor(statement.getQuery()).getVisibleFields().stream().map(field -> new ViewColumn(field.getName().get(), field.getType().getTypeId())).collect(toImmutableList());
// use DEFINER security by default
Optional<Identity> owner = Optional.of(session.getIdentity());
if (statement.getSecurity().orElse(null) == INVOKER) {
owner = Optional.empty();
}
ViewDefinition definition = new ViewDefinition(sql, session.getCatalog(), session.getSchema(), columns, statement.getComment(), owner);
metadata.createView(session, name, definition, statement.isReplace());
stateMachine.setOutput(analysis.getTarget());
stateMachine.setReferencedTables(analysis.getReferencedTables());
return immediateVoidFuture();
}
use of io.trino.sql.analyzer.Analysis in project trino by trinodb.
the class QueryPlanner method planAggregation.
private PlanBuilder planAggregation(PlanBuilder subPlan, List<List<Symbol>> groupingSets, Optional<Symbol> groupIdSymbol, List<FunctionCall> aggregates, Function<Expression, Symbol> coercions) {
ImmutableList.Builder<AggregationAssignment> aggregateMappingBuilder = ImmutableList.builder();
// deduplicate based on scope-aware equality
for (FunctionCall function : scopeAwareDistinct(subPlan, aggregates)) {
Symbol symbol = symbolAllocator.newSymbol(function, analysis.getType(function));
// TODO: for ORDER BY arguments, rewrite them such that they match the actual arguments to the function. This is necessary to maintain the semantics of DISTINCT + ORDER BY,
// which requires that ORDER BY be a subset of arguments
// What can happen currently is that if the argument requires a coercion, the argument will take a different input that the ORDER BY clause, which is undefined behavior
Aggregation aggregation = new Aggregation(analysis.getResolvedFunction(function), function.getArguments().stream().map(argument -> {
if (argument instanceof LambdaExpression) {
return subPlan.rewrite(argument);
}
return coercions.apply(argument).toSymbolReference();
}).collect(toImmutableList()), function.isDistinct(), function.getFilter().map(coercions), function.getOrderBy().map(orderBy -> translateOrderingScheme(orderBy.getSortItems(), coercions)), Optional.empty());
aggregateMappingBuilder.add(new AggregationAssignment(symbol, function, aggregation));
}
List<AggregationAssignment> aggregateMappings = aggregateMappingBuilder.build();
ImmutableSet.Builder<Integer> globalGroupingSets = ImmutableSet.builder();
for (int i = 0; i < groupingSets.size(); i++) {
if (groupingSets.get(i).isEmpty()) {
globalGroupingSets.add(i);
}
}
ImmutableList.Builder<Symbol> groupingKeys = ImmutableList.builder();
groupingSets.stream().flatMap(List::stream).distinct().forEach(groupingKeys::add);
groupIdSymbol.ifPresent(groupingKeys::add);
AggregationNode aggregationNode = new AggregationNode(idAllocator.getNextId(), subPlan.getRoot(), aggregateMappings.stream().collect(toImmutableMap(AggregationAssignment::getSymbol, AggregationAssignment::getRewritten)), groupingSets(groupingKeys.build(), groupingSets.size(), globalGroupingSets.build()), ImmutableList.of(), AggregationNode.Step.SINGLE, Optional.empty(), groupIdSymbol);
return new PlanBuilder(subPlan.getTranslations().withAdditionalMappings(aggregateMappings.stream().collect(toImmutableMap(assignment -> scopeAwareKey(assignment.getAstExpression(), analysis, subPlan.getScope()), AggregationAssignment::getSymbol))), aggregationNode);
}
use of io.trino.sql.analyzer.Analysis 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);
}
use of io.trino.sql.analyzer.Analysis in project trino by trinodb.
the class QueryPlanner method aggregate.
private PlanBuilder aggregate(PlanBuilder subPlan, QuerySpecification node) {
if (!analysis.isAggregation(node)) {
return subPlan;
}
ImmutableList.Builder<Expression> inputBuilder = ImmutableList.builder();
analysis.getAggregates(node).stream().map(FunctionCall::getArguments).flatMap(List::stream).filter(// lambda expression is generated at execution time
expression -> !(expression instanceof LambdaExpression)).forEach(inputBuilder::add);
analysis.getAggregates(node).stream().map(FunctionCall::getOrderBy).map(NodeUtils::getSortItemsFromOrderBy).flatMap(List::stream).map(SortItem::getSortKey).forEach(inputBuilder::add);
// filter expressions need to be projected first
analysis.getAggregates(node).stream().map(FunctionCall::getFilter).filter(Optional::isPresent).map(Optional::get).forEach(inputBuilder::add);
GroupingSetAnalysis groupingSetAnalysis = analysis.getGroupingSets(node);
inputBuilder.addAll(groupingSetAnalysis.getComplexExpressions());
List<Expression> inputs = inputBuilder.build();
subPlan = subqueryPlanner.handleSubqueries(subPlan, inputs, analysis.getSubqueries(node));
subPlan = subPlan.appendProjections(inputs, symbolAllocator, idAllocator);
// Add projection to coerce inputs to their site-specific types.
// This is important because the same lexical expression may need to be coerced
// in different ways if it's referenced by multiple arguments to the window function.
// For example, given v::integer,
// avg(v)
// Needs to be rewritten as
// avg(CAST(v AS double))
PlanAndMappings coercions = coerce(subPlan, inputs, analysis, idAllocator, symbolAllocator, typeCoercion);
subPlan = coercions.getSubPlan();
GroupingSetsPlan groupingSets = planGroupingSets(subPlan, node, groupingSetAnalysis);
subPlan = planAggregation(groupingSets.getSubPlan(), groupingSets.getGroupingSets(), groupingSets.getGroupIdSymbol(), analysis.getAggregates(node), coercions::get);
return planGroupingOperations(subPlan, node, groupingSets.getGroupIdSymbol(), groupingSets.getColumnOnlyGroupingSets());
}
use of io.trino.sql.analyzer.Analysis in project trino by trinodb.
the class QueryPlanner method planExpand.
public RelationPlan planExpand(Query query) {
checkArgument(analysis.isExpandableQuery(query), "query is not registered as expandable");
Union union = (Union) query.getQueryBody();
ImmutableList.Builder<NodeAndMappings> recursionSteps = ImmutableList.builder();
// plan anchor relation
Relation anchorNode = union.getRelations().get(0);
RelationPlan anchorPlan = new RelationPlanner(analysis, symbolAllocator, idAllocator, lambdaDeclarationToSymbolMap, plannerContext, outerContext, session, recursiveSubqueries).process(anchorNode, null);
// prune anchor plan outputs to contain only the symbols exposed in the scope
NodeAndMappings prunedAnchorPlan = pruneInvisibleFields(anchorPlan, idAllocator);
// if the anchor plan has duplicate output symbols, add projection on top to make the symbols unique
// This is necessary to successfully unroll recursion: the recursion step relation must follow
// the same layout while it might not have duplicate outputs where the anchor plan did
NodeAndMappings disambiguatedAnchorPlan = disambiguateOutputs(prunedAnchorPlan, symbolAllocator, idAllocator);
anchorPlan = new RelationPlan(disambiguatedAnchorPlan.getNode(), analysis.getScope(query), disambiguatedAnchorPlan.getFields(), outerContext);
recursionSteps.add(copy(anchorPlan.getRoot(), anchorPlan.getFieldMappings()));
// plan recursion step
Relation recursionStepRelation = union.getRelations().get(1);
RelationPlan recursionStepPlan = new RelationPlanner(analysis, symbolAllocator, idAllocator, lambdaDeclarationToSymbolMap, plannerContext, outerContext, session, ImmutableMap.of(NodeRef.of(analysis.getRecursiveReference(query)), anchorPlan)).process(recursionStepRelation, null);
// coerce recursion step outputs and prune them to contain only the symbols exposed in the scope
NodeAndMappings coercedRecursionStep;
List<Type> types = analysis.getRelationCoercion(recursionStepRelation);
if (types == null) {
coercedRecursionStep = pruneInvisibleFields(recursionStepPlan, idAllocator);
} else {
coercedRecursionStep = coerce(recursionStepPlan, types, symbolAllocator, idAllocator);
}
NodeAndMappings replacementSpot = new NodeAndMappings(anchorPlan.getRoot(), anchorPlan.getFieldMappings());
PlanNode recursionStep = coercedRecursionStep.getNode();
List<Symbol> mappings = coercedRecursionStep.getFields();
// unroll recursion
int maxRecursionDepth = getMaxRecursionDepth(session);
for (int i = 0; i < maxRecursionDepth; i++) {
recursionSteps.add(copy(recursionStep, mappings));
NodeAndMappings replacement = copy(recursionStep, mappings);
// if the recursion step plan has duplicate output symbols, add projection on top to make the symbols unique
// This is necessary to successfully unroll recursion: the relation on the next recursion step must follow
// the same layout while it might not have duplicate outputs where the plan for this step did
replacement = disambiguateOutputs(replacement, symbolAllocator, idAllocator);
recursionStep = replace(recursionStep, replacementSpot, replacement);
replacementSpot = replacement;
}
// after the last recursion step, check if the recursion converged. the last step is expected to return empty result
// 1. append window to count rows
NodeAndMappings checkConvergenceStep = copy(recursionStep, mappings);
Symbol countSymbol = symbolAllocator.newSymbol("count", BIGINT);
ResolvedFunction function = plannerContext.getMetadata().resolveFunction(session, QualifiedName.of("count"), ImmutableList.of());
WindowNode.Function countFunction = new WindowNode.Function(function, ImmutableList.of(), DEFAULT_FRAME, false);
WindowNode windowNode = new WindowNode(idAllocator.getNextId(), checkConvergenceStep.getNode(), new WindowNode.Specification(ImmutableList.of(), Optional.empty()), ImmutableMap.of(countSymbol, countFunction), Optional.empty(), ImmutableSet.of(), 0);
// 2. append filter to fail on non-empty result
ResolvedFunction fail = plannerContext.getMetadata().resolveFunction(session, QualifiedName.of("fail"), fromTypes(VARCHAR));
String recursionLimitExceededMessage = format("Recursion depth limit exceeded (%s). Use 'max_recursion_depth' session property to modify the limit.", maxRecursionDepth);
Expression predicate = new IfExpression(new ComparisonExpression(GREATER_THAN_OR_EQUAL, countSymbol.toSymbolReference(), new GenericLiteral("BIGINT", "0")), new Cast(new FunctionCall(fail.toQualifiedName(), ImmutableList.of(new Cast(new StringLiteral(recursionLimitExceededMessage), toSqlType(VARCHAR)))), toSqlType(BOOLEAN)), TRUE_LITERAL);
FilterNode filterNode = new FilterNode(idAllocator.getNextId(), windowNode, predicate);
recursionSteps.add(new NodeAndMappings(filterNode, checkConvergenceStep.getFields()));
// union all the recursion steps
List<NodeAndMappings> recursionStepsToUnion = recursionSteps.build();
List<Symbol> unionOutputSymbols = anchorPlan.getFieldMappings().stream().map(symbol -> symbolAllocator.newSymbol(symbol, "_expanded")).collect(toImmutableList());
ImmutableListMultimap.Builder<Symbol, Symbol> unionSymbolMapping = ImmutableListMultimap.builder();
for (NodeAndMappings plan : recursionStepsToUnion) {
for (int i = 0; i < unionOutputSymbols.size(); i++) {
unionSymbolMapping.put(unionOutputSymbols.get(i), plan.getFields().get(i));
}
}
List<PlanNode> nodesToUnion = recursionStepsToUnion.stream().map(NodeAndMappings::getNode).collect(toImmutableList());
PlanNode result = new UnionNode(idAllocator.getNextId(), nodesToUnion, unionSymbolMapping.build(), unionOutputSymbols);
if (union.isDistinct()) {
result = new AggregationNode(idAllocator.getNextId(), result, ImmutableMap.of(), singleGroupingSet(result.getOutputSymbols()), ImmutableList.of(), AggregationNode.Step.SINGLE, Optional.empty(), Optional.empty());
}
return new RelationPlan(result, anchorPlan.getScope(), unionOutputSymbols, outerContext);
}
Aggregations