Skip to content

Commit 8fa2e76

Browse files
authored
Merge pull request #425 from jeffgbutler/duplicate-alias-exception
Add check for duplicate table aliases
2 parents 0562ade + 3fafae1 commit 8fa2e76

File tree

5 files changed

+99
-19
lines changed

5 files changed

+99
-19
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/*
2+
* Copyright 2016-2021 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.mybatis.dynamic.sql.exception;
17+
18+
import java.util.Objects;
19+
20+
import org.mybatis.dynamic.sql.SqlTable;
21+
22+
/**
23+
* This exception is thrown when a query is built that attempts to specify more than one
24+
* alias for the same instance of an SqlTable object. That error that would produce a select
25+
* statement that doesn't work.
26+
*
27+
* <p>This error usually occurs when building a self-join query. The workaround is to create
28+
* a second instance of the SqlTable object to use in the self-join.
29+
*
30+
* @since 1.3.1
31+
* @author Jeff Butler
32+
*/
33+
public class DuplicateTableAliasException extends RuntimeException {
34+
35+
private final SqlTable table;
36+
private final String newAlias;
37+
private final String existingAlias;
38+
39+
public DuplicateTableAliasException(SqlTable table, String newAlias, String existingAlias) {
40+
this.table = Objects.requireNonNull(table);
41+
this.newAlias = Objects.requireNonNull(newAlias);
42+
this.existingAlias = Objects.requireNonNull(existingAlias);
43+
}
44+
45+
@Override
46+
public String getMessage() {
47+
return "Table \"" + table.tableNameAtRuntime() //$NON-NLS-1$
48+
+ "\" with requested alias \"" + newAlias //$NON-NLS-1$
49+
+ "\" is already aliased in this query with alias \"" + existingAlias //$NON-NLS-1$
50+
+ "\". Attempting to re-alias a table in the same query is not supported."; //$NON-NLS-1$
51+
}
52+
}

src/main/java/org/mybatis/dynamic/sql/select/AbstractQueryExpressionDSL.java

+24-10
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2016-2020 the original author or authors.
2+
* Copyright 2016-2021 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -17,6 +17,7 @@
1717

1818
import java.util.ArrayList;
1919
import java.util.Arrays;
20+
import java.util.Collections;
2021
import java.util.HashMap;
2122
import java.util.List;
2223
import java.util.Map;
@@ -26,6 +27,7 @@
2627

2728
import org.mybatis.dynamic.sql.SqlTable;
2829
import org.mybatis.dynamic.sql.TableExpression;
30+
import org.mybatis.dynamic.sql.exception.DuplicateTableAliasException;
2931
import org.mybatis.dynamic.sql.select.join.JoinCriterion;
3032
import org.mybatis.dynamic.sql.select.join.JoinModel;
3133
import org.mybatis.dynamic.sql.select.join.JoinSpecification;
@@ -39,7 +41,7 @@ public abstract class AbstractQueryExpressionDSL<W extends AbstractWhereDSL<?>,
3941
extends AbstractWhereSupport<W> {
4042

4143
private final List<JoinSpecification.Builder> joinSpecificationBuilders = new ArrayList<>();
42-
protected final Map<SqlTable, String> tableAliases = new HashMap<>();
44+
private final Map<SqlTable, String> tableAliases = new HashMap<>();
4345
private final TableExpression table;
4446

4547
protected AbstractQueryExpressionDSL(TableExpression table) {
@@ -58,7 +60,7 @@ public T join(SqlTable joinTable, JoinCriterion onJoinCriterion,
5860

5961
public T join(SqlTable joinTable, String tableAlias, JoinCriterion onJoinCriterion,
6062
JoinCriterion...andJoinCriteria) {
61-
tableAliases.put(joinTable, tableAlias);
63+
addTableAlias(joinTable, tableAlias);
6264
return join(joinTable, onJoinCriterion, andJoinCriteria);
6365
}
6466

@@ -70,7 +72,7 @@ public T join(SqlTable joinTable, JoinCriterion onJoinCriterion,
7072

7173
public T join(SqlTable joinTable, String tableAlias, JoinCriterion onJoinCriterion,
7274
List<JoinCriterion> andJoinCriteria) {
73-
tableAliases.put(joinTable, tableAlias);
75+
addTableAlias(joinTable, tableAlias);
7476
return join(joinTable, onJoinCriterion, andJoinCriteria);
7577
}
7678

@@ -89,7 +91,7 @@ public T leftJoin(SqlTable joinTable, JoinCriterion onJoinCriterion,
8991

9092
public T leftJoin(SqlTable joinTable, String tableAlias, JoinCriterion onJoinCriterion,
9193
JoinCriterion...andJoinCriteria) {
92-
tableAliases.put(joinTable, tableAlias);
94+
addTableAlias(joinTable, tableAlias);
9395
return leftJoin(joinTable, onJoinCriterion, andJoinCriteria);
9496
}
9597

@@ -101,7 +103,7 @@ public T leftJoin(SqlTable joinTable, JoinCriterion onJoinCriterion,
101103

102104
public T leftJoin(SqlTable joinTable, String tableAlias, JoinCriterion onJoinCriterion,
103105
List<JoinCriterion> andJoinCriteria) {
104-
tableAliases.put(joinTable, tableAlias);
106+
addTableAlias(joinTable, tableAlias);
105107
return leftJoin(joinTable, onJoinCriterion, andJoinCriteria);
106108
}
107109

@@ -120,7 +122,7 @@ public T rightJoin(SqlTable joinTable, JoinCriterion onJoinCriterion,
120122

121123
public T rightJoin(SqlTable joinTable, String tableAlias, JoinCriterion onJoinCriterion,
122124
JoinCriterion...andJoinCriteria) {
123-
tableAliases.put(joinTable, tableAlias);
125+
addTableAlias(joinTable, tableAlias);
124126
return rightJoin(joinTable, onJoinCriterion, andJoinCriteria);
125127
}
126128

@@ -132,7 +134,7 @@ public T rightJoin(SqlTable joinTable, JoinCriterion onJoinCriterion,
132134

133135
public T rightJoin(SqlTable joinTable, String tableAlias, JoinCriterion onJoinCriterion,
134136
List<JoinCriterion> andJoinCriteria) {
135-
tableAliases.put(joinTable, tableAlias);
137+
addTableAlias(joinTable, tableAlias);
136138
return rightJoin(joinTable, onJoinCriterion, andJoinCriteria);
137139
}
138140

@@ -151,7 +153,7 @@ public T fullJoin(SqlTable joinTable, JoinCriterion onJoinCriterion,
151153

152154
public T fullJoin(SqlTable joinTable, String tableAlias, JoinCriterion onJoinCriterion,
153155
JoinCriterion...andJoinCriteria) {
154-
tableAliases.put(joinTable, tableAlias);
156+
addTableAlias(joinTable, tableAlias);
155157
return fullJoin(joinTable, onJoinCriterion, andJoinCriteria);
156158
}
157159

@@ -163,7 +165,7 @@ public T fullJoin(SqlTable joinTable, JoinCriterion onJoinCriterion,
163165

164166
public T fullJoin(SqlTable joinTable, String tableAlias, JoinCriterion onJoinCriterion,
165167
List<JoinCriterion> andJoinCriteria) {
166-
tableAliases.put(joinTable, tableAlias);
168+
addTableAlias(joinTable, tableAlias);
167169
return fullJoin(joinTable, onJoinCriterion, andJoinCriteria);
168170
}
169171

@@ -197,6 +199,18 @@ protected Optional<JoinModel> buildJoinModel() {
197199
.collect(Collectors.toList())));
198200
}
199201

202+
protected void addTableAlias(SqlTable table, String tableAlias) {
203+
if (tableAliases.containsKey(table)) {
204+
throw new DuplicateTableAliasException(table, tableAlias, tableAliases.get(table));
205+
}
206+
207+
tableAliases.put(table, tableAlias);
208+
}
209+
210+
protected Map<SqlTable, String> tableAliases() {
211+
return Collections.unmodifiableMap(tableAliases);
212+
}
213+
200214
protected static SubQuery buildSubQuery(Buildable<SelectModel> selectModel) {
201215
return new SubQuery.Builder()
202216
.withSelectModel(selectModel.build())

src/main/java/org/mybatis/dynamic/sql/select/CountDSL.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2016-2020 the original author or authors.
2+
* Copyright 2016-2021 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -63,7 +63,7 @@ private SelectModel buildModel() {
6363
QueryExpressionModel.Builder b = new QueryExpressionModel.Builder()
6464
.withSelectColumn(countColumn)
6565
.withTable(table())
66-
.withTableAliases(tableAliases)
66+
.withTableAliases(tableAliases())
6767
.withWhereModel(whereBuilder.buildWhereModel());
6868

6969
buildJoinModel().ifPresent(b::withJoinModel);

src/main/java/org/mybatis/dynamic/sql/select/QueryExpressionDSL.java

+6-6
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ public class QueryExpressionDSL<R>
5656

5757
QueryExpressionDSL(FromGatherer<R> fromGatherer, SqlTable table, String tableAlias) {
5858
this(fromGatherer, table);
59-
tableAliases.put(table, tableAlias);
59+
addTableAlias(table, tableAlias);
6060
}
6161

6262
@Override
@@ -75,7 +75,7 @@ public JoinSpecificationStarter join(SqlTable joinTable) {
7575
}
7676

7777
public JoinSpecificationStarter join(SqlTable joinTable, String tableAlias) {
78-
tableAliases.put(joinTable, tableAlias);
78+
addTableAlias(joinTable, tableAlias);
7979
return join(joinTable);
8080
}
8181

@@ -88,7 +88,7 @@ public JoinSpecificationStarter leftJoin(SqlTable joinTable) {
8888
}
8989

9090
public JoinSpecificationStarter leftJoin(SqlTable joinTable, String tableAlias) {
91-
tableAliases.put(joinTable, tableAlias);
91+
addTableAlias(joinTable, tableAlias);
9292
return leftJoin(joinTable);
9393
}
9494

@@ -101,7 +101,7 @@ public JoinSpecificationStarter rightJoin(SqlTable joinTable) {
101101
}
102102

103103
public JoinSpecificationStarter rightJoin(SqlTable joinTable, String tableAlias) {
104-
tableAliases.put(joinTable, tableAlias);
104+
addTableAlias(joinTable, tableAlias);
105105
return rightJoin(joinTable);
106106
}
107107

@@ -114,7 +114,7 @@ public JoinSpecificationStarter fullJoin(SqlTable joinTable) {
114114
}
115115

116116
public JoinSpecificationStarter fullJoin(SqlTable joinTable, String tableAlias) {
117-
tableAliases.put(joinTable, tableAlias);
117+
addTableAlias(joinTable, tableAlias);
118118
return fullJoin(joinTable);
119119
}
120120

@@ -153,7 +153,7 @@ protected QueryExpressionModel buildModel() {
153153
.withConnector(connector)
154154
.withTable(table())
155155
.isDistinct(isDistinct)
156-
.withTableAliases(tableAliases)
156+
.withTableAliases(tableAliases())
157157
.withWhereModel(whereBuilder.buildWhereModel())
158158
.withJoinModel(buildJoinModel().orElse(null))
159159
.withGroupByModel(groupByModel)

src/test/java/examples/joins/JoinMapperTest.java

+15-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2016-2020 the original author or authors.
2+
* Copyright 2016-2021 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -22,6 +22,7 @@
2222
import static examples.joins.OrderMasterDynamicSQLSupport.orderMaster;
2323
import static examples.joins.UserDynamicSQLSupport.*;
2424
import static org.assertj.core.api.Assertions.assertThat;
25+
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
2526
import static org.mybatis.dynamic.sql.SqlBuilder.*;
2627

2728
import java.io.InputStream;
@@ -41,6 +42,7 @@
4142
import org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory;
4243
import org.junit.jupiter.api.BeforeEach;
4344
import org.junit.jupiter.api.Test;
45+
import org.mybatis.dynamic.sql.exception.DuplicateTableAliasException;
4446
import org.mybatis.dynamic.sql.render.RenderingStrategies;
4547
import org.mybatis.dynamic.sql.select.render.SelectStatementProvider;
4648
import org.mybatis.dynamic.sql.util.mybatis3.CommonSelectMapper;
@@ -944,6 +946,18 @@ void testSelf() {
944946
}
945947
}
946948

949+
@Test
950+
void testSelfWithDuplicateAlias() {
951+
assertThatExceptionOfType(DuplicateTableAliasException.class).isThrownBy(() ->
952+
select(user.userId, user.userName, user.parentId)
953+
.from(user, "u1")
954+
.join(user, "u2").on(user.userId, equalTo(user.parentId))
955+
.where(user.userId, isEqualTo(4))
956+
.build()
957+
.render(RenderingStrategies.MYBATIS3)
958+
).withMessage("Table \"User\" with requested alias \"u2\" is already aliased in this query with alias \"u1\". Attempting to re-alias a table in the same query is not supported.");
959+
}
960+
947961
@Test
948962
void testLimitAndOffsetAfterJoin() {
949963
try (SqlSession session = sqlSessionFactory.openSession()) {

0 commit comments

Comments
 (0)