HHH-7374 - Support KEY, ENTRY and VALUE qualifiers in WHERE clause
This commit is contained in:
parent
d971317e1b
commit
67c5000885
|
@ -652,6 +652,9 @@ addrExpr! [ boolean root ]
|
|||
#addrExpr = #(#i, #lhs2, #rhs2);
|
||||
processIndex(#addrExpr);
|
||||
}
|
||||
| mcr:mapComponentReference {
|
||||
#addrExpr = #mcr;
|
||||
}
|
||||
| p:identifier {
|
||||
// #addrExpr = #p;
|
||||
// resolve(#addrExpr);
|
||||
|
|
|
@ -645,13 +645,13 @@ identPrimary
|
|||
( options { greedy=true; } :
|
||||
( op:OPEN^ { #op.setType(METHOD_CALL);} e:exprList CLOSE! ) {
|
||||
AST path = #e.getFirstChild();
|
||||
if ( #i.getText().equals( "key" ) ) {
|
||||
if ( #i.getText().equalsIgnoreCase( "key" ) ) {
|
||||
#identPrimary = #( [KEY], path );
|
||||
}
|
||||
else if ( #i.getText().equals( "value" ) ) {
|
||||
else if ( #i.getText().equalsIgnoreCase( "value" ) ) {
|
||||
#identPrimary = #( [VALUE], path );
|
||||
}
|
||||
else if ( #i.getText().equals( "entry" ) ) {
|
||||
else if ( #i.getText().equalsIgnoreCase( "entry" ) ) {
|
||||
#identPrimary = #( [ENTRY], path );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -436,6 +436,7 @@ addrExpr
|
|||
| i:ALIAS_REF { out(i); }
|
||||
| j:INDEX_OP { out(j); }
|
||||
| v:RESULT_VARIABLE_REF { out(v); }
|
||||
| mcr:mapComponentReference { out(mcr); }
|
||||
;
|
||||
|
||||
sqlToken
|
||||
|
|
|
@ -28,6 +28,7 @@ import java.util.Map;
|
|||
import antlr.SemanticException;
|
||||
import antlr.collections.AST;
|
||||
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.hql.internal.antlr.HqlSqlTokenTypes;
|
||||
import org.hibernate.hql.internal.ast.util.ColumnHelper;
|
||||
import org.hibernate.internal.util.StringHelper;
|
||||
|
|
|
@ -210,7 +210,7 @@ class FromElementType {
|
|||
getTableAlias(),
|
||||
getSuffix( size, k ),
|
||||
allProperties
|
||||
);
|
||||
);
|
||||
return trimLeadingCommaAndSpaces( fragment );
|
||||
}
|
||||
}
|
||||
|
@ -327,15 +327,17 @@ class FromElementType {
|
|||
String[] toColumns(String tableAlias, String path, boolean inSelect, boolean forceAlias) {
|
||||
checkInitialized();
|
||||
PropertyMapping propertyMapping = getPropertyMapping( path );
|
||||
// If this from element is a collection and the path is a collection property (maxIndex, etc.) then
|
||||
// generate a sub-query.
|
||||
//
|
||||
// NOTE : in the case of this being a collection property in the select, not generating the subquery
|
||||
// will not generally work. The specific cases I am thinking about are the minIndex, maxIndex
|
||||
// (most likely minElement, maxElement as well) cases.
|
||||
// todo : if ^^ is the case we should thrown an exception here rather than waiting for the sql error
|
||||
// if the dialect supports select-clause subqueries we could go ahead and generate the subquery also
|
||||
|
||||
if ( !inSelect && queryableCollection != null && CollectionProperties.isCollectionProperty( path ) ) {
|
||||
// If this from element is a collection and the path is a collection property (maxIndex, etc.)
|
||||
// requiring a sub-query then generate a sub-query.
|
||||
//h
|
||||
// Unless we are in the select clause, because some dialects do not support
|
||||
// Note however, that some dialects do not However, in the case of this being a collection property reference being in the select, not generating the subquery
|
||||
// will not generally work. The specific cases I am thinking about are the minIndex, maxIndex
|
||||
// (most likely minElement, maxElement as well) cases.
|
||||
// todo : if ^^ is the case we should thrown an exception here rather than waiting for the sql error
|
||||
// if the dialect supports select-clause subqueries we could go ahead and generate the subquery also
|
||||
Map enabledFilters = fromElement.getWalker().getEnabledFilters();
|
||||
String subquery = CollectionSubqueryFactory.createCollectionSubquery(
|
||||
joinSequence.copy().setUseThetaStyle( true ),
|
||||
|
|
|
@ -39,7 +39,12 @@ public class MapKeyNode extends AbstractMapComponentNode {
|
|||
|
||||
@Override
|
||||
protected String[] resolveColumns(QueryableCollection collectionPersister) {
|
||||
return collectionPersister.getIndexColumnNames();
|
||||
final FromElement fromElement = getFromElement();
|
||||
return fromElement.toColumns(
|
||||
fromElement.getCollectionTableAlias(),
|
||||
"index", // the JPA KEY "qualifier" is the same concept as the HQL INDEX function/property
|
||||
getWalker().isInSelect()
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -39,7 +39,12 @@ public class MapValueNode extends AbstractMapComponentNode {
|
|||
|
||||
@Override
|
||||
protected String[] resolveColumns(QueryableCollection collectionPersister) {
|
||||
return collectionPersister.getElementColumnNames();
|
||||
final FromElement fromElement = getFromElement();
|
||||
return fromElement.toColumns(
|
||||
fromElement.getCollectionTableAlias(),
|
||||
"elements", // the JPA VALUE "qualifier" is the same concept as the HQL ELEMENTS function/property
|
||||
getWalker().isInSelect()
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -250,7 +250,7 @@ public class ASTParserLoadingTest extends BaseCoreFunctionalTestCase {
|
|||
|
||||
@Test
|
||||
@SuppressWarnings( {"unchecked"})
|
||||
public void testJPAQLQualifiedIdentificationVariables() {
|
||||
public void testJPAQLMapKeyQualifier() {
|
||||
Session s = openSession();
|
||||
s.beginTransaction();
|
||||
Human me = new Human();
|
||||
|
@ -264,47 +264,183 @@ public class ASTParserLoadingTest extends BaseCoreFunctionalTestCase {
|
|||
s.getTransaction().commit();
|
||||
s.close();
|
||||
|
||||
s = openSession();
|
||||
s.beginTransaction();
|
||||
List results = s.createQuery( "select entry(h.family) from Human h" ).list();
|
||||
assertEquals( 1, results.size() );
|
||||
Object result = results.get(0);
|
||||
assertTrue( Map.Entry.class.isAssignableFrom( result.getClass() ) );
|
||||
Map.Entry entry = (Map.Entry) result;
|
||||
assertTrue( String.class.isAssignableFrom( entry.getKey().getClass() ) );
|
||||
assertTrue( Human.class.isAssignableFrom( entry.getValue().getClass() ) );
|
||||
s.getTransaction().commit();
|
||||
s.close();
|
||||
// in SELECT clause
|
||||
{
|
||||
// hibernate-only form
|
||||
s = openSession();
|
||||
s.beginTransaction();
|
||||
List results = s.createQuery( "select distinct key(h.family) from Human h" ).list();
|
||||
assertEquals( 1, results.size() );
|
||||
Object key = results.get(0);
|
||||
assertTrue( String.class.isAssignableFrom( key.getClass() ) );
|
||||
s.getTransaction().commit();
|
||||
s.close();
|
||||
}
|
||||
|
||||
{
|
||||
// jpa form
|
||||
s = openSession();
|
||||
s.beginTransaction();
|
||||
List results = s.createQuery( "select distinct KEY(f) from Human h join h.family f" ).list();
|
||||
assertEquals( 1, results.size() );
|
||||
Object key = results.get(0);
|
||||
assertTrue( String.class.isAssignableFrom( key.getClass() ) );
|
||||
s.getTransaction().commit();
|
||||
s.close();
|
||||
}
|
||||
|
||||
// in WHERE clause
|
||||
{
|
||||
// hibernate-only form
|
||||
s = openSession();
|
||||
s.beginTransaction();
|
||||
Long count = (Long) s.createQuery( "select count(*) from Human h where KEY(h.family) = 'son'" ).uniqueResult();
|
||||
assertEquals( (Long)1L, count );
|
||||
s.getTransaction().commit();
|
||||
s.close();
|
||||
}
|
||||
|
||||
{
|
||||
// jpa form
|
||||
s = openSession();
|
||||
s.beginTransaction();
|
||||
Long count = (Long) s.createQuery( "select count(*) from Human h join h.family f where key(f) = 'son'" ).uniqueResult();
|
||||
assertEquals( (Long)1L, count );
|
||||
s.getTransaction().commit();
|
||||
s.close();
|
||||
}
|
||||
|
||||
s = openSession();
|
||||
s.beginTransaction();
|
||||
results = s.createQuery( "select entry(f) from Human h join h.family f" ).list();
|
||||
assertEquals( 1, results.size() );
|
||||
result = results.get(0);
|
||||
assertTrue( Map.Entry.class.isAssignableFrom( result.getClass() ) );
|
||||
entry = (Map.Entry) result;
|
||||
assertTrue( String.class.isAssignableFrom( entry.getKey().getClass() ) );
|
||||
assertTrue( Human.class.isAssignableFrom( entry.getValue().getClass() ) );
|
||||
s.delete( me );
|
||||
s.delete( joe );
|
||||
s.getTransaction().commit();
|
||||
s.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings( {"unchecked"})
|
||||
public void testJPAQLMapEntryQualifier() {
|
||||
Session s = openSession();
|
||||
s.beginTransaction();
|
||||
Human me = new Human();
|
||||
me.setName( new Name( "Steve", null, "Ebersole" ) );
|
||||
Human joe = new Human();
|
||||
me.setName( new Name( "Joe", null, "Ebersole" ) );
|
||||
me.setFamily( new HashMap() );
|
||||
me.getFamily().put( "son", joe );
|
||||
s.save( me );
|
||||
s.save( joe );
|
||||
s.getTransaction().commit();
|
||||
s.close();
|
||||
|
||||
s = openSession();
|
||||
s.beginTransaction();
|
||||
results = s.createQuery( "select distinct key(h.family) from Human h" ).list();
|
||||
assertEquals( 1, results.size() );
|
||||
Object key = results.get(0);
|
||||
assertTrue( String.class.isAssignableFrom( key.getClass() ) );
|
||||
s.getTransaction().commit();
|
||||
s.close();
|
||||
// in SELECT clause
|
||||
{
|
||||
// hibernate-only form
|
||||
s = openSession();
|
||||
s.beginTransaction();
|
||||
List results = s.createQuery( "select entry(h.family) from Human h" ).list();
|
||||
assertEquals( 1, results.size() );
|
||||
Object result = results.get(0);
|
||||
assertTrue( Map.Entry.class.isAssignableFrom( result.getClass() ) );
|
||||
Map.Entry entry = (Map.Entry) result;
|
||||
assertTrue( String.class.isAssignableFrom( entry.getKey().getClass() ) );
|
||||
assertTrue( Human.class.isAssignableFrom( entry.getValue().getClass() ) );
|
||||
s.getTransaction().commit();
|
||||
s.close();
|
||||
}
|
||||
|
||||
{
|
||||
// jpa form
|
||||
s = openSession();
|
||||
s.beginTransaction();
|
||||
List results = s.createQuery( "select ENTRY(f) from Human h join h.family f" ).list();
|
||||
assertEquals( 1, results.size() );
|
||||
Object result = results.get(0);
|
||||
assertTrue( Map.Entry.class.isAssignableFrom( result.getClass() ) );
|
||||
Map.Entry entry = (Map.Entry) result;
|
||||
assertTrue( String.class.isAssignableFrom( entry.getKey().getClass() ) );
|
||||
assertTrue( Human.class.isAssignableFrom( entry.getValue().getClass() ) );
|
||||
s.getTransaction().commit();
|
||||
s.close();
|
||||
}
|
||||
|
||||
// not exactly sure of the syntax of ENTRY in the WHERE clause...
|
||||
|
||||
|
||||
s = openSession();
|
||||
s.beginTransaction();
|
||||
results = s.createQuery( "select distinct key(f) from Human h join h.family f" ).list();
|
||||
assertEquals( 1, results.size() );
|
||||
key = results.get(0);
|
||||
assertTrue( String.class.isAssignableFrom( key.getClass() ) );
|
||||
s.delete( me );
|
||||
s.delete( joe );
|
||||
s.getTransaction().commit();
|
||||
s.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings( {"unchecked"})
|
||||
public void testJPAQLMapValueQualifier() {
|
||||
Session s = openSession();
|
||||
s.beginTransaction();
|
||||
Human me = new Human();
|
||||
me.setName( new Name( "Steve", null, "Ebersole" ) );
|
||||
Human joe = new Human();
|
||||
me.setName( new Name( "Joe", null, "Ebersole" ) );
|
||||
me.setFamily( new HashMap() );
|
||||
me.getFamily().put( "son", joe );
|
||||
s.save( me );
|
||||
s.save( joe );
|
||||
s.getTransaction().commit();
|
||||
s.close();
|
||||
|
||||
// in SELECT clause
|
||||
{
|
||||
// hibernate-only form
|
||||
s = openSession();
|
||||
s.beginTransaction();
|
||||
List results = s.createQuery( "select value(h.family) from Human h" ).list();
|
||||
assertEquals( 1, results.size() );
|
||||
Object result = results.get(0);
|
||||
assertTrue( Human.class.isAssignableFrom( result.getClass() ) );
|
||||
s.getTransaction().commit();
|
||||
s.close();
|
||||
}
|
||||
|
||||
{
|
||||
// jpa form
|
||||
s = openSession();
|
||||
s.beginTransaction();
|
||||
List results = s.createQuery( "select VALUE(f) from Human h join h.family f" ).list();
|
||||
assertEquals( 1, results.size() );
|
||||
Object result = results.get(0);
|
||||
assertTrue( Human.class.isAssignableFrom( result.getClass() ) );
|
||||
s.getTransaction().commit();
|
||||
s.close();
|
||||
}
|
||||
|
||||
// in WHERE clause
|
||||
{
|
||||
// hibernate-only form
|
||||
s = openSession();
|
||||
s.beginTransaction();
|
||||
Long count = (Long) s.createQuery( "select count(*) from Human h where VALUE(h.family) = :joe" ).setParameter( "joe", joe ).uniqueResult();
|
||||
// ACTUALLY EXACTLY THE SAME AS:
|
||||
// select count(*) from Human h where h.family = :joe
|
||||
assertEquals( (Long)1L, count );
|
||||
s.getTransaction().commit();
|
||||
s.close();
|
||||
}
|
||||
|
||||
{
|
||||
// jpa form
|
||||
s = openSession();
|
||||
s.beginTransaction();
|
||||
Long count = (Long) s.createQuery( "select count(*) from Human h join h.family f where value(f) = :joe" ).setParameter( "joe", joe ).uniqueResult();
|
||||
// ACTUALLY EXACTLY THE SAME AS:
|
||||
// select count(*) from Human h join h.family f where f = :joe
|
||||
assertEquals( (Long)1L, count );
|
||||
s.getTransaction().commit();
|
||||
s.close();
|
||||
}
|
||||
|
||||
s = openSession();
|
||||
s.beginTransaction();
|
||||
|
|
|
@ -11,3 +11,5 @@ log4j.logger.org.hibernate.testing.cache=debug
|
|||
|
||||
# SQL Logging - HHH-6833
|
||||
log4j.logger.org.hibernate.SQL=debug
|
||||
|
||||
log4j.logger.org.hibernate.hql.internal.ast=debug
|
Loading…
Reference in New Issue