Commit 38e88b02f52e8b4fa143a74f64d7c6408c89d387

Authored by YunaiV
1 parent 61b0624a

🐛修复数据权限,不支持隐式内连接的问题

yudao-framework/yudao-spring-boot-starter-biz-data-permission/src/main/java/cn/iocoder/yudao/framework/datapermission/core/db/DataPermissionDatabaseInterceptor.java
... ... @@ -18,7 +18,6 @@ import net.sf.jsqlparser.expression.operators.conditional.OrExpression;
18 18 import net.sf.jsqlparser.expression.operators.relational.ExistsExpression;
19 19 import net.sf.jsqlparser.expression.operators.relational.ExpressionList;
20 20 import net.sf.jsqlparser.expression.operators.relational.InExpression;
21   -import net.sf.jsqlparser.expression.operators.relational.ItemsList;
22 21 import net.sf.jsqlparser.schema.Table;
23 22 import net.sf.jsqlparser.statement.delete.Delete;
24 23 import net.sf.jsqlparser.statement.select.*;
... ... @@ -37,7 +36,7 @@ import java.util.concurrent.ConcurrentHashMap;
37 36  
38 37 /**
39 38 * 数据权限拦截器,通过 {@link DataPermissionRule} 数据权限规则,重写 SQL 的方式来实现
40   - * 主要的 SQL 重写方法,可见 {@link #builderExpression(Expression, Table)} 方法
  39 + * 主要的 SQL 重写方法,可见 {@link #builderExpression(Expression, List)} 方法
41 40 *
42 41 * 整体的代码实现上,参考 {@link com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor} 实现。
43 42 * 所以每次 MyBatis Plus 升级时,需要 Review 下其具体的实现是否有变更!
... ... @@ -53,8 +52,7 @@ public class DataPermissionDatabaseInterceptor extends JsqlParserSupport impleme
53 52 private final MappedStatementCache mappedStatementCache = new MappedStatementCache();
54 53  
55 54 @Override // SELECT 场景
56   - public void beforeQuery(Executor executor, MappedStatement ms, Object parameter,
57   - RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
  55 + public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
58 56 // 获得 Mapper 对应的数据权限的规则
59 57 List<DataPermissionRule> rules = ruleFactory.getDataPermissionRule(ms.getId());
60 58 if (mappedStatementCache.noRewritable(ms, rules)) { // 如果无需重写,则跳过
... ... @@ -68,12 +66,14 @@ public class DataPermissionDatabaseInterceptor extends JsqlParserSupport impleme
68 66 // 处理 SQL
69 67 mpBs.sql(parserSingle(mpBs.sql(), null));
70 68 } finally {
  69 + // 添加是否需要重写的缓存
71 70 addMappedStatementCache(ms);
  71 + // 清空上下文
72 72 ContextHolder.clear();
73 73 }
74 74 }
75 75  
76   - @Override // 只处理 UPDATE / DELETE 场景,不处理 INSERT 场景
  76 + @Override // 只处理 UPDATE / DELETE 场景,不处理 INSERT 场景(因为 INSERT 不需要数据权限)
77 77 public void beforePrepare(StatementHandler sh, Connection connection, Integer transactionTimeout) {
78 78 PluginUtils.MPStatementHandler mpSh = PluginUtils.mpStatementHandler(sh);
79 79 MappedStatement ms = mpSh.mappedStatement();
... ... @@ -92,7 +92,9 @@ public class DataPermissionDatabaseInterceptor extends JsqlParserSupport impleme
92 92 // 处理 SQL
93 93 mpBs.sql(parserMulti(mpBs.sql(), null));
94 94 } finally {
  95 + // 添加是否需要重写的缓存
95 96 addMappedStatementCache(ms);
  97 + // 清空上下文
96 98 ContextHolder.clear();
97 99 }
98 100 }
... ... @@ -107,24 +109,6 @@ public class DataPermissionDatabaseInterceptor extends JsqlParserSupport impleme
107 109 }
108 110 }
109 111  
110   - protected void processSelectBody(SelectBody selectBody) {
111   - if (selectBody == null) {
112   - return;
113   - }
114   - if (selectBody instanceof PlainSelect) {
115   - processPlainSelect((PlainSelect) selectBody);
116   - } else if (selectBody instanceof WithItem) {
117   - WithItem withItem = (WithItem) selectBody;
118   - processSelectBody(withItem.getSubSelect().getSelectBody());
119   - } else {
120   - SetOperationList operationList = (SetOperationList) selectBody;
121   - List<SelectBody> selectBodys = operationList.getSelects();
122   - if (CollectionUtils.isNotEmpty(selectBodys)) {
123   - selectBodys.forEach(this::processSelectBody);
124   - }
125   - }
126   - }
127   -
128 112 /**
129 113 * update 语句处理
130 114 */
... ... @@ -142,28 +126,77 @@ public class DataPermissionDatabaseInterceptor extends JsqlParserSupport impleme
142 126 delete.setWhere(this.builderExpression(delete.getWhere(), delete.getTable()));
143 127 }
144 128  
  129 + // ========== 和 TenantLineInnerInterceptor 一致的逻辑 ==========
  130 +
  131 + protected void processSelectBody(SelectBody selectBody) {
  132 + if (selectBody == null) {
  133 + return;
  134 + }
  135 + if (selectBody instanceof PlainSelect) {
  136 + processPlainSelect((PlainSelect) selectBody);
  137 + } else if (selectBody instanceof WithItem) {
  138 + WithItem withItem = (WithItem) selectBody;
  139 + processSelectBody(withItem.getSubSelect().getSelectBody());
  140 + } else {
  141 + SetOperationList operationList = (SetOperationList) selectBody;
  142 + List<SelectBody> selectBodyList = operationList.getSelects();
  143 + if (CollectionUtils.isNotEmpty(selectBodyList)) {
  144 + selectBodyList.forEach(this::processSelectBody);
  145 + }
  146 + }
  147 + }
  148 +
145 149 /**
146 150 * 处理 PlainSelect
147 151 */
148 152 protected void processPlainSelect(PlainSelect plainSelect) {
149   - FromItem fromItem = plainSelect.getFromItem();
150   - Expression where = plainSelect.getWhere();
151   - processWhereSubSelect(where);
152   - if (fromItem instanceof Table) {
153   - Table fromTable = (Table) fromItem;
154   - plainSelect.setWhere(builderExpression(where, fromTable));
155   - } else {
156   - processFromItem(fromItem);
157   - }
158 153 //#3087 github
159 154 List<SelectItem> selectItems = plainSelect.getSelectItems();
160 155 if (CollectionUtils.isNotEmpty(selectItems)) {
161 156 selectItems.forEach(this::processSelectItem);
162 157 }
  158 +
  159 + // 处理 where 中的子查询
  160 + Expression where = plainSelect.getWhere();
  161 + processWhereSubSelect(where);
  162 +
  163 + // 处理 fromItem
  164 + FromItem fromItem = plainSelect.getFromItem();
  165 + List<Table> list = processFromItem(fromItem);
  166 + List<Table> mainTables = new ArrayList<>(list);
  167 +
  168 + // 处理 join
163 169 List<Join> joins = plainSelect.getJoins();
164 170 if (CollectionUtils.isNotEmpty(joins)) {
165   - processJoins(joins);
  171 + mainTables = processJoins(mainTables, joins);
  172 + }
  173 +
  174 + // 当有 mainTable 时,进行 where 条件追加
  175 + if (CollectionUtils.isNotEmpty(mainTables)) {
  176 + plainSelect.setWhere(builderExpression(where, mainTables));
  177 + }
  178 + }
  179 +
  180 + private List<Table> processFromItem(FromItem fromItem) {
  181 + // 处理括号括起来的表达式
  182 + while (fromItem instanceof ParenthesisFromItem) {
  183 + fromItem = ((ParenthesisFromItem) fromItem).getFromItem();
  184 + }
  185 +
  186 + List<Table> mainTables = new ArrayList<>();
  187 + // 无 join 时的处理逻辑
  188 + if (fromItem instanceof Table) {
  189 + Table fromTable = (Table) fromItem;
  190 + mainTables.add(fromTable);
  191 + } else if (fromItem instanceof SubJoin) {
  192 + // SubJoin 类型则还需要添加上 where 条件
  193 + List<Table> tables = processSubJoin((SubJoin) fromItem);
  194 + mainTables.addAll(tables);
  195 + } else {
  196 + // 处理下 fromItem
  197 + processOtherFromItem(fromItem);
166 198 }
  199 + return mainTables;
167 200 }
168 201  
169 202 /**
... ... @@ -191,7 +224,7 @@ public class DataPermissionDatabaseInterceptor extends JsqlParserSupport impleme
191 224 return;
192 225 }
193 226 if (where instanceof FromItem) {
194   - processFromItem((FromItem) where);
  227 + processOtherFromItem((FromItem) where);
195 228 return;
196 229 }
197 230 if (where.toString().indexOf("SELECT") > 0) {
... ... @@ -204,9 +237,9 @@ public class DataPermissionDatabaseInterceptor extends JsqlParserSupport impleme
204 237 } else if (where instanceof InExpression) {
205 238 // in
206 239 InExpression expression = (InExpression) where;
207   - ItemsList itemsList = expression.getRightItemsList();
208   - if (itemsList instanceof SubSelect) {
209   - processSelectBody(((SubSelect) itemsList).getSelectBody());
  240 + Expression inExpression = expression.getRightExpression();
  241 + if (inExpression instanceof SubSelect) {
  242 + processSelectBody(((SubSelect) inExpression).getSelectBody());
210 243 }
211 244 } else if (where instanceof ExistsExpression) {
212 245 // exists
... ... @@ -239,7 +272,7 @@ public class DataPermissionDatabaseInterceptor extends JsqlParserSupport impleme
239 272 * <p>支持: 1. select fun(args..) 2. select fun1(fun2(args..),args..)<p>
240 273 * <p> fixed gitee pulls/141</p>
241 274 *
242   - * @param function 函数
  275 + * @param function
243 276 */
244 277 protected void processFunction(Function function) {
245 278 ExpressionList parameters = function.getParameters();
... ... @@ -257,22 +290,19 @@ public class DataPermissionDatabaseInterceptor extends JsqlParserSupport impleme
257 290 /**
258 291 * 处理子查询等
259 292 */
260   - protected void processFromItem(FromItem fromItem) {
261   - if (fromItem instanceof SubJoin) {
262   - SubJoin subJoin = (SubJoin) fromItem;
263   - if (subJoin.getJoinList() != null) {
264   - processJoins(subJoin.getJoinList());
265   - }
266   - if (subJoin.getLeft() != null) {
267   - processFromItem(subJoin.getLeft());
268   - }
269   - } else if (fromItem instanceof SubSelect) {
  293 + protected void processOtherFromItem(FromItem fromItem) {
  294 + // 去除括号
  295 + while (fromItem instanceof ParenthesisFromItem) {
  296 + fromItem = ((ParenthesisFromItem) fromItem).getFromItem();
  297 + }
  298 +
  299 + if (fromItem instanceof SubSelect) {
270 300 SubSelect subSelect = (SubSelect) fromItem;
271 301 if (subSelect.getSelectBody() != null) {
272 302 processSelectBody(subSelect.getSelectBody());
273 303 }
274 304 } else if (fromItem instanceof ValuesList) {
275   - logger.debug("Perform a subquery, if you do not give us feedback");
  305 + logger.debug("Perform a subQuery, if you do not give us feedback");
276 306 } else if (fromItem instanceof LateralSubSelect) {
277 307 LateralSubSelect lateralSubSelect = (LateralSubSelect) fromItem;
278 308 if (lateralSubSelect.getSubSelect() != null) {
... ... @@ -285,74 +315,175 @@ public class DataPermissionDatabaseInterceptor extends JsqlParserSupport impleme
285 315 }
286 316  
287 317 /**
  318 + * 处理 sub join
  319 + *
  320 + * @param subJoin subJoin
  321 + * @return Table subJoin 中的主表
  322 + */
  323 + private List<Table> processSubJoin(SubJoin subJoin) {
  324 + List<Table> mainTables = new ArrayList<>();
  325 + if (subJoin.getJoinList() != null) {
  326 + List<Table> list = processFromItem(subJoin.getLeft());
  327 + mainTables.addAll(list);
  328 + mainTables = processJoins(mainTables, subJoin.getJoinList());
  329 + }
  330 + return mainTables;
  331 + }
  332 +
  333 + /**
288 334 * 处理 joins
289 335 *
290   - * @param joins join 集合
  336 + * @param mainTables 可以为 null
  337 + * @param joins join 集合
  338 + * @return List<Table> 右连接查询的 Table 列表
291 339 */
292   - private void processJoins(List<Join> joins) {
  340 + private List<Table> processJoins(List<Table> mainTables, List<Join> joins) {
  341 + // join 表达式中最终的主表
  342 + Table mainTable = null;
  343 + // 当前 join 的左表
  344 + Table leftTable = null;
  345 +
  346 + if (mainTables == null) {
  347 + mainTables = new ArrayList<>();
  348 + } else if (mainTables.size() == 1) {
  349 + mainTable = mainTables.get(0);
  350 + leftTable = mainTable;
  351 + }
  352 +
293 353 //对于 on 表达式写在最后的 join,需要记录下前面多个 on 的表名
294   - Deque<Table> tables = new LinkedList<>();
  354 + Deque<List<Table>> onTableDeque = new LinkedList<>();
295 355 for (Join join : joins) {
296 356 // 处理 on 表达式
297   - FromItem fromItem = join.getRightItem();
298   - if (fromItem instanceof Table) {
299   - Table fromTable = (Table) fromItem;
  357 + FromItem joinItem = join.getRightItem();
  358 +
  359 + // 获取当前 join 的表,subJoint 可以看作是一张表
  360 + List<Table> joinTables = null;
  361 + if (joinItem instanceof Table) {
  362 + joinTables = new ArrayList<>();
  363 + joinTables.add((Table) joinItem);
  364 + } else if (joinItem instanceof SubJoin) {
  365 + joinTables = processSubJoin((SubJoin) joinItem);
  366 + }
  367 +
  368 + if (joinTables != null) {
  369 +
  370 + // 如果是隐式内连接
  371 + if (join.isSimple()) {
  372 + mainTables.addAll(joinTables);
  373 + continue;
  374 + }
  375 +
  376 + // 当前表是否忽略
  377 + Table joinTable = joinTables.get(0);
  378 +
  379 + List<Table> onTables = null;
  380 + // 如果不要忽略,且是右连接,则记录下当前表
  381 + if (join.isRight()) {
  382 + mainTable = joinTable;
  383 + if (leftTable != null) {
  384 + onTables = Collections.singletonList(leftTable);
  385 + }
  386 + } else if (join.isLeft()) {
  387 + onTables = Collections.singletonList(joinTable);
  388 + } else if (join.isInner()) {
  389 + if (mainTable == null) {
  390 + onTables = Collections.singletonList(joinTable);
  391 + } else {
  392 + onTables = Arrays.asList(mainTable, joinTable);
  393 + }
  394 + mainTable = null;
  395 + }
  396 +
  397 + mainTables = new ArrayList<>();
  398 + if (mainTable != null) {
  399 + mainTables.add(mainTable);
  400 + }
  401 +
300 402 // 获取 join 尾缀的 on 表达式列表
301 403 Collection<Expression> originOnExpressions = join.getOnExpressions();
302 404 // 正常 join on 表达式只有一个,立刻处理
303   - if (originOnExpressions.size() == 1) {
304   - processJoin(join);
  405 + if (originOnExpressions.size() == 1 && onTables != null) {
  406 + List<Expression> onExpressions = new LinkedList<>();
  407 + onExpressions.add(builderExpression(originOnExpressions.iterator().next(), onTables));
  408 + join.setOnExpressions(onExpressions);
  409 + leftTable = joinTable;
305 410 continue;
306 411 }
307   - tables.push(fromTable);
  412 + // 表名压栈,忽略的表压入 null,以便后续不处理
  413 + onTableDeque.push(onTables);
308 414 // 尾缀多个 on 表达式的时候统一处理
309 415 if (originOnExpressions.size() > 1) {
310 416 Collection<Expression> onExpressions = new LinkedList<>();
311 417 for (Expression originOnExpression : originOnExpressions) {
312   - Table currentTable = tables.poll();
313   - onExpressions.add(builderExpression(originOnExpression, currentTable));
  418 + List<Table> currentTableList = onTableDeque.poll();
  419 + if (CollectionUtils.isEmpty(currentTableList)) {
  420 + onExpressions.add(originOnExpression);
  421 + } else {
  422 + onExpressions.add(builderExpression(originOnExpression, currentTableList));
  423 + }
314 424 }
315 425 join.setOnExpressions(onExpressions);
316 426 }
  427 + leftTable = joinTable;
317 428 } else {
318   - // 处理右边连接的子表达式
319   - processFromItem(fromItem);
  429 + processOtherFromItem(joinItem);
  430 + leftTable = null;
320 431 }
321 432 }
  433 +
  434 + return mainTables;
322 435 }
323 436  
  437 + // ========== 和 TenantLineInnerInterceptor 存在差异的逻辑:关键,实现权限条件的拼接 ==========
  438 +
324 439 /**
325   - * 处理联接语句
  440 + * 处理条件
  441 + *
  442 + * @param currentExpression 当前 where 条件
  443 + * @param table 单个表
326 444 */
327   - protected void processJoin(Join join) {
328   - if (join.getRightItem() instanceof Table) {
329   - Table fromTable = (Table) join.getRightItem();
330   - Expression originOnExpression = CollUtil.getFirst(join.getOnExpressions());
331   - originOnExpression = builderExpression(originOnExpression, fromTable);
332   - join.setOnExpressions(CollUtil.newArrayList(originOnExpression));
333   - }
  445 + protected Expression builderExpression(Expression currentExpression, Table table) {
  446 + return this.builderExpression(currentExpression, Collections.singletonList(table));
334 447 }
335 448  
336 449 /**
337 450 * 处理条件
  451 + *
  452 + * @param currentExpression 当前 where 条件
  453 + * @param tables 多个表
338 454 */
339   - protected Expression builderExpression(Expression currentExpression, Table table) {
340   - // 获得 Table 对应的数据权限条件
341   - Expression equalsTo = buildDataPermissionExpression(table);
342   - if (equalsTo == null) { // 如果没条件,则返回 currentExpression 默认
  455 + protected Expression builderExpression(Expression currentExpression, List<Table> tables) {
  456 + // 没有表需要处理直接返回
  457 + if (CollectionUtils.isEmpty(tables)) {
343 458 return currentExpression;
344 459 }
345 460  
346   - // 表达式为空,则直接返回 equalsTo
  461 + // 第一步,获得 Table 对应的数据权限条件
  462 + Expression dataPermissionExpression = null;
  463 + for (Table table : tables) {
  464 + // 构建每个表的权限 Expression 条件
  465 + Expression expression = buildDataPermissionExpression(table);
  466 + if (expression == null) {
  467 + continue;
  468 + }
  469 + // 合并到 dataPermissionExpression 中
  470 + dataPermissionExpression = dataPermissionExpression == null ? expression
  471 + : new AndExpression(dataPermissionExpression, expression);
  472 + }
  473 +
  474 + // 第二步,合并多个 Expression 条件
  475 + if (dataPermissionExpression == null) {
  476 + return currentExpression;
  477 + }
347 478 if (currentExpression == null) {
348   - return equalsTo;
  479 + return dataPermissionExpression;
349 480 }
350   - // 如果表达式为 Or,则需要 (currentExpression) AND equalsTo
  481 + // ① 如果表达式为 Or,则需要 (currentExpression) AND dataPermissionExpression
351 482 if (currentExpression instanceof OrExpression) {
352   - return new AndExpression(new Parenthesis(currentExpression), equalsTo);
  483 + return new AndExpression(new Parenthesis(currentExpression), dataPermissionExpression);
353 484 }
354   - // 如果表达式为 And,则直接返回 currentExpression AND equalsTo
355   - return new AndExpression(currentExpression, equalsTo);
  485 + // ② 如果表达式为 And,则直接返回 where AND dataPermissionExpression
  486 + return new AndExpression(currentExpression, dataPermissionExpression);
356 487 }
357 488  
358 489 /**
... ...
yudao-framework/yudao-spring-boot-starter-biz-data-permission/src/test/java/cn/iocoder/yudao/framework/datapermission/core/db/DataPermissionDatabaseInterceptorTest2.java
... ... @@ -46,7 +46,7 @@ public class DataPermissionDatabaseInterceptorTest2 extends BaseMockitoUnitTest
46 46  
47 47 @Override
48 48 public Set<String> getTableNames() {
49   - return asSet("entity", "entity1", "entity2", "t1", "t2", // 支持 MyBatis Plus 的单元测试
  49 + return asSet("entity", "entity1", "entity2", "entity3", "t1", "t2", "sys_dict_item", // 支持 MyBatis Plus 的单元测试
50 50 "t_user", "t_role"); // 满足自己的单元测试
51 51 }
52 52  
... ... @@ -84,30 +84,30 @@ public class DataPermissionDatabaseInterceptorTest2 extends BaseMockitoUnitTest
84 84 @Test
85 85 void delete() {
86 86 assertSql("delete from entity where id = ?",
87   - "DELETE FROM entity WHERE id = ? AND tenant_id = 1");
  87 + "DELETE FROM entity WHERE id = ? AND entity.tenant_id = 1");
88 88 }
89 89  
90 90 @Test
91 91 void update() {
92 92 assertSql("update entity set name = ? where id = ?",
93   - "UPDATE entity SET name = ? WHERE id = ? AND tenant_id = 1");
  93 + "UPDATE entity SET name = ? WHERE id = ? AND entity.tenant_id = 1");
94 94 }
95 95  
96 96 @Test
97 97 void selectSingle() {
98 98 // 单表
99 99 assertSql("select * from entity where id = ?",
100   - "SELECT * FROM entity WHERE id = ? AND tenant_id = 1");
  100 + "SELECT * FROM entity WHERE id = ? AND entity.tenant_id = 1");
101 101  
102 102 assertSql("select * from entity where id = ? or name = ?",
103   - "SELECT * FROM entity WHERE (id = ? OR name = ?) AND tenant_id = 1");
  103 + "SELECT * FROM entity WHERE (id = ? OR name = ?) AND entity.tenant_id = 1");
104 104  
105 105 assertSql("SELECT * FROM entity WHERE (id = ? OR name = ?)",
106   - "SELECT * FROM entity WHERE (id = ? OR name = ?) AND tenant_id = 1");
  106 + "SELECT * FROM entity WHERE (id = ? OR name = ?) AND entity.tenant_id = 1");
107 107  
108 108 /* not */
109 109 assertSql("SELECT * FROM entity WHERE not (id = ? OR name = ?)",
110   - "SELECT * FROM entity WHERE NOT (id = ? OR name = ?) AND tenant_id = 1");
  110 + "SELECT * FROM entity WHERE NOT (id = ? OR name = ?) AND entity.tenant_id = 1");
111 111 }
112 112  
113 113 @Test
... ... @@ -167,10 +167,12 @@ public class DataPermissionDatabaseInterceptorTest2 extends BaseMockitoUnitTest
167 167 assertSql("SELECT * FROM entity e WHERE e.id >= (select e1.id from entity1 e1 where e1.id = ?)",
168 168 "SELECT * FROM entity e WHERE e.id >= (SELECT e1.id FROM entity1 e1 WHERE e1.id = ? AND e1.tenant_id = 1) AND e.tenant_id = 1");
169 169  
  170 +
170 171 /* <= */
171 172 assertSql("SELECT * FROM entity e WHERE e.id <= (select e1.id from entity1 e1 where e1.id = ?)",
172 173 "SELECT * FROM entity e WHERE e.id <= (SELECT e1.id FROM entity1 e1 WHERE e1.id = ? AND e1.tenant_id = 1) AND e.tenant_id = 1");
173 174  
  175 +
174 176 /* <> */
175 177 assertSql("SELECT * FROM entity e WHERE e.id <> (select e1.id from entity1 e1 where e1.id = ?)",
176 178 "SELECT * FROM entity e WHERE e.id <> (SELECT e1.id FROM entity1 e1 WHERE e1.id = ? AND e1.tenant_id = 1) AND e.tenant_id = 1");
... ... @@ -204,6 +206,14 @@ public class DataPermissionDatabaseInterceptorTest2 extends BaseMockitoUnitTest
204 206 "SELECT * FROM entity e " +
205 207 "LEFT JOIN entity1 e1 ON e1.id = e.id AND e1.tenant_id = 1 " +
206 208 "WHERE (e.id = ? OR e.name = ?) AND e.tenant_id = 1");
  209 +
  210 + assertSql("SELECT * FROM entity e " +
  211 + "left join entity1 e1 on e1.id = e.id " +
  212 + "left join entity2 e2 on e1.id = e2.id",
  213 + "SELECT * FROM entity e " +
  214 + "LEFT JOIN entity1 e1 ON e1.id = e.id AND e1.tenant_id = 1 " +
  215 + "LEFT JOIN entity2 e2 ON e1.id = e2.id AND e2.tenant_id = 1 " +
  216 + "WHERE e.tenant_id = 1");
207 217 }
208 218  
209 219 @Test
... ... @@ -212,17 +222,125 @@ public class DataPermissionDatabaseInterceptorTest2 extends BaseMockitoUnitTest
212 222 assertSql("SELECT * FROM entity e " +
213 223 "right join entity1 e1 on e1.id = e.id",
214 224 "SELECT * FROM entity e " +
215   - "RIGHT JOIN entity1 e1 ON e1.id = e.id AND e1.tenant_id = 1 " +
216   - "WHERE e.tenant_id = 1");
  225 + "RIGHT JOIN entity1 e1 ON e1.id = e.id AND e.tenant_id = 1 " +
  226 + "WHERE e1.tenant_id = 1");
  227 +
  228 + assertSql("SELECT * FROM with_as_1 e " +
  229 + "right join entity1 e1 on e1.id = e.id",
  230 + "SELECT * FROM with_as_1 e " +
  231 + "RIGHT JOIN entity1 e1 ON e1.id = e.id " +
  232 + "WHERE e1.tenant_id = 1");
217 233  
218 234 assertSql("SELECT * FROM entity e " +
219 235 "right join entity1 e1 on e1.id = e.id " +
220 236 "WHERE e.id = ? OR e.name = ?",
221 237 "SELECT * FROM entity e " +
222   - "RIGHT JOIN entity1 e1 ON e1.id = e.id AND e1.tenant_id = 1 " +
223   - "WHERE (e.id = ? OR e.name = ?) AND e.tenant_id = 1");
  238 + "RIGHT JOIN entity1 e1 ON e1.id = e.id AND e.tenant_id = 1 " +
  239 + "WHERE (e.id = ? OR e.name = ?) AND e1.tenant_id = 1");
  240 +
  241 + assertSql("SELECT * FROM entity e " +
  242 + "right join entity1 e1 on e1.id = e.id " +
  243 + "right join entity2 e2 on e1.id = e2.id ",
  244 + "SELECT * FROM entity e " +
  245 + "RIGHT JOIN entity1 e1 ON e1.id = e.id AND e.tenant_id = 1 " +
  246 + "RIGHT JOIN entity2 e2 ON e1.id = e2.id AND e1.tenant_id = 1 " +
  247 + "WHERE e2.tenant_id = 1");
  248 + }
  249 +
  250 + @Test
  251 + void selectMixJoin() {
  252 + assertSql("SELECT * FROM entity e " +
  253 + "right join entity1 e1 on e1.id = e.id " +
  254 + "left join entity2 e2 on e1.id = e2.id",
  255 + "SELECT * FROM entity e " +
  256 + "RIGHT JOIN entity1 e1 ON e1.id = e.id AND e.tenant_id = 1 " +
  257 + "LEFT JOIN entity2 e2 ON e1.id = e2.id AND e2.tenant_id = 1 " +
  258 + "WHERE e1.tenant_id = 1");
  259 +
  260 + assertSql("SELECT * FROM entity e " +
  261 + "left join entity1 e1 on e1.id = e.id " +
  262 + "right join entity2 e2 on e1.id = e2.id",
  263 + "SELECT * FROM entity e " +
  264 + "LEFT JOIN entity1 e1 ON e1.id = e.id AND e1.tenant_id = 1 " +
  265 + "RIGHT JOIN entity2 e2 ON e1.id = e2.id AND e1.tenant_id = 1 " +
  266 + "WHERE e2.tenant_id = 1");
  267 +
  268 + assertSql("SELECT * FROM entity e " +
  269 + "left join entity1 e1 on e1.id = e.id " +
  270 + "inner join entity2 e2 on e1.id = e2.id",
  271 + "SELECT * FROM entity e " +
  272 + "LEFT JOIN entity1 e1 ON e1.id = e.id AND e1.tenant_id = 1 " +
  273 + "INNER JOIN entity2 e2 ON e1.id = e2.id AND e.tenant_id = 1 AND e2.tenant_id = 1");
224 274 }
225 275  
  276 +
  277 + @Test
  278 + void selectJoinSubSelect() {
  279 + assertSql("select * from (select * from entity) e1 " +
  280 + "left join entity2 e2 on e1.id = e2.id",
  281 + "SELECT * FROM (SELECT * FROM entity WHERE entity.tenant_id = 1) e1 " +
  282 + "LEFT JOIN entity2 e2 ON e1.id = e2.id AND e2.tenant_id = 1");
  283 +
  284 + assertSql("select * from entity1 e1 " +
  285 + "left join (select * from entity2) e2 " +
  286 + "on e1.id = e2.id",
  287 + "SELECT * FROM entity1 e1 " +
  288 + "LEFT JOIN (SELECT * FROM entity2 WHERE entity2.tenant_id = 1) e2 " +
  289 + "ON e1.id = e2.id " +
  290 + "WHERE e1.tenant_id = 1");
  291 + }
  292 +
  293 + @Test
  294 + void selectSubJoin() {
  295 +
  296 + assertSql("select * FROM " +
  297 + "(entity1 e1 right JOIN entity2 e2 ON e1.id = e2.id)",
  298 + "SELECT * FROM " +
  299 + "(entity1 e1 RIGHT JOIN entity2 e2 ON e1.id = e2.id AND e1.tenant_id = 1) " +
  300 + "WHERE e2.tenant_id = 1");
  301 +
  302 + assertSql("select * FROM " +
  303 + "(entity1 e1 LEFT JOIN entity2 e2 ON e1.id = e2.id)",
  304 + "SELECT * FROM " +
  305 + "(entity1 e1 LEFT JOIN entity2 e2 ON e1.id = e2.id AND e2.tenant_id = 1) " +
  306 + "WHERE e1.tenant_id = 1");
  307 +
  308 +
  309 + assertSql("select * FROM " +
  310 + "(entity1 e1 LEFT JOIN entity2 e2 ON e1.id = e2.id) " +
  311 + "right join entity3 e3 on e1.id = e3.id",
  312 + "SELECT * FROM " +
  313 + "(entity1 e1 LEFT JOIN entity2 e2 ON e1.id = e2.id AND e2.tenant_id = 1) " +
  314 + "RIGHT JOIN entity3 e3 ON e1.id = e3.id AND e1.tenant_id = 1 " +
  315 + "WHERE e3.tenant_id = 1");
  316 +
  317 +
  318 + assertSql("select * FROM entity e " +
  319 + "LEFT JOIN (entity1 e1 right join entity2 e2 ON e1.id = e2.id) " +
  320 + "on e.id = e2.id",
  321 + "SELECT * FROM entity e " +
  322 + "LEFT JOIN (entity1 e1 RIGHT JOIN entity2 e2 ON e1.id = e2.id AND e1.tenant_id = 1) " +
  323 + "ON e.id = e2.id AND e2.tenant_id = 1 " +
  324 + "WHERE e.tenant_id = 1");
  325 +
  326 + assertSql("select * FROM entity e " +
  327 + "LEFT JOIN (entity1 e1 left join entity2 e2 ON e1.id = e2.id) " +
  328 + "on e.id = e2.id",
  329 + "SELECT * FROM entity e " +
  330 + "LEFT JOIN (entity1 e1 LEFT JOIN entity2 e2 ON e1.id = e2.id AND e2.tenant_id = 1) " +
  331 + "ON e.id = e2.id AND e1.tenant_id = 1 " +
  332 + "WHERE e.tenant_id = 1");
  333 +
  334 + assertSql("select * FROM entity e " +
  335 + "RIGHT JOIN (entity1 e1 left join entity2 e2 ON e1.id = e2.id) " +
  336 + "on e.id = e2.id",
  337 + "SELECT * FROM entity e " +
  338 + "RIGHT JOIN (entity1 e1 LEFT JOIN entity2 e2 ON e1.id = e2.id AND e2.tenant_id = 1) " +
  339 + "ON e.id = e2.id AND e.tenant_id = 1 " +
  340 + "WHERE e1.tenant_id = 1");
  341 + }
  342 +
  343 +
226 344 @Test
227 345 void selectLeftJoinMultipleTrailingOn() {
228 346 // 多个 on 尾缀的
... ... @@ -256,51 +374,97 @@ public class DataPermissionDatabaseInterceptorTest2 extends BaseMockitoUnitTest
256 374 "inner join entity1 e1 on e1.id = e.id " +
257 375 "WHERE e.id = ? OR e.name = ?",
258 376 "SELECT * FROM entity e " +
259   - "INNER JOIN entity1 e1 ON e1.id = e.id AND e1.tenant_id = 1 " +
260   - "WHERE (e.id = ? OR e.name = ?) AND e.tenant_id = 1");
  377 + "INNER JOIN entity1 e1 ON e1.id = e.id AND e.tenant_id = 1 AND e1.tenant_id = 1 " +
  378 + "WHERE e.id = ? OR e.name = ?");
261 379  
262 380 assertSql("SELECT * FROM entity e " +
263 381 "inner join entity1 e1 on e1.id = e.id " +
264 382 "WHERE (e.id = ? OR e.name = ?)",
265 383 "SELECT * FROM entity e " +
266   - "INNER JOIN entity1 e1 ON e1.id = e.id AND e1.tenant_id = 1 " +
267   - "WHERE (e.id = ? OR e.name = ?) AND e.tenant_id = 1");
  384 + "INNER JOIN entity1 e1 ON e1.id = e.id AND e.tenant_id = 1 AND e1.tenant_id = 1 " +
  385 + "WHERE (e.id = ? OR e.name = ?)");
  386 +
  387 + // 隐式内连接
  388 + assertSql("SELECT * FROM entity,entity1 " +
  389 + "WHERE entity.id = entity1.id",
  390 + "SELECT * FROM entity, entity1 " +
  391 + "WHERE entity.id = entity1.id AND entity.tenant_id = 1 AND entity1.tenant_id = 1");
  392 +
  393 + // 隐式内连接
  394 + assertSql("SELECT * FROM entity a, with_as_entity1 b " +
  395 + "WHERE a.id = b.id",
  396 + "SELECT * FROM entity a, with_as_entity1 b " +
  397 + "WHERE a.id = b.id AND a.tenant_id = 1");
  398 +
  399 + assertSql("SELECT * FROM with_as_entity a, with_as_entity1 b " +
  400 + "WHERE a.id = b.id",
  401 + "SELECT * FROM with_as_entity a, with_as_entity1 b " +
  402 + "WHERE a.id = b.id");
  403 +
  404 + // SubJoin with 隐式内连接
  405 + assertSql("SELECT * FROM (entity,entity1) " +
  406 + "WHERE entity.id = entity1.id",
  407 + "SELECT * FROM (entity, entity1) " +
  408 + "WHERE entity.id = entity1.id " +
  409 + "AND entity.tenant_id = 1 AND entity1.tenant_id = 1");
  410 +
  411 + assertSql("SELECT * FROM ((entity,entity1),entity2) " +
  412 + "WHERE entity.id = entity1.id and entity.id = entity2.id",
  413 + "SELECT * FROM ((entity, entity1), entity2) " +
  414 + "WHERE entity.id = entity1.id AND entity.id = entity2.id " +
  415 + "AND entity.tenant_id = 1 AND entity1.tenant_id = 1 AND entity2.tenant_id = 1");
  416 +
  417 + assertSql("SELECT * FROM (entity,(entity1,entity2)) " +
  418 + "WHERE entity.id = entity1.id and entity.id = entity2.id",
  419 + "SELECT * FROM (entity, (entity1, entity2)) " +
  420 + "WHERE entity.id = entity1.id AND entity.id = entity2.id " +
  421 + "AND entity.tenant_id = 1 AND entity1.tenant_id = 1 AND entity2.tenant_id = 1");
  422 +
  423 + // 沙雕的括号写法
  424 + assertSql("SELECT * FROM (((entity,entity1))) " +
  425 + "WHERE entity.id = entity1.id",
  426 + "SELECT * FROM (((entity, entity1))) " +
  427 + "WHERE entity.id = entity1.id " +
  428 + "AND entity.tenant_id = 1 AND entity1.tenant_id = 1");
268 429  
269   - // 垃圾 inner join todo
270   -// assertSql("SELECT * FROM entity,entity1 " +
271   -// "WHERE entity.id = entity1.id",
272   -// "SELECT * FROM entity e " +
273   -// "INNER JOIN entity1 e1 ON e1.id = e.id AND e1.tenant_id = 1 " +
274   -// "WHERE (e.id = ? OR e.name = ?) AND e.tenant_id = 1");
275 430 }
276 431  
  432 +
277 433 @Test
278 434 void selectWithAs() {
279 435 assertSql("with with_as_A as (select * from entity) select * from with_as_A",
280   - "WITH with_as_A AS (SELECT * FROM entity WHERE tenant_id = 1) SELECT * FROM with_as_A");
  436 + "WITH with_as_A AS (SELECT * FROM entity WHERE entity.tenant_id = 1) SELECT * FROM with_as_A");
  437 + }
  438 +
  439 +
  440 + @Test
  441 + void selectIgnoreTable() {
  442 + assertSql(" SELECT dict.dict_code, item.item_text AS \"text\", item.item_value AS \"value\" FROM sys_dict_item item INNER JOIN sys_dict dict ON dict.id = item.dict_id WHERE dict.dict_code IN (1, 2, 3) AND item.item_value IN (1, 2, 3)",
  443 + "SELECT dict.dict_code, item.item_text AS \"text\", item.item_value AS \"value\" FROM sys_dict_item item INNER JOIN sys_dict dict ON dict.id = item.dict_id AND item.tenant_id = 1 WHERE dict.dict_code IN (1, 2, 3) AND item.item_value IN (1, 2, 3)");
281 444 }
282 445  
283 446 private void assertSql(String sql, String targetSql) {
284 447 assertEquals(targetSql, interceptor.parserSingle(sql, null));
285 448 }
286 449  
  450 +
287 451 // ========== 额外的测试 ==========
288 452  
289 453 @Test
290 454 public void testSelectSingle() {
291 455 // 单表
292 456 assertSql("select * from t_user where id = ?",
293   - "SELECT * FROM t_user WHERE id = ? AND tenant_id = 1 AND dept_id IN (10, 20)");
  457 + "SELECT * FROM t_user WHERE id = ? AND t_user.tenant_id = 1 AND t_user.dept_id IN (10, 20)");
294 458  
295 459 assertSql("select * from t_user where id = ? or name = ?",
296   - "SELECT * FROM t_user WHERE (id = ? OR name = ?) AND tenant_id = 1 AND dept_id IN (10, 20)");
  460 + "SELECT * FROM t_user WHERE (id = ? OR name = ?) AND t_user.tenant_id = 1 AND t_user.dept_id IN (10, 20)");
297 461  
298 462 assertSql("SELECT * FROM t_user WHERE (id = ? OR name = ?)",
299   - "SELECT * FROM t_user WHERE (id = ? OR name = ?) AND tenant_id = 1 AND dept_id IN (10, 20)");
  463 + "SELECT * FROM t_user WHERE (id = ? OR name = ?) AND t_user.tenant_id = 1 AND t_user.dept_id IN (10, 20)");
300 464  
301 465 /* not */
302 466 assertSql("SELECT * FROM t_user WHERE not (id = ? OR name = ?)",
303   - "SELECT * FROM t_user WHERE NOT (id = ? OR name = ?) AND tenant_id = 1 AND dept_id IN (10, 20)");
  467 + "SELECT * FROM t_user WHERE NOT (id = ? OR name = ?) AND t_user.tenant_id = 1 AND t_user.dept_id IN (10, 20)");
304 468 }
305 469  
306 470 @Test
... ... @@ -329,16 +493,16 @@ public class DataPermissionDatabaseInterceptorTest2 extends BaseMockitoUnitTest
329 493 "right join t_role e1 on e1.id = e.id " +
330 494 "WHERE e.id = ? OR e.name = ?",
331 495 "SELECT * FROM t_user e " +
332   - "RIGHT JOIN t_role e1 ON e1.id = e.id AND e1.tenant_id = 1 " +
333   - "WHERE (e.id = ? OR e.name = ?) AND e.tenant_id = 1 AND e.dept_id IN (10, 20)");
  496 + "RIGHT JOIN t_role e1 ON e1.id = e.id AND e.tenant_id = 1 AND e.dept_id IN (10, 20) " +
  497 + "WHERE (e.id = ? OR e.name = ?) AND e1.tenant_id = 1");
334 498  
335 499 // 条件 e.id = ? OR e.name = ? 带括号
336 500 assertSql("SELECT * FROM t_user e " +
337 501 "right join t_role e1 on e1.id = e.id " +
338 502 "WHERE (e.id = ? OR e.name = ?)",
339 503 "SELECT * FROM t_user e " +
340   - "RIGHT JOIN t_role e1 ON e1.id = e.id AND e1.tenant_id = 1 " +
341   - "WHERE (e.id = ? OR e.name = ?) AND e.tenant_id = 1 AND e.dept_id IN (10, 20)");
  504 + "RIGHT JOIN t_role e1 ON e1.id = e.id AND e.tenant_id = 1 AND e.dept_id IN (10, 20) " +
  505 + "WHERE (e.id = ? OR e.name = ?) AND e1.tenant_id = 1");
342 506 }
343 507  
344 508 @Test
... ... @@ -348,23 +512,22 @@ public class DataPermissionDatabaseInterceptorTest2 extends BaseMockitoUnitTest
348 512 "inner join entity1 e1 on e1.id = e.id " +
349 513 "WHERE e.id = ? OR e.name = ?",
350 514 "SELECT * FROM t_user e " +
351   - "INNER JOIN entity1 e1 ON e1.id = e.id AND e1.tenant_id = 1 " +
352   - "WHERE (e.id = ? OR e.name = ?) AND e.tenant_id = 1 AND e.dept_id IN (10, 20)");
  515 + "INNER JOIN entity1 e1 ON e1.id = e.id AND e.tenant_id = 1 AND e.dept_id IN (10, 20) AND e1.tenant_id = 1 " +
  516 + "WHERE e.id = ? OR e.name = ?");
353 517  
354 518 // 条件 e.id = ? OR e.name = ? 带括号
355 519 assertSql("SELECT * FROM t_user e " +
356   - "inner join t_role e1 on e1.id = e.id " +
  520 + "inner join entity1 e1 on e1.id = e.id " +
357 521 "WHERE (e.id = ? OR e.name = ?)",
358 522 "SELECT * FROM t_user e " +
359   - "INNER JOIN t_role e1 ON e1.id = e.id AND e1.tenant_id = 1 " +
360   - "WHERE (e.id = ? OR e.name = ?) AND e.tenant_id = 1 AND e.dept_id IN (10, 20)");
361   -
362   - // 垃圾 inner join todo
363   -// assertSql("SELECT * FROM entity,entity1 " +
364   -// "WHERE entity.id = entity1.id",
365   -// "SELECT * FROM entity e " +
366   -// "INNER JOIN entity1 e1 ON e1.id = e.id AND e1.tenant_id = 1 " +
367   -// "WHERE (e.id = ? OR e.name = ?) AND e.tenant_id = 1");
  523 + "INNER JOIN entity1 e1 ON e1.id = e.id AND e.tenant_id = 1 AND e.dept_id IN (10, 20) AND e1.tenant_id = 1 " +
  524 + "WHERE (e.id = ? OR e.name = ?)");
  525 +
  526 + // 没有 On 的 inner join
  527 + assertSql("SELECT * FROM entity,entity1 " +
  528 + "WHERE entity.id = entity1.id",
  529 + "SELECT * FROM entity, entity1 " +
  530 + "WHERE entity.id = entity1.id AND entity.tenant_id = 1 AND entity1.tenant_id = 1");
368 531 }
369 532  
370 533 }
... ...
yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/util/MyBatisUtils.java
... ... @@ -4,6 +4,7 @@ import cn.hutool.core.collection.CollectionUtil;
4 4 import cn.iocoder.yudao.framework.common.pojo.PageParam;
5 5 import cn.iocoder.yudao.framework.common.pojo.SortingField;
6 6 import com.baomidou.mybatisplus.core.metadata.OrderItem;
  7 +import com.baomidou.mybatisplus.core.toolkit.StringPool;
7 8 import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
8 9 import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor;
9 10 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
... ... @@ -78,7 +79,10 @@ public class MyBatisUtils {
78 79 * @return Column 对象
79 80 */
80 81 public static Column buildColumn(String tableName, Alias tableAlias, String column) {
81   - return new Column(tableAlias != null ? tableAlias.getName() + "." + column : column);
  82 + if (tableAlias != null) {
  83 + tableName = tableAlias.getName();
  84 + }
  85 + return new Column(tableName + StringPool.DOT + column);
82 86 }
83 87  
84 88 }
... ...