diff --git a/partiql-plan/api/partiql-plan.api b/partiql-plan/api/partiql-plan.api index d46203ced0..2ca8b4d204 100644 --- a/partiql-plan/api/partiql-plan.api +++ b/partiql-plan/api/partiql-plan.api @@ -210,7 +210,7 @@ public final class org/partiql/plan/Plan { public static final fun rexOpCollection (Ljava/util/List;)Lorg/partiql/plan/Rex$Op$Collection; 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 rexOpLit (Lorg/partiql/spi/value/Datum;)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 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; @@ -1143,13 +1143,13 @@ public final class org/partiql/plan/Rex$Op$Global$Companion { public final class org/partiql/plan/Rex$Op$Lit : org/partiql/plan/Rex$Op { public static final field Companion Lorg/partiql/plan/Rex$Op$Lit$Companion; - public final field value Lorg/partiql/value/PartiQLValue; - public fun (Lorg/partiql/value/PartiQLValue;)V + public final field value Lorg/partiql/spi/value/Datum; + public fun (Lorg/partiql/spi/value/Datum;)V public fun accept (Lorg/partiql/plan/visitor/PlanVisitor;Ljava/lang/Object;)Ljava/lang/Object; public static final fun builder ()Lorg/partiql/plan/builder/RexOpLitBuilder; - public final fun component1 ()Lorg/partiql/value/PartiQLValue; - public final fun copy (Lorg/partiql/value/PartiQLValue;)Lorg/partiql/plan/Rex$Op$Lit; - public static synthetic fun copy$default (Lorg/partiql/plan/Rex$Op$Lit;Lorg/partiql/value/PartiQLValue;ILjava/lang/Object;)Lorg/partiql/plan/Rex$Op$Lit; + public final fun component1 ()Lorg/partiql/spi/value/Datum; + public final fun copy (Lorg/partiql/spi/value/Datum;)Lorg/partiql/plan/Rex$Op$Lit; + public static synthetic fun copy$default (Lorg/partiql/plan/Rex$Op$Lit;Lorg/partiql/spi/value/Datum;ILjava/lang/Object;)Lorg/partiql/plan/Rex$Op$Lit; public fun equals (Ljava/lang/Object;)Z public fun getChildren ()Ljava/util/List; public fun hashCode ()I @@ -1644,8 +1644,8 @@ public final class org/partiql/plan/builder/PlanBuilder { 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; public static synthetic fun rexOpGlobal$default (Lorg/partiql/plan/builder/PlanBuilder;Lorg/partiql/plan/Ref;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lorg/partiql/plan/Rex$Op$Global; - public final fun rexOpLit (Lorg/partiql/value/PartiQLValue;Lkotlin/jvm/functions/Function1;)Lorg/partiql/plan/Rex$Op$Lit; - 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 rexOpLit (Lorg/partiql/spi/value/Datum;Lkotlin/jvm/functions/Function1;)Lorg/partiql/plan/Rex$Op$Lit; + public static synthetic fun rexOpLit$default (Lorg/partiql/plan/builder/PlanBuilder;Lorg/partiql/spi/value/Datum;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 rexOpNullif (Lorg/partiql/plan/Rex;Lorg/partiql/plan/Rex;Lkotlin/jvm/functions/Function1;)Lorg/partiql/plan/Rex$Op$Nullif; @@ -2191,12 +2191,12 @@ public final class org/partiql/plan/builder/RexOpGlobalBuilder { public final class org/partiql/plan/builder/RexOpLitBuilder { public fun ()V - public fun (Lorg/partiql/value/PartiQLValue;)V - public synthetic fun (Lorg/partiql/value/PartiQLValue;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun (Lorg/partiql/spi/value/Datum;)V + public synthetic fun (Lorg/partiql/spi/value/Datum;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public final fun build ()Lorg/partiql/plan/Rex$Op$Lit; - public final fun getValue ()Lorg/partiql/value/PartiQLValue; - public final fun setValue (Lorg/partiql/value/PartiQLValue;)V - public final fun value (Lorg/partiql/value/PartiQLValue;)Lorg/partiql/plan/builder/RexOpLitBuilder; + public final fun getValue ()Lorg/partiql/spi/value/Datum; + public final fun setValue (Lorg/partiql/spi/value/Datum;)V + public final fun value (Lorg/partiql/spi/value/Datum;)Lorg/partiql/plan/builder/RexOpLitBuilder; } public final class org/partiql/plan/builder/RexOpMissingBuilder { diff --git a/partiql-plan/build.gradle.kts b/partiql-plan/build.gradle.kts index 180327261f..580ef5b92c 100644 --- a/partiql-plan/build.gradle.kts +++ b/partiql-plan/build.gradle.kts @@ -63,7 +63,6 @@ val generate = tasks.register("generate") { "--poems", "visitor", "--poems", "builder", "--poems", "util", - "--opt-in", "org.partiql.value.PartiQLValueExperimental", "./src/main/resources/partiql_plan.ion" ) } diff --git a/partiql-plan/src/main/resources/partiql_plan.ion b/partiql-plan/src/main/resources/partiql_plan.ion index 19414e918f..3d6caac4ce 100644 --- a/partiql-plan/src/main/resources/partiql_plan.ion +++ b/partiql-plan/src/main/resources/partiql_plan.ion @@ -1,6 +1,6 @@ imports::{ kotlin: [ - partiql_value::'org.partiql.value.PartiQLValue', + partiql_value::'org.partiql.spi.value.Datum', partiql_value_type::'org.partiql.types.PType', static_type::'org.partiql.types.PType', function::'org.partiql.spi.fn.Function', 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 428588d38e..a097cb317f 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 @@ -72,7 +72,7 @@ import org.partiql.spi.catalog.Name import org.partiql.spi.catalog.Table import org.partiql.spi.fn.Aggregation import org.partiql.spi.fn.Function -import org.partiql.value.PartiQLValue +import org.partiql.spi.value.Datum import org.partiql.value.PartiQLValueExperimental import kotlin.random.Random @@ -234,7 +234,7 @@ internal data class Rex( } internal data class Lit( - @JvmField internal val `value`: PartiQLValue, + @JvmField internal val `value`: Datum, ) : Op() { public override val children: List = emptyList() 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 2da241825f..2d0c88c2d5 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 @@ -99,7 +99,6 @@ internal class PlanTransform( override fun visitRexOp(node: Rex.Op, ctx: Unit) = super.visitRexOp(node, ctx) as org.partiql.plan.Rex.Op - @OptIn(PartiQLValueExperimental::class) override fun visitRexOpLit(node: Rex.Op.Lit, ctx: Unit) = org.partiql.plan.rexOpLit(node.value) override fun visitRexOpVar(node: Rex.Op.Var, ctx: Unit) = diff --git a/partiql-planner/src/main/kotlin/org/partiql/planner/internal/transforms/PlanTransformV1.kt b/partiql-planner/src/main/kotlin/org/partiql/planner/internal/transforms/PlanTransformV1.kt index 99012e03f5..abd17b07e8 100644 --- a/partiql-planner/src/main/kotlin/org/partiql/planner/internal/transforms/PlanTransformV1.kt +++ b/partiql-planner/src/main/kotlin/org/partiql/planner/internal/transforms/PlanTransformV1.kt @@ -21,10 +21,8 @@ import org.partiql.planner.internal.PlannerFlag import org.partiql.planner.internal.ProblemGenerator import org.partiql.planner.internal.ir.SetQuantifier import org.partiql.planner.internal.ir.visitor.PlanBaseVisitor -import org.partiql.spi.value.Datum import org.partiql.types.Field import org.partiql.types.PType -import org.partiql.value.PartiQLValueExperimental import org.partiql.planner.internal.ir.PartiQLPlan as IPlan import org.partiql.planner.internal.ir.PlanNode as INode import org.partiql.planner.internal.ir.Rel as IRel @@ -244,9 +242,8 @@ internal class PlanTransformV1(private val flags: Set) { return factory.rexVar(depth = node.depth, offset = node.ref) } - @OptIn(PartiQLValueExperimental::class) override fun visitRexOpLit(node: IRex.Op.Lit, ctx: PType): Any { - return factory.rexLit(Datum.of(node.value)) + return factory.rexLit(node.value) } // RELATION OPERATORS diff --git a/partiql-planner/src/main/kotlin/org/partiql/planner/internal/transforms/RelConverter.kt b/partiql-planner/src/main/kotlin/org/partiql/planner/internal/transforms/RelConverter.kt index 6579a873fa..36ac5360a7 100644 --- a/partiql-planner/src/main/kotlin/org/partiql/planner/internal/transforms/RelConverter.kt +++ b/partiql-planner/src/main/kotlin/org/partiql/planner/internal/transforms/RelConverter.kt @@ -71,11 +71,10 @@ import org.partiql.planner.internal.ir.rexOpStruct import org.partiql.planner.internal.ir.rexOpStructField import org.partiql.planner.internal.ir.rexOpVarLocal import org.partiql.planner.internal.typer.CompilerType +import org.partiql.spi.value.Datum import org.partiql.types.PType import org.partiql.value.PartiQLValueExperimental -import org.partiql.value.boolValue import org.partiql.value.int32Value -import org.partiql.value.stringValue /** * Lexically scoped state for use in translating an individual SELECT statement. @@ -276,13 +275,12 @@ internal object RelConverter { * * TODO compute basic schema */ - @OptIn(PartiQLValueExperimental::class) override fun visitFromJoin(node: From.Join, nil: Rel): Rel { val lhs = visitFrom(node.lhs, nil) val rhs = visitFrom(node.rhs, nil) val schema = lhs.type.schema + rhs.type.schema // Note: This gets more specific in PlanTyper. It is only used to find binding names here. val props = emptySet() - val condition = node.condition?.let { RexConverter.apply(it, env) } ?: rex(BOOL, rexOpLit(boolValue(true))) + val condition = node.condition?.let { RexConverter.apply(it, env) } ?: rex(BOOL, rexOpLit(Datum.bool(true))) val joinType = when (node.type) { From.Join.Type.LEFT_OUTER, From.Join.Type.LEFT -> Rel.Op.Join.Type.LEFT From.Join.Type.RIGHT_OUTER, From.Join.Type.RIGHT -> Rel.Op.Join.Type.RIGHT @@ -423,7 +421,7 @@ internal object RelConverter { schema.add(binding) val fields = input.type.schema.mapIndexed { bindingIndex, currBinding -> rexOpStructField( - k = rex(STRING, rexOpLit(stringValue(currBinding.name))), + k = rex(STRING, rexOpLit(Datum.string(currBinding.name))), v = rex(ANY, rexOpVarLocal(0, bindingIndex)) ) } 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 977f19d22e..ed268e646b 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 @@ -16,7 +16,6 @@ package org.partiql.planner.internal.transforms -import com.amazon.ionelement.api.loadSingleElement import org.partiql.ast.AstNode import org.partiql.ast.DatetimeField import org.partiql.ast.Expr @@ -55,16 +54,12 @@ import org.partiql.planner.internal.ir.rexOpVarUnresolved import org.partiql.planner.internal.typer.CompilerType import org.partiql.planner.internal.typer.PlanTyper.Companion.toCType import org.partiql.spi.catalog.Identifier +import org.partiql.spi.value.Datum +import org.partiql.spi.value.ion.IonDatum import org.partiql.types.PType import org.partiql.value.MissingValue import org.partiql.value.PartiQLValueExperimental import org.partiql.value.StringValue -import org.partiql.value.boolValue -import org.partiql.value.int32Value -import org.partiql.value.int64Value -import org.partiql.value.io.PartiQLValueIonReaderBuilder -import org.partiql.value.nullValue -import org.partiql.value.stringValue import org.partiql.ast.Identifier as AstIdentifier /** @@ -76,7 +71,6 @@ internal object RexConverter { internal fun applyRel(expr: Expr, context: Env): Rex = expr.accept(ToRex, context) - @OptIn(PartiQLValueExperimental::class) @Suppress("PARAMETER_NAME_CHANGED_ON_OVERRIDE") private object ToRex : AstBaseVisitor() { @@ -94,26 +88,29 @@ internal object RexConverter { override fun defaultReturn(node: AstNode, context: Env): Rex = throw IllegalArgumentException("unsupported rex $node") + /** + * TODO REMOVE PartiQLValue from AST – https://github.com/partiql/partiql-lang-kotlin/issues/1589 + */ + @OptIn(PartiQLValueExperimental::class) override fun visitExprLit(node: Expr.Lit, context: Env): Rex { val type = CompilerType( _delegate = node.value.type.toPType(), isNullValue = node.value.isNull, isMissingValue = node.value is MissingValue ) - val op = rexOpLit(node.value) + val op = rexOpLit(Datum.of(node.value)) return rex(type, op) } /** - * TODO PartiQLValue will be replaced by Datum (i.e. IonDatum) is a subsequent PR. + * TODO add registration of variant types to catalog functionality. */ override fun visitExprVariant(node: Expr.Variant, ctx: Env): Rex { - if (node.encoding != "ion") { - throw IllegalArgumentException("unsupported encoding ${node.encoding}") + val value = when (node.encoding.lowercase()) { + "ion" -> IonDatum.of(node.value) + else -> throw IllegalArgumentException("unsupported variant encoding ${node.encoding}") } - val ion = loadSingleElement(node.value) - val value = PartiQLValueIonReaderBuilder.standard().build(ion).read() - val type = CompilerType(value.type.toPType()) + val type = CompilerType(value.type) return rex(type, rexOpLit(value)) } @@ -299,6 +296,7 @@ internal object RexConverter { } } + @OptIn(PartiQLValueExperimental::class) override fun visitExprPath(node: Expr.Path, context: Env): Rex { // Args val root = visitExprCoerce(node.root, context) @@ -416,7 +414,7 @@ internal object RexConverter { val schema = acc.type.schema + scan.type.schema val props = emptySet() val type = relType(schema, props) - rel(type, relOpJoin(acc, scan, rex(BOOL, rexOpLit(boolValue(true))), Rel.Op.Join.Type.INNER)) + rel(type, relOpJoin(acc, scan, rex(BOOL, rexOpLit(Datum.bool(true))), Rel.Op.Join.Type.INNER)) } // compute the ref used by select construct @@ -472,7 +470,7 @@ internal object RexConverter { return rel(relType, relOpUnpivot(path)) } - private fun rexString(str: String) = rex(STRING, rexOpLit(stringValue(str))) + private fun rexString(str: String) = rex(STRING, rexOpLit(Datum.string(str))) override fun visitExprCall(node: Expr.Call, context: Env): Rex { val type = (ANY) @@ -580,7 +578,7 @@ internal object RexConverter { }.toMutableList() val defaultRex = when (val default = node.default) { - null -> rex(type = ANY, op = rexOpLit(value = nullValue())) + null -> rex(type = ANY, op = rexOpLit(value = Datum.nullValue())) else -> visitExprCoerce(default, context) } val op = rexOpCase(branches = branches, default = defaultRex) @@ -758,7 +756,7 @@ internal object RexConverter { val type = ANY // Args val arg0 = visitExprCoerce(node.value, ctx) - val arg1 = node.start?.let { visitExprCoerce(it, ctx) } ?: rex(INT, rexOpLit(int64Value(1))) + val arg1 = node.start?.let { visitExprCoerce(it, ctx) } ?: rex(INT, rexOpLit(Datum.bigint(1))) val arg2 = node.length?.let { visitExprCoerce(it, ctx) } // Call Variants val call = when (arg2) { @@ -836,8 +834,8 @@ internal object RexConverter { call( "substring", cv, - rex(INT4, rexOpLit(int32Value(1))), - rex(ANY, call("minus", sp, rex(INT4, rexOpLit(int32Value(1))))) + rex(INT4, rexOpLit(Datum.integer(1))), + rex(ANY, call("minus", sp, rex(INT4, rexOpLit(Datum.integer(1))))) ) ) val p2 = rex(ANY, call("concat", p1, rs)) @@ -968,7 +966,14 @@ internal object RexConverter { return rexOpCallUnresolved(id, args.toList()) } - private fun Int?.toRex() = rex(INT4, rexOpLit(int32Value(this))) + private fun Int?.toRex(): Rex { + val datum = if (this == null) { + Datum.nullValue(PType.integer()) + } else { + Datum.integer(this) + } + return rex(INT4, rexOpLit(datum)) + } private val ANY: CompilerType = CompilerType(PType.dynamic()) private val BOOL: CompilerType = CompilerType(PType.bool()) diff --git a/partiql-planner/src/main/kotlin/org/partiql/planner/internal/typer/DynamicTyper.kt b/partiql-planner/src/main/kotlin/org/partiql/planner/internal/typer/DynamicTyper.kt index 229999d540..1ca72f36ec 100644 --- a/partiql-planner/src/main/kotlin/org/partiql/planner/internal/typer/DynamicTyper.kt +++ b/partiql-planner/src/main/kotlin/org/partiql/planner/internal/typer/DynamicTyper.kt @@ -3,34 +3,9 @@ package org.partiql.planner.internal.typer import org.partiql.planner.internal.ir.Rex import org.partiql.planner.internal.typer.PlanTyper.Companion.anyOf import org.partiql.planner.internal.typer.PlanTyper.Companion.toCType +import org.partiql.spi.value.Datum import org.partiql.types.PType import org.partiql.types.PType.Kind -import org.partiql.value.MissingValue -import org.partiql.value.PartiQLValue -import org.partiql.value.PartiQLValueExperimental -import org.partiql.value.bagValue -import org.partiql.value.blobValue -import org.partiql.value.boolValue -import org.partiql.value.charValue -import org.partiql.value.clobValue -import org.partiql.value.dateValue -import org.partiql.value.decimalValue -import org.partiql.value.float32Value -import org.partiql.value.float64Value -import org.partiql.value.int16Value -import org.partiql.value.int32Value -import org.partiql.value.int64Value -import org.partiql.value.int8Value -import org.partiql.value.intValue -import org.partiql.value.listValue -import org.partiql.value.missingValue -import org.partiql.value.nullValue -import org.partiql.value.sexpValue -import org.partiql.value.stringValue -import org.partiql.value.structValue -import org.partiql.value.symbolValue -import org.partiql.value.timeValue -import org.partiql.value.timestampValue /** * Graph of super types for quick lookup because we don't have a tree. @@ -66,7 +41,6 @@ internal class DynamicTyper { /** * Checks for literal NULL */ - @OptIn(PartiQLValueExperimental::class) private fun Rex.isLiteralNull(): Boolean { val op = this.op return op is Rex.Op.Lit && op.value.isNull @@ -75,10 +49,9 @@ internal class DynamicTyper { /** * Checks for literal MISSING */ - @OptIn(PartiQLValueExperimental::class) private fun Rex.isLiteralMissing(): Boolean { val op = this.op - return op is Rex.Op.Lit && op.value is MissingValue + return op is Rex.Op.Lit && op.value.isMissing } /** @@ -107,7 +80,6 @@ internal class DynamicTyper { * * @return */ - @OptIn(PartiQLValueExperimental::class) fun mapping(): Pair?> { var s = supertype ?: return CompilerType(PType.dynamic()) to null val superTypeBase = s.kind @@ -137,8 +109,8 @@ internal class DynamicTyper { // Otherwise, return the supertype along with the coercion mapping val mapping = args.map { when { - it.isLiteralNull() -> Mapping.Replacement(Rex(s, Rex.Op.Lit(nullValue(s.kind)))) - it.isLiteralMissing() -> Mapping.Replacement(Rex(s, Rex.Op.Lit(missingValue()))) + it.isLiteralNull() -> Mapping.Replacement(Rex(s, Rex.Op.Lit(Datum.nullValue(s)))) + it.isLiteralMissing() -> Mapping.Replacement(Rex(s, Rex.Op.Lit(Datum.missing()))) it.type == s -> Mapping.Coercion(s) else -> null } @@ -219,7 +191,7 @@ internal class DynamicTyper { companion object { @JvmStatic - private val N = Kind.values().size + private val N = Kind.entries.size @JvmStatic private fun edges(vararg edges: Pair): Array { @@ -456,40 +428,7 @@ internal class DynamicTyper { Kind.SEXP -> PType.sexp() // TODO: To be updated Kind.STRUCT -> PType.struct() // TODO: To be updated Kind.UNKNOWN -> PType.unknown() // TODO: To be updated + Kind.VARIANT -> PType.dynamic() }.toCType() - - @OptIn(PartiQLValueExperimental::class) - private fun nullValue(kind: Kind): PartiQLValue { - return when (kind) { - Kind.DYNAMIC -> nullValue() - Kind.BOOL -> boolValue(null) - Kind.TINYINT -> int8Value(null) - Kind.SMALLINT -> int16Value(null) - Kind.INTEGER -> int32Value(null) - Kind.BIGINT -> int64Value(null) - Kind.NUMERIC -> intValue(null) - Kind.DECIMAL -> decimalValue(null) - Kind.DECIMAL_ARBITRARY -> decimalValue(null) - Kind.REAL -> float32Value(null) - Kind.DOUBLE -> float64Value(null) - Kind.CHAR -> charValue(null) - Kind.VARCHAR -> TODO("No implementation of VAR CHAR") - Kind.STRING -> stringValue(null) - Kind.SYMBOL -> symbolValue(null) - Kind.BLOB -> blobValue(null) - Kind.CLOB -> clobValue(null) - Kind.DATE -> dateValue(null) - Kind.TIMEZ, - Kind.TIME -> timeValue(null) - Kind.TIMESTAMPZ, - Kind.TIMESTAMP -> timestampValue(null) - Kind.BAG -> bagValue(null) - Kind.ARRAY -> listValue(null) - Kind.ROW -> structValue(null) - Kind.SEXP -> sexpValue(null) - Kind.STRUCT -> structValue() - Kind.UNKNOWN -> nullValue() - } - } } } 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 9406139991..7f32496802 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 @@ -51,14 +51,10 @@ import org.partiql.planner.internal.ir.rexOpSubquery import org.partiql.planner.internal.ir.statementQuery import org.partiql.planner.internal.ir.util.PlanRewriter import org.partiql.planner.internal.utils.PlanUtils +import org.partiql.spi.value.Datum import org.partiql.types.Field import org.partiql.types.PType import org.partiql.types.PType.Kind -import org.partiql.value.BoolValue -import org.partiql.value.MissingValue -import org.partiql.value.PartiQLValueExperimental -import org.partiql.value.TextValue -import org.partiql.value.stringValue import kotlin.math.max /** @@ -66,7 +62,6 @@ import kotlin.math.max * * @property env */ -@OptIn(PartiQLValueExperimental::class) internal class PlanTyper(private val env: Env) { /** @@ -562,7 +557,6 @@ internal class PlanTyper(private val env: Env) { * * @property typeEnv TypeEnv in which this rex tree is evaluated. */ - @OptIn(PartiQLValueExperimental::class) private inner class RexTyper( private val typeEnv: TypeEnv, private val strategy: Strategy, @@ -648,7 +642,7 @@ internal class PlanTyper(private val env: Env) { return rex(root.type.typeParameter, rexOpPathIndex(root, key)) } - private fun Rex.isLiteralMissing(): Boolean = this.op is Rex.Op.Lit && this.op.value is MissingValue + private fun Rex.isLiteralMissing(): Boolean = this.op is Rex.Op.Lit && this.op.value.isMissing override fun visitRexOpPathKey(node: Rex.Op.Path.Key, ctx: CompilerType?): Rex { val root = visitRex(node.root, node.root.type) @@ -676,10 +670,18 @@ internal class PlanTyper(private val env: Env) { } // Get Literal Key - val keyOp = key.op - val keyLiteral = when (keyOp is Rex.Op.Lit && keyOp.value is TextValue<*> && !keyOp.value.isNull) { - true -> keyOp.value.string!! - false -> return rex(CompilerType(PType.dynamic()), rexOpPathKey(root, key)) + var keyLiteral: String? = null + if (key.op is Rex.Op.Lit && !key.op.value.isNull) { + val value = key.op.value + val kind = value.type.kind + if (kind == Kind.STRING || kind == Kind.SYMBOL || kind == Kind.CHAR) { + keyLiteral = value.string + } + } + + // The key expression was NOT a text literal. + if (keyLiteral == null) { + return rex(CompilerType(PType.dynamic()), rexOpPathKey(root, key)) } // Find Type @@ -748,7 +750,7 @@ internal class PlanTyper(private val env: Env) { } } - private fun rexString(str: String) = rex(CompilerType(PType.string()), Rex.Op.Lit(stringValue(str))) + private fun rexString(str: String) = rex(CompilerType(PType.string()), Rex.Op.Lit(Datum.string(str))) override fun visitRexOpCastUnresolved(node: Rex.Op.Cast.Unresolved, ctx: CompilerType?): Rex { val arg = visitRex(node.arg, null) @@ -950,9 +952,8 @@ internal class PlanTyper(private val env: Env) { /** * Returns the boolean value of the expression. For now, only handle literals. */ - @OptIn(PartiQLValueExperimental::class) private fun boolOrNull(op: Rex.Op): Boolean? { - return if (op is Rex.Op.Lit && op.value is BoolValue) op.value.value else null + return if (op is Rex.Op.Lit && op.value.type.kind == Kind.BOOL) op.value.boolean else null } /** @@ -994,7 +995,6 @@ internal class PlanTyper(private val env: Env) { return rex(CompilerType(type), rexOpCollection(values)) } - @OptIn(PartiQLValueExperimental::class) override fun visitRexOpStruct(node: Rex.Op.Struct, ctx: CompilerType?): Rex { val fields = node.fields.map { val k = visitRex(it.k, it.k.type) @@ -1003,14 +1003,22 @@ internal class PlanTyper(private val env: Env) { } var structIsClosed = true val structTypeFields = mutableListOf() - for (field in fields) { - val keyOp = field.k.op - // TODO: Check key type - if (keyOp !is Rex.Op.Lit || keyOp.value !is TextValue<*>) { + for ((k, v) in fields) { + // see if the key is string literal + var key: String? = null + if (k.op is Rex.Op.Lit && !k.op.value.isNull) { + val kind = k.op.value.type.kind + if (kind == Kind.STRING || kind == Kind.SYMBOL || kind == Kind.CHAR) { + key = k.op.value.string + } + } + // unknown key, so the struct becomes open. + if (key == null) { structIsClosed = false continue } - structTypeFields.add(CompilerType.Field(keyOp.value.string!!, field.v.type)) + // known key, add to the computed type + structTypeFields.add(CompilerType.Field(key, v.type)) } val type = when (structIsClosed) { true -> CompilerType(PType.row(structTypeFields as Collection)) diff --git a/partiql-planner/src/main/kotlin/org/partiql/planner/internal/typer/Scope.kt b/partiql-planner/src/main/kotlin/org/partiql/planner/internal/typer/Scope.kt index bc76fd397f..0a16600ce7 100644 --- a/partiql-planner/src/main/kotlin/org/partiql/planner/internal/typer/Scope.kt +++ b/partiql-planner/src/main/kotlin/org/partiql/planner/internal/typer/Scope.kt @@ -9,11 +9,10 @@ import org.partiql.planner.internal.ir.rexOpPathSymbol import org.partiql.planner.internal.ir.rexOpVarLocal import org.partiql.planner.internal.typer.PlanTyper.Companion.toCType import org.partiql.spi.catalog.Identifier +import org.partiql.spi.value.Datum import org.partiql.types.PType import org.partiql.types.PType.Kind import org.partiql.types.StaticType -import org.partiql.value.PartiQLValueExperimental -import org.partiql.value.stringValue /** * Represents local variable scopes. @@ -173,7 +172,6 @@ internal data class Scope( rex(type, op) } - @OptIn(PartiQLValueExperimental::class) - private fun string(text: String) = rex(CompilerType(PType.string()), rexOpLit(stringValue(text))) + private fun string(text: String) = rex(CompilerType(PType.string()), rexOpLit(Datum.string(text))) } } diff --git a/partiql-planner/src/main/resources/partiql_plan_internal.ion b/partiql-planner/src/main/resources/partiql_plan_internal.ion index a45efb3bdc..2689bc6825 100644 --- a/partiql-planner/src/main/resources/partiql_plan_internal.ion +++ b/partiql-planner/src/main/resources/partiql_plan_internal.ion @@ -2,7 +2,7 @@ imports::{ kotlin: [ identifier::'org.partiql.spi.catalog.Identifier', name::'org.partiql.spi.catalog.Name', - partiql_value::'org.partiql.value.PartiQLValue', + datum::'org.partiql.spi.value.Datum', partiql_value_type::'org.partiql.planner.internal.typer.CompilerType', static_type::'org.partiql.planner.internal.typer.CompilerType', fn_signature::'org.partiql.spi.fn.Function', @@ -73,7 +73,7 @@ rex::{ op:[ lit::{ - value: partiql_value, + value: datum, }, var::[ diff --git a/partiql-planner/src/test/kotlin/org/partiql/planner/internal/typer/PlanTyperTest.kt b/partiql-planner/src/test/kotlin/org/partiql/planner/internal/typer/PlanTyperTest.kt index b9ac8534b7..c9df0f0676 100644 --- a/partiql-planner/src/test/kotlin/org/partiql/planner/internal/typer/PlanTyperTest.kt +++ b/partiql-planner/src/test/kotlin/org/partiql/planner/internal/typer/PlanTyperTest.kt @@ -22,10 +22,8 @@ import org.partiql.spi.catalog.Identifier import org.partiql.spi.catalog.Name import org.partiql.spi.catalog.Session import org.partiql.spi.catalog.Table +import org.partiql.spi.value.Datum import org.partiql.types.PType -import org.partiql.value.PartiQLValueExperimental -import org.partiql.value.int32Value -import org.partiql.value.stringValue import kotlin.io.path.toPath class PlanTyperTest { @@ -40,20 +38,19 @@ class PlanTyperTest { private val DOUBLE_PRECISION = PType.doublePrecision().toCType() private val DECIMAL = PType.decimal().toCType() - @OptIn(PartiQLValueExperimental::class) private val LITERAL_STRUCT_1 = rex( ANY, rexOpStruct( fields = listOf( rexOpStructField( - k = rex(STRING, rexOpLit(stringValue("FiRsT_KeY"))), + k = rex(STRING, rexOpLit(Datum.string(("FiRsT_KeY")))), v = rex( ANY, rexOpStruct( fields = listOf( rexOpStructField( - k = rex(STRING, rexOpLit(stringValue("sEcoNd_KEY"))), - v = rex(INT4, rexOpLit(int32Value(5))) + k = rex(STRING, rexOpLit(Datum.string(("sEcoNd_KEY")))), + v = rex(INT4, rexOpLit(Datum.integer(5))) ) ) ) @@ -67,7 +64,6 @@ class PlanTyperTest { listOf(CompilerType.Field("sEcoNd_KEY", INT4)), ).toCType() - @OptIn(PartiQLValueExperimental::class) private val LITERAL_STRUCT_1_TYPED: Rex get() { val topLevelStruct = PType.row( @@ -78,14 +74,14 @@ class PlanTyperTest { rexOpStruct( fields = listOf( rexOpStructField( - k = rex(STRING, rexOpLit(stringValue("FiRsT_KeY"))), + k = rex(STRING, rexOpLit(Datum.string(("FiRsT_KeY")))), v = rex( type = LITERAL_STRUCT_1_FIRST_KEY_TYPE, rexOpStruct( fields = listOf( rexOpStructField( - k = rex(STRING, rexOpLit(stringValue("sEcoNd_KEY"))), - v = rex(INT4, rexOpLit(int32Value(5))) + k = rex(STRING, rexOpLit(Datum.string(("sEcoNd_KEY")))), + v = rex(INT4, rexOpLit(Datum.integer(5))) ) ) ) @@ -307,8 +303,7 @@ class PlanTyperTest { assertEquals(expected, actual) } - @OptIn(PartiQLValueExperimental::class) - private fun rexString(str: String) = rex(STRING, rexOpLit(stringValue(str))) + private fun rexString(str: String) = rex(STRING, rexOpLit(Datum.string((str)))) private fun Rex.pathKey(key: String, type: CompilerType = ANY): Rex = Rex(type, rexOpPathKey(this, rexString(key))) diff --git a/partiql-spi/api/partiql-spi.api b/partiql-spi/api/partiql-spi.api index 03b459c544..cad4cdf15b 100644 --- a/partiql-spi/api/partiql-spi.api +++ b/partiql-spi/api/partiql-spi.api @@ -718,10 +718,12 @@ public final class org/partiql/spi/value/ion/IonDatum : org/partiql/spi/value/Da public fun isNull ()Z public fun iterator ()Ljava/util/Iterator; public static final fun of (Lcom/amazon/ionelement/api/AnyElement;)Lorg/partiql/spi/value/Datum; + public static final fun of (Ljava/lang/String;)Lorg/partiql/spi/value/Datum; } public final class org/partiql/spi/value/ion/IonDatum$Companion { public final fun of (Lcom/amazon/ionelement/api/AnyElement;)Lorg/partiql/spi/value/Datum; + public final fun of (Ljava/lang/String;)Lorg/partiql/spi/value/Datum; } public abstract class org/partiql/value/BagValue : org/partiql/value/CollectionValue { diff --git a/partiql-spi/src/main/java/org/partiql/spi/value/DatumComparator.java b/partiql-spi/src/main/java/org/partiql/spi/value/DatumComparator.java index 35c7571f0d..ae515dc45d 100644 --- a/partiql-spi/src/main/java/org/partiql/spi/value/DatumComparator.java +++ b/partiql-spi/src/main/java/org/partiql/spi/value/DatumComparator.java @@ -179,6 +179,7 @@ private static Map initializeTypePrecedence() { // OTHER precedence.put(PType.Kind.DYNAMIC, 100); precedence.put(PType.Kind.UNKNOWN, 100); + precedence.put(PType.Kind.VARIANT, 100); return precedence; } diff --git a/partiql-spi/src/main/kotlin/org/partiql/spi/value/ion/IonDatum.kt b/partiql-spi/src/main/kotlin/org/partiql/spi/value/ion/IonDatum.kt index 525a48a067..13018d988a 100644 --- a/partiql-spi/src/main/kotlin/org/partiql/spi/value/ion/IonDatum.kt +++ b/partiql-spi/src/main/kotlin/org/partiql/spi/value/ion/IonDatum.kt @@ -14,6 +14,7 @@ import com.amazon.ionelement.api.ElementType.STRING import com.amazon.ionelement.api.ElementType.STRUCT import com.amazon.ionelement.api.ElementType.SYMBOL import com.amazon.ionelement.api.ElementType.TIMESTAMP +import com.amazon.ionelement.api.loadSingleElement import org.partiql.spi.value.Datum import org.partiql.spi.value.Field import org.partiql.types.PType @@ -132,6 +133,16 @@ public class IonDatum private constructor(value: AnyElement, type: PType) : } return IonDatum(value, type) } + + @JvmStatic + public fun of(value: String): Datum { + val ion = try { + loadSingleElement(value) + } catch (e: Exception) { + throw IllegalArgumentException("Invalid Ion value: $value", e) + } + return of(ion) + } } override fun getType(): PType = _type diff --git a/partiql-types/README.adoc b/partiql-types/README.adoc deleted file mode 100644 index 3f27adbb33..0000000000 --- a/partiql-types/README.adoc +++ /dev/null @@ -1,317 +0,0 @@ -= PartiQL Types & Values -:toc: -:sectlinks: - -This package contains classes for manipulating both PartiQL types and values. APIs for `org.partiql.types.StaticType` -are stable, but the value representation classes (PartiQLValue) are early in their lifecycle and are subject to change. -These particular classes have been marked with xref:https://kotlinlang.org/docs/opt-in-requirements.html[@RequiresOptIn]. - -[#types] -== Types - -PartiQL's data model extends SQL to Ion's type system to cover schema-less and nested data. This section should be -considered a draft, but the _StaticType_ class within `org.partiql.types` is stable. - -.Boolean Type -[cols="1m,3,>2",grid=rows,frame=all] -|=== -| Type | Description | Values - -| bool -| Boolean value -| `TRUE` or `FALSE` - -|=== - -.Numeric Types -[cols="1m,3,>2a",grid=rows,frame=all] -|=== -| Type | Description | Range - -| int8 -| Signed integer that can be stored in one byte -| -128 to +127 - -| int16 -| Signed integer that can be stored in two bytes -| -32768 to +32767 - -| int32 -| Signed integer that can be stored in four bytes -| -2147483648 to +2147483647 - -| int64 -| Signed integer that can be stored in eight bytes -| -9223372036854775808 to +9223372036854775807 - -| int -| Signed integer of arbitrary size -| ... - -| decimal -| Exact numeric type with arbitrary precision -| ... - -| decimal(p,s) -| Exact numeric with fixed precision and scale -| ... - -| float32 -| Single-precision, binary floating point (IEEE 754 32-bit) -| ... - -| float64 -| Double-precision, binary floating point (IEEE 754 64-bit) -| ... - -|=== - -.Character String Types -[cols="1m,5",grid=rows,frame=all] -|=== -| Type | Description - -| char(n) -| Unicode codepoint sequence of fixed length _n_ - -| string -| Unicode codepoint sequence of arbitrary length - -| string(n) -| Unicode codepoint sequence of variable length up to _n_. This is equivalent to the SQL type `CHARACTER VARYING(n)`, `VARCHAR(n)`. - -| symbol -| Unicode codepoint sequences of arbitrary length. The primary difference is the intended semantics: symbols represent case-sensitive identifiers as opposed to textual literal values. - -|=== - -.Bit String Types -[cols="1m,5",grid=rows,frame=all] -|=== -| Type | Description - -| bit(n) -| Bit string of fixed length _n_ - -| binary -| Bit string of arbitrary length - -| binary(n) -| Bit string of variable length up to _n_ - -|=== - -.Byte String Types -[cols="1m,5",grid=rows,frame=all] -|=== -| Type | Description - -| byte(n) -| Octet string of fixed length _n_ - -| blob -| Octet string of arbitrary length - -| blob(n) -| Octet string of variable length up to _n_ - -| clob -| Unicode codepoint sequence of arbitrary length - -| clob(n) -| Unicode codepoint sequence of variable length up to _n_ - -|=== - -.Date/Time Types -[cols="1m,5",grid=rows,frame=all] -|=== -| Type | Description - -| date -| A date with no _time_ - -| time -| A _date_-less time with arbitrary precision and no time zone offset - -| time(p) -| A _date_-less time with seconds precision _p_ and no time zone offset - -| time(p,z) -| A _date_-less time with seconds precision _p_ and time zone offset _z_ - -| timestamp -| A _date_ and _time_ with arbitrary precision - -| timestamp(p) -| A _date_ and _time_ with seconds precision _p_ and no time zone offset - -| timestamp(p,z) -| A _date_ and _time_ with seconds precision _p_ and time zone offset _z_ - -// Open Question: Should we model timestamp that only has UTF offsets which are a strict subset of timezones. - -// SQL-99 4.7.2 -| interval(p) -| A time interval with seconds precision _p_ - -2+a| NOTE: A time zone offset _z_ is an hour/minute pair of integers - -|=== - -.Collection Types -[cols="1m,5",grid=rows,frame=all] -|=== -| Type | Description - -| bag -| Unordered collection of values - -| list -| Ordered collection of values - -| sexp -| Ordered collections of values with application-defined semantics - -|=== - -.Struct Type -[cols="1m,5",grid=rows,frame=all] -|=== -| Type | Description - -| struct -| Unordered collection of unconstrained fields - -2+a| NOTE: Fields are name-value pairs where names are strings - -|=== - -.Absent Types -[cols="1m,3,>2m",grid=rows,frame=all] -|=== -| Type | Description | Value - -| null -| The null value -| NULL - -| missing -| The missing value -| MISSING - -|=== - -.Union Types -[cols="1m,5",grid=rows,frame=all] -|=== - -s| Type s| Description - -| any -| The union of all types - -| absent -| `missing\|null` - -| number -| `int8\|int16\|int32\|int64\|decimal\|float32\|float64` - -| exact_numeric -| `int8\|int16\|int32\|int64\|decimal` - -| collection -| `bag\|list\|sexp` - -|=== - -== Values - -PartiQL's value classes are based upon xref:https://github.com/amazon-ion/ion-element-kotlin[IonElement]. - -IMPORTANT: Like SQL, all values are nullable. - -=== Examples - -[source,kotlin] ----- -// simple value -intValue(1) // 1 - -// complex values -structValue(sequenceOf( - "a" to intValue(1, listOf("hello")), // second parameter is a list of annotations - "b" to sexpValue(sequenceOf( - symbolValue("x"), - stringValue("y"), - boolValue(null), - )), - "c" to bagValue(sequenceOf( - intValue(1), - intValue(2), - intValue(3), - )) -)) - -// PartiQL Text -// { -// 'a': hello::1, -// 'b': (x 'y' null), -// 'c': << 1, 2, 3 >>, -// } ----- - -=== Ion Usage - -PartiQL Value classes can be serialized and deserialized from both Ion text and binary Ion via the standard IonReader. -You can write PartiQL Values as plain Ion, or PartiQL values encoded as Ion (as some types in PartiQL to not exist in Ion). - -IMPORTANT: The encoding of PartiQL data as Ion has not been standardized. - -.Ion Encoding Type -[source,kotlin] ----- -/** - * The ion value annotations are always treated as annotations on PartiQL value. - * For example, $missing::null will be treated as nullValue(annotations = ["missing"]) - */ -IonGeneric - -/** - * We examine the **last** annotation before convert to PartiQL Value. - * If the annotation is PartiQL reserved, we validate Semantics and the constructed PartiQL value may be different. - * For example: - * - $missing::null will be treated as missingValue(annotations = []) - * - a::b::$missing:null will be treated as missingValue(annotations = ["a", "b"]) - * - a::$missing::b::null will be treated as nullValue(annotation = ["a", "$missing", "b"] - * - $missing::1 will cause an exception. - */ -IonForPartiQL ----- - -These APIs mirror IonJava, but we are working on the appropriate APIs for easier reading and writing. In the future, -we would like entry points such as `myValue.encode(PartiQLValueEncoding.ION)` and `decode(input, PartiQLValueEncoding.ION)`. -For DOM to DOM, we may have `myValue.toIon()`. - -[source,kotlin] ----- -val bag = bagValue(sequenceOf( - intValue(1), - intValue(2), - intValue(3), -)) - -// PartiQL Text writer -val pBuffer = ByteArrayOutputStream() -val pWriter = PartiQLValueWriterBuilder.standard().build(pBuffer) - -pWriter.append(bag) -println(pBuffer.toString()) // > <<1,2,3>> - -// PartiQL to Ion writer -val iBuffer = ByteArrayOutputStream() -val iWriter = PartiQLValueIonWriterBuilder.standardIonTextBuilder().build(iBuffer) - -iWriter.append(bag) -println(iBuffer.toString()) // > $bag::[1,2,3] ----- diff --git a/partiql-types/api/partiql-types.api b/partiql-types/api/partiql-types.api index fd6340a1ac..c20627b88e 100644 --- a/partiql-types/api/partiql-types.api +++ b/partiql-types/api/partiql-types.api @@ -323,6 +323,7 @@ public abstract interface class org/partiql/types/PType { public static fun doublePrecision ()Lorg/partiql/types/PType; public static fun dynamic ()Lorg/partiql/types/PType; public static fun fromStaticType (Lorg/partiql/types/StaticType;)Lorg/partiql/types/PType; + public fun getEncoding ()Ljava/lang/String; public fun getFields ()Ljava/util/Collection; public abstract fun getKind ()Lorg/partiql/types/PType$Kind; public fun getLength ()I @@ -346,6 +347,7 @@ public abstract interface class org/partiql/types/PType { public static fun tinyint ()Lorg/partiql/types/PType; public static fun unknown ()Lorg/partiql/types/PType; public static fun varchar (I)Lorg/partiql/types/PType; + public static fun variant (Ljava/lang/String;)Lorg/partiql/types/PType; } public final class org/partiql/types/PType$Kind : java/lang/Enum { @@ -377,6 +379,7 @@ public final class org/partiql/types/PType$Kind : java/lang/Enum { public static final field TINYINT Lorg/partiql/types/PType$Kind; public static final field UNKNOWN Lorg/partiql/types/PType$Kind; public static final field VARCHAR Lorg/partiql/types/PType$Kind; + public static final field VARIANT Lorg/partiql/types/PType$Kind; public static fun valueOf (Ljava/lang/String;)Lorg/partiql/types/PType$Kind; public static fun values ()[Lorg/partiql/types/PType$Kind; } diff --git a/partiql-types/src/main/java/org/partiql/types/PType.java b/partiql-types/src/main/java/org/partiql/types/PType.java index 600e89f561..6000c357ce 100644 --- a/partiql-types/src/main/java/org/partiql/types/PType.java +++ b/partiql-types/src/main/java/org/partiql/types/PType.java @@ -44,6 +44,16 @@ public interface PType { @NotNull Kind getKind(); + /** + * Returns the variant type encoding. + * + * @throws UnsupportedOperationException if this is called on a type whose {@link Kind} is not: + * {@link Kind#VARIANT} + */ + default String getEncoding() throws UnsupportedOperationException { + throw new UnsupportedOperationException(); + } + /** * The fields of the type * @throws UnsupportedOperationException if this is called on a type whose {@link Kind} is not: @@ -431,6 +441,18 @@ enum Kind { */ STRUCT, + /** + * PartiQL's variant type. + *
+ * @see Spark VARIANT + * @see Snowflake VARIANT + *
+ * Type Syntax: NOT APPLICABLE + *
+ * Applicable methods: Implementation defined. + */ + VARIANT, + /** * PartiQL's unknown type. This temporarily represents literal null and missing values. *
@@ -705,6 +727,14 @@ static PType struct() { return new PTypePrimitive(Kind.STRUCT); } + /** + * @return a PartiQL variant type with the given encoding. + */ + @NotNull + static PType variant(String encoding) { + return new PTypeVariant(encoding); + } + /** * @return a PartiQL unknown type * @deprecated this API is experimental and is subject to modification/deletion without prior notice. diff --git a/partiql-types/src/main/java/org/partiql/types/PTypeVariant.java b/partiql-types/src/main/java/org/partiql/types/PTypeVariant.java new file mode 100644 index 0000000000..1fbf1c8ddf --- /dev/null +++ b/partiql-types/src/main/java/org/partiql/types/PTypeVariant.java @@ -0,0 +1,47 @@ +package org.partiql.types; + +import org.jetbrains.annotations.NotNull; + +import java.util.Objects; + +class PTypeVariant implements PType { + + @NotNull + final String _encoding; + + public PTypeVariant(@NotNull String encoding) { + this._encoding = encoding; + } + + @NotNull + @Override + public Kind getKind() { + return Kind.VARIANT; + } + + @Override + public String getEncoding() throws UnsupportedOperationException { + return _encoding; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof PTypeVariant)) return false; + PTypeVariant that = (PTypeVariant) o; + return Objects.equals(_encoding, that._encoding); + } + + @Override + public int hashCode() { + int hashcode = 0; + hashcode += "variant".hashCode(); + hashcode += 31 * _encoding.hashCode(); + return hashcode; + } + + @Override + public String toString() { + return "VARIANT(" + _encoding + ")"; + } +}