This is an automated email from the ASF dual-hosted git repository.
jerryshao pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/gravitino.git
The following commit(s) were added to refs/heads/main by this push:
new f02f537c7a [#13343] fix(doris): handle missing-table errors from DROP
TABLE (#13344)
f02f537c7a is described below
commit f02f537c7a252a8a94f37468a6af3a53a6eb82e5
Author: Qi Yu <[email protected]>
AuthorDate: Sun Sep 20 14:30:36 2026 +0800
[#13343] fix(doris): handle missing-table errors from DROP TABLE (#13344)
### What changes were proposed in this pull request?
Recognize Doris `Unknown table` errors with quoted or unquoted names and
with or without a cluster-qualified schema. Remove repeated
`errCode/detailMessage` prefixes before converting the exception. Add
unit and integration regression tests.
### Why are the changes needed?
Doris currently treats a missing table on DROP as a generic runtime
error, so the API returns HTTP 500 instead of `dropped:false`.
Fix: #13343
### Does this PR introduce _any_ user-facing change?
Yes. Dropping a missing Doris table returns HTTP 200 with
`dropped:false`. Doris error messages no longer repeat the same backend
prefix. No API or property keys change.
### How was this patch tested?
- `./gradlew spotlessApply`
- `./gradlew :catalogs:catalog-jdbc-doris:test -PskipITs
-PskipDockerTests=true -PskipWeb=true` (61 tests passed)
- `./gradlew :catalogs:catalog-jdbc-doris:test --tests
org.apache.gravitino.catalog.doris.integration.test.CatalogDorisIT.testDropMissingTableReturnsFalse
-PskipDockerTests=false -PdorisMultiVersionTest -PskipWeb=true` (passed
with Doris container)
---
.../doris/converter/DorisExceptionConverter.java | 47 +++++++++++++++-------
.../converter/TestDorisExceptionConverter.java | 32 +++++++++++++++
.../doris/integration/test/CatalogDorisIT.java | 6 +++
3 files changed, 70 insertions(+), 15 deletions(-)
diff --git
a/catalogs/catalog-jdbc-doris/src/main/java/org/apache/gravitino/catalog/doris/converter/DorisExceptionConverter.java
b/catalogs/catalog-jdbc-doris/src/main/java/org/apache/gravitino/catalog/doris/converter/DorisExceptionConverter.java
index bbd4e5099e..0c19f360aa 100644
---
a/catalogs/catalog-jdbc-doris/src/main/java/org/apache/gravitino/catalog/doris/converter/DorisExceptionConverter.java
+++
b/catalogs/catalog-jdbc-doris/src/main/java/org/apache/gravitino/catalog/doris/converter/DorisExceptionConverter.java
@@ -20,6 +20,7 @@ package org.apache.gravitino.catalog.doris.converter;
import com.google.common.annotations.VisibleForTesting;
import java.sql.SQLException;
+import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.gravitino.catalog.jdbc.converter.JdbcExceptionConverter;
import org.apache.gravitino.exceptions.ConnectionFailedException;
@@ -67,11 +68,14 @@ public class DorisExceptionConverter extends
JdbcExceptionConverter {
Pattern.compile(UNKNOWN_DATABASE_PATTERN_STRING);
private static final String TABLE_NOT_EXIST_PATTERN_STRING =
- ".*detailMessage = Unknown table '.*' in .*:.*";
+ ".*detailMessage = Unknown table (?:'[^']+'|\\S+) in \\S+.*";
private static final Pattern TABLE_NOT_EXIST_PATTERN =
Pattern.compile(TABLE_NOT_EXIST_PATTERN_STRING);
+ private static final Pattern DUPLICATED_ERROR_PREFIX_PATTERN =
+ Pattern.compile("^(errCode = \\d+, detailMessage = )\\1");
+
private static final String DELETE_NON_EXISTING_PARTITION_STRING =
".*?detailMessage = Error in list of partitions to .*?";
@@ -93,29 +97,30 @@ public class DorisExceptionConverter extends
JdbcExceptionConverter {
@SuppressWarnings("FormatStringAnnotation")
@Override
public GravitinoRuntimeException toGravitinoException(SQLException se) {
+ String message = removeDuplicatedErrorPrefix(se.getMessage());
int errorCode = se.getErrorCode();
if (errorCode == CODE_OTHER) {
- errorCode = getErrorCodeFromMessage(se.getMessage());
+ errorCode = getErrorCodeFromMessage(message);
}
switch (errorCode) {
case CODE_DATABASE_EXISTS:
- return new SchemaAlreadyExistsException(se, se.getMessage());
+ return new SchemaAlreadyExistsException(se, message);
case CODE_TABLE_EXISTS:
- return new TableAlreadyExistsException(se, se.getMessage());
+ return new TableAlreadyExistsException(se, message);
case CODE_DATABASE_NOT_EXISTS:
case CODE_UNKNOWN_DATABASE:
- return new NoSuchSchemaException(se, se.getMessage());
+ return new NoSuchSchemaException(se, message);
case CODE_NO_SUCH_TABLE:
- return new NoSuchTableException(se, se.getMessage());
+ return new NoSuchTableException(se, message);
case CODE_UNAUTHORIZED:
- return new UnauthorizedException(se, se.getMessage());
+ return new UnauthorizedException(se, message);
case CODE_NO_SUCH_COLUMN:
- return new NoSuchColumnException(se, se.getMessage());
+ return new NoSuchColumnException(se, message);
case CODE_DELETE_NON_EXISTING_PARTITION:
- return new NoSuchPartitionException(se, se.getMessage());
+ return new NoSuchPartitionException(se, message);
case CODE_PARTITION_ALREADY_EXISTS:
- return new PartitionAlreadyExistsException(se, se.getMessage());
+ return new PartitionAlreadyExistsException(se, message);
case CODE_BUCKETS_AUTO_NOT_SUPPORTED:
String bucketsAutoMessage =
String.format(
@@ -123,19 +128,31 @@ public class DorisExceptionConverter extends
JdbcExceptionConverter {
+ "BUCKETS AUTO was introduced in Doris 1.2.2. "
+ "Please either upgrade to Doris 1.2.2+ or specify a
specific bucket number instead of AUTO. "
+ "Original error: %s",
- se.getMessage());
+ message);
return new GravitinoRuntimeException(se, bucketsAutoMessage);
default:
- if (se.getMessage() != null && se.getMessage().contains("Access
denied")) {
- return new ConnectionFailedException(se, se.getMessage());
+ if (message != null && message.contains("Access denied")) {
+ return new ConnectionFailedException(se, message);
}
- return new GravitinoRuntimeException(se, se.getMessage());
+ return new GravitinoRuntimeException(se, message);
+ }
+ }
+
+ private static String removeDuplicatedErrorPrefix(String message) {
+ if (message == null) {
+ return null;
+ }
+ Matcher matcher = DUPLICATED_ERROR_PREFIX_PATTERN.matcher(message);
+ while (matcher.find()) {
+ message = matcher.replaceFirst("$1");
+ matcher = DUPLICATED_ERROR_PREFIX_PATTERN.matcher(message);
}
+ return message;
}
@VisibleForTesting
static int getErrorCodeFromMessage(String message) {
- if (message.isEmpty()) {
+ if (message == null || message.isEmpty()) {
return CODE_OTHER;
}
if (DATABASE_ALREADY_EXISTS_PATTERN.matcher(message).matches()) {
diff --git
a/catalogs/catalog-jdbc-doris/src/test/java/org/apache/gravitino/catalog/doris/converter/TestDorisExceptionConverter.java
b/catalogs/catalog-jdbc-doris/src/test/java/org/apache/gravitino/catalog/doris/converter/TestDorisExceptionConverter.java
index b365eb2d73..0133a99f73 100644
---
a/catalogs/catalog-jdbc-doris/src/test/java/org/apache/gravitino/catalog/doris/converter/TestDorisExceptionConverter.java
+++
b/catalogs/catalog-jdbc-doris/src/test/java/org/apache/gravitino/catalog/doris/converter/TestDorisExceptionConverter.java
@@ -18,10 +18,42 @@
*/
package org.apache.gravitino.catalog.doris.converter;
+import java.sql.SQLException;
+import org.apache.gravitino.exceptions.GravitinoRuntimeException;
+import org.apache.gravitino.exceptions.NoSuchTableException;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
public class TestDorisExceptionConverter {
+ @Test
+ public void testUnknownTableWithoutQuotes() {
+ String message = "errCode = 2, detailMessage = Unknown table
no_such_table_xyz in s_12412";
+ SQLException sqlException =
+ new SQLException(message, "HY000", DorisExceptionConverter.CODE_OTHER);
+
+ GravitinoRuntimeException converted =
+ new DorisExceptionConverter().toGravitinoException(sqlException);
+
+ Assertions.assertTrue(converted instanceof NoSuchTableException);
+ Assertions.assertEquals(message, converted.getMessage());
+ Assertions.assertSame(sqlException, converted.getCause());
+ }
+
+ @Test
+ public void testRepeatedErrorPrefixIsRemoved() {
+ String prefix = "errCode = 2, detailMessage = ";
+ String detail = "Unknown table no_such_table_xyz in s_12412";
+ SQLException sqlException =
+ new SQLException(prefix + prefix + detail, "HY000",
DorisExceptionConverter.CODE_OTHER);
+
+ GravitinoRuntimeException converted =
+ new DorisExceptionConverter().toGravitinoException(sqlException);
+
+ Assertions.assertTrue(converted instanceof NoSuchTableException);
+ Assertions.assertEquals(prefix + detail, converted.getMessage());
+ Assertions.assertSame(sqlException, converted.getCause());
+ }
+
@Test
public void testGetErrorCodeFromMessage() {
String msg =
diff --git
a/catalogs/catalog-jdbc-doris/src/test/java/org/apache/gravitino/catalog/doris/integration/test/CatalogDorisIT.java
b/catalogs/catalog-jdbc-doris/src/test/java/org/apache/gravitino/catalog/doris/integration/test/CatalogDorisIT.java
index 93ba4b09e7..bdfc8ce065 100644
---
a/catalogs/catalog-jdbc-doris/src/test/java/org/apache/gravitino/catalog/doris/integration/test/CatalogDorisIT.java
+++
b/catalogs/catalog-jdbc-doris/src/test/java/org/apache/gravitino/catalog/doris/integration/test/CatalogDorisIT.java
@@ -197,6 +197,12 @@ public class CatalogDorisIT extends BaseIT {
Assertions.assertDoesNotThrow(() -> metalake.testConnection(catalogName));
}
+ @Test
+ void testDropMissingTableReturnsFalse() {
+ String missingTable = GravitinoITUtils.genRandomName("missing_table");
+
assertFalse(catalog.asTableCatalog().dropTable(NameIdentifier.of(schemaName,
missingTable)));
+ }
+
private void createSchema() {
NameIdentifier ident = NameIdentifier.of(metalakeName, catalogName,
schemaName);
String propKey = "key";