This is an automated email from the ASF dual-hosted git repository.
lzljs3620320 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/paimon.git
The following commit(s) were added to refs/heads/master by this push:
new 66396f70cb [core] RESTCatalog: fix NotImplementedException handle when
create and alter table (#5274)
66396f70cb is described below
commit 66396f70cb41809bf160f25dbcb2dad3e573e858
Author: jerry <[email protected]>
AuthorDate: Tue Mar 18 20:28:08 2025 +0800
[core] RESTCatalog: fix NotImplementedException handle when create and
alter table (#5274)
---
.../java/org/apache/paimon/rest/RESTCatalog.java | 8 ++
.../apache/paimon/rest/auth/DLFAuthProvider.java | 8 +-
.../rest/responses/BaseResourceAuditResponse.java | 88 ++++++++++++++++++++++
.../paimon/rest/responses/GetDatabaseResponse.java | 11 ++-
.../paimon/rest/responses/GetTableResponse.java | 10 ++-
.../paimon/rest/responses/GetViewResponse.java | 10 ++-
.../org/apache/paimon/catalog/CatalogTestBase.java | 36 +++++++++
.../org/apache/paimon/rest/MockRESTMessage.java | 32 +++++++-
.../org/apache/paimon/rest/RESTCatalogServer.java | 66 +++++++++++-----
.../apache/paimon/rest/RESTCatalogTestBase.java | 14 ++++
.../apache/paimon/flink/CatalogTableITCase.java | 4 +-
paimon-open-api/rest-catalog-open-api.yaml | 30 ++++++++
.../paimon/open/api/RESTCatalogController.java | 45 +++++++++--
.../org/apache/paimon/spark/SparkReadITCase.java | 4 +-
14 files changed, 326 insertions(+), 40 deletions(-)
diff --git a/paimon-core/src/main/java/org/apache/paimon/rest/RESTCatalog.java
b/paimon-core/src/main/java/org/apache/paimon/rest/RESTCatalog.java
index 32cfd52069..e37e85688c 100644
--- a/paimon-core/src/main/java/org/apache/paimon/rest/RESTCatalog.java
+++ b/paimon-core/src/main/java/org/apache/paimon/rest/RESTCatalog.java
@@ -75,6 +75,7 @@ import org.apache.paimon.schema.SchemaChange;
import org.apache.paimon.schema.TableSchema;
import org.apache.paimon.table.Table;
import org.apache.paimon.table.TableSnapshot;
+import org.apache.paimon.table.system.SystemTableLoader;
import org.apache.paimon.utils.Pair;
import org.apache.paimon.view.View;
import org.apache.paimon.view.ViewImpl;
@@ -286,6 +287,9 @@ public class RESTCatalog implements Catalog {
@Override
public List<String> listTables(String databaseName) throws
DatabaseNotExistException {
try {
+ if (isSystemDatabase(databaseName)) {
+ return SystemTableLoader.loadGlobalTableNames();
+ }
return listDataFromPageApi(
queryParams ->
client.get(
@@ -465,6 +469,8 @@ public class RESTCatalog implements Catalog {
if (!ignoreIfExists) {
throw new TableAlreadyExistException(identifier);
}
+ } catch (NotImplementedException e) {
+ throw new RuntimeException(new
UnsupportedOperationException(e.getMessage()));
} catch (NoSuchResourceException e) {
throw new DatabaseNotExistException(identifier.getDatabaseName());
} catch (BadRequestException e) {
@@ -522,6 +528,8 @@ public class RESTCatalog implements Catalog {
throw new TableNoPermissionException(identifier, e);
} catch (ServiceFailureException e) {
throw new IllegalStateException(e.getMessage());
+ } catch (NotImplementedException e) {
+ throw new UnsupportedOperationException(e.getMessage());
} catch (BadRequestException e) {
throw new RuntimeException(new
IllegalArgumentException(e.getMessage()));
}
diff --git
a/paimon-core/src/main/java/org/apache/paimon/rest/auth/DLFAuthProvider.java
b/paimon-core/src/main/java/org/apache/paimon/rest/auth/DLFAuthProvider.java
index 0f5371228d..f82720b33f 100644
--- a/paimon-core/src/main/java/org/apache/paimon/rest/auth/DLFAuthProvider.java
+++ b/paimon-core/src/main/java/org/apache/paimon/rest/auth/DLFAuthProvider.java
@@ -89,9 +89,11 @@ public class DLFAuthProvider implements AuthProvider {
public Map<String, String> header(
Map<String, String> baseHeader, RESTAuthParameter
restAuthParameter) {
try {
- ZonedDateTime now = ZonedDateTime.now(ZoneOffset.UTC);
- String date = now.format(AUTH_DATE_FORMATTER);
- String dateTime = now.format(AUTH_DATE_TIME_FORMATTER);
+ String dateTime =
+ baseHeader.getOrDefault(
+ DLF_DATE_HEADER_KEY.toLowerCase(),
+
ZonedDateTime.now(ZoneOffset.UTC).format(AUTH_DATE_TIME_FORMATTER));
+ String date = dateTime.substring(0, 8);
Map<String, String> signHeaders =
generateSignHeaders(
restAuthParameter.data(), dateTime,
token.getSecurityToken());
diff --git
a/paimon-core/src/main/java/org/apache/paimon/rest/responses/BaseResourceAuditResponse.java
b/paimon-core/src/main/java/org/apache/paimon/rest/responses/BaseResourceAuditResponse.java
new file mode 100644
index 0000000000..d867efd180
--- /dev/null
+++
b/paimon-core/src/main/java/org/apache/paimon/rest/responses/BaseResourceAuditResponse.java
@@ -0,0 +1,88 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.paimon.rest.responses;
+
+import org.apache.paimon.rest.RESTResponse;
+
+import
org.apache.paimon.shade.jackson2.com.fasterxml.jackson.annotation.JsonCreator;
+import
org.apache.paimon.shade.jackson2.com.fasterxml.jackson.annotation.JsonGetter;
+import
org.apache.paimon.shade.jackson2.com.fasterxml.jackson.annotation.JsonProperty;
+
+/** Base class for database, table, view, audit response. */
+public abstract class BaseResourceAuditResponse implements RESTResponse {
+ protected static final String FIELD_OWNER = "owner";
+ protected static final String FIELD_CREATED_AT = "createdAt";
+ protected static final String FIELD_CREATED_BY = "createdBy";
+ protected static final String FIELD_UPDATED_AT = "updatedAt";
+ protected static final String FIELD_UPDATED_BY = "updatedBy";
+
+ @JsonProperty(FIELD_OWNER)
+ private final String owner;
+
+ @JsonProperty(FIELD_CREATED_AT)
+ private final long createdAt;
+
+ @JsonProperty(FIELD_CREATED_BY)
+ private final String createdBy;
+
+ @JsonProperty(FIELD_UPDATED_AT)
+ private final long updatedAt;
+
+ @JsonProperty(FIELD_UPDATED_BY)
+ private final String updatedBy;
+
+ @JsonCreator
+ public BaseResourceAuditResponse(
+ @JsonProperty(FIELD_OWNER) String owner,
+ @JsonProperty(FIELD_CREATED_AT) long createdAt,
+ @JsonProperty(FIELD_CREATED_BY) String createdBy,
+ @JsonProperty(FIELD_UPDATED_AT) long updatedAt,
+ @JsonProperty(FIELD_UPDATED_BY) String updatedBy) {
+ this.owner = owner;
+ this.createdAt = createdAt;
+ this.createdBy = createdBy;
+ this.updatedAt = updatedAt;
+ this.updatedBy = updatedBy;
+ }
+
+ @JsonGetter(FIELD_OWNER)
+ public String getOwner() {
+ return owner;
+ }
+
+ @JsonGetter(FIELD_CREATED_AT)
+ public long getCreatedAt() {
+ return createdAt;
+ }
+
+ @JsonGetter(FIELD_CREATED_BY)
+ public String getCreatedBy() {
+ return createdBy;
+ }
+
+ @JsonGetter(FIELD_UPDATED_AT)
+ public long getUpdatedAt() {
+ return updatedAt;
+ }
+
+ @JsonGetter(FIELD_UPDATED_BY)
+ public String getUpdatedBy() {
+ return updatedBy;
+ }
+}
diff --git
a/paimon-core/src/main/java/org/apache/paimon/rest/responses/GetDatabaseResponse.java
b/paimon-core/src/main/java/org/apache/paimon/rest/responses/GetDatabaseResponse.java
index 81c706cccb..b226f41888 100644
---
a/paimon-core/src/main/java/org/apache/paimon/rest/responses/GetDatabaseResponse.java
+++
b/paimon-core/src/main/java/org/apache/paimon/rest/responses/GetDatabaseResponse.java
@@ -33,7 +33,8 @@ import static org.apache.paimon.catalog.Catalog.COMMENT_PROP;
/** Response for getting database. */
@JsonIgnoreProperties(ignoreUnknown = true)
-public class GetDatabaseResponse implements RESTResponse, Database {
+public class GetDatabaseResponse extends BaseResourceAuditResponse
+ implements RESTResponse, Database {
private static final String FIELD_ID = "id";
private static final String FIELD_NAME = "name";
@@ -52,7 +53,13 @@ public class GetDatabaseResponse implements RESTResponse,
Database {
public GetDatabaseResponse(
@JsonProperty(FIELD_ID) String id,
@JsonProperty(FIELD_NAME) String name,
- @JsonProperty(FIELD_OPTIONS) Map<String, String> options) {
+ @JsonProperty(FIELD_OPTIONS) Map<String, String> options,
+ @JsonProperty(FIELD_OWNER) String owner,
+ @JsonProperty(FIELD_CREATED_AT) long createdAt,
+ @JsonProperty(FIELD_CREATED_BY) String createdBy,
+ @JsonProperty(FIELD_UPDATED_AT) long updatedAt,
+ @JsonProperty(FIELD_UPDATED_BY) String updatedBy) {
+ super(owner, createdAt, createdBy, updatedAt, updatedBy);
this.id = id;
this.name = name;
this.options = options;
diff --git
a/paimon-core/src/main/java/org/apache/paimon/rest/responses/GetTableResponse.java
b/paimon-core/src/main/java/org/apache/paimon/rest/responses/GetTableResponse.java
index 82b55fae7f..c556b591cc 100644
---
a/paimon-core/src/main/java/org/apache/paimon/rest/responses/GetTableResponse.java
+++
b/paimon-core/src/main/java/org/apache/paimon/rest/responses/GetTableResponse.java
@@ -28,7 +28,7 @@ import
org.apache.paimon.shade.jackson2.com.fasterxml.jackson.annotation.JsonPro
/** Response for getting table. */
@JsonIgnoreProperties(ignoreUnknown = true)
-public class GetTableResponse implements RESTResponse {
+public class GetTableResponse extends BaseResourceAuditResponse implements
RESTResponse {
private static final String FIELD_ID = "id";
private static final String FIELD_NAME = "name";
@@ -57,7 +57,13 @@ public class GetTableResponse implements RESTResponse {
@JsonProperty(FIELD_NAME) String name,
@JsonProperty(FIELD_IS_EXTERNAL) boolean isExternal,
@JsonProperty(FIELD_SCHEMA_ID) long schemaId,
- @JsonProperty(FIELD_SCHEMA) Schema schema) {
+ @JsonProperty(FIELD_SCHEMA) Schema schema,
+ @JsonProperty(FIELD_OWNER) String owner,
+ @JsonProperty(FIELD_CREATED_AT) long createdAt,
+ @JsonProperty(FIELD_CREATED_BY) String createdBy,
+ @JsonProperty(FIELD_UPDATED_AT) long updatedAt,
+ @JsonProperty(FIELD_UPDATED_BY) String updatedBy) {
+ super(owner, createdAt, createdBy, updatedAt, updatedBy);
this.id = id;
this.name = name;
this.isExternal = isExternal;
diff --git
a/paimon-core/src/main/java/org/apache/paimon/rest/responses/GetViewResponse.java
b/paimon-core/src/main/java/org/apache/paimon/rest/responses/GetViewResponse.java
index 7fe1237691..ab1e0341b1 100644
---
a/paimon-core/src/main/java/org/apache/paimon/rest/responses/GetViewResponse.java
+++
b/paimon-core/src/main/java/org/apache/paimon/rest/responses/GetViewResponse.java
@@ -28,7 +28,7 @@ import
org.apache.paimon.shade.jackson2.com.fasterxml.jackson.annotation.JsonPro
/** Response for getting view. */
@JsonIgnoreProperties(ignoreUnknown = true)
-public class GetViewResponse implements RESTResponse {
+public class GetViewResponse extends BaseResourceAuditResponse implements
RESTResponse {
private static final String FIELD_ID = "id";
private static final String FIELD_NAME = "name";
@@ -47,7 +47,13 @@ public class GetViewResponse implements RESTResponse {
public GetViewResponse(
@JsonProperty(FIELD_ID) String id,
@JsonProperty(FIELD_NAME) String name,
- @JsonProperty(FIELD_SCHEMA) ViewSchema schema) {
+ @JsonProperty(FIELD_SCHEMA) ViewSchema schema,
+ @JsonProperty(FIELD_OWNER) String owner,
+ @JsonProperty(FIELD_CREATED_AT) long createdAt,
+ @JsonProperty(FIELD_CREATED_BY) String createdBy,
+ @JsonProperty(FIELD_UPDATED_AT) long updatedAt,
+ @JsonProperty(FIELD_UPDATED_BY) String updatedBy) {
+ super(owner, createdAt, createdBy, updatedAt, updatedBy);
this.id = id;
this.name = name;
this.schema = schema;
diff --git
a/paimon-core/src/test/java/org/apache/paimon/catalog/CatalogTestBase.java
b/paimon-core/src/test/java/org/apache/paimon/catalog/CatalogTestBase.java
index 77f62ed8b6..49d6f623c1 100644
--- a/paimon-core/src/test/java/org/apache/paimon/catalog/CatalogTestBase.java
+++ b/paimon-core/src/test/java/org/apache/paimon/catalog/CatalogTestBase.java
@@ -36,12 +36,15 @@ import org.apache.paimon.table.Table;
import org.apache.paimon.table.sink.BatchTableCommit;
import org.apache.paimon.table.sink.BatchTableWrite;
import org.apache.paimon.table.sink.BatchWriteBuilder;
+import org.apache.paimon.table.system.AllTableOptionsTable;
+import org.apache.paimon.table.system.CatalogOptionsTable;
import org.apache.paimon.types.DataField;
import org.apache.paimon.types.DataTypes;
import org.apache.paimon.types.RowType;
import org.apache.paimon.view.View;
import org.apache.paimon.view.ViewImpl;
+import org.apache.paimon.shade.guava30.com.google.common.collect.ImmutableMap;
import org.apache.paimon.shade.guava30.com.google.common.collect.Lists;
import org.apache.paimon.shade.guava30.com.google.common.collect.Maps;
@@ -483,6 +486,20 @@ public abstract class CatalogTestBase {
.hasRootCauseInstanceOf(IllegalArgumentException.class)
.hasRootCauseMessage(
"Unrecognized option for boolean: max. Expected either
true or false(case insensitive)");
+
+ // conflict options
+ Schema conflictOptionsSchema =
+ Schema.newBuilder()
+ .column("a", DataTypes.INT())
+ .options(ImmutableMap.of("changelog-producer",
"input"))
+ .build();
+ assertThatThrownBy(
+ () ->
+ catalog.createTable(
+ Identifier.create("test_db",
"conflict_options_table"),
+ conflictOptionsSchema,
+ false))
+ .isInstanceOf(RuntimeException.class);
}
@Test
@@ -538,6 +555,12 @@ public abstract class CatalogTestBase {
assertThatExceptionOfType(Catalog.TableNotExistException.class)
.isThrownBy(
() ->
catalog.getTable(Identifier.create(SYSTEM_DATABASE_NAME, "1111")));
+
+ List<String> sysTables = catalog.listTables(SYSTEM_DATABASE_NAME);
+ assertThat(sysTables)
+ .containsExactlyInAnyOrder(
+ AllTableOptionsTable.ALL_TABLE_OPTIONS,
+ CatalogOptionsTable.CATALOG_OPTIONS);
}
@Test
@@ -683,6 +706,19 @@ public abstract class CatalogTestBase {
anyCauseMatches(
Catalog.ColumnAlreadyExistException.class,
"Column col1 already exists in the
test_db.test_table table."));
+
+ // conflict options
+ assertThatThrownBy(
+ () ->
+ catalog.alterTable(
+ identifier,
+ Lists.newArrayList(
+ SchemaChange.setOption(
+ "changelog-producer",
"input")),
+ false))
+ .isInstanceOf(RuntimeException.class)
+ .hasMessageContaining(
+ "Can not set changelog-producer on table without
primary keys");
}
@Test
diff --git
a/paimon-core/src/test/java/org/apache/paimon/rest/MockRESTMessage.java
b/paimon-core/src/test/java/org/apache/paimon/rest/MockRESTMessage.java
index 1470b52001..3c099f7372 100644
--- a/paimon-core/src/test/java/org/apache/paimon/rest/MockRESTMessage.java
+++ b/paimon-core/src/test/java/org/apache/paimon/rest/MockRESTMessage.java
@@ -82,7 +82,15 @@ public class MockRESTMessage {
Map<String, String> options = new HashMap<>();
options.put("a", "b");
options.put(COMMENT_PROP, "comment");
- return new GetDatabaseResponse(UUID.randomUUID().toString(), name,
options);
+ return new GetDatabaseResponse(
+ UUID.randomUUID().toString(),
+ name,
+ options,
+ "owner",
+ System.currentTimeMillis(),
+ "created",
+ System.currentTimeMillis(),
+ "updated");
}
public static ListDatabasesResponse listDatabasesResponse(String name) {
@@ -219,7 +227,17 @@ public class MockRESTMessage {
Map<String, String> options = new HashMap<>();
options.put("option-1", "value-1");
options.put("option-2", "value-2");
- return new GetTableResponse(UUID.randomUUID().toString(), "", false,
1, schema(options));
+ return new GetTableResponse(
+ UUID.randomUUID().toString(),
+ "",
+ false,
+ 1,
+ schema(options),
+ "owner",
+ System.currentTimeMillis(),
+ "created",
+ System.currentTimeMillis(),
+ "updated");
}
public static CreateViewRequest createViewRequest(String name) {
@@ -228,7 +246,15 @@ public class MockRESTMessage {
}
public static GetViewResponse getViewResponse() {
- return new GetViewResponse(UUID.randomUUID().toString(), "",
viewSchema());
+ return new GetViewResponse(
+ UUID.randomUUID().toString(),
+ "",
+ viewSchema(),
+ "owner",
+ System.currentTimeMillis(),
+ "created",
+ System.currentTimeMillis(),
+ "updated");
}
public static ListViewsResponse listViewsResponse() {
diff --git
a/paimon-core/src/test/java/org/apache/paimon/rest/RESTCatalogServer.java
b/paimon-core/src/test/java/org/apache/paimon/rest/RESTCatalogServer.java
index 9654f2647c..e151f976c5 100644
--- a/paimon-core/src/test/java/org/apache/paimon/rest/RESTCatalogServer.java
+++ b/paimon-core/src/test/java/org/apache/paimon/rest/RESTCatalogServer.java
@@ -273,10 +273,6 @@ public class RESTCatalogServer {
resources.length == 2 &&
resources[1].startsWith("tables");
boolean isTableDetails =
resources.length == 2 &&
resources[1].startsWith("table-details");
- boolean isViewRename =
- resources.length == 3
- && "views".equals(resources[1])
- && "rename".equals(resources[2]);
boolean isView =
resources.length == 3
&& "views".equals(resources[1])
@@ -485,14 +481,18 @@ public class RESTCatalogServer {
new ErrorResponse(
null, null,
e.getCause().getCause().getMessage(), 400);
return mockResponse(response, 400);
- } else if (e instanceof UnsupportedOperationException) {
+ } else if (e instanceof UnsupportedOperationException
+ || e.getCause() instanceof
UnsupportedOperationException) {
response = new ErrorResponse(null, null,
e.getMessage(), 501);
return mockResponse(response, 501);
- } else if (e instanceof IllegalStateException) {
+ } else if (e instanceof IllegalStateException
+ || e.getCause() instanceof IllegalStateException) {
response = new ErrorResponse(null, null,
e.getMessage(), 500);
return mockResponse(response, 500);
}
- return new MockResponse().setResponseCode(500);
+ return new MockResponse()
+ .setResponseCode(500)
+ .setBody(e.getCause().getMessage());
}
}
};
@@ -671,7 +671,12 @@ public class RESTCatalogServer {
new GetDatabaseResponse(
UUID.randomUUID().toString(),
database.name(),
- database.options());
+ database.options(),
+ "owner",
+ 1L,
+ "created",
+ 1L,
+ "updated");
return mockResponse(response, 200);
case "DELETE":
catalog.dropDatabase(databaseName, false, true);
@@ -740,7 +745,7 @@ public class RESTCatalogServer {
tableMetadata =
createTableMetadata(
requestBody.getIdentifier(),
- 1L,
+ 0L,
requestBody.getSchema(),
UUID.randomUUID().toString(),
false);
@@ -842,7 +847,12 @@ public class RESTCatalogServer {
identifier.getTableName(),
entry.getValue().isExternal(),
entry.getValue().schema().id(),
- entry.getValue().schema().toSchema());
+ entry.getValue().schema().toSchema(),
+ "owner",
+ 1L,
+ "created",
+ 1L,
+ "updated");
tableDetails.add(getTableResponse);
}
}
@@ -862,7 +872,6 @@ public class RESTCatalogServer {
switch (method) {
case "GET":
TableMetadata tableMetadata;
- identifier.isSystemTable();
if (identifier.isSystemTable()) {
TableSchema schema = catalog.loadTableSchema(identifier);
tableMetadata =
@@ -877,7 +886,12 @@ public class RESTCatalogServer {
identifier.getTableName(),
tableMetadata.isExternal(),
tableMetadata.schema().id(),
- tableMetadata.schema().toSchema());
+ tableMetadata.schema().toSchema(),
+ "owner",
+ 1L,
+ "created",
+ 1L,
+ "updated");
return mockResponse(response, 200);
case "POST":
AlterTableRequest requestBody =
@@ -891,6 +905,8 @@ public class RESTCatalogServer {
System.out.println(e.getMessage());
}
tableMetadataStore.remove(identifier.getFullName());
+ tableSnapshotStore.remove(identifier.getFullName());
+ tablePartitionsStore.remove(identifier.getFullName());
return new MockResponse().setResponseCode(200);
default:
return new MockResponse().setResponseCode(404);
@@ -1171,7 +1187,15 @@ public class RESTCatalogServer {
view.dialects(),
view.comment().orElse(null),
view.options());
- return new GetViewResponse("id",
identifier.getTableName(), schema);
+ return new GetViewResponse(
+ "id",
+ identifier.getTableName(),
+ schema,
+ "owner",
+ 1L,
+ "created",
+ 1L,
+ "updated");
})
.collect(Collectors.toList());
}
@@ -1190,7 +1214,16 @@ public class RESTCatalogServer {
view.dialects(),
view.comment().orElse(null),
view.options());
- response = new GetViewResponse("id",
identifier.getTableName(), schema);
+ response =
+ new GetViewResponse(
+ "id",
+ identifier.getTableName(),
+ schema,
+ "owner",
+ 1L,
+ "created",
+ 1L,
+ "updated");
return mockResponse(response, 200);
}
throw new Catalog.ViewNotExistException(identifier);
@@ -1416,11 +1449,6 @@ public class RESTCatalogServer {
return createTableMetadata(identifier, 1L, schema,
UUID.randomUUID().toString(), true);
}
- private Partition spec2Partition(Map<String, String> spec) {
- // todo: need update
- return new Partition(spec, 123, 456, 789, 123, false);
- }
-
private FileStoreTable getFileTable(Identifier identifier)
throws Catalog.TableNotExistException {
if (tableMetadataStore.containsKey(identifier.getFullName())) {
diff --git
a/paimon-core/src/test/java/org/apache/paimon/rest/RESTCatalogTestBase.java
b/paimon-core/src/test/java/org/apache/paimon/rest/RESTCatalogTestBase.java
index 1a6b80ffe3..a2be1a631a 100644
--- a/paimon-core/src/test/java/org/apache/paimon/rest/RESTCatalogTestBase.java
+++ b/paimon-core/src/test/java/org/apache/paimon/rest/RESTCatalogTestBase.java
@@ -850,6 +850,20 @@ public abstract class RESTCatalogTestBase extends
CatalogTestBase {
assertThat(snapshot.get().fileSizeInBytes()).isEqualTo(2);
assertThat(snapshot.get().fileCount()).isEqualTo(3);
assertThat(snapshot.get().lastFileCreationTime()).isEqualTo(4);
+
+ // drop table then create table
+ catalog.dropTable(hasSnapshotTableIdentifier, true);
+ createTable(hasSnapshotTableIdentifier, Maps.newHashMap(),
Lists.newArrayList("col1"));
+ snapshot = catalog.loadSnapshot(hasSnapshotTableIdentifier);
+ assertThat(snapshot).isEmpty();
+ updateSnapshotOnRestServer(
+ hasSnapshotTableIdentifier, createSnapshotWithMillis(id,
millis), 5, 6, 7, 8);
+ snapshot = catalog.loadSnapshot(hasSnapshotTableIdentifier);
+ assertThat(snapshot.get().recordCount()).isEqualTo(5);
+
+ // test no snapshot
+ catalog.loadSnapshot(hasSnapshotTableIdentifier);
+ createTable(hasSnapshotTableIdentifier, Maps.newHashMap(),
Lists.newArrayList("col1"));
Identifier noSnapshotTableIdentifier =
Identifier.create("test_db_a_1", "unknown");
createTable(noSnapshotTableIdentifier, Maps.newHashMap(),
Lists.newArrayList("col1"));
snapshot = catalog.loadSnapshot(noSnapshotTableIdentifier);
diff --git
a/paimon-flink/paimon-flink-common/src/test/java/org/apache/paimon/flink/CatalogTableITCase.java
b/paimon-flink/paimon-flink-common/src/test/java/org/apache/paimon/flink/CatalogTableITCase.java
index 8cd6afbb4d..3be5071e7a 100644
---
a/paimon-flink/paimon-flink-common/src/test/java/org/apache/paimon/flink/CatalogTableITCase.java
+++
b/paimon-flink/paimon-flink-common/src/test/java/org/apache/paimon/flink/CatalogTableITCase.java
@@ -552,14 +552,14 @@ public class CatalogTableITCase extends CatalogITCaseBase
{
assertThatThrownBy(
() -> sql("CREATE TABLE T (a INT) WITH
('changelog-producer' = 'input')"))
.rootCause()
- .isInstanceOf(UnsupportedOperationException.class)
+ .isInstanceOf(RuntimeException.class)
.hasMessageContaining(
"Can not set changelog-producer on table without
primary keys");
sql("CREATE TABLE T (a INT)");
assertThatThrownBy(() -> sql("ALTER TABLE T SET
('changelog-producer'='input')"))
.rootCause()
- .isInstanceOf(UnsupportedOperationException.class)
+ .isInstanceOf(RuntimeException.class)
.hasMessageContaining(
"Can not set changelog-producer on table without
primary keys");
}
diff --git a/paimon-open-api/rest-catalog-open-api.yaml
b/paimon-open-api/rest-catalog-open-api.yaml
index fe10e71c3e..4183e28741 100644
--- a/paimon-open-api/rest-catalog-open-api.yaml
+++ b/paimon-open-api/rest-catalog-open-api.yaml
@@ -1219,6 +1219,16 @@ components:
$ref: '#/components/schemas/Schema'
uuid:
type: string
+ owner:
+ type: string
+ createdAt:
+ format: int64
+ createdBy:
+ type: string
+ updatedAt:
+ format: int64
+ updatedBy:
+ type: string
SchemaChange:
anyOf:
- $ref: '#/components/schemas/SetOption'
@@ -1509,6 +1519,16 @@ components:
type: object
additionalProperties:
type: string
+ owner:
+ type: string
+ createdAt:
+ format: int64
+ createdBy:
+ type: string
+ updatedAt:
+ format: int64
+ updatedBy:
+ type: string
ListTablesResponse:
type: object
properties:
@@ -1576,6 +1596,16 @@ components:
type: string
schema:
$ref: '#/components/schemas/ViewSchema'
+ owner:
+ type: string
+ createdAt:
+ format: int64
+ createdBy:
+ type: string
+ updatedAt:
+ format: int64
+ updatedBy:
+ type: string
ListViewsResponse:
type: object
properties:
diff --git
a/paimon-open-api/src/main/java/org/apache/paimon/open/api/RESTCatalogController.java
b/paimon-open-api/src/main/java/org/apache/paimon/open/api/RESTCatalogController.java
index 0ca57e0bde..faaa260bdd 100644
---
a/paimon-open-api/src/main/java/org/apache/paimon/open/api/RESTCatalogController.java
+++
b/paimon-open-api/src/main/java/org/apache/paimon/open/api/RESTCatalogController.java
@@ -164,7 +164,15 @@ public class RESTCatalogController {
public GetDatabaseResponse getDatabases(
@PathVariable String prefix, @PathVariable String database) {
Map<String, String> options = new HashMap<>();
- return new GetDatabaseResponse(UUID.randomUUID().toString(), "name",
options);
+ return new GetDatabaseResponse(
+ UUID.randomUUID().toString(),
+ "name",
+ options,
+ "owner",
+ System.currentTimeMillis(),
+ "created",
+ System.currentTimeMillis(),
+ "updated");
}
@Operation(
@@ -267,7 +275,12 @@ public class RESTCatalogController {
ImmutableList.of(),
ImmutableList.of(),
new HashMap<>(),
- "test-comment"));
+ "test-comment"),
+ "owner",
+ System.currentTimeMillis(),
+ "created",
+ System.currentTimeMillis(),
+ "updated");
return new ListTableDetailsResponse(ImmutableList.of(singleTable),
null);
}
@@ -305,7 +318,12 @@ public class RESTCatalogController {
ImmutableList.of(),
ImmutableList.of(),
new HashMap<>(),
- "comment"));
+ "comment"),
+ "owner",
+ System.currentTimeMillis(),
+ "created",
+ System.currentTimeMillis(),
+ "updated");
}
@Operation(
@@ -640,7 +658,16 @@ public class RESTCatalogController {
Collections.emptyMap(),
"comment",
Collections.singletonMap("pt", "1"));
- GetViewResponse singleView = new GetViewResponse("id", "name", schema);
+ GetViewResponse singleView =
+ new GetViewResponse(
+ "id",
+ "name",
+ schema,
+ "owner",
+ System.currentTimeMillis(),
+ "created",
+ System.currentTimeMillis(),
+ "updated");
return new ListViewDetailsResponse(ImmutableList.of(singleView), null);
}
@@ -692,7 +719,15 @@ public class RESTCatalogController {
Collections.emptyMap(),
"comment",
Collections.singletonMap("pt", "1"));
- return new GetViewResponse("id", "name", schema);
+ return new GetViewResponse(
+ "id",
+ "name",
+ schema,
+ "owner",
+ System.currentTimeMillis(),
+ "created",
+ System.currentTimeMillis(),
+ "updated");
}
@Operation(
diff --git
a/paimon-spark/paimon-spark-ut/src/test/java/org/apache/paimon/spark/SparkReadITCase.java
b/paimon-spark/paimon-spark-ut/src/test/java/org/apache/paimon/spark/SparkReadITCase.java
index b00267410a..a96954dcdf 100644
---
a/paimon-spark/paimon-spark-ut/src/test/java/org/apache/paimon/spark/SparkReadITCase.java
+++
b/paimon-spark/paimon-spark-ut/src/test/java/org/apache/paimon/spark/SparkReadITCase.java
@@ -312,7 +312,7 @@ public class SparkReadITCase extends SparkReadTestBase {
spark.sql(
"CREATE TABLE T (a INT) TBLPROPERTIES
('changelog-producer' = 'input')"))
.rootCause()
- .isInstanceOf(UnsupportedOperationException.class)
+ .isInstanceOf(RuntimeException.class)
.hasMessageContaining(
"Can not set changelog-producer on table without
primary keys");
@@ -323,7 +323,7 @@ public class SparkReadITCase extends SparkReadTestBase {
spark.sql(
"ALTER TABLE T SET
TBLPROPERTIES('changelog-producer' 'input')"))
.rootCause()
- .isInstanceOf(UnsupportedOperationException.class)
+ .isInstanceOf(RuntimeException.class)
.hasMessageContaining(
"Can not set changelog-producer on table without
primary keys");
}