You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@flink.apache.org by dw...@apache.org on 2024/10/15 07:39:41 UTC

(flink) branch master updated: [FLINK-35822] Introduce DESCRIBE FUNCTION (#25115)

This is an automated email from the ASF dual-hosted git repository.

dwysakowicz pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/flink.git


The following commit(s) were added to refs/heads/master by this push:
     new 1e40fa5ccc0 [FLINK-35822] Introduce DESCRIBE FUNCTION (#25115)
1e40fa5ccc0 is described below

commit 1e40fa5ccc0ae814281a94098ef0a6e8d9403608
Author: nateab <nb...@confluent.io>
AuthorDate: Tue Oct 15 00:39:34 2024 -0700

    [FLINK-35822] Introduce DESCRIBE FUNCTION (#25115)
---
 docs/content.zh/docs/dev/table/sql/describe.md     | 118 ++++++++++++++-
 docs/content/docs/dev/table/sql/describe.md        | 161 ++++++++++++++++++++-
 .../src/test/resources/sql/function.q              | 158 +++++++++++++++++++-
 .../service/operation/OperationExecutor.java       |   4 +-
 .../src/main/codegen/data/Parser.tdd               |   2 +
 .../src/main/codegen/includes/parserImpls.ftl      |  19 +++
 ...ribeModel.java => SqlRichDescribeFunction.java} |  32 ++--
 .../flink/sql/parser/dql/SqlRichDescribeModel.java |   2 +-
 .../flink/sql/parser/dql/SqlRichDescribeTable.java |   2 +-
 .../flink/sql/parser/FlinkSqlParserImplTest.java   |  11 ++
 .../table/catalog/ContextResolvedFunction.java     |  41 +++++-
 .../flink/table/catalog/FunctionCatalog.java       |   6 +-
 .../operations/DescribeFunctionOperation.java      | 132 +++++++++++++++++
 .../converters/SqlDescribeFunctionConverter.java   |  37 +++++
 .../operations/converters/SqlNodeConverters.java   |   1 +
 .../table/planner/calcite/FlinkPlannerImpl.scala   |   1 +
 .../operations/SqlOtherOperationConverterTest.java |  27 ++++
 17 files changed, 724 insertions(+), 30 deletions(-)

diff --git a/docs/content.zh/docs/dev/table/sql/describe.md b/docs/content.zh/docs/dev/table/sql/describe.md
index 5f7480da1b5..d4219d6eea9 100644
--- a/docs/content.zh/docs/dev/table/sql/describe.md
+++ b/docs/content.zh/docs/dev/table/sql/describe.md
@@ -90,6 +90,12 @@ tableEnv.executeSql("DESCRIBE CATALOG cat2").print();
 
 // 打印完整的元数据
 tableEnv.executeSql("DESC CATALOG EXTENDED cat2").print();
+
+tableEnv.executeSql("CREATE FUNCTION MySum as 'org.example.SumScalarFunction' USING JAR 'file://home/users/mysum-udf.jar';").print();
+
+tableEnv.executeSql("DESCRIBE FUNCTION MySum").print();
+
+tableEnv.executeSql("DESC FUNCTION EXTENDED MySum").print();
 ```
 {{< /tab >}}
 {{< tab "Scala" >}}
@@ -122,6 +128,14 @@ tableEnv.executeSql("DESCRIBE CATALOG cat2").print()
 
 // 打印完整的元数据
 tableEnv.executeSql("DESC CATALOG EXTENDED cat2").print()
+
+tableEnv.executeSql("CREATE FUNCTION MySum as 'org.example.SumScalarFunction' USING JAR 'file://home/users/mysum-udf.jar';").print()
+
+// print the metadata
+tableEnv.executeSql("DESCRIBE FUNCTION MySum").print()
+
+// print the complete metadata
+tableEnv.executeSql("DESC FUNCTION EXTENDED MySum").print()
 ```
 {{< /tab >}}
 {{< tab "Python" >}}
@@ -154,6 +168,12 @@ table_env.execute_sql("DESCRIBE CATALOG cat2").print()
 
 # 打印完整的元数据
 table_env.execute_sql("DESC CATALOG EXTENDED cat2").print()
+
+table_env.execute_sql("CREATE FUNCTION MySum as 'org.example.SumScalarFunction' USING JAR 'file://home/users/mysum-udf.jar';").print()
+
+table_env.execute_sql("DESCRIBE FUNCTION MySum").print()
+
+table_env.execute_sql("DESC FUNCTION EXTENDED MySum").print()
 ```
 {{< /tab >}}
 {{< tab "SQL CLI" >}}
@@ -181,6 +201,12 @@ Flink SQL> CREATE CATALOG cat2 WITH ('type'='generic_in_memory', 'default-databa
 Flink SQL> DESCRIBE CATALOG cat2;
 
 Flink SQL> DESC CATALOG EXTENDED cat2;
+
+Flink SQL> CREATE FUNCTION MySum as 'org.example.SumScalarFunction' USING JAR 'file://home/users/mysum-udf.jar';
+
+Flink SQL> DESCRIBE FUNCTION MySum;
+
+Flink SQL> DESC FUNCTION EXTENDED MySum;
       
 Flink SQL> DESCRIBE JOB '228d70913eab60dda85c5e7f78b5782c';
       
@@ -261,6 +287,34 @@ Flink SQL> DESC JOB '228d70913eab60dda85c5e7f78b5782c';
 | option:default-database |                db |
 +-------------------------+-------------------+
 4 rows in set
+
+# DESCRIBE FUNCTION MySum
++-------------------+---------------------------------------------------------------------+
+|         info name |                                                          info value |
++-------------------+---------------------------------------------------------------------+
+|   system function |                                                               false |
+|         temporary |                                                               false |
+|        class name |                                       org.example.SumScalarFunction |
+| function language |                                                                JAVA |
+|     resource uris | ResourceUri{resourceType=JAR, uri='file:/home/users/mysum-udf.jar'} |
++-------------------+---------------------------------------------------------------------+
+5 rows in set
+# DESCRIBE FUNCTION EXTENDED MySum
++-------------------+---------------------------------------------------------------------+
+|         info name |                                                          info value |
++-------------------+---------------------------------------------------------------------+
+|   system function |                                                               false |
+|         temporary |                                                               false |
+|        class name |                                       org.example.SumScalarFunction |
+| function language |                                                                JAVA |
+|     resource uris | ResourceUri{resourceType=JAR, uri='file:/home/users/mysum-udf.jar'} |
+|              kind |                                                              SCALAR |
+|      requirements |                                                                  [] |
+|     deterministic |                                                                true |
+|  constant folding |                                                                true |
+|         signature |                       MySum(<INTEGER NOT NULL>, <INTEGER NOT NULL>) |
++-------------------+---------------------------------------------------------------------+
+10 rows in set
 ```
 {{< /tab >}}
 {{< tab "Python" >}}
@@ -297,6 +351,34 @@ Flink SQL> DESC JOB '228d70913eab60dda85c5e7f78b5782c';
 | option:default-database |                db |
 +-------------------------+-------------------+
 4 rows in set
+
+# DESCRIBE FUNCTION MySum
++-------------------+---------------------------------------------------------------------+
+|         info name |                                                          info value |
++-------------------+---------------------------------------------------------------------+
+|   system function |                                                               false |
+|         temporary |                                                               false |
+|        class name |                                       org.example.SumScalarFunction |
+| function language |                                                                JAVA |
+|     resource uris | ResourceUri{resourceType=JAR, uri='file:/home/users/mysum-udf.jar'} |
++-------------------+---------------------------------------------------------------------+
+5 rows in set
+# DESCRIBE FUNCTION EXTENDED MySum
++-------------------+---------------------------------------------------------------------+
+|         info name |                                                          info value |
++-------------------+---------------------------------------------------------------------+
+|   system function |                                                               false |
+|         temporary |                                                               false |
+|        class name |                                       org.example.SumScalarFunction |
+| function language |                                                                JAVA |
+|     resource uris | ResourceUri{resourceType=JAR, uri='file:/home/users/mysum-udf.jar'} |
+|              kind |                                                              SCALAR |
+|      requirements |                                                                  [] |
+|     deterministic |                                                                true |
+|  constant folding |                                                                true |
+|         signature |                       MySum(<INTEGER NOT NULL>, <INTEGER NOT NULL>) |
++-------------------+---------------------------------------------------------------------+
+10 rows in set
 ```
 {{< /tab >}}
 {{< tab "SQL CLI" >}}
@@ -334,6 +416,34 @@ Flink SQL> DESC JOB '228d70913eab60dda85c5e7f78b5782c';
 +-------------------------+-------------------+
 4 rows in set
 
+# DESCRIBE FUNCTION MySum
++-------------------+---------------------------------------------------------------------+
+|         info name |                                                          info value |
++-------------------+---------------------------------------------------------------------+
+|   system function |                                                               false |
+|         temporary |                                                               false |
+|        class name |                                       org.example.SumScalarFunction |
+| function language |                                                                JAVA |
+|     resource uris | ResourceUri{resourceType=JAR, uri='file:/home/users/mysum-udf.jar'} |
++-------------------+---------------------------------------------------------------------+
+5 rows in set
+# DESCRIBE FUNCTION EXTENDED MySum
++-------------------+---------------------------------------------------------------------+
+|         info name |                                                          info value |
++-------------------+---------------------------------------------------------------------+
+|   system function |                                                               false |
+|         temporary |                                                               false |
+|        class name |                                       org.example.SumScalarFunction |
+| function language |                                                                JAVA |
+|     resource uris | ResourceUri{resourceType=JAR, uri='file:/home/users/mysum-udf.jar'} |
+|              kind |                                                              SCALAR |
+|      requirements |                                                                  [] |
+|     deterministic |                                                                true |
+|  constant folding |                                                                true |
+|         signature |                       MySum(<INTEGER NOT NULL>, <INTEGER NOT NULL>) |
++-------------------+---------------------------------------------------------------------+
+10 rows in set
+
 # DESCRIBE JOB '228d70913eab60dda85c5e7f78b5782c'
 +----------------------------------+----------+---------+-------------------------+
 |                           job id | job name |  status |              start time |
@@ -363,10 +473,16 @@ Flink SQL> DESC JOB '228d70913eab60dda85c5e7f78b5782c';
 { DESCRIBE | DESC } CATALOG [EXTENDED] catalog_name
 ```
 
+### DESCRIBE FUNCTION
+
+```sql
+{ DESCRIBE | DESC } FUNCTION [EXTENDED] [catalog_name.][db_name.]function_name
+```
+
 ### DESCRIBE JOB
 
 ```sql
 { DESCRIBE | DESC } JOB '<job_id>'
 ```
 
-<span class="label label-danger">Attention</span> DESCRIBE JOB 语句仅适用于 [SQL CLI]({{< ref "docs/dev/table/sqlClient" >}}) 或者 [SQL Gateway]({{< ref "docs/dev/table/sql-gateway/overview" >}}).
\ No newline at end of file
+<span class="label label-danger">Attention</span> DESCRIBE JOB 语句仅适用于 [SQL CLI]({{< ref "docs/dev/table/sqlClient" >}}) 或者 [SQL Gateway]({{< ref "docs/dev/table/sql-gateway/overview" >}}).
diff --git a/docs/content/docs/dev/table/sql/describe.md b/docs/content/docs/dev/table/sql/describe.md
index 4fdde4a29a8..185224bfeb5 100644
--- a/docs/content/docs/dev/table/sql/describe.md
+++ b/docs/content/docs/dev/table/sql/describe.md
@@ -26,7 +26,7 @@ under the License.
 
 # DESCRIBE Statements
 
-DESCRIBE statements are used to describe the schema of a table or a view, or the metadata of a catalog, or the specified job in the Flink cluster.
+DESCRIBE statements are used to describe the schema of a table or a view, or the metadata of a catalog or a function, or the specified job in the Flink cluster.
 
 
 ## Run a DESCRIBE statement
@@ -89,6 +89,15 @@ tableEnv.executeSql("DESCRIBE CATALOG cat2").print();
 
 // print the complete metadata
 tableEnv.executeSql("DESC CATALOG EXTENDED cat2").print();
+
+// register a function named "MySum"
+tableEnv.executeSql("CREATE FUNCTION MySum as 'org.example.SumScalarFunction' USING JAR 'file://home/users/mysum-udf.jar';").print();
+
+// print the metadata
+tableEnv.executeSql("DESCRIBE FUNCTION MySum").print();
+
+// print the complete metadata
+tableEnv.executeSql("DESC FUNCTION EXTENDED MySum").print();
 ```
 {{< /tab >}}
 {{< tab "Scala" >}}
@@ -121,6 +130,16 @@ tableEnv.executeSql("DESCRIBE CATALOG cat2").print()
 
 // print the complete metadata
 tableEnv.executeSql("DESC CATALOG EXTENDED cat2").print()
+
+
+// register a function named "MySum"
+tableEnv.executeSql("CREATE FUNCTION MySum as 'org.example.SumScalarFunction' USING JAR 'file://home/users/mysum-udf.jar';").print()
+
+// print the metadata
+tableEnv.executeSql("DESCRIBE FUNCTION MySum").print()
+
+// print the complete metadata
+tableEnv.executeSql("DESC FUNCTION EXTENDED MySum").print()
 ```
 {{< /tab >}}
 {{< tab "Python" >}}
@@ -153,6 +172,16 @@ table_env.execute_sql("DESCRIBE CATALOG cat2").print()
 
 # print the complete metadata
 table_env.execute_sql("DESC CATALOG EXTENDED cat2").print()
+
+
+// register a function named "MySum"
+table_env.execute_sql("CREATE FUNCTION MySum as 'org.example.SumScalarFunction' USING JAR 'file://home/users/mysum-udf.jar';").print()
+
+// print the metadata
+table_env.execute_sql("DESCRIBE FUNCTION MySum").print()
+
+// print the complete metadata
+table_env.execute_sql("DESC FUNCTION EXTENDED MySum").print()
 ```
 {{< /tab >}}
 {{< tab "SQL CLI" >}}
@@ -180,6 +209,12 @@ Flink SQL> CREATE CATALOG cat2 WITH ('type'='generic_in_memory', 'default-databa
 Flink SQL> DESCRIBE CATALOG cat2;
 
 Flink SQL> DESC CATALOG EXTENDED cat2;
+
+Flink SQL> CREATE FUNCTION MySum as 'org.example.SumScalarFunction' USING JAR 'file://home/users/mysum-udf.jar';
+
+Flink SQL> DESCRIBE FUNCTION MySum;
+
+Flink SQL> DESC FUNCTION EXTENDED MySum;
       
 Flink SQL> DESCRIBE JOB '228d70913eab60dda85c5e7f78b5782c';
       
@@ -224,6 +259,35 @@ The result of the above example is:
 | option:default-database |                db |
 +-------------------------+-------------------+
 4 rows in set
+
+# DESCRIBE FUNCTION MySum
++-------------------+---------------------------------------------------------------------+
+|         info name |                                                          info value |
++-------------------+---------------------------------------------------------------------+
+|   system function |                                                               false |
+|         temporary |                                                               false |
+|        class name |                                       org.example.SumScalarFunction |
+| function language |                                                                JAVA |
+|     resource uris | ResourceUri{resourceType=JAR, uri='file:/home/users/mysum-udf.jar'} |
++-------------------+---------------------------------------------------------------------+
+5 rows in set
+
+# DESCRIBE FUNCTION EXTENDED MySum
++-------------------+---------------------------------------------------------------------+
+|         info name |                                                          info value |
++-------------------+---------------------------------------------------------------------+
+|   system function |                                                               false |
+|         temporary |                                                               false |
+|        class name |                                       org.example.SumScalarFunction |
+| function language |                                                                JAVA |
+|     resource uris | ResourceUri{resourceType=JAR, uri='file:/home/users/mysum-udf.jar'} |
+|              kind |                                                              SCALAR |
+|      requirements |                                                                  [] |
+|     deterministic |                                                                true |
+|  constant folding |                                                                true |
+|         signature |                       MySum(<INTEGER NOT NULL>, <INTEGER NOT NULL>) |
++-------------------+---------------------------------------------------------------------+
+10 rows in set
 ```
 {{< /tab >}}
 {{< tab "Scala" >}}
@@ -260,6 +324,35 @@ The result of the above example is:
 | option:default-database |                db |
 +-------------------------+-------------------+
 4 rows in set
+
+# DESCRIBE FUNCTION MySum
++-------------------+---------------------------------------------------------------------+
+|         info name |                                                          info value |
++-------------------+---------------------------------------------------------------------+
+|   system function |                                                               false |
+|         temporary |                                                               false |
+|        class name |                                       org.example.SumScalarFunction |
+| function language |                                                                JAVA |
+|     resource uris | ResourceUri{resourceType=JAR, uri='file:/home/users/mysum-udf.jar'} |
++-------------------+---------------------------------------------------------------------+
+5 rows in set
+
+# DESCRIBE FUNCTION EXTENDED MySum
++-------------------+---------------------------------------------------------------------+
+|         info name |                                                          info value |
++-------------------+---------------------------------------------------------------------+
+|   system function |                                                               false |
+|         temporary |                                                               false |
+|        class name |                                       org.example.SumScalarFunction |
+| function language |                                                                JAVA |
+|     resource uris | ResourceUri{resourceType=JAR, uri='file:/home/users/mysum-udf.jar'} |
+|              kind |                                                              SCALAR |
+|      requirements |                                                                  [] |
+|     deterministic |                                                                true |
+|  constant folding |                                                                true |
+|         signature |                       MySum(<INTEGER NOT NULL>, <INTEGER NOT NULL>) |
++-------------------+---------------------------------------------------------------------+
+10 rows in set
 ```
 {{< /tab >}}
 {{< tab "Python" >}}
@@ -296,6 +389,35 @@ The result of the above example is:
 | option:default-database |                db |
 +-------------------------+-------------------+
 4 rows in set
+
+# DESCRIBE FUNCTION MySum
++-------------------+---------------------------------------------------------------------+
+|         info name |                                                          info value |
++-------------------+---------------------------------------------------------------------+
+|   system function |                                                               false |
+|         temporary |                                                               false |
+|        class name |                                       org.example.SumScalarFunction |
+| function language |                                                                JAVA |
+|     resource uris | ResourceUri{resourceType=JAR, uri='file:/home/users/mysum-udf.jar'} |
++-------------------+---------------------------------------------------------------------+
+5 rows in set
+
+# DESCRIBE FUNCTION EXTENDED MySum
++-------------------+---------------------------------------------------------------------+
+|         info name |                                                          info value |
++-------------------+---------------------------------------------------------------------+
+|   system function |                                                               false |
+|         temporary |                                                               false |
+|        class name |                                       org.example.SumScalarFunction |
+| function language |                                                                JAVA |
+|     resource uris | ResourceUri{resourceType=JAR, uri='file:/home/users/mysum-udf.jar'} |
+|              kind |                                                              SCALAR |
+|      requirements |                                                                  [] |
+|     deterministic |                                                                true |
+|  constant folding |                                                                true |
+|         signature |                       MySum(<INTEGER NOT NULL>, <INTEGER NOT NULL>) |
++-------------------+---------------------------------------------------------------------+
+10 rows in set
 ```
 {{< /tab >}}
 {{< tab "SQL CLI" >}}
@@ -333,6 +455,35 @@ The result of the above example is:
 +-------------------------+-------------------+
 4 rows in set
 
+# DESCRIBE FUNCTION MySum
++-------------------+---------------------------------------------------------------------+
+|         info name |                                                          info value |
++-------------------+---------------------------------------------------------------------+
+|   system function |                                                               false |
+|         temporary |                                                               false |
+|        class name |                                       org.example.SumScalarFunction |
+| function language |                                                                JAVA |
+|     resource uris | ResourceUri{resourceType=JAR, uri='file:/home/users/mysum-udf.jar'} |
++-------------------+---------------------------------------------------------------------+
+5 rows in set
+
+# DESCRIBE FUNCTION EXTENDED MySum
++-------------------+---------------------------------------------------------------------+
+|         info name |                                                          info value |
++-------------------+---------------------------------------------------------------------+
+|   system function |                                                               false |
+|         temporary |                                                               false |
+|        class name |                                       org.example.SumScalarFunction |
+| function language |                                                                JAVA |
+|     resource uris | ResourceUri{resourceType=JAR, uri='file:/home/users/mysum-udf.jar'} |
+|              kind |                                                              SCALAR |
+|      requirements |                                                                  [] |
+|     deterministic |                                                                true |
+|  constant folding |                                                                true |
+|         signature |                       MySum(<INTEGER NOT NULL>, <INTEGER NOT NULL>) |
++-------------------+---------------------------------------------------------------------+
+10 rows in set
+
 # DESCRIBE JOB '228d70913eab60dda85c5e7f78b5782c'
 +----------------------------------+----------+---------+-------------------------+
 |                           job id | job name |  status |              start time |
@@ -362,9 +513,15 @@ The result of the above example is:
 { DESCRIBE | DESC } CATALOG [EXTENDED] catalog_name
 ```
 
+### DESCRIBE FUNCTION
+
+```sql
+{ DESCRIBE | DESC } FUNCTION [EXTENDED] [catalog_name.][db_name.]function_name
+```
+
 ### DESCRIBE JOB
 ```sql
 { DESCRIBE | DESC } JOB '<job_id>'
 ```
 
-<span class="label label-danger">Attention</span> DESCRIBE JOB statements only work in [SQL CLI]({{< ref "docs/dev/table/sqlClient" >}}) or [SQL Gateway]({{< ref "docs/dev/table/sql-gateway/overview" >}}).
\ No newline at end of file
+<span class="label label-danger">Attention</span> DESCRIBE JOB statements only work in [SQL CLI]({{< ref "docs/dev/table/sqlClient" >}}) or [SQL Gateway]({{< ref "docs/dev/table/sql-gateway/overview" >}}).
diff --git a/flink-table/flink-sql-client/src/test/resources/sql/function.q b/flink-table/flink-sql-client/src/test/resources/sql/function.q
index 0bca632de4f..270ad9ff1f1 100644
--- a/flink-table/flink-sql-client/src/test/resources/sql/function.q
+++ b/flink-table/flink-sql-client/src/test/resources/sql/function.q
@@ -1,4 +1,4 @@
-# function.q - CREATE/DROP/ALTER FUNCTION
+# function.q - CREATE/DROP/ALTER/SHOW/DESCRIBE FUNCTION
 #
 # Licensed to the Apache Software Foundation (ASF) under one or more
 # contributor license agreements.  See the NOTICE file distributed with
@@ -346,3 +346,159 @@ show user functions;
 SHOW JARS;
 Empty set
 !ok
+
+# ==========================================================================
+# test describe function
+# ==========================================================================
+
+ADD JAR '$VAR_UDF_JAR_PATH';
+[INFO] Execute statement succeeded.
+!info
+
+describe function `SUM`;
++--------------------+------------+
+|          info name | info value |
++--------------------+------------+
+| is system function |       true |
+|       is temporary |      false |
++--------------------+------------+
+2 rows in set
+!ok
+
+describe function extended `SUM`;
++---------------------------+----------------+
+|                 info name |     info value |
++---------------------------+----------------+
+|        is system function |           true |
+|              is temporary |          false |
+|                      kind |      AGGREGATE |
+|              requirements |             [] |
+|          is deterministic |           true |
+| supports constant folding |           true |
+|                 signature | SUM(<NUMERIC>) |
++---------------------------+----------------+
+7 rows in set
+!ok
+
+describe function temp_upperudf;
++--------------------+---------------------------------------------$VAR_UDF_JAR_PATH_DASH+
+|          info name |                                 $VAR_UDF_JAR_PATH_SPACE info value |
++--------------------+---------------------------------------------$VAR_UDF_JAR_PATH_DASH+
+| is system function |                                      $VAR_UDF_JAR_PATH_SPACE false |
+|       is temporary |                                       $VAR_UDF_JAR_PATH_SPACE true |
+|         class name |                                   $VAR_UDF_JAR_PATH_SPACE UpperUDF |
+|  function language |                                       $VAR_UDF_JAR_PATH_SPACE JAVA |
+|      resource uris | [ResourceUri{resourceType=JAR, uri='$VAR_UDF_JAR_PATH'}] |
++--------------------+---------------------------------------------$VAR_UDF_JAR_PATH_DASH+
+5 rows in set
+!ok
+
+describe function extended temp_upperudf;
++---------------------------+---------------------------------------------$VAR_UDF_JAR_PATH_DASH+
+|                 info name |                                 $VAR_UDF_JAR_PATH_SPACE info value |
++---------------------------+---------------------------------------------$VAR_UDF_JAR_PATH_DASH+
+|        is system function |                                      $VAR_UDF_JAR_PATH_SPACE false |
+|              is temporary |                                       $VAR_UDF_JAR_PATH_SPACE true |
+|                class name |                                   $VAR_UDF_JAR_PATH_SPACE UpperUDF |
+|         function language |                                       $VAR_UDF_JAR_PATH_SPACE JAVA |
+|             resource uris | [ResourceUri{resourceType=JAR, uri='$VAR_UDF_JAR_PATH'}] |
+|                      kind |                                     $VAR_UDF_JAR_PATH_SPACE SCALAR |
+|              requirements |                                         $VAR_UDF_JAR_PATH_SPACE [] |
+|          is deterministic |                                       $VAR_UDF_JAR_PATH_SPACE true |
+| supports constant folding |                                       $VAR_UDF_JAR_PATH_SPACE true |
+|                 signature |                $VAR_UDF_JAR_PATH_SPACE c1.db.temp_upperudf(STRING) |
++---------------------------+---------------------------------------------$VAR_UDF_JAR_PATH_DASH+
+10 rows in set
+!ok
+
+desc function temp_upperudf;
++--------------------+---------------------------------------------$VAR_UDF_JAR_PATH_DASH+
+|          info name |                                 $VAR_UDF_JAR_PATH_SPACE info value |
++--------------------+---------------------------------------------$VAR_UDF_JAR_PATH_DASH+
+| is system function |                                      $VAR_UDF_JAR_PATH_SPACE false |
+|       is temporary |                                       $VAR_UDF_JAR_PATH_SPACE true |
+|         class name |                                   $VAR_UDF_JAR_PATH_SPACE UpperUDF |
+|  function language |                                       $VAR_UDF_JAR_PATH_SPACE JAVA |
+|      resource uris | [ResourceUri{resourceType=JAR, uri='$VAR_UDF_JAR_PATH'}] |
++--------------------+---------------------------------------------$VAR_UDF_JAR_PATH_DASH+
+5 rows in set
+!ok
+
+desc function extended temp_upperudf;
++---------------------------+---------------------------------------------$VAR_UDF_JAR_PATH_DASH+
+|                 info name |                                 $VAR_UDF_JAR_PATH_SPACE info value |
++---------------------------+---------------------------------------------$VAR_UDF_JAR_PATH_DASH+
+|        is system function |                                      $VAR_UDF_JAR_PATH_SPACE false |
+|              is temporary |                                       $VAR_UDF_JAR_PATH_SPACE true |
+|                class name |                                   $VAR_UDF_JAR_PATH_SPACE UpperUDF |
+|         function language |                                       $VAR_UDF_JAR_PATH_SPACE JAVA |
+|             resource uris | [ResourceUri{resourceType=JAR, uri='$VAR_UDF_JAR_PATH'}] |
+|                      kind |                                     $VAR_UDF_JAR_PATH_SPACE SCALAR |
+|              requirements |                                         $VAR_UDF_JAR_PATH_SPACE [] |
+|          is deterministic |                                       $VAR_UDF_JAR_PATH_SPACE true |
+| supports constant folding |                                       $VAR_UDF_JAR_PATH_SPACE true |
+|                 signature |                $VAR_UDF_JAR_PATH_SPACE c1.db.temp_upperudf(STRING) |
++---------------------------+---------------------------------------------$VAR_UDF_JAR_PATH_DASH+
+10 rows in set
+!ok
+
+# test that system functions get resolved before catalog functions
+create temporary system function temp_upperudf AS 'UpperUDF' using jar '$VAR_UDF_JAR_PATH';
+[INFO] Execute statement succeeded.
+!info
+
+# we see both the temp system function and the catalog function for temp_upperudf
+show user functions;
++---------------+
+| function name |
++---------------+
+|        func11 |
+|         func3 |
+|         func4 |
+| temp_upperudf |
+| temp_upperudf |
+|      tmp_func |
+|      upperudf |
++---------------+
+7 rows in set
+!ok
+
+# but the system function gets resolved first
+describe function temp_upperudf;
++--------------------+------------+
+|          info name | info value |
++--------------------+------------+
+| is system function |       true |
+|       is temporary |       true |
++--------------------+------------+
+2 rows in set
+!ok
+
+# but the catalog function should get resolved when using the full name
+describe function `c1`.`db`.temp_upperudf;
++--------------------+---------------------------------------------$VAR_UDF_JAR_PATH_DASH+
+|          info name |                                 $VAR_UDF_JAR_PATH_SPACE info value |
++--------------------+---------------------------------------------$VAR_UDF_JAR_PATH_DASH+
+| is system function |                                      $VAR_UDF_JAR_PATH_SPACE false |
+|       is temporary |                                       $VAR_UDF_JAR_PATH_SPACE true |
+|         class name |                                   $VAR_UDF_JAR_PATH_SPACE UpperUDF |
+|  function language |                                       $VAR_UDF_JAR_PATH_SPACE JAVA |
+|      resource uris | [ResourceUri{resourceType=JAR, uri='$VAR_UDF_JAR_PATH'}] |
++--------------------+---------------------------------------------$VAR_UDF_JAR_PATH_DASH+
+5 rows in set
+!ok
+
+describe function extended temp_upperudf;
++---------------------------+-----------------------+
+|                 info name |            info value |
++---------------------------+-----------------------+
+|        is system function |                  true |
+|              is temporary |                  true |
+|                      kind |                SCALAR |
+|              requirements |                    [] |
+|          is deterministic |                  true |
+| supports constant folding |                  true |
+|                 signature | temp_upperudf(STRING) |
++---------------------------+-----------------------+
+7 rows in set
+!ok
diff --git a/flink-table/flink-sql-gateway/src/main/java/org/apache/flink/table/gateway/service/operation/OperationExecutor.java b/flink-table/flink-sql-gateway/src/main/java/org/apache/flink/table/gateway/service/operation/OperationExecutor.java
index c6683bd0b2b..4288668accc 100644
--- a/flink-table/flink-sql-gateway/src/main/java/org/apache/flink/table/gateway/service/operation/OperationExecutor.java
+++ b/flink-table/flink-sql-gateway/src/main/java/org/apache/flink/table/gateway/service/operation/OperationExecutor.java
@@ -72,6 +72,7 @@ import org.apache.flink.table.operations.BeginStatementSetOperation;
 import org.apache.flink.table.operations.CallProcedureOperation;
 import org.apache.flink.table.operations.CompileAndExecutePlanOperation;
 import org.apache.flink.table.operations.DeleteFromFilterOperation;
+import org.apache.flink.table.operations.DescribeFunctionOperation;
 import org.apache.flink.table.operations.EndStatementSetOperation;
 import org.apache.flink.table.operations.ExecutableOperation;
 import org.apache.flink.table.operations.LoadModuleOperation;
@@ -510,7 +511,8 @@ public class OperationExecutor {
                 || op instanceof ShowJarsOperation
                 || op instanceof CreateTempSystemFunctionOperation
                 || op instanceof CreateCatalogFunctionOperation
-                || op instanceof ShowFunctionsOperation) {
+                || op instanceof ShowFunctionsOperation
+                || op instanceof DescribeFunctionOperation) {
             return callExecutableOperation(handle, (ExecutableOperation) op);
         } else if (op instanceof MaterializedTableOperation) {
             return sessionContext
diff --git a/flink-table/flink-sql-parser/src/main/codegen/data/Parser.tdd b/flink-table/flink-sql-parser/src/main/codegen/data/Parser.tdd
index 722d7fdf086..64c450d573e 100644
--- a/flink-table/flink-sql-parser/src/main/codegen/data/Parser.tdd
+++ b/flink-table/flink-sql-parser/src/main/codegen/data/Parser.tdd
@@ -135,6 +135,7 @@
     "org.apache.flink.sql.parser.dql.SqlShowCreateView"
     "org.apache.flink.sql.parser.dql.SqlShowCreateCatalog"
     "org.apache.flink.sql.parser.dql.SqlShowViews"
+    "org.apache.flink.sql.parser.dql.SqlRichDescribeFunction"
     "org.apache.flink.sql.parser.dql.SqlRichDescribeModel"
     "org.apache.flink.sql.parser.dql.SqlRichDescribeTable"
     "org.apache.flink.sql.parser.dql.SqlUnloadModule"
@@ -635,6 +636,7 @@
     "SqlShowJobs()"
     "SqlTruncateTable()"
     "SqlDescribeJob()"
+    "SqlRichDescribeFunction()"
     "SqlRichDescribeModel()"
     "SqlRichDescribeTable()"
   ]
diff --git a/flink-table/flink-sql-parser/src/main/codegen/includes/parserImpls.ftl b/flink-table/flink-sql-parser/src/main/codegen/includes/parserImpls.ftl
index 38c4c85227d..b02be2ad72c 100644
--- a/flink-table/flink-sql-parser/src/main/codegen/includes/parserImpls.ftl
+++ b/flink-table/flink-sql-parser/src/main/codegen/includes/parserImpls.ftl
@@ -803,6 +803,25 @@ SqlShowCreate SqlShowCreate() :
     )
 }
 
+/**
+ * DESCRIBE | DESC FUNCTION [ EXTENDED] [[catalogName.] dataBasesName].functionName sql call.
+ * Here we add Rich in className to match the naming of SqlRichDescribeTable.
+ */
+SqlRichDescribeFunction SqlRichDescribeFunction() :
+{
+    SqlIdentifier functionName;
+    SqlParserPos pos;
+    boolean isExtended = false;
+}
+{
+    ( <DESCRIBE> | <DESC> ) <FUNCTION> { pos = getPos();}
+    [ <EXTENDED> { isExtended = true;} ]
+    functionName = CompoundIdentifier()
+    {
+        return new SqlRichDescribeFunction(pos, functionName, isExtended);
+    }
+}
+
 /**
  * DESCRIBE | DESC MODEL [ EXTENDED] [[catalogName.] dataBasesName].modelName sql call.
  * Here we add Rich in className to match the naming of SqlRichDescribeTable.
diff --git a/flink-table/flink-sql-parser/src/main/java/org/apache/flink/sql/parser/dql/SqlRichDescribeModel.java b/flink-table/flink-sql-parser/src/main/java/org/apache/flink/sql/parser/dql/SqlRichDescribeFunction.java
similarity index 63%
copy from flink-table/flink-sql-parser/src/main/java/org/apache/flink/sql/parser/dql/SqlRichDescribeModel.java
copy to flink-table/flink-sql-parser/src/main/java/org/apache/flink/sql/parser/dql/SqlRichDescribeFunction.java
index b756122cd1b..57bce5fa7d7 100644
--- a/flink-table/flink-sql-parser/src/main/java/org/apache/flink/sql/parser/dql/SqlRichDescribeModel.java
+++ b/flink-table/flink-sql-parser/src/main/java/org/apache/flink/sql/parser/dql/SqlRichDescribeFunction.java
@@ -31,22 +31,22 @@ import java.util.Collections;
 import java.util.List;
 
 /**
- * DESCRIBE MODEL [EXTENDED] [[catalogName.] dataBasesName].sqlIdentifier sql call. Here we add Rich
- * in className to follow the convention of {@link org.apache.calcite.sql.SqlDescribeTable}, which
- * only had it to distinguish from calcite's original SqlDescribeTable, even though calcite does not
- * have SqlDescribeModel.
+ * DESCRIBE FUNCTION [EXTENDED] [[catalogName.] dataBasesName].sqlIdentifier sql call. Here we add
+ * Rich in className to follow the convention of {@link org.apache.calcite.sql.SqlDescribeTable},
+ * which only had it to distinguish from calcite's original SqlDescribeTable, even though calcite
+ * does not have SqlDescribeFunction.
  */
-public class SqlRichDescribeModel extends SqlCall {
+public class SqlRichDescribeFunction extends SqlCall {
 
     public static final SqlSpecialOperator OPERATOR =
-            new SqlSpecialOperator("DESCRIBE MODEL", SqlKind.OTHER);
-    protected final SqlIdentifier modelNameIdentifier;
-    private boolean isExtended;
+            new SqlSpecialOperator("DESCRIBE FUNCTION", SqlKind.OTHER);
+    protected final SqlIdentifier functionNameIdentifier;
+    private final boolean isExtended;
 
-    public SqlRichDescribeModel(
-            SqlParserPos pos, SqlIdentifier modelNameIdentifier, boolean isExtended) {
+    public SqlRichDescribeFunction(
+            SqlParserPos pos, SqlIdentifier functionNameIdentifier, boolean isExtended) {
         super(pos);
-        this.modelNameIdentifier = modelNameIdentifier;
+        this.functionNameIdentifier = functionNameIdentifier;
         this.isExtended = isExtended;
     }
 
@@ -57,23 +57,23 @@ public class SqlRichDescribeModel extends SqlCall {
 
     @Override
     public List<SqlNode> getOperandList() {
-        return Collections.singletonList(modelNameIdentifier);
+        return Collections.singletonList(functionNameIdentifier);
     }
 
     public boolean isExtended() {
         return isExtended;
     }
 
-    public String[] fullModelName() {
-        return modelNameIdentifier.names.toArray(new String[0]);
+    public String[] fullFunctionName() {
+        return functionNameIdentifier.names.toArray(new String[0]);
     }
 
     @Override
     public void unparse(SqlWriter writer, int leftPrec, int rightPrec) {
-        writer.keyword("DESCRIBE MODEL");
+        writer.keyword("DESCRIBE FUNCTION");
         if (isExtended) {
             writer.keyword("EXTENDED");
         }
-        modelNameIdentifier.unparse(writer, leftPrec, rightPrec);
+        functionNameIdentifier.unparse(writer, leftPrec, rightPrec);
     }
 }
diff --git a/flink-table/flink-sql-parser/src/main/java/org/apache/flink/sql/parser/dql/SqlRichDescribeModel.java b/flink-table/flink-sql-parser/src/main/java/org/apache/flink/sql/parser/dql/SqlRichDescribeModel.java
index b756122cd1b..6a5f5acfc50 100644
--- a/flink-table/flink-sql-parser/src/main/java/org/apache/flink/sql/parser/dql/SqlRichDescribeModel.java
+++ b/flink-table/flink-sql-parser/src/main/java/org/apache/flink/sql/parser/dql/SqlRichDescribeModel.java
@@ -41,7 +41,7 @@ public class SqlRichDescribeModel extends SqlCall {
     public static final SqlSpecialOperator OPERATOR =
             new SqlSpecialOperator("DESCRIBE MODEL", SqlKind.OTHER);
     protected final SqlIdentifier modelNameIdentifier;
-    private boolean isExtended;
+    private final boolean isExtended;
 
     public SqlRichDescribeModel(
             SqlParserPos pos, SqlIdentifier modelNameIdentifier, boolean isExtended) {
diff --git a/flink-table/flink-sql-parser/src/main/java/org/apache/flink/sql/parser/dql/SqlRichDescribeTable.java b/flink-table/flink-sql-parser/src/main/java/org/apache/flink/sql/parser/dql/SqlRichDescribeTable.java
index 6a0174d4ed2..c6ce0117a13 100644
--- a/flink-table/flink-sql-parser/src/main/java/org/apache/flink/sql/parser/dql/SqlRichDescribeTable.java
+++ b/flink-table/flink-sql-parser/src/main/java/org/apache/flink/sql/parser/dql/SqlRichDescribeTable.java
@@ -39,7 +39,7 @@ public class SqlRichDescribeTable extends SqlCall {
     public static final SqlSpecialOperator OPERATOR =
             new SqlSpecialOperator("DESCRIBE TABLE", SqlKind.DESCRIBE_TABLE);
     protected final SqlIdentifier tableNameIdentifier;
-    private boolean isExtended = false;
+    private final boolean isExtended;
 
     public SqlRichDescribeTable(
             SqlParserPos pos, SqlIdentifier tableNameIdentifier, boolean isExtended) {
diff --git a/flink-table/flink-sql-parser/src/test/java/org/apache/flink/sql/parser/FlinkSqlParserImplTest.java b/flink-table/flink-sql-parser/src/test/java/org/apache/flink/sql/parser/FlinkSqlParserImplTest.java
index 39c9a41c18c..4245ff492cc 100644
--- a/flink-table/flink-sql-parser/src/test/java/org/apache/flink/sql/parser/FlinkSqlParserImplTest.java
+++ b/flink-table/flink-sql-parser/src/test/java/org/apache/flink/sql/parser/FlinkSqlParserImplTest.java
@@ -462,6 +462,17 @@ class FlinkSqlParserImplTest extends SqlParserTest {
         sql("desc model catalog1.db1.mdl").ok("DESCRIBE MODEL `CATALOG1`.`DB1`.`MDL`");
     }
 
+    @Test
+    void testDescribeFunction() {
+        sql("describe function fn").ok("DESCRIBE FUNCTION `FN`");
+        sql("describe function catalog1.db1.fn").ok("DESCRIBE FUNCTION `CATALOG1`.`DB1`.`FN`");
+        sql("describe function extended fn").ok("DESCRIBE FUNCTION EXTENDED `FN`");
+
+        sql("desc function fn").ok("DESCRIBE FUNCTION `FN`");
+        sql("desc function catalog1.db1.fn").ok("DESCRIBE FUNCTION `CATALOG1`.`DB1`.`FN`");
+        sql("desc function extended fn").ok("DESCRIBE FUNCTION EXTENDED `FN`");
+    }
+
     @Test
     void testShowColumns() {
         sql("show columns from tbl").ok("SHOW COLUMNS FROM `TBL`");
diff --git a/flink-table/flink-table-api-java/src/main/java/org/apache/flink/table/catalog/ContextResolvedFunction.java b/flink-table/flink-table-api-java/src/main/java/org/apache/flink/table/catalog/ContextResolvedFunction.java
index 313a4cf94e0..ea7dca69b58 100644
--- a/flink-table/flink-table-api-java/src/main/java/org/apache/flink/table/catalog/ContextResolvedFunction.java
+++ b/flink-table/flink-table-api-java/src/main/java/org/apache/flink/table/catalog/ContextResolvedFunction.java
@@ -68,12 +68,25 @@ public final class ContextResolvedFunction {
 
     private final FunctionDefinition functionDefinition;
 
+    private final @Nullable CatalogFunction catalogFunction;
+
     public static ContextResolvedFunction permanent(
             FunctionIdentifier functionIdentifier, FunctionDefinition functionDefinition) {
         Preconditions.checkNotNull(
                 functionIdentifier,
                 "Function identifier should not be null for a permanent function.");
-        return new ContextResolvedFunction(false, functionIdentifier, functionDefinition);
+        return new ContextResolvedFunction(false, functionIdentifier, functionDefinition, null);
+    }
+
+    public static ContextResolvedFunction permanent(
+            FunctionIdentifier functionIdentifier,
+            FunctionDefinition functionDefinition,
+            CatalogFunction catalogFunction) {
+        Preconditions.checkNotNull(
+                functionIdentifier,
+                "Function identifier should not be null for a permanent function.");
+        return new ContextResolvedFunction(
+                false, functionIdentifier, functionDefinition, catalogFunction);
     }
 
     public static ContextResolvedFunction temporary(
@@ -81,29 +94,43 @@ public final class ContextResolvedFunction {
         Preconditions.checkNotNull(
                 functionIdentifier,
                 "Function identifier should not be null for a temporary function.");
-        return new ContextResolvedFunction(true, functionIdentifier, functionDefinition);
+        return new ContextResolvedFunction(true, functionIdentifier, functionDefinition, null);
+    }
+
+    public static ContextResolvedFunction temporary(
+            FunctionIdentifier functionIdentifier,
+            FunctionDefinition functionDefinition,
+            CatalogFunction catalogFunction) {
+        Preconditions.checkNotNull(
+                functionIdentifier,
+                "Function identifier should not be null for a temporary function.");
+        return new ContextResolvedFunction(
+                true, functionIdentifier, functionDefinition, catalogFunction);
     }
 
     public static ContextResolvedFunction anonymous(FunctionDefinition functionDefinition) {
-        return new ContextResolvedFunction(true, null, functionDefinition);
+        return new ContextResolvedFunction(true, null, functionDefinition, null);
     }
 
     public static ContextResolvedFunction fromCallExpression(CallExpression callExpression) {
         return new ContextResolvedFunction(
                 callExpression.isTemporary(),
                 callExpression.getFunctionIdentifier().orElse(null),
-                callExpression.getFunctionDefinition());
+                callExpression.getFunctionDefinition(),
+                null);
     }
 
     private ContextResolvedFunction(
             boolean isTemporary,
             @Nullable FunctionIdentifier functionIdentifier,
-            FunctionDefinition functionDefinition) {
+            FunctionDefinition functionDefinition,
+            @Nullable CatalogFunction catalogFunction) {
         this.isTemporary = isTemporary;
         this.functionIdentifier = functionIdentifier;
         this.functionDefinition =
                 Preconditions.checkNotNull(
                         functionDefinition, "Function definition must not be null.");
+        this.catalogFunction = catalogFunction;
     }
 
     public boolean isAnonymous() {
@@ -127,6 +154,10 @@ public final class ContextResolvedFunction {
         return functionDefinition;
     }
 
+    public CatalogFunction getCatalogFunction() {
+        return catalogFunction;
+    }
+
     public String asSummaryString() {
         if (functionIdentifier == null) {
             return functionDefinition.toString();
diff --git a/flink-table/flink-table-api-java/src/main/java/org/apache/flink/table/catalog/FunctionCatalog.java b/flink-table/flink-table-api-java/src/main/java/org/apache/flink/table/catalog/FunctionCatalog.java
index 83074cd020b..de4b24114dc 100644
--- a/flink-table/flink-table-api-java/src/main/java/org/apache/flink/table/catalog/FunctionCatalog.java
+++ b/flink-table/flink-table-api-java/src/main/java/org/apache/flink/table/catalog/FunctionCatalog.java
@@ -629,7 +629,8 @@ public final class FunctionCatalog {
             return Optional.of(
                     ContextResolvedFunction.temporary(
                             FunctionIdentifier.of(oi),
-                            getFunctionDefinition(oi.getObjectName(), potentialResult)));
+                            getFunctionDefinition(oi.getObjectName(), potentialResult),
+                            potentialResult));
         }
 
         Optional<Catalog> catalogOptional = catalogManager.getCatalog(oi.getCatalogName());
@@ -658,7 +659,8 @@ public final class FunctionCatalog {
                 }
 
                 return Optional.of(
-                        ContextResolvedFunction.permanent(FunctionIdentifier.of(oi), fd));
+                        ContextResolvedFunction.permanent(
+                                FunctionIdentifier.of(oi), fd, catalogFunction));
             } catch (FunctionNotExistException e) {
                 // Ignore
             }
diff --git a/flink-table/flink-table-api-java/src/main/java/org/apache/flink/table/operations/DescribeFunctionOperation.java b/flink-table/flink-table-api-java/src/main/java/org/apache/flink/table/operations/DescribeFunctionOperation.java
new file mode 100644
index 00000000000..7fa63bdb9c7
--- /dev/null
+++ b/flink-table/flink-table-api-java/src/main/java/org/apache/flink/table/operations/DescribeFunctionOperation.java
@@ -0,0 +1,132 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.flink.table.operations;
+
+import org.apache.flink.annotation.Internal;
+import org.apache.flink.table.api.DataTypes;
+import org.apache.flink.table.api.ValidationException;
+import org.apache.flink.table.api.internal.TableResultInternal;
+import org.apache.flink.table.catalog.CatalogFunction;
+import org.apache.flink.table.catalog.ContextResolvedFunction;
+import org.apache.flink.table.catalog.UnresolvedIdentifier;
+import org.apache.flink.table.functions.FunctionDefinition;
+import org.apache.flink.table.types.DataType;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+import static org.apache.flink.table.api.internal.TableResultUtils.buildTableResult;
+import static org.apache.flink.table.types.inference.TypeInferenceUtil.generateSignature;
+
+/**
+ * Operation to describe a FUNCTION.
+ *
+ * <p>Syntax:
+ *
+ * <pre>
+ * DESCRIBE FUNCTION [EXTENDED] [[catalogName.] dataBasesName].sqlIdentifier
+ * </pre>
+ */
+@Internal
+public class DescribeFunctionOperation implements Operation, ExecutableOperation {
+
+    private final UnresolvedIdentifier sqlIdentifier;
+    private final boolean isExtended;
+
+    public DescribeFunctionOperation(UnresolvedIdentifier sqlIdentifier, boolean isExtended) {
+        this.sqlIdentifier = sqlIdentifier;
+        this.isExtended = isExtended;
+    }
+
+    public UnresolvedIdentifier getSqlIdentifier() {
+        return sqlIdentifier;
+    }
+
+    public boolean isExtended() {
+        return isExtended;
+    }
+
+    @Override
+    public String asSummaryString() {
+        Map<String, Object> params = new LinkedHashMap<>();
+        params.put("identifier", sqlIdentifier);
+        params.put("isExtended", isExtended);
+        return OperationUtils.formatWithChildren(
+                "DESCRIBE FUNCTION", params, Collections.emptyList(), Operation::asSummaryString);
+    }
+
+    @Override
+    public TableResultInternal execute(Context ctx) {
+        // DESCRIBE FUNCTION <function> shows all the function properties.
+        Optional<ContextResolvedFunction> functionOpt =
+                ctx.getFunctionCatalog().lookupFunction(sqlIdentifier);
+        if (!functionOpt.isPresent()) {
+            throw new ValidationException(
+                    String.format(
+                            "Function with the identifier '%s' doesn't exist.",
+                            sqlIdentifier.asSummaryString()));
+        }
+        final ContextResolvedFunction function = functionOpt.get();
+        final CatalogFunction catalogFunction = function.getCatalogFunction();
+
+        List<List<Object>> rows = new ArrayList<>();
+        rows.add(Arrays.asList("is system function", String.valueOf(catalogFunction == null)));
+        rows.add(Arrays.asList("is temporary", String.valueOf(function.isTemporary())));
+        if (catalogFunction != null) {
+            rows.add(Arrays.asList("class name", catalogFunction.getClassName()));
+            rows.add(
+                    Arrays.asList(
+                            "function language", catalogFunction.getFunctionLanguage().toString()));
+            rows.add(
+                    Arrays.asList(
+                            "resource uris", catalogFunction.getFunctionResources().toString()));
+        }
+
+        if (isExtended) {
+            final FunctionDefinition definition = function.getDefinition();
+            rows.add(Arrays.asList("kind", definition.getKind().toString()));
+            rows.add(Arrays.asList("requirements", definition.getRequirements().toString()));
+            rows.add(
+                    Arrays.asList(
+                            "is deterministic", String.valueOf(definition.isDeterministic())));
+            rows.add(
+                    Arrays.asList(
+                            "supports constant folding",
+                            String.valueOf(definition.supportsConstantFolding())));
+            rows.add(
+                    Arrays.asList(
+                            "signature",
+                            generateSignature(
+                                    definition.getTypeInference(
+                                            ctx.getCatalogManager().getDataTypeFactory()),
+                                    function.toString(),
+                                    definition)));
+        }
+
+        return buildTableResult(
+                new String[] {"info name", "info value"},
+                new DataType[] {DataTypes.STRING(), DataTypes.STRING()},
+                rows.stream().map(List::toArray).toArray(Object[][]::new));
+    }
+}
diff --git a/flink-table/flink-table-planner/src/main/java/org/apache/flink/table/planner/operations/converters/SqlDescribeFunctionConverter.java b/flink-table/flink-table-planner/src/main/java/org/apache/flink/table/planner/operations/converters/SqlDescribeFunctionConverter.java
new file mode 100644
index 00000000000..2da2e434091
--- /dev/null
+++ b/flink-table/flink-table-planner/src/main/java/org/apache/flink/table/planner/operations/converters/SqlDescribeFunctionConverter.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.flink.table.planner.operations.converters;
+
+import org.apache.flink.sql.parser.dql.SqlRichDescribeFunction;
+import org.apache.flink.table.catalog.UnresolvedIdentifier;
+import org.apache.flink.table.operations.DescribeFunctionOperation;
+import org.apache.flink.table.operations.Operation;
+
+/** A converter for {@link org.apache.flink.sql.parser.dql.SqlRichDescribeFunction}. */
+public class SqlDescribeFunctionConverter implements SqlNodeConverter<SqlRichDescribeFunction> {
+
+    @Override
+    public Operation convertSqlNode(
+            SqlRichDescribeFunction sqlRichDescribeFunction, ConvertContext context) {
+        UnresolvedIdentifier unresolvedIdentifier =
+                UnresolvedIdentifier.of(sqlRichDescribeFunction.fullFunctionName());
+        return new DescribeFunctionOperation(
+                unresolvedIdentifier, sqlRichDescribeFunction.isExtended());
+    }
+}
diff --git a/flink-table/flink-table-planner/src/main/java/org/apache/flink/table/planner/operations/converters/SqlNodeConverters.java b/flink-table/flink-table-planner/src/main/java/org/apache/flink/table/planner/operations/converters/SqlNodeConverters.java
index a2126791e4b..25ab375469f 100644
--- a/flink-table/flink-table-planner/src/main/java/org/apache/flink/table/planner/operations/converters/SqlNodeConverters.java
+++ b/flink-table/flink-table-planner/src/main/java/org/apache/flink/table/planner/operations/converters/SqlNodeConverters.java
@@ -67,6 +67,7 @@ public class SqlNodeConverters {
         register(new SqlShowTablesConverter());
         register(new SqlShowViewsConverter());
         register(new SqlShowCatalogsConverter());
+        register(new SqlDescribeFunctionConverter());
     }
 
     /**
diff --git a/flink-table/flink-table-planner/src/main/scala/org/apache/flink/table/planner/calcite/FlinkPlannerImpl.scala b/flink-table/flink-table-planner/src/main/scala/org/apache/flink/table/planner/calcite/FlinkPlannerImpl.scala
index 4420ec96372..649ba8d90a2 100644
--- a/flink-table/flink-table-planner/src/main/scala/org/apache/flink/table/planner/calcite/FlinkPlannerImpl.scala
+++ b/flink-table/flink-table-planner/src/main/scala/org/apache/flink/table/planner/calcite/FlinkPlannerImpl.scala
@@ -148,6 +148,7 @@ class FlinkPlannerImpl(
         || sqlNode.isInstanceOf[SqlShowProcedures]
         || sqlNode.isInstanceOf[SqlShowJobs]
         || sqlNode.isInstanceOf[SqlDescribeJob]
+        || sqlNode.isInstanceOf[SqlRichDescribeFunction]
         || sqlNode.isInstanceOf[SqlRichDescribeModel]
         || sqlNode.isInstanceOf[SqlRichDescribeTable]
         || sqlNode.isInstanceOf[SqlUnloadModule]
diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/operations/SqlOtherOperationConverterTest.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/operations/SqlOtherOperationConverterTest.java
index e4dabb3c4d2..07e62ca75eb 100644
--- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/operations/SqlOtherOperationConverterTest.java
+++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/operations/SqlOtherOperationConverterTest.java
@@ -20,7 +20,9 @@ package org.apache.flink.table.planner.operations;
 
 import org.apache.flink.table.api.SqlParserException;
 import org.apache.flink.table.api.ValidationException;
+import org.apache.flink.table.catalog.UnresolvedIdentifier;
 import org.apache.flink.table.operations.DescribeCatalogOperation;
+import org.apache.flink.table.operations.DescribeFunctionOperation;
 import org.apache.flink.table.operations.LoadModuleOperation;
 import org.apache.flink.table.operations.Operation;
 import org.apache.flink.table.operations.ShowCatalogsOperation;
@@ -106,6 +108,31 @@ public class SqlOtherOperationConverterTest extends SqlNodeToOperationConversion
                                 catalogName, extended));
     }
 
+    @ParameterizedTest
+    @CsvSource({"true,true", "true,false", "false,true", "false,false"})
+    void testDescribeFunction(boolean abbr, boolean extended) {
+        final String functionName = "f1";
+        final UnresolvedIdentifier functionIdentifier = UnresolvedIdentifier.of(functionName);
+        final String sql =
+                String.format(
+                        "%s FUNCTION %s %s",
+                        abbr ? "DESC" : "DESCRIBE", extended ? "EXTENDED" : "", functionName);
+        Operation operation = parse(sql);
+        assertThat(operation)
+                .isInstanceOf(DescribeFunctionOperation.class)
+                .asInstanceOf(InstanceOfAssertFactories.type(DescribeFunctionOperation.class))
+                .extracting(
+                        DescribeFunctionOperation::getSqlIdentifier,
+                        DescribeFunctionOperation::isExtended,
+                        DescribeFunctionOperation::asSummaryString)
+                .containsExactly(
+                        functionIdentifier,
+                        extended,
+                        String.format(
+                                "DESCRIBE FUNCTION: (identifier: [%s], isExtended: [%b])",
+                                functionIdentifier, extended));
+    }
+
     @Test
     void testUseDatabase() {
         final String sql1 = "USE db1";