diff --git a/partiql-ast/api/partiql-ast.api b/partiql-ast/api/partiql-ast.api index 8759e111b..54da223ce 100644 --- a/partiql-ast/api/partiql-ast.api +++ b/partiql-ast/api/partiql-ast.api @@ -25,7 +25,7 @@ public final class org/partiql/ast/Ast { public static final fun exprInCollection (Lorg/partiql/ast/expr/Expr;Lorg/partiql/ast/expr/Expr;Z)Lorg/partiql/ast/expr/ExprInCollection; public static final fun exprIsType (Lorg/partiql/ast/expr/Expr;Lorg/partiql/ast/DataType;Z)Lorg/partiql/ast/expr/ExprIsType; public static final fun exprLike (Lorg/partiql/ast/expr/Expr;Lorg/partiql/ast/expr/Expr;Lorg/partiql/ast/expr/Expr;Z)Lorg/partiql/ast/expr/ExprLike; - public static final fun exprLit (Lorg/partiql/value/PartiQLValue;)Lorg/partiql/ast/expr/ExprLit; + public static final fun exprLit (Lorg/partiql/ast/Literal;)Lorg/partiql/ast/expr/ExprLit; public static final fun exprMatch (Lorg/partiql/ast/expr/Expr;Lorg/partiql/ast/graph/GraphMatch;)Lorg/partiql/ast/expr/ExprMatch; public static final fun exprNot (Lorg/partiql/ast/expr/Expr;)Lorg/partiql/ast/expr/ExprNot; public static final fun exprNullIf (Lorg/partiql/ast/expr/Expr;Lorg/partiql/ast/expr/Expr;)Lorg/partiql/ast/expr/ExprNullIf; @@ -233,6 +233,8 @@ public abstract class org/partiql/ast/AstRewriter : org/partiql/ast/AstVisitor { public fun visitLet (Lorg/partiql/ast/Let;Ljava/lang/Object;)Lorg/partiql/ast/AstNode; public synthetic fun visitLetBinding (Lorg/partiql/ast/Let$Binding;Ljava/lang/Object;)Ljava/lang/Object; public fun visitLetBinding (Lorg/partiql/ast/Let$Binding;Ljava/lang/Object;)Lorg/partiql/ast/AstNode; + public synthetic fun visitLiteral (Lorg/partiql/ast/Literal;Ljava/lang/Object;)Ljava/lang/Object; + public fun visitLiteral (Lorg/partiql/ast/Literal;Ljava/lang/Object;)Lorg/partiql/ast/AstNode; public synthetic fun visitPathStepAllElements (Lorg/partiql/ast/expr/PathStep$AllElements;Ljava/lang/Object;)Ljava/lang/Object; public fun visitPathStepAllElements (Lorg/partiql/ast/expr/PathStep$AllElements;Ljava/lang/Object;)Lorg/partiql/ast/AstNode; public synthetic fun visitPathStepAllFields (Lorg/partiql/ast/expr/PathStep$AllFields;Ljava/lang/Object;)Ljava/lang/Object; @@ -348,6 +350,7 @@ public abstract class org/partiql/ast/AstVisitor { public fun visitKeyValue (Lorg/partiql/ast/ddl/KeyValue;Ljava/lang/Object;)Ljava/lang/Object; public fun visitLet (Lorg/partiql/ast/Let;Ljava/lang/Object;)Ljava/lang/Object; public fun visitLetBinding (Lorg/partiql/ast/Let$Binding;Ljava/lang/Object;)Ljava/lang/Object; + public fun visitLiteral (Lorg/partiql/ast/Literal;Ljava/lang/Object;)Ljava/lang/Object; public fun visitNullable (Lorg/partiql/ast/ddl/AttributeConstraint$Null;Ljava/lang/Object;)Ljava/lang/Object; public fun visitOrderBy (Lorg/partiql/ast/OrderBy;Ljava/lang/Object;)Ljava/lang/Object; public fun visitPartitionBy (Lorg/partiql/ast/ddl/PartitionBy;Ljava/lang/Object;)Ljava/lang/Object; @@ -929,6 +932,42 @@ public class org/partiql/ast/Let$Builder { public fun toString ()Ljava/lang/String; } +public class org/partiql/ast/Literal : org/partiql/ast/AstEnum { + public static final field APPROX_NUM I + public static final field BOOL I + public static final field EXACT_NUM I + public static final field INT_NUM I + public static final field MISSING I + public static final field NULL I + public static final field STRING I + public static final field TYPED_STRING I + public static final field UNKNOWN I + public fun accept (Lorg/partiql/ast/AstVisitor;Ljava/lang/Object;)Ljava/lang/Object; + public static fun approxNum (Ljava/lang/String;)Lorg/partiql/ast/Literal; + public fun bigDecimalValue ()Ljava/math/BigDecimal; + public static fun bool (Z)Lorg/partiql/ast/Literal; + public fun booleanValue ()Z + protected fun canEqual (Ljava/lang/Object;)Z + public fun children ()Ljava/util/Collection; + public fun code ()I + public fun dataType ()Lorg/partiql/ast/DataType; + public fun equals (Ljava/lang/Object;)Z + public static fun exactNum (Ljava/lang/String;)Lorg/partiql/ast/Literal; + public static fun exactNum (Ljava/math/BigDecimal;)Lorg/partiql/ast/Literal; + public fun hashCode ()I + public static fun intNum (I)Lorg/partiql/ast/Literal; + public static fun intNum (J)Lorg/partiql/ast/Literal; + public static fun intNum (Ljava/lang/String;)Lorg/partiql/ast/Literal; + public static fun intNum (Ljava/math/BigInteger;)Lorg/partiql/ast/Literal; + public static fun missing ()Lorg/partiql/ast/Literal; + public fun name ()Ljava/lang/String; + public static fun nul ()Lorg/partiql/ast/Literal; + public fun numberValue ()Ljava/lang/String; + public static fun string (Ljava/lang/String;)Lorg/partiql/ast/Literal; + public fun stringValue ()Ljava/lang/String; + public static fun typedString (Lorg/partiql/ast/DataType;Ljava/lang/String;)Lorg/partiql/ast/Literal; +} + public class org/partiql/ast/Nulls : org/partiql/ast/AstEnum { public static final field FIRST I public static final field LAST I @@ -1657,22 +1696,15 @@ public class org/partiql/ast/expr/ExprLike$Builder { } public class org/partiql/ast/expr/ExprLit : org/partiql/ast/expr/Expr { - public final field value Lorg/partiql/value/PartiQLValue; - public fun (Lorg/partiql/value/PartiQLValue;)V + public field lit Lorg/partiql/ast/Literal; + public fun (Lorg/partiql/ast/Literal;)V public fun accept (Lorg/partiql/ast/AstVisitor;Ljava/lang/Object;)Ljava/lang/Object; - public static fun builder ()Lorg/partiql/ast/expr/ExprLit$Builder; protected fun canEqual (Ljava/lang/Object;)Z public fun children ()Ljava/util/Collection; public fun equals (Ljava/lang/Object;)Z public fun hashCode ()I } -public class org/partiql/ast/expr/ExprLit$Builder { - public fun build ()Lorg/partiql/ast/expr/ExprLit; - public fun toString ()Ljava/lang/String; - public fun value (Lorg/partiql/value/PartiQLValue;)Lorg/partiql/ast/expr/ExprLit$Builder; -} - public class org/partiql/ast/expr/ExprMatch : org/partiql/ast/expr/Expr { public final field expr Lorg/partiql/ast/expr/Expr; public final field pattern Lorg/partiql/ast/graph/GraphMatch; diff --git a/partiql-ast/build.gradle.kts b/partiql-ast/build.gradle.kts index 7d99c0b8c..f930fe36e 100644 --- a/partiql-ast/build.gradle.kts +++ b/partiql-ast/build.gradle.kts @@ -20,10 +20,6 @@ plugins { dependencies { api(Deps.ionElement) - api(project(":partiql-types")) - // TODO REMOVE ME ONCE PartiQLValue IS REMOVED - // THE AST NEEDS ITS OWN "VALUE" REPRESENTATION - api(project(":partiql-spi")) compileOnly(Deps.lombok) annotationProcessor(Deps.lombok) } diff --git a/partiql-ast/src/main/java/org/partiql/ast/AstVisitor.java b/partiql-ast/src/main/java/org/partiql/ast/AstVisitor.java index 51ae5a12c..7c1767fe9 100644 --- a/partiql-ast/src/main/java/org/partiql/ast/AstVisitor.java +++ b/partiql-ast/src/main/java/org/partiql/ast/AstVisitor.java @@ -292,6 +292,10 @@ public R visitExprWindowOver(ExprWindow.Over node, C ctx) { return defaultVisit(node, ctx); } + public R visitLiteral(Literal node, C ctx) { + return defaultVisit(node, ctx); + } + public R visitQueryBody(QueryBody node, C ctx) { return node.accept(this, ctx); } diff --git a/partiql-ast/src/main/java/org/partiql/ast/Explain.java b/partiql-ast/src/main/java/org/partiql/ast/Explain.java index 888208624..d93dc73c8 100644 --- a/partiql-ast/src/main/java/org/partiql/ast/Explain.java +++ b/partiql-ast/src/main/java/org/partiql/ast/Explain.java @@ -3,7 +3,6 @@ import lombok.Builder; import lombok.EqualsAndHashCode; import org.jetbrains.annotations.NotNull; -import org.partiql.value.PartiQLValue; import java.util.ArrayList; import java.util.Collection; @@ -16,14 +15,13 @@ @Builder(builderClassName = "Builder") @EqualsAndHashCode(callSuper = false) public class Explain extends Statement { - // TODO get rid of PartiQLValue once https://github.com/partiql/partiql-lang-kotlin/issues/1589 is resolved @NotNull - public final Map options; + public final Map options; @NotNull public final Statement statement; - public Explain(@NotNull Map options, @NotNull Statement statement) { + public Explain(@NotNull Map options, @NotNull Statement statement) { this.options = options; this.statement = statement; } diff --git a/partiql-ast/src/main/java/org/partiql/ast/Literal.java b/partiql-ast/src/main/java/org/partiql/ast/Literal.java new file mode 100644 index 000000000..58943714e --- /dev/null +++ b/partiql-ast/src/main/java/org/partiql/ast/Literal.java @@ -0,0 +1,254 @@ +package org.partiql.ast; + +import lombok.EqualsAndHashCode; +import org.jetbrains.annotations.NotNull; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.Collection; +import java.util.Collections; + +import static java.util.Objects.requireNonNull; + +/** + * TODO docs + */ +@EqualsAndHashCode(callSuper = false) +public class Literal extends AstEnum { + public static final int UNKNOWN = 0; + // absent literals + public static final int NULL = 1; + public static final int MISSING = 2; + // boolean literal + public static final int BOOL = 3; + // numeric literals + public static final int APPROX_NUM = 4; + public static final int EXACT_NUM = 5; + public static final int INT_NUM = 6; + // string literal + public static final int STRING = 7; + // typed string literal + public static final int TYPED_STRING = 8; + + // Literal fields + private final int code; + private final Boolean boolValue; + private final String stringValue; + private final DataType dataType; + + /////// Constructors + private Literal(int code, Boolean value, String stringValue, DataType dataType) { + this.code = code; + this.boolValue = value; + this.stringValue = stringValue; + this.dataType = dataType; + } + + // Private constructor for absent literals + private Literal(int code) { + this.code = code; + // Rest set to null + this.boolValue = null; + this.stringValue = null; + this.dataType = null; + } + + // Private constructor for boolean literal + private Literal(boolean value) { + this.code = BOOL; + this.boolValue = value; + // Rest set to null + this.stringValue = null; + this.dataType = null; + } + + // Private constructor for literals stored w/ just a string (e.g. numerics, single-quoted strings) + private Literal(int code, String value) { + this.code = code; + this.stringValue = value; + // Rest set to null + this.boolValue = null; + this.dataType = null; + } + + // Private constructor for typed string literal + private Literal(DataType dataType, String value) { + this.code = TYPED_STRING; + this.stringValue = value; + this.dataType = dataType; + // Rest set to null + this.boolValue = null; + } + + @Override + public int code() { + return code; + } + + @NotNull + @Override + public String name() { + switch (code) { + case NULL: return "NULL"; + case MISSING: return "MISSING"; + case BOOL: return "BOOL"; + case APPROX_NUM: return "APPROX_NUM"; + case EXACT_NUM: return "EXACT_NUM"; + case INT_NUM: return "INT_NUM"; + case STRING: return "STRING"; + case TYPED_STRING: return "TYPED_STRING"; + default: return "UNKNOWN"; + } + } + + @Override + @NotNull + public Collection children() { + return Collections.emptyList(); + } + + @Override + public R accept(@NotNull AstVisitor visitor, C ctx) { + return visitor.visitLiteral(this, ctx); + } + + // Factory methods + @NotNull + public static Literal approxNum(@NotNull String value) { + return new Literal(APPROX_NUM, value); + } + + @NotNull + public static Literal bool(boolean value) { + return new Literal(value); + } + + @NotNull + public static Literal exactNum(@NotNull BigDecimal value) { + if (value.scale() == 0) { + return new Literal(EXACT_NUM, value + "."); + } else { + return new Literal(EXACT_NUM, value.toString()); + } + } + + @NotNull + public static Literal exactNum(@NotNull String value) { + return new Literal(EXACT_NUM, value); + } + + @NotNull + public static Literal intNum(int value) { + return new Literal(INT_NUM, Integer.toString(value)); + } + + @NotNull + public static Literal intNum(long value) { + return new Literal(INT_NUM, Long.toString(value)); + } + + @NotNull + public static Literal intNum(@NotNull BigInteger value) { + return new Literal(INT_NUM, value.toString()); + } + + @NotNull + public static Literal intNum(@NotNull String value) { + return new Literal(INT_NUM, value); + } + + @NotNull + public static Literal nul() { + return new Literal(NULL); + } + + @NotNull + public static Literal missing() { + return new Literal(MISSING); + } + + @NotNull + public static Literal string(@NotNull String value) { + return new Literal(STRING, value); + } + + @NotNull + public static Literal typedString(@NotNull DataType type, @NotNull String value) { + return new Literal(type, value); + } + + // Value extraction + /** + * TODO docs + * Valid for just BOOL + */ + public boolean booleanValue() { + if (code == BOOL) { + requireNonNull(boolValue, "bool value"); + return boolValue; + } + throw new UnsupportedOperationException(); + } + + /** + * TODO docs + * Valid for just APPROX_NUM, EXACT_NUM, and INT_NUM. + */ + @NotNull + public String numberValue() { + switch (code) { + case APPROX_NUM: + case EXACT_NUM: + case INT_NUM: + requireNonNull(stringValue, "string value for numerics"); + return stringValue; + default: + throw new UnsupportedOperationException(); + } + } + + /** + * TODO docs + * Valid for just EXACT_NUM and INT_NUM + */ + @NotNull + public BigDecimal bigDecimalValue() { + switch (code) { + case EXACT_NUM: + case INT_NUM: + requireNonNull(stringValue, "string value for exact and int numerics"); + return new BigDecimal(stringValue); + default: + throw new UnsupportedOperationException(); + } + } + + /** + * TODO docs + * Valid for just STRING and TYPED_STRING + */ + @NotNull + public String stringValue() { + switch (code) { + case STRING: + case TYPED_STRING: + requireNonNull(stringValue, "string value"); + return stringValue; + default: + throw new UnsupportedOperationException(); + } + } + + /** + * TODO docs + * Valid for just TYPED_STRING + */ + @NotNull + public DataType dataType() { + if (code == TYPED_STRING) { + requireNonNull(dataType, "data type"); + return dataType; + } + throw new UnsupportedOperationException(); + } +} diff --git a/partiql-ast/src/main/java/org/partiql/ast/expr/ExprLit.java b/partiql-ast/src/main/java/org/partiql/ast/expr/ExprLit.java index fbb790854..2d21d072c 100644 --- a/partiql-ast/src/main/java/org/partiql/ast/expr/ExprLit.java +++ b/partiql-ast/src/main/java/org/partiql/ast/expr/ExprLit.java @@ -1,32 +1,33 @@ package org.partiql.ast.expr; -import lombok.Builder; import lombok.EqualsAndHashCode; import org.jetbrains.annotations.NotNull; import org.partiql.ast.AstNode; import org.partiql.ast.AstVisitor; -import org.partiql.value.PartiQLValue; +import org.partiql.ast.Literal; +import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; +import java.util.List; /** * TODO docs, equals, hashcode */ -@Builder(builderClassName = "Builder") @EqualsAndHashCode(callSuper = false) public class ExprLit extends Expr { @NotNull - public final PartiQLValue value; // This representation be changed in https://github.com/partiql/partiql-lang-kotlin/issues/1589 + public Literal lit; - public ExprLit(@NotNull PartiQLValue value) { - this.value = value; + public ExprLit(@NotNull Literal lit) { + this.lit = lit; } - @Override @NotNull + @Override public Collection children() { - return Collections.emptyList(); + List kids = new ArrayList<>(); + kids.add(lit); + return kids; } @Override diff --git a/partiql-ast/src/main/java/org/partiql/ast/sql/SqlDialect.kt b/partiql-ast/src/main/java/org/partiql/ast/sql/SqlDialect.kt index ac1502292..78107c1f7 100644 --- a/partiql-ast/src/main/java/org/partiql/ast/sql/SqlDialect.kt +++ b/partiql-ast/src/main/java/org/partiql/ast/sql/SqlDialect.kt @@ -14,7 +14,9 @@ package org.partiql.ast.sql -import org.partiql.ast.Ast.exprLit +import org.partiql.ast.Ast.exprVarRef +import org.partiql.ast.Ast.identifier +import org.partiql.ast.Ast.identifierChain import org.partiql.ast.AstNode import org.partiql.ast.AstVisitor import org.partiql.ast.DataType @@ -31,6 +33,7 @@ import org.partiql.ast.Identifier import org.partiql.ast.IdentifierChain import org.partiql.ast.JoinType import org.partiql.ast.Let +import org.partiql.ast.Literal import org.partiql.ast.Nulls import org.partiql.ast.Order import org.partiql.ast.OrderBy @@ -79,14 +82,6 @@ import org.partiql.ast.expr.ExprVarRef import org.partiql.ast.expr.ExprVariant import org.partiql.ast.expr.PathStep import org.partiql.ast.expr.Scope -import org.partiql.value.MissingValue -import org.partiql.value.NullValue -import org.partiql.value.PartiQLValueExperimental -import org.partiql.value.StringValue -import org.partiql.value.io.PartiQLValueTextWriter -import org.partiql.value.symbolValue -import java.io.ByteArrayOutputStream -import java.io.PrintStream /** * SqlDialect represents the base behavior for transforming an [AstNode] tree into a [SqlBlock] tree. @@ -212,9 +207,9 @@ public abstract class SqlDialect : AstVisitor() { // no params DataType.DATE -> tail concat node.name() // with params - DataType.TIME, DataType.TIMESTAMP -> tail concat type(node.name(), node.precision) - DataType.TIME_WITH_TIME_ZONE -> tail concat type("TIME WITH TIMEZONE", node.precision, gap = true) - DataType.TIMESTAMP_WITH_TIME_ZONE -> tail concat type("TIMESTAMP WITH TIMEZONE", node.precision, gap = true) + DataType.TIME, DataType.TIMESTAMP -> tail concat type(node.name(), node.precision, gap = true) + DataType.TIME_WITH_TIME_ZONE -> tail concat type("TIME", node.precision, gap = true) concat(" WITH TIME ZONE") + DataType.TIMESTAMP_WITH_TIME_ZONE -> tail concat type("TIMESTAMP", node.precision, gap = true) concat(" WITH TIME ZONE") // DataType.INTERVAL -> tail concat type("INTERVAL", node.precision) // @@ -229,20 +224,22 @@ public abstract class SqlDialect : AstVisitor() { // Expressions - @OptIn(PartiQLValueExperimental::class) override fun visitExprLit(node: ExprLit, tail: SqlBlock): SqlBlock { - // Simplified PartiQL Value writing, as this intentionally omits formatting - val value = when (node.value) { - is MissingValue -> "MISSING" // force uppercase - is NullValue -> "NULL" // force uppercase - else -> { - val buffer = ByteArrayOutputStream() - val valueWriter = PartiQLValueTextWriter(PrintStream(buffer), false) - valueWriter.append(node.value) - buffer.toString() + val lit = node.lit + var t = tail + val litText = when (lit.code()) { + Literal.NULL -> "NULL" + Literal.MISSING -> "MISSING" + Literal.BOOL -> lit.booleanValue().toString() + Literal.APPROX_NUM, Literal.EXACT_NUM, Literal.INT_NUM -> lit.numberValue() + Literal.STRING -> String.format("'%s'", lit.stringValue()) + Literal.TYPED_STRING -> { + t = visitDataType(lit.dataType(), t) + String.format(" '%s'", lit.stringValue()) } + else -> error("Unsupported literal type $lit") } - return tail concat value + return t concat litText } override fun visitExprVariant(node: ExprVariant, tail: SqlBlock): SqlBlock { @@ -334,7 +331,6 @@ public abstract class SqlDialect : AstVisitor() { override fun visitPathStepAllFields(node: PathStep.AllFields, tail: SqlBlock): SqlBlock = tail concat ".*" - @OptIn(PartiQLValueExperimental::class) override fun visitExprCall(node: ExprCall, tail: SqlBlock): SqlBlock { var t = tail val f = node.function @@ -344,13 +340,14 @@ public abstract class SqlDialect : AstVisitor() { } // Special case -- DATE_ADD('', , ) -> DATE_ADD(, , ) // Special case -- DATE_DIFF('', , ) -> DATE_DIFF(, , ) - // TODO this will need to be rewritten once PartiQLValue is removed from AST if (f.next == null && (f.root.symbol.uppercase() == "DATE_ADD" || f.root.symbol.uppercase() == "DATE_DIFF") && node.args.size == 3 ) { - val dtField = ((node.args[0] as ExprLit).value as StringValue).value!! - val newArgs = listOf(exprLit(symbolValue(dtField))) + node.args.drop(1) + val dtField = (node.args[0] as ExprLit).lit.stringValue() + // Represent as an `ExprVarRef` to mimic a literal symbol. + // TODO consider some other representation for unquoted strings + val newArgs = listOf(exprVarRef(identifierChain(identifier(dtField, isDelimited = false), next = null), scope = Scope.DEFAULT())) + node.args.drop(1) t = visitIdentifierChain(f, t) t = t concat list { newArgs } return t diff --git a/partiql-ast/src/main/kotlin/org/partiql/ast/Ast.kt b/partiql-ast/src/main/kotlin/org/partiql/ast/Ast.kt index 26ecd2a82..e05dfaefe 100644 --- a/partiql-ast/src/main/kotlin/org/partiql/ast/Ast.kt +++ b/partiql-ast/src/main/kotlin/org/partiql/ast/Ast.kt @@ -53,8 +53,6 @@ import org.partiql.ast.graph.GraphPattern import org.partiql.ast.graph.GraphQuantifier import org.partiql.ast.graph.GraphRestrictor import org.partiql.ast.graph.GraphSelector -import org.partiql.value.PartiQLValue -import org.partiql.value.PartiQLValueExperimental // TODO docs for all factory methods // Also consider defaults for nullable. Need to look more into backwards compatibility. @@ -126,10 +124,8 @@ public object Ast { return ExprLike(value, pattern, escape, not) } - // This representation will be changed in https://github.com/partiql/partiql-lang-kotlin/issues/1589 - @OptIn(PartiQLValueExperimental::class) @JvmStatic - public fun exprLit(value: PartiQLValue): ExprLit { + public fun exprLit(value: Literal): ExprLit { return ExprLit(value) } @@ -393,10 +389,8 @@ public object Ast { return ExcludeStep.CollWildcard() } - // This representation will be changed in https://github.com/partiql/partiql-lang-kotlin/issues/1589 - @OptIn(PartiQLValueExperimental::class) @JvmStatic - public fun explain(options: Map, statement: Statement): Explain { + public fun explain(options: Map, statement: Statement): Explain { return Explain(options, statement) } diff --git a/partiql-ast/src/main/kotlin/org/partiql/ast/AstRewriter.kt b/partiql-ast/src/main/kotlin/org/partiql/ast/AstRewriter.kt index 71472aeac..8a05cdfdb 100644 --- a/partiql-ast/src/main/kotlin/org/partiql/ast/AstRewriter.kt +++ b/partiql-ast/src/main/kotlin/org/partiql/ast/AstRewriter.kt @@ -43,7 +43,6 @@ import org.partiql.ast.graph.GraphMatch import org.partiql.ast.graph.GraphPattern import org.partiql.ast.graph.GraphQuantifier import org.partiql.ast.graph.GraphSelector -import org.partiql.value.PartiQLValueExperimental // TODO docs public abstract class AstRewriter : AstVisitor() { @@ -201,9 +200,7 @@ public abstract class AstRewriter : AstVisitor() { } } - @OptIn(PartiQLValueExperimental::class) override fun visitExprLit(node: ExprLit, ctx: C): AstNode { - val value = node.value return node } @@ -413,6 +410,10 @@ public abstract class AstRewriter : AstVisitor() { } } + override fun visitLiteral(node: Literal, ctx: C): AstNode { + return node + } + override fun visitPathStepField(node: PathStep.Field, ctx: C): AstNode { val field = visitIdentifier(node.field, ctx) as Identifier val next = node.next?.let { visitPathStep(it, ctx) as PathStep? } @@ -552,7 +553,6 @@ public abstract class AstRewriter : AstVisitor() { return node } - @OptIn(PartiQLValueExperimental::class) override fun visitExplain(node: Explain, ctx: C): AstNode { val statement = visitStatement(node.statement, ctx) as Statement return if (statement !== node.statement) { diff --git a/partiql-ast/src/test/kotlin/org/partiql/ast/sql/SqlDialectTest.kt b/partiql-ast/src/test/kotlin/org/partiql/ast/sql/SqlDialectTest.kt index d8bcd4258..62718ea35 100644 --- a/partiql-ast/src/test/kotlin/org/partiql/ast/sql/SqlDialectTest.kt +++ b/partiql-ast/src/test/kotlin/org/partiql/ast/sql/SqlDialectTest.kt @@ -84,6 +84,14 @@ import org.partiql.ast.Identifier import org.partiql.ast.IdentifierChain import org.partiql.ast.JoinType import org.partiql.ast.Let +import org.partiql.ast.Literal.approxNum +import org.partiql.ast.Literal.bool +import org.partiql.ast.Literal.exactNum +import org.partiql.ast.Literal.intNum +import org.partiql.ast.Literal.missing +import org.partiql.ast.Literal.nul +import org.partiql.ast.Literal.string +import org.partiql.ast.Literal.typedString import org.partiql.ast.Nulls import org.partiql.ast.Order import org.partiql.ast.OrderBy @@ -94,27 +102,7 @@ import org.partiql.ast.SetQuantifier import org.partiql.ast.expr.Expr import org.partiql.ast.expr.Scope import org.partiql.ast.expr.TrimSpec -import org.partiql.value.PartiQLValueExperimental -import org.partiql.value.boolValue -import org.partiql.value.dateValue -import org.partiql.value.datetime.DateTimeValue -import org.partiql.value.datetime.TimeZone -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.missingValue -import org.partiql.value.nullValue -import org.partiql.value.stringValue -import org.partiql.value.symbolValue -import org.partiql.value.timeValue -import org.partiql.value.timestampValue import java.math.BigDecimal -import java.math.BigInteger import kotlin.test.assertFails /** @@ -122,7 +110,6 @@ import kotlin.test.assertFails * * It does NOT test formatted output. */ -@OptIn(PartiQLValueExperimental::class) class SqlDialectTest { // Identifiers & Paths @@ -245,7 +232,7 @@ class SqlDialectTest { companion object { - private val NULL = exprLit(nullValue()) + private val NULL = exprLit(nul()) @JvmStatic fun types() = listOf( @@ -272,9 +259,9 @@ class SqlDialectTest { expect("CLOB", DataType.CLOB()), expect("DATE", DataType.DATE()), expect("TIME", DataType.TIME()), - expect("TIME(1)", DataType.TIME(1)), - expect("TIME WITH TIMEZONE", DataType.TIME_WITH_TIME_ZONE()), - expect("TIME WITH TIMEZONE (1)", DataType.TIME_WITH_TIME_ZONE(1)), + expect("TIME (1)", DataType.TIME(1)), + expect("TIME WITH TIME ZONE", DataType.TIME_WITH_TIME_ZONE()), + expect("TIME (1) WITH TIME ZONE", DataType.TIME_WITH_TIME_ZONE(1)), // TODO TIMESTAMP // TODO INTERVAL // TODO other types in `DataType` @@ -415,58 +402,52 @@ class SqlDialectTest { // Expressions - @OptIn(PartiQLValueExperimental::class) @JvmStatic fun exprLitCases() = listOf( expect( - "NULL", exprLit(nullValue()) + "NULL", exprLit(nul()) ), expect( - "MISSING", exprLit(missingValue()) + "MISSING", exprLit(missing()) ), expect( - "true", exprLit(boolValue(true)) + "true", exprLit(bool(true)) ), expect( - "1", exprLit(int8Value(1)) + "1", exprLit(intNum(1)) ), expect( - "2", exprLit(int16Value(2)) + "2", exprLit(intNum(2)) ), expect( - "3", exprLit(int32Value(3)) + "3", exprLit(intNum(3)) ), expect( - "4", exprLit(int64Value(4)) + "4", exprLit(intNum(4)) ), expect( - "5", exprLit(intValue(BigInteger.valueOf(5))) + "5.", exprLit(exactNum("5.")) ), - // TODO fix PartiQL Text writer for floats - // expect("1.1e0") { - expect("1.1", exprLit(float32Value(1.1f))), - // TODO fix PartiQL Text writer for floats - // expect("1.2e0") { + expect("1.1e0", exprLit(approxNum("1.1e0"))), + expect("1.2E0", exprLit(approxNum("1.2E0"))), + expect("1.2345E-5", exprLit(approxNum("1.2345E-5"))), expect( - "1.2", exprLit(float64Value(1.2)) + "1.3", exprLit(exactNum(BigDecimal.valueOf(1.3))) ), expect( - "1.3", exprLit(decimalValue(BigDecimal.valueOf(1.3))) + """'hello'""", exprLit(string("hello")) ), expect( - """'hello'""", exprLit(stringValue("hello")) + """hello""", id("hello") ), expect( - """hello""", exprLit(symbolValue("hello")) + "DATE '0001-02-03'", exprLit(typedString(DataType.DATE(), "0001-02-03")) ), expect( - "DATE '0001-02-03'", exprLit(dateValue(DateTimeValue.date(1, 2, 3))) + "TIME '01:02:03.456-00:30'", exprLit(typedString(DataType.TIME(), "01:02:03.456-00:30")) ), expect( - "TIME '01:02:03.456-00:30'", exprLit(timeValue(DateTimeValue.time(1, 2, BigDecimal.valueOf(3.456), TimeZone.UtcOffset.of(-30)))) - ), - expect( - "TIMESTAMP '0001-02-03 04:05:06.78-00:30'", exprLit(timestampValue(DateTimeValue.timestamp(1, 2, 3, 4, 5, BigDecimal.valueOf(6.78), TimeZone.UtcOffset.of(-30)))) + "TIMESTAMP '0001-02-03 04:05:06.78-00:30'", exprLit(typedString(DataType.TIMESTAMP(), "0001-02-03 04:05:06.78-00:30")) ), // expect("""{{ '''Hello''' '''World''' }}""") { @@ -699,7 +680,7 @@ class SqlDialectTest { next = exprPathStepElement( element = exprOperator( symbol = "+", - lhs = exprLit(int32Value(1)), + lhs = exprLit(intNum(1)), rhs = exprVarRef( identifierChain = idChain(id("a")), scope = Scope.DEFAULT() @@ -716,7 +697,7 @@ class SqlDialectTest { identifierChain = idChain(id("x")), scope = Scope.DEFAULT() ), - next = exprPathStepElement(exprLit(stringValue("y")), next = null) + next = exprPathStepElement(exprLit(string("y")), next = null) ) ), ) @@ -727,7 +708,7 @@ class SqlDialectTest { "foo(1)", exprCall( function = idChain(id("foo")), - args = listOf(exprLit(int32Value(1))), + args = listOf(exprLit(intNum(1))), setq = null ) ), @@ -736,8 +717,8 @@ class SqlDialectTest { exprCall( function = idChain(id("foo")), args = listOf( - exprLit(int32Value(1)), - exprLit(int32Value(2)), + exprLit(intNum(1)), + exprLit(intNum(2)), ), setq = null ) @@ -749,7 +730,7 @@ class SqlDialectTest { root = id("foo"), next = idChain(id("bar")) ), - args = listOf(exprLit(int32Value(1))), + args = listOf(exprLit(intNum(1))), setq = null ) ), @@ -761,8 +742,8 @@ class SqlDialectTest { next = idChain(id("bar")) ), args = listOf( - exprLit(int32Value(1)), - exprLit(int32Value(2)) + exprLit(intNum(1)), + exprLit(intNum(2)) ), setq = null ) @@ -848,9 +829,9 @@ class SqlDialectTest { "<<1, 2, 3>>", exprBag( values = listOf( - exprLit(int32Value(1)), - exprLit(int32Value(2)), - exprLit(int32Value(3)) + exprLit(intNum(1)), + exprLit(intNum(2)), + exprLit(intNum(3)) ) ) ), @@ -862,9 +843,9 @@ class SqlDialectTest { "[1, 2, 3]", exprArray( values = listOf( - exprLit(int32Value(1)), - exprLit(int32Value(2)), - exprLit(int32Value(3)) + exprLit(intNum(1)), + exprLit(intNum(2)), + exprLit(intNum(3)) ) ) ), @@ -884,9 +865,9 @@ class SqlDialectTest { rows = listOf( exprRowValue( values = listOf( - exprLit(int32Value(1)), - exprLit(int32Value(2)), - exprLit(int32Value(3)) + exprLit(intNum(1)), + exprLit(intNum(2)), + exprLit(intNum(3)) ) ) ) @@ -902,9 +883,9 @@ class SqlDialectTest { "(1, 2, 3)", exprRowValue( values = listOf( - exprLit(int32Value(1)), - exprLit(int32Value(2)), - exprLit(int32Value(3)) + exprLit(intNum(1)), + exprLit(intNum(2)), + exprLit(intNum(3)) ) ) ), @@ -919,9 +900,9 @@ class SqlDialectTest { // "SEXP (1, 2, 3)", // exprCollection { // type = Expr.Collection.Type.SEXP -// values += exprLit(int32Value(1)) -// values += exprLit(int32Value(2)) -// values += exprLit(int32Value(3)) +// values += exprLit(integer(1)) +// values += exprLit(integer(2)) +// values += exprLit(integer(3)) // } // ), ) @@ -934,8 +915,8 @@ class SqlDialectTest { exprStruct( fields = listOf( exprStructField( - name = exprLit(symbolValue("a")), - value = exprLit(int32Value(1)) + name = v("a"), + value = exprLit(intNum(1)) ) ) ) @@ -945,12 +926,12 @@ class SqlDialectTest { exprStruct( fields = listOf( exprStructField( - name = exprLit(symbolValue("a")), - value = exprLit(int32Value(1)) + name = v("a"), + value = exprLit(intNum(1)) ), exprStructField( - name = exprLit(symbolValue("b")), - value = exprLit(boolValue(false)) + name = v("b"), + value = exprLit(bool(false)) ) ) ) @@ -1157,7 +1138,7 @@ class SqlDialectTest { exprCall( function = idChain(id("DATE_ADD")), args = listOf( - exprLit(stringValue("MINUTE")), + exprLit(string("MINUTE")), v("x"), v("y") ), @@ -1169,7 +1150,7 @@ class SqlDialectTest { exprCall( function = idChain(id("DATE_DIFF")), args = listOf( - exprLit(stringValue("MINUTE")), + exprLit(string("MINUTE")), v("x"), v("y") ), @@ -1966,7 +1947,7 @@ class SqlDialectTest { select = select("a"), from = table("T") ), - limit = exprLit(int32Value(1)) + limit = exprLit(intNum(1)) ) ), expect( @@ -1976,7 +1957,7 @@ class SqlDialectTest { select = select("a"), from = table("T") ), - offset = exprLit(int32Value(2)) + offset = exprLit(intNum(2)) ) ), expect( @@ -1986,8 +1967,8 @@ class SqlDialectTest { select = select("a"), from = table("T") ), - limit = exprLit(int32Value(1)), - offset = exprLit(int32Value(2)) + limit = exprLit(intNum(1)), + offset = exprLit(intNum(2)) ) ), expect( @@ -2408,7 +2389,7 @@ class SqlDialectTest { ) ) ), - limit = exprLit(int32Value(1)) // LIMIT associated with SQL set op + limit = exprLit(intNum(1)) // LIMIT associated with SQL set op ) ), expect( @@ -2428,7 +2409,7 @@ class SqlDialectTest { select = select("b"), from = table("S"), ), - limit = exprLit(int32Value(1)) // LIMIT associated with rhs SFW query + limit = exprLit(intNum(1)) // LIMIT associated with rhs SFW query ) ) ) @@ -2556,7 +2537,7 @@ class SqlDialectTest { "1 = (SELECT a FROM T)", exprOperator( symbol = "=", - lhs = exprLit(int32Value(1)), + lhs = exprLit(intNum(1)), rhs = qSet( body = sfw( select = select("a"), @@ -2571,8 +2552,8 @@ class SqlDialectTest { symbol = "=", lhs = exprRowValue( values = listOf( - exprLit(int32Value(1)), - exprLit(int32Value(2)) + exprLit(intNum(1)), + exprLit(intNum(2)) ) ), rhs = qSet( diff --git a/partiql-parser/build.gradle.kts b/partiql-parser/build.gradle.kts index 46c5032e8..eb98d6ad8 100644 --- a/partiql-parser/build.gradle.kts +++ b/partiql-parser/build.gradle.kts @@ -22,6 +22,7 @@ plugins { dependencies { antlr(Deps.antlr) api(project(":partiql-ast")) + api(project(":partiql-spi")) api(project(":partiql-types")) implementation(Deps.ionElement) shadow(Deps.antlrRuntime) diff --git a/partiql-parser/src/main/antlr/PartiQLParser.g4 b/partiql-parser/src/main/antlr/PartiQLParser.g4 index 41453f3ad..a56fbad35 100644 --- a/partiql-parser/src/main/antlr/PartiQLParser.g4 +++ b/partiql-parser/src/main/antlr/PartiQLParser.g4 @@ -879,6 +879,7 @@ literal | LITERAL_STRING # LiteralString | LITERAL_INTEGER # LiteralInteger | LITERAL_DECIMAL # LiteralDecimal + | LITERAL_FLOAT # LiteralFloat | ION_CLOSURE # LiteralIon | DATE LITERAL_STRING # LiteralDate | TIME ( PAREN_LEFT LITERAL_INTEGER PAREN_RIGHT )? (WITH TIME ZONE)? LITERAL_STRING # LiteralTime diff --git a/partiql-parser/src/main/antlr/PartiQLTokens.g4 b/partiql-parser/src/main/antlr/PartiQLTokens.g4 index 830baebe3..654803a21 100644 --- a/partiql-parser/src/main/antlr/PartiQLTokens.g4 +++ b/partiql-parser/src/main/antlr/PartiQLTokens.g4 @@ -391,12 +391,16 @@ LITERAL_STRING : '\'' ( ('\'\'') | ~('\'') )* '\''; LITERAL_INTEGER - : DIGIT DIGIT*; + : DIGIT+; -LITERAL_DECIMAL: - DIGIT+ '.' DIGIT* ([e] [+-]? DIGIT+)? - | '.' DIGIT DIGIT* ([e] [+-]? DIGIT+)? - | DIGIT DIGIT* ([e] [+-]? DIGIT+)? +LITERAL_DECIMAL + : DIGIT+ '.' DIGIT* + | '.' DIGIT+ + ; + +LITERAL_FLOAT + : DIGIT+ ('.' DIGIT*)? 'E' [+-]? DIGIT+ + | '.' DIGIT+ 'E' [+-]? DIGIT+ ; IDENTIFIER diff --git a/partiql-parser/src/main/kotlin/org/partiql/parser/internal/PartiQLParserDefault.kt b/partiql-parser/src/main/kotlin/org/partiql/parser/internal/PartiQLParserDefault.kt index e5a468f6c..6d551db5c 100644 --- a/partiql-parser/src/main/kotlin/org/partiql/parser/internal/PartiQLParserDefault.kt +++ b/partiql-parser/src/main/kotlin/org/partiql/parser/internal/PartiQLParserDefault.kt @@ -134,6 +134,14 @@ import org.partiql.ast.Identifier import org.partiql.ast.IdentifierChain import org.partiql.ast.JoinType import org.partiql.ast.Let +import org.partiql.ast.Literal.approxNum +import org.partiql.ast.Literal.bool +import org.partiql.ast.Literal.exactNum +import org.partiql.ast.Literal.intNum +import org.partiql.ast.Literal.missing +import org.partiql.ast.Literal.nul +import org.partiql.ast.Literal.string +import org.partiql.ast.Literal.typedString import org.partiql.ast.Nulls import org.partiql.ast.Order import org.partiql.ast.Select @@ -168,7 +176,6 @@ import org.partiql.parser.PartiQLLexerException import org.partiql.parser.PartiQLParser import org.partiql.parser.PartiQLParserException import org.partiql.parser.internal.antlr.PartiQLParserBaseVisitor -import org.partiql.parser.internal.util.DateTimeUtils import org.partiql.spi.Context import org.partiql.spi.SourceLocation import org.partiql.spi.SourceLocations @@ -176,29 +183,9 @@ import org.partiql.spi.errors.PError import org.partiql.spi.errors.PErrorKind import org.partiql.spi.errors.PErrorListener import org.partiql.spi.errors.PErrorListenerException -import org.partiql.value.PartiQLValueExperimental -import org.partiql.value.boolValue -import org.partiql.value.dateValue -import org.partiql.value.datetime.DateTimeException -import org.partiql.value.datetime.DateTimeValue -import org.partiql.value.decimalValue -import org.partiql.value.int32Value -import org.partiql.value.int64Value -import org.partiql.value.intValue -import org.partiql.value.missingValue -import org.partiql.value.nullValue -import org.partiql.value.stringValue -import org.partiql.value.timeValue -import org.partiql.value.timestampValue -import java.math.BigDecimal import java.math.BigInteger -import java.math.MathContext -import java.math.RoundingMode import java.nio.channels.ClosedByInterruptException import java.nio.charset.StandardCharsets -import java.time.LocalDate -import java.time.format.DateTimeFormatter -import java.time.format.DateTimeParseException import org.partiql.parser.internal.antlr.PartiQLParser as GeneratedParser import org.partiql.parser.internal.antlr.PartiQLTokens as GeneratedLexer @@ -220,7 +207,6 @@ import org.partiql.parser.internal.antlr.PartiQLTokens as GeneratedLexer */ internal class PartiQLParserDefault : PartiQLParser { - @OptIn(PartiQLValueExperimental::class) @Throws(PErrorListenerException::class) override fun parse(source: String, ctx: Context): PartiQLParser.Result { try { @@ -232,7 +218,7 @@ internal class PartiQLParserDefault : PartiQLParser { ctx.errorListener.report(error) val locations = SourceLocations() return PartiQLParser.Result( - mutableListOf(org.partiql.ast.Query(org.partiql.ast.expr.ExprLit(nullValue()))) as List, + mutableListOf(org.partiql.ast.Query(exprLit(nul()))) as List, locations ) } @@ -362,7 +348,6 @@ internal class PartiQLParserDefault : PartiQLParser { /** * Translate an ANTLR ParseTree to a PartiQL AST */ - @OptIn(PartiQLValueExperimental::class) private class Visitor( private val tokens: CommonTokenStream, private val locations: MutableMap, @@ -427,6 +412,8 @@ internal class PartiQLParserDefault : PartiQLParser { internal val DATE_PATTERN_REGEX = Regex("\\d\\d\\d\\d-\\d\\d-\\d\\d") internal val GENERIC_TIME_REGEX = Regex("\\d\\d:\\d\\d:\\d\\d(\\.\\d*)?([+|-]\\d\\d:\\d\\d)?") + + internal val GENERIC_TIMESTAMP_REGEX = Regex("\\d\\d\\d\\d-\\d\\d-\\d\\d\\s\\d\\d:\\d\\d:\\d\\d(\\.\\d*)?([+|-]\\d\\d:\\d\\d)?") } /** @@ -475,10 +462,9 @@ internal class PartiQLParserDefault : PartiQLParser { } } explain( - // TODO get rid of usage of PartiQLValue https://github.com/partiql/partiql-lang-kotlin/issues/1589 options = mapOf( - "type" to stringValue(type), - "format" to stringValue(format) + "type" to (type?.let { string(it) } ?: nul()), + "format" to (format?.let { string(it) } ?: nul()) ), statement = visit(ctx.statement()) as Statement, ) @@ -1736,7 +1722,6 @@ internal class PartiQLParserDefault : PartiQLParser { } val lhs = visitExpr(ctx.expr(0)) val rhs = visitExpr(ctx.expr(1)) - // TODO change to not use PartiQLValue -- https://github.com/partiql/partiql-lang-kotlin/issues/1589 val fieldLit = ctx.dt.text.lowercase() // TODO error on invalid datetime fields like TIMEZONE_HOUR and TIMEZONE_MINUTE when { @@ -1879,13 +1864,11 @@ internal class PartiQLParserDefault : PartiQLParser { } override fun visitLiteralDecimal(ctx: GeneratedParser.LiteralDecimalContext) = translate(ctx) { - val decimal = try { - val v = ctx.LITERAL_DECIMAL().text.trim() - BigDecimal(v, MathContext(38, RoundingMode.HALF_EVEN)) - } catch (e: NumberFormatException) { - throw error(ctx, "Invalid decimal literal", e) - } - exprLit(decimalValue(decimal)) + exprLit(exactNum(ctx.text)) + } + + override fun visitLiteralFloat(ctx: GeneratedParser.LiteralFloatContext) = translate(ctx) { + exprLit(approxNum(ctx.text)) } override fun visitArray(ctx: GeneratedParser.ArrayContext) = translate(ctx) { @@ -1894,19 +1877,19 @@ internal class PartiQLParserDefault : PartiQLParser { } override fun visitLiteralNull(ctx: GeneratedParser.LiteralNullContext) = translate(ctx) { - exprLit(nullValue()) + exprLit(nul()) } override fun visitLiteralMissing(ctx: GeneratedParser.LiteralMissingContext) = translate(ctx) { - exprLit(missingValue()) + exprLit(missing()) } override fun visitLiteralTrue(ctx: GeneratedParser.LiteralTrueContext) = translate(ctx) { - exprLit(boolValue(true)) + exprLit(bool(true)) } override fun visitLiteralFalse(ctx: GeneratedParser.LiteralFalseContext) = translate(ctx) { - exprLit(boolValue(false)) + exprLit(bool(false)) } override fun visitLiteralIon(ctx: GeneratedParser.LiteralIonContext) = translate(ctx) { @@ -1917,35 +1900,12 @@ internal class PartiQLParserDefault : PartiQLParser { override fun visitLiteralString(ctx: GeneratedParser.LiteralStringContext) = translate(ctx) { val value = ctx.LITERAL_STRING().getStringValue() - exprLit(stringValue(value)) + exprLit(string(value)) } override fun visitLiteralInteger(ctx: GeneratedParser.LiteralIntegerContext) = translate(ctx) { val n = ctx.LITERAL_INTEGER().text - - // 1st, try parse as int - try { - val v = n.toInt(10) - return@translate exprLit(int32Value(v)) - } catch (ex: NumberFormatException) { - // ignore - } - - // 2nd, try parse as long - try { - val v = n.toLong(10) - return@translate exprLit(int64Value(v)) - } catch (ex: NumberFormatException) { - // ignore - } - - // 3rd, try parse as BigInteger - try { - val v = BigInteger(n) - return@translate exprLit(intValue(v)) - } catch (ex: NumberFormatException) { - throw ex - } + exprLit(intNum(n)) } override fun visitLiteralDate(ctx: GeneratedParser.LiteralDateContext) = translate(ctx) { @@ -1954,37 +1914,67 @@ internal class PartiQLParserDefault : PartiQLParser { if (DATE_PATTERN_REGEX.matches(dateString).not()) { throw error(pattern, "Expected DATE string to be of the format yyyy-MM-dd") } - val value = try { - LocalDate.parse(dateString, DateTimeFormatter.ISO_LOCAL_DATE) - } catch (e: DateTimeParseException) { - throw error(pattern, e.localizedMessage, e) - } catch (e: IndexOutOfBoundsException) { - throw error(pattern, e.localizedMessage, e) - } - val date = DateTimeValue.date(value.year, value.monthValue, value.dayOfMonth) - exprLit(dateValue(date)) + exprLit(typedString(DataType.DATE(), dateString)) } override fun visitLiteralTime(ctx: GeneratedParser.LiteralTimeContext) = translate(ctx) { - val (timeString, precision) = getTimeStringAndPrecision(ctx.LITERAL_STRING(), ctx.LITERAL_INTEGER()) - val time = try { - DateTimeUtils.parseTimeLiteral(timeString) - } catch (e: DateTimeException) { - throw error(ctx, "Invalid Date Time Literal", e) + val pattern = ctx.LITERAL_STRING().symbol + val timeString = ctx.LITERAL_STRING().getStringValue() + if (GENERIC_TIME_REGEX.matches(timeString).not()) { + throw error(pattern, "Expected TIME string to be of the format HH:mm:ss[.SSS]") + } + val precision = ctx.LITERAL_INTEGER()?.let { + val p = it.text.toBigInteger().toInt() + if (p < 0 || 9 < p) throw error(it.symbol, "Precision out of bounds") + p + } + val type = when (ctx.ZONE()) { + null -> { + if (precision == null) { + DataType.TIME() + } else { + DataType.TIME(precision) + } + } + else -> { + if (precision == null) { + DataType.TIME_WITH_TIME_ZONE() + } else { + DataType.TIME_WITH_TIME_ZONE(precision) + } + } } - val value = time.toPrecision(precision) - exprLit(timeValue(value)) + exprLit(typedString(type, timeString)) } override fun visitLiteralTimestamp(ctx: GeneratedParser.LiteralTimestampContext) = translate(ctx) { - val (timeString, precision) = getTimeStringAndPrecision(ctx.LITERAL_STRING(), ctx.LITERAL_INTEGER()) - val timestamp = try { - DateTimeUtils.parseTimestamp(timeString) - } catch (e: DateTimeException) { - throw error(ctx, "Invalid Date Time Literal", e) + val pattern = ctx.LITERAL_STRING().symbol + val timestampString = ctx.LITERAL_STRING().getStringValue() + if (GENERIC_TIMESTAMP_REGEX.matches(timestampString).not()) { + throw error(pattern, "Expected TIMESTAMP string to be of the format yyyy-MM-dd HH:mm:ss[.SSS]") + } + val precision = ctx.LITERAL_INTEGER()?.let { + val p = it.text.toBigInteger().toInt() + if (p < 0 || 9 < p) throw error(it.symbol, "Precision out of bounds") + p + } + val type = when (ctx.ZONE()) { + null -> { + if (precision == null) { + DataType.TIMESTAMP() + } else { + DataType.TIMESTAMP(precision) + } + } + else -> { + if (precision == null) { + DataType.TIMESTAMP_WITH_TIME_ZONE() + } else { + DataType.TIMESTAMP_WITH_TIME_ZONE(precision) + } + } } - val value = timestamp.toPrecision(precision) - exprLit(timestampValue(value)) + exprLit(typedString(type, timestampString)) } override fun visitTuple(ctx: GeneratedParser.TupleContext) = translate(ctx) { @@ -2202,41 +2192,6 @@ internal class PartiQLParserDefault : PartiQLParser { else -> throw error(ctx, "Expected set quantifier ALL or DISTINCT") } - /** - * With the and nodes of a literal time expression, returns the parsed string and precision. - * TIME ()? (WITH TIME ZONE)? - */ - private fun getTimeStringAndPrecision( - stringNode: TerminalNode, - integerNode: TerminalNode?, - ): Pair { - val timeString = stringNode.getStringValue() - val precision = when (integerNode) { - null -> { - try { - getPrecisionFromTimeString(timeString) - } catch (e: Exception) { - throw error(stringNode.symbol, "Unable to parse precision.", e) - } - } - else -> { - val p = integerNode.text.toBigInteger().toInt() - if (p < 0 || 9 < p) throw error(integerNode.symbol, "Precision out of bounds") - p - } - } - return timeString to precision - } - - private fun getPrecisionFromTimeString(timeString: String): Int { - val matcher = GENERIC_TIME_REGEX.toPattern().matcher(timeString) - if (!matcher.find()) { - throw IllegalArgumentException("Time string does not match the format 'HH:MM:SS[.ddd....][+|-HH:MM]'") - } - val fraction = matcher.group(1)?.removePrefix(".") - return fraction?.length ?: 0 - } - /** * Converts a Path expression into a Projection Item (either ALL or EXPR). Note: A Projection Item only allows a * subset of a typical Path expressions. See the following examples. diff --git a/partiql-parser/src/test/kotlin/org/partiql/parser/internal/PartiQLParserBagOpTests.kt b/partiql-parser/src/test/kotlin/org/partiql/parser/internal/PartiQLParserBagOpTests.kt index e7a3aa9d8..9b67e6d25 100644 --- a/partiql-parser/src/test/kotlin/org/partiql/parser/internal/PartiQLParserBagOpTests.kt +++ b/partiql-parser/src/test/kotlin/org/partiql/parser/internal/PartiQLParserBagOpTests.kt @@ -15,13 +15,12 @@ import org.partiql.ast.Ast.selectStar import org.partiql.ast.Ast.setOp import org.partiql.ast.AstNode import org.partiql.ast.FromType +import org.partiql.ast.Literal.intNum +import org.partiql.ast.Literal.string import org.partiql.ast.SetOpType import org.partiql.ast.SetQuantifier import org.partiql.ast.expr.Expr import org.partiql.ast.expr.ExprQuerySet -import org.partiql.value.PartiQLValueExperimental -import org.partiql.value.int32Value -import org.partiql.value.stringValue import kotlin.test.assertEquals class PartiQLParserBagOpTests { @@ -30,7 +29,6 @@ class PartiQLParserBagOpTests { private fun queryBody(body: () -> Expr) = query(body()) - @OptIn(PartiQLValueExperimental::class) private fun createSFW(i: Int): ExprQuerySet = exprQuerySet( body = queryBodySFW( @@ -43,8 +41,8 @@ class PartiQLParserBagOpTests { exprStruct( fields = mutableListOf( exprStructField( - name = exprLit(value = stringValue("a")), - value = exprLit(value = int32Value(i)) + name = exprLit(string("a")), + value = exprLit(intNum(i)) ) ) ) @@ -67,8 +65,7 @@ class PartiQLParserBagOpTests { offset = null ) - @OptIn(PartiQLValueExperimental::class) - private fun createLit(i: Int) = exprLit(int32Value(i)) + private fun createLit(i: Int) = exprLit(intNum(i)) // SQL Union @Test diff --git a/partiql-parser/src/test/kotlin/org/partiql/parser/internal/PartiQLParserDDLTests.kt b/partiql-parser/src/test/kotlin/org/partiql/parser/internal/PartiQLParserDDLTests.kt index 51a24ac1d..4999e5b88 100644 --- a/partiql-parser/src/test/kotlin/org/partiql/parser/internal/PartiQLParserDDLTests.kt +++ b/partiql-parser/src/test/kotlin/org/partiql/parser/internal/PartiQLParserDDLTests.kt @@ -20,9 +20,9 @@ import org.partiql.ast.Ast.partitionBy import org.partiql.ast.Ast.tableConstraintUnique import org.partiql.ast.AstNode import org.partiql.ast.DataType +import org.partiql.ast.Literal.intNum import org.partiql.ast.expr.Scope import org.partiql.value.PartiQLValueExperimental -import org.partiql.value.int32Value import java.util.stream.Stream import kotlin.test.assertEquals @@ -238,7 +238,7 @@ class PartiQLParserDDLTests { Scope.DEFAULT() ), exprLit( - int32Value(0) + intNum(0) ) ) ) diff --git a/partiql-parser/src/test/kotlin/org/partiql/parser/internal/PartiQLParserOperatorTests.kt b/partiql-parser/src/test/kotlin/org/partiql/parser/internal/PartiQLParserOperatorTests.kt index 3742b57b1..fa75371bf 100644 --- a/partiql-parser/src/test/kotlin/org/partiql/parser/internal/PartiQLParserOperatorTests.kt +++ b/partiql-parser/src/test/kotlin/org/partiql/parser/internal/PartiQLParserOperatorTests.kt @@ -5,12 +5,10 @@ import org.partiql.ast.Ast.exprLit import org.partiql.ast.Ast.exprOperator import org.partiql.ast.Ast.query import org.partiql.ast.AstNode +import org.partiql.ast.Literal.intNum import org.partiql.ast.expr.Expr -import org.partiql.value.PartiQLValueExperimental -import org.partiql.value.int32Value import kotlin.test.assertEquals -@OptIn(PartiQLValueExperimental::class) class PartiQLParserOperatorTests { private val parser = PartiQLParserDefault() @@ -24,7 +22,7 @@ class PartiQLParserOperatorTests { exprOperator( symbol = "-", lhs = null, - rhs = exprLit(int32Value(2)) + rhs = exprLit(intNum(2)) ) } ) @@ -35,8 +33,8 @@ class PartiQLParserOperatorTests { queryBody { exprOperator( symbol = "<=", - lhs = exprLit(int32Value(1)), - rhs = exprLit(int32Value(2)) + lhs = exprLit(intNum(1)), + rhs = exprLit(intNum(2)) ) } ) @@ -48,7 +46,7 @@ class PartiQLParserOperatorTests { exprOperator( symbol = "==!", lhs = null, - rhs = exprLit(int32Value(2)) + rhs = exprLit(intNum(2)) ) } ) @@ -59,8 +57,8 @@ class PartiQLParserOperatorTests { queryBody { exprOperator( symbol = "==!", - lhs = exprLit(int32Value(1)), - rhs = exprLit(int32Value(2)) + lhs = exprLit(intNum(1)), + rhs = exprLit(intNum(2)) ) } ) diff --git a/partiql-parser/src/test/kotlin/org/partiql/parser/internal/PartiQLParserSessionAttributeTests.kt b/partiql-parser/src/test/kotlin/org/partiql/parser/internal/PartiQLParserSessionAttributeTests.kt index 09c9f0dc6..2fe4f8b7d 100644 --- a/partiql-parser/src/test/kotlin/org/partiql/parser/internal/PartiQLParserSessionAttributeTests.kt +++ b/partiql-parser/src/test/kotlin/org/partiql/parser/internal/PartiQLParserSessionAttributeTests.kt @@ -6,13 +6,11 @@ import org.partiql.ast.Ast.exprOperator import org.partiql.ast.Ast.exprSessionAttribute import org.partiql.ast.Ast.query import org.partiql.ast.AstNode +import org.partiql.ast.Literal.intNum import org.partiql.ast.expr.Expr import org.partiql.ast.expr.SessionAttribute -import org.partiql.value.PartiQLValueExperimental -import org.partiql.value.int32Value import kotlin.test.assertEquals -@OptIn(PartiQLValueExperimental::class) class PartiQLParserSessionAttributeTests { private val parser = PartiQLParserDefault() @@ -49,7 +47,7 @@ class PartiQLParserSessionAttributeTests { queryBody { exprOperator( symbol = "=", - lhs = exprLit(int32Value(1)), + lhs = exprLit(intNum(1)), rhs = exprSessionAttribute(SessionAttribute.CURRENT_USER()) ) } diff --git a/partiql-planner/src/main/kotlin/org/partiql/planner/internal/helpers/ToBinder.kt b/partiql-planner/src/main/kotlin/org/partiql/planner/internal/helpers/ToBinder.kt index 316693c48..899f2f424 100644 --- a/partiql-planner/src/main/kotlin/org/partiql/planner/internal/helpers/ToBinder.kt +++ b/partiql-planner/src/main/kotlin/org/partiql/planner/internal/helpers/ToBinder.kt @@ -3,6 +3,7 @@ package org.partiql.planner.internal.helpers import org.partiql.ast.Ast.identifier import org.partiql.ast.Identifier import org.partiql.ast.IdentifierChain +import org.partiql.ast.Literal import org.partiql.ast.expr.Expr import org.partiql.ast.expr.ExprCast import org.partiql.ast.expr.ExprLit @@ -10,8 +11,6 @@ import org.partiql.ast.expr.ExprPath import org.partiql.ast.expr.ExprSessionAttribute import org.partiql.ast.expr.ExprVarRef import org.partiql.ast.expr.PathStep -import org.partiql.value.PartiQLValueExperimental -import org.partiql.value.StringValue private val col = { index: () -> Int -> "_${index()}" } @@ -58,7 +57,6 @@ private fun IdentifierChain.toBinder(): Identifier { private fun Identifier.toBinder(): Identifier = symbol.toBinder() -@OptIn(PartiQLValueExperimental::class) private fun ExprPath.toBinder(index: () -> Int): Identifier { if (next == null) return root.toBinder(index) var cur = next @@ -71,8 +69,8 @@ private fun ExprPath.toBinder(index: () -> Int): Identifier { is PathStep.Field -> prev.field.toBinder() is PathStep.Element -> { val k = prev.element - if (k is ExprLit && k.value is StringValue) { - (k.value as StringValue).value!!.toBinder() + if (k is ExprLit && k.lit.code() == Literal.STRING) { + k.lit.stringValue().toBinder() } else { col(index).toBinder() } diff --git a/partiql-planner/src/main/kotlin/org/partiql/planner/internal/transforms/NormalizeSelect.kt b/partiql-planner/src/main/kotlin/org/partiql/planner/internal/transforms/NormalizeSelect.kt index cd01ce157..072172f6e 100644 --- a/partiql-planner/src/main/kotlin/org/partiql/planner/internal/transforms/NormalizeSelect.kt +++ b/partiql-planner/src/main/kotlin/org/partiql/planner/internal/transforms/NormalizeSelect.kt @@ -37,6 +37,7 @@ import org.partiql.ast.FromExpr import org.partiql.ast.FromJoin import org.partiql.ast.FromTableRef import org.partiql.ast.GroupBy +import org.partiql.ast.Literal.string import org.partiql.ast.QueryBody import org.partiql.ast.SelectItem import org.partiql.ast.SelectList @@ -44,14 +45,11 @@ import org.partiql.ast.SelectStar import org.partiql.ast.SelectValue import org.partiql.ast.expr.Expr import org.partiql.ast.expr.ExprCase -import org.partiql.ast.expr.ExprLit import org.partiql.ast.expr.ExprQuerySet import org.partiql.ast.expr.ExprStruct import org.partiql.ast.expr.ExprVarRef import org.partiql.ast.expr.Scope import org.partiql.planner.internal.helpers.toBinder -import org.partiql.value.PartiQLValueExperimental -import org.partiql.value.stringValue /** * Converts SQL-style SELECT to PartiQL SELECT VALUE. @@ -315,12 +313,11 @@ internal object NormalizeSelect { ) } - @OptIn(PartiQLValueExperimental::class) private fun visitSelectProjectWithoutProjectAll(node: SelectList): SelectValue { val structFields = node.items.map { item -> val itemExpr = item as? SelectItem.Expr ?: error("Expected the projection to be an expression.") exprStructField( - name = exprLit(stringValue(itemExpr.asAlias?.symbol!!)), + name = exprLit(string(itemExpr.asAlias?.symbol!!)), value = item.expr ) } @@ -343,19 +340,17 @@ internal object NormalizeSelect { defaultExpr = buildSimpleStruct(expr, col(index)) ) - @OptIn(PartiQLValueExperimental::class) private fun buildSimpleStruct(expr: Expr, name: String): ExprStruct = exprStruct( fields = listOf( exprStructField( - name = exprLit(stringValue(name)), + name = exprLit(string(name)), value = expr ) ) ) - @OptIn(PartiQLValueExperimental::class) private fun structField(name: String, expr: Expr): ExprStruct.Field = exprStructField( - name = ExprLit(stringValue(name)), + name = exprLit(string(name)), value = expr ) 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 fe68632bf..bbc99472d 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 @@ -33,6 +33,7 @@ import org.partiql.ast.GroupBy import org.partiql.ast.GroupByStrategy import org.partiql.ast.IdentifierChain import org.partiql.ast.JoinType +import org.partiql.ast.Literal.intNum import org.partiql.ast.Nulls import org.partiql.ast.Order import org.partiql.ast.OrderBy @@ -88,7 +89,6 @@ import org.partiql.planner.internal.typer.CompilerType 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 /** @@ -439,7 +439,7 @@ internal object RelConverter { relOpAggregateCallUnresolved( name, org.partiql.planner.internal.ir.SetQuantifier.ALL, - args = listOf(exprLit(int32Value(1)).toRex(env)) + args = listOf(exprLit(intNum(1)).toRex(env)) ) } else { val setq = when (expr.setq?.code()) { 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 a8dbb5bd9..d5c382ad3 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 @@ -20,6 +20,7 @@ import com.amazon.ionelement.api.loadSingleElement import org.partiql.ast.AstNode import org.partiql.ast.AstVisitor import org.partiql.ast.DataType +import org.partiql.ast.Literal import org.partiql.ast.QueryBody import org.partiql.ast.SelectList import org.partiql.ast.SelectStar @@ -84,19 +85,31 @@ import org.partiql.planner.internal.ir.rexOpVarLocal 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.planner.internal.utils.DateTimeUtils import org.partiql.spi.catalog.Identifier import org.partiql.types.PType import org.partiql.value.DecimalValue -import org.partiql.value.MissingValue import org.partiql.value.PartiQLValue import org.partiql.value.PartiQLValueExperimental -import org.partiql.value.StringValue import org.partiql.value.boolValue +import org.partiql.value.dateValue +import org.partiql.value.datetime.DateTimeValue +import org.partiql.value.decimalValue +import org.partiql.value.float64Value import org.partiql.value.int32Value import org.partiql.value.int64Value +import org.partiql.value.intValue import org.partiql.value.io.PartiQLValueIonReaderBuilder +import org.partiql.value.missingValue import org.partiql.value.nullValue import org.partiql.value.stringValue +import org.partiql.value.timeValue +import org.partiql.value.timestampValue +import java.math.BigInteger +import java.math.MathContext +import java.math.RoundingMode +import java.time.LocalDate +import java.time.format.DateTimeFormatter import org.partiql.ast.SetQuantifier as AstSetQuantifier /** @@ -127,28 +140,99 @@ internal object RexConverter { throw IllegalArgumentException("unsupported rex $node") override fun visitExprLit(node: ExprLit, context: Env): Rex { - val type = when (val value = node.value) { + val pValue = node.lit.toPartiQLValue() + val type = when (pValue) { // Specifically handle the lack of an infinite precision/scale // TODO: PartiQLValue won't be in AST soon is DecimalValue -> { - when (val decimal = value.value) { + when (val decimal = pValue.value) { null -> PType.decimal(38, 19) else -> PType.decimal(decimal.precision(), decimal.scale()) } } - else -> value.type.toPType() + else -> pValue.type.toPType() } val cType = CompilerType( _delegate = type, - isNullValue = node.value.isNull, - isMissingValue = node.value is MissingValue + isNullValue = node.lit.code() == Literal.NULL, + isMissingValue = node.lit.code() == Literal.MISSING ) - val op = rexOpLit(node.value) + val op = rexOpLit(pValue) return rex(cType, op) } - private fun PartiQLValue.toPType(): PType { - return this.type.toPType() + private fun Literal.toPartiQLValue(): PartiQLValue { + val lit = this + return when (lit.code()) { + Literal.NULL -> nullValue() + Literal.MISSING -> missingValue() + Literal.STRING -> stringValue(lit.stringValue()) + Literal.BOOL -> boolValue(lit.booleanValue()) + Literal.EXACT_NUM -> { + // TODO previous behavior inferred decimals with scale = 0 to be a PartiQLValue.IntValue with + // PType of numeric. Since we're keeping numeric and decimal, need to take another look at + // whether the literal should have type decimal or numeric. + val dec = lit.bigDecimalValue().round(MathContext(38, RoundingMode.HALF_EVEN)) + if (dec.scale() == 0) { + intValue(dec.toBigInteger()) + } else { + decimalValue(dec) + } + } + Literal.INT_NUM -> { + val n = lit.numberValue() + // 1st, try parse as int + try { + val v = n.toInt(10) + return int32Value(v) + } catch (ex: NumberFormatException) { + // ignore + } + + // 2nd, try parse as long + try { + val v = n.toLong(10) + return int64Value(v) + } catch (ex: NumberFormatException) { + // ignore + } + + // 3rd, try parse as BigInteger + try { + val v = BigInteger(n) + return intValue(v) + } catch (ex: NumberFormatException) { + throw ex + } + } + Literal.APPROX_NUM -> { + float64Value(lit.numberValue().toDouble()) + } + Literal.TYPED_STRING -> { + val type = this.dataType() + val typedString = this.stringValue() + when (type.code()) { + DataType.DATE -> { + val value = LocalDate.parse(typedString, DateTimeFormatter.ISO_LOCAL_DATE) + val date = DateTimeValue.date(value.year, value.monthValue, value.dayOfMonth) + dateValue(date) + } + DataType.TIME, DataType.TIME_WITH_TIME_ZONE -> { + val time = DateTimeUtils.parseTimeLiteral(typedString) + val precision = type.precision ?: 6 + timeValue(time.toPrecision(precision)) + } + DataType.TIMESTAMP, DataType.TIMESTAMP_WITH_TIME_ZONE -> { + val timestamp = DateTimeUtils.parseTimestamp(typedString) + val precision = type.precision ?: 6 + val value = timestamp.toPrecision(precision) + timestampValue(value) + } + else -> error("Unsupported typed literal string: $this") + } + } + else -> error("Unsupported literal: $this") + } } /** @@ -391,8 +475,8 @@ internal object RexConverter { is PathStep.Element -> { val key = visitExprCoerce(curStep.element, context) val op = when (val astKey = curStep.element) { - is ExprLit -> when (astKey.value) { - is StringValue -> rexOpPathKey(curPathNavi, key) + is ExprLit -> when (astKey.lit.code()) { + Literal.STRING -> rexOpPathKey(curPathNavi, key) else -> rexOpPathIndex(curPathNavi, key) } is ExprCast -> when (astKey.asType.code() == DataType.STRING) { diff --git a/partiql-parser/src/main/kotlin/org/partiql/parser/internal/util/DateTimeUtils.kt b/partiql-planner/src/main/kotlin/org/partiql/planner/internal/utils/DateTimeUtils.kt similarity index 98% rename from partiql-parser/src/main/kotlin/org/partiql/parser/internal/util/DateTimeUtils.kt rename to partiql-planner/src/main/kotlin/org/partiql/planner/internal/utils/DateTimeUtils.kt index 02fa77c48..1cd4dabc0 100644 --- a/partiql-parser/src/main/kotlin/org/partiql/parser/internal/util/DateTimeUtils.kt +++ b/partiql-planner/src/main/kotlin/org/partiql/planner/internal/utils/DateTimeUtils.kt @@ -1,4 +1,4 @@ -package org.partiql.parser.internal.util +package org.partiql.planner.internal.utils import org.partiql.value.datetime.Date import org.partiql.value.datetime.DateTimeException diff --git a/partiql-planner/src/test/kotlin/org/partiql/planner/internal/transforms/NormalizeSelectTest.kt b/partiql-planner/src/test/kotlin/org/partiql/planner/internal/transforms/NormalizeSelectTest.kt index 7e9f7fee8..d68173229 100644 --- a/partiql-planner/src/test/kotlin/org/partiql/planner/internal/transforms/NormalizeSelectTest.kt +++ b/partiql-planner/src/test/kotlin/org/partiql/planner/internal/transforms/NormalizeSelectTest.kt @@ -15,12 +15,11 @@ import org.partiql.ast.Ast.selectItemExpr import org.partiql.ast.Ast.selectList import org.partiql.ast.Ast.selectValue import org.partiql.ast.FromType +import org.partiql.ast.Literal.intNum +import org.partiql.ast.Literal.string import org.partiql.ast.SelectItem import org.partiql.ast.expr.Expr import org.partiql.ast.expr.Scope -import org.partiql.value.PartiQLValueExperimental -import org.partiql.value.int32Value -import org.partiql.value.stringValue import kotlin.test.assertEquals class NormalizeSelectTest { @@ -166,7 +165,6 @@ class NormalizeSelectTest { orderBy = null ) - @OptIn(PartiQLValueExperimental::class) private fun selectValue(vararg items: Pair) = exprQuerySet( body = queryBodySFW( @@ -174,7 +172,7 @@ class NormalizeSelectTest { constructor = exprStruct( items.map { exprStructField( - name = exprLit(stringValue(it.first)), + name = exprLit(string(it.first)), value = it.second ) } @@ -221,6 +219,5 @@ class NormalizeSelectTest { asAlias = asAlias?.let { identifier(asAlias, isDelimited = false) } ) - @OptIn(PartiQLValueExperimental::class) - private fun lit(value: Int) = exprLit(int32Value(value)) + private fun lit(value: Int) = exprLit(intNum(value)) }