HHH-18661 Add unnest() set-returning function and enable XML/JSON based array support on more databases
This commit is contained in:
parent
9bb5e64521
commit
25ddb64a4c
|
@ -434,6 +434,8 @@ use master
|
||||||
go
|
go
|
||||||
create login $SYBASE_USER with password $SYBASE_PASSWORD
|
create login $SYBASE_USER with password $SYBASE_PASSWORD
|
||||||
go
|
go
|
||||||
|
exec sp_configure 'enable xml', 1
|
||||||
|
go
|
||||||
exec sp_dboption $SYBASE_DB, 'abort tran on log full', true
|
exec sp_dboption $SYBASE_DB, 'abort tran on log full', true
|
||||||
go
|
go
|
||||||
exec sp_dboption $SYBASE_DB, 'allow nulls by default', true
|
exec sp_dboption $SYBASE_DB, 'allow nulls by default', true
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
:array-example-dir-hql: {core-project-dir}/src/test/java/org/hibernate/orm/test/function/array
|
:array-example-dir-hql: {core-project-dir}/src/test/java/org/hibernate/orm/test/function/array
|
||||||
:json-example-dir-hql: {core-project-dir}/src/test/java/org/hibernate/orm/test/function/json
|
:json-example-dir-hql: {core-project-dir}/src/test/java/org/hibernate/orm/test/function/json
|
||||||
:xml-example-dir-hql: {core-project-dir}/src/test/java/org/hibernate/orm/test/function/xml
|
:xml-example-dir-hql: {core-project-dir}/src/test/java/org/hibernate/orm/test/function/xml
|
||||||
|
:srf-example-dir-hql: {core-project-dir}/src/test/java/org/hibernate/orm/test/function/srf
|
||||||
:extrasdir: extras
|
:extrasdir: extras
|
||||||
|
|
||||||
This chapter describes Hibernate Query Language (HQL) and Jakarta Persistence Query Language (JPQL).
|
This chapter describes Hibernate Query Language (HQL) and Jakarta Persistence Query Language (JPQL).
|
||||||
|
@ -1197,32 +1198,33 @@ The following functions deal with SQL array types, which are not supported on ev
|
||||||
|===
|
|===
|
||||||
| Function | Purpose
|
| Function | Purpose
|
||||||
|
|
||||||
| `array()` | Creates an array based on the passed arguments
|
| <<hql-array-constructor-functions,`array()`>> | Creates an array based on the passed arguments
|
||||||
| `array_list()` | Like `array`, but returns the result as `List<?>`
|
| <<hql-array-constructor-functions,`array_list()`>> | Like `array`, but returns the result as `List<?>`
|
||||||
| `array_agg()` | Aggregates row values into an array
|
| <<hql-array-aggregate-functions,`array_agg()`>> | Aggregates row values into an array
|
||||||
| `array_position()` | Determines the position of an element in an array
|
| <<hql-array-position-functions,`array_position()`>> | Determines the position of an element in an array
|
||||||
| `array_positions()` | Determines all positions of an element in an array
|
| <<hql-array-positions-functions,`array_positions()`>> | Determines all positions of an element in an array
|
||||||
| `array_positions_list()` | Like `array_positions`, but returns the result as `List<Integer>`
|
| <<hql-array-positions-functions,`array_positions_list()`>> | Like `array_positions`, but returns the result as `List<Integer>`
|
||||||
| `array_length()` | Determines the length of an array
|
| <<hql-array-length-functions,`array_length()`>> | Determines the length of an array
|
||||||
| `array_concat()` | Concatenates array with each other in order
|
| <<hql-array-concat-functions,`array_concat()`>> | Concatenates array with each other in order
|
||||||
| `array_prepend()` | Prepends element to array
|
| <<hql-array-prepend-functions,`array_prepend()`>> | Prepends element to array
|
||||||
| `array_append()` | Appends element to array
|
| <<hql-array-append-functions,`array_append()`>> | Appends element to array
|
||||||
| `array_contains()` | Whether an array contains an element
|
| <<hql-array-contains-functions,`array_contains()`>> | Whether an array contains an element
|
||||||
| `array_contains_nullable()` | Whether an array contains an element, supporting `null` element
|
| <<hql-array-contains-functions,`array_contains_nullable()`>> | Whether an array contains an element, supporting `null` element
|
||||||
| `array_includes()` | Whether an array contains another array
|
| <<hql-array-includes-example,`array_includes()`>> | Whether an array contains another array
|
||||||
| `array_includes_nullable()` | Whether an array contains another array, supporting `null` elements
|
| <<hql-array-includes-example,`array_includes_nullable()`>> | Whether an array contains another array, supporting `null` elements
|
||||||
| `array_intersects()` | Whether an array holds at least one element of another array
|
| <<hql-array-intersects-functions,`array_intersects()`>> | Whether an array holds at least one element of another array
|
||||||
| `array_intersects_nullable()` | Whether an array holds at least one element of another array, supporting `null` elements
|
| <<hql-array-intersects-functions,`array_intersects_nullable()`>> | Whether an array holds at least one element of another array, supporting `null` elements
|
||||||
| `array_get()` | Accesses the element of an array by index
|
| <<hql-array-get-functions,`array_get()`>> | Accesses the element of an array by index
|
||||||
| `array_set()` | Creates array copy with given element at given index
|
| <<hql-array-set-functions,`array_set()`>> | Creates array copy with given element at given index
|
||||||
| `array_remove()` | Creates array copy with given element removed
|
| <<hql-array-remove-functions,`array_remove()`>> | Creates array copy with given element removed
|
||||||
| `array_remove_index()` | Creates array copy with the element at the given index removed
|
| <<hql-array-remove-index-functions,`array_remove_index()`>> | Creates array copy with the element at the given index removed
|
||||||
| `array_slice()` | Creates a sub-array of the based on lower and upper index
|
| <<hql-array-slice-functions,`array_slice()`>> | Creates a sub-array of the based on lower and upper index
|
||||||
| `array_replace()` | Creates array copy replacing a given element with another
|
| <<hql-array-replace-functions,`array_replace()`>> | Creates array copy replacing a given element with another
|
||||||
| `array_trim()` | Creates array copy trimming the last _N_ elements
|
| <<hql-array-trim-functions,`array_trim()`>> | Creates array copy trimming the last _N_ elements
|
||||||
| `array_fill()` | Creates array filled with the same element _N_ times
|
| <<hql-array-fill-functions,`array_fill()`>> | Creates array filled with the same element _N_ times
|
||||||
| `array_fill_list()` | Like `array_fill`, but returns the result as `List<?>`
|
| <<hql-array-fill-functions,`array_fill_list()`>> | Like `array_fill`, but returns the result as `List<?>`
|
||||||
| `array_to_string()` | String representation of array
|
| <<hql-array-to-string-functions,`array_to_string()`>> | String representation of array
|
||||||
|
| <<hql-array-unnest,`unnest()`>> | Turns an array into rows
|
||||||
|===
|
|===
|
||||||
|
|
||||||
[[hql-array-constructor-functions]]
|
[[hql-array-constructor-functions]]
|
||||||
|
@ -1637,6 +1639,32 @@ include::{array-example-dir-hql}/ArrayToStringTest.java[tags=hql-array-to-string
|
||||||
----
|
----
|
||||||
====
|
====
|
||||||
|
|
||||||
|
[[hql-array-unnest]]
|
||||||
|
===== `unnest()`
|
||||||
|
|
||||||
|
A <<hql-from-set-returning-functions,set-returning function>>, which turns the single array argument into rows.
|
||||||
|
Returns no rows if the array argument is `null` or an empty array.
|
||||||
|
The `index()` function can be used to access the 1-based array index of an array element.
|
||||||
|
|
||||||
|
[[hql-array-unnest-struct-example]]
|
||||||
|
====
|
||||||
|
[source, java, indent=0]
|
||||||
|
----
|
||||||
|
include::{array-example-dir-hql}/ArrayUnnestStructTest.java[tags=hql-array-unnest-aggregate-with-ordinality-example]
|
||||||
|
----
|
||||||
|
====
|
||||||
|
|
||||||
|
The `lateral` keyword is mandatory if the argument refers to a from node item of the same query level.
|
||||||
|
Basic plural attributes can also be joined directly, which is syntax sugar for `lateral unnest(...)`.
|
||||||
|
|
||||||
|
[[hql-array-unnest-example]]
|
||||||
|
====
|
||||||
|
[source, java, indent=0]
|
||||||
|
----
|
||||||
|
include::{array-example-dir-hql}/ArrayUnnestTest.java[tags=hql-array-unnest-example]
|
||||||
|
----
|
||||||
|
====
|
||||||
|
|
||||||
[[hql-functions-json]]
|
[[hql-functions-json]]
|
||||||
==== Functions for dealing with JSON
|
==== Functions for dealing with JSON
|
||||||
|
|
||||||
|
@ -2916,6 +2944,48 @@ The CTE name can be used for a `from` clause root or a `join`, similar to entity
|
||||||
|
|
||||||
Refer to the <<hql-with-cte,with clause>> chapter for details about CTEs.
|
Refer to the <<hql-with-cte,with clause>> chapter for details about CTEs.
|
||||||
|
|
||||||
|
[[hql-from-set-returning-functions]]
|
||||||
|
==== Set-returning functions in `from` clause
|
||||||
|
|
||||||
|
A set-returning function is a function that produces rows instead of a single scalar value
|
||||||
|
and is exclusively used in the `from` clause, either as root node or join target.
|
||||||
|
|
||||||
|
The `index()` function can be used to access the 1-based index of a returned row.
|
||||||
|
|
||||||
|
The following set-returning functions are available on many platforms:
|
||||||
|
|
||||||
|
|===
|
||||||
|
| Function | purpose
|
||||||
|
|
||||||
|
| <<hql-array-unnest,`unnest()`>> | Turns an array into rows
|
||||||
|
//| `generate_series()` | Creates a series of values as rows
|
||||||
|
|===
|
||||||
|
|
||||||
|
To use set returning functions defined in the database, it is required to register them in a `FunctionContributor`:
|
||||||
|
|
||||||
|
[[hql-from-set-returning-functions-contributor-example]]
|
||||||
|
====
|
||||||
|
[source, java, indent=0]
|
||||||
|
----
|
||||||
|
include::{srf-example-dir-hql}/CustomSetReturningFunctionTest.java[tags=hql-set-returning-function-contributor-example]
|
||||||
|
----
|
||||||
|
====
|
||||||
|
|
||||||
|
After that, the function can be used in the `from` clause:
|
||||||
|
|
||||||
|
[[hql-from-set-returning-functions-custom-example]]
|
||||||
|
====
|
||||||
|
[source, java, indent=0]
|
||||||
|
----
|
||||||
|
include::{srf-example-dir-hql}/CustomSetReturningFunctionTest.java[tags=hql-set-returning-function-custom-example]
|
||||||
|
----
|
||||||
|
====
|
||||||
|
|
||||||
|
NOTE: The `index()` function represents the idea of the `with ordinality` SQL syntax,
|
||||||
|
which is not supported on some databases for user defined functions.
|
||||||
|
Hibernate ORM tries to emulate this feature by wrapping invocations as lateral subqueries and using `row_number()`,
|
||||||
|
which may lead to worse performance.
|
||||||
|
|
||||||
[[hql-join]]
|
[[hql-join]]
|
||||||
=== Declaring joined entities
|
=== Declaring joined entities
|
||||||
|
|
||||||
|
@ -3131,6 +3201,17 @@ Most databases support some flavor of `join lateral`, and Hibernate emulates the
|
||||||
But emulation is neither very efficient, nor does it support all possible query shapes, so it's important to test on your target database.
|
But emulation is neither very efficient, nor does it support all possible query shapes, so it's important to test on your target database.
|
||||||
====
|
====
|
||||||
|
|
||||||
|
[[hql-join-set-returning-function]]
|
||||||
|
==== Set-returning functions in joins
|
||||||
|
|
||||||
|
A `join` clause may contain a set-returning function, either:
|
||||||
|
|
||||||
|
- an uncorrelated set-returning function, which is almost the same as a <<hql-from-set-returning-functions,set-returning function in the `from` clause>>, except that it may have an `on` restriction, or
|
||||||
|
- a _lateral join_, which is a correlated set-returning function, and may refer to other roots declared earlier in the same `from` clause.
|
||||||
|
|
||||||
|
The `lateral` keyword just distinguishes the two cases.
|
||||||
|
A lateral join may be an inner or left outer join, but not a right join, nor a full join.
|
||||||
|
|
||||||
[[hql-implicit-join]]
|
[[hql-implicit-join]]
|
||||||
==== Implicit association joins (path expressions)
|
==== Implicit association joins (path expressions)
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,7 @@ import java.util.TimeZone;
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
import org.hibernate.LockMode;
|
import org.hibernate.LockMode;
|
||||||
import org.hibernate.LockOptions;
|
import org.hibernate.LockOptions;
|
||||||
import org.hibernate.PessimisticLockException;
|
import org.hibernate.PessimisticLockException;
|
||||||
|
@ -92,7 +93,6 @@ import static org.hibernate.type.SqlTypes.GEOMETRY;
|
||||||
import static org.hibernate.type.SqlTypes.INET;
|
import static org.hibernate.type.SqlTypes.INET;
|
||||||
import static org.hibernate.type.SqlTypes.INTEGER;
|
import static org.hibernate.type.SqlTypes.INTEGER;
|
||||||
import static org.hibernate.type.SqlTypes.JSON;
|
import static org.hibernate.type.SqlTypes.JSON;
|
||||||
import static org.hibernate.type.SqlTypes.JSON_ARRAY;
|
|
||||||
import static org.hibernate.type.SqlTypes.LONG32NVARCHAR;
|
import static org.hibernate.type.SqlTypes.LONG32NVARCHAR;
|
||||||
import static org.hibernate.type.SqlTypes.LONG32VARBINARY;
|
import static org.hibernate.type.SqlTypes.LONG32VARBINARY;
|
||||||
import static org.hibernate.type.SqlTypes.LONG32VARCHAR;
|
import static org.hibernate.type.SqlTypes.LONG32VARCHAR;
|
||||||
|
@ -263,11 +263,9 @@ public class CockroachLegacyDialect extends Dialect {
|
||||||
if ( getVersion().isSameOrAfter( 20 ) ) {
|
if ( getVersion().isSameOrAfter( 20 ) ) {
|
||||||
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( INET, "inet", this ) );
|
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( INET, "inet", this ) );
|
||||||
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( JSON, "jsonb", this ) );
|
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( JSON, "jsonb", this ) );
|
||||||
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( JSON_ARRAY, "jsonb", this ) );
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( JSON, "json", this ) );
|
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( JSON, "json", this ) );
|
||||||
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( JSON_ARRAY, "json", this ) );
|
|
||||||
}
|
}
|
||||||
ddlTypeRegistry.addDescriptor( new NamedNativeEnumDdlTypeImpl( this ) );
|
ddlTypeRegistry.addDescriptor( new NamedNativeEnumDdlTypeImpl( this ) );
|
||||||
ddlTypeRegistry.addDescriptor( new NamedNativeOrdinalEnumDdlTypeImpl( this ) );
|
ddlTypeRegistry.addDescriptor( new NamedNativeOrdinalEnumDdlTypeImpl( this ) );
|
||||||
|
@ -372,11 +370,11 @@ public class CockroachLegacyDialect extends Dialect {
|
||||||
if ( getVersion().isSameOrAfter( 20, 0 ) ) {
|
if ( getVersion().isSameOrAfter( 20, 0 ) ) {
|
||||||
jdbcTypeRegistry.addDescriptorIfAbsent( PgJdbcHelper.getInetJdbcType( serviceRegistry ) );
|
jdbcTypeRegistry.addDescriptorIfAbsent( PgJdbcHelper.getInetJdbcType( serviceRegistry ) );
|
||||||
jdbcTypeRegistry.addDescriptorIfAbsent( PgJdbcHelper.getJsonbJdbcType( serviceRegistry ) );
|
jdbcTypeRegistry.addDescriptorIfAbsent( PgJdbcHelper.getJsonbJdbcType( serviceRegistry ) );
|
||||||
jdbcTypeRegistry.addDescriptorIfAbsent( PgJdbcHelper.getJsonbArrayJdbcType( serviceRegistry ) );
|
jdbcTypeRegistry.addTypeConstructorIfAbsent( PgJdbcHelper.getJsonbArrayJdbcType( serviceRegistry ) );
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
jdbcTypeRegistry.addDescriptorIfAbsent( PgJdbcHelper.getJsonJdbcType( serviceRegistry ) );
|
jdbcTypeRegistry.addDescriptorIfAbsent( PgJdbcHelper.getJsonJdbcType( serviceRegistry ) );
|
||||||
jdbcTypeRegistry.addDescriptorIfAbsent( PgJdbcHelper.getJsonArrayJdbcType( serviceRegistry ) );
|
jdbcTypeRegistry.addTypeConstructorIfAbsent( PgJdbcHelper.getJsonArrayJdbcType( serviceRegistry ) );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -384,11 +382,11 @@ public class CockroachLegacyDialect extends Dialect {
|
||||||
if ( getVersion().isSameOrAfter( 20, 0 ) ) {
|
if ( getVersion().isSameOrAfter( 20, 0 ) ) {
|
||||||
jdbcTypeRegistry.addDescriptorIfAbsent( PostgreSQLCastingInetJdbcType.INSTANCE );
|
jdbcTypeRegistry.addDescriptorIfAbsent( PostgreSQLCastingInetJdbcType.INSTANCE );
|
||||||
jdbcTypeRegistry.addDescriptorIfAbsent( PostgreSQLCastingJsonJdbcType.JSONB_INSTANCE );
|
jdbcTypeRegistry.addDescriptorIfAbsent( PostgreSQLCastingJsonJdbcType.JSONB_INSTANCE );
|
||||||
jdbcTypeRegistry.addDescriptorIfAbsent( PostgreSQLCastingJsonArrayJdbcType.JSONB_INSTANCE );
|
jdbcTypeRegistry.addTypeConstructorIfAbsent( PostgreSQLCastingJsonArrayJdbcTypeConstructor.JSONB_INSTANCE );
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
jdbcTypeRegistry.addDescriptorIfAbsent( PostgreSQLCastingJsonJdbcType.JSON_INSTANCE );
|
jdbcTypeRegistry.addDescriptorIfAbsent( PostgreSQLCastingJsonJdbcType.JSON_INSTANCE );
|
||||||
jdbcTypeRegistry.addDescriptorIfAbsent( PostgreSQLCastingJsonArrayJdbcType.JSON_INSTANCE );
|
jdbcTypeRegistry.addTypeConstructorIfAbsent( PostgreSQLCastingJsonArrayJdbcTypeConstructor.JSON_INSTANCE );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -398,11 +396,11 @@ public class CockroachLegacyDialect extends Dialect {
|
||||||
if ( getVersion().isSameOrAfter( 20, 0 ) ) {
|
if ( getVersion().isSameOrAfter( 20, 0 ) ) {
|
||||||
jdbcTypeRegistry.addDescriptorIfAbsent( PostgreSQLCastingInetJdbcType.INSTANCE );
|
jdbcTypeRegistry.addDescriptorIfAbsent( PostgreSQLCastingInetJdbcType.INSTANCE );
|
||||||
jdbcTypeRegistry.addDescriptorIfAbsent( PostgreSQLCastingJsonJdbcType.JSONB_INSTANCE );
|
jdbcTypeRegistry.addDescriptorIfAbsent( PostgreSQLCastingJsonJdbcType.JSONB_INSTANCE );
|
||||||
jdbcTypeRegistry.addDescriptorIfAbsent( PostgreSQLCastingJsonArrayJdbcType.JSONB_INSTANCE );
|
jdbcTypeRegistry.addTypeConstructorIfAbsent( PostgreSQLCastingJsonArrayJdbcTypeConstructor.JSONB_INSTANCE );
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
jdbcTypeRegistry.addDescriptorIfAbsent( PostgreSQLCastingJsonJdbcType.JSON_INSTANCE );
|
jdbcTypeRegistry.addDescriptorIfAbsent( PostgreSQLCastingJsonJdbcType.JSON_INSTANCE );
|
||||||
jdbcTypeRegistry.addDescriptorIfAbsent( PostgreSQLCastingJsonArrayJdbcType.JSON_INSTANCE );
|
jdbcTypeRegistry.addTypeConstructorIfAbsent( PostgreSQLCastingJsonArrayJdbcTypeConstructor.JSON_INSTANCE );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -424,6 +422,7 @@ public class CockroachLegacyDialect extends Dialect {
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Replace the standard array constructor
|
||||||
jdbcTypeRegistry.addTypeConstructor( PostgreSQLArrayJdbcTypeConstructor.INSTANCE );
|
jdbcTypeRegistry.addTypeConstructor( PostgreSQLArrayJdbcTypeConstructor.INSTANCE );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -518,6 +517,8 @@ public class CockroachLegacyDialect extends Dialect {
|
||||||
functionFactory.jsonArrayAppend_postgresql( false );
|
functionFactory.jsonArrayAppend_postgresql( false );
|
||||||
functionFactory.jsonArrayInsert_postgresql();
|
functionFactory.jsonArrayInsert_postgresql();
|
||||||
|
|
||||||
|
functionFactory.unnest_postgresql();
|
||||||
|
|
||||||
// Postgres uses # instead of ^ for XOR
|
// Postgres uses # instead of ^ for XOR
|
||||||
functionContributions.getFunctionRegistry().patternDescriptorBuilder( "bitxor", "(?1#?2)" )
|
functionContributions.getFunctionRegistry().patternDescriptorBuilder( "bitxor", "(?1#?2)" )
|
||||||
.setExactArgumentCount( 2 )
|
.setExactArgumentCount( 2 )
|
||||||
|
@ -534,6 +535,11 @@ public class CockroachLegacyDialect extends Dialect {
|
||||||
functionContributions.getFunctionRegistry().registerAlternateKey( "truncate", "trunc" );
|
functionContributions.getFunctionRegistry().registerAlternateKey( "truncate", "trunc" );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable String getDefaultOrdinalityColumnName() {
|
||||||
|
return "ordinality";
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TimeZoneSupport getTimeZoneSupport() {
|
public TimeZoneSupport getTimeZoneSupport() {
|
||||||
return TimeZoneSupport.NORMALIZE;
|
return TimeZoneSupport.NORMALIZE;
|
||||||
|
|
|
@ -88,6 +88,7 @@ import org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorDB
|
||||||
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.type.JavaObjectType;
|
import org.hibernate.type.JavaObjectType;
|
||||||
|
import org.hibernate.type.SqlTypes;
|
||||||
import org.hibernate.type.StandardBasicTypes;
|
import org.hibernate.type.StandardBasicTypes;
|
||||||
import org.hibernate.type.descriptor.ValueExtractor;
|
import org.hibernate.type.descriptor.ValueExtractor;
|
||||||
import org.hibernate.type.descriptor.java.JavaType;
|
import org.hibernate.type.descriptor.java.JavaType;
|
||||||
|
@ -455,6 +456,14 @@ public class DB2LegacyDialect extends Dialect {
|
||||||
functionFactory.xmlexists_db2_legacy();
|
functionFactory.xmlexists_db2_legacy();
|
||||||
}
|
}
|
||||||
functionFactory.xmlagg();
|
functionFactory.xmlagg();
|
||||||
|
|
||||||
|
functionFactory.unnest_emulated();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getPreferredSqlTypeCodeForArray() {
|
||||||
|
// Even if DB2 11 supports JSON functions, it's not possible to unnest a JSON array to rows, so stick to XML
|
||||||
|
return SqlTypes.XML_ARRAY;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -10,8 +10,11 @@ import java.util.function.Consumer;
|
||||||
import org.hibernate.LockMode;
|
import org.hibernate.LockMode;
|
||||||
import org.hibernate.dialect.DatabaseVersion;
|
import org.hibernate.dialect.DatabaseVersion;
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||||
|
import org.hibernate.metamodel.mapping.CollectionPart;
|
||||||
import org.hibernate.metamodel.mapping.JdbcMappingContainer;
|
import org.hibernate.metamodel.mapping.JdbcMappingContainer;
|
||||||
|
import org.hibernate.metamodel.mapping.ModelPart;
|
||||||
import org.hibernate.query.IllegalQueryOperationException;
|
import org.hibernate.query.IllegalQueryOperationException;
|
||||||
|
import org.hibernate.query.derived.AnonymousTupleTableGroupProducer;
|
||||||
import org.hibernate.query.sqm.ComparisonOperator;
|
import org.hibernate.query.sqm.ComparisonOperator;
|
||||||
import org.hibernate.query.sqm.FetchClauseType;
|
import org.hibernate.query.sqm.FetchClauseType;
|
||||||
import org.hibernate.sql.ast.Clause;
|
import org.hibernate.sql.ast.Clause;
|
||||||
|
@ -19,6 +22,7 @@ import org.hibernate.sql.ast.SqlAstNodeRenderingMode;
|
||||||
import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator;
|
import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator;
|
||||||
import org.hibernate.sql.ast.spi.SqlSelection;
|
import org.hibernate.sql.ast.spi.SqlSelection;
|
||||||
import org.hibernate.sql.ast.tree.MutationStatement;
|
import org.hibernate.sql.ast.tree.MutationStatement;
|
||||||
|
import org.hibernate.sql.ast.tree.SqlAstNode;
|
||||||
import org.hibernate.sql.ast.tree.Statement;
|
import org.hibernate.sql.ast.tree.Statement;
|
||||||
import org.hibernate.sql.ast.tree.delete.DeleteStatement;
|
import org.hibernate.sql.ast.tree.delete.DeleteStatement;
|
||||||
import org.hibernate.sql.ast.tree.expression.CaseSearchedExpression;
|
import org.hibernate.sql.ast.tree.expression.CaseSearchedExpression;
|
||||||
|
@ -28,6 +32,8 @@ import org.hibernate.sql.ast.tree.expression.Expression;
|
||||||
import org.hibernate.sql.ast.tree.expression.Literal;
|
import org.hibernate.sql.ast.tree.expression.Literal;
|
||||||
import org.hibernate.sql.ast.tree.expression.SqlTuple;
|
import org.hibernate.sql.ast.tree.expression.SqlTuple;
|
||||||
import org.hibernate.sql.ast.tree.expression.Summarization;
|
import org.hibernate.sql.ast.tree.expression.Summarization;
|
||||||
|
import org.hibernate.sql.ast.tree.from.DerivedTableReference;
|
||||||
|
import org.hibernate.sql.ast.tree.from.FunctionTableReference;
|
||||||
import org.hibernate.sql.ast.tree.from.NamedTableReference;
|
import org.hibernate.sql.ast.tree.from.NamedTableReference;
|
||||||
import org.hibernate.sql.ast.tree.from.QueryPartTableReference;
|
import org.hibernate.sql.ast.tree.from.QueryPartTableReference;
|
||||||
import org.hibernate.sql.ast.tree.from.TableGroup;
|
import org.hibernate.sql.ast.tree.from.TableGroup;
|
||||||
|
@ -253,6 +259,34 @@ public class DB2LegacySqlAstTranslator<T extends JdbcOperation> extends Abstract
|
||||||
inLateral = oldLateral;
|
inLateral = oldLateral;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void renderDerivedTableReference(DerivedTableReference tableReference) {
|
||||||
|
if ( tableReference instanceof FunctionTableReference && tableReference.isLateral() ) {
|
||||||
|
// No need for a lateral keyword for functions
|
||||||
|
tableReference.accept( this );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
super.renderDerivedTableReference( tableReference );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void renderNamedSetReturningFunction(String functionName, List<? extends SqlAstNode> sqlAstArguments, AnonymousTupleTableGroupProducer tupleType, String tableIdentifierVariable, SqlAstNodeRenderingMode argumentRenderingMode) {
|
||||||
|
final ModelPart ordinalitySubPart = tupleType.findSubPart( CollectionPart.Nature.INDEX.getName(), null );
|
||||||
|
if ( ordinalitySubPart != null ) {
|
||||||
|
appendSql( "lateral (select t.*, row_number() over() " );
|
||||||
|
appendSql( ordinalitySubPart.asBasicValuedModelPart().getSelectionExpression() );
|
||||||
|
appendSql( " from table(" );
|
||||||
|
renderSimpleNamedFunction( functionName, sqlAstArguments, argumentRenderingMode );
|
||||||
|
append( ") t)" );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
appendSql( "table(" );
|
||||||
|
super.renderNamedSetReturningFunction( functionName, sqlAstArguments, tupleType, tableIdentifierVariable, argumentRenderingMode );
|
||||||
|
append( ')' );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visitSelectStatement(SelectStatement statement) {
|
public void visitSelectStatement(SelectStatement statement) {
|
||||||
if ( getQueryPartForRowNumbering() == statement.getQueryPart() && inLateral ) {
|
if ( getQueryPartForRowNumbering() == statement.getQueryPart() && inLateral ) {
|
||||||
|
|
|
@ -4,17 +4,13 @@
|
||||||
*/
|
*/
|
||||||
package org.hibernate.community.dialect;
|
package org.hibernate.community.dialect;
|
||||||
|
|
||||||
import org.hibernate.LockMode;
|
|
||||||
import org.hibernate.dialect.DatabaseVersion;
|
import org.hibernate.dialect.DatabaseVersion;
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||||
import org.hibernate.query.sqm.ComparisonOperator;
|
import org.hibernate.query.sqm.ComparisonOperator;
|
||||||
import org.hibernate.sql.ast.tree.Statement;
|
import org.hibernate.sql.ast.tree.Statement;
|
||||||
import org.hibernate.sql.ast.tree.expression.Expression;
|
import org.hibernate.sql.ast.tree.expression.Expression;
|
||||||
import org.hibernate.sql.ast.tree.expression.Literal;
|
import org.hibernate.sql.ast.tree.expression.Literal;
|
||||||
import org.hibernate.sql.ast.tree.from.FunctionTableReference;
|
import org.hibernate.sql.ast.tree.from.QueryPartTableReference;
|
||||||
import org.hibernate.sql.ast.tree.from.NamedTableReference;
|
|
||||||
import org.hibernate.sql.ast.tree.from.TableGroup;
|
|
||||||
import org.hibernate.sql.ast.tree.from.TableReference;
|
|
||||||
import org.hibernate.sql.ast.tree.select.QueryPart;
|
import org.hibernate.sql.ast.tree.select.QueryPart;
|
||||||
import org.hibernate.sql.exec.spi.JdbcOperation;
|
import org.hibernate.sql.exec.spi.JdbcOperation;
|
||||||
|
|
||||||
|
@ -58,28 +54,10 @@ public class DB2zLegacySqlAstTranslator<T extends JdbcOperation> extends DB2Lega
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean renderPrimaryTableReference(TableGroup tableGroup, LockMode lockMode) {
|
public void visitQueryPartTableReference(QueryPartTableReference tableReference) {
|
||||||
if ( shouldInlineCte( tableGroup ) ) {
|
|
||||||
inlineCteTableGroup( tableGroup, lockMode );
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
final TableReference tableReference = tableGroup.getPrimaryTableReference();
|
|
||||||
if ( tableReference instanceof NamedTableReference ) {
|
|
||||||
return renderNamedTableReference( (NamedTableReference) tableReference, lockMode );
|
|
||||||
}
|
|
||||||
// DB2 z/OS we need the "table" qualifier for table valued functions or lateral sub-queries
|
// DB2 z/OS we need the "table" qualifier for table valued functions or lateral sub-queries
|
||||||
append( "table " );
|
append( "table " );
|
||||||
tableReference.accept( this );
|
super.visitQueryPartTableReference( tableReference );
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void visitFunctionTableReference(FunctionTableReference tableReference) {
|
|
||||||
// For the table qualifier we need parenthesis on DB2 z/OS
|
|
||||||
append( OPEN_PARENTHESIS );
|
|
||||||
tableReference.getFunctionExpression().accept( this );
|
|
||||||
append( CLOSE_PARENTHESIS );
|
|
||||||
renderDerivedTableReference( tableReference );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -14,6 +14,7 @@ import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.TimeZone;
|
import java.util.TimeZone;
|
||||||
|
|
||||||
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
import org.hibernate.PessimisticLockException;
|
import org.hibernate.PessimisticLockException;
|
||||||
import org.hibernate.QueryTimeoutException;
|
import org.hibernate.QueryTimeoutException;
|
||||||
import org.hibernate.boot.model.FunctionContributions;
|
import org.hibernate.boot.model.FunctionContributions;
|
||||||
|
@ -97,7 +98,6 @@ import static org.hibernate.type.SqlTypes.FLOAT;
|
||||||
import static org.hibernate.type.SqlTypes.GEOMETRY;
|
import static org.hibernate.type.SqlTypes.GEOMETRY;
|
||||||
import static org.hibernate.type.SqlTypes.INTERVAL_SECOND;
|
import static org.hibernate.type.SqlTypes.INTERVAL_SECOND;
|
||||||
import static org.hibernate.type.SqlTypes.JSON;
|
import static org.hibernate.type.SqlTypes.JSON;
|
||||||
import static org.hibernate.type.SqlTypes.JSON_ARRAY;
|
|
||||||
import static org.hibernate.type.SqlTypes.LONG32NVARCHAR;
|
import static org.hibernate.type.SqlTypes.LONG32NVARCHAR;
|
||||||
import static org.hibernate.type.SqlTypes.LONG32VARBINARY;
|
import static org.hibernate.type.SqlTypes.LONG32VARBINARY;
|
||||||
import static org.hibernate.type.SqlTypes.LONG32VARCHAR;
|
import static org.hibernate.type.SqlTypes.LONG32VARCHAR;
|
||||||
|
@ -265,7 +265,6 @@ public class H2LegacyDialect extends Dialect {
|
||||||
}
|
}
|
||||||
if ( getVersion().isSameOrAfter( 1, 4, 200 ) ) {
|
if ( getVersion().isSameOrAfter( 1, 4, 200 ) ) {
|
||||||
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( JSON, "json", this ) );
|
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( JSON, "json", this ) );
|
||||||
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( JSON_ARRAY, "json", this ) );
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ddlTypeRegistry.addDescriptor( new NativeEnumDdlTypeImpl( this ) );
|
ddlTypeRegistry.addDescriptor( new NativeEnumDdlTypeImpl( this ) );
|
||||||
|
@ -296,7 +295,8 @@ public class H2LegacyDialect extends Dialect {
|
||||||
}
|
}
|
||||||
if ( getVersion().isSameOrAfter( 1, 4, 200 ) ) {
|
if ( getVersion().isSameOrAfter( 1, 4, 200 ) ) {
|
||||||
jdbcTypeRegistry.addDescriptorIfAbsent( H2JsonJdbcType.INSTANCE );
|
jdbcTypeRegistry.addDescriptorIfAbsent( H2JsonJdbcType.INSTANCE );
|
||||||
jdbcTypeRegistry.addDescriptorIfAbsent( H2JsonArrayJdbcType.INSTANCE );
|
// Replace the standard array constructor
|
||||||
|
jdbcTypeRegistry.addTypeConstructor( H2JsonArrayJdbcTypeConstructor.INSTANCE );
|
||||||
}
|
}
|
||||||
jdbcTypeRegistry.addDescriptor( EnumJdbcType.INSTANCE );
|
jdbcTypeRegistry.addDescriptor( EnumJdbcType.INSTANCE );
|
||||||
jdbcTypeRegistry.addDescriptor( OrdinalEnumJdbcType.INSTANCE );
|
jdbcTypeRegistry.addDescriptor( OrdinalEnumJdbcType.INSTANCE );
|
||||||
|
@ -427,6 +427,8 @@ public class H2LegacyDialect extends Dialect {
|
||||||
else {
|
else {
|
||||||
functionFactory.listagg_groupConcat();
|
functionFactory.listagg_groupConcat();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
functionFactory.unnest_h2( getMaximumArraySize() );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -439,6 +441,11 @@ public class H2LegacyDialect extends Dialect {
|
||||||
return 1000;
|
return 1000;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable String getDefaultOrdinalityColumnName() {
|
||||||
|
return "nord";
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void augmentPhysicalTableTypes(List<String> tableTypesList) {
|
public void augmentPhysicalTableTypes(List<String> tableTypesList) {
|
||||||
if ( getVersion().isSameOrAfter( 2 ) ) {
|
if ( getVersion().isSameOrAfter( 2 ) ) {
|
||||||
|
|
|
@ -488,18 +488,28 @@ public class HANALegacyDialect extends Dialect {
|
||||||
typeConfiguration
|
typeConfiguration
|
||||||
);
|
);
|
||||||
|
|
||||||
if ( getVersion().isSameOrAfter(2, 0, 20) ) {
|
if ( getVersion().isSameOrAfter( 2, 0 ) ) {
|
||||||
// Introduced in 2.0 SPS 02
|
// Introduced in 2.0 SPS 00
|
||||||
functionFactory.jsonValue_no_passing();
|
functionFactory.jsonValue_no_passing();
|
||||||
functionFactory.jsonQuery_no_passing();
|
functionFactory.jsonQuery_no_passing();
|
||||||
functionFactory.jsonExists_hana();
|
functionFactory.jsonExists_hana();
|
||||||
if ( getVersion().isSameOrAfter(2, 0, 40) ) {
|
|
||||||
|
functionFactory.unnest_hana();
|
||||||
|
// functionFactory.json_table();
|
||||||
|
|
||||||
|
if ( getVersion().isSameOrAfter(2, 0, 20 ) ) {
|
||||||
|
if ( getVersion().isSameOrAfter( 2, 0, 40 ) ) {
|
||||||
// Introduced in 2.0 SPS 04
|
// Introduced in 2.0 SPS 04
|
||||||
functionFactory.jsonObject_hana();
|
functionFactory.jsonObject_hana();
|
||||||
functionFactory.jsonArray_hana();
|
functionFactory.jsonArray_hana();
|
||||||
functionFactory.jsonArrayAgg_hana();
|
functionFactory.jsonArrayAgg_hana();
|
||||||
functionFactory.jsonObjectAgg_hana();
|
functionFactory.jsonObjectAgg_hana();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// functionFactory.xmltable();
|
||||||
|
}
|
||||||
|
|
||||||
|
// functionFactory.xmlextract();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,17 +9,22 @@ import java.util.List;
|
||||||
import org.hibernate.MappingException;
|
import org.hibernate.MappingException;
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||||
import org.hibernate.internal.util.collections.Stack;
|
import org.hibernate.internal.util.collections.Stack;
|
||||||
|
import org.hibernate.metamodel.mapping.CollectionPart;
|
||||||
|
import org.hibernate.metamodel.mapping.ModelPart;
|
||||||
import org.hibernate.query.IllegalQueryOperationException;
|
import org.hibernate.query.IllegalQueryOperationException;
|
||||||
|
import org.hibernate.query.derived.AnonymousTupleTableGroupProducer;
|
||||||
import org.hibernate.query.sqm.ComparisonOperator;
|
import org.hibernate.query.sqm.ComparisonOperator;
|
||||||
import org.hibernate.sql.ast.Clause;
|
import org.hibernate.sql.ast.Clause;
|
||||||
import org.hibernate.sql.ast.SqlAstNodeRenderingMode;
|
import org.hibernate.sql.ast.SqlAstNodeRenderingMode;
|
||||||
import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator;
|
import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator;
|
||||||
|
import org.hibernate.sql.ast.tree.SqlAstNode;
|
||||||
import org.hibernate.sql.ast.tree.Statement;
|
import org.hibernate.sql.ast.tree.Statement;
|
||||||
import org.hibernate.sql.ast.tree.cte.CteStatement;
|
import org.hibernate.sql.ast.tree.cte.CteStatement;
|
||||||
import org.hibernate.sql.ast.tree.expression.BinaryArithmeticExpression;
|
import org.hibernate.sql.ast.tree.expression.BinaryArithmeticExpression;
|
||||||
import org.hibernate.sql.ast.tree.expression.Expression;
|
import org.hibernate.sql.ast.tree.expression.Expression;
|
||||||
import org.hibernate.sql.ast.tree.expression.Literal;
|
import org.hibernate.sql.ast.tree.expression.Literal;
|
||||||
import org.hibernate.sql.ast.tree.expression.Summarization;
|
import org.hibernate.sql.ast.tree.expression.Summarization;
|
||||||
|
import org.hibernate.sql.ast.tree.from.DerivedTableReference;
|
||||||
import org.hibernate.sql.ast.tree.from.FunctionTableReference;
|
import org.hibernate.sql.ast.tree.from.FunctionTableReference;
|
||||||
import org.hibernate.sql.ast.tree.from.NamedTableReference;
|
import org.hibernate.sql.ast.tree.from.NamedTableReference;
|
||||||
import org.hibernate.sql.ast.tree.from.QueryPartTableReference;
|
import org.hibernate.sql.ast.tree.from.QueryPartTableReference;
|
||||||
|
@ -34,6 +39,8 @@ import org.hibernate.sql.ast.tree.update.UpdateStatement;
|
||||||
import org.hibernate.sql.exec.spi.JdbcOperation;
|
import org.hibernate.sql.exec.spi.JdbcOperation;
|
||||||
import org.hibernate.sql.model.internal.TableInsertStandard;
|
import org.hibernate.sql.model.internal.TableInsertStandard;
|
||||||
|
|
||||||
|
import static org.hibernate.dialect.SybaseASESqlAstTranslator.isLob;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An SQL AST translator for the Legacy HANA dialect.
|
* An SQL AST translator for the Legacy HANA dialect.
|
||||||
*/
|
*/
|
||||||
|
@ -192,15 +199,35 @@ public class HANALegacySqlAstTranslator<T extends JdbcOperation> extends Abstrac
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected SqlAstNodeRenderingMode getParameterRenderingMode() {
|
protected void renderDerivedTableReference(DerivedTableReference tableReference) {
|
||||||
// HANA does not support parameters in lateral subqueries for some reason, so inline all the parameters in this case
|
if ( tableReference instanceof FunctionTableReference && tableReference.isLateral() ) {
|
||||||
return inLateral ? SqlAstNodeRenderingMode.INLINE_ALL_PARAMETERS : super.getParameterRenderingMode();
|
// No need for a lateral keyword for functions
|
||||||
|
tableReference.accept( this );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
super.renderDerivedTableReference( tableReference );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visitFunctionTableReference(FunctionTableReference tableReference) {
|
public void renderNamedSetReturningFunction(String functionName, List<? extends SqlAstNode> sqlAstArguments, AnonymousTupleTableGroupProducer tupleType, String tableIdentifierVariable, SqlAstNodeRenderingMode argumentRenderingMode) {
|
||||||
tableReference.getFunctionExpression().accept( this );
|
final ModelPart ordinalitySubPart = tupleType.findSubPart( CollectionPart.Nature.INDEX.getName(), null );
|
||||||
renderTableReferenceIdentificationVariable( tableReference );
|
if ( ordinalitySubPart != null ) {
|
||||||
|
appendSql( "(select t.*, row_number() over() " );
|
||||||
|
appendSql( ordinalitySubPart.asBasicValuedModelPart().getSelectionExpression() );
|
||||||
|
appendSql( " from " );
|
||||||
|
renderSimpleNamedFunction( functionName, sqlAstArguments, argumentRenderingMode );
|
||||||
|
append( " t)" );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
super.renderNamedSetReturningFunction( functionName, sqlAstArguments, tupleType, tableIdentifierVariable, argumentRenderingMode );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected SqlAstNodeRenderingMode getParameterRenderingMode() {
|
||||||
|
// HANA does not support parameters in lateral subqueries for some reason, so inline all the parameters in this case
|
||||||
|
return inLateral ? SqlAstNodeRenderingMode.INLINE_ALL_PARAMETERS : super.getParameterRenderingMode();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -212,7 +239,38 @@ public class HANALegacySqlAstTranslator<T extends JdbcOperation> extends Abstrac
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void renderComparison(Expression lhs, ComparisonOperator operator, Expression rhs) {
|
protected void renderComparison(Expression lhs, ComparisonOperator operator, Expression rhs) {
|
||||||
|
// In SAP HANA, LOBs are not "comparable", so we have to use a like predicate for comparison
|
||||||
|
final boolean isLob = isLob( lhs.getExpressionType() );
|
||||||
if ( operator == ComparisonOperator.DISTINCT_FROM || operator == ComparisonOperator.NOT_DISTINCT_FROM ) {
|
if ( operator == ComparisonOperator.DISTINCT_FROM || operator == ComparisonOperator.NOT_DISTINCT_FROM ) {
|
||||||
|
if ( isLob ) {
|
||||||
|
switch ( operator ) {
|
||||||
|
case DISTINCT_FROM:
|
||||||
|
appendSql( "case when " );
|
||||||
|
lhs.accept( this );
|
||||||
|
appendSql( " like " );
|
||||||
|
rhs.accept( this );
|
||||||
|
appendSql( " or " );
|
||||||
|
lhs.accept( this );
|
||||||
|
appendSql( " is null and " );
|
||||||
|
rhs.accept( this );
|
||||||
|
appendSql( " is null then 0 else 1 end=1" );
|
||||||
|
return;
|
||||||
|
case NOT_DISTINCT_FROM:
|
||||||
|
appendSql( "case when " );
|
||||||
|
lhs.accept( this );
|
||||||
|
appendSql( " like " );
|
||||||
|
rhs.accept( this );
|
||||||
|
appendSql( " or " );
|
||||||
|
lhs.accept( this );
|
||||||
|
appendSql( " is null and " );
|
||||||
|
rhs.accept( this );
|
||||||
|
appendSql( " is null then 0 else 1 end=0" );
|
||||||
|
return;
|
||||||
|
default:
|
||||||
|
// Fall through
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
// HANA does not support plain parameters in the select clause of the intersect emulation
|
// HANA does not support plain parameters in the select clause of the intersect emulation
|
||||||
withParameterRenderingMode(
|
withParameterRenderingMode(
|
||||||
SqlAstNodeRenderingMode.NO_PLAIN_PARAMETER,
|
SqlAstNodeRenderingMode.NO_PLAIN_PARAMETER,
|
||||||
|
@ -220,7 +278,24 @@ public class HANALegacySqlAstTranslator<T extends JdbcOperation> extends Abstrac
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
renderComparisonEmulateIntersect( lhs, operator, rhs );
|
if ( isLob ) {
|
||||||
|
switch ( operator ) {
|
||||||
|
case EQUAL:
|
||||||
|
lhs.accept( this );
|
||||||
|
appendSql( " like " );
|
||||||
|
rhs.accept( this );
|
||||||
|
return;
|
||||||
|
case NOT_EQUAL:
|
||||||
|
lhs.accept( this );
|
||||||
|
appendSql( " not like " );
|
||||||
|
rhs.accept( this );
|
||||||
|
return;
|
||||||
|
default:
|
||||||
|
// Fall through
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
renderComparisonStandard( lhs, operator, rhs );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,7 @@ import java.sql.DatabaseMetaData;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import java.sql.Types;
|
import java.sql.Types;
|
||||||
|
|
||||||
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
import org.hibernate.JDBCException;
|
import org.hibernate.JDBCException;
|
||||||
import org.hibernate.LockMode;
|
import org.hibernate.LockMode;
|
||||||
import org.hibernate.StaleObjectStateException;
|
import org.hibernate.StaleObjectStateException;
|
||||||
|
@ -277,6 +278,8 @@ public class HSQLLegacyDialect extends Dialect {
|
||||||
functionFactory.jsonObjectAgg_h2();
|
functionFactory.jsonObjectAgg_h2();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
functionFactory.unnest( "c1", "c2" );
|
||||||
|
|
||||||
//trim() requires parameters to be cast when used as trim character
|
//trim() requires parameters to be cast when used as trim character
|
||||||
functionContributions.getFunctionRegistry().register( "trim", new TrimFunction(
|
functionContributions.getFunctionRegistry().register( "trim", new TrimFunction(
|
||||||
this,
|
this,
|
||||||
|
@ -285,6 +288,11 @@ public class HSQLLegacyDialect extends Dialect {
|
||||||
) );
|
) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable String getDefaultOrdinalityColumnName() {
|
||||||
|
return "c2";
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String currentTime() {
|
public String currentTime() {
|
||||||
return "localtime";
|
return "localtime";
|
||||||
|
|
|
@ -22,6 +22,8 @@ import org.hibernate.sql.ast.tree.expression.Expression;
|
||||||
import org.hibernate.sql.ast.tree.expression.Literal;
|
import org.hibernate.sql.ast.tree.expression.Literal;
|
||||||
import org.hibernate.sql.ast.tree.expression.SqlTuple;
|
import org.hibernate.sql.ast.tree.expression.SqlTuple;
|
||||||
import org.hibernate.sql.ast.tree.expression.Summarization;
|
import org.hibernate.sql.ast.tree.expression.Summarization;
|
||||||
|
import org.hibernate.sql.ast.tree.from.DerivedTableReference;
|
||||||
|
import org.hibernate.sql.ast.tree.from.FunctionTableReference;
|
||||||
import org.hibernate.sql.ast.tree.from.NamedTableReference;
|
import org.hibernate.sql.ast.tree.from.NamedTableReference;
|
||||||
import org.hibernate.sql.ast.tree.insert.ConflictClause;
|
import org.hibernate.sql.ast.tree.insert.ConflictClause;
|
||||||
import org.hibernate.sql.ast.tree.insert.InsertSelectStatement;
|
import org.hibernate.sql.ast.tree.insert.InsertSelectStatement;
|
||||||
|
@ -72,6 +74,17 @@ public class HSQLLegacySqlAstTranslator<T extends JdbcOperation> extends Abstrac
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void renderDerivedTableReference(DerivedTableReference tableReference) {
|
||||||
|
if ( tableReference instanceof FunctionTableReference && tableReference.isLateral() ) {
|
||||||
|
// No need for a lateral keyword for functions
|
||||||
|
tableReference.accept( this );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
super.renderDerivedTableReference( tableReference );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void visitConflictClause(ConflictClause conflictClause) {
|
protected void visitConflictClause(ConflictClause conflictClause) {
|
||||||
if ( conflictClause != null ) {
|
if ( conflictClause != null ) {
|
||||||
|
|
|
@ -29,7 +29,7 @@ import org.hibernate.tool.schema.extract.spi.SequenceInformationExtractor;
|
||||||
import org.hibernate.type.SqlTypes;
|
import org.hibernate.type.SqlTypes;
|
||||||
import org.hibernate.type.StandardBasicTypes;
|
import org.hibernate.type.StandardBasicTypes;
|
||||||
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
||||||
import org.hibernate.type.descriptor.jdbc.JsonArrayJdbcType;
|
import org.hibernate.type.descriptor.jdbc.JsonArrayJdbcTypeConstructor;
|
||||||
import org.hibernate.type.descriptor.jdbc.JsonJdbcType;
|
import org.hibernate.type.descriptor.jdbc.JsonJdbcType;
|
||||||
import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry;
|
import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry;
|
||||||
import org.hibernate.type.descriptor.sql.internal.DdlTypeImpl;
|
import org.hibernate.type.descriptor.sql.internal.DdlTypeImpl;
|
||||||
|
@ -96,7 +96,12 @@ public class MariaDBLegacyDialect extends MySQLLegacyDialect {
|
||||||
commonFunctionFactory.jsonArrayAgg_mariadb();
|
commonFunctionFactory.jsonArrayAgg_mariadb();
|
||||||
commonFunctionFactory.jsonObjectAgg_mariadb();
|
commonFunctionFactory.jsonObjectAgg_mariadb();
|
||||||
commonFunctionFactory.jsonArrayAppend_mariadb();
|
commonFunctionFactory.jsonArrayAppend_mariadb();
|
||||||
|
|
||||||
if ( getVersion().isSameOrAfter( 10, 3, 3 ) ) {
|
if ( getVersion().isSameOrAfter( 10, 3, 3 ) ) {
|
||||||
|
if ( getVersion().isSameOrAfter( 10, 6 ) ) {
|
||||||
|
commonFunctionFactory.unnest_emulated();
|
||||||
|
}
|
||||||
|
|
||||||
commonFunctionFactory.inverseDistributionOrderedSetAggregates_windowEmulation();
|
commonFunctionFactory.inverseDistributionOrderedSetAggregates_windowEmulation();
|
||||||
functionContributions.getFunctionRegistry().patternDescriptorBuilder( "median", "median(?1) over ()" )
|
functionContributions.getFunctionRegistry().patternDescriptorBuilder( "median", "median(?1) over ()" )
|
||||||
.setInvariantType( functionContributions.getTypeConfiguration().getBasicTypeRegistry().resolve( StandardBasicTypes.DOUBLE ) )
|
.setInvariantType( functionContributions.getTypeConfiguration().getBasicTypeRegistry().resolve( StandardBasicTypes.DOUBLE ) )
|
||||||
|
@ -145,7 +150,7 @@ public class MariaDBLegacyDialect extends MySQLLegacyDialect {
|
||||||
final JdbcTypeRegistry jdbcTypeRegistry = typeContributions.getTypeConfiguration().getJdbcTypeRegistry();
|
final JdbcTypeRegistry jdbcTypeRegistry = typeContributions.getTypeConfiguration().getJdbcTypeRegistry();
|
||||||
// Make sure we register the JSON type descriptor before calling super, because MariaDB does not need casting
|
// Make sure we register the JSON type descriptor before calling super, because MariaDB does not need casting
|
||||||
jdbcTypeRegistry.addDescriptorIfAbsent( SqlTypes.JSON, JsonJdbcType.INSTANCE );
|
jdbcTypeRegistry.addDescriptorIfAbsent( SqlTypes.JSON, JsonJdbcType.INSTANCE );
|
||||||
jdbcTypeRegistry.addDescriptorIfAbsent( SqlTypes.JSON_ARRAY, JsonArrayJdbcType.INSTANCE );
|
jdbcTypeRegistry.addTypeConstructorIfAbsent( JsonArrayJdbcTypeConstructor.INSTANCE );
|
||||||
|
|
||||||
super.contributeTypes( typeContributions, serviceRegistry );
|
super.contributeTypes( typeContributions, serviceRegistry );
|
||||||
if ( getVersion().isSameOrAfter( 10, 7 ) ) {
|
if ( getVersion().isSameOrAfter( 10, 7 ) ) {
|
||||||
|
|
|
@ -22,6 +22,7 @@ import org.hibernate.sql.ast.tree.expression.ColumnReference;
|
||||||
import org.hibernate.sql.ast.tree.expression.Expression;
|
import org.hibernate.sql.ast.tree.expression.Expression;
|
||||||
import org.hibernate.sql.ast.tree.expression.Literal;
|
import org.hibernate.sql.ast.tree.expression.Literal;
|
||||||
import org.hibernate.sql.ast.tree.expression.Summarization;
|
import org.hibernate.sql.ast.tree.expression.Summarization;
|
||||||
|
import org.hibernate.sql.ast.tree.from.DerivedTableReference;
|
||||||
import org.hibernate.sql.ast.tree.from.NamedTableReference;
|
import org.hibernate.sql.ast.tree.from.NamedTableReference;
|
||||||
import org.hibernate.sql.ast.tree.from.QueryPartTableReference;
|
import org.hibernate.sql.ast.tree.from.QueryPartTableReference;
|
||||||
import org.hibernate.sql.ast.tree.insert.ConflictClause;
|
import org.hibernate.sql.ast.tree.insert.ConflictClause;
|
||||||
|
@ -277,6 +278,11 @@ public class MariaDBLegacySqlAstTranslator<T extends JdbcOperation> extends Abst
|
||||||
emulateQueryPartTableReferenceColumnAliasing( tableReference );
|
emulateQueryPartTableReferenceColumnAliasing( tableReference );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void renderDerivedTableReferenceIdentificationVariable(DerivedTableReference tableReference) {
|
||||||
|
renderTableReferenceIdentificationVariable( tableReference );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visitOffsetFetchClause(QueryPart queryPart) {
|
public void visitOffsetFetchClause(QueryPart queryPart) {
|
||||||
if ( !isRowNumberingCurrentQueryPart() ) {
|
if ( !isRowNumberingCurrentQueryPart() ) {
|
||||||
|
|
|
@ -537,6 +537,11 @@ public class MySQLLegacyDialect extends Dialect {
|
||||||
return Types.BIT;
|
return Types.BIT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getPreferredSqlTypeCodeForArray() {
|
||||||
|
return getMySQLVersion().isSameOrAfter( 5, 7 ) ? SqlTypes.JSON_ARRAY : super.getPreferredSqlTypeCodeForArray();
|
||||||
|
}
|
||||||
|
|
||||||
// @Override
|
// @Override
|
||||||
// public int getDefaultDecimalPrecision() {
|
// public int getDefaultDecimalPrecision() {
|
||||||
// //this is the maximum, but I guess it's too high
|
// //this is the maximum, but I guess it's too high
|
||||||
|
@ -667,6 +672,10 @@ public class MySQLLegacyDialect extends Dialect {
|
||||||
functionFactory.jsonMergepatch_mysql();
|
functionFactory.jsonMergepatch_mysql();
|
||||||
functionFactory.jsonArrayAppend_mysql();
|
functionFactory.jsonArrayAppend_mysql();
|
||||||
functionFactory.jsonArrayInsert_mysql();
|
functionFactory.jsonArrayInsert_mysql();
|
||||||
|
|
||||||
|
if ( getMySQLVersion().isSameOrAfter( 8 ) ) {
|
||||||
|
functionFactory.unnest_emulated();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -678,7 +687,7 @@ public class MySQLLegacyDialect extends Dialect {
|
||||||
|
|
||||||
if ( getMySQLVersion().isSameOrAfter( 5, 7 ) ) {
|
if ( getMySQLVersion().isSameOrAfter( 5, 7 ) ) {
|
||||||
jdbcTypeRegistry.addDescriptorIfAbsent( SqlTypes.JSON, MySQLCastingJsonJdbcType.INSTANCE );
|
jdbcTypeRegistry.addDescriptorIfAbsent( SqlTypes.JSON, MySQLCastingJsonJdbcType.INSTANCE );
|
||||||
jdbcTypeRegistry.addDescriptorIfAbsent( SqlTypes.JSON_ARRAY, MySQLCastingJsonArrayJdbcType.INSTANCE );
|
jdbcTypeRegistry.addTypeConstructorIfAbsent( MySQLCastingJsonArrayJdbcTypeConstructor.INSTANCE );
|
||||||
}
|
}
|
||||||
|
|
||||||
// MySQL requires a custom binder for binding untyped nulls with the NULL type
|
// MySQL requires a custom binder for binding untyped nulls with the NULL type
|
||||||
|
|
|
@ -23,6 +23,8 @@ import org.hibernate.sql.ast.tree.expression.ColumnReference;
|
||||||
import org.hibernate.sql.ast.tree.expression.Expression;
|
import org.hibernate.sql.ast.tree.expression.Expression;
|
||||||
import org.hibernate.sql.ast.tree.expression.Literal;
|
import org.hibernate.sql.ast.tree.expression.Literal;
|
||||||
import org.hibernate.sql.ast.tree.expression.Summarization;
|
import org.hibernate.sql.ast.tree.expression.Summarization;
|
||||||
|
import org.hibernate.sql.ast.tree.from.DerivedTableReference;
|
||||||
|
import org.hibernate.sql.ast.tree.from.FunctionTableReference;
|
||||||
import org.hibernate.sql.ast.tree.from.NamedTableReference;
|
import org.hibernate.sql.ast.tree.from.NamedTableReference;
|
||||||
import org.hibernate.sql.ast.tree.from.QueryPartTableReference;
|
import org.hibernate.sql.ast.tree.from.QueryPartTableReference;
|
||||||
import org.hibernate.sql.ast.tree.from.ValuesTableReference;
|
import org.hibernate.sql.ast.tree.from.ValuesTableReference;
|
||||||
|
@ -269,6 +271,27 @@ public class MySQLLegacySqlAstTranslator<T extends JdbcOperation> extends Abstra
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void renderDerivedTableReference(DerivedTableReference tableReference) {
|
||||||
|
if ( tableReference instanceof FunctionTableReference && tableReference.isLateral() ) {
|
||||||
|
// No need for a lateral keyword for functions
|
||||||
|
tableReference.accept( this );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
super.renderDerivedTableReference( tableReference );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void renderDerivedTableReferenceIdentificationVariable(DerivedTableReference tableReference) {
|
||||||
|
if ( getDialect().getVersion().isSameOrAfter( 8 ) ) {
|
||||||
|
super.renderDerivedTableReferenceIdentificationVariable( tableReference );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
renderTableReferenceIdentificationVariable( tableReference );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visitOffsetFetchClause(QueryPart queryPart) {
|
public void visitOffsetFetchClause(QueryPart queryPart) {
|
||||||
if ( !isRowNumberingCurrentQueryPart() ) {
|
if ( !isRowNumberingCurrentQueryPart() ) {
|
||||||
|
|
|
@ -91,7 +91,6 @@ import org.hibernate.type.descriptor.jdbc.BlobJdbcType;
|
||||||
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
||||||
import org.hibernate.type.descriptor.jdbc.NullJdbcType;
|
import org.hibernate.type.descriptor.jdbc.NullJdbcType;
|
||||||
import org.hibernate.type.descriptor.jdbc.ObjectNullAsNullTypeJdbcType;
|
import org.hibernate.type.descriptor.jdbc.ObjectNullAsNullTypeJdbcType;
|
||||||
import org.hibernate.type.descriptor.jdbc.OracleJsonArrayBlobJdbcType;
|
|
||||||
import org.hibernate.type.descriptor.jdbc.OracleJsonBlobJdbcType;
|
import org.hibernate.type.descriptor.jdbc.OracleJsonBlobJdbcType;
|
||||||
import org.hibernate.type.descriptor.jdbc.SqlTypedJdbcType;
|
import org.hibernate.type.descriptor.jdbc.SqlTypedJdbcType;
|
||||||
import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry;
|
import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry;
|
||||||
|
@ -123,7 +122,6 @@ import static org.hibernate.type.SqlTypes.FLOAT;
|
||||||
import static org.hibernate.type.SqlTypes.GEOMETRY;
|
import static org.hibernate.type.SqlTypes.GEOMETRY;
|
||||||
import static org.hibernate.type.SqlTypes.INTEGER;
|
import static org.hibernate.type.SqlTypes.INTEGER;
|
||||||
import static org.hibernate.type.SqlTypes.JSON;
|
import static org.hibernate.type.SqlTypes.JSON;
|
||||||
import static org.hibernate.type.SqlTypes.JSON_ARRAY;
|
|
||||||
import static org.hibernate.type.SqlTypes.NUMERIC;
|
import static org.hibernate.type.SqlTypes.NUMERIC;
|
||||||
import static org.hibernate.type.SqlTypes.NVARCHAR;
|
import static org.hibernate.type.SqlTypes.NVARCHAR;
|
||||||
import static org.hibernate.type.SqlTypes.REAL;
|
import static org.hibernate.type.SqlTypes.REAL;
|
||||||
|
@ -335,6 +333,8 @@ public class OracleLegacyDialect extends Dialect {
|
||||||
functionFactory.xmlquery_oracle();
|
functionFactory.xmlquery_oracle();
|
||||||
functionFactory.xmlexists();
|
functionFactory.xmlexists();
|
||||||
functionFactory.xmlagg();
|
functionFactory.xmlagg();
|
||||||
|
|
||||||
|
functionFactory.unnest_oracle();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -745,11 +745,9 @@ public class OracleLegacyDialect extends Dialect {
|
||||||
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( GEOMETRY, "MDSYS.SDO_GEOMETRY", this ) );
|
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( GEOMETRY, "MDSYS.SDO_GEOMETRY", this ) );
|
||||||
if ( getVersion().isSameOrAfter( 21 ) ) {
|
if ( getVersion().isSameOrAfter( 21 ) ) {
|
||||||
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( JSON, "json", this ) );
|
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( JSON, "json", this ) );
|
||||||
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( JSON_ARRAY, "json", this ) );
|
|
||||||
}
|
}
|
||||||
else if ( getVersion().isSameOrAfter( 12 ) ) {
|
else if ( getVersion().isSameOrAfter( 12 ) ) {
|
||||||
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( JSON, "blob", this ) );
|
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( JSON, "blob", this ) );
|
||||||
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( JSON_ARRAY, "blob", this ) );
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -919,11 +917,11 @@ public class OracleLegacyDialect extends Dialect {
|
||||||
|
|
||||||
if ( getVersion().isSameOrAfter( 21 ) ) {
|
if ( getVersion().isSameOrAfter( 21 ) ) {
|
||||||
typeContributions.contributeJdbcType( OracleJsonJdbcType.INSTANCE );
|
typeContributions.contributeJdbcType( OracleJsonJdbcType.INSTANCE );
|
||||||
typeContributions.contributeJdbcType( OracleJsonArrayJdbcType.INSTANCE );
|
typeContributions.contributeJdbcTypeConstructor( OracleJsonArrayJdbcTypeConstructor.NATIVE_INSTANCE );
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
typeContributions.contributeJdbcType( OracleJsonBlobJdbcType.INSTANCE );
|
typeContributions.contributeJdbcType( OracleJsonBlobJdbcType.INSTANCE );
|
||||||
typeContributions.contributeJdbcType( OracleJsonArrayBlobJdbcType.INSTANCE );
|
typeContributions.contributeJdbcTypeConstructor( OracleJsonArrayJdbcTypeConstructor.BLOB_INSTANCE );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,18 +10,23 @@ import java.util.List;
|
||||||
import org.hibernate.dialect.OracleArrayJdbcType;
|
import org.hibernate.dialect.OracleArrayJdbcType;
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||||
import org.hibernate.internal.util.collections.Stack;
|
import org.hibernate.internal.util.collections.Stack;
|
||||||
|
import org.hibernate.metamodel.mapping.CollectionPart;
|
||||||
import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart;
|
import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart;
|
||||||
import org.hibernate.metamodel.mapping.EntityIdentifierMapping;
|
import org.hibernate.metamodel.mapping.EntityIdentifierMapping;
|
||||||
import org.hibernate.metamodel.mapping.EntityMappingType;
|
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||||
import org.hibernate.metamodel.mapping.JdbcMappingContainer;
|
import org.hibernate.metamodel.mapping.JdbcMappingContainer;
|
||||||
|
import org.hibernate.metamodel.mapping.ModelPart;
|
||||||
import org.hibernate.query.IllegalQueryOperationException;
|
import org.hibernate.query.IllegalQueryOperationException;
|
||||||
|
import org.hibernate.query.derived.AnonymousTupleTableGroupProducer;
|
||||||
import org.hibernate.query.sqm.ComparisonOperator;
|
import org.hibernate.query.sqm.ComparisonOperator;
|
||||||
import org.hibernate.query.sqm.FetchClauseType;
|
import org.hibernate.query.sqm.FetchClauseType;
|
||||||
import org.hibernate.query.sqm.FrameExclusion;
|
import org.hibernate.query.sqm.FrameExclusion;
|
||||||
import org.hibernate.query.sqm.FrameKind;
|
import org.hibernate.query.sqm.FrameKind;
|
||||||
import org.hibernate.sql.ast.Clause;
|
import org.hibernate.sql.ast.Clause;
|
||||||
|
import org.hibernate.sql.ast.SqlAstNodeRenderingMode;
|
||||||
import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator;
|
import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator;
|
||||||
import org.hibernate.sql.ast.spi.SqlSelection;
|
import org.hibernate.sql.ast.spi.SqlSelection;
|
||||||
|
import org.hibernate.sql.ast.tree.SqlAstNode;
|
||||||
import org.hibernate.sql.ast.tree.Statement;
|
import org.hibernate.sql.ast.tree.Statement;
|
||||||
import org.hibernate.sql.ast.tree.cte.CteMaterialization;
|
import org.hibernate.sql.ast.tree.cte.CteMaterialization;
|
||||||
import org.hibernate.sql.ast.tree.expression.CaseSearchedExpression;
|
import org.hibernate.sql.ast.tree.expression.CaseSearchedExpression;
|
||||||
|
@ -33,6 +38,7 @@ import org.hibernate.sql.ast.tree.expression.Over;
|
||||||
import org.hibernate.sql.ast.tree.expression.SqlTuple;
|
import org.hibernate.sql.ast.tree.expression.SqlTuple;
|
||||||
import org.hibernate.sql.ast.tree.expression.SqlTupleContainer;
|
import org.hibernate.sql.ast.tree.expression.SqlTupleContainer;
|
||||||
import org.hibernate.sql.ast.tree.expression.Summarization;
|
import org.hibernate.sql.ast.tree.expression.Summarization;
|
||||||
|
import org.hibernate.sql.ast.tree.from.DerivedTableReference;
|
||||||
import org.hibernate.sql.ast.tree.from.FromClause;
|
import org.hibernate.sql.ast.tree.from.FromClause;
|
||||||
import org.hibernate.sql.ast.tree.from.FunctionTableReference;
|
import org.hibernate.sql.ast.tree.from.FunctionTableReference;
|
||||||
import org.hibernate.sql.ast.tree.from.NamedTableReference;
|
import org.hibernate.sql.ast.tree.from.NamedTableReference;
|
||||||
|
@ -298,9 +304,42 @@ public class OracleLegacySqlAstTranslator<T extends JdbcOperation> extends Abstr
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visitFunctionTableReference(FunctionTableReference tableReference) {
|
public void visitFunctionTableReference(FunctionTableReference tableReference) {
|
||||||
append( "table(" );
|
|
||||||
tableReference.getFunctionExpression().accept( this );
|
tableReference.getFunctionExpression().accept( this );
|
||||||
append( CLOSE_PARENTHESIS );
|
if ( !tableReference.rendersIdentifierVariable() ) {
|
||||||
|
renderDerivedTableReferenceIdentificationVariable( tableReference );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void renderNamedSetReturningFunction(String functionName, List<? extends SqlAstNode> sqlAstArguments, AnonymousTupleTableGroupProducer tupleType, String tableIdentifierVariable, SqlAstNodeRenderingMode argumentRenderingMode) {
|
||||||
|
final ModelPart ordinalitySubPart = tupleType.findSubPart( CollectionPart.Nature.INDEX.getName(), null );
|
||||||
|
if ( ordinalitySubPart != null ) {
|
||||||
|
appendSql( "lateral (select t.*, rownum " );
|
||||||
|
appendSql( ordinalitySubPart.asBasicValuedModelPart().getSelectionExpression() );
|
||||||
|
appendSql( " from table(" );
|
||||||
|
renderSimpleNamedFunction( functionName, sqlAstArguments, argumentRenderingMode );
|
||||||
|
append( ") t)" );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
appendSql( "table(" );
|
||||||
|
super.renderNamedSetReturningFunction( functionName, sqlAstArguments, tupleType, tableIdentifierVariable, argumentRenderingMode );
|
||||||
|
append( ')' );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void renderDerivedTableReference(DerivedTableReference tableReference) {
|
||||||
|
if ( tableReference instanceof FunctionTableReference && tableReference.isLateral() ) {
|
||||||
|
// No need for a lateral keyword for functions
|
||||||
|
tableReference.accept( this );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
super.renderDerivedTableReference( tableReference );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void renderDerivedTableReferenceIdentificationVariable(DerivedTableReference tableReference) {
|
||||||
renderTableReferenceIdentificationVariable( tableReference );
|
renderTableReferenceIdentificationVariable( tableReference );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,7 @@ import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.TimeZone;
|
import java.util.TimeZone;
|
||||||
|
|
||||||
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
import org.hibernate.Length;
|
import org.hibernate.Length;
|
||||||
import org.hibernate.LockMode;
|
import org.hibernate.LockMode;
|
||||||
import org.hibernate.LockOptions;
|
import org.hibernate.LockOptions;
|
||||||
|
@ -109,7 +110,6 @@ import static org.hibernate.type.SqlTypes.GEOGRAPHY;
|
||||||
import static org.hibernate.type.SqlTypes.GEOMETRY;
|
import static org.hibernate.type.SqlTypes.GEOMETRY;
|
||||||
import static org.hibernate.type.SqlTypes.INET;
|
import static org.hibernate.type.SqlTypes.INET;
|
||||||
import static org.hibernate.type.SqlTypes.JSON;
|
import static org.hibernate.type.SqlTypes.JSON;
|
||||||
import static org.hibernate.type.SqlTypes.JSON_ARRAY;
|
|
||||||
import static org.hibernate.type.SqlTypes.LONG32NVARCHAR;
|
import static org.hibernate.type.SqlTypes.LONG32NVARCHAR;
|
||||||
import static org.hibernate.type.SqlTypes.LONG32VARBINARY;
|
import static org.hibernate.type.SqlTypes.LONG32VARBINARY;
|
||||||
import static org.hibernate.type.SqlTypes.LONG32VARCHAR;
|
import static org.hibernate.type.SqlTypes.LONG32VARCHAR;
|
||||||
|
@ -259,11 +259,9 @@ public class PostgreSQLLegacyDialect extends Dialect {
|
||||||
// Prefer jsonb if possible
|
// Prefer jsonb if possible
|
||||||
if ( getVersion().isSameOrAfter( 9, 4 ) ) {
|
if ( getVersion().isSameOrAfter( 9, 4 ) ) {
|
||||||
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( JSON, "jsonb", this ) );
|
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( JSON, "jsonb", this ) );
|
||||||
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( JSON_ARRAY, "jsonb", this ) );
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( JSON, "json", this ) );
|
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( JSON, "json", this ) );
|
||||||
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( JSON_ARRAY, "json", this ) );
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ddlTypeRegistry.addDescriptor( new NamedNativeEnumDdlTypeImpl( this ) );
|
ddlTypeRegistry.addDescriptor( new NamedNativeEnumDdlTypeImpl( this ) );
|
||||||
|
@ -706,6 +704,18 @@ public class PostgreSQLLegacyDialect extends Dialect {
|
||||||
);
|
);
|
||||||
functionContributions.getFunctionRegistry().registerAlternateKey( "truncate", "trunc" );
|
functionContributions.getFunctionRegistry().registerAlternateKey( "truncate", "trunc" );
|
||||||
functionFactory.dateTrunc();
|
functionFactory.dateTrunc();
|
||||||
|
|
||||||
|
if ( getVersion().isSameOrAfter( 17 ) ) {
|
||||||
|
functionFactory.unnest( null, "ordinality" );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
functionFactory.unnest_postgresql();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable String getDefaultOrdinalityColumnName() {
|
||||||
|
return "ordinality";
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1460,21 +1470,21 @@ public class PostgreSQLLegacyDialect extends Dialect {
|
||||||
if ( getVersion().isSameOrAfter( 9, 4 ) ) {
|
if ( getVersion().isSameOrAfter( 9, 4 ) ) {
|
||||||
if ( PgJdbcHelper.isUsable( serviceRegistry ) ) {
|
if ( PgJdbcHelper.isUsable( serviceRegistry ) ) {
|
||||||
jdbcTypeRegistry.addDescriptorIfAbsent( PgJdbcHelper.getJsonbJdbcType( serviceRegistry ) );
|
jdbcTypeRegistry.addDescriptorIfAbsent( PgJdbcHelper.getJsonbJdbcType( serviceRegistry ) );
|
||||||
jdbcTypeRegistry.addDescriptorIfAbsent( PgJdbcHelper.getJsonbArrayJdbcType( serviceRegistry ) );
|
jdbcTypeRegistry.addTypeConstructorIfAbsent( PgJdbcHelper.getJsonbArrayJdbcType( serviceRegistry ) );
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
jdbcTypeRegistry.addDescriptorIfAbsent( PostgreSQLCastingJsonJdbcType.JSONB_INSTANCE );
|
jdbcTypeRegistry.addDescriptorIfAbsent( PostgreSQLCastingJsonJdbcType.JSONB_INSTANCE );
|
||||||
jdbcTypeRegistry.addDescriptorIfAbsent( PostgreSQLCastingJsonArrayJdbcType.JSONB_INSTANCE );
|
jdbcTypeRegistry.addTypeConstructorIfAbsent( PostgreSQLCastingJsonArrayJdbcTypeConstructor.JSONB_INSTANCE );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if ( PgJdbcHelper.isUsable( serviceRegistry ) ) {
|
if ( PgJdbcHelper.isUsable( serviceRegistry ) ) {
|
||||||
jdbcTypeRegistry.addDescriptorIfAbsent( PgJdbcHelper.getJsonJdbcType( serviceRegistry ) );
|
jdbcTypeRegistry.addDescriptorIfAbsent( PgJdbcHelper.getJsonJdbcType( serviceRegistry ) );
|
||||||
jdbcTypeRegistry.addDescriptorIfAbsent( PgJdbcHelper.getJsonArrayJdbcType( serviceRegistry ) );
|
jdbcTypeRegistry.addTypeConstructorIfAbsent( PgJdbcHelper.getJsonArrayJdbcType( serviceRegistry ) );
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
jdbcTypeRegistry.addDescriptorIfAbsent( PostgreSQLCastingJsonJdbcType.JSON_INSTANCE );
|
jdbcTypeRegistry.addDescriptorIfAbsent( PostgreSQLCastingJsonJdbcType.JSON_INSTANCE );
|
||||||
jdbcTypeRegistry.addDescriptorIfAbsent( PostgreSQLCastingJsonArrayJdbcType.JSON_INSTANCE );
|
jdbcTypeRegistry.addTypeConstructorIfAbsent( PostgreSQLCastingJsonArrayJdbcTypeConstructor.JSON_INSTANCE );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1490,11 +1500,11 @@ public class PostgreSQLLegacyDialect extends Dialect {
|
||||||
if ( getVersion().isSameOrAfter( 9, 2 ) ) {
|
if ( getVersion().isSameOrAfter( 9, 2 ) ) {
|
||||||
if ( getVersion().isSameOrAfter( 9, 4 ) ) {
|
if ( getVersion().isSameOrAfter( 9, 4 ) ) {
|
||||||
jdbcTypeRegistry.addDescriptorIfAbsent( PostgreSQLCastingJsonJdbcType.JSONB_INSTANCE );
|
jdbcTypeRegistry.addDescriptorIfAbsent( PostgreSQLCastingJsonJdbcType.JSONB_INSTANCE );
|
||||||
jdbcTypeRegistry.addDescriptorIfAbsent( PostgreSQLCastingJsonArrayJdbcType.JSONB_INSTANCE );
|
jdbcTypeRegistry.addTypeConstructorIfAbsent( PostgreSQLCastingJsonArrayJdbcTypeConstructor.JSONB_INSTANCE );
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
jdbcTypeRegistry.addDescriptorIfAbsent( PostgreSQLCastingJsonJdbcType.JSON_INSTANCE );
|
jdbcTypeRegistry.addDescriptorIfAbsent( PostgreSQLCastingJsonJdbcType.JSON_INSTANCE );
|
||||||
jdbcTypeRegistry.addDescriptorIfAbsent( PostgreSQLCastingJsonArrayJdbcType.JSON_INSTANCE );
|
jdbcTypeRegistry.addTypeConstructorIfAbsent( PostgreSQLCastingJsonArrayJdbcTypeConstructor.JSON_INSTANCE );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1513,6 +1523,7 @@ public class PostgreSQLLegacyDialect extends Dialect {
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Replace the standard array constructor
|
||||||
jdbcTypeRegistry.addTypeConstructor( PostgreSQLArrayJdbcTypeConstructor.INSTANCE );
|
jdbcTypeRegistry.addTypeConstructor( PostgreSQLArrayJdbcTypeConstructor.INSTANCE );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,8 @@ import org.hibernate.dialect.DatabaseVersion;
|
||||||
import org.hibernate.dialect.Dialect;
|
import org.hibernate.dialect.Dialect;
|
||||||
import org.hibernate.dialect.DmlTargetColumnQualifierSupport;
|
import org.hibernate.dialect.DmlTargetColumnQualifierSupport;
|
||||||
import org.hibernate.dialect.Replacer;
|
import org.hibernate.dialect.Replacer;
|
||||||
|
import org.hibernate.dialect.SQLServerCastingXmlArrayJdbcTypeConstructor;
|
||||||
|
import org.hibernate.dialect.SQLServerCastingXmlJdbcType;
|
||||||
import org.hibernate.dialect.TimeZoneSupport;
|
import org.hibernate.dialect.TimeZoneSupport;
|
||||||
import org.hibernate.dialect.function.CommonFunctionFactory;
|
import org.hibernate.dialect.function.CommonFunctionFactory;
|
||||||
import org.hibernate.dialect.function.CountFunction;
|
import org.hibernate.dialect.function.CountFunction;
|
||||||
|
@ -74,7 +76,6 @@ import org.hibernate.type.descriptor.jdbc.JdbcType;
|
||||||
import org.hibernate.type.descriptor.jdbc.TimestampUtcAsJdbcTimestampJdbcType;
|
import org.hibernate.type.descriptor.jdbc.TimestampUtcAsJdbcTimestampJdbcType;
|
||||||
import org.hibernate.type.descriptor.jdbc.TinyIntAsSmallIntJdbcType;
|
import org.hibernate.type.descriptor.jdbc.TinyIntAsSmallIntJdbcType;
|
||||||
import org.hibernate.type.descriptor.jdbc.UUIDJdbcType;
|
import org.hibernate.type.descriptor.jdbc.UUIDJdbcType;
|
||||||
import org.hibernate.type.descriptor.jdbc.XmlJdbcType;
|
|
||||||
import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry;
|
import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry;
|
||||||
import org.hibernate.type.descriptor.sql.internal.DdlTypeImpl;
|
import org.hibernate.type.descriptor.sql.internal.DdlTypeImpl;
|
||||||
import org.hibernate.type.descriptor.sql.spi.DdlTypeRegistry;
|
import org.hibernate.type.descriptor.sql.spi.DdlTypeRegistry;
|
||||||
|
@ -244,6 +245,11 @@ public class SQLServerLegacyDialect extends AbstractTransactSQLDialect {
|
||||||
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( UUID, "uniqueidentifier", this ) );
|
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( UUID, "uniqueidentifier", this ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getPreferredSqlTypeCodeForArray() {
|
||||||
|
return XML_ARRAY;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public JdbcType resolveSqlTypeDescriptor(
|
public JdbcType resolveSqlTypeDescriptor(
|
||||||
String columnTypeName,
|
String columnTypeName,
|
||||||
|
@ -308,8 +314,9 @@ public class SQLServerLegacyDialect extends AbstractTransactSQLDialect {
|
||||||
Types.TINYINT,
|
Types.TINYINT,
|
||||||
TinyIntAsSmallIntJdbcType.INSTANCE
|
TinyIntAsSmallIntJdbcType.INSTANCE
|
||||||
);
|
);
|
||||||
typeContributions.contributeJdbcType( XmlJdbcType.INSTANCE );
|
typeContributions.contributeJdbcType( SQLServerCastingXmlJdbcType.INSTANCE );
|
||||||
typeContributions.contributeJdbcType( UUIDJdbcType.INSTANCE );
|
typeContributions.contributeJdbcType( UUIDJdbcType.INSTANCE );
|
||||||
|
typeContributions.contributeJdbcTypeConstructor( SQLServerCastingXmlArrayJdbcTypeConstructor.INSTANCE );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -421,6 +428,9 @@ public class SQLServerLegacyDialect extends AbstractTransactSQLDialect {
|
||||||
functionFactory.xmlquery_sqlserver();
|
functionFactory.xmlquery_sqlserver();
|
||||||
functionFactory.xmlexists_sqlserver();
|
functionFactory.xmlexists_sqlserver();
|
||||||
functionFactory.xmlagg_sqlserver();
|
functionFactory.xmlagg_sqlserver();
|
||||||
|
|
||||||
|
functionFactory.unnest_sqlserver();
|
||||||
|
|
||||||
if ( getVersion().isSameOrAfter( 14 ) ) {
|
if ( getVersion().isSameOrAfter( 14 ) ) {
|
||||||
functionFactory.listagg_stringAggWithinGroup( "varchar(max)" );
|
functionFactory.listagg_stringAggWithinGroup( "varchar(max)" );
|
||||||
functionFactory.jsonArrayAgg_sqlserver( getVersion().isSameOrAfter( 16 ) );
|
functionFactory.jsonArrayAgg_sqlserver( getVersion().isSameOrAfter( 16 ) );
|
||||||
|
|
|
@ -11,14 +11,19 @@ import org.hibernate.LockOptions;
|
||||||
import org.hibernate.dialect.DatabaseVersion;
|
import org.hibernate.dialect.DatabaseVersion;
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||||
import org.hibernate.internal.util.collections.Stack;
|
import org.hibernate.internal.util.collections.Stack;
|
||||||
|
import org.hibernate.metamodel.mapping.CollectionPart;
|
||||||
import org.hibernate.metamodel.mapping.JdbcMappingContainer;
|
import org.hibernate.metamodel.mapping.JdbcMappingContainer;
|
||||||
|
import org.hibernate.metamodel.mapping.ModelPart;
|
||||||
import org.hibernate.query.IllegalQueryOperationException;
|
import org.hibernate.query.IllegalQueryOperationException;
|
||||||
|
import org.hibernate.query.derived.AnonymousTupleTableGroupProducer;
|
||||||
import org.hibernate.query.sqm.ComparisonOperator;
|
import org.hibernate.query.sqm.ComparisonOperator;
|
||||||
import org.hibernate.query.sqm.FetchClauseType;
|
import org.hibernate.query.sqm.FetchClauseType;
|
||||||
import org.hibernate.sql.ast.Clause;
|
import org.hibernate.sql.ast.Clause;
|
||||||
import org.hibernate.sql.ast.SqlAstJoinType;
|
import org.hibernate.sql.ast.SqlAstJoinType;
|
||||||
|
import org.hibernate.sql.ast.SqlAstNodeRenderingMode;
|
||||||
import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator;
|
import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator;
|
||||||
import org.hibernate.sql.ast.spi.SqlSelection;
|
import org.hibernate.sql.ast.spi.SqlSelection;
|
||||||
|
import org.hibernate.sql.ast.tree.SqlAstNode;
|
||||||
import org.hibernate.sql.ast.tree.Statement;
|
import org.hibernate.sql.ast.tree.Statement;
|
||||||
import org.hibernate.sql.ast.tree.delete.DeleteStatement;
|
import org.hibernate.sql.ast.tree.delete.DeleteStatement;
|
||||||
import org.hibernate.sql.ast.tree.expression.BinaryArithmeticExpression;
|
import org.hibernate.sql.ast.tree.expression.BinaryArithmeticExpression;
|
||||||
|
@ -26,10 +31,9 @@ import org.hibernate.sql.ast.tree.expression.Expression;
|
||||||
import org.hibernate.sql.ast.tree.expression.Literal;
|
import org.hibernate.sql.ast.tree.expression.Literal;
|
||||||
import org.hibernate.sql.ast.tree.expression.SqlTuple;
|
import org.hibernate.sql.ast.tree.expression.SqlTuple;
|
||||||
import org.hibernate.sql.ast.tree.expression.Summarization;
|
import org.hibernate.sql.ast.tree.expression.Summarization;
|
||||||
|
import org.hibernate.sql.ast.tree.from.DerivedTableReference;
|
||||||
import org.hibernate.sql.ast.tree.from.NamedTableReference;
|
import org.hibernate.sql.ast.tree.from.NamedTableReference;
|
||||||
import org.hibernate.sql.ast.tree.from.TableGroup;
|
|
||||||
import org.hibernate.sql.ast.tree.from.TableGroupJoin;
|
import org.hibernate.sql.ast.tree.from.TableGroupJoin;
|
||||||
import org.hibernate.sql.ast.tree.from.TableReference;
|
|
||||||
import org.hibernate.sql.ast.tree.from.UnionTableReference;
|
import org.hibernate.sql.ast.tree.from.UnionTableReference;
|
||||||
import org.hibernate.sql.ast.tree.insert.ConflictClause;
|
import org.hibernate.sql.ast.tree.insert.ConflictClause;
|
||||||
import org.hibernate.sql.ast.tree.insert.InsertSelectStatement;
|
import org.hibernate.sql.ast.tree.insert.InsertSelectStatement;
|
||||||
|
@ -180,17 +184,24 @@ public class SQLServerLegacySqlAstTranslator<T extends JdbcOperation> extends Ab
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected boolean renderPrimaryTableReference(TableGroup tableGroup, LockMode lockMode) {
|
@Override
|
||||||
if ( shouldInlineCte( tableGroup ) ) {
|
protected void renderDerivedTableReference(DerivedTableReference tableReference) {
|
||||||
inlineCteTableGroup( tableGroup, lockMode );
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
final TableReference tableReference = tableGroup.getPrimaryTableReference();
|
|
||||||
if ( tableReference instanceof NamedTableReference ) {
|
|
||||||
return renderNamedTableReference( (NamedTableReference) tableReference, lockMode );
|
|
||||||
}
|
|
||||||
tableReference.accept( this );
|
tableReference.accept( this );
|
||||||
return false;
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void renderNamedSetReturningFunction(String functionName, List<? extends SqlAstNode> sqlAstArguments, AnonymousTupleTableGroupProducer tupleType, String tableIdentifierVariable, SqlAstNodeRenderingMode argumentRenderingMode) {
|
||||||
|
final ModelPart ordinalitySubPart = tupleType.findSubPart( CollectionPart.Nature.INDEX.getName(), null );
|
||||||
|
if ( ordinalitySubPart != null ) {
|
||||||
|
appendSql( "(select t.*, row_number() over(order by (select 1)) " );
|
||||||
|
appendSql( ordinalitySubPart.asBasicValuedModelPart().getSelectionExpression() );
|
||||||
|
appendSql( " from " );
|
||||||
|
renderSimpleNamedFunction( functionName, sqlAstArguments, argumentRenderingMode );
|
||||||
|
append( " t)" );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
super.renderNamedSetReturningFunction( functionName, sqlAstArguments, tupleType, tableIdentifierVariable, argumentRenderingMode );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -12,10 +12,12 @@ import java.sql.Types;
|
||||||
import org.hibernate.LockMode;
|
import org.hibernate.LockMode;
|
||||||
import org.hibernate.LockOptions;
|
import org.hibernate.LockOptions;
|
||||||
import org.hibernate.QueryTimeoutException;
|
import org.hibernate.QueryTimeoutException;
|
||||||
|
import org.hibernate.boot.model.FunctionContributions;
|
||||||
import org.hibernate.boot.model.TypeContributions;
|
import org.hibernate.boot.model.TypeContributions;
|
||||||
import org.hibernate.dialect.DatabaseVersion;
|
import org.hibernate.dialect.DatabaseVersion;
|
||||||
import org.hibernate.dialect.Dialect;
|
import org.hibernate.dialect.Dialect;
|
||||||
import org.hibernate.dialect.SybaseDriverKind;
|
import org.hibernate.dialect.SybaseDriverKind;
|
||||||
|
import org.hibernate.dialect.function.CommonFunctionFactory;
|
||||||
import org.hibernate.dialect.pagination.LimitHandler;
|
import org.hibernate.dialect.pagination.LimitHandler;
|
||||||
import org.hibernate.dialect.pagination.TopLimitHandler;
|
import org.hibernate.dialect.pagination.TopLimitHandler;
|
||||||
import org.hibernate.engine.jdbc.Size;
|
import org.hibernate.engine.jdbc.Size;
|
||||||
|
@ -52,6 +54,7 @@ import static org.hibernate.type.SqlTypes.DATE;
|
||||||
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;
|
||||||
import static org.hibernate.type.SqlTypes.TIMESTAMP_WITH_TIMEZONE;
|
import static org.hibernate.type.SqlTypes.TIMESTAMP_WITH_TIMEZONE;
|
||||||
|
import static org.hibernate.type.SqlTypes.XML_ARRAY;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A {@linkplain Dialect SQL dialect} for Sybase Adaptive Server Enterprise 11.9 and above.
|
* A {@linkplain Dialect SQL dialect} for Sybase Adaptive Server Enterprise 11.9 and above.
|
||||||
|
@ -140,6 +143,11 @@ public class SybaseASELegacyDialect extends SybaseLegacyDialect {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getPreferredSqlTypeCodeForArray() {
|
||||||
|
return XML_ARRAY;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getMaxVarcharLength() {
|
public int getMaxVarcharLength() {
|
||||||
// the maximum length of a VARCHAR or VARBINARY
|
// the maximum length of a VARCHAR or VARBINARY
|
||||||
|
@ -151,6 +159,15 @@ public class SybaseASELegacyDialect extends SybaseLegacyDialect {
|
||||||
return 16_384;
|
return 16_384;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initializeFunctionRegistry(FunctionContributions functionContributions) {
|
||||||
|
super.initializeFunctionRegistry( functionContributions );
|
||||||
|
|
||||||
|
CommonFunctionFactory functionFactory = new CommonFunctionFactory( functionContributions);
|
||||||
|
|
||||||
|
functionFactory.unnest_sybasease();
|
||||||
|
}
|
||||||
|
|
||||||
private static boolean isAnsiNull(DatabaseMetaData databaseMetaData) {
|
private static boolean isAnsiNull(DatabaseMetaData databaseMetaData) {
|
||||||
if ( databaseMetaData != null ) {
|
if ( databaseMetaData != null ) {
|
||||||
try (java.sql.Statement s = databaseMetaData.getConnection().createStatement() ) {
|
try (java.sql.Statement s = databaseMetaData.getConnection().createStatement() ) {
|
||||||
|
|
|
@ -48,6 +48,8 @@ import org.hibernate.sql.ast.tree.select.SelectClause;
|
||||||
import org.hibernate.sql.ast.tree.update.UpdateStatement;
|
import org.hibernate.sql.ast.tree.update.UpdateStatement;
|
||||||
import org.hibernate.sql.exec.spi.JdbcOperation;
|
import org.hibernate.sql.exec.spi.JdbcOperation;
|
||||||
|
|
||||||
|
import static org.hibernate.dialect.SybaseASESqlAstTranslator.isLob;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A SQL AST translator for Sybase ASE.
|
* A SQL AST translator for Sybase ASE.
|
||||||
*
|
*
|
||||||
|
@ -336,7 +338,7 @@ public class SybaseASELegacySqlAstTranslator<T extends JdbcOperation> extends Ab
|
||||||
append( '(' );
|
append( '(' );
|
||||||
visitValuesListEmulateSelectUnion( tableReference.getValuesList() );
|
visitValuesListEmulateSelectUnion( tableReference.getValuesList() );
|
||||||
append( ')' );
|
append( ')' );
|
||||||
renderDerivedTableReference( tableReference );
|
renderDerivedTableReferenceIdentificationVariable( tableReference );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -371,8 +373,56 @@ public class SybaseASELegacySqlAstTranslator<T extends JdbcOperation> extends Ab
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void renderComparison(Expression lhs, ComparisonOperator operator, Expression rhs) {
|
protected void renderComparison(Expression lhs, ComparisonOperator operator, Expression rhs) {
|
||||||
|
// In Sybase ASE, XMLTYPE is not "comparable", so we have to cast the two parts to varchar for this purpose
|
||||||
|
final boolean isLob = isLob( lhs.getExpressionType() );
|
||||||
|
if ( isLob ) {
|
||||||
|
switch ( operator ) {
|
||||||
|
case EQUAL:
|
||||||
|
lhs.accept( this );
|
||||||
|
appendSql( " like " );
|
||||||
|
rhs.accept( this );
|
||||||
|
return;
|
||||||
|
case NOT_EQUAL:
|
||||||
|
lhs.accept( this );
|
||||||
|
appendSql( " not like " );
|
||||||
|
rhs.accept( this );
|
||||||
|
return;
|
||||||
|
default:
|
||||||
|
// Fall through
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
// I think intersect is only supported in 16.0 SP3
|
// I think intersect is only supported in 16.0 SP3
|
||||||
if ( getDialect().isAnsiNullOn() ) {
|
if ( getDialect().isAnsiNullOn() ) {
|
||||||
|
if ( isLob ) {
|
||||||
|
switch ( operator ) {
|
||||||
|
case DISTINCT_FROM:
|
||||||
|
appendSql( "case when " );
|
||||||
|
lhs.accept( this );
|
||||||
|
appendSql( " like " );
|
||||||
|
rhs.accept( this );
|
||||||
|
appendSql( " or " );
|
||||||
|
lhs.accept( this );
|
||||||
|
appendSql( " is null and " );
|
||||||
|
rhs.accept( this );
|
||||||
|
appendSql( " is null then 0 else 1 end=1" );
|
||||||
|
return;
|
||||||
|
case NOT_DISTINCT_FROM:
|
||||||
|
appendSql( "case when " );
|
||||||
|
lhs.accept( this );
|
||||||
|
appendSql( " like " );
|
||||||
|
rhs.accept( this );
|
||||||
|
appendSql( " or " );
|
||||||
|
lhs.accept( this );
|
||||||
|
appendSql( " is null and " );
|
||||||
|
rhs.accept( this );
|
||||||
|
appendSql( " is null then 0 else 1 end=0" );
|
||||||
|
return;
|
||||||
|
default:
|
||||||
|
// Fall through
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
if ( supportsDistinctFromPredicate() ) {
|
if ( supportsDistinctFromPredicate() ) {
|
||||||
renderComparisonEmulateIntersect( lhs, operator, rhs );
|
renderComparisonEmulateIntersect( lhs, operator, rhs );
|
||||||
}
|
}
|
||||||
|
@ -393,10 +443,20 @@ public class SybaseASELegacySqlAstTranslator<T extends JdbcOperation> extends Ab
|
||||||
lhs.accept( this );
|
lhs.accept( this );
|
||||||
switch ( operator ) {
|
switch ( operator ) {
|
||||||
case DISTINCT_FROM:
|
case DISTINCT_FROM:
|
||||||
|
if ( isLob ) {
|
||||||
|
appendSql( " not like " );
|
||||||
|
}
|
||||||
|
else {
|
||||||
appendSql( "<>" );
|
appendSql( "<>" );
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case NOT_DISTINCT_FROM:
|
case NOT_DISTINCT_FROM:
|
||||||
|
if ( isLob ) {
|
||||||
|
appendSql( " like " );
|
||||||
|
}
|
||||||
|
else {
|
||||||
appendSql( '=' );
|
appendSql( '=' );
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case LESS_THAN:
|
case LESS_THAN:
|
||||||
case GREATER_THAN:
|
case GREATER_THAN:
|
||||||
|
|
|
@ -204,7 +204,7 @@ public class SybaseLegacySqlAstTranslator<T extends JdbcOperation> extends Abstr
|
||||||
append( '(' );
|
append( '(' );
|
||||||
visitValuesListEmulateSelectUnion( tableReference.getValuesList() );
|
visitValuesListEmulateSelectUnion( tableReference.getValuesList() );
|
||||||
append( ')' );
|
append( ')' );
|
||||||
renderDerivedTableReference( tableReference );
|
renderDerivedTableReferenceIdentificationVariable( tableReference );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -225,6 +225,7 @@ entityWithJoins
|
||||||
fromRoot
|
fromRoot
|
||||||
: entityName variable? # RootEntity
|
: entityName variable? # RootEntity
|
||||||
| LEFT_PAREN subquery RIGHT_PAREN variable? # RootSubquery
|
| LEFT_PAREN subquery RIGHT_PAREN variable? # RootSubquery
|
||||||
|
| setReturningFunction variable? # RootFunction
|
||||||
;
|
;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -275,8 +276,9 @@ joinType
|
||||||
* The joined path, with an optional identification variable
|
* The joined path, with an optional identification variable
|
||||||
*/
|
*/
|
||||||
joinTarget
|
joinTarget
|
||||||
: path variable? #JoinPath
|
: path variable? # JoinPath
|
||||||
| LATERAL? LEFT_PAREN subquery RIGHT_PAREN variable? #JoinSubquery
|
| LATERAL? LEFT_PAREN subquery RIGHT_PAREN variable? # JoinSubquery
|
||||||
|
| LATERAL? setReturningFunction variable? # JoinFunction
|
||||||
;
|
;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1114,6 +1116,17 @@ function
|
||||||
| genericFunction
|
| genericFunction
|
||||||
;
|
;
|
||||||
|
|
||||||
|
setReturningFunction
|
||||||
|
: simpleSetReturningFunction
|
||||||
|
;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A simple set returning function invocation without special syntax.
|
||||||
|
*/
|
||||||
|
simpleSetReturningFunction
|
||||||
|
: identifier LEFT_PAREN genericFunctionArguments? RIGHT_PAREN
|
||||||
|
;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A syntax for calling user-defined or native database functions, required by JPQL
|
* A syntax for calling user-defined or native database functions, required by JPQL
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -85,8 +85,11 @@ import org.hibernate.type.descriptor.java.ByteArrayJavaType;
|
||||||
import org.hibernate.type.descriptor.java.CharacterArrayJavaType;
|
import org.hibernate.type.descriptor.java.CharacterArrayJavaType;
|
||||||
import org.hibernate.type.descriptor.java.spi.JavaTypeRegistry;
|
import org.hibernate.type.descriptor.java.spi.JavaTypeRegistry;
|
||||||
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
||||||
import org.hibernate.type.descriptor.jdbc.JsonArrayAsStringJdbcType;
|
import org.hibernate.type.descriptor.jdbc.JsonArrayJdbcTypeConstructor;
|
||||||
|
import org.hibernate.type.descriptor.jdbc.JsonAsStringArrayJdbcTypeConstructor;
|
||||||
import org.hibernate.type.descriptor.jdbc.JsonAsStringJdbcType;
|
import org.hibernate.type.descriptor.jdbc.JsonAsStringJdbcType;
|
||||||
|
import org.hibernate.type.descriptor.jdbc.XmlArrayJdbcTypeConstructor;
|
||||||
|
import org.hibernate.type.descriptor.jdbc.XmlAsStringArrayJdbcTypeConstructor;
|
||||||
import org.hibernate.type.descriptor.jdbc.XmlAsStringJdbcType;
|
import org.hibernate.type.descriptor.jdbc.XmlAsStringJdbcType;
|
||||||
import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry;
|
import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry;
|
||||||
import org.hibernate.type.descriptor.sql.DdlType;
|
import org.hibernate.type.descriptor.sql.DdlType;
|
||||||
|
@ -103,7 +106,6 @@ import org.jboss.jandex.Indexer;
|
||||||
import jakarta.persistence.AttributeConverter;
|
import jakarta.persistence.AttributeConverter;
|
||||||
|
|
||||||
import static org.hibernate.internal.util.collections.CollectionHelper.mutableJoin;
|
import static org.hibernate.internal.util.collections.CollectionHelper.mutableJoin;
|
||||||
import static org.hibernate.internal.util.config.ConfigurationHelper.getPreferredSqlTypeCodeForArray;
|
|
||||||
import static org.hibernate.internal.util.config.ConfigurationHelper.getPreferredSqlTypeCodeForDuration;
|
import static org.hibernate.internal.util.config.ConfigurationHelper.getPreferredSqlTypeCodeForDuration;
|
||||||
import static org.hibernate.internal.util.config.ConfigurationHelper.getPreferredSqlTypeCodeForInstant;
|
import static org.hibernate.internal.util.config.ConfigurationHelper.getPreferredSqlTypeCodeForInstant;
|
||||||
import static org.hibernate.internal.util.config.ConfigurationHelper.getPreferredSqlTypeCodeForUuid;
|
import static org.hibernate.internal.util.config.ConfigurationHelper.getPreferredSqlTypeCodeForUuid;
|
||||||
|
@ -763,9 +765,6 @@ public class MetadataBuildingProcess {
|
||||||
// add Dialect contributed types
|
// add Dialect contributed types
|
||||||
final Dialect dialect = options.getServiceRegistry().requireService( JdbcServices.class ).getDialect();
|
final Dialect dialect = options.getServiceRegistry().requireService( JdbcServices.class ).getDialect();
|
||||||
dialect.contribute( typeContributions, options.getServiceRegistry() );
|
dialect.contribute( typeContributions, options.getServiceRegistry() );
|
||||||
// Capture the dialect configured JdbcTypes so that we can detect if a TypeContributor overwrote them,
|
|
||||||
// which has precedence over the fallback and preferred type registrations
|
|
||||||
final JdbcType dialectArrayDescriptor = jdbcTypeRegistry.findDescriptor( SqlTypes.ARRAY );
|
|
||||||
|
|
||||||
// add TypeContributor contributed types.
|
// add TypeContributor contributed types.
|
||||||
for ( TypeContributor contributor : classLoaderService.loadJavaServices( TypeContributor.class ) ) {
|
for ( TypeContributor contributor : classLoaderService.loadJavaServices( TypeContributor.class ) ) {
|
||||||
|
@ -790,17 +789,23 @@ public class MetadataBuildingProcess {
|
||||||
addFallbackIfNecessary( jdbcTypeRegistry, SqlTypes.UUID, SqlTypes.BINARY );
|
addFallbackIfNecessary( jdbcTypeRegistry, SqlTypes.UUID, SqlTypes.BINARY );
|
||||||
}
|
}
|
||||||
|
|
||||||
final int preferredSqlTypeCodeForArray = getPreferredSqlTypeCodeForArray( serviceRegistry );
|
jdbcTypeRegistry.addDescriptorIfAbsent( JsonAsStringJdbcType.VARCHAR_INSTANCE );
|
||||||
if ( preferredSqlTypeCodeForArray != SqlTypes.ARRAY ) {
|
jdbcTypeRegistry.addDescriptorIfAbsent( XmlAsStringJdbcType.VARCHAR_INSTANCE );
|
||||||
adaptToPreferredSqlTypeCode(
|
if ( jdbcTypeRegistry.getConstructor( SqlTypes.JSON_ARRAY ) == null ) {
|
||||||
jdbcTypeRegistry,
|
if ( jdbcTypeRegistry.getDescriptor( SqlTypes.JSON ).getDdlTypeCode() == SqlTypes.JSON ) {
|
||||||
dialectArrayDescriptor,
|
jdbcTypeRegistry.addTypeConstructor( JsonArrayJdbcTypeConstructor.INSTANCE );
|
||||||
SqlTypes.ARRAY,
|
|
||||||
preferredSqlTypeCodeForArray
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
addFallbackIfNecessary( jdbcTypeRegistry, SqlTypes.ARRAY, SqlTypes.VARBINARY );
|
jdbcTypeRegistry.addTypeConstructor( JsonAsStringArrayJdbcTypeConstructor.INSTANCE );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ( jdbcTypeRegistry.getConstructor( SqlTypes.XML_ARRAY ) == null ) {
|
||||||
|
if ( jdbcTypeRegistry.getDescriptor( SqlTypes.SQLXML ).getDdlTypeCode() == SqlTypes.SQLXML ) {
|
||||||
|
jdbcTypeRegistry.addTypeConstructor( XmlArrayJdbcTypeConstructor.INSTANCE );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
jdbcTypeRegistry.addTypeConstructor( XmlAsStringArrayJdbcTypeConstructor.INSTANCE );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final int preferredSqlTypeCodeForDuration = getPreferredSqlTypeCodeForDuration( serviceRegistry );
|
final int preferredSqlTypeCodeForDuration = getPreferredSqlTypeCodeForDuration( serviceRegistry );
|
||||||
|
@ -823,10 +828,6 @@ public class MetadataBuildingProcess {
|
||||||
addFallbackIfNecessary( jdbcTypeRegistry, SqlTypes.POINT, SqlTypes.VARBINARY );
|
addFallbackIfNecessary( jdbcTypeRegistry, SqlTypes.POINT, SqlTypes.VARBINARY );
|
||||||
addFallbackIfNecessary( jdbcTypeRegistry, SqlTypes.GEOGRAPHY, SqlTypes.GEOMETRY );
|
addFallbackIfNecessary( jdbcTypeRegistry, SqlTypes.GEOGRAPHY, SqlTypes.GEOMETRY );
|
||||||
|
|
||||||
jdbcTypeRegistry.addDescriptorIfAbsent( JsonAsStringJdbcType.VARCHAR_INSTANCE );
|
|
||||||
jdbcTypeRegistry.addDescriptorIfAbsent( JsonArrayAsStringJdbcType.VARCHAR_INSTANCE );
|
|
||||||
jdbcTypeRegistry.addDescriptorIfAbsent( XmlAsStringJdbcType.VARCHAR_INSTANCE );
|
|
||||||
|
|
||||||
addFallbackIfNecessary( jdbcTypeRegistry, SqlTypes.MATERIALIZED_BLOB, SqlTypes.BLOB );
|
addFallbackIfNecessary( jdbcTypeRegistry, SqlTypes.MATERIALIZED_BLOB, SqlTypes.BLOB );
|
||||||
addFallbackIfNecessary( jdbcTypeRegistry, SqlTypes.MATERIALIZED_CLOB, SqlTypes.CLOB );
|
addFallbackIfNecessary( jdbcTypeRegistry, SqlTypes.MATERIALIZED_CLOB, SqlTypes.CLOB );
|
||||||
addFallbackIfNecessary( jdbcTypeRegistry, SqlTypes.MATERIALIZED_NCLOB, SqlTypes.NCLOB );
|
addFallbackIfNecessary( jdbcTypeRegistry, SqlTypes.MATERIALIZED_NCLOB, SqlTypes.NCLOB );
|
||||||
|
|
|
@ -19,6 +19,7 @@ import java.util.TimeZone;
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
import org.hibernate.LockMode;
|
import org.hibernate.LockMode;
|
||||||
import org.hibernate.LockOptions;
|
import org.hibernate.LockOptions;
|
||||||
import org.hibernate.PessimisticLockException;
|
import org.hibernate.PessimisticLockException;
|
||||||
|
@ -95,7 +96,6 @@ import static org.hibernate.type.SqlTypes.GEOMETRY;
|
||||||
import static org.hibernate.type.SqlTypes.INET;
|
import static org.hibernate.type.SqlTypes.INET;
|
||||||
import static org.hibernate.type.SqlTypes.INTEGER;
|
import static org.hibernate.type.SqlTypes.INTEGER;
|
||||||
import static org.hibernate.type.SqlTypes.JSON;
|
import static org.hibernate.type.SqlTypes.JSON;
|
||||||
import static org.hibernate.type.SqlTypes.JSON_ARRAY;
|
|
||||||
import static org.hibernate.type.SqlTypes.LONG32NVARCHAR;
|
import static org.hibernate.type.SqlTypes.LONG32NVARCHAR;
|
||||||
import static org.hibernate.type.SqlTypes.LONG32VARBINARY;
|
import static org.hibernate.type.SqlTypes.LONG32VARBINARY;
|
||||||
import static org.hibernate.type.SqlTypes.LONG32VARCHAR;
|
import static org.hibernate.type.SqlTypes.LONG32VARCHAR;
|
||||||
|
@ -260,7 +260,6 @@ public class CockroachDialect extends Dialect {
|
||||||
// Prefer jsonb if possible
|
// Prefer jsonb if possible
|
||||||
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( INET, "inet", this ) );
|
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( INET, "inet", this ) );
|
||||||
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( JSON, "jsonb", this ) );
|
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( JSON, "jsonb", this ) );
|
||||||
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( JSON_ARRAY, "jsonb", this ) );
|
|
||||||
|
|
||||||
ddlTypeRegistry.addDescriptor( new NamedNativeEnumDdlTypeImpl( this ) );
|
ddlTypeRegistry.addDescriptor( new NamedNativeEnumDdlTypeImpl( this ) );
|
||||||
ddlTypeRegistry.addDescriptor( new NamedNativeOrdinalEnumDdlTypeImpl( this ) );
|
ddlTypeRegistry.addDescriptor( new NamedNativeOrdinalEnumDdlTypeImpl( this ) );
|
||||||
|
@ -356,13 +355,13 @@ public class CockroachDialect extends Dialect {
|
||||||
jdbcTypeRegistry.addDescriptorIfAbsent( PgJdbcHelper.getIntervalJdbcType( serviceRegistry ) );
|
jdbcTypeRegistry.addDescriptorIfAbsent( PgJdbcHelper.getIntervalJdbcType( serviceRegistry ) );
|
||||||
jdbcTypeRegistry.addDescriptorIfAbsent( PgJdbcHelper.getInetJdbcType( serviceRegistry ) );
|
jdbcTypeRegistry.addDescriptorIfAbsent( PgJdbcHelper.getInetJdbcType( serviceRegistry ) );
|
||||||
jdbcTypeRegistry.addDescriptorIfAbsent( PgJdbcHelper.getJsonbJdbcType( serviceRegistry ) );
|
jdbcTypeRegistry.addDescriptorIfAbsent( PgJdbcHelper.getJsonbJdbcType( serviceRegistry ) );
|
||||||
jdbcTypeRegistry.addDescriptorIfAbsent( PgJdbcHelper.getJsonbArrayJdbcType( serviceRegistry ) );
|
jdbcTypeRegistry.addTypeConstructorIfAbsent( PgJdbcHelper.getJsonbArrayJdbcType( serviceRegistry ) );
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
jdbcTypeRegistry.addDescriptorIfAbsent( PostgreSQLCastingIntervalSecondJdbcType.INSTANCE );
|
jdbcTypeRegistry.addDescriptorIfAbsent( PostgreSQLCastingIntervalSecondJdbcType.INSTANCE );
|
||||||
jdbcTypeRegistry.addDescriptorIfAbsent( PostgreSQLCastingInetJdbcType.INSTANCE );
|
jdbcTypeRegistry.addDescriptorIfAbsent( PostgreSQLCastingInetJdbcType.INSTANCE );
|
||||||
jdbcTypeRegistry.addDescriptorIfAbsent( PostgreSQLCastingJsonJdbcType.JSONB_INSTANCE );
|
jdbcTypeRegistry.addDescriptorIfAbsent( PostgreSQLCastingJsonJdbcType.JSONB_INSTANCE );
|
||||||
jdbcTypeRegistry.addDescriptorIfAbsent( PostgreSQLCastingJsonArrayJdbcType.JSONB_INSTANCE );
|
jdbcTypeRegistry.addTypeConstructorIfAbsent( PostgreSQLCastingJsonArrayJdbcTypeConstructor.JSONB_INSTANCE );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -370,7 +369,7 @@ public class CockroachDialect extends Dialect {
|
||||||
jdbcTypeRegistry.addDescriptorIfAbsent( PostgreSQLCastingInetJdbcType.INSTANCE );
|
jdbcTypeRegistry.addDescriptorIfAbsent( PostgreSQLCastingInetJdbcType.INSTANCE );
|
||||||
jdbcTypeRegistry.addDescriptorIfAbsent( PostgreSQLCastingIntervalSecondJdbcType.INSTANCE );
|
jdbcTypeRegistry.addDescriptorIfAbsent( PostgreSQLCastingIntervalSecondJdbcType.INSTANCE );
|
||||||
jdbcTypeRegistry.addDescriptorIfAbsent( PostgreSQLCastingJsonJdbcType.JSONB_INSTANCE );
|
jdbcTypeRegistry.addDescriptorIfAbsent( PostgreSQLCastingJsonJdbcType.JSONB_INSTANCE );
|
||||||
jdbcTypeRegistry.addDescriptorIfAbsent( PostgreSQLCastingJsonArrayJdbcType.JSONB_INSTANCE );
|
jdbcTypeRegistry.addTypeConstructorIfAbsent( PostgreSQLCastingJsonArrayJdbcTypeConstructor.JSONB_INSTANCE );
|
||||||
}
|
}
|
||||||
|
|
||||||
// Force Blob binding to byte[] for CockroachDB
|
// Force Blob binding to byte[] for CockroachDB
|
||||||
|
@ -391,6 +390,7 @@ public class CockroachDialect extends Dialect {
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Replace the standard array constructor
|
||||||
jdbcTypeRegistry.addTypeConstructor( PostgreSQLArrayJdbcTypeConstructor.INSTANCE );
|
jdbcTypeRegistry.addTypeConstructor( PostgreSQLArrayJdbcTypeConstructor.INSTANCE );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -485,6 +485,8 @@ public class CockroachDialect extends Dialect {
|
||||||
functionFactory.jsonArrayAppend_postgresql( false );
|
functionFactory.jsonArrayAppend_postgresql( false );
|
||||||
functionFactory.jsonArrayInsert_postgresql();
|
functionFactory.jsonArrayInsert_postgresql();
|
||||||
|
|
||||||
|
functionFactory.unnest_postgresql();
|
||||||
|
|
||||||
// Postgres uses # instead of ^ for XOR
|
// Postgres uses # instead of ^ for XOR
|
||||||
functionContributions.getFunctionRegistry().patternDescriptorBuilder( "bitxor", "(?1#?2)" )
|
functionContributions.getFunctionRegistry().patternDescriptorBuilder( "bitxor", "(?1#?2)" )
|
||||||
.setExactArgumentCount( 2 )
|
.setExactArgumentCount( 2 )
|
||||||
|
@ -498,6 +500,11 @@ public class CockroachDialect extends Dialect {
|
||||||
functionContributions.getFunctionRegistry().registerAlternateKey( "truncate", "trunc" );
|
functionContributions.getFunctionRegistry().registerAlternateKey( "truncate", "trunc" );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable String getDefaultOrdinalityColumnName() {
|
||||||
|
return "ordinality";
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TimeZoneSupport getTimeZoneSupport() {
|
public TimeZoneSupport getTimeZoneSupport() {
|
||||||
return TimeZoneSupport.NORMALIZE;
|
return TimeZoneSupport.NORMALIZE;
|
||||||
|
|
|
@ -78,6 +78,7 @@ import org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorDB
|
||||||
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.type.JavaObjectType;
|
import org.hibernate.type.JavaObjectType;
|
||||||
|
import org.hibernate.type.SqlTypes;
|
||||||
import org.hibernate.type.StandardBasicTypes;
|
import org.hibernate.type.StandardBasicTypes;
|
||||||
import org.hibernate.type.descriptor.ValueExtractor;
|
import org.hibernate.type.descriptor.ValueExtractor;
|
||||||
import org.hibernate.type.descriptor.java.JavaType;
|
import org.hibernate.type.descriptor.java.JavaType;
|
||||||
|
@ -440,6 +441,14 @@ public class DB2Dialect extends Dialect {
|
||||||
functionFactory.xmlexists_db2_legacy();
|
functionFactory.xmlexists_db2_legacy();
|
||||||
}
|
}
|
||||||
functionFactory.xmlagg();
|
functionFactory.xmlagg();
|
||||||
|
|
||||||
|
functionFactory.unnest_emulated();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getPreferredSqlTypeCodeForArray() {
|
||||||
|
// Even if DB2 11 supports JSON functions, it's not possible to unnest a JSON array to rows, so stick to XML
|
||||||
|
return SqlTypes.XML_ARRAY;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -9,8 +9,11 @@ import java.util.function.Consumer;
|
||||||
|
|
||||||
import org.hibernate.LockMode;
|
import org.hibernate.LockMode;
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||||
|
import org.hibernate.metamodel.mapping.CollectionPart;
|
||||||
import org.hibernate.metamodel.mapping.JdbcMappingContainer;
|
import org.hibernate.metamodel.mapping.JdbcMappingContainer;
|
||||||
|
import org.hibernate.metamodel.mapping.ModelPart;
|
||||||
import org.hibernate.query.IllegalQueryOperationException;
|
import org.hibernate.query.IllegalQueryOperationException;
|
||||||
|
import org.hibernate.query.derived.AnonymousTupleTableGroupProducer;
|
||||||
import org.hibernate.query.sqm.ComparisonOperator;
|
import org.hibernate.query.sqm.ComparisonOperator;
|
||||||
import org.hibernate.query.sqm.FetchClauseType;
|
import org.hibernate.query.sqm.FetchClauseType;
|
||||||
import org.hibernate.sql.ast.Clause;
|
import org.hibernate.sql.ast.Clause;
|
||||||
|
@ -18,6 +21,7 @@ import org.hibernate.sql.ast.SqlAstNodeRenderingMode;
|
||||||
import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator;
|
import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator;
|
||||||
import org.hibernate.sql.ast.spi.SqlSelection;
|
import org.hibernate.sql.ast.spi.SqlSelection;
|
||||||
import org.hibernate.sql.ast.tree.MutationStatement;
|
import org.hibernate.sql.ast.tree.MutationStatement;
|
||||||
|
import org.hibernate.sql.ast.tree.SqlAstNode;
|
||||||
import org.hibernate.sql.ast.tree.Statement;
|
import org.hibernate.sql.ast.tree.Statement;
|
||||||
import org.hibernate.sql.ast.tree.delete.DeleteStatement;
|
import org.hibernate.sql.ast.tree.delete.DeleteStatement;
|
||||||
import org.hibernate.sql.ast.tree.expression.CaseSearchedExpression;
|
import org.hibernate.sql.ast.tree.expression.CaseSearchedExpression;
|
||||||
|
@ -26,6 +30,8 @@ import org.hibernate.sql.ast.tree.expression.ColumnReference;
|
||||||
import org.hibernate.sql.ast.tree.expression.Expression;
|
import org.hibernate.sql.ast.tree.expression.Expression;
|
||||||
import org.hibernate.sql.ast.tree.expression.Literal;
|
import org.hibernate.sql.ast.tree.expression.Literal;
|
||||||
import org.hibernate.sql.ast.tree.expression.SqlTuple;
|
import org.hibernate.sql.ast.tree.expression.SqlTuple;
|
||||||
|
import org.hibernate.sql.ast.tree.from.DerivedTableReference;
|
||||||
|
import org.hibernate.sql.ast.tree.from.FunctionTableReference;
|
||||||
import org.hibernate.sql.ast.tree.from.NamedTableReference;
|
import org.hibernate.sql.ast.tree.from.NamedTableReference;
|
||||||
import org.hibernate.sql.ast.tree.from.QueryPartTableReference;
|
import org.hibernate.sql.ast.tree.from.QueryPartTableReference;
|
||||||
import org.hibernate.sql.ast.tree.from.TableGroup;
|
import org.hibernate.sql.ast.tree.from.TableGroup;
|
||||||
|
@ -252,6 +258,34 @@ public class DB2SqlAstTranslator<T extends JdbcOperation> extends AbstractSqlAst
|
||||||
inLateral = oldLateral;
|
inLateral = oldLateral;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void renderDerivedTableReference(DerivedTableReference tableReference) {
|
||||||
|
if ( tableReference instanceof FunctionTableReference && tableReference.isLateral() ) {
|
||||||
|
// No need for a lateral keyword for functions
|
||||||
|
tableReference.accept( this );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
super.renderDerivedTableReference( tableReference );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void renderNamedSetReturningFunction(String functionName, List<? extends SqlAstNode> sqlAstArguments, AnonymousTupleTableGroupProducer tupleType, String tableIdentifierVariable, SqlAstNodeRenderingMode argumentRenderingMode) {
|
||||||
|
final ModelPart ordinalitySubPart = tupleType.findSubPart( CollectionPart.Nature.INDEX.getName(), null );
|
||||||
|
if ( ordinalitySubPart != null ) {
|
||||||
|
appendSql( "lateral (select t.*, row_number() over() " );
|
||||||
|
appendSql( ordinalitySubPart.asBasicValuedModelPart().getSelectionExpression() );
|
||||||
|
appendSql( " from table(" );
|
||||||
|
renderSimpleNamedFunction( functionName, sqlAstArguments, argumentRenderingMode );
|
||||||
|
append( ") t)" );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
appendSql( "table(" );
|
||||||
|
super.renderNamedSetReturningFunction( functionName, sqlAstArguments, tupleType, tableIdentifierVariable, argumentRenderingMode );
|
||||||
|
append( ')' );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visitSelectStatement(SelectStatement statement) {
|
public void visitSelectStatement(SelectStatement statement) {
|
||||||
if ( getQueryPartForRowNumbering() == statement.getQueryPart() && inLateral ) {
|
if ( getQueryPartForRowNumbering() == statement.getQueryPart() && inLateral ) {
|
||||||
|
|
|
@ -63,6 +63,11 @@ public class DB2StructJdbcType implements StructJdbcType {
|
||||||
return SqlTypes.SQLXML;
|
return SqlTypes.SQLXML;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getDdlTypeCode() {
|
||||||
|
return SqlTypes.SQLXML;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getDefaultSqlTypeCode() {
|
public int getDefaultSqlTypeCode() {
|
||||||
return SqlTypes.STRUCT;
|
return SqlTypes.STRUCT;
|
||||||
|
|
|
@ -4,15 +4,11 @@
|
||||||
*/
|
*/
|
||||||
package org.hibernate.dialect;
|
package org.hibernate.dialect;
|
||||||
|
|
||||||
import org.hibernate.LockMode;
|
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||||
import org.hibernate.query.sqm.ComparisonOperator;
|
import org.hibernate.query.sqm.ComparisonOperator;
|
||||||
import org.hibernate.sql.ast.tree.Statement;
|
import org.hibernate.sql.ast.tree.Statement;
|
||||||
import org.hibernate.sql.ast.tree.expression.Expression;
|
import org.hibernate.sql.ast.tree.expression.Expression;
|
||||||
import org.hibernate.sql.ast.tree.from.FunctionTableReference;
|
import org.hibernate.sql.ast.tree.from.QueryPartTableReference;
|
||||||
import org.hibernate.sql.ast.tree.from.NamedTableReference;
|
|
||||||
import org.hibernate.sql.ast.tree.from.TableGroup;
|
|
||||||
import org.hibernate.sql.ast.tree.from.TableReference;
|
|
||||||
import org.hibernate.sql.ast.tree.select.QueryPart;
|
import org.hibernate.sql.ast.tree.select.QueryPart;
|
||||||
import org.hibernate.sql.exec.spi.JdbcOperation;
|
import org.hibernate.sql.exec.spi.JdbcOperation;
|
||||||
|
|
||||||
|
@ -51,28 +47,10 @@ public class DB2zSqlAstTranslator<T extends JdbcOperation> extends DB2SqlAstTran
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean renderPrimaryTableReference(TableGroup tableGroup, LockMode lockMode) {
|
public void visitQueryPartTableReference(QueryPartTableReference tableReference) {
|
||||||
if ( shouldInlineCte( tableGroup ) ) {
|
|
||||||
inlineCteTableGroup( tableGroup, lockMode );
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
final TableReference tableReference = tableGroup.getPrimaryTableReference();
|
|
||||||
if ( tableReference instanceof NamedTableReference ) {
|
|
||||||
return renderNamedTableReference( (NamedTableReference) tableReference, lockMode );
|
|
||||||
}
|
|
||||||
// DB2 z/OS we need the "table" qualifier for table valued functions or lateral sub-queries
|
// DB2 z/OS we need the "table" qualifier for table valued functions or lateral sub-queries
|
||||||
append( "table " );
|
append( "table " );
|
||||||
tableReference.accept( this );
|
super.visitQueryPartTableReference( tableReference );
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void visitFunctionTableReference(FunctionTableReference tableReference) {
|
|
||||||
// For the table qualifier we need parenthesis on DB2 z/OS
|
|
||||||
append( OPEN_PARENTHESIS );
|
|
||||||
tableReference.getFunctionExpression().accept( this );
|
|
||||||
append( CLOSE_PARENTHESIS );
|
|
||||||
renderDerivedTableReference( tableReference );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -1871,7 +1871,7 @@ public abstract class Dialect implements ConversionContext, TypeContributor, Fun
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( supportsStandardArrays() ) {
|
if ( supportsStandardArrays() ) {
|
||||||
jdbcTypeRegistry.addTypeConstructor( ArrayJdbcTypeConstructor.INSTANCE );
|
jdbcTypeRegistry.addTypeConstructorIfAbsent( ArrayJdbcTypeConstructor.INSTANCE );
|
||||||
}
|
}
|
||||||
if ( supportsMaterializedLobAccess() ) {
|
if ( supportsMaterializedLobAccess() ) {
|
||||||
jdbcTypeRegistry.addDescriptor( SqlTypes.MATERIALIZED_BLOB, BlobJdbcType.MATERIALIZED );
|
jdbcTypeRegistry.addDescriptor( SqlTypes.MATERIALIZED_BLOB, BlobJdbcType.MATERIALIZED );
|
||||||
|
@ -5290,6 +5290,14 @@ public abstract class Dialect implements ConversionContext, TypeContributor, Fun
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the default name of the ordinality column for a set-returning function
|
||||||
|
* if it supports that, otherwise returns {@code null}.
|
||||||
|
*/
|
||||||
|
public @Nullable String getDefaultOrdinalityColumnName() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pluggable strategy for determining the {@link Size} to use for
|
* Pluggable strategy for determining the {@link Size} to use for
|
||||||
* columns of a given SQL type.
|
* columns of a given SQL type.
|
||||||
|
|
|
@ -14,6 +14,7 @@ import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.TimeZone;
|
import java.util.TimeZone;
|
||||||
|
|
||||||
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
import org.hibernate.PessimisticLockException;
|
import org.hibernate.PessimisticLockException;
|
||||||
import org.hibernate.QueryTimeoutException;
|
import org.hibernate.QueryTimeoutException;
|
||||||
import org.hibernate.boot.model.FunctionContributions;
|
import org.hibernate.boot.model.FunctionContributions;
|
||||||
|
@ -89,7 +90,6 @@ import static org.hibernate.type.SqlTypes.FLOAT;
|
||||||
import static org.hibernate.type.SqlTypes.GEOMETRY;
|
import static org.hibernate.type.SqlTypes.GEOMETRY;
|
||||||
import static org.hibernate.type.SqlTypes.INTERVAL_SECOND;
|
import static org.hibernate.type.SqlTypes.INTERVAL_SECOND;
|
||||||
import static org.hibernate.type.SqlTypes.JSON;
|
import static org.hibernate.type.SqlTypes.JSON;
|
||||||
import static org.hibernate.type.SqlTypes.JSON_ARRAY;
|
|
||||||
import static org.hibernate.type.SqlTypes.LONG32NVARCHAR;
|
import static org.hibernate.type.SqlTypes.LONG32NVARCHAR;
|
||||||
import static org.hibernate.type.SqlTypes.LONG32VARBINARY;
|
import static org.hibernate.type.SqlTypes.LONG32VARBINARY;
|
||||||
import static org.hibernate.type.SqlTypes.LONG32VARCHAR;
|
import static org.hibernate.type.SqlTypes.LONG32VARCHAR;
|
||||||
|
@ -229,7 +229,6 @@ public class H2Dialect extends Dialect {
|
||||||
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( GEOMETRY, "geometry", this ) );
|
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( GEOMETRY, "geometry", this ) );
|
||||||
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( INTERVAL_SECOND, "interval second($p,$s)", this ) );
|
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( INTERVAL_SECOND, "interval second($p,$s)", this ) );
|
||||||
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( JSON, "json", this ) );
|
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( JSON, "json", this ) );
|
||||||
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( JSON_ARRAY, "json", this ) );
|
|
||||||
ddlTypeRegistry.addDescriptor( new NativeEnumDdlTypeImpl( this ) );
|
ddlTypeRegistry.addDescriptor( new NativeEnumDdlTypeImpl( this ) );
|
||||||
ddlTypeRegistry.addDescriptor( new NativeOrdinalEnumDdlTypeImpl( this ) );
|
ddlTypeRegistry.addDescriptor( new NativeOrdinalEnumDdlTypeImpl( this ) );
|
||||||
}
|
}
|
||||||
|
@ -246,7 +245,8 @@ public class H2Dialect extends Dialect {
|
||||||
jdbcTypeRegistry.addDescriptorIfAbsent( UUIDJdbcType.INSTANCE );
|
jdbcTypeRegistry.addDescriptorIfAbsent( UUIDJdbcType.INSTANCE );
|
||||||
jdbcTypeRegistry.addDescriptorIfAbsent( H2DurationIntervalSecondJdbcType.INSTANCE );
|
jdbcTypeRegistry.addDescriptorIfAbsent( H2DurationIntervalSecondJdbcType.INSTANCE );
|
||||||
jdbcTypeRegistry.addDescriptorIfAbsent( H2JsonJdbcType.INSTANCE );
|
jdbcTypeRegistry.addDescriptorIfAbsent( H2JsonJdbcType.INSTANCE );
|
||||||
jdbcTypeRegistry.addDescriptorIfAbsent( H2JsonArrayJdbcType.INSTANCE );
|
// Replace the standard array constructor
|
||||||
|
jdbcTypeRegistry.addTypeConstructor( H2JsonArrayJdbcTypeConstructor.INSTANCE );
|
||||||
jdbcTypeRegistry.addDescriptor( EnumJdbcType.INSTANCE );
|
jdbcTypeRegistry.addDescriptor( EnumJdbcType.INSTANCE );
|
||||||
jdbcTypeRegistry.addDescriptor( OrdinalEnumJdbcType.INSTANCE );
|
jdbcTypeRegistry.addDescriptor( OrdinalEnumJdbcType.INSTANCE );
|
||||||
}
|
}
|
||||||
|
@ -358,6 +358,8 @@ public class H2Dialect extends Dialect {
|
||||||
functionFactory.xmlforest_h2();
|
functionFactory.xmlforest_h2();
|
||||||
functionFactory.xmlconcat_h2();
|
functionFactory.xmlconcat_h2();
|
||||||
functionFactory.xmlpi_h2();
|
functionFactory.xmlpi_h2();
|
||||||
|
|
||||||
|
functionFactory.unnest_h2( getMaximumArraySize() );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -370,6 +372,11 @@ public class H2Dialect extends Dialect {
|
||||||
return 1000;
|
return 1000;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable String getDefaultOrdinalityColumnName() {
|
||||||
|
return "nord";
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void augmentPhysicalTableTypes(List<String> tableTypesList) {
|
public void augmentPhysicalTableTypes(List<String> tableTypesList) {
|
||||||
tableTypesList.add( "BASE TABLE" );
|
tableTypesList.add( "BASE TABLE" );
|
||||||
|
|
|
@ -13,18 +13,16 @@ import org.hibernate.type.descriptor.ValueBinder;
|
||||||
import org.hibernate.type.descriptor.WrapperOptions;
|
import org.hibernate.type.descriptor.WrapperOptions;
|
||||||
import org.hibernate.type.descriptor.java.JavaType;
|
import org.hibernate.type.descriptor.java.JavaType;
|
||||||
import org.hibernate.type.descriptor.jdbc.BasicBinder;
|
import org.hibernate.type.descriptor.jdbc.BasicBinder;
|
||||||
|
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
||||||
import org.hibernate.type.descriptor.jdbc.JsonArrayJdbcType;
|
import org.hibernate.type.descriptor.jdbc.JsonArrayJdbcType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* H2 requires binding JSON via {@code setBytes} methods.
|
* H2 requires binding JSON via {@code setBytes} methods.
|
||||||
*/
|
*/
|
||||||
public class H2JsonArrayJdbcType extends JsonArrayJdbcType {
|
public class H2JsonArrayJdbcType extends JsonArrayJdbcType {
|
||||||
/**
|
|
||||||
* Singleton access
|
|
||||||
*/
|
|
||||||
public static final H2JsonArrayJdbcType INSTANCE = new H2JsonArrayJdbcType();
|
|
||||||
|
|
||||||
protected H2JsonArrayJdbcType() {
|
public H2JsonArrayJdbcType(JdbcType elementJdbcType) {
|
||||||
|
super( elementJdbcType );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -0,0 +1,43 @@
|
||||||
|
/*
|
||||||
|
* SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
|
* Copyright Red Hat Inc. and Hibernate Authors
|
||||||
|
*/
|
||||||
|
package org.hibernate.dialect;
|
||||||
|
|
||||||
|
import org.hibernate.tool.schema.extract.spi.ColumnTypeInformation;
|
||||||
|
import org.hibernate.type.BasicType;
|
||||||
|
import org.hibernate.type.SqlTypes;
|
||||||
|
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
||||||
|
import org.hibernate.type.descriptor.jdbc.JdbcTypeConstructor;
|
||||||
|
import org.hibernate.type.spi.TypeConfiguration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Factory for {@link H2JsonArrayJdbcType}.
|
||||||
|
*/
|
||||||
|
public class H2JsonArrayJdbcTypeConstructor implements JdbcTypeConstructor {
|
||||||
|
|
||||||
|
public static final H2JsonArrayJdbcTypeConstructor INSTANCE = new H2JsonArrayJdbcTypeConstructor();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JdbcType resolveType(
|
||||||
|
TypeConfiguration typeConfiguration,
|
||||||
|
Dialect dialect,
|
||||||
|
BasicType<?> elementType,
|
||||||
|
ColumnTypeInformation columnTypeInformation) {
|
||||||
|
return resolveType( typeConfiguration, dialect, elementType.getJdbcType(), columnTypeInformation );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JdbcType resolveType(
|
||||||
|
TypeConfiguration typeConfiguration,
|
||||||
|
Dialect dialect,
|
||||||
|
JdbcType elementType,
|
||||||
|
ColumnTypeInformation columnTypeInformation) {
|
||||||
|
return new H2JsonArrayJdbcType( elementType );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getDefaultSqlTypeCode() {
|
||||||
|
return SqlTypes.JSON_ARRAY;
|
||||||
|
}
|
||||||
|
}
|
|
@ -66,6 +66,7 @@ import org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorHA
|
||||||
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.internal.StandardTableExporter;
|
||||||
import org.hibernate.tool.schema.spi.Exporter;
|
import org.hibernate.tool.schema.spi.Exporter;
|
||||||
|
import org.hibernate.type.SqlTypes;
|
||||||
import org.hibernate.type.StandardBasicTypes;
|
import org.hibernate.type.StandardBasicTypes;
|
||||||
import org.hibernate.type.descriptor.ValueBinder;
|
import org.hibernate.type.descriptor.ValueBinder;
|
||||||
import org.hibernate.type.descriptor.ValueExtractor;
|
import org.hibernate.type.descriptor.ValueExtractor;
|
||||||
|
@ -413,6 +414,12 @@ public class HANADialect extends Dialect {
|
||||||
return 5000;
|
return 5000;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getPreferredSqlTypeCodeForArray() {
|
||||||
|
// Prefer XML since JSON was only added later
|
||||||
|
return getVersion().isSameOrAfter( 2 ) ? SqlTypes.XML_ARRAY : super.getPreferredSqlTypeCodeForArray();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void initializeFunctionRegistry(FunctionContributions functionContributions) {
|
public void initializeFunctionRegistry(FunctionContributions functionContributions) {
|
||||||
super.initializeFunctionRegistry(functionContributions);
|
super.initializeFunctionRegistry(functionContributions);
|
||||||
|
@ -490,15 +497,23 @@ public class HANADialect extends Dialect {
|
||||||
typeConfiguration
|
typeConfiguration
|
||||||
);
|
);
|
||||||
|
|
||||||
// Introduced in 2.0 SPS 02
|
// Introduced in 2.0 SPS 00
|
||||||
functionFactory.jsonValue_no_passing();
|
functionFactory.jsonValue_no_passing();
|
||||||
functionFactory.jsonQuery_no_passing();
|
functionFactory.jsonQuery_no_passing();
|
||||||
functionFactory.jsonExists_hana();
|
functionFactory.jsonExists_hana();
|
||||||
|
|
||||||
|
functionFactory.unnest_hana();
|
||||||
|
// functionFactory.json_table();
|
||||||
|
|
||||||
// Introduced in 2.0 SPS 04
|
// Introduced in 2.0 SPS 04
|
||||||
functionFactory.jsonObject_hana();
|
functionFactory.jsonObject_hana();
|
||||||
functionFactory.jsonArray_hana();
|
functionFactory.jsonArray_hana();
|
||||||
functionFactory.jsonArrayAgg_hana();
|
functionFactory.jsonArrayAgg_hana();
|
||||||
functionFactory.jsonObjectAgg_hana();
|
functionFactory.jsonObjectAgg_hana();
|
||||||
|
|
||||||
|
// functionFactory.xmltable();
|
||||||
|
|
||||||
|
// functionFactory.xmlextract();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -9,17 +9,22 @@ import java.util.List;
|
||||||
import org.hibernate.MappingException;
|
import org.hibernate.MappingException;
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||||
import org.hibernate.internal.util.collections.Stack;
|
import org.hibernate.internal.util.collections.Stack;
|
||||||
|
import org.hibernate.metamodel.mapping.CollectionPart;
|
||||||
|
import org.hibernate.metamodel.mapping.ModelPart;
|
||||||
import org.hibernate.query.IllegalQueryOperationException;
|
import org.hibernate.query.IllegalQueryOperationException;
|
||||||
|
import org.hibernate.query.derived.AnonymousTupleTableGroupProducer;
|
||||||
import org.hibernate.query.sqm.ComparisonOperator;
|
import org.hibernate.query.sqm.ComparisonOperator;
|
||||||
import org.hibernate.sql.ast.Clause;
|
import org.hibernate.sql.ast.Clause;
|
||||||
import org.hibernate.sql.ast.SqlAstNodeRenderingMode;
|
import org.hibernate.sql.ast.SqlAstNodeRenderingMode;
|
||||||
import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator;
|
import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator;
|
||||||
|
import org.hibernate.sql.ast.tree.SqlAstNode;
|
||||||
import org.hibernate.sql.ast.tree.Statement;
|
import org.hibernate.sql.ast.tree.Statement;
|
||||||
import org.hibernate.sql.ast.tree.cte.CteStatement;
|
import org.hibernate.sql.ast.tree.cte.CteStatement;
|
||||||
import org.hibernate.sql.ast.tree.expression.BinaryArithmeticExpression;
|
import org.hibernate.sql.ast.tree.expression.BinaryArithmeticExpression;
|
||||||
import org.hibernate.sql.ast.tree.expression.Expression;
|
import org.hibernate.sql.ast.tree.expression.Expression;
|
||||||
import org.hibernate.sql.ast.tree.expression.Literal;
|
import org.hibernate.sql.ast.tree.expression.Literal;
|
||||||
import org.hibernate.sql.ast.tree.expression.Summarization;
|
import org.hibernate.sql.ast.tree.expression.Summarization;
|
||||||
|
import org.hibernate.sql.ast.tree.from.DerivedTableReference;
|
||||||
import org.hibernate.sql.ast.tree.from.FunctionTableReference;
|
import org.hibernate.sql.ast.tree.from.FunctionTableReference;
|
||||||
import org.hibernate.sql.ast.tree.from.NamedTableReference;
|
import org.hibernate.sql.ast.tree.from.NamedTableReference;
|
||||||
import org.hibernate.sql.ast.tree.from.QueryPartTableReference;
|
import org.hibernate.sql.ast.tree.from.QueryPartTableReference;
|
||||||
|
@ -34,6 +39,8 @@ import org.hibernate.sql.ast.tree.update.UpdateStatement;
|
||||||
import org.hibernate.sql.exec.spi.JdbcOperation;
|
import org.hibernate.sql.exec.spi.JdbcOperation;
|
||||||
import org.hibernate.sql.model.internal.TableInsertStandard;
|
import org.hibernate.sql.model.internal.TableInsertStandard;
|
||||||
|
|
||||||
|
import static org.hibernate.dialect.SybaseASESqlAstTranslator.isLob;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An SQL AST translator for HANA.
|
* An SQL AST translator for HANA.
|
||||||
*
|
*
|
||||||
|
@ -193,6 +200,32 @@ public class HANASqlAstTranslator<T extends JdbcOperation> extends AbstractSqlAs
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void renderDerivedTableReference(DerivedTableReference tableReference) {
|
||||||
|
if ( tableReference instanceof FunctionTableReference && tableReference.isLateral() ) {
|
||||||
|
// No need for a lateral keyword for functions
|
||||||
|
tableReference.accept( this );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
super.renderDerivedTableReference( tableReference );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void renderNamedSetReturningFunction(String functionName, List<? extends SqlAstNode> sqlAstArguments, AnonymousTupleTableGroupProducer tupleType, String tableIdentifierVariable, SqlAstNodeRenderingMode argumentRenderingMode) {
|
||||||
|
final ModelPart ordinalitySubPart = tupleType.findSubPart( CollectionPart.Nature.INDEX.getName(), null );
|
||||||
|
if ( ordinalitySubPart != null ) {
|
||||||
|
appendSql( "(select t.*, row_number() over() " );
|
||||||
|
appendSql( ordinalitySubPart.asBasicValuedModelPart().getSelectionExpression() );
|
||||||
|
appendSql( " from " );
|
||||||
|
renderSimpleNamedFunction( functionName, sqlAstArguments, argumentRenderingMode );
|
||||||
|
append( " t)" );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
super.renderNamedSetReturningFunction( functionName, sqlAstArguments, tupleType, tableIdentifierVariable, argumentRenderingMode );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected SqlAstNodeRenderingMode getParameterRenderingMode() {
|
protected SqlAstNodeRenderingMode getParameterRenderingMode() {
|
||||||
// HANA does not support parameters in lateral subqueries for some reason, so inline all the parameters in this case
|
// HANA does not support parameters in lateral subqueries for some reason, so inline all the parameters in this case
|
||||||
|
@ -200,8 +233,7 @@ public class HANASqlAstTranslator<T extends JdbcOperation> extends AbstractSqlAs
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visitFunctionTableReference(FunctionTableReference tableReference) {
|
protected void renderDerivedTableReferenceIdentificationVariable(DerivedTableReference tableReference) {
|
||||||
tableReference.getFunctionExpression().accept( this );
|
|
||||||
renderTableReferenceIdentificationVariable( tableReference );
|
renderTableReferenceIdentificationVariable( tableReference );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -214,7 +246,38 @@ public class HANASqlAstTranslator<T extends JdbcOperation> extends AbstractSqlAs
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void renderComparison(Expression lhs, ComparisonOperator operator, Expression rhs) {
|
protected void renderComparison(Expression lhs, ComparisonOperator operator, Expression rhs) {
|
||||||
|
// In SAP HANA, LOBs are not "comparable", so we have to use a like predicate for comparison
|
||||||
|
final boolean isLob = isLob( lhs.getExpressionType() );
|
||||||
if ( operator == ComparisonOperator.DISTINCT_FROM || operator == ComparisonOperator.NOT_DISTINCT_FROM ) {
|
if ( operator == ComparisonOperator.DISTINCT_FROM || operator == ComparisonOperator.NOT_DISTINCT_FROM ) {
|
||||||
|
if ( isLob ) {
|
||||||
|
switch ( operator ) {
|
||||||
|
case DISTINCT_FROM:
|
||||||
|
appendSql( "case when " );
|
||||||
|
lhs.accept( this );
|
||||||
|
appendSql( " like " );
|
||||||
|
rhs.accept( this );
|
||||||
|
appendSql( " or " );
|
||||||
|
lhs.accept( this );
|
||||||
|
appendSql( " is null and " );
|
||||||
|
rhs.accept( this );
|
||||||
|
appendSql( " is null then 0 else 1 end=1" );
|
||||||
|
return;
|
||||||
|
case NOT_DISTINCT_FROM:
|
||||||
|
appendSql( "case when " );
|
||||||
|
lhs.accept( this );
|
||||||
|
appendSql( " like " );
|
||||||
|
rhs.accept( this );
|
||||||
|
appendSql( " or " );
|
||||||
|
lhs.accept( this );
|
||||||
|
appendSql( " is null and " );
|
||||||
|
rhs.accept( this );
|
||||||
|
appendSql( " is null then 0 else 1 end=0" );
|
||||||
|
return;
|
||||||
|
default:
|
||||||
|
// Fall through
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
// HANA does not support plain parameters in the select clause of the intersect emulation
|
// HANA does not support plain parameters in the select clause of the intersect emulation
|
||||||
withParameterRenderingMode(
|
withParameterRenderingMode(
|
||||||
SqlAstNodeRenderingMode.NO_PLAIN_PARAMETER,
|
SqlAstNodeRenderingMode.NO_PLAIN_PARAMETER,
|
||||||
|
@ -222,7 +285,24 @@ public class HANASqlAstTranslator<T extends JdbcOperation> extends AbstractSqlAs
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
renderComparisonEmulateIntersect( lhs, operator, rhs );
|
if ( isLob ) {
|
||||||
|
switch ( operator ) {
|
||||||
|
case EQUAL:
|
||||||
|
lhs.accept( this );
|
||||||
|
appendSql( " like " );
|
||||||
|
rhs.accept( this );
|
||||||
|
return;
|
||||||
|
case NOT_EQUAL:
|
||||||
|
lhs.accept( this );
|
||||||
|
appendSql( " not like " );
|
||||||
|
rhs.accept( this );
|
||||||
|
return;
|
||||||
|
default:
|
||||||
|
// Fall through
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
renderComparisonStandard( lhs, operator, rhs );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@ import java.sql.DatabaseMetaData;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import java.sql.Types;
|
import java.sql.Types;
|
||||||
|
|
||||||
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
import org.hibernate.boot.model.FunctionContributions;
|
import org.hibernate.boot.model.FunctionContributions;
|
||||||
import org.hibernate.dialect.function.CommonFunctionFactory;
|
import org.hibernate.dialect.function.CommonFunctionFactory;
|
||||||
import org.hibernate.dialect.function.TrimFunction;
|
import org.hibernate.dialect.function.TrimFunction;
|
||||||
|
@ -212,6 +213,8 @@ public class HSQLDialect extends Dialect {
|
||||||
functionFactory.jsonObjectAgg_h2();
|
functionFactory.jsonObjectAgg_h2();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
functionFactory.unnest( "c1", "c2" );
|
||||||
|
|
||||||
//trim() requires parameters to be cast when used as trim character
|
//trim() requires parameters to be cast when used as trim character
|
||||||
functionContributions.getFunctionRegistry().register( "trim", new TrimFunction(
|
functionContributions.getFunctionRegistry().register( "trim", new TrimFunction(
|
||||||
this,
|
this,
|
||||||
|
@ -220,6 +223,11 @@ public class HSQLDialect extends Dialect {
|
||||||
) );
|
) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable String getDefaultOrdinalityColumnName() {
|
||||||
|
return "c2";
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String currentTime() {
|
public String currentTime() {
|
||||||
return "localtime";
|
return "localtime";
|
||||||
|
|
|
@ -22,6 +22,8 @@ import org.hibernate.sql.ast.tree.expression.Expression;
|
||||||
import org.hibernate.sql.ast.tree.expression.Literal;
|
import org.hibernate.sql.ast.tree.expression.Literal;
|
||||||
import org.hibernate.sql.ast.tree.expression.SqlTuple;
|
import org.hibernate.sql.ast.tree.expression.SqlTuple;
|
||||||
import org.hibernate.sql.ast.tree.expression.Summarization;
|
import org.hibernate.sql.ast.tree.expression.Summarization;
|
||||||
|
import org.hibernate.sql.ast.tree.from.DerivedTableReference;
|
||||||
|
import org.hibernate.sql.ast.tree.from.FunctionTableReference;
|
||||||
import org.hibernate.sql.ast.tree.from.NamedTableReference;
|
import org.hibernate.sql.ast.tree.from.NamedTableReference;
|
||||||
import org.hibernate.sql.ast.tree.insert.ConflictClause;
|
import org.hibernate.sql.ast.tree.insert.ConflictClause;
|
||||||
import org.hibernate.sql.ast.tree.insert.InsertSelectStatement;
|
import org.hibernate.sql.ast.tree.insert.InsertSelectStatement;
|
||||||
|
@ -72,6 +74,17 @@ public class HSQLSqlAstTranslator<T extends JdbcOperation> extends AbstractSqlAs
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void renderDerivedTableReference(DerivedTableReference tableReference) {
|
||||||
|
if ( tableReference instanceof FunctionTableReference && tableReference.isLateral() ) {
|
||||||
|
// No need for a lateral keyword for functions
|
||||||
|
tableReference.accept( this );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
super.renderDerivedTableReference( tableReference );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void visitConflictClause(ConflictClause conflictClause) {
|
protected void visitConflictClause(ConflictClause conflictClause) {
|
||||||
if ( conflictClause != null ) {
|
if ( conflictClause != null ) {
|
||||||
|
|
|
@ -31,6 +31,8 @@ import org.hibernate.type.BasicPluralType;
|
||||||
import org.hibernate.type.BasicType;
|
import org.hibernate.type.BasicType;
|
||||||
import org.hibernate.type.SqlTypes;
|
import org.hibernate.type.SqlTypes;
|
||||||
import org.hibernate.type.descriptor.WrapperOptions;
|
import org.hibernate.type.descriptor.WrapperOptions;
|
||||||
|
import org.hibernate.type.descriptor.java.BasicPluralJavaType;
|
||||||
|
import org.hibernate.type.descriptor.java.EnumJavaType;
|
||||||
import org.hibernate.type.descriptor.java.JavaType;
|
import org.hibernate.type.descriptor.java.JavaType;
|
||||||
import org.hibernate.type.descriptor.java.JdbcDateJavaType;
|
import org.hibernate.type.descriptor.java.JdbcDateJavaType;
|
||||||
import org.hibernate.type.descriptor.java.JdbcTimeJavaType;
|
import org.hibernate.type.descriptor.java.JdbcTimeJavaType;
|
||||||
|
@ -38,6 +40,9 @@ import org.hibernate.type.descriptor.java.JdbcTimestampJavaType;
|
||||||
import org.hibernate.type.descriptor.java.OffsetDateTimeJavaType;
|
import org.hibernate.type.descriptor.java.OffsetDateTimeJavaType;
|
||||||
import org.hibernate.type.descriptor.java.PrimitiveByteArrayJavaType;
|
import org.hibernate.type.descriptor.java.PrimitiveByteArrayJavaType;
|
||||||
import org.hibernate.type.descriptor.jdbc.AggregateJdbcType;
|
import org.hibernate.type.descriptor.jdbc.AggregateJdbcType;
|
||||||
|
import org.hibernate.type.descriptor.jdbc.ArrayJdbcType;
|
||||||
|
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
||||||
|
import org.hibernate.type.descriptor.jdbc.JsonArrayJdbcType;
|
||||||
|
|
||||||
import static org.hibernate.dialect.StructHelper.getEmbeddedPart;
|
import static org.hibernate.dialect.StructHelper.getEmbeddedPart;
|
||||||
import static org.hibernate.dialect.StructHelper.instantiate;
|
import static org.hibernate.dialect.StructHelper.instantiate;
|
||||||
|
@ -57,6 +62,43 @@ public class JsonHelper {
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String arrayToString(MappingType elementMappingType, Object[] values, WrapperOptions options) {
|
||||||
|
if ( values.length == 0 ) {
|
||||||
|
return "[]";
|
||||||
|
}
|
||||||
|
final StringBuilder sb = new StringBuilder();
|
||||||
|
final JsonAppender jsonAppender = new JsonAppender( sb );
|
||||||
|
char separator = '[';
|
||||||
|
for ( Object value : values ) {
|
||||||
|
sb.append( separator );
|
||||||
|
toString( elementMappingType, value, options, jsonAppender );
|
||||||
|
separator = ',';
|
||||||
|
}
|
||||||
|
sb.append( ']' );
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String arrayToString(
|
||||||
|
JavaType<?> elementJavaType,
|
||||||
|
JdbcType elementJdbcType,
|
||||||
|
Object[] values,
|
||||||
|
WrapperOptions options) {
|
||||||
|
if ( values.length == 0 ) {
|
||||||
|
return "[]";
|
||||||
|
}
|
||||||
|
final StringBuilder sb = new StringBuilder();
|
||||||
|
final JsonAppender jsonAppender = new JsonAppender( sb );
|
||||||
|
char separator = '[';
|
||||||
|
for ( Object value : values ) {
|
||||||
|
sb.append( separator );
|
||||||
|
//noinspection unchecked
|
||||||
|
convertedValueToString( (JavaType<Object>) elementJavaType, elementJdbcType, value, options, jsonAppender );
|
||||||
|
separator = ',';
|
||||||
|
}
|
||||||
|
sb.append( ']' );
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
private static void toString(EmbeddableMappingType embeddableMappingType, Object value, WrapperOptions options, JsonAppender appender) {
|
private static void toString(EmbeddableMappingType embeddableMappingType, Object value, WrapperOptions options, JsonAppender appender) {
|
||||||
toString( embeddableMappingType, options, appender, value, '{' );
|
toString( embeddableMappingType, options, appender, value, '{' );
|
||||||
appender.append( '}' );
|
appender.append( '}' );
|
||||||
|
@ -130,34 +172,45 @@ public class JsonHelper {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void convertedValueToString(
|
private static void convertedValueToString(
|
||||||
MappingType mappedType,
|
JavaType<Object> javaType,
|
||||||
|
JdbcType jdbcType,
|
||||||
Object value,
|
Object value,
|
||||||
WrapperOptions options,
|
WrapperOptions options,
|
||||||
JsonAppender appender) {
|
JsonAppender appender) {
|
||||||
if ( value == null ) {
|
if ( value == null ) {
|
||||||
appender.append( "null" );
|
appender.append( "null" );
|
||||||
}
|
}
|
||||||
else if ( mappedType instanceof EmbeddableMappingType ) {
|
else if ( jdbcType instanceof AggregateJdbcType aggregateJdbcType ) {
|
||||||
toString( (EmbeddableMappingType) mappedType, value, options, appender );
|
toString( aggregateJdbcType.getEmbeddableMappingType(), value, options, appender );
|
||||||
}
|
|
||||||
else if ( mappedType instanceof BasicType<?> ) {
|
|
||||||
//noinspection unchecked
|
|
||||||
final BasicType<Object> basicType = (BasicType<Object>) mappedType;
|
|
||||||
convertedBasicValueToString( value, options, appender, basicType );
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
throw new UnsupportedOperationException( "Support for mapping type not yet implemented: " + mappedType.getClass().getName() );
|
convertedBasicValueToString( value, options, appender, javaType, jdbcType );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private static void convertedBasicValueToString(
|
private static void convertedBasicValueToString(
|
||||||
Object value,
|
Object value,
|
||||||
WrapperOptions options,
|
WrapperOptions options,
|
||||||
JsonAppender appender,
|
JsonAppender appender,
|
||||||
BasicType<Object> basicType) {
|
BasicType<Object> basicType) {
|
||||||
//noinspection unchecked
|
//noinspection unchecked
|
||||||
final JavaType<Object> javaType = (JavaType<Object>) basicType.getJdbcJavaType();
|
convertedBasicValueToString(
|
||||||
switch ( basicType.getJdbcType().getDefaultSqlTypeCode() ) {
|
value,
|
||||||
|
options,
|
||||||
|
appender,
|
||||||
|
(JavaType<Object>) basicType.getJdbcJavaType(),
|
||||||
|
basicType.getJdbcType()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void convertedBasicValueToString(
|
||||||
|
Object value,
|
||||||
|
WrapperOptions options,
|
||||||
|
JsonAppender appender,
|
||||||
|
JavaType<Object> javaType,
|
||||||
|
JdbcType jdbcType) {
|
||||||
|
switch ( jdbcType.getDefaultSqlTypeCode() ) {
|
||||||
case SqlTypes.TINYINT:
|
case SqlTypes.TINYINT:
|
||||||
case SqlTypes.SMALLINT:
|
case SqlTypes.SMALLINT:
|
||||||
case SqlTypes.INTEGER:
|
case SqlTypes.INTEGER:
|
||||||
|
@ -272,19 +325,21 @@ public class JsonHelper {
|
||||||
final int length = Array.getLength( value );
|
final int length = Array.getLength( value );
|
||||||
appender.append( '[' );
|
appender.append( '[' );
|
||||||
if ( length != 0 ) {
|
if ( length != 0 ) {
|
||||||
final BasicType<Object> elementType = ( (BasicPluralType<?, Object>) basicType ).getElementType();
|
//noinspection unchecked
|
||||||
|
final JavaType<Object> elementJavaType = ( (BasicPluralJavaType<Object>) javaType ).getElementJavaType();
|
||||||
|
final JdbcType elementJdbcType = ( (ArrayJdbcType) jdbcType ).getElementJdbcType();
|
||||||
Object arrayElement = Array.get( value, 0 );
|
Object arrayElement = Array.get( value, 0 );
|
||||||
convertedValueToString( elementType, arrayElement, options, appender );
|
convertedValueToString( elementJavaType, elementJdbcType, arrayElement, options, appender );
|
||||||
for ( int i = 1; i < length; i++ ) {
|
for ( int i = 1; i < length; i++ ) {
|
||||||
arrayElement = Array.get( value, i );
|
arrayElement = Array.get( value, i );
|
||||||
appender.append( ',' );
|
appender.append( ',' );
|
||||||
convertedValueToString( elementType, arrayElement, options, appender );
|
convertedValueToString( elementJavaType, elementJdbcType, arrayElement, options, appender );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
appender.append( ']' );
|
appender.append( ']' );
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new UnsupportedOperationException( "Unsupported JdbcType nested in JSON: " + basicType.getJdbcType() );
|
throw new UnsupportedOperationException( "Unsupported JdbcType nested in JSON: " + jdbcType );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -314,6 +369,39 @@ public class JsonHelper {
|
||||||
return (X) values;
|
return (X) values;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static <X> X arrayFromString(
|
||||||
|
JavaType<X> javaType,
|
||||||
|
JsonArrayJdbcType jsonArrayJdbcType,
|
||||||
|
String string,
|
||||||
|
WrapperOptions options) throws SQLException {
|
||||||
|
if ( string == null ) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
final JavaType<?> elementJavaType = ((BasicPluralJavaType<?>) javaType).getElementJavaType();
|
||||||
|
final Class<?> preferredJavaTypeClass = jsonArrayJdbcType.getElementJdbcType().getPreferredJavaTypeClass( options );
|
||||||
|
final JavaType<?> jdbcJavaType;
|
||||||
|
if ( preferredJavaTypeClass == null || preferredJavaTypeClass == elementJavaType.getJavaTypeClass() ) {
|
||||||
|
jdbcJavaType = elementJavaType;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
jdbcJavaType = options.getSessionFactory().getTypeConfiguration().getJavaTypeRegistry()
|
||||||
|
.resolveDescriptor( preferredJavaTypeClass );
|
||||||
|
}
|
||||||
|
final CustomArrayList arrayList = new CustomArrayList();
|
||||||
|
final int i = fromArrayString(
|
||||||
|
string,
|
||||||
|
false,
|
||||||
|
options,
|
||||||
|
0,
|
||||||
|
arrayList,
|
||||||
|
elementJavaType,
|
||||||
|
jdbcJavaType,
|
||||||
|
jsonArrayJdbcType.getElementJdbcType()
|
||||||
|
);
|
||||||
|
assert string.charAt( i - 1 ) == ']';
|
||||||
|
return javaType.wrap( arrayList, options );
|
||||||
|
}
|
||||||
|
|
||||||
private static int fromString(
|
private static int fromString(
|
||||||
EmbeddableMappingType embeddableMappingType,
|
EmbeddableMappingType embeddableMappingType,
|
||||||
String string,
|
String string,
|
||||||
|
@ -559,7 +647,30 @@ public class JsonHelper {
|
||||||
int begin,
|
int begin,
|
||||||
CustomArrayList arrayList,
|
CustomArrayList arrayList,
|
||||||
BasicType<?> elementType) throws SQLException {
|
BasicType<?> elementType) throws SQLException {
|
||||||
|
return fromArrayString(
|
||||||
|
string,
|
||||||
|
returnEmbeddable,
|
||||||
|
options,
|
||||||
|
begin,
|
||||||
|
arrayList,
|
||||||
|
elementType.getMappedJavaType(),
|
||||||
|
elementType.getJdbcJavaType(),
|
||||||
|
elementType.getJdbcType()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int fromArrayString(
|
||||||
|
String string,
|
||||||
|
boolean returnEmbeddable,
|
||||||
|
WrapperOptions options,
|
||||||
|
int begin,
|
||||||
|
CustomArrayList arrayList,
|
||||||
|
JavaType<?> javaType,
|
||||||
|
JavaType<?> jdbcJavaType,
|
||||||
|
JdbcType jdbcType) throws SQLException {
|
||||||
|
if ( string.length() == begin + 2 ) {
|
||||||
|
return begin + 2;
|
||||||
|
}
|
||||||
boolean hasEscape = false;
|
boolean hasEscape = false;
|
||||||
assert string.charAt( begin ) == '[';
|
assert string.charAt( begin ) == '[';
|
||||||
int start = begin + 1;
|
int start = begin + 1;
|
||||||
|
@ -586,7 +697,9 @@ public class JsonHelper {
|
||||||
s = State.VALUE_END;
|
s = State.VALUE_END;
|
||||||
arrayList.add(
|
arrayList.add(
|
||||||
fromString(
|
fromString(
|
||||||
elementType,
|
javaType,
|
||||||
|
jdbcJavaType,
|
||||||
|
jdbcType,
|
||||||
string,
|
string,
|
||||||
start,
|
start,
|
||||||
i,
|
i,
|
||||||
|
@ -693,7 +806,9 @@ public class JsonHelper {
|
||||||
string,
|
string,
|
||||||
i,
|
i,
|
||||||
arrayList.getUnderlyingArray(),
|
arrayList.getUnderlyingArray(),
|
||||||
elementType,
|
javaType,
|
||||||
|
jdbcJavaType,
|
||||||
|
jdbcType,
|
||||||
elementIndex,
|
elementIndex,
|
||||||
returnEmbeddable,
|
returnEmbeddable,
|
||||||
options
|
options
|
||||||
|
@ -728,6 +843,29 @@ public class JsonHelper {
|
||||||
int selectableIndex,
|
int selectableIndex,
|
||||||
boolean returnEmbeddable,
|
boolean returnEmbeddable,
|
||||||
WrapperOptions options) throws SQLException {
|
WrapperOptions options) throws SQLException {
|
||||||
|
return consumeLiteral(
|
||||||
|
string,
|
||||||
|
start,
|
||||||
|
values,
|
||||||
|
jdbcMapping.getMappedJavaType(),
|
||||||
|
jdbcMapping.getJdbcJavaType(),
|
||||||
|
jdbcMapping.getJdbcType(),
|
||||||
|
selectableIndex,
|
||||||
|
returnEmbeddable,
|
||||||
|
options
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int consumeLiteral(
|
||||||
|
String string,
|
||||||
|
int start,
|
||||||
|
Object[] values,
|
||||||
|
JavaType<?> javaType,
|
||||||
|
JavaType<?> jdbcJavaType,
|
||||||
|
JdbcType jdbcType,
|
||||||
|
int selectableIndex,
|
||||||
|
boolean returnEmbeddable,
|
||||||
|
WrapperOptions options) throws SQLException {
|
||||||
final char c = string.charAt( start );
|
final char c = string.charAt( start );
|
||||||
switch ( c ) {
|
switch ( c ) {
|
||||||
case 'n':
|
case 'n':
|
||||||
|
@ -750,7 +888,9 @@ public class JsonHelper {
|
||||||
start,
|
start,
|
||||||
start + 1,
|
start + 1,
|
||||||
values,
|
values,
|
||||||
jdbcMapping,
|
javaType,
|
||||||
|
jdbcJavaType,
|
||||||
|
jdbcType,
|
||||||
selectableIndex,
|
selectableIndex,
|
||||||
returnEmbeddable,
|
returnEmbeddable,
|
||||||
options
|
options
|
||||||
|
@ -762,14 +902,18 @@ public class JsonHelper {
|
||||||
start,
|
start,
|
||||||
start + 1,
|
start + 1,
|
||||||
values,
|
values,
|
||||||
jdbcMapping,
|
javaType,
|
||||||
|
jdbcJavaType,
|
||||||
|
jdbcType,
|
||||||
selectableIndex,
|
selectableIndex,
|
||||||
returnEmbeddable,
|
returnEmbeddable,
|
||||||
options
|
options
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
values[selectableIndex] = fromString(
|
values[selectableIndex] = fromString(
|
||||||
jdbcMapping,
|
javaType,
|
||||||
|
jdbcJavaType,
|
||||||
|
jdbcType,
|
||||||
string,
|
string,
|
||||||
start,
|
start,
|
||||||
start + 1,
|
start + 1,
|
||||||
|
@ -806,7 +950,9 @@ public class JsonHelper {
|
||||||
start,
|
start,
|
||||||
i,
|
i,
|
||||||
values,
|
values,
|
||||||
jdbcMapping,
|
javaType,
|
||||||
|
jdbcJavaType,
|
||||||
|
jdbcType,
|
||||||
selectableIndex,
|
selectableIndex,
|
||||||
returnEmbeddable,
|
returnEmbeddable,
|
||||||
options
|
options
|
||||||
|
@ -818,7 +964,9 @@ public class JsonHelper {
|
||||||
start,
|
start,
|
||||||
i,
|
i,
|
||||||
values,
|
values,
|
||||||
jdbcMapping,
|
javaType,
|
||||||
|
jdbcJavaType,
|
||||||
|
jdbcType,
|
||||||
selectableIndex,
|
selectableIndex,
|
||||||
returnEmbeddable,
|
returnEmbeddable,
|
||||||
options
|
options
|
||||||
|
@ -836,7 +984,9 @@ public class JsonHelper {
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
values[selectableIndex] = fromString(
|
values[selectableIndex] = fromString(
|
||||||
jdbcMapping,
|
javaType,
|
||||||
|
jdbcJavaType,
|
||||||
|
jdbcType,
|
||||||
string,
|
string,
|
||||||
start,
|
start,
|
||||||
i,
|
i,
|
||||||
|
@ -856,7 +1006,9 @@ public class JsonHelper {
|
||||||
int start,
|
int start,
|
||||||
int dotIndex,
|
int dotIndex,
|
||||||
Object[] values,
|
Object[] values,
|
||||||
JdbcMapping jdbcMapping,
|
JavaType<?> javaType,
|
||||||
|
JavaType<?> jdbcJavaType,
|
||||||
|
JdbcType jdbcType,
|
||||||
int selectableIndex,
|
int selectableIndex,
|
||||||
boolean returnEmbeddable,
|
boolean returnEmbeddable,
|
||||||
WrapperOptions options) throws SQLException {
|
WrapperOptions options) throws SQLException {
|
||||||
|
@ -870,7 +1022,9 @@ public class JsonHelper {
|
||||||
start,
|
start,
|
||||||
i,
|
i,
|
||||||
values,
|
values,
|
||||||
jdbcMapping,
|
javaType,
|
||||||
|
jdbcJavaType,
|
||||||
|
jdbcType,
|
||||||
selectableIndex,
|
selectableIndex,
|
||||||
returnEmbeddable,
|
returnEmbeddable,
|
||||||
options
|
options
|
||||||
|
@ -888,7 +1042,9 @@ public class JsonHelper {
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
values[selectableIndex] = fromString(
|
values[selectableIndex] = fromString(
|
||||||
jdbcMapping,
|
javaType,
|
||||||
|
jdbcJavaType,
|
||||||
|
jdbcType,
|
||||||
string,
|
string,
|
||||||
start,
|
start,
|
||||||
i,
|
i,
|
||||||
|
@ -906,7 +1062,9 @@ public class JsonHelper {
|
||||||
int start,
|
int start,
|
||||||
int eIndex,
|
int eIndex,
|
||||||
Object[] values,
|
Object[] values,
|
||||||
JdbcMapping jdbcMapping,
|
JavaType<?> javaType,
|
||||||
|
JavaType<?> jdbcJavaType,
|
||||||
|
JdbcType jdbcType,
|
||||||
int selectableIndex,
|
int selectableIndex,
|
||||||
boolean returnEmbeddable,
|
boolean returnEmbeddable,
|
||||||
WrapperOptions options) throws SQLException {
|
WrapperOptions options) throws SQLException {
|
||||||
|
@ -933,7 +1091,9 @@ public class JsonHelper {
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
values[selectableIndex] = fromString(
|
values[selectableIndex] = fromString(
|
||||||
jdbcMapping,
|
javaType,
|
||||||
|
jdbcJavaType,
|
||||||
|
jdbcType,
|
||||||
string,
|
string,
|
||||||
start,
|
start,
|
||||||
i,
|
i,
|
||||||
|
@ -1001,10 +1161,35 @@ public class JsonHelper {
|
||||||
boolean hasEscape,
|
boolean hasEscape,
|
||||||
boolean returnEmbeddable,
|
boolean returnEmbeddable,
|
||||||
WrapperOptions options) throws SQLException {
|
WrapperOptions options) throws SQLException {
|
||||||
|
return fromString(
|
||||||
|
jdbcMapping.getMappedJavaType(),
|
||||||
|
jdbcMapping.getJdbcJavaType(),
|
||||||
|
jdbcMapping.getJdbcType(),
|
||||||
|
string,
|
||||||
|
start,
|
||||||
|
end,
|
||||||
|
hasEscape,
|
||||||
|
returnEmbeddable,
|
||||||
|
options
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Object fromString(
|
||||||
|
JavaType<?> javaType,
|
||||||
|
JavaType<?> jdbcJavaType,
|
||||||
|
JdbcType jdbcType,
|
||||||
|
String string,
|
||||||
|
int start,
|
||||||
|
int end,
|
||||||
|
boolean hasEscape,
|
||||||
|
boolean returnEmbeddable,
|
||||||
|
WrapperOptions options) throws SQLException {
|
||||||
if ( hasEscape ) {
|
if ( hasEscape ) {
|
||||||
final String unescaped = unescape( string, start, end );
|
final String unescaped = unescape( string, start, end );
|
||||||
return fromString(
|
return fromString(
|
||||||
jdbcMapping,
|
javaType,
|
||||||
|
jdbcJavaType,
|
||||||
|
jdbcType,
|
||||||
unescaped,
|
unescaped,
|
||||||
0,
|
0,
|
||||||
unescaped.length(),
|
unescaped.length(),
|
||||||
|
@ -1012,22 +1197,33 @@ public class JsonHelper {
|
||||||
options
|
options
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return fromString( jdbcMapping, string, start, end, returnEmbeddable, options );
|
return fromString(
|
||||||
|
javaType,
|
||||||
|
jdbcJavaType,
|
||||||
|
jdbcType,
|
||||||
|
string,
|
||||||
|
start,
|
||||||
|
end,
|
||||||
|
returnEmbeddable,
|
||||||
|
options
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Object fromString(
|
private static Object fromString(
|
||||||
JdbcMapping jdbcMapping,
|
JavaType<?> javaType,
|
||||||
|
JavaType<?> jdbcJavaType,
|
||||||
|
JdbcType jdbcType,
|
||||||
String string,
|
String string,
|
||||||
int start,
|
int start,
|
||||||
int end,
|
int end,
|
||||||
boolean returnEmbeddable,
|
boolean returnEmbeddable,
|
||||||
WrapperOptions options) throws SQLException {
|
WrapperOptions options) throws SQLException {
|
||||||
switch ( jdbcMapping.getJdbcType().getDefaultSqlTypeCode() ) {
|
switch ( jdbcType.getDefaultSqlTypeCode() ) {
|
||||||
case SqlTypes.BINARY:
|
case SqlTypes.BINARY:
|
||||||
case SqlTypes.VARBINARY:
|
case SqlTypes.VARBINARY:
|
||||||
case SqlTypes.LONGVARBINARY:
|
case SqlTypes.LONGVARBINARY:
|
||||||
case SqlTypes.LONG32VARBINARY:
|
case SqlTypes.LONG32VARBINARY:
|
||||||
return jdbcMapping.getJdbcJavaType().wrap(
|
return jdbcJavaType.wrap(
|
||||||
PrimitiveByteArrayJavaType.INSTANCE.fromEncodedString(
|
PrimitiveByteArrayJavaType.INSTANCE.fromEncodedString(
|
||||||
string,
|
string,
|
||||||
start,
|
start,
|
||||||
|
@ -1036,7 +1232,7 @@ public class JsonHelper {
|
||||||
options
|
options
|
||||||
);
|
);
|
||||||
case SqlTypes.DATE:
|
case SqlTypes.DATE:
|
||||||
return jdbcMapping.getJdbcJavaType().wrap(
|
return jdbcJavaType.wrap(
|
||||||
JdbcDateJavaType.INSTANCE.fromEncodedString(
|
JdbcDateJavaType.INSTANCE.fromEncodedString(
|
||||||
string,
|
string,
|
||||||
start,
|
start,
|
||||||
|
@ -1047,7 +1243,7 @@ public class JsonHelper {
|
||||||
case SqlTypes.TIME:
|
case SqlTypes.TIME:
|
||||||
case SqlTypes.TIME_WITH_TIMEZONE:
|
case SqlTypes.TIME_WITH_TIMEZONE:
|
||||||
case SqlTypes.TIME_UTC:
|
case SqlTypes.TIME_UTC:
|
||||||
return jdbcMapping.getJdbcJavaType().wrap(
|
return jdbcJavaType.wrap(
|
||||||
JdbcTimeJavaType.INSTANCE.fromEncodedString(
|
JdbcTimeJavaType.INSTANCE.fromEncodedString(
|
||||||
string,
|
string,
|
||||||
start,
|
start,
|
||||||
|
@ -1056,7 +1252,7 @@ public class JsonHelper {
|
||||||
options
|
options
|
||||||
);
|
);
|
||||||
case SqlTypes.TIMESTAMP:
|
case SqlTypes.TIMESTAMP:
|
||||||
return jdbcMapping.getJdbcJavaType().wrap(
|
return jdbcJavaType.wrap(
|
||||||
JdbcTimestampJavaType.INSTANCE.fromEncodedString(
|
JdbcTimestampJavaType.INSTANCE.fromEncodedString(
|
||||||
string,
|
string,
|
||||||
start,
|
start,
|
||||||
|
@ -1066,7 +1262,7 @@ public class JsonHelper {
|
||||||
);
|
);
|
||||||
case SqlTypes.TIMESTAMP_WITH_TIMEZONE:
|
case SqlTypes.TIMESTAMP_WITH_TIMEZONE:
|
||||||
case SqlTypes.TIMESTAMP_UTC:
|
case SqlTypes.TIMESTAMP_UTC:
|
||||||
return jdbcMapping.getJdbcJavaType().wrap(
|
return jdbcJavaType.wrap(
|
||||||
OffsetDateTimeJavaType.INSTANCE.fromEncodedString(
|
OffsetDateTimeJavaType.INSTANCE.fromEncodedString(
|
||||||
string,
|
string,
|
||||||
start,
|
start,
|
||||||
|
@ -1077,28 +1273,21 @@ public class JsonHelper {
|
||||||
case SqlTypes.TINYINT:
|
case SqlTypes.TINYINT:
|
||||||
case SqlTypes.SMALLINT:
|
case SqlTypes.SMALLINT:
|
||||||
case SqlTypes.INTEGER:
|
case SqlTypes.INTEGER:
|
||||||
if ( jdbcMapping.getValueConverter() == null ) {
|
if ( jdbcJavaType.getJavaTypeClass() == Boolean.class ) {
|
||||||
Class<?> javaTypeClass = jdbcMapping.getJavaTypeDescriptor().getJavaTypeClass();
|
return jdbcJavaType.wrap( Integer.parseInt( string, start, end, 10 ), options );
|
||||||
if ( javaTypeClass == Boolean.class ) {
|
|
||||||
// BooleanJavaType has this as an implicit conversion
|
|
||||||
return Integer.parseInt( string, start, end, 10 ) == 1;
|
|
||||||
}
|
|
||||||
if ( javaTypeClass.isEnum() ) {
|
|
||||||
return javaTypeClass.getEnumConstants()[Integer.parseInt( string, start, end, 10 )];
|
|
||||||
}
|
}
|
||||||
|
else if ( jdbcJavaType instanceof EnumJavaType<?> ) {
|
||||||
|
return jdbcJavaType.wrap( Integer.parseInt( string, start, end, 10 ), options );
|
||||||
}
|
}
|
||||||
case SqlTypes.CHAR:
|
case SqlTypes.CHAR:
|
||||||
case SqlTypes.NCHAR:
|
case SqlTypes.NCHAR:
|
||||||
case SqlTypes.VARCHAR:
|
case SqlTypes.VARCHAR:
|
||||||
case SqlTypes.NVARCHAR:
|
case SqlTypes.NVARCHAR:
|
||||||
if ( jdbcMapping.getValueConverter() == null
|
if ( jdbcJavaType.getJavaTypeClass() == Boolean.class && end == start + 1 ) {
|
||||||
&& jdbcMapping.getJavaTypeDescriptor().getJavaTypeClass() == Boolean.class ) {
|
return jdbcJavaType.wrap( string.charAt( start ), options );
|
||||||
// BooleanJavaType has this as an implicit conversion
|
|
||||||
return end == start + 1 && string.charAt( start ) == 'Y';
|
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
if ( jdbcMapping.getJdbcType() instanceof AggregateJdbcType ) {
|
if ( jdbcType instanceof AggregateJdbcType aggregateJdbcType ) {
|
||||||
final AggregateJdbcType aggregateJdbcType = (AggregateJdbcType) jdbcMapping.getJdbcType();
|
|
||||||
final Object[] subValues = aggregateJdbcType.extractJdbcValues(
|
final Object[] subValues = aggregateJdbcType.extractJdbcValues(
|
||||||
CharSequenceHelper.subSequence(
|
CharSequenceHelper.subSequence(
|
||||||
string,
|
string,
|
||||||
|
@ -1119,7 +1308,7 @@ public class JsonHelper {
|
||||||
return subValues;
|
return subValues;
|
||||||
}
|
}
|
||||||
|
|
||||||
return jdbcMapping.getJdbcJavaType().fromEncodedString(
|
return jdbcJavaType.fromEncodedString(
|
||||||
string,
|
string,
|
||||||
start,
|
start,
|
||||||
end
|
end
|
||||||
|
|
|
@ -30,7 +30,7 @@ import org.hibernate.tool.schema.extract.spi.SequenceInformationExtractor;
|
||||||
import org.hibernate.type.SqlTypes;
|
import org.hibernate.type.SqlTypes;
|
||||||
import org.hibernate.type.StandardBasicTypes;
|
import org.hibernate.type.StandardBasicTypes;
|
||||||
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
||||||
import org.hibernate.type.descriptor.jdbc.JsonArrayJdbcType;
|
import org.hibernate.type.descriptor.jdbc.JsonArrayJdbcTypeConstructor;
|
||||||
import org.hibernate.type.descriptor.jdbc.JsonJdbcType;
|
import org.hibernate.type.descriptor.jdbc.JsonJdbcType;
|
||||||
import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry;
|
import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry;
|
||||||
import org.hibernate.type.descriptor.sql.internal.DdlTypeImpl;
|
import org.hibernate.type.descriptor.sql.internal.DdlTypeImpl;
|
||||||
|
@ -99,6 +99,11 @@ public class MariaDBDialect extends MySQLDialect {
|
||||||
commonFunctionFactory.jsonArrayAgg_mariadb();
|
commonFunctionFactory.jsonArrayAgg_mariadb();
|
||||||
commonFunctionFactory.jsonObjectAgg_mariadb();
|
commonFunctionFactory.jsonObjectAgg_mariadb();
|
||||||
commonFunctionFactory.jsonArrayAppend_mariadb();
|
commonFunctionFactory.jsonArrayAppend_mariadb();
|
||||||
|
|
||||||
|
if ( getVersion().isSameOrAfter( 10, 6 ) ) {
|
||||||
|
commonFunctionFactory.unnest_emulated();
|
||||||
|
}
|
||||||
|
|
||||||
commonFunctionFactory.inverseDistributionOrderedSetAggregates_windowEmulation();
|
commonFunctionFactory.inverseDistributionOrderedSetAggregates_windowEmulation();
|
||||||
functionContributions.getFunctionRegistry().patternDescriptorBuilder( "median", "median(?1) over ()" )
|
functionContributions.getFunctionRegistry().patternDescriptorBuilder( "median", "median(?1) over ()" )
|
||||||
.setInvariantType( functionContributions.getTypeConfiguration().getBasicTypeRegistry().resolve( StandardBasicTypes.DOUBLE ) )
|
.setInvariantType( functionContributions.getTypeConfiguration().getBasicTypeRegistry().resolve( StandardBasicTypes.DOUBLE ) )
|
||||||
|
@ -152,7 +157,7 @@ public class MariaDBDialect extends MySQLDialect {
|
||||||
final JdbcTypeRegistry jdbcTypeRegistry = typeContributions.getTypeConfiguration().getJdbcTypeRegistry();
|
final JdbcTypeRegistry jdbcTypeRegistry = typeContributions.getTypeConfiguration().getJdbcTypeRegistry();
|
||||||
// Make sure we register the JSON type descriptor before calling super, because MariaDB does not need casting
|
// Make sure we register the JSON type descriptor before calling super, because MariaDB does not need casting
|
||||||
jdbcTypeRegistry.addDescriptorIfAbsent( SqlTypes.JSON, JsonJdbcType.INSTANCE );
|
jdbcTypeRegistry.addDescriptorIfAbsent( SqlTypes.JSON, JsonJdbcType.INSTANCE );
|
||||||
jdbcTypeRegistry.addDescriptorIfAbsent( SqlTypes.JSON_ARRAY, JsonArrayJdbcType.INSTANCE );
|
jdbcTypeRegistry.addTypeConstructorIfAbsent( JsonArrayJdbcTypeConstructor.INSTANCE );
|
||||||
|
|
||||||
super.contributeTypes( typeContributions, serviceRegistry );
|
super.contributeTypes( typeContributions, serviceRegistry );
|
||||||
if ( getVersion().isSameOrAfter( 10, 7 ) ) {
|
if ( getVersion().isSameOrAfter( 10, 7 ) ) {
|
||||||
|
|
|
@ -21,6 +21,7 @@ import org.hibernate.sql.ast.tree.expression.ColumnReference;
|
||||||
import org.hibernate.sql.ast.tree.expression.Expression;
|
import org.hibernate.sql.ast.tree.expression.Expression;
|
||||||
import org.hibernate.sql.ast.tree.expression.Literal;
|
import org.hibernate.sql.ast.tree.expression.Literal;
|
||||||
import org.hibernate.sql.ast.tree.expression.Summarization;
|
import org.hibernate.sql.ast.tree.expression.Summarization;
|
||||||
|
import org.hibernate.sql.ast.tree.from.DerivedTableReference;
|
||||||
import org.hibernate.sql.ast.tree.from.NamedTableReference;
|
import org.hibernate.sql.ast.tree.from.NamedTableReference;
|
||||||
import org.hibernate.sql.ast.tree.from.QueryPartTableReference;
|
import org.hibernate.sql.ast.tree.from.QueryPartTableReference;
|
||||||
import org.hibernate.sql.ast.tree.insert.ConflictClause;
|
import org.hibernate.sql.ast.tree.insert.ConflictClause;
|
||||||
|
@ -280,6 +281,11 @@ public class MariaDBSqlAstTranslator<T extends JdbcOperation> extends AbstractSq
|
||||||
emulateQueryPartTableReferenceColumnAliasing( tableReference );
|
emulateQueryPartTableReferenceColumnAliasing( tableReference );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void renderDerivedTableReferenceIdentificationVariable(DerivedTableReference tableReference) {
|
||||||
|
renderTableReferenceIdentificationVariable( tableReference );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visitOffsetFetchClause(QueryPart queryPart) {
|
public void visitOffsetFetchClause(QueryPart queryPart) {
|
||||||
if ( !isRowNumberingCurrentQueryPart() ) {
|
if ( !isRowNumberingCurrentQueryPart() ) {
|
||||||
|
|
|
@ -5,16 +5,17 @@
|
||||||
package org.hibernate.dialect;
|
package org.hibernate.dialect;
|
||||||
|
|
||||||
import org.hibernate.sql.ast.spi.SqlAppender;
|
import org.hibernate.sql.ast.spi.SqlAppender;
|
||||||
|
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
||||||
import org.hibernate.type.descriptor.jdbc.JsonArrayJdbcType;
|
import org.hibernate.type.descriptor.jdbc.JsonArrayJdbcType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Christian Beikov
|
* @author Christian Beikov
|
||||||
*/
|
*/
|
||||||
public class MySQLCastingJsonArrayJdbcType extends JsonArrayJdbcType {
|
public class MySQLCastingJsonArrayJdbcType extends JsonArrayJdbcType {
|
||||||
/**
|
|
||||||
* Singleton access
|
public MySQLCastingJsonArrayJdbcType(JdbcType elementJdbcType) {
|
||||||
*/
|
super( elementJdbcType );
|
||||||
public static final JsonArrayJdbcType INSTANCE = new MySQLCastingJsonArrayJdbcType();
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void appendWriteExpression(
|
public void appendWriteExpression(
|
||||||
|
|
|
@ -0,0 +1,43 @@
|
||||||
|
/*
|
||||||
|
* SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
|
* Copyright Red Hat Inc. and Hibernate Authors
|
||||||
|
*/
|
||||||
|
package org.hibernate.dialect;
|
||||||
|
|
||||||
|
import org.hibernate.tool.schema.extract.spi.ColumnTypeInformation;
|
||||||
|
import org.hibernate.type.BasicType;
|
||||||
|
import org.hibernate.type.SqlTypes;
|
||||||
|
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
||||||
|
import org.hibernate.type.descriptor.jdbc.JdbcTypeConstructor;
|
||||||
|
import org.hibernate.type.spi.TypeConfiguration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Factory for {@link MySQLCastingJsonArrayJdbcType}.
|
||||||
|
*/
|
||||||
|
public class MySQLCastingJsonArrayJdbcTypeConstructor implements JdbcTypeConstructor {
|
||||||
|
|
||||||
|
public static final MySQLCastingJsonArrayJdbcTypeConstructor INSTANCE = new MySQLCastingJsonArrayJdbcTypeConstructor();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JdbcType resolveType(
|
||||||
|
TypeConfiguration typeConfiguration,
|
||||||
|
Dialect dialect,
|
||||||
|
BasicType<?> elementType,
|
||||||
|
ColumnTypeInformation columnTypeInformation) {
|
||||||
|
return resolveType( typeConfiguration, dialect, elementType.getJdbcType(), columnTypeInformation );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JdbcType resolveType(
|
||||||
|
TypeConfiguration typeConfiguration,
|
||||||
|
Dialect dialect,
|
||||||
|
JdbcType elementType,
|
||||||
|
ColumnTypeInformation columnTypeInformation) {
|
||||||
|
return new MySQLCastingJsonArrayJdbcType( elementType );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getDefaultSqlTypeCode() {
|
||||||
|
return SqlTypes.JSON_ARRAY;
|
||||||
|
}
|
||||||
|
}
|
|
@ -537,6 +537,11 @@ public class MySQLDialect extends Dialect {
|
||||||
return Types.BIT;
|
return Types.BIT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getPreferredSqlTypeCodeForArray() {
|
||||||
|
return SqlTypes.JSON_ARRAY;
|
||||||
|
}
|
||||||
|
|
||||||
// @Override
|
// @Override
|
||||||
// public int getDefaultDecimalPrecision() {
|
// public int getDefaultDecimalPrecision() {
|
||||||
// //this is the maximum, but I guess it's too high
|
// //this is the maximum, but I guess it's too high
|
||||||
|
@ -652,6 +657,10 @@ public class MySQLDialect extends Dialect {
|
||||||
functionFactory.jsonMergepatch_mysql();
|
functionFactory.jsonMergepatch_mysql();
|
||||||
functionFactory.jsonArrayAppend_mysql();
|
functionFactory.jsonArrayAppend_mysql();
|
||||||
functionFactory.jsonArrayInsert_mysql();
|
functionFactory.jsonArrayInsert_mysql();
|
||||||
|
|
||||||
|
if ( getMySQLVersion().isSameOrAfter( 8 ) ) {
|
||||||
|
functionFactory.unnest_emulated();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -661,7 +670,7 @@ public class MySQLDialect extends Dialect {
|
||||||
final JdbcTypeRegistry jdbcTypeRegistry = typeContributions.getTypeConfiguration().getJdbcTypeRegistry();
|
final JdbcTypeRegistry jdbcTypeRegistry = typeContributions.getTypeConfiguration().getJdbcTypeRegistry();
|
||||||
|
|
||||||
jdbcTypeRegistry.addDescriptorIfAbsent( SqlTypes.JSON, MySQLCastingJsonJdbcType.INSTANCE );
|
jdbcTypeRegistry.addDescriptorIfAbsent( SqlTypes.JSON, MySQLCastingJsonJdbcType.INSTANCE );
|
||||||
jdbcTypeRegistry.addDescriptorIfAbsent( SqlTypes.JSON_ARRAY, MySQLCastingJsonArrayJdbcType.INSTANCE );
|
jdbcTypeRegistry.addTypeConstructorIfAbsent( MySQLCastingJsonArrayJdbcTypeConstructor.INSTANCE );
|
||||||
|
|
||||||
// MySQL requires a custom binder for binding untyped nulls with the NULL type
|
// MySQL requires a custom binder for binding untyped nulls with the NULL type
|
||||||
typeContributions.contributeJdbcType( NullJdbcType.INSTANCE );
|
typeContributions.contributeJdbcType( NullJdbcType.INSTANCE );
|
||||||
|
|
|
@ -23,6 +23,8 @@ import org.hibernate.sql.ast.tree.expression.ColumnReference;
|
||||||
import org.hibernate.sql.ast.tree.expression.Expression;
|
import org.hibernate.sql.ast.tree.expression.Expression;
|
||||||
import org.hibernate.sql.ast.tree.expression.Literal;
|
import org.hibernate.sql.ast.tree.expression.Literal;
|
||||||
import org.hibernate.sql.ast.tree.expression.Summarization;
|
import org.hibernate.sql.ast.tree.expression.Summarization;
|
||||||
|
import org.hibernate.sql.ast.tree.from.DerivedTableReference;
|
||||||
|
import org.hibernate.sql.ast.tree.from.FunctionTableReference;
|
||||||
import org.hibernate.sql.ast.tree.from.NamedTableReference;
|
import org.hibernate.sql.ast.tree.from.NamedTableReference;
|
||||||
import org.hibernate.sql.ast.tree.from.QueryPartTableReference;
|
import org.hibernate.sql.ast.tree.from.QueryPartTableReference;
|
||||||
import org.hibernate.sql.ast.tree.from.ValuesTableReference;
|
import org.hibernate.sql.ast.tree.from.ValuesTableReference;
|
||||||
|
@ -315,6 +317,17 @@ public class MySQLSqlAstTranslator<T extends JdbcOperation> extends AbstractSqlA
|
||||||
emulateValuesTableReferenceColumnAliasing( tableReference );
|
emulateValuesTableReferenceColumnAliasing( tableReference );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void renderDerivedTableReference(DerivedTableReference tableReference) {
|
||||||
|
if ( tableReference instanceof FunctionTableReference && tableReference.isLateral() ) {
|
||||||
|
// No need for a lateral keyword for functions
|
||||||
|
tableReference.accept( this );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
super.renderDerivedTableReference( tableReference );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visitOffsetFetchClause(QueryPart queryPart) {
|
public void visitOffsetFetchClause(QueryPart queryPart) {
|
||||||
if ( !isRowNumberingCurrentQueryPart() ) {
|
if ( !isRowNumberingCurrentQueryPart() ) {
|
||||||
|
|
|
@ -83,13 +83,10 @@ import org.hibernate.type.JavaObjectType;
|
||||||
import org.hibernate.type.NullType;
|
import org.hibernate.type.NullType;
|
||||||
import org.hibernate.type.StandardBasicTypes;
|
import org.hibernate.type.StandardBasicTypes;
|
||||||
import org.hibernate.type.descriptor.java.PrimitiveByteArrayJavaType;
|
import org.hibernate.type.descriptor.java.PrimitiveByteArrayJavaType;
|
||||||
import org.hibernate.type.descriptor.jdbc.ArrayJdbcType;
|
|
||||||
import org.hibernate.type.descriptor.jdbc.BlobJdbcType;
|
import org.hibernate.type.descriptor.jdbc.BlobJdbcType;
|
||||||
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
||||||
import org.hibernate.type.descriptor.jdbc.NullJdbcType;
|
import org.hibernate.type.descriptor.jdbc.NullJdbcType;
|
||||||
import org.hibernate.type.descriptor.jdbc.ObjectJdbcType;
|
|
||||||
import org.hibernate.type.descriptor.jdbc.ObjectNullAsNullTypeJdbcType;
|
import org.hibernate.type.descriptor.jdbc.ObjectNullAsNullTypeJdbcType;
|
||||||
import org.hibernate.type.descriptor.jdbc.OracleJsonArrayBlobJdbcType;
|
|
||||||
import org.hibernate.type.descriptor.jdbc.OracleJsonBlobJdbcType;
|
import org.hibernate.type.descriptor.jdbc.OracleJsonBlobJdbcType;
|
||||||
import org.hibernate.type.descriptor.jdbc.SqlTypedJdbcType;
|
import org.hibernate.type.descriptor.jdbc.SqlTypedJdbcType;
|
||||||
import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry;
|
import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry;
|
||||||
|
@ -134,7 +131,6 @@ import static org.hibernate.type.SqlTypes.FLOAT;
|
||||||
import static org.hibernate.type.SqlTypes.GEOMETRY;
|
import static org.hibernate.type.SqlTypes.GEOMETRY;
|
||||||
import static org.hibernate.type.SqlTypes.INTEGER;
|
import static org.hibernate.type.SqlTypes.INTEGER;
|
||||||
import static org.hibernate.type.SqlTypes.JSON;
|
import static org.hibernate.type.SqlTypes.JSON;
|
||||||
import static org.hibernate.type.SqlTypes.JSON_ARRAY;
|
|
||||||
import static org.hibernate.type.SqlTypes.NUMERIC;
|
import static org.hibernate.type.SqlTypes.NUMERIC;
|
||||||
import static org.hibernate.type.SqlTypes.NVARCHAR;
|
import static org.hibernate.type.SqlTypes.NVARCHAR;
|
||||||
import static org.hibernate.type.SqlTypes.REAL;
|
import static org.hibernate.type.SqlTypes.REAL;
|
||||||
|
@ -425,6 +421,8 @@ public class OracleDialect extends Dialect {
|
||||||
functionFactory.xmlquery_oracle();
|
functionFactory.xmlquery_oracle();
|
||||||
functionFactory.xmlexists();
|
functionFactory.xmlexists();
|
||||||
functionFactory.xmlagg();
|
functionFactory.xmlagg();
|
||||||
|
|
||||||
|
functionFactory.unnest_oracle();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -821,11 +819,9 @@ public class OracleDialect extends Dialect {
|
||||||
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( GEOMETRY, "MDSYS.SDO_GEOMETRY", this ) );
|
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( GEOMETRY, "MDSYS.SDO_GEOMETRY", this ) );
|
||||||
if ( getVersion().isSameOrAfter( 21 ) ) {
|
if ( getVersion().isSameOrAfter( 21 ) ) {
|
||||||
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( JSON, "json", this ) );
|
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( JSON, "json", this ) );
|
||||||
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( JSON_ARRAY, "json", this ) );
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( JSON, "blob", this ) );
|
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( JSON, "blob", this ) );
|
||||||
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( JSON_ARRAY, "blob", this ) );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ddlTypeRegistry.addDescriptor( new ArrayDdlTypeImpl( this, false ) );
|
ddlTypeRegistry.addDescriptor( new ArrayDdlTypeImpl( this, false ) );
|
||||||
|
@ -995,16 +991,16 @@ public class OracleDialect extends Dialect {
|
||||||
|
|
||||||
if ( getVersion().isSameOrAfter( 21 ) ) {
|
if ( getVersion().isSameOrAfter( 21 ) ) {
|
||||||
typeContributions.contributeJdbcType( OracleJsonJdbcType.INSTANCE );
|
typeContributions.contributeJdbcType( OracleJsonJdbcType.INSTANCE );
|
||||||
typeContributions.contributeJdbcType( OracleJsonArrayJdbcType.INSTANCE );
|
typeContributions.contributeJdbcTypeConstructor( OracleJsonArrayJdbcTypeConstructor.NATIVE_INSTANCE );
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
typeContributions.contributeJdbcType( OracleJsonBlobJdbcType.INSTANCE );
|
typeContributions.contributeJdbcType( OracleJsonBlobJdbcType.INSTANCE );
|
||||||
typeContributions.contributeJdbcType( OracleJsonArrayBlobJdbcType.INSTANCE );
|
typeContributions.contributeJdbcTypeConstructor( OracleJsonArrayJdbcTypeConstructor.BLOB_INSTANCE );
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( OracleJdbcHelper.isUsable( serviceRegistry ) ) {
|
if ( OracleJdbcHelper.isUsable( serviceRegistry ) ) {
|
||||||
// Register a JdbcType to allow reading from native queries
|
// Register a JdbcType to allow reading from native queries
|
||||||
typeContributions.contributeJdbcType( new ArrayJdbcType( ObjectJdbcType.INSTANCE ) );
|
// typeContributions.contributeJdbcType( new ArrayJdbcType( ObjectJdbcType.INSTANCE ) );
|
||||||
typeContributions.contributeJdbcTypeConstructor( getArrayJdbcTypeConstructor( serviceRegistry ) );
|
typeContributions.contributeJdbcTypeConstructor( getArrayJdbcTypeConstructor( serviceRegistry ) );
|
||||||
typeContributions.contributeJdbcTypeConstructor( getNestedTableJdbcTypeConstructor( serviceRegistry ) );
|
typeContributions.contributeJdbcTypeConstructor( getNestedTableJdbcTypeConstructor( serviceRegistry ) );
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ package org.hibernate.dialect;
|
||||||
|
|
||||||
import org.hibernate.type.descriptor.converter.spi.BasicValueConverter;
|
import org.hibernate.type.descriptor.converter.spi.BasicValueConverter;
|
||||||
import org.hibernate.type.descriptor.java.JavaType;
|
import org.hibernate.type.descriptor.java.JavaType;
|
||||||
|
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
||||||
import org.hibernate.type.descriptor.jdbc.OracleJsonArrayBlobJdbcType;
|
import org.hibernate.type.descriptor.jdbc.OracleJsonArrayBlobJdbcType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -14,12 +15,9 @@ import org.hibernate.type.descriptor.jdbc.OracleJsonArrayBlobJdbcType;
|
||||||
* @author Christian Beikov
|
* @author Christian Beikov
|
||||||
*/
|
*/
|
||||||
public class OracleJsonArrayJdbcType extends OracleJsonArrayBlobJdbcType {
|
public class OracleJsonArrayJdbcType extends OracleJsonArrayBlobJdbcType {
|
||||||
/**
|
|
||||||
* Singleton access
|
|
||||||
*/
|
|
||||||
public static final OracleJsonArrayJdbcType INSTANCE = new OracleJsonArrayJdbcType();
|
|
||||||
|
|
||||||
private OracleJsonArrayJdbcType() {
|
public OracleJsonArrayJdbcType(JdbcType elementJdbcType) {
|
||||||
|
super( elementJdbcType );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -0,0 +1,51 @@
|
||||||
|
/*
|
||||||
|
* SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
|
* Copyright Red Hat Inc. and Hibernate Authors
|
||||||
|
*/
|
||||||
|
package org.hibernate.dialect;
|
||||||
|
|
||||||
|
import org.hibernate.tool.schema.extract.spi.ColumnTypeInformation;
|
||||||
|
import org.hibernate.type.BasicType;
|
||||||
|
import org.hibernate.type.SqlTypes;
|
||||||
|
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
||||||
|
import org.hibernate.type.descriptor.jdbc.JdbcTypeConstructor;
|
||||||
|
import org.hibernate.type.descriptor.jdbc.OracleJsonArrayBlobJdbcType;
|
||||||
|
import org.hibernate.type.spi.TypeConfiguration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Factory for {@link OracleJsonArrayJdbcType} and {@link OracleJsonArrayBlobJdbcType}.
|
||||||
|
*/
|
||||||
|
public class OracleJsonArrayJdbcTypeConstructor implements JdbcTypeConstructor {
|
||||||
|
|
||||||
|
public static final OracleJsonArrayJdbcTypeConstructor NATIVE_INSTANCE = new OracleJsonArrayJdbcTypeConstructor( true );
|
||||||
|
public static final OracleJsonArrayJdbcTypeConstructor BLOB_INSTANCE = new OracleJsonArrayJdbcTypeConstructor( false );
|
||||||
|
|
||||||
|
private final boolean nativeJson;
|
||||||
|
|
||||||
|
public OracleJsonArrayJdbcTypeConstructor(boolean nativeJson) {
|
||||||
|
this.nativeJson = nativeJson;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JdbcType resolveType(
|
||||||
|
TypeConfiguration typeConfiguration,
|
||||||
|
Dialect dialect,
|
||||||
|
BasicType<?> elementType,
|
||||||
|
ColumnTypeInformation columnTypeInformation) {
|
||||||
|
return resolveType( typeConfiguration, dialect, elementType.getJdbcType(), columnTypeInformation );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JdbcType resolveType(
|
||||||
|
TypeConfiguration typeConfiguration,
|
||||||
|
Dialect dialect,
|
||||||
|
JdbcType elementType,
|
||||||
|
ColumnTypeInformation columnTypeInformation) {
|
||||||
|
return nativeJson ? new OracleJsonArrayJdbcType( elementType ) : new OracleJsonArrayBlobJdbcType( elementType );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getDefaultSqlTypeCode() {
|
||||||
|
return SqlTypes.JSON_ARRAY;
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,17 +8,22 @@ import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||||
|
import org.hibernate.metamodel.mapping.CollectionPart;
|
||||||
import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart;
|
import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart;
|
||||||
import org.hibernate.metamodel.mapping.EntityIdentifierMapping;
|
import org.hibernate.metamodel.mapping.EntityIdentifierMapping;
|
||||||
import org.hibernate.metamodel.mapping.EntityMappingType;
|
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||||
import org.hibernate.metamodel.mapping.JdbcMappingContainer;
|
import org.hibernate.metamodel.mapping.JdbcMappingContainer;
|
||||||
|
import org.hibernate.metamodel.mapping.ModelPart;
|
||||||
import org.hibernate.query.IllegalQueryOperationException;
|
import org.hibernate.query.IllegalQueryOperationException;
|
||||||
|
import org.hibernate.query.derived.AnonymousTupleTableGroupProducer;
|
||||||
import org.hibernate.query.sqm.ComparisonOperator;
|
import org.hibernate.query.sqm.ComparisonOperator;
|
||||||
import org.hibernate.query.sqm.FetchClauseType;
|
import org.hibernate.query.sqm.FetchClauseType;
|
||||||
import org.hibernate.query.sqm.FrameExclusion;
|
import org.hibernate.query.sqm.FrameExclusion;
|
||||||
import org.hibernate.query.sqm.FrameKind;
|
import org.hibernate.query.sqm.FrameKind;
|
||||||
import org.hibernate.sql.ast.Clause;
|
import org.hibernate.sql.ast.Clause;
|
||||||
|
import org.hibernate.sql.ast.SqlAstNodeRenderingMode;
|
||||||
import org.hibernate.sql.ast.spi.SqlSelection;
|
import org.hibernate.sql.ast.spi.SqlSelection;
|
||||||
|
import org.hibernate.sql.ast.tree.SqlAstNode;
|
||||||
import org.hibernate.sql.ast.tree.Statement;
|
import org.hibernate.sql.ast.tree.Statement;
|
||||||
import org.hibernate.sql.ast.tree.cte.CteMaterialization;
|
import org.hibernate.sql.ast.tree.cte.CteMaterialization;
|
||||||
import org.hibernate.sql.ast.tree.expression.BinaryArithmeticExpression;
|
import org.hibernate.sql.ast.tree.expression.BinaryArithmeticExpression;
|
||||||
|
@ -30,6 +35,7 @@ import org.hibernate.sql.ast.tree.expression.Over;
|
||||||
import org.hibernate.sql.ast.tree.expression.SqlTuple;
|
import org.hibernate.sql.ast.tree.expression.SqlTuple;
|
||||||
import org.hibernate.sql.ast.tree.expression.SqlTupleContainer;
|
import org.hibernate.sql.ast.tree.expression.SqlTupleContainer;
|
||||||
import org.hibernate.sql.ast.tree.expression.Summarization;
|
import org.hibernate.sql.ast.tree.expression.Summarization;
|
||||||
|
import org.hibernate.sql.ast.tree.from.DerivedTableReference;
|
||||||
import org.hibernate.sql.ast.tree.from.FromClause;
|
import org.hibernate.sql.ast.tree.from.FromClause;
|
||||||
import org.hibernate.sql.ast.tree.from.FunctionTableReference;
|
import org.hibernate.sql.ast.tree.from.FunctionTableReference;
|
||||||
import org.hibernate.sql.ast.tree.from.NamedTableReference;
|
import org.hibernate.sql.ast.tree.from.NamedTableReference;
|
||||||
|
@ -254,9 +260,42 @@ public class OracleSqlAstTranslator<T extends JdbcOperation> extends SqlAstTrans
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visitFunctionTableReference(FunctionTableReference tableReference) {
|
public void visitFunctionTableReference(FunctionTableReference tableReference) {
|
||||||
append( "table(" );
|
|
||||||
tableReference.getFunctionExpression().accept( this );
|
tableReference.getFunctionExpression().accept( this );
|
||||||
append( CLOSE_PARENTHESIS );
|
if ( !tableReference.rendersIdentifierVariable() ) {
|
||||||
|
renderDerivedTableReferenceIdentificationVariable( tableReference );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void renderNamedSetReturningFunction(String functionName, List<? extends SqlAstNode> sqlAstArguments, AnonymousTupleTableGroupProducer tupleType, String tableIdentifierVariable, SqlAstNodeRenderingMode argumentRenderingMode) {
|
||||||
|
final ModelPart ordinalitySubPart = tupleType.findSubPart( CollectionPart.Nature.INDEX.getName(), null );
|
||||||
|
if ( ordinalitySubPart != null ) {
|
||||||
|
appendSql( "lateral (select t.*, rownum " );
|
||||||
|
appendSql( ordinalitySubPart.asBasicValuedModelPart().getSelectionExpression() );
|
||||||
|
appendSql( " from table(" );
|
||||||
|
renderSimpleNamedFunction( functionName, sqlAstArguments, argumentRenderingMode );
|
||||||
|
append( ") t)" );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
appendSql( "table(" );
|
||||||
|
super.renderNamedSetReturningFunction( functionName, sqlAstArguments, tupleType, tableIdentifierVariable, argumentRenderingMode );
|
||||||
|
append( ')' );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void renderDerivedTableReference(DerivedTableReference tableReference) {
|
||||||
|
if ( tableReference instanceof FunctionTableReference && tableReference.isLateral() ) {
|
||||||
|
// No need for a lateral keyword for functions
|
||||||
|
tableReference.accept( this );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
super.renderDerivedTableReference( tableReference );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void renderDerivedTableReferenceIdentificationVariable(DerivedTableReference tableReference) {
|
||||||
renderTableReferenceIdentificationVariable( tableReference );
|
renderTableReferenceIdentificationVariable( tableReference );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,7 @@ import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
|
||||||
import org.hibernate.boot.registry.classloading.spi.ClassLoadingException;
|
import org.hibernate.boot.registry.classloading.spi.ClassLoadingException;
|
||||||
import org.hibernate.service.ServiceRegistry;
|
import org.hibernate.service.ServiceRegistry;
|
||||||
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
||||||
|
import org.hibernate.type.descriptor.jdbc.JdbcTypeConstructor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The following class provides some convenience methods for accessing JdbcType instance,
|
* The following class provides some convenience methods for accessing JdbcType instance,
|
||||||
|
@ -52,12 +53,12 @@ public final class PgJdbcHelper {
|
||||||
return createJdbcType( serviceRegistry, "org.hibernate.dialect.PostgreSQLJsonPGObjectJsonbType" );
|
return createJdbcType( serviceRegistry, "org.hibernate.dialect.PostgreSQLJsonPGObjectJsonbType" );
|
||||||
}
|
}
|
||||||
|
|
||||||
public static JdbcType getJsonArrayJdbcType(ServiceRegistry serviceRegistry) {
|
public static JdbcTypeConstructor getJsonArrayJdbcType(ServiceRegistry serviceRegistry) {
|
||||||
return createJdbcType( serviceRegistry, "org.hibernate.dialect.PostgreSQLJsonArrayPGObjectJsonType" );
|
return createJdbcTypeConstructor( serviceRegistry, "org.hibernate.dialect.PostgreSQLJsonArrayPGObjectJsonJdbcTypeConstructor" );
|
||||||
}
|
}
|
||||||
|
|
||||||
public static JdbcType getJsonbArrayJdbcType(ServiceRegistry serviceRegistry) {
|
public static JdbcTypeConstructor getJsonbArrayJdbcType(ServiceRegistry serviceRegistry) {
|
||||||
return createJdbcType( serviceRegistry, "org.hibernate.dialect.PostgreSQLJsonArrayPGObjectJsonbType" );
|
return createJdbcTypeConstructor( serviceRegistry, "org.hibernate.dialect.PostgreSQLJsonArrayPGObjectJsonbJdbcTypeConstructor" );
|
||||||
}
|
}
|
||||||
|
|
||||||
public static JdbcType createJdbcType(ServiceRegistry serviceRegistry, String className) {
|
public static JdbcType createJdbcType(ServiceRegistry serviceRegistry, String className) {
|
||||||
|
@ -74,4 +75,19 @@ public final class PgJdbcHelper {
|
||||||
throw new HibernateError( "Could not construct JdbcType", e );
|
throw new HibernateError( "Could not construct JdbcType", e );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static JdbcTypeConstructor createJdbcTypeConstructor(ServiceRegistry serviceRegistry, String className) {
|
||||||
|
final ClassLoaderService classLoaderService = serviceRegistry.requireService( ClassLoaderService.class );
|
||||||
|
try {
|
||||||
|
final Class<?> clazz = classLoaderService.classForName( className );
|
||||||
|
final Constructor<?> constructor = clazz.getConstructor();
|
||||||
|
return (JdbcTypeConstructor) constructor.newInstance();
|
||||||
|
}
|
||||||
|
catch (NoSuchMethodException e) {
|
||||||
|
throw new HibernateError( "Class does not have an empty constructor", e );
|
||||||
|
}
|
||||||
|
catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
|
||||||
|
throw new HibernateError( "Could not construct JdbcTypeConstructor", e );
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -73,9 +73,11 @@ public class PostgreSQLArrayJdbcType extends ArrayJdbcType {
|
||||||
);
|
);
|
||||||
objects = new Object[domainObjects.length];
|
objects = new Object[domainObjects.length];
|
||||||
for ( int i = 0; i < domainObjects.length; i++ ) {
|
for ( int i = 0; i < domainObjects.length; i++ ) {
|
||||||
|
if ( domainObjects[i] != null ) {
|
||||||
objects[i] = aggregateJdbcType.createJdbcValue( domainObjects[i], options );
|
objects[i] = aggregateJdbcType.createJdbcValue( domainObjects[i], options );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
objects = arrayJdbcType.getArray( this, elementBinder, value, options );
|
objects = arrayJdbcType.getArray( this, elementBinder, value, options );
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
package org.hibernate.dialect;
|
package org.hibernate.dialect;
|
||||||
|
|
||||||
import org.hibernate.sql.ast.spi.SqlAppender;
|
import org.hibernate.sql.ast.spi.SqlAppender;
|
||||||
|
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
||||||
import org.hibernate.type.descriptor.jdbc.JsonArrayJdbcType;
|
import org.hibernate.type.descriptor.jdbc.JsonArrayJdbcType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -12,12 +13,10 @@ import org.hibernate.type.descriptor.jdbc.JsonArrayJdbcType;
|
||||||
*/
|
*/
|
||||||
public class PostgreSQLCastingJsonArrayJdbcType extends JsonArrayJdbcType {
|
public class PostgreSQLCastingJsonArrayJdbcType extends JsonArrayJdbcType {
|
||||||
|
|
||||||
public static final PostgreSQLCastingJsonArrayJdbcType JSON_INSTANCE = new PostgreSQLCastingJsonArrayJdbcType( false );
|
|
||||||
public static final PostgreSQLCastingJsonArrayJdbcType JSONB_INSTANCE = new PostgreSQLCastingJsonArrayJdbcType( true );
|
|
||||||
|
|
||||||
private final boolean jsonb;
|
private final boolean jsonb;
|
||||||
|
|
||||||
public PostgreSQLCastingJsonArrayJdbcType(boolean jsonb) {
|
public PostgreSQLCastingJsonArrayJdbcType(JdbcType elementJdbcType, boolean jsonb) {
|
||||||
|
super( elementJdbcType );
|
||||||
this.jsonb = jsonb;
|
this.jsonb = jsonb;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
/*
|
||||||
|
* SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
|
* Copyright Red Hat Inc. and Hibernate Authors
|
||||||
|
*/
|
||||||
|
package org.hibernate.dialect;
|
||||||
|
|
||||||
|
import org.hibernate.tool.schema.extract.spi.ColumnTypeInformation;
|
||||||
|
import org.hibernate.type.BasicType;
|
||||||
|
import org.hibernate.type.SqlTypes;
|
||||||
|
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
||||||
|
import org.hibernate.type.descriptor.jdbc.JdbcTypeConstructor;
|
||||||
|
import org.hibernate.type.spi.TypeConfiguration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Factory for {@link PostgreSQLCastingJsonArrayJdbcType}.
|
||||||
|
*/
|
||||||
|
public class PostgreSQLCastingJsonArrayJdbcTypeConstructor implements JdbcTypeConstructor {
|
||||||
|
|
||||||
|
public static final PostgreSQLCastingJsonArrayJdbcTypeConstructor JSONB_INSTANCE = new PostgreSQLCastingJsonArrayJdbcTypeConstructor( true );
|
||||||
|
public static final PostgreSQLCastingJsonArrayJdbcTypeConstructor JSON_INSTANCE = new PostgreSQLCastingJsonArrayJdbcTypeConstructor( false );
|
||||||
|
|
||||||
|
private final boolean jsonb;
|
||||||
|
|
||||||
|
public PostgreSQLCastingJsonArrayJdbcTypeConstructor(boolean jsonb) {
|
||||||
|
this.jsonb = jsonb;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JdbcType resolveType(
|
||||||
|
TypeConfiguration typeConfiguration,
|
||||||
|
Dialect dialect,
|
||||||
|
BasicType<?> elementType,
|
||||||
|
ColumnTypeInformation columnTypeInformation) {
|
||||||
|
return resolveType( typeConfiguration, dialect, elementType.getJdbcType(), columnTypeInformation );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JdbcType resolveType(
|
||||||
|
TypeConfiguration typeConfiguration,
|
||||||
|
Dialect dialect,
|
||||||
|
JdbcType elementType,
|
||||||
|
ColumnTypeInformation columnTypeInformation) {
|
||||||
|
return new PostgreSQLCastingJsonArrayJdbcType( elementType, jsonb );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getDefaultSqlTypeCode() {
|
||||||
|
return SqlTypes.JSON_ARRAY;
|
||||||
|
}
|
||||||
|
}
|
|
@ -17,6 +17,7 @@ import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.TimeZone;
|
import java.util.TimeZone;
|
||||||
|
|
||||||
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
import org.hibernate.Length;
|
import org.hibernate.Length;
|
||||||
import org.hibernate.LockMode;
|
import org.hibernate.LockMode;
|
||||||
import org.hibernate.LockOptions;
|
import org.hibernate.LockOptions;
|
||||||
|
@ -111,7 +112,6 @@ import static org.hibernate.type.SqlTypes.GEOGRAPHY;
|
||||||
import static org.hibernate.type.SqlTypes.GEOMETRY;
|
import static org.hibernate.type.SqlTypes.GEOMETRY;
|
||||||
import static org.hibernate.type.SqlTypes.INET;
|
import static org.hibernate.type.SqlTypes.INET;
|
||||||
import static org.hibernate.type.SqlTypes.JSON;
|
import static org.hibernate.type.SqlTypes.JSON;
|
||||||
import static org.hibernate.type.SqlTypes.JSON_ARRAY;
|
|
||||||
import static org.hibernate.type.SqlTypes.LONG32NVARCHAR;
|
import static org.hibernate.type.SqlTypes.LONG32NVARCHAR;
|
||||||
import static org.hibernate.type.SqlTypes.LONG32VARBINARY;
|
import static org.hibernate.type.SqlTypes.LONG32VARBINARY;
|
||||||
import static org.hibernate.type.SqlTypes.LONG32VARCHAR;
|
import static org.hibernate.type.SqlTypes.LONG32VARCHAR;
|
||||||
|
@ -261,7 +261,6 @@ public class PostgreSQLDialect extends Dialect {
|
||||||
|
|
||||||
// Prefer jsonb if possible
|
// Prefer jsonb if possible
|
||||||
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( JSON, "jsonb", this ) );
|
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( JSON, "jsonb", this ) );
|
||||||
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( JSON_ARRAY, "jsonb", this ) );
|
|
||||||
|
|
||||||
ddlTypeRegistry.addDescriptor( new NamedNativeEnumDdlTypeImpl( this ) );
|
ddlTypeRegistry.addDescriptor( new NamedNativeEnumDdlTypeImpl( this ) );
|
||||||
ddlTypeRegistry.addDescriptor( new NamedNativeOrdinalEnumDdlTypeImpl( this ) );
|
ddlTypeRegistry.addDescriptor( new NamedNativeOrdinalEnumDdlTypeImpl( this ) );
|
||||||
|
@ -665,6 +664,18 @@ public class PostgreSQLDialect extends Dialect {
|
||||||
);
|
);
|
||||||
functionContributions.getFunctionRegistry().registerAlternateKey( "truncate", "trunc" );
|
functionContributions.getFunctionRegistry().registerAlternateKey( "truncate", "trunc" );
|
||||||
functionFactory.dateTrunc();
|
functionFactory.dateTrunc();
|
||||||
|
|
||||||
|
if ( getVersion().isSameOrAfter( 17 ) ) {
|
||||||
|
functionFactory.unnest( null, "ordinality" );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
functionFactory.unnest_postgresql();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable String getDefaultOrdinalityColumnName() {
|
||||||
|
return "ordinality";
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1415,14 +1426,14 @@ public class PostgreSQLDialect extends Dialect {
|
||||||
jdbcTypeRegistry.addDescriptorIfAbsent( PgJdbcHelper.getIntervalJdbcType( serviceRegistry ) );
|
jdbcTypeRegistry.addDescriptorIfAbsent( PgJdbcHelper.getIntervalJdbcType( serviceRegistry ) );
|
||||||
jdbcTypeRegistry.addDescriptorIfAbsent( PgJdbcHelper.getStructJdbcType( serviceRegistry ) );
|
jdbcTypeRegistry.addDescriptorIfAbsent( PgJdbcHelper.getStructJdbcType( serviceRegistry ) );
|
||||||
jdbcTypeRegistry.addDescriptorIfAbsent( PgJdbcHelper.getJsonbJdbcType( serviceRegistry ) );
|
jdbcTypeRegistry.addDescriptorIfAbsent( PgJdbcHelper.getJsonbJdbcType( serviceRegistry ) );
|
||||||
jdbcTypeRegistry.addDescriptorIfAbsent( PgJdbcHelper.getJsonbArrayJdbcType( serviceRegistry ) );
|
jdbcTypeRegistry.addTypeConstructorIfAbsent( PgJdbcHelper.getJsonbArrayJdbcType( serviceRegistry ) );
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
jdbcTypeRegistry.addDescriptorIfAbsent( PostgreSQLCastingInetJdbcType.INSTANCE );
|
jdbcTypeRegistry.addDescriptorIfAbsent( PostgreSQLCastingInetJdbcType.INSTANCE );
|
||||||
jdbcTypeRegistry.addDescriptorIfAbsent( PostgreSQLCastingIntervalSecondJdbcType.INSTANCE );
|
jdbcTypeRegistry.addDescriptorIfAbsent( PostgreSQLCastingIntervalSecondJdbcType.INSTANCE );
|
||||||
jdbcTypeRegistry.addDescriptorIfAbsent( PostgreSQLStructCastingJdbcType.INSTANCE );
|
jdbcTypeRegistry.addDescriptorIfAbsent( PostgreSQLStructCastingJdbcType.INSTANCE );
|
||||||
jdbcTypeRegistry.addDescriptorIfAbsent( PostgreSQLCastingJsonJdbcType.JSONB_INSTANCE );
|
jdbcTypeRegistry.addDescriptorIfAbsent( PostgreSQLCastingJsonJdbcType.JSONB_INSTANCE );
|
||||||
jdbcTypeRegistry.addDescriptorIfAbsent( PostgreSQLCastingJsonArrayJdbcType.JSONB_INSTANCE );
|
jdbcTypeRegistry.addTypeConstructorIfAbsent( PostgreSQLCastingJsonArrayJdbcTypeConstructor.JSONB_INSTANCE );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -1430,7 +1441,7 @@ public class PostgreSQLDialect extends Dialect {
|
||||||
jdbcTypeRegistry.addDescriptorIfAbsent( PostgreSQLCastingIntervalSecondJdbcType.INSTANCE );
|
jdbcTypeRegistry.addDescriptorIfAbsent( PostgreSQLCastingIntervalSecondJdbcType.INSTANCE );
|
||||||
jdbcTypeRegistry.addDescriptorIfAbsent( PostgreSQLStructCastingJdbcType.INSTANCE );
|
jdbcTypeRegistry.addDescriptorIfAbsent( PostgreSQLStructCastingJdbcType.INSTANCE );
|
||||||
jdbcTypeRegistry.addDescriptorIfAbsent( PostgreSQLCastingJsonJdbcType.JSONB_INSTANCE );
|
jdbcTypeRegistry.addDescriptorIfAbsent( PostgreSQLCastingJsonJdbcType.JSONB_INSTANCE );
|
||||||
jdbcTypeRegistry.addDescriptorIfAbsent( PostgreSQLCastingJsonArrayJdbcType.JSONB_INSTANCE );
|
jdbcTypeRegistry.addTypeConstructorIfAbsent( PostgreSQLCastingJsonArrayJdbcTypeConstructor.JSONB_INSTANCE );
|
||||||
}
|
}
|
||||||
|
|
||||||
// PostgreSQL requires a custom binder for binding untyped nulls as VARBINARY
|
// PostgreSQL requires a custom binder for binding untyped nulls as VARBINARY
|
||||||
|
@ -1450,6 +1461,7 @@ public class PostgreSQLDialect extends Dialect {
|
||||||
jdbcTypeRegistry.addDescriptor( PostgreSQLOrdinalEnumJdbcType.INSTANCE );
|
jdbcTypeRegistry.addDescriptor( PostgreSQLOrdinalEnumJdbcType.INSTANCE );
|
||||||
jdbcTypeRegistry.addDescriptor( PostgreSQLUUIDJdbcType.INSTANCE );
|
jdbcTypeRegistry.addDescriptor( PostgreSQLUUIDJdbcType.INSTANCE );
|
||||||
|
|
||||||
|
// Replace the standard array constructor
|
||||||
jdbcTypeRegistry.addTypeConstructor( PostgreSQLArrayJdbcTypeConstructor.INSTANCE );
|
jdbcTypeRegistry.addTypeConstructor( PostgreSQLArrayJdbcTypeConstructor.INSTANCE );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,41 @@
|
||||||
|
/*
|
||||||
|
* SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
|
* Copyright Red Hat Inc. and Hibernate Authors
|
||||||
|
*/
|
||||||
|
package org.hibernate.dialect;
|
||||||
|
|
||||||
|
import org.hibernate.tool.schema.extract.spi.ColumnTypeInformation;
|
||||||
|
import org.hibernate.type.BasicType;
|
||||||
|
import org.hibernate.type.SqlTypes;
|
||||||
|
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
||||||
|
import org.hibernate.type.descriptor.jdbc.JdbcTypeConstructor;
|
||||||
|
import org.hibernate.type.spi.TypeConfiguration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Factory for {@link PostgreSQLCastingJsonArrayJdbcType}.
|
||||||
|
*/
|
||||||
|
public class PostgreSQLJsonArrayPGObjectJsonJdbcTypeConstructor implements JdbcTypeConstructor {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JdbcType resolveType(
|
||||||
|
TypeConfiguration typeConfiguration,
|
||||||
|
Dialect dialect,
|
||||||
|
BasicType<?> elementType,
|
||||||
|
ColumnTypeInformation columnTypeInformation) {
|
||||||
|
return resolveType( typeConfiguration, dialect, elementType.getJdbcType(), columnTypeInformation );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JdbcType resolveType(
|
||||||
|
TypeConfiguration typeConfiguration,
|
||||||
|
Dialect dialect,
|
||||||
|
JdbcType elementType,
|
||||||
|
ColumnTypeInformation columnTypeInformation) {
|
||||||
|
return new PostgreSQLJsonArrayPGObjectType( elementType, false );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getDefaultSqlTypeCode() {
|
||||||
|
return SqlTypes.JSON_ARRAY;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,14 +0,0 @@
|
||||||
/*
|
|
||||||
* SPDX-License-Identifier: LGPL-2.1-or-later
|
|
||||||
* Copyright Red Hat Inc. and Hibernate Authors
|
|
||||||
*/
|
|
||||||
package org.hibernate.dialect;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Christian Beikov
|
|
||||||
*/
|
|
||||||
public class PostgreSQLJsonArrayPGObjectJsonType extends AbstractPostgreSQLJsonArrayPGObjectType {
|
|
||||||
public PostgreSQLJsonArrayPGObjectJsonType() {
|
|
||||||
super( false );
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,41 @@
|
||||||
|
/*
|
||||||
|
* SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
|
* Copyright Red Hat Inc. and Hibernate Authors
|
||||||
|
*/
|
||||||
|
package org.hibernate.dialect;
|
||||||
|
|
||||||
|
import org.hibernate.tool.schema.extract.spi.ColumnTypeInformation;
|
||||||
|
import org.hibernate.type.BasicType;
|
||||||
|
import org.hibernate.type.SqlTypes;
|
||||||
|
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
||||||
|
import org.hibernate.type.descriptor.jdbc.JdbcTypeConstructor;
|
||||||
|
import org.hibernate.type.spi.TypeConfiguration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Factory for {@link PostgreSQLCastingJsonArrayJdbcType}.
|
||||||
|
*/
|
||||||
|
public class PostgreSQLJsonArrayPGObjectJsonbJdbcTypeConstructor implements JdbcTypeConstructor {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JdbcType resolveType(
|
||||||
|
TypeConfiguration typeConfiguration,
|
||||||
|
Dialect dialect,
|
||||||
|
BasicType<?> elementType,
|
||||||
|
ColumnTypeInformation columnTypeInformation) {
|
||||||
|
return resolveType( typeConfiguration, dialect, elementType.getJdbcType(), columnTypeInformation );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JdbcType resolveType(
|
||||||
|
TypeConfiguration typeConfiguration,
|
||||||
|
Dialect dialect,
|
||||||
|
JdbcType elementType,
|
||||||
|
ColumnTypeInformation columnTypeInformation) {
|
||||||
|
return new PostgreSQLJsonArrayPGObjectType( elementType, true );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getDefaultSqlTypeCode() {
|
||||||
|
return SqlTypes.JSON_ARRAY;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,14 +0,0 @@
|
||||||
/*
|
|
||||||
* SPDX-License-Identifier: LGPL-2.1-or-later
|
|
||||||
* Copyright Red Hat Inc. and Hibernate Authors
|
|
||||||
*/
|
|
||||||
package org.hibernate.dialect;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Christian Beikov
|
|
||||||
*/
|
|
||||||
public class PostgreSQLJsonArrayPGObjectJsonbType extends AbstractPostgreSQLJsonArrayPGObjectType {
|
|
||||||
public PostgreSQLJsonArrayPGObjectJsonbType() {
|
|
||||||
super( true );
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -16,6 +16,7 @@ import org.hibernate.type.descriptor.WrapperOptions;
|
||||||
import org.hibernate.type.descriptor.java.JavaType;
|
import org.hibernate.type.descriptor.java.JavaType;
|
||||||
import org.hibernate.type.descriptor.jdbc.BasicBinder;
|
import org.hibernate.type.descriptor.jdbc.BasicBinder;
|
||||||
import org.hibernate.type.descriptor.jdbc.BasicExtractor;
|
import org.hibernate.type.descriptor.jdbc.BasicExtractor;
|
||||||
|
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
||||||
import org.hibernate.type.descriptor.jdbc.JsonArrayJdbcType;
|
import org.hibernate.type.descriptor.jdbc.JsonArrayJdbcType;
|
||||||
|
|
||||||
import org.postgresql.util.PGobject;
|
import org.postgresql.util.PGobject;
|
||||||
|
@ -23,10 +24,12 @@ import org.postgresql.util.PGobject;
|
||||||
/**
|
/**
|
||||||
* @author Christian Beikov
|
* @author Christian Beikov
|
||||||
*/
|
*/
|
||||||
public abstract class AbstractPostgreSQLJsonArrayPGObjectType extends JsonArrayJdbcType {
|
public class PostgreSQLJsonArrayPGObjectType extends JsonArrayJdbcType {
|
||||||
|
|
||||||
private final boolean jsonb;
|
private final boolean jsonb;
|
||||||
protected AbstractPostgreSQLJsonArrayPGObjectType(boolean jsonb) {
|
|
||||||
|
public PostgreSQLJsonArrayPGObjectType(JdbcType elementJdbcType, boolean jsonb) {
|
||||||
|
super( elementJdbcType );
|
||||||
this.jsonb = jsonb;
|
this.jsonb = jsonb;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,7 +44,7 @@ public abstract class AbstractPostgreSQLJsonArrayPGObjectType extends JsonArrayJ
|
||||||
@Override
|
@Override
|
||||||
protected void doBind(PreparedStatement st, X value, int index, WrapperOptions options)
|
protected void doBind(PreparedStatement st, X value, int index, WrapperOptions options)
|
||||||
throws SQLException {
|
throws SQLException {
|
||||||
final String stringValue = ( (AbstractPostgreSQLJsonArrayPGObjectType) getJdbcType() ).toString(
|
final String stringValue = ( (PostgreSQLJsonArrayPGObjectType) getJdbcType() ).toString(
|
||||||
value,
|
value,
|
||||||
getJavaType(),
|
getJavaType(),
|
||||||
options
|
options
|
||||||
|
@ -55,7 +58,7 @@ public abstract class AbstractPostgreSQLJsonArrayPGObjectType extends JsonArrayJ
|
||||||
@Override
|
@Override
|
||||||
protected void doBind(CallableStatement st, X value, String name, WrapperOptions options)
|
protected void doBind(CallableStatement st, X value, String name, WrapperOptions options)
|
||||||
throws SQLException {
|
throws SQLException {
|
||||||
final String stringValue = ( (AbstractPostgreSQLJsonArrayPGObjectType) getJdbcType() ).toString(
|
final String stringValue = ( (PostgreSQLJsonArrayPGObjectType) getJdbcType() ).toString(
|
||||||
value,
|
value,
|
||||||
getJavaType(),
|
getJavaType(),
|
||||||
options
|
options
|
||||||
|
@ -91,7 +94,7 @@ public abstract class AbstractPostgreSQLJsonArrayPGObjectType extends JsonArrayJ
|
||||||
if ( object == null ) {
|
if ( object == null ) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return ( (AbstractPostgreSQLJsonArrayPGObjectType) getJdbcType() ).fromString(
|
return ( (PostgreSQLJsonArrayPGObjectType) getJdbcType() ).fromString(
|
||||||
object.toString(),
|
object.toString(),
|
||||||
getJavaType(),
|
getJavaType(),
|
||||||
options
|
options
|
|
@ -0,0 +1,29 @@
|
||||||
|
/*
|
||||||
|
* SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
|
* Copyright Red Hat Inc. and Hibernate Authors
|
||||||
|
*/
|
||||||
|
package org.hibernate.dialect;
|
||||||
|
|
||||||
|
import org.hibernate.sql.ast.spi.SqlAppender;
|
||||||
|
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
||||||
|
import org.hibernate.type.descriptor.jdbc.XmlArrayJdbcType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Christian Beikov
|
||||||
|
*/
|
||||||
|
public class SQLServerCastingXmlArrayJdbcType extends XmlArrayJdbcType {
|
||||||
|
|
||||||
|
public SQLServerCastingXmlArrayJdbcType(JdbcType elementJdbcType) {
|
||||||
|
super( elementJdbcType );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void appendWriteExpression(
|
||||||
|
String writeExpression,
|
||||||
|
SqlAppender appender,
|
||||||
|
Dialect dialect) {
|
||||||
|
appender.append( "cast(" );
|
||||||
|
appender.append( writeExpression );
|
||||||
|
appender.append( " as xml)" );
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,43 @@
|
||||||
|
/*
|
||||||
|
* SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
|
* Copyright Red Hat Inc. and Hibernate Authors
|
||||||
|
*/
|
||||||
|
package org.hibernate.dialect;
|
||||||
|
|
||||||
|
import org.hibernate.tool.schema.extract.spi.ColumnTypeInformation;
|
||||||
|
import org.hibernate.type.BasicType;
|
||||||
|
import org.hibernate.type.SqlTypes;
|
||||||
|
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
||||||
|
import org.hibernate.type.descriptor.jdbc.JdbcTypeConstructor;
|
||||||
|
import org.hibernate.type.spi.TypeConfiguration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Factory for {@link SQLServerCastingXmlArrayJdbcType}.
|
||||||
|
*/
|
||||||
|
public class SQLServerCastingXmlArrayJdbcTypeConstructor implements JdbcTypeConstructor {
|
||||||
|
|
||||||
|
public static final SQLServerCastingXmlArrayJdbcTypeConstructor INSTANCE = new SQLServerCastingXmlArrayJdbcTypeConstructor();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JdbcType resolveType(
|
||||||
|
TypeConfiguration typeConfiguration,
|
||||||
|
Dialect dialect,
|
||||||
|
BasicType<?> elementType,
|
||||||
|
ColumnTypeInformation columnTypeInformation) {
|
||||||
|
return resolveType( typeConfiguration, dialect, elementType.getJdbcType(), columnTypeInformation );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JdbcType resolveType(
|
||||||
|
TypeConfiguration typeConfiguration,
|
||||||
|
Dialect dialect,
|
||||||
|
JdbcType elementType,
|
||||||
|
ColumnTypeInformation columnTypeInformation) {
|
||||||
|
return new SQLServerCastingXmlArrayJdbcType( elementType );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getDefaultSqlTypeCode() {
|
||||||
|
return SqlTypes.XML_ARRAY;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,43 @@
|
||||||
|
/*
|
||||||
|
* SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
|
* Copyright Red Hat Inc. and Hibernate Authors
|
||||||
|
*/
|
||||||
|
package org.hibernate.dialect;
|
||||||
|
|
||||||
|
import org.hibernate.metamodel.mapping.EmbeddableMappingType;
|
||||||
|
import org.hibernate.metamodel.spi.RuntimeModelCreationContext;
|
||||||
|
import org.hibernate.sql.ast.spi.SqlAppender;
|
||||||
|
import org.hibernate.type.descriptor.jdbc.AggregateJdbcType;
|
||||||
|
import org.hibernate.type.descriptor.jdbc.XmlJdbcType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Christian Beikov
|
||||||
|
*/
|
||||||
|
public class SQLServerCastingXmlJdbcType extends XmlJdbcType {
|
||||||
|
/**
|
||||||
|
* Singleton access
|
||||||
|
*/
|
||||||
|
public static final XmlJdbcType INSTANCE = new SQLServerCastingXmlJdbcType( null );
|
||||||
|
|
||||||
|
public SQLServerCastingXmlJdbcType(EmbeddableMappingType embeddableMappingType) {
|
||||||
|
super( embeddableMappingType );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AggregateJdbcType resolveAggregateJdbcType(
|
||||||
|
EmbeddableMappingType mappingType,
|
||||||
|
String sqlType,
|
||||||
|
RuntimeModelCreationContext creationContext) {
|
||||||
|
return new SQLServerCastingXmlJdbcType( mappingType );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void appendWriteExpression(
|
||||||
|
String writeExpression,
|
||||||
|
SqlAppender appender,
|
||||||
|
Dialect dialect) {
|
||||||
|
appender.append( "cast(" );
|
||||||
|
appender.append( writeExpression );
|
||||||
|
appender.append( " as xml)" );
|
||||||
|
}
|
||||||
|
}
|
|
@ -82,7 +82,6 @@ import org.hibernate.type.descriptor.jdbc.JdbcType;
|
||||||
import org.hibernate.type.descriptor.jdbc.TimestampUtcAsJdbcTimestampJdbcType;
|
import org.hibernate.type.descriptor.jdbc.TimestampUtcAsJdbcTimestampJdbcType;
|
||||||
import org.hibernate.type.descriptor.jdbc.TinyIntAsSmallIntJdbcType;
|
import org.hibernate.type.descriptor.jdbc.TinyIntAsSmallIntJdbcType;
|
||||||
import org.hibernate.type.descriptor.jdbc.UUIDJdbcType;
|
import org.hibernate.type.descriptor.jdbc.UUIDJdbcType;
|
||||||
import org.hibernate.type.descriptor.jdbc.XmlJdbcType;
|
|
||||||
import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry;
|
import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry;
|
||||||
import org.hibernate.type.descriptor.sql.internal.DdlTypeImpl;
|
import org.hibernate.type.descriptor.sql.internal.DdlTypeImpl;
|
||||||
import org.hibernate.type.descriptor.sql.spi.DdlTypeRegistry;
|
import org.hibernate.type.descriptor.sql.spi.DdlTypeRegistry;
|
||||||
|
@ -116,6 +115,7 @@ import static org.hibernate.type.SqlTypes.TIME_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.VARCHAR;
|
import static org.hibernate.type.SqlTypes.VARCHAR;
|
||||||
|
import static org.hibernate.type.SqlTypes.XML_ARRAY;
|
||||||
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsDate;
|
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsDate;
|
||||||
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTime;
|
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTime;
|
||||||
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTimestampWithMicros;
|
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTimestampWithMicros;
|
||||||
|
@ -267,6 +267,11 @@ public class SQLServerDialect extends AbstractTransactSQLDialect {
|
||||||
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( UUID, "uniqueidentifier", this ) );
|
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( UUID, "uniqueidentifier", this ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getPreferredSqlTypeCodeForArray() {
|
||||||
|
return XML_ARRAY;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public JdbcType resolveSqlTypeDescriptor(
|
public JdbcType resolveSqlTypeDescriptor(
|
||||||
String columnTypeName,
|
String columnTypeName,
|
||||||
|
@ -329,8 +334,9 @@ public class SQLServerDialect extends AbstractTransactSQLDialect {
|
||||||
Types.TINYINT,
|
Types.TINYINT,
|
||||||
TinyIntAsSmallIntJdbcType.INSTANCE
|
TinyIntAsSmallIntJdbcType.INSTANCE
|
||||||
);
|
);
|
||||||
typeContributions.contributeJdbcType( XmlJdbcType.INSTANCE );
|
typeContributions.contributeJdbcType( SQLServerCastingXmlJdbcType.INSTANCE );
|
||||||
typeContributions.contributeJdbcType( UUIDJdbcType.INSTANCE );
|
typeContributions.contributeJdbcType( UUIDJdbcType.INSTANCE );
|
||||||
|
typeContributions.contributeJdbcTypeConstructor( SQLServerCastingXmlArrayJdbcTypeConstructor.INSTANCE );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -439,6 +445,9 @@ public class SQLServerDialect extends AbstractTransactSQLDialect {
|
||||||
functionFactory.xmlquery_sqlserver();
|
functionFactory.xmlquery_sqlserver();
|
||||||
functionFactory.xmlexists_sqlserver();
|
functionFactory.xmlexists_sqlserver();
|
||||||
functionFactory.xmlagg_sqlserver();
|
functionFactory.xmlagg_sqlserver();
|
||||||
|
|
||||||
|
functionFactory.unnest_sqlserver();
|
||||||
|
|
||||||
if ( getVersion().isSameOrAfter( 14 ) ) {
|
if ( getVersion().isSameOrAfter( 14 ) ) {
|
||||||
functionFactory.listagg_stringAggWithinGroup( "varchar(max)" );
|
functionFactory.listagg_stringAggWithinGroup( "varchar(max)" );
|
||||||
functionFactory.jsonArrayAgg_sqlserver( getVersion().isSameOrAfter( 16 ) );
|
functionFactory.jsonArrayAgg_sqlserver( getVersion().isSameOrAfter( 16 ) );
|
||||||
|
|
|
@ -10,13 +10,18 @@ import org.hibernate.LockMode;
|
||||||
import org.hibernate.LockOptions;
|
import org.hibernate.LockOptions;
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||||
import org.hibernate.internal.util.collections.Stack;
|
import org.hibernate.internal.util.collections.Stack;
|
||||||
|
import org.hibernate.metamodel.mapping.CollectionPart;
|
||||||
import org.hibernate.metamodel.mapping.JdbcMappingContainer;
|
import org.hibernate.metamodel.mapping.JdbcMappingContainer;
|
||||||
|
import org.hibernate.metamodel.mapping.ModelPart;
|
||||||
import org.hibernate.query.IllegalQueryOperationException;
|
import org.hibernate.query.IllegalQueryOperationException;
|
||||||
|
import org.hibernate.query.derived.AnonymousTupleTableGroupProducer;
|
||||||
import org.hibernate.query.sqm.ComparisonOperator;
|
import org.hibernate.query.sqm.ComparisonOperator;
|
||||||
import org.hibernate.query.sqm.FetchClauseType;
|
import org.hibernate.query.sqm.FetchClauseType;
|
||||||
import org.hibernate.sql.ast.Clause;
|
import org.hibernate.sql.ast.Clause;
|
||||||
import org.hibernate.sql.ast.SqlAstJoinType;
|
import org.hibernate.sql.ast.SqlAstJoinType;
|
||||||
|
import org.hibernate.sql.ast.SqlAstNodeRenderingMode;
|
||||||
import org.hibernate.sql.ast.spi.SqlSelection;
|
import org.hibernate.sql.ast.spi.SqlSelection;
|
||||||
|
import org.hibernate.sql.ast.tree.SqlAstNode;
|
||||||
import org.hibernate.sql.ast.tree.Statement;
|
import org.hibernate.sql.ast.tree.Statement;
|
||||||
import org.hibernate.sql.ast.tree.delete.DeleteStatement;
|
import org.hibernate.sql.ast.tree.delete.DeleteStatement;
|
||||||
import org.hibernate.sql.ast.tree.expression.BinaryArithmeticExpression;
|
import org.hibernate.sql.ast.tree.expression.BinaryArithmeticExpression;
|
||||||
|
@ -24,10 +29,9 @@ import org.hibernate.sql.ast.tree.expression.Expression;
|
||||||
import org.hibernate.sql.ast.tree.expression.Literal;
|
import org.hibernate.sql.ast.tree.expression.Literal;
|
||||||
import org.hibernate.sql.ast.tree.expression.SqlTuple;
|
import org.hibernate.sql.ast.tree.expression.SqlTuple;
|
||||||
import org.hibernate.sql.ast.tree.expression.Summarization;
|
import org.hibernate.sql.ast.tree.expression.Summarization;
|
||||||
|
import org.hibernate.sql.ast.tree.from.DerivedTableReference;
|
||||||
import org.hibernate.sql.ast.tree.from.NamedTableReference;
|
import org.hibernate.sql.ast.tree.from.NamedTableReference;
|
||||||
import org.hibernate.sql.ast.tree.from.TableGroup;
|
|
||||||
import org.hibernate.sql.ast.tree.from.TableGroupJoin;
|
import org.hibernate.sql.ast.tree.from.TableGroupJoin;
|
||||||
import org.hibernate.sql.ast.tree.from.TableReference;
|
|
||||||
import org.hibernate.sql.ast.tree.from.UnionTableReference;
|
import org.hibernate.sql.ast.tree.from.UnionTableReference;
|
||||||
import org.hibernate.sql.ast.tree.insert.ConflictClause;
|
import org.hibernate.sql.ast.tree.insert.ConflictClause;
|
||||||
import org.hibernate.sql.ast.tree.insert.InsertSelectStatement;
|
import org.hibernate.sql.ast.tree.insert.InsertSelectStatement;
|
||||||
|
@ -180,17 +184,24 @@ public class SQLServerSqlAstTranslator<T extends JdbcOperation> extends SqlAstTr
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected boolean renderPrimaryTableReference(TableGroup tableGroup, LockMode lockMode) {
|
@Override
|
||||||
if ( shouldInlineCte( tableGroup ) ) {
|
protected void renderDerivedTableReference(DerivedTableReference tableReference) {
|
||||||
inlineCteTableGroup( tableGroup, lockMode );
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
final TableReference tableReference = tableGroup.getPrimaryTableReference();
|
|
||||||
if ( tableReference instanceof NamedTableReference ) {
|
|
||||||
return renderNamedTableReference( (NamedTableReference) tableReference, lockMode );
|
|
||||||
}
|
|
||||||
tableReference.accept( this );
|
tableReference.accept( this );
|
||||||
return false;
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void renderNamedSetReturningFunction(String functionName, List<? extends SqlAstNode> sqlAstArguments, AnonymousTupleTableGroupProducer tupleType, String tableIdentifierVariable, SqlAstNodeRenderingMode argumentRenderingMode) {
|
||||||
|
final ModelPart ordinalitySubPart = tupleType.findSubPart( CollectionPart.Nature.INDEX.getName(), null );
|
||||||
|
if ( ordinalitySubPart != null ) {
|
||||||
|
appendSql( "(select t.*, row_number() over(order by (select 1)) " );
|
||||||
|
appendSql( ordinalitySubPart.asBasicValuedModelPart().getSelectionExpression() );
|
||||||
|
appendSql( " from " );
|
||||||
|
renderSimpleNamedFunction( functionName, sqlAstArguments, argumentRenderingMode );
|
||||||
|
append( " t)" );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
super.renderNamedSetReturningFunction( functionName, sqlAstArguments, tupleType, tableIdentifierVariable, argumentRenderingMode );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -6,7 +6,6 @@ package org.hibernate.dialect;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.hibernate.LockMode;
|
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||||
import org.hibernate.query.sqm.ComparisonOperator;
|
import org.hibernate.query.sqm.ComparisonOperator;
|
||||||
import org.hibernate.sql.ast.Clause;
|
import org.hibernate.sql.ast.Clause;
|
||||||
|
@ -18,9 +17,6 @@ import org.hibernate.sql.ast.tree.expression.Literal;
|
||||||
import org.hibernate.sql.ast.tree.expression.SqlTuple;
|
import org.hibernate.sql.ast.tree.expression.SqlTuple;
|
||||||
import org.hibernate.sql.ast.tree.expression.Summarization;
|
import org.hibernate.sql.ast.tree.expression.Summarization;
|
||||||
import org.hibernate.sql.ast.tree.from.DerivedTableReference;
|
import org.hibernate.sql.ast.tree.from.DerivedTableReference;
|
||||||
import org.hibernate.sql.ast.tree.from.NamedTableReference;
|
|
||||||
import org.hibernate.sql.ast.tree.from.TableGroup;
|
|
||||||
import org.hibernate.sql.ast.tree.from.TableReference;
|
|
||||||
import org.hibernate.sql.ast.tree.select.QueryPart;
|
import org.hibernate.sql.ast.tree.select.QueryPart;
|
||||||
import org.hibernate.sql.ast.tree.select.QuerySpec;
|
import org.hibernate.sql.ast.tree.select.QuerySpec;
|
||||||
import org.hibernate.sql.ast.tree.select.SelectClause;
|
import org.hibernate.sql.ast.tree.select.SelectClause;
|
||||||
|
@ -107,17 +103,8 @@ public class SpannerSqlAstTranslator<T extends JdbcOperation> extends AbstractSq
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean renderPrimaryTableReference(TableGroup tableGroup, LockMode lockMode) {
|
protected void renderDerivedTableReference(DerivedTableReference tableReference) {
|
||||||
if ( shouldInlineCte( tableGroup ) ) {
|
final boolean correlated = tableReference.isLateral();
|
||||||
inlineCteTableGroup( tableGroup, lockMode );
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
final TableReference tableReference = tableGroup.getPrimaryTableReference();
|
|
||||||
if ( tableReference instanceof NamedTableReference ) {
|
|
||||||
return renderNamedTableReference( (NamedTableReference) tableReference, lockMode );
|
|
||||||
}
|
|
||||||
final DerivedTableReference derivedTableReference = (DerivedTableReference) tableReference;
|
|
||||||
final boolean correlated = derivedTableReference.isLateral();
|
|
||||||
final boolean oldCorrelated = this.correlated;
|
final boolean oldCorrelated = this.correlated;
|
||||||
if ( correlated ) {
|
if ( correlated ) {
|
||||||
this.correlated = true;
|
this.correlated = true;
|
||||||
|
@ -128,7 +115,6 @@ public class SpannerSqlAstTranslator<T extends JdbcOperation> extends AbstractSq
|
||||||
this.correlated = oldCorrelated;
|
this.correlated = oldCorrelated;
|
||||||
appendSql( CLOSE_PARENTHESIS );
|
appendSql( CLOSE_PARENTHESIS );
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -13,7 +13,9 @@ import org.hibernate.Length;
|
||||||
import org.hibernate.LockMode;
|
import org.hibernate.LockMode;
|
||||||
import org.hibernate.LockOptions;
|
import org.hibernate.LockOptions;
|
||||||
import org.hibernate.QueryTimeoutException;
|
import org.hibernate.QueryTimeoutException;
|
||||||
|
import org.hibernate.boot.model.FunctionContributions;
|
||||||
import org.hibernate.boot.model.TypeContributions;
|
import org.hibernate.boot.model.TypeContributions;
|
||||||
|
import org.hibernate.dialect.function.CommonFunctionFactory;
|
||||||
import org.hibernate.dialect.pagination.LimitHandler;
|
import org.hibernate.dialect.pagination.LimitHandler;
|
||||||
import org.hibernate.dialect.pagination.TopLimitHandler;
|
import org.hibernate.dialect.pagination.TopLimitHandler;
|
||||||
import org.hibernate.engine.jdbc.Size;
|
import org.hibernate.engine.jdbc.Size;
|
||||||
|
@ -52,6 +54,7 @@ import static org.hibernate.type.SqlTypes.NCLOB;
|
||||||
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;
|
||||||
import static org.hibernate.type.SqlTypes.TIMESTAMP_WITH_TIMEZONE;
|
import static org.hibernate.type.SqlTypes.TIMESTAMP_WITH_TIMEZONE;
|
||||||
|
import static org.hibernate.type.SqlTypes.XML_ARRAY;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A {@linkplain Dialect SQL dialect} for Sybase Adaptive Server Enterprise 16 and above.
|
* A {@linkplain Dialect SQL dialect} for Sybase Adaptive Server Enterprise 16 and above.
|
||||||
|
@ -157,6 +160,11 @@ public class SybaseASEDialect extends SybaseDialect {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getPreferredSqlTypeCodeForArray() {
|
||||||
|
return XML_ARRAY;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getMaxVarcharLength() {
|
public int getMaxVarcharLength() {
|
||||||
// the maximum length of a VARCHAR or VARBINARY
|
// the maximum length of a VARCHAR or VARBINARY
|
||||||
|
@ -168,6 +176,15 @@ public class SybaseASEDialect extends SybaseDialect {
|
||||||
return 16_384;
|
return 16_384;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initializeFunctionRegistry(FunctionContributions functionContributions) {
|
||||||
|
super.initializeFunctionRegistry( functionContributions );
|
||||||
|
|
||||||
|
CommonFunctionFactory functionFactory = new CommonFunctionFactory( functionContributions);
|
||||||
|
|
||||||
|
functionFactory.unnest_sybasease();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long getDefaultLobLength() {
|
public long getDefaultLobLength() {
|
||||||
return Length.LONG32;
|
return Length.LONG32;
|
||||||
|
|
|
@ -11,6 +11,7 @@ import org.hibernate.LockMode;
|
||||||
import org.hibernate.LockOptions;
|
import org.hibernate.LockOptions;
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||||
import org.hibernate.internal.util.collections.Stack;
|
import org.hibernate.internal.util.collections.Stack;
|
||||||
|
import org.hibernate.metamodel.mapping.JdbcMappingContainer;
|
||||||
import org.hibernate.query.IllegalQueryOperationException;
|
import org.hibernate.query.IllegalQueryOperationException;
|
||||||
import org.hibernate.query.sqm.ComparisonOperator;
|
import org.hibernate.query.sqm.ComparisonOperator;
|
||||||
import org.hibernate.sql.ast.Clause;
|
import org.hibernate.sql.ast.Clause;
|
||||||
|
@ -46,6 +47,7 @@ import org.hibernate.sql.ast.tree.select.QuerySpec;
|
||||||
import org.hibernate.sql.ast.tree.select.SelectClause;
|
import org.hibernate.sql.ast.tree.select.SelectClause;
|
||||||
import org.hibernate.sql.ast.tree.update.UpdateStatement;
|
import org.hibernate.sql.ast.tree.update.UpdateStatement;
|
||||||
import org.hibernate.sql.exec.spi.JdbcOperation;
|
import org.hibernate.sql.exec.spi.JdbcOperation;
|
||||||
|
import org.hibernate.type.SqlTypes;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A SQL AST translator for Sybase ASE.
|
* A SQL AST translator for Sybase ASE.
|
||||||
|
@ -320,7 +322,7 @@ public class SybaseASESqlAstTranslator<T extends JdbcOperation> extends Abstract
|
||||||
append( '(' );
|
append( '(' );
|
||||||
visitValuesListEmulateSelectUnion( tableReference.getValuesList() );
|
visitValuesListEmulateSelectUnion( tableReference.getValuesList() );
|
||||||
append( ')' );
|
append( ')' );
|
||||||
renderDerivedTableReference( tableReference );
|
renderDerivedTableReferenceIdentificationVariable( tableReference );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -355,8 +357,56 @@ public class SybaseASESqlAstTranslator<T extends JdbcOperation> extends Abstract
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void renderComparison(Expression lhs, ComparisonOperator operator, Expression rhs) {
|
protected void renderComparison(Expression lhs, ComparisonOperator operator, Expression rhs) {
|
||||||
|
// In Sybase ASE, XMLTYPE is not "comparable", so we have to cast the two parts to varchar for this purpose
|
||||||
|
final boolean isLob = isLob( lhs.getExpressionType() );
|
||||||
|
if ( isLob ) {
|
||||||
|
switch ( operator ) {
|
||||||
|
case EQUAL:
|
||||||
|
lhs.accept( this );
|
||||||
|
appendSql( " like " );
|
||||||
|
rhs.accept( this );
|
||||||
|
return;
|
||||||
|
case NOT_EQUAL:
|
||||||
|
lhs.accept( this );
|
||||||
|
appendSql( " not like " );
|
||||||
|
rhs.accept( this );
|
||||||
|
return;
|
||||||
|
default:
|
||||||
|
// Fall through
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
// I think intersect is only supported in 16.0 SP3
|
// I think intersect is only supported in 16.0 SP3
|
||||||
if ( getDialect().isAnsiNullOn() ) {
|
if ( getDialect().isAnsiNullOn() ) {
|
||||||
|
if ( isLob ) {
|
||||||
|
switch ( operator ) {
|
||||||
|
case DISTINCT_FROM:
|
||||||
|
appendSql( "case when " );
|
||||||
|
lhs.accept( this );
|
||||||
|
appendSql( " like " );
|
||||||
|
rhs.accept( this );
|
||||||
|
appendSql( " or " );
|
||||||
|
lhs.accept( this );
|
||||||
|
appendSql( " is null and " );
|
||||||
|
rhs.accept( this );
|
||||||
|
appendSql( " is null then 0 else 1 end=1" );
|
||||||
|
return;
|
||||||
|
case NOT_DISTINCT_FROM:
|
||||||
|
appendSql( "case when " );
|
||||||
|
lhs.accept( this );
|
||||||
|
appendSql( " like " );
|
||||||
|
rhs.accept( this );
|
||||||
|
appendSql( " or " );
|
||||||
|
lhs.accept( this );
|
||||||
|
appendSql( " is null and " );
|
||||||
|
rhs.accept( this );
|
||||||
|
appendSql( " is null then 0 else 1 end=0" );
|
||||||
|
return;
|
||||||
|
default:
|
||||||
|
// Fall through
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
if ( supportsDistinctFromPredicate() ) {
|
if ( supportsDistinctFromPredicate() ) {
|
||||||
renderComparisonEmulateIntersect( lhs, operator, rhs );
|
renderComparisonEmulateIntersect( lhs, operator, rhs );
|
||||||
}
|
}
|
||||||
|
@ -377,10 +427,20 @@ public class SybaseASESqlAstTranslator<T extends JdbcOperation> extends Abstract
|
||||||
lhs.accept( this );
|
lhs.accept( this );
|
||||||
switch ( operator ) {
|
switch ( operator ) {
|
||||||
case DISTINCT_FROM:
|
case DISTINCT_FROM:
|
||||||
|
if ( isLob ) {
|
||||||
|
appendSql( " not like " );
|
||||||
|
}
|
||||||
|
else {
|
||||||
appendSql( "<>" );
|
appendSql( "<>" );
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case NOT_DISTINCT_FROM:
|
case NOT_DISTINCT_FROM:
|
||||||
|
if ( isLob ) {
|
||||||
|
appendSql( " like " );
|
||||||
|
}
|
||||||
|
else {
|
||||||
appendSql( '=' );
|
appendSql( '=' );
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case LESS_THAN:
|
case LESS_THAN:
|
||||||
case GREATER_THAN:
|
case GREATER_THAN:
|
||||||
|
@ -416,6 +476,21 @@ public class SybaseASESqlAstTranslator<T extends JdbcOperation> extends Abstract
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean isLob(JdbcMappingContainer expressionType) {
|
||||||
|
return expressionType != null && expressionType.getJdbcTypeCount() == 1 && switch ( expressionType.getSingleJdbcMapping().getJdbcType().getDdlTypeCode() ) {
|
||||||
|
case SqlTypes.LONG32NVARCHAR,
|
||||||
|
SqlTypes.LONG32VARCHAR,
|
||||||
|
SqlTypes.LONGNVARCHAR,
|
||||||
|
SqlTypes.LONGVARCHAR,
|
||||||
|
SqlTypes.LONG32VARBINARY,
|
||||||
|
SqlTypes.LONGVARBINARY,
|
||||||
|
SqlTypes.CLOB,
|
||||||
|
SqlTypes.NCLOB,
|
||||||
|
SqlTypes.BLOB -> true;
|
||||||
|
default -> false;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean supportsIntersect() {
|
protected boolean supportsIntersect() {
|
||||||
// At least the version that
|
// At least the version that
|
||||||
|
|
|
@ -204,7 +204,7 @@ public class SybaseSqlAstTranslator<T extends JdbcOperation> extends AbstractSql
|
||||||
append( '(' );
|
append( '(' );
|
||||||
visitValuesListEmulateSelectUnion( tableReference.getValuesList() );
|
visitValuesListEmulateSelectUnion( tableReference.getValuesList() );
|
||||||
append( ')' );
|
append( ')' );
|
||||||
renderDerivedTableReference( tableReference );
|
renderDerivedTableReferenceIdentificationVariable( tableReference );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -22,6 +22,7 @@ import org.hibernate.sql.ast.tree.expression.ColumnReference;
|
||||||
import org.hibernate.sql.ast.tree.expression.Expression;
|
import org.hibernate.sql.ast.tree.expression.Expression;
|
||||||
import org.hibernate.sql.ast.tree.expression.Literal;
|
import org.hibernate.sql.ast.tree.expression.Literal;
|
||||||
import org.hibernate.sql.ast.tree.expression.Summarization;
|
import org.hibernate.sql.ast.tree.expression.Summarization;
|
||||||
|
import org.hibernate.sql.ast.tree.from.DerivedTableReference;
|
||||||
import org.hibernate.sql.ast.tree.from.NamedTableReference;
|
import org.hibernate.sql.ast.tree.from.NamedTableReference;
|
||||||
import org.hibernate.sql.ast.tree.from.QueryPartTableReference;
|
import org.hibernate.sql.ast.tree.from.QueryPartTableReference;
|
||||||
import org.hibernate.sql.ast.tree.from.ValuesTableReference;
|
import org.hibernate.sql.ast.tree.from.ValuesTableReference;
|
||||||
|
@ -249,6 +250,11 @@ public class TiDBSqlAstTranslator<T extends JdbcOperation> extends AbstractSqlAs
|
||||||
emulateQueryPartTableReferenceColumnAliasing( tableReference );
|
emulateQueryPartTableReferenceColumnAliasing( tableReference );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void renderDerivedTableReferenceIdentificationVariable(DerivedTableReference tableReference) {
|
||||||
|
renderTableReferenceIdentificationVariable( tableReference );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visitOffsetFetchClause(QueryPart queryPart) {
|
public void visitOffsetFetchClause(QueryPart queryPart) {
|
||||||
if ( !isRowNumberingCurrentQueryPart() ) {
|
if ( !isRowNumberingCurrentQueryPart() ) {
|
||||||
|
|
|
@ -15,6 +15,8 @@ import java.util.Base64;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.hibernate.Internal;
|
import org.hibernate.Internal;
|
||||||
|
import org.hibernate.engine.spi.LazySessionWrapperOptions;
|
||||||
|
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||||
import org.hibernate.internal.util.CharSequenceHelper;
|
import org.hibernate.internal.util.CharSequenceHelper;
|
||||||
import org.hibernate.metamodel.mapping.EmbeddableMappingType;
|
import org.hibernate.metamodel.mapping.EmbeddableMappingType;
|
||||||
import org.hibernate.metamodel.mapping.JdbcMapping;
|
import org.hibernate.metamodel.mapping.JdbcMapping;
|
||||||
|
@ -24,6 +26,7 @@ import org.hibernate.metamodel.mapping.internal.EmbeddedAttributeMapping;
|
||||||
import org.hibernate.sql.ast.spi.SqlAppender;
|
import org.hibernate.sql.ast.spi.SqlAppender;
|
||||||
import org.hibernate.type.SqlTypes;
|
import org.hibernate.type.SqlTypes;
|
||||||
import org.hibernate.type.descriptor.WrapperOptions;
|
import org.hibernate.type.descriptor.WrapperOptions;
|
||||||
|
import org.hibernate.type.descriptor.java.BasicPluralJavaType;
|
||||||
import org.hibernate.type.descriptor.java.IntegerJavaType;
|
import org.hibernate.type.descriptor.java.IntegerJavaType;
|
||||||
import org.hibernate.type.descriptor.java.JavaType;
|
import org.hibernate.type.descriptor.java.JavaType;
|
||||||
import org.hibernate.type.descriptor.java.JdbcDateJavaType;
|
import org.hibernate.type.descriptor.java.JdbcDateJavaType;
|
||||||
|
@ -32,6 +35,8 @@ import org.hibernate.type.descriptor.java.JdbcTimestampJavaType;
|
||||||
import org.hibernate.type.descriptor.java.OffsetDateTimeJavaType;
|
import org.hibernate.type.descriptor.java.OffsetDateTimeJavaType;
|
||||||
import org.hibernate.type.descriptor.jdbc.AggregateJdbcType;
|
import org.hibernate.type.descriptor.jdbc.AggregateJdbcType;
|
||||||
|
|
||||||
|
import static java.lang.Character.isLetter;
|
||||||
|
import static java.lang.Character.isLetterOrDigit;
|
||||||
import static org.hibernate.dialect.StructHelper.getEmbeddedPart;
|
import static org.hibernate.dialect.StructHelper.getEmbeddedPart;
|
||||||
import static org.hibernate.dialect.StructHelper.instantiate;
|
import static org.hibernate.dialect.StructHelper.instantiate;
|
||||||
|
|
||||||
|
@ -486,7 +491,6 @@ public class XmlHelper {
|
||||||
WrapperOptions options,
|
WrapperOptions options,
|
||||||
XMLAppender sb) {
|
XMLAppender sb) {
|
||||||
final Object[] array = embeddableMappingType.getValues( value );
|
final Object[] array = embeddableMappingType.getValues( value );
|
||||||
final int numberOfAttributes = embeddableMappingType.getNumberOfAttributeMappings();
|
|
||||||
for ( int i = 0; i < array.length; i++ ) {
|
for ( int i = 0; i < array.length; i++ ) {
|
||||||
if ( array[i] == null ) {
|
if ( array[i] == null ) {
|
||||||
continue;
|
continue;
|
||||||
|
@ -646,6 +650,28 @@ public class XmlHelper {
|
||||||
return selectableIndex;
|
return selectableIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean isValidXmlName(String name) {
|
||||||
|
if ( name.isEmpty()
|
||||||
|
|| !isValidXmlNameStart( name.charAt( 0 ) )
|
||||||
|
|| name.regionMatches( true, 0, "xml", 0, 3 ) ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for ( int i = 1; i < name.length(); i++ ) {
|
||||||
|
if ( !isValidXmlNameChar( name.charAt( i ) ) ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isValidXmlNameStart(char c) {
|
||||||
|
return isLetter( c ) || c == '_' || c == ':';
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isValidXmlNameChar(char c) {
|
||||||
|
return isLetterOrDigit( c ) || c == '_' || c == ':' || c == '-' || c == '.';
|
||||||
|
}
|
||||||
|
|
||||||
private static class XMLAppender extends OutputStream implements SqlAppender {
|
private static class XMLAppender extends OutputStream implements SqlAppender {
|
||||||
|
|
||||||
private final static char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray();
|
private final static char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray();
|
||||||
|
@ -781,4 +807,47 @@ public class XmlHelper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static CollectionTags determineCollectionTags(BasicPluralJavaType<?> pluralJavaType, SessionFactoryImplementor sessionFactory) {
|
||||||
|
//noinspection unchecked
|
||||||
|
final JavaType<Object> javaType = (JavaType<Object>) pluralJavaType;
|
||||||
|
final LazySessionWrapperOptions lazySessionWrapperOptions = new LazySessionWrapperOptions( sessionFactory );
|
||||||
|
// Produce the XML string for a collection with a null element to find out the root and element tag names
|
||||||
|
final String nullElementXml;
|
||||||
|
try {
|
||||||
|
nullElementXml = sessionFactory.getSessionFactoryOptions().getXmlFormatMapper().toString(
|
||||||
|
javaType.fromString( "{null}" ),
|
||||||
|
javaType,
|
||||||
|
lazySessionWrapperOptions
|
||||||
|
);
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
lazySessionWrapperOptions.cleanup();
|
||||||
|
}
|
||||||
|
|
||||||
|
// There must be an end tag for the root, so find that first
|
||||||
|
final int rootCloseTagPosition = nullElementXml.lastIndexOf( '<' );
|
||||||
|
assert nullElementXml.charAt( rootCloseTagPosition + 1 ) == '/';
|
||||||
|
final int rootNameStart = rootCloseTagPosition + 2;
|
||||||
|
final int rootCloseTagEnd = nullElementXml.indexOf( '>', rootCloseTagPosition );
|
||||||
|
final String rootTag = nullElementXml.substring( rootNameStart, rootCloseTagEnd );
|
||||||
|
|
||||||
|
// Then search for the open tag of the root and determine the start of the first item
|
||||||
|
final int itemTagStart = nullElementXml.indexOf(
|
||||||
|
'<',
|
||||||
|
nullElementXml.indexOf( "<" + rootTag + ">" ) + rootTag.length() + 2
|
||||||
|
);
|
||||||
|
final int itemNameStart = itemTagStart + 1;
|
||||||
|
int itemNameEnd = itemNameStart;
|
||||||
|
for ( int i = itemNameStart + 1; i < nullElementXml.length(); i++ ) {
|
||||||
|
if ( !isValidXmlNameChar( nullElementXml.charAt( i ) ) ) {
|
||||||
|
itemNameEnd = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
final String elementNodeName = nullElementXml.substring( itemNameStart, itemNameEnd );
|
||||||
|
return new CollectionTags( rootTag, elementNodeName );
|
||||||
|
}
|
||||||
|
|
||||||
|
public record CollectionTags(String rootName, String elementName) {}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,8 @@ import org.hibernate.dialect.Dialect;
|
||||||
import org.hibernate.mapping.AggregateColumn;
|
import org.hibernate.mapping.AggregateColumn;
|
||||||
import org.hibernate.mapping.Column;
|
import org.hibernate.mapping.Column;
|
||||||
import org.hibernate.metamodel.mapping.SelectableMapping;
|
import org.hibernate.metamodel.mapping.SelectableMapping;
|
||||||
|
import org.hibernate.metamodel.mapping.SqlTypedMapping;
|
||||||
|
import org.hibernate.metamodel.mapping.internal.SqlTypedMappingImpl;
|
||||||
import org.hibernate.sql.ast.SqlAstTranslator;
|
import org.hibernate.sql.ast.SqlAstTranslator;
|
||||||
import org.hibernate.sql.ast.spi.SqlAppender;
|
import org.hibernate.sql.ast.spi.SqlAppender;
|
||||||
import org.hibernate.sql.ast.tree.expression.Expression;
|
import org.hibernate.sql.ast.tree.expression.Expression;
|
||||||
|
@ -39,13 +41,51 @@ public interface AggregateSupport {
|
||||||
* @param aggregateColumn The type information for the aggregate column
|
* @param aggregateColumn The type information for the aggregate column
|
||||||
* @param column The column within the aggregate type, for which to return the read expression
|
* @param column The column within the aggregate type, for which to return the read expression
|
||||||
*/
|
*/
|
||||||
String aggregateComponentCustomReadExpression(
|
default String aggregateComponentCustomReadExpression(
|
||||||
String template,
|
String template,
|
||||||
String placeholder,
|
String placeholder,
|
||||||
String aggregateParentReadExpression,
|
String aggregateParentReadExpression,
|
||||||
String columnExpression,
|
String columnExpression,
|
||||||
AggregateColumn aggregateColumn,
|
AggregateColumn aggregateColumn,
|
||||||
Column column);
|
Column column) {
|
||||||
|
return aggregateComponentCustomReadExpression(
|
||||||
|
template,
|
||||||
|
placeholder,
|
||||||
|
aggregateParentReadExpression,
|
||||||
|
columnExpression,
|
||||||
|
aggregateColumn.getTypeCode(),
|
||||||
|
new SqlTypedMappingImpl(
|
||||||
|
column.getTypeName(),
|
||||||
|
column.getLength(),
|
||||||
|
column.getPrecision(),
|
||||||
|
column.getScale(),
|
||||||
|
column.getTemporalPrecision(),
|
||||||
|
column.getType()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the custom read expression to use for {@code column}.
|
||||||
|
* Replaces the given {@code placeholder} in the given {@code template}
|
||||||
|
* by the custom read expression to use for {@code column}.
|
||||||
|
*
|
||||||
|
* @param template The custom read expression template of the column
|
||||||
|
* @param placeholder The placeholder to replace with the actual read expression
|
||||||
|
* @param aggregateParentReadExpression The expression to the aggregate column, which contains the column
|
||||||
|
* @param columnExpression The column within the aggregate type, for which to return the read expression
|
||||||
|
* @param aggregateColumnTypeCode The SQL type code of the aggregate column
|
||||||
|
* @param column The column within the aggregate type, for which to return the read expression
|
||||||
|
*
|
||||||
|
* @since 7.0
|
||||||
|
*/
|
||||||
|
String aggregateComponentCustomReadExpression(
|
||||||
|
String template,
|
||||||
|
String placeholder,
|
||||||
|
String aggregateParentReadExpression,
|
||||||
|
String columnExpression,
|
||||||
|
int aggregateColumnTypeCode,
|
||||||
|
SqlTypedMapping column);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the assignment expression to use for {@code column},
|
* Returns the assignment expression to use for {@code column},
|
||||||
|
|
|
@ -12,6 +12,7 @@ import org.hibernate.boot.model.relational.Namespace;
|
||||||
import org.hibernate.mapping.AggregateColumn;
|
import org.hibernate.mapping.AggregateColumn;
|
||||||
import org.hibernate.mapping.Column;
|
import org.hibernate.mapping.Column;
|
||||||
import org.hibernate.metamodel.mapping.SelectableMapping;
|
import org.hibernate.metamodel.mapping.SelectableMapping;
|
||||||
|
import org.hibernate.metamodel.mapping.SqlTypedMapping;
|
||||||
import org.hibernate.type.spi.TypeConfiguration;
|
import org.hibernate.type.spi.TypeConfiguration;
|
||||||
|
|
||||||
public class AggregateSupportImpl implements AggregateSupport {
|
public class AggregateSupportImpl implements AggregateSupport {
|
||||||
|
@ -19,13 +20,7 @@ public class AggregateSupportImpl implements AggregateSupport {
|
||||||
public static final AggregateSupport INSTANCE = new AggregateSupportImpl();
|
public static final AggregateSupport INSTANCE = new AggregateSupportImpl();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String aggregateComponentCustomReadExpression(
|
public String aggregateComponentCustomReadExpression(String template, String placeholder, String aggregateParentReadExpression, String columnExpression, int aggregateColumnTypeCode, SqlTypedMapping column) {
|
||||||
String template,
|
|
||||||
String placeholder,
|
|
||||||
String aggregateParentReadExpression,
|
|
||||||
String columnExpression,
|
|
||||||
AggregateColumn aggregateColumn,
|
|
||||||
Column column) {
|
|
||||||
throw new UnsupportedOperationException( "Dialect does not support aggregateComponentCustomReadExpression: " + getClass().getName() );
|
throw new UnsupportedOperationException( "Dialect does not support aggregateComponentCustomReadExpression: " + getClass().getName() );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,7 @@ import org.hibernate.metamodel.mapping.EmbeddableMappingType;
|
||||||
import org.hibernate.metamodel.mapping.SelectableMapping;
|
import org.hibernate.metamodel.mapping.SelectableMapping;
|
||||||
import org.hibernate.metamodel.mapping.SelectablePath;
|
import org.hibernate.metamodel.mapping.SelectablePath;
|
||||||
import org.hibernate.metamodel.mapping.SqlExpressible;
|
import org.hibernate.metamodel.mapping.SqlExpressible;
|
||||||
|
import org.hibernate.metamodel.mapping.SqlTypedMapping;
|
||||||
import org.hibernate.sql.ast.SqlAstNodeRenderingMode;
|
import org.hibernate.sql.ast.SqlAstNodeRenderingMode;
|
||||||
import org.hibernate.sql.ast.SqlAstTranslator;
|
import org.hibernate.sql.ast.SqlAstTranslator;
|
||||||
import org.hibernate.sql.ast.spi.SqlAppender;
|
import org.hibernate.sql.ast.spi.SqlAppender;
|
||||||
|
@ -47,13 +48,13 @@ public class DB2AggregateSupport extends AggregateSupportImpl {
|
||||||
String placeholder,
|
String placeholder,
|
||||||
String aggregateParentReadExpression,
|
String aggregateParentReadExpression,
|
||||||
String columnExpression,
|
String columnExpression,
|
||||||
AggregateColumn aggregateColumn,
|
int aggregateColumnTypeCode,
|
||||||
Column column) {
|
SqlTypedMapping column) {
|
||||||
switch ( aggregateColumn.getTypeCode() ) {
|
switch ( aggregateColumnTypeCode ) {
|
||||||
case STRUCT:
|
case STRUCT:
|
||||||
return template.replace( placeholder, aggregateParentReadExpression + ".." + columnExpression );
|
return template.replace( placeholder, aggregateParentReadExpression + ".." + columnExpression );
|
||||||
}
|
}
|
||||||
throw new IllegalArgumentException( "Unsupported aggregate SQL type: " + aggregateColumn.getTypeCode() );
|
throw new IllegalArgumentException( "Unsupported aggregate SQL type: " + aggregateColumnTypeCode );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -24,6 +24,7 @@ import org.hibernate.metamodel.mapping.EmbeddableMappingType;
|
||||||
import org.hibernate.metamodel.mapping.JdbcMapping;
|
import org.hibernate.metamodel.mapping.JdbcMapping;
|
||||||
import org.hibernate.metamodel.mapping.SelectableMapping;
|
import org.hibernate.metamodel.mapping.SelectableMapping;
|
||||||
import org.hibernate.metamodel.mapping.SelectablePath;
|
import org.hibernate.metamodel.mapping.SelectablePath;
|
||||||
|
import org.hibernate.metamodel.mapping.SqlTypedMapping;
|
||||||
import org.hibernate.sql.ast.SqlAstNodeRenderingMode;
|
import org.hibernate.sql.ast.SqlAstNodeRenderingMode;
|
||||||
import org.hibernate.sql.ast.SqlAstTranslator;
|
import org.hibernate.sql.ast.SqlAstTranslator;
|
||||||
import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator;
|
import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator;
|
||||||
|
@ -113,9 +114,9 @@ public class OracleAggregateSupport extends AggregateSupportImpl {
|
||||||
String placeholder,
|
String placeholder,
|
||||||
String aggregateParentReadExpression,
|
String aggregateParentReadExpression,
|
||||||
String columnExpression,
|
String columnExpression,
|
||||||
AggregateColumn aggregateColumn,
|
int aggregateColumnTypeCode,
|
||||||
Column column) {
|
SqlTypedMapping column) {
|
||||||
switch ( aggregateColumn.getTypeCode() ) {
|
switch ( aggregateColumnTypeCode ) {
|
||||||
case JSON:
|
case JSON:
|
||||||
String jsonTypeName = "json";
|
String jsonTypeName = "json";
|
||||||
switch ( jsonSupport ) {
|
switch ( jsonSupport ) {
|
||||||
|
@ -132,14 +133,14 @@ public class OracleAggregateSupport extends AggregateSupportImpl {
|
||||||
else {
|
else {
|
||||||
parentPartExpression = aggregateParentReadExpression + ",'$.";
|
parentPartExpression = aggregateParentReadExpression + ",'$.";
|
||||||
}
|
}
|
||||||
switch ( column.getTypeCode() ) {
|
switch ( column.getJdbcMapping().getJdbcType().getDefaultSqlTypeCode() ) {
|
||||||
case BIT:
|
case BIT:
|
||||||
return template.replace(
|
return template.replace(
|
||||||
placeholder,
|
placeholder,
|
||||||
"decode(json_value(" + parentPartExpression + columnExpression + "'),'true',1,'false',0,null)"
|
"decode(json_value(" + parentPartExpression + columnExpression + "'),'true',1,'false',0,null)"
|
||||||
);
|
);
|
||||||
case BOOLEAN:
|
case BOOLEAN:
|
||||||
if ( column.getTypeName().toLowerCase( Locale.ROOT ).trim().startsWith( "number" ) ) {
|
if ( column.getColumnDefinition().toLowerCase( Locale.ROOT ).trim().startsWith( "number" ) ) {
|
||||||
return template.replace(
|
return template.replace(
|
||||||
placeholder,
|
placeholder,
|
||||||
"decode(json_value(" + parentPartExpression + columnExpression + "'),'true',1,'false',0,null)"
|
"decode(json_value(" + parentPartExpression + columnExpression + "'),'true',1,'false',0,null)"
|
||||||
|
@ -152,7 +153,7 @@ public class OracleAggregateSupport extends AggregateSupportImpl {
|
||||||
case BIGINT:
|
case BIGINT:
|
||||||
return template.replace(
|
return template.replace(
|
||||||
placeholder,
|
placeholder,
|
||||||
"json_value(" + parentPartExpression + columnExpression + "' returning " + column.getTypeName() + ')'
|
"json_value(" + parentPartExpression + columnExpression + "' returning " + column.getColumnDefinition() + ')'
|
||||||
);
|
);
|
||||||
case DATE:
|
case DATE:
|
||||||
return template.replace(
|
return template.replace(
|
||||||
|
@ -189,10 +190,10 @@ public class OracleAggregateSupport extends AggregateSupportImpl {
|
||||||
// We encode binary data as hex, so we have to decode here
|
// We encode binary data as hex, so we have to decode here
|
||||||
return template.replace(
|
return template.replace(
|
||||||
placeholder,
|
placeholder,
|
||||||
"(select * from json_table(" + aggregateParentReadExpression + ",'$' columns (" + columnExpression + " " + column.getTypeName() + " path '$." + columnExpression + "')))"
|
"(select * from json_table(" + aggregateParentReadExpression + ",'$' columns (" + columnExpression + " " + column.getColumnDefinition() + " path '$." + columnExpression + "')))"
|
||||||
);
|
);
|
||||||
case ARRAY:
|
case ARRAY:
|
||||||
final BasicPluralType<?, ?> pluralType = (BasicPluralType<?, ?>) column.getValue().getType();
|
final BasicPluralType<?, ?> pluralType = (BasicPluralType<?, ?>) column.getJdbcMapping();
|
||||||
final OracleArrayJdbcType jdbcType = (OracleArrayJdbcType) pluralType.getJdbcType();
|
final OracleArrayJdbcType jdbcType = (OracleArrayJdbcType) pluralType.getJdbcType();
|
||||||
switch ( jdbcType.getElementJdbcType().getDefaultSqlTypeCode() ) {
|
switch ( jdbcType.getElementJdbcType().getDefaultSqlTypeCode() ) {
|
||||||
case BOOLEAN:
|
case BOOLEAN:
|
||||||
|
@ -211,7 +212,7 @@ public class OracleAggregateSupport extends AggregateSupportImpl {
|
||||||
default:
|
default:
|
||||||
return template.replace(
|
return template.replace(
|
||||||
placeholder,
|
placeholder,
|
||||||
"json_value(" + parentPartExpression + columnExpression + "' returning " + column.getTypeName() + ')'
|
"json_value(" + parentPartExpression + columnExpression + "' returning " + column.getColumnDefinition() + ')'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
case JSON:
|
case JSON:
|
||||||
|
@ -222,7 +223,7 @@ public class OracleAggregateSupport extends AggregateSupportImpl {
|
||||||
default:
|
default:
|
||||||
return template.replace(
|
return template.replace(
|
||||||
placeholder,
|
placeholder,
|
||||||
"cast(json_value(" + parentPartExpression + columnExpression + "') as " + column.getTypeName() + ')'
|
"cast(json_value(" + parentPartExpression + columnExpression + "') as " + column.getColumnDefinition() + ')'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
case NONE:
|
case NONE:
|
||||||
|
@ -233,7 +234,7 @@ public class OracleAggregateSupport extends AggregateSupportImpl {
|
||||||
case STRUCT_TABLE:
|
case STRUCT_TABLE:
|
||||||
return template.replace( placeholder, aggregateParentReadExpression + "." + columnExpression );
|
return template.replace( placeholder, aggregateParentReadExpression + "." + columnExpression );
|
||||||
}
|
}
|
||||||
throw new IllegalArgumentException( "Unsupported aggregate SQL type: " + aggregateColumn.getTypeCode() );
|
throw new IllegalArgumentException( "Unsupported aggregate SQL type: " + aggregateColumnTypeCode );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -14,6 +14,7 @@ import org.hibernate.mapping.Column;
|
||||||
import org.hibernate.metamodel.mapping.JdbcMapping;
|
import org.hibernate.metamodel.mapping.JdbcMapping;
|
||||||
import org.hibernate.metamodel.mapping.SelectableMapping;
|
import org.hibernate.metamodel.mapping.SelectableMapping;
|
||||||
import org.hibernate.metamodel.mapping.SelectablePath;
|
import org.hibernate.metamodel.mapping.SelectablePath;
|
||||||
|
import org.hibernate.metamodel.mapping.SqlTypedMapping;
|
||||||
import org.hibernate.sql.ast.SqlAstNodeRenderingMode;
|
import org.hibernate.sql.ast.SqlAstNodeRenderingMode;
|
||||||
import org.hibernate.sql.ast.SqlAstTranslator;
|
import org.hibernate.sql.ast.SqlAstTranslator;
|
||||||
import org.hibernate.sql.ast.spi.SqlAppender;
|
import org.hibernate.sql.ast.spi.SqlAppender;
|
||||||
|
@ -51,12 +52,12 @@ public class PostgreSQLAggregateSupport extends AggregateSupportImpl {
|
||||||
String placeholder,
|
String placeholder,
|
||||||
String aggregateParentReadExpression,
|
String aggregateParentReadExpression,
|
||||||
String columnExpression,
|
String columnExpression,
|
||||||
AggregateColumn aggregateColumn,
|
int aggregateColumnTypeCode,
|
||||||
Column column) {
|
SqlTypedMapping column) {
|
||||||
switch ( aggregateColumn.getTypeCode() ) {
|
switch ( aggregateColumnTypeCode ) {
|
||||||
case JSON_ARRAY:
|
case JSON_ARRAY:
|
||||||
case JSON:
|
case JSON:
|
||||||
switch ( column.getTypeCode() ) {
|
switch ( column.getJdbcMapping().getJdbcType().getDefaultSqlTypeCode() ) {
|
||||||
case JSON:
|
case JSON:
|
||||||
return template.replace(
|
return template.replace(
|
||||||
placeholder,
|
placeholder,
|
||||||
|
@ -71,7 +72,7 @@ public class PostgreSQLAggregateSupport extends AggregateSupportImpl {
|
||||||
"decode(" + aggregateParentReadExpression + "->>'" + columnExpression + "','hex')"
|
"decode(" + aggregateParentReadExpression + "->>'" + columnExpression + "','hex')"
|
||||||
);
|
);
|
||||||
case ARRAY:
|
case ARRAY:
|
||||||
final BasicPluralType<?, ?> pluralType = (BasicPluralType<?, ?>) column.getValue().getType();
|
final BasicPluralType<?, ?> pluralType = (BasicPluralType<?, ?>) column.getJdbcMapping();
|
||||||
switch ( pluralType.getElementType().getJdbcType().getDefaultSqlTypeCode() ) {
|
switch ( pluralType.getElementType().getJdbcType().getDefaultSqlTypeCode() ) {
|
||||||
case BOOLEAN:
|
case BOOLEAN:
|
||||||
case TINYINT:
|
case TINYINT:
|
||||||
|
@ -85,7 +86,7 @@ public class PostgreSQLAggregateSupport extends AggregateSupportImpl {
|
||||||
// because casting a jsonb[] to text[] will not omit the quotes of the jsonb text values
|
// because casting a jsonb[] to text[] will not omit the quotes of the jsonb text values
|
||||||
return template.replace(
|
return template.replace(
|
||||||
placeholder,
|
placeholder,
|
||||||
"cast(array(select jsonb_array_elements(" + aggregateParentReadExpression + "->'" + columnExpression + "')) as " + column.getTypeName() + ')'
|
"cast(array(select jsonb_array_elements(" + aggregateParentReadExpression + "->'" + columnExpression + "')) as " + column.getColumnDefinition() + ')'
|
||||||
);
|
);
|
||||||
case BINARY:
|
case BINARY:
|
||||||
case VARBINARY:
|
case VARBINARY:
|
||||||
|
@ -98,13 +99,13 @@ public class PostgreSQLAggregateSupport extends AggregateSupportImpl {
|
||||||
default:
|
default:
|
||||||
return template.replace(
|
return template.replace(
|
||||||
placeholder,
|
placeholder,
|
||||||
"cast(array(select jsonb_array_elements_text(" + aggregateParentReadExpression + "->'" + columnExpression + "')) as " + column.getTypeName() + ')'
|
"cast(array(select jsonb_array_elements_text(" + aggregateParentReadExpression + "->'" + columnExpression + "')) as " + column.getColumnDefinition() + ')'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
return template.replace(
|
return template.replace(
|
||||||
placeholder,
|
placeholder,
|
||||||
"cast(" + aggregateParentReadExpression + "->>'" + columnExpression + "' as " + column.getTypeName() + ')'
|
"cast(" + aggregateParentReadExpression + "->>'" + columnExpression + "' as " + column.getColumnDefinition() + ')'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
case STRUCT:
|
case STRUCT:
|
||||||
|
@ -112,7 +113,7 @@ public class PostgreSQLAggregateSupport extends AggregateSupportImpl {
|
||||||
case STRUCT_TABLE:
|
case STRUCT_TABLE:
|
||||||
return template.replace( placeholder, '(' + aggregateParentReadExpression + ")." + columnExpression );
|
return template.replace( placeholder, '(' + aggregateParentReadExpression + ")." + columnExpression );
|
||||||
}
|
}
|
||||||
throw new IllegalArgumentException( "Unsupported aggregate SQL type: " + aggregateColumn.getTypeCode() );
|
throw new IllegalArgumentException( "Unsupported aggregate SQL type: " + aggregateColumnTypeCode );
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String jsonCustomWriteExpression(String customWriteExpression, JdbcMapping jdbcMapping) {
|
private static String jsonCustomWriteExpression(String customWriteExpression, JdbcMapping jdbcMapping) {
|
||||||
|
|
|
@ -7,75 +7,11 @@ package org.hibernate.dialect.function;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
import org.hibernate.boot.model.FunctionContributions;
|
import org.hibernate.boot.model.FunctionContributions;
|
||||||
import org.hibernate.dialect.Dialect;
|
import org.hibernate.dialect.Dialect;
|
||||||
|
|
||||||
import org.hibernate.dialect.function.array.ArrayAggFunction;
|
import org.hibernate.dialect.function.array.*;
|
||||||
import org.hibernate.dialect.function.array.ArrayAndElementArgumentTypeResolver;
|
|
||||||
import org.hibernate.dialect.function.array.ArrayAndElementArgumentValidator;
|
|
||||||
import org.hibernate.dialect.function.array.ArrayArgumentValidator;
|
|
||||||
import org.hibernate.dialect.function.array.ArrayConcatElementFunction;
|
|
||||||
import org.hibernate.dialect.function.array.ArrayConcatFunction;
|
|
||||||
import org.hibernate.dialect.function.array.ArrayConstructorFunction;
|
|
||||||
import org.hibernate.dialect.function.array.ArrayContainsOperatorFunction;
|
|
||||||
import org.hibernate.dialect.function.array.ArrayContainsUnnestFunction;
|
|
||||||
import org.hibernate.dialect.function.array.ArrayIncludesOperatorFunction;
|
|
||||||
import org.hibernate.dialect.function.array.ArrayIncludesUnnestFunction;
|
|
||||||
import org.hibernate.dialect.function.array.ArrayIntersectsOperatorFunction;
|
|
||||||
import org.hibernate.dialect.function.array.ArrayIntersectsUnnestFunction;
|
|
||||||
import org.hibernate.dialect.function.array.ArrayGetUnnestFunction;
|
|
||||||
import org.hibernate.dialect.function.array.ArrayRemoveIndexUnnestFunction;
|
|
||||||
import org.hibernate.dialect.function.array.ArrayReplaceUnnestFunction;
|
|
||||||
import org.hibernate.dialect.function.array.ArraySetUnnestFunction;
|
|
||||||
import org.hibernate.dialect.function.array.ArraySliceUnnestFunction;
|
|
||||||
import org.hibernate.dialect.function.array.ArrayToStringFunction;
|
|
||||||
import org.hibernate.dialect.function.array.ArrayViaArgumentReturnTypeResolver;
|
|
||||||
import org.hibernate.dialect.function.array.CockroachArrayFillFunction;
|
|
||||||
import org.hibernate.dialect.function.array.ElementViaArrayArgumentReturnTypeResolver;
|
|
||||||
import org.hibernate.dialect.function.array.H2ArrayContainsFunction;
|
|
||||||
import org.hibernate.dialect.function.array.H2ArrayFillFunction;
|
|
||||||
import org.hibernate.dialect.function.array.H2ArrayIncludesFunction;
|
|
||||||
import org.hibernate.dialect.function.array.H2ArrayIntersectsFunction;
|
|
||||||
import org.hibernate.dialect.function.array.H2ArrayPositionFunction;
|
|
||||||
import org.hibernate.dialect.function.array.H2ArrayPositionsFunction;
|
|
||||||
import org.hibernate.dialect.function.array.H2ArrayRemoveFunction;
|
|
||||||
import org.hibernate.dialect.function.array.H2ArrayRemoveIndexFunction;
|
|
||||||
import org.hibernate.dialect.function.array.H2ArrayReplaceFunction;
|
|
||||||
import org.hibernate.dialect.function.array.H2ArraySetFunction;
|
|
||||||
import org.hibernate.dialect.function.array.H2ArrayToStringFunction;
|
|
||||||
import org.hibernate.dialect.function.array.HSQLArrayConstructorFunction;
|
|
||||||
import org.hibernate.dialect.function.array.HSQLArrayFillFunction;
|
|
||||||
import org.hibernate.dialect.function.array.HSQLArrayPositionFunction;
|
|
||||||
import org.hibernate.dialect.function.array.HSQLArrayPositionsFunction;
|
|
||||||
import org.hibernate.dialect.function.array.HSQLArrayRemoveFunction;
|
|
||||||
import org.hibernate.dialect.function.array.HSQLArraySetFunction;
|
|
||||||
import org.hibernate.dialect.function.array.HSQLArrayToStringFunction;
|
|
||||||
import org.hibernate.dialect.function.array.OracleArrayConcatElementFunction;
|
|
||||||
import org.hibernate.dialect.function.array.OracleArrayConcatFunction;
|
|
||||||
import org.hibernate.dialect.function.array.OracleArrayFillFunction;
|
|
||||||
import org.hibernate.dialect.function.array.OracleArrayIncludesFunction;
|
|
||||||
import org.hibernate.dialect.function.array.OracleArrayIntersectsFunction;
|
|
||||||
import org.hibernate.dialect.function.array.OracleArrayGetFunction;
|
|
||||||
import org.hibernate.dialect.function.array.OracleArrayLengthFunction;
|
|
||||||
import org.hibernate.dialect.function.array.OracleArrayPositionFunction;
|
|
||||||
import org.hibernate.dialect.function.array.OracleArrayPositionsFunction;
|
|
||||||
import org.hibernate.dialect.function.array.OracleArrayRemoveFunction;
|
|
||||||
import org.hibernate.dialect.function.array.OracleArrayRemoveIndexFunction;
|
|
||||||
import org.hibernate.dialect.function.array.OracleArrayReplaceFunction;
|
|
||||||
import org.hibernate.dialect.function.array.OracleArraySetFunction;
|
|
||||||
import org.hibernate.dialect.function.array.OracleArraySliceFunction;
|
|
||||||
import org.hibernate.dialect.function.array.OracleArrayToStringFunction;
|
|
||||||
import org.hibernate.dialect.function.array.OracleArrayTrimFunction;
|
|
||||||
import org.hibernate.dialect.function.array.PostgreSQLArrayConcatElementFunction;
|
|
||||||
import org.hibernate.dialect.function.array.PostgreSQLArrayConcatFunction;
|
|
||||||
import org.hibernate.dialect.function.array.PostgreSQLArrayFillFunction;
|
|
||||||
import org.hibernate.dialect.function.array.PostgreSQLArrayPositionFunction;
|
|
||||||
import org.hibernate.dialect.function.array.PostgreSQLArrayConstructorFunction;
|
|
||||||
import org.hibernate.dialect.function.array.OracleArrayAggEmulation;
|
|
||||||
import org.hibernate.dialect.function.array.OracleArrayConstructorFunction;
|
|
||||||
import org.hibernate.dialect.function.array.OracleArrayContainsFunction;
|
|
||||||
import org.hibernate.dialect.function.array.PostgreSQLArrayPositionsFunction;
|
|
||||||
import org.hibernate.dialect.function.array.PostgreSQLArrayTrimEmulation;
|
|
||||||
import org.hibernate.dialect.function.json.CockroachDBJsonExistsFunction;
|
import org.hibernate.dialect.function.json.CockroachDBJsonExistsFunction;
|
||||||
import org.hibernate.dialect.function.json.CockroachDBJsonQueryFunction;
|
import org.hibernate.dialect.function.json.CockroachDBJsonQueryFunction;
|
||||||
import org.hibernate.dialect.function.json.CockroachDBJsonRemoveFunction;
|
import org.hibernate.dialect.function.json.CockroachDBJsonRemoveFunction;
|
||||||
|
@ -4294,4 +4230,61 @@ public class CommonFunctionFactory {
|
||||||
public void xmlagg_sqlserver() {
|
public void xmlagg_sqlserver() {
|
||||||
functionRegistry.register( "xmlagg", new SQLServerXmlAggFunction( typeConfiguration ) );
|
functionRegistry.register( "xmlagg", new SQLServerXmlAggFunction( typeConfiguration ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Standard unnest() function
|
||||||
|
*/
|
||||||
|
public void unnest(@Nullable String defaultBasicArrayElementColumnName, String defaultIndexSelectionExpression) {
|
||||||
|
functionRegistry.register( "unnest", new UnnestFunction( defaultBasicArrayElementColumnName, defaultIndexSelectionExpression ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Standard unnest() function for databases that don't support arrays natively
|
||||||
|
*/
|
||||||
|
public void unnest_emulated() {
|
||||||
|
// Pass an arbitrary value
|
||||||
|
unnest( "v", "i" );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* H2 unnest() function
|
||||||
|
*/
|
||||||
|
public void unnest_h2(int maxArraySize) {
|
||||||
|
functionRegistry.register( "unnest", new H2UnnestFunction( maxArraySize ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Oracle unnest() function
|
||||||
|
*/
|
||||||
|
public void unnest_oracle() {
|
||||||
|
functionRegistry.register( "unnest", new OracleUnnestFunction() );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PostgreSQL unnest() function
|
||||||
|
*/
|
||||||
|
public void unnest_postgresql() {
|
||||||
|
functionRegistry.register( "unnest", new PostgreSQLUnnestFunction() );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SQL Server unnest() function
|
||||||
|
*/
|
||||||
|
public void unnest_sqlserver() {
|
||||||
|
functionRegistry.register( "unnest", new SQLServerUnnestFunction() );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sybase ASE unnest() function
|
||||||
|
*/
|
||||||
|
public void unnest_sybasease() {
|
||||||
|
functionRegistry.register( "unnest", new SybaseASEUnnestFunction() );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HANA unnest() function
|
||||||
|
*/
|
||||||
|
public void unnest_hana() {
|
||||||
|
functionRegistry.register( "unnest", new HANAUnnestFunction() );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,222 @@
|
||||||
|
/*
|
||||||
|
* SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
|
* Copyright Red Hat Inc. and Hibernate Authors
|
||||||
|
*/
|
||||||
|
package org.hibernate.dialect.function;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
|
import org.hibernate.metamodel.mapping.AttributeMapping;
|
||||||
|
import org.hibernate.metamodel.mapping.CollectionPart;
|
||||||
|
import org.hibernate.metamodel.mapping.EmbeddableMappingType;
|
||||||
|
import org.hibernate.metamodel.mapping.JdbcMappingContainer;
|
||||||
|
import org.hibernate.metamodel.mapping.MappingType;
|
||||||
|
import org.hibernate.metamodel.mapping.SelectableMapping;
|
||||||
|
import org.hibernate.metamodel.mapping.SelectablePath;
|
||||||
|
import org.hibernate.metamodel.mapping.SqlTypedMapping;
|
||||||
|
import org.hibernate.metamodel.mapping.internal.SelectableMappingImpl;
|
||||||
|
import org.hibernate.query.derived.AnonymousTupleType;
|
||||||
|
import org.hibernate.query.sqm.SqmExpressible;
|
||||||
|
import org.hibernate.query.sqm.produce.function.SetReturningFunctionTypeResolver;
|
||||||
|
import org.hibernate.query.sqm.tree.SqmTypedNode;
|
||||||
|
import org.hibernate.sql.ast.tree.SqlAstNode;
|
||||||
|
import org.hibernate.sql.ast.tree.expression.Expression;
|
||||||
|
import org.hibernate.type.BasicPluralType;
|
||||||
|
import org.hibernate.type.BasicType;
|
||||||
|
import org.hibernate.type.descriptor.jdbc.AggregateJdbcType;
|
||||||
|
import org.hibernate.type.spi.TypeConfiguration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @since 7.0
|
||||||
|
*/
|
||||||
|
public class UnnestSetReturningFunctionTypeResolver implements SetReturningFunctionTypeResolver {
|
||||||
|
|
||||||
|
protected final @Nullable String defaultBasicArrayColumnName;
|
||||||
|
protected final String defaultIndexSelectionExpression;
|
||||||
|
|
||||||
|
public UnnestSetReturningFunctionTypeResolver(@Nullable String defaultBasicArrayColumnName, String defaultIndexSelectionExpression) {
|
||||||
|
this.defaultBasicArrayColumnName = defaultBasicArrayColumnName;
|
||||||
|
this.defaultIndexSelectionExpression = defaultIndexSelectionExpression;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AnonymousTupleType<?> resolveTupleType(List<? extends SqmTypedNode<?>> arguments, TypeConfiguration typeConfiguration) {
|
||||||
|
final SqmTypedNode<?> arrayArgument = arguments.get( 0 );
|
||||||
|
final SqmExpressible<?> expressible = arrayArgument.getExpressible();
|
||||||
|
if ( expressible == null ) {
|
||||||
|
throw new IllegalArgumentException( "Couldn't determine array type of argument to function 'unnest'" );
|
||||||
|
}
|
||||||
|
if ( !( expressible.getSqmType() instanceof BasicPluralType<?,?> pluralType ) ) {
|
||||||
|
throw new IllegalArgumentException( "Argument passed to function 'unnest' is not a BasicPluralType. Found: " + expressible );
|
||||||
|
}
|
||||||
|
|
||||||
|
final BasicType<?> elementType = pluralType.getElementType();
|
||||||
|
final SqmExpressible<?>[] componentTypes;
|
||||||
|
final String[] componentNames;
|
||||||
|
if ( elementType.getJdbcType() instanceof AggregateJdbcType aggregateJdbcType
|
||||||
|
&& aggregateJdbcType.getEmbeddableMappingType() != null ) {
|
||||||
|
final EmbeddableMappingType embeddableMappingType = aggregateJdbcType.getEmbeddableMappingType();
|
||||||
|
componentTypes = determineComponentTypes( embeddableMappingType );
|
||||||
|
componentNames = new String[componentTypes.length];
|
||||||
|
final int numberOfAttributeMappings = embeddableMappingType.getNumberOfAttributeMappings();
|
||||||
|
int index = 0;
|
||||||
|
for ( int i = 0; i < numberOfAttributeMappings; i++ ) {
|
||||||
|
final AttributeMapping attributeMapping = embeddableMappingType.getAttributeMapping( i );
|
||||||
|
if ( attributeMapping.getMappedType() instanceof SqmExpressible<?> ) {
|
||||||
|
componentNames[index++] = attributeMapping.getAttributeName();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert index == componentNames.length - 1;
|
||||||
|
componentTypes[index] = typeConfiguration.getBasicTypeForJavaType( Long.class );
|
||||||
|
componentNames[index] = CollectionPart.Nature.INDEX.getName();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
componentTypes = new SqmExpressible<?>[]{ elementType, typeConfiguration.getBasicTypeForJavaType( Long.class ) };
|
||||||
|
componentNames = new String[]{ CollectionPart.Nature.ELEMENT.getName(), CollectionPart.Nature.INDEX.getName() };
|
||||||
|
}
|
||||||
|
return new AnonymousTupleType<>( componentTypes, componentNames );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SelectableMapping[] resolveFunctionReturnType(
|
||||||
|
List<? extends SqlAstNode> arguments,
|
||||||
|
String tableIdentifierVariable,
|
||||||
|
boolean withOrdinality,
|
||||||
|
TypeConfiguration typeConfiguration) {
|
||||||
|
final Expression expression = (Expression) arguments.get( 0 );
|
||||||
|
final JdbcMappingContainer expressionType = expression.getExpressionType();
|
||||||
|
if ( expressionType == null ) {
|
||||||
|
throw new IllegalArgumentException( "Couldn't determine array type of argument to function 'unnest'" );
|
||||||
|
}
|
||||||
|
if ( !( expressionType.getSingleJdbcMapping() instanceof BasicPluralType<?,?> pluralType ) ) {
|
||||||
|
throw new IllegalArgumentException( "Argument passed to function 'unnest' is not a BasicPluralType. Found: " + expressionType );
|
||||||
|
}
|
||||||
|
|
||||||
|
final SelectableMapping indexMapping = withOrdinality ? new SelectableMappingImpl(
|
||||||
|
"",
|
||||||
|
defaultIndexSelectionExpression,
|
||||||
|
new SelectablePath( CollectionPart.Nature.INDEX.getName() ),
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
typeConfiguration.getBasicTypeForJavaType( Long.class )
|
||||||
|
) : null;
|
||||||
|
|
||||||
|
final BasicType<?> elementType = pluralType.getElementType();
|
||||||
|
final SelectableMapping[] returnType;
|
||||||
|
if ( elementType.getJdbcType() instanceof AggregateJdbcType aggregateJdbcType
|
||||||
|
&& aggregateJdbcType.getEmbeddableMappingType() != null ) {
|
||||||
|
final EmbeddableMappingType embeddableMappingType = aggregateJdbcType.getEmbeddableMappingType();
|
||||||
|
final int jdbcValueCount = embeddableMappingType.getJdbcValueCount();
|
||||||
|
returnType = new SelectableMapping[jdbcValueCount + (indexMapping == null ? 0 : 1)];
|
||||||
|
for ( int i = 0; i < jdbcValueCount; i++ ) {
|
||||||
|
final SelectableMapping selectableMapping = embeddableMappingType.getJdbcValueSelectable( i );
|
||||||
|
final String selectableName = selectableMapping.getSelectableName();
|
||||||
|
returnType[i] = new SelectableMappingImpl(
|
||||||
|
selectableMapping.getContainingTableExpression(),
|
||||||
|
selectableName,
|
||||||
|
new SelectablePath( selectableName ),
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
selectableMapping.getColumnDefinition(),
|
||||||
|
selectableMapping.getLength(),
|
||||||
|
selectableMapping.getPrecision(),
|
||||||
|
selectableMapping.getScale(),
|
||||||
|
selectableMapping.getTemporalPrecision(),
|
||||||
|
selectableMapping.isLob(),
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
selectableMapping.isFormula(),
|
||||||
|
selectableMapping.getJdbcMapping()
|
||||||
|
);
|
||||||
|
if ( indexMapping != null ) {
|
||||||
|
returnType[jdbcValueCount] = indexMapping;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
final String elementSelectionExpression = defaultBasicArrayColumnName == null
|
||||||
|
? tableIdentifierVariable
|
||||||
|
: defaultBasicArrayColumnName;
|
||||||
|
final SelectableMapping elementMapping;
|
||||||
|
if ( expressionType instanceof SqlTypedMapping typedMapping ) {
|
||||||
|
elementMapping = new SelectableMappingImpl(
|
||||||
|
"",
|
||||||
|
elementSelectionExpression,
|
||||||
|
new SelectablePath( CollectionPart.Nature.ELEMENT.getName() ),
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
typedMapping.getColumnDefinition(),
|
||||||
|
typedMapping.getLength(),
|
||||||
|
typedMapping.getPrecision(),
|
||||||
|
typedMapping.getScale(),
|
||||||
|
typedMapping.getTemporalPrecision(),
|
||||||
|
typedMapping.isLob(),
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
elementType
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
elementMapping = new SelectableMappingImpl(
|
||||||
|
"",
|
||||||
|
elementSelectionExpression,
|
||||||
|
new SelectablePath( CollectionPart.Nature.ELEMENT.getName() ),
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
elementType
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if ( indexMapping == null ) {
|
||||||
|
returnType = new SelectableMapping[]{ elementMapping };
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
returnType = new SelectableMapping[] {elementMapping, indexMapping};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return returnType;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static SqmExpressible<?>[] determineComponentTypes(EmbeddableMappingType embeddableMappingType) {
|
||||||
|
final int numberOfAttributeMappings = embeddableMappingType.getNumberOfAttributeMappings();
|
||||||
|
final ArrayList<SqmExpressible<?>> expressibles = new ArrayList<>( numberOfAttributeMappings + 1 );
|
||||||
|
|
||||||
|
for ( int i = 0; i < numberOfAttributeMappings; i++ ) {
|
||||||
|
final AttributeMapping attributeMapping = embeddableMappingType.getAttributeMapping( i );
|
||||||
|
final MappingType mappedType = attributeMapping.getMappedType();
|
||||||
|
if ( mappedType instanceof SqmExpressible<?> ) {
|
||||||
|
expressibles.add( (SqmExpressible<?>) mappedType );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return expressibles.toArray( new SqmExpressible<?>[expressibles.size() + 1] );
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,338 @@
|
||||||
|
/*
|
||||||
|
* SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
|
* Copyright Red Hat Inc. and Hibernate Authors
|
||||||
|
*/
|
||||||
|
package org.hibernate.dialect.function.array;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.hibernate.dialect.function.UnnestSetReturningFunctionTypeResolver;
|
||||||
|
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||||
|
import org.hibernate.internal.util.NullnessUtil;
|
||||||
|
import org.hibernate.metamodel.mapping.CollectionPart;
|
||||||
|
import org.hibernate.metamodel.mapping.EmbeddableMappingType;
|
||||||
|
import org.hibernate.metamodel.mapping.JdbcMappingContainer;
|
||||||
|
import org.hibernate.metamodel.mapping.SelectableMapping;
|
||||||
|
import org.hibernate.metamodel.mapping.SelectablePath;
|
||||||
|
import org.hibernate.metamodel.mapping.SqlTypedMapping;
|
||||||
|
import org.hibernate.metamodel.mapping.internal.SelectableMappingImpl;
|
||||||
|
import org.hibernate.query.derived.AnonymousTupleTableGroupProducer;
|
||||||
|
import org.hibernate.query.derived.AnonymousTupleType;
|
||||||
|
import org.hibernate.query.spi.QueryEngine;
|
||||||
|
import org.hibernate.query.sqm.ComparisonOperator;
|
||||||
|
import org.hibernate.query.sqm.function.SelfRenderingSqmSetReturningFunction;
|
||||||
|
import org.hibernate.query.sqm.sql.SqmToSqlAstConverter;
|
||||||
|
import org.hibernate.query.sqm.tree.SqmTypedNode;
|
||||||
|
import org.hibernate.spi.NavigablePath;
|
||||||
|
import org.hibernate.sql.Template;
|
||||||
|
import org.hibernate.sql.ast.SqlAstTranslator;
|
||||||
|
import org.hibernate.sql.ast.spi.FromClauseAccess;
|
||||||
|
import org.hibernate.sql.ast.spi.SqlAppender;
|
||||||
|
import org.hibernate.sql.ast.tree.SqlAstNode;
|
||||||
|
import org.hibernate.sql.ast.tree.expression.ColumnReference;
|
||||||
|
import org.hibernate.sql.ast.tree.expression.Expression;
|
||||||
|
import org.hibernate.sql.ast.tree.expression.SelfRenderingExpression;
|
||||||
|
import org.hibernate.sql.ast.tree.from.FunctionTableGroup;
|
||||||
|
import org.hibernate.sql.ast.tree.from.TableGroup;
|
||||||
|
import org.hibernate.sql.ast.tree.from.TableGroupJoin;
|
||||||
|
import org.hibernate.sql.ast.tree.predicate.ComparisonPredicate;
|
||||||
|
import org.hibernate.type.BasicPluralType;
|
||||||
|
import org.hibernate.type.BasicType;
|
||||||
|
import org.hibernate.type.descriptor.jdbc.AggregateJdbcType;
|
||||||
|
|
||||||
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
|
import org.hibernate.type.spi.TypeConfiguration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* H2 unnest function.
|
||||||
|
* <p>
|
||||||
|
* H2 does not support "lateral" i.e. the use of a from node within another,
|
||||||
|
* but we can apply the same trick that we already applied everywhere else for H2,
|
||||||
|
* which is to join a sequence table to emulate array element rows
|
||||||
|
* and eliminate non-existing array elements by checking the index against array length.
|
||||||
|
* Finally, we rewrite the selection expressions to access the array by joined sequence index.
|
||||||
|
*/
|
||||||
|
public class H2UnnestFunction extends UnnestFunction {
|
||||||
|
|
||||||
|
private final int maximumArraySize;
|
||||||
|
|
||||||
|
public H2UnnestFunction(int maximumArraySize) {
|
||||||
|
super( new H2UnnestSetReturningFunctionTypeResolver() );
|
||||||
|
this.maximumArraySize = maximumArraySize;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected <T> SelfRenderingSqmSetReturningFunction<T> generateSqmSetReturningFunctionExpression(
|
||||||
|
List<? extends SqmTypedNode<?>> arguments,
|
||||||
|
QueryEngine queryEngine) {
|
||||||
|
//noinspection unchecked
|
||||||
|
return new SelfRenderingSqmSetReturningFunction<>(
|
||||||
|
this,
|
||||||
|
this,
|
||||||
|
arguments,
|
||||||
|
getArgumentsValidator(),
|
||||||
|
getSetReturningTypeResolver(),
|
||||||
|
(AnonymousTupleType<T>) getSetReturningTypeResolver().resolveTupleType( arguments, queryEngine.getTypeConfiguration() ),
|
||||||
|
queryEngine.getCriteriaBuilder(),
|
||||||
|
getName()
|
||||||
|
) {
|
||||||
|
@Override
|
||||||
|
public TableGroup convertToSqlAst(
|
||||||
|
NavigablePath navigablePath,
|
||||||
|
String identifierVariable,
|
||||||
|
boolean lateral,
|
||||||
|
boolean canUseInnerJoins,
|
||||||
|
boolean withOrdinality,
|
||||||
|
SqmToSqlAstConverter walker) {
|
||||||
|
// Register a transformer that adds a join predicate "array_length(array) <= index"
|
||||||
|
final FunctionTableGroup functionTableGroup = (FunctionTableGroup) super.convertToSqlAst(
|
||||||
|
navigablePath,
|
||||||
|
identifierVariable,
|
||||||
|
lateral,
|
||||||
|
canUseInnerJoins,
|
||||||
|
withOrdinality,
|
||||||
|
walker
|
||||||
|
);
|
||||||
|
//noinspection unchecked
|
||||||
|
final List<SqlAstNode> sqlArguments = (List<SqlAstNode>) functionTableGroup.getPrimaryTableReference()
|
||||||
|
.getFunctionExpression()
|
||||||
|
.getArguments();
|
||||||
|
// Can only do this transformation if the argument is a column reference
|
||||||
|
final ColumnReference columnReference = ( (Expression) sqlArguments.get( 0 ) ).getColumnReference();
|
||||||
|
if ( columnReference != null ) {
|
||||||
|
final String tableQualifier = columnReference.getQualifier();
|
||||||
|
// Find the table group which the unnest argument refers to
|
||||||
|
final FromClauseAccess fromClauseAccess = walker.getFromClauseAccess();
|
||||||
|
final TableGroup sourceTableGroup =
|
||||||
|
fromClauseAccess.findTableGroupByIdentificationVariable( tableQualifier );
|
||||||
|
if ( sourceTableGroup != null ) {
|
||||||
|
// Register a query transformer to register a join predicate
|
||||||
|
walker.registerQueryTransformer( (cteContainer, querySpec, converter) -> {
|
||||||
|
final TableGroup parentTableGroup = querySpec.getFromClause().queryTableGroups(
|
||||||
|
tg -> tg.findTableGroupJoin( functionTableGroup ) == null ? null : tg
|
||||||
|
);
|
||||||
|
final TableGroupJoin join = parentTableGroup.findTableGroupJoin( functionTableGroup );
|
||||||
|
final BasicType<Integer> integerType = walker.getCreationContext()
|
||||||
|
.getSessionFactory()
|
||||||
|
.getNodeBuilder()
|
||||||
|
.getIntegerType();
|
||||||
|
final Expression lhs = new SelfRenderingExpression() {
|
||||||
|
@Override
|
||||||
|
public void renderToSql(
|
||||||
|
SqlAppender sqlAppender,
|
||||||
|
SqlAstTranslator<?> walker,
|
||||||
|
SessionFactoryImplementor sessionFactory) {
|
||||||
|
sqlAppender.append( "coalesce(array_length(" );
|
||||||
|
columnReference.accept( walker );
|
||||||
|
sqlAppender.append( "),0)" );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JdbcMappingContainer getExpressionType() {
|
||||||
|
return integerType;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
final Expression rhs = new ColumnReference(
|
||||||
|
functionTableGroup.getPrimaryTableReference().getIdentificationVariable(),
|
||||||
|
// The default column name for the system_range function
|
||||||
|
"x",
|
||||||
|
false,
|
||||||
|
null,
|
||||||
|
integerType
|
||||||
|
);
|
||||||
|
join.applyPredicate( new ComparisonPredicate( lhs, ComparisonOperator.GREATER_THAN_OR_EQUAL, rhs ) );
|
||||||
|
return querySpec;
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return functionTableGroup;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void renderJsonTable(
|
||||||
|
SqlAppender sqlAppender,
|
||||||
|
Expression array,
|
||||||
|
BasicPluralType<?, ?> pluralType,
|
||||||
|
@Nullable SqlTypedMapping sqlTypedMapping,
|
||||||
|
AnonymousTupleTableGroupProducer tupleType,
|
||||||
|
String tableIdentifierVariable,
|
||||||
|
SqlAstTranslator<?> walker) {
|
||||||
|
renderUnnest( sqlAppender, array, pluralType, sqlTypedMapping, tupleType, tableIdentifierVariable, walker );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void renderUnnest(
|
||||||
|
SqlAppender sqlAppender,
|
||||||
|
Expression array,
|
||||||
|
BasicPluralType<?, ?> pluralType,
|
||||||
|
@Nullable SqlTypedMapping sqlTypedMapping,
|
||||||
|
AnonymousTupleTableGroupProducer tupleType,
|
||||||
|
String tableIdentifierVariable,
|
||||||
|
SqlAstTranslator<?> walker) {
|
||||||
|
final ColumnReference columnReference = array.getColumnReference();
|
||||||
|
if ( columnReference != null ) {
|
||||||
|
sqlAppender.append( "system_range(1," );
|
||||||
|
sqlAppender.append( Integer.toString( maximumArraySize ) );
|
||||||
|
sqlAppender.append( ")" );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
super.renderUnnest( sqlAppender, array, pluralType, sqlTypedMapping, tupleType, tableIdentifierVariable, walker );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class H2UnnestSetReturningFunctionTypeResolver extends UnnestSetReturningFunctionTypeResolver {
|
||||||
|
|
||||||
|
public H2UnnestSetReturningFunctionTypeResolver() {
|
||||||
|
// c1 is the default column name for the "unnest()" function
|
||||||
|
super( "c1", "nord" );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SelectableMapping[] resolveFunctionReturnType(
|
||||||
|
List<? extends SqlAstNode> arguments,
|
||||||
|
String tableIdentifierVariable,
|
||||||
|
boolean withOrdinality,
|
||||||
|
TypeConfiguration typeConfiguration) {
|
||||||
|
final Expression expression = (Expression) arguments.get( 0 );
|
||||||
|
final JdbcMappingContainer expressionType = expression.getExpressionType();
|
||||||
|
if ( expressionType == null ) {
|
||||||
|
throw new IllegalArgumentException( "Couldn't determine array type of argument to function 'unnest'" );
|
||||||
|
}
|
||||||
|
if ( !( expressionType.getSingleJdbcMapping() instanceof BasicPluralType<?,?> pluralType ) ) {
|
||||||
|
throw new IllegalArgumentException( "Argument passed to function 'unnest' is not a BasicPluralType. Found: " + expressionType );
|
||||||
|
}
|
||||||
|
|
||||||
|
final SelectableMapping indexMapping = withOrdinality ? new SelectableMappingImpl(
|
||||||
|
"",
|
||||||
|
expression.getColumnReference() != null ? "x" : defaultIndexSelectionExpression,
|
||||||
|
new SelectablePath( CollectionPart.Nature.INDEX.getName() ),
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
typeConfiguration.getBasicTypeForJavaType( Long.class )
|
||||||
|
) : null;
|
||||||
|
|
||||||
|
final BasicType<?> elementType = pluralType.getElementType();
|
||||||
|
final SelectableMapping[] returnType;
|
||||||
|
if ( elementType.getJdbcType() instanceof AggregateJdbcType aggregateJdbcType
|
||||||
|
&& aggregateJdbcType.getEmbeddableMappingType() != null ) {
|
||||||
|
final ColumnReference arrayColumnReference = expression.getColumnReference();
|
||||||
|
if ( arrayColumnReference == null ) {
|
||||||
|
throw new IllegalArgumentException( "Argument passed to function 'unnest' is not a column reference, but an aggregate type, which is not yet supported." );
|
||||||
|
}
|
||||||
|
// For column references we render an emulation through system_range(),
|
||||||
|
// so we need to render an array access to get to the element
|
||||||
|
final String elementReadExpression = "array_get(" + arrayColumnReference.getExpressionText() + "," + Template.TEMPLATE + ".x)";
|
||||||
|
final String arrayReadExpression = NullnessUtil.castNonNull( arrayColumnReference.getReadExpression() );
|
||||||
|
final EmbeddableMappingType embeddableMappingType = aggregateJdbcType.getEmbeddableMappingType();
|
||||||
|
final int jdbcValueCount = embeddableMappingType.getJdbcValueCount();
|
||||||
|
returnType = new SelectableMapping[jdbcValueCount + (indexMapping == null ? 0 : 1)];
|
||||||
|
for ( int i = 0; i < jdbcValueCount; i++ ) {
|
||||||
|
final SelectableMapping selectableMapping = embeddableMappingType.getJdbcValueSelectable( i );
|
||||||
|
// The array expression has to be replaced with the actual array_get read expression in this emulation
|
||||||
|
final String customReadExpression = selectableMapping.getCustomReadExpression()
|
||||||
|
.replace( arrayReadExpression, elementReadExpression );
|
||||||
|
returnType[i] = new SelectableMappingImpl(
|
||||||
|
selectableMapping.getContainingTableExpression(),
|
||||||
|
selectableMapping.getSelectablePath().getSelectableName(),
|
||||||
|
new SelectablePath( selectableMapping.getSelectablePath().getSelectableName() ),
|
||||||
|
customReadExpression,
|
||||||
|
selectableMapping.getCustomWriteExpression(),
|
||||||
|
selectableMapping.getColumnDefinition(),
|
||||||
|
selectableMapping.getLength(),
|
||||||
|
selectableMapping.getPrecision(),
|
||||||
|
selectableMapping.getScale(),
|
||||||
|
selectableMapping.getTemporalPrecision(),
|
||||||
|
selectableMapping.isLob(),
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
selectableMapping.isFormula(),
|
||||||
|
selectableMapping.getJdbcMapping()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if ( indexMapping != null ) {
|
||||||
|
returnType[jdbcValueCount] = indexMapping;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
final String elementSelectionExpression;
|
||||||
|
final String elementReadExpression;
|
||||||
|
final ColumnReference columnReference = expression.getColumnReference();
|
||||||
|
if ( columnReference != null ) {
|
||||||
|
// For column references we render an emulation through system_range(),
|
||||||
|
// so we need to render an array access to get to the element
|
||||||
|
elementSelectionExpression = columnReference.getColumnExpression();
|
||||||
|
elementReadExpression = "array_get(" + columnReference.getExpressionText() + "," + Template.TEMPLATE + ".x)";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
elementSelectionExpression = defaultBasicArrayColumnName;
|
||||||
|
elementReadExpression = null;
|
||||||
|
}
|
||||||
|
final SelectableMapping elementMapping;
|
||||||
|
if ( expressionType instanceof SqlTypedMapping typedMapping ) {
|
||||||
|
elementMapping = new SelectableMappingImpl(
|
||||||
|
"",
|
||||||
|
elementSelectionExpression,
|
||||||
|
new SelectablePath( CollectionPart.Nature.ELEMENT.getName() ),
|
||||||
|
elementReadExpression,
|
||||||
|
null,
|
||||||
|
typedMapping.getColumnDefinition(),
|
||||||
|
typedMapping.getLength(),
|
||||||
|
typedMapping.getPrecision(),
|
||||||
|
typedMapping.getScale(),
|
||||||
|
typedMapping.getTemporalPrecision(),
|
||||||
|
typedMapping.isLob(),
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
elementType
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
elementMapping = new SelectableMappingImpl(
|
||||||
|
"",
|
||||||
|
elementSelectionExpression,
|
||||||
|
new SelectablePath( CollectionPart.Nature.ELEMENT.getName() ),
|
||||||
|
elementReadExpression,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
elementType
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if ( indexMapping == null ) {
|
||||||
|
returnType = new SelectableMapping[]{ elementMapping };
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
returnType = new SelectableMapping[] {elementMapping, indexMapping};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return returnType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,533 @@
|
||||||
|
/*
|
||||||
|
* SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
|
* Copyright Red Hat Inc. and Hibernate Authors
|
||||||
|
*/
|
||||||
|
package org.hibernate.dialect.function.array;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.hibernate.QueryException;
|
||||||
|
import org.hibernate.dialect.XmlHelper;
|
||||||
|
import org.hibernate.dialect.function.json.ExpressionTypeHelper;
|
||||||
|
import org.hibernate.engine.jdbc.Size;
|
||||||
|
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||||
|
import org.hibernate.metamodel.mapping.CollectionPart;
|
||||||
|
import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart;
|
||||||
|
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||||
|
import org.hibernate.metamodel.mapping.EntityValuedModelPart;
|
||||||
|
import org.hibernate.metamodel.mapping.JdbcMapping;
|
||||||
|
import org.hibernate.metamodel.mapping.JdbcMappingContainer;
|
||||||
|
import org.hibernate.metamodel.mapping.ModelPartContainer;
|
||||||
|
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
|
||||||
|
import org.hibernate.metamodel.mapping.SqlTypedMapping;
|
||||||
|
import org.hibernate.metamodel.mapping.ValuedModelPart;
|
||||||
|
import org.hibernate.metamodel.mapping.internal.EmbeddedCollectionPart;
|
||||||
|
import org.hibernate.query.derived.AnonymousTupleTableGroupProducer;
|
||||||
|
import org.hibernate.query.derived.AnonymousTupleType;
|
||||||
|
import org.hibernate.query.spi.QueryEngine;
|
||||||
|
import org.hibernate.query.sqm.ComparisonOperator;
|
||||||
|
import org.hibernate.query.sqm.function.SelfRenderingSqmSetReturningFunction;
|
||||||
|
import org.hibernate.query.sqm.sql.SqmToSqlAstConverter;
|
||||||
|
import org.hibernate.query.sqm.tree.SqmTypedNode;
|
||||||
|
import org.hibernate.spi.NavigablePath;
|
||||||
|
import org.hibernate.sql.ast.SqlAstTranslator;
|
||||||
|
import org.hibernate.sql.ast.internal.ColumnQualifierCollectorSqlAstWalker;
|
||||||
|
import org.hibernate.sql.ast.spi.FromClauseAccess;
|
||||||
|
import org.hibernate.sql.ast.spi.SqlAppender;
|
||||||
|
import org.hibernate.sql.ast.tree.SqlAstNode;
|
||||||
|
import org.hibernate.sql.ast.tree.cte.CteColumn;
|
||||||
|
import org.hibernate.sql.ast.tree.cte.CteStatement;
|
||||||
|
import org.hibernate.sql.ast.tree.cte.CteTable;
|
||||||
|
import org.hibernate.sql.ast.tree.expression.ColumnReference;
|
||||||
|
import org.hibernate.sql.ast.tree.expression.Expression;
|
||||||
|
import org.hibernate.sql.ast.tree.expression.SelfRenderingExpression;
|
||||||
|
import org.hibernate.sql.ast.tree.expression.SqlTuple;
|
||||||
|
import org.hibernate.sql.ast.tree.from.FunctionTableGroup;
|
||||||
|
import org.hibernate.sql.ast.tree.from.StandardTableGroup;
|
||||||
|
import org.hibernate.sql.ast.tree.from.TableGroup;
|
||||||
|
import org.hibernate.sql.ast.tree.from.TableGroupJoin;
|
||||||
|
import org.hibernate.sql.ast.tree.from.TableGroupProducer;
|
||||||
|
import org.hibernate.sql.ast.tree.predicate.ComparisonPredicate;
|
||||||
|
import org.hibernate.sql.ast.tree.predicate.NullnessPredicate;
|
||||||
|
import org.hibernate.sql.ast.tree.select.QuerySpec;
|
||||||
|
import org.hibernate.sql.ast.tree.select.SelectStatement;
|
||||||
|
import org.hibernate.sql.results.internal.SqlSelectionImpl;
|
||||||
|
import org.hibernate.type.BasicPluralType;
|
||||||
|
import org.hibernate.type.BasicType;
|
||||||
|
import org.hibernate.type.Type;
|
||||||
|
import org.hibernate.type.descriptor.java.BasicPluralJavaType;
|
||||||
|
import org.hibernate.type.descriptor.sql.spi.DdlTypeRegistry;
|
||||||
|
|
||||||
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HANA unnest function.
|
||||||
|
*/
|
||||||
|
public class HANAUnnestFunction extends UnnestFunction {
|
||||||
|
|
||||||
|
public HANAUnnestFunction() {
|
||||||
|
super( "v", "i" );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected <T> SelfRenderingSqmSetReturningFunction<T> generateSqmSetReturningFunctionExpression(
|
||||||
|
List<? extends SqmTypedNode<?>> arguments,
|
||||||
|
QueryEngine queryEngine) {
|
||||||
|
//noinspection unchecked
|
||||||
|
return new SelfRenderingSqmSetReturningFunction<>(
|
||||||
|
this,
|
||||||
|
this,
|
||||||
|
arguments,
|
||||||
|
getArgumentsValidator(),
|
||||||
|
getSetReturningTypeResolver(),
|
||||||
|
(AnonymousTupleType<T>) getSetReturningTypeResolver().resolveTupleType( arguments, queryEngine.getTypeConfiguration() ),
|
||||||
|
queryEngine.getCriteriaBuilder(),
|
||||||
|
getName()
|
||||||
|
) {
|
||||||
|
@Override
|
||||||
|
public TableGroup convertToSqlAst(
|
||||||
|
NavigablePath navigablePath,
|
||||||
|
String identifierVariable,
|
||||||
|
boolean lateral,
|
||||||
|
boolean canUseInnerJoins,
|
||||||
|
boolean withOrdinality,
|
||||||
|
SqmToSqlAstConverter walker) {
|
||||||
|
// SAP HANA only supports table column references i.e. `TABLE_NAME.COLUMN_NAME`
|
||||||
|
// or constants as arguments to xmltable/json_table, so it's impossible to do lateral joins.
|
||||||
|
// There is a nice trick we can apply to make this work though, which is to figure out
|
||||||
|
// the table group an expression belongs to and render a special CTE returning xml/json that can be joined.
|
||||||
|
// The xml/json of that CTE needs to be extended by table group primary key data,
|
||||||
|
// so we can join it later.
|
||||||
|
final FunctionTableGroup functionTableGroup = (FunctionTableGroup) super.convertToSqlAst(
|
||||||
|
navigablePath,
|
||||||
|
identifierVariable,
|
||||||
|
lateral,
|
||||||
|
canUseInnerJoins,
|
||||||
|
withOrdinality,
|
||||||
|
walker
|
||||||
|
);
|
||||||
|
//noinspection unchecked
|
||||||
|
final List<SqlAstNode> sqlArguments = (List<SqlAstNode>) functionTableGroup.getPrimaryTableReference()
|
||||||
|
.getFunctionExpression()
|
||||||
|
.getArguments();
|
||||||
|
final Expression argument = (Expression) sqlArguments.get( 0 );
|
||||||
|
final Set<String> qualifiers = ColumnQualifierCollectorSqlAstWalker.determineColumnQualifiers( argument );
|
||||||
|
// Can only do this transformation if the argument contains a single column reference qualifier
|
||||||
|
if ( qualifiers.size() == 1 ) {
|
||||||
|
final String tableQualifier = qualifiers.iterator().next();
|
||||||
|
// Find the table group which the unnest argument refers to
|
||||||
|
final FromClauseAccess fromClauseAccess = walker.getFromClauseAccess();
|
||||||
|
final TableGroup sourceTableGroup =
|
||||||
|
fromClauseAccess.findTableGroupByIdentificationVariable( tableQualifier );
|
||||||
|
if ( sourceTableGroup != null ) {
|
||||||
|
final List<ColumnInfo> idColumns = new ArrayList<>();
|
||||||
|
addIdColumns( sourceTableGroup.getModelPart(), idColumns );
|
||||||
|
|
||||||
|
// Register a query transformer to register the CTE and rewrite the array argument
|
||||||
|
walker.registerQueryTransformer( (cteContainer, querySpec, converter) -> {
|
||||||
|
// Determine a CTE name that is available
|
||||||
|
final String baseName = "_data";
|
||||||
|
String cteName;
|
||||||
|
int index = 0;
|
||||||
|
do {
|
||||||
|
cteName = baseName + ( index++ );
|
||||||
|
} while ( cteContainer.getCteStatement( cteName ) != null );
|
||||||
|
|
||||||
|
final TableGroup parentTableGroup = querySpec.getFromClause().queryTableGroups(
|
||||||
|
tg -> tg.findTableGroupJoin( functionTableGroup ) == null ? null : tg
|
||||||
|
);
|
||||||
|
final TableGroupJoin join = parentTableGroup.findTableGroupJoin( functionTableGroup );
|
||||||
|
final Expression lhs = createExpression( tableQualifier, idColumns );
|
||||||
|
final Expression rhs = createExpression(
|
||||||
|
functionTableGroup.getPrimaryTableReference().getIdentificationVariable(),
|
||||||
|
idColumns
|
||||||
|
);
|
||||||
|
join.applyPredicate( new ComparisonPredicate( lhs, ComparisonOperator.EQUAL, rhs ) );
|
||||||
|
|
||||||
|
final String tableName = cteName;
|
||||||
|
final List<CteColumn> cteColumns = List.of(
|
||||||
|
new CteColumn( "v", argument.getExpressionType().getSingleJdbcMapping() )
|
||||||
|
);
|
||||||
|
final QuerySpec cteQuery = new QuerySpec( false );
|
||||||
|
cteQuery.getFromClause().addRoot(
|
||||||
|
new StandardTableGroup(
|
||||||
|
true,
|
||||||
|
sourceTableGroup.getNavigablePath(),
|
||||||
|
(TableGroupProducer) sourceTableGroup.getModelPart(),
|
||||||
|
false,
|
||||||
|
null,
|
||||||
|
sourceTableGroup.findTableReference( tableQualifier ),
|
||||||
|
false,
|
||||||
|
null,
|
||||||
|
joinTableName -> false,
|
||||||
|
(joinTableName, tg) -> null,
|
||||||
|
null
|
||||||
|
)
|
||||||
|
);
|
||||||
|
final Expression wrapperExpression;
|
||||||
|
if ( ExpressionTypeHelper.isXml( argument ) ) {
|
||||||
|
wrapperExpression = new XmlWrapperExpression( idColumns, tableQualifier, argument );
|
||||||
|
// xmltable is allergic to null values and produces no result if one occurs,
|
||||||
|
// so we must filter them out
|
||||||
|
cteQuery.applyPredicate( new NullnessPredicate( argument, true ) );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
wrapperExpression = new JsonWrapperExpression( idColumns, tableQualifier, argument );
|
||||||
|
}
|
||||||
|
cteQuery.getSelectClause().addSqlSelection( new SqlSelectionImpl( wrapperExpression ) );
|
||||||
|
cteContainer.addCteStatement( new CteStatement(
|
||||||
|
new CteTable( tableName, cteColumns ),
|
||||||
|
new SelectStatement( cteQuery )
|
||||||
|
) );
|
||||||
|
sqlArguments.set( 0, new TableColumnReferenceExpression( argument, tableName, idColumns ) );
|
||||||
|
return querySpec;
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return functionTableGroup;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Expression createExpression(String qualifier, List<ColumnInfo> idColumns) {
|
||||||
|
if ( idColumns.size() == 1 ) {
|
||||||
|
final ColumnInfo columnInfo = idColumns.get( 0 );
|
||||||
|
return new ColumnReference( qualifier, columnInfo.name(), false, null, columnInfo.jdbcMapping() );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
final ArrayList<Expression> expressions = new ArrayList<>( idColumns.size() );
|
||||||
|
for ( ColumnInfo columnInfo : idColumns ) {
|
||||||
|
expressions.add(
|
||||||
|
new ColumnReference(
|
||||||
|
qualifier,
|
||||||
|
columnInfo.name(),
|
||||||
|
false,
|
||||||
|
null,
|
||||||
|
columnInfo.jdbcMapping()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return new SqlTuple( expressions, null );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addIdColumns(ModelPartContainer modelPartContainer, List<ColumnInfo> idColumns) {
|
||||||
|
if ( modelPartContainer instanceof EntityValuedModelPart entityValuedModelPart ) {
|
||||||
|
addIdColumns( entityValuedModelPart.getEntityMappingType(), idColumns );
|
||||||
|
}
|
||||||
|
else if ( modelPartContainer instanceof PluralAttributeMapping pluralAttributeMapping ) {
|
||||||
|
addIdColumns( pluralAttributeMapping, idColumns );
|
||||||
|
}
|
||||||
|
else if ( modelPartContainer instanceof EmbeddableValuedModelPart embeddableModelPart ) {
|
||||||
|
addIdColumns( embeddableModelPart, idColumns );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw new QueryException( "Unsupported model part container: " + modelPartContainer );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addIdColumns(EmbeddableValuedModelPart embeddableModelPart, List<ColumnInfo> idColumns) {
|
||||||
|
if ( embeddableModelPart instanceof EmbeddedCollectionPart collectionPart ) {
|
||||||
|
addIdColumns( collectionPart.getCollectionAttribute(), idColumns );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
addIdColumns( embeddableModelPart.asAttributeMapping().getDeclaringType(), idColumns );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addIdColumns(PluralAttributeMapping pluralAttributeMapping, List<ColumnInfo> idColumns) {
|
||||||
|
final DdlTypeRegistry ddlTypeRegistry = pluralAttributeMapping.getCollectionDescriptor()
|
||||||
|
.getFactory()
|
||||||
|
.getTypeConfiguration()
|
||||||
|
.getDdlTypeRegistry();
|
||||||
|
addIdColumns( pluralAttributeMapping.getKeyDescriptor().getKeyPart(), ddlTypeRegistry, idColumns );
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addIdColumns(EntityMappingType entityMappingType, List<ColumnInfo> idColumns) {
|
||||||
|
final DdlTypeRegistry ddlTypeRegistry = entityMappingType.getEntityPersister()
|
||||||
|
.getFactory()
|
||||||
|
.getTypeConfiguration()
|
||||||
|
.getDdlTypeRegistry();
|
||||||
|
addIdColumns( entityMappingType.getIdentifierMapping(), ddlTypeRegistry, idColumns );
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addIdColumns(
|
||||||
|
ValuedModelPart modelPart,
|
||||||
|
DdlTypeRegistry ddlTypeRegistry,
|
||||||
|
List<ColumnInfo> idColumns) {
|
||||||
|
modelPart.forEachSelectable( (selectionIndex, selectableMapping) -> {
|
||||||
|
final JdbcMapping jdbcMapping = selectableMapping.getJdbcMapping().getSingleJdbcMapping();
|
||||||
|
idColumns.add( new ColumnInfo(
|
||||||
|
selectableMapping.getSelectionExpression(),
|
||||||
|
jdbcMapping,
|
||||||
|
ddlTypeRegistry.getTypeName(
|
||||||
|
jdbcMapping.getJdbcType().getDefaultSqlTypeCode(),
|
||||||
|
selectableMapping.toSize(),
|
||||||
|
(Type) jdbcMapping
|
||||||
|
)
|
||||||
|
) );
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
record ColumnInfo(String name, JdbcMapping jdbcMapping, String ddlType) {}
|
||||||
|
|
||||||
|
static class TableColumnReferenceExpression implements SelfRenderingExpression {
|
||||||
|
|
||||||
|
private final Expression argument;
|
||||||
|
private final String tableName;
|
||||||
|
private final List<ColumnInfo> idColumns;
|
||||||
|
|
||||||
|
public TableColumnReferenceExpression(Expression argument, String tableName, List<ColumnInfo> idColumns) {
|
||||||
|
this.argument = argument;
|
||||||
|
this.tableName = tableName;
|
||||||
|
this.idColumns = idColumns;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void renderToSql(
|
||||||
|
SqlAppender sqlAppender,
|
||||||
|
SqlAstTranslator<?> walker,
|
||||||
|
SessionFactoryImplementor sessionFactory) {
|
||||||
|
sqlAppender.appendSql( tableName );
|
||||||
|
sqlAppender.appendSql( ".v" );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JdbcMappingContainer getExpressionType() {
|
||||||
|
return argument.getExpressionType();
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<ColumnInfo> getIdColumns() {
|
||||||
|
return idColumns;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void renderXmlTable(
|
||||||
|
SqlAppender sqlAppender,
|
||||||
|
Expression array,
|
||||||
|
BasicPluralType<?, ?> pluralType,
|
||||||
|
@Nullable SqlTypedMapping sqlTypedMapping,
|
||||||
|
AnonymousTupleTableGroupProducer tupleType,
|
||||||
|
String tableIdentifierVariable,
|
||||||
|
SqlAstTranslator<?> walker) {
|
||||||
|
final XmlHelper.CollectionTags collectionTags = XmlHelper.determineCollectionTags(
|
||||||
|
(BasicPluralJavaType<?>) pluralType.getJavaTypeDescriptor(), walker.getSessionFactory()
|
||||||
|
);
|
||||||
|
|
||||||
|
sqlAppender.appendSql( "xmltable('/" );
|
||||||
|
sqlAppender.appendSql( collectionTags.rootName() );
|
||||||
|
sqlAppender.appendSql( '/' );
|
||||||
|
sqlAppender.appendSql( collectionTags.elementName() );
|
||||||
|
sqlAppender.appendSql( "' passing " );
|
||||||
|
array.accept( walker );
|
||||||
|
sqlAppender.appendSql( " columns" );
|
||||||
|
char separator = ' ';
|
||||||
|
final int offset;
|
||||||
|
if ( array instanceof TableColumnReferenceExpression expression ) {
|
||||||
|
offset = expression.getIdColumns().size();
|
||||||
|
for ( ColumnInfo columnInfo : expression.getIdColumns() ) {
|
||||||
|
sqlAppender.appendSql( separator );
|
||||||
|
sqlAppender.appendSql( columnInfo.name() );
|
||||||
|
sqlAppender.appendSql( ' ' );
|
||||||
|
sqlAppender.appendSql( columnInfo.ddlType() );
|
||||||
|
sqlAppender.appendSql( " path 'ancestor::" );
|
||||||
|
sqlAppender.appendSql( collectionTags.rootName() );
|
||||||
|
sqlAppender.appendSql( "/@" );
|
||||||
|
sqlAppender.appendSql( columnInfo.name() );
|
||||||
|
sqlAppender.appendSql( '\'' );
|
||||||
|
separator = ',';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
offset = 0;
|
||||||
|
}
|
||||||
|
if ( tupleType.findSubPart( CollectionPart.Nature.ELEMENT.getName(), null ) == null ) {
|
||||||
|
tupleType.forEachSelectable( offset, (selectionIndex, selectableMapping) -> {
|
||||||
|
if ( selectionIndex == 0 ) {
|
||||||
|
sqlAppender.append( ' ' );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
sqlAppender.append( ',' );
|
||||||
|
}
|
||||||
|
sqlAppender.append( selectableMapping.getSelectionExpression() );
|
||||||
|
if ( CollectionPart.Nature.INDEX.getName().equals( selectableMapping.getSelectableName() ) ) {
|
||||||
|
sqlAppender.append( " for ordinality" );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
sqlAppender.append( ' ' );
|
||||||
|
sqlAppender.append( getDdlType( selectableMapping, walker ) );
|
||||||
|
sqlAppender.appendSql( " path '" );
|
||||||
|
sqlAppender.appendSql( selectableMapping.getSelectableName() );
|
||||||
|
sqlAppender.appendSql( "'" );
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
tupleType.forEachSelectable( offset, (selectionIndex, selectableMapping) -> {
|
||||||
|
if ( selectionIndex == 0 ) {
|
||||||
|
sqlAppender.append( ' ' );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
sqlAppender.append( ',' );
|
||||||
|
}
|
||||||
|
sqlAppender.append( selectableMapping.getSelectionExpression() );
|
||||||
|
if ( CollectionPart.Nature.INDEX.getName().equals( selectableMapping.getSelectableName() ) ) {
|
||||||
|
sqlAppender.append( " for ordinality" );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
sqlAppender.append( ' ' );
|
||||||
|
sqlAppender.append( getDdlType( selectableMapping, walker ) );
|
||||||
|
sqlAppender.appendSql( " path '" );
|
||||||
|
sqlAppender.appendSql( "." );
|
||||||
|
sqlAppender.appendSql( "'" );
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
sqlAppender.appendSql( ')' );
|
||||||
|
}
|
||||||
|
|
||||||
|
static class XmlWrapperExpression implements SelfRenderingExpression {
|
||||||
|
private final List<ColumnInfo> idColumns;
|
||||||
|
private final String tableQualifier;
|
||||||
|
private final Expression argument;
|
||||||
|
|
||||||
|
public XmlWrapperExpression(List<ColumnInfo> idColumns, String tableQualifier, Expression argument) {
|
||||||
|
this.idColumns = idColumns;
|
||||||
|
this.tableQualifier = tableQualifier;
|
||||||
|
this.argument = argument;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void renderToSql(
|
||||||
|
SqlAppender sqlAppender,
|
||||||
|
SqlAstTranslator<?> walker,
|
||||||
|
SessionFactoryImplementor sessionFactory) {
|
||||||
|
final BasicPluralType<?, ?> pluralType = (BasicPluralType<?, ?>) argument.getExpressionType().getSingleJdbcMapping();
|
||||||
|
final XmlHelper.CollectionTags collectionTags = XmlHelper.determineCollectionTags(
|
||||||
|
(BasicPluralJavaType<?>) pluralType.getJavaTypeDescriptor(),
|
||||||
|
sessionFactory
|
||||||
|
);
|
||||||
|
|
||||||
|
// Produce a XML string e.g. <root id="1">...</root>
|
||||||
|
// which will contain the original XML as well as id column information for correlation
|
||||||
|
sqlAppender.appendSql( "trim('/>' from (select" );
|
||||||
|
char separator = ' ';
|
||||||
|
for ( ColumnInfo columnInfo : idColumns ) {
|
||||||
|
sqlAppender.appendSql( separator );
|
||||||
|
sqlAppender.appendSql( tableQualifier );
|
||||||
|
sqlAppender.appendSql( '.' );
|
||||||
|
sqlAppender.appendSql( columnInfo.name() );
|
||||||
|
sqlAppender.appendSql( ' ' );
|
||||||
|
sqlAppender.appendDoubleQuoteEscapedString( columnInfo.name() );
|
||||||
|
separator = ',';
|
||||||
|
}
|
||||||
|
sqlAppender.appendSql( " from sys.dummy for xml('root'='no','columnstyle'='attribute','rowname'='Strings','format'='no')))||" );
|
||||||
|
sqlAppender.appendSql( "substring(" );
|
||||||
|
argument.accept( walker );
|
||||||
|
sqlAppender.appendSql( ",locate('<" );
|
||||||
|
sqlAppender.appendSql( collectionTags.rootName() );
|
||||||
|
sqlAppender.appendSql( ">'," );
|
||||||
|
argument.accept( walker );
|
||||||
|
sqlAppender.appendSql( ")+" );
|
||||||
|
sqlAppender.appendSql( collectionTags.rootName().length() + 2 );
|
||||||
|
sqlAppender.appendSql( ",length(" );
|
||||||
|
argument.accept( walker );
|
||||||
|
sqlAppender.appendSql( "))" );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JdbcMappingContainer getExpressionType() {
|
||||||
|
return argument.getExpressionType();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void renderJsonTable(
|
||||||
|
SqlAppender sqlAppender,
|
||||||
|
Expression array,
|
||||||
|
BasicPluralType<?, ?> pluralType,
|
||||||
|
@Nullable SqlTypedMapping sqlTypedMapping,
|
||||||
|
AnonymousTupleTableGroupProducer tupleType,
|
||||||
|
String tableIdentifierVariable,
|
||||||
|
SqlAstTranslator<?> walker) {
|
||||||
|
final BasicType<?> elementType = pluralType.getElementType();
|
||||||
|
final String columnType = walker.getSessionFactory().getTypeConfiguration().getDdlTypeRegistry().getTypeName(
|
||||||
|
elementType.getJdbcType().getDdlTypeCode(),
|
||||||
|
sqlTypedMapping == null ? Size.nil() : sqlTypedMapping.toSize(),
|
||||||
|
elementType
|
||||||
|
);
|
||||||
|
sqlAppender.appendSql( "json_table(" );
|
||||||
|
array.accept( walker );
|
||||||
|
|
||||||
|
if ( array instanceof TableColumnReferenceExpression expression ) {
|
||||||
|
sqlAppender.appendSql( ",'$' columns(" );
|
||||||
|
for ( ColumnInfo columnInfo : expression.getIdColumns() ) {
|
||||||
|
sqlAppender.appendSql( columnInfo.name() );
|
||||||
|
sqlAppender.appendSql( ' ' );
|
||||||
|
sqlAppender.appendSql( columnInfo.ddlType() );
|
||||||
|
sqlAppender.appendSql( " path '$." );
|
||||||
|
sqlAppender.appendSql( columnInfo.name() );
|
||||||
|
sqlAppender.appendSql( "'," );
|
||||||
|
}
|
||||||
|
|
||||||
|
sqlAppender.appendSql( "nested path '$.v' columns (" );
|
||||||
|
sqlAppender.append( tupleType.getColumnNames().get( 0 ) );
|
||||||
|
sqlAppender.appendSql( ' ' );
|
||||||
|
sqlAppender.append( columnType );
|
||||||
|
sqlAppender.appendSql( " path '$')))" );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
sqlAppender.appendSql( ",'$[*]' columns(" );
|
||||||
|
sqlAppender.append( tupleType.getColumnNames().get( 0 ) );
|
||||||
|
sqlAppender.appendSql( ' ' );
|
||||||
|
sqlAppender.append( columnType );
|
||||||
|
sqlAppender.appendSql( " path '$'))" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class JsonWrapperExpression implements SelfRenderingExpression {
|
||||||
|
private final List<ColumnInfo> idColumns;
|
||||||
|
private final String tableQualifier;
|
||||||
|
private final Expression argument;
|
||||||
|
|
||||||
|
public JsonWrapperExpression(List<ColumnInfo> idColumns, String tableQualifier, Expression argument) {
|
||||||
|
this.idColumns = idColumns;
|
||||||
|
this.tableQualifier = tableQualifier;
|
||||||
|
this.argument = argument;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void renderToSql(
|
||||||
|
SqlAppender sqlAppender,
|
||||||
|
SqlAstTranslator<?> walker,
|
||||||
|
SessionFactoryImplementor sessionFactory) {
|
||||||
|
// Produce a JSON string e.g. {"id":1,"v":[...]}
|
||||||
|
// which will contain the original JSON as well as id column information for correlation
|
||||||
|
sqlAppender.appendSql( "'{'||trim('{}' from (select" );
|
||||||
|
char separator = ' ';
|
||||||
|
for ( ColumnInfo columnInfo : idColumns ) {
|
||||||
|
sqlAppender.appendSql( separator );
|
||||||
|
sqlAppender.appendSql( tableQualifier );
|
||||||
|
sqlAppender.appendSql( '.' );
|
||||||
|
sqlAppender.appendSql( columnInfo.name() );
|
||||||
|
sqlAppender.appendSql( ' ' );
|
||||||
|
sqlAppender.appendDoubleQuoteEscapedString( columnInfo.name() );
|
||||||
|
separator = ',';
|
||||||
|
}
|
||||||
|
sqlAppender.appendSql( " from sys.dummy for json('arraywrap'='no')))||" );
|
||||||
|
sqlAppender.appendSql( "'\"v\":'||" );
|
||||||
|
argument.accept( walker );
|
||||||
|
sqlAppender.appendSql( "||'}'" );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JdbcMappingContainer getExpressionType() {
|
||||||
|
return argument.getExpressionType();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,50 @@
|
||||||
|
/*
|
||||||
|
* SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
|
* Copyright Red Hat Inc. and Hibernate Authors
|
||||||
|
*/
|
||||||
|
package org.hibernate.dialect.function.array;
|
||||||
|
|
||||||
|
import org.hibernate.metamodel.mapping.CollectionPart;
|
||||||
|
import org.hibernate.metamodel.mapping.ModelPart;
|
||||||
|
import org.hibernate.metamodel.mapping.SqlTypedMapping;
|
||||||
|
import org.hibernate.query.derived.AnonymousTupleTableGroupProducer;
|
||||||
|
import org.hibernate.sql.ast.SqlAstTranslator;
|
||||||
|
import org.hibernate.sql.ast.spi.SqlAppender;
|
||||||
|
import org.hibernate.sql.ast.tree.expression.Expression;
|
||||||
|
import org.hibernate.type.BasicPluralType;
|
||||||
|
|
||||||
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Oracle unnest function.
|
||||||
|
*/
|
||||||
|
public class OracleUnnestFunction extends UnnestFunction {
|
||||||
|
|
||||||
|
public OracleUnnestFunction() {
|
||||||
|
super( "column_value", "i" );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void renderUnnest(
|
||||||
|
SqlAppender sqlAppender,
|
||||||
|
Expression array,
|
||||||
|
BasicPluralType<?, ?> pluralType,
|
||||||
|
@Nullable SqlTypedMapping sqlTypedMapping,
|
||||||
|
AnonymousTupleTableGroupProducer tupleType,
|
||||||
|
String tableIdentifierVariable,
|
||||||
|
SqlAstTranslator<?> walker) {
|
||||||
|
final ModelPart ordinalitySubPart = tupleType.findSubPart( CollectionPart.Nature.INDEX.getName(), null );
|
||||||
|
final boolean withOrdinality = ordinalitySubPart != null;
|
||||||
|
if ( withOrdinality ) {
|
||||||
|
sqlAppender.appendSql( "lateral (select t.*, rownum " );
|
||||||
|
sqlAppender.appendSql( ordinalitySubPart.asBasicValuedModelPart().getSelectionExpression() );
|
||||||
|
sqlAppender.appendSql( " from " );
|
||||||
|
}
|
||||||
|
sqlAppender.appendSql( "table(" );
|
||||||
|
array.accept( walker );
|
||||||
|
sqlAppender.appendSql( ")" );
|
||||||
|
if ( withOrdinality ) {
|
||||||
|
sqlAppender.appendSql( " t)" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,71 @@
|
||||||
|
/*
|
||||||
|
* SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
|
* Copyright Red Hat Inc. and Hibernate Authors
|
||||||
|
*/
|
||||||
|
package org.hibernate.dialect.function.array;
|
||||||
|
|
||||||
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
|
import org.hibernate.dialect.aggregate.AggregateSupport;
|
||||||
|
import org.hibernate.metamodel.mapping.CollectionPart;
|
||||||
|
import org.hibernate.metamodel.mapping.SqlTypedMapping;
|
||||||
|
import org.hibernate.query.derived.AnonymousTupleTableGroupProducer;
|
||||||
|
import org.hibernate.sql.ast.SqlAstTranslator;
|
||||||
|
import org.hibernate.sql.ast.spi.SqlAppender;
|
||||||
|
import org.hibernate.sql.ast.tree.expression.Expression;
|
||||||
|
import org.hibernate.type.BasicPluralType;
|
||||||
|
import org.hibernate.type.SqlTypes;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PostgreSQL unnest function.
|
||||||
|
*/
|
||||||
|
public class PostgreSQLUnnestFunction extends UnnestFunction {
|
||||||
|
|
||||||
|
public PostgreSQLUnnestFunction() {
|
||||||
|
super( null, "ordinality" );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void renderJsonTable(
|
||||||
|
SqlAppender sqlAppender,
|
||||||
|
Expression array,
|
||||||
|
BasicPluralType<?, ?> pluralType,
|
||||||
|
@Nullable SqlTypedMapping sqlTypedMapping,
|
||||||
|
AnonymousTupleTableGroupProducer tupleType,
|
||||||
|
String tableIdentifierVariable,
|
||||||
|
SqlAstTranslator<?> walker) {
|
||||||
|
final AggregateSupport aggregateSupport = walker.getSessionFactory().getJdbcServices().getDialect()
|
||||||
|
.getAggregateSupport();
|
||||||
|
sqlAppender.appendSql( "(select" );
|
||||||
|
tupleType.forEachSelectable( 0, (selectionIndex, selectableMapping) -> {
|
||||||
|
if ( selectionIndex == 0 ) {
|
||||||
|
sqlAppender.append( ' ' );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
sqlAppender.append( ',' );
|
||||||
|
}
|
||||||
|
if ( CollectionPart.Nature.INDEX.getName().equals( selectableMapping.getSelectableName() ) ) {
|
||||||
|
sqlAppender.appendSql( "t.ordinality" );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
sqlAppender.append( aggregateSupport.aggregateComponentCustomReadExpression(
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
"t.value",
|
||||||
|
selectableMapping.getSelectableName(),
|
||||||
|
SqlTypes.JSON,
|
||||||
|
selectableMapping
|
||||||
|
) );
|
||||||
|
}
|
||||||
|
sqlAppender.append( ' ' );
|
||||||
|
sqlAppender.append( selectableMapping.getSelectionExpression() );
|
||||||
|
} );
|
||||||
|
sqlAppender.appendSql( " from jsonb_array_elements(" );
|
||||||
|
array.accept( walker );
|
||||||
|
sqlAppender.appendSql( ')' );
|
||||||
|
if ( tupleType.findSubPart( CollectionPart.Nature.INDEX.getName(), null ) != null ) {
|
||||||
|
sqlAppender.appendSql( " with ordinality" );
|
||||||
|
}
|
||||||
|
sqlAppender.appendSql( " t)" );
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,162 @@
|
||||||
|
/*
|
||||||
|
* SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
|
* Copyright Red Hat Inc. and Hibernate Authors
|
||||||
|
*/
|
||||||
|
package org.hibernate.dialect.function.array;
|
||||||
|
|
||||||
|
|
||||||
|
import org.hibernate.dialect.XmlHelper;
|
||||||
|
import org.hibernate.metamodel.mapping.CollectionPart;
|
||||||
|
import org.hibernate.metamodel.mapping.SqlTypedMapping;
|
||||||
|
import org.hibernate.query.derived.AnonymousTupleTableGroupProducer;
|
||||||
|
import org.hibernate.sql.ast.SqlAstTranslator;
|
||||||
|
import org.hibernate.sql.ast.spi.SqlAppender;
|
||||||
|
import org.hibernate.sql.ast.tree.expression.Expression;
|
||||||
|
import org.hibernate.type.BasicPluralType;
|
||||||
|
import org.hibernate.type.descriptor.java.BasicPluralJavaType;
|
||||||
|
|
||||||
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SQL Server unnest function.
|
||||||
|
*/
|
||||||
|
public class SQLServerUnnestFunction extends UnnestFunction {
|
||||||
|
|
||||||
|
public SQLServerUnnestFunction() {
|
||||||
|
super( "v", "i" );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void renderJsonTable(
|
||||||
|
SqlAppender sqlAppender,
|
||||||
|
Expression array,
|
||||||
|
BasicPluralType<?, ?> pluralType,
|
||||||
|
@Nullable SqlTypedMapping sqlTypedMapping,
|
||||||
|
AnonymousTupleTableGroupProducer tupleType,
|
||||||
|
String tableIdentifierVariable,
|
||||||
|
SqlAstTranslator<?> walker) {
|
||||||
|
sqlAppender.appendSql( "openjson(" );
|
||||||
|
array.accept( walker );
|
||||||
|
sqlAppender.appendSql( ",'$[*]') with (" );
|
||||||
|
|
||||||
|
if ( tupleType.findSubPart( CollectionPart.Nature.ELEMENT.getName(), null ) == null ) {
|
||||||
|
tupleType.forEachSelectable( 0, (selectionIndex, selectableMapping) -> {
|
||||||
|
if ( selectionIndex == 0 ) {
|
||||||
|
sqlAppender.append( ' ' );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
sqlAppender.append( ',' );
|
||||||
|
}
|
||||||
|
if ( CollectionPart.Nature.INDEX.getName().equals( selectableMapping.getSelectableName() ) ) {
|
||||||
|
sqlAppender.append( selectableMapping.getSelectionExpression() );
|
||||||
|
sqlAppender.append( " for ordinality" );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
sqlAppender.append( selectableMapping.getSelectionExpression() );
|
||||||
|
sqlAppender.append( ' ' );
|
||||||
|
sqlAppender.append( getDdlType( selectableMapping, walker ) );
|
||||||
|
sqlAppender.appendSql( " path '$." );
|
||||||
|
sqlAppender.append( selectableMapping.getSelectableName() );
|
||||||
|
sqlAppender.appendSql( '\'' );
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
tupleType.forEachSelectable( 0, (selectionIndex, selectableMapping) -> {
|
||||||
|
if ( selectionIndex == 0 ) {
|
||||||
|
sqlAppender.append( ' ' );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
sqlAppender.append( ',' );
|
||||||
|
}
|
||||||
|
if ( CollectionPart.Nature.INDEX.getName().equals( selectableMapping.getSelectableName() ) ) {
|
||||||
|
sqlAppender.append( selectableMapping.getSelectionExpression() );
|
||||||
|
sqlAppender.append( " for ordinality" );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
sqlAppender.append( selectableMapping.getSelectionExpression() );
|
||||||
|
sqlAppender.append( ' ' );
|
||||||
|
sqlAppender.append( getDdlType( selectableMapping, walker ) );
|
||||||
|
sqlAppender.appendSql( " path '$'" );
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
sqlAppender.appendSql( ')' );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void renderXmlTable(
|
||||||
|
SqlAppender sqlAppender,
|
||||||
|
Expression array,
|
||||||
|
BasicPluralType<?, ?> pluralType,
|
||||||
|
@Nullable SqlTypedMapping sqlTypedMapping,
|
||||||
|
AnonymousTupleTableGroupProducer tupleType,
|
||||||
|
String tableIdentifierVariable,
|
||||||
|
SqlAstTranslator<?> walker) {
|
||||||
|
final XmlHelper.CollectionTags collectionTags = XmlHelper.determineCollectionTags(
|
||||||
|
(BasicPluralJavaType<?>) pluralType.getJavaTypeDescriptor(), walker.getSessionFactory()
|
||||||
|
);
|
||||||
|
|
||||||
|
sqlAppender.appendSql( "(select" );
|
||||||
|
|
||||||
|
if ( tupleType.findSubPart( CollectionPart.Nature.ELEMENT.getName(), null ) == null ) {
|
||||||
|
tupleType.forEachSelectable( 0, (selectionIndex, selectableMapping) -> {
|
||||||
|
if ( selectionIndex == 0 ) {
|
||||||
|
sqlAppender.append( ' ' );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
sqlAppender.append( ',' );
|
||||||
|
}
|
||||||
|
if ( CollectionPart.Nature.INDEX.getName().equals( selectableMapping.getSelectableName() ) ) {
|
||||||
|
sqlAppender.appendSql( "t.v.value('count(for $a in . return $a/../" );
|
||||||
|
sqlAppender.appendSql( collectionTags.elementName() );
|
||||||
|
sqlAppender.appendSql( "[.<<$a])+1','" );
|
||||||
|
sqlAppender.append( getDdlType( selectableMapping, walker ) );
|
||||||
|
sqlAppender.appendSql( "') " );
|
||||||
|
sqlAppender.appendSql( selectableMapping.getSelectionExpression() );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
sqlAppender.appendSql( "t.v.value('");
|
||||||
|
sqlAppender.appendSql( selectableMapping.getSelectableName() );
|
||||||
|
sqlAppender.appendSql( "/text()[1]','" );
|
||||||
|
sqlAppender.append( getDdlType( selectableMapping, walker ) );
|
||||||
|
sqlAppender.appendSql( "') " );
|
||||||
|
sqlAppender.appendSql( selectableMapping.getSelectionExpression() );
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
tupleType.forEachSelectable( 0, (selectionIndex, selectableMapping) -> {
|
||||||
|
if ( selectionIndex == 0 ) {
|
||||||
|
sqlAppender.append( ' ' );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
sqlAppender.append( ',' );
|
||||||
|
}
|
||||||
|
if ( CollectionPart.Nature.INDEX.getName().equals( selectableMapping.getSelectableName() ) ) {
|
||||||
|
sqlAppender.appendSql( "t.v.value('count(for $a in . return $a/../" );
|
||||||
|
sqlAppender.appendSql( collectionTags.elementName() );
|
||||||
|
sqlAppender.appendSql( "[.<<$a])+1','" );
|
||||||
|
sqlAppender.append( getDdlType( selectableMapping, walker ) );
|
||||||
|
sqlAppender.appendSql( "') " );
|
||||||
|
sqlAppender.appendSql( selectableMapping.getSelectionExpression() );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
sqlAppender.appendSql( "t.v.value('text()[1]','" );
|
||||||
|
sqlAppender.append( getDdlType( selectableMapping, walker ) );
|
||||||
|
sqlAppender.appendSql( "') " );
|
||||||
|
sqlAppender.appendSql( selectableMapping.getSelectionExpression() );
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
sqlAppender.appendSql( " from " );
|
||||||
|
array.accept( walker );
|
||||||
|
sqlAppender.appendSql( ".nodes('/" );
|
||||||
|
sqlAppender.appendSql( collectionTags.rootName() );
|
||||||
|
sqlAppender.appendSql( '/' );
|
||||||
|
sqlAppender.appendSql( collectionTags.elementName() );
|
||||||
|
sqlAppender.appendSql( "') t(v))" );
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,93 @@
|
||||||
|
/*
|
||||||
|
* SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
|
* Copyright Red Hat Inc. and Hibernate Authors
|
||||||
|
*/
|
||||||
|
package org.hibernate.dialect.function.array;
|
||||||
|
|
||||||
|
import org.hibernate.dialect.XmlHelper;
|
||||||
|
import org.hibernate.metamodel.mapping.CollectionPart;
|
||||||
|
import org.hibernate.metamodel.mapping.SqlTypedMapping;
|
||||||
|
import org.hibernate.query.derived.AnonymousTupleTableGroupProducer;
|
||||||
|
import org.hibernate.sql.ast.SqlAstTranslator;
|
||||||
|
import org.hibernate.sql.ast.spi.SqlAppender;
|
||||||
|
import org.hibernate.sql.ast.tree.expression.Expression;
|
||||||
|
import org.hibernate.type.BasicPluralType;
|
||||||
|
import org.hibernate.type.descriptor.java.BasicPluralJavaType;
|
||||||
|
|
||||||
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sybase ASE unnest function.
|
||||||
|
*/
|
||||||
|
public class SybaseASEUnnestFunction extends UnnestFunction {
|
||||||
|
|
||||||
|
public SybaseASEUnnestFunction() {
|
||||||
|
super( "v", "i" );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void renderXmlTable(
|
||||||
|
SqlAppender sqlAppender,
|
||||||
|
Expression array,
|
||||||
|
BasicPluralType<?, ?> pluralType,
|
||||||
|
@Nullable SqlTypedMapping sqlTypedMapping,
|
||||||
|
AnonymousTupleTableGroupProducer tupleType,
|
||||||
|
String tableIdentifierVariable,
|
||||||
|
SqlAstTranslator<?> walker) {
|
||||||
|
final XmlHelper.CollectionTags collectionTags = XmlHelper.determineCollectionTags(
|
||||||
|
(BasicPluralJavaType<?>) pluralType.getJavaTypeDescriptor(), walker.getSessionFactory()
|
||||||
|
);
|
||||||
|
sqlAppender.appendSql( "xmltable('/" );
|
||||||
|
sqlAppender.appendSql( collectionTags.rootName() );
|
||||||
|
sqlAppender.appendSql( '/' );
|
||||||
|
sqlAppender.appendSql( collectionTags.elementName() );
|
||||||
|
sqlAppender.appendSql( "' passing " );
|
||||||
|
array.accept( walker );
|
||||||
|
sqlAppender.appendSql( " columns" );
|
||||||
|
|
||||||
|
if ( tupleType.findSubPart( CollectionPart.Nature.ELEMENT.getName(), null ) == null ) {
|
||||||
|
tupleType.forEachSelectable( 0, (selectionIndex, selectableMapping) -> {
|
||||||
|
if ( selectionIndex == 0 ) {
|
||||||
|
sqlAppender.append( ' ' );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
sqlAppender.append( ',' );
|
||||||
|
}
|
||||||
|
sqlAppender.append( selectableMapping.getSelectionExpression() );
|
||||||
|
if ( CollectionPart.Nature.INDEX.getName().equals( selectableMapping.getSelectableName() ) ) {
|
||||||
|
sqlAppender.append( " bigint for ordinality" );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
sqlAppender.append( ' ' );
|
||||||
|
sqlAppender.append( getDdlType( selectableMapping, walker ) );
|
||||||
|
sqlAppender.appendSql( " path '" );
|
||||||
|
sqlAppender.appendSql( selectableMapping.getSelectableName() );
|
||||||
|
sqlAppender.appendSql( "'" );
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
tupleType.forEachSelectable( 0, (selectionIndex, selectableMapping) -> {
|
||||||
|
if ( selectionIndex == 0 ) {
|
||||||
|
sqlAppender.append( ' ' );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
sqlAppender.append( ',' );
|
||||||
|
}
|
||||||
|
sqlAppender.append( selectableMapping.getSelectionExpression() );
|
||||||
|
if ( CollectionPart.Nature.INDEX.getName().equals( selectableMapping.getSelectableName() ) ) {
|
||||||
|
sqlAppender.append( " bigint for ordinality" );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
sqlAppender.append( ' ' );
|
||||||
|
sqlAppender.append( getDdlType( selectableMapping, walker ) );
|
||||||
|
sqlAppender.appendSql( " path '" );
|
||||||
|
sqlAppender.appendSql( "." );
|
||||||
|
sqlAppender.appendSql( "'" );
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
sqlAppender.appendSql( ')' );
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,216 @@
|
||||||
|
/*
|
||||||
|
* SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
|
* Copyright Red Hat Inc. and Hibernate Authors
|
||||||
|
*/
|
||||||
|
package org.hibernate.dialect.function.array;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.hibernate.dialect.XmlHelper;
|
||||||
|
import org.hibernate.dialect.function.UnnestSetReturningFunctionTypeResolver;
|
||||||
|
import org.hibernate.metamodel.mapping.CollectionPart;
|
||||||
|
import org.hibernate.metamodel.mapping.SqlTypedMapping;
|
||||||
|
import org.hibernate.query.derived.AnonymousTupleTableGroupProducer;
|
||||||
|
import org.hibernate.query.sqm.function.AbstractSqmSelfRenderingSetReturningFunctionDescriptor;
|
||||||
|
import org.hibernate.query.sqm.produce.function.SetReturningFunctionTypeResolver;
|
||||||
|
import org.hibernate.sql.ast.SqlAstTranslator;
|
||||||
|
import org.hibernate.sql.ast.spi.SqlAppender;
|
||||||
|
import org.hibernate.sql.ast.tree.SqlAstNode;
|
||||||
|
import org.hibernate.sql.ast.tree.expression.Expression;
|
||||||
|
import org.hibernate.type.BasicPluralType;
|
||||||
|
import org.hibernate.type.SqlTypes;
|
||||||
|
import org.hibernate.type.Type;
|
||||||
|
import org.hibernate.type.descriptor.java.BasicPluralJavaType;
|
||||||
|
|
||||||
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Standard unnest function.
|
||||||
|
*/
|
||||||
|
public class UnnestFunction extends AbstractSqmSelfRenderingSetReturningFunctionDescriptor {
|
||||||
|
|
||||||
|
public UnnestFunction(@Nullable String defaultBasicArrayColumnName, String defaultIndexSelectionExpression) {
|
||||||
|
this( new UnnestSetReturningFunctionTypeResolver( defaultBasicArrayColumnName, defaultIndexSelectionExpression ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
protected UnnestFunction(SetReturningFunctionTypeResolver setReturningFunctionTypeResolver) {
|
||||||
|
super(
|
||||||
|
"unnest",
|
||||||
|
null,
|
||||||
|
setReturningFunctionTypeResolver,
|
||||||
|
null
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void render(
|
||||||
|
SqlAppender sqlAppender,
|
||||||
|
List<? extends SqlAstNode> sqlAstArguments,
|
||||||
|
AnonymousTupleTableGroupProducer tupleType,
|
||||||
|
String tableIdentifierVariable,
|
||||||
|
SqlAstTranslator<?> walker) {
|
||||||
|
final Expression array = (Expression) sqlAstArguments.get( 0 );
|
||||||
|
final @Nullable SqlTypedMapping sqlTypedMapping = array.getExpressionType() instanceof SqlTypedMapping
|
||||||
|
? (SqlTypedMapping) array.getExpressionType()
|
||||||
|
: null;
|
||||||
|
final BasicPluralType<?, ?> pluralType = (BasicPluralType<?, ?>) array.getExpressionType().getSingleJdbcMapping();
|
||||||
|
final int ddlTypeCode = pluralType.getJdbcType().getDefaultSqlTypeCode();
|
||||||
|
if ( ddlTypeCode == SqlTypes.JSON_ARRAY ) {
|
||||||
|
renderJsonTable( sqlAppender, array, pluralType, sqlTypedMapping, tupleType, tableIdentifierVariable, walker );
|
||||||
|
}
|
||||||
|
else if ( ddlTypeCode == SqlTypes.XML_ARRAY ) {
|
||||||
|
renderXmlTable( sqlAppender, array, pluralType, sqlTypedMapping, tupleType, tableIdentifierVariable, walker );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
renderUnnest( sqlAppender, array, pluralType, sqlTypedMapping, tupleType, tableIdentifierVariable, walker );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String getDdlType(SqlTypedMapping sqlTypedMapping, SqlAstTranslator<?> translator) {
|
||||||
|
final String columnDefinition = sqlTypedMapping.getColumnDefinition();
|
||||||
|
if ( columnDefinition != null ) {
|
||||||
|
return columnDefinition;
|
||||||
|
}
|
||||||
|
return translator.getSessionFactory().getTypeConfiguration().getDdlTypeRegistry().getTypeName(
|
||||||
|
sqlTypedMapping.getJdbcMapping().getJdbcType().getDdlTypeCode(),
|
||||||
|
sqlTypedMapping.toSize(),
|
||||||
|
(Type) sqlTypedMapping.getJdbcMapping()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void renderJsonTable(
|
||||||
|
SqlAppender sqlAppender,
|
||||||
|
Expression array,
|
||||||
|
BasicPluralType<?, ?> pluralType,
|
||||||
|
@Nullable SqlTypedMapping sqlTypedMapping,
|
||||||
|
AnonymousTupleTableGroupProducer tupleType,
|
||||||
|
String tableIdentifierVariable,
|
||||||
|
SqlAstTranslator<?> walker) {
|
||||||
|
sqlAppender.appendSql( "json_table(" );
|
||||||
|
array.accept( walker );
|
||||||
|
sqlAppender.appendSql( ",'$[*]' columns(" );
|
||||||
|
if ( tupleType.findSubPart( CollectionPart.Nature.ELEMENT.getName(), null ) == null ) {
|
||||||
|
tupleType.forEachSelectable( 0, (selectionIndex, selectableMapping) -> {
|
||||||
|
if ( selectionIndex == 0 ) {
|
||||||
|
sqlAppender.append( ' ' );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
sqlAppender.append( ',' );
|
||||||
|
}
|
||||||
|
sqlAppender.append( selectableMapping.getSelectionExpression() );
|
||||||
|
sqlAppender.append( ' ' );
|
||||||
|
if ( CollectionPart.Nature.INDEX.getName().equals( selectableMapping.getSelectableName() ) ) {
|
||||||
|
sqlAppender.append( " for ordinality" );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
sqlAppender.append( getDdlType( selectableMapping, walker ) );
|
||||||
|
sqlAppender.appendSql( " path '$." );
|
||||||
|
sqlAppender.append( selectableMapping.getSelectableName() );
|
||||||
|
sqlAppender.appendSql( '\'' );
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
tupleType.forEachSelectable( 0, (selectionIndex, selectableMapping) -> {
|
||||||
|
if ( selectionIndex == 0 ) {
|
||||||
|
sqlAppender.append( ' ' );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
sqlAppender.append( ',' );
|
||||||
|
}
|
||||||
|
sqlAppender.append( selectableMapping.getSelectionExpression() );
|
||||||
|
if ( CollectionPart.Nature.INDEX.getName().equals( selectableMapping.getSelectableName() ) ) {
|
||||||
|
sqlAppender.append( " for ordinality" );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
sqlAppender.append( ' ' );
|
||||||
|
sqlAppender.append( getDdlType( selectableMapping, walker ) );
|
||||||
|
sqlAppender.appendSql( " path '$'" );
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
sqlAppender.appendSql( "))" );
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void renderXmlTable(
|
||||||
|
SqlAppender sqlAppender,
|
||||||
|
Expression array,
|
||||||
|
BasicPluralType<?, ?> pluralType,
|
||||||
|
@Nullable SqlTypedMapping sqlTypedMapping,
|
||||||
|
AnonymousTupleTableGroupProducer tupleType,
|
||||||
|
String tableIdentifierVariable,
|
||||||
|
SqlAstTranslator<?> walker) {
|
||||||
|
final XmlHelper.CollectionTags collectionTags = XmlHelper.determineCollectionTags(
|
||||||
|
(BasicPluralJavaType<?>) pluralType.getJavaTypeDescriptor(), walker.getSessionFactory()
|
||||||
|
);
|
||||||
|
|
||||||
|
sqlAppender.appendSql( "xmltable('$d/" );
|
||||||
|
sqlAppender.appendSql( collectionTags.rootName() );
|
||||||
|
sqlAppender.appendSql( '/' );
|
||||||
|
sqlAppender.appendSql( collectionTags.elementName() );
|
||||||
|
sqlAppender.appendSql( "' passing " );
|
||||||
|
array.accept( walker );
|
||||||
|
sqlAppender.appendSql( " as \"d\" columns" );
|
||||||
|
if ( tupleType.findSubPart( CollectionPart.Nature.ELEMENT.getName(), null ) == null ) {
|
||||||
|
tupleType.forEachSelectable( 0, (selectionIndex, selectableMapping) -> {
|
||||||
|
if ( selectionIndex == 0 ) {
|
||||||
|
sqlAppender.append( ' ' );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
sqlAppender.append( ',' );
|
||||||
|
}
|
||||||
|
sqlAppender.append( selectableMapping.getSelectionExpression() );
|
||||||
|
if ( CollectionPart.Nature.INDEX.getName().equals( selectableMapping.getSelectableName() ) ) {
|
||||||
|
sqlAppender.append( " for ordinality" );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
sqlAppender.append( ' ' );
|
||||||
|
sqlAppender.append( getDdlType( selectableMapping, walker ) );
|
||||||
|
sqlAppender.appendSql( " path '" );
|
||||||
|
sqlAppender.appendSql( selectableMapping.getSelectableName() );
|
||||||
|
sqlAppender.appendSql( "/text()" );
|
||||||
|
sqlAppender.appendSql( "'" );
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
tupleType.forEachSelectable( 0, (selectionIndex, selectableMapping) -> {
|
||||||
|
if ( selectionIndex == 0 ) {
|
||||||
|
sqlAppender.append( ' ' );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
sqlAppender.append( ',' );
|
||||||
|
}
|
||||||
|
sqlAppender.append( selectableMapping.getSelectionExpression() );
|
||||||
|
if ( CollectionPart.Nature.INDEX.getName().equals( selectableMapping.getSelectableName() ) ) {
|
||||||
|
sqlAppender.append( " for ordinality" );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
sqlAppender.append( ' ' );
|
||||||
|
sqlAppender.append( getDdlType( selectableMapping, walker ) );
|
||||||
|
sqlAppender.appendSql( " path '" );
|
||||||
|
sqlAppender.appendSql( "text()" );
|
||||||
|
sqlAppender.appendSql( "'" );
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
sqlAppender.appendSql( ')' );
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void renderUnnest(
|
||||||
|
SqlAppender sqlAppender,
|
||||||
|
Expression array,
|
||||||
|
BasicPluralType<?, ?> pluralType,
|
||||||
|
@Nullable SqlTypedMapping sqlTypedMapping,
|
||||||
|
AnonymousTupleTableGroupProducer tupleType,
|
||||||
|
String tableIdentifierVariable,
|
||||||
|
SqlAstTranslator<?> walker) {
|
||||||
|
sqlAppender.appendSql( "unnest(" );
|
||||||
|
array.accept( walker );
|
||||||
|
sqlAppender.appendSql( ')' );
|
||||||
|
if ( tupleType.findSubPart( CollectionPart.Nature.INDEX.getName(), null ) != null ) {
|
||||||
|
sqlAppender.append( " with ordinality" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -38,7 +38,7 @@ public class JsonArrayAggFunction extends AbstractSqmSelfRenderingFunctionDescri
|
||||||
FunctionKind.ORDERED_SET_AGGREGATE,
|
FunctionKind.ORDERED_SET_AGGREGATE,
|
||||||
StandardArgumentsValidators.between( 1, 2 ),
|
StandardArgumentsValidators.between( 1, 2 ),
|
||||||
StandardFunctionReturnTypeResolvers.invariant(
|
StandardFunctionReturnTypeResolvers.invariant(
|
||||||
typeConfiguration.getBasicTypeRegistry().resolve( String.class, SqlTypes.JSON_ARRAY )
|
typeConfiguration.getBasicTypeRegistry().resolve( String.class, SqlTypes.JSON )
|
||||||
),
|
),
|
||||||
null
|
null
|
||||||
);
|
);
|
||||||
|
|
|
@ -28,7 +28,7 @@ public class JsonArrayFunction extends AbstractSqmSelfRenderingFunctionDescripto
|
||||||
FunctionKind.NORMAL,
|
FunctionKind.NORMAL,
|
||||||
null,
|
null,
|
||||||
StandardFunctionReturnTypeResolvers.invariant(
|
StandardFunctionReturnTypeResolvers.invariant(
|
||||||
typeConfiguration.getBasicTypeRegistry().resolve( String.class, SqlTypes.JSON_ARRAY )
|
typeConfiguration.getBasicTypeRegistry().resolve( String.class, SqlTypes.JSON )
|
||||||
),
|
),
|
||||||
null
|
null
|
||||||
);
|
);
|
||||||
|
|
|
@ -119,8 +119,10 @@ public class SQLServerXmlAggFunction extends XmlAggFunction {
|
||||||
),
|
),
|
||||||
alias,
|
alias,
|
||||||
List.of("v"),
|
List.of("v"),
|
||||||
|
Set.of(),
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
|
false,
|
||||||
null
|
null
|
||||||
);
|
);
|
||||||
tableGroup.addTableGroupJoin(
|
tableGroup.addTableGroupJoin(
|
||||||
|
|
|
@ -7,6 +7,7 @@ package org.hibernate.dialect.function.xml;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.hibernate.dialect.XmlHelper;
|
||||||
import org.hibernate.query.ReturnableType;
|
import org.hibernate.query.ReturnableType;
|
||||||
import org.hibernate.query.spi.QueryEngine;
|
import org.hibernate.query.spi.QueryEngine;
|
||||||
import org.hibernate.query.sqm.function.AbstractSqmSelfRenderingFunctionDescriptor;
|
import org.hibernate.query.sqm.function.AbstractSqmSelfRenderingFunctionDescriptor;
|
||||||
|
@ -33,8 +34,6 @@ import org.hibernate.type.spi.TypeConfiguration;
|
||||||
|
|
||||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
|
|
||||||
import static java.lang.Character.isLetter;
|
|
||||||
import static java.lang.Character.isLetterOrDigit;
|
|
||||||
import static org.hibernate.query.sqm.produce.function.FunctionParameterType.STRING;
|
import static org.hibernate.query.sqm.produce.function.FunctionParameterType.STRING;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -56,7 +55,7 @@ public class XmlElementFunction extends AbstractSqmSelfRenderingFunctionDescript
|
||||||
TypeConfiguration typeConfiguration) {
|
TypeConfiguration typeConfiguration) {
|
||||||
//noinspection unchecked
|
//noinspection unchecked
|
||||||
final String elementName = ( (SqmLiteral<String>) arguments.get( 0 ) ).getLiteralValue();
|
final String elementName = ( (SqmLiteral<String>) arguments.get( 0 ) ).getLiteralValue();
|
||||||
if ( !isValidXmlName( elementName ) ) {
|
if ( !XmlHelper.isValidXmlName( elementName ) ) {
|
||||||
throw new FunctionArgumentException(
|
throw new FunctionArgumentException(
|
||||||
String.format(
|
String.format(
|
||||||
"Invalid XML element name passed to 'xmlelement()': %s",
|
"Invalid XML element name passed to 'xmlelement()': %s",
|
||||||
|
@ -68,7 +67,7 @@ public class XmlElementFunction extends AbstractSqmSelfRenderingFunctionDescript
|
||||||
&& arguments.get( 1 ) instanceof SqmXmlAttributesExpression attributesExpression ) {
|
&& arguments.get( 1 ) instanceof SqmXmlAttributesExpression attributesExpression ) {
|
||||||
final Map<String, SqmExpression<?>> attributes = attributesExpression.getAttributes();
|
final Map<String, SqmExpression<?>> attributes = attributesExpression.getAttributes();
|
||||||
for ( Map.Entry<String, SqmExpression<?>> entry : attributes.entrySet() ) {
|
for ( Map.Entry<String, SqmExpression<?>> entry : attributes.entrySet() ) {
|
||||||
if ( !isValidXmlName( entry.getKey() ) ) {
|
if ( !XmlHelper.isValidXmlName( entry.getKey() ) ) {
|
||||||
throw new FunctionArgumentException(
|
throw new FunctionArgumentException(
|
||||||
String.format(
|
String.format(
|
||||||
"Invalid XML attribute name passed to 'xmlattributes()': %s",
|
"Invalid XML attribute name passed to 'xmlattributes()': %s",
|
||||||
|
@ -79,29 +78,6 @@ public class XmlElementFunction extends AbstractSqmSelfRenderingFunctionDescript
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean isValidXmlName(String name) {
|
|
||||||
if ( name.isEmpty()
|
|
||||||
|| !isValidXmlNameStart( name.charAt( 0 ) )
|
|
||||||
|| name.regionMatches( true, 0, "xml", 0, 3 ) ) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
for ( int i = 1; i < name.length(); i++ ) {
|
|
||||||
if ( !isValidXmlNameChar( name.charAt( i ) ) ) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean isValidXmlNameStart(char c) {
|
|
||||||
return isLetter( c ) || c == '_' || c == ':';
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean isValidXmlNameChar(char c) {
|
|
||||||
return isLetterOrDigit( c ) || c == '_' || c == ':' || c == '-' || c == '.';
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
StandardFunctionReturnTypeResolvers.invariant(
|
StandardFunctionReturnTypeResolvers.invariant(
|
||||||
|
|
|
@ -6,6 +6,7 @@ package org.hibernate.dialect.function.xml;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.hibernate.dialect.XmlHelper;
|
||||||
import org.hibernate.query.ReturnableType;
|
import org.hibernate.query.ReturnableType;
|
||||||
import org.hibernate.query.sqm.function.AbstractSqmSelfRenderingFunctionDescriptor;
|
import org.hibernate.query.sqm.function.AbstractSqmSelfRenderingFunctionDescriptor;
|
||||||
import org.hibernate.query.sqm.function.FunctionKind;
|
import org.hibernate.query.sqm.function.FunctionKind;
|
||||||
|
@ -22,9 +23,6 @@ import org.hibernate.sql.ast.tree.expression.AliasedExpression;
|
||||||
import org.hibernate.type.SqlTypes;
|
import org.hibernate.type.SqlTypes;
|
||||||
import org.hibernate.type.spi.TypeConfiguration;
|
import org.hibernate.type.spi.TypeConfiguration;
|
||||||
|
|
||||||
import static java.lang.Character.isLetter;
|
|
||||||
import static java.lang.Character.isLetterOrDigit;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Standard xmlforest function.
|
* Standard xmlforest function.
|
||||||
*/
|
*/
|
||||||
|
@ -52,7 +50,7 @@ public class XmlForestFunction extends AbstractSqmSelfRenderingFunctionDescripto
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if ( !isValidXmlName( namedExpression.getName() ) ) {
|
if ( !XmlHelper.isValidXmlName( namedExpression.getName() ) ) {
|
||||||
throw new FunctionArgumentException(
|
throw new FunctionArgumentException(
|
||||||
String.format(
|
String.format(
|
||||||
"Invalid XML element name passed to 'xmlforest()': %s",
|
"Invalid XML element name passed to 'xmlforest()': %s",
|
||||||
|
@ -63,28 +61,6 @@ public class XmlForestFunction extends AbstractSqmSelfRenderingFunctionDescripto
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean isValidXmlName(String name) {
|
|
||||||
if ( name.isEmpty()
|
|
||||||
|| !isValidXmlNameStart( name.charAt( 0 ) )
|
|
||||||
|| name.regionMatches( true, 0, "xml", 0, 3 ) ) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
for ( int i = 1; i < name.length(); i++ ) {
|
|
||||||
if ( !isValidXmlNameChar( name.charAt( i ) ) ) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean isValidXmlNameStart(char c) {
|
|
||||||
return isLetter( c ) || c == '_' || c == ':';
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean isValidXmlNameChar(char c) {
|
|
||||||
return isLetterOrDigit( c ) || c == '_' || c == ':' || c == '-' || c == '.';
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
StandardFunctionReturnTypeResolvers.invariant(
|
StandardFunctionReturnTypeResolvers.invariant(
|
||||||
|
|
|
@ -0,0 +1,68 @@
|
||||||
|
/*
|
||||||
|
* SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
|
* Copyright Red Hat Inc. and Hibernate Authors
|
||||||
|
*/
|
||||||
|
package org.hibernate.engine.spi;
|
||||||
|
|
||||||
|
import java.util.TimeZone;
|
||||||
|
|
||||||
|
import org.hibernate.Internal;
|
||||||
|
import org.hibernate.type.descriptor.WrapperOptions;
|
||||||
|
|
||||||
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A lazy session implementation that is needed for rendering literals.
|
||||||
|
* Usually, only the {@link WrapperOptions} interface is needed,
|
||||||
|
* but for creating LOBs, it might be to have a full-blown session.
|
||||||
|
*/
|
||||||
|
@Internal
|
||||||
|
public class LazySessionWrapperOptions extends AbstractDelegatingWrapperOptions {
|
||||||
|
|
||||||
|
private final SessionFactoryImplementor sessionFactory;
|
||||||
|
private @Nullable SessionImplementor session;
|
||||||
|
|
||||||
|
public LazySessionWrapperOptions(SessionFactoryImplementor sessionFactory) {
|
||||||
|
this.sessionFactory = sessionFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void cleanup() {
|
||||||
|
if ( session != null ) {
|
||||||
|
session.close();
|
||||||
|
session = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected SessionImplementor delegate() {
|
||||||
|
if ( session == null ) {
|
||||||
|
session = sessionFactory.openTemporarySession();
|
||||||
|
}
|
||||||
|
return session;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SharedSessionContractImplementor getSession() {
|
||||||
|
return delegate();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SessionFactoryImplementor getSessionFactory() {
|
||||||
|
return sessionFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean useStreamForLobBinding() {
|
||||||
|
return sessionFactory.getFastSessionServices().useStreamForLobBinding();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getPreferredSqlTypeCodeForBoolean() {
|
||||||
|
return sessionFactory.getFastSessionServices().getPreferredSqlTypeCodeForBoolean();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TimeZone getJdbcTimeZone() {
|
||||||
|
return sessionFactory.getSessionFactoryOptions().getJdbcTimeZone();
|
||||||
|
}
|
||||||
|
}
|
|
@ -1160,7 +1160,6 @@ public class BasicValue extends SimpleValue implements JdbcTypeIndicators, Resol
|
||||||
this.jdbcTypeCode = jdbcTypeCode;
|
this.jdbcTypeCode = jdbcTypeCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public Integer getExplicitJdbcTypeCode() {
|
public Integer getExplicitJdbcTypeCode() {
|
||||||
return jdbcTypeCode == null ? getPreferredSqlTypeCodeForArray() : jdbcTypeCode;
|
return jdbcTypeCode == null ? getPreferredSqlTypeCodeForArray() : jdbcTypeCode;
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,6 +32,8 @@ import org.hibernate.type.EntityType;
|
||||||
import org.hibernate.type.Type;
|
import org.hibernate.type.Type;
|
||||||
import org.hibernate.type.descriptor.JdbcTypeNameMapper;
|
import org.hibernate.type.descriptor.JdbcTypeNameMapper;
|
||||||
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
||||||
|
import org.hibernate.type.descriptor.jdbc.JdbcTypeConstructor;
|
||||||
|
import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry;
|
||||||
import org.hibernate.type.descriptor.sql.DdlType;
|
import org.hibernate.type.descriptor.sql.DdlType;
|
||||||
import org.hibernate.type.descriptor.sql.spi.DdlTypeRegistry;
|
import org.hibernate.type.descriptor.sql.spi.DdlTypeRegistry;
|
||||||
import org.hibernate.type.MappingContext;
|
import org.hibernate.type.MappingContext;
|
||||||
|
@ -116,6 +118,10 @@ public class Column implements Selectable, Serializable, Cloneable, ColumnTypeIn
|
||||||
this.value = value;
|
this.value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public JdbcMapping getType() {
|
||||||
|
return getValue().getSelectableType( getValue().getBuildingContext().getMetadataCollector(), getTypeIndex() );
|
||||||
|
}
|
||||||
|
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
@ -316,10 +322,22 @@ public class Column implements Selectable, Serializable, Cloneable, ColumnTypeIn
|
||||||
return sqlTypeCode;
|
return sqlTypeCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getSqlTypeName(DdlTypeRegistry ddlTypeRegistry, Dialect dialect, MappingContext mapping) {
|
private String getSqlTypeName(TypeConfiguration typeConfiguration, Dialect dialect, MappingContext mapping) {
|
||||||
if ( sqlTypeName == null ) {
|
if ( sqlTypeName == null ) {
|
||||||
final int typeCode = getSqlTypeCode( mapping );
|
final DdlTypeRegistry ddlTypeRegistry = typeConfiguration.getDdlTypeRegistry();
|
||||||
final DdlType descriptor = ddlTypeRegistry.getDescriptor( typeCode );
|
final JdbcTypeRegistry jdbcTypeRegistry = typeConfiguration.getJdbcTypeRegistry();
|
||||||
|
final int sqlTypeCode = getSqlTypeCode( mapping );
|
||||||
|
final JdbcTypeConstructor constructor = jdbcTypeRegistry.getConstructor( sqlTypeCode );
|
||||||
|
final JdbcType jdbcType;
|
||||||
|
if ( constructor == null ) {
|
||||||
|
jdbcType = jdbcTypeRegistry.findDescriptor( sqlTypeCode );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
jdbcType = ( (BasicType<?>) getUnderlyingType( mapping, getValue().getType(), typeIndex ) ).getJdbcType();
|
||||||
|
}
|
||||||
|
final DdlType descriptor = jdbcType == null
|
||||||
|
? null
|
||||||
|
: ddlTypeRegistry.getDescriptor( jdbcType.getDdlTypeCode() );
|
||||||
if ( descriptor == null ) {
|
if ( descriptor == null ) {
|
||||||
throw new MappingException(
|
throw new MappingException(
|
||||||
String.format(
|
String.format(
|
||||||
|
@ -327,8 +345,8 @@ public class Column implements Selectable, Serializable, Cloneable, ColumnTypeIn
|
||||||
"Unable to determine SQL type name for column '%s' of table '%s' because there is no type mapping for org.hibernate.type.SqlTypes code: %s (%s)",
|
"Unable to determine SQL type name for column '%s' of table '%s' because there is no type mapping for org.hibernate.type.SqlTypes code: %s (%s)",
|
||||||
getName(),
|
getName(),
|
||||||
getValue().getTable().getName(),
|
getValue().getTable().getName(),
|
||||||
typeCode,
|
sqlTypeCode,
|
||||||
JdbcTypeNameMapper.getTypeName( typeCode )
|
JdbcTypeNameMapper.getTypeName( sqlTypeCode )
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -400,7 +418,7 @@ public class Column implements Selectable, Serializable, Cloneable, ColumnTypeIn
|
||||||
|
|
||||||
public String getSqlType(Metadata mapping) {
|
public String getSqlType(Metadata mapping) {
|
||||||
final Database database = mapping.getDatabase();
|
final Database database = mapping.getDatabase();
|
||||||
return getSqlTypeName( database.getTypeConfiguration().getDdlTypeRegistry(), database.getDialect(), mapping );
|
return getSqlTypeName( database.getTypeConfiguration(), database.getDialect(), mapping );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -408,7 +426,7 @@ public class Column implements Selectable, Serializable, Cloneable, ColumnTypeIn
|
||||||
*/
|
*/
|
||||||
@Deprecated(since = "6.2")
|
@Deprecated(since = "6.2")
|
||||||
public String getSqlType(TypeConfiguration typeConfiguration, Dialect dialect, Mapping mapping) {
|
public String getSqlType(TypeConfiguration typeConfiguration, Dialect dialect, Mapping mapping) {
|
||||||
return getSqlTypeName( typeConfiguration.getDdlTypeRegistry(), dialect, mapping );
|
return getSqlTypeName( typeConfiguration, dialect, mapping );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -6,17 +6,19 @@ package org.hibernate.metamodel.mapping;
|
||||||
|
|
||||||
import org.hibernate.engine.jdbc.Size;
|
import org.hibernate.engine.jdbc.Size;
|
||||||
|
|
||||||
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Models the type of a thing that can be used as an expression in a SQL query
|
* Models the type of a thing that can be used as an expression in a SQL query
|
||||||
*
|
*
|
||||||
* @author Christian Beikov
|
* @author Christian Beikov
|
||||||
*/
|
*/
|
||||||
public interface SqlTypedMapping {
|
public interface SqlTypedMapping {
|
||||||
String getColumnDefinition();
|
@Nullable String getColumnDefinition();
|
||||||
Long getLength();
|
@Nullable Long getLength();
|
||||||
Integer getPrecision();
|
@Nullable Integer getPrecision();
|
||||||
Integer getScale();
|
@Nullable Integer getScale();
|
||||||
Integer getTemporalPrecision();
|
@Nullable Integer getTemporalPrecision();
|
||||||
default boolean isLob() {
|
default boolean isLob() {
|
||||||
return getJdbcMapping().getJdbcType().isLob();
|
return getJdbcMapping().getJdbcType().isLob();
|
||||||
}
|
}
|
||||||
|
|
|
@ -77,6 +77,7 @@ import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry;
|
||||||
import org.hibernate.type.spi.CompositeTypeImplementor;
|
import org.hibernate.type.spi.CompositeTypeImplementor;
|
||||||
import org.hibernate.type.spi.TypeConfiguration;
|
import org.hibernate.type.spi.TypeConfiguration;
|
||||||
|
|
||||||
|
import static org.hibernate.type.SqlTypes.ARRAY;
|
||||||
import static org.hibernate.type.SqlTypes.JSON;
|
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.SQLXML;
|
import static org.hibernate.type.SqlTypes.SQLXML;
|
||||||
|
@ -263,13 +264,18 @@ public class EmbeddableMappingTypeImpl extends AbstractEmbeddableMapping impleme
|
||||||
final TypeConfiguration typeConfiguration = creationContext.getTypeConfiguration();
|
final TypeConfiguration typeConfiguration = creationContext.getTypeConfiguration();
|
||||||
final BasicTypeRegistry basicTypeRegistry = typeConfiguration.getBasicTypeRegistry();
|
final BasicTypeRegistry basicTypeRegistry = typeConfiguration.getBasicTypeRegistry();
|
||||||
final Column aggregateColumn = bootDescriptor.getAggregateColumn();
|
final Column aggregateColumn = bootDescriptor.getAggregateColumn();
|
||||||
Integer aggregateSqlTypeCode = aggregateColumn.getSqlTypeCode();
|
final BasicValue basicValue = (BasicValue) aggregateColumn.getValue();
|
||||||
|
final BasicValue.Resolution<?> resolution = basicValue.getResolution();
|
||||||
|
final int aggregateColumnSqlTypeCode = resolution.getJdbcType().getDefaultSqlTypeCode();
|
||||||
|
final int aggregateSqlTypeCode;
|
||||||
boolean isArray = false;
|
boolean isArray = false;
|
||||||
String structTypeName = null;
|
String structTypeName = null;
|
||||||
switch ( aggregateSqlTypeCode ) {
|
switch ( aggregateColumnSqlTypeCode ) {
|
||||||
case STRUCT:
|
case STRUCT:
|
||||||
|
aggregateSqlTypeCode = STRUCT;
|
||||||
structTypeName = aggregateColumn.getSqlType( creationContext.getMetadata() );
|
structTypeName = aggregateColumn.getSqlType( creationContext.getMetadata() );
|
||||||
break;
|
break;
|
||||||
|
case ARRAY:
|
||||||
case STRUCT_ARRAY:
|
case STRUCT_ARRAY:
|
||||||
case STRUCT_TABLE:
|
case STRUCT_TABLE:
|
||||||
isArray = true;
|
isArray = true;
|
||||||
|
@ -290,6 +296,9 @@ public class EmbeddableMappingTypeImpl extends AbstractEmbeddableMapping impleme
|
||||||
isArray = true;
|
isArray = true;
|
||||||
aggregateSqlTypeCode = SQLXML;
|
aggregateSqlTypeCode = SQLXML;
|
||||||
break;
|
break;
|
||||||
|
default:
|
||||||
|
aggregateSqlTypeCode = aggregateColumnSqlTypeCode;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
final JdbcTypeRegistry jdbcTypeRegistry = typeConfiguration.getJdbcTypeRegistry();
|
final JdbcTypeRegistry jdbcTypeRegistry = typeConfiguration.getJdbcTypeRegistry();
|
||||||
final AggregateJdbcType aggregateJdbcType = jdbcTypeRegistry.resolveAggregateDescriptor(
|
final AggregateJdbcType aggregateJdbcType = jdbcTypeRegistry.resolveAggregateDescriptor(
|
||||||
|
@ -307,7 +316,6 @@ public class EmbeddableMappingTypeImpl extends AbstractEmbeddableMapping impleme
|
||||||
basicTypeRegistry.register( basicType, bootDescriptor.getStructName().render() );
|
basicTypeRegistry.register( basicType, bootDescriptor.getStructName().render() );
|
||||||
basicTypeRegistry.register( basicType, getMappedJavaType().getJavaTypeClass().getName() );
|
basicTypeRegistry.register( basicType, getMappedJavaType().getJavaTypeClass().getName() );
|
||||||
}
|
}
|
||||||
final BasicValue basicValue = (BasicValue) aggregateColumn.getValue();
|
|
||||||
final BasicType<?> resolvedJdbcMapping;
|
final BasicType<?> resolvedJdbcMapping;
|
||||||
if ( isArray ) {
|
if ( isArray ) {
|
||||||
final JdbcTypeConstructor arrayConstructor = jdbcTypeRegistry.getConstructor( SqlTypes.ARRAY );
|
final JdbcTypeConstructor arrayConstructor = jdbcTypeRegistry.getConstructor( SqlTypes.ARRAY );
|
||||||
|
@ -315,7 +323,7 @@ public class EmbeddableMappingTypeImpl extends AbstractEmbeddableMapping impleme
|
||||||
throw new IllegalArgumentException( "No JdbcTypeConstructor registered for SqlTypes.ARRAY" );
|
throw new IllegalArgumentException( "No JdbcTypeConstructor registered for SqlTypes.ARRAY" );
|
||||||
}
|
}
|
||||||
//noinspection rawtypes,unchecked
|
//noinspection rawtypes,unchecked
|
||||||
final BasicType<?> arrayType = ( (BasicPluralJavaType) basicValue.getResolution().getDomainJavaType() ).resolveType(
|
final BasicType<?> arrayType = ( (BasicPluralJavaType) resolution.getDomainJavaType() ).resolveType(
|
||||||
typeConfiguration,
|
typeConfiguration,
|
||||||
creationContext.getDialect(),
|
creationContext.getDialect(),
|
||||||
basicType,
|
basicType,
|
||||||
|
@ -328,7 +336,7 @@ public class EmbeddableMappingTypeImpl extends AbstractEmbeddableMapping impleme
|
||||||
else {
|
else {
|
||||||
resolvedJdbcMapping = basicType;
|
resolvedJdbcMapping = basicType;
|
||||||
}
|
}
|
||||||
basicValue.getResolution().updateResolution( resolvedJdbcMapping );
|
resolution.updateResolution( resolvedJdbcMapping );
|
||||||
return resolvedJdbcMapping;
|
return resolvedJdbcMapping;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,24 +7,30 @@ package org.hibernate.metamodel.mapping.internal;
|
||||||
import org.hibernate.metamodel.mapping.JdbcMapping;
|
import org.hibernate.metamodel.mapping.JdbcMapping;
|
||||||
import org.hibernate.metamodel.mapping.SqlTypedMapping;
|
import org.hibernate.metamodel.mapping.SqlTypedMapping;
|
||||||
|
|
||||||
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Christian Beikov
|
* @author Christian Beikov
|
||||||
*/
|
*/
|
||||||
public class SqlTypedMappingImpl implements SqlTypedMapping {
|
public class SqlTypedMappingImpl implements SqlTypedMapping {
|
||||||
|
|
||||||
private final String columnDefinition;
|
private final @Nullable String columnDefinition;
|
||||||
private final Long length;
|
private final @Nullable Long length;
|
||||||
private final Integer precision;
|
private final @Nullable Integer precision;
|
||||||
private final Integer scale;
|
private final @Nullable Integer scale;
|
||||||
private final Integer temporalPrecision;
|
private final @Nullable Integer temporalPrecision;
|
||||||
private final JdbcMapping jdbcMapping;
|
private final JdbcMapping jdbcMapping;
|
||||||
|
|
||||||
|
public SqlTypedMappingImpl(JdbcMapping jdbcMapping) {
|
||||||
|
this( null, null, null, null, null, jdbcMapping );
|
||||||
|
}
|
||||||
|
|
||||||
public SqlTypedMappingImpl(
|
public SqlTypedMappingImpl(
|
||||||
String columnDefinition,
|
@Nullable String columnDefinition,
|
||||||
Long length,
|
@Nullable Long length,
|
||||||
Integer precision,
|
@Nullable Integer precision,
|
||||||
Integer scale,
|
@Nullable Integer scale,
|
||||||
Integer temporalPrecision,
|
@Nullable Integer temporalPrecision,
|
||||||
JdbcMapping jdbcMapping) {
|
JdbcMapping jdbcMapping) {
|
||||||
// Save memory by using interned strings. Probability is high that we have multiple duplicate strings
|
// Save memory by using interned strings. Probability is high that we have multiple duplicate strings
|
||||||
this.columnDefinition = columnDefinition == null ? null : columnDefinition.intern();
|
this.columnDefinition = columnDefinition == null ? null : columnDefinition.intern();
|
||||||
|
@ -36,27 +42,27 @@ public class SqlTypedMappingImpl implements SqlTypedMapping {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getColumnDefinition() {
|
public @Nullable String getColumnDefinition() {
|
||||||
return columnDefinition;
|
return columnDefinition;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Long getLength() {
|
public @Nullable Long getLength() {
|
||||||
return length;
|
return length;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Integer getPrecision() {
|
public @Nullable Integer getPrecision() {
|
||||||
return precision;
|
return precision;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Integer getTemporalPrecision() {
|
public @Nullable Integer getTemporalPrecision() {
|
||||||
return temporalPrecision;
|
return temporalPrecision;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Integer getScale() {
|
public @Nullable Integer getScale() {
|
||||||
return scale;
|
return scale;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -60,6 +60,7 @@ import org.hibernate.persister.collection.CollectionPersister;
|
||||||
import org.hibernate.persister.entity.EntityPersister;
|
import org.hibernate.persister.entity.EntityPersister;
|
||||||
import org.hibernate.persister.spi.PersisterFactory;
|
import org.hibernate.persister.spi.PersisterFactory;
|
||||||
import org.hibernate.query.BindableType;
|
import org.hibernate.query.BindableType;
|
||||||
|
import org.hibernate.query.derived.AnonymousTupleSimpleSqmPathSource;
|
||||||
import org.hibernate.query.derived.AnonymousTupleSqmPathSource;
|
import org.hibernate.query.derived.AnonymousTupleSqmPathSource;
|
||||||
import org.hibernate.query.sqm.SqmExpressible;
|
import org.hibernate.query.sqm.SqmExpressible;
|
||||||
import org.hibernate.query.sqm.tree.domain.SqmPath;
|
import org.hibernate.query.sqm.tree.domain.SqmPath;
|
||||||
|
@ -742,7 +743,8 @@ public class MappingMetamodelImpl extends QueryParameterBindingTypeResolverImpl
|
||||||
return getTypeConfiguration().getBasicTypeForJavaType( sqmExpressible.getRelationalJavaType().getJavaType() );
|
return getTypeConfiguration().getBasicTypeForJavaType( sqmExpressible.getRelationalJavaType().getJavaType() );
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( sqmExpressible instanceof BasicSqmPathSource<?> ) {
|
if ( sqmExpressible instanceof BasicSqmPathSource<?>
|
||||||
|
|| sqmExpressible instanceof AnonymousTupleSimpleSqmPathSource<?> ) {
|
||||||
return resolveMappingExpressible( sqmExpressible.getSqmType(), tableGroupLocator );
|
return resolveMappingExpressible( sqmExpressible.getSqmType(), tableGroupLocator );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -27,10 +27,14 @@ import org.hibernate.query.sqm.spi.SqmCreationHelper;
|
||||||
import org.hibernate.query.sqm.tree.SqmJoinType;
|
import org.hibernate.query.sqm.tree.SqmJoinType;
|
||||||
import org.hibernate.query.sqm.tree.domain.SqmPath;
|
import org.hibernate.query.sqm.tree.domain.SqmPath;
|
||||||
import org.hibernate.query.sqm.tree.domain.SqmSingularJoin;
|
import org.hibernate.query.sqm.tree.domain.SqmSingularJoin;
|
||||||
import org.hibernate.query.sqm.tree.from.SqmAttributeJoin;
|
import org.hibernate.query.sqm.tree.expression.SqmSetReturningFunction;
|
||||||
import org.hibernate.query.sqm.tree.from.SqmFrom;
|
import org.hibernate.query.sqm.tree.from.SqmFrom;
|
||||||
|
import org.hibernate.query.sqm.tree.from.SqmFunctionJoin;
|
||||||
|
import org.hibernate.query.sqm.tree.from.SqmJoin;
|
||||||
|
import org.hibernate.query.sqm.tree.from.SqmRoot;
|
||||||
import org.hibernate.spi.EntityIdentifierNavigablePath;
|
import org.hibernate.spi.EntityIdentifierNavigablePath;
|
||||||
import org.hibernate.spi.NavigablePath;
|
import org.hibernate.spi.NavigablePath;
|
||||||
|
import org.hibernate.type.BasicPluralType;
|
||||||
import org.hibernate.type.descriptor.java.JavaType;
|
import org.hibernate.type.descriptor.java.JavaType;
|
||||||
|
|
||||||
import static jakarta.persistence.metamodel.Bindable.BindableType.SINGULAR_ATTRIBUTE;
|
import static jakarta.persistence.metamodel.Bindable.BindableType.SINGULAR_ATTRIBUTE;
|
||||||
|
@ -143,7 +147,7 @@ public class SingularAttributeImpl<D,J>
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SqmAttributeJoin<D,J> createSqmJoin(
|
public SqmJoin<D,J> createSqmJoin(
|
||||||
SqmFrom<?,D> lhs,
|
SqmFrom<?,D> lhs,
|
||||||
SqmJoinType joinType,
|
SqmJoinType joinType,
|
||||||
String alias,
|
String alias,
|
||||||
|
@ -152,6 +156,21 @@ public class SingularAttributeImpl<D,J>
|
||||||
if ( getType() instanceof AnyMappingDomainType ) {
|
if ( getType() instanceof AnyMappingDomainType ) {
|
||||||
throw new SemanticException( "An @Any attribute cannot be join fetched" );
|
throw new SemanticException( "An @Any attribute cannot be join fetched" );
|
||||||
}
|
}
|
||||||
|
else if ( sqmPathSource.getSqmPathType() instanceof BasicPluralType<?,?> ) {
|
||||||
|
final SqmSetReturningFunction<J> setReturningFunction = creationState.getCreationContext()
|
||||||
|
.getNodeBuilder()
|
||||||
|
.unnestArray( lhs.get( getName() ) );
|
||||||
|
//noinspection unchecked
|
||||||
|
return (SqmJoin<D, J>) new SqmFunctionJoin<>(
|
||||||
|
createNavigablePath( lhs, alias ),
|
||||||
|
setReturningFunction,
|
||||||
|
true,
|
||||||
|
setReturningFunction.getType(),
|
||||||
|
alias,
|
||||||
|
joinType,
|
||||||
|
(SqmRoot<Object>) lhs
|
||||||
|
);
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
return new SqmSingularJoin<>(
|
return new SqmSingularJoin<>(
|
||||||
lhs,
|
lhs,
|
||||||
|
|
|
@ -469,6 +469,11 @@ public abstract class CriteriaDefinition<R>
|
||||||
return query.from(cte);
|
return query.from(cte);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <X> JpaFunctionRoot<X> from(JpaSetReturningFunction<X> function) {
|
||||||
|
return query.from( function );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public JpaCriteriaQuery<Long> createCountQuery() {
|
public JpaCriteriaQuery<Long> createCountQuery() {
|
||||||
return query.createCountQuery();
|
return query.createCountQuery();
|
||||||
|
|
|
@ -4193,6 +4193,37 @@ public interface HibernateCriteriaBuilder extends CriteriaBuilder {
|
||||||
@Incubating
|
@Incubating
|
||||||
<T> JpaExpression<T> named(Expression<T> expression, String name);
|
<T> JpaExpression<T> named(Expression<T> expression, String name);
|
||||||
|
|
||||||
|
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
// Set-Returning functions
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new set-returning function expression.
|
||||||
|
*
|
||||||
|
* @since 7.0
|
||||||
|
* @see JpaSelectCriteria#from(JpaSetReturningFunction)
|
||||||
|
* @see JpaFrom#join(JpaSetReturningFunction)
|
||||||
|
*/
|
||||||
|
@Incubating
|
||||||
|
<E> JpaSetReturningFunction<E> setReturningFunction(String name, Expression<?>... args);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an unnest function expression to turn an array into a set of rows.
|
||||||
|
*
|
||||||
|
* @since 7.0
|
||||||
|
* @see JpaFrom#join(JpaSetReturningFunction)
|
||||||
|
*/
|
||||||
|
@Incubating
|
||||||
|
<E> JpaSetReturningFunction<E> unnestArray(Expression<E[]> array);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an unnest function expression to turn an array into a set of rows.
|
||||||
|
*
|
||||||
|
* @since 7.0
|
||||||
|
* @see JpaFrom#join(JpaSetReturningFunction)
|
||||||
|
*/
|
||||||
|
@Incubating
|
||||||
|
<E> JpaSetReturningFunction<E> unnestCollection(Expression<? extends Collection<E>> collection);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
JpaPredicate and(List<Predicate> restrictions);
|
JpaPredicate and(List<Predicate> restrictions);
|
||||||
|
|
||||||
|
|
|
@ -4,10 +4,13 @@
|
||||||
*/
|
*/
|
||||||
package org.hibernate.query.criteria;
|
package org.hibernate.query.criteria;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
import org.hibernate.Incubating;
|
import org.hibernate.Incubating;
|
||||||
import org.hibernate.metamodel.model.domain.EntityDomainType;
|
import org.hibernate.metamodel.model.domain.EntityDomainType;
|
||||||
import org.hibernate.query.sqm.tree.SqmJoinType;
|
import org.hibernate.query.sqm.tree.SqmJoinType;
|
||||||
|
|
||||||
|
import jakarta.persistence.criteria.Expression;
|
||||||
import jakarta.persistence.criteria.From;
|
import jakarta.persistence.criteria.From;
|
||||||
import jakarta.persistence.criteria.JoinType;
|
import jakarta.persistence.criteria.JoinType;
|
||||||
import jakarta.persistence.criteria.Subquery;
|
import jakarta.persistence.criteria.Subquery;
|
||||||
|
@ -62,6 +65,130 @@ public interface JpaFrom<O,T> extends JpaPath<T>, JpaFetchParent<O,T>, From<O,T>
|
||||||
@Incubating
|
@Incubating
|
||||||
<X> JpaDerivedJoin<X> join(Subquery<X> subquery, SqmJoinType joinType, boolean lateral);
|
<X> JpaDerivedJoin<X> join(Subquery<X> subquery, SqmJoinType joinType, boolean lateral);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Like calling the overload {@link #join(JpaSetReturningFunction, SqmJoinType)} with {@link SqmJoinType#INNER}.
|
||||||
|
*
|
||||||
|
* @see #join(JpaSetReturningFunction, SqmJoinType)
|
||||||
|
* @since 7.0
|
||||||
|
*/
|
||||||
|
@Incubating
|
||||||
|
<X> JpaFunctionJoin<X> join(JpaSetReturningFunction<X> function);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Like calling the overload {@link #join(JpaSetReturningFunction, SqmJoinType, boolean)} passing {@code false}
|
||||||
|
* for the {@code lateral} parameter.
|
||||||
|
*
|
||||||
|
* @see #join(JpaSetReturningFunction, SqmJoinType, boolean)
|
||||||
|
* @since 7.0
|
||||||
|
*/
|
||||||
|
@Incubating
|
||||||
|
<X> JpaFunctionJoin<X> join(JpaSetReturningFunction<X> function, SqmJoinType joinType);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Like calling the overload {@link #joinLateral(JpaSetReturningFunction, SqmJoinType)} with {@link SqmJoinType#INNER}.
|
||||||
|
*
|
||||||
|
* @see #joinLateral(JpaSetReturningFunction, SqmJoinType)
|
||||||
|
* @since 7.0
|
||||||
|
*/
|
||||||
|
@Incubating
|
||||||
|
<X> JpaFunctionJoin<X> joinLateral(JpaSetReturningFunction<X> function);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Like calling the overload {@link #join(JpaSetReturningFunction, SqmJoinType, boolean)} passing {@code true}
|
||||||
|
* for the {@code lateral} parameter.
|
||||||
|
*
|
||||||
|
* @see #join(JpaSetReturningFunction, SqmJoinType, boolean)
|
||||||
|
* @since 7.0
|
||||||
|
*/
|
||||||
|
@Incubating
|
||||||
|
<X> JpaFunctionJoin<X> joinLateral(JpaSetReturningFunction<X> function, SqmJoinType joinType);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates and returns a join node for the given set returning function.
|
||||||
|
* If function arguments refer to correlated paths, the {@code lateral} argument must be set to {@code true}.
|
||||||
|
* Failing to do so when necessary may lead to an error during query compilation or execution.
|
||||||
|
*
|
||||||
|
* @since 7.0
|
||||||
|
*/
|
||||||
|
@Incubating
|
||||||
|
<X> JpaFunctionJoin<X> join(JpaSetReturningFunction<X> function, SqmJoinType joinType, boolean lateral);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Like calling the overload {@link #joinArray(String, SqmJoinType)} with {@link SqmJoinType#INNER}.
|
||||||
|
*
|
||||||
|
* @see #joinArray(String, SqmJoinType)
|
||||||
|
* @since 7.0
|
||||||
|
*/
|
||||||
|
@Incubating
|
||||||
|
<X> JpaFunctionJoin<X> joinArray(String arrayAttributeName);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Like calling the overload {@link #join(JpaSetReturningFunction, SqmJoinType)} with {@link HibernateCriteriaBuilder#unnestArray(Expression)}
|
||||||
|
* with the result of {@link #get(String)} passing the given attribute name.
|
||||||
|
*
|
||||||
|
* @see #joinLateral(JpaSetReturningFunction, SqmJoinType)
|
||||||
|
* @since 7.0
|
||||||
|
*/
|
||||||
|
@Incubating
|
||||||
|
<X> JpaFunctionJoin<X> joinArray(String arrayAttributeName, SqmJoinType joinType);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Like calling the overload {@link #joinArray(SingularAttribute, SqmJoinType)} with {@link SqmJoinType#INNER}.
|
||||||
|
*
|
||||||
|
* @see #joinArray(SingularAttribute, SqmJoinType)
|
||||||
|
* @since 7.0
|
||||||
|
*/
|
||||||
|
@Incubating
|
||||||
|
<X> JpaFunctionJoin<X> joinArray(SingularAttribute<? super T, X[]> arrayAttribute);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Like calling the overload {@link #join(JpaSetReturningFunction, SqmJoinType)} with {@link HibernateCriteriaBuilder#unnestArray(Expression)}
|
||||||
|
* with the given attribute.
|
||||||
|
*
|
||||||
|
* @see #joinLateral(JpaSetReturningFunction, SqmJoinType)
|
||||||
|
* @since 7.0
|
||||||
|
*/
|
||||||
|
@Incubating
|
||||||
|
<X> JpaFunctionJoin<X> joinArray(SingularAttribute<? super T, X[]> arrayAttribute, SqmJoinType joinType);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Like calling the overload {@link #joinArrayCollection(String, SqmJoinType)} with {@link SqmJoinType#INNER}.
|
||||||
|
*
|
||||||
|
* @see #joinArrayCollection(String, SqmJoinType)
|
||||||
|
* @since 7.0
|
||||||
|
*/
|
||||||
|
@Incubating
|
||||||
|
<X> JpaFunctionJoin<X> joinArrayCollection(String collectionAttributeName);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Like calling the overload {@link #join(JpaSetReturningFunction, SqmJoinType)} with {@link HibernateCriteriaBuilder#unnestCollection(Expression)}
|
||||||
|
* with the result of {@link #get(String)} passing the given attribute name.
|
||||||
|
*
|
||||||
|
* @see #joinLateral(JpaSetReturningFunction, SqmJoinType)
|
||||||
|
* @since 7.0
|
||||||
|
*/
|
||||||
|
@Incubating
|
||||||
|
<X> JpaFunctionJoin<X> joinArrayCollection(String collectionAttributeName, SqmJoinType joinType);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Like calling the overload {@link #joinArrayCollection(SingularAttribute, SqmJoinType)} with {@link SqmJoinType#INNER}.
|
||||||
|
*
|
||||||
|
* @see #joinArrayCollection(SingularAttribute, SqmJoinType)
|
||||||
|
* @since 7.0
|
||||||
|
*/
|
||||||
|
@Incubating
|
||||||
|
<X> JpaFunctionJoin<X> joinArrayCollection(SingularAttribute<? super T, ? extends Collection<X>> collectionAttribute);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Like calling the overload {@link #join(JpaSetReturningFunction, SqmJoinType)} with {@link HibernateCriteriaBuilder#unnestCollection(Expression)}
|
||||||
|
* with the given attribute.
|
||||||
|
*
|
||||||
|
* @see #joinLateral(JpaSetReturningFunction, SqmJoinType)
|
||||||
|
* @since 7.0
|
||||||
|
*/
|
||||||
|
@Incubating
|
||||||
|
<X> JpaFunctionJoin<X> joinArrayCollection(SingularAttribute<? super T, ? extends Collection<X>> collectionAttribute, SqmJoinType joinType);
|
||||||
|
|
||||||
@Incubating
|
@Incubating
|
||||||
<X> JpaJoin<?, X> join(JpaCteCriteria<X> cte);
|
<X> JpaJoin<?, X> join(JpaCteCriteria<X> cte);
|
||||||
|
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue