Skip to content

Commit 19886c3

Browse files
committed
Simplifies joins and fixes bugs
1 parent 607c4c0 commit 19886c3

File tree

14 files changed

+546
-196
lines changed

14 files changed

+546
-196
lines changed

partiql-eval/src/main/kotlin/org/partiql/eval/internal/Compiler.kt

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,9 @@ import org.partiql.eval.internal.operator.rel.RelFilter
1111
import org.partiql.eval.internal.operator.rel.RelIntersectAll
1212
import org.partiql.eval.internal.operator.rel.RelIntersectDistinct
1313
import org.partiql.eval.internal.operator.rel.RelJoinInner
14-
import org.partiql.eval.internal.operator.rel.RelJoinLeft
1514
import org.partiql.eval.internal.operator.rel.RelJoinOuterFull
16-
import org.partiql.eval.internal.operator.rel.RelJoinRight
15+
import org.partiql.eval.internal.operator.rel.RelJoinOuterLeft
16+
import org.partiql.eval.internal.operator.rel.RelJoinOuterRight
1717
import org.partiql.eval.internal.operator.rel.RelLimit
1818
import org.partiql.eval.internal.operator.rel.RelOffset
1919
import org.partiql.eval.internal.operator.rel.RelProject
@@ -364,9 +364,9 @@ internal class Compiler(
364364
val condition = visitRex(node.rex, ctx)
365365
return when (node.type) {
366366
Rel.Op.Join.Type.INNER -> RelJoinInner(lhs, rhs, condition)
367-
Rel.Op.Join.Type.LEFT -> RelJoinLeft(lhs, rhs, condition)
368-
Rel.Op.Join.Type.RIGHT -> RelJoinRight(lhs, rhs, condition)
369-
Rel.Op.Join.Type.FULL -> RelJoinOuterFull(lhs, rhs, condition)
367+
Rel.Op.Join.Type.LEFT -> RelJoinOuterLeft(lhs, rhs, condition, rhsType = node.rhs.type)
368+
Rel.Op.Join.Type.RIGHT -> RelJoinOuterRight(lhs, rhs, condition, lhsType = node.lhs.type)
369+
Rel.Op.Join.Type.FULL -> RelJoinOuterFull(lhs, rhs, condition, lhsType = node.lhs.type, rhsType = node.rhs.type)
370370
}
371371
}
372372

partiql-eval/src/main/kotlin/org/partiql/eval/internal/Environment.kt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,11 @@ internal class Environment(
1818

1919
@OptIn(PartiQLValueExperimental::class)
2020
operator fun get(index: Int): PartiQLValue {
21-
return this.bindings[index]
21+
try {
22+
return this.bindings[index]
23+
} catch (_: Throwable) {
24+
throw IllegalStateException("Received error when searching for binding at index $index. Current bindings are: $this.")
25+
}
2226
}
2327

2428
@OptIn(PartiQLValueExperimental::class)
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
package org.partiql.eval.internal.helpers
2+
3+
import org.partiql.types.AnyOfType
4+
import org.partiql.types.AnyType
5+
import org.partiql.types.BagType
6+
import org.partiql.types.BlobType
7+
import org.partiql.types.BoolType
8+
import org.partiql.types.ClobType
9+
import org.partiql.types.DateType
10+
import org.partiql.types.DecimalType
11+
import org.partiql.types.FloatType
12+
import org.partiql.types.GraphType
13+
import org.partiql.types.IntType
14+
import org.partiql.types.ListType
15+
import org.partiql.types.MissingType
16+
import org.partiql.types.NullType
17+
import org.partiql.types.SexpType
18+
import org.partiql.types.StaticType
19+
import org.partiql.types.StringType
20+
import org.partiql.types.StructType
21+
import org.partiql.types.SymbolType
22+
import org.partiql.types.TimeType
23+
import org.partiql.types.TimestampType
24+
import org.partiql.value.PartiQLValueExperimental
25+
import org.partiql.value.PartiQLValueType
26+
27+
internal object TypesUtility {
28+
29+
@OptIn(PartiQLValueExperimental::class)
30+
internal fun StaticType.toRuntimeType(): PartiQLValueType {
31+
if (this is AnyOfType) {
32+
// handle anyOf(null, T) cases
33+
val t = types.filter { it !is NullType && it !is MissingType }
34+
return if (t.size != 1) {
35+
PartiQLValueType.ANY
36+
} else {
37+
t.first().asRuntimeType()
38+
}
39+
}
40+
return this.asRuntimeType()
41+
}
42+
43+
@OptIn(PartiQLValueExperimental::class)
44+
private fun StaticType.asRuntimeType(): PartiQLValueType = when (this) {
45+
is AnyOfType -> PartiQLValueType.ANY
46+
is AnyType -> PartiQLValueType.ANY
47+
is BlobType -> PartiQLValueType.BLOB
48+
is BoolType -> PartiQLValueType.BOOL
49+
is ClobType -> PartiQLValueType.CLOB
50+
is BagType -> PartiQLValueType.BAG
51+
is ListType -> PartiQLValueType.LIST
52+
is SexpType -> PartiQLValueType.SEXP
53+
is DateType -> PartiQLValueType.DATE
54+
// TODO: Run time decimal type does not model precision scale constraint yet
55+
// despite that we match to Decimal vs Decimal_ARBITRARY (PVT) here
56+
// but when mapping it back to Static Type, (i.e, mapping function return type to Value Type)
57+
// we can only map to Unconstrained decimal (Static Type)
58+
is DecimalType -> {
59+
when (this.precisionScaleConstraint) {
60+
is DecimalType.PrecisionScaleConstraint.Constrained -> PartiQLValueType.DECIMAL
61+
DecimalType.PrecisionScaleConstraint.Unconstrained -> PartiQLValueType.DECIMAL_ARBITRARY
62+
}
63+
}
64+
is FloatType -> PartiQLValueType.FLOAT64
65+
is GraphType -> error("Graph type missing from runtime types")
66+
is IntType -> when (this.rangeConstraint) {
67+
IntType.IntRangeConstraint.SHORT -> PartiQLValueType.INT16
68+
IntType.IntRangeConstraint.INT4 -> PartiQLValueType.INT32
69+
IntType.IntRangeConstraint.LONG -> PartiQLValueType.INT64
70+
IntType.IntRangeConstraint.UNCONSTRAINED -> PartiQLValueType.INT
71+
}
72+
MissingType -> PartiQLValueType.MISSING
73+
is NullType -> PartiQLValueType.NULL
74+
is StringType -> PartiQLValueType.STRING
75+
is StructType -> PartiQLValueType.STRUCT
76+
is SymbolType -> PartiQLValueType.SYMBOL
77+
is TimeType -> PartiQLValueType.TIME
78+
is TimestampType -> PartiQLValueType.TIMESTAMP
79+
}
80+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package org.partiql.eval.internal.helpers
2+
3+
import org.partiql.value.BoolValue
4+
import org.partiql.value.PartiQLValue
5+
import org.partiql.value.PartiQLValueExperimental
6+
7+
/**
8+
* Holds helper functions for [PartiQLValue].
9+
*/
10+
internal object ValueUtility {
11+
12+
/**
13+
* @return whether the value is a boolean and the value itself is not-null and true.
14+
*/
15+
@OptIn(PartiQLValueExperimental::class)
16+
@JvmStatic
17+
fun PartiQLValue.isTrue(): Boolean {
18+
return this is BoolValue && this.value == true
19+
}
20+
}
Lines changed: 54 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,64 @@
11
package org.partiql.eval.internal.operator.rel
22

3+
import org.partiql.eval.internal.Environment
34
import org.partiql.eval.internal.Record
5+
import org.partiql.eval.internal.helpers.ValueUtility.isTrue
46
import org.partiql.eval.internal.operator.Operator
7+
import org.partiql.value.PartiQLValueExperimental
58

69
internal class RelJoinInner(
7-
override val lhs: Operator.Relation,
8-
override val rhs: Operator.Relation,
9-
override val condition: Operator.Expr,
10-
) : RelJoinNestedLoop() {
11-
override fun join(condition: Boolean, lhs: Record, rhs: Record): Record? {
12-
return when (condition) {
13-
true -> lhs + rhs
10+
private val lhs: Operator.Relation,
11+
private val rhs: Operator.Relation,
12+
private val condition: Operator.Expr,
13+
) : RelPeeking() {
14+
15+
private lateinit var env: Environment
16+
private lateinit var iterator: Iterator<Record>
17+
18+
override fun openPeeking(env: Environment) {
19+
this.env = env
20+
lhs.open(env)
21+
iterator = implementation()
22+
}
23+
24+
override fun peek(): Record? {
25+
return when (iterator.hasNext()) {
26+
true -> iterator.next()
1427
false -> null
1528
}
1629
}
30+
31+
override fun closePeeking() {
32+
lhs.close()
33+
rhs.close()
34+
iterator = emptyList<Record>().iterator()
35+
}
36+
37+
/**
38+
* INNER JOIN (LATERAL)
39+
*
40+
* Algorithm:
41+
* ```
42+
* for lhsRecord in lhs:
43+
* for rhsRecord in rhs(lhsRecord):
44+
* if (condition matches):
45+
* conditionMatched = true
46+
* yield(lhsRecord + rhsRecord)
47+
* ```
48+
*
49+
* Development Note: The non-lateral version wouldn't need to push to the current environment.
50+
*/
51+
@OptIn(PartiQLValueExperimental::class)
52+
private fun implementation() = iterator {
53+
for (lhsRecord in lhs) {
54+
rhs.open(env.push(lhsRecord))
55+
for (rhsRecord in rhs) {
56+
val input = lhsRecord + rhsRecord
57+
val result = condition.eval(env.push(input))
58+
if (result.isTrue()) {
59+
yield(lhsRecord + rhsRecord)
60+
}
61+
}
62+
}
63+
}
1764
}

partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rel/RelJoinLeft.kt

Lines changed: 0 additions & 18 deletions
This file was deleted.

partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rel/RelJoinNestedLoop.kt

Lines changed: 0 additions & 98 deletions
This file was deleted.

0 commit comments

Comments
 (0)