public static ComparisonExpression.Type flipComparison(ComparisonExpression.Type type) { switch (type) { case EQUAL: return EQUAL; case NOT_EQUAL: return NOT_EQUAL; case LESS_THAN: return GREATER_THAN; case LESS_THAN_OR_EQUAL: return GREATER_THAN_OR_EQUAL; case GREATER_THAN: return LESS_THAN; case GREATER_THAN_OR_EQUAL: return LESS_THAN_OR_EQUAL; case IS_DISTINCT_FROM: return IS_DISTINCT_FROM; default: throw new IllegalArgumentException("Unsupported comparison: " + type); } }
private static ComparisonExpression.Type negate(ComparisonExpression.Type type) { switch (type) { case EQUAL: return NOT_EQUAL; case NOT_EQUAL: return EQUAL; case LESS_THAN: return GREATER_THAN_OR_EQUAL; case LESS_THAN_OR_EQUAL: return GREATER_THAN; case GREATER_THAN: return LESS_THAN_OR_EQUAL; case GREATER_THAN_OR_EQUAL: return LESS_THAN; default: throw new IllegalArgumentException("Unsupported comparison: " + type); } }
@Override public Void visitJoin(JoinNode node, Integer indent) { List<Expression> joinExpressions = new ArrayList<>(); for (JoinNode.EquiJoinClause clause : node.getCriteria()) { joinExpressions.add(new ComparisonExpression(ComparisonExpression.Type.EQUAL, new QualifiedNameReference(clause.getLeft().toQualifiedName()), new QualifiedNameReference(clause.getRight().toQualifiedName()))); } print(indent, "- %s[%s] => [%s]", node.getType().getJoinLabel(), Joiner.on(" AND ").join(joinExpressions), formatOutputs(node.getOutputSymbols())); node.getLeft().accept(this, indent + 1); node.getRight().accept(this, indent + 1); return null; }
@Override public Void visitIndexJoin(IndexJoinNode node, Integer indent) { List<Expression> joinExpressions = new ArrayList<>(); for (IndexJoinNode.EquiJoinClause clause : node.getCriteria()) { joinExpressions.add(new ComparisonExpression(ComparisonExpression.Type.EQUAL, new QualifiedNameReference(clause.getProbe().toQualifiedName()), new QualifiedNameReference(clause.getIndex().toQualifiedName()))); } print(indent, "- %sIndexJoin[%s] => [%s]", node.getType().getJoinLabel(), Joiner.on(" AND ").join(joinExpressions), formatOutputs(node.getOutputSymbols())); node.getProbeSource().accept(this, indent + 1); node.getIndexSource().accept(this, indent + 1); return null; }
private static Predicate<Expression> joinEqualityExpression(final Collection<Symbol> leftSymbols) { return expression -> { // At this point in time, our join predicates need to be deterministic if (isDeterministic(expression) && expression instanceof ComparisonExpression) { ComparisonExpression comparison = (ComparisonExpression) expression; if (comparison.getType() == ComparisonExpression.Type.EQUAL) { Set<Symbol> symbols1 = DependencyExtractor.extractUnique(comparison.getLeft()); Set<Symbol> symbols2 = DependencyExtractor.extractUnique(comparison.getRight()); return (Iterables.all(symbols1, in(leftSymbols)) && Iterables.all(symbols2, not(in(leftSymbols)))) || (Iterables.all(symbols2, in(leftSymbols)) && Iterables.all(symbols1, not(in(leftSymbols)))); } } return false; }; }
@Override public PlanNode visitSample(SampleNode node, RewriteContext<Void> context) { if (node.getSampleType() == SampleNode.Type.BERNOULLI) { PlanNode rewrittenSource = context.rewrite(node.getSource()); ComparisonExpression expression = new ComparisonExpression( ComparisonExpression.Type.LESS_THAN, new FunctionCall(QualifiedName.of("rand"), ImmutableList.<Expression>of()), new DoubleLiteral(Double.toString(node.getSampleRatio()))); return new FilterNode(node.getId(), rewrittenSource, expression); } else if (node.getSampleType() == SampleNode.Type.POISSONIZED || node.getSampleType() == SampleNode.Type.SYSTEM) { return context.defaultRewrite(node); } throw new UnsupportedOperationException("not yet implemented"); }
@Override protected ExtractionResult visitComparisonExpression(ComparisonExpression node, Boolean complement) { Optional<NormalizedSimpleComparison> optionalNormalized = toNormalizedSimpleComparison(session, metadata, types, node); if (!optionalNormalized.isPresent()) { return super.visitComparisonExpression(node, complement); } NormalizedSimpleComparison normalized = optionalNormalized.get(); Symbol symbol = Symbol.fromQualifiedName(normalized.getNameReference().getName()); Type type = checkedTypeLookup(symbol); NullableValue value = normalized.getValue(); // Handle the cases where implicit coercions can happen in comparisons // TODO: how to abstract this out if (value.getType().equals(DOUBLE) && type.equals(BIGINT)) { return process(coerceDoubleToLongComparison(normalized), complement); } if (value.getType().equals(BIGINT) && type.equals(DOUBLE)) { value = NullableValue.of(DOUBLE, ((Long) value.getValue()).doubleValue()); } checkState(value.isNull() || value.getType().equals(type), "INVARIANT: comparison should be working on the same types"); return createComparisonExtractionResult(normalized.getComparisonType(), symbol, type, value.getValue(), complement); }
private static Domain extractOrderableDomain(ComparisonExpression.Type comparisonType, Type type, Object value, boolean complement) { checkArgument(value != null); switch (comparisonType) { case EQUAL: return Domain.create(complementIfNecessary(ValueSet.ofRanges(Range.equal(type, value)), complement), false); case GREATER_THAN: return Domain.create(complementIfNecessary(ValueSet.ofRanges(Range.greaterThan(type, value)), complement), false); case GREATER_THAN_OR_EQUAL: return Domain.create(complementIfNecessary(ValueSet.ofRanges(Range.greaterThanOrEqual(type, value)), complement), false); case LESS_THAN: return Domain.create(complementIfNecessary(ValueSet.ofRanges(Range.lessThan(type, value)), complement), false); case LESS_THAN_OR_EQUAL: return Domain.create(complementIfNecessary(ValueSet.ofRanges(Range.lessThanOrEqual(type, value)), complement), false); case NOT_EQUAL: return Domain.create(complementIfNecessary(ValueSet.ofRanges(Range.lessThan(type, value), Range.greaterThan(type, value)), complement), false); case IS_DISTINCT_FROM: // Need to potential complement the whole domain for IS_DISTINCT_FROM since it is null-aware return complementIfNecessary(Domain.create(ValueSet.ofRanges(Range.lessThan(type, value), Range.greaterThan(type, value)), true), complement); default: throw new AssertionError("Unhandled type: " + comparisonType); } }
@Override protected ExtractionResult visitInPredicate(InPredicate node, Boolean complement) { if (!(node.getValue() instanceof QualifiedNameReference) || !(node.getValueList() instanceof InListExpression)) { return super.visitInPredicate(node, complement); } InListExpression valueList = (InListExpression) node.getValueList(); checkState(!valueList.getValues().isEmpty(), "InListExpression should never be empty"); ImmutableList.Builder<Expression> disjuncts = ImmutableList.builder(); for (Expression expression : valueList.getValues()) { disjuncts.add(new ComparisonExpression(EQUAL, node.getValue(), expression)); } return process(or(disjuncts.build()), complement); }
@Override public Void visitJoin(JoinNode node, Void context) { List<Expression> joinExpressions = new ArrayList<>(); for (JoinNode.EquiJoinClause clause : node.getCriteria()) { joinExpressions.add(new ComparisonExpression(ComparisonExpression.Type.EQUAL, new QualifiedNameReference(clause.getLeft().toQualifiedName()), new QualifiedNameReference(clause.getRight().toQualifiedName()))); } String criteria = Joiner.on(" AND ").join(joinExpressions); printNode(node, node.getType().getJoinLabel(), criteria, NODE_COLORS.get(NodeType.JOIN)); node.getLeft().accept(this, context); node.getRight().accept(this, context); return null; }
@Override public Void visitIndexJoin(IndexJoinNode node, Void context) { List<Expression> joinExpressions = new ArrayList<>(); for (IndexJoinNode.EquiJoinClause clause : node.getCriteria()) { joinExpressions.add(new ComparisonExpression(ComparisonExpression.Type.EQUAL, new QualifiedNameReference(clause.getProbe().toQualifiedName()), new QualifiedNameReference(clause.getIndex().toQualifiedName()))); } String criteria = Joiner.on(" AND ").join(joinExpressions); String joinLabel = format("%sIndexJoin", node.getType().getJoinLabel()); printNode(node, joinLabel, criteria, NODE_COLORS.get(NodeType.JOIN)); node.getProbeSource().accept(this, context); node.getIndexSource().accept(this, context); return null; }
@Test public void testComparisonExpressionWithNulls() { for (ComparisonExpression.Type type : ComparisonExpression.Type.values()) { if (type == ComparisonExpression.Type.IS_DISTINCT_FROM) { // IS DISTINCT FROM has different NULL semantics continue; } assertFilter(format("NULL %s NULL", type.getValue()), false); assertFilter(format("42 %s NULL", type.getValue()), false); assertFilter(format("NULL %s 42", type.getValue()), false); assertFilter(format("11.1 %s NULL", type.getValue()), false); assertFilter(format("NULL %s 11.1", type.getValue()), false); } }
private static ComparisonExpression.Type getComparisonOperator(Token symbol) { switch (symbol.getType()) { case SqlBaseLexer.EQ: return ComparisonExpression.Type.EQUAL; case SqlBaseLexer.NEQ: return ComparisonExpression.Type.NOT_EQUAL; case SqlBaseLexer.LT: return ComparisonExpression.Type.LESS_THAN; case SqlBaseLexer.LTE: return ComparisonExpression.Type.LESS_THAN_OR_EQUAL; case SqlBaseLexer.GT: return ComparisonExpression.Type.GREATER_THAN; case SqlBaseLexer.GTE: return ComparisonExpression.Type.GREATER_THAN_OR_EQUAL; } throw new IllegalArgumentException("Unsupported operator: " + symbol.getText()); }
public static ComparisonExpression.Type getComparisonType(String expression) { if (equalsExpr.contains(expression)) { return ComparisonExpression.Type.EQUAL; } if (notEqualsExpr.contains(expression)) { return ComparisonExpression.Type.NOT_EQUAL; } if (greaterExpr.contains(expression)) { return ComparisonExpression.Type.GREATER_THAN; } if (lessExpr.contains(expression)) { return ComparisonExpression.Type.LESS_THAN; } if (greaterEqualExpr.contains(expression)) { return ComparisonExpression.Type.GREATER_THAN_OR_EQUAL; } if (lessEqualExpr.contains(expression)) { return ComparisonExpression.Type.LESS_THAN_OR_EQUAL; } return null; }
public static String comparisonTypeToEnglish(ComparisonExpression.Type type) { switch (type) { case EQUAL: return " equal to "; case NOT_EQUAL: return " not equal to "; case LESS_THAN: return " less than "; case GREATER_THAN: return " greater than "; case GREATER_THAN_OR_EQUAL: return " greater than or equal to "; case IS_DISTINCT_FROM: return " distinct from "; default: return null; } }
private Relation render(List<ForeignKey> keys) { if (keys.isEmpty()) { return QueryUtil.table(new QualifiedName(baseTable)); } ForeignKey key = keys.get(0); if (keys.size() == 1) { return new Join(Join.Type.INNER, QueryUtil.table(new QualifiedName(key.getSourceTable())), QueryUtil.table(new QualifiedName(key.getDestinationTable())), Optional.of(new JoinOn(new ComparisonExpression( ComparisonExpression.Type.EQUAL, new QualifiedNameReference(QualifiedName.of( key.getSourceTable(), key.getSourceColumn())), new QualifiedNameReference(QualifiedName.of( key.getDestinationTable(), key.getDestinationColumn())))))); } return new Join(Join.Type.INNER, render(keys.subList(1, keys.size())), QueryUtil.table(new QualifiedName(key.getDestinationTable())), Optional.of(new JoinOn(new ComparisonExpression( ComparisonExpression.Type.EQUAL, new QualifiedNameReference(QualifiedName.of( key.getSourceTable(), key.getSourceColumn())), new QualifiedNameReference(QualifiedName.of( key.getDestinationTable(), key.getDestinationColumn())))))); }
protected static String processFuncNullifzero(Formatter formatter, FunctionCall node) { Expression x = node.getArguments().get(0); List<WhenClause> listWhen = new ArrayList<WhenClause>(); ComparisonExpression ce = new ComparisonExpression(ComparisonExpression.Type.EQUAL, x, new LongLiteral("0")); WhenClause wc = new WhenClause(ce, new NullLiteral()); listWhen.add(wc); SearchedCaseExpression sce = new SearchedCaseExpression(listWhen, x); return formatter.process(sce, null); }
protected static Expression processFuncLast(ComparisonExpression node) { System.out.println("Processing last()"); Expression rightNode = node.getRight(); Expression leftNode = node.getLeft(); FunctionCall last = (FunctionCall) rightNode; // # of arguments are already checked outside 1 or 2 String number = last.getArguments().get(0).toString(); String format = "DAY"; // default if (last.getArguments().size() == 2) { format = last.getArguments().get(1).toString().replaceAll("\"", ""); } IntervalLiteral.Sign sign; if (number.startsWith("-")) { sign = IntervalLiteral.Sign.NEGATIVE; number = number.substring(1); } else { sign = IntervalLiteral.Sign.POSITIVE; } CurrentTime cTime = new CurrentTime(CurrentTime.Type.DATE); IntervalLiteral interval = new IntervalLiteral(number, sign, format); ArithmeticExpression arithmOp = new ArithmeticExpression(ArithmeticExpression.Type.SUBTRACT, cTime, interval); BetweenPredicate bPredicate = new BetweenPredicate(leftNode, arithmOp, cTime); return bPredicate; }
@Override protected RowExpression visitComparisonExpression(ComparisonExpression node, Void context) { RowExpression left = process(node.getLeft(), context); RowExpression right = process(node.getRight(), context); return call( comparisonExpressionSignature(node.getType(), left.getType(), right.getType()), BOOLEAN, left, right); }
public static Signature comparisonExpressionSignature(ComparisonExpression.Type expressionType, Type leftType, Type rightType) { for (OperatorType operatorType : OperatorType.values()) { if (operatorType.name().equals(expressionType.name())) { return internalOperator(expressionType.name(), parseTypeSignature(StandardTypes.BOOLEAN), leftType.getTypeSignature(), rightType.getTypeSignature()); } } return internalScalarFunction(expressionType.name(), parseTypeSignature(StandardTypes.BOOLEAN), leftType.getTypeSignature(), rightType.getTypeSignature()); }
public static Expression normalize(Expression expression) { if (expression instanceof NotExpression) { NotExpression not = (NotExpression) expression; if (not.getValue() instanceof ComparisonExpression) { ComparisonExpression comparison = (ComparisonExpression) not.getValue(); return new ComparisonExpression(negate(comparison.getType()), comparison.getLeft(), comparison.getRight()); } } return expression; }
/** * Determines whether an Expression may be successfully applied to the equality inference */ public static Predicate<Expression> isInferenceCandidate() { return expression -> { expression = normalizeInPredicateToEquality(expression); if (DeterminismEvaluator.isDeterministic(expression) && expression instanceof ComparisonExpression) { ComparisonExpression comparison = (ComparisonExpression) expression; if (comparison.getType() == ComparisonExpression.Type.EQUAL) { // We should only consider equalities that have distinct left and right components return !comparison.getLeft().equals(comparison.getRight()); } } return false; }; }
/** * Rewrite single value InPredicates as equality if possible */ private static Expression normalizeInPredicateToEquality(Expression expression) { if (expression instanceof InPredicate) { InPredicate inPredicate = (InPredicate) expression; if (inPredicate.getValueList() instanceof InListExpression) { InListExpression valueList = (InListExpression) inPredicate.getValueList(); if (valueList.getValues().size() == 1) { return new ComparisonExpression(ComparisonExpression.Type.EQUAL, inPredicate.getValue(), Iterables.getOnlyElement(valueList.getValues())); } } } return expression; }
public Builder addEquality(Expression expression) { expression = normalizeInPredicateToEquality(expression); checkArgument(isInferenceCandidate().apply(expression), "Expression must be a simple equality: " + expression); ComparisonExpression comparison = (ComparisonExpression) expression; addEquality(comparison.getLeft(), comparison.getRight()); return this; }
@Override protected Object visitComparisonExpression(ComparisonExpression node, Object context) { ComparisonExpression.Type type = node.getType(); Object left = process(node.getLeft(), context); if (left == null && type != ComparisonExpression.Type.IS_DISTINCT_FROM) { return null; } Object right = process(node.getRight(), context); if (type == ComparisonExpression.Type.IS_DISTINCT_FROM) { if (left == null && right == null) { return false; } else if (left == null || right == null) { return true; } } else if (right == null) { return null; } if (hasUnresolvedValue(left, right)) { return new ComparisonExpression(type, toExpression(left, expressionTypes.get(node.getLeft())), toExpression(right, expressionTypes.get(node.getRight()))); } if (type == ComparisonExpression.Type.IS_DISTINCT_FROM) { type = ComparisonExpression.Type.NOT_EQUAL; } return invokeOperator(OperatorType.valueOf(type.name()), types(node.getLeft(), node.getRight()), ImmutableList.of(left, right)); }
private static Domain extractEquatableDomain(ComparisonExpression.Type comparisonType, Type type, Object value, boolean complement) { checkArgument(value != null); switch (comparisonType) { case EQUAL: return Domain.create(complementIfNecessary(ValueSet.of(type, value), complement), false); case NOT_EQUAL: return Domain.create(complementIfNecessary(ValueSet.of(type, value).complement(), complement), false); case IS_DISTINCT_FROM: // Need to potential complement the whole domain for IS_DISTINCT_FROM since it is null-aware return complementIfNecessary(Domain.create(ValueSet.of(type, value).complement(), true), complement); default: throw new AssertionError("Unhandled type: " + comparisonType); } }
@Override protected ExtractionResult visitBetweenPredicate(BetweenPredicate node, Boolean complement) { // Re-write as two comparison expressions return process(and( new ComparisonExpression(GREATER_THAN_OR_EQUAL, node.getValue(), node.getMin()), new ComparisonExpression(LESS_THAN_OR_EQUAL, node.getValue(), node.getMax())), complement); }
/** * Extract a normalized simple comparison between a QualifiedNameReference and a native value if possible. */ private static Optional<NormalizedSimpleComparison> toNormalizedSimpleComparison(Session session, Metadata metadata, Map<Symbol, Type> types, ComparisonExpression comparison) { IdentityHashMap<Expression, Type> expressionTypes = ExpressionAnalyzer.getExpressionTypes(session, metadata, new SqlParser(), types, comparison); Object left = ExpressionInterpreter.expressionOptimizer(comparison.getLeft(), metadata, session, expressionTypes).optimize(NoOpSymbolResolver.INSTANCE); Object right = ExpressionInterpreter.expressionOptimizer(comparison.getRight(), metadata, session, expressionTypes).optimize(NoOpSymbolResolver.INSTANCE); if (left instanceof QualifiedNameReference && !(right instanceof Expression)) { return Optional.of(new NormalizedSimpleComparison((QualifiedNameReference) left, comparison.getType(), new NullableValue(expressionTypes.get(comparison.getRight()), right))); } if (right instanceof QualifiedNameReference && !(left instanceof Expression)) { return Optional.of(new NormalizedSimpleComparison((QualifiedNameReference) right, flipComparison(comparison.getType()), new NullableValue(expressionTypes.get(comparison.getLeft()), left))); } return Optional.empty(); }
@Override protected Type visitComparisonExpression(ComparisonExpression node, StackableAstVisitorContext<AnalysisContext> context) { OperatorType operatorType; if (node.getType() == ComparisonExpression.Type.IS_DISTINCT_FROM) { operatorType = OperatorType.EQUAL; } else { operatorType = OperatorType.valueOf(node.getType().name()); } return getOperator(context, node, operatorType, node.getLeft(), node.getRight()); }
private static Set<Expression> equalityAsSet(Expression expression) { Preconditions.checkArgument(expression instanceof ComparisonExpression); ComparisonExpression comparisonExpression = (ComparisonExpression) expression; Preconditions.checkArgument(comparisonExpression.getType() == EQUAL); return ImmutableSet.of(comparisonExpression.getLeft(), comparisonExpression.getRight()); }
@Override public Node visitComparison(SqlBaseParser.ComparisonContext context) { return new ComparisonExpression( getLocation(context.comparisonOperator()), getComparisonOperator(((TerminalNode) context.comparisonOperator().getChild(0)).getSymbol()), (Expression) visit(context.value), (Expression) visit(context.right)); }
@Test public void testShowPartitions() { assertStatement("SHOW PARTITIONS FROM t", new ShowPartitions(QualifiedName.of("t"), Optional.empty(), ImmutableList.of(), Optional.empty())); assertStatement("SHOW PARTITIONS FROM t WHERE x = 1", new ShowPartitions( QualifiedName.of("t"), Optional.of(new ComparisonExpression(ComparisonExpression.Type.EQUAL, new QualifiedNameReference(QualifiedName.of("x")), new LongLiteral("1"))), ImmutableList.of(), Optional.empty())); assertStatement("SHOW PARTITIONS FROM t WHERE x = 1 ORDER BY y", new ShowPartitions( QualifiedName.of("t"), Optional.of(new ComparisonExpression(ComparisonExpression.Type.EQUAL, new QualifiedNameReference(QualifiedName.of("x")), new LongLiteral("1"))), ImmutableList.of(new SortItem(new QualifiedNameReference(QualifiedName.of("y")), SortItem.Ordering.ASCENDING, SortItem.NullOrdering.UNDEFINED)), Optional.empty())); assertStatement("SHOW PARTITIONS FROM t WHERE x = 1 ORDER BY y LIMIT 10", new ShowPartitions( QualifiedName.of("t"), Optional.of(new ComparisonExpression(ComparisonExpression.Type.EQUAL, new QualifiedNameReference(QualifiedName.of("x")), new LongLiteral("1"))), ImmutableList.of(new SortItem(new QualifiedNameReference(QualifiedName.of("y")), SortItem.Ordering.ASCENDING, SortItem.NullOrdering.UNDEFINED)), Optional.of("10"))); assertStatement("SHOW PARTITIONS FROM t WHERE x = 1 ORDER BY y LIMIT ALL", new ShowPartitions( QualifiedName.of("t"), Optional.of(new ComparisonExpression(ComparisonExpression.Type.EQUAL, new QualifiedNameReference(QualifiedName.of("x")), new LongLiteral("1"))), ImmutableList.of(new SortItem(new QualifiedNameReference(QualifiedName.of("y")), SortItem.Ordering.ASCENDING, SortItem.NullOrdering.UNDEFINED)), Optional.of("ALL"))); }
@Test public void testDelete() { assertStatement("DELETE FROM t", new Delete(table(QualifiedName.of("t")), Optional.empty())); assertStatement("DELETE FROM t WHERE a = b", new Delete(table(QualifiedName.of("t")), Optional.of( new ComparisonExpression(ComparisonExpression.Type.EQUAL, new QualifiedNameReference(QualifiedName.of("a")), new QualifiedNameReference(QualifiedName.of("b")))))); }
@Override protected String visitComparisonExpression(ComparisonExpression node, Void context) { return formatBinaryExpression(node.getType().getValue(), node.getLeft(), node.getRight()); }
@Override protected String visitComparisonExpression(ComparisonExpression node, StackableAstVisitorContext<Integer> indent) { return formatBinaryExpression(node.getType().getValue(), node.getLeft(), node.getRight(), indent); }
/** * Parses predicats of types =, >, >=, <, <= and <> * @param compareExp * @param state * @return */ private QueryWrapper processComparison(ComparisonExpression compareExp, QueryState state) { String field = getVariableName(compareExp.getLeft()); FieldAndType fat = getFieldAndType(field, state); field = fat.getFieldName(); if(compareExp.getRight() instanceof QualifiedNameReference || compareExp.getRight() instanceof DereferenceExpression){ state.addException("Matching two columns is not supported : "+compareExp); return null; } // get value of the expression Object value = getLiteralValue(compareExp.getRight(), state); if(state.hasException()) return null; QueryBuilder comparison = null; String[] types = new String[state.getSources().size()]; for(int i=0; i<types.length; i++) types[i] = state.getSources().get(i).getSource(); if(compareExp.getType() == ComparisonExpression.Type.EQUAL){ if(field.equals(Heading.ID)) comparison = QueryBuilders.idsQuery(types).ids((String)value); else if(field.equals(Heading.SEARCH)) comparison = QueryBuilders.queryStringQuery((String)value); else if(value instanceof String) comparison = queryForString(field, (String)value); else comparison = QueryBuilders.termQuery(field, value); }else if(compareExp.getType() == ComparisonExpression.Type.GREATER_THAN_OR_EQUAL){ comparison = QueryBuilders.rangeQuery(field).from(value); }else if(compareExp.getType() == ComparisonExpression.Type.LESS_THAN_OR_EQUAL){ comparison = QueryBuilders.rangeQuery(field).to(value); }else if(compareExp.getType() == ComparisonExpression.Type.GREATER_THAN){ comparison = QueryBuilders.rangeQuery(field).gt(value); }else if(compareExp.getType() == ComparisonExpression.Type.LESS_THAN){ comparison = QueryBuilders.rangeQuery(field).lt(value); }else if(compareExp.getType() == ComparisonExpression.Type.NOT_EQUAL){ if(field.equals(Heading.ID)){ state.addException("Matching document _id using '<>' is not supported"); return null; } comparison = QueryBuilders.notQuery(QueryBuilders.termQuery(field, value)); }; if(fat.getFieldType() == Types.REF) return new QueryWrapper( comparison, field.split("\\.")[0]); return new QueryWrapper(comparison); }
@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; }
@SuppressWarnings("unchecked") public int execute(String update) throws SQLException { Matcher matcher = updateRegex.matcher(update); if(!matcher.find()) throw new SQLException("Unable to parse UPDATE statement"); // get index and type to update String index = statement.getConnection().getSchema(); String type = matcher.group(1); if(matcher.group(2) != null){ index = type; type = matcher.group(2); } // get fields and values to update try{ Map<String, Object> fieldValues = new HashMap<String, Object>(); SqlParser parser = new SqlParser(); String[] parts = matcher.group(3).replaceAll(",\\s*([\"|\\w|\\.]+\\s*=)", "<-SPLIT->$1").split("<-SPLIT->"); for(String p : parts){ ComparisonExpression comparison = (ComparisonExpression) parser.createExpression(p); String field = comparison.getLeft().toString().replaceAll("\"", ""); field = Heading.findOriginal(matcher.group(3), field, "", "\\s*="); Object value = getLiteralValue(comparison.getRight()); if(field.indexOf('.') == -1) { fieldValues.put(field, value); continue; } // create nested object Map<String, Object> map = fieldValues; String[] objectDef = field.split("\\."); for(int k=0; k<objectDef.length; k++){ String key = objectDef[k]; if(k == objectDef.length-1) map.put(key, value); else{ if(!map.containsKey(key)) map.put(key, new HashMap<String, Object>()); map = (Map<String, Object>)map.get(key); } } } // get ID's for documents to be updated String select = "SELECT _id FROM "+type+" WHERE "+matcher.group(4); Query query = (Query)new SqlParser().createStatement(select); this.queryState.buildRequest(select, query.getQueryBody(), index); ResultSet rs = this.queryState.execute(); // execute updates in batch mode based on id's returned int maxRequestsPerBulk = Utils.getIntProp(props, Utils.PROP_FETCH_SIZE, 2500); List<UpdateRequestBuilder> indexReqs = new ArrayList<UpdateRequestBuilder>(); int updateCount = 0; while(rs != null){ while(rs.next()){ String id = rs.getString(1); indexReqs.add( client.prepareUpdate(index, type, id).setDoc(fieldValues) ); if(indexReqs.size() >= maxRequestsPerBulk){ updateCount += this.execute(indexReqs, maxRequestsPerBulk); indexReqs.clear(); } } rs.close(); rs = queryState.moreResults(true); } if(indexReqs.size() > 0) updateCount += this.execute(indexReqs, maxRequestsPerBulk); return updateCount; }catch(Exception e){ throw new SQLException("Unable to execute UPDATE due to "+e.getMessage(),e); } }
@Override public Expression visitJoin(JoinNode node, Void context) { Expression leftPredicate = node.getLeft().accept(this, context); Expression rightPredicate = node.getRight().accept(this, context); List<Expression> joinConjuncts = new ArrayList<>(); for (JoinNode.EquiJoinClause clause : node.getCriteria()) { joinConjuncts.add(new ComparisonExpression(ComparisonExpression.Type.EQUAL, new QualifiedNameReference(clause.getLeft().toQualifiedName()), new QualifiedNameReference(clause.getRight().toQualifiedName()))); } switch (node.getType()) { case INNER: return combineConjuncts(ImmutableList.<Expression>builder() .add(leftPredicate) .add(rightPredicate) .addAll(joinConjuncts) .build()); case LEFT: return combineConjuncts(ImmutableList.<Expression>builder() .add(leftPredicate) .addAll(pullNullableConjunctsThroughOuterJoin(extractConjuncts(rightPredicate), in(node.getRight().getOutputSymbols()))) .addAll(pullNullableConjunctsThroughOuterJoin(joinConjuncts, in(node.getRight().getOutputSymbols()))) .build()); case RIGHT: return combineConjuncts(ImmutableList.<Expression>builder() .add(rightPredicate) .addAll(pullNullableConjunctsThroughOuterJoin(extractConjuncts(leftPredicate), in(node.getLeft().getOutputSymbols()))) .addAll(pullNullableConjunctsThroughOuterJoin(joinConjuncts, in(node.getLeft().getOutputSymbols()))) .build()); case FULL: return combineConjuncts(ImmutableList.<Expression>builder() .addAll(pullNullableConjunctsThroughOuterJoin(extractConjuncts(leftPredicate), in(node.getLeft().getOutputSymbols()))) .addAll(pullNullableConjunctsThroughOuterJoin(extractConjuncts(rightPredicate), in(node.getRight().getOutputSymbols()))) .addAll(pullNullableConjunctsThroughOuterJoin(joinConjuncts, in(node.getLeft().getOutputSymbols()), in(node.getRight().getOutputSymbols()))) .build()); default: throw new UnsupportedOperationException("Unknown join type: " + node.getType()); } }
@Override protected Object visitLikePredicate(LikePredicate node, Object context) { Object value = process(node.getValue(), context); if (value == null) { return null; } if (value instanceof Slice && node.getPattern() instanceof StringLiteral && (node.getEscape() instanceof StringLiteral || node.getEscape() == null)) { // fast path when we know the pattern and escape are constant return LikeFunctions.like((Slice) value, getConstantPattern(node)); } Object pattern = process(node.getPattern(), context); if (pattern == null) { return null; } Object escape = null; if (node.getEscape() != null) { escape = process(node.getEscape(), context); if (escape == null) { return null; } } if (value instanceof Slice && pattern instanceof Slice && (escape == null || escape instanceof Slice)) { Regex regex; if (escape == null) { regex = LikeFunctions.likePattern((Slice) pattern); } else { regex = LikeFunctions.likePattern((Slice) pattern, (Slice) escape); } return LikeFunctions.like((Slice) value, regex); } // if pattern is a constant without % or _ replace with a comparison if (pattern instanceof Slice && escape == null) { String stringPattern = ((Slice) pattern).toStringUtf8(); if (!stringPattern.contains("%") && !stringPattern.contains("_")) { return new ComparisonExpression(ComparisonExpression.Type.EQUAL, toExpression(value, expressionTypes.get(node.getValue())), toExpression(pattern, expressionTypes.get(node.getPattern()))); } } Expression optimizedEscape = null; if (node.getEscape() != null) { optimizedEscape = toExpression(escape, expressionTypes.get(node.getEscape())); } return new LikePredicate( toExpression(value, expressionTypes.get(node.getValue())), toExpression(pattern, expressionTypes.get(node.getPattern())), optimizedEscape); }