added order by support

This commit is contained in:
wea_ondara
2022-07-15 12:51:16 +02:00
parent 85dede86f0
commit 5dae201bfb
11 changed files with 235 additions and 20 deletions

View File

@@ -1,9 +1,11 @@
package jef;
import jef.expressions.Expression;
import jef.operations.CountOp;
import jef.expressions.OrderExpression;
import jef.operations.FilterOp;
import jef.operations.LimitOp;
import jef.operations.SortOp;
import jef.serializable.SerializableFunction;
import jef.serializable.SerializablePredicate;
import java.io.Serializable;
@@ -93,14 +95,19 @@ public interface Queryable<T extends Serializable> {
// return null;
// }
//
// default Queryable<T> sorted() {
// return null;
// default SortOp<T> sorted() {//TODO sort by id
// return new SortOp(this);
// }
//
// default Queryable<T> sorted(Comparator<? super T> comparator) {
// return null;
// }
//
default <R> SortOp<T> sorted(SerializableFunction<? super T, R> fieldSelector) {
return new SortOp<T>(this, fieldSelector, OrderExpression.SortDirection.ASCENDING);
}
default <R> SortOp<T> sortedDescending(SerializableFunction<? super T, R> fieldSelector) {
return new SortOp<T>(this, fieldSelector, OrderExpression.SortDirection.DESCENDING);
}
//
// default Queryable<T> peek(Consumer<? super T> consumer) {
// return null;
// }

View File

@@ -1,21 +1,27 @@
package jef.asm;
import jef.expressions.Expression;
import jef.serializable.SerializableFunction;
import jef.serializable.SerializablePredicate;
import lombok.Getter;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.Opcodes;
import java.io.InputStream;
import java.io.Serializable;
import java.lang.invoke.SerializedLambda;
import java.util.stream.IntStream;
@Getter
public class PredicateParser {
private final SerializablePredicate<?> predicate;
public class AsmParser {
private final Serializable lambda;
public PredicateParser(SerializablePredicate<?> predicate) {
this.predicate = predicate;
public AsmParser(SerializablePredicate<?> predicate) {
this.lambda = predicate;
}
public AsmParser(SerializableFunction<?, ?> function) {
this.lambda = function;
}
public Expression parse() throws AsmParseException {
@@ -27,7 +33,7 @@ public class PredicateParser {
}
private Expression parseExpression() throws Exception {
var cls = predicate.getClass();
var cls = lambda.getClass();
var loader = cls.getClassLoader();
InputStream is;
// System.out.println(cls);
@@ -42,7 +48,7 @@ public class PredicateParser {
var x = cls.getDeclaredMethod("writeReplace");
// System.out.println(x);
x.setAccessible(true);
var serlambda = (SerializedLambda) x.invoke(predicate);
var serlambda = (SerializedLambda) x.invoke(lambda);
Object[] args = IntStream.range(0, serlambda.getCapturedArgCount()).mapToObj(serlambda::getCapturedArg).toArray();
// System.out.println(serlambda);

View File

@@ -38,7 +38,7 @@ class FilterMethodVisitor extends MethodVisitor {
private final String[] parameterClasses;
private final Object[] args;
private final Consumer<Expression> exprConsumer;
private Expression prediacteExpr;
private Expression lambdaExpr;
protected FilterMethodVisitor(int api, String descriptor, Object[] args, Consumer<Expression> exprConsumer) {
super(api);
@@ -153,7 +153,7 @@ class FilterMethodVisitor extends MethodVisitor {
case Opcodes.ICONST_3 -> varStack.push(ConstantExpression.V3);
case Opcodes.ICONST_4 -> varStack.push(ConstantExpression.V4);
case Opcodes.ICONST_5 -> varStack.push(ConstantExpression.V5);
case Opcodes.IRETURN -> {
case Opcodes.IRETURN, Opcodes.LRETURN, Opcodes.FRETURN, Opcodes.DRETURN, Opcodes.ARETURN -> {
//collapse conditions
for (int i = condStack.size() - 1; i >= 0; i--) {
condStack.get(i).e1 = varStack.pop();
@@ -161,7 +161,7 @@ class FilterMethodVisitor extends MethodVisitor {
evalCond(condStack.get(i));
}
// condStack.clear();
prediacteExpr = varStack.pop();
lambdaExpr = varStack.pop();
}
case Opcodes.FCMPL, Opcodes.FCMPG, Opcodes.DCMPL, Opcodes.DCMPG, Opcodes.LCMP -> {
var var2 = varStack.pop();
@@ -386,7 +386,7 @@ class FilterMethodVisitor extends MethodVisitor {
System.out.println("end");
super.visitEnd();
exprConsumer.accept(prediacteExpr);
exprConsumer.accept(lambdaExpr);
}
@Override
@@ -445,6 +445,10 @@ class FilterMethodVisitor extends MethodVisitor {
169, "RET",
172, "IRETURN",
173, "LRETURN",
174, "FRETURN",
175, "DRETURN",
176, "ARETURN",
//field
180, "GETFIELD",

View File

@@ -17,6 +17,7 @@ public interface Expression {
LIMIT,
NULL,
OR,
ORDER,
PARAMETER,
SELECT,
TABLE,

View File

@@ -0,0 +1,53 @@
package jef.expressions;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.ArrayList;
import java.util.List;
@Getter
@AllArgsConstructor
public class OrderExpression implements Expression {
private final Expression expr;
private final List<Sort> sorts;
@Override
public Type getType() {
return Type.ORDER;
}
@Override
public Priority getPriority() {
return Priority.UNDEFINED;
}
@Override
public String toString() {
var ret = expr.toString();
if (!sorts.isEmpty()) {
var sortStrings = new ArrayList<String>(sorts.size());
for (Sort sort : sorts) {
sortStrings.add(sort.getExpr() + " " + sort.getDirection().getS());
}
ret += " ORDER BY " + String.join(", ", sortStrings);
}
return ret;
}
@Getter
@AllArgsConstructor
public static class Sort {
private final FieldExpression expr;
private final SortDirection direction;
}
@Getter
@AllArgsConstructor
public enum SortDirection {
ASCENDING("ASC"),
DESCENDING("DESC"),
;
private final String s;
}
}

View File

@@ -9,6 +9,7 @@ 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;
@@ -31,6 +32,7 @@ public abstract class ExpressionModifier {
case LIMIT -> modifyLimit((LimitExpression) expr);
case NULL -> modifyNull((NullExpression) expr);
case OR -> modifyOr((OrExpression) expr);
case ORDER -> modifyOrder((OrderExpression) expr);
case PARAMETER -> modifyParameter((ParameterExpression) expr);
case SELECT -> modifySelect((SelectExpression) expr);
case TABLE -> modifyTable((TableExpression) expr);
@@ -85,6 +87,10 @@ public abstract class ExpressionModifier {
return expr;
}
public Expression modifyOrder(OrderExpression expr) {
return expr;
}
public Expression modifySelect(SelectExpression expr) {
return new SelectExpression(expr.getFields(), modify(expr.getFrom()), expr.getFromAlias());
}

View File

@@ -8,6 +8,7 @@ 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;
@@ -16,6 +17,7 @@ import jef.expressions.UnaryExpression;
import jef.expressions.WhereExpression;
import jef.expressions.selectable.SelectableExpression;
import java.util.ArrayList;
import java.util.Collection;
import java.util.stream.Collectors;
@@ -87,6 +89,18 @@ public class DebugExpressionVisitor extends ExpressionVisitor {
}
}
@Override
public void visitOrder(OrderExpression expr) {
visit(expr.getExpr());
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());
}
System.out.println(i() + " ORDER BY " + String.join(", ", sortStrings));
}
}
@Override
public void visitParameter(ParameterExpression expr) {
if (expr.getValue() instanceof Collection c) {

View File

@@ -9,6 +9,7 @@ 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;
@@ -27,6 +28,7 @@ public abstract class ExpressionVisitor {
case LIMIT -> visitLimit((LimitExpression) expr);
case NULL -> visitNull((NullExpression) expr);
case OR -> visitOr((OrExpression) expr);
case ORDER -> visitOrder((OrderExpression) expr);
case PARAMETER -> visitParameter((ParameterExpression) expr);
case SELECT -> visitSelect((SelectExpression) expr);
case TABLE -> visitTable((TableExpression) expr);
@@ -70,6 +72,9 @@ public abstract class ExpressionVisitor {
}
}
public void visitOrder(OrderExpression expr) {
}
public void visitParameter(ParameterExpression expr) {
}

View File

@@ -2,7 +2,7 @@ package jef.operations;
import jef.Queryable;
import jef.asm.AsmParseException;
import jef.asm.PredicateParser;
import jef.asm.AsmParser;
import jef.expressions.Expression;
import jef.expressions.SelectExpression;
import jef.expressions.WhereExpression;
@@ -26,7 +26,7 @@ public class FilterOp<T extends Serializable> implements Queryable<T>, Operation
public FilterOp(Queryable<T> queryable, SerializablePredicate<? super T> predicate) {
this.queryable = queryable;
this.predicate = predicate;
var parser = new PredicateParser(predicate);
var parser = new AsmParser(predicate);
Expression expr;
try {
expr = parser.parse();

View File

@@ -0,0 +1,82 @@
package jef.operations;
import jef.Queryable;
import jef.asm.AsmParseException;
import jef.asm.AsmParser;
import jef.expressions.Expression;
import jef.expressions.FieldExpression;
import jef.expressions.OrderExpression;
import jef.expressions.SelectExpression;
import jef.expressions.modifier.ExpressionOptimizerBottomUp;
import jef.expressions.modifier.IConst0Fixer;
import jef.expressions.modifier.TableAliasInjector;
import jef.expressions.modifier.TernaryRewriter;
import jef.expressions.selectable.DatabaseSelectAllExpression;
import jef.serializable.SerializableFunction;
import lombok.AllArgsConstructor;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
@AllArgsConstructor
public class SortOp<T extends Serializable> implements Queryable<T> {
private final Queryable<T> queryable;
private final List<OrderExpression.Sort> sorts = new ArrayList<>();
public SortOp(Queryable<T> queryable, SerializableFunction<? super T, ?> fieldSelector, OrderExpression.SortDirection direction) {
this.queryable = queryable;
var f = parseFunction(fieldSelector);
this.sorts.add(new OrderExpression.Sort(f, direction));
}
@Override
public String getTableAlias() {
return String.valueOf((char) (queryable.getTableAlias().charAt(0) + (char) 1));
}
@Override
public Expression getExpression() {
return new OrderExpression(new SelectExpression(List.of(new DatabaseSelectAllExpression()), queryable.getExpression(), getTableAlias()), sorts);
}
@Override
public String toString() {
return getExpression().toString();
}
public SortOp<T> thenSorted(SerializableFunction<? super T, ?> fieldSelector) {
var f = parseFunction(fieldSelector);
this.sorts.add(new OrderExpression.Sort(f, OrderExpression.SortDirection.ASCENDING));
return this;
}
public SortOp<T> thenSortedDescending(SerializableFunction<? super T, ?> fieldSelector) {
var f = parseFunction(fieldSelector);
this.sorts.add(new OrderExpression.Sort(f, OrderExpression.SortDirection.DESCENDING));
return this;
}
private FieldExpression parseFunction(SerializableFunction<? super T, ?> fieldSelector) {
var parser = new AsmParser(fieldSelector);
Expression expr;
try {
expr = parser.parse();
} catch (AsmParseException e) {
throw new RuntimeException(e);
}
System.out.println(expr);
expr = new TernaryRewriter().modify(expr);
System.out.println(expr);
expr = new IConst0Fixer().modify(expr);
System.out.println(expr);
// expr = new ExpressionOptimizer().modify(expr);
expr = new ExpressionOptimizerBottomUp().modify(expr);
expr = new TableAliasInjector(getTableAlias()).modify(expr);
if (expr instanceof FieldExpression f) {
return f;
} else {
throw new RuntimeException(expr + " is not a field");
}
}
}

View File

@@ -0,0 +1,37 @@
package jef.operations;
import jef.DBSet;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
class SortOpTest {
@Test
public void test() {
String act;
act = new DBSet<FilterOpTest.TestClass>("table1")
.sorted(e -> e.getI()).thenSorted(e -> e.getD()).thenSorted(e -> e.getF()).thenSorted(e -> e.getL()).thenSorted(e -> e.getO())
.toString();
Assertions.assertEquals("SELECT * FROM (SELECT * FROM `table1`) a ORDER BY `a`.`i` ASC, `a`.`d` ASC, `a`.`f` ASC, `a`.`l` ASC, `a`.`o` ASC", act);
act = new DBSet<FilterOpTest.TestClass>("table1")
.sortedDescending(e -> e.getI()).thenSortedDescending(e -> e.getD()).thenSortedDescending(e -> e.getF())
/**/.thenSortedDescending(e -> e.getL()).thenSortedDescending(e -> e.getO())
.toString();
Assertions.assertEquals("SELECT * FROM (SELECT * FROM `table1`) a ORDER BY `a`.`i` DESC, `a`.`d` DESC, `a`.`f` DESC, `a`.`l` DESC, `a`.`o` DESC", act);
//alternating patterns
act = new DBSet<FilterOpTest.TestClass>("table1")
.sortedDescending(e -> e.getI()).thenSorted(e -> e.getD()).thenSortedDescending(e -> e.getF()).thenSorted(e -> e.getL())
/**/.thenSortedDescending(e -> e.getO())
.toString();
Assertions.assertEquals("SELECT * FROM (SELECT * FROM `table1`) a ORDER BY `a`.`i` DESC, `a`.`d` ASC, `a`.`f` DESC, `a`.`l` ASC, `a`.`o` DESC", act);
act = new DBSet<FilterOpTest.TestClass>("table1")
.sorted(e -> e.getI()).thenSortedDescending(e -> e.getD()).thenSorted(e -> e.getF()).thenSortedDescending(e -> e.getL())
/**/.thenSorted(e -> e.getO())
.toString();
Assertions.assertEquals("SELECT * FROM (SELECT * FROM `table1`) a ORDER BY `a`.`i` ASC, `a`.`d` DESC, `a`.`f` ASC, `a`.`l` DESC, `a`.`o` ASC", act);
}
}