@@ -4,11 +4,15 @@ import org.partiql.planner.PartiQLPlanner
4
4
import org.partiql.planner.internal.ir.Agg
5
5
import org.partiql.planner.internal.ir.Catalog
6
6
import org.partiql.planner.internal.ir.Fn
7
- import org.partiql.planner.internal.ir.Identifier
8
- import org.partiql.planner.internal.ir.Rel
9
7
import org.partiql.planner.internal.ir.Rex
10
- import org.partiql.planner.internal.ir.identifierSymbol
8
+ import org.partiql.planner.internal.ir.catalogSymbolRef
9
+ import org.partiql.planner.internal.ir.rex
10
+ import org.partiql.planner.internal.ir.rexOpGlobal
11
+ import org.partiql.planner.internal.ir.rexOpLit
12
+ import org.partiql.planner.internal.ir.rexOpPathKey
13
+ import org.partiql.planner.internal.ir.rexOpPathSymbol
11
14
import org.partiql.planner.internal.typer.FnResolver
15
+ import org.partiql.planner.internal.typer.TypeEnv
12
16
import org.partiql.spi.BindingCase
13
17
import org.partiql.spi.BindingName
14
18
import org.partiql.spi.BindingPath
@@ -17,94 +21,29 @@ import org.partiql.spi.connector.ConnectorObjectHandle
17
21
import org.partiql.spi.connector.ConnectorObjectPath
18
22
import org.partiql.spi.connector.ConnectorSession
19
23
import org.partiql.types.StaticType
20
- import org.partiql.types.StructType
21
- import org.partiql.types.TupleConstraint
22
24
import org.partiql.types.function.FunctionSignature
25
+ import org.partiql.value.PartiQLValueExperimental
26
+ import org.partiql.value.stringValue
23
27
24
28
/* *
25
29
* Handle for associating a catalog name with catalog related metadata objects.
26
30
*/
27
31
internal typealias Handle <T > = Pair <String , T >
28
32
29
33
/* *
30
- * TypeEnv represents the environment in which we type expressions and resolve variables while planning.
34
+ * Metadata for a resolved global variable
31
35
*
32
- * TODO TypeEnv should be a stack of locals; also the strategy has been kept here because it's easier to
33
- * pass through the traversal like this, but is conceptually odd to associate with the TypeEnv.
34
- * @property schema
35
- * @property strategy
36
- */
37
- internal class TypeEnv (
38
- val schema : List <Rel .Binding >,
39
- val strategy : ResolutionStrategy ,
40
- ) {
41
-
42
- /* *
43
- * Return a copy with GLOBAL lookup strategy
44
- */
45
- fun global () = TypeEnv (schema, ResolutionStrategy .GLOBAL )
46
-
47
- /* *
48
- * Return a copy with LOCAL lookup strategy
49
- */
50
- fun local () = TypeEnv (schema, ResolutionStrategy .LOCAL )
51
-
52
- /* *
53
- * Debug string
54
- */
55
- override fun toString () = buildString {
56
- append(" (" )
57
- append(" strategy=$strategy " )
58
- append(" , " )
59
- val bindings = " < " + schema.joinToString { " ${it.name} : ${it.type} " } + " >"
60
- append(" bindings=$bindings " )
61
- append(" )" )
62
- }
63
- }
64
-
65
- /* *
66
- * Metadata regarding a resolved variable.
36
+ * @property type Resolved StaticType
37
+ * @property ordinal The relevant catalog's index offset in the [Env.catalogs] list
67
38
* @property depth The depth/level of the path match.
39
+ * @property position The relevant value's index offset in the [Catalog.values] list
68
40
*/
69
- internal sealed interface ResolvedVar {
70
-
71
- public val type: StaticType
72
- public val ordinal: Int
73
- public val depth: Int
74
-
75
- /* *
76
- * Metadata for a resolved local variable.
77
- *
78
- * @property type Resolved StaticType
79
- * @property ordinal Index offset in [TypeEnv]
80
- * @property resolvedSteps The fully resolved path steps.s
81
- */
82
- class Local (
83
- override val type : StaticType ,
84
- override val ordinal : Int ,
85
- val rootType : StaticType ,
86
- val resolvedSteps : List <BindingName >,
87
- ) : ResolvedVar {
88
- // the depth are always going to be 1 because this is local variable.
89
- // the global path, however the path length maybe, going to be replaced by a binding name.
90
- override val depth: Int = 1
91
- }
92
-
93
- /* *
94
- * Metadata for a resolved global variable
95
- *
96
- * @property type Resolved StaticType
97
- * @property ordinal The relevant catalog's index offset in the [Env.catalogs] list
98
- * @property depth The depth/level of the path match.
99
- * @property position The relevant value's index offset in the [Catalog.values] list
100
- */
101
- class Global (
102
- override val type : StaticType ,
103
- override val ordinal : Int ,
104
- override val depth : Int ,
105
- val position : Int ,
106
- ) : ResolvedVar
107
- }
41
+ internal class ResolvedVar (
42
+ val type : StaticType ,
43
+ val ordinal : Int ,
44
+ val depth : Int ,
45
+ val position : Int ,
46
+ )
108
47
109
48
/* *
110
49
* Variable resolution strategies — https://partiql.org/assets/PartiQL-Specification.pdf#page=35
@@ -235,7 +174,7 @@ internal class Env(
235
174
type
236
175
)
237
176
// Return resolution metadata
238
- ResolvedVar . Global (type, catalogIndex, depth, valueIndex)
177
+ ResolvedVar (type, catalogIndex, depth, valueIndex)
239
178
}
240
179
}
241
180
}
@@ -282,31 +221,13 @@ internal class Env(
282
221
}
283
222
}
284
223
285
- private fun BindingPath.toCaseSensitive (): BindingPath {
286
- return this .copy(steps = this .steps.map { it.copy(bindingCase = BindingCase .SENSITIVE ) })
287
- }
288
-
289
224
/* *
290
225
* Attempt to resolve a [BindingPath] in the global + local type environments.
291
226
*/
292
- fun resolve (path : BindingPath , locals : TypeEnv , scope : Rex .Op .Var .Scope ): ResolvedVar ? {
293
- val strategy = when (scope) {
294
- Rex .Op .Var .Scope .DEFAULT -> locals.strategy
295
- Rex .Op .Var .Scope .LOCAL -> ResolutionStrategy .LOCAL
296
- }
227
+ fun resolve (path : BindingPath , locals : TypeEnv , strategy : ResolutionStrategy ): Rex ? {
297
228
return when (strategy) {
298
- ResolutionStrategy .LOCAL -> {
299
- var type: ResolvedVar ? = null
300
- type = type ? : resolveLocalBind(path, locals.schema)
301
- type = type ? : resolveGlobalBind(path)
302
- type
303
- }
304
- ResolutionStrategy .GLOBAL -> {
305
- var type: ResolvedVar ? = null
306
- type = type ? : resolveGlobalBind(path)
307
- type = type ? : resolveLocalBind(path, locals.schema)
308
- type
309
- }
229
+ ResolutionStrategy .LOCAL -> locals.resolve(path) ? : resolveGlobalBind(path)
230
+ ResolutionStrategy .GLOBAL -> resolveGlobalBind(path) ? : locals.resolve(path)
310
231
}
311
232
}
312
233
@@ -320,7 +241,7 @@ internal class Env(
320
241
* TODO: Add global bindings
321
242
* TODO: Replace paths with global variable references if found
322
243
*/
323
- private fun resolveGlobalBind (path : BindingPath ): ResolvedVar ? {
244
+ private fun resolveGlobalBind (path : BindingPath ): Rex ? {
324
245
val currentCatalog = session.currentCatalog?.let { BindingName (it, BindingCase .SENSITIVE ) }
325
246
val currentCatalogPath = BindingPath (session.currentDirectory.map { BindingName (it, BindingCase .SENSITIVE ) })
326
247
val absoluteCatalogPath = BindingPath (currentCatalogPath.steps + path.steps)
@@ -335,122 +256,13 @@ internal class Env(
335
256
? : getGlobalType(currentCatalog, path, path)
336
257
? : getGlobalType(currentCatalog, path, absoluteCatalogPath)
337
258
}
338
- }
339
- return resolvedVar
259
+ } ? : return null
260
+ // rewrite as path expression for any remaining steps.
261
+ val root = rex(resolvedVar.type, rexOpGlobal(catalogSymbolRef(resolvedVar.ordinal, resolvedVar.position)))
262
+ val tail = path.steps.drop(resolvedVar.depth)
263
+ return if (tail.isEmpty()) root else root.toPath(tail)
340
264
}
341
265
342
- /* *
343
- * Check locals, else search structs.
344
- */
345
- internal fun resolveLocalBind (path : BindingPath , locals : List <Rel .Binding >): ResolvedVar ? {
346
- if (path.steps.isEmpty()) {
347
- return null
348
- }
349
-
350
- // 1. Check locals for root
351
- locals.forEachIndexed { ordinal, binding ->
352
- val root = path.steps[0 ]
353
- if (root.isEquivalentTo(binding.name)) {
354
- return ResolvedVar .Local (binding.type, ordinal, binding.type, path.steps)
355
- }
356
- }
357
-
358
- // 2. Check if this variable is referencing a struct field, carrying ordinals
359
- val matches = mutableListOf<ResolvedVar .Local >()
360
- for (ordinal in locals.indices) {
361
- val rootType = locals[ordinal].type
362
- val pathPrefix = BindingName (locals[ordinal].name, BindingCase .SENSITIVE )
363
- if (rootType is StructType ) {
364
- val varType = inferStructLookup(rootType, path)
365
- if (varType != null ) {
366
- // we found this path within a struct!
367
- val match = ResolvedVar .Local (
368
- varType.resolvedType,
369
- ordinal,
370
- rootType,
371
- listOf (pathPrefix) + varType.replacementPath.steps,
372
- )
373
- matches.add(match)
374
- }
375
- }
376
- }
377
-
378
- // 0 -> no match
379
- // 1 -> resolved
380
- // N -> ambiguous
381
- return when (matches.size) {
382
- 0 -> null
383
- 1 -> matches.single()
384
- else -> null // TODO emit ambiguous error
385
- }
386
- }
387
-
388
- /* *
389
- * Searches for the path within the given struct, returning null if not found.
390
- *
391
- * @return a [ResolvedPath] that contains the disambiguated [ResolvedPath.replacementPath] and the path's
392
- * [StaticType]. Returns NULL if unable to find the [path] given the [struct].
393
- */
394
- private fun inferStructLookup (struct : StructType , path : BindingPath ): ResolvedPath ? {
395
- var curr: StaticType = struct
396
- val replacementSteps = path.steps.map { step ->
397
- // Assume ORDERED for now
398
- val currentStruct = curr as ? StructType ? : return null
399
- val (replacement, stepType) = inferStructLookup(currentStruct, step) ? : return null
400
- curr = stepType
401
- replacement
402
- }
403
- // Lookup final field
404
- return ResolvedPath (
405
- BindingPath (replacementSteps),
406
- curr
407
- )
408
- }
409
-
410
- /* *
411
- * Represents a disambiguated [BindingPath] and its inferred [StaticType].
412
- */
413
- private class ResolvedPath (
414
- val replacementPath : BindingPath ,
415
- val resolvedType : StaticType ,
416
- )
417
-
418
- /* *
419
- * @return a disambiguated [key] and the resulting [StaticType].
420
- */
421
- private fun inferStructLookup (struct : StructType , key : BindingName ): Pair <BindingName , StaticType >? {
422
- val isClosed = struct.constraints.contains(TupleConstraint .Open (false ))
423
- val isOrdered = struct.constraints.contains(TupleConstraint .Ordered )
424
- return when {
425
- // 1. Struct is closed and ordered
426
- isClosed && isOrdered -> {
427
- struct.fields.firstOrNull { entry -> key.isEquivalentTo(entry.key) }?.let {
428
- (sensitive(it.key) to it.value)
429
- }
430
- }
431
- // 2. Struct is closed
432
- isClosed -> {
433
- val matches = struct.fields.filter { entry -> key.isEquivalentTo(entry.key) }
434
- when (matches.size) {
435
- 0 -> null
436
- 1 -> matches.first().let { (sensitive(it.key) to it.value) }
437
- else -> {
438
- val firstKey = matches.first().key
439
- val sharedKey = when (matches.all { it.key == firstKey }) {
440
- true -> sensitive(firstKey)
441
- false -> key
442
- }
443
- sharedKey to StaticType .unionOf(matches.map { it.value }.toSet()).flatten()
444
- }
445
- }
446
- }
447
- // 3. Struct is open
448
- else -> key to StaticType .ANY
449
- }
450
- }
451
-
452
- private fun sensitive (str : String ): BindingName = BindingName (str, BindingCase .SENSITIVE )
453
-
454
266
/* *
455
267
* Logic for determining how many BindingNames were “matched” by the ConnectorMetadata
456
268
* 1. Matched = RelativePath - Not Found
@@ -466,16 +278,12 @@ internal class Env(
466
278
return originalPath.steps.size + outputCatalogPath.steps.size - inputCatalogPath.steps.size
467
279
}
468
280
469
- private fun String.toIdentifier () = identifierSymbol(
470
- symbol = this ,
471
- caseSensitivity = Identifier .CaseSensitivity .SENSITIVE
472
- )
473
-
474
- private fun BindingName.toIdentifier () = identifierSymbol(
475
- symbol = name,
476
- caseSensitivity = when (bindingCase) {
477
- BindingCase .SENSITIVE -> Identifier .CaseSensitivity .SENSITIVE
478
- BindingCase .INSENSITIVE -> Identifier .CaseSensitivity .INSENSITIVE
281
+ @OptIn(PartiQLValueExperimental ::class )
282
+ private fun Rex.toPath (steps : List <BindingName >): Rex = steps.fold(this ) { curr, step ->
283
+ val op = when (step.bindingCase) {
284
+ BindingCase .SENSITIVE -> rexOpPathKey(curr, rex(StaticType .STRING , rexOpLit(stringValue(step.name))))
285
+ BindingCase .INSENSITIVE -> rexOpPathSymbol(curr, step.name)
479
286
}
480
- )
287
+ rex(StaticType .ANY , op)
288
+ }
481
289
}
0 commit comments