Skip to content

Commit 66396f7

Browse files
authored
[core] RESTCatalog: fix NotImplementedException handle when create and alter table (#5274)
1 parent 8a8a953 commit 66396f7

File tree

14 files changed

+326
-40
lines changed

14 files changed

+326
-40
lines changed

paimon-core/src/main/java/org/apache/paimon/rest/RESTCatalog.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@
7575
import org.apache.paimon.schema.TableSchema;
7676
import org.apache.paimon.table.Table;
7777
import org.apache.paimon.table.TableSnapshot;
78+
import org.apache.paimon.table.system.SystemTableLoader;
7879
import org.apache.paimon.utils.Pair;
7980
import org.apache.paimon.view.View;
8081
import org.apache.paimon.view.ViewImpl;
@@ -286,6 +287,9 @@ public void alterDatabase(String name, List<PropertyChange> changes, boolean ign
286287
@Override
287288
public List<String> listTables(String databaseName) throws DatabaseNotExistException {
288289
try {
290+
if (isSystemDatabase(databaseName)) {
291+
return SystemTableLoader.loadGlobalTableNames();
292+
}
289293
return listDataFromPageApi(
290294
queryParams ->
291295
client.get(
@@ -465,6 +469,8 @@ public void createTable(Identifier identifier, Schema schema, boolean ignoreIfEx
465469
if (!ignoreIfExists) {
466470
throw new TableAlreadyExistException(identifier);
467471
}
472+
} catch (NotImplementedException e) {
473+
throw new RuntimeException(new UnsupportedOperationException(e.getMessage()));
468474
} catch (NoSuchResourceException e) {
469475
throw new DatabaseNotExistException(identifier.getDatabaseName());
470476
} catch (BadRequestException e) {
@@ -522,6 +528,8 @@ public void alterTable(
522528
throw new TableNoPermissionException(identifier, e);
523529
} catch (ServiceFailureException e) {
524530
throw new IllegalStateException(e.getMessage());
531+
} catch (NotImplementedException e) {
532+
throw new UnsupportedOperationException(e.getMessage());
525533
} catch (BadRequestException e) {
526534
throw new RuntimeException(new IllegalArgumentException(e.getMessage()));
527535
}

paimon-core/src/main/java/org/apache/paimon/rest/auth/DLFAuthProvider.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -89,9 +89,11 @@ public DLFAuthProvider(
8989
public Map<String, String> header(
9090
Map<String, String> baseHeader, RESTAuthParameter restAuthParameter) {
9191
try {
92-
ZonedDateTime now = ZonedDateTime.now(ZoneOffset.UTC);
93-
String date = now.format(AUTH_DATE_FORMATTER);
94-
String dateTime = now.format(AUTH_DATE_TIME_FORMATTER);
92+
String dateTime =
93+
baseHeader.getOrDefault(
94+
DLF_DATE_HEADER_KEY.toLowerCase(),
95+
ZonedDateTime.now(ZoneOffset.UTC).format(AUTH_DATE_TIME_FORMATTER));
96+
String date = dateTime.substring(0, 8);
9597
Map<String, String> signHeaders =
9698
generateSignHeaders(
9799
restAuthParameter.data(), dateTime, token.getSecurityToken());
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
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.rest.RESTResponse;
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.JsonProperty;
26+
27+
/** Base class for database, table, view, audit response. */
28+
public abstract class BaseResourceAuditResponse implements RESTResponse {
29+
protected static final String FIELD_OWNER = "owner";
30+
protected static final String FIELD_CREATED_AT = "createdAt";
31+
protected static final String FIELD_CREATED_BY = "createdBy";
32+
protected static final String FIELD_UPDATED_AT = "updatedAt";
33+
protected static final String FIELD_UPDATED_BY = "updatedBy";
34+
35+
@JsonProperty(FIELD_OWNER)
36+
private final String owner;
37+
38+
@JsonProperty(FIELD_CREATED_AT)
39+
private final long createdAt;
40+
41+
@JsonProperty(FIELD_CREATED_BY)
42+
private final String createdBy;
43+
44+
@JsonProperty(FIELD_UPDATED_AT)
45+
private final long updatedAt;
46+
47+
@JsonProperty(FIELD_UPDATED_BY)
48+
private final String updatedBy;
49+
50+
@JsonCreator
51+
public BaseResourceAuditResponse(
52+
@JsonProperty(FIELD_OWNER) String owner,
53+
@JsonProperty(FIELD_CREATED_AT) long createdAt,
54+
@JsonProperty(FIELD_CREATED_BY) String createdBy,
55+
@JsonProperty(FIELD_UPDATED_AT) long updatedAt,
56+
@JsonProperty(FIELD_UPDATED_BY) String updatedBy) {
57+
this.owner = owner;
58+
this.createdAt = createdAt;
59+
this.createdBy = createdBy;
60+
this.updatedAt = updatedAt;
61+
this.updatedBy = updatedBy;
62+
}
63+
64+
@JsonGetter(FIELD_OWNER)
65+
public String getOwner() {
66+
return owner;
67+
}
68+
69+
@JsonGetter(FIELD_CREATED_AT)
70+
public long getCreatedAt() {
71+
return createdAt;
72+
}
73+
74+
@JsonGetter(FIELD_CREATED_BY)
75+
public String getCreatedBy() {
76+
return createdBy;
77+
}
78+
79+
@JsonGetter(FIELD_UPDATED_AT)
80+
public long getUpdatedAt() {
81+
return updatedAt;
82+
}
83+
84+
@JsonGetter(FIELD_UPDATED_BY)
85+
public String getUpdatedBy() {
86+
return updatedBy;
87+
}
88+
}

paimon-core/src/main/java/org/apache/paimon/rest/responses/GetDatabaseResponse.java

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@
3333

3434
/** Response for getting database. */
3535
@JsonIgnoreProperties(ignoreUnknown = true)
36-
public class GetDatabaseResponse implements RESTResponse, Database {
36+
public class GetDatabaseResponse extends BaseResourceAuditResponse
37+
implements RESTResponse, Database {
3738

3839
private static final String FIELD_ID = "id";
3940
private static final String FIELD_NAME = "name";
@@ -52,7 +53,13 @@ public class GetDatabaseResponse implements RESTResponse, Database {
5253
public GetDatabaseResponse(
5354
@JsonProperty(FIELD_ID) String id,
5455
@JsonProperty(FIELD_NAME) String name,
55-
@JsonProperty(FIELD_OPTIONS) Map<String, String> options) {
56+
@JsonProperty(FIELD_OPTIONS) Map<String, String> options,
57+
@JsonProperty(FIELD_OWNER) String owner,
58+
@JsonProperty(FIELD_CREATED_AT) long createdAt,
59+
@JsonProperty(FIELD_CREATED_BY) String createdBy,
60+
@JsonProperty(FIELD_UPDATED_AT) long updatedAt,
61+
@JsonProperty(FIELD_UPDATED_BY) String updatedBy) {
62+
super(owner, createdAt, createdBy, updatedAt, updatedBy);
5663
this.id = id;
5764
this.name = name;
5865
this.options = options;

paimon-core/src/main/java/org/apache/paimon/rest/responses/GetTableResponse.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828

2929
/** Response for getting table. */
3030
@JsonIgnoreProperties(ignoreUnknown = true)
31-
public class GetTableResponse implements RESTResponse {
31+
public class GetTableResponse extends BaseResourceAuditResponse implements RESTResponse {
3232

3333
private static final String FIELD_ID = "id";
3434
private static final String FIELD_NAME = "name";
@@ -57,7 +57,13 @@ public GetTableResponse(
5757
@JsonProperty(FIELD_NAME) String name,
5858
@JsonProperty(FIELD_IS_EXTERNAL) boolean isExternal,
5959
@JsonProperty(FIELD_SCHEMA_ID) long schemaId,
60-
@JsonProperty(FIELD_SCHEMA) Schema schema) {
60+
@JsonProperty(FIELD_SCHEMA) Schema schema,
61+
@JsonProperty(FIELD_OWNER) String owner,
62+
@JsonProperty(FIELD_CREATED_AT) long createdAt,
63+
@JsonProperty(FIELD_CREATED_BY) String createdBy,
64+
@JsonProperty(FIELD_UPDATED_AT) long updatedAt,
65+
@JsonProperty(FIELD_UPDATED_BY) String updatedBy) {
66+
super(owner, createdAt, createdBy, updatedAt, updatedBy);
6167
this.id = id;
6268
this.name = name;
6369
this.isExternal = isExternal;

paimon-core/src/main/java/org/apache/paimon/rest/responses/GetViewResponse.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828

2929
/** Response for getting view. */
3030
@JsonIgnoreProperties(ignoreUnknown = true)
31-
public class GetViewResponse implements RESTResponse {
31+
public class GetViewResponse extends BaseResourceAuditResponse implements RESTResponse {
3232

3333
private static final String FIELD_ID = "id";
3434
private static final String FIELD_NAME = "name";
@@ -47,7 +47,13 @@ public class GetViewResponse implements RESTResponse {
4747
public GetViewResponse(
4848
@JsonProperty(FIELD_ID) String id,
4949
@JsonProperty(FIELD_NAME) String name,
50-
@JsonProperty(FIELD_SCHEMA) ViewSchema schema) {
50+
@JsonProperty(FIELD_SCHEMA) ViewSchema schema,
51+
@JsonProperty(FIELD_OWNER) String owner,
52+
@JsonProperty(FIELD_CREATED_AT) long createdAt,
53+
@JsonProperty(FIELD_CREATED_BY) String createdBy,
54+
@JsonProperty(FIELD_UPDATED_AT) long updatedAt,
55+
@JsonProperty(FIELD_UPDATED_BY) String updatedBy) {
56+
super(owner, createdAt, createdBy, updatedAt, updatedBy);
5157
this.id = id;
5258
this.name = name;
5359
this.schema = schema;

paimon-core/src/test/java/org/apache/paimon/catalog/CatalogTestBase.java

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,12 +36,15 @@
3636
import org.apache.paimon.table.sink.BatchTableCommit;
3737
import org.apache.paimon.table.sink.BatchTableWrite;
3838
import org.apache.paimon.table.sink.BatchWriteBuilder;
39+
import org.apache.paimon.table.system.AllTableOptionsTable;
40+
import org.apache.paimon.table.system.CatalogOptionsTable;
3941
import org.apache.paimon.types.DataField;
4042
import org.apache.paimon.types.DataTypes;
4143
import org.apache.paimon.types.RowType;
4244
import org.apache.paimon.view.View;
4345
import org.apache.paimon.view.ViewImpl;
4446

47+
import org.apache.paimon.shade.guava30.com.google.common.collect.ImmutableMap;
4548
import org.apache.paimon.shade.guava30.com.google.common.collect.Lists;
4649
import org.apache.paimon.shade.guava30.com.google.common.collect.Maps;
4750

@@ -483,6 +486,20 @@ public void testCreateTable() throws Exception {
483486
.hasRootCauseInstanceOf(IllegalArgumentException.class)
484487
.hasRootCauseMessage(
485488
"Unrecognized option for boolean: max. Expected either true or false(case insensitive)");
489+
490+
// conflict options
491+
Schema conflictOptionsSchema =
492+
Schema.newBuilder()
493+
.column("a", DataTypes.INT())
494+
.options(ImmutableMap.of("changelog-producer", "input"))
495+
.build();
496+
assertThatThrownBy(
497+
() ->
498+
catalog.createTable(
499+
Identifier.create("test_db", "conflict_options_table"),
500+
conflictOptionsSchema,
501+
false))
502+
.isInstanceOf(RuntimeException.class);
486503
}
487504

488505
@Test
@@ -538,6 +555,12 @@ public void testGetTable() throws Exception {
538555
assertThatExceptionOfType(Catalog.TableNotExistException.class)
539556
.isThrownBy(
540557
() -> catalog.getTable(Identifier.create(SYSTEM_DATABASE_NAME, "1111")));
558+
559+
List<String> sysTables = catalog.listTables(SYSTEM_DATABASE_NAME);
560+
assertThat(sysTables)
561+
.containsExactlyInAnyOrder(
562+
AllTableOptionsTable.ALL_TABLE_OPTIONS,
563+
CatalogOptionsTable.CATALOG_OPTIONS);
541564
}
542565

543566
@Test
@@ -683,6 +706,19 @@ public void testAlterTable() throws Exception {
683706
anyCauseMatches(
684707
Catalog.ColumnAlreadyExistException.class,
685708
"Column col1 already exists in the test_db.test_table table."));
709+
710+
// conflict options
711+
assertThatThrownBy(
712+
() ->
713+
catalog.alterTable(
714+
identifier,
715+
Lists.newArrayList(
716+
SchemaChange.setOption(
717+
"changelog-producer", "input")),
718+
false))
719+
.isInstanceOf(RuntimeException.class)
720+
.hasMessageContaining(
721+
"Can not set changelog-producer on table without primary keys");
686722
}
687723

688724
@Test

paimon-core/src/test/java/org/apache/paimon/rest/MockRESTMessage.java

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,15 @@ public static GetDatabaseResponse getDatabaseResponse(String name) {
8282
Map<String, String> options = new HashMap<>();
8383
options.put("a", "b");
8484
options.put(COMMENT_PROP, "comment");
85-
return new GetDatabaseResponse(UUID.randomUUID().toString(), name, options);
85+
return new GetDatabaseResponse(
86+
UUID.randomUUID().toString(),
87+
name,
88+
options,
89+
"owner",
90+
System.currentTimeMillis(),
91+
"created",
92+
System.currentTimeMillis(),
93+
"updated");
8694
}
8795

8896
public static ListDatabasesResponse listDatabasesResponse(String name) {
@@ -219,7 +227,17 @@ public static GetTableResponse getTableResponse() {
219227
Map<String, String> options = new HashMap<>();
220228
options.put("option-1", "value-1");
221229
options.put("option-2", "value-2");
222-
return new GetTableResponse(UUID.randomUUID().toString(), "", false, 1, schema(options));
230+
return new GetTableResponse(
231+
UUID.randomUUID().toString(),
232+
"",
233+
false,
234+
1,
235+
schema(options),
236+
"owner",
237+
System.currentTimeMillis(),
238+
"created",
239+
System.currentTimeMillis(),
240+
"updated");
223241
}
224242

225243
public static CreateViewRequest createViewRequest(String name) {
@@ -228,7 +246,15 @@ public static CreateViewRequest createViewRequest(String name) {
228246
}
229247

230248
public static GetViewResponse getViewResponse() {
231-
return new GetViewResponse(UUID.randomUUID().toString(), "", viewSchema());
249+
return new GetViewResponse(
250+
UUID.randomUUID().toString(),
251+
"",
252+
viewSchema(),
253+
"owner",
254+
System.currentTimeMillis(),
255+
"created",
256+
System.currentTimeMillis(),
257+
"updated");
232258
}
233259

234260
public static ListViewsResponse listViewsResponse() {

0 commit comments

Comments
 (0)