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