GaussDB-CREATE FUNCTION
功能描述
创建一个函数。
注意事项
- 如果创建函数时参数或返回值带有精度,不进行精度检测。
- 创建函数时,函数定义中对表对象的操作建议都显式指定模式,否则可能会导致函数执行异常。
- 在创建函数时,函数内部通过SET语句设置current_schema和search_path无效。执行完函数后的search_path和current_schema与执行函数前的search_path和current_schema保持一致。
- 如果函数参数中带有出参,想要出参生效,必须打开guc参数 set behavior_compat_options = 'proc_outparam_override';SELECT、CALL调用函数时,必须要在出参位置提供实参进行调用,否则函数调用失败。
- 兼容PostgreSQL风格的函数支持重载。在指定REPLACE的时候,如果参数个数、类型、返回值有变化,不会替换原有函数,而是会建立新的函数。
- 不能创建仅形参名字不同(函数名和参数列表类型都一样)的重载函数。
- 不能创建与存储过程拥有相同名称和参数列表的函数。
- 不支持形参仅在自定义ref cursor类型和sys_refcursor类型不同的重载。
- 不支持仅返回的数据类型不同的函数重载。
- 不支持仅默认值不同的函数重载。
- 重载的函数在调用时变量需要明确具体的类型。
- ORA兼容模式的数据库,建立ORA风格的函数;PG兼容模式的数据库,建立PG风格的函数。不建议混合创建。
- 在函数内部使用未声明的变量,函数被调用时会报错。
- SELECT调用可以指定不同参数来进行同名函数调用。
- 在创建function时,不能在avg函数外面嵌套其他agg函数,或者其他系统函数。
- 在普通集群模式下,暂不支持将返回值、参数以及变量设置为建在非系统默认安装Node Group的表,sql function内部语句暂不支持对建在非系统默认安装Node Group的表操作。
- 新创建的函数默认会给PUBLIC授予执行权限(详见GRANT)。用户默认继承PUBLIC角色权限,因此其他用户也会有函数的执行权限并可以查看函数的定义,另外执行函数时还需要具备函数所在schema的USAGE权限。用户在创建函数时可以选择收回PUBLIC默认执行权限,然后根据需要将执行权限授予其他用户,为了避免出现新函数能被所有人访问的时间窗口,应在一个事务中创建函数并且设置函数执行权限。开启数据库对象隔离属性后,普通用户只能查看有权限执行的函数定义。
- 函数定义时如果指定为IMMUTABLE和SHIPPABLE类型,应该尽量避免函数中存在INSERT,UPDATE,DELETE,MERGE和DDL操作,因为上述操作应该由CN判断对应的执行节点,否则执行结果可能产生错误。如果在声明为IMMUTABLE和SHIPPABLE类型的函数中下推执行了DDL,可能会导致各节点数据库对象不一致。修复此类问题可以在CN上创建VOLATILE PL/SQL函数,函数定义中使用execute语句动态执行用于修复系统对象的DDL,再使用EXECUTE DIRECT ON语法在指定的DN上执行修复函数调用,从而解决引入的问题。
- 在函数内部调用其它无参数的函数时,可以省略括号,直接使用函数名进行调用。
- 不打开参数set behavior_compat_options = 'proc_outparam_override'时,被匿名块或存储过程直接调用的函数的RETURN值会被当做OUT出参的第一个值导致调用失败,想正确使用OUT、IN OUT出参,需打开参数set behavior_compat_options = 'proc_outparam_override',见示例。
- 在函数内部调用其他有出参的函数,如果在赋值表达式中调用时,需要打开guc参数 set behavior_compat_options = 'proc_outparam_override' ,并提前定义与出参类型相同的变量,然后将变量作为出参调用带有出参的其他函数,出参才能生效。否则,被调函数的出参会被忽略。
- 在打开GUC参数proc_outparam_override后,函数返回值为setof类型时,out出参不会生效。
- 兼容Oracle风格的函数支持参数注释的查看与导出、导入。
- 兼容Oracle风格的函数支持介于IS/AS与plsql_body之间的注释的查看与导出、导入。
- 被授予CREATE ANY FUNCTION权限的用户,可以在用户模式下创建/替换函数。
- 函数默认为SECURITY INVOKER权限,如果想将默认行为改为SECURITY DEFINER权限,需要设置guc参数behavior_compat_options='plsql_security_definer'。
-
函数创建时依赖未定义对象,如参数behavior_compat_options='plpgsql_dependency'打开,创建可执行,通过WARNING提示;如参数未打开,函数创建不可执行。
-
behavior_compat_options='plpgsql_dependency'打开时,函数体中,调用函数A,函数A出入参包含函数B时,函数B不建立依赖。例如functionA(functionB()),gs_dependencies表仅建立和functionA的依赖。
-
如O风格函数已被视图直接依赖,且参数behavior_compat_options='plpgsql_dependency'打开,再次创建函数后视图可正常访问;如参数未打开,视图访问失败。
-
创建函数时,不支持使用函数自身作为入参默认值。
-
带OUT模式参数的函数不能在SQL语句中被调用。
-
带OUT模式参数的函数不能被SELECT INTO语法调用。
-
带OUT模式参数的函数不支持嵌套调用。
b := func(a,func(c,1));建议改为:
tmp := func(c,1); b := func(a,tmp); -
在创建函数时,不会检查函数内返回值的类型。
-
如果将定义者权限的函数创建到其他用户Schema下,则会以其他用户的权限执行该函数,有越权风险,请谨慎使用。
-
在表达式中使用out参数作为出参时,如下情况不会生效,例如:使用execute immediate sqlv using func语法执行函数、使用select func into语法执行函数、使用insert、update等DML语句执行、使用select where a=func();带out出参的函数,作为入参时,fun(func(out b),a),out出参b未生效等。
-
在FUNCTION的RETURN语句中,返回复合类型的构造器的调用时,实际返回类型与定义返回类型不一致时,可以隐式转换为定义返回类型时则对结果进行类型转换,支持跨schema调用,如:RETURN schema.record;不支持跨database调用;在FUNCTION的RETURN语句中,返回FUNCTION的调用时,不支持在运算操作的表达式中带OUT参数的FUNCTION,如RETURN func(c out) + 1。
-
函数复杂调用,如:func(x).a,函数调用返回复合类型,支持跨schema调用,不支持通过database.schema.func(x).b的方式调用。
-
调用带out出参的存储过程,设置GUC参数set behavior_compat_options = 'proc_outparam_transfer_length'后可以传递参数长度。规格限制如下:
- 支持的基本类型包括:CHAR(n)、CHARACTER(n)、NCHAR(n)、VARCHAR(n)、VARYING(n)、VARCHAR2(n)、NVARCHAR2(n)。
- out出参不生效的情况下(比如perform)不需要传递长度。
- 不支持精度传递的基本类型包括:NUMERIC、DECIMAL、NUMBER、FLOAT、DEC、INTEGER、TIME、TIMESTAMP、INTERVAL、TIME WITH TIME ZONE、TIMESTAMP WITH TIME ZONE、TIME WITHOUT TIME ZONE、TIMESTAMP WITHOUT TIME ZONE。
- GUC参数set behavior_compat_options是否设置为proc_outparam_override时都支持传递参数长度。
-
函数中存在通过guc参数控制特性的语法、函数等,如果在会话内更改相关guc参数,可能存在与预期结果不符,修改参数后,调用函数可能会维持修改前的行为,请谨慎变更guc参数。
语法格式
-
兼容PostgreSQL风格的创建自定义函数语法。
| ``` CREATE [ OR REPLACE ] FUNCTION function_name ( [ { argname [ argmode ] argtype [ { DEFAULT | := | = } expression ]} [, ...] ] ) [ RETURNS rettype [ DETERMINISTIC ] | RETURNS TABLE ( { column_name column_type } [, ...] )] LANGUAGE lang_name [ {IMMUTABLE | STABLE | VOLATILE } | {SHIPPABLE | NOT SHIPPABLE} | WINDOW | [ NOT ] LEAKPROOF | {CALLED ON NULL INPUT | RETURNS NULL ON NULL INPUT | STRICT } | {[ EXTERNAL ] SECURITY INVOKER | [ EXTERNAL ] SECURITY DEFINER | AUTHID DEFINER | AUTHID CURRENT_USER} | {fenced | not fenced} | COST execution_cost | ROWS result_rows | SET configuration_parameter { {TO | =} value | FROM CURRENT }} ][...] { AS 'definition' | AS 'obj_file', 'link_symbol' };
| ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -
兼容O风格的创建自定义函数的语法。
| ``` CREATE [ OR REPLACE ] FUNCTION function_name ( [ { argname [ argmode ] argtype [ { DEFAULT | := | = } expression ] } [, ...] ] ) RETURN rettype [ DETERMINISTIC ] [ {IMMUTABLE | STABLE | VOLATILE } | {SHIPPABLE | NOT SHIPPABLE} | {FENCED | NOT FENCED} | [ NOT ] LEAKPROOF | {CALLED ON NULL INPUT | RETURNS NULL ON NULL INPUT | STRICT } | {[ EXTERNAL ] SECURITY INVOKER | [ EXTERNAL ] SECURITY DEFINER | AUTHID DEFINER | AUTHID CURRENT_USER} | COST execution_cost | ROWS result_rows | SET configuration_parameter { {TO | =} value | FROM CURRENT } | LANGUAGE lang_name ][...] { IS | AS } plsql_body /
| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
参数说明
-
function_name
要创建的函数名称(可以用模式修饰)。
取值范围:字符串,要符合标识符命名规范。且最多为63个字符。若超过63个字符,数据库会截断并保留前63个字符当做函数名称。
建议不要创建和系统函数重名的函数,否则调用时需要指定函数的Schema。
-
argname
函数参数的名称。
取值范围:字符串,要符合标识符命名规范。且最多为63个字符。若超过63个字符,数据库会截断并保留前63个字符当做函数参数名称。
-
argmode
函数参数的模式。
取值范围:IN,OUT,INOUT或VARIADIC。缺省值是IN。只有OUT模式的参数后面能跟VARIADIC。并且OUT和INOUT模式的参数不能用在RETURNS TABLE的函数定义中。
VARIADIC用于声明数组类型的参数。
-
argtype
函数参数的类型。可以使用%ROWTYPE间接引用表的类型,或者使用%TYPE间接引用表或复合类型中某一列的类型。详细可参考定义变量
-
expression
参数的默认表达式。
- 在参数a_format_version值为10c和a_format_dev_version值为s2的情况下,函数参数为INOUT模式时不支持默认表达式。
- 推荐使用方式:将所有默认值参数定义在所有非默认值参数后。
- 调用带有默认参数的函数时,入参从左往右排入函数,如果有非默认参数的入参缺失则报错。
- 打开 proc_uncheck_default_param 参数,调用带有默认参数的函数时,入参从左往右排入函数,允许缺省默认参数个入参,如果有非默认参数的入参缺失,则会用错位的默认值填充该参数。
- 在参数a_format_version值为10c、a_format_dev_version值为s1和关闭proc_outparam_override,函数参数同时包括out出参和default时,默认值不可缺省。
-
rettype
函数返回值的数据类型。与argtype相同,可以使用%ROWTYPE或者%TYPE间接引用类型。
如果存在OUT或INOUT参数,可以省略RETURNS子句。如果没有省略,则该子句必须和输出参数所表示的结果类型一致。
SETOF修饰词表示该函数将返回一个集合,而不是单独一项。
-
column_name
字段名称。
-
column_type
字段类型。
-
definition
一个定义函数的字符串常量,含义取决于语言。它可以是一个内部函数名称、一个指向某个目标文件的路径、一个SQL查询、一个过程语言文本。
-
DETERMINISTIC
SQL语法兼容接口,未实现功能,不推荐使用。
-
LANGUAGE lang_name
用以实现函数的语言的名称。可以是SQL,C,internal,或者是用户定义的过程语言名称。为了保证向下兼容,该名称可以用单引号(包围)。若采用单引号,则引号内必须为大写。
由于兼容性问题,ORA风格的语法无论指定任何语言,最终创建的语言都为plpgsql。
- internel函数在定义时,如果AS指定为内部系统函数,则新创建函数的参数类型,参数个数,与返回值类型需要与内部系统函数保持一致,且需要有执行此内部系统函数的权限。
- internal函数只支持拥有sysadmin权限的用户创建。
-
WINDOW
表示该函数是窗口函数,通常只用于C语言编写的函数。替换函数定义时不能改变WINDOW属性。
NOTICE:
自定义窗口函数只支持LANGUAGE是internal,并且引用的内部函数必须是窗口函数。
-
IMMUTABLE
表示该函数在给出同样的参数值时总是返回同样的结果。
-
STABLE
表示该函数不能修改数据库,对相同参数值,在同一次表扫描里,该函数的返回值不变,但是返回值可能在不同SQL语句之间变化。
-
VOLATILE
表示该函数值可以在一次表扫描内改变,因此不会做任何优化。
-
SHIPPABLE
NOT SHIPPABLE
表示该函数是否可以下推到DN上执行。
- 对于IMMUTABLE类型的函数,函数始终可以下推到DN上执行。
- 对于STABLE/VOLATILE类型的函数,仅当函数的属性是SHIPPABLE的时候,函数可以下推到DN执行。
NOTICE:
对于指定了SHIPPABLE/IMMUABLE的函数或者存储过程,其不能包含EXCEPTION或调用含有EXCEPTION的函数或者存储过程。
-
LEAKPROOF
指出该函数的参数只包括返回值。LEAKPROOF只能由系统管理员设置。
-
CALLED ON NULL INPUT
表明该函数的某些参数是NULL的时候可以按照正常的方式调用。该参数可以省略。
-
RETURNS NULL ON NULL INPUT
STRICT
STRICT用于指定如果函数的某个参数是NULL,此函数总是返回NULL。如果声明了这个参数,当有NULL值参数时该函数不会被执行;而只是自动返回一个NULL结果。
RETURNS NULL ON NULL INPUT和STRICT的功能相同。
-
EXTERNAL
目的是和SQL兼容,是可选的,这个特性适合于所有函数,而不仅是外部函数。
-
SECURITY INVOKER
AUTHID CURRENT_USER
表明该函数将带着调用它的用户的权限执行。该参数可以省略。
SECURITY INVOKER和AUTHID CURRENT_USER的功能相同。
-
SECURITY DEFINER
AUTHID DEFINER
声明该函数将以创建它的用户的权限执行。
AUTHID DEFINER和SECURITY DEFINER的功能相同。
-
FENCED
NOT FENCED
该参数用于声明函数是在保护模式还是非保护模式下执行。如果函数声明为NOT FENCED模式,则函数的执行在CN或者DN进程中进行。如果函数声明为FENCED模式,则函数在新fork的进程执行,这样函数的异常不会影响CN或者DN进程。
FENCED/NOT FENCED模式的选择:
- 正在开发或者调试的Function使用FENCED模式。开发测试完成,使用NOT FENCED模式执行,减少fork进程以及通信的开销。
- 复杂的操作系统操作,例:打开文件,信号处理,线程处理等操作,使用FENCED模式。否则可能影响GaussDB数据库的执行。
- 用户自定义PL/SQL函数,如果不指定该参数,默认为NOT FENCED,且不支持指定为FENCED执行模式。
-
COST execution_cost
用来估计函数的执行成本。
execution_cost以cpu_operator_cost为单位。
取值范围:>=0的数值
-
ROWS result_rows
估计函数返回的行数。用于函数返回的是一个集合。
取值范围:>=0的数值,默认值是1000行。
-
configuration_parameter
-
value
把指定的数据库会话参数值设置为给定的值。如果value是DEFAULT或者RESET,则在新的会话中使用系统的缺省设置。OFF关闭设置。
取值范围:字符串
- DEFAULT
- OFF
- RESET
指定默认值。
-
FROM CURRENT
取当前会话中的值设置为configuration_parameter的值。
-
-
plsql_body
PL/SQL存储过程体。
NOTICE:
当在函数体中进行创建用户、修改密码或加解密等涉及密码或密钥相关操作时,系统表及日志中会记录密码或密钥的明文信息。为防止敏感信息泄露,不建议用户在函数体中进行涉及密码或密钥等敏感信息的相关操作。
示例
| ``` --创建一个兼容性为ORA的数据库。 gaussdb=# CREATE DATABASE ora_compatible_db DBCOMPATIBILITY 'ORA'; CREATE DATABASE --切换数据库。 gaussdb=# \c ora_compatible_db --定义函数为SQL查询。 ora_compatible_db=# CREATE FUNCTION func_add_sql(integer, integer) RETURNS integer AS 'select 2;' LANGUAGE SQL IMMUTABLE RETURNS NULL ON NULL INPUT; --利用参数名用plpgsql自增一个整数。 ora_compatible_db=# CREATE OR REPLACE FUNCTION func_increment_plsql(i integer) RETURNS integer AS LANGUAGE plpgsql; --返回RECORD类型。 ora_compatible_db=# CREATE OR REPLACE FUNCTION compute(i int, out result_1 bigint, out result_2 bigint) RETURNS SETOF RECORD AS LANGUAGE plpgsql; --返回一个包含多个输出参数的记录。 ora_compatible_db=# CREATE FUNCTION func_dup_sql(in int, out f1 int, out f2 text) AS SELECT $1, CAST($1 AS text) || ' is text' LANGUAGE SQL; --调用func_dup_sql函数。 ora_compatible_db=# SELECT * FROM func_dup_sql(42); f1 | f2 ----+------------ 42 | 42 is text (1 row) --计算两个整数的和,并返回结果。如果输入为null,则返回null。 ora_compatible_db=# CREATE FUNCTION func_add_sql2(num1 integer, num2 integer) RETURN integer AS BEGIN RETURN num1 + num2; END; / --删除函数。 ora_compatible_db=# DROP FUNCTION func_add_sql; ora_compatible_db=# DROP FUNCTION func_increment_plsql; ora_compatible_db=# DROP FUNCTION compute; ora_compatible_db=# DROP FUNCTION func_dup_sql; ora_compatible_db=# DROP FUNCTION func_add_sql2; --不打开参数set behavior_compat_options = 'proc_outparam_override'时,被匿名块或存储过程直接调用的函数的RETURN值会被当做OUT出参的第一个值,导致调用失败。 ora_compatible_db=# CREATE TYPE rec AS(c1 int, c2 int); ora_compatible_db=# CREATE OR REPLACE FUNCTION func(a in out rec, b in out int) RETURN int AS BEGIN a.c1:=100; a.c2:=200; b:=300; RETURN 1; END; / ora_compatible_db=# DECLARE r rec; b int; BEGIN func(r,b); --不支持 END; / ERROR: cannot assign non-composite value to a row variable CONTEXT: PL/SQL function inline_code_block line 4 at SQL statement ora_compatible_db=# DROP FUNCTION func; ora_compatible_db=# DROP TYPE rec; --以下示例只有当数据库兼容模式为ORA时可以执行。 --创建一个含有IN/OUT类型参数的函数。 ora_compatible_db=# CREATE OR REPLACE FUNCTION func_001(a in out date, b in out date) --#add in & inout #defult value RETURN integer AS BEGIN raise info '%', a; raise info '%', b; RETURN 1; END; / ora_compatible_db=# DECLARE date1 date := '2022-02-02'; date2 date := '2022-02-02'; BEGIN func_001(date1, date2); END; / INFO: 2022-02-02 00:00:00 CONTEXT: PL/SQL function inline_code_block line 5 at SQL statement INFO: 2022-02-02 00:00:00 CONTEXT: PL/SQL function inline_code_block line 5 at SQL statement ERROR: invalid input syntax for type timestamp: "1" CONTEXT: PL/SQL function inline_code_block line 5 at SQL statement --创建一个含有IN/OUT类型参数的函数。 ora_compatible_db=# CREATE OR REPLACE FUNCTION func_001(a in out INT, b in out date) --#add in & inout #defult value RETURN INT AS BEGIN raise info '%', a; raise info '%', b; RETURN a; END; / ora_compatible_db=# DECLARE date1 int := 1; date2 date := '2022-02-02'; BEGIN func_001(date1, date2); END; / INFO: 1 CONTEXT: PL/SQL function inline_code_block line 5 at SQL statement INFO: 2022-02-02 00:00:00 CONTEXT: PL/SQL function inline_code_block line 5 at SQL statement ANONYMOUS BLOCK EXECUTE --删除函数。 ora_compatible_db=# DROP FUNCTION func_001; --切换回初始数据库,并删除测试数据库。请用真实的数据库名替换postgres。 ora_compatible_db=# \c postgees gaussdb=# DROP DATABASE ora_compatible_db;
| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
#### 相关链接
[ALTER FUNCTION](https://doc.hcs.huawei.com/db/zh-cn/gaussdbqlh/24.1.30/devg-dist/gaussdb-12-0488.html#ZH-CN_TOPIC_0000001865745804),[DROP FUNCTION](https://doc.hcs.huawei.com/db/zh-cn/gaussdbqlh/24.1.30/devg-dist/gaussdb-12-0597.html#ZH-CN_TOPIC_0000001865745684)
#### 优化建议
- analyse | analyze
- 不支持在事务或匿名块中执行analyze 。
- 不支持在函数或存储过程中执行analyze操作。
更多详情请参考GaussDB 文档中心:<https://doc.hcs.huawei.com/db/zh-cn/gaussdbqlh/24.1.30/productdesc/qlh_03_0001.html>