Skip to content

Commit

Permalink
Initializes PartiQL's Engine (#1283)
Browse files Browse the repository at this point in the history
  • Loading branch information
johnedquinn authored and rchowell committed Dec 8, 2023
1 parent a216777 commit 56332d9
Show file tree
Hide file tree
Showing 13 changed files with 361 additions and 0 deletions.
59 changes: 59 additions & 0 deletions partiql-eval/src/main/kotlin/org/partiql/eval/PartiQLEngine.kt
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()
}
}
}
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 partiql-eval/src/main/kotlin/org/partiql/eval/impl/PhysicalNode.kt
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>
}
}
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)
}
}
}
8 changes: 8 additions & 0 deletions partiql-eval/src/main/kotlin/org/partiql/eval/impl/Record.kt
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>
)
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()
)
}
}
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
}
}
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())
}
}
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)
}
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]
}
}
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) }
)
}
}
}
}
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))) }
}
}
}
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)
}
}

0 comments on commit 56332d9

Please sign in to comment.