Repository navigation
Fix jdbc-v2: report the IPv4/IPv6 Java class in getColumnClassName - #3191
polyglotAI-bot wants to merge 1 commit into
Conversation
ResultSetMetaData#getColumnClassName returned java.lang.Object for IPv4 and IPv6 columns because JdbcUtils.DATA_TYPE_CLASS_MAP mapped both types to Object. IPv4 now maps to java.net.Inet4Address and IPv6 to java.net.InetAddress: the reader returns an Inet4Address for an IPv4-mapped IPv6 value, so InetAddress is the class that getObject actually returns. getObject returns the same values as before. ArrayResultSet.convertValue used an exact class match, so an array element could not be returned for a superclass target such as InetAddress. It now uses isInstance, the same as JdbcUtils.convert. Fixes: #3189 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
IMO an IPv6 column should report java.net.Inet6Address (and not InetAddress ) . If I save an IPv4 address in a IPv6 column I'm implicitly asking for a conversion ( like 10.5.2.1 -> ::ffff:10.5.2.1 ). Like trying to save a Short value in a Long column I expect to get a Long back when I do getObject. I do not think that the DB should "remember" what type of value I passed way after the storing of it is done. Also calling ResultSet.getObject(index, java.net.Inet6Address.class) and ResultSet.getObject(index, java.net.InetAddress.class) on an IPv6 column should both work, while ResultSet.getObject(index, java.net.Inet4Address.class) should fail. For the same reason calling ResultSet.getObject(index, java.net.Inet4Address.class) and ResultSet.getObject(index, java.net.InetAddress.class) on an IPv4 column should both work, while ResultSet.getObject(index, java.net.Inet6Address.class) should fail. |
|
Thank you for the review, @ventod. I checked each of your cases on this branch with a live server (26.9). The Query:
@chernser, which option do you want?
If you select B, I will update this PR. |
Description
Fixes #3189.
ResultSetMetaData#getColumnClassNamereturnedjava.lang.ObjectforIPv4andIPv6columns. The cause isJdbcUtils.DATA_TYPE_CLASS_MAP: it mapped both types toObject.class. That map is the single source of the column class for metadata and forgetObject().IPv4now maps tojava.net.Inet4Address. The reader always returns anInet4AddressforIPv4.IPv6now maps tojava.net.InetAddress, notInet6Address.BinaryStreamReaderreadsIPv6withInetAddress.getByAddress(16 bytes), which returns anInet4Addressfor an IPv4-mapped value (::ffff:a.b.c.d). The existingJdbcDataTypeTests.testIpAddressTypesrequiresgetObject()to return thatInet4Address. JDBC definesgetColumnClassNameas the class of the instances thatgetObject()returns, soInetAddressis the correct class. WithInet6Address,JdbcUtils.convertwould convert mapped values toInet6Address, andgetObject()would return different values than before.getColumnType()staysTypes.OTHER.ResultSet#getObject()returns the same values as before.Alternative (needs a decision): to report
Inet6AddressforIPv6, as the issue expects,getObject()must return anInet6Addressfor IPv4-mapped values. That changes the behavior of an existing value path. If you want that, I can change it.Changes
JdbcUtils.getDataTypeClassMap():IPv4→Inet4Address.class,IPv6→InetAddress.class.ArrayResultSet.convertValue(): the "no conversion needed" check was an exact class match (targetType == value.getClass()).ArrayResultSet.getObject(2)uses the class from the same map, so anArray(IPv6)element failed withValue of class java.net.Inet6Address cannot be converted to class java.net.InetAddress. The check is nowtargetType.isInstance(value), the same as inJdbcUtils.convert. NoValueConverterstarget is a superclass of a value class that gets here, so no converter that ran before is skipped.docs/features.md: new "IP address type mapping" bullet.history/latest/3189.md: changelog entry.Compatibility
getColumnClassNamereports a different value forIPv4/IPv6columns. That is the fix.java.sql.Array#getArray()forArray(IPv4)/Array(IPv6)now returns anInet4Address[]/InetAddress[], not anObject[], the same as other typed arrays (for exampleUUID[]). A cast toObject[]still works.getObject(int, Class)on the result set of ajava.sql.Arraynow also accepts a superclass or interface of the element value (for exampleNumber.classfor anInt32element). Before, it threw.createArrayOf("IPv4", new String[]{...}).getResultSet().getObject(2)now throws: there is noString→Inet4Addressconverter. Before, it returned theString. A typed array ofInetAddressvalues works.Test
ResultSetMetaDataImplTest.testGetColumnClassNameOfIpAddressTypes(integration,@DataProvider):IPv4,IPv6, theirNullableandLowCardinalityforms, and an IPv4-mappedIPv6value. For each row, the test checks the type name,Types.OTHER, and the class name, and thatgetObject()returns an instance of the reported class. The IPv4-mapped row is the contrast case:getObject()still returns anInet4Address. The test runs the same checks on theVALUEcolumn ofjava.sql.Array#getResultSet(). All 5 original rows failed onmainwithexpected [...] but found [java.lang.Object].ArrayResultSetTest.testInetAddressValues(unit):getObject(2),getObject(2, InetAddress.class), and anullelement. Negative case:getObject(2, Inet4Address.class)on a non-mapped IPv6 element still throwsSQLException. Fails without theArrayResultSetchange.jdbc-v2module: 1963 unit tests pass. 775 of 777 integration tests pass. The 2 failures areConnectionTest.testSSLModeVerifyCaandConnectionTest.testSecureConnection. These SSL tests need a TLS setup that my local environment does not have. They are not related to this change. No existing tests were edited.Pre-PR validation gate
main)docs/changes_checklist.md"Conditional logic or guard changed": theArrayResultSetguard now admits superclass targets. A negative test shows that unrelated targets still throw.docs/features.mdupdated (jdbc-v2 behavior)history/latest/3189.mdStatement#executeQuery)Not in this PR
Two related defects that I found while working on this. Neither is caused by this change, and they are tracked for separate investigation:
BinaryStreamReader.readArrayItemcreates anInet6Address[]forArray(Nullable(IPv6)), so an IPv4-mapped element is likely to fail on read.InetAddressConverter.convertToIpv4comparesbytes[10] != 0xFF(abytewith anint), so it rejects every IPv4-mappedInet6Address.🤖 Generated with Claude Code