Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 20 additions & 3 deletions hibernate-core/src/main/antlr/hql-sql.g
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,15 @@ tokens
return dot;
}

protected AST lookupFkRefSource(AST path) throws SemanticException {
if ( path.getType() == DOT ) {
return lookupProperty( path, true, isInSelect() );
}
else {
return lookupNonQualifiedProperty( path );
}
}

protected boolean isNonQualifiedPropertyRef(AST ident) { return false; }

protected AST lookupNonQualifiedProperty(AST property) throws SemanticException { return property; }
Expand Down Expand Up @@ -713,12 +722,15 @@ identifier
;

addrExpr! [ boolean root ]
: #(d:DOT lhs:addrExprLhs rhs:propertyName ) {
: #(d:DOT lhs:addrExprLhs rhs:propertyName ) {
// This gives lookupProperty() a chance to transform the tree
// to process collection properties (.elements, etc).
#addrExpr = #(#d, #lhs, #rhs);
#addrExpr = lookupProperty(#addrExpr,root,false);
}
| fk_ref:fkRef {
#addrExpr = #fk_ref;
}
| #(i:INDEX_OP lhs2:addrExprLhs rhs2:expr [ null ]) {
#addrExpr = #(#i, #lhs2, #rhs2);
processIndex(#addrExpr);
Expand All @@ -743,6 +755,12 @@ addrExpr! [ boolean root ]
}
;

fkRef
: #( r:FK_REF p:propertyRef ) {
#p = lookupProperty( #p, false, isInSelect() );
}
;

addrExprLhs
: addrExpr [ false ]
;
Expand All @@ -764,8 +782,7 @@ propertyRef!
#propertyRef = #(#d, #lhs, #rhs);
#propertyRef = lookupProperty(#propertyRef,false,true);
}
|
p:identifier {
| p:identifier {
// In many cases, things other than property-refs are recognized
// by this propertyRef rule. Some of those I have seen:
// 1) select-clause from-aliases
Expand Down
10 changes: 9 additions & 1 deletion hibernate-core/src/main/antlr/hql.g
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ tokens
EXISTS="exists";
FALSE="false";
FETCH="fetch";
FK_REF;
FROM="from";
FULL="full";
GROUP="group";
Expand Down Expand Up @@ -721,14 +722,21 @@ atom

// level 0 - the basic element of an expression
primaryExpression
: { validateSoftKeyword("function") && LA(2) == OPEN && LA(3) == QUOTED_STRING }? jpaFunctionSyntax
: { validateSoftKeyword("fk") && LA(2) == OPEN }? fkRefPath
| { validateSoftKeyword("function") && LA(2) == OPEN && LA(3) == QUOTED_STRING }? jpaFunctionSyntax
| { validateSoftKeyword("cast") && LA(2) == OPEN }? castFunction
| identPrimary ( options {greedy=true;} : DOT^ "class" )?
| constant
| parameter
| OPEN! (expressionOrVector | subQuery) CLOSE!
;

fkRefPath!
: "fk" OPEN p:identPrimary CLOSE {
#fkRefPath = #( [FK_REF], #p );
}
;

jpaFunctionSyntax!
: i:IDENT OPEN n:QUOTED_STRING (COMMA a:exprList)? CLOSE {
final String functionName = unquote( #n.getText() );
Expand Down
1 change: 1 addition & 0 deletions hibernate-core/src/main/antlr/sql-gen.g
Original file line number Diff line number Diff line change
Expand Up @@ -500,6 +500,7 @@ parameter

addrExpr
: #(r:DOT . .) { out(r); }
| #(fk:FK_REF .) { out(fk); }
| i:ALIAS_REF { out(i); }
| j:INDEX_OP { out(j); }
| v:RESULT_VARIABLE_REF { out(v); }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import org.hibernate.hql.internal.ast.tree.BinaryLogicOperatorNode;
import org.hibernate.hql.internal.ast.tree.BooleanLiteralNode;
import org.hibernate.hql.internal.ast.tree.CastFunctionNode;
import org.hibernate.hql.internal.ast.tree.FkRefNode;
import org.hibernate.hql.internal.ast.tree.NullNode;
import org.hibernate.hql.internal.ast.tree.SearchedCaseNode;
import org.hibernate.hql.internal.ast.tree.SimpleCaseNode;
Expand Down Expand Up @@ -196,6 +197,9 @@ public Class getASTNodeType(int tokenType) {
case NULL : {
return NullNode.class;
}
case FK_REF: {
return FkRefNode.class;
}
default:
return SqlNode.class;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import org.hibernate.QueryException;
import org.hibernate.engine.internal.JoinSequence;
import org.hibernate.hql.internal.CollectionProperties;
import org.hibernate.hql.internal.antlr.HqlSqlTokenTypes;
import org.hibernate.hql.internal.antlr.SqlTokenTypes;
import org.hibernate.hql.internal.ast.util.ASTUtil;
import org.hibernate.hql.internal.ast.util.ColumnHelper;
Expand Down Expand Up @@ -260,7 +261,7 @@ private void initText() {
}

private Type prepareLhs() throws SemanticException {
FromReferenceNode lhs = getLhs();
final FromReferenceNode lhs = getLhs();
lhs.prepareForDot( propertyName );
return getDataType();
}
Expand Down Expand Up @@ -390,10 +391,11 @@ private void dereferenceEntity(
final boolean joinIsNeeded;

if ( isDotNode( parent ) ) {
// our parent is another dot node, meaning we are being further dereferenced.
// thus we need to generate a join unless the association is non-nullable and
// parent refers to the associated entity's PK (because 'our' table would know the FK).
parentAsDotNode = (DotNode) parent;

// our parent is another dot node, meaning we are being further de-referenced.
// depending on the exact de-reference we may need to generate a physical join.

property = parentAsDotNode.propertyName;
joinIsNeeded = generateJoin && (
entityType.isNullable() ||
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
/*
* 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.hql.internal.ast.tree;

import org.hibernate.QueryException;
import org.hibernate.hql.internal.ast.InvalidPathException;
import org.hibernate.type.BasicType;
import org.hibernate.type.CompositeType;
import org.hibernate.type.ManyToOneType;
import org.hibernate.type.Type;

import antlr.SemanticException;
import antlr.collections.AST;

/**
* Represents a `fk()` pseudo-function
*
* @author Steve Ebersole
*/
public class FkRefNode
extends HqlSqlWalkerNode
implements ResolvableNode, DisplayableNode, PathNode {
private FromReferenceNode toOnePath;

private Type fkType;
private String[] columns;

private FromReferenceNode resolveToOnePath() {
if ( toOnePath == null ) {
try {
resolve( false, true );
}
catch (SemanticException e) {
final String msg = "Unable to resolve to-one path `fk(" + toOnePath.getPath() + "`)";
throw new QueryException( msg, new InvalidPathException( msg ) );
}
}

assert toOnePath != null;
return toOnePath;
}

@Override
public String getDisplayText() {
final FromReferenceNode toOnePath = resolveToOnePath();
return "fk(`" + toOnePath.getDisplayText() + "` )";
}

@Override
public String getPath() {
return toOnePath.getDisplayText() + ".{fk}";
}

@Override
public void resolve(
boolean generateJoin,
boolean implicitJoin) throws SemanticException {
if ( toOnePath != null ) {
return;
}

final AST firstChild = getFirstChild();
assert firstChild instanceof FromReferenceNode;

toOnePath = (FromReferenceNode) firstChild;
toOnePath.resolve( false, true, null, toOnePath.getFromElement() );

final Type sourcePathDataType = toOnePath.getDataType();
if ( ! ( sourcePathDataType instanceof ManyToOneType ) ) {
throw new InvalidPathException(
"Argument to fk() function must be a to-one path, but found " + sourcePathDataType
);
}
final ManyToOneType toOneType = (ManyToOneType) sourcePathDataType;
final FromElement fromElement = toOnePath.getFromElement();

fkType = toOneType.getIdentifierOrUniqueKeyType( getSessionFactoryHelper().getFactory() );
assert fkType instanceof BasicType
|| fkType instanceof CompositeType;

columns = fromElement.toColumns(
fromElement.getTableAlias(),
toOneType.getPropertyName(),
getWalker().isInSelect()
);
assert columns != null && columns.length > 0;

setText( String.join( ", ", columns ) );
}

@Override
public void resolve(
boolean generateJoin,
boolean implicitJoin,
String classAlias,
AST parent,
AST parentPredicate) throws SemanticException {
resolve( false, true );
}

@Override
public void resolve(
boolean generateJoin,
boolean implicitJoin,
String classAlias,
AST parent) throws SemanticException {
resolve( false, true );
}

@Override
public void resolve(
boolean generateJoin,
boolean implicitJoin,
String classAlias) throws SemanticException {
resolve( false, true );
}

@Override
public void resolveInFunctionCall(
boolean generateJoin,
boolean implicitJoin) throws SemanticException {
resolve( false, true );
}

@Override
public void resolveIndex(AST parent) throws SemanticException {
throw new InvalidPathException( "fk() paths cannot be de-referenced as indexed path" );
}
}
Loading