@Override public PhysicalOperation visitProject(ProjectNode node, LocalExecutionPlanContext context) { PlanNode sourceNode; Expression filterExpression; if (node.getSource() instanceof FilterNode) { FilterNode filterNode = (FilterNode) node.getSource(); sourceNode = filterNode.getSource(); filterExpression = filterNode.getPredicate(); } else { sourceNode = node.getSource(); filterExpression = BooleanLiteral.TRUE_LITERAL; } List<Symbol> outputSymbols = node.getOutputSymbols(); return visitScanFilterAndProject(context, node.getId(), sourceNode, filterExpression, node.getAssignments(), outputSymbols); }
private PlanNode rewriteFilterSource(FilterNode filterNode, PlanNode source, Symbol rowNumberSymbol, int upperBound) { ExtractionResult extractionResult = fromPredicate(metadata, session, filterNode.getPredicate(), types); TupleDomain<Symbol> tupleDomain = extractionResult.getTupleDomain(); if (!isEqualRange(tupleDomain, rowNumberSymbol, upperBound)) { return new FilterNode(filterNode.getId(), source, filterNode.getPredicate()); } // Remove the row number domain because it is absorbed into the node Map<Symbol, Domain> newDomains = tupleDomain.getDomains().get().entrySet().stream() .filter(entry -> !entry.getKey().equals(rowNumberSymbol)) .collect(toMap(Map.Entry::getKey, Map.Entry::getValue)); // Construct a new predicate TupleDomain<Symbol> newTupleDomain = TupleDomain.withColumnDomains(newDomains); Expression newPredicate = ExpressionUtils.combineConjuncts( extractionResult.getRemainingExpression(), toPredicate(newTupleDomain)); if (newPredicate.equals(BooleanLiteral.TRUE_LITERAL)) { return source; } return new FilterNode(filterNode.getId(), source, newPredicate); }
/** * Extracts the literal value from an expression (if expression is supported) * @param expression * @param state * @return a Long, Boolean, Double or String object */ private Object getLiteralValue(Expression expression, QueryState state){ if(expression instanceof LongLiteral) return ((LongLiteral)expression).getValue(); else if(expression instanceof BooleanLiteral) return ((BooleanLiteral)expression).getValue(); else if(expression instanceof DoubleLiteral) return ((DoubleLiteral)expression).getValue(); else if(expression instanceof StringLiteral) return ((StringLiteral)expression).getValue(); else if(expression instanceof ArithmeticUnaryExpression){ ArithmeticUnaryExpression unaryExp = (ArithmeticUnaryExpression)expression; Sign sign = unaryExp.getSign(); Number num = (Number)getLiteralValue(unaryExp.getValue(), state); if(sign == Sign.MINUS){ if(num instanceof Long) return -1*num.longValue(); else if(num instanceof Double) return -1*num.doubleValue(); else { state.addException("Unsupported numeric literal expression encountered : "+num.getClass()); return null; } } return num; } else if(expression instanceof FunctionCall){ FunctionCall fc = (FunctionCall)expression; if(fc.getName().toString().equals("now")) return new Date(); else state.addException("Function '"+fc.getName()+"' is not supported"); }else if(expression instanceof CurrentTime){ CurrentTime ct = (CurrentTime)expression; if(ct.getType() == CurrentTime.Type.DATE) return new LocalDate().toDate(); else if(ct.getType() == CurrentTime.Type.TIME) return new Date(new LocalTime(DateTimeZone.UTC).getMillisOfDay()); else if(ct.getType() == CurrentTime.Type.TIMESTAMP) return new Date(); else if(ct.getType() == CurrentTime.Type.LOCALTIME) return new Date(new LocalTime(DateTimeZone.UTC).getMillisOfDay()); else if(ct.getType() == CurrentTime.Type.LOCALTIMESTAMP) return new Date(); else state.addException("CurrentTime function '"+ct.getType()+"' is not supported"); }else state.addException("Literal type "+expression.getClass().getSimpleName()+" is not supported"); return null; }
private Object getObject(Literal literal){ Object value = null; if(literal instanceof LongLiteral) value = ((LongLiteral)literal).getValue(); else if(literal instanceof BooleanLiteral) value = ((BooleanLiteral)literal).getValue(); else if(literal instanceof DoubleLiteral) value = ((DoubleLiteral)literal).getValue(); else if(literal instanceof StringLiteral) value = ((StringLiteral)literal).getValue(); else if(literal instanceof TimeLiteral) value = ((TimeLiteral)literal).getValue(); else if(literal instanceof TimestampLiteral) value = ((TimestampLiteral)literal).getValue(); return value; }
private Object getLiteralValue(Expression expression) throws SQLException{ if(expression instanceof LongLiteral) return ((LongLiteral)expression).getValue(); else if(expression instanceof BooleanLiteral) return ((BooleanLiteral)expression).getValue(); else if(expression instanceof DoubleLiteral) return ((DoubleLiteral)expression).getValue(); else if(expression instanceof StringLiteral) return ((StringLiteral)expression).getValue(); throw new SQLException("Unsupported literal type: "+expression); }
@Override public PlanNode visitFilter(FilterNode node, RewriteContext<Void> context) { PlanNode source = context.rewrite(node.getSource()); Expression canonicalized = canonicalizeExpression(node.getPredicate()); if (canonicalized.equals(BooleanLiteral.TRUE_LITERAL)) { return source; } return new FilterNode(node.getId(), source, canonicalized); }
@Override public PlanNode optimize(PlanNode plan, Session session, Map<Symbol, Type> types, SymbolAllocator symbolAllocator, PlanNodeIdAllocator idAllocator) { requireNonNull(plan, "plan is null"); requireNonNull(session, "session is null"); requireNonNull(types, "types is null"); requireNonNull(idAllocator, "idAllocator is null"); return SimplePlanRewriter.rewriteWith(new Rewriter(symbolAllocator, idAllocator, metadata, sqlParser, session), plan, BooleanLiteral.TRUE_LITERAL); }
@Override public PlanNode visitPlan(PlanNode node, RewriteContext<Expression> context) { PlanNode rewrittenNode = context.defaultRewrite(node, BooleanLiteral.TRUE_LITERAL); if (!context.get().equals(BooleanLiteral.TRUE_LITERAL)) { // Drop in a FilterNode b/c we cannot push our predicate down any further rewrittenNode = new FilterNode(idAllocator.getNextId(), rewrittenNode, context.get()); } return rewrittenNode; }
@Override public PlanNode visitTableScan(TableScanNode node, RewriteContext<Expression> context) { Expression predicate = simplifyExpression(context.get()); if (!BooleanLiteral.TRUE_LITERAL.equals(predicate)) { return new FilterNode(idAllocator.getNextId(), node, predicate); } return node; }
@Override public PlanNode visitTableScan(TableScanNode node, RewriteContext<Void> context) { if (node.getLayout().isPresent()) { return node; } return planTableScan(node, BooleanLiteral.TRUE_LITERAL); }
@Test public void testJoinPrecedence() { assertStatement("SELECT * FROM a CROSS JOIN b LEFT JOIN c ON true", simpleQuery( selectList(new AllColumns()), new Join( Join.Type.LEFT, new Join( Join.Type.CROSS, new Table(QualifiedName.of("a")), new Table(QualifiedName.of("b")), Optional.empty() ), new Table(QualifiedName.of("c")), Optional.of(new JoinOn(BooleanLiteral.TRUE_LITERAL))))); assertStatement("SELECT * FROM a CROSS JOIN b NATURAL JOIN c CROSS JOIN d NATURAL JOIN e", simpleQuery( selectList(new AllColumns()), new Join( Join.Type.INNER, new Join( Join.Type.CROSS, new Join( Join.Type.INNER, new Join( Join.Type.CROSS, new Table(QualifiedName.of("a")), new Table(QualifiedName.of("b")), Optional.empty() ), new Table(QualifiedName.of("c")), Optional.of(new NaturalJoin())), new Table(QualifiedName.of("d")), Optional.empty() ), new Table(QualifiedName.of("e")), Optional.of(new NaturalJoin())))); }
@Override protected String visitBooleanLiteral(BooleanLiteral node, Void context) { return String.valueOf(node.getValue()); }
@Override protected String visitBooleanLiteral(BooleanLiteral node, StackableAstVisitorContext<Integer> indent) { return String.valueOf(node.getValue()); }
@Override protected IComparison visitExpression(Expression node, QueryState state) { if( node instanceof LogicalBinaryExpression){ LogicalBinaryExpression boolExp = (LogicalBinaryExpression)node; IComparison left = boolExp.getLeft().accept(this, state); IComparison right = boolExp.getRight().accept(this, state); return new BooleanComparison(left, right, boolExp.getType() == Type.AND); }else if( node instanceof ComparisonExpression){ ComparisonExpression compareExp = (ComparisonExpression)node; Column column = new SelectParser().visitExpression(compareExp.getLeft(), state); Column leftCol = state.getHeading().getColumnByLabel(column.getLabel()); if(leftCol == null){ state.addException("Having reference "+column+" not found in SELECT clause"); return null; } // right hand side is a concrete literal to compare with if(compareExp.getRight() instanceof Literal){ Object value; if(compareExp.getRight() instanceof LongLiteral) value = ((LongLiteral)compareExp.getRight()).getValue(); else if(compareExp.getRight() instanceof BooleanLiteral) value = ((BooleanLiteral)compareExp.getRight()).getValue(); else if(compareExp.getRight() instanceof DoubleLiteral) value = ((DoubleLiteral)compareExp.getRight()).getValue(); else if(compareExp.getRight() instanceof StringLiteral) value = ((StringLiteral)compareExp.getRight()).getValue(); else { state.addException("Unable to get value from "+compareExp.getRight()); return null; } return new SimpleComparison(leftCol, compareExp.getType(), (Number)value); // right hand side refers to another column } else if(compareExp.getRight() instanceof DereferenceExpression || compareExp.getRight() instanceof QualifiedNameReference){ String col2; if(compareExp.getLeft() instanceof DereferenceExpression){ // parse columns like 'reference.field' col2 = SelectParser.visitDereferenceExpression((DereferenceExpression)compareExp.getRight()); }else{ col2 = ((QualifiedNameReference)compareExp.getRight()).getName().toString(); } col2 = Heading.findOriginal(state.originalSql(), col2, "having.+", "\\W"); Column rightCol = state.getHeading().getColumnByLabel(col2); if(rightCol == null){ state.addException("column "+col2+" not found in SELECT clause"); return null; } return new SimpleComparison(leftCol, compareExp.getType(), rightCol); }else { // unknown right hand side so state.addException("Unable to get value from "+compareExp.getRight()); return null; } }else if( node instanceof NotExpression){ state.addException("NOT is currently not supported, use '<>' instead"); }else{ state.addException("Unable to parse "+node+" ("+node.getClass().getName()+") is not a supported expression"); } return null; }
@Override protected RowExpression visitBooleanLiteral(BooleanLiteral node, Void context) { return constant(node.getValue(), BOOLEAN); }
@Override protected Object visitBooleanLiteral(BooleanLiteral node, ConnectorSession session) { return node.getValue(); }
@Override protected Object visitBooleanLiteral(BooleanLiteral node, Object context) { return node.equals(BooleanLiteral.TRUE_LITERAL); }
@Override public PlanWithProperties visitTableScan(TableScanNode node, Context context) { return planTableScan(node, BooleanLiteral.TRUE_LITERAL, context); }
private PlanWithProperties planTableScan(TableScanNode node, Expression predicate, Context context) { // don't include non-deterministic predicates Expression deterministicPredicate = stripNonDeterministicConjuncts(predicate); DomainTranslator.ExtractionResult decomposedPredicate = DomainTranslator.fromPredicate( metadata, session, deterministicPredicate, symbolAllocator.getTypes()); TupleDomain<ColumnHandle> simplifiedConstraint = decomposedPredicate.getTupleDomain() .transform(node.getAssignments()::get) .intersect(node.getCurrentConstraint()); Map<ColumnHandle, Symbol> assignments = ImmutableBiMap.copyOf(node.getAssignments()).inverse(); Expression constraint = combineConjuncts( deterministicPredicate, DomainTranslator.toPredicate(node.getCurrentConstraint().transform(assignments::get))); // Layouts will be returned in order of the connector's preference List<TableLayoutResult> layouts = metadata.getLayouts( session, node.getTable(), new Constraint<>(simplifiedConstraint, bindings -> !shouldPrune(constraint, node.getAssignments(), bindings)), Optional.of(node.getOutputSymbols().stream() .map(node.getAssignments()::get) .collect(toImmutableSet()))); if (layouts.isEmpty()) { return new PlanWithProperties( new ValuesNode(idAllocator.getNextId(), node.getOutputSymbols(), ImmutableList.of()), ActualProperties.undistributed()); } // Filter out layouts that cannot supply all the required columns layouts = layouts.stream() .filter(layoutHasAllNeededOutputs(node)) .collect(toList()); checkState(!layouts.isEmpty(), "No usable layouts for %s", node); List<PlanWithProperties> possiblePlans = layouts.stream() .map(layout -> { TableScanNode tableScan = new TableScanNode( node.getId(), node.getTable(), node.getOutputSymbols(), node.getAssignments(), Optional.of(layout.getLayout().getHandle()), simplifiedConstraint.intersect(layout.getLayout().getPredicate()), Optional.ofNullable(node.getOriginalConstraint()).orElse(predicate)); PlanWithProperties result = new PlanWithProperties(tableScan, deriveProperties(tableScan, ImmutableList.of())); Expression resultingPredicate = combineConjuncts( DomainTranslator.toPredicate(layout.getUnenforcedConstraint().transform(assignments::get)), stripDeterministicConjuncts(predicate), decomposedPredicate.getRemainingExpression()); if (!BooleanLiteral.TRUE_LITERAL.equals(resultingPredicate)) { return withDerivedProperties( new FilterNode(idAllocator.getNextId(), result.getNode(), resultingPredicate), deriveProperties(tableScan, ImmutableList.of())); } return result; }) .collect(toList()); return pickPlan(possiblePlans, context); }
private PlanNode planTableScan(TableScanNode node, Expression predicate) { DomainTranslator.ExtractionResult decomposedPredicate = DomainTranslator.fromPredicate( metadata, session, predicate, symbolAllocator.getTypes()); TupleDomain<ColumnHandle> simplifiedConstraint = decomposedPredicate.getTupleDomain() .transform(node.getAssignments()::get) .intersect(node.getCurrentConstraint()); List<TableLayoutResult> layouts = metadata.getLayouts( session, node.getTable(), new Constraint<>(simplifiedConstraint, bindings -> true), Optional.of(ImmutableSet.copyOf(node.getAssignments().values()))); if (layouts.isEmpty()) { return new ValuesNode(idAllocator.getNextId(), node.getOutputSymbols(), ImmutableList.of()); } TableLayoutResult layout = layouts.get(0); TableScanNode result = new TableScanNode( node.getId(), node.getTable(), node.getOutputSymbols(), node.getAssignments(), Optional.of(layout.getLayout().getHandle()), simplifiedConstraint.intersect(layout.getLayout().getPredicate()), Optional.ofNullable(node.getOriginalConstraint()).orElse(predicate)); Map<ColumnHandle, Symbol> assignments = ImmutableBiMap.copyOf(node.getAssignments()).inverse(); Expression resultingPredicate = combineConjuncts( decomposedPredicate.getRemainingExpression(), DomainTranslator.toPredicate(layout.getUnenforcedConstraint().transform(assignments::get))); if (!BooleanLiteral.TRUE_LITERAL.equals(resultingPredicate)) { return new FilterNode(idAllocator.getNextId(), result, resultingPredicate); } return result; }
@Override public PlanNode visitTableScan(TableScanNode node, RewriteContext<Context> context) { return planTableScan(node, BooleanLiteral.TRUE_LITERAL, context.get()); }
@Override protected ExtractionResult visitBooleanLiteral(BooleanLiteral node, Boolean complement) { boolean value = complement ? !node.getValue() : node.getValue(); return new ExtractionResult(value ? TupleDomain.all() : TupleDomain.none(), TRUE_LITERAL); }
@Override protected Type visitBooleanLiteral(BooleanLiteral node, StackableAstVisitorContext<AnalysisContext> context) { expressionTypes.put(node, BOOLEAN); return BOOLEAN; }
@Test public void testTableScan() throws Exception { // Effective predicate is True if there is no effective predicate Map<Symbol, ColumnHandle> assignments = Maps.filterKeys(scanAssignments, Predicates.in(ImmutableList.of(A, B, C, D))); PlanNode node = new TableScanNode( newId(), DUAL_TABLE_HANDLE, ImmutableList.copyOf(assignments.keySet()), assignments, Optional.empty(), TupleDomain.all(), null); Expression effectivePredicate = EffectivePredicateExtractor.extract(node, TYPES); Assert.assertEquals(effectivePredicate, BooleanLiteral.TRUE_LITERAL); node = new TableScanNode( newId(), DUAL_TABLE_HANDLE, ImmutableList.copyOf(assignments.keySet()), assignments, Optional.empty(), TupleDomain.none(), null); effectivePredicate = EffectivePredicateExtractor.extract(node, TYPES); Assert.assertEquals(effectivePredicate, FALSE_LITERAL); node = new TableScanNode( newId(), DUAL_TABLE_HANDLE, ImmutableList.copyOf(assignments.keySet()), assignments, Optional.empty(), TupleDomain.withColumnDomains(ImmutableMap.of(scanAssignments.get(A), Domain.singleValue(BIGINT, 1L))), null); effectivePredicate = EffectivePredicateExtractor.extract(node, TYPES); Assert.assertEquals(normalizeConjuncts(effectivePredicate), normalizeConjuncts(equals(number(1L), AE))); node = new TableScanNode( newId(), DUAL_TABLE_HANDLE, ImmutableList.copyOf(assignments.keySet()), assignments, Optional.empty(), TupleDomain.withColumnDomains(ImmutableMap.of( scanAssignments.get(A), Domain.singleValue(BIGINT, 1L), scanAssignments.get(B), Domain.singleValue(BIGINT, 2L))), null); effectivePredicate = EffectivePredicateExtractor.extract(node, TYPES); Assert.assertEquals(normalizeConjuncts(effectivePredicate), normalizeConjuncts(equals(number(2L), BE), equals(number(1L), AE))); node = new TableScanNode( newId(), DUAL_TABLE_HANDLE, ImmutableList.copyOf(assignments.keySet()), assignments, Optional.empty(), TupleDomain.all(), null); effectivePredicate = EffectivePredicateExtractor.extract(node, TYPES); Assert.assertEquals(effectivePredicate, BooleanLiteral.TRUE_LITERAL); }
@Override public Node visitBooleanValue(SqlBaseParser.BooleanValueContext context) { return new BooleanLiteral(getLocation(context), context.getText()); }
@Override protected String visitBooleanLiteral(BooleanLiteral node, Boolean unmangleNames) { return String.valueOf(node.getValue()); }