Skip to content

Commit

Permalink
Rename UniqueIdResolver to MetadataResolver (#609)
Browse files Browse the repository at this point in the history
  • Loading branch information
dlurton authored May 23, 2022
1 parent 6fd6cc5 commit d2b6ede
Show file tree
Hide file tree
Showing 4 changed files with 40 additions and 22 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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()

Expand All @@ -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.
*
Expand All @@ -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
Original file line number Diff line number Diff line change
Expand Up @@ -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 <name> <case-sensitivity> <scope-qualifier>)` to
* `(id <unique-index>)`) or `(global_id <name> <unique-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
Expand Down Expand Up @@ -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)

Expand Down Expand Up @@ -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]. */
Expand Down Expand Up @@ -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
}
}
Expand Down
17 changes: 10 additions & 7 deletions lang/test/org/partiql/lang/planner/Util.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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<String, String>) =
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<String, String>) =
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)
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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",
Expand Down

0 comments on commit d2b6ede

Please sign in to comment.