From 37a791e6da32fa3ee4b683b36929494c7f504375 Mon Sep 17 00:00:00 2001 From: Roman Meier Date: Tue, 21 Dec 2021 22:32:18 +0100 Subject: [PATCH 1/4] add support for xmltype in postgres Signed-off-by: Meier Roman --- .../java/io/vertx/pgclient/data/PgSQLXML.java | 36 ++++++ .../java/io/vertx/pgclient/impl/RowImpl.java | 21 ++-- .../vertx/pgclient/impl/codec/DataType.java | 18 +-- .../pgclient/impl/codec/DataTypeCodec.java | 68 +++++++---- .../pgclient/data/PgSQLXMLCodecTest.java | 115 ++++++++++++++++++ 5 files changed, 217 insertions(+), 41 deletions(-) create mode 100644 vertx-pg-client/src/main/java/io/vertx/pgclient/data/PgSQLXML.java create mode 100644 vertx-pg-client/src/test/java/io/vertx/pgclient/data/PgSQLXMLCodecTest.java diff --git a/vertx-pg-client/src/main/java/io/vertx/pgclient/data/PgSQLXML.java b/vertx-pg-client/src/main/java/io/vertx/pgclient/data/PgSQLXML.java new file mode 100644 index 000000000..1c1ac53d8 --- /dev/null +++ b/vertx-pg-client/src/main/java/io/vertx/pgclient/data/PgSQLXML.java @@ -0,0 +1,36 @@ +package io.vertx.pgclient.data; + +import io.vertx.codegen.annotations.Nullable; + +import java.util.Objects; + +public class PgSQLXML { + + @Nullable final String xmlData; + + public PgSQLXML(String xmlData) { + this.xmlData = xmlData; + } + + public String getXmlData() { + return xmlData; + } + + @Override + public String toString() { + return xmlData; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + PgSQLXML pgSQLXML = (PgSQLXML) o; + return Objects.equals(xmlData, pgSQLXML.xmlData); + } + + @Override + public int hashCode() { + return Objects.hash(xmlData); + } +} diff --git a/vertx-pg-client/src/main/java/io/vertx/pgclient/impl/RowImpl.java b/vertx-pg-client/src/main/java/io/vertx/pgclient/impl/RowImpl.java index 716919314..e9b8467e4 100644 --- a/vertx-pg-client/src/main/java/io/vertx/pgclient/impl/RowImpl.java +++ b/vertx-pg-client/src/main/java/io/vertx/pgclient/impl/RowImpl.java @@ -19,16 +19,9 @@ import io.vertx.core.json.JsonArray; import io.vertx.core.json.JsonObject; -import io.vertx.pgclient.data.Box; -import io.vertx.pgclient.data.Circle; -import io.vertx.pgclient.data.Line; -import io.vertx.pgclient.data.LineSegment; +import io.vertx.pgclient.data.*; import io.vertx.sqlclient.Row; import io.vertx.sqlclient.data.Numeric; -import io.vertx.pgclient.data.Path; -import io.vertx.pgclient.data.Polygon; -import io.vertx.pgclient.data.Interval; -import io.vertx.pgclient.data.Point; import io.vertx.sqlclient.impl.ArrayTuple; import io.vertx.sqlclient.impl.RowDesc; import io.vertx.core.buffer.Buffer; @@ -118,6 +111,8 @@ public T get(Class type, int position) { return type.cast(getArrayOfIntervals(position)); } else if (componentType == Box.class) { return type.cast(getArrayOfBoxs(position)); + } else if (componentType == PgSQLXML.class) { + return type.cast(getArrayOfPgXMLSQLs(position)); } else if (componentType == Object.class) { return type.cast(getJsonArray_(position)); } else if (componentType.isEnum()) { @@ -174,6 +169,8 @@ public T get(Class type, int position) { return type.cast(getJson(position)); } else if (type == JsonArray.class) { return type.cast(getJson(position)); + } else if (type == PgSQLXML.class) { + return type.cast(getPgSQLXML(position)); } else if (type == Object.class) { return type.cast(getValue(position)); } else if (type.isEnum()) { @@ -215,6 +212,10 @@ private Interval getInterval(int pos) { return (Interval) getValue(pos); } + private PgSQLXML getPgSQLXML(int pos) { + return (PgSQLXML) getValue(pos); + } + private Object getEnum(Class enumType, int pos) { Object val = getValue(pos); if (val instanceof String) { @@ -270,6 +271,10 @@ private Interval[] getArrayOfIntervals(int pos) { return (Interval[]) getValue(pos); } + private PgSQLXML[] getArrayOfPgXMLSQLs(int pos) { + return (PgSQLXML[]) getValue(pos); + } + private Object[] getArrayOfEnums(Class enumType, int pos) { Object val = getValue(pos); if (val instanceof String[]) { diff --git a/vertx-pg-client/src/main/java/io/vertx/pgclient/impl/codec/DataType.java b/vertx-pg-client/src/main/java/io/vertx/pgclient/impl/codec/DataType.java index 0ac278820..b1baacb80 100644 --- a/vertx-pg-client/src/main/java/io/vertx/pgclient/impl/codec/DataType.java +++ b/vertx-pg-client/src/main/java/io/vertx/pgclient/impl/codec/DataType.java @@ -22,18 +22,9 @@ import io.vertx.core.impl.logging.LoggerFactory; import io.vertx.core.json.JsonArray; import io.vertx.core.json.JsonObject; -import io.vertx.pgclient.data.Box; -import io.vertx.pgclient.data.Circle; -import io.vertx.pgclient.data.Inet; -import io.vertx.pgclient.data.Line; -import io.vertx.pgclient.data.LineSegment; -import io.vertx.pgclient.data.Money; +import io.vertx.pgclient.data.*; import io.vertx.sqlclient.Tuple; import io.vertx.sqlclient.data.Numeric; -import io.vertx.pgclient.data.Interval; -import io.vertx.pgclient.data.Path; -import io.vertx.pgclient.data.Point; -import io.vertx.pgclient.data.Polygon; import io.vertx.core.buffer.Buffer; import java.sql.JDBCType; @@ -105,8 +96,8 @@ public enum DataType { JSON_ARRAY(199, true, Object[].class, JDBCType.OTHER, Tuple::getArrayOfJsons), JSONB(3802, true, Object.class, JDBCType.OTHER, Tuple::getJson), JSONB_ARRAY(3807, true, Object[].class, JDBCType.OTHER, Tuple::getArrayOfJsons), - XML(142, true, Object.class, JDBCType.OTHER), - XML_ARRAY(143, true, Object[].class, JDBCType.OTHER), + XML(142, true, Object.class, JDBCType.SQLXML), + XML_ARRAY(143, true, Object[].class, JDBCType.SQLXML), POINT(600, true, Point.class, JDBCType.OTHER), POINT_ARRAY(1017, true, Point[].class, JDBCType.OTHER), LINE(628, true, Line.class, JDBCType.OTHER), @@ -229,5 +220,8 @@ static DataType lookup(Class type) { encodingTypeToDataType.put(Polygon[].class, POLYGON_ARRAY); encodingTypeToDataType.put(Circle.class, CIRCLE); encodingTypeToDataType.put(Circle[].class, CIRCLE_ARRAY); + + encodingTypeToDataType.put(PgSQLXML.class, XML); + encodingTypeToDataType.put(PgSQLXML[].class, XML_ARRAY); } } diff --git a/vertx-pg-client/src/main/java/io/vertx/pgclient/impl/codec/DataTypeCodec.java b/vertx-pg-client/src/main/java/io/vertx/pgclient/impl/codec/DataTypeCodec.java index 2e0562eed..b8205695a 100644 --- a/vertx-pg-client/src/main/java/io/vertx/pgclient/impl/codec/DataTypeCodec.java +++ b/vertx-pg-client/src/main/java/io/vertx/pgclient/impl/codec/DataTypeCodec.java @@ -20,16 +20,16 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.handler.codec.DecoderException; +import io.vertx.core.buffer.Buffer; import io.vertx.core.impl.logging.Logger; import io.vertx.core.impl.logging.LoggerFactory; import io.vertx.core.json.Json; -import io.vertx.sqlclient.Tuple; -import io.vertx.sqlclient.data.Numeric; -import io.vertx.pgclient.data.*; -import io.vertx.pgclient.impl.util.UTF8StringEndDetector; -import io.vertx.core.buffer.Buffer; import io.vertx.core.json.JsonArray; import io.vertx.core.json.JsonObject; +import io.vertx.pgclient.data.*; +import io.vertx.pgclient.impl.util.UTF8StringEndDetector; +import io.vertx.sqlclient.Tuple; +import io.vertx.sqlclient.data.Numeric; import io.vertx.sqlclient.impl.codec.CommonCodec; import java.net.Inet4Address; @@ -50,7 +50,7 @@ import static java.time.format.DateTimeFormatter.ISO_LOCAL_DATE; import static java.time.format.DateTimeFormatter.ISO_LOCAL_TIME; -import static java.util.concurrent.TimeUnit.*; +import static java.util.concurrent.TimeUnit.NANOSECONDS; /** * @author Julien Viet @@ -91,6 +91,8 @@ public class DataTypeCodec { private static final OffsetDateTime OFFSET_DATE_TIME_EPOCH = LocalDateTime.of(2000, 1, 1, 0, 0, 0).atOffset(ZoneOffset.UTC); private static final Inet[] empty_inet_array = new Inet[0]; private static final Money[] empty_money_array = new Money[0]; + private static final PgSQLXML[] empty_pgsqlxml_array = new PgSQLXML[0]; + // Sentinel used when an object is refused by the data type public static final Object REFUSED_SENTINEL = new Object(); @@ -107,7 +109,7 @@ public class DataTypeCodec { private static final IntFunction OFFSETTIME_ARRAY_FACTORY = size -> size == 0 ? empty_offset_time_array : new OffsetTime[size]; private static final IntFunction LOCALDATETIME_ARRAY_FACTORY = size -> size == 0 ? empty_local_date_time_array : new LocalDateTime[size]; private static final IntFunction OFFSETDATETIME_ARRAY_FACTORY = size -> size == 0 ? empty_offset_date_time_array : new OffsetDateTime[size]; - private static final IntFunction BUFFER_ARRAY_FACTORY =size -> size == 0 ? empty_buffer_array : new Buffer[size]; + private static final IntFunction BUFFER_ARRAY_FACTORY = size -> size == 0 ? empty_buffer_array : new Buffer[size]; private static final IntFunction UUID_ARRAY_FACTORY = size -> size == 0 ? empty_uuid_array : new UUID[size]; private static final IntFunction JSON_ARRAY_FACTORY = size -> size == 0 ? empty_json_array : new Object[size]; private static final IntFunction NUMERIC_ARRAY_FACTORY = size -> size == 0 ? empty_numeric_array : new Numeric[size]; @@ -121,20 +123,18 @@ public class DataTypeCodec { private static final IntFunction INTERVAL_ARRAY_FACTORY = size -> size == 0 ? empty_interval_array : new Interval[size]; private static final IntFunction INET_ARRAY_FACTORY = size -> size == 0 ? empty_inet_array : new Inet[size]; private static final IntFunction MONEY_ARRAY_FACTORY = size -> size == 0 ? empty_money_array : new Money[size]; - + private static final IntFunction PGSQLXML_ARRAY_FACTORY = size -> size == 0 ? empty_pgsqlxml_array : new PgSQLXML[size]; private static final java.time.format.DateTimeFormatter TIMETZ_FORMAT = new DateTimeFormatterBuilder() .parseCaseInsensitive() .append(ISO_LOCAL_TIME) .appendOffset("+HH:mm", "00:00") .toFormatter(); - private static final java.time.format.DateTimeFormatter TIMESTAMP_FORMAT = new DateTimeFormatterBuilder() .parseCaseInsensitive() .append(ISO_LOCAL_DATE) .appendLiteral(' ') .append(ISO_LOCAL_TIME) .toFormatter(); - private static final java.time.format.DateTimeFormatter TIMESTAMPTZ_FORMAT = new DateTimeFormatterBuilder() .append(TIMESTAMP_FORMAT) .appendOffset("+HH:mm", "00:00") @@ -360,6 +360,12 @@ public static void encodeBinary(DataType id, Object value, ByteBuf buff) { case MONEY_ARRAY: binaryEncodeArray((Money[]) value, DataType.MONEY, buff); break; + case XML: + binaryEncodePgXMLSQL((PgSQLXML) value, buff); + break; + case XML_ARRAY: + binaryEncodeArray((PgSQLXML[]) value, DataType.XML, buff); + break; default: logger.debug("Data type " + id + " does not support binary encoding"); defaultEncodeBinary(value, buff); @@ -497,6 +503,10 @@ public static Object decodeBinary(DataType id, int index, int len, ByteBuf buff) return binaryDecodeMoney(index, len, buff); case MONEY_ARRAY: return binaryDecodeArray(MONEY_ARRAY_FACTORY, DataType.MONEY, index, len, buff); + case XML: + return binaryDecodePgXMLSQL(index, len, buff); + case XML_ARRAY: + return binaryDecodeArray(PGSQLXML_ARRAY_FACTORY, DataType.XML, index, len, buff); default: logger.debug("Data type " + id + " does not support binary decoding"); return defaultDecodeBinary(index, len, buff); @@ -637,6 +647,10 @@ public static Object decodeText(DataType id, int index, int len, ByteBuf buff) { return textDecodeMoney(index, len, buff); case MONEY_ARRAY: return textDecodeArray(MONEY_ARRAY_FACTORY, DataType.MONEY, index, len, buff); + case XML: + return textDecodePgSQLXML(index, len, buff); + case XML_ARRAY: + return textDecodeArray(PGSQLXML_ARRAY_FACTORY, DataType.XML, index, len, buff); default: return defaultDecodeText(index, len, buff); } @@ -669,7 +683,7 @@ private static Boolean binaryDecodeBOOL(int index, int len, ByteBuf buff) { } private static Boolean textDecodeBOOL(int index, int len, ByteBuf buff) { - if(buff.getByte(index) == 't') { + if (buff.getByte(index) == 't') { return Boolean.TRUE; } else { return Boolean.FALSE; @@ -770,7 +784,7 @@ private static Line textDecodeLine(int index, int len, ByteBuf buff) { private static LineSegment textDecodeLseg(int index, int len, ByteBuf buff) { // Lseg representation: [p1,p2] - int idxOfPointsSeparator = buff.indexOf(index, index+len, (byte) ')') + 1; + int idxOfPointsSeparator = buff.indexOf(index, index + len, (byte) ')') + 1; int lenOfP1 = idxOfPointsSeparator - index - 1; Point p1 = textDecodePOINT(index + 1, lenOfP1, buff); Point p2 = textDecodePOINT(idxOfPointsSeparator + 1, len - lenOfP1 - 3, buff); @@ -779,7 +793,7 @@ private static LineSegment textDecodeLseg(int index, int len, ByteBuf buff) { private static Box textDecodeBox(int index, int len, ByteBuf buff) { // Box representation: p1,p2 - int idxOfPointsSeparator = buff.indexOf(index, index+len, (byte) ')') + 1; + int idxOfPointsSeparator = buff.indexOf(index, index + len, (byte) ')') + 1; int lenOfUpperRightCornerPoint = idxOfPointsSeparator - index; Point upperRightCorner = textDecodePOINT(index, lenOfUpperRightCornerPoint, buff); Point lowerLeftCorner = textDecodePOINT(idxOfPointsSeparator + 1, len - lenOfUpperRightCornerPoint - 1, buff); @@ -905,7 +919,7 @@ private static Interval textDecodeINTERVAL(int index, int len, ByteBuf buff) { : Integer.parseInt(timeChunk.substring(sidx)); } else { // seconds with microseconds - seconds = isNeg ? -Integer.parseInt(timeChunk.substring(sidx).substring(0, m)) + seconds = isNeg ? -Integer.parseInt(timeChunk.substring(sidx).substring(0, m)) : Integer.parseInt(timeChunk.substring(sidx).substring(0, m)); microseconds = isNeg ? -Integer.parseInt(timeChunk.substring(sidx).substring(m + 1)) : Integer.parseInt(timeChunk.substring(sidx).substring(m + 1)); @@ -990,7 +1004,6 @@ private static String textDecodeNAME(int index, int len, ByteBuf buff) { return buff.getCharSequence(index, len, StandardCharsets.UTF_8).toString(); } - private static void binaryEncodeNAME(String value, ByteBuf buff) { String s = String.valueOf(value); buff.writeCharSequence(s, StandardCharsets.UTF_8); @@ -1466,7 +1479,7 @@ private static void binaryEncodeMoney(Money money, ByteBuf buff) { private static Money binaryDecodeMoney(int index, int len, ByteBuf buff) { long value = binaryDecodeINT8(index, len, buff); - return new Money(value / 100, Math.abs(((int)value % 100))); + return new Money(value / 100, Math.abs(((int) value % 100))); } private static String binaryDecodeTsQuery(int index, int len, ByteBuf buff) { @@ -1477,6 +1490,19 @@ private static void binaryEncodeTsQuery(String value, ByteBuf buff) { buff.writeCharSequence(String.valueOf(value), StandardCharsets.UTF_8); } + private static PgSQLXML binaryDecodePgXMLSQL(int index, int len, ByteBuf buff) { + return new PgSQLXML(buff.getCharSequence(index, len, StandardCharsets.UTF_8).toString()); + } + + private static void binaryEncodePgXMLSQL(PgSQLXML value, ByteBuf buff) { + buff.writeCharSequence(value.toString(), StandardCharsets.UTF_8); + } + + private static PgSQLXML textDecodePgSQLXML(int index, int len, ByteBuf buff) { + String s = textDecodeVARCHAR(index, len, buff); + return new PgSQLXML(s); + } + private static String textDecodeTsVector(int index, int len, ByteBuf buff) { return buff.getCharSequence(index, len, StandardCharsets.UTF_8).toString(); } @@ -1535,7 +1561,7 @@ private static Money textDecodeMoney(int index, int len, ByteBuf buff) { * Decode the specified {@code buff} formatted as an hex string starting at the buffer readable index * with the specified {@code length} to a {@link Buffer}. * - * @param len the hex string length + * @param len the hex string length * @param buff the byte buff to read from * @return the decoded value as a Buffer */ @@ -1551,7 +1577,7 @@ private static Buffer decodeHexStringToBytes(int index, int len, ByteBuf buff) { } private static byte decodeHexChar(byte ch) { - return (byte)(((ch & 0x1F) + ((ch >> 6) * 0x19) - 0x10) & 0x0F); + return (byte) (((ch & 0x1F) + ((ch >> 6) * 0x19) - 0x10) & 0x0F); } private static boolean isHexFormat(int index, int len, ByteBuf buff) { @@ -1619,7 +1645,7 @@ private static T[] binaryDecodeArray(IntFunction supplier, DataType typ return array; } - private static void binaryEncodeArray(T[] values, DataType type, ByteBuf buff){ + private static void binaryEncodeArray(T[] values, DataType type, ByteBuf buff) { int startIndex = buff.writerIndex(); buff.writeInt(1); // ndim buff.writeInt(0); // dataoffset @@ -1681,7 +1707,7 @@ private static T textDecodeArrayElement(DataType type, int index, int len, B // Some escaping - improve that later... String s = buff.toString(index + 1, len - 2, StandardCharsets.UTF_8); StringBuilder sb = new StringBuilder(); - for (int i = 0;i < s.length();i++) { + for (int i = 0; i < s.length(); i++) { char c = s.charAt(i); if (c == '\\') { c = s.charAt(++i); @@ -1696,7 +1722,7 @@ private static T textDecodeArrayElement(DataType type, int index, int len, B } } - private static void textEncodeArray(T[] values, DataType type, ByteBuf buff){ + private static void textEncodeArray(T[] values, DataType type, ByteBuf buff) { buff.writeByte('{'); int len = values.length; for (int i = 0; i < len; i++) { diff --git a/vertx-pg-client/src/test/java/io/vertx/pgclient/data/PgSQLXMLCodecTest.java b/vertx-pg-client/src/test/java/io/vertx/pgclient/data/PgSQLXMLCodecTest.java new file mode 100644 index 000000000..4575e56ec --- /dev/null +++ b/vertx-pg-client/src/test/java/io/vertx/pgclient/data/PgSQLXMLCodecTest.java @@ -0,0 +1,115 @@ +package io.vertx.pgclient.data; + +import io.vertx.ext.unit.TestContext; +import io.vertx.pgclient.PgConnection; +import io.vertx.pgclient.PgException; +import io.vertx.sqlclient.*; +import org.junit.Test; + +import java.util.function.BiFunction; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; + +public class PgSQLXMLCodecTest extends DataTypeTestBase { + + @Test + public void testBinaryEncodePgSQLXMLAsVarcharOrXML(TestContext ctx) { + PgSQLXML asVarchar = new PgSQLXML(""); + PgSQLXML asXML = new PgSQLXML(""); + + PgConnection.connect(vertx, options, ctx.asyncAssertSuccess(conn -> { + conn.preparedQuery("SELECT ($1::xml)::VARCHAR, ($2::xml)").execute(Tuple.of(asVarchar, asXML), + ctx.asyncAssertSuccess(rows -> { + ctx.assertEquals(1, rows.size()); + Row row = rows.iterator().next(); + String v1 = row.getString(0); + PgSQLXML v2 = (PgSQLXML)row.getValue(1); + ctx.assertEquals("", v1); + ctx.assertEquals(asXML, v2); + }) + ); + })); + } + + @Test + public void testBinaryEncodePgSQLXMLMalformed(TestContext ctx) { + PgSQLXML malformedXml = new PgSQLXML(">>"); + + PgConnection.connect(vertx, options, ctx.asyncAssertSuccess(conn -> { + conn.preparedQuery("SELECT ($1::xml)").execute(Tuple.of(malformedXml), + ctx.asyncAssertFailure(err -> { + assertThat(((PgException) err).getCode(), is(equalTo("2200N"))); + }) + ); + })); + } + + @Test + public void testTextDecodePgSQLXML(TestContext ctx) { + testDecodePgSQLXML(ctx, SqlClient::query); + } + + @Test + public void testBinaryDecodePgSQLXML(TestContext ctx) { + testDecodePgSQLXML(ctx, SqlClient::preparedQuery); + } + + private void testDecodePgSQLXML(TestContext ctx, BiFunction>> a) { + PgSQLXML first = new PgSQLXML(""); + + PgConnection.connect(vertx, options, ctx.asyncAssertSuccess(conn -> { + a.apply(conn, "SELECT ''::xml") + .execute(ctx.asyncAssertSuccess(rows -> { + ctx.assertEquals(1, rows.size()); + Row row = rows.iterator().next(); + PgSQLXML v1 = (PgSQLXML) row.getValue(0); + ctx.assertEquals(first, v1); + })); + })); + } + + @Test + public void testBinaryDecodePgSQLXMLArray(TestContext ctx) throws Exception { + PgSQLXML first = new PgSQLXML(""); + PgSQLXML second = new PgSQLXML(""); + + PgConnection.connect(vertx, options, ctx.asyncAssertSuccess(conn -> { + conn.preparedQuery("SELECT ARRAY[''::xml,''::xml]") + .execute(ctx.asyncAssertSuccess(rows -> { + ctx.assertEquals(1, rows.size()); + Row row = rows.iterator().next(); + PgSQLXML[] array = (PgSQLXML[]) row.getValue(0); + PgSQLXML v1 = array[0]; + PgSQLXML v2 = array[1]; + ctx.assertEquals(first, v1); + ctx.assertEquals(second, v2); + })); + })); + } + + @Test + public void testBinaryEncodePgSQLXMLArray(TestContext ctx) { + PgSQLXML first = new PgSQLXML(""); + PgSQLXML second = new PgSQLXML(""); + + + PgConnection.connect(vertx, options, ctx.asyncAssertSuccess(conn -> { + conn.preparedQuery("SELECT ($1::xml[])::VARCHAR[]").execute(Tuple.of( + new PgSQLXML[]{ + new PgSQLXML(""), + new PgSQLXML("")} + ), + ctx.asyncAssertSuccess(rows -> { + ctx.assertEquals(1, rows.size()); + Row row = rows.iterator().next(); + String[] array = row.getArrayOfStrings(0); + String v1 = array[0]; + String v2 = array[1]; + ctx.assertEquals(first.getXmlData(), v1); + ctx.assertEquals(second.getXmlData(), v2); + })); + })); + } +} From 54c289991daba30313ce76cb8a2fe02016c0261f Mon Sep 17 00:00:00 2001 From: Roman Meier Date: Thu, 23 Dec 2021 00:56:00 +0100 Subject: [PATCH 2/4] add support for postgres xml type Signed-off-by: Roman Meier --- .../java/io/vertx/pgclient/data/PgSQLXML.java | 36 ----------- .../java/io/vertx/pgclient/impl/RowImpl.java | 12 ---- .../vertx/pgclient/impl/codec/DataType.java | 8 +-- .../pgclient/impl/codec/DataTypeCodec.java | 60 ++++++++----------- ...SQLXMLCodecTest.java => XMLCodecTest.java} | 38 ++++++------ 5 files changed, 49 insertions(+), 105 deletions(-) delete mode 100644 vertx-pg-client/src/main/java/io/vertx/pgclient/data/PgSQLXML.java rename vertx-pg-client/src/test/java/io/vertx/pgclient/data/{PgSQLXMLCodecTest.java => XMLCodecTest.java} (68%) diff --git a/vertx-pg-client/src/main/java/io/vertx/pgclient/data/PgSQLXML.java b/vertx-pg-client/src/main/java/io/vertx/pgclient/data/PgSQLXML.java deleted file mode 100644 index 1c1ac53d8..000000000 --- a/vertx-pg-client/src/main/java/io/vertx/pgclient/data/PgSQLXML.java +++ /dev/null @@ -1,36 +0,0 @@ -package io.vertx.pgclient.data; - -import io.vertx.codegen.annotations.Nullable; - -import java.util.Objects; - -public class PgSQLXML { - - @Nullable final String xmlData; - - public PgSQLXML(String xmlData) { - this.xmlData = xmlData; - } - - public String getXmlData() { - return xmlData; - } - - @Override - public String toString() { - return xmlData; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - PgSQLXML pgSQLXML = (PgSQLXML) o; - return Objects.equals(xmlData, pgSQLXML.xmlData); - } - - @Override - public int hashCode() { - return Objects.hash(xmlData); - } -} diff --git a/vertx-pg-client/src/main/java/io/vertx/pgclient/impl/RowImpl.java b/vertx-pg-client/src/main/java/io/vertx/pgclient/impl/RowImpl.java index e9b8467e4..d5a3af7cd 100644 --- a/vertx-pg-client/src/main/java/io/vertx/pgclient/impl/RowImpl.java +++ b/vertx-pg-client/src/main/java/io/vertx/pgclient/impl/RowImpl.java @@ -111,8 +111,6 @@ public T get(Class type, int position) { return type.cast(getArrayOfIntervals(position)); } else if (componentType == Box.class) { return type.cast(getArrayOfBoxs(position)); - } else if (componentType == PgSQLXML.class) { - return type.cast(getArrayOfPgXMLSQLs(position)); } else if (componentType == Object.class) { return type.cast(getJsonArray_(position)); } else if (componentType.isEnum()) { @@ -169,8 +167,6 @@ public T get(Class type, int position) { return type.cast(getJson(position)); } else if (type == JsonArray.class) { return type.cast(getJson(position)); - } else if (type == PgSQLXML.class) { - return type.cast(getPgSQLXML(position)); } else if (type == Object.class) { return type.cast(getValue(position)); } else if (type.isEnum()) { @@ -212,10 +208,6 @@ private Interval getInterval(int pos) { return (Interval) getValue(pos); } - private PgSQLXML getPgSQLXML(int pos) { - return (PgSQLXML) getValue(pos); - } - private Object getEnum(Class enumType, int pos) { Object val = getValue(pos); if (val instanceof String) { @@ -271,10 +263,6 @@ private Interval[] getArrayOfIntervals(int pos) { return (Interval[]) getValue(pos); } - private PgSQLXML[] getArrayOfPgXMLSQLs(int pos) { - return (PgSQLXML[]) getValue(pos); - } - private Object[] getArrayOfEnums(Class enumType, int pos) { Object val = getValue(pos); if (val instanceof String[]) { diff --git a/vertx-pg-client/src/main/java/io/vertx/pgclient/impl/codec/DataType.java b/vertx-pg-client/src/main/java/io/vertx/pgclient/impl/codec/DataType.java index b1baacb80..40903d2ef 100644 --- a/vertx-pg-client/src/main/java/io/vertx/pgclient/impl/codec/DataType.java +++ b/vertx-pg-client/src/main/java/io/vertx/pgclient/impl/codec/DataType.java @@ -96,8 +96,8 @@ public enum DataType { JSON_ARRAY(199, true, Object[].class, JDBCType.OTHER, Tuple::getArrayOfJsons), JSONB(3802, true, Object.class, JDBCType.OTHER, Tuple::getJson), JSONB_ARRAY(3807, true, Object[].class, JDBCType.OTHER, Tuple::getArrayOfJsons), - XML(142, true, Object.class, JDBCType.SQLXML), - XML_ARRAY(143, true, Object[].class, JDBCType.SQLXML), + XML(142, true, String.class, JDBCType.SQLXML), + XML_ARRAY(143, true, String[].class, JDBCType.SQLXML), POINT(600, true, Point.class, JDBCType.OTHER), POINT_ARRAY(1017, true, Point[].class, JDBCType.OTHER), LINE(628, true, Line.class, JDBCType.OTHER), @@ -221,7 +221,7 @@ static DataType lookup(Class type) { encodingTypeToDataType.put(Circle.class, CIRCLE); encodingTypeToDataType.put(Circle[].class, CIRCLE_ARRAY); - encodingTypeToDataType.put(PgSQLXML.class, XML); - encodingTypeToDataType.put(PgSQLXML[].class, XML_ARRAY); +// encodingTypeToDataType.put(String.class, XML); +// encodingTypeToDataType.put(String[].class, XML_ARRAY); } } diff --git a/vertx-pg-client/src/main/java/io/vertx/pgclient/impl/codec/DataTypeCodec.java b/vertx-pg-client/src/main/java/io/vertx/pgclient/impl/codec/DataTypeCodec.java index b8205695a..d8694acb2 100644 --- a/vertx-pg-client/src/main/java/io/vertx/pgclient/impl/codec/DataTypeCodec.java +++ b/vertx-pg-client/src/main/java/io/vertx/pgclient/impl/codec/DataTypeCodec.java @@ -55,13 +55,17 @@ /** * @author Julien Viet * @author Emad Alblueshi - * + *

* See also https://www.npgsql.org/doc/dev/type-representations.html */ public class DataTypeCodec { + // Sentinel used when an object is refused by the data type + public static final Object REFUSED_SENTINEL = new Object(); + // 4714-11-24 00:00:00 BC + public static final LocalDateTime LDT_MINUS_INFINITY = LocalDateTime.parse("4714-11-24 00:00:00 BC", + DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss G", Locale.ROOT)); private static final Logger logger = LoggerFactory.getLogger(DataTypeCodec.class); - private static final String[] empty_string_array = new String[0]; private static final LocalDate[] empty_local_date_array = new LocalDate[0]; private static final LocalTime[] empty_local_time_array = new LocalTime[0]; @@ -88,15 +92,11 @@ public class DataTypeCodec { private static final Double[] empty_double_array = new Double[0]; private static final LocalDate LOCAL_DATE_EPOCH = LocalDate.of(2000, 1, 1); private static final LocalDateTime LOCAL_DATE_TIME_EPOCH = LocalDateTime.of(2000, 1, 1, 0, 0, 0); + // 294277-01-09 04:00:54.775807 + public static final LocalDateTime LDT_PLUS_INFINITY = LOCAL_DATE_TIME_EPOCH.plus(Long.MAX_VALUE, ChronoUnit.MICROS); private static final OffsetDateTime OFFSET_DATE_TIME_EPOCH = LocalDateTime.of(2000, 1, 1, 0, 0, 0).atOffset(ZoneOffset.UTC); private static final Inet[] empty_inet_array = new Inet[0]; private static final Money[] empty_money_array = new Money[0]; - private static final PgSQLXML[] empty_pgsqlxml_array = new PgSQLXML[0]; - - - // Sentinel used when an object is refused by the data type - public static final Object REFUSED_SENTINEL = new Object(); - private static final IntFunction BOOLEAN_ARRAY_FACTORY = size -> size == 0 ? empty_boolean_array : new Boolean[size]; private static final IntFunction SHORT_ARRAY_FACTORY = size -> size == 0 ? empty_short_array : new Short[size]; private static final IntFunction INTEGER_ARRAY_FACTORY = size -> size == 0 ? empty_integer_array : new Integer[size]; @@ -123,7 +123,6 @@ public class DataTypeCodec { private static final IntFunction INTERVAL_ARRAY_FACTORY = size -> size == 0 ? empty_interval_array : new Interval[size]; private static final IntFunction INET_ARRAY_FACTORY = size -> size == 0 ? empty_inet_array : new Inet[size]; private static final IntFunction MONEY_ARRAY_FACTORY = size -> size == 0 ? empty_money_array : new Money[size]; - private static final IntFunction PGSQLXML_ARRAY_FACTORY = size -> size == 0 ? empty_pgsqlxml_array : new PgSQLXML[size]; private static final java.time.format.DateTimeFormatter TIMETZ_FORMAT = new DateTimeFormatterBuilder() .parseCaseInsensitive() .append(ISO_LOCAL_TIME) @@ -277,13 +276,13 @@ public static void encodeBinary(DataType id, Object value, ByteBuf buff) { binaryEncodeArray((UUID[]) value, DataType.UUID, buff); break; case JSON: - binaryEncodeJSON((Object) value, buff); + binaryEncodeJSON(value, buff); break; case JSON_ARRAY: binaryEncodeArray((Object[]) value, DataType.JSON, buff); break; case JSONB: - binaryEncodeJSONB((Object) value, buff); + binaryEncodeJSONB(value, buff); break; case JSONB_ARRAY: binaryEncodeArray((Object[]) value, DataType.JSONB, buff); @@ -361,10 +360,10 @@ public static void encodeBinary(DataType id, Object value, ByteBuf buff) { binaryEncodeArray((Money[]) value, DataType.MONEY, buff); break; case XML: - binaryEncodePgXMLSQL((PgSQLXML) value, buff); + binaryEncodeXML((String) value, buff); break; case XML_ARRAY: - binaryEncodeArray((PgSQLXML[]) value, DataType.XML, buff); + binaryEncodeArray((String[]) value, DataType.XML, buff); break; default: logger.debug("Data type " + id + " does not support binary encoding"); @@ -504,9 +503,9 @@ public static Object decodeBinary(DataType id, int index, int len, ByteBuf buff) case MONEY_ARRAY: return binaryDecodeArray(MONEY_ARRAY_FACTORY, DataType.MONEY, index, len, buff); case XML: - return binaryDecodePgXMLSQL(index, len, buff); + return binaryDecodeXML(index, len, buff); case XML_ARRAY: - return binaryDecodeArray(PGSQLXML_ARRAY_FACTORY, DataType.XML, index, len, buff); + return binaryDecodeArray(STRING_ARRAY_FACTORY, DataType.XML, index, len, buff); default: logger.debug("Data type " + id + " does not support binary decoding"); return defaultDecodeBinary(index, len, buff); @@ -648,9 +647,9 @@ public static Object decodeText(DataType id, int index, int len, ByteBuf buff) { case MONEY_ARRAY: return textDecodeArray(MONEY_ARRAY_FACTORY, DataType.MONEY, index, len, buff); case XML: - return textDecodePgSQLXML(index, len, buff); + return textDecodeXML(index, len, buff); case XML_ARRAY: - return textDecodeArray(PGSQLXML_ARRAY_FACTORY, DataType.XML, index, len, buff); + return textDecodeArray(STRING_ARRAY_FACTORY, DataType.XML, index, len, buff); default: return defaultDecodeText(index, len, buff); } @@ -876,7 +875,7 @@ private static Interval textDecodeINTERVAL(int index, int len, ByteBuf buff) { int years = 0, months = 0, days = 0, hours = 0, minutes = 0, seconds = 0, microseconds = 0; final List chunks = new ArrayList<>(7); int idx = 0; - for (;;) { + for (; ; ) { int newIdx = value.indexOf(' ', idx); if (newIdx == -1) { chunks.add(value.substring(idx)); @@ -909,11 +908,11 @@ private static Interval textDecodeINTERVAL(int index, int len, ByteBuf buff) { boolean isNeg = timeChunk.charAt(0) == '-'; if (isNeg) timeChunk = timeChunk.substring(1); int sidx = 0; - for (;;) { + for (; ; ) { int newIdx = timeChunk.indexOf(':', sidx); if (newIdx == -1) { int m = timeChunk.substring(sidx).indexOf('.'); - if(m == -1) { + if (m == -1) { // seconds without microseconds seconds = isNeg ? -Integer.parseInt(timeChunk.substring(sidx)) : Integer.parseInt(timeChunk.substring(sidx)); @@ -927,7 +926,7 @@ private static Interval textDecodeINTERVAL(int index, int len, ByteBuf buff) { break; } // hours - if(sidx == 0) { + if (sidx == 0) { hours = isNeg ? -Integer.parseInt(timeChunk.substring(sidx, newIdx)) : Integer.parseInt(timeChunk.substring(sidx, newIdx)); } else { @@ -1082,12 +1081,6 @@ private static OffsetTime textDecodeTIMETZ(int index, int len, ByteBuf buff) { return OffsetTime.parse(cs, TIMETZ_FORMAT); } - // 294277-01-09 04:00:54.775807 - public static final LocalDateTime LDT_PLUS_INFINITY = LOCAL_DATE_TIME_EPOCH.plus(Long.MAX_VALUE, ChronoUnit.MICROS); - // 4714-11-24 00:00:00 BC - public static final LocalDateTime LDT_MINUS_INFINITY = LocalDateTime.parse("4714-11-24 00:00:00 BC", - DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss G", Locale.ROOT)); - private static void binaryEncodeTIMESTAMP(LocalDateTime value, ByteBuf buff) { if (value.compareTo(LDT_PLUS_INFINITY) >= 0) { value = LDT_PLUS_INFINITY; @@ -1490,17 +1483,16 @@ private static void binaryEncodeTsQuery(String value, ByteBuf buff) { buff.writeCharSequence(String.valueOf(value), StandardCharsets.UTF_8); } - private static PgSQLXML binaryDecodePgXMLSQL(int index, int len, ByteBuf buff) { - return new PgSQLXML(buff.getCharSequence(index, len, StandardCharsets.UTF_8).toString()); + private static String binaryDecodeXML(int index, int len, ByteBuf buff) { + return buff.getCharSequence(index, len, StandardCharsets.UTF_8).toString(); } - private static void binaryEncodePgXMLSQL(PgSQLXML value, ByteBuf buff) { - buff.writeCharSequence(value.toString(), StandardCharsets.UTF_8); + private static void binaryEncodeXML(String value, ByteBuf buff) { + buff.writeCharSequence(value, StandardCharsets.UTF_8); } - private static PgSQLXML textDecodePgSQLXML(int index, int len, ByteBuf buff) { - String s = textDecodeVARCHAR(index, len, buff); - return new PgSQLXML(s); + private static String textDecodeXML(int index, int len, ByteBuf buff) { + return buff.getCharSequence(index, len, StandardCharsets.UTF_8).toString(); } private static String textDecodeTsVector(int index, int len, ByteBuf buff) { diff --git a/vertx-pg-client/src/test/java/io/vertx/pgclient/data/PgSQLXMLCodecTest.java b/vertx-pg-client/src/test/java/io/vertx/pgclient/data/XMLCodecTest.java similarity index 68% rename from vertx-pg-client/src/test/java/io/vertx/pgclient/data/PgSQLXMLCodecTest.java rename to vertx-pg-client/src/test/java/io/vertx/pgclient/data/XMLCodecTest.java index 4575e56ec..438850888 100644 --- a/vertx-pg-client/src/test/java/io/vertx/pgclient/data/PgSQLXMLCodecTest.java +++ b/vertx-pg-client/src/test/java/io/vertx/pgclient/data/XMLCodecTest.java @@ -12,12 +12,12 @@ import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; -public class PgSQLXMLCodecTest extends DataTypeTestBase { +public class XMLCodecTest extends DataTypeTestBase { @Test public void testBinaryEncodePgSQLXMLAsVarcharOrXML(TestContext ctx) { - PgSQLXML asVarchar = new PgSQLXML(""); - PgSQLXML asXML = new PgSQLXML(""); + String asVarchar = ""; + String asXML = ""; PgConnection.connect(vertx, options, ctx.asyncAssertSuccess(conn -> { conn.preparedQuery("SELECT ($1::xml)::VARCHAR, ($2::xml)").execute(Tuple.of(asVarchar, asXML), @@ -25,7 +25,7 @@ public void testBinaryEncodePgSQLXMLAsVarcharOrXML(TestContext ctx) { ctx.assertEquals(1, rows.size()); Row row = rows.iterator().next(); String v1 = row.getString(0); - PgSQLXML v2 = (PgSQLXML)row.getValue(1); + String v2 = (String)row.getValue(1); ctx.assertEquals("", v1); ctx.assertEquals(asXML, v2); }) @@ -35,7 +35,7 @@ public void testBinaryEncodePgSQLXMLAsVarcharOrXML(TestContext ctx) { @Test public void testBinaryEncodePgSQLXMLMalformed(TestContext ctx) { - PgSQLXML malformedXml = new PgSQLXML(">>"); + String malformedXml = ">>"; PgConnection.connect(vertx, options, ctx.asyncAssertSuccess(conn -> { conn.preparedQuery("SELECT ($1::xml)").execute(Tuple.of(malformedXml), @@ -57,14 +57,14 @@ public void testBinaryDecodePgSQLXML(TestContext ctx) { } private void testDecodePgSQLXML(TestContext ctx, BiFunction>> a) { - PgSQLXML first = new PgSQLXML(""); + String first = ""; PgConnection.connect(vertx, options, ctx.asyncAssertSuccess(conn -> { a.apply(conn, "SELECT ''::xml") .execute(ctx.asyncAssertSuccess(rows -> { ctx.assertEquals(1, rows.size()); Row row = rows.iterator().next(); - PgSQLXML v1 = (PgSQLXML) row.getValue(0); + String v1 = (String) row.getValue(0); ctx.assertEquals(first, v1); })); })); @@ -72,17 +72,17 @@ private void testDecodePgSQLXML(TestContext ctx, BiFunction"); - PgSQLXML second = new PgSQLXML(""); + String first = ""; + String second = ""; PgConnection.connect(vertx, options, ctx.asyncAssertSuccess(conn -> { conn.preparedQuery("SELECT ARRAY[''::xml,''::xml]") .execute(ctx.asyncAssertSuccess(rows -> { ctx.assertEquals(1, rows.size()); Row row = rows.iterator().next(); - PgSQLXML[] array = (PgSQLXML[]) row.getValue(0); - PgSQLXML v1 = array[0]; - PgSQLXML v2 = array[1]; + String[] array = (String[]) row.getValue(0); + String v1 = array[0]; + String v2 = array[1]; ctx.assertEquals(first, v1); ctx.assertEquals(second, v2); })); @@ -91,15 +91,15 @@ public void testBinaryDecodePgSQLXMLArray(TestContext ctx) throws Exception { @Test public void testBinaryEncodePgSQLXMLArray(TestContext ctx) { - PgSQLXML first = new PgSQLXML(""); - PgSQLXML second = new PgSQLXML(""); + String first = ""; + String second = ""; PgConnection.connect(vertx, options, ctx.asyncAssertSuccess(conn -> { conn.preparedQuery("SELECT ($1::xml[])::VARCHAR[]").execute(Tuple.of( - new PgSQLXML[]{ - new PgSQLXML(""), - new PgSQLXML("")} + new String[]{ + "", + ""} ), ctx.asyncAssertSuccess(rows -> { ctx.assertEquals(1, rows.size()); @@ -107,8 +107,8 @@ public void testBinaryEncodePgSQLXMLArray(TestContext ctx) { String[] array = row.getArrayOfStrings(0); String v1 = array[0]; String v2 = array[1]; - ctx.assertEquals(first.getXmlData(), v1); - ctx.assertEquals(second.getXmlData(), v2); + ctx.assertEquals(first, v1); + ctx.assertEquals(second, v2); })); })); } From 03fc0b06cb020d6ea24cf84d6fdcefe819fd9a1a Mon Sep 17 00:00:00 2001 From: Roman Meier Date: Mon, 31 Jan 2022 10:32:29 +0100 Subject: [PATCH 3/4] revert style changes Signed-off-by: Roman Meier --- .../java/io/vertx/pgclient/impl/RowImpl.java | 9 ++++- .../vertx/pgclient/impl/codec/DataType.java | 21 ++++++++---- .../pgclient/impl/codec/DataTypeCodec.java | 34 +++++++++++-------- 3 files changed, 42 insertions(+), 22 deletions(-) diff --git a/vertx-pg-client/src/main/java/io/vertx/pgclient/impl/RowImpl.java b/vertx-pg-client/src/main/java/io/vertx/pgclient/impl/RowImpl.java index d5a3af7cd..716919314 100644 --- a/vertx-pg-client/src/main/java/io/vertx/pgclient/impl/RowImpl.java +++ b/vertx-pg-client/src/main/java/io/vertx/pgclient/impl/RowImpl.java @@ -19,9 +19,16 @@ import io.vertx.core.json.JsonArray; import io.vertx.core.json.JsonObject; -import io.vertx.pgclient.data.*; +import io.vertx.pgclient.data.Box; +import io.vertx.pgclient.data.Circle; +import io.vertx.pgclient.data.Line; +import io.vertx.pgclient.data.LineSegment; import io.vertx.sqlclient.Row; import io.vertx.sqlclient.data.Numeric; +import io.vertx.pgclient.data.Path; +import io.vertx.pgclient.data.Polygon; +import io.vertx.pgclient.data.Interval; +import io.vertx.pgclient.data.Point; import io.vertx.sqlclient.impl.ArrayTuple; import io.vertx.sqlclient.impl.RowDesc; import io.vertx.core.buffer.Buffer; diff --git a/vertx-pg-client/src/main/java/io/vertx/pgclient/impl/codec/DataType.java b/vertx-pg-client/src/main/java/io/vertx/pgclient/impl/codec/DataType.java index 40903d2ef..8fef35514 100644 --- a/vertx-pg-client/src/main/java/io/vertx/pgclient/impl/codec/DataType.java +++ b/vertx-pg-client/src/main/java/io/vertx/pgclient/impl/codec/DataType.java @@ -22,9 +22,18 @@ import io.vertx.core.impl.logging.LoggerFactory; import io.vertx.core.json.JsonArray; import io.vertx.core.json.JsonObject; -import io.vertx.pgclient.data.*; +import io.vertx.pgclient.data.Box; +import io.vertx.pgclient.data.Circle; +import io.vertx.pgclient.data.Inet; +import io.vertx.pgclient.data.Line; +import io.vertx.pgclient.data.LineSegment; +import io.vertx.pgclient.data.Money; import io.vertx.sqlclient.Tuple; import io.vertx.sqlclient.data.Numeric; +import io.vertx.pgclient.data.Interval; +import io.vertx.pgclient.data.Path; +import io.vertx.pgclient.data.Point; +import io.vertx.pgclient.data.Polygon; import io.vertx.core.buffer.Buffer; import java.sql.JDBCType; @@ -177,8 +186,11 @@ static DataType lookup(Class type) { for (DataType dataType : values()) { oidToDataType.put(dataType.id, dataType); } - encodingTypeToDataType.put(String.class, VARCHAR); - encodingTypeToDataType.put(String[].class, VARCHAR_ARRAY); +// encodingTypeToDataType.put(String.class, VARCHAR); +// encodingTypeToDataType.put(String[].class, VARCHAR_ARRAY); + encodingTypeToDataType.put(String.class, XML); + encodingTypeToDataType.put(String[].class, XML_ARRAY); + encodingTypeToDataType.put(Boolean.class, BOOL); encodingTypeToDataType.put(Boolean[].class, BOOL_ARRAY); encodingTypeToDataType.put(Short.class, INT2); @@ -220,8 +232,5 @@ static DataType lookup(Class type) { encodingTypeToDataType.put(Polygon[].class, POLYGON_ARRAY); encodingTypeToDataType.put(Circle.class, CIRCLE); encodingTypeToDataType.put(Circle[].class, CIRCLE_ARRAY); - -// encodingTypeToDataType.put(String.class, XML); -// encodingTypeToDataType.put(String[].class, XML_ARRAY); } } diff --git a/vertx-pg-client/src/main/java/io/vertx/pgclient/impl/codec/DataTypeCodec.java b/vertx-pg-client/src/main/java/io/vertx/pgclient/impl/codec/DataTypeCodec.java index d8694acb2..9dc26e701 100644 --- a/vertx-pg-client/src/main/java/io/vertx/pgclient/impl/codec/DataTypeCodec.java +++ b/vertx-pg-client/src/main/java/io/vertx/pgclient/impl/codec/DataTypeCodec.java @@ -24,12 +24,13 @@ import io.vertx.core.impl.logging.Logger; import io.vertx.core.impl.logging.LoggerFactory; import io.vertx.core.json.Json; -import io.vertx.core.json.JsonArray; -import io.vertx.core.json.JsonObject; -import io.vertx.pgclient.data.*; -import io.vertx.pgclient.impl.util.UTF8StringEndDetector; import io.vertx.sqlclient.Tuple; import io.vertx.sqlclient.data.Numeric; +import io.vertx.pgclient.data.*; +import io.vertx.pgclient.impl.util.UTF8StringEndDetector; +import io.vertx.core.buffer.Buffer; +import io.vertx.core.json.JsonArray; +import io.vertx.core.json.JsonObject; import io.vertx.sqlclient.impl.codec.CommonCodec; import java.net.Inet4Address; @@ -50,21 +51,16 @@ import static java.time.format.DateTimeFormatter.ISO_LOCAL_DATE; import static java.time.format.DateTimeFormatter.ISO_LOCAL_TIME; -import static java.util.concurrent.TimeUnit.NANOSECONDS; +import static java.util.concurrent.TimeUnit.*; /** * @author Julien Viet * @author Emad Alblueshi - *

+ * * See also https://www.npgsql.org/doc/dev/type-representations.html */ public class DataTypeCodec { - // Sentinel used when an object is refused by the data type - public static final Object REFUSED_SENTINEL = new Object(); - // 4714-11-24 00:00:00 BC - public static final LocalDateTime LDT_MINUS_INFINITY = LocalDateTime.parse("4714-11-24 00:00:00 BC", - DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss G", Locale.ROOT)); private static final Logger logger = LoggerFactory.getLogger(DataTypeCodec.class); private static final String[] empty_string_array = new String[0]; private static final LocalDate[] empty_local_date_array = new LocalDate[0]; @@ -92,11 +88,13 @@ public class DataTypeCodec { private static final Double[] empty_double_array = new Double[0]; private static final LocalDate LOCAL_DATE_EPOCH = LocalDate.of(2000, 1, 1); private static final LocalDateTime LOCAL_DATE_TIME_EPOCH = LocalDateTime.of(2000, 1, 1, 0, 0, 0); - // 294277-01-09 04:00:54.775807 - public static final LocalDateTime LDT_PLUS_INFINITY = LOCAL_DATE_TIME_EPOCH.plus(Long.MAX_VALUE, ChronoUnit.MICROS); private static final OffsetDateTime OFFSET_DATE_TIME_EPOCH = LocalDateTime.of(2000, 1, 1, 0, 0, 0).atOffset(ZoneOffset.UTC); private static final Inet[] empty_inet_array = new Inet[0]; private static final Money[] empty_money_array = new Money[0]; + + // Sentinel used when an object is refused by the data type + public static final Object REFUSED_SENTINEL = new Object(); + private static final IntFunction BOOLEAN_ARRAY_FACTORY = size -> size == 0 ? empty_boolean_array : new Boolean[size]; private static final IntFunction SHORT_ARRAY_FACTORY = size -> size == 0 ? empty_short_array : new Short[size]; private static final IntFunction INTEGER_ARRAY_FACTORY = size -> size == 0 ? empty_integer_array : new Integer[size]; @@ -276,13 +274,13 @@ public static void encodeBinary(DataType id, Object value, ByteBuf buff) { binaryEncodeArray((UUID[]) value, DataType.UUID, buff); break; case JSON: - binaryEncodeJSON(value, buff); + binaryEncodeJSON((Object) value, buff); break; case JSON_ARRAY: binaryEncodeArray((Object[]) value, DataType.JSON, buff); break; case JSONB: - binaryEncodeJSONB(value, buff); + binaryEncodeJSONB((Object) value, buff); break; case JSONB_ARRAY: binaryEncodeArray((Object[]) value, DataType.JSONB, buff); @@ -1081,6 +1079,12 @@ private static OffsetTime textDecodeTIMETZ(int index, int len, ByteBuf buff) { return OffsetTime.parse(cs, TIMETZ_FORMAT); } + // 294277-01-09 04:00:54.775807 + public static final LocalDateTime LDT_PLUS_INFINITY = LOCAL_DATE_TIME_EPOCH.plus(Long.MAX_VALUE, ChronoUnit.MICROS); + // 4714-11-24 00:00:00 BC + public static final LocalDateTime LDT_MINUS_INFINITY = LocalDateTime.parse("4714-11-24 00:00:00 BC", + DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss G", Locale.ROOT)); + private static void binaryEncodeTIMESTAMP(LocalDateTime value, ByteBuf buff) { if (value.compareTo(LDT_PLUS_INFINITY) >= 0) { value = LDT_PLUS_INFINITY; From 6d6d1673887786974da958e8533606d7050edb5e Mon Sep 17 00:00:00 2001 From: Roman Meier Date: Mon, 31 Jan 2022 10:38:54 +0100 Subject: [PATCH 4/4] revert style changes Signed-off-by: Roman Meier --- .../pgclient/impl/codec/DataTypeCodec.java | 35 +++++++++++-------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/vertx-pg-client/src/main/java/io/vertx/pgclient/impl/codec/DataTypeCodec.java b/vertx-pg-client/src/main/java/io/vertx/pgclient/impl/codec/DataTypeCodec.java index 9dc26e701..d1dee4a1c 100644 --- a/vertx-pg-client/src/main/java/io/vertx/pgclient/impl/codec/DataTypeCodec.java +++ b/vertx-pg-client/src/main/java/io/vertx/pgclient/impl/codec/DataTypeCodec.java @@ -62,6 +62,7 @@ public class DataTypeCodec { private static final Logger logger = LoggerFactory.getLogger(DataTypeCodec.class); + private static final String[] empty_string_array = new String[0]; private static final LocalDate[] empty_local_date_array = new LocalDate[0]; private static final LocalTime[] empty_local_time_array = new LocalTime[0]; @@ -107,7 +108,7 @@ public class DataTypeCodec { private static final IntFunction OFFSETTIME_ARRAY_FACTORY = size -> size == 0 ? empty_offset_time_array : new OffsetTime[size]; private static final IntFunction LOCALDATETIME_ARRAY_FACTORY = size -> size == 0 ? empty_local_date_time_array : new LocalDateTime[size]; private static final IntFunction OFFSETDATETIME_ARRAY_FACTORY = size -> size == 0 ? empty_offset_date_time_array : new OffsetDateTime[size]; - private static final IntFunction BUFFER_ARRAY_FACTORY = size -> size == 0 ? empty_buffer_array : new Buffer[size]; + private static final IntFunction BUFFER_ARRAY_FACTORY =size -> size == 0 ? empty_buffer_array : new Buffer[size]; private static final IntFunction UUID_ARRAY_FACTORY = size -> size == 0 ? empty_uuid_array : new UUID[size]; private static final IntFunction JSON_ARRAY_FACTORY = size -> size == 0 ? empty_json_array : new Object[size]; private static final IntFunction NUMERIC_ARRAY_FACTORY = size -> size == 0 ? empty_numeric_array : new Numeric[size]; @@ -121,17 +122,20 @@ public class DataTypeCodec { private static final IntFunction INTERVAL_ARRAY_FACTORY = size -> size == 0 ? empty_interval_array : new Interval[size]; private static final IntFunction INET_ARRAY_FACTORY = size -> size == 0 ? empty_inet_array : new Inet[size]; private static final IntFunction MONEY_ARRAY_FACTORY = size -> size == 0 ? empty_money_array : new Money[size]; + private static final java.time.format.DateTimeFormatter TIMETZ_FORMAT = new DateTimeFormatterBuilder() .parseCaseInsensitive() .append(ISO_LOCAL_TIME) .appendOffset("+HH:mm", "00:00") .toFormatter(); + private static final java.time.format.DateTimeFormatter TIMESTAMP_FORMAT = new DateTimeFormatterBuilder() .parseCaseInsensitive() .append(ISO_LOCAL_DATE) .appendLiteral(' ') .append(ISO_LOCAL_TIME) .toFormatter(); + private static final java.time.format.DateTimeFormatter TIMESTAMPTZ_FORMAT = new DateTimeFormatterBuilder() .append(TIMESTAMP_FORMAT) .appendOffset("+HH:mm", "00:00") @@ -680,7 +684,7 @@ private static Boolean binaryDecodeBOOL(int index, int len, ByteBuf buff) { } private static Boolean textDecodeBOOL(int index, int len, ByteBuf buff) { - if (buff.getByte(index) == 't') { + if(buff.getByte(index) == 't') { return Boolean.TRUE; } else { return Boolean.FALSE; @@ -781,7 +785,7 @@ private static Line textDecodeLine(int index, int len, ByteBuf buff) { private static LineSegment textDecodeLseg(int index, int len, ByteBuf buff) { // Lseg representation: [p1,p2] - int idxOfPointsSeparator = buff.indexOf(index, index + len, (byte) ')') + 1; + int idxOfPointsSeparator = buff.indexOf(index, index+len, (byte) ')') + 1; int lenOfP1 = idxOfPointsSeparator - index - 1; Point p1 = textDecodePOINT(index + 1, lenOfP1, buff); Point p2 = textDecodePOINT(idxOfPointsSeparator + 1, len - lenOfP1 - 3, buff); @@ -790,7 +794,7 @@ private static LineSegment textDecodeLseg(int index, int len, ByteBuf buff) { private static Box textDecodeBox(int index, int len, ByteBuf buff) { // Box representation: p1,p2 - int idxOfPointsSeparator = buff.indexOf(index, index + len, (byte) ')') + 1; + int idxOfPointsSeparator = buff.indexOf(index, index+len, (byte) ')') + 1; int lenOfUpperRightCornerPoint = idxOfPointsSeparator - index; Point upperRightCorner = textDecodePOINT(index, lenOfUpperRightCornerPoint, buff); Point lowerLeftCorner = textDecodePOINT(idxOfPointsSeparator + 1, len - lenOfUpperRightCornerPoint - 1, buff); @@ -873,7 +877,7 @@ private static Interval textDecodeINTERVAL(int index, int len, ByteBuf buff) { int years = 0, months = 0, days = 0, hours = 0, minutes = 0, seconds = 0, microseconds = 0; final List chunks = new ArrayList<>(7); int idx = 0; - for (; ; ) { + for (;;) { int newIdx = value.indexOf(' ', idx); if (newIdx == -1) { chunks.add(value.substring(idx)); @@ -906,17 +910,17 @@ private static Interval textDecodeINTERVAL(int index, int len, ByteBuf buff) { boolean isNeg = timeChunk.charAt(0) == '-'; if (isNeg) timeChunk = timeChunk.substring(1); int sidx = 0; - for (; ; ) { + for (;;) { int newIdx = timeChunk.indexOf(':', sidx); if (newIdx == -1) { int m = timeChunk.substring(sidx).indexOf('.'); - if (m == -1) { + if(m == -1) { // seconds without microseconds seconds = isNeg ? -Integer.parseInt(timeChunk.substring(sidx)) : Integer.parseInt(timeChunk.substring(sidx)); } else { // seconds with microseconds - seconds = isNeg ? -Integer.parseInt(timeChunk.substring(sidx).substring(0, m)) + seconds = isNeg ? -Integer.parseInt(timeChunk.substring(sidx).substring(0, m)) : Integer.parseInt(timeChunk.substring(sidx).substring(0, m)); microseconds = isNeg ? -Integer.parseInt(timeChunk.substring(sidx).substring(m + 1)) : Integer.parseInt(timeChunk.substring(sidx).substring(m + 1)); @@ -924,7 +928,7 @@ private static Interval textDecodeINTERVAL(int index, int len, ByteBuf buff) { break; } // hours - if (sidx == 0) { + if(sidx == 0) { hours = isNeg ? -Integer.parseInt(timeChunk.substring(sidx, newIdx)) : Integer.parseInt(timeChunk.substring(sidx, newIdx)); } else { @@ -1001,6 +1005,7 @@ private static String textDecodeNAME(int index, int len, ByteBuf buff) { return buff.getCharSequence(index, len, StandardCharsets.UTF_8).toString(); } + private static void binaryEncodeNAME(String value, ByteBuf buff) { String s = String.valueOf(value); buff.writeCharSequence(s, StandardCharsets.UTF_8); @@ -1476,7 +1481,7 @@ private static void binaryEncodeMoney(Money money, ByteBuf buff) { private static Money binaryDecodeMoney(int index, int len, ByteBuf buff) { long value = binaryDecodeINT8(index, len, buff); - return new Money(value / 100, Math.abs(((int) value % 100))); + return new Money(value / 100, Math.abs(((int)value % 100))); } private static String binaryDecodeTsQuery(int index, int len, ByteBuf buff) { @@ -1557,7 +1562,7 @@ private static Money textDecodeMoney(int index, int len, ByteBuf buff) { * Decode the specified {@code buff} formatted as an hex string starting at the buffer readable index * with the specified {@code length} to a {@link Buffer}. * - * @param len the hex string length + * @param len the hex string length * @param buff the byte buff to read from * @return the decoded value as a Buffer */ @@ -1573,7 +1578,7 @@ private static Buffer decodeHexStringToBytes(int index, int len, ByteBuf buff) { } private static byte decodeHexChar(byte ch) { - return (byte) (((ch & 0x1F) + ((ch >> 6) * 0x19) - 0x10) & 0x0F); + return (byte)(((ch & 0x1F) + ((ch >> 6) * 0x19) - 0x10) & 0x0F); } private static boolean isHexFormat(int index, int len, ByteBuf buff) { @@ -1641,7 +1646,7 @@ private static T[] binaryDecodeArray(IntFunction supplier, DataType typ return array; } - private static void binaryEncodeArray(T[] values, DataType type, ByteBuf buff) { + private static void binaryEncodeArray(T[] values, DataType type, ByteBuf buff){ int startIndex = buff.writerIndex(); buff.writeInt(1); // ndim buff.writeInt(0); // dataoffset @@ -1703,7 +1708,7 @@ private static T textDecodeArrayElement(DataType type, int index, int len, B // Some escaping - improve that later... String s = buff.toString(index + 1, len - 2, StandardCharsets.UTF_8); StringBuilder sb = new StringBuilder(); - for (int i = 0; i < s.length(); i++) { + for (int i = 0;i < s.length();i++) { char c = s.charAt(i); if (c == '\\') { c = s.charAt(++i); @@ -1718,7 +1723,7 @@ private static T textDecodeArrayElement(DataType type, int index, int len, B } } - private static void textEncodeArray(T[] values, DataType type, ByteBuf buff) { + private static void textEncodeArray(T[] values, DataType type, ByteBuf buff){ buff.writeByte('{'); int len = values.length; for (int i = 0; i < len; i++) {