You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@doris.apache.org by mo...@apache.org on 2021/08/07 13:29:26 UTC

[incubator-doris] branch master updated: [Feature] Support alias function (#6261)

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

morningman pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-doris.git


The following commit(s) were added to refs/heads/master by this push:
     new 70825ce  [Feature] Support alias function (#6261)
70825ce is described below

commit 70825ce846be69560630ca80892f8973e9bd0eb2
Author: qiye <ji...@gmail.com>
AuthorDate: Sat Aug 7 21:29:13 2021 +0800

    [Feature] Support alias function (#6261)
    
    Implement #6260.
    
    Add alias function type.
---
 docs/.vuepress/sidebar/en.js                       |   1 +
 docs/.vuepress/sidebar/zh-CN.js                    |   1 +
 .../sql-reference/sql-functions/digital-masking.md |  56 +++++
 .../Data Definition/create-function.md             |  83 ++++---
 .../Data Definition/show-functions.md              |   8 +-
 .../sql-reference/sql-functions/digital-masking.md |  56 +++++
 .../Data Definition/create-function.md             |  47 ++--
 .../Data Definition/show-functions.md              |   8 +-
 fe/fe-core/src/main/cup/sql_parser.cup             |  13 +-
 .../java/org/apache/doris/analysis/Analyzer.java   |   2 +
 .../apache/doris/analysis/CreateFunctionStmt.java  |  57 ++++-
 .../apache/doris/analysis/FunctionCallExpr.java    |  93 +++++++-
 .../org/apache/doris/analysis/LiteralExpr.java     |  35 +++
 .../org/apache/doris/catalog/AliasFunction.java    | 258 +++++++++++++++++++++
 .../java/org/apache/doris/catalog/Function.java    |  11 +-
 .../java/org/apache/doris/catalog/FunctionSet.java |   1 +
 .../doris/rewrite/RewriteAliasFunctionRule.java    |  48 ++++
 fe/fe-core/src/main/jflex/sql_scanner.flex         |   2 +
 .../apache/doris/catalog/CreateFunctionTest.java   |  23 ++
 19 files changed, 749 insertions(+), 54 deletions(-)

diff --git a/docs/.vuepress/sidebar/en.js b/docs/.vuepress/sidebar/en.js
index 682fd9f..34bca23 100644
--- a/docs/.vuepress/sidebar/en.js
+++ b/docs/.vuepress/sidebar/en.js
@@ -399,6 +399,7 @@ module.exports = [
           },
           "window-function",
           "cast",
+          "digital-masking",
         ],
       },
       {
diff --git a/docs/.vuepress/sidebar/zh-CN.js b/docs/.vuepress/sidebar/zh-CN.js
index 0ada9ad..86ac1a1 100644
--- a/docs/.vuepress/sidebar/zh-CN.js
+++ b/docs/.vuepress/sidebar/zh-CN.js
@@ -404,6 +404,7 @@ module.exports = [
           },
           "window-function",
           "cast",
+          "digital-masking",
         ],
       },
       {
diff --git a/docs/en/sql-reference/sql-functions/digital-masking.md b/docs/en/sql-reference/sql-functions/digital-masking.md
new file mode 100644
index 0000000..bd2b22b
--- /dev/null
+++ b/docs/en/sql-reference/sql-functions/digital-masking.md
@@ -0,0 +1,56 @@
+---
+{
+    "title": "DIGITAL-MASKING",
+    "language": "en"
+}
+---
+
+<!-- 
+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.
+-->
+
+# DIGITAL_MASKING
+
+## description
+
+### Syntax
+
+```
+digital_masking(digital_number)
+```
+
+Alias function, the original function is `concat(left(id,3),'****',right(id,4))`.
+
+Desensitizes the input `digital_number` and returns the result after masking desensitization.
+
+## example
+
+1. Desensitize the cell phone number
+
+    ```sql
+    mysql> select digital_masking(13812345678);
+    +------------------------------+
+    | digital_masking(13812345678) |
+    +------------------------------+
+    | 138****5678                  |
+    +------------------------------+
+    ```
+
+## keyword
+
+DIGITAL_MASKING
diff --git a/docs/en/sql-reference/sql-statements/Data Definition/create-function.md b/docs/en/sql-reference/sql-statements/Data Definition/create-function.md
index fc0a5f0..7eed4e4 100644
--- a/docs/en/sql-reference/sql-statements/Data Definition/create-function.md	
+++ b/docs/en/sql-reference/sql-statements/Data Definition/create-function.md	
@@ -25,50 +25,60 @@ under the License.
 -->
 
 # CREATE FUNCTION
-##Description
+## Description
 ### Syntax
 
 ```
-CREATE [AGGREGATE] FUNCTION function_name
-	(angry type [...])
-	RETURNS ret_type
-	[INTERMEDIATE inter_type]
-	[PROPERTIES ("key" = "value" [, ...]) ]
+CREATE [AGGREGATE] [ALIAS] FUNCTION function_name
+    (arg_type [, ...])
+    [RETURNS ret_type]
+    [INTERMEDIATE inter_type]
+    [WITH PARAMETER(param [,...]) AS origin_function]
+    [PROPERTIES ("key" = "value" [, ...]) ]
 ```
 
 ### Parameters
 
->`AGGREGATE`: If this is the case, it means that the created function is an aggregate function, otherwise it is a scalar function.
+> `AGGREGATE`: If this is the case, it means that the created function is an aggregate function.
 >
->`Function_name`: To create the name of the function, you can include the name of the database. For example: `db1.my_func'.
->
->` arg_type': The parameter type of the function is the same as the type defined at the time of table building. Variable-length parameters can be represented by `,...`. If it is a variable-length type, the type of the variable-length part of the parameters is the same as the last non-variable-length parameter type.
+> `ALIAS`: If this is the case, it means that the created function is an alias function.
+> 
+> If the above two items are not present, it means that the created function is a scalar function.
+> 
+> `Function_name`: To create the name of the function, you can include the name of the database. For example: `db1.my_func'.
 >
->`ret_type`: Function return type.
+> `arg_type`: The parameter type of the function is the same as the type defined at the time of table building. Variable-length parameters can be represented by `,...`. If it is a variable-length type, the type of the variable-length part of the parameters is the same as the last non-variable-length parameter type.
+> **NOTICE**: `ALIAS FUNCTION` variable-length parameters are not supported, and there is at least one parameter.
+> 
+> `ret_type`: Required for creating a new function. This parameter is not required if you are aliasing an existing function.
 >
->`Inter_type`: A data type used to represent the intermediate stage of an aggregate function.
+> `inter_type`: A data type used to represent the intermediate stage of an aggregate function.
+> 
+> `param`: The parameter used to represent the alias function, containing at least one.
+> 
+> `origin_function`: Used to represent the original function corresponding to the alias function.
 >
->`properties`: Used to set properties related to this function. Properties that can be set include
+> `properties`: Used to set properties related to aggregate function and scalar function. Properties that can be set include
 >
-> "Object_file": Custom function dynamic library URL path, currently only supports HTTP/HTTPS protocol, this path needs to remain valid throughout the life cycle of the function. This option is mandatory
+>           "Object_file": Custom function dynamic library URL path, currently only supports HTTP/HTTPS protocol, this path needs to remain valid throughout the life cycle of the function. This option is mandatory
 >
-> "symbol": Function signature of scalar functions for finding function entries from dynamic libraries. This option is mandatory for scalar functions
+>           "symbol": Function signature of scalar functions for finding function entries from dynamic libraries. This option is mandatory for scalar functions
 >
-> "init_fn": Initialization function signature of aggregate function. Necessary for aggregation functions
+>           "init_fn": Initialization function signature of aggregate function. Necessary for aggregation functions
 >
-> "update_fn": Update function signature of aggregate function. Necessary for aggregation functions
+>           "update_fn": Update function signature of aggregate function. Necessary for aggregation functions
 >
-> "merge_fn": Merge function signature of aggregate function. Necessary for aggregation functions
+>           "merge_fn": Merge function signature of aggregate function. Necessary for aggregation functions
 >
-> "serialize_fn": Serialized function signature of aggregate function. For aggregation functions, it is optional, and if not specified, the default serialization function will be used
+>           "serialize_fn": Serialized function signature of aggregate function. For aggregation functions, it is optional, and if not specified, the default serialization function will be used
 >
-> "finalize_fn": A function signature that aggregates functions to obtain the final result. For aggregation functions, it is optional. If not specified, the default fetch result function will be used.
+>           "finalize_fn": A function signature that aggregates functions to obtain the final result. For aggregation functions, it is optional. If not specified, the default fetch result function will be used.
 >
-> "md5": The MD5 value of the function dynamic link library, which is used to verify that the downloaded content is correct. This option is optional
+>           "md5": The MD5 value of the function dynamic link library, which is used to verify that the downloaded content is correct. This option is optional
 >
-> "prepare_fn": Function signature of the prepare function for finding the entry from the dynamic library. This option is optional for custom functions
+>           "prepare_fn": Function signature of the prepare function for finding the entry from the dynamic library. This option is optional for custom functions
 > 
-> "close_fn": Function signature of the close function for finding the entry from the dynamic library. This option is optional for custom functions
+>           "close_fn": Function signature of the close function for finding the entry from the dynamic library. This option is optional for custom functions
 
 
 This statement creates a custom function. Executing this command requires that the user have `ADMIN` privileges.
@@ -100,12 +110,29 @@ If the `function_name` contains the database name, the custom function will be c
 	
 	```
 	CREATE AGGREGATE FUNCTION my_count (BIGINT) RETURNS BIGINT PROPERTIES (
-		"init_fn"= "_ZN9doris_udf6AddUdfEPNS_15FunctionContextERKNS_6IntValES4_",
-		"update_fn" = "zn9dorisudf11CountupdateepnsFunctionContexterknsIntvalepnsbigintvale",
-		"merge_fn" = "zn9dorisudf10CountMergeepnsFunctionContexterknsBigintvaleps2
-		"finalize_fn" = "zn9dorisudf13CountFinalizepnsFunctionContexterknsBigintvale",
-		"object_file" = "http://host:port/libudasample.so"
+	    "init_fn"="_ZN9doris_udf9CountInitEPNS_15FunctionContextEPNS_9BigIntValE",
+	    "update_fn"="_ZN9doris_udf11CountUpdateEPNS_15FunctionContextERKNS_6IntValEPNS_9BigIntValE",
+	    "merge_fn"="_ZN9doris_udf10CountMergeEPNS_15FunctionContextERKNS_9BigIntValEPS2_",
+	    "finalize_fn"="_ZN9doris_udf13CountFinalizeEPNS_15FunctionContextERKNS_9BigIntValE",
+	    "object_file"="http://host:port/libudasample.so"
 	);
 	```
+
+4.  Create a scalar function with variable length parameters
+
+    ```
+    CREATE FUNCTION strconcat(varchar, ...) RETURNS varchar properties (
+        "symbol" = "_ZN9doris_udf6StrConcatUdfEPNS_15FunctionContextERKNS_6IntValES4_",
+        "object_file" = "http://host:port/libmyStrConcat.so"
+    );
+    ```
+
+5. Create a custom alias function
+
+    ```
+    CREATE ALIAS FUNCTION id_masking(INT) WITH PARAMETER(id) 
+        AS CONCAT(LEFT(id, 3), '****', RIGHT(id, 4));
+    ```
+
 ## keyword
 CREATE,FUNCTION
diff --git a/docs/en/sql-reference/sql-statements/Data Definition/show-functions.md b/docs/en/sql-reference/sql-statements/Data Definition/show-functions.md
index c53878c..b942046 100644
--- a/docs/en/sql-reference/sql-statements/Data Definition/show-functions.md	
+++ b/docs/en/sql-reference/sql-statements/Data Definition/show-functions.md	
@@ -59,8 +59,14 @@ Intermediate Type: NULL
     Function Type: Aggregate
 Intermediate Type: NULL
        Properties: {"object_file":"http://host:port/libudasample.so","finalize_fn":"_ZN9doris_udf13CountFinalizeEPNS_15FunctionContextERKNS_9BigIntValE","init_fn":"_ZN9doris_udf9CountInitEPNS_15FunctionContextEPNS_9BigIntValE","merge_fn":"_ZN9doris_udf10CountMergeEPNS_15FunctionContextERKNS_9BigIntValEPS2_","md5":"37d185f80f95569e2676da3d5b5b9d2f","update_fn":"_ZN9doris_udf11CountUpdateEPNS_15FunctionContextERKNS_6IntValEPNS_9BigIntValE"}
+*************************** 3. row ***************************
+        Signature: id_masking(INT)
+      Return Type: VARCHAR
+    Function Type: Alias
+Intermediate Type: NULL
+       Properties: {"parameter":"id","origin_function":"concat(left(`id`, 3), `****`, right(`id`, 4))"}
 
-2 rows in set (0.00 sec)
+3 rows in set (0.00 sec)
 mysql> show builtin functions in testDb like 'year%';
 +---------------+
 | Function Name |
diff --git a/docs/zh-CN/sql-reference/sql-functions/digital-masking.md b/docs/zh-CN/sql-reference/sql-functions/digital-masking.md
new file mode 100644
index 0000000..32ea5a3
--- /dev/null
+++ b/docs/zh-CN/sql-reference/sql-functions/digital-masking.md
@@ -0,0 +1,56 @@
+---
+{
+    "title": "DIGITAL-MASKING",
+    "language": "zh-CN"
+}
+---
+
+<!-- 
+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.
+-->
+
+# DIGITAL_MASKING
+
+## description
+
+### Syntax
+
+```
+digital_masking(digital_number)
+```
+
+别名函数,原始函数为 `concat(left(id,3),'****',right(id,4))`。
+
+将输入的 `digital_number` 进行脱敏处理,返回遮盖脱敏后的结果。
+
+## example
+
+1. 将手机号码进行脱敏处理
+
+    ```sql
+    mysql> select digital_masking(13812345678);
+    +------------------------------+
+    | digital_masking(13812345678) |
+    +------------------------------+
+    | 138****5678                  |
+    +------------------------------+
+    ```
+
+## keyword
+
+DIGITAL_MASKING
diff --git a/docs/zh-CN/sql-reference/sql-statements/Data Definition/create-function.md b/docs/zh-CN/sql-reference/sql-statements/Data Definition/create-function.md
index 0c3f3b7..8582626 100644
--- a/docs/zh-CN/sql-reference/sql-statements/Data Definition/create-function.md	
+++ b/docs/zh-CN/sql-reference/sql-statements/Data Definition/create-function.md	
@@ -25,30 +25,40 @@ under the License.
 -->
 
 # CREATE FUNCTION
-## description
+## Description
 ### Syntax
 
 ```
-CREATE [AGGREGATE] FUNCTION function_name
+CREATE [AGGREGATE] [ALIAS] FUNCTION function_name
     (arg_type [, ...])
-    RETURNS ret_type
+    [RETURNS ret_type]
     [INTERMEDIATE inter_type]
+    [WITH PARAMETER(param [,...]) AS origin_function]
     [PROPERTIES ("key" = "value" [, ...]) ]
 ```
 
 ### Parameters
 
-> `AGGREGATE`: 如果有此项,表示的是创建的函数是一个聚合函数,否则创建的是一个标量函数。
->
+> `AGGREGATE`: 如果有此项,表示的是创建的函数是一个聚合函数。
+> 
+> `ALIAS`:如果有此项,表示的是创建的函数是一个别名函数。
+> 
+> 如果没有上述两项,表示创建的函数是一个标量函数
+> 
 > `function_name`: 要创建函数的名字, 可以包含数据库的名字。比如:`db1.my_func`。
 >
 > `arg_type`: 函数的参数类型,与建表时定义的类型一致。变长参数时可以使用`, ...`来表示,如果是变长类型,那么变长部分参数的类型与最后一个非变长参数类型一致。
+> **注意**:`ALIAS FUNCTION` 不支持变长参数,且至少有一个参数。
 > 
-> `ret_type`: 函数返回类型。
+> `ret_type`: 对创建新的函数来说,是必填项。如果是给已有函数取别名则可不用填写该参数。
 > 
 > `inter_type`: 用于表示聚合函数中间阶段的数据类型。
 > 
-> `properties`: 用于设定此函数相关属性,能够设置的属性包括
+> `param`:用于表示别名函数的参数,至少包含一个。
+> 
+> `origin_function`:用于表示别名函数对应的原始函数。
+> 
+> `properties`: 用于设定聚合函数和标量函数相关属性,能够设置的属性包括
 >       
 >           "object_file": 自定义函数动态库的URL路径,当前只支持 HTTP/HTTPS 协议,此路径需要在函数整个生命周期内保持有效。此选项为必选项
 >
@@ -97,7 +107,7 @@ CREATE [AGGREGATE] FUNCTION function_name
 	);
 	```
 
-2. 创建一个自定义聚合函数
+3. 创建一个自定义聚合函数
 
 	```
 	CREATE AGGREGATE FUNCTION my_count (BIGINT) RETURNS BIGINT PROPERTIES (
@@ -109,14 +119,21 @@ CREATE [AGGREGATE] FUNCTION function_name
 	);
 	```
 
-3. 创建一个变长参数的标量函数
+4. 创建一个变长参数的标量函数
+
+    ```
+    CREATE FUNCTION strconcat(varchar, ...) RETURNS varchar properties (
+        "symbol" = "_ZN9doris_udf6StrConcatUdfEPNS_15FunctionContextERKNS_6IntValES4_",
+        "object_file" = "http://host:port/libmyStrConcat.so"
+    );
+    ```
+
+5. 创建一个自定义别名函数
 
-       ```
-       CREATE FUNCTION strconcat(varchar, ...) RETURNS varchar properties (
-           "symbol" = "_ZN9doris_udf6StrConcatUdfEPNS_15FunctionContextERKNS_6IntValES4_",
-           "object_file" = "http://host:port/libmyStrConcat.so"
-       );
-       ```
+    ```
+    CREATE ALIAS FUNCTION id_masking(INT) WITH PARAMETER(id) 
+        AS CONCAT(LEFT(id, 3), '****', RIGHT(id, 4));
+    ```
 
 ## keyword
 
diff --git a/docs/zh-CN/sql-reference/sql-statements/Data Definition/show-functions.md b/docs/zh-CN/sql-reference/sql-statements/Data Definition/show-functions.md
index b8808ba..1220de2 100644
--- a/docs/zh-CN/sql-reference/sql-statements/Data Definition/show-functions.md	
+++ b/docs/zh-CN/sql-reference/sql-statements/Data Definition/show-functions.md	
@@ -60,8 +60,14 @@ Intermediate Type: NULL
     Function Type: Aggregate
 Intermediate Type: NULL
        Properties: {"object_file":"http://host:port/libudasample.so","finalize_fn":"_ZN9doris_udf13CountFinalizeEPNS_15FunctionContextERKNS_9BigIntValE","init_fn":"_ZN9doris_udf9CountInitEPNS_15FunctionContextEPNS_9BigIntValE","merge_fn":"_ZN9doris_udf10CountMergeEPNS_15FunctionContextERKNS_9BigIntValEPS2_","md5":"37d185f80f95569e2676da3d5b5b9d2f","update_fn":"_ZN9doris_udf11CountUpdateEPNS_15FunctionContextERKNS_6IntValEPNS_9BigIntValE"}
+*************************** 3. row ***************************
+        Signature: id_masking(INT)
+      Return Type: VARCHAR
+    Function Type: Alias
+Intermediate Type: NULL
+       Properties: {"parameter":"id","origin_function":"concat(left(`id`, 3), `****`, right(`id`, 4))"}
 
-2 rows in set (0.00 sec)
+3 rows in set (0.00 sec)
 mysql> show builtin functions in testDb like 'year%';
 +---------------+
 | Function Name |
diff --git a/fe/fe-core/src/main/cup/sql_parser.cup b/fe/fe-core/src/main/cup/sql_parser.cup
index f71647a..0b21b71 100644
--- a/fe/fe-core/src/main/cup/sql_parser.cup
+++ b/fe/fe-core/src/main/cup/sql_parser.cup
@@ -233,7 +233,7 @@ parser code {:
 :};
 
 // Total keywords of doris
-terminal String KW_ADD, KW_ADMIN, KW_AFTER, KW_AGGREGATE, KW_ALL, KW_ALTER, KW_AND, KW_ANTI, KW_APPEND, KW_AS, KW_ASC, KW_AUTHORS, KW_ARRAY,
+terminal String KW_ADD, KW_ADMIN, KW_AFTER, KW_AGGREGATE, KW_ALIAS, KW_ALL, KW_ALTER, KW_AND, KW_ANTI, KW_APPEND, KW_AS, KW_ASC, KW_AUTHORS, KW_ARRAY,
     KW_BACKEND, KW_BACKUP, KW_BETWEEN, KW_BEGIN, KW_BIGINT, KW_BITMAP, KW_BITMAP_UNION, KW_BOOLEAN, KW_BROKER, KW_BACKENDS, KW_BY, KW_BUILTIN,
     KW_CANCEL, KW_CASE, KW_CAST, KW_CHAIN, KW_CHAR, KW_CHARSET, KW_CHECK, KW_CLUSTER, KW_CLUSTERS,
     KW_COLLATE, KW_COLLATION, KW_COLUMN, KW_COLON, KW_COLUMNS, KW_COMMENT, KW_COMMIT, KW_COMMITTED,
@@ -254,7 +254,7 @@ terminal String KW_ADD, KW_ADMIN, KW_AFTER, KW_AGGREGATE, KW_ALL, KW_ALTER, KW_A
     KW_MAP, KW_MATERIALIZED, KW_MAX, KW_MAX_VALUE, KW_MERGE, KW_MIN, KW_MINUTE, KW_MINUS, KW_MIGRATE, KW_MIGRATIONS, KW_MODIFY, KW_MONTH,
     KW_NAME, KW_NAMED_STRUCT, KW_NAMES, KW_NEGATIVE, KW_NO, KW_NOT, KW_NULL, KW_NULLS,
     KW_OBSERVER, KW_OFFSET, KW_ON, KW_ONLY, KW_OPEN, KW_OR, KW_ORDER, KW_OUTER, KW_OUTFILE, KW_OVER,
-    KW_PARTITION, KW_PARTITIONS, KW_PASSWORD, KW_LDAP_ADMIN_PASSWORD, KW_PATH, KW_PAUSE, KW_PIPE, KW_PRECEDING,
+    KW_PARAMETER, KW_PARTITION, KW_PARTITIONS, KW_PASSWORD, KW_LDAP_ADMIN_PASSWORD, KW_PATH, KW_PAUSE, KW_PIPE, KW_PRECEDING,
     KW_PLUGIN, KW_PLUGINS,
     KW_PROC, KW_PROCEDURE, KW_PROCESSLIST, KW_PROFILE, KW_PROPERTIES, KW_PROPERTY,
     KW_QUERY, KW_QUOTA,
@@ -1145,6 +1145,11 @@ create_stmt ::=
     {:
         RESULT = new CreateFunctionStmt(isAggregate, functionName, args, returnType, intermediateType, properties);
     :}
+    | KW_CREATE KW_ALIAS KW_FUNCTION function_name:functionName LPAREN func_args_def:args RPAREN
+            KW_WITH KW_PARAMETER LPAREN ident_list:parameters RPAREN KW_AS expr:func
+    {:
+        RESULT = new CreateFunctionStmt(functionName, args, parameters, func);
+    :}
     /* Table */
     | KW_CREATE opt_external:isExternal KW_TABLE opt_if_not_exists:ifNotExists table_name:name KW_LIKE table_name:existed_name
     {:
@@ -4935,6 +4940,8 @@ keyword ::=
     {: RESULT = id; :}
     | KW_AGGREGATE:id
     {: RESULT = id; :}
+    | KW_ALIAS:id
+    {: RESULT = id; :}
     | KW_AUTHORS:id
     {: RESULT = id; :}
     | KW_ARRAY:id
@@ -5083,6 +5090,8 @@ keyword ::=
     {: RESULT = id; :}
     | KW_OPEN:id
     {: RESULT = id; :}
+    | KW_PARAMETER:id
+    {: RESULT = id; :}
     | KW_PARTITIONS:id
     {: RESULT = id; :}
     | KW_PASSWORD:id
diff --git a/fe/fe-core/src/main/java/org/apache/doris/analysis/Analyzer.java b/fe/fe-core/src/main/java/org/apache/doris/analysis/Analyzer.java
index 7c7888c..48b147a 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/analysis/Analyzer.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/Analyzer.java
@@ -41,6 +41,7 @@ import org.apache.doris.rewrite.ExprRewriteRule;
 import org.apache.doris.rewrite.ExprRewriter;
 import org.apache.doris.rewrite.ExtractCommonFactorsRule;
 import org.apache.doris.rewrite.FoldConstantsRule;
+import org.apache.doris.rewrite.RewriteAliasFunctionRule;
 import org.apache.doris.rewrite.RewriteEncryptKeyRule;
 import org.apache.doris.rewrite.RewriteFromUnixTimeRule;
 import org.apache.doris.rewrite.NormalizeBinaryPredicatesRule;
@@ -270,6 +271,7 @@ public class Analyzer {
             rules.add(RewriteFromUnixTimeRule.INSTANCE);
             rules.add(SimplifyInvalidDateBinaryPredicatesDateRule.INSTANCE);
             rules.add(RewriteEncryptKeyRule.INSTANCE);
+            rules.add(RewriteAliasFunctionRule.INSTANCE);
             List<ExprRewriteRule> onceRules = Lists.newArrayList();
             onceRules.add(ExtractCommonFactorsRule.INSTANCE);
             exprRewriter_ = new ExprRewriter(rules, onceRules);
diff --git a/fe/fe-core/src/main/java/org/apache/doris/analysis/CreateFunctionStmt.java b/fe/fe-core/src/main/java/org/apache/doris/analysis/CreateFunctionStmt.java
index 2a4f7ab..6e376ad 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/analysis/CreateFunctionStmt.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/CreateFunctionStmt.java
@@ -18,9 +18,11 @@
 package org.apache.doris.analysis;
 
 import org.apache.doris.catalog.AggregateFunction;
+import org.apache.doris.catalog.AliasFunction;
 import org.apache.doris.catalog.Catalog;
 import org.apache.doris.catalog.Function;
 import org.apache.doris.catalog.ScalarFunction;
+import org.apache.doris.catalog.Type;
 import org.apache.doris.common.AnalysisException;
 import org.apache.doris.common.ErrorCode;
 import org.apache.doris.common.ErrorReport;
@@ -31,6 +33,7 @@ import org.apache.doris.mysql.privilege.PrivPredicate;
 import org.apache.doris.qe.ConnectContext;
 
 import com.google.common.base.Strings;
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSortedMap;
 
 import org.apache.commons.codec.binary.Hex;
@@ -39,6 +42,7 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
+import java.util.List;
 import java.util.Map;
 
 // create a user define function
@@ -58,10 +62,13 @@ public class CreateFunctionStmt extends DdlStmt {
 
     private final FunctionName functionName;
     private final boolean isAggregate;
+    private final boolean isAlias;
     private final FunctionArgsDef argsDef;
     private final TypeDef returnType;
     private TypeDef intermediateType;
     private final Map<String, String> properties;
+    private final List<String> parameters;
+    private final Expr originFunction;
 
     // needed item set after analyzed
     private String objectFile;
@@ -83,11 +90,34 @@ public class CreateFunctionStmt extends DdlStmt {
         } else {
             this.properties = ImmutableSortedMap.copyOf(properties, String.CASE_INSENSITIVE_ORDER);
         }
+        this.isAlias = false;
+        this.parameters = ImmutableList.of();
+        this.originFunction = null;
+    }
+
+    public CreateFunctionStmt(FunctionName functionName, FunctionArgsDef argsDef,
+                              List<String> parameters, Expr originFunction) {
+        this.functionName = functionName;
+        this.isAlias = true;
+        this.argsDef = argsDef;
+        if (parameters == null) {
+            this.parameters = ImmutableList.of();
+        } else {
+            this.parameters = ImmutableList.copyOf(parameters);
+        }
+        this.originFunction = originFunction;
+        this.isAggregate = false;
+        this.returnType = new TypeDef(Type.VARCHAR);
+        this.properties = ImmutableSortedMap.of();
     }
 
     public FunctionName getFunctionName() { return functionName; }
     public Function getFunction() { return function; }
 
+    public Expr getOriginFunction() {
+        return originFunction;
+    }
+
     @Override
     public void analyze(Analyzer analyzer) throws UserException {
         super.analyze(analyzer);
@@ -96,6 +126,8 @@ public class CreateFunctionStmt extends DdlStmt {
         // check
         if (isAggregate) {
             analyzeUda();
+        } else if (isAlias) {
+            analyzeAliasFunction();
         } else {
             analyzeUdf();
         }
@@ -112,6 +144,11 @@ public class CreateFunctionStmt extends DdlStmt {
         // check argument
         argsDef.analyze(analyzer);
 
+        // alias function does not need analyze following params
+        if (isAlias) {
+            return;
+        }
+
         returnType.analyze(analyzer);
         if (intermediateType != null) {
             intermediateType.analyze(analyzer);
@@ -197,18 +234,34 @@ public class CreateFunctionStmt extends DdlStmt {
         function.setChecksum(checksum);
     }
 
+    private void analyzeAliasFunction() throws AnalysisException {
+        function = AliasFunction.createFunction(functionName, argsDef.getArgTypes(),
+                Type.VARCHAR, argsDef.isVariadic(), parameters, originFunction);
+        ((AliasFunction) function).analyze();
+    }
+
     @Override
     public String toSql() {
         StringBuilder stringBuilder = new StringBuilder();
         stringBuilder.append("CREATE ");
         if (isAggregate) {
             stringBuilder.append("AGGREGATE ");
+        } else if (isAlias) {
+            stringBuilder.append("ALIAS ");
         }
+
         stringBuilder.append("FUNCTION ");
         stringBuilder.append(functionName.toString());
         stringBuilder.append(argsDef.toSql());
-        stringBuilder.append(" RETURNS ");
-        stringBuilder.append(returnType.toString());
+        if (isAlias) {
+            stringBuilder.append(" WITH PARAMETER (")
+                    .append(parameters.toString())
+                    .append(") AS ")
+                    .append(originFunction.toSql());
+        } else {
+            stringBuilder.append(" RETURNS ");
+            stringBuilder.append(returnType.toString());
+        }
         if (properties.size() > 0) {
             stringBuilder.append(" PROPERTIES (");
             int i = 0;
diff --git a/fe/fe-core/src/main/java/org/apache/doris/analysis/FunctionCallExpr.java b/fe/fe-core/src/main/java/org/apache/doris/analysis/FunctionCallExpr.java
index a04cdb1..bda3af9 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/analysis/FunctionCallExpr.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/FunctionCallExpr.java
@@ -19,6 +19,7 @@ package org.apache.doris.analysis;
 
 import org.apache.doris.catalog.AggregateFunction;
 import org.apache.doris.catalog.ArrayType;
+import org.apache.doris.catalog.AliasFunction;
 import org.apache.doris.catalog.Catalog;
 import org.apache.doris.catalog.Database;
 import org.apache.doris.catalog.Function;
@@ -71,6 +72,11 @@ public class FunctionCallExpr extends Expr {
                     .add("stddev").add("stddev_val").add("stddev_samp")
                     .add("variance").add("variance_pop").add("variance_pop").add("var_samp").add("var_pop").build();
     private static final String ELEMENT_EXTRACT_FN_NAME = "%element_extract%";
+    
+    // Save the functionCallExpr in the original statement
+    private Expr originStmtFnExpr;
+
+    private boolean isRewrote = false;
 
     public void setIsAnalyticFnCall(boolean v) {
         isAnalyticFnCall = v;
@@ -84,6 +90,10 @@ public class FunctionCallExpr extends Expr {
         return fnName;
     }
 
+    public FunctionParams getFnParams() {
+        return fnParams;
+    }
+
     // only used restore from readFields.
     private FunctionCallExpr() {
         super();
@@ -184,15 +194,21 @@ public class FunctionCallExpr extends Expr {
 
     @Override
     public String toSqlImpl() {
+        Expr expr;
+        if (originStmtFnExpr != null) {
+            expr = originStmtFnExpr;
+        } else {
+            expr = this;
+        }
         StringBuilder sb = new StringBuilder();
-        sb.append(fnName).append("(");
-        if (fnParams.isStar()) {
+        sb.append(((FunctionCallExpr) expr).fnName).append("(");
+        if (((FunctionCallExpr) expr).fnParams.isStar()) {
             sb.append("*");
         }
-        if (fnParams.isDistinct()) {
+        if (((FunctionCallExpr) expr).fnParams.isDistinct()) {
             sb.append("DISTINCT ");
         }
-        sb.append(Joiner.on(", ").join(childrenToSql())).append(")");
+        sb.append(Joiner.on(", ").join(expr.childrenToSql())).append(")");
         return sb.toString();
     }
 
@@ -729,6 +745,75 @@ public class FunctionCallExpr extends Expr {
         }
     }
 
+    /**
+     * rewrite alias function to real function
+     * reset function name, function params and it's children to real function's
+     * @return
+     * @throws AnalysisException
+     */
+    public Expr rewriteExpr() throws AnalysisException {
+        if (isRewrote) {
+            return this;
+        }
+        // clone a new functionCallExpr to rewrite
+        FunctionCallExpr retExpr = (FunctionCallExpr) clone();
+        // clone origin function call expr in origin stmt
+        retExpr.originStmtFnExpr = clone();
+        // clone alias function origin expr for alias
+        FunctionCallExpr oriExpr = (FunctionCallExpr) ((AliasFunction) retExpr.fn).getOriginFunction().clone();
+        // reset fn name
+        retExpr.fnName = oriExpr.getFnName();
+        // reset fn params
+        List<Expr> inputParamsExprs = retExpr.fnParams.exprs();
+        List<String> parameters = ((AliasFunction) retExpr.fn).getParameters();
+        Preconditions.checkArgument(inputParamsExprs.size() == parameters.size(),
+                "Alias function [" + retExpr.fn.getFunctionName().getFunction() + "] args number is not equal to it's definition");
+        List<Expr> oriParamsExprs = oriExpr.fnParams.exprs();
+
+        // replace origin function params exprs' with input params expr depending on parameter name
+        for (int i = 0; i < oriParamsExprs.size(); i++) {
+            Expr expr = replaceParams(parameters, inputParamsExprs, oriParamsExprs.get(i));
+            oriParamsExprs.set(i, expr);
+        }
+
+        retExpr.fnParams = new FunctionParams(oriExpr.fnParams.isDistinct(), oriParamsExprs);
+
+        // reset children
+        retExpr.children.clear();
+        retExpr.children.addAll(oriExpr.getChildren());
+        retExpr.isRewrote = true;
+        return retExpr;
+    }
+
+    /**
+     * replace origin function expr and it's children with input params exprs depending on parameter name
+     * @param parameters
+     * @param inputParamsExprs
+     * @param oriExpr
+     * @return
+     * @throws AnalysisException
+     */
+    private Expr replaceParams(List<String> parameters, List<Expr> inputParamsExprs, Expr oriExpr) throws AnalysisException {
+        for (int i = 0; i < oriExpr.getChildren().size(); i++) {
+            Expr retExpr = replaceParams(parameters, inputParamsExprs, oriExpr.getChild(i));
+            oriExpr.setChild(i, retExpr);
+        }
+        if (oriExpr instanceof SlotRef) {
+            String columnName = ((SlotRef) oriExpr).getColumnName();
+            int index = parameters.indexOf(columnName);
+            if (index != -1) {
+                return inputParamsExprs.get(index);
+            }
+        }
+        // Initialize literalExpr without type information, because literalExpr does not save type information
+        // when it is persisted, so after fe restart, read the image,
+        // it will be missing type and report an error during analyze.
+        if (oriExpr instanceof LiteralExpr && oriExpr.getType().equals(Type.INVALID)) {
+            oriExpr = LiteralExpr.init((LiteralExpr) oriExpr);
+        }
+        return oriExpr;
+    }
+
     @Override
     public boolean isVectorized() {
         return false;
diff --git a/fe/fe-core/src/main/java/org/apache/doris/analysis/LiteralExpr.java b/fe/fe-core/src/main/java/org/apache/doris/analysis/LiteralExpr.java
index f6ab8f6..6a8622d 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/analysis/LiteralExpr.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/LiteralExpr.java
@@ -86,6 +86,41 @@ public abstract class LiteralExpr extends Expr implements Comparable<LiteralExpr
         return literalExpr;
     }
 
+    /**
+     * Init LiteralExpr's Type information
+     * only use in rewrite alias function
+     * @param expr
+     * @return
+     * @throws AnalysisException
+     */
+    public static LiteralExpr init(LiteralExpr expr) throws AnalysisException {
+        Preconditions.checkArgument(expr.getType().equals(Type.INVALID));
+        String value = expr.getStringValue();
+        LiteralExpr literalExpr = null;
+        if (expr instanceof NullLiteral) {
+            literalExpr = new NullLiteral();
+        } else if (expr instanceof BoolLiteral) {
+            literalExpr = new BoolLiteral(value);
+        } else if (expr instanceof IntLiteral) {
+            literalExpr = new IntLiteral(Long.parseLong(value));
+        } else if (expr instanceof LargeIntLiteral) {
+            literalExpr = new LargeIntLiteral(value);
+        } else if (expr instanceof FloatLiteral) {
+            literalExpr = new FloatLiteral(value);
+        } else if (expr instanceof DecimalLiteral) {
+            literalExpr = new DecimalLiteral(value);
+        } else if (expr instanceof StringLiteral) {
+            literalExpr = new StringLiteral(value);
+        } else if (expr instanceof DateLiteral) {
+            literalExpr = new DateLiteral(value, expr.getType());
+        } else {
+            throw new AnalysisException("Type[" + expr.getType().toSql() + "] not supported.");
+        }
+
+        Preconditions.checkNotNull(literalExpr);
+        return literalExpr;
+    }
+
     public static LiteralExpr createInfinity(Type type, boolean isMax) throws AnalysisException {
         Preconditions.checkArgument(!type.equals(Type.INVALID));
         if (isMax) {
diff --git a/fe/fe-core/src/main/java/org/apache/doris/catalog/AliasFunction.java b/fe/fe-core/src/main/java/org/apache/doris/catalog/AliasFunction.java
new file mode 100644
index 0000000..6646eaf
--- /dev/null
+++ b/fe/fe-core/src/main/java/org/apache/doris/catalog/AliasFunction.java
@@ -0,0 +1,258 @@
+// 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.doris.catalog;
+
+import org.apache.doris.analysis.Expr;
+import org.apache.doris.analysis.FunctionCallExpr;
+import org.apache.doris.analysis.FunctionName;
+import org.apache.doris.analysis.SelectStmt;
+import org.apache.doris.analysis.SlotRef;
+import org.apache.doris.analysis.SqlParser;
+import org.apache.doris.analysis.SqlScanner;
+import org.apache.doris.common.AnalysisException;
+import org.apache.doris.common.io.Text;
+import org.apache.doris.common.util.SqlParserUtils;
+import org.apache.doris.qe.SqlModeHelper;
+import org.apache.doris.thrift.TFunctionBinaryType;
+
+import com.google.common.collect.Lists;
+import com.google.gson.Gson;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import java.io.StringReader;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * Internal representation of an alias function.
+ */
+public class AliasFunction extends Function {
+    private static final Logger LOG = LogManager.getLogger(AliasFunction.class);
+
+    private static final String DIGITAL_MASKING = "digital_masking";
+
+    private Expr originFunction;
+    private List<String> parameters = new ArrayList<>();
+
+    // Only used for serialization
+    protected AliasFunction() {
+    }
+
+    public AliasFunction(FunctionName fnName, Type[] argTypes, Type retType, boolean hasVarArgs) {
+        super(fnName, argTypes, retType, hasVarArgs);
+    }
+
+    public AliasFunction(FunctionName fnName, ArrayList<Type> argTypes, Type retType, boolean hasVarArgs) {
+        super(fnName, argTypes, retType, hasVarArgs);
+    }
+
+    public static AliasFunction createFunction(FunctionName functionName, Type[] argTypes, Type retType,
+                                               boolean hasVarArgs, List<String> parameters, Expr originFunction) {
+        AliasFunction aliasFunction = new AliasFunction(functionName, argTypes, retType, hasVarArgs);
+        aliasFunction.setBinaryType(TFunctionBinaryType.NATIVE);
+        aliasFunction.setUserVisible(true);
+        aliasFunction.originFunction = originFunction;
+        aliasFunction.parameters = parameters;
+        return aliasFunction;
+    }
+
+    public static void initBuiltins(FunctionSet functionSet) {
+        String oriStmt = "select concat(left(id,3),'****',right(id,4));";
+        try {
+            /**
+             * Please ensure that the condition checks in {@link #analyze} are satisfied
+             */
+            functionSet.addBuiltin(createBuiltin(DIGITAL_MASKING, Lists.newArrayList(Type.INT), Type.VARCHAR,
+                    false, Lists.newArrayList("id"), getExpr(oriStmt), true));
+        } catch (AnalysisException e) {
+            LOG.error("Add builtin alias function error {}", e);
+        }
+    }
+
+    public static Expr getExpr(String sql) throws AnalysisException {
+        SelectStmt parsedStmt;
+        // Parse statement with parser generated by CUP&FLEX
+        SqlScanner input = new SqlScanner(new StringReader(sql), SqlModeHelper.MODE_DEFAULT);
+        SqlParser parser = new SqlParser(input);
+        try {
+            parsedStmt = (SelectStmt) SqlParserUtils.getFirstStmt(parser);
+        } catch (Error e) {
+            LOG.info("error happened when parsing stmt {}", sql, e);
+            throw new AnalysisException("sql parsing error, please check your sql");
+        } catch (AnalysisException e) {
+            String syntaxError = parser.getErrorMsg(sql);
+            LOG.info("analysis exception happened when parsing stmt {}, error: {}",
+                    sql, syntaxError, e);
+            if (syntaxError == null) {
+                throw  e;
+            } else {
+                throw new AnalysisException(syntaxError, e);
+            }
+        } catch (Exception e) {
+            // TODO(lingbin): we catch 'Exception' to prevent unexpected error,
+            // should be removed this try-catch clause future.
+            LOG.info("unexpected exception happened when parsing stmt {}, error: {}",
+                    sql, parser.getErrorMsg(sql), e);
+            throw new AnalysisException("Unexpected exception: " + e.getMessage());
+        }
+
+        return parsedStmt.getSelectList().getItems().get(0).getExpr();
+    }
+
+    private static AliasFunction createBuiltin(String name, ArrayList<Type> argTypes, Type retType,
+                                              boolean hasVarArgs, List<String> parameters, Expr originFunction,
+                                              boolean userVisible) {
+        AliasFunction aliasFunction = new AliasFunction(new FunctionName(name), argTypes, retType, hasVarArgs);
+        aliasFunction.setBinaryType(TFunctionBinaryType.BUILTIN);
+        aliasFunction.setUserVisible(userVisible);
+        aliasFunction.originFunction = originFunction;
+        aliasFunction.parameters = parameters;
+        return aliasFunction;
+    }
+
+    public Expr getOriginFunction() {
+        return originFunction;
+    }
+
+    public void setOriginFunction(Expr originFunction) {
+        this.originFunction = originFunction;
+    }
+
+    public List<String> getParameters() {
+        return parameters;
+    }
+
+    public void setParameters(List<String> parameters) {
+        this.parameters = parameters;
+    }
+
+    public void analyze() throws AnalysisException {
+        if (parameters.size() != getArgs().length) {
+            throw new AnalysisException("Alias function [" + functionName() + "] args number is not equal to parameters number");
+        }
+        List<Expr> exprs = ((FunctionCallExpr) originFunction).getFnParams().exprs();
+        Set<String> set = new HashSet<>();
+        for (String str : parameters) {
+            if (!set.add(str)) {
+                throw new AnalysisException("Alias function [" + functionName() + "] has duplicate parameter [" + str + "].");
+            }
+            boolean existFlag = false;
+            for (Expr expr : exprs) {
+                existFlag |= checkParams(expr, str);
+            }
+            if (!existFlag) {
+                throw new AnalysisException("Alias function [" + functionName() + "]  do not contain parameter [" + str + "].");
+            }
+        }
+    }
+
+    private boolean checkParams(Expr expr, String parma) {
+        for (Expr e : expr.getChildren()) {
+            if (checkParams(e, parma)) {
+                return true;
+            }
+        }
+        if (expr instanceof SlotRef) {
+            if (parma.equals(((SlotRef) expr).getColumnName())) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public String toSql(boolean ifNotExists) {
+        setSlotRefLabel(originFunction);
+        StringBuilder sb = new StringBuilder("CREATE ALIAS FUNCTION ");
+        if (ifNotExists) {
+            sb.append("IF NOT EXISTS ");
+        }
+        sb.append(signatureString())
+            .append(" WITH PARAMETER(")
+            .append(getParamsSting(parameters))
+            .append(") AS ")
+            .append(originFunction.toSql())
+            .append(";");
+        return sb.toString();
+    }
+
+    @Override
+    public void write(DataOutput output) throws IOException {
+        // 1. type
+        FunctionType.ALIAS.write(output);
+        // 2. parent
+        super.writeFields(output);
+        // 3. parameter
+        output.writeInt(parameters.size());
+        for (String p : parameters) {
+            Text.writeString(output, p);
+        }
+        // 4. expr
+        Expr.writeTo(originFunction, output);
+    }
+
+    @Override
+    public void readFields(DataInput input) throws IOException {
+        super.readFields(input);
+        int counter = input.readInt();
+        for (int i = 0; i < counter; i++) {
+            parameters.add(Text.readString(input));
+        }
+        originFunction = Expr.readIn(input);
+    }
+
+    @Override
+    public String getProperties() {
+        Map<String, String> properties = new HashMap<>();
+        properties.put("parameter", getParamsSting(parameters));
+        setSlotRefLabel(originFunction);
+        String functionStr = originFunction.toSql();
+        functionStr = functionStr.replaceAll("'", "`");
+        properties.put("origin_function", functionStr);
+        return new Gson().toJson(properties);
+    }
+
+    /**
+     * set slotRef label to column name
+     * @param expr
+     */
+    private void setSlotRefLabel(Expr expr) {
+        for (Expr e : expr.getChildren()) {
+            setSlotRefLabel(e);
+        }
+        if (expr instanceof SlotRef) {
+            ((SlotRef) expr).setLabel("`" + ((SlotRef) expr).getColumnName() + "`");
+        }
+    }
+
+    private String getParamsSting(List<String> parameters) {
+        return parameters.stream()
+                .map(String::toString)
+                .collect(Collectors.joining(", "));
+    }
+}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/catalog/Function.java b/fe/fe-core/src/main/java/org/apache/doris/catalog/Function.java
index 6931b19..6013c93 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/catalog/Function.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/catalog/Function.java
@@ -563,7 +563,8 @@ public class Function implements Writable {
     enum FunctionType {
         ORIGIN(0),
         SCALAR(1),
-        AGGREGATE(2);
+        AGGREGATE(2),
+        ALIAS(3);
 
         private int code;
 
@@ -582,6 +583,8 @@ public class Function implements Writable {
                     return SCALAR;
                 case 2:
                     return AGGREGATE;
+                case 3:
+                    return ALIAS;
             }
             return null;
         }
@@ -652,6 +655,9 @@ public class Function implements Writable {
             case AGGREGATE:
                 function = new AggregateFunction();
                 break;
+            case ALIAS:
+                function = new AliasFunction();
+                break;
             default:
                 throw new Error("Unsupported function type, type=" + functionType);
         }
@@ -675,6 +681,9 @@ public class Function implements Writable {
             if (this instanceof ScalarFunction) {
                 row.add("Scalar");
                 row.add("NULL");
+            } else if (this instanceof AliasFunction) {
+                row.add("Alias");
+                row.add("NULL");
             } else {
                 row.add("Aggregate");
                 AggregateFunction aggFunc = (AggregateFunction) this;
diff --git a/fe/fe-core/src/main/java/org/apache/doris/catalog/FunctionSet.java b/fe/fe-core/src/main/java/org/apache/doris/catalog/FunctionSet.java
index 2abaf6b..4dacd6a 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/catalog/FunctionSet.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/catalog/FunctionSet.java
@@ -77,6 +77,7 @@ public class FunctionSet {
         ScalarBuiltins.initBuiltins(this);
         LikePredicate.initBuiltins(this);
         InPredicate.initBuiltins(this);
+        AliasFunction.initBuiltins(this);
     }
 
     public void buildNonNullResultWithNullParamFunction(Set<String> funcNames) {
diff --git a/fe/fe-core/src/main/java/org/apache/doris/rewrite/RewriteAliasFunctionRule.java b/fe/fe-core/src/main/java/org/apache/doris/rewrite/RewriteAliasFunctionRule.java
new file mode 100644
index 0000000..c3a0f3e
--- /dev/null
+++ b/fe/fe-core/src/main/java/org/apache/doris/rewrite/RewriteAliasFunctionRule.java
@@ -0,0 +1,48 @@
+// 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.doris.rewrite;
+
+import org.apache.doris.analysis.Analyzer;
+import org.apache.doris.analysis.Expr;
+import org.apache.doris.analysis.FunctionCallExpr;
+import org.apache.doris.catalog.AliasFunction;
+import org.apache.doris.catalog.Function;
+import org.apache.doris.common.AnalysisException;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+/**
+ * rewrite alias function to real function
+ */
+public class RewriteAliasFunctionRule implements ExprRewriteRule{
+    private final static Logger LOG = LogManager.getLogger(RewriteAliasFunctionRule.class);
+    public static RewriteAliasFunctionRule INSTANCE = new RewriteAliasFunctionRule();
+
+    @Override
+    public Expr apply(Expr expr, Analyzer analyzer) throws AnalysisException {
+        if (expr instanceof FunctionCallExpr) {
+            Function fn = expr.getFn();
+            if (fn instanceof AliasFunction) {
+                return ((FunctionCallExpr) expr).rewriteExpr();
+            }
+
+        }
+        return expr;
+    }
+}
diff --git a/fe/fe-core/src/main/jflex/sql_scanner.flex b/fe/fe-core/src/main/jflex/sql_scanner.flex
index bdf27b8..9a5bcc5 100644
--- a/fe/fe-core/src/main/jflex/sql_scanner.flex
+++ b/fe/fe-core/src/main/jflex/sql_scanner.flex
@@ -92,6 +92,7 @@ import org.apache.doris.qe.SqlModeHelper;
         keywordMap.put("admin", new Integer(SqlParserSymbols.KW_ADMIN));
         keywordMap.put("after", new Integer(SqlParserSymbols.KW_AFTER));
         keywordMap.put("aggregate", new Integer(SqlParserSymbols.KW_AGGREGATE));
+        keywordMap.put("alias", new Integer(SqlParserSymbols.KW_ALIAS));
         keywordMap.put("all", new Integer(SqlParserSymbols.KW_ALL));
         keywordMap.put("alter", new Integer(SqlParserSymbols.KW_ALTER));
         keywordMap.put("and", new Integer(SqlParserSymbols.KW_AND));
@@ -281,6 +282,7 @@ import org.apache.doris.qe.SqlModeHelper;
         keywordMap.put("outer", new Integer(SqlParserSymbols.KW_OUTER));
         keywordMap.put("outfile", new Integer(SqlParserSymbols.KW_OUTFILE));
         keywordMap.put("over", new Integer(SqlParserSymbols.KW_OVER));
+        keywordMap.put("parameter", new Integer(SqlParserSymbols.KW_PARAMETER));
         keywordMap.put("partition", new Integer(SqlParserSymbols.KW_PARTITION));
         keywordMap.put("partitions", new Integer(SqlParserSymbols.KW_PARTITIONS));
         keywordMap.put("password", new Integer(SqlParserSymbols.KW_PASSWORD));
diff --git a/fe/fe-core/src/test/java/org/apache/doris/catalog/CreateFunctionTest.java b/fe/fe-core/src/test/java/org/apache/doris/catalog/CreateFunctionTest.java
index 00248b0..c744ef0 100644
--- a/fe/fe-core/src/test/java/org/apache/doris/catalog/CreateFunctionTest.java
+++ b/fe/fe-core/src/test/java/org/apache/doris/catalog/CreateFunctionTest.java
@@ -103,5 +103,28 @@ public class CreateFunctionTest {
         Assert.assertEquals(1, constExprLists.size());
         Assert.assertEquals(1, constExprLists.get(0).size());
         Assert.assertTrue(constExprLists.get(0).get(0) instanceof FunctionCallExpr);
+
+        // create alias function
+        createFuncStr = "create alias function db1.id_masking(int) with parameter(id) as concat(left(id,3),'****',right(id,4));";
+        createFunctionStmt = (CreateFunctionStmt) UtFrameUtils.parseAndAnalyzeStmt(createFuncStr, ctx);
+        Catalog.getCurrentCatalog().createFunction(createFunctionStmt);
+
+        functions = db.getFunctions();
+        Assert.assertEquals(2, functions.size());
+
+        queryStr = "select db1.id_masking(13888888888);";
+        ctx.getState().reset();
+        stmtExecutor = new StmtExecutor(ctx, queryStr);
+        stmtExecutor.execute();
+        Assert.assertNotEquals(QueryState.MysqlStateType.ERR, ctx.getState().getStateType());
+        planner = stmtExecutor.planner();
+        Assert.assertEquals(1, planner.getFragments().size());
+        fragment = planner.getFragments().get(0);
+        Assert.assertTrue(fragment.getPlanRoot() instanceof UnionNode);
+        unionNode =  (UnionNode)fragment.getPlanRoot();
+        constExprLists = Deencapsulation.getField(unionNode, "constExprLists_");
+        Assert.assertEquals(1, constExprLists.size());
+        Assert.assertEquals(1, constExprLists.get(0).size());
+        Assert.assertTrue(constExprLists.get(0).get(0) instanceof FunctionCallExpr);
     }
 }

---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org