This is an automated email from the ASF dual-hosted git repository.
lzljs3620320 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/paimon.git
The following commit(s) were added to refs/heads/master by this push:
new ca5d7a12f6 [core] RestCatalog: support ListTableSummaries and
ListViewSummaries (#5601)
ca5d7a12f6 is described below
commit ca5d7a12f680cb8ff76e2c7ae64a907596e82a60
Author: XiaoHongbo <[email protected]>
AuthorDate: Fri May 16 17:48:05 2025 +0800
[core] RestCatalog: support ListTableSummaries and ListViewSummaries (#5601)
---
docs/static/rest-catalog-open-api.yaml | 100 ++++-
.../main/java/org/apache/paimon/rest/RESTApi.java | 127 +++++-
.../main/java/org/apache/paimon/rest/RESTUtil.java | 5 +-
.../java/org/apache/paimon/rest/ResourcePaths.java | 8 +
.../org/apache/paimon/catalog/AbstractCatalog.java | 4 +-
.../java/org/apache/paimon/catalog/Catalog.java | 101 ++++-
.../org/apache/paimon/catalog/DelegateCatalog.java | 25 +-
.../java/org/apache/paimon/rest/RESTCatalog.java | 30 +-
.../org/apache/paimon/catalog/CatalogTestBase.java | 80 ++++
.../org/apache/paimon/rest/RESTCatalogServer.java | 204 ++++++----
.../org/apache/paimon/rest/RESTCatalogTest.java | 444 ++++++++++++++++-----
.../java/org/apache/paimon/hive/HiveCatalog.java | 1 +
12 files changed, 891 insertions(+), 238 deletions(-)
diff --git a/docs/static/rest-catalog-open-api.yaml
b/docs/static/rest-catalog-open-api.yaml
index 0ee548e926..efd483e7b6 100644
--- a/docs/static/rest-catalog-open-api.yaml
+++ b/docs/static/rest-catalog-open-api.yaml
@@ -233,7 +233,7 @@ paths:
schema:
type: string
- name: tableNamePattern
- description: A sql LIKE pattern (% and _) for table names.
+ description: A sql LIKE pattern (%) for table names. Currently, only
prefix matching is supported.
in: query
schema:
type: string
@@ -311,7 +311,7 @@ paths:
schema:
type: string
- name: tableNamePattern
- description: A sql LIKE pattern (% and _) for table names.
+ description: A sql LIKE pattern (%) for table names. Currently, only
prefix matching is supported.
in: query
schema:
type: string
@@ -328,6 +328,50 @@ paths:
$ref: '#/components/responses/DatabaseNotExistErrorResponse'
"500":
$ref: '#/components/responses/ServerErrorResponse'
+ /v1/{prefix}/tables:
+ get:
+ tags:
+ - table
+ summary: List tables globally
+ operationId: List tables globally
+ description: list tables paged globally which matches the given database
name pattern and table name pattern both.
+ parameters:
+ - name: prefix
+ in: path
+ required: true
+ schema:
+ type: string
+ - name: databaseNamePattern
+ description: A sql LIKE pattern (%) for database names. All
databases will be returned if not set or empty. Currently, only prefix matching
is supported.
+ in: path
+ schema:
+ type: string
+ - name: tableNamePattern
+ description: A sql LIKE pattern (%) for table names. All tables will
be returned if not set or empty. Currently, only prefix matching is supported.
+ in: query
+ schema:
+ type: string
+ - name: maxResults
+ in: query
+ schema:
+ type: integer
+ format: int32
+ - name: pageToken
+ in: query
+ schema:
+ type: string
+
+ responses:
+ "200":
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ListTablesResponse'
+ "401":
+ $ref: '#/components/responses/UnauthorizedErrorResponse'
+ "500":
+ $ref: '#/components/responses/ServerErrorResponse'
/v1/{prefix}/databases/{database}/tables/{table}:
get:
tags:
@@ -716,7 +760,7 @@ paths:
schema:
type: string
- name: partitionNamePattern
- description: A sql LIKE pattern (% and _) for partition names.
+ description: A sql LIKE pattern (%) for partition names. Currently,
only prefix matching is supported.
in: query
schema:
type: string
@@ -959,7 +1003,7 @@ paths:
schema:
type: string
- name: viewNamePattern
- description: A sql LIKE pattern (% and _) for view names.
+ description: A sql LIKE pattern (%) for view names. Currently, only
prefix matching is supported.
in: query
schema:
type: string
@@ -1037,7 +1081,7 @@ paths:
schema:
type: string
- name: viewNamePattern
- description: A sql LIKE pattern (% and _) for view names.
+ description: A sql LIKE pattern (%) for view names. Currently, only
prefix matching is supported.
in: query
schema:
type: string
@@ -1054,6 +1098,50 @@ paths:
$ref: '#/components/responses/DatabaseNotExistErrorResponse'
"500":
$ref: '#/components/responses/ServerErrorResponse'
+ /v1/{prefix}/view-summaries:
+ get:
+ tags:
+ - table
+ summary: List views globally
+ operationId: listViewsGlobally
+ description: List views globally which matches the given database name
pattern and view name pattern both.
+ parameters:
+ - name: prefix
+ in: path
+ required: true
+ schema:
+ type: string
+ - name: databaseNamePattern
+ description: A sql LIKE pattern (%) for database names. All
databases will be returned if not set or empty. Currently, only prefix matching
is supported.
+ in: path
+ schema:
+ type: string
+ - name: viewNamePattern
+ description: A sql LIKE pattern (%) for view names. All views will
be returned if not set or empty. Currently, only prefix matching is supported.
+ in: query
+ schema:
+ type: string
+ - name: maxResults
+ in: query
+ schema:
+ type: integer
+ format: int32
+ - name: pageToken
+ in: query
+ schema:
+ type: string
+
+ responses:
+ "200":
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ListViewsResponse'
+ "401":
+ $ref: '#/components/responses/UnauthorizedErrorResponse'
+ "500":
+ $ref: '#/components/responses/ServerErrorResponse'
/v1/{prefix}/databases/{database}/views/{view}:
get:
tags:
@@ -2383,6 +2471,7 @@ components:
type: array
items:
type: string
+ description: table name or full table name of the table. full
table name format is like "db.table"
nextPageToken:
type: string
ListTableDetailsResponse:
@@ -2460,6 +2549,7 @@ components:
type: array
items:
type: string
+ description: view name or full view name of the view. full view
name format is like "db.view"
nextPageToken:
type: string
ListViewDetailsResponse:
diff --git a/paimon-api/src/main/java/org/apache/paimon/rest/RESTApi.java
b/paimon-api/src/main/java/org/apache/paimon/rest/RESTApi.java
index f050520653..0dece3bc79 100644
--- a/paimon-api/src/main/java/org/apache/paimon/rest/RESTApi.java
+++ b/paimon-api/src/main/java/org/apache/paimon/rest/RESTApi.java
@@ -70,6 +70,7 @@ import org.apache.paimon.schema.SchemaChange;
import org.apache.paimon.table.Instant;
import org.apache.paimon.table.TableSnapshot;
import org.apache.paimon.utils.JsonSerdeUtil;
+import org.apache.paimon.utils.Pair;
import org.apache.paimon.utils.StringUtils;
import org.apache.paimon.view.ViewChange;
import org.apache.paimon.view.ViewSchema;
@@ -127,6 +128,7 @@ public class RESTApi {
public static final String MAX_RESULTS = "maxResults";
public static final String PAGE_TOKEN = "pageToken";
+ public static final String DATABASE_NAME_PATTERN = "databaseNamePattern";
public static final String TABLE_NAME_PATTERN = "tableNamePattern";
public static final String VIEW_NAME_PATTERN = "viewNamePattern";
public static final String PARTITION_NAME_PATTERN = "partitionNamePattern";
@@ -223,11 +225,16 @@ public class RESTApi {
* @return {@link PagedList}: elements and nextPageToken.
*/
public PagedList<String> listDatabasesPaged(
- @Nullable Integer maxResults, @Nullable String pageToken) {
+ @Nullable Integer maxResults,
+ @Nullable String pageToken,
+ @Nullable String databaseNamePattern) {
ListDatabasesResponse response =
client.get(
resourcePaths.databases(),
- buildPagedQueryParams(maxResults, pageToken),
+ buildPagedQueryParams(
+ maxResults,
+ pageToken,
+ Pair.of(DATABASE_NAME_PATTERN,
databaseNamePattern)),
ListDatabasesResponse.class,
restAuthFunction);
List<String> databases = response.getDatabases();
@@ -341,7 +348,9 @@ public class RESTApi {
client.get(
resourcePaths.tables(databaseName),
buildPagedQueryParams(
- maxResults, pageToken, TABLE_NAME_PATTERN,
tableNamePattern),
+ maxResults,
+ pageToken,
+ Pair.of(TABLE_NAME_PATTERN, tableNamePattern)),
ListTablesResponse.class,
restAuthFunction);
List<String> tables = response.getTables();
@@ -379,7 +388,9 @@ public class RESTApi {
client.get(
resourcePaths.tableDetails(databaseName),
buildPagedQueryParams(
- maxResults, pageToken, TABLE_NAME_PATTERN,
tableNamePattern),
+ maxResults,
+ pageToken,
+ Pair.of(TABLE_NAME_PATTERN, tableNamePattern)),
ListTableDetailsResponse.class,
restAuthFunction);
List<GetTableResponse> tables = response.getTableDetails();
@@ -389,6 +400,47 @@ public class RESTApi {
return new PagedList<>(tables, response.getNextPageToken());
}
+ /**
+ * List table for a catalog.
+ *
+ * <p>Gets an array of table for a catalog. There is no guarantee of a
specific ordering of the
+ * elements in the array.
+ *
+ * @param databaseNamePattern A sql LIKE pattern (%) for database names.
All databases will be
+ * returned if not set or empty. Currently, only prefix matching is
supported.
+ * @param tableNamePattern A sql LIKE pattern (%) for table names. All
tables will be returned
+ * if not set or empty. Currently, only prefix matching is supported.
+ * @param maxResults Optional parameter indicating the maximum number of
results to include in
+ * the result. If maxResults is not specified or set to 0, will return
the default number of
+ * max results.
+ * @param pageToken Optional parameter indicating the next page token
allows list to be start
+ * from a specific point.
+ * @return {@link PagedList}: elements and nextPageToken.
+ * @throws ForbiddenException Exception thrown on HTTP 403 means don't
have the permission for
+ * this database
+ */
+ public PagedList<String> listTablesPagedGlobally(
+ @Nullable String databaseNamePattern,
+ @Nullable String tableNamePattern,
+ @Nullable Integer maxResults,
+ @Nullable String pageToken) {
+ ListTablesResponse response =
+ client.get(
+ resourcePaths.tables(),
+ buildPagedQueryParams(
+ maxResults,
+ pageToken,
+ Pair.of(DATABASE_NAME_PATTERN,
databaseNamePattern),
+ Pair.of(TABLE_NAME_PATTERN, tableNamePattern)),
+ ListTablesResponse.class,
+ restAuthFunction);
+ List<String> tables = response.getTables();
+ if (tables == null) {
+ return new PagedList<>(emptyList(), null);
+ }
+ return new PagedList<>(tables, response.getNextPageToken());
+ }
+
/**
* Get table.
*
@@ -614,8 +666,7 @@ public class RESTApi {
buildPagedQueryParams(
maxResults,
pageToken,
- PARTITION_NAME_PATTERN,
- partitionNamePattern),
+ Pair.of(PARTITION_NAME_PATTERN,
partitionNamePattern)),
ListPartitionsResponse.class,
restAuthFunction);
List<Partition> partitions = response.getPartitions();
@@ -829,7 +880,7 @@ public class RESTApi {
client.get(
resourcePaths.views(databaseName),
buildPagedQueryParams(
- maxResults, pageToken, VIEW_NAME_PATTERN,
viewNamePattern),
+ maxResults, pageToken,
Pair.of(VIEW_NAME_PATTERN, viewNamePattern)),
ListViewsResponse.class,
restAuthFunction);
List<String> views = response.getViews();
@@ -867,7 +918,7 @@ public class RESTApi {
client.get(
resourcePaths.viewDetails(databaseName),
buildPagedQueryParams(
- maxResults, pageToken, VIEW_NAME_PATTERN,
viewNamePattern),
+ maxResults, pageToken,
Pair.of(VIEW_NAME_PATTERN, viewNamePattern)),
ListViewDetailsResponse.class,
restAuthFunction);
List<GetViewResponse> views = response.getViewDetails();
@@ -877,6 +928,47 @@ public class RESTApi {
return new PagedList<>(views, response.getNextPageToken());
}
+ /**
+ * List views for a catalog.
+ *
+ * <p>Gets an array of views for a catalog. There is no guarantee of a
specific ordering of the
+ * elements in the array.
+ *
+ * @param databaseNamePattern A sql LIKE pattern (%) for database names.
All databases will be
+ * returned if not set or empty. Currently, only prefix matching is
supported.
+ * @param viewNamePattern A sql LIKE pattern (%) for view names. All views
will be returned if
+ * not set or empty. Currently, only prefix matching is supported.
+ * @param maxResults Optional parameter indicating the maximum number of
results to include in
+ * the result. If maxResults is not specified or set to 0, will return
the default number of
+ * max results.
+ * @param pageToken Optional parameter indicating the next page token
allows list to be start
+ * from a specific point.
+ * @return {@link PagedList}: elements and nextPageToken.
+ * @throws ForbiddenException Exception thrown on HTTP 403 means don't
have the permission for
+ * this database
+ */
+ public PagedList<String> listViewsPagedGlobally(
+ @Nullable String databaseNamePattern,
+ @Nullable String viewNamePattern,
+ @Nullable Integer maxResults,
+ @Nullable String pageToken) {
+ ListViewsResponse response =
+ client.get(
+ resourcePaths.views(),
+ buildPagedQueryParams(
+ maxResults,
+ pageToken,
+ Pair.of(DATABASE_NAME_PATTERN,
databaseNamePattern),
+ Pair.of(VIEW_NAME_PATTERN, viewNamePattern)),
+ ListViewsResponse.class,
+ restAuthFunction);
+ List<String> views = response.getViews();
+ if (views == null) {
+ return new PagedList<>(emptyList(), null);
+ }
+ return new PagedList<>(views, response.getNextPageToken());
+ }
+
/**
* Rename view.
*
@@ -960,14 +1052,14 @@ public class RESTApi {
private Map<String, String> buildPagedQueryParams(
@Nullable Integer maxResults, @Nullable String pageToken) {
- return buildPagedQueryParams(maxResults, pageToken, null, null);
+ return buildPagedQueryParams(maxResults, pageToken, null);
}
- private Map<String, String> buildPagedQueryParams(
+ @SafeVarargs
+ private final Map<String, String> buildPagedQueryParams(
@Nullable Integer maxResults,
@Nullable String pageToken,
- @Nullable String namePatternKey,
- @Nullable String namePatternValue) {
+ @Nullable Pair<String, String>... namePatternPairs) {
Map<String, String> queryParams = Maps.newHashMap();
if (Objects.nonNull(maxResults) && maxResults > 0) {
queryParams.put(MAX_RESULTS, maxResults.toString());
@@ -975,8 +1067,15 @@ public class RESTApi {
if (Objects.nonNull(pageToken)) {
queryParams.put(PAGE_TOKEN, pageToken);
}
- if (Objects.nonNull(namePatternValue)) {
- queryParams.put(namePatternKey, namePatternValue);
+ if (Objects.nonNull(namePatternPairs)) {
+ for (Pair<String, String> namePatternPair : namePatternPairs) {
+ String namePatternKey = namePatternPair.getKey();
+ String namePatternValue = namePatternPair.getValue();
+ if (StringUtils.isNotEmpty(namePatternKey)
+ && StringUtils.isNotEmpty(namePatternValue)) {
+ queryParams.put(namePatternKey, namePatternValue);
+ }
+ }
}
return queryParams;
}
diff --git a/paimon-api/src/main/java/org/apache/paimon/rest/RESTUtil.java
b/paimon-api/src/main/java/org/apache/paimon/rest/RESTUtil.java
index dd42394d04..2b17a07af5 100644
--- a/paimon-api/src/main/java/org/apache/paimon/rest/RESTUtil.java
+++ b/paimon-api/src/main/java/org/apache/paimon/rest/RESTUtil.java
@@ -116,13 +116,12 @@ public class RESTUtil {
continue;
}
- if (c == '%' || c == '_') {
+ if (c == '%') {
inWildcardZone = true;
} else {
if (inWildcardZone) {
throw new IllegalArgumentException(
- "Can only support sql like prefix query now. "
- + "Note please escape the underline if
you want to match it exactly");
+ "Can only support prefix sql like pattern
query now.");
}
}
}
diff --git a/paimon-api/src/main/java/org/apache/paimon/rest/ResourcePaths.java
b/paimon-api/src/main/java/org/apache/paimon/rest/ResourcePaths.java
index ac79e977cb..a378b8dd76 100644
--- a/paimon-api/src/main/java/org/apache/paimon/rest/ResourcePaths.java
+++ b/paimon-api/src/main/java/org/apache/paimon/rest/ResourcePaths.java
@@ -70,6 +70,10 @@ public class ResourcePaths {
return SLASH.join(V1, prefix, DATABASES, encodeString(databaseName),
TABLE_DETAILS);
}
+ public String tables() {
+ return SLASH.join(V1, prefix, TABLES);
+ }
+
public String table(String databaseName, String objectName) {
return SLASH.join(
V1,
@@ -206,6 +210,10 @@ public class ResourcePaths {
return SLASH.join(V1, prefix, DATABASES, encodeString(databaseName),
VIEW_DETAILS);
}
+ public String views() {
+ return SLASH.join(V1, prefix, VIEWS);
+ }
+
public String view(String databaseName, String viewName) {
return SLASH.join(
V1, prefix, DATABASES, encodeString(databaseName), VIEWS,
encodeString(viewName));
diff --git
a/paimon-core/src/main/java/org/apache/paimon/catalog/AbstractCatalog.java
b/paimon-core/src/main/java/org/apache/paimon/catalog/AbstractCatalog.java
index 8286155fe2..ca1d4f9b52 100644
--- a/paimon-core/src/main/java/org/apache/paimon/catalog/AbstractCatalog.java
+++ b/paimon-core/src/main/java/org/apache/paimon/catalog/AbstractCatalog.java
@@ -142,7 +142,9 @@ public abstract class AbstractCatalog implements Catalog {
}
@Override
- public PagedList<String> listDatabasesPaged(Integer maxResults, String
pageToken) {
+ public PagedList<String> listDatabasesPaged(
+ Integer maxResults, String pageToken, String databaseNamePattern) {
+ CatalogUtils.validateNamePattern(this, databaseNamePattern);
return new PagedList<>(listDatabases(), null);
}
diff --git a/paimon-core/src/main/java/org/apache/paimon/catalog/Catalog.java
b/paimon-core/src/main/java/org/apache/paimon/catalog/Catalog.java
index 5183891cef..b10bce009d 100644
--- a/paimon-core/src/main/java/org/apache/paimon/catalog/Catalog.java
+++ b/paimon-core/src/main/java/org/apache/paimon/catalog/Catalog.java
@@ -67,11 +67,18 @@ public interface Catalog extends AutoCloseable {
* max results.
* @param pageToken Optional parameter indicating the next page token
allows list to be start
* from a specific point.
+ * @param databaseNamePattern A sql LIKE pattern (%) for database names.
All databases will be
+ * returned * if not set or empty. Currently, only prefix matching is
supported.
* @return a list of the names of databases with provided page size in
this catalog and next
* page token, or a list of the names of all databases if the catalog
does not {@link
* #supportsListObjectsPaged()}.
+ * @throws UnsupportedOperationException if and does not {@link
#supportsListByPattern()} when
+ * databaseNamePattern is not null
*/
- PagedList<String> listDatabasesPaged(@Nullable Integer maxResults,
@Nullable String pageToken);
+ PagedList<String> listDatabasesPaged(
+ @Nullable Integer maxResults,
+ @Nullable String pageToken,
+ @Nullable String databaseNamePattern);
/**
* Create a database, see {@link Catalog#createDatabase(String name,
boolean ignoreIfExists, Map
@@ -166,13 +173,13 @@ public interface Catalog extends AutoCloseable {
* max results.
* @param pageToken Optional parameter indicating the next page token
allows list to be start
* from a specific point.
- * @param tableNamePattern A sql LIKE pattern (% and _) for table names.
All tables will be
- * returned if not set or empty. Currently, only prefix matching is
supported. Note please
- * escape the underline if you want to match it exactly.
+ * @param tableNamePattern A sql LIKE pattern (%) for table names. All
tables will be returned
+ * if not set or empty. Currently, only prefix matching is supported.
* @return a list of the names of tables with provided page size in this
database and next page
* token, or a list of the names of all tables in this database if the
catalog does not
* {@link #supportsListObjectsPaged()}.
- * @throws DatabaseNotExistException if the database does not exist
+ * @throws DatabaseNotExistException if the database does not exist.
+ * @throws UnsupportedOperationException if does not {@link
#supportsListByPattern()}
*/
PagedList<String> listTablesPaged(
String databaseName,
@@ -193,13 +200,13 @@ public interface Catalog extends AutoCloseable {
* max results.
* @param pageToken Optional parameter indicating the next page token
allows list to be start
* from a specific point.
- * @param tableNamePattern A sql LIKE pattern (% and _) for table names.
All table details will
- * be returned if not set or empty. Currently, only prefix matching is
supported. Note
- * please escape the underline if you want to match it exactly.
+ * @param tableNamePattern A sql LIKE pattern (%) for table names. All
table details will be
+ * returned if not set or empty. Currently, only prefix matching is
supported.
* @return a list of the table details with provided page size in this
database and next page
* token, or a list of the details of all tables in this database if
the catalog does not
* {@link #supportsListObjectsPaged()}.
* @throws DatabaseNotExistException if the database does not exist
+ * @throws UnsupportedOperationException if does not {@link
#supportsListByPattern()}
*/
PagedList<Table> listTableDetailsPaged(
String databaseName,
@@ -208,6 +215,34 @@ public interface Catalog extends AutoCloseable {
@Nullable String tableNamePattern)
throws DatabaseNotExistException;
+ /**
+ * Gets an array of tables for a catalog.
+ *
+ * <p>NOTE: System tables will not be listed.
+ *
+ * @param databaseNamePattern A sql LIKE pattern (%) for database names.
All databases will be
+ * returned if not set or empty. Currently, only prefix matching is
supported.
+ * @param tableNamePattern A sql LIKE pattern (%) for table names. All
tables will be returned
+ * if not set or empty. Currently, only prefix matching is supported.
+ * @param maxResults Optional parameter indicating the maximum number of
results to include in
+ * the result. If maxResults is not specified or set to 0, will return
the default number of
+ * max results.
+ * @param pageToken Optional parameter indicating the next page token
allows list to be start
+ * from a specific point.
+ * @return a list of the tables with provided page size under this
databaseNamePattern &
+ * tableNamePattern and next page token
+ * @throws UnsupportedOperationException if does not {@link
#supportsListObjectsPaged()} or does
+ * not {@link #supportsListByPattern()}.
+ */
+ default PagedList<String> listTablesPagedGlobally(
+ @Nullable String databaseNamePattern,
+ @Nullable String tableNamePattern,
+ @Nullable Integer maxResults,
+ @Nullable String pageToken) {
+ throw new UnsupportedOperationException(
+ "Current Catalog does not support listTablesPagedGlobally");
+ }
+
/**
* Drop a table.
*
@@ -324,13 +359,13 @@ public interface Catalog extends AutoCloseable {
* max results.
* @param pageToken Optional parameter indicating the next page token
allows list to be start
* from a specific point.
- * @param partitionNamePattern A sql LIKE pattern (% and _) for partition
names. All partitions
- * will be * returned if not set or empty. Currently, only prefix
matching is supported.
- * Note please * escape the underline if you want to match it exactly.
+ * @param partitionNamePattern A sql LIKE pattern (%) for partition names.
All partitions will
+ * be * returned if not set or empty. Currently, only prefix matching
is supported.
* @return a list of the partitions with provided page size(@param
maxResults) in this table and
* next page token, or a list of all partitions of the table if the
catalog does not {@link
* #supportsListObjectsPaged()}.
* @throws TableNotExistException if the table does not exist
+ * @throws UnsupportedOperationException if does not {@link
#supportsListByPattern()}
*/
PagedList<Partition> listPartitionsPaged(
Identifier identifier,
@@ -400,13 +435,13 @@ public interface Catalog extends AutoCloseable {
* max results.
* @param pageToken Optional parameter indicating the next page token
allows list to be start
* from a specific point.
- * @param viewNamePattern A sql LIKE pattern (% and _) for view names. All
views will be
- * returned if not set or empty. Currently, only prefix matching is
supported. Note please
- * escape the underline if you want to match it exactly.
+ * @param viewNamePattern A sql LIKE pattern (%) for view names. All views
will be returned if
+ * not set or empty. Currently, only prefix matching is supported.
* @return a list of the names of views with provided page size in this
database and next page
* token, or a list of the names of all views in this database if the
catalog does not
* {@link #supportsListObjectsPaged()}.
* @throws DatabaseNotExistException if the database does not exist
+ * @throws UnsupportedOperationException if does not {@link
#supportsListByPattern()}
*/
default PagedList<String> listViewsPaged(
String databaseName,
@@ -427,13 +462,13 @@ public interface Catalog extends AutoCloseable {
* max results.
* @param pageToken Optional parameter indicating the next page token
allows list to be start
* from a specific point.
- * @param viewNamePattern A sql LIKE pattern (% and _) for view names. All
view details will be
- * returned if not set or empty. Currently, only prefix matching is
supported. Note please
- * escape the underline if you want to match it exactly.
+ * @param viewNamePattern A sql LIKE pattern (%) for view names. All view
details will be
+ * returned if not set or empty. Currently, only prefix matching is
supported.
* @return a list of the view details with provided page size (@param
maxResults) in this
* database and next page token, or a list of the details of all views
in this database if
* the catalog does not {@link #supportsListObjectsPaged()}.
* @throws DatabaseNotExistException if the database does not exist
+ * @throws UnsupportedOperationException if does not {@link
#supportsListByPattern()}
*/
default PagedList<View> listViewDetailsPaged(
String databaseName,
@@ -444,6 +479,34 @@ public interface Catalog extends AutoCloseable {
return new PagedList<>(Collections.emptyList(), null);
}
+ /**
+ * Gets an array of views for a catalog.
+ *
+ * <p>NOTE: System tables will not be listed.
+ *
+ * @param databaseNamePattern A sql LIKE pattern (%) for database names.
All databases will be
+ * returned if not set or empty. Currently, only prefix matching is
supported.
+ * @param viewNamePattern A sql LIKE pattern (%) for view names. All views
will be returned if
+ * not set or empty. Currently, only prefix matching is supported.
+ * @param maxResults Optional parameter indicating the maximum number of
results to include in
+ * the result. If maxResults is not specified or set to 0, will return
the default number of
+ * max results.
+ * @param pageToken Optional parameter indicating the next page token
allows list to be start
+ * from a specific point.
+ * @return a list of the views with provided page size under this
databaseNamePattern &
+ * tableNamePattern and next page token
+ * @throws UnsupportedOperationException if does not {@link
#supportsListObjectsPaged()} or does
+ * not {@link #supportsListByPattern()}}.
+ */
+ default PagedList<String> listViewsPagedGlobally(
+ @Nullable String databaseNamePattern,
+ @Nullable String viewNamePattern,
+ @Nullable Integer maxResults,
+ @Nullable String pageToken) {
+ throw new UnsupportedOperationException(
+ "Current Catalog does not support listViewsPagedGlobally");
+ }
+
/**
* Rename a view.
*
@@ -504,7 +567,7 @@ public interface Catalog extends AutoCloseable {
* String)} would fall back to {@link #listTables(String)}.
*
* <ul>
- * <li>{@link #listDatabasesPaged(Integer, String)}.
+ * <li>{@link #listDatabasesPaged(Integer, String, String)}.
* <li>{@link #listTablesPaged(String, Integer, String, String)}.
* <li>{@link #listTableDetailsPaged(String, Integer, String, String)}.
* <li>{@link #listViewsPaged(String, Integer, String, String)}.
@@ -519,7 +582,7 @@ public interface Catalog extends AutoCloseable {
* corresponding methods will throw exception if name pattern provided.
*
* <ul>
- * <li>{@link #listDatabasesPaged(Integer, String)}.
+ * <li>{@link #listDatabasesPaged(Integer, String, String)}.
* <li>{@link #listTablesPaged(String, Integer, String, String)}.
* <li>{@link #listTableDetailsPaged(String, Integer, String, String)}.
* <li>{@link #listViewsPaged(String, Integer, String, String)}.
diff --git
a/paimon-core/src/main/java/org/apache/paimon/catalog/DelegateCatalog.java
b/paimon-core/src/main/java/org/apache/paimon/catalog/DelegateCatalog.java
index 768e4dfc3d..9438f8ef22 100644
--- a/paimon-core/src/main/java/org/apache/paimon/catalog/DelegateCatalog.java
+++ b/paimon-core/src/main/java/org/apache/paimon/catalog/DelegateCatalog.java
@@ -67,8 +67,9 @@ public abstract class DelegateCatalog implements Catalog {
}
@Override
- public PagedList<String> listDatabasesPaged(Integer maxResults, String
pageToken) {
- return wrapped.listDatabasesPaged(maxResults, pageToken);
+ public PagedList<String> listDatabasesPaged(
+ Integer maxResults, String pageToken, String databaseNamePattern) {
+ return wrapped.listDatabasesPaged(maxResults, pageToken,
databaseNamePattern);
}
@Override
@@ -113,6 +114,16 @@ public abstract class DelegateCatalog implements Catalog {
return wrapped.listTableDetailsPaged(databaseName, maxResults,
pageToken, tableNamePattern);
}
+ @Override
+ public PagedList<String> listTablesPagedGlobally(
+ String databaseNamePattern,
+ String tableNamePattern,
+ Integer maxResults,
+ String pageToken) {
+ return wrapped.listTablesPagedGlobally(
+ databaseNamePattern, tableNamePattern, maxResults, pageToken);
+ }
+
@Override
public void dropTable(Identifier identifier, boolean ignoreIfNotExists)
throws TableNotExistException {
@@ -288,6 +299,16 @@ public abstract class DelegateCatalog implements Catalog {
return wrapped.listViewDetailsPaged(databaseName, maxResults,
pageToken, tableNamePattern);
}
+ @Override
+ public PagedList<String> listViewsPagedGlobally(
+ String databaseNamePattern,
+ String viewNamePattern,
+ Integer maxResults,
+ String pageToken) {
+ return wrapped.listViewsPagedGlobally(
+ databaseNamePattern, viewNamePattern, maxResults, pageToken);
+ }
+
@Override
public void renameView(Identifier fromView, Identifier toView, boolean
ignoreIfNotExists)
throws ViewNotExistException, ViewAlreadyExistException {
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 428fc3da26..d1b06c8569 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
@@ -119,8 +119,10 @@ public class RESTCatalog implements Catalog {
@Override
public PagedList<String> listDatabasesPaged(
- @Nullable Integer maxResults, @Nullable String pageToken) {
- return api.listDatabasesPaged(maxResults, pageToken);
+ @Nullable Integer maxResults,
+ @Nullable String pageToken,
+ @Nullable String databaseNamePattern) {
+ return api.listDatabasesPaged(maxResults, pageToken,
databaseNamePattern);
}
@Override
@@ -245,6 +247,18 @@ public class RESTCatalog implements Catalog {
}
}
+ @Override
+ public PagedList<String> listTablesPagedGlobally(
+ @Nullable String databaseNamePattern,
+ @Nullable String tableNamePattern,
+ @Nullable Integer maxResults,
+ @Nullable String pageToken) {
+ PagedList<String> tables =
+ api.listTablesPagedGlobally(
+ databaseNamePattern, tableNamePattern, maxResults,
pageToken);
+ return new PagedList<>(tables.getElements(),
tables.getNextPageToken());
+ }
+
@Override
public Table getTable(Identifier identifier) throws TableNotExistException
{
return CatalogUtils.loadTable(
@@ -765,6 +779,18 @@ public class RESTCatalog implements Catalog {
}
}
+ @Override
+ public PagedList<String> listViewsPagedGlobally(
+ @Nullable String databaseNamePattern,
+ @Nullable String viewNamePattern,
+ @Nullable Integer maxResults,
+ @Nullable String pageToken) {
+ PagedList<String> views =
+ api.listViewsPagedGlobally(
+ databaseNamePattern, viewNamePattern, maxResults,
pageToken);
+ return new PagedList<>(views.getElements(), views.getNextPageToken());
+ }
+
private ViewImpl toView(String db, GetViewResponse response) {
ViewSchema schema = response.getSchema();
Map<String, String> options = new HashMap<>(schema.options());
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 293692a906..da60f47b8b 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
@@ -49,6 +49,7 @@ import
org.apache.paimon.shade.guava30.com.google.common.collect.Lists;
import org.apache.paimon.shade.guava30.com.google.common.collect.Maps;
import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
@@ -369,6 +370,44 @@ public abstract class CatalogTestBase {
"non_existing_db", finalMaxResults,
pageToken, null));
}
+ @Test
+ public void testListTablesPagedGlobally() throws Exception {
+ // List table paged globally throws UnsupportedOperationException if
current catalog does
+ // not
+ // supportsListObjectsPaged or current catalog does not
supportsListByPattern
+ String databaseName = "list_tables_paged_globally_db";
+ catalog.createDatabase(databaseName, false);
+ if (!catalog.supportsListObjectsPaged() ||
!catalog.supportsListByPattern()) {
+ Assertions.assertThrows(
+ UnsupportedOperationException.class,
+ () -> catalog.listTablesPagedGlobally(databaseName, null,
null, null));
+ }
+
+ String[] tableNames = {"table1", "table2", "table3", "abd", "def",
"opr"};
+ for (String tableName : tableNames) {
+ catalog.createTable(
+ Identifier.create(databaseName, tableName),
DEFAULT_TABLE_SCHEMA, false);
+ }
+
+ if (!catalog.supportsListObjectsPaged() ||
!catalog.supportsListByPattern()) {
+ Assertions.assertThrows(
+ UnsupportedOperationException.class,
+ () -> catalog.listTablesPagedGlobally(null, null, null,
null));
+ Assertions.assertThrows(
+ UnsupportedOperationException.class,
+ () -> catalog.listTablesPagedGlobally(databaseName, null,
null, null));
+ Assertions.assertThrows(
+ UnsupportedOperationException.class,
+ () -> catalog.listTablesPagedGlobally(null, null, 100,
null));
+ Assertions.assertThrows(
+ UnsupportedOperationException.class,
+ () -> catalog.listTablesPagedGlobally(databaseName, "abc",
null, null));
+ Assertions.assertThrows(
+ UnsupportedOperationException.class,
+ () -> catalog.listTablesPagedGlobally(databaseName, "abc",
null, "table"));
+ }
+ }
+
@Test
public void testCreateTable() throws Exception {
catalog.createDatabase("test_db", false);
@@ -1206,6 +1245,47 @@ public abstract class CatalogTestBase {
"non_existing_db", finalMaxResults,
pageToken, null));
}
+ @Test
+ public void testListViewsPagedGlobally() throws Exception {
+ if (!supportsView()) {
+ return;
+ }
+
+ // List view paged globally throws UnsupportedOperationException if
current catalog does not
+ // supportsListObjectsPaged or odes not supportsListByPattern
+ String databaseName = "list_views_paged_globally_db";
+ catalog.createDatabase(databaseName, false);
+ if (!catalog.supportsListObjectsPaged() ||
!catalog.supportsListByPattern()) {
+ Assertions.assertThrows(
+ UnsupportedOperationException.class,
+ () -> catalog.listViewsPagedGlobally(databaseName, null,
null, null));
+ }
+
+ View view = buildView(databaseName);
+ String[] viewNames = {"view1", "view2", "view3", "abd", "def", "opr"};
+ for (String viewName : viewNames) {
+ catalog.createView(Identifier.create(databaseName, viewName),
view, false);
+ }
+
+ if (!catalog.supportsListObjectsPaged() ||
!catalog.supportsListByPattern()) {
+ Assertions.assertThrows(
+ UnsupportedOperationException.class,
+ () -> catalog.listViewsPagedGlobally(null, null, null,
null));
+ Assertions.assertThrows(
+ UnsupportedOperationException.class,
+ () -> catalog.listViewsPagedGlobally(databaseName, null,
null, null));
+ Assertions.assertThrows(
+ UnsupportedOperationException.class,
+ () -> catalog.listViewsPagedGlobally(null, null, 100,
null));
+ Assertions.assertThrows(
+ UnsupportedOperationException.class,
+ () -> catalog.listViewsPagedGlobally(databaseName, "abc",
null, null));
+ Assertions.assertThrows(
+ UnsupportedOperationException.class,
+ () -> catalog.listViewsPagedGlobally(databaseName, "abc",
null, "view"));
+ }
+ }
+
@Test
public void testFormatTable() throws Exception {
if (!supportsFormatTable()) {
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 6ac647f4ff..ce31cdecb1 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
@@ -96,6 +96,7 @@ import okhttp3.mockwebserver.Dispatcher;
import okhttp3.mockwebserver.MockResponse;
import okhttp3.mockwebserver.MockWebServer;
import okhttp3.mockwebserver.RecordedRequest;
+import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.testcontainers.shaded.com.google.common.collect.ImmutableMap;
@@ -120,6 +121,7 @@ import static
org.apache.paimon.CoreOptions.SNAPSHOT_CLEAN_EMPTY_DIRECTORIES;
import static org.apache.paimon.CoreOptions.TYPE;
import static org.apache.paimon.TableType.FORMAT_TABLE;
import static org.apache.paimon.options.CatalogOptions.WAREHOUSE;
+import static org.apache.paimon.rest.RESTApi.DATABASE_NAME_PATTERN;
import static org.apache.paimon.rest.RESTApi.MAX_RESULTS;
import static org.apache.paimon.rest.RESTApi.PAGE_TOKEN;
import static org.apache.paimon.rest.RESTApi.PARTITION_NAME_PATTERN;
@@ -283,6 +285,10 @@ public class RESTCatalogServer {
return renameTableHandle(restAuthParameter.data());
} else if
(resourcePaths.renameView().equals(request.getPath())) {
return renameViewHandle(restAuthParameter.data());
+ } else if (StringUtils.startsWith(request.getPath(),
resourcePaths.tables())) {
+ return tablesHandle(parameters);
+ } else if (StringUtils.startsWith(request.getPath(),
resourcePaths.views())) {
+ return viewsHandle(parameters);
} else if (request.getPath().startsWith(databaseUri)) {
String[] resources =
request.getPath()
@@ -913,7 +919,17 @@ public class RESTCatalogServer {
String method, String data, Map<String, String> parameters) throws
Exception {
switch (method) {
case "GET":
- List<String> databases = new
ArrayList<>(databaseStore.keySet());
+ String databaseNamePattern =
parameters.get(DATABASE_NAME_PATTERN);
+ List<String> databases =
+ new ArrayList<>(databaseStore.keySet())
+ .stream()
+ .filter(
+ databaseName ->
+
Objects.isNull(databaseNamePattern)
+ ||
matchNamePattern(
+
databaseName,
+
databaseNamePattern))
+ .collect(Collectors.toList());
return generateFinalListDatabasesResponse(parameters,
databases);
case "POST":
CreateDatabaseRequest requestBody =
@@ -968,18 +984,8 @@ public class RESTCatalogServer {
int maxResults;
try {
maxResults = getMaxResults(parameters);
- } catch (Exception e) {
- LOG.error(
- "parse maxResults {} to int failed",
- parameters.getOrDefault(MAX_RESULTS, null));
- return mockResponse(
- new ErrorResponse(
- ErrorResponse.RESOURCE_TYPE_TABLE,
- null,
- "invalid input queryParameter maxResults"
- + parameters.get(MAX_RESULTS),
- 400),
- 400);
+ } catch (NumberFormatException e) {
+ return handleInvalidMaxResults(parameters);
}
String pageToken = parameters.getOrDefault(PAGE_TOKEN, null);
PagedList<String> pagedDbs = buildPagedEntities(databases,
maxResults, pageToken);
@@ -1157,18 +1163,8 @@ public class RESTCatalogServer {
int maxResults;
try {
maxResults = getMaxResults(parameters);
- } catch (Exception e) {
- LOG.error(
- "parse maxResults {} to int failed",
- parameters.getOrDefault(MAX_RESULTS, null));
- return mockResponse(
- new ErrorResponse(
- ErrorResponse.RESOURCE_TYPE_TABLE,
- null,
- "invalid input queryParameter maxResults"
- + parameters.get(MAX_RESULTS),
- 400),
- 400);
+ } catch (NumberFormatException e) {
+ return handleInvalidMaxResults(parameters);
}
String pageToken = parameters.getOrDefault(PAGE_TOKEN, null);
@@ -1189,18 +1185,8 @@ public class RESTCatalogServer {
int maxResults;
try {
maxResults = getMaxResults(parameters);
- } catch (Exception e) {
- LOG.error(
- "parse maxResults {} to int failed",
- parameters.getOrDefault(MAX_RESULTS, null));
- return mockResponse(
- new ErrorResponse(
- ErrorResponse.RESOURCE_TYPE_TABLE,
- null,
- "invalid input queryParameter maxResults"
- + parameters.get(MAX_RESULTS),
- 400),
- 400);
+ } catch (NumberFormatException e) {
+ return handleInvalidMaxResults(parameters);
}
String pageToken = parameters.get(PAGE_TOKEN);
PagedList<GetTableResponse> pagedTableDetails =
@@ -1242,6 +1228,44 @@ public class RESTCatalogServer {
return tableDetails;
}
+ private MockResponse tablesHandle(Map<String, String> parameters) {
+ RESTResponse response;
+ List<String> tables = listTables(parameters);
+ if (!tables.isEmpty()) {
+ int maxResults;
+ try {
+ maxResults = getMaxResults(parameters);
+ } catch (NumberFormatException e) {
+ return handleInvalidMaxResults(parameters);
+ }
+ String pageToken = parameters.get(PAGE_TOKEN);
+ PagedList<String> pagedTables = buildPagedEntities(tables,
maxResults, pageToken);
+ response =
+ new ListTablesResponse(
+ pagedTables.getElements(),
pagedTables.getNextPageToken());
+ } else {
+ response = new ListTablesResponse(Collections.emptyList(), null);
+ }
+ return mockResponse(response, 200);
+ }
+
+ private List<String> listTables(Map<String, String> parameters) {
+ String tableNamePattern = parameters.get(TABLE_NAME_PATTERN);
+ String databaseNamePattern = parameters.get(DATABASE_NAME_PATTERN);
+ List<String> tables = new ArrayList<>();
+ for (Map.Entry<String, TableMetadata> entry :
tableMetadataStore.entrySet()) {
+ Identifier identifier = Identifier.fromString(entry.getKey());
+ if ((Objects.isNull(databaseNamePattern))
+ || matchNamePattern(identifier.getDatabaseName(),
databaseNamePattern)
+ && (Objects.isNull(tableNamePattern)
+ || matchNamePattern(
+ identifier.getTableName(),
tableNamePattern))) {
+ tables.add(identifier.getFullName());
+ }
+ }
+ return tables;
+ }
+
private boolean isFormatTable(Schema schema) {
return Options.fromMap(schema.options()).get(TYPE) == FORMAT_TABLE;
}
@@ -1441,18 +1465,8 @@ public class RESTCatalogServer {
int maxResults;
try {
maxResults = getMaxResults(parameters);
- } catch (Exception e) {
- LOG.error(
- "parse maxResults {} to int failed",
- parameters.getOrDefault(MAX_RESULTS, null));
- return mockResponse(
- new ErrorResponse(
- ErrorResponse.RESOURCE_TYPE_TABLE,
- null,
- "invalid input queryParameter maxResults"
- + parameters.get(MAX_RESULTS),
- 400),
- 400);
+ } catch (NumberFormatException e) {
+ return handleInvalidMaxResults(parameters);
}
String pageToken = parameters.getOrDefault(PAGE_TOKEN, null);
@@ -1516,18 +1530,8 @@ public class RESTCatalogServer {
int maxResults;
try {
maxResults = getMaxResults(parameters);
- } catch (Exception e) {
- LOG.error(
- "parse maxResults {} to int failed",
- parameters.getOrDefault(MAX_RESULTS, null));
- return mockResponse(
- new ErrorResponse(
- ErrorResponse.RESOURCE_TYPE_TABLE,
- null,
- "invalid input queryParameter maxResults"
- + parameters.get(MAX_RESULTS),
- 400),
- 400);
+ } catch (NumberFormatException e) {
+ return handleInvalidMaxResults(parameters);
}
String pageToken = parameters.getOrDefault(PAGE_TOKEN, null);
@@ -1551,18 +1555,8 @@ public class RESTCatalogServer {
int maxResults;
try {
maxResults = getMaxResults(parameters);
- } catch (Exception e) {
- LOG.error(
- "parse maxResults {} to int failed",
- parameters.getOrDefault(MAX_RESULTS, null));
- return mockResponse(
- new ErrorResponse(
- ErrorResponse.RESOURCE_TYPE_TABLE,
- null,
- "invalid input queryParameter maxResults"
- + parameters.get(MAX_RESULTS),
- 400),
- 400);
+ } catch (NumberFormatException e) {
+ return handleInvalidMaxResults(parameters);
}
String pageToken = parameters.getOrDefault(PAGE_TOKEN, null);
@@ -1615,6 +1609,43 @@ public class RESTCatalogServer {
.collect(Collectors.toList());
}
+ private MockResponse viewsHandle(Map<String, String> parameters) {
+ RESTResponse response;
+ List<String> views = listViews(parameters);
+ if (!views.isEmpty()) {
+ int maxResults;
+ try {
+ maxResults = getMaxResults(parameters);
+ } catch (NumberFormatException e) {
+ return handleInvalidMaxResults(parameters);
+ }
+ String pageToken = parameters.get(PAGE_TOKEN);
+ PagedList<String> pagedViews = buildPagedEntities(views,
maxResults, pageToken);
+ response =
+ new ListViewsResponse(pagedViews.getElements(),
pagedViews.getNextPageToken());
+ } else {
+ response = new ListViewsResponse(Collections.emptyList(), null);
+ }
+ return mockResponse(response, 200);
+ }
+
+ private List<String> listViews(Map<String, String> parameters) {
+ String viewNamePattern = parameters.get(VIEW_NAME_PATTERN);
+ String databaseNamePattern = parameters.get(DATABASE_NAME_PATTERN);
+ List<String> fullViews = new ArrayList<>();
+ for (Map.Entry<String, View> entry : viewStore.entrySet()) {
+ Identifier identifier = Identifier.fromString(entry.getKey());
+ if ((Objects.isNull(databaseNamePattern))
+ || matchNamePattern(identifier.getDatabaseName(),
databaseNamePattern)
+ && (Objects.isNull(viewNamePattern)
+ || matchNamePattern(
+ identifier.getTableName(),
viewNamePattern))) {
+ fullViews.add(identifier.getFullName());
+ }
+ }
+ return fullViews;
+ }
+
private MockResponse viewHandle(String method, Identifier identifier,
String requestData)
throws Exception {
RESTResponse response;
@@ -2033,18 +2064,25 @@ public class RESTCatalogServer {
escaped = true;
continue;
}
- switch (c) {
- case '%':
- regex.append(".*");
- break;
- case '_':
- regex.append(".");
- break;
- default:
- regex.append(c);
- break;
+ if (c == '%') {
+ regex.append(".*");
+ } else {
+ regex.append(c);
}
}
return "^" + regex + "$";
}
+
+ private MockResponse handleInvalidMaxResults(Map<String, String>
parameters) {
+ String maxResults = parameters.get(MAX_RESULTS);
+ LOG.error("Invalid maxResults value: {}", maxResults);
+ return mockResponse(
+ new ErrorResponse(
+ ErrorResponse.RESOURCE_TYPE_TABLE,
+ null,
+ String.format(
+ "Invalid input for queryParameter maxResults:
%s", maxResults),
+ 400),
+ 400);
+ }
}
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 f6e259d2da..ad5d5ed1ea 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
@@ -20,6 +20,7 @@ package org.apache.paimon.rest;
import org.apache.paimon.PagedList;
import org.apache.paimon.Snapshot;
+import org.apache.paimon.TableType;
import org.apache.paimon.catalog.Catalog;
import org.apache.paimon.catalog.CatalogTestBase;
import org.apache.paimon.catalog.Identifier;
@@ -62,6 +63,7 @@ import
org.apache.paimon.shade.guava30.com.google.common.collect.Lists;
import org.apache.paimon.shade.guava30.com.google.common.collect.Maps;
import org.apache.commons.io.FileUtils;
+import org.apache.commons.lang3.StringUtils;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
@@ -74,6 +76,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
+import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
@@ -123,18 +126,18 @@ public abstract class RESTCatalogTest extends
CatalogTestBase {
@Test
void testListDatabasesPaged() throws Catalog.DatabaseAlreadyExistException
{
// List databases paged returns an empty list when there are no
databases in the catalog
- PagedList<String> pagedDatabases = catalog.listDatabasesPaged(null,
null);
+ PagedList<String> pagedDatabases = catalog.listDatabasesPaged(null,
null, null);
assertThat(pagedDatabases.getElements()).isEmpty();
assertNull(pagedDatabases.getNextPageToken());
- String[] dbNames = {"ghj", "db1", "db2", "db3", "ert"};
+ String[] dbNames = {"ghj", "db1", "db2", "db3", "ert", "db_name"};
for (String dbName : dbNames) {
catalog.createDatabase(dbName, true);
}
// when maxResults is null or 0, the page length is set to a server
configured value
String[] sortedDbNames =
Arrays.stream(dbNames).sorted().toArray(String[]::new);
- pagedDatabases = catalog.listDatabasesPaged(null, null);
+ pagedDatabases = catalog.listDatabasesPaged(null, null, null);
List<String> dbs = pagedDatabases.getElements();
assertThat(dbs).containsExactly(sortedDbNames);
assertNull(pagedDatabases.getNextPageToken());
@@ -143,37 +146,65 @@ public abstract class RESTCatalogTest extends
CatalogTestBase {
// server configured value
// when pageToken is null, will list tables from the beginning
int maxResults = 2;
- pagedDatabases = catalog.listDatabasesPaged(maxResults, null);
+ pagedDatabases = catalog.listDatabasesPaged(maxResults, null, null);
dbs = pagedDatabases.getElements();
assertEquals(maxResults, dbs.size());
assertThat(dbs).containsExactly("db1", "db2");
assertEquals("db2", pagedDatabases.getNextPageToken());
// when pageToken is not null, will list tables from the pageToken
(exclusive)
- pagedDatabases = catalog.listDatabasesPaged(maxResults,
pagedDatabases.getNextPageToken());
+ pagedDatabases =
+ catalog.listDatabasesPaged(maxResults,
pagedDatabases.getNextPageToken(), null);
dbs = pagedDatabases.getElements();
assertEquals(maxResults, dbs.size());
- assertThat(dbs).containsExactly("db3", "ert");
- assertEquals("ert", pagedDatabases.getNextPageToken());
+ assertThat(dbs).containsExactly("db3", "db_name");
- pagedDatabases = catalog.listDatabasesPaged(maxResults,
pagedDatabases.getNextPageToken());
+ pagedDatabases =
+ catalog.listDatabasesPaged(maxResults,
pagedDatabases.getNextPageToken(), null);
dbs = pagedDatabases.getElements();
- assertEquals(1, dbs.size());
- assertThat(dbs).containsExactly("ghj");
+ assertEquals(2, dbs.size());
+ assertThat(dbs).containsExactly("ert", "ghj");
+
+ pagedDatabases =
+ catalog.listDatabasesPaged(maxResults,
pagedDatabases.getNextPageToken(), null);
+ dbs = pagedDatabases.getElements();
+ assertTrue(dbs.isEmpty());
assertNull(pagedDatabases.getNextPageToken());
maxResults = 8;
- pagedDatabases = catalog.listDatabasesPaged(maxResults, null);
+ pagedDatabases = catalog.listDatabasesPaged(maxResults, null, null);
dbs = pagedDatabases.getElements();
String[] expectedTableNames =
Arrays.stream(dbNames).sorted().toArray(String[]::new);
assertThat(dbs).containsExactly(expectedTableNames);
assertNull(pagedDatabases.getNextPageToken());
- pagedDatabases = catalog.listDatabasesPaged(maxResults, "ddd");
+ pagedDatabases = catalog.listDatabasesPaged(maxResults, "ddd", null);
dbs = pagedDatabases.getElements();
assertEquals(2, dbs.size());
assertThat(dbs).containsExactly("ert", "ghj");
assertNull(pagedDatabases.getNextPageToken());
+
+ pagedDatabases = catalog.listDatabasesPaged(maxResults, null, "db%");
+ dbs = pagedDatabases.getElements();
+ assertEquals(4, dbs.size());
+ assertThat(dbs).containsExactly("db1", "db2", "db3", "db_name");
+ assertNull(pagedDatabases.getNextPageToken());
+
+ pagedDatabases = catalog.listDatabasesPaged(maxResults, null, "db");
+ dbs = pagedDatabases.getElements();
+ assertTrue(dbs.isEmpty());
+ assertNull(pagedDatabases.getNextPageToken());
+
+ pagedDatabases = catalog.listDatabasesPaged(maxResults, null, "db_");
+ dbs = pagedDatabases.getElements();
+ assertTrue(dbs.isEmpty());
+ assertNull(pagedDatabases.getNextPageToken());
+
+ pagedDatabases = catalog.listDatabasesPaged(maxResults, null, "db_%");
+ dbs = pagedDatabases.getElements();
+ assertEquals(1, dbs.size());
+ assertThat(dbs).containsExactly("db_name");
+ assertNull(pagedDatabases.getNextPageToken());
}
@Test
@@ -326,7 +357,7 @@ public abstract class RESTCatalogTest extends
CatalogTestBase {
assertThat(pagedTables.getElements()).isEmpty();
assertNull(pagedTables.getNextPageToken());
- String[] tableNames = {"table1", "table2", "table3", "abd", "def",
"opr"};
+ String[] tableNames = {"table1", "table2", "table3", "abd", "def",
"opr", "table_name"};
for (String tableName : tableNames) {
catalog.createTable(
Identifier.create(databaseName, tableName),
DEFAULT_TABLE_SCHEMA, false);
@@ -370,7 +401,7 @@ public abstract class RESTCatalogTest extends
CatalogTestBase {
catalog.listTablesPaged(
databaseName, maxResults,
pagedTables.getNextPageToken(), null);
tables = pagedTables.getElements();
- assertEquals(0, tables.size());
+ assertEquals(1, tables.size());
assertNull(pagedTables.getNextPageToken());
maxResults = 8;
@@ -381,8 +412,8 @@ public abstract class RESTCatalogTest extends
CatalogTestBase {
pagedTables = catalog.listTablesPaged(databaseName, maxResults,
"table1", null);
tables = pagedTables.getElements();
- assertEquals(2, tables.size());
- assertThat(tables).containsExactly("table2", "table3");
+ assertEquals(3, tables.size());
+ assertThat(tables).containsExactly("table2", "table3", "table_name");
assertNull(pagedTables.getNextPageToken());
// List tables throws DatabaseNotExistException when the database does
not exist
@@ -391,43 +422,34 @@ public abstract class RESTCatalogTest extends
CatalogTestBase {
pagedTables = catalog.listTablesPaged(databaseName, null, null,
"table%");
tables = pagedTables.getElements();
- assertEquals(3, tables.size());
- assertThat(tables).containsExactly("table1", "table2", "table3");
+ assertEquals(4, tables.size());
+ assertThat(tables).containsExactly("table1", "table2", "table3",
"table_name");
assertNull(pagedTables.getNextPageToken());
pagedTables = catalog.listTablesPaged(databaseName, null, null,
"table_");
tables = pagedTables.getElements();
- assertEquals(3, tables.size());
- assertThat(tables).containsExactly("table1", "table2", "table3");
+ assertTrue(tables.isEmpty());
assertNull(pagedTables.getNextPageToken());
pagedTables = catalog.listTablesPaged(databaseName, null, null,
"table_%");
tables = pagedTables.getElements();
- assertEquals(3, tables.size());
- assertThat(tables).containsExactly("table1", "table2", "table3");
+ assertEquals(1, tables.size());
+ assertThat(tables).containsExactly("table_name");
assertNull(pagedTables.getNextPageToken());
- pagedTables = catalog.listTablesPaged(databaseName, null, null,
"table%_");
+ pagedTables = catalog.listTablesPaged(databaseName, null, null,
"table_name");
tables = pagedTables.getElements();
- assertEquals(3, tables.size());
- assertThat(tables).containsExactly("table1", "table2", "table3");
+ assertEquals(1, tables.size());
+ assertThat(tables).containsExactly("table_name");
assertNull(pagedTables.getNextPageToken());
- pagedTables = catalog.listTablesPaged(databaseName, null, null,
"table\\_");
- Assertions.assertTrue(pagedTables.getElements().isEmpty());
- Assertions.assertNull(pagedTables.getNextPageToken());
-
- pagedTables = catalog.listTablesPaged(databaseName, null, null,
"tabl_");
- Assertions.assertTrue(pagedTables.getElements().isEmpty());
- Assertions.assertNull(pagedTables.getNextPageToken());
-
Assertions.assertThrows(
BadRequestException.class,
- () -> catalog.listTablesPaged(databaseName, null, null,
"ta%le"));
+ () -> catalog.listTablesPaged(databaseName, null, null,
"%table"));
Assertions.assertThrows(
BadRequestException.class,
- () -> catalog.listTablesPaged(databaseName, null, null,
"ta_le"));
+ () -> catalog.listTablesPaged(databaseName, null, null,
"ta%le"));
}
@Test
@@ -440,7 +462,7 @@ public abstract class RESTCatalogTest extends
CatalogTestBase {
assertThat(pagedTableDetails.getElements()).isEmpty();
assertNull(pagedTableDetails.getNextPageToken());
- String[] tableNames = {"table1", "table2", "table3", "abd", "def",
"opr"};
+ String[] tableNames = {"table1", "table2", "table3", "abd", "def",
"opr", "table_name"};
String[] expectedTableNames =
Arrays.stream(tableNames).sorted().toArray(String[]::new);
for (String tableName : tableNames) {
catalog.createTable(
@@ -471,7 +493,7 @@ public abstract class RESTCatalogTest extends
CatalogTestBase {
pagedTableDetails =
catalog.listTableDetailsPaged(
databaseName, maxResults,
pagedTableDetails.getNextPageToken(), null);
- assertEquals(0, pagedTableDetails.getElements().size());
+ assertEquals(1, pagedTableDetails.getElements().size());
assertNull(pagedTableDetails.getNextPageToken());
maxResults = 8;
@@ -483,7 +505,7 @@ public abstract class RESTCatalogTest extends
CatalogTestBase {
String pageToken = "table1";
pagedTableDetails =
catalog.listTableDetailsPaged(databaseName, maxResults,
pageToken, null);
- assertPagedTableDetails(pagedTableDetails, 2, "table2", "table3");
+ assertPagedTableDetails(pagedTableDetails, 3, "table2", "table3",
"table_name");
assertNull(pagedTableDetails.getNextPageToken());
// List table details throws DatabaseNotExistException when the
database does not exist
@@ -499,36 +521,163 @@ public abstract class RESTCatalogTest extends
CatalogTestBase {
.isThrownBy(() -> catalog.listTables("non_existing_db"));
pagedTableDetails = catalog.listTableDetailsPaged(databaseName, null,
null, "table%");
- assertPagedTableDetails(pagedTableDetails, 3, "table1", "table2",
"table3");
+ assertPagedTableDetails(pagedTableDetails, 4, "table1", "table2",
"table3", "table_name");
assertNull(pagedTableDetails.getNextPageToken());
pagedTableDetails = catalog.listTableDetailsPaged(databaseName, null,
null, "table_");
- assertPagedTableDetails(pagedTableDetails, 3, "table1", "table2",
"table3");
+ Assertions.assertTrue(pagedTableDetails.getElements().isEmpty());
assertNull(pagedTableDetails.getNextPageToken());
pagedTableDetails = catalog.listTableDetailsPaged(databaseName, null,
null, "table_%");
- assertPagedTableDetails(pagedTableDetails, 3, "table1", "table2",
"table3");
+ assertPagedTableDetails(pagedTableDetails, 1, "table_name");
assertNull(pagedTableDetails.getNextPageToken());
- pagedTableDetails = catalog.listTableDetailsPaged(databaseName, null,
null, "table%_");
- assertPagedTableDetails(pagedTableDetails, 3, "table1", "table2",
"table3");
- assertNull(pagedTableDetails.getNextPageToken());
+ Assertions.assertThrows(
+ BadRequestException.class,
+ () -> catalog.listTableDetailsPaged(databaseName, null, null,
"ta%le"));
- pagedTableDetails = catalog.listTableDetailsPaged(databaseName, null,
null, "table\\_");
- assertTrue(pagedTableDetails.getElements().isEmpty());
- assertNull(pagedTableDetails.getNextPageToken());
+ Assertions.assertThrows(
+ BadRequestException.class,
+ () -> catalog.listTableDetailsPaged(databaseName, null, null,
"%tale"));
+ }
- pagedTableDetails = catalog.listTableDetailsPaged(databaseName, null,
null, "tabl_");
- assertTrue(pagedTableDetails.getElements().isEmpty());
- assertNull(pagedTableDetails.getNextPageToken());
+ @Test
+ public void testListTablesPagedGlobally() throws Exception {
+ // List table paged globally returns an empty list when there are no
tables in the catalog
+
+ PagedList<String> pagedTables = catalog.listTablesPagedGlobally(null,
null, null, null);
+ assertThat(pagedTables.getElements()).isEmpty();
+ assertNull(pagedTables.getNextPageToken());
+
+ String databaseName = "list_tables_paged_globally db";
+ String databaseName2 = "sample";
+ String databaseNamePattern = "list_tables_paged_globally%";
+ String[] tableNames = {
+ "table1", "table2", "table3", "abd", "def", "opr", "format_table",
"table_name"
+ };
+ prepareDataForListTablesPagedGlobally(databaseName, databaseName2,
tableNames);
+
+ String[] expectedTableNames =
+ Arrays.stream(tableNames).map((databaseName +
".")::concat).toArray(String[]::new);
+ String[] fullTableNames = Arrays.copyOf(expectedTableNames,
tableNames.length + 1);
+ fullTableNames[tableNames.length] = databaseName2 + ".table1";
+
+ pagedTables = catalog.listTablesPagedGlobally(databaseNamePattern,
null, null, null);
+
assertThat(pagedTables.getElements()).containsExactlyInAnyOrder(expectedTableNames);
+ assertNull(pagedTables.getNextPageToken());
+
+ assertListTablesPagedGloballyWithLoop(databaseNamePattern,
expectedTableNames);
+ assertListTablesPagedGloballyWithLoop(null, fullTableNames);
+
+ assertListTablesPagedGloballyWithTablePattern(
+ databaseName, databaseNamePattern, expectedTableNames);
+ }
+
+ protected void prepareDataForListTablesPagedGlobally(
+ String databaseName, String databaseName2, String[] tableNames)
+ throws Catalog.DatabaseAlreadyExistException,
Catalog.TableAlreadyExistException,
+ Catalog.DatabaseNotExistException {
+ catalog.createDatabase(databaseName, false);
+ catalog.createDatabase(databaseName2, false);
+
+ Map<String, String> options = new HashMap<>();
+ options.put("type", TableType.FORMAT_TABLE.toString());
+
+ Schema formatTableSchema =
+ new Schema(
+ Lists.newArrayList(
+ new DataField(0, "pk", DataTypes.INT()),
+ new DataField(1, "col1", DataTypes.STRING()),
+ new DataField(2, "col2", DataTypes.STRING())),
+ Collections.emptyList(),
+ Collections.emptyList(),
+ options,
+ "");
+
+ for (String tableName : tableNames) {
+ if (StringUtils.equals(tableName, "format_table")) {
+ catalog.createTable(
+ Identifier.create(databaseName, tableName),
formatTableSchema, false);
+ } else {
+ catalog.createTable(
+ Identifier.create(databaseName, tableName),
DEFAULT_TABLE_SCHEMA, false);
+ }
+ }
+
+ catalog.createTable(
+ Identifier.create(databaseName2, "table1"),
DEFAULT_TABLE_SCHEMA, false);
+ }
+
+ protected void assertListTablesPagedGloballyWithLoop(
+ String databaseNamePattern, String[] expectedTableNames) {
+ int maxResults = 2;
+ PagedList<String> pagedTables;
+ List<String> tables = new ArrayList<>();
+ String pageToken = null;
+ do {
+ pagedTables =
+ catalog.listTablesPagedGlobally(
+ databaseNamePattern, null, maxResults, pageToken);
+ pageToken = pagedTables.getNextPageToken();
+ if (pagedTables.getElements() != null) {
+ tables.addAll(pagedTables.getElements());
+ }
+ if (pageToken == null
+ || pagedTables.getElements() == null
+ || pagedTables.getElements().isEmpty()) {
+ break;
+ }
+ } while (StringUtils.isNotEmpty(pageToken));
+ assertEquals(expectedTableNames.length, tables.size());
+ assertThat(tables).containsExactlyInAnyOrder(expectedTableNames);
+ assertNull(pagedTables.getNextPageToken());
+ }
+
+ protected void assertListTablesPagedGloballyWithTablePattern(
+ String databaseName, String databaseNamePattern, String[]
expectedTableNames) {
+ int maxResults = 9;
+ PagedList<String> pagedTables =
+ catalog.listTablesPagedGlobally(databaseNamePattern, null,
maxResults, null);
+ assertEquals(
+ Math.min(maxResults, expectedTableNames.length),
pagedTables.getElements().size());
+
assertThat(pagedTables.getElements()).containsExactlyInAnyOrder(expectedTableNames);
+ assertNull(pagedTables.getNextPageToken());
+
+ pagedTables = catalog.listTablesPagedGlobally(databaseNamePattern,
"table%", null, null);
+ assertEquals(4, pagedTables.getElements().size());
+ assertThat(pagedTables.getElements())
+ .containsExactlyInAnyOrder(
+ buildFullName(databaseName, "table1"),
+ buildFullName(databaseName, "table2"),
+ buildFullName(databaseName, "table3"),
+ buildFullName(databaseName, "table_name"));
+ assertNull(pagedTables.getNextPageToken());
+
+ pagedTables = catalog.listTablesPagedGlobally(databaseNamePattern,
"table_", null, null);
+ assertTrue(pagedTables.getElements().isEmpty());
+ assertNull(pagedTables.getNextPageToken());
+
+ pagedTables = catalog.listTablesPagedGlobally(databaseNamePattern,
"table_%", null, null);
+ assertEquals(1, pagedTables.getElements().size());
+ assertThat(pagedTables.getElements())
+ .containsExactlyInAnyOrder(buildFullName(databaseName,
"table_name"));
+ assertNull(pagedTables.getNextPageToken());
+
+ pagedTables = catalog.listTablesPagedGlobally(databaseNamePattern,
"tabl_", null, null);
+ assertTrue(pagedTables.getElements().isEmpty());
+ assertNull(pagedTables.getNextPageToken());
Assertions.assertThrows(
BadRequestException.class,
- () -> catalog.listTableDetailsPaged(databaseName, null, null,
"ta%le"));
+ () -> catalog.listTablesPagedGlobally(databaseNamePattern,
"ta%le", null, null));
Assertions.assertThrows(
BadRequestException.class,
- () -> catalog.listTableDetailsPaged(databaseName, null, null,
"ta_le"));
+ () -> catalog.listTablesPagedGlobally(databaseNamePattern,
"%tale", null, null));
+ }
+
+ private String buildFullName(String database, String tableName) {
+ return String.format("%s.%s", database, tableName);
}
@Test
@@ -569,7 +718,7 @@ public abstract class RESTCatalogTest extends
CatalogTestBase {
// catalogs except RestCatalog
// even if the maxResults or pageToken is not null
View view = buildView(databaseName);
- String[] viewNames = {"view1", "view2", "view3", "abd", "def", "opr"};
+ String[] viewNames = {"view1", "view2", "view3", "abd", "def", "opr",
"view_name"};
String[] sortedViewNames =
Arrays.stream(viewNames).sorted().toArray(String[]::new);
for (String viewName : viewNames) {
catalog.createView(Identifier.create(databaseName, viewName),
view, false);
@@ -604,7 +753,7 @@ public abstract class RESTCatalogTest extends
CatalogTestBase {
String pageToken = "view1";
pagedViews = catalog.listViewsPaged(databaseName, maxResults,
pageToken, null);
- assertPagedViews(pagedViews, "view2", "view3");
+ assertPagedViews(pagedViews, "view2", "view3", "view_name");
assertNull(pagedViews.getNextPageToken());
// List views throws DatabaseNotExistException when the database does
not exist
@@ -616,36 +765,24 @@ public abstract class RESTCatalogTest extends
CatalogTestBase {
"non_existing_db", finalMaxResults,
pageToken, null));
pagedViews = catalog.listViewsPaged(databaseName, null, null, "view%");
- assertPagedViews(pagedViews, "view1", "view2", "view3");
- assertNull(pagedViews.getNextPageToken());
-
- pagedViews = catalog.listViewsPaged(databaseName, null, null, "view_");
- assertPagedViews(pagedViews, "view1", "view2", "view3");
+ assertPagedViews(pagedViews, "view1", "view2", "view3", "view_name");
assertNull(pagedViews.getNextPageToken());
pagedViews = catalog.listViewsPaged(databaseName, null, null,
"view_%");
- assertPagedViews(pagedViews, "view1", "view2", "view3");
+ assertPagedViews(pagedViews, "view_name");
assertNull(pagedViews.getNextPageToken());
- pagedViews = catalog.listViewsPaged(databaseName, null, null,
"view%_");
- assertPagedViews(pagedViews, "view1", "view2", "view3");
- assertNull(pagedViews.getNextPageToken());
-
- pagedViews = catalog.listViewsPaged(databaseName, null, null,
"view\\_");
+ pagedViews = catalog.listViewsPaged(databaseName, null, null, "view_");
assertTrue(pagedViews.getElements().isEmpty());
assertNull(pagedViews.getNextPageToken());
- pagedViews = catalog.listViewsPaged(databaseName, null, null, "vie_");
- Assertions.assertTrue(pagedViews.getElements().isEmpty());
- assertNull(pagedViews.getNextPageToken());
-
Assertions.assertThrows(
BadRequestException.class,
() -> catalog.listViewsPaged(databaseName, null, null,
"vi%ew"));
Assertions.assertThrows(
BadRequestException.class,
- () -> catalog.listViewsPaged(databaseName, null, null,
"vi_ew"));
+ () -> catalog.listViewsPaged(databaseName, null, null,
"%view"));
}
@Test
@@ -658,7 +795,7 @@ public abstract class RESTCatalogTest extends
CatalogTestBase {
assertThat(pagedViewDetails.getElements()).isEmpty();
assertNull(pagedViewDetails.getNextPageToken());
- String[] viewNames = {"view1", "view2", "view3", "abd", "def", "opr"};
+ String[] viewNames = {"view1", "view2", "view3", "abd", "def", "opr",
"view_name"};
View view = buildView(databaseName);
for (String viewName : viewNames) {
catalog.createView(Identifier.create(databaseName, viewName),
view, false);
@@ -688,7 +825,7 @@ public abstract class RESTCatalogTest extends
CatalogTestBase {
pagedViewDetails =
catalog.listViewDetailsPaged(
databaseName, maxResults,
pagedViewDetails.getNextPageToken(), null);
- assertEquals(0, pagedViewDetails.getElements().size());
+ assertEquals(1, pagedViewDetails.getElements().size());
assertNull(pagedViewDetails.getNextPageToken());
maxResults = 8;
@@ -703,7 +840,7 @@ public abstract class RESTCatalogTest extends
CatalogTestBase {
String pageToken = "view1";
pagedViewDetails = catalog.listViewDetailsPaged(databaseName,
maxResults, pageToken, null);
- assertPagedViewDetails(pagedViewDetails, view, 2, "view2", "view3");
+ assertPagedViewDetails(pagedViewDetails, view, 3, "view2", "view3",
"view_name");
assertNull(pagedViewDetails.getNextPageToken());
// List view details throws DatabaseNotExistException when the
database does not exist
@@ -715,36 +852,134 @@ public abstract class RESTCatalogTest extends
CatalogTestBase {
"non_existing_db", finalMaxResults,
pageToken, null));
pagedViewDetails = catalog.listViewDetailsPaged(databaseName, null,
null, "view%");
- assertPagedViewDetails(pagedViewDetails, view, 3, "view1", "view2",
"view3");
+ assertPagedViewDetails(pagedViewDetails, view, 4, "view1", "view2",
"view3", "view_name");
assertNull(pagedViewDetails.getNextPageToken());
pagedViewDetails = catalog.listViewDetailsPaged(databaseName, null,
null, "view_");
- assertPagedViewDetails(pagedViewDetails, view, 3, "view1", "view2",
"view3");
- assertNull(pagedViewDetails.getNextPageToken());
-
- pagedViewDetails = catalog.listViewDetailsPaged(databaseName, null,
null, "view%_");
- assertPagedViewDetails(pagedViewDetails, view, 3, "view1", "view2",
"view3");
+ Assertions.assertTrue(pagedViewDetails.getElements().isEmpty());
assertNull(pagedViewDetails.getNextPageToken());
pagedViewDetails = catalog.listViewDetailsPaged(databaseName, null,
null, "view_%");
- assertPagedViewDetails(pagedViewDetails, view, 3, "view1", "view2",
"view3");
+ assertPagedViewDetails(pagedViewDetails, view, 1, "view_name");
assertNull(pagedViewDetails.getNextPageToken());
- pagedViewDetails = catalog.listViewDetailsPaged(databaseName, null,
null, "vie_");
- Assertions.assertTrue(pagedViewDetails.getElements().isEmpty());
- assertNull(pagedViewDetails.getNextPageToken());
+ Assertions.assertThrows(
+ BadRequestException.class,
+ () -> catalog.listViewDetailsPaged(databaseName, null, null,
"vi%ew"));
- pagedViewDetails = catalog.listViewDetailsPaged(databaseName, null,
null, "view\\_");
- Assertions.assertTrue(pagedViewDetails.getElements().isEmpty());
- assertNull(pagedViewDetails.getNextPageToken());
+ Assertions.assertThrows(
+ BadRequestException.class,
+ () -> catalog.listViewDetailsPaged(databaseName, null, null,
"%view"));
+ }
+
+ @Test
+ public void testListViewsPagedGlobally() throws Exception {
+ // list views paged globally returns an empty list when there are no
views in the catalog
+
+ PagedList<String> pagedViews = catalog.listViewsPagedGlobally(null,
null, null, null);
+ assertThat(pagedViews.getElements()).isEmpty();
+ assertNull(pagedViews.getNextPageToken());
+
+ String databaseName = "list_views_paged_globally_db";
+ String databaseName2 = "sample";
+ String databaseNamePattern = "list_views_paged_globally%";
+ String[] viewNames = {"view1", "view2", "view3", "abd", "def", "opr",
"view_name"};
+ prepareDataForListViewsPagedGlobally(databaseName, databaseName2,
viewNames);
+
+ String[] expectedViewNames =
+ Arrays.stream(viewNames).map((databaseName +
".")::concat).toArray(String[]::new);
+ String[] fullTableNames = Arrays.copyOf(expectedViewNames,
viewNames.length + 1);
+ fullTableNames[viewNames.length] = databaseName2 + ".view1";
+
+ pagedViews = catalog.listViewsPagedGlobally(databaseNamePattern, null,
null, null);
+ assertEquals(expectedViewNames.length,
pagedViews.getElements().size());
+
assertThat(pagedViews.getElements()).containsExactlyInAnyOrder(expectedViewNames);
+ assertNull(pagedViews.getNextPageToken());
+
+ assertListViewsPagedGloballyWithLoop(databaseNamePattern,
expectedViewNames);
+ assertListViewsPagedGloballyWithLoop(null, fullTableNames);
+
+ assertListViewsPagedGloballyWithViewPattern(
+ databaseName, databaseNamePattern, expectedViewNames);
+ }
+
+ protected void prepareDataForListViewsPagedGlobally(
+ String databaseName, String databaseName2, String[] viewNames)
+ throws Catalog.DatabaseAlreadyExistException,
Catalog.DatabaseNotExistException,
+ Catalog.ViewAlreadyExistException {
+ catalog.createDatabase(databaseName, false);
+ catalog.createDatabase(databaseName2, false);
+
+ View view = buildView(databaseName);
+ for (String viewName : viewNames) {
+ catalog.createView(Identifier.create(databaseName, viewName),
view, false);
+ }
+
+ catalog.createView(Identifier.create(databaseName2, "view1"), view,
false);
+ }
+
+ protected void assertListViewsPagedGloballyWithLoop(
+ String databaseNamePattern, String[] expectedViewNames) {
+ int maxResults = 2;
+ PagedList<String> pagedViews;
+ List<String> views = new ArrayList<>();
+ String pageToken = null;
+ do {
+ pagedViews =
+ catalog.listViewsPagedGlobally(
+ databaseNamePattern, null, maxResults, pageToken);
+ pageToken = pagedViews.getNextPageToken();
+ if (pagedViews.getElements() != null) {
+ views.addAll(pagedViews.getElements());
+ }
+ if (pageToken == null
+ || pagedViews.getElements() == null
+ || pagedViews.getElements().isEmpty()) {
+ break;
+ }
+ } while (StringUtils.isNotEmpty(pageToken));
+ assertEquals(expectedViewNames.length, views.size());
+ assertThat(views).containsExactlyInAnyOrder(expectedViewNames);
+ assertNull(pagedViews.getNextPageToken());
+ }
+
+ protected void assertListViewsPagedGloballyWithViewPattern(
+ String databaseName, String databaseNamePattern, String[]
expectedViewNames) {
+ int maxResults = 8;
+ PagedList<String> pagedViews =
+ catalog.listViewsPagedGlobally(databaseNamePattern, null,
maxResults, null);
+ assertEquals(
+ Math.min(maxResults, expectedViewNames.length),
pagedViews.getElements().size());
+
assertThat(pagedViews.getElements()).containsExactlyInAnyOrder(expectedViewNames);
+ assertNull(pagedViews.getNextPageToken());
+
+ pagedViews = catalog.listViewsPagedGlobally(databaseNamePattern,
"view%", null, null);
+ assertEquals(4, pagedViews.getElements().size());
+ assertThat(pagedViews.getElements())
+ .containsExactlyInAnyOrder(
+ buildFullName(databaseName, "view1"),
+ buildFullName(databaseName, "view2"),
+ buildFullName(databaseName, "view3"),
+ buildFullName(databaseName, "view_name"));
+ assertNull(pagedViews.getNextPageToken());
+
+ pagedViews = catalog.listViewsPagedGlobally(databaseNamePattern,
"view_", null, null);
+ assertTrue(pagedViews.getElements().isEmpty());
+ assertNull(pagedViews.getNextPageToken());
+
+ pagedViews = catalog.listViewsPagedGlobally(databaseNamePattern,
"view_%", null, null);
+ assertEquals(1, pagedViews.getElements().size());
+ assertThat(pagedViews.getElements())
+ .containsExactlyInAnyOrder(buildFullName(databaseName,
"view_name"));
+ assertNull(pagedViews.getNextPageToken());
Assertions.assertThrows(
BadRequestException.class,
- () -> catalog.listViewDetailsPaged(databaseName, null, null,
"vi%ew"));
+ () -> catalog.listViewsPagedGlobally(databaseNamePattern,
"vi%ew", null, null));
Assertions.assertThrows(
BadRequestException.class,
- () -> catalog.listViewDetailsPaged(databaseName, null, null,
"vi_ew"));
+ () -> catalog.listViewsPagedGlobally(databaseNamePattern,
"%view", null, null));
}
@Test
@@ -855,7 +1090,8 @@ public abstract class RESTCatalogTest extends
CatalogTestBase {
singletonMap("dt", "20240102"),
singletonMap("dt", "20260101"),
singletonMap("dt", "20250104"),
- singletonMap("dt", "20250103"));
+ singletonMap("dt", "20250103"),
+ singletonMap("dt", "2025010_test"));
catalog.dropDatabase(databaseName, true, true);
catalog.createDatabase(databaseName, true);
Identifier identifier = Identifier.create(databaseName, "table");
@@ -908,13 +1144,13 @@ public abstract class RESTCatalogTest extends
CatalogTestBase {
catalog.listPartitionsPaged(
identifier, maxResults,
pagedPartitions.getNextPageToken(), null);
assertPagedPartitions(
- pagedPartitions, maxResults, partitionSpecs.get(4),
partitionSpecs.get(3));
- assertEquals("dt=20260101", pagedPartitions.getNextPageToken());
+ pagedPartitions, maxResults, partitionSpecs.get(4),
partitionSpecs.get(6));
+ assertEquals("dt=2025010_test", pagedPartitions.getNextPageToken());
pagedPartitions =
catalog.listPartitionsPaged(
identifier, maxResults,
pagedPartitions.getNextPageToken(), null);
- assertThat(pagedPartitions.getElements()).isEmpty();
+ assertPagedPartitions(pagedPartitions, 1, partitionSpecs.get(3));
assertNull(pagedPartitions.getNextPageToken());
maxResults = 8;
@@ -931,29 +1167,19 @@ public abstract class RESTCatalogTest extends
CatalogTestBase {
pagedPartitions = catalog.listPartitionsPaged(identifier, maxResults,
null, "dt=2025%");
assertPagedPartitions(
pagedPartitions,
- 4,
+ 5,
partitionSpecs.get(0),
partitionSpecs.get(1),
partitionSpecs.get(5),
- partitionSpecs.get(4));
+ partitionSpecs.get(4),
+ partitionSpecs.get(6));
assertNull(pagedPartitions.getNextPageToken());
- pagedPartitions = catalog.listPartitionsPaged(identifier, maxResults,
null, "dt=2025010_");
- assertPagedPartitions(
- pagedPartitions,
- 4,
- partitionSpecs.get(0),
- partitionSpecs.get(1),
- partitionSpecs.get(5),
- partitionSpecs.get(4));
+ pagedPartitions = catalog.listPartitionsPaged(identifier, maxResults,
null, "dt=2025010_%");
+ assertPagedPartitions(pagedPartitions, 1, partitionSpecs.get(6));
assertNull(pagedPartitions.getNextPageToken());
- pagedPartitions =
- catalog.listPartitionsPaged(identifier, maxResults, null,
"dt=2025010\\_");
- assertTrue(pagedPartitions.getElements().isEmpty());
- assertNull(pagedPartitions.getNextPageToken());
-
- pagedPartitions = catalog.listPartitionsPaged(identifier, maxResults,
null, "dt=202501_");
+ pagedPartitions = catalog.listPartitionsPaged(identifier, maxResults,
null, "dt=2025010_");
assertTrue(pagedPartitions.getElements().isEmpty());
assertNull(pagedPartitions.getNextPageToken());
@@ -963,7 +1189,7 @@ public abstract class RESTCatalogTest extends
CatalogTestBase {
assertThrows(
BadRequestException.class,
- () -> catalog.listPartitionsPaged(identifier, null, null,
"dt=_0101"));
+ () -> catalog.listPartitionsPaged(identifier, null, null,
"dt=01%01"));
}
@Test
diff --git
a/paimon-hive/paimon-hive-catalog/src/main/java/org/apache/paimon/hive/HiveCatalog.java
b/paimon-hive/paimon-hive-catalog/src/main/java/org/apache/paimon/hive/HiveCatalog.java
index 728d5f176c..8362c2a94d 100644
---
a/paimon-hive/paimon-hive-catalog/src/main/java/org/apache/paimon/hive/HiveCatalog.java
+++
b/paimon-hive/paimon-hive-catalog/src/main/java/org/apache/paimon/hive/HiveCatalog.java
@@ -870,6 +870,7 @@ public class HiveCatalog extends AbstractCatalog {
public PagedList<View> listViewDetailsPaged(
String databaseName, Integer maxResults, String pageToken, String
viewNamePattern)
throws DatabaseNotExistException {
+ CatalogUtils.validateNamePattern(this, viewNamePattern);
if (isSystemDatabase(databaseName)) {
return new PagedList<>(Collections.emptyList(), null);
}