From ee24667b9f16d81c61e21ba1202a76716ca6f74f Mon Sep 17 00:00:00 2001 From: meiyi Date: Thu, 14 Dec 2023 14:38:56 +0800 Subject: [PATCH] [fix](group commit) Fix some group commit problems (#28319) --- be/src/http/action/http_stream.cpp | 31 +++++---- be/src/http/action/stream_load.cpp | 32 +++++---- .../java/org/apache/doris/common/Config.java | 4 ++ .../apache/doris/analysis/ArrayLiteral.java | 8 ++- .../org/apache/doris/analysis/MapLiteral.java | 12 +++- .../doris/analysis/NativeInsertStmt.java | 13 +++- .../apache/doris/analysis/StructLiteral.java | 13 +++- .../doris/common/util/PropertyAnalyzer.java | 3 +- .../commands/InsertIntoTableCommand.java | 16 ++--- .../doris/planner/GroupCommitPlanner.java | 4 ++ .../apache/doris/planner/OlapTableSink.java | 1 + .../org/apache/doris/qe/StmtExecutor.java | 14 +++- .../doris/service/FrontendServiceImpl.java | 2 +- .../data/insert_p0/insert_with_null.out | 67 +++++++++++++------ .../suites/insert_p0/insert_with_null.groovy | 19 ++++-- 15 files changed, 165 insertions(+), 74 deletions(-) diff --git a/be/src/http/action/http_stream.cpp b/be/src/http/action/http_stream.cpp index a151519471..ef4decb038 100644 --- a/be/src/http/action/http_stream.cpp +++ b/be/src/http/action/http_stream.cpp @@ -179,23 +179,28 @@ int HttpStreamAction::on_header(HttpRequest* req) { group_commit_mode = ""; } } - if (!group_commit_mode.empty() || config::wait_internal_group_commit_finish) { - ctx->group_commit = load_size_smaller_than_wal_limit(req); - if (!ctx->group_commit) { - LOG(WARNING) << "The data size for this http load(" - << std::stol(req->header(HttpHeaders::CONTENT_LENGTH)) - << " Bytes) exceeds the WAL (Write-Ahead Log) limit (" - << config::wal_max_disk_size * 0.8 - << " Bytes). Please set this load to \"group commit\"=false."; - st = Status::InternalError("Http load size too large."); + ctx->two_phase_commit = req->header(HTTP_TWO_PHASE_COMMIT) == "true"; + auto temp_partitions = !req->header(HTTP_TEMP_PARTITIONS).empty(); + auto partitions = !req->header(HTTP_PARTITIONS).empty(); + if (!temp_partitions && !partitions && !ctx->two_phase_commit && + (!group_commit_mode.empty() || config::wait_internal_group_commit_finish)) { + if (config::wait_internal_group_commit_finish) { + ctx->group_commit = true; + } else { + ctx->group_commit = load_size_smaller_than_wal_limit(req); + if (!ctx->group_commit) { + LOG(WARNING) << "The data size for this http load(" + << req->header(HttpHeaders::CONTENT_LENGTH) + << " Bytes) exceeds the WAL (Write-Ahead Log) limit (" + << config::wal_max_disk_size * 0.8 + << " Bytes). Please set this load to \"group commit\"=false."; + st = Status::InternalError("Http load size too large."); + } } } - ctx->two_phase_commit = req->header(HTTP_TWO_PHASE_COMMIT) == "true"; - LOG(INFO) << "new income streaming load request." << ctx->brief() - << " sql : " << req->header(HTTP_SQL); - + << " sql : " << req->header(HTTP_SQL) << ", group_commit=" << ctx->group_commit; if (st.ok()) { st = _on_header(req, ctx); } diff --git a/be/src/http/action/stream_load.cpp b/be/src/http/action/stream_load.cpp index ad476ee08e..846bbbdc1c 100644 --- a/be/src/http/action/stream_load.cpp +++ b/be/src/http/action/stream_load.cpp @@ -199,28 +199,36 @@ int StreamLoadAction::on_header(HttpRequest* req) { group_commit_mode = ""; } } - if (!group_commit_mode.empty() || config::wait_internal_group_commit_finish) { + auto partial_columns = !req->header(HTTP_PARTIAL_COLUMNS).empty() && + iequal(req->header(HTTP_PARTIAL_COLUMNS), "true"); + ctx->two_phase_commit = req->header(HTTP_TWO_PHASE_COMMIT) == "true"; + auto temp_partitions = !req->header(HTTP_TEMP_PARTITIONS).empty(); + auto partitions = !req->header(HTTP_PARTITIONS).empty(); + if (!partial_columns && !partitions && !temp_partitions && !ctx->two_phase_commit && + (!group_commit_mode.empty() || config::wait_internal_group_commit_finish)) { if (!group_commit_mode.empty() && !ctx->label.empty()) { st = Status::InternalError("label and group_commit can't be set at the same time"); } - ctx->group_commit = load_size_smaller_than_wal_limit(req); - if (!ctx->group_commit) { - LOG(WARNING) << "The data size for this stream load(" - << std::stol(req->header(HttpHeaders::CONTENT_LENGTH)) - << " Bytes) exceeds the WAL (Write-Ahead Log) limit (" - << config::wal_max_disk_size * 0.8 - << " Bytes). Please set this load to \"group commit\"=false."; - st = Status::InternalError("Stream load size too large."); + if (config::wait_internal_group_commit_finish) { + ctx->group_commit = true; + } else { + ctx->group_commit = load_size_smaller_than_wal_limit(req); + if (!ctx->group_commit) { + LOG(WARNING) << "The data size for this stream load(" + << req->header(HttpHeaders::CONTENT_LENGTH) + << " Bytes) exceeds the WAL (Write-Ahead Log) limit (" + << config::wal_max_disk_size * 0.8 + << " Bytes). Please set this load to \"group commit\"=false."; + st = Status::InternalError("Stream load size too large."); + } } } if (!ctx->group_commit && ctx->label.empty()) { ctx->label = generate_uuid_string(); } - ctx->two_phase_commit = req->header(HTTP_TWO_PHASE_COMMIT) == "true"; - LOG(INFO) << "new income streaming load request." << ctx->brief() << ", db=" << ctx->db - << ", tbl=" << ctx->table; + << ", tbl=" << ctx->table << ", group_commit=" << ctx->group_commit; if (st.ok()) { st = _on_header(req, ctx); diff --git a/fe/fe-common/src/main/java/org/apache/doris/common/Config.java b/fe/fe-common/src/main/java/org/apache/doris/common/Config.java index 9421364b48..486e2bfdfb 100644 --- a/fe/fe-common/src/main/java/org/apache/doris/common/Config.java +++ b/fe/fe-common/src/main/java/org/apache/doris/common/Config.java @@ -507,6 +507,10 @@ public class Config extends ConfigBase { + "insert into and stream load use group commit by default."}) public static boolean wait_internal_group_commit_finish = false; + @ConfField(mutable = false, masterOnly = true, description = {"攒批的默认提交时间,单位是毫秒", + "Default commit interval in ms for group commit"}) + public static int group_commit_interval_ms_default_value = 10000; + @ConfField(mutable = true, masterOnly = true, description = {"Stream load 的默认超时时间,单位是秒。", "Default timeout for stream load job, in seconds."}) public static int stream_load_default_timeout_second = 86400 * 3; // 3days diff --git a/fe/fe-core/src/main/java/org/apache/doris/analysis/ArrayLiteral.java b/fe/fe-core/src/main/java/org/apache/doris/analysis/ArrayLiteral.java index c9661a5527..540a0747b4 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/analysis/ArrayLiteral.java +++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/ArrayLiteral.java @@ -136,8 +136,14 @@ public class ArrayLiteral extends LiteralExpr { public String getStringValueInFe() { List list = new ArrayList<>(children.size()); children.forEach(v -> { + String stringLiteral; + if (v instanceof NullLiteral) { + stringLiteral = "null"; + } else { + stringLiteral = getStringLiteralForComplexType(v); + } // we should use type to decide we output array is suitable for json format - list.add(getStringLiteralForComplexType(v)); + list.add(stringLiteral); }); return "[" + StringUtils.join(list, ", ") + "]"; } diff --git a/fe/fe-core/src/main/java/org/apache/doris/analysis/MapLiteral.java b/fe/fe-core/src/main/java/org/apache/doris/analysis/MapLiteral.java index d35f825d6a..e55519101c 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/analysis/MapLiteral.java +++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/MapLiteral.java @@ -155,11 +155,21 @@ public class MapLiteral extends LiteralExpr { public String getStringValue() { List list = new ArrayList<>(children.size()); for (int i = 0; i < children.size() && i + 1 < children.size(); i += 2) { - list.add(children.get(i).getStringValue() + ":" + children.get(i + 1).getStringValue()); + list.add(getStringValue(children.get(i)) + ":" + getStringValue(children.get(i + 1))); } return "{" + StringUtils.join(list, ", ") + "}"; } + private String getStringValue(Expr expr) { + if (expr instanceof NullLiteral) { + return "null"; + } + if (expr instanceof StringLiteral) { + return "\"" + expr.getStringValue() + "\""; + } + return expr.getStringValue(); + } + @Override public String getStringValueForArray() { List list = new ArrayList<>(children.size()); 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 3937e5a674..b339b04254 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 @@ -54,6 +54,7 @@ import org.apache.doris.planner.GroupCommitPlanner; import org.apache.doris.planner.OlapTableSink; import org.apache.doris.planner.external.jdbc.JdbcTableSink; import org.apache.doris.qe.ConnectContext; +import org.apache.doris.qe.SqlModeHelper; import org.apache.doris.rewrite.ExprRewriter; import org.apache.doris.service.FrontendOptions; import org.apache.doris.thrift.TQueryOptions; @@ -1106,7 +1107,9 @@ public class NativeInsertStmt extends InsertStmt { LOG.warn("analyze group commit failed", e); return; } - if (ConnectContext.get().getSessionVariable().isEnableInsertGroupCommit() + boolean partialUpdate = ConnectContext.get().getSessionVariable().isEnableUniqueKeyPartialUpdate(); + if (!partialUpdate && ConnectContext.get().getSessionVariable().isEnableInsertGroupCommit() + && ConnectContext.get().getSessionVariable().getSqlMode() != SqlModeHelper.MODE_NO_BACKSLASH_ESCAPES && targetTable instanceof OlapTable && !ConnectContext.get().isTxnModel() && getQueryStmt() instanceof SelectStmt @@ -1117,18 +1120,22 @@ public class NativeInsertStmt extends InsertStmt { if (selectStmt.getValueList() != null) { for (List row : selectStmt.getValueList().getRows()) { for (Expr expr : row) { - if (!expr.isLiteralOrCastExpr()) { + if (!(expr instanceof LiteralExpr)) { return; } } } + // Does not support: insert into tbl values(); + if (selectStmt.getValueList().getFirstRow().isEmpty() && CollectionUtils.isEmpty(targetColumnNames)) { + return; + } } else { SelectList selectList = selectStmt.getSelectList(); if (selectList != null) { List items = selectList.getItems(); if (items != null) { for (SelectListItem item : items) { - if (item.getExpr() != null && !item.getExpr().isLiteralOrCastExpr()) { + if (item.getExpr() != null && !(item.getExpr() instanceof LiteralExpr)) { return; } } diff --git a/fe/fe-core/src/main/java/org/apache/doris/analysis/StructLiteral.java b/fe/fe-core/src/main/java/org/apache/doris/analysis/StructLiteral.java index 4645bd9edc..effd5c7d61 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/analysis/StructLiteral.java +++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/StructLiteral.java @@ -71,10 +71,21 @@ public class StructLiteral extends LiteralExpr { return "STRUCT(" + StringUtils.join(list, ", ") + ")"; } + private String getStringValue(Expr expr) { + String stringValue = expr.getStringValue(); + if (stringValue.isEmpty()) { + return "''"; + } + if (expr instanceof StringLiteral) { + return "\"" + stringValue + "\""; + } + return stringValue; + } + @Override public String getStringValue() { List list = new ArrayList<>(children.size()); - children.forEach(v -> list.add(v.getStringValue())); + children.forEach(v -> list.add(getStringValue(v))); return "{" + StringUtils.join(list, ", ") + "}"; } diff --git a/fe/fe-core/src/main/java/org/apache/doris/common/util/PropertyAnalyzer.java b/fe/fe-core/src/main/java/org/apache/doris/common/util/PropertyAnalyzer.java index 6fb93756f6..279eba12c1 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/common/util/PropertyAnalyzer.java +++ b/fe/fe-core/src/main/java/org/apache/doris/common/util/PropertyAnalyzer.java @@ -171,7 +171,8 @@ public class PropertyAnalyzer { private static final double MIN_FPP = 0.0001; public static final String PROPERTIES_GROUP_COMMIT_INTERVAL_MS = "group_commit_interval_ms"; - public static final int PROPERTIES_GROUP_COMMIT_INTERVAL_MS_DEFAULT_VALUE = 10000; + public static final int PROPERTIES_GROUP_COMMIT_INTERVAL_MS_DEFAULT_VALUE + = Config.group_commit_interval_ms_default_value; // compaction policy public static final String SIZE_BASED_COMPACTION_POLICY = "size_based"; diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/InsertIntoTableCommand.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/InsertIntoTableCommand.java index 9dcba2ce9a..eaee75936d 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/InsertIntoTableCommand.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/InsertIntoTableCommand.java @@ -46,6 +46,7 @@ import org.apache.doris.planner.UnionNode; import org.apache.doris.proto.InternalService; import org.apache.doris.proto.InternalService.PGroupCommitInsertResponse; import org.apache.doris.qe.ConnectContext; +import org.apache.doris.qe.SqlModeHelper; import org.apache.doris.qe.StmtExecutor; import org.apache.doris.rpc.RpcException; import org.apache.doris.thrift.TStatusCode; @@ -219,17 +220,14 @@ public class InsertIntoTableCommand extends Command implements ForwardWithSync, private boolean analyzeGroupCommit(ConnectContext ctx, DataSink sink, PhysicalOlapTableSink physicalOlapTableSink) { - if (!(sink instanceof OlapTableSink)) { + if (!(sink instanceof OlapTableSink) || !ctx.getSessionVariable().isEnableInsertGroupCommit() + || ctx.getSessionVariable().isEnableUniqueKeyPartialUpdate()) { return false; } - if (!ctx.getSessionVariable().isEnableInsertGroupCommit()) { - return false; - } - return ConnectContext.get().getSessionVariable().isEnableInsertGroupCommit() - && physicalOlapTableSink.getTargetTable() instanceof OlapTable - && !ConnectContext.get().isTxnModel() - && sink.getFragment().getPlanRoot() instanceof UnionNode - && physicalOlapTableSink.getPartitionIds().isEmpty(); + return ConnectContext.get().getSessionVariable().getSqlMode() != SqlModeHelper.MODE_NO_BACKSLASH_ESCAPES + && physicalOlapTableSink.getTargetTable() instanceof OlapTable && !ConnectContext.get().isTxnModel() + && sink.getFragment().getPlanRoot() instanceof UnionNode && physicalOlapTableSink.getPartitionIds() + .isEmpty(); } @Override diff --git a/fe/fe-core/src/main/java/org/apache/doris/planner/GroupCommitPlanner.java b/fe/fe-core/src/main/java/org/apache/doris/planner/GroupCommitPlanner.java index bc99d771d9..6593142630 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/planner/GroupCommitPlanner.java +++ b/fe/fe-core/src/main/java/org/apache/doris/planner/GroupCommitPlanner.java @@ -212,6 +212,8 @@ public class GroupCommitPlanner { if (selectStmt.getValueList() != null) { for (List row : selectStmt.getValueList().getRows()) { InternalService.PDataRow data = StmtExecutor.getRowStringValue(row); + LOG.debug("add row: [{}]", data.getColList().stream().map(c -> c.getValue()) + .collect(Collectors.joining(","))); rows.add(data); } } else { @@ -224,6 +226,8 @@ public class GroupCommitPlanner { } } InternalService.PDataRow data = StmtExecutor.getRowStringValue(exprList); + LOG.debug("add row: [{}]", data.getColList().stream().map(c -> c.getValue()) + .collect(Collectors.joining(","))); rows.add(data); } return rows; diff --git a/fe/fe-core/src/main/java/org/apache/doris/planner/OlapTableSink.java b/fe/fe-core/src/main/java/org/apache/doris/planner/OlapTableSink.java index 357876c81c..610404e03a 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/planner/OlapTableSink.java +++ b/fe/fe-core/src/main/java/org/apache/doris/planner/OlapTableSink.java @@ -387,6 +387,7 @@ public class OlapTableSink extends DataSink { exprs.add(e.clone()); } for (Expr e : exprs) { + e.reset(); e.analyze(funcAnalyzer); } partitionParam.setPartitionFunctionExprs(Expr.treesToThrift(exprs)); 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 0aabb8d7f3..d19f68a6ca 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 @@ -293,9 +293,15 @@ public class StmtExecutor { if (expr instanceof NullLiteral) { row.addColBuilder().setValue(NULL_VALUE_FOR_LOAD); } else if (expr instanceof ArrayLiteral) { - row.addColBuilder().setValue(String.format("\"%s\"", expr.getStringValueForArray())); + row.addColBuilder().setValue(String.format("\"%s\"", expr.getStringValueInFe())); } else { - row.addColBuilder().setValue(String.format("\"%s\"", expr.getStringValue())); + String stringValue = expr.getStringValueInFe(); + if (stringValue.equals(NULL_VALUE_FOR_LOAD) || stringValue.startsWith("\"") || stringValue.endsWith( + "\"")) { + row.addColBuilder().setValue(String.format("\"%s\"", stringValue)); + } else { + row.addColBuilder().setValue(String.format("%s", stringValue)); + } } } return row.build(); @@ -452,8 +458,10 @@ public class StmtExecutor { // try to fall back to legacy planner LOG.debug("nereids cannot process statement\n" + originStmt.originStmt + "\n because of " + e.getMessage(), e); + boolean isInsertIntoCommand = parsedStmt != null && parsedStmt instanceof LogicalPlanAdapter + && ((LogicalPlanAdapter) parsedStmt).getLogicalPlan() instanceof InsertIntoTableCommand; if (e instanceof NereidsException - && !context.getSessionVariable().enableFallbackToOriginalPlanner) { + && !context.getSessionVariable().enableFallbackToOriginalPlanner && !isInsertIntoCommand) { LOG.warn("Analyze failed. {}", context.getQueryIdentifier(), e); throw ((NereidsException) e).getException(); } diff --git a/fe/fe-core/src/main/java/org/apache/doris/service/FrontendServiceImpl.java b/fe/fe-core/src/main/java/org/apache/doris/service/FrontendServiceImpl.java index a8fdbf60cf..85048cf605 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/service/FrontendServiceImpl.java +++ b/fe/fe-core/src/main/java/org/apache/doris/service/FrontendServiceImpl.java @@ -1994,7 +1994,7 @@ public class FrontendServiceImpl implements FrontendService.Iface { parsedStmt.setOrigStmt(new OriginStatement(originStmt, 0)); parsedStmt.setUserInfo(ctx.getCurrentUserIdentity()); if (!StringUtils.isEmpty(request.getGroupCommitMode())) { - if (parsedStmt.getLabel() != null) { + if (!Config.wait_internal_group_commit_finish && parsedStmt.getLabel() != null) { throw new AnalysisException("label and group_commit can't be set at the same time"); } ctx.getSessionVariable().groupCommit = request.getGroupCommitMode(); diff --git a/regression-test/data/insert_p0/insert_with_null.out b/regression-test/data/insert_p0/insert_with_null.out index 7ee81a10bc..c4f2e363aa 100644 --- a/regression-test/data/insert_p0/insert_with_null.out +++ b/regression-test/data/insert_p0/insert_with_null.out @@ -2,54 +2,77 @@ -- !sql -- 1 "b" ["k1=v1, k2=v2"] 2 N ["k3=v3, k4=v4"] -4 null [] -5 NULL ["k5, k6"] -6 \N ["k7", "k8"] -7 abc \N +3 null [] +4 NULL ["k5, k6"] +5 \N ["k7", "k8"] +6 \n ["k7", "k8"] +7 ["k7", "k8"] +8 ["k7", "k8"] +9 "a ["k7", "k8"] +10 a" ["k7", "k8"] +22 \N \N -- !sql -- -6 \N ["k7", "k8"] +5 \N ["k7", "k8"] +22 \N \N -- !sql -- -7 abc \N +22 \N \N -- !sql -- 1 "b" ["k1=v1, k2=v2"] 2 N ["k3=v3, k4=v4"] -4 null [] -5 NULL ["k5, k6"] -6 \N ["k7", "k8"] +3 null [] +4 NULL ["k5, k6"] +5 \N ["k7", "k8"] +6 \n ["k7", "k8"] +7 ["k7", "k8"] +8 ["k7", "k8"] +9 "a ["k7", "k8"] +10 a" ["k7", "k8"] -- !sql -- -6 \N ["k7", "k8"] +5 \N ["k7", "k8"] -- !sql -- -- !sql -- 1 "b" ["k1=v1, k2=v2"] 2 N ["k3=v3, k4=v4"] -4 null [] -5 NULL ["k5, k6"] -6 \N ["k7", "k8"] -7 abc \N +3 null [] +4 NULL ["k5, k6"] +5 \N ["k7", "k8"] +6 \n ["k7", "k8"] +7 ["k7", "k8"] +8 ["k7", "k8"] +9 "a ["k7", "k8"] +10 a" ["k7", "k8"] +22 \N \N -- !sql -- -6 \N ["k7", "k8"] +5 \N ["k7", "k8"] +22 \N \N -- !sql -- -7 abc \N +22 \N \N -- !sql -- 1 "b" ["k1=v1, k2=v2"] 2 N ["k3=v3, k4=v4"] -4 null [] -5 NULL ["k5, k6"] -6 \N ["k7", "k8"] -7 abc \N +3 null [] +4 NULL ["k5, k6"] +5 \N ["k7", "k8"] +6 \n ["k7", "k8"] +7 ["k7", "k8"] +8 ["k7", "k8"] +9 "a ["k7", "k8"] +10 a" ["k7", "k8"] +22 \N \N -- !sql -- -6 \N ["k7", "k8"] +5 \N ["k7", "k8"] +22 \N \N -- !sql -- -7 abc \N +22 \N \N diff --git a/regression-test/suites/insert_p0/insert_with_null.groovy b/regression-test/suites/insert_p0/insert_with_null.groovy index 5a7a19f82e..e93fa8eb41 100644 --- a/regression-test/suites/insert_p0/insert_with_null.groovy +++ b/regression-test/suites/insert_p0/insert_with_null.groovy @@ -78,16 +78,21 @@ suite("insert_with_null") { sql """ insert into ${table} values(1, '"b"', ["k1=v1, k2=v2"]); """ sql """ insert into ${table} values(2, "\\N", ['k3=v3, k4=v4']); """ - // sql """ insert into ${table} values(3, "\\\\N", []); """ - sql """ insert into ${table} values(4, 'null', []); """ - sql """ insert into ${table} values(5, 'NULL', ['k5, k6']); """ - sql """ insert into ${table} values(6, null, ["k7", "k8"]); """ + sql """ insert into ${table} values(3, 'null', []); """ + sql """ insert into ${table} values(4, 'NULL', ['k5, k6']); """ + sql """ insert into ${table} values(5, null, ["k7", "k8"]); """ + sql """ insert into ${table} values(6, "\\n", ["k7", "k8"]); """ + sql """ insert into ${table} values(7, "", ["k7", "k8"]); """ + sql """ insert into ${table} values(8, '', ["k7", "k8"]); """ + sql """ insert into ${table} values(9, '"a', ["k7", "k8"]); """ + sql """ insert into ${table} values(10, 'a"', ["k7", "k8"]); """ + // sql """ insert into ${table} values(21, "\\\\N", []); """ if (write_mode != "txn_insert") { - sql """ insert into ${table}(id, name) values(7, 'abc'); """ - getRowCount(6) + sql """ insert into ${table}(id) values(22); """ + getRowCount(11) } else { sql "commit" - getRowCount(5) + getRowCount(10) } qt_sql """ select * from ${table} order by id asc; """