Skip to content

Commit 0a722bb

Browse files
findepidreab8
authored andcommitted
HHH-951 - Fix setMaxResults causes "ORA-00918: column ambiguously defined" exception on Oracle
1 parent 444c7f0 commit 0a722bb

File tree

4 files changed

+136
-2
lines changed

4 files changed

+136
-2
lines changed

hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/SelectClause.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ public class SelectClause extends SelectExpressionList {
3131
private boolean scalarSelect;
3232

3333
private List fromElementsForLoad = new ArrayList();
34+
private List alreadyRenderedIdentifiers = new ArrayList();
3435
//private Type[] sqlResultTypes;
3536
private Type[] queryReturnTypes;
3637
private String[][] columnNames;
@@ -224,6 +225,7 @@ public void initializeExplicitSelectClause(FromClause fromClause) throws Semanti
224225
//sqlResultTypeList.add( type );
225226
// Generate the select expression.
226227
String text = fromElement.renderIdentifierSelect( size, k );
228+
alreadyRenderedIdentifiers.add( text );
227229
SelectExpressionImpl generatedExpr = (SelectExpressionImpl) appender.append(
228230
SqlTokenTypes.SELECT_EXPR,
229231
text,
@@ -355,6 +357,7 @@ private void addCollectionFromElement(FromElement fromElement) {
355357
}
356358
}
357359

360+
@Override
358361
protected AST getFirstSelectExpression() {
359362
AST n = getFirstChild();
360363
// Skip 'DISTINCT' and 'ALL', so we return the first expression node.
@@ -450,7 +453,10 @@ private void renderNonScalarIdentifiers(
450453
expr.setText( text );
451454
}
452455
else {
453-
appender.append( SqlTokenTypes.SQL_TOKEN, text, false );
456+
if (! alreadyRenderedIdentifiers.contains(text)) {
457+
appender.append( SqlTokenTypes.SQL_TOKEN, text, false );
458+
alreadyRenderedIdentifiers.add(text);
459+
}
454460
}
455461
}
456462
}
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
/*
2+
* Hibernate, Relational Persistence for Idiomatic Java
3+
*
4+
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
5+
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
6+
*/
7+
package org.hibernate.test.pagination;
8+
9+
/**
10+
* @author Piotr Findeisen <[email protected]>
11+
*/
12+
public class DataMetaPoint {
13+
private long id;
14+
private DataPoint dataPoint;
15+
private String meta;
16+
17+
/**
18+
* @return Returns the id.
19+
*/
20+
public long getId() {
21+
return id;
22+
}
23+
24+
/**
25+
* @param id
26+
* The id to set.
27+
*/
28+
public void setId(long id) {
29+
this.id = id;
30+
}
31+
32+
public DataPoint getDataPoint() {
33+
return dataPoint;
34+
}
35+
36+
public void setDataPoint(DataPoint dataPoint) {
37+
this.dataPoint = dataPoint;
38+
}
39+
40+
public String getMeta() {
41+
return meta;
42+
}
43+
44+
public void setMeta(String meta) {
45+
this.meta = meta;
46+
}
47+
48+
@Override
49+
public int hashCode() {
50+
final int prime = 31;
51+
int result = 1;
52+
result = prime * result + ((dataPoint == null) ? 0 : dataPoint.hashCode());
53+
result = prime * result + (int) (id ^ (id >>> 32));
54+
result = prime * result + ((meta == null) ? 0 : meta.hashCode());
55+
return result;
56+
}
57+
58+
@Override
59+
public boolean equals(Object o) {
60+
if (this == o) {
61+
return true;
62+
}
63+
if (o == null || getClass() != o.getClass()) {
64+
return false;
65+
}
66+
67+
DataMetaPoint dataPoint = (DataMetaPoint) o;
68+
69+
if (meta != null ? !meta.equals(dataPoint.meta) : dataPoint.meta != null) {
70+
return false;
71+
}
72+
if (dataPoint != null ? !dataPoint.equals(dataPoint.dataPoint) : dataPoint.dataPoint != null) {
73+
return false;
74+
}
75+
76+
return true;
77+
}
78+
}

hibernate-core/src/test/java/org/hibernate/test/pagination/DataPoint.hbm.xml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,4 +28,13 @@
2828
<property name="description"/>
2929
</class>
3030

31+
<class name="DataMetaPoint"
32+
dynamic-update="true">
33+
<id name="id">
34+
<generator class="increment"/>
35+
</id>
36+
<property name="meta" />
37+
<many-to-one name="dataPoint" class="DataPoint" />
38+
</class>
39+
3140
</hibernate-mapping>

hibernate-core/src/test/java/org/hibernate/test/pagination/PaginationTest.java

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,21 +7,27 @@
77
package org.hibernate.test.pagination;
88

99
import java.math.BigDecimal;
10+
import java.util.Collections;
1011
import java.util.List;
12+
import java.util.regex.Matcher;
13+
import java.util.regex.Pattern;
1114

1215
import org.hibernate.Criteria;
1316
import org.hibernate.Query;
1417
import org.hibernate.SQLQuery;
1518
import org.hibernate.Session;
1619
import org.hibernate.criterion.Order;
17-
20+
import org.hibernate.engine.query.spi.HQLQueryPlan;
1821
import org.hibernate.testing.DialectChecks;
1922
import org.hibernate.testing.RequiresDialectFeature;
23+
import org.hibernate.testing.TestForIssue;
2024
import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase;
2125
import org.junit.Test;
2226

27+
import static java.lang.String.format;
2328
import static org.junit.Assert.assertEquals;
2429
import static org.junit.Assert.assertTrue;
30+
import static org.junit.Assert.fail;
2531

2632
/**
2733
* @author Gavin King
@@ -101,6 +107,41 @@ public void testOffset() {
101107
cleanupTestData();
102108
}
103109

110+
/**
111+
* @author Piotr Findeisen <[email protected]>
112+
*/
113+
@Test
114+
@TestForIssue( jiraKey = "HHH-951" )
115+
@RequiresDialectFeature(
116+
value = DialectChecks.SupportLimitCheck.class,
117+
comment = "Dialect does not support limit"
118+
)
119+
public void testLimitWithExpreesionAndFetchJoin() {
120+
Session session = openSession();
121+
session.beginTransaction();
122+
123+
String hql = "SELECT b, 1 FROM DataMetaPoint b inner join fetch b.dataPoint dp";
124+
session.createQuery(hql)
125+
.setMaxResults(3)
126+
// This should not fail
127+
.list();
128+
129+
HQLQueryPlan queryPlan = new HQLQueryPlan(hql, false, Collections.EMPTY_MAP, sessionFactory());
130+
String sqlQuery = queryPlan.getTranslators()[0]
131+
.collectSqlStrings().get(0);
132+
133+
session.getTransaction().commit();
134+
session.close();
135+
136+
Matcher matcher = Pattern.compile(
137+
"(?is)\\b(?<column>\\w+\\.\\w+)\\s+as\\s+(?<alias>\\w+)\\b.*\\k<column>\\s+as\\s+\\k<alias>")
138+
.matcher(sqlQuery);
139+
if (matcher.find()) {
140+
fail(format("Column %s mapped to alias %s twice in generated SQL: %s", matcher.group("column"),
141+
matcher.group("alias"), sqlQuery));
142+
}
143+
}
144+
104145
@Test
105146
@RequiresDialectFeature(
106147
value = DialectChecks.SupportLimitAndOffsetCheck.class,

0 commit comments

Comments
 (0)