Skip to content

Commit 54c043f

Browse files
committed
Deprecates absent types
Removes all logic regarding absent types in planner
1 parent 2a13661 commit 54c043f

File tree

22 files changed

+621
-702
lines changed

22 files changed

+621
-702
lines changed

partiql-planner/src/main/kotlin/org/partiql/planner/internal/transforms/PlanTransform.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -365,7 +365,7 @@ internal object PlanTransform : PlanBaseVisitor<PlanNode, ProblemCallback>() {
365365
org.partiql.plan.Agg(
366366
FunctionSignature.Aggregation(
367367
"UNKNOWN_AGG::$name",
368-
returns = PartiQLValueType.MISSING,
368+
returns = PartiQLValueType.ANY,
369369
parameters = emptyList()
370370
)
371371
)

partiql-planner/src/main/kotlin/org/partiql/planner/internal/transforms/RexConverter.kt

+5-12
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,6 @@ import org.partiql.planner.internal.ir.rexOpStructField
4242
import org.partiql.planner.internal.ir.rexOpSubquery
4343
import org.partiql.planner.internal.ir.rexOpTupleUnion
4444
import org.partiql.planner.internal.ir.rexOpVarUnresolved
45-
import org.partiql.planner.internal.typer.toNonNullStaticType
4645
import org.partiql.planner.internal.typer.toStaticType
4746
import org.partiql.types.StaticType
4847
import org.partiql.types.TimeType
@@ -70,10 +69,7 @@ internal object RexConverter {
7069
throw IllegalArgumentException("unsupported rex $node")
7170

7271
override fun visitExprLit(node: Expr.Lit, context: Env): Rex {
73-
val type = when (node.value.isNull) {
74-
true -> node.value.type.toStaticType()
75-
else -> node.value.type.toNonNullStaticType()
76-
}
72+
val type = node.value.type.toStaticType()
7773
val op = rexOpLit(node.value)
7874
return rex(type, op)
7975
}
@@ -82,10 +78,7 @@ internal object RexConverter {
8278
val value =
8379
PartiQLValueIonReaderBuilder
8480
.standard().build(node.value).read()
85-
val type = when (value.isNull) {
86-
true -> value.type.toStaticType()
87-
else -> value.type.toNonNullStaticType()
88-
}
81+
val type = value.type.toStaticType()
8982
return rex(type, rexOpLit(value))
9083
}
9184

@@ -287,7 +280,7 @@ internal object RexConverter {
287280
}.toMutableList()
288281

289282
val defaultRex = when (val default = node.default) {
290-
null -> rex(type = StaticType.NULL, op = rexOpLit(value = nullValue()))
283+
null -> rex(type = StaticType.ANY, op = rexOpLit(value = nullValue()))
291284
else -> visitExprCoerce(default, context)
292285
}
293286
val op = rexOpCase(branches = branches, default = defaultRex)
@@ -528,8 +521,8 @@ internal object RexConverter {
528521
val type = node.asType
529522
val arg0 = visitExprCoerce(node.value, ctx)
530523
return when (type) {
531-
is Type.NullType -> rex(StaticType.NULL, call("cast_null", arg0))
532-
is Type.Missing -> rex(StaticType.MISSING, call("cast_missing", arg0))
524+
is Type.NullType -> error("Cannot cast any value to NULL")
525+
is Type.Missing -> error("Cannot cast any value to MISSING")
533526
is Type.Bool -> rex(StaticType.BOOL, call("cast_bool", arg0))
534527
is Type.Tinyint -> TODO("Static Type does not have TINYINT type")
535528
is Type.Smallint, is Type.Int2 -> rex(StaticType.INT2, call("cast_int16", arg0))

partiql-planner/src/main/kotlin/org/partiql/planner/internal/typer/DynamicTyper.kt

+41-53
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,7 @@
22

33
package org.partiql.planner.internal.typer
44

5-
import org.partiql.types.MissingType
6-
import org.partiql.types.NullType
5+
import org.partiql.planner.internal.ir.Rex
76
import org.partiql.types.StaticType
87
import org.partiql.value.PartiQLValueExperimental
98
import org.partiql.value.PartiQLValueType
@@ -27,8 +26,6 @@ import org.partiql.value.PartiQLValueType.INT64
2726
import org.partiql.value.PartiQLValueType.INT8
2827
import org.partiql.value.PartiQLValueType.INTERVAL
2928
import org.partiql.value.PartiQLValueType.LIST
30-
import org.partiql.value.PartiQLValueType.MISSING
31-
import org.partiql.value.PartiQLValueType.NULL
3229
import org.partiql.value.PartiQLValueType.SEXP
3330
import org.partiql.value.PartiQLValueType.STRING
3431
import org.partiql.value.PartiQLValueType.STRUCT
@@ -57,59 +54,63 @@ internal class DynamicTyper {
5754
private var supertype: PartiQLValueType? = null
5855
private var args = mutableListOf<PartiQLValueType>()
5956

60-
private var nullable = false
61-
private var missable = false
6257
private val types = mutableSetOf<StaticType>()
6358

6459
/**
65-
* This primarily unpacks a StaticType because of NULL, MISSING.
66-
*
67-
* - T
68-
* - NULL
69-
* - MISSING
70-
* - (NULL)
71-
* - (MISSING)
72-
* - (T..)
73-
* - (T..|NULL)
74-
* - (T..|MISSING)
75-
* - (T..|NULL|MISSING)
76-
* - (NULL|MISSING)
77-
*
60+
* Adds the [rex]'s [Rex.type] to the typing accumulator (if the [rex] is not a literal NULL/MISSING).
61+
*/
62+
fun accumulate(rex: Rex) {
63+
when (rex.isLiteralAbsent()) {
64+
true -> accumulateUnknown()
65+
false -> accumulate(rex.type)
66+
}
67+
}
68+
69+
/**
70+
* Checks for literal NULL or MISSING.
71+
*/
72+
private fun Rex.isLiteralAbsent(): Boolean {
73+
val op = this.op
74+
return op is Rex.Op.Lit && (op.value.type == PartiQLValueType.MISSING || op.value.type == PartiQLValueType.NULL)
75+
}
76+
77+
/**
78+
* When a literal null or missing value is present in the query, its type is unknown. Therefore, its type must be
79+
* inferred. This function ignores literal null/missing values, yet adds their indices to know how to return the
80+
* mapping.
81+
*/
82+
private fun accumulateUnknown() {
83+
args.add(ANY)
84+
}
85+
86+
/**
87+
* This adds non-unknown types (aka not NULL / MISSING literals) to the typing accumulator.
7888
* @param type
7989
*/
80-
fun accumulate(type: StaticType) {
81-
val nonAbsentTypes = mutableSetOf<StaticType>()
90+
private fun accumulate(type: StaticType) {
8291
val flatType = type.flatten()
8392
if (flatType == StaticType.ANY) {
84-
// Use ANY runtime; do not expand ANY
8593
types.add(flatType)
8694
args.add(ANY)
8795
calculate(ANY)
8896
return
8997
}
90-
for (t in flatType.allTypes) {
91-
when (t) {
92-
is NullType -> nullable = true
93-
is MissingType -> missable = true
94-
else -> nonAbsentTypes.add(t)
95-
}
96-
}
97-
when (nonAbsentTypes.size) {
98+
val allTypes = flatType.allTypes
99+
when (allTypes.size) {
98100
0 -> {
99-
// Ignore in calculating supertype.
100-
args.add(NULL)
101+
error("This should not have happened.")
101102
}
102103
1 -> {
103104
// Had single type
104-
val single = nonAbsentTypes.first()
105+
val single = allTypes.first()
105106
val singleRuntime = single.toRuntimeType()
106107
types.add(single)
107108
args.add(singleRuntime)
108109
calculate(singleRuntime)
109110
}
110111
else -> {
111112
// Had a union; use ANY runtime
112-
types.addAll(nonAbsentTypes)
113+
types.addAll(allTypes)
113114
args.add(ANY)
114115
calculate(ANY)
115116
}
@@ -124,31 +125,20 @@ internal class DynamicTyper {
124125
* @return
125126
*/
126127
fun mapping(): Pair<StaticType, List<Pair<PartiQLValueType, PartiQLValueType>>?> {
127-
val modifiers = mutableSetOf<StaticType>()
128-
if (nullable) modifiers.add(StaticType.NULL)
129-
if (missable) modifiers.add(StaticType.MISSING)
130128
// If at top supertype, then return union of all accumulated types
131129
if (supertype == ANY) {
132-
return StaticType.unionOf(types + modifiers).flatten() to null
130+
return StaticType.unionOf(types).flatten() to null
133131
}
134132
// If a collection, then return union of all accumulated types as these coercion rules are not defined by SQL.
135133
if (supertype == STRUCT || supertype == BAG || supertype == LIST || supertype == SEXP) {
136-
return StaticType.unionOf(types + modifiers) to null
134+
return StaticType.unionOf(types) to null
137135
}
138136
// If not initialized, then return null, missing, or null|missing.
139-
val s = supertype
140-
if (s == null) {
141-
val t = if (modifiers.isEmpty()) StaticType.MISSING else StaticType.unionOf(modifiers).flatten()
142-
return t to null
143-
}
137+
val s = supertype ?: return StaticType.ANY to null
144138
// Otherwise, return the supertype along with the coercion mapping
145-
val type = s.toNonNullStaticType()
139+
val type = s.toStaticType()
146140
val mapping = args.map { it to s }
147-
return if (modifiers.isEmpty()) {
148-
type to mapping
149-
} else {
150-
StaticType.unionOf(setOf(type) + modifiers).flatten() to mapping
151-
}
141+
return type to mapping
152142
}
153143

154144
private fun calculate(type: PartiQLValueType) {
@@ -163,7 +153,7 @@ internal class DynamicTyper {
163153
// Lookup and set the new minimum common supertype
164154
supertype = when {
165155
type == ANY -> type
166-
type == NULL || type == MISSING || s == type -> return // skip
156+
s == type -> return // skip
167157
else -> graph[s][type] ?: ANY // lookup, if missing then go to top.
168158
}
169159
}
@@ -206,8 +196,6 @@ internal class DynamicTyper {
206196
graph[type] = arrayOfNulls(N)
207197
}
208198
graph[ANY] = edges()
209-
graph[NULL] = edges()
210-
graph[MISSING] = edges()
211199
graph[BOOL] = edges(
212200
BOOL to BOOL
213201
)

partiql-planner/src/main/kotlin/org/partiql/planner/internal/typer/FnResolver.kt

+11-42
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,6 @@ import org.partiql.planner.internal.ir.Agg
55
import org.partiql.planner.internal.ir.Fn
66
import org.partiql.planner.internal.ir.Identifier
77
import org.partiql.planner.internal.ir.Rex
8-
import org.partiql.planner.internal.typer.FnResolver.Companion.compareTo
9-
import org.partiql.types.AnyOfType
10-
import org.partiql.types.NullType
118
import org.partiql.types.StaticType
129
import org.partiql.types.function.FunctionParameter
1310
import org.partiql.types.function.FunctionSignature
@@ -33,8 +30,6 @@ import org.partiql.value.PartiQLValueType.INT64
3330
import org.partiql.value.PartiQLValueType.INT8
3431
import org.partiql.value.PartiQLValueType.INTERVAL
3532
import org.partiql.value.PartiQLValueType.LIST
36-
import org.partiql.value.PartiQLValueType.MISSING
37-
import org.partiql.value.PartiQLValueType.NULL
3833
import org.partiql.value.PartiQLValueType.SEXP
3934
import org.partiql.value.PartiQLValueType.STRING
4035
import org.partiql.value.PartiQLValueType.STRUCT
@@ -76,27 +71,19 @@ internal sealed class FnMatch<T : FunctionSignature> {
7671
*
7772
* @property signature
7873
* @property mapping
79-
* @property isMissable TRUE when anyone of the arguments _could_ be MISSING. We *always* propagate MISSING.
8074
*/
8175
public data class Ok<T : FunctionSignature>(
8276
public val signature: T,
8377
public val mapping: Mapping,
84-
public val isMissable: Boolean,
8578
) : FnMatch<T>()
8679

8780
/**
8881
* This represents dynamic dispatch.
8982
*
9083
* @property candidates an ordered list of potentially applicable functions to dispatch dynamically.
91-
* @property isMissable TRUE when the argument permutations may not definitively invoke one of the candidates. You
92-
* can think of [isMissable] as being the same as "not exhaustive". For example, if we have ABS(INT | STRING), then
93-
* this function call [isMissable] because there isn't an `ABS(STRING)` function signature AKA we haven't exhausted
94-
* all the arguments. On the other hand, take an "exhaustive" scenario: ABS(INT | DEC). In this case, [isMissable]
95-
* is false because we have functions for each potential argument AKA we have exhausted the arguments.
9684
*/
9785
public data class Dynamic<T : FunctionSignature>(
98-
public val candidates: List<Ok<T>>,
99-
public val isMissable: Boolean
86+
public val candidates: List<Ok<T>>
10087
) : FnMatch<T>()
10188

10289
public data class Error<T : FunctionSignature>(
@@ -162,12 +149,8 @@ internal class FnResolver(private val header: Header) {
162149
*/
163150
public fun resolveFn(fn: Fn.Unresolved, args: List<Rex>): FnMatch<FunctionSignature.Scalar> {
164151
val candidates = lookup(fn)
165-
var canReturnMissing = false
166152
val parameterPermutations = buildArgumentPermutations(args.map { it.type }).mapNotNull { argList ->
167153
argList.mapIndexed { i, arg ->
168-
if (arg.isMissable()) {
169-
canReturnMissing = true
170-
}
171154
// Skip over if we cannot convert type to runtime type.
172155
val argType = arg.toRuntimeTypeOrNull() ?: return@mapNotNull null
173156
FunctionParameter("arg-$i", argType)
@@ -176,12 +159,10 @@ internal class FnResolver(private val header: Header) {
176159
val potentialFunctions = parameterPermutations.mapNotNull { parameters ->
177160
when (val match = match(candidates, parameters)) {
178161
null -> {
179-
canReturnMissing = true
180162
null
181163
}
182164
else -> {
183-
val isMissable = canReturnMissing || isUnsafeCast(match.signature.specific)
184-
FnMatch.Ok(match.signature, match.mapping, isMissable)
165+
FnMatch.Ok(match.signature, match.mapping)
185166
}
186167
}
187168
}
@@ -190,18 +171,12 @@ internal class FnResolver(private val header: Header) {
190171
return when (orderedUniqueFunctions.size) {
191172
0 -> FnMatch.Error(fn.identifier, args, candidates)
192173
1 -> orderedUniqueFunctions.first()
193-
else -> FnMatch.Dynamic(orderedUniqueFunctions, canReturnMissing)
174+
else -> FnMatch.Dynamic(orderedUniqueFunctions)
194175
}
195176
}
196177

197178
private fun buildArgumentPermutations(args: List<StaticType>): List<List<StaticType>> {
198-
val flattenedArgs = args.map {
199-
if (it is AnyOfType) {
200-
it.flatten().allTypes.filter { it !is NullType }
201-
} else {
202-
it.flatten().allTypes
203-
}
204-
}
179+
val flattenedArgs = args.map { it.flatten().allTypes }
205180
return buildArgumentPermutations(flattenedArgs, accumulator = emptyList())
206181
}
207182

@@ -229,19 +204,13 @@ internal class FnResolver(private val header: Header) {
229204
*/
230205
public fun resolveAgg(agg: Agg.Unresolved, args: List<Rex>): FnMatch<FunctionSignature.Aggregation> {
231206
val candidates = lookup(agg)
232-
var hadMissingArg = false
233207
val parameters = args.mapIndexed { i, arg ->
234-
if (!hadMissingArg && arg.type.isMissable()) {
235-
hadMissingArg = true
236-
}
237208
FunctionParameter("arg-$i", arg.type.toRuntimeType())
238209
}
239-
val match = match(candidates, parameters)
240-
return when (match) {
210+
return when (val match = match(candidates, parameters)) {
241211
null -> FnMatch.Error(agg.identifier, args, candidates)
242212
else -> {
243-
val isMissable = hadMissingArg || isUnsafeCast(match.signature.specific)
244-
FnMatch.Ok(match.signature, match.mapping, isMissable)
213+
FnMatch.Ok(match.signature, match.mapping)
245214
}
246215
}
247216
}
@@ -290,9 +259,7 @@ internal class FnResolver(private val header: Header) {
290259
a.type == p.type -> mapping.add(null)
291260
// 2. Match ANY, no coercion needed
292261
p.type == ANY -> mapping.add(null)
293-
// 3. Match NULL argument
294-
a.type == NULL -> mapping.add(null)
295-
// 4. Check for a coercion
262+
// 3. Check for a coercion
296263
else -> {
297264
val coercion = lookupCoercion(a.type, p.type)
298265
when (coercion) {
@@ -440,8 +407,10 @@ internal class FnResolver(private val header: Header) {
440407
// This is not explicitly defined in the PartiQL Specification!!
441408
// This does not imply the ability to CAST; this defines function resolution behavior.
442409
private val precedence: Map<PartiQLValueType, Int> = listOf(
443-
NULL,
444-
MISSING,
410+
@Suppress("DEPRECATION")
411+
PartiQLValueType.NULL, // TODO: Remove
412+
@Suppress("DEPRECATION")
413+
PartiQLValueType.MISSING, // TODO: Remove
445414
BOOL,
446415
INT8,
447416
INT16,

0 commit comments

Comments
 (0)