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 622e41b990 [core] rest: support function paged api (#5774)
622e41b990 is described below
commit 622e41b99090394638d7323526e5e81c44cd2b4d
Author: jerry <[email protected]>
AuthorDate: Thu Jun 19 19:10:04 2025 +0800
[core] rest: support function paged api (#5774)
---
docs/static/rest-catalog-open-api.yaml | 114 ++++++++++++-
.../java/org/apache/paimon/catalog/Identifier.java | 2 +-
.../main/java/org/apache/paimon/rest/RESTApi.java | 124 ++++++++++++++
.../java/org/apache/paimon/rest/ResourcePaths.java | 9 +
...ponse.java => ListFunctionDetailsResponse.java} | 31 ++--
...nse.java => ListFunctionsGloballyResponse.java} | 18 +-
.../rest/responses/ListFunctionsResponse.java | 4 -
.../java/org/apache/paimon/catalog/Catalog.java | 92 +++++++++-
.../java/org/apache/paimon/rest/RESTCatalog.java | 48 ++++++
.../org/apache/paimon/rest/RESTCatalogServer.java | 190 +++++++++++++++++----
.../org/apache/paimon/rest/RESTCatalogTest.java | 60 ++++++-
.../org/apache/paimon/flink/RESTCatalogITCase.java | 3 +-
.../flink/procedure/FunctionProcedureITCase.java | 9 +-
13 files changed, 631 insertions(+), 73 deletions(-)
diff --git a/docs/static/rest-catalog-open-api.yaml
b/docs/static/rest-catalog-open-api.yaml
index 402a4553b3..8e2202729a 100644
--- a/docs/static/rest-catalog-open-api.yaml
+++ b/docs/static/rest-catalog-open-api.yaml
@@ -1398,6 +1398,11 @@ paths:
in: query
schema:
type: string
+ - name: functionNamePattern
+ description: A sql LIKE pattern (%) for function names. Currently,
only prefix matching is supported.
+ in: query
+ schema:
+ type: string
responses:
"200":
description: OK
@@ -1414,7 +1419,7 @@ paths:
post:
tags:
- function
- summary: Create Function
+ summary: Create function
operationId: createFunction
parameters:
- name: prefix
@@ -1446,6 +1451,97 @@ paths:
"500":
$ref: '#/components/responses/ServerErrorResponse'
+ /v1/{prefix}/databases/{database}/function-details:
+ get:
+ tags:
+ - function
+ summary: List function details
+ operationId: listFunctionDetails
+ parameters:
+ - name: prefix
+ in: path
+ required: true
+ schema:
+ type: string
+ - name: database
+ in: path
+ required: true
+ schema:
+ type: string
+ - name: maxResults
+ in: query
+ schema:
+ type: integer
+ format: int32
+ - name: pageToken
+ in: query
+ schema:
+ type: string
+ - name: functionNamePattern
+ description: A sql LIKE pattern (%) for function names. Currently,
only prefix matching is supported.
+ in: query
+ schema:
+ type: string
+ responses:
+ "200":
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ListFunctionDetailsResponse'
+ "401":
+ $ref: '#/components/responses/UnauthorizedErrorResponse'
+ "404":
+ $ref: '#/components/responses/DatabaseNotExistErrorResponse'
+ "500":
+ $ref: '#/components/responses/ServerErrorResponse'
+
+ /v1/{prefix}/functions:
+ get:
+ tags:
+ - function
+ summary: List functions globally
+ operationId: listFunctionsGlobally
+ description: List functions globally which matches the given database
name pattern and function 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: functionNamePattern
+ description: A sql LIKE pattern (%) for function names. All
functions 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/ListFunctionsGloballyResponse'
+ "401":
+ $ref: '#/components/responses/UnauthorizedErrorResponse'
+ "500":
+ $ref: '#/components/responses/ServerErrorResponse'
+
+
/v1/{prefix}/databases/{database}/functions/{function}:
get:
tags:
@@ -2739,6 +2835,13 @@ components:
$ref: '#/components/schemas/GetViewResponse'
nextPageToken:
type: string
+ ListFunctionsGloballyResponse:
+ type: object
+ properties:
+ functions:
+ type: array
+ items:
+ $ref: '#/components/schemas/Identifier'
ListViewsGloballyResponse:
type: object
properties:
@@ -2758,6 +2861,15 @@ components:
type: string
nextPageToken:
type: string
+ ListFunctionDetailsResponse:
+ type: object
+ properties:
+ functionDetails:
+ type: array
+ items:
+ $ref: '#/components/schemas/GetFunctionResponse'
+ nextPageToken:
+ type: string
GetFunctionResponse:
type: object
properties:
diff --git a/paimon-api/src/main/java/org/apache/paimon/catalog/Identifier.java
b/paimon-api/src/main/java/org/apache/paimon/catalog/Identifier.java
index f88431924e..309ddad3be 100644
--- a/paimon-api/src/main/java/org/apache/paimon/catalog/Identifier.java
+++ b/paimon-api/src/main/java/org/apache/paimon/catalog/Identifier.java
@@ -204,7 +204,7 @@ public class Identifier implements Serializable {
checkArgument(
!StringUtils.isNullOrWhitespaceOnly(fullName), "fullName
cannot be null or empty");
- String[] paths = fullName.split("\\.");
+ String[] paths = fullName.split("\\.", 2);
if (paths.length != 2) {
throw new IllegalArgumentException(
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 d80cb566ea..e615ca99db 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
@@ -61,6 +61,8 @@ import
org.apache.paimon.rest.responses.GetVersionSnapshotResponse;
import org.apache.paimon.rest.responses.GetViewResponse;
import org.apache.paimon.rest.responses.ListBranchesResponse;
import org.apache.paimon.rest.responses.ListDatabasesResponse;
+import org.apache.paimon.rest.responses.ListFunctionDetailsResponse;
+import org.apache.paimon.rest.responses.ListFunctionsGloballyResponse;
import org.apache.paimon.rest.responses.ListFunctionsResponse;
import org.apache.paimon.rest.responses.ListPartitionsResponse;
import org.apache.paimon.rest.responses.ListSnapshotsResponse;
@@ -139,6 +141,7 @@ public class RESTApi {
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 FUNCTION_NAME_PATTERN = "functionNamePattern";
public static final String PARTITION_NAME_PATTERN = "partitionNamePattern";
public static final long TOKEN_EXPIRATION_SAFE_TIME_MILLIS = 3_600_000L;
@@ -847,6 +850,127 @@ public class RESTApi {
restAuthFunction));
}
+ /**
+ * List functions by page.
+ *
+ * <p>Gets an array of functions for a database. There is no guarantee of
a specific ordering of
+ * the elements in the array.
+ *
+ * @param databaseName database name
+ * @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.
+ * @param functionNamePattern A sql LIKE pattern (%) for function names.
All functions will be
+ * returned if not set or empty. Currently, only prefix matching is
supported.
+ * @return {@link PagedList}: elements and nextPageToken.
+ * @throws NoSuchResourceException Exception thrown on HTTP 404 means the
database not exists
+ * @throws ForbiddenException Exception thrown on HTTP 403 means don't
have the permission for
+ * this database
+ */
+ public PagedList<String> listFunctionsPaged(
+ String databaseName,
+ @Nullable Integer maxResults,
+ @Nullable String pageToken,
+ @Nullable String functionNamePattern) {
+ ListFunctionsResponse response =
+ client.get(
+ resourcePaths.functions(databaseName),
+ buildPagedQueryParams(
+ maxResults,
+ pageToken,
+ Pair.of(FUNCTION_NAME_PATTERN,
functionNamePattern)),
+ ListFunctionsResponse.class,
+ restAuthFunction);
+ List<String> functions = response.functions();
+ if (functions == null) {
+ return new PagedList<>(emptyList(), null);
+ }
+ return new PagedList<>(functions, response.getNextPageToken());
+ }
+
+ /**
+ * List function details.
+ *
+ * <p>Gets an array of function details for a database. There is no
guarantee of a specific
+ * ordering of the elements in the array.
+ *
+ * @param databaseName database name
+ * @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.
+ * @param functionNamePattern A sql LIKE pattern (%) for function names.
All functions will be
+ * returned if not set or empty. Currently, only prefix matching is
supported.
+ * @return {@link PagedList}: elements and nextPageToken.
+ * @throws NoSuchResourceException Exception thrown on HTTP 404 means the
database not exists
+ * @throws ForbiddenException Exception thrown on HTTP 403 means don't
have the permission for
+ * this database
+ */
+ public PagedList<GetFunctionResponse> listFunctionDetailsPaged(
+ String databaseName,
+ @Nullable Integer maxResults,
+ @Nullable String pageToken,
+ @Nullable String functionNamePattern) {
+ ListFunctionDetailsResponse response =
+ client.get(
+ resourcePaths.functionDetails(databaseName),
+ buildPagedQueryParams(
+ maxResults,
+ pageToken,
+ Pair.of(FUNCTION_NAME_PATTERN,
functionNamePattern)),
+ ListFunctionDetailsResponse.class,
+ restAuthFunction);
+ List<GetFunctionResponse> functionDetails = response.data();
+ if (functionDetails == null) {
+ return new PagedList<>(emptyList(), null);
+ }
+ return new PagedList<>(functionDetails, response.getNextPageToken());
+ }
+
+ /**
+ * List functions for a catalog.
+ *
+ * <p>Gets an array of functions 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 functionNamePattern A sql LIKE pattern (%) for function names.
All functions 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<Identifier> listFunctionsPagedGlobally(
+ @Nullable String databaseNamePattern,
+ @Nullable String functionNamePattern,
+ @Nullable Integer maxResults,
+ @Nullable String pageToken) {
+ ListFunctionsGloballyResponse response =
+ client.get(
+ resourcePaths.functions(),
+ buildPagedQueryParams(
+ maxResults,
+ pageToken,
+ Pair.of(DATABASE_NAME_PATTERN,
databaseNamePattern),
+ Pair.of(FUNCTION_NAME_PATTERN,
functionNamePattern)),
+ ListFunctionsGloballyResponse.class,
+ restAuthFunction);
+ List<Identifier> functions = response.data();
+ if (functions == null) {
+ return new PagedList<>(emptyList(), null);
+ }
+ return new PagedList<>(functions, response.getNextPageToken());
+ }
+
/**
* Get a function by identifier.
*
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 99020f65d2..25f7ab9a2d 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
@@ -38,6 +38,7 @@ public class ResourcePaths {
protected static final String VIEW_DETAILS = "view-details";
protected static final String ROLLBACK = "rollback";
protected static final String FUNCTIONS = "functions";
+ protected static final String FUNCTION_DETAILS = "function-details";
private static final Joiner SLASH = Joiner.on("/").skipNulls();
@@ -251,6 +252,14 @@ public class ResourcePaths {
return SLASH.join(V1, prefix, DATABASES, encodeString(databaseName),
FUNCTIONS);
}
+ public String functions() {
+ return SLASH.join(V1, prefix, FUNCTIONS);
+ }
+
+ public String functionDetails(String databaseName) {
+ return SLASH.join(V1, prefix, DATABASES, encodeString(databaseName),
FUNCTION_DETAILS);
+ }
+
public String function(String databaseName, String functionName) {
return SLASH.join(
V1,
diff --git
a/paimon-api/src/main/java/org/apache/paimon/rest/responses/ListFunctionsResponse.java
b/paimon-api/src/main/java/org/apache/paimon/rest/responses/ListFunctionDetailsResponse.java
similarity index 66%
copy from
paimon-api/src/main/java/org/apache/paimon/rest/responses/ListFunctionsResponse.java
copy to
paimon-api/src/main/java/org/apache/paimon/rest/responses/ListFunctionDetailsResponse.java
index 0c2165077a..c0d9919ba9 100644
---
a/paimon-api/src/main/java/org/apache/paimon/rest/responses/ListFunctionsResponse.java
+++
b/paimon-api/src/main/java/org/apache/paimon/rest/responses/ListFunctionDetailsResponse.java
@@ -25,34 +25,35 @@ import
org.apache.paimon.shade.jackson2.com.fasterxml.jackson.annotation.JsonPro
import java.util.List;
-/** Response for listing functions. */
+/** Response for listing function details. */
@JsonIgnoreProperties(ignoreUnknown = true)
-public class ListFunctionsResponse implements PagedResponse<String> {
+public class ListFunctionDetailsResponse implements
PagedResponse<GetFunctionResponse> {
- private static final String FIELD_FUNCTIONS = "functions";
+ private static final String FIELD_FUNCTION_DETAILS = "functionDetails";
private static final String FIELD_NEXT_PAGE_TOKEN = "nextPageToken";
- @JsonProperty(FIELD_FUNCTIONS)
- private final List<String> functions;
+ @JsonProperty(FIELD_FUNCTION_DETAILS)
+ private final List<GetFunctionResponse> functionDetails;
@JsonProperty(FIELD_NEXT_PAGE_TOKEN)
private final String nextPageToken;
- public ListFunctionsResponse(@JsonProperty(FIELD_FUNCTIONS) List<String>
functions) {
- this(functions, null);
+ public ListFunctionDetailsResponse(
+ @JsonProperty(FIELD_FUNCTION_DETAILS) List<GetFunctionResponse>
functionDetails) {
+ this(functionDetails, null);
}
@JsonCreator
- public ListFunctionsResponse(
- @JsonProperty(FIELD_FUNCTIONS) List<String> functions,
+ public ListFunctionDetailsResponse(
+ @JsonProperty(FIELD_FUNCTION_DETAILS) List<GetFunctionResponse>
functionDetails,
@JsonProperty(FIELD_NEXT_PAGE_TOKEN) String nextPageToken) {
- this.functions = functions;
+ this.functionDetails = functionDetails;
this.nextPageToken = nextPageToken;
}
- @JsonGetter(FIELD_FUNCTIONS)
- public List<String> functions() {
- return this.functions;
+ @JsonGetter(FIELD_FUNCTION_DETAILS)
+ public List<GetFunctionResponse> getFunctionDetails() {
+ return this.functionDetails;
}
@JsonGetter(FIELD_NEXT_PAGE_TOKEN)
@@ -61,7 +62,7 @@ public class ListFunctionsResponse implements
PagedResponse<String> {
}
@Override
- public List<String> data() {
- return functions();
+ public List<GetFunctionResponse> data() {
+ return getFunctionDetails();
}
}
diff --git
a/paimon-api/src/main/java/org/apache/paimon/rest/responses/ListFunctionsResponse.java
b/paimon-api/src/main/java/org/apache/paimon/rest/responses/ListFunctionsGloballyResponse.java
similarity index 82%
copy from
paimon-api/src/main/java/org/apache/paimon/rest/responses/ListFunctionsResponse.java
copy to
paimon-api/src/main/java/org/apache/paimon/rest/responses/ListFunctionsGloballyResponse.java
index 0c2165077a..4a077114d2 100644
---
a/paimon-api/src/main/java/org/apache/paimon/rest/responses/ListFunctionsResponse.java
+++
b/paimon-api/src/main/java/org/apache/paimon/rest/responses/ListFunctionsGloballyResponse.java
@@ -18,6 +18,8 @@
package org.apache.paimon.rest.responses;
+import org.apache.paimon.catalog.Identifier;
+
import
org.apache.paimon.shade.jackson2.com.fasterxml.jackson.annotation.JsonCreator;
import
org.apache.paimon.shade.jackson2.com.fasterxml.jackson.annotation.JsonGetter;
import
org.apache.paimon.shade.jackson2.com.fasterxml.jackson.annotation.JsonIgnoreProperties;
@@ -27,31 +29,27 @@ import java.util.List;
/** Response for listing functions. */
@JsonIgnoreProperties(ignoreUnknown = true)
-public class ListFunctionsResponse implements PagedResponse<String> {
+public class ListFunctionsGloballyResponse implements
PagedResponse<Identifier> {
private static final String FIELD_FUNCTIONS = "functions";
private static final String FIELD_NEXT_PAGE_TOKEN = "nextPageToken";
@JsonProperty(FIELD_FUNCTIONS)
- private final List<String> functions;
+ private final List<Identifier> functions;
@JsonProperty(FIELD_NEXT_PAGE_TOKEN)
private final String nextPageToken;
- public ListFunctionsResponse(@JsonProperty(FIELD_FUNCTIONS) List<String>
functions) {
- this(functions, null);
- }
-
@JsonCreator
- public ListFunctionsResponse(
- @JsonProperty(FIELD_FUNCTIONS) List<String> functions,
+ public ListFunctionsGloballyResponse(
+ @JsonProperty(FIELD_FUNCTIONS) List<Identifier> functions,
@JsonProperty(FIELD_NEXT_PAGE_TOKEN) String nextPageToken) {
this.functions = functions;
this.nextPageToken = nextPageToken;
}
@JsonGetter(FIELD_FUNCTIONS)
- public List<String> functions() {
+ public List<Identifier> functions() {
return this.functions;
}
@@ -61,7 +59,7 @@ public class ListFunctionsResponse implements
PagedResponse<String> {
}
@Override
- public List<String> data() {
+ public List<Identifier> data() {
return functions();
}
}
diff --git
a/paimon-api/src/main/java/org/apache/paimon/rest/responses/ListFunctionsResponse.java
b/paimon-api/src/main/java/org/apache/paimon/rest/responses/ListFunctionsResponse.java
index 0c2165077a..0ac536dc4a 100644
---
a/paimon-api/src/main/java/org/apache/paimon/rest/responses/ListFunctionsResponse.java
+++
b/paimon-api/src/main/java/org/apache/paimon/rest/responses/ListFunctionsResponse.java
@@ -38,10 +38,6 @@ public class ListFunctionsResponse implements
PagedResponse<String> {
@JsonProperty(FIELD_NEXT_PAGE_TOKEN)
private final String nextPageToken;
- public ListFunctionsResponse(@JsonProperty(FIELD_FUNCTIONS) List<String>
functions) {
- this(functions, null);
- }
-
@JsonCreator
public ListFunctionsResponse(
@JsonProperty(FIELD_FUNCTIONS) List<String> functions,
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 0ae69f84d2..6eda935c26 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
@@ -443,7 +443,7 @@ public interface Catalog extends AutoCloseable {
* 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()}
+ * @throws UnsupportedOperationException if it does not {@link
#supportsListByPattern()}
*/
default PagedList<String> listViewsPaged(
String databaseName,
@@ -470,7 +470,7 @@ public interface Catalog extends AutoCloseable {
* 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()}
+ * @throws UnsupportedOperationException if it does not {@link
#supportsListByPattern()}
*/
default PagedList<View> listViewDetailsPaged(
String databaseName,
@@ -484,8 +484,6 @@ public interface Catalog extends AutoCloseable {
/**
* 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
@@ -496,9 +494,9 @@ public interface Catalog extends AutoCloseable {
* @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()}}.
+ * viewNamePattern and next page token
+ * @throws UnsupportedOperationException if it does not {@link
#supportsListObjectsPaged()} or
+ * does not {@link #supportsListByPattern()}}.
*/
default PagedList<Identifier> listViewsPagedGlobally(
@Nullable String databaseNamePattern,
@@ -785,6 +783,86 @@ public interface Catalog extends AutoCloseable {
*/
List<String> listFunctions(String databaseName) throws
DatabaseNotExistException;
+ /**
+ * Get paged list names of function under this database. An empty list is
returned if none
+ * function exists.
+ *
+ * @param databaseName Name of the database to list function.
+ * @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.
+ * @param functionNamePattern A sql LIKE pattern (%) for function names.
All function will be
+ * returned if not set or empty. Currently, only prefix matching is
supported.
+ * @return a list of the names of function with provided page size in this
database and next
+ * page token, or a list of the names of all function in this database
if the catalog does
+ * not {@link #supportsListObjectsPaged()}.
+ * @throws DatabaseNotExistException if the database does not exist
+ * @throws UnsupportedOperationException if it does not {@link
#supportsListByPattern()}
+ */
+ default PagedList<String> listFunctionsPaged(
+ String databaseName,
+ @Nullable Integer maxResults,
+ @Nullable String pageToken,
+ @Nullable String functionNamePattern)
+ throws DatabaseNotExistException {
+ return new PagedList<>(listFunctions(databaseName), null);
+ }
+
+ /**
+ * Gets an array of function for a catalog.
+ *
+ * @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 functionNamePattern A sql LIKE pattern (%) for function names.
All functions 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 function identifier with provided page size under
this
+ * databaseNamePattern & functionNamePattern and next page token
+ * @throws UnsupportedOperationException if it does not {@link
#supportsListObjectsPaged()} or
+ * does not {@link #supportsListByPattern()}}.
+ */
+ default PagedList<Identifier> listFunctionsPagedGlobally(
+ @Nullable String databaseNamePattern,
+ @Nullable String functionNamePattern,
+ @Nullable Integer maxResults,
+ @Nullable String pageToken) {
+ throw new UnsupportedOperationException(
+ "Current Catalog does not support listFunctionsPagedGlobally");
+ }
+
+ /**
+ * Get paged list function details under this database. An empty list is
returned if none
+ * function exists.
+ *
+ * @param databaseName Name of the database to list function detail.
+ * @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.
+ * @param functionNamePattern A sql LIKE pattern (%) for function names.
All function details
+ * will be returned if not set or empty. Currently, only prefix
matching is supported.
+ * @return a list of the function detail with provided page size (@param
maxResults) in this
+ * database and next page token, or a list of the details of all
functions in this database
+ * if the catalog does not {@link #supportsListObjectsPaged()}.
+ * @throws DatabaseNotExistException if the database does not exist
+ * @throws UnsupportedOperationException if it does not {@link
#supportsListByPattern()}
+ */
+ default PagedList<Function> listFunctionDetailsPaged(
+ String databaseName,
+ @Nullable Integer maxResults,
+ @Nullable String pageToken,
+ @Nullable String functionNamePattern)
+ throws DatabaseNotExistException {
+ return new PagedList<>(Collections.emptyList(), null);
+ }
+
/**
* Get function by name.
*
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 72ff174c88..b84903dafa 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
@@ -30,6 +30,7 @@ import org.apache.paimon.catalog.PropertyChange;
import org.apache.paimon.catalog.TableMetadata;
import org.apache.paimon.fs.FileIO;
import org.apache.paimon.fs.Path;
+import org.apache.paimon.function.Function;
import org.apache.paimon.function.FunctionChange;
import org.apache.paimon.partition.Partition;
import org.apache.paimon.partition.PartitionStatistics;
@@ -730,6 +731,53 @@ public class RESTCatalog implements Catalog {
}
}
+ @Override
+ public PagedList<String> listFunctionsPaged(
+ String databaseName,
+ @Nullable Integer maxResults,
+ @Nullable String pageToken,
+ @Nullable String functionNamePattern)
+ throws DatabaseNotExistException {
+ try {
+ return api.listFunctionsPaged(databaseName, maxResults, pageToken,
functionNamePattern);
+ } catch (NoSuchResourceException e) {
+ throw new DatabaseNotExistException(databaseName);
+ }
+ }
+
+ @Override
+ public PagedList<Identifier> listFunctionsPagedGlobally(
+ @Nullable String databaseNamePattern,
+ @Nullable String functionNamePattern,
+ @Nullable Integer maxResults,
+ @Nullable String pageToken) {
+ PagedList<Identifier> functions =
+ api.listFunctionsPagedGlobally(
+ databaseNamePattern, functionNamePattern, maxResults,
pageToken);
+ return new PagedList<>(functions.getElements(),
functions.getNextPageToken());
+ }
+
+ @Override
+ public PagedList<Function> listFunctionDetailsPaged(
+ String databaseName,
+ @Nullable Integer maxResults,
+ @Nullable String pageToken,
+ @Nullable String functionNamePattern)
+ throws DatabaseNotExistException {
+ try {
+ PagedList<GetFunctionResponse> functions =
+ api.listFunctionDetailsPaged(
+ databaseName, maxResults, pageToken,
functionNamePattern);
+ return new PagedList<>(
+ functions.getElements().stream()
+ .map(v ->
v.toFunction(Identifier.create(databaseName, v.name())))
+ .collect(Collectors.toList()),
+ functions.getNextPageToken());
+ } catch (NoSuchResourceException e) {
+ throw new DatabaseNotExistException(databaseName);
+ }
+ }
+
@Override
public View getView(Identifier identifier) throws ViewNotExistException {
try {
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 e330362c3c..f46621913c 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
@@ -72,6 +72,8 @@ import
org.apache.paimon.rest.responses.GetVersionSnapshotResponse;
import org.apache.paimon.rest.responses.GetViewResponse;
import org.apache.paimon.rest.responses.ListBranchesResponse;
import org.apache.paimon.rest.responses.ListDatabasesResponse;
+import org.apache.paimon.rest.responses.ListFunctionDetailsResponse;
+import org.apache.paimon.rest.responses.ListFunctionsGloballyResponse;
import org.apache.paimon.rest.responses.ListFunctionsResponse;
import org.apache.paimon.rest.responses.ListPartitionsResponse;
import org.apache.paimon.rest.responses.ListSnapshotsResponse;
@@ -132,11 +134,17 @@ 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.FUNCTION_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;
import static org.apache.paimon.rest.RESTApi.TABLE_NAME_PATTERN;
import static org.apache.paimon.rest.RESTApi.VIEW_NAME_PATTERN;
+import static org.apache.paimon.rest.ResourcePaths.FUNCTIONS;
+import static org.apache.paimon.rest.ResourcePaths.FUNCTION_DETAILS;
+import static org.apache.paimon.rest.ResourcePaths.TABLE_DETAILS;
+import static org.apache.paimon.rest.ResourcePaths.VIEWS;
+import static org.apache.paimon.rest.ResourcePaths.VIEW_DETAILS;
/** Mock REST server for testing. */
public class RESTCatalogServer {
@@ -292,6 +300,9 @@ public class RESTCatalogServer {
return tablesHandle(parameters);
} else if (StringUtils.startsWith(request.getPath(),
resourcePaths.views())) {
return viewsHandle(parameters);
+ } else if (StringUtils.startsWith(
+ request.getPath(), resourcePaths.functions())) {
+ return functionsHandle(parameters);
} else if (request.getPath().startsWith(databaseUri)) {
String[] resources =
request.getPath()
@@ -305,17 +316,19 @@ public class RESTCatalogServer {
throw new
Catalog.DatabaseNotExistException(databaseName);
}
boolean isFunctions =
- resources.length == 2 &&
resources[1].startsWith("functions");
+ resources.length == 2 &&
resources[1].startsWith(FUNCTIONS);
boolean isFunction =
- resources.length == 3 &&
resources[1].startsWith("functions");
- boolean isViews = resources.length == 2 &&
resources[1].startsWith("views");
+ resources.length == 3 &&
resources[1].startsWith(FUNCTIONS);
+ boolean isFunctionsDetails =
+ resources.length == 2 &&
resources[1].startsWith(FUNCTION_DETAILS);
+ boolean isViews = resources.length == 2 &&
resources[1].startsWith(VIEWS);
boolean isViewsDetails =
- resources.length == 2 &&
resources[1].startsWith("view-details");
+ resources.length == 2 &&
resources[1].startsWith(VIEW_DETAILS);
boolean isTables =
resources.length == 2
&&
resources[1].startsWith(ResourcePaths.TABLES);
boolean isTableDetails =
- resources.length == 2 &&
resources[1].startsWith("table-details");
+ resources.length == 2 &&
resources[1].startsWith(TABLE_DETAILS);
boolean isView =
resources.length == 3
&& "views".equals(resources[1])
@@ -460,6 +473,9 @@ public class RESTCatalogServer {
} else if (isFunction) {
return functionApiHandler(
identifier, restAuthParameter.method(),
data, parameters);
+ } else if (isFunctionsDetails) {
+ return functionDetailsHandle(
+ restAuthParameter.method(), databaseName,
parameters);
} else if (isViews) {
return viewsHandle(
restAuthParameter.method(),
@@ -846,23 +862,40 @@ public class RESTCatalogServer {
throws Exception {
switch (method) {
case "GET":
- List<String> functions = new
ArrayList<>(functionStore.keySet());
+ String namePattern = parameters.get(FUNCTION_NAME_PATTERN);
+ List<String> functions =
+ (new ArrayList<>(functionStore.keySet()))
+ .stream()
+ .map(n -> Identifier.fromString(n))
+ .filter(
+ identifier ->
+ identifier
+
.getDatabaseName()
+
.equals(databaseName)
+ &&
(Objects.isNull(namePattern)
+ ||
matchNamePattern(
+
identifier
+
.getObjectName(),
+
namePattern)))
+ .map(i -> i.getObjectName())
+ .collect(Collectors.toList());
return generateFinalListFunctionsResponse(parameters,
functions);
case "POST":
CreateFunctionRequest requestBody =
RESTApi.fromJson(data, CreateFunctionRequest.class);
String functionName = requestBody.name();
- if (!functionStore.containsKey(functionName)) {
+ Identifier identity = Identifier.create(databaseName,
functionName);
+ if (!functionStore.containsKey(identity.getFullName())) {
Function function =
new FunctionImpl(
- Identifier.create(databaseName,
functionName),
+ identity,
requestBody.inputParams(),
requestBody.returnParams(),
requestBody.isDeterministic(),
requestBody.definitions(),
requestBody.comment(),
requestBody.options());
- functionStore.put(functionName, function);
+ functionStore.put(identity.getFullName(), function);
return new MockResponse().setResponseCode(200);
} else {
throw new Catalog.FunctionAlreadyExistException(
@@ -876,31 +909,16 @@ public class RESTCatalogServer {
private MockResponse functionApiHandler(
Identifier identifier, String method, String data, Map<String,
String> parameters)
throws Exception {
- String functionName = identifier.getObjectName();
- if (!functionStore.containsKey(functionName)) {
+ if (!functionStore.containsKey(identifier.getFullName())) {
throw new Catalog.FunctionNotExistException(identifier);
}
- Function function = functionStore.get(functionName);
+ Function function = functionStore.get(identifier.getFullName());
switch (method) {
case "DELETE":
- functionStore.remove(functionName);
+ functionStore.remove(identifier.getFullName());
break;
case "GET":
- GetFunctionResponse response =
- new GetFunctionResponse(
- UUID.randomUUID().toString(),
- function.name(),
- function.inputParams().orElse(null),
- function.returnParams().orElse(null),
- function.isDeterministic(),
- function.definitions(),
- function.comment(),
- function.options(),
- "owner",
- 1L,
- "owner",
- 1L,
- "owner");
+ GetFunctionResponse response = toGetFunctionResponse(function);
return mockResponse(response, 200);
case "POST":
AlterFunctionRequest requestBody =
@@ -956,14 +974,14 @@ public class RESTCatalogServer {
}
function =
new FunctionImpl(
- Identifier.create(null, functionName),
+ identifier,
function.inputParams().orElse(null),
function.returnParams().orElse(null),
function.isDeterministic(),
newDefinitions,
newComment,
newOptions);
- functionStore.put(functionName, function);
+ functionStore.put(identifier.getFullName(), function);
break;
default:
return new MockResponse().setResponseCode(404);
@@ -971,6 +989,74 @@ public class RESTCatalogServer {
return new MockResponse().setResponseCode(200);
}
+ private MockResponse functionDetailsHandle(
+ String method, String databaseName, Map<String, String>
parameters) {
+ RESTResponse response;
+ if ("GET".equals(method)) {
+
+ List<GetFunctionResponse> functionsDetails =
+ listFunctionDetails(databaseName, parameters);
+ if (!functionsDetails.isEmpty()) {
+
+ int maxResults;
+ try {
+ maxResults = getMaxResults(parameters);
+ } catch (NumberFormatException e) {
+ return handleInvalidMaxResults(parameters);
+ }
+ String pageToken = parameters.getOrDefault(PAGE_TOKEN, null);
+
+ PagedList<GetFunctionResponse> pagedFunctionDetails =
+ buildPagedEntities(functionsDetails, maxResults,
pageToken);
+ response =
+ new ListFunctionDetailsResponse(
+ pagedFunctionDetails.getElements(),
+ pagedFunctionDetails.getNextPageToken());
+ } else {
+ response = new
ListFunctionDetailsResponse(Collections.emptyList(), null);
+ }
+ return mockResponse(response, 200);
+ } else {
+ return new MockResponse().setResponseCode(404);
+ }
+ }
+
+ private List<GetFunctionResponse> listFunctionDetails(
+ String databaseName, Map<String, String> parameters) {
+ String namePattern = parameters.get(FUNCTION_NAME_PATTERN);
+ return functionStore.keySet().stream()
+ .map(Identifier::fromString)
+ .filter(identifier ->
identifier.getDatabaseName().equals(databaseName))
+ .filter(
+ identifier ->
+ (Objects.isNull(namePattern)
+ || matchNamePattern(
+ identifier.getObjectName(),
namePattern)))
+ .map(
+ identifier -> {
+ return toGetFunctionResponse(
+
functionStore.get(identifier.getFullName()));
+ })
+ .collect(Collectors.toList());
+ }
+
+ private GetFunctionResponse toGetFunctionResponse(Function function) {
+ return new GetFunctionResponse(
+ UUID.randomUUID().toString(),
+ function.name(),
+ function.inputParams().orElse(null),
+ function.returnParams().orElse(null),
+ function.isDeterministic(),
+ function.definitions(),
+ function.comment(),
+ function.options(),
+ "owner",
+ 1L,
+ "owner",
+ 1L,
+ "owner");
+ }
+
private MockResponse databasesApiHandler(
String method, String data, Map<String, String> parameters) throws
Exception {
switch (method) {
@@ -1675,6 +1761,44 @@ public class RESTCatalogServer {
.collect(Collectors.toList());
}
+ private MockResponse functionsHandle(Map<String, String> parameters) {
+ RESTResponse response;
+ List<Identifier> functions = listFunctions(parameters);
+ if (!functions.isEmpty()) {
+ int maxResults;
+ try {
+ maxResults = getMaxResults(parameters);
+ } catch (NumberFormatException e) {
+ return handleInvalidMaxResults(parameters);
+ }
+ String pageToken = parameters.get(PAGE_TOKEN);
+ PagedList<Identifier> pagedFunctions =
+ buildPagedEntities(functions, maxResults, pageToken);
+ response =
+ new ListFunctionsGloballyResponse(
+ pagedFunctions.getElements(),
pagedFunctions.getNextPageToken());
+ } else {
+ response = new
ListFunctionsGloballyResponse(Collections.emptyList(), null);
+ }
+ return mockResponse(response, 200);
+ }
+
+ private List<Identifier> listFunctions(Map<String, String> parameters) {
+ String namePattern = parameters.get(FUNCTION_NAME_PATTERN);
+ String databaseNamePattern = parameters.get(DATABASE_NAME_PATTERN);
+ List<Identifier> fullFunctions = new ArrayList<>();
+ for (Map.Entry<String, Function> entry : functionStore.entrySet()) {
+ Identifier identifier = Identifier.fromString(entry.getKey());
+ if ((Objects.isNull(databaseNamePattern))
+ || matchNamePattern(identifier.getDatabaseName(),
databaseNamePattern)
+ && (Objects.isNull(namePattern)
+ ||
matchNamePattern(identifier.getObjectName(), namePattern))) {
+ fullFunctions.add(identifier);
+ }
+ }
+ return fullFunctions;
+ }
+
private MockResponse viewsHandle(Map<String, String> parameters) {
RESTResponse response;
List<Identifier> views = listViews(parameters);
@@ -2106,6 +2230,12 @@ public class RESTCatalogServer {
return ((GetViewResponse) entity).getName();
} else if (entity instanceof Partition) {
return PartitionUtils.buildPartitionName(((Partition)
entity).spec());
+ } else if (entity instanceof GetFunctionResponse) {
+ GetFunctionResponse functionResponse = (GetFunctionResponse)
entity;
+ return functionResponse.name();
+ } else if (entity instanceof Identifier) {
+ Identifier identifier = (Identifier) entity;
+ return identifier.getFullName();
} else {
return entity.toString();
}
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 6aabce23a4..c476cbc78b 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
@@ -82,6 +82,7 @@ import java.util.Map;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicInteger;
+import java.util.stream.Collectors;
import static java.util.Collections.emptyMap;
import static java.util.Collections.singletonList;
@@ -1662,7 +1663,7 @@ public abstract class RESTCatalogTest extends
CatalogTestBase {
IllegalArgumentException.class,
() -> catalog.dropFunction(identifierWithoutAlphabet, true));
- Identifier identifier = new Identifier("rest_catalog_db",
"function.na_me-01");
+ Identifier identifier =
Identifier.fromString("rest_catalog_db.function.na_me-01");
Function function = MockRESTMessage.function(identifier);
catalog.createFunction(identifier, function, true);
@@ -1689,6 +1690,63 @@ public abstract class RESTCatalogTest extends
CatalogTestBase {
Catalog.FunctionNotExistException.class, () ->
catalog.getFunction(identifier));
}
+ @Test
+ void testListFunctions() throws Exception {
+ String db1 = "db_rest_catalog_db";
+ String db2 = "db2_rest_catalog";
+ Identifier identifier = new Identifier(db1, "list_function");
+ Identifier identifier1 = new Identifier(db1, "function");
+ Identifier identifier2 = new Identifier(db2, "list_function");
+ Identifier identifier3 = new Identifier(db2, "function");
+ catalog.createDatabase(db1, false);
+ catalog.createDatabase(db2, false);
+ catalog.createFunction(identifier,
MockRESTMessage.function(identifier), true);
+ catalog.createFunction(identifier1,
MockRESTMessage.function(identifier1), true);
+ catalog.createFunction(identifier2,
MockRESTMessage.function(identifier2), true);
+ catalog.createFunction(identifier3,
MockRESTMessage.function(identifier3), true);
+ assertThat(catalog.listFunctionsPaged(db1, null, null,
null).getElements())
+ .containsExactlyInAnyOrder(identifier.getObjectName(),
identifier1.getObjectName());
+ assertThat(catalog.listFunctionsPaged(db1, 1, null,
null).getElements())
+ .containsAnyOf(identifier.getObjectName(),
identifier1.getObjectName());
+ assertThat(
+ catalog.listFunctionsPaged(db1, 1,
identifier1.getObjectName(), null)
+ .getElements())
+ .containsExactlyInAnyOrder(identifier.getObjectName());
+ assertThat(catalog.listFunctionsPaged(db1, null, null,
"func%").getElements())
+ .containsExactlyInAnyOrder(identifier1.getObjectName());
+ assertThat(
+ catalog.listFunctionsPagedGlobally("db2_rest%",
"func%", null, null)
+ .getElements())
+ .containsExactlyInAnyOrder(identifier3);
+ assertThat(catalog.listFunctionsPagedGlobally("db2_rest%", null, 1,
null).getElements())
+ .containsAnyOf(identifier2, identifier3);
+ assertThat(
+ catalog.listFunctionsPagedGlobally(
+ "db2_rest%", null, 1,
identifier3.getFullName())
+ .getElements())
+ .containsExactlyInAnyOrder(identifier2);
+
+ assertThat(
+ catalog.listFunctionDetailsPaged(db1, 1, null,
null).getElements().stream()
+ .map(f -> f.fullName())
+ .collect(Collectors.toList()))
+ .containsAnyOf(identifier.getFullName(),
identifier1.getFullName());
+
+ assertThat(
+ catalog.listFunctionDetailsPaged(db2, 4, null,
"func%").getElements()
+ .stream()
+ .map(f -> f.fullName())
+ .collect(Collectors.toList()))
+ .containsExactly(identifier3.getFullName());
+
+ assertThat(
+ catalog.listFunctionDetailsPaged(db2, 1,
identifier3.getObjectName(), null)
+ .getElements().stream()
+ .map(f -> f.fullName())
+ .collect(Collectors.toList()))
+ .contains(identifier2.getFullName());
+ }
+
@Test
void testAlterFunction() throws Exception {
Identifier identifier = new Identifier("rest_catalog_db",
"alter_function_name");
diff --git
a/paimon-flink/paimon-flink-common/src/test/java/org/apache/paimon/flink/RESTCatalogITCase.java
b/paimon-flink/paimon-flink-common/src/test/java/org/apache/paimon/flink/RESTCatalogITCase.java
index 7742bbb3f3..d79aa713c9 100644
---
a/paimon-flink/paimon-flink-common/src/test/java/org/apache/paimon/flink/RESTCatalogITCase.java
+++
b/paimon-flink/paimon-flink-common/src/test/java/org/apache/paimon/flink/RESTCatalogITCase.java
@@ -136,7 +136,8 @@ class RESTCatalogITCase extends RESTCatalogITCaseBase {
jarResourcePath,
ResourceType.JAR,
jarResourcePath2));
- assertThat(batchSql(String.format("SHOW
FUNCTIONS"))).contains(Row.of(functionName));
+ assertThat(batchSql(String.format("SHOW FUNCTIONS in %s",
DATABASE_NAME)))
+ .contains(Row.of(functionName));
ObjectPath functionObjectPath = new ObjectPath(DATABASE_NAME,
functionName);
CatalogFunction getFunction = catalog.getFunction(functionObjectPath);
assertThat(getFunction).isEqualTo(function);
diff --git
a/paimon-flink/paimon-flink-common/src/test/java/org/apache/paimon/flink/procedure/FunctionProcedureITCase.java
b/paimon-flink/paimon-flink-common/src/test/java/org/apache/paimon/flink/procedure/FunctionProcedureITCase.java
index 33ba3e9a3f..ce784c8b88 100644
---
a/paimon-flink/paimon-flink-common/src/test/java/org/apache/paimon/flink/procedure/FunctionProcedureITCase.java
+++
b/paimon-flink/paimon-flink-common/src/test/java/org/apache/paimon/flink/procedure/FunctionProcedureITCase.java
@@ -45,7 +45,8 @@ public class FunctionProcedureITCase extends
RESTCatalogITCaseBase {
+ "'[{\"id\": 0, \"name\":\"area\",
\"type\":\"BIGINT\"}]', true, 'comment', 'k1=v1,k2=v2')",
DATABASE_NAME, functionName));
assertThat(result.toString()).contains("Success");
- assertThat(batchSql(String.format("SHOW
FUNCTIONS"))).contains(Row.of(functionName));
+ assertThat(batchSql(String.format("SHOW FUNCTIONS in %s",
DATABASE_NAME)))
+ .contains(Row.of(functionName));
result =
sql(
String.format(
@@ -54,11 +55,13 @@ public class FunctionProcedureITCase extends
RESTCatalogITCaseBase {
assertThat(result.toString()).contains("Success");
Catalog catalog = tEnv.getCatalog("PAIMON").get();
ObjectPath functionObjectPath = new ObjectPath(DATABASE_NAME,
functionName);
- assertThat(batchSql(String.format("SHOW
FUNCTIONS"))).contains(Row.of(functionName));
+ assertThat(batchSql(String.format("SHOW FUNCTIONS in %s",
DATABASE_NAME)))
+ .contains(Row.of(functionName));
CatalogFunction getFunction = catalog.getFunction(functionObjectPath);
assertThat(getFunction.getClassName()).isEqualTo("xxxx");
result = sql(String.format("CALL sys.drop_function('%s.%s')",
DATABASE_NAME, functionName));
assertThat(result.toString()).contains("Success");
- assertThat(batchSql(String.format("SHOW
FUNCTIONS"))).doesNotContain(Row.of(functionName));
+ assertThat(batchSql(String.format("SHOW FUNCTIONS in %s",
DATABASE_NAME)))
+ .doesNotContain(Row.of(functionName));
}
}