From 3d250868c68c5c0df2ea8f72bc9163acf2c7eac8 Mon Sep 17 00:00:00 2001 From: dmitrybugakov Date: Sun, 8 Oct 2023 19:46:26 +0200 Subject: [PATCH] [FEATURE]: Add IPv6 type. #430 --- .../housepower/data/DataTypeFactory.java | 2 + .../housepower/data/type/DataTypeIPv6.java | 90 +++++++++++++++++++ .../housepower/jdbc/ClickHouseResultSet.java | 16 ++++ .../jdbc/{ => type}/BooleanTypeITest.java | 3 +- .../jdbc/{ => type}/DecimalTypeITest.java | 4 +- .../housepower/jdbc/type/IPv4TypeITest.java | 77 ++++++++++++++++ .../housepower/jdbc/type/IPv6TypeITest.java | 67 ++++++++++++++ .../{ => type}/LowCardinalityTypeTest.java | 3 +- 8 files changed, 259 insertions(+), 3 deletions(-) create mode 100644 clickhouse-native-jdbc/src/main/java/com/github/housepower/data/type/DataTypeIPv6.java rename clickhouse-native-jdbc/src/test/java/com/github/housepower/jdbc/{ => type}/BooleanTypeITest.java (95%) rename clickhouse-native-jdbc/src/test/java/com/github/housepower/jdbc/{ => type}/DecimalTypeITest.java (97%) create mode 100644 clickhouse-native-jdbc/src/test/java/com/github/housepower/jdbc/type/IPv4TypeITest.java create mode 100644 clickhouse-native-jdbc/src/test/java/com/github/housepower/jdbc/type/IPv6TypeITest.java rename clickhouse-native-jdbc/src/test/java/com/github/housepower/jdbc/{ => type}/LowCardinalityTypeTest.java (98%) diff --git a/clickhouse-native-jdbc/src/main/java/com/github/housepower/data/DataTypeFactory.java b/clickhouse-native-jdbc/src/main/java/com/github/housepower/data/DataTypeFactory.java index 4f3d6f1d..817ccbd0 100644 --- a/clickhouse-native-jdbc/src/main/java/com/github/housepower/data/DataTypeFactory.java +++ b/clickhouse-native-jdbc/src/main/java/com/github/housepower/data/DataTypeFactory.java @@ -25,6 +25,7 @@ import com.github.housepower.data.type.DataTypeFloat32; import com.github.housepower.data.type.DataTypeFloat64; import com.github.housepower.data.type.DataTypeIPv4; +import com.github.housepower.data.type.DataTypeIPv6; import com.github.housepower.data.type.DataTypeInt16; import com.github.housepower.data.type.DataTypeInt32; import com.github.housepower.data.type.DataTypeInt64; @@ -103,6 +104,7 @@ public class DataTypeFactory { Map> creators = new HashMap<>(); registerType(creators, new DataTypeIPv4()); + registerType(creators, new DataTypeIPv6()); registerType(creators, new DataTypeUUID()); registerType(creators, new DataTypeFloat32()); registerType(creators, new DataTypeFloat64()); diff --git a/clickhouse-native-jdbc/src/main/java/com/github/housepower/data/type/DataTypeIPv6.java b/clickhouse-native-jdbc/src/main/java/com/github/housepower/data/type/DataTypeIPv6.java new file mode 100644 index 00000000..58becbf3 --- /dev/null +++ b/clickhouse-native-jdbc/src/main/java/com/github/housepower/data/type/DataTypeIPv6.java @@ -0,0 +1,90 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.github.housepower.data.type; + +import com.github.housepower.data.IDataType; +import com.github.housepower.misc.SQLLexer; +import com.github.housepower.serde.BinaryDeserializer; +import com.github.housepower.serde.BinarySerializer; + +import java.io.IOException; +import java.math.BigInteger; +import java.sql.SQLException; +import java.sql.Types; + +public class DataTypeIPv6 implements IDataType { + + @Override + public String name() { + return "IPv6"; + } + + @Override + public int sqlTypeId() { + return Types.BIGINT; + } + + @Override + public BigInteger defaultValue() { + return BigInteger.ZERO; + } + + @Override + public Class javaType() { + return BigInteger.class; + } + + @Override + public int getPrecision() { + return 0; + } + + @Override + public int getScale() { + return 39; + } + + @Override + public void serializeBinary(BigInteger data, BinarySerializer serializer) throws SQLException, IOException { + byte[] bytes = data.toByteArray(); + if (bytes.length > 16) { + throw new SQLException("IPv6 representation exceeds 16 bytes."); + } + byte[] paddedBytes = new byte[16]; + int offset = 16 - bytes.length; + System.arraycopy(bytes, 0, paddedBytes, offset, bytes.length); + serializer.writeBytes(paddedBytes, 0, paddedBytes.length); + } + + @Override + public BigInteger deserializeBinary(BinaryDeserializer deserializer) throws SQLException, IOException { + byte[] bytes = deserializer.readBytes(16); + return new BigInteger(1, bytes); // force it to be positive + } + @Override + public String[] getAliases() { + return new String[0]; + } + + @Override + public BigInteger deserializeText(SQLLexer lexer) throws SQLException { + String ipv6String = convertIPv6ToHexadecimalString(lexer.stringLiteral()); + return new BigInteger(ipv6String, 16); + } + + private static String convertIPv6ToHexadecimalString(String ipv6) { + return ipv6.replace(":", ""); + } +} diff --git a/clickhouse-native-jdbc/src/main/java/com/github/housepower/jdbc/ClickHouseResultSet.java b/clickhouse-native-jdbc/src/main/java/com/github/housepower/jdbc/ClickHouseResultSet.java index ba957f52..66237632 100644 --- a/clickhouse-native-jdbc/src/main/java/com/github/housepower/jdbc/ClickHouseResultSet.java +++ b/clickhouse-native-jdbc/src/main/java/com/github/housepower/jdbc/ClickHouseResultSet.java @@ -28,6 +28,7 @@ import com.github.housepower.settings.ClickHouseConfig; import java.math.BigDecimal; +import java.math.BigInteger; import java.net.MalformedURLException; import java.net.URL; import java.sql.*; @@ -143,6 +144,21 @@ public URL getURL(String name) throws SQLException { return this.getURL(this.findColumn(name)); } + public BigInteger getBigInteger(String columnName) throws SQLException { + return getBigInteger(findColumn(columnName)); + } + + public BigInteger getBigInteger(int columnIndex) throws SQLException { + Object valueObj = getObject(columnIndex); + if (valueObj instanceof BigInteger) { + return (BigInteger) valueObj; + } + if (wasNull()) { + return null; + } + throw new SQLException("Column " + columnIndex + " is not of type BigInteger."); + } + @Override public Array getArray(String name) throws SQLException { return this.getArray(this.findColumn(name)); diff --git a/clickhouse-native-jdbc/src/test/java/com/github/housepower/jdbc/BooleanTypeITest.java b/clickhouse-native-jdbc/src/test/java/com/github/housepower/jdbc/type/BooleanTypeITest.java similarity index 95% rename from clickhouse-native-jdbc/src/test/java/com/github/housepower/jdbc/BooleanTypeITest.java rename to clickhouse-native-jdbc/src/test/java/com/github/housepower/jdbc/type/BooleanTypeITest.java index d4dce9ea..4d6b8c41 100644 --- a/clickhouse-native-jdbc/src/test/java/com/github/housepower/jdbc/BooleanTypeITest.java +++ b/clickhouse-native-jdbc/src/test/java/com/github/housepower/jdbc/type/BooleanTypeITest.java @@ -12,8 +12,9 @@ * limitations under the License. */ -package com.github.housepower.jdbc; +package com.github.housepower.jdbc.type; +import com.github.housepower.jdbc.AbstractITest; import com.github.housepower.misc.BytesHelper; import org.junit.jupiter.api.Test; diff --git a/clickhouse-native-jdbc/src/test/java/com/github/housepower/jdbc/DecimalTypeITest.java b/clickhouse-native-jdbc/src/test/java/com/github/housepower/jdbc/type/DecimalTypeITest.java similarity index 97% rename from clickhouse-native-jdbc/src/test/java/com/github/housepower/jdbc/DecimalTypeITest.java rename to clickhouse-native-jdbc/src/test/java/com/github/housepower/jdbc/type/DecimalTypeITest.java index accda7d1..25fa9084 100644 --- a/clickhouse-native-jdbc/src/test/java/com/github/housepower/jdbc/DecimalTypeITest.java +++ b/clickhouse-native-jdbc/src/test/java/com/github/housepower/jdbc/type/DecimalTypeITest.java @@ -12,8 +12,10 @@ * limitations under the License. */ -package com.github.housepower.jdbc; +package com.github.housepower.jdbc.type; +import com.github.housepower.jdbc.AbstractITest; +import com.github.housepower.jdbc.ClickHouseArray; import com.github.housepower.misc.BytesHelper; import com.google.common.base.Strings; import org.junit.jupiter.api.Test; diff --git a/clickhouse-native-jdbc/src/test/java/com/github/housepower/jdbc/type/IPv4TypeITest.java b/clickhouse-native-jdbc/src/test/java/com/github/housepower/jdbc/type/IPv4TypeITest.java new file mode 100644 index 00000000..b7048083 --- /dev/null +++ b/clickhouse-native-jdbc/src/test/java/com/github/housepower/jdbc/type/IPv4TypeITest.java @@ -0,0 +1,77 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.github.housepower.jdbc.type; + +import com.github.housepower.jdbc.AbstractITest; +import com.github.housepower.misc.BytesHelper; +import org.junit.jupiter.api.Test; + +import java.sql.PreparedStatement; +import java.sql.ResultSet; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class IPv4TypeITest extends AbstractITest implements BytesHelper { + + @Test + public void testIPv4Type() throws Exception { + withStatement(statement -> { + statement.execute("DROP TABLE IF EXISTS ipv4_test"); + statement.execute("CREATE TABLE IF NOT EXISTS ipv4_test (value IPv4, nullableValue Nullable(IPv4)) Engine=Memory()"); + + Integer rowCnt = 300; + Long testIPv4Value1 = ipToLong("192.168.1.1"); + Long testIPv4Value2 = ipToLong("127.0.0.1"); + + try (PreparedStatement pstmt = statement.getConnection().prepareStatement( + "INSERT INTO ipv4_test (value, nullableValue) values(?, ?);")) { + for (int i = 0; i < rowCnt; i++) { + pstmt.setLong(1, testIPv4Value1); + pstmt.setLong(2, testIPv4Value2); + pstmt.addBatch(); + } + pstmt.executeBatch(); + } + + ResultSet rs = statement.executeQuery("SELECT * FROM ipv4_test;"); + int size = 0; + while (rs.next()) { + size++; + Long value = rs.getLong(1); + assertEquals(value, testIPv4Value1); + Long nullableValue = rs.getLong(2); + assertEquals(nullableValue, testIPv4Value2); + } + + assertEquals(size, (int) rowCnt); + + statement.execute("DROP TABLE IF EXISTS ipv4_test"); + }); + } + + public long ipToLong(String ipAddress) { + String[] ipAddressInArray = ipAddress.split("\\."); + + long result = 0; + for (int i = 0; i < ipAddressInArray.length; i++) { + int power = 3 - i; + int ip = Integer.parseInt(ipAddressInArray[i]); + result += ip * Math.pow(256, power); + } + + return result; + } + +} diff --git a/clickhouse-native-jdbc/src/test/java/com/github/housepower/jdbc/type/IPv6TypeITest.java b/clickhouse-native-jdbc/src/test/java/com/github/housepower/jdbc/type/IPv6TypeITest.java new file mode 100644 index 00000000..1ff32777 --- /dev/null +++ b/clickhouse-native-jdbc/src/test/java/com/github/housepower/jdbc/type/IPv6TypeITest.java @@ -0,0 +1,67 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.github.housepower.jdbc.type; + +import com.github.housepower.jdbc.AbstractITest; +import com.github.housepower.jdbc.ClickHouseResultSet; +import com.github.housepower.misc.BytesHelper; +import org.junit.jupiter.api.Test; + +import java.math.BigInteger; +import java.sql.PreparedStatement; +import java.sql.Types; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class IPv6TypeITest extends AbstractITest implements BytesHelper { + + @Test + public void testIPv6Type() throws Exception { + withStatement(statement -> { + statement.execute("DROP TABLE IF EXISTS ipv6_test"); + statement.execute("CREATE TABLE IF NOT EXISTS ipv6_test (value IPv6, nullableValue Nullable(IPv6)) Engine=Memory()"); + + Integer rowCnt = 300; + + BigInteger testIPv6Value1 = new BigInteger("20010db885a3000000008a2e03707334", 16); + BigInteger testIPv6Value2 = new BigInteger("1", 16); + + try (PreparedStatement pstmt = statement.getConnection().prepareStatement( + "INSERT INTO ipv6_test (value, nullableValue) values(?, ?);")) { + for (int i = 0; i < rowCnt; i++) { + pstmt.setObject(1, testIPv6Value1, Types.BIGINT); + pstmt.setObject(2, testIPv6Value2, Types.BIGINT); + pstmt.addBatch(); + } + pstmt.executeBatch(); + } + + ClickHouseResultSet rs = (ClickHouseResultSet) statement.executeQuery("SELECT * FROM ipv6_test;"); + int size = 0; + while (rs.next()) { + size++; + BigInteger value = rs.getBigInteger(1); + assertEquals(value, testIPv6Value1); + BigInteger nullableValue = rs.getBigInteger(2); + assertEquals(nullableValue, testIPv6Value2); + } + + assertEquals(size, (int) rowCnt); + + statement.execute("DROP TABLE IF EXISTS ipv6_test"); + }); + } + +} diff --git a/clickhouse-native-jdbc/src/test/java/com/github/housepower/jdbc/LowCardinalityTypeTest.java b/clickhouse-native-jdbc/src/test/java/com/github/housepower/jdbc/type/LowCardinalityTypeTest.java similarity index 98% rename from clickhouse-native-jdbc/src/test/java/com/github/housepower/jdbc/LowCardinalityTypeTest.java rename to clickhouse-native-jdbc/src/test/java/com/github/housepower/jdbc/type/LowCardinalityTypeTest.java index b36aa805..e9cff35c 100644 --- a/clickhouse-native-jdbc/src/test/java/com/github/housepower/jdbc/LowCardinalityTypeTest.java +++ b/clickhouse-native-jdbc/src/test/java/com/github/housepower/jdbc/type/LowCardinalityTypeTest.java @@ -12,8 +12,9 @@ * limitations under the License. */ -package com.github.housepower.jdbc; +package com.github.housepower.jdbc.type; +import com.github.housepower.jdbc.AbstractITest; import com.github.housepower.misc.BytesHelper; import org.junit.jupiter.api.Test;