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 892646ea85 [core] fix: function need belong to database (#5613)
892646ea85 is described below

commit 892646ea85e2ab5baf0eb022c172166f5e8b164d
Author: jerry <[email protected]>
AuthorDate: Fri May 16 17:48:34 2025 +0800

    [core] fix: function need belong to database (#5613)
---
 docs/static/rest-catalog-open-api.yaml             |  37 ++++++-
 .../java/org/apache/paimon/function/Function.java  |   9 +-
 .../org/apache/paimon/function/FunctionImpl.java   |  28 +++---
 .../main/java/org/apache/paimon/rest/RESTApi.java  |  71 +++++++++++---
 .../java/org/apache/paimon/rest/ResourcePaths.java |  14 ++-
 .../rest/requests/CreateFunctionRequest.java       |   4 +-
 .../paimon/rest/responses/GetFunctionResponse.java |   6 +-
 .../org/apache/paimon/catalog/AbstractCatalog.java |  14 +--
 .../java/org/apache/paimon/catalog/Catalog.java    | 109 ++++++++++++---------
 .../org/apache/paimon/catalog/DelegateCatalog.java |  22 ++---
 .../java/org/apache/paimon/rest/RESTCatalog.java   |  42 ++++----
 .../org/apache/paimon/rest/MockRESTMessage.java    |  19 ++--
 .../org/apache/paimon/rest/RESTCatalogServer.java  |  86 ++++++----------
 .../org/apache/paimon/rest/RESTCatalogTest.java    |  64 ++++++------
 14 files changed, 299 insertions(+), 226 deletions(-)

diff --git a/docs/static/rest-catalog-open-api.yaml 
b/docs/static/rest-catalog-open-api.yaml
index efd483e7b6..acb7d57f2a 100644
--- a/docs/static/rest-catalog-open-api.yaml
+++ b/docs/static/rest-catalog-open-api.yaml
@@ -1272,7 +1272,7 @@ paths:
           $ref: '#/components/responses/ViewAlreadyExistErrorResponse'
         "500":
           $ref: '#/components/responses/ServerErrorResponse'
-  /v1/{prefix}/functions:
+  /v1/{prefix}/databases/{database}/functions:
     get:
       tags:
         - function
@@ -1284,6 +1284,11 @@ paths:
           required: true
           schema:
             type: string
+        - name: database
+          in: path
+          required: true
+          schema:
+            type: string
         - name: maxResults
           in: query
           schema:
@@ -1302,6 +1307,8 @@ paths:
                 $ref: '#/components/schemas/ListFunctionsResponse'
         "401":
           $ref: '#/components/responses/UnauthorizedErrorResponse'
+        "404":
+          $ref: '#/components/responses/DatabaseNotExistErrorResponse'
         "500":
           $ref: '#/components/responses/ServerErrorResponse'
     post:
@@ -1315,6 +1322,11 @@ paths:
           required: true
           schema:
             type: string
+        - name: database
+          in: path
+          required: true
+          schema:
+            type: string
       requestBody:
         content:
           application/json:
@@ -1327,12 +1339,14 @@ paths:
           $ref: '#/components/responses/BadRequestErrorResponse'
         "401":
           $ref: '#/components/responses/UnauthorizedErrorResponse'
+        "404":
+          $ref: '#/components/responses/DatabaseNotExistErrorResponse'
         "409":
           $ref: '#/components/responses/FunctionAlreadyExistErrorResponse'
         "500":
           $ref: '#/components/responses/ServerErrorResponse'
 
-  /v1/{prefix}/functions/{function}:
+  /v1/{prefix}/databases/{database}/functions/{function}:
     get:
       tags:
         - function
@@ -1344,6 +1358,11 @@ paths:
           required: true
           schema:
             type: string
+        - name: database
+          in: path
+          required: true
+          schema:
+            type: string
         - name: function
           in: path
           required: true
@@ -1359,7 +1378,7 @@ paths:
         "401":
           $ref: '#/components/responses/UnauthorizedErrorResponse'
         "404":
-          $ref: '#/components/responses/DatabaseNotExistErrorResponse'
+          $ref: '#/components/responses/FunctionNotExistErrorResponse'
         "500":
           $ref: '#/components/responses/ServerErrorResponse'
     post:
@@ -1373,6 +1392,11 @@ paths:
           required: true
           schema:
             type: string
+        - name: database
+          in: path
+          required: true
+          schema:
+            type: string
         - name: function
           in: path
           required: true
@@ -1403,6 +1427,11 @@ paths:
           required: true
           schema:
             type: string
+        - name: database
+          in: path
+          required: true
+          schema:
+            type: string
         - name: function
           in: path
           required: true
@@ -2573,8 +2602,6 @@ components:
     GetFunctionResponse:
       type: object
       properties:
-        uuid:
-          type: string
         name:
           type: string
         inputParams:
diff --git a/paimon-api/src/main/java/org/apache/paimon/function/Function.java 
b/paimon-api/src/main/java/org/apache/paimon/function/Function.java
index 1f892387d4..b5d01e63c1 100644
--- a/paimon-api/src/main/java/org/apache/paimon/function/Function.java
+++ b/paimon-api/src/main/java/org/apache/paimon/function/Function.java
@@ -22,17 +22,18 @@ import org.apache.paimon.types.DataField;
 
 import java.util.List;
 import java.util.Map;
+import java.util.Optional;
 
 /** Interface for function. */
 public interface Function {
 
-    String uuid();
-
     String name();
 
-    List<DataField> inputParams();
+    String fullName();
+
+    Optional<List<DataField>> inputParams();
 
-    List<DataField> returnParams();
+    Optional<List<DataField>> returnParams();
 
     boolean isDeterministic();
 
diff --git 
a/paimon-api/src/main/java/org/apache/paimon/function/FunctionImpl.java 
b/paimon-api/src/main/java/org/apache/paimon/function/FunctionImpl.java
index 85ad987de9..b0290ea50e 100644
--- a/paimon-api/src/main/java/org/apache/paimon/function/FunctionImpl.java
+++ b/paimon-api/src/main/java/org/apache/paimon/function/FunctionImpl.java
@@ -18,17 +18,17 @@
 
 package org.apache.paimon.function;
 
+import org.apache.paimon.catalog.Identifier;
 import org.apache.paimon.types.DataField;
 
 import java.util.List;
 import java.util.Map;
+import java.util.Optional;
 
 /** Function implementation. */
 public class FunctionImpl implements Function {
 
-    private final String uuid;
-
-    private final String name;
+    private final Identifier identifier;
 
     private final List<DataField> inputParams;
 
@@ -43,16 +43,14 @@ public class FunctionImpl implements Function {
     private final Map<String, String> options;
 
     public FunctionImpl(
-            String uuid,
-            String functionName,
+            Identifier identifier,
             List<DataField> inputParams,
             List<DataField> returnParams,
             boolean deterministic,
             Map<String, FunctionDefinition> definitions,
             String comment,
             Map<String, String> options) {
-        this.uuid = uuid;
-        this.name = functionName;
+        this.identifier = identifier;
         this.inputParams = inputParams;
         this.returnParams = returnParams;
         this.deterministic = deterministic;
@@ -62,23 +60,23 @@ public class FunctionImpl implements Function {
     }
 
     @Override
-    public String uuid() {
-        return this.uuid;
+    public String name() {
+        return identifier.getObjectName();
     }
 
     @Override
-    public String name() {
-        return this.name;
+    public String fullName() {
+        return identifier.getFullName();
     }
 
     @Override
-    public List<DataField> inputParams() {
-        return inputParams;
+    public Optional<List<DataField>> inputParams() {
+        return Optional.ofNullable(inputParams);
     }
 
     @Override
-    public List<DataField> returnParams() {
-        return returnParams;
+    public Optional<List<DataField>> returnParams() {
+        return Optional.ofNullable(returnParams);
     }
 
     @Override
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 0dece3bc79..b08af5ab17 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
@@ -753,38 +753,79 @@ public class RESTApi {
         return response.branches();
     }
 
-    /** TODO. */
-    public List<String> listFunctions() {
+    /**
+     * List functions for database.
+     *
+     * @param databaseName
+     * @return a list of function name
+     */
+    public List<String> listFunctions(String databaseName) {
         return listDataFromPageApi(
                 queryParams ->
                         client.get(
-                                resourcePaths.functions(),
+                                resourcePaths.functions(databaseName),
                                 queryParams,
                                 ListFunctionsResponse.class,
                                 restAuthFunction));
     }
 
-    /** TODO. */
-    public GetFunctionResponse getFunction(String functionName) {
+    /**
+     * Get a function by identifier.
+     *
+     * @param identifier the identifier of the function to retrieve
+     * @return the function response object
+     * @throws NoSuchResourceException if the function does not exist
+     * @throws ForbiddenException if the user lacks permission to access the 
function
+     */
+    public GetFunctionResponse getFunction(Identifier identifier) {
         return client.get(
-                resourcePaths.function(functionName), 
GetFunctionResponse.class, restAuthFunction);
+                resourcePaths.function(identifier.getDatabaseName(), 
identifier.getObjectName()),
+                GetFunctionResponse.class,
+                restAuthFunction);
     }
 
-    /** TODO. */
-    public void createFunction(org.apache.paimon.function.Function function) {
+    /**
+     * Create a function.
+     *
+     * @param identifier database name and function name.
+     * @param function the function to be created
+     * @throws AlreadyExistsException Exception thrown on HTTP 409 means a 
function already exists
+     * @throws ForbiddenException Exception thrown on HTTP 403 means don't 
have the permission for
+     *     creating function
+     */
+    public void createFunction(
+            Identifier identifier, org.apache.paimon.function.Function 
function) {
         client.post(
-                resourcePaths.functions(), new 
CreateFunctionRequest(function), restAuthFunction);
+                resourcePaths.functions(identifier.getDatabaseName()),
+                new CreateFunctionRequest(function),
+                restAuthFunction);
     }
 
-    /** TODO. */
-    public void dropFunction(String functionName) {
-        client.delete(resourcePaths.function(functionName), restAuthFunction);
+    /**
+     * Drop a function.
+     *
+     * @param identifier database name and function name.
+     * @throws NoSuchResourceException Exception thrown on HTTP 404 means the 
function not exists
+     * @throws ForbiddenException Exception thrown on HTTP 403 means don't 
have the permission for
+     *     this function
+     */
+    public void dropFunction(Identifier identifier) {
+        client.delete(
+                resourcePaths.function(identifier.getDatabaseName(), 
identifier.getObjectName()),
+                restAuthFunction);
     }
 
-    /** TODO. */
-    public void alterFunction(String functionName, List<FunctionChange> 
changes) {
+    /**
+     * Alter a function.
+     *
+     * @param identifier database name and function name.
+     * @param changes list of function changes to apply
+     * @throws NoSuchResourceException if the function does not exist
+     * @throws ForbiddenException if the user lacks permission to modify the 
function
+     */
+    public void alterFunction(Identifier identifier, List<FunctionChange> 
changes) {
         client.post(
-                resourcePaths.function(functionName),
+                resourcePaths.function(identifier.getDatabaseName(), 
identifier.getObjectName()),
                 new AlterFunctionRequest(changes),
                 restAuthFunction);
     }
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 a378b8dd76..70048e2a16 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
@@ -223,11 +223,17 @@ public class ResourcePaths {
         return SLASH.join(V1, prefix, VIEWS, "rename");
     }
 
-    public String functions() {
-        return SLASH.join(V1, prefix, FUNCTIONS);
+    public String functions(String databaseName) {
+        return SLASH.join(V1, prefix, DATABASES, encodeString(databaseName), 
FUNCTIONS);
     }
 
-    public String function(String functionName) {
-        return SLASH.join(V1, prefix, FUNCTIONS, encodeString(functionName));
+    public String function(String databaseName, String functionName) {
+        return SLASH.join(
+                V1,
+                prefix,
+                DATABASES,
+                encodeString(databaseName),
+                FUNCTIONS,
+                encodeString(functionName));
     }
 }
diff --git 
a/paimon-api/src/main/java/org/apache/paimon/rest/requests/CreateFunctionRequest.java
 
b/paimon-api/src/main/java/org/apache/paimon/rest/requests/CreateFunctionRequest.java
index 31c816f835..6700995691 100644
--- 
a/paimon-api/src/main/java/org/apache/paimon/rest/requests/CreateFunctionRequest.java
+++ 
b/paimon-api/src/main/java/org/apache/paimon/rest/requests/CreateFunctionRequest.java
@@ -84,8 +84,8 @@ public class CreateFunctionRequest implements RESTRequest {
 
     public CreateFunctionRequest(Function function) {
         this.functionName = function.name();
-        this.inputParams = function.inputParams();
-        this.returnParams = function.returnParams();
+        this.inputParams = function.inputParams().orElse(null);
+        this.returnParams = function.returnParams().orElse(null);
         this.deterministic = function.isDeterministic();
         this.definitions = function.definitions();
         this.comment = function.comment();
diff --git 
a/paimon-api/src/main/java/org/apache/paimon/rest/responses/GetFunctionResponse.java
 
b/paimon-api/src/main/java/org/apache/paimon/rest/responses/GetFunctionResponse.java
index 27fce00ef8..22c95cc648 100644
--- 
a/paimon-api/src/main/java/org/apache/paimon/rest/responses/GetFunctionResponse.java
+++ 
b/paimon-api/src/main/java/org/apache/paimon/rest/responses/GetFunctionResponse.java
@@ -18,6 +18,7 @@
 
 package org.apache.paimon.rest.responses;
 
+import org.apache.paimon.catalog.Identifier;
 import org.apache.paimon.function.Function;
 import org.apache.paimon.function.FunctionDefinition;
 import org.apache.paimon.function.FunctionImpl;
@@ -136,10 +137,9 @@ public class GetFunctionResponse extends AuditRESTResponse 
{
         return options;
     }
 
-    public Function toFunction() {
+    public Function toFunction(Identifier identifier) {
         return new FunctionImpl(
-                uuid,
-                functionName,
+                identifier,
                 inputParams,
                 returnParams,
                 deterministic,
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 ca1d4f9b52..956efc0440 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
@@ -525,30 +525,30 @@ public abstract class AbstractCatalog implements Catalog {
             throws TableNotExistException {}
 
     @Override
-    public List<String> listFunctions() {
+    public List<String> listFunctions(String databaseName) {
         return Collections.emptyList();
     }
 
     @Override
-    public Function getFunction(String functionName) throws 
FunctionNotExistException {
-        throw new FunctionNotExistException(functionName);
+    public Function getFunction(Identifier identifier) throws 
FunctionNotExistException {
+        throw new FunctionNotExistException(identifier);
     }
 
     @Override
-    public void createFunction(String functionName, Function function, boolean 
ignoreIfExists)
-            throws FunctionAlreadyExistException {
+    public void createFunction(Identifier identifier, Function function, 
boolean ignoreIfExists)
+            throws FunctionAlreadyExistException, DatabaseNotExistException {
         throw new UnsupportedOperationException();
     }
 
     @Override
-    public void dropFunction(String functionName, boolean ignoreIfNotExists)
+    public void dropFunction(Identifier identifier, boolean ignoreIfNotExists)
             throws FunctionNotExistException {
         throw new UnsupportedOperationException();
     }
 
     @Override
     public void alterFunction(
-            String functionName, List<FunctionChange> changes, boolean 
ignoreIfNotExists)
+            Identifier identifier, List<FunctionChange> changes, boolean 
ignoreIfNotExists)
             throws FunctionNotExistException, DefinitionAlreadyExistException,
                     DefinitionNotExistException {
         throw new UnsupportedOperationException();
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 b10bce009d..446a93b783 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
@@ -731,47 +731,59 @@ public interface Catalog extends AutoCloseable {
     void alterPartitions(Identifier identifier, List<PartitionStatistics> 
partitions)
             throws TableNotExistException;
 
-    /** List all functions in catalog. */
-    List<String> listFunctions();
+    /**
+     * Get the names of all functions in this catalog.
+     *
+     * @return a list of the names of all functions
+     * @throws DatabaseNotExistException if the database does not exist
+     */
+    List<String> listFunctions(String databaseName) throws 
DatabaseNotExistException;
 
     /**
      * Get function by name.
      *
-     * @param functionName
-     * @throws FunctionNotExistException
+     * @param identifier Path of the function to get
+     * @return The requested function
+     * @throws FunctionNotExistException if the function does not exist
      */
-    Function getFunction(String functionName) throws FunctionNotExistException;
+    Function getFunction(Identifier identifier) throws 
FunctionNotExistException;
 
     /**
-     * Create function.
+     * Create a new function.
+     *
+     * <p>NOTE: System functions can not be created.
      *
-     * @param functionName
-     * @param function
-     * @param ignoreIfExists
-     * @throws FunctionAlreadyExistException
+     * @param identifier path of the function to be created
+     * @param function the function definition
+     * @param ignoreIfExists flag to specify behavior when a function already 
exists at the given
+     *     path: if set to false, it throws a FunctionAlreadyExistException, 
if set to true, do
+     *     nothing.
+     * @throws FunctionAlreadyExistException if function already exists and 
ignoreIfExists is false
+     * @throws DatabaseNotExistException if the database in identifier doesn't 
exist
      */
-    void createFunction(String functionName, Function function, boolean 
ignoreIfExists)
-            throws FunctionAlreadyExistException;
+    void createFunction(Identifier identifier, Function function, boolean 
ignoreIfExists)
+            throws FunctionAlreadyExistException, DatabaseNotExistException;
 
     /**
      * Drop function.
      *
-     * @param functionName
+     * @param identifier
      * @param ignoreIfNotExists
      * @throws FunctionNotExistException
      */
-    void dropFunction(String functionName, boolean ignoreIfNotExists)
+    void dropFunction(Identifier identifier, boolean ignoreIfNotExists)
             throws FunctionNotExistException;
 
     /**
      * Alter function.
      *
-     * @param functionName
+     * @param identifier
      * @param changes
      * @param ignoreIfNotExists
      * @throws FunctionNotExistException
      */
-    void alterFunction(String functionName, List<FunctionChange> changes, 
boolean ignoreIfNotExists)
+    void alterFunction(
+            Identifier identifier, List<FunctionChange> changes, boolean 
ignoreIfNotExists)
             throws FunctionNotExistException, DefinitionAlreadyExistException,
                     DefinitionNotExistException;
 
@@ -1211,19 +1223,19 @@ public interface Catalog extends AutoCloseable {
 
         private static final String MSG = "Function %s already exists.";
 
-        private final String functionName;
+        private final Identifier identifier;
 
-        public FunctionAlreadyExistException(String functionName) {
-            this(functionName, null);
+        public FunctionAlreadyExistException(Identifier identifier) {
+            this(identifier, null);
         }
 
-        public FunctionAlreadyExistException(String functionName, Throwable 
cause) {
-            super(String.format(MSG, functionName), cause);
-            this.functionName = functionName;
+        public FunctionAlreadyExistException(Identifier identifier, Throwable 
cause) {
+            super(String.format(MSG, identifier.getFullName()), cause);
+            this.identifier = identifier;
         }
 
-        public String functionName() {
-            return functionName;
+        public Identifier identifier() {
+            return identifier;
         }
     }
 
@@ -1232,19 +1244,19 @@ public interface Catalog extends AutoCloseable {
 
         private static final String MSG = "Function %s doesn't exist.";
 
-        private final String functionName;
+        private final Identifier identifier;
 
-        public FunctionNotExistException(String functionName) {
-            this(functionName, null);
+        public FunctionNotExistException(Identifier identifier) {
+            this(identifier, null);
         }
 
-        public FunctionNotExistException(String functionName, Throwable cause) 
{
-            super(String.format(MSG, functionName), cause);
-            this.functionName = functionName;
+        public FunctionNotExistException(Identifier identifier, Throwable 
cause) {
+            super(String.format(MSG, identifier), cause);
+            this.identifier = identifier;
         }
 
-        public String functionName() {
-            return functionName;
+        public Identifier identifier() {
+            return identifier;
         }
     }
 
@@ -1253,21 +1265,22 @@ public interface Catalog extends AutoCloseable {
 
         private static final String MSG = "Definition %s in function %s 
already exists.";
 
-        private final String functionName;
+        private final Identifier identifier;
         private final String name;
 
-        public DefinitionAlreadyExistException(String functionName, String 
name) {
-            this(functionName, name, null);
+        public DefinitionAlreadyExistException(Identifier identifier, String 
name) {
+            this(identifier, name, null);
         }
 
-        public DefinitionAlreadyExistException(String functionName, String 
name, Throwable cause) {
-            super(String.format(MSG, name, functionName), cause);
-            this.functionName = functionName;
+        public DefinitionAlreadyExistException(
+                Identifier identifier, String name, Throwable cause) {
+            super(String.format(MSG, name, identifier.getFullName()), cause);
+            this.identifier = identifier;
             this.name = name;
         }
 
-        public String functionName() {
-            return functionName;
+        public Identifier identifier() {
+            return identifier;
         }
 
         public String name() {
@@ -1280,21 +1293,21 @@ public interface Catalog extends AutoCloseable {
 
         private static final String MSG = "Definition %s in function %s 
doesn't exist.";
 
-        private final String functionName;
+        private final Identifier identifier;
         private final String name;
 
-        public DefinitionNotExistException(String functionName, String name) {
-            this(functionName, name, null);
+        public DefinitionNotExistException(Identifier identifier, String name) 
{
+            this(identifier, name, null);
         }
 
-        public DefinitionNotExistException(String functionName, String name, 
Throwable cause) {
-            super(String.format(MSG, name, functionName), cause);
-            this.functionName = functionName;
+        public DefinitionNotExistException(Identifier identifier, String name, 
Throwable cause) {
+            super(String.format(MSG, name, identifier.getFullName()), cause);
+            this.identifier = identifier;
             this.name = name;
         }
 
-        public String functionName() {
-            return functionName;
+        public Identifier identifier() {
+            return identifier;
         }
 
         public String name() {
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 9438f8ef22..42dfd400b2 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
@@ -223,33 +223,33 @@ public abstract class DelegateCatalog implements Catalog {
     }
 
     @Override
-    public List<String> listFunctions() {
-        return wrapped.listFunctions();
+    public List<String> listFunctions(String databaseName) throws 
DatabaseNotExistException {
+        return wrapped.listFunctions(databaseName);
     }
 
     @Override
-    public Function getFunction(String functionName) throws 
FunctionNotExistException {
-        return wrapped.getFunction(functionName);
+    public Function getFunction(Identifier identifier) throws 
FunctionNotExistException {
+        return wrapped.getFunction(identifier);
     }
 
     @Override
-    public void createFunction(String functionName, Function function, boolean 
ignoreIfExists)
-            throws FunctionAlreadyExistException {
-        wrapped.createFunction(functionName, function, ignoreIfExists);
+    public void createFunction(Identifier identifier, Function function, 
boolean ignoreIfExists)
+            throws FunctionAlreadyExistException, DatabaseNotExistException {
+        wrapped.createFunction(identifier, function, ignoreIfExists);
     }
 
     @Override
-    public void dropFunction(String functionName, boolean ignoreIfNotExists)
+    public void dropFunction(Identifier identifier, boolean ignoreIfNotExists)
             throws FunctionNotExistException {
-        wrapped.dropFunction(functionName, ignoreIfNotExists);
+        wrapped.dropFunction(identifier, ignoreIfNotExists);
     }
 
     @Override
     public void alterFunction(
-            String functionName, List<FunctionChange> changes, boolean 
ignoreIfNotExists)
+            Identifier identifier, List<FunctionChange> changes, boolean 
ignoreIfNotExists)
             throws FunctionNotExistException, DefinitionAlreadyExistException,
                     DefinitionNotExistException {
-        wrapped.alterFunction(functionName, changes, ignoreIfNotExists);
+        wrapped.alterFunction(identifier, changes, ignoreIfNotExists);
     }
 
     @Override
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 d1b06c8569..2fabf3deee 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
@@ -626,65 +626,71 @@ public class RESTCatalog implements Catalog {
     }
 
     @Override
-    public List<String> listFunctions() {
-        return api.listFunctions();
+    public List<String> listFunctions(String databaseName) throws 
DatabaseNotExistException {
+        try {
+            return api.listFunctions(databaseName);
+        } catch (NoSuchResourceException e) {
+            throw new DatabaseNotExistException(databaseName, e);
+        }
     }
 
     @Override
-    public org.apache.paimon.function.Function getFunction(String functionName)
+    public org.apache.paimon.function.Function getFunction(Identifier 
identifier)
             throws FunctionNotExistException {
         try {
-            GetFunctionResponse response = api.getFunction(functionName);
-            return response.toFunction();
+            GetFunctionResponse response = api.getFunction(identifier);
+            return response.toFunction(identifier);
         } catch (NoSuchResourceException e) {
-            throw new FunctionNotExistException(functionName, e);
+            throw new FunctionNotExistException(identifier, e);
         }
     }
 
     @Override
     public void createFunction(
-            String functionName,
+            Identifier identifier,
             org.apache.paimon.function.Function function,
             boolean ignoreIfExists)
-            throws FunctionAlreadyExistException {
+            throws FunctionAlreadyExistException, DatabaseNotExistException {
         try {
-            api.createFunction(function);
+            api.createFunction(identifier, function);
+        } catch (NoSuchResourceException e) {
+            throw new DatabaseNotExistException(identifier.getDatabaseName(), 
e);
         } catch (AlreadyExistsException e) {
             if (ignoreIfExists) {
                 return;
             }
-            throw new FunctionAlreadyExistException(functionName, e);
+            throw new FunctionAlreadyExistException(identifier, e);
         }
     }
 
     @Override
-    public void dropFunction(String functionName, boolean ignoreIfNotExists)
+    public void dropFunction(Identifier identifier, boolean ignoreIfNotExists)
             throws FunctionNotExistException {
         try {
-            api.dropFunction(functionName);
+            api.dropFunction(identifier);
         } catch (NoSuchResourceException e) {
             if (ignoreIfNotExists) {
                 return;
             }
-            throw new FunctionNotExistException(functionName, e);
+            throw new FunctionNotExistException(identifier, e);
         }
     }
 
     @Override
     public void alterFunction(
-            String functionName, List<FunctionChange> changes, boolean 
ignoreIfNotExists)
+            Identifier identifier, List<FunctionChange> changes, boolean 
ignoreIfNotExists)
             throws FunctionNotExistException, DefinitionAlreadyExistException,
                     DefinitionNotExistException {
         try {
-            api.alterFunction(functionName, changes);
+            api.alterFunction(identifier, changes);
         } catch (AlreadyExistsException e) {
-            throw new DefinitionAlreadyExistException(functionName, 
e.resourceName());
+            throw new DefinitionAlreadyExistException(identifier, 
e.resourceName());
         } catch (NoSuchResourceException e) {
             if (StringUtils.equals(e.resourceType(), 
ErrorResponse.RESOURCE_TYPE_DEFINITION)) {
-                throw new DefinitionNotExistException(functionName, 
e.resourceName());
+                throw new DefinitionNotExistException(identifier, 
e.resourceName());
             }
             if (!ignoreIfNotExists) {
-                throw new FunctionNotExistException(functionName);
+                throw new FunctionNotExistException(identifier, e);
             }
         } catch (BadRequestException e) {
             throw new IllegalArgumentException(e.getMessage());
diff --git 
a/paimon-core/src/test/java/org/apache/paimon/rest/MockRESTMessage.java 
b/paimon-core/src/test/java/org/apache/paimon/rest/MockRESTMessage.java
index 0bf9a17920..acb7a8ff56 100644
--- a/paimon-core/src/test/java/org/apache/paimon/rest/MockRESTMessage.java
+++ b/paimon-core/src/test/java/org/apache/paimon/rest/MockRESTMessage.java
@@ -286,12 +286,12 @@ public class MockRESTMessage {
     }
 
     public static GetFunctionResponse getFunctionResponse() {
-        Function function = function("function");
+        Function function = function(Identifier.create(databaseName(), 
"function"));
         return new GetFunctionResponse(
-                function.uuid(),
+                UUID.randomUUID().toString(),
                 function.name(),
-                function.inputParams(),
-                function.returnParams(),
+                function.inputParams().orElse(null),
+                function.returnParams().orElse(null),
                 function.isDeterministic(),
                 function.definitions(),
                 function.comment(),
@@ -304,18 +304,18 @@ public class MockRESTMessage {
     }
 
     public static CreateFunctionRequest createFunctionRequest() {
-        Function function = function("function");
+        Function function = function(Identifier.create(databaseName(), 
"function"));
         return new CreateFunctionRequest(
                 function.name(),
-                function.inputParams(),
-                function.returnParams(),
+                function.inputParams().orElse(null),
+                function.returnParams().orElse(null),
                 function.isDeterministic(),
                 function.definitions(),
                 function.comment(),
                 function.options());
     }
 
-    public static Function function(String functionName) {
+    public static Function function(Identifier identifier) {
         List<DataField> inputParams =
                 Lists.newArrayList(
                         new DataField(0, "length", DataTypes.DOUBLE()),
@@ -334,8 +334,7 @@ public class MockRESTMessage {
         definitions.put("spark", sparkFunction);
         definitions.put("trino", trinoFunction);
         return new FunctionImpl(
-                UUID.randomUUID().toString(),
-                functionName,
+                identifier,
                 inputParams,
                 returnParams,
                 false,
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 ce31cdecb1..69a512f624 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
@@ -137,7 +137,6 @@ public class RESTCatalogServer {
     public static final String AUTHORIZATION_HEADER_KEY = "Authorization";
 
     private final String databaseUri;
-    private final String functionUri;
 
     private final FileSystemCatalog catalog;
     private final MockWebServer server;
@@ -165,7 +164,6 @@ public class RESTCatalogServer {
                 
this.configResponse.getDefaults().get(RESTCatalogInternalOptions.PREFIX.key());
         this.resourcePaths = new ResourcePaths(prefix);
         this.databaseUri = resourcePaths.databases();
-        this.functionUri = resourcePaths.functions();
         Options conf = new Options();
         this.configResponse.getDefaults().forEach(conf::setString);
         conf.setString(WAREHOUSE.key(), dataPath);
@@ -276,11 +274,6 @@ public class RESTCatalogServer {
                     } else if (databaseUri.equals(request.getPath())
                             || request.getPath().contains(databaseUri + "?")) {
                         return databasesApiHandler(restAuthParameter.method(), 
data, parameters);
-                    } else if (functionUri.equals(request.getPath())) {
-                        return functionsApiHandler(restAuthParameter.method(), 
data, parameters);
-                    } else if (request.getPath().startsWith(functionUri)) {
-                        return functionApiHandler(
-                                request.getPath(), restAuthParameter.method(), 
data, parameters);
                     } else if 
(resourcePaths.renameTable().equals(request.getPath())) {
                         return renameTableHandle(restAuthParameter.data());
                     } else if 
(resourcePaths.renameView().equals(request.getPath())) {
@@ -301,6 +294,10 @@ public class RESTCatalogServer {
                         if (!databaseStore.containsKey(databaseName)) {
                             throw new 
Catalog.DatabaseNotExistException(databaseName);
                         }
+                        boolean isFunctions =
+                                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");
                         boolean isViewsDetails =
                                 resources.length == 2 && 
resources[1].startsWith("view-details");
@@ -435,6 +432,12 @@ public class RESTCatalogServer {
                                     parameters);
                         } else if (isTableDetails) {
                             return tableDetailsHandle(parameters, 
databaseName);
+                        } else if (isFunctions) {
+                            return functionsApiHandler(
+                                    databaseName, restAuthParameter.method(), 
data, parameters);
+                        } else if (isFunction) {
+                            return functionApiHandler(
+                                    identifier, restAuthParameter.method(), 
data, parameters);
                         } else if (isViews) {
                             return viewsHandle(
                                     restAuthParameter.method(),
@@ -524,8 +527,8 @@ public class RESTCatalogServer {
                 } catch (Catalog.FunctionAlreadyExistException e) {
                     response =
                             new ErrorResponse(
-                                    ErrorResponse.RESOURCE_TYPE_COLUMN,
-                                    e.functionName(),
+                                    ErrorResponse.RESOURCE_TYPE_DEFINITION,
+                                    e.identifier().getObjectName(),
                                     e.getMessage(),
                                     409);
                     return mockResponse(response, 409);
@@ -533,7 +536,7 @@ public class RESTCatalogServer {
                     response =
                             new ErrorResponse(
                                     ErrorResponse.RESOURCE_TYPE_DEFINITION,
-                                    e.functionName(),
+                                    e.name(),
                                     e.getMessage(),
                                     409);
                     return mockResponse(response, 409);
@@ -557,7 +560,7 @@ public class RESTCatalogServer {
                     response =
                             new ErrorResponse(
                                     ErrorResponse.RESOURCE_TYPE_FUNCTION,
-                                    e.functionName(),
+                                    e.identifier().getObjectName(),
                                     e.getMessage(),
                                     404);
                     return mockResponse(response, 404);
@@ -565,7 +568,7 @@ public class RESTCatalogServer {
                     response =
                             new ErrorResponse(
                                     ErrorResponse.RESOURCE_TYPE_DEFINITION,
-                                    e.functionName(),
+                                    e.name(),
                                     e.getMessage(),
                                     404);
                     return mockResponse(response, 404);
@@ -763,32 +766,9 @@ public class RESTCatalogServer {
         }
     }
 
-    private MockResponse functionDetailsHandler(String functionName) throws 
Exception {
-        if (functionStore.containsKey(functionName)) {
-            Function function = functionStore.get(functionName);
-            GetFunctionResponse response =
-                    new GetFunctionResponse(
-                            function.uuid(),
-                            function.name(),
-                            function.inputParams(),
-                            function.returnParams(),
-                            function.isDeterministic(),
-                            function.definitions(),
-                            function.comment(),
-                            function.options(),
-                            "owner",
-                            1L,
-                            "owner",
-                            1L,
-                            "owner");
-            return mockResponse(response, 200);
-        } else {
-            throw new Catalog.FunctionNotExistException(functionName);
-        }
-    }
-
     private MockResponse functionsApiHandler(
-            String method, String data, Map<String, String> parameters) throws 
Exception {
+            String databaseName, String method, String data, Map<String, 
String> parameters)
+            throws Exception {
         switch (method) {
             case "GET":
                 List<String> functions = new 
ArrayList<>(functionStore.keySet());
@@ -800,8 +780,7 @@ public class RESTCatalogServer {
                 if (!functionStore.containsKey(functionName)) {
                     Function function =
                             new FunctionImpl(
-                                    UUID.randomUUID().toString(),
-                                    functionName,
+                                    Identifier.create(databaseName, 
functionName),
                                     requestBody.inputParams(),
                                     requestBody.returnParams(),
                                     requestBody.isDeterministic(),
@@ -811,7 +790,8 @@ public class RESTCatalogServer {
                     functionStore.put(functionName, function);
                     return new MockResponse().setResponseCode(200);
                 } else {
-                    throw new 
Catalog.FunctionAlreadyExistException(functionName);
+                    throw new Catalog.FunctionAlreadyExistException(
+                            Identifier.create(databaseName, functionName));
                 }
             default:
                 return new MockResponse().setResponseCode(404);
@@ -819,12 +799,11 @@ public class RESTCatalogServer {
     }
 
     private MockResponse functionApiHandler(
-            String path, String method, String data, Map<String, String> 
parameters)
+            Identifier identifier, String method, String data, Map<String, 
String> parameters)
             throws Exception {
-        String[] resources = path.substring((functionUri + 
"/").length()).split("/");
-        String functionName = RESTUtil.decodeString(resources[0]);
+        String functionName = identifier.getObjectName();
         if (!functionStore.containsKey(functionName)) {
-            throw new Catalog.FunctionNotExistException(functionName);
+            throw new Catalog.FunctionNotExistException(identifier);
         }
         Function function = functionStore.get(functionName);
         switch (method) {
@@ -834,10 +813,10 @@ public class RESTCatalogServer {
             case "GET":
                 GetFunctionResponse response =
                         new GetFunctionResponse(
-                                function.uuid(),
+                                UUID.randomUUID().toString(),
                                 function.name(),
-                                function.inputParams(),
-                                function.returnParams(),
+                                function.inputParams().orElse(null),
+                                function.returnParams().orElse(null),
                                 function.isDeterministic(),
                                 function.definitions(),
                                 function.comment(),
@@ -873,7 +852,7 @@ public class RESTCatalogServer {
                                 (FunctionChange.AddDefinition) functionChange;
                         if (function.definition(addDefinition.name()) != null) 
{
                             throw new Catalog.DefinitionAlreadyExistException(
-                                    functionName, addDefinition.name());
+                                    identifier, addDefinition.name());
                         }
                         newDefinitions.put(addDefinition.name(), 
addDefinition.definition());
                     } else if (functionChange instanceof 
FunctionChange.UpdateDefinition) {
@@ -884,7 +863,7 @@ public class RESTCatalogServer {
                                     updateDefinition.name(), 
updateDefinition.definition());
                         } else {
                             throw new Catalog.DefinitionNotExistException(
-                                    functionName, updateDefinition.name());
+                                    identifier, updateDefinition.name());
                         }
                     } else if (functionChange instanceof 
FunctionChange.DropDefinition) {
                         FunctionChange.DropDefinition dropDefinition =
@@ -893,16 +872,15 @@ public class RESTCatalogServer {
                             newDefinitions.remove(dropDefinition.name());
                         } else {
                             throw new Catalog.DefinitionNotExistException(
-                                    functionName, dropDefinition.name());
+                                    identifier, dropDefinition.name());
                         }
                     }
                 }
                 function =
                         new FunctionImpl(
-                                functionName,
-                                function.uuid(),
-                                function.inputParams(),
-                                function.returnParams(),
+                                Identifier.create(null, functionName),
+                                function.inputParams().orElse(null),
+                                function.returnParams().orElse(null),
                                 function.isDeterministic(),
                                 newDefinitions,
                                 newComment,
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 ad5d5ed1ea..8657e220e9 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
@@ -1549,86 +1549,90 @@ public abstract class RESTCatalogTest extends 
CatalogTestBase {
 
     @Test
     void testFunction() throws Exception {
-        Function function = MockRESTMessage.function("function");
+        Identifier identifier = new Identifier("rest_catalog_db", "function");
+        catalog.createDatabase(identifier.getDatabaseName(), false);
+        Function function = MockRESTMessage.function(identifier);
 
-        catalog.createFunction(function.name(), function, true);
+        catalog.createFunction(identifier, function, true);
         assertThrows(
                 Catalog.FunctionAlreadyExistException.class,
-                () -> catalog.createFunction(function.name(), function, 
false));
+                () -> catalog.createFunction(identifier, function, false));
 
-        assertThat(catalog.listFunctions().contains(function.name())).isTrue();
+        
assertThat(catalog.listFunctions(identifier.getDatabaseName()).contains(function.name()))
+                .isTrue();
 
-        Function getFunction = catalog.getFunction(function.name());
+        Function getFunction = catalog.getFunction(identifier);
         assertThat(getFunction.name()).isEqualTo(function.name());
         for (String dialect : function.definitions().keySet()) {
             
assertThat(getFunction.definition(dialect)).isEqualTo(function.definition(dialect));
         }
-        catalog.dropFunction(function.name(), true);
+        catalog.dropFunction(identifier, true);
 
-        
assertThat(catalog.listFunctions().contains(function.name())).isFalse();
+        
assertThat(catalog.listFunctions(identifier.getDatabaseName()).contains(function.name()))
+                .isFalse();
         assertThrows(
                 Catalog.FunctionNotExistException.class,
-                () -> catalog.dropFunction(function.name(), false));
+                () -> catalog.dropFunction(identifier, false));
         assertThrows(
-                Catalog.FunctionNotExistException.class,
-                () -> catalog.getFunction(function.name()));
+                Catalog.FunctionNotExistException.class, () -> 
catalog.getFunction(identifier));
     }
 
     @Test
     void testAlterFunction() throws Exception {
-        String functionName = "alter_function_name";
-        Function function = MockRESTMessage.function(functionName);
+        Identifier identifier = new Identifier("rest_catalog_db", 
"alter_function_name");
+        catalog.createDatabase(identifier.getDatabaseName(), false);
+        Function function = MockRESTMessage.function(identifier);
         FunctionDefinition definition = FunctionDefinition.sql("x * y + 1");
         FunctionChange.AddDefinition addDefinition =
                 (FunctionChange.AddDefinition) 
FunctionChange.addDefinition("flink_1", definition);
         assertDoesNotThrow(
-                () -> catalog.alterFunction(functionName, 
ImmutableList.of(addDefinition), true));
+                () -> catalog.alterFunction(identifier, 
ImmutableList.of(addDefinition), true));
         assertThrows(
                 Catalog.FunctionNotExistException.class,
-                () -> catalog.alterFunction(functionName, 
ImmutableList.of(addDefinition), false));
-        catalog.createFunction(function.name(), function, true);
+                () -> catalog.alterFunction(identifier, 
ImmutableList.of(addDefinition), false));
+        catalog.createFunction(identifier, function, true);
         // set options
         String key = UUID.randomUUID().toString();
         String value = UUID.randomUUID().toString();
         FunctionChange setOption = FunctionChange.setOption(key, value);
-        catalog.alterFunction(functionName, ImmutableList.of(setOption), 
false);
-        Function catalogFunction = catalog.getFunction(functionName);
+        catalog.alterFunction(identifier, ImmutableList.of(setOption), false);
+        Function catalogFunction = catalog.getFunction(identifier);
         assertThat(catalogFunction.options().get(key)).isEqualTo(value);
 
         // remove options
         catalog.alterFunction(
-                functionName, 
ImmutableList.of(FunctionChange.removeOption(key)), false);
-        catalogFunction = catalog.getFunction(functionName);
+                identifier, 
ImmutableList.of(FunctionChange.removeOption(key)), false);
+        catalogFunction = catalog.getFunction(identifier);
         
assertThat(catalogFunction.options().containsKey(key)).isEqualTo(false);
 
         // update comment
         String newComment = "new comment";
         catalog.alterFunction(
-                functionName, 
ImmutableList.of(FunctionChange.updateComment(newComment)), false);
-        catalogFunction = catalog.getFunction(functionName);
+                identifier, 
ImmutableList.of(FunctionChange.updateComment(newComment)), false);
+        catalogFunction = catalog.getFunction(identifier);
         assertThat(catalogFunction.comment()).isEqualTo(newComment);
         // add definition
-        catalog.alterFunction(functionName, ImmutableList.of(addDefinition), 
false);
-        catalogFunction = catalog.getFunction(functionName);
+        catalog.alterFunction(identifier, ImmutableList.of(addDefinition), 
false);
+        catalogFunction = catalog.getFunction(identifier);
         assertThat(catalogFunction.definition(addDefinition.name()))
                 .isEqualTo(addDefinition.definition());
         assertThrows(
                 Catalog.DefinitionAlreadyExistException.class,
-                () -> catalog.alterFunction(functionName, 
ImmutableList.of(addDefinition), false));
+                () -> catalog.alterFunction(identifier, 
ImmutableList.of(addDefinition), false));
 
         // update definition
         FunctionChange.UpdateDefinition updateDefinition =
                 (FunctionChange.UpdateDefinition)
                         FunctionChange.updateDefinition("flink_1", definition);
-        catalog.alterFunction(functionName, 
ImmutableList.of(updateDefinition), false);
-        catalogFunction = catalog.getFunction(functionName);
+        catalog.alterFunction(identifier, ImmutableList.of(updateDefinition), 
false);
+        catalogFunction = catalog.getFunction(identifier);
         assertThat(catalogFunction.definition(updateDefinition.name()))
                 .isEqualTo(updateDefinition.definition());
         assertThrows(
                 Catalog.DefinitionNotExistException.class,
                 () ->
                         catalog.alterFunction(
-                                functionName,
+                                identifier,
                                 ImmutableList.of(
                                         
FunctionChange.updateDefinition("no_exist", definition)),
                                 false));
@@ -1637,13 +1641,13 @@ public abstract class RESTCatalogTest extends 
CatalogTestBase {
         FunctionChange.DropDefinition dropDefinition =
                 (FunctionChange.DropDefinition)
                         FunctionChange.dropDefinition(updateDefinition.name());
-        catalog.alterFunction(functionName, ImmutableList.of(dropDefinition), 
false);
-        catalogFunction = catalog.getFunction(functionName);
+        catalog.alterFunction(identifier, ImmutableList.of(dropDefinition), 
false);
+        catalogFunction = catalog.getFunction(identifier);
         
assertThat(catalogFunction.definition(updateDefinition.name())).isNull();
 
         assertThrows(
                 Catalog.DefinitionNotExistException.class,
-                () -> catalog.alterFunction(functionName, 
ImmutableList.of(dropDefinition), false));
+                () -> catalog.alterFunction(identifier, 
ImmutableList.of(dropDefinition), false));
     }
 
     @Test

Reply via email to