From e2685b895d30d78897fcd3c07ceb2e72283703dc Mon Sep 17 00:00:00 2001
From: caohdgege <1170050364@qq.com>
Date: Mon, 2 Nov 2020 13:55:25 +0800
Subject: [PATCH] bugfix: fix the exception when limit condition contains
 VariantRefExpr (#3246)

---
 .../rm/datasource/exec/DeleteExecutor.java    |  4 +++-
 .../rm/datasource/exec/UpdateExecutor.java    |  4 +++-
 .../io/seata/sqlparser/WhereRecognizer.java   |  2 +-
 .../druid/mysql/BaseMySQLRecognizer.java      | 23 +++++++++++++++----
 .../druid/mysql/MySQLDeleteRecognizer.java    |  4 ++--
 .../druid/mysql/MySQLUpdateRecognizer.java    |  4 ++--
 .../druid/MySQLDeleteRecognizerTest.java      | 13 ++++++++---
 7 files changed, 39 insertions(+), 15 deletions(-)

diff --git a/rm-datasource/src/main/java/io/seata/rm/datasource/exec/DeleteExecutor.java b/rm-datasource/src/main/java/io/seata/rm/datasource/exec/DeleteExecutor.java
index 4f67d1e40..e48e86589 100644
--- a/rm-datasource/src/main/java/io/seata/rm/datasource/exec/DeleteExecutor.java
+++ b/rm-datasource/src/main/java/io/seata/rm/datasource/exec/DeleteExecutor.java
@@ -26,6 +26,7 @@ import io.seata.rm.datasource.ColumnUtils;
 import io.seata.rm.datasource.StatementProxy;
 import io.seata.rm.datasource.sql.struct.TableMeta;
 import io.seata.rm.datasource.sql.struct.TableRecords;
+import io.seata.sqlparser.ParametersHolder;
 import io.seata.sqlparser.SQLDeleteRecognizer;
 import io.seata.sqlparser.SQLRecognizer;
 
@@ -70,7 +71,8 @@ public class DeleteExecutor<T, S extends Statement> extends AbstractDMLBaseExecu
         if (StringUtils.isNotBlank(orderBy)) {
             suffix.append(orderBy);
         }
-        String limit = visitor.getLimit();
+        ParametersHolder parametersHolder = statementProxy instanceof ParametersHolder ? (ParametersHolder)statementProxy : null;
+        String limit = visitor.getLimit(parametersHolder, paramAppenderList);
         if (StringUtils.isNotBlank(limit)) {
             suffix.append(limit);
         }
diff --git a/rm-datasource/src/main/java/io/seata/rm/datasource/exec/UpdateExecutor.java b/rm-datasource/src/main/java/io/seata/rm/datasource/exec/UpdateExecutor.java
index b899aa74e..e2b39b3cf 100644
--- a/rm-datasource/src/main/java/io/seata/rm/datasource/exec/UpdateExecutor.java
+++ b/rm-datasource/src/main/java/io/seata/rm/datasource/exec/UpdateExecutor.java
@@ -34,6 +34,7 @@ import io.seata.rm.datasource.SqlGenerateUtils;
 import io.seata.rm.datasource.StatementProxy;
 import io.seata.rm.datasource.sql.struct.TableMeta;
 import io.seata.rm.datasource.sql.struct.TableRecords;
+import io.seata.sqlparser.ParametersHolder;
 import io.seata.sqlparser.SQLRecognizer;
 import io.seata.sqlparser.SQLUpdateRecognizer;
 
@@ -85,7 +86,8 @@ public class UpdateExecutor<T, S extends Statement> extends AbstractDMLBaseExecu
         if (StringUtils.isNotBlank(orderBy)) {
             suffix.append(orderBy);
         }
-        String limit = recognizer.getLimit();
+        ParametersHolder parametersHolder = statementProxy instanceof ParametersHolder ? (ParametersHolder)statementProxy : null;
+        String limit = recognizer.getLimit(parametersHolder, paramAppenderList);
         if (StringUtils.isNotBlank(limit)) {
             suffix.append(limit);
         }
diff --git a/sqlparser/seata-sqlparser-core/src/main/java/io/seata/sqlparser/WhereRecognizer.java b/sqlparser/seata-sqlparser-core/src/main/java/io/seata/sqlparser/WhereRecognizer.java
index a010f644b..28f2b4db5 100644
--- a/sqlparser/seata-sqlparser-core/src/main/java/io/seata/sqlparser/WhereRecognizer.java
+++ b/sqlparser/seata-sqlparser-core/src/main/java/io/seata/sqlparser/WhereRecognizer.java
@@ -46,7 +46,7 @@ public interface WhereRecognizer extends SQLRecognizer {
      *
      * @return The limit SQL.
      */
-    default String getLimit() {
+    default String getLimit(ParametersHolder parametersHolder, ArrayList<List<Object>> paramAppenderList) {
         return null;
     }
 
diff --git a/sqlparser/seata-sqlparser-druid/src/main/java/io/seata/sqlparser/druid/mysql/BaseMySQLRecognizer.java b/sqlparser/seata-sqlparser-druid/src/main/java/io/seata/sqlparser/druid/mysql/BaseMySQLRecognizer.java
index 58ebe6cfa..43c94b7e7 100644
--- a/sqlparser/seata-sqlparser-druid/src/main/java/io/seata/sqlparser/druid/mysql/BaseMySQLRecognizer.java
+++ b/sqlparser/seata-sqlparser-druid/src/main/java/io/seata/sqlparser/druid/mysql/BaseMySQLRecognizer.java
@@ -17,6 +17,7 @@ package io.seata.sqlparser.druid.mysql;
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Map;
 import java.util.Objects;
 
 import com.alibaba.druid.sql.ast.SQLExpr;
@@ -96,7 +97,7 @@ public abstract class BaseMySQLRecognizer extends BaseRecognizer {
         return sb.toString();
     }
 
-    protected String getLimit(SQLStatement sqlStatement, SQLType sqlType) {
+    protected String getLimit(SQLStatement sqlStatement, SQLType sqlType, ParametersHolder parametersHolder, ArrayList<List<Object>> paramAppenderList) {
         SQLLimit limit = null;
         if (SQLType.UPDATE == sqlType) {
             limit = ((MySqlUpdateStatement)sqlStatement).getLimit();
@@ -107,12 +108,24 @@ public abstract class BaseMySQLRecognizer extends BaseRecognizer {
             StringBuilder builder = new StringBuilder(" LIMIT ");
             SQLIntegerExpr expr;
             if (limit.getOffset() != null) {
-                expr = (SQLIntegerExpr)limit.getOffset();
-                builder.append(expr.getNumber()).append(",");
+                if (limit.getOffset() instanceof SQLVariantRefExpr) {
+                    builder.append("?,");
+                    Map<Integer, ArrayList<Object>> parameters = parametersHolder.getParameters();
+                    paramAppenderList.add(parameters.get(parameters.size() - 1));
+                } else {
+                    expr = (SQLIntegerExpr)limit.getOffset();
+                    builder.append(expr.getNumber()).append(",");
+                }
             }
             if (limit.getRowCount() != null) {
-                expr = (SQLIntegerExpr)limit.getRowCount();
-                builder.append(expr.getNumber());
+                if (limit.getRowCount() instanceof SQLVariantRefExpr) {
+                    builder.append("?");
+                    Map<Integer, ArrayList<Object>> parameters = parametersHolder.getParameters();
+                    paramAppenderList.add(parameters.get(parameters.size()));
+                } else {
+                    expr = (SQLIntegerExpr)limit.getRowCount();
+                    builder.append(expr.getNumber());
+                }
             }
             return builder.toString();
         }
diff --git a/sqlparser/seata-sqlparser-druid/src/main/java/io/seata/sqlparser/druid/mysql/MySQLDeleteRecognizer.java b/sqlparser/seata-sqlparser-druid/src/main/java/io/seata/sqlparser/druid/mysql/MySQLDeleteRecognizer.java
index 62c3a099f..6dcf19870 100644
--- a/sqlparser/seata-sqlparser-druid/src/main/java/io/seata/sqlparser/druid/mysql/MySQLDeleteRecognizer.java
+++ b/sqlparser/seata-sqlparser-druid/src/main/java/io/seata/sqlparser/druid/mysql/MySQLDeleteRecognizer.java
@@ -94,8 +94,8 @@ public class MySQLDeleteRecognizer extends BaseMySQLRecognizer implements SQLDel
     }
 
     @Override
-    public String getLimit() {
-        return super.getLimit(ast, getSQLType());
+    public String getLimit(ParametersHolder parametersHolder, ArrayList<List<Object>> paramAppenderList) {
+        return super.getLimit(ast, getSQLType(), parametersHolder, paramAppenderList);
     }
 
     @Override
diff --git a/sqlparser/seata-sqlparser-druid/src/main/java/io/seata/sqlparser/druid/mysql/MySQLUpdateRecognizer.java b/sqlparser/seata-sqlparser-druid/src/main/java/io/seata/sqlparser/druid/mysql/MySQLUpdateRecognizer.java
index 6eac619a7..5f01571da 100644
--- a/sqlparser/seata-sqlparser-druid/src/main/java/io/seata/sqlparser/druid/mysql/MySQLUpdateRecognizer.java
+++ b/sqlparser/seata-sqlparser-druid/src/main/java/io/seata/sqlparser/druid/mysql/MySQLUpdateRecognizer.java
@@ -132,8 +132,8 @@ public class MySQLUpdateRecognizer extends BaseMySQLRecognizer implements SQLUpd
     }
 
     @Override
-    public String getLimit() {
-        return super.getLimit(ast, getSQLType());
+    public String getLimit(ParametersHolder parametersHolder, ArrayList<List<Object>> paramAppenderList) {
+        return super.getLimit(ast, getSQLType(), parametersHolder, paramAppenderList);
     }
 
     @Override
diff --git a/sqlparser/seata-sqlparser-druid/src/test/java/io/seata/sqlparser/druid/MySQLDeleteRecognizerTest.java b/sqlparser/seata-sqlparser-druid/src/test/java/io/seata/sqlparser/druid/MySQLDeleteRecognizerTest.java
index f5bbb3783..dc19801b9 100644
--- a/sqlparser/seata-sqlparser-druid/src/test/java/io/seata/sqlparser/druid/MySQLDeleteRecognizerTest.java
+++ b/sqlparser/seata-sqlparser-druid/src/test/java/io/seata/sqlparser/druid/MySQLDeleteRecognizerTest.java
@@ -49,10 +49,17 @@ public class MySQLDeleteRecognizerTest extends AbstractRecognizerTest {
      */
     @Test
     public void deleteRecognizerTest_0() {
+        ParametersHolder parametersHolder = new ParametersHolder() {
+            @Override
+            public Map<Integer,ArrayList<Object>> getParameters() {
+                return Collections.EMPTY_MAP;
+            }
+        };
 
         String sql = "DELETE FROM t1 WHERE id = 'id1' order by id asc,name desc limit 1,2";
 
         SQLStatement statement = getSQLStatement(sql);
+        ArrayList<List<Object>> paramAppenderList = new ArrayList<>();
 
         MySQLDeleteRecognizer mySQLDeleteRecognizer = new MySQLDeleteRecognizer(sql, statement);
         String orderBy = mySQLDeleteRecognizer.getOrderBy();
@@ -60,18 +67,18 @@ public class MySQLDeleteRecognizerTest extends AbstractRecognizerTest {
         Assertions.assertEquals(sql, mySQLDeleteRecognizer.getOriginalSQL());
         Assertions.assertEquals("t1", mySQLDeleteRecognizer.getTableName());
         Assertions.assertEquals("id = 'id1'", mySQLDeleteRecognizer.getWhereCondition());
-        String limit = mySQLDeleteRecognizer.getLimit();
+        String limit = mySQLDeleteRecognizer.getLimit(parametersHolder, paramAppenderList);
         Assertions.assertEquals(" LIMIT 1,2", limit);
         sql = "DELETE FROM t1 WHERE id > 1 order by id ,name desc limit 1";
         statement = getSQLStatement(sql);
         mySQLDeleteRecognizer = new MySQLDeleteRecognizer(sql, statement);
         orderBy = mySQLDeleteRecognizer.getOrderBy();
         Assertions.assertTrue(orderBy.equalsIgnoreCase(" order by id,name desc"));
-        Assertions.assertEquals(" LIMIT 1", mySQLDeleteRecognizer.getLimit());
+        Assertions.assertEquals(" LIMIT 1", mySQLDeleteRecognizer.getLimit(parametersHolder, paramAppenderList));
         sql = "DELETE FROM t1 WHERE id > 1";
         statement = getSQLStatement(sql);
         mySQLDeleteRecognizer = new MySQLDeleteRecognizer(sql, statement);
-        Assertions.assertEquals(null, mySQLDeleteRecognizer.getLimit());
+        Assertions.assertEquals(null, mySQLDeleteRecognizer.getLimit(parametersHolder, paramAppenderList));
         orderBy = mySQLDeleteRecognizer.getOrderBy();
         Assertions.assertEquals(null, mySQLDeleteRecognizer.getOrderBy());
 
-- 
GitLab