From d25de2d50265b5f9c3447a1bfe1cf489a05a8eac Mon Sep 17 00:00:00 2001 From: John Ed Quinn Date: Sat, 27 Apr 2024 09:46:52 -0700 Subject: [PATCH] Initializes PQLValue and adds functionality to evaluator --- partiql-eval/api/partiql-eval.api | 49 ++ partiql-eval/build.gradle.kts | 9 + .../main/java/org/partiql/eval/BagValue.java | 37 ++ .../java/org/partiql/eval/BinaryValue.java | 34 + .../main/java/org/partiql/eval/BlobValue.java | 34 + .../main/java/org/partiql/eval/BoolValue.java | 32 + .../main/java/org/partiql/eval/ByteValue.java | 32 + .../main/java/org/partiql/eval/CharValue.java | 36 + .../main/java/org/partiql/eval/ClobValue.java | 34 + .../main/java/org/partiql/eval/DateValue.java | 38 ++ .../partiql/eval/DecimalArbitraryValue.java | 36 + .../java/org/partiql/eval/DecimalValue.java | 36 + .../java/org/partiql/eval/Float32Value.java | 32 + .../java/org/partiql/eval/Float64Value.java | 32 + .../java/org/partiql/eval/Int16Value.java | 32 + .../java/org/partiql/eval/Int32Value.java | 31 + .../java/org/partiql/eval/Int64Value.java | 32 + .../main/java/org/partiql/eval/Int8Value.java | 32 + .../main/java/org/partiql/eval/IntValue.java | 36 + .../java/org/partiql/eval/IntervalValue.java | 32 + .../eval/IterableFromIteratorSupplier.java | 20 + .../main/java/org/partiql/eval/ListValue.java | 37 ++ .../java/org/partiql/eval/MissingValue.java | 24 + .../main/java/org/partiql/eval/NullValue.java | 280 ++++++++ .../partiql/eval/PQLToPartiQLIterator.java | 26 + .../main/java/org/partiql/eval/PQLValue.java | 615 ++++++++++++++++++ .../eval/PartiQLValueIterableWrapper.java | 37 ++ .../main/java/org/partiql/eval/SexpValue.java | 37 ++ .../java/org/partiql/eval/StringValue.java | 34 + .../java/org/partiql/eval/StructField.java | 44 ++ .../eval/StructFieldIterableWrapper.java | 50 ++ .../java/org/partiql/eval/StructValue.java | 37 ++ .../java/org/partiql/eval/SymbolValue.java | 34 + .../main/java/org/partiql/eval/TimeValue.java | 37 ++ .../java/org/partiql/eval/TimestampValue.java | 38 ++ .../org/partiql/eval/PartiQLEngineDefault.kt | 2 +- .../org/partiql/eval/internal/Compiler.kt | 3 +- .../org/partiql/eval/internal/Environment.kt | 15 +- .../org/partiql/eval/internal/Record.kt | 10 +- .../eval/internal/helpers/IteratorSupplier.kt | 7 + .../internal/helpers/RecordValueIterator.kt | 8 +- .../eval/internal/helpers/ValueUtility.kt | 97 +++ .../eval/internal/operator/Operator.kt | 4 +- .../internal/operator/rel/RelAggregate.kt | 12 +- .../eval/internal/operator/rel/RelDistinct.kt | 13 +- .../eval/internal/operator/rel/RelExclude.kt | 74 +-- .../eval/internal/operator/rel/RelFilter.kt | 6 +- .../operator/rel/RelJoinNestedLoop.kt | 28 +- .../eval/internal/operator/rel/RelLimit.kt | 9 +- .../eval/internal/operator/rel/RelOffset.kt | 9 +- .../eval/internal/operator/rel/RelProject.kt | 2 - .../eval/internal/operator/rel/RelScan.kt | 8 +- .../internal/operator/rel/RelScanIndexed.kt | 17 +- .../operator/rel/RelScanIndexedPermissive.kt | 22 +- .../operator/rel/RelScanPermissive.kt | 8 +- .../eval/internal/operator/rel/RelSort.kt | 5 +- .../eval/internal/operator/rel/RelUnpivot.kt | 33 +- .../internal/operator/rex/ExprCallDynamic.kt | 16 +- .../internal/operator/rex/ExprCallStatic.kt | 10 +- .../eval/internal/operator/rex/ExprCase.kt | 10 +- .../eval/internal/operator/rex/ExprCast.kt | 8 +- .../internal/operator/rex/ExprCoalesce.kt | 7 +- .../internal/operator/rex/ExprCollection.kt | 13 +- .../eval/internal/operator/rex/ExprLiteral.kt | 6 +- .../eval/internal/operator/rex/ExprMissing.kt | 6 +- .../eval/internal/operator/rex/ExprNullIf.kt | 8 +- .../internal/operator/rex/ExprPathIndex.kt | 38 +- .../eval/internal/operator/rex/ExprPathKey.kt | 25 +- .../internal/operator/rex/ExprPathSymbol.kt | 19 +- .../internal/operator/rex/ExprPermissive.kt | 11 +- .../eval/internal/operator/rex/ExprPivot.kt | 20 +- .../operator/rex/ExprPivotPermissive.kt | 24 +- .../eval/internal/operator/rex/ExprSelect.kt | 21 +- .../eval/internal/operator/rex/ExprStruct.kt | 28 +- .../internal/operator/rex/ExprSubquery.kt | 29 +- .../internal/operator/rex/ExprTupleUnion.kt | 24 +- .../internal/operator/rex/ExprVarGlobal.kt | 5 +- .../internal/operator/rex/ExprVarLocal.kt | 4 +- .../internal/operator/rex/ExprVarOuter.kt | 4 +- .../operator/rex/ExprCallDynamicTest.kt | 24 +- .../kotlin/org/partiql/value/PartiQLValue.kt | 10 +- 81 files changed, 2437 insertions(+), 341 deletions(-) create mode 100644 partiql-eval/src/main/java/org/partiql/eval/BagValue.java create mode 100644 partiql-eval/src/main/java/org/partiql/eval/BinaryValue.java create mode 100644 partiql-eval/src/main/java/org/partiql/eval/BlobValue.java create mode 100644 partiql-eval/src/main/java/org/partiql/eval/BoolValue.java create mode 100644 partiql-eval/src/main/java/org/partiql/eval/ByteValue.java create mode 100644 partiql-eval/src/main/java/org/partiql/eval/CharValue.java create mode 100644 partiql-eval/src/main/java/org/partiql/eval/ClobValue.java create mode 100644 partiql-eval/src/main/java/org/partiql/eval/DateValue.java create mode 100644 partiql-eval/src/main/java/org/partiql/eval/DecimalArbitraryValue.java create mode 100644 partiql-eval/src/main/java/org/partiql/eval/DecimalValue.java create mode 100644 partiql-eval/src/main/java/org/partiql/eval/Float32Value.java create mode 100644 partiql-eval/src/main/java/org/partiql/eval/Float64Value.java create mode 100644 partiql-eval/src/main/java/org/partiql/eval/Int16Value.java create mode 100644 partiql-eval/src/main/java/org/partiql/eval/Int32Value.java create mode 100644 partiql-eval/src/main/java/org/partiql/eval/Int64Value.java create mode 100644 partiql-eval/src/main/java/org/partiql/eval/Int8Value.java create mode 100644 partiql-eval/src/main/java/org/partiql/eval/IntValue.java create mode 100644 partiql-eval/src/main/java/org/partiql/eval/IntervalValue.java create mode 100644 partiql-eval/src/main/java/org/partiql/eval/IterableFromIteratorSupplier.java create mode 100644 partiql-eval/src/main/java/org/partiql/eval/ListValue.java create mode 100644 partiql-eval/src/main/java/org/partiql/eval/MissingValue.java create mode 100644 partiql-eval/src/main/java/org/partiql/eval/NullValue.java create mode 100644 partiql-eval/src/main/java/org/partiql/eval/PQLToPartiQLIterator.java create mode 100644 partiql-eval/src/main/java/org/partiql/eval/PQLValue.java create mode 100644 partiql-eval/src/main/java/org/partiql/eval/PartiQLValueIterableWrapper.java create mode 100644 partiql-eval/src/main/java/org/partiql/eval/SexpValue.java create mode 100644 partiql-eval/src/main/java/org/partiql/eval/StringValue.java create mode 100644 partiql-eval/src/main/java/org/partiql/eval/StructField.java create mode 100644 partiql-eval/src/main/java/org/partiql/eval/StructFieldIterableWrapper.java create mode 100644 partiql-eval/src/main/java/org/partiql/eval/StructValue.java create mode 100644 partiql-eval/src/main/java/org/partiql/eval/SymbolValue.java create mode 100644 partiql-eval/src/main/java/org/partiql/eval/TimeValue.java create mode 100644 partiql-eval/src/main/java/org/partiql/eval/TimestampValue.java create mode 100644 partiql-eval/src/main/kotlin/org/partiql/eval/internal/helpers/IteratorSupplier.kt create mode 100644 partiql-eval/src/main/kotlin/org/partiql/eval/internal/helpers/ValueUtility.kt diff --git a/partiql-eval/api/partiql-eval.api b/partiql-eval/api/partiql-eval.api index fa8ddf39c..00263dd6b 100644 --- a/partiql-eval/api/partiql-eval.api +++ b/partiql-eval/api/partiql-eval.api @@ -1,3 +1,46 @@ +public abstract interface class org/partiql/eval/PQLValue { + public static fun bagValue (Ljava/lang/Iterable;)Lorg/partiql/eval/PQLValue; + public static fun boolValue (Z)Lorg/partiql/eval/PQLValue; + public fun getBagValues ()Ljava/util/Iterator; + public fun getBinaryValue ()[B + public fun getBlobValue ()[B + public fun getBoolValue ()Z + public fun getByteValue ()B + public fun getCharValue ()Ljava/lang/String; + public fun getClobValue ()[B + public fun getDateValue ()Lorg/partiql/value/datetime/Date; + public fun getDecimalArbitraryValue ()Ljava/math/BigDecimal; + public fun getDecimalValue ()Ljava/math/BigDecimal; + public fun getFloat32Value ()F + public fun getFloat64Value ()D + public fun getInt16Value ()S + public fun getInt32Value ()I + public fun getInt64Value ()J + public fun getInt8Value ()B + public fun getIntValue ()Ljava/math/BigInteger; + public fun getIntervalValue ()J + public fun getListValues ()Ljava/util/Iterator; + public fun getSexpValues ()Ljava/util/Iterator; + public fun getStringValue ()Ljava/lang/String; + public fun getStructFields ()Ljava/util/Iterator; + public fun getSymbolValue ()Ljava/lang/String; + public fun getTimeValue ()Lorg/partiql/value/datetime/Time; + public fun getTimestampValue ()Lorg/partiql/value/datetime/Timestamp; + public abstract fun getType ()Lorg/partiql/value/PartiQLValueType; + public static fun int32Value (I)Lorg/partiql/eval/PQLValue; + public static fun int64Value (J)Lorg/partiql/eval/PQLValue; + public abstract fun isNull ()Z + public static fun listValue (Ljava/lang/Iterable;)Lorg/partiql/eval/PQLValue; + public static fun missingValue ()Lorg/partiql/eval/PQLValue; + public static fun nullValue ()Lorg/partiql/eval/PQLValue; + public static fun nullValue (Lorg/partiql/value/PartiQLValueType;)Lorg/partiql/eval/PQLValue; + public static fun of (Lorg/partiql/value/PartiQLValue;)Lorg/partiql/eval/PQLValue; + public static fun sexpValue (Ljava/lang/Iterable;)Lorg/partiql/eval/PQLValue; + public static fun stringValue (Ljava/lang/String;)Lorg/partiql/eval/PQLValue; + public static fun structValue (Ljava/lang/Iterable;)Lorg/partiql/eval/PQLValue; + public fun toPartiQLValue ()Lorg/partiql/value/PartiQLValue; +} + public abstract interface class org/partiql/eval/PartiQLEngine { public static final field Companion Lorg/partiql/eval/PartiQLEngine$Companion; public static fun builder ()Lorg/partiql/eval/PartiQLEngineBuilder; @@ -63,3 +106,9 @@ public abstract interface class org/partiql/eval/PartiQLStatement { public abstract interface class org/partiql/eval/PartiQLStatement$Query : org/partiql/eval/PartiQLStatement { } +public abstract interface class org/partiql/eval/StructField { + public abstract fun getName ()Ljava/lang/String; + public abstract fun getValue ()Lorg/partiql/eval/PQLValue; + public static fun of (Ljava/lang/String;Lorg/partiql/eval/PQLValue;)Lorg/partiql/eval/StructField; +} + diff --git a/partiql-eval/build.gradle.kts b/partiql-eval/build.gradle.kts index 7778690e1..e9ad56502 100644 --- a/partiql-eval/build.gradle.kts +++ b/partiql-eval/build.gradle.kts @@ -43,6 +43,15 @@ kotlin { explicitApi = null } +// Need to add this as we have both Java and Kotlin sources. Dokka already handles multi-language projects. If +// Javadoc is enabled, we end up overwriting index.html (causing compilation errors). +tasks.withType() { + enabled = false +} +tasks.withType() { + duplicatesStrategy = DuplicatesStrategy.EXCLUDE +} + publish { artifactId = "partiql-eval" name = "PartiQL Lang Kotlin Evaluator" diff --git a/partiql-eval/src/main/java/org/partiql/eval/BagValue.java b/partiql-eval/src/main/java/org/partiql/eval/BagValue.java new file mode 100644 index 000000000..b48251ad7 --- /dev/null +++ b/partiql-eval/src/main/java/org/partiql/eval/BagValue.java @@ -0,0 +1,37 @@ +package org.partiql.eval; + +import org.jetbrains.annotations.NotNull; +import org.partiql.value.PartiQLValueType; + +import java.util.Collections; +import java.util.Iterator; + +/** + * This shall always be package-private (internal). + */ +class BagValue implements PQLValue { + + @NotNull + final Iterable _value; + + BagValue(@NotNull Iterable value) { + _value = value; + } + + @Override + public boolean isNull() { + return false; + } + + @Override + @NotNull + public Iterator getBagValues() { + return _value.iterator(); + } + + @NotNull + @Override + public PartiQLValueType getType() { + return PartiQLValueType.BAG; + } +} diff --git a/partiql-eval/src/main/java/org/partiql/eval/BinaryValue.java b/partiql-eval/src/main/java/org/partiql/eval/BinaryValue.java new file mode 100644 index 000000000..2b5077c54 --- /dev/null +++ b/partiql-eval/src/main/java/org/partiql/eval/BinaryValue.java @@ -0,0 +1,34 @@ +package org.partiql.eval; + +import org.jetbrains.annotations.NotNull; +import org.partiql.value.PartiQLValueType; + +/** + * This shall always be package-private (internal). + */ +class BinaryValue implements PQLValue { + + @NotNull + final byte[] _value; + + BinaryValue(@NotNull byte[] value) { + _value = value; + } + + @Override + public boolean isNull() { + return false; + } + + @Override + @NotNull + public byte[] getBinaryValue() { + return _value; + } + + @NotNull + @Override + public PartiQLValueType getType() { + return PartiQLValueType.BINARY; + } +} diff --git a/partiql-eval/src/main/java/org/partiql/eval/BlobValue.java b/partiql-eval/src/main/java/org/partiql/eval/BlobValue.java new file mode 100644 index 000000000..7af3f7186 --- /dev/null +++ b/partiql-eval/src/main/java/org/partiql/eval/BlobValue.java @@ -0,0 +1,34 @@ +package org.partiql.eval; + +import org.jetbrains.annotations.NotNull; +import org.partiql.value.PartiQLValueType; + +/** + * This shall always be package-private (internal). + */ +class BlobValue implements PQLValue { + + @NotNull + final byte[] _value; + + BlobValue(@NotNull byte[] value) { + _value = value; + } + + @Override + public boolean isNull() { + return false; + } + + @Override + @NotNull + public byte[] getBlobValue() { + return _value; + } + + @NotNull + @Override + public PartiQLValueType getType() { + return PartiQLValueType.BLOB; + } +} diff --git a/partiql-eval/src/main/java/org/partiql/eval/BoolValue.java b/partiql-eval/src/main/java/org/partiql/eval/BoolValue.java new file mode 100644 index 000000000..11b7719fb --- /dev/null +++ b/partiql-eval/src/main/java/org/partiql/eval/BoolValue.java @@ -0,0 +1,32 @@ +package org.partiql.eval; + +import org.jetbrains.annotations.NotNull; +import org.partiql.value.PartiQLValueType; + +/** + * This shall always be package-private (internal). + */ +class BoolValue implements PQLValue { + + final boolean _value; + + BoolValue(boolean value) { + _value = value; + } + + @Override + public boolean isNull() { + return false; + } + + @Override + public boolean getBoolValue() { + return _value; + } + + @NotNull + @Override + public PartiQLValueType getType() { + return PartiQLValueType.BOOL; + } +} diff --git a/partiql-eval/src/main/java/org/partiql/eval/ByteValue.java b/partiql-eval/src/main/java/org/partiql/eval/ByteValue.java new file mode 100644 index 000000000..87be9f13b --- /dev/null +++ b/partiql-eval/src/main/java/org/partiql/eval/ByteValue.java @@ -0,0 +1,32 @@ +package org.partiql.eval; + +import org.jetbrains.annotations.NotNull; +import org.partiql.value.PartiQLValueType; + +/** + * This shall always be package-private (internal). + */ +class ByteValue implements PQLValue { + + final byte _value; + + ByteValue(byte value) { + _value = value; + } + + @Override + public boolean isNull() { + return false; + } + + @Override + public byte getByteValue() { + return _value; + } + + @NotNull + @Override + public PartiQLValueType getType() { + return PartiQLValueType.BYTE; + } +} diff --git a/partiql-eval/src/main/java/org/partiql/eval/CharValue.java b/partiql-eval/src/main/java/org/partiql/eval/CharValue.java new file mode 100644 index 000000000..504d7ff16 --- /dev/null +++ b/partiql-eval/src/main/java/org/partiql/eval/CharValue.java @@ -0,0 +1,36 @@ +package org.partiql.eval; + +import org.jetbrains.annotations.NotNull; +import org.partiql.value.PartiQLValueType; + +import java.math.BigInteger; + +/** + * This shall always be package-private (internal). + */ +class CharValue implements PQLValue { + + @NotNull + final String _value; + + CharValue(@NotNull String value) { + _value = value; + } + + @Override + public boolean isNull() { + return false; + } + + @Override + @NotNull + public String getCharValue() { + return _value; + } + + @NotNull + @Override + public PartiQLValueType getType() { + return PartiQLValueType.CHAR; + } +} diff --git a/partiql-eval/src/main/java/org/partiql/eval/ClobValue.java b/partiql-eval/src/main/java/org/partiql/eval/ClobValue.java new file mode 100644 index 000000000..938b48976 --- /dev/null +++ b/partiql-eval/src/main/java/org/partiql/eval/ClobValue.java @@ -0,0 +1,34 @@ +package org.partiql.eval; + +import org.jetbrains.annotations.NotNull; +import org.partiql.value.PartiQLValueType; + +/** + * This shall always be package-private (internal). + */ +class ClobValue implements PQLValue { + + @NotNull + final byte[] _value; + + ClobValue(@NotNull byte[] value) { + _value = value; + } + + @Override + public boolean isNull() { + return false; + } + + @Override + @NotNull + public byte[] getClobValue() { + return _value; + } + + @NotNull + @Override + public PartiQLValueType getType() { + return PartiQLValueType.CLOB; + } +} diff --git a/partiql-eval/src/main/java/org/partiql/eval/DateValue.java b/partiql-eval/src/main/java/org/partiql/eval/DateValue.java new file mode 100644 index 000000000..4ff7c37a5 --- /dev/null +++ b/partiql-eval/src/main/java/org/partiql/eval/DateValue.java @@ -0,0 +1,38 @@ +package org.partiql.eval; + +import org.jetbrains.annotations.NotNull; +import org.partiql.value.PartiQLValueType; +import org.partiql.value.datetime.Date; +import org.partiql.value.datetime.Time; + +import java.util.Objects; + +/** + * This shall always be package-private (internal). + */ +class DateValue implements PQLValue { + + @NotNull + final Date _value; + + DateValue(@NotNull Date value) { + _value = value; + } + + @Override + public boolean isNull() { + return false; + } + + @Override + @NotNull + public Date getDateValue() { + return _value; + } + + @NotNull + @Override + public PartiQLValueType getType() { + return PartiQLValueType.DATE; + } +} diff --git a/partiql-eval/src/main/java/org/partiql/eval/DecimalArbitraryValue.java b/partiql-eval/src/main/java/org/partiql/eval/DecimalArbitraryValue.java new file mode 100644 index 000000000..94272e38d --- /dev/null +++ b/partiql-eval/src/main/java/org/partiql/eval/DecimalArbitraryValue.java @@ -0,0 +1,36 @@ +package org.partiql.eval; + +import org.jetbrains.annotations.NotNull; +import org.partiql.value.PartiQLValueType; + +import java.math.BigDecimal; + +/** + * This shall always be package-private (internal). + */ +class DecimalArbitraryValue implements PQLValue { + + @NotNull + final BigDecimal _value; + + DecimalArbitraryValue(@NotNull BigDecimal value) { + _value = value; + } + + @Override + public boolean isNull() { + return false; + } + + @Override + @NotNull + public BigDecimal getDecimalArbitraryValue() { + return _value; + } + + @NotNull + @Override + public PartiQLValueType getType() { + return PartiQLValueType.DECIMAL_ARBITRARY; + } +} diff --git a/partiql-eval/src/main/java/org/partiql/eval/DecimalValue.java b/partiql-eval/src/main/java/org/partiql/eval/DecimalValue.java new file mode 100644 index 000000000..37985e773 --- /dev/null +++ b/partiql-eval/src/main/java/org/partiql/eval/DecimalValue.java @@ -0,0 +1,36 @@ +package org.partiql.eval; + +import org.jetbrains.annotations.NotNull; +import org.partiql.value.PartiQLValueType; + +import java.math.BigDecimal; + +/** + * This shall always be package-private (internal). + */ +class DecimalValue implements PQLValue { + + @NotNull + final BigDecimal _value; + + DecimalValue(@NotNull BigDecimal value) { + _value = value; + } + + @Override + public boolean isNull() { + return false; + } + + @Override + @NotNull + public BigDecimal getDecimalValue() { + return _value; + } + + @NotNull + @Override + public PartiQLValueType getType() { + return PartiQLValueType.DECIMAL; + } +} diff --git a/partiql-eval/src/main/java/org/partiql/eval/Float32Value.java b/partiql-eval/src/main/java/org/partiql/eval/Float32Value.java new file mode 100644 index 000000000..09cdc2056 --- /dev/null +++ b/partiql-eval/src/main/java/org/partiql/eval/Float32Value.java @@ -0,0 +1,32 @@ +package org.partiql.eval; + +import org.jetbrains.annotations.NotNull; +import org.partiql.value.PartiQLValueType; + +/** + * This shall always be package-private (internal). + */ +class Float32Value implements PQLValue { + + final float _value; + + Float32Value(float value) { + _value = value; + } + + @Override + public boolean isNull() { + return false; + } + + @Override + public float getFloat32Value() { + return _value; + } + + @NotNull + @Override + public PartiQLValueType getType() { + return PartiQLValueType.FLOAT32; + } +} diff --git a/partiql-eval/src/main/java/org/partiql/eval/Float64Value.java b/partiql-eval/src/main/java/org/partiql/eval/Float64Value.java new file mode 100644 index 000000000..7fcbbc1b6 --- /dev/null +++ b/partiql-eval/src/main/java/org/partiql/eval/Float64Value.java @@ -0,0 +1,32 @@ +package org.partiql.eval; + +import org.jetbrains.annotations.NotNull; +import org.partiql.value.PartiQLValueType; + +/** + * This shall always be package-private (internal). + */ +class Float64Value implements PQLValue { + + final double _value; + + Float64Value(double value) { + _value = value; + } + + @Override + public boolean isNull() { + return false; + } + + @Override + public double getFloat64Value() { + return _value; + } + + @NotNull + @Override + public PartiQLValueType getType() { + return PartiQLValueType.FLOAT64; + } +} diff --git a/partiql-eval/src/main/java/org/partiql/eval/Int16Value.java b/partiql-eval/src/main/java/org/partiql/eval/Int16Value.java new file mode 100644 index 000000000..97228831f --- /dev/null +++ b/partiql-eval/src/main/java/org/partiql/eval/Int16Value.java @@ -0,0 +1,32 @@ +package org.partiql.eval; + +import org.jetbrains.annotations.NotNull; +import org.partiql.value.PartiQLValueType; + +/** + * This shall always be package-private (internal). + */ +class Int16Value implements PQLValue { + + final short _value; + + Int16Value(short value) { + _value = value; + } + + @Override + public boolean isNull() { + return false; + } + + @Override + public short getInt16Value() { + return _value; + } + + @NotNull + @Override + public PartiQLValueType getType() { + return PartiQLValueType.INT16; + } +} diff --git a/partiql-eval/src/main/java/org/partiql/eval/Int32Value.java b/partiql-eval/src/main/java/org/partiql/eval/Int32Value.java new file mode 100644 index 000000000..f143b87d6 --- /dev/null +++ b/partiql-eval/src/main/java/org/partiql/eval/Int32Value.java @@ -0,0 +1,31 @@ +package org.partiql.eval; + +import org.jetbrains.annotations.NotNull; +import org.partiql.value.PartiQLValueType; + +/** + * This shall always be package-private (internal). + */ +class Int32Value implements PQLValue { + + final int _value; + Int32Value(int value) { + _value = value; + } + + @Override + public boolean isNull() { + return false; + } + + @Override + public int getInt32Value() { + return _value; + } + + @NotNull + @Override + public PartiQLValueType getType() { + return PartiQLValueType.INT32; + } +} diff --git a/partiql-eval/src/main/java/org/partiql/eval/Int64Value.java b/partiql-eval/src/main/java/org/partiql/eval/Int64Value.java new file mode 100644 index 000000000..8645a0c2f --- /dev/null +++ b/partiql-eval/src/main/java/org/partiql/eval/Int64Value.java @@ -0,0 +1,32 @@ +package org.partiql.eval; + +import org.jetbrains.annotations.NotNull; +import org.partiql.value.PartiQLValueType; + +/** + * This shall always be package-private (internal). + */ +class Int64Value implements PQLValue { + + final long _value; + + Int64Value(long value) { + _value = value; + } + + @Override + public boolean isNull() { + return false; + } + + @Override + public long getInt64Value() { + return _value; + } + + @NotNull + @Override + public PartiQLValueType getType() { + return PartiQLValueType.INT64; + } +} diff --git a/partiql-eval/src/main/java/org/partiql/eval/Int8Value.java b/partiql-eval/src/main/java/org/partiql/eval/Int8Value.java new file mode 100644 index 000000000..b1e45463d --- /dev/null +++ b/partiql-eval/src/main/java/org/partiql/eval/Int8Value.java @@ -0,0 +1,32 @@ +package org.partiql.eval; + +import org.jetbrains.annotations.NotNull; +import org.partiql.value.PartiQLValueType; + +/** + * This shall always be package-private (internal). + */ +class Int8Value implements PQLValue { + + final byte _value; + + Int8Value(byte value) { + _value = value; + } + + @Override + public boolean isNull() { + return false; + } + + @Override + public byte getInt8Value() { + return _value; + } + + @NotNull + @Override + public PartiQLValueType getType() { + return PartiQLValueType.BYTE; + } +} diff --git a/partiql-eval/src/main/java/org/partiql/eval/IntValue.java b/partiql-eval/src/main/java/org/partiql/eval/IntValue.java new file mode 100644 index 000000000..8a818aef0 --- /dev/null +++ b/partiql-eval/src/main/java/org/partiql/eval/IntValue.java @@ -0,0 +1,36 @@ +package org.partiql.eval; + +import org.jetbrains.annotations.NotNull; +import org.partiql.value.PartiQLValueType; + +import java.math.BigInteger; + +/** + * This shall always be package-private (internal). + */ +class IntValue implements PQLValue { + + @NotNull + final BigInteger _value; + + IntValue(@NotNull BigInteger value) { + _value = value; + } + + @Override + public boolean isNull() { + return false; + } + + @Override + @NotNull + public BigInteger getIntValue() { + return _value; + } + + @NotNull + @Override + public PartiQLValueType getType() { + return PartiQLValueType.INT; + } +} diff --git a/partiql-eval/src/main/java/org/partiql/eval/IntervalValue.java b/partiql-eval/src/main/java/org/partiql/eval/IntervalValue.java new file mode 100644 index 000000000..d777ac340 --- /dev/null +++ b/partiql-eval/src/main/java/org/partiql/eval/IntervalValue.java @@ -0,0 +1,32 @@ +package org.partiql.eval; + +import org.jetbrains.annotations.NotNull; +import org.partiql.value.PartiQLValueType; + +/** + * This shall always be package-private (internal). + */ +class IntervalValue implements PQLValue { + + final long _value; + + IntervalValue(long value) { + _value = value; + } + + @Override + public boolean isNull() { + return false; + } + + @Override + public long getIntervalValue() { + return _value; + } + + @NotNull + @Override + public PartiQLValueType getType() { + return PartiQLValueType.INTERVAL; + } +} diff --git a/partiql-eval/src/main/java/org/partiql/eval/IterableFromIteratorSupplier.java b/partiql-eval/src/main/java/org/partiql/eval/IterableFromIteratorSupplier.java new file mode 100644 index 000000000..6f33b75e3 --- /dev/null +++ b/partiql-eval/src/main/java/org/partiql/eval/IterableFromIteratorSupplier.java @@ -0,0 +1,20 @@ +package org.partiql.eval; + +import org.jetbrains.annotations.NotNull; + +import java.util.Iterator; +import java.util.function.Supplier; + +class IterableFromIteratorSupplier implements Iterable{ + @NotNull + final Supplier> _supplier; + + public IterableFromIteratorSupplier(@NotNull Supplier> supplier) { + _supplier = supplier; + } + + @Override + public Iterator iterator() { + return _supplier.get(); + } +} diff --git a/partiql-eval/src/main/java/org/partiql/eval/ListValue.java b/partiql-eval/src/main/java/org/partiql/eval/ListValue.java new file mode 100644 index 000000000..7020b244e --- /dev/null +++ b/partiql-eval/src/main/java/org/partiql/eval/ListValue.java @@ -0,0 +1,37 @@ +package org.partiql.eval; + +import org.jetbrains.annotations.NotNull; +import org.partiql.value.PartiQLValueType; + +import java.util.Collections; +import java.util.Iterator; + +/** + * This shall always be package-private (internal). + */ +class ListValue implements PQLValue { + + @NotNull + final Iterable _value; + + ListValue(@NotNull Iterable value) { + _value = value; + } + + @Override + public boolean isNull() { + return false; + } + + @Override + @NotNull + public Iterator getListValues() { + return _value.iterator(); + } + + @NotNull + @Override + public PartiQLValueType getType() { + return PartiQLValueType.LIST; + } +} diff --git a/partiql-eval/src/main/java/org/partiql/eval/MissingValue.java b/partiql-eval/src/main/java/org/partiql/eval/MissingValue.java new file mode 100644 index 000000000..be71c2247 --- /dev/null +++ b/partiql-eval/src/main/java/org/partiql/eval/MissingValue.java @@ -0,0 +1,24 @@ +package org.partiql.eval; + +import org.jetbrains.annotations.NotNull; +import org.partiql.value.PartiQLValueType; + +/** + * This shall always be package-private (internal). + */ +class MissingValue implements PQLValue { + + + MissingValue() {} + + @Override + public boolean isNull() { + return false; + } + + @NotNull + @Override + public PartiQLValueType getType() { + return PartiQLValueType.MISSING; + } +} diff --git a/partiql-eval/src/main/java/org/partiql/eval/NullValue.java b/partiql-eval/src/main/java/org/partiql/eval/NullValue.java new file mode 100644 index 000000000..f2833f026 --- /dev/null +++ b/partiql-eval/src/main/java/org/partiql/eval/NullValue.java @@ -0,0 +1,280 @@ +package org.partiql.eval; + +import org.jetbrains.annotations.NotNull; +import org.partiql.value.PartiQLValueType; +import org.partiql.value.datetime.Date; +import org.partiql.value.datetime.Time; +import org.partiql.value.datetime.Timestamp; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.Iterator; + +/** + * This shall always be package-private (internal). + */ +class NullValue implements PQLValue { + + @NotNull + final PartiQLValueType _type; + + NullValue() { + this._type = PartiQLValueType.NULL; + } + + NullValue(@NotNull PartiQLValueType type) { + this._type = type; + } + + @Override + public boolean isNull() { + return true; + } + + @NotNull + @Override + public PartiQLValueType getType() { + return _type; + } + + @Override + public boolean getBoolValue() { + if (_type == PartiQLValueType.BOOL) { + throw new NullPointerException(); + } else { + throw new UnsupportedOperationException(); + } + } + + @Override + public byte getInt8Value() { + if (_type == PartiQLValueType.INT8) { + throw new NullPointerException(); + } else { + throw new UnsupportedOperationException(); + } + } + + @Override + public short getInt16Value() { + if (_type == PartiQLValueType.INT16) { + throw new NullPointerException(); + } else { + throw new UnsupportedOperationException(); + } + } + + @Override + public int getInt32Value() { + if (_type == PartiQLValueType.INT32) { + throw new NullPointerException(); + } else { + throw new UnsupportedOperationException(); + } + } + + @Override + public long getInt64Value() { + if (_type == PartiQLValueType.INT64) { + throw new NullPointerException(); + } else { + throw new UnsupportedOperationException(); + } + } + + @NotNull + @Override + public BigInteger getIntValue() { + if (_type == PartiQLValueType.INT) { + throw new NullPointerException(); + } else { + throw new UnsupportedOperationException(); + } + } + + @NotNull + @Override + public BigDecimal getDecimalValue() { + if (_type == PartiQLValueType.DECIMAL) { + throw new NullPointerException(); + } else { + throw new UnsupportedOperationException(); + } + } + + @NotNull + @Override + public BigDecimal getDecimalArbitraryValue() { + if (_type == PartiQLValueType.DECIMAL_ARBITRARY) { + throw new NullPointerException(); + } else { + throw new UnsupportedOperationException(); + } + } + + @Override + public byte getByteValue() { + if (_type == PartiQLValueType.BYTE) { + throw new NullPointerException(); + } else { + throw new UnsupportedOperationException(); + } + } + + @NotNull + @Override + public byte[] getBinaryValue() { + if (_type == PartiQLValueType.BINARY) { + throw new NullPointerException(); + } else { + throw new UnsupportedOperationException(); + } + } + + @NotNull + @Override + public byte[] getBlobValue() { + if (_type == PartiQLValueType.BLOB) { + throw new NullPointerException(); + } else { + throw new UnsupportedOperationException(); + } + } + + @NotNull + @Override + public byte[] getClobValue() { + if (_type == PartiQLValueType.CLOB) { + throw new NullPointerException(); + } else { + throw new UnsupportedOperationException(); + } + } + + @NotNull + @Override + public Date getDateValue() { + if (_type == PartiQLValueType.DATE) { + throw new NullPointerException(); + } else { + throw new UnsupportedOperationException(); + } + } + + @Override + public double getFloat64Value() { + if (_type == PartiQLValueType.FLOAT64) { + throw new NullPointerException(); + } else { + throw new UnsupportedOperationException(); + } + } + + @Override + public float getFloat32Value() { + if (_type == PartiQLValueType.FLOAT32) { + throw new NullPointerException(); + } else { + throw new UnsupportedOperationException(); + } + } + + @NotNull + @Override + public Iterator getBagValues() { + if (_type == PartiQLValueType.BAG) { + throw new NullPointerException(); + } else { + throw new UnsupportedOperationException(); + } + } + + @NotNull + @Override + public Iterator getListValues() { + if (_type == PartiQLValueType.LIST) { + throw new NullPointerException(); + } else { + throw new UnsupportedOperationException(); + } + } + + @NotNull + @Override + public Iterator getSexpValues() { + if (_type == PartiQLValueType.SEXP) { + throw new NullPointerException(); + } else { + throw new UnsupportedOperationException(); + } + } + + @NotNull + @Override + public Iterator getStructFields() { + if (_type == PartiQLValueType.STRUCT) { + throw new NullPointerException(); + } else { + throw new UnsupportedOperationException(); + } + } + + @NotNull + @Override + public String getCharValue() { + if (_type == PartiQLValueType.CHAR) { + throw new NullPointerException(); + } else { + throw new UnsupportedOperationException(); + } + } + + @NotNull + @Override + public String getStringValue() { + if (_type == PartiQLValueType.STRING) { + throw new NullPointerException(); + } else { + throw new UnsupportedOperationException(); + } + } + + @NotNull + @Override + public String getSymbolValue() { + if (_type == PartiQLValueType.SYMBOL) { + throw new NullPointerException(); + } else { + throw new UnsupportedOperationException(); + } + } + + @NotNull + @Override + public Time getTimeValue() { + if (_type == PartiQLValueType.TIME) { + throw new NullPointerException(); + } else { + throw new UnsupportedOperationException(); + } + } + + @NotNull + @Override + public Timestamp getTimestampValue() { + if (_type == PartiQLValueType.TIMESTAMP) { + throw new NullPointerException(); + } else { + throw new UnsupportedOperationException(); + } + } + + @Override + public long getIntervalValue() { + if (_type == PartiQLValueType.INTERVAL) { + throw new NullPointerException(); + } else { + throw new UnsupportedOperationException(); + } + } +} diff --git a/partiql-eval/src/main/java/org/partiql/eval/PQLToPartiQLIterator.java b/partiql-eval/src/main/java/org/partiql/eval/PQLToPartiQLIterator.java new file mode 100644 index 000000000..663f5dd02 --- /dev/null +++ b/partiql-eval/src/main/java/org/partiql/eval/PQLToPartiQLIterator.java @@ -0,0 +1,26 @@ +package org.partiql.eval; + +import org.jetbrains.annotations.NotNull; +import org.partiql.value.PartiQLValue; + +import java.util.Iterator; + +class PQLToPartiQLIterator implements Iterator { + + @NotNull + final Iterator pqlValues; + + PQLToPartiQLIterator(@NotNull Iterator pqlValues) { + this.pqlValues = pqlValues; + } + + @Override + public boolean hasNext() { + return pqlValues.hasNext(); + } + + @Override + public PartiQLValue next() { + return pqlValues.next().toPartiQLValue(); + } +} diff --git a/partiql-eval/src/main/java/org/partiql/eval/PQLValue.java b/partiql-eval/src/main/java/org/partiql/eval/PQLValue.java new file mode 100644 index 000000000..fd47e67f6 --- /dev/null +++ b/partiql-eval/src/main/java/org/partiql/eval/PQLValue.java @@ -0,0 +1,615 @@ +package org.partiql.eval; + +import kotlin.NotImplementedError; +import kotlin.Pair; +import org.jetbrains.annotations.NotNull; +import org.partiql.value.PartiQL; +import org.partiql.value.PartiQLValue; +import org.partiql.value.PartiQLValueType; +import org.partiql.value.datetime.Date; +import org.partiql.value.datetime.Time; +import org.partiql.value.datetime.Timestamp; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.BitSet; +import java.util.Iterator; +import java.util.Objects; + +/** + * This is a representation of a value in PartiQL's type system. The intention of this modeling is to provide a layer of + * indirection between PartiQL's type semantics and Java's type semantics. + * + *

+ * INTERNAL NOTES: + *

+ * This is intended to completely replace {@link org.partiql.value.PartiQLValue} in the future. As it stands, this + * implementation will initially be used solely for the evaluator. However, the scope of this can be expanded by copying + * and pasting its contents to completely replace {@link org.partiql.value.PartiQLValue}. + *

+ * There are some pre-requisites to actually replacing {@link PartiQLValue} including, but not limited to, ...: + * - The comparator for ordering and aggregations + * - Adding support for annotations + */ +public interface PQLValue { + + /** + * Determines whether the current value is a null value of any type (for example, null or null.int). It should be + * called before calling getters that return value types (int, long, boolean, double). + */ + boolean isNull(); + + /** + * @return the type of the data at the cursor. + */ + @NotNull + PartiQLValueType getType(); + + /** + * @return the underlying value applicable to the type {@link PartiQLValueType#STRING}. + * @throws UnsupportedOperationException if the operation is not applicable to the type returned from + * {@link #getType()}; for example, if {@link #getType()} returns a {@link PartiQLValueType#INT}, then this method + * will throw this exception upon invocation. + * @throws NullPointerException if this instance also returns true on {@link #isNull()}; callers should check that + * {@link #isNull()} returns false before attempting to invoke this method. + */ + @NotNull + default String getStringValue() { + throw new UnsupportedOperationException(); + } + + /** + * @return the underlying value applicable to the type {@link PartiQLValueType#CHAR}. + * @throws UnsupportedOperationException if the operation is not applicable to the type returned from + * {@link #getType()}; for example, if {@link #getType()} returns a {@link PartiQLValueType#INT}, then this method + * will throw this exception upon invocation. + * @throws NullPointerException if this instance also returns true on {@link #isNull()}; callers should check that + * {@link #isNull()} returns false before attempting to invoke this method. + */ + @NotNull + default String getCharValue() { + throw new UnsupportedOperationException(); + } + + /** + * @return the underlying value applicable to the type {@link PartiQLValueType#SYMBOL}. + * @throws UnsupportedOperationException if the operation is not applicable to the type returned from + * {@link #getType()}; for example, if {@link #getType()} returns a {@link PartiQLValueType#INT}, then this method + * will throw this exception upon invocation. + * @throws NullPointerException if this instance also returns true on {@link #isNull()}; callers should check that + * {@link #isNull()} returns false before attempting to invoke this method. + */ + @NotNull + default String getSymbolValue() { + throw new UnsupportedOperationException(); + } + + /** + * @return the underlying value applicable to the type {@link PartiQLValueType#BOOL}. + * @throws UnsupportedOperationException if the operation is not applicable to the type returned from + * {@link #getType()}; for example, if {@link #getType()} returns a {@link PartiQLValueType#INT}, then this method + * will throw this exception upon invocation. + * @throws NullPointerException if this instance also returns true on {@link #isNull()}; callers should check that + * {@link #isNull()} returns false before attempting to invoke this method. + */ + default boolean getBoolValue() { + throw new UnsupportedOperationException(); + } + + /** + * @return the underlying value applicable to the type {@link PartiQLValueType#BINARY}. + * @throws UnsupportedOperationException if the operation is not applicable to the type returned from + * {@link #getType()}; for example, if {@link #getType()} returns a {@link PartiQLValueType#INT}, then this method + * will throw this exception upon invocation. + * @throws NullPointerException if this instance also returns true on {@link #isNull()}; callers should check that + * {@link #isNull()} returns false before attempting to invoke this method. + */ + @NotNull + default byte[] getBinaryValue() { + throw new UnsupportedOperationException(); + } + + /** + * @return the underlying value applicable to the type {@link PartiQLValueType#BLOB}. + * @throws UnsupportedOperationException if the operation is not applicable to the type returned from + * {@link #getType()}; for example, if {@link #getType()} returns a {@link PartiQLValueType#INT}, then this method + * will throw this exception upon invocation. + * @throws NullPointerException if this instance also returns true on {@link #isNull()}; callers should check that + * {@link #isNull()} returns false before attempting to invoke this method. + */ + @NotNull + default byte[] getBlobValue() { + throw new UnsupportedOperationException(); + } + + /** + * @return the underlying value applicable to the type {@link PartiQLValueType#CLOB}. + * @throws UnsupportedOperationException if the operation is not applicable to the type returned from + * {@link #getType()}; for example, if {@link #getType()} returns a {@link PartiQLValueType#INT}, then this method + * will throw this exception upon invocation. + * @throws NullPointerException if this instance also returns true on {@link #isNull()}; callers should check that + * {@link #isNull()} returns false before attempting to invoke this method. + */ + @NotNull + default byte[] getClobValue() { + throw new UnsupportedOperationException(); + } + + /** + * @return the underlying value applicable to the type {@link PartiQLValueType#BYTE}. + * @throws UnsupportedOperationException if the operation is not applicable to the type returned from + * {@link #getType()}; for example, if {@link #getType()} returns a {@link PartiQLValueType#INT}, then this method + * will throw this exception upon invocation. + * @throws NullPointerException if this instance also returns true on {@link #isNull()}; callers should check that + * {@link #isNull()} returns false before attempting to invoke this method. + */ + default byte getByteValue() { + throw new UnsupportedOperationException(); + } + + /** + * @return the underlying value applicable to the type {@link PartiQLValueType#DATE}. + * @throws UnsupportedOperationException if the operation is not applicable to the type returned from + * {@link #getType()}; for example, if {@link #getType()} returns a {@link PartiQLValueType#INT}, then this method + * will throw this exception upon invocation. + * @throws NullPointerException if this instance also returns true on {@link #isNull()}; callers should check that + * {@link #isNull()} returns false before attempting to invoke this method. + */ + @NotNull + default Date getDateValue() { + throw new UnsupportedOperationException(); + } + + /** + * @return the underlying value applicable to the type {@link PartiQLValueType#TIME}. + * @throws UnsupportedOperationException if the operation is not applicable to the type returned from + * {@link #getType()}; for example, if {@link #getType()} returns a {@link PartiQLValueType#INT}, then this method + * will throw this exception upon invocation. + * @throws NullPointerException if this instance also returns true on {@link #isNull()}; callers should check that + * {@link #isNull()} returns false before attempting to invoke this method. + */ + @NotNull + default Time getTimeValue() { + throw new UnsupportedOperationException(); + } + + /** + * @return the underlying value applicable to the type {@link PartiQLValueType#TIMESTAMP}. + * @throws UnsupportedOperationException if the operation is not applicable to the type returned from + * {@link #getType()}; for example, if {@link #getType()} returns a {@link PartiQLValueType#INT}, then this method + * will throw this exception upon invocation. + * @throws NullPointerException if this instance also returns true on {@link #isNull()}; callers should check that + * {@link #isNull()} returns false before attempting to invoke this method. + */ + @NotNull + default Timestamp getTimestampValue() { + throw new UnsupportedOperationException(); + } + + /** + * @return the underlying value applicable to the type {@link PartiQLValueType#INTERVAL}. + * @throws UnsupportedOperationException if the operation is not applicable to the type returned from + * {@link #getType()}; for example, if {@link #getType()} returns a {@link PartiQLValueType#INT}, then this method + * will throw this exception upon invocation. + * @throws NullPointerException if this instance also returns true on {@link #isNull()}; callers should check that + * {@link #isNull()} returns false before attempting to invoke this method. + */ + @Deprecated + default long getIntervalValue() { + throw new UnsupportedOperationException(); + } + + /** + * @return the underlying value applicable to the type {@link PartiQLValueType#INT8}. + * @throws UnsupportedOperationException if the operation is not applicable to the type returned from + * {@link #getType()}; for example, if {@link #getType()} returns a {@link PartiQLValueType#INT}, then this method + * will throw this exception upon invocation. + * @throws NullPointerException if this instance also returns true on {@link #isNull()}; callers should check that + * {@link #isNull()} returns false before attempting to invoke this method. + */ + default byte getInt8Value() { + throw new UnsupportedOperationException(); + } + + /** + * @return the underlying value applicable to the type {@link PartiQLValueType#INT16}. + * @throws UnsupportedOperationException if the operation is not applicable to the type returned from + * {@link #getType()}; for example, if {@link #getType()} returns a {@link PartiQLValueType#INT}, then this method + * will throw this exception upon invocation. + * @throws NullPointerException if this instance also returns true on {@link #isNull()}; callers should check that + * {@link #isNull()} returns false before attempting to invoke this method. + */ + default short getInt16Value() { + throw new UnsupportedOperationException(); + } + + /** + * @return the underlying value applicable to the type {@link PartiQLValueType#INT32}. + * @throws UnsupportedOperationException if the operation is not applicable to the type returned from + * {@link #getType()}; for example, if {@link #getType()} returns a {@link PartiQLValueType#INT}, then this method + * will throw this exception upon invocation. + * @throws NullPointerException if this instance also returns true on {@link #isNull()}; callers should check that + * {@link #isNull()} returns false before attempting to invoke this method. + */ + default int getInt32Value() { + throw new UnsupportedOperationException(); + } + + /** + * @return the underlying value applicable to the type {@link PartiQLValueType#INT64}. + * @throws UnsupportedOperationException if the operation is not applicable to the type returned from + * {@link #getType()}; for example, if {@link #getType()} returns a {@link PartiQLValueType#INT}, then this method + * will throw this exception upon invocation. + * @throws NullPointerException if this instance also returns true on {@link #isNull()}; callers should check that + * {@link #isNull()} returns false before attempting to invoke this method. + */ + default long getInt64Value() { + throw new UnsupportedOperationException(); + } + + /** + * @return the underlying value applicable to the type {@link PartiQLValueType#INT}. + * @throws UnsupportedOperationException if the operation is not applicable to the type returned from + * {@link #getType()}; for example, if {@link #getType()} returns a {@link PartiQLValueType#INT}, then this method + * will throw this exception upon invocation. + * @throws NullPointerException if this instance also returns true on {@link #isNull()}; callers should check that + * {@link #isNull()} returns false before attempting to invoke this method. + */ + @NotNull + default BigInteger getIntValue() { + throw new UnsupportedOperationException(); + } + + /** + * @return the underlying value applicable to the type {@link PartiQLValueType#FLOAT32}. + * @throws UnsupportedOperationException if the operation is not applicable to the type returned from + * {@link #getType()}; for example, if {@link #getType()} returns a {@link PartiQLValueType#INT}, then this method + * will throw this exception upon invocation. + * @throws NullPointerException if this instance also returns true on {@link #isNull()}; callers should check that + * {@link #isNull()} returns false before attempting to invoke this method. + */ + default float getFloat32Value() { + throw new UnsupportedOperationException(); + } + + /** + * @return the underlying value applicable to the type {@link PartiQLValueType#FLOAT64}. + * @throws UnsupportedOperationException if the operation is not applicable to the type returned from + * {@link #getType()}; for example, if {@link #getType()} returns a {@link PartiQLValueType#INT}, then this method + * will throw this exception upon invocation. + * @throws NullPointerException if this instance also returns true on {@link #isNull()}; callers should check that + * {@link #isNull()} returns false before attempting to invoke this method. + */ + default double getFloat64Value() { + throw new UnsupportedOperationException(); + } + + /** + * @return the underlying value applicable to the type {@link PartiQLValueType#DECIMAL}. + * @throws UnsupportedOperationException if the operation is not applicable to the type returned from + * {@link #getType()}; for example, if {@link #getType()} returns a {@link PartiQLValueType#INT}, then this method + * will throw this exception upon invocation. + * @throws NullPointerException if this instance also returns true on {@link #isNull()}; callers should check that + * {@link #isNull()} returns false before attempting to invoke this method. + */ + @NotNull + default BigDecimal getDecimalValue() { + throw new UnsupportedOperationException(); + } + + /** + * @return the underlying value applicable to the type {@link PartiQLValueType#DECIMAL_ARBITRARY}. + * @throws UnsupportedOperationException if the operation is not applicable to the type returned from + * {@link #getType()}; for example, if {@link #getType()} returns a {@link PartiQLValueType#INT}, then this method + * will throw this exception upon invocation. + * @throws NullPointerException if this instance also returns true on {@link #isNull()}; callers should check that + * {@link #isNull()} returns false before attempting to invoke this method. + */ + @NotNull + default BigDecimal getDecimalArbitraryValue() { + throw new UnsupportedOperationException(); + } + + /** + * @return the underlying value applicable to the type {@link PartiQLValueType#BAG}. + * @throws UnsupportedOperationException if the operation is not applicable to the type returned from + * {@link #getType()}; for example, if {@link #getType()} returns a {@link PartiQLValueType#INT}, then this method + * will throw this exception upon invocation. + * @throws NullPointerException if this instance also returns true on {@link #isNull()}; callers should check that + * {@link #isNull()} returns false before attempting to invoke this method. + */ + @NotNull + default Iterator getBagValues() { + throw new UnsupportedOperationException(); + } + + /** + * @return the underlying value applicable to the type {@link PartiQLValueType#LIST}. + * @throws UnsupportedOperationException if the operation is not applicable to the type returned from + * {@link #getType()}; for example, if {@link #getType()} returns a {@link PartiQLValueType#INT}, then this method + * will throw this exception upon invocation. + * @throws NullPointerException if this instance also returns true on {@link #isNull()}; callers should check that + * {@link #isNull()} returns false before attempting to invoke this method. + */ + @NotNull + default Iterator getListValues() { + throw new UnsupportedOperationException(); + } + + /** + * @return the underlying value applicable to the type {@link PartiQLValueType#STRUCT}. + * @throws UnsupportedOperationException if the operation is not applicable to the type returned from + * {@link #getType()}; for example, if {@link #getType()} returns a {@link PartiQLValueType#INT}, then this method + * will throw this exception upon invocation. + * @throws NullPointerException if this instance also returns true on {@link #isNull()}; callers should check that + * {@link #isNull()} returns false before attempting to invoke this method. + */ + @NotNull + default Iterator getStructFields() { + throw new UnsupportedOperationException(); + } + + /** + * @return the underlying value applicable to the type {@link PartiQLValueType#SEXP}. + * @throws UnsupportedOperationException if the operation is not applicable to the type returned from + * {@link #getType()}; for example, if {@link #getType()} returns a {@link PartiQLValueType#INT}, then this method + * will throw this exception upon invocation. + * @throws NullPointerException if this instance also returns true on {@link #isNull()}; callers should check that + * {@link #isNull()} returns false before attempting to invoke this method. + */ + @NotNull + default Iterator getSexpValues() { + throw new UnsupportedOperationException(); + } + + /** + * Converts a {@link PQLValue} into a {@link PartiQLValue}. + * + * @return the equivalent {@link PartiQLValue} + * @deprecated this is an experimental API and is designed for use by the internal PartiQL library. This may + * be modified/removed at any time. + */ + @NotNull + @Deprecated + default PartiQLValue toPartiQLValue() { + PartiQLValueType type = this.getType(); + switch (type) { + case BOOL: + return this.isNull() ? PartiQL.boolValue(null) : PartiQL.boolValue(this.getBoolValue()); + case INT8: + return this.isNull() ? PartiQL.int8Value(null) : PartiQL.int8Value(this.getInt8Value()); + case INT16: + return this.isNull() ? PartiQL.int16Value(null) : PartiQL.int16Value(this.getInt16Value()); + case INT32: + return this.isNull() ? PartiQL.int32Value(null) : PartiQL.int32Value(this.getInt32Value()); + case INT64: + return this.isNull() ? PartiQL.int64Value(null) : PartiQL.int64Value(this.getInt64Value()); + case INT: + return this.isNull() ? PartiQL.intValue(null) : PartiQL.intValue(this.getIntValue()); + case DECIMAL: + return this.isNull() ? PartiQL.decimalValue(null) : PartiQL.decimalValue(this.getDecimalValue()); + case DECIMAL_ARBITRARY: + return this.isNull() ? PartiQL.decimalValue(null) : PartiQL.decimalValue(this.getDecimalArbitraryValue()); + case FLOAT32: + return this.isNull() ? PartiQL.float32Value(null) : PartiQL.float32Value(this.getFloat32Value()); + case FLOAT64: + return this.isNull() ? PartiQL.float64Value(null) : PartiQL.float64Value(this.getFloat64Value()); + case CHAR: + return this.isNull() ? PartiQL.charValue(null) : PartiQL.charValue(this.getCharValue().charAt(0)); + case STRING: + return this.isNull() ? PartiQL.stringValue(null) : PartiQL.stringValue(this.getStringValue()); + case SYMBOL: + return this.isNull() ? PartiQL.symbolValue(null) : PartiQL.symbolValue(this.getSymbolValue()); + case BINARY: + return this.isNull() ? PartiQL.binaryValue(null) : PartiQL.binaryValue(BitSet.valueOf(this.getBinaryValue())); + case BYTE: + return this.isNull() ? PartiQL.byteValue(null) : PartiQL.byteValue(this.getByteValue()); + case BLOB: + return this.isNull() ? PartiQL.blobValue(null) : PartiQL.blobValue(this.getBlobValue()); + case CLOB: + return this.isNull() ? PartiQL.clobValue(null) : PartiQL.clobValue(this.getClobValue()); + case DATE: + return this.isNull() ? PartiQL.dateValue(null) : PartiQL.dateValue(this.getDateValue()); + case TIME: + return this.isNull() ? PartiQL.timeValue(null) : PartiQL.timeValue(this.getTimeValue()); + case TIMESTAMP: + return this.isNull() ? PartiQL.timestampValue(null) : PartiQL.timestampValue(this.getTimestampValue()); + case INTERVAL: + return this.isNull() ? PartiQL.intervalValue(null) : PartiQL.intervalValue(this.getIntervalValue()); + case BAG: + return this.isNull() ? PartiQL.bagValue((Iterable) null) : PartiQL.bagValue( + new IterableFromIteratorSupplier<>(() -> new PQLToPartiQLIterator(this.getBagValues())) + ); + case LIST: + return this.isNull() ? PartiQL.listValue((Iterable) null) : PartiQL.listValue( + new IterableFromIteratorSupplier<>(() -> new PQLToPartiQLIterator(this.getListValues())) + ); + case SEXP: + return this.isNull() ? PartiQL.sexpValue((Iterable) null) : PartiQL.sexpValue( + new IterableFromIteratorSupplier<>(() -> new PQLToPartiQLIterator(this.getSexpValues())) + ); + case STRUCT: + return this.isNull() ? PartiQL.structValue((Iterable>) null) : PartiQL.structValue( + new IterableFromIteratorSupplier<>(() -> { + Iterator _fields = this.getStructFields(); + return new Iterator>() { + @Override + public boolean hasNext() { + return _fields.hasNext(); + } + + @Override + public Pair next() { + StructField field = _fields.next(); + return new Pair<>(field.getName(), field.getValue().toPartiQLValue()); + } + }; + }) + ); + case NULL: + return PartiQL.nullValue(); + case MISSING: + return PartiQL.missingValue(); + case ANY: + default: + throw new UnsupportedOperationException(); + } + } + + /** + * Converts a {@link PartiQLValue} into {@link PQLValue}. + * + * @return the equivalent {@link PQLValue} + */ + @NotNull + @Deprecated + static PQLValue of(PartiQLValue value) { + PartiQLValueType type = value.getType(); + if (value.isNull()) { + return new NullValue(type); + } + switch (type) { + case MISSING: + return new MissingValue(); + case NULL: + return new NullValue(); + case INT8: + org.partiql.value.Int8Value int8Value = (org.partiql.value.Int8Value) value; + return new Int8Value(Objects.requireNonNull(int8Value.getValue())); + case STRUCT: + @SuppressWarnings("unchecked") + org.partiql.value.StructValue STRUCTValue = (org.partiql.value.StructValue) value; + return new StructValue(new StructFieldIterableWrapper(Objects.requireNonNull(STRUCTValue.getEntries()))); + case STRING: + org.partiql.value.StringValue STRINGValue = (org.partiql.value.StringValue) value; + return new StringValue(Objects.requireNonNull(STRINGValue.getValue())); + case INT64: + org.partiql.value.Int64Value INT64Value = (org.partiql.value.Int64Value) value; + return new Int64Value(Objects.requireNonNull(INT64Value.getValue())); + case INT32: + org.partiql.value.Int32Value INT32Value = (org.partiql.value.Int32Value) value; + return new Int32Value(Objects.requireNonNull(INT32Value.getValue())); + case INT16: + org.partiql.value.Int16Value INT16Value = (org.partiql.value.Int16Value) value; + return new Int16Value(Objects.requireNonNull(INT16Value.getValue())); + case SEXP: + @SuppressWarnings("unchecked") + org.partiql.value.SexpValue sexpValue = (org.partiql.value.SexpValue) value; + return new SexpValue(new PartiQLValueIterableWrapper(Objects.requireNonNull(sexpValue))); + case LIST: + @SuppressWarnings("unchecked") + org.partiql.value.ListValue LISTValue = (org.partiql.value.ListValue) value; + return new ListValue(new PartiQLValueIterableWrapper(Objects.requireNonNull(LISTValue))); + case BOOL: + org.partiql.value.BoolValue BOOLValue = (org.partiql.value.BoolValue) value; + return new BoolValue(Objects.requireNonNull(BOOLValue.getValue())); + case INT: + org.partiql.value.IntValue INTValue = (org.partiql.value.IntValue) value; + return new IntValue(Objects.requireNonNull(INTValue.getValue())); + case BAG: + @SuppressWarnings("unchecked") + org.partiql.value.BagValue BAGValue = (org.partiql.value.BagValue) value; + return new BagValue(new PartiQLValueIterableWrapper(Objects.requireNonNull(BAGValue))); + case BINARY: + org.partiql.value.BinaryValue BINARYValue = (org.partiql.value.BinaryValue) value; + return new BinaryValue(Objects.requireNonNull(BINARYValue.getValue().toByteArray())); + case DATE: + org.partiql.value.DateValue DATEValue = (org.partiql.value.DateValue) value; + return new DateValue(Objects.requireNonNull(DATEValue.getValue())); + case INTERVAL: + org.partiql.value.IntervalValue INTERVALValue = (org.partiql.value.IntervalValue) value; + return new IntervalValue(Objects.requireNonNull(INTERVALValue.getValue())); + case TIMESTAMP: + org.partiql.value.TimestampValue TIMESTAMPValue = (org.partiql.value.TimestampValue) value; + return new TimestampValue(Objects.requireNonNull(TIMESTAMPValue.getValue())); + case TIME: + org.partiql.value.TimeValue TIMEValue = (org.partiql.value.TimeValue) value; + return new TimeValue(Objects.requireNonNull(TIMEValue.getValue())); + case FLOAT32: + org.partiql.value.Float32Value FLOAT32Value = (org.partiql.value.Float32Value) value; + return new Float32Value(Objects.requireNonNull(FLOAT32Value.getValue())); + case FLOAT64: + org.partiql.value.Float64Value FLOAT64Value = (org.partiql.value.Float64Value) value; + return new Float64Value(Objects.requireNonNull(FLOAT64Value.getValue())); + case DECIMAL: + org.partiql.value.DecimalValue DECIMALValue = (org.partiql.value.DecimalValue) value; + return new DecimalValue(Objects.requireNonNull(DECIMALValue.getValue())); + case CHAR: + org.partiql.value.CharValue CHARValue = (org.partiql.value.CharValue) value; + return new CharValue(Objects.requireNonNull(Objects.requireNonNull(CHARValue.getValue()).toString())); + case SYMBOL: + org.partiql.value.SymbolValue SYMBOLValue = (org.partiql.value.SymbolValue) value; + return new SymbolValue(Objects.requireNonNull(SYMBOLValue.getValue())); + case CLOB: + org.partiql.value.ClobValue CLOBValue = (org.partiql.value.ClobValue) value; + return new ClobValue(Objects.requireNonNull(CLOBValue.getValue())); + case BLOB: + org.partiql.value.BlobValue BLOBValue = (org.partiql.value.BlobValue) value; + return new BlobValue(Objects.requireNonNull(BLOBValue.getValue())); + case BYTE: + org.partiql.value.ByteValue BYTEValue = (org.partiql.value.ByteValue) value; + return new ByteValue(Objects.requireNonNull(BYTEValue.getValue())); + case DECIMAL_ARBITRARY: + org.partiql.value.DecimalValue DECIMAL_ARBITRARYValue = (org.partiql.value.DecimalValue) value; + return new DecimalArbitraryValue(Objects.requireNonNull(DECIMAL_ARBITRARYValue.getValue())); + case ANY: + default: + throw new NotImplementedError(); + } + } + + @NotNull + static PQLValue nullValue() { + return new NullValue(); + } + + @NotNull + static PQLValue missingValue() { + return new MissingValue(); + } + + @NotNull + static PQLValue nullValue(@NotNull PartiQLValueType type) { + return new NullValue(type); + } + + @NotNull + static PQLValue bagValue(@NotNull Iterable values) { + return new BagValue(values); + } + + @NotNull + static PQLValue int64Value(long value) { + return new Int64Value(value); + } + + @NotNull + static PQLValue int32Value(int value) { + return new Int32Value(value); + } + + @NotNull + static PQLValue boolValue(boolean value) { + return new BoolValue(value); + } + + @NotNull + static PQLValue sexpValue(@NotNull Iterable values) { + return new SexpValue(values); + } + + @NotNull + static PQLValue listValue(@NotNull Iterable values) { + return new ListValue(values); + } + + @NotNull + static PQLValue structValue(@NotNull Iterable values) { + return new StructValue(values); + } + + @NotNull + static PQLValue stringValue(@NotNull String value) { + return new StringValue(value); + } +} diff --git a/partiql-eval/src/main/java/org/partiql/eval/PartiQLValueIterableWrapper.java b/partiql-eval/src/main/java/org/partiql/eval/PartiQLValueIterableWrapper.java new file mode 100644 index 000000000..9a25e4950 --- /dev/null +++ b/partiql-eval/src/main/java/org/partiql/eval/PartiQLValueIterableWrapper.java @@ -0,0 +1,37 @@ +package org.partiql.eval; + +import org.partiql.value.CollectionValue; +import org.partiql.value.PartiQLValue; + +class PartiQLValueIterableWrapper implements Iterable { + + private final CollectionValue _value; + + PartiQLValueIterableWrapper(CollectionValue value) { + _value = value; + } + + @Override + public Iterator iterator() { + return new Iterator(_value.iterator()); + } + + static class Iterator implements java.util.Iterator { + private final java.util.Iterator _value; + + private Iterator(java.util.Iterator value) { + _value = value; + } + + @Override + public boolean hasNext() { + return _value.hasNext(); + } + + @Override + public PQLValue next() { + PartiQLValue value = _value.next(); + return PQLValue.of(value); + } + } +} diff --git a/partiql-eval/src/main/java/org/partiql/eval/SexpValue.java b/partiql-eval/src/main/java/org/partiql/eval/SexpValue.java new file mode 100644 index 000000000..98a7c0b46 --- /dev/null +++ b/partiql-eval/src/main/java/org/partiql/eval/SexpValue.java @@ -0,0 +1,37 @@ +package org.partiql.eval; + +import org.jetbrains.annotations.NotNull; +import org.partiql.value.PartiQLValueType; + +import java.util.Collections; +import java.util.Iterator; + +/** + * This shall always be package-private (internal). + */ +class SexpValue implements PQLValue { + + @NotNull + final Iterable _value; + + SexpValue(@NotNull Iterable value) { + _value = value; + } + + @Override + public boolean isNull() { + return false; + } + + @Override + @NotNull + public Iterator getSexpValues() { + return _value.iterator(); + } + + @NotNull + @Override + public PartiQLValueType getType() { + return PartiQLValueType.SEXP; + } +} diff --git a/partiql-eval/src/main/java/org/partiql/eval/StringValue.java b/partiql-eval/src/main/java/org/partiql/eval/StringValue.java new file mode 100644 index 000000000..4e79eaa4a --- /dev/null +++ b/partiql-eval/src/main/java/org/partiql/eval/StringValue.java @@ -0,0 +1,34 @@ +package org.partiql.eval; + +import org.jetbrains.annotations.NotNull; +import org.partiql.value.PartiQLValueType; + +/** + * This shall always be package-private (internal). + */ +class StringValue implements PQLValue { + + @NotNull + final String _value; + + StringValue(@NotNull String value) { + _value = value; + } + + @Override + public boolean isNull() { + return false; + } + + @Override + @NotNull + public String getStringValue() { + return _value; + } + + @NotNull + @Override + public PartiQLValueType getType() { + return PartiQLValueType.STRING; + } +} diff --git a/partiql-eval/src/main/java/org/partiql/eval/StructField.java b/partiql-eval/src/main/java/org/partiql/eval/StructField.java new file mode 100644 index 000000000..67f64ebe9 --- /dev/null +++ b/partiql-eval/src/main/java/org/partiql/eval/StructField.java @@ -0,0 +1,44 @@ +package org.partiql.eval; + +import org.jetbrains.annotations.NotNull; + +/** + * Represents the key-value pairs that are embedded within values of type {@link org.partiql.value.PartiQLValueType#STRUCT}. + */ +public interface StructField { + + /** + * @return the key in the key-value pair that the {@link StructField} represents. + */ + @NotNull + String getName(); + + /** + * @return the value in the key-value pair that the {@link StructField} represents. + */ + @NotNull + PQLValue getValue(); + + /** + * Returns an instance of a {@link StructField} + * @param name the key in the key-value pair + * @param value the value in the key-value pair + * @return an instance of {@link StructField} that holds the {@code name} and {@code value} + */ + @NotNull + static StructField of(@NotNull String name, @NotNull PQLValue value) { + return new StructField() { + @NotNull + @Override + public String getName() { + return name; + } + + @NotNull + @Override + public PQLValue getValue() { + return value; + } + }; + } +} diff --git a/partiql-eval/src/main/java/org/partiql/eval/StructFieldIterableWrapper.java b/partiql-eval/src/main/java/org/partiql/eval/StructFieldIterableWrapper.java new file mode 100644 index 000000000..3590587b4 --- /dev/null +++ b/partiql-eval/src/main/java/org/partiql/eval/StructFieldIterableWrapper.java @@ -0,0 +1,50 @@ +package org.partiql.eval; + +import kotlin.Pair; +import org.jetbrains.annotations.NotNull; +import org.partiql.value.PartiQLValue; + +class StructFieldIterableWrapper implements Iterable { + + private final Iterable> _value; + + StructFieldIterableWrapper(Iterable> value) { + _value = value; + } + + @Override + public Iterator iterator() { + return new Iterator(_value.iterator()); + } + + static class Iterator implements java.util.Iterator { + private final java.util.Iterator> _value; + + private Iterator(java.util.Iterator> value) { + _value = value; + } + + @Override + public boolean hasNext() { + return _value.hasNext(); + } + + @Override + public StructField next() { + Pair value = _value.next(); + return new StructField() { + @NotNull + @Override + public String getName() { + return value.getFirst(); + } + + @NotNull + @Override + public PQLValue getValue() { + return PQLValue.of(value.getSecond()); + } + }; + } + } +} diff --git a/partiql-eval/src/main/java/org/partiql/eval/StructValue.java b/partiql-eval/src/main/java/org/partiql/eval/StructValue.java new file mode 100644 index 000000000..f50f58a73 --- /dev/null +++ b/partiql-eval/src/main/java/org/partiql/eval/StructValue.java @@ -0,0 +1,37 @@ +package org.partiql.eval; + +import org.jetbrains.annotations.NotNull; +import org.partiql.value.PartiQLValueType; + +import java.util.Collections; +import java.util.Iterator; + +/** + * This shall always be package-private (internal). + */ +class StructValue implements PQLValue { + + @NotNull + final Iterable _value; + + StructValue(@NotNull Iterable value) { + _value = value; + } + + @Override + public boolean isNull() { + return false; + } + + @Override + @NotNull + public Iterator getStructFields() { + return _value.iterator(); + } + + @NotNull + @Override + public PartiQLValueType getType() { + return PartiQLValueType.STRUCT; + } +} diff --git a/partiql-eval/src/main/java/org/partiql/eval/SymbolValue.java b/partiql-eval/src/main/java/org/partiql/eval/SymbolValue.java new file mode 100644 index 000000000..ce0f1402f --- /dev/null +++ b/partiql-eval/src/main/java/org/partiql/eval/SymbolValue.java @@ -0,0 +1,34 @@ +package org.partiql.eval; + +import org.jetbrains.annotations.NotNull; +import org.partiql.value.PartiQLValueType; + +/** + * This shall always be package-private (internal). + */ +class SymbolValue implements PQLValue { + + @NotNull + final String _value; + + SymbolValue(@NotNull String value) { + _value = value; + } + + @Override + public boolean isNull() { + return false; + } + + @Override + @NotNull + public String getSymbolValue() { + return _value; + } + + @NotNull + @Override + public PartiQLValueType getType() { + return PartiQLValueType.SYMBOL; + } +} diff --git a/partiql-eval/src/main/java/org/partiql/eval/TimeValue.java b/partiql-eval/src/main/java/org/partiql/eval/TimeValue.java new file mode 100644 index 000000000..258ca0241 --- /dev/null +++ b/partiql-eval/src/main/java/org/partiql/eval/TimeValue.java @@ -0,0 +1,37 @@ +package org.partiql.eval; + +import org.jetbrains.annotations.NotNull; +import org.partiql.value.PartiQLValueType; +import org.partiql.value.datetime.Time; + +import java.util.Objects; + +/** + * This shall always be package-private (internal). + */ +class TimeValue implements PQLValue { + + @NotNull + final Time _value; + + TimeValue(@NotNull Time value) { + _value = value; + } + + @Override + public boolean isNull() { + return false; + } + + @Override + @NotNull + public Time getTimeValue() { + return _value; + } + + @NotNull + @Override + public PartiQLValueType getType() { + return PartiQLValueType.TIME; + } +} diff --git a/partiql-eval/src/main/java/org/partiql/eval/TimestampValue.java b/partiql-eval/src/main/java/org/partiql/eval/TimestampValue.java new file mode 100644 index 000000000..eac874827 --- /dev/null +++ b/partiql-eval/src/main/java/org/partiql/eval/TimestampValue.java @@ -0,0 +1,38 @@ +package org.partiql.eval; + +import org.jetbrains.annotations.NotNull; +import org.partiql.value.PartiQLValueType; +import org.partiql.value.datetime.Time; +import org.partiql.value.datetime.Timestamp; + +import java.util.Objects; + +/** + * This shall always be package-private (internal). + */ +class TimestampValue implements PQLValue { + + @NotNull + final Timestamp _value; + + TimestampValue(@NotNull Timestamp value) { + _value = value; + } + + @Override + public boolean isNull() { + return false; + } + + @Override + @NotNull + public Timestamp getTimestampValue() { + return _value; + } + + @NotNull + @Override + public PartiQLValueType getType() { + return PartiQLValueType.TIMESTAMP; + } +} diff --git a/partiql-eval/src/main/kotlin/org/partiql/eval/PartiQLEngineDefault.kt b/partiql-eval/src/main/kotlin/org/partiql/eval/PartiQLEngineDefault.kt index 9cf78860d..57ed45f26 100644 --- a/partiql-eval/src/main/kotlin/org/partiql/eval/PartiQLEngineDefault.kt +++ b/partiql-eval/src/main/kotlin/org/partiql/eval/PartiQLEngineDefault.kt @@ -19,7 +19,7 @@ internal class PartiQLEngineDefault : PartiQLEngine { val expression = compiler.compile() return object : PartiQLStatement.Query { override fun execute(): PartiQLValue { - return expression.eval(Environment.empty) + return expression.eval(Environment.empty).toPartiQLValue() } } } catch (ex: Exception) { diff --git a/partiql-eval/src/main/kotlin/org/partiql/eval/internal/Compiler.kt b/partiql-eval/src/main/kotlin/org/partiql/eval/internal/Compiler.kt index ae2950342..f93971890 100644 --- a/partiql-eval/src/main/kotlin/org/partiql/eval/internal/Compiler.kt +++ b/partiql-eval/src/main/kotlin/org/partiql/eval/internal/Compiler.kt @@ -1,5 +1,6 @@ package org.partiql.eval.internal +import org.partiql.eval.PQLValue import org.partiql.eval.PartiQLEngine import org.partiql.eval.internal.operator.Operator import org.partiql.eval.internal.operator.rel.RelAggregate @@ -380,7 +381,7 @@ internal class Compiler( @OptIn(PartiQLValueExperimental::class) override fun visitRexOpLit(node: Rex.Op.Lit, ctx: StaticType?): Operator { - return ExprLiteral(node.value) + return ExprLiteral(PQLValue.of(node.value)) } override fun visitRelOpDistinct(node: Rel.Op.Distinct, ctx: StaticType?): Operator { diff --git a/partiql-eval/src/main/kotlin/org/partiql/eval/internal/Environment.kt b/partiql-eval/src/main/kotlin/org/partiql/eval/internal/Environment.kt index 420076639..75a31090e 100644 --- a/partiql-eval/src/main/kotlin/org/partiql/eval/internal/Environment.kt +++ b/partiql-eval/src/main/kotlin/org/partiql/eval/internal/Environment.kt @@ -1,7 +1,6 @@ package org.partiql.eval.internal -import org.partiql.value.PartiQLValue -import org.partiql.value.PartiQLValueExperimental +import org.partiql.eval.PQLValue /** * This class represents the Variables Environment defined in the PartiQL Specification. @@ -16,13 +15,15 @@ internal class Environment( val empty: Environment = Environment(Record.empty, null) } - @OptIn(PartiQLValueExperimental::class) - operator fun get(index: Int): PartiQLValue { - return this.bindings[index] + operator fun get(index: Int): PQLValue { + try { + return this.bindings[index] + } catch (_: Throwable) { + throw IllegalStateException("Received error when searching for binding at index $index. Current bindings are: $this.") + } } - @OptIn(PartiQLValueExperimental::class) - fun getOrNull(index: Int): PartiQLValue? { + fun getOrNull(index: Int): PQLValue? { return this.bindings.values.getOrNull(index) } diff --git a/partiql-eval/src/main/kotlin/org/partiql/eval/internal/Record.kt b/partiql-eval/src/main/kotlin/org/partiql/eval/internal/Record.kt index 0df43b5e2..c3ec60080 100644 --- a/partiql-eval/src/main/kotlin/org/partiql/eval/internal/Record.kt +++ b/partiql-eval/src/main/kotlin/org/partiql/eval/internal/Record.kt @@ -1,14 +1,12 @@ package org.partiql.eval.internal -import org.partiql.value.PartiQLValue -import org.partiql.value.PartiQLValueExperimental +import org.partiql.eval.PQLValue -@OptIn(PartiQLValueExperimental::class) -internal data class Record(val values: Array) { +internal data class Record(val values: Array) { companion object { val empty = Record(emptyArray()) - fun of(vararg values: PartiQLValue) = Record(arrayOf(*(values))) + fun of(vararg values: PQLValue) = Record(arrayOf(*(values))) } override fun equals(other: Any?): Boolean { @@ -30,7 +28,7 @@ internal data class Record(val values: Array) { return Record(this.values.copyOf()) } - public operator fun get(index: Int): PartiQLValue { + public operator fun get(index: Int): PQLValue { return this.values[index] } diff --git a/partiql-eval/src/main/kotlin/org/partiql/eval/internal/helpers/IteratorSupplier.kt b/partiql-eval/src/main/kotlin/org/partiql/eval/internal/helpers/IteratorSupplier.kt new file mode 100644 index 000000000..2d325626b --- /dev/null +++ b/partiql-eval/src/main/kotlin/org/partiql/eval/internal/helpers/IteratorSupplier.kt @@ -0,0 +1,7 @@ +package org.partiql.eval.internal.helpers + +internal class IteratorSupplier(private val supplier: () -> Iterator) : Iterable { + override fun iterator(): Iterator { + return supplier.invoke() + } +} diff --git a/partiql-eval/src/main/kotlin/org/partiql/eval/internal/helpers/RecordValueIterator.kt b/partiql-eval/src/main/kotlin/org/partiql/eval/internal/helpers/RecordValueIterator.kt index a43c8d988..e1a7fff39 100644 --- a/partiql-eval/src/main/kotlin/org/partiql/eval/internal/helpers/RecordValueIterator.kt +++ b/partiql-eval/src/main/kotlin/org/partiql/eval/internal/helpers/RecordValueIterator.kt @@ -1,15 +1,13 @@ package org.partiql.eval.internal.helpers +import org.partiql.eval.PQLValue import org.partiql.eval.internal.Record -import org.partiql.value.CollectionValue -import org.partiql.value.PartiQLValueExperimental /** - * An [Iterator] over a [CollectionValue] lazily producing [Record]s as you call [next]. + * An [Iterator] over an [Iterator] lazily producing [Record]s as you call [next]. */ -@OptIn(PartiQLValueExperimental::class) internal class RecordValueIterator( - collectionValue: CollectionValue<*> + collectionValue: Iterator ) : Iterator { private val collectionIter = collectionValue.iterator() diff --git a/partiql-eval/src/main/kotlin/org/partiql/eval/internal/helpers/ValueUtility.kt b/partiql-eval/src/main/kotlin/org/partiql/eval/internal/helpers/ValueUtility.kt new file mode 100644 index 000000000..d60867c05 --- /dev/null +++ b/partiql-eval/src/main/kotlin/org/partiql/eval/internal/helpers/ValueUtility.kt @@ -0,0 +1,97 @@ +package org.partiql.eval.internal.helpers + +import org.partiql.errors.TypeCheckException +import org.partiql.eval.PQLValue +import org.partiql.value.PartiQLValue +import org.partiql.value.PartiQLValueExperimental +import org.partiql.value.PartiQLValueType +import java.math.BigInteger + +/** + * Holds helper functions for [PartiQLValue]. + */ +internal object ValueUtility { + + /** + * @return whether the value is a boolean and the value itself is not-null and true. + */ + @OptIn(PartiQLValueExperimental::class) + @JvmStatic + fun PQLValue.isTrue(): Boolean { + return this.type == PartiQLValueType.BOOL && !this.isNull && this.boolValue + } + + /** + * Asserts that [this] is of a specific type. Note that, if [this] value is null ([PQLValue.isNull]), then the null + * value is coerced to the expected type. + * @throws TypeCheckException when the input value is a non-null value of the wrong type. + * @return a [PQLValue] corresponding to the expected type; this will either be the input value if the value is + * already of the expected type, or it will be a null value of the expected type. + */ + @OptIn(PartiQLValueExperimental::class) + fun PQLValue.check(type: PartiQLValueType): PQLValue { + if (this.type == type) { + return this + } + if (!this.isNull) { + throw TypeCheckException("Expected type $type but received ${this.type}.") + } + return PQLValue.nullValue(type) + } + + /** + * Returns the underlying string value of a PartiQL text value + * + * @throws NullPointerException if the value is null + * @throws TypeCheckException if the value's type is not a text type (string, symbol, char) + */ + @OptIn(PartiQLValueExperimental::class) + fun PQLValue.getText(): String { + return when (this.type) { + PartiQLValueType.STRING -> this.stringValue + PartiQLValueType.SYMBOL -> this.symbolValue + PartiQLValueType.CHAR -> this.charValue + else -> throw TypeCheckException("Expected text, but received ${this.type}.") + } + } + + /** + * Takes in a [PQLValue] that is any integer type ([PartiQLValueType.INT8], [PartiQLValueType.INT8], + * [PartiQLValueType.INT8], [PartiQLValueType.INT8], [PartiQLValueType.INT8]) and returns the [BigInteger] (potentially + * coerced) that represents the integer. + * + * @throws NullPointerException if the value is null + * @throws TypeCheckException if type is not an integer type + */ + @OptIn(PartiQLValueExperimental::class) + fun PQLValue.getBigIntCoerced(): BigInteger { + return when (this.type) { + PartiQLValueType.INT8 -> this.int8Value.toInt().toBigInteger() + PartiQLValueType.INT16 -> this.int16Value.toInt().toBigInteger() + PartiQLValueType.INT32 -> this.int32Value.toBigInteger() + PartiQLValueType.INT64 -> this.int64Value.toBigInteger() + PartiQLValueType.INT -> this.intValue + else -> throw TypeCheckException() + } + } + + /** + * Takes in a [PQLValue] that is any integer type ([PartiQLValueType.INT8], [PartiQLValueType.INT8], + * [PartiQLValueType.INT8], [PartiQLValueType.INT8], [PartiQLValueType.INT8]) and returns the [Int] (potentially + * coerced) that represents the integer. + * + * @throws NullPointerException if the value is null + * @throws TypeCheckException if type is not an integer type + */ + @OptIn(PartiQLValueExperimental::class) + fun PQLValue.getInt32Coerced(): Int { + return when (this.type) { + PartiQLValueType.INT8 -> this.int8Value.toInt() + PartiQLValueType.INT16 -> this.int16Value.toInt() + PartiQLValueType.INT32 -> this.int32Value + PartiQLValueType.INT64 -> this.int64Value.toInt() + PartiQLValueType.INT -> this.intValue.toInt() + else -> throw TypeCheckException() + } + } +} diff --git a/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/Operator.kt b/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/Operator.kt index 19d283a29..6bc7d304d 100644 --- a/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/Operator.kt +++ b/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/Operator.kt @@ -1,10 +1,10 @@ package org.partiql.eval.internal.operator +import org.partiql.eval.PQLValue import org.partiql.eval.internal.Environment import org.partiql.eval.internal.Record import org.partiql.spi.fn.Agg import org.partiql.spi.fn.FnExperimental -import org.partiql.value.PartiQLValue import org.partiql.value.PartiQLValueExperimental internal sealed interface Operator { @@ -15,7 +15,7 @@ internal sealed interface Operator { interface Expr : Operator { @OptIn(PartiQLValueExperimental::class) - fun eval(env: Environment): PartiQLValue + fun eval(env: Environment): PQLValue } /** diff --git a/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rel/RelAggregate.kt b/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rel/RelAggregate.kt index 77accc1e6..47efc2788 100644 --- a/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rel/RelAggregate.kt +++ b/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rel/RelAggregate.kt @@ -1,5 +1,6 @@ package org.partiql.eval.internal.operator.rel +import org.partiql.eval.PQLValue import org.partiql.eval.internal.Environment import org.partiql.eval.internal.Record import org.partiql.eval.internal.operator.Operator @@ -65,7 +66,7 @@ internal class RelAggregate( val key = it.eval(env.push(inputRecord)) when (key.type == PartiQLValueType.MISSING) { true -> nullValue() - false -> key + false -> key.toPartiQLValue() } } val accumulators = aggregationMap.getOrPut(evaluatedGroupByKeys) { @@ -83,7 +84,8 @@ internal class RelAggregate( // Aggregate Values in Aggregation State accumulators.forEachIndexed { index, function -> - val valueToAggregate = function.args.map { it.eval(env.push(inputRecord)) } + // TODO: Add support for aggregating PQLValues directly + val valueToAggregate = function.args.map { it.eval(env.push(inputRecord)).toPartiQLValue() } // Skip over aggregation if NULL/MISSING if (valueToAggregate.any { it.type == PartiQLValueType.MISSING || it.isNull }) { return@forEachIndexed @@ -98,10 +100,10 @@ internal class RelAggregate( // No Aggregations Created if (keys.isEmpty() && aggregationMap.isEmpty()) { - val record = mutableListOf() + val record = mutableListOf() functions.forEach { function -> val accumulator = function.delegate.accumulator() - record.add(accumulator.value()) + record.add(PQLValue.of(accumulator.value())) } records = iterator { yield(Record.of(*record.toTypedArray())) } return @@ -109,7 +111,7 @@ internal class RelAggregate( records = iterator { aggregationMap.forEach { (keysEvaluated, accumulators) -> - val recordValues = accumulators.map { acc -> acc.delegate.value() } + keysEvaluated.map { value -> value } + val recordValues = accumulators.map { acc -> PQLValue.of(acc.delegate.value()) } + keysEvaluated.map { value -> PQLValue.of(value) } yield(Record.of(*recordValues.toTypedArray())) } } diff --git a/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rel/RelDistinct.kt b/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rel/RelDistinct.kt index 15dcc1ba5..a1debc2e5 100644 --- a/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rel/RelDistinct.kt +++ b/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rel/RelDistinct.kt @@ -3,27 +3,34 @@ package org.partiql.eval.internal.operator.rel import org.partiql.eval.internal.Environment import org.partiql.eval.internal.Record import org.partiql.eval.internal.operator.Operator +import org.partiql.value.PartiQLValue +import org.partiql.value.PartiQLValueExperimental internal class RelDistinct( val input: Operator.Relation ) : RelPeeking() { - private val seen = mutableSetOf() + // TODO: Add hashcode/equals support for PQLValue. Then we can use Record directly. + @OptIn(PartiQLValueExperimental::class) + private val seen = mutableSetOf>() override fun openPeeking(env: Environment) { input.open(env) } + @OptIn(PartiQLValueExperimental::class) override fun peek(): Record? { for (next in input) { - if (seen.contains(next).not()) { - seen.add(next) + val transformed = List(next.values.size) { next.values[it].toPartiQLValue() } + if (seen.contains(transformed).not()) { + seen.add(transformed) return next } } return null } + @OptIn(PartiQLValueExperimental::class) override fun closePeeking() { seen.clear() input.close() diff --git a/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rel/RelExclude.kt b/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rel/RelExclude.kt index 82cf5f27d..06b66555e 100644 --- a/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rel/RelExclude.kt +++ b/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rel/RelExclude.kt @@ -1,7 +1,10 @@ package org.partiql.eval.internal.operator.rel +import org.partiql.eval.PQLValue +import org.partiql.eval.StructField import org.partiql.eval.internal.Environment import org.partiql.eval.internal.Record +import org.partiql.eval.internal.helpers.IteratorSupplier import org.partiql.eval.internal.operator.Operator import org.partiql.plan.Rel import org.partiql.plan.relOpExcludeTypeCollIndex @@ -9,18 +12,9 @@ import org.partiql.plan.relOpExcludeTypeCollWildcard import org.partiql.plan.relOpExcludeTypeStructKey import org.partiql.plan.relOpExcludeTypeStructSymbol import org.partiql.plan.relOpExcludeTypeStructWildcard -import org.partiql.value.BagValue -import org.partiql.value.CollectionValue -import org.partiql.value.ListValue import org.partiql.value.PartiQLValue import org.partiql.value.PartiQLValueExperimental import org.partiql.value.PartiQLValueType -import org.partiql.value.SexpValue -import org.partiql.value.StructValue -import org.partiql.value.bagValue -import org.partiql.value.listValue -import org.partiql.value.sexpValue -import org.partiql.value.structValue internal class RelExclude( private val input: Operator.Relation, @@ -41,7 +35,7 @@ internal class RelExclude( exclusions.forEach { path -> val root = path.root.ref val value = record.values[root] - record.values[root] = exclude(value, path.steps) + record.values[root] = excludeValue(value, path.steps) } return record } @@ -50,11 +44,10 @@ internal class RelExclude( input.close() } - @OptIn(PartiQLValueExperimental::class) - private fun exclude( - structValue: StructValue<*>, + private fun excludeStruct( + structValue: PQLValue, exclusions: List - ): PartiQLValue { + ): PQLValue { val structSymbolsToRemove = mutableSetOf() val structKeysToRemove = mutableSetOf() // keys stored as lowercase strings val branches = mutableMapOf>() @@ -64,7 +57,7 @@ internal class RelExclude( when (val leafType = exclusion.type) { is Rel.Op.Exclude.Type.StructWildcard -> { // struct wildcard at current level. return empty struct - return structValue() + return PQLValue.structValue(emptyList()) } is Rel.Op.Exclude.Type.StructSymbol -> structSymbolsToRemove.add(leafType.symbol) is Rel.Op.Exclude.Type.StructKey -> structKeysToRemove.add(leafType.key.lowercase()) @@ -80,33 +73,34 @@ internal class RelExclude( } } } - val finalStruct = structValue.entries.mapNotNull { structField -> - if (structSymbolsToRemove.contains(structField.first) || structKeysToRemove.contains(structField.first.lowercase())) { + val structSupplier = IteratorSupplier { structValue.structFields } + val finalStruct = structSupplier.mapNotNull { structField -> + if (structSymbolsToRemove.contains(structField.name) || structKeysToRemove.contains(structField.name.lowercase())) { // struct attr is to be removed at current level null } else { // deeper level exclusions - val name = structField.first - var value = structField.second + val name = structField.name + var value = structField.value // apply struct key exclusions at deeper levels val structKey = relOpExcludeTypeStructKey(name) branches[structKey]?.let { - value = exclude(value, it) + value = excludeValue(value, it) } // apply struct symbol exclusions at deeper levels val structSymbol = relOpExcludeTypeStructSymbol(name) branches[structSymbol]?.let { - value = exclude(value, it) + value = excludeValue(value, it) } // apply struct wildcard exclusions at deeper levels val structWildcard = relOpExcludeTypeStructWildcard() branches[structWildcard]?.let { - value = exclude(value, it) + value = excludeValue(value, it) } Pair(name, value) } - } - return structValue(finalStruct) + }.map { StructField.of(it.first, it.second) } + return PQLValue.structValue(finalStruct) } /** @@ -114,21 +108,21 @@ internal class RelExclude( * (i.e. [PartiQLValueType.LIST], [PartiQLValueType.BAG], or [PartiQLValueType.SEXP]). */ @OptIn(PartiQLValueExperimental::class) - private fun newCollValue(type: PartiQLValueType, coll: Iterable): PartiQLValue { + private fun newCollValue(type: PartiQLValueType, coll: Iterable): PQLValue { return when (type) { - PartiQLValueType.LIST -> listValue(coll) - PartiQLValueType.BAG -> bagValue(coll) - PartiQLValueType.SEXP -> sexpValue(coll) + PartiQLValueType.LIST -> PQLValue.listValue(coll) + PartiQLValueType.BAG -> PQLValue.bagValue(coll) + PartiQLValueType.SEXP -> PQLValue.sexpValue(coll) else -> error("Collection type required") } } @OptIn(PartiQLValueExperimental::class) - private fun exclude( - coll: CollectionValue<*>, + private fun excludeCollection( + coll: Iterable, type: PartiQLValueType, exclusions: List - ): PartiQLValue { + ): PQLValue { val indexesToRemove = mutableSetOf() val branches = mutableMapOf>() exclusions.forEach { exclusion -> @@ -161,17 +155,17 @@ internal class RelExclude( } else { // deeper level exclusions var value = element - if (coll is ListValue || coll is SexpValue) { + if (type == PartiQLValueType.LIST || type == PartiQLValueType.SEXP) { // apply collection index exclusions at deeper levels for lists and sexps val collIndex = relOpExcludeTypeCollIndex(index) branches[collIndex]?.let { - value = exclude(element, it) + value = excludeValue(element, it) } } // apply collection wildcard exclusions at deeper levels for lists, bags, and sexps val collWildcard = relOpExcludeTypeCollWildcard() branches[collWildcard]?.let { - value = exclude(value, it) + value = excludeValue(value, it) } value } @@ -180,12 +174,12 @@ internal class RelExclude( } @OptIn(PartiQLValueExperimental::class) - private fun exclude(initialPartiQLValue: PartiQLValue, exclusions: List): PartiQLValue { - return when (initialPartiQLValue) { - is StructValue<*> -> exclude(initialPartiQLValue, exclusions) - is BagValue<*> -> exclude(initialPartiQLValue, PartiQLValueType.BAG, exclusions) - is ListValue<*> -> exclude(initialPartiQLValue, PartiQLValueType.LIST, exclusions) - is SexpValue<*> -> exclude(initialPartiQLValue, PartiQLValueType.SEXP, exclusions) + private fun excludeValue(initialPartiQLValue: PQLValue, exclusions: List): PQLValue { + return when (initialPartiQLValue.type) { + PartiQLValueType.STRUCT -> excludeStruct(initialPartiQLValue, exclusions) + PartiQLValueType.BAG -> excludeCollection(IteratorSupplier { initialPartiQLValue.bagValues }, initialPartiQLValue.type, exclusions) + PartiQLValueType.LIST -> excludeCollection(IteratorSupplier { initialPartiQLValue.listValues }, initialPartiQLValue.type, exclusions) + PartiQLValueType.SEXP -> excludeCollection(IteratorSupplier { initialPartiQLValue.sexpValues }, initialPartiQLValue.type, exclusions) else -> { initialPartiQLValue } diff --git a/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rel/RelFilter.kt b/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rel/RelFilter.kt index 2081f14d5..fbce3305e 100644 --- a/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rel/RelFilter.kt +++ b/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rel/RelFilter.kt @@ -2,9 +2,8 @@ package org.partiql.eval.internal.operator.rel import org.partiql.eval.internal.Environment import org.partiql.eval.internal.Record +import org.partiql.eval.internal.helpers.ValueUtility.isTrue import org.partiql.eval.internal.operator.Operator -import org.partiql.value.BoolValue -import org.partiql.value.PartiQLValueExperimental internal class RelFilter( val input: Operator.Relation, @@ -31,9 +30,8 @@ internal class RelFilter( input.close() } - @OptIn(PartiQLValueExperimental::class) private fun conditionIsTrue(record: Record, expr: Operator.Expr): Boolean { val condition = expr.eval(env.push(record)) - return condition is BoolValue && condition.value == true + return condition.isTrue() } } diff --git a/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rel/RelJoinNestedLoop.kt b/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rel/RelJoinNestedLoop.kt index cb39e4818..2f0c10a3c 100644 --- a/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rel/RelJoinNestedLoop.kt +++ b/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rel/RelJoinNestedLoop.kt @@ -1,12 +1,14 @@ package org.partiql.eval.internal.operator.rel +import org.partiql.eval.PQLValue +import org.partiql.eval.StructField import org.partiql.eval.internal.Environment import org.partiql.eval.internal.Record +import org.partiql.eval.internal.helpers.IteratorSupplier +import org.partiql.eval.internal.helpers.ValueUtility.isTrue import org.partiql.eval.internal.operator.Operator -import org.partiql.value.BoolValue -import org.partiql.value.PartiQLValue import org.partiql.value.PartiQLValueExperimental -import org.partiql.value.StructValue +import org.partiql.value.PartiQLValueType import org.partiql.value.nullValue import org.partiql.value.structValue @@ -73,12 +75,6 @@ internal abstract class RelJoinNestedLoop : RelPeeking() { rhs.close() } - @OptIn(PartiQLValueExperimental::class) - private fun PartiQLValue.isTrue(): Boolean { - return this is BoolValue && this.value == true - } - - @OptIn(PartiQLValueExperimental::class) internal fun Record.padNull() { this.values.indices.forEach { index -> this.values[index] = values[index].padNull() @@ -86,13 +82,15 @@ internal abstract class RelJoinNestedLoop : RelPeeking() { } @OptIn(PartiQLValueExperimental::class) - private fun PartiQLValue.padNull(): PartiQLValue { - return when (this) { - is StructValue<*> -> { - val newFields = this.entries.map { it.first to nullValue() } - structValue(newFields) + private fun PQLValue.padNull(): PQLValue { + return when (this.type) { + PartiQLValueType.STRUCT -> { + val newFields = IteratorSupplier { this.structFields }.map { + StructField.of(it.name, PQLValue.nullValue()) + } + PQLValue.structValue(newFields) } - else -> nullValue() + else -> PQLValue.nullValue() } } } diff --git a/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rel/RelLimit.kt b/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rel/RelLimit.kt index f0b9a0a21..f3a5c2b75 100644 --- a/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rel/RelLimit.kt +++ b/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rel/RelLimit.kt @@ -1,10 +1,9 @@ package org.partiql.eval.internal.operator.rel -import org.partiql.errors.TypeCheckException import org.partiql.eval.internal.Environment import org.partiql.eval.internal.Record +import org.partiql.eval.internal.helpers.ValueUtility.getBigIntCoerced import org.partiql.eval.internal.operator.Operator -import org.partiql.value.NumericValue import org.partiql.value.PartiQLValueExperimental import java.math.BigInteger @@ -22,11 +21,7 @@ internal class RelLimit( _seen = BigInteger.ZERO val l = limit.eval(env.push(Record.empty)) - if (l is NumericValue<*>) { - _limit = l.toInt().value!! - } else { - throw TypeCheckException() - } + _limit = l.getBigIntCoerced() } override fun hasNext(): Boolean { diff --git a/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rel/RelOffset.kt b/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rel/RelOffset.kt index f9bbef6e5..a24bac50c 100644 --- a/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rel/RelOffset.kt +++ b/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rel/RelOffset.kt @@ -1,10 +1,9 @@ package org.partiql.eval.internal.operator.rel -import org.partiql.errors.TypeCheckException import org.partiql.eval.internal.Environment import org.partiql.eval.internal.Record +import org.partiql.eval.internal.helpers.ValueUtility.getBigIntCoerced import org.partiql.eval.internal.operator.Operator -import org.partiql.value.NumericValue import org.partiql.value.PartiQLValueExperimental import java.math.BigInteger @@ -24,11 +23,7 @@ internal class RelOffset( _seen = BigInteger.ZERO val o = offset.eval(env.push(Record.empty)) - if (o is NumericValue<*>) { - _offset = o.toInt().value!! - } else { - throw TypeCheckException() - } + _offset = o.getBigIntCoerced() } override fun hasNext(): Boolean { diff --git a/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rel/RelProject.kt b/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rel/RelProject.kt index 774b7119c..657d7ae63 100644 --- a/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rel/RelProject.kt +++ b/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rel/RelProject.kt @@ -3,9 +3,7 @@ package org.partiql.eval.internal.operator.rel import org.partiql.eval.internal.Environment import org.partiql.eval.internal.Record import org.partiql.eval.internal.operator.Operator -import org.partiql.value.PartiQLValueExperimental -@OptIn(PartiQLValueExperimental::class) internal class RelProject( private val input: Operator.Relation, private val projections: List diff --git a/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rel/RelScan.kt b/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rel/RelScan.kt index 0c31d3abe..1035e56d7 100644 --- a/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rel/RelScan.kt +++ b/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rel/RelScan.kt @@ -5,8 +5,8 @@ import org.partiql.eval.internal.Environment import org.partiql.eval.internal.Record import org.partiql.eval.internal.helpers.RecordValueIterator import org.partiql.eval.internal.operator.Operator -import org.partiql.value.CollectionValue import org.partiql.value.PartiQLValueExperimental +import org.partiql.value.PartiQLValueType @OptIn(PartiQLValueExperimental::class) internal class RelScan( @@ -17,8 +17,10 @@ internal class RelScan( override fun open(env: Environment) { val r = expr.eval(env.push(Record.empty)) - records = when (r) { - is CollectionValue<*> -> RecordValueIterator(r) + records = when (r.type) { + PartiQLValueType.LIST -> RecordValueIterator(r.listValues) + PartiQLValueType.BAG -> RecordValueIterator(r.bagValues) + PartiQLValueType.SEXP -> RecordValueIterator(r.sexpValues) else -> { close() throw TypeCheckException() diff --git a/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rel/RelScanIndexed.kt b/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rel/RelScanIndexed.kt index 90f5dd774..250d4e223 100644 --- a/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rel/RelScanIndexed.kt +++ b/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rel/RelScanIndexed.kt @@ -1,32 +1,31 @@ package org.partiql.eval.internal.operator.rel import org.partiql.errors.TypeCheckException +import org.partiql.eval.PQLValue import org.partiql.eval.internal.Environment import org.partiql.eval.internal.Record import org.partiql.eval.internal.operator.Operator -import org.partiql.value.BagValue -import org.partiql.value.CollectionValue -import org.partiql.value.PartiQLValue import org.partiql.value.PartiQLValueExperimental -import org.partiql.value.int64Value +import org.partiql.value.PartiQLValueType @OptIn(PartiQLValueExperimental::class) internal class RelScanIndexed( private val expr: Operator.Expr ) : Operator.Relation { - private lateinit var iterator: Iterator + private lateinit var iterator: Iterator private var index: Long = 0 override fun open(env: Environment) { val r = expr.eval(env.push(Record.empty)) index = 0 - iterator = when (r) { - is BagValue<*> -> { + iterator = when (r.type) { + PartiQLValueType.BAG -> { close() throw TypeCheckException() } - is CollectionValue<*> -> r.iterator() + PartiQLValueType.LIST -> r.listValues + PartiQLValueType.SEXP -> r.sexpValues else -> { close() throw TypeCheckException() @@ -42,7 +41,7 @@ internal class RelScanIndexed( val i = index val v = iterator.next() index += 1 - return Record.of(v, int64Value(i)) + return Record.of(v, PQLValue.int64Value(i)) } override fun close() {} diff --git a/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rel/RelScanIndexedPermissive.kt b/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rel/RelScanIndexedPermissive.kt index 3f0999fb8..6986cdee8 100644 --- a/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rel/RelScanIndexedPermissive.kt +++ b/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rel/RelScanIndexedPermissive.kt @@ -1,33 +1,31 @@ package org.partiql.eval.internal.operator.rel +import org.partiql.eval.PQLValue import org.partiql.eval.internal.Environment import org.partiql.eval.internal.Record import org.partiql.eval.internal.operator.Operator -import org.partiql.value.BagValue -import org.partiql.value.CollectionValue -import org.partiql.value.PartiQLValue import org.partiql.value.PartiQLValueExperimental -import org.partiql.value.int64Value -import org.partiql.value.missingValue +import org.partiql.value.PartiQLValueType @OptIn(PartiQLValueExperimental::class) internal class RelScanIndexedPermissive( private val expr: Operator.Expr ) : Operator.Relation { - private lateinit var iterator: Iterator + private lateinit var iterator: Iterator private var index: Long = 0 private var isIndexable: Boolean = true override fun open(env: Environment) { val r = expr.eval(env.push(Record.empty)) index = 0 - iterator = when (r) { - is BagValue<*> -> { + iterator = when (r.type) { + PartiQLValueType.BAG -> { isIndexable = false - r.iterator() + r.bagValues } - is CollectionValue<*> -> r.iterator() + PartiQLValueType.LIST -> r.listValues + PartiQLValueType.SEXP -> r.sexpValues else -> { isIndexable = false iterator { yield(r) } @@ -45,9 +43,9 @@ internal class RelScanIndexedPermissive( true -> { val i = index index += 1 - Record.of(v, int64Value(i)) + Record.of(v, PQLValue.int64Value(i)) } - false -> Record.of(v, missingValue()) + false -> Record.of(v, PQLValue.missingValue()) } } diff --git a/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rel/RelScanPermissive.kt b/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rel/RelScanPermissive.kt index a5d5c2cbe..3ac49953d 100644 --- a/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rel/RelScanPermissive.kt +++ b/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rel/RelScanPermissive.kt @@ -4,8 +4,8 @@ import org.partiql.eval.internal.Environment import org.partiql.eval.internal.Record import org.partiql.eval.internal.helpers.RecordValueIterator import org.partiql.eval.internal.operator.Operator -import org.partiql.value.CollectionValue import org.partiql.value.PartiQLValueExperimental +import org.partiql.value.PartiQLValueType @OptIn(PartiQLValueExperimental::class) internal class RelScanPermissive( @@ -16,8 +16,10 @@ internal class RelScanPermissive( override fun open(env: Environment) { val r = expr.eval(env.push(Record.empty)) - records = when (r) { - is CollectionValue<*> -> RecordValueIterator(r) + records = when (r.type) { + PartiQLValueType.BAG -> RecordValueIterator(r.bagValues) + PartiQLValueType.LIST -> RecordValueIterator(r.listValues) + PartiQLValueType.SEXP -> RecordValueIterator(r.sexpValues) else -> iterator { yield(Record.of(r)) } } } diff --git a/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rel/RelSort.kt b/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rel/RelSort.kt index d694ddf38..32f3819b9 100644 --- a/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rel/RelSort.kt +++ b/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rel/RelSort.kt @@ -32,8 +32,9 @@ internal class RelSort( private val comparator = object : Comparator { override fun compare(l: Record, r: Record): Int { specs.forEach { spec -> - val lVal = spec.first.eval(env.push(l)) - val rVal = spec.first.eval(env.push(r)) + // TODO: Write comparator for PQLValue + val lVal = spec.first.eval(env.push(l)).toPartiQLValue() + val rVal = spec.first.eval(env.push(r)).toPartiQLValue() // DESC_NULLS_FIRST(l, r) == ASC_NULLS_LAST(r, l) // DESC_NULLS_LAST(l, r) == ASC_NULLS_FIRST(r, l) diff --git a/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rel/RelUnpivot.kt b/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rel/RelUnpivot.kt index 233cc334c..64b85227c 100644 --- a/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rel/RelUnpivot.kt +++ b/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rel/RelUnpivot.kt @@ -1,15 +1,13 @@ package org.partiql.eval.internal.operator.rel import org.partiql.errors.TypeCheckException +import org.partiql.eval.PQLValue +import org.partiql.eval.StructField import org.partiql.eval.internal.Environment import org.partiql.eval.internal.Record import org.partiql.eval.internal.operator.Operator -import org.partiql.value.MissingValue -import org.partiql.value.PartiQLValue import org.partiql.value.PartiQLValueExperimental -import org.partiql.value.StructValue -import org.partiql.value.stringValue -import org.partiql.value.structValue +import org.partiql.value.PartiQLValueType /** * The unpivot operator produces a bag of records from a struct. @@ -23,21 +21,21 @@ internal sealed class RelUnpivot : Operator.Relation { /** * Iterator of the struct fields. */ - private lateinit var _iterator: Iterator> + private lateinit var _iterator: Iterator internal lateinit var env: Environment /** * Each mode overrides. */ - abstract fun struct(): StructValue<*> + abstract fun struct(): PQLValue /** * Initialize the _iterator from the concrete implementation's struct() */ override fun open(env: Environment) { this.env = env - _iterator = struct().entries.iterator() + _iterator = struct().structFields } override fun hasNext(): Boolean { @@ -46,8 +44,8 @@ internal sealed class RelUnpivot : Operator.Relation { override fun next(): Record { val f = _iterator.next() - val k = stringValue(f.first) - val v = f.second + val k = PQLValue.stringValue(f.name) + val v = f.value return Record.of(k, v) } @@ -60,9 +58,9 @@ internal sealed class RelUnpivot : Operator.Relation { */ class Strict(private val expr: Operator.Expr) : RelUnpivot() { - override fun struct(): StructValue<*> { + override fun struct(): PQLValue { val v = expr.eval(env.push(Record.empty)) - if (v !is StructValue<*>) { + if (v.type != PartiQLValueType.STRUCT) { throw TypeCheckException() } return v @@ -80,10 +78,13 @@ internal sealed class RelUnpivot : Operator.Relation { */ class Permissive(private val expr: Operator.Expr) : RelUnpivot() { - override fun struct(): StructValue<*> = when (val v = expr.eval(env.push(Record.empty))) { - is StructValue<*> -> v - is MissingValue -> structValue() - else -> structValue("_1" to v) + override fun struct(): PQLValue { + val v = expr.eval(env.push(Record.empty)) + return when (v.type) { + PartiQLValueType.STRUCT -> v + PartiQLValueType.MISSING -> PQLValue.structValue(emptyList()) + else -> PQLValue.structValue(listOf(StructField.of("_1", v))) + } } } } diff --git a/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprCallDynamic.kt b/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprCallDynamic.kt index 613a666ad..fbca16600 100644 --- a/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprCallDynamic.kt +++ b/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprCallDynamic.kt @@ -1,6 +1,7 @@ package org.partiql.eval.internal.operator.rex import org.partiql.errors.TypeCheckException +import org.partiql.eval.PQLValue import org.partiql.eval.internal.Environment import org.partiql.eval.internal.helpers.toNull import org.partiql.eval.internal.operator.Operator @@ -28,11 +29,14 @@ internal class ExprCallDynamic( private val candidateIndex = CandidateIndex.All(candidates) - override fun eval(env: Environment): PartiQLValue { + override fun eval(env: Environment): PQLValue { val actualArgs = args.map { it.eval(env) }.toTypedArray() val actualTypes = actualArgs.map { it.type } candidateIndex.get(actualTypes)?.let { - return it.eval(actualArgs, env) + val transformedArgs = Array(actualArgs.size) { + actualArgs[it].toPartiQLValue() + } + return it.eval(transformedArgs, env) } val errorString = buildString { val argString = actualArgs.joinToString(", ") @@ -59,17 +63,17 @@ internal class ExprCallDynamic( */ private val nil = fn.signature.returns.toNull() - fun eval(originalArgs: Array, env: Environment): PartiQLValue { + fun eval(originalArgs: Array, env: Environment): PQLValue { val args = originalArgs.mapIndexed { i, arg -> if (arg.isNull && fn.signature.isNullCall) { - return nil() + return PQLValue.of(nil()) } when (val c = coercions[i]) { null -> arg - else -> ExprCast(ExprLiteral(arg), c).eval(env) + else -> ExprCast(ExprLiteral(PQLValue.of(arg)), c).eval(env).toPartiQLValue() } }.toTypedArray() - return fn.invoke(args) + return PQLValue.of(fn.invoke(args)) } } diff --git a/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprCallStatic.kt b/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprCallStatic.kt index b130a7ea9..2b7dc0096 100644 --- a/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprCallStatic.kt +++ b/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprCallStatic.kt @@ -1,11 +1,11 @@ package org.partiql.eval.internal.operator.rex +import org.partiql.eval.PQLValue import org.partiql.eval.internal.Environment import org.partiql.eval.internal.helpers.toNull import org.partiql.eval.internal.operator.Operator import org.partiql.spi.fn.Fn import org.partiql.spi.fn.FnExperimental -import org.partiql.value.PartiQLValue import org.partiql.value.PartiQLValueExperimental @OptIn(FnExperimental::class, PartiQLValueExperimental::class) @@ -19,13 +19,13 @@ internal class ExprCallStatic( */ private val nil = fn.signature.returns.toNull() - override fun eval(env: Environment): PartiQLValue { + override fun eval(env: Environment): PQLValue { // Evaluate arguments val args = inputs.map { input -> val r = input.eval(env) - if (r.isNull && fn.signature.isNullCall) return nil() - r + if (r.isNull && fn.signature.isNullCall) return PQLValue.of(nil()) + r.toPartiQLValue() }.toTypedArray() - return fn.invoke(args) + return PQLValue.of(fn.invoke(args)) } } diff --git a/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprCase.kt b/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprCase.kt index b658180c3..9b05c84d8 100644 --- a/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprCase.kt +++ b/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprCase.kt @@ -1,10 +1,10 @@ package org.partiql.eval.internal.operator.rex +import org.partiql.eval.PQLValue import org.partiql.eval.internal.Environment import org.partiql.eval.internal.operator.Operator -import org.partiql.value.BoolValue -import org.partiql.value.PartiQLValue import org.partiql.value.PartiQLValueExperimental +import org.partiql.value.PartiQLValueType internal class ExprCase( private val branches: List>, @@ -12,7 +12,7 @@ internal class ExprCase( ) : Operator.Expr { @OptIn(PartiQLValueExperimental::class) - override fun eval(env: Environment): PartiQLValue { + override fun eval(env: Environment): PQLValue { branches.forEach { branch -> val condition = branch.first.eval(env) if (condition.isTrue()) { @@ -23,7 +23,7 @@ internal class ExprCase( } @OptIn(PartiQLValueExperimental::class) - private fun PartiQLValue.isTrue(): Boolean { - return this is BoolValue && this.value == true + private fun PQLValue.isTrue(): Boolean { + return this.type == PartiQLValueType.BOOL && !this.isNull && this.boolValue } } diff --git a/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprCast.kt b/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprCast.kt index cd8154c24..42a7624f6 100644 --- a/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprCast.kt +++ b/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprCast.kt @@ -6,6 +6,7 @@ import com.amazon.ionelement.api.IonElementException import com.amazon.ionelement.api.createIonElementLoader import org.partiql.errors.DataException import org.partiql.errors.TypeCheckException +import org.partiql.eval.PQLValue import org.partiql.eval.internal.Environment import org.partiql.eval.internal.operator.Operator import org.partiql.plan.Ref @@ -60,10 +61,10 @@ import java.math.BigInteger // TODO: This is incomplete internal class ExprCast(val arg: Operator.Expr, val cast: Ref.Cast) : Operator.Expr { @OptIn(PartiQLValueExperimental::class) - override fun eval(env: Environment): PartiQLValue { - val arg = arg.eval(env) + override fun eval(env: Environment): PQLValue { + val arg = arg.eval(env).toPartiQLValue() try { - return when (arg.type) { + val partiqlValue = when (arg.type) { PartiQLValueType.ANY -> TODO("Not Possible") PartiQLValueType.BOOL -> castFromBool(arg as BoolValue, cast.target) PartiQLValueType.INT8 -> castFromNumeric(arg as Int8Value, cast.target) @@ -93,6 +94,7 @@ internal class ExprCast(val arg: Operator.Expr, val cast: Ref.Cast) : Operator.E PartiQLValueType.NULL -> castFromNull(arg as NullValue, cast.target) PartiQLValueType.MISSING -> error("cast from MISSING should be handled by Typer") } + return PQLValue.of(partiqlValue) } catch (e: DataException) { throw TypeCheckException() } diff --git a/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprCoalesce.kt b/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprCoalesce.kt index 09b2438fe..34da131e5 100644 --- a/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprCoalesce.kt +++ b/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprCoalesce.kt @@ -1,24 +1,23 @@ package org.partiql.eval.internal.operator.rex +import org.partiql.eval.PQLValue import org.partiql.eval.internal.Environment import org.partiql.eval.internal.operator.Operator -import org.partiql.value.PartiQLValue import org.partiql.value.PartiQLValueExperimental import org.partiql.value.PartiQLValueType -import org.partiql.value.nullValue internal class ExprCoalesce( private val args: Array ) : Operator.Expr { @PartiQLValueExperimental - override fun eval(env: Environment): PartiQLValue { + override fun eval(env: Environment): PQLValue { for (arg in args) { val result = arg.eval(env) if (!result.isNull && result.type != PartiQLValueType.MISSING) { return result } } - return nullValue() + return PQLValue.nullValue() } } diff --git a/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprCollection.kt b/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprCollection.kt index 7a08733ca..a35bb93dc 100644 --- a/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprCollection.kt +++ b/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprCollection.kt @@ -1,16 +1,13 @@ package org.partiql.eval.internal.operator.rex +import org.partiql.eval.PQLValue import org.partiql.eval.internal.Environment import org.partiql.eval.internal.operator.Operator import org.partiql.types.BagType import org.partiql.types.ListType import org.partiql.types.SexpType import org.partiql.types.StaticType -import org.partiql.value.PartiQLValue import org.partiql.value.PartiQLValueExperimental -import org.partiql.value.bagValue -import org.partiql.value.listValue -import org.partiql.value.sexpValue internal class ExprCollection( private val values: List, @@ -18,11 +15,11 @@ internal class ExprCollection( ) : Operator.Expr { @PartiQLValueExperimental - override fun eval(env: Environment): PartiQLValue { + override fun eval(env: Environment): PQLValue { return when (type) { - is BagType -> bagValue(values.map { it.eval(env) }) - is SexpType -> sexpValue(values.map { it.eval(env) }) - is ListType -> listValue(values.map { it.eval(env) }) + is BagType -> PQLValue.bagValue(values.map { it.eval(env) }) + is SexpType -> PQLValue.sexpValue(values.map { it.eval(env) }) + is ListType -> PQLValue.listValue(values.map { it.eval(env) }) else -> error("Unsupported type for collection $type") } } diff --git a/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprLiteral.kt b/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprLiteral.kt index c7c2ac8af..4a84fff77 100644 --- a/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprLiteral.kt +++ b/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprLiteral.kt @@ -1,13 +1,13 @@ package org.partiql.eval.internal.operator.rex +import org.partiql.eval.PQLValue import org.partiql.eval.internal.Environment import org.partiql.eval.internal.operator.Operator -import org.partiql.value.PartiQLValue import org.partiql.value.PartiQLValueExperimental -internal class ExprLiteral @OptIn(PartiQLValueExperimental::class) constructor(private val value: PartiQLValue) : Operator.Expr { +internal class ExprLiteral @OptIn(PartiQLValueExperimental::class) constructor(private val value: PQLValue) : Operator.Expr { @PartiQLValueExperimental - override fun eval(env: Environment): PartiQLValue { + override fun eval(env: Environment): PQLValue { return value } } diff --git a/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprMissing.kt b/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprMissing.kt index b51627664..020465031 100644 --- a/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprMissing.kt +++ b/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprMissing.kt @@ -1,17 +1,15 @@ package org.partiql.eval.internal.operator.rex import org.partiql.errors.TypeCheckException +import org.partiql.eval.PQLValue import org.partiql.eval.internal.Environment import org.partiql.eval.internal.operator.Operator -import org.partiql.value.PartiQLValue -import org.partiql.value.PartiQLValueExperimental internal class ExprMissing( private val message: String, ) : Operator.Expr { - @OptIn(PartiQLValueExperimental::class) - override fun eval(env: Environment): PartiQLValue { + override fun eval(env: Environment): PQLValue { throw TypeCheckException(message) } } diff --git a/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprNullIf.kt b/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprNullIf.kt index 63b994af9..efda7b1c7 100644 --- a/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprNullIf.kt +++ b/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprNullIf.kt @@ -1,10 +1,10 @@ package org.partiql.eval.internal.operator.rex +import org.partiql.eval.PQLValue import org.partiql.eval.internal.Environment import org.partiql.eval.internal.operator.Operator import org.partiql.value.PartiQLValue import org.partiql.value.PartiQLValueExperimental -import org.partiql.value.nullValue internal class ExprNullIf( private val valueExpr: Operator.Expr, @@ -15,11 +15,11 @@ internal class ExprNullIf( private val comparator = PartiQLValue.comparator() @PartiQLValueExperimental - override fun eval(env: Environment): PartiQLValue { + override fun eval(env: Environment): PQLValue { val value = valueExpr.eval(env) val nullifier = nullifierExpr.eval(env) - return when (comparator.compare(value, nullifier)) { - 0 -> nullValue() + return when (comparator.compare(value.toPartiQLValue(), nullifier.toPartiQLValue())) { + 0 -> PQLValue.nullValue() else -> value } } diff --git a/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprPathIndex.kt b/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprPathIndex.kt index ebcce61c4..2eb7ec0ed 100644 --- a/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprPathIndex.kt +++ b/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprPathIndex.kt @@ -1,19 +1,12 @@ package org.partiql.eval.internal.operator.rex -import org.partiql.errors.DataException import org.partiql.errors.TypeCheckException +import org.partiql.eval.PQLValue import org.partiql.eval.internal.Environment +import org.partiql.eval.internal.helpers.ValueUtility.getInt32Coerced import org.partiql.eval.internal.operator.Operator -import org.partiql.value.CollectionValue -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.NumericValue -import org.partiql.value.PartiQLValue import org.partiql.value.PartiQLValueExperimental -import org.partiql.value.check +import org.partiql.value.PartiQLValueType internal class ExprPathIndex( @JvmField val root: Operator.Expr, @@ -21,25 +14,20 @@ internal class ExprPathIndex( ) : Operator.Expr { @OptIn(PartiQLValueExperimental::class) - override fun eval(env: Environment): PartiQLValue { - val collection = root.eval(env).check>() + override fun eval(env: Environment): PQLValue { + val input = root.eval(env) + val iterator = when (input.type) { + PartiQLValueType.BAG -> input.bagValues + PartiQLValueType.LIST -> input.listValues + PartiQLValueType.SEXP -> input.sexpValues + else -> throw TypeCheckException() + } // Calculate index - val index = when (val k = key.eval(env)) { - is Int16Value, - is Int32Value, - is Int64Value, - is Int8Value, - is IntValue -> try { - (k as NumericValue<*>).toInt32().value - } catch (e: DataException) { - throw TypeCheckException() - } - else -> throw TypeCheckException() - } ?: throw TypeCheckException() + val k = key.eval(env) + val index = k.getInt32Coerced() // Get element - val iterator = collection.iterator() var i = 0 while (iterator.hasNext()) { val v = iterator.next() diff --git a/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprPathKey.kt b/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprPathKey.kt index 34000c7fb..de707a581 100644 --- a/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprPathKey.kt +++ b/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprPathKey.kt @@ -1,13 +1,12 @@ package org.partiql.eval.internal.operator.rex import org.partiql.errors.TypeCheckException +import org.partiql.eval.PQLValue import org.partiql.eval.internal.Environment +import org.partiql.eval.internal.helpers.ValueUtility.check import org.partiql.eval.internal.operator.Operator -import org.partiql.value.PartiQLValue import org.partiql.value.PartiQLValueExperimental -import org.partiql.value.StringValue -import org.partiql.value.StructValue -import org.partiql.value.check +import org.partiql.value.PartiQLValueType internal class ExprPathKey( @JvmField val root: Operator.Expr, @@ -15,10 +14,18 @@ internal class ExprPathKey( ) : Operator.Expr { @OptIn(PartiQLValueExperimental::class) - override fun eval(env: Environment): PartiQLValue { - val rootEvaluated = root.eval(env).check>() - val keyEvaluated = key.eval(env).check() - val keyString = keyEvaluated.value ?: error("String value was null") - return rootEvaluated[keyString] ?: throw TypeCheckException() + override fun eval(env: Environment): PQLValue { + val rootEvaluated = root.eval(env).check(PartiQLValueType.STRUCT) + val keyEvaluated = key.eval(env).check(PartiQLValueType.STRING) + if (rootEvaluated.isNull || keyEvaluated.isNull) { + return PQLValue.nullValue() + } + val keyString = keyEvaluated.stringValue + for (entry in rootEvaluated.structFields) { + if (entry.name == keyString) { + return entry.value + } + } + throw TypeCheckException() } } diff --git a/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprPathSymbol.kt b/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprPathSymbol.kt index 17cce2315..e9f758c42 100644 --- a/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprPathSymbol.kt +++ b/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprPathSymbol.kt @@ -1,13 +1,12 @@ package org.partiql.eval.internal.operator.rex import org.partiql.errors.TypeCheckException +import org.partiql.eval.PQLValue import org.partiql.eval.internal.Environment +import org.partiql.eval.internal.helpers.ValueUtility.check import org.partiql.eval.internal.operator.Operator -import org.partiql.value.PartiQLValue import org.partiql.value.PartiQLValueExperimental -import org.partiql.value.StructValue -import org.partiql.value.check -import org.partiql.value.nullValue +import org.partiql.value.PartiQLValueType internal class ExprPathSymbol( @JvmField val root: Operator.Expr, @@ -15,14 +14,14 @@ internal class ExprPathSymbol( ) : Operator.Expr { @OptIn(PartiQLValueExperimental::class) - override fun eval(env: Environment): PartiQLValue { - val struct = root.eval(env).check>() + override fun eval(env: Environment): PQLValue { + val struct = root.eval(env).check(PartiQLValueType.STRUCT) if (struct.isNull) { - return nullValue() + return PQLValue.nullValue() } - for ((k, v) in struct.entries) { - if (k.equals(symbol, ignoreCase = true)) { - return v + for (entry in struct.structFields) { + if (entry.name.equals(symbol, ignoreCase = true)) { + return entry.value } } throw TypeCheckException("Couldn't find symbol '$symbol' in $struct.") diff --git a/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprPermissive.kt b/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprPermissive.kt index 3021ceba0..122c78fba 100644 --- a/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprPermissive.kt +++ b/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprPermissive.kt @@ -2,24 +2,21 @@ package org.partiql.eval.internal.operator.rex import org.partiql.errors.CardinalityViolation import org.partiql.errors.TypeCheckException +import org.partiql.eval.PQLValue import org.partiql.eval.internal.Environment import org.partiql.eval.internal.operator.Operator -import org.partiql.value.PartiQLValue -import org.partiql.value.PartiQLValueExperimental -import org.partiql.value.missingValue internal class ExprPermissive( val target: Operator.Expr ) : Operator.Expr { - @OptIn(PartiQLValueExperimental::class) - override fun eval(env: Environment): PartiQLValue { + override fun eval(env: Environment): PQLValue { return try { target.eval(env) } catch (e: TypeCheckException) { - missingValue() + PQLValue.missingValue() } catch (e: CardinalityViolation) { - missingValue() + PQLValue.missingValue() } } } diff --git a/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprPivot.kt b/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprPivot.kt index aa206d4f1..3c71a98ba 100644 --- a/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprPivot.kt +++ b/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprPivot.kt @@ -1,31 +1,29 @@ package org.partiql.eval.internal.operator.rex +import org.partiql.eval.PQLValue +import org.partiql.eval.StructField import org.partiql.eval.internal.Environment +import org.partiql.eval.internal.helpers.ValueUtility.getText import org.partiql.eval.internal.operator.Operator -import org.partiql.value.PartiQLValue -import org.partiql.value.PartiQLValueExperimental -import org.partiql.value.StringValue -import org.partiql.value.check -import org.partiql.value.structValue -@OptIn(PartiQLValueExperimental::class) internal class ExprPivot( private val input: Operator.Relation, private val key: Operator.Expr, private val value: Operator.Expr, ) : Operator.Expr { - override fun eval(env: Environment): PartiQLValue { + override fun eval(env: Environment): PQLValue { input.open(env) - val fields = mutableListOf>() + val fields = mutableListOf() while (input.hasNext()) { val row = input.next() val newEnv = env.push(row) - val k = key.eval(newEnv).check() + val k = key.eval(newEnv) + val keyString = k.getText() val v = value.eval(newEnv) - fields.add(k.value!! to v) + fields.add(StructField.of(keyString, v)) } input.close() - return structValue(fields) + return PQLValue.structValue(fields) } } diff --git a/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprPivotPermissive.kt b/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprPivotPermissive.kt index 0393323de..72e22eb3c 100644 --- a/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprPivotPermissive.kt +++ b/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprPivotPermissive.kt @@ -1,30 +1,34 @@ package org.partiql.eval.internal.operator.rex +import org.partiql.errors.TypeCheckException +import org.partiql.eval.PQLValue +import org.partiql.eval.StructField import org.partiql.eval.internal.Environment +import org.partiql.eval.internal.helpers.ValueUtility.getText import org.partiql.eval.internal.operator.Operator -import org.partiql.value.PartiQLValue -import org.partiql.value.PartiQLValueExperimental -import org.partiql.value.StringValue -import org.partiql.value.structValue -@OptIn(PartiQLValueExperimental::class) internal class ExprPivotPermissive( private val input: Operator.Relation, private val key: Operator.Expr, private val value: Operator.Expr, ) : Operator.Expr { - override fun eval(env: Environment): PartiQLValue { + override fun eval(env: Environment): PQLValue { input.open(env) - val fields = mutableListOf>() + val fields = mutableListOf() while (input.hasNext()) { val row = input.next() val newEnv = env.push(row) - val k = key.eval(newEnv) as? StringValue ?: continue + val keyString = try { + val k = key.eval(newEnv) + k.getText() + } catch (_: TypeCheckException) { + continue + } val v = value.eval(newEnv) - fields.add(k.value!! to v) + fields.add(StructField.of(keyString, v)) } input.close() - return structValue(fields) + return PQLValue.structValue(fields) } } diff --git a/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprSelect.kt b/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprSelect.kt index 9ab766afa..387fd10a8 100644 --- a/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprSelect.kt +++ b/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprSelect.kt @@ -1,11 +1,9 @@ package org.partiql.eval.internal.operator.rex +import org.partiql.eval.PQLValue import org.partiql.eval.internal.Environment import org.partiql.eval.internal.operator.Operator -import org.partiql.value.PartiQLValue import org.partiql.value.PartiQLValueExperimental -import org.partiql.value.bagValue -import org.partiql.value.listValue /** * Invoke the constructor over all inputs. @@ -24,10 +22,10 @@ internal class ExprSelect( private val input: Operator.Relation, private val constructor: Operator.Expr, private val env: Environment, - ) : Iterable { + ) : Iterable { - override fun iterator(): Iterator { - return object : Iterator { + override fun iterator(): Iterator { + return object : Iterator { private var _init = false override fun hasNext(): Boolean { @@ -42,21 +40,20 @@ internal class ExprSelect( return hasNext } - override fun next(): PartiQLValue { + override fun next(): PQLValue { val r = input.next() - val result = constructor.eval(env.push(r)) - return result + return constructor.eval(env.push(r)) } } } } @PartiQLValueExperimental - override fun eval(env: Environment): PartiQLValue { + override fun eval(env: Environment): PQLValue { val elements = Elements(input, constructor, env) return when (ordered) { - true -> listValue(elements) - false -> bagValue(elements) + true -> PQLValue.listValue(elements) + false -> PQLValue.bagValue(elements) } } } diff --git a/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprStruct.kt b/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprStruct.kt index 5aef5eebe..957f846d1 100644 --- a/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprStruct.kt +++ b/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprStruct.kt @@ -1,25 +1,29 @@ package org.partiql.eval.internal.operator.rex +import org.partiql.eval.PQLValue +import org.partiql.eval.StructField import org.partiql.eval.internal.Environment +import org.partiql.eval.internal.helpers.ValueUtility.getText import org.partiql.eval.internal.operator.Operator -import org.partiql.value.MissingValue -import org.partiql.value.PartiQLValue import org.partiql.value.PartiQLValueExperimental -import org.partiql.value.TextValue -import org.partiql.value.check -import org.partiql.value.structValue +import org.partiql.value.PartiQLValueType -internal class ExprStruct(val fields: List) : Operator.Expr { +internal class ExprStruct(private val fields: List) : Operator.Expr { @OptIn(PartiQLValueExperimental::class) - override fun eval(env: Environment): PartiQLValue { + override fun eval(env: Environment): PQLValue { val fields = fields.mapNotNull { - val key = it.key.eval(env).check>() - when (val value = it.value.eval(env)) { - is MissingValue -> null - else -> key.value!! to value + val key = it.key.eval(env) + if (key.isNull) { + return PQLValue.nullValue() + } + val keyString = key.getText() + val value = it.value.eval(env) + when (value.type) { + PartiQLValueType.MISSING -> null + else -> StructField.of(keyString, value) } } - return structValue(fields) + return PQLValue.structValue(fields) } internal class Field(val key: Operator.Expr, val value: Operator.Expr) diff --git a/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprSubquery.kt b/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprSubquery.kt index 98c713b79..be9cf6c3b 100644 --- a/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprSubquery.kt +++ b/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprSubquery.kt @@ -2,14 +2,13 @@ package org.partiql.eval.internal.operator.rex import org.partiql.errors.CardinalityViolation import org.partiql.errors.TypeCheckException +import org.partiql.eval.PQLValue import org.partiql.eval.internal.Environment +import org.partiql.eval.internal.helpers.IteratorSupplier +import org.partiql.eval.internal.helpers.ValueUtility.check import org.partiql.eval.internal.operator.Operator -import org.partiql.value.PartiQLValue import org.partiql.value.PartiQLValueExperimental -import org.partiql.value.StructValue -import org.partiql.value.check -import org.partiql.value.listValue -import org.partiql.value.nullValue +import org.partiql.value.PartiQLValueType /** * The PartiQL Specification talks about subqueries and how they are coerced. Specifically, subqueries are @@ -29,10 +28,10 @@ internal abstract class ExprSubquery : Operator.Expr { ) : ExprSubquery() { @PartiQLValueExperimental - override fun eval(env: Environment): PartiQLValue { - val tuple = getFirst(env) ?: return nullValue() - val values = tuple.values.iterator() - return listValue(values.asSequence().toList()) + override fun eval(env: Environment): PQLValue { + val tuple = getFirst(env) ?: return PQLValue.nullValue() + val values = IteratorSupplier { tuple.structFields }.map { it.value } + return PQLValue.listValue(values) } } @@ -42,9 +41,9 @@ internal abstract class ExprSubquery : Operator.Expr { ) : ExprSubquery() { @PartiQLValueExperimental - override fun eval(env: Environment): PartiQLValue { - val tuple = getFirst(env) ?: return nullValue() - val values = tuple.values.iterator() + override fun eval(env: Environment): PQLValue { + val tuple = getFirst(env) ?: return PQLValue.nullValue() + val values = tuple.structFields.asSequence().map { it.value }.iterator() if (values.hasNext().not()) { throw TypeCheckException() } @@ -62,17 +61,17 @@ internal abstract class ExprSubquery : Operator.Expr { * * @return the constructed [constructor]. Returns null when no rows are returned from the [input]. * @throws CardinalityViolation when more than one row is returned from the [input]. - * @throws TypeCheckException when the constructor is not a [StructValue]. + * @throws TypeCheckException when the constructor is not a [PartiQLValueType.STRUCT]. */ @OptIn(PartiQLValueExperimental::class) - fun getFirst(env: Environment): StructValue<*>? { + fun getFirst(env: Environment): PQLValue? { input.open(env) if (input.hasNext().not()) { input.close() return null } val firstRecord = input.next() - val tuple = constructor.eval(env.push(firstRecord)).check>() + val tuple = constructor.eval(env.push(firstRecord)).check(PartiQLValueType.STRUCT) if (input.hasNext()) { input.close() throw CardinalityViolation() diff --git a/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprTupleUnion.kt b/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprTupleUnion.kt index 12f9f1ecf..646079008 100644 --- a/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprTupleUnion.kt +++ b/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprTupleUnion.kt @@ -1,39 +1,29 @@ package org.partiql.eval.internal.operator.rex +import org.partiql.eval.PQLValue import org.partiql.eval.internal.Environment +import org.partiql.eval.internal.helpers.ValueUtility.check import org.partiql.eval.internal.operator.Operator -import org.partiql.value.NullValue -import org.partiql.value.PartiQLValue import org.partiql.value.PartiQLValueExperimental -import org.partiql.value.StructValue -import org.partiql.value.missingValue -import org.partiql.value.structValue +import org.partiql.value.PartiQLValueType internal class ExprTupleUnion( val args: Array ) : Operator.Expr { @OptIn(PartiQLValueExperimental::class) - override fun eval(env: Environment): PartiQLValue { - // Return MISSING on Mistyping Case + override fun eval(env: Environment): PQLValue { val tuples = args.map { - when (val arg = it.eval(env)) { - is StructValue<*> -> arg - is NullValue -> structValue(null) - else -> when (arg.isNull) { - true -> structValue(null) - false -> return missingValue() - } - } + it.eval(env).check(PartiQLValueType.STRUCT) } // Return NULL if any arguments are NULL tuples.forEach { if (it.isNull) { - return structValue(null) + return PQLValue.nullValue(PartiQLValueType.STRUCT) } } - return structValue(tuples.flatMap { it.entries }) + return PQLValue.structValue(tuples.flatMap { it.structFields.asSequence() }) } } diff --git a/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprVarGlobal.kt b/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprVarGlobal.kt index 20af79217..78066200a 100644 --- a/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprVarGlobal.kt +++ b/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprVarGlobal.kt @@ -1,10 +1,10 @@ package org.partiql.eval.internal.operator.rex +import org.partiql.eval.PQLValue import org.partiql.eval.internal.Environment import org.partiql.eval.internal.operator.Operator import org.partiql.spi.connector.ConnectorBindings import org.partiql.spi.connector.ConnectorPath -import org.partiql.value.PartiQLValue import org.partiql.value.PartiQLValueExperimental @OptIn(PartiQLValueExperimental::class) @@ -13,5 +13,6 @@ internal class ExprVarGlobal( private val bindings: ConnectorBindings, ) : Operator.Expr { - override fun eval(env: Environment): PartiQLValue = bindings.getValue(path) + // TODO: Potentially make ConnectorBindings return PQLValue + override fun eval(env: Environment): PQLValue = PQLValue.of(bindings.getValue(path)) } diff --git a/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprVarLocal.kt b/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprVarLocal.kt index f44283f98..06459ad85 100644 --- a/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprVarLocal.kt +++ b/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprVarLocal.kt @@ -1,8 +1,8 @@ package org.partiql.eval.internal.operator.rex +import org.partiql.eval.PQLValue import org.partiql.eval.internal.Environment import org.partiql.eval.internal.operator.Operator -import org.partiql.value.PartiQLValue import org.partiql.value.PartiQLValueExperimental /** @@ -13,7 +13,7 @@ internal class ExprVarLocal( ) : Operator.Expr { @PartiQLValueExperimental - override fun eval(env: Environment): PartiQLValue { + override fun eval(env: Environment): PQLValue { return env[ref] } } diff --git a/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprVarOuter.kt b/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprVarOuter.kt index 4ef0498ad..bd49837d5 100644 --- a/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprVarOuter.kt +++ b/partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprVarOuter.kt @@ -1,8 +1,8 @@ package org.partiql.eval.internal.operator.rex +import org.partiql.eval.PQLValue import org.partiql.eval.internal.Environment import org.partiql.eval.internal.operator.Operator -import org.partiql.value.PartiQLValue import org.partiql.value.PartiQLValueExperimental /** @@ -14,7 +14,7 @@ internal class ExprVarOuter( ) : Operator.Expr { @PartiQLValueExperimental - override fun eval(env: Environment): PartiQLValue { + override fun eval(env: Environment): PQLValue { var current = env repeat(depth) { current = current.next() ?: error("We ran out of environments for depth ($depth) and env: $env.") diff --git a/partiql-eval/src/test/kotlin/org/partiql/eval/internal/operator/rex/ExprCallDynamicTest.kt b/partiql-eval/src/test/kotlin/org/partiql/eval/internal/operator/rex/ExprCallDynamicTest.kt index 2344f46f7..1b9813661 100644 --- a/partiql-eval/src/test/kotlin/org/partiql/eval/internal/operator/rex/ExprCallDynamicTest.kt +++ b/partiql-eval/src/test/kotlin/org/partiql/eval/internal/operator/rex/ExprCallDynamicTest.kt @@ -5,21 +5,21 @@ import org.junit.jupiter.api.parallel.Execution import org.junit.jupiter.api.parallel.ExecutionMode import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.MethodSource +import org.partiql.eval.PQLValue +import org.partiql.eval.PQLValue.bagValue +import org.partiql.eval.PQLValue.boolValue +import org.partiql.eval.PQLValue.int32Value +import org.partiql.eval.PQLValue.listValue +import org.partiql.eval.PQLValue.stringValue import org.partiql.eval.internal.Environment +import org.partiql.eval.internal.helpers.ValueUtility.check import org.partiql.spi.fn.Fn import org.partiql.spi.fn.FnExperimental import org.partiql.spi.fn.FnParameter import org.partiql.spi.fn.FnSignature -import org.partiql.value.Int32Value import org.partiql.value.PartiQLValue import org.partiql.value.PartiQLValueExperimental import org.partiql.value.PartiQLValueType -import org.partiql.value.bagValue -import org.partiql.value.boolValue -import org.partiql.value.check -import org.partiql.value.int32Value -import org.partiql.value.listValue -import org.partiql.value.stringValue class ExprCallDynamicTest { @@ -29,8 +29,8 @@ class ExprCallDynamicTest { fun sanityTests(tc: DynamicTestCase) = tc.assert() public class DynamicTestCase @OptIn(PartiQLValueExperimental::class) constructor( - val lhs: PartiQLValue, - val rhs: PartiQLValue, + val lhs: PQLValue, + val rhs: PQLValue, val expectedIndex: Int, ) { @@ -41,8 +41,8 @@ class ExprCallDynamicTest { candidates = candidates, args = arrayOf(ExprLiteral(lhs), ExprLiteral(rhs)), ) - val result = expr.eval(Environment.empty).check() - assertEquals(expectedIndex, result.value) + val result = expr.eval(Environment.empty).check(PartiQLValueType.INT32) + assertEquals(expectedIndex, result.int32Value) } companion object { @@ -77,7 +77,7 @@ class ExprCallDynamicTest { ) ) - override fun invoke(args: Array): PartiQLValue = int32Value(index) + override fun invoke(args: Array): PartiQLValue = int32Value(index).toPartiQLValue() }, coercions = arrayOf(null, null) ) diff --git a/partiql-types/src/main/kotlin/org/partiql/value/PartiQLValue.kt b/partiql-types/src/main/kotlin/org/partiql/value/PartiQLValue.kt index 5256ded05..8048b342f 100644 --- a/partiql-types/src/main/kotlin/org/partiql/value/PartiQLValue.kt +++ b/partiql-types/src/main/kotlin/org/partiql/value/PartiQLValue.kt @@ -429,7 +429,8 @@ public abstract class BagValue : CollectionValue { val lhs = this.toList() val rhs = other.toList() // this is incorrect as it assumes ordered-ness, but we don't have a sort or hash yet - return lhs == rhs + val result = lhs == rhs + return result } override fun hashCode(): Int { @@ -548,14 +549,17 @@ public abstract class StructValue : PartiQLValue { lhs.entries.forEach { (key, values) -> val lGroup: Map = values.groupingBy { it }.eachCount() val rGroup: Map = rhs[key]!!.groupingBy { it }.eachCount() - if (lGroup != rGroup) return false + val matches = lGroup == rGroup + if (!matches) { + return false + } } return true } override fun hashCode(): Int { // TODO - return entries.hashCode() + return entries.toList().hashCode() } override fun toString(): String {