diff --git a/integration-tests/src/main/java/org/apache/polaris/service/it/test/PolarisSparkIntegrationTest.java b/integration-tests/src/main/java/org/apache/polaris/service/it/test/PolarisSparkIntegrationTest.java index 2bdd109281..c12d59acb4 100644 --- a/integration-tests/src/main/java/org/apache/polaris/service/it/test/PolarisSparkIntegrationTest.java +++ b/integration-tests/src/main/java/org/apache/polaris/service/it/test/PolarisSparkIntegrationTest.java @@ -147,6 +147,29 @@ public void testCreateView() { assertThat(recordCount).isEqualTo(3); } + @Test + public void testSetWriteDataPathToSubdirectory() { + onSpark("CREATE NAMESPACE ns1"); + onSpark("USE ns1"); + onSpark("CREATE TABLE tb1 (col1 integer, col2 string)"); + + LoadTableResponse tableResponse = loadTable(catalogName, "ns1", "tb1"); + String tableLocation = tableResponse.tableMetadata().location(); + assertThat(tableLocation).isNotNull(); + + // Set a custom write data path to a subdirectory within the table location + String writeDataPath = tableLocation + "/alternative_data"; + onSpark("ALTER TABLE tb1 SET TBLPROPERTIES ('write.data.path' = '" + writeDataPath + "')"); + + tableResponse = loadTable(catalogName, "ns1", "tb1"); + assertThat(tableResponse.tableMetadata().properties()) + .containsEntry("write.data.path", writeDataPath); + + onSpark("INSERT INTO tb1 VALUES (1, 'a'), (2, 'b'), (3, 'c')"); + long recordCount = onSpark("SELECT * FROM tb1").count(); + assertThat(recordCount).isEqualTo(3); + } + private LoadTableResponse loadTable(String catalog, String namespace, String table) { try (Response response = catalogApi diff --git a/runtime/service/src/main/java/org/apache/polaris/service/catalog/iceberg/IcebergCatalog.java b/runtime/service/src/main/java/org/apache/polaris/service/catalog/iceberg/IcebergCatalog.java index 745a9ed4f2..bda38f0095 100644 --- a/runtime/service/src/main/java/org/apache/polaris/service/catalog/iceberg/IcebergCatalog.java +++ b/runtime/service/src/main/java/org/apache/polaris/service/catalog/iceberg/IcebergCatalog.java @@ -992,6 +992,7 @@ private void validateNoLocationOverlap( IcebergTableLikeEntity virtualEntity = IcebergTableLikeEntity.of( new PolarisEntity.Builder() + .setName(identifier.name()) .setType(PolarisEntityType.TABLE_LIKE) .setSubType(PolarisEntitySubType.ICEBERG_TABLE) .setParentId(lastNamespace.getId()) diff --git a/runtime/service/src/test/java/org/apache/polaris/service/catalog/iceberg/IcebergAllowedLocationTest.java b/runtime/service/src/test/java/org/apache/polaris/service/catalog/iceberg/IcebergAllowedLocationTest.java index 09ae01739b..8251314945 100644 --- a/runtime/service/src/test/java/org/apache/polaris/service/catalog/iceberg/IcebergAllowedLocationTest.java +++ b/runtime/service/src/test/java/org/apache/polaris/service/catalog/iceberg/IcebergAllowedLocationTest.java @@ -304,6 +304,53 @@ void testViewOutsideAllowedLocations(@TempDir Path tmpDir) { .build(); } + @Test + void testSetWriteDataPathToSubdirectoryUnderTableLocation(@TempDir Path tmpDir) { + var services = getTestServices(); + var tableName = getTableName(); + var tableId = TableIdentifier.of(namespace, tableName); + + var catalogLocation = tmpDir.resolve(catalog).toAbsolutePath().toUri().toString(); + var namespaceLocation = catalogLocation + "/" + namespace; + + createCatalog(services, Map.of(), catalogLocation, List.of(catalogLocation)); + createNamespace(services, namespaceLocation); + + var createTableRequest = + CreateTableRequest.builder().withName(tableName).withSchema(SCHEMA).build(); + + var createResponse = + services + .restApi() + .createTable( + catalog, + namespace, + createTableRequest, + null, + services.realmContext(), + services.securityContext()); + assertThat(createResponse.getStatus()).isEqualTo(Response.Status.OK.getStatusCode()); + var tableLocation = namespaceLocation + "/" + tableName; + + // Update the table to set write.data.path to a subdirectory under the table's location + String writeDataPath = tableLocation + "/alternative_data"; + Map updatedProperties = new HashMap<>(); + updatedProperties.put("write.data.path", writeDataPath); + + var updateRequest = + UpdateTableRequest.create( + tableId, List.of(), List.of(new MetadataUpdate.SetProperties(updatedProperties))); + + var updateResponse = + services + .catalogAdapter() + .newHandlerWrapper(services.securityContext(), catalog) + .updateTable(tableId, updateRequest); + + assertThat(updateResponse.tableMetadata().properties()) + .containsEntry("write.data.path", writeDataPath); + } + private void createCatalog( TestServices services, Map catalogConfig,