HHH-18803 Add XML aggregate support for DB2
This commit is contained in:
parent
d39aa6d162
commit
1077b6f0a9
|
@ -63,7 +63,9 @@ import org.hibernate.exception.spi.SQLExceptionConversionDelegate;
|
||||||
import org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor;
|
import org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor;
|
||||||
import org.hibernate.exception.spi.ViolatedConstraintNameExtractor;
|
import org.hibernate.exception.spi.ViolatedConstraintNameExtractor;
|
||||||
import org.hibernate.internal.util.JdbcExceptionHelper;
|
import org.hibernate.internal.util.JdbcExceptionHelper;
|
||||||
|
import org.hibernate.mapping.AggregateColumn;
|
||||||
import org.hibernate.mapping.Column;
|
import org.hibernate.mapping.Column;
|
||||||
|
import org.hibernate.mapping.Table;
|
||||||
import org.hibernate.metamodel.mapping.EntityMappingType;
|
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||||
import org.hibernate.metamodel.spi.RuntimeModelCreationContext;
|
import org.hibernate.metamodel.spi.RuntimeModelCreationContext;
|
||||||
import org.hibernate.procedure.internal.DB2CallableStatementSupport;
|
import org.hibernate.procedure.internal.DB2CallableStatementSupport;
|
||||||
|
@ -87,6 +89,8 @@ import org.hibernate.sql.exec.spi.JdbcOperation;
|
||||||
import org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorDB2DatabaseImpl;
|
import org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorDB2DatabaseImpl;
|
||||||
import org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorNoOpImpl;
|
import org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorNoOpImpl;
|
||||||
import org.hibernate.tool.schema.extract.spi.SequenceInformationExtractor;
|
import org.hibernate.tool.schema.extract.spi.SequenceInformationExtractor;
|
||||||
|
import org.hibernate.tool.schema.internal.StandardTableExporter;
|
||||||
|
import org.hibernate.tool.schema.spi.Exporter;
|
||||||
import org.hibernate.type.JavaObjectType;
|
import org.hibernate.type.JavaObjectType;
|
||||||
import org.hibernate.type.SqlTypes;
|
import org.hibernate.type.SqlTypes;
|
||||||
import org.hibernate.type.StandardBasicTypes;
|
import org.hibernate.type.StandardBasicTypes;
|
||||||
|
@ -96,6 +100,7 @@ import org.hibernate.type.descriptor.java.PrimitiveByteArrayJavaType;
|
||||||
import org.hibernate.type.descriptor.jdbc.CharJdbcType;
|
import org.hibernate.type.descriptor.jdbc.CharJdbcType;
|
||||||
import org.hibernate.type.descriptor.jdbc.ClobJdbcType;
|
import org.hibernate.type.descriptor.jdbc.ClobJdbcType;
|
||||||
import org.hibernate.type.descriptor.jdbc.InstantJdbcType;
|
import org.hibernate.type.descriptor.jdbc.InstantJdbcType;
|
||||||
|
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
||||||
import org.hibernate.type.descriptor.jdbc.LocalDateJdbcType;
|
import org.hibernate.type.descriptor.jdbc.LocalDateJdbcType;
|
||||||
import org.hibernate.type.descriptor.jdbc.LocalDateTimeJdbcType;
|
import org.hibernate.type.descriptor.jdbc.LocalDateTimeJdbcType;
|
||||||
import org.hibernate.type.descriptor.jdbc.LocalTimeJdbcType;
|
import org.hibernate.type.descriptor.jdbc.LocalTimeJdbcType;
|
||||||
|
@ -154,6 +159,17 @@ public class DB2LegacyDialect extends Dialect {
|
||||||
? LegacyDB2LimitHandler.INSTANCE
|
? LegacyDB2LimitHandler.INSTANCE
|
||||||
: DB2LimitHandler.INSTANCE;
|
: DB2LimitHandler.INSTANCE;
|
||||||
private final UniqueDelegate uniqueDelegate = createUniqueDelegate();
|
private final UniqueDelegate uniqueDelegate = createUniqueDelegate();
|
||||||
|
private final StandardTableExporter db2TableExporter = new StandardTableExporter( this ) {
|
||||||
|
@Override
|
||||||
|
protected void applyAggregateColumnCheck(StringBuilder buf, AggregateColumn aggregateColumn) {
|
||||||
|
final JdbcType jdbcType = aggregateColumn.getType().getJdbcType();
|
||||||
|
if ( jdbcType.isLob() || jdbcType.isXml() ) {
|
||||||
|
// LOB or XML columns can't have check constraints
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
super.applyAggregateColumnCheck( buf, aggregateColumn );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
public DB2LegacyDialect() {
|
public DB2LegacyDialect() {
|
||||||
this( DatabaseVersion.make( 9, 0 ) );
|
this( DatabaseVersion.make( 9, 0 ) );
|
||||||
|
@ -189,6 +205,11 @@ public class DB2LegacyDialect extends Dialect {
|
||||||
return this.getVersion();
|
return this.getVersion();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Exporter<Table> getTableExporter() {
|
||||||
|
return this.db2TableExporter;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getDefaultStatementBatchSize() {
|
public int getDefaultStatementBatchSize() {
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -53,7 +53,9 @@ import org.hibernate.exception.LockTimeoutException;
|
||||||
import org.hibernate.exception.spi.SQLExceptionConversionDelegate;
|
import org.hibernate.exception.spi.SQLExceptionConversionDelegate;
|
||||||
import org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor;
|
import org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor;
|
||||||
import org.hibernate.exception.spi.ViolatedConstraintNameExtractor;
|
import org.hibernate.exception.spi.ViolatedConstraintNameExtractor;
|
||||||
|
import org.hibernate.mapping.AggregateColumn;
|
||||||
import org.hibernate.mapping.Column;
|
import org.hibernate.mapping.Column;
|
||||||
|
import org.hibernate.mapping.Table;
|
||||||
import org.hibernate.metamodel.mapping.EntityMappingType;
|
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||||
import org.hibernate.metamodel.spi.RuntimeModelCreationContext;
|
import org.hibernate.metamodel.spi.RuntimeModelCreationContext;
|
||||||
import org.hibernate.procedure.internal.DB2CallableStatementSupport;
|
import org.hibernate.procedure.internal.DB2CallableStatementSupport;
|
||||||
|
@ -77,6 +79,8 @@ import org.hibernate.sql.exec.spi.JdbcOperation;
|
||||||
import org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorDB2DatabaseImpl;
|
import org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorDB2DatabaseImpl;
|
||||||
import org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorNoOpImpl;
|
import org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorNoOpImpl;
|
||||||
import org.hibernate.tool.schema.extract.spi.SequenceInformationExtractor;
|
import org.hibernate.tool.schema.extract.spi.SequenceInformationExtractor;
|
||||||
|
import org.hibernate.tool.schema.internal.StandardTableExporter;
|
||||||
|
import org.hibernate.tool.schema.spi.Exporter;
|
||||||
import org.hibernate.type.JavaObjectType;
|
import org.hibernate.type.JavaObjectType;
|
||||||
import org.hibernate.type.SqlTypes;
|
import org.hibernate.type.SqlTypes;
|
||||||
import org.hibernate.type.StandardBasicTypes;
|
import org.hibernate.type.StandardBasicTypes;
|
||||||
|
@ -84,6 +88,7 @@ import org.hibernate.type.descriptor.ValueExtractor;
|
||||||
import org.hibernate.type.descriptor.java.JavaType;
|
import org.hibernate.type.descriptor.java.JavaType;
|
||||||
import org.hibernate.type.descriptor.java.PrimitiveByteArrayJavaType;
|
import org.hibernate.type.descriptor.java.PrimitiveByteArrayJavaType;
|
||||||
import org.hibernate.type.descriptor.jdbc.InstantJdbcType;
|
import org.hibernate.type.descriptor.jdbc.InstantJdbcType;
|
||||||
|
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
||||||
import org.hibernate.type.descriptor.jdbc.LocalDateJdbcType;
|
import org.hibernate.type.descriptor.jdbc.LocalDateJdbcType;
|
||||||
import org.hibernate.type.descriptor.jdbc.LocalDateTimeJdbcType;
|
import org.hibernate.type.descriptor.jdbc.LocalDateTimeJdbcType;
|
||||||
import org.hibernate.type.descriptor.jdbc.LocalTimeJdbcType;
|
import org.hibernate.type.descriptor.jdbc.LocalTimeJdbcType;
|
||||||
|
@ -146,6 +151,17 @@ public class DB2Dialect extends Dialect {
|
||||||
? LegacyDB2LimitHandler.INSTANCE
|
? LegacyDB2LimitHandler.INSTANCE
|
||||||
: DB2LimitHandler.INSTANCE;
|
: DB2LimitHandler.INSTANCE;
|
||||||
private final UniqueDelegate uniqueDelegate = createUniqueDelegate();
|
private final UniqueDelegate uniqueDelegate = createUniqueDelegate();
|
||||||
|
private final StandardTableExporter db2TableExporter = new StandardTableExporter( this ) {
|
||||||
|
@Override
|
||||||
|
protected void applyAggregateColumnCheck(StringBuilder buf, AggregateColumn aggregateColumn) {
|
||||||
|
final JdbcType jdbcType = aggregateColumn.getType().getJdbcType();
|
||||||
|
if ( jdbcType.isLob() || jdbcType.isXml() ) {
|
||||||
|
// LOB or XML columns can't have check constraints
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
super.applyAggregateColumnCheck( buf, aggregateColumn );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
public DB2Dialect() {
|
public DB2Dialect() {
|
||||||
this( MINIMUM_VERSION );
|
this( MINIMUM_VERSION );
|
||||||
|
@ -171,6 +187,11 @@ public class DB2Dialect extends Dialect {
|
||||||
return this.getVersion();
|
return this.getVersion();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Exporter<Table> getTableExporter() {
|
||||||
|
return this.db2TableExporter;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getDefaultStatementBatchSize() {
|
public int getDefaultStatementBatchSize() {
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -45,6 +45,7 @@ import static org.hibernate.type.SqlTypes.JSON;
|
||||||
import static org.hibernate.type.SqlTypes.JSON_ARRAY;
|
import static org.hibernate.type.SqlTypes.JSON_ARRAY;
|
||||||
import static org.hibernate.type.SqlTypes.LONG32VARBINARY;
|
import static org.hibernate.type.SqlTypes.LONG32VARBINARY;
|
||||||
import static org.hibernate.type.SqlTypes.SMALLINT;
|
import static org.hibernate.type.SqlTypes.SMALLINT;
|
||||||
|
import static org.hibernate.type.SqlTypes.SQLXML;
|
||||||
import static org.hibernate.type.SqlTypes.STRUCT;
|
import static org.hibernate.type.SqlTypes.STRUCT;
|
||||||
import static org.hibernate.type.SqlTypes.TIME;
|
import static org.hibernate.type.SqlTypes.TIME;
|
||||||
import static org.hibernate.type.SqlTypes.TIMESTAMP;
|
import static org.hibernate.type.SqlTypes.TIMESTAMP;
|
||||||
|
@ -52,6 +53,7 @@ import static org.hibernate.type.SqlTypes.TIMESTAMP_UTC;
|
||||||
import static org.hibernate.type.SqlTypes.TIMESTAMP_WITH_TIMEZONE;
|
import static org.hibernate.type.SqlTypes.TIMESTAMP_WITH_TIMEZONE;
|
||||||
import static org.hibernate.type.SqlTypes.UUID;
|
import static org.hibernate.type.SqlTypes.UUID;
|
||||||
import static org.hibernate.type.SqlTypes.VARBINARY;
|
import static org.hibernate.type.SqlTypes.VARBINARY;
|
||||||
|
import static org.hibernate.type.SqlTypes.XML_ARRAY;
|
||||||
|
|
||||||
public class DB2AggregateSupport extends AggregateSupportImpl {
|
public class DB2AggregateSupport extends AggregateSupportImpl {
|
||||||
|
|
||||||
|
@ -59,6 +61,9 @@ public class DB2AggregateSupport extends AggregateSupportImpl {
|
||||||
public static final AggregateSupport JSON_INSTANCE = new DB2AggregateSupport( true );
|
public static final AggregateSupport JSON_INSTANCE = new DB2AggregateSupport( true );
|
||||||
private static final String JSON_QUERY_START = "json_query(";
|
private static final String JSON_QUERY_START = "json_query(";
|
||||||
private static final String JSON_QUERY_JSON_END = "')";
|
private static final String JSON_QUERY_JSON_END = "')";
|
||||||
|
private static final String XML_EXTRACT_START = "xmlelement(name \"" + XmlHelper.ROOT_TAG + "\",xmlquery(";
|
||||||
|
private static final String XML_EXTRACT_SEPARATOR = "/*' passing ";
|
||||||
|
private static final String XML_EXTRACT_END = " as \"d\"))";
|
||||||
|
|
||||||
private final boolean jsonSupport;
|
private final boolean jsonSupport;
|
||||||
|
|
||||||
|
@ -134,12 +139,91 @@ public class DB2AggregateSupport extends AggregateSupportImpl {
|
||||||
"json_value(" + parentPartExpression + columnExpression + "' returning " + column.getColumnDefinition() + ")"
|
"json_value(" + parentPartExpression + columnExpression + "' returning " + column.getColumnDefinition() + ")"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
case SQLXML:
|
||||||
|
case XML_ARRAY:
|
||||||
|
switch ( column.getJdbcMapping().getJdbcType().getDefaultSqlTypeCode() ) {
|
||||||
|
case BOOLEAN:
|
||||||
|
if ( SqlTypes.isNumericType( column.getJdbcMapping().getJdbcType().getDdlTypeCode() ) ) {
|
||||||
|
return template.replace(
|
||||||
|
placeholder,
|
||||||
|
"decode(xmlcast(xmlquery(" + xmlExtractArguments( aggregateParentReadExpression, columnExpression ) + ") as varchar(5)),'true',1,'false',0)"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return template.replace(
|
||||||
|
placeholder,
|
||||||
|
"decode(xmlcast(xmlquery(" + xmlExtractArguments( aggregateParentReadExpression, columnExpression ) + ") as varchar(5)),'true',true,'false',false)"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
case BINARY:
|
||||||
|
case VARBINARY:
|
||||||
|
case LONG32VARBINARY:
|
||||||
|
case BLOB:
|
||||||
|
// We encode binary data as hex, so we have to decode here
|
||||||
|
return template.replace(
|
||||||
|
placeholder,
|
||||||
|
"hextoraw(xmlcast(xmlquery(" + xmlExtractArguments( aggregateParentReadExpression, columnExpression ) + ") as clob))"
|
||||||
|
);
|
||||||
|
case TIMESTAMP_WITH_TIMEZONE:
|
||||||
|
case TIMESTAMP_UTC:
|
||||||
|
return template.replace(
|
||||||
|
placeholder,
|
||||||
|
"cast(trim(trailing 'Z' from xmlcast(xmlquery(" + xmlExtractArguments( aggregateParentReadExpression, columnExpression ) + ") as varchar(35))) as " + column.getColumnDefinition() + ")"
|
||||||
|
);
|
||||||
|
case SQLXML:
|
||||||
|
return template.replace(
|
||||||
|
placeholder,
|
||||||
|
XML_EXTRACT_START + xmlExtractArguments( aggregateParentReadExpression, columnExpression + "/*" ) + "))"
|
||||||
|
);
|
||||||
|
case XML_ARRAY:
|
||||||
|
if ( typeConfiguration.getCurrentBaseSqlTypeIndicators().isXmlFormatMapperLegacyFormatEnabled() ) {
|
||||||
|
throw new IllegalArgumentException( "XML array '" + columnExpression + "' in '" + aggregateParentReadExpression + "' is not supported with legacy format enabled." );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return template.replace(
|
||||||
|
placeholder,
|
||||||
|
"xmlelement(name \"Collection\",xmlquery(" + xmlExtractArguments( aggregateParentReadExpression, columnExpression + "/*" ) + "))"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
case UUID:
|
||||||
|
if ( SqlTypes.isBinaryType( column.getJdbcMapping().getJdbcType().getDdlTypeCode() ) ) {
|
||||||
|
return template.replace(
|
||||||
|
placeholder,
|
||||||
|
"hextoraw(replace(xmlcast(xmlquery(" + xmlExtractArguments( aggregateParentReadExpression, columnExpression ) + ") as varchar(36)),'-',''))"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// Fall-through intended
|
||||||
|
default:
|
||||||
|
return template.replace(
|
||||||
|
placeholder,
|
||||||
|
"xmlcast(xmlquery(" + xmlExtractArguments( aggregateParentReadExpression, columnExpression ) + ") as " + column.getColumnDefinition() + ")"
|
||||||
|
);
|
||||||
|
}
|
||||||
case STRUCT:
|
case STRUCT:
|
||||||
return template.replace( placeholder, aggregateParentReadExpression + ".." + columnExpression );
|
return template.replace( placeholder, aggregateParentReadExpression + ".." + columnExpression );
|
||||||
}
|
}
|
||||||
throw new IllegalArgumentException( "Unsupported aggregate SQL type: " + aggregateColumnTypeCode );
|
throw new IllegalArgumentException( "Unsupported aggregate SQL type: " + aggregateColumnTypeCode );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static String xmlExtractArguments(String aggregateParentReadExpression, String xpathFragment) {
|
||||||
|
final String extractArguments;
|
||||||
|
final int separatorIndex;
|
||||||
|
if ( aggregateParentReadExpression.startsWith( XML_EXTRACT_START )
|
||||||
|
&& aggregateParentReadExpression.endsWith( XML_EXTRACT_END )
|
||||||
|
&& (separatorIndex = aggregateParentReadExpression.indexOf( XML_EXTRACT_SEPARATOR )) != -1 ) {
|
||||||
|
final StringBuilder sb = new StringBuilder( aggregateParentReadExpression.length() - XML_EXTRACT_START.length() + xpathFragment.length() );
|
||||||
|
sb.append( aggregateParentReadExpression, XML_EXTRACT_START.length(), separatorIndex );
|
||||||
|
sb.append( '/' );
|
||||||
|
sb.append( xpathFragment );
|
||||||
|
sb.append( aggregateParentReadExpression, separatorIndex + 2, aggregateParentReadExpression.length() - 2 );
|
||||||
|
extractArguments = sb.toString();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
extractArguments = "'$d/" + XmlHelper.ROOT_TAG + "/" + xpathFragment + "' passing " + aggregateParentReadExpression + " as \"d\"";
|
||||||
|
}
|
||||||
|
return extractArguments;
|
||||||
|
}
|
||||||
|
|
||||||
private static String jsonCustomWriteExpression(String customWriteExpression, JdbcMapping jdbcMapping) {
|
private static String jsonCustomWriteExpression(String customWriteExpression, JdbcMapping jdbcMapping) {
|
||||||
final int sqlTypeCode = jdbcMapping.getJdbcType().getDefaultSqlTypeCode();
|
final int sqlTypeCode = jdbcMapping.getJdbcType().getDefaultSqlTypeCode();
|
||||||
switch ( sqlTypeCode ) {
|
switch ( sqlTypeCode ) {
|
||||||
|
@ -167,6 +251,33 @@ public class DB2AggregateSupport extends AggregateSupportImpl {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static String xmlCustomWriteExpression(String customWriteExpression, JdbcMapping jdbcMapping) {
|
||||||
|
final int sqlTypeCode = jdbcMapping.getJdbcType().getDefaultSqlTypeCode();
|
||||||
|
switch ( sqlTypeCode ) {
|
||||||
|
case BINARY:
|
||||||
|
case VARBINARY:
|
||||||
|
case LONG32VARBINARY:
|
||||||
|
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 XML_ARRAY:
|
||||||
|
// return "(" + customWriteExpression + ") format json";
|
||||||
|
case BOOLEAN:
|
||||||
|
return "decode(" + customWriteExpression + ",true,'true',false,'false')";
|
||||||
|
case TIME:
|
||||||
|
return "varchar_format(timestamp('1970-01-01'," + customWriteExpression + "),'HH24:MI:SS')";
|
||||||
|
case TIMESTAMP:
|
||||||
|
return "replace(varchar_format(" + customWriteExpression + ",'YYYY-MM-DD HH24:MI:SS.FF9'),' ','T')";
|
||||||
|
case TIMESTAMP_UTC:
|
||||||
|
return "replace(varchar_format(" + customWriteExpression + ",'YYYY-MM-DD HH24:MI:SS.FF9'),' ','T')||'Z'";
|
||||||
|
default:
|
||||||
|
return customWriteExpression;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String aggregateComponentAssignmentExpression(
|
public String aggregateComponentAssignmentExpression(
|
||||||
String aggregateParentAssignmentExpression,
|
String aggregateParentAssignmentExpression,
|
||||||
|
@ -181,6 +292,9 @@ public class DB2AggregateSupport extends AggregateSupportImpl {
|
||||||
return aggregateParentAssignmentExpression;
|
return aggregateParentAssignmentExpression;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case SQLXML:
|
||||||
|
case XML_ARRAY:
|
||||||
|
return aggregateParentAssignmentExpression;
|
||||||
case STRUCT:
|
case STRUCT:
|
||||||
return aggregateParentAssignmentExpression + ".." + columnExpression;
|
return aggregateParentAssignmentExpression + ".." + columnExpression;
|
||||||
}
|
}
|
||||||
|
@ -201,6 +315,9 @@ public class DB2AggregateSupport extends AggregateSupportImpl {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case SQLXML:
|
||||||
|
case XML_ARRAY:
|
||||||
|
return null;
|
||||||
case STRUCT:
|
case STRUCT:
|
||||||
final StringBuilder sb = new StringBuilder();
|
final StringBuilder sb = new StringBuilder();
|
||||||
appendStructCustomWriteExpression( aggregateColumn, aggregatedColumns, sb );
|
appendStructCustomWriteExpression( aggregateColumn, aggregatedColumns, sb );
|
||||||
|
@ -240,6 +357,9 @@ public class DB2AggregateSupport extends AggregateSupportImpl {
|
||||||
else if ( aggregateColumnSqlTypeCode == JSON ) {
|
else if ( aggregateColumnSqlTypeCode == JSON ) {
|
||||||
return columnSqlTypeCode == ARRAY ? JSON_ARRAY : columnSqlTypeCode;
|
return columnSqlTypeCode == ARRAY ? JSON_ARRAY : columnSqlTypeCode;
|
||||||
}
|
}
|
||||||
|
else if ( aggregateColumnSqlTypeCode == SQLXML ) {
|
||||||
|
return columnSqlTypeCode == ARRAY ? XML_ARRAY : columnSqlTypeCode;
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
return columnSqlTypeCode;
|
return columnSqlTypeCode;
|
||||||
}
|
}
|
||||||
|
@ -247,7 +367,7 @@ public class DB2AggregateSupport extends AggregateSupportImpl {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean requiresAggregateCustomWriteExpressionRenderer(int aggregateSqlTypeCode) {
|
public boolean requiresAggregateCustomWriteExpressionRenderer(int aggregateSqlTypeCode) {
|
||||||
return aggregateSqlTypeCode == STRUCT || aggregateSqlTypeCode == JSON;
|
return aggregateSqlTypeCode == STRUCT || aggregateSqlTypeCode == JSON || aggregateSqlTypeCode == SQLXML;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -259,28 +379,17 @@ public class DB2AggregateSupport extends AggregateSupportImpl {
|
||||||
switch ( aggregateSqlTypeCode ) {
|
switch ( aggregateSqlTypeCode ) {
|
||||||
case JSON:
|
case JSON:
|
||||||
if ( jsonSupport ) {
|
if ( jsonSupport ) {
|
||||||
return jsonAggregateColumnWriter( aggregateColumn, columnsToUpdate );
|
return new RootJsonWriteExpression( aggregateColumn, columnsToUpdate );
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case SQLXML:
|
||||||
|
return new RootXmlWriteExpression( aggregateColumn, columnsToUpdate );
|
||||||
case STRUCT:
|
case STRUCT:
|
||||||
return structAggregateColumnWriter( aggregateColumn, columnsToUpdate, typeConfiguration );
|
return new RootStructWriteExpression( aggregateColumn, columnsToUpdate, typeConfiguration );
|
||||||
}
|
}
|
||||||
throw new IllegalArgumentException( "Unsupported aggregate SQL type: " + aggregateSqlTypeCode );
|
throw new IllegalArgumentException( "Unsupported aggregate SQL type: " + aggregateSqlTypeCode );
|
||||||
}
|
}
|
||||||
|
|
||||||
private WriteExpressionRenderer jsonAggregateColumnWriter(
|
|
||||||
SelectableMapping aggregateColumn,
|
|
||||||
SelectableMapping[] columns) {
|
|
||||||
return new RootJsonWriteExpression( aggregateColumn, columns );
|
|
||||||
}
|
|
||||||
|
|
||||||
private WriteExpressionRenderer structAggregateColumnWriter(
|
|
||||||
SelectableMapping aggregateColumn,
|
|
||||||
SelectableMapping[] columns,
|
|
||||||
TypeConfiguration typeConfiguration) {
|
|
||||||
return new RootStructWriteExpression( aggregateColumn, columns, typeConfiguration );
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String determineTypeName(SelectableMapping column, TypeConfiguration typeConfiguration) {
|
private static String determineTypeName(SelectableMapping column, TypeConfiguration typeConfiguration) {
|
||||||
final String typeName;
|
final String typeName;
|
||||||
if ( column.getColumnDefinition() == null ) {
|
if ( column.getColumnDefinition() == null ) {
|
||||||
|
@ -470,21 +579,23 @@ public class DB2AggregateSupport extends AggregateSupportImpl {
|
||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
final String columnType = aggregateColumn.getTypeName();
|
final String columnType = aggregateColumn.getTypeName();
|
||||||
|
final boolean legacyXmlFormatEnabled = aggregateColumn.getValue().getBuildingContext().getBuildingOptions()
|
||||||
|
.isXmlFormatMapperLegacyFormatEnabled();
|
||||||
// The serialize and deserialize functions, as well as the transform are for supporting struct types in native queries and functions
|
// The serialize and deserialize functions, as well as the transform are for supporting struct types in native queries and functions
|
||||||
var list = new ArrayList<AuxiliaryDatabaseObject>( 3 );
|
var list = new ArrayList<AuxiliaryDatabaseObject>( 3 );
|
||||||
var serializerSb = new StringBuilder();
|
var serializerSb = new StringBuilder();
|
||||||
var deserializerSb = new StringBuilder();
|
var deserializerSb = new StringBuilder();
|
||||||
serializerSb.append( "create function " ).append( columnType ).append( "_serializer(v " ).append( columnType ).append( ") returns xml language sql " )
|
serializerSb.append( "create function " ).append( columnType ).append( "_serializer(v " ).append( columnType ).append( ") returns xml language sql " )
|
||||||
.append( "return xmlelement(name \"").append( XmlHelper.ROOT_TAG ).append( "\"" );
|
.append( "return xmlelement(name \"").append( XmlHelper.ROOT_TAG ).append( "\"" );
|
||||||
appendSerializer( aggregatedColumns, serializerSb, "v.." );
|
appendSerializer( aggregatedColumns, serializerSb, "v..", legacyXmlFormatEnabled );
|
||||||
serializerSb.append( ')' );
|
serializerSb.append( ')' );
|
||||||
|
|
||||||
deserializerSb.append( "create function " ).append( columnType ).append( "_deserializer(v xml) returns " ).append( columnType ).append( " language sql " )
|
deserializerSb.append( "create function " ).append( columnType ).append( "_deserializer(v xml) returns " ).append( columnType ).append( " language sql " )
|
||||||
.append( "return select " ).append( columnType ).append( "()" );
|
.append( "return select " ).append( columnType ).append( "()" );
|
||||||
appendDeserializerConstructor( aggregatedColumns, deserializerSb, "" );
|
appendDeserializerConstructor( aggregatedColumns, deserializerSb, "", legacyXmlFormatEnabled );
|
||||||
deserializerSb.append( " from xmltable('$" ).append( XmlHelper.ROOT_TAG ).append( "' passing v as \"" )
|
deserializerSb.append( " from xmltable('$" ).append( XmlHelper.ROOT_TAG ).append( "' passing v as \"" )
|
||||||
.append( XmlHelper.ROOT_TAG ).append( "\" columns" );
|
.append( XmlHelper.ROOT_TAG ).append( "\" columns" );
|
||||||
appendDeserializerColumns( aggregatedColumns, deserializerSb, ' ', "" );
|
appendDeserializerColumns( aggregatedColumns, deserializerSb, ' ', "", legacyXmlFormatEnabled );
|
||||||
deserializerSb.append( ") as t" );
|
deserializerSb.append( ") as t" );
|
||||||
list.add(
|
list.add(
|
||||||
new NamedAuxiliaryDatabaseObject(
|
new NamedAuxiliaryDatabaseObject(
|
||||||
|
@ -516,7 +627,7 @@ public class DB2AggregateSupport extends AggregateSupportImpl {
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void appendSerializer(List<Column> aggregatedColumns, StringBuilder serializerSb, String prefix) {
|
private static void appendSerializer(List<Column> aggregatedColumns, StringBuilder serializerSb, String prefix, boolean legacyXmlFormatEnabled) {
|
||||||
char sep;
|
char sep;
|
||||||
if ( aggregatedColumns.size() > 1 ) {
|
if ( aggregatedColumns.size() > 1 ) {
|
||||||
serializerSb.append( ",xmlconcat" );
|
serializerSb.append( ",xmlconcat" );
|
||||||
|
@ -533,12 +644,26 @@ public class DB2AggregateSupport extends AggregateSupportImpl {
|
||||||
appendSerializer(
|
appendSerializer(
|
||||||
aggregateColumn.getComponent().getAggregatedColumns(),
|
aggregateColumn.getComponent().getAggregatedColumns(),
|
||||||
serializerSb,
|
serializerSb,
|
||||||
prefix + udtColumn.getName() + ".."
|
prefix + udtColumn.getName() + "..",
|
||||||
|
legacyXmlFormatEnabled
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
else if ( needsVarcharForBitDataCast( udtColumn.getSqlType() ) ) {
|
else if ( needsVarcharForBitDataCast( udtColumn.getSqlType() ) ) {
|
||||||
serializerSb.append( ",cast(" ).append( prefix ).append( udtColumn.getName() ).append( " as varchar(" )
|
if ( legacyXmlFormatEnabled ) {
|
||||||
.append( udtColumn.getColumnSize( null, null ).getLength() ).append( ") for bit data)" );
|
serializerSb.append( ",cast(" ).append( prefix ).append( udtColumn.getName() ).append( " as " );
|
||||||
|
final long binaryLength = udtColumn.getColumnSize( null, null ).getLength();
|
||||||
|
// Legacy is Base64 encoded which is 4/3 bigger
|
||||||
|
final long varcharLength = ( binaryLength << 2 ) / 3;
|
||||||
|
if ( varcharLength < 32_672L ) {
|
||||||
|
serializerSb.append( "varchar(" ).append( varcharLength ).append( ") for bit data)" );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
serializerSb.append( "clob)" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
serializerSb.append( ",hex(" ).append( prefix ).append( udtColumn.getName() ).append( ")" );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
serializerSb.append( ',' ).append( prefix ).append( udtColumn.getName() );
|
serializerSb.append( ',' ).append( prefix ).append( udtColumn.getName() );
|
||||||
|
@ -554,7 +679,8 @@ public class DB2AggregateSupport extends AggregateSupportImpl {
|
||||||
private static void appendDeserializerConstructor(
|
private static void appendDeserializerConstructor(
|
||||||
List<Column> aggregatedColumns,
|
List<Column> aggregatedColumns,
|
||||||
StringBuilder deserializerSb,
|
StringBuilder deserializerSb,
|
||||||
String prefix) {
|
String prefix,
|
||||||
|
boolean legacyXmlFormatEnabled) {
|
||||||
for ( Column udtColumn : aggregatedColumns ) {
|
for ( Column udtColumn : aggregatedColumns ) {
|
||||||
deserializerSb.append( ".." ).append( udtColumn.getName() ).append( '(' );
|
deserializerSb.append( ".." ).append( udtColumn.getName() ).append( '(' );
|
||||||
if ( udtColumn.getSqlTypeCode() == STRUCT ) {
|
if ( udtColumn.getSqlTypeCode() == STRUCT ) {
|
||||||
|
@ -563,14 +689,21 @@ public class DB2AggregateSupport extends AggregateSupportImpl {
|
||||||
appendDeserializerConstructor(
|
appendDeserializerConstructor(
|
||||||
aggregateColumn.getComponent().getAggregatedColumns(),
|
aggregateColumn.getComponent().getAggregatedColumns(),
|
||||||
deserializerSb,
|
deserializerSb,
|
||||||
udtColumn.getName() + "_"
|
udtColumn.getName() + "_",
|
||||||
|
legacyXmlFormatEnabled
|
||||||
);
|
);
|
||||||
deserializerSb.append( ')' );
|
deserializerSb.append( ')' );
|
||||||
}
|
}
|
||||||
else if ( needsVarcharForBitDataCast( udtColumn.getSqlType() ) ) {
|
else if ( needsVarcharForBitDataCast( udtColumn.getSqlType() ) ) {
|
||||||
|
if ( legacyXmlFormatEnabled ) {
|
||||||
deserializerSb.append( "cast(t." ).append( prefix ).append( udtColumn.getName() ).append( " as " )
|
deserializerSb.append( "cast(t." ).append( prefix ).append( udtColumn.getName() ).append( " as " )
|
||||||
.append( udtColumn.getSqlType() ).append( "))" );
|
.append( udtColumn.getSqlType() ).append( "))" );
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
deserializerSb.append( "cast(hextoraw(t." ).append( prefix ).append( udtColumn.getName() ).append( ") as " )
|
||||||
|
.append( udtColumn.getSqlType() ).append( "))" );
|
||||||
|
}
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
deserializerSb.append( "t." ).append( prefix ).append( udtColumn.getName() ).append( ')' );
|
deserializerSb.append( "t." ).append( prefix ).append( udtColumn.getName() ).append( ')' );
|
||||||
}
|
}
|
||||||
|
@ -581,7 +714,8 @@ public class DB2AggregateSupport extends AggregateSupportImpl {
|
||||||
List<Column> aggregatedColumns,
|
List<Column> aggregatedColumns,
|
||||||
StringBuilder deserializerSb,
|
StringBuilder deserializerSb,
|
||||||
char sep,
|
char sep,
|
||||||
String prefix) {
|
String prefix,
|
||||||
|
boolean legacyXmlFormatEnabled) {
|
||||||
for ( Column udtColumn : aggregatedColumns ) {
|
for ( Column udtColumn : aggregatedColumns ) {
|
||||||
if ( udtColumn.getSqlTypeCode() == STRUCT ) {
|
if ( udtColumn.getSqlTypeCode() == STRUCT ) {
|
||||||
final AggregateColumn aggregateColumn = (AggregateColumn) udtColumn;
|
final AggregateColumn aggregateColumn = (AggregateColumn) udtColumn;
|
||||||
|
@ -589,15 +723,29 @@ public class DB2AggregateSupport extends AggregateSupportImpl {
|
||||||
aggregateColumn.getComponent().getAggregatedColumns(),
|
aggregateColumn.getComponent().getAggregatedColumns(),
|
||||||
deserializerSb,
|
deserializerSb,
|
||||||
sep,
|
sep,
|
||||||
udtColumn.getName() + "_"
|
udtColumn.getName() + "_",
|
||||||
|
legacyXmlFormatEnabled
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
deserializerSb.append( sep );
|
deserializerSb.append( sep );
|
||||||
deserializerSb.append( prefix ).append( udtColumn.getName() ).append( ' ' );
|
deserializerSb.append( prefix ).append( udtColumn.getName() ).append( ' ' );
|
||||||
if ( needsVarcharForBitDataCast( udtColumn.getSqlType() ) ) {
|
if ( needsVarcharForBitDataCast( udtColumn.getSqlType() ) ) {
|
||||||
deserializerSb.append( "varchar(" )
|
final long binaryLength = udtColumn.getColumnSize( null, null ).getLength();
|
||||||
.append( udtColumn.getColumnSize( null, null ).getLength() ).append( ") for bit data" );
|
final long varcharLength;
|
||||||
|
if ( legacyXmlFormatEnabled ) {
|
||||||
|
// Legacy is Base64 encoded which is 4/3 bigger
|
||||||
|
varcharLength = ( binaryLength << 2 ) / 3;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
varcharLength = binaryLength << 1;
|
||||||
|
}
|
||||||
|
if ( varcharLength < 32_672L ) {
|
||||||
|
deserializerSb.append( "varchar(" ).append( varcharLength ).append( ") for bit data" );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
deserializerSb.append( "clob" );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
deserializerSb.append( udtColumn.getSqlType() );
|
deserializerSb.append( udtColumn.getSqlType() );
|
||||||
|
@ -659,7 +807,7 @@ public class DB2AggregateSupport extends AggregateSupportImpl {
|
||||||
if ( jsonWriteExpression == null ) {
|
if ( jsonWriteExpression == null ) {
|
||||||
subExpressions.put(
|
subExpressions.put(
|
||||||
selectableMapping.getSelectableName(),
|
selectableMapping.getSelectableName(),
|
||||||
new PassThroughExpression( selectableMapping )
|
new PassThroughJsonWriteExpression( selectableMapping )
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
else if ( jsonWriteExpression instanceof AggregateJsonWriteExpression writeExpression ) {
|
else if ( jsonWriteExpression instanceof AggregateJsonWriteExpression writeExpression ) {
|
||||||
|
@ -760,11 +908,11 @@ public class DB2AggregateSupport extends AggregateSupportImpl {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class PassThroughExpression implements JsonWriteExpression {
|
private static class PassThroughJsonWriteExpression implements JsonWriteExpression {
|
||||||
|
|
||||||
private final SelectableMapping selectableMapping;
|
private final SelectableMapping selectableMapping;
|
||||||
|
|
||||||
PassThroughExpression(SelectableMapping selectableMapping) {
|
PassThroughJsonWriteExpression(SelectableMapping selectableMapping) {
|
||||||
this.selectableMapping = selectableMapping;
|
this.selectableMapping = selectableMapping;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -781,4 +929,158 @@ public class DB2AggregateSupport extends AggregateSupportImpl {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static class RootXmlWriteExpression implements WriteExpressionRenderer {
|
||||||
|
private final SelectableMapping aggregateColumn;
|
||||||
|
private final SelectableMapping[] columns;
|
||||||
|
|
||||||
|
RootXmlWriteExpression(SelectableMapping aggregateColumn, SelectableMapping[] columns) {
|
||||||
|
this.aggregateColumn = aggregateColumn;
|
||||||
|
this.columns = columns;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void render(
|
||||||
|
SqlAppender sqlAppender,
|
||||||
|
SqlAstTranslator<?> translator,
|
||||||
|
AggregateColumnWriteExpression aggregateColumnWriteExpression,
|
||||||
|
String qualifier) {
|
||||||
|
sqlAppender.append( "xmldocument(xmlquery('transform copy $d-out:=if(empty($d-in)) then <" );
|
||||||
|
sqlAppender.append( XmlHelper.ROOT_TAG );
|
||||||
|
sqlAppender.append( "/> else $d-in/" );
|
||||||
|
sqlAppender.append( XmlHelper.ROOT_TAG );
|
||||||
|
sqlAppender.append( " modify " );
|
||||||
|
|
||||||
|
char separator = '(';
|
||||||
|
for ( SelectableMapping column : columns ) {
|
||||||
|
final SelectablePath selectablePath = column.getSelectablePath();
|
||||||
|
final String tagXPath = columnXPath( selectablePath );
|
||||||
|
final String columnVariable = columnVariable( selectablePath );
|
||||||
|
sqlAppender.append( separator );
|
||||||
|
sqlAppender.append( "if(empty($" );
|
||||||
|
sqlAppender.append( columnVariable );
|
||||||
|
sqlAppender.append( ")) then do delete $d-out" );
|
||||||
|
sqlAppender.append( tagXPath );
|
||||||
|
sqlAppender.append( " else if(empty($d-out" );
|
||||||
|
sqlAppender.append( tagXPath );
|
||||||
|
sqlAppender.append( ")) then" );
|
||||||
|
|
||||||
|
SelectablePath parentPath = selectablePath.getParent();
|
||||||
|
assert parentPath != null;
|
||||||
|
renderParentInserts( sqlAppender, parentPath, "{$" + columnVariable + "}" );
|
||||||
|
|
||||||
|
sqlAppender.append( " do insert $" );
|
||||||
|
sqlAppender.append( columnVariable );
|
||||||
|
sqlAppender.append( " into $d-out" );
|
||||||
|
sqlAppender.append( columnXPath( selectablePath.getParent() ) );
|
||||||
|
sqlAppender.append( " else do replace $d-out" );
|
||||||
|
sqlAppender.append( tagXPath );
|
||||||
|
sqlAppender.append( " with $" );
|
||||||
|
sqlAppender.append( columnVariable );
|
||||||
|
separator = ',';
|
||||||
|
}
|
||||||
|
|
||||||
|
sqlAppender.append( ") return <" );
|
||||||
|
sqlAppender.append( XmlHelper.ROOT_TAG );
|
||||||
|
sqlAppender.append( ">{$d-out/*}</" );
|
||||||
|
sqlAppender.append( XmlHelper.ROOT_TAG );
|
||||||
|
sqlAppender.append( ">' passing " );
|
||||||
|
if ( qualifier != null && !qualifier.isBlank() ) {
|
||||||
|
sqlAppender.append( qualifier );
|
||||||
|
sqlAppender.append( '.' );
|
||||||
|
}
|
||||||
|
sqlAppender.append( aggregateColumn.getSelectionExpression() );
|
||||||
|
sqlAppender.append( " as \"d-in\"" );
|
||||||
|
|
||||||
|
for ( SelectableMapping column : columns ) {
|
||||||
|
sqlAppender.append( ",xmlelement(name " );
|
||||||
|
sqlAppender.appendDoubleQuoteEscapedString( column.getSelectableName() );
|
||||||
|
sqlAppender.append( ',' );
|
||||||
|
appendColumn(
|
||||||
|
sqlAppender,
|
||||||
|
column,
|
||||||
|
xmlCustomWriteExpression( column.getCustomWriteExpression(), column.getJdbcMapping() ),
|
||||||
|
translator,
|
||||||
|
aggregateColumnWriteExpression
|
||||||
|
);
|
||||||
|
sqlAppender.append( " option null on null) as " );
|
||||||
|
sqlAppender.appendDoubleQuoteEscapedString( columnVariable( column.getSelectablePath() ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
sqlAppender.append( "))" );
|
||||||
|
}
|
||||||
|
|
||||||
|
private void renderParentInserts(SqlAppender sqlAppender, SelectablePath parentPath, String parentContent) {
|
||||||
|
if ( !parentPath.isRoot() ) {
|
||||||
|
final String newParentContent = "<" + parentPath.getSelectableName() + ">" + parentContent + "</" + parentPath.getSelectableName() + ">";
|
||||||
|
final SelectablePath grandParentPath = parentPath.getParent();
|
||||||
|
assert grandParentPath != null;
|
||||||
|
|
||||||
|
sqlAppender.append( " if(empty($d-out" );
|
||||||
|
sqlAppender.append( columnXPath( parentPath ) );
|
||||||
|
sqlAppender.append( ")) then" );
|
||||||
|
renderParentInserts( sqlAppender, grandParentPath, newParentContent );
|
||||||
|
sqlAppender.append( " do insert " );
|
||||||
|
sqlAppender.append( newParentContent );
|
||||||
|
sqlAppender.append( " into $d-out" );
|
||||||
|
sqlAppender.append( columnXPath( grandParentPath ) );
|
||||||
|
sqlAppender.append( " else" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String columnXPath(SelectablePath selectablePath) {
|
||||||
|
final SelectablePath[] parts = selectablePath.getParts();
|
||||||
|
final StringBuilder xpath = new StringBuilder();
|
||||||
|
for ( int i = 1; i < parts.length; i++ ) {
|
||||||
|
xpath.append( '/' );
|
||||||
|
xpath.append( parts[i].getSelectableName() );
|
||||||
|
}
|
||||||
|
return xpath.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String columnVariable(SelectablePath selectablePath) {
|
||||||
|
final SelectablePath[] parts = selectablePath.getParts();
|
||||||
|
final StringBuilder variable = new StringBuilder();
|
||||||
|
for ( int i = 1; i < parts.length; i++ ) {
|
||||||
|
variable.append( parts[i].getSelectableName() );
|
||||||
|
variable.append( '-' );
|
||||||
|
}
|
||||||
|
variable.append( "in" );
|
||||||
|
return variable.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void appendColumn(
|
||||||
|
SqlAppender sb,
|
||||||
|
SelectableMapping selectableMapping,
|
||||||
|
String customWriteExpression,
|
||||||
|
SqlAstTranslator<?> translator,
|
||||||
|
AggregateColumnWriteExpression expression) {
|
||||||
|
final String customWriteExpressionStart;
|
||||||
|
final String customWriteExpressionEnd;
|
||||||
|
if ( customWriteExpression.equals( "?" ) ) {
|
||||||
|
customWriteExpressionStart = "";
|
||||||
|
customWriteExpressionEnd = "";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
final String[] parts = StringHelper.split( "?", customWriteExpression );
|
||||||
|
assert parts.length == 2;
|
||||||
|
customWriteExpressionStart = parts[0];
|
||||||
|
customWriteExpressionEnd = parts[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
final boolean isArray = selectableMapping.getJdbcMapping().getJdbcType().getDefaultSqlTypeCode() == XML_ARRAY;
|
||||||
|
if ( isArray ) {
|
||||||
|
sb.append( "xmlquery('$d/*/*' passing " );
|
||||||
|
}
|
||||||
|
sb.append( customWriteExpressionStart );
|
||||||
|
// We use NO_UNTYPED here so that expressions which require type inference are casted explicitly,
|
||||||
|
// since we don't know how the custom write expression looks like where this is embedded,
|
||||||
|
// so we have to be pessimistic and avoid ambiguities
|
||||||
|
translator.render( expression.getValueExpression( selectableMapping ), SqlAstNodeRenderingMode.NO_UNTYPED );
|
||||||
|
sb.append( customWriteExpressionEnd );
|
||||||
|
if ( isArray ) {
|
||||||
|
sb.append( " as \"d\")" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -207,7 +207,7 @@ public class StandardTableExporter implements Exporter<Table> {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void applyAggregateColumnCheck(StringBuilder buf, AggregateColumn aggregateColumn) {
|
protected void applyAggregateColumnCheck(StringBuilder buf, AggregateColumn aggregateColumn) {
|
||||||
final AggregateSupport aggregateSupport = dialect.getAggregateSupport();
|
final AggregateSupport aggregateSupport = dialect.getAggregateSupport();
|
||||||
final int checkStart = buf.length();
|
final int checkStart = buf.length();
|
||||||
buf.append( ", check (" );
|
buf.append( ", check (" );
|
||||||
|
|
|
@ -304,6 +304,18 @@ public class NestedXmlEmbeddableTest extends BaseSessionFactoryFunctionalTest {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@RequiresDialectFeature(feature = DialectFeatureChecks.SupportsXmlComponentUpdate.class)
|
||||||
|
public void testUpdateAggregateMemberOnNestedNull() {
|
||||||
|
sessionFactoryScope().inTransaction(
|
||||||
|
entityManager -> {
|
||||||
|
entityManager.createMutationQuery( "update XmlHolder b set b.theXml.simpleEmbeddable.doubleNested = null" ).executeUpdate();
|
||||||
|
entityManager.createMutationQuery( "update XmlHolder b set b.theXml.simpleEmbeddable.doubleNested.theNested.theLeaf.stringField = 'Abc'" ).executeUpdate();
|
||||||
|
assertEquals( "Abc", entityManager.find( XmlHolder.class, 1L ).getTheXml().simpleEmbeddable.doubleNested.theNested.theLeaf.stringField );
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@RequiresDialectFeature(feature = DialectFeatureChecks.SupportsXmlComponentUpdate.class)
|
@RequiresDialectFeature(feature = DialectFeatureChecks.SupportsXmlComponentUpdate.class)
|
||||||
public void testUpdateMultipleAggregateMembers() {
|
public void testUpdateMultipleAggregateMembers() {
|
||||||
|
|
Loading…
Reference in New Issue