This is an automated email from the ASF dual-hosted git repository.

Jackie-Jiang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/pinot.git


The following commit(s) were added to refs/heads/master by this push:
     new 47a5f8671f6 Return HTTP 400 for DDL table validator rejections (#19544)
47a5f8671f6 is described below

commit 47a5f8671f6626f95ddb329c0bb9b2eec27dce43
Author: Xiang Fu <[email protected]>
AuthorDate: Mon Sep 14 13:28:05 2026 -0700

    Return HTTP 400 for DDL table validator rejections (#19544)
---
 .../api/resources/PinotDdlRestletResource.java     |  3 +-
 .../api/PinotDdlRestletResourceTest.java           | 39 ++++++++++++++++++++++
 2 files changed, 41 insertions(+), 1 deletion(-)

diff --git 
a/pinot-controller/src/main/java/org/apache/pinot/controller/api/resources/PinotDdlRestletResource.java
 
b/pinot-controller/src/main/java/org/apache/pinot/controller/api/resources/PinotDdlRestletResource.java
index 9a7c72caadd..d640f88687c 100644
--- 
a/pinot-controller/src/main/java/org/apache/pinot/controller/api/resources/PinotDdlRestletResource.java
+++ 
b/pinot-controller/src/main/java/org/apache/pinot/controller/api/resources/PinotDdlRestletResource.java
@@ -76,6 +76,7 @@ import org.apache.pinot.spi.data.DateTimeFieldSpec;
 import org.apache.pinot.spi.data.FieldSpec;
 import org.apache.pinot.spi.data.LogicalTableConfig;
 import org.apache.pinot.spi.data.Schema;
+import org.apache.pinot.spi.exception.ConfigValidationException;
 import org.apache.pinot.spi.exception.DatabaseConflictException;
 import org.apache.pinot.spi.utils.CommonConstants;
 import org.apache.pinot.spi.utils.JsonUtils;
@@ -603,7 +604,7 @@ public class PinotDdlRestletResource {
           _pinotHelixResourceManager, _controllerConf, _pinotTaskManager);
     } catch (ControllerApplicationException e) {
       throw e;
-    } catch (IllegalArgumentException | IllegalStateException e) {
+    } catch (IllegalArgumentException | IllegalStateException | 
ConfigValidationException e) {
       // The Pinot validators consistently raise these for user-facing config 
errors
       // (upsert without primary keys, field configs referencing non-existent 
columns,
       // bad task configs, etc.). Surface as 400 — the caller can fix their 
DDL.
diff --git 
a/pinot-controller/src/test/java/org/apache/pinot/controller/api/PinotDdlRestletResourceTest.java
 
b/pinot-controller/src/test/java/org/apache/pinot/controller/api/PinotDdlRestletResourceTest.java
index 8d1b9d6a857..9203bf91d9c 100644
--- 
a/pinot-controller/src/test/java/org/apache/pinot/controller/api/PinotDdlRestletResourceTest.java
+++ 
b/pinot-controller/src/test/java/org/apache/pinot/controller/api/PinotDdlRestletResourceTest.java
@@ -25,14 +25,19 @@ import java.util.Map;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.commons.lang3.tuple.Pair;
 import org.apache.pinot.controller.helix.ControllerTest;
+import org.apache.pinot.spi.config.table.TableConfigValidator;
+import org.apache.pinot.spi.config.table.TableConfigValidatorRegistry;
+import org.apache.pinot.spi.exception.ConfigValidationException;
 import org.apache.pinot.spi.utils.JsonUtils;
 import org.testng.annotations.AfterClass;
 import org.testng.annotations.BeforeClass;
+import org.testng.annotations.DataProvider;
 import org.testng.annotations.Test;
 
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertFalse;
 import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertNull;
 import static org.testng.Assert.assertTrue;
 import static org.testng.Assert.fail;
 
@@ -103,6 +108,40 @@ public class PinotDdlRestletResourceTest extends 
ControllerTest {
         "Dry-run table must not be persisted; got " + listResponse);
   }
 
+  @DataProvider(name = "tableValidatorFailures")
+  public Object[][] tableValidatorFailures() {
+    return new Object[][]{{false, true}, {true, true}, {false, false}, {true, 
false}};
+  }
+
+  @Test(dataProvider = "tableValidatorFailures")
+  public void tableValidatorFailureDoesNotPersist(boolean dryRun, boolean 
configValidationFailure)
+      throws IOException {
+    String tableName = "ddlValidatorFailure" + dryRun + 
configValidationFailure;
+    String tableNameWithType = tableName + "_OFFLINE";
+    String message = "Validator rejected column id for " + tableName;
+    TableConfigValidator validator = (tableConfig, schema) -> {
+      if (tableNameWithType.equals(tableConfig.getTableName())) {
+        if (configValidationFailure) {
+          throw new ConfigValidationException(message);
+        }
+        throw new RuntimeException(message);
+      }
+    };
+    TableConfigValidatorRegistry.register(validator);
+    try {
+      String url = DEFAULT_INSTANCE.getControllerBaseApiUrl() + 
"/sql/ddl?dryRun=" + dryRun;
+      String sql = "CREATE TABLE " + tableName + " (id INT) TABLE_TYPE = 
OFFLINE";
+      Pair<Integer, String> response = postRequestWithStatusCode(url,
+          JsonUtils.objectToString(Map.of("sql", sql)));
+      assertEquals(response.getLeft().intValue(), configValidationFailure ? 
400 : 500, response.getRight());
+      assertTrue(response.getRight().contains(message), response.getRight());
+      
assertNull(DEFAULT_INSTANCE.getHelixResourceManager().getTableConfig(tableNameWithType));
+      
assertNull(DEFAULT_INSTANCE.getHelixResourceManager().getSchema(tableName));
+    } finally {
+      TableConfigValidatorRegistry.unregister(validator);
+    }
+  }
+
   @Test
   public void createIfNotExistsIsIdempotent()
       throws IOException {


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to