Skip to content

Commit

Permalink
Handles permissive/strict for function calls
Browse files Browse the repository at this point in the history
  • Loading branch information
johnedquinn committed Jan 30, 2024
1 parent 9e64c39 commit c16584f
Show file tree
Hide file tree
Showing 4 changed files with 17 additions and 35 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import org.partiql.spi.function.PartiQLFunction
import org.partiql.spi.function.PartiQLFunctionExperimental
import org.partiql.types.function.FunctionSignature
import org.partiql.value.PartiQLValueExperimental
import org.partiql.value.PartiQLValueType
import java.lang.IllegalStateException

internal class Compiler @OptIn(PartiQLFunctionExperimental::class) constructor(
Expand Down Expand Up @@ -141,19 +142,22 @@ internal class Compiler @OptIn(PartiQLFunctionExperimental::class) constructor(
return ExprPathIndex(root, index)
}

@OptIn(PartiQLFunctionExperimental::class)
@OptIn(PartiQLFunctionExperimental::class, PartiQLValueExperimental::class)
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 when (function.signature.name.equals("is_missing", ignoreCase = true)) {
false -> ExprCallStatic(function, args)
val fnTakesInMissing = function.signature.parameters.any {
it.type == PartiQLValueType.MISSING || it.type == PartiQLValueType.ANY
}
return when (fnTakesInMissing) {
true -> ExprCallStatic(function, args.map { it.modeHandled() }.toTypedArray())
false -> ExprCallStatic(function, args)
}
}

@OptIn(PartiQLFunctionExperimental::class, PartiQLValueExperimental::class)
override fun visitRexOpCallDynamic(node: Rex.Op.Call.Dynamic, ctx: Unit): Operator {
val args = node.args.map { visitRex(it, ctx) }.toTypedArray()
val args = node.args.map { visitRex(it, ctx).modeHandled() }.toTypedArray()
val candidates = node.candidates.map { candidate ->
val fn = getFunction(candidate.fn.signature)
val coercions = candidate.coercions.map { it?.signature?.let { sig -> getFunction(sig) } }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,9 @@ internal class ExprCallDynamic(
@OptIn(PartiQLValueExperimental::class)
internal fun matches(args: Array<PartiQLValue>): Boolean {
for (i in args.indices) {
if (types[i] == PartiQLValueType.ANY) {
return true
}
if (args[i].type != types[i]) {
return false
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
package org.partiql.eval.internal.operator.rex

import org.partiql.errors.TypeCheckException
import org.partiql.eval.internal.Record
import org.partiql.eval.internal.helpers.toNull
import org.partiql.eval.internal.operator.Operator
import org.partiql.spi.function.PartiQLFunction
import org.partiql.spi.function.PartiQLFunctionExperimental
import org.partiql.value.PartiQLValue
import org.partiql.value.PartiQLValueExperimental
import org.partiql.value.missingValue

@OptIn(PartiQLValueExperimental::class, PartiQLFunctionExperimental::class)
internal class ExprCallStatic(
Expand All @@ -31,31 +29,4 @@ internal class ExprCallStatic(
}.toTypedArray()
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
Expand Up @@ -270,6 +270,10 @@ class PartiQLEngineDefaultTest {
expected = boolValue(true), // TODO: Is this right?
mode = PartiQLEngine.Mode.STRICT
),
SuccessTestCase(
input = "SELECT VALUE t.a IS MISSING FROM << { 'b': 1 }, { 'a': 2 } >> AS t;",
expected = bagValue(boolValue(true), boolValue(false))
),
// PartiQL Specification Section 7.1.1 -- Equality
SuccessTestCase(
input = "5 = 'a';",
Expand All @@ -278,7 +282,7 @@ class PartiQLEngineDefaultTest {
// PartiQL Specification Section 7.1.1 -- Equality
SuccessTestCase(
input = "5 = 'a';",
expected = boolValue(false), // TODO: Is this correct? See: The eqg, unlike the =, returns true when a NULL is compared to a NULL or a MISSING to a MISSING
expected = boolValue(false), // TODO: Is this correct?
mode = PartiQLEngine.Mode.STRICT
),
// PartiQL Specification Section 8
Expand Down Expand Up @@ -462,7 +466,7 @@ class PartiQLEngineDefaultTest {
val functions = mapOf(
"partiql" to PartiQLPlugin.functions
)
val prepared = engine.prepare(plan.plan, PartiQLEngine.Session(functions = functions))
val prepared = engine.prepare(plan.plan, PartiQLEngine.Session(functions = functions, mode = mode))
val result = engine.execute(prepared) as PartiQLResult.Value
val output = result.value
assertEquals(expected, output, comparisonString(expected, output))
Expand Down

0 comments on commit c16584f

Please sign in to comment.