This is an automated email from the ASF dual-hosted git repository. yashmayya 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 6950c850cd GET /logicalTables API to return database specific tables. (#15944) 6950c850cd is described below commit 6950c850cd7ce14af7cf667da8f09c4824bb96fd Author: Abhishek Bafna <aba...@startree.ai> AuthorDate: Fri May 30 20:07:39 2025 +0530 GET /logicalTables API to return database specific tables. (#15944) --- .../api/resources/PinotLogicalTableResource.java | 3 +- .../helix/core/PinotHelixResourceManager.java | 15 +++++ .../resources/PinotLogicalTableResourceTest.java | 66 +++++++++++++++++++--- 3 files changed, 76 insertions(+), 8 deletions(-) diff --git a/pinot-controller/src/main/java/org/apache/pinot/controller/api/resources/PinotLogicalTableResource.java b/pinot-controller/src/main/java/org/apache/pinot/controller/api/resources/PinotLogicalTableResource.java index 73265bd832..56a9f02edd 100644 --- a/pinot-controller/src/main/java/org/apache/pinot/controller/api/resources/PinotLogicalTableResource.java +++ b/pinot-controller/src/main/java/org/apache/pinot/controller/api/resources/PinotLogicalTableResource.java @@ -93,7 +93,8 @@ public class PinotLogicalTableResource { @Authorize(targetType = TargetType.CLUSTER, paramName = "tableName", action = Actions.Cluster.GET_TABLE) @ApiOperation(value = "List all logical table names", notes = "Lists all logical table names") public List<String> listLogicalTableNames(@Context HttpHeaders headers) { - return _pinotHelixResourceManager.getAllLogicalTableNames(); + String databaseName = DatabaseUtils.extractDatabaseFromHttpHeaders(headers); + return _pinotHelixResourceManager.getAllLogicalTableNames(databaseName); } @GET diff --git a/pinot-controller/src/main/java/org/apache/pinot/controller/helix/core/PinotHelixResourceManager.java b/pinot-controller/src/main/java/org/apache/pinot/controller/helix/core/PinotHelixResourceManager.java index ef823d55bf..93ece7c42e 100644 --- a/pinot-controller/src/main/java/org/apache/pinot/controller/helix/core/PinotHelixResourceManager.java +++ b/pinot-controller/src/main/java/org/apache/pinot/controller/helix/core/PinotHelixResourceManager.java @@ -2378,6 +2378,10 @@ public class PinotHelixResourceManager { return ZKMetadataProvider.getLogicalTableConfig(_propertyStore, tableName); } + /** + * Returns all logical table names in the cluster regardless of their database name. + * @return List of logical table names + */ public List<String> getAllLogicalTableNames() { List<String> logicalTableNames = _propertyStore.getChildNames( PinotHelixPropertyStoreZnRecordProvider.forLogicalTable(_propertyStore).getRelativePath(), @@ -2385,6 +2389,17 @@ public class PinotHelixResourceManager { return logicalTableNames != null ? logicalTableNames : Collections.emptyList(); } + /** + * Returns all logical table names in the cluster that belong to the given database. + * @param databaseName The name of the database + * @return List of logical table names that belong to the given database + */ + public List<String> getAllLogicalTableNames(String databaseName) { + return getAllLogicalTableNames().stream() + .filter(tableName -> DatabaseUtils.isPartOfDatabase(tableName, databaseName)) + .collect(Collectors.toList()); + } + /** * Returns the ZK metdata for the given jobId and jobType * @param jobId the id of the job diff --git a/pinot-controller/src/test/java/org/apache/pinot/controller/api/resources/PinotLogicalTableResourceTest.java b/pinot-controller/src/test/java/org/apache/pinot/controller/api/resources/PinotLogicalTableResourceTest.java index 3458777e8d..50a29e2e55 100644 --- a/pinot-controller/src/test/java/org/apache/pinot/controller/api/resources/PinotLogicalTableResourceTest.java +++ b/pinot-controller/src/test/java/org/apache/pinot/controller/api/resources/PinotLogicalTableResourceTest.java @@ -20,6 +20,7 @@ package org.apache.pinot.controller.api.resources; import com.fasterxml.jackson.databind.ObjectMapper; import java.io.IOException; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; @@ -32,6 +33,7 @@ import org.apache.pinot.spi.config.table.TableConfig; import org.apache.pinot.spi.config.table.TableType; import org.apache.pinot.spi.data.LogicalTableConfig; import org.apache.pinot.spi.data.TimeBoundaryConfig; +import org.apache.pinot.spi.utils.CommonConstants; import org.apache.pinot.spi.utils.builder.ControllerRequestURLBuilder; import org.testng.annotations.AfterClass; import org.testng.annotations.AfterMethod; @@ -334,9 +336,11 @@ public class PinotLogicalTableResourceTest extends ControllerTest { throwable.getMessage()); } + @Test public void testLogicalTableTimeBoundaryConfigValidation() throws IOException { // Test logical table time boundary strategy validation + addDummySchema(LOGICAL_TABLE_NAME); List<String> physicalTableNamesWithType = createHybridTables(List.of("test_table_8")); LogicalTableConfig logicalTableConfig = getDummyLogicalTableConfig(LOGICAL_TABLE_NAME, physicalTableNamesWithType, BROKER_TENANT); @@ -356,7 +360,7 @@ public class PinotLogicalTableResourceTest extends ControllerTest { ControllerTest.sendPostRequest(_addLogicalTableUrl, logicalTableConfig.toSingleLineJsonString(), getHeaders()); }); assertTrue(throwable.getMessage() - .contains("Reason: 'timeBoundaryConfig.strategy' should not be null or empty for hybrid logical tables"), + .contains("Reason: 'timeBoundaryConfig.boundaryStrategy' should not be null or empty"), throwable.getMessage()); // Test logical table with time boundary config but empty strategy @@ -365,7 +369,7 @@ public class PinotLogicalTableResourceTest extends ControllerTest { ControllerTest.sendPostRequest(_addLogicalTableUrl, logicalTableConfig.toSingleLineJsonString(), getHeaders()); }); assertTrue(throwable.getMessage() - .contains("Reason: 'timeBoundaryConfig.strategy' should not be null or empty for hybrid logical tables"), + .contains("Reason: 'timeBoundaryConfig.boundaryStrategy' should not be null or empty"), throwable.getMessage()); // Test logical table with time boundary config but null parameters @@ -374,7 +378,7 @@ public class PinotLogicalTableResourceTest extends ControllerTest { ControllerTest.sendPostRequest(_addLogicalTableUrl, logicalTableConfig.toSingleLineJsonString(), getHeaders()); }); assertTrue(throwable.getMessage() - .contains("Reason: 'timeBoundaryConfig.parameters' should not be null or empty for hybrid logical tables"), + .contains("Reason: 'timeBoundaryConfig.parameters' should not be null or empty"), throwable.getMessage()); // Test logical table with time boundary config but empty parameters @@ -383,7 +387,7 @@ public class PinotLogicalTableResourceTest extends ControllerTest { ControllerTest.sendPostRequest(_addLogicalTableUrl, logicalTableConfig.toSingleLineJsonString(), getHeaders()); }); assertTrue(throwable.getMessage() - .contains("Reason: 'timeBoundaryConfig.parameters' should not be null or empty for hybrid logical tables"), + .contains("Reason: 'timeBoundaryConfig.parameters' should not be null or empty"), throwable.getMessage()); } @@ -447,7 +451,8 @@ public class PinotLogicalTableResourceTest extends ControllerTest { assertEquals(response, objectMapper.writeValueAsString(List.of())); // setup physical tables and logical tables - List<String> logicalTableNames = List.of("db.test_logical_table_1", "test_logical_table_2", "test_logical_table_3"); + List<String> logicalTableNames = + List.of("db.test_logical_table_1", "default.test_logical_table_2", "test_logical_table_3"); List<String> physicalTableNames = List.of("test_table_1", "test_table_2", "db.test_table_3"); List<String> physicalTableNamesWithType = createHybridTables(physicalTableNames); @@ -459,9 +464,56 @@ public class PinotLogicalTableResourceTest extends ControllerTest { ControllerTest.sendPostRequest(_addLogicalTableUrl, logicalTableConfig.toSingleLineJsonString(), getHeaders()); } - // verify logical table names + // verify logical table names without headers, should return tables without database prefix (or default database) String getLogicalTableNamesResponse = ControllerTest.sendGetRequest(getLogicalTableNamesUrl, getHeaders()); - assertEquals(getLogicalTableNamesResponse, objectMapper.writeValueAsString(logicalTableNames)); + assertEquals(getLogicalTableNamesResponse, + objectMapper.writeValueAsString(List.of("test_logical_table_2", "test_logical_table_3"))); + + // verify logical table names with headers, should return tables with database prefix + Map<String, String> headers = new HashMap<>(getHeaders()); + headers.put(CommonConstants.DATABASE, "db"); + getLogicalTableNamesResponse = ControllerTest.sendGetRequest(getLogicalTableNamesUrl, headers); + assertEquals(getLogicalTableNamesResponse, + objectMapper.writeValueAsString(List.of("db.test_logical_table_1"))); + } + + @Test + public void testLogicalTableDatabaseHeaderMismatchValidation() + throws IOException { + Map<String, String> headers = new HashMap<>(getHeaders()); + headers.put(CommonConstants.DATABASE, "db1"); + String logicalTableName = "db2.test_logical_table"; + LogicalTableConfig logicalTableConfig = getDummyLogicalTableConfig(logicalTableName, + List.of("test_table_1_OFFLINE", "test_table_2_REALTIME"), BROKER_TENANT); + + // Test add logical table with database header mismatch + String msg = expectThrows(IOException.class, + () -> ControllerTest.sendPostRequest(_addLogicalTableUrl, logicalTableConfig.toSingleLineJsonString(), + headers)).getMessage(); + assertTrue(msg.contains("Database name 'db2' from table prefix does not match database name 'db1' from header"), + msg); + + // Test get logical table with database header mismatch + String getLogicalTableUrl = _controllerRequestURLBuilder.forLogicalTableGet(logicalTableName); + msg = expectThrows(IOException.class, + () -> ControllerTest.sendGetRequest(getLogicalTableUrl, headers)).getMessage(); + assertTrue(msg.contains("Database name 'db2' from table prefix does not match database name 'db1' from header"), + msg); + + // Test update logical table with database header mismatch + String updateLogicalTableUrl = _controllerRequestURLBuilder.forLogicalTableUpdate(logicalTableName); + msg = expectThrows(IOException.class, + () -> ControllerTest.sendPutRequest(updateLogicalTableUrl, logicalTableConfig.toSingleLineJsonString(), + headers)).getMessage(); + assertTrue(msg.contains("Database name 'db2' from table prefix does not match database name 'db1' from header"), + msg); + + // Test delete logical table with database header mismatch + String deleteLogicalTableUrl = _controllerRequestURLBuilder.forLogicalTableDelete(logicalTableName); + msg = expectThrows(IOException.class, + () -> ControllerTest.sendDeleteRequest(deleteLogicalTableUrl, headers)).getMessage(); + assertTrue(msg.contains("Database name 'db2' from table prefix does not match database name 'db1' from header"), + msg); } @Test --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscr...@pinot.apache.org For additional commands, e-mail: commits-h...@pinot.apache.org