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);
         }


Reply via email to