Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import java.math.BigInteger;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.time.Duration;
import java.time.Instant;
import java.time.LocalDate;
Expand Down Expand Up @@ -486,12 +487,12 @@ public TemporalAmount getTemporalAmount(String colName) {

@Override
public Inet4Address getInet4Address(String colName) {
return readValue(colName);
return InetAddressConverter.convertToIpv4(readValue(colName));
}

@Override
public Inet6Address getInet6Address(String colName) {
return readValue(colName);
return InetAddressConverter.convertToIpv6(readValue(colName));
}

@Override
Expand Down Expand Up @@ -651,12 +652,12 @@ public TemporalAmount getTemporalAmount(int index) {

@Override
public Inet4Address getInet4Address(int index) {
return readValue(index);
return InetAddressConverter.convertToIpv4(readValue(index));
}

@Override
public Inet6Address getInet6Address(int index) {
return readValue(index);
return InetAddressConverter.convertToIpv6(readValue(index));
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package com.clickhouse.client.api.data_formats.internal;

import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.UnknownHostException;

public class InetAddressConverter {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

According to the project guidelines, tests are required for new functionality. Please include unit tests for this converter class to verify both conversion methods work correctly with various edge cases (null values, valid IPv4/IPv6 addresses, invalid conversions, etc.).


/**
* Converts IPv4 address to IPv6 address.
*
* @param value IPv4 address
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Javadoc for convertToIpv6 method should be updated to clarify that it accepts any InetAddress (including null and IPv6 addresses), not just IPv4 addresses as currently stated.

* @return IPv6 address
* @throws IllegalArgumentException when failed to convert to IPv6 address
*/
public static Inet6Address convertToIpv6(InetAddress value) {
if (value == null || value instanceof Inet6Address) {
return (Inet6Address) value;
}

// https://en.wikipedia.org/wiki/IPv6#IPv4-mapped_IPv6_addresses
byte[] bytes = new byte[16];
bytes[10] = (byte) 0xFF;
bytes[11] = (byte) 0xFF;
System.arraycopy(value.getAddress(), 0, bytes, 12, 4);

try {
return Inet6Address.getByAddress(null, bytes, null);
} catch (UnknownHostException e) {
throw new IllegalArgumentException(e);
}
}

/**
* Converts IPv6 address to IPv4 address if applicable.
*
* @param value IPv6 address
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Javadoc for convertToIpv4 method should be updated to clarify that it accepts any InetAddress (including null and IPv4 addresses), not just IPv6 addresses as currently stated.

* @return IPv4 address
* @throws IllegalArgumentException when failed to convert to IPv4 address
*/
public static Inet4Address convertToIpv4(InetAddress value) {
if (value == null || value instanceof Inet4Address) {
return (Inet4Address) value;
}

byte[] bytes = value.getAddress();
boolean invalid = false;
for (int i = 0; i < 10; i++) {
if (bytes[i] != (byte) 0) {
invalid = true;
break;
}
}

if (!invalid) {
invalid = bytes[10] != 0xFF || bytes[11] != 0xFF;
}

if (invalid) {
throw new IllegalArgumentException("Failed to convert IPv6 to IPv4");
}

byte[] addr = new byte[4];
System.arraycopy(bytes, 12, addr, 0, 4);
try {
return (Inet4Address) InetAddress.getByAddress(addr);
} catch (UnknownHostException e) {
throw new IllegalArgumentException(e);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -162,12 +162,12 @@ public TemporalAmount getTemporalAmount(String colName) {

@Override
public Inet4Address getInet4Address(String colName) {
return readValue(colName);
return InetAddressConverter.convertToIpv4(readValue(colName));
}

@Override
public Inet6Address getInet6Address(String colName) {
return readValue(colName);
return InetAddressConverter.convertToIpv6(readValue(colName));
}

@Override
Expand Down Expand Up @@ -323,12 +323,12 @@ public TemporalAmount getTemporalAmount(int index) {

@Override
public Inet4Address getInet4Address(int index) {
return readValue(index);
return InetAddressConverter.convertToIpv4(readValue(index));
}

@Override
public Inet6Address getInet6Address(int index) {
return readValue(index);
return InetAddressConverter.convertToIpv6(readValue(index));
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@
import java.math.BigInteger;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.sql.Timestamp;
import java.time.*;
import java.time.temporal.TemporalUnit;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -781,7 +781,34 @@ public void testIPAddresses() throws Exception {
testDataTypes(columns, valueGenerators, verifiers);
}

@Test
@Test(groups = {"integration"})
public void testConversionOfIpAddresses() throws Exception {

try (QueryResponse response = client.query("SELECT toIPv6('::ffff:90.176.75.97') ipv4, toIPv6('2001:db8:85a3::8a2e:370:7334') ipv6").get()) {
ClickHouseBinaryFormatReader reader = client.newBinaryFormatReader(response);

reader.next();
InetAddress ipv4 = reader.getInet4Address(1);
Assert.assertNotNull(ipv4);
InetAddress ipv6 = reader.getInet6Address(2);
Assert.assertNotNull(ipv6);
InetAddress ipv4_as_ipv6 = reader.getInet6Address(1);
Assert.assertEquals(Inet4Address.getByAddress(ipv4_as_ipv6.getAddress()), ipv4);
Assert.assertThrows(() -> reader.getInet4Address(2));
}

List<GenericRecord> records = client.queryAll("SELECT toIPv6('::ffff:90.176.75.97') ipv4, toIPv6('2001:db8:85a3::8a2e:370:7334') ipv6");
GenericRecord record = records.get(0);
InetAddress ipv4 = record.getInet4Address(1);
Assert.assertNotNull(ipv4);
InetAddress ipv6 = record.getInet6Address(2);
Assert.assertNotNull(ipv6);
InetAddress ipv4_as_ipv6 = record.getInet6Address(1);
Assert.assertEquals(Inet4Address.getByAddress(ipv4_as_ipv6.getAddress()), ipv4);
Assert.assertThrows(() -> record.getInet4Address(2));
}

@Test(groups = {"integration"})
public void testDateTimeDataTypes() {
final List<String> columns = Arrays.asList(
"min_date Date",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@
import java.time.OffsetDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.Map;
Expand Down Expand Up @@ -519,7 +518,7 @@ public void testStringTypes() throws SQLException {
@Test(groups = { "integration" })
public void testIpAddressTypes() throws SQLException, UnknownHostException {
runQuery("CREATE TABLE test_ips (order Int8, "
+ "ipv4_ip IPv4, ipv4_name IPv4, ipv6 IPv6"
+ "ipv4_ip IPv4, ipv4_name IPv4, ipv6 IPv6, ipv4_as_ipv6 IPv6"
+ ") ENGINE = MergeTree ORDER BY ()");

// Insert random (valid) values
Expand All @@ -529,12 +528,14 @@ public void testIpAddressTypes() throws SQLException, UnknownHostException {
InetAddress ipv4AddressByIp = Inet4Address.getByName(rand.nextInt(256) + "." + rand.nextInt(256) + "." + rand.nextInt(256) + "." + rand.nextInt(256));
InetAddress ipv4AddressByName = Inet4Address.getByName("www.example.com");
InetAddress ipv6Address = Inet6Address.getByName("2001:adb8:85a3:1:2:8a2e:370:7334");
InetAddress ipv4AsIpv6 = Inet4Address.getByName("90.176.75.97");

try (Connection conn = getConnection()) {
try (PreparedStatement stmt = conn.prepareStatement("INSERT INTO test_ips VALUES ( 1, ?, ?, ? )")) {
try (PreparedStatement stmt = conn.prepareStatement("INSERT INTO test_ips VALUES ( 1, ?, ?, ?, ? )")) {
stmt.setObject(1, ipv4AddressByIp);
stmt.setObject(2, ipv4AddressByName);
stmt.setObject(3, ipv6Address);
stmt.setObject(4, ipv4AsIpv6);
stmt.executeUpdate();
}
}
Expand All @@ -549,6 +550,7 @@ public void testIpAddressTypes() throws SQLException, UnknownHostException {
assertEquals(rs.getObject("ipv4_name"), ipv4AddressByName);
assertEquals(rs.getObject("ipv6"), ipv6Address);
assertEquals(rs.getString("ipv6"), ipv6Address.toString());
assertEquals(rs.getObject("ipv4_as_ipv6"), ipv4AsIpv6);
assertFalse(rs.next());
}
}
Expand Down
Loading