From 755b5d06dc008fa23582bc95df9cff35cd57092c Mon Sep 17 00:00:00 2001 From: Thomas Segismont Date: Wed, 13 Mar 2024 16:43:10 +0100 Subject: [PATCH 1/2] Added Cidr data type support in pgclient --- vertx-pg-client/README.adoc | 6 +- .../java/io/vertx/pgclient/data/Cidr.java | 40 +++++++++ .../vertx/pgclient/impl/codec/DataType.java | 3 +- .../pgclient/impl/codec/DataTypeCodec.java | 88 +++++++++++++++++++ .../java/io/vertx/pgclient/data/CidrTest.java | 48 ++++++++++ 5 files changed, 183 insertions(+), 2 deletions(-) create mode 100644 vertx-pg-client/src/main/java/io/vertx/pgclient/data/Cidr.java create mode 100644 vertx-pg-client/src/test/java/io/vertx/pgclient/data/CidrTest.java diff --git a/vertx-pg-client/README.adoc b/vertx-pg-client/README.adoc index 2c586afb6..7010b4e27 100644 --- a/vertx-pg-client/README.adoc +++ b/vertx-pg-client/README.adoc @@ -221,6 +221,10 @@ The *Reactive Postgres Client* currently supports the following data types |`io.vertx.pgclient.data.Money[]` |✔ +|`CIDR` +|`io.vertx.pgclient.data.Cidr` +|✔ + |`PATH` |`i.r.p.data.Path` |✔ @@ -270,7 +274,7 @@ Note: PostgreSQL JSON and JSONB types are represented by the following Java type The following types -_MONEY_, _BIT_, _VARBIT_, _MACADDR_, _CIDR_, _MACADDR8_, +_MONEY_, _BIT_, _VARBIT_, _MACADDR_, _MACADDR8_, _XML_, _HSTORE_, _OID_, _VOID_ diff --git a/vertx-pg-client/src/main/java/io/vertx/pgclient/data/Cidr.java b/vertx-pg-client/src/main/java/io/vertx/pgclient/data/Cidr.java new file mode 100644 index 000000000..ac85bc0df --- /dev/null +++ b/vertx-pg-client/src/main/java/io/vertx/pgclient/data/Cidr.java @@ -0,0 +1,40 @@ +package io.vertx.pgclient.data; + +import java.net.Inet4Address; +import java.net.Inet6Address; +import java.net.InetAddress; + +/** + * A PostgreSQL classless internet domain routing. + */ +public class Cidr { + private InetAddress address; + private Integer netmask; + + public InetAddress getAddress(){ + return address; + } + public Cidr setAddress(InetAddress address) { + if (address instanceof Inet4Address || address instanceof Inet6Address) { + this.address = address; + } else { + throw new IllegalArgumentException("Invalid IP address type"); + } + return this; + } + + public Integer getNetmask(){ + return netmask; + } + + public Cidr setNetmask(Integer netmask) { + if (netmask != null && ((getAddress() instanceof Inet4Address && (netmask < 0 || netmask > 32)) || + (getAddress() instanceof Inet6Address && (netmask < 0 || netmask > 128)))) { + throw new IllegalArgumentException("Invalid netmask: " + netmask); + } + this.netmask = netmask; + return this; + } + + +} 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..3c755d53c 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 @@ -28,6 +28,7 @@ import io.vertx.pgclient.data.Line; import io.vertx.pgclient.data.LineSegment; import io.vertx.pgclient.data.Money; +import io.vertx.pgclient.data.Cidr; import io.vertx.sqlclient.Tuple; import io.vertx.sqlclient.data.Numeric; import io.vertx.pgclient.data.Interval; @@ -97,7 +98,7 @@ public enum DataType { MACADDR(829, true, Object.class, JDBCType.OTHER), INET(869, true, Inet.class, JDBCType.OTHER), INET_ARRAY(1041, true, Inet[].class, JDBCType.OTHER), - CIDR(650, true, Object.class, JDBCType.OTHER), + CIDR(650, true, Cidr.class, JDBCType.OTHER), MACADDR8(774, true, Object[].class, JDBCType.OTHER), UUID(2950, true, UUID.class, JDBCType.OTHER, Tuple::getUUID), UUID_ARRAY(2951, true, UUID[].class, JDBCType.OTHER, Tuple::getArrayOfUUIDs), 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 975223562..d12b37f58 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 @@ -364,6 +364,9 @@ public static void encodeBinary(DataType id, Object value, ByteBuf buff) { case MONEY_ARRAY: binaryEncodeArray((Money[]) value, DataType.MONEY, buff); break; + case CIDR: + binaryEncodeCidr((Cidr) value, buff); + break; default: logger.debug("Data type " + id + " does not support binary encoding"); defaultEncodeBinary(value, buff); @@ -501,6 +504,8 @@ 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 CIDR: + return binaryDecodeCidr(index, len, buff); default: logger.debug("Data type " + id + " does not support binary decoding"); return defaultDecodeBinary(index, len, buff); @@ -641,6 +646,8 @@ 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 CIDR: + return textDecodeCidr(index, len, buff); default: return defaultDecodeText(index, len, buff); } @@ -1713,4 +1720,85 @@ private static void textEncodeArray(T[] values, DataType type, ByteBuf buff) } buff.writeByte('}'); } + + private static Cidr binaryDecodeCidr(int index, int len, ByteBuf buff){ + byte family = buff.getByte(index); + byte netmask = buff.getByte(index+1); + Integer val; + int size = buff.getByte(index+3); + byte[] data = new byte[size]; + buff.getBytes(index+4,data); + InetAddress address; + + switch (family){ + case 2: + case 3: + // IPV4 and IPV6 + try { + address = InetAddress.getByAddress(data); + }catch (UnknownHostException e){ + throw new DecoderException(e); + } + break; + default: + throw new DecoderException("Invalid IP family: " + family); + } + val = Byte.toUnsignedInt(netmask); + return new Cidr().setAddress(address).setNetmask(val); + } + + private static void binaryEncodeCidr(Cidr value, ByteBuf buff) { + InetAddress address = value.getAddress(); + byte family; + byte[] data; + int netmask; + + if (address instanceof Inet6Address) { + family = 3; + Inet6Address inet6Address = (Inet6Address) address; + data = inet6Address.getAddress(); + netmask = (value.getNetmask() == null) ? 128 : value.getNetmask(); + } else if (address instanceof Inet4Address) { + family = 2; + Inet4Address inet4Address = (Inet4Address) address; + data = inet4Address.getAddress(); + netmask = (value.getNetmask() == null) ? 32 : value.getNetmask(); + } else { + throw new DecoderException("Invalid inet address"); + } + + buff.writeByte(family); + buff.writeByte(netmask); + buff.writeByte(0); // INET + buff.writeByte(data.length); + buff.writeBytes(data); + } + + private static Cidr textDecodeCidr(int index, int len, ByteBuf buff) { + Cidr cidr = new Cidr(); + int sepIdx = buff.indexOf(index, index + len, (byte) '/'); + String s; + + if (sepIdx == -1) { + s = textdecodeTEXT(index, len, buff); + } else { + s = textdecodeTEXT(index, sepIdx - index, buff); + String t = textdecodeTEXT(sepIdx + 1, len - (sepIdx + 1 - index), buff); + try { + int netmask = Integer.parseInt(t); + cidr.setNetmask(netmask); + } catch (NumberFormatException e) { + throw new DecoderException(e); + } + } + + try { + InetAddress v = InetAddress.getByName(s); + cidr.setAddress(v); + } catch (UnknownHostException e) { + throw new DecoderException(e); + } + + return cidr; + } } diff --git a/vertx-pg-client/src/test/java/io/vertx/pgclient/data/CidrTest.java b/vertx-pg-client/src/test/java/io/vertx/pgclient/data/CidrTest.java new file mode 100644 index 000000000..9de19664c --- /dev/null +++ b/vertx-pg-client/src/test/java/io/vertx/pgclient/data/CidrTest.java @@ -0,0 +1,48 @@ +package io.vertx.pgclient.data; + +import org.junit.Test; + +import java.net.InetAddress; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThrows; + +public class CidrTest extends DataTypeTestBase{ + + @Test + public void testValidIPv4() throws Exception { + InetAddress address = InetAddress.getByName("192.168.1.1"); + Cidr cidr = new Cidr(); + cidr.setAddress(address); + cidr.setNetmask(24); + assertEquals(address, cidr.getAddress()); + assertEquals(Integer.valueOf(24), cidr.getNetmask()); + } + + @Test + public void testValidIPv6() throws Exception { + InetAddress address = InetAddress.getByName("fe80::f03c:91ff:feae:e944"); + Cidr cidr = new Cidr(); + cidr.setAddress(address); + cidr.setNetmask(64); + assertEquals(address, cidr.getAddress()); + assertEquals(Integer.valueOf(64), cidr.getNetmask()); + } + + @Test + public void testInvalidNetmaskIPv4() throws Exception { + InetAddress address = InetAddress.getByName("192.168.1.1"); + Cidr cidr = new Cidr(); + cidr.setAddress(address); + assertThrows(IllegalArgumentException.class, () -> cidr.setNetmask(33)); + } + + @Test + public void testInvalidNetmaskIPv6() throws Exception { + InetAddress address = InetAddress.getByName("fe80::f03c:91ff:feae:e944"); + Cidr cidr = new Cidr(); + cidr.setAddress(address); + assertThrows(IllegalArgumentException.class, () -> cidr.setNetmask(129)); + } + +} From f107d9a2cbedabd695de7d6cc649e00d297760a8 Mon Sep 17 00:00:00 2001 From: Ayan Koche1 Date: Tue, 23 Apr 2024 14:19:23 +0530 Subject: [PATCH 2/2] added pg dependent tests --- .../io/vertx/pgclient/data/CidrCodecTest.java | 116 ++++++++++++++++++ .../java/io/vertx/pgclient/data/CidrTest.java | 48 -------- 2 files changed, 116 insertions(+), 48 deletions(-) create mode 100644 vertx-pg-client/src/test/java/io/vertx/pgclient/data/CidrCodecTest.java delete mode 100644 vertx-pg-client/src/test/java/io/vertx/pgclient/data/CidrTest.java diff --git a/vertx-pg-client/src/test/java/io/vertx/pgclient/data/CidrCodecTest.java b/vertx-pg-client/src/test/java/io/vertx/pgclient/data/CidrCodecTest.java new file mode 100644 index 000000000..c35c69309 --- /dev/null +++ b/vertx-pg-client/src/test/java/io/vertx/pgclient/data/CidrCodecTest.java @@ -0,0 +1,116 @@ +package io.vertx.pgclient.data; + +import io.vertx.ext.unit.TestContext; +import io.vertx.pgclient.PgConnection; +import io.vertx.sqlclient.*; +import org.junit.Test; + +import java.net.Inet4Address; +import java.net.Inet6Address; +import java.net.InetAddress; +import java.util.function.BiFunction; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThrows; + +public class CidrCodecTest extends DataTypeTestBase{ + + @Test + public void testValidIPv4() throws Exception { + InetAddress address = InetAddress.getByName("192.168.1.1"); + Cidr cidr = new Cidr(); + cidr.setAddress(address); + cidr.setNetmask(24); + assertEquals(address, cidr.getAddress()); + assertEquals(Integer.valueOf(24), cidr.getNetmask()); + } + + @Test + public void testValidIPv6() throws Exception { + InetAddress address = InetAddress.getByName("fe80::f03c:91ff:feae:e944"); + Cidr cidr = new Cidr(); + cidr.setAddress(address); + cidr.setNetmask(64); + assertEquals(address, cidr.getAddress()); + assertEquals(Integer.valueOf(64), cidr.getNetmask()); + } + + @Test + public void testInvalidNetmaskIPv4() throws Exception { + InetAddress address = InetAddress.getByName("192.168.1.1"); + Cidr cidr = new Cidr(); + cidr.setAddress(address); + assertThrows(IllegalArgumentException.class, () -> cidr.setNetmask(33)); + } + + @Test + public void testInvalidNetmaskIPv6() throws Exception { + InetAddress address = InetAddress.getByName("fe80::f03c:91ff:feae:e944"); + Cidr cidr = new Cidr(); + cidr.setAddress(address); + assertThrows(IllegalArgumentException.class, () -> cidr.setNetmask(129)); + } + + @Test + public void testBinaryDecodeCIDR(TestContext ctx) throws Exception { + testDecodeCIDR(ctx, SqlClient::preparedQuery); + } + + private void testDecodeCIDR(TestContext ctx, BiFunction>> a) throws Exception { + InetAddress addr1 = Inet4Address.getByName("128.0.0.0"); + InetAddress addr2 = Inet6Address.getByName("2001:0db8:1234:0000:0000:0000:0000:0000"); + PgConnection.connect(vertx, options).onComplete(ctx.asyncAssertSuccess(conn -> { + a.apply(conn, "SELECT " + + "'128.0.0.0'::CIDR," + + "'128.0.0.0/4'::CIDR," + + "'2001:0db8:1234:0000:0000:0000:0000:0000'::CIDR," + + "'2001:0db8:1234:0000:0000:0000:0000:0000/56'::CIDR") + .execute() + .onComplete(ctx.asyncAssertSuccess(rows -> { + ctx.assertEquals(1, rows.size()); + Row row = rows.iterator().next(); + Cidr v1 = (Cidr) row.getValue(0); + Cidr v2 = (Cidr) row.getValue(1); + Cidr v3 = (Cidr) row.getValue(2); + Cidr v4 = (Cidr) row.getValue(3); + ctx.assertEquals(addr1, v1.getAddress()); + ctx.assertEquals(32,v1.getNetmask()); + ctx.assertEquals(addr1, v2.getAddress()); + ctx.assertEquals(4, v2.getNetmask()); + ctx.assertEquals(addr2, v3.getAddress()); + ctx.assertEquals(128, v3.getNetmask()); + ctx.assertEquals(addr2, v4.getAddress()); + ctx.assertEquals(56, v4.getNetmask()); + })); + })); + } + + @Test + public void testBinaryEncodeCIDR(TestContext ctx) throws Exception { + InetAddress addr1 = Inet4Address.getByName("128.0.0.0"); + InetAddress addr2 = Inet6Address.getByName("2001:0db8:1234:0000:0000:0000:0000:0000"); + PgConnection.connect(vertx, options).onComplete(ctx.asyncAssertSuccess(conn -> { + conn + .preparedQuery("SELECT ($1::CIDR)::VARCHAR, ($2::CIDR)::VARCHAR, ($3::CIDR)::VARCHAR, ($4::CIDR)::VARCHAR") + .execute(Tuple.of( + new Cidr().setAddress(addr1), + new Cidr().setAddress(addr1).setNetmask(4), + new Cidr().setAddress(addr2), + new Cidr().setAddress(addr2).setNetmask(56) + )) + .onComplete(ctx.asyncAssertSuccess(rows -> { + ctx.assertEquals(1, rows.size()); + Row row = rows.iterator().next(); + String v1 = row.getString(0); + String v2 = row.getString(1); + String v3 = row.getString(2); + String v4 = row.getString(3); + ctx.assertEquals("128.0.0.0/32", v1); + ctx.assertEquals("128.0.0.0/4", v2); + ctx.assertEquals("2001:db8:1234::/128", v3); + ctx.assertEquals("2001:db8:1234::/56", v4); + })); + })); + } + +} diff --git a/vertx-pg-client/src/test/java/io/vertx/pgclient/data/CidrTest.java b/vertx-pg-client/src/test/java/io/vertx/pgclient/data/CidrTest.java deleted file mode 100644 index 9de19664c..000000000 --- a/vertx-pg-client/src/test/java/io/vertx/pgclient/data/CidrTest.java +++ /dev/null @@ -1,48 +0,0 @@ -package io.vertx.pgclient.data; - -import org.junit.Test; - -import java.net.InetAddress; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertThrows; - -public class CidrTest extends DataTypeTestBase{ - - @Test - public void testValidIPv4() throws Exception { - InetAddress address = InetAddress.getByName("192.168.1.1"); - Cidr cidr = new Cidr(); - cidr.setAddress(address); - cidr.setNetmask(24); - assertEquals(address, cidr.getAddress()); - assertEquals(Integer.valueOf(24), cidr.getNetmask()); - } - - @Test - public void testValidIPv6() throws Exception { - InetAddress address = InetAddress.getByName("fe80::f03c:91ff:feae:e944"); - Cidr cidr = new Cidr(); - cidr.setAddress(address); - cidr.setNetmask(64); - assertEquals(address, cidr.getAddress()); - assertEquals(Integer.valueOf(64), cidr.getNetmask()); - } - - @Test - public void testInvalidNetmaskIPv4() throws Exception { - InetAddress address = InetAddress.getByName("192.168.1.1"); - Cidr cidr = new Cidr(); - cidr.setAddress(address); - assertThrows(IllegalArgumentException.class, () -> cidr.setNetmask(33)); - } - - @Test - public void testInvalidNetmaskIPv6() throws Exception { - InetAddress address = InetAddress.getByName("fe80::f03c:91ff:feae:e944"); - Cidr cidr = new Cidr(); - cidr.setAddress(address); - assertThrows(IllegalArgumentException.class, () -> cidr.setNetmask(129)); - } - -}