This is an automated email from the ASF dual-hosted git repository. yzou pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/polaris.git
The following commit(s) were added to refs/heads/main by this push: new b48cfb654 Add server and client support for the new generic table `baseLocation` field (#2122) b48cfb654 is described below commit b48cfb654cdd867830ca6105f790104503bfb1b1 Author: Yun Zou <yunzou.colost...@gmail.com> AuthorDate: Fri Jul 18 18:22:03 2025 -0700 Add server and client support for the new generic table `baseLocation` field (#2122) --- .../org/apache/polaris/spark/PolarisCatalog.java | 6 +++- .../apache/polaris/spark/PolarisRESTCatalog.java | 7 ++++- .../apache/polaris/spark/PolarisSparkCatalog.java | 26 +++++++++++++++++- .../polaris/spark/utils/PolarisCatalogUtils.java | 8 ++++-- .../polaris/spark/PolarisInMemoryCatalog.java | 7 ++++- .../polaris/spark/rest/DeserializationTest.java | 28 +++++++++++++------ .../core/entity/table/GenericTableEntity.java | 12 ++++++++ .../core/entity/table/IcebergTableLikeEntity.java | 3 +- .../polaris/core/entity/table/TableLikeEntity.java | 4 +-- .../quarkus/admin/PolarisAuthzTestBase.java | 3 +- .../AbstractPolarisGenericTableCatalogTest.java | 32 +++++++++++++--------- ...PolarisGenericTableCatalogHandlerAuthzTest.java | 7 +++-- .../catalog/generic/GenericTableCatalog.java | 6 +++- .../generic/GenericTableCatalogAdapter.java | 1 + .../generic/GenericTableCatalogHandler.java | 11 ++++++-- .../generic/PolarisGenericTableCatalog.java | 7 ++++- 16 files changed, 128 insertions(+), 40 deletions(-) diff --git a/plugins/spark/v3.5/spark/src/main/java/org/apache/polaris/spark/PolarisCatalog.java b/plugins/spark/v3.5/spark/src/main/java/org/apache/polaris/spark/PolarisCatalog.java index 99802d3cb..08116c9e6 100644 --- a/plugins/spark/v3.5/spark/src/main/java/org/apache/polaris/spark/PolarisCatalog.java +++ b/plugins/spark/v3.5/spark/src/main/java/org/apache/polaris/spark/PolarisCatalog.java @@ -32,5 +32,9 @@ public interface PolarisCatalog { boolean dropGenericTable(TableIdentifier identifier); GenericTable createGenericTable( - TableIdentifier identifier, String format, String doc, Map<String, String> props); + TableIdentifier identifier, + String format, + String baseLocation, + String doc, + Map<String, String> props); } diff --git a/plugins/spark/v3.5/spark/src/main/java/org/apache/polaris/spark/PolarisRESTCatalog.java b/plugins/spark/v3.5/spark/src/main/java/org/apache/polaris/spark/PolarisRESTCatalog.java index 5dfd1f898..5be0f6952 100644 --- a/plugins/spark/v3.5/spark/src/main/java/org/apache/polaris/spark/PolarisRESTCatalog.java +++ b/plugins/spark/v3.5/spark/src/main/java/org/apache/polaris/spark/PolarisRESTCatalog.java @@ -200,13 +200,18 @@ public class PolarisRESTCatalog implements PolarisCatalog, Closeable { @Override public GenericTable createGenericTable( - TableIdentifier identifier, String format, String doc, Map<String, String> props) { + TableIdentifier identifier, + String format, + String baseLocation, + String doc, + Map<String, String> props) { Endpoint.check(endpoints, PolarisEndpoints.V1_CREATE_GENERIC_TABLE); CreateGenericTableRESTRequest request = new CreateGenericTableRESTRequest( CreateGenericTableRequest.builder() .setName(identifier.name()) .setFormat(format) + .setBaseLocation(baseLocation) .setDoc(doc) .setProperties(props) .build()); diff --git a/plugins/spark/v3.5/spark/src/main/java/org/apache/polaris/spark/PolarisSparkCatalog.java b/plugins/spark/v3.5/spark/src/main/java/org/apache/polaris/spark/PolarisSparkCatalog.java index fe0c6e180..964674e54 100644 --- a/plugins/spark/v3.5/spark/src/main/java/org/apache/polaris/spark/PolarisSparkCatalog.java +++ b/plugins/spark/v3.5/spark/src/main/java/org/apache/polaris/spark/PolarisSparkCatalog.java @@ -38,6 +38,8 @@ import org.apache.spark.sql.connector.catalog.TableChange; import org.apache.spark.sql.connector.expressions.Transform; import org.apache.spark.sql.types.StructType; import org.apache.spark.sql.util.CaseInsensitiveStringMap; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * A spark TableCatalog Implementation interacts with Polaris specific APIs only. The APIs it @@ -45,6 +47,7 @@ import org.apache.spark.sql.util.CaseInsensitiveStringMap; * expected to be for non-iceberg tables. */ public class PolarisSparkCatalog implements TableCatalog { + private static final Logger LOGGER = LoggerFactory.getLogger(PolarisSparkCatalog.class); private PolarisCatalog polarisCatalog = null; private String catalogName = null; @@ -83,9 +86,30 @@ public class PolarisSparkCatalog implements TableCatalog { throws TableAlreadyExistsException, NoSuchNamespaceException { try { String format = properties.get(PolarisCatalogUtils.TABLE_PROVIDER_KEY); + + String baseLocation; + // Extract the base table location from the spark properties. + // Spark pass the table base location either with the + // TableCatalog.PROP_LOCATION key, or with "path" key if created + // with the path option. + if (properties.get(TableCatalog.PROP_LOCATION) != null) { + baseLocation = properties.get(TableCatalog.PROP_LOCATION); + if (properties.get(PolarisCatalogUtils.TABLE_PATH_KEY) != null) { + LOGGER.debug( + "Both location and path are propagated in the table properties, location {}, path {}", + baseLocation, + properties.get(PolarisCatalogUtils.TABLE_PATH_KEY)); + } + } else { + baseLocation = properties.get(PolarisCatalogUtils.TABLE_PATH_KEY); + } GenericTable genericTable = this.polarisCatalog.createGenericTable( - Spark3Util.identifierToTableIdentifier(identifier), format, null, properties); + Spark3Util.identifierToTableIdentifier(identifier), + format, + baseLocation, + null, + properties); return PolarisCatalogUtils.loadSparkTable(genericTable); } catch (AlreadyExistsException e) { throw new TableAlreadyExistsException(identifier); diff --git a/plugins/spark/v3.5/spark/src/main/java/org/apache/polaris/spark/utils/PolarisCatalogUtils.java b/plugins/spark/v3.5/spark/src/main/java/org/apache/polaris/spark/utils/PolarisCatalogUtils.java index e7cd76bca..df433ec9f 100644 --- a/plugins/spark/v3.5/spark/src/main/java/org/apache/polaris/spark/utils/PolarisCatalogUtils.java +++ b/plugins/spark/v3.5/spark/src/main/java/org/apache/polaris/spark/utils/PolarisCatalogUtils.java @@ -76,12 +76,16 @@ public class PolarisCatalogUtils { boolean hasPathClause = properties.get(TABLE_PATH_KEY) != null; Map<String, String> tableProperties = Maps.newHashMap(); tableProperties.putAll(properties); - if (!hasPathClause && hasLocationClause) { + if (!hasPathClause) { // DataSourceV2 requires the path property on table loading. However, spark today // doesn't create the corresponding path property if the path keyword is not // provided by user when location is provided. Here, we duplicate the location // property as path to make sure the table can be loaded. - tableProperties.put(TABLE_PATH_KEY, properties.get(TableCatalog.PROP_LOCATION)); + if (genericTable.getBaseLocation() != null && !genericTable.getBaseLocation().isEmpty()) { + tableProperties.put(TABLE_PATH_KEY, genericTable.getBaseLocation()); + } else if (hasLocationClause) { + tableProperties.put(TABLE_PATH_KEY, properties.get(TableCatalog.PROP_LOCATION)); + } } return DataSourceV2Utils.getTableFromProvider( provider, new CaseInsensitiveStringMap(tableProperties), scala.Option.empty()); diff --git a/plugins/spark/v3.5/spark/src/test/java/org/apache/polaris/spark/PolarisInMemoryCatalog.java b/plugins/spark/v3.5/spark/src/test/java/org/apache/polaris/spark/PolarisInMemoryCatalog.java index c846659df..2d71d9cb6 100644 --- a/plugins/spark/v3.5/spark/src/test/java/org/apache/polaris/spark/PolarisInMemoryCatalog.java +++ b/plugins/spark/v3.5/spark/src/test/java/org/apache/polaris/spark/PolarisInMemoryCatalog.java @@ -65,7 +65,11 @@ public class PolarisInMemoryCatalog extends InMemoryCatalog implements PolarisCa @Override public GenericTable createGenericTable( - TableIdentifier identifier, String format, String doc, Map<String, String> props) { + TableIdentifier identifier, + String format, + String baseLocation, + String doc, + Map<String, String> props) { if (!namespaceExists(identifier.namespace())) { throw new NoSuchNamespaceException( "Cannot create generic table %s. Namespace does not exist: %s", @@ -78,6 +82,7 @@ public class PolarisInMemoryCatalog extends InMemoryCatalog implements PolarisCa GenericTable.builder() .setName(identifier.name()) .setFormat(format) + .setBaseLocation(baseLocation) .setProperties(props) .build()); diff --git a/plugins/spark/v3.5/spark/src/test/java/org/apache/polaris/spark/rest/DeserializationTest.java b/plugins/spark/v3.5/spark/src/test/java/org/apache/polaris/spark/rest/DeserializationTest.java index d4d4da6ab..0f7d3c99b 100644 --- a/plugins/spark/v3.5/spark/src/test/java/org/apache/polaris/spark/rest/DeserializationTest.java +++ b/plugins/spark/v3.5/spark/src/test/java/org/apache/polaris/spark/rest/DeserializationTest.java @@ -65,15 +65,19 @@ public class DeserializationTest { @ParameterizedTest @MethodSource("genericTableTestCases") - public void testLoadGenericTableRESTResponse(String doc, Map<String, String> properties) + public void testLoadGenericTableRESTResponse( + String baseLocation, String doc, Map<String, String> properties) throws JsonProcessingException { - GenericTable table = + GenericTable.Builder tableBuilder = GenericTable.builder() .setFormat("delta") .setName("test-table") .setProperties(properties) - .setDoc(doc) - .build(); + .setDoc(doc); + if (baseLocation != null) { + tableBuilder.setBaseLocation(baseLocation); + } + GenericTable table = tableBuilder.build(); LoadGenericTableRESTResponse response = new LoadGenericTableRESTResponse(table); String json = mapper.writeValueAsString(response); LoadGenericTableRESTResponse deserializedResponse = @@ -82,11 +86,13 @@ public class DeserializationTest { assertThat(deserializedResponse.getTable().getName()).isEqualTo("test-table"); assertThat(deserializedResponse.getTable().getDoc()).isEqualTo(doc); assertThat(deserializedResponse.getTable().getProperties().size()).isEqualTo(properties.size()); + assertThat(deserializedResponse.getTable().getBaseLocation()).isEqualTo(baseLocation); } @ParameterizedTest @MethodSource("genericTableTestCases") - public void testCreateGenericTableRESTRequest(String doc, Map<String, String> properties) + public void testCreateGenericTableRESTRequest( + String baseLocation, String doc, Map<String, String> properties) throws JsonProcessingException { CreateGenericTableRESTRequest request = new CreateGenericTableRESTRequest( @@ -94,6 +100,7 @@ public class DeserializationTest { .setName("test-table") .setFormat("delta") .setDoc(doc) + .setBaseLocation(baseLocation) .setProperties(properties) .build()); String json = mapper.writeValueAsString(request); @@ -103,6 +110,7 @@ public class DeserializationTest { assertThat(deserializedRequest.getFormat()).isEqualTo("delta"); assertThat(deserializedRequest.getDoc()).isEqualTo(doc); assertThat(deserializedRequest.getProperties().size()).isEqualTo(properties.size()); + assertThat(deserializedRequest.getBaseLocation()).isEqualTo(baseLocation); } @Test @@ -150,10 +158,12 @@ public class DeserializationTest { var doc = "table for testing"; var properties = Maps.newHashMap(); properties.put("location", "s3://path/to/table/"); + var baseLocation = "s3://path/to/table/"; return Stream.of( - Arguments.of(doc, properties), - Arguments.of(null, Maps.newHashMap()), - Arguments.of(doc, Maps.newHashMap()), - Arguments.of(null, properties)); + Arguments.of(null, doc, properties), + Arguments.of(baseLocation, doc, properties), + Arguments.of(null, null, Maps.newHashMap()), + Arguments.of(baseLocation, doc, Maps.newHashMap()), + Arguments.of(baseLocation, null, properties)); } } diff --git a/polaris-core/src/main/java/org/apache/polaris/core/entity/table/GenericTableEntity.java b/polaris-core/src/main/java/org/apache/polaris/core/entity/table/GenericTableEntity.java index a5eae86fe..5f9250803 100644 --- a/polaris-core/src/main/java/org/apache/polaris/core/entity/table/GenericTableEntity.java +++ b/polaris-core/src/main/java/org/apache/polaris/core/entity/table/GenericTableEntity.java @@ -25,6 +25,7 @@ import org.apache.iceberg.rest.RESTUtil; import org.apache.polaris.core.entity.NamespaceEntity; import org.apache.polaris.core.entity.PolarisBaseEntity; import org.apache.polaris.core.entity.PolarisEntity; +import org.apache.polaris.core.entity.PolarisEntityConstants; import org.apache.polaris.core.entity.PolarisEntitySubType; import org.apache.polaris.core.entity.PolarisEntityType; @@ -58,6 +59,12 @@ public class GenericTableEntity extends TableLikeEntity { return getInternalPropertiesAsMap().get(GenericTableEntity.DOC_KEY); } + @Override + @JsonIgnore + public String getBaseLocation() { + return getInternalPropertiesAsMap().get(PolarisEntityConstants.ENTITY_BASE_LOCATION); + } + public static class Builder extends PolarisEntity.BaseBuilder<GenericTableEntity, GenericTableEntity.Builder> { public Builder(TableIdentifier tableIdentifier, String format) { @@ -79,6 +86,11 @@ public class GenericTableEntity extends TableLikeEntity { return this; } + public GenericTableEntity.Builder setBaseLocation(String location) { + internalProperties.put(PolarisEntityConstants.ENTITY_BASE_LOCATION, location); + return this; + } + public GenericTableEntity.Builder setTableIdentifier(TableIdentifier identifier) { Namespace namespace = identifier.namespace(); setParentNamespace(namespace); diff --git a/polaris-core/src/main/java/org/apache/polaris/core/entity/table/IcebergTableLikeEntity.java b/polaris-core/src/main/java/org/apache/polaris/core/entity/table/IcebergTableLikeEntity.java index 21e922982..57d15a103 100644 --- a/polaris-core/src/main/java/org/apache/polaris/core/entity/table/IcebergTableLikeEntity.java +++ b/polaris-core/src/main/java/org/apache/polaris/core/entity/table/IcebergTableLikeEntity.java @@ -23,7 +23,6 @@ import java.util.Optional; import org.apache.iceberg.catalog.Namespace; import org.apache.iceberg.catalog.TableIdentifier; import org.apache.iceberg.rest.RESTUtil; -import org.apache.polaris.core.entity.LocationBasedEntity; import org.apache.polaris.core.entity.NamespaceEntity; import org.apache.polaris.core.entity.PolarisBaseEntity; import org.apache.polaris.core.entity.PolarisEntity; @@ -34,7 +33,7 @@ import org.apache.polaris.core.entity.PolarisEntityType; * An entity type for {@link TableLikeEntity} instances that conform to iceberg semantics around * locations. This includes both Iceberg tables and Iceberg views. */ -public class IcebergTableLikeEntity extends TableLikeEntity implements LocationBasedEntity { +public class IcebergTableLikeEntity extends TableLikeEntity { // For applicable types, this key on the "internalProperties" map will return the location // of the internalProperties JSON file. public static final String METADATA_LOCATION_KEY = "metadata-location"; diff --git a/polaris-core/src/main/java/org/apache/polaris/core/entity/table/TableLikeEntity.java b/polaris-core/src/main/java/org/apache/polaris/core/entity/table/TableLikeEntity.java index 22808d1a7..21ba80f4a 100644 --- a/polaris-core/src/main/java/org/apache/polaris/core/entity/table/TableLikeEntity.java +++ b/polaris-core/src/main/java/org/apache/polaris/core/entity/table/TableLikeEntity.java @@ -23,16 +23,16 @@ import jakarta.annotation.Nonnull; import org.apache.iceberg.catalog.Namespace; import org.apache.iceberg.catalog.TableIdentifier; import org.apache.iceberg.rest.RESTUtil; +import org.apache.polaris.core.entity.LocationBasedEntity; import org.apache.polaris.core.entity.NamespaceEntity; import org.apache.polaris.core.entity.PolarisBaseEntity; import org.apache.polaris.core.entity.PolarisEntity; -import org.apache.polaris.core.entity.PolarisEntityType; /** * An entity type for all table-like entities including Iceberg tables, Iceberg views, and generic * tables. This entity maps to {@link PolarisEntityType#TABLE_LIKE} */ -public abstract class TableLikeEntity extends PolarisEntity { +public abstract class TableLikeEntity extends PolarisEntity implements LocationBasedEntity { public TableLikeEntity(@Nonnull PolarisBaseEntity sourceEntity) { super(sourceEntity); diff --git a/runtime/service/src/test/java/org/apache/polaris/service/quarkus/admin/PolarisAuthzTestBase.java b/runtime/service/src/test/java/org/apache/polaris/service/quarkus/admin/PolarisAuthzTestBase.java index 013e2c99e..c86cefe6e 100644 --- a/runtime/service/src/test/java/org/apache/polaris/service/quarkus/admin/PolarisAuthzTestBase.java +++ b/runtime/service/src/test/java/org/apache/polaris/service/quarkus/admin/PolarisAuthzTestBase.java @@ -325,7 +325,8 @@ public abstract class PolarisAuthzTestBase { baseCatalog.buildTable(TABLE_NS1B_1, SCHEMA).create(); baseCatalog.buildTable(TABLE_NS2_1, SCHEMA).create(); - genericTableCatalog.createGenericTable(TABLE_NS1_1_GENERIC, "format", "doc", Map.of()); + genericTableCatalog.createGenericTable( + TABLE_NS1_1_GENERIC, "format", "file:///tmp/test_table", "doc", Map.of()); policyCatalog.createPolicy( POLICY_NS1_1, diff --git a/runtime/service/src/test/java/org/apache/polaris/service/quarkus/catalog/AbstractPolarisGenericTableCatalogTest.java b/runtime/service/src/test/java/org/apache/polaris/service/quarkus/catalog/AbstractPolarisGenericTableCatalogTest.java index ea044fbab..ade4768f4 100644 --- a/runtime/service/src/test/java/org/apache/polaris/service/quarkus/catalog/AbstractPolarisGenericTableCatalogTest.java +++ b/runtime/service/src/test/java/org/apache/polaris/service/quarkus/catalog/AbstractPolarisGenericTableCatalogTest.java @@ -81,6 +81,9 @@ import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestInfo; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.NullSource; +import org.junit.jupiter.params.provider.ValueSource; import org.mockito.Mockito; import software.amazon.awssdk.services.sts.StsClient; import software.amazon.awssdk.services.sts.model.AssumeRoleRequest; @@ -277,7 +280,7 @@ public abstract class AbstractPolarisGenericTableCatalogTest { Assertions.assertThatCode( () -> genericTableCatalog.createGenericTable( - TableIdentifier.of("ns", "t1"), "test-format", "doc", Map.of())) + TableIdentifier.of("ns", "t1"), "test-format", null, "doc", Map.of())) .doesNotThrowAnyException(); } @@ -286,12 +289,12 @@ public abstract class AbstractPolarisGenericTableCatalogTest { Namespace namespace = Namespace.of("ns"); icebergCatalog.createNamespace(namespace); genericTableCatalog.createGenericTable( - TableIdentifier.of("ns", "t1"), "format1", "doc", Map.of()); + TableIdentifier.of("ns", "t1"), "format1", null, "doc", Map.of()); Assertions.assertThatCode( () -> genericTableCatalog.createGenericTable( - TableIdentifier.of("ns", "t1"), "format2", "doc", Map.of())) + TableIdentifier.of("ns", "t1"), "format2", null, "doc", Map.of())) .hasMessageContaining("already exists"); Assertions.assertThatCode( @@ -308,7 +311,7 @@ public abstract class AbstractPolarisGenericTableCatalogTest { Assertions.assertThatCode( () -> genericTableCatalog.createGenericTable( - TableIdentifier.of("ns", "t1"), "format2", "doc", Map.of())) + TableIdentifier.of("ns", "t1"), "format2", null, "doc", Map.of())) .hasMessageContaining("already exists"); Assertions.assertThatCode( @@ -316,8 +319,10 @@ public abstract class AbstractPolarisGenericTableCatalogTest { .hasMessageContaining("already exists"); } - @Test - public void testGenericTableRoundTrip() { + @ParameterizedTest + @NullSource + @ValueSource(strings = {"", "file://path/to/my/table"}) + public void testGenericTableRoundTrip(String baseLocation) { Namespace namespace = Namespace.of("ns"); icebergCatalog.createNamespace(namespace); @@ -327,7 +332,7 @@ public abstract class AbstractPolarisGenericTableCatalogTest { String doc = "round-trip-doc"; genericTableCatalog.createGenericTable( - TableIdentifier.of("ns", tableName), format, doc, properties); + TableIdentifier.of("ns", tableName), format, baseLocation, doc, properties); GenericTableEntity resultEntity = genericTableCatalog.loadGenericTable(TableIdentifier.of("ns", tableName)); @@ -335,6 +340,7 @@ public abstract class AbstractPolarisGenericTableCatalogTest { Assertions.assertThat(resultEntity.getFormat()).isEqualTo(format); Assertions.assertThat(resultEntity.getPropertiesAsMap()).isEqualTo(properties); Assertions.assertThat(resultEntity.getName()).isEqualTo(tableName); + Assertions.assertThat(resultEntity.getBaseLocation()).isEqualTo(baseLocation); } @Test @@ -381,7 +387,7 @@ public abstract class AbstractPolarisGenericTableCatalogTest { String tableName = "t1"; genericTableCatalog.createGenericTable( - TableIdentifier.of("ns", tableName), "format", "doc", Map.of()); + TableIdentifier.of("ns", tableName), "format", null, "doc", Map.of()); Assertions.assertThatCode(() -> icebergCatalog.loadTable(TableIdentifier.of("ns", tableName))) .hasMessageContaining("does not exist: ns.t1"); } @@ -394,7 +400,7 @@ public abstract class AbstractPolarisGenericTableCatalogTest { String tableName = "t1"; genericTableCatalog.createGenericTable( - TableIdentifier.of("ns", tableName), "format", "doc", Map.of()); + TableIdentifier.of("ns", tableName), "format", null, "doc", Map.of()); Assertions.assertThatCode(() -> icebergCatalog.loadView(TableIdentifier.of("ns", tableName))) .hasMessageContaining("does not exist: ns.t1"); } @@ -406,7 +412,7 @@ public abstract class AbstractPolarisGenericTableCatalogTest { for (int i = 0; i < 10; i++) { genericTableCatalog.createGenericTable( - TableIdentifier.of("ns", "t" + i), "format", "doc", Map.of()); + TableIdentifier.of("ns", "t" + i), "format", null, "doc", Map.of()); } List<TableIdentifier> listResult = genericTableCatalog.listGenericTables(namespace); @@ -468,7 +474,7 @@ public abstract class AbstractPolarisGenericTableCatalogTest { for (int i = 0; i < 10; i++) { genericTableCatalog.createGenericTable( - TableIdentifier.of("ns", "g" + i), "format", "doc", Map.of()); + TableIdentifier.of("ns", "g" + i), "format", null, "doc", Map.of()); } Assertions.assertThat(genericTableCatalog.listGenericTables(namespace).size()).isEqualTo(10); @@ -514,7 +520,7 @@ public abstract class AbstractPolarisGenericTableCatalogTest { Namespace namespace = Namespace.of("ns"); icebergCatalog.createNamespace(namespace); genericTableCatalog.createGenericTable( - TableIdentifier.of("ns", "t1"), "format", "doc", Map.of()); + TableIdentifier.of("ns", "t1"), "format", null, "doc", Map.of()); Assertions.assertThat(icebergCatalog.dropTable(TableIdentifier.of("ns", "t1"))).isFalse(); Assertions.assertThat(genericTableCatalog.loadGenericTable(TableIdentifier.of("ns", "t1"))) @@ -526,7 +532,7 @@ public abstract class AbstractPolarisGenericTableCatalogTest { Namespace namespace = Namespace.of("ns"); icebergCatalog.createNamespace(namespace); genericTableCatalog.createGenericTable( - TableIdentifier.of("ns", "t1"), "format", "doc", Map.of()); + TableIdentifier.of("ns", "t1"), "format", null, "doc", Map.of()); Assertions.assertThat(icebergCatalog.dropView(TableIdentifier.of("ns", "t1"))).isFalse(); Assertions.assertThat(genericTableCatalog.loadGenericTable(TableIdentifier.of("ns", "t1"))) diff --git a/runtime/service/src/test/java/org/apache/polaris/service/quarkus/catalog/PolarisGenericTableCatalogHandlerAuthzTest.java b/runtime/service/src/test/java/org/apache/polaris/service/quarkus/catalog/PolarisGenericTableCatalogHandlerAuthzTest.java index 6deb37bb3..446e5505b 100644 --- a/runtime/service/src/test/java/org/apache/polaris/service/quarkus/catalog/PolarisGenericTableCatalogHandlerAuthzTest.java +++ b/runtime/service/src/test/java/org/apache/polaris/service/quarkus/catalog/PolarisGenericTableCatalogHandlerAuthzTest.java @@ -188,7 +188,7 @@ public class PolarisGenericTableCatalogHandlerAuthzTest extends PolarisAuthzTest PolarisPrivilege.CATALOG_MANAGE_CONTENT), () -> { newWrapper(Set.of(PRINCIPAL_ROLE1)) - .createGenericTable(newtable, "format", "doc", Map.of()); + .createGenericTable(newtable, "format", "file:///temp/", "doc", Map.of()); }, () -> { newWrapper(Set.of(PRINCIPAL_ROLE2)).dropGenericTable(newtable); @@ -209,7 +209,8 @@ public class PolarisGenericTableCatalogHandlerAuthzTest extends PolarisAuthzTest PolarisPrivilege.TABLE_LIST), () -> { newWrapper(Set.of(PRINCIPAL_ROLE1)) - .createGenericTable(TableIdentifier.of(NS2, "newtable"), "format", "doc", Map.of()); + .createGenericTable( + TableIdentifier.of(NS2, "newtable"), "format", null, "doc", Map.of()); }); } @@ -256,7 +257,7 @@ public class PolarisGenericTableCatalogHandlerAuthzTest extends PolarisAuthzTest }, () -> { newWrapper(Set.of(PRINCIPAL_ROLE2)) - .createGenericTable(TABLE_NS1_1_GENERIC, "format", "doc", Map.of()); + .createGenericTable(TABLE_NS1_1_GENERIC, "format", "file:///temp/", "doc", Map.of()); }); } diff --git a/service/common/src/main/java/org/apache/polaris/service/catalog/generic/GenericTableCatalog.java b/service/common/src/main/java/org/apache/polaris/service/catalog/generic/GenericTableCatalog.java index 197be3645..ff1661290 100644 --- a/service/common/src/main/java/org/apache/polaris/service/catalog/generic/GenericTableCatalog.java +++ b/service/common/src/main/java/org/apache/polaris/service/catalog/generic/GenericTableCatalog.java @@ -32,7 +32,11 @@ public interface GenericTableCatalog { /** Create a generic table with the specified identifier */ GenericTableEntity createGenericTable( - TableIdentifier tableIdentifier, String format, String doc, Map<String, String> properties); + TableIdentifier tableIdentifier, + String format, + String baseLocation, + String doc, + Map<String, String> properties); /** Retrieve a generic table entity with a given identifier */ GenericTableEntity loadGenericTable(TableIdentifier tableIdentifier); diff --git a/service/common/src/main/java/org/apache/polaris/service/catalog/generic/GenericTableCatalogAdapter.java b/service/common/src/main/java/org/apache/polaris/service/catalog/generic/GenericTableCatalogAdapter.java index 0c40960f5..ddc03a291 100644 --- a/service/common/src/main/java/org/apache/polaris/service/catalog/generic/GenericTableCatalogAdapter.java +++ b/service/common/src/main/java/org/apache/polaris/service/catalog/generic/GenericTableCatalogAdapter.java @@ -101,6 +101,7 @@ public class GenericTableCatalogAdapter handler.createGenericTable( TableIdentifier.of(decodeNamespace(namespace), createGenericTableRequest.getName()), createGenericTableRequest.getFormat(), + createGenericTableRequest.getBaseLocation(), createGenericTableRequest.getDoc(), reservedProperties.removeReservedProperties(createGenericTableRequest.getProperties())); diff --git a/service/common/src/main/java/org/apache/polaris/service/catalog/generic/GenericTableCatalogHandler.java b/service/common/src/main/java/org/apache/polaris/service/catalog/generic/GenericTableCatalogHandler.java index 66a2b81d1..670cddbb0 100644 --- a/service/common/src/main/java/org/apache/polaris/service/catalog/generic/GenericTableCatalogHandler.java +++ b/service/common/src/main/java/org/apache/polaris/service/catalog/generic/GenericTableCatalogHandler.java @@ -69,16 +69,22 @@ public class GenericTableCatalogHandler extends CatalogHandler { } public LoadGenericTableResponse createGenericTable( - TableIdentifier identifier, String format, String doc, Map<String, String> properties) { + TableIdentifier identifier, + String format, + String baseLocation, + String doc, + Map<String, String> properties) { PolarisAuthorizableOperation op = PolarisAuthorizableOperation.CREATE_TABLE_DIRECT; authorizeCreateTableLikeUnderNamespaceOperationOrThrow(op, identifier); GenericTableEntity createdEntity = - this.genericTableCatalog.createGenericTable(identifier, format, doc, properties); + this.genericTableCatalog.createGenericTable( + identifier, format, baseLocation, doc, properties); GenericTable createdTable = GenericTable.builder() .setName(createdEntity.getName()) .setFormat(createdEntity.getFormat()) + .setBaseLocation(createdEntity.getBaseLocation()) .setDoc(createdEntity.getDoc()) .setProperties(createdEntity.getPropertiesAsMap()) .build(); @@ -102,6 +108,7 @@ public class GenericTableCatalogHandler extends CatalogHandler { GenericTable.builder() .setName(loadedEntity.getName()) .setFormat(loadedEntity.getFormat()) + .setBaseLocation(loadedEntity.getBaseLocation()) .setDoc(loadedEntity.getDoc()) .setProperties(loadedEntity.getPropertiesAsMap()) .build(); diff --git a/service/common/src/main/java/org/apache/polaris/service/catalog/generic/PolarisGenericTableCatalog.java b/service/common/src/main/java/org/apache/polaris/service/catalog/generic/PolarisGenericTableCatalog.java index 2b884e787..a198c6cb3 100644 --- a/service/common/src/main/java/org/apache/polaris/service/catalog/generic/PolarisGenericTableCatalog.java +++ b/service/common/src/main/java/org/apache/polaris/service/catalog/generic/PolarisGenericTableCatalog.java @@ -75,7 +75,11 @@ public class PolarisGenericTableCatalog implements GenericTableCatalog { @Override public GenericTableEntity createGenericTable( - TableIdentifier tableIdentifier, String format, String doc, Map<String, String> properties) { + TableIdentifier tableIdentifier, + String format, + String baseLocation, + String doc, + Map<String, String> properties) { PolarisResolvedPathWrapper resolvedParent = resolvedEntityView.getResolvedPath(tableIdentifier.namespace()); if (resolvedParent == null) { @@ -105,6 +109,7 @@ public class PolarisGenericTableCatalog implements GenericTableCatalog { .getId()) .setProperties(properties) .setDoc(doc) + .setBaseLocation(baseLocation) .setCreateTimestamp(System.currentTimeMillis()) .build(); } else {