Skip to content

Commit

Permalink
Adds PartiQLFunction.Aggregation and updates Plugin interface (#1310)
Browse files Browse the repository at this point in the history
  • Loading branch information
RCHowell authored Dec 15, 2023
1 parent 5c1240c commit 0a75bd3
Show file tree
Hide file tree
Showing 15 changed files with 145 additions and 224 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ Thank you to all who have contributed!
- **Breaking** The default integer literal type is now 32-bit; if the literal can not fit in a 32-bit integer, it overflows to 64-bit.
- **BREAKING** `PartiQLValueType` now distinguishes between Arbitrary Precision Decimal and Fixed Precision Decimal.
- **BREAKING** Function Signature Changes. Now Function signature has two subclasses, `Scalar` and `Aggregation`.
- **BREAKING** Plugin Changes. Only return one Connector.Factory, use Kotlin fields. JVM signature remains the same.
- **BREAKING** In the produced plan:
- The new plan is fully resolved and typed.
- Operators will be converted to function call.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,12 +113,13 @@ class ServiceLoaderUtil {
} else {
listOf()
}
return plugins.flatMap { plugin -> plugin.getFunctions() }
return plugins.flatMap { plugin -> plugin.functions }
.filterIsInstance<PartiQLFunction.Scalar>()
.map { partiqlFunc -> PartiQLtoExprFunction(partiqlFunc) }
}

@OptIn(PartiQLValueExperimental::class, PartiQLFunctionExperimental::class)
private fun PartiQLtoExprFunction(customFunction: PartiQLFunction): ExprFunction {
private fun PartiQLtoExprFunction(customFunction: PartiQLFunction.Scalar): ExprFunction {
val name = customFunction.signature.name
val parameters = customFunction.signature.parameters.map { it.type }
val returnType = customFunction.signature.returns
Expand All @@ -130,8 +131,8 @@ class ServiceLoaderUtil {
)

override fun callWithRequired(session: EvaluationSession, required: List<ExprValue>): ExprValue {
val partiQLArguments = required.mapIndexed { i, expr -> ExprToPartiQLValue(expr, parameters[i]) }
val partiQLResult = customFunction.invoke(session.toConnectorSession(), partiQLArguments)
val partiQLArguments = required.mapIndexed { i, expr -> ExprToPartiQLValue(expr, parameters[i]) }.toTypedArray()
val partiQLResult = customFunction.invoke(partiQLArguments)
return PartiQLtoExprValue(partiQLResult)
}
}
Expand Down
34 changes: 0 additions & 34 deletions partiql-cli/src/test/kotlin/org/partiql/cli/utils/PowTest.kt

This file was deleted.

This file was deleted.

38 changes: 0 additions & 38 deletions partiql-cli/src/test/kotlin/org/partiql/cli/utils/TrimLeadTest.kt

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ internal class PlanTyper(
/**
* Rewrite the statement with inferred types and resolved variables
*/
public fun resolve(statement: Statement): Statement {
fun resolve(statement: Statement): Statement {
if (statement !is Statement.Query) {
throw IllegalArgumentException("PartiQLPlanner only supports Query statements")
}
Expand Down Expand Up @@ -182,15 +182,15 @@ internal class PlanTyper(
}

// compute element type
val t = rex.type as StructType
val t = rex.type
val e = if (t.contentClosed) {
StaticType.unionOf(t.fields.map { it.value }.toSet()).flatten()
} else {
StaticType.ANY
ANY
}

// compute rel type
val kType = StaticType.STRING
val kType = STRING
val vType = e
val type = ctx!!.copyWithSchema(listOf(kType, vType))

Expand Down Expand Up @@ -419,7 +419,7 @@ internal class PlanTyper(

if (resolvedVar == null) {
handleUndefinedVariable(path.steps.last())
return rex(StaticType.ANY, rexOpErr("Undefined variable ${node.identifier}"))
return rex(ANY, rexOpErr("Undefined variable ${node.identifier}"))
}
val type = resolvedVar.type
val op = when (resolvedVar) {
Expand All @@ -439,7 +439,7 @@ internal class PlanTyper(
* Match path as far as possible (rewriting the steps), then infer based on resolved root and rewritten steps.
*/
override fun visitRexOpPath(node: Rex.Op.Path, ctx: StaticType?): Rex {
val visitedSteps = node.steps.map { visitRexOpPathStep(it, null) as Rex.Op.Path.Step }
val visitedSteps = node.steps.map { visitRexOpPathStep(it, null) }
// 1. Resolve path prefix
val (root, steps) = when (val rootOp = node.root.op) {
is Rex.Op.Var.Unresolved -> {
Expand All @@ -448,7 +448,7 @@ internal class PlanTyper(
val resolvedVar = env.resolve(path, locals, rootOp.scope)
if (resolvedVar == null) {
handleUndefinedVariable(path.steps.last())
return rex(StaticType.ANY, node)
return rex(ANY, node)
}
val type = resolvedVar.type
val (op, steps) = when (resolvedVar) {
Expand Down Expand Up @@ -498,7 +498,7 @@ internal class PlanTyper(
}

// 4. Invalid path reference; always MISSING
if (type == StaticType.MISSING) {
if (type == MISSING) {
handleAlwaysMissing()
return rexErr("Unknown identifier $node")
}
Expand Down Expand Up @@ -540,7 +540,7 @@ internal class PlanTyper(
is FnMatch.Dynamic -> {
val types = mutableSetOf<StaticType>()
if (match.isMissable && !isNotMissable) {
types.add(StaticType.MISSING)
types.add(MISSING)
}
val candidates = match.candidates.map { candidate ->
val rex = toRexCall(candidate, args, isNotMissable)
Expand Down Expand Up @@ -574,7 +574,7 @@ internal class PlanTyper(
newArgs.forEach {
if (it.type == MissingType && !isNotMissable) {
handleAlwaysMissing()
return rex(StaticType.MISSING, rexOpCallStatic(newFn, newArgs))
return rex(MISSING, rexOpCallStatic(newFn, newArgs))
}
}

Expand Down Expand Up @@ -615,14 +615,14 @@ internal class PlanTyper(

// Return type with calculated nullability
var type = when {
isNull -> StaticType.NULL
isNull -> NULL
isNullable -> returns.toStaticType()
else -> returns.toNonNullStaticType()
}

// Some operators can return MISSING during runtime
if (match.isMissable && !isNotMissable) {
type = StaticType.unionOf(type, StaticType.MISSING)
type = StaticType.unionOf(type, MISSING)
}

// Finally, rewrite this node
Expand Down Expand Up @@ -740,8 +740,8 @@ internal class PlanTyper(
}
val ref = call.args.getOrNull(0) ?: error("IS STRUCT requires an argument.")
val simplifiedCondition = when {
ref.type.allTypes.all { it is StructType } -> rex(StaticType.BOOL, rexOpLit(boolValue(true)))
ref.type.allTypes.none { it is StructType } -> rex(StaticType.BOOL, rexOpLit(boolValue(false)))
ref.type.allTypes.all { it is StructType } -> rex(BOOL, rexOpLit(boolValue(true)))
ref.type.allTypes.none { it is StructType } -> rex(BOOL, rexOpLit(boolValue(false)))
else -> condition
}

Expand Down Expand Up @@ -789,9 +789,9 @@ internal class PlanTyper(
when (field.k.op) {
is Rex.Op.Lit -> {
// A field is only included in the StructType if its key is a text literal
val key = field.k.op as Rex.Op.Lit
val key = field.k.op
if (key.value is TextValue<*>) {
val name = (key.value as TextValue<*>).string!!
val name = key.value.string!!
val type = field.v.type
structKeysSeent.add(name)
structTypeFields.add(StructType.Field(name, type))
Expand Down Expand Up @@ -919,7 +919,7 @@ internal class PlanTyper(
}

override fun visitRexOpErr(node: Rex.Op.Err, ctx: StaticType?): PlanNode {
val type = ctx ?: StaticType.ANY
val type = ctx ?: ANY
return rex(type, node)
}

Expand Down Expand Up @@ -968,13 +968,13 @@ internal class PlanTyper(
PlanningProblemDetails.CompileError("TupleUnion wasn't normalized to exclude union types.")
)
)
possibleOutputTypes.add(StaticType.MISSING)
possibleOutputTypes.add(MISSING)
}
is NullType -> {
return StaticType.NULL
return NULL
}
else -> {
return StaticType.MISSING
return MISSING
}
}
}
Expand Down Expand Up @@ -1057,7 +1057,7 @@ internal class PlanTyper(
*/
private fun inferPathStep(type: StaticType, step: Rex.Op.Path.Step): Pair<StaticType, Rex.Op.Path.Step> =
when (type) {
is AnyType -> StaticType.ANY to step
is AnyType -> ANY to step
is StructType -> inferPathStep(type, step)
is ListType, is SexpType -> inferPathStep(type as CollectionType, step) to step
is AnyOfType -> {
Expand All @@ -1066,7 +1066,7 @@ internal class PlanTyper(
else -> {
val prevTypes = type.allTypes
if (prevTypes.any { it is AnyType }) {
StaticType.ANY to step
ANY to step
} else {
val results = prevTypes.map { inferPathStep(it, step) }
val types = results.map { it.first }
Expand All @@ -1081,7 +1081,7 @@ internal class PlanTyper(
}
}
}
else -> StaticType.MISSING to step
else -> MISSING to step
}

/**
Expand Down Expand Up @@ -1156,13 +1156,13 @@ internal class PlanTyper(
isClosed && isOrdered -> {
struct.fields.firstOrNull { entry -> binding.isEquivalentTo(entry.key) }?.let {
(sensitive(it.key) to it.value)
} ?: (key to StaticType.MISSING)
} ?: (key to MISSING)
}
// 2. Struct is closed
isClosed -> {
val matches = struct.fields.filter { entry -> binding.isEquivalentTo(entry.key) }
when (matches.size) {
0 -> (key to StaticType.MISSING)
0 -> (key to MISSING)
1 -> matches.first().let { (sensitive(it.key) to it.value) }
else -> {
val firstKey = matches.first().key
Expand All @@ -1175,7 +1175,7 @@ internal class PlanTyper(
}
}
// 3. Struct is open
else -> (key to StaticType.ANY)
else -> (key to ANY)
}
return type to name
}
Expand All @@ -1197,7 +1197,7 @@ internal class PlanTyper(
* Let TX be the single-column table that is the result of applying the <value expression>
* to each row of T and eliminating null values <--- all NULL values are eliminated as inputs
*/
public fun resolveAgg(agg: Agg.Unresolved, arguments: List<Rex>): Pair<Rel.Op.Aggregate.Call, StaticType> {
fun resolveAgg(agg: Agg.Unresolved, arguments: List<Rex>): Pair<Rel.Op.Aggregate.Call, StaticType> {
var missingArg = false
val args = arguments.map {
val arg = visitRex(it, null)
Expand Down Expand Up @@ -1227,7 +1227,7 @@ internal class PlanTyper(

// Some operators can return MISSING during runtime
if (match.isMissable) {
type = StaticType.unionOf(type, StaticType.MISSING).flatten()
type = StaticType.unionOf(type, MISSING).flatten()
}

// Finally, rewrite this node
Expand All @@ -1248,7 +1248,7 @@ internal class PlanTyper(

private fun Rex.type(typeEnv: TypeEnv) = RexTyper(typeEnv).visitRex(this, this.type)

private fun rexErr(message: String) = rex(StaticType.MISSING, rexOpErr(message))
private fun rexErr(message: String) = rex(MISSING, rexOpErr(message))

/**
* I found decorating the tree with the binding names (for resolution) was easier than associating introduced
Expand Down Expand Up @@ -1315,7 +1315,7 @@ internal class PlanTyper(
private fun getElementTypeForFromSource(fromSourceType: StaticType): StaticType = when (fromSourceType) {
is BagType -> fromSourceType.elementType
is ListType -> fromSourceType.elementType
is AnyType -> StaticType.ANY
is AnyType -> ANY
is AnyOfType -> AnyOfType(fromSourceType.types.map { getElementTypeForFromSource(it) }.toSet())
// All the other types coerce into a bag of themselves (including null/missing/sexp).
else -> fromSourceType
Expand Down Expand Up @@ -1437,7 +1437,7 @@ internal class PlanTyper(
private fun Fn.Unresolved.isNotMissable(): Boolean {
return when (identifier) {
is Identifier.Qualified -> false
is Identifier.Symbol -> when ((identifier as Identifier.Symbol).symbol) {
is Identifier.Symbol -> when (identifier.symbol) {
"and" -> true
"or" -> true
"not" -> true
Expand All @@ -1450,7 +1450,7 @@ internal class PlanTyper(
}

private fun Fn.Unresolved.isTypeAssertion(): Boolean {
return (identifier is Identifier.Symbol && (identifier as Identifier.Symbol).symbol.startsWith("is"))
return (identifier is Identifier.Symbol && identifier.symbol.startsWith("is"))
}

/**
Expand Down
Loading

0 comments on commit 0a75bd3

Please sign in to comment.