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";

Reply via email to