private PlanBuilder appendScalarSubqueryJoin(PlanBuilder builder, SubqueryExpression scalarSubquery) { EnforceSingleRowNode enforceSingleRowNode = new EnforceSingleRowNode(idAllocator.getNextId(), createRelationPlan(scalarSubquery).getRoot()); TranslationMap translations = copyTranslations(builder); translations.put(scalarSubquery, getOnlyElement(enforceSingleRowNode.getOutputSymbols())); // Cross join current (root) relation with subquery PlanNode root = builder.getRoot(); if (root.getOutputSymbols().isEmpty()) { // there is nothing to join with - e.g. SELECT (SELECT 1) return new PlanBuilder(translations, enforceSingleRowNode, builder.getSampleWeight()); } else { return new PlanBuilder(translations, new JoinNode(idAllocator.getNextId(), JoinNode.Type.FULL, root, enforceSingleRowNode, ImmutableList.of(), Optional.empty(), Optional.empty()), builder.getSampleWeight()); } }
@Override protected Type visitInPredicate(InPredicate node, StackableAstVisitorContext<AnalysisContext> context) { Expression value = node.getValue(); process(value, context); Expression valueList = node.getValueList(); process(valueList, context); if (valueList instanceof InListExpression) { InListExpression inListExpression = (InListExpression) valueList; coerceToSingleType(context, "IN value and list items must be the same type: %s", ImmutableList.<Expression>builder().add(value).addAll(inListExpression.getValues()).build()); } else if (valueList instanceof SubqueryExpression) { coerceToSingleType(context, node, "value and result of subquery must be of the same type for IN expression: %s vs %s", value, valueList); } expressionTypes.put(node, BOOLEAN); return BOOLEAN; }
@Override protected Type visitSubqueryExpression(SubqueryExpression node, StackableAstVisitorContext<AnalysisContext> context) { StatementAnalyzer analyzer = statementAnalyzerFactory.apply(node); RelationType descriptor = analyzer.process(node.getQuery(), context.getContext()); // Subquery should only produce one column if (descriptor.getVisibleFieldCount() != 1) { throw new SemanticException(MULTIPLE_FIELDS_FROM_SUBQUERY, node, "Multiple columns returned by subquery are not yet supported. Found %s", descriptor.getVisibleFieldCount()); } Optional<Node> previousNode = context.getPreviousNode(); if (previousNode.isPresent() && previousNode.get() instanceof InPredicate && ((InPredicate) previousNode.get()).getValue() != node) { subqueryInPredicates.add((InPredicate) previousNode.get()); } else { scalarSubqueries.add(node); } Type type = Iterables.getOnlyElement(descriptor.getVisibleFields()).getType(); expressionTypes.put(node, type); return type; }
/** * Semijoins are planned as follows: * 1) SQL constructs that need to be semijoined are extracted during Analysis phase (currently only InPredicates so far) * 2) Create a new SemiJoinNode that connects the semijoin lookup field with the planned subquery and have it output a new boolean * symbol for the result of the semijoin. * 3) Add an entry to the TranslationMap that notes to map the InPredicate into semijoin output symbol * <p> * Currently, we only support semijoins deriving from InPredicates, but we will probably need * to add support for more SQL constructs in the future. */ private PlanBuilder appendSemiJoin(PlanBuilder subPlan, InPredicate inPredicate) { TranslationMap translations = copyTranslations(subPlan); subPlan = appendProjections(subPlan, ImmutableList.of(inPredicate.getValue())); Symbol sourceJoinSymbol = subPlan.translate(inPredicate.getValue()); checkState(inPredicate.getValueList() instanceof SubqueryExpression); SubqueryExpression subqueryExpression = (SubqueryExpression) inPredicate.getValueList(); RelationPlanner relationPlanner = new RelationPlanner(analysis, symbolAllocator, idAllocator, metadata, session); RelationPlan valueListRelation = relationPlanner.process(subqueryExpression.getQuery(), null); Symbol filteringSourceJoinSymbol = getOnlyElement(valueListRelation.getRoot().getOutputSymbols()); Symbol semiJoinOutputSymbol = symbolAllocator.newSymbol("semijoinresult", BOOLEAN); translations.put(inPredicate, semiJoinOutputSymbol); return new PlanBuilder(translations, new SemiJoinNode(idAllocator.getNextId(), subPlan.getRoot(), valueListRelation.getRoot(), sourceJoinSymbol, filteringSourceJoinSymbol, semiJoinOutputSymbol, Optional.empty(), Optional.empty()), subPlan.getSampleWeight()); }
private PlanBuilder appendScalarSubqueryJoins(PlanBuilder builder, Set<SubqueryExpression> scalarSubqueries) { for (SubqueryExpression scalarSubquery : scalarSubqueries) { builder = appendScalarSubqueryJoin(builder, scalarSubquery); } return builder; }
private PlanBuilder appendSemiJoin(PlanBuilder subPlan, InPredicate inPredicate) { TranslationMap translations = new TranslationMap(subPlan.getRelationPlan(), analysis); translations.copyMappingsFrom(subPlan.getTranslations()); subPlan = appendProjections(subPlan, ImmutableList.of(inPredicate.getValue())); Symbol sourceJoinSymbol = subPlan.translate(inPredicate.getValue()); checkState(inPredicate.getValueList() instanceof SubqueryExpression); SubqueryExpression subqueryExpression = (SubqueryExpression) inPredicate.getValueList(); RelationPlanner relationPlanner = new RelationPlanner(analysis, symbolAllocator, idAllocator, metadata, session); RelationPlan valueListRelation = relationPlanner.process(subqueryExpression.getQuery(), null); Symbol filteringSourceJoinSymbol = Iterables.getOnlyElement(valueListRelation.getRoot().getOutputSymbols()); Symbol semiJoinOutputSymbol = symbolAllocator.newSymbol("semijoinresult", BOOLEAN); translations.put(inPredicate, semiJoinOutputSymbol); return new PlanBuilder(translations, new SemiJoinNode(idAllocator.getNextId(), subPlan.getRoot(), valueListRelation.getRoot(), sourceJoinSymbol, filteringSourceJoinSymbol, semiJoinOutputSymbol, Optional.empty(), Optional.empty()), subPlan.getSampleWeight()); }
public ExpressionAnalysis( IdentityHashMap<Expression, Type> expressionTypes, IdentityHashMap<Expression, Type> expressionCoercions, Set<InPredicate> subqueryInPredicates, Set<SubqueryExpression> scalarSubqueries, Set<Expression> columnReferences) { this.expressionTypes = requireNonNull(expressionTypes, "expressionTypes is null"); this.expressionCoercions = requireNonNull(expressionCoercions, "expressionCoercions is null"); this.subqueryInPredicates = requireNonNull(subqueryInPredicates, "subqueryInPredicates is null"); this.scalarSubqueries = requireNonNull(scalarSubqueries, "subqueryInPredicates is null"); this.columnReferences = ImmutableSet.copyOf(requireNonNull(columnReferences, "columnReferences is null")); }
@Override protected String visitSubqueryExpression(SubqueryExpression node, Void context) { return "(" + sqlFormatter.formatSql(node.getQuery()) + ")"; }
@Override protected String visitSubqueryExpression(SubqueryExpression node, StackableAstVisitorContext<Integer> indent) { return "(\n" + formatSql(node.getQuery(), parameters, indent.getContext() + 1) + indentString(indent.getContext()) + ')'; }
private RelationPlan createRelationPlan(SubqueryExpression subqueryExpression) { return new RelationPlanner(analysis, symbolAllocator, idAllocator, metadata, session) .process(subqueryExpression.getQuery(), null); }
public Set<SubqueryExpression> getScalarSubqueries() { return scalarSubqueries; }
@Override protected Boolean visitSubqueryExpression(SubqueryExpression node, Void context) { throw new SemanticException(NOT_SUPPORTED, node, "Scalar subqueries not yet supported"); }
public Set<SubqueryExpression> getScalarSubqueries(Node node) { return scalarSubqueries.get(node); }
@Override public Node visitSubqueryExpression(SqlBaseParser.SubqueryExpressionContext context) { return new SubqueryExpression(getLocation(context), (Query) visit(context.query())); }
@Override protected String visitSubqueryExpression(SubqueryExpression node, Boolean unmangleNames) { return "(" + formatSql(node.getQuery(), unmangleNames) + ")"; }