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

org.babyfish.jimmer.sql.ast.impl.Literals Maven / Gradle / Ivy

There is a newer version: 0.9.19
Show newest version
package org.babyfish.jimmer.sql.ast.impl;

import org.babyfish.jimmer.meta.ImmutableProp;
import org.babyfish.jimmer.runtime.ImmutableSpi;
import org.babyfish.jimmer.sql.ast.*;
import org.babyfish.jimmer.sql.ast.impl.render.AbstractSqlBuilder;
import org.babyfish.jimmer.sql.ast.impl.render.BatchSqlBuilder;
import org.babyfish.jimmer.sql.ast.table.spi.PropExpressionImplementor;
import org.babyfish.jimmer.sql.runtime.ExecutionException;
import org.babyfish.jimmer.sql.runtime.JSqlClientImplementor;
import org.babyfish.jimmer.sql.runtime.ScalarProvider;
import org.babyfish.jimmer.sql.runtime.SqlBuilder;
import org.jetbrains.annotations.NotNull;

import java.util.*;

public class Literals {

    private Literals() {}

    public static StringExpression string(String value) {
        return new Str(value);
    }

    public static > NumericExpression number(N value) {
        return new Num<>(value);
    }

    public static > ComparableExpression comparable(T value) {
        return new Cmp<>(value);
    }

    public static void bind(Expression mayBeLiteral, Expression expression) {
        if (!(mayBeLiteral instanceof Any)) {
            return;
        }
        if (expression instanceof PropExpression) {
            ((Any)mayBeLiteral).setMatchedProp(((PropExpressionImplementor)expression).getProp());
        } else if (expression instanceof TupleExpressionImplementor) {
            TupleExpressionImplementor tupleExpr = (TupleExpressionImplementor) expression;
            int size = tupleExpr.size();
            ImmutableProp[] props = new ImmutableProp[size];
            boolean hasProp = false;
            for (int i = 0; i < size; i++) {
                Selection expr = tupleExpr.get(i);
                if (expr instanceof PropExpression) {
                    props[i] = ((PropExpressionImplementor)expr).getProp();
                    hasProp = true;
                }
            }
            if (hasProp) {
                ((Any) mayBeLiteral).setMatchedProps(props);
            }
        }
    }

    @SuppressWarnings("unchecked")
    public static Collection convert(Collection literals, Expression expression, JSqlClientImplementor sqlClient) {
        if (literals == null || literals.isEmpty()) {
            return literals;
        }
        if (expression instanceof PropExpression) {
            ImmutableProp prop = ((PropExpressionImplementor)expression).getProp();
            ScalarProvider scalarProvider = sqlClient.getScalarProvider(prop);
            if (scalarProvider == null) {
                return literals;
            }
            List newLiterals = new ArrayList<>(literals.size());
            for (Object literal : literals) {
                try {
                    newLiterals.add(literal != null ? scalarProvider.toSql(literal) : null);
                } catch (Exception ex) {
                    throw new ExecutionException(
                            "Cannot convert the value \"" +
                                    literal +
                                    "\" of prop \"" +
                                    prop +
                                    "\" by scalar provider \"" +
                                    scalarProvider.getClass().getName() +
                                    "\"",
                            ex
                    );
                }
            }
            return newLiterals;
        } else if (expression instanceof TupleExpressionImplementor) {
            TupleExpressionImplementor tupleExpr = (TupleExpressionImplementor) expression;
            int size = tupleExpr.size();
            ImmutableProp[] props = new ImmutableProp[size];
            ScalarProvider[] scalarProviders = new ScalarProvider[size];
            boolean hasScalarProvider = false;
            for (int i = 0; i < size; i++) {
                Selection expr = tupleExpr.get(i);
                if (expr instanceof PropExpression) {
                    ImmutableProp prop = ((PropExpressionImplementor)expr).getProp();
                    ScalarProvider scalarProvider = sqlClient.getScalarProvider(prop);
                    if (scalarProvider != null) {
                        props[i] = prop;
                        scalarProviders[i] = scalarProvider;
                        hasScalarProvider = true;
                    }
                }
            }
            if (!hasScalarProvider) {
                return literals;
            }
            List newLiterals = new ArrayList<>(literals.size());
            for (Object literal : literals) {
                newLiterals.add(
                        ((TupleImplementor)literal).convert((value, index) -> {
                            if (value != null) {
                                ScalarProvider scalarProvider = scalarProviders[index];
                                if (scalarProvider != null) {
                                    try {
                                        return scalarProvider.toSql(value);
                                    } catch (Exception ex) {
                                        throw new ExecutionException(
                                                "Cannot convert the tuple item[" +
                                                        "index" +
                                                        "] of prop \"" +
                                                        props[index] +
                                                        "\" by scalar provider \"" +
                                                        scalarProvider.getClass().getName() +
                                                        "\"",
                                                ex
                                        );
                                    }
                                }
                            }
                            return value;
                        })
                );
            }
            return newLiterals;
        } else {
            return literals;
        }
    }

    @SuppressWarnings({"unchecked", "rawtypes"})
    public static  Expression any(T value) {
        if (value instanceof String) {
            return (Expression) string((String)value);
        }
        if (value instanceof Number) {
            return (Expression) number((Number & Comparable)value);
        }
        if (value instanceof Comparable) {
            return (Expression) comparable((Comparable)value);
        }
        if (value instanceof Expression) {
            return (Expression) value;
        }
        return new Any<>(value);
    }

    static class Any extends AbstractExpression implements LiteralExpressionImplementor {

        private final T value;

        // Single
        private ImmutableProp matchedProp;

        // Tuple
        private ImmutableProp[] matchedProps;

        public Any(T value) {
            if (value == null) {
                throw new IllegalArgumentException(
                        "The value of literal expression cannot be null." +
                                "Except for `eq` and `ne`, most SQL expressions cannot be constructed " +
                                "based on using null, in order to avoid this problem:\n" +
                                "- java users can\n" +
                                "   Change `whereIf(, )` to whereIf(, () -> )\n" +
                                "- kotlin users can\n" +
                                "   Change `if (value !== null) where()` to `value?.let { where() }`"
                );
            }
            this.value = value;
        }

        @Override
        public T getValue() {
            return value;
        }

        @SuppressWarnings("unchecked")
        @Override
        public Class getType() {
            return (Class)value.getClass();
        }

        @Override
        public void accept(@NotNull AstVisitor visitor) {
        }

        @Override
        public void renderTo(@NotNull AbstractSqlBuilder builder) {
            if (builder instanceof BatchSqlBuilder) {
                ((BatchSqlBuilder) builder).rawVariable(finalValue(builder.sqlClient()));
            } else {
                ((SqlBuilder) builder).variable(finalValue(builder.sqlClient()));
            }
        }

        private Object finalValue(JSqlClientImplementor sqlClient) {
            if (matchedProp != null) {
                ScalarProvider scalarProvider = sqlClient.getScalarProvider(matchedProp);
                if (scalarProvider != null) {
                    try {
                        return scalarProvider.toSql(value);
                    } catch (Exception ex) {
                        throw new ExecutionException(
                                "Cannot convert the value \"" +
                                        value +
                                        "\" of prop \"" +
                                        matchedProp +
                                        "\" by the scalar provider \"" +
                                        scalarProvider.getClass().getName() +
                                        "\"",
                                ex
                        );
                    }
                }
            } else if (matchedProps != null) {
                return ((TupleImplementor)value).convert((it, index) -> {
                    ImmutableProp prop = matchedProps[index];
                    ScalarProvider scalarProvider =
                            sqlClient.getScalarProvider(prop);
                    if (scalarProvider != null) {
                        try {
                            return scalarProvider.toSql(it);
                        } catch (Exception ex) {
                            throw new ExecutionException(
                                    "Cannot convert the tuple item[" +
                                            index +
                                            "] of prop \"" +
                                            matchedProps[index] +
                                            "\" by the scalar provider \"" +
                                            scalarProvider.getClass().getName() +
                                            "\"",
                                    ex
                            );
                        }
                    }
                    return it;
                });
            }
            return Variables.process(value, getType(), sqlClient);
        }

        @Override
        protected boolean determineHasVirtualPredicate() {
            return false;
        }

        @Override
        protected Ast onResolveVirtualPredicate(AstContext ctx) {
            return this;
        }

        @Override
        public int precedence() {
            return 0;
        }

        public void setMatchedProp(ImmutableProp matchedProp) {
            if (this.matchedProp != null && this.matchedProp != matchedProp) {
                throw new IllegalStateException(
                        "The matched property of current literal expression has been configured, " +
                                "is the current literal expression is shared by difference parts of SQL DSL?"
                );
            }
            this.matchedProp = matchedProp;
        }

        public void setMatchedProps(ImmutableProp[] matchedProps) {
            if (this.matchedProps != null && this.matchedProps != matchedProps) {
                throw new IllegalStateException(
                        "The matched properties of current literal expression has been configured, " +
                                "is the current literal expression is shared by difference parts of SQL DSL?"
                );
            }
            this.matchedProps = matchedProps;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            Any any = (Any) o;
            return value.equals(any.value) && Objects.equals(matchedProp, any.matchedProp) && Arrays.equals(matchedProps, any.matchedProps);
        }

        @Override
        public int hashCode() {
            int result = Objects.hash(value, matchedProp);
            result = 31 * result + Arrays.hashCode(matchedProps);
            return result;
        }
    }

    private static class Str extends Any implements StringExpressionImplementor {
        public Str(String value) {
            super(value);
        }
    }

    private static class Num> extends Any implements NumericExpressionImplementor {
        public Num(N value) {
            super(value);
        }
    }

    private static class Cmp> extends Any implements ComparableExpressionImplementor {
        public Cmp(T value) {
            super(value);
        }
    }
}