optimization tests + query builder

This commit is contained in:
wea_ondara
2022-07-26 19:33:35 +02:00
parent ff4385aa5c
commit 3824a6f595
35 changed files with 816 additions and 24 deletions

View File

@@ -28,7 +28,7 @@ public class DbSet<T extends SerializableObject> implements Queryable<T> {
@Override
public Expression getExpression() {
return new SelectExpression(List.of(new DatabaseSelectAllExpression()), new TableExpression(table), "");
return new SelectExpression(List.of(DatabaseSelectAllExpression.INSTANCE), new TableExpression(table), "");
}
@Override

View File

@@ -14,7 +14,8 @@ import java.util.Spliterator;
public interface Queryable<T extends Serializable> {
//
//TODO documentation
//TODO table alias thing still not ready
String getTableAlias();
Expression getExpression();

View File

@@ -468,7 +468,7 @@ class FilterMethodVisitor extends MethodVisitor {
private void debugExpr() {
if (!varStack.isEmpty()) {
System.out.println("-------------------> " + new WhereExpression(new SelectExpression(List.of(new DatabaseSelectAllExpression()), new TableExpression("dummy"), ""), varStack.peek()));
System.out.println("-------------------> " + new WhereExpression(new SelectExpression(List.of(DatabaseSelectAllExpression.INSTANCE), new TableExpression("dummy"), ""), varStack.peek()));
}
}

View File

@@ -1,13 +1,16 @@
package jef.expressions;
import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
@Getter
@AllArgsConstructor
@EqualsAndHashCode
public class AndExpression implements Expression {
private final List<Expression> exprs;
@@ -25,6 +28,19 @@ public class AndExpression implements Expression {
return Priority.LOGIC_AND;
}
// @Override
// public boolean equals(Object o) {
// if (this == o) return true;
// if (o == null || getClass() != o.getClass()) return false;
// AndExpression that = (AndExpression) o;
// return exprs.equals(that.exprs);
// }
//
// @Override
// public int hashCode() {
// return Objects.hash(exprs);
// }
@Override
public String toString() {
return exprs.stream().map(e -> {

View File

@@ -1,12 +1,14 @@
package jef.expressions;
import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import java.util.Map;
@Getter
@AllArgsConstructor
@EqualsAndHashCode
public class BinaryExpression implements Expression {
private final Expression left;
private final Expression right;
@@ -45,6 +47,7 @@ public class BinaryExpression implements Expression {
}
@AllArgsConstructor
@Getter
public enum Operator {
EQ("="),
NE("<>"),

View File

@@ -1,10 +1,12 @@
package jef.expressions;
import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode;
import lombok.Getter;
@Getter
@AllArgsConstructor
@EqualsAndHashCode
public class ConstantExpression implements Expression {
public static final jef.expressions.ConstantExpression V0 = new jef.expressions.ConstantExpression(0);
public static final jef.expressions.ConstantExpression V1 = new jef.expressions.ConstantExpression(1);

View File

@@ -1,9 +1,11 @@
package jef.expressions;
import jef.expressions.selectable.SelectableExpression;
import lombok.EqualsAndHashCode;
import lombok.Getter;
@Getter
@EqualsAndHashCode
public class FieldExpression extends ConstantExpression implements SelectableExpression, Expression {
private final String schema;
private final String table;

View File

@@ -1,9 +1,11 @@
package jef.expressions;
import jef.expressions.selectable.SelectableExpression;
import lombok.EqualsAndHashCode;
import lombok.Getter;
@Getter
@EqualsAndHashCode(callSuper = false)
public class IntermediateFieldExpression extends ConstantExpression implements SelectableExpression, Expression {
private final String name;
private final String classDescriptor;

View File

@@ -1,10 +1,12 @@
package jef.expressions;
import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode;
import lombok.Getter;
@Getter
@AllArgsConstructor
@EqualsAndHashCode
public class LimitExpression implements Expression {
private final Expression expr;
private final Long start;
@@ -23,12 +25,12 @@ public class LimitExpression implements Expression {
@Override
public String toString() {
var ret = expr.toString();
if (count != null) {
ret += " LIMIT " + count;
}
if (start != null) {
ret += " OFFSET " + start;
}
if (count != null) {
ret += " LIMIT " + count;
}
return ret;
}
}

View File

@@ -1,5 +1,8 @@
package jef.expressions;
import lombok.EqualsAndHashCode;
@EqualsAndHashCode(callSuper = false)
public class NullExpression extends ConstantExpression {
public static final NullExpression INSTANCE = new NullExpression();

View File

@@ -1,6 +1,7 @@
package jef.expressions;
import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import java.util.List;
@@ -8,6 +9,7 @@ import java.util.stream.Collectors;
@Getter
@AllArgsConstructor
@EqualsAndHashCode
public class OrExpression implements Expression {
private final List<Expression> exprs;

View File

@@ -1,6 +1,7 @@
package jef.expressions;
import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import java.util.ArrayList;
@@ -8,6 +9,7 @@ import java.util.List;
@Getter
@AllArgsConstructor
@EqualsAndHashCode
public class OrderExpression implements Expression {
private final Expression expr;
private final List<Sort> sorts;
@@ -28,7 +30,7 @@ public class OrderExpression implements Expression {
if (!sorts.isEmpty()) {
var sortStrings = new ArrayList<String>(sorts.size());
for (Sort sort : sorts) {
sortStrings.add(sort.getExpr() + " " + sort.getDirection().getS());
sortStrings.add(sort.getExpr() + " " + sort.getDirection().getString());
}
ret += " ORDER BY " + String.join(", ", sortStrings);
}
@@ -37,6 +39,7 @@ public class OrderExpression implements Expression {
@Getter
@AllArgsConstructor
@EqualsAndHashCode
public static class Sort {
private final FieldExpression expr;
private final SortDirection direction;
@@ -48,6 +51,6 @@ public class OrderExpression implements Expression {
ASCENDING("ASC"),
DESCENDING("DESC"),
;
private final String s;
private final String string;
}
}

View File

@@ -1,5 +1,6 @@
package jef.expressions;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import java.util.Collection;
@@ -7,6 +8,7 @@ import java.util.Objects;
import java.util.stream.Collectors;
@Getter
@EqualsAndHashCode(callSuper = false)
public class ParameterExpression extends ConstantExpression implements Expression {
private final int index;
private final boolean isInput;

View File

@@ -2,6 +2,7 @@ package jef.expressions;
import jef.expressions.selectable.SelectableExpression;
import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import java.util.List;
@@ -9,6 +10,7 @@ import java.util.stream.Collectors;
@Getter
@AllArgsConstructor
@EqualsAndHashCode
public class SelectExpression implements Expression {
private final List<SelectableExpression> fields;
private final Expression from;

View File

@@ -1,8 +1,10 @@
package jef.expressions;
import lombok.EqualsAndHashCode;
import lombok.Getter;
@Getter
@EqualsAndHashCode(callSuper = false)
public class TableExpression extends ConstantExpression {
private final String name;

View File

@@ -1,10 +1,12 @@
package jef.expressions;
import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode;
import lombok.Getter;
@Getter
@AllArgsConstructor
@EqualsAndHashCode
public class TernaryExpression implements Expression {
private final Expression cond;
private final Expression whenTrue;

View File

@@ -1,10 +1,12 @@
package jef.expressions;
import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode;
import lombok.Getter;
@Getter
@AllArgsConstructor
@EqualsAndHashCode
public class UnaryExpression implements Expression {
private final Expression expr;
private final Operator operator;
@@ -29,6 +31,7 @@ public class UnaryExpression implements Expression {
}
@AllArgsConstructor
@Getter
public enum Operator {
NOT("NOT"),
// NEG("-"),

View File

@@ -1,10 +1,12 @@
package jef.expressions;
import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode;
import lombok.Getter;
@Getter
@AllArgsConstructor
@EqualsAndHashCode
public class WhereExpression implements Expression {
private Expression queryable;
private Expression where;

View File

@@ -4,11 +4,17 @@ import jef.expressions.AndExpression;
import jef.expressions.BinaryExpression;
import jef.expressions.ConstantExpression;
import jef.expressions.Expression;
import jef.expressions.LimitExpression;
import jef.expressions.OrExpression;
import jef.expressions.OrderExpression;
import jef.expressions.SelectExpression;
import jef.expressions.TernaryExpression;
import jef.expressions.UnaryExpression;
import jef.expressions.WhereExpression;
import jef.expressions.selectable.DatabaseSelectAllExpression;
import java.util.ArrayList;
import java.util.List;
public class ExpressionOptimizerBottomUp extends ExpressionModifier {
@Override
@@ -24,7 +30,6 @@ public class ExpressionOptimizerBottomUp extends ExpressionModifier {
ands.add(e);
}
}
ands.replaceAll(this::modify);
// x && false -> false
for (Expression e : ands) {
@@ -32,11 +37,39 @@ public class ExpressionOptimizerBottomUp extends ExpressionModifier {
return ConstantExpression.V0;
}
}
//remove && true
while (ands.remove(ConstantExpression.V1)) ;
return new AndExpression(ands);
}
@Override
public Expression modifyLimit(LimitExpression expr) {
var inner = modify(expr.getExpr());
//<- SELECT * FROM (SELECT * FROM (SELECT * FROM table) WHERE 1) LIMIT 1
//-> SELECT * FROM (SELECT * FROM table) WHERE 1 LIMIT 1
if (inner instanceof SelectExpression s
&& s.getFields().equals(List.of(DatabaseSelectAllExpression.INSTANCE))
&& s.getFrom() instanceof WhereExpression w) {
return new LimitExpression(w, expr.getStart(), expr.getCount());
}
//<- SELECT * FROM (SELECT * FROM (SELECT * FROM table) ORDER BY x ASC 1) LIMIT 1
//-> SELECT * FROM (SELECT * FROM table) ORDER BY x ASC LIMIT 1
else if (inner instanceof SelectExpression s
&& s.getFields().equals(List.of(DatabaseSelectAllExpression.INSTANCE))
&& s.getFrom() instanceof OrderExpression o) {
return new LimitExpression(o, expr.getStart(), expr.getCount());
}
//<- SELECT * FROM (SELECT * FROM (SELECT * FROM table) LIMIT 3) LIMIT 1
//-> SELECT * FROM (SELECT * FROM table) LIMIT 1
else if (inner instanceof LimitExpression l) {
return new LimitExpression(l.getExpr(), expr.getStart(), expr.getCount());
}
return super.modifyLimit(expr);
}
@Override
public Expression modifyOr(OrExpression expr) {
var orsOpt = expr.getExprs().stream().map(this::modify).toList();
@@ -50,7 +83,6 @@ public class ExpressionOptimizerBottomUp extends ExpressionModifier {
ors.add(e);
}
}
ors.replaceAll(this::modify);
// x || true -> true
for (Expression e : ors) {
@@ -58,17 +90,51 @@ public class ExpressionOptimizerBottomUp extends ExpressionModifier {
return ConstantExpression.V1;
}
}
//remove OR false
while (ors.remove(ConstantExpression.V0)) ;
return new OrExpression(ors);
}
@Override
public Expression modifyOrder(OrderExpression expr) {
var inner = modify(expr.getExpr());
//<- order(select(where(...)))
//-> order(where(...))
if (inner instanceof SelectExpression s
&& s.getFields().equals(List.of(DatabaseSelectAllExpression.INSTANCE))
&& s.getFrom() instanceof WhereExpression w) {
return new OrderExpression(w, expr.getSorts());
}
//<- order1(order2(...))
//-> order1(...=
else if (inner instanceof OrderExpression o) {
return new OrderExpression(o.getExpr(), expr.getSorts());
}
return super.modifyOrder(expr);
}
@Override
public Expression modifySelect(SelectExpression expr) {
var from = modify(expr.getFrom());
if (from instanceof SelectExpression s
&& s.getFields().equals(expr.getFields())) {
return s;
}
return super.modifySelect(expr);
}
@Override
public Expression modifyTernary(TernaryExpression expr) {
var cond = modify(expr.getCond());
var whenTrue = modify(expr.getWhenTrue());
var whenFalse = modify(expr.getWhenFalse());
return TernaryOptimizerUtil.optimizeTernary(new TernaryExpression(cond, whenTrue, whenFalse));
var optimized = TernaryOptimizerUtil.optimizeTernary(new TernaryExpression(cond, whenTrue, whenFalse));
if (optimized.equals(expr)) {
return super.modifyTernary(expr);
}
return modify(optimized);
}
@Override
@@ -93,4 +159,24 @@ public class ExpressionOptimizerBottomUp extends ExpressionModifier {
return super.modifyUnary(expr);
}
}
@Override
public Expression modifyWhere(WhereExpression expr) {
var queryable = modify(expr.getQueryable());
var where = modify(expr.getWhere());
//<- where(cond1, where(cond2, ...))
//-> where(and(cond1, cond2) ...))
if (queryable instanceof WhereExpression w) {
return new WhereExpression(w.getQueryable(), new AndExpression(w.getWhere(), where));
}
//<- where(cond1, select(where(cond2, ...)))
//-> where(and(cond1, cond2) ...))
else if (queryable instanceof SelectExpression s
&& s.getFields().equals(List.of(DatabaseSelectAllExpression.INSTANCE))
&& s.getFrom() instanceof WhereExpression w) {
return new WhereExpression(w.getQueryable(), new AndExpression(w.getWhere(), where));
}
return super.modifyWhere(expr);
}
}

View File

@@ -4,11 +4,17 @@ import jef.expressions.AndExpression;
import jef.expressions.BinaryExpression;
import jef.expressions.ConstantExpression;
import jef.expressions.Expression;
import jef.expressions.LimitExpression;
import jef.expressions.OrExpression;
import jef.expressions.OrderExpression;
import jef.expressions.SelectExpression;
import jef.expressions.TernaryExpression;
import jef.expressions.UnaryExpression;
import jef.expressions.WhereExpression;
import jef.expressions.selectable.DatabaseSelectAllExpression;
import java.util.ArrayList;
import java.util.List;
public class ExpressionOptimizerTopDown extends ExpressionModifier {
@Override
@@ -31,11 +37,37 @@ public class ExpressionOptimizerTopDown extends ExpressionModifier {
return ConstantExpression.V0;
}
}
//remove && true
while (ands.remove(ConstantExpression.V1)) ;
return new AndExpression(ands);
}
@Override
public Expression modifyLimit(LimitExpression expr) {
//<- SELECT * FROM (SELECT * FROM (SELECT * FROM table) WHERE 1) LIMIT 1
//-> SELECT * FROM (SELECT * FROM table) WHERE 1 LIMIT 1
if (expr.getExpr() instanceof SelectExpression s
&& s.getFields().equals(List.of(DatabaseSelectAllExpression.INSTANCE))
&& s.getFrom() instanceof WhereExpression w) {
return modify(new LimitExpression(w, expr.getStart(), expr.getCount()));
}
//<- SELECT * FROM (SELECT * FROM (SELECT * FROM table) ORDER BY x ASC 1) LIMIT 1
//-> SELECT * FROM (SELECT * FROM table) ORDER BY x ASC LIMIT 1
else if (expr.getExpr() instanceof SelectExpression s
&& s.getFields().equals(List.of(DatabaseSelectAllExpression.INSTANCE))
&& s.getFrom() instanceof OrderExpression o) {
return modify(new LimitExpression(o, expr.getStart(), expr.getCount()));
}
//<- SELECT * FROM (SELECT * FROM (SELECT * FROM table) LIMIT 3) LIMIT 1
//-> SELECT * FROM (SELECT * FROM table) LIMIT 1
else if (expr.getExpr() instanceof LimitExpression l) {
return modify(new LimitExpression(l.getExpr(), expr.getStart(), expr.getCount()));
}
return super.modifyLimit(expr);
}
@Override
public Expression modifyOr(OrExpression expr) {
var ors = new ArrayList<Expression>(expr.getExprs().size() * 2);
@@ -56,14 +88,46 @@ public class ExpressionOptimizerTopDown extends ExpressionModifier {
return ConstantExpression.V1;
}
}
//remove OR false
while (ors.remove(ConstantExpression.V0)) ;
return new OrExpression(ors);
}
@Override
public Expression modifyOrder(OrderExpression expr) {
//<- order(select(where(...)))
//-> order(where(...))
if (expr.getExpr() instanceof SelectExpression s
&& s.getFields().equals(List.of(DatabaseSelectAllExpression.INSTANCE))
&& s.getFrom() instanceof WhereExpression w) {
return modify(new OrderExpression(w, expr.getSorts()));
}
//<- order1(order2(...))
//-> order1(...=
else if (expr.getExpr() instanceof OrderExpression o) {
return modify(new OrderExpression(o.getExpr(), expr.getSorts()));
}
return super.modifyOrder(expr);
}
@Override
public Expression modifySelect(SelectExpression expr) {
if (expr.getFrom() instanceof SelectExpression s
&& s.getFields().equals(List.of(DatabaseSelectAllExpression.INSTANCE))) {
return modify(s);
}
return super.modifySelect(expr);
}
@Override
public Expression modifyTernary(TernaryExpression expr) {
return modify(TernaryOptimizerUtil.optimizeTernary(expr));
var optimized = TernaryOptimizerUtil.optimizeTernary(expr);
if (optimized == expr) {
return super.modifyTernary(expr);
}
return modify(optimized);
}
@Override
@@ -77,9 +141,31 @@ public class ExpressionOptimizerTopDown extends ExpressionModifier {
&& expr.getOperator() == UnaryExpression.Operator.NOT
&& b.getOperator().isInvertible()) {
//!(a < b) -> a >= b
return new BinaryExpression(b.getLeft(), b.getRight(), b.getOperator().invert());
return modify(new BinaryExpression(b.getLeft(), b.getRight(), b.getOperator().invert()));
} else if (expr.getExpr() instanceof BinaryExpression b
&& expr.getOperator() == UnaryExpression.Operator.NOT
&& b.getOperator() == BinaryExpression.Operator.IS) {
//!(a < b) -> a >= b
return modify(new BinaryExpression(b.getLeft(), new UnaryExpression(b.getRight(), UnaryExpression.Operator.NOT), b.getOperator()));
} else {
return super.modifyUnary(expr);
}
}
@Override
public Expression modifyWhere(WhereExpression expr) {
//<- where(cond1, where(cond2, ...))
//-> where(and(cond1, cond2) ...))
if (expr.getQueryable() instanceof WhereExpression w) {
return modify(new WhereExpression(w.getQueryable(), new AndExpression(w.getWhere(), expr.getWhere())));
}
//<- where(cond1, select(where(cond2, ...)))
//-> where(and(cond1, cond2) ...))
else if (expr.getQueryable() instanceof SelectExpression s
&& s.getFields().equals(List.of(DatabaseSelectAllExpression.INSTANCE))
&& s.getFrom() instanceof WhereExpression w) {
return modify(new WhereExpression(w.getQueryable(), new AndExpression(w.getWhere(), expr.getWhere())));
}
return super.modifyWhere(expr);
}
}

View File

@@ -1,8 +1,13 @@
package jef.expressions.selectable;
import jef.expressions.Expression;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class DatabaseSelectAllExpression implements Expression, SelectableExpression {
public static final DatabaseSelectAllExpression INSTANCE = new DatabaseSelectAllExpression();
@Override
public Expression.Type getType() {
return Expression.Type.CONSTANT;
@@ -13,6 +18,16 @@ public class DatabaseSelectAllExpression implements Expression, SelectableExpres
return Expression.Priority.UNDEFINED;
}
@Override
public boolean equals(Object obj) {
return this == obj;
}
@Override
public int hashCode() {
return 0;
}
@Override
public String toString() {
return "*";

View File

@@ -95,7 +95,7 @@ public class DebugExpressionVisitor extends ExpressionVisitor {
if (!expr.getSorts().isEmpty()) {
var sortStrings = new ArrayList<String>(expr.getSorts().size());
for (OrderExpression.Sort sort : expr.getSorts()) {
sortStrings.add(sort.getExpr() + " " + sort.getDirection().getS());
sortStrings.add(sort.getExpr() + " " + sort.getDirection().getString());
}
System.out.println(i() + " ORDER BY " + String.join(", ", sortStrings));
}

View File

@@ -73,6 +73,7 @@ public abstract class ExpressionVisitor {
}
public void visitOrder(OrderExpression expr) {
visit(expr.getExpr());
}
public void visitParameter(ParameterExpression expr) {

View File

@@ -22,6 +22,7 @@ public class FilterOp<T extends Serializable> implements Queryable<T>, Operation
private final Queryable<T> queryable;
private final Predicate<? super T> predicate;
private final Expression predicateExpr;
// private final Expression finalExpr;
public FilterOp(Queryable<T> queryable, SerializablePredicate<? super T> predicate) {
this.queryable = queryable;
@@ -40,8 +41,13 @@ public class FilterOp<T extends Serializable> implements Queryable<T>, Operation
System.out.println(expr);
// expr = new ExpressionOptimizer().modify(expr);
expr = new ExpressionOptimizerBottomUp().modify(expr);
expr = new TableAliasInjector(getTableAlias()).modify(expr);
expr = new TableAliasInjector(getTableAlias()).modify(expr);//TODO this does not work together with expression optimization
this.predicateExpr = expr;
//TODO optimize whole expression
// this.finalExpr = new ExpressionOptimizerBottomUp().modify(
// new WhereExpression(
// new SelectExpression(List.of(DatabaseSelectAllExpression.INSTANCE), queryable.getExpression(), getTableAlias()),
// predicateExpr));
}
@Override
@@ -51,7 +57,7 @@ public class FilterOp<T extends Serializable> implements Queryable<T>, Operation
@Override
public Expression getExpression() {
return new WhereExpression(new SelectExpression(List.of(new DatabaseSelectAllExpression()), queryable.getExpression(), getTableAlias()), predicateExpr);
return new WhereExpression(new SelectExpression(List.of(DatabaseSelectAllExpression.INSTANCE), queryable.getExpression(), getTableAlias()), predicateExpr);
}
@Override

View File

@@ -27,7 +27,7 @@ public class LimitOp<T extends Serializable> implements Queryable<T> {
@Override
public Expression getExpression() {
return new LimitExpression(new SelectExpression(List.of(new DatabaseSelectAllExpression()), queryable.getExpression(), getTableAlias()), start, count);
return new LimitExpression(new SelectExpression(List.of(DatabaseSelectAllExpression.INSTANCE), queryable.getExpression(), getTableAlias()), start, count);
}
@Override

View File

@@ -37,7 +37,7 @@ public class SortOp<T extends Serializable> implements Queryable<T> {
@Override
public Expression getExpression() {
return new OrderExpression(new SelectExpression(List.of(new DatabaseSelectAllExpression()), queryable.getExpression(), getTableAlias()), sorts);
return new OrderExpression(new SelectExpression(List.of(DatabaseSelectAllExpression.INSTANCE), queryable.getExpression(), getTableAlias()), sorts);
}
@Override

View File

@@ -0,0 +1,5 @@
package jef.query;
public class MysqlQueryBuilder extends QueryBuilder {
}

View File

@@ -0,0 +1,173 @@
package jef.query;
import jef.expressions.AndExpression;
import jef.expressions.BinaryExpression;
import jef.expressions.ConstantExpression;
import jef.expressions.Expression;
import jef.expressions.FieldExpression;
import jef.expressions.IntermediateFieldExpression;
import jef.expressions.LimitExpression;
import jef.expressions.NullExpression;
import jef.expressions.OrExpression;
import jef.expressions.OrderExpression;
import jef.expressions.ParameterExpression;
import jef.expressions.SelectExpression;
import jef.expressions.TableExpression;
import jef.expressions.TernaryExpression;
import jef.expressions.UnaryExpression;
import jef.expressions.WhereExpression;
import jef.expressions.visitors.ExpressionVisitor;
import lombok.Getter;
import java.util.Collection;
import java.util.Objects;
import java.util.stream.Collectors;
public class QueryBuilder extends ExpressionVisitor {
@Getter
private String query = "";
public void visitAnd(AndExpression expr) {
for (int i = 0; i < expr.getExprs().size(); i++) {
visit(expr.getExprs().get(i));
if (i + 1 < expr.getExprs().size()) {
query += " AND ";
}
}
}
public void visitBinary(BinaryExpression expr) {
visit(expr.getLeft());
query += " " + expr.getOperator().getString() + " ";
visit(expr.getRight());
}
public void visitConstant(ConstantExpression expr) {
query += expr.getValue().toString();
}
public void visitField(FieldExpression expr) {
if (expr.getSchema() != null && !expr.getSchema().isBlank()) {
query += "`" + expr.getSchema() + "`.";
}
if (expr.getTable() != null && !expr.getTable().isBlank()) {
query += "`" + expr.getTable() + "`.";
}
query += "`" + expr.getValue().toString() + "`";
}
public void visitIntermediateField(IntermediateFieldExpression expr) {
query += expr.getValue().toString();
}
public void visitLimit(LimitExpression expr) {
visit(expr.getExpr());
if (expr.getStart() != null) {
query += " OFFSET " + expr.getStart();
}
if (expr.getCount() != null) {
query += " LIMIT " + expr.getCount();
}
}
public void visitNull(NullExpression expr) {
query += "NULL";
}
public void visitOr(OrExpression expr) {
for (int i = 0; i < expr.getExprs().size(); i++) {
visit(expr.getExprs().get(i));
if (i + 1 < expr.getExprs().size()) {
query += " OR ";
}
}
}
public void visitOrder(OrderExpression expr) {
if (expr.getSorts().isEmpty()) {
return;
}
visit(expr.getExpr());
query += " ORDER BY";
for (int i = 0; i < expr.getSorts().size(); i++) {
query += " ";
var sort = expr.getSorts().get(i);
if (sort.getExpr().getSchema() != null && !sort.getExpr().getSchema().isBlank()) {
query += "`" + sort.getExpr().getSchema() + "`.";
}
if (sort.getExpr().getTable() != null && !sort.getExpr().getTable().isBlank()) {
query += "`" + sort.getExpr().getTable() + "`.";
}
query += "`" + sort.getExpr().getName() + "`" + " " + sort.getDirection().getString();
if (i + 1 < expr.getSorts().size()) {
query += ",";
}
}
}
public void visitParameter(ParameterExpression expr) {
//TODO this function is super bad, ParameterExpression::toString also bad
if (expr.isInput()) {
query += "param #" + expr.getIndex();
} else if (expr.getValue() == null) {
query += "NULL";
} else if (expr.getValue() instanceof Collection) {
query += "(" + ((Collection<?>) expr.getValue()).stream().map(e -> e == null ? "NULL" : Objects.toString(e)).collect(Collectors.joining(", ")) + ")";
} else {
throw new UnsupportedOperationException();
}
}
public void visitSelect(SelectExpression expr) {
query += "SELECT ";
for (int i = 0; i < expr.getFields().size(); i++) {
query += expr.getFields().get(i).toString();
if (i + 1 < expr.getFields().size()) {
query += ", ";
}
}
query += " FROM ";
var wrap = expr.getFrom().getType() != Expression.Type.TABLE;
if (wrap) {
query += "(";
}
visit(expr.getFrom());
if (wrap) {
query += ")";
}
if (expr.getFromAlias().length() > 0 && expr.getFromAlias().charAt(0) >= 'a') {
query += " " + expr.getFromAlias();
}
}
public void visitTable(TableExpression expr) {
query += "`" + expr.getName() + "`";
}
public void visitTernary(TernaryExpression expr) {
query += "IF(";
visit(expr.getCond());
query += ", ";
visit(expr.getWhenTrue());
query += ", ";
visit(expr.getWhenFalse());
query += ")";
}
public void visitUnary(UnaryExpression expr) {
query += expr.getOperator().getString() + " ";
if (expr.getExpr().getPriority().getValue() < expr.getPriority().getValue()) {
query += "(";
}
visit(expr.getExpr());
if (expr.getExpr().getPriority().getValue() < expr.getPriority().getValue()) {
query += ")";
}
}
public void visitWhere(WhereExpression expr) {
visit(expr.getQueryable());
query += " WHERE ";
visit(expr.getWhere());
}
}