This is an automated email from the ASF dual-hosted git repository.

lide pushed a commit to branch branch-2.0
in repository https://gitbox.apache.org/repos/asf/doris.git


The following commit(s) were added to refs/heads/branch-2.0 by this push:
     new 7b9b8c97ec6 [Feature](PreparedStatement) implement general server side 
prepared (#34247)
7b9b8c97ec6 is described below

commit 7b9b8c97ec6956590381d93a8a04386f38781d6c
Author: lihangyu <15605149...@163.com>
AuthorDate: Wed May 8 09:59:26 2024 +0800

    [Feature](PreparedStatement) implement general server side prepared (#34247)
---
 be/src/runtime/runtime_state.h                     |   5 +
 be/src/service/point_query_executor.cpp            |   1 -
 be/src/vec/sink/vresult_sink.cpp                   |  12 +-
 .../main/java/org/apache/doris/catalog/Type.java   |   4 +
 fe/fe-core/src/main/cup/sql_parser.cup             |   8 +-
 .../java/org/apache/doris/analysis/Analyzer.java   |  16 +-
 .../org/apache/doris/analysis/BinaryPredicate.java |   8 +-
 .../main/java/org/apache/doris/analysis/Expr.java  |   2 +-
 .../org/apache/doris/analysis/LiteralExpr.java     |  16 +-
 .../apache/doris/analysis/NativeInsertStmt.java    |  11 ++
 .../org/apache/doris/analysis/PlaceHolderExpr.java |   7 +-
 .../org/apache/doris/analysis/PrepareStmt.java     | 131 ++++++++++------
 .../org/apache/doris/analysis/StatementBase.java   |  14 +-
 .../org/apache/doris/analysis/StringLiteral.java   |   2 +
 .../java/org/apache/doris/catalog/OlapTable.java   |  11 +-
 .../java/org/apache/doris/mysql/MysqlProto.java    |   1 -
 .../org/apache/doris/planner/OlapScanNode.java     |  10 +-
 .../java/org/apache/doris/qe/ConnectProcessor.java |  54 +++----
 .../main/java/org/apache/doris/qe/Coordinator.java |   3 +
 .../java/org/apache/doris/qe/PointQueryExec.java   |   2 +-
 .../java/org/apache/doris/qe/SessionVariable.java  |   6 +
 .../java/org/apache/doris/qe/StmtExecutor.java     |  44 ++++--
 fe/fe-core/src/main/jflex/sql_scanner.flex         |   1 -
 .../data/point_query_p0/test_point_query.out       |  36 -----
 .../data/prepared_stmt_p0/prepared_stmt.out        |  55 +++++++
 .../org/apache/doris/regression/suite/Suite.groovy |   6 +
 .../apache/doris/regression/util/JdbcUtils.groovy  |   9 ++
 .../suites/point_query_p0/test_point_query.groovy  |  12 +-
 .../suites/prepared_stmt_p0/prepared_stmt.groovy   | 168 +++++++++++++++++++++
 29 files changed, 470 insertions(+), 185 deletions(-)

diff --git a/be/src/runtime/runtime_state.h b/be/src/runtime/runtime_state.h
index 1e7375cbb97..8e57638dee6 100644
--- a/be/src/runtime/runtime_state.h
+++ b/be/src/runtime/runtime_state.h
@@ -142,6 +142,11 @@ public:
                _query_options.enable_common_expr_pushdown;
     }
 
+    bool mysql_row_binary_format() const {
+        return _query_options.__isset.mysql_row_binary_format &&
+               _query_options.mysql_row_binary_format;
+    }
+
     Status query_status() {
         std::lock_guard<std::mutex> l(_process_status_lock);
         return _process_status;
diff --git a/be/src/service/point_query_executor.cpp 
b/be/src/service/point_query_executor.cpp
index 20a111b2b38..d9a31b8c9d6 100644
--- a/be/src/service/point_query_executor.cpp
+++ b/be/src/service/point_query_executor.cpp
@@ -41,7 +41,6 @@
 #include "vec/exprs/vexpr.h"
 #include "vec/exprs/vexpr_context.h"
 #include "vec/jsonb/serialize.h"
-#include "vec/sink/vmysql_result_writer.cpp"
 #include "vec/sink/vmysql_result_writer.h"
 
 namespace doris {
diff --git a/be/src/vec/sink/vresult_sink.cpp b/be/src/vec/sink/vresult_sink.cpp
index 4c731199d06..e71176a2bfc 100644
--- a/be/src/vec/sink/vresult_sink.cpp
+++ b/be/src/vec/sink/vresult_sink.cpp
@@ -91,10 +91,16 @@ Status VResultSink::prepare(RuntimeState* state) {
 
     // create writer based on sink type
     switch (_sink_type) {
-    case TResultSinkType::MYSQL_PROTOCAL:
-        _writer.reset(new (std::nothrow)
-                              VMysqlResultWriter(_sender.get(), 
_output_vexpr_ctxs, _profile));
+    case TResultSinkType::MYSQL_PROTOCAL: {
+        if (state->mysql_row_binary_format()) {
+            _writer.reset(new (std::nothrow) VMysqlResultWriter<true>(
+                    _sender.get(), _output_vexpr_ctxs, _profile));
+        } else {
+            _writer.reset(new (std::nothrow) VMysqlResultWriter<false>(
+                    _sender.get(), _output_vexpr_ctxs, _profile));
+        }
         break;
+    }
     default:
         return Status::InternalError("Unknown result sink type");
     }
diff --git a/fe/fe-common/src/main/java/org/apache/doris/catalog/Type.java 
b/fe/fe-common/src/main/java/org/apache/doris/catalog/Type.java
index ea10972faa2..e5f14a58127 100644
--- a/fe/fe-common/src/main/java/org/apache/doris/catalog/Type.java
+++ b/fe/fe-common/src/main/java/org/apache/doris/catalog/Type.java
@@ -354,6 +354,10 @@ public abstract class Type {
         return isScalarType(PrimitiveType.INVALID_TYPE);
     }
 
+    public boolean isUnsupported() {
+        return isScalarType(PrimitiveType.UNSUPPORTED);
+    }
+
     public boolean isValid() {
         return !isInvalid();
     }
diff --git a/fe/fe-core/src/main/cup/sql_parser.cup 
b/fe/fe-core/src/main/cup/sql_parser.cup
index d31934cc599..3cd8b66b543 100644
--- a/fe/fe-core/src/main/cup/sql_parser.cup
+++ b/fe/fe-core/src/main/cup/sql_parser.cup
@@ -1159,7 +1159,11 @@ stmt ::=
     | insert_overwrite_stmt : stmt
     {: RESULT = stmt; :}
     | update_stmt : stmt
-    {: RESULT = stmt; :}
+    {:
+        RESULT = stmt;
+        stmt.setPlaceHolders(parser.placeholder_expr_list);
+        parser.placeholder_expr_list.clear();
+    :}
     | backup_stmt : stmt
     {: RESULT = stmt; :}
     | restore_stmt : stmt
@@ -5340,7 +5344,7 @@ expr_or_default ::=
 prepare_stmt ::=
     KW_PREPARE variable_name:name KW_FROM select_stmt:s
     {:
-        RESULT = new PrepareStmt(s, name, false);
+        RESULT = new PrepareStmt(s, name);
         s.setPlaceHolders(parser.placeholder_expr_list);
         parser.placeholder_expr_list.clear();
     :}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/analysis/Analyzer.java 
b/fe/fe-core/src/main/java/org/apache/doris/analysis/Analyzer.java
index 2990e924698..ea6ed101242 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/analysis/Analyzer.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/Analyzer.java
@@ -593,6 +593,14 @@ public class Analyzer {
         return callDepth;
     }
 
+    public void setPrepareStmt(PrepareStmt stmt) {
+        prepareStmt = stmt;
+    }
+
+    public PrepareStmt getPrepareStmt() {
+        return prepareStmt;
+    }
+
     public void setInlineView(boolean inlineView) {
         isInlineView = inlineView;
     }
@@ -605,14 +613,6 @@ public class Analyzer {
         explicitViewAlias = alias;
     }
 
-    public void setPrepareStmt(PrepareStmt stmt) {
-        prepareStmt = stmt;
-    }
-
-    public PrepareStmt getPrepareStmt() {
-        return prepareStmt;
-    }
-
     public String getExplicitViewAlias() {
         return explicitViewAlias;
     }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/analysis/BinaryPredicate.java 
b/fe/fe-core/src/main/java/org/apache/doris/analysis/BinaryPredicate.java
index 3a193213171..ba895061e07 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/analysis/BinaryPredicate.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/BinaryPredicate.java
@@ -483,11 +483,13 @@ public class BinaryPredicate extends Predicate implements 
Writable {
     public void analyzeImpl(Analyzer analyzer) throws AnalysisException {
         super.analyzeImpl(analyzer);
         this.checkIncludeBitmap();
-        // Ignore placeholder
-        if (getChild(0) instanceof PlaceHolderExpr || getChild(1) instanceof 
PlaceHolderExpr) {
+        // Ignore placeholder, when it type is invalid.
+        // Invalid type could happen when analyze prepared point query select 
statement,
+        // since the value is occupied but not assigned
+        if ((getChild(0) instanceof PlaceHolderExpr && getChild(0).type == 
Type.UNSUPPORTED)
+                || (getChild(1) instanceof PlaceHolderExpr && getChild(1).type 
== Type.UNSUPPORTED)) {
             return;
         }
-
         for (Expr expr : children) {
             if (expr instanceof Subquery) {
                 Subquery subquery = (Subquery) expr;
diff --git a/fe/fe-core/src/main/java/org/apache/doris/analysis/Expr.java 
b/fe/fe-core/src/main/java/org/apache/doris/analysis/Expr.java
index f1c2d90270c..765d88148d1 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/analysis/Expr.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/Expr.java
@@ -1513,7 +1513,7 @@ public abstract class Expr extends TreeNode<Expr> 
implements ParseNode, Cloneabl
      *                           failure to convert a string literal to a date 
literal
      */
     public final Expr castTo(Type targetType) throws AnalysisException {
-        if (this instanceof PlaceHolderExpr && this.type.isInvalid()) {
+        if (this instanceof PlaceHolderExpr && this.type.isUnsupported()) {
             return this;
         }
         // If the targetType is NULL_TYPE then ignore the cast because 
NULL_TYPE
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/analysis/LiteralExpr.java 
b/fe/fe-core/src/main/java/org/apache/doris/analysis/LiteralExpr.java
index 00fd4b898d4..3d8c7f4f06d 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/analysis/LiteralExpr.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/LiteralExpr.java
@@ -320,14 +320,6 @@ public abstract class LiteralExpr extends Expr implements 
Comparable<LiteralExpr
         return getStringValue();
     }
 
-    // Parse from binary data, the format follows mysql binary protocal
-    // see 
https://dev.mysql.com/doc/dev/mysql-server/latest/page_protocol_binary_resultset.html.
-    // Return next offset
-    public void setupParamFromBinary(ByteBuffer data) {
-        Preconditions.checkState(false,
-                "should implement this in derived class. " + 
this.type.toSql());
-    }
-
     public static LiteralExpr getLiteralByMysqlType(int mysqlType) throws 
AnalysisException {
         switch (mysqlType) {
             // MYSQL_TYPE_TINY
@@ -438,4 +430,12 @@ public abstract class LiteralExpr extends Expr implements 
Comparable<LiteralExpr
             default: throw new AnalysisException("Wrong type from thrift;");
         }
     }
+
+    // Parse from binary data, the format follows mysql binary protocal
+    // see 
https://dev.mysql.com/doc/dev/mysql-server/latest/page_protocol_binary_resultset.html.
+    // Return next offset
+    public void setupParamFromBinary(ByteBuffer data) {
+        Preconditions.checkState(false,
+                "should implement this in derived class. " + 
this.type.toSql());
+    }
 }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/analysis/NativeInsertStmt.java 
b/fe/fe-core/src/main/java/org/apache/doris/analysis/NativeInsertStmt.java
index c016d7bd213..7f84dc54f6f 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/analysis/NativeInsertStmt.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/NativeInsertStmt.java
@@ -158,6 +158,17 @@ public class NativeInsertStmt extends InsertStmt {
         }
     }
 
+    public NativeInsertStmt(NativeInsertStmt other) {
+        super(other.label, null, null);
+        this.tblName = other.tblName;
+        this.targetPartitionNames = other.targetPartitionNames;
+        this.label = other.label;
+        this.queryStmt = other.queryStmt;
+        this.planHints = other.planHints;
+        this.targetColumnNames = other.targetColumnNames;
+        this.isValuesOrConstantSelect = other.isValuesOrConstantSelect;
+    }
+
     public NativeInsertStmt(InsertTarget target, String label, List<String> 
cols, InsertSource source,
             List<String> hints) {
         super(new LabelName(null, label), null, null);
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/analysis/PlaceHolderExpr.java 
b/fe/fe-core/src/main/java/org/apache/doris/analysis/PlaceHolderExpr.java
index a15e567daaa..55cf1f1bba0 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/analysis/PlaceHolderExpr.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/PlaceHolderExpr.java
@@ -39,7 +39,7 @@ public class PlaceHolderExpr extends LiteralExpr {
     int mysqlTypeCode = -1;
 
     public PlaceHolderExpr() {
-
+        type = Type.UNSUPPORTED;
     }
 
     public void setTypeCode(int mysqlTypeCode) {
@@ -164,7 +164,10 @@ public class PlaceHolderExpr extends LiteralExpr {
 
     @Override
     public String toSqlImpl() {
-        return getStringValue();
+        if (this.lExpr == null) {
+            return "?";
+        }
+        return "_placeholder_(" + this.lExpr.toSqlImpl() + ")";
     }
 
     @Override
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/analysis/PrepareStmt.java 
b/fe/fe-core/src/main/java/org/apache/doris/analysis/PrepareStmt.java
index f9bb9e5e058..383728b6fb4 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/analysis/PrepareStmt.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/PrepareStmt.java
@@ -39,41 +39,39 @@ import java.util.Map;
 import java.util.UUID;
 
 public class PrepareStmt extends StatementBase {
+    // We provide bellow types of prepared statement:
+    // NONE, which is not prepared
+    // FULL_PREPARED, which is real prepared, which will cache analyzed 
statement and planner
+    // STATEMENT, which only cache statement it self, but need to analyze each 
time executed.
+    public enum PreparedType {
+        NONE, FULL_PREPARED, STATEMENT
+    }
+
     private static final Logger LOG = LogManager.getLogger(PrepareStmt.class);
     private StatementBase inner;
     private String stmtName;
-
     // Cached for better CPU performance, since serialize DescriptorTable and
     // outputExprs are heavy work
     private ByteString serializedDescTable;
     private ByteString serializedOutputExpr;
-    private TDescriptorTable descTable;
+
 
     private UUID id;
-    // whether return binary protocol mysql row or not
-    private boolean binaryRowFormat;
-    int schemaVersion = -1;
-    OlapTable tbl;
-    ConnectContext context;
+    private int schemaVersion = -1;
+    private OlapTable tbl;
+    private ConnectContext context;
+    private PreparedType preparedType = PreparedType.STATEMENT;
+    boolean isPointQueryShortCircuit = false;
+
+    private TDescriptorTable descTable;
     // Serialized mysql Field, this could avoid serialize mysql field each 
time sendFields.
     // Since, serialize fields is too heavy when table is wide
     Map<String, byte[]> serializedFields =  Maps.newHashMap();
 
-    // We provide bellow types of prepared statement:
-    // NONE, which is not prepared
-    // FULL_PREPARED, which is really prepared, which will cache analyzed 
statement and planner
-    // STATEMENT, which only cache statement itself, but need to analyze each 
time executed
-    public enum PreparedType {
-        NONE, FULL_PREPARED, STATEMENT
-    }
-
-    private PreparedType preparedType = PreparedType.STATEMENT;
-
-    public PrepareStmt(StatementBase stmt, String name, boolean 
binaryRowFormat) {
+    public PrepareStmt(StatementBase stmt, String name) {
         this.inner = stmt;
         this.stmtName = name;
         this.id = UUID.randomUUID();
-        this.binaryRowFormat = binaryRowFormat;
     }
 
     public void setContext(ConnectContext ctx) {
@@ -81,7 +79,8 @@ public class PrepareStmt extends StatementBase {
     }
 
     public boolean needReAnalyze() {
-        if (preparedType == PreparedType.FULL_PREPARED && schemaVersion == 
tbl.getBaseSchemaVersion()) {
+        if (preparedType == PreparedType.FULL_PREPARED
+                    && schemaVersion == tbl.getBaseSchemaVersion()) {
             return false;
         }
         reset();
@@ -96,10 +95,6 @@ public class PrepareStmt extends StatementBase {
         return id;
     }
 
-    public boolean isBinaryProtocol() {
-        return binaryRowFormat;
-    }
-
     public byte[] getSerializedField(String colName) {
         return serializedFields.getOrDefault(colName, null);
     }
@@ -142,34 +137,53 @@ public class PrepareStmt extends StatementBase {
         return serializedOutputExpr;
     }
 
+    public boolean isPointQueryShortCircuit() {
+        return isPointQueryShortCircuit;
+    }
+
     @Override
     public void analyze(Analyzer analyzer) throws UserException {
+        // TODO support more Statement
+        if (!(inner instanceof SelectStmt) && !(inner instanceof 
NativeInsertStmt)) {
+            throw new UserException("Only support prepare SelectStmt or 
NativeInsertStmt");
+        }
+        analyzer.setPrepareStmt(this);
         if (inner instanceof SelectStmt) {
-            // Use tmpAnalyzer since selectStmt will be reAnalyzed
-            Analyzer tmpAnalyzer = new Analyzer(context.getEnv(), context);
+            // Try to use FULL_PREPARED to increase performance
             SelectStmt selectStmt = (SelectStmt) inner;
-            inner.analyze(tmpAnalyzer);
-            if (!selectStmt.checkAndSetPointQuery()) {
-                throw new UserException("Only support prepare SelectStmt point 
query now");
+            try {
+                // Use tmpAnalyzer since selectStmt will be reAnalyzed
+                Analyzer tmpAnalyzer = new Analyzer(context.getEnv(), context);
+                inner.analyze(tmpAnalyzer);
+                // Case 1 short circuit point query
+                if (selectStmt.checkAndSetPointQuery()) {
+                    tbl = (OlapTable) 
selectStmt.getTableRefs().get(0).getTable();
+                    schemaVersion = tbl.getBaseSchemaVersion();
+                    preparedType = PreparedType.FULL_PREPARED;
+                    isPointQueryShortCircuit = true;
+                    LOG.debug("using FULL_PREPARED prepared");
+                    return;
+                }
+            } catch (UserException e) {
+                LOG.debug("fallback to STATEMENT prepared, {}", e);
+            } finally {
+                // will be reanalyzed
+                selectStmt.reset();
+            }
+            // use session var to decide whether to use full prepared or let 
user client handle to do fail over
+            if (preparedType != PreparedType.FULL_PREPARED
+                    && 
!ConnectContext.get().getSessionVariable().enableServeSidePreparedStatement) {
+                throw new UserException("Failed to prepare statement"
+                                + "try to set 
enable_server_side_prepared_statement = true");
             }
-            tbl = (OlapTable) selectStmt.getTableRefs().get(0).getTable();
-            schemaVersion = tbl.getBaseSchemaVersion();
-            // reset will be reAnalyzed
-            selectStmt.reset();
-            analyzer.setPrepareStmt(this);
-            // tmpAnalyzer.setPrepareStmt(this);
-            preparedType = PreparedType.FULL_PREPARED;
         } else if (inner instanceof NativeInsertStmt) {
             LabelName label = ((NativeInsertStmt) inner).getLoadLabel();
-            if (label == null || Strings.isNullOrEmpty(label.getLabelName())) {
-                analyzer.setPrepareStmt(this);
-                preparedType = PreparedType.STATEMENT;
-            } else {
+            if (label != null && !Strings.isNullOrEmpty(label.getLabelName())) 
{
                 throw new UserException("Only support prepare InsertStmt 
without label now");
             }
-        } else {
-            throw new UserException("Only support prepare SelectStmt or 
InsertStmt now");
         }
+        preparedType = PreparedType.STATEMENT;
+        LOG.debug("using STATEMENT prepared");
     }
 
     public String getName() {
@@ -181,10 +195,6 @@ public class PrepareStmt extends StatementBase {
         return RedirectStatus.NO_FORWARD;
     }
 
-    public StatementBase getInnerStmt() {
-        return inner;
-    }
-
     public List<PlaceHolderExpr> placeholders() {
         return inner.getPlaceHolders();
     }
@@ -193,6 +203,10 @@ public class PrepareStmt extends StatementBase {
         return inner.getPlaceHolders().size();
     }
 
+    public PreparedType getPreparedType() {
+        return preparedType;
+    }
+
     public List<Expr> getPlaceHolderExprList() {
         ArrayList<Expr> slots = new ArrayList<>();
         for (PlaceHolderExpr pexpr : inner.getPlaceHolders()) {
@@ -209,6 +223,27 @@ public class PrepareStmt extends StatementBase {
         return lables;
     }
 
+    public StatementBase getInnerStmt() {
+        if (preparedType == PreparedType.FULL_PREPARED) {
+            // For performance reason we could reuse the inner statement when 
FULL_PREPARED
+            return inner;
+        }
+        // Make a copy of Statement, since anlyze will modify the structure of 
Statement.
+        // But we should keep the original statement
+        if (inner instanceof SelectStmt) {
+            return new SelectStmt((SelectStmt) inner);
+        }
+        if (inner instanceof NativeInsertStmt) {
+            return new NativeInsertStmt((NativeInsertStmt) inner);
+        }
+        // Other statement could reuse the inner statement
+        return inner;
+    }
+
+    public int argsSize() {
+        return inner.getPlaceHolders().size();
+    }
+
     public void asignValues(List<LiteralExpr> values) throws UserException {
         if (values.size() != inner.getPlaceHolders().size()) {
             throw new UserException("Invalid arguments size "
@@ -222,10 +257,6 @@ public class PrepareStmt extends StatementBase {
         }
     }
 
-    public PreparedType getPreparedType() {
-        return preparedType;
-    }
-
     @Override
     public void reset() {
         serializedDescTable = null;
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/analysis/StatementBase.java 
b/fe/fe-core/src/main/java/org/apache/doris/analysis/StatementBase.java
index 198bf0b8972..26cacc2317c 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/analysis/StatementBase.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/StatementBase.java
@@ -31,13 +31,15 @@ import org.apache.doris.thrift.TQueryOptions;
 
 import com.google.common.base.Preconditions;
 import com.google.common.base.Strings;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
 
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 
 public abstract class StatementBase implements ParseNode {
-
+    private static final Logger LOG = 
LogManager.getLogger(StatementBase.class);
     private String clusterName;
 
     // Set this variable if this QueryStmt is the top level query from an 
EXPLAIN <query>
@@ -57,7 +59,6 @@ public abstract class StatementBase implements ParseNode {
     private UserIdentity userInfo;
 
     private boolean isPrepared = false;
-
     // select * from tbl where a = ? and b = ?
     // `?` is the placeholder
     private ArrayList<PlaceHolderExpr> placeholders = new ArrayList<>();
@@ -105,14 +106,15 @@ public abstract class StatementBase implements ParseNode {
         this.explainOptions = options;
     }
 
-    public boolean isExplain() {
-        return this.explainOptions != null;
-    }
-
     public void setPlaceHolders(ArrayList<PlaceHolderExpr> placeholders) {
+        LOG.debug("setPlaceHolders {}", placeholders);
         this.placeholders = new ArrayList<PlaceHolderExpr>(placeholders);
     }
 
+    public boolean isExplain() {
+        return this.explainOptions != null;
+    }
+
     public ArrayList<PlaceHolderExpr> getPlaceHolders() {
         return this.placeholders;
     }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/analysis/StringLiteral.java 
b/fe/fe-core/src/main/java/org/apache/doris/analysis/StringLiteral.java
index 66747e0002f..57bc67fdc3e 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/analysis/StringLiteral.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/StringLiteral.java
@@ -311,5 +311,7 @@ public class StringLiteral extends LiteralExpr {
         if (LOG.isDebugEnabled()) {
             LOG.debug("parsed value '{}'", value);
         }
+        // Set it's literal type from binary info
+        type = Type.VARCHAR;
     }
 }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/catalog/OlapTable.java 
b/fe/fe-core/src/main/java/org/apache/doris/catalog/OlapTable.java
index a6525831197..2595a7f9ae3 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/catalog/OlapTable.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/catalog/OlapTable.java
@@ -1980,6 +1980,12 @@ public class OlapTable extends Table {
         return false;
     }
 
+    public int getBaseSchemaVersion() {
+        MaterializedIndexMeta baseIndexMeta = indexIdToMeta.get(baseIndexId);
+        return baseIndexMeta.getSchemaVersion();
+    }
+
+
     public void setEnableSingleReplicaCompaction(boolean 
enableSingleReplicaCompaction) {
         if (tableProperty == null) {
             tableProperty = new TableProperty(new HashMap<>());
@@ -2122,11 +2128,6 @@ public class OlapTable extends Table {
         return null;
     }
 
-    public int getBaseSchemaVersion() {
-        MaterializedIndexMeta baseIndexMeta = indexIdToMeta.get(baseIndexId);
-        return baseIndexMeta.getSchemaVersion();
-    }
-
     public int getIndexSchemaVersion(long indexId) {
         MaterializedIndexMeta indexMeta = indexIdToMeta.get(indexId);
         return indexMeta.getSchemaVersion();
diff --git a/fe/fe-core/src/main/java/org/apache/doris/mysql/MysqlProto.java 
b/fe/fe-core/src/main/java/org/apache/doris/mysql/MysqlProto.java
index c463e8f4264..dbc49b1e3bd 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/mysql/MysqlProto.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/mysql/MysqlProto.java
@@ -200,7 +200,6 @@ public class MysqlProto {
         }
 
         if (handshakeResponse == null) {
-            // receive response failed.
             return false;
         }
         if (capability.isDeprecatedEOF()) {
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/planner/OlapScanNode.java 
b/fe/fe-core/src/main/java/org/apache/doris/planner/OlapScanNode.java
index 68be7541043..f7263980136 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/planner/OlapScanNode.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/planner/OlapScanNode.java
@@ -30,6 +30,7 @@ import org.apache.doris.analysis.InPredicate;
 import org.apache.doris.analysis.IntLiteral;
 import org.apache.doris.analysis.LiteralExpr;
 import org.apache.doris.analysis.PartitionNames;
+import org.apache.doris.analysis.PrepareStmt;
 import org.apache.doris.analysis.SlotDescriptor;
 import org.apache.doris.analysis.SlotId;
 import org.apache.doris.analysis.SlotRef;
@@ -204,6 +205,7 @@ public class OlapScanNode extends ScanNode {
     // only used in short circuit plan at present
     private final PartitionPruneV2ForShortCircuitPlan cachedPartitionPruner =
                         new PartitionPruneV2ForShortCircuitPlan();
+    PrepareStmt preparedStatment = null;
 
     // Constructs node to scan given data files of table 'tbl'.
     public OlapScanNode(PlanNodeId id, TupleDescriptor desc, String 
planNodeName) {
@@ -544,9 +546,9 @@ public class OlapScanNode extends ScanNode {
         super.init(analyzer);
 
         filterDeletedRows(analyzer);
-        // lazy evaluation, since stmt is a prepared statment
-        isFromPrepareStmt = analyzer.getPrepareStmt() != null;
-        if (!isFromPrepareStmt) {
+        // point query could do lazy evaluation, since stmt is a prepared 
statment
+        preparedStatment = analyzer.getPrepareStmt();
+        if (preparedStatment == null || 
!preparedStatment.isPointQueryShortCircuit()) {
             computeColumnsFilter();
             computePartitionInfo();
         }
@@ -606,7 +608,7 @@ public class OlapScanNode extends ScanNode {
         }
 
         // prepare stmt evaluate lazily in Coordinator execute
-        if (!isFromPrepareStmt) {
+        if (preparedStatment == null || 
!preparedStatment.isPointQueryShortCircuit()) {
             try {
                 createScanRangeLocations();
             } catch (AnalysisException e) {
diff --git a/fe/fe-core/src/main/java/org/apache/doris/qe/ConnectProcessor.java 
b/fe/fe-core/src/main/java/org/apache/doris/qe/ConnectProcessor.java
index bc1ba7ac655..a6358675bc7 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/qe/ConnectProcessor.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/qe/ConnectProcessor.java
@@ -222,44 +222,35 @@ public class ConnectProcessor {
         packetBuf.get();
         // iteration_count always 1,
         packetBuf.getInt();
-        LOG.debug("execute prepared statement {}", stmtId);
         PrepareStmtContext prepareCtx = 
ctx.getPreparedStmt(String.valueOf(stmtId));
-        if (prepareCtx == null) {
-            LOG.debug("No such statement in context, stmtId:{}", stmtId);
-            ctx.getState().setError(ErrorCode.ERR_UNKNOWN_COM_ERROR,
-                    "msg: Not supported such prepared statement");
-            return;
-        }
-        ctx.setStartTime();
-        if (prepareCtx.stmt.getInnerStmt() instanceof QueryStmt) {
-            ctx.getState().setIsQuery(true);
-        }
-        prepareCtx.stmt.setIsPrepared();
         int paramCount = prepareCtx.stmt.getParmCount();
+        LOG.debug("execute prepared statement {}, paramCount {}", stmtId, 
paramCount);
         // null bitmap
-        byte[] nullbitmapData = new byte[(paramCount + 7) / 8];
-        packetBuf.get(nullbitmapData);
         String stmtStr = "";
         try {
-            // new_params_bind_flag
-            if ((int) packetBuf.get() != 0) {
-                // parse params's types
-                for (int i = 0; i < paramCount; ++i) {
-                    int typeCode = packetBuf.getChar();
-                    LOG.debug("code {}", typeCode);
-                    
prepareCtx.stmt.placeholders().get(i).setTypeCode(typeCode);
-                }
-            }
             List<LiteralExpr> realValueExprs = new ArrayList<>();
-            // parse param data
-            for (int i = 0; i < paramCount; ++i) {
-                if (isNull(nullbitmapData, i)) {
-                    realValueExprs.add(new NullLiteral());
-                    continue;
+            if (paramCount > 0) {
+                byte[] nullbitmapData = new byte[(paramCount + 7) / 8];
+                packetBuf.get(nullbitmapData);
+                // new_params_bind_flag
+                if ((int) packetBuf.get() != 0) {
+                    // parse params's types
+                    for (int i = 0; i < paramCount; ++i) {
+                        int typeCode = packetBuf.getChar();
+                        LOG.debug("code {}", typeCode);
+                        
prepareCtx.stmt.placeholders().get(i).setTypeCode(typeCode);
+                    }
+                }
+                // parse param data
+                for (int i = 0; i < paramCount; ++i) {
+                    if (isNull(nullbitmapData, i)) {
+                        realValueExprs.add(new NullLiteral());
+                        continue;
+                    }
+                    LiteralExpr l = 
prepareCtx.stmt.placeholders().get(i).createLiteralFromType();
+                    l.setupParamFromBinary(packetBuf);
+                    realValueExprs.add(l);
                 }
-                LiteralExpr l = 
prepareCtx.stmt.placeholders().get(i).createLiteralFromType();
-                l.setupParamFromBinary(packetBuf);
-                realValueExprs.add(l);
             }
             ExecuteStmt executeStmt = new ExecuteStmt(String.valueOf(stmtId), 
realValueExprs);
             // TODO set real origin statement
@@ -571,7 +562,6 @@ public class ConnectProcessor {
         LOG.debug("handle command {}", command);
         ctx.setCommand(command);
         ctx.setStartTime();
-
         switch (command) {
             case COM_INIT_DB:
                 handleInitDb();
diff --git a/fe/fe-core/src/main/java/org/apache/doris/qe/Coordinator.java 
b/fe/fe-core/src/main/java/org/apache/doris/qe/Coordinator.java
index 164072c0b5c..e2889310428 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/qe/Coordinator.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/qe/Coordinator.java
@@ -36,6 +36,7 @@ import org.apache.doris.common.util.RuntimeProfile;
 import org.apache.doris.common.util.TimeUtils;
 import org.apache.doris.load.loadv2.LoadJob;
 import org.apache.doris.metric.MetricRepo;
+import org.apache.doris.mysql.MysqlCommand;
 import org.apache.doris.nereids.stats.StatsErrorEstimator;
 import org.apache.doris.planner.DataPartition;
 import org.apache.doris.planner.DataSink;
@@ -373,6 +374,8 @@ public class Coordinator implements CoordInterface {
         this.queryOptions.setQueryTimeout(context.getExecTimeout());
         this.queryOptions.setExecutionTimeout(context.getExecTimeout());
         
this.queryOptions.setEnableScanNodeRunSerial(context.getSessionVariable().isEnableScanRunSerial());
+        this.queryOptions.setMysqlRowBinaryFormat(
+                    context.getCommand() == MysqlCommand.COM_STMT_EXECUTE);
     }
 
     public long getJobId() {
diff --git a/fe/fe-core/src/main/java/org/apache/doris/qe/PointQueryExec.java 
b/fe/fe-core/src/main/java/org/apache/doris/qe/PointQueryExec.java
index f5712d3a92b..634a4967d85 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/qe/PointQueryExec.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/qe/PointQueryExec.java
@@ -113,7 +113,7 @@ public class PointQueryExec implements CoordInterface {
             this.cacheID = prepareStmt.getID();
             this.serializedDescTable = prepareStmt.getSerializedDescTable();
             this.serializedOutputExpr = prepareStmt.getSerializedOutputExprs();
-            this.isBinaryProtocol = prepareStmt.isBinaryProtocol();
+            this.isBinaryProtocol = true;
         } else {
             // TODO
             // planner.getDescTable().toThrift();
diff --git a/fe/fe-core/src/main/java/org/apache/doris/qe/SessionVariable.java 
b/fe/fe-core/src/main/java/org/apache/doris/qe/SessionVariable.java
index cfa7fc64e24..56743ba04c5 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/qe/SessionVariable.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/qe/SessionVariable.java
@@ -116,6 +116,8 @@ public class SessionVariable implements Serializable, 
Writable {
     public static final String ENABLE_INSERT_STRICT = "enable_insert_strict";
     public static final String ENABLE_SPILLING = "enable_spilling";
     public static final String ENABLE_EXCHANGE_NODE_PARALLEL_MERGE = 
"enable_exchange_node_parallel_merge";
+
+    public static final String ENABLE_SERVER_SIDE_PREPARED_STATEMENT = 
"enable_server_side_prepared_statement";
     public static final String PREFER_JOIN_METHOD = "prefer_join_method";
 
     public static final String ENABLE_FOLD_CONSTANT_BY_BE = 
"enable_fold_constant_by_be";
@@ -1078,6 +1080,10 @@ public class SessionVariable implements Serializable, 
Writable {
     @VariableMgr.VarAttr(name = TOPN_OPT_LIMIT_THRESHOLD)
     public long topnOptLimitThreshold = 1024;
 
+    @VariableMgr.VarAttr(name = ENABLE_SERVER_SIDE_PREPARED_STATEMENT, 
needForward = true, description = {
+            "是否启用开启服务端prepared statement", "Set whether to enable server side 
prepared statement."})
+    public boolean enableServeSidePreparedStatement = false;
+
     // Default value is false, which means the group by and having clause
     // should first use column name not alias. According to mysql.
     @VariableMgr.VarAttr(name = GROUP_BY_AND_HAVING_USE_ALIAS_FIRST)
diff --git a/fe/fe-core/src/main/java/org/apache/doris/qe/StmtExecutor.java 
b/fe/fe-core/src/main/java/org/apache/doris/qe/StmtExecutor.java
index 6ee43bd834b..1f0e4d43d20 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/qe/StmtExecutor.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/qe/StmtExecutor.java
@@ -117,6 +117,7 @@ import org.apache.doris.load.loadv2.LoadManagerAdapter;
 import org.apache.doris.mysql.MysqlChannel;
 import org.apache.doris.mysql.MysqlCommand;
 import org.apache.doris.mysql.MysqlEofPacket;
+import org.apache.doris.mysql.MysqlOkPacket;
 import org.apache.doris.mysql.MysqlSerializer;
 import org.apache.doris.mysql.ProxyMysqlChannel;
 import org.apache.doris.mysql.privilege.PrivPredicate;
@@ -958,6 +959,7 @@ public class StmtExecutor {
             }
             // continue analyze
             preparedStmtReanalyzed = true;
+            preparedStmtCtx.stmt.reset();
             preparedStmtCtx.stmt.analyze(analyzer);
         }
 
@@ -973,7 +975,7 @@ public class StmtExecutor {
         if (parsedStmt instanceof PrepareStmt || context.getCommand() == 
MysqlCommand.COM_STMT_PREPARE) {
             if (context.getCommand() == MysqlCommand.COM_STMT_PREPARE) {
                 prepareStmt = new PrepareStmt(parsedStmt,
-                        String.valueOf(context.getEnv().getNextStmtId()), true 
/*binary protocol*/);
+                        String.valueOf(context.getEnv().getNextStmtId()));
             } else {
                 prepareStmt = (PrepareStmt) parsedStmt;
             }
@@ -1070,7 +1072,8 @@ public class StmtExecutor {
                 throw new AnalysisException("Unexpected exception: " + 
e.getMessage());
             }
         }
-        if (preparedStmtReanalyzed) {
+        if (preparedStmtReanalyzed
+                && preparedStmtCtx.stmt.getPreparedType() == 
PrepareStmt.PreparedType.FULL_PREPARED) {
             LOG.debug("update planner and analyzer after prepared statement 
reanalyzed");
             preparedStmtCtx.planner = planner;
             preparedStmtCtx.analyzer = analyzer;
@@ -1178,6 +1181,11 @@ public class StmtExecutor {
                         Lists.newArrayList(parsedStmt.getColLabels());
                 // Re-analyze the stmt with a new analyzer.
                 analyzer = new Analyzer(context.getEnv(), context);
+                if (prepareStmt != null) {
+                    // Re-analyze prepareStmt with a new analyzer
+                    prepareStmt.reset();
+                    prepareStmt.analyze(analyzer);
+                }
 
                 if (prepareStmt != null) {
                     // Re-analyze prepareStmt with a new analyzer
@@ -1406,12 +1414,13 @@ public class StmtExecutor {
         }
 
         // handle selects that fe can do without be, so we can make sql tools 
happy, especially the setup step.
-        Optional<ResultSet> resultSet = planner.handleQueryInFe(parsedStmt);
-        if (resultSet.isPresent()) {
-            sendResultSet(resultSet.get());
-            return;
+        if (context.getCommand() != MysqlCommand.COM_STMT_EXECUTE) {
+            Optional<ResultSet> resultSet = 
planner.handleQueryInFe(parsedStmt);
+            if (resultSet.isPresent()) {
+                sendResultSet(resultSet.get());
+                return;
+            }
         }
-
         MysqlChannel channel = context.getMysqlChannel();
         boolean isOutfileQuery = queryStmt.hasOutFileClause();
 
@@ -2088,11 +2097,11 @@ public class StmtExecutor {
     private void handlePrepareStmt() throws Exception {
         // register prepareStmt
         LOG.debug("add prepared statement {}, isBinaryProtocol {}",
-                        prepareStmt.getName(), prepareStmt.isBinaryProtocol());
+                        prepareStmt.getName(), context.getCommand() == 
MysqlCommand.COM_STMT_PREPARE);
         context.addPreparedStmt(prepareStmt.getName(),
                 new PrepareStmtContext(prepareStmt,
                             context, planner, analyzer, 
prepareStmt.getName()));
-        if (prepareStmt.isBinaryProtocol()) {
+        if (context.getCommand() == MysqlCommand.COM_STMT_PREPARE) {
             sendStmtPrepareOK();
         }
     }
@@ -2168,13 +2177,18 @@ public class StmtExecutor {
                 serializer.writeField(colNames.get(i), 
Type.fromPrimitiveType(types.get(i)));
                 
context.getMysqlChannel().sendOnePacket(serializer.toByteBuffer());
             }
+            serializer.reset();
+            if (!context.getMysqlChannel().clientDeprecatedEOF()) {
+                MysqlEofPacket eofPacket = new 
MysqlEofPacket(context.getState());
+                eofPacket.writeTo(serializer);
+            } else {
+                MysqlOkPacket okPacket = new MysqlOkPacket(context.getState());
+                okPacket.writeTo(serializer);
+            }
+            context.getMysqlChannel().sendOnePacket(serializer.toByteBuffer());
         }
-        // send EOF if nessessary
-        if (!context.getMysqlChannel().clientDeprecatedEOF()) {
-            context.getState().setEof();
-        } else {
-            context.getState().setOk();
-        }
+        context.getMysqlChannel().flush();
+        context.getState().setNoop();
     }
 
     private void sendFields(List<String> colNames, List<Type> types) throws 
IOException {
diff --git a/fe/fe-core/src/main/jflex/sql_scanner.flex 
b/fe/fe-core/src/main/jflex/sql_scanner.flex
index 96fc35f6d45..583fd916d20 100644
--- a/fe/fe-core/src/main/jflex/sql_scanner.flex
+++ b/fe/fe-core/src/main/jflex/sql_scanner.flex
@@ -557,7 +557,6 @@ import org.apache.doris.qe.SqlModeHelper;
     tokenIdMap.put(new Integer(SqlParserSymbols.BITXOR), "^");
     tokenIdMap.put(new Integer(SqlParserSymbols.NUMERIC_OVERFLOW), "NUMERIC 
OVERFLOW");
     tokenIdMap.put(new Integer(SqlParserSymbols.PLACEHOLDER), "?");
-
   }
 
   public static boolean isKeyword(Integer tokenId) {
diff --git a/regression-test/data/point_query_p0/test_point_query.out 
b/regression-test/data/point_query_p0/test_point_query.out
index d23a62474c3..ff4b1932b3a 100644
--- a/regression-test/data/point_query_p0/test_point_query.out
+++ b/regression-test/data/point_query_p0/test_point_query.out
@@ -71,18 +71,6 @@
 -- !sql --
 6120202020646464       6C616F6F71      32.92200050354004
 
--- !sql --
-1231   119291.110000000        ddd     laooq   \N      2020-01-01T12:36:38     
\N      1022-01-01      \N      1.111   [119181.111100000, 819019.119100000, 
null]      \N      0       0
-
--- !sql --
-1237   120939.111300000        a    ddd        laooq   2030-01-02      
2020-01-01T12:36:38     22.822  7022-01-01      false   90696620686827832.374   
[1.100000000, 2.200000000, 3.300000000, 4.400000000, 5.500000000]       []      
0       0
-
--- !sql --
-1231   119291.110000000        ddd     laooq   \N      2020-01-01T12:36:38     
\N      1022-01-01      \N      1.111   [119181.111100000, 819019.119100000, 
null]      \N      0       0
-
--- !sql --
-1237   120939.111300000        a    ddd        laooq   2030-01-02      
2020-01-01T12:36:38     22.822  7022-01-01      false   90696620686827832.374   
[1.100000000, 2.200000000, 3.300000000, 4.400000000, 5.500000000]       []      
0       0
-
 -- !sql --
 0      1       2       3
 
@@ -158,18 +146,6 @@
 -- !sql --
 6120202020646464       6C616F6F71      32.92200050354004
 
--- !sql --
-1231   119291.110000000        ddd     laooq   \N      2020-01-01T12:36:38     
\N      1022-01-01      \N      1.111   [119181.111100000, 819019.119100000, 
null]      \N      0       0
-
--- !sql --
-1237   120939.111300000        a    ddd        laooq   2030-01-02      
2020-01-01T12:36:38     22.822  7022-01-01      false   90696620686827832.374   
[1.100000000, 2.200000000, 3.300000000, 4.400000000, 5.500000000]       []      
0       0
-
--- !sql --
-1231   119291.110000000        ddd     laooq   \N      2020-01-01T12:36:38     
\N      1022-01-01      \N      1.111   [119181.111100000, 819019.119100000, 
null]      \N      0       0
-
--- !sql --
-1237   120939.111300000        a    ddd        laooq   2030-01-02      
2020-01-01T12:36:38     22.822  7022-01-01      false   90696620686827832.374   
[1.100000000, 2.200000000, 3.300000000, 4.400000000, 5.500000000]       []      
0       0
-
 -- !sql --
 0      1       2       3
 
@@ -245,18 +221,6 @@
 -- !sql --
 6120202020646464       6C616F6F71      32.92200050354004
 
--- !sql --
-1231   119291.110000000        ddd     laooq   \N      2020-01-01T12:36:38     
\N      1022-01-01      \N      1.111   [119181.111100000, 819019.119100000, 
null]      \N      0       0
-
--- !sql --
-1237   120939.111300000        a    ddd        laooq   2030-01-02      
2020-01-01T12:36:38     22.822  7022-01-01      false   90696620686827832.374   
[1.100000000, 2.200000000, 3.300000000, 4.400000000, 5.500000000]       []      
0       0
-
--- !sql --
-1231   119291.110000000        ddd     laooq   \N      2020-01-01T12:36:38     
\N      1022-01-01      \N      1.111   [119181.111100000, 819019.119100000, 
null]      \N      0       0
-
--- !sql --
-1237   120939.111300000        a    ddd        laooq   2030-01-02      
2020-01-01T12:36:38     22.822  7022-01-01      false   90696620686827832.374   
[1.100000000, 2.200000000, 3.300000000, 4.400000000, 5.500000000]       []      
0       0
-
 -- !sql --
 0      1       2       3
 
diff --git a/regression-test/data/prepared_stmt_p0/prepared_stmt.out 
b/regression-test/data/prepared_stmt_p0/prepared_stmt.out
new file mode 100644
index 00000000000..396ee931683
--- /dev/null
+++ b/regression-test/data/prepared_stmt_p0/prepared_stmt.out
@@ -0,0 +1,55 @@
+-- This file is automatically generated. You should know what you did if you 
want to edit this
+-- !sql --
+1231   119291.110000000        ddd     laooq   \N      2020-01-01 12:36:38     
\N      1022-01-01      ["2022-01-01 11:30:38", "2022-01-01 11:30:38", 
"2022-01-01 11:30:38"]
+1232   12222.991211350 xxx     laooq   2023-01-02      2020-01-01 12:36:38     
522.762 2022-01-01      ["2023-01-01 11:30:38", "2023-01-01 11:30:38"]
+1233   1.392932911     yyy     laooq   2024-01-02      2020-01-01 12:36:38     
52.862  3022-01-01      ["2024-01-01 11:30:38", "2024-01-01 11:30:38", 
"2024-01-01 11:30:38"]
+1234   12919291.129191137      xxddd   laooq   2025-01-02      2020-01-01 
12:36:38     552.872 4022-01-01      ["2025-01-01 11:30:38", "2025-01-01 
11:30:38", "2025-01-01 11:30:38"]
+1235   991129292901.111380000  dd      \N      2120-01-02      2020-01-01 
12:36:38     652.692 5022-01-01      []
+1236   100320.111390000        laa    ddd      laooq   2220-01-02      
2020-01-01 12:36:38     2.7692  6022-01-01      [null]
+1237   120939.111300000        a    ddd        laooq   2030-01-02      
2020-01-01 12:36:38     22.822  7022-01-01      ["2025-01-01 11:30:38"]
+
+-- !sql --
+1231   119291.110000000        ddd     laooq   \N      2020-01-01 12:36:38     
\N      1022-01-01      ["2022-01-01 11:30:38", "2022-01-01 11:30:38", 
"2022-01-01 11:30:38"]
+1232   12222.991211350 xxx     laooq   2023-01-02      2020-01-01 12:36:38     
522.762 2022-01-01      ["2023-01-01 11:30:38", "2023-01-01 11:30:38"]
+1233   1.392932911     yyy     laooq   2024-01-02      2020-01-01 12:36:38     
52.862  3022-01-01      ["2024-01-01 11:30:38", "2024-01-01 11:30:38", 
"2024-01-01 11:30:38"]
+1234   12919291.129191137      xxddd   laooq   2025-01-02      2020-01-01 
12:36:38     552.872 4022-01-01      ["2025-01-01 11:30:38", "2025-01-01 
11:30:38", "2025-01-01 11:30:38"]
+1235   991129292901.111380000  dd      \N      2120-01-02      2020-01-01 
12:36:38     652.692 5022-01-01      []
+1236   100320.111390000        laa    ddd      laooq   2220-01-02      
2020-01-01 12:36:38     2.7692  6022-01-01      [null]
+1237   120939.111300000        a    ddd        laooq   2030-01-02      
2020-01-01 12:36:38     22.822  7022-01-01      ["2025-01-01 11:30:38"]
+
+-- !select0 --
+1231   119291.110000000        ddd     laooq   \N      2020-01-01 12:36:38     
\N      1022-01-01      ["2022-01-01 11:30:38", "2022-01-01 11:30:38", 
"2022-01-01 11:30:38"]
+
+-- !select0 --
+1232   12222.991211350 xxx     laooq   2023-01-02      2020-01-01 12:36:38     
522.762 2022-01-01      ["2023-01-01 11:30:38", "2023-01-01 11:30:38"]
+
+-- !select0 --
+1232   12222.991211350 xxx     laooq   2023-01-02      2020-01-01 12:36:38     
522.762 2022-01-01      ["2023-01-01 11:30:38", "2023-01-01 11:30:38"]
+
+-- !select1 --
+646464 xxxx---
+
+-- !select1 --
+787878 yyyy---
+
+-- !select1 --
+787878 yyyy---
+
+-- !select2 --
+1237   120939.111300000        a    ddd        laooq   2030-01-02      
2020-01-01 12:36:38     22.822  7022-01-01      ["2025-01-01 11:30:38"] 1237    
120939.111300000        a    ddd        laooq   2030-01-02      2020-01-01 
12:36:38     22.822  7022-01-01      ["2025-01-01 11:30:38"]
+
+-- !select2 --
+1237   120939.111300000        a    ddd        laooq   2030-01-02      
2020-01-01 12:36:38     22.822  7022-01-01      ["2025-01-01 11:30:38"] 1237    
120939.111300000        a    ddd        laooq   2030-01-02      2020-01-01 
12:36:38     22.822  7022-01-01      ["2025-01-01 11:30:38"]
+
+-- !select2 --
+1237   120939.111300000        a    ddd        laooq   2030-01-02      
2020-01-01 12:36:38     22.822  7022-01-01      ["2025-01-01 11:30:38"] 1237    
120939.111300000        a    ddd        laooq   2030-01-02      2020-01-01 
12:36:38     22.822  7022-01-01      ["2025-01-01 11:30:38"]
+
+-- !select3 --
+1      1       user1   30      1234    12345
+
+-- !select4 --
+10
+
+-- !select5 --
+1
+
diff --git 
a/regression-test/framework/src/main/groovy/org/apache/doris/regression/suite/Suite.groovy
 
b/regression-test/framework/src/main/groovy/org/apache/doris/regression/suite/Suite.groovy
index 494cf0224b4..7575c185d54 100644
--- 
a/regression-test/framework/src/main/groovy/org/apache/doris/regression/suite/Suite.groovy
+++ 
b/regression-test/framework/src/main/groovy/org/apache/doris/regression/suite/Suite.groovy
@@ -672,6 +672,12 @@ class Suite implements GroovyInterceptable {
         return result
     }
 
+    List<List<Object>> exec(Object stmt) {
+        logger.info("Execute sql: ${stmt}".toString())
+        def (result, meta )= JdbcUtils.executeToList(context.getConnection(),  
(PreparedStatement) stmt)
+        return result
+    }
+
     void quickRunTest(String tag, Object arg, boolean isOrder = false) {
         if (context.config.generateOutputFile || 
context.config.forceGenerateOutputFile) {
             Tuple2<List<List<Object>>, ResultSetMetaData> tupleResult = null
diff --git 
a/regression-test/framework/src/main/groovy/org/apache/doris/regression/util/JdbcUtils.groovy
 
b/regression-test/framework/src/main/groovy/org/apache/doris/regression/util/JdbcUtils.groovy
index 8791dd289b3..d52bea3c514 100644
--- 
a/regression-test/framework/src/main/groovy/org/apache/doris/regression/util/JdbcUtils.groovy
+++ 
b/regression-test/framework/src/main/groovy/org/apache/doris/regression/util/JdbcUtils.groovy
@@ -41,6 +41,15 @@ class JdbcUtils {
         return conn.prepareStatement(sql);
     }
 
+    static Tuple2<List<List<Object>>, ResultSetMetaData> 
executeToList(Connection conn, PreparedStatement stmt) {
+        boolean hasResultSet = stmt.execute()
+        if (!hasResultSet) {
+            return [ImmutableList.of(ImmutableList.of(stmt.getUpdateCount())), 
null]
+        } else {
+            return toList(stmt.resultSet)
+        } 
+    }
+
     static Tuple2<List<List<Object>>, ResultSetMetaData> 
executeToStringList(Connection conn, PreparedStatement stmt) {
         return toStringList(stmt.executeQuery())
     }
diff --git a/regression-test/suites/point_query_p0/test_point_query.groovy 
b/regression-test/suites/point_query_p0/test_point_query.groovy
index 8c01c8cc208..5be89b840b1 100644
--- a/regression-test/suites/point_query_p0/test_point_query.groovy
+++ b/regression-test/suites/point_query_p0/test_point_query.groovy
@@ -227,13 +227,13 @@ suite("test_point_query") {
                 qt_sql """select /*+ SET_VAR(enable_nereids_planner=false) */ 
* from ${tableName} where k1 = 1237 and k2 = 120939.11130 and k3 = 'a    ddd'"""
                 qt_sql """select /*+ SET_VAR(enable_nereids_planner=false) */ 
hex(k3), hex(k4), k7 + 10.1 from ${tableName} where k1 = 1237 and k2 = 
120939.11130 and k3 = 'a    ddd'"""
                 // prepared text
-                sql """ prepare stmt1 from  select * from ${tableName} where 
k1 = % and k2 = % and k3 = % """
-                qt_sql """execute stmt1 using (1231, 119291.11, 'ddd')"""
-                qt_sql """execute stmt1 using (1237, 120939.11130, 'a    
ddd')"""
+                // sql """ prepare stmt1 from  select * from ${tableName} 
where k1 = % and k2 = % and k3 = % """
+                // qt_sql """execute stmt1 using (1231, 119291.11, 'ddd')"""
+                // qt_sql """execute stmt1 using (1237, 120939.11130, 'a    
ddd')"""
 
-                sql """prepare stmt2 from  select * from ${tableName} where k1 
= % and k2 = % and k3 = %"""
-                qt_sql """execute stmt2 using (1231, 119291.11, 'ddd')"""
-                qt_sql """execute stmt2 using (1237, 120939.11130, 'a    
ddd')"""
+                // sql """prepare stmt2 from  select * from ${tableName} where 
k1 = % and k2 = % and k3 = %"""
+                // qt_sql """execute stmt2 using (1231, 119291.11, 'ddd')"""
+                // qt_sql """execute stmt2 using (1237, 120939.11130, 'a    
ddd')"""
                 tableName = "test_query"
                 sql """DROP TABLE IF EXISTS ${tableName}"""
                 sql """CREATE TABLE ${tableName} (
diff --git a/regression-test/suites/prepared_stmt_p0/prepared_stmt.groovy 
b/regression-test/suites/prepared_stmt_p0/prepared_stmt.groovy
new file mode 100644
index 00000000000..7a746f41c97
--- /dev/null
+++ b/regression-test/suites/prepared_stmt_p0/prepared_stmt.groovy
@@ -0,0 +1,168 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+import java.math.BigDecimal;
+
+suite("test_prepared_stmt", "nonConcurrent") {
+    def tableName = "tbl_prepared_stmt"
+    def user = context.config.jdbcUser
+    def password = context.config.jdbcPassword
+    def url = context.config.jdbcUrl + "&useServerPrepStmts=true"
+    // def url = context.config.jdbcUrl
+    def result1 = connect(user=user, password=password, url=url) {
+    sql "set global enable_server_side_prepared_statement = true"
+    def insert_prepared = { stmt, k1 , k2, k3, k4, k5, k6, k7, k8, k9 ->
+        java.text.SimpleDateFormat formater = new 
java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+        if (k1 == null) {
+            stmt.setNull(1, java.sql.Types.INTEGER);
+        } else {
+            stmt.setInt(1, k1)
+        }
+        if (k2 == null) {
+            stmt.setNull(2, java.sql.Types.DECIMAL);
+        } else {
+            stmt.setBigDecimal(2, k2)
+        }
+        if (k3 == null) {
+            stmt.setNull(3, java.sql.Types.VARCHAR);    
+        } else {
+            stmt.setString(3, k3)
+        }
+        if (k4 == null) {
+            stmt.setNull(4, java.sql.Types.VARCHAR);     
+        } else {
+            stmt.setString(4, k4)
+        }
+        if (k5 == null) {
+            stmt.setNull(5, java.sql.Types.DATE);     
+        } else {
+            stmt.setDate(5, java.sql.Date.valueOf(k5))
+        }
+        if (k6 == null) {
+            stmt.setNull(6, java.sql.Types.TIMESTAMP);     
+        } else {
+            stmt.setTimestamp(6, new 
java.sql.Timestamp(formater.parse(k6).getTime()))
+        }
+        if (k7 == null) {
+            stmt.setNull(7, java.sql.Types.FLOAT);
+        } else {
+            stmt.setFloat(7, k7)
+        }
+        if (k8 == null) {
+            stmt.setNull(8, java.sql.Types.DATE);
+        } else {
+            stmt.setTimestamp(8, new 
java.sql.Timestamp(formater.parse(k8).getTime()))
+        }
+        if (k9 == null) {
+            stmt.setNull(9, java.sql.Types.VARCHAR);
+        } else {
+            stmt.setString(9, k9)
+        }
+        exec stmt
+    }
+    sql """DROP TABLE IF EXISTS ${tableName} """
+    sql """
+        CREATE TABLE IF NOT EXISTS ${tableName} (
+                `k1` int(11) NULL COMMENT "",
+                `k2` decimalv3(27, 9) NULL COMMENT "",
+                `k3` varchar(30) NULL COMMENT "",
+                `k4` varchar(30) NULL COMMENT "",
+                `k5` date NULL COMMENT "",
+                `k6` datetime NULL COMMENT "",
+                `k7` float NULL COMMENT "",
+                `k8` datev2 NULL COMMENT "",
+                `k9` array<datetime> NULL COMMENT ""
+              ) ENGINE=OLAP
+              DUPLICATE KEY(`k1`, `k2`, `k3`)
+              DISTRIBUTED BY HASH(`k1`, k2, k3) BUCKETS 1
+              PROPERTIES (
+              "replication_allocation" = "tag.location.default: 1",
+              "light_schema_change" = "true",
+              "storage_format" = "V2"
+              )
+          """
+      
+      def insert_stmt = prepareStatement """ INSERT INTO ${tableName} 
VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?) """
+      assertEquals(insert_stmt.class, 
com.mysql.cj.jdbc.ServerPreparedStatement);
+      insert_prepared insert_stmt, 1231, 119291.11, "ddd", "laooq", null, 
"2020-01-01 12:36:38", null, "1022-01-01 11:30:38", "[2022-01-01 11:30:38, 
2022-01-01 11:30:38, 2022-01-01 11:30:38]"
+      insert_prepared insert_stmt, 1232, 12222.99121135, "xxx", "laooq", 
"2023-01-02", "2020-01-01 12:36:38", 522.762, "2022-01-01 11:30:38", 
"[2023-01-01 11:30:38, 2023-01-01 11:30:38]"
+      insert_prepared insert_stmt, 1233, 1.392932911136, "yyy", "laooq", 
"2024-01-02", "2020-01-01 12:36:38", 52.862, "3022-01-01 11:30:38", 
"[2024-01-01 11:30:38, 2024-01-01 11:30:38, 2024-01-01 11:30:38]"
+      insert_prepared insert_stmt, 1234, 12919291.129191137, "xxddd", "laooq", 
"2025-01-02", "2020-01-01 12:36:38", 552.872, "4022-01-01 11:30:38", 
"[2025-01-01 11:30:38, 2025-01-01 11:30:38, 2025-01-01 11:30:38]"
+      insert_prepared insert_stmt, 1235, 991129292901.11138, "dd", null, 
"2120-01-02", "2020-01-01 12:36:38", 652.692, "5022-01-01 11:30:38", "[]"
+      insert_prepared insert_stmt, 1236, 100320.11139, "laa    ddd", "laooq", 
"2220-01-02", "2020-01-01 12:36:38", 2.7692, "6022-01-01 11:30:38", "[null]"
+      insert_prepared insert_stmt, 1237, 120939.11130, "a    ddd", "laooq", 
"2030-01-02", "2020-01-01 12:36:38", 22.822, "7022-01-01 11:30:38", 
"[2025-01-01 11:30:38]"
+
+    qt_sql """select * from  ${tableName} order by 1, 2, 3"""
+    qt_sql """select * from  ${tableName} order by 1, 2, 3"""
+
+    def stmt_read = prepareStatement "select * from ${tableName} where k1 = ? 
order by k1"
+    assertEquals(stmt_read.class, com.mysql.cj.jdbc.ServerPreparedStatement);
+    stmt_read.setInt(1, 1231)
+    qe_select0 stmt_read
+    stmt_read.setInt(1, 1232)
+    qe_select0 stmt_read
+    qe_select0 stmt_read
+    def stmt_read1 = prepareStatement "select hex(k3), ? from ${tableName} 
where k1 = ? order by 1"
+    assertEquals(stmt_read1.class, com.mysql.cj.jdbc.ServerPreparedStatement);
+    stmt_read1.setString(1, "xxxx---")
+    stmt_read1.setInt(2, 1231)
+    qe_select1 stmt_read1
+    stmt_read1.setString(1, "yyyy---")
+    stmt_read1.setInt(2, 1232)
+    qe_select1 stmt_read1
+    qe_select1 stmt_read1
+      def stmt_read2 = prepareStatement "select * from ${tableName} as t1 join 
${tableName} as t2 on t1.`k1` = t2.`k1` where t1.`k1` >= ? and t1.`k2` >= ? and 
size(t1.`k9`) > ? order by 1, 2, 3"
+      assertEquals(stmt_read2.class, 
com.mysql.cj.jdbc.ServerPreparedStatement);
+      stmt_read2.setInt(1, 1237)
+      stmt_read2.setBigDecimal(2, new BigDecimal("120939.11130"))
+      stmt_read2.setInt(3, 0)
+      qe_select2 stmt_read2
+      qe_select2 stmt_read2
+      qe_select2 stmt_read2
+
+      sql "DROP TABLE IF EXISTS mytable1"
+      sql """
+        CREATE TABLE mytable1
+        (
+            siteid INT DEFAULT '10',
+            citycode SMALLINT,
+            username VARCHAR(32) DEFAULT '',
+            pv BIGINT SUM DEFAULT '0'
+        )
+        AGGREGATE KEY(siteid, citycode, username)
+        DISTRIBUTED BY HASH(siteid) BUCKETS 10
+        PROPERTIES("replication_num" = "1");
+        """
+    
+     sql """insert into mytable1 values(1,1,'user1',10);"""
+     sql """insert into mytable1 values(1,1,'user1',10);"""
+     sql """insert into mytable1 values(1,1,'user1',10);"""
+     stmt_read = prepareStatement "SELECT *, ? FROM (select *, ? from mytable1 
where citycode = ?) AS `SpotfireCustomQuery1` WHERE 1 = 1"
+     stmt_read.setInt(1, 12345)
+     stmt_read.setInt(2, 1234)
+     stmt_read.setInt(3, 1)
+     qe_select3 stmt_read
+
+     stmt_read = prepareStatement "SELECT 10"
+     assertEquals(stmt_read.class, com.mysql.cj.jdbc.ServerPreparedStatement);
+     qe_select4 stmt_read
+     stmt_read = prepareStatement "SELECT 1"
+     assertEquals(stmt_read.class, com.mysql.cj.jdbc.ServerPreparedStatement);
+     qe_select5 stmt_read
+    sql "set global enable_server_side_prepared_statement = false"
+    }
+}


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscr...@doris.apache.org
For additional commands, e-mail: commits-h...@doris.apache.org

Reply via email to