Skip to content
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

Rename UniqueIdResolver to MetadataResolver #609

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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