All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.aerospike.jdbc.query.AerospikeExpressionParser Maven / Gradle / Ivy

package com.aerospike.jdbc.query;

import com.google.common.base.CharMatcher;
import com.google.common.base.Joiner;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import io.prestosql.sql.SqlFormatter;
import io.prestosql.sql.tree.*;

import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.util.List;
import java.util.Locale;
import java.util.PrimitiveIterator;
import java.util.function.Function;
import java.util.stream.Collectors;

public final class AerospikeExpressionParser {

    private static final ThreadLocal doubleFormatter = ThreadLocal.withInitial(() ->
            new DecimalFormat("0.###################E0###", new DecimalFormatSymbols(Locale.US)));

    private AerospikeExpressionParser() {
    }

    public static String formatExpression(Expression expression) {
        return (new Formatter()).process(expression, null);
    }

    private static String formatIdentifier(String s) {
        return '"' + s.replace("\"", "\"\"") + '"';
    }

    static String formatStringLiteral(String s) {
        s = s.replace("'", "''");
        if (CharMatcher.inRange(' ', '~').matchesAllOf(s)) {
            return "'" + s + "'";
        } else {
            StringBuilder builder = new StringBuilder();
            builder.append("U&'");
            PrimitiveIterator.OfInt iterator = s.codePoints().iterator();

            while (iterator.hasNext()) {
                int codePoint = iterator.nextInt();
                Preconditions.checkArgument(codePoint >= 0, "Invalid UTF-8 encoding in characters: %s", s);
                if (isAsciiPrintable(codePoint)) {
                    char ch = (char) codePoint;
                    if (ch == '\\') {
                        builder.append(ch);
                    }

                    builder.append(ch);
                } else if (codePoint <= 65535) {
                    builder.append('\\');
                    builder.append(String.format("%04X", codePoint));
                } else {
                    builder.append("\\+");
                    builder.append(String.format("%06X", codePoint));
                }
            }

            builder.append("'");
            return builder.toString();
        }
    }

    public static List formatOrderBy(OrderBy orderBy) {
        return formatSortItems(orderBy.getSortItems());
    }

    private static List formatSortItems(List sortItems) {
        return sortItems.stream().map(sortItemFormatterFunction()).collect(Collectors.toList());
    }

    private static boolean isAsciiPrintable(int codePoint) {
        return codePoint >= 32 && codePoint < 127;
    }

    private static String formatGroupingSet(List groupingSet) {
        return String.format("(%s)", Joiner.on(", ").join(groupingSet.stream()
                .map(io.prestosql.sql.ExpressionFormatter::formatExpression).iterator()));
    }

    private static Function sortItemFormatterFunction() {
        return (input) -> {
            StringBuilder builder = new StringBuilder();
            builder.append(formatExpression(input.getSortKey()));
            switch (input.getOrdering()) {
                case ASCENDING:
                    builder.append(" ASC");
                    break;
                case DESCENDING:
                    builder.append(" DESC");
                    break;
                default:
                    throw new UnsupportedOperationException("unknown ordering: " + input.getOrdering());
            }

            switch (input.getNullOrdering()) {
                case FIRST:
                    builder.append(" NULLS FIRST");
                    break;
                case LAST:
                    builder.append(" NULLS LAST");
                case UNDEFINED:
                    break;
                default:
                    throw new UnsupportedOperationException("unknown null ordering: " + input.getNullOrdering());
            }

            return builder.toString();
        };
    }

    public static class Formatter extends AstVisitor {
        public Formatter() {
        }

        protected String visitNode(Node node, Void context) {
            throw new UnsupportedOperationException();
        }

        protected String visitRow(Row node, Void context) {
            return Joiner.on(", ").join(node.getItems().stream()
                    .map((child) -> this.process(child, context)).collect(Collectors.toList()));
        }

        protected String visitExpression(Expression node, Void context) {
            throw new UnsupportedOperationException();
        }

        protected String visitAtTimeZone(AtTimeZone node, Void context) {
            throw new UnsupportedOperationException();
        }

        protected String visitCurrentUser(CurrentUser node, Void context) {
            throw new UnsupportedOperationException();
        }

        protected String visitCurrentPath(CurrentPath node, Void context) {
            throw new UnsupportedOperationException();
        }

        protected String visitFormat(Format node, Void context) {
            throw new UnsupportedOperationException();
        }

        protected String visitCurrentTime(CurrentTime node, Void context) {
            throw new UnsupportedOperationException();
        }

        protected String visitExtract(Extract node, Void context) {
            throw new UnsupportedOperationException();
        }

        protected String visitBooleanLiteral(BooleanLiteral node, Void context) {
            return String.valueOf(node.getValue());
        }

        protected String visitStringLiteral(StringLiteral node, Void context) {
            return AerospikeExpressionParser.formatStringLiteral(node.getValue());
        }

        protected String visitCharLiteral(CharLiteral node, Void context) {
            return "CHAR " + AerospikeExpressionParser.formatStringLiteral(node.getValue());
        }

        protected String visitBinaryLiteral(BinaryLiteral node, Void context) {
            return "X'" + node.toHexString() + "'";
        }

        protected String visitParameter(Parameter node, Void context) {
            return "?";
        }

        protected String visitAllRows(AllRows node, Void context) {
            return "ALL";
        }

        protected String visitArrayConstructor(ArrayConstructor node, Void context) {
            ImmutableList.Builder valueStrings = ImmutableList.builder();

            for (Expression value : node.getValues()) {
                valueStrings.add(SqlFormatter.formatSql(value));
            }

            return "ARRAY[" + Joiner.on(",").join(valueStrings.build()) + "]";
        }

        protected String visitSubscriptExpression(SubscriptExpression node, Void context) {
            return SqlFormatter.formatSql(node.getBase()) + "[" + SqlFormatter.formatSql(node.getIndex()) + "]";
        }

        protected String visitLongLiteral(LongLiteral node, Void context) {
            return Long.toString(node.getValue());
        }

        protected String visitDoubleLiteral(DoubleLiteral node, Void context) {
            return (AerospikeExpressionParser.doubleFormatter.get()).format(node.getValue());
        }

        protected String visitDecimalLiteral(DecimalLiteral node, Void context) {
            return "DECIMAL '" + node.getValue() + "'";
        }

        protected String visitGenericLiteral(GenericLiteral node, Void context) {
            return node.getType() + " " + AerospikeExpressionParser.formatStringLiteral(node.getValue());
        }

        protected String visitTimeLiteral(TimeLiteral node, Void context) {
            return "TIME '" + node.getValue() + "'";
        }

        protected String visitTimestampLiteral(TimestampLiteral node, Void context) {
            return "TIMESTAMP '" + node.getValue() + "'";
        }

        protected String visitNullLiteral(NullLiteral node, Void context) {
            return "null";
        }

        protected String visitIntervalLiteral(IntervalLiteral node, Void context) {
            String sign = node.getSign() == IntervalLiteral.Sign.NEGATIVE ? "- " : "";
            StringBuilder builder = (new StringBuilder()).append("INTERVAL ").append(sign).append(" '")
                    .append(node.getValue()).append("' ").append(node.getStartField());
            if (node.getEndField().isPresent()) {
                builder.append(" TO ").append(node.getEndField().get());
            }

            return builder.toString();
        }

        protected String visitSubqueryExpression(SubqueryExpression node, Void context) {
            return "(" + SqlFormatter.formatSql(node.getQuery()) + ")";
        }

        protected String visitExists(ExistsPredicate node, Void context) {
            return "(EXISTS " + SqlFormatter.formatSql(node.getSubquery()) + ")";
        }

        protected String visitIdentifier(Identifier node, Void context) {
            return !node.isDelimited() ? node.getValue() : '"' + node.getValue().replace("\"", "\"\"") + '"';
        }

        protected String visitLambdaArgumentDeclaration(LambdaArgumentDeclaration node, Void context) {
            return io.prestosql.sql.ExpressionFormatter.formatExpression(node.getName());
        }

        protected String visitSymbolReference(SymbolReference node, Void context) {
            return AerospikeExpressionParser.formatIdentifier(node.getName());
        }

        protected String visitDereferenceExpression(DereferenceExpression node, Void context) {
            String baseString = this.process(node.getBase(), context);
            return baseString + "." + this.process(node.getField());
        }

        public String visitFieldReference(FieldReference node, Void context) {
            throw new UnsupportedOperationException();
        }

        protected String visitFunctionCall(FunctionCall node, Void context) {
            throw new UnsupportedOperationException();
        }

        protected String visitLambdaExpression(LambdaExpression node, Void context) {
            throw new UnsupportedOperationException();
        }

        protected String visitBindExpression(BindExpression node, Void context) {
            throw new UnsupportedOperationException();
        }

        protected String visitLogicalBinaryExpression(LogicalBinaryExpression node, Void context) {
            return this.formatBinaryExpression(node.getOperator().toString(), node.getLeft(), node.getRight());
        }

        protected String visitNotExpression(NotExpression node, Void context) {
            return "(NOT " + this.process(node.getValue(), context) + ")";
        }

        protected String visitComparisonExpression(ComparisonExpression node, Void context) {
            return this.formatBinaryExpression(node.getOperator().getValue(), node.getLeft(), node.getRight());
        }

        protected String visitIsNullPredicate(IsNullPredicate node, Void context) {
            return "(" + this.process(node.getValue(), context) + " IS NULL)";
        }

        protected String visitIsNotNullPredicate(IsNotNullPredicate node, Void context) {
            return "(" + this.process(node.getValue(), context) + " IS NOT NULL)";
        }

        protected String visitNullIfExpression(NullIfExpression node, Void context) {
            throw new UnsupportedOperationException();
        }

        protected String visitIfExpression(IfExpression node, Void context) {
            throw new UnsupportedOperationException();
        }

        protected String visitTryExpression(TryExpression node, Void context) {
            throw new UnsupportedOperationException();
        }

        protected String visitCoalesceExpression(CoalesceExpression node, Void context) {
            throw new UnsupportedOperationException();
        }

        protected String visitArithmeticUnary(ArithmeticUnaryExpression node, Void context) {
            throw new UnsupportedOperationException();
        }

        protected String visitArithmeticBinary(ArithmeticBinaryExpression node, Void context) {
            throw new UnsupportedOperationException();
        }

        protected String visitLikePredicate(LikePredicate node, Void context) {
            StringBuilder builder = new StringBuilder();
            builder.append('(').append(this.process(node.getValue(), context)).append(" LIKE ")
                    .append(this.process(node.getPattern(), context));
            node.getEscape().ifPresent((escape) -> {
                builder.append(" ESCAPE ").append(this.process(escape, context));
            });
            builder.append(')');
            return builder.toString();
        }

        protected String visitAllColumns(AllColumns node, Void context) {
            StringBuilder builder = new StringBuilder();
            if (node.getTarget().isPresent()) {
                builder.append(this.process(node.getTarget().get(), context));
                builder.append(".*");
            } else {
                builder.append("*");
            }

            if (!node.getAliases().isEmpty()) {
                builder.append(" AS (");
                Joiner.on(", ").appendTo(builder, node.getAliases().stream().map((alias) -> this.process(alias, context))
                        .collect(Collectors.toList()));
                builder.append(")");
            }

            return builder.toString();
        }

        public String visitCast(Cast node, Void context) {
            throw new UnsupportedOperationException();
        }

        protected String visitSearchedCaseExpression(SearchedCaseExpression node, Void context) {
            throw new UnsupportedOperationException();
        }

        protected String visitSimpleCaseExpression(SimpleCaseExpression node, Void context) {
            throw new UnsupportedOperationException();
        }

        protected String visitWhenClause(WhenClause node, Void context) {
            return "WHEN " + this.process(node.getOperand(), context) + " THEN " + this.process(node.getResult(), context);
        }

        protected String visitBetweenPredicate(BetweenPredicate node, Void context) {
            return "(" + this.process(node.getValue(), context) + " BETWEEN " + this.process(node.getMin(), context)
                    + " AND " + this.process(node.getMax(), context) + ")";
        }

        protected String visitInPredicate(InPredicate node, Void context) {
            return "(" + this.process(node.getValue(), context) + " IN " + this.process(node.getValueList(), context) + ")";
        }

        protected String visitInListExpression(InListExpression node, Void context) {
            return "(" + this.joinExpressions(node.getValues()) + ")";
        }

        private String visitFilter(Expression node, Void context) {
            return "(WHERE " + this.process(node, context) + ')';
        }

        public String visitWindow(Window node, Void context) {
            throw new UnsupportedOperationException();
        }

        public String visitWindowFrame(WindowFrame node, Void context) {
            throw new UnsupportedOperationException();
        }

        public String visitFrameBound(FrameBound node, Void context) {
            throw new UnsupportedOperationException();
        }

        protected String visitQuantifiedComparisonExpression(QuantifiedComparisonExpression node, Void context) {
            return "(" + this.process(node.getValue(), context) + ' ' + node.getOperator().getValue() + ' '
                    + node.getQuantifier().toString() + ' ' + this.process(node.getSubquery(), context) + ")";
        }

        protected String visitGroupingOperation(GroupingOperation node, Void context) {
            return "GROUPING (" + this.joinExpressions(node.getGroupingColumns()) + ")";
        }

        protected String visitRowDataType(RowDataType node, Void context) {
            return node.getFields().stream().map(this::process)
                    .collect(Collectors.joining(", ", "ROW(", ")"));
        }

        protected String visitRowField(RowDataType.Field node, Void context) {
            StringBuilder result = new StringBuilder();
            if (node.getName().isPresent()) {
                result.append(this.process(node.getName().get(), context));
                result.append(" ");
            }

            result.append(this.process(node.getType(), context));
            return result.toString();
        }

        protected String visitGenericDataType(GenericDataType node, Void context) {
            StringBuilder result = new StringBuilder();
            result.append(node.getName());
            if (!node.getArguments().isEmpty()) {
                result.append(node.getArguments().stream().map(this::process)
                        .collect(Collectors.joining(", ", "(", ")")));
            }

            return result.toString();
        }

        protected String visitTypeParameter(TypeParameter node, Void context) {
            return this.process(node.getValue(), context);
        }

        protected String visitNumericTypeParameter(NumericParameter node, Void context) {
            return node.getValue();
        }

        protected String visitIntervalDataType(IntervalDayTimeDataType node, Void context) {
            StringBuilder builder = new StringBuilder();
            builder.append("INTERVAL ");
            builder.append(node.getFrom());
            if (node.getFrom() != node.getTo()) {
                builder.append(" TO ").append(node.getTo());
            }

            return builder.toString();
        }

        protected String visitDateTimeType(DateTimeDataType node, Void context) {
            StringBuilder builder = new StringBuilder();
            builder.append(node.getType().toString().toLowerCase(Locale.ENGLISH));
            if (node.getPrecision().isPresent()) {
                builder.append("(").append(node.getPrecision().get()).append(")");
            }

            if (node.isWithTimeZone()) {
                builder.append(" with time zone");
            }

            return builder.toString();
        }

        private String formatBinaryExpression(String operator, Expression left, Expression right) {
            return left + operator + right;
        }

        private String joinExpressions(List expressions) {
            throw new UnsupportedOperationException();
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy