Pārlūkot izejas kodu

Merge pull request #4035 from Hccake/3.0

:bug: fix gitee I4FP6E, right join bug
qmdx 3 gadi atpakaļ
vecāks
revīzija
2884cd272d

+ 174 - 65
mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/plugins/inner/TenantLineInnerInterceptor.java

@@ -41,7 +41,10 @@ import org.apache.ibatis.session.RowBounds;
 
 import java.sql.Connection;
 import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.Deque;
 import java.util.LinkedList;
 import java.util.List;
@@ -234,27 +237,55 @@ public class TenantLineInnerInterceptor extends JsqlParserSupport implements Inn
      * 处理 PlainSelect
      */
     protected void processPlainSelect(PlainSelect plainSelect) {
-        FromItem fromItem = plainSelect.getFromItem();
-        Expression where = plainSelect.getWhere();
-        processWhereSubSelect(where);
-        if (fromItem instanceof Table) {
-            Table fromTable = (Table) fromItem;
-            if (!tenantLineHandler.ignoreTable(fromTable.getName())) {
-                //#1186 github
-                plainSelect.setWhere(builderExpression(where, fromTable));
-            }
-        } else {
-            processFromItem(fromItem);
-        }
         //#3087 github
         List<SelectItem> selectItems = plainSelect.getSelectItems();
         if (CollectionUtils.isNotEmpty(selectItems)) {
             selectItems.forEach(this::processSelectItem);
         }
+
+        // 处理 where 中的子查询
+        Expression where = plainSelect.getWhere();
+        processWhereSubSelect(where);
+
+        // 处理 fromItem
+        FromItem fromItem = plainSelect.getFromItem();
+        List<Table> list = processFromItem(fromItem);
+        List<Table> mainTables = new ArrayList<>(list);
+
+        // 处理 join
         List<Join> joins = plainSelect.getJoins();
         if (CollectionUtils.isNotEmpty(joins)) {
-            processJoins(joins);
+            mainTables = processJoins(mainTables, joins);
+        }
+
+        // 当有 mainTable 时,进行 where 条件追加
+        if (CollectionUtils.isNotEmpty(mainTables)) {
+            plainSelect.setWhere(builderExpression(where, mainTables));
+        }
+    }
+
+    private List<Table> processFromItem(FromItem fromItem) {
+        // 处理括号括起来的表达式
+        while (fromItem instanceof ParenthesisFromItem) {
+            fromItem = ((ParenthesisFromItem) fromItem).getFromItem();
+        }
+
+        List<Table> mainTables = new ArrayList<>();
+        // 无 join 时的处理逻辑
+        if (fromItem instanceof Table) {
+            Table fromTable = (Table) fromItem;
+            if (!tenantLineHandler.ignoreTable(fromTable.getName())) {
+                mainTables.add(fromTable);
+            }
+        } else if (fromItem instanceof SubJoin) {
+            // SubJoin 类型则还需要添加上 where 条件
+            List<Table> tables = processSubJoin((SubJoin) fromItem);
+            mainTables.addAll(tables);
+        } else {
+            // 处理下 fromItem
+            processOtherFromItem(fromItem);
         }
+        return mainTables;
     }
 
     /**
@@ -282,7 +313,7 @@ public class TenantLineInnerInterceptor extends JsqlParserSupport implements Inn
             return;
         }
         if (where instanceof FromItem) {
-            processFromItem((FromItem) where);
+            processOtherFromItem((FromItem) where);
             return;
         }
         if (where.toString().indexOf("SELECT") > 0) {
@@ -348,16 +379,13 @@ public class TenantLineInnerInterceptor extends JsqlParserSupport implements Inn
     /**
      * 处理子查询等
      */
-    protected void processFromItem(FromItem fromItem) {
-        if (fromItem instanceof SubJoin) {
-            SubJoin subJoin = (SubJoin) fromItem;
-            if (subJoin.getJoinList() != null) {
-                processJoins(subJoin.getJoinList());
-            }
-            if (subJoin.getLeft() != null) {
-                processFromItem(subJoin.getLeft());
-            }
-        } else if (fromItem instanceof SubSelect) {
+    protected void processOtherFromItem(FromItem fromItem) {
+        // 去除括号
+        while (fromItem instanceof ParenthesisFromItem) {
+            fromItem = ((ParenthesisFromItem) fromItem).getFromItem();
+        }
+
+        if (fromItem instanceof SubSelect) {
             SubSelect subSelect = (SubSelect) fromItem;
             if (subSelect.getSelectBody() != null) {
                 processSelectBody(subSelect.getSelectBody());
@@ -375,82 +403,160 @@ public class TenantLineInnerInterceptor extends JsqlParserSupport implements Inn
         }
     }
 
+    /**
+     * 处理 sub join
+     *
+     * @param subJoin subJoin
+     * @return Table subJoin 中的主表
+     */
+    private List<Table> processSubJoin(SubJoin subJoin) {
+        List<Table> mainTables = new ArrayList<>();
+        if (subJoin.getJoinList() != null) {
+            List<Table> list = processFromItem(subJoin.getLeft());
+            mainTables.addAll(list);
+            mainTables = processJoins(mainTables, subJoin.getJoinList());
+        }
+        return mainTables;
+    }
+
     /**
      * 处理 joins
      *
-     * @param joins join 集合
+     * @param mainTables 可以为 null
+     * @param joins      join 集合
+     * @return List<Table> 右连接查询的 Table 列表
      */
-    private void processJoins(List<Join> joins) {
+    private List<Table> processJoins(List<Table> mainTables, List<Join> joins) {
+        if (mainTables == null) {
+            mainTables = new ArrayList<>();
+        }
+
+        // join 表达式中最终的主表
+        Table mainTable = null;
+        // 当前 join 的左表
+        Table leftTable = null;
+        if (mainTables.size() == 1) {
+            mainTable = mainTables.get(0);
+            leftTable = mainTable;
+        }
+
         //对于 on 表达式写在最后的 join,需要记录下前面多个 on 的表名
-        Deque<Table> tables = new LinkedList<>();
+        Deque<List<Table>> onTableDeque = new LinkedList<>();
         for (Join join : joins) {
             // 处理 on 表达式
-            FromItem fromItem = join.getRightItem();
-            if (fromItem instanceof Table) {
-                Table fromTable = (Table) fromItem;
+            FromItem joinItem = join.getRightItem();
+
+            // 获取当前 join 的表,subJoint 可以看作是一张表
+            List<Table> joinTables = null;
+            if (joinItem instanceof Table) {
+                joinTables = new ArrayList<>();
+                joinTables.add((Table) joinItem);
+            } else if (joinItem instanceof SubJoin) {
+                joinTables = processSubJoin((SubJoin) joinItem);
+            }
+
+            if (joinTables != null) {
+
+                // 如果是隐式内连接
+                if (join.isSimple()) {
+                    mainTables.addAll(joinTables);
+                    continue;
+                }
+
+                // 当前表是否忽略
+                Table joinTable = joinTables.get(0);
+                boolean joinTableNeedIgnore = tenantLineHandler.ignoreTable(joinTable.getName());
+
+                List<Table> onTables = null;
+                // 如果不要忽略,且是右连接,则记录下当前表
+                if (join.isRight()) {
+                    mainTable = joinTableNeedIgnore ? null : joinTable;
+                    if (leftTable != null) {
+                        onTables = Collections.singletonList(leftTable);
+                    }
+                } else if (join.isLeft()) {
+                    if (!joinTableNeedIgnore) {
+                        onTables = Collections.singletonList(joinTable);
+                    }
+                } else if (join.isInner()) {
+                    if (mainTable == null) {
+                        onTables = Collections.singletonList(joinTable);
+                    } else {
+                        onTables = Arrays.asList(mainTable, joinTable);
+                    }
+                    mainTable = null;
+                }
+                mainTables = new ArrayList<>();
+                if (mainTable != null) {
+                    mainTables.add(mainTable);
+                }
+
                 // 获取 join 尾缀的 on 表达式列表
                 Collection<Expression> originOnExpressions = join.getOnExpressions();
                 // 正常 join on 表达式只有一个,立刻处理
-                if (originOnExpressions.size() == 1) {
-                    processJoin(join);
+                if (originOnExpressions.size() == 1 && onTables != null) {
+                    List<Expression> onExpressions = new LinkedList<>();
+                    onExpressions.add(builderExpression(originOnExpressions.iterator().next(), onTables));
+                    join.setOnExpressions(onExpressions);
+                    leftTable = joinTable;
                     continue;
                 }
-                // 当前表是否忽略
-                boolean needIgnore = tenantLineHandler.ignoreTable(fromTable.getName());
                 // 表名压栈,忽略的表压入 null,以便后续不处理
-                tables.push(needIgnore ? null : fromTable);
+                onTableDeque.push(onTables);
                 // 尾缀多个 on 表达式的时候统一处理
                 if (originOnExpressions.size() > 1) {
                     Collection<Expression> onExpressions = new LinkedList<>();
                     for (Expression originOnExpression : originOnExpressions) {
-                        Table currentTable = tables.poll();
-                        if (currentTable == null) {
+                        List<Table> currentTableList = onTableDeque.poll();
+                        if (CollectionUtils.isEmpty(currentTableList)) {
                             onExpressions.add(originOnExpression);
                         } else {
-                            onExpressions.add(builderExpression(originOnExpression, currentTable));
+                            onExpressions.add(builderExpression(originOnExpression, currentTableList));
                         }
                     }
                     join.setOnExpressions(onExpressions);
                 }
+                leftTable = joinTable;
             } else {
-                // 处理右边连接的子表达式
-                processFromItem(fromItem);
+                processOtherFromItem(joinItem);
+                leftTable = null;
             }
+
         }
+
+        return mainTables;
     }
 
     /**
-     * 处理联接语句
+     * 处理条件
      */
-    protected void processJoin(Join join) {
-        if (join.getRightItem() instanceof Table) {
-            Table fromTable = (Table) join.getRightItem();
-            if (tenantLineHandler.ignoreTable(fromTable.getName())) {
-                // 过滤退出执行
-                return;
+    protected Expression builderExpression(Expression currentExpression, List<Table> tables) {
+        // 没有表需要处理直接返回
+        if (CollectionUtils.isEmpty(tables)) {
+            return currentExpression;
+        }
+        // 租户
+        Expression tenantId = tenantLineHandler.getTenantId();
+        // 构造每张表的条件
+        List<EqualsTo> equalsTos = tables.stream()
+            .map(item -> new EqualsTo(getAliasColumn(item), tenantId))
+            .collect(Collectors.toList());
+        // 注入的表达式
+        Expression injectExpression = equalsTos.get(0);
+        // 如果有多表,则用 and 连接
+        if (equalsTos.size() > 1) {
+            for (int i = 1; i < equalsTos.size(); i++) {
+                injectExpression = new AndExpression(injectExpression, equalsTos.get(i));
             }
-            // 走到这里说明 on 表达式肯定只有一个
-            Collection<Expression> originOnExpressions = join.getOnExpressions();
-            List<Expression> onExpressions = new LinkedList<>();
-            onExpressions.add(builderExpression(originOnExpressions.iterator().next(), fromTable));
-            join.setOnExpressions(onExpressions);
         }
-    }
 
-    /**
-     * 处理条件
-     */
-    protected Expression builderExpression(Expression currentExpression, Table table) {
-        EqualsTo equalsTo = new EqualsTo();
-        equalsTo.setLeftExpression(this.getAliasColumn(table));
-        equalsTo.setRightExpression(tenantLineHandler.getTenantId());
         if (currentExpression == null) {
-            return equalsTo;
+            return injectExpression;
         }
         if (currentExpression instanceof OrExpression) {
-            return new AndExpression(new Parenthesis(currentExpression), equalsTo);
+            return new AndExpression(new Parenthesis(currentExpression), injectExpression);
         } else {
-            return new AndExpression(currentExpression, equalsTo);
+            return new AndExpression(currentExpression, injectExpression);
         }
     }
 
@@ -463,10 +569,13 @@ public class TenantLineInnerInterceptor extends JsqlParserSupport implements Inn
      */
     protected Column getAliasColumn(Table table) {
         StringBuilder column = new StringBuilder();
+        // 为了兼容隐式内连接,没有别名时条件就需要加上表名
         if (table.getAlias() != null) {
-            column.append(table.getAlias().getName()).append(StringPool.DOT);
+            column.append(table.getAlias().getName());
+        } else {
+            column.append(table.getName());
         }
-        column.append(tenantLineHandler.getTenantIdColumn());
+        column.append(StringPool.DOT).append(tenantLineHandler.getTenantIdColumn());
         return new Column(column.toString());
     }
 

+ 168 - 26
mybatis-plus-extension/src/test/java/com/baomidou/mybatisplus/extension/plugins/inner/TenantLineInnerInterceptorTest.java

@@ -38,48 +38,48 @@ class TenantLineInnerInterceptorTest {
             "INSERT INTO entity (id, name, tenant_id) VALUES (?, ?, ?)");
         // insert into select
         assertSql("insert into entity (id,name) select id,name from entity2",
-            "INSERT INTO entity (id, name, tenant_id) SELECT id, name, tenant_id FROM entity2 WHERE tenant_id = 1");
+            "INSERT INTO entity (id, name, tenant_id) SELECT id, name, tenant_id FROM entity2 WHERE entity2.tenant_id = 1");
 
         assertSql("insert into entity (id,name) select * from entity2",
-            "INSERT INTO entity (id, name, tenant_id) SELECT * FROM entity2 WHERE tenant_id = 1");
+            "INSERT INTO entity (id, name, tenant_id) SELECT * FROM entity2 WHERE entity2.tenant_id = 1");
 
         assertSql("insert into entity (id,name) select id,name from (select id,name from entity3) t",
-            "INSERT INTO entity (id, name, tenant_id) SELECT id, name, tenant_id FROM (SELECT id, name, tenant_id FROM entity3 WHERE tenant_id = 1) t");
+            "INSERT INTO entity (id, name, tenant_id) SELECT id, name, tenant_id FROM (SELECT id, name, tenant_id FROM entity3 WHERE entity3.tenant_id = 1) t");
 
         assertSql("insert into entity (id,name) select * from (select id,name from entity3) t",
-            "INSERT INTO entity (id, name, tenant_id) SELECT * FROM (SELECT id, name, tenant_id FROM entity3 WHERE tenant_id = 1) t");
+            "INSERT INTO entity (id, name, tenant_id) SELECT * FROM (SELECT id, name, tenant_id FROM entity3 WHERE entity3.tenant_id = 1) t");
 
         assertSql("insert into entity (id,name) select t.* from (select id,name from entity3) t",
-            "INSERT INTO entity (id, name, tenant_id) SELECT t.* FROM (SELECT id, name, tenant_id FROM entity3 WHERE tenant_id = 1) t");
+            "INSERT INTO entity (id, name, tenant_id) SELECT t.* FROM (SELECT id, name, tenant_id FROM entity3 WHERE entity3.tenant_id = 1) t");
     }
 
     @Test
     void delete() {
         assertSql("delete from entity where id = ?",
-            "DELETE FROM entity WHERE tenant_id = 1 AND id = ?");
+            "DELETE FROM entity WHERE entity.tenant_id = 1 AND id = ?");
     }
 
     @Test
     void update() {
         assertSql("update entity set name = ? where id = ?",
-            "UPDATE entity SET name = ? WHERE tenant_id = 1 AND id = ?");
+            "UPDATE entity SET name = ? WHERE entity.tenant_id = 1 AND id = ?");
     }
 
     @Test
     void selectSingle() {
         // 单表
         assertSql("select * from entity where id = ?",
-            "SELECT * FROM entity WHERE id = ? AND tenant_id = 1");
+            "SELECT * FROM entity WHERE id = ? AND entity.tenant_id = 1");
 
         assertSql("select * from entity where id = ? or name = ?",
-            "SELECT * FROM entity WHERE (id = ? OR name = ?) AND tenant_id = 1");
+            "SELECT * FROM entity WHERE (id = ? OR name = ?) AND entity.tenant_id = 1");
 
         assertSql("SELECT * FROM entity WHERE (id = ? OR name = ?)",
-            "SELECT * FROM entity WHERE (id = ? OR name = ?) AND tenant_id = 1");
+            "SELECT * FROM entity WHERE (id = ? OR name = ?) AND entity.tenant_id = 1");
 
         /* not */
         assertSql("SELECT * FROM entity WHERE not (id = ? OR name = ?)",
-            "SELECT * FROM entity WHERE NOT (id = ? OR name = ?) AND tenant_id = 1");
+            "SELECT * FROM entity WHERE NOT (id = ? OR name = ?) AND entity.tenant_id = 1");
     }
 
     @Test
@@ -178,6 +178,14 @@ class TenantLineInnerInterceptorTest {
             "SELECT * FROM entity e " +
                 "LEFT JOIN entity1 e1 ON e1.id = e.id AND e1.tenant_id = 1 " +
                 "WHERE (e.id = ? OR e.name = ?) AND e.tenant_id = 1");
+
+        assertSql("SELECT * FROM entity e " +
+                "left join entity1 e1 on e1.id = e.id " +
+                "left join entity2 e2 on e1.id = e2.id",
+            "SELECT * FROM entity e " +
+                "LEFT JOIN entity1 e1 ON e1.id = e.id AND e1.tenant_id = 1 " +
+                "LEFT JOIN entity2 e2 ON e1.id = e2.id AND e2.tenant_id = 1 " +
+                "WHERE e.tenant_id = 1");
     }
 
     @Test
@@ -186,17 +194,125 @@ class TenantLineInnerInterceptorTest {
         assertSql("SELECT * FROM entity e " +
                 "right join entity1 e1 on e1.id = e.id",
             "SELECT * FROM entity e " +
-                "RIGHT JOIN entity1 e1 ON e1.id = e.id AND e1.tenant_id = 1 " +
-                "WHERE e.tenant_id = 1");
+                "RIGHT JOIN entity1 e1 ON e1.id = e.id AND e.tenant_id = 1 " +
+                "WHERE e1.tenant_id = 1");
+
+        assertSql("SELECT * FROM with_as_1 e " +
+                "right join entity1 e1 on e1.id = e.id",
+            "SELECT * FROM with_as_1 e " +
+                "RIGHT JOIN entity1 e1 ON e1.id = e.id " +
+                "WHERE e1.tenant_id = 1");
 
         assertSql("SELECT * FROM entity e " +
                 "right join entity1 e1 on e1.id = e.id " +
                 "WHERE e.id = ? OR e.name = ?",
             "SELECT * FROM entity e " +
-                "RIGHT JOIN entity1 e1 ON e1.id = e.id AND e1.tenant_id = 1 " +
-                "WHERE (e.id = ? OR e.name = ?) AND e.tenant_id = 1");
+                "RIGHT JOIN entity1 e1 ON e1.id = e.id AND e.tenant_id = 1 " +
+                "WHERE (e.id = ? OR e.name = ?) AND e1.tenant_id = 1");
+
+        assertSql("SELECT * FROM entity e " +
+                "right join entity1 e1 on e1.id = e.id " +
+                "right join entity2 e2 on e1.id = e2.id ",
+            "SELECT * FROM entity e " +
+                "RIGHT JOIN entity1 e1 ON e1.id = e.id AND e.tenant_id = 1 " +
+                "RIGHT JOIN entity2 e2 ON e1.id = e2.id AND e1.tenant_id = 1 " +
+                "WHERE e2.tenant_id = 1");
     }
 
+    @Test
+    void selectMixJoin() {
+        assertSql("SELECT * FROM entity e " +
+                "right join entity1 e1 on e1.id = e.id " +
+                "left join entity2 e2 on e1.id = e2.id",
+            "SELECT * FROM entity e " +
+                "RIGHT JOIN entity1 e1 ON e1.id = e.id AND e.tenant_id = 1 " +
+                "LEFT JOIN entity2 e2 ON e1.id = e2.id AND e2.tenant_id = 1 " +
+                "WHERE e1.tenant_id = 1");
+
+        assertSql("SELECT * FROM entity e " +
+                "left join entity1 e1 on e1.id = e.id " +
+                "right join entity2 e2 on e1.id = e2.id",
+            "SELECT * FROM entity e " +
+                "LEFT JOIN entity1 e1 ON e1.id = e.id AND e1.tenant_id = 1 " +
+                "RIGHT JOIN entity2 e2 ON e1.id = e2.id AND e1.tenant_id = 1 " +
+                "WHERE e2.tenant_id = 1");
+
+        assertSql("SELECT * FROM entity e " +
+                "left join entity1 e1 on e1.id = e.id " +
+                "inner join entity2 e2 on e1.id = e2.id",
+            "SELECT * FROM entity e " +
+                "LEFT JOIN entity1 e1 ON e1.id = e.id AND e1.tenant_id = 1 " +
+                "INNER JOIN entity2 e2 ON e1.id = e2.id AND e.tenant_id = 1 AND e2.tenant_id = 1");
+    }
+
+
+    @Test
+    void selectJoinSubSelect() {
+        assertSql("select * from (select * from entity) e1 " +
+                "left join entity2 e2 on e1.id = e2.id",
+            "SELECT * FROM (SELECT * FROM entity WHERE entity.tenant_id = 1) e1 " +
+                "LEFT JOIN entity2 e2 ON e1.id = e2.id AND e2.tenant_id = 1");
+
+        assertSql("select * from entity1 e1 " +
+                "left join (select * from entity2) e2 " +
+                "on e1.id = e2.id",
+            "SELECT * FROM entity1 e1 " +
+                "LEFT JOIN (SELECT * FROM entity2 WHERE entity2.tenant_id = 1) e2 " +
+                "ON e1.id = e2.id " +
+                "WHERE e1.tenant_id = 1");
+    }
+
+    @Test
+    void selectSubJoin() {
+
+        assertSql("select * FROM " +
+                "(entity1 e1 right JOIN entity2 e2 ON e1.id = e2.id)",
+            "SELECT * FROM " +
+                "(entity1 e1 RIGHT JOIN entity2 e2 ON e1.id = e2.id AND e1.tenant_id = 1) " +
+                "WHERE e2.tenant_id = 1");
+
+        assertSql("select * FROM " +
+                "(entity1 e1 LEFT JOIN entity2 e2 ON e1.id = e2.id)",
+            "SELECT * FROM " +
+                "(entity1 e1 LEFT JOIN entity2 e2 ON e1.id = e2.id AND e2.tenant_id = 1) " +
+                "WHERE e1.tenant_id = 1");
+
+
+        assertSql("select * FROM " +
+                "(entity1 e1 LEFT JOIN entity2 e2 ON e1.id = e2.id) " +
+                "right join entity3 e3 on e1.id = e3.id",
+            "SELECT * FROM " +
+                "(entity1 e1 LEFT JOIN entity2 e2 ON e1.id = e2.id AND e2.tenant_id = 1) " +
+                "RIGHT JOIN entity3 e3 ON e1.id = e3.id AND e1.tenant_id = 1 " +
+                "WHERE e3.tenant_id = 1");
+
+
+        assertSql("select * FROM entity e " +
+                "LEFT JOIN (entity1 e1 right join entity2 e2 ON e1.id = e2.id) " +
+                "on e.id = e2.id",
+            "SELECT * FROM entity e " +
+                "LEFT JOIN (entity1 e1 RIGHT JOIN entity2 e2 ON e1.id = e2.id AND e1.tenant_id = 1) " +
+                "ON e.id = e2.id AND e2.tenant_id = 1 " +
+                "WHERE e.tenant_id = 1");
+
+        assertSql("select * FROM entity e " +
+                "LEFT JOIN (entity1 e1 left join entity2 e2 ON e1.id = e2.id) " +
+                "on e.id = e2.id",
+            "SELECT * FROM entity e " +
+                "LEFT JOIN (entity1 e1 LEFT JOIN entity2 e2 ON e1.id = e2.id AND e2.tenant_id = 1) " +
+                "ON e.id = e2.id AND e1.tenant_id = 1 " +
+                "WHERE e.tenant_id = 1");
+
+        assertSql("select * FROM entity e " +
+                "RIGHT JOIN (entity1 e1 left join entity2 e2 ON e1.id = e2.id) " +
+                "on e.id = e2.id",
+            "SELECT * FROM entity e " +
+                "RIGHT JOIN (entity1 e1 LEFT JOIN entity2 e2 ON e1.id = e2.id AND e2.tenant_id = 1) " +
+                "ON e.id = e2.id AND e.tenant_id = 1 " +
+                "WHERE e1.tenant_id = 1");
+    }
+
+
     @Test
     void selectLeftJoinMultipleTrailingOn() {
         // 多个 on 尾缀的
@@ -230,29 +346,55 @@ class TenantLineInnerInterceptorTest {
                 "inner join entity1 e1 on e1.id = e.id " +
                 "WHERE e.id = ? OR e.name = ?",
             "SELECT * FROM entity e " +
-                "INNER JOIN entity1 e1 ON e1.id = e.id AND e1.tenant_id = 1 " +
-                "WHERE (e.id = ? OR e.name = ?) AND e.tenant_id = 1");
+                "INNER JOIN entity1 e1 ON e1.id = e.id AND e.tenant_id = 1 AND e1.tenant_id = 1 " +
+                "WHERE e.id = ? OR e.name = ?");
 
         assertSql("SELECT * FROM entity e " +
                 "inner join entity1 e1 on e1.id = e.id " +
                 "WHERE (e.id = ? OR e.name = ?)",
             "SELECT * FROM entity e " +
-                "INNER JOIN entity1 e1 ON e1.id = e.id AND e1.tenant_id = 1 " +
-                "WHERE (e.id = ? OR e.name = ?) AND e.tenant_id = 1");
+                "INNER JOIN entity1 e1 ON e1.id = e.id AND e.tenant_id = 1 AND e1.tenant_id = 1 " +
+                "WHERE (e.id = ? OR e.name = ?)");
+
+        // 隐式内连接
+        assertSql("SELECT * FROM entity,entity1 " +
+                "WHERE entity.id = entity1.id",
+            "SELECT * FROM entity, entity1 " +
+                "WHERE entity.id = entity1.id AND entity.tenant_id = 1 AND entity1.tenant_id = 1");
+
+        // SubJoin with 隐式内连接
+        assertSql("SELECT * FROM (entity,entity1) " +
+                "WHERE entity.id = entity1.id",
+            "SELECT * FROM (entity, entity1) " +
+                "WHERE entity.id = entity1.id " +
+                "AND entity.tenant_id = 1 AND entity1.tenant_id = 1");
+
+        assertSql("SELECT * FROM ((entity,entity1),entity2) " +
+                "WHERE entity.id = entity1.id and entity.id = entity2.id",
+            "SELECT * FROM ((entity, entity1), entity2) " +
+                "WHERE entity.id = entity1.id AND entity.id = entity2.id " +
+                "AND entity.tenant_id = 1 AND entity1.tenant_id = 1 AND entity2.tenant_id = 1");
+
+        assertSql("SELECT * FROM (entity,(entity1,entity2)) " +
+                "WHERE entity.id = entity1.id and entity.id = entity2.id",
+            "SELECT * FROM (entity, (entity1, entity2)) " +
+                "WHERE entity.id = entity1.id AND entity.id = entity2.id " +
+                "AND entity.tenant_id = 1 AND entity1.tenant_id = 1 AND entity2.tenant_id = 1");
+
+        // 沙雕的括号写法
+        assertSql("SELECT * FROM (((entity,entity1))) " +
+                "WHERE entity.id = entity1.id",
+            "SELECT * FROM (((entity, entity1))) " +
+                "WHERE entity.id = entity1.id " +
+                "AND entity.tenant_id = 1 AND entity1.tenant_id = 1");
 
-        // 垃圾 inner join todo
-//        assertSql("SELECT * FROM entity,entity1 " +
-//                "WHERE entity.id = entity1.id",
-//            "SELECT * FROM entity e " +
-//                "INNER JOIN entity1 e1 ON e1.id = e.id AND e1.tenant_id = 1 " +
-//                "WHERE (e.id = ? OR e.name = ?) AND e.tenant_id = 1");
     }
 
 
     @Test
     void selectWithAs() {
         assertSql("with with_as_A as (select * from entity) select * from with_as_A",
-            "WITH with_as_A AS (SELECT * FROM entity WHERE tenant_id = 1) SELECT * FROM with_as_A");
+            "WITH with_as_A AS (SELECT * FROM entity WHERE entity.tenant_id = 1) SELECT * FROM with_as_A");
     }
 
     void assertSql(String sql, String targetSql) {