Skip to content

Commit

Permalink
Adds support for PERMISSIVE vs STRICT
Browse files Browse the repository at this point in the history
  • Loading branch information
johnedquinn committed Jan 30, 2024
1 parent 101c19b commit 7155cb8
Show file tree
Hide file tree
Showing 20 changed files with 864 additions and 62 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,12 @@ public interface PartiQLEngine {

public class Session @OptIn(PartiQLFunctionExperimental::class) constructor(
val bindings: Map<String, ConnectorBindings> = mapOf(),
val functions: Map<String, List<PartiQLFunction>> = mapOf()
val functions: Map<String, List<PartiQLFunction>> = mapOf(),
val mode: Mode = Mode.PERMISSIVE
)

public enum class Mode {
PERMISSIVE,
STRICT // AKA, Type Checking Mode in the PartiQL Specification
}
}
43 changes: 34 additions & 9 deletions partiql-eval/src/main/kotlin/org/partiql/eval/internal/Compiler.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import org.partiql.eval.internal.operator.rel.RelJoinRight
import org.partiql.eval.internal.operator.rel.RelProject
import org.partiql.eval.internal.operator.rel.RelScan
import org.partiql.eval.internal.operator.rel.RelScanIndexed
import org.partiql.eval.internal.operator.rel.RelScanIndexedPermissive
import org.partiql.eval.internal.operator.rel.RelScanPermissive
import org.partiql.eval.internal.operator.rex.ExprCallDynamic
import org.partiql.eval.internal.operator.rex.ExprCallStatic
import org.partiql.eval.internal.operator.rex.ExprCase
Expand All @@ -20,7 +22,9 @@ import org.partiql.eval.internal.operator.rex.ExprLiteral
import org.partiql.eval.internal.operator.rex.ExprPathIndex
import org.partiql.eval.internal.operator.rex.ExprPathKey
import org.partiql.eval.internal.operator.rex.ExprPathSymbol
import org.partiql.eval.internal.operator.rex.ExprPermissive
import org.partiql.eval.internal.operator.rex.ExprPivot
import org.partiql.eval.internal.operator.rex.ExprPivotPermissive
import org.partiql.eval.internal.operator.rex.ExprSelect
import org.partiql.eval.internal.operator.rex.ExprStruct
import org.partiql.eval.internal.operator.rex.ExprTupleUnion
Expand Down Expand Up @@ -71,7 +75,7 @@ internal class Compiler @OptIn(PartiQLFunctionExperimental::class) constructor(

// TODO: Re-look at
override fun visitStatementQuery(node: Statement.Query, ctx: Unit): Operator.Expr {
return visitRex(node.root, ctx)
return visitRex(node.root, ctx).modeHandled()
}

// REX
Expand All @@ -81,27 +85,31 @@ internal class Compiler @OptIn(PartiQLFunctionExperimental::class) constructor(
}

override fun visitRexOpCollection(node: Rex.Op.Collection, ctx: Unit): Operator {
val values = node.values.map { visitRex(it, ctx) }
val values = node.values.map { visitRex(it, ctx).modeHandled() }
return ExprCollection(values)
}
override fun visitRexOpStruct(node: Rex.Op.Struct, ctx: Unit): Operator {
val fields = node.fields.map {
ExprStruct.Field(visitRex(it.k, ctx), visitRex(it.v, ctx))
val value = visitRex(it.v, ctx).modeHandled()
ExprStruct.Field(visitRex(it.k, ctx), value)
}
return ExprStruct(fields)
}

override fun visitRexOpSelect(node: Rex.Op.Select, ctx: Unit): Operator {
val rel = visitRel(node.rel, ctx)
val constructor = visitRex(node.constructor, ctx)
val constructor = visitRex(node.constructor, ctx).modeHandled()
return ExprSelect(rel, constructor)
}

override fun visitRexOpPivot(node: Rex.Op.Pivot, ctx: Unit): Operator {
val rel = visitRel(node.rel, ctx)
val key = visitRex(node.key, ctx)
val value = visitRex(node.value, ctx)
return ExprPivot(rel, key, value)
return when (session.mode) {
PartiQLEngine.Mode.PERMISSIVE -> ExprPivotPermissive(rel, key, value)
PartiQLEngine.Mode.STRICT -> ExprPivot(rel, key, value)
}
}
override fun visitRexOpVar(node: Rex.Op.Var, ctx: Unit): Operator {
return ExprVar(node.ref)
Expand Down Expand Up @@ -137,7 +145,10 @@ internal class Compiler @OptIn(PartiQLFunctionExperimental::class) constructor(
override fun visitRexOpCallStatic(node: Rex.Op.Call.Static, ctx: Unit): Operator {
val function = getFunction(node.fn.signature)
val args = node.args.map { visitRex(it, ctx) }.toTypedArray()
return ExprCallStatic(function, args)
return when (function.signature.name.equals("is_missing", ignoreCase = true)) {
false -> ExprCallStatic(function, args)
true -> ExprCallStatic(function, args.map { it.modeHandled() }.toTypedArray())
}
}

@OptIn(PartiQLFunctionExperimental::class, PartiQLValueExperimental::class)
Expand Down Expand Up @@ -174,18 +185,24 @@ internal class Compiler @OptIn(PartiQLFunctionExperimental::class) constructor(

override fun visitRelOpScan(node: Rel.Op.Scan, ctx: Unit): Operator {
val rex = visitRex(node.rex, ctx)
return RelScan(rex)
return when (session.mode) {
PartiQLEngine.Mode.PERMISSIVE -> RelScanPermissive(rex)
PartiQLEngine.Mode.STRICT -> RelScan(rex)
}
}

override fun visitRelOpProject(node: Rel.Op.Project, ctx: Unit): Operator {
val input = visitRel(node.input, ctx)
val projections = node.projections.map { visitRex(it, ctx) }
val projections = node.projections.map { visitRex(it, ctx).modeHandled() }
return RelProject(input, projections)
}

override fun visitRelOpScanIndexed(node: Rel.Op.ScanIndexed, ctx: Unit): Operator {
val rex = visitRex(node.rex, ctx)
return RelScanIndexed(rex)
return when (session.mode) {
PartiQLEngine.Mode.PERMISSIVE -> RelScanIndexedPermissive(rex)
PartiQLEngine.Mode.STRICT -> RelScanIndexed(rex)
}
}

override fun visitRexOpTupleUnion(node: Rex.Op.TupleUnion, ctx: Unit): Operator {
Expand Down Expand Up @@ -228,4 +245,12 @@ internal class Compiler @OptIn(PartiQLFunctionExperimental::class) constructor(
val condition = visitRex(node.predicate, ctx)
return RelFilter(input, condition)
}

// HELPERS
private fun Operator.Expr.modeHandled(): Operator.Expr {
return when (session.mode) {
PartiQLEngine.Mode.PERMISSIVE -> ExprPermissive(this)
PartiQLEngine.Mode.STRICT -> this
}
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.partiql.eval.internal.operator.rel

import org.partiql.errors.TypeCheckException
import org.partiql.eval.internal.Record
import org.partiql.eval.internal.operator.Operator
import org.partiql.value.CollectionValue
Expand All @@ -16,7 +17,10 @@ internal class RelScan(
val r = expr.eval(Record.empty)
records = when (r) {
is CollectionValue<*> -> r.map { Record.of(it) }.iterator()
else -> iterator { yield(Record.of(r)) }
else -> {
close()
throw TypeCheckException()
}
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package org.partiql.eval.internal.operator.rel

import org.partiql.errors.TypeCheckException
import org.partiql.eval.internal.Record
import org.partiql.eval.internal.operator.Operator
import org.partiql.value.BagValue
import org.partiql.value.CollectionValue
import org.partiql.value.PartiQLValue
import org.partiql.value.PartiQLValueExperimental
Expand All @@ -19,8 +21,15 @@ internal class RelScanIndexed(
val r = expr.eval(Record.empty)
index = 0
iterator = when (r) {
is BagValue<*> -> {
close()
throw TypeCheckException()
}
is CollectionValue<*> -> r.iterator()
else -> iterator { yield(r) }
else -> {
close()
throw TypeCheckException()
}
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package org.partiql.eval.internal.operator.rel

import org.partiql.eval.internal.Record
import org.partiql.eval.internal.operator.Operator
import org.partiql.value.BagValue
import org.partiql.value.CollectionValue
import org.partiql.value.PartiQLValue
import org.partiql.value.PartiQLValueExperimental
import org.partiql.value.int64Value
import org.partiql.value.missingValue

@OptIn(PartiQLValueExperimental::class)
internal class RelScanIndexedPermissive(
private val expr: Operator.Expr
) : Operator.Relation {

private lateinit var iterator: Iterator<PartiQLValue>
private var index: Long = 0
private var isIndexable: Boolean = true

override fun open() {
val r = expr.eval(Record.empty)
index = 0
iterator = when (r) {
is BagValue<*> -> {
isIndexable = false
r.iterator()
}
is CollectionValue<*> -> r.iterator()
else -> {
isIndexable = false
iterator { yield(r) }
}
}
}

override fun next(): Record? {
if (!iterator.hasNext()) {
return null
}
val v = iterator.next()
return when (isIndexable) {
true -> {
val i = index
index += 1
Record.of(v, int64Value(i))
}
false -> Record.of(v, missingValue())
}
}

override fun close() {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package org.partiql.eval.internal.operator.rel

import org.partiql.eval.internal.Record
import org.partiql.eval.internal.operator.Operator
import org.partiql.value.CollectionValue
import org.partiql.value.PartiQLValueExperimental

@OptIn(PartiQLValueExperimental::class)
internal class RelScanPermissive(
private val expr: Operator.Expr
) : Operator.Relation {

private lateinit var records: Iterator<Record>

override fun open() {
val r = expr.eval(Record.empty)
records = when (r) {
is CollectionValue<*> -> r.map { Record.of(it) }.iterator()
else -> iterator { yield(Record.of(r)) }
}
}

override fun next(): Record? {
return if (records.hasNext()) {
records.next()
} else {
null
}
}

override fun close() {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,40 @@ internal class ExprCallStatic(
@OptIn(PartiQLValueExperimental::class)
private val nil = fn.signature.returns.toNull()

override fun eval(record: Record): PartiQLValue = try {
override fun eval(record: Record): PartiQLValue {
// Evaluate arguments
val args = inputs.map { input ->
val r = input.eval(record)
if (r.isNull && fn.signature.isNullCall) return nil()
r
}.toTypedArray()
fn.invoke(args)
} catch (ex: TypeCheckException) {
missingValue()
return fn.invoke(args)
}

@OptIn(PartiQLValueExperimental::class, PartiQLFunctionExperimental::class)
internal class Permissive(
private val fn: PartiQLFunction.Scalar,
private val inputs: Array<Operator.Expr>,
) : Operator.Expr {

/**
* Memoize creation of
*/
@OptIn(PartiQLValueExperimental::class)
private val nil = fn.signature.returns.toNull()

override fun eval(record: Record): PartiQLValue {
// Evaluate arguments
val args = inputs.map { input ->
try {
val r = input.eval(record)
if (r.isNull && fn.signature.isNullCall) return nil()
r
} catch (e: TypeCheckException) {
missingValue()
}
}.toTypedArray()
return fn.invoke(args)
}
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.partiql.eval.internal.operator.rex

import org.partiql.errors.TypeCheckException
import org.partiql.eval.internal.Record
import org.partiql.eval.internal.operator.Operator
import org.partiql.value.CollectionValue
Expand All @@ -11,7 +12,6 @@ import org.partiql.value.IntValue
import org.partiql.value.PartiQLValue
import org.partiql.value.PartiQLValueExperimental
import org.partiql.value.check
import org.partiql.value.missingValue

internal class ExprPathIndex(
@JvmField val root: Operator.Expr,
Expand All @@ -21,7 +21,6 @@ internal class ExprPathIndex(
@OptIn(PartiQLValueExperimental::class)
override fun eval(record: Record): PartiQLValue {
val collection = root.eval(record).check<CollectionValue<PartiQLValue>>()
val value = missingValue()

// Calculate index
val index = when (val k = key.eval(record)) {
Expand All @@ -30,8 +29,8 @@ internal class ExprPathIndex(
is Int64Value -> k.int
is Int8Value -> k.int
is IntValue -> k.int
else -> return value
} ?: return value
else -> throw TypeCheckException()
} ?: throw TypeCheckException()

// Get element
val iterator = collection.iterator()
Expand All @@ -43,6 +42,6 @@ internal class ExprPathIndex(
}
i++
}
return value
throw TypeCheckException()
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
package org.partiql.eval.internal.operator.rex

import org.partiql.errors.TypeCheckException
import org.partiql.eval.internal.Record
import org.partiql.eval.internal.operator.Operator
import org.partiql.value.PartiQLValue
import org.partiql.value.PartiQLValueExperimental
import org.partiql.value.StringValue
import org.partiql.value.StructValue
import org.partiql.value.check
import org.partiql.value.missingValue

internal class ExprPathKey(
@JvmField val root: Operator.Expr,
Expand All @@ -19,6 +19,6 @@ internal class ExprPathKey(
val rootEvaluated = root.eval(record).check<StructValue<PartiQLValue>>()
val keyEvaluated = key.eval(record).check<StringValue>()
val keyString = keyEvaluated.value ?: error("String value was null")
return rootEvaluated[keyString] ?: missingValue()
return rootEvaluated[keyString] ?: throw TypeCheckException()
}
}
Loading

0 comments on commit 7155cb8

Please sign in to comment.