-
Notifications
You must be signed in to change notification settings - Fork 63
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Initializes PartiQL's Engine (#1283)
- Loading branch information
1 parent
a216777
commit 56332d9
Showing
13 changed files
with
361 additions
and
0 deletions.
There are no files selected for viewing
59 changes: 59 additions & 0 deletions
59
partiql-eval/src/main/kotlin/org/partiql/eval/PartiQLEngine.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
package org.partiql.eval | ||
|
||
import org.partiql.eval.impl.PartiQLEngineDefault | ||
import org.partiql.plan.PartiQLPlan | ||
import org.partiql.spi.Plugin | ||
import org.partiql.value.PartiQLValue | ||
import org.partiql.value.PartiQLValueExperimental | ||
|
||
/** | ||
* PartiQL's Experimental Engine. | ||
* | ||
* It represents the execution of queries and does NOT represent the | ||
* maintenance of an individual's session. For example, by the time the engine is invoked, all functions | ||
* should be resolved via the SQL Path (which takes into consideration the user's current catalog/schema). | ||
* | ||
* This is in contrast to an actual application of PartiQL. Applications of PartiQL should instantiate a | ||
* [org.partiql.planner.PartiQLPlanner] and should pass in a user's session. This engine has no idea what the session is. | ||
* It assumes that the [org.partiql.plan.PartiQLPlan] has been resolved to accommodate session specifics. | ||
* | ||
* This engine also internalizes the mechanics of the engine itself. Internally, it creates a physical plan to operate on, | ||
* and it executes directly on that plan. The limited number of APIs exposed in this library is intentional to allow for | ||
* under-the-hood experimentation by the PartiQL Community. | ||
*/ | ||
public interface PartiQLEngine { | ||
|
||
public fun execute(plan: PartiQLPlan): Result | ||
|
||
public enum class Implementation { | ||
DEFAULT | ||
} | ||
|
||
public sealed interface Result { | ||
public data class Success @OptIn(PartiQLValueExperimental::class) constructor( | ||
val output: PartiQLValue | ||
) : Result | ||
|
||
public data class Error @OptIn(PartiQLValueExperimental::class) constructor( | ||
val output: PartiQLValue | ||
) : Result | ||
} | ||
|
||
public class Builder { | ||
|
||
private var plugins: List<Plugin> = emptyList() | ||
private var implementation: Implementation = Implementation.DEFAULT | ||
|
||
public fun withPlugins(plugins: List<Plugin>): Builder = this.apply { | ||
this.plugins = plugins | ||
} | ||
|
||
public fun withImplementation(impl: Implementation): Builder = this.apply { | ||
this.implementation = impl | ||
} | ||
|
||
public fun build(): PartiQLEngine = when (this.implementation) { | ||
Implementation.DEFAULT -> PartiQLEngineDefault() | ||
} | ||
} | ||
} |
14 changes: 14 additions & 0 deletions
14
partiql-eval/src/main/kotlin/org/partiql/eval/impl/PartiQLEngineDefault.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
package org.partiql.eval.impl | ||
|
||
import org.partiql.eval.PartiQLEngine | ||
import org.partiql.plan.PartiQLPlan | ||
import org.partiql.value.PartiQLValueExperimental | ||
|
||
internal class PartiQLEngineDefault : PartiQLEngine { | ||
@OptIn(PartiQLValueExperimental::class) | ||
override fun execute(plan: PartiQLPlan): PartiQLEngine.Result { | ||
val expression = PlanToPhysical.convert(plan) | ||
val value = expression.evaluate(Record(emptyList())) | ||
return PartiQLEngine.Result.Success(value) | ||
} | ||
} |
16 changes: 16 additions & 0 deletions
16
partiql-eval/src/main/kotlin/org/partiql/eval/impl/PhysicalNode.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
package org.partiql.eval.impl | ||
|
||
import org.partiql.value.PartiQLValue | ||
import org.partiql.value.PartiQLValueExperimental | ||
|
||
internal interface PhysicalNode { | ||
|
||
interface Expression : PhysicalNode { | ||
@OptIn(PartiQLValueExperimental::class) | ||
fun evaluate(record: Record): PartiQLValue | ||
} | ||
|
||
interface Relation : PhysicalNode { | ||
fun evaluate(): Iterator<Record> | ||
} | ||
} |
85 changes: 85 additions & 0 deletions
85
partiql-eval/src/main/kotlin/org/partiql/eval/impl/PlanToPhysical.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
package org.partiql.eval.impl | ||
|
||
import org.partiql.eval.impl.expression.Collection | ||
import org.partiql.eval.impl.expression.Literal | ||
import org.partiql.eval.impl.expression.Select | ||
import org.partiql.eval.impl.expression.Struct | ||
import org.partiql.eval.impl.expression.Variable | ||
import org.partiql.eval.impl.relation.Projection | ||
import org.partiql.eval.impl.relation.Scan | ||
import org.partiql.plan.PartiQLPlan | ||
import org.partiql.plan.PlanNode | ||
import org.partiql.plan.Rel | ||
import org.partiql.plan.Rex | ||
import org.partiql.plan.Statement | ||
import org.partiql.plan.visitor.PlanBaseVisitor | ||
import org.partiql.value.PartiQLValueExperimental | ||
|
||
internal object PlanToPhysical { | ||
|
||
fun convert(plan: PartiQLPlan): PhysicalNode.Expression { | ||
return PlanToCodeTransformer.visitPartiQLPlan(plan, Unit) | ||
} | ||
|
||
private object PlanToCodeTransformer : PlanBaseVisitor<PhysicalNode, Unit>() { | ||
override fun defaultReturn(node: PlanNode, ctx: Unit): PhysicalNode { | ||
TODO("Not yet implemented") | ||
} | ||
|
||
override fun visitRexOpStruct(node: Rex.Op.Struct, ctx: Unit): PhysicalNode { | ||
val fields = node.fields.map { | ||
Struct.Field(visitRex(it.k, ctx), visitRex(it.v, ctx)) | ||
} | ||
return Struct(fields) | ||
} | ||
|
||
override fun visitRexOpVar(node: Rex.Op.Var, ctx: Unit): PhysicalNode { | ||
return Variable(node.ref) | ||
} | ||
|
||
override fun visitRexOpCollection(node: Rex.Op.Collection, ctx: Unit): PhysicalNode { | ||
val values = node.values.map { visitRex(it, ctx) } | ||
return Collection(values) | ||
} | ||
|
||
override fun visitRelOpProject(node: Rel.Op.Project, ctx: Unit): PhysicalNode { | ||
val input = visitRel(node.input, ctx) | ||
val projections = node.projections.map { visitRex(it, ctx) } | ||
return Projection(input, projections) | ||
} | ||
|
||
override fun visitRexOpSelect(node: Rex.Op.Select, ctx: Unit): PhysicalNode { | ||
val rel = visitRel(node.rel, ctx) | ||
val constructor = visitRex(node.constructor, ctx) | ||
return Select(rel, constructor) | ||
} | ||
|
||
override fun visitRelOpScan(node: Rel.Op.Scan, ctx: Unit): PhysicalNode { | ||
val rex = visitRex(node.rex, ctx) | ||
return Scan(rex) | ||
} | ||
|
||
@OptIn(PartiQLValueExperimental::class) | ||
override fun visitRexOpLit(node: Rex.Op.Lit, ctx: Unit): PhysicalNode { | ||
return Literal(node.value) | ||
} | ||
|
||
override fun visitRel(node: Rel, ctx: Unit): PhysicalNode.Relation { | ||
return super.visitRelOp(node.op, ctx) as PhysicalNode.Relation | ||
} | ||
|
||
override fun visitRex(node: Rex, ctx: Unit): PhysicalNode.Expression { | ||
return super.visitRexOp(node.op, ctx) as PhysicalNode.Expression | ||
} | ||
|
||
// TODO: Re-look at | ||
override fun visitPartiQLPlan(node: PartiQLPlan, ctx: Unit): PhysicalNode.Expression { | ||
return visitStatement(node.statement, ctx) as PhysicalNode.Expression | ||
} | ||
|
||
// TODO: Re-look at | ||
override fun visitStatementQuery(node: Statement.Query, ctx: Unit): PhysicalNode.Expression { | ||
return visitRex(node.root, ctx) | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
package org.partiql.eval.impl | ||
|
||
import org.partiql.value.PartiQLValue | ||
import org.partiql.value.PartiQLValueExperimental | ||
|
||
internal class Record @OptIn(PartiQLValueExperimental::class) constructor( | ||
val values: List<PartiQLValue> | ||
) |
18 changes: 18 additions & 0 deletions
18
partiql-eval/src/main/kotlin/org/partiql/eval/impl/expression/Collection.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
package org.partiql.eval.impl.expression | ||
|
||
import org.partiql.eval.impl.PhysicalNode | ||
import org.partiql.eval.impl.Record | ||
import org.partiql.value.PartiQLValue | ||
import org.partiql.value.PartiQLValueExperimental | ||
import org.partiql.value.bagValue | ||
|
||
internal class Collection( | ||
private val values: List<PhysicalNode.Expression> | ||
) : PhysicalNode.Expression { | ||
@PartiQLValueExperimental | ||
override fun evaluate(record: Record): PartiQLValue { | ||
return bagValue( | ||
values.map { it.evaluate(record) }.asSequence() | ||
) | ||
} | ||
} |
13 changes: 13 additions & 0 deletions
13
partiql-eval/src/main/kotlin/org/partiql/eval/impl/expression/Literal.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
package org.partiql.eval.impl.expression | ||
|
||
import org.partiql.eval.impl.PhysicalNode | ||
import org.partiql.eval.impl.Record | ||
import org.partiql.value.PartiQLValue | ||
import org.partiql.value.PartiQLValueExperimental | ||
|
||
internal class Literal @OptIn(PartiQLValueExperimental::class) constructor(private val value: PartiQLValue) : PhysicalNode.Expression { | ||
@PartiQLValueExperimental | ||
override fun evaluate(record: Record): PartiQLValue { | ||
return value | ||
} | ||
} |
22 changes: 22 additions & 0 deletions
22
partiql-eval/src/main/kotlin/org/partiql/eval/impl/expression/Select.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
package org.partiql.eval.impl.expression | ||
|
||
import org.partiql.eval.impl.PhysicalNode | ||
import org.partiql.eval.impl.Record | ||
import org.partiql.value.PartiQLValue | ||
import org.partiql.value.PartiQLValueExperimental | ||
import org.partiql.value.bagValue | ||
|
||
internal class Select( | ||
val input: PhysicalNode.Relation, | ||
val constructor: PhysicalNode.Expression | ||
) : PhysicalNode.Expression { | ||
@PartiQLValueExperimental | ||
override fun evaluate(record: Record): PartiQLValue { | ||
val elements = mutableListOf<PartiQLValue>() | ||
input.evaluate().forEach { record -> | ||
val element = constructor.evaluate(record) | ||
elements.add(element) | ||
} | ||
return bagValue(elements.asSequence()) | ||
} | ||
} |
22 changes: 22 additions & 0 deletions
22
partiql-eval/src/main/kotlin/org/partiql/eval/impl/expression/Struct.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
package org.partiql.eval.impl.expression | ||
|
||
import org.partiql.eval.impl.PhysicalNode | ||
import org.partiql.eval.impl.Record | ||
import org.partiql.value.PartiQLValue | ||
import org.partiql.value.PartiQLValueExperimental | ||
import org.partiql.value.StringValue | ||
import org.partiql.value.structValue | ||
|
||
internal class Struct(val fields: List<Field>) : PhysicalNode.Expression { | ||
@OptIn(PartiQLValueExperimental::class) | ||
override fun evaluate(record: Record): PartiQLValue { | ||
val fields = fields.map { | ||
val key = it.key.evaluate(record) as? StringValue ?: error("Expected struct key to be a STRING.") | ||
val value = it.key.evaluate(record) | ||
key.value!! to value | ||
} | ||
return structValue(fields.asSequence()) | ||
} | ||
|
||
internal class Field(val key: PhysicalNode.Expression, val value: PhysicalNode.Expression) | ||
} |
13 changes: 13 additions & 0 deletions
13
partiql-eval/src/main/kotlin/org/partiql/eval/impl/expression/Variable.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
package org.partiql.eval.impl.expression | ||
|
||
import org.partiql.eval.impl.PhysicalNode | ||
import org.partiql.eval.impl.Record | ||
import org.partiql.value.PartiQLValue | ||
import org.partiql.value.PartiQLValueExperimental | ||
|
||
internal class Variable(private val index: Int) : PhysicalNode.Expression { | ||
@PartiQLValueExperimental | ||
override fun evaluate(record: Record): PartiQLValue { | ||
return record.values[index] | ||
} | ||
} |
29 changes: 29 additions & 0 deletions
29
partiql-eval/src/main/kotlin/org/partiql/eval/impl/relation/Projection.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
package org.partiql.eval.impl.relation | ||
|
||
import org.partiql.eval.impl.PhysicalNode | ||
import org.partiql.eval.impl.Record | ||
import org.partiql.value.PartiQLValueExperimental | ||
|
||
internal class Projection( | ||
private val input: PhysicalNode.Relation, | ||
val projections: List<PhysicalNode.Expression> | ||
) : PhysicalNode.Relation { | ||
@OptIn(PartiQLValueExperimental::class) | ||
override fun evaluate(): Iterator<Record> { | ||
|
||
val inputIter = input.evaluate() | ||
|
||
return object : Iterator<Record> { | ||
override fun hasNext(): Boolean { | ||
return inputIter.hasNext() | ||
} | ||
|
||
override fun next(): Record { | ||
val inputRecord = inputIter.next() | ||
return Record( | ||
projections.map { it.evaluate(inputRecord) } | ||
) | ||
} | ||
} | ||
} | ||
} |
19 changes: 19 additions & 0 deletions
19
partiql-eval/src/main/kotlin/org/partiql/eval/impl/relation/Scan.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
package org.partiql.eval.impl.relation | ||
|
||
import org.partiql.eval.impl.PhysicalNode | ||
import org.partiql.eval.impl.Record | ||
import org.partiql.value.CollectionValue | ||
import org.partiql.value.PartiQLValueExperimental | ||
|
||
internal class Scan( | ||
private val expr: PhysicalNode.Expression | ||
) : PhysicalNode.Relation { | ||
|
||
@OptIn(PartiQLValueExperimental::class) | ||
override fun evaluate(): Iterator<Record> { | ||
return when (val value = expr.evaluate(Record(emptyList()))) { | ||
is CollectionValue<*> -> value.elements!!.map { Record(listOf(it)) }.iterator() | ||
else -> iterator { yield(Record(listOf(value))) } | ||
} | ||
} | ||
} |
43 changes: 43 additions & 0 deletions
43
partiql-eval/src/test/kotlin/org/partiql/eval/impl/PartiQLEngineDefaultTest.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
package org.partiql.eval.impl | ||
|
||
import org.junit.jupiter.api.Test | ||
import org.partiql.eval.PartiQLEngine | ||
import org.partiql.parser.PartiQLParserBuilder | ||
import org.partiql.planner.PartiQLPlanner | ||
import org.partiql.planner.PartiQLPlannerBuilder | ||
import org.partiql.value.BagValue | ||
import org.partiql.value.PartiQLValueExperimental | ||
import org.partiql.value.bagValue | ||
import org.partiql.value.int32Value | ||
import kotlin.test.assertEquals | ||
|
||
class PartiQLEngineDefaultTest { | ||
|
||
private val engine = PartiQLEngineDefault() | ||
private val planner = PartiQLPlannerBuilder().build() | ||
private val parser = PartiQLParserBuilder.standard().build() | ||
|
||
@OptIn(PartiQLValueExperimental::class) | ||
@Test | ||
fun testLiterals() { | ||
val statement = parser.parse("SELECT VALUE 1 FROM <<0, 1>>;").root | ||
val session = PartiQLPlanner.Session("q", "u") | ||
val plan = planner.plan(statement, session) | ||
val result = engine.execute(plan.plan) as PartiQLEngine.Result.Success | ||
val output = result.output as BagValue<*> | ||
val expected = bagValue(sequenceOf(int32Value(1), int32Value(1))) | ||
assertEquals(expected, output) | ||
} | ||
|
||
@OptIn(PartiQLValueExperimental::class) | ||
@Test | ||
fun testReference() { | ||
val statement = parser.parse("SELECT VALUE t FROM <<10, 20, 30>> AS t;").root | ||
val session = PartiQLPlanner.Session("q", "u") | ||
val plan = planner.plan(statement, session) | ||
val result = engine.execute(plan.plan) as PartiQLEngine.Result.Success | ||
val output = result.output as BagValue<*> | ||
val expected = bagValue(sequenceOf(int32Value(10), int32Value(20), int32Value(30))) | ||
assertEquals(expected, output) | ||
} | ||
} |