-
Notifications
You must be signed in to change notification settings - Fork 63
Add COALESCE
and NULLIF
to the logical plan
#1404
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -55,9 +55,11 @@ import org.partiql.planner.internal.ir.rexOpCallDynamic | |
import org.partiql.planner.internal.ir.rexOpCallDynamicCandidate | ||
import org.partiql.planner.internal.ir.rexOpCallStatic | ||
import org.partiql.planner.internal.ir.rexOpCaseBranch | ||
import org.partiql.planner.internal.ir.rexOpCoalesce | ||
import org.partiql.planner.internal.ir.rexOpCollection | ||
import org.partiql.planner.internal.ir.rexOpErr | ||
import org.partiql.planner.internal.ir.rexOpLit | ||
import org.partiql.planner.internal.ir.rexOpNullif | ||
import org.partiql.planner.internal.ir.rexOpPathIndex | ||
import org.partiql.planner.internal.ir.rexOpPathKey | ||
import org.partiql.planner.internal.ir.rexOpPathSymbol | ||
|
@@ -760,6 +762,54 @@ internal class PlanTyper( | |
return rex(type, op) | ||
} | ||
|
||
// COALESCE(v1, v2,..., vN) | ||
// == | ||
// CASE | ||
// WHEN v1 IS NOT NULL THEN v1 -- WHEN branch always a boolean | ||
// WHEN v2 IS NOT NULL THEN v2 -- WHEN branch always a boolean | ||
// ... -- similarly for v3..vN-1 | ||
// ELSE vN | ||
// END | ||
// --> minimal common supertype of(<type v1>, <type v2>, ..., <type v3>) | ||
override fun visitRexOpCoalesce(node: Rex.Op.Coalesce, ctx: StaticType?): Rex { | ||
val args = node.args.map { visitRex(it, it.type) }.toMutableList() | ||
val typer = DynamicTyper() | ||
args.forEach { v -> | ||
typer.accumulate(v.type) | ||
} | ||
val (type, mapping) = typer.mapping() | ||
if (mapping != null) { | ||
assert(mapping.size == args.size) { "Coercion mappings `len ${mapping.size}` did not match the number of COALESCE arguments `len ${args.size}`" } | ||
for (i in args.indices) { | ||
val (operand, target) = mapping[i] | ||
if (operand == target) continue // skip; no coercion needed | ||
val cast = env.fnResolver.cast(operand, target) | ||
val rex = rex(type, rexOpCallStatic(fnResolved(cast), listOf(args[i]))) | ||
args[i] = rex | ||
} | ||
} | ||
val op = rexOpCoalesce(args) | ||
return rex(type, op) | ||
} | ||
|
||
// NULLIF(v1, v2) | ||
// == | ||
// CASE | ||
// WHEN v1 = v2 THEN NULL -- WHEN branch always a boolean | ||
// ELSE v1 | ||
// END | ||
// --> minimal common supertype of (NULL, <type v1>) | ||
override fun visitRexOpNullif(node: Rex.Op.Nullif, ctx: StaticType?): Rex { | ||
val v1 = visitRex(node.v1, node.v1.type) | ||
val v2 = visitRex(node.v2, node.v2.type) | ||
val typer = DynamicTyper() | ||
typer.accumulate(NULL) | ||
typer.accumulate(v1.type) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Only add
Per my read of SQL-99, I can't find any mention of this promotion behavior, so leaving the result type as just the minimal common supertype of (<type v1>, null). Postgresql would do something like the minimal common supertype of (<type v1>, <type v2>, null). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I believe we can skip the dynamic typer here. It's just type of union(v1, null).flatten() or something as simple as that There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah I'll simplify it. I initially included the dynamic typer in case we wanted to follow what postgresql did, which is easy to do w/ the dynamic typer. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Followup from offline discussion, will keep the dynamic typer here to ensure consistency with the typing of |
||
val (type, _) = typer.mapping() | ||
val op = rexOpNullif(v1, v2) | ||
return rex(type, op) | ||
} | ||
|
||
/** | ||
* In this context, Boolean means PartiQLValueType Bool, which can be nullable. | ||
* Hence, we permit Static Type BOOL, Static Type NULL, Static Type Missing here. | ||
|
Uh oh!
There was an error while loading. Please reload this page.