() {
+ duplicatesStrategy = DuplicatesStrategy.EXCLUDE
+}
+
publish {
artifactId = "partiql-types"
name = "PartiQL Types"
diff --git a/partiql-types/src/main/java/org/partiql/value/PartiQLCursor.java b/partiql-types/src/main/java/org/partiql/value/PartiQLCursor.java
new file mode 100644
index 0000000000..eb3bd6dcbd
--- /dev/null
+++ b/partiql-types/src/main/java/org/partiql/value/PartiQLCursor.java
@@ -0,0 +1,191 @@
+package org.partiql.value;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.Iterator;
+import org.jetbrains.annotations.NotNull;
+import org.partiql.value.datetime.Date;
+import org.partiql.value.datetime.Time;
+import org.partiql.value.datetime.Timestamp;
+
+/**
+ * Data representing a database result set, which is usually generated by executing a statement that queries the database.
+ *
+ * A {@link PartiQLCursor} object maintains a cursor pointing to its current position in the underlying data. Initially the
+ * cursor is positioned before the first value. The {@link #next()} method moves the cursor to the next value. Please use
+ * {@link #hasNext()} before calling {@link #next()}.
+ *
+ * @see PartiQLValueLoader#load(PartiQLCursor)
+ * @see PartiQLValue
+ */
+public interface PartiQLCursor extends AutoCloseable, Iterator {
+
+ @Override
+ @NotNull
+ PartiQLValueType next();
+
+ /**
+ * Positions the reader just before the contents of the current value, which must be a container (list, bag,
+ * sexp, or struct). There's no current value immediately after stepping in, so the next thing you'll want to do is call
+ * {@link #hasNext()} and {@link #next()} to move onto the first child value.
+ *
+ * If the container itself is the null value, stepIn() shall fail. Please use {@link #isNullValue()} before
+ * invoking this.
+ *
+ * At any time {@link #stepOut()} may be called to move the cursor back to (just after) the parent value, even if
+ * there are more children remaining.
+ */
+ public void stepIn();
+
+ /**
+ * Positions the iterator after the current parent's value, moving up one level in the data hierarchy. There's no
+ * current value immediately after stepping out, so the next thing you'll want to do is call {@link #hasNext()} and
+ * {@link #next()} to move onto the following value.
+ */
+ public void stepOut();
+
+ /**
+ * 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).
+ */
+ public boolean isNullValue();
+
+ /**
+ * Determines whether the current value is the missing value. Similarly, one can invoke {@link #getType()}.
+ */
+ public boolean isMissingValue();
+
+ /**
+ * @return the type of the data at the cursor.
+ */
+ @NotNull
+ public PartiQLValueType getType();
+
+ /**
+ * @return the field name of the current value; or null if there is no valid current value or if the current value
+ * is not a field of a struct.
+ */
+ public String getFieldName();
+
+ /**
+ * This is only applicable when the current value's type is {@link PartiQLValueType#STRING}.
+ */
+ @NotNull
+ String getStringValue();
+
+ /**
+ * This is only applicable when the current value's type is {@link PartiQLValueType#CHAR}.
+ */
+ @NotNull
+ String getCharValue();
+
+ /**
+ * This is only applicable when the current value's type is {@link PartiQLValueType#SYMBOL}.
+ */
+ @NotNull
+ String getSymbolValue();
+
+ /**
+ * This is only applicable when the current value's type is {@link PartiQLValueType#BOOL}.
+ */
+ public boolean getBoolValue();
+
+ /**
+ * This is only applicable when the current value's type is {@link PartiQLValueType#BINARY}.
+ */
+ public byte[] getBinaryValue();
+
+ /**
+ * This is only applicable when the current value's type is {@link PartiQLValueType#BLOB}.
+ */
+ public byte[] getBlobValue();
+
+ /**
+ * This is only applicable when the current value's type is {@link PartiQLValueType#CLOB}.
+ */
+ public byte[] getClobValue();
+
+ /**
+ * This is only applicable when the current value's type is {@link PartiQLValueType#BYTE}.
+ */
+ public byte getByteValue();
+
+ /**
+ * This is only applicable when the current value's type is {@link PartiQLValueType#DATE}.
+ */
+ @NotNull
+ public Date getDateValue();
+
+ /**
+ * This is only applicable when the current value's type is {@link PartiQLValueType#TIME}.
+ */
+ @NotNull
+ public Time getTimeValue();
+
+ /**
+ * This is only applicable when the current value's type is {@link PartiQLValueType#TIMESTAMP}.
+ */
+ @NotNull
+ public Timestamp getTimestampValue();
+
+ /**
+ * This is only applicable when the current value's type is {@link PartiQLValueType#INTERVAL}.
+ */
+ @Deprecated
+ public long getIntervalValue();
+
+ /**
+ * This is only applicable when the current value's type is {@link PartiQLValueType#INT8}.
+ */
+ public byte getInt8Value();
+
+ /**
+ * This is only applicable when the current value's type is {@link PartiQLValueType#INT16}.
+ */
+ public short getInt16Value();
+
+ /**
+ * This is only applicable when the current value's type is {@link PartiQLValueType#INT32}.
+ */
+ public int getInt32Value();
+
+ /**
+ * This is only applicable when the current value's type is {@link PartiQLValueType#INT64}.
+ */
+ public long getInt64Value();
+
+ /**
+ * This is only applicable when the current value's type is {@link PartiQLValueType#INT}.
+ */
+ @NotNull
+ public BigInteger getIntValue();
+
+ /**
+ * This is only applicable when the current value's type is {@link PartiQLValueType#FLOAT32}.
+ */
+ public float getFloat32Value();
+
+ /**
+ * This is only applicable when the current value's type is {@link PartiQLValueType#FLOAT64}.
+ */
+ public double getFloat64Value();
+
+ /**
+ * This is only applicable when the current value's type is {@link PartiQLValueType#DECIMAL}.
+ */
+ @NotNull
+ public BigDecimal getDecimalValue();
+
+ /**
+ * This is only applicable when the current value's type is {@link PartiQLValueType#DECIMAL_ARBITRARY}.
+ */
+ @NotNull
+ public BigDecimal getDecimalArbitraryValue();
+
+ /**
+ * Converts a {@link PartiQLValue} into {@link PartiQLCursor}.
+ */
+ static PartiQLCursor of(PartiQLValue value) {
+ return new PartiQLCursorDefault(value);
+ }
+}
diff --git a/partiql-types/src/main/java/org/partiql/value/PartiQLCursorDefault.java b/partiql-types/src/main/java/org/partiql/value/PartiQLCursorDefault.java
new file mode 100644
index 0000000000..bc4483f921
--- /dev/null
+++ b/partiql-types/src/main/java/org/partiql/value/PartiQLCursorDefault.java
@@ -0,0 +1,398 @@
+package org.partiql.value;
+
+import kotlin.Pair;
+import org.jetbrains.annotations.NotNull;
+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.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Objects;
+import java.util.Stack;
+
+class PartiQLCursorDefault implements PartiQLCursor {
+
+ @NotNull
+ private final Stack> iteratorStack;
+
+ @NotNull
+ private Iterator currentIter;
+
+ private NamedValue currentValue;
+
+ PartiQLCursorDefault(PartiQLValue delegate) {
+ List wrappedList = new ArrayList<>();
+ wrappedList.add(delegate);
+ Iterator topLevelIterator = unnamed(wrappedList.iterator());
+ this.iteratorStack = new Stack<>();
+ this.iteratorStack.push(topLevelIterator);
+ this.currentIter = topLevelIterator;
+ this.currentValue = null;
+ }
+
+ @Override
+ public void close() {
+ currentIter = Collections.emptyIterator();
+ currentValue = null;
+ iteratorStack.empty();
+ }
+
+ @Override
+ public boolean hasNext() {
+ return currentIter.hasNext();
+ }
+
+ @NotNull
+ @Override
+ public PartiQLValueType next() {
+ currentValue = currentIter.next();
+ return currentValue.value.getType();
+ }
+
+ @Override
+ public void stepIn() {
+ org.partiql.value.PartiQLValue value = currentValue.value;
+ PartiQLValueType type = currentValue.value.getType();
+ Iterator children;
+ switch (type) {
+ case LIST:
+ @SuppressWarnings("unchecked")
+ ListValue list = (ListValue) value;
+ children = unnamed(list.iterator());
+ break;
+ case BAG:
+ @SuppressWarnings("unchecked")
+ BagValue bag = (BagValue) value;
+ children = unnamed(bag.iterator());
+ break;
+ case SEXP:
+ @SuppressWarnings("unchecked")
+ SexpValue sexp = (SexpValue) value;
+ children = unnamed(sexp.iterator());
+ break;
+ case STRUCT:
+ @SuppressWarnings("unchecked")
+ StructValue struct = (StructValue) value;
+ children = named(struct.getEntries());
+ break;
+ default:
+ throw new IllegalStateException();
+ }
+ iteratorStack.push(children);
+ currentValue = null;
+ currentIter = iteratorStack.peek();
+ }
+
+ @Override
+ public void stepOut() {
+ iteratorStack.pop();
+ currentValue = null;
+ currentIter = iteratorStack.peek();
+ }
+
+ @Override
+ public boolean isNullValue() {
+ return currentValue.value.isNull();
+ }
+
+ @Override
+ public boolean isMissingValue() {
+ return currentValue.value.getType() == PartiQLValueType.MISSING;
+ }
+
+ @NotNull
+ @Override
+ public PartiQLValueType getType() {
+ return currentValue.value.getType();
+ }
+
+ @Override
+ public String getFieldName() {
+ return currentValue.name;
+ }
+
+ @NotNull
+ @Override
+ public String getStringValue() {
+ if (currentValue.value.getType() == PartiQLValueType.STRING) {
+ return Objects.requireNonNull(((StringValue) currentValue.value).getValue());
+ }
+ throw new IllegalStateException();
+ }
+
+ @NotNull
+ @Override
+ public String getCharValue() {
+ if (currentValue.value.getType() == PartiQLValueType.CHAR) {
+ return Objects.requireNonNull((Objects.requireNonNull(((CharValue) currentValue.value).getValue()).toString()));
+ }
+ throw new IllegalStateException();
+ }
+
+ @NotNull
+ @Override
+ public String getSymbolValue() {
+ if (currentValue.value.getType() == PartiQLValueType.SYMBOL) {
+ return Objects.requireNonNull(((SymbolValue) currentValue.value).getValue());
+ }
+ throw new IllegalStateException();
+ }
+
+ @Override
+ public boolean getBoolValue() {
+ PartiQLValueType type = currentValue.value.getType();
+ if (type == PartiQLValueType.BOOL) {
+ BoolValue value = (BoolValue) (currentValue.value);
+ return Boolean.TRUE.equals(value.getValue());
+ } else {
+ throw new IllegalStateException();
+ }
+ }
+
+ @Override
+ public byte[] getBinaryValue() {
+ PartiQLValueType type = currentValue.value.getType();
+ if (type == PartiQLValueType.BINARY) {
+ BinaryValue binaryValue = (BinaryValue) (currentValue.value);
+ return Objects.requireNonNull(binaryValue.getValue()).toByteArray();
+ }
+ throw new IllegalStateException();
+ }
+
+ @Override
+ public byte[] getBlobValue() {
+ PartiQLValueType type = currentValue.value.getType();
+ if (type == PartiQLValueType.BLOB) {
+ BlobValue blobValue = (BlobValue) (currentValue.value);
+ return Objects.requireNonNull(blobValue.getValue());
+ }
+ throw new IllegalStateException();
+ }
+
+ @Override
+ public byte[] getClobValue() {
+ PartiQLValueType type = currentValue.value.getType();
+ if (type == PartiQLValueType.CLOB) {
+ ClobValue clobValue = (ClobValue) (currentValue.value);
+ return Objects.requireNonNull(clobValue.getValue());
+ }
+ throw new IllegalStateException();
+ }
+
+ @Override
+ public byte getByteValue() {
+ PartiQLValueType type = currentValue.value.getType();
+ if (type == PartiQLValueType.BYTE) {
+ ByteValue byteValue = (ByteValue) (currentValue.value);
+ return Objects.requireNonNull(byteValue.getValue());
+ }
+ throw new IllegalStateException();
+ }
+
+ @Override
+ @NotNull
+ public Date getDateValue() {
+ PartiQLValueType type = currentValue.value.getType();
+ if (type == PartiQLValueType.DATE) {
+ DateValue value = (DateValue) (currentValue.value);
+ return Objects.requireNonNull(value.getValue());
+ }
+ throw new IllegalStateException();
+ }
+
+ @Override
+ @NotNull
+ public Time getTimeValue() {
+ PartiQLValueType type = currentValue.value.getType();
+ if (type == PartiQLValueType.TIME) {
+ TimeValue value = (TimeValue) (currentValue.value);
+ return Objects.requireNonNull(value.getValue());
+ }
+ throw new IllegalStateException();
+ }
+
+ @Override
+ @NotNull
+ public Timestamp getTimestampValue() {
+ PartiQLValueType type = currentValue.value.getType();
+ if (type == PartiQLValueType.TIMESTAMP) {
+ TimestampValue value = (TimestampValue) (currentValue.value);
+ return Objects.requireNonNull(value.getValue());
+ }
+ throw new IllegalStateException();
+ }
+
+ @Deprecated
+ @Override
+ public long getIntervalValue() {
+ PartiQLValueType type = currentValue.value.getType();
+ if (type == PartiQLValueType.INTERVAL) {
+ IntervalValue value = (IntervalValue) (currentValue.value);
+ return Objects.requireNonNull(value.getValue());
+ }
+ throw new IllegalStateException();
+ }
+
+ @Override
+ public byte getInt8Value() {
+ PartiQLValueType type = currentValue.value.getType();
+ if (type == PartiQLValueType.INT8) {
+ Int8Value value = (Int8Value) (currentValue.value);
+ return Objects.requireNonNull(value.getValue());
+ }
+ throw new IllegalStateException();
+ }
+
+ @Override
+ public short getInt16Value() {
+ PartiQLValueType type = currentValue.value.getType();
+ if (type == PartiQLValueType.INT16) {
+ Int16Value value = (Int16Value) (currentValue.value);
+ return Objects.requireNonNull(value.getValue());
+ }
+ throw new IllegalStateException();
+ }
+
+ @Override
+ public int getInt32Value() {
+ PartiQLValueType type = currentValue.value.getType();
+ if (type == PartiQLValueType.INT32) {
+ Int32Value value = (Int32Value) (currentValue.value);
+ return Objects.requireNonNull(value.getValue());
+ }
+ throw new IllegalStateException();
+ }
+
+ @Override
+ public long getInt64Value() {
+ PartiQLValueType type = currentValue.value.getType();
+ if (type == PartiQLValueType.INT64) {
+ Int64Value value = (Int64Value) (currentValue.value);
+ return Objects.requireNonNull(value.getValue());
+ }
+ throw new IllegalStateException();
+ }
+
+ @Override
+ @NotNull
+ public BigInteger getIntValue() {
+ PartiQLValueType type = currentValue.value.getType();
+ if (type == PartiQLValueType.INT) {
+ IntValue value = (IntValue) (currentValue.value);
+ return Objects.requireNonNull(value.getValue());
+ }
+ throw new IllegalStateException();
+ }
+
+ @Override
+ public float getFloat32Value() {
+ PartiQLValueType type = currentValue.value.getType();
+ if (type == PartiQLValueType.FLOAT32) {
+ Float32Value value = (Float32Value) (currentValue.value);
+ return Objects.requireNonNull(value.getValue());
+ }
+ throw new IllegalStateException();
+ }
+
+ @Override
+ public double getFloat64Value() {
+ PartiQLValueType type = currentValue.value.getType();
+ if (type == PartiQLValueType.FLOAT64) {
+ Float64Value value = (Float64Value) (currentValue.value);
+ return Objects.requireNonNull(value.getValue());
+ }
+ throw new IllegalStateException();
+ }
+
+ @Override
+ @NotNull
+ public BigDecimal getDecimalValue() {
+ PartiQLValueType type = currentValue.value.getType();
+ if (type == PartiQLValueType.DECIMAL) {
+ DecimalValue value = (DecimalValue) (currentValue.value);
+ return Objects.requireNonNull(value.getValue());
+ }
+ throw new IllegalStateException();
+ }
+
+ @NotNull
+ @Override
+ public BigDecimal getDecimalArbitraryValue() {
+ PartiQLValueType type = currentValue.value.getType();
+ if (type == PartiQLValueType.DECIMAL_ARBITRARY) {
+ DecimalValue value = (DecimalValue) (currentValue.value);
+ return Objects.requireNonNull(value.getValue());
+ }
+ throw new IllegalStateException();
+ }
+
+ private NamedIterator named(Iterable> values) {
+ return new NamedIterator(values);
+ }
+
+ private UnnamedIterator unnamed(Iterator values) {
+ return new UnnamedIterator(values);
+ }
+
+ private static class UnnamedIterator implements Iterator {
+
+ @NotNull
+ Iterator values;
+
+ UnnamedIterator(@NotNull Iterator values) {
+ this.values = values;
+ }
+
+ @Override
+ public boolean hasNext() {
+ return values.hasNext();
+ }
+
+ @Override
+ public NamedValue next() {
+ return new NamedValue(values.next());
+ }
+ }
+
+ private static class NamedIterator implements Iterator {
+
+ @NotNull
+ Iterator> values;
+
+ NamedIterator(@NotNull Iterable> values) {
+ this.values = values.iterator();
+ }
+
+ @Override
+ public boolean hasNext() {
+ return values.hasNext();
+ }
+
+ @Override
+ public NamedValue next() {
+ Pair next = values.next();
+ return new NamedValue(next.getFirst(), next.getSecond());
+ }
+ }
+
+ private static class NamedValue {
+ public String name;
+
+ @NotNull
+ public PartiQLValue value;
+
+ private NamedValue(String name, @NotNull PartiQLValue value) {
+ this.name = name;
+ this.value = value;
+ }
+
+ private NamedValue(@NotNull PartiQLValue value) {
+ this.name = null;
+ this.value = value;
+ }
+ }
+}
diff --git a/partiql-types/src/main/java/org/partiql/value/PartiQLValueLoader.java b/partiql-types/src/main/java/org/partiql/value/PartiQLValueLoader.java
new file mode 100644
index 0000000000..ae73c4c72f
--- /dev/null
+++ b/partiql-types/src/main/java/org/partiql/value/PartiQLValueLoader.java
@@ -0,0 +1,30 @@
+package org.partiql.value;
+
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * Provides functions for loading {@link PartiQLCursor} into {@link PartiQLValue} instances.
+ *
+ * @see PartiQLCursor
+ */
+public interface PartiQLValueLoader {
+ /**
+ * Loads un-materialized {@link PartiQLCursor} into a materialized {@link PartiQLValue}. The {@link PartiQLCursor} cursor
+ * must be set before the value that you'd like to load.
+ *
+ * This method will invoke {@link PartiQLCursor#next()}. This method will not throw an error is there is
+ * more data to be processed after the value immediately following the cursor.
+ *
+ * @param data the PartiQL data to load.
+ * @return a materialized, in-memory instance of a {@link PartiQLValue} containing the contents of the {@code data}.
+ */
+ @NotNull
+ PartiQLValue load(@NotNull PartiQLCursor data);
+
+ /**
+ * @return a basic implementation of {@link PartiQLValueLoader}.
+ */
+ static PartiQLValueLoader standard() {
+ return new PartiQLValueLoaderDefault();
+ }
+}
diff --git a/partiql-types/src/main/java/org/partiql/value/PartiQLValueLoaderDefault.java b/partiql-types/src/main/java/org/partiql/value/PartiQLValueLoaderDefault.java
new file mode 100644
index 0000000000..a31c5b4050
--- /dev/null
+++ b/partiql-types/src/main/java/org/partiql/value/PartiQLValueLoaderDefault.java
@@ -0,0 +1,134 @@
+package org.partiql.value;
+
+import kotlin.Pair;
+import kotlin.jvm.functions.Function1;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.ArrayList;
+import java.util.BitSet;
+import java.util.List;
+
+import static org.partiql.value.PartiQL.*;
+
+class PartiQLValueLoaderDefault implements PartiQLValueLoader {
+ @NotNull
+ @Override
+ public PartiQLValue load(@NotNull PartiQLCursor data) {
+ data.next();
+ return _loadSingleValue(data);
+ }
+
+ /**
+ * MUST place the cursor on the data before executing.
+ *
+ * @param data the input PartiQL Data.
+ * @return a materialized PartiQL Value
+ */
+ @NotNull
+ private PartiQLValue _loadSingleValue(@NotNull PartiQLCursor data) {
+ PartiQLValueType type = data.getType();
+ switch (type) {
+ case BOOL:
+ return boolValue(orNull(data, PartiQLCursor::getBoolValue));
+ case INT8:
+ return int8Value(orNull(data, PartiQLCursor::getInt8Value));
+ case INT16:
+ return int16Value(orNull(data, PartiQLCursor::getInt16Value));
+ case INT32:
+ return int32Value(orNull(data, PartiQLCursor::getInt32Value));
+ case INT64:
+ return int64Value(orNull(data, PartiQLCursor::getInt64Value));
+ case INT:
+ return intValue(orNull(data, PartiQLCursor::getIntValue));
+ case LIST:
+ return collectionValue(data, PartiQL::listValue);
+ case BAG:
+ return collectionValue(data, PartiQL::bagValue);
+ case SEXP:
+ return collectionValue(data, PartiQL::sexpValue);
+ case STRUCT:
+ return createStructValue(data);
+ case NULL:
+ return nullValue();
+ case STRING:
+ return stringValue(orNull(data, PartiQLCursor::getStringValue));
+ case SYMBOL:
+ return symbolValue(orNull(data, PartiQLCursor::getSymbolValue));
+ case CHAR:
+ // TODO: The implementation of CHAR VALUE is wrong.
+ String val = orNull(data, PartiQLCursor::getCharValue);
+ if (val == null) {
+ return charValue(null);
+ }
+ return charValue(val.charAt(0));
+ case MISSING:
+ return missingValue();
+ case DECIMAL_ARBITRARY:
+ return decimalValue(orNull(data, PartiQLCursor::getDecimalArbitraryValue));
+ case DECIMAL:
+ return decimalValue(orNull(data, PartiQLCursor::getDecimalValue));
+ case INTERVAL:
+ return intervalValue(orNull(data, PartiQLCursor::getIntervalValue));
+ case TIMESTAMP:
+ return timestampValue(orNull(data, PartiQLCursor::getTimestampValue));
+ case DATE:
+ return dateValue(orNull(data, PartiQLCursor::getDateValue));
+ case CLOB:
+ return clobValue(orNull(data, PartiQLCursor::getClobValue));
+ case BLOB:
+ return blobValue(orNull(data, PartiQLCursor::getBlobValue));
+ case BINARY:
+ byte[] bytes = orNull(data, PartiQLCursor::getBinaryValue);
+ if (bytes == null) {
+ return binaryValue(null);
+ }
+ return binaryValue(BitSet.valueOf(bytes));
+ case BYTE:
+ return byteValue(orNull(data, PartiQLCursor::getByteValue));
+ case TIME:
+ return timeValue(orNull(data, PartiQLCursor::getTimeValue));
+ case FLOAT32:
+ return float32Value(orNull(data, PartiQLCursor::getFloat32Value));
+ case FLOAT64:
+ return float64Value(orNull(data, PartiQLCursor::getFloat64Value));
+ case ANY:
+ default:
+ throw new IllegalStateException("Cannot load data of type: " + type);
+ }
+ }
+
+ private R orNull(PartiQLCursor data, Function1 result) {
+ return data.isNullValue() ? null : result.invoke(data);
+ }
+
+ private PartiQLValue collectionValue(PartiQLCursor data, Function1, PartiQLValue> collectionConstructor) {
+ if (data.isNullValue()) {
+ return collectionConstructor.invoke(null);
+ }
+ data.stepIn();
+ List values = new ArrayList<>();
+ while (data.hasNext()) {
+ data.next();
+ PartiQLValue value = this._loadSingleValue(data);
+ values.add(value);
+ }
+ data.stepOut();
+ return collectionConstructor.invoke(values);
+ }
+
+ private PartiQLValue createStructValue(PartiQLCursor data) {
+ if (data.isNullValue()) {
+ return structValue((Iterable extends Pair>) null);
+ }
+ data.stepIn();
+ List> values = new ArrayList<>();
+ while (data.hasNext()) {
+ data.next();
+ String name = data.getFieldName();
+ PartiQLValue value = this._loadSingleValue(data);
+ values.add(new Pair<>(name, value));
+ }
+ data.stepOut();
+ return structValue(values);
+ }
+}
diff --git a/plugins/partiql-fs/src/main/kotlin/org/partiql/plugins/fs/connector/FsMetadata.kt b/plugins/partiql-fs/src/main/kotlin/org/partiql/plugins/fs/connector/FsMetadata.kt
index 740444cfa3..b30fddb657 100644
--- a/plugins/partiql-fs/src/main/kotlin/org/partiql/plugins/fs/connector/FsMetadata.kt
+++ b/plugins/partiql-fs/src/main/kotlin/org/partiql/plugins/fs/connector/FsMetadata.kt
@@ -4,7 +4,6 @@ import org.partiql.plugins.fs.index.FsIndex
import org.partiql.plugins.fs.index.FsNode
import org.partiql.spi.BindingPath
import org.partiql.spi.connector.ConnectorHandle
-import org.partiql.spi.connector.ConnectorObject
import org.partiql.spi.connector.ConnectorPath
import org.partiql.spi.connector.ConnectorSession
import org.partiql.spi.connector.sql.SqlMetadata
diff --git a/plugins/partiql-fs/src/main/kotlin/org/partiql/plugins/fs/connector/FsObject.kt b/plugins/partiql-fs/src/main/kotlin/org/partiql/plugins/fs/connector/FsObject.kt
index 66e9c288ae..e9473f6d87 100644
--- a/plugins/partiql-fs/src/main/kotlin/org/partiql/plugins/fs/connector/FsObject.kt
+++ b/plugins/partiql-fs/src/main/kotlin/org/partiql/plugins/fs/connector/FsObject.kt
@@ -11,5 +11,4 @@ import org.partiql.types.StaticType
internal class FsObject(private val type: StaticType) : ConnectorObject {
override fun getType(): StaticType = type
-
}
diff --git a/plugins/partiql-fs/src/main/kotlin/org/partiql/plugins/fs/index/FsIndex.kt b/plugins/partiql-fs/src/main/kotlin/org/partiql/plugins/fs/index/FsIndex.kt
index b980114442..922b7a7661 100644
--- a/plugins/partiql-fs/src/main/kotlin/org/partiql/plugins/fs/index/FsIndex.kt
+++ b/plugins/partiql-fs/src/main/kotlin/org/partiql/plugins/fs/index/FsIndex.kt
@@ -15,7 +15,7 @@ internal class FsIndex(private val root: FsNode) {
/**
* Search the FsNode for the type.
*/
- fun search(path: BindingPath): Pair? {
+ fun search(path: BindingPath): Pair? {
val match = mutableListOf()
var curr: FsNode? = root
for (step in path.steps) {
@@ -38,7 +38,7 @@ internal class FsIndex(private val root: FsNode) {
/**
* List all FsNodes in the path.
*/
- fun list(path: BindingPath): List {
+ fun list(path: BindingPath): List {
var curr: FsNode? = root
for (step in path.steps) {
if (curr == null) return emptyList()
diff --git a/plugins/partiql-fs/src/main/kotlin/org/partiql/plugins/fs/index/FsMatch.kt b/plugins/partiql-fs/src/main/kotlin/org/partiql/plugins/fs/index/FsMatch.kt
index bb2d004e8d..40401c766e 100644
--- a/plugins/partiql-fs/src/main/kotlin/org/partiql/plugins/fs/index/FsMatch.kt
+++ b/plugins/partiql-fs/src/main/kotlin/org/partiql/plugins/fs/index/FsMatch.kt
@@ -1,3 +1,3 @@
package org.partiql.plugins.fs.index
-internal typealias KMatch = List
+internal typealias FsMatch = List
diff --git a/test/partiql-tests-runner/src/test/kotlin/org/partiql/runner/executor/EvalExecutor.kt b/test/partiql-tests-runner/src/test/kotlin/org/partiql/runner/executor/EvalExecutor.kt
index fd45707425..86a5aaf05e 100644
--- a/test/partiql-tests-runner/src/test/kotlin/org/partiql/runner/executor/EvalExecutor.kt
+++ b/test/partiql-tests-runner/src/test/kotlin/org/partiql/runner/executor/EvalExecutor.kt
@@ -26,8 +26,10 @@ import org.partiql.spi.BindingPath
import org.partiql.spi.connector.Connector
import org.partiql.spi.connector.ConnectorSession
import org.partiql.types.StaticType
+import org.partiql.value.PartiQLCursor
import org.partiql.value.PartiQLValue
import org.partiql.value.PartiQLValueExperimental
+import org.partiql.value.PartiQLValueLoader
import org.partiql.value.io.PartiQLValueIonReaderBuilder
import org.partiql.value.toIon
@@ -49,20 +51,23 @@ class EvalExecutor(
override fun fromIon(value: IonValue): PartiQLResult {
val partiql = PartiQLValueIonReaderBuilder.standard().build(value.toIonElement()).read()
-
- return PartiQLResult.Value(partiql)
+ val data = PartiQLCursor.of(partiql)
+ return PartiQLResult.Value(data)
}
override fun toIon(value: PartiQLResult): IonValue {
if (value is PartiQLResult.Value) {
- return value.value.toIon().toIonValue(ION)
+ val actualValue = PartiQLValueLoader.standard().load(value.value)
+ return actualValue.toIon().toIonValue(ION)
}
error("PartiQLResult cannot be converted to Ion")
}
override fun compare(actual: PartiQLResult, expect: PartiQLResult): Boolean {
if (actual is PartiQLResult.Value && expect is PartiQLResult.Value) {
- return valueComparison(actual.value, expect.value)
+ val value = PartiQLValueLoader.standard().load(actual.value)
+ val expectedValue = PartiQLValueLoader.standard().load(expect.value)
+ return valueComparison(value, expectedValue)
}
error("Cannot compare different types of PartiQLResult")
}