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

Adds PartiQLFunction.Aggregation and updates Plugin interface #1310

Merged
merged 2 commits into from
Dec 15, 2023
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
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