Skip to content

Commit 13b49bf

Browse files
committed
[core] Add load version snapshot and list snapshots to Catalog
1 parent 25e06f2 commit 13b49bf

File tree

11 files changed

+515
-8
lines changed

11 files changed

+515
-8
lines changed

docs/static/rest-catalog-open-api.yaml

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -732,6 +732,102 @@ paths:
732732
$ref: '#/components/examples/SnapshotNotExistError'
733733
"500":
734734
$ref: '#/components/responses/ServerErrorResponse'
735+
/v1/{prefix}/databases/{database}/tables/{table}/snapshots/{version}:
736+
get:
737+
tags:
738+
- table
739+
summary: Get version snapshot
740+
operationId: getVersionSnapshot
741+
parameters:
742+
- name: prefix
743+
in: path
744+
required: true
745+
schema:
746+
type: string
747+
- name: database
748+
in: path
749+
required: true
750+
schema:
751+
type: string
752+
- name: table
753+
in: path
754+
required: true
755+
schema:
756+
type: string
757+
- name: version
758+
in: path
759+
required: true
760+
schema:
761+
type: string
762+
responses:
763+
"200":
764+
description: OK
765+
content:
766+
application/json:
767+
schema:
768+
$ref: '#/components/schemas/GetVersionSnapshotResponse'
769+
"401":
770+
$ref: '#/components/responses/UnauthorizedErrorResponse'
771+
404:
772+
description:
773+
Not Found
774+
- TableNotExistException, table does not exist
775+
- SnapshotNotExistException, the requested snapshot does not exist
776+
content:
777+
application/json:
778+
schema:
779+
$ref: '#/components/responses/ResourceNotExistErrorResponse'
780+
examples:
781+
TableNotExist:
782+
$ref: '#/components/examples/TableNotExistError'
783+
SnapshotNotExist:
784+
$ref: '#/components/examples/SnapshotNotExistError'
785+
"500":
786+
$ref: '#/components/responses/ServerErrorResponse'
787+
/v1/{prefix}/databases/{database}/tables/{table}/snapshots:
788+
get:
789+
tags:
790+
- table
791+
summary: List snapshots
792+
operationId: listSnapshots
793+
parameters:
794+
- name: prefix
795+
in: path
796+
required: true
797+
schema:
798+
type: string
799+
- name: database
800+
in: path
801+
required: true
802+
schema:
803+
type: string
804+
- name: table
805+
in: path
806+
required: true
807+
schema:
808+
type: string
809+
- name: maxResults
810+
in: query
811+
schema:
812+
type: integer
813+
format: int32
814+
- name: pageToken
815+
in: query
816+
schema:
817+
type: string
818+
responses:
819+
"200":
820+
description: OK
821+
content:
822+
application/json:
823+
schema:
824+
$ref: '#/components/schemas/ListSnapshotsResponse'
825+
"401":
826+
$ref: '#/components/responses/UnauthorizedErrorResponse'
827+
"404":
828+
$ref: '#/components/responses/TableNotExistErrorResponse'
829+
"500":
830+
$ref: '#/components/responses/ServerErrorResponse'
735831
/v1/{prefix}/databases/{database}/tables/{table}/partitions:
736832
get:
737833
tags:
@@ -2428,6 +2524,20 @@ components:
24282524
properties:
24292525
snapshot:
24302526
$ref: '#/components/schemas/TableSnapshot'
2527+
GetVersionSnapshotResponse:
2528+
type: object
2529+
properties:
2530+
snapshot:
2531+
$ref: '#/components/schemas/Snapshot'
2532+
ListSnapshotsResponse:
2533+
type: object
2534+
properties:
2535+
snapshots:
2536+
type: array
2537+
items:
2538+
$ref: '#/components/schemas/Snapshot'
2539+
nextPageToken:
2540+
type: string
24312541
AuthTableQueryRequest:
24322542
type: object
24332543
properties:

paimon-api/src/main/java/org/apache/paimon/rest/RESTApi.java

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,11 +56,13 @@
5656
import org.apache.paimon.rest.responses.GetTableResponse;
5757
import org.apache.paimon.rest.responses.GetTableSnapshotResponse;
5858
import org.apache.paimon.rest.responses.GetTableTokenResponse;
59+
import org.apache.paimon.rest.responses.GetVersionSnapshotResponse;
5960
import org.apache.paimon.rest.responses.GetViewResponse;
6061
import org.apache.paimon.rest.responses.ListBranchesResponse;
6162
import org.apache.paimon.rest.responses.ListDatabasesResponse;
6263
import org.apache.paimon.rest.responses.ListFunctionsResponse;
6364
import org.apache.paimon.rest.responses.ListPartitionsResponse;
65+
import org.apache.paimon.rest.responses.ListSnapshotsResponse;
6466
import org.apache.paimon.rest.responses.ListTableDetailsResponse;
6567
import org.apache.paimon.rest.responses.ListTablesResponse;
6668
import org.apache.paimon.rest.responses.ListViewDetailsResponse;
@@ -462,7 +464,7 @@ public GetTableResponse getTable(Identifier identifier) {
462464
* Load latest snapshot for table.
463465
*
464466
* @param identifier database name and table name.
465-
* @return {@link TableSnapshot} Optional snapshot.
467+
* @return {@link TableSnapshot} snapshot with statistics.
466468
* @throws NoSuchResourceException Exception thrown on HTTP 404 means the table or the latest
467469
* snapshot not exists
468470
* @throws ForbiddenException Exception thrown on HTTP 403 means don't have the permission for
@@ -478,6 +480,66 @@ public TableSnapshot loadSnapshot(Identifier identifier) {
478480
return response.getSnapshot();
479481
}
480482

483+
/**
484+
* Return the snapshot of table for given version. Version parsing order is:
485+
*
486+
* <ul>
487+
* <li>1. If it is 'EARLIEST', get the earliest snapshot.
488+
* <li>2. If it is 'LATEST', get the latest snapshot.
489+
* <li>3. If it is a number, get snapshot by snapshot id.
490+
* <li>4. Else try to get snapshot from Tag name.
491+
* </ul>
492+
*
493+
* @param identifier database name and table name.
494+
* @param version version to snapshot
495+
* @return Optional snapshot.
496+
* @throws NoSuchResourceException Exception thrown on HTTP 404 means the table or the snapshot
497+
* not exists
498+
* @throws ForbiddenException Exception thrown on HTTP 403 means don't have the permission for
499+
* this table
500+
*/
501+
public Snapshot loadSnapshot(Identifier identifier, String version) {
502+
GetVersionSnapshotResponse response =
503+
client.get(
504+
resourcePaths.tableSnapshot(
505+
identifier.getDatabaseName(), identifier.getObjectName(), version),
506+
GetVersionSnapshotResponse.class,
507+
restAuthFunction);
508+
return response.getSnapshot();
509+
}
510+
511+
/**
512+
* Get paged snapshot list of the table, the snapshot list will be returned in descending order.
513+
*
514+
* @param identifier path of the table to list partitions
515+
* @param maxResults Optional parameter indicating the maximum number of results to include in
516+
* the result. If maxResults is not specified or set to 0, will return the default number of
517+
* max results.
518+
* @param pageToken Optional parameter indicating the next page token allows list to be start
519+
* from a specific point.
520+
* @return a list of the snapshots with provided page size(@param maxResults) in this table and
521+
* next page token.
522+
* @throws NoSuchResourceException Exception thrown on HTTP 404 means the table or the latest
523+
* snapshot not exists
524+
* @throws ForbiddenException Exception thrown on HTTP 403 means don't have the permission for
525+
* this table
526+
*/
527+
public PagedList<Snapshot> listSnapshotsPaged(
528+
Identifier identifier, @Nullable Integer maxResults, @Nullable String pageToken) {
529+
ListSnapshotsResponse response =
530+
client.get(
531+
resourcePaths.snapshots(
532+
identifier.getDatabaseName(), identifier.getObjectName()),
533+
buildPagedQueryParams(maxResults, pageToken),
534+
ListSnapshotsResponse.class,
535+
restAuthFunction);
536+
List<Snapshot> snapshots = response.getSnapshots();
537+
if (snapshots == null) {
538+
return new PagedList<>(emptyList(), null);
539+
}
540+
return new PagedList<>(snapshots, response.getNextPageToken());
541+
}
542+
481543
/**
482544
* Commit snapshot for table.
483545
*

paimon-api/src/main/java/org/apache/paimon/rest/ResourcePaths.java

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ public class ResourcePaths {
3232
protected static final String TABLES = "tables";
3333
protected static final String PARTITIONS = "partitions";
3434
protected static final String BRANCHES = "branches";
35+
protected static final String SNAPSHOTS = "snapshots";
3536
protected static final String VIEWS = "views";
3637
protected static final String TABLE_DETAILS = "table-details";
3738
protected static final String VIEW_DETAILS = "view-details";
@@ -132,6 +133,29 @@ public String tableSnapshot(String databaseName, String objectName) {
132133
"snapshot");
133134
}
134135

136+
public String tableSnapshot(String databaseName, String objectName, String version) {
137+
return SLASH.join(
138+
V1,
139+
prefix,
140+
DATABASES,
141+
encodeString(databaseName),
142+
TABLES,
143+
encodeString(objectName),
144+
SNAPSHOTS,
145+
version);
146+
}
147+
148+
public String snapshots(String databaseName, String objectName) {
149+
return SLASH.join(
150+
V1,
151+
prefix,
152+
DATABASES,
153+
encodeString(databaseName),
154+
TABLES,
155+
encodeString(objectName),
156+
SNAPSHOTS);
157+
}
158+
135159
public String authTable(String databaseName, String objectName) {
136160
return SLASH.join(
137161
V1,
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
package org.apache.paimon.rest.responses;
20+
21+
import org.apache.paimon.Snapshot;
22+
import org.apache.paimon.rest.RESTResponse;
23+
24+
import org.apache.paimon.shade.jackson2.com.fasterxml.jackson.annotation.JsonCreator;
25+
import org.apache.paimon.shade.jackson2.com.fasterxml.jackson.annotation.JsonGetter;
26+
import org.apache.paimon.shade.jackson2.com.fasterxml.jackson.annotation.JsonIgnoreProperties;
27+
import org.apache.paimon.shade.jackson2.com.fasterxml.jackson.annotation.JsonProperty;
28+
29+
/** Response for table snapshot by a version. */
30+
@JsonIgnoreProperties(ignoreUnknown = true)
31+
public class GetVersionSnapshotResponse implements RESTResponse {
32+
33+
private static final String FIELD_SNAPSHOT = "snapshot";
34+
35+
@JsonProperty(FIELD_SNAPSHOT)
36+
private final Snapshot snapshot;
37+
38+
@JsonCreator
39+
public GetVersionSnapshotResponse(@JsonProperty(FIELD_SNAPSHOT) Snapshot snapshot) {
40+
this.snapshot = snapshot;
41+
}
42+
43+
@JsonGetter(FIELD_SNAPSHOT)
44+
public Snapshot getSnapshot() {
45+
return snapshot;
46+
}
47+
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
package org.apache.paimon.rest.responses;
20+
21+
import org.apache.paimon.Snapshot;
22+
23+
import org.apache.paimon.shade.jackson2.com.fasterxml.jackson.annotation.JsonCreator;
24+
import org.apache.paimon.shade.jackson2.com.fasterxml.jackson.annotation.JsonGetter;
25+
import org.apache.paimon.shade.jackson2.com.fasterxml.jackson.annotation.JsonIgnoreProperties;
26+
import org.apache.paimon.shade.jackson2.com.fasterxml.jackson.annotation.JsonProperty;
27+
28+
import java.util.List;
29+
30+
/** Response for list snapshots. */
31+
@JsonIgnoreProperties(ignoreUnknown = true)
32+
public class ListSnapshotsResponse implements PagedResponse<Snapshot> {
33+
34+
private static final String FIELD_SNAPSHOTS = "snapshots";
35+
private static final String FIELD_NEXT_PAGE_TOKEN = "nextPageToken";
36+
37+
@JsonProperty(FIELD_SNAPSHOTS)
38+
private final List<Snapshot> snapshots;
39+
40+
@JsonProperty(FIELD_NEXT_PAGE_TOKEN)
41+
private final String nextPageToken;
42+
43+
public ListSnapshotsResponse(@JsonProperty(FIELD_SNAPSHOTS) List<Snapshot> snapshots) {
44+
this(snapshots, null);
45+
}
46+
47+
@JsonCreator
48+
public ListSnapshotsResponse(
49+
@JsonProperty(FIELD_SNAPSHOTS) List<Snapshot> snapshots,
50+
@JsonProperty(FIELD_NEXT_PAGE_TOKEN) String nextPageToken) {
51+
this.snapshots = snapshots;
52+
this.nextPageToken = nextPageToken;
53+
}
54+
55+
@JsonGetter(FIELD_SNAPSHOTS)
56+
public List<Snapshot> getSnapshots() {
57+
return this.snapshots;
58+
}
59+
60+
@Override
61+
public List<Snapshot> data() {
62+
return snapshots;
63+
}
64+
65+
@JsonGetter(FIELD_NEXT_PAGE_TOKEN)
66+
public String getNextPageToken() {
67+
return this.nextPageToken;
68+
}
69+
}

paimon-core/src/main/java/org/apache/paimon/catalog/AbstractCatalog.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -486,6 +486,17 @@ public Optional<TableSnapshot> loadSnapshot(Identifier identifier) {
486486
throw new UnsupportedOperationException();
487487
}
488488

489+
@Override
490+
public Optional<Snapshot> loadSnapshot(Identifier identifier, String version) {
491+
throw new UnsupportedOperationException();
492+
}
493+
494+
@Override
495+
public PagedList<Snapshot> listSnapshotsPaged(
496+
Identifier identifier, @Nullable Integer maxResults, @Nullable String pageToken) {
497+
throw new UnsupportedOperationException();
498+
}
499+
489500
@Override
490501
public void rollbackTo(Identifier identifier, Instant instant)
491502
throws Catalog.TableNotExistException {

0 commit comments

Comments
 (0)