Skip to content

Commit

Permalink
HHH-17246 Handle UUID specially within JSON
Browse files Browse the repository at this point in the history
  • Loading branch information
beikov committed Nov 13, 2024
1 parent a5fa3e4 commit 877eb95
Show file tree
Hide file tree
Showing 9 changed files with 69 additions and 68 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -1232,6 +1232,13 @@ private static Object fromString(
),
options
);
case SqlTypes.UUID:
return jdbcJavaType.wrap(
PrimitiveByteArrayJavaType.INSTANCE.fromString(
string.substring( start, end ).replace( "-", "" )
),
options
);
case SqlTypes.DATE:
return jdbcJavaType.wrap(
JdbcDateJavaType.INSTANCE.fromEncodedString(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,6 @@
import org.hibernate.type.JavaObjectType;
import org.hibernate.type.NullType;
import org.hibernate.type.StandardBasicTypes;
import org.hibernate.type.descriptor.java.OracleUUIDJavaType;
import org.hibernate.type.descriptor.java.PrimitiveByteArrayJavaType;
import org.hibernate.type.descriptor.jdbc.BlobJdbcType;
import org.hibernate.type.descriptor.jdbc.JdbcType;
Expand Down Expand Up @@ -1042,8 +1041,6 @@ public void contributeTypes(TypeContributions typeContributions, ServiceRegistry
)
);

typeContributions.contributeJavaType( OracleUUIDJavaType.INSTANCE );

if(getVersion().isSameOrAfter(23)) {
final JdbcTypeRegistry jdbcTypeRegistry = typeContributions.getTypeConfiguration().getJdbcTypeRegistry();
jdbcTypeRegistry.addDescriptor(OracleEnumJdbcType.INSTANCE);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import static org.hibernate.type.SqlTypes.TIMESTAMP_UTC;
import static org.hibernate.type.SqlTypes.TIMESTAMP_WITH_TIMEZONE;
import static org.hibernate.type.SqlTypes.TINYINT;
import static org.hibernate.type.SqlTypes.UUID;
import static org.hibernate.type.SqlTypes.VARBINARY;

/**
Expand Down Expand Up @@ -355,6 +356,8 @@ private String determineValueExpression(String expression, int elementSqlTypeCod
case VARBINARY:
case LONG32VARBINARY:
return "hextoraw(" + expression + ")";
case UUID:
return "hextoraw(replace(" + expression + ",'-',''))";
default:
return expression;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,12 +50,15 @@
import static org.hibernate.type.SqlTypes.TIMESTAMP;
import static org.hibernate.type.SqlTypes.TIMESTAMP_UTC;
import static org.hibernate.type.SqlTypes.TIMESTAMP_WITH_TIMEZONE;
import static org.hibernate.type.SqlTypes.UUID;
import static org.hibernate.type.SqlTypes.VARBINARY;

public class DB2AggregateSupport extends AggregateSupportImpl {

public static final AggregateSupport INSTANCE = new DB2AggregateSupport( false );
public static final AggregateSupport JSON_INSTANCE = new DB2AggregateSupport( true );
private static final String JSON_QUERY_START = "json_query(";
private static final String JSON_QUERY_JSON_END = "')";

private final boolean jsonSupport;

Expand All @@ -77,25 +80,32 @@ public String aggregateComponentCustomReadExpression(
if ( !jsonSupport ) {
break;
}
final String parentPartExpression;
if ( aggregateParentReadExpression.startsWith( JSON_QUERY_START ) && aggregateParentReadExpression.endsWith( JSON_QUERY_JSON_END ) ) {
parentPartExpression = aggregateParentReadExpression.substring( JSON_QUERY_START.length(), aggregateParentReadExpression.length() - JSON_QUERY_JSON_END.length() ) + ".";
}
else {
parentPartExpression = aggregateParentReadExpression + ",'$.";
}
switch ( column.getJdbcMapping().getJdbcType().getDefaultSqlTypeCode() ) {
case BOOLEAN:
if ( SqlTypes.isNumericType( column.getJdbcMapping().getJdbcType().getDdlTypeCode() ) ) {
return template.replace(
placeholder,
"decode(json_value(" + aggregateParentReadExpression + ",'$." + columnExpression + "'),'true',1,'false',0)"
"decode(json_value(" + parentPartExpression + columnExpression + "'),'true',1,'false',0)"
);
}
else {
return template.replace(
placeholder,
"decode(json_value(" + aggregateParentReadExpression + ",'$." + columnExpression + "'),'true',true,'false',false)"
"decode(json_value(" + parentPartExpression + columnExpression + "'),'true',true,'false',false)"
);
}
case TIMESTAMP_WITH_TIMEZONE:
case TIMESTAMP_UTC:
return template.replace(
placeholder,
"cast(trim(trailing 'Z' from json_value(" + aggregateParentReadExpression + ",'$." + columnExpression + "' returning varchar(35))) as " + column.getColumnDefinition() + ")"
"cast(trim(trailing 'Z' from json_value(" + parentPartExpression + columnExpression + "' returning varchar(35))) as " + column.getColumnDefinition() + ")"
);
case BINARY:
case VARBINARY:
Expand All @@ -104,18 +114,23 @@ public String aggregateComponentCustomReadExpression(
// We encode binary data as hex, so we have to decode here
return template.replace(
placeholder,
"hextoraw(json_value(" + aggregateParentReadExpression + ",'$." + columnExpression + "'))"
"hextoraw(json_value(" + parentPartExpression + columnExpression + "'))"
);
case UUID:
return template.replace(
placeholder,
"hextoraw(replace(json_value(" + parentPartExpression + columnExpression + "'),'-',''))"
);
case JSON:
case JSON_ARRAY:
return template.replace(
placeholder,
"json_query(" + aggregateParentReadExpression + ",'$." + columnExpression + "')"
"json_query(" + parentPartExpression + columnExpression + "')"
);
default:
return template.replace(
placeholder,
"json_value(" + aggregateParentReadExpression + ",'$." + columnExpression + "' returning " + column.getColumnDefinition() + ")"
"json_value(" + parentPartExpression + columnExpression + "' returning " + column.getColumnDefinition() + ")"
);
}
case STRUCT:
Expand All @@ -133,6 +148,8 @@ private static String jsonCustomWriteExpression(String customWriteExpression, Jd
case BLOB:
// We encode binary data as hex
return "hex(" + customWriteExpression + ")";
case UUID:
return "regexp_replace(lower(hex(" + customWriteExpression + ")),'^(.{8})(.{4})(.{4})(.{4})(.{12})$','$1-$2-$3-$4-$5')";
case ARRAY:
case JSON_ARRAY:
return "(" + customWriteExpression + ") format json";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
import static org.hibernate.type.SqlTypes.TIMESTAMP;
import static org.hibernate.type.SqlTypes.TIMESTAMP_UTC;
import static org.hibernate.type.SqlTypes.TINYINT;
import static org.hibernate.type.SqlTypes.UUID;
import static org.hibernate.type.SqlTypes.VARBINARY;
import static org.hibernate.type.SqlTypes.VARCHAR;

Expand Down Expand Up @@ -89,6 +90,14 @@ public String aggregateComponentCustomReadExpression(
placeholder,
"unhex(json_unquote(" + queryExpression( aggregateParentReadExpression, columnExpression ) + "))"
);
case UUID:
if ( column.getJdbcMapping().getJdbcType().isBinary() ) {
return template.replace(
placeholder,
"unhex(replace(json_unquote(" + queryExpression( aggregateParentReadExpression, columnExpression ) + "),'-',''))"
);
}
// Fall-through intended
default:
return template.replace(
placeholder,
Expand Down Expand Up @@ -148,16 +157,16 @@ private static String jsonCustomWriteExpression(String customWriteExpression, Jd
return "date_format(" + customWriteExpression + ",'%Y-%m-%dT%T.%f')";
case TIMESTAMP_UTC:
return "date_format(" + customWriteExpression + ",'%Y-%m-%dT%T.%fZ')";
case UUID:
if ( jdbcMapping.getJdbcType().isBinary() ) {
return "regexp_replace(lower(hex(" + customWriteExpression + ")),'^(.{8})(.{4})(.{4})(.{4})(.{12})$','$1-$2-$3-$4-$5')";
}
// Fall-through intended
default:
return customWriteExpression;
}
}

@Override
public int aggregateComponentSqlTypeCode(int aggregateColumnSqlTypeCode, int columnSqlTypeCode) {
return super.aggregateComponentSqlTypeCode( aggregateColumnSqlTypeCode, columnSqlTypeCode );
}

@Override
public String aggregateComponentAssignmentExpression(
String aggregateParentAssignmentExpression,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,11 @@ public String aggregateComponentCustomReadExpression(
placeholder,
"hextoraw(json_value(" + parentPartExpression + columnExpression + "'))"
);
case UUID:
return template.replace(
placeholder,
"hextoraw(replace(json_value(" + parentPartExpression + columnExpression + "'),'-',''))"
);
case CLOB:
case NCLOB:
case BLOB:
Expand Down Expand Up @@ -277,12 +282,16 @@ private String jsonCustomWriteExpression(
switch ( sqlTypeCode ) {
case CLOB:
return "to_clob(" + customWriteExpression + ")";
case UUID:
return "regexp_replace(lower(rawtohex(" + customWriteExpression + ")),'^(.{8})(.{4})(.{4})(.{4})(.{12})$','\\1-\\2-\\3-\\4-\\5')";
case ARRAY:
final BasicPluralType<?, ?> pluralType = (BasicPluralType<?, ?>) jdbcMapping;
final OracleArrayJdbcType jdbcType = (OracleArrayJdbcType) pluralType.getJdbcType();
switch ( jdbcType.getElementJdbcType().getDefaultSqlTypeCode() ) {
case CLOB:
return "(select json_arrayagg(to_clob(t.column_value)) from table(" + customWriteExpression + ") t)";
case UUID:
return "(select json_arrayagg(regexp_replace(lower(rawtohex(t.column_value)),'^(.{8})(.{4})(.{4})(.{4})(.{12})$','\\1-\\2-\\3-\\4-\\5')) from table(" + customWriteExpression + ") t)";
case BIT:
return "decode(" + customWriteExpression + ",1,'true',0,'false',null)";
case BOOLEAN:
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,17 @@
*/
package org.hibernate.orm.test.id.uuid;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import org.hibernate.annotations.JdbcType;
import jakarta.persistence.Table;
import org.hibernate.dialect.SybaseASEDialect;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.JiraKey;
import org.hibernate.testing.orm.junit.RequiresDialect;
import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.hibernate.type.descriptor.java.UUIDJavaType;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
Expand All @@ -29,13 +31,15 @@
@SessionFactory
public class SybaseASEUUIDTest {

private static final UUID uuid = UUID.fromString("53886a8a-7082-4879-b430-25cb94415b00");
private static final UUID THE_UUID = UUID.fromString("53886a8a-7082-4879-b430-25cb94415b00");

@BeforeEach
void setUp(SessionFactoryScope scope) {
scope.inTransaction( session -> {
final Book book = new Book(uuid, "John Doe");
session.persist( book );
session.createNativeQuery( "insert into book (id, author) values (?,?)" )
.setParameter( 1, UUIDJavaType.ToBytesTransformer.INSTANCE.transform( THE_UUID ) )
.setParameter( 2, "John Doe" )
.executeUpdate();
} );
}

Expand All @@ -49,22 +53,22 @@ void tearDown(SessionFactoryScope scope) {
@Test
@JiraKey( value = "HHH-17246" )
public void testTrailingZeroByteTruncation(SessionFactoryScope scope) {
scope.inSession(
session -> assertEquals( 15, session.createNativeQuery("select id from Book", byte[].class).getSingleResult().length )
);
scope.inTransaction(
session -> {
// Assert that our assumption is correct i.e. Sybase truncates trailing zero bytes
assertEquals( 15, session.createNativeQuery("select id from book", byte[].class).getSingleResult().length );
Book b = session.createQuery( "from Book", Book.class ).getSingleResult();
assertEquals(uuid, b.id);
assertEquals( THE_UUID, b.id );
}
);
}

@Entity(name = "Book")
@Table(name = "book")
static class Book {
@Id
// The purpose is to effectively provoke the trailing 0 bytes truncation
@JdbcType( SybaseUuidAsVarbinaryJdbcType.class )
@Column(columnDefinition = "varbinary(16)")
UUID id;

String author;
Expand Down

This file was deleted.

0 comments on commit 877eb95

Please sign in to comment.