added order by support
This commit is contained in:
@@ -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,13 +95,18 @@ public interface Queryable<T extends Serializable> {
|
||||
// return null;
|
||||
// }
|
||||
//
|
||||
// default Queryable<T> sorted() {
|
||||
// return null;
|
||||
// }
|
||||
//
|
||||
// default Queryable<T> sorted(Comparator<? super T> comparator) {
|
||||
// return null;
|
||||
// default SortOp<T> sorted() {//TODO sort by id
|
||||
// return new SortOp(this);
|
||||
// }
|
||||
|
||||
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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -17,6 +17,7 @@ public interface Expression {
|
||||
LIMIT,
|
||||
NULL,
|
||||
OR,
|
||||
ORDER,
|
||||
PARAMETER,
|
||||
SELECT,
|
||||
TABLE,
|
||||
|
||||
53
src/main/java/jef/expressions/OrderExpression.java
Normal file
53
src/main/java/jef/expressions/OrderExpression.java
Normal 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;
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
82
src/main/java/jef/operations/SortOp.java
Normal file
82
src/main/java/jef/operations/SortOp.java
Normal 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");
|
||||
}
|
||||
}
|
||||
}
|
||||
37
src/test/java/jef/operations/SortOpTest.java
Normal file
37
src/test/java/jef/operations/SortOpTest.java
Normal 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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user