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

Simplify functions in preparation for PartiQL-PATH #1530

Merged
merged 7 commits into from
Jul 31, 2024
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 @@ -57,7 +57,6 @@ import org.partiql.plan.Statement
import org.partiql.plan.debug.PlanPrinter
import org.partiql.plan.visitor.PlanBaseVisitor
import org.partiql.spi.fn.Agg
import org.partiql.spi.fn.FnExperimental
import org.partiql.types.PType
import org.partiql.value.PartiQLValueExperimental
import java.lang.IllegalStateException
Expand Down Expand Up @@ -179,7 +178,6 @@ internal class Compiler(
return RelAggregate(input, groups, calls)
}

@OptIn(FnExperimental::class)
override fun visitRelOpAggregateCall(node: Rel.Op.Aggregate.Call, ctx: PType?): Operator.Aggregation {
val args = node.args.map { visitRex(it, it.type).modeHandled() }
val setQuantifier: Operator.Aggregation.SetQuantifier = when (node.setQuantifier) {
Expand Down Expand Up @@ -212,7 +210,6 @@ internal class Compiler(
return ExprPathIndex(root, index)
}

@OptIn(FnExperimental::class)
override fun visitRexOpCallStatic(node: Rex.Op.Call.Static, ctx: PType?): Operator {
val fn = symbols.getFn(node.fn)
val args = node.args.map { visitRex(it, ctx) }.toTypedArray()
Expand All @@ -225,7 +222,6 @@ internal class Compiler(
}
}

@OptIn(FnExperimental::class)
override fun visitRexOpCallDynamic(node: Rex.Op.Call.Dynamic, ctx: PType?): Operator {
val args = node.args.map { visitRex(it, ctx).modeHandled() }.toTypedArray()
// Check candidate list size
Expand Down
19 changes: 8 additions & 11 deletions partiql-eval/src/main/kotlin/org/partiql/eval/internal/Symbols.kt
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
@file:OptIn(FnExperimental::class)

package org.partiql.eval.internal

Expand All @@ -7,30 +6,30 @@ import org.partiql.eval.internal.operator.rex.ExprVarGlobal
import org.partiql.plan.Catalog
import org.partiql.plan.PartiQLPlan
import org.partiql.plan.Ref
import org.partiql.spi.connector.ConnectorAggProvider
import org.partiql.spi.connector.ConnectorBindings
import org.partiql.spi.connector.ConnectorFnProvider
import org.partiql.spi.connector.ConnectorPath
import org.partiql.spi.fn.Agg
import org.partiql.spi.fn.Fn
import org.partiql.spi.fn.FnExperimental
import org.partiql.spi.fn.SqlFnProvider

/**
*
*
* @property catalogs
*/
@OptIn(FnExperimental::class)

internal class Symbols private constructor(private val catalogs: Array<C>) {

private class C(
val name: String,
val bindings: ConnectorBindings,
val functions: ConnectorFnProvider,
val aggregations: ConnectorAggProvider,
val items: Array<Catalog.Item>,
) {

// TEMPORARY FOR DEPENDENCY REASONS
fun getFn(path: ConnectorPath, specific: String): Fn? = SqlFnProvider.getFn(specific)
fun getAgg(path: ConnectorPath, specific: String): Agg? = SqlFnProvider.getAgg(specific)

override fun toString(): String = name
}

Expand All @@ -52,7 +51,7 @@ internal class Symbols private constructor(private val catalogs: Array<C>) {
}
// Lookup in connector
val path = ConnectorPath(item.path)
return catalog.functions.getFn(path, item.specific)
return catalog.getFn(path, item.specific)
?: error("Catalog `$catalog` has no entry for function $item")
}

Expand All @@ -64,7 +63,7 @@ internal class Symbols private constructor(private val catalogs: Array<C>) {
}
// Lookup in connector
val path = ConnectorPath(item.path)
return catalog.aggregations.getAgg(path, item.specific)
return catalog.getAgg(path, item.specific)
?: error("Catalog `$catalog` has no entry for aggregation function $item")
}

Expand All @@ -85,8 +84,6 @@ internal class Symbols private constructor(private val catalogs: Array<C>) {
C(
name = it.name,
bindings = connector.getBindings(),
functions = connector.getFunctions(),
aggregations = connector.getAggregations(),
items = it.items.toTypedArray()
)
}.toTypedArray()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ import org.partiql.eval.internal.Environment
import org.partiql.eval.internal.Record
import org.partiql.eval.value.Datum
import org.partiql.spi.fn.Agg
import org.partiql.spi.fn.FnExperimental
import org.partiql.value.PartiQLValueExperimental

internal sealed interface Operator {

Expand All @@ -14,7 +12,6 @@ internal sealed interface Operator {
*/
interface Expr : Operator {

@OptIn(PartiQLValueExperimental::class)
fun eval(env: Environment): Datum
}

Expand All @@ -30,7 +27,6 @@ internal sealed interface Operator {

interface Aggregation : Operator {

@OptIn(FnExperimental::class)
val delegate: Agg

val args: List<Expr>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import org.partiql.eval.internal.Record
import org.partiql.eval.internal.operator.Operator
import org.partiql.eval.value.Datum
import org.partiql.spi.fn.Agg
import org.partiql.spi.fn.FnExperimental
import org.partiql.value.PartiQLValue
import org.partiql.value.PartiQLValueExperimental
import org.partiql.value.PartiQLValueType
Expand Down Expand Up @@ -51,13 +50,13 @@ internal class RelAggregate(
*
* @property seen maintains which values have already been seen. If null, we accumulate all values coming through.
*/
class AccumulatorWrapper @OptIn(PartiQLValueExperimental::class, FnExperimental::class) constructor(
class AccumulatorWrapper @OptIn(PartiQLValueExperimental::class) constructor(
val delegate: Agg.Accumulator,
val args: List<Operator.Expr>,
val seen: TreeSet<List<PartiQLValue>>?
)

@OptIn(PartiQLValueExperimental::class, FnExperimental::class)
@OptIn(PartiQLValueExperimental::class)
override fun open(env: Environment) {
input.open(env)
for (inputRecord in input) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import org.partiql.eval.internal.operator.Operator
import org.partiql.eval.value.Datum
import org.partiql.plan.Ref
import org.partiql.spi.fn.Fn
import org.partiql.spi.fn.FnExperimental
import org.partiql.types.PType
import org.partiql.value.PartiQLValue
import org.partiql.value.PartiQLValueExperimental
Expand All @@ -20,7 +19,7 @@ import org.partiql.value.PartiQLValueType
* [ExprCallStatic]'s. By doing this, this implementation can evaluate ([eval]) the input [Record], execute and gather the
* arguments, and pass the [PartiQLValue]s directly to the [Candidate.eval].
*/
@OptIn(PartiQLValueExperimental::class, FnExperimental::class)
@OptIn(PartiQLValueExperimental::class)
internal class ExprCallDynamic(
private val name: String,
private val candidates: Array<Candidate>,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,9 @@ import org.partiql.eval.internal.Environment
import org.partiql.eval.internal.operator.Operator
import org.partiql.eval.value.Datum
import org.partiql.spi.fn.Fn
import org.partiql.spi.fn.FnExperimental
import org.partiql.value.PartiQLValueExperimental

@OptIn(FnExperimental::class, PartiQLValueExperimental::class)
@OptIn(PartiQLValueExperimental::class)
internal class ExprCallStatic(
private val fn: Fn,
private val inputs: Array<Operator.Expr>,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1258,7 +1258,7 @@ class PartiQLEngineDefaultTest {

internal fun assert() {
val statement = parser.parse(input).root
val catalogBuilder = MemoryCatalog.PartiQL().name("memory")
val catalogBuilder = MemoryCatalog.builder().name("memory")
globals.forEach { global ->
catalogBuilder.define(global.name, global.type, loader.loadSingleElement(global.value))
}
Expand Down Expand Up @@ -1344,7 +1344,7 @@ class PartiQLEngineDefaultTest {

private fun run(mode: PartiQLEngine.Mode): Pair<PartiQLValue, PartiQLPlan> {
val statement = parser.parse(input).root
val catalog = MemoryCatalog.PartiQL().name("memory").build()
val catalog = MemoryCatalog.builder().name("memory").build()
val connector = MemoryConnector(catalog)
val connectorSession = object : ConnectorSession {
override fun getQueryId(): String = "q"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import org.partiql.eval.value.Datum.int32Value
import org.partiql.eval.value.Datum.listValue
import org.partiql.eval.value.Datum.stringValue
import org.partiql.spi.fn.Fn
import org.partiql.spi.fn.FnExperimental
import org.partiql.spi.fn.FnParameter
import org.partiql.spi.fn.FnSignature
import org.partiql.value.PartiQLValue
Expand Down Expand Up @@ -64,7 +63,7 @@ class ExprCallDynamicTest {
PartiQLValueType.ANY to PartiQLValueType.ANY, // Index 12
)

@OptIn(FnExperimental::class, PartiQLValueExperimental::class)
@OptIn(PartiQLValueExperimental::class)
internal val candidates = params.mapIndexed { index, it ->
ExprCallDynamic.Candidate(
fn = object : Fn {
Expand Down
1 change: 0 additions & 1 deletion partiql-planner/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,6 @@ tasks.register<Exec>("codegen") {
"--poems", "builder",
"--poems", "util",
"--opt-in", "org.partiql.value.PartiQLValueExperimental",
"--opt-in", "org.partiql.spi.fn.FnExperimental",
"./src/main/resources/partiql_plan_internal.ion"
)
}
Expand Down
53 changes: 24 additions & 29 deletions partiql-planner/src/main/kotlin/org/partiql/planner/internal/Env.kt
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,10 @@ import org.partiql.planner.internal.ir.rexOpCastResolved
import org.partiql.planner.internal.ir.rexOpVarGlobal
import org.partiql.planner.internal.typer.CompilerType
import org.partiql.planner.internal.typer.Scope.Companion.toPath
import org.partiql.spi.BindingCase
import org.partiql.spi.BindingName
import org.partiql.spi.BindingPath
import org.partiql.spi.connector.ConnectorMetadata
import org.partiql.spi.fn.AggSignature
import org.partiql.spi.fn.FnExperimental
import org.partiql.spi.fn.SqlFnProvider
import org.partiql.types.PType
import org.partiql.types.PType.Kind

Expand Down Expand Up @@ -53,14 +51,9 @@ internal class Env(private val session: Session) {
private val objects: PathResolverObj = PathResolverObj(catalog, catalogs, session)

/**
* A [PathResolver] for looking up functions given both unqualified and qualified names.
* A [SqlFnProvider] for looking up built-in functions.
*/
private val fns: PathResolverFn = PathResolverFn(catalog, catalogs, session)

/**
* A [PathResolver] for aggregation function lookup.
*/
private val aggs: PathResolverAgg = PathResolverAgg(catalog, catalogs, session)
private val fns: SqlFnProvider = SqlFnProvider

/**
* This function looks up a global [BindingPath], returning a global reference expression.
Expand All @@ -85,19 +78,23 @@ internal class Env(private val session: Session) {
return if (tail.isEmpty()) root else root.toPath(tail)
}

@OptIn(FnExperimental::class)
fun resolveFn(path: BindingPath, args: List<Rex>): Rex? {
val item = fns.lookup(path) ?: return null
// Invoke FnResolver to determine if we made a match
val variants = item.handle.entity.getVariants()
// Assume all functions are defined in the current catalog and reject qualified routine names.
if (path.steps.size > 1) {
error("Qualified functions are not supported.")
}
val catalog = session.getCatalog()
val name = path.steps.last().name.lowercase()
// Invoke existing function resolution logic
val variants = fns.lookupFn(name) ?: return null
val match = FnResolver.resolve(variants, args.map { it.type })
// If Type mismatch, then we return a missingOp whose trace is all possible candidates.
if (match == null) {
val candidates = variants.map { fnSignature ->
rexOpCallDynamicCandidate(
fn = refFn(
item.catalog,
name = Name.of(item.handle.path.steps),
catalog = catalog,
name = Name.of(name),
signature = fnSignature
),
coercions = emptyList()
Expand All @@ -114,8 +111,8 @@ internal class Env(private val session: Session) {
// Create an internal typed reference for every candidate
rexOpCallDynamicCandidate(
fn = refFn(
catalog = item.catalog,
name = Name.of(item.handle.path.steps),
catalog = catalog,
name = Name.of(name),
signature = it.signature,
),
coercions = it.mapping.toList(),
Expand All @@ -127,8 +124,8 @@ internal class Env(private val session: Session) {
is FnMatch.Static -> {
// Create an internal typed reference
val ref = refFn(
catalog = item.catalog,
name = Name.of(item.handle.path.steps),
catalog = catalog,
name = Name.of(name),
signature = match.signature,
)
// Apply the coercions as explicit casts
Expand All @@ -144,18 +141,18 @@ internal class Env(private val session: Session) {
}
}

@OptIn(FnExperimental::class)
fun resolveAgg(name: String, setQuantifier: Rel.Op.Aggregate.SetQuantifier, args: List<Rex>): Rel.Op.Aggregate.Call.Resolved? {
fun resolveAgg(path: String, setQuantifier: Rel.Op.Aggregate.SetQuantifier, args: List<Rex>): Rel.Op.Aggregate.Call.Resolved? {
// TODO: Eventually, do we want to support sensitive lookup? With a path?
val path = BindingPath(listOf(BindingName(name, BindingCase.INSENSITIVE)))
val item = aggs.lookup(path) ?: return null
val candidates = item.handle.entity.getVariants()
val catalog = session.getCatalog()
val name = path.lowercase()
// Invoke existing function resolution logic
val candidates = fns.lookupAgg(name) ?: return null
val parameters = args.mapIndexed { i, arg -> arg.type }
val match = match(candidates, parameters) ?: return null
val agg = match.first
val mapping = match.second
// Create an internal typed reference
val ref = refAgg(item.catalog, Name.of(item.handle.path.steps), agg)
val ref = refAgg(catalog, Name.of(name), agg)
// Apply the coercions as explicit casts
val coercions: List<Rex> = args.mapIndexed { i, arg ->
when (val cast = mapping[i]) {
Expand Down Expand Up @@ -217,7 +214,6 @@ internal class Env(private val session: Session) {
return userInputPath.steps.size + actualAbsolutePath.size - pathSentToConnector.steps.size
}

@OptIn(FnExperimental::class)
private fun match(candidates: List<AggSignature>, args: List<PType>): Pair<AggSignature, Array<Ref.Cast?>>? {
// 1. Check for an exact match
for (candidate in candidates) {
Expand All @@ -243,7 +239,7 @@ internal class Env(private val session: Session) {
/**
* Check if this function accepts the exact input argument types. Assume same arity.
*/
@OptIn(FnExperimental::class)

private fun AggSignature.matches(args: List<PType>): Boolean {
for (i in args.indices) {
val a = args[i]
Expand All @@ -259,7 +255,6 @@ internal class Env(private val session: Session) {
* @param args
* @return
*/
@OptIn(FnExperimental::class)
private fun AggSignature.match(args: List<PType>): Pair<AggSignature, Array<Ref.Cast?>>? {
val mapping = arrayOfNulls<Ref.Cast?>(args.size)
for (i in args.indices) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,16 @@
package org.partiql.planner.internal

import org.partiql.spi.fn.FnExperimental
import org.partiql.spi.fn.FnParameter
import org.partiql.spi.fn.FnSignature
import org.partiql.types.PType
import org.partiql.types.PType.Kind
import org.partiql.value.PartiQLValueExperimental

/**
* Function precedence comparator; this is not formally specified.
*
* 1. Fewest args first
* 2. Parameters are compared left-to-right
*/
@OptIn(PartiQLValueExperimental::class, FnExperimental::class)
internal object FnComparator : Comparator<FnSignature> {

override fun compare(fn1: FnSignature, fn2: FnSignature): Int {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
package org.partiql.planner.internal

import org.partiql.planner.internal.ir.Ref
import org.partiql.spi.fn.FnExperimental
import org.partiql.spi.fn.FnSignature

/**
* Result of matching an unresolved function.
*/
@OptIn(FnExperimental::class)

internal sealed class FnMatch {

/**
Expand Down
Loading
Loading