This is an automated email from the ASF dual-hosted git repository.
yiguolei pushed a commit to branch branch-4.0
in repository https://gitbox.apache.org/repos/asf/doris.git
The following commit(s) were added to refs/heads/branch-4.0 by this push:
new 9812408dec8 branch-4.0: [improvement](error msg) prioritize retaining
the error URL and first_error_msg when truncation occurs #56759 (#58145)
9812408dec8 is described below
commit 9812408dec811f098bff3828beb52ecdcefc7e4d
Author: github-actions[bot]
<41898282+github-actions[bot]@users.noreply.github.com>
AuthorDate: Thu Nov 20 09:55:29 2025 +0800
branch-4.0: [improvement](error msg) prioritize retaining the error URL and
first_error_msg when truncation occurs #56759 (#58145)
Cherry-picked from #56759
Co-authored-by: Refrain <[email protected]>
---
.../insert/BaseExternalTableInsertExecutor.java | 12 +-
.../commands/insert/DictionaryInsertExecutor.java | 13 +-
.../trees/plans/commands/insert/InsertUtils.java | 96 +++++++++
.../insert/OlapGroupCommitInsertExecutor.java | 13 +-
.../plans/commands/insert/OlapInsertExecutor.java | 12 +-
.../plans/commands/insert/InsertUtilsTest.java | 220 +++++++++++++++++++++
.../fault_tolerance_nereids.groovy | 2 +-
.../manual-partitioning.md.groovy | 5 +-
.../insert_overwrite_auto_detect.groovy | 6 +-
.../test_iot_overwrite_and_create.groovy | 10 +-
.../insert_p0/test_error_msg_truncate.groovy | 150 ++++++++++++++
.../suites/insert_p0/test_insert_docs_demo.groovy | 2 +-
.../test_tvf_strict_mode_and_filter_ratio.groovy | 2 +-
.../test_auto_partition_behavior.groovy | 2 +-
.../test_list_partition_datatype.groovy | 2 +-
.../test_multi_column_partition.groovy | 2 +-
.../create/test_create_vault_with_kerberos.groovy | 2 +-
17 files changed, 514 insertions(+), 37 deletions(-)
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/insert/BaseExternalTableInsertExecutor.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/insert/BaseExternalTableInsertExecutor.java
index ccaf1229480..b1f2856b767 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/insert/BaseExternalTableInsertExecutor.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/insert/BaseExternalTableInsertExecutor.java
@@ -135,18 +135,20 @@ public abstract class BaseExternalTableInsertExecutor
extends AbstractInsertExec
String queryId = DebugUtil.printId(ctx.queryId());
// if any throwable being thrown during insert operation, first we
should abort this txn
LOG.warn("insert [{}] with query id {} failed", labelName, queryId, t);
- StringBuilder sb = new StringBuilder(t.getMessage());
+ String firstErrorMsgPart = "";
+ String urlPart = "";
if (txnId != INVALID_TXN_ID) {
LOG.warn("insert [{}] with query id {} abort txn {} failed",
labelName, queryId, txnId);
if (!Strings.isNullOrEmpty(coordinator.getFirstErrorMsg())) {
- sb.append(". first_error_msg: ").append(
- StringUtils.abbreviate(coordinator.getFirstErrorMsg(),
Config.first_error_msg_max_length));
+ firstErrorMsgPart =
StringUtils.abbreviate(coordinator.getFirstErrorMsg(),
+ Config.first_error_msg_max_length);
}
if (!Strings.isNullOrEmpty(coordinator.getTrackingUrl())) {
- sb.append(". url: ").append(coordinator.getTrackingUrl());
+ urlPart = coordinator.getTrackingUrl();
}
}
- ctx.getState().setError(ErrorCode.ERR_UNKNOWN_ERROR, t.getMessage());
+ String finalErrorMsg = InsertUtils.getFinalErrorMsg(t.getMessage(),
firstErrorMsgPart, urlPart);
+ ctx.getState().setError(ErrorCode.ERR_UNKNOWN_ERROR, finalErrorMsg);
if (table instanceof ExternalTable) {
try {
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/insert/DictionaryInsertExecutor.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/insert/DictionaryInsertExecutor.java
index 18afeb39cc8..d29a3350ba3 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/insert/DictionaryInsertExecutor.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/insert/DictionaryInsertExecutor.java
@@ -28,7 +28,6 @@ import org.apache.doris.planner.PlanFragment;
import org.apache.doris.qe.ConnectContext;
import org.apache.doris.qe.StmtExecutor;
-import com.google.common.base.Strings;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@@ -79,12 +78,14 @@ public class DictionaryInsertExecutor extends
AbstractInsertExecutor {
errMsg = t.getMessage() == null ? "unknown reason" :
ddlException.getMessage();
String queryId = DebugUtil.printId(ctx.queryId());
LOG.warn("dictionary insert [{}] with query id {} failed", labelName,
queryId, ddlException);
- StringBuilder sb = new StringBuilder(errMsg);
- if (!Strings.isNullOrEmpty(coordinator.getTrackingUrl())) {
- sb.append(". url: ").append(coordinator.getTrackingUrl());
- }
+
+ String finalErrorMsg = InsertUtils.getFinalErrorMsg(
+ errMsg,
+ "",
+ coordinator.getTrackingUrl()
+ );
// we should set the context to make the caller know the command failed
- ctx.getState().setError(ddlException.getMysqlErrorCode(),
sb.toString());
+ ctx.getState().setError(ddlException.getMysqlErrorCode(),
finalErrorMsg);
}
@Override
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/insert/InsertUtils.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/insert/InsertUtils.java
index 5e7d2c82f47..077e57daaa9 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/insert/InsertUtils.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/insert/InsertUtils.java
@@ -27,6 +27,7 @@ import org.apache.doris.catalog.Table;
import org.apache.doris.catalog.TableIf;
import org.apache.doris.common.Config;
import org.apache.doris.common.FormatOptions;
+import org.apache.doris.common.util.DebugPointUtil;
import org.apache.doris.datasource.hive.HMSExternalTable;
import org.apache.doris.datasource.jdbc.JdbcExternalTable;
import org.apache.doris.nereids.CascadesContext;
@@ -86,6 +87,7 @@ import org.apache.doris.transaction.TransactionEntry;
import org.apache.doris.transaction.TransactionState;
import org.apache.doris.transaction.TransactionStatus;
+import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
@@ -671,4 +673,98 @@ public class InsertUtils {
}
}
}
+
+ /**
+ * Cut the error message to ensure it fits within MySQL error packet size
limit.
+ * @param msg the main error message
+ * @param firstErrorMsg the first error message from coordinator
+ * @param url the tracking URL for error log
+ * @return the final truncated error message
+ */
+ public static String getFinalErrorMsg(String msg, String firstErrorMsg,
String url) {
+ int maxTotalBytes = 512;
+
+ // For test
+ // 1. error msg length > 512, we will truncate it first to make sure
the URL
+ // and FirstErrorMsg keep complete.
+ // 2. if the FirstErrorMsg and URL length > 512 too, we will remind
user use
+ // `show load` too see detail
+ if (DebugPointUtil.isEnable("TestErrorMsgTruncate")) {
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < 128; i++) {
+ sb.append("Test");
+ }
+ msg = sb.toString();
+ firstErrorMsg = sb.toString();
+ url = sb.toString();
+ }
+
+ // Calculate lengths first to avoid unnecessary string concatenation
+ int firstErrorMsgPartLen = 0;
+ int urlPartLen = 0;
+
+ if (!Strings.isNullOrEmpty(firstErrorMsg)) {
+ firstErrorMsgPartLen = ". first_error_msg: ".length() +
firstErrorMsg.length();
+ }
+
+ if (!Strings.isNullOrEmpty(url)) {
+ urlPartLen = ". url: ".length() + url.length();
+ }
+
+ // use boolean too avoid string copy
+ boolean useFirstErrorMsgPlaceholder = false;
+ boolean useUrlPlaceholder = false;
+
+ // special case: url length > 512 or first error msg length > 512 or
sum > 512
+ if (urlPartLen > maxTotalBytes && firstErrorMsgPartLen >
maxTotalBytes) {
+ useUrlPlaceholder = true;
+ urlPartLen = ". url: please use `show load` for detail
msg".length();
+ // only show once is enough
+ firstErrorMsgPartLen = 0;
+ } else if (urlPartLen > maxTotalBytes) {
+ useUrlPlaceholder = true;
+ urlPartLen = ". url: please use `show load` for detail
msg".length();
+ } else if (firstErrorMsgPartLen > maxTotalBytes) {
+ useFirstErrorMsgPlaceholder = true;
+ firstErrorMsgPartLen = ". first_error_msg: please use `show load`
for detail msg".length();
+ } else if (urlPartLen + firstErrorMsgPartLen > maxTotalBytes) {
+ int tempLen = ". url: please use `show load` for detail
msg".length();
+ if (tempLen + firstErrorMsgPartLen > maxTotalBytes) {
+ // just leave firstErrorMsg
+ urlPartLen = 0;
+ } else {
+ useUrlPlaceholder = true;
+ urlPartLen = tempLen;
+ }
+ }
+
+ int maxMessageBytes = maxTotalBytes - firstErrorMsgPartLen -
urlPartLen;
+
+ if (msg.length() > maxMessageBytes && maxMessageBytes > 0) {
+ msg = msg.substring(0, maxMessageBytes - 1);
+ }
+
+ StringBuilder finalErrorMsg = new StringBuilder();
+ finalErrorMsg.append(msg);
+
+ // Append firstErrorMsg part directly
+ if (firstErrorMsgPartLen > 0) {
+ if (useFirstErrorMsgPlaceholder) {
+ finalErrorMsg.append(". first_error_msg: please use `show
load` for detail msg");
+ } else {
+ finalErrorMsg.append(". first_error_msg:
").append(firstErrorMsg);
+ }
+ }
+
+ // Append url part directly
+ if (urlPartLen > 0) {
+ if (useUrlPlaceholder) {
+ finalErrorMsg.append(". url: please use `show load` for detail
msg");
+ } else {
+ finalErrorMsg.append(". url: ").append(url);
+ }
+ }
+
+ return finalErrorMsg.toString();
+ }
}
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/insert/OlapGroupCommitInsertExecutor.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/insert/OlapGroupCommitInsertExecutor.java
index 803b009d136..0b4d9026d91 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/insert/OlapGroupCommitInsertExecutor.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/insert/OlapGroupCommitInsertExecutor.java
@@ -192,15 +192,18 @@ public class OlapGroupCommitInsertExecutor extends
OlapInsertExecutor {
String queryId = DebugUtil.printId(ctx.queryId());
// if any throwable being thrown during insert operation, first we
should abort this txn
LOG.warn("insert [{}] with query id {} failed, url={}", labelName,
queryId, coordinator.getTrackingUrl(), t);
- StringBuilder sb = new StringBuilder(t.getMessage());
+ String firstErrorMsgPart = "";
+ String urlPart = "";
if (!Strings.isNullOrEmpty(coordinator.getFirstErrorMsg())) {
- sb.append(". first_error_msg: ").append(
- StringUtils.abbreviate(coordinator.getFirstErrorMsg(),
Config.first_error_msg_max_length));
+ firstErrorMsgPart =
StringUtils.abbreviate(coordinator.getFirstErrorMsg(),
+ Config.first_error_msg_max_length);
}
if (!Strings.isNullOrEmpty(coordinator.getTrackingUrl())) {
- sb.append(". url: ").append(coordinator.getTrackingUrl());
+ urlPart = coordinator.getTrackingUrl();
}
- ctx.getState().setError(ErrorCode.ERR_UNKNOWN_ERROR, sb.toString());
+
+ String finalErrorMsg = InsertUtils.getFinalErrorMsg(errMsg,
firstErrorMsgPart, urlPart);
+ ctx.getState().setError(ErrorCode.ERR_UNKNOWN_ERROR, finalErrorMsg);
}
@Override
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/insert/OlapInsertExecutor.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/insert/OlapInsertExecutor.java
index 7fceb289ef1..b078afd5abd 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/insert/OlapInsertExecutor.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/insert/OlapInsertExecutor.java
@@ -290,15 +290,17 @@ public class OlapInsertExecutor extends
AbstractInsertExecutor {
if (Config.isCloudMode() &&
t.getMessage().contains(FeConstants.CLOUD_RETRY_E230)) {
return;
}
- StringBuilder sb = new StringBuilder(t.getMessage());
+ String firstErrorMsgPart = "";
+ String urlPart = "";
if (!Strings.isNullOrEmpty(coordinator.getFirstErrorMsg())) {
- sb.append(". first_error_msg: ").append(
- StringUtils.abbreviate(coordinator.getFirstErrorMsg(),
Config.first_error_msg_max_length));
+ firstErrorMsgPart =
StringUtils.abbreviate(coordinator.getFirstErrorMsg(),
+ Config.first_error_msg_max_length);
}
if (!Strings.isNullOrEmpty(coordinator.getTrackingUrl())) {
- sb.append(". url: ").append(coordinator.getTrackingUrl());
+ urlPart = coordinator.getTrackingUrl();
}
- ctx.getState().setError(ErrorCode.ERR_UNKNOWN_ERROR, sb.toString());
+ String finalErrorMsg = InsertUtils.getFinalErrorMsg(errMsg,
firstErrorMsgPart, urlPart);
+ ctx.getState().setError(ErrorCode.ERR_UNKNOWN_ERROR, finalErrorMsg);
}
@Override
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/plans/commands/insert/InsertUtilsTest.java
b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/plans/commands/insert/InsertUtilsTest.java
new file mode 100644
index 00000000000..98bd8b1f5e9
--- /dev/null
+++
b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/plans/commands/insert/InsertUtilsTest.java
@@ -0,0 +1,220 @@
+// 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.
+
+package org.apache.doris.nereids.trees.plans.commands.insert;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Test for InsertUtils.getFinalErrorMsg()
+ */
+public class InsertUtilsTest {
+
+ private static final int MAX_TOTAL_BYTES = 512;
+
+ private String generateString(int length) {
+ return generateString(length, "X");
+ }
+
+ private String generateString(int length, String prefix) {
+ StringBuilder sb = new StringBuilder(length);
+ sb.append(prefix);
+ for (int i = prefix.length(); i < length; i++) {
+ sb.append((char) ('A' + (i % 26)));
+ }
+ return sb.toString();
+ }
+
+ /**
+ * case1: normal
+ */
+ @Test
+ public void testNormalCase() {
+ String msg = "Insert failed";
+ String firstErrorMsg = "Row format error";
+ String url = "http://example.com/error_log";
+
+ String result = InsertUtils.getFinalErrorMsg(msg, firstErrorMsg, url);
+
+ Assertions.assertTrue(result.contains(msg));
+ Assertions.assertTrue(result.contains(firstErrorMsg));
+ Assertions.assertTrue(result.contains(url));
+ Assertions.assertTrue(result.contains("first_error_msg:"));
+ Assertions.assertTrue(result.contains("url:"));
+ Assertions.assertTrue(result.length() <= MAX_TOTAL_BYTES);
+ }
+
+ /**
+ * case2: Msg is too long
+ */
+ @Test
+ public void testLongMsg() {
+ String msg = generateString(600);
+ String firstErrorMsg = "Short error";
+ String url = "http://example.com";
+
+ String result = InsertUtils.getFinalErrorMsg(msg, firstErrorMsg, url);
+
+ Assertions.assertTrue(result.contains(firstErrorMsg));
+ Assertions.assertTrue(result.contains(url));
+ Assertions.assertTrue(result.length() <= MAX_TOTAL_BYTES);
+ Assertions.assertTrue(result.indexOf(msg) == -1 || result.length() <=
MAX_TOTAL_BYTES);
+ }
+
+ /**
+ * case3: firstErrorMsg is too long
+ */
+ @Test
+ public void testLongFirstErrorMsg() {
+ String msg = "Insert failed";
+ String firstErrorMsg = generateString(600);
+ String url = "http://example.com";
+
+ String result = InsertUtils.getFinalErrorMsg(msg, firstErrorMsg, url);
+
+ Assertions.assertTrue(result.contains(msg));
+ Assertions.assertTrue(result.contains("please use `show load` for
detail msg"));
+ Assertions.assertTrue(result.contains(url));
+ Assertions.assertTrue(result.length() <= MAX_TOTAL_BYTES);
+ Assertions.assertFalse(result.contains(firstErrorMsg));
+ }
+
+ /**
+ * case4: url is too long
+ */
+ @Test
+ public void testLongUrl() {
+ String msg = "Insert failed";
+ String firstErrorMsg = "Row format error";
+ String url = "http://example.com/" + generateString(600);
+
+ String result = InsertUtils.getFinalErrorMsg(msg, firstErrorMsg, url);
+
+ Assertions.assertTrue(result.contains(msg));
+ Assertions.assertTrue(result.contains(firstErrorMsg));
+ Assertions.assertTrue(result.contains("please use `show load` for
detail msg"));
+ Assertions.assertTrue(result.length() <= MAX_TOTAL_BYTES);
+ Assertions.assertFalse(result.contains(url));
+ }
+
+ /**
+ * case5:firstErrorMsg and url are too long
+ */
+ @Test
+ public void testBothFirstErrorMsgAndUrlTooLong() {
+ String msg = "Insert failed";
+ String firstErrorMsg = generateString(600);
+ String url = "http://example.com/" + generateString(600);
+
+ String result = InsertUtils.getFinalErrorMsg(msg, firstErrorMsg, url);
+
+ Assertions.assertTrue(result.contains(msg));
+ Assertions.assertTrue(result.contains("please use `show load` for
detail msg"));
+ Assertions.assertTrue(result.length() <= MAX_TOTAL_BYTES);
+ Assertions.assertFalse(result.contains(firstErrorMsg));
+ Assertions.assertFalse(result.contains(url));
+ }
+
+ /**
+ * case6: firstErrorMsg , msg and url are too long
+ */
+ @Test
+ public void testAllParametersTooLong() {
+ String msg = generateString(600);
+ String firstErrorMsg = generateString(600);
+ String url = "http://example.com/" + generateString(600);
+
+ String result = InsertUtils.getFinalErrorMsg(msg, firstErrorMsg, url);
+
+ Assertions.assertTrue(result.contains("please use `show load` for
detail msg"));
+ Assertions.assertTrue(result.length() <= MAX_TOTAL_BYTES);
+ Assertions.assertFalse(result.contains(msg));
+ Assertions.assertFalse(result.contains(firstErrorMsg));
+ Assertions.assertFalse(result.contains(url));
+ }
+
+ /**
+ * case7 : msg length == 512
+ */
+ @Test
+ public void testMsgExactly512() {
+ String msg = generateString(512);
+ String firstErrorMsg = "";
+ String url = "";
+
+ String result = InsertUtils.getFinalErrorMsg(msg, firstErrorMsg, url);
+
+ Assertions.assertTrue(result.length() <= MAX_TOTAL_BYTES);
+ }
+
+ /**
+ * case8: urlPartLen + firstErrorMsgPartLen > 512, but tempLen +
firstErrorMsgPartLen still > 512
+ * Should only keep firstErrorMsg and drop url
+ */
+ @Test
+ public void testUrlAndFirstErrorMsgSumTooLong_DropUrl() {
+ String msg = "Insert failed";
+ // ". first_error_msg: ".length() = 19
+ // We need firstErrorMsgPartLen such that:
+ // ". url: please use `show load` for detail msg".length() +
firstErrorMsgPartLen > 512
+ // ". url: please use `show load` for detail msg".length() = 47
+ // So firstErrorMsgPartLen should be > 465
+ // firstErrorMsgPartLen = 19 + firstErrorMsg.length() > 465
+ // firstErrorMsg.length() should be > 446
+ String firstErrorMsg = generateString(470, "ERROR_MSG_");
+ String url = generateString(100, "URL_");
+
+ String result = InsertUtils.getFinalErrorMsg(msg, firstErrorMsg, url);
+
+ Assertions.assertTrue(result.contains(msg));
+ Assertions.assertTrue(result.contains(firstErrorMsg));
+ Assertions.assertFalse(result.contains(url));
+ Assertions.assertFalse(result.contains("please use `show load` for
detail msg"));
+ Assertions.assertTrue(result.length() <= MAX_TOTAL_BYTES);
+ }
+
+ /**
+ * case9: urlPartLen + firstErrorMsgPartLen > 512, but tempLen +
firstErrorMsgPartLen <= 512
+ * Should use url placeholder and keep firstErrorMsg
+ */
+ @Test
+ public void testUrlAndFirstErrorMsgSumTooLong_UseUrlPlaceholder() {
+ String msg = "Insert failed";
+ // ". first_error_msg: ".length() = 19
+ // ". url: ".length() = 7
+ // ". url: please use `show load` for detail msg".length() = 47
+ // We need: urlPartLen + firstErrorMsgPartLen > 512
+ // AND: 47 + firstErrorMsgPartLen <= 512
+ // So firstErrorMsgPartLen should be in range (512-urlPartLen, 465]
+ // Let's make firstErrorMsg.length() = 400, so firstErrorMsgPartLen =
419
+ // And url.length() = 100, so urlPartLen = 107
+ // Then urlPartLen + firstErrorMsgPartLen = 526 > 512 ✓
+ // And 47 + 419 = 466 <= 512 ✓
+ String firstErrorMsg = generateString(400, "FIRST_ERROR_");
+ String url = generateString(100, "URL_CONTENT_");
+
+ String result = InsertUtils.getFinalErrorMsg(msg, firstErrorMsg, url);
+
+ Assertions.assertTrue(result.contains(msg));
+ Assertions.assertTrue(result.contains(firstErrorMsg));
+ Assertions.assertTrue(result.contains("please use `show load` for
detail msg"));
+ Assertions.assertFalse(result.contains(url));
+ Assertions.assertTrue(result.length() <= MAX_TOTAL_BYTES);
+ }
+}
+
diff --git
a/regression-test/suites/ddl_p0/test_create_table_generated_column/fault_tolerance_nereids.groovy
b/regression-test/suites/ddl_p0/test_create_table_generated_column/fault_tolerance_nereids.groovy
index e85abae01ef..02409621bdc 100644
---
a/regression-test/suites/ddl_p0/test_create_table_generated_column/fault_tolerance_nereids.groovy
+++
b/regression-test/suites/ddl_p0/test_create_table_generated_column/fault_tolerance_nereids.groovy
@@ -175,7 +175,7 @@ suite("test_generated_column_fault_tolerance_nereids") {
PROPERTIES("replication_num" = "1");
;"""
// qt_common_default_test_insert_null
- def exception_str = isGroupCommitMode() ? "too many filtered rows" :
"Insert has filtered data in strict mode"
+ def exception_str = isGroupCommitMode() ? "too many filtered rows" : "null
value for not null column"
test {
sql "INSERT INTO test_gen_col_common_ft(a,b) values(1,null);"
exception exception_str
diff --git
a/regression-test/suites/doc/table-design/data-partitioning/manual-partitioning.md.groovy
b/regression-test/suites/doc/table-design/data-partitioning/manual-partitioning.md.groovy
index 0ab6b1cc5f1..1e1e451240a 100644
---
a/regression-test/suites/doc/table-design/data-partitioning/manual-partitioning.md.groovy
+++
b/regression-test/suites/doc/table-design/data-partitioning/manual-partitioning.md.groovy
@@ -67,8 +67,9 @@
suite("docs/table-design/data-partitioning/manual-partitioning.md") {
sql " insert into null_range2 values (null) "
Assertions.fail("The SQL above should throw an exception as
follows:\n\t\terrCode = 2, detailMessage = Insert has filtered data in strict
mode. url:
http://127.0.0.1:8040/api/_load_error_log?file=__shard_0/error_log_insert_stmt_b3a6d1f1fac74750-b3bb5d6e92a66da4_b3a6d1f1fac74750_b3bb5d6e92a66da4")
} catch (Exception e) {
- assertTrue(e.getMessage().contains("errCode = 2, detailMessage =
Insert has filtered data in strict mode."))
- assertTrue(e.getMessage().contains("url:"))
+ log.info("Caught exception message: " + e.getMessage())
+ assertTrue(e.getMessage().contains("errCode = 2"))
+ assertTrue(e.getMessage().contains("no partition for this tuple"))
}
} catch (Throwable t) {
Assertions.fail("examples in
docs/table-design/data-partitioning/manual-partitioning.md failed to exec,
please fix it", t)
diff --git
a/regression-test/suites/insert_overwrite_p0/insert_overwrite_auto_detect.groovy
b/regression-test/suites/insert_overwrite_p0/insert_overwrite_auto_detect.groovy
index 6c3c7e7cdd7..ee7c7c64481 100644
---
a/regression-test/suites/insert_overwrite_p0/insert_overwrite_auto_detect.groovy
+++
b/regression-test/suites/insert_overwrite_p0/insert_overwrite_auto_detect.groovy
@@ -114,7 +114,8 @@ suite("test_iot_auto_detect") {
} catch (Exception e) {
log.info(e.getMessage())
assertTrue(e.getMessage().contains('Insert has filtered data in strict
mode') ||
- e.getMessage().contains('Cannot found origin partitions in auto
detect overwriting'))
+ e.getMessage().contains('Cannot found origin partitions in auto
detect overwriting') ||
+ e.getMessage().contains('no partition for this tuple'))
}
sql " drop table if exists dt; "
@@ -138,7 +139,8 @@ suite("test_iot_auto_detect") {
sql """ insert overwrite table dt partition(*) values ("2023-02-02"),
("3000-12-12"); """
check { result, exception, startTime, endTime ->
assertTrue(exception.getMessage().contains('Insert has filtered data
in strict mode') ||
- exception.getMessage().contains('Cannot found origin partitions
in auto detect overwriting'))
+ exception.getMessage().contains('Cannot found origin partitions
in auto detect overwriting') ||
+ exception.getMessage().contains('no partition for this tuple'))
}
}
// test no rows(no partition hits) overwrite
diff --git
a/regression-test/suites/insert_overwrite_p0/test_iot_overwrite_and_create.groovy
b/regression-test/suites/insert_overwrite_p0/test_iot_overwrite_and_create.groovy
index 4d0b667dd44..fa13377d881 100644
---
a/regression-test/suites/insert_overwrite_p0/test_iot_overwrite_and_create.groovy
+++
b/regression-test/suites/insert_overwrite_p0/test_iot_overwrite_and_create.groovy
@@ -44,11 +44,11 @@ suite("test_iot_overwrite_and_create") {
test{
sql """insert overwrite table auto_list partition(p1, p2) values
("zzz");"""
- exception "Insert has filtered data in strict mode."
+ exception "no partition for this tuple"
}
test{
sql """insert overwrite table auto_list partition(p3) values
("zzz3");"""
- exception "Insert has filtered data in strict mode."
+ exception "no partition for this tuple"
}
sql """ insert into auto_list values
("Beijing"),("Shanghai"),("xxx"),("list"),("1234567"); """
@@ -58,14 +58,14 @@ suite("test_iot_overwrite_and_create") {
sql "set enable_auto_create_when_overwrite = false;"
test{
sql """insert overwrite table auto_list values ("zzz3");"""
- exception "Insert has filtered data in strict mode."
+ exception "no partition for this tuple"
}
test{
sql """insert overwrite table auto_list partition(p1, p2) values
("zzz");"""
- exception "Insert has filtered data in strict mode."
+ exception "no partition for this tuple"
}
test{
sql """insert overwrite table auto_list partition(*) values
("zzz3");"""
- exception "Cannot found origin partitions in auto detect overwriting"
+ exception "no partition for this tuple"
}
}
diff --git a/regression-test/suites/insert_p0/test_error_msg_truncate.groovy
b/regression-test/suites/insert_p0/test_error_msg_truncate.groovy
new file mode 100644
index 00000000000..df0e2e44497
--- /dev/null
+++ b/regression-test/suites/insert_p0/test_error_msg_truncate.groovy
@@ -0,0 +1,150 @@
+// 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.
+
+suite("test_error_msg_truncate","nonConcurrent") {
+ def tableName = "test_error_msg_truncate_table"
+ sql """ DROP TABLE IF EXISTS ${tableName} """
+ sql """
+ CREATE TABLE IF NOT EXISTS ${tableName} (
+ id INT,
+ value VARCHAR(10)
+ ) PROPERTIES (
+ "replication_num" = "1"
+ );
+ """
+
+ GetDebugPoint().clearDebugPointsForAllFEs()
+
+ try {
+ // case1: Test with debug point - error message should be truncated to
512 bytes
+ GetDebugPoint().enableDebugPointForAllFEs("TestErrorMsgTruncate")
+
+ def hasException = false
+ try {
+ sql """
+ INSERT INTO ${tableName} VALUES (1,
'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa');
+ """
+ } catch (Exception e) {
+ hasException = true
+ String errorMsg = e.getMessage()
+ logger.info("Case1 - Error message with debug point: " + errorMsg)
+ logger.info("Case1 - Error message length: " + errorMsg.length() +
" bytes")
+
+ // Check if error message contains "Test" (added by debug point)
+ assertTrue(errorMsg.contains("Test"),
+ "Error message should contain 'Test' from debug point
injection")
+
+ // Check if error message contains url
+ assertTrue(errorMsg.contains("url:"),
+ "Error message should contain 'url:'")
+
+ // Check if error message contains url
+ assertTrue(errorMsg.contains("please use `show load` for detail
msg"),
+ "Error message should contain 'please use `show load` for
detail msg'")
+
+ // Check if error message total length is within limit (512 bytes)
+ assertTrue(errorMsg.length() <= 512,
+ "Error message length should not exceed 512 bytes, actual: " +
errorMsg.length())
+
+ logger.info("Case1 - Truncation test passed")
+ }
+
+ assertTrue(hasException, "Should throw exception for inserting
oversized value")
+
+ } catch (Exception e) {
+ logger.info("Test failed with exception: " + e.getMessage())
+ throw e
+ } finally {
+ GetDebugPoint().clearDebugPointsForAllFEs()
+ }
+
+ // case2: Test without debug point - verify first_error_msg and url content
+ try {
+ sql """
+ INSERT INTO ${tableName} VALUES (2,
'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb');
+ """
+ fail("Should throw exception")
+ } catch (Exception e) {
+ String errorMsg = e.getMessage()
+ logger.info("Case2 - Normal error message: " + errorMsg)
+ logger.info("Case2 - Error message length: " + errorMsg.length() + "
bytes")
+
+ // Should NOT contain "Test" (no debug point)
+ assertFalse(errorMsg.contains("Test"),
+ "Error message should NOT contain 'Test' without debug point")
+
+ // Check if error message contains first_error_msg
+ assertTrue(errorMsg.contains("first_error_msg:"),
+ "Error message should contain 'first_error_msg:'")
+
+ // Extract and verify first_error_msg content
+ def firstErrorMsgPattern = ~/first_error_msg:\s*([^.]+(?:\.[^u])*)/
+ def matcher = errorMsg =~ firstErrorMsgPattern
+ if (matcher.find()) {
+ String firstErrorMsg = matcher.group(1).trim()
+ logger.info("Case2 - first_error_msg content: " + firstErrorMsg)
+
+ // Verify it contains expected error information
+ assertTrue(firstErrorMsg.contains("column_name[value]"),
+ "first_error_msg should mention the column name")
+ assertTrue(firstErrorMsg.contains("length"),
+ "first_error_msg should mention length issue")
+ assertTrue(firstErrorMsg.contains("schema"),
+ "first_error_msg should mention schema")
+
+ logger.info("Case2 - first_error_msg validation passed")
+ } else {
+ fail("Could not extract first_error_msg from error message")
+ }
+
+ // Check if error message contains url
+ assertTrue(errorMsg.contains("url:"),
+ "Error message should contain 'url:'")
+
+ // Extract and verify URL
+ def urlPattern = ~/url:\s*(http:\/\/[^\s]+)/
+ matcher = errorMsg =~ urlPattern
+ if (matcher.find()) {
+ String url = matcher.group(1).trim()
+ logger.info("Case2 - URL: " + url)
+
+ // Verify URL format
+ assertTrue(url.startsWith("http://"),
+ "URL should start with http://")
+ assertTrue(url.contains("_load_error_log"),
+ "URL should be a load error log URL")
+ assertTrue(url.contains("file="),
+ "URL should contain file parameter")
+
+ // Check URL contains necessary components
+ def urlComponents = url.split("\\?")
+ assertTrue(urlComponents.length == 2,
+ "URL should have query parameters")
+
+ def queryParams = urlComponents[1]
+ assertTrue(queryParams.contains("file="),
+ "URL should have file parameter")
+
+ logger.info("Case2 - URL format validation passed")
+ logger.info("Case2 - URL is complete and well-formed")
+ } else {
+ fail("Could not extract URL from error message")
+ }
+ }
+
+ sql """ DROP TABLE IF EXISTS ${tableName} """
+}
diff --git a/regression-test/suites/insert_p0/test_insert_docs_demo.groovy
b/regression-test/suites/insert_p0/test_insert_docs_demo.groovy
index 8e9ef72820b..53a900ef6bb 100644
--- a/regression-test/suites/insert_p0/test_insert_docs_demo.groovy
+++ b/regression-test/suites/insert_p0/test_insert_docs_demo.groovy
@@ -107,5 +107,5 @@ suite("test_insert_docs_demo") {
(NULL, "Alexander", 60),
(5, "Ava", 17);
"""
- }, "Insert has too many filtered data 1/5 insert_max_filter_ratio is 0.1",
"first_error_msg")
+ }, "null value for not null column", "first_error_msg")
}
diff --git
a/regression-test/suites/load_p0/tvf/test_tvf_strict_mode_and_filter_ratio.groovy
b/regression-test/suites/load_p0/tvf/test_tvf_strict_mode_and_filter_ratio.groovy
index 2506f210a81..3bb06993cb4 100644
---
a/regression-test/suites/load_p0/tvf/test_tvf_strict_mode_and_filter_ratio.groovy
+++
b/regression-test/suites/load_p0/tvf/test_tvf_strict_mode_and_filter_ratio.groovy
@@ -181,7 +181,7 @@ suite("test_tvf_strict_mode_and_filter_ratio", "p0") {
"column_separator" = "|"
);
"""
- exception """Insert has filtered data in strict mode"""
+ exception """null value for not null column"""
}
qt_sql_not_null_to_null_strict1 "select * from
test_insert_select_tvf_strict_mode_and_filter_ratio order by 1"
diff --git
a/regression-test/suites/partition_p0/auto_partition/test_auto_partition_behavior.groovy
b/regression-test/suites/partition_p0/auto_partition/test_auto_partition_behavior.groovy
index 5a862fcaa7d..9cbc291ce5f 100644
---
a/regression-test/suites/partition_p0/auto_partition/test_auto_partition_behavior.groovy
+++
b/regression-test/suites/partition_p0/auto_partition/test_auto_partition_behavior.groovy
@@ -166,7 +166,7 @@ suite("test_auto_partition_behavior") {
sql """ insert into rewrite values ("Xxx"); """
test {
sql """ insert overwrite table rewrite partition(p1) values ("") """
- exception "Insert has filtered data in strict mode"
+ exception "no partition for this tuple"
}
sql """ insert overwrite table rewrite partition(p1) values ("Xxx") """
qt_sql_overwrite """ select * from rewrite """ // Xxx
diff --git
a/regression-test/suites/partition_p0/list_partition/test_list_partition_datatype.groovy
b/regression-test/suites/partition_p0/list_partition/test_list_partition_datatype.groovy
index d678930d31a..2337004073e 100644
---
a/regression-test/suites/partition_p0/list_partition/test_list_partition_datatype.groovy
+++
b/regression-test/suites/partition_p0/list_partition/test_list_partition_datatype.groovy
@@ -352,7 +352,7 @@ suite("test_list_partition_datatype", "p0") {
PROPERTIES ("replication_allocation" = "tag.location.default: 1")
"""
sql """INSERT INTO test_list_partition_ddl_tbl_1 VALUES("0000-01-01",
"0000-01-01"), ("9999-12-31", "9999-12-31")"""
- def exception_str = isGroupCommitMode() ? "too many filtered rows" :
"Insert has filtered data in strict mode"
+ def exception_str = isGroupCommitMode() ? "too many filtered rows" : "no
partition for this tuple"
test {
sql """INSERT INTO test_list_partition_ddl_tbl_1 VALUES("2000-01-02",
"2000-01-03")"""
exception exception_str
diff --git
a/regression-test/suites/partition_p0/multi_partition/test_multi_column_partition.groovy
b/regression-test/suites/partition_p0/multi_partition/test_multi_column_partition.groovy
index 3bb3c548c98..606f31f12fb 100644
---
a/regression-test/suites/partition_p0/multi_partition/test_multi_column_partition.groovy
+++
b/regression-test/suites/partition_p0/multi_partition/test_multi_column_partition.groovy
@@ -280,7 +280,7 @@ suite("test_multi_partition_key", "p0") {
"values(0, NULL, 0, 0, 0, '2000-01-01 00:00:00', '2000-01-01',
'a', 'a', 0.001, -0.001, 0.001)"
qt_sql7 "select k1 from test_multi_col_test_partition_null_value
partition(partition_a) where k2 is null"
sql "ALTER TABLE test_multi_col_test_partition_null_value DROP PARTITION
partition_a"
- def exception_str = isGroupCommitMode() ? "too many filtered rows" :
"Insert has filtered data in strict mode"
+ def exception_str = isGroupCommitMode() ? "too many filtered rows" : "no
partition for this tuple"
test {
sql "insert into test_multi_col_test_partition_null_value " +
"values(0, NULL, 0, 0, 0, '2000-01-01 00:00:00', '2000-01-01',
'a', 'a', 0.001, -0.001, 0.001)"
diff --git
a/regression-test/suites/vault_p0/create/test_create_vault_with_kerberos.groovy
b/regression-test/suites/vault_p0/create/test_create_vault_with_kerberos.groovy
index e774f3e3f31..dd946289df1 100644
---
a/regression-test/suites/vault_p0/create/test_create_vault_with_kerberos.groovy
+++
b/regression-test/suites/vault_p0/create/test_create_vault_with_kerberos.groovy
@@ -71,7 +71,7 @@ suite("test_create_vault_with_kerberos", "nonConcurrent") {
expectExceptionLike({
sql """ insert into ${tableName} values(1, 1); """
- }, "Permission denied: user=not_exist_user")
+ }, "open file failed")
expectExceptionLike({
sql """
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]