From 9dcfb77920676b20bd8f54769e2520523a333987 Mon Sep 17 00:00:00 2001 From: yantian Date: Tue, 14 Jan 2025 14:51:28 +0800 Subject: [PATCH 01/11] support get view --- .../org/apache/paimon/rest/RESTCatalog.java | 46 ++++++++++ .../org/apache/paimon/rest/ResourcePaths.java | 4 + .../rest/responses/GetViewResponse.java | 69 ++++++++++++++ .../paimon/rest/responses/ViewSchema.java | 90 +++++++++++++++++++ .../java/org/apache/paimon/view/ViewImpl.java | 3 + 5 files changed, 212 insertions(+) create mode 100644 paimon-core/src/main/java/org/apache/paimon/rest/responses/GetViewResponse.java create mode 100644 paimon-core/src/main/java/org/apache/paimon/rest/responses/ViewSchema.java 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 4b4e90fb7c20..c14f5dba41ab 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 @@ -54,6 +54,7 @@ import org.apache.paimon.rest.responses.ErrorResponseResourceType; import org.apache.paimon.rest.responses.GetDatabaseResponse; import org.apache.paimon.rest.responses.GetTableResponse; +import org.apache.paimon.rest.responses.GetViewResponse; import org.apache.paimon.rest.responses.ListDatabasesResponse; import org.apache.paimon.rest.responses.ListPartitionsResponse; import org.apache.paimon.rest.responses.ListTablesResponse; @@ -68,6 +69,8 @@ import org.apache.paimon.table.sink.BatchWriteBuilder; import org.apache.paimon.utils.Pair; import org.apache.paimon.utils.Preconditions; +import org.apache.paimon.view.View; +import org.apache.paimon.view.ViewImpl; import org.apache.paimon.shade.guava30.com.google.common.collect.ImmutableList; @@ -505,6 +508,49 @@ public List listPartitions(Identifier identifier) throws TableNotExis return response.getPartitions(); } + @Override + public View getView(Identifier identifier) throws ViewNotExistException { + try { + GetViewResponse response = + client.get( + resourcePaths.view( + identifier.getDatabaseName(), identifier.getTableName()), + GetViewResponse.class, + headers()); + return new ViewImpl( + identifier, + response.getSchema().rowType(), + response.getSchema().query(), + response.getSchema().comment(), + response.getSchema().options()); + } catch (NoSuchResourceException e) { + throw new ViewNotExistException(identifier); + } + } + + @Override + public void dropView(Identifier identifier, boolean ignoreIfNotExists) + throws ViewNotExistException { + throw new UnsupportedOperationException(); + } + + @Override + public void createView(Identifier identifier, View view, boolean ignoreIfExists) + throws ViewAlreadyExistException, DatabaseNotExistException { + throw new UnsupportedOperationException(); + } + + @Override + public List listViews(String databaseName) throws DatabaseNotExistException { + throw new UnsupportedOperationException(); + } + + @Override + public void renameView(Identifier fromView, Identifier toView, boolean ignoreIfNotExists) + throws ViewNotExistException, ViewAlreadyExistException { + throw new UnsupportedOperationException(); + } + @Override public boolean caseSensitive() { return options.getOptional(CASE_SENSITIVE).orElse(true); diff --git a/paimon-core/src/main/java/org/apache/paimon/rest/ResourcePaths.java b/paimon-core/src/main/java/org/apache/paimon/rest/ResourcePaths.java index 7b092196626f..bd4967319495 100644 --- a/paimon-core/src/main/java/org/apache/paimon/rest/ResourcePaths.java +++ b/paimon-core/src/main/java/org/apache/paimon/rest/ResourcePaths.java @@ -84,4 +84,8 @@ public String markDonePartitions(String databaseName, String tableName) { return SLASH.join( V1, prefix, DATABASES, databaseName, TABLES, tableName, "partitions", "mark"); } + + public String view(String databaseName, String viewName) { + return SLASH.join(V1, prefix, DATABASES, databaseName, "views", viewName); + } } 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 new file mode 100644 index 000000000000..4c578beeb845 --- /dev/null +++ b/paimon-core/src/main/java/org/apache/paimon/rest/responses/GetViewResponse.java @@ -0,0 +1,69 @@ +/* + * 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.JsonIgnoreProperties; +import org.apache.paimon.shade.jackson2.com.fasterxml.jackson.annotation.JsonProperty; + +/** Response for getting view. */ +@JsonIgnoreProperties(ignoreUnknown = true) +public class GetViewResponse implements RESTResponse { + + private static final String FIELD_ID = "id"; + private static final String FIELD_NAME = "name"; + private static final String FIELD_SCHEMA = "schema"; + + @JsonProperty(FIELD_ID) + private final String id; + + @JsonProperty(FIELD_NAME) + private final String name; + + @JsonProperty(FIELD_SCHEMA) + private final ViewSchema schema; + + @JsonCreator + public GetViewResponse( + @JsonProperty(FIELD_ID) String id, + @JsonProperty(FIELD_NAME) String name, + @JsonProperty(FIELD_SCHEMA) ViewSchema schema) { + this.id = id; + this.name = name; + this.schema = schema; + } + + @JsonGetter(FIELD_ID) + public String getId() { + return this.id; + } + + @JsonGetter(FIELD_NAME) + public String getName() { + return this.name; + } + + @JsonGetter(FIELD_SCHEMA) + public ViewSchema getSchema() { + return this.schema; + } +} diff --git a/paimon-core/src/main/java/org/apache/paimon/rest/responses/ViewSchema.java b/paimon-core/src/main/java/org/apache/paimon/rest/responses/ViewSchema.java new file mode 100644 index 000000000000..cf43b93b223e --- /dev/null +++ b/paimon-core/src/main/java/org/apache/paimon/rest/responses/ViewSchema.java @@ -0,0 +1,90 @@ +/* + * 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.types.DataField; +import org.apache.paimon.types.RowType; + +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.JsonInclude; +import org.apache.paimon.shade.jackson2.com.fasterxml.jackson.annotation.JsonProperty; + +import javax.annotation.Nullable; + +import java.util.List; +import java.util.Map; + +public class ViewSchema { + private static final String FIELD_FIELDS = "fields"; + private static final String FIELD_OPTIONS = "options"; + private static final String FIELD_COMMENT = "comment"; + private static final String FIELD_QUERY = "query"; + + @JsonProperty(FIELD_QUERY) + private final String query; + + @Nullable + @JsonProperty(FIELD_COMMENT) + @JsonInclude(JsonInclude.Include.NON_NULL) + private final String comment; + + @JsonProperty(FIELD_OPTIONS) + private final Map options; + + @JsonProperty(FIELD_FIELDS) + private final List fields; + + @JsonCreator + public ViewSchema( + @JsonProperty(FIELD_FIELDS) List fields, + @JsonProperty(FIELD_OPTIONS) Map options, + @Nullable @JsonProperty(FIELD_COMMENT) String comment, + @JsonProperty(FIELD_QUERY) String query) { + this.fields = fields; + this.options = options; + this.comment = comment; + this.query = query; + } + + public RowType rowType() { + return new RowType(fields); + } + + @JsonGetter(FIELD_QUERY) + public String query() { + return query; + } + + @Nullable + @JsonGetter(FIELD_COMMENT) + public String comment() { + return comment; + } + + @JsonGetter(FIELD_OPTIONS) + public Map options() { + return options; + } + + @JsonGetter(FIELD_FIELDS) + public List fields() { + return fields; + } +} diff --git a/paimon-core/src/main/java/org/apache/paimon/view/ViewImpl.java b/paimon-core/src/main/java/org/apache/paimon/view/ViewImpl.java index 1cd48d4ce445..69226ff1fcd6 100644 --- a/paimon-core/src/main/java/org/apache/paimon/view/ViewImpl.java +++ b/paimon-core/src/main/java/org/apache/paimon/view/ViewImpl.java @@ -21,6 +21,8 @@ import org.apache.paimon.catalog.Identifier; import org.apache.paimon.types.RowType; +import org.apache.paimon.shade.jackson2.com.fasterxml.jackson.annotation.JsonIgnoreProperties; + import javax.annotation.Nullable; import java.util.HashMap; @@ -29,6 +31,7 @@ import java.util.Optional; /** Implementation of {@link View}. */ +@JsonIgnoreProperties(ignoreUnknown = true) public class ViewImpl implements View { private final Identifier identifier; From c327e6ef4565d53dd569cd7895af94d3a0a4cada Mon Sep 17 00:00:00 2001 From: yantian Date: Tue, 14 Jan 2025 15:14:52 +0800 Subject: [PATCH 02/11] support other view api in RESTCatalog --- .../org/apache/paimon/rest/RESTCatalog.java | 60 +++++++++++++++++-- .../org/apache/paimon/rest/ResourcePaths.java | 8 +++ .../rest/{responses => }/ViewSchema.java | 4 +- .../rest/requests/CreateViewRequest.java | 60 +++++++++++++++++++ ...meTableRequest.java => RenameRequest.java} | 6 +- .../rest/responses/GetViewResponse.java | 1 + .../rest/responses/ListViewsResponse.java | 48 +++++++++++++++ .../apache/paimon/rest/MockRESTMessage.java | 6 +- .../apache/paimon/rest/RESTCatalogServer.java | 6 +- .../paimon/rest/RESTObjectMapperTest.java | 7 +-- .../open/api/RESTCatalogController.java | 4 +- 11 files changed, 189 insertions(+), 21 deletions(-) rename paimon-core/src/main/java/org/apache/paimon/rest/{responses => }/ViewSchema.java (94%) create mode 100644 paimon-core/src/main/java/org/apache/paimon/rest/requests/CreateViewRequest.java rename paimon-core/src/main/java/org/apache/paimon/rest/requests/{RenameTableRequest.java => RenameRequest.java} (89%) create mode 100644 paimon-core/src/main/java/org/apache/paimon/rest/responses/ListViewsResponse.java 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 c14f5dba41ab..4a9894357880 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 @@ -45,9 +45,10 @@ import org.apache.paimon.rest.requests.CreateDatabaseRequest; import org.apache.paimon.rest.requests.CreatePartitionsRequest; import org.apache.paimon.rest.requests.CreateTableRequest; +import org.apache.paimon.rest.requests.CreateViewRequest; import org.apache.paimon.rest.requests.DropPartitionsRequest; import org.apache.paimon.rest.requests.MarkDonePartitionsRequest; -import org.apache.paimon.rest.requests.RenameTableRequest; +import org.apache.paimon.rest.requests.RenameRequest; import org.apache.paimon.rest.responses.AlterDatabaseResponse; import org.apache.paimon.rest.responses.ConfigResponse; import org.apache.paimon.rest.responses.CreateDatabaseResponse; @@ -58,6 +59,7 @@ import org.apache.paimon.rest.responses.ListDatabasesResponse; import org.apache.paimon.rest.responses.ListPartitionsResponse; import org.apache.paimon.rest.responses.ListTablesResponse; +import org.apache.paimon.rest.responses.ListViewsResponse; import org.apache.paimon.schema.Schema; import org.apache.paimon.schema.SchemaChange; import org.apache.paimon.schema.TableSchema; @@ -329,7 +331,7 @@ public void renameTable(Identifier fromTable, Identifier toTable, boolean ignore checkNotSystemTable(fromTable, "renameTable"); checkNotSystemTable(toTable, "renameTable"); try { - RenameTableRequest request = new RenameTableRequest(toTable); + RenameRequest request = new RenameRequest(toTable); client.post( resourcePaths.renameTable( fromTable.getDatabaseName(), fromTable.getTableName()), @@ -531,23 +533,71 @@ public View getView(Identifier identifier) throws ViewNotExistException { @Override public void dropView(Identifier identifier, boolean ignoreIfNotExists) throws ViewNotExistException { - throw new UnsupportedOperationException(); + try { + client.delete( + resourcePaths.view(identifier.getDatabaseName(), identifier.getTableName()), + headers()); + } catch (NoSuchResourceException e) { + if (!ignoreIfNotExists) { + throw new ViewNotExistException(identifier); + } + } } @Override public void createView(Identifier identifier, View view, boolean ignoreIfExists) throws ViewAlreadyExistException, DatabaseNotExistException { - throw new UnsupportedOperationException(); + try { + ViewSchema schema = + new ViewSchema( + view.rowType().getFields(), + view.options(), + view.comment().orElse(null), + view.query()); + CreateViewRequest request = new CreateViewRequest(identifier, schema); + client.post( + resourcePaths.views(identifier.getDatabaseName()), + request, + GetViewResponse.class, + headers()); + } catch (NoSuchResourceException e) { + throw new DatabaseNotExistException(identifier.getDatabaseName()); + } catch (AlreadyExistsException e) { + if (!ignoreIfExists) { + throw new ViewAlreadyExistException(identifier); + } + } } @Override public List listViews(String databaseName) throws DatabaseNotExistException { - throw new UnsupportedOperationException(); + try { + ListViewsResponse response = + client.get( + resourcePaths.views(databaseName), ListViewsResponse.class, headers()); + return response.getViews(); + } catch (NoSuchResourceException e) { + throw new DatabaseNotExistException(databaseName); + } } @Override public void renameView(Identifier fromView, Identifier toView, boolean ignoreIfNotExists) throws ViewNotExistException, ViewAlreadyExistException { + try { + RenameRequest request = new RenameRequest(toView); + client.post( + resourcePaths.renameView(fromView.getDatabaseName(), fromView.getTableName()), + request, + GetViewResponse.class, + headers()); + } catch (NoSuchResourceException e) { + if (!ignoreIfNotExists) { + throw new ViewNotExistException(fromView); + } + } catch (AlreadyExistsException e) { + throw new ViewAlreadyExistException(toView); + } throw new UnsupportedOperationException(); } diff --git a/paimon-core/src/main/java/org/apache/paimon/rest/ResourcePaths.java b/paimon-core/src/main/java/org/apache/paimon/rest/ResourcePaths.java index bd4967319495..ad078d409ac8 100644 --- a/paimon-core/src/main/java/org/apache/paimon/rest/ResourcePaths.java +++ b/paimon-core/src/main/java/org/apache/paimon/rest/ResourcePaths.java @@ -85,7 +85,15 @@ public String markDonePartitions(String databaseName, String tableName) { V1, prefix, DATABASES, databaseName, TABLES, tableName, "partitions", "mark"); } + public String views(String databaseName) { + return SLASH.join(V1, prefix, DATABASES, databaseName, "views"); + } + public String view(String databaseName, String viewName) { return SLASH.join(V1, prefix, DATABASES, databaseName, "views", viewName); } + + public String renameView(String databaseName, String viewName) { + return SLASH.join(V1, prefix, DATABASES, databaseName, "views", viewName, "rename"); + } } diff --git a/paimon-core/src/main/java/org/apache/paimon/rest/responses/ViewSchema.java b/paimon-core/src/main/java/org/apache/paimon/rest/ViewSchema.java similarity index 94% rename from paimon-core/src/main/java/org/apache/paimon/rest/responses/ViewSchema.java rename to paimon-core/src/main/java/org/apache/paimon/rest/ViewSchema.java index cf43b93b223e..af074234763f 100644 --- a/paimon-core/src/main/java/org/apache/paimon/rest/responses/ViewSchema.java +++ b/paimon-core/src/main/java/org/apache/paimon/rest/ViewSchema.java @@ -16,13 +16,14 @@ * limitations under the License. */ -package org.apache.paimon.rest.responses; +package org.apache.paimon.rest; import org.apache.paimon.types.DataField; import org.apache.paimon.types.RowType; 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.JsonIgnoreProperties; import org.apache.paimon.shade.jackson2.com.fasterxml.jackson.annotation.JsonInclude; import org.apache.paimon.shade.jackson2.com.fasterxml.jackson.annotation.JsonProperty; @@ -31,6 +32,7 @@ import java.util.List; import java.util.Map; +@JsonIgnoreProperties(ignoreUnknown = true) public class ViewSchema { private static final String FIELD_FIELDS = "fields"; private static final String FIELD_OPTIONS = "options"; diff --git a/paimon-core/src/main/java/org/apache/paimon/rest/requests/CreateViewRequest.java b/paimon-core/src/main/java/org/apache/paimon/rest/requests/CreateViewRequest.java new file mode 100644 index 000000000000..8eddda7453ca --- /dev/null +++ b/paimon-core/src/main/java/org/apache/paimon/rest/requests/CreateViewRequest.java @@ -0,0 +1,60 @@ +/* + * 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.requests; + +import org.apache.paimon.catalog.Identifier; +import org.apache.paimon.rest.RESTRequest; +import org.apache.paimon.rest.ViewSchema; + +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.JsonIgnoreProperties; +import org.apache.paimon.shade.jackson2.com.fasterxml.jackson.annotation.JsonProperty; + +/** Request for creating view. */ +@JsonIgnoreProperties(ignoreUnknown = true) +public class CreateViewRequest implements RESTRequest { + + private static final String FIELD_IDENTIFIER = "identifier"; + private static final String FIELD_SCHEMA = "schema"; + + @JsonProperty(FIELD_IDENTIFIER) + private final Identifier identifier; + + @JsonProperty(FIELD_SCHEMA) + private final ViewSchema schema; + + @JsonCreator + public CreateViewRequest( + @JsonProperty(FIELD_IDENTIFIER) Identifier identifier, + @JsonProperty(FIELD_SCHEMA) ViewSchema schema) { + this.schema = schema; + this.identifier = identifier; + } + + @JsonGetter(FIELD_IDENTIFIER) + public Identifier getIdentifier() { + return identifier; + } + + @JsonGetter(FIELD_SCHEMA) + public ViewSchema getSchema() { + return schema; + } +} diff --git a/paimon-core/src/main/java/org/apache/paimon/rest/requests/RenameTableRequest.java b/paimon-core/src/main/java/org/apache/paimon/rest/requests/RenameRequest.java similarity index 89% rename from paimon-core/src/main/java/org/apache/paimon/rest/requests/RenameTableRequest.java rename to paimon-core/src/main/java/org/apache/paimon/rest/requests/RenameRequest.java index fd2eb4f9518b..610ee81549fc 100644 --- a/paimon-core/src/main/java/org/apache/paimon/rest/requests/RenameTableRequest.java +++ b/paimon-core/src/main/java/org/apache/paimon/rest/requests/RenameRequest.java @@ -26,9 +26,9 @@ import org.apache.paimon.shade.jackson2.com.fasterxml.jackson.annotation.JsonIgnoreProperties; import org.apache.paimon.shade.jackson2.com.fasterxml.jackson.annotation.JsonProperty; -/** Request for renaming table. */ +/** Request for renaming. */ @JsonIgnoreProperties(ignoreUnknown = true) -public class RenameTableRequest implements RESTRequest { +public class RenameRequest implements RESTRequest { private static final String FIELD_NEW_IDENTIFIER_NAME = "newIdentifier"; @@ -36,7 +36,7 @@ public class RenameTableRequest implements RESTRequest { private final Identifier newIdentifier; @JsonCreator - public RenameTableRequest(@JsonProperty(FIELD_NEW_IDENTIFIER_NAME) Identifier newIdentifier) { + public RenameRequest(@JsonProperty(FIELD_NEW_IDENTIFIER_NAME) Identifier newIdentifier) { this.newIdentifier = newIdentifier; } 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 4c578beeb845..e31d2e82ad5b 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 @@ -19,6 +19,7 @@ package org.apache.paimon.rest.responses; import org.apache.paimon.rest.RESTResponse; +import org.apache.paimon.rest.ViewSchema; import org.apache.paimon.shade.jackson2.com.fasterxml.jackson.annotation.JsonCreator; import org.apache.paimon.shade.jackson2.com.fasterxml.jackson.annotation.JsonGetter; diff --git a/paimon-core/src/main/java/org/apache/paimon/rest/responses/ListViewsResponse.java b/paimon-core/src/main/java/org/apache/paimon/rest/responses/ListViewsResponse.java new file mode 100644 index 000000000000..d785fd68c2b8 --- /dev/null +++ b/paimon-core/src/main/java/org/apache/paimon/rest/responses/ListViewsResponse.java @@ -0,0 +1,48 @@ +/* + * 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.JsonIgnoreProperties; +import org.apache.paimon.shade.jackson2.com.fasterxml.jackson.annotation.JsonProperty; + +import java.util.List; + +/** Response for listing tables. */ +@JsonIgnoreProperties(ignoreUnknown = true) +public class ListViewsResponse implements RESTResponse { + + private static final String FIELD_VIEWS = "views"; + + @JsonProperty(FIELD_VIEWS) + private final List views; + + @JsonCreator + public ListViewsResponse(@JsonProperty(FIELD_VIEWS) List views) { + this.views = views; + } + + @JsonGetter(FIELD_VIEWS) + public List getViews() { + return this.views; + } +} 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 822e06d7fbbc..2ea5fc98cba2 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 @@ -27,7 +27,7 @@ import org.apache.paimon.rest.requests.CreatePartitionsRequest; import org.apache.paimon.rest.requests.CreateTableRequest; import org.apache.paimon.rest.requests.DropPartitionsRequest; -import org.apache.paimon.rest.requests.RenameTableRequest; +import org.apache.paimon.rest.requests.RenameRequest; import org.apache.paimon.rest.responses.AlterDatabaseResponse; import org.apache.paimon.rest.responses.CreateDatabaseResponse; import org.apache.paimon.rest.responses.GetDatabaseResponse; @@ -124,9 +124,9 @@ public static CreateTableRequest createTableRequest(String name) { return new CreateTableRequest(identifier, schema); } - public static RenameTableRequest renameRequest(String toTableName) { + public static RenameRequest renameRequest(String toTableName) { Identifier newIdentifier = Identifier.create(databaseName(), toTableName); - return new RenameTableRequest(newIdentifier); + return new RenameRequest(newIdentifier); } public static AlterTableRequest alterTableRequest() { 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 1c802cf4c0ef..7402bc5e0efe 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 @@ -32,7 +32,7 @@ import org.apache.paimon.rest.requests.CreateTableRequest; import org.apache.paimon.rest.requests.DropPartitionsRequest; import org.apache.paimon.rest.requests.MarkDonePartitionsRequest; -import org.apache.paimon.rest.requests.RenameTableRequest; +import org.apache.paimon.rest.requests.RenameRequest; import org.apache.paimon.rest.responses.CreateDatabaseResponse; import org.apache.paimon.rest.responses.ErrorResponse; import org.apache.paimon.rest.responses.ErrorResponseResourceType; @@ -259,8 +259,8 @@ public MockResponse dispatch(RecordedRequest request) { private static MockResponse renameTableApiHandler( Catalog catalog, RecordedRequest request, String databaseName, String tableName) throws Exception { - RenameTableRequest requestBody = - OBJECT_MAPPER.readValue(request.getBody().readUtf8(), RenameTableRequest.class); + RenameRequest requestBody = + OBJECT_MAPPER.readValue(request.getBody().readUtf8(), RenameRequest.class); catalog.renameTable( Identifier.create(databaseName, tableName), requestBody.getNewIdentifier(), false); GetTableResponse response = diff --git a/paimon-core/src/test/java/org/apache/paimon/rest/RESTObjectMapperTest.java b/paimon-core/src/test/java/org/apache/paimon/rest/RESTObjectMapperTest.java index 6e260a5e7341..9850d8d41438 100644 --- a/paimon-core/src/test/java/org/apache/paimon/rest/RESTObjectMapperTest.java +++ b/paimon-core/src/test/java/org/apache/paimon/rest/RESTObjectMapperTest.java @@ -25,7 +25,7 @@ import org.apache.paimon.rest.requests.CreatePartitionsRequest; import org.apache.paimon.rest.requests.CreateTableRequest; import org.apache.paimon.rest.requests.DropPartitionsRequest; -import org.apache.paimon.rest.requests.RenameTableRequest; +import org.apache.paimon.rest.requests.RenameRequest; import org.apache.paimon.rest.responses.AlterDatabaseResponse; import org.apache.paimon.rest.responses.ConfigResponse; import org.apache.paimon.rest.responses.CreateDatabaseResponse; @@ -172,10 +172,9 @@ public void dataFieldParseTest() throws Exception { @Test public void renameTableRequestParseTest() throws Exception { - RenameTableRequest request = MockRESTMessage.renameRequest("t2"); + RenameRequest request = MockRESTMessage.renameRequest("t2"); String requestStr = OBJECT_MAPPER.writeValueAsString(request); - RenameTableRequest parseData = - OBJECT_MAPPER.readValue(requestStr, RenameTableRequest.class); + RenameRequest parseData = OBJECT_MAPPER.readValue(requestStr, RenameRequest.class); assertEquals(request.getNewIdentifier(), parseData.getNewIdentifier()); } 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 a9f3d02f5442..f8821f68aa71 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 @@ -28,7 +28,7 @@ import org.apache.paimon.rest.requests.CreateTableRequest; import org.apache.paimon.rest.requests.DropPartitionsRequest; import org.apache.paimon.rest.requests.MarkDonePartitionsRequest; -import org.apache.paimon.rest.requests.RenameTableRequest; +import org.apache.paimon.rest.requests.RenameRequest; import org.apache.paimon.rest.responses.AlterDatabaseResponse; import org.apache.paimon.rest.responses.ConfigResponse; import org.apache.paimon.rest.responses.CreateDatabaseResponse; @@ -347,7 +347,7 @@ public GetTableResponse renameTable( @PathVariable String prefix, @PathVariable String database, @PathVariable String table, - @RequestBody RenameTableRequest request) { + @RequestBody RenameRequest request) { return new GetTableResponse( UUID.randomUUID().toString(), "", From 0901821d7634accf6998e3bc88836c0e21707987 Mon Sep 17 00:00:00 2001 From: yantian Date: Tue, 14 Jan 2025 16:36:49 +0800 Subject: [PATCH 03/11] add test for view api --- .../org/apache/paimon/rest/RESTCatalog.java | 1 - .../org/apache/paimon/rest/ViewSchema.java | 16 +++ .../responses/ErrorResponseResourceType.java | 1 + .../paimon/catalog/CatalogTestBase.java | 1 - .../apache/paimon/rest/MockRESTMessage.java | 25 ++++ .../apache/paimon/rest/RESTCatalogServer.java | 111 +++++++++++++++++- .../apache/paimon/rest/RESTCatalogTest.java | 5 + .../paimon/rest/RESTObjectMapperTest.java | 30 +++++ .../apache/paimon/rest/TestRESTCatalog.java | 57 +++++++++ 9 files changed, 243 insertions(+), 4 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 4a9894357880..8e701929f07d 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 @@ -598,7 +598,6 @@ public void renameView(Identifier fromView, Identifier toView, boolean ignoreIfN } catch (AlreadyExistsException e) { throw new ViewAlreadyExistException(toView); } - throw new UnsupportedOperationException(); } @Override diff --git a/paimon-core/src/main/java/org/apache/paimon/rest/ViewSchema.java b/paimon-core/src/main/java/org/apache/paimon/rest/ViewSchema.java index af074234763f..ad70015d60df 100644 --- a/paimon-core/src/main/java/org/apache/paimon/rest/ViewSchema.java +++ b/paimon-core/src/main/java/org/apache/paimon/rest/ViewSchema.java @@ -31,6 +31,7 @@ import java.util.List; import java.util.Map; +import java.util.Objects; @JsonIgnoreProperties(ignoreUnknown = true) public class ViewSchema { @@ -89,4 +90,19 @@ public Map options() { public List fields() { return fields; } + + @Override + public boolean equals(Object o) { + if (o == null || getClass() != o.getClass()) return false; + ViewSchema that = (ViewSchema) o; + return Objects.equals(query, that.query) + && Objects.equals(comment, that.comment) + && Objects.equals(options, that.options) + && Objects.equals(fields, that.fields); + } + + @Override + public int hashCode() { + return Objects.hash(query, comment, options, fields); + } } diff --git a/paimon-core/src/main/java/org/apache/paimon/rest/responses/ErrorResponseResourceType.java b/paimon-core/src/main/java/org/apache/paimon/rest/responses/ErrorResponseResourceType.java index 590f38e720d4..5dc6cffade1a 100644 --- a/paimon-core/src/main/java/org/apache/paimon/rest/responses/ErrorResponseResourceType.java +++ b/paimon-core/src/main/java/org/apache/paimon/rest/responses/ErrorResponseResourceType.java @@ -23,4 +23,5 @@ public enum ErrorResponseResourceType { DATABASE, TABLE, COLUMN, + VIEW } 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 c8b9192c1c38..644c47470260 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 @@ -970,7 +970,6 @@ public void testView() throws Exception { .isInstanceOf(Catalog.ViewNotExistException.class); catalog.renameView(identifier, newIdentifier, false); - catalog.dropView(newIdentifier, false); catalog.dropView(newIdentifier, true); assertThatThrownBy(() -> catalog.dropView(newIdentifier, false)) .isInstanceOf(Catalog.ViewNotExistException.class); 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 2ea5fc98cba2..15e1f2a903f4 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 @@ -26,15 +26,18 @@ import org.apache.paimon.rest.requests.CreateDatabaseRequest; import org.apache.paimon.rest.requests.CreatePartitionsRequest; import org.apache.paimon.rest.requests.CreateTableRequest; +import org.apache.paimon.rest.requests.CreateViewRequest; import org.apache.paimon.rest.requests.DropPartitionsRequest; import org.apache.paimon.rest.requests.RenameRequest; import org.apache.paimon.rest.responses.AlterDatabaseResponse; import org.apache.paimon.rest.responses.CreateDatabaseResponse; import org.apache.paimon.rest.responses.GetDatabaseResponse; import org.apache.paimon.rest.responses.GetTableResponse; +import org.apache.paimon.rest.responses.GetViewResponse; import org.apache.paimon.rest.responses.ListDatabasesResponse; import org.apache.paimon.rest.responses.ListPartitionsResponse; import org.apache.paimon.rest.responses.ListTablesResponse; +import org.apache.paimon.rest.responses.ListViewsResponse; import org.apache.paimon.schema.Schema; import org.apache.paimon.schema.SchemaChange; import org.apache.paimon.types.DataField; @@ -230,6 +233,28 @@ public static AlterPartitionsRequest alterPartitionsRequest() { return new AlterPartitionsRequest(ImmutableList.of(partition())); } + public static CreateViewRequest createViewRequest(String name) { + Identifier identifier = Identifier.create(databaseName(), name); + return new CreateViewRequest(identifier, viewSchema()); + } + + public static GetViewResponse getViewResponse() { + return new GetViewResponse(UUID.randomUUID().toString(), "", viewSchema()); + } + + public static ListViewsResponse listViewsResponse() { + return new ListViewsResponse(ImmutableList.of("view")); + } + + private static ViewSchema viewSchema() { + List fields = + Arrays.asList( + new DataField(0, "f0", new IntType()), + new DataField(1, "f1", new IntType())); + return new ViewSchema( + fields, Collections.singletonMap("pt", "1"), "comment", "select * from t1"); + } + private static Partition partition() { return new Partition(Collections.singletonMap("pt", "1"), 1, 1, 1, 1); } 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 7402bc5e0efe..55d45d11a942 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 @@ -30,6 +30,7 @@ import org.apache.paimon.rest.requests.CreateDatabaseRequest; import org.apache.paimon.rest.requests.CreatePartitionsRequest; import org.apache.paimon.rest.requests.CreateTableRequest; +import org.apache.paimon.rest.requests.CreateViewRequest; import org.apache.paimon.rest.requests.DropPartitionsRequest; import org.apache.paimon.rest.requests.MarkDonePartitionsRequest; import org.apache.paimon.rest.requests.RenameRequest; @@ -38,14 +39,18 @@ import org.apache.paimon.rest.responses.ErrorResponseResourceType; import org.apache.paimon.rest.responses.GetDatabaseResponse; import org.apache.paimon.rest.responses.GetTableResponse; +import org.apache.paimon.rest.responses.GetViewResponse; import org.apache.paimon.rest.responses.ListDatabasesResponse; import org.apache.paimon.rest.responses.ListPartitionsResponse; import org.apache.paimon.rest.responses.ListTablesResponse; +import org.apache.paimon.rest.responses.ListViewsResponse; import org.apache.paimon.schema.Schema; import org.apache.paimon.table.FileStoreTable; import org.apache.paimon.table.FormatTable; import org.apache.paimon.table.Table; import org.apache.paimon.types.DataField; +import org.apache.paimon.view.View; +import org.apache.paimon.view.ViewImpl; import org.apache.paimon.shade.jackson2.com.fasterxml.jackson.core.JsonProcessingException; @@ -116,10 +121,18 @@ public MockResponse dispatch(RecordedRequest request) { .substring((DATABASE_URI + "/").length()) .split("/"); String databaseName = resources[0]; + boolean isViews = resources.length == 2 && "views".equals(resources[1]); + boolean isView = resources.length == 3 && "views".equals(resources[1]); + boolean isViewRename = + resources.length == 4 + && "views".equals(resources[1]) + && "rename".equals(resources[3]); boolean isTables = resources.length == 2 && "tables".equals(resources[1]); boolean isTable = resources.length == 3 && "tables".equals(resources[1]); boolean isTableRename = - resources.length == 4 && "rename".equals(resources[3]); + resources.length == 4 + && "tables".equals(resources[1]) + && "rename".equals(resources[3]); boolean isPartitions = resources.length == 4 && "tables".equals(resources[1]) @@ -181,6 +194,14 @@ public MockResponse dispatch(RecordedRequest request) { return tableApiHandler(catalog, request, databaseName, tableName); } else if (isTables) { return tablesApiHandler(catalog, request, databaseName); + } else if (isViews) { + return viewsApiHandler(catalog, request, databaseName); + } else if (isView) { + String viewName = resources[2]; + return viewApiHandler(catalog, request, databaseName, viewName); + } else if (isViewRename) { + String viewName = resources[2]; + return renameViewApiHandler(catalog, request, databaseName, viewName); } else { return databaseApiHandler(catalog, request, databaseName); } @@ -234,6 +255,22 @@ public MockResponse dispatch(RecordedRequest request) { e.getMessage(), 409); return mockResponse(response, 409); + } catch (Catalog.ViewNotExistException e) { + response = + new ErrorResponse( + ErrorResponseResourceType.VIEW, + e.identifier().getTableName(), + e.getMessage(), + 404); + return mockResponse(response, 404); + } catch (Catalog.ViewAlreadyExistException e) { + response = + new ErrorResponse( + ErrorResponseResourceType.VIEW, + e.identifier().getTableName(), + e.getMessage(), + 409); + return mockResponse(response, 409); } catch (IllegalArgumentException e) { response = new ErrorResponse(null, null, e.getMessage(), 400); return mockResponse(response, 400); @@ -310,12 +347,82 @@ private static MockResponse databaseApiHandler( } } + private static MockResponse viewsApiHandler( + Catalog catalog, RecordedRequest request, String databaseName) throws Exception { + RESTResponse response; + switch (request.getMethod()) { + case "GET": + response = new ListViewsResponse(catalog.listViews(databaseName)); + return mockResponse(response, 200); + case "POST": + CreateViewRequest requestBody = + OBJECT_MAPPER.readValue( + request.getBody().readUtf8(), CreateViewRequest.class); + ViewImpl view = + new ViewImpl( + requestBody.getIdentifier(), + requestBody.getSchema().rowType(), + requestBody.getSchema().query(), + requestBody.getSchema().comment(), + requestBody.getSchema().options()); + catalog.createView(requestBody.getIdentifier(), view, false); + response = + new GetViewResponse( + UUID.randomUUID().toString(), "", requestBody.getSchema()); + return mockResponse(response, 200); + default: + return new MockResponse().setResponseCode(404); + } + } + + private static MockResponse viewApiHandler( + Catalog catalog, RecordedRequest request, String databaseName, String viewName) + throws Exception { + RESTResponse response; + Identifier identifier = Identifier.create(databaseName, viewName); + switch (request.getMethod()) { + case "GET": + View view = catalog.getView(identifier); + ViewSchema schema = + new ViewSchema( + view.rowType().getFields(), + view.options(), + view.comment().orElse(null), + view.query()); + response = new GetViewResponse("id", identifier.getTableName(), schema); + return mockResponse(response, 200); + case "DELETE": + catalog.dropView(identifier, false); + return new MockResponse().setResponseCode(200); + default: + return new MockResponse().setResponseCode(404); + } + } + + private static MockResponse renameViewApiHandler( + Catalog catalog, RecordedRequest request, String databaseName, String viewName) + throws Exception { + RenameRequest requestBody = + OBJECT_MAPPER.readValue(request.getBody().readUtf8(), RenameRequest.class); + catalog.renameView( + Identifier.create(databaseName, viewName), requestBody.getNewIdentifier(), false); + Identifier identifier = requestBody.getNewIdentifier(); + View view = catalog.getView(identifier); + ViewSchema schema = + new ViewSchema( + view.rowType().getFields(), + view.options(), + view.comment().orElse(null), + view.query()); + GetViewResponse response = new GetViewResponse("id", identifier.getTableName(), schema); + return mockResponse(response, 200); + } + private static MockResponse tablesApiHandler( Catalog catalog, RecordedRequest request, String databaseName) throws Exception { RESTResponse response; switch (request.getMethod()) { case "GET": - catalog.listTables(databaseName); response = new ListTablesResponse(catalog.listTables(databaseName)); return mockResponse(response, 200); case "POST": diff --git a/paimon-core/src/test/java/org/apache/paimon/rest/RESTCatalogTest.java b/paimon-core/src/test/java/org/apache/paimon/rest/RESTCatalogTest.java index f63d70332650..d1ce64b6c543 100644 --- a/paimon-core/src/test/java/org/apache/paimon/rest/RESTCatalogTest.java +++ b/paimon-core/src/test/java/org/apache/paimon/rest/RESTCatalogTest.java @@ -117,6 +117,11 @@ protected boolean supportPartitions() { return true; } + @Override + protected boolean supportsView() { + return true; + } + private void createTable( Identifier identifier, Map options, List partitionKeys) throws Exception { diff --git a/paimon-core/src/test/java/org/apache/paimon/rest/RESTObjectMapperTest.java b/paimon-core/src/test/java/org/apache/paimon/rest/RESTObjectMapperTest.java index 9850d8d41438..79f02816f97e 100644 --- a/paimon-core/src/test/java/org/apache/paimon/rest/RESTObjectMapperTest.java +++ b/paimon-core/src/test/java/org/apache/paimon/rest/RESTObjectMapperTest.java @@ -24,6 +24,7 @@ import org.apache.paimon.rest.requests.CreateDatabaseRequest; import org.apache.paimon.rest.requests.CreatePartitionsRequest; import org.apache.paimon.rest.requests.CreateTableRequest; +import org.apache.paimon.rest.requests.CreateViewRequest; import org.apache.paimon.rest.requests.DropPartitionsRequest; import org.apache.paimon.rest.requests.RenameRequest; import org.apache.paimon.rest.responses.AlterDatabaseResponse; @@ -32,9 +33,11 @@ import org.apache.paimon.rest.responses.ErrorResponse; import org.apache.paimon.rest.responses.GetDatabaseResponse; import org.apache.paimon.rest.responses.GetTableResponse; +import org.apache.paimon.rest.responses.GetViewResponse; import org.apache.paimon.rest.responses.ListDatabasesResponse; import org.apache.paimon.rest.responses.ListPartitionsResponse; import org.apache.paimon.rest.responses.ListTablesResponse; +import org.apache.paimon.rest.responses.ListViewsResponse; import org.apache.paimon.types.DataField; import org.apache.paimon.types.DataTypes; import org.apache.paimon.types.IntType; @@ -241,4 +244,31 @@ public void alterPartitionsRequestParseTest() throws Exception { OBJECT_MAPPER.readValue(requestStr, AlterPartitionsRequest.class); assertEquals(request.getPartitions(), parseData.getPartitions()); } + + @Test + public void createViewRequestParseTest() throws Exception { + CreateViewRequest request = MockRESTMessage.createViewRequest("t1"); + String requestStr = OBJECT_MAPPER.writeValueAsString(request); + CreateViewRequest parseData = OBJECT_MAPPER.readValue(requestStr, CreateViewRequest.class); + assertEquals(request.getIdentifier(), parseData.getIdentifier()); + assertEquals(request.getSchema(), parseData.getSchema()); + } + + @Test + public void getViewResponseParseTest() throws Exception { + GetViewResponse response = MockRESTMessage.getViewResponse(); + String responseStr = OBJECT_MAPPER.writeValueAsString(response); + GetViewResponse parseData = OBJECT_MAPPER.readValue(responseStr, GetViewResponse.class); + assertEquals(response.getId(), parseData.getId()); + assertEquals(response.getName(), parseData.getName()); + assertEquals(response.getSchema(), parseData.getSchema()); + } + + @Test + public void listViewsResponseParseTest() throws Exception { + ListViewsResponse response = MockRESTMessage.listViewsResponse(); + String responseStr = OBJECT_MAPPER.writeValueAsString(response); + ListViewsResponse parseData = OBJECT_MAPPER.readValue(responseStr, ListViewsResponse.class); + assertEquals(response.getViews(), parseData.getViews()); + } } diff --git a/paimon-core/src/test/java/org/apache/paimon/rest/TestRESTCatalog.java b/paimon-core/src/test/java/org/apache/paimon/rest/TestRESTCatalog.java index a0f820e7ad0b..5e925b395a0b 100644 --- a/paimon-core/src/test/java/org/apache/paimon/rest/TestRESTCatalog.java +++ b/paimon-core/src/test/java/org/apache/paimon/rest/TestRESTCatalog.java @@ -31,6 +31,7 @@ import org.apache.paimon.schema.Schema; import org.apache.paimon.schema.SchemaChange; import org.apache.paimon.schema.TableSchema; +import org.apache.paimon.view.View; import java.io.IOException; import java.io.UncheckedIOException; @@ -45,6 +46,7 @@ public class TestRESTCatalog extends FileSystemCatalog { public Map tableFullName2Schema = new HashMap(); public Map> tableFullName2Partitions = new HashMap>(); + public final Map viewFullName2View = new HashMap(); public TestRESTCatalog(FileIO fileIO, Path warehouse, Options options) { super(fileIO, warehouse, options); @@ -129,6 +131,61 @@ public List listPartitions(Identifier identifier) throws TableNotExis return tableFullName2Partitions.get(identifier.getFullName()); } + @Override + public View getView(Identifier identifier) throws ViewNotExistException { + if (viewFullName2View.containsKey(identifier.getFullName())) { + return viewFullName2View.get(identifier.getFullName()); + } + throw new ViewNotExistException(identifier); + } + + @Override + public void dropView(Identifier identifier, boolean ignoreIfNotExists) + throws ViewNotExistException { + if (viewFullName2View.containsKey(identifier.getFullName())) { + viewFullName2View.remove(identifier.getFullName()); + } + if (!ignoreIfNotExists) { + throw new ViewNotExistException(identifier); + } + } + + @Override + public void createView(Identifier identifier, View view, boolean ignoreIfExists) + throws ViewAlreadyExistException, DatabaseNotExistException { + getDatabase(identifier.getDatabaseName()); + if (viewFullName2View.containsKey(identifier.getFullName()) && !ignoreIfExists) { + throw new ViewAlreadyExistException(identifier); + } + viewFullName2View.put(identifier.getFullName(), view); + } + + @Override + public List listViews(String databaseName) throws DatabaseNotExistException { + getDatabase(databaseName); + return viewFullName2View.keySet().stream() + .map(v -> Identifier.fromString(v)) + .filter(identifier -> identifier.getDatabaseName().equals(databaseName)) + .map(identifier -> identifier.getTableName()) + .collect(Collectors.toList()); + } + + @Override + public void renameView(Identifier fromView, Identifier toView, boolean ignoreIfNotExists) + throws ViewNotExistException, ViewAlreadyExistException { + if (!viewFullName2View.containsKey(fromView.getFullName()) && !ignoreIfNotExists) { + throw new ViewNotExistException(fromView); + } + if (viewFullName2View.containsKey(toView.getFullName())) { + throw new ViewAlreadyExistException(toView); + } + if (viewFullName2View.containsKey(fromView.getFullName())) { + View view = viewFullName2View.get(fromView.getFullName()); + viewFullName2View.remove(fromView.getFullName()); + viewFullName2View.put(toView.getFullName(), view); + } + } + @Override protected List listTablesImpl(String databaseName) { List tables = super.listTablesImpl(databaseName); From 9801f0345d3772bd25b8090d7a2fde30b7332f11 Mon Sep 17 00:00:00 2001 From: yantian Date: Tue, 14 Jan 2025 16:39:47 +0800 Subject: [PATCH 04/11] format --- .../src/main/java/org/apache/paimon/rest/RESTCatalog.java | 1 - 1 file changed, 1 deletion(-) 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 db42883b8af1..b96baa7cec0a 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 @@ -67,7 +67,6 @@ import org.apache.paimon.table.Table; import org.apache.paimon.table.sink.BatchWriteBuilder; import org.apache.paimon.utils.Pair; -import org.apache.paimon.utils.Preconditions; import org.apache.paimon.view.View; import org.apache.paimon.view.ViewImpl; From e98d18542320419b39eb959f1c918beec55ceb43 Mon Sep 17 00:00:00 2001 From: yantian Date: Tue, 14 Jan 2025 17:23:35 +0800 Subject: [PATCH 05/11] add open api define for view and return void when create alert rename table --- .../org/apache/paimon/rest/RESTCatalog.java | 14 +- .../org/apache/paimon/rest/ViewSchema.java | 5 +- .../apache/paimon/rest/RESTCatalogServer.java | 158 ++++++------ paimon-open-api/rest-catalog-open-api.yaml | 231 ++++++++++++++++-- .../open/api/RESTCatalogController.java | 172 +++++++++---- 5 files changed, 417 insertions(+), 163 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 b96baa7cec0a..26367fc0627c 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 @@ -315,11 +315,7 @@ public void createTable(Identifier identifier, Schema schema, boolean ignoreIfEx checkNotSystemTable(identifier, "createTable"); validateAutoCreateClose(schema.options()); CreateTableRequest request = new CreateTableRequest(identifier, schema); - client.post( - resourcePaths.tables(identifier.getDatabaseName()), - request, - GetTableResponse.class, - headers()); + client.post(resourcePaths.tables(identifier.getDatabaseName()), request, headers()); } catch (AlreadyExistsException e) { if (!ignoreIfExists) { throw new TableAlreadyExistException(identifier); @@ -348,7 +344,6 @@ public void renameTable(Identifier fromTable, Identifier toTable, boolean ignore resourcePaths.renameTable( fromTable.getDatabaseName(), fromTable.getTableName()), request, - GetTableResponse.class, headers()); } catch (NoSuchResourceException e) { if (!ignoreIfNotExists) { @@ -371,7 +366,6 @@ public void alterTable( client.post( resourcePaths.table(identifier.getDatabaseName(), identifier.getTableName()), request, - GetTableResponse.class, headers()); } catch (NoSuchResourceException e) { if (!ignoreIfNotExists) { @@ -567,11 +561,7 @@ public void createView(Identifier identifier, View view, boolean ignoreIfExists) view.comment().orElse(null), view.query()); CreateViewRequest request = new CreateViewRequest(identifier, schema); - client.post( - resourcePaths.views(identifier.getDatabaseName()), - request, - GetViewResponse.class, - headers()); + client.post(resourcePaths.views(identifier.getDatabaseName()), request, headers()); } catch (NoSuchResourceException e) { throw new DatabaseNotExistException(identifier.getDatabaseName()); } catch (AlreadyExistsException e) { diff --git a/paimon-core/src/main/java/org/apache/paimon/rest/ViewSchema.java b/paimon-core/src/main/java/org/apache/paimon/rest/ViewSchema.java index ad70015d60df..73bda3edd3bb 100644 --- a/paimon-core/src/main/java/org/apache/paimon/rest/ViewSchema.java +++ b/paimon-core/src/main/java/org/apache/paimon/rest/ViewSchema.java @@ -33,6 +33,7 @@ import java.util.Map; import java.util.Objects; +/** Schema for view. */ @JsonIgnoreProperties(ignoreUnknown = true) public class ViewSchema { private static final String FIELD_FIELDS = "fields"; @@ -93,7 +94,9 @@ public List fields() { @Override public boolean equals(Object o) { - if (o == null || getClass() != o.getClass()) return false; + if (o == null || getClass() != o.getClass()) { + return false; + } ViewSchema that = (ViewSchema) o; return Objects.equals(query, that.query) && Objects.equals(comment, that.comment) 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 55d45d11a942..fe9ea8b176dd 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 @@ -293,21 +293,6 @@ public MockResponse dispatch(RecordedRequest request) { }; } - private static MockResponse renameTableApiHandler( - Catalog catalog, RecordedRequest request, String databaseName, String tableName) - throws Exception { - RenameRequest requestBody = - OBJECT_MAPPER.readValue(request.getBody().readUtf8(), RenameRequest.class); - catalog.renameTable( - Identifier.create(databaseName, tableName), requestBody.getNewIdentifier(), false); - GetTableResponse response = - getTable( - catalog, - requestBody.getNewIdentifier().getDatabaseName(), - requestBody.getNewIdentifier().getTableName()); - return mockResponse(response, 200); - } - private static MockResponse databasesApiHandler(Catalog catalog, RecordedRequest request) throws Exception { RESTResponse response; @@ -347,6 +332,78 @@ private static MockResponse databaseApiHandler( } } + private static MockResponse tablesApiHandler( + Catalog catalog, RecordedRequest request, String databaseName) throws Exception { + RESTResponse response; + switch (request.getMethod()) { + case "GET": + response = new ListTablesResponse(catalog.listTables(databaseName)); + return mockResponse(response, 200); + case "POST": + CreateTableRequest requestBody = + OBJECT_MAPPER.readValue( + request.getBody().readUtf8(), CreateTableRequest.class); + catalog.createTable(requestBody.getIdentifier(), requestBody.getSchema(), false); + return new MockResponse().setResponseCode(200); + default: + return new MockResponse().setResponseCode(404); + } + } + + private static MockResponse tableApiHandler( + Catalog catalog, RecordedRequest request, String databaseName, String tableName) + throws Exception { + RESTResponse response; + Identifier identifier = Identifier.create(databaseName, tableName); + switch (request.getMethod()) { + case "GET": + response = getTable(catalog, databaseName, tableName); + return mockResponse(response, 200); + case "POST": + AlterTableRequest requestBody = + OBJECT_MAPPER.readValue( + request.getBody().readUtf8(), AlterTableRequest.class); + catalog.alterTable(identifier, requestBody.getChanges(), false); + return new MockResponse().setResponseCode(200); + case "DELETE": + catalog.dropTable(identifier, false); + return new MockResponse().setResponseCode(200); + default: + return new MockResponse().setResponseCode(404); + } + } + + private static MockResponse renameTableApiHandler( + Catalog catalog, RecordedRequest request, String databaseName, String tableName) + throws Exception { + RenameRequest requestBody = + OBJECT_MAPPER.readValue(request.getBody().readUtf8(), RenameRequest.class); + catalog.renameTable( + Identifier.create(databaseName, tableName), requestBody.getNewIdentifier(), false); + return new MockResponse().setResponseCode(200); + } + + private static MockResponse partitionsApiHandler( + Catalog catalog, RecordedRequest request, String databaseName, String tableName) + throws Exception { + RESTResponse response; + Identifier identifier = Identifier.create(databaseName, tableName); + switch (request.getMethod()) { + case "GET": + List partitions = catalog.listPartitions(identifier); + response = new ListPartitionsResponse(partitions); + return mockResponse(response, 200); + case "POST": + CreatePartitionsRequest requestBody = + OBJECT_MAPPER.readValue( + request.getBody().readUtf8(), CreatePartitionsRequest.class); + catalog.createPartitions(identifier, requestBody.getPartitionSpecs()); + return new MockResponse().setResponseCode(200); + default: + return new MockResponse().setResponseCode(404); + } + } + private static MockResponse viewsApiHandler( Catalog catalog, RecordedRequest request, String databaseName) throws Exception { RESTResponse response; @@ -366,10 +423,7 @@ private static MockResponse viewsApiHandler( requestBody.getSchema().comment(), requestBody.getSchema().options()); catalog.createView(requestBody.getIdentifier(), view, false); - response = - new GetViewResponse( - UUID.randomUUID().toString(), "", requestBody.getSchema()); - return mockResponse(response, 200); + return new MockResponse().setResponseCode(200); default: return new MockResponse().setResponseCode(404); } @@ -418,72 +472,6 @@ private static MockResponse renameViewApiHandler( return mockResponse(response, 200); } - private static MockResponse tablesApiHandler( - Catalog catalog, RecordedRequest request, String databaseName) throws Exception { - RESTResponse response; - switch (request.getMethod()) { - case "GET": - response = new ListTablesResponse(catalog.listTables(databaseName)); - return mockResponse(response, 200); - case "POST": - CreateTableRequest requestBody = - OBJECT_MAPPER.readValue( - request.getBody().readUtf8(), CreateTableRequest.class); - catalog.createTable(requestBody.getIdentifier(), requestBody.getSchema(), false); - response = - new GetTableResponse( - UUID.randomUUID().toString(), "", 1L, requestBody.getSchema()); - return mockResponse(response, 200); - default: - return new MockResponse().setResponseCode(404); - } - } - - private static MockResponse tableApiHandler( - Catalog catalog, RecordedRequest request, String databaseName, String tableName) - throws Exception { - RESTResponse response; - Identifier identifier = Identifier.create(databaseName, tableName); - switch (request.getMethod()) { - case "GET": - response = getTable(catalog, databaseName, tableName); - return mockResponse(response, 200); - case "POST": - AlterTableRequest requestBody = - OBJECT_MAPPER.readValue( - request.getBody().readUtf8(), AlterTableRequest.class); - catalog.alterTable(identifier, requestBody.getChanges(), false); - response = getTable(catalog, databaseName, tableName); - return mockResponse(response, 200); - case "DELETE": - catalog.dropTable(identifier, false); - return new MockResponse().setResponseCode(200); - default: - return new MockResponse().setResponseCode(404); - } - } - - private static MockResponse partitionsApiHandler( - Catalog catalog, RecordedRequest request, String databaseName, String tableName) - throws Exception { - RESTResponse response; - Identifier identifier = Identifier.create(databaseName, tableName); - switch (request.getMethod()) { - case "GET": - List partitions = catalog.listPartitions(identifier); - response = new ListPartitionsResponse(partitions); - return mockResponse(response, 200); - case "POST": - CreatePartitionsRequest requestBody = - OBJECT_MAPPER.readValue( - request.getBody().readUtf8(), CreatePartitionsRequest.class); - catalog.createPartitions(identifier, requestBody.getPartitionSpecs()); - return new MockResponse().setResponseCode(200); - default: - return new MockResponse().setResponseCode(404); - } - } - private static GetTableResponse getTable(Catalog catalog, String databaseName, String tableName) throws Exception { Identifier identifier = Identifier.create(databaseName, tableName); diff --git a/paimon-open-api/rest-catalog-open-api.yaml b/paimon-open-api/rest-catalog-open-api.yaml index 41d2632454db..2482fb18b1c4 100644 --- a/paimon-open-api/rest-catalog-open-api.yaml +++ b/paimon-open-api/rest-catalog-open-api.yaml @@ -240,11 +240,7 @@ paths: $ref: '#/components/schemas/CreateTableRequest' responses: "200": - description: OK - content: - application/json: - schema: - $ref: '#/components/schemas/GetTableResponse' + description: Success, no content "500": description: Internal Server Error /v1/{prefix}/databases/{database}/tables/{table}: @@ -312,11 +308,7 @@ paths: $ref: '#/components/schemas/AlterTableRequest' responses: "200": - description: OK - content: - application/json: - schema: - $ref: '#/components/schemas/GetTableResponse' + description: Success, no content "404": description: Resource not found content: @@ -384,14 +376,10 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/RenameTableRequest' + $ref: '#/components/schemas/RenameRequest' responses: "200": - description: OK - content: - application/json: - schema: - $ref: '#/components/schemas/GetTableResponse' + description: Success, no content "404": description: Resource not found content: @@ -594,6 +582,177 @@ paths: $ref: '#/components/schemas/ErrorResponse' "500": description: Internal Server Error + /v1/{prefix}/databases/{database}/views: + get: + tags: + - view + summary: List views + operationId: listViews + parameters: + - name: prefix + in: path + required: true + schema: + type: string + - name: database + in: path + required: true + schema: + type: string + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/ListViewsResponse' + "500": + description: Internal Server Error + post: + tags: + - view + summary: Create view + operationId: createView + parameters: + - name: prefix + in: path + required: true + schema: + type: string + - name: database + in: path + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/CreateViewRequest' + responses: + "200": + description: Success, no content + "404": + description: Resource not found + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + "500": + description: Internal Server Error + /v1/{prefix}/databases/{database}/views/{view}: + get: + tags: + - view + summary: Get view + operationId: getView + parameters: + - name: prefix + in: path + required: true + schema: + type: string + - name: database + in: path + required: true + schema: + type: string + - name: view + in: path + required: true + schema: + type: string + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/GetViewResponse' + "404": + description: Resource not found + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + "500": + description: Internal Server Error + delete: + tags: + - view + summary: Drop view + operationId: dropView + parameters: + - name: prefix + in: path + required: true + schema: + type: string + - name: database + in: path + required: true + schema: + type: string + - name: view + in: path + required: true + schema: + type: string + responses: + "200": + description: Success, no content + "404": + description: Resource not found + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + "500": + description: Internal Server Error + /v1/{prefix}/databases/{database}/views/{view}/rename: + post: + tags: + - table + summary: Rename view + operationId: renameView + parameters: + - name: prefix + in: path + required: true + schema: + type: string + - name: database + in: path + required: true + schema: + type: string + - name: view + in: path + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/RenameRequest' + responses: + "200": + description: Success, no content + "404": + description: Resource not found + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + "409": + description: Resource has exist + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + "500": + description: Internal Server Error components: schemas: CreateDatabaseRequest: @@ -661,6 +820,13 @@ components: $ref: '#/components/schemas/Identifier' schema: $ref: '#/components/schemas/Schema' + CreateViewRequest: + type: object + properties: + identifier: + $ref: '#/components/schemas/Identifier' + schema: + $ref: '#/components/schemas/ViewSchema' DataField: type: object properties: @@ -895,7 +1061,7 @@ components: type: string type: type: string - RenameTableRequest: + RenameRequest: type: object properties: newIdentifier: @@ -976,6 +1142,37 @@ components: type: array items: $ref: '#/components/schemas/Partition' + GetViewResponse: + type: object + properties: + id: + type: string + name: + type: string + schema: + $ref: '#/components/schemas/ViewSchema' + ListViewsResponse: + type: object + properties: + views: + type: array + items: + type: string + ViewSchema: + type: object + properties: + fields: + type: array + items: + $ref: '#/components/schemas/DataField' + options: + type: object + additionalProperties: + type: string + comment: + type: string + query: + type: string Partition: 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 f8821f68aa71..b48c9e9beec9 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 @@ -20,12 +20,14 @@ import org.apache.paimon.partition.Partition; import org.apache.paimon.rest.ResourcePaths; +import org.apache.paimon.rest.ViewSchema; import org.apache.paimon.rest.requests.AlterDatabaseRequest; import org.apache.paimon.rest.requests.AlterPartitionsRequest; import org.apache.paimon.rest.requests.AlterTableRequest; import org.apache.paimon.rest.requests.CreateDatabaseRequest; import org.apache.paimon.rest.requests.CreatePartitionsRequest; import org.apache.paimon.rest.requests.CreateTableRequest; +import org.apache.paimon.rest.requests.CreateViewRequest; import org.apache.paimon.rest.requests.DropPartitionsRequest; import org.apache.paimon.rest.requests.MarkDonePartitionsRequest; import org.apache.paimon.rest.requests.RenameRequest; @@ -35,9 +37,13 @@ import org.apache.paimon.rest.responses.ErrorResponse; import org.apache.paimon.rest.responses.GetDatabaseResponse; import org.apache.paimon.rest.responses.GetTableResponse; +import org.apache.paimon.rest.responses.GetViewResponse; import org.apache.paimon.rest.responses.ListDatabasesResponse; import org.apache.paimon.rest.responses.ListPartitionsResponse; import org.apache.paimon.rest.responses.ListTablesResponse; +import org.apache.paimon.rest.responses.ListViewsResponse; +import org.apache.paimon.types.DataField; +import org.apache.paimon.types.IntType; import org.apache.paimon.shade.guava30.com.google.common.collect.ImmutableList; import org.apache.paimon.shade.guava30.com.google.common.collect.Lists; @@ -55,7 +61,10 @@ import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RestController; +import java.util.Arrays; +import java.util.Collections; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.UUID; @@ -251,37 +260,22 @@ public GetTableResponse getTable( summary = "Create table", tags = {"table"}) @ApiResponses({ - @ApiResponse( - responseCode = "200", - content = {@Content(schema = @Schema(implementation = GetTableResponse.class))}), + @ApiResponse(responseCode = "200", description = "Success, no content"), @ApiResponse( responseCode = "500", content = {@Content(schema = @Schema())}) }) @PostMapping("/v1/{prefix}/databases/{database}/tables") - public GetTableResponse createTable( + public void createTable( @PathVariable String prefix, @PathVariable String database, - @RequestBody CreateTableRequest request) { - return new GetTableResponse( - UUID.randomUUID().toString(), - "", - 1, - new org.apache.paimon.schema.Schema( - ImmutableList.of(), - ImmutableList.of(), - ImmutableList.of(), - new HashMap<>(), - "comment")); - } + @RequestBody CreateTableRequest request) {} @Operation( summary = "Alter table", tags = {"table"}) @ApiResponses({ - @ApiResponse( - responseCode = "200", - content = {@Content(schema = @Schema(implementation = GetTableResponse.class))}), + @ApiResponse(responseCode = "200", description = "Success, no content"), @ApiResponse( responseCode = "404", description = "Resource not found", @@ -291,22 +285,11 @@ public GetTableResponse createTable( content = {@Content(schema = @Schema())}) }) @PostMapping("/v1/{prefix}/databases/{database}/tables/{table}") - public GetTableResponse alterTable( + public void alterTable( @PathVariable String prefix, @PathVariable String database, @PathVariable String table, - @RequestBody AlterTableRequest request) { - return new GetTableResponse( - UUID.randomUUID().toString(), - "", - 1, - new org.apache.paimon.schema.Schema( - ImmutableList.of(), - ImmutableList.of(), - ImmutableList.of(), - new HashMap<>(), - "comment")); - } + @RequestBody AlterTableRequest request) {} @Operation( summary = "Drop table", @@ -331,9 +314,7 @@ public void dropTable( summary = "Rename table", tags = {"table"}) @ApiResponses({ - @ApiResponse( - responseCode = "200", - content = {@Content(schema = @Schema(implementation = GetTableResponse.class))}), + @ApiResponse(responseCode = "200", description = "Success, no content"), @ApiResponse( responseCode = "404", description = "Resource not found", @@ -343,22 +324,11 @@ public void dropTable( content = {@Content(schema = @Schema())}) }) @PostMapping("/v1/{prefix}/databases/{database}/tables/{table}/rename") - public GetTableResponse renameTable( + public void renameTable( @PathVariable String prefix, @PathVariable String database, @PathVariable String table, - @RequestBody RenameRequest request) { - return new GetTableResponse( - UUID.randomUUID().toString(), - "", - 1, - new org.apache.paimon.schema.Schema( - ImmutableList.of(), - ImmutableList.of(), - ImmutableList.of(), - new HashMap<>(), - "comment")); - } + @RequestBody RenameRequest request) {} @Operation( summary = "List partitions", @@ -467,4 +437,110 @@ public void markDonePartitions( @PathVariable String database, @PathVariable String table, @RequestBody MarkDonePartitionsRequest request) {} + + @Operation( + summary = "List views", + tags = {"view"}) + @ApiResponses({ + @ApiResponse( + responseCode = "200", + content = {@Content(schema = @Schema(implementation = ListViewsResponse.class))}), + @ApiResponse( + responseCode = "404", + description = "Resource not found", + content = {@Content(schema = @Schema(implementation = ErrorResponse.class))}), + @ApiResponse( + responseCode = "500", + content = {@Content(schema = @Schema())}) + }) + @GetMapping("/v1/{prefix}/databases/{database}/views") + public ListViewsResponse listViews(@PathVariable String prefix, @PathVariable String database) { + return new ListViewsResponse(ImmutableList.of("view1")); + } + + @Operation( + summary = "create view", + tags = {"view"}) + @ApiResponses({ + @ApiResponse(responseCode = "200", description = "Success, no content"), + @ApiResponse( + responseCode = "404", + description = "Resource not found", + content = {@Content(schema = @Schema(implementation = ErrorResponse.class))}), + @ApiResponse( + responseCode = "500", + content = {@Content(schema = @Schema())}) + }) + @PostMapping("/v1/{prefix}/databases/{database}/views") + public void createView( + @PathVariable String prefix, + @PathVariable String database, + @RequestBody CreateViewRequest request) {} + + @Operation( + summary = "Get view", + tags = {"view"}) + @ApiResponses({ + @ApiResponse( + responseCode = "200", + content = {@Content(schema = @Schema(implementation = GetViewResponse.class))}), + @ApiResponse( + responseCode = "404", + description = "Resource not found", + content = {@Content(schema = @Schema(implementation = ErrorResponse.class))}), + @ApiResponse( + responseCode = "500", + content = {@Content(schema = @Schema())}) + }) + @GetMapping("/v1/{prefix}/databases/{database}/views/{view}") + public GetViewResponse getView( + @PathVariable String prefix, @PathVariable String database, @PathVariable String view) { + List fields = + Arrays.asList( + new DataField(0, "f0", new IntType()), + new DataField(1, "f1", new IntType())); + ViewSchema schema = + new ViewSchema( + fields, Collections.singletonMap("pt", "1"), "comment", "select * from t1"); + return new GetViewResponse("id", "name", schema); + } + + @Operation( + summary = "Drop view", + tags = {"view"}) + @ApiResponses({ + @ApiResponse(responseCode = "200", description = "Success, no content"), + @ApiResponse( + responseCode = "404", + description = "Resource not found", + content = {@Content(schema = @Schema(implementation = ErrorResponse.class))}), + @ApiResponse( + responseCode = "500", + content = {@Content(schema = @Schema())}) + }) + @DeleteMapping("/v1/{prefix}/databases/{database}/views/{view}") + public void dropView( + @PathVariable String prefix, + @PathVariable String database, + @PathVariable String view) {} + + @Operation( + summary = "Rename view", + tags = {"view"}) + @ApiResponses({ + @ApiResponse(responseCode = "200", description = "Success, no content"), + @ApiResponse( + responseCode = "404", + description = "Resource not found", + content = {@Content(schema = @Schema(implementation = ErrorResponse.class))}), + @ApiResponse( + responseCode = "500", + content = {@Content(schema = @Schema())}) + }) + @PostMapping("/v1/{prefix}/databases/{database}/views/{view}/rename") + public void renameView( + @PathVariable String prefix, + @PathVariable String database, + @PathVariable String view, + @RequestBody RenameRequest request) {} } From 36d4488a7d5a6a153ae2e1738090a6a49159a687 Mon Sep 17 00:00:00 2001 From: yantian Date: Tue, 14 Jan 2025 17:55:55 +0800 Subject: [PATCH 06/11] use schema in ViewImpl --- .../org/apache/paimon/rest/RESTCatalog.java | 1 + .../rest/requests/CreateViewRequest.java | 2 +- .../rest/responses/GetViewResponse.java | 2 +- .../java/org/apache/paimon/view/ViewImpl.java | 29 +++++++------------ .../paimon/{rest => view}/ViewSchema.java | 16 ++++++++-- .../apache/paimon/rest/MockRESTMessage.java | 1 + .../apache/paimon/rest/RESTCatalogServer.java | 1 + .../open/api/RESTCatalogController.java | 2 +- 8 files changed, 30 insertions(+), 24 deletions(-) rename paimon-core/src/main/java/org/apache/paimon/{rest => view}/ViewSchema.java (88%) 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 26367fc0627c..996e292ee51e 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 @@ -69,6 +69,7 @@ import org.apache.paimon.utils.Pair; import org.apache.paimon.view.View; import org.apache.paimon.view.ViewImpl; +import org.apache.paimon.view.ViewSchema; import org.apache.paimon.shade.guava30.com.google.common.collect.ImmutableList; diff --git a/paimon-core/src/main/java/org/apache/paimon/rest/requests/CreateViewRequest.java b/paimon-core/src/main/java/org/apache/paimon/rest/requests/CreateViewRequest.java index 8eddda7453ca..0951f822a96d 100644 --- a/paimon-core/src/main/java/org/apache/paimon/rest/requests/CreateViewRequest.java +++ b/paimon-core/src/main/java/org/apache/paimon/rest/requests/CreateViewRequest.java @@ -20,7 +20,7 @@ import org.apache.paimon.catalog.Identifier; import org.apache.paimon.rest.RESTRequest; -import org.apache.paimon.rest.ViewSchema; +import org.apache.paimon.view.ViewSchema; import org.apache.paimon.shade.jackson2.com.fasterxml.jackson.annotation.JsonCreator; import org.apache.paimon.shade.jackson2.com.fasterxml.jackson.annotation.JsonGetter; 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 e31d2e82ad5b..7fe1237691b1 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 @@ -19,7 +19,7 @@ package org.apache.paimon.rest.responses; import org.apache.paimon.rest.RESTResponse; -import org.apache.paimon.rest.ViewSchema; +import org.apache.paimon.view.ViewSchema; import org.apache.paimon.shade.jackson2.com.fasterxml.jackson.annotation.JsonCreator; import org.apache.paimon.shade.jackson2.com.fasterxml.jackson.annotation.JsonGetter; diff --git a/paimon-core/src/main/java/org/apache/paimon/view/ViewImpl.java b/paimon-core/src/main/java/org/apache/paimon/view/ViewImpl.java index 69226ff1fcd6..8edb517d93ec 100644 --- a/paimon-core/src/main/java/org/apache/paimon/view/ViewImpl.java +++ b/paimon-core/src/main/java/org/apache/paimon/view/ViewImpl.java @@ -35,10 +35,7 @@ public class ViewImpl implements View { private final Identifier identifier; - private final RowType rowType; - private final String query; - @Nullable private final String comment; - private final Map options; + private final ViewSchema viewSchema; public ViewImpl( Identifier identifier, @@ -47,10 +44,7 @@ public ViewImpl( @Nullable String comment, Map options) { this.identifier = identifier; - this.rowType = rowType; - this.query = query; - this.comment = comment; - this.options = options; + this.viewSchema = new ViewSchema(query, comment, options, rowType); } @Override @@ -65,29 +59,29 @@ public String fullName() { @Override public RowType rowType() { - return rowType; + return this.viewSchema.rowType(); } @Override public String query() { - return query; + return this.viewSchema.query(); } @Override public Optional comment() { - return Optional.ofNullable(comment); + return Optional.ofNullable(this.viewSchema.comment()); } @Override public Map options() { - return options; + return this.viewSchema.options(); } @Override public View copy(Map dynamicOptions) { - Map newOptions = new HashMap<>(options); + Map newOptions = new HashMap<>(options()); newOptions.putAll(dynamicOptions); - return new ViewImpl(identifier, rowType, query, comment, newOptions); + return new ViewImpl(identifier, rowType(), query(), this.viewSchema.comment(), newOptions); } @Override @@ -100,14 +94,11 @@ public boolean equals(Object o) { } ViewImpl view = (ViewImpl) o; return Objects.equals(identifier, view.identifier) - && Objects.equals(rowType, view.rowType) - && Objects.equals(query, view.query) - && Objects.equals(comment, view.comment) - && Objects.equals(options, view.options); + && Objects.equals(viewSchema, view.viewSchema); } @Override public int hashCode() { - return Objects.hash(identifier, rowType, query, comment, options); + return Objects.hash(identifier, viewSchema); } } diff --git a/paimon-core/src/main/java/org/apache/paimon/rest/ViewSchema.java b/paimon-core/src/main/java/org/apache/paimon/view/ViewSchema.java similarity index 88% rename from paimon-core/src/main/java/org/apache/paimon/rest/ViewSchema.java rename to paimon-core/src/main/java/org/apache/paimon/view/ViewSchema.java index 73bda3edd3bb..5cb9b46f0f78 100644 --- a/paimon-core/src/main/java/org/apache/paimon/rest/ViewSchema.java +++ b/paimon-core/src/main/java/org/apache/paimon/view/ViewSchema.java @@ -16,7 +16,7 @@ * limitations under the License. */ -package org.apache.paimon.rest; +package org.apache.paimon.view; import org.apache.paimon.types.DataField; import org.apache.paimon.types.RowType; @@ -55,6 +55,8 @@ public class ViewSchema { @JsonProperty(FIELD_FIELDS) private final List fields; + private final RowType rowType; + @JsonCreator public ViewSchema( @JsonProperty(FIELD_FIELDS) List fields, @@ -65,10 +67,20 @@ public ViewSchema( this.options = options; this.comment = comment; this.query = query; + this.rowType = new RowType(fields); + } + + public ViewSchema( + String query, @Nullable String comment, Map options, RowType rowType) { + this.query = query; + this.comment = comment; + this.options = options; + this.rowType = rowType; + this.fields = rowType.getFields(); } public RowType rowType() { - return new RowType(fields); + return rowType; } @JsonGetter(FIELD_QUERY) 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 15e1f2a903f4..c8ffb69458ab 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 @@ -45,6 +45,7 @@ import org.apache.paimon.types.DataTypes; import org.apache.paimon.types.IntType; import org.apache.paimon.types.RowType; +import org.apache.paimon.view.ViewSchema; import org.apache.paimon.shade.guava30.com.google.common.collect.ImmutableList; import org.apache.paimon.shade.guava30.com.google.common.collect.Lists; 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 fe9ea8b176dd..45d7211c8b3d 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 @@ -51,6 +51,7 @@ import org.apache.paimon.types.DataField; import org.apache.paimon.view.View; import org.apache.paimon.view.ViewImpl; +import org.apache.paimon.view.ViewSchema; import org.apache.paimon.shade.jackson2.com.fasterxml.jackson.core.JsonProcessingException; 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 b48c9e9beec9..75ad8f07de51 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 @@ -20,7 +20,6 @@ import org.apache.paimon.partition.Partition; import org.apache.paimon.rest.ResourcePaths; -import org.apache.paimon.rest.ViewSchema; import org.apache.paimon.rest.requests.AlterDatabaseRequest; import org.apache.paimon.rest.requests.AlterPartitionsRequest; import org.apache.paimon.rest.requests.AlterTableRequest; @@ -44,6 +43,7 @@ import org.apache.paimon.rest.responses.ListViewsResponse; import org.apache.paimon.types.DataField; import org.apache.paimon.types.IntType; +import org.apache.paimon.view.ViewSchema; import org.apache.paimon.shade.guava30.com.google.common.collect.ImmutableList; import org.apache.paimon.shade.guava30.com.google.common.collect.Lists; From e4c58bf47a2d8b8334d98b5ee3217f28793e471c Mon Sep 17 00:00:00 2001 From: yantian Date: Wed, 15 Jan 2025 09:19:04 +0800 Subject: [PATCH 07/11] use row type in view schema --- .../java/org/apache/paimon/types/RowType.java | 6 ++++- .../org/apache/paimon/rest/RESTCatalog.java | 3 ++- .../org/apache/paimon/view/ViewSchema.java | 22 +++++-------------- .../apache/paimon/rest/MockRESTMessage.java | 5 ++++- .../apache/paimon/rest/RESTCatalogServer.java | 7 ++---- .../open/api/RESTCatalogController.java | 6 ++++- 6 files changed, 24 insertions(+), 25 deletions(-) diff --git a/paimon-common/src/main/java/org/apache/paimon/types/RowType.java b/paimon-common/src/main/java/org/apache/paimon/types/RowType.java index 681a07af584d..04ff200a5312 100644 --- a/paimon-common/src/main/java/org/apache/paimon/types/RowType.java +++ b/paimon-common/src/main/java/org/apache/paimon/types/RowType.java @@ -24,6 +24,8 @@ import org.apache.paimon.utils.Preconditions; import org.apache.paimon.utils.StringUtils; +import org.apache.paimon.shade.jackson2.com.fasterxml.jackson.annotation.JsonCreator; +import org.apache.paimon.shade.jackson2.com.fasterxml.jackson.annotation.JsonProperty; import org.apache.paimon.shade.jackson2.com.fasterxml.jackson.core.JsonGenerator; import java.io.IOException; @@ -51,6 +53,7 @@ public final class RowType extends DataType { private static final long serialVersionUID = 1L; + private static final String FIELD_FIELDS = "fields"; public static final String FORMAT = "ROW<%s>"; @@ -67,7 +70,8 @@ public RowType(boolean isNullable, List fields) { validateFields(fields); } - public RowType(List fields) { + @JsonCreator + public RowType(@JsonProperty(FIELD_FIELDS) List fields) { this(true, fields); } 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 996e292ee51e..1687d1541447 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 @@ -66,6 +66,7 @@ import org.apache.paimon.table.FileStoreTable; import org.apache.paimon.table.Table; import org.apache.paimon.table.sink.BatchWriteBuilder; +import org.apache.paimon.types.RowType; import org.apache.paimon.utils.Pair; import org.apache.paimon.view.View; import org.apache.paimon.view.ViewImpl; @@ -557,7 +558,7 @@ public void createView(Identifier identifier, View view, boolean ignoreIfExists) try { ViewSchema schema = new ViewSchema( - view.rowType().getFields(), + new RowType(view.rowType().getFields()), view.options(), view.comment().orElse(null), view.query()); diff --git a/paimon-core/src/main/java/org/apache/paimon/view/ViewSchema.java b/paimon-core/src/main/java/org/apache/paimon/view/ViewSchema.java index 5cb9b46f0f78..ff64f9d5c068 100644 --- a/paimon-core/src/main/java/org/apache/paimon/view/ViewSchema.java +++ b/paimon-core/src/main/java/org/apache/paimon/view/ViewSchema.java @@ -18,7 +18,6 @@ package org.apache.paimon.view; -import org.apache.paimon.types.DataField; import org.apache.paimon.types.RowType; import org.apache.paimon.shade.jackson2.com.fasterxml.jackson.annotation.JsonCreator; @@ -29,14 +28,13 @@ import javax.annotation.Nullable; -import java.util.List; import java.util.Map; import java.util.Objects; /** Schema for view. */ @JsonIgnoreProperties(ignoreUnknown = true) public class ViewSchema { - private static final String FIELD_FIELDS = "fields"; + private static final String FIELD_FIELDS = "rowType"; private static final String FIELD_OPTIONS = "options"; private static final String FIELD_COMMENT = "comment"; private static final String FIELD_QUERY = "query"; @@ -53,21 +51,18 @@ public class ViewSchema { private final Map options; @JsonProperty(FIELD_FIELDS) - private final List fields; - private final RowType rowType; @JsonCreator public ViewSchema( - @JsonProperty(FIELD_FIELDS) List fields, + @JsonProperty(FIELD_FIELDS) RowType rowType, @JsonProperty(FIELD_OPTIONS) Map options, @Nullable @JsonProperty(FIELD_COMMENT) String comment, @JsonProperty(FIELD_QUERY) String query) { - this.fields = fields; this.options = options; this.comment = comment; this.query = query; - this.rowType = new RowType(fields); + this.rowType = rowType; } public ViewSchema( @@ -76,9 +71,9 @@ public ViewSchema( this.comment = comment; this.options = options; this.rowType = rowType; - this.fields = rowType.getFields(); } + @JsonGetter(FIELD_FIELDS) public RowType rowType() { return rowType; } @@ -99,11 +94,6 @@ public Map options() { return options; } - @JsonGetter(FIELD_FIELDS) - public List fields() { - return fields; - } - @Override public boolean equals(Object o) { if (o == null || getClass() != o.getClass()) { @@ -113,11 +103,11 @@ public boolean equals(Object o) { return Objects.equals(query, that.query) && Objects.equals(comment, that.comment) && Objects.equals(options, that.options) - && Objects.equals(fields, that.fields); + && Objects.equals(rowType, that.rowType); } @Override public int hashCode() { - return Objects.hash(query, comment, options, fields); + return Objects.hash(query, comment, options, rowType); } } 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 c8ffb69458ab..c74a3c3bab52 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 @@ -253,7 +253,10 @@ private static ViewSchema viewSchema() { new DataField(0, "f0", new IntType()), new DataField(1, "f1", new IntType())); return new ViewSchema( - fields, Collections.singletonMap("pt", "1"), "comment", "select * from t1"); + new RowType(fields), + Collections.singletonMap("pt", "1"), + "comment", + "select * from t1"); } private static Partition partition() { 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 45d7211c8b3d..67cf06be96dd 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 @@ -440,7 +440,7 @@ private static MockResponse viewApiHandler( View view = catalog.getView(identifier); ViewSchema schema = new ViewSchema( - view.rowType().getFields(), + view.rowType(), view.options(), view.comment().orElse(null), view.query()); @@ -465,10 +465,7 @@ private static MockResponse renameViewApiHandler( View view = catalog.getView(identifier); ViewSchema schema = new ViewSchema( - view.rowType().getFields(), - view.options(), - view.comment().orElse(null), - view.query()); + view.rowType(), view.options(), view.comment().orElse(null), view.query()); GetViewResponse response = new GetViewResponse("id", identifier.getTableName(), schema); return mockResponse(response, 200); } 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 75ad8f07de51..aa072c4a5bab 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 @@ -43,6 +43,7 @@ import org.apache.paimon.rest.responses.ListViewsResponse; import org.apache.paimon.types.DataField; import org.apache.paimon.types.IntType; +import org.apache.paimon.types.RowType; import org.apache.paimon.view.ViewSchema; import org.apache.paimon.shade.guava30.com.google.common.collect.ImmutableList; @@ -501,7 +502,10 @@ public GetViewResponse getView( new DataField(1, "f1", new IntType())); ViewSchema schema = new ViewSchema( - fields, Collections.singletonMap("pt", "1"), "comment", "select * from t1"); + new RowType(fields), + Collections.singletonMap("pt", "1"), + "comment", + "select * from t1"); return new GetViewResponse("id", "name", schema); } From 1400ad91ebd2a2fcba91a7aedbf7959e1877d0a7 Mon Sep 17 00:00:00 2001 From: yantian Date: Wed, 15 Jan 2025 09:23:57 +0800 Subject: [PATCH 08/11] update open api for row type --- paimon-open-api/rest-catalog-open-api.yaml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/paimon-open-api/rest-catalog-open-api.yaml b/paimon-open-api/rest-catalog-open-api.yaml index 2482fb18b1c4..677c9bb8edb7 100644 --- a/paimon-open-api/rest-catalog-open-api.yaml +++ b/paimon-open-api/rest-catalog-open-api.yaml @@ -885,7 +885,9 @@ components: pattern: ^ROW.* example: ROW fields: - $ref: '#/components/schemas/DataField' + type: array + items: + $ref: '#/components/schemas/DataField' Identifier: type: object properties: @@ -1161,10 +1163,8 @@ components: ViewSchema: type: object properties: - fields: - type: array - items: - $ref: '#/components/schemas/DataField' + rowType: + $ref: '#/components/schemas/RowType' options: type: object additionalProperties: From 12faaf26ff7bc80414223e4a23088a36247b8e96 Mon Sep 17 00:00:00 2001 From: yantian Date: Wed, 15 Jan 2025 10:45:10 +0800 Subject: [PATCH 09/11] update rename url follow iceberg --- .../org/apache/paimon/rest/RESTCatalog.java | 18 ++---- .../org/apache/paimon/rest/ResourcePaths.java | 8 +-- ...meRequest.java => RenameTableRequest.java} | 30 ++++++--- .../apache/paimon/rest/MockRESTMessage.java | 9 +-- .../apache/paimon/rest/RESTCatalogServer.java | 62 +++++++++---------- .../paimon/rest/RESTObjectMapperTest.java | 10 +-- paimon-open-api/rest-catalog-open-api.yaml | 26 +++----- .../open/api/RESTCatalogController.java | 24 ++++--- 8 files changed, 90 insertions(+), 97 deletions(-) rename paimon-core/src/main/java/org/apache/paimon/rest/requests/{RenameRequest.java => RenameTableRequest.java} (64%) 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 1687d1541447..8fe62cc9761a 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 @@ -48,7 +48,7 @@ import org.apache.paimon.rest.requests.CreateViewRequest; import org.apache.paimon.rest.requests.DropPartitionsRequest; import org.apache.paimon.rest.requests.MarkDonePartitionsRequest; -import org.apache.paimon.rest.requests.RenameRequest; +import org.apache.paimon.rest.requests.RenameTableRequest; import org.apache.paimon.rest.responses.AlterDatabaseResponse; import org.apache.paimon.rest.responses.ConfigResponse; import org.apache.paimon.rest.responses.CreateDatabaseResponse; @@ -341,12 +341,8 @@ public void renameTable(Identifier fromTable, Identifier toTable, boolean ignore checkNotSystemTable(fromTable, "renameTable"); checkNotSystemTable(toTable, "renameTable"); try { - RenameRequest request = new RenameRequest(toTable); - client.post( - resourcePaths.renameTable( - fromTable.getDatabaseName(), fromTable.getTableName()), - request, - headers()); + RenameTableRequest request = new RenameTableRequest(fromTable, toTable); + client.post(resourcePaths.renameTable(fromTable.getDatabaseName()), request, headers()); } catch (NoSuchResourceException e) { if (!ignoreIfNotExists) { throw new TableNotExistException(fromTable); @@ -589,12 +585,8 @@ public List listViews(String databaseName) throws DatabaseNotExistExcept public void renameView(Identifier fromView, Identifier toView, boolean ignoreIfNotExists) throws ViewNotExistException, ViewAlreadyExistException { try { - RenameRequest request = new RenameRequest(toView); - client.post( - resourcePaths.renameView(fromView.getDatabaseName(), fromView.getTableName()), - request, - GetViewResponse.class, - headers()); + RenameTableRequest request = new RenameTableRequest(fromView, toView); + client.post(resourcePaths.renameView(fromView.getDatabaseName()), request, headers()); } catch (NoSuchResourceException e) { if (!ignoreIfNotExists) { throw new ViewNotExistException(fromView); diff --git a/paimon-core/src/main/java/org/apache/paimon/rest/ResourcePaths.java b/paimon-core/src/main/java/org/apache/paimon/rest/ResourcePaths.java index ad078d409ac8..dddf4b81eb5b 100644 --- a/paimon-core/src/main/java/org/apache/paimon/rest/ResourcePaths.java +++ b/paimon-core/src/main/java/org/apache/paimon/rest/ResourcePaths.java @@ -62,8 +62,8 @@ public String table(String databaseName, String tableName) { return SLASH.join(V1, prefix, DATABASES, databaseName, TABLES, tableName); } - public String renameTable(String databaseName, String tableName) { - return SLASH.join(V1, prefix, DATABASES, databaseName, TABLES, tableName, "rename"); + public String renameTable(String databaseName) { + return SLASH.join(V1, prefix, DATABASES, databaseName, TABLES, "rename"); } public String partitions(String databaseName, String tableName) { @@ -93,7 +93,7 @@ public String view(String databaseName, String viewName) { return SLASH.join(V1, prefix, DATABASES, databaseName, "views", viewName); } - public String renameView(String databaseName, String viewName) { - return SLASH.join(V1, prefix, DATABASES, databaseName, "views", viewName, "rename"); + public String renameView(String databaseName) { + return SLASH.join(V1, prefix, DATABASES, databaseName, "views", "rename"); } } diff --git a/paimon-core/src/main/java/org/apache/paimon/rest/requests/RenameRequest.java b/paimon-core/src/main/java/org/apache/paimon/rest/requests/RenameTableRequest.java similarity index 64% rename from paimon-core/src/main/java/org/apache/paimon/rest/requests/RenameRequest.java rename to paimon-core/src/main/java/org/apache/paimon/rest/requests/RenameTableRequest.java index 610ee81549fc..483e26538e5e 100644 --- a/paimon-core/src/main/java/org/apache/paimon/rest/requests/RenameRequest.java +++ b/paimon-core/src/main/java/org/apache/paimon/rest/requests/RenameTableRequest.java @@ -28,20 +28,32 @@ /** Request for renaming. */ @JsonIgnoreProperties(ignoreUnknown = true) -public class RenameRequest implements RESTRequest { +public class RenameTableRequest implements RESTRequest { - private static final String FIELD_NEW_IDENTIFIER_NAME = "newIdentifier"; + private static final String FIELD_SOURCE = "source"; + private static final String FIELD_DESTINATION = "destination"; - @JsonProperty(FIELD_NEW_IDENTIFIER_NAME) - private final Identifier newIdentifier; + @JsonProperty(FIELD_SOURCE) + private final Identifier source; + + @JsonProperty(FIELD_DESTINATION) + private final Identifier destination; @JsonCreator - public RenameRequest(@JsonProperty(FIELD_NEW_IDENTIFIER_NAME) Identifier newIdentifier) { - this.newIdentifier = newIdentifier; + public RenameTableRequest( + @JsonProperty(FIELD_SOURCE) Identifier source, + @JsonProperty(FIELD_DESTINATION) Identifier destination) { + this.source = source; + this.destination = destination; + } + + @JsonGetter(FIELD_DESTINATION) + public Identifier getDestination() { + return destination; } - @JsonGetter(FIELD_NEW_IDENTIFIER_NAME) - public Identifier getNewIdentifier() { - return newIdentifier; + @JsonGetter(FIELD_SOURCE) + public Identifier getSource() { + return source; } } 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 c74a3c3bab52..766cb09b0bdd 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 @@ -28,7 +28,7 @@ import org.apache.paimon.rest.requests.CreateTableRequest; import org.apache.paimon.rest.requests.CreateViewRequest; import org.apache.paimon.rest.requests.DropPartitionsRequest; -import org.apache.paimon.rest.requests.RenameRequest; +import org.apache.paimon.rest.requests.RenameTableRequest; import org.apache.paimon.rest.responses.AlterDatabaseResponse; import org.apache.paimon.rest.responses.CreateDatabaseResponse; import org.apache.paimon.rest.responses.GetDatabaseResponse; @@ -128,9 +128,10 @@ public static CreateTableRequest createTableRequest(String name) { return new CreateTableRequest(identifier, schema); } - public static RenameRequest renameRequest(String toTableName) { - Identifier newIdentifier = Identifier.create(databaseName(), toTableName); - return new RenameRequest(newIdentifier); + public static RenameTableRequest renameRequest(String sourceTable, String toTableName) { + Identifier source = Identifier.create(databaseName(), sourceTable); + Identifier destination = Identifier.create(databaseName(), toTableName); + return new RenameTableRequest(source, destination); } public static AlterTableRequest alterTableRequest() { 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 67cf06be96dd..6f7b7e17969a 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 @@ -33,7 +33,7 @@ import org.apache.paimon.rest.requests.CreateViewRequest; import org.apache.paimon.rest.requests.DropPartitionsRequest; import org.apache.paimon.rest.requests.MarkDonePartitionsRequest; -import org.apache.paimon.rest.requests.RenameRequest; +import org.apache.paimon.rest.requests.RenameTableRequest; import org.apache.paimon.rest.responses.CreateDatabaseResponse; import org.apache.paimon.rest.responses.ErrorResponse; import org.apache.paimon.rest.responses.ErrorResponseResourceType; @@ -105,6 +105,7 @@ public static Dispatcher initDispatcher(Catalog catalog, String authToken) { @Override public MockResponse dispatch(RecordedRequest request) { String token = request.getHeaders().get("Authorization"); + System.out.println(request.getPath()); RESTResponse response; try { if (!("Bearer " + authToken).equals(token)) { @@ -123,17 +124,23 @@ public MockResponse dispatch(RecordedRequest request) { .split("/"); String databaseName = resources[0]; boolean isViews = resources.length == 2 && "views".equals(resources[1]); - boolean isView = resources.length == 3 && "views".equals(resources[1]); - boolean isViewRename = - resources.length == 4 - && "views".equals(resources[1]) - && "rename".equals(resources[3]); boolean isTables = resources.length == 2 && "tables".equals(resources[1]); - boolean isTable = resources.length == 3 && "tables".equals(resources[1]); boolean isTableRename = - resources.length == 4 + resources.length == 3 + && "tables".equals(resources[1]) + && "rename".equals(resources[2]); + boolean isViewRename = + resources.length == 3 + && "views".equals(resources[1]) + && "rename".equals(resources[2]); + boolean isView = + resources.length == 3 + && "views".equals(resources[1]) + && !"rename".equals(resources[2]); + boolean isTable = + resources.length == 3 && "tables".equals(resources[1]) - && "rename".equals(resources[3]); + && !"rename".equals(resources[2]); boolean isPartitions = resources.length == 4 && "tables".equals(resources[1]) @@ -188,8 +195,7 @@ public MockResponse dispatch(RecordedRequest request) { String tableName = resources[2]; return partitionsApiHandler(catalog, request, databaseName, tableName); } else if (isTableRename) { - return renameTableApiHandler( - catalog, request, databaseName, resources[2]); + return renameTableApiHandler(catalog, request); } else if (isTable) { String tableName = resources[2]; return tableApiHandler(catalog, request, databaseName, tableName); @@ -197,12 +203,12 @@ public MockResponse dispatch(RecordedRequest request) { return tablesApiHandler(catalog, request, databaseName); } else if (isViews) { return viewsApiHandler(catalog, request, databaseName); + } else if (isViewRename) { + System.out.println("View rename"); + return renameViewApiHandler(catalog, request); } else if (isView) { String viewName = resources[2]; return viewApiHandler(catalog, request, databaseName, viewName); - } else if (isViewRename) { - String viewName = resources[2]; - return renameViewApiHandler(catalog, request, databaseName, viewName); } else { return databaseApiHandler(catalog, request, databaseName); } @@ -374,13 +380,11 @@ private static MockResponse tableApiHandler( } } - private static MockResponse renameTableApiHandler( - Catalog catalog, RecordedRequest request, String databaseName, String tableName) + private static MockResponse renameTableApiHandler(Catalog catalog, RecordedRequest request) throws Exception { - RenameRequest requestBody = - OBJECT_MAPPER.readValue(request.getBody().readUtf8(), RenameRequest.class); - catalog.renameTable( - Identifier.create(databaseName, tableName), requestBody.getNewIdentifier(), false); + RenameTableRequest requestBody = + OBJECT_MAPPER.readValue(request.getBody().readUtf8(), RenameTableRequest.class); + catalog.renameTable(requestBody.getSource(), requestBody.getDestination(), false); return new MockResponse().setResponseCode(200); } @@ -454,20 +458,12 @@ private static MockResponse viewApiHandler( } } - private static MockResponse renameViewApiHandler( - Catalog catalog, RecordedRequest request, String databaseName, String viewName) + private static MockResponse renameViewApiHandler(Catalog catalog, RecordedRequest request) throws Exception { - RenameRequest requestBody = - OBJECT_MAPPER.readValue(request.getBody().readUtf8(), RenameRequest.class); - catalog.renameView( - Identifier.create(databaseName, viewName), requestBody.getNewIdentifier(), false); - Identifier identifier = requestBody.getNewIdentifier(); - View view = catalog.getView(identifier); - ViewSchema schema = - new ViewSchema( - view.rowType(), view.options(), view.comment().orElse(null), view.query()); - GetViewResponse response = new GetViewResponse("id", identifier.getTableName(), schema); - return mockResponse(response, 200); + RenameTableRequest requestBody = + OBJECT_MAPPER.readValue(request.getBody().readUtf8(), RenameTableRequest.class); + catalog.renameView(requestBody.getSource(), requestBody.getDestination(), false); + return new MockResponse().setResponseCode(200); } private static GetTableResponse getTable(Catalog catalog, String databaseName, String tableName) diff --git a/paimon-core/src/test/java/org/apache/paimon/rest/RESTObjectMapperTest.java b/paimon-core/src/test/java/org/apache/paimon/rest/RESTObjectMapperTest.java index 79f02816f97e..fa56f8111828 100644 --- a/paimon-core/src/test/java/org/apache/paimon/rest/RESTObjectMapperTest.java +++ b/paimon-core/src/test/java/org/apache/paimon/rest/RESTObjectMapperTest.java @@ -26,7 +26,7 @@ import org.apache.paimon.rest.requests.CreateTableRequest; import org.apache.paimon.rest.requests.CreateViewRequest; import org.apache.paimon.rest.requests.DropPartitionsRequest; -import org.apache.paimon.rest.requests.RenameRequest; +import org.apache.paimon.rest.requests.RenameTableRequest; import org.apache.paimon.rest.responses.AlterDatabaseResponse; import org.apache.paimon.rest.responses.ConfigResponse; import org.apache.paimon.rest.responses.CreateDatabaseResponse; @@ -175,10 +175,12 @@ public void dataFieldParseTest() throws Exception { @Test public void renameTableRequestParseTest() throws Exception { - RenameRequest request = MockRESTMessage.renameRequest("t2"); + RenameTableRequest request = MockRESTMessage.renameRequest("t1", "t2"); String requestStr = OBJECT_MAPPER.writeValueAsString(request); - RenameRequest parseData = OBJECT_MAPPER.readValue(requestStr, RenameRequest.class); - assertEquals(request.getNewIdentifier(), parseData.getNewIdentifier()); + RenameTableRequest parseData = + OBJECT_MAPPER.readValue(requestStr, RenameTableRequest.class); + assertEquals(request.getSource(), parseData.getSource()); + assertEquals(request.getDestination(), parseData.getDestination()); } @Test diff --git a/paimon-open-api/rest-catalog-open-api.yaml b/paimon-open-api/rest-catalog-open-api.yaml index 677c9bb8edb7..ef196b5957d1 100644 --- a/paimon-open-api/rest-catalog-open-api.yaml +++ b/paimon-open-api/rest-catalog-open-api.yaml @@ -350,7 +350,7 @@ paths: $ref: '#/components/schemas/ErrorResponse' "500": description: Internal Server Error - /v1/{prefix}/databases/{database}/tables/{table}/rename: + /v1/{prefix}/databases/{database}/tables/rename: post: tags: - table @@ -367,16 +367,11 @@ paths: required: true schema: type: string - - name: table - in: path - required: true - schema: - type: string requestBody: content: application/json: schema: - $ref: '#/components/schemas/RenameRequest' + $ref: '#/components/schemas/RenameTableRequest' responses: "200": description: Success, no content @@ -709,10 +704,10 @@ paths: $ref: '#/components/schemas/ErrorResponse' "500": description: Internal Server Error - /v1/{prefix}/databases/{database}/views/{view}/rename: + /v1/{prefix}/databases/{database}/views/rename: post: tags: - - table + - view summary: Rename view operationId: renameView parameters: @@ -726,16 +721,11 @@ paths: required: true schema: type: string - - name: view - in: path - required: true - schema: - type: string requestBody: content: application/json: schema: - $ref: '#/components/schemas/RenameRequest' + $ref: '#/components/schemas/RenameTableRequest' responses: "200": description: Success, no content @@ -1063,10 +1053,12 @@ components: type: string type: type: string - RenameRequest: + RenameTableRequest: type: object properties: - newIdentifier: + source: + $ref: '#/components/schemas/Identifier' + destination: $ref: '#/components/schemas/Identifier' AlterDatabaseRequest: type: object 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 aa072c4a5bab..c2c917d0cd09 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 @@ -29,7 +29,7 @@ import org.apache.paimon.rest.requests.CreateViewRequest; import org.apache.paimon.rest.requests.DropPartitionsRequest; import org.apache.paimon.rest.requests.MarkDonePartitionsRequest; -import org.apache.paimon.rest.requests.RenameRequest; +import org.apache.paimon.rest.requests.RenameTableRequest; import org.apache.paimon.rest.responses.AlterDatabaseResponse; import org.apache.paimon.rest.responses.ConfigResponse; import org.apache.paimon.rest.responses.CreateDatabaseResponse; @@ -324,12 +324,11 @@ public void dropTable( responseCode = "500", content = {@Content(schema = @Schema())}) }) - @PostMapping("/v1/{prefix}/databases/{database}/tables/{table}/rename") + @PostMapping("/v1/{prefix}/databases/{database}/tables/rename") public void renameTable( @PathVariable String prefix, @PathVariable String database, - @PathVariable String table, - @RequestBody RenameRequest request) {} + @RequestBody RenameTableRequest request) {} @Operation( summary = "List partitions", @@ -510,7 +509,7 @@ public GetViewResponse getView( } @Operation( - summary = "Drop view", + summary = "Rename view", tags = {"view"}) @ApiResponses({ @ApiResponse(responseCode = "200", description = "Success, no content"), @@ -522,14 +521,14 @@ public GetViewResponse getView( responseCode = "500", content = {@Content(schema = @Schema())}) }) - @DeleteMapping("/v1/{prefix}/databases/{database}/views/{view}") - public void dropView( + @PostMapping("/v1/{prefix}/databases/{database}/views/rename") + public void renameView( @PathVariable String prefix, @PathVariable String database, - @PathVariable String view) {} + @RequestBody RenameTableRequest request) {} @Operation( - summary = "Rename view", + summary = "Drop view", tags = {"view"}) @ApiResponses({ @ApiResponse(responseCode = "200", description = "Success, no content"), @@ -541,10 +540,9 @@ public void dropView( responseCode = "500", content = {@Content(schema = @Schema())}) }) - @PostMapping("/v1/{prefix}/databases/{database}/views/{view}/rename") - public void renameView( + @DeleteMapping("/v1/{prefix}/databases/{database}/views/{view}") + public void dropView( @PathVariable String prefix, @PathVariable String database, - @PathVariable String view, - @RequestBody RenameRequest request) {} + @PathVariable String view) {} } From 1580221e4ca3b4a28f1d28ce993c39496abf6f3e Mon Sep 17 00:00:00 2001 From: yantian Date: Wed, 15 Jan 2025 12:26:41 +0800 Subject: [PATCH 10/11] remove table in commit path --- .../org/apache/paimon/rest/RESTCatalog.java | 3 +- .../org/apache/paimon/rest/ResourcePaths.java | 4 +- .../apache/paimon/rest/RESTCatalogServer.java | 10 ++-- paimon-open-api/rest-catalog-open-api.yaml | 51 ++++++++++++++++--- .../open/api/RESTCatalogController.java | 3 +- 5 files changed, 54 insertions(+), 17 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 81996c0c2a28..87b8e8cb9612 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 @@ -297,8 +297,7 @@ public boolean commitSnapshot(Identifier identifier, Snapshot snapshot) { CommitTableRequest request = new CommitTableRequest(identifier, snapshot); CommitTableResponse response = client.post( - resourcePaths.commitTable( - identifier.getDatabaseName(), identifier.getTableName()), + resourcePaths.commitTable(identifier.getDatabaseName()), request, CommitTableResponse.class, headers()); diff --git a/paimon-core/src/main/java/org/apache/paimon/rest/ResourcePaths.java b/paimon-core/src/main/java/org/apache/paimon/rest/ResourcePaths.java index 567a166b7a7e..d77475fe40dc 100644 --- a/paimon-core/src/main/java/org/apache/paimon/rest/ResourcePaths.java +++ b/paimon-core/src/main/java/org/apache/paimon/rest/ResourcePaths.java @@ -66,8 +66,8 @@ public String renameTable(String databaseName) { return SLASH.join(V1, prefix, DATABASES, databaseName, TABLES, "rename"); } - public String commitTable(String databaseName, String tableName) { - return SLASH.join(V1, prefix, DATABASES, databaseName, TABLES, tableName, "commit"); + public String commitTable(String databaseName) { + return SLASH.join(V1, prefix, DATABASES, databaseName, TABLES, "commit"); } public String partitions(String databaseName, String tableName) { 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 99244d815b2c..91024867b7ea 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 @@ -109,7 +109,6 @@ public static Dispatcher initDispatcher(Catalog catalog, String authToken) { @Override public MockResponse dispatch(RecordedRequest request) { String token = request.getHeaders().get("Authorization"); - System.out.println(request.getPath()); RESTResponse response; try { if (!("Bearer " + authToken).equals(token)) { @@ -130,7 +129,6 @@ public MockResponse dispatch(RecordedRequest request) { boolean isViews = resources.length == 2 && "views".equals(resources[1]); boolean isTables = resources.length == 2 && "tables".equals(resources[1]); boolean isTableRename = - resources.length == 3 && "tables".equals(resources[1]) && "rename".equals(resources[2]); @@ -145,9 +143,12 @@ public MockResponse dispatch(RecordedRequest request) { boolean isTable = resources.length == 3 && "tables".equals(resources[1]) - && !"rename".equals(resources[2]); + && !"rename".equals(resources[2]) + && !"commit".equals(resources[2]); boolean isTableCommit = - resources.length == 4 && "commit".equals(resources[3]); + resources.length == 3 + && "tables".equals(resources[1]) + && "commit".equals(resources[2]); boolean isPartitions = resources.length == 4 && "tables".equals(resources[1]) @@ -214,7 +215,6 @@ public MockResponse dispatch(RecordedRequest request) { } else if (isViews) { return viewsApiHandler(catalog, request, databaseName); } else if (isViewRename) { - System.out.println("View rename"); return renameViewApiHandler(catalog, request); } else if (isView) { String viewName = resources[2]; diff --git a/paimon-open-api/rest-catalog-open-api.yaml b/paimon-open-api/rest-catalog-open-api.yaml index d0468516c9c5..02ea7de8d0df 100644 --- a/paimon-open-api/rest-catalog-open-api.yaml +++ b/paimon-open-api/rest-catalog-open-api.yaml @@ -389,7 +389,7 @@ paths: $ref: '#/components/schemas/ErrorResponse' "500": description: Internal Server Error - /v1/{prefix}/databases/{database}/tables/{table}/commit: + /v1/{prefix}/databases/{database}/tables/commit: post: tags: - table @@ -406,11 +406,6 @@ paths: required: true schema: type: string - - name: table - in: path - required: true - schema: - type: string requestBody: content: application/json: @@ -1120,8 +1115,52 @@ components: properties: version: type: integer + format: int32 + nullable: true id: type: integer + format: int64 + schemaId: + type: integer + format: int64 + baseManifestList: + type: string + deltaManifestList: + type: string + changelogManifestList: + type: string + nullable: true + indexManifest: + type: string + commitUser: + type: string + commitIdentifier: + type: string + commitKind: + type: string + enum: ["APPEND", "COMPACT", "OVERWRITE", "ANALYZE"] + timeMillis: + type: integer + format: int64 + logOffsets: + type: object + additionalProperties: + type: integer + format: int64 + totalRecordCount: + type: integer + format: int64 + deltaRecordCount: + type: integer + format: int64 + changelogRecordCount: + type: integer + format: int64 + watermark: + type: integer + format: int64 + statistics: + type: string CommitTableResponse: 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 8ba25b18305f..e657407a47c3 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 @@ -347,11 +347,10 @@ public void renameTable( responseCode = "500", content = {@Content(schema = @Schema())}) }) - @PostMapping("/v1/{prefix}/databases/{database}/tables/{table}/commit") + @PostMapping("/v1/{prefix}/databases/{database}/tables/commit") public CommitTableResponse commitTable( @PathVariable String prefix, @PathVariable String database, - @PathVariable String table, @RequestBody CommitTableRequest request) { return new CommitTableResponse(true); } From 90e0d48d1cf33112ce1629e4095f9b6a3313cc50 Mon Sep 17 00:00:00 2001 From: yantian Date: Wed, 15 Jan 2025 12:35:10 +0800 Subject: [PATCH 11/11] format --- paimon-common/src/main/java/org/apache/paimon/types/RowType.java | 1 + 1 file changed, 1 insertion(+) diff --git a/paimon-common/src/main/java/org/apache/paimon/types/RowType.java b/paimon-common/src/main/java/org/apache/paimon/types/RowType.java index 04ff200a5312..d0c0881b6aa3 100644 --- a/paimon-common/src/main/java/org/apache/paimon/types/RowType.java +++ b/paimon-common/src/main/java/org/apache/paimon/types/RowType.java @@ -53,6 +53,7 @@ public final class RowType extends DataType { private static final long serialVersionUID = 1L; + private static final String FIELD_FIELDS = "fields"; public static final String FORMAT = "ROW<%s>";