Skip to content

Commit 941a760

Browse files
authored
Initializes PartiQL's Engine (#1283)
1 parent 4ac1b3d commit 941a760

File tree

13 files changed

+361
-0
lines changed

13 files changed

+361
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package org.partiql.eval
2+
3+
import org.partiql.eval.impl.PartiQLEngineDefault
4+
import org.partiql.plan.PartiQLPlan
5+
import org.partiql.spi.Plugin
6+
import org.partiql.value.PartiQLValue
7+
import org.partiql.value.PartiQLValueExperimental
8+
9+
/**
10+
* PartiQL's Experimental Engine.
11+
*
12+
* It represents the execution of queries and does NOT represent the
13+
* maintenance of an individual's session. For example, by the time the engine is invoked, all functions
14+
* should be resolved via the SQL Path (which takes into consideration the user's current catalog/schema).
15+
*
16+
* This is in contrast to an actual application of PartiQL. Applications of PartiQL should instantiate a
17+
* [org.partiql.planner.PartiQLPlanner] and should pass in a user's session. This engine has no idea what the session is.
18+
* It assumes that the [org.partiql.plan.PartiQLPlan] has been resolved to accommodate session specifics.
19+
*
20+
* This engine also internalizes the mechanics of the engine itself. Internally, it creates a physical plan to operate on,
21+
* and it executes directly on that plan. The limited number of APIs exposed in this library is intentional to allow for
22+
* under-the-hood experimentation by the PartiQL Community.
23+
*/
24+
public interface PartiQLEngine {
25+
26+
public fun execute(plan: PartiQLPlan): Result
27+
28+
public enum class Implementation {
29+
DEFAULT
30+
}
31+
32+
public sealed interface Result {
33+
public data class Success @OptIn(PartiQLValueExperimental::class) constructor(
34+
val output: PartiQLValue
35+
) : Result
36+
37+
public data class Error @OptIn(PartiQLValueExperimental::class) constructor(
38+
val output: PartiQLValue
39+
) : Result
40+
}
41+
42+
public class Builder {
43+
44+
private var plugins: List<Plugin> = emptyList()
45+
private var implementation: Implementation = Implementation.DEFAULT
46+
47+
public fun withPlugins(plugins: List<Plugin>): Builder = this.apply {
48+
this.plugins = plugins
49+
}
50+
51+
public fun withImplementation(impl: Implementation): Builder = this.apply {
52+
this.implementation = impl
53+
}
54+
55+
public fun build(): PartiQLEngine = when (this.implementation) {
56+
Implementation.DEFAULT -> PartiQLEngineDefault()
57+
}
58+
}
59+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package org.partiql.eval.impl
2+
3+
import org.partiql.eval.PartiQLEngine
4+
import org.partiql.plan.PartiQLPlan
5+
import org.partiql.value.PartiQLValueExperimental
6+
7+
internal class PartiQLEngineDefault : PartiQLEngine {
8+
@OptIn(PartiQLValueExperimental::class)
9+
override fun execute(plan: PartiQLPlan): PartiQLEngine.Result {
10+
val expression = PlanToPhysical.convert(plan)
11+
val value = expression.evaluate(Record(emptyList()))
12+
return PartiQLEngine.Result.Success(value)
13+
}
14+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package org.partiql.eval.impl
2+
3+
import org.partiql.value.PartiQLValue
4+
import org.partiql.value.PartiQLValueExperimental
5+
6+
internal interface PhysicalNode {
7+
8+
interface Expression : PhysicalNode {
9+
@OptIn(PartiQLValueExperimental::class)
10+
fun evaluate(record: Record): PartiQLValue
11+
}
12+
13+
interface Relation : PhysicalNode {
14+
fun evaluate(): Iterator<Record>
15+
}
16+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
package org.partiql.eval.impl
2+
3+
import org.partiql.eval.impl.expression.Collection
4+
import org.partiql.eval.impl.expression.Literal
5+
import org.partiql.eval.impl.expression.Select
6+
import org.partiql.eval.impl.expression.Struct
7+
import org.partiql.eval.impl.expression.Variable
8+
import org.partiql.eval.impl.relation.Projection
9+
import org.partiql.eval.impl.relation.Scan
10+
import org.partiql.plan.PartiQLPlan
11+
import org.partiql.plan.PlanNode
12+
import org.partiql.plan.Rel
13+
import org.partiql.plan.Rex
14+
import org.partiql.plan.Statement
15+
import org.partiql.plan.visitor.PlanBaseVisitor
16+
import org.partiql.value.PartiQLValueExperimental
17+
18+
internal object PlanToPhysical {
19+
20+
fun convert(plan: PartiQLPlan): PhysicalNode.Expression {
21+
return PlanToCodeTransformer.visitPartiQLPlan(plan, Unit)
22+
}
23+
24+
private object PlanToCodeTransformer : PlanBaseVisitor<PhysicalNode, Unit>() {
25+
override fun defaultReturn(node: PlanNode, ctx: Unit): PhysicalNode {
26+
TODO("Not yet implemented")
27+
}
28+
29+
override fun visitRexOpStruct(node: Rex.Op.Struct, ctx: Unit): PhysicalNode {
30+
val fields = node.fields.map {
31+
Struct.Field(visitRex(it.k, ctx), visitRex(it.v, ctx))
32+
}
33+
return Struct(fields)
34+
}
35+
36+
override fun visitRexOpVar(node: Rex.Op.Var, ctx: Unit): PhysicalNode {
37+
return Variable(node.ref)
38+
}
39+
40+
override fun visitRexOpCollection(node: Rex.Op.Collection, ctx: Unit): PhysicalNode {
41+
val values = node.values.map { visitRex(it, ctx) }
42+
return Collection(values)
43+
}
44+
45+
override fun visitRelOpProject(node: Rel.Op.Project, ctx: Unit): PhysicalNode {
46+
val input = visitRel(node.input, ctx)
47+
val projections = node.projections.map { visitRex(it, ctx) }
48+
return Projection(input, projections)
49+
}
50+
51+
override fun visitRexOpSelect(node: Rex.Op.Select, ctx: Unit): PhysicalNode {
52+
val rel = visitRel(node.rel, ctx)
53+
val constructor = visitRex(node.constructor, ctx)
54+
return Select(rel, constructor)
55+
}
56+
57+
override fun visitRelOpScan(node: Rel.Op.Scan, ctx: Unit): PhysicalNode {
58+
val rex = visitRex(node.rex, ctx)
59+
return Scan(rex)
60+
}
61+
62+
@OptIn(PartiQLValueExperimental::class)
63+
override fun visitRexOpLit(node: Rex.Op.Lit, ctx: Unit): PhysicalNode {
64+
return Literal(node.value)
65+
}
66+
67+
override fun visitRel(node: Rel, ctx: Unit): PhysicalNode.Relation {
68+
return super.visitRelOp(node.op, ctx) as PhysicalNode.Relation
69+
}
70+
71+
override fun visitRex(node: Rex, ctx: Unit): PhysicalNode.Expression {
72+
return super.visitRexOp(node.op, ctx) as PhysicalNode.Expression
73+
}
74+
75+
// TODO: Re-look at
76+
override fun visitPartiQLPlan(node: PartiQLPlan, ctx: Unit): PhysicalNode.Expression {
77+
return visitStatement(node.statement, ctx) as PhysicalNode.Expression
78+
}
79+
80+
// TODO: Re-look at
81+
override fun visitStatementQuery(node: Statement.Query, ctx: Unit): PhysicalNode.Expression {
82+
return visitRex(node.root, ctx)
83+
}
84+
}
85+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package org.partiql.eval.impl
2+
3+
import org.partiql.value.PartiQLValue
4+
import org.partiql.value.PartiQLValueExperimental
5+
6+
internal class Record @OptIn(PartiQLValueExperimental::class) constructor(
7+
val values: List<PartiQLValue>
8+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package org.partiql.eval.impl.expression
2+
3+
import org.partiql.eval.impl.PhysicalNode
4+
import org.partiql.eval.impl.Record
5+
import org.partiql.value.PartiQLValue
6+
import org.partiql.value.PartiQLValueExperimental
7+
import org.partiql.value.bagValue
8+
9+
internal class Collection(
10+
private val values: List<PhysicalNode.Expression>
11+
) : PhysicalNode.Expression {
12+
@PartiQLValueExperimental
13+
override fun evaluate(record: Record): PartiQLValue {
14+
return bagValue(
15+
values.map { it.evaluate(record) }.asSequence()
16+
)
17+
}
18+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package org.partiql.eval.impl.expression
2+
3+
import org.partiql.eval.impl.PhysicalNode
4+
import org.partiql.eval.impl.Record
5+
import org.partiql.value.PartiQLValue
6+
import org.partiql.value.PartiQLValueExperimental
7+
8+
internal class Literal @OptIn(PartiQLValueExperimental::class) constructor(private val value: PartiQLValue) : PhysicalNode.Expression {
9+
@PartiQLValueExperimental
10+
override fun evaluate(record: Record): PartiQLValue {
11+
return value
12+
}
13+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package org.partiql.eval.impl.expression
2+
3+
import org.partiql.eval.impl.PhysicalNode
4+
import org.partiql.eval.impl.Record
5+
import org.partiql.value.PartiQLValue
6+
import org.partiql.value.PartiQLValueExperimental
7+
import org.partiql.value.bagValue
8+
9+
internal class Select(
10+
val input: PhysicalNode.Relation,
11+
val constructor: PhysicalNode.Expression
12+
) : PhysicalNode.Expression {
13+
@PartiQLValueExperimental
14+
override fun evaluate(record: Record): PartiQLValue {
15+
val elements = mutableListOf<PartiQLValue>()
16+
input.evaluate().forEach { record ->
17+
val element = constructor.evaluate(record)
18+
elements.add(element)
19+
}
20+
return bagValue(elements.asSequence())
21+
}
22+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package org.partiql.eval.impl.expression
2+
3+
import org.partiql.eval.impl.PhysicalNode
4+
import org.partiql.eval.impl.Record
5+
import org.partiql.value.PartiQLValue
6+
import org.partiql.value.PartiQLValueExperimental
7+
import org.partiql.value.StringValue
8+
import org.partiql.value.structValue
9+
10+
internal class Struct(val fields: List<Field>) : PhysicalNode.Expression {
11+
@OptIn(PartiQLValueExperimental::class)
12+
override fun evaluate(record: Record): PartiQLValue {
13+
val fields = fields.map {
14+
val key = it.key.evaluate(record) as? StringValue ?: error("Expected struct key to be a STRING.")
15+
val value = it.key.evaluate(record)
16+
key.value!! to value
17+
}
18+
return structValue(fields.asSequence())
19+
}
20+
21+
internal class Field(val key: PhysicalNode.Expression, val value: PhysicalNode.Expression)
22+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package org.partiql.eval.impl.expression
2+
3+
import org.partiql.eval.impl.PhysicalNode
4+
import org.partiql.eval.impl.Record
5+
import org.partiql.value.PartiQLValue
6+
import org.partiql.value.PartiQLValueExperimental
7+
8+
internal class Variable(private val index: Int) : PhysicalNode.Expression {
9+
@PartiQLValueExperimental
10+
override fun evaluate(record: Record): PartiQLValue {
11+
return record.values[index]
12+
}
13+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package org.partiql.eval.impl.relation
2+
3+
import org.partiql.eval.impl.PhysicalNode
4+
import org.partiql.eval.impl.Record
5+
import org.partiql.value.PartiQLValueExperimental
6+
7+
internal class Projection(
8+
private val input: PhysicalNode.Relation,
9+
val projections: List<PhysicalNode.Expression>
10+
) : PhysicalNode.Relation {
11+
@OptIn(PartiQLValueExperimental::class)
12+
override fun evaluate(): Iterator<Record> {
13+
14+
val inputIter = input.evaluate()
15+
16+
return object : Iterator<Record> {
17+
override fun hasNext(): Boolean {
18+
return inputIter.hasNext()
19+
}
20+
21+
override fun next(): Record {
22+
val inputRecord = inputIter.next()
23+
return Record(
24+
projections.map { it.evaluate(inputRecord) }
25+
)
26+
}
27+
}
28+
}
29+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package org.partiql.eval.impl.relation
2+
3+
import org.partiql.eval.impl.PhysicalNode
4+
import org.partiql.eval.impl.Record
5+
import org.partiql.value.CollectionValue
6+
import org.partiql.value.PartiQLValueExperimental
7+
8+
internal class Scan(
9+
private val expr: PhysicalNode.Expression
10+
) : PhysicalNode.Relation {
11+
12+
@OptIn(PartiQLValueExperimental::class)
13+
override fun evaluate(): Iterator<Record> {
14+
return when (val value = expr.evaluate(Record(emptyList()))) {
15+
is CollectionValue<*> -> value.elements!!.map { Record(listOf(it)) }.iterator()
16+
else -> iterator { yield(Record(listOf(value))) }
17+
}
18+
}
19+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package org.partiql.eval.impl
2+
3+
import org.junit.jupiter.api.Test
4+
import org.partiql.eval.PartiQLEngine
5+
import org.partiql.parser.PartiQLParserBuilder
6+
import org.partiql.planner.PartiQLPlanner
7+
import org.partiql.planner.PartiQLPlannerBuilder
8+
import org.partiql.value.BagValue
9+
import org.partiql.value.PartiQLValueExperimental
10+
import org.partiql.value.bagValue
11+
import org.partiql.value.int32Value
12+
import kotlin.test.assertEquals
13+
14+
class PartiQLEngineDefaultTest {
15+
16+
private val engine = PartiQLEngineDefault()
17+
private val planner = PartiQLPlannerBuilder().build()
18+
private val parser = PartiQLParserBuilder.standard().build()
19+
20+
@OptIn(PartiQLValueExperimental::class)
21+
@Test
22+
fun testLiterals() {
23+
val statement = parser.parse("SELECT VALUE 1 FROM <<0, 1>>;").root
24+
val session = PartiQLPlanner.Session("q", "u")
25+
val plan = planner.plan(statement, session)
26+
val result = engine.execute(plan.plan) as PartiQLEngine.Result.Success
27+
val output = result.output as BagValue<*>
28+
val expected = bagValue(sequenceOf(int32Value(1), int32Value(1)))
29+
assertEquals(expected, output)
30+
}
31+
32+
@OptIn(PartiQLValueExperimental::class)
33+
@Test
34+
fun testReference() {
35+
val statement = parser.parse("SELECT VALUE t FROM <<10, 20, 30>> AS t;").root
36+
val session = PartiQLPlanner.Session("q", "u")
37+
val plan = planner.plan(statement, session)
38+
val result = engine.execute(plan.plan) as PartiQLEngine.Result.Success
39+
val output = result.output as BagValue<*>
40+
val expected = bagValue(sequenceOf(int32Value(10), int32Value(20), int32Value(30)))
41+
assertEquals(expected, output)
42+
}
43+
}

0 commit comments

Comments
 (0)