Skip to content

Commit 5732cff

Browse files
committed
Java enum Row/Tuple support for PostgreSQL - fixes #612
1 parent c07160d commit 5732cff

File tree

6 files changed

+347
-11
lines changed

6 files changed

+347
-11
lines changed

vertx-pg-client/src/main/asciidoc/index.adoc

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,33 @@ Text search is handling using java `String`
348348
{@link examples.PgClientExamples#tsQuery02Example}
349349
----
350350

351+
== Handling enumerated types
352+
353+
PostgreSQL https://www.postgresql.org/docs/9.1/datatype-enum.html[enumerated types] are mapped to java strings.
354+
355+
[source,$lang]
356+
----
357+
{@link examples.PgClientExamples#enumeratedType01Example}
358+
----
359+
360+
== Using Java enum types
361+
362+
You can map Java https://docs.oracle.com/javase/tutorial/java/javaOO/enum.html[enum types] to these column
363+
types:
364+
365+
- Strings (VARCHAR, TEXT)
366+
- PosgreSQL enumerated types
367+
- Numbers (INT2, INT4, INT8)
368+
369+
[source,$lang]
370+
----
371+
{@link examples.PgClientExamples#enumType01Example}
372+
----
373+
374+
String and PostgreSQL enumerated types are matched with the Java enum's name returned by the `name()` method.
375+
376+
Numbers types are matched with the Java enum's ordinal returned by the `ordinal()` method.
377+
351378
== Collector queries
352379

353380
You can use Java collectors with the query API:

vertx-pg-client/src/main/java/examples/PgClientExamples.java

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -537,6 +537,36 @@ public void tsQuery02Example(SqlClient client) {
537537
});
538538
}
539539

540+
public void enumeratedType01Example(SqlClient client) {
541+
client
542+
.preparedQuery("INSERT INTO colors VALUES ($2)")
543+
.execute(Tuple.of("red"), res -> {
544+
// ...
545+
});
546+
}
547+
548+
enum Color {
549+
red
550+
}
551+
552+
public void enumType01Example(SqlClient client) {
553+
client
554+
.preparedQuery("INSERT INTO colors VALUES ($1)")
555+
.execute(Tuple.of(Color.red))
556+
.flatMap(res ->
557+
client
558+
.preparedQuery("SELECT color FROM colors")
559+
.execute()
560+
).onComplete(res -> {
561+
if (res.succeeded()) {
562+
RowSet<Row> rows = res.result();
563+
for (Row row : rows) {
564+
System.out.println(row.get(Color.class, "color"));
565+
}
566+
}
567+
});
568+
}
569+
540570
public void collector01Example(SqlClient client) {
541571

542572
// Create a collector projecting a row set to a map

vertx-pg-client/src/main/java/io/vertx/pgclient/impl/RowImpl.java

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import io.vertx.sqlclient.impl.RowDesc;
3434
import io.vertx.core.buffer.Buffer;
3535

36+
import java.lang.reflect.Array;
3637
import java.time.*;
3738
import java.util.List;
3839
import java.util.UUID;
@@ -121,6 +122,8 @@ public <T> T get(Class<T> type, int pos) {
121122
return type.cast(getJson(pos));
122123
} else if (type == Object.class) {
123124
return type.cast(getValue(pos));
125+
} else if (type.isEnum()) {
126+
return type.cast(getEnum(type, pos));
124127
}
125128
throw new UnsupportedOperationException("Unsupported type " + type.getName());
126129
}
@@ -179,6 +182,8 @@ public <T> T[] getValues(Class<T> type, int pos) {
179182
return (T[]) getBoxArray(pos);
180183
} else if (type == Object.class) {
181184
return (T[]) getJsonArray_(pos);
185+
} else if (type.isEnum()) {
186+
return (T[]) getEnumArray(type, pos);
182187
}
183188
throw new UnsupportedOperationException("Unsupported type " + type.getName());
184189
}
@@ -278,6 +283,52 @@ public Character[] getCharArray(String name) {
278283
return pos == -1 ? null : getCharArray(pos);
279284
}
280285

286+
public Object getEnum(Class enumType, int pos) {
287+
Object val = getValue(pos);
288+
if (val instanceof String) {
289+
return Enum.valueOf(enumType, (String) val);
290+
} else if (val instanceof Number) {
291+
int ordinal = ((Number) val).intValue();
292+
if (ordinal >= 0) {
293+
Object[] constants = enumType.getEnumConstants();
294+
if (ordinal < constants.length) {
295+
return constants[ordinal];
296+
}
297+
}
298+
}
299+
return null;
300+
}
301+
302+
public Object[] getEnumArray(Class enumType, int pos) {
303+
Object val = getValue(pos);
304+
if (val instanceof String[]) {
305+
String[] array = (String[]) val;
306+
Object[] ret = (Object[]) Array.newInstance(enumType, array.length);
307+
for (int i = 0;i < array.length;i++) {
308+
String string = array[i];
309+
if (string != null) {
310+
ret[i] = Enum.valueOf(enumType, string);
311+
}
312+
}
313+
return ret;
314+
} else if (val instanceof Number[]) {
315+
Number[] array = (Number[]) val;
316+
Object[] ret = (Object[]) Array.newInstance(enumType, array.length);
317+
Object[] constants = enumType.getEnumConstants();
318+
for (int i = 0;i < array.length;i++) {
319+
Number number = array[i];
320+
int ordinal = number.intValue();
321+
if (ordinal >= 0) {
322+
if (ordinal < constants.length) {
323+
ret[i] = constants[ordinal];
324+
}
325+
}
326+
}
327+
return ret;
328+
}
329+
return null;
330+
}
331+
281332
public Character getChar(int pos) {
282333
Object val = getValue(pos);
283334
if (val instanceof Character) {

vertx-pg-client/src/main/java/io/vertx/pgclient/impl/codec/DataTypeCodec.java

Lines changed: 56 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -606,16 +606,21 @@ public static Object decodeText(DataType id, int index, int len, ByteBuf buff) {
606606

607607

608608
public static Object prepare(DataType type, Object value) {
609+
if (value instanceof Enum) {
610+
value = prepare(type, (Enum<?>) value);
611+
} else if (value instanceof Enum[]) {
612+
value = prepare(type, (Enum<?>[]) value);
613+
}
609614
switch (type) {
610615
case JSON:
611616
case JSONB:
612617
if (value == null ||
613-
value == Tuple.JSON_NULL ||
614-
value instanceof String ||
615-
value instanceof Boolean ||
616-
value instanceof Number ||
617-
value instanceof JsonObject ||
618-
value instanceof JsonArray) {
618+
value == Tuple.JSON_NULL ||
619+
value instanceof String ||
620+
value instanceof Boolean ||
621+
value instanceof Number ||
622+
value instanceof JsonObject ||
623+
value instanceof JsonArray) {
619624
return value;
620625
} else {
621626
return REFUSED_SENTINEL;
@@ -625,13 +630,53 @@ public static Object prepare(DataType type, Object value) {
625630
return Arrays.stream((String[]) value).collect(Collectors.joining(",", "{", "}"));
626631
} else if (value == null || value instanceof String) {
627632
return value;
628-
} else {
629-
return REFUSED_SENTINEL;
630633
}
631-
default:
632-
Class<?> javaType = type.decodingType;
633-
return value == null || javaType.isInstance(value) ? value : REFUSED_SENTINEL;
634+
break;
635+
}
636+
Class<?> javaType = type.decodingType;
637+
if (value == null || javaType.isInstance(value)) {
638+
return value;
639+
}
640+
return REFUSED_SENTINEL;
641+
}
642+
643+
private static Object prepare(DataType type, Enum<?> value) {
644+
switch (type) {
645+
case INT2:
646+
case INT4:
647+
case INT8:
648+
return value.ordinal();
649+
case UNKNOWN:
650+
case VARCHAR:
651+
case TEXT:
652+
return value.name();
634653
}
654+
return value;
655+
}
656+
657+
private static Object prepare(DataType type, Enum<?>[] value) {
658+
int len = value.length;
659+
switch (type) {
660+
case INT2_ARRAY:
661+
case INT4_ARRAY:
662+
case INT8_ARRAY: {
663+
Number[] ret = new Number[len];
664+
for (int i = 0; i < len; i++) {
665+
ret[i] = value[i].ordinal();
666+
}
667+
return ret;
668+
}
669+
case UNKNOWN:
670+
case VARCHAR_ARRAY:
671+
case TEXT_ARRAY: {
672+
String[] ret = new String[len];
673+
for (int i = 0; i < len; i++) {
674+
ret[i] = value[i].name();
675+
}
676+
return ret;
677+
}
678+
}
679+
return value;
635680
}
636681

637682
private static Object defaultDecodeText(int index, int len, ByteBuf buff) {

0 commit comments

Comments
 (0)