From a0b1ab1eed20e7fcc66f4d91258945460c89e048 Mon Sep 17 00:00:00 2001 From: John Ed Quinn Date: Fri, 24 Jan 2025 13:21:22 -0800 Subject: [PATCH] Adds Datum#row() constructor methods --- .../java/org/partiql/spi/value/Datum.java | 65 ++++++++++ .../java/org/partiql/spi/value/DatumRow.java | 111 ++++++++++++++++++ 2 files changed, 176 insertions(+) create mode 100644 partiql-spi/src/main/java/org/partiql/spi/value/DatumRow.java diff --git a/partiql-spi/src/main/java/org/partiql/spi/value/Datum.java b/partiql-spi/src/main/java/org/partiql/spi/value/Datum.java index 84939eb1b..592efca0b 100644 --- a/partiql-spi/src/main/java/org/partiql/spi/value/Datum.java +++ b/partiql-spi/src/main/java/org/partiql/spi/value/Datum.java @@ -13,9 +13,13 @@ import java.math.RoundingMode; import java.nio.charset.Charset; import java.time.*; +import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.Iterator; +import java.util.List; +import java.util.stream.Collectors; /** * This is a representation of a value in PartiQL's type system. The intention of this modeling is to @@ -691,6 +695,15 @@ static Datum struct() { return struct(Collections.emptyList()); } + /** + * @param values the backing values + * @return a value of type {@link PType#STRUCT} + */ + @NotNull + static Datum struct(@NotNull Field... values) { + return new DatumStruct(Arrays.stream(values).collect(Collectors.toList())); + } + /** * @param values the backing values * @return a value of type {@link PType#STRUCT} @@ -700,6 +713,58 @@ static Datum struct(@NotNull Iterable values) { return new DatumStruct(values); } + /** + * Returns an empty {@link PType#ROW} + * @return a value of type {@link PType#ROW} + */ + @NotNull + static Datum row() { + return new DatumRow(new ArrayList<>(), PType.row()); + } + + /** + * This creates a row. + * @param values the backing values + * @return a value of type {@link PType#ROW} + */ + @NotNull + static Datum row(@NotNull Field... values) { + return new DatumRow(Arrays.stream(values).collect(Collectors.toList())); + } + + /** + * This creates a row. Use this if you'd like to save on the computational cost of computing the final type. + * @param typeFields the backing type fields + * @param values the backing values + * @return a value of type {@link PType#ROW} + */ + @NotNull + static Datum row(List typeFields, @NotNull Field... values) { + return row(typeFields, Arrays.stream(values).collect(Collectors.toList())); + } + + /** + * Creates a row with the given values. + * @param values the backing values + * @return a value of type {@link PType#ROW} + */ + @NotNull + static Datum row(@NotNull List values) { + return new DatumRow(values); + } + + /** + * Creates a row with the given values. Use this if you'd like to save on the computational cost of computing the final type. + * @param typeFields the backing type fields + * @param values the backing values + * @return a value of type {@link PType#ROW} + */ + @NotNull + static Datum row(@NotNull List typeFields, @NotNull List values) { + PType type = PType.row(typeFields); + return new DatumRow(values, type); + } + /** * @param value the backing Ion * @return a value of type {@link PType#VARIANT} diff --git a/partiql-spi/src/main/java/org/partiql/spi/value/DatumRow.java b/partiql-spi/src/main/java/org/partiql/spi/value/DatumRow.java new file mode 100644 index 000000000..ab17ce1da --- /dev/null +++ b/partiql-spi/src/main/java/org/partiql/spi/value/DatumRow.java @@ -0,0 +1,111 @@ +package org.partiql.spi.value; + +import org.jetbrains.annotations.NotNull; +import org.partiql.spi.types.PType; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +/** + * This shall always be package-private (internal). + */ +class DatumRow implements Datum { + + @NotNull + private final HashMap> _delegate; + + @NotNull + private final HashMap> _delegateNormalized; + + private final PType _type; + + DatumRow(@NotNull Iterable fields, @NotNull PType type) { + _type = type; + _delegate = new HashMap<>(); + _delegateNormalized = new HashMap<>(); + for (Field field : fields) { + String key = field.getName(); + String keyNormalized = field.getName().toLowerCase(); + Datum value = field.getValue(); + addFieldToStruct(_delegate, key, value); + addFieldToStruct(_delegateNormalized, keyNormalized, value); + } + } + + DatumRow(@NotNull Iterable fields) { + this(fields, getTypeFromFields(fields)); + } + + private static PType getTypeFromFields(@NotNull Iterable fields) { + List fieldTypes = new ArrayList<>(); + fields.forEach((f) -> { + PType fType = f.getValue().getType(); + org.partiql.spi.types.Field typeField = org.partiql.spi.types.Field.of(f.getName(), fType); + fieldTypes.add(typeField); + }); + return PType.row(fieldTypes); + } + + private void addFieldToStruct(Map> struct, String key, Datum value) { + List values = struct.getOrDefault(key, new ArrayList<>()); + values.add(value); + struct.put(key, values); + } + + @Override + @NotNull + public Iterator getFields() { + return _delegate.entrySet().stream().flatMap( + entry -> entry.getValue().stream().map( + value -> Field.of(entry.getKey(), value) + ) + ).iterator(); + } + + @Override + public Datum get(@NotNull String name) { + List values = _delegate.get(name); + if (values == null) { + return null; + } + if (values.isEmpty()) { + return null; + } + return values.get(0); + } + + @Override + public Datum getInsensitive(@NotNull String name) { + List values = _delegateNormalized.get(name.toLowerCase()); + if (values == null) { + return null; + } + if (values.isEmpty()) { + return null; + } + return values.get(0); + } + + @NotNull + @Override + public PType getType() { + return _type; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("struct::{ "); + for (Map.Entry> entry : _delegate.entrySet()) { + sb.append(entry.getKey()); + sb.append(": "); + sb.append(entry.getValue().toString()); + sb.append(", "); + } + sb.append(" }"); + return sb.toString(); + } +}