* Handle quoted identifiers in HQL and the Ordering parser
* Switch from the "expression" to "expressionOrPredicate" rule in the HQL grammar where it makes sense as required by some HQL tests * Cleanup parser rule ordering to allow more keywords in the identifier rule * Implement literal support for Ordering parser * Add special AvgFunction as needed by H2, HSQL, DB2, SQL Server and Sybase that casts arguments to double if necessary * Fix wrong deduplication of order by fragments in case a plural attribute is fetched multiple times * Implement support for de-referencing any-valued mappings in HQL * Avoid unnecessary entity subtypes in polymorphic splitted queries if a base type also matches the requested type * Implement pagination support for polymorphic splitted queries * Cleanup path part resolving by removing lots of duplicate code * Aligh HQL parsing expectations to the expected behavior of 5.x * Add method to `JavaType` that allows determining if a type is can be widened to another which is used for arithmetic type resolving * Implement validations for fetch owner checking * Fix issues with the id table creation due to lacking column lengths in the column DDL type * Fix issues and add some optimizations related to multi-table delete handling * Add the notion of a special "implicit" alias to avoid generating a unique alias for unaliased or implicit HQL joins * Properly implement multiple bag fetch validation * Make sure filter predicates are applied for all plural attribute joins * Fix some issues with undecidable parameter type inference * Fix some issues with negated SQM predicates not being converted to the proper SQL AST predicates * Fix issues with qualifying DML target referencing columns * Fix `is null` semantics for tuples referring to embeddable types * Capture necessary details from JdbcValuesMetadata in the cached data to avoid executing a query on cache hit when types should be inferred * Get rid of special CollectionPropertyNames and writeup a migration guide section for the replacements
This commit is contained in:
parent
39dae088ec
commit
cbcec73d4f
|
@ -270,6 +270,8 @@ exec sp_dboption $SYBASE_DB, 'full logging for alter table', true
|
||||||
go
|
go
|
||||||
sp_dboption $SYBASE_DB, \"select into\", true
|
sp_dboption $SYBASE_DB, \"select into\", true
|
||||||
go
|
go
|
||||||
|
sp_dboption tempdb, 'ddl in tran', true
|
||||||
|
go
|
||||||
EOSQL
|
EOSQL
|
||||||
|
|
||||||
/opt/sybase/OCS-16_0/bin/isql -Usa -P myPassword -S MYSYBASE -i ./init1.sql
|
/opt/sybase/OCS-16_0/bin/isql -Usa -P myPassword -S MYSYBASE -i ./init1.sql
|
||||||
|
|
|
@ -870,7 +870,7 @@ public class FirebirdDialect extends Dialect {
|
||||||
return getVersion() < 210
|
return getVersion() < 210
|
||||||
? super.getFallbackSqmMutationStrategy( entityDescriptor, runtimeModelCreationContext )
|
? super.getFallbackSqmMutationStrategy( entityDescriptor, runtimeModelCreationContext )
|
||||||
: new GlobalTemporaryTableStrategy(
|
: new GlobalTemporaryTableStrategy(
|
||||||
new IdTable( entityDescriptor, name -> "HT_" + name, this ),
|
new IdTable( entityDescriptor, name -> "HT_" + name, this, runtimeModelCreationContext ),
|
||||||
() -> new TempIdTableExporter( false, this::getTypeName ) {
|
() -> new TempIdTableExporter( false, this::getTypeName ) {
|
||||||
@Override
|
@Override
|
||||||
protected String getCreateOptions() {
|
protected String getCreateOptions() {
|
||||||
|
|
|
@ -388,7 +388,7 @@ public class InformixDialect extends Dialect {
|
||||||
EntityMappingType rootEntityDescriptor,
|
EntityMappingType rootEntityDescriptor,
|
||||||
RuntimeModelCreationContext runtimeModelCreationContext) {
|
RuntimeModelCreationContext runtimeModelCreationContext) {
|
||||||
return new LocalTemporaryTableStrategy(
|
return new LocalTemporaryTableStrategy(
|
||||||
new IdTable( rootEntityDescriptor, basename -> "HT_" + basename, this ),
|
new IdTable( rootEntityDescriptor, basename -> "HT_" + basename, this, runtimeModelCreationContext ),
|
||||||
() -> new TempIdTableExporter( true, this::getTypeName ) {
|
() -> new TempIdTableExporter( true, this::getTypeName ) {
|
||||||
@Override
|
@Override
|
||||||
protected String getCreateCommand() {
|
protected String getCreateCommand() {
|
||||||
|
|
|
@ -413,7 +413,7 @@ public class IngresDialect extends Dialect {
|
||||||
EntityMappingType rootEntityDescriptor,
|
EntityMappingType rootEntityDescriptor,
|
||||||
RuntimeModelCreationContext runtimeModelCreationContext) {
|
RuntimeModelCreationContext runtimeModelCreationContext) {
|
||||||
return new GlobalTemporaryTableStrategy(
|
return new GlobalTemporaryTableStrategy(
|
||||||
new IdTable( rootEntityDescriptor, name -> "session." + name, this ),
|
new IdTable( rootEntityDescriptor, name -> "session." + name, this, runtimeModelCreationContext ),
|
||||||
() -> new TempIdTableExporter( false, this::getTypeName ) {
|
() -> new TempIdTableExporter( false, this::getTypeName ) {
|
||||||
@Override
|
@Override
|
||||||
protected String getCreateOptions() {
|
protected String getCreateOptions() {
|
||||||
|
|
|
@ -295,7 +295,7 @@ public class TeradataDialect extends Dialect {
|
||||||
EntityMappingType rootEntityDescriptor,
|
EntityMappingType rootEntityDescriptor,
|
||||||
RuntimeModelCreationContext runtimeModelCreationContext) {
|
RuntimeModelCreationContext runtimeModelCreationContext) {
|
||||||
return new GlobalTemporaryTableStrategy(
|
return new GlobalTemporaryTableStrategy(
|
||||||
new IdTable( rootEntityDescriptor, basename -> "HT_" + basename, this ),
|
new IdTable( rootEntityDescriptor, basename -> "HT_" + basename, this, runtimeModelCreationContext ),
|
||||||
() -> new TempIdTableExporter( false, this::getTypeName ) {
|
() -> new TempIdTableExporter( false, this::getTypeName ) {
|
||||||
@Override
|
@Override
|
||||||
public String getCreateOptions() {
|
public String getCreateOptions() {
|
||||||
|
|
|
@ -314,8 +314,12 @@ public class TimesTenDialect extends Dialect {
|
||||||
EntityMappingType rootEntityDescriptor,
|
EntityMappingType rootEntityDescriptor,
|
||||||
RuntimeModelCreationContext runtimeModelCreationContext) {
|
RuntimeModelCreationContext runtimeModelCreationContext) {
|
||||||
return new GlobalTemporaryTableStrategy(
|
return new GlobalTemporaryTableStrategy(
|
||||||
new IdTable( rootEntityDescriptor,
|
new IdTable(
|
||||||
name -> name.length() > 30 ? name.substring( 0, 30 ) : name, this ),
|
rootEntityDescriptor,
|
||||||
|
name -> name.length() > 30 ? name.substring( 0, 30 ) : name,
|
||||||
|
this,
|
||||||
|
runtimeModelCreationContext
|
||||||
|
),
|
||||||
() -> new TempIdTableExporter( false, this::getTypeName ) {
|
() -> new TempIdTableExporter( false, this::getTypeName ) {
|
||||||
@Override
|
@Override
|
||||||
protected String getCreateOptions() {
|
protected String getCreateOptions() {
|
||||||
|
|
|
@ -42,6 +42,9 @@ BIG_DECIMAL_SUFFIX : [bB] [dD];
|
||||||
fragment
|
fragment
|
||||||
BIG_INTEGER_SUFFIX : [bB] [iI];
|
BIG_INTEGER_SUFFIX : [bB] [iI];
|
||||||
|
|
||||||
|
// Although this is not 100% correct because this accepts leading zeros,
|
||||||
|
// we stick to this because temporal literals use this rule for simplicity.
|
||||||
|
// Since we don't support octal literals, this shouldn't really be a big issue
|
||||||
fragment
|
fragment
|
||||||
INTEGER_NUMBER
|
INTEGER_NUMBER
|
||||||
: DIGIT+
|
: DIGIT+
|
||||||
|
@ -73,8 +76,8 @@ fragment SINGLE_QUOTE : '\'';
|
||||||
fragment DOUBLE_QUOTE : '"';
|
fragment DOUBLE_QUOTE : '"';
|
||||||
|
|
||||||
STRING_LITERAL
|
STRING_LITERAL
|
||||||
: DOUBLE_QUOTE ( ~('"') | ESCAPE_SEQUENCE | DOUBLE_QUOTE DOUBLE_QUOTE )* DOUBLE_QUOTE
|
: DOUBLE_QUOTE ( ESCAPE_SEQUENCE | DOUBLE_QUOTE DOUBLE_QUOTE | ~('"') )* DOUBLE_QUOTE
|
||||||
| SINGLE_QUOTE ( ~('\'') | ESCAPE_SEQUENCE | SINGLE_QUOTE SINGLE_QUOTE )* SINGLE_QUOTE
|
| SINGLE_QUOTE ( ESCAPE_SEQUENCE | SINGLE_QUOTE SINGLE_QUOTE | ~('\'') )* SINGLE_QUOTE
|
||||||
;
|
;
|
||||||
|
|
||||||
fragment BACKSLASH : '\\';
|
fragment BACKSLASH : '\\';
|
||||||
|
@ -318,5 +321,5 @@ fragment
|
||||||
BACKTICK : '`';
|
BACKTICK : '`';
|
||||||
|
|
||||||
QUOTED_IDENTIFIER
|
QUOTED_IDENTIFIER
|
||||||
: BACKTICK ( ~([\\`]) | ESCAPE_SEQUENCE )* BACKTICK
|
: BACKTICK ( ESCAPE_SEQUENCE | '\\' BACKTICK | ~([`]) )* BACKTICK
|
||||||
;
|
;
|
||||||
|
|
|
@ -52,7 +52,7 @@ setClause
|
||||||
;
|
;
|
||||||
|
|
||||||
assignment
|
assignment
|
||||||
: dotIdentifierSequence EQUAL expression
|
: dotIdentifierSequence EQUAL expressionOrPredicate
|
||||||
;
|
;
|
||||||
|
|
||||||
insertStatement
|
insertStatement
|
||||||
|
@ -68,7 +68,7 @@ valuesList
|
||||||
;
|
;
|
||||||
|
|
||||||
values
|
values
|
||||||
: LEFT_PAREN expression (COMMA expression)* RIGHT_PAREN
|
: LEFT_PAREN expressionOrPredicate (COMMA expressionOrPredicate)* RIGHT_PAREN
|
||||||
;
|
;
|
||||||
|
|
||||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
@ -116,20 +116,14 @@ pathRoot
|
||||||
: entityName identificationVariableDef?
|
: entityName identificationVariableDef?
|
||||||
;
|
;
|
||||||
|
|
||||||
/**
|
|
||||||
* Specialized dotIdentifierSequence for cases where we expect an entity-name. We handle it specially
|
|
||||||
* for the sake of performance. Specifically we concatenate together the entity name as we walk the
|
|
||||||
* parse tree. Relying on the `EntiytNameContext#getText` or `DotIdentifierSequenceContext#getText`
|
|
||||||
* performs walk to determine the name.
|
|
||||||
*/
|
|
||||||
entityName
|
entityName
|
||||||
returns [String fullNameText]
|
: identifier (DOT identifier)*
|
||||||
: (i=identifier { $fullNameText = _localctx.i.getText(); }) (DOT c=identifier { $fullNameText += ("." + _localctx.c.getText() ); })*
|
|
||||||
;
|
;
|
||||||
|
|
||||||
identificationVariableDef
|
identificationVariableDef
|
||||||
: (AS identifier)
|
: (AS identifier)
|
||||||
| IDENTIFIER
|
| IDENTIFIER
|
||||||
|
| QUOTED_IDENTIFIER
|
||||||
;
|
;
|
||||||
|
|
||||||
crossJoin
|
crossJoin
|
||||||
|
@ -171,19 +165,14 @@ selectionList
|
||||||
;
|
;
|
||||||
|
|
||||||
selection
|
selection
|
||||||
: selectExpression resultIdentifier?
|
: selectExpression identificationVariableDef?
|
||||||
;
|
;
|
||||||
|
|
||||||
selectExpression
|
selectExpression
|
||||||
: dynamicInstantiation
|
: dynamicInstantiation
|
||||||
| jpaSelectObjectSyntax
|
| jpaSelectObjectSyntax
|
||||||
| mapEntrySelection
|
| mapEntrySelection
|
||||||
| expression
|
| expressionOrPredicate
|
||||||
;
|
|
||||||
|
|
||||||
resultIdentifier
|
|
||||||
: (AS identifier)
|
|
||||||
| IDENTIFIER
|
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|
||||||
|
@ -206,11 +195,11 @@ dynamicInstantiationArgs
|
||||||
;
|
;
|
||||||
|
|
||||||
dynamicInstantiationArg
|
dynamicInstantiationArg
|
||||||
: dynamicInstantiationArgExpression (AS? identifier)?
|
: dynamicInstantiationArgExpression identificationVariableDef?
|
||||||
;
|
;
|
||||||
|
|
||||||
dynamicInstantiationArgExpression
|
dynamicInstantiationArgExpression
|
||||||
: expression
|
: expressionOrPredicate
|
||||||
| dynamicInstantiation
|
| dynamicInstantiation
|
||||||
;
|
;
|
||||||
|
|
||||||
|
@ -428,10 +417,10 @@ comparisonOperator
|
||||||
;
|
;
|
||||||
|
|
||||||
inList
|
inList
|
||||||
: (ELEMENTS|INDICES) LEFT_PAREN dotIdentifierSequence RIGHT_PAREN # PersistentCollectionReferenceInList
|
: (ELEMENTS|INDICES) LEFT_PAREN dotIdentifierSequence RIGHT_PAREN # PersistentCollectionReferenceInList
|
||||||
| LEFT_PAREN expression (COMMA expression)* RIGHT_PAREN # ExplicitTupleInList
|
| LEFT_PAREN (expressionOrPredicate (COMMA expressionOrPredicate)*)? RIGHT_PAREN# ExplicitTupleInList
|
||||||
| LEFT_PAREN subQuery RIGHT_PAREN # SubQueryInList
|
| LEFT_PAREN subQuery RIGHT_PAREN # SubQueryInList
|
||||||
| parameter # ParamInList
|
| parameter # ParamInList
|
||||||
;
|
;
|
||||||
|
|
||||||
likeEscape
|
likeEscape
|
||||||
|
@ -444,16 +433,17 @@ likeEscape
|
||||||
|
|
||||||
expression
|
expression
|
||||||
//highest to lowest precedence
|
//highest to lowest precedence
|
||||||
: LEFT_PAREN expression RIGHT_PAREN # GroupedExpression
|
: LEFT_PAREN expression RIGHT_PAREN # GroupedExpression
|
||||||
| LEFT_PAREN expression (COMMA expression)* RIGHT_PAREN # TupleExpression
|
| LEFT_PAREN expressionOrPredicate (COMMA expressionOrPredicate)+ RIGHT_PAREN # TupleExpression
|
||||||
| LEFT_PAREN subQuery RIGHT_PAREN # SubQueryExpression
|
| LEFT_PAREN subQuery RIGHT_PAREN # SubQueryExpression
|
||||||
| primaryExpression collationSpecification? # CollateExpression
|
| primaryExpression collationSpecification? # CollateExpression
|
||||||
| signOperator expression # UnaryExpression
|
| signOperator numericLiteral # UnaryNumericLiteralExpression
|
||||||
| expression datetimeField # ToDurationExpression
|
| signOperator expression # UnaryExpression
|
||||||
| expression BY datetimeField # FromDurationExpression
|
| expression datetimeField # ToDurationExpression
|
||||||
| expression multiplicativeOperator expression # MultiplicationExpression
|
| expression BY datetimeField # FromDurationExpression
|
||||||
| expression additiveOperator expression # AdditionExpression
|
| expression multiplicativeOperator expression # MultiplicationExpression
|
||||||
| expression DOUBLE_PIPE expression # ConcatenationExpression
|
| expression additiveOperator expression # AdditionExpression
|
||||||
|
| expression DOUBLE_PIPE expression # ConcatenationExpression
|
||||||
;
|
;
|
||||||
|
|
||||||
primaryExpression
|
primaryExpression
|
||||||
|
@ -464,8 +454,14 @@ primaryExpression
|
||||||
| entityIdReference # EntityIdExpression
|
| entityIdReference # EntityIdExpression
|
||||||
| entityVersionReference # EntityVersionExpression
|
| entityVersionReference # EntityVersionExpression
|
||||||
| entityNaturalIdReference # EntityNaturalIdExpression
|
| entityNaturalIdReference # EntityNaturalIdExpression
|
||||||
| path # PathExpression
|
| syntacticDomainPath (pathContinuation)? # SyntacticPathExpression
|
||||||
| function # FunctionExpression
|
| function # FunctionExpression
|
||||||
|
| generalPathFragment # GeneralPathExpression
|
||||||
|
;
|
||||||
|
|
||||||
|
expressionOrPredicate
|
||||||
|
: expression
|
||||||
|
| predicate
|
||||||
;
|
;
|
||||||
|
|
||||||
multiplicativeOperator
|
multiplicativeOperator
|
||||||
|
@ -506,15 +502,15 @@ caseList
|
||||||
;
|
;
|
||||||
|
|
||||||
simpleCaseList
|
simpleCaseList
|
||||||
: CASE expression (simpleCaseWhen)+ (caseOtherwise)? END
|
: CASE expressionOrPredicate (simpleCaseWhen)+ (caseOtherwise)? END
|
||||||
;
|
;
|
||||||
|
|
||||||
simpleCaseWhen
|
simpleCaseWhen
|
||||||
: WHEN expression THEN expression
|
: WHEN expression THEN expressionOrPredicate
|
||||||
;
|
;
|
||||||
|
|
||||||
caseOtherwise
|
caseOtherwise
|
||||||
: ELSE expression
|
: ELSE expressionOrPredicate
|
||||||
;
|
;
|
||||||
|
|
||||||
searchedCaseList
|
searchedCaseList
|
||||||
|
@ -522,24 +518,28 @@ searchedCaseList
|
||||||
;
|
;
|
||||||
|
|
||||||
searchedCaseWhen
|
searchedCaseWhen
|
||||||
: WHEN predicate THEN expression
|
: WHEN predicate THEN expressionOrPredicate
|
||||||
;
|
;
|
||||||
|
|
||||||
literal
|
literal
|
||||||
: STRING_LITERAL
|
: STRING_LITERAL
|
||||||
| INTEGER_LITERAL
|
| NULL
|
||||||
|
| TRUE
|
||||||
|
| FALSE
|
||||||
|
| numericLiteral
|
||||||
|
| binaryLiteral
|
||||||
|
| temporalLiteral
|
||||||
|
| generalizedLiteral
|
||||||
|
;
|
||||||
|
|
||||||
|
numericLiteral
|
||||||
|
: INTEGER_LITERAL
|
||||||
| LONG_LITERAL
|
| LONG_LITERAL
|
||||||
| BIG_INTEGER_LITERAL
|
| BIG_INTEGER_LITERAL
|
||||||
| FLOAT_LITERAL
|
| FLOAT_LITERAL
|
||||||
| DOUBLE_LITERAL
|
| DOUBLE_LITERAL
|
||||||
| BIG_DECIMAL_LITERAL
|
| BIG_DECIMAL_LITERAL
|
||||||
| HEX_LITERAL
|
| HEX_LITERAL
|
||||||
| NULL
|
|
||||||
| TRUE
|
|
||||||
| FALSE
|
|
||||||
| binaryLiteral
|
|
||||||
| temporalLiteral
|
|
||||||
| generalizedLiteral
|
|
||||||
;
|
;
|
||||||
|
|
||||||
binaryLiteral
|
binaryLiteral
|
||||||
|
@ -652,7 +652,7 @@ genericFunctionName
|
||||||
;
|
;
|
||||||
|
|
||||||
nonStandardFunctionArguments
|
nonStandardFunctionArguments
|
||||||
: (DISTINCT | datetimeField COMMA)? expression (COMMA expression)*
|
: (DISTINCT | datetimeField COMMA)? expressionOrPredicate (COMMA expressionOrPredicate)*
|
||||||
;
|
;
|
||||||
|
|
||||||
jpaCollectionFunction
|
jpaCollectionFunction
|
||||||
|
@ -897,11 +897,11 @@ positionFunctionStringArgument
|
||||||
;
|
;
|
||||||
|
|
||||||
cube
|
cube
|
||||||
: CUBE LEFT_PAREN expression (COMMA expression)* RIGHT_PAREN
|
: CUBE LEFT_PAREN expressionOrPredicate (COMMA expressionOrPredicate)* RIGHT_PAREN
|
||||||
;
|
;
|
||||||
|
|
||||||
rollup
|
rollup
|
||||||
: ROLLUP LEFT_PAREN expression (COMMA expression)* RIGHT_PAREN
|
: ROLLUP LEFT_PAREN expressionOrPredicate (COMMA expressionOrPredicate)* RIGHT_PAREN
|
||||||
;
|
;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -941,13 +941,15 @@ identifier
|
||||||
| COS
|
| COS
|
||||||
| COUNT
|
| COUNT
|
||||||
| CROSS
|
| CROSS
|
||||||
|
| CUBE
|
||||||
|
| CURRENT
|
||||||
| CURRENT_DATE
|
| CURRENT_DATE
|
||||||
| CURRENT_INSTANT
|
| CURRENT_INSTANT
|
||||||
| CURRENT_TIME
|
| CURRENT_TIME
|
||||||
| CURRENT_TIMESTAMP
|
| CURRENT_TIMESTAMP
|
||||||
| DATE
|
| DATE
|
||||||
| DAY
|
| DAY
|
||||||
| DAY
|
| DATETIME
|
||||||
| DELETE
|
| DELETE
|
||||||
| DESC
|
| DESC
|
||||||
| DISTINCT
|
| DISTINCT
|
||||||
|
@ -958,11 +960,13 @@ identifier
|
||||||
| ENTRY
|
| ENTRY
|
||||||
| ESCAPE
|
| ESCAPE
|
||||||
| EVERY
|
| EVERY
|
||||||
|
| EXCEPT
|
||||||
| EXISTS
|
| EXISTS
|
||||||
| EXP
|
| EXP
|
||||||
| EXTRACT
|
| EXTRACT
|
||||||
| FETCH
|
| FETCH
|
||||||
| FILTER
|
| FILTER
|
||||||
|
| FIRST
|
||||||
| FLOOR
|
| FLOOR
|
||||||
| FOR
|
| FOR
|
||||||
| FORMAT
|
| FORMAT
|
||||||
|
@ -971,6 +975,7 @@ identifier
|
||||||
| FUNCTION
|
| FUNCTION
|
||||||
| GREATEST
|
| GREATEST
|
||||||
| GROUP
|
| GROUP
|
||||||
|
| HAVING
|
||||||
| HOUR
|
| HOUR
|
||||||
| ID
|
| ID
|
||||||
| IFNULL
|
| IFNULL
|
||||||
|
@ -981,10 +986,12 @@ identifier
|
||||||
| INNER
|
| INNER
|
||||||
| INSERT
|
| INSERT
|
||||||
| INSTANT
|
| INSTANT
|
||||||
|
| INTERSECT
|
||||||
| INTO
|
| INTO
|
||||||
| IS
|
| IS
|
||||||
| JOIN
|
| JOIN
|
||||||
| KEY
|
| KEY
|
||||||
|
| LAST
|
||||||
| LEADING
|
| LEADING
|
||||||
| LEAST
|
| LEAST
|
||||||
| LEFT
|
| LEFT
|
||||||
|
@ -993,6 +1000,10 @@ identifier
|
||||||
| LIMIT
|
| LIMIT
|
||||||
| LIST
|
| LIST
|
||||||
| LN
|
| LN
|
||||||
|
| LOCAL
|
||||||
|
| LOCAL_DATE
|
||||||
|
| LOCAL_DATETIME
|
||||||
|
| LOCAL_TIME
|
||||||
| LOCATE
|
| LOCATE
|
||||||
| LOWER
|
| LOWER
|
||||||
| MAP
|
| MAP
|
||||||
|
@ -1000,7 +1011,6 @@ identifier
|
||||||
| MAXELEMENT
|
| MAXELEMENT
|
||||||
| MAXINDEX
|
| MAXINDEX
|
||||||
| MEMBER
|
| MEMBER
|
||||||
| MEMBER
|
|
||||||
| MICROSECOND
|
| MICROSECOND
|
||||||
| MILLISECOND
|
| MILLISECOND
|
||||||
| MIN
|
| MIN
|
||||||
|
@ -1012,22 +1022,32 @@ identifier
|
||||||
| NANOSECOND
|
| NANOSECOND
|
||||||
| NATURALID
|
| NATURALID
|
||||||
| NEW
|
| NEW
|
||||||
|
| NEXT
|
||||||
| NOT
|
| NOT
|
||||||
| NULLIF
|
| NULLIF
|
||||||
|
| NULLS
|
||||||
| OBJECT
|
| OBJECT
|
||||||
| OF
|
| OF
|
||||||
|
| OFFSET
|
||||||
|
| OFFSET_DATETIME
|
||||||
| ON
|
| ON
|
||||||
|
| ONLY
|
||||||
| OR
|
| OR
|
||||||
| ORDER
|
| ORDER
|
||||||
| OUTER
|
| OUTER
|
||||||
|
| OVERLAY
|
||||||
| PAD
|
| PAD
|
||||||
|
| PERCENT
|
||||||
|
| PLACING
|
||||||
| POSITION
|
| POSITION
|
||||||
| POWER
|
| POWER
|
||||||
| QUARTER
|
| QUARTER
|
||||||
| REPLACE
|
| REPLACE
|
||||||
| RIGHT
|
| RIGHT
|
||||||
| RIGHT
|
| ROLLUP
|
||||||
| ROUND
|
| ROUND
|
||||||
|
| ROW
|
||||||
|
| ROWS
|
||||||
| SECOND
|
| SECOND
|
||||||
| SELECT
|
| SELECT
|
||||||
| SET
|
| SET
|
||||||
|
@ -1041,6 +1061,7 @@ identifier
|
||||||
| SUM
|
| SUM
|
||||||
| TAN
|
| TAN
|
||||||
| THEN
|
| THEN
|
||||||
|
| TIES
|
||||||
| TIME
|
| TIME
|
||||||
| TIMESTAMP
|
| TIMESTAMP
|
||||||
| TIMEZONE_HOUR
|
| TIMEZONE_HOUR
|
||||||
|
@ -1049,6 +1070,7 @@ identifier
|
||||||
| TREAT
|
| TREAT
|
||||||
| TRIM
|
| TRIM
|
||||||
| TYPE
|
| TYPE
|
||||||
|
| UNION
|
||||||
| UPDATE
|
| UPDATE
|
||||||
| UPPER
|
| UPPER
|
||||||
| VALUE
|
| VALUE
|
||||||
|
@ -1056,6 +1078,7 @@ identifier
|
||||||
| VERSION
|
| VERSION
|
||||||
| VERSIONED
|
| VERSIONED
|
||||||
| WEEK
|
| WEEK
|
||||||
|
| WHEN
|
||||||
| WHERE
|
| WHERE
|
||||||
| WITH
|
| WITH
|
||||||
| YEAR) {
|
| YEAR) {
|
||||||
|
|
|
@ -22,10 +22,13 @@ WS : ( ' ' | '\t' | '\f' | EOL ) -> skip;
|
||||||
fragment
|
fragment
|
||||||
EOL : [\r\n]+;
|
EOL : [\r\n]+;
|
||||||
|
|
||||||
|
fragment
|
||||||
|
DIGIT : [0-9];
|
||||||
|
|
||||||
INTEGER_LITERAL : INTEGER_NUMBER ;
|
INTEGER_LITERAL : INTEGER_NUMBER ;
|
||||||
|
|
||||||
fragment
|
fragment
|
||||||
INTEGER_NUMBER : ('0' | '1'..'9' '0'..'9'*) ;
|
INTEGER_NUMBER : ('0' | '1'..'9' DIGIT*) ;
|
||||||
|
|
||||||
LONG_LITERAL : INTEGER_NUMBER ('l'|'L');
|
LONG_LITERAL : INTEGER_NUMBER ('l'|'L');
|
||||||
|
|
||||||
|
@ -34,7 +37,7 @@ BIG_INTEGER_LITERAL : INTEGER_NUMBER ('bi'|'BI') ;
|
||||||
HEX_LITERAL : '0' ('x'|'X') HEX_DIGIT+ ('l'|'L')? ;
|
HEX_LITERAL : '0' ('x'|'X') HEX_DIGIT+ ('l'|'L')? ;
|
||||||
|
|
||||||
fragment
|
fragment
|
||||||
HEX_DIGIT : ('0'..'9'|'a'..'f'|'A'..'F') ;
|
HEX_DIGIT : (DIGIT|'a'..'f'|'A'..'F') ;
|
||||||
|
|
||||||
OCTAL_LITERAL : '0' ('0'..'7')+ ('l'|'L')? ;
|
OCTAL_LITERAL : '0' ('0'..'7')+ ('l'|'L')? ;
|
||||||
|
|
||||||
|
@ -42,10 +45,10 @@ FLOAT_LITERAL : FLOATING_POINT_NUMBER ('f'|'F')? ;
|
||||||
|
|
||||||
fragment
|
fragment
|
||||||
FLOATING_POINT_NUMBER
|
FLOATING_POINT_NUMBER
|
||||||
: ('0'..'9')+ '.' ('0'..'9')* EXPONENT?
|
: DIGIT+ '.' DIGIT* EXPONENT?
|
||||||
| '.' ('0'..'9')+ EXPONENT?
|
| '.' DIGIT+ EXPONENT?
|
||||||
| ('0'..'9')+ EXPONENT
|
| DIGIT+ EXPONENT
|
||||||
| ('0'..'9')+
|
| DIGIT+
|
||||||
;
|
;
|
||||||
|
|
||||||
DOUBLE_LITERAL : FLOATING_POINT_NUMBER ('d'|'D') ;
|
DOUBLE_LITERAL : FLOATING_POINT_NUMBER ('d'|'D') ;
|
||||||
|
@ -53,15 +56,18 @@ DOUBLE_LITERAL : FLOATING_POINT_NUMBER ('d'|'D') ;
|
||||||
BIG_DECIMAL_LITERAL : FLOATING_POINT_NUMBER ('bd'|'BD') ;
|
BIG_DECIMAL_LITERAL : FLOATING_POINT_NUMBER ('bd'|'BD') ;
|
||||||
|
|
||||||
fragment
|
fragment
|
||||||
EXPONENT : ('e'|'E') ('+'|'-')? ('0'..'9')+ ;
|
EXPONENT : ('e'|'E') ('+'|'-')? DIGIT+ ;
|
||||||
|
|
||||||
|
fragment SINGLE_QUOTE : '\'';
|
||||||
|
fragment DOUBLE_QUOTE : '"';
|
||||||
|
|
||||||
CHARACTER_LITERAL
|
CHARACTER_LITERAL
|
||||||
: '\'' ( ESCAPE_SEQUENCE | ~('\''|'\\') ) '\'' {setText(getText().substring(1, getText().length()-1));}
|
: SINGLE_QUOTE ( ESCAPE_SEQUENCE | SINGLE_QUOTE SINGLE_QUOTE | ~('\'') ) SINGLE_QUOTE
|
||||||
;
|
;
|
||||||
|
|
||||||
STRING_LITERAL
|
STRING_LITERAL
|
||||||
: '"' ( ESCAPE_SEQUENCE | ~('\\'|'"') )* '"' {setText(getText().substring(1, getText().length()-1));}
|
: DOUBLE_QUOTE ( ESCAPE_SEQUENCE | DOUBLE_QUOTE DOUBLE_QUOTE | ~('"') )* DOUBLE_QUOTE
|
||||||
| ('\'' ( ESCAPE_SEQUENCE | ~('\\'|'\'') )* '\'')+ {setText(getText().substring(1, getText().length()-1).replace("''", "'"));}
|
| SINGLE_QUOTE ( ESCAPE_SEQUENCE | SINGLE_QUOTE SINGLE_QUOTE | ~('\'') )* SINGLE_QUOTE
|
||||||
;
|
;
|
||||||
|
|
||||||
fragment
|
fragment
|
||||||
|
@ -107,12 +113,19 @@ DESC : [dD] [eE] [sS] [cC] ( [eE] [nN] [dD] [iI] [nN] [gG] )?;
|
||||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
// Identifiers
|
// Identifiers
|
||||||
|
|
||||||
|
fragment
|
||||||
|
LETTER : [a-zA-Z\u0080-\ufffe_$];
|
||||||
|
|
||||||
|
// Identifiers
|
||||||
IDENTIFIER
|
IDENTIFIER
|
||||||
: ('a'..'z'|'A'..'Z'|'_'|'$'|'\u0080'..'\ufffe')('a'..'z'|'A'..'Z'|'_'|'$'|'0'..'9'|'\u0080'..'\ufffe')*
|
: LETTER (LETTER | DIGIT)*
|
||||||
;
|
;
|
||||||
|
|
||||||
|
fragment
|
||||||
|
BACKTICK : '`';
|
||||||
|
|
||||||
QUOTED_IDENTIFIER
|
QUOTED_IDENTIFIER
|
||||||
: '`' ( ESCAPE_SEQUENCE | ~('\\'|'`') )* '`'
|
: BACKTICK ( ESCAPE_SEQUENCE | '\\' BACKTICK | ~([`]) )* BACKTICK
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -33,9 +33,9 @@ sortSpecification
|
||||||
;
|
;
|
||||||
|
|
||||||
expression
|
expression
|
||||||
: function
|
: function # FunctionExpression
|
||||||
| identifier
|
| identifier # IdentifierExpression
|
||||||
| dotIdentifier
|
| dotIdentifier # DotIdentifierExpression
|
||||||
;
|
;
|
||||||
|
|
||||||
function
|
function
|
||||||
|
@ -52,7 +52,23 @@ packagedFunction
|
||||||
;
|
;
|
||||||
|
|
||||||
functionArguments
|
functionArguments
|
||||||
: OPEN_PAREN expression* CLOSE_PAREN
|
: OPEN_PAREN (functionArgument ( COMMA functionArgument )* )? CLOSE_PAREN
|
||||||
|
;
|
||||||
|
|
||||||
|
functionArgument
|
||||||
|
: expression
|
||||||
|
| literal
|
||||||
|
;
|
||||||
|
|
||||||
|
literal
|
||||||
|
: STRING_LITERAL
|
||||||
|
| INTEGER_LITERAL
|
||||||
|
| LONG_LITERAL
|
||||||
|
| BIG_INTEGER_LITERAL
|
||||||
|
| FLOAT_LITERAL
|
||||||
|
| DOUBLE_LITERAL
|
||||||
|
| BIG_DECIMAL_LITERAL
|
||||||
|
| HEX_LITERAL
|
||||||
;
|
;
|
||||||
|
|
||||||
collationSpecification
|
collationSpecification
|
||||||
|
@ -69,6 +85,7 @@ nullsPrecedence
|
||||||
|
|
||||||
identifier
|
identifier
|
||||||
: IDENTIFIER
|
: IDENTIFIER
|
||||||
|
| QUOTED_IDENTIFIER
|
||||||
// keyword-as-identifier
|
// keyword-as-identifier
|
||||||
| FIRST
|
| FIRST
|
||||||
| LAST
|
| LAST
|
||||||
|
@ -78,5 +95,5 @@ identifier
|
||||||
;
|
;
|
||||||
|
|
||||||
dotIdentifier
|
dotIdentifier
|
||||||
: IDENTIFIER (DOT IDENTIFIER)+
|
: identifier (DOT identifier)+
|
||||||
;
|
;
|
||||||
|
|
|
@ -26,25 +26,18 @@ public class Identifier implements Comparable<Identifier> {
|
||||||
* If passed text is {@code null}, {@code null} is returned.
|
* If passed text is {@code null}, {@code null} is returned.
|
||||||
* <p/>
|
* <p/>
|
||||||
* If passed text is surrounded in quote markers, the generated Identifier
|
* If passed text is surrounded in quote markers, the generated Identifier
|
||||||
* is considered quoted. Quote markers include back-ticks (`), and
|
* is considered quoted. Quote markers include back-ticks (`),
|
||||||
* double-quotes (").
|
* double-quotes (") and brackets ([ and ]).
|
||||||
|
*
|
||||||
|
* If the text, after trimming, contains a character that is not a valid identifier character,
|
||||||
|
* the identifier is treated as quoted.
|
||||||
*
|
*
|
||||||
* @param text The text form
|
* @param text The text form
|
||||||
*
|
*
|
||||||
* @return The identifier form, or {@code null} if text was {@code null}
|
* @return The identifier form, or {@code null} if text was {@code null}
|
||||||
*/
|
*/
|
||||||
public static Identifier toIdentifier(String text) {
|
public static Identifier toIdentifier(String text) {
|
||||||
if ( StringHelper.isEmpty( text ) ) {
|
return toIdentifier( text, false );
|
||||||
return null;
|
|
||||||
}
|
|
||||||
final String trimmedText = text.trim();
|
|
||||||
if ( isQuoted( trimmedText ) ) {
|
|
||||||
final String bareName = trimmedText.substring( 1, trimmedText.length() - 1 );
|
|
||||||
return new Identifier( bareName, true );
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return new Identifier( trimmedText, false );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -53,8 +46,11 @@ public class Identifier implements Comparable<Identifier> {
|
||||||
* If passed text is {@code null}, {@code null} is returned.
|
* If passed text is {@code null}, {@code null} is returned.
|
||||||
* <p/>
|
* <p/>
|
||||||
* If passed text is surrounded in quote markers, the generated Identifier
|
* If passed text is surrounded in quote markers, the generated Identifier
|
||||||
* is considered quoted. Quote markers include back-ticks (`), and
|
* is considered quoted. Quote markers include back-ticks (`),
|
||||||
* double-quotes (").
|
* double-quotes (") and brackets ([ and ]).
|
||||||
|
*
|
||||||
|
* If the text, after trimming, contains a character that is not a valid identifier character,
|
||||||
|
* the identifier is treated as quoted.
|
||||||
*
|
*
|
||||||
* @param text The text form
|
* @param text The text form
|
||||||
* @param quote Whether to quote unquoted text forms
|
* @param quote Whether to quote unquoted text forms
|
||||||
|
@ -62,17 +58,65 @@ public class Identifier implements Comparable<Identifier> {
|
||||||
* @return The identifier form, or {@code null} if text was {@code null}
|
* @return The identifier form, or {@code null} if text was {@code null}
|
||||||
*/
|
*/
|
||||||
public static Identifier toIdentifier(String text, boolean quote) {
|
public static Identifier toIdentifier(String text, boolean quote) {
|
||||||
|
return toIdentifier( text, quote, true );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Means to generate an {@link Identifier} instance from its simple text form.
|
||||||
|
* <p/>
|
||||||
|
* If passed text is {@code null}, {@code null} is returned.
|
||||||
|
* <p/>
|
||||||
|
* If passed text is surrounded in quote markers, the generated Identifier
|
||||||
|
* is considered quoted. Quote markers include back-ticks (`),
|
||||||
|
* double-quotes (") and brackets ([ and ]).
|
||||||
|
*
|
||||||
|
* @param text The text form
|
||||||
|
* @param quote Whether to quote unquoted text forms
|
||||||
|
* @param quoteOnNonIdentifierChar Controls whether to treat the result as quoted if text contains characters that are invalid for identifiers
|
||||||
|
*
|
||||||
|
* @return The identifier form, or {@code null} if text was {@code null}
|
||||||
|
*/
|
||||||
|
public static Identifier toIdentifier(String text, boolean quote, boolean quoteOnNonIdentifierChar) {
|
||||||
if ( StringHelper.isEmpty( text ) ) {
|
if ( StringHelper.isEmpty( text ) ) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
final String trimmedText = text.trim();
|
int start = 0;
|
||||||
if ( isQuoted( trimmedText ) ) {
|
int end = text.length();
|
||||||
final String bareName = trimmedText.substring( 1, trimmedText.length() - 1 );
|
while ( start < end ) {
|
||||||
return new Identifier( bareName, true );
|
if ( !Character.isWhitespace( text.charAt( start ) ) ) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
start++;
|
||||||
}
|
}
|
||||||
else {
|
while ( start < end ) {
|
||||||
return new Identifier( trimmedText, quote );
|
if ( !Character.isWhitespace( text.charAt( end - 1 ) ) ) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
end--;
|
||||||
}
|
}
|
||||||
|
if ( isQuoted( text, start, end ) ) {
|
||||||
|
start++;
|
||||||
|
end--;
|
||||||
|
quote = true;
|
||||||
|
}
|
||||||
|
else if ( quoteOnNonIdentifierChar && !quote ) {
|
||||||
|
// Check the letters to determine if we must quote the text
|
||||||
|
char c = text.charAt( start );
|
||||||
|
if ( !Character.isLetter( c ) && c != '_' ) {
|
||||||
|
// SQL identifiers must begin with a letter or underscore
|
||||||
|
quote = true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
for ( int i = start + 1; i < end; i++ ) {
|
||||||
|
c = text.charAt( i );
|
||||||
|
if ( !Character.isLetterOrDigit( c ) && c != '_' ) {
|
||||||
|
quote = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new Identifier( text.substring( start, end ), quote );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -91,9 +135,21 @@ public class Identifier implements Comparable<Identifier> {
|
||||||
* @return {@code true} if the given identifier text is considered quoted; {@code false} otherwise.
|
* @return {@code true} if the given identifier text is considered quoted; {@code false} otherwise.
|
||||||
*/
|
*/
|
||||||
public static boolean isQuoted(String name) {
|
public static boolean isQuoted(String name) {
|
||||||
return ( name.startsWith( "`" ) && name.endsWith( "`" ) )
|
return isQuoted( name, 0, name.length() );
|
||||||
|| ( name.startsWith( "[" ) && name.endsWith( "]" ) )
|
}
|
||||||
|| ( name.startsWith( "\"" ) && name.endsWith( "\"" ) );
|
|
||||||
|
public static boolean isQuoted(String name, int start, int end) {
|
||||||
|
if ( start + 2 < end ) {
|
||||||
|
switch ( name.charAt( start ) ) {
|
||||||
|
case '`':
|
||||||
|
return name.charAt( end - 1 ) == '`';
|
||||||
|
case '[':
|
||||||
|
return name.charAt( end - 1 ) == ']';
|
||||||
|
case '"':
|
||||||
|
return name.charAt( end - 1 ) == '"';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String unQuote(String name) {
|
public static String unQuote(String name) {
|
||||||
|
|
|
@ -260,7 +260,7 @@ public abstract class AbstractTransactSQLDialect extends Dialect {
|
||||||
EntityMappingType entityDescriptor,
|
EntityMappingType entityDescriptor,
|
||||||
RuntimeModelCreationContext runtimeModelCreationContext) {
|
RuntimeModelCreationContext runtimeModelCreationContext) {
|
||||||
return new LocalTemporaryTableStrategy(
|
return new LocalTemporaryTableStrategy(
|
||||||
new IdTable( entityDescriptor, basename -> "#" + basename, this ),
|
new IdTable( entityDescriptor, basename -> "#" + basename, this, runtimeModelCreationContext ),
|
||||||
() -> new TempIdTableExporter( true, this::getTypeName ) {
|
() -> new TempIdTableExporter( true, this::getTypeName ) {
|
||||||
@Override
|
@Override
|
||||||
protected String getCreateCommand() {
|
protected String getCreateCommand() {
|
||||||
|
|
|
@ -34,6 +34,7 @@ import org.hibernate.query.spi.QueryEngine;
|
||||||
import org.hibernate.query.sqm.mutation.internal.cte.CteStrategy;
|
import org.hibernate.query.sqm.mutation.internal.cte.CteStrategy;
|
||||||
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy;
|
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy;
|
||||||
import org.hibernate.service.ServiceRegistry;
|
import org.hibernate.service.ServiceRegistry;
|
||||||
|
import org.hibernate.sql.ast.SqlAstNodeRenderingMode;
|
||||||
import org.hibernate.sql.ast.SqlAstTranslator;
|
import org.hibernate.sql.ast.SqlAstTranslator;
|
||||||
import org.hibernate.sql.ast.SqlAstTranslatorFactory;
|
import org.hibernate.sql.ast.SqlAstTranslatorFactory;
|
||||||
import org.hibernate.sql.ast.spi.SqlAppender;
|
import org.hibernate.sql.ast.spi.SqlAppender;
|
||||||
|
@ -155,6 +156,9 @@ public class DB2Dialect extends Dialect {
|
||||||
public void initializeFunctionRegistry(QueryEngine queryEngine) {
|
public void initializeFunctionRegistry(QueryEngine queryEngine) {
|
||||||
super.initializeFunctionRegistry( queryEngine );
|
super.initializeFunctionRegistry( queryEngine );
|
||||||
|
|
||||||
|
// AVG by default uses the input type, so we possibly need to cast the argument type, hence a special function
|
||||||
|
CommonFunctionFactory.avg_castingNonDoubleArguments( this, queryEngine, SqlAstNodeRenderingMode.DEFAULT );
|
||||||
|
|
||||||
CommonFunctionFactory.cot( queryEngine );
|
CommonFunctionFactory.cot( queryEngine );
|
||||||
CommonFunctionFactory.degrees( queryEngine );
|
CommonFunctionFactory.degrees( queryEngine );
|
||||||
CommonFunctionFactory.log( queryEngine );
|
CommonFunctionFactory.log( queryEngine );
|
||||||
|
|
|
@ -782,7 +782,12 @@ public class DerbyDialect extends Dialect {
|
||||||
EntityMappingType rootEntityDescriptor,
|
EntityMappingType rootEntityDescriptor,
|
||||||
RuntimeModelCreationContext runtimeModelCreationContext) {
|
RuntimeModelCreationContext runtimeModelCreationContext) {
|
||||||
return new LocalTemporaryTableStrategy(
|
return new LocalTemporaryTableStrategy(
|
||||||
new IdTable( rootEntityDescriptor, basename -> "session.HT_" + basename, this ),
|
new IdTable(
|
||||||
|
rootEntityDescriptor,
|
||||||
|
basename -> "session.HT_" + basename,
|
||||||
|
this,
|
||||||
|
runtimeModelCreationContext
|
||||||
|
),
|
||||||
() -> new TempIdTableExporter( true, this::getTypeName ) {
|
() -> new TempIdTableExporter( true, this::getTypeName ) {
|
||||||
@Override
|
@Override
|
||||||
protected String getCreateCommand() {
|
protected String getCreateCommand() {
|
||||||
|
|
|
@ -513,7 +513,7 @@ public abstract class Dialect implements ConversionContext {
|
||||||
|
|
||||||
//aggregate functions, supported on every database
|
//aggregate functions, supported on every database
|
||||||
|
|
||||||
CommonFunctionFactory.aggregates( this, queryEngine, SqlAstNodeRenderingMode.DEFAULT );
|
CommonFunctionFactory.aggregates( this, queryEngine, SqlAstNodeRenderingMode.DEFAULT, "||", null );
|
||||||
|
|
||||||
//the ANSI SQL-defined aggregate functions any() and every() are only
|
//the ANSI SQL-defined aggregate functions any() and every() are only
|
||||||
//supported on one database, but can be emulated using sum() and case,
|
//supported on one database, but can be emulated using sum() and case,
|
||||||
|
@ -1834,7 +1834,7 @@ public abstract class Dialect implements ConversionContext {
|
||||||
EntityMappingType entityDescriptor,
|
EntityMappingType entityDescriptor,
|
||||||
RuntimeModelCreationContext runtimeModelCreationContext) {
|
RuntimeModelCreationContext runtimeModelCreationContext) {
|
||||||
return new PersistentTableStrategy(
|
return new PersistentTableStrategy(
|
||||||
new IdTable( entityDescriptor, name -> name, this ),
|
new IdTable( entityDescriptor, name -> name, this, runtimeModelCreationContext ),
|
||||||
AfterUseAction.CLEAN,
|
AfterUseAction.CLEAN,
|
||||||
PhysicalIdTableExporter::new,
|
PhysicalIdTableExporter::new,
|
||||||
runtimeModelCreationContext.getSessionFactory()
|
runtimeModelCreationContext.getSessionFactory()
|
||||||
|
|
|
@ -192,7 +192,9 @@ public class H2Dialect extends Dialect {
|
||||||
super.initializeFunctionRegistry( queryEngine );
|
super.initializeFunctionRegistry( queryEngine );
|
||||||
|
|
||||||
// H2 needs an actual argument type for aggregates like SUM, AVG, MIN, MAX to determine the result type
|
// H2 needs an actual argument type for aggregates like SUM, AVG, MIN, MAX to determine the result type
|
||||||
CommonFunctionFactory.aggregates( this, queryEngine, SqlAstNodeRenderingMode.NO_PLAIN_PARAMETER );
|
CommonFunctionFactory.aggregates( this, queryEngine, SqlAstNodeRenderingMode.NO_PLAIN_PARAMETER, "||", null );
|
||||||
|
// AVG by default uses the input type, so we possibly need to cast the argument type, hence a special function
|
||||||
|
CommonFunctionFactory.avg_castingNonDoubleArguments( this, queryEngine, SqlAstNodeRenderingMode.NO_PLAIN_PARAMETER );
|
||||||
|
|
||||||
CommonFunctionFactory.pi( queryEngine );
|
CommonFunctionFactory.pi( queryEngine );
|
||||||
CommonFunctionFactory.cot( queryEngine );
|
CommonFunctionFactory.cot( queryEngine );
|
||||||
|
@ -374,7 +376,7 @@ public class H2Dialect extends Dialect {
|
||||||
EntityMappingType entityDescriptor,
|
EntityMappingType entityDescriptor,
|
||||||
RuntimeModelCreationContext runtimeModelCreationContext) {
|
RuntimeModelCreationContext runtimeModelCreationContext) {
|
||||||
return new LocalTemporaryTableStrategy(
|
return new LocalTemporaryTableStrategy(
|
||||||
new IdTable( entityDescriptor, basename -> "HT_" + basename, this ),
|
new IdTable( entityDescriptor, basename -> "HT_" + basename, this, runtimeModelCreationContext ),
|
||||||
this::getTypeName,
|
this::getTypeName,
|
||||||
AfterUseAction.CLEAN,
|
AfterUseAction.CLEAN,
|
||||||
TempTableDdlTransactionHandling.NONE,
|
TempTableDdlTransactionHandling.NONE,
|
||||||
|
|
|
@ -128,7 +128,7 @@ public class HANAColumnStoreDialect extends AbstractHANADialect {
|
||||||
EntityMappingType entityDescriptor,
|
EntityMappingType entityDescriptor,
|
||||||
RuntimeModelCreationContext runtimeModelCreationContext) {
|
RuntimeModelCreationContext runtimeModelCreationContext) {
|
||||||
return new GlobalTemporaryTableStrategy(
|
return new GlobalTemporaryTableStrategy(
|
||||||
new IdTable( entityDescriptor, basename -> "HT_" + basename, this ),
|
new IdTable( entityDescriptor, basename -> "HT_" + basename, this, runtimeModelCreationContext ),
|
||||||
() -> new PhysicalIdTableExporter() {
|
() -> new PhysicalIdTableExporter() {
|
||||||
@Override
|
@Override
|
||||||
protected String getCreateCommand() {
|
protected String getCreateCommand() {
|
||||||
|
|
|
@ -50,7 +50,7 @@ public class HANARowStoreDialect extends AbstractHANADialect {
|
||||||
EntityMappingType entityDescriptor,
|
EntityMappingType entityDescriptor,
|
||||||
RuntimeModelCreationContext runtimeModelCreationContext) {
|
RuntimeModelCreationContext runtimeModelCreationContext) {
|
||||||
return new GlobalTemporaryTableStrategy(
|
return new GlobalTemporaryTableStrategy(
|
||||||
new IdTable( entityDescriptor, basename -> "HT_" + basename, this ),
|
new IdTable( entityDescriptor, basename -> "HT_" + basename, this, runtimeModelCreationContext ),
|
||||||
() -> new PhysicalIdTableExporter() {
|
() -> new PhysicalIdTableExporter() {
|
||||||
@Override
|
@Override
|
||||||
protected String getCreateCommand() {
|
protected String getCreateCommand() {
|
||||||
|
|
|
@ -55,6 +55,7 @@ import org.hibernate.query.sqm.mutation.internal.idtable.IdTable;
|
||||||
import org.hibernate.query.sqm.mutation.internal.idtable.LocalTemporaryTableStrategy;
|
import org.hibernate.query.sqm.mutation.internal.idtable.LocalTemporaryTableStrategy;
|
||||||
import org.hibernate.query.sqm.mutation.internal.idtable.TempIdTableExporter;
|
import org.hibernate.query.sqm.mutation.internal.idtable.TempIdTableExporter;
|
||||||
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy;
|
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy;
|
||||||
|
import org.hibernate.sql.ast.SqlAstNodeRenderingMode;
|
||||||
import org.hibernate.sql.ast.SqlAstTranslator;
|
import org.hibernate.sql.ast.SqlAstTranslator;
|
||||||
import org.hibernate.sql.ast.SqlAstTranslatorFactory;
|
import org.hibernate.sql.ast.SqlAstTranslatorFactory;
|
||||||
import org.hibernate.sql.ast.spi.SqlAppender;
|
import org.hibernate.sql.ast.spi.SqlAppender;
|
||||||
|
@ -155,6 +156,9 @@ public class HSQLDialect extends Dialect {
|
||||||
public void initializeFunctionRegistry(QueryEngine queryEngine) {
|
public void initializeFunctionRegistry(QueryEngine queryEngine) {
|
||||||
super.initializeFunctionRegistry( queryEngine );
|
super.initializeFunctionRegistry( queryEngine );
|
||||||
|
|
||||||
|
// AVG by default uses the input type, so we possibly need to cast the argument type, hence a special function
|
||||||
|
CommonFunctionFactory.avg_castingNonDoubleArguments( this, queryEngine, SqlAstNodeRenderingMode.DEFAULT );
|
||||||
|
|
||||||
CommonFunctionFactory.cot( queryEngine );
|
CommonFunctionFactory.cot( queryEngine );
|
||||||
CommonFunctionFactory.radians( queryEngine );
|
CommonFunctionFactory.radians( queryEngine );
|
||||||
CommonFunctionFactory.degrees( queryEngine );
|
CommonFunctionFactory.degrees( queryEngine );
|
||||||
|
@ -511,7 +515,7 @@ public class HSQLDialect extends Dialect {
|
||||||
|
|
||||||
if ( version < 200 ) {
|
if ( version < 200 ) {
|
||||||
return new GlobalTemporaryTableStrategy(
|
return new GlobalTemporaryTableStrategy(
|
||||||
new IdTable( rootEntityDescriptor, name -> "HT_" + name, this ),
|
new IdTable( rootEntityDescriptor, name -> "HT_" + name, this, runtimeModelCreationContext ),
|
||||||
() -> new TempIdTableExporter( false, this::getTypeName ),
|
() -> new TempIdTableExporter( false, this::getTypeName ),
|
||||||
// Version 1.8 GLOBAL TEMPORARY table definitions persist beyond the end
|
// Version 1.8 GLOBAL TEMPORARY table definitions persist beyond the end
|
||||||
// of the session (by default, data is cleared at commit).
|
// of the session (by default, data is cleared at commit).
|
||||||
|
@ -523,7 +527,7 @@ public class HSQLDialect extends Dialect {
|
||||||
return new LocalTemporaryTableStrategy(
|
return new LocalTemporaryTableStrategy(
|
||||||
// With HSQLDB 2.0, the table name is qualified with MODULE to assist the drop
|
// With HSQLDB 2.0, the table name is qualified with MODULE to assist the drop
|
||||||
// statement (in-case there is a global name beginning with HT_)
|
// statement (in-case there is a global name beginning with HT_)
|
||||||
new IdTable( rootEntityDescriptor, name -> "MODULE.HT_" + name, this ),
|
new IdTable( rootEntityDescriptor, name -> "MODULE.HT_" + name, this, runtimeModelCreationContext ),
|
||||||
() -> new TempIdTableExporter( true, this::getTypeName ) {
|
() -> new TempIdTableExporter( true, this::getTypeName ) {
|
||||||
@Override
|
@Override
|
||||||
protected String getCreateCommand() {
|
protected String getCreateCommand() {
|
||||||
|
|
|
@ -697,7 +697,7 @@ public class MySQLDialect extends Dialect {
|
||||||
RuntimeModelCreationContext runtimeModelCreationContext) {
|
RuntimeModelCreationContext runtimeModelCreationContext) {
|
||||||
|
|
||||||
return new LocalTemporaryTableStrategy(
|
return new LocalTemporaryTableStrategy(
|
||||||
new IdTable( rootEntityDescriptor, basename -> "HT_" + basename, this ),
|
new IdTable( rootEntityDescriptor, basename -> "HT_" + basename, this, runtimeModelCreationContext ),
|
||||||
() -> new TempIdTableExporter( true, this::getTypeName ) {
|
() -> new TempIdTableExporter( true, this::getTypeName ) {
|
||||||
@Override
|
@Override
|
||||||
protected String getCreateCommand() {
|
protected String getCreateCommand() {
|
||||||
|
|
|
@ -906,7 +906,8 @@ public class OracleDialect extends Dialect {
|
||||||
new IdTable(
|
new IdTable(
|
||||||
rootEntityDescriptor,
|
rootEntityDescriptor,
|
||||||
name -> "HT_" + ( name.length() > 27 ? name.substring( 0, 27 ) : name ),
|
name -> "HT_" + ( name.length() > 27 ? name.substring( 0, 27 ) : name ),
|
||||||
this
|
this,
|
||||||
|
runtimeModelCreationContext
|
||||||
),
|
),
|
||||||
() -> new TempIdTableExporter( false, this::getTypeName ) {
|
() -> new TempIdTableExporter( false, this::getTypeName ) {
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -185,6 +185,9 @@ public class SQLServerDialect extends AbstractTransactSQLDialect {
|
||||||
// For SQL-Server we need to cast certain arguments to varchar(max) to be able to concat them
|
// For SQL-Server we need to cast certain arguments to varchar(max) to be able to concat them
|
||||||
CommonFunctionFactory.aggregates( this, queryEngine, SqlAstNodeRenderingMode.DEFAULT, "+", "varchar(max)" );
|
CommonFunctionFactory.aggregates( this, queryEngine, SqlAstNodeRenderingMode.DEFAULT, "+", "varchar(max)" );
|
||||||
|
|
||||||
|
// AVG by default uses the input type, so we possibly need to cast the argument type, hence a special function
|
||||||
|
CommonFunctionFactory.avg_castingNonDoubleArguments( this, queryEngine, SqlAstNodeRenderingMode.DEFAULT );
|
||||||
|
|
||||||
CommonFunctionFactory.truncate_round( queryEngine );
|
CommonFunctionFactory.truncate_round( queryEngine );
|
||||||
CommonFunctionFactory.everyAny_sumIif( queryEngine );
|
CommonFunctionFactory.everyAny_sumIif( queryEngine );
|
||||||
CommonFunctionFactory.bitLength_pattern( queryEngine, "datalength(?1) * 8" );
|
CommonFunctionFactory.bitLength_pattern( queryEngine, "datalength(?1) * 8" );
|
||||||
|
|
|
@ -301,7 +301,8 @@ public class SybaseASESqlAstTranslator<T extends JdbcOperation> extends Abstract
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visitColumnReference(ColumnReference columnReference) {
|
public void visitColumnReference(ColumnReference columnReference) {
|
||||||
if ( getDmlTargetTableAlias() != null && getDmlTargetTableAlias().equals( columnReference.getQualifier() ) ) {
|
final String dmlTargetTableAlias = getDmlTargetTableAlias();
|
||||||
|
if ( dmlTargetTableAlias != null && dmlTargetTableAlias.equals( columnReference.getQualifier() ) ) {
|
||||||
// Sybase needs a table name prefix
|
// Sybase needs a table name prefix
|
||||||
// but not if this is a restricted union table reference subquery
|
// but not if this is a restricted union table reference subquery
|
||||||
final QuerySpec currentQuerySpec = (QuerySpec) getQueryPartStack().getCurrent();
|
final QuerySpec currentQuerySpec = (QuerySpec) getQueryPartStack().getCurrent();
|
||||||
|
@ -317,11 +318,11 @@ public class SybaseASESqlAstTranslator<T extends JdbcOperation> extends Abstract
|
||||||
// This is fine for now as this is only temporary anyway until we render aliases for table references
|
// This is fine for now as this is only temporary anyway until we render aliases for table references
|
||||||
appendSql(
|
appendSql(
|
||||||
columnReference.getColumnExpression()
|
columnReference.getColumnExpression()
|
||||||
.replaceAll( "(\\b)(" + getDmlTargetTableAlias() + "\\.)(\\b)", "$1$3" )
|
.replaceAll( "(\\b)(" + dmlTargetTableAlias + "\\.)(\\b)", "$1$3" )
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
appendSql( ( (MutationStatement) getStatement() ).getTargetTable().getTableExpression() );
|
appendSql( getCurrentDmlStatement().getTargetTable().getTableExpression() );
|
||||||
appendSql( '.' );
|
appendSql( '.' );
|
||||||
appendSql( columnReference.getColumnExpression() );
|
appendSql( columnReference.getColumnExpression() );
|
||||||
}
|
}
|
||||||
|
|
|
@ -211,6 +211,9 @@ public class SybaseDialect extends AbstractTransactSQLDialect {
|
||||||
// For SQL-Server we need to cast certain arguments to varchar(16384) to be able to concat them
|
// For SQL-Server we need to cast certain arguments to varchar(16384) to be able to concat them
|
||||||
CommonFunctionFactory.aggregates( this, queryEngine, SqlAstNodeRenderingMode.DEFAULT, "+", "varchar(16384)" );
|
CommonFunctionFactory.aggregates( this, queryEngine, SqlAstNodeRenderingMode.DEFAULT, "+", "varchar(16384)" );
|
||||||
|
|
||||||
|
// AVG by default uses the input type, so we possibly need to cast the argument type, hence a special function
|
||||||
|
CommonFunctionFactory.avg_castingNonDoubleArguments( this, queryEngine, SqlAstNodeRenderingMode.DEFAULT );
|
||||||
|
|
||||||
queryEngine.getSqmFunctionRegistry().register( "concat", new SybaseConcatFunction( this, queryEngine.getTypeConfiguration() ) );
|
queryEngine.getSqmFunctionRegistry().register( "concat", new SybaseConcatFunction( this, queryEngine.getTypeConfiguration() ) );
|
||||||
|
|
||||||
//this doesn't work 100% on earlier versions of Sybase
|
//this doesn't work 100% on earlier versions of Sybase
|
||||||
|
|
|
@ -0,0 +1,115 @@
|
||||||
|
/*
|
||||||
|
* Hibernate, Relational Persistence for Idiomatic Java
|
||||||
|
*
|
||||||
|
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
|
||||||
|
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
|
||||||
|
*/
|
||||||
|
package org.hibernate.dialect.function;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.hibernate.dialect.Dialect;
|
||||||
|
import org.hibernate.metamodel.mapping.JdbcMapping;
|
||||||
|
import org.hibernate.query.CastType;
|
||||||
|
import org.hibernate.query.sqm.function.AbstractSqmSelfRenderingFunctionDescriptor;
|
||||||
|
import org.hibernate.query.sqm.function.FunctionKind;
|
||||||
|
import org.hibernate.query.sqm.produce.function.StandardArgumentsValidators;
|
||||||
|
import org.hibernate.query.sqm.produce.function.StandardFunctionReturnTypeResolvers;
|
||||||
|
import org.hibernate.query.sqm.produce.function.internal.PatternRenderer;
|
||||||
|
import org.hibernate.sql.ast.SqlAstNodeRenderingMode;
|
||||||
|
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.Distinct;
|
||||||
|
import org.hibernate.sql.ast.tree.expression.Expression;
|
||||||
|
import org.hibernate.sql.ast.tree.predicate.Predicate;
|
||||||
|
import org.hibernate.type.StandardBasicTypes;
|
||||||
|
import org.hibernate.type.spi.TypeConfiguration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Christian Beikov
|
||||||
|
*/
|
||||||
|
public class AvgFunction extends AbstractSqmSelfRenderingFunctionDescriptor {
|
||||||
|
|
||||||
|
public static final String FUNCTION_NAME = "avg";
|
||||||
|
private final Dialect dialect;
|
||||||
|
private final SqlAstNodeRenderingMode defaultArgumentRenderingMode;
|
||||||
|
private final String doubleCastType;
|
||||||
|
|
||||||
|
public AvgFunction(
|
||||||
|
Dialect dialect,
|
||||||
|
TypeConfiguration typeConfiguration,
|
||||||
|
SqlAstNodeRenderingMode defaultArgumentRenderingMode,
|
||||||
|
String doubleCastType) {
|
||||||
|
super(
|
||||||
|
FUNCTION_NAME,
|
||||||
|
FunctionKind.AGGREGATE,
|
||||||
|
StandardArgumentsValidators.exactly( 1 ),
|
||||||
|
StandardFunctionReturnTypeResolvers.invariant(
|
||||||
|
typeConfiguration.getBasicTypeRegistry().resolve( StandardBasicTypes.DOUBLE )
|
||||||
|
)
|
||||||
|
);
|
||||||
|
this.dialect = dialect;
|
||||||
|
this.defaultArgumentRenderingMode = defaultArgumentRenderingMode;
|
||||||
|
this.doubleCastType = doubleCastType;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void render(SqlAppender sqlAppender, List<SqlAstNode> sqlAstArguments, SqlAstTranslator<?> walker) {
|
||||||
|
render( sqlAppender, sqlAstArguments, null, walker );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void render(
|
||||||
|
SqlAppender sqlAppender,
|
||||||
|
List<SqlAstNode> sqlAstArguments,
|
||||||
|
Predicate filter,
|
||||||
|
SqlAstTranslator<?> translator) {
|
||||||
|
final boolean caseWrapper = filter != null && !translator.supportsFilterClause();
|
||||||
|
sqlAppender.appendSql( "avg(" );
|
||||||
|
final Expression arg;
|
||||||
|
if ( sqlAstArguments.get( 0 ) instanceof Distinct ) {
|
||||||
|
sqlAppender.appendSql( "distinct " );
|
||||||
|
arg = ( (Distinct) sqlAstArguments.get( 0 ) ).getExpression();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
arg = (Expression) sqlAstArguments.get( 0 );
|
||||||
|
}
|
||||||
|
if ( caseWrapper ) {
|
||||||
|
sqlAppender.appendSql( "case when " );
|
||||||
|
filter.accept( translator );
|
||||||
|
sqlAppender.appendSql( " then " );
|
||||||
|
renderArgument( sqlAppender, translator, arg );
|
||||||
|
sqlAppender.appendSql( " else null end)" );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
renderArgument( sqlAppender, translator, arg );
|
||||||
|
sqlAppender.appendSql( ')' );
|
||||||
|
if ( filter != null ) {
|
||||||
|
sqlAppender.appendSql( " filter (where " );
|
||||||
|
filter.accept( translator );
|
||||||
|
sqlAppender.appendSql( ')' );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void renderArgument(SqlAppender sqlAppender, SqlAstTranslator<?> translator, Expression realArg) {
|
||||||
|
final JdbcMapping sourceMapping = realArg.getExpressionType().getJdbcMappings().get( 0 );
|
||||||
|
// Only cast to float/double if this is an integer
|
||||||
|
if ( sourceMapping.getJdbcTypeDescriptor().isInteger() ) {
|
||||||
|
final String cast = dialect.castPattern( sourceMapping.getCastType(), CastType.DOUBLE );
|
||||||
|
new PatternRenderer( cast.replace( "?2", doubleCastType ) )
|
||||||
|
.render( sqlAppender, Collections.singletonList( realArg ), translator );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
translator.render( realArg, defaultArgumentRenderingMode );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getArgumentListSignature() {
|
||||||
|
return "(arg)";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -27,6 +27,7 @@ import org.hibernate.sql.ast.SqlAstNodeRenderingMode;
|
||||||
import org.hibernate.sql.ast.tree.SqlAstNode;
|
import org.hibernate.sql.ast.tree.SqlAstNode;
|
||||||
import org.hibernate.type.BasicType;
|
import org.hibernate.type.BasicType;
|
||||||
import org.hibernate.type.BasicTypeRegistry;
|
import org.hibernate.type.BasicTypeRegistry;
|
||||||
|
import org.hibernate.type.SqlTypes;
|
||||||
import org.hibernate.type.StandardBasicTypes;
|
import org.hibernate.type.StandardBasicTypes;
|
||||||
import org.hibernate.type.spi.TypeConfiguration;
|
import org.hibernate.type.spi.TypeConfiguration;
|
||||||
|
|
||||||
|
@ -1701,13 +1702,6 @@ public class CommonFunctionFactory {
|
||||||
.register();
|
.register();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void aggregates(
|
|
||||||
Dialect dialect,
|
|
||||||
QueryEngine queryEngine,
|
|
||||||
SqlAstNodeRenderingMode inferenceArgumentRenderingMode) {
|
|
||||||
aggregates( dialect, queryEngine, inferenceArgumentRenderingMode, "||", null );
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void aggregates(
|
public static void aggregates(
|
||||||
Dialect dialect,
|
Dialect dialect,
|
||||||
QueryEngine queryEngine,
|
QueryEngine queryEngine,
|
||||||
|
@ -1837,7 +1831,28 @@ public class CommonFunctionFactory {
|
||||||
|
|
||||||
queryEngine.getSqmFunctionRegistry().register(
|
queryEngine.getSqmFunctionRegistry().register(
|
||||||
CountFunction.FUNCTION_NAME,
|
CountFunction.FUNCTION_NAME,
|
||||||
new CountFunction( dialect, queryEngine.getTypeConfiguration(), concatOperator, concatArgumentCastType )
|
new CountFunction(
|
||||||
|
dialect,
|
||||||
|
queryEngine.getTypeConfiguration(),
|
||||||
|
inferenceArgumentRenderingMode,
|
||||||
|
concatOperator,
|
||||||
|
concatArgumentCastType
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void avg_castingNonDoubleArguments(
|
||||||
|
Dialect dialect,
|
||||||
|
QueryEngine queryEngine,
|
||||||
|
SqlAstNodeRenderingMode inferenceArgumentRenderingMode) {
|
||||||
|
queryEngine.getSqmFunctionRegistry().register(
|
||||||
|
AvgFunction.FUNCTION_NAME,
|
||||||
|
new AvgFunction(
|
||||||
|
dialect,
|
||||||
|
queryEngine.getTypeConfiguration(),
|
||||||
|
inferenceArgumentRenderingMode,
|
||||||
|
dialect.getTypeName( SqlTypes.DOUBLE )
|
||||||
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -43,10 +43,16 @@ public class CountFunction extends AbstractSqmSelfRenderingFunctionDescriptor {
|
||||||
|
|
||||||
public static final String FUNCTION_NAME = "count";
|
public static final String FUNCTION_NAME = "count";
|
||||||
private final Dialect dialect;
|
private final Dialect dialect;
|
||||||
|
private final SqlAstNodeRenderingMode defaultArgumentRenderingMode;
|
||||||
private final String concatOperator;
|
private final String concatOperator;
|
||||||
private final String concatArgumentCastType;
|
private final String concatArgumentCastType;
|
||||||
|
|
||||||
public CountFunction(Dialect dialect, TypeConfiguration typeConfiguration, String concatOperator, String concatArgumentCastType) {
|
public CountFunction(
|
||||||
|
Dialect dialect,
|
||||||
|
TypeConfiguration typeConfiguration,
|
||||||
|
SqlAstNodeRenderingMode defaultArgumentRenderingMode,
|
||||||
|
String concatOperator,
|
||||||
|
String concatArgumentCastType) {
|
||||||
super(
|
super(
|
||||||
FUNCTION_NAME,
|
FUNCTION_NAME,
|
||||||
FunctionKind.AGGREGATE,
|
FunctionKind.AGGREGATE,
|
||||||
|
@ -56,6 +62,7 @@ public class CountFunction extends AbstractSqmSelfRenderingFunctionDescriptor {
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
this.dialect = dialect;
|
this.dialect = dialect;
|
||||||
|
this.defaultArgumentRenderingMode = defaultArgumentRenderingMode;
|
||||||
this.concatOperator = concatOperator;
|
this.concatOperator = concatOperator;
|
||||||
this.concatArgumentCastType = concatArgumentCastType;
|
this.concatArgumentCastType = concatArgumentCastType;
|
||||||
}
|
}
|
||||||
|
@ -159,11 +166,11 @@ public class CountFunction extends AbstractSqmSelfRenderingFunctionDescriptor {
|
||||||
filter.accept( translator );
|
filter.accept( translator );
|
||||||
sqlAppender.appendSql( " and " );
|
sqlAppender.appendSql( " and " );
|
||||||
}
|
}
|
||||||
translator.render( expressions.get( 0 ), SqlAstNodeRenderingMode.DEFAULT );
|
translator.render( expressions.get( 0 ), defaultArgumentRenderingMode );
|
||||||
sqlAppender.appendSql( " is not null" );
|
sqlAppender.appendSql( " is not null" );
|
||||||
for ( int i = 1; i < expressions.size(); i++ ) {
|
for ( int i = 1; i < expressions.size(); i++ ) {
|
||||||
sqlAppender.appendSql( " and " );
|
sqlAppender.appendSql( " and " );
|
||||||
translator.render( expressions.get( i ), SqlAstNodeRenderingMode.DEFAULT );
|
translator.render( expressions.get( i ), defaultArgumentRenderingMode );
|
||||||
sqlAppender.appendSql( " is not null" );
|
sqlAppender.appendSql( " is not null" );
|
||||||
}
|
}
|
||||||
sqlAppender.appendSql( " then 1 else null end" );
|
sqlAppender.appendSql( " then 1 else null end" );
|
||||||
|
@ -219,7 +226,7 @@ public class CountFunction extends AbstractSqmSelfRenderingFunctionDescriptor {
|
||||||
}
|
}
|
||||||
// Rendering the tuple will add parenthesis around
|
// Rendering the tuple will add parenthesis around
|
||||||
else if ( requiresParenthesis ) {
|
else if ( requiresParenthesis ) {
|
||||||
translator.render( tuple, SqlAstNodeRenderingMode.DEFAULT );
|
translator.render( tuple, defaultArgumentRenderingMode );
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
renderCommaSeparatedList( sqlAppender, translator, expressions );
|
renderCommaSeparatedList( sqlAppender, translator, expressions );
|
||||||
|
@ -230,10 +237,10 @@ public class CountFunction extends AbstractSqmSelfRenderingFunctionDescriptor {
|
||||||
SqlAppender sqlAppender,
|
SqlAppender sqlAppender,
|
||||||
SqlAstTranslator<?> translator,
|
SqlAstTranslator<?> translator,
|
||||||
List<? extends Expression> expressions) {
|
List<? extends Expression> expressions) {
|
||||||
translator.render( expressions.get( 0 ), SqlAstNodeRenderingMode.DEFAULT );
|
translator.render( expressions.get( 0 ), defaultArgumentRenderingMode );
|
||||||
for ( int i = 1; i < expressions.size(); i++ ) {
|
for ( int i = 1; i < expressions.size(); i++ ) {
|
||||||
sqlAppender.appendSql( ',' );
|
sqlAppender.appendSql( ',' );
|
||||||
translator.render( expressions.get( i ), SqlAstNodeRenderingMode.DEFAULT );
|
translator.render( expressions.get( i ), defaultArgumentRenderingMode );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -251,24 +258,24 @@ public class CountFunction extends AbstractSqmSelfRenderingFunctionDescriptor {
|
||||||
sqlAppender.appendSql( "1" );
|
sqlAppender.appendSql( "1" );
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
translator.render( realArg, SqlAstNodeRenderingMode.DEFAULT );
|
translator.render( realArg, defaultArgumentRenderingMode );
|
||||||
}
|
}
|
||||||
sqlAppender.appendSql( " else null end" );
|
sqlAppender.appendSql( " else null end" );
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
translator.render( realArg, SqlAstNodeRenderingMode.DEFAULT );
|
translator.render( realArg, defaultArgumentRenderingMode );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void renderCastedArgument(SqlAppender sqlAppender, SqlAstTranslator<?> translator, Expression realArg) {
|
private void renderCastedArgument(SqlAppender sqlAppender, SqlAstTranslator<?> translator, Expression realArg) {
|
||||||
if ( concatArgumentCastType == null ) {
|
if ( concatArgumentCastType == null ) {
|
||||||
translator.render( realArg, SqlAstNodeRenderingMode.DEFAULT );
|
translator.render( realArg, defaultArgumentRenderingMode );
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
final JdbcMapping sourceMapping = realArg.getExpressionType().getJdbcMappings().get( 0 );
|
final JdbcMapping sourceMapping = realArg.getExpressionType().getJdbcMappings().get( 0 );
|
||||||
// No need to cast if we already have a string
|
// No need to cast if we already have a string
|
||||||
if ( sourceMapping.getCastType() == CastType.STRING ) {
|
if ( sourceMapping.getCastType() == CastType.STRING ) {
|
||||||
translator.render( realArg, SqlAstNodeRenderingMode.DEFAULT );
|
translator.render( realArg, defaultArgumentRenderingMode );
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
final String cast = dialect.castPattern( sourceMapping.getCastType(), CastType.STRING );
|
final String cast = dialect.castPattern( sourceMapping.getCastType(), CastType.STRING );
|
||||||
|
|
|
@ -46,4 +46,8 @@ public class CurrentFunction
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean alwaysIncludesParentheses() {
|
||||||
|
return sql.indexOf( '(' ) != -1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -91,7 +91,7 @@ public class NormalizingIdentifierHelperImpl implements IdentifierHelper {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Identifier applyGlobalQuoting(String text) {
|
public Identifier applyGlobalQuoting(String text) {
|
||||||
return Identifier.toIdentifier( text, globallyQuoteIdentifiers && !globallyQuoteIdentifiersSkipColumnDefinitions );
|
return Identifier.toIdentifier( text, globallyQuoteIdentifiers && !globallyQuoteIdentifiersSkipColumnDefinitions, false );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -0,0 +1,141 @@
|
||||||
|
/*
|
||||||
|
* Hibernate, Relational Persistence for Idiomatic Java
|
||||||
|
*
|
||||||
|
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||||
|
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||||
|
*/
|
||||||
|
package org.hibernate.internal.util;
|
||||||
|
|
||||||
|
public final class QuotingHelper {
|
||||||
|
|
||||||
|
private QuotingHelper() { /* static methods only - hide constructor */
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String unquoteIdentifier(String text) {
|
||||||
|
final int end = text.length() - 1;
|
||||||
|
assert text.charAt( 0 ) == '`' && text.charAt( end ) == '`';
|
||||||
|
// Unquote a parsed quoted identifier and handle escape sequences
|
||||||
|
final StringBuilder sb = new StringBuilder( text.length() - 2 );
|
||||||
|
for ( int i = 1; i < end; i++ ) {
|
||||||
|
char c = text.charAt( i );
|
||||||
|
switch ( c ) {
|
||||||
|
case '\\':
|
||||||
|
if ( ( i + 1 ) < end ) {
|
||||||
|
char nextChar = text.charAt( ++i );
|
||||||
|
switch ( nextChar ) {
|
||||||
|
case 'b':
|
||||||
|
c = '\b';
|
||||||
|
break;
|
||||||
|
case 't':
|
||||||
|
c = '\t';
|
||||||
|
break;
|
||||||
|
case 'n':
|
||||||
|
c = '\n';
|
||||||
|
break;
|
||||||
|
case 'f':
|
||||||
|
c = '\f';
|
||||||
|
break;
|
||||||
|
case 'r':
|
||||||
|
c = '\r';
|
||||||
|
break;
|
||||||
|
case '\\':
|
||||||
|
c = '\\';
|
||||||
|
break;
|
||||||
|
case '\'':
|
||||||
|
c = '\'';
|
||||||
|
break;
|
||||||
|
case '"':
|
||||||
|
c = '"';
|
||||||
|
break;
|
||||||
|
case '`':
|
||||||
|
c = '`';
|
||||||
|
break;
|
||||||
|
case 'u':
|
||||||
|
c = (char) Integer.parseInt( text.substring( i + 1, i + 5 ), 16 );
|
||||||
|
i += 4;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
sb.append( '\\' );
|
||||||
|
c = nextChar;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
sb.append( c );
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String unquoteStringLiteral(String text) {
|
||||||
|
assert text.length() > 1;
|
||||||
|
final int end = text.length() - 1;
|
||||||
|
final char delimiter = text.charAt( 0 );
|
||||||
|
assert delimiter == text.charAt( end );
|
||||||
|
// Unescape the parsed literal and handle escape sequences
|
||||||
|
final StringBuilder sb = new StringBuilder( text.length() - 2 );
|
||||||
|
for ( int i = 1; i < end; i++ ) {
|
||||||
|
char c = text.charAt( i );
|
||||||
|
switch ( c ) {
|
||||||
|
case '\'':
|
||||||
|
if ( delimiter == '\'' ) {
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case '"':
|
||||||
|
if ( delimiter == '"' ) {
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case '\\':
|
||||||
|
if ( ( i + 1 ) < end ) {
|
||||||
|
char nextChar = text.charAt( ++i );
|
||||||
|
switch ( nextChar ) {
|
||||||
|
case 'b':
|
||||||
|
c = '\b';
|
||||||
|
break;
|
||||||
|
case 't':
|
||||||
|
c = '\t';
|
||||||
|
break;
|
||||||
|
case 'n':
|
||||||
|
c = '\n';
|
||||||
|
break;
|
||||||
|
case 'f':
|
||||||
|
c = '\f';
|
||||||
|
break;
|
||||||
|
case 'r':
|
||||||
|
c = '\r';
|
||||||
|
break;
|
||||||
|
case '\\':
|
||||||
|
c = '\\';
|
||||||
|
break;
|
||||||
|
case '\'':
|
||||||
|
c = '\'';
|
||||||
|
break;
|
||||||
|
case '"':
|
||||||
|
c = '"';
|
||||||
|
break;
|
||||||
|
case '`':
|
||||||
|
c = '`';
|
||||||
|
break;
|
||||||
|
case 'u':
|
||||||
|
c = (char) Integer.parseInt( text.substring( i + 1, i + 5 ), 16 );
|
||||||
|
i += 4;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
sb.append( '\\' );
|
||||||
|
c = nextChar;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
sb.append( c );
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
}
|
|
@ -29,7 +29,7 @@ public final class ArrayHelper {
|
||||||
|
|
||||||
public static int indexOf(Object[] array, Object object) {
|
public static int indexOf(Object[] array, Object object) {
|
||||||
for ( int i = 0; i < array.length; i++ ) {
|
for ( int i = 0; i < array.length; i++ ) {
|
||||||
if ( array[i].equals( object ) ) {
|
if ( object.equals( array[i] ) ) {
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
*/
|
*/
|
||||||
package org.hibernate.loader.ast.internal;
|
package org.hibernate.loader.ast.internal;
|
||||||
|
|
||||||
|
import java.util.AbstractMap;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
@ -251,7 +252,7 @@ public class LoaderSelectBuilder {
|
||||||
private final EntityGraphTraversalState entityGraphTraversalState;
|
private final EntityGraphTraversalState entityGraphTraversalState;
|
||||||
|
|
||||||
private int fetchDepth;
|
private int fetchDepth;
|
||||||
private Map<OrderByFragment, TableGroup> orderByFragments;
|
private List<Map.Entry<OrderByFragment, TableGroup>> orderByFragments;
|
||||||
private boolean hasCollectionJoinFetches;
|
private boolean hasCollectionJoinFetches;
|
||||||
private String currentBagRole;
|
private String currentBagRole;
|
||||||
|
|
||||||
|
@ -483,9 +484,9 @@ public class LoaderSelectBuilder {
|
||||||
|
|
||||||
if ( orderByFragments != null ) {
|
if ( orderByFragments != null ) {
|
||||||
orderByFragments.forEach(
|
orderByFragments.forEach(
|
||||||
(orderByFragment, tableGroup) -> orderByFragment.apply(
|
entry -> entry.getKey().apply(
|
||||||
rootQuerySpec,
|
rootQuerySpec,
|
||||||
tableGroup,
|
entry.getValue(),
|
||||||
sqlAstCreationState
|
sqlAstCreationState
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
@ -652,9 +653,9 @@ public class LoaderSelectBuilder {
|
||||||
|
|
||||||
private void applyOrdering(TableGroup tableGroup, OrderByFragment orderByFragment) {
|
private void applyOrdering(TableGroup tableGroup, OrderByFragment orderByFragment) {
|
||||||
if ( orderByFragments == null ) {
|
if ( orderByFragments == null ) {
|
||||||
orderByFragments = new LinkedHashMap<>();
|
orderByFragments = new ArrayList<>();
|
||||||
}
|
}
|
||||||
orderByFragments.put( orderByFragment, tableGroup );
|
orderByFragments.add( new AbstractMap.SimpleEntry<>( orderByFragment, tableGroup ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<Fetch> visitFetches(
|
private List<Fetch> visitFetches(
|
||||||
|
@ -1010,13 +1011,14 @@ public class LoaderSelectBuilder {
|
||||||
if ( jdbcTypeCount == 1 ) {
|
if ( jdbcTypeCount == 1 ) {
|
||||||
assert fkDescriptor instanceof SimpleForeignKeyDescriptor;
|
assert fkDescriptor instanceof SimpleForeignKeyDescriptor;
|
||||||
final SimpleForeignKeyDescriptor simpleFkDescriptor = (SimpleForeignKeyDescriptor) fkDescriptor;
|
final SimpleForeignKeyDescriptor simpleFkDescriptor = (SimpleForeignKeyDescriptor) fkDescriptor;
|
||||||
|
final TableReference tableReference = rootTableGroup.resolveTableReference(
|
||||||
|
navigablePath,
|
||||||
|
simpleFkDescriptor.getContainingTableExpression()
|
||||||
|
);
|
||||||
fkExpression = sqlAstCreationState.getSqlExpressionResolver().resolveSqlExpression(
|
fkExpression = sqlAstCreationState.getSqlExpressionResolver().resolveSqlExpression(
|
||||||
createColumnReferenceKey(
|
createColumnReferenceKey( tableReference, simpleFkDescriptor.getSelectionExpression() ),
|
||||||
simpleFkDescriptor.getContainingTableExpression(),
|
|
||||||
simpleFkDescriptor.getSelectionExpression()
|
|
||||||
),
|
|
||||||
sqlAstProcessingState -> new ColumnReference(
|
sqlAstProcessingState -> new ColumnReference(
|
||||||
rootTableGroup.resolveTableReference( navigablePath, simpleFkDescriptor.getContainingTableExpression() ),
|
tableReference,
|
||||||
simpleFkDescriptor.getSelectionExpression(),
|
simpleFkDescriptor.getSelectionExpression(),
|
||||||
false,
|
false,
|
||||||
null,
|
null,
|
||||||
|
@ -1029,21 +1031,26 @@ public class LoaderSelectBuilder {
|
||||||
else {
|
else {
|
||||||
final List<ColumnReference> columnReferences = new ArrayList<>( jdbcTypeCount );
|
final List<ColumnReference> columnReferences = new ArrayList<>( jdbcTypeCount );
|
||||||
fkDescriptor.forEachSelectable(
|
fkDescriptor.forEachSelectable(
|
||||||
(columnIndex, selection) ->
|
(columnIndex, selection) -> {
|
||||||
columnReferences.add(
|
final TableReference tableReference = rootTableGroup.resolveTableReference(
|
||||||
(ColumnReference) sqlAstCreationState.getSqlExpressionResolver()
|
navigablePath,
|
||||||
.resolveSqlExpression(
|
selection.getContainingTableExpression()
|
||||||
createColumnReferenceKey(
|
);
|
||||||
selection.getContainingTableExpression(),
|
columnReferences.add(
|
||||||
selection.getSelectionExpression()
|
(ColumnReference) sqlAstCreationState.getSqlExpressionResolver()
|
||||||
),
|
.resolveSqlExpression(
|
||||||
sqlAstProcessingState -> new ColumnReference(
|
createColumnReferenceKey(
|
||||||
rootTableGroup.resolveTableReference( navigablePath, selection.getContainingTableExpression() ),
|
tableReference,
|
||||||
selection,
|
selection.getSelectionExpression()
|
||||||
this.creationContext.getSessionFactory()
|
),
|
||||||
)
|
sqlAstProcessingState -> new ColumnReference(
|
||||||
)
|
tableReference,
|
||||||
)
|
selection,
|
||||||
|
this.creationContext.getSessionFactory()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
fkExpression = new SqlTuple( columnReferences, fkDescriptor );
|
fkExpression = new SqlTuple( columnReferences, fkDescriptor );
|
||||||
|
|
|
@ -542,6 +542,22 @@ public abstract class PersistentClass implements AttributeContainer, Serializabl
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Property getSubclassProperty(String propertyName) throws MappingException {
|
||||||
|
Property identifierProperty = getIdentifierProperty();
|
||||||
|
if ( identifierProperty != null
|
||||||
|
&& identifierProperty.getName().equals( StringHelper.root( propertyName ) ) ) {
|
||||||
|
return identifierProperty;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Iterator<Property> iter = getSubclassPropertyClosureIterator();
|
||||||
|
Component identifierMapper = getIdentifierMapper();
|
||||||
|
if ( identifierMapper != null ) {
|
||||||
|
iter = new JoinedIterator<>( identifierMapper.getPropertyIterator(), iter );
|
||||||
|
}
|
||||||
|
return getProperty( propertyName, iter );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check to see if this PersistentClass defines a property with the given name.
|
* Check to see if this PersistentClass defines a property with the given name.
|
||||||
*
|
*
|
||||||
|
|
|
@ -7,11 +7,12 @@
|
||||||
package org.hibernate.metamodel.mapping;
|
package org.hibernate.metamodel.mapping;
|
||||||
|
|
||||||
import org.hibernate.sql.results.graph.Fetchable;
|
import org.hibernate.sql.results.graph.Fetchable;
|
||||||
|
import org.hibernate.type.descriptor.java.JavaTypedExpressable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
||||||
*/
|
*/
|
||||||
public interface CollectionPart extends ModelPart, Fetchable {
|
public interface CollectionPart extends ModelPart, Fetchable, JavaTypedExpressable {
|
||||||
enum Nature {
|
enum Nature {
|
||||||
ELEMENT( "{element}" ),
|
ELEMENT( "{element}" ),
|
||||||
INDEX( "{index}" ),
|
INDEX( "{index}" ),
|
||||||
|
|
|
@ -57,7 +57,7 @@ public class MappingModelHelper {
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
colRef = (ColumnReference) sqlExpressionResolver.resolveSqlExpression(
|
colRef = (ColumnReference) sqlExpressionResolver.resolveSqlExpression(
|
||||||
createColumnReferenceKey( selection.getContainingTableExpression(), selection.getSelectionExpression() ),
|
createColumnReferenceKey( qualifier, selection.getSelectionExpression() ),
|
||||||
sqlAstProcessingState -> new ColumnReference(
|
sqlAstProcessingState -> new ColumnReference(
|
||||||
qualifier,
|
qualifier,
|
||||||
selection,
|
selection,
|
||||||
|
@ -89,7 +89,7 @@ public class MappingModelHelper {
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return sqlExpressionResolver.resolveSqlExpression(
|
return sqlExpressionResolver.resolveSqlExpression(
|
||||||
createColumnReferenceKey( basicPart.getContainingTableExpression(), basicPart.getSelectionExpression() ),
|
createColumnReferenceKey( qualifier, basicPart.getSelectionExpression() ),
|
||||||
sqlAstProcessingState -> new ColumnReference(
|
sqlAstProcessingState -> new ColumnReference(
|
||||||
qualifier,
|
qualifier,
|
||||||
basicPart,
|
basicPart,
|
||||||
|
|
|
@ -61,7 +61,7 @@ public abstract class AbstractDomainPath implements DomainPath {
|
||||||
final TableReference tableReference = tableGroup.resolveTableReference( getNavigablePath(), selection.getContainingTableExpression() );
|
final TableReference tableReference = tableGroup.resolveTableReference( getNavigablePath(), selection.getContainingTableExpression() );
|
||||||
return creationState.getSqlExpressionResolver().resolveSqlExpression(
|
return creationState.getSqlExpressionResolver().resolveSqlExpression(
|
||||||
SqlExpressionResolver.createColumnReferenceKey(
|
SqlExpressionResolver.createColumnReferenceKey(
|
||||||
selection.getContainingTableExpression(),
|
tableReference,
|
||||||
selection.getSelectionExpression()
|
selection.getSelectionExpression()
|
||||||
),
|
),
|
||||||
sqlAstProcessingState -> new ColumnReference(
|
sqlAstProcessingState -> new ColumnReference(
|
||||||
|
@ -247,7 +247,7 @@ public abstract class AbstractDomainPath implements DomainPath {
|
||||||
final TableReference tableReference = tableGroup.resolveTableReference( getNavigablePath(), selection.getContainingTableExpression() );
|
final TableReference tableReference = tableGroup.resolveTableReference( getNavigablePath(), selection.getContainingTableExpression() );
|
||||||
final Expression expression = creationState.getSqlExpressionResolver().resolveSqlExpression(
|
final Expression expression = creationState.getSqlExpressionResolver().resolveSqlExpression(
|
||||||
SqlExpressionResolver.createColumnReferenceKey(
|
SqlExpressionResolver.createColumnReferenceKey(
|
||||||
selection.getContainingTableExpression(),
|
tableReference,
|
||||||
selection.getSelectionExpression()
|
selection.getSelectionExpression()
|
||||||
),
|
),
|
||||||
sqlAstProcessingState -> new ColumnReference(
|
sqlAstProcessingState -> new ColumnReference(
|
||||||
|
|
|
@ -10,7 +10,6 @@ import java.io.Serializable;
|
||||||
import java.util.function.BiConsumer;
|
import java.util.function.BiConsumer;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
import org.hibernate.NotYetImplementedFor6Exception;
|
|
||||||
import org.hibernate.SharedSessionContract;
|
import org.hibernate.SharedSessionContract;
|
||||||
import org.hibernate.engine.FetchStyle;
|
import org.hibernate.engine.FetchStyle;
|
||||||
import org.hibernate.engine.FetchTiming;
|
import org.hibernate.engine.FetchTiming;
|
||||||
|
@ -33,8 +32,17 @@ import org.hibernate.metamodel.model.domain.NavigableRole;
|
||||||
import org.hibernate.property.access.spi.PropertyAccess;
|
import org.hibernate.property.access.spi.PropertyAccess;
|
||||||
import org.hibernate.query.NavigablePath;
|
import org.hibernate.query.NavigablePath;
|
||||||
import org.hibernate.sql.ast.Clause;
|
import org.hibernate.sql.ast.Clause;
|
||||||
|
import org.hibernate.sql.ast.SqlAstJoinType;
|
||||||
|
import org.hibernate.sql.ast.spi.FromClauseAccess;
|
||||||
|
import org.hibernate.sql.ast.spi.SqlAliasBaseGenerator;
|
||||||
|
import org.hibernate.sql.ast.spi.SqlAstCreationContext;
|
||||||
|
import org.hibernate.sql.ast.spi.SqlExpressionResolver;
|
||||||
import org.hibernate.sql.ast.spi.SqlSelection;
|
import org.hibernate.sql.ast.spi.SqlSelection;
|
||||||
|
import org.hibernate.sql.ast.tree.from.StandardVirtualTableGroup;
|
||||||
import org.hibernate.sql.ast.tree.from.TableGroup;
|
import org.hibernate.sql.ast.tree.from.TableGroup;
|
||||||
|
import org.hibernate.sql.ast.tree.from.TableGroupJoin;
|
||||||
|
import org.hibernate.sql.ast.tree.from.TableGroupJoinProducer;
|
||||||
|
import org.hibernate.sql.ast.tree.predicate.Predicate;
|
||||||
import org.hibernate.sql.results.graph.DomainResult;
|
import org.hibernate.sql.results.graph.DomainResult;
|
||||||
import org.hibernate.sql.results.graph.DomainResultCreationState;
|
import org.hibernate.sql.results.graph.DomainResultCreationState;
|
||||||
import org.hibernate.sql.results.graph.Fetch;
|
import org.hibernate.sql.results.graph.Fetch;
|
||||||
|
@ -53,7 +61,7 @@ import org.hibernate.type.descriptor.java.MutabilityPlan;
|
||||||
*/
|
*/
|
||||||
public class DiscriminatedAssociationAttributeMapping
|
public class DiscriminatedAssociationAttributeMapping
|
||||||
extends AbstractSingularAttributeMapping
|
extends AbstractSingularAttributeMapping
|
||||||
implements DiscriminatedAssociationModelPart {
|
implements DiscriminatedAssociationModelPart, TableGroupJoinProducer {
|
||||||
private final NavigableRole navigableRole;
|
private final NavigableRole navigableRole;
|
||||||
private final DiscriminatedAssociationMapping discriminatorMapping;
|
private final DiscriminatedAssociationMapping discriminatorMapping;
|
||||||
private final SessionFactoryImplementor sessionFactory;
|
private final SessionFactoryImplementor sessionFactory;
|
||||||
|
@ -141,17 +149,24 @@ public class DiscriminatedAssociationAttributeMapping
|
||||||
TableGroup tableGroup,
|
TableGroup tableGroup,
|
||||||
String resultVariable,
|
String resultVariable,
|
||||||
DomainResultCreationState creationState) {
|
DomainResultCreationState creationState) {
|
||||||
throw new NotYetImplementedFor6Exception( getClass() );
|
return discriminatorMapping.createDomainResult(
|
||||||
|
navigablePath,
|
||||||
|
tableGroup,
|
||||||
|
resultVariable,
|
||||||
|
creationState
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void applySqlSelections(NavigablePath navigablePath, TableGroup tableGroup, DomainResultCreationState creationState) {
|
public void applySqlSelections(NavigablePath navigablePath, TableGroup tableGroup, DomainResultCreationState creationState) {
|
||||||
throw new NotYetImplementedFor6Exception( getClass() );
|
discriminatorMapping.getDiscriminatorPart().applySqlSelections( navigablePath, tableGroup, creationState );
|
||||||
|
discriminatorMapping.getKeyPart().applySqlSelections( navigablePath, tableGroup, creationState );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void applySqlSelections(NavigablePath navigablePath, TableGroup tableGroup, DomainResultCreationState creationState, BiConsumer<SqlSelection, JdbcMapping> selectionConsumer) {
|
public void applySqlSelections(NavigablePath navigablePath, TableGroup tableGroup, DomainResultCreationState creationState, BiConsumer<SqlSelection, JdbcMapping> selectionConsumer) {
|
||||||
throw new NotYetImplementedFor6Exception( getClass() );
|
discriminatorMapping.getDiscriminatorPart().applySqlSelections( navigablePath, tableGroup, creationState, selectionConsumer );
|
||||||
|
discriminatorMapping.getKeyPart().applySqlSelections( navigablePath, tableGroup, creationState, selectionConsumer );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -361,4 +376,62 @@ public class DiscriminatedAssociationAttributeMapping
|
||||||
return anyType.assemble( cached, persistenceContext, null );
|
return anyType.assemble( cached, persistenceContext, null );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TableGroupJoin createTableGroupJoin(
|
||||||
|
NavigablePath navigablePath,
|
||||||
|
TableGroup lhs,
|
||||||
|
String explicitSourceAlias,
|
||||||
|
SqlAstJoinType sqlAstJoinType,
|
||||||
|
boolean fetched,
|
||||||
|
boolean addsPredicate,
|
||||||
|
SqlAliasBaseGenerator aliasBaseGenerator,
|
||||||
|
SqlExpressionResolver sqlExpressionResolver,
|
||||||
|
FromClauseAccess fromClauseAccess,
|
||||||
|
SqlAstCreationContext creationContext) {
|
||||||
|
final TableGroup tableGroup = createRootTableGroupJoin(
|
||||||
|
navigablePath,
|
||||||
|
lhs,
|
||||||
|
explicitSourceAlias,
|
||||||
|
sqlAstJoinType,
|
||||||
|
fetched,
|
||||||
|
null,
|
||||||
|
aliasBaseGenerator,
|
||||||
|
sqlExpressionResolver,
|
||||||
|
fromClauseAccess,
|
||||||
|
creationContext
|
||||||
|
);
|
||||||
|
|
||||||
|
return new TableGroupJoin( navigablePath, sqlAstJoinType, tableGroup );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TableGroup createRootTableGroupJoin(
|
||||||
|
NavigablePath navigablePath,
|
||||||
|
TableGroup lhs,
|
||||||
|
String explicitSourceAlias,
|
||||||
|
SqlAstJoinType sqlAstJoinType,
|
||||||
|
boolean fetched,
|
||||||
|
Consumer<Predicate> predicateConsumer,
|
||||||
|
SqlAliasBaseGenerator aliasBaseGenerator,
|
||||||
|
SqlExpressionResolver sqlExpressionResolver,
|
||||||
|
FromClauseAccess fromClauseAccess,
|
||||||
|
SqlAstCreationContext creationContext) {
|
||||||
|
return new StandardVirtualTableGroup(
|
||||||
|
navigablePath,
|
||||||
|
this,
|
||||||
|
lhs,
|
||||||
|
fetched
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SqlAstJoinType getDefaultSqlAstJoinType(TableGroup parentTableGroup) {
|
||||||
|
return SqlAstJoinType.LEFT;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getSqlAliasStem() {
|
||||||
|
return getAttributeName();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -144,6 +144,11 @@ public class DiscriminatedCollectionPart implements DiscriminatedAssociationMode
|
||||||
return discriminatorMapping.getJavaTypeDescriptor();
|
return discriminatorMapping.getJavaTypeDescriptor();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JavaType<?> getExpressableJavaTypeDescriptor() {
|
||||||
|
return getJavaTypeDescriptor();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public NavigableRole getNavigableRole() {
|
public NavigableRole getNavigableRole() {
|
||||||
return partRole;
|
return partRole;
|
||||||
|
|
|
@ -42,6 +42,7 @@ import org.hibernate.sql.ast.tree.from.PluralTableGroup;
|
||||||
import org.hibernate.sql.ast.tree.from.StandardVirtualTableGroup;
|
import org.hibernate.sql.ast.tree.from.StandardVirtualTableGroup;
|
||||||
import org.hibernate.sql.ast.tree.from.TableGroup;
|
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.predicate.Predicate;
|
import org.hibernate.sql.ast.tree.predicate.Predicate;
|
||||||
import org.hibernate.sql.results.graph.DomainResult;
|
import org.hibernate.sql.results.graph.DomainResult;
|
||||||
import org.hibernate.sql.results.graph.DomainResultCreationState;
|
import org.hibernate.sql.results.graph.DomainResultCreationState;
|
||||||
|
@ -199,15 +200,16 @@ public class EmbeddedCollectionPart implements CollectionPart, EmbeddableValuedF
|
||||||
getEmbeddableTypeDescriptor().forEachSelectable(
|
getEmbeddableTypeDescriptor().forEachSelectable(
|
||||||
(columnIndex, selection) -> {
|
(columnIndex, selection) -> {
|
||||||
assert containingTableExpression.equals( selection.getContainingTableExpression() );
|
assert containingTableExpression.equals( selection.getContainingTableExpression() );
|
||||||
|
final TableReference tableReference = tableGroup.resolveTableReference(
|
||||||
|
tableGroup.getNavigablePath()
|
||||||
|
.append( getNavigableRole().getNavigableName() ),
|
||||||
|
selection.getContainingTableExpression()
|
||||||
|
);
|
||||||
expressions.add(
|
expressions.add(
|
||||||
sqlExpressionResolver.resolveSqlExpression(
|
sqlExpressionResolver.resolveSqlExpression(
|
||||||
SqlExpressionResolver.createColumnReferenceKey( selection.getContainingTableExpression(), selection.getSelectionExpression() ),
|
SqlExpressionResolver.createColumnReferenceKey( tableReference, selection.getSelectionExpression() ),
|
||||||
sqlAstProcessingState -> new ColumnReference(
|
sqlAstProcessingState -> new ColumnReference(
|
||||||
tableGroup.resolveTableReference(
|
tableReference,
|
||||||
tableGroup.getNavigablePath()
|
|
||||||
.append( getNavigableRole().getNavigableName() ),
|
|
||||||
selection.getContainingTableExpression()
|
|
||||||
),
|
|
||||||
selection,
|
selection,
|
||||||
sqlAstCreationState.getCreationContext().getSessionFactory()
|
sqlAstCreationState.getCreationContext().getSessionFactory()
|
||||||
)
|
)
|
||||||
|
@ -300,6 +302,11 @@ public class EmbeddedCollectionPart implements CollectionPart, EmbeddableValuedF
|
||||||
return getEmbeddableTypeDescriptor().getJavaTypeDescriptor();
|
return getEmbeddableTypeDescriptor().getJavaTypeDescriptor();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JavaType<?> getExpressableJavaTypeDescriptor() {
|
||||||
|
return getJavaTypeDescriptor();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public NavigableRole getNavigableRole() {
|
public NavigableRole getNavigableRole() {
|
||||||
return navigableRole;
|
return navigableRole;
|
||||||
|
|
|
@ -359,6 +359,11 @@ public class EntityCollectionPart
|
||||||
return getEntityMappingType().getJavaTypeDescriptor();
|
return getEntityMappingType().getJavaTypeDescriptor();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JavaType<?> getExpressableJavaTypeDescriptor() {
|
||||||
|
return getJavaTypeDescriptor();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public NavigableRole getNavigableRole() {
|
public NavigableRole getNavigableRole() {
|
||||||
return navigableRole;
|
return navigableRole;
|
||||||
|
|
|
@ -10,7 +10,6 @@ import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import org.hibernate.NotYetImplementedFor6Exception;
|
|
||||||
import org.hibernate.metamodel.mapping.MappingModelCreationLogger;
|
import org.hibernate.metamodel.mapping.MappingModelCreationLogger;
|
||||||
import org.hibernate.metamodel.mapping.ModelPart;
|
import org.hibernate.metamodel.mapping.ModelPart;
|
||||||
import org.hibernate.metamodel.mapping.NonTransientException;
|
import org.hibernate.metamodel.mapping.NonTransientException;
|
||||||
|
|
|
@ -31,6 +31,7 @@ import org.hibernate.metamodel.mapping.JdbcMapping;
|
||||||
import org.hibernate.metamodel.mapping.ManagedMappingType;
|
import org.hibernate.metamodel.mapping.ManagedMappingType;
|
||||||
import org.hibernate.metamodel.mapping.ModelPart;
|
import org.hibernate.metamodel.mapping.ModelPart;
|
||||||
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
|
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
|
||||||
|
import org.hibernate.metamodel.mapping.Queryable;
|
||||||
import org.hibernate.metamodel.mapping.StateArrayContributorMetadataAccess;
|
import org.hibernate.metamodel.mapping.StateArrayContributorMetadataAccess;
|
||||||
import org.hibernate.metamodel.mapping.ordering.OrderByFragment;
|
import org.hibernate.metamodel.mapping.ordering.OrderByFragment;
|
||||||
import org.hibernate.metamodel.mapping.ordering.OrderByFragmentTranslator;
|
import org.hibernate.metamodel.mapping.ordering.OrderByFragmentTranslator;
|
||||||
|
@ -212,7 +213,7 @@ public class PluralAttributeMappingImpl
|
||||||
final boolean hasManyToManyOrder = bootDescriptor.getManyToManyOrdering() != null;
|
final boolean hasManyToManyOrder = bootDescriptor.getManyToManyOrdering() != null;
|
||||||
|
|
||||||
if ( hasOrder || hasManyToManyOrder ) {
|
if ( hasOrder || hasManyToManyOrder ) {
|
||||||
final TranslationContext context = () -> collectionDescriptor.getFactory().getSessionFactoryOptions().getJpaCompliance();
|
final TranslationContext context = collectionDescriptor::getFactory;
|
||||||
|
|
||||||
if ( hasOrder ) {
|
if ( hasOrder ) {
|
||||||
if ( log.isDebugEnabled() ) {
|
if ( log.isDebugEnabled() ) {
|
||||||
|
@ -796,29 +797,22 @@ public class PluralAttributeMappingImpl
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ModelPart findSubPart(String name, EntityMappingType treatTargetType) {
|
public ModelPart findSubPart(String name, EntityMappingType treatTargetType) {
|
||||||
|
if ( elementDescriptor instanceof Queryable ) {
|
||||||
|
final ModelPart subPart = ( (Queryable) elementDescriptor ).findSubPart( name, null );
|
||||||
|
if ( subPart != null ) {
|
||||||
|
return subPart;
|
||||||
|
}
|
||||||
|
}
|
||||||
final CollectionPart.Nature nature = CollectionPart.Nature.fromName( name );
|
final CollectionPart.Nature nature = CollectionPart.Nature.fromName( name );
|
||||||
if ( nature == CollectionPart.Nature.ELEMENT ) {
|
if ( nature != null ) {
|
||||||
return elementDescriptor;
|
switch ( nature ) {
|
||||||
}
|
case ELEMENT:
|
||||||
|
return elementDescriptor;
|
||||||
if ( nature == CollectionPart.Nature.INDEX ) {
|
case INDEX:
|
||||||
return indexDescriptor;
|
return indexDescriptor;
|
||||||
}
|
case ID:
|
||||||
|
return identifierDescriptor;
|
||||||
if ( nature == CollectionPart.Nature.ID ) {
|
}
|
||||||
return identifierDescriptor;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( elementDescriptor instanceof EntityCollectionPart ) {
|
|
||||||
return ( (EntityCollectionPart) elementDescriptor ).findSubPart( name );
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( elementDescriptor instanceof EmbeddedCollectionPart ) {
|
|
||||||
return ( (EmbeddedCollectionPart) elementDescriptor ).findSubPart( name, treatTargetType );
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( elementDescriptor instanceof DiscriminatedCollectionPart ) {
|
|
||||||
return ( (DiscriminatedCollectionPart) elementDescriptor ).findSubPart( name, treatTargetType );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
*/
|
*/
|
||||||
package org.hibernate.metamodel.mapping.ordering;
|
package org.hibernate.metamodel.mapping.ordering;
|
||||||
|
|
||||||
|
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||||
import org.hibernate.jpa.spi.JpaCompliance;
|
import org.hibernate.jpa.spi.JpaCompliance;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -14,5 +15,10 @@ import org.hibernate.jpa.spi.JpaCompliance;
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
||||||
*/
|
*/
|
||||||
public interface TranslationContext {
|
public interface TranslationContext {
|
||||||
JpaCompliance getJpaCompliance();
|
|
||||||
|
SessionFactoryImplementor getFactory();
|
||||||
|
|
||||||
|
default JpaCompliance getJpaCompliance() {
|
||||||
|
return getFactory().getSessionFactoryOptions().getJpaCompliance();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,6 +56,7 @@ public class CollectionPartPath extends AbstractDomainPath {
|
||||||
@Override
|
@Override
|
||||||
public SequencePart resolvePathPart(
|
public SequencePart resolvePathPart(
|
||||||
String name,
|
String name,
|
||||||
|
String identifier,
|
||||||
boolean isTerminal,
|
boolean isTerminal,
|
||||||
TranslationContext translationContext) {
|
TranslationContext translationContext) {
|
||||||
if ( referencedPart instanceof EmbeddedCollectionPart ) {
|
if ( referencedPart instanceof EmbeddedCollectionPart ) {
|
||||||
|
|
|
@ -34,12 +34,10 @@ import org.hibernate.sql.ast.tree.select.SortSpecification;
|
||||||
public class ColumnReference implements OrderingExpression, SequencePart {
|
public class ColumnReference implements OrderingExpression, SequencePart {
|
||||||
private final String columnExpression;
|
private final String columnExpression;
|
||||||
private final boolean isColumnExpressionFormula;
|
private final boolean isColumnExpressionFormula;
|
||||||
private final NavigablePath rootPath;
|
|
||||||
|
|
||||||
public ColumnReference(String columnExpression, boolean isColumnExpressionFormula, NavigablePath rootPath) {
|
public ColumnReference(String columnExpression, boolean isColumnExpressionFormula) {
|
||||||
this.columnExpression = columnExpression;
|
this.columnExpression = columnExpression;
|
||||||
this.isColumnExpressionFormula = isColumnExpressionFormula;
|
this.isColumnExpressionFormula = isColumnExpressionFormula;
|
||||||
this.rootPath = rootPath;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getColumnExpression() {
|
public String getColumnExpression() {
|
||||||
|
@ -50,14 +48,6 @@ public class ColumnReference implements OrderingExpression, SequencePart {
|
||||||
return isColumnExpressionFormula;
|
return isColumnExpressionFormula;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public SequencePart resolvePathPart(
|
|
||||||
String name,
|
|
||||||
boolean isTerminal,
|
|
||||||
TranslationContext translationContext) {
|
|
||||||
throw new UnsupportedMappingException( "ColumnReference cannot be de-referenced" );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Expression resolve(
|
public Expression resolve(
|
||||||
QuerySpec ast,
|
QuerySpec ast,
|
||||||
|
@ -85,6 +75,15 @@ public class ColumnReference implements OrderingExpression, SequencePart {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SequencePart resolvePathPart(
|
||||||
|
String name,
|
||||||
|
String identifier,
|
||||||
|
boolean isTerminal,
|
||||||
|
TranslationContext translationContext) {
|
||||||
|
throw new UnsupportedMappingException( "ColumnReference cannot be de-referenced" );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void apply(
|
public void apply(
|
||||||
QuerySpec ast,
|
QuerySpec ast,
|
||||||
|
|
|
@ -48,6 +48,7 @@ public class DomainPathContinuation extends AbstractDomainPath {
|
||||||
@Override
|
@Override
|
||||||
public SequencePart resolvePathPart(
|
public SequencePart resolvePathPart(
|
||||||
String name,
|
String name,
|
||||||
|
String identifier,
|
||||||
boolean isTerminal,
|
boolean isTerminal,
|
||||||
TranslationContext translationContext) {
|
TranslationContext translationContext) {
|
||||||
if ( referencedModelPart instanceof EmbeddableValuedModelPart ) {
|
if ( referencedModelPart instanceof EmbeddableValuedModelPart ) {
|
||||||
|
|
|
@ -35,7 +35,11 @@ public class FkDomainPathContinuation extends DomainPathContinuation {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SequencePart resolvePathPart(String name, boolean isTerminal, TranslationContext translationContext) {
|
public SequencePart resolvePathPart(
|
||||||
|
String name,
|
||||||
|
String identifier,
|
||||||
|
boolean isTerminal,
|
||||||
|
TranslationContext translationContext) {
|
||||||
HashSet<String> furtherPaths = new LinkedHashSet<>( possiblePaths.size() );
|
HashSet<String> furtherPaths = new LinkedHashSet<>( possiblePaths.size() );
|
||||||
for ( String path : possiblePaths ) {
|
for ( String path : possiblePaths ) {
|
||||||
if ( !path.startsWith( name ) ) {
|
if ( !path.startsWith( name ) ) {
|
||||||
|
|
|
@ -22,20 +22,11 @@ public class OrderingSpecification implements Node {
|
||||||
private NullPrecedence nullPrecedence = NullPrecedence.NONE;
|
private NullPrecedence nullPrecedence = NullPrecedence.NONE;
|
||||||
private String orderByValue;
|
private String orderByValue;
|
||||||
|
|
||||||
public String getOrderByValue() {
|
|
||||||
return orderByValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
public OrderingSpecification(OrderingExpression orderingExpression, String orderByValue) {
|
public OrderingSpecification(OrderingExpression orderingExpression, String orderByValue) {
|
||||||
this.orderingExpression = orderingExpression;
|
this.orderingExpression = orderingExpression;
|
||||||
this.orderByValue = orderByValue;
|
this.orderByValue = orderByValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
public OrderingSpecification(OrderingExpression orderingExpression, String orderByValue,SortOrder sortOrder) {
|
|
||||||
this(orderingExpression, orderByValue);
|
|
||||||
this.sortOrder = sortOrder;
|
|
||||||
}
|
|
||||||
|
|
||||||
public OrderingExpression getExpression() {
|
public OrderingExpression getExpression() {
|
||||||
return orderingExpression;
|
return orderingExpression;
|
||||||
}
|
}
|
||||||
|
@ -64,6 +55,10 @@ public class OrderingSpecification implements Node {
|
||||||
this.nullPrecedence = nullPrecedence;
|
this.nullPrecedence = nullPrecedence;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getOrderByValue() {
|
||||||
|
return orderByValue;
|
||||||
|
}
|
||||||
|
|
||||||
public void setOrderByValue(String orderByValue) {
|
public void setOrderByValue(String orderByValue) {
|
||||||
this.orderByValue = orderByValue;
|
this.orderByValue = orderByValue;
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,13 +10,17 @@ import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.hibernate.internal.util.QuotingHelper;
|
||||||
|
import org.hibernate.query.NullPrecedence;
|
||||||
import org.hibernate.query.SortOrder;
|
import org.hibernate.query.SortOrder;
|
||||||
import org.hibernate.grammars.ordering.OrderingParser;
|
import org.hibernate.grammars.ordering.OrderingParser;
|
||||||
import org.hibernate.grammars.ordering.OrderingParser.ExpressionContext;
|
|
||||||
import org.hibernate.grammars.ordering.OrderingParserBaseVisitor;
|
import org.hibernate.grammars.ordering.OrderingParserBaseVisitor;
|
||||||
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
|
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
|
||||||
import org.hibernate.metamodel.mapping.ordering.TranslationContext;
|
import org.hibernate.metamodel.mapping.ordering.TranslationContext;
|
||||||
|
import org.hibernate.query.sqm.ParsingException;
|
||||||
|
import org.hibernate.query.sqm.function.SqmFunctionDescriptor;
|
||||||
|
|
||||||
|
import org.antlr.v4.runtime.tree.ParseTree;
|
||||||
import org.antlr.v4.runtime.tree.TerminalNode;
|
import org.antlr.v4.runtime.tree.TerminalNode;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -34,18 +38,19 @@ public class ParseTreeVisitor extends OrderingParserBaseVisitor<Object> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<OrderingSpecification> visitOrderByFragment(OrderingParser.OrderByFragmentContext parsedFragment) {
|
public List<OrderingSpecification> visitOrderByFragment(OrderingParser.OrderByFragmentContext ctx) {
|
||||||
final List<OrderingParser.SortSpecificationContext> parsedSortSpecifications = parsedFragment.sortSpecification();
|
final int size = ctx.getChildCount();
|
||||||
assert parsedSortSpecifications != null;
|
// Shift 1 bit instead of division by 2
|
||||||
|
final int specificationCount = ( size + 1 ) >> 1;
|
||||||
|
|
||||||
if ( parsedSortSpecifications.size() == 1 ) {
|
if ( specificationCount == 1 ) {
|
||||||
return Collections.singletonList( visitSortSpecification( parsedSortSpecifications.get( 0 ) ) );
|
return Collections.singletonList( visitSortSpecification( (OrderingParser.SortSpecificationContext) ctx.getChild( 0 ) ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
final List<OrderingSpecification> specifications = new ArrayList<>( parsedSortSpecifications.size() );
|
final List<OrderingSpecification> specifications = new ArrayList<>( specificationCount );
|
||||||
|
|
||||||
for ( OrderingParser.SortSpecificationContext parsedSortSpecification : parsedSortSpecifications ) {
|
for ( int i = 0; i < size; i += 2 ) {
|
||||||
specifications.add( visitSortSpecification( parsedSortSpecification ) );
|
specifications.add( visitSortSpecification( (OrderingParser.SortSpecificationContext) ctx.getChild( i ) ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
return specifications;
|
return specifications;
|
||||||
|
@ -56,7 +61,7 @@ public class ParseTreeVisitor extends OrderingParserBaseVisitor<Object> {
|
||||||
assert parsedSpec != null;
|
assert parsedSpec != null;
|
||||||
assert parsedSpec.expression() != null;
|
assert parsedSpec.expression() != null;
|
||||||
|
|
||||||
final OrderingExpression orderingExpression = visitExpression( parsedSpec.expression() );
|
final OrderingExpression orderingExpression = (OrderingExpression) parsedSpec.getChild( 0 ).accept( this );
|
||||||
if ( translationContext.getJpaCompliance().isJpaOrderByMappingComplianceEnabled() ) {
|
if ( translationContext.getJpaCompliance().isJpaOrderByMappingComplianceEnabled() ) {
|
||||||
if ( orderingExpression instanceof DomainPath ) {
|
if ( orderingExpression instanceof DomainPath ) {
|
||||||
// nothing to do
|
// nothing to do
|
||||||
|
@ -70,85 +75,157 @@ public class ParseTreeVisitor extends OrderingParserBaseVisitor<Object> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final OrderingSpecification result = new OrderingSpecification( orderingExpression, parsedSpec.expression().getText());
|
final OrderingSpecification result = new OrderingSpecification( orderingExpression, parsedSpec.getChild( 0 ).getText() );
|
||||||
|
int i = 1;
|
||||||
|
|
||||||
if ( parsedSpec.collationSpecification() != null ) {
|
if ( parsedSpec.getChildCount() > i ) {
|
||||||
result.setCollation( parsedSpec.collationSpecification().identifier().getText() );
|
final ParseTree parseTree = parsedSpec.getChild( i );
|
||||||
|
if ( parseTree instanceof OrderingParser.CollationSpecificationContext ) {
|
||||||
|
result.setCollation( (String) parseTree.getChild( 1 ).getChild( 0 ).accept( this ) );
|
||||||
|
i++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
if ( parsedSpec.getChildCount() > i ) {
|
||||||
if ( parsedSpec.direction() != null && parsedSpec.direction().DESC() != null ) {
|
final ParseTree parseTree = parsedSpec.getChild( i );
|
||||||
result.setSortOrder( SortOrder.DESCENDING );
|
if ( parseTree instanceof OrderingParser.DirectionContext ) {
|
||||||
|
final OrderingParser.DirectionContext directionCtx = (OrderingParser.DirectionContext) parseTree;
|
||||||
|
if ( ( (TerminalNode) directionCtx.getChild( 0 ) ).getSymbol().getType() == OrderingParser.ASC ) {
|
||||||
|
result.setSortOrder( SortOrder.ASCENDING );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
result.setSortOrder( SortOrder.DESCENDING );
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
if ( parsedSpec.getChildCount() > i ) {
|
||||||
result.setSortOrder( SortOrder.ASCENDING );
|
final ParseTree parseTree = parsedSpec.getChild( i );
|
||||||
|
if ( parseTree instanceof OrderingParser.NullsPrecedenceContext ) {
|
||||||
|
final OrderingParser.NullsPrecedenceContext nullsCtx = (OrderingParser.NullsPrecedenceContext) parseTree;
|
||||||
|
if ( ( (TerminalNode) nullsCtx.getChild( 1 ) ).getSymbol().getType() == OrderingParser.FIRST ) {
|
||||||
|
result.setNullPrecedence( NullPrecedence.FIRST );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
result.setNullPrecedence( NullPrecedence.LAST );
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo (6.0) : null-precedence (see grammar notes)
|
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public OrderingExpression visitExpression(ExpressionContext ctx) {
|
public OrderingExpression visitFunctionExpression(OrderingParser.FunctionExpressionContext ctx) {
|
||||||
if ( ctx.function() != null ) {
|
return visitFunction( (OrderingParser.FunctionContext) ctx.getChild( 0 ) );
|
||||||
return visitFunction( ctx.function() );
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if ( ctx.identifier() != null ) {
|
@Override
|
||||||
pathConsumer.consumeIdentifier( ctx.identifier().getText(), true, true );
|
public OrderingExpression visitIdentifierExpression(OrderingParser.IdentifierExpressionContext ctx) {
|
||||||
return (OrderingExpression) pathConsumer.getConsumedPart();
|
return visitIdentifier( (OrderingParser.IdentifierContext) ctx.getChild( 0 ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
assert ctx.dotIdentifier() != null;
|
@Override
|
||||||
final int numberOfParts = ctx.dotIdentifier().IDENTIFIER().size();
|
public OrderingExpression visitDotIdentifierExpression(OrderingParser.DotIdentifierExpressionContext ctx) {
|
||||||
boolean firstPass = true;
|
return visitDotIdentifier( (OrderingParser.DotIdentifierContext) ctx.getChild( 0 ) );
|
||||||
|
}
|
||||||
|
|
||||||
for ( int i = 0; i < numberOfParts; i++ ) {
|
@Override
|
||||||
final TerminalNode partNode = ctx.dotIdentifier().IDENTIFIER().get( i );
|
public OrderingExpression visitDotIdentifier(OrderingParser.DotIdentifierContext ctx) {
|
||||||
|
final int size = ctx.getChildCount();
|
||||||
|
final int end = size - 1;
|
||||||
|
// For nested paths, which must be on the domain model, we don't care about the possibly quoted identifier,
|
||||||
|
// so we just pass the unquoted one
|
||||||
|
String partName = (String) ctx.getChild( 0 ).getChild( 0 ).accept( this );
|
||||||
|
pathConsumer.consumeIdentifier(
|
||||||
|
partName,
|
||||||
|
partName,
|
||||||
|
true,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
|
||||||
|
for ( int i = 2; i < end; i += 2 ) {
|
||||||
|
partName = (String) ctx.getChild( i ).getChild( 0 ).accept( this );
|
||||||
pathConsumer.consumeIdentifier(
|
pathConsumer.consumeIdentifier(
|
||||||
partNode.getText(),
|
partName,
|
||||||
firstPass,
|
partName,
|
||||||
true
|
false,
|
||||||
|
false
|
||||||
);
|
);
|
||||||
firstPass = false;
|
|
||||||
}
|
}
|
||||||
|
partName = (String) ctx.getChild( end ).getChild( 0 ).accept( this );
|
||||||
|
pathConsumer.consumeIdentifier(
|
||||||
|
partName,
|
||||||
|
partName,
|
||||||
|
false,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
|
||||||
return (OrderingExpression) pathConsumer.getConsumedPart();
|
return (OrderingExpression) pathConsumer.getConsumedPart();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public FunctionExpression visitFunction(OrderingParser.FunctionContext ctx) {
|
public OrderingExpression visitIdentifier(OrderingParser.IdentifierContext ctx) {
|
||||||
if ( ctx.simpleFunction() != null ) {
|
final String unquotedIdentifier = (String) ctx.getChild( 0 ).accept( this );
|
||||||
final FunctionExpression function = new FunctionExpression(
|
final SqmFunctionDescriptor descriptor = translationContext.getFactory()
|
||||||
ctx.simpleFunction().identifier().getText(),
|
.getQueryEngine()
|
||||||
ctx.simpleFunction().functionArguments().expression().size()
|
.getSqmFunctionRegistry()
|
||||||
);
|
.findFunctionDescriptor( unquotedIdentifier );
|
||||||
|
// If there is no function with this name, it always requires parenthesis or if this is a quoted identifiers
|
||||||
for ( int i = 0; i < ctx.simpleFunction().functionArguments().expression().size(); i++ ) {
|
// then we interpret this as a path part instead of as function
|
||||||
final ExpressionContext arg = ctx.simpleFunction().functionArguments().expression( i );
|
final String identifier = ctx.getChild( 0 ).getText();
|
||||||
function.addArgument( visitExpression( arg ) );
|
if ( descriptor == null || descriptor.alwaysIncludesParentheses() || !unquotedIdentifier.equals( identifier ) ) {
|
||||||
}
|
pathConsumer.consumeIdentifier( unquotedIdentifier, identifier, true, true );
|
||||||
|
return (OrderingExpression) pathConsumer.getConsumedPart();
|
||||||
return function;
|
|
||||||
}
|
}
|
||||||
|
return new SelfRenderingOrderingExpression( unquotedIdentifier );
|
||||||
|
}
|
||||||
|
|
||||||
assert ctx.packagedFunction() != null;
|
@Override
|
||||||
|
public FunctionExpression visitFunction(OrderingParser.FunctionContext ctx) {
|
||||||
|
final ParseTree functionCtx = ctx.getChild( 0 );
|
||||||
|
final OrderingParser.FunctionArgumentsContext argumentsCtx = (OrderingParser.FunctionArgumentsContext) functionCtx.getChild( 1 );
|
||||||
|
final int size = argumentsCtx.getChildCount();
|
||||||
|
// Shift 1 bit instead of division by 2
|
||||||
|
final int expressionsCount = ( ( size - 1 ) >> 1 );
|
||||||
final FunctionExpression function = new FunctionExpression(
|
final FunctionExpression function = new FunctionExpression(
|
||||||
ctx.packagedFunction().dotIdentifier().getText(),
|
functionCtx.getChild( 0 ).getText(),
|
||||||
ctx.packagedFunction().functionArguments().expression().size()
|
expressionsCount
|
||||||
);
|
);
|
||||||
|
|
||||||
for ( int i = 0; i < ctx.packagedFunction().functionArguments().expression().size(); i++ ) {
|
for ( int i = 1; i < size; i += 2 ) {
|
||||||
final ExpressionContext arg = ctx.packagedFunction().functionArguments().expression( i );
|
function.addArgument( (OrderingExpression) argumentsCtx.getChild( i ).accept( this ) );
|
||||||
function.addArgument( visitExpression( arg ) );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return function;
|
return function;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public OrderingExpression visitFunctionArgument(OrderingParser.FunctionArgumentContext ctx) {
|
||||||
|
return (OrderingExpression) ctx.getChild( 0 ).accept( this );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public OrderingExpression visitLiteral(OrderingParser.LiteralContext ctx) {
|
||||||
|
return new SelfRenderingOrderingExpression( ctx.getText() );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String visitCollationSpecification(OrderingParser.CollationSpecificationContext ctx) {
|
public String visitCollationSpecification(OrderingParser.CollationSpecificationContext ctx) {
|
||||||
throw new IllegalStateException( "Unexpected call to #visitCollationSpecification" );
|
throw new IllegalStateException( "Unexpected call to #visitCollationSpecification" );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object visitTerminal(TerminalNode node) {
|
||||||
|
if ( node.getSymbol().getType() == OrderingParser.EOF ) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
switch ( node.getSymbol().getType() ) {
|
||||||
|
case OrderingParser.IDENTIFIER:
|
||||||
|
return node.getText();
|
||||||
|
case OrderingParser.QUOTED_IDENTIFIER:
|
||||||
|
return QuotingHelper.unquoteIdentifier( node.getText() );
|
||||||
|
default:
|
||||||
|
throw new ParsingException( "Unexpected terminal node [" + node.getText() + "]");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,7 +26,7 @@ public class PathConsumer {
|
||||||
|
|
||||||
private final SequencePart rootSequencePart;
|
private final SequencePart rootSequencePart;
|
||||||
|
|
||||||
private String pathSoFar;
|
private StringBuilder pathSoFar = new StringBuilder();
|
||||||
private SequencePart currentPart;
|
private SequencePart currentPart;
|
||||||
|
|
||||||
public PathConsumer(
|
public PathConsumer(
|
||||||
|
@ -41,32 +41,33 @@ public class PathConsumer {
|
||||||
return currentPart;
|
return currentPart;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void consumeIdentifier(String identifier, boolean isBase, boolean isTerminal) {
|
public void consumeIdentifier(
|
||||||
|
String unquotedIdentifier,
|
||||||
|
String identifier, boolean isBase,
|
||||||
|
boolean isTerminal) {
|
||||||
if ( isBase ) {
|
if ( isBase ) {
|
||||||
// each time we start a new sequence we need to reset our state
|
// each time we start a new sequence we need to reset our state
|
||||||
reset();
|
reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( pathSoFar == null ) {
|
if ( pathSoFar.length() != 0 ) {
|
||||||
pathSoFar = identifier;
|
pathSoFar.append( '.' );
|
||||||
}
|
|
||||||
else {
|
|
||||||
pathSoFar += ( '.' + identifier );
|
|
||||||
}
|
}
|
||||||
|
pathSoFar.append( unquotedIdentifier );
|
||||||
|
|
||||||
log.tracef(
|
log.tracef(
|
||||||
"BasicDotIdentifierHandler#consumeIdentifier( %s, %s, %s ) - %s",
|
"BasicDotIdentifierHandler#consumeIdentifier( %s, %s, %s ) - %s",
|
||||||
identifier,
|
unquotedIdentifier,
|
||||||
isBase,
|
isBase,
|
||||||
isTerminal,
|
isTerminal,
|
||||||
pathSoFar
|
pathSoFar
|
||||||
);
|
);
|
||||||
|
|
||||||
currentPart = currentPart.resolvePathPart( identifier, isTerminal, translationContext );
|
currentPart = currentPart.resolvePathPart( unquotedIdentifier, identifier, isTerminal, translationContext );
|
||||||
}
|
}
|
||||||
|
|
||||||
private void reset() {
|
private void reset() {
|
||||||
pathSoFar = null;
|
pathSoFar.setLength( 0 );
|
||||||
currentPart = rootSequencePart;
|
currentPart = rootSequencePart;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,6 +48,7 @@ public class PluralAttributePath extends AbstractDomainPath {
|
||||||
@Override
|
@Override
|
||||||
public DomainPath resolvePathPart(
|
public DomainPath resolvePathPart(
|
||||||
String name,
|
String name,
|
||||||
|
String identifier,
|
||||||
boolean isTerminal,
|
boolean isTerminal,
|
||||||
TranslationContext translationContext) {
|
TranslationContext translationContext) {
|
||||||
final ModelPart subPart = pluralAttributeMapping.findSubPart( name, null );
|
final ModelPart subPart = pluralAttributeMapping.findSubPart( name, null );
|
||||||
|
@ -60,8 +61,11 @@ public class PluralAttributePath extends AbstractDomainPath {
|
||||||
return new DomainPathContinuation( navigablePath.append( name ), this, subPart );
|
return new DomainPathContinuation( navigablePath.append( name ), this, subPart );
|
||||||
}
|
}
|
||||||
if ( subPart instanceof ToOneAttributeMapping ) {
|
if ( subPart instanceof ToOneAttributeMapping ) {
|
||||||
return new FkDomainPathContinuation( navigablePath.append( name ), this,
|
return new FkDomainPathContinuation(
|
||||||
(ToOneAttributeMapping) subPart );
|
navigablePath.append( name ),
|
||||||
|
this,
|
||||||
|
(ToOneAttributeMapping) subPart
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// leaf case:
|
// leaf case:
|
||||||
|
|
|
@ -26,18 +26,30 @@ public class RootSequencePart implements SequencePart {
|
||||||
@Override
|
@Override
|
||||||
public SequencePart resolvePathPart(
|
public SequencePart resolvePathPart(
|
||||||
String name,
|
String name,
|
||||||
|
String identifier,
|
||||||
boolean isTerminal,
|
boolean isTerminal,
|
||||||
TranslationContext translationContext) {
|
TranslationContext translationContext) {
|
||||||
// could be a column-reference (isTerminal would have to be true) or a domain-path
|
// could be a column-reference (isTerminal would have to be true) or a domain-path
|
||||||
|
|
||||||
final DomainPath subDomainPath = pluralAttributePath.resolvePathPart( name, isTerminal, translationContext );
|
final DomainPath subDomainPath = pluralAttributePath.resolvePathPart(
|
||||||
|
name,
|
||||||
|
identifier,
|
||||||
|
isTerminal,
|
||||||
|
translationContext
|
||||||
|
);
|
||||||
if ( subDomainPath != null ) {
|
if ( subDomainPath != null ) {
|
||||||
return subDomainPath;
|
return subDomainPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( isTerminal ) {
|
if ( isTerminal ) {
|
||||||
// assume a column-reference
|
// assume a column-reference
|
||||||
return new ColumnReference( name, false, pluralAttributePath.getNavigablePath() );
|
return new ColumnReference(
|
||||||
|
translationContext.getFactory()
|
||||||
|
.getJdbcServices()
|
||||||
|
.getDialect()
|
||||||
|
.quote( identifier ),
|
||||||
|
false
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new PathResolutionException(
|
throw new PathResolutionException(
|
||||||
|
|
|
@ -0,0 +1,86 @@
|
||||||
|
/*
|
||||||
|
* Hibernate, Relational Persistence for Idiomatic Java
|
||||||
|
*
|
||||||
|
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
|
||||||
|
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
|
||||||
|
*/
|
||||||
|
package org.hibernate.metamodel.mapping.ordering.ast;
|
||||||
|
|
||||||
|
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||||
|
import org.hibernate.metamodel.mapping.JdbcMappingContainer;
|
||||||
|
import org.hibernate.query.NullPrecedence;
|
||||||
|
import org.hibernate.query.SortOrder;
|
||||||
|
import org.hibernate.sql.ast.SqlAstTranslator;
|
||||||
|
import org.hibernate.sql.ast.spi.SqlAppender;
|
||||||
|
import org.hibernate.sql.ast.spi.SqlAstCreationState;
|
||||||
|
import org.hibernate.sql.ast.tree.expression.Expression;
|
||||||
|
import org.hibernate.sql.ast.tree.expression.SelfRenderingExpression;
|
||||||
|
import org.hibernate.sql.ast.tree.from.TableGroup;
|
||||||
|
import org.hibernate.sql.ast.tree.select.QuerySpec;
|
||||||
|
import org.hibernate.sql.ast.tree.select.SortSpecification;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a self rendering expression i.e. usually a literal used in an order-by fragment
|
||||||
|
*
|
||||||
|
* @apiNote This is Hibernate-specific feature. For {@link jakarta.persistence.OrderBy} (JPA)
|
||||||
|
* all path references are expected to be domain paths (attributes).
|
||||||
|
*
|
||||||
|
* @author Christian Beikov
|
||||||
|
*/
|
||||||
|
public class SelfRenderingOrderingExpression implements OrderingExpression, SelfRenderingExpression {
|
||||||
|
private final String expression;
|
||||||
|
|
||||||
|
public SelfRenderingOrderingExpression(String expression) {
|
||||||
|
this.expression = expression;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getExpression() {
|
||||||
|
return expression;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JdbcMappingContainer getExpressionType() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void renderToSql(
|
||||||
|
SqlAppender sqlAppender,
|
||||||
|
SqlAstTranslator<?> walker,
|
||||||
|
SessionFactoryImplementor sessionFactory) {
|
||||||
|
sqlAppender.append( expression );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Expression resolve(
|
||||||
|
QuerySpec ast,
|
||||||
|
TableGroup tableGroup,
|
||||||
|
String modelPartName,
|
||||||
|
SqlAstCreationState creationState) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void apply(
|
||||||
|
QuerySpec ast,
|
||||||
|
TableGroup tableGroup,
|
||||||
|
String collation,
|
||||||
|
String modelPartName,
|
||||||
|
SortOrder sortOrder,
|
||||||
|
NullPrecedence nullPrecedence,
|
||||||
|
SqlAstCreationState creationState) {
|
||||||
|
final Expression expression = resolve( ast, tableGroup, modelPartName, creationState );
|
||||||
|
// It makes no sense to order by an expression multiple times
|
||||||
|
// SQL Server even reports a query error in this case
|
||||||
|
if ( ast.hasSortSpecifications() ) {
|
||||||
|
for ( SortSpecification sortSpecification : ast.getSortSpecifications() ) {
|
||||||
|
if ( sortSpecification.getSortExpression() == expression ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast.addSortSpecification( new SortSpecification( expression, collation, sortOrder, nullPrecedence ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -16,6 +16,7 @@ import org.hibernate.metamodel.mapping.ordering.TranslationContext;
|
||||||
public interface SequencePart {
|
public interface SequencePart {
|
||||||
SequencePart resolvePathPart(
|
SequencePart resolvePathPart(
|
||||||
String name,
|
String name,
|
||||||
|
String identifier,
|
||||||
boolean isTerminal,
|
boolean isTerminal,
|
||||||
TranslationContext translationContext);
|
TranslationContext translationContext);
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
*/
|
*/
|
||||||
package org.hibernate.metamodel.model.domain.internal;
|
package org.hibernate.metamodel.model.domain.internal;
|
||||||
|
|
||||||
|
import org.hibernate.metamodel.model.domain.AllowableParameterType;
|
||||||
import org.hibernate.metamodel.UnsupportedMappingException;
|
import org.hibernate.metamodel.UnsupportedMappingException;
|
||||||
import org.hibernate.metamodel.model.domain.AnyMappingDomainType;
|
import org.hibernate.metamodel.model.domain.AnyMappingDomainType;
|
||||||
import org.hibernate.metamodel.model.domain.BasicDomainType;
|
import org.hibernate.metamodel.model.domain.BasicDomainType;
|
||||||
|
@ -19,7 +20,7 @@ import static jakarta.persistence.metamodel.Bindable.BindableType.SINGULAR_ATTRI
|
||||||
/**
|
/**
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
||||||
*/
|
*/
|
||||||
public class AnyMappingSqmPathSource<J> extends AbstractSqmPathSource<J> {
|
public class AnyMappingSqmPathSource<J> extends AbstractSqmPathSource<J> implements AllowableParameterType<J> {
|
||||||
private final SqmPathSource<?> keyPathSource;
|
private final SqmPathSource<?> keyPathSource;
|
||||||
|
|
||||||
@SuppressWarnings("WeakerAccess")
|
@SuppressWarnings("WeakerAccess")
|
||||||
|
@ -58,4 +59,14 @@ public class AnyMappingSqmPathSource<J> extends AbstractSqmPathSource<J> {
|
||||||
return new SqmAnyValuedSimplePath<>( navigablePath, this, lhs, lhs.nodeBuilder() );
|
return new SqmAnyValuedSimplePath<>( navigablePath, this, lhs, lhs.nodeBuilder() );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PersistenceType getPersistenceType() {
|
||||||
|
// todo (6.0): no idea what is best here
|
||||||
|
return PersistenceType.EMBEDDABLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<J> getJavaType() {
|
||||||
|
return getBindableJavaType();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -87,7 +87,7 @@ public class DiscriminatorSqmPath extends AbstractSqmPath implements SelfInterpr
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SemanticPathPart resolvePathPart(String name, boolean isTerminal, SqmCreationState creationState) {
|
public SqmPath<?> resolvePathPart(String name, boolean isTerminal, SqmCreationState creationState) {
|
||||||
throw new IllegalStateException( "Discriminator cannot be de-referenced" );
|
throw new IllegalStateException( "Discriminator cannot be de-referenced" );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,11 +10,14 @@ import java.io.ObjectStreamException;
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.TreeMap;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.ConcurrentSkipListMap;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
import jakarta.persistence.EntityGraph;
|
import jakarta.persistence.EntityGraph;
|
||||||
|
@ -79,16 +82,16 @@ public class JpaMetamodelImpl implements JpaMetamodel, Serializable {
|
||||||
private final TypeConfiguration typeConfiguration;
|
private final TypeConfiguration typeConfiguration;
|
||||||
private final JpaCompliance jpaCompliance;
|
private final JpaCompliance jpaCompliance;
|
||||||
|
|
||||||
private final Map<String, EntityDomainType<?>> jpaEntityTypeMap = new ConcurrentHashMap<>();
|
private final Map<String, EntityDomainType<?>> jpaEntityTypeMap = new TreeMap<>(); // Need ordering for deterministic implementers list in SqmPolymorphicRootDescriptor
|
||||||
private final Map<Class<?>, MappedSuperclassDomainType<?>> jpaMappedSuperclassTypeMap = new ConcurrentHashMap<>();
|
private final Map<Class<?>, MappedSuperclassDomainType<?>> jpaMappedSuperclassTypeMap = new HashMap<>();
|
||||||
private final Map<Class, EmbeddableDomainType<?>> jpaEmbeddableDescriptorMap = new ConcurrentHashMap<>();
|
private final Map<Class, EmbeddableDomainType<?>> jpaEmbeddableDescriptorMap = new HashMap<>();
|
||||||
private final Map<String, Map<Class<?>, Enum<?>>> allowedEnumLiteralTexts = new ConcurrentHashMap<>();
|
private final Map<String, Map<Class<?>, Enum<?>>> allowedEnumLiteralTexts = new HashMap<>();
|
||||||
|
|
||||||
private final transient Map<String, RootGraphImplementor> entityGraphMap = new ConcurrentHashMap<>();
|
private final transient Map<String, RootGraphImplementor> entityGraphMap = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
private final Map<Class, SqmPolymorphicRootDescriptor<?>> polymorphicEntityReferenceMap = new ConcurrentHashMap<>();
|
private final Map<Class, SqmPolymorphicRootDescriptor<?>> polymorphicEntityReferenceMap = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
private final Map<Class, String> entityProxyInterfaceMap = new ConcurrentHashMap<>();
|
private final Map<Class, String> entityProxyInterfaceMap = new HashMap<>();
|
||||||
|
|
||||||
private final Map<String, ImportInfo<?>> nameToImportMap = new ConcurrentHashMap<>();
|
private final Map<String, ImportInfo<?>> nameToImportMap = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
@ -465,6 +468,18 @@ public class JpaMetamodelImpl implements JpaMetamodel, Serializable {
|
||||||
visitEntityTypes(
|
visitEntityTypes(
|
||||||
entityDomainType -> {
|
entityDomainType -> {
|
||||||
if ( javaType.isAssignableFrom( entityDomainType.getJavaType() ) ) {
|
if ( javaType.isAssignableFrom( entityDomainType.getJavaType() ) ) {
|
||||||
|
final ManagedDomainType<?> superType = entityDomainType.getSuperType();
|
||||||
|
// If the entity super type is also assignable, skip adding this entity type
|
||||||
|
if ( superType instanceof EntityDomainType<?>
|
||||||
|
&& javaType.isAssignableFrom( superType.getJavaType() ) ) {
|
||||||
|
final Queryable entityPersister = (Queryable) typeConfiguration.getSessionFactory()
|
||||||
|
.getMetamodel()
|
||||||
|
.getEntityDescriptor( ( (EntityDomainType<?>) superType ).getHibernateEntityName() );
|
||||||
|
// But only skip adding this type if the parent doesn't require explicit polymorphism
|
||||||
|
if ( !entityPersister.isExplicitPolymorphism() ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
final Queryable entityPersister = (Queryable) typeConfiguration.getSessionFactory()
|
final Queryable entityPersister = (Queryable) typeConfiguration.getSessionFactory()
|
||||||
.getMetamodel()
|
.getMetamodel()
|
||||||
.getEntityDescriptor( entityDomainType.getHibernateEntityName() );
|
.getEntityDescriptor( entityDomainType.getHibernateEntityName() );
|
||||||
|
@ -531,11 +546,11 @@ public class JpaMetamodelImpl implements JpaMetamodel, Serializable {
|
||||||
|
|
||||||
this.allowedEnumLiteralTexts.computeIfAbsent(
|
this.allowedEnumLiteralTexts.computeIfAbsent(
|
||||||
enumConstant.name(),
|
enumConstant.name(),
|
||||||
k -> new ConcurrentHashMap<>()
|
k -> new HashMap<>()
|
||||||
).put( enumClass, enumConstant );
|
).put( enumClass, enumConstant );
|
||||||
this.allowedEnumLiteralTexts.computeIfAbsent(
|
this.allowedEnumLiteralTexts.computeIfAbsent(
|
||||||
qualifiedEnumLiteral,
|
qualifiedEnumLiteral,
|
||||||
k -> new ConcurrentHashMap<>()
|
k -> new HashMap<>()
|
||||||
).put( enumClass, enumConstant );
|
).put( enumClass, enumConstant );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,26 +0,0 @@
|
||||||
/*
|
|
||||||
* Hibernate, Relational Persistence for Idiomatic Java
|
|
||||||
*
|
|
||||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
|
||||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
|
||||||
*/
|
|
||||||
package org.hibernate.persister.collection;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The names of all the collection properties.
|
|
||||||
*
|
|
||||||
* @author josh
|
|
||||||
*/
|
|
||||||
public final class CollectionPropertyNames {
|
|
||||||
private CollectionPropertyNames() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public static final String COLLECTION_SIZE = "size";
|
|
||||||
public static final String COLLECTION_ELEMENTS = "elements";
|
|
||||||
public static final String COLLECTION_INDICES = "indices";
|
|
||||||
public static final String COLLECTION_MAX_INDEX = "maxIndex";
|
|
||||||
public static final String COLLECTION_MIN_INDEX = "minIndex";
|
|
||||||
public static final String COLLECTION_MAX_ELEMENT = "maxElement";
|
|
||||||
public static final String COLLECTION_MIN_ELEMENT = "minElement";
|
|
||||||
public static final String COLLECTION_INDEX = "index";
|
|
||||||
}
|
|
|
@ -5558,10 +5558,15 @@ public abstract class AbstractEntityPersister
|
||||||
creationProcess.registerInitializationCallback(
|
creationProcess.registerInitializationCallback(
|
||||||
"Entity(" + getEntityName() + ") `sqmMultiTableMutationStrategy` interpretation",
|
"Entity(" + getEntityName() + ") `sqmMultiTableMutationStrategy` interpretation",
|
||||||
() -> {
|
() -> {
|
||||||
sqmMultiTableMutationStrategy = interpretSqmMultiTableStrategy(
|
try {
|
||||||
this,
|
sqmMultiTableMutationStrategy = interpretSqmMultiTableStrategy(
|
||||||
creationProcess
|
this,
|
||||||
);
|
creationProcess
|
||||||
|
);
|
||||||
|
}
|
||||||
|
catch (Exception ex) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
if ( sqmMultiTableMutationStrategy == null ) {
|
if ( sqmMultiTableMutationStrategy == null ) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -939,8 +939,8 @@ public class SingleTableEntityPersister extends AbstractEntityPersister {
|
||||||
}
|
}
|
||||||
final Object value = getDiscriminatorValue();
|
final Object value = getDiscriminatorValue();
|
||||||
final boolean hasNotNullDiscriminator = value == NOT_NULL_DISCRIMINATOR;
|
final boolean hasNotNullDiscriminator = value == NOT_NULL_DISCRIMINATOR;
|
||||||
final boolean hasNullDiscrininator = value == NULL_DISCRIMINATOR;
|
final boolean hasNullDiscriminator = value == NULL_DISCRIMINATOR;
|
||||||
if ( hasNotNullDiscriminator || hasNullDiscrininator ) {
|
if ( hasNotNullDiscriminator || hasNullDiscriminator ) {
|
||||||
final NullnessPredicate nullnessPredicate = new NullnessPredicate( sqlExpression );
|
final NullnessPredicate nullnessPredicate = new NullnessPredicate( sqlExpression );
|
||||||
if ( hasNotNullDiscriminator ) {
|
if ( hasNotNullDiscriminator ) {
|
||||||
return new NegatedPredicate( nullnessPredicate );
|
return new NegatedPredicate( nullnessPredicate );
|
||||||
|
|
|
@ -10,7 +10,6 @@ import java.lang.reflect.Field;
|
||||||
|
|
||||||
import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
|
import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
|
||||||
import org.hibernate.metamodel.model.domain.EntityDomainType;
|
import org.hibernate.metamodel.model.domain.EntityDomainType;
|
||||||
import org.hibernate.query.criteria.JpaPath;
|
|
||||||
import org.hibernate.query.hql.HqlLogging;
|
import org.hibernate.query.hql.HqlLogging;
|
||||||
import org.hibernate.query.hql.spi.DotIdentifierConsumer;
|
import org.hibernate.query.hql.spi.DotIdentifierConsumer;
|
||||||
import org.hibernate.query.hql.spi.SemanticPathPart;
|
import org.hibernate.query.hql.spi.SemanticPathPart;
|
||||||
|
@ -48,7 +47,7 @@ import org.hibernate.type.descriptor.java.spi.JavaTypeRegistry;
|
||||||
public class BasicDotIdentifierConsumer implements DotIdentifierConsumer {
|
public class BasicDotIdentifierConsumer implements DotIdentifierConsumer {
|
||||||
private final SqmCreationState creationState;
|
private final SqmCreationState creationState;
|
||||||
|
|
||||||
private String pathSoFar;
|
private StringBuilder pathSoFar = new StringBuilder();
|
||||||
private SemanticPathPart currentPart;
|
private SemanticPathPart currentPart;
|
||||||
|
|
||||||
public BasicDotIdentifierConsumer(SqmCreationState creationState) {
|
public BasicDotIdentifierConsumer(SqmCreationState creationState) {
|
||||||
|
@ -76,12 +75,10 @@ public class BasicDotIdentifierConsumer implements DotIdentifierConsumer {
|
||||||
reset();
|
reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( pathSoFar == null ) {
|
if ( pathSoFar.length() != 0 ) {
|
||||||
pathSoFar = identifier;
|
pathSoFar.append( '.' );
|
||||||
}
|
|
||||||
else {
|
|
||||||
pathSoFar += ( '.' + identifier );
|
|
||||||
}
|
}
|
||||||
|
pathSoFar.append( identifier );
|
||||||
|
|
||||||
HqlLogging.QUERY_LOGGER.tracef(
|
HqlLogging.QUERY_LOGGER.tracef(
|
||||||
"BasicDotIdentifierHandler#consumeIdentifier( %s, %s, %s ) - %s",
|
"BasicDotIdentifierHandler#consumeIdentifier( %s, %s, %s ) - %s",
|
||||||
|
@ -102,7 +99,7 @@ public class BasicDotIdentifierConsumer implements DotIdentifierConsumer {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void reset() {
|
protected void reset() {
|
||||||
pathSoFar = null;
|
pathSoFar.setLength( 0 );
|
||||||
currentPart = createBasePart();
|
currentPart = createBasePart();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -179,7 +176,8 @@ public class BasicDotIdentifierConsumer implements DotIdentifierConsumer {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
final String importableName = creationContext.getJpaMetamodel().qualifyImportableName( pathSoFar );
|
final String path = pathSoFar.toString();
|
||||||
|
final String importableName = creationContext.getJpaMetamodel().qualifyImportableName( path );
|
||||||
if ( importableName != null ) {
|
if ( importableName != null ) {
|
||||||
final EntityDomainType<?> entityDomainType = creationContext.getJpaMetamodel().entity( importableName );
|
final EntityDomainType<?> entityDomainType = creationContext.getJpaMetamodel().entity( importableName );
|
||||||
if ( entityDomainType != null ) {
|
if ( entityDomainType != null ) {
|
||||||
|
@ -189,7 +187,7 @@ public class BasicDotIdentifierConsumer implements DotIdentifierConsumer {
|
||||||
|
|
||||||
final SqmFunctionDescriptor functionDescriptor = creationContext.getQueryEngine()
|
final SqmFunctionDescriptor functionDescriptor = creationContext.getQueryEngine()
|
||||||
.getSqmFunctionRegistry()
|
.getSqmFunctionRegistry()
|
||||||
.findFunctionDescriptor( pathSoFar );
|
.findFunctionDescriptor( path );
|
||||||
if ( functionDescriptor != null ) {
|
if ( functionDescriptor != null ) {
|
||||||
return functionDescriptor.generateSqmExpression(
|
return functionDescriptor.generateSqmExpression(
|
||||||
null,
|
null,
|
||||||
|
@ -212,10 +210,10 @@ public class BasicDotIdentifierConsumer implements DotIdentifierConsumer {
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// see if it is a named field/enum reference
|
// see if it is a named field/enum reference
|
||||||
final int splitPosition = pathSoFar.lastIndexOf( '.' );
|
final int splitPosition = path.lastIndexOf( '.' );
|
||||||
if ( splitPosition > 0 ) {
|
if ( splitPosition > 0 ) {
|
||||||
final String prefix = pathSoFar.substring( 0, splitPosition );
|
final String prefix = path.substring( 0, splitPosition );
|
||||||
final String terminal = pathSoFar.substring( splitPosition + 1 );
|
final String terminal = path.substring( splitPosition + 1 );
|
||||||
//TODO: try interpreting paths of form foo.bar.Foo.Bar as foo.bar.Foo$Bar
|
//TODO: try interpreting paths of form foo.bar.Foo.Bar as foo.bar.Foo$Bar
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -254,7 +252,7 @@ public class BasicDotIdentifierConsumer implements DotIdentifierConsumer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new ParsingException( "Could not interpret dot-ident : " + pathSoFar );
|
throw new ParsingException( "Could not interpret dot-ident : " + path );
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void validateAsRoot(SqmFrom<?, ?> pathRoot) {
|
protected void validateAsRoot(SqmFrom<?, ?> pathRoot) {
|
||||||
|
|
|
@ -7,19 +7,11 @@
|
||||||
package org.hibernate.query.hql.internal;
|
package org.hibernate.query.hql.internal;
|
||||||
|
|
||||||
import org.hibernate.NotYetImplementedFor6Exception;
|
import org.hibernate.NotYetImplementedFor6Exception;
|
||||||
import org.hibernate.metamodel.mapping.ModelPartContainer;
|
|
||||||
import org.hibernate.metamodel.model.domain.EntityDomainType;
|
|
||||||
import org.hibernate.query.hql.HqlLogging;
|
import org.hibernate.query.hql.HqlLogging;
|
||||||
import org.hibernate.query.hql.spi.SemanticPathPart;
|
import org.hibernate.query.hql.spi.SemanticPathPart;
|
||||||
import org.hibernate.query.hql.spi.SqmCreationState;
|
import org.hibernate.query.hql.spi.SqmCreationState;
|
||||||
import org.hibernate.query.sqm.tree.domain.SqmPath;
|
import org.hibernate.query.sqm.tree.domain.SqmPath;
|
||||||
import org.hibernate.query.sqm.tree.expression.SqmExpression;
|
import org.hibernate.query.sqm.tree.expression.SqmExpression;
|
||||||
import org.hibernate.query.sqm.tree.from.SqmAttributeJoin;
|
|
||||||
import org.hibernate.query.sqm.tree.from.SqmCrossJoin;
|
|
||||||
import org.hibernate.query.sqm.tree.from.SqmEntityJoin;
|
|
||||||
import org.hibernate.query.sqm.tree.from.SqmFrom;
|
|
||||||
import org.hibernate.query.sqm.tree.from.SqmJoin;
|
|
||||||
import org.hibernate.query.sqm.tree.from.SqmRoot;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specialized "intermediate" SemanticPathPart for processing domain model paths
|
* Specialized "intermediate" SemanticPathPart for processing domain model paths
|
||||||
|
@ -49,53 +41,7 @@ public class DomainPathPart implements SemanticPathPart {
|
||||||
currentPath,
|
currentPath,
|
||||||
name
|
name
|
||||||
);
|
);
|
||||||
final SqmPath<?> reusablePath = currentPath.getReusablePath( name );
|
currentPath = currentPath.resolvePathPart( name, isTerminal, creationState );
|
||||||
if ( reusablePath != null ) {
|
|
||||||
currentPath = reusablePath;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// Try to resolve an existing attribute join without ON clause
|
|
||||||
SqmPath<?> resolvedPath = null;
|
|
||||||
if ( currentPath instanceof SqmFrom<?, ?> ) {
|
|
||||||
ModelPartContainer modelPartContainer = null;
|
|
||||||
for ( SqmJoin<?, ?> sqmJoin : ( (SqmFrom<?, ?>) currentPath ).getSqmJoins() ) {
|
|
||||||
if ( sqmJoin instanceof SqmAttributeJoin<?, ?>
|
|
||||||
&& name.equals( sqmJoin.getReferencedPathSource().getPathName() ) ) {
|
|
||||||
final SqmAttributeJoin<?, ?> attributeJoin = (SqmAttributeJoin<?, ?>) sqmJoin;
|
|
||||||
if ( attributeJoin.getOn() == null ) {
|
|
||||||
// todo (6.0): to match the expectation of the JPA spec I think we also have to check
|
|
||||||
// that the join type is INNER or the default join type for the attribute,
|
|
||||||
// but as far as I understand, in 5.x we expect to ignore this behavior
|
|
||||||
// if ( attributeJoin.getSqmJoinType() != SqmJoinType.INNER ) {
|
|
||||||
// if ( attributeJoin.getAttribute().isCollection() ) {
|
|
||||||
// continue;
|
|
||||||
// }
|
|
||||||
// if ( modelPartContainer == null ) {
|
|
||||||
// modelPartContainer = findModelPartContainer( attributeJoin, creationState );
|
|
||||||
// }
|
|
||||||
// final TableGroupJoinProducer joinProducer = (TableGroupJoinProducer) modelPartContainer.findSubPart(
|
|
||||||
// name,
|
|
||||||
// null
|
|
||||||
// );
|
|
||||||
// if ( attributeJoin.getSqmJoinType().getCorrespondingSqlJoinType() != joinProducer.getDefaultSqlAstJoinType( null ) ) {
|
|
||||||
// continue;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
resolvedPath = sqmJoin;
|
|
||||||
if ( attributeJoin.isFetched() ) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ( resolvedPath == null ) {
|
|
||||||
currentPath = currentPath.get( name );
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
currentPath = resolvedPath;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ( isTerminal ) {
|
if ( isTerminal ) {
|
||||||
return currentPath;
|
return currentPath;
|
||||||
}
|
}
|
||||||
|
@ -104,45 +50,6 @@ public class DomainPathPart implements SemanticPathPart {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private ModelPartContainer findModelPartContainer(SqmAttributeJoin<?, ?> attributeJoin, SqmCreationState creationState) {
|
|
||||||
final SqmFrom<?, ?> lhs = attributeJoin.getLhs();
|
|
||||||
if ( lhs instanceof SqmAttributeJoin<?, ?> ) {
|
|
||||||
final SqmAttributeJoin<?, ?> lhsAttributeJoin = (SqmAttributeJoin<?, ?>) lhs;
|
|
||||||
if ( lhsAttributeJoin.getReferencedPathSource() instanceof EntityDomainType<?> ) {
|
|
||||||
final String entityName = ( (EntityDomainType<?>) lhsAttributeJoin.getReferencedPathSource() ).getHibernateEntityName();
|
|
||||||
return (ModelPartContainer) creationState.getCreationContext().getQueryEngine()
|
|
||||||
.getTypeConfiguration()
|
|
||||||
.getSessionFactory()
|
|
||||||
.getMetamodel()
|
|
||||||
.entityPersister( entityName )
|
|
||||||
.findSubPart( attributeJoin.getAttribute().getName(), null );
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return (ModelPartContainer) findModelPartContainer( lhsAttributeJoin, creationState )
|
|
||||||
.findSubPart( attributeJoin.getAttribute().getName(), null );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
final String entityName;
|
|
||||||
if ( lhs instanceof SqmRoot<?> ) {
|
|
||||||
entityName = ( (SqmRoot<?>) lhs ).getEntityName();
|
|
||||||
}
|
|
||||||
else if ( lhs instanceof SqmEntityJoin<?> ) {
|
|
||||||
entityName = ( (SqmEntityJoin<?>) lhs ).getEntityName();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
assert lhs instanceof SqmCrossJoin<?>;
|
|
||||||
entityName = ( (SqmCrossJoin<?>) lhs ).getEntityName();
|
|
||||||
}
|
|
||||||
return (ModelPartContainer) creationState.getCreationContext().getQueryEngine()
|
|
||||||
.getTypeConfiguration()
|
|
||||||
.getSessionFactory()
|
|
||||||
.getMetamodel()
|
|
||||||
.entityPersister( entityName )
|
|
||||||
.findSubPart( attributeJoin.getAttribute().getName(), null );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SqmPath<?> resolveIndexedAccess(
|
public SqmPath<?> resolveIndexedAccess(
|
||||||
SqmExpression<?> selector,
|
SqmExpression<?> selector,
|
||||||
|
|
|
@ -9,7 +9,6 @@ package org.hibernate.query.hql.internal;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
import org.hibernate.metamodel.model.domain.EntityDomainType;
|
import org.hibernate.metamodel.model.domain.EntityDomainType;
|
||||||
import org.hibernate.query.NavigablePath;
|
|
||||||
import org.hibernate.query.SemanticException;
|
import org.hibernate.query.SemanticException;
|
||||||
import org.hibernate.query.hql.HqlInterpretationException;
|
import org.hibernate.query.hql.HqlInterpretationException;
|
||||||
import org.hibernate.query.hql.spi.DotIdentifierConsumer;
|
import org.hibernate.query.hql.spi.DotIdentifierConsumer;
|
||||||
|
@ -19,11 +18,13 @@ import org.hibernate.query.sqm.SqmJoinable;
|
||||||
import org.hibernate.query.sqm.SqmPathSource;
|
import org.hibernate.query.sqm.SqmPathSource;
|
||||||
import org.hibernate.query.hql.spi.SqmCreationProcessingState;
|
import org.hibernate.query.hql.spi.SqmCreationProcessingState;
|
||||||
import org.hibernate.query.hql.spi.SqmCreationState;
|
import org.hibernate.query.hql.spi.SqmCreationState;
|
||||||
|
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.SqmPolymorphicRootDescriptor;
|
import org.hibernate.query.sqm.tree.domain.SqmPolymorphicRootDescriptor;
|
||||||
import org.hibernate.query.sqm.tree.from.SqmAttributeJoin;
|
import org.hibernate.query.sqm.tree.from.SqmAttributeJoin;
|
||||||
import org.hibernate.query.sqm.tree.from.SqmEntityJoin;
|
import org.hibernate.query.sqm.tree.from.SqmEntityJoin;
|
||||||
import org.hibernate.query.sqm.tree.from.SqmFrom;
|
import org.hibernate.query.sqm.tree.from.SqmFrom;
|
||||||
|
import org.hibernate.query.sqm.tree.from.SqmJoin;
|
||||||
import org.hibernate.query.sqm.tree.from.SqmRoot;
|
import org.hibernate.query.sqm.tree.from.SqmRoot;
|
||||||
|
|
||||||
import org.jboss.logging.Logger;
|
import org.jboss.logging.Logger;
|
||||||
|
@ -37,7 +38,7 @@ public class QualifiedJoinPathConsumer implements DotIdentifierConsumer {
|
||||||
private static final Logger log = Logger.getLogger( QualifiedJoinPathConsumer.class );
|
private static final Logger log = Logger.getLogger( QualifiedJoinPathConsumer.class );
|
||||||
|
|
||||||
private final SqmCreationState creationState;
|
private final SqmCreationState creationState;
|
||||||
private final SqmRoot sqmRoot;
|
private final SqmRoot<?> sqmRoot;
|
||||||
|
|
||||||
private final SqmJoinType joinType;
|
private final SqmJoinType joinType;
|
||||||
private final boolean fetch;
|
private final boolean fetch;
|
||||||
|
@ -110,7 +111,7 @@ public class QualifiedJoinPathConsumer implements DotIdentifierConsumer {
|
||||||
final SqmCreationProcessingState processingState = creationState.getCurrentProcessingState();
|
final SqmCreationProcessingState processingState = creationState.getCurrentProcessingState();
|
||||||
final SqmPathRegistry pathRegistry = processingState.getPathRegistry();
|
final SqmPathRegistry pathRegistry = processingState.getPathRegistry();
|
||||||
|
|
||||||
final SqmFrom pathRootByAlias = pathRegistry.findFromByAlias( identifier );
|
final SqmFrom<?, Object> pathRootByAlias = pathRegistry.findFromByAlias( identifier );
|
||||||
if ( pathRootByAlias != null ) {
|
if ( pathRootByAlias != null ) {
|
||||||
// identifier is an alias (identification variable)
|
// identifier is an alias (identification variable)
|
||||||
|
|
||||||
|
@ -127,7 +128,7 @@ public class QualifiedJoinPathConsumer implements DotIdentifierConsumer {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
final SqmFrom pathRootByExposedNavigable = pathRegistry.findFromExposing( identifier );
|
final SqmFrom<?, Object> pathRootByExposedNavigable = pathRegistry.findFromExposing( identifier );
|
||||||
if ( pathRootByExposedNavigable != null ) {
|
if ( pathRootByExposedNavigable != null ) {
|
||||||
return new AttributeJoinDelegate(
|
return new AttributeJoinDelegate(
|
||||||
createJoin( pathRootByExposedNavigable, identifier, isTerminal ),
|
createJoin( pathRootByExposedNavigable, identifier, isTerminal ),
|
||||||
|
@ -152,7 +153,7 @@ public class QualifiedJoinPathConsumer implements DotIdentifierConsumer {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private SqmFrom createJoin(SqmFrom lhs, String identifier, boolean isTerminal) {
|
private SqmFrom<?, ?> createJoin(SqmFrom<?, Object> lhs, String identifier, boolean isTerminal) {
|
||||||
return createJoin(
|
return createJoin(
|
||||||
lhs,
|
lhs,
|
||||||
identifier,
|
identifier,
|
||||||
|
@ -164,15 +165,16 @@ public class QualifiedJoinPathConsumer implements DotIdentifierConsumer {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static SqmFrom createJoin(
|
private static SqmFrom<?, Object> createJoin(
|
||||||
SqmFrom lhs,
|
SqmFrom<?, Object> lhs,
|
||||||
String name,
|
String name,
|
||||||
SqmJoinType joinType,
|
SqmJoinType joinType,
|
||||||
String alias,
|
String alias,
|
||||||
boolean fetch,
|
boolean fetch,
|
||||||
boolean isTerminal,
|
boolean isTerminal,
|
||||||
SqmCreationState creationState) {
|
SqmCreationState creationState) {
|
||||||
final SqmPathSource subPathSource = lhs.getReferencedPathSource().findSubPathSource( name );
|
//noinspection unchecked
|
||||||
|
final SqmPathSource<Object> subPathSource = (SqmPathSource<Object>) lhs.getReferencedPathSource().findSubPathSource( name );
|
||||||
if ( subPathSource == null ) {
|
if ( subPathSource == null ) {
|
||||||
throw new HqlInterpretationException(
|
throw new HqlInterpretationException(
|
||||||
String.format(
|
String.format(
|
||||||
|
@ -183,14 +185,22 @@ public class QualifiedJoinPathConsumer implements DotIdentifierConsumer {
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
final SqmAttributeJoin join = ( (SqmJoinable) subPathSource ).createSqmJoin(
|
if ( !isTerminal ) {
|
||||||
|
for ( SqmJoin<?, ?> sqmJoin : lhs.getSqmJoins() ) {
|
||||||
|
if ( sqmJoin.getAlias() == null && sqmJoin.getReferencedPathSource() == subPathSource ) {
|
||||||
|
//noinspection unchecked
|
||||||
|
return (SqmFrom<?, Object>) sqmJoin;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
final SqmAttributeJoin<Object, Object> join = ( (SqmJoinable) subPathSource ).createSqmJoin(
|
||||||
lhs,
|
lhs,
|
||||||
joinType,
|
joinType,
|
||||||
isTerminal ? alias : null,
|
isTerminal ? alias : SqmCreationHelper.IMPLICIT_ALIAS,
|
||||||
fetch,
|
fetch,
|
||||||
creationState
|
creationState
|
||||||
);
|
);
|
||||||
//noinspection unchecked
|
|
||||||
lhs.addSqmJoin( join );
|
lhs.addSqmJoin( join );
|
||||||
creationState.getCurrentProcessingState().getPathRegistry().register( join );
|
creationState.getCurrentProcessingState().getPathRegistry().register( join );
|
||||||
return join;
|
return join;
|
||||||
|
@ -209,10 +219,10 @@ public class QualifiedJoinPathConsumer implements DotIdentifierConsumer {
|
||||||
private final boolean fetch;
|
private final boolean fetch;
|
||||||
private final String alias;
|
private final String alias;
|
||||||
|
|
||||||
private SqmFrom currentPath;
|
private SqmFrom<?, Object> currentPath;
|
||||||
|
|
||||||
public AttributeJoinDelegate(
|
public AttributeJoinDelegate(
|
||||||
SqmFrom base,
|
SqmFrom<?, ?> base,
|
||||||
SqmJoinType joinType,
|
SqmJoinType joinType,
|
||||||
boolean fetch,
|
boolean fetch,
|
||||||
String alias,
|
String alias,
|
||||||
|
@ -222,7 +232,8 @@ public class QualifiedJoinPathConsumer implements DotIdentifierConsumer {
|
||||||
this.alias = alias;
|
this.alias = alias;
|
||||||
this.creationState = creationState;
|
this.creationState = creationState;
|
||||||
|
|
||||||
this.currentPath = base;
|
//noinspection unchecked
|
||||||
|
this.currentPath = (SqmFrom<?, Object>) base;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -240,7 +251,7 @@ public class QualifiedJoinPathConsumer implements DotIdentifierConsumer {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void consumeTreat(String entityName, boolean isTerminal) {
|
public void consumeTreat(String entityName, boolean isTerminal) {
|
||||||
final EntityDomainType<?> entityDomainType = creationState.getCreationContext().getJpaMetamodel()
|
final EntityDomainType<Object> entityDomainType = creationState.getCreationContext().getJpaMetamodel()
|
||||||
.entity( entityName );
|
.entity( entityName );
|
||||||
currentPath = currentPath.treatAs( entityDomainType, isTerminal ? alias : null );
|
currentPath = currentPath.treatAs( entityDomainType, isTerminal ? alias : null );
|
||||||
creationState.getCurrentProcessingState().getPathRegistry().register( currentPath );
|
creationState.getCurrentProcessingState().getPathRegistry().register( currentPath );
|
||||||
|
@ -254,7 +265,7 @@ public class QualifiedJoinPathConsumer implements DotIdentifierConsumer {
|
||||||
|
|
||||||
private static class ExpectingEntityJoinDelegate implements ConsumerDelegate {
|
private static class ExpectingEntityJoinDelegate implements ConsumerDelegate {
|
||||||
private final SqmCreationState creationState;
|
private final SqmCreationState creationState;
|
||||||
private final SqmRoot sqmRoot;
|
private final SqmRoot<?> sqmRoot;
|
||||||
|
|
||||||
private final SqmJoinType joinType;
|
private final SqmJoinType joinType;
|
||||||
private final boolean fetch;
|
private final boolean fetch;
|
||||||
|
@ -267,7 +278,7 @@ public class QualifiedJoinPathConsumer implements DotIdentifierConsumer {
|
||||||
public ExpectingEntityJoinDelegate(
|
public ExpectingEntityJoinDelegate(
|
||||||
String identifier,
|
String identifier,
|
||||||
boolean isTerminal,
|
boolean isTerminal,
|
||||||
SqmRoot sqmRoot,
|
SqmRoot<?> sqmRoot,
|
||||||
SqmJoinType joinType,
|
SqmJoinType joinType,
|
||||||
String alias,
|
String alias,
|
||||||
boolean fetch,
|
boolean fetch,
|
||||||
|
|
|
@ -20,9 +20,14 @@ import org.hibernate.query.hql.spi.SqmCreationOptions;
|
||||||
import org.hibernate.query.hql.spi.SqmCreationProcessingState;
|
import org.hibernate.query.hql.spi.SqmCreationProcessingState;
|
||||||
import org.hibernate.query.hql.spi.SqmCreationState;
|
import org.hibernate.query.hql.spi.SqmCreationState;
|
||||||
import org.hibernate.query.hql.spi.SqmPathRegistry;
|
import org.hibernate.query.hql.spi.SqmPathRegistry;
|
||||||
|
import org.hibernate.query.sqm.internal.SqmDmlCreationProcessingState;
|
||||||
import org.hibernate.query.sqm.internal.SqmQuerySpecCreationProcessingStateStandardImpl;
|
import org.hibernate.query.sqm.internal.SqmQuerySpecCreationProcessingStateStandardImpl;
|
||||||
import org.hibernate.query.sqm.spi.BaseSemanticQueryWalker;
|
import org.hibernate.query.sqm.spi.BaseSemanticQueryWalker;
|
||||||
import org.hibernate.query.sqm.spi.SqmCreationContext;
|
import org.hibernate.query.sqm.spi.SqmCreationContext;
|
||||||
|
import org.hibernate.query.sqm.tree.SqmDeleteOrUpdateStatement;
|
||||||
|
import org.hibernate.query.sqm.tree.SqmQuery;
|
||||||
|
import org.hibernate.query.sqm.tree.cte.SqmCteContainer;
|
||||||
|
import org.hibernate.query.sqm.tree.cte.SqmCteStatement;
|
||||||
import org.hibernate.query.sqm.tree.delete.SqmDeleteStatement;
|
import org.hibernate.query.sqm.tree.delete.SqmDeleteStatement;
|
||||||
import org.hibernate.query.sqm.tree.domain.SqmBasicValuedSimplePath;
|
import org.hibernate.query.sqm.tree.domain.SqmBasicValuedSimplePath;
|
||||||
import org.hibernate.query.sqm.tree.domain.SqmEmbeddedValuedSimplePath;
|
import org.hibernate.query.sqm.tree.domain.SqmEmbeddedValuedSimplePath;
|
||||||
|
@ -81,6 +86,7 @@ import org.hibernate.type.descriptor.java.JavaType;
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
||||||
*/
|
*/
|
||||||
public class QuerySplitter {
|
public class QuerySplitter {
|
||||||
|
|
||||||
public static <R> SqmSelectStatement<R>[] split(
|
public static <R> SqmSelectStatement<R>[] split(
|
||||||
SqmSelectStatement<R> statement,
|
SqmSelectStatement<R> statement,
|
||||||
SessionFactoryImplementor sessionFactory) {
|
SessionFactoryImplementor sessionFactory) {
|
||||||
|
@ -128,10 +134,46 @@ public class QuerySplitter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static <R> SqmDeleteStatement<R>[] split(
|
||||||
|
SqmDeleteStatement<R> statement,
|
||||||
|
SessionFactoryImplementor sessionFactory) {
|
||||||
|
// We only allow unmapped polymorphism in a very restricted way. Specifically,
|
||||||
|
// the unmapped polymorphic reference can only be a root and can be the only
|
||||||
|
// root. Use that restriction to locate the unmapped polymorphic reference
|
||||||
|
final SqmRoot<?> unmappedPolymorphicReference = findUnmappedPolymorphicReference( statement );
|
||||||
|
|
||||||
|
if ( unmappedPolymorphicReference == null ) {
|
||||||
|
return new SqmDeleteStatement[] { statement };
|
||||||
|
}
|
||||||
|
|
||||||
|
final SqmPolymorphicRootDescriptor<?> unmappedPolymorphicDescriptor = (SqmPolymorphicRootDescriptor<?>) unmappedPolymorphicReference.getReferencedPathSource();
|
||||||
|
final SqmDeleteStatement<R>[] expanded = new SqmDeleteStatement[ unmappedPolymorphicDescriptor.getImplementors().size() ];
|
||||||
|
|
||||||
|
int i = -1;
|
||||||
|
for ( EntityDomainType<?> mappedDescriptor : unmappedPolymorphicDescriptor.getImplementors() ) {
|
||||||
|
i++;
|
||||||
|
final UnmappedPolymorphismReplacer<R> replacer = new UnmappedPolymorphismReplacer<>(
|
||||||
|
unmappedPolymorphicReference,
|
||||||
|
mappedDescriptor,
|
||||||
|
sessionFactory
|
||||||
|
);
|
||||||
|
expanded[i] = replacer.visitDeleteStatement( statement );
|
||||||
|
}
|
||||||
|
|
||||||
|
return expanded;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static SqmRoot<?> findUnmappedPolymorphicReference(SqmDeleteOrUpdateStatement<?> queryPart) {
|
||||||
|
if ( queryPart.getTarget().getReferencedPathSource() instanceof SqmPolymorphicRootDescriptor<?> ) {
|
||||||
|
return queryPart.getTarget();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
private static class UnmappedPolymorphismReplacer<R> extends BaseSemanticQueryWalker implements SqmCreationState {
|
private static class UnmappedPolymorphismReplacer<R> extends BaseSemanticQueryWalker implements SqmCreationState {
|
||||||
private final SqmRoot unmappedPolymorphicFromElement;
|
private final SqmRoot unmappedPolymorphicFromElement;
|
||||||
private final EntityDomainType mappedDescriptor;
|
private final EntityDomainType<R> mappedDescriptor;
|
||||||
private final SqmCreationContext creationContext;
|
private final SqmCreationContext creationContext;
|
||||||
private final Stack<SqmCreationProcessingState> processingStateStack = new StandardStack<>();
|
private final Stack<SqmCreationProcessingState> processingStateStack = new StandardStack<>();
|
||||||
|
|
||||||
|
@ -168,9 +210,56 @@ public class QuerySplitter {
|
||||||
throw new UnsupportedOperationException( "Not valid" );
|
throw new UnsupportedOperationException( "Not valid" );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object visitCteContainer(SqmCteContainer consumer) {
|
||||||
|
final SqmCteContainer processingQuery = (SqmCteContainer) getProcessingStateStack().getCurrent()
|
||||||
|
.getProcessingQuery();
|
||||||
|
processingQuery.setWithRecursive( consumer.isWithRecursive() );
|
||||||
|
for ( SqmCteStatement<?> cteStatement : consumer.getCteStatements() ) {
|
||||||
|
processingQuery.addCteStatement( visitCteStatement( cteStatement ) );
|
||||||
|
}
|
||||||
|
return processingQuery;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SqmCteStatement<?> visitCteStatement(SqmCteStatement<?> sqmCteStatement) {
|
||||||
|
// No need to copy anything here
|
||||||
|
return sqmCteStatement;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SqmDeleteStatement<R> visitDeleteStatement(SqmDeleteStatement<?> statement) {
|
public SqmDeleteStatement<R> visitDeleteStatement(SqmDeleteStatement<?> statement) {
|
||||||
throw new UnsupportedOperationException( "Not valid" );
|
final SqmRoot<?> sqmRoot = statement.getTarget();
|
||||||
|
final SqmRoot<R> copy = new SqmRoot<>(
|
||||||
|
mappedDescriptor,
|
||||||
|
sqmRoot.getExplicitAlias(),
|
||||||
|
sqmRoot.isAllowJoins(),
|
||||||
|
sqmRoot.nodeBuilder()
|
||||||
|
);
|
||||||
|
sqmFromCopyMap.put( sqmRoot, copy );
|
||||||
|
sqmPathCopyMap.put( sqmRoot.getNavigablePath(), copy );
|
||||||
|
final SqmDeleteStatement<R> statementCopy = new SqmDeleteStatement<>(
|
||||||
|
copy,
|
||||||
|
statement.getQuerySource(),
|
||||||
|
statement.nodeBuilder()
|
||||||
|
);
|
||||||
|
|
||||||
|
processingStateStack.push(
|
||||||
|
new SqmDmlCreationProcessingState(
|
||||||
|
statementCopy,
|
||||||
|
this
|
||||||
|
)
|
||||||
|
);
|
||||||
|
getProcessingStateStack().getCurrent().getPathRegistry().register( copy );
|
||||||
|
try {
|
||||||
|
visitCteContainer( statement );
|
||||||
|
statementCopy.setWhereClause( visitWhereClause( statement.getWhereClause() ) );
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
processingStateStack.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
return statementCopy;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -185,6 +274,7 @@ public class QuerySplitter {
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
try {
|
try {
|
||||||
|
visitCteContainer( statement );
|
||||||
copy.setQueryPart( visitQueryPart( statement.getQueryPart() ) );
|
copy.setQueryPart( visitQueryPart( statement.getQueryPart() ) );
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
|
@ -292,15 +382,13 @@ public class QuerySplitter {
|
||||||
sqmRoot.isAllowJoins(),
|
sqmRoot.isAllowJoins(),
|
||||||
sqmRoot.nodeBuilder()
|
sqmRoot.nodeBuilder()
|
||||||
);
|
);
|
||||||
return (SqmRoot<?>) getProcessingStateStack().getCurrent().getPathRegistry().resolvePath(
|
getProcessingStateStack().getCurrent().getPathRegistry().register( copy );
|
||||||
copy.getNavigablePath(),
|
sqmFromCopyMap.put( sqmRoot, copy );
|
||||||
navigablePath -> {
|
sqmPathCopyMap.put( sqmRoot.getNavigablePath(), copy );
|
||||||
sqmFromCopyMap.put( sqmRoot, copy );
|
if ( currentFromClauseCopy != null ) {
|
||||||
sqmPathCopyMap.put( sqmRoot.getNavigablePath(), copy );
|
currentFromClauseCopy.addRoot( copy );
|
||||||
currentFromClauseCopy.addRoot( copy );
|
}
|
||||||
return copy;
|
return copy;
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -309,21 +397,17 @@ public class QuerySplitter {
|
||||||
if ( sqmFrom != null ) {
|
if ( sqmFrom != null ) {
|
||||||
return (SqmCrossJoin<?>) sqmFrom;
|
return (SqmCrossJoin<?>) sqmFrom;
|
||||||
}
|
}
|
||||||
return (SqmCrossJoin<?>) getProcessingStateStack().getCurrent().getPathRegistry().resolvePath(
|
final SqmRoot<?> sqmRoot = (SqmRoot<?>) sqmFromCopyMap.get( join.findRoot() );
|
||||||
join.getNavigablePath(),
|
final SqmCrossJoin copy = new SqmCrossJoin<>(
|
||||||
navigablePath -> {
|
join.getReferencedPathSource(),
|
||||||
final SqmRoot<?> sqmRoot = (SqmRoot<?>) sqmFromCopyMap.get( join.findRoot() );
|
join.getExplicitAlias(),
|
||||||
final SqmCrossJoin copy = new SqmCrossJoin<>(
|
sqmRoot
|
||||||
join.getReferencedPathSource(),
|
|
||||||
join.getExplicitAlias(),
|
|
||||||
sqmRoot
|
|
||||||
);
|
|
||||||
sqmFromCopyMap.put( join, copy );
|
|
||||||
sqmPathCopyMap.put( join.getNavigablePath(), copy );
|
|
||||||
sqmRoot.addSqmJoin( copy );
|
|
||||||
return copy;
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
|
getProcessingStateStack().getCurrent().getPathRegistry().register( copy );
|
||||||
|
sqmFromCopyMap.put( join, copy );
|
||||||
|
sqmPathCopyMap.put( join.getNavigablePath(), copy );
|
||||||
|
sqmRoot.addSqmJoin( copy );
|
||||||
|
return copy;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -332,22 +416,18 @@ public class QuerySplitter {
|
||||||
if ( sqmFrom != null ) {
|
if ( sqmFrom != null ) {
|
||||||
return (SqmEntityJoin<?>) sqmFrom;
|
return (SqmEntityJoin<?>) sqmFrom;
|
||||||
}
|
}
|
||||||
return (SqmEntityJoin<?>) getProcessingStateStack().getCurrent().getPathRegistry().resolvePath(
|
final SqmRoot<?> sqmRoot = (SqmRoot<?>) sqmFromCopyMap.get( join.findRoot() );
|
||||||
join.getNavigablePath(),
|
final SqmEntityJoin copy = new SqmEntityJoin<>(
|
||||||
navigablePath -> {
|
join.getReferencedPathSource(),
|
||||||
final SqmRoot<?> sqmRoot = (SqmRoot<?>) sqmFromCopyMap.get( join.findRoot() );
|
join.getExplicitAlias(),
|
||||||
final SqmEntityJoin copy = new SqmEntityJoin<>(
|
join.getSqmJoinType(),
|
||||||
join.getReferencedPathSource(),
|
sqmRoot
|
||||||
join.getExplicitAlias(),
|
|
||||||
join.getSqmJoinType(),
|
|
||||||
sqmRoot
|
|
||||||
);
|
|
||||||
sqmFromCopyMap.put( join, copy );
|
|
||||||
sqmPathCopyMap.put( join.getNavigablePath(), copy );
|
|
||||||
sqmRoot.addSqmJoin( copy );
|
|
||||||
return copy;
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
|
getProcessingStateStack().getCurrent().getPathRegistry().register( copy );
|
||||||
|
sqmFromCopyMap.put( join, copy );
|
||||||
|
sqmPathCopyMap.put( join.getNavigablePath(), copy );
|
||||||
|
sqmRoot.addSqmJoin( copy );
|
||||||
|
return copy;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -356,92 +436,69 @@ public class QuerySplitter {
|
||||||
if ( sqmFrom != null ) {
|
if ( sqmFrom != null ) {
|
||||||
return (SqmAttributeJoin<?, ?>) sqmFrom;
|
return (SqmAttributeJoin<?, ?>) sqmFrom;
|
||||||
}
|
}
|
||||||
return (SqmAttributeJoin<?, ?>) getProcessingStateStack().getCurrent().getPathRegistry().resolvePath(
|
SqmAttributeJoin copy = join.makeCopy( getProcessingStateStack().getCurrent() );
|
||||||
join.getNavigablePath(),
|
getProcessingStateStack().getCurrent().getPathRegistry().register( copy );
|
||||||
navigablePath -> {
|
sqmFromCopyMap.put( join, copy );
|
||||||
SqmAttributeJoin copy = join.makeCopy( getProcessingStateStack().getCurrent() );
|
sqmPathCopyMap.put( join.getNavigablePath(), copy );
|
||||||
sqmFromCopyMap.put( join, copy );
|
( (SqmFrom<?, ?>) copy.getParent() ).addSqmJoin( copy );
|
||||||
sqmPathCopyMap.put( join.getNavigablePath(), copy );
|
return copy;
|
||||||
( (SqmFrom<?, ?>) copy.getParent() ).addSqmJoin( copy );
|
|
||||||
return copy;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SqmBasicValuedSimplePath<?> visitBasicValuedPath(SqmBasicValuedSimplePath<?> path) {
|
public SqmBasicValuedSimplePath<?> visitBasicValuedPath(SqmBasicValuedSimplePath<?> path) {
|
||||||
final SqmPathRegistry pathRegistry = getProcessingStateStack().getCurrent().getPathRegistry();
|
final SqmPathRegistry pathRegistry = getProcessingStateStack().getCurrent().getPathRegistry();
|
||||||
|
|
||||||
return (SqmBasicValuedSimplePath<?>) pathRegistry.resolvePath(
|
final SqmBasicValuedSimplePath<?> copy = new SqmBasicValuedSimplePath<>(
|
||||||
path.getNavigablePath(),
|
path.getNavigablePath(),
|
||||||
navigablePath -> {
|
path.getReferencedPathSource(),
|
||||||
final SqmBasicValuedSimplePath<?> copy = new SqmBasicValuedSimplePath<>(
|
pathRegistry.findFromByPath( path.getLhs().getNavigablePath() ),
|
||||||
navigablePath,
|
path.nodeBuilder()
|
||||||
path.getReferencedPathSource(),
|
|
||||||
pathRegistry.findFromByPath( path.getLhs().getNavigablePath() ),
|
|
||||||
path.nodeBuilder()
|
|
||||||
);
|
|
||||||
sqmPathCopyMap.put( path.getNavigablePath(), copy );
|
|
||||||
return copy;
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
|
pathRegistry.register( copy );
|
||||||
|
sqmPathCopyMap.put( path.getNavigablePath(), copy );
|
||||||
|
return copy;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SqmEmbeddedValuedSimplePath<?> visitEmbeddableValuedPath(SqmEmbeddedValuedSimplePath<?> path) {
|
public SqmEmbeddedValuedSimplePath<?> visitEmbeddableValuedPath(SqmEmbeddedValuedSimplePath<?> path) {
|
||||||
final SqmPathRegistry pathRegistry = getProcessingStateStack().getCurrent().getPathRegistry();
|
final SqmPathRegistry pathRegistry = getProcessingStateStack().getCurrent().getPathRegistry();
|
||||||
|
final SqmEmbeddedValuedSimplePath<?> copy = new SqmEmbeddedValuedSimplePath<>(
|
||||||
return (SqmEmbeddedValuedSimplePath<?>) pathRegistry.resolvePath(
|
|
||||||
path.getNavigablePath(),
|
path.getNavigablePath(),
|
||||||
navigablePath -> {
|
path.getReferencedPathSource(),
|
||||||
final SqmEmbeddedValuedSimplePath<?> copy = new SqmEmbeddedValuedSimplePath<>(
|
pathRegistry.findFromByPath( path.getLhs().getNavigablePath() ),
|
||||||
navigablePath,
|
path.nodeBuilder()
|
||||||
path.getReferencedPathSource(),
|
|
||||||
pathRegistry.findFromByPath( path.getLhs().getNavigablePath() ),
|
|
||||||
path.nodeBuilder()
|
|
||||||
);
|
|
||||||
sqmPathCopyMap.put( path.getNavigablePath(), copy );
|
|
||||||
return copy;
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
|
pathRegistry.register( copy );
|
||||||
|
sqmPathCopyMap.put( path.getNavigablePath(), copy );
|
||||||
|
return copy;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SqmEntityValuedSimplePath<?> visitEntityValuedPath(SqmEntityValuedSimplePath<?> path) {
|
public SqmEntityValuedSimplePath<?> visitEntityValuedPath(SqmEntityValuedSimplePath<?> path) {
|
||||||
final SqmPathRegistry pathRegistry = getProcessingStateStack().getCurrent().getPathRegistry();
|
final SqmPathRegistry pathRegistry = getProcessingStateStack().getCurrent().getPathRegistry();
|
||||||
|
final SqmEntityValuedSimplePath<?> copy = new SqmEntityValuedSimplePath<>(
|
||||||
return (SqmEntityValuedSimplePath<?>) pathRegistry.resolvePath(
|
|
||||||
path.getNavigablePath(),
|
path.getNavigablePath(),
|
||||||
navigablePath -> {
|
path.getReferencedPathSource(),
|
||||||
final SqmEntityValuedSimplePath<?> copy = new SqmEntityValuedSimplePath<>(
|
pathRegistry.findFromByPath( path.getLhs().getNavigablePath() ),
|
||||||
navigablePath,
|
path.nodeBuilder()
|
||||||
path.getReferencedPathSource(),
|
|
||||||
pathRegistry.findFromByPath( path.getLhs().getNavigablePath() ),
|
|
||||||
path.nodeBuilder()
|
|
||||||
);
|
|
||||||
sqmPathCopyMap.put( path.getNavigablePath(), copy );
|
|
||||||
return copy;
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
|
pathRegistry.register( copy );
|
||||||
|
sqmPathCopyMap.put( path.getNavigablePath(), copy );
|
||||||
|
return copy;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SqmPluralValuedSimplePath<?> visitPluralValuedPath(SqmPluralValuedSimplePath<?> path) {
|
public SqmPluralValuedSimplePath<?> visitPluralValuedPath(SqmPluralValuedSimplePath<?> path) {
|
||||||
final SqmPathRegistry pathRegistry = getProcessingStateStack().getCurrent().getPathRegistry();
|
final SqmPathRegistry pathRegistry = getProcessingStateStack().getCurrent().getPathRegistry();
|
||||||
|
final SqmPluralValuedSimplePath<?> copy = new SqmPluralValuedSimplePath<>(
|
||||||
return (SqmPluralValuedSimplePath<?>) pathRegistry.resolvePath(
|
|
||||||
path.getNavigablePath(),
|
path.getNavigablePath(),
|
||||||
navigablePath -> {
|
path.getReferencedPathSource(),
|
||||||
final SqmPluralValuedSimplePath<?> copy = new SqmPluralValuedSimplePath<>(
|
pathRegistry.findFromByPath( path.getLhs().getNavigablePath() ),
|
||||||
navigablePath,
|
path.nodeBuilder()
|
||||||
path.getReferencedPathSource(),
|
|
||||||
pathRegistry.findFromByPath( path.getLhs().getNavigablePath() ),
|
|
||||||
path.nodeBuilder()
|
|
||||||
);
|
|
||||||
sqmPathCopyMap.put( path.getNavigablePath(), copy );
|
|
||||||
return copy;
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
|
pathRegistry.register( copy );
|
||||||
|
sqmPathCopyMap.put( path.getNavigablePath(), copy );
|
||||||
|
return copy;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -41,6 +41,7 @@ import org.hibernate.grammars.hql.HqlLexer;
|
||||||
import org.hibernate.grammars.hql.HqlParser;
|
import org.hibernate.grammars.hql.HqlParser;
|
||||||
import org.hibernate.grammars.hql.HqlParserBaseVisitor;
|
import org.hibernate.grammars.hql.HqlParserBaseVisitor;
|
||||||
import org.hibernate.internal.util.CharSequenceHelper;
|
import org.hibernate.internal.util.CharSequenceHelper;
|
||||||
|
import org.hibernate.internal.util.QuotingHelper;
|
||||||
import org.hibernate.internal.util.collections.Stack;
|
import org.hibernate.internal.util.collections.Stack;
|
||||||
import org.hibernate.internal.util.collections.StandardStack;
|
import org.hibernate.internal.util.collections.StandardStack;
|
||||||
import org.hibernate.metamodel.CollectionClassification;
|
import org.hibernate.metamodel.CollectionClassification;
|
||||||
|
@ -106,7 +107,6 @@ import org.hibernate.query.sqm.tree.domain.SqmMinIndexPath;
|
||||||
import org.hibernate.query.sqm.tree.domain.SqmPath;
|
import org.hibernate.query.sqm.tree.domain.SqmPath;
|
||||||
import org.hibernate.query.sqm.tree.domain.SqmPluralValuedSimplePath;
|
import org.hibernate.query.sqm.tree.domain.SqmPluralValuedSimplePath;
|
||||||
import org.hibernate.query.sqm.tree.domain.SqmPolymorphicRootDescriptor;
|
import org.hibernate.query.sqm.tree.domain.SqmPolymorphicRootDescriptor;
|
||||||
import org.hibernate.query.sqm.tree.domain.SqmTreatedPath;
|
|
||||||
import org.hibernate.query.sqm.tree.expression.SqmAliasedNodeRef;
|
import org.hibernate.query.sqm.tree.expression.SqmAliasedNodeRef;
|
||||||
import org.hibernate.query.sqm.tree.expression.SqmAny;
|
import org.hibernate.query.sqm.tree.expression.SqmAny;
|
||||||
import org.hibernate.query.sqm.tree.expression.SqmBinaryArithmetic;
|
import org.hibernate.query.sqm.tree.expression.SqmBinaryArithmetic;
|
||||||
|
@ -134,7 +134,6 @@ import org.hibernate.query.sqm.tree.expression.SqmToDuration;
|
||||||
import org.hibernate.query.sqm.tree.expression.SqmTrimSpecification;
|
import org.hibernate.query.sqm.tree.expression.SqmTrimSpecification;
|
||||||
import org.hibernate.query.sqm.tree.expression.SqmTuple;
|
import org.hibernate.query.sqm.tree.expression.SqmTuple;
|
||||||
import org.hibernate.query.sqm.tree.expression.SqmUnaryOperation;
|
import org.hibernate.query.sqm.tree.expression.SqmUnaryOperation;
|
||||||
import org.hibernate.query.sqm.tree.from.DowncastLocation;
|
|
||||||
import org.hibernate.query.sqm.tree.from.SqmAttributeJoin;
|
import org.hibernate.query.sqm.tree.from.SqmAttributeJoin;
|
||||||
import org.hibernate.query.sqm.tree.from.SqmCrossJoin;
|
import org.hibernate.query.sqm.tree.from.SqmCrossJoin;
|
||||||
import org.hibernate.query.sqm.tree.from.SqmEntityJoin;
|
import org.hibernate.query.sqm.tree.from.SqmEntityJoin;
|
||||||
|
@ -339,7 +338,7 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
|
||||||
final ParseTree parseTree = ctx.getChild( 0 );
|
final ParseTree parseTree = ctx.getChild( 0 );
|
||||||
if ( parseTree instanceof HqlParser.SelectStatementContext ) {
|
if ( parseTree instanceof HqlParser.SelectStatementContext ) {
|
||||||
final SqmSelectStatement<R> selectStatement = visitSelectStatement( (HqlParser.SelectStatementContext) parseTree );
|
final SqmSelectStatement<R> selectStatement = visitSelectStatement( (HqlParser.SelectStatementContext) parseTree );
|
||||||
selectStatement.getQueryPart().validateQueryGroupFetchStructure();
|
selectStatement.getQueryPart().validateFetchStructureAndOwners();
|
||||||
return selectStatement;
|
return selectStatement;
|
||||||
}
|
}
|
||||||
else if ( parseTree instanceof HqlParser.InsertStatementContext ) {
|
else if ( parseTree instanceof HqlParser.InsertStatementContext ) {
|
||||||
|
@ -425,6 +424,11 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
|
||||||
dmlTargetIndex + 1
|
dmlTargetIndex + 1
|
||||||
);
|
);
|
||||||
final SqmRoot<R> root = visitDmlTarget( dmlTargetContext );
|
final SqmRoot<R> root = visitDmlTarget( dmlTargetContext );
|
||||||
|
if ( root.getReferencedPathSource() instanceof SqmPolymorphicRootDescriptor<?> ) {
|
||||||
|
throw new SemanticException(
|
||||||
|
"Can't create an INSERT for a non entity name: " + root.getReferencedPathSource().getHibernateEntityName()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
final HqlParser.QueryExpressionContext queryExpressionContext = ctx.queryExpression();
|
final HqlParser.QueryExpressionContext queryExpressionContext = ctx.queryExpression();
|
||||||
if ( queryExpressionContext != null ) {
|
if ( queryExpressionContext != null ) {
|
||||||
|
@ -477,10 +481,12 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
|
||||||
processingState.getPathRegistry().register( root );
|
processingState.getPathRegistry().register( root );
|
||||||
|
|
||||||
try {
|
try {
|
||||||
for ( HqlParser.ValuesContext values : ctx.valuesList().values() ) {
|
final HqlParser.ValuesListContext valuesListContext = ctx.valuesList();
|
||||||
SqmValues sqmValues = new SqmValues();
|
for ( int i = 1; i < valuesListContext.getChildCount(); i += 2 ) {
|
||||||
for ( HqlParser.ExpressionContext expressionContext : values.expression() ) {
|
final ParseTree values = valuesListContext.getChild( i );
|
||||||
sqmValues.getExpressions().add( (SqmExpression<?>) expressionContext.accept( this ) );
|
final SqmValues sqmValues = new SqmValues();
|
||||||
|
for ( int j = 1; j < values.getChildCount(); j += 2 ) {
|
||||||
|
sqmValues.getExpressions().add( (SqmExpression<?>) values.getChild( j ).accept( this ) );
|
||||||
}
|
}
|
||||||
insertStatement.getValuesList().add( sqmValues );
|
insertStatement.getValuesList().add( sqmValues );
|
||||||
}
|
}
|
||||||
|
@ -506,6 +512,11 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
|
||||||
final int dmlTargetIndex = versioned ? 2 : 1;
|
final int dmlTargetIndex = versioned ? 2 : 1;
|
||||||
final HqlParser.DmlTargetContext dmlTargetContext = (HqlParser.DmlTargetContext) ctx.getChild( dmlTargetIndex );
|
final HqlParser.DmlTargetContext dmlTargetContext = (HqlParser.DmlTargetContext) ctx.getChild( dmlTargetIndex );
|
||||||
final SqmRoot<R> root = visitDmlTarget( dmlTargetContext );
|
final SqmRoot<R> root = visitDmlTarget( dmlTargetContext );
|
||||||
|
if ( root.getReferencedPathSource() instanceof SqmPolymorphicRootDescriptor<?> ) {
|
||||||
|
throw new SemanticException(
|
||||||
|
"Can't create an UPDATE for a non entity name: " + root.getReferencedPathSource().getHibernateEntityName()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
final SqmUpdateStatement<R> updateStatement = new SqmUpdateStatement<>( root, creationContext.getNodeBuilder() );
|
final SqmUpdateStatement<R> updateStatement = new SqmUpdateStatement<>( root, creationContext.getNodeBuilder() );
|
||||||
parameterCollector = updateStatement;
|
parameterCollector = updateStatement;
|
||||||
|
@ -884,7 +895,7 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
resultIdentifier = applyJpaCompliance(
|
resultIdentifier = applyJpaCompliance(
|
||||||
visitResultIdentifier( (HqlParser.ResultIdentifierContext) ctx.getChild( 1 ) )
|
visitIdentificationVariableDef( (HqlParser.IdentificationVariableDefContext) ctx.getChild( 1 ) )
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
final SqmSelectableNode<?> selectableNode = visitSelectableNode( ctx );
|
final SqmSelectableNode<?> selectableNode = visitSelectableNode( ctx );
|
||||||
|
@ -912,16 +923,7 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
|
||||||
|
|
||||||
private SqmSelectableNode<?> visitSelectableNode(HqlParser.SelectionContext ctx) {
|
private SqmSelectableNode<?> visitSelectableNode(HqlParser.SelectionContext ctx) {
|
||||||
final ParseTree subCtx = ctx.getChild( 0 ).getChild( 0 );
|
final ParseTree subCtx = ctx.getChild( 0 ).getChild( 0 );
|
||||||
if ( subCtx instanceof HqlParser.DynamicInstantiationContext ) {
|
if ( subCtx instanceof HqlParser.ExpressionOrPredicateContext ) {
|
||||||
return visitDynamicInstantiation( (HqlParser.DynamicInstantiationContext) subCtx );
|
|
||||||
}
|
|
||||||
else if ( subCtx instanceof HqlParser.JpaSelectObjectSyntaxContext ) {
|
|
||||||
return visitJpaSelectObjectSyntax( (HqlParser.JpaSelectObjectSyntaxContext) subCtx );
|
|
||||||
}
|
|
||||||
else if ( subCtx instanceof HqlParser.MapEntrySelectionContext ) {
|
|
||||||
return visitMapEntrySelection( (HqlParser.MapEntrySelectionContext) subCtx );
|
|
||||||
}
|
|
||||||
else if ( subCtx instanceof HqlParser.ExpressionContext ) {
|
|
||||||
final SqmExpression<?> sqmExpression = (SqmExpression<?>) subCtx.accept( this );
|
final SqmExpression<?> sqmExpression = (SqmExpression<?>) subCtx.accept( this );
|
||||||
if ( sqmExpression instanceof SqmPath ) {
|
if ( sqmExpression instanceof SqmPath ) {
|
||||||
final SqmPath<?> sqmPath = (SqmPath<?>) sqmExpression;
|
final SqmPath<?> sqmPath = (SqmPath<?>) sqmExpression;
|
||||||
|
@ -945,40 +947,7 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
|
||||||
|
|
||||||
return sqmExpression;
|
return sqmExpression;
|
||||||
}
|
}
|
||||||
|
return (SqmSelectableNode<?>) subCtx.accept( this );
|
||||||
throw new ParsingException( "Unexpected selection rule type : " + ctx.getText() );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String visitResultIdentifier(HqlParser.ResultIdentifierContext resultIdentifierContext) {
|
|
||||||
if ( resultIdentifierContext != null ) {
|
|
||||||
if ( resultIdentifierContext.getChildCount() == 1 ) {
|
|
||||||
return resultIdentifierContext.getText();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
final HqlParser.IdentifierContext identifierContext = (HqlParser.IdentifierContext) resultIdentifierContext.getChild( 1 );
|
|
||||||
final Token aliasToken = identifierContext.getStart();
|
|
||||||
final String explicitAlias = aliasToken.getText();
|
|
||||||
|
|
||||||
if ( aliasToken.getType() != IDENTIFIER ) {
|
|
||||||
// we have a reserved word used as an identification variable.
|
|
||||||
if ( creationOptions.useStrictJpaCompliance() ) {
|
|
||||||
throw new StrictJpaComplianceViolation(
|
|
||||||
String.format(
|
|
||||||
Locale.ROOT,
|
|
||||||
"Strict JPQL compliance was violated : %s [%s]",
|
|
||||||
StrictJpaComplianceViolation.Type.RESERVED_WORD_USED_AS_ALIAS.description(),
|
|
||||||
explicitAlias
|
|
||||||
),
|
|
||||||
StrictJpaComplianceViolation.Type.RESERVED_WORD_USED_AS_ALIAS
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return explicitAlias;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -1041,15 +1010,13 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
|
||||||
public SqmDynamicInstantiationArgument<?> visitDynamicInstantiationArg(HqlParser.DynamicInstantiationArgContext ctx) {
|
public SqmDynamicInstantiationArgument<?> visitDynamicInstantiationArg(HqlParser.DynamicInstantiationArgContext ctx) {
|
||||||
final String alias;
|
final String alias;
|
||||||
if ( ctx.getChildCount() > 1 ) {
|
if ( ctx.getChildCount() > 1 ) {
|
||||||
alias = ctx.getChild( ctx.getChildCount() - 1 ).getText();
|
alias = visitIdentificationVariableDef( (HqlParser.IdentificationVariableDefContext) ctx.getChild( ctx.getChildCount() - 1 ) );
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
alias = null;
|
alias = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
final SqmSelectableNode<?> argExpression = visitDynamicInstantiationArgExpression(
|
final SqmSelectableNode<?> argExpression = (SqmSelectableNode<?>) ctx.getChild( 0 ).accept( this );
|
||||||
(HqlParser.DynamicInstantiationArgExpressionContext) ctx.getChild( 0 )
|
|
||||||
);
|
|
||||||
|
|
||||||
final SqmDynamicInstantiationArgument<?> argument = new SqmDynamicInstantiationArgument<>(
|
final SqmDynamicInstantiationArgument<?> argument = new SqmDynamicInstantiationArgument<>(
|
||||||
argExpression,
|
argExpression,
|
||||||
|
@ -1068,19 +1035,6 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
|
||||||
return argument;
|
return argument;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public SqmSelectableNode<?> visitDynamicInstantiationArgExpression(HqlParser.DynamicInstantiationArgExpressionContext ctx) {
|
|
||||||
final ParseTree parseTree = ctx.getChild( 0 );
|
|
||||||
if ( parseTree instanceof HqlParser.DynamicInstantiationContext ) {
|
|
||||||
return visitDynamicInstantiation( (HqlParser.DynamicInstantiationContext) parseTree );
|
|
||||||
}
|
|
||||||
else if ( parseTree instanceof HqlParser.ExpressionContext ) {
|
|
||||||
return (SqmExpression<?>) parseTree.accept( this );
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new ParsingException( "Unexpected dynamic-instantiation-argument rule type : " + ctx.getText() );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SqmPath<?> visitJpaSelectObjectSyntax(HqlParser.JpaSelectObjectSyntaxContext ctx) {
|
public SqmPath<?> visitJpaSelectObjectSyntax(HqlParser.JpaSelectObjectSyntaxContext ctx) {
|
||||||
final String alias = ctx.getChild( 2 ).getText();
|
final String alias = ctx.getChild( 2 ).getText();
|
||||||
|
@ -1126,7 +1080,7 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
|
||||||
return new SqmAliasedNodeRef( position, integerDomainType, creationContext.getNodeBuilder() );
|
return new SqmAliasedNodeRef( position, integerDomainType, creationContext.getNodeBuilder() );
|
||||||
}
|
}
|
||||||
else if ( child instanceof HqlParser.IdentifierContext ) {
|
else if ( child instanceof HqlParser.IdentifierContext ) {
|
||||||
final String identifierText = child.getText();
|
final String identifierText = visitIdentifier( (HqlParser.IdentifierContext) child );
|
||||||
|
|
||||||
final Integer correspondingPosition = getCurrentProcessingState()
|
final Integer correspondingPosition = getCurrentProcessingState()
|
||||||
.getPathRegistry()
|
.getPathRegistry()
|
||||||
|
@ -1312,13 +1266,36 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SqmExpression<?> visitPathExpression(HqlParser.PathExpressionContext ctx) {
|
public Object visitSyntacticPathExpression(HqlParser.SyntacticPathExpressionContext ctx) {
|
||||||
final HqlParser.PathContext path = (HqlParser.PathContext) ctx.getChild( 0 );
|
SemanticPathPart part = visitSyntacticDomainPath( (HqlParser.SyntacticDomainPathContext) ctx.getChild( 0 ) );
|
||||||
final Object accept = path.accept( this );
|
if ( ctx.getChildCount() == 2 ) {
|
||||||
if ( accept instanceof DomainPathPart ) {
|
dotIdentifierConsumerStack.push(
|
||||||
return ( (DomainPathPart) accept ).getSqmExpression();
|
new BasicDotIdentifierConsumer( part, this ) {
|
||||||
|
@Override
|
||||||
|
protected void reset() {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
try {
|
||||||
|
part = (SemanticPathPart) ctx.getChild( 1 ).accept( this );
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
dotIdentifierConsumerStack.pop();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return (SqmExpression<?>) accept;
|
if ( part instanceof DomainPathPart ) {
|
||||||
|
return ( (DomainPathPart) part ).getSqmExpression();
|
||||||
|
}
|
||||||
|
return (SqmExpression<?>) part;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object visitGeneralPathExpression(HqlParser.GeneralPathExpressionContext ctx) {
|
||||||
|
final SemanticPathPart part = visitGeneralPathFragment( (HqlParser.GeneralPathFragmentContext) ctx.getChild( 0 ) );
|
||||||
|
if ( part instanceof DomainPathPart ) {
|
||||||
|
return ( (DomainPathPart) part ).getSqmExpression();
|
||||||
|
}
|
||||||
|
return (SqmExpression<?>) part;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -1365,31 +1342,46 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
|
||||||
return (SqmExpression<?>) firstChild.accept( this );
|
return (SqmExpression<?>) firstChild.accept( this );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getEntityName(HqlParser.EntityNameContext parserEntityName) {
|
||||||
|
final StringBuilder sb = new StringBuilder();
|
||||||
|
final int end = parserEntityName.getChildCount();
|
||||||
|
sb.append( visitIdentifier( (HqlParser.IdentifierContext) parserEntityName.getChild( 0 ) ) );
|
||||||
|
for ( int i = 2; i < end; i += 2 ) {
|
||||||
|
sb.append( '.' );
|
||||||
|
sb.append( visitIdentifier( (HqlParser.IdentifierContext) parserEntityName.getChild( i ) ) );
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String visitIdentifier(HqlParser.IdentifierContext ctx) {
|
||||||
|
final TerminalNode node = (TerminalNode) ctx.getChild( 0 );
|
||||||
|
if ( node.getSymbol().getType() == HqlParser.QUOTED_IDENTIFIER ) {
|
||||||
|
return QuotingHelper.unquoteIdentifier( node.getText() );
|
||||||
|
}
|
||||||
|
return node.getText();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public EntityDomainType<?> visitEntityName(HqlParser.EntityNameContext parserEntityName) {
|
public EntityDomainType<?> visitEntityName(HqlParser.EntityNameContext parserEntityName) {
|
||||||
final String entityName = parserEntityName.fullNameText;
|
final String entityName = getEntityName( parserEntityName );
|
||||||
final EntityDomainType<?> entityReference = resolveEntityReference( entityName );
|
final EntityDomainType<?> entityReference = getCreationContext()
|
||||||
|
.getJpaMetamodel()
|
||||||
|
.getHqlEntityReference( entityName );
|
||||||
if ( entityReference == null ) {
|
if ( entityReference == null ) {
|
||||||
throw new UnknownEntityException( "Could not resolve entity name [" + entityName + "] as DML target", entityName );
|
throw new UnknownEntityException( "Could not resolve entity name [" + entityName + "] as DML target", entityName );
|
||||||
}
|
}
|
||||||
checkFQNEntityNameJpaComplianceViolationIfNeeded( entityName, entityReference );
|
checkFQNEntityNameJpaComplianceViolationIfNeeded( entityName, entityReference );
|
||||||
|
if ( entityReference instanceof SqmPolymorphicRootDescriptor<?> && getCreationOptions().useStrictJpaCompliance() ) {
|
||||||
|
throw new StrictJpaComplianceViolation(
|
||||||
|
"Encountered the use of a non entity name [" + entityName + "], " +
|
||||||
|
"but strict JPQL compliance was requested which doesn't allow this",
|
||||||
|
StrictJpaComplianceViolation.Type.NON_ENTITY_NAME
|
||||||
|
);
|
||||||
|
}
|
||||||
return entityReference;
|
return entityReference;
|
||||||
}
|
}
|
||||||
|
|
||||||
private EntityDomainType<?> resolveEntityReference(String entityName) {
|
|
||||||
log.debugf( "Attempting to resolve path [%s] as entity reference...", entityName );
|
|
||||||
EntityDomainType<?> reference = null;
|
|
||||||
try {
|
|
||||||
entityName = creationContext.getJpaMetamodel().qualifyImportableName( entityName );
|
|
||||||
reference = creationContext.getJpaMetamodel().entity( entityName );
|
|
||||||
}
|
|
||||||
catch (Exception ignore) {
|
|
||||||
}
|
|
||||||
|
|
||||||
return reference;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SqmFromClause visitFromClause(HqlParser.FromClauseContext parserFromClause) {
|
public SqmFromClause visitFromClause(HqlParser.FromClauseContext parserFromClause) {
|
||||||
final SqmFromClause fromClause;
|
final SqmFromClause fromClause;
|
||||||
|
@ -1436,7 +1428,7 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
|
||||||
public SqmRoot<?> visitPathRoot(HqlParser.PathRootContext ctx) {
|
public SqmRoot<?> visitPathRoot(HqlParser.PathRootContext ctx) {
|
||||||
final HqlParser.EntityNameContext entityNameContext = (HqlParser.EntityNameContext) ctx.getChild( 0 );
|
final HqlParser.EntityNameContext entityNameContext = (HqlParser.EntityNameContext) ctx.getChild( 0 );
|
||||||
final List<ParseTree> entityNameParseTreeChildren = entityNameContext.children;
|
final List<ParseTree> entityNameParseTreeChildren = entityNameContext.children;
|
||||||
final String name = entityNameContext.fullNameText;
|
final String name = getEntityName( entityNameContext );
|
||||||
|
|
||||||
log.debugf( "Handling root path - %s", name );
|
log.debugf( "Handling root path - %s", name );
|
||||||
final EntityDomainType entityDescriptor = getCreationContext()
|
final EntityDomainType entityDescriptor = getCreationContext()
|
||||||
|
@ -1542,10 +1534,14 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return identifierContext.getText();
|
return visitIdentifier( identifierContext );
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return lastChild.getText();
|
final TerminalNode node = (TerminalNode) lastChild;
|
||||||
|
if ( node.getSymbol().getType() == HqlParser.QUOTED_IDENTIFIER ) {
|
||||||
|
return QuotingHelper.unquoteIdentifier( node.getText() );
|
||||||
|
}
|
||||||
|
return node.getText();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1569,7 +1565,7 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
|
||||||
private <T> void consumeCrossJoin(HqlParser.CrossJoinContext parserJoin, SqmRoot<T> sqmRoot) {
|
private <T> void consumeCrossJoin(HqlParser.CrossJoinContext parserJoin, SqmRoot<T> sqmRoot) {
|
||||||
final HqlParser.PathRootContext pathRootContext = (HqlParser.PathRootContext) parserJoin.getChild( 2 );
|
final HqlParser.PathRootContext pathRootContext = (HqlParser.PathRootContext) parserJoin.getChild( 2 );
|
||||||
final HqlParser.EntityNameContext entityNameContext = (HqlParser.EntityNameContext) pathRootContext.getChild( 0 );
|
final HqlParser.EntityNameContext entityNameContext = (HqlParser.EntityNameContext) pathRootContext.getChild( 0 );
|
||||||
final String name = entityNameContext.fullNameText;
|
final String name = getEntityName( entityNameContext );
|
||||||
|
|
||||||
SqmTreeCreationLogger.LOGGER.debugf( "Handling root path - %s", name );
|
SqmTreeCreationLogger.LOGGER.debugf( "Handling root path - %s", name );
|
||||||
|
|
||||||
|
@ -1640,12 +1636,17 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
|
||||||
identificationVariableDefContext = null;
|
identificationVariableDefContext = null;
|
||||||
}
|
}
|
||||||
final String alias = visitIdentificationVariableDef( identificationVariableDefContext );
|
final String alias = visitIdentificationVariableDef( identificationVariableDefContext );
|
||||||
|
final boolean fetch = parserJoin.getChild( 2 ) instanceof TerminalNode;
|
||||||
|
|
||||||
|
if ( fetch && processingStateStack.depth() > 1 ) {
|
||||||
|
throw new SemanticException( "fetch not allowed in subquery from-elements" );
|
||||||
|
}
|
||||||
|
|
||||||
dotIdentifierConsumerStack.push(
|
dotIdentifierConsumerStack.push(
|
||||||
new QualifiedJoinPathConsumer(
|
new QualifiedJoinPathConsumer(
|
||||||
sqmRoot,
|
sqmRoot,
|
||||||
joinType,
|
joinType,
|
||||||
parserJoin.getChild( 2 ) instanceof TerminalNode,
|
fetch,
|
||||||
alias,
|
alias,
|
||||||
this
|
this
|
||||||
)
|
)
|
||||||
|
@ -1927,15 +1928,11 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
|
||||||
ctx = ctx.getChild( 0 );
|
ctx = ctx.getChild( 0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( ctx instanceof HqlParser.PathContext && ctx.getChildCount() == 1 ) {
|
if ( ctx instanceof HqlParser.GeneralPathFragmentContext && ctx.getChildCount() == 1 ) {
|
||||||
ctx = ctx.getChild( 0 );
|
ctx = ctx.getChild( 0 );
|
||||||
|
|
||||||
if ( ctx instanceof HqlParser.GeneralPathFragmentContext && ctx.getChildCount() == 1 ) {
|
if ( ctx instanceof HqlParser.DotIdentifierSequenceContext ) {
|
||||||
ctx = ctx.getChild( 0 );
|
return creationContext.getJpaMetamodel().getAllowedEnumLiteralTexts().get( ctx.getText() );
|
||||||
|
|
||||||
if ( ctx instanceof HqlParser.DotIdentifierSequenceContext ) {
|
|
||||||
return creationContext.getJpaMetamodel().getAllowedEnumLiteralTexts().get( ctx.getText() );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2008,10 +2005,12 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
|
||||||
final List<SqmExpression<?>> listExpressions = new ArrayList<>( estimatedSize );
|
final List<SqmExpression<?>> listExpressions = new ArrayList<>( estimatedSize );
|
||||||
for ( int i = 1; i < size; i++ ) {
|
for ( int i = 1; i < size; i++ ) {
|
||||||
final ParseTree parseTree = tupleExpressionListContext.getChild( i );
|
final ParseTree parseTree = tupleExpressionListContext.getChild( i );
|
||||||
if ( parseTree instanceof HqlParser.ExpressionContext ) {
|
if ( parseTree instanceof HqlParser.ExpressionOrPredicateContext ) {
|
||||||
final HqlParser.ExpressionContext expressionContext = (HqlParser.ExpressionContext) parseTree;
|
final ParseTree child = parseTree.getChild( 0 );
|
||||||
|
final HqlParser.ExpressionContext expressionContext;
|
||||||
final Map<Class<?>, Enum<?>> possibleEnumValues;
|
final Map<Class<?>, Enum<?>> possibleEnumValues;
|
||||||
if ( isEnum && ( possibleEnumValues = getPossibleEnumValues( expressionContext ) ) != null ) {
|
if ( isEnum && child instanceof HqlParser.ExpressionContext
|
||||||
|
&& ( possibleEnumValues = getPossibleEnumValues( expressionContext = (HqlParser.ExpressionContext) child ) ) != null ) {
|
||||||
listExpressions.add(
|
listExpressions.add(
|
||||||
resolveEnumShorthandLiteral(
|
resolveEnumShorthandLiteral(
|
||||||
expressionContext,
|
expressionContext,
|
||||||
|
@ -2021,7 +2020,7 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
listExpressions.add( (SqmExpression<?>) expressionContext.accept( this ) );
|
listExpressions.add( (SqmExpression<?>) child.accept( this ) );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2367,7 +2366,7 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
|
||||||
final List<SqmExpression<?>> expressions = new ArrayList<>( estimateExpressionsCount );
|
final List<SqmExpression<?>> expressions = new ArrayList<>( estimateExpressionsCount );
|
||||||
for ( int i = 0; i < size; i++ ) {
|
for ( int i = 0; i < size; i++ ) {
|
||||||
final ParseTree parseTree = parentContext.getChild( i );
|
final ParseTree parseTree = parentContext.getChild( i );
|
||||||
if ( parseTree instanceof HqlParser.ExpressionContext ) {
|
if ( parseTree instanceof HqlParser.ExpressionOrPredicateContext ) {
|
||||||
expressions.add( (SqmExpression<?>) parseTree.accept( this ) );
|
expressions.add( (SqmExpression<?>) parseTree.accept( this ) );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2524,6 +2523,36 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
|
||||||
return (SqmExpression<?>) ctx.getChild( 0 ).accept( this );
|
return (SqmExpression<?>) ctx.getChild( 0 ).accept( this );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SqmExpression<?> visitUnaryNumericLiteralExpression(HqlParser.UnaryNumericLiteralExpressionContext ctx) {
|
||||||
|
final TerminalNode node = (TerminalNode) ctx.getChild( 1 ).getChild( 0 );
|
||||||
|
final String text;
|
||||||
|
if ( ( (TerminalNode) ctx.getChild( 0 ).getChild( 0 ) ).getSymbol().getType() == HqlParser.MINUS ) {
|
||||||
|
text = "-" + node.getText();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
text = node.getText();
|
||||||
|
}
|
||||||
|
switch ( node.getSymbol().getType() ) {
|
||||||
|
case HqlParser.INTEGER_LITERAL:
|
||||||
|
return integerOrLongLiteral( text );
|
||||||
|
case HqlParser.LONG_LITERAL:
|
||||||
|
return longLiteral( text );
|
||||||
|
case HqlParser.BIG_INTEGER_LITERAL:
|
||||||
|
return bigIntegerLiteral( text );
|
||||||
|
case HqlParser.HEX_LITERAL:
|
||||||
|
return hexLiteral( text );
|
||||||
|
case HqlParser.FLOAT_LITERAL:
|
||||||
|
return floatLiteral( text );
|
||||||
|
case HqlParser.DOUBLE_LITERAL:
|
||||||
|
return doubleLiteral( text );
|
||||||
|
case HqlParser.BIG_DECIMAL_LITERAL:
|
||||||
|
return bigDecimalLiteral( text );
|
||||||
|
default:
|
||||||
|
throw new ParsingException("Unexpected terminal node [" + text + "]");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object visitBinaryLiteral(HqlParser.BinaryLiteralContext ctx) {
|
public Object visitBinaryLiteral(HqlParser.BinaryLiteralContext ctx) {
|
||||||
final TerminalNode firstNode = (TerminalNode) ctx.getChild( 0 );
|
final TerminalNode firstNode = (TerminalNode) ctx.getChild( 0 );
|
||||||
|
@ -2561,7 +2590,7 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
|
||||||
case HqlParser.STRING_LITERAL:
|
case HqlParser.STRING_LITERAL:
|
||||||
return stringLiteral( node.getText() );
|
return stringLiteral( node.getText() );
|
||||||
case HqlParser.INTEGER_LITERAL:
|
case HqlParser.INTEGER_LITERAL:
|
||||||
return integerLiteral( node.getText() );
|
return integerOrLongLiteral( node.getText() );
|
||||||
case HqlParser.LONG_LITERAL:
|
case HqlParser.LONG_LITERAL:
|
||||||
return longLiteral( node.getText() );
|
return longLiteral( node.getText() );
|
||||||
case HqlParser.BIG_INTEGER_LITERAL:
|
case HqlParser.BIG_INTEGER_LITERAL:
|
||||||
|
@ -2680,7 +2709,7 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
|
||||||
final TerminalNode firstChild = (TerminalNode) ctx.getChild( 0 );
|
final TerminalNode firstChild = (TerminalNode) ctx.getChild( 0 );
|
||||||
final String timezoneText;
|
final String timezoneText;
|
||||||
if ( firstChild.getSymbol().getType() == HqlParser.STRING_LITERAL ) {
|
if ( firstChild.getSymbol().getType() == HqlParser.STRING_LITERAL ) {
|
||||||
timezoneText = unescapeStringLiteral( ctx.getText() );
|
timezoneText = QuotingHelper.unquoteStringLiteral( ctx.getText() );
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
timezoneText = ctx.getText();
|
timezoneText = ctx.getText();
|
||||||
|
@ -2864,77 +2893,9 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String unescapeStringLiteral(String text) {
|
|
||||||
// Unescape the parsed literal and handle escape sequences
|
|
||||||
final StringBuilder sb = new StringBuilder( text.length() - 2 );
|
|
||||||
final int end = text.length() - 1;
|
|
||||||
final char delimiter = text.charAt( 0 );
|
|
||||||
for ( int i = 1; i < end; i++ ) {
|
|
||||||
char c = text.charAt( i );
|
|
||||||
switch ( c ) {
|
|
||||||
case '\'':
|
|
||||||
if ( delimiter == '\'' ) {
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case '"':
|
|
||||||
if ( delimiter == '"' ) {
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case '\\':
|
|
||||||
if ( ( i + 1 ) < end ) {
|
|
||||||
char nextChar = text.charAt( ++i );
|
|
||||||
switch ( nextChar ) {
|
|
||||||
case 'b':
|
|
||||||
c = '\b';
|
|
||||||
break;
|
|
||||||
case 't':
|
|
||||||
c = '\t';
|
|
||||||
break;
|
|
||||||
case 'n':
|
|
||||||
c = '\n';
|
|
||||||
break;
|
|
||||||
case 'f':
|
|
||||||
c = '\f';
|
|
||||||
break;
|
|
||||||
case 'r':
|
|
||||||
c = '\r';
|
|
||||||
break;
|
|
||||||
case '\\':
|
|
||||||
c = '\\';
|
|
||||||
break;
|
|
||||||
case '\'':
|
|
||||||
c = '\'';
|
|
||||||
break;
|
|
||||||
case '"':
|
|
||||||
c = '"';
|
|
||||||
break;
|
|
||||||
case '`':
|
|
||||||
c = '`';
|
|
||||||
break;
|
|
||||||
case 'u':
|
|
||||||
c = (char) Integer.parseInt( text.substring( i + 1, i + 5 ), 16 );
|
|
||||||
i += 4;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
sb.append( '\\' );
|
|
||||||
c = nextChar;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
sb.append( c );
|
|
||||||
}
|
|
||||||
return sb.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
private SqmLiteral<String> stringLiteral(String text) {
|
private SqmLiteral<String> stringLiteral(String text) {
|
||||||
return new SqmLiteral<>(
|
return new SqmLiteral<>(
|
||||||
unescapeStringLiteral( text ),
|
QuotingHelper.unquoteStringLiteral( text ),
|
||||||
resolveExpressableTypeBasic( String.class ),
|
resolveExpressableTypeBasic( String.class ),
|
||||||
creationContext.getNodeBuilder()
|
creationContext.getNodeBuilder()
|
||||||
);
|
);
|
||||||
|
@ -2950,6 +2911,35 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private SqmLiteral<? extends Number> integerOrLongLiteral(String text) {
|
||||||
|
try {
|
||||||
|
final Integer value = Integer.valueOf( text );
|
||||||
|
return new SqmLiteral<>(
|
||||||
|
value,
|
||||||
|
resolveExpressableTypeBasic( Integer.class ),
|
||||||
|
creationContext.getNodeBuilder()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
catch (NumberFormatException e) {
|
||||||
|
// This is at least what 5.x did
|
||||||
|
try {
|
||||||
|
final Long value = Long.valueOf( text );
|
||||||
|
return new SqmLiteral<>(
|
||||||
|
value,
|
||||||
|
resolveExpressableTypeBasic( Long.class ),
|
||||||
|
creationContext.getNodeBuilder()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
catch (NumberFormatException e2) {
|
||||||
|
e.addSuppressed( e2 );
|
||||||
|
throw new LiteralNumberFormatException(
|
||||||
|
"Unable to convert sqm literal [" + text + "] to Integer",
|
||||||
|
e
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private SqmLiteral<Integer> integerLiteral(String text) {
|
private SqmLiteral<Integer> integerLiteral(String text) {
|
||||||
try {
|
try {
|
||||||
final Integer value = Integer.valueOf( text );
|
final Integer value = Integer.valueOf( text );
|
||||||
|
@ -3132,7 +3122,7 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SqmExpression<?> visitJpaNonStandardFunction(HqlParser.JpaNonStandardFunctionContext ctx) {
|
public SqmExpression<?> visitJpaNonStandardFunction(HqlParser.JpaNonStandardFunctionContext ctx) {
|
||||||
final String functionName = unescapeStringLiteral( ctx.getChild( 2 ).getText() ).toLowerCase();
|
final String functionName = QuotingHelper.unquoteStringLiteral( ctx.getChild( 2 ).getText() ).toLowerCase();
|
||||||
final List<SqmTypedNode<?>> functionArguments;
|
final List<SqmTypedNode<?>> functionArguments;
|
||||||
if ( ctx.getChildCount() > 4 ) {
|
if ( ctx.getChildCount() > 4 ) {
|
||||||
//noinspection unchecked
|
//noinspection unchecked
|
||||||
|
@ -3249,7 +3239,7 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
|
||||||
for ( ; i < size; i += 2 ) {
|
for ( ; i < size; i += 2 ) {
|
||||||
// we handle the final argument differently...
|
// we handle the final argument differently...
|
||||||
if ( i == lastIndex ) {
|
if ( i == lastIndex ) {
|
||||||
arguments.add( visitFinalFunctionArgument( (HqlParser.ExpressionContext) ctx.getChild( i ) ) );
|
arguments.add( visitFinalFunctionArgument( ctx.getChild( i ) ) );
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
arguments.add( (SqmTypedNode<?>) ctx.getChild( i ).accept( this ) );
|
arguments.add( (SqmTypedNode<?>) ctx.getChild( i ).accept( this ) );
|
||||||
|
@ -3275,7 +3265,7 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
|
||||||
return arguments;
|
return arguments;
|
||||||
}
|
}
|
||||||
|
|
||||||
private SqmExpression<?> visitFinalFunctionArgument(HqlParser.ExpressionContext expression) {
|
private SqmExpression<?> visitFinalFunctionArgument(ParseTree expression) {
|
||||||
// the final argument to a function may accept multi-value parameter (varargs),
|
// the final argument to a function may accept multi-value parameter (varargs),
|
||||||
// but only if we are operating in non-strict JPA mode
|
// but only if we are operating in non-strict JPA mode
|
||||||
parameterDeclarationContextStack.push( () -> !creationOptions.useStrictJpaCompliance() );
|
parameterDeclarationContextStack.push( () -> !creationOptions.useStrictJpaCompliance() );
|
||||||
|
@ -3467,7 +3457,7 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object visitFormat(HqlParser.FormatContext ctx) {
|
public Object visitFormat(HqlParser.FormatContext ctx) {
|
||||||
String format = unescapeStringLiteral( ctx.getChild( 0 ).getText() );
|
String format = QuotingHelper.unquoteStringLiteral( ctx.getChild( 0 ).getText() );
|
||||||
if (!FORMAT.matcher(format).matches()) {
|
if (!FORMAT.matcher(format).matches()) {
|
||||||
throw new SemanticException("illegal format pattern: '" + format + "'");
|
throw new SemanticException("illegal format pattern: '" + format + "'");
|
||||||
}
|
}
|
||||||
|
@ -3843,7 +3833,7 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
|
||||||
@Override
|
@Override
|
||||||
public SqmLiteral<Character> visitTrimCharacter(HqlParser.TrimCharacterContext ctx) {
|
public SqmLiteral<Character> visitTrimCharacter(HqlParser.TrimCharacterContext ctx) {
|
||||||
final String trimCharText = ctx != null
|
final String trimCharText = ctx != null
|
||||||
? unescapeStringLiteral( ctx.getText() )
|
? QuotingHelper.unquoteStringLiteral( ctx.getText() )
|
||||||
: " "; // JPA says space is the default
|
: " "; // JPA says space is the default
|
||||||
|
|
||||||
if ( trimCharText.length() != 1 ) {
|
if ( trimCharText.length() != 1 ) {
|
||||||
|
@ -4059,7 +4049,19 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
|
||||||
final SqmPath<?> indexedPath = pathPart.resolveIndexedAccess( indexExpression, !hasIndexContinuation, this );
|
final SqmPath<?> indexedPath = pathPart.resolveIndexedAccess( indexExpression, !hasIndexContinuation, this );
|
||||||
|
|
||||||
if ( hasIndexContinuation ) {
|
if ( hasIndexContinuation ) {
|
||||||
return (SemanticPathPart) idxCtx.getChild( 4 ).accept( this );
|
dotIdentifierConsumerStack.push(
|
||||||
|
new BasicDotIdentifierConsumer( indexedPath, this ) {
|
||||||
|
@Override
|
||||||
|
protected void reset() {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
try {
|
||||||
|
return (SemanticPathPart) idxCtx.getChild( 4 ).accept( this );
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
dotIdentifierConsumerStack.pop();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return indexedPath;
|
return indexedPath;
|
||||||
}
|
}
|
||||||
|
@ -4079,7 +4081,7 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
|
||||||
assert identifierContext.getChildCount() == 1;
|
assert identifierContext.getChildCount() == 1;
|
||||||
|
|
||||||
dotIdentifierConsumer.consumeIdentifier(
|
dotIdentifierConsumer.consumeIdentifier(
|
||||||
identifierContext.getChild( 0 ).getText(),
|
visitIdentifier( identifierContext ),
|
||||||
true,
|
true,
|
||||||
! hasContinuations
|
! hasContinuations
|
||||||
);
|
);
|
||||||
|
@ -4090,7 +4092,7 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
|
||||||
final HqlParser.IdentifierContext identifier = (HqlParser.IdentifierContext) continuation.getChild( 1 );
|
final HqlParser.IdentifierContext identifier = (HqlParser.IdentifierContext) continuation.getChild( 1 );
|
||||||
assert identifier.getChildCount() == 1;
|
assert identifier.getChildCount() == 1;
|
||||||
dotIdentifierConsumer.consumeIdentifier(
|
dotIdentifierConsumer.consumeIdentifier(
|
||||||
identifier.getChild( 0 ).getText(),
|
visitIdentifier( identifier ),
|
||||||
false,
|
false,
|
||||||
i >= numberOfContinuations
|
i >= numberOfContinuations
|
||||||
);
|
);
|
||||||
|
|
|
@ -8,13 +8,11 @@ package org.hibernate.query.hql.internal;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.LinkedHashMap;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
import org.hibernate.internal.util.MutableInteger;
|
|
||||||
import org.hibernate.jpa.spi.JpaCompliance;
|
import org.hibernate.jpa.spi.JpaCompliance;
|
||||||
import org.hibernate.query.NavigablePath;
|
import org.hibernate.query.NavigablePath;
|
||||||
import org.hibernate.query.hql.HqlLogging;
|
import org.hibernate.query.hql.HqlLogging;
|
||||||
|
@ -117,27 +115,6 @@ public class SqmPathRegistryImpl implements SqmPathRegistry {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public <X> SqmPath<X> findPath(NavigablePath path) {
|
|
||||||
final SqmPath<?> found = sqmPathByPath.get( path );
|
|
||||||
if ( found != null ) {
|
|
||||||
//noinspection unchecked
|
|
||||||
return (SqmPath<X>) found;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( associatedProcessingState.getParentProcessingState() != null ) {
|
|
||||||
final SqmFrom<?, X> containingQueryFrom = associatedProcessingState.getParentProcessingState()
|
|
||||||
.getPathRegistry()
|
|
||||||
.findFromByPath( path );
|
|
||||||
if ( containingQueryFrom != null ) {
|
|
||||||
// todo (6.0) create a correlation?
|
|
||||||
return containingQueryFrom;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <X extends SqmFrom<?, ?>> X findFromByPath(NavigablePath navigablePath) {
|
public <X extends SqmFrom<?, ?>> X findFromByPath(NavigablePath navigablePath) {
|
||||||
final SqmFrom<?, ?> found = sqmFromByPath.get( navigablePath );
|
final SqmFrom<?, ?> found = sqmFromByPath.get( navigablePath );
|
||||||
|
@ -247,21 +224,6 @@ public class SqmPathRegistryImpl implements SqmPathRegistry {
|
||||||
return (X) sqmFrom;
|
return (X) sqmFrom;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public <X> SqmPath<X> resolvePath(NavigablePath navigablePath, Function<NavigablePath, SqmPath<X>> creator) {
|
|
||||||
SqmTreeCreationLogger.LOGGER.tracef( "SqmProcessingIndex#resolvePath(NavigablePath) : %s", navigablePath );
|
|
||||||
|
|
||||||
final SqmPath<?> existing = sqmPathByPath.get( navigablePath );
|
|
||||||
if ( existing != null ) {
|
|
||||||
//noinspection unchecked
|
|
||||||
return (SqmPath<X>) existing;
|
|
||||||
}
|
|
||||||
|
|
||||||
final SqmPath<X> sqmPath = creator.apply( navigablePath );
|
|
||||||
register( sqmPath );
|
|
||||||
return sqmPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean definesAttribute(SqmPathSource<?> containerType, String name) {
|
private boolean definesAttribute(SqmPathSource<?> containerType, String name) {
|
||||||
return containerType.findSubPathSource( name ) != null;
|
return containerType.findSubPathSource( name ) != null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -71,22 +71,6 @@ public interface SqmPathRegistry {
|
||||||
*/
|
*/
|
||||||
<X extends SqmFrom<?, ?>> X resolveFrom(SqmPath<?> path);
|
<X extends SqmFrom<?, ?>> X resolveFrom(SqmPath<?> path);
|
||||||
|
|
||||||
/**
|
|
||||||
* Find an SqmPath by its NavigablePath. Will return a SqmFrom if the NavigablePath
|
|
||||||
* has (yet) been resolved to a SqmFrom. Otherwise, it will be a non-SqmFrom SqmPath
|
|
||||||
*
|
|
||||||
* @return matching SqmPath or {@code null}
|
|
||||||
*/
|
|
||||||
<X> SqmPath<X> findPath(NavigablePath path);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Similar to {@link #findPath}, but accepting a producer to be used
|
|
||||||
* to create and register a SqmPath if none yet registered.
|
|
||||||
*
|
|
||||||
* @return The existing or just-created SqmPath
|
|
||||||
*/
|
|
||||||
<X> SqmPath<X> resolvePath(NavigablePath path, Function<NavigablePath, SqmPath<X>> creator);
|
|
||||||
|
|
||||||
|
|
||||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
// SqmSelection
|
// SqmSelection
|
||||||
|
|
|
@ -27,8 +27,7 @@ public class QueryHelper {
|
||||||
return types[0];
|
return types[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
//noinspection unchecked
|
SqmExpressable<? extends T> highest = highestPrecedenceType2( types[0], types[1] );
|
||||||
SqmExpressable<? extends T> highest = highestPrecedenceType2( (SqmExpressable) types[0], types[1] );
|
|
||||||
for ( int i = 2; i < types.length; i++ ) {
|
for ( int i = 2; i < types.length; i++ ) {
|
||||||
highest = highestPrecedenceType2( highest, types[i] );
|
highest = highestPrecedenceType2( highest, types[i] );
|
||||||
}
|
}
|
||||||
|
@ -58,6 +57,9 @@ public class QueryHelper {
|
||||||
}
|
}
|
||||||
|
|
||||||
// any other precedence rules?
|
// any other precedence rules?
|
||||||
|
if ( type2.getExpressableJavaTypeDescriptor().isWider( type1.getExpressableJavaTypeDescriptor() ) ) {
|
||||||
|
return type2;
|
||||||
|
}
|
||||||
|
|
||||||
return type1;
|
return type1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ package org.hibernate.query.internal;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.IdentityHashMap;
|
import java.util.IdentityHashMap;
|
||||||
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
@ -195,6 +196,8 @@ public class QueryParameterBindingsImpl implements QueryParameterBindings {
|
||||||
for ( QueryParameterBinding<?> binding : parameterBindingMap.values() ) {
|
for ( QueryParameterBinding<?> binding : parameterBindingMap.values() ) {
|
||||||
final MappingModelExpressable<?> mappingType = determineMappingType( binding, persistenceContext );
|
final MappingModelExpressable<?> mappingType = determineMappingType( binding, persistenceContext );
|
||||||
assert mappingType instanceof JavaTypedExpressable;
|
assert mappingType instanceof JavaTypedExpressable;
|
||||||
|
//noinspection unchecked
|
||||||
|
final JavaType<Object> javaType = ( (JavaTypedExpressable<Object>) mappingType ).getExpressableJavaTypeDescriptor();
|
||||||
|
|
||||||
if ( binding.isMultiValued() ) {
|
if ( binding.isMultiValued() ) {
|
||||||
for ( Object bindValue : binding.getBindValues() ) {
|
for ( Object bindValue : binding.getBindValues() ) {
|
||||||
|
@ -203,8 +206,9 @@ public class QueryParameterBindingsImpl implements QueryParameterBindings {
|
||||||
final Object disassembled = mappingType.disassemble( bindValue, persistenceContext );
|
final Object disassembled = mappingType.disassemble( bindValue, persistenceContext );
|
||||||
allBindValues.add( disassembled );
|
allBindValues.add( disassembled );
|
||||||
|
|
||||||
//noinspection unchecked
|
final int valueHashCode = bindValue != null
|
||||||
final int valueHashCode = ( (JavaTypedExpressable<Object>) mappingType ).getExpressableJavaTypeDescriptor().extractHashCode( bindValue );
|
? javaType.extractHashCode( bindValue )
|
||||||
|
: 0;
|
||||||
|
|
||||||
hashCode = 31 * hashCode + valueHashCode;
|
hashCode = 31 * hashCode + valueHashCode;
|
||||||
}
|
}
|
||||||
|
@ -215,8 +219,9 @@ public class QueryParameterBindingsImpl implements QueryParameterBindings {
|
||||||
final Object disassembled = mappingType.disassemble( bindValue, persistenceContext );
|
final Object disassembled = mappingType.disassemble( bindValue, persistenceContext );
|
||||||
allBindValues.add( disassembled );
|
allBindValues.add( disassembled );
|
||||||
|
|
||||||
//noinspection unchecked
|
final int valueHashCode = bindValue != null
|
||||||
final int valueHashCode = ( (JavaTypedExpressable<Object>) mappingType ).getExpressableJavaTypeDescriptor().extractHashCode( bindValue );
|
? javaType.extractHashCode( bindValue )
|
||||||
|
: 0;
|
||||||
|
|
||||||
hashCode = 31 * hashCode + valueHashCode;
|
hashCode = 31 * hashCode + valueHashCode;
|
||||||
}
|
}
|
||||||
|
@ -249,12 +254,19 @@ public class QueryParameterBindingsImpl implements QueryParameterBindings {
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( binding.isMultiValued() ) {
|
if ( binding.isMultiValued() ) {
|
||||||
final Object firstBindValue = binding.getBindValues().iterator().next();
|
final Iterator<?> iterator = binding.getBindValues().iterator();
|
||||||
return typeConfiguration.getBasicTypeForJavaType( firstBindValue.getClass() );
|
Object firstNonNullBindValue = null;
|
||||||
|
if ( iterator.hasNext() && firstNonNullBindValue == null ) {
|
||||||
|
firstNonNullBindValue = iterator.next();
|
||||||
|
}
|
||||||
|
if ( firstNonNullBindValue != null ) {
|
||||||
|
return typeConfiguration.getBasicTypeForJavaType( firstNonNullBindValue.getClass() );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
else if ( binding.getBindValue() != null ) {
|
||||||
return typeConfiguration.getBasicTypeForJavaType( binding.getBindValue().getClass() );
|
return typeConfiguration.getBasicTypeForJavaType( binding.getBindValue().getClass() );
|
||||||
}
|
}
|
||||||
|
return typeConfiguration.getBasicTypeForJavaType( binding.getBindType().getJavaType() );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -287,7 +287,7 @@ public class ResultSetMappingImpl implements ResultSetMapping {
|
||||||
JdbcValuesMetadata jdbcResultsMetadata,
|
JdbcValuesMetadata jdbcResultsMetadata,
|
||||||
SessionFactoryImplementor sessionFactory) {
|
SessionFactoryImplementor sessionFactory) {
|
||||||
final int jdbcPosition = valuesArrayPosition + 1;
|
final int jdbcPosition = valuesArrayPosition + 1;
|
||||||
final BasicType<?> jdbcMapping = jdbcResultsMetadata.resolveType( jdbcPosition, null );
|
final BasicType<?> jdbcMapping = jdbcResultsMetadata.resolveType( jdbcPosition, null, sessionFactory );
|
||||||
|
|
||||||
final String name = jdbcResultsMetadata.resolveColumnName( jdbcPosition );
|
final String name = jdbcResultsMetadata.resolveColumnName( jdbcPosition );
|
||||||
|
|
||||||
|
|
|
@ -119,7 +119,11 @@ public class CompleteResultBuilderBasicValuedStandard implements CompleteResultB
|
||||||
basicType = explicitType;
|
basicType = explicitType;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
basicType = jdbcResultsMetadata.resolveType( jdbcPosition, explicitJavaTypeDescriptor );
|
basicType = jdbcResultsMetadata.resolveType(
|
||||||
|
jdbcPosition,
|
||||||
|
explicitJavaTypeDescriptor,
|
||||||
|
sessionFactory
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
final int valuesArrayPosition = ResultsHelper.jdbcPositionToValuesArrayPosition( jdbcPosition );
|
final int valuesArrayPosition = ResultsHelper.jdbcPositionToValuesArrayPosition( jdbcPosition );
|
||||||
|
|
|
@ -6,6 +6,8 @@
|
||||||
*/
|
*/
|
||||||
package org.hibernate.query.results.dynamic;
|
package org.hibernate.query.results.dynamic;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import org.hibernate.query.NativeQuery;
|
import org.hibernate.query.NativeQuery;
|
||||||
import org.hibernate.query.results.FetchBuilder;
|
import org.hibernate.query.results.FetchBuilder;
|
||||||
import org.hibernate.sql.results.graph.Fetchable;
|
import org.hibernate.sql.results.graph.Fetchable;
|
||||||
|
@ -14,4 +16,5 @@ import org.hibernate.sql.results.graph.Fetchable;
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
||||||
*/
|
*/
|
||||||
public interface DynamicFetchBuilder extends FetchBuilder, NativeQuery.ReturnProperty {
|
public interface DynamicFetchBuilder extends FetchBuilder, NativeQuery.ReturnProperty {
|
||||||
|
List<String> getColumnAliases();
|
||||||
}
|
}
|
||||||
|
|
|
@ -209,6 +209,11 @@ public class DynamicFetchBuilderLegacy implements DynamicFetchBuilder, NativeQue
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> getColumnAliases() {
|
||||||
|
return columnNames;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public NativeQuery.FetchReturn setLockMode(LockMode lockMode) {
|
public NativeQuery.FetchReturn setLockMode(LockMode lockMode) {
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -129,4 +129,9 @@ public class DynamicFetchBuilderStandard
|
||||||
columnNames.add( columnAlias );
|
columnNames.add( columnAlias );
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> getColumnAliases() {
|
||||||
|
return columnNames;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -122,7 +122,13 @@ public class DynamicResultBuilderBasicConverted<O,R> implements DynamicResultBui
|
||||||
else {
|
else {
|
||||||
jdbcPosition = currentJdbcPosition;
|
jdbcPosition = currentJdbcPosition;
|
||||||
}
|
}
|
||||||
final BasicType<?> basicType = jdbcResultsMetadata.resolveType( jdbcPosition, basicValueConverter.getRelationalJavaDescriptor() );
|
final BasicType<?> basicType = jdbcResultsMetadata.resolveType(
|
||||||
|
jdbcPosition,
|
||||||
|
basicValueConverter.getRelationalJavaDescriptor(),
|
||||||
|
domainResultCreationState.getSqlAstCreationState()
|
||||||
|
.getCreationContext()
|
||||||
|
.getSessionFactory()
|
||||||
|
);
|
||||||
|
|
||||||
final int valuesArrayPosition = ResultsHelper.jdbcPositionToValuesArrayPosition( jdbcPosition );
|
final int valuesArrayPosition = ResultsHelper.jdbcPositionToValuesArrayPosition( jdbcPosition );
|
||||||
return new SqlSelectionImpl( valuesArrayPosition, (BasicValuedMapping) basicType );
|
return new SqlSelectionImpl( valuesArrayPosition, (BasicValuedMapping) basicType );
|
||||||
|
|
|
@ -136,7 +136,11 @@ public class DynamicResultBuilderBasicStandard implements DynamicResultBuilderBa
|
||||||
basicType = explicitType;
|
basicType = explicitType;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
basicType = jdbcResultsMetadata.resolveType( jdbcPosition, explicitJavaTypeDescriptor );
|
basicType = jdbcResultsMetadata.resolveType(
|
||||||
|
jdbcPosition,
|
||||||
|
explicitJavaTypeDescriptor,
|
||||||
|
sessionFactory
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return new SqlSelectionImpl( valuesArrayPosition, (BasicValuedMapping) basicType );
|
return new SqlSelectionImpl( valuesArrayPosition, (BasicValuedMapping) basicType );
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,7 @@ import org.hibernate.metamodel.mapping.CollectionPart;
|
||||||
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.JdbcMapping;
|
import org.hibernate.metamodel.mapping.JdbcMapping;
|
||||||
|
import org.hibernate.metamodel.mapping.internal.SingleAttributeIdentifierMapping;
|
||||||
import org.hibernate.query.NativeQuery;
|
import org.hibernate.query.NativeQuery;
|
||||||
import org.hibernate.query.NavigablePath;
|
import org.hibernate.query.NavigablePath;
|
||||||
import org.hibernate.query.results.DomainResultCreationStateImpl;
|
import org.hibernate.query.results.DomainResultCreationStateImpl;
|
||||||
|
@ -169,13 +170,23 @@ public class DynamicResultBuilderEntityStandard
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
final TableReference tableReference = tableGroup.getPrimaryTableReference();
|
final TableReference tableReference = tableGroup.getPrimaryTableReference();
|
||||||
|
final List<String> idColumnAliases;
|
||||||
if ( idColumnNames != null ) {
|
final DynamicFetchBuilder idFetchBuilder;
|
||||||
|
if ( this.idColumnNames != null ) {
|
||||||
|
idColumnAliases = this.idColumnNames;
|
||||||
|
}
|
||||||
|
else if ( ( idFetchBuilder = findIdFetchBuilder() ) != null ) {
|
||||||
|
idColumnAliases = idFetchBuilder.getColumnAliases();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
idColumnAliases = null;
|
||||||
|
}
|
||||||
|
if ( idColumnAliases != null ) {
|
||||||
final EntityIdentifierMapping identifierMapping = entityMapping.getIdentifierMapping();
|
final EntityIdentifierMapping identifierMapping = entityMapping.getIdentifierMapping();
|
||||||
identifierMapping.forEachSelectable(
|
identifierMapping.forEachSelectable(
|
||||||
(selectionIndex, selectableMapping) -> {
|
(selectionIndex, selectableMapping) -> {
|
||||||
resolveSqlSelection(
|
resolveSqlSelection(
|
||||||
idColumnNames.get( selectionIndex ),
|
idColumnAliases.get( selectionIndex ),
|
||||||
createColumnReferenceKey( tableReference, selectableMapping.getSelectionExpression() ),
|
createColumnReferenceKey( tableReference, selectableMapping.getSelectionExpression() ),
|
||||||
selectableMapping.getJdbcMapping(),
|
selectableMapping.getJdbcMapping(),
|
||||||
jdbcResultsMetadata,
|
jdbcResultsMetadata,
|
||||||
|
@ -229,6 +240,14 @@ public class DynamicResultBuilderEntityStandard
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private DynamicFetchBuilder findIdFetchBuilder() {
|
||||||
|
final EntityIdentifierMapping identifierMapping = entityMapping.getIdentifierMapping();
|
||||||
|
if ( identifierMapping instanceof SingleAttributeIdentifierMapping ) {
|
||||||
|
return findFetchBuilder( ( (SingleAttributeIdentifierMapping) identifierMapping ).getAttributeName() );
|
||||||
|
}
|
||||||
|
return findFetchBuilder( identifierMapping.getPartName() );
|
||||||
|
}
|
||||||
|
|
||||||
private void resolveSqlSelection(
|
private void resolveSqlSelection(
|
||||||
String columnAlias,
|
String columnAlias,
|
||||||
String columnKey,
|
String columnKey,
|
||||||
|
|
|
@ -28,7 +28,6 @@ import org.hibernate.sql.results.graph.embeddable.EmbeddableValuedFetchable;
|
||||||
import org.hibernate.sql.results.jdbc.spi.JdbcValuesMetadata;
|
import org.hibernate.sql.results.jdbc.spi.JdbcValuesMetadata;
|
||||||
|
|
||||||
import static org.hibernate.query.results.ResultsHelper.impl;
|
import static org.hibernate.query.results.ResultsHelper.impl;
|
||||||
import static org.hibernate.sql.ast.spi.SqlExpressionResolver.createColumnReferenceKey;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
||||||
|
|
|
@ -1545,7 +1545,7 @@ public abstract class AbstractQuery<R> implements QueryImplementor<R> {
|
||||||
throw getSession().getExceptionConverter().convert( e );
|
throw getSession().getExceptionConverter().convert( e );
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
afterQuery( true );
|
afterQuery( success );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -30,6 +30,7 @@ public class StrictJpaComplianceViolation extends SemanticException {
|
||||||
LIMIT_OFFSET_CLAUSE( "use of LIMIT/OFFSET clause" ),
|
LIMIT_OFFSET_CLAUSE( "use of LIMIT/OFFSET clause" ),
|
||||||
IDENTIFICATION_VARIABLE_NOT_DECLARED_IN_FROM_CLAUSE( "use of an alias not declared in the FROM clause" ),
|
IDENTIFICATION_VARIABLE_NOT_DECLARED_IN_FROM_CLAUSE( "use of an alias not declared in the FROM clause" ),
|
||||||
FQN_ENTITY_NAME( "use of FQN for entity name" ),
|
FQN_ENTITY_NAME( "use of FQN for entity name" ),
|
||||||
|
NON_ENTITY_NAME( "use of class or interface FQN for entity name" ),
|
||||||
IMPLICIT_TREAT( "use of implicit treat" ),
|
IMPLICIT_TREAT( "use of implicit treat" ),
|
||||||
MIXED_POSITIONAL_NAMED_PARAMETERS( "mix of positional and named parameters" ),
|
MIXED_POSITIONAL_NAMED_PARAMETERS( "mix of positional and named parameters" ),
|
||||||
;
|
;
|
||||||
|
|
|
@ -78,7 +78,7 @@ public class SelfRenderingSqmFunction<T> extends SqmFunction<T> {
|
||||||
getRenderingSupport(),
|
getRenderingSupport(),
|
||||||
resolveSqlAstArguments( getArguments(), walker ),
|
resolveSqlAstArguments( getArguments(), walker ),
|
||||||
resultType,
|
resultType,
|
||||||
getMappingModelExpressable( walker, resultType )
|
resultType == null ? null : getMappingModelExpressable( walker, resultType )
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
/*
|
||||||
|
* Hibernate, Relational Persistence for Idiomatic Java
|
||||||
|
*
|
||||||
|
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
|
||||||
|
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
|
||||||
|
*/
|
||||||
|
package org.hibernate.query.sqm.internal;
|
||||||
|
|
||||||
|
import org.hibernate.query.spi.DomainQueryExecutionContext;
|
||||||
|
import org.hibernate.query.spi.NonSelectQueryPlan;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Christian Beikov
|
||||||
|
*/
|
||||||
|
public class AggregatedNonSelectQueryPlanImpl implements NonSelectQueryPlan {
|
||||||
|
private final NonSelectQueryPlan[] aggregatedQueryPlans;
|
||||||
|
|
||||||
|
public AggregatedNonSelectQueryPlanImpl(NonSelectQueryPlan[] aggregatedQueryPlans) {
|
||||||
|
this.aggregatedQueryPlans = aggregatedQueryPlans;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int executeUpdate(DomainQueryExecutionContext executionContext) {
|
||||||
|
int updated = 0;
|
||||||
|
for ( NonSelectQueryPlan aggregatedQueryPlan : aggregatedQueryPlans ) {
|
||||||
|
updated += aggregatedQueryPlan.executeUpdate( executionContext );
|
||||||
|
}
|
||||||
|
return updated;
|
||||||
|
}
|
||||||
|
}
|
|
@ -12,6 +12,7 @@ import java.util.List;
|
||||||
|
|
||||||
import org.hibernate.ScrollMode;
|
import org.hibernate.ScrollMode;
|
||||||
import org.hibernate.internal.EmptyScrollableResults;
|
import org.hibernate.internal.EmptyScrollableResults;
|
||||||
|
import org.hibernate.query.Limit;
|
||||||
import org.hibernate.query.spi.DomainQueryExecutionContext;
|
import org.hibernate.query.spi.DomainQueryExecutionContext;
|
||||||
import org.hibernate.query.spi.ScrollableResultsImplementor;
|
import org.hibernate.query.spi.ScrollableResultsImplementor;
|
||||||
import org.hibernate.query.spi.SelectQueryPlan;
|
import org.hibernate.query.spi.SelectQueryPlan;
|
||||||
|
@ -30,13 +31,41 @@ public class AggregatedSelectQueryPlanImpl<R> implements SelectQueryPlan<R> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<R> performList(DomainQueryExecutionContext executionContext) {
|
public List<R> performList(DomainQueryExecutionContext executionContext) {
|
||||||
if ( executionContext.getQueryOptions().getEffectiveLimit().getMaxRowsJpa() == 0 ) {
|
final Limit effectiveLimit = executionContext.getQueryOptions().getEffectiveLimit();
|
||||||
|
final int maxRowsJpa = effectiveLimit.getMaxRowsJpa();
|
||||||
|
if ( maxRowsJpa == 0 ) {
|
||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
|
int elementsToSkip = effectiveLimit.getFirstRowJpa();
|
||||||
final List<R> overallResults = new ArrayList<>();
|
final List<R> overallResults = new ArrayList<>();
|
||||||
|
|
||||||
for ( SelectQueryPlan<R> aggregatedQueryPlan : aggregatedQueryPlans ) {
|
for ( SelectQueryPlan<R> aggregatedQueryPlan : aggregatedQueryPlans ) {
|
||||||
overallResults.addAll( aggregatedQueryPlan.performList( executionContext ) );
|
final List<R> list = aggregatedQueryPlan.performList( executionContext );
|
||||||
|
final int size = list.size();
|
||||||
|
if ( size <= elementsToSkip ) {
|
||||||
|
// More elements to skip than the collection size
|
||||||
|
elementsToSkip -= size;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
final int availableElements = size - elementsToSkip;
|
||||||
|
if ( overallResults.size() + availableElements >= maxRowsJpa ) {
|
||||||
|
// This result list is the last one i.e. fulfills the limit
|
||||||
|
final int end = elementsToSkip + ( maxRowsJpa - overallResults.size() );
|
||||||
|
for ( int i = elementsToSkip; i < end; i++ ) {
|
||||||
|
overallResults.add( list.get( i ) );
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if ( elementsToSkip > 0 ) {
|
||||||
|
// We can skip a part of this result list
|
||||||
|
for ( int i = availableElements; i < size; i++ ) {
|
||||||
|
overallResults.add( list.get( i ) );
|
||||||
|
}
|
||||||
|
elementsToSkip = 0;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
overallResults.addAll( list );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return overallResults;
|
return overallResults;
|
||||||
|
|
|
@ -34,6 +34,7 @@ import org.hibernate.query.sqm.sql.SqmTranslation;
|
||||||
import org.hibernate.query.sqm.sql.SqmTranslator;
|
import org.hibernate.query.sqm.sql.SqmTranslator;
|
||||||
import org.hibernate.query.sqm.sql.SqmTranslatorFactory;
|
import org.hibernate.query.sqm.sql.SqmTranslatorFactory;
|
||||||
import org.hibernate.query.sqm.tree.expression.SqmParameter;
|
import org.hibernate.query.sqm.tree.expression.SqmParameter;
|
||||||
|
import org.hibernate.query.sqm.tree.select.SqmDynamicInstantiation;
|
||||||
import org.hibernate.query.sqm.tree.select.SqmSelectStatement;
|
import org.hibernate.query.sqm.tree.select.SqmSelectStatement;
|
||||||
import org.hibernate.query.sqm.tree.select.SqmSelection;
|
import org.hibernate.query.sqm.tree.select.SqmSelection;
|
||||||
import org.hibernate.sql.ast.SqlAstTranslator;
|
import org.hibernate.sql.ast.SqlAstTranslator;
|
||||||
|
@ -239,12 +240,17 @@ public class ConcreteSqmSelectQueryPlan<R> implements SelectQueryPlan<R> {
|
||||||
SqmSelectStatement sqm,
|
SqmSelectStatement sqm,
|
||||||
QueryOptions queryOptions) {
|
QueryOptions queryOptions) {
|
||||||
final List<String> aliases = new ArrayList<>();
|
final List<String> aliases = new ArrayList<>();
|
||||||
sqm.getQuerySpec().getSelectClause().getSelections().forEach(
|
for ( SqmSelection<?> sqmSelection : sqm.getQuerySpec().getSelectClause().getSelections() ) {
|
||||||
sqmSelection ->
|
// The row a tuple transformer gets to see only contains 1 element for a dynamic instantiation
|
||||||
sqmSelection.getSelectableNode().visitSubSelectableNodes(
|
if ( sqmSelection.getSelectableNode() instanceof SqmDynamicInstantiation<?> ) {
|
||||||
subSelection -> aliases.add( subSelection.getAlias() )
|
aliases.add( sqmSelection.getAlias() );
|
||||||
)
|
}
|
||||||
);
|
else {
|
||||||
|
sqmSelection.getSelectableNode().visitSubSelectableNodes(
|
||||||
|
subSelection -> aliases.add( subSelection.getAlias() )
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return new RowTransformerTupleTransformerAdapter<>(
|
return new RowTransformerTupleTransformerAdapter<>(
|
||||||
ArrayHelper.toStringArray( aliases ), queryOptions.getTupleTransformer()
|
ArrayHelper.toStringArray( aliases ), queryOptions.getTupleTransformer()
|
||||||
|
|
|
@ -31,6 +31,7 @@ import org.hibernate.internal.CoreMessageLogger;
|
||||||
import org.hibernate.internal.util.collections.IdentitySet;
|
import org.hibernate.internal.util.collections.IdentitySet;
|
||||||
import org.hibernate.metamodel.model.domain.BasicDomainType;
|
import org.hibernate.metamodel.model.domain.BasicDomainType;
|
||||||
import org.hibernate.metamodel.model.domain.DomainType;
|
import org.hibernate.metamodel.model.domain.DomainType;
|
||||||
|
import org.hibernate.metamodel.model.domain.EntityDomainType;
|
||||||
import org.hibernate.persister.entity.EntityPersister;
|
import org.hibernate.persister.entity.EntityPersister;
|
||||||
import org.hibernate.query.ImmutableEntityUpdateQueryHandlingMode;
|
import org.hibernate.query.ImmutableEntityUpdateQueryHandlingMode;
|
||||||
import org.hibernate.query.Query;
|
import org.hibernate.query.Query;
|
||||||
|
@ -226,7 +227,7 @@ public class QuerySqmImpl<R>
|
||||||
SqmUtil.verifyIsSelectStatement( sqmStatement, null );
|
SqmUtil.verifyIsSelectStatement( sqmStatement, null );
|
||||||
final SqmQueryPart<R> queryPart = ( (SqmSelectStatement<R>) sqmStatement ).getQueryPart();
|
final SqmQueryPart<R> queryPart = ( (SqmSelectStatement<R>) sqmStatement ).getQueryPart();
|
||||||
// For criteria queries, we have to validate the fetch structure here
|
// For criteria queries, we have to validate the fetch structure here
|
||||||
queryPart.validateQueryGroupFetchStructure();
|
queryPart.validateFetchStructureAndOwners();
|
||||||
visitQueryReturnType(
|
visitQueryReturnType(
|
||||||
queryPart,
|
queryPart,
|
||||||
resultType,
|
resultType,
|
||||||
|
@ -627,7 +628,12 @@ public class QuerySqmImpl<R>
|
||||||
executionContextToUse = this;
|
executionContextToUse = this;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
executionContextToUse = new DelegatingDomainQueryExecutionContext( this );
|
executionContextToUse = new DelegatingDomainQueryExecutionContext( this ) {
|
||||||
|
@Override
|
||||||
|
public QueryOptions getQueryOptions() {
|
||||||
|
return normalizedQueryOptions;
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -792,11 +798,23 @@ public class QuerySqmImpl<R>
|
||||||
}
|
}
|
||||||
|
|
||||||
private NonSelectQueryPlan buildDeleteQueryPlan() {
|
private NonSelectQueryPlan buildDeleteQueryPlan() {
|
||||||
final SqmDeleteStatement<R> sqmDelete = (SqmDeleteStatement<R>) getSqmStatement();
|
final SqmDeleteStatement<R>[] concreteSqmStatements = QuerySplitter.split(
|
||||||
|
(SqmDeleteStatement<R>) getSqmStatement(),
|
||||||
|
getSessionFactory()
|
||||||
|
);
|
||||||
|
|
||||||
final String entityNameToDelete = sqmDelete.getTarget().getReferencedPathSource().getHibernateEntityName();
|
if ( concreteSqmStatements.length > 1 ) {
|
||||||
|
return buildAggregatedDeleteQueryPlan( concreteSqmStatements );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return buildConcreteDeleteQueryPlan( concreteSqmStatements[0] );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private NonSelectQueryPlan buildConcreteDeleteQueryPlan(SqmDeleteStatement<R> sqmDelete) {
|
||||||
|
final EntityDomainType<?> entityDomainType = sqmDelete.getTarget().getReferencedPathSource();
|
||||||
|
final String entityNameToDelete = entityDomainType.getHibernateEntityName();
|
||||||
final EntityPersister entityDescriptor = getSessionFactory().getDomainModel().findEntityDescriptor( entityNameToDelete );
|
final EntityPersister entityDescriptor = getSessionFactory().getDomainModel().findEntityDescriptor( entityNameToDelete );
|
||||||
|
|
||||||
final SqmMultiTableMutationStrategy multiTableStrategy = entityDescriptor.getSqmMultiTableMutationStrategy();
|
final SqmMultiTableMutationStrategy multiTableStrategy = entityDescriptor.getSqmMultiTableMutationStrategy();
|
||||||
if ( multiTableStrategy == null ) {
|
if ( multiTableStrategy == null ) {
|
||||||
return new SimpleDeleteQueryPlan( entityDescriptor, sqmDelete, domainParameterXref );
|
return new SimpleDeleteQueryPlan( entityDescriptor, sqmDelete, domainParameterXref );
|
||||||
|
@ -806,6 +824,16 @@ public class QuerySqmImpl<R>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private NonSelectQueryPlan buildAggregatedDeleteQueryPlan(SqmDeleteStatement<R>[] concreteSqmStatements) {
|
||||||
|
final NonSelectQueryPlan[] aggregatedQueryPlans = new NonSelectQueryPlan[ concreteSqmStatements.length ];
|
||||||
|
|
||||||
|
for ( int i = 0, x = concreteSqmStatements.length; i < x; i++ ) {
|
||||||
|
aggregatedQueryPlans[i] = buildConcreteDeleteQueryPlan( concreteSqmStatements[i] );
|
||||||
|
}
|
||||||
|
|
||||||
|
return new AggregatedNonSelectQueryPlanImpl( aggregatedQueryPlans );
|
||||||
|
}
|
||||||
|
|
||||||
private NonSelectQueryPlan buildUpdateQueryPlan() {
|
private NonSelectQueryPlan buildUpdateQueryPlan() {
|
||||||
final SqmUpdateStatement<R> sqmUpdate = (SqmUpdateStatement<R>) getSqmStatement();
|
final SqmUpdateStatement<R> sqmUpdate = (SqmUpdateStatement<R>) getSqmStatement();
|
||||||
|
|
||||||
|
|
|
@ -201,6 +201,7 @@ public class MatchingIdSelectionHelper {
|
||||||
|
|
||||||
final MultiTableSqmMutationConverter sqmConverter = new MultiTableSqmMutationConverter(
|
final MultiTableSqmMutationConverter sqmConverter = new MultiTableSqmMutationConverter(
|
||||||
entityDescriptor,
|
entityDescriptor,
|
||||||
|
sqmMutationStatement,
|
||||||
sqmMutationStatement.getTarget(),
|
sqmMutationStatement.getTarget(),
|
||||||
domainParameterXref,
|
domainParameterXref,
|
||||||
executionContext.getQueryOptions(),
|
executionContext.getQueryOptions(),
|
||||||
|
|
|
@ -21,6 +21,7 @@ import org.hibernate.query.sqm.sql.BaseSqmToSqlAstConverter;
|
||||||
import org.hibernate.query.sqm.sql.internal.DomainResultProducer;
|
import org.hibernate.query.sqm.sql.internal.DomainResultProducer;
|
||||||
import org.hibernate.query.sqm.sql.internal.SqlAstProcessingStateImpl;
|
import org.hibernate.query.sqm.sql.internal.SqlAstProcessingStateImpl;
|
||||||
import org.hibernate.query.sqm.sql.internal.SqlAstQueryPartProcessingStateImpl;
|
import org.hibernate.query.sqm.sql.internal.SqlAstQueryPartProcessingStateImpl;
|
||||||
|
import org.hibernate.query.sqm.tree.SqmStatement;
|
||||||
import org.hibernate.query.sqm.tree.expression.SqmParameter;
|
import org.hibernate.query.sqm.tree.expression.SqmParameter;
|
||||||
import org.hibernate.query.sqm.tree.from.SqmRoot;
|
import org.hibernate.query.sqm.tree.from.SqmRoot;
|
||||||
import org.hibernate.query.sqm.tree.predicate.SqmWhereClause;
|
import org.hibernate.query.sqm.tree.predicate.SqmWhereClause;
|
||||||
|
@ -63,6 +64,7 @@ public class MultiTableSqmMutationConverter extends BaseSqmToSqlAstConverter<Sta
|
||||||
|
|
||||||
public MultiTableSqmMutationConverter(
|
public MultiTableSqmMutationConverter(
|
||||||
EntityMappingType mutatingEntityDescriptor,
|
EntityMappingType mutatingEntityDescriptor,
|
||||||
|
SqmStatement<?> statement,
|
||||||
SqmRoot<?> sqmRoot,
|
SqmRoot<?> sqmRoot,
|
||||||
DomainParameterXref domainParameterXref,
|
DomainParameterXref domainParameterXref,
|
||||||
QueryOptions queryOptions,
|
QueryOptions queryOptions,
|
||||||
|
@ -71,6 +73,7 @@ public class MultiTableSqmMutationConverter extends BaseSqmToSqlAstConverter<Sta
|
||||||
SqlAstCreationContext creationContext) {
|
SqlAstCreationContext creationContext) {
|
||||||
this(
|
this(
|
||||||
mutatingEntityDescriptor,
|
mutatingEntityDescriptor,
|
||||||
|
statement,
|
||||||
sqmRoot,
|
sqmRoot,
|
||||||
sqmRoot.getExplicitAlias(),
|
sqmRoot.getExplicitAlias(),
|
||||||
domainParameterXref,
|
domainParameterXref,
|
||||||
|
@ -83,6 +86,7 @@ public class MultiTableSqmMutationConverter extends BaseSqmToSqlAstConverter<Sta
|
||||||
|
|
||||||
public MultiTableSqmMutationConverter(
|
public MultiTableSqmMutationConverter(
|
||||||
EntityMappingType mutatingEntityDescriptor,
|
EntityMappingType mutatingEntityDescriptor,
|
||||||
|
SqmStatement<?> statement,
|
||||||
SqmRoot<?> sqmRoot,
|
SqmRoot<?> sqmRoot,
|
||||||
String sourceAlias,
|
String sourceAlias,
|
||||||
DomainParameterXref domainParameterXref,
|
DomainParameterXref domainParameterXref,
|
||||||
|
@ -90,7 +94,7 @@ public class MultiTableSqmMutationConverter extends BaseSqmToSqlAstConverter<Sta
|
||||||
LoadQueryInfluencers loadQueryInfluencers,
|
LoadQueryInfluencers loadQueryInfluencers,
|
||||||
QueryParameterBindings domainParameterBindings,
|
QueryParameterBindings domainParameterBindings,
|
||||||
SqlAstCreationContext creationContext) {
|
SqlAstCreationContext creationContext) {
|
||||||
super( creationContext, null, queryOptions, loadQueryInfluencers, domainParameterXref, domainParameterBindings );
|
super( creationContext, statement, queryOptions, loadQueryInfluencers, domainParameterXref, domainParameterBindings );
|
||||||
this.mutatingEntityDescriptor = mutatingEntityDescriptor;
|
this.mutatingEntityDescriptor = mutatingEntityDescriptor;
|
||||||
|
|
||||||
final SqlAstProcessingStateImpl rootProcessingState = new SqlAstProcessingStateImpl(
|
final SqlAstProcessingStateImpl rootProcessingState = new SqlAstProcessingStateImpl(
|
||||||
|
@ -114,6 +118,11 @@ public class MultiTableSqmMutationConverter extends BaseSqmToSqlAstConverter<Sta
|
||||||
getFromClauseAccess().registerTableGroup( sqmRoot.getNavigablePath(), mutatingTableGroup );
|
getFromClauseAccess().registerTableGroup( sqmRoot.getNavigablePath(), mutatingTableGroup );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void pruneTableGroupJoins() {
|
||||||
|
super.pruneTableGroupJoins();
|
||||||
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
public EntityMappingType getMutatingEntityDescriptor() {
|
public EntityMappingType getMutatingEntityDescriptor() {
|
||||||
return mutatingEntityDescriptor;
|
return mutatingEntityDescriptor;
|
||||||
|
@ -139,7 +148,7 @@ public class MultiTableSqmMutationConverter extends BaseSqmToSqlAstConverter<Sta
|
||||||
this.parameterResolutionConsumer = parameterResolutionConsumer;
|
this.parameterResolutionConsumer = parameterResolutionConsumer;
|
||||||
|
|
||||||
for ( SqmAssignment assignment : setClause.getAssignments() ) {
|
for ( SqmAssignment assignment : setClause.getAssignments() ) {
|
||||||
visitAssignment( assignment, assignmentConsumer );
|
assignmentConsumer.accept( visitAssignment( assignment ) );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -147,16 +156,6 @@ public class MultiTableSqmMutationConverter extends BaseSqmToSqlAstConverter<Sta
|
||||||
throw new UnsupportedOperationException();
|
throw new UnsupportedOperationException();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void visitAssignment(
|
|
||||||
SqmAssignment sqmAssignment,
|
|
||||||
Consumer<Assignment> assignmentConsumer) {
|
|
||||||
final Assignable assignable = (Assignable) sqmAssignment.getTargetPath().accept( this );
|
|
||||||
|
|
||||||
final Expression value = (Expression) sqmAssignment.getValue().accept( this );
|
|
||||||
|
|
||||||
assignmentConsumer.accept( new Assignment( assignable, value ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Assignment visitAssignment(SqmAssignment sqmAssignment) {
|
public Assignment visitAssignment(SqmAssignment sqmAssignment) {
|
||||||
return new Assignment(
|
return new Assignment(
|
||||||
|
|
|
@ -104,7 +104,7 @@ public class SqmMutationStrategyHelper {
|
||||||
else {
|
else {
|
||||||
// element-collection or many-to-many - delete the collection-table row
|
// element-collection or many-to-many - delete the collection-table row
|
||||||
|
|
||||||
final TableReference tableReference = new TableReference( separateCollectionTable, null, true, sessionFactory );
|
final TableReference tableReference = new TableReference( separateCollectionTable, DeleteStatement.DEFAULT_ALIAS, true, sessionFactory );
|
||||||
|
|
||||||
final DeleteStatement sqlAstDelete = new DeleteStatement(
|
final DeleteStatement sqlAstDelete = new DeleteStatement(
|
||||||
tableReference,
|
tableReference,
|
||||||
|
|
|
@ -117,6 +117,7 @@ public abstract class AbstractCteMutationHandler extends AbstractMutationHandler
|
||||||
|
|
||||||
final MultiTableSqmMutationConverter sqmConverter = new MultiTableSqmMutationConverter(
|
final MultiTableSqmMutationConverter sqmConverter = new MultiTableSqmMutationConverter(
|
||||||
entityDescriptor,
|
entityDescriptor,
|
||||||
|
sqmMutationStatement,
|
||||||
sqmMutationStatement.getTarget(),
|
sqmMutationStatement.getTarget(),
|
||||||
explicitDmlTargetAlias,
|
explicitDmlTargetAlias,
|
||||||
domainParameterXref,
|
domainParameterXref,
|
||||||
|
@ -140,6 +141,7 @@ public abstract class AbstractCteMutationHandler extends AbstractMutationHandler
|
||||||
columnReference -> {},
|
columnReference -> {},
|
||||||
(sqmParam, mappingType, jdbcParameters) -> paramTypeResolutions.put( sqmParam, mappingType )
|
(sqmParam, mappingType, jdbcParameters) -> paramTypeResolutions.put( sqmParam, mappingType )
|
||||||
);
|
);
|
||||||
|
sqmConverter.pruneTableGroupJoins();
|
||||||
|
|
||||||
final CteStatement idSelectCte = new CteStatement(
|
final CteStatement idSelectCte = new CteStatement(
|
||||||
BaseSqmToSqlAstConverter.createCteTable( getCteTable(), factory ),
|
BaseSqmToSqlAstConverter.createCteTable( getCteTable(), factory ),
|
||||||
|
@ -165,7 +167,7 @@ public abstract class AbstractCteMutationHandler extends AbstractMutationHandler
|
||||||
.getSqlAstTranslatorFactory()
|
.getSqlAstTranslatorFactory()
|
||||||
.buildSelectTranslator( factory, statement );
|
.buildSelectTranslator( factory, statement );
|
||||||
|
|
||||||
final Expression count = createCountStart( factory, sqmConverter );
|
final Expression count = createCountStar( factory, sqmConverter );
|
||||||
domainResults.add(
|
domainResults.add(
|
||||||
new BasicResult<>(
|
new BasicResult<>(
|
||||||
0,
|
0,
|
||||||
|
@ -215,7 +217,7 @@ public abstract class AbstractCteMutationHandler extends AbstractMutationHandler
|
||||||
return ( (Number) list.get( 0 ) ).intValue();
|
return ( (Number) list.get( 0 ) ).intValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
private Expression createCountStart(
|
private Expression createCountStar(
|
||||||
SessionFactoryImplementor factory,
|
SessionFactoryImplementor factory,
|
||||||
MultiTableSqmMutationConverter sqmConverter) {
|
MultiTableSqmMutationConverter sqmConverter) {
|
||||||
final SqmExpression<?> arg = new SqmStar( factory.getNodeBuilder() );
|
final SqmExpression<?> arg = new SqmStar( factory.getNodeBuilder() );
|
||||||
|
|
|
@ -160,7 +160,7 @@ public final class ExecuteWithIdTableHelper {
|
||||||
|
|
||||||
public static QuerySpec createIdTableSelectQuerySpec(
|
public static QuerySpec createIdTableSelectQuerySpec(
|
||||||
IdTable idTable,
|
IdTable idTable,
|
||||||
Function<SharedSessionContractImplementor,String> sessionUidAccess,
|
Function<SharedSessionContractImplementor, String> sessionUidAccess,
|
||||||
EntityMappingType entityDescriptor,
|
EntityMappingType entityDescriptor,
|
||||||
ExecutionContext executionContext) {
|
ExecutionContext executionContext) {
|
||||||
return createIdTableSelectQuerySpec( idTable, null, sessionUidAccess, entityDescriptor, executionContext );
|
return createIdTableSelectQuerySpec( idTable, null, sessionUidAccess, entityDescriptor, executionContext );
|
||||||
|
@ -169,14 +169,14 @@ public final class ExecuteWithIdTableHelper {
|
||||||
public static QuerySpec createIdTableSelectQuerySpec(
|
public static QuerySpec createIdTableSelectQuerySpec(
|
||||||
IdTable idTable,
|
IdTable idTable,
|
||||||
ModelPart fkModelPart,
|
ModelPart fkModelPart,
|
||||||
Function<SharedSessionContractImplementor,String> sessionUidAccess,
|
Function<SharedSessionContractImplementor, String> sessionUidAccess,
|
||||||
EntityMappingType entityDescriptor,
|
EntityMappingType entityDescriptor,
|
||||||
ExecutionContext executionContext) {
|
ExecutionContext executionContext) {
|
||||||
final QuerySpec querySpec = new QuerySpec( false );
|
final QuerySpec querySpec = new QuerySpec( false );
|
||||||
|
|
||||||
final TableReference idTableReference = new TableReference(
|
final TableReference idTableReference = new TableReference(
|
||||||
idTable.getTableExpression(),
|
idTable.getTableExpression(),
|
||||||
null,
|
IdTable.DEFAULT_ALIAS,
|
||||||
true,
|
true,
|
||||||
executionContext.getSession().getFactory()
|
executionContext.getSession().getFactory()
|
||||||
);
|
);
|
||||||
|
|
|
@ -7,23 +7,33 @@
|
||||||
package org.hibernate.query.sqm.mutation.internal.idtable;
|
package org.hibernate.query.sqm.mutation.internal.idtable;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
import org.hibernate.boot.model.naming.Identifier;
|
||||||
import org.hibernate.boot.model.relational.Exportable;
|
import org.hibernate.boot.model.relational.Exportable;
|
||||||
import org.hibernate.dialect.Dialect;
|
import org.hibernate.dialect.Dialect;
|
||||||
|
import org.hibernate.mapping.Collection;
|
||||||
|
import org.hibernate.mapping.Column;
|
||||||
import org.hibernate.mapping.Contributable;
|
import org.hibernate.mapping.Contributable;
|
||||||
|
import org.hibernate.mapping.PersistentClass;
|
||||||
|
import org.hibernate.mapping.Selectable;
|
||||||
|
import org.hibernate.mapping.Value;
|
||||||
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.JdbcMapping;
|
||||||
import org.hibernate.metamodel.mapping.ModelPart;
|
import org.hibernate.metamodel.mapping.ModelPart;
|
||||||
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
|
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
|
||||||
import org.hibernate.persister.entity.Joinable;
|
import org.hibernate.metamodel.spi.RuntimeModelCreationContext;
|
||||||
import org.hibernate.sql.ast.tree.from.TableGroup;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
||||||
*/
|
*/
|
||||||
public class IdTable implements Exportable, Contributable {
|
public class IdTable implements Exportable, Contributable {
|
||||||
|
|
||||||
|
public static final String DEFAULT_ALIAS = "idtable_";
|
||||||
|
|
||||||
private final EntityMappingType entityDescriptor;
|
private final EntityMappingType entityDescriptor;
|
||||||
private final String qualifiedTableName;
|
private final String qualifiedTableName;
|
||||||
|
|
||||||
|
@ -34,27 +44,38 @@ public class IdTable implements Exportable, Contributable {
|
||||||
|
|
||||||
public IdTable(
|
public IdTable(
|
||||||
EntityMappingType entityDescriptor,
|
EntityMappingType entityDescriptor,
|
||||||
Function<String,String> idTableNameAdjuster,
|
Function<String, String> idTableNameAdjuster,
|
||||||
Dialect dialect) {
|
Dialect dialect,
|
||||||
|
RuntimeModelCreationContext runtimeModelCreationContext) {
|
||||||
this.entityDescriptor = entityDescriptor;
|
this.entityDescriptor = entityDescriptor;
|
||||||
|
|
||||||
this.qualifiedTableName = idTableNameAdjuster.apply(
|
// The table name might be a sub-query, which is inappropriate for an id table name
|
||||||
// The table name might be a sub-query, which is inappropriate for an id table name
|
final String originalTableName = entityDescriptor.getEntityPersister().getSynchronizedQuerySpaces()[0];
|
||||||
entityDescriptor.getEntityPersister().getSynchronizedQuerySpaces()[0]
|
if ( Identifier.isQuoted( originalTableName ) ) {
|
||||||
);
|
this.qualifiedTableName = dialect.quote( idTableNameAdjuster.apply( Identifier.unQuote( originalTableName ) ) );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.qualifiedTableName = idTableNameAdjuster.apply( originalTableName );
|
||||||
|
}
|
||||||
|
|
||||||
|
final PersistentClass entityBinding = runtimeModelCreationContext.getBootModel()
|
||||||
|
.getEntityBinding( entityDescriptor.getEntityName() );
|
||||||
|
|
||||||
|
final Iterator<Column> itr = entityBinding.getTable().getPrimaryKey().getColumnIterator();
|
||||||
|
final Iterator<JdbcMapping> jdbcMappings = entityDescriptor.getIdentifierMapping().getJdbcMappings().iterator();
|
||||||
|
while ( itr.hasNext() ) {
|
||||||
|
final Column column = itr.next();
|
||||||
|
final JdbcMapping jdbcMapping = jdbcMappings.next();
|
||||||
|
columns.add(
|
||||||
|
new IdTableColumn(
|
||||||
|
this,
|
||||||
|
column.getText( dialect ),
|
||||||
|
jdbcMapping,
|
||||||
|
column.getSqlType( dialect, runtimeModelCreationContext.getMetadata() )
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
entityDescriptor.getIdentifierMapping().forEachSelectable(
|
|
||||||
(columnIndex, selection) -> columns.add(
|
|
||||||
new IdTableColumn(
|
|
||||||
this,
|
|
||||||
selection.getSelectionExpression(),
|
|
||||||
selection.getJdbcMapping(),
|
|
||||||
dialect.getTypeName(
|
|
||||||
selection.getJdbcMapping().getJdbcTypeDescriptor()
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
entityDescriptor.visitSubTypeAttributeMappings(
|
entityDescriptor.visitSubTypeAttributeMappings(
|
||||||
attribute -> {
|
attribute -> {
|
||||||
if ( attribute instanceof PluralAttributeMapping ) {
|
if ( attribute instanceof PluralAttributeMapping ) {
|
||||||
|
@ -64,17 +85,27 @@ public class IdTable implements Exportable, Contributable {
|
||||||
// Ensure that the FK target columns are available
|
// Ensure that the FK target columns are available
|
||||||
final ModelPart fkTarget = pluralAttribute.getKeyDescriptor().getTargetPart();
|
final ModelPart fkTarget = pluralAttribute.getKeyDescriptor().getTargetPart();
|
||||||
if ( !( fkTarget instanceof EntityIdentifierMapping ) ) {
|
if ( !( fkTarget instanceof EntityIdentifierMapping ) ) {
|
||||||
|
final Value value = entityBinding.getSubclassProperty( pluralAttribute.getAttributeName() )
|
||||||
|
.getValue();
|
||||||
|
final Iterator<Selectable> columnIterator = ( (Collection) value ).getKey()
|
||||||
|
.getColumnIterator();
|
||||||
fkTarget.forEachSelectable(
|
fkTarget.forEachSelectable(
|
||||||
(columnIndex, selection) -> columns.add(
|
(columnIndex, selection) -> {
|
||||||
new IdTableColumn(
|
final Selectable selectable = columnIterator.next();
|
||||||
this,
|
if ( selectable instanceof Column ) {
|
||||||
selection.getSelectionExpression(),
|
columns.add(
|
||||||
selection.getJdbcMapping(),
|
new IdTableColumn(
|
||||||
dialect.getTypeName(
|
this,
|
||||||
selection.getJdbcMapping().getJdbcTypeDescriptor()
|
selectable.getText( dialect ),
|
||||||
|
selection.getJdbcMapping(),
|
||||||
|
( (Column) selectable ).getSqlType(
|
||||||
|
dialect,
|
||||||
|
runtimeModelCreationContext.getMetadata()
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
);
|
||||||
)
|
}
|
||||||
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,6 @@ import java.util.IdentityHashMap;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
@ -23,6 +22,7 @@ import org.hibernate.engine.spi.LoadQueryInfluencers;
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||||
import org.hibernate.internal.FilterHelper;
|
import org.hibernate.internal.FilterHelper;
|
||||||
|
import org.hibernate.internal.util.MutableBoolean;
|
||||||
import org.hibernate.internal.util.MutableInteger;
|
import org.hibernate.internal.util.MutableInteger;
|
||||||
import org.hibernate.metamodel.mapping.EntityIdentifierMapping;
|
import org.hibernate.metamodel.mapping.EntityIdentifierMapping;
|
||||||
import org.hibernate.metamodel.mapping.EntityMappingType;
|
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||||
|
@ -51,7 +51,7 @@ import org.hibernate.sql.ast.tree.expression.JdbcParameter;
|
||||||
import org.hibernate.sql.ast.tree.expression.SqlTuple;
|
import org.hibernate.sql.ast.tree.expression.SqlTuple;
|
||||||
import org.hibernate.sql.ast.tree.from.TableGroup;
|
import org.hibernate.sql.ast.tree.from.TableGroup;
|
||||||
import org.hibernate.sql.ast.tree.from.TableReference;
|
import org.hibernate.sql.ast.tree.from.TableReference;
|
||||||
import org.hibernate.sql.ast.tree.from.UnionTableGroup;
|
import org.hibernate.sql.ast.tree.from.UnionTableReference;
|
||||||
import org.hibernate.sql.ast.tree.predicate.FilterPredicate;
|
import org.hibernate.sql.ast.tree.predicate.FilterPredicate;
|
||||||
import org.hibernate.sql.ast.tree.predicate.InSubQueryPredicate;
|
import org.hibernate.sql.ast.tree.predicate.InSubQueryPredicate;
|
||||||
import org.hibernate.sql.ast.tree.predicate.Predicate;
|
import org.hibernate.sql.ast.tree.predicate.Predicate;
|
||||||
|
@ -107,9 +107,9 @@ public class RestrictedDeleteExecutionDelegate implements TableBasedDeleteHandle
|
||||||
this.idTableExporterAccess = idTableExporterAccess;
|
this.idTableExporterAccess = idTableExporterAccess;
|
||||||
this.sessionUidAccess = sessionUidAccess;
|
this.sessionUidAccess = sessionUidAccess;
|
||||||
this.sessionFactory = sessionFactory;
|
this.sessionFactory = sessionFactory;
|
||||||
|
this.converter = new MultiTableSqmMutationConverter(
|
||||||
converter = new MultiTableSqmMutationConverter(
|
|
||||||
entityDescriptor,
|
entityDescriptor,
|
||||||
|
sqmDelete,
|
||||||
sqmDelete.getTarget(),
|
sqmDelete.getTarget(),
|
||||||
domainParameterXref,
|
domainParameterXref,
|
||||||
queryOptions,
|
queryOptions,
|
||||||
|
@ -149,12 +149,12 @@ public class RestrictedDeleteExecutionDelegate implements TableBasedDeleteHandle
|
||||||
// 2) we also inspect each ColumnReference that is part of the where-clause to see which
|
// 2) we also inspect each ColumnReference that is part of the where-clause to see which
|
||||||
// table it comes from. if all of the referenced columns (if any at all) are from the root table
|
// table it comes from. if all of the referenced columns (if any at all) are from the root table
|
||||||
// we can perform all of the deletes without using an id-table
|
// we can perform all of the deletes without using an id-table
|
||||||
final AtomicBoolean needsIdTableWrapper = new AtomicBoolean( false );
|
final MutableBoolean needsIdTableWrapper = new MutableBoolean( false );
|
||||||
Predicate predicate = converter.visitWhereClause(
|
Predicate predicate = converter.visitWhereClause(
|
||||||
sqmDelete.getWhereClause(),
|
sqmDelete.getWhereClause(),
|
||||||
columnReference -> {
|
columnReference -> {
|
||||||
if ( ! hierarchyRootTableReference.getIdentificationVariable().equals( columnReference.getQualifier() ) ) {
|
if ( ! hierarchyRootTableReference.getIdentificationVariable().equals( columnReference.getQualifier() ) ) {
|
||||||
needsIdTableWrapper.set( true );
|
needsIdTableWrapper.setValue( true );
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
(sqmParameter, mappingType, jdbcParameters) -> {
|
(sqmParameter, mappingType, jdbcParameters) -> {
|
||||||
|
@ -172,11 +172,17 @@ public class RestrictedDeleteExecutionDelegate implements TableBasedDeleteHandle
|
||||||
deletingTableGroup
|
deletingTableGroup
|
||||||
);
|
);
|
||||||
if ( filterPredicate != null ) {
|
if ( filterPredicate != null ) {
|
||||||
needsIdTableWrapper.set( true );
|
needsIdTableWrapper.setValue( true );
|
||||||
predicate = SqlAstTreeHelper.combinePredicates( predicate, filterPredicate );
|
predicate = SqlAstTreeHelper.combinePredicates( predicate, filterPredicate );
|
||||||
}
|
}
|
||||||
|
converter.pruneTableGroupJoins();
|
||||||
|
|
||||||
boolean needsIdTable = needsIdTableWrapper.get();
|
// We need an id table if we want to delete from an intermediate table to avoid FK violations
|
||||||
|
// The intermediate table has a FK to the root table, so we can't delete from the root table first
|
||||||
|
// Deleting from the intermediate table first also isn't possible,
|
||||||
|
// because that is the source for deletion in other tables, hence we need an id table
|
||||||
|
final boolean needsIdTable = needsIdTableWrapper.getValue()
|
||||||
|
|| entityDescriptor != entityDescriptor.getRootEntityDescriptor();
|
||||||
|
|
||||||
final SqmJdbcExecutionContextAdapter executionContextAdapter = SqmJdbcExecutionContextAdapter.omittingLockingAndPaging( executionContext );
|
final SqmJdbcExecutionContextAdapter executionContextAdapter = SqmJdbcExecutionContextAdapter.omittingLockingAndPaging( executionContext );
|
||||||
|
|
||||||
|
@ -208,17 +214,9 @@ public class RestrictedDeleteExecutionDelegate implements TableBasedDeleteHandle
|
||||||
Map<SqmParameter, MappingModelExpressable> paramTypeResolutions,
|
Map<SqmParameter, MappingModelExpressable> paramTypeResolutions,
|
||||||
SqlExpressionResolver sqlExpressionResolver,
|
SqlExpressionResolver sqlExpressionResolver,
|
||||||
ExecutionContext executionContext) {
|
ExecutionContext executionContext) {
|
||||||
final EntityPersister rootEntityPersister;
|
assert entityDescriptor == entityDescriptor.getRootEntityDescriptor();
|
||||||
final String rootEntityName = entityDescriptor.getEntityPersister().getRootEntityName();
|
|
||||||
if ( rootEntityName.equals( entityDescriptor.getEntityName() ) ) {
|
|
||||||
rootEntityPersister = entityDescriptor.getEntityPersister();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
rootEntityPersister = sessionFactory.getDomainModel().findEntityDescriptor( rootEntityName );
|
|
||||||
}
|
|
||||||
|
|
||||||
final MutableInteger rows = new MutableInteger();
|
|
||||||
|
|
||||||
|
final EntityPersister rootEntityPersister = entityDescriptor.getEntityPersister();
|
||||||
final String rootTableName = ( (Joinable) rootEntityPersister ).getTableName();
|
final String rootTableName = ( (Joinable) rootEntityPersister ).getTableName();
|
||||||
final TableReference rootTableReference = tableGroup.resolveTableReference(
|
final TableReference rootTableReference = tableGroup.resolveTableReference(
|
||||||
tableGroup.getNavigablePath(),
|
tableGroup.getNavigablePath(),
|
||||||
|
@ -247,48 +245,114 @@ public class RestrictedDeleteExecutionDelegate implements TableBasedDeleteHandle
|
||||||
executionContext.getSession()
|
executionContext.getSession()
|
||||||
);
|
);
|
||||||
|
|
||||||
entityDescriptor.visitConstraintOrderedTables(
|
SqmMutationStrategyHelper.cleanUpCollectionTables(
|
||||||
(tableExpression, tableKeyColumnVisitationSupplier) -> {
|
entityDescriptor,
|
||||||
if ( tableExpression.equals( rootTableName ) ) {
|
(tableReference, attributeMapping) -> {
|
||||||
rows.set(
|
// No need for a predicate if there is no supplied predicate i.e. this is a full cleanup
|
||||||
deleteFromRootTableWithoutIdTable(
|
if ( suppliedPredicate == null ) {
|
||||||
rootTableReference,
|
return null;
|
||||||
suppliedPredicate,
|
}
|
||||||
jdbcParameterBindings,
|
final ForeignKeyDescriptor fkDescriptor = attributeMapping.getKeyDescriptor();
|
||||||
executionContext
|
final QuerySpec idSelectFkSubQuery;
|
||||||
)
|
// todo (6.0): based on the location of the attribute mapping, we could prune the table group of the subquery
|
||||||
);
|
if ( fkDescriptor.getTargetPart() instanceof EntityIdentifierMapping ) {
|
||||||
|
idSelectFkSubQuery = matchingIdSubQuerySpec;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
rows.set(
|
idSelectFkSubQuery = ExecuteWithoutIdTableHelper.createIdMatchingSubQuerySpec(
|
||||||
rows.get() + deleteFromNonRootTableWithoutIdTable(
|
tableGroup.getNavigablePath(),
|
||||||
resolveUnionTableReference( tableGroup, tableExpression ),
|
rootTableReference,
|
||||||
|
suppliedPredicate,
|
||||||
|
rootEntityPersister,
|
||||||
|
sqlExpressionResolver,
|
||||||
|
sessionFactory
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return new InSubQueryPredicate(
|
||||||
|
MappingModelHelper.buildColumnReferenceExpression(
|
||||||
|
fkDescriptor,
|
||||||
|
null,
|
||||||
|
sessionFactory
|
||||||
|
),
|
||||||
|
idSelectFkSubQuery,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
|
||||||
|
},
|
||||||
|
jdbcParameterBindings,
|
||||||
|
executionContext
|
||||||
|
);
|
||||||
|
|
||||||
|
if ( rootTableReference instanceof UnionTableReference ) {
|
||||||
|
final MutableInteger rows = new MutableInteger();
|
||||||
|
entityDescriptor.visitConstraintOrderedTables(
|
||||||
|
(tableExpression, tableKeyColumnVisitationSupplier) -> {
|
||||||
|
final TableReference tableReference = new TableReference(
|
||||||
|
tableExpression,
|
||||||
|
tableGroup.getPrimaryTableReference().getIdentificationVariable(),
|
||||||
|
false,
|
||||||
|
sessionFactory
|
||||||
|
);
|
||||||
|
final QuerySpec idMatchingSubQuerySpec;
|
||||||
|
// No need for a predicate if there is no supplied predicate i.e. this is a full cleanup
|
||||||
|
if ( suppliedPredicate == null ) {
|
||||||
|
idMatchingSubQuerySpec = null;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
idMatchingSubQuerySpec = matchingIdSubQuerySpec;
|
||||||
|
}
|
||||||
|
rows.plus(
|
||||||
|
deleteFromNonRootTableWithoutIdTable(
|
||||||
|
tableReference,
|
||||||
tableKeyColumnVisitationSupplier,
|
tableKeyColumnVisitationSupplier,
|
||||||
sqlExpressionResolver,
|
sqlExpressionResolver,
|
||||||
tableGroup,
|
tableGroup,
|
||||||
matchingIdSubQuerySpec,
|
idMatchingSubQuerySpec,
|
||||||
jdbcParameterBindings,
|
jdbcParameterBindings,
|
||||||
executionContext
|
executionContext
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
return rows.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
private TableReference resolveUnionTableReference(TableGroup tableGroup, String tableExpression) {
|
|
||||||
if ( tableGroup instanceof UnionTableGroup ) {
|
|
||||||
return new TableReference(
|
|
||||||
tableExpression,
|
|
||||||
tableGroup.getPrimaryTableReference().getIdentificationVariable(),
|
|
||||||
false,
|
|
||||||
sessionFactory
|
|
||||||
);
|
);
|
||||||
|
return rows.get();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return tableGroup.getTableReference( tableGroup.getNavigablePath(), tableExpression, true, true );
|
entityDescriptor.visitConstraintOrderedTables(
|
||||||
|
(tableExpression, tableKeyColumnVisitationSupplier) -> {
|
||||||
|
if ( !tableExpression.equals( rootTableName ) ) {
|
||||||
|
final TableReference tableReference = tableGroup.getTableReference(
|
||||||
|
tableGroup.getNavigablePath(),
|
||||||
|
tableExpression,
|
||||||
|
true,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
final QuerySpec idMatchingSubQuerySpec;
|
||||||
|
// No need for a predicate if there is no supplied predicate i.e. this is a full cleanup
|
||||||
|
if ( suppliedPredicate == null ) {
|
||||||
|
idMatchingSubQuerySpec = null;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
idMatchingSubQuerySpec = matchingIdSubQuerySpec;
|
||||||
|
}
|
||||||
|
deleteFromNonRootTableWithoutIdTable(
|
||||||
|
tableReference,
|
||||||
|
tableKeyColumnVisitationSupplier,
|
||||||
|
sqlExpressionResolver,
|
||||||
|
tableGroup,
|
||||||
|
idMatchingSubQuerySpec,
|
||||||
|
jdbcParameterBindings,
|
||||||
|
executionContext
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return deleteFromRootTableWithoutIdTable(
|
||||||
|
rootTableReference,
|
||||||
|
suppliedPredicate,
|
||||||
|
jdbcParameterBindings,
|
||||||
|
executionContext
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -304,7 +368,7 @@ public class RestrictedDeleteExecutionDelegate implements TableBasedDeleteHandle
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private int deleteFromNonRootTableWithoutIdTable(
|
private int deleteFromNonRootTableWithoutIdTable(
|
||||||
TableReference targetTableReference,
|
TableReference targetTableReference,
|
||||||
Supplier<Consumer<SelectableConsumer>> tableKeyColumnVisitationSupplier,
|
Supplier<Consumer<SelectableConsumer>> tableKeyColumnVisitationSupplier,
|
||||||
SqlExpressionResolver sqlExpressionResolver,
|
SqlExpressionResolver sqlExpressionResolver,
|
||||||
|
@ -315,50 +379,62 @@ public class RestrictedDeleteExecutionDelegate implements TableBasedDeleteHandle
|
||||||
assert targetTableReference != null;
|
assert targetTableReference != null;
|
||||||
log.tracef( "deleteFromNonRootTable - %s", targetTableReference.getTableExpression() );
|
log.tracef( "deleteFromNonRootTable - %s", targetTableReference.getTableExpression() );
|
||||||
|
|
||||||
/*
|
final TableReference deleteTableReference = new TableReference(
|
||||||
* delete from sub_table
|
targetTableReference.getTableExpression(),
|
||||||
* where sub_id in (
|
DeleteStatement.DEFAULT_ALIAS,
|
||||||
* select root_id from root_table
|
true,
|
||||||
* where {predicate}
|
sessionFactory
|
||||||
* )
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Create the `sub_id` reference as the LHS of the in-subquery predicate
|
|
||||||
*/
|
|
||||||
final List<ColumnReference> deletingTableColumnRefs = new ArrayList<>();
|
|
||||||
tableKeyColumnVisitationSupplier.get().accept(
|
|
||||||
(columnIndex, selection) -> {
|
|
||||||
assert targetTableReference.getTableReference( selection.getContainingTableExpression() ) != null;
|
|
||||||
|
|
||||||
final Expression expression = sqlExpressionResolver.resolveSqlExpression(
|
|
||||||
SqlExpressionResolver.createColumnReferenceKey( targetTableReference, selection.getSelectionExpression() ),
|
|
||||||
sqlAstProcessingState -> new ColumnReference(
|
|
||||||
targetTableReference,
|
|
||||||
selection,
|
|
||||||
sessionFactory
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
deletingTableColumnRefs.add( (ColumnReference) expression );
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
|
final Predicate tableDeletePredicate;
|
||||||
final Expression deletingTableColumnRefsExpression;
|
if ( matchingIdSubQuerySpec == null ) {
|
||||||
if ( deletingTableColumnRefs.size() == 1 ) {
|
tableDeletePredicate = null;
|
||||||
deletingTableColumnRefsExpression = deletingTableColumnRefs.get( 0 );
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
deletingTableColumnRefsExpression = new SqlTuple( deletingTableColumnRefs, entityDescriptor.getIdentifierMapping() );
|
/*
|
||||||
|
* delete from sub_table
|
||||||
|
* where sub_id in (
|
||||||
|
* select root_id from root_table
|
||||||
|
* where {predicate}
|
||||||
|
* )
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Create the `sub_id` reference as the LHS of the in-subquery predicate
|
||||||
|
*/
|
||||||
|
final List<ColumnReference> deletingTableColumnRefs = new ArrayList<>();
|
||||||
|
tableKeyColumnVisitationSupplier.get().accept(
|
||||||
|
(columnIndex, selection) -> {
|
||||||
|
assert deleteTableReference.getTableReference( selection.getContainingTableExpression() ) != null;
|
||||||
|
|
||||||
|
final Expression expression = sqlExpressionResolver.resolveSqlExpression(
|
||||||
|
SqlExpressionResolver.createColumnReferenceKey( deleteTableReference, selection.getSelectionExpression() ),
|
||||||
|
sqlAstProcessingState -> new ColumnReference(
|
||||||
|
deleteTableReference,
|
||||||
|
selection,
|
||||||
|
sessionFactory
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
deletingTableColumnRefs.add( (ColumnReference) expression );
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
final Expression deletingTableColumnRefsExpression;
|
||||||
|
if ( deletingTableColumnRefs.size() == 1 ) {
|
||||||
|
deletingTableColumnRefsExpression = deletingTableColumnRefs.get( 0 );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
deletingTableColumnRefsExpression = new SqlTuple( deletingTableColumnRefs, entityDescriptor.getIdentifierMapping() );
|
||||||
|
}
|
||||||
|
|
||||||
|
tableDeletePredicate = new InSubQueryPredicate(
|
||||||
|
deletingTableColumnRefsExpression,
|
||||||
|
matchingIdSubQuerySpec,
|
||||||
|
false
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
final InSubQueryPredicate idMatchPredicate = new InSubQueryPredicate(
|
final DeleteStatement sqlAstDelete = new DeleteStatement( deleteTableReference, tableDeletePredicate );
|
||||||
deletingTableColumnRefsExpression,
|
|
||||||
matchingIdSubQuerySpec,
|
|
||||||
false
|
|
||||||
);
|
|
||||||
|
|
||||||
final DeleteStatement sqlAstDelete = new DeleteStatement( targetTableReference, idMatchPredicate );
|
|
||||||
final int rows = executeSqlDelete(
|
final int rows = executeSqlDelete(
|
||||||
sqlAstDelete,
|
sqlAstDelete,
|
||||||
jdbcParameterBindings,
|
jdbcParameterBindings,
|
||||||
|
@ -510,6 +586,12 @@ public class RestrictedDeleteExecutionDelegate implements TableBasedDeleteHandle
|
||||||
final SessionFactoryImplementor factory = executionContext.getSession().getFactory();
|
final SessionFactoryImplementor factory = executionContext.getSession().getFactory();
|
||||||
|
|
||||||
final TableKeyExpressionCollector keyColumnCollector = new TableKeyExpressionCollector( entityDescriptor );
|
final TableKeyExpressionCollector keyColumnCollector = new TableKeyExpressionCollector( entityDescriptor );
|
||||||
|
final TableReference targetTable = new TableReference(
|
||||||
|
tableExpression,
|
||||||
|
DeleteStatement.DEFAULT_ALIAS,
|
||||||
|
true,
|
||||||
|
factory
|
||||||
|
);
|
||||||
|
|
||||||
tableKeyColumnVisitationSupplier.get().accept(
|
tableKeyColumnVisitationSupplier.get().accept(
|
||||||
(columnIndex, selection) -> {
|
(columnIndex, selection) -> {
|
||||||
|
@ -520,7 +602,7 @@ public class RestrictedDeleteExecutionDelegate implements TableBasedDeleteHandle
|
||||||
|
|
||||||
keyColumnCollector.apply(
|
keyColumnCollector.apply(
|
||||||
new ColumnReference(
|
new ColumnReference(
|
||||||
(String) null,
|
targetTable,
|
||||||
selection,
|
selection,
|
||||||
factory
|
factory
|
||||||
)
|
)
|
||||||
|
@ -535,10 +617,7 @@ public class RestrictedDeleteExecutionDelegate implements TableBasedDeleteHandle
|
||||||
);
|
);
|
||||||
|
|
||||||
executeSqlDelete(
|
executeSqlDelete(
|
||||||
new DeleteStatement(
|
new DeleteStatement( targetTable, predicate ),
|
||||||
new TableReference( tableExpression, null, true, factory ),
|
|
||||||
predicate
|
|
||||||
),
|
|
||||||
JdbcParameterBindings.NO_BINDINGS,
|
JdbcParameterBindings.NO_BINDINGS,
|
||||||
executionContext
|
executionContext
|
||||||
);
|
);
|
||||||
|
|
|
@ -126,6 +126,7 @@ public class TableBasedUpdateHandler
|
||||||
|
|
||||||
final MultiTableSqmMutationConverter converterDelegate = new MultiTableSqmMutationConverter(
|
final MultiTableSqmMutationConverter converterDelegate = new MultiTableSqmMutationConverter(
|
||||||
entityDescriptor,
|
entityDescriptor,
|
||||||
|
getSqmDeleteOrUpdateStatement(),
|
||||||
getSqmDeleteOrUpdateStatement().getTarget(),
|
getSqmDeleteOrUpdateStatement().getTarget(),
|
||||||
domainParameterXref,
|
domainParameterXref,
|
||||||
executionContext.getQueryOptions(),
|
executionContext.getQueryOptions(),
|
||||||
|
@ -203,6 +204,7 @@ public class TableBasedUpdateHandler
|
||||||
if ( filterPredicate != null ) {
|
if ( filterPredicate != null ) {
|
||||||
predicate = SqlAstTreeHelper.combinePredicates( predicate, filterPredicate );
|
predicate = SqlAstTreeHelper.combinePredicates( predicate, filterPredicate );
|
||||||
}
|
}
|
||||||
|
converterDelegate.pruneTableGroupJoins();
|
||||||
|
|
||||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
// cross-reference the TableReference by alias. The TableGroup already
|
// cross-reference the TableReference by alias. The TableGroup already
|
||||||
|
|
|
@ -58,7 +58,7 @@ public class TempIdTableExporter implements IdTableExporter {
|
||||||
|
|
||||||
buffer.append( column.getColumnName() ).append( ' ' );
|
buffer.append( column.getColumnName() ).append( ' ' );
|
||||||
final int sqlTypeCode = column.getJdbcMapping().getJdbcTypeDescriptor().getDefaultSqlTypeCode();
|
final int sqlTypeCode = column.getJdbcMapping().getJdbcTypeDescriptor().getDefaultSqlTypeCode();
|
||||||
final String databaseTypeName = databaseTypeNameResolver.apply( sqlTypeCode );
|
final String databaseTypeName = column.getSqlTypeDefinition();
|
||||||
|
|
||||||
buffer.append( " " ).append( databaseTypeName ).append( " " );
|
buffer.append( " " ).append( databaseTypeName ).append( " " );
|
||||||
|
|
||||||
|
|
|
@ -162,7 +162,7 @@ public class InlineDeleteHandler implements DeleteHandler {
|
||||||
DomainQueryExecutionContext executionContext) {
|
DomainQueryExecutionContext executionContext) {
|
||||||
final TableReference targetTableReference = new TableReference(
|
final TableReference targetTableReference = new TableReference(
|
||||||
targetTableExpression,
|
targetTableExpression,
|
||||||
null,
|
DeleteStatement.DEFAULT_ALIAS,
|
||||||
false,
|
false,
|
||||||
sessionFactory
|
sessionFactory
|
||||||
);
|
);
|
||||||
|
|
|
@ -15,18 +15,35 @@ import org.hibernate.query.sqm.tree.domain.SqmPath;
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
||||||
*/
|
*/
|
||||||
public class SqmCreationHelper {
|
public class SqmCreationHelper {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a special alias that we use for implicit joins within the FROM clause.
|
||||||
|
* Passing this alias will cause that we don't generate a unique alias for a path,
|
||||||
|
* but instead use a <code>null</code> alias.
|
||||||
|
*
|
||||||
|
* The effect of this is, that we use the same table group for a query like
|
||||||
|
* `... exists ( from alias.intermediate.attribute where alias.intermediate.otherAttribute is not null )`
|
||||||
|
* for the path in the FROM clause and the one in the WHERE clause.
|
||||||
|
*/
|
||||||
|
public static final String IMPLICIT_ALIAS = "{implicit}";
|
||||||
|
|
||||||
public static NavigablePath buildRootNavigablePath(String base, String alias) {
|
public static NavigablePath buildRootNavigablePath(String base, String alias) {
|
||||||
// Make sure we always create a unique alias, otherwise we might use a wrong table group for the same join
|
return new NavigablePath( base, determineAlias( alias ) );
|
||||||
return alias == null
|
|
||||||
? new NavigablePath( base, Long.toString( System.nanoTime() ) )
|
|
||||||
: new NavigablePath( base, alias );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static NavigablePath buildSubNavigablePath(NavigablePath lhs, String base, String alias) {
|
public static NavigablePath buildSubNavigablePath(NavigablePath lhs, String base, String alias) {
|
||||||
|
return lhs.append( base, determineAlias( alias ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String determineAlias(String alias) {
|
||||||
// Make sure we always create a unique alias, otherwise we might use a wrong table group for the same join
|
// Make sure we always create a unique alias, otherwise we might use a wrong table group for the same join
|
||||||
return alias == null
|
if ( alias == null ) {
|
||||||
? lhs.append( base, Long.toString( System.nanoTime() ) )
|
return Long.toString( System.nanoTime() );
|
||||||
: lhs.append( base, alias );
|
}
|
||||||
|
else if ( alias == IMPLICIT_ALIAS ) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return alias;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static NavigablePath buildSubNavigablePath(SqmPath<?> lhs, String subNavigable, String alias) {
|
public static NavigablePath buildSubNavigablePath(SqmPath<?> lhs, String subNavigable, String alias) {
|
||||||
|
|
|
@ -82,6 +82,7 @@ import org.hibernate.metamodel.model.domain.EntityDomainType;
|
||||||
import org.hibernate.metamodel.model.domain.PluralPersistentAttribute;
|
import org.hibernate.metamodel.model.domain.PluralPersistentAttribute;
|
||||||
import org.hibernate.metamodel.model.domain.internal.CompositeSqmPathSource;
|
import org.hibernate.metamodel.model.domain.internal.CompositeSqmPathSource;
|
||||||
import org.hibernate.query.criteria.JpaPath;
|
import org.hibernate.query.criteria.JpaPath;
|
||||||
|
import org.hibernate.query.internal.QueryHelper;
|
||||||
import org.hibernate.query.sqm.function.SelfRenderingFunctionSqlAstExpression;
|
import org.hibernate.query.sqm.function.SelfRenderingFunctionSqlAstExpression;
|
||||||
import org.hibernate.query.sqm.produce.function.internal.PatternRenderer;
|
import org.hibernate.query.sqm.produce.function.internal.PatternRenderer;
|
||||||
import org.hibernate.query.sqm.tree.SqmJoinType;
|
import org.hibernate.query.sqm.tree.SqmJoinType;
|
||||||
|
@ -368,12 +369,13 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
||||||
private final EntityGraphTraversalState entityGraphTraversalState;
|
private final EntityGraphTraversalState entityGraphTraversalState;
|
||||||
|
|
||||||
private int fetchDepth;
|
private int fetchDepth;
|
||||||
|
private String currentBagRole;
|
||||||
private boolean resolvingCircularFetch;
|
private boolean resolvingCircularFetch;
|
||||||
private ForeignKeyDescriptor.Nature currentlyResolvingForeignKeySide;
|
private ForeignKeyDescriptor.Nature currentlyResolvingForeignKeySide;
|
||||||
private SqmQueryPart<?> currentSqmQueryPart;
|
private SqmQueryPart<?> currentSqmQueryPart;
|
||||||
|
|
||||||
private Map<String, FilterPredicate> collectionFilterPredicates;
|
private Map<String, FilterPredicate> collectionFilterPredicates;
|
||||||
private OrderByFragmentConsumer orderByFragmentConsumer;
|
private List<Map.Entry<OrderByFragment, TableGroup>> orderByFragments;
|
||||||
|
|
||||||
private final SqlAliasBaseManager sqlAliasBaseManager = new SqlAliasBaseManager();
|
private final SqlAliasBaseManager sqlAliasBaseManager = new SqlAliasBaseManager();
|
||||||
private final Stack<SqlAstProcessingState> processingStateStack = new StandardStack<>();
|
private final Stack<SqlAstProcessingState> processingStateStack = new StandardStack<>();
|
||||||
|
@ -1134,6 +1136,9 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
||||||
|
|
||||||
for ( SqmDynamicInstantiationArgument<?> sqmArgument : sqmDynamicInstantiation.getArguments() ) {
|
for ( SqmDynamicInstantiationArgument<?> sqmArgument : sqmDynamicInstantiation.getArguments() ) {
|
||||||
final SqmSelectableNode<?> selectableNode = sqmArgument.getSelectableNode();
|
final SqmSelectableNode<?> selectableNode = sqmArgument.getSelectableNode();
|
||||||
|
if ( selectableNode instanceof SqmPath<?> ) {
|
||||||
|
prepareForSelection( (SqmPath<?>) selectableNode );
|
||||||
|
}
|
||||||
final DomainResultProducer<?> argumentResultProducer = (DomainResultProducer<?>) selectableNode.accept( this );
|
final DomainResultProducer<?> argumentResultProducer = (DomainResultProducer<?>) selectableNode.accept( this );
|
||||||
dynamicInstantiation.addArgument( sqmArgument.getAlias(), argumentResultProducer, this );
|
dynamicInstantiation.addArgument( sqmArgument.getAlias(), argumentResultProducer, this );
|
||||||
}
|
}
|
||||||
|
@ -1423,10 +1428,6 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
||||||
pushProcessingState( processingState );
|
pushProcessingState( processingState );
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if ( topLevel ) {
|
|
||||||
orderByFragmentConsumer = new StandardOrderByFragmentConsumer();
|
|
||||||
}
|
|
||||||
|
|
||||||
// we want to visit the from-clause first
|
// we want to visit the from-clause first
|
||||||
visitFromClause( sqmQuerySpec.getFromClause() );
|
visitFromClause( sqmQuerySpec.getFromClause() );
|
||||||
|
|
||||||
|
@ -1451,12 +1452,16 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
||||||
visitOrderByOffsetAndFetch( sqmQuerySpec, sqlQuerySpec );
|
visitOrderByOffsetAndFetch( sqmQuerySpec, sqlQuerySpec );
|
||||||
|
|
||||||
if ( topLevel && statement instanceof SqmSelectStatement<?> ) {
|
if ( topLevel && statement instanceof SqmSelectStatement<?> ) {
|
||||||
orderByFragmentConsumer.visitFragments(
|
if ( orderByFragments != null ) {
|
||||||
(orderByFragment, tableGroup) -> {
|
orderByFragments.forEach(
|
||||||
orderByFragment.apply( sqlQuerySpec, tableGroup, this );
|
entry -> entry.getKey().apply(
|
||||||
}
|
sqlQuerySpec,
|
||||||
);
|
entry.getValue(),
|
||||||
orderByFragmentConsumer = null;
|
this
|
||||||
|
)
|
||||||
|
);
|
||||||
|
orderByFragments = null;
|
||||||
|
}
|
||||||
applyCollectionFilterPredicates( sqlQuerySpec );
|
applyCollectionFilterPredicates( sqlQuerySpec );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1500,33 +1505,6 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
||||||
return getFromClauseAccess().getTableGroup( navigablePath );
|
return getFromClauseAccess().getTableGroup( navigablePath );
|
||||||
}
|
}
|
||||||
|
|
||||||
private interface OrderByFragmentConsumer {
|
|
||||||
void accept(OrderByFragment orderByFragment, TableGroup tableGroup);
|
|
||||||
|
|
||||||
void visitFragments(BiConsumer<OrderByFragment,TableGroup> consumer);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class StandardOrderByFragmentConsumer implements OrderByFragmentConsumer {
|
|
||||||
private Map<OrderByFragment, TableGroup> fragments;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void accept(OrderByFragment orderByFragment, TableGroup tableGroup) {
|
|
||||||
if ( fragments == null ) {
|
|
||||||
fragments = new LinkedHashMap<>();
|
|
||||||
}
|
|
||||||
fragments.put( orderByFragment, tableGroup );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void visitFragments(BiConsumer<OrderByFragment, TableGroup> consumer) {
|
|
||||||
if ( fragments == null || fragments.isEmpty() ) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
fragments.forEach( consumer );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void applyCollectionFilterPredicates(QuerySpec sqlQuerySpec) {
|
protected void applyCollectionFilterPredicates(QuerySpec sqlQuerySpec) {
|
||||||
final List<TableGroup> roots = sqlQuerySpec.getFromClause().getRoots();
|
final List<TableGroup> roots = sqlQuerySpec.getFromClause().getRoots();
|
||||||
if ( roots != null && roots.size() == 1 ) {
|
if ( roots != null && roots.size() == 1 ) {
|
||||||
|
@ -2410,8 +2388,7 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
||||||
implicitJoinChecker = BaseSqmToSqlAstConverter::verifyManipulationImplicitJoin;
|
implicitJoinChecker = BaseSqmToSqlAstConverter::verifyManipulationImplicitJoin;
|
||||||
}
|
}
|
||||||
final FromClauseIndex fromClauseIndex = fromClauseIndexStack.getCurrent();
|
final FromClauseIndex fromClauseIndex = fromClauseIndexStack.getCurrent();
|
||||||
final boolean useInnerJoin = currentClauseStack.getCurrent() == Clause.SELECT;
|
prepareReusablePath( fromClauseIndex, sqmPath, false, implicitJoinChecker );
|
||||||
prepareReusablePath( fromClauseIndex, sqmPath, useInnerJoin, implicitJoinChecker );
|
|
||||||
return supplier.get();
|
return supplier.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2437,6 +2414,7 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
||||||
);
|
);
|
||||||
if ( parentPath instanceof SqmTreatedPath<?, ?> ) {
|
if ( parentPath instanceof SqmTreatedPath<?, ?> ) {
|
||||||
fromClauseIndex.register( (SqmPath<?>) parentPath, parentTableGroup );
|
fromClauseIndex.register( (SqmPath<?>) parentPath, parentTableGroup );
|
||||||
|
return parentTableGroup;
|
||||||
}
|
}
|
||||||
final TableGroup newTableGroup = createTableGroup( parentTableGroup, (SqmPath<?>) parentPath, useInnerJoin );
|
final TableGroup newTableGroup = createTableGroup( parentTableGroup, (SqmPath<?>) parentPath, useInnerJoin );
|
||||||
if ( newTableGroup != null ) {
|
if ( newTableGroup != null ) {
|
||||||
|
@ -2974,6 +2952,14 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
||||||
this,
|
this,
|
||||||
creationContext
|
creationContext
|
||||||
);
|
);
|
||||||
|
final FilterPredicate filterPredicate = FilterHelper.createFilterPredicate(
|
||||||
|
getLoadQueryInfluencers(),
|
||||||
|
(Joinable) pluralAttributeMapping.getCollectionDescriptor(),
|
||||||
|
tableGroup
|
||||||
|
);
|
||||||
|
if ( filterPredicate != null ) {
|
||||||
|
subQuerySpec.applyPredicate( filterPredicate );
|
||||||
|
}
|
||||||
|
|
||||||
getFromClauseAccess().registerTableGroup( pluralPath.getNavigablePath(), tableGroup );
|
getFromClauseAccess().registerTableGroup( pluralPath.getNavigablePath(), tableGroup );
|
||||||
registerPluralTableGroupParts( tableGroup );
|
registerPluralTableGroupParts( tableGroup );
|
||||||
|
@ -3109,6 +3095,14 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
||||||
this,
|
this,
|
||||||
creationContext
|
creationContext
|
||||||
);
|
);
|
||||||
|
final FilterPredicate filterPredicate = FilterHelper.createFilterPredicate(
|
||||||
|
getLoadQueryInfluencers(),
|
||||||
|
(Joinable) pluralAttributeMapping.getCollectionDescriptor(),
|
||||||
|
tableGroup
|
||||||
|
);
|
||||||
|
if ( filterPredicate != null ) {
|
||||||
|
subQuerySpec.applyPredicate( filterPredicate );
|
||||||
|
}
|
||||||
|
|
||||||
getFromClauseAccess().registerTableGroup( pluralPartPath.getNavigablePath(), tableGroup );
|
getFromClauseAccess().registerTableGroup( pluralPartPath.getNavigablePath(), tableGroup );
|
||||||
registerPluralTableGroupParts( tableGroup );
|
registerPluralTableGroupParts( tableGroup );
|
||||||
|
@ -3687,6 +3681,10 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
||||||
|
|
||||||
log.debugf( "Determining mapping-model type for generalized SqmExpression : %s", sqmExpression );
|
log.debugf( "Determining mapping-model type for generalized SqmExpression : %s", sqmExpression );
|
||||||
final SqmExpressable<?> nodeType = sqmExpression.getNodeType();
|
final SqmExpressable<?> nodeType = sqmExpression.getNodeType();
|
||||||
|
if ( nodeType == null ) {
|
||||||
|
// We can't determine the type of the expression
|
||||||
|
return null;
|
||||||
|
}
|
||||||
final MappingModelExpressable valueMapping = domainModel.resolveMappingExpressable(
|
final MappingModelExpressable valueMapping = domainModel.resolveMappingExpressable(
|
||||||
nodeType,
|
nodeType,
|
||||||
this::findTableGroupByPath
|
this::findTableGroupByPath
|
||||||
|
@ -4040,20 +4038,19 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
||||||
}
|
}
|
||||||
|
|
||||||
private BasicValuedMapping getExpressionType(SqmBinaryArithmetic<?> expression) {
|
private BasicValuedMapping getExpressionType(SqmBinaryArithmetic<?> expression) {
|
||||||
final SqmExpressable<?> leftHandOperandType = expression.getLeftHandOperand().getNodeType();
|
final SqmExpressable<?> sqmExpressable = QueryHelper.highestPrecedenceType(
|
||||||
if ( leftHandOperandType instanceof BasicValuedMapping ) {
|
expression.getLeftHandOperand().getNodeType(),
|
||||||
return (BasicValuedMapping) leftHandOperandType;
|
expression.getRightHandOperand().getNodeType()
|
||||||
|
);
|
||||||
|
if ( sqmExpressable instanceof BasicValuedMapping ) {
|
||||||
|
return (BasicValuedMapping) sqmExpressable;
|
||||||
}
|
}
|
||||||
else {
|
else if ( sqmExpressable != null ) {
|
||||||
final SqmExpressable<?> rightHandOperandType = expression.getRightHandOperand().getNodeType();
|
|
||||||
if ( rightHandOperandType instanceof BasicValuedMapping ) {
|
|
||||||
return (BasicValuedMapping) rightHandOperandType;
|
|
||||||
}
|
|
||||||
|
|
||||||
return getTypeConfiguration().getBasicTypeForJavaType(
|
return getTypeConfiguration().getBasicTypeForJavaType(
|
||||||
leftHandOperandType.getExpressableJavaTypeDescriptor().getJavaTypeClass()
|
sqmExpressable.getExpressableJavaTypeDescriptor().getJavaTypeClass()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
return JavaObjectType.INSTANCE;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Expression toSqlExpression(Object value) {
|
private Expression toSqlExpression(Object value) {
|
||||||
|
@ -4664,33 +4661,6 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// @Override
|
|
||||||
// public Object visitPluralAttributeElementBinding(PluralAttributeElementBinding binding) {
|
|
||||||
// final TableGroup resolvedTableGroup = fromClauseIndex.findResolvedTableGroup( binding.getFromElement() );
|
|
||||||
//
|
|
||||||
// return getCurrentDomainReferenceExpressionBuilder().buildPluralAttributeElementReferenceExpression(
|
|
||||||
// binding,
|
|
||||||
// resolvedTableGroup,
|
|
||||||
// PersisterHelper.convert( binding.getNavigablePath() )
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// @Override
|
|
||||||
// public ColumnReference visitExplicitColumnReference(SqmColumnReference sqmColumnReference) {
|
|
||||||
// final TableGroup tableGroup = fromClauseIndex.findTableGroup(
|
|
||||||
// sqmColumnReference.getSqmFromBase().getNavigablePath()
|
|
||||||
// );
|
|
||||||
//
|
|
||||||
// final ColumnReference columnReference = tableGroup.locateColumnReferenceByName( sqmColumnReference.getColumnName() );
|
|
||||||
//
|
|
||||||
// if ( columnReference == null ) {
|
|
||||||
// throw new HibernateException( "Could not resolve ColumnReference" );
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// return columnReference;
|
|
||||||
// }
|
|
||||||
|
|
||||||
|
|
||||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
// Predicates
|
// Predicates
|
||||||
|
|
||||||
|
@ -4763,6 +4733,14 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
||||||
this,
|
this,
|
||||||
creationContext
|
creationContext
|
||||||
);
|
);
|
||||||
|
final FilterPredicate filterPredicate = FilterHelper.createFilterPredicate(
|
||||||
|
getLoadQueryInfluencers(),
|
||||||
|
(Joinable) pluralAttributeMapping.getCollectionDescriptor(),
|
||||||
|
tableGroup
|
||||||
|
);
|
||||||
|
if ( filterPredicate != null ) {
|
||||||
|
subQuerySpec.applyPredicate( filterPredicate );
|
||||||
|
}
|
||||||
|
|
||||||
getFromClauseAccess().registerTableGroup( pluralPath.getNavigablePath(), tableGroup );
|
getFromClauseAccess().registerTableGroup( pluralPath.getNavigablePath(), tableGroup );
|
||||||
registerPluralTableGroupParts( tableGroup );
|
registerPluralTableGroupParts( tableGroup );
|
||||||
|
@ -4837,8 +4815,11 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
||||||
finally {
|
finally {
|
||||||
inferrableTypeAccessStack.pop();
|
inferrableTypeAccessStack.pop();
|
||||||
}
|
}
|
||||||
|
ComparisonOperator sqmOperator = predicate.getSqmOperator();
|
||||||
return new ComparisonPredicate( lhs, predicate.getSqmOperator(), rhs, getBooleanType() );
|
if ( predicate.isNegated() ) {
|
||||||
|
sqmOperator = sqmOperator.negated();
|
||||||
|
}
|
||||||
|
return new ComparisonPredicate( lhs, sqmOperator, rhs, getBooleanType() );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -4906,11 +4887,7 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
||||||
new SqlSelectionImpl( 1, 0, jdbcLiteral )
|
new SqlSelectionImpl( 1, 0, jdbcLiteral )
|
||||||
);
|
);
|
||||||
|
|
||||||
final ExistsPredicate existsPredicate = new ExistsPredicate( subQuerySpec, getBooleanType() );
|
return new ExistsPredicate( subQuerySpec, !predicate.isNegated(), getBooleanType() );
|
||||||
if ( predicate.isNegated() ) {
|
|
||||||
return existsPredicate;
|
|
||||||
}
|
|
||||||
return new NegatedPredicate( existsPredicate );
|
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
popProcessingStateStack();
|
popProcessingStateStack();
|
||||||
|
@ -5081,19 +5058,22 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
||||||
SqmParameter<?> sqmParameter,
|
SqmParameter<?> sqmParameter,
|
||||||
QueryParameterImplementor<?> domainParam,
|
QueryParameterImplementor<?> domainParam,
|
||||||
QueryParameterBinding<?> domainParamBinding) {
|
QueryParameterBinding<?> domainParamBinding) {
|
||||||
|
final Iterator<?> iterator = domainParamBinding.getBindValues().iterator();
|
||||||
|
|
||||||
final InListPredicate inListPredicate = new InListPredicate(
|
final InListPredicate inListPredicate = new InListPredicate(
|
||||||
(Expression) sqmPredicate.getTestExpression().accept( this ),
|
(Expression) sqmPredicate.getTestExpression().accept( this ),
|
||||||
sqmPredicate.isNegated(),
|
sqmPredicate.isNegated(),
|
||||||
getBooleanType()
|
getBooleanType()
|
||||||
);
|
);
|
||||||
|
if ( !iterator.hasNext() ) {
|
||||||
|
return inListPredicate;
|
||||||
|
}
|
||||||
|
|
||||||
inferrableTypeAccessStack.push(
|
inferrableTypeAccessStack.push(
|
||||||
() -> determineValueMapping( sqmPredicate.getTestExpression() )
|
() -> determineValueMapping( sqmPredicate.getTestExpression() )
|
||||||
);
|
);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final Iterator<?> iterator = domainParamBinding.getBindValues().iterator();
|
|
||||||
inListPredicate.addExpression( consumeSingleSqmParameter( sqmParameter ) );
|
inListPredicate.addExpression( consumeSingleSqmParameter( sqmParameter ) );
|
||||||
iterator.next();
|
iterator.next();
|
||||||
while ( iterator.hasNext() ) {
|
while ( iterator.hasNext() ) {
|
||||||
|
@ -5145,7 +5125,11 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object visitExistsPredicate(SqmExistsPredicate predicate) {
|
public Object visitExistsPredicate(SqmExistsPredicate predicate) {
|
||||||
return new ExistsPredicate( (QueryPart) predicate.getExpression().accept( this ), getBooleanType() );
|
return new ExistsPredicate(
|
||||||
|
(QueryPart) predicate.getExpression().accept( this ),
|
||||||
|
predicate.isNegated(),
|
||||||
|
getBooleanType()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -5174,165 +5158,192 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
||||||
// .getOrMakeJavaDescriptor( namedClass );
|
// .getOrMakeJavaDescriptor( namedClass );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public void addFetch(List<Fetch> fetches, FetchParent fetchParent, Fetchable fetchable, Boolean isKeyFetchable) {
|
||||||
public List<Fetch> visitFetches(FetchParent fetchParent) {
|
final NavigablePath resolvedNavigablePath = fetchParent.resolveNavigablePath( fetchable );
|
||||||
final List<Fetch> fetches = CollectionHelper.arrayList( fetchParent.getReferencedMappingType().getNumberOfFetchables() );
|
|
||||||
final List<String> bagRoles = new ArrayList<>();
|
|
||||||
|
|
||||||
final BiConsumer<Fetchable, Boolean> fetchableBiConsumer = (fetchable, isKeyFetchable) -> {
|
final String alias;
|
||||||
final NavigablePath resolvedNavigablePath = fetchParent.resolveNavigablePath( fetchable );
|
FetchTiming fetchTiming = fetchable.getMappedFetchOptions().getTiming();
|
||||||
|
boolean joined = false;
|
||||||
|
|
||||||
final String alias;
|
EntityGraphTraversalState.TraversalResult traversalResult = null;
|
||||||
FetchTiming fetchTiming = fetchable.getMappedFetchOptions().getTiming();
|
final FromClauseIndex fromClauseIndex = getFromClauseIndex();
|
||||||
boolean joined = false;
|
final SqmAttributeJoin<?, ?> fetchedJoin = fromClauseIndex.findFetchedJoinByPath( resolvedNavigablePath );
|
||||||
|
boolean explicitFetch = false;
|
||||||
|
|
||||||
EntityGraphTraversalState.TraversalResult traversalResult = null;
|
final NavigablePath fetchablePath;
|
||||||
final FromClauseIndex fromClauseIndex = getFromClauseIndex();
|
if ( fetchedJoin != null ) {
|
||||||
final SqmAttributeJoin<?, ?> fetchedJoin = fromClauseIndex.findFetchedJoinByPath( resolvedNavigablePath );
|
fetchablePath = fetchedJoin.getNavigablePath();
|
||||||
boolean explicitFetch = false;
|
// there was an explicit fetch in the SQM
|
||||||
|
// there should be a TableGroupJoin registered for this `fetchablePath` already
|
||||||
|
assert fromClauseIndex.getTableGroup( fetchedJoin.getNavigablePath() ) != null;
|
||||||
|
|
||||||
final NavigablePath fetchablePath;
|
if ( fetchedJoin.isFetched() ) {
|
||||||
if ( fetchedJoin != null ) {
|
fetchTiming = FetchTiming.IMMEDIATE;
|
||||||
fetchablePath = fetchedJoin.getNavigablePath();
|
|
||||||
// there was an explicit fetch in the SQM
|
|
||||||
// there should be a TableGroupJoin registered for this `fetchablePath` already
|
|
||||||
assert fromClauseIndex.getTableGroup( fetchedJoin.getNavigablePath() ) != null;
|
|
||||||
|
|
||||||
if ( fetchedJoin.isFetched() ) {
|
|
||||||
fetchTiming = FetchTiming.IMMEDIATE;
|
|
||||||
}
|
|
||||||
joined = true;
|
|
||||||
alias = fetchedJoin.getExplicitAlias();
|
|
||||||
explicitFetch = true;
|
|
||||||
}
|
}
|
||||||
else {
|
joined = true;
|
||||||
fetchablePath = resolvedNavigablePath;
|
alias = fetchedJoin.getExplicitAlias();
|
||||||
// there was not an explicit fetch in the SQM
|
explicitFetch = true;
|
||||||
alias = null;
|
}
|
||||||
|
else {
|
||||||
|
fetchablePath = resolvedNavigablePath;
|
||||||
|
// there was not an explicit fetch in the SQM
|
||||||
|
alias = null;
|
||||||
|
|
||||||
if ( !( fetchable instanceof CollectionPart ) ) {
|
if ( !( fetchable instanceof CollectionPart ) ) {
|
||||||
if ( entityGraphTraversalState != null ) {
|
if ( entityGraphTraversalState != null ) {
|
||||||
traversalResult = entityGraphTraversalState.traverse( fetchParent, fetchable, isKeyFetchable );
|
traversalResult = entityGraphTraversalState.traverse(
|
||||||
fetchTiming = traversalResult.getFetchTiming();
|
fetchParent,
|
||||||
joined = traversalResult.isJoined();
|
fetchable,
|
||||||
explicitFetch = true;
|
isKeyFetchable
|
||||||
}
|
);
|
||||||
else if ( getLoadQueryInfluencers().hasEnabledFetchProfiles() ) {
|
fetchTiming = traversalResult.getFetchTiming();
|
||||||
// There is no point in checking the fetch profile if it can't affect this fetchable
|
joined = traversalResult.isJoined();
|
||||||
if ( fetchTiming != FetchTiming.IMMEDIATE || fetchable.incrementFetchDepth() ) {
|
explicitFetch = true;
|
||||||
final String fetchableRole = fetchable.getNavigableRole().getFullPath();
|
}
|
||||||
|
else if ( getLoadQueryInfluencers().hasEnabledFetchProfiles() ) {
|
||||||
|
// There is no point in checking the fetch profile if it can't affect this fetchable
|
||||||
|
if ( fetchTiming != FetchTiming.IMMEDIATE || fetchable.incrementFetchDepth() ) {
|
||||||
|
final String fetchableRole = fetchable.getNavigableRole().getFullPath();
|
||||||
|
|
||||||
for ( String enabledFetchProfileName : getLoadQueryInfluencers().getEnabledFetchProfileNames() ) {
|
for ( String enabledFetchProfileName : getLoadQueryInfluencers()
|
||||||
final FetchProfile enabledFetchProfile = getCreationContext().getSessionFactory()
|
.getEnabledFetchProfileNames() ) {
|
||||||
.getFetchProfile( enabledFetchProfileName );
|
final FetchProfile enabledFetchProfile = getCreationContext()
|
||||||
final org.hibernate.engine.profile.Fetch profileFetch = enabledFetchProfile.getFetchByRole(
|
.getSessionFactory()
|
||||||
fetchableRole );
|
.getFetchProfile( enabledFetchProfileName );
|
||||||
|
final org.hibernate.engine.profile.Fetch profileFetch = enabledFetchProfile.getFetchByRole(
|
||||||
|
fetchableRole );
|
||||||
|
|
||||||
if ( profileFetch != null ) {
|
if ( profileFetch != null ) {
|
||||||
fetchTiming = FetchTiming.IMMEDIATE;
|
fetchTiming = FetchTiming.IMMEDIATE;
|
||||||
joined = joined || profileFetch.getStyle() == org.hibernate.engine.profile.Fetch.Style.JOIN;
|
joined = joined || profileFetch.getStyle() == org.hibernate.engine.profile.Fetch.Style.JOIN;
|
||||||
explicitFetch = true;
|
explicitFetch = true;
|
||||||
|
|
||||||
|
if ( currentBagRole != null && fetchable instanceof PluralAttributeMapping ) {
|
||||||
|
final CollectionClassification collectionClassification = ( (PluralAttributeMapping) fetchable ).getMappedType()
|
||||||
|
.getCollectionSemantics()
|
||||||
|
.getCollectionClassification();
|
||||||
|
if ( collectionClassification == CollectionClassification.BAG ) {
|
||||||
|
// To avoid a MultipleBagFetchException due to fetch profiles in a circular model,
|
||||||
|
// we skip join fetching in case we encounter an existing bag role
|
||||||
|
joined = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
final TableGroup existingJoinedGroup = fromClauseIndex.findTableGroup( fetchablePath );
|
final TableGroup existingJoinedGroup = fromClauseIndex.findTableGroup( fetchablePath );
|
||||||
if ( existingJoinedGroup != null ) {
|
if ( existingJoinedGroup != null ) {
|
||||||
// we can use this to trigger the fetch from the joined group.
|
// we can use this to trigger the fetch from the joined group.
|
||||||
|
|
||||||
// todo (6.0) : do we want to do this though?
|
// todo (6.0) : do we want to do this though?
|
||||||
// On the positive side it would allow EntityGraph to use the existing TableGroup. But that ties in
|
// On the positive side it would allow EntityGraph to use the existing TableGroup. But that ties in
|
||||||
// to the discussion above regarding how to handle eager and EntityGraph (JOIN versus SELECT).
|
// to the discussion above regarding how to handle eager and EntityGraph (JOIN versus SELECT).
|
||||||
// Can be problematic if the existing one is restricted
|
// Can be problematic if the existing one is restricted
|
||||||
//fetchTiming = FetchTiming.IMMEDIATE;
|
//fetchTiming = FetchTiming.IMMEDIATE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// lastly, account for any app-defined max-fetch-depth
|
// lastly, account for any app-defined max-fetch-depth
|
||||||
final Integer maxDepth = getCreationContext().getMaximumFetchDepth();
|
final Integer maxDepth = getCreationContext().getMaximumFetchDepth();
|
||||||
if ( maxDepth != null ) {
|
if ( maxDepth != null ) {
|
||||||
if ( fetchDepth >= maxDepth ) {
|
if ( fetchDepth >= maxDepth ) {
|
||||||
joined = false;
|
joined = false;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( joined && fetchable instanceof TableGroupJoinProducer ) {
|
|
||||||
TableGroupJoinProducer tableGroupJoinProducer = (TableGroupJoinProducer) fetchable;
|
|
||||||
fromClauseIndex.resolveTableGroup(
|
|
||||||
fetchablePath,
|
|
||||||
np -> {
|
|
||||||
// generate the join
|
|
||||||
final TableGroup lhs = fromClauseIndex.getTableGroup( fetchParent.getNavigablePath() );
|
|
||||||
final TableGroupJoin tableGroupJoin = ( (TableGroupJoinProducer) fetchable ).createTableGroupJoin(
|
|
||||||
fetchablePath,
|
|
||||||
lhs,
|
|
||||||
alias,
|
|
||||||
tableGroupJoinProducer.getDefaultSqlAstJoinType( lhs ),
|
|
||||||
true,
|
|
||||||
false,
|
|
||||||
this
|
|
||||||
);
|
|
||||||
lhs.addTableGroupJoin( tableGroupJoin );
|
|
||||||
return tableGroupJoin.getJoinedGroup();
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final boolean incrementFetchDepth = fetchable.incrementFetchDepth();
|
if ( joined && fetchable instanceof TableGroupJoinProducer ) {
|
||||||
try {
|
TableGroupJoinProducer tableGroupJoinProducer = (TableGroupJoinProducer) fetchable;
|
||||||
if ( incrementFetchDepth ) {
|
fromClauseIndex.resolveTableGroup(
|
||||||
fetchDepth++;
|
fetchablePath,
|
||||||
}
|
np -> {
|
||||||
// There is no need to check for circular fetches if this is an explicit fetch
|
// generate the join
|
||||||
if ( !explicitFetch && !isResolvingCircularFetch() ) {
|
final TableGroup lhs = fromClauseIndex.getTableGroup( fetchParent.getNavigablePath() );
|
||||||
final Fetch biDirectionalFetch = fetchable.resolveCircularFetch(
|
final TableGroupJoin tableGroupJoin = ( (TableGroupJoinProducer) fetchable ).createTableGroupJoin(
|
||||||
fetchablePath,
|
fetchablePath,
|
||||||
fetchParent,
|
lhs,
|
||||||
fetchTiming,
|
alias,
|
||||||
this
|
tableGroupJoinProducer.getDefaultSqlAstJoinType( lhs ),
|
||||||
);
|
true,
|
||||||
|
false,
|
||||||
if ( biDirectionalFetch != null ) {
|
BaseSqmToSqlAstConverter.this
|
||||||
fetches.add( biDirectionalFetch );
|
);
|
||||||
return;
|
lhs.addTableGroupJoin( tableGroupJoin );
|
||||||
}
|
return tableGroupJoin.getJoinedGroup();
|
||||||
}
|
|
||||||
final Fetch fetch = buildFetch( fetchablePath, fetchParent, fetchable, fetchTiming, joined, alias );
|
|
||||||
|
|
||||||
if ( fetch != null ) {
|
|
||||||
if ( fetch.getTiming() == FetchTiming.IMMEDIATE && fetchable instanceof PluralAttributeMapping ) {
|
|
||||||
final PluralAttributeMapping pluralAttributeMapping = (PluralAttributeMapping) fetchable;
|
|
||||||
final CollectionClassification collectionClassification = pluralAttributeMapping.getMappedType()
|
|
||||||
.getCollectionSemantics()
|
|
||||||
.getCollectionClassification();
|
|
||||||
if ( collectionClassification == CollectionClassification.BAG ) {
|
|
||||||
bagRoles.add( fetchable.getNavigableRole().getNavigableName() );
|
|
||||||
}
|
}
|
||||||
}
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fetches.add( fetch );
|
final boolean incrementFetchDepth = fetchable.incrementFetchDepth();
|
||||||
|
try {
|
||||||
|
if ( incrementFetchDepth ) {
|
||||||
|
fetchDepth++;
|
||||||
|
}
|
||||||
|
// There is no need to check for circular fetches if this is an explicit fetch
|
||||||
|
if ( !explicitFetch && !isResolvingCircularFetch() ) {
|
||||||
|
final Fetch biDirectionalFetch = fetchable.resolveCircularFetch(
|
||||||
|
fetchablePath,
|
||||||
|
fetchParent,
|
||||||
|
fetchTiming,
|
||||||
|
this
|
||||||
|
);
|
||||||
|
|
||||||
|
if ( biDirectionalFetch != null ) {
|
||||||
|
fetches.add( biDirectionalFetch );
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
finally {
|
final Fetch fetch = buildFetch(
|
||||||
if ( incrementFetchDepth ) {
|
fetchablePath,
|
||||||
fetchDepth--;
|
fetchParent,
|
||||||
}
|
fetchable,
|
||||||
if ( entityGraphTraversalState != null && traversalResult != null ) {
|
fetchTiming,
|
||||||
entityGraphTraversalState.backtrack( traversalResult.getPreviousContext() );
|
joined,
|
||||||
|
alias
|
||||||
|
);
|
||||||
|
|
||||||
|
if ( fetch != null ) {
|
||||||
|
if ( fetch.getTiming() == FetchTiming.IMMEDIATE && fetchable instanceof PluralAttributeMapping ) {
|
||||||
|
final PluralAttributeMapping pluralAttributeMapping = (PluralAttributeMapping) fetchable;
|
||||||
|
final CollectionClassification collectionClassification = pluralAttributeMapping.getMappedType()
|
||||||
|
.getCollectionSemantics()
|
||||||
|
.getCollectionClassification();
|
||||||
|
if ( collectionClassification == CollectionClassification.BAG ) {
|
||||||
|
if ( currentBagRole != null ) {
|
||||||
|
throw new MultipleBagFetchException(
|
||||||
|
Arrays.asList(
|
||||||
|
currentBagRole,
|
||||||
|
fetchable.getNavigableRole().getNavigableName()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
currentBagRole = fetchable.getNavigableRole().getNavigableName();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
fetches.add( fetch );
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
finally {
|
||||||
|
if ( incrementFetchDepth ) {
|
||||||
|
fetchDepth--;
|
||||||
|
}
|
||||||
|
if ( entityGraphTraversalState != null && traversalResult != null ) {
|
||||||
|
entityGraphTraversalState.backtrack( traversalResult.getPreviousContext() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Fetch> visitFetches(FetchParent fetchParent) {
|
||||||
|
final List<Fetch> fetches = CollectionHelper.arrayList( fetchParent.getReferencedMappingType().getNumberOfFetchables() );
|
||||||
|
|
||||||
// todo (6.0) : determine how to best handle TREAT
|
// todo (6.0) : determine how to best handle TREAT
|
||||||
// fetchParent.getReferencedMappingContainer().visitKeyFetchables( fetchableBiConsumer, treatTargetType );
|
// fetchParent.getReferencedMappingContainer().visitKeyFetchables( fetchableBiConsumer, treatTargetType );
|
||||||
// fetchParent.getReferencedMappingContainer().visitFetchables( fetchableBiConsumer, treatTargetType );
|
// fetchParent.getReferencedMappingContainer().visitFetchables( fetchableBiConsumer, treatTargetType );
|
||||||
fetchParent.getReferencedMappingContainer().visitKeyFetchables( fetchable -> fetchableBiConsumer.accept( fetchable, true ), null );
|
fetchParent.getReferencedMappingContainer().visitKeyFetchables( fetchable -> addFetch( fetches, fetchParent, fetchable, true ), null );
|
||||||
fetchParent.getReferencedMappingContainer().visitFetchables( fetchable -> fetchableBiConsumer.accept( fetchable, false ), null );
|
fetchParent.getReferencedMappingContainer().visitFetchables( fetchable -> addFetch( fetches, fetchParent, fetchable, false ), null );
|
||||||
if ( bagRoles.size() > 1 ) {
|
|
||||||
throw new MultipleBagFetchException( bagRoles );
|
|
||||||
}
|
|
||||||
return fetches;
|
return fetches;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5400,16 +5411,9 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( orderByFragmentConsumer != null ) {
|
if ( currentQuerySpec().isRoot() ) {
|
||||||
assert tableGroup.getModelPart() == pluralAttributeMapping;
|
assert tableGroup.getModelPart() == pluralAttributeMapping;
|
||||||
|
applyOrdering( tableGroup, pluralAttributeMapping );
|
||||||
if ( pluralAttributeMapping.getOrderByFragment() != null ) {
|
|
||||||
orderByFragmentConsumer.accept( pluralAttributeMapping.getOrderByFragment(), tableGroup );
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( pluralAttributeMapping.getManyToManyOrderByFragment() != null ) {
|
|
||||||
orderByFragmentConsumer.accept( pluralAttributeMapping.getManyToManyOrderByFragment(), tableGroup );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5428,6 +5432,23 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void applyOrdering(TableGroup tableGroup, PluralAttributeMapping pluralAttributeMapping) {
|
||||||
|
if ( pluralAttributeMapping.getOrderByFragment() != null ) {
|
||||||
|
applyOrdering( tableGroup, pluralAttributeMapping.getOrderByFragment() );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( pluralAttributeMapping.getManyToManyOrderByFragment() != null ) {
|
||||||
|
applyOrdering( tableGroup, pluralAttributeMapping.getManyToManyOrderByFragment() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void applyOrdering(TableGroup tableGroup, OrderByFragment orderByFragment) {
|
||||||
|
if ( orderByFragments == null ) {
|
||||||
|
orderByFragments = new ArrayList<>();
|
||||||
|
}
|
||||||
|
orderByFragments.add( new AbstractMap.SimpleEntry<>( orderByFragment, tableGroup ) );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isResolvingCircularFetch() {
|
public boolean isResolvingCircularFetch() {
|
||||||
return resolvingCircularFetch;
|
return resolvingCircularFetch;
|
||||||
|
|
|
@ -7,7 +7,9 @@
|
||||||
package org.hibernate.query.sqm.sql.internal;
|
package org.hibernate.query.sqm.sql.internal;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||||
import org.hibernate.metamodel.mapping.BasicEntityIdentifierMapping;
|
import org.hibernate.metamodel.mapping.BasicEntityIdentifierMapping;
|
||||||
|
@ -31,6 +33,7 @@ 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.from.TableGroup;
|
import org.hibernate.sql.ast.tree.from.TableGroup;
|
||||||
import org.hibernate.sql.ast.tree.from.TableReference;
|
import org.hibernate.sql.ast.tree.from.TableReference;
|
||||||
|
import org.hibernate.sql.ast.tree.update.Assignable;
|
||||||
import org.hibernate.sql.results.graph.DomainResultCreationState;
|
import org.hibernate.sql.results.graph.DomainResultCreationState;
|
||||||
|
|
||||||
import static org.hibernate.sql.ast.spi.SqlExpressionResolver.createColumnReferenceKey;
|
import static org.hibernate.sql.ast.spi.SqlExpressionResolver.createColumnReferenceKey;
|
||||||
|
@ -38,7 +41,8 @@ import static org.hibernate.sql.ast.spi.SqlExpressionResolver.createColumnRefere
|
||||||
/**
|
/**
|
||||||
* @author Koen Aers
|
* @author Koen Aers
|
||||||
*/
|
*/
|
||||||
public class EntityValuedPathInterpretation<T> extends AbstractSqmPathInterpretation<T> implements SqlTupleContainer {
|
public class EntityValuedPathInterpretation<T> extends AbstractSqmPathInterpretation<T> implements SqlTupleContainer,
|
||||||
|
Assignable {
|
||||||
|
|
||||||
public static <T> EntityValuedPathInterpretation<T> from(
|
public static <T> EntityValuedPathInterpretation<T> from(
|
||||||
SqmEntityValuedSimplePath<T> sqmPath,
|
SqmEntityValuedSimplePath<T> sqmPath,
|
||||||
|
@ -232,12 +236,34 @@ public class EntityValuedPathInterpretation<T> extends AbstractSqmPathInterpreta
|
||||||
sqlExpression.accept( sqlTreeWalker );
|
sqlExpression.accept( sqlTreeWalker );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<ColumnReference> getColumnReferences() {
|
||||||
|
if ( sqlExpression instanceof SqlTuple ) {
|
||||||
|
//noinspection unchecked
|
||||||
|
return (List<ColumnReference>) ( (SqlTuple) sqlExpression ).getExpressions();
|
||||||
|
}
|
||||||
|
return Collections.singletonList( (ColumnReference) sqlExpression );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visitColumnReferences(Consumer<ColumnReference> columnReferenceConsumer) {
|
||||||
|
if ( sqlExpression instanceof SqlTuple ) {
|
||||||
|
for ( Expression e : ( (SqlTuple) sqlExpression ).getExpressions() ) {
|
||||||
|
columnReferenceConsumer.accept( (ColumnReference) e );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
columnReferenceConsumer.accept( (ColumnReference) sqlExpression );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SqlTuple getSqlTuple() {
|
public SqlTuple getSqlTuple() {
|
||||||
return sqlExpression instanceof SqlTuple
|
return sqlExpression instanceof SqlTuple
|
||||||
? (SqlTuple) sqlExpression
|
? (SqlTuple) sqlExpression
|
||||||
: null;
|
: null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void applySqlSelections(DomainResultCreationState creationState) {
|
public void applySqlSelections(DomainResultCreationState creationState) {
|
||||||
creationState.getSqlAstCreationState().getSqlExpressionResolver().resolveSqlSelection(
|
creationState.getSqlAstCreationState().getSqlExpressionResolver().resolveSqlSelection(
|
||||||
|
|
|
@ -53,7 +53,7 @@ public abstract class AbstractSqmAttributeJoin<O,T>
|
||||||
lhs,
|
lhs,
|
||||||
SqmCreationHelper.buildSubNavigablePath( lhs, joinedNavigable.getName(), alias ),
|
SqmCreationHelper.buildSubNavigablePath( lhs, joinedNavigable.getName(), alias ),
|
||||||
joinedNavigable,
|
joinedNavigable,
|
||||||
alias,
|
alias == SqmCreationHelper.IMPLICIT_ALIAS ? null : alias,
|
||||||
joinType,
|
joinType,
|
||||||
fetched,
|
fetched,
|
||||||
nodeBuilder
|
nodeBuilder
|
||||||
|
|
|
@ -24,6 +24,7 @@ import jakarta.persistence.metamodel.PluralAttribute;
|
||||||
import jakarta.persistence.metamodel.SetAttribute;
|
import jakarta.persistence.metamodel.SetAttribute;
|
||||||
import jakarta.persistence.metamodel.SingularAttribute;
|
import jakarta.persistence.metamodel.SingularAttribute;
|
||||||
|
|
||||||
|
import org.hibernate.metamodel.mapping.ModelPartContainer;
|
||||||
import org.hibernate.metamodel.model.domain.BagPersistentAttribute;
|
import org.hibernate.metamodel.model.domain.BagPersistentAttribute;
|
||||||
import org.hibernate.metamodel.model.domain.EntityDomainType;
|
import org.hibernate.metamodel.model.domain.EntityDomainType;
|
||||||
import org.hibernate.metamodel.model.domain.ListPersistentAttribute;
|
import org.hibernate.metamodel.model.domain.ListPersistentAttribute;
|
||||||
|
@ -44,6 +45,7 @@ import org.hibernate.query.hql.spi.SqmCreationState;
|
||||||
import org.hibernate.query.sqm.spi.SqmCreationHelper;
|
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.from.SqmAttributeJoin;
|
import org.hibernate.query.sqm.tree.from.SqmAttributeJoin;
|
||||||
|
import org.hibernate.query.sqm.tree.from.SqmCrossJoin;
|
||||||
import org.hibernate.query.sqm.tree.from.SqmEntityJoin;
|
import org.hibernate.query.sqm.tree.from.SqmEntityJoin;
|
||||||
import org.hibernate.query.sqm.tree.from.SqmFrom;
|
import org.hibernate.query.sqm.tree.from.SqmFrom;
|
||||||
import org.hibernate.query.sqm.tree.from.SqmJoin;
|
import org.hibernate.query.sqm.tree.from.SqmJoin;
|
||||||
|
@ -129,18 +131,84 @@ public abstract class AbstractSqmFrom<O,T> extends AbstractSqmPath<T> implements
|
||||||
String name,
|
String name,
|
||||||
boolean isTerminal,
|
boolean isTerminal,
|
||||||
SqmCreationState creationState) {
|
SqmCreationState creationState) {
|
||||||
final NavigablePath subNavPath = getNavigablePath().append( name );
|
// Try to resolve an existing attribute join without ON clause
|
||||||
return creationState.getProcessingStateStack().getCurrent().getPathRegistry().resolvePath(
|
SqmPath<?> resolvedPath = null;
|
||||||
subNavPath,
|
ModelPartContainer modelPartContainer = null;
|
||||||
snp -> {
|
for ( SqmJoin<?, ?> sqmJoin : getSqmJoins() ) {
|
||||||
final SqmPathSource<?> subSource = getReferencedPathSource().findSubPathSource( name );
|
if ( sqmJoin instanceof SqmAttributeJoin<?, ?>
|
||||||
if ( subSource == null ) {
|
&& name.equals( sqmJoin.getReferencedPathSource().getPathName() ) ) {
|
||||||
throw UnknownPathException.unknownSubPath( this, name );
|
final SqmAttributeJoin<?, ?> attributeJoin = (SqmAttributeJoin<?, ?>) sqmJoin;
|
||||||
|
if ( attributeJoin.getOn() == null ) {
|
||||||
|
// todo (6.0): to match the expectation of the JPA spec I think we also have to check
|
||||||
|
// that the join type is INNER or the default join type for the attribute,
|
||||||
|
// but as far as I understand, in 5.x we expect to ignore this behavior
|
||||||
|
// if ( attributeJoin.getSqmJoinType() != SqmJoinType.INNER ) {
|
||||||
|
// if ( attributeJoin.getAttribute().isCollection() ) {
|
||||||
|
// continue;
|
||||||
|
// }
|
||||||
|
// if ( modelPartContainer == null ) {
|
||||||
|
// modelPartContainer = findModelPartContainer( attributeJoin, creationState );
|
||||||
|
// }
|
||||||
|
// final TableGroupJoinProducer joinProducer = (TableGroupJoinProducer) modelPartContainer.findSubPart(
|
||||||
|
// name,
|
||||||
|
// null
|
||||||
|
// );
|
||||||
|
// if ( attributeJoin.getSqmJoinType().getCorrespondingSqlJoinType() != joinProducer.getDefaultSqlAstJoinType( null ) ) {
|
||||||
|
// continue;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
resolvedPath = sqmJoin;
|
||||||
|
if ( attributeJoin.isFetched() ) {
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return subSource.createSqmPath( this, getReferencedPathSource().getIntermediatePathSource( subSource ) );
|
|
||||||
}
|
}
|
||||||
);
|
}
|
||||||
|
}
|
||||||
|
if ( resolvedPath != null ) {
|
||||||
|
return resolvedPath;
|
||||||
|
}
|
||||||
|
final SqmPath<?> sqmPath = get( name );
|
||||||
|
creationState.getProcessingStateStack().getCurrent().getPathRegistry().register( sqmPath );
|
||||||
|
return sqmPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ModelPartContainer findModelPartContainer(SqmAttributeJoin<?, ?> attributeJoin, SqmCreationState creationState) {
|
||||||
|
final SqmFrom<?, ?> lhs = attributeJoin.getLhs();
|
||||||
|
if ( lhs instanceof SqmAttributeJoin<?, ?> ) {
|
||||||
|
final SqmAttributeJoin<?, ?> lhsAttributeJoin = (SqmAttributeJoin<?, ?>) lhs;
|
||||||
|
if ( lhsAttributeJoin.getReferencedPathSource() instanceof EntityDomainType<?> ) {
|
||||||
|
final String entityName = ( (EntityDomainType<?>) lhsAttributeJoin.getReferencedPathSource() ).getHibernateEntityName();
|
||||||
|
return (ModelPartContainer) creationState.getCreationContext().getQueryEngine()
|
||||||
|
.getTypeConfiguration()
|
||||||
|
.getSessionFactory()
|
||||||
|
.getMetamodel()
|
||||||
|
.entityPersister( entityName )
|
||||||
|
.findSubPart( attributeJoin.getAttribute().getName(), null );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return (ModelPartContainer) findModelPartContainer( lhsAttributeJoin, creationState )
|
||||||
|
.findSubPart( attributeJoin.getAttribute().getName(), null );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
final String entityName;
|
||||||
|
if ( lhs instanceof SqmRoot<?> ) {
|
||||||
|
entityName = ( (SqmRoot<?>) lhs ).getEntityName();
|
||||||
|
}
|
||||||
|
else if ( lhs instanceof SqmEntityJoin<?> ) {
|
||||||
|
entityName = ( (SqmEntityJoin<?>) lhs ).getEntityName();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
assert lhs instanceof SqmCrossJoin<?>;
|
||||||
|
entityName = ( (SqmCrossJoin<?>) lhs ).getEntityName();
|
||||||
|
}
|
||||||
|
return (ModelPartContainer) creationState.getCreationContext().getQueryEngine()
|
||||||
|
.getTypeConfiguration()
|
||||||
|
.getSessionFactory()
|
||||||
|
.getMetamodel()
|
||||||
|
.entityPersister( entityName )
|
||||||
|
.findSubPart( attributeJoin.getAttribute().getName(), null );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue