Skip to content

Commit ee8121d

Browse files
authored
VortexExpr VTables (#3713)
Adds the same vtable machinery as arrays and layouts already use. It uses the "Encoding" naming scheme from arrays and layouts. I don't particularly like it, but it's consistent. Open to renames later. Further, adds an expression registry to the Vortex session that will be used for deserialization. Expressions only decide their "options" serialization. So in theory, can support many container formats, not just proto, provided each expression can deserialize their own options format. --------- Signed-off-by: Nicholas Gates <[email protected]>
1 parent a4217f3 commit ee8121d

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

73 files changed

+2769
-2436
lines changed

Cargo.lock

Lines changed: 9 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ datafusion-common = { version = "48" }
8888
datafusion-physical-plan = { version = "48" }
8989
dirs = "6.0.0"
9090
divan = { package = "codspeed-divan-compat", version = "3.0" }
91+
dyn-eq = "0.1.3"
9192
dyn-hash = "0.2.0"
9293
enum-iterator = "2.0.0"
9394
erased-serde = "0.4"

java/testfiles/Cargo.lock

Lines changed: 10 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

java/vortex-jni/src/main/java/dev/vortex/api/Expression.java

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,31 @@
55

66
import dev.vortex.api.expressions.*;
77

8+
import java.util.List;
9+
import java.util.Optional;
10+
811
/**
912
* Vortex expression language.
1013
*/
1114
public interface Expression {
12-
String type();
13-
14-
<T> T accept(Visitor<T> visitor);
15+
/**
16+
* The globally unique identifier for this type of expression.
17+
*/
18+
String id();
19+
20+
/**
21+
* Returns the children of this expression.
22+
*/
23+
List<Expression> children();
24+
25+
/**
26+
* Returns the serialized metadata for this expression, or empty if serialization is not supported.
27+
*/
28+
Optional<byte[]> metadata();
29+
30+
default <T> T accept(Visitor<T> visitor) {
31+
return visitor.visitOther(this);
32+
}
1533

1634
interface Visitor<T> {
1735
T visitLiteral(Literal<?> literal);
@@ -23,5 +41,10 @@ interface Visitor<T> {
2341
T visitNot(Not not);
2442

2543
T visitGetItem(GetItem getItem);
44+
45+
/**
46+
* For expressions that do not have a specific visitor method.
47+
*/
48+
T visitOther(Expression expression);
2649
}
2750
}

java/vortex-jni/src/main/java/dev/vortex/api/expressions/Binary.java

Lines changed: 79 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,13 @@
33

44
package dev.vortex.api.expressions;
55

6+
import com.google.protobuf.InvalidProtocolBufferException;
67
import dev.vortex.api.Expression;
8+
import dev.vortex.proto.ExprProtos;
9+
10+
import java.util.List;
711
import java.util.Objects;
12+
import java.util.Optional;
813
import java.util.stream.Stream;
914

1015
public final class Binary implements Expression {
@@ -18,6 +23,20 @@ private Binary(BinaryOp operator, Expression left, Expression right) {
1823
this.right = right;
1924
}
2025

26+
public static Binary parse(byte[] metadata, List<Expression> children) {
27+
if (children.size() != 2) {
28+
throw new IllegalArgumentException(
29+
"Binary expression must have exactly two children, found: " + children.size());
30+
}
31+
try {
32+
ExprProtos.BinaryOpts opts = ExprProtos.BinaryOpts.parseFrom(metadata);
33+
BinaryOp operator = BinaryOp.fromProto(opts.getOp());
34+
return new Binary(operator, children.get(0), children.get(1));
35+
} catch (InvalidProtocolBufferException e) {
36+
throw new IllegalArgumentException("Failed to parse Binary metadata", e);
37+
}
38+
}
39+
2140
public static Binary of(BinaryOp operator, Expression left, Expression right) {
2241
return new Binary(operator, left, right);
2342
}
@@ -57,10 +76,23 @@ public static Binary ltEq(Expression left, Expression right) {
5776
}
5877

5978
@Override
60-
public String type() {
79+
public String id() {
6180
return "binary";
6281
}
6382

83+
@Override
84+
public List<Expression> children() {
85+
return List.of(left, right);
86+
}
87+
88+
@Override
89+
public Optional<byte[]> metadata() {
90+
return Optional.of(ExprProtos.BinaryOpts.newBuilder()
91+
.setOp(operator.toProto())
92+
.build()
93+
.toByteArray());
94+
}
95+
6496
@Override
6597
public String toString() {
6698
return "(" + left + " " + operator + " " + right + ")";
@@ -131,5 +163,51 @@ public String toString() {
131163
throw new IllegalStateException("Unknown Operator: " + this);
132164
}
133165
}
166+
167+
static BinaryOp fromProto(ExprProtos.BinaryOpts.BinaryOp proto) {
168+
switch (proto) {
169+
case Eq:
170+
return EQ;
171+
case NotEq:
172+
return NOT_EQ;
173+
case Gt:
174+
return GT;
175+
case Gte:
176+
return GT_EQ;
177+
case Lt:
178+
return LT;
179+
case Lte:
180+
return LT_EQ;
181+
case And:
182+
return AND;
183+
case Or:
184+
return OR;
185+
default:
186+
throw new IllegalArgumentException("Unsupported binary operator proto: " + proto);
187+
}
188+
}
189+
190+
ExprProtos.BinaryOpts.BinaryOp toProto() {
191+
switch (this) {
192+
case EQ:
193+
return ExprProtos.BinaryOpts.BinaryOp.Eq;
194+
case NOT_EQ:
195+
return ExprProtos.BinaryOpts.BinaryOp.NotEq;
196+
case GT:
197+
return ExprProtos.BinaryOpts.BinaryOp.Gt;
198+
case GT_EQ:
199+
return ExprProtos.BinaryOpts.BinaryOp.Gte;
200+
case LT:
201+
return ExprProtos.BinaryOpts.BinaryOp.Lt;
202+
case LT_EQ:
203+
return ExprProtos.BinaryOpts.BinaryOp.Lte;
204+
case AND:
205+
return ExprProtos.BinaryOpts.BinaryOp.And;
206+
case OR:
207+
return ExprProtos.BinaryOpts.BinaryOp.Or;
208+
default:
209+
throw new IllegalArgumentException("Unsupported binary operator: " + this);
210+
}
211+
}
134212
}
135213
}

java/vortex-jni/src/main/java/dev/vortex/api/expressions/GetItem.java

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,13 @@
33

44
package dev.vortex.api.expressions;
55

6+
import com.google.protobuf.InvalidProtocolBufferException;
67
import dev.vortex.api.Expression;
8+
import dev.vortex.proto.ExprProtos;
9+
10+
import java.util.List;
711
import java.util.Objects;
12+
import java.util.Optional;
813

914
public final class GetItem implements Expression {
1015
private final String path;
@@ -19,6 +24,19 @@ public static GetItem of(Expression child, String path) {
1924
return new GetItem(child, path);
2025
}
2126

27+
public static GetItem parse(byte[] metadata, List<Expression> children) {
28+
if (children.size() != 1) {
29+
throw new IllegalArgumentException(
30+
"GetItem expression must have exactly one child, found: " + children.size());
31+
}
32+
try {
33+
ExprProtos.GetItemOpts opts = ExprProtos.GetItemOpts.parseFrom(metadata);
34+
return new GetItem(children.get(0), opts.getPath());
35+
} catch (InvalidProtocolBufferException e) {
36+
throw new IllegalArgumentException("Failed to parse GetItem metadata", e);
37+
}
38+
}
39+
2240
public Expression getChild() {
2341
return child;
2442
}
@@ -28,10 +46,21 @@ public String getPath() {
2846
}
2947

3048
@Override
31-
public String type() {
49+
public String id() {
3250
return "get_item";
3351
}
3452

53+
@Override
54+
public List<Expression> children() {
55+
return List.of(child);
56+
}
57+
58+
@Override
59+
public Optional<byte[]> metadata() {
60+
return Optional.of(
61+
ExprProtos.GetItemOpts.newBuilder().setPath(path).build().toByteArray());
62+
}
63+
3564
@Override
3665
public <T> T accept(Visitor<T> visitor) {
3766
return visitor.visitGetItem(this);

java/vortex-jni/src/main/java/dev/vortex/api/expressions/Identity.java

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,51 @@
44
package dev.vortex.api.expressions;
55

66
import dev.vortex.api.Expression;
7+
import dev.vortex.proto.ExprProtos;
8+
9+
import java.util.List;
10+
import java.util.Optional;
711

812
public final class Identity implements Expression {
913
public static final Identity INSTANCE = new Identity();
1014

1115
private Identity() {}
1216

17+
public static Identity parse(byte[] metadata, List<Expression> children) {
18+
if (!children.isEmpty()) {
19+
throw new IllegalArgumentException("Identity expression must have no children, found: " + children.size());
20+
}
21+
try {
22+
ExprProtos.VarOpts varOpts = ExprProtos.VarOpts.parseFrom(metadata);
23+
if (!varOpts.getVar().isEmpty()) {
24+
throw new IllegalArgumentException(
25+
"Identity expression must have empty var name, found: " + varOpts.getVar());
26+
}
27+
return INSTANCE;
28+
} catch (Exception e) {
29+
throw new IllegalArgumentException("Failed to parse metadata for Identity expression", e);
30+
}
31+
}
32+
1333
@Override
14-
public String type() {
15-
return "identity";
34+
public String id() {
35+
return "var";
36+
}
37+
38+
@Override
39+
public List<Expression> children() {
40+
return List.of();
41+
}
42+
43+
@Override
44+
public Optional<byte[]> metadata() {
45+
// TODO(ngates): currently Vortex has a Var expression, but this will be reverted back
46+
// to an Identity expression in the future.
47+
return Optional.of(ExprProtos.VarOpts.newBuilder()
48+
// Identity has empty var name
49+
.setVar("")
50+
.build()
51+
.toByteArray());
1652
}
1753

1854
@Override

0 commit comments

Comments
 (0)