diff --git a/lang/src/org/partiql/lang/planner/UniqueIdResolver.kt b/lang/src/org/partiql/lang/planner/MetadataResolver.kt similarity index 65% rename from lang/src/org/partiql/lang/planner/UniqueIdResolver.kt rename to lang/src/org/partiql/lang/planner/MetadataResolver.kt index cf581e8999..2f677b279a 100644 --- a/lang/src/org/partiql/lang/planner/UniqueIdResolver.kt +++ b/lang/src/org/partiql/lang/planner/MetadataResolver.kt @@ -8,6 +8,9 @@ sealed class ResolutionResult { /** * A success case, indicates the [uniqueId] of the match to the [BindingName] in the global scope. * Typically, this is defined by the storage layer. + * + * In the future, this will likely contain much more than just a unique id. It might include detailed schema + * information about global variables. */ data class GlobalVariable(val uniqueId: String) : ResolutionResult() @@ -22,9 +25,19 @@ sealed class ResolutionResult { object Undefined : ResolutionResult() } -fun interface UniqueIdResolver { +/** + * Supplies the query planner with metadata about the current database. Meant to be implemented by the application + * embedding PartiQL. + * + * Metadata is associated with global variables. Global variables can be tables or (less commonly) any other + * application specific global variable. + * + * In the future, new methods could be added which expose information about other types of database metadata such as + * available indexes and table statistics. + */ +interface MetadataResolver { /** - * Implementations try to resolve a global variable which is typically a database table to a unique identifier + * Implementations try to resolve a variable which is typically a database table to a schema * using [bindingName]. [bindingName] includes both the name as specified by the query author and a [BindingCase] * which indicates if query author included double quotes (") which mean the lookup should be case-sensitive. * @@ -41,10 +54,12 @@ fun interface UniqueIdResolver { * Note that while [ResolutionResult.LocalVariable] exists, it is intentionally marked `internal` and cannot * be used by outside this project. */ - fun resolve(bindingName: BindingName): ResolutionResult + fun resolveVariable(bindingName: BindingName): ResolutionResult } -private val EMPTY = UniqueIdResolver { ResolutionResult.Undefined } +private val EMPTY: MetadataResolver = object : MetadataResolver { + override fun resolveVariable(bindingName: BindingName): ResolutionResult = ResolutionResult.Undefined +} -/** Convenience function for obtaining an instance of [UniqueIdResolver] with no defined variables. */ -fun emptyUniqueIdResolver(): UniqueIdResolver = EMPTY +/** Convenience function for obtaining an instance of [MetadataResolver] with no defined variables. */ +fun emptyMetadataResolver(): MetadataResolver = EMPTY diff --git a/lang/src/org/partiql/lang/planner/transforms/LogicalToLogicalResolvedVisitorTransform.kt b/lang/src/org/partiql/lang/planner/transforms/LogicalToLogicalResolvedVisitorTransform.kt index c2416c4b60..3f4f2ccfa1 100644 --- a/lang/src/org/partiql/lang/planner/transforms/LogicalToLogicalResolvedVisitorTransform.kt +++ b/lang/src/org/partiql/lang/planner/transforms/LogicalToLogicalResolvedVisitorTransform.kt @@ -10,15 +10,15 @@ import org.partiql.lang.errors.Problem import org.partiql.lang.errors.ProblemHandler import org.partiql.lang.eval.BindingName import org.partiql.lang.eval.builtins.DYNAMIC_LOOKUP_FUNCTION_NAME +import org.partiql.lang.planner.MetadataResolver import org.partiql.lang.planner.ResolutionResult -import org.partiql.lang.planner.UniqueIdResolver import org.partiql.pig.runtime.asPrimitive /** * Resolves all variables by rewriting `(id )` to * `(id )`) or `(global_id )`, or a `$__dynamic_lookup__` call site (if enabled). * - * Local variables are resolved independently within this pass, but we rely on [globals] to resolve global variables. + * Local variables are resolved independently within this pass, but we rely on [resolver] to resolve global variables. * * There are actually two passes here: * 1. All [PartiqlLogical.VarDecl] nodes are allocated unique indexes (which is stored in a meta). This pass is @@ -71,14 +71,14 @@ import org.partiql.pig.runtime.asPrimitive */ internal fun PartiqlLogical.Plan.toResolvedPlan( problemHandler: ProblemHandler, - globals: UniqueIdResolver, + resolver: MetadataResolver, allowUndefinedVariables: Boolean = false ): PartiqlLogicalResolved.Plan { // Allocate a unique id for each `VarDecl` val (planWithAllocatedVariables, allLocals) = this.allocateVariableIds() // Transform to `partiql_logical_resolved` while resolving variables. - val resolvedSt = LogicalToLogicalResolvedVisitorTransform(allowUndefinedVariables, problemHandler, globals) + val resolvedSt = LogicalToLogicalResolvedVisitorTransform(allowUndefinedVariables, problemHandler, resolver) .transformPlan(planWithAllocatedVariables) .copy(locals = allLocals) @@ -119,7 +119,7 @@ private data class LogicalToLogicalResolvedVisitorTransform( /** Where to send error reports. */ private val problemHandler: ProblemHandler, /** If a variable is not found using [inputScope], we will attempt to locate the binding here instead. */ - private val globals: UniqueIdResolver, + private val globals: MetadataResolver, ) : PartiqlLogicalToPartiqlLogicalResolvedVisitorTransform() { /** The current [LocalScope]. */ @@ -253,14 +253,14 @@ private data class LogicalToLogicalResolvedVisitorTransform( node.qualifier is PartiqlLogical.ScopeQualifier.Unqualified ) { // look up variable in globals first, then locals - when (val globalResolutionResult = globals.resolve(bindingName)) { + when (val globalResolutionResult = globals.resolveVariable(bindingName)) { ResolutionResult.Undefined -> lookupLocalVariable(bindingName) else -> globalResolutionResult } } else { // look up variable in locals first, then globals. when (val localResolutionResult = lookupLocalVariable(bindingName)) { - ResolutionResult.Undefined -> globals.resolve(bindingName) + ResolutionResult.Undefined -> globals.resolveVariable(bindingName) else -> localResolutionResult } } diff --git a/lang/test/org/partiql/lang/planner/Util.kt b/lang/test/org/partiql/lang/planner/Util.kt index f2e5381ef5..b4635b1e16 100644 --- a/lang/test/org/partiql/lang/planner/Util.kt +++ b/lang/test/org/partiql/lang/planner/Util.kt @@ -3,18 +3,21 @@ package org.partiql.lang.planner import org.partiql.lang.ast.SourceLocationMeta import org.partiql.lang.errors.Problem import org.partiql.lang.errors.ProblemDetails +import org.partiql.lang.eval.BindingName /** - * Creates a fake implementation of [UniqueIdResolver] with the specified [globalVariableNames]. + * Creates a fake implementation of [MetadataResolver] with the specified [globalVariableNames]. * * The fake unique identifier of bound variables is computed to be `fake_uid_for_${globalVariableName}`. */ -fun createFakeGlobalBindings(vararg globalVariableNames: Pair) = - UniqueIdResolver { bindingName -> - val matches = globalVariableNames.filter { bindingName.isEquivalentTo(it.first) } - when (matches.size) { - 0 -> ResolutionResult.Undefined - else -> ResolutionResult.GlobalVariable(matches.first().second) +fun createFakeMetadataResolver(vararg globalVariableNames: Pair) = + object : MetadataResolver { + override fun resolveVariable(bindingName: BindingName): ResolutionResult { + val matches = globalVariableNames.filter { bindingName.isEquivalentTo(it.first) } + return when (matches.size) { + 0 -> ResolutionResult.Undefined + else -> ResolutionResult.GlobalVariable(matches.first().second) + } } } diff --git a/lang/test/org/partiql/lang/planner/transforms/LogicalToLogicalResolvedVisitorTransformTests.kt b/lang/test/org/partiql/lang/planner/transforms/LogicalToLogicalResolvedVisitorTransformTests.kt index b985bc0046..eccfd4c4b3 100644 --- a/lang/test/org/partiql/lang/planner/transforms/LogicalToLogicalResolvedVisitorTransformTests.kt +++ b/lang/test/org/partiql/lang/planner/transforms/LogicalToLogicalResolvedVisitorTransformTests.kt @@ -14,7 +14,7 @@ import org.partiql.lang.errors.ProblemCollector import org.partiql.lang.eval.BindingCase import org.partiql.lang.eval.builtins.DYNAMIC_LOOKUP_FUNCTION_NAME import org.partiql.lang.eval.sourceLocationMeta -import org.partiql.lang.planner.createFakeGlobalBindings +import org.partiql.lang.planner.createFakeMetadataResolver import org.partiql.lang.planner.problem import org.partiql.lang.syntax.SqlParser import org.partiql.lang.util.ArgumentsProviderBase @@ -91,7 +91,7 @@ class LogicalToLogicalResolvedVisitorTransformTests { } /** Mock table resolver. That can resolve f, foo, or UPPERCASE_FOO, while respecting case-sensitivity. */ - private val globalBindings = createFakeGlobalBindings( + private val globalBindings = createFakeMetadataResolver( *listOf( "shadow", "foo",