Jelajahi Sumber

Merge pull request #4590 from Lroyia/3.0

【FIXBUG】修复sybase在查询字段含子查询时,拼接分页SQL会取错FROM的位置的问题
qmdx 2 tahun lalu
induk
melakukan
5dd131e0e8

+ 62 - 2
mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/plugins/pagination/dialects/SybaseDialect.java

@@ -17,6 +17,12 @@ package com.baomidou.mybatisplus.extension.plugins.pagination.dialects;
 
 import com.baomidou.mybatisplus.extension.plugins.pagination.DialectModel;
 
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
 /**
  * sybase 数据库分页方言
  *
@@ -37,8 +43,10 @@ public class SybaseDialect implements IDialect {
 
     @Override
     public DialectModel buildPaginationSql(String originalSql, long offset, long limit) {
-        String tempSql = originalSql.toUpperCase();
-        int index = tempSql.indexOf(" FROM ");
+        int index = findMainFROM(originalSql);
+        if(index == -1){
+            index = originalSql.toUpperCase().indexOf(" FROM ");
+        }
         String sql = "select";
         if (hasTop) {
             sql += " top " + (offset + limit);
@@ -48,4 +56,56 @@ public class SybaseDialect implements IDialect {
         sql += "drop table #t ";
         return new DialectModel(sql, offset, offset + limit).setConsumerChain();
     }
+
+    /**
+     * 查找主查询的FROM位置
+     * @param sql   需要查找的SQL
+     * @return  FROM位置的起始下标
+     * @author lroyia
+     * @since  2022年6月15日 17:57:28
+     */
+    private int findMainFROM(String sql){
+        String tempSql = sql.toUpperCase();
+        tempSql = tempSql.replace("\n", " ").replace("\t", " ").replace("\r", " ");
+        Matcher select_ = Pattern.compile("SELECT ").matcher(tempSql);
+        Matcher from_ = Pattern.compile(" FROM ").matcher(tempSql);
+        List<Integer> selectIndex = new ArrayList<>(10);
+        List<Integer> fromIndex = new ArrayList<>(10);
+        while (select_.find()){
+            int start = select_.start();
+            if(start == 0 || tempSql.charAt(start - 1) == ' '|| tempSql.charAt(start - 1) == '('){
+                selectIndex.add(start);
+            }
+        }
+        while (from_.find()){
+            fromIndex.add(from_.start());
+        }
+
+        // 形成select与from的混合顺序下标列表
+        List<Integer> indexList = new ArrayList<>(20);
+        indexList.addAll(selectIndex);
+        indexList.addAll(fromIndex);
+        indexList.sort(Comparator.naturalOrder());
+        // 无法匹配有效下标
+        if(indexList.size() < 2){
+            return -1;
+        }
+        // 利用栈逻辑匹配select与from
+        int selectCount = 1;
+        for(int i = 1; i < indexList.size(); i++){
+            int each = indexList.get(i);
+            if(fromIndex.contains(each)){
+                // pointer弹栈
+                selectCount--;
+            }else{
+                // pointer压栈
+                selectCount++;
+            }
+            // from将全部select弹出,代表当前这个from为主要from
+            if(selectCount == 0){
+                return each;
+            }
+        }
+        return -1;
+    }
 }