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 <[email protected]>
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: [email protected]
For additional commands, e-mail: [email protected]