diff --git a/partiql-eval/api/partiql-eval.api b/partiql-eval/api/partiql-eval.api index d1574b9fd..cf3d853c4 100644 --- a/partiql-eval/api/partiql-eval.api +++ b/partiql-eval/api/partiql-eval.api @@ -66,6 +66,9 @@ public abstract interface class org/partiql/eval/PartiQLStatement$Query : org/pa public abstract interface class org/partiql/eval/value/Datum : java/lang/Iterable { public static fun bagValue (Ljava/lang/Iterable;)Lorg/partiql/eval/value/Datum; public static fun boolValue (Z)Lorg/partiql/eval/value/Datum; + public static fun decimal (Ljava/math/BigDecimal;II)Lorg/partiql/eval/value/Datum; + public static fun decimalArbitrary (Ljava/math/BigDecimal;)Lorg/partiql/eval/value/Datum; + public static fun doublePrecision (D)Lorg/partiql/eval/value/Datum; public fun get (Ljava/lang/String;)Lorg/partiql/eval/value/Datum; public fun getBigDecimal ()Ljava/math/BigDecimal; public fun getBigInteger ()Ljava/math/BigInteger; @@ -87,6 +90,7 @@ public abstract interface class org/partiql/eval/value/Datum : java/lang/Iterabl public abstract fun getType ()Lorg/partiql/types/PType; public static fun int32Value (I)Lorg/partiql/eval/value/Datum; public static fun int64Value (J)Lorg/partiql/eval/value/Datum; + public static fun intArbitrary (Ljava/math/BigInteger;)Lorg/partiql/eval/value/Datum; public fun isMissing ()Z public fun isNull ()Z public fun iterator ()Ljava/util/Iterator; @@ -96,9 +100,12 @@ public abstract interface class org/partiql/eval/value/Datum : java/lang/Iterabl public static fun nullValue ()Lorg/partiql/eval/value/Datum; public static fun nullValue (Lorg/partiql/types/PType;)Lorg/partiql/eval/value/Datum; public static fun of (Lorg/partiql/value/PartiQLValue;)Lorg/partiql/eval/value/Datum; + public static fun real (F)Lorg/partiql/eval/value/Datum; public static fun sexpValue (Ljava/lang/Iterable;)Lorg/partiql/eval/value/Datum; + public static fun smallInt (S)Lorg/partiql/eval/value/Datum; public static fun stringValue (Ljava/lang/String;)Lorg/partiql/eval/value/Datum; public static fun structValue (Ljava/lang/Iterable;)Lorg/partiql/eval/value/Datum; + public static fun tinyInt (B)Lorg/partiql/eval/value/Datum; public fun toPartiQLValue ()Lorg/partiql/value/PartiQLValue; } diff --git a/partiql-eval/src/main/java/org/partiql/eval/value/Datum.java b/partiql-eval/src/main/java/org/partiql/eval/value/Datum.java index 2b068b5f4..40cc98d54 100644 --- a/partiql-eval/src/main/java/org/partiql/eval/value/Datum.java +++ b/partiql-eval/src/main/java/org/partiql/eval/value/Datum.java @@ -345,6 +345,9 @@ default Datum getInsensitive(@NotNull String name) { @Deprecated default PartiQLValue toPartiQLValue() { PType type = this.getType(); + if (this.isMissing()) { + return PartiQL.missingValue(); + } switch (type.getKind()) { case BOOL: return this.isNull() ? PartiQL.boolValue(null) : PartiQL.boolValue(this.getBoolean()); @@ -534,6 +537,16 @@ static Datum bagValue(@NotNull Iterable values) { return new DatumCollection(values, PType.typeBag()); } + @NotNull + static Datum tinyInt(byte value) { + return new DatumByte(value, PType.typeTinyInt()); + } + + @NotNull + static Datum smallInt(short value) { + return new DatumShort(value); + } + @NotNull static Datum int64Value(long value) { return new DatumLong(value); @@ -544,6 +557,33 @@ static Datum int32Value(int value) { return new DatumInt(value); } + @Deprecated + @NotNull + static Datum intArbitrary(@NotNull BigInteger value) { + return new DatumBigInteger(value); + } + + @NotNull + static Datum real(float value) { + return new DatumFloat(value); + } + + @NotNull + static Datum doublePrecision(double value) { + return new DatumDouble(value); + } + + @Deprecated + @NotNull + static Datum decimalArbitrary(@NotNull BigDecimal value) { + return new DatumDecimal(value, PType.typeDecimalArbitrary()); + } + + @NotNull + static Datum decimal(@NotNull BigDecimal value, int precision, int scale) { + return new DatumDecimal(value, PType.typeDecimal(precision, scale)); + } + @NotNull static Datum boolValue(boolean value) { return new DatumBoolean(value); diff --git a/partiql-eval/src/main/kotlin/org/partiql/eval/internal/Compiler.kt b/partiql-eval/src/main/kotlin/org/partiql/eval/internal/Compiler.kt index a2245c1dc..f795753ef 100644 --- a/partiql-eval/src/main/kotlin/org/partiql/eval/internal/Compiler.kt +++ b/partiql-eval/src/main/kotlin/org/partiql/eval/internal/Compiler.kt @@ -25,6 +25,8 @@ import org.partiql.eval.internal.operator.rel.RelSort import org.partiql.eval.internal.operator.rel.RelUnionAll import org.partiql.eval.internal.operator.rel.RelUnionDistinct import org.partiql.eval.internal.operator.rel.RelUnpivot +import org.partiql.eval.internal.operator.rex.ExprArithmeticBinary +import org.partiql.eval.internal.operator.rex.ExprArithmeticUnary import org.partiql.eval.internal.operator.rex.ExprCallDynamic import org.partiql.eval.internal.operator.rex.ExprCallStatic import org.partiql.eval.internal.operator.rex.ExprCase @@ -279,6 +281,98 @@ internal class Compiler( } } + override fun visitRexOpAdd(node: Rex.Op.Add, ctx: PType?): Operator { + val (lhs, rhs) = visitBinaryArithmeticArgs(node.lhs, node.rhs, ctx!!) + val factory = getArithmeticBinaryFactory(ctx) + return factory.add(lhs, rhs) + } + + override fun visitRexOpMultiply(node: Rex.Op.Multiply, ctx: PType?): Operator { + val (lhs, rhs) = visitBinaryArithmeticArgs(node.lhs, node.rhs, ctx!!) + val factory = getArithmeticBinaryFactory(ctx) + return factory.multiply(lhs, rhs) + } + + override fun visitRexOpDivide(node: Rex.Op.Divide, ctx: PType?): Operator { + val (lhs, rhs) = visitBinaryArithmeticArgs(node.lhs, node.rhs, ctx!!) + val factory = getArithmeticBinaryFactory(ctx) + return factory.divide(lhs, rhs) + } + + override fun visitRexOpSubtract(node: Rex.Op.Subtract, ctx: PType?): Operator { + val (lhs, rhs) = visitBinaryArithmeticArgs(node.lhs, node.rhs, ctx!!) + val factory = getArithmeticBinaryFactory(ctx) + return factory.subtract(lhs, rhs) + } + + override fun visitRexOpModulo(node: Rex.Op.Modulo, ctx: PType?): Operator { + val (lhs, rhs) = visitBinaryArithmeticArgs(node.lhs, node.rhs, ctx!!) + val factory = getArithmeticBinaryFactory(ctx) + return factory.modulo(lhs, rhs) + } + + override fun visitRexOpNegative(node: Rex.Op.Negative, ctx: PType?): Operator { + val arg = visitRex(node.arg, ctx) + val factory = getArithmeticUnaryFactory(node.arg.type) + return factory.negative(arg) + } + + override fun visitRexOpPositive(node: Rex.Op.Positive, ctx: PType?): Operator { + val arg = visitRex(node.arg, ctx) + val factory = getArithmeticUnaryFactory(node.arg.type) + return factory.positive(arg) + } + + private fun visitBinaryArithmeticArgs(l: Rex, r: Rex, returnType: PType): Pair { + val lhsVisited = visitRex(l, l.type) + val rhsVisited = visitRex(r, r.type) + return when (returnType.kind) { + PType.Kind.TINYINT, PType.Kind.SMALLINT, PType.Kind.INT, PType.Kind.BIGINT, PType.Kind.INT_ARBITRARY, + PType.Kind.REAL, PType.Kind.DOUBLE_PRECISION, + PType.Kind.DECIMAL_ARBITRARY -> lhsVisited.coerce(l.type, returnType) to rhsVisited.coerce(r.type, returnType) + PType.Kind.DECIMAL -> lhsVisited.coerce(l.type, returnType) to rhsVisited.coerce(r.type, PType.typeDecimalArbitrary()) + PType.Kind.DYNAMIC -> lhsVisited to rhsVisited + else -> error("Unsupported type: $returnType") + } + } + + private fun getArithmeticUnaryFactory(returns: PType): ExprArithmeticUnary.Factory { + return when (returns.kind) { + PType.Kind.TINYINT -> ExprArithmeticUnary.Factory.Byte + PType.Kind.SMALLINT -> ExprArithmeticUnary.Factory.Short + PType.Kind.INT -> ExprArithmeticUnary.Factory.Int + PType.Kind.BIGINT -> ExprArithmeticUnary.Factory.BigInt + PType.Kind.INT_ARBITRARY -> ExprArithmeticUnary.Factory.IntArbitrary + PType.Kind.REAL -> ExprArithmeticUnary.Factory.Float + PType.Kind.DOUBLE_PRECISION -> ExprArithmeticUnary.Factory.Double + PType.Kind.DECIMAL -> ExprArithmeticUnary.Factory.Decimal(returns) + PType.Kind.DECIMAL_ARBITRARY -> ExprArithmeticUnary.Factory.DecimalArbitrary + PType.Kind.DYNAMIC -> ExprArithmeticUnary.Factory.Dynamic + else -> error("Unsupported type: $returns") + } + } + + private fun getArithmeticBinaryFactory(returns: PType): ExprArithmeticBinary.Factory { + return when (returns.kind) { + PType.Kind.TINYINT -> ExprArithmeticBinary.Factory.Byte + PType.Kind.SMALLINT -> ExprArithmeticBinary.Factory.Short + PType.Kind.INT -> ExprArithmeticBinary.Factory.Int + PType.Kind.BIGINT -> ExprArithmeticBinary.Factory.BigInt + PType.Kind.INT_ARBITRARY -> ExprArithmeticBinary.Factory.IntArbitrary + PType.Kind.REAL -> ExprArithmeticBinary.Factory.Float + PType.Kind.DOUBLE_PRECISION -> ExprArithmeticBinary.Factory.Double + PType.Kind.DECIMAL -> ExprArithmeticBinary.Factory.Decimal(returns) + PType.Kind.DECIMAL_ARBITRARY -> ExprArithmeticBinary.Factory.DecimalArbitrary + PType.Kind.DYNAMIC -> ExprArithmeticBinary.Factory.Dynamic + else -> error("Unsupported type: $returns") + } + } + + private fun Operator.Expr.coerce(input: PType, target: PType): Operator.Expr { + if (input == target) return this + return ExprCast(this, Ref.Cast(input, target, isNullable = true)) + } + // REL override fun visitRel(node: Rel, ctx: PType?): Operator.Relation { return super.visitRelOp(node.op, ctx) as Operator.Relation @@ -431,4 +525,11 @@ internal class Compiler( } return item } + + internal companion object { + private fun Operator.Expr.coerce(input: PType, target: PType): Operator.Expr { + if (input == target) return this + return ExprCast(this, Ref.Cast(input, target, isNullable = true)) + } + } } diff --git a/partiql-eval/src/main/kotlin/org/partiql/eval/internal/helpers/ArithmeticTyper.kt b/partiql-eval/src/main/kotlin/org/partiql/eval/internal/helpers/ArithmeticTyper.kt new file mode 100644 index 000000000..2cc26e888 --- /dev/null +++ b/partiql-eval/src/main/kotlin/org/partiql/eval/internal/helpers/ArithmeticTyper.kt @@ -0,0 +1,117 @@ +package org.partiql.eval.internal.helpers + +import org.partiql.types.PType +import org.partiql.types.PType.Kind +import kotlin.math.max +import kotlin.math.min + +/** + * This is a mirror copy to [org.partiql.planner.internal.typer.ArithmeticTyper]. + */ +internal object ArithmeticTyper { + + /** + * Follows SQL-Server for decimals: + * Result Precision = max(s1, s2) + max(p1 - s1, p2 - s2) + 1 + * Result Scale = max(s1, s2) + * @return null if the operation is not allowed on the input types. + */ + internal fun add(lhs: PType, rhs: PType): PType? = arithmeticBinary(lhs, rhs) { lDec, rDec -> + val precision = max(lDec.scale, rDec.scale) + max(lDec.precision - lDec.scale, rDec.precision - rDec.scale) + 1 + val scale = max(lDec.scale, rDec.scale) + precision to scale + } + + /** + * Follows SQL-Server for decimals: + * Result Precision = max(s1, s2) + max(p1 - s1, p2 - s2) + 1 + * Result Scale = max(s1, s2) + * @return null if the operation is not allowed on the input types. + */ + internal fun subtract(lhs: PType, rhs: PType): PType? = arithmeticBinary(lhs, rhs) { lDec, rDec -> + val precision = max(lhs.scale, rhs.scale) + max(lhs.precision - lhs.scale, rhs.precision - rhs.scale) + 1 + val scale = max(lhs.scale, rhs.scale) + precision to scale + } + + /** + * Follows SQL-Server for decimals: + * Result Precision = p1 - s1 + s2 + max(6, s1 + p2 + 1) + * Result Scale = max(6, s1 + p2 + 1) + * @return null if the operation is not allowed on the input types. + */ + internal fun divide(lhs: PType, rhs: PType): PType? = arithmeticBinary(lhs, rhs) { lDec, rDec -> + val precision = lhs.precision - rhs.scale + lhs.scale + max(6, lhs.scale + rhs.precision + 1) + val scale = max(6, lhs.scale + rhs.precision + 1) + precision to scale + } + + /** + * Follows SQL-Server for decimals: + * Result Precision = p1 + p2 + 1 + * Result Scale = s1 + s2 + * @return null if the operation is not allowed on the input types. + */ + internal fun multiply(lhs: PType, rhs: PType): PType? = arithmeticBinary(lhs, rhs) { lDec, rDec -> + val precision = lhs.precision + rhs.precision + 1 + val scale = lhs.scale + rhs.scale + precision to scale + } + + /** + * Follows SQL-Server for decimals: + * Result Precision: min(p1 - s1, p2 - s2) + max(s1, s2) + * Result Scale: max(s1, s2) + * @return null if the operation is not allowed on the input types. + */ + internal fun modulo(lhs: PType, rhs: PType): PType? = arithmeticBinary(lhs, rhs) { lDec, rDec -> + val precision = min(lhs.precision - lhs.scale, rhs.precision - rhs.scale) + max(lhs.scale, rhs.scale) + val scale = max(lhs.scale, rhs.scale) + precision to scale + } + + internal fun negative(arg: PType): PType? = arithmeticUnary(arg) + + internal fun positive(arg: PType): PType? = arithmeticUnary(arg) + + private fun arithmeticUnary(arg: PType): PType? { + val argMayBeNumber = arg.kind == Kind.DYNAMIC || TypeFamily.NUMBERS.contains(arg.kind) + return when (argMayBeNumber) { + true -> arg + false -> null + } + } + + private fun arithmeticBinary( + lhs: PType, + rhs: PType, + handleDecimal: (PType, PType) -> Pair + ): PType? { + val lhsCannotBeNumber = lhs.kind != Kind.DYNAMIC && !TypeFamily.NUMBERS.contains(lhs.kind) + val rhsCannotBeNumber = rhs.kind != Kind.DYNAMIC && !TypeFamily.NUMBERS.contains(rhs.kind) + if (lhsCannotBeNumber || rhsCannotBeNumber) { + return null + } + if (lhs.kind == Kind.DYNAMIC || rhs.kind == Kind.DYNAMIC) { + return PType.typeDynamic() + } + val lhsPrecedence = TypePrecedence[lhs.kind]!! + val rhsPrecedence = TypePrecedence[rhs.kind]!! + val comp = lhsPrecedence.compareTo(rhsPrecedence) + when (comp) { + -1 -> return rhs + 1 -> return lhs + 0 -> if (lhs.kind != Kind.DECIMAL) { + return lhs + } + + else -> error("This shouldn't have occurred.") + } + val (precision, scale) = handleDecimal(lhs, rhs) + // TODO: Check if this is what we want + return when (precision > 38 || scale > 38) { + true -> PType.typeDecimalArbitrary() + false -> PType.typeDecimal(precision, scale) + } + } +} diff --git a/partiql-eval/src/main/kotlin/org/partiql/eval/internal/helpers/Coercions.kt b/partiql-eval/src/main/kotlin/org/partiql/eval/internal/helpers/Coercions.kt new file mode 100644 index 000000000..ab450968d --- /dev/null +++ b/partiql-eval/src/main/kotlin/org/partiql/eval/internal/helpers/Coercions.kt @@ -0,0 +1,217 @@ +package org.partiql.eval.internal.helpers + +import org.partiql.plan.Ref +import org.partiql.types.Field +import org.partiql.types.PType +import org.partiql.types.PType.Kind + +/** + * DEVELOPMENT NOTE: THIS IS ESSENTIALLY A COPY OF [org.partiql.planner.internal.casts.Coercions]. Please keep them updated. + * + * Important SQL Definitions: + * - assignable: The characteristic of a data type that permits a value of that data type to be + * assigned to a site of a specified data type. + */ +internal object Coercions { + + fun get(input: PType, target: PType): Ref.Cast? { + return getCoercion(input, target) + } + + /** + * Remaining coercions from SQL:1999: + * - Values corresponding to the binary data type are mutually assignable. + * - Values corresponding to the data types BIT and BIT VARYING are always mutually comparable and + * are mutually assignable. + * - Values of type interval are mutually assignable only if the source and target of the assignment are + * both year-month intervals or if they are both day-time intervals. + * - Values corresponding to user-defined types are discussed in Subclause 4.8.4, ‘‘User-defined type + * comparison and assignment’’. + */ + private fun getCoercion(input: PType, target: PType): Ref.Cast? { + return when { + isAssignable(input, target) -> coercion(input, target) + else -> null + } + } + + private val TYPES_NUMBER = setOf( + Kind.TINYINT, + Kind.SMALLINT, + Kind.INT, + Kind.BIGINT, + Kind.INT_ARBITRARY, + Kind.REAL, + Kind.DOUBLE_PRECISION, + Kind.DECIMAL, + Kind.DECIMAL_ARBITRARY + ) + + private val TYPES_TEXT = setOf( + Kind.CHAR, + Kind.VARCHAR, + Kind.STRING, + Kind.CLOB, + Kind.SYMBOL + ) + + private val TYPES_COLLECTION = setOf( + Kind.LIST, + Kind.SEXP, + Kind.BAG + ) + + private fun isAssignable(input: PType, target: PType): Boolean { + return areAssignableNumberTypes(input, target) || + areAssignableTextTypes(input, target) || + areAssignableBooleanTypes(input, target) || + areAssignableDateTimeTypes(input, target) || + areAssignableCollectionTypes(input, target) || + areAssignableStructuralTypes(input, target) || + areAssignableDynamicTypes(target) + } + + /** + * NOT specified by SQL:1999. We assume that we can coerce a collection of one type to another if the subtype + * of each collection is assignable. + */ + private fun areAssignableCollectionTypes(input: PType, target: PType): Boolean { + return input.kind in TYPES_COLLECTION && target.kind in TYPES_COLLECTION && isAssignable(input.typeParameter, target.typeParameter) + } + + /** + * NOT specified by SQL:1999. We assume that we can statically coerce anything to DYNAMIC. However, note that + * CAST( AS DYNAMIC) is NEVER inserted. We check for the use of DYNAMIC at function resolution. This is merely + * for the [PType.getTypeParameter] and [PType.getFields] + */ + private fun areAssignableDynamicTypes(target: PType): Boolean { + return target.kind == Kind.DYNAMIC + } + + /** + * NOT completely specified by SQL:1999. + * + * From SQL:1999: + * ``` + * Values corresponding to row types are mutually assignable if and only if both have the same degree + * and every field in one row type is mutually assignable to the field in the same ordinal position of + * the other row type. Values corresponding to row types are mutually comparable if and only if both + * have the same degree and every field in one row type is mutually comparable to the field in the + * same ordinal position of the other row type. + * ``` + */ + private fun areAssignableStructuralTypes(input: PType, target: PType): Boolean { + return when { + input.kind == Kind.ROW && target.kind == Kind.ROW -> fieldsAreAssignable(input.fields!!.toList(), target.fields!!.toList()) + input.kind == Kind.STRUCT && target.kind == Kind.ROW -> when (input.fields) { + null -> true + else -> namedFieldsAreAssignableUnordered(input.fields!!.toList(), target.fields!!.toList()) + } + input.kind == Kind.ROW && target.kind == Kind.STRUCT -> when (target.fields) { + null -> true + else -> namedFieldsAreAssignableUnordered(input.fields!!.toList(), target.fields!!.toList()) + } + input.kind == Kind.STRUCT && target.kind == Kind.STRUCT -> when { + input.fields == null || target.fields == null -> true + else -> fieldsAreAssignable(input.fields!!.toList(), target.fields!!.toList()) + } + else -> false + } + } + + private fun fieldsAreAssignable(input: List, target: List): Boolean { + if (input.size != target.size) { return false } + val iIter = input.iterator() + val tIter = target.iterator() + while (iIter.hasNext()) { + val iField = iIter.next() + val tField = tIter.next() + if (!isAssignable(iField.type, tField.type)) { + return false + } + } + return true + } + + /** + * This is a PartiQL extension. We assume that structs/rows with the same field names may be assignable + * if all names match AND types are assignable. + */ + private fun namedFieldsAreAssignableUnordered(input: List, target: List): Boolean { + if (input.size != target.size) { return false } + val inputSorted = input.sortedBy { it.name } + val targetSorted = target.sortedBy { it.name } + val iIter = inputSorted.iterator() + val tIter = targetSorted.iterator() + while (iIter.hasNext()) { + val iField = iIter.next() + val tField = tIter.next() + if (iField.name != tField.name) { + return false + } + if (!isAssignable(iField.type, tField.type)) { + return false + } + } + return true + } + + /** + * From SQL:1999: + * ``` + * Values of the data types NUMERIC, DECIMAL, INTEGER, SMALLINT, FLOAT, REAL, and + * DOUBLE PRECISION are numbers and are all mutually comparable and mutually assignable. + * ``` + */ + private fun areAssignableNumberTypes(input: PType, target: PType): Boolean { + return input.kind in TYPES_NUMBER && target.kind in TYPES_NUMBER + } + + /** + * From SQL:1999: + * ``` + * Values corresponding to the data type boolean are always mutually comparable and are mutually + * assignable. + * ``` + */ + private fun areAssignableBooleanTypes(input: PType, target: PType): Boolean { + return input.kind == Kind.BOOL && target.kind == Kind.BOOL + } + + /** + * From SQL:1999: + * ``` + * Values corresponding to the data types CHARACTER, CHARACTER VARYING, and CHARACTER + * LARGE OBJECT are mutually assignable if and only if they are taken from the same character + * repertoire. (For this implementation, we shall assume that all text types share the same + * character repertoire.) + * ``` + */ + private fun areAssignableTextTypes(input: PType, target: PType): Boolean { + return input.kind in TYPES_TEXT && target.kind in TYPES_TEXT + } + + /** + * From SQL:1999: + * ``` + * Values of type datetime are mutually assignable only if the source and target of the assignment are + * both of type DATE, or both of type TIME (regardless whether WITH TIME ZONE or WITHOUT + * TIME ZONE is specified or implicit), or both of type TIMESTAMP (regardless whether WITH TIME + * ZONE or WITHOUT TIME ZONE is specified or implicit) + * ``` + */ + private fun areAssignableDateTimeTypes(input: PType, target: PType): Boolean { + val i = input.kind + val t = target.kind + return when { + i == Kind.DATE && t == Kind.DATE -> true + (i == Kind.TIME_WITH_TZ || i == Kind.TIME_WITHOUT_TZ) && (t == Kind.TIME_WITH_TZ || t == Kind.TIME_WITHOUT_TZ) -> true + (i == Kind.TIMESTAMP_WITH_TZ || i == Kind.TIMESTAMP_WITHOUT_TZ) && (t == Kind.TIMESTAMP_WITH_TZ || t == Kind.TIMESTAMP_WITHOUT_TZ) -> true + else -> false + } + } + + private fun coercion(input: PType, target: PType): Ref.Cast { + return Ref.Cast(input, target, isNullable = true) + } +} diff --git a/partiql-eval/src/main/kotlin/org/partiql/eval/internal/helpers/FnComparator.kt b/partiql-eval/src/main/kotlin/org/partiql/eval/internal/helpers/FnComparator.kt new file mode 100644 index 000000000..ace4636c1 --- /dev/null +++ b/partiql-eval/src/main/kotlin/org/partiql/eval/internal/helpers/FnComparator.kt @@ -0,0 +1,81 @@ +package org.partiql.eval.internal.helpers + +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 + +/** + * DEVELOPMENT NOTE: THIS IS A COPY OF [org.partiql.planner.internal.FnComparator]. Keep them updated. + * + * Function precedence comparator; this is not formally specified. + * + * 1. Fewest args first + * 2. Parameters are compared left-to-right + */ +@OptIn(FnExperimental::class) +internal object FnComparator : Comparator { + + override fun compare(fn1: FnSignature, fn2: FnSignature): Int { + // Compare number of arguments + if (fn1.parameters.size != fn2.parameters.size) { + return fn1.parameters.size - fn2.parameters.size + } + // Compare operand type precedence + for (i in fn1.parameters.indices) { + val p1 = fn1.parameters[i] + val p2 = fn2.parameters[i] + val comparison = p1.compareTo(p2) + if (comparison != 0) return comparison + } + // unreachable? + return 0 + } + + private fun FnParameter.compareTo(other: FnParameter): Int = + comparePrecedence(this.type, other.type) + + private fun comparePrecedence(t1: PType, t2: PType): Int { + if (t1 == t2) return 0 + val p1 = precedence[t1.kind]!! + val p2 = precedence[t2.kind]!! + return p1 - p2 + } + + /** + * This simply describes some precedence for ordering functions. + * This is not explicitly defined in the PartiQL Specification!! + * This does not imply the ability to CAST; this defines function resolution behavior. + * This excludes [Kind.ROW] and [Kind.UNKNOWN]. + */ + private val precedence: Map = listOf( + Kind.BOOL, + Kind.TINYINT, + Kind.SMALLINT, + Kind.INT, + Kind.BIGINT, + Kind.INT_ARBITRARY, + Kind.DECIMAL, + Kind.REAL, + Kind.DOUBLE_PRECISION, + Kind.DECIMAL_ARBITRARY, // Arbitrary precision decimal has a higher precedence than FLOAT + Kind.CHAR, + Kind.VARCHAR, + Kind.SYMBOL, + Kind.STRING, + Kind.CLOB, + Kind.BLOB, + Kind.DATE, + Kind.TIME_WITHOUT_TZ, + Kind.TIME_WITH_TZ, + Kind.TIMESTAMP_WITHOUT_TZ, + Kind.TIMESTAMP_WITH_TZ, + Kind.LIST, + Kind.SEXP, + Kind.BAG, + Kind.ROW, + Kind.STRUCT, + Kind.DYNAMIC, + ).mapIndexed { precedence, type -> type to precedence }.toMap() +} diff --git a/partiql-eval/src/main/kotlin/org/partiql/eval/internal/helpers/FunctionResolver.kt b/partiql-eval/src/main/kotlin/org/partiql/eval/internal/helpers/FunctionResolver.kt new file mode 100644 index 000000000..6b92608c7 --- /dev/null +++ b/partiql-eval/src/main/kotlin/org/partiql/eval/internal/helpers/FunctionResolver.kt @@ -0,0 +1,216 @@ +package org.partiql.eval.internal.helpers + +import org.partiql.plan.Ref +import org.partiql.spi.fn.FnExperimental +import org.partiql.spi.fn.FnSignature +import org.partiql.types.PType +import org.partiql.types.PType.Kind + +/** + * DEVELOPMENT NOTE: This is essentially a copy of [org.partiql.planner.internal.FnResolver]. Keep them updated. + * + * Resolution of static calls. + * + * 1. Sort all functions by resolution precedence. + * 2. Check for a function accepting exactly the input argument types. If one exists, use it. + * 3. Look for the best match + * a. Discard candidates whose arguments do not match or cannot be coerced. + * b. Check all candidates and keep those with the most exact matches. + * + * Resolution of dynamic calls. + * + * + * Reference https://www.postgresql.org/docs/current/typeconv-func.html + */ +@OptIn(FnExperimental::class) +internal object FunctionResolver { + + /** + * Resolution of either a static or dynamic function. + * + * TODO: How do we handle DYNAMIC? + * + * @param variants + * @param args + * @return + */ + fun resolve(variants: List, args: List): FnMatch? { + val candidates = variants + .mapIndexed { index, fnSignature -> Candidate(index, fnSignature) } + .filter { it.signature.parameters.size == args.size } + .ifEmpty { return null } + + // 1. Look for exact match + for (candidate in candidates) { + if (candidate.signature.matchesExactly(args)) { + return FnMatch.Static(candidate.index, arrayOfNulls(args.size)) + } + } + + // 2. Discard functions that cannot be matched (via implicit coercion or exact matches) + val invocableMatches = match(candidates, args).ifEmpty { return null } + if (invocableMatches.size == 1) { + return invocableMatches.first().match + } + + // 3. If there are DYNAMIC nodes, return all candidates + for (arg in args) { + if (arg.kind == Kind.DYNAMIC) { + return FnMatch.Dynamic(invocableMatches.map { it.match }) + } + } + + // 4. Run through all candidates and keep those with the most exact matches on input types. + val matches = matchOn(invocableMatches) { it.numberOfExactInputTypes } + if (matches.size == 1) { + return matches.first().match + } + + // TODO: Do we care about preferred types? This is a PostgreSQL concept. + // 5. Run through all candidates and keep those that accept preferred types (of the input data type's type category) at the most positions where type conversion will be required. + + // 6. Find the highest precedence one. NOTE: This is a remnant of the previous implementation. Whether we want + // to keep this is up to us. + return matches.sortedWith(MatchResultComparator).first().match + } + + private class Candidate( + val index: Int, + val signature: FnSignature + ) + + /** + * Resolution of a static function. + * + * @param candidates + * @param args + * @return + */ + private fun match(candidates: List, args: List): List { + val matches = mutableSetOf() + for (candidate in candidates) { + val m = candidate.match(args) ?: continue + matches.add(m) + } + return matches.toList() + } + + private fun matchOn(candidates: List, toCompare: (MatchResult) -> Int): List { + var mostExactMatches = 0 + val matches = mutableSetOf() + for (candidate in candidates) { + when (toCompare(candidate).compareTo(mostExactMatches)) { + -1 -> continue + 0 -> matches.add(candidate) + 1 -> { + mostExactMatches = toCompare(candidate) + matches.clear() + matches.add(candidate) + } + else -> error("CompareTo should never return outside of range [-1, 1]") + } + } + return matches.toList() + } + + /** + * Check if this function accepts the exact input argument types. Assume same arity. + */ + private fun FnSignature.matchesExactly(args: List): Boolean { + for (i in args.indices) { + val a = args[i] + val p = parameters[i] + if (a != p.type) return false + } + return true + } + + /** + * Attempt to match arguments to the parameters; return the implicit casts if necessary. + * + * @param args + * @return + */ + private fun Candidate.match(args: List): MatchResult? { + val mapping = arrayOfNulls(args.size) + var exactInputTypes: Int = 0 + for (i in args.indices) { + val arg = args[i] + val p = this.signature.parameters[i] + when { + // 1. Exact match + arg == p.type -> { + exactInputTypes++ + continue + } + // 2. Match ANY parameter, no coercion needed + p.type.kind == Kind.DYNAMIC -> continue + arg.kind == Kind.UNKNOWN -> continue + // 3. Allow for ANY arguments + arg.kind == Kind.DYNAMIC -> { + mapping[i] = Ref.Cast(arg, p.type, true) + } + // 4. Check for a coercion + else -> when (val coercion = Coercions.get(arg, p.type)) { + null -> return null // short-circuit + else -> mapping[i] = coercion.toPublic() + } + } + } + return MatchResult( + FnMatch.Static(this.index, mapping), + this.signature, + exactInputTypes, + ) + } + + private class MatchResult( + val match: FnMatch.Static, + val signature: FnSignature, + val numberOfExactInputTypes: Int, + ) + + private object MatchResultComparator : Comparator { + override fun compare(o1: MatchResult, o2: MatchResult): Int { + return FnComparator.reversed().compare(o1.signature, o2.signature) + } + } + + private fun Ref.Cast.toPublic() = Ref.Cast( + input = input, + target = target, + isNullable = isNullable + ) + + /** + * Result of matching an unresolved function. + */ + public sealed class FnMatch { + + /** + * Successful match of a static function call. + * + * @property signature + * @property mapping + */ + public data class Static( + val index: Int, + val mapping: Array, + ) : FnMatch() { + + /** + * The number of exact matches. Useful when ranking function matches. + */ + val exact: Int = mapping.count { it != null } + } + + /** + * This represents dynamic dispatch. + * + * @property candidates Ordered list of potentially applicable functions to dispatch dynamically. + */ + public data class Dynamic( + val candidates: List, + ) : FnMatch() + } +} diff --git a/partiql-eval/src/main/kotlin/org/partiql/eval/internal/helpers/TypeFamily.kt b/partiql-eval/src/main/kotlin/org/partiql/eval/internal/helpers/TypeFamily.kt new file mode 100644 index 000000000..993edeffc --- /dev/null +++ b/partiql-eval/src/main/kotlin/org/partiql/eval/internal/helpers/TypeFamily.kt @@ -0,0 +1,20 @@ +package org.partiql.eval.internal.helpers + +import org.partiql.types.PType + +/** + * This is a utility class to help with planning. + */ +internal object TypeFamily { + val NUMBERS = setOf( + PType.Kind.TINYINT, + PType.Kind.SMALLINT, + PType.Kind.INT, + PType.Kind.BIGINT, + PType.Kind.INT_ARBITRARY, + PType.Kind.DECIMAL, + PType.Kind.DECIMAL_ARBITRARY, + PType.Kind.REAL, + PType.Kind.DOUBLE_PRECISION, + ) +} diff --git a/partiql-eval/src/main/kotlin/org/partiql/eval/internal/helpers/TypePrecedence.kt b/partiql-eval/src/main/kotlin/org/partiql/eval/internal/helpers/TypePrecedence.kt new file mode 100644 index 000000000..73b9f5fcf --- /dev/null +++ b/partiql-eval/src/main/kotlin/org/partiql/eval/internal/helpers/TypePrecedence.kt @@ -0,0 +1,55 @@ +package org.partiql.eval.internal.helpers + +import org.partiql.types.PType.Kind + +/** + * Map of the type precedences. + */ +internal object TypePrecedence : Map { + + private val _delegate: Map = listOf( + Kind.BOOL, + Kind.TINYINT, + Kind.SMALLINT, + Kind.INT, + Kind.BIGINT, + Kind.INT_ARBITRARY, + Kind.DECIMAL, + Kind.REAL, + Kind.DOUBLE_PRECISION, + Kind.DECIMAL_ARBITRARY, // Arbitrary precision decimal has a higher precedence than FLOAT + Kind.CHAR, + Kind.VARCHAR, + Kind.SYMBOL, + Kind.STRING, + Kind.CLOB, + Kind.BLOB, + Kind.DATE, + Kind.TIME_WITHOUT_TZ, + Kind.TIME_WITH_TZ, + Kind.TIMESTAMP_WITHOUT_TZ, + Kind.TIMESTAMP_WITH_TZ, + Kind.LIST, + Kind.SEXP, + Kind.BAG, + Kind.ROW, + Kind.STRUCT, + Kind.DYNAMIC, + ).mapIndexed { precedence, type -> type to precedence }.toMap() + + override val entries: Set> = _delegate.entries + + override val keys: Set = _delegate.keys + + override val size: Int = _delegate.size + + override val values: Collection = _delegate.values + + override fun isEmpty(): Boolean = _delegate.isEmpty() + + override fun get(key: Kind): Int? = _delegate.get(key) + + override fun containsValue(value: Int): Boolean = _delegate.containsValue(value) + + override fun containsKey(key: Kind): Boolean = _delegate.containsKey(key) +} diff --git a/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprArithmeticBinary.kt b/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprArithmeticBinary.kt new file mode 100644 index 000000000..69f0e8a2c --- /dev/null +++ b/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprArithmeticBinary.kt @@ -0,0 +1,253 @@ +package org.partiql.eval.internal.operator.rex + +import org.partiql.errors.TypeCheckException +import org.partiql.eval.internal.Environment +import org.partiql.eval.internal.helpers.ArithmeticTyper +import org.partiql.eval.internal.operator.Operator +import org.partiql.eval.value.Datum +import org.partiql.plan.Ref +import org.partiql.types.PType +import org.partiql.types.PType.Kind +import java.math.BigDecimal +import java.math.BigInteger + +internal class ExprArithmeticBinary( + val lhs: Operator.Expr, + val rhs: Operator.Expr, + val extract: (Datum) -> T, + val toReturn: (T) -> Datum, + val op: (T, T) -> T, + val returnType: PType +) : Operator.Expr { + + override fun eval(env: Environment): Datum { + val lhsEval = lhs.eval(env) + val rhsEval = rhs.eval(env) + if (lhsEval.isNull || rhsEval.isNull) return Datum.nullValue(returnType) + if (lhsEval.isMissing || rhsEval.isMissing) throw TypeCheckException() + return toReturn(op(extract(lhsEval), extract(rhsEval))) + } + + internal interface Factory { + + fun add(lhs: Operator.Expr, rhs: Operator.Expr): Operator.Expr + + fun subtract(lhs: Operator.Expr, rhs: Operator.Expr): Operator.Expr + + fun modulo(lhs: Operator.Expr, rhs: Operator.Expr): Operator.Expr + + fun divide(lhs: Operator.Expr, rhs: Operator.Expr): Operator.Expr + + fun multiply(lhs: Operator.Expr, rhs: Operator.Expr): Operator.Expr + + abstract class Simple : Factory { + + abstract val extract: (Datum) -> T + abstract val toReturn: (T) -> Datum + abstract val add: (T, T) -> T + abstract val subtract: (T, T) -> T + abstract val divide: (T, T) -> T + abstract val multiply: (T, T) -> T + abstract val modulo: (T, T) -> T + abstract val returnType: PType + + override fun add(lhs: Operator.Expr, rhs: Operator.Expr): Operator.Expr { + return ExprArithmeticBinary(lhs, rhs, extract, toReturn, add, returnType) + } + + override fun subtract(lhs: Operator.Expr, rhs: Operator.Expr): Operator.Expr { + return ExprArithmeticBinary(lhs, rhs, extract, toReturn, subtract, returnType) + } + + override fun divide(lhs: Operator.Expr, rhs: Operator.Expr): Operator.Expr { + return ExprArithmeticBinary(lhs, rhs, extract, toReturn, divide, returnType) + } + + override fun multiply(lhs: Operator.Expr, rhs: Operator.Expr): Operator.Expr { + return ExprArithmeticBinary(lhs, rhs, extract, toReturn, multiply, returnType) + } + + override fun modulo(lhs: Operator.Expr, rhs: Operator.Expr): Operator.Expr { + return ExprArithmeticBinary(lhs, rhs, extract, toReturn, modulo, returnType) + } + } + + object Int : Simple() { + override val extract: (Datum) -> kotlin.Int = Datum::getInt + override val toReturn: (kotlin.Int) -> Datum = Datum::int32Value + override val add: (kotlin.Int, kotlin.Int) -> kotlin.Int = kotlin.Int::plus + override val subtract: (kotlin.Int, kotlin.Int) -> kotlin.Int = kotlin.Int::minus + override val divide: (kotlin.Int, kotlin.Int) -> kotlin.Int = kotlin.Int::div + override val multiply: (kotlin.Int, kotlin.Int) -> kotlin.Int = kotlin.Int::times + override val modulo: (kotlin.Int, kotlin.Int) -> kotlin.Int = kotlin.Int::rem + override val returnType: PType = PType.typeInt() + } + + object BigInt : Simple() { + override val extract: (Datum) -> kotlin.Long = Datum::getLong + override val toReturn: (kotlin.Long) -> Datum = Datum::int64Value + override val add: (kotlin.Long, kotlin.Long) -> kotlin.Long = kotlin.Long::plus + override val subtract: (kotlin.Long, kotlin.Long) -> kotlin.Long = kotlin.Long::minus + override val divide: (kotlin.Long, kotlin.Long) -> kotlin.Long = kotlin.Long::div + override val multiply: (kotlin.Long, kotlin.Long) -> kotlin.Long = kotlin.Long::times + override val modulo: (kotlin.Long, kotlin.Long) -> kotlin.Long = kotlin.Long::rem + override val returnType: PType = PType.typeBigInt() + } + + object Short : Simple() { + override val extract: (Datum) -> kotlin.Short = Datum::getShort + override val toReturn: (kotlin.Short) -> Datum = Datum::smallInt + override val add: (kotlin.Short, kotlin.Short) -> kotlin.Short = { x, y -> (x + y).toShort() } + override val subtract: (kotlin.Short, kotlin.Short) -> kotlin.Short = { x, y -> (x - y).toShort() } + override val divide: (kotlin.Short, kotlin.Short) -> kotlin.Short = { x, y -> (x / y).toShort() } + override val multiply: (kotlin.Short, kotlin.Short) -> kotlin.Short = { x, y -> (x * y).toShort() } + override val modulo: (kotlin.Short, kotlin.Short) -> kotlin.Short = { x, y -> (x % y).toShort() } + override val returnType: PType = PType.typeSmallInt() + } + + object Float : Simple() { + override val extract: (Datum) -> kotlin.Float = Datum::getFloat + override val toReturn: (kotlin.Float) -> Datum = Datum::real + override val add: (kotlin.Float, kotlin.Float) -> kotlin.Float = kotlin.Float::plus + override val subtract: (kotlin.Float, kotlin.Float) -> kotlin.Float = kotlin.Float::minus + override val divide: (kotlin.Float, kotlin.Float) -> kotlin.Float = kotlin.Float::div + override val multiply: (kotlin.Float, kotlin.Float) -> kotlin.Float = kotlin.Float::times + override val modulo: (kotlin.Float, kotlin.Float) -> kotlin.Float = kotlin.Float::rem + override val returnType: PType = PType.typeReal() + } + + object Double : Simple() { + override val extract: (Datum) -> kotlin.Double = Datum::getDouble + override val toReturn: (kotlin.Double) -> Datum = Datum::doublePrecision + override val add: (kotlin.Double, kotlin.Double) -> kotlin.Double = kotlin.Double::plus + override val subtract: (kotlin.Double, kotlin.Double) -> kotlin.Double = kotlin.Double::minus + override val divide: (kotlin.Double, kotlin.Double) -> kotlin.Double = kotlin.Double::div + override val multiply: (kotlin.Double, kotlin.Double) -> kotlin.Double = kotlin.Double::times + override val modulo: (kotlin.Double, kotlin.Double) -> kotlin.Double = kotlin.Double::rem + override val returnType: PType = PType.typeDoublePrecision() + } + + object IntArbitrary : Simple() { + override val extract: (Datum) -> BigInteger = Datum::getBigInteger + override val toReturn: (BigInteger) -> Datum = Datum::intArbitrary + override val add: (BigInteger, BigInteger) -> BigInteger = BigInteger::plus + override val subtract: (BigInteger, BigInteger) -> BigInteger = BigInteger::minus + override val divide: (BigInteger, BigInteger) -> BigInteger = BigInteger::divide + override val multiply: (BigInteger, BigInteger) -> BigInteger = BigInteger::times + override val modulo: (BigInteger, BigInteger) -> BigInteger = BigInteger::rem + override val returnType: PType = PType.typeIntArbitrary() + } + + object DecimalArbitrary : Simple() { + override val extract: (Datum) -> BigDecimal = Datum::getBigDecimal + override val toReturn: (BigDecimal) -> Datum = Datum::decimalArbitrary + override val add: (BigDecimal, BigDecimal) -> BigDecimal = BigDecimal::plus + override val subtract: (BigDecimal, BigDecimal) -> BigDecimal = BigDecimal::minus + override val divide: (BigDecimal, BigDecimal) -> BigDecimal = BigDecimal::divide + override val multiply: (BigDecimal, BigDecimal) -> BigDecimal = BigDecimal::times + override val modulo: (BigDecimal, BigDecimal) -> BigDecimal = BigDecimal::remainder + override val returnType: PType = PType.typeDecimalArbitrary() + } + + object Byte : Simple() { + override val extract: (Datum) -> kotlin.Byte = Datum::getByte + override val toReturn: (kotlin.Byte) -> Datum = Datum::tinyInt + override val add: (kotlin.Byte, kotlin.Byte) -> kotlin.Byte = { x, y -> (x + y).toByte() } + override val subtract: (kotlin.Byte, kotlin.Byte) -> kotlin.Byte = { x, y -> (x - y).toByte() } + override val divide: (kotlin.Byte, kotlin.Byte) -> kotlin.Byte = { x, y -> (x / y).toByte() } + override val multiply: (kotlin.Byte, kotlin.Byte) -> kotlin.Byte = { x, y -> (x * y).toByte() } + override val modulo: (kotlin.Byte, kotlin.Byte) -> kotlin.Byte = { x, y -> (x % y).toByte() } + override val returnType: PType = PType.typeTinyInt() + } + + class Decimal(private val returnType: PType) : Factory { + override fun add(lhs: Operator.Expr, rhs: Operator.Expr): Operator.Expr { + return Decimal(lhs, rhs, returnType, BigDecimal::plus) + } + + override fun subtract(lhs: Operator.Expr, rhs: Operator.Expr): Operator.Expr { + return Decimal(lhs, rhs, returnType, BigDecimal::minus) + } + + override fun multiply(lhs: Operator.Expr, rhs: Operator.Expr): Operator.Expr { + return Decimal(lhs, rhs, returnType, BigDecimal::times) + } + + override fun divide(lhs: Operator.Expr, rhs: Operator.Expr): Operator.Expr { + return Decimal(lhs, rhs, returnType, BigDecimal::divide) + } + override fun modulo(lhs: Operator.Expr, rhs: Operator.Expr): Operator.Expr { + return Decimal(lhs, rhs, returnType, BigDecimal::remainder) + } + } + + object Dynamic : Factory { + override fun add(lhs: Operator.Expr, rhs: Operator.Expr): Operator.Expr { + return Dynamic(lhs, rhs, Factory::add, ArithmeticTyper::add) + } + + override fun subtract(lhs: Operator.Expr, rhs: Operator.Expr): Operator.Expr { + return Dynamic(lhs, rhs, Factory::subtract, ArithmeticTyper::subtract) + } + + override fun divide(lhs: Operator.Expr, rhs: Operator.Expr): Operator.Expr { + return Dynamic(lhs, rhs, Factory::divide, ArithmeticTyper::divide) + } + + override fun multiply(lhs: Operator.Expr, rhs: Operator.Expr): Operator.Expr { + return Dynamic(lhs, rhs, Factory::multiply, ArithmeticTyper::multiply) + } + + override fun modulo(lhs: Operator.Expr, rhs: Operator.Expr): Operator.Expr { + return Dynamic(lhs, rhs, Factory::modulo, ArithmeticTyper::modulo) + } + } + } + + internal class Decimal( + private val lhs: Operator.Expr, + private val rhs: Operator.Expr, + private val returnType: PType, + private val op: (BigDecimal, BigDecimal) -> BigDecimal + ) : Operator.Expr { + + override fun eval(env: Environment): Datum { + val lhsEval = lhs.eval(env) + val rhsEval = rhs.eval(env) + if (lhsEval.isNull || rhsEval.isNull) return Datum.nullValue(returnType) + if (lhsEval.isMissing || rhsEval.isMissing) throw TypeCheckException() + return Datum.decimal(op(lhsEval.bigDecimal, rhsEval.bigDecimal), returnType.precision, returnType.scale) + } + } + + internal class Dynamic( + val lhs: Operator.Expr, + val rhs: Operator.Expr, + val op: (Factory, Operator.Expr, Operator.Expr) -> Operator.Expr, + val type: (PType, PType) -> PType? + ) : Operator.Expr { + override fun eval(env: Environment): Datum { + val lhsEval = lhs.eval(env) + val rhsEval = rhs.eval(env) + val returns = type(lhsEval.type, rhsEval.type) ?: throw TypeCheckException() + val factory = when (returns.kind) { + PType.Kind.TINYINT -> Factory.Byte + PType.Kind.SMALLINT -> Factory.Short + PType.Kind.INT -> Factory.Int + PType.Kind.BIGINT -> Factory.BigInt + PType.Kind.INT_ARBITRARY -> Factory.IntArbitrary + PType.Kind.REAL -> Factory.Float + PType.Kind.DOUBLE_PRECISION -> Factory.Double + PType.Kind.DECIMAL -> Factory.Decimal(returns) + PType.Kind.DECIMAL_ARBITRARY -> Factory.DecimalArbitrary + else -> throw TypeCheckException() + } + return op(factory, lhsEval.coerce(lhsEval.type, returns), rhsEval.coerce(rhsEval.type, returns)).eval(env) + } + + private fun Datum.coerce(input: PType, target: PType): Operator.Expr { + if (input == target) return ExprLiteral(this) + return ExprCast(ExprLiteral(this), Ref.Cast(input, target, isNullable = true)) + } + } +} diff --git a/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprArithmeticUnary.kt b/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprArithmeticUnary.kt new file mode 100644 index 000000000..61d0705dd --- /dev/null +++ b/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprArithmeticUnary.kt @@ -0,0 +1,178 @@ +package org.partiql.eval.internal.operator.rex + +import org.partiql.errors.TypeCheckException +import org.partiql.eval.internal.Environment +import org.partiql.eval.internal.helpers.ArithmeticTyper +import org.partiql.eval.internal.operator.Operator +import org.partiql.eval.value.Datum +import org.partiql.plan.Ref +import org.partiql.types.PType +import java.math.BigDecimal +import java.math.BigInteger + +internal class ExprArithmeticUnary( + val arg: Operator.Expr, + val extract: (Datum) -> T, + val toReturn: (T) -> Datum, + val op: (T) -> T, + val returnType: PType +) : Operator.Expr { + + override fun eval(env: Environment): Datum { + val argEval = arg.eval(env) + if (argEval.isNull) return Datum.nullValue(returnType) + if (argEval.isMissing) throw TypeCheckException() + return toReturn(op(extract(argEval))) + } + + internal interface Factory { + + fun positive(arg: Operator.Expr): Operator.Expr + + fun negative(arg: Operator.Expr): Operator.Expr + + abstract class Simple : Factory { + + abstract val extract: (Datum) -> T + abstract val toReturn: (T) -> Datum + abstract val positive: (T) -> T + abstract val negative: (T) -> T + abstract val returnType: PType + + override fun positive(arg: Operator.Expr): Operator.Expr { + return ExprArithmeticUnary(arg, extract, toReturn, positive, returnType) + } + + override fun negative(arg: Operator.Expr): Operator.Expr { + return ExprArithmeticUnary(arg, extract, toReturn, negative, returnType) + } + } + + object Int : Simple() { + override val extract: (Datum) -> kotlin.Int = Datum::getInt + override val toReturn: (kotlin.Int) -> Datum = Datum::int32Value + override val positive: (kotlin.Int) -> kotlin.Int = kotlin.Int::unaryPlus + override val negative: (kotlin.Int) -> kotlin.Int = kotlin.Int::unaryMinus + override val returnType: PType = PType.typeInt() + } + + object BigInt : Simple() { + override val extract: (Datum) -> kotlin.Long = Datum::getLong + override val toReturn: (kotlin.Long) -> Datum = Datum::int64Value + override val positive: (kotlin.Long) -> kotlin.Long = kotlin.Long::unaryPlus + override val negative: (kotlin.Long) -> kotlin.Long = kotlin.Long::unaryMinus + override val returnType: PType = PType.typeBigInt() + } + + object Short : Simple() { + override val extract: (Datum) -> kotlin.Short = Datum::getShort + override val toReturn: (kotlin.Short) -> Datum = Datum::smallInt + override val positive: (kotlin.Short) -> kotlin.Short = { x -> x } + override val negative: (kotlin.Short) -> kotlin.Short = { x -> (x * -1).toShort() } + override val returnType: PType = PType.typeSmallInt() + } + + object Float : Simple() { + override val extract: (Datum) -> kotlin.Float = Datum::getFloat + override val toReturn: (kotlin.Float) -> Datum = Datum::real + override val positive: (kotlin.Float) -> kotlin.Float = kotlin.Float::unaryPlus + override val negative: (kotlin.Float) -> kotlin.Float = kotlin.Float::unaryMinus + override val returnType: PType = PType.typeReal() + } + + object Double : Simple() { + override val extract: (Datum) -> kotlin.Double = Datum::getDouble + override val toReturn: (kotlin.Double) -> Datum = Datum::doublePrecision + override val positive: (kotlin.Double) -> kotlin.Double = kotlin.Double::unaryPlus + override val negative: (kotlin.Double) -> kotlin.Double = kotlin.Double::unaryMinus + override val returnType: PType = PType.typeDoublePrecision() + } + + object IntArbitrary : Simple() { + override val extract: (Datum) -> BigInteger = Datum::getBigInteger + override val toReturn: (BigInteger) -> Datum = Datum::intArbitrary + override val positive: (BigInteger) -> BigInteger = { x -> x } + override val negative: (BigInteger) -> BigInteger = BigInteger::unaryMinus + override val returnType: PType = PType.typeIntArbitrary() + } + + object DecimalArbitrary : Simple() { + override val extract: (Datum) -> BigDecimal = Datum::getBigDecimal + override val toReturn: (BigDecimal) -> Datum = Datum::decimalArbitrary + override val positive: (BigDecimal) -> BigDecimal = { x -> x } + override val negative: (BigDecimal) -> BigDecimal = BigDecimal::unaryMinus + override val returnType: PType = PType.typeDecimalArbitrary() + } + + object Byte : Simple() { + override val extract: (Datum) -> kotlin.Byte = Datum::getByte + override val toReturn: (kotlin.Byte) -> Datum = Datum::tinyInt + override val positive: (kotlin.Byte) -> kotlin.Byte = { x -> x } + override val negative: (kotlin.Byte) -> kotlin.Byte = { x -> (x * -1).toByte() } + override val returnType: PType = PType.typeTinyInt() + } + + class Decimal(private val returnType: PType) : Factory { + override fun positive(arg: Operator.Expr): Operator.Expr { + return Decimal(arg, returnType) { x -> x } + } + + override fun negative(arg: Operator.Expr): Operator.Expr { + return Decimal(arg, returnType, BigDecimal::unaryMinus) + } + } + + object Dynamic : Factory { + override fun positive(arg: Operator.Expr): Operator.Expr { + return Dynamic(arg, Factory::positive, ArithmeticTyper::positive) + } + + override fun negative(arg: Operator.Expr): Operator.Expr { + return Dynamic(arg, Factory::negative, ArithmeticTyper::negative) + } + } + } + + internal class Decimal( + private val arg: Operator.Expr, + private val returnType: PType, + private val op: (BigDecimal) -> BigDecimal + ) : Operator.Expr { + + override fun eval(env: Environment): Datum { + val lhsEval = arg.eval(env) + if (lhsEval.isNull) return Datum.nullValue(returnType) + if (lhsEval.isMissing) throw TypeCheckException() + return Datum.decimal(op(lhsEval.bigDecimal), returnType.precision, returnType.scale) + } + } + + internal class Dynamic( + val arg: Operator.Expr, + val op: (Factory, Operator.Expr) -> Operator.Expr, + val type: (PType) -> PType? + ) : Operator.Expr { + override fun eval(env: Environment): Datum { + val lhsEval = arg.eval(env) + val returns = type(lhsEval.type) ?: throw TypeCheckException() + val factory = when (returns.kind) { + PType.Kind.TINYINT -> Factory.Byte + PType.Kind.SMALLINT -> Factory.Short + PType.Kind.INT -> Factory.Int + PType.Kind.BIGINT -> Factory.BigInt + PType.Kind.INT_ARBITRARY -> Factory.IntArbitrary + PType.Kind.REAL -> Factory.Float + PType.Kind.DOUBLE_PRECISION -> Factory.Double + PType.Kind.DECIMAL -> Factory.Decimal(returns) + PType.Kind.DECIMAL_ARBITRARY -> Factory.DecimalArbitrary + else -> throw TypeCheckException() + } + return op(factory, lhsEval.coerce(lhsEval.type, returns)).eval(env) + } + + private fun Datum.coerce(input: PType, target: PType): Operator.Expr { + if (input == target) return ExprLiteral(this) + return ExprCast(ExprLiteral(this), Ref.Cast(input, target, isNullable = true)) + } + } +} diff --git a/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprCallDynamic.kt b/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprCallDynamic.kt index 337f28224..385fe86b8 100644 --- a/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprCallDynamic.kt +++ b/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprCallDynamic.kt @@ -2,15 +2,14 @@ package org.partiql.eval.internal.operator.rex import org.partiql.errors.TypeCheckException import org.partiql.eval.internal.Environment +import org.partiql.eval.internal.helpers.FunctionResolver 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 -import org.partiql.value.PartiQLValueType /** * This represents Dynamic Dispatch. @@ -23,26 +22,21 @@ import org.partiql.value.PartiQLValueType @OptIn(PartiQLValueExperimental::class, FnExperimental::class) internal class ExprCallDynamic( private val name: String, - private val candidates: Array, + candidates: Array, private val args: Array ) : Operator.Expr { - private val candidateIndex = CandidateIndex.All(candidates) + private val candidateImpls = candidates.map { it } + private val candidates = candidates.map { it.fn.signature } override fun eval(env: Environment): Datum { val actualArgs = args.map { it.eval(env) }.toTypedArray() - val actualTypes = actualArgs.map { it.type } - candidateIndex.get(actualTypes)?.let { - val transformedArgs = Array(actualArgs.size) { - actualArgs[it].toPartiQLValue() - } - return it.eval(transformedArgs, env) - } - val errorString = buildString { - val argString = actualArgs.joinToString(", ") - append("Could not dynamically find function ($name) for arguments $argString.") + val transformedArgs = Array(actualArgs.size) { + actualArgs[it].toPartiQLValue() } - throw TypeCheckException(errorString) + val actualTypes = actualArgs.map { it.type } + val match = FunctionResolver.resolve(candidates, actualTypes) as? FunctionResolver.FnMatch.Static ?: throw TypeCheckException() + return candidateImpls[match.index].eval(transformedArgs, env) } /** @@ -76,152 +70,4 @@ internal class ExprCallDynamic( return Datum.of(fn.invoke(args)) } } - - private sealed interface CandidateIndex { - - public fun get(args: List): Candidate? - - /** - * Preserves the original ordering of the passed-in candidates while making it faster to lookup matching - * functions. Utilizes both [Direct] and [Indirect]. - * - * Say a user passes in the following ordered candidates: - * [ - * foo(int16, int16) -> int16, - * foo(int32, int32) -> int32, - * foo(int64, int64) -> int64, - * foo(string, string) -> string, - * foo(struct, struct) -> struct, - * foo(numeric, numeric) -> numeric, - * foo(int64, dynamic) -> dynamic, - * foo(struct, dynamic) -> dynamic, - * foo(bool, bool) -> bool - * ] - * - * With the above candidates, the [CandidateIndex.All] will maintain the original ordering by utilizing: - * - [CandidateIndex.Direct] to match hashable runtime types - * - [CandidateIndex.Indirect] to match the dynamic type - * - * For the above example, the internal representation of [CandidateIndex.All] is a list of - * [CandidateIndex.Direct] and [CandidateIndex.Indirect] that looks like: - * ALL listOf( - * DIRECT hashMap( - * [int16, int16] --> foo(int16, int16) -> int16, - * [int32, int32] --> foo(int32, int32) -> int32, - * [int64, int64] --> foo(int64, int64) -> int64 - * [string, string] --> foo(string, string) -> string, - * [struct, struct] --> foo(struct, struct) -> struct, - * [numeric, numeric] --> foo(numeric, numeric) -> numeric - * ), - * INDIRECT listOf( - * foo(int64, dynamic) -> dynamic, - * foo(struct, dynamic) -> dynamic - * ), - * DIRECT hashMap( - * [bool, bool] --> foo(bool, bool) -> bool - * ) - * ) - * - * @param candidates - */ - class All(private val candidates: Array) : CandidateIndex { - - private val lookups: List - - init { - val lookupsMutable = mutableListOf() - val accumulator = mutableListOf, Candidate>>() - - // Indicates that we are currently processing dynamic candidates that accept ANY. - var activelyProcessingAny = true - - candidates.forEach { candidate -> - // Gather the input types to the dynamic invocation - val lookupTypes = candidate.coercions.mapIndexed { index, cast -> - when (cast) { - null -> candidate.fn.signature.parameters[index].type - else -> cast.input - } - } - val parametersIncludeAny = lookupTypes.any { it.kind == PType.Kind.DYNAMIC } - // A way to simplify logic further below. If it's empty, add something and set the processing type. - if (accumulator.isEmpty()) { - activelyProcessingAny = parametersIncludeAny - accumulator.add(lookupTypes to candidate) - return@forEach - } - when (parametersIncludeAny) { - true -> when (activelyProcessingAny) { - true -> accumulator.add(lookupTypes to candidate) - false -> { - activelyProcessingAny = true - lookupsMutable.add(Direct.of(accumulator.toList())) - accumulator.clear() - accumulator.add(lookupTypes to candidate) - } - } - false -> when (activelyProcessingAny) { - false -> accumulator.add(lookupTypes to candidate) - true -> { - activelyProcessingAny = false - lookupsMutable.add(Indirect(accumulator.toList())) - accumulator.clear() - accumulator.add(lookupTypes to candidate) - } - } - } - } - // Add any remaining candidates (that we didn't submit due to not ending while switching) - when (accumulator.isEmpty()) { - true -> { /* Do nothing! */ } - false -> when (activelyProcessingAny) { - true -> lookupsMutable.add(Indirect(accumulator.toList())) - false -> lookupsMutable.add(Direct.of(accumulator.toList())) - } - } - this.lookups = lookupsMutable - } - - override fun get(args: List): Candidate? { - return this.lookups.firstNotNullOfOrNull { it.get(args) } - } - } - - /** - * An O(1) structure to quickly find directly matching dynamic candidates. This is specifically used for runtime - * types that can be matched directly. AKA int32, int64, etc. This does NOT include [PartiQLValueType.ANY]. - */ - data class Direct private constructor(val directCandidates: HashMap, Candidate>) : CandidateIndex { - - companion object { - internal fun of(candidates: List, Candidate>>): Direct { - val candidateMap = java.util.HashMap, Candidate>() - candidateMap.putAll(candidates) - return Direct(candidateMap) - } - } - - override fun get(args: List): Candidate? { - return directCandidates[args] - } - } - - /** - * Holds all candidates that expect a [PartiQLValueType.ANY] on input. This maintains the original - * precedence order. - */ - data class Indirect(private val candidates: List, Candidate>>) : CandidateIndex { - override fun get(args: List): Candidate? { - candidates.forEach { (types, candidate) -> - for (i in args.indices) { - if (args[i] != types[i] && types[i].kind != PType.Kind.DYNAMIC) { - return@forEach - } - } - return candidate - } - return null - } - } - } } diff --git a/partiql-eval/src/test/kotlin/org/partiql/eval/internal/PartiQLEngineDefaultTest.kt b/partiql-eval/src/test/kotlin/org/partiql/eval/internal/PartiQLEngineDefaultTest.kt index 13a9e3415..7cbf10fc8 100644 --- a/partiql-eval/src/test/kotlin/org/partiql/eval/internal/PartiQLEngineDefaultTest.kt +++ b/partiql-eval/src/test/kotlin/org/partiql/eval/internal/PartiQLEngineDefaultTest.kt @@ -74,6 +74,11 @@ class PartiQLEngineDefaultTest { @Execution(ExecutionMode.CONCURRENT) fun globalsTests(tc: SuccessTestCase) = tc.assert() + @ParameterizedTest + @MethodSource("arithmeticUnaryTestCases") + @Execution(ExecutionMode.CONCURRENT) + fun arithmeticUnaryTests(tc: SuccessTestCase) = tc.assert() + companion object { @JvmStatic @@ -159,6 +164,34 @@ class PartiQLEngineDefaultTest { ), ) + @JvmStatic + fun arithmeticUnaryTestCases() = listOf( + SuccessTestCase( + input = """ + +(1 + 2.0) + """.trimIndent(), + expected = decimalValue(BigDecimal.valueOf(3.0)) + ), + SuccessTestCase( + input = """ + +(1 + 2) + """.trimIndent(), + expected = int32Value(3) + ), + SuccessTestCase( + input = """ + -(1 + 2.0) + """.trimIndent(), + expected = decimalValue(BigDecimal.valueOf(-3.0)) + ), + SuccessTestCase( + input = """ + -(1 + 2) + """.trimIndent(), + expected = int32Value(-3) + ), + ) + @JvmStatic fun joinTestCases() = listOf( // LEFT OUTER JOIN -- Easy @@ -1233,8 +1266,8 @@ class PartiQLEngineDefaultTest { val globals: List = emptyList(), ) { - private val engine = PartiQLEngine.builder().build() private val planner = PartiQLPlannerBuilder().build() + private val engine = PartiQLEngine.builder().build() private val parser = PartiQLParser.default() private val loader = createIonElementLoader() @@ -1287,9 +1320,11 @@ class PartiQLEngineDefaultTest { expectedWriter.append(expected) return buildString { PlanPrinter.append(this, plan) + appendLine("Expected Type: ${expected.type}") appendLine("Expected : $expectedBuffer") expectedBuffer.reset() expectedWriter.append(actual) + appendLine("Actual Type: ${actual.type}") appendLine("Actual : $expectedBuffer") } } @@ -1387,22 +1422,19 @@ class PartiQLEngineDefaultTest { fun developmentTest() { val tc = SuccessTestCase( input = """ - SELECT VALUE - CASE x + 1 - WHEN NULL THEN 'shouldnt be null' - WHEN MISSING THEN 'shouldnt be missing' - WHEN i THEN 'ONE' - WHEN f THEN 'TWO' - WHEN d THEN 'THREE' - ELSE '?' - END - FROM << i, f, d, null, missing >> AS x + -- 3 - 6.1 * 2e0 / 2 + -- + -- (1 + 2) - (3.1 + d) * f / x + -- 1 > min_int + CAST([1, 2, 3] AS INT) """, - expected = boolValue(true), + expected = missingValue(), globals = listOf( SuccessTestCase.Global("i", "1"), SuccessTestCase.Global("f", "2e0"), - SuccessTestCase.Global("d", "3.") + SuccessTestCase.Global("d", "3."), + SuccessTestCase.Global("x", "2"), + SuccessTestCase.Global("min_int", "-9223372036854775808") ) ) tc.assert() diff --git a/partiql-plan/api/partiql-plan.api b/partiql-plan/api/partiql-plan.api index 15010e052..d44e99fa5 100644 --- a/partiql-plan/api/partiql-plan.api +++ b/partiql-plan/api/partiql-plan.api @@ -199,6 +199,7 @@ public final class org/partiql/plan/Plan { public static final fun relOpUnpivot (Lorg/partiql/plan/Rex;)Lorg/partiql/plan/Rel$Op$Unpivot; public static final fun relType (Ljava/util/List;Ljava/util/Set;)Lorg/partiql/plan/Rel$Type; public static final fun rex (Lorg/partiql/types/PType;Lorg/partiql/plan/Rex$Op;)Lorg/partiql/plan/Rex; + public static final fun rexOpAdd (Lorg/partiql/plan/Rex;Lorg/partiql/plan/Rex;)Lorg/partiql/plan/Rex$Op$Add; public static final fun rexOpCallDynamic (Ljava/util/List;Ljava/util/List;)Lorg/partiql/plan/Rex$Op$Call$Dynamic; public static final fun rexOpCallDynamicCandidate (Lorg/partiql/plan/Ref;Ljava/util/List;)Lorg/partiql/plan/Rex$Op$Call$Dynamic$Candidate; public static final fun rexOpCallStatic (Lorg/partiql/plan/Ref;Ljava/util/List;)Lorg/partiql/plan/Rex$Op$Call$Static; @@ -207,19 +208,25 @@ public final class org/partiql/plan/Plan { public static final fun rexOpCast (Lorg/partiql/plan/Ref$Cast;Lorg/partiql/plan/Rex;)Lorg/partiql/plan/Rex$Op$Cast; public static final fun rexOpCoalesce (Ljava/util/List;)Lorg/partiql/plan/Rex$Op$Coalesce; public static final fun rexOpCollection (Ljava/util/List;)Lorg/partiql/plan/Rex$Op$Collection; + public static final fun rexOpDivide (Lorg/partiql/plan/Rex;Lorg/partiql/plan/Rex;)Lorg/partiql/plan/Rex$Op$Divide; public static final fun rexOpErr (Ljava/lang/String;Ljava/util/List;)Lorg/partiql/plan/Rex$Op$Err; public static final fun rexOpGlobal (Lorg/partiql/plan/Ref;)Lorg/partiql/plan/Rex$Op$Global; public static final fun rexOpLit (Lorg/partiql/value/PartiQLValue;)Lorg/partiql/plan/Rex$Op$Lit; public static final fun rexOpMissing (Ljava/lang/String;Ljava/util/List;)Lorg/partiql/plan/Rex$Op$Missing; + public static final fun rexOpModulo (Lorg/partiql/plan/Rex;Lorg/partiql/plan/Rex;)Lorg/partiql/plan/Rex$Op$Modulo; + public static final fun rexOpMultiply (Lorg/partiql/plan/Rex;Lorg/partiql/plan/Rex;)Lorg/partiql/plan/Rex$Op$Multiply; + public static final fun rexOpNegative (Lorg/partiql/plan/Rex;)Lorg/partiql/plan/Rex$Op$Negative; public static final fun rexOpNullif (Lorg/partiql/plan/Rex;Lorg/partiql/plan/Rex;)Lorg/partiql/plan/Rex$Op$Nullif; public static final fun rexOpPathIndex (Lorg/partiql/plan/Rex;Lorg/partiql/plan/Rex;)Lorg/partiql/plan/Rex$Op$Path$Index; public static final fun rexOpPathKey (Lorg/partiql/plan/Rex;Lorg/partiql/plan/Rex;)Lorg/partiql/plan/Rex$Op$Path$Key; public static final fun rexOpPathSymbol (Lorg/partiql/plan/Rex;Ljava/lang/String;)Lorg/partiql/plan/Rex$Op$Path$Symbol; public static final fun rexOpPivot (Lorg/partiql/plan/Rex;Lorg/partiql/plan/Rex;Lorg/partiql/plan/Rel;)Lorg/partiql/plan/Rex$Op$Pivot; + public static final fun rexOpPositive (Lorg/partiql/plan/Rex;)Lorg/partiql/plan/Rex$Op$Positive; public static final fun rexOpSelect (Lorg/partiql/plan/Rex;Lorg/partiql/plan/Rel;)Lorg/partiql/plan/Rex$Op$Select; public static final fun rexOpStruct (Ljava/util/List;)Lorg/partiql/plan/Rex$Op$Struct; public static final fun rexOpStructField (Lorg/partiql/plan/Rex;Lorg/partiql/plan/Rex;)Lorg/partiql/plan/Rex$Op$Struct$Field; public static final fun rexOpSubquery (Lorg/partiql/plan/Rex;Lorg/partiql/plan/Rel;Lorg/partiql/plan/Rex$Op$Subquery$Coercion;)Lorg/partiql/plan/Rex$Op$Subquery; + public static final fun rexOpSubtract (Lorg/partiql/plan/Rex;Lorg/partiql/plan/Rex;)Lorg/partiql/plan/Rex$Op$Subtract; public static final fun rexOpTupleUnion (Ljava/util/List;)Lorg/partiql/plan/Rex$Op$TupleUnion; public static final fun rexOpVar (II)Lorg/partiql/plan/Rex$Op$Var; public static final fun statementQuery (Lorg/partiql/plan/Rex;)Lorg/partiql/plan/Statement$Query; @@ -946,6 +953,27 @@ public abstract class org/partiql/plan/Rex$Op : org/partiql/plan/PlanNode { public fun accept (Lorg/partiql/plan/visitor/PlanVisitor;Ljava/lang/Object;)Ljava/lang/Object; } +public final class org/partiql/plan/Rex$Op$Add : org/partiql/plan/Rex$Op { + public static final field Companion Lorg/partiql/plan/Rex$Op$Add$Companion; + public final field lhs Lorg/partiql/plan/Rex; + public final field rhs Lorg/partiql/plan/Rex; + public fun (Lorg/partiql/plan/Rex;Lorg/partiql/plan/Rex;)V + public fun accept (Lorg/partiql/plan/visitor/PlanVisitor;Ljava/lang/Object;)Ljava/lang/Object; + public static final fun builder ()Lorg/partiql/plan/builder/RexOpAddBuilder; + public final fun component1 ()Lorg/partiql/plan/Rex; + public final fun component2 ()Lorg/partiql/plan/Rex; + public final fun copy (Lorg/partiql/plan/Rex;Lorg/partiql/plan/Rex;)Lorg/partiql/plan/Rex$Op$Add; + public static synthetic fun copy$default (Lorg/partiql/plan/Rex$Op$Add;Lorg/partiql/plan/Rex;Lorg/partiql/plan/Rex;ILjava/lang/Object;)Lorg/partiql/plan/Rex$Op$Add; + public fun equals (Ljava/lang/Object;)Z + public fun getChildren ()Ljava/util/List; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class org/partiql/plan/Rex$Op$Add$Companion { + public final fun builder ()Lorg/partiql/plan/builder/RexOpAddBuilder; +} + public abstract class org/partiql/plan/Rex$Op$Call : org/partiql/plan/Rex$Op { public fun accept (Lorg/partiql/plan/visitor/PlanVisitor;Ljava/lang/Object;)Ljava/lang/Object; } @@ -1114,6 +1142,27 @@ public final class org/partiql/plan/Rex$Op$Collection$Companion { public final fun builder ()Lorg/partiql/plan/builder/RexOpCollectionBuilder; } +public final class org/partiql/plan/Rex$Op$Divide : org/partiql/plan/Rex$Op { + public static final field Companion Lorg/partiql/plan/Rex$Op$Divide$Companion; + public final field lhs Lorg/partiql/plan/Rex; + public final field rhs Lorg/partiql/plan/Rex; + public fun (Lorg/partiql/plan/Rex;Lorg/partiql/plan/Rex;)V + public fun accept (Lorg/partiql/plan/visitor/PlanVisitor;Ljava/lang/Object;)Ljava/lang/Object; + public static final fun builder ()Lorg/partiql/plan/builder/RexOpDivideBuilder; + public final fun component1 ()Lorg/partiql/plan/Rex; + public final fun component2 ()Lorg/partiql/plan/Rex; + public final fun copy (Lorg/partiql/plan/Rex;Lorg/partiql/plan/Rex;)Lorg/partiql/plan/Rex$Op$Divide; + public static synthetic fun copy$default (Lorg/partiql/plan/Rex$Op$Divide;Lorg/partiql/plan/Rex;Lorg/partiql/plan/Rex;ILjava/lang/Object;)Lorg/partiql/plan/Rex$Op$Divide; + public fun equals (Ljava/lang/Object;)Z + public fun getChildren ()Ljava/util/List; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class org/partiql/plan/Rex$Op$Divide$Companion { + public final fun builder ()Lorg/partiql/plan/builder/RexOpDivideBuilder; +} + public final class org/partiql/plan/Rex$Op$Err : org/partiql/plan/Rex$Op { public static final field Companion Lorg/partiql/plan/Rex$Op$Err$Companion; public final field causes Ljava/util/List; @@ -1194,6 +1243,67 @@ public final class org/partiql/plan/Rex$Op$Missing$Companion { public final fun builder ()Lorg/partiql/plan/builder/RexOpMissingBuilder; } +public final class org/partiql/plan/Rex$Op$Modulo : org/partiql/plan/Rex$Op { + public static final field Companion Lorg/partiql/plan/Rex$Op$Modulo$Companion; + public final field lhs Lorg/partiql/plan/Rex; + public final field rhs Lorg/partiql/plan/Rex; + public fun (Lorg/partiql/plan/Rex;Lorg/partiql/plan/Rex;)V + public fun accept (Lorg/partiql/plan/visitor/PlanVisitor;Ljava/lang/Object;)Ljava/lang/Object; + public static final fun builder ()Lorg/partiql/plan/builder/RexOpModuloBuilder; + public final fun component1 ()Lorg/partiql/plan/Rex; + public final fun component2 ()Lorg/partiql/plan/Rex; + public final fun copy (Lorg/partiql/plan/Rex;Lorg/partiql/plan/Rex;)Lorg/partiql/plan/Rex$Op$Modulo; + public static synthetic fun copy$default (Lorg/partiql/plan/Rex$Op$Modulo;Lorg/partiql/plan/Rex;Lorg/partiql/plan/Rex;ILjava/lang/Object;)Lorg/partiql/plan/Rex$Op$Modulo; + public fun equals (Ljava/lang/Object;)Z + public fun getChildren ()Ljava/util/List; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class org/partiql/plan/Rex$Op$Modulo$Companion { + public final fun builder ()Lorg/partiql/plan/builder/RexOpModuloBuilder; +} + +public final class org/partiql/plan/Rex$Op$Multiply : org/partiql/plan/Rex$Op { + public static final field Companion Lorg/partiql/plan/Rex$Op$Multiply$Companion; + public final field lhs Lorg/partiql/plan/Rex; + public final field rhs Lorg/partiql/plan/Rex; + public fun (Lorg/partiql/plan/Rex;Lorg/partiql/plan/Rex;)V + public fun accept (Lorg/partiql/plan/visitor/PlanVisitor;Ljava/lang/Object;)Ljava/lang/Object; + public static final fun builder ()Lorg/partiql/plan/builder/RexOpMultiplyBuilder; + public final fun component1 ()Lorg/partiql/plan/Rex; + public final fun component2 ()Lorg/partiql/plan/Rex; + public final fun copy (Lorg/partiql/plan/Rex;Lorg/partiql/plan/Rex;)Lorg/partiql/plan/Rex$Op$Multiply; + public static synthetic fun copy$default (Lorg/partiql/plan/Rex$Op$Multiply;Lorg/partiql/plan/Rex;Lorg/partiql/plan/Rex;ILjava/lang/Object;)Lorg/partiql/plan/Rex$Op$Multiply; + public fun equals (Ljava/lang/Object;)Z + public fun getChildren ()Ljava/util/List; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class org/partiql/plan/Rex$Op$Multiply$Companion { + public final fun builder ()Lorg/partiql/plan/builder/RexOpMultiplyBuilder; +} + +public final class org/partiql/plan/Rex$Op$Negative : org/partiql/plan/Rex$Op { + public static final field Companion Lorg/partiql/plan/Rex$Op$Negative$Companion; + public final field arg Lorg/partiql/plan/Rex; + public fun (Lorg/partiql/plan/Rex;)V + public fun accept (Lorg/partiql/plan/visitor/PlanVisitor;Ljava/lang/Object;)Ljava/lang/Object; + public static final fun builder ()Lorg/partiql/plan/builder/RexOpNegativeBuilder; + public final fun component1 ()Lorg/partiql/plan/Rex; + public final fun copy (Lorg/partiql/plan/Rex;)Lorg/partiql/plan/Rex$Op$Negative; + public static synthetic fun copy$default (Lorg/partiql/plan/Rex$Op$Negative;Lorg/partiql/plan/Rex;ILjava/lang/Object;)Lorg/partiql/plan/Rex$Op$Negative; + public fun equals (Ljava/lang/Object;)Z + public fun getChildren ()Ljava/util/List; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class org/partiql/plan/Rex$Op$Negative$Companion { + public final fun builder ()Lorg/partiql/plan/builder/RexOpNegativeBuilder; +} + public final class org/partiql/plan/Rex$Op$Nullif : org/partiql/plan/Rex$Op { public static final field Companion Lorg/partiql/plan/Rex$Op$Nullif$Companion; public final field nullifier Lorg/partiql/plan/Rex; @@ -1305,6 +1415,25 @@ public final class org/partiql/plan/Rex$Op$Pivot$Companion { public final fun builder ()Lorg/partiql/plan/builder/RexOpPivotBuilder; } +public final class org/partiql/plan/Rex$Op$Positive : org/partiql/plan/Rex$Op { + public static final field Companion Lorg/partiql/plan/Rex$Op$Positive$Companion; + public final field arg Lorg/partiql/plan/Rex; + public fun (Lorg/partiql/plan/Rex;)V + public fun accept (Lorg/partiql/plan/visitor/PlanVisitor;Ljava/lang/Object;)Ljava/lang/Object; + public static final fun builder ()Lorg/partiql/plan/builder/RexOpPositiveBuilder; + public final fun component1 ()Lorg/partiql/plan/Rex; + public final fun copy (Lorg/partiql/plan/Rex;)Lorg/partiql/plan/Rex$Op$Positive; + public static synthetic fun copy$default (Lorg/partiql/plan/Rex$Op$Positive;Lorg/partiql/plan/Rex;ILjava/lang/Object;)Lorg/partiql/plan/Rex$Op$Positive; + public fun equals (Ljava/lang/Object;)Z + public fun getChildren ()Ljava/util/List; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class org/partiql/plan/Rex$Op$Positive$Companion { + public final fun builder ()Lorg/partiql/plan/builder/RexOpPositiveBuilder; +} + public final class org/partiql/plan/Rex$Op$Select : org/partiql/plan/Rex$Op { public static final field Companion Lorg/partiql/plan/Rex$Op$Select$Companion; public final field constructor Lorg/partiql/plan/Rex; @@ -1396,6 +1525,27 @@ public final class org/partiql/plan/Rex$Op$Subquery$Companion { public final fun builder ()Lorg/partiql/plan/builder/RexOpSubqueryBuilder; } +public final class org/partiql/plan/Rex$Op$Subtract : org/partiql/plan/Rex$Op { + public static final field Companion Lorg/partiql/plan/Rex$Op$Subtract$Companion; + public final field lhs Lorg/partiql/plan/Rex; + public final field rhs Lorg/partiql/plan/Rex; + public fun (Lorg/partiql/plan/Rex;Lorg/partiql/plan/Rex;)V + public fun accept (Lorg/partiql/plan/visitor/PlanVisitor;Ljava/lang/Object;)Ljava/lang/Object; + public static final fun builder ()Lorg/partiql/plan/builder/RexOpSubtractBuilder; + public final fun component1 ()Lorg/partiql/plan/Rex; + public final fun component2 ()Lorg/partiql/plan/Rex; + public final fun copy (Lorg/partiql/plan/Rex;Lorg/partiql/plan/Rex;)Lorg/partiql/plan/Rex$Op$Subtract; + public static synthetic fun copy$default (Lorg/partiql/plan/Rex$Op$Subtract;Lorg/partiql/plan/Rex;Lorg/partiql/plan/Rex;ILjava/lang/Object;)Lorg/partiql/plan/Rex$Op$Subtract; + public fun equals (Ljava/lang/Object;)Z + public fun getChildren ()Ljava/util/List; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class org/partiql/plan/Rex$Op$Subtract$Companion { + public final fun builder ()Lorg/partiql/plan/builder/RexOpSubtractBuilder; +} + public final class org/partiql/plan/Rex$Op$TupleUnion : org/partiql/plan/Rex$Op { public static final field Companion Lorg/partiql/plan/Rex$Op$TupleUnion$Companion; public final field args Ljava/util/List; @@ -1628,6 +1778,8 @@ public final class org/partiql/plan/builder/PlanBuilder { public static synthetic fun relType$default (Lorg/partiql/plan/builder/PlanBuilder;Ljava/util/List;Ljava/util/Set;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lorg/partiql/plan/Rel$Type; public final fun rex (Lorg/partiql/types/PType;Lorg/partiql/plan/Rex$Op;Lkotlin/jvm/functions/Function1;)Lorg/partiql/plan/Rex; public static synthetic fun rex$default (Lorg/partiql/plan/builder/PlanBuilder;Lorg/partiql/types/PType;Lorg/partiql/plan/Rex$Op;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lorg/partiql/plan/Rex; + public final fun rexOpAdd (Lorg/partiql/plan/Rex;Lorg/partiql/plan/Rex;Lkotlin/jvm/functions/Function1;)Lorg/partiql/plan/Rex$Op$Add; + public static synthetic fun rexOpAdd$default (Lorg/partiql/plan/builder/PlanBuilder;Lorg/partiql/plan/Rex;Lorg/partiql/plan/Rex;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lorg/partiql/plan/Rex$Op$Add; public final fun rexOpCallDynamic (Ljava/util/List;Ljava/util/List;Lkotlin/jvm/functions/Function1;)Lorg/partiql/plan/Rex$Op$Call$Dynamic; public static synthetic fun rexOpCallDynamic$default (Lorg/partiql/plan/builder/PlanBuilder;Ljava/util/List;Ljava/util/List;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lorg/partiql/plan/Rex$Op$Call$Dynamic; public final fun rexOpCallDynamicCandidate (Lorg/partiql/plan/Ref;Ljava/util/List;Lkotlin/jvm/functions/Function1;)Lorg/partiql/plan/Rex$Op$Call$Dynamic$Candidate; @@ -1644,6 +1796,8 @@ public final class org/partiql/plan/builder/PlanBuilder { public static synthetic fun rexOpCoalesce$default (Lorg/partiql/plan/builder/PlanBuilder;Ljava/util/List;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lorg/partiql/plan/Rex$Op$Coalesce; public final fun rexOpCollection (Ljava/util/List;Lkotlin/jvm/functions/Function1;)Lorg/partiql/plan/Rex$Op$Collection; public static synthetic fun rexOpCollection$default (Lorg/partiql/plan/builder/PlanBuilder;Ljava/util/List;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lorg/partiql/plan/Rex$Op$Collection; + public final fun rexOpDivide (Lorg/partiql/plan/Rex;Lorg/partiql/plan/Rex;Lkotlin/jvm/functions/Function1;)Lorg/partiql/plan/Rex$Op$Divide; + public static synthetic fun rexOpDivide$default (Lorg/partiql/plan/builder/PlanBuilder;Lorg/partiql/plan/Rex;Lorg/partiql/plan/Rex;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lorg/partiql/plan/Rex$Op$Divide; public final fun rexOpErr (Ljava/lang/String;Ljava/util/List;Lkotlin/jvm/functions/Function1;)Lorg/partiql/plan/Rex$Op$Err; public static synthetic fun rexOpErr$default (Lorg/partiql/plan/builder/PlanBuilder;Ljava/lang/String;Ljava/util/List;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lorg/partiql/plan/Rex$Op$Err; public final fun rexOpGlobal (Lorg/partiql/plan/Ref;Lkotlin/jvm/functions/Function1;)Lorg/partiql/plan/Rex$Op$Global; @@ -1652,6 +1806,12 @@ public final class org/partiql/plan/builder/PlanBuilder { public static synthetic fun rexOpLit$default (Lorg/partiql/plan/builder/PlanBuilder;Lorg/partiql/value/PartiQLValue;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lorg/partiql/plan/Rex$Op$Lit; public final fun rexOpMissing (Ljava/lang/String;Ljava/util/List;Lkotlin/jvm/functions/Function1;)Lorg/partiql/plan/Rex$Op$Missing; public static synthetic fun rexOpMissing$default (Lorg/partiql/plan/builder/PlanBuilder;Ljava/lang/String;Ljava/util/List;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lorg/partiql/plan/Rex$Op$Missing; + public final fun rexOpModulo (Lorg/partiql/plan/Rex;Lorg/partiql/plan/Rex;Lkotlin/jvm/functions/Function1;)Lorg/partiql/plan/Rex$Op$Modulo; + public static synthetic fun rexOpModulo$default (Lorg/partiql/plan/builder/PlanBuilder;Lorg/partiql/plan/Rex;Lorg/partiql/plan/Rex;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lorg/partiql/plan/Rex$Op$Modulo; + public final fun rexOpMultiply (Lorg/partiql/plan/Rex;Lorg/partiql/plan/Rex;Lkotlin/jvm/functions/Function1;)Lorg/partiql/plan/Rex$Op$Multiply; + public static synthetic fun rexOpMultiply$default (Lorg/partiql/plan/builder/PlanBuilder;Lorg/partiql/plan/Rex;Lorg/partiql/plan/Rex;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lorg/partiql/plan/Rex$Op$Multiply; + public final fun rexOpNegative (Lorg/partiql/plan/Rex;Lkotlin/jvm/functions/Function1;)Lorg/partiql/plan/Rex$Op$Negative; + public static synthetic fun rexOpNegative$default (Lorg/partiql/plan/builder/PlanBuilder;Lorg/partiql/plan/Rex;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lorg/partiql/plan/Rex$Op$Negative; public final fun rexOpNullif (Lorg/partiql/plan/Rex;Lorg/partiql/plan/Rex;Lkotlin/jvm/functions/Function1;)Lorg/partiql/plan/Rex$Op$Nullif; public static synthetic fun rexOpNullif$default (Lorg/partiql/plan/builder/PlanBuilder;Lorg/partiql/plan/Rex;Lorg/partiql/plan/Rex;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lorg/partiql/plan/Rex$Op$Nullif; public final fun rexOpPathIndex (Lorg/partiql/plan/Rex;Lorg/partiql/plan/Rex;Lkotlin/jvm/functions/Function1;)Lorg/partiql/plan/Rex$Op$Path$Index; @@ -1662,6 +1822,8 @@ public final class org/partiql/plan/builder/PlanBuilder { public static synthetic fun rexOpPathSymbol$default (Lorg/partiql/plan/builder/PlanBuilder;Lorg/partiql/plan/Rex;Ljava/lang/String;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lorg/partiql/plan/Rex$Op$Path$Symbol; public final fun rexOpPivot (Lorg/partiql/plan/Rex;Lorg/partiql/plan/Rex;Lorg/partiql/plan/Rel;Lkotlin/jvm/functions/Function1;)Lorg/partiql/plan/Rex$Op$Pivot; public static synthetic fun rexOpPivot$default (Lorg/partiql/plan/builder/PlanBuilder;Lorg/partiql/plan/Rex;Lorg/partiql/plan/Rex;Lorg/partiql/plan/Rel;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lorg/partiql/plan/Rex$Op$Pivot; + public final fun rexOpPositive (Lorg/partiql/plan/Rex;Lkotlin/jvm/functions/Function1;)Lorg/partiql/plan/Rex$Op$Positive; + public static synthetic fun rexOpPositive$default (Lorg/partiql/plan/builder/PlanBuilder;Lorg/partiql/plan/Rex;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lorg/partiql/plan/Rex$Op$Positive; public final fun rexOpSelect (Lorg/partiql/plan/Rex;Lorg/partiql/plan/Rel;Lkotlin/jvm/functions/Function1;)Lorg/partiql/plan/Rex$Op$Select; public static synthetic fun rexOpSelect$default (Lorg/partiql/plan/builder/PlanBuilder;Lorg/partiql/plan/Rex;Lorg/partiql/plan/Rel;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lorg/partiql/plan/Rex$Op$Select; public final fun rexOpStruct (Ljava/util/List;Lkotlin/jvm/functions/Function1;)Lorg/partiql/plan/Rex$Op$Struct; @@ -1670,6 +1832,8 @@ public final class org/partiql/plan/builder/PlanBuilder { public static synthetic fun rexOpStructField$default (Lorg/partiql/plan/builder/PlanBuilder;Lorg/partiql/plan/Rex;Lorg/partiql/plan/Rex;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lorg/partiql/plan/Rex$Op$Struct$Field; public final fun rexOpSubquery (Lorg/partiql/plan/Rex;Lorg/partiql/plan/Rel;Lorg/partiql/plan/Rex$Op$Subquery$Coercion;Lkotlin/jvm/functions/Function1;)Lorg/partiql/plan/Rex$Op$Subquery; public static synthetic fun rexOpSubquery$default (Lorg/partiql/plan/builder/PlanBuilder;Lorg/partiql/plan/Rex;Lorg/partiql/plan/Rel;Lorg/partiql/plan/Rex$Op$Subquery$Coercion;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lorg/partiql/plan/Rex$Op$Subquery; + public final fun rexOpSubtract (Lorg/partiql/plan/Rex;Lorg/partiql/plan/Rex;Lkotlin/jvm/functions/Function1;)Lorg/partiql/plan/Rex$Op$Subtract; + public static synthetic fun rexOpSubtract$default (Lorg/partiql/plan/builder/PlanBuilder;Lorg/partiql/plan/Rex;Lorg/partiql/plan/Rex;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lorg/partiql/plan/Rex$Op$Subtract; public final fun rexOpTupleUnion (Ljava/util/List;Lkotlin/jvm/functions/Function1;)Lorg/partiql/plan/Rex$Op$TupleUnion; public static synthetic fun rexOpTupleUnion$default (Lorg/partiql/plan/builder/PlanBuilder;Ljava/util/List;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lorg/partiql/plan/Rex$Op$TupleUnion; public final fun rexOpVar (Ljava/lang/Integer;Ljava/lang/Integer;Lkotlin/jvm/functions/Function1;)Lorg/partiql/plan/Rex$Op$Var; @@ -2072,6 +2236,19 @@ public final class org/partiql/plan/builder/RexBuilder { public final fun type (Lorg/partiql/types/PType;)Lorg/partiql/plan/builder/RexBuilder; } +public final class org/partiql/plan/builder/RexOpAddBuilder { + public fun ()V + public fun (Lorg/partiql/plan/Rex;Lorg/partiql/plan/Rex;)V + public synthetic fun (Lorg/partiql/plan/Rex;Lorg/partiql/plan/Rex;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun build ()Lorg/partiql/plan/Rex$Op$Add; + public final fun getLhs ()Lorg/partiql/plan/Rex; + public final fun getRhs ()Lorg/partiql/plan/Rex; + public final fun lhs (Lorg/partiql/plan/Rex;)Lorg/partiql/plan/builder/RexOpAddBuilder; + public final fun rhs (Lorg/partiql/plan/Rex;)Lorg/partiql/plan/builder/RexOpAddBuilder; + public final fun setLhs (Lorg/partiql/plan/Rex;)V + public final fun setRhs (Lorg/partiql/plan/Rex;)V +} + public final class org/partiql/plan/builder/RexOpCallDynamicBuilder { public fun ()V public fun (Ljava/util/List;Ljava/util/List;)V @@ -2170,6 +2347,19 @@ public final class org/partiql/plan/builder/RexOpCollectionBuilder { public final fun values (Ljava/util/List;)Lorg/partiql/plan/builder/RexOpCollectionBuilder; } +public final class org/partiql/plan/builder/RexOpDivideBuilder { + public fun ()V + public fun (Lorg/partiql/plan/Rex;Lorg/partiql/plan/Rex;)V + public synthetic fun (Lorg/partiql/plan/Rex;Lorg/partiql/plan/Rex;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun build ()Lorg/partiql/plan/Rex$Op$Divide; + public final fun getLhs ()Lorg/partiql/plan/Rex; + public final fun getRhs ()Lorg/partiql/plan/Rex; + public final fun lhs (Lorg/partiql/plan/Rex;)Lorg/partiql/plan/builder/RexOpDivideBuilder; + public final fun rhs (Lorg/partiql/plan/Rex;)Lorg/partiql/plan/builder/RexOpDivideBuilder; + public final fun setLhs (Lorg/partiql/plan/Rex;)V + public final fun setRhs (Lorg/partiql/plan/Rex;)V +} + public final class org/partiql/plan/builder/RexOpErrBuilder { public fun ()V public fun (Ljava/lang/String;Ljava/util/List;)V @@ -2216,6 +2406,42 @@ public final class org/partiql/plan/builder/RexOpMissingBuilder { public final fun setMessage (Ljava/lang/String;)V } +public final class org/partiql/plan/builder/RexOpModuloBuilder { + public fun ()V + public fun (Lorg/partiql/plan/Rex;Lorg/partiql/plan/Rex;)V + public synthetic fun (Lorg/partiql/plan/Rex;Lorg/partiql/plan/Rex;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun build ()Lorg/partiql/plan/Rex$Op$Modulo; + public final fun getLhs ()Lorg/partiql/plan/Rex; + public final fun getRhs ()Lorg/partiql/plan/Rex; + public final fun lhs (Lorg/partiql/plan/Rex;)Lorg/partiql/plan/builder/RexOpModuloBuilder; + public final fun rhs (Lorg/partiql/plan/Rex;)Lorg/partiql/plan/builder/RexOpModuloBuilder; + public final fun setLhs (Lorg/partiql/plan/Rex;)V + public final fun setRhs (Lorg/partiql/plan/Rex;)V +} + +public final class org/partiql/plan/builder/RexOpMultiplyBuilder { + public fun ()V + public fun (Lorg/partiql/plan/Rex;Lorg/partiql/plan/Rex;)V + public synthetic fun (Lorg/partiql/plan/Rex;Lorg/partiql/plan/Rex;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun build ()Lorg/partiql/plan/Rex$Op$Multiply; + public final fun getLhs ()Lorg/partiql/plan/Rex; + public final fun getRhs ()Lorg/partiql/plan/Rex; + public final fun lhs (Lorg/partiql/plan/Rex;)Lorg/partiql/plan/builder/RexOpMultiplyBuilder; + public final fun rhs (Lorg/partiql/plan/Rex;)Lorg/partiql/plan/builder/RexOpMultiplyBuilder; + public final fun setLhs (Lorg/partiql/plan/Rex;)V + public final fun setRhs (Lorg/partiql/plan/Rex;)V +} + +public final class org/partiql/plan/builder/RexOpNegativeBuilder { + public fun ()V + public fun (Lorg/partiql/plan/Rex;)V + public synthetic fun (Lorg/partiql/plan/Rex;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun arg (Lorg/partiql/plan/Rex;)Lorg/partiql/plan/builder/RexOpNegativeBuilder; + public final fun build ()Lorg/partiql/plan/Rex$Op$Negative; + public final fun getArg ()Lorg/partiql/plan/Rex; + public final fun setArg (Lorg/partiql/plan/Rex;)V +} + public final class org/partiql/plan/builder/RexOpNullifBuilder { public fun ()V public fun (Lorg/partiql/plan/Rex;Lorg/partiql/plan/Rex;)V @@ -2284,6 +2510,16 @@ public final class org/partiql/plan/builder/RexOpPivotBuilder { public final fun value (Lorg/partiql/plan/Rex;)Lorg/partiql/plan/builder/RexOpPivotBuilder; } +public final class org/partiql/plan/builder/RexOpPositiveBuilder { + public fun ()V + public fun (Lorg/partiql/plan/Rex;)V + public synthetic fun (Lorg/partiql/plan/Rex;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun arg (Lorg/partiql/plan/Rex;)Lorg/partiql/plan/builder/RexOpPositiveBuilder; + public final fun build ()Lorg/partiql/plan/Rex$Op$Positive; + public final fun getArg ()Lorg/partiql/plan/Rex; + public final fun setArg (Lorg/partiql/plan/Rex;)V +} + public final class org/partiql/plan/builder/RexOpSelectBuilder { public fun ()V public fun (Lorg/partiql/plan/Rex;Lorg/partiql/plan/Rel;)V @@ -2336,6 +2572,19 @@ public final class org/partiql/plan/builder/RexOpSubqueryBuilder { public final fun setRel (Lorg/partiql/plan/Rel;)V } +public final class org/partiql/plan/builder/RexOpSubtractBuilder { + public fun ()V + public fun (Lorg/partiql/plan/Rex;Lorg/partiql/plan/Rex;)V + public synthetic fun (Lorg/partiql/plan/Rex;Lorg/partiql/plan/Rex;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun build ()Lorg/partiql/plan/Rex$Op$Subtract; + public final fun getLhs ()Lorg/partiql/plan/Rex; + public final fun getRhs ()Lorg/partiql/plan/Rex; + public final fun lhs (Lorg/partiql/plan/Rex;)Lorg/partiql/plan/builder/RexOpSubtractBuilder; + public final fun rhs (Lorg/partiql/plan/Rex;)Lorg/partiql/plan/builder/RexOpSubtractBuilder; + public final fun setLhs (Lorg/partiql/plan/Rex;)V + public final fun setRhs (Lorg/partiql/plan/Rex;)V +} + public final class org/partiql/plan/builder/RexOpTupleUnionBuilder { public fun ()V public fun (Ljava/util/List;)V @@ -2455,6 +2704,8 @@ public abstract class org/partiql/plan/util/PlanRewriter : org/partiql/plan/visi public fun visitRelType (Lorg/partiql/plan/Rel$Type;Ljava/lang/Object;)Lorg/partiql/plan/PlanNode; public synthetic fun visitRex (Lorg/partiql/plan/Rex;Ljava/lang/Object;)Ljava/lang/Object; public fun visitRex (Lorg/partiql/plan/Rex;Ljava/lang/Object;)Lorg/partiql/plan/PlanNode; + public synthetic fun visitRexOpAdd (Lorg/partiql/plan/Rex$Op$Add;Ljava/lang/Object;)Ljava/lang/Object; + public fun visitRexOpAdd (Lorg/partiql/plan/Rex$Op$Add;Ljava/lang/Object;)Lorg/partiql/plan/PlanNode; public synthetic fun visitRexOpCallDynamic (Lorg/partiql/plan/Rex$Op$Call$Dynamic;Ljava/lang/Object;)Ljava/lang/Object; public fun visitRexOpCallDynamic (Lorg/partiql/plan/Rex$Op$Call$Dynamic;Ljava/lang/Object;)Lorg/partiql/plan/PlanNode; public synthetic fun visitRexOpCallDynamicCandidate (Lorg/partiql/plan/Rex$Op$Call$Dynamic$Candidate;Ljava/lang/Object;)Ljava/lang/Object; @@ -2471,6 +2722,8 @@ public abstract class org/partiql/plan/util/PlanRewriter : org/partiql/plan/visi public fun visitRexOpCoalesce (Lorg/partiql/plan/Rex$Op$Coalesce;Ljava/lang/Object;)Lorg/partiql/plan/PlanNode; public synthetic fun visitRexOpCollection (Lorg/partiql/plan/Rex$Op$Collection;Ljava/lang/Object;)Ljava/lang/Object; public fun visitRexOpCollection (Lorg/partiql/plan/Rex$Op$Collection;Ljava/lang/Object;)Lorg/partiql/plan/PlanNode; + public synthetic fun visitRexOpDivide (Lorg/partiql/plan/Rex$Op$Divide;Ljava/lang/Object;)Ljava/lang/Object; + public fun visitRexOpDivide (Lorg/partiql/plan/Rex$Op$Divide;Ljava/lang/Object;)Lorg/partiql/plan/PlanNode; public synthetic fun visitRexOpErr (Lorg/partiql/plan/Rex$Op$Err;Ljava/lang/Object;)Ljava/lang/Object; public fun visitRexOpErr (Lorg/partiql/plan/Rex$Op$Err;Ljava/lang/Object;)Lorg/partiql/plan/PlanNode; public synthetic fun visitRexOpGlobal (Lorg/partiql/plan/Rex$Op$Global;Ljava/lang/Object;)Ljava/lang/Object; @@ -2479,6 +2732,12 @@ public abstract class org/partiql/plan/util/PlanRewriter : org/partiql/plan/visi public fun visitRexOpLit (Lorg/partiql/plan/Rex$Op$Lit;Ljava/lang/Object;)Lorg/partiql/plan/PlanNode; public synthetic fun visitRexOpMissing (Lorg/partiql/plan/Rex$Op$Missing;Ljava/lang/Object;)Ljava/lang/Object; public fun visitRexOpMissing (Lorg/partiql/plan/Rex$Op$Missing;Ljava/lang/Object;)Lorg/partiql/plan/PlanNode; + public synthetic fun visitRexOpModulo (Lorg/partiql/plan/Rex$Op$Modulo;Ljava/lang/Object;)Ljava/lang/Object; + public fun visitRexOpModulo (Lorg/partiql/plan/Rex$Op$Modulo;Ljava/lang/Object;)Lorg/partiql/plan/PlanNode; + public synthetic fun visitRexOpMultiply (Lorg/partiql/plan/Rex$Op$Multiply;Ljava/lang/Object;)Ljava/lang/Object; + public fun visitRexOpMultiply (Lorg/partiql/plan/Rex$Op$Multiply;Ljava/lang/Object;)Lorg/partiql/plan/PlanNode; + public synthetic fun visitRexOpNegative (Lorg/partiql/plan/Rex$Op$Negative;Ljava/lang/Object;)Ljava/lang/Object; + public fun visitRexOpNegative (Lorg/partiql/plan/Rex$Op$Negative;Ljava/lang/Object;)Lorg/partiql/plan/PlanNode; public synthetic fun visitRexOpNullif (Lorg/partiql/plan/Rex$Op$Nullif;Ljava/lang/Object;)Ljava/lang/Object; public fun visitRexOpNullif (Lorg/partiql/plan/Rex$Op$Nullif;Ljava/lang/Object;)Lorg/partiql/plan/PlanNode; public synthetic fun visitRexOpPathIndex (Lorg/partiql/plan/Rex$Op$Path$Index;Ljava/lang/Object;)Ljava/lang/Object; @@ -2489,6 +2748,8 @@ public abstract class org/partiql/plan/util/PlanRewriter : org/partiql/plan/visi public fun visitRexOpPathSymbol (Lorg/partiql/plan/Rex$Op$Path$Symbol;Ljava/lang/Object;)Lorg/partiql/plan/PlanNode; public synthetic fun visitRexOpPivot (Lorg/partiql/plan/Rex$Op$Pivot;Ljava/lang/Object;)Ljava/lang/Object; public fun visitRexOpPivot (Lorg/partiql/plan/Rex$Op$Pivot;Ljava/lang/Object;)Lorg/partiql/plan/PlanNode; + public synthetic fun visitRexOpPositive (Lorg/partiql/plan/Rex$Op$Positive;Ljava/lang/Object;)Ljava/lang/Object; + public fun visitRexOpPositive (Lorg/partiql/plan/Rex$Op$Positive;Ljava/lang/Object;)Lorg/partiql/plan/PlanNode; public synthetic fun visitRexOpSelect (Lorg/partiql/plan/Rex$Op$Select;Ljava/lang/Object;)Ljava/lang/Object; public fun visitRexOpSelect (Lorg/partiql/plan/Rex$Op$Select;Ljava/lang/Object;)Lorg/partiql/plan/PlanNode; public synthetic fun visitRexOpStruct (Lorg/partiql/plan/Rex$Op$Struct;Ljava/lang/Object;)Ljava/lang/Object; @@ -2497,6 +2758,8 @@ public abstract class org/partiql/plan/util/PlanRewriter : org/partiql/plan/visi public fun visitRexOpStructField (Lorg/partiql/plan/Rex$Op$Struct$Field;Ljava/lang/Object;)Lorg/partiql/plan/PlanNode; public synthetic fun visitRexOpSubquery (Lorg/partiql/plan/Rex$Op$Subquery;Ljava/lang/Object;)Ljava/lang/Object; public fun visitRexOpSubquery (Lorg/partiql/plan/Rex$Op$Subquery;Ljava/lang/Object;)Lorg/partiql/plan/PlanNode; + public synthetic fun visitRexOpSubtract (Lorg/partiql/plan/Rex$Op$Subtract;Ljava/lang/Object;)Ljava/lang/Object; + public fun visitRexOpSubtract (Lorg/partiql/plan/Rex$Op$Subtract;Ljava/lang/Object;)Lorg/partiql/plan/PlanNode; public synthetic fun visitRexOpTupleUnion (Lorg/partiql/plan/Rex$Op$TupleUnion;Ljava/lang/Object;)Ljava/lang/Object; public fun visitRexOpTupleUnion (Lorg/partiql/plan/Rex$Op$TupleUnion;Ljava/lang/Object;)Lorg/partiql/plan/PlanNode; public synthetic fun visitRexOpVar (Lorg/partiql/plan/Rex$Op$Var;Ljava/lang/Object;)Ljava/lang/Object; @@ -2554,6 +2817,7 @@ public abstract class org/partiql/plan/visitor/PlanBaseVisitor : org/partiql/pla public fun visitRelType (Lorg/partiql/plan/Rel$Type;Ljava/lang/Object;)Ljava/lang/Object; public fun visitRex (Lorg/partiql/plan/Rex;Ljava/lang/Object;)Ljava/lang/Object; public fun visitRexOp (Lorg/partiql/plan/Rex$Op;Ljava/lang/Object;)Ljava/lang/Object; + public fun visitRexOpAdd (Lorg/partiql/plan/Rex$Op$Add;Ljava/lang/Object;)Ljava/lang/Object; public fun visitRexOpCall (Lorg/partiql/plan/Rex$Op$Call;Ljava/lang/Object;)Ljava/lang/Object; public fun visitRexOpCallDynamic (Lorg/partiql/plan/Rex$Op$Call$Dynamic;Ljava/lang/Object;)Ljava/lang/Object; public fun visitRexOpCallDynamicCandidate (Lorg/partiql/plan/Rex$Op$Call$Dynamic$Candidate;Ljava/lang/Object;)Ljava/lang/Object; @@ -2563,20 +2827,26 @@ public abstract class org/partiql/plan/visitor/PlanBaseVisitor : org/partiql/pla public fun visitRexOpCast (Lorg/partiql/plan/Rex$Op$Cast;Ljava/lang/Object;)Ljava/lang/Object; public fun visitRexOpCoalesce (Lorg/partiql/plan/Rex$Op$Coalesce;Ljava/lang/Object;)Ljava/lang/Object; public fun visitRexOpCollection (Lorg/partiql/plan/Rex$Op$Collection;Ljava/lang/Object;)Ljava/lang/Object; + public fun visitRexOpDivide (Lorg/partiql/plan/Rex$Op$Divide;Ljava/lang/Object;)Ljava/lang/Object; public fun visitRexOpErr (Lorg/partiql/plan/Rex$Op$Err;Ljava/lang/Object;)Ljava/lang/Object; public fun visitRexOpGlobal (Lorg/partiql/plan/Rex$Op$Global;Ljava/lang/Object;)Ljava/lang/Object; public fun visitRexOpLit (Lorg/partiql/plan/Rex$Op$Lit;Ljava/lang/Object;)Ljava/lang/Object; public fun visitRexOpMissing (Lorg/partiql/plan/Rex$Op$Missing;Ljava/lang/Object;)Ljava/lang/Object; + public fun visitRexOpModulo (Lorg/partiql/plan/Rex$Op$Modulo;Ljava/lang/Object;)Ljava/lang/Object; + public fun visitRexOpMultiply (Lorg/partiql/plan/Rex$Op$Multiply;Ljava/lang/Object;)Ljava/lang/Object; + public fun visitRexOpNegative (Lorg/partiql/plan/Rex$Op$Negative;Ljava/lang/Object;)Ljava/lang/Object; public fun visitRexOpNullif (Lorg/partiql/plan/Rex$Op$Nullif;Ljava/lang/Object;)Ljava/lang/Object; public fun visitRexOpPath (Lorg/partiql/plan/Rex$Op$Path;Ljava/lang/Object;)Ljava/lang/Object; public fun visitRexOpPathIndex (Lorg/partiql/plan/Rex$Op$Path$Index;Ljava/lang/Object;)Ljava/lang/Object; public fun visitRexOpPathKey (Lorg/partiql/plan/Rex$Op$Path$Key;Ljava/lang/Object;)Ljava/lang/Object; public fun visitRexOpPathSymbol (Lorg/partiql/plan/Rex$Op$Path$Symbol;Ljava/lang/Object;)Ljava/lang/Object; public fun visitRexOpPivot (Lorg/partiql/plan/Rex$Op$Pivot;Ljava/lang/Object;)Ljava/lang/Object; + public fun visitRexOpPositive (Lorg/partiql/plan/Rex$Op$Positive;Ljava/lang/Object;)Ljava/lang/Object; public fun visitRexOpSelect (Lorg/partiql/plan/Rex$Op$Select;Ljava/lang/Object;)Ljava/lang/Object; public fun visitRexOpStruct (Lorg/partiql/plan/Rex$Op$Struct;Ljava/lang/Object;)Ljava/lang/Object; public fun visitRexOpStructField (Lorg/partiql/plan/Rex$Op$Struct$Field;Ljava/lang/Object;)Ljava/lang/Object; public fun visitRexOpSubquery (Lorg/partiql/plan/Rex$Op$Subquery;Ljava/lang/Object;)Ljava/lang/Object; + public fun visitRexOpSubtract (Lorg/partiql/plan/Rex$Op$Subtract;Ljava/lang/Object;)Ljava/lang/Object; public fun visitRexOpTupleUnion (Lorg/partiql/plan/Rex$Op$TupleUnion;Ljava/lang/Object;)Ljava/lang/Object; public fun visitRexOpVar (Lorg/partiql/plan/Rex$Op$Var;Ljava/lang/Object;)Ljava/lang/Object; public fun visitStatement (Lorg/partiql/plan/Statement;Ljava/lang/Object;)Ljava/lang/Object; @@ -2629,6 +2899,7 @@ public abstract interface class org/partiql/plan/visitor/PlanVisitor { public abstract fun visitRelType (Lorg/partiql/plan/Rel$Type;Ljava/lang/Object;)Ljava/lang/Object; public abstract fun visitRex (Lorg/partiql/plan/Rex;Ljava/lang/Object;)Ljava/lang/Object; public abstract fun visitRexOp (Lorg/partiql/plan/Rex$Op;Ljava/lang/Object;)Ljava/lang/Object; + public abstract fun visitRexOpAdd (Lorg/partiql/plan/Rex$Op$Add;Ljava/lang/Object;)Ljava/lang/Object; public abstract fun visitRexOpCall (Lorg/partiql/plan/Rex$Op$Call;Ljava/lang/Object;)Ljava/lang/Object; public abstract fun visitRexOpCallDynamic (Lorg/partiql/plan/Rex$Op$Call$Dynamic;Ljava/lang/Object;)Ljava/lang/Object; public abstract fun visitRexOpCallDynamicCandidate (Lorg/partiql/plan/Rex$Op$Call$Dynamic$Candidate;Ljava/lang/Object;)Ljava/lang/Object; @@ -2638,20 +2909,26 @@ public abstract interface class org/partiql/plan/visitor/PlanVisitor { public abstract fun visitRexOpCast (Lorg/partiql/plan/Rex$Op$Cast;Ljava/lang/Object;)Ljava/lang/Object; public abstract fun visitRexOpCoalesce (Lorg/partiql/plan/Rex$Op$Coalesce;Ljava/lang/Object;)Ljava/lang/Object; public abstract fun visitRexOpCollection (Lorg/partiql/plan/Rex$Op$Collection;Ljava/lang/Object;)Ljava/lang/Object; + public abstract fun visitRexOpDivide (Lorg/partiql/plan/Rex$Op$Divide;Ljava/lang/Object;)Ljava/lang/Object; public abstract fun visitRexOpErr (Lorg/partiql/plan/Rex$Op$Err;Ljava/lang/Object;)Ljava/lang/Object; public abstract fun visitRexOpGlobal (Lorg/partiql/plan/Rex$Op$Global;Ljava/lang/Object;)Ljava/lang/Object; public abstract fun visitRexOpLit (Lorg/partiql/plan/Rex$Op$Lit;Ljava/lang/Object;)Ljava/lang/Object; public abstract fun visitRexOpMissing (Lorg/partiql/plan/Rex$Op$Missing;Ljava/lang/Object;)Ljava/lang/Object; + public abstract fun visitRexOpModulo (Lorg/partiql/plan/Rex$Op$Modulo;Ljava/lang/Object;)Ljava/lang/Object; + public abstract fun visitRexOpMultiply (Lorg/partiql/plan/Rex$Op$Multiply;Ljava/lang/Object;)Ljava/lang/Object; + public abstract fun visitRexOpNegative (Lorg/partiql/plan/Rex$Op$Negative;Ljava/lang/Object;)Ljava/lang/Object; public abstract fun visitRexOpNullif (Lorg/partiql/plan/Rex$Op$Nullif;Ljava/lang/Object;)Ljava/lang/Object; public abstract fun visitRexOpPath (Lorg/partiql/plan/Rex$Op$Path;Ljava/lang/Object;)Ljava/lang/Object; public abstract fun visitRexOpPathIndex (Lorg/partiql/plan/Rex$Op$Path$Index;Ljava/lang/Object;)Ljava/lang/Object; public abstract fun visitRexOpPathKey (Lorg/partiql/plan/Rex$Op$Path$Key;Ljava/lang/Object;)Ljava/lang/Object; public abstract fun visitRexOpPathSymbol (Lorg/partiql/plan/Rex$Op$Path$Symbol;Ljava/lang/Object;)Ljava/lang/Object; public abstract fun visitRexOpPivot (Lorg/partiql/plan/Rex$Op$Pivot;Ljava/lang/Object;)Ljava/lang/Object; + public abstract fun visitRexOpPositive (Lorg/partiql/plan/Rex$Op$Positive;Ljava/lang/Object;)Ljava/lang/Object; public abstract fun visitRexOpSelect (Lorg/partiql/plan/Rex$Op$Select;Ljava/lang/Object;)Ljava/lang/Object; public abstract fun visitRexOpStruct (Lorg/partiql/plan/Rex$Op$Struct;Ljava/lang/Object;)Ljava/lang/Object; public abstract fun visitRexOpStructField (Lorg/partiql/plan/Rex$Op$Struct$Field;Ljava/lang/Object;)Ljava/lang/Object; public abstract fun visitRexOpSubquery (Lorg/partiql/plan/Rex$Op$Subquery;Ljava/lang/Object;)Ljava/lang/Object; + public abstract fun visitRexOpSubtract (Lorg/partiql/plan/Rex$Op$Subtract;Ljava/lang/Object;)Ljava/lang/Object; public abstract fun visitRexOpTupleUnion (Lorg/partiql/plan/Rex$Op$TupleUnion;Ljava/lang/Object;)Ljava/lang/Object; public abstract fun visitRexOpVar (Lorg/partiql/plan/Rex$Op$Var;Ljava/lang/Object;)Ljava/lang/Object; public abstract fun visitStatement (Lorg/partiql/plan/Statement;Ljava/lang/Object;)Ljava/lang/Object; diff --git a/partiql-plan/src/main/resources/partiql_plan.ion b/partiql-plan/src/main/resources/partiql_plan.ion index 801234b5b..7c3c29bdb 100644 --- a/partiql-plan/src/main/resources/partiql_plan.ion +++ b/partiql-plan/src/main/resources/partiql_plan.ion @@ -109,6 +109,41 @@ rex::{ arg: rex, }, + // + // START OF SPECIAL OPERATIONS + // The following expressions are SQL/PartiQL operations that cannot be directly represented by + // user-defined functions. Therefore, they are represented as distinct nodes. + // + negative::{ + arg: rex + }, + positive::{ + arg: rex + }, + add::{ + lhs: rex, + rhs: rex + }, + subtract::{ + lhs: rex, + rhs: rex + }, + multiply::{ + lhs: rex, + rhs: rex + }, + divide::{ + lhs: rex, + rhs: rex + }, + modulo::{ + lhs: rex, + rhs: rex + }, + // + // END OF SPECIAL OPERATIONS + // + call::[ static::{ fn: ref, diff --git a/partiql-planner/src/main/kotlin/org/partiql/planner/internal/FnComparator.kt b/partiql-planner/src/main/kotlin/org/partiql/planner/internal/FnComparator.kt index f94f52c10..337afef70 100644 --- a/partiql-planner/src/main/kotlin/org/partiql/planner/internal/FnComparator.kt +++ b/partiql-planner/src/main/kotlin/org/partiql/planner/internal/FnComparator.kt @@ -5,7 +5,6 @@ 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. @@ -13,7 +12,7 @@ import org.partiql.value.PartiQLValueExperimental * 1. Fewest args first * 2. Parameters are compared left-to-right */ -@OptIn(PartiQLValueExperimental::class, FnExperimental::class) +@OptIn(FnExperimental::class) internal object FnComparator : Comparator { override fun compare(fn1: FnSignature, fn2: FnSignature): Int { diff --git a/partiql-planner/src/main/kotlin/org/partiql/planner/internal/FnResolver.kt b/partiql-planner/src/main/kotlin/org/partiql/planner/internal/FnResolver.kt index 33501e4d4..95ba085f2 100644 --- a/partiql-planner/src/main/kotlin/org/partiql/planner/internal/FnResolver.kt +++ b/partiql-planner/src/main/kotlin/org/partiql/planner/internal/FnResolver.kt @@ -48,23 +48,14 @@ internal object FnResolver { } // 2. Discard functions that cannot be matched (via implicit coercion or exact matches) - var matches = match(candidates, args).ifEmpty { return null } - if (matches.size == 1) { - return matches.first().match + val invocableMatches = match(candidates, args).ifEmpty { return null } + if (invocableMatches.size == 1) { + return invocableMatches.first().match } - // 3. Run through all candidates and keep those with the most exact matches on input types. - matches = matchOn(matches) { it.numberOfExactInputTypes } - if (matches.size == 1) { - return matches.first().match - } - - // TODO: Do we care about preferred types? This is a PostgreSQL concept. - // 4. Run through all candidates and keep those that accept preferred types (of the input data type's type category) at the most positions where type conversion will be required. - - // 5. If there are DYNAMIC nodes, return all candidates + // 3. If there are DYNAMIC nodes, return all candidates var isDynamic = false - for (match in matches) { + for (match in invocableMatches) { val params = match.match.signature.parameters for (index in params.indices) { if ((args[index].kind == Kind.DYNAMIC) && params[index].type.kind != Kind.DYNAMIC) { @@ -73,9 +64,18 @@ internal object FnResolver { } } if (isDynamic) { - return FnMatch.Dynamic(matches.map { it.match }) + return FnMatch.Dynamic(invocableMatches.map { it.match }) } + // 4. Run through all candidates and keep those with the most exact matches on input types. + val matches = matchOn(invocableMatches) { it.numberOfExactInputTypes } + if (matches.size == 1) { + return matches.first().match + } + + // TODO: Do we care about preferred types? This is a PostgreSQL concept. + // 5. Run through all candidates and keep those that accept preferred types (of the input data type's type category) at the most positions where type conversion will be required. + // 6. Find the highest precedence one. NOTE: This is a remnant of the previous implementation. Whether we want // to keep this is up to us. return matches.sortedWith(MatchResultComparator).first().match diff --git a/partiql-planner/src/main/kotlin/org/partiql/planner/internal/PlanningProblemDetails.kt b/partiql-planner/src/main/kotlin/org/partiql/planner/internal/PlanningProblemDetails.kt index 252102c2b..ad2bf2f77 100644 --- a/partiql-planner/src/main/kotlin/org/partiql/planner/internal/PlanningProblemDetails.kt +++ b/partiql-planner/src/main/kotlin/org/partiql/planner/internal/PlanningProblemDetails.kt @@ -166,6 +166,13 @@ internal open class PlanningProblemDetails( "Unknown function `$identifier($types)" }) + data class UnknownCast( + val source: PType, + val target: PType, + ) : PlanningProblemDetails(ProblemSeverity.ERROR, { + "Cast does not exist for $source to $target." + }) + public data class UnknownAggregateFunction( val identifier: Identifier, val args: List, diff --git a/partiql-planner/src/main/kotlin/org/partiql/planner/internal/ProblemGenerator.kt b/partiql-planner/src/main/kotlin/org/partiql/planner/internal/ProblemGenerator.kt index 10d2f93d2..cf046f040 100644 --- a/partiql-planner/src/main/kotlin/org/partiql/planner/internal/ProblemGenerator.kt +++ b/partiql-planner/src/main/kotlin/org/partiql/planner/internal/ProblemGenerator.kt @@ -77,6 +77,9 @@ internal object ProblemGenerator { fun undefinedFunction(args: List, identifier: String, location: ProblemLocation = UNKNOWN_PROBLEM_LOCATION): Problem = problem(location, PlanningProblemDetails.UnknownFunction(identifier, args)) + fun undefinedCast(source: PType, target: PType, location: ProblemLocation = UNKNOWN_PROBLEM_LOCATION): Problem = + problem(location, PlanningProblemDetails.UnknownCast(source, target)) + fun undefinedVariable(id: Identifier, inScopeVariables: Set = emptySet(), location: ProblemLocation = UNKNOWN_PROBLEM_LOCATION): Problem = problem(location, PlanningProblemDetails.UndefinedVariable(id, inScopeVariables)) diff --git a/partiql-planner/src/main/kotlin/org/partiql/planner/internal/TypeFamily.kt b/partiql-planner/src/main/kotlin/org/partiql/planner/internal/TypeFamily.kt new file mode 100644 index 000000000..053ed1b1d --- /dev/null +++ b/partiql-planner/src/main/kotlin/org/partiql/planner/internal/TypeFamily.kt @@ -0,0 +1,20 @@ +package org.partiql.planner.internal + +import org.partiql.types.PType + +/** + * This is a utility class to help with planning. + */ +internal object TypeFamily { + val NUMBERS = setOf( + PType.Kind.TINYINT, + PType.Kind.SMALLINT, + PType.Kind.INT, + PType.Kind.BIGINT, + PType.Kind.INT_ARBITRARY, + PType.Kind.DECIMAL, + PType.Kind.DECIMAL_ARBITRARY, + PType.Kind.REAL, + PType.Kind.DOUBLE_PRECISION, + ) +} diff --git a/partiql-planner/src/main/kotlin/org/partiql/planner/internal/TypePrecedence.kt b/partiql-planner/src/main/kotlin/org/partiql/planner/internal/TypePrecedence.kt new file mode 100644 index 000000000..ed9cf7daa --- /dev/null +++ b/partiql-planner/src/main/kotlin/org/partiql/planner/internal/TypePrecedence.kt @@ -0,0 +1,55 @@ +package org.partiql.planner.internal + +import org.partiql.types.PType.Kind + +/** + * Map of the type precedences. + */ +internal object TypePrecedence : Map { + + private val _delegate: Map = listOf( + Kind.BOOL, + Kind.TINYINT, + Kind.SMALLINT, + Kind.INT, + Kind.BIGINT, + Kind.INT_ARBITRARY, + Kind.DECIMAL, + Kind.REAL, + Kind.DOUBLE_PRECISION, + Kind.DECIMAL_ARBITRARY, // Arbitrary precision decimal has a higher precedence than FLOAT + Kind.CHAR, + Kind.VARCHAR, + Kind.SYMBOL, + Kind.STRING, + Kind.CLOB, + Kind.BLOB, + Kind.DATE, + Kind.TIME_WITHOUT_TZ, + Kind.TIME_WITH_TZ, + Kind.TIMESTAMP_WITHOUT_TZ, + Kind.TIMESTAMP_WITH_TZ, + Kind.LIST, + Kind.SEXP, + Kind.BAG, + Kind.ROW, + Kind.STRUCT, + Kind.DYNAMIC, + ).mapIndexed { precedence, type -> type to precedence }.toMap() + + override val entries: Set> = _delegate.entries + + override val keys: Set = _delegate.keys + + override val size: Int = _delegate.size + + override val values: Collection = _delegate.values + + override fun isEmpty(): Boolean = _delegate.isEmpty() + + override fun get(key: Kind): Int? = _delegate.get(key) + + override fun containsValue(value: Int): Boolean = _delegate.containsValue(value) + + override fun containsKey(key: Kind): Boolean = _delegate.containsKey(key) +} diff --git a/partiql-planner/src/main/kotlin/org/partiql/planner/internal/ir/Nodes.kt b/partiql-planner/src/main/kotlin/org/partiql/planner/internal/ir/Nodes.kt index 9860a132f..97b66fe70 100644 --- a/partiql-planner/src/main/kotlin/org/partiql/planner/internal/ir/Nodes.kt +++ b/partiql-planner/src/main/kotlin/org/partiql/planner/internal/ir/Nodes.kt @@ -43,6 +43,7 @@ import org.partiql.planner.internal.ir.builder.RelOpSortSpecBuilder import org.partiql.planner.internal.ir.builder.RelOpUnpivotBuilder import org.partiql.planner.internal.ir.builder.RelTypeBuilder import org.partiql.planner.internal.ir.builder.RexBuilder +import org.partiql.planner.internal.ir.builder.RexOpAddBuilder import org.partiql.planner.internal.ir.builder.RexOpCallDynamicBuilder import org.partiql.planner.internal.ir.builder.RexOpCallDynamicCandidateBuilder import org.partiql.planner.internal.ir.builder.RexOpCallStaticBuilder @@ -53,17 +54,23 @@ import org.partiql.planner.internal.ir.builder.RexOpCastResolvedBuilder import org.partiql.planner.internal.ir.builder.RexOpCastUnresolvedBuilder import org.partiql.planner.internal.ir.builder.RexOpCoalesceBuilder import org.partiql.planner.internal.ir.builder.RexOpCollectionBuilder +import org.partiql.planner.internal.ir.builder.RexOpDivideBuilder import org.partiql.planner.internal.ir.builder.RexOpErrBuilder import org.partiql.planner.internal.ir.builder.RexOpLitBuilder +import org.partiql.planner.internal.ir.builder.RexOpModuloBuilder +import org.partiql.planner.internal.ir.builder.RexOpMultiplyBuilder +import org.partiql.planner.internal.ir.builder.RexOpNegativeBuilder import org.partiql.planner.internal.ir.builder.RexOpNullifBuilder import org.partiql.planner.internal.ir.builder.RexOpPathIndexBuilder import org.partiql.planner.internal.ir.builder.RexOpPathKeyBuilder import org.partiql.planner.internal.ir.builder.RexOpPathSymbolBuilder import org.partiql.planner.internal.ir.builder.RexOpPivotBuilder +import org.partiql.planner.internal.ir.builder.RexOpPositiveBuilder import org.partiql.planner.internal.ir.builder.RexOpSelectBuilder import org.partiql.planner.internal.ir.builder.RexOpStructBuilder import org.partiql.planner.internal.ir.builder.RexOpStructFieldBuilder import org.partiql.planner.internal.ir.builder.RexOpSubqueryBuilder +import org.partiql.planner.internal.ir.builder.RexOpSubtractBuilder import org.partiql.planner.internal.ir.builder.RexOpTupleUnionBuilder import org.partiql.planner.internal.ir.builder.RexOpVarGlobalBuilder import org.partiql.planner.internal.ir.builder.RexOpVarLocalBuilder @@ -288,6 +295,13 @@ internal data class Rex( is TupleUnion -> visitor.visitRexOpTupleUnion(this, ctx) is Err -> visitor.visitRexOpErr(this, ctx) is Missing -> visitor.visitRexOpMissing(this, ctx) + is Add -> visitor.visitRexOpAdd(this, ctx) + is Subtract -> visitor.visitRexOpSubtract(this, ctx) + is Multiply -> visitor.visitRexOpMultiply(this, ctx) + is Divide -> visitor.visitRexOpDivide(this, ctx) + is Negative -> visitor.visitRexOpNegative(this, ctx) + is Positive -> visitor.visitRexOpPositive(this, ctx) + is Modulo -> visitor.visitRexOpModulo(this, ctx) } internal data class Lit( @@ -480,6 +494,142 @@ internal data class Rex( } } + internal data class Negative( + @JvmField internal val arg: Rex, + ) : Op() { + public override val children: List by lazy { + val kids = mutableListOf() + kids.add(arg) + kids.filterNotNull() + } + + public override fun accept(visitor: PlanVisitor, ctx: C): R = + visitor.visitRexOpNegative(this, ctx) + + internal companion object { + @JvmStatic + internal fun builder(): RexOpNegativeBuilder = RexOpNegativeBuilder() + } + } + + internal data class Positive( + @JvmField internal val arg: Rex, + ) : Op() { + public override val children: List by lazy { + val kids = mutableListOf() + kids.add(arg) + kids.filterNotNull() + } + + public override fun accept(visitor: PlanVisitor, ctx: C): R = + visitor.visitRexOpPositive(this, ctx) + + internal companion object { + @JvmStatic + internal fun builder(): RexOpPositiveBuilder = RexOpPositiveBuilder() + } + } + + internal data class Add( + @JvmField internal val lhs: Rex, + @JvmField internal val rhs: Rex, + ) : Op() { + public override val children: List by lazy { + val kids = mutableListOf() + kids.add(lhs) + kids.add(rhs) + kids.filterNotNull() + } + + public override fun accept(visitor: PlanVisitor, ctx: C): R = + visitor.visitRexOpAdd(this, ctx) + + internal companion object { + @JvmStatic + internal fun builder(): RexOpAddBuilder = RexOpAddBuilder() + } + } + + internal data class Subtract( + @JvmField internal val lhs: Rex, + @JvmField internal val rhs: Rex, + ) : Op() { + public override val children: List by lazy { + val kids = mutableListOf() + kids.add(lhs) + kids.add(rhs) + kids.filterNotNull() + } + + public override fun accept(visitor: PlanVisitor, ctx: C): R = + visitor.visitRexOpSubtract(this, ctx) + + internal companion object { + @JvmStatic + internal fun builder(): RexOpSubtractBuilder = RexOpSubtractBuilder() + } + } + + internal data class Multiply( + @JvmField internal val lhs: Rex, + @JvmField internal val rhs: Rex, + ) : Op() { + public override val children: List by lazy { + val kids = mutableListOf() + kids.add(lhs) + kids.add(rhs) + kids.filterNotNull() + } + + public override fun accept(visitor: PlanVisitor, ctx: C): R = + visitor.visitRexOpMultiply(this, ctx) + + internal companion object { + @JvmStatic + internal fun builder(): RexOpMultiplyBuilder = RexOpMultiplyBuilder() + } + } + + internal data class Divide( + @JvmField internal val lhs: Rex, + @JvmField internal val rhs: Rex, + ) : Op() { + public override val children: List by lazy { + val kids = mutableListOf() + kids.add(lhs) + kids.add(rhs) + kids.filterNotNull() + } + + public override fun accept(visitor: PlanVisitor, ctx: C): R = + visitor.visitRexOpDivide(this, ctx) + + internal companion object { + @JvmStatic + internal fun builder(): RexOpDivideBuilder = RexOpDivideBuilder() + } + } + + internal data class Modulo( + @JvmField internal val lhs: Rex, + @JvmField internal val rhs: Rex, + ) : Op() { + public override val children: List by lazy { + val kids = mutableListOf() + kids.add(lhs) + kids.add(rhs) + kids.filterNotNull() + } + + public override fun accept(visitor: PlanVisitor, ctx: C): R = + visitor.visitRexOpModulo(this, ctx) + + internal companion object { + @JvmStatic + internal fun builder(): RexOpModuloBuilder = RexOpModuloBuilder() + } + } + internal sealed class Call : Op() { public override fun accept(visitor: PlanVisitor, ctx: C): R = when (this) { is Unresolved -> visitor.visitRexOpCallUnresolved(this, ctx) diff --git a/partiql-planner/src/main/kotlin/org/partiql/planner/internal/transforms/PlanTransform.kt b/partiql-planner/src/main/kotlin/org/partiql/planner/internal/transforms/PlanTransform.kt index 0d0c7de20..8c9fe0fcc 100644 --- a/partiql-planner/src/main/kotlin/org/partiql/planner/internal/transforms/PlanTransform.kt +++ b/partiql-planner/src/main/kotlin/org/partiql/planner/internal/transforms/PlanTransform.kt @@ -157,11 +157,18 @@ internal class PlanTransform( override fun visitRexOpPath(node: Rex.Op.Path, ctx: Unit) = super.visitRexOpPath(node, ctx) as org.partiql.plan.Rex.Op.Path - override fun visitRexOpCast(node: Rex.Op.Cast, ctx: Unit) = - super.visitRexOpCast(node, ctx) as org.partiql.plan.Rex.Op.Cast - override fun visitRexOpCastUnresolved(node: Rex.Op.Cast.Unresolved, ctx: Unit): PlanNode { - error("Unresolved cast $node") + val problem = ProblemGenerator.undefinedCast(node.arg.type, node.target) + return when (signalMode) { + true -> { + onProblem.invoke(problem) + rexOpErr(problem.toString(), emptyList()) + } + false -> { + onProblem.invoke(ProblemGenerator.asWarning(problem)) + org.partiql.plan.rexOpMissing(problem.toString(), emptyList()) + } + } } override fun visitRexOpCastResolved(node: Rex.Op.Cast.Resolved, ctx: Unit): PlanNode { @@ -198,6 +205,34 @@ internal class PlanTransform( return org.partiql.plan.Rex.Op.Call.Dynamic.Candidate(fn, coercions) } + override fun visitRexOpAdd(node: Rex.Op.Add, ctx: Unit): PlanNode { + return org.partiql.plan.Rex.Op.Add(lhs = visitRex(node.lhs, ctx), rhs = visitRex(node.rhs, ctx)) + } + + override fun visitRexOpSubtract(node: Rex.Op.Subtract, ctx: Unit): PlanNode { + return org.partiql.plan.Rex.Op.Subtract(lhs = visitRex(node.lhs, ctx), rhs = visitRex(node.rhs, ctx)) + } + + override fun visitRexOpMultiply(node: Rex.Op.Multiply, ctx: Unit): PlanNode { + return org.partiql.plan.Rex.Op.Multiply(lhs = visitRex(node.lhs, ctx), rhs = visitRex(node.rhs, ctx)) + } + + override fun visitRexOpDivide(node: Rex.Op.Divide, ctx: Unit): PlanNode { + return org.partiql.plan.Rex.Op.Divide(lhs = visitRex(node.lhs, ctx), rhs = visitRex(node.rhs, ctx)) + } + + override fun visitRexOpModulo(node: Rex.Op.Modulo, ctx: Unit): PlanNode { + return org.partiql.plan.Rex.Op.Modulo(lhs = visitRex(node.lhs, ctx), rhs = visitRex(node.rhs, ctx)) + } + + override fun visitRexOpNegative(node: Rex.Op.Negative, ctx: Unit): PlanNode { + return org.partiql.plan.Rex.Op.Negative(arg = visitRex(node.arg, ctx)) + } + + override fun visitRexOpPositive(node: Rex.Op.Positive, ctx: Unit): PlanNode { + return org.partiql.plan.Rex.Op.Positive(arg = visitRex(node.arg, ctx)) + } + override fun visitRexOpCase(node: Rex.Op.Case, ctx: Unit) = org.partiql.plan.Rex.Op.Case( branches = node.branches.map { visitRexOpCaseBranch(it, ctx) }, default = visitRex(node.default, ctx) ) @@ -255,7 +290,6 @@ internal class PlanTransform( return org.partiql.plan.Rex.Op.Err(node.problem.toString(), trace) } - @OptIn(PartiQLValueExperimental::class) override fun visitRexOpMissing(node: Rex.Op.Missing, ctx: Unit): PlanNode { // gather problem from subtree. val trace = node.causes.map { visitRexOp(it, ctx) } diff --git a/partiql-planner/src/main/kotlin/org/partiql/planner/internal/transforms/RexConverter.kt b/partiql-planner/src/main/kotlin/org/partiql/planner/internal/transforms/RexConverter.kt index 26e6c90a7..92462e9ac 100644 --- a/partiql-planner/src/main/kotlin/org/partiql/planner/internal/transforms/RexConverter.kt +++ b/partiql-planner/src/main/kotlin/org/partiql/planner/internal/transforms/RexConverter.kt @@ -147,21 +147,15 @@ internal object RexConverter { return rex(type, op) } + // TODO move hard-coded operator resolution into SPI private fun resolveUnaryOp(symbol: String, rhs: Expr, context: Env): Rex { val type = (ANY) - // Args val arg = visitExprCoerce(rhs, context) - val args = listOf(arg) - // Fn - val name = when (symbol) { - // TODO move hard-coded operator resolution into SPI - "+" -> "pos" - "-" -> "neg" + return when (symbol) { + "+" -> Rex(type = type, op = Rex.Op.Positive(arg)) + "-" -> Rex(type = type, op = Rex.Op.Negative(arg)) else -> error("unsupported unary op $symbol") } - val id = identifierSymbol(name.lowercase(), Identifier.CaseSensitivity.INSENSITIVE) - val op = rexOpCallUnresolved(id, args) - return rex(type, op) } private fun resolveBinaryOp(lhs: Expr, symbol: String, rhs: Expr, context: Env): Rex { @@ -199,25 +193,20 @@ internal object RexConverter { } } // Wrap if a NOT, if necessary + // TODO eventually move hard-coded operator resolution into SPI return when (symbol) { "<>", "!=" -> { val op = negate(call("eq", *args.toTypedArray())) rex(type, op) } - else -> { + "<", ">", "<=", ">=", "=", "||", "&" -> { val name = when (symbol) { - // TODO eventually move hard-coded operator resolution into SPI "<" -> "lt" ">" -> "gt" "<=" -> "lte" ">=" -> "gte" "=" -> "eq" "||" -> "concat" - "+" -> "plus" - "-" -> "minus" - "*" -> "times" - "/" -> "divide" - "%" -> "modulo" "&" -> "bitwise_and" else -> error("unsupported binary op $symbol") } @@ -225,6 +214,18 @@ internal object RexConverter { val op = rexOpCallUnresolved(id, args) rex(type, op) } + else -> { + val operator: (Rex, Rex) -> Rex.Op = when (symbol) { + "+" -> Rex.Op::Add + "-" -> Rex.Op::Subtract + "*" -> Rex.Op::Multiply + "/" -> Rex.Op::Divide + "%" -> Rex.Op::Modulo + else -> error("unsupported binary op $symbol") + } + val op = operator(args[0], args[1]) + Rex(type, op) + } } } diff --git a/partiql-planner/src/main/kotlin/org/partiql/planner/internal/typer/ArithmeticTyper.kt b/partiql-planner/src/main/kotlin/org/partiql/planner/internal/typer/ArithmeticTyper.kt new file mode 100644 index 000000000..e448c24b1 --- /dev/null +++ b/partiql-planner/src/main/kotlin/org/partiql/planner/internal/typer/ArithmeticTyper.kt @@ -0,0 +1,119 @@ +package org.partiql.planner.internal.typer + +import org.partiql.planner.internal.TypeFamily +import org.partiql.planner.internal.TypePrecedence +import org.partiql.types.PType +import org.partiql.types.PType.Kind +import kotlin.math.max +import kotlin.math.min + +/** + * There exists a mirror copy in the eval package as well. + */ +internal object ArithmeticTyper { + + /** + * Follows SQL-Server for decimals: + * Result Precision = max(s1, s2) + max(p1 - s1, p2 - s2) + 1 + * Result Scale = max(s1, s2) + * @return null if the operation is not allowed on the input types. + */ + internal fun add(lhs: PType, rhs: PType): PType? = arithmeticBinary(lhs, rhs) { lDec, rDec -> + val precision = max(lDec.scale, rDec.scale) + max(lDec.precision - lDec.scale, rDec.precision - rDec.scale) + 1 + val scale = max(lDec.scale, rDec.scale) + precision to scale + } + + /** + * Follows SQL-Server for decimals: + * Result Precision = max(s1, s2) + max(p1 - s1, p2 - s2) + 1 + * Result Scale = max(s1, s2) + * @return null if the operation is not allowed on the input types. + */ + internal fun subtract(lhs: PType, rhs: PType): PType? = arithmeticBinary(lhs, rhs) { lDec, rDec -> + val precision = max(lhs.scale, rhs.scale) + max(lhs.precision - lhs.scale, rhs.precision - rhs.scale) + 1 + val scale = max(lhs.scale, rhs.scale) + precision to scale + } + + /** + * Follows SQL-Server for decimals: + * Result Precision = p1 - s1 + s2 + max(6, s1 + p2 + 1) + * Result Scale = max(6, s1 + p2 + 1) + * @return null if the operation is not allowed on the input types. + */ + internal fun divide(lhs: PType, rhs: PType): PType? = arithmeticBinary(lhs, rhs) { lDec, rDec -> + val precision = lhs.precision - rhs.scale + lhs.scale + max(6, lhs.scale + rhs.precision + 1) + val scale = max(6, lhs.scale + rhs.precision + 1) + precision to scale + } + + /** + * Follows SQL-Server for decimals: + * Result Precision = p1 + p2 + 1 + * Result Scale = s1 + s2 + * @return null if the operation is not allowed on the input types. + */ + internal fun multiply(lhs: PType, rhs: PType): PType? = arithmeticBinary(lhs, rhs) { lDec, rDec -> + val precision = lhs.precision + rhs.precision + 1 + val scale = lhs.scale + rhs.scale + precision to scale + } + + /** + * Follows SQL-Server for decimals: + * Result Precision: min(p1 - s1, p2 - s2) + max(s1, s2) + * Result Scale: max(s1, s2) + * @return null if the operation is not allowed on the input types. + */ + internal fun modulo(lhs: PType, rhs: PType): PType? = arithmeticBinary(lhs, rhs) { lDec, rDec -> + val precision = min(lhs.precision - lhs.scale, rhs.precision - rhs.scale) + max(lhs.scale, rhs.scale) + val scale = max(lhs.scale, rhs.scale) + precision to scale + } + + internal fun negative(arg: PType): PType? = arithmeticUnary(arg) + + internal fun positive(arg: PType): PType? = arithmeticUnary(arg) + + private fun arithmeticUnary(arg: PType): PType? { + val argMayBeNumber = arg.kind == Kind.DYNAMIC || TypeFamily.NUMBERS.contains(arg.kind) + return when (argMayBeNumber) { + true -> arg + false -> null + } + } + + private fun arithmeticBinary( + lhs: PType, + rhs: PType, + handleDecimal: (PType, PType) -> Pair + ): PType? { + val lhsCannotBeNumber = lhs.kind != Kind.DYNAMIC && !TypeFamily.NUMBERS.contains(lhs.kind) + val rhsCannotBeNumber = rhs.kind != Kind.DYNAMIC && !TypeFamily.NUMBERS.contains(rhs.kind) + if (lhsCannotBeNumber || rhsCannotBeNumber) { + return null + } + if (lhs.kind == Kind.DYNAMIC || rhs.kind == Kind.DYNAMIC) { + return PType.typeDynamic() + } + val lhsPrecedence = TypePrecedence[lhs.kind]!! + val rhsPrecedence = TypePrecedence[rhs.kind]!! + val comp = lhsPrecedence.compareTo(rhsPrecedence) + when (comp) { + -1 -> return rhs + 1 -> return lhs + 0 -> if (lhs.kind != Kind.DECIMAL) { + return lhs + } + + else -> error("This shouldn't have occurred.") + } + val (precision, scale) = handleDecimal(lhs, rhs) + // TODO: Check if this is what we want + return when (precision > 38 || scale > 38) { + true -> PType.typeDecimalArbitrary() + false -> PType.typeDecimal(precision, scale) + } + } +} diff --git a/partiql-planner/src/main/kotlin/org/partiql/planner/internal/typer/PlanTyper.kt b/partiql-planner/src/main/kotlin/org/partiql/planner/internal/typer/PlanTyper.kt index 976f60829..5d14b058d 100644 --- a/partiql-planner/src/main/kotlin/org/partiql/planner/internal/typer/PlanTyper.kt +++ b/partiql-planner/src/main/kotlin/org/partiql/planner/internal/typer/PlanTyper.kt @@ -64,7 +64,6 @@ import org.partiql.value.MissingValue import org.partiql.value.PartiQLValueExperimental import org.partiql.value.TextValue import org.partiql.value.stringValue -import java.lang.reflect.Type import kotlin.math.max /** @@ -793,7 +792,7 @@ internal class PlanTyper(private val env: Env) { override fun visitRexOpCastUnresolved(node: Rex.Op.Cast.Unresolved, ctx: CompilerType?): Rex { val arg = visitRex(node.arg, null) - val cast = env.resolveCast(arg, node.target) ?: return ProblemGenerator.errorRex( + val cast = env.resolveCast(arg, node.target) ?: return ProblemGenerator.missingRex( node.copy(node.target, arg), ProblemGenerator.undefinedFunction(listOf(arg.type), "CAST( AS ${node.target})") ) @@ -1087,6 +1086,61 @@ internal class PlanTyper(private val env: Env) { } } + override fun visitRexOpAdd(node: Rex.Op.Add, ctx: CompilerType?): Rex { + return visitArithmeticBinary(node.lhs, node.rhs, "PLUS", Rex.Op::Add, ArithmeticTyper::add) + } + + override fun visitRexOpSubtract(node: Rex.Op.Subtract, ctx: CompilerType?): PlanNode { + return visitArithmeticBinary(node.lhs, node.rhs, "MINUS", Rex.Op::Subtract, ArithmeticTyper::subtract) + } + + override fun visitRexOpMultiply(node: Rex.Op.Multiply, ctx: CompilerType?): PlanNode { + return visitArithmeticBinary(node.lhs, node.rhs, "MULTIPLY", Rex.Op::Multiply, ArithmeticTyper::multiply) + } + + override fun visitRexOpDivide(node: Rex.Op.Divide, ctx: CompilerType?): PlanNode { + return visitArithmeticBinary(node.lhs, node.rhs, "DIVIDE", Rex.Op::Divide, ArithmeticTyper::divide) + } + + override fun visitRexOpModulo(node: Rex.Op.Modulo, ctx: CompilerType?): PlanNode { + return visitArithmeticBinary(node.lhs, node.rhs, "MODULO", Rex.Op::Modulo, ArithmeticTyper::modulo) + } + + override fun visitRexOpNegative(node: Rex.Op.Negative, ctx: CompilerType?): Rex { + return visitArithmeticUnary(node.arg, "NEG", Rex.Op::Negative, ArithmeticTyper::negative) + } + + override fun visitRexOpPositive(node: Rex.Op.Positive, ctx: CompilerType?): Rex { + return visitArithmeticUnary(node.arg, "POS", Rex.Op::Positive, ArithmeticTyper::positive) + } + + private fun visitArithmeticUnary(arg: Rex, op: String, opInit: (Rex) -> Rex.Op, type: (PType) -> PType?): Rex { + val value = visitRex(arg, arg.type) + val vType = value.type + if (vType.kind == Kind.DYNAMIC) { + return Rex(PType.typeDynamic().toCType(), opInit(value)) + } + val returns = type(vType) ?: return ProblemGenerator.missingRex( + causes = listOf(value.op), + problem = ProblemGenerator.incompatibleTypesForOp(op, listOf(vType)) + ) + return Rex(type = returns.toCType(), op = opInit(value)) + } + + private fun visitArithmeticBinary(lhs: Rex, rhs: Rex, op: String, opInit: (Rex, Rex) -> Rex.Op, type: (PType, PType) -> PType?): Rex { + val lhsVisited = visitRex(lhs, lhs.type) + val rhsVisited = visitRex(rhs, rhs.type) + val opVisited = opInit(lhsVisited, rhsVisited) + if (lhsVisited.type.kind == Kind.DYNAMIC || rhsVisited.type.kind == Kind.DYNAMIC) { + return Rex(PType.typeDynamic().toCType(), opVisited) + } + val typeVisited = type.invoke(lhsVisited.type, rhsVisited.type)?.toCType() ?: return ProblemGenerator.missingRex( + causes = listOf(lhsVisited.op, rhsVisited.op), + problem = ProblemGenerator.incompatibleTypesForOp(op, listOf(lhsVisited.type, rhsVisited.type)) + ) + return Rex(type = typeVisited.toCType(), op = opVisited) + } + /** * Calculate output type of a row-value subquery. */ diff --git a/partiql-planner/src/main/resources/partiql_plan_internal.ion b/partiql-planner/src/main/resources/partiql_plan_internal.ion index 0c1cfdde1..03fa4d25d 100644 --- a/partiql-planner/src/main/resources/partiql_plan_internal.ion +++ b/partiql-planner/src/main/resources/partiql_plan_internal.ion @@ -128,6 +128,41 @@ rex::{ } ], + // + // START OF SPECIAL OPERATIONS + // The following expressions are SQL/PartiQL operations that cannot be directly represented by + // user-defined functions. Therefore, they are represented as distinct nodes. + // + negative::{ + arg: rex + }, + positive::{ + arg: rex + }, + add::{ + lhs: rex, + rhs: rex + }, + subtract::{ + lhs: rex, + rhs: rex + }, + multiply::{ + lhs: rex, + rhs: rex + }, + divide::{ + lhs: rex, + rhs: rex + }, + modulo::{ + lhs: rex, + rhs: rex + }, + // + // END OF SPECIAL OPERATIONS + // + call::[ unresolved::{ diff --git a/partiql-planner/src/test/kotlin/org/partiql/planner/PlannerErrorReportingTests.kt b/partiql-planner/src/test/kotlin/org/partiql/planner/PlannerErrorReportingTests.kt index e97c546a9..19689be38 100644 --- a/partiql-planner/src/test/kotlin/org/partiql/planner/PlannerErrorReportingTests.kt +++ b/partiql-planner/src/test/kotlin/org/partiql/planner/PlannerErrorReportingTests.kt @@ -147,7 +147,7 @@ internal class PlannerErrorReportingTests { "1 + MISSING", false, assertOnProblemCount(1, 0), - expectedType = PType.typeInt().toCType() + expectedType = StaticType.ANY ), // This will be a non-resolved function error. // As plus does not contain a function that match argument type with @@ -157,7 +157,7 @@ internal class PlannerErrorReportingTests { "1 + MISSING", true, assertOnProblemCount(0, 1), - expectedType = PType.typeInt().toCType() + expectedType = StaticType.ANY ), // Attempting to do path navigation(symbol) on missing(which is not tuple) // returns missing in quite mode, and error out in signal mode @@ -279,13 +279,13 @@ internal class PlannerErrorReportingTests { "1 + not_a_function(1)", false, assertOnProblemCount(0, 1), - StaticType.INT4, + StaticType.ANY, ), TestCase( "1 + not_a_function(1)", true, assertOnProblemCount(0, 1), - StaticType.INT4, + StaticType.ANY, ), TestCase( diff --git a/partiql-planner/src/test/kotlin/org/partiql/planner/internal/typer/PlanTyperTestsPorted.kt b/partiql-planner/src/test/kotlin/org/partiql/planner/internal/typer/PlanTyperTestsPorted.kt index 67077fc7a..af50dc07e 100644 --- a/partiql-planner/src/test/kotlin/org/partiql/planner/internal/typer/PlanTyperTestsPorted.kt +++ b/partiql-planner/src/test/kotlin/org/partiql/planner/internal/typer/PlanTyperTestsPorted.kt @@ -3422,9 +3422,9 @@ internal class PlanTyperTestsPorted { query = """ +MISSING """.trimIndent(), - expected = StaticType.DECIMAL, // This is due to it being the highest precedence type + expected = StaticType.ANY, // This is due to it being the highest precedence type problemHandler = assertProblemExists( - ProblemGenerator.expressionAlwaysReturnsMissing("Static function always receives MISSING arguments.") + ProblemGenerator.incompatibleTypesForOp("POS", listOf(PType.typeUnknown())) ) ), )