Skip to content

Commit

Permalink
Add java-client support for primitive array types (#5494)
Browse files Browse the repository at this point in the history
This is a quick pass at adding support for sending primitive typed arrays, String, and Instant[] with the java client. A more thorough (and dense) recursive solution was initially created, but this was a more explicit version created for expediency.

The better solution can come at a later date. Ideally, we'd also add integration tests. In lieu of that, a new example utility that demonstrates all of the types was created.

Fixes #4721
  • Loading branch information
devinrsmith authored and stanbrub committed May 17, 2024
1 parent a550c4f commit d0ddd90
Show file tree
Hide file tree
Showing 4 changed files with 647 additions and 79 deletions.
1 change: 1 addition & 0 deletions java-client/flight-examples/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ application.applicationDistribution.into('bin') {
from(createApplication('do-put-table', 'io.deephaven.client.examples.DoPutTable'))

from(createApplication('add-to-input-table', 'io.deephaven.client.examples.AddToInputTable'))
from(createApplication('add-to-blink-table', 'io.deephaven.client.examples.AddToBlinkTable'))
from(createApplication('kv-input-table', 'io.deephaven.client.examples.KeyValueInputTable'))

from(createApplication('get-table', 'io.deephaven.client.examples.GetDirectTable'))
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
//
// Copyright (c) 2016-2024 Deephaven Data Labs and Patent Pending
//
package io.deephaven.client.examples;

import io.deephaven.client.impl.FlightSession;
import io.deephaven.client.impl.TableHandle;
import io.deephaven.client.impl.TableHandle.TableHandleException;
import io.deephaven.qst.column.header.ColumnHeader;
import io.deephaven.qst.table.BlinkInputTable;
import io.deephaven.qst.table.NewTable;
import io.deephaven.qst.table.TableHeader;
import io.deephaven.qst.table.TableSpec;
import io.deephaven.qst.type.Type;
import picocli.CommandLine;
import picocli.CommandLine.Command;

import java.time.Instant;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

@Command(name = "add-to-blink-table", mixinStandardHelpOptions = true,
description = "Add to Blink Table", version = "0.1.0")
class AddToBlinkTable extends FlightExampleBase {

public <T> void addAndPublish(FlightSession flight, String name, Type<T> type, T... data)
throws TableHandleException, InterruptedException, ExecutionException, TimeoutException {
final ColumnHeader<T> header = ColumnHeader.of(name, type);
final ColumnHeader<T>.Rows rows = header.start(data.length);
for (T datum : data) {
rows.row(datum);
}
final NewTable newTable = rows.newTable();
final BlinkInputTable blinkTable = BlinkInputTable.of(TableHeader.of(header));
final TableSpec tail = blinkTable.tail(32);
final List<TableHandle> handles = flight.session().execute(List.of(blinkTable, tail));
try (
final TableHandle blinkHandle = handles.get(0);
final TableHandle output = handles.get(1)) {
flight.addToInputTable(blinkHandle, newTable, bufferAllocator).get(5, TimeUnit.SECONDS);
flight.session().publish(name + "_Table", output).get(5, TimeUnit.SECONDS);
}
}

@Override
protected void execute(FlightSession flight) throws Exception {
addAndPublish(flight, "Boolean", Type.booleanType(), null, true, false);
addAndPublish(flight, "Byte", Type.byteType(), null, (byte) 42);
addAndPublish(flight, "Char", Type.charType(), null, 'a');
addAndPublish(flight, "Short", Type.shortType(), null, (short) 42);
addAndPublish(flight, "Int", Type.intType(), null, 42);
addAndPublish(flight, "Long", Type.longType(), null, 42L);
addAndPublish(flight, "Float", Type.floatType(), null, 42.24f);
addAndPublish(flight, "Double", Type.doubleType(), null, 42.24);

addAndPublish(flight, "BoxedBoolean", Type.booleanType().boxedType(), null, true, false);
addAndPublish(flight, "BoxedByte", Type.byteType().boxedType(), null, (byte) 42);
addAndPublish(flight, "BoxedChar", Type.charType().boxedType(), null, 'a');
addAndPublish(flight, "BoxedShort", Type.shortType().boxedType(), null, (short) 42);
addAndPublish(flight, "BoxedInt", Type.intType().boxedType(), null, 42);
addAndPublish(flight, "BoxedLong", Type.longType().boxedType(), null, 42L);
addAndPublish(flight, "BoxedFloat", Type.floatType().boxedType(), null, 42.24f);
addAndPublish(flight, "BoxedDouble", Type.doubleType().boxedType(), null, 42.24);

addAndPublish(flight, "String", Type.stringType(), null, "", "Hello");
addAndPublish(flight, "Instant", Type.instantType(), null, Instant.now());

addAndPublish(flight, "BooleanArray", Type.booleanType().arrayType(),
null,
new boolean[] {true},
new boolean[] {true, false});
addAndPublish(flight, "ByteArray", Type.byteType().arrayType(),
null,
new byte[] {},
new byte[] {(byte) 42, (byte) 43});
addAndPublish(flight, "CharArray", Type.charType().arrayType(),
null,
new char[] {},
new char[] {'a', 'b'});
addAndPublish(flight, "ShortArray", Type.shortType().arrayType(),
null,
new short[] {},
new short[] {(short) 42, (short) 43});
addAndPublish(flight, "IntArray", Type.intType().arrayType(),
null,
new int[] {},
new int[] {42, 43});
addAndPublish(flight, "LongArray", Type.longType().arrayType(),
null,
new long[] {},
new long[] {42L, 43L});
addAndPublish(flight, "FloatArray", Type.floatType().arrayType(),
null,
new float[] {},
new float[] {42.42f, 43.43f});
addAndPublish(flight, "DoubleArray", Type.doubleType().arrayType(),
null,
new double[] {},
new double[] {42.42, 43.43});

addAndPublish(flight, "StringArray", Type.stringType().arrayType(),
null,
new String[] {},
new String[] {null, "", "Hello World"});
addAndPublish(flight, "InstantArray", Type.instantType().arrayType(),
null,
new Instant[] {},
new Instant[] {null, Instant.now()});
}

public static void main(String[] args) {
int execute = new CommandLine(new AddToBlinkTable()).execute(args);
System.exit(execute);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,13 @@
import io.deephaven.qst.type.DoubleType;
import io.deephaven.qst.type.FloatType;
import io.deephaven.qst.type.GenericType;
import io.deephaven.qst.type.GenericType.Visitor;
import io.deephaven.qst.type.GenericVectorType;
import io.deephaven.qst.type.InstantType;
import io.deephaven.qst.type.IntType;
import io.deephaven.qst.type.LongType;
import io.deephaven.qst.type.NativeArrayType;
import io.deephaven.qst.type.PrimitiveType;
import io.deephaven.qst.type.PrimitiveVectorType;
import io.deephaven.qst.type.ShortType;
import io.deephaven.qst.type.StringType;
import io.deephaven.qst.type.Type;
Expand All @@ -33,7 +35,8 @@
/**
* Utilities for creating a {@link Field}.
*/
public class FieldAdapter implements Type.Visitor<Field>, PrimitiveType.Visitor<Field> {
public class FieldAdapter implements Type.Visitor<Field>, PrimitiveType.Visitor<Field>, GenericType.Visitor<Field>,
ArrayType.Visitor<Field> {

/**
* Convert a {@code header} into a {@link Field}.
Expand Down Expand Up @@ -98,6 +101,10 @@ private static Field field(String name, FieldType type) {
return new Field(name, type, null);
}

private static UnsupportedOperationException unsupported(Type<?> type) {
return new UnsupportedOperationException(String.format("Field type '%s' is not supported yet", type));
}

private final String name;

private FieldAdapter(String name) {
Expand All @@ -109,40 +116,6 @@ public Field visit(PrimitiveType<?> primitive) {
return primitive.walk((PrimitiveType.Visitor<Field>) this);
}

@Override
public Field visit(GenericType<?> generic) {
return generic.walk(new Visitor<Field>() {
@Override
public Field visit(BoxedType<?> boxedType) {
return FieldAdapter.this.visit(boxedType.primitiveType());
}

@Override
public Field visit(StringType stringType) {
return stringField(name);
}

@Override
public Field visit(InstantType instantType) {
return instantField(name);
}

@Override
public Field visit(ArrayType<?, ?> arrayType) {
if (arrayType.componentType().equals(Type.find(byte.class))) {
return byteVectorField(name);
} else {
throw new UnsupportedOperationException();
}
}

@Override
public Field visit(CustomType<?> customType) {
throw new UnsupportedOperationException();
}
});
}

@Override
public Field visit(ByteType byteType) {
return byteField(name);
Expand Down Expand Up @@ -182,4 +155,136 @@ public Field visit(FloatType floatType) {
public Field visit(DoubleType doubleType) {
return doubleField(name);
}

// ----------------------------------------------------------

@Override
public Field visit(GenericType<?> generic) {
return generic.walk((GenericType.Visitor<Field>) this);
}

@Override
public Field visit(BoxedType<?> boxedType) {
// same field type as primitives
return boxedType.primitiveType().walk((PrimitiveType.Visitor<Field>) this);
}

@Override
public Field visit(StringType stringType) {
return stringField(name);
}

@Override
public Field visit(InstantType instantType) {
return instantField(name);
}

@Override
public Field visit(CustomType<?> customType) {
throw unsupported(customType);
}

// ----------------------------------------------------------

@Override
public Field visit(ArrayType<?, ?> arrayType) {
return arrayType.walk((ArrayType.Visitor<Field>) this);
}

@Override
public Field visit(NativeArrayType<?, ?> nativeArrayType) {
return nativeArrayType.componentType().walk(new NativeArrayVisitor());
}

@Override
public Field visit(PrimitiveVectorType<?, ?> vectorPrimitiveType) {
throw unsupported(vectorPrimitiveType);
}

@Override
public Field visit(GenericVectorType<?, ?> genericVectorType) {
throw unsupported(genericVectorType);
}

// ----------------------------------------------------------

final class NativeArrayVisitor
implements Type.Visitor<Field>, PrimitiveType.Visitor<Field>, GenericType.Visitor<Field> {
@Override
public Field visit(PrimitiveType<?> primitiveType) {
return primitiveType.walk((PrimitiveType.Visitor<Field>) this);
}

@Override
public Field visit(BooleanType booleanType) {
return field(name, MinorType.LIST.getType(), "java.lang.Boolean[]");
}

@Override
public Field visit(ByteType byteType) {
return byteVectorField(name);
}

@Override
public Field visit(CharType charType) {
return field(name, MinorType.LIST.getType(), "char[]");
}

@Override
public Field visit(ShortType shortType) {
return field(name, MinorType.LIST.getType(), "short[]");
}

@Override
public Field visit(IntType intType) {
return field(name, MinorType.LIST.getType(), "int[]");
}

@Override
public Field visit(LongType longType) {
return field(name, MinorType.LIST.getType(), "long[]");
}

@Override
public Field visit(FloatType floatType) {
return field(name, MinorType.LIST.getType(), "float[]");
}

@Override
public Field visit(DoubleType doubleType) {
return field(name, MinorType.LIST.getType(), "double[]");
}

// ----------------------------------------------------------

@Override
public Field visit(GenericType<?> genericType) {
return genericType.walk((GenericType.Visitor<Field>) this);
}

@Override
public Field visit(BoxedType<?> boxedType) {
throw unsupported(boxedType.arrayType());
}

@Override
public Field visit(StringType stringType) {
return field(name, MinorType.LIST.getType(), "java.lang.String[]");
}

@Override
public Field visit(InstantType instantType) {
return field(name, MinorType.LIST.getType(), "java.time.Instant[]");
}

@Override
public Field visit(ArrayType<?, ?> arrayType) {
throw unsupported(arrayType.arrayType());
}

@Override
public Field visit(CustomType<?> customType) {
throw unsupported(customType.arrayType());
}
}
}
Loading

0 comments on commit d0ddd90

Please sign in to comment.