CREATE FUNCTION
功能描述
创建一个函数。
注意事项
- 如果创建函数时参数或返回值带有精度,不进行精度检测。
- 创建函数时,函数定义中对表对象的操作建议都显式指定模式,否则可能会导致函数执行异常。
- 在创建函数时,函数内部通过SET语句设置current_schema和search_path无效。执行完函数search_path和current_schema与执行函数前的search_path和current_schema保持一致。
- 如果函数参数中带有出参,SELECT调用函数必须缺省出参,CALL调用函数适配Oracle必须指定出参,对于调用重载的带有PACKAGE属性的函数,CALL调用函数可以缺省出参,具体信息参见CALL的示例。
- 兼容PostgreSQL风格的函数或者带有PACKAGE属性的函数支持重载。在指定REPLACE的时候,如果参数个数、类型、返回值有变化,不会替换原有函数,而是会建立新的函数。
- SELECT调用可以指定不同参数来进行同名函数调用。由于语法CALL适配自Oracle,因此不支持调用不带有PACKAGE属性的同名函数。
- 在创建function时,不能在avg函数外面嵌套其他agg函数,或者其他系统函数。
- 在非逻辑集群模式下,暂不支持将返回值、参数以及变量设置为建在非系统默认安装Node Group的表,sql function内部语句暂不支持对建在非系统默认安装Node Group的表操作。
- 在逻辑集群模式下,如果函数返回值和参数是用户表类型,所有涉及表都必须在同一逻辑集群内;如果函数内部涉及对多个逻辑集群表操作,函数定义时不能为IMMUTABLE和SHIPPABLE类型,以避免函数被下推到DN执行。
- 在逻辑集群模式下,函数参数、返回值不能用%type引用表字段类型,否则会导致函数创建失败。
- 新创建的函数默认会给PUBLIC授予执行权限(详见GRANT)。用户可以选择收回PUBLIC默认执行权限,然后根据需要将执行权限授予其他用户,为了避免出现新函数能被所有人访问的时间窗口,应在一个事务中创建函数并且设置函数执行权限。
语法格式
- 兼容PostgreSQL风格的创建自定义函数语法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
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} | {PACKAGE} | COST execution_cost | ROWS result_rows | SET configuration_parameter { {TO | =} value | FROM CURRENT }} ][...] { AS 'definition' | AS 'obj_file', 'link_symbol' }
- Oracle风格的创建自定义函数的语法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
CREATE [ OR REPLACE ] FUNCTION function_name ( [ { argname [ argmode ] argtype [ { DEFAULT | := | = } expression ] } [, ...] ] ) RETURN rettype [ DETERMINISTIC ] [ {IMMUTABLE | STABLE | VOLATILE } | {SHIPPABLE | NOT SHIPPABLE} | {PACKAGE} | {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 ][...] { IS | AS } plsql_body /
参数说明
- OR REPLACE
如果函数已存在,则重新定义。
- function_name
要创建的函数名字(可以用模式修饰)。
取值范围:字符串,要符合标识符的命名规范。
如果创建的函数名与系统函数同名,建议指定schema。调用自定义函数时需指定schema,否则系统会优先调用系统函数。
- argname
函数参数的名字。
取值范围:字符串,要符合标识符的命名规范。
- argmode
函数参数的模式。
取值范围:IN,OUT,INOUT或VARIADIC。缺省值是IN。只有OUT模式的参数后面能跟VARIADIC。并且OUT和INOUT模式的参数不能用在RETURNS TABLE的函数定义中。
VARIADIC用于声明数组类型的参数。
- argtype
函数参数的类型。
- expression
函数参数的默认表达式。
- rettype
函数返回值的数据类型。
如果存在OUT或IN OUT参数,可以省略RETURNS子句。如果存在,该子句必须和输出参数所表示的结果类型一致:如果有多个输出参数,则为RECORD,否则与单个输出参数的类型相同。
SETOF修饰词表示该函数将返回一个集合,而不是单独一项。
- DETERMINISTIC
为适配Oracle SQL语法,未实现功能,不推荐使用。
- column_name
字段名称。
- column_type
字段类型。
- definition
一个定义函数的字符串常量,含义取决于语言。它可以是一个内部函数名字、一个指向某个目标文件的路径、一个SQL查询、一个过程语言文本。
- LANGUAGE lang_name
用以实现函数的语言的名字。可以是SQL,internal,或者是用户定义的过程语言名字。为了保证向下兼容,该名字可以用单引号(包围)。若采用单引号,则引号内必须为大写。
- WINDOW
表示该函数是窗口函数,替换函数定义时不能改变WINDOW属性。
自定义窗口函数只支持LANGUAGE是internal,并且引用的内部函数必须是窗口函数。
- IMMUTABLE
表示该函数在给出同样的参数值时总是返回同样的结果。
如果函数的入参是常量,会在优化器阶段计算该函数的值。益处是可以尽早获取表达式的值,从而能更准确的进行代价估算,生成的执行计划也更优。
用户自定义的IMMUTABLE的函数是会被自动下推到DN执行的,但是这样可能有潜在的风险,即如果用户错误定义了函数的IMMUTABLE属性,但是函数执行的过程并不是IMMUTABLE的,那么可能会导致结果错误等严重问题。因此,用户在指定函数的属性为IMMUTABLE的时候,要特别慎重。
举例如下:
- 如果自定义函数中引用了表,视图等对象,那么该函数就不能定义为IMMUTABLE,因为当表的数据发生变化的时候,函数的返回值可能发生变化。
- 如果自定义函数中引用了STABLE/VOALATILE类型的函数,那么该函数不能定义为IMMUTABLE。
- 如果自定义函数中有不下推的因素,则该函数不能定义成IMMUTABLE,因为IMMUTABLE意味着要下推到DN执行,与函数内部的不下推因素相互冲突。典型场景例如,包含不下推的函数、语法等。
- 如果自定义函数中含有聚合运算,但聚合运算的运算需要生成STREAM计划才能完成计算的(部分结果在DN计算,部分结果在CN计算,例如listagg函数等)。
同时,为了防止这种情况下可能出现严重问题,数据库内部可以通过设置behavior_compat_options=‘check_function_conflicts’来开启对函数定义冲突的检查,目前可以识别出上述1和2场景。
- STABLE
表示该函数不能修改数据库,对相同参数值,在同一次表扫描里,该函数的返回值不变,但是返回值可能在不同SQL语句之间变化。
- VOLATILE
表示该函数值可以在一次表扫描内改变,因此不会做任何优化。
- SHIPPABLE
NOT SHIPPABLE
表示该函数是否可以下推到DN上执行。
- 对于IMMUTABLE类型的函数,函数始终可以下推到DN上执行。
- 对于STABLE/VOLATILE类型的函数,仅当函数的属性是SHIPPABLE的时候,函数可以下推到DN执行。
用户在定义函数的SHIPPABLE属性时也需特别慎重,SHIPPABLE意味着整个函数会下推到DN上执行,如果设置不当,会导致结果错误等严重问题。
与定义IMMUTABLE属性一样,SHIPPABLE属性的定义也有诸多约束,简单来说就是函数内部不能有不可下推的因素,函数下推到单DN执行后,函数内部的计算逻辑仅依赖当前DN的数据集合。
举例如下:
- 如果函数内部引用了表,并且表为HASH分布,那么该函数通常不能定义为SHIPPABLE。
- 函数内部有不可下推的因素,函数,语法等,那么该函数不能定义为SHIPPABLE,可参考语句下推调优。
- 函数内部的计算过程可能需要跨DN数据,这种情况该函数通常不能定义为SHIPPABLE,例如一些聚合运算等。
- PACKAGE
表示该函数是否支持重载。PostgreSQL风格的函数本身就支持重载,此参数主要是针对Oracle风格的函数。
- 不允许package函数和非package函数重载或者替换。
- package函数不支持VARIADIC类型的参数。
- 不允许修改函数的package属性。
- 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
该函数只对用户定义的C函数生效,声明函数是在保护模式还是非保护模式下执行。如果函数声明为NOT FENCED模式,则函数的执行在CN或者DN进程中进行。如果函数声明为FENCED模式,则函数在新fork的进程执行,这样函数的异常不会影响CN或者DN进程。
FENCED/NOT FENCED模式的选择:
- 正在开发或者调试的Function使用FENCED模式。开发测试完成,使用NOT FENCED模式执行,减少fork进程以及通信的开销。
- 复杂的操作系统操作,例:打开文件,信号处理,线程处理等操作,使用FENCED模式。否则可能影响GaussDB(DWS)数据库的执行。
- 默认值为FENCED。
- COST execution_cost
用来估计函数的执行成本。
execution_cost以cpu_operator_cost为单位。
取值范围:正数
- ROWS result_rows
估计函数返回的行数。用于函数返回的是一个集合。
取值范围:正数,默认值是1000行。
- configuration_parameter
- value
把指定的数据库会话参数值设置为给定的值。如果value是DEFAULT或者RESET,则在新的会话中使用系统的缺省设置。OFF关闭设置。
取值范围:字符串
- DEFAULT
- OFF
- RESET
指定默认值。
- from current
取当前会话中的值设置为configuration_parameter的值。
- value
- plsql_body
PL/SQL存储过程体。
当在函数中创建用户时,日志中会记录密码的明文。因此不建议用户在函数中创建用户。
示例
创建定义为SQL查询的函数func_add_sql:
1 2 3 4 5 6 |
DROP FUNCTION IF EXISTS func_add_sql; CREATE FUNCTION func_add_sql(integer, integer) RETURNS integer AS 'select $1 + $2;' LANGUAGE SQL IMMUTABLE RETURNS NULL ON NULL INPUT; |
创建利用参数名自增一个整数的函数func_increment_plsql:
1 2 3 4 5 6 |
DROP FUNCTION IF EXISTS func_increment_plsql; CREATE OR REPLACE FUNCTION func_increment_plsql(i integer) RETURNS integer AS $$ BEGIN RETURN i + 1; END; $$ LANGUAGE plpgsql; |
创建返回RECORD类型的函数:
1 2 3 4 5 6 7 8 9 10 |
DROP FUNCTION IF EXISTS compute; CREATE OR REPLACE FUNCTION compute(i int, out result_1 bigint, out result_2 bigint) returns SETOF RECORD as $$ begin result_1 = i + 1; result_2 = i * 10; return next; end; $$language plpgsql; |
创建记录返回包含多个输出参数的函数:
1 2 3 4 5 |
DROP FUNCTION IF EXISTS func_dup_sql; CREATE FUNCTION func_dup_sql(in int, out f1 int, out f2 text) AS $$ SELECT $1, CAST($1 AS text) || ' is text' $$ LANGUAGE SQL; SELECT * FROM func_dup_sql(42); |
创建计算两个整数的和的函数,并返回结果。若果输入为null,则返回null:
1 2 3 4 5 6 7 8 |
DROP FUNCTION IF EXISTS func_add_sql2; CREATE FUNCTION func_add_sql2(num1 integer, num2 integer) RETURN integer AS BEGIN RETURN num1 + num2; END; / ; |
创建package属性的重载函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
CREATE OR REPLACE FUNCTION package_func_overload(col int, col2 int) return integer package as declare col_type text; begin col := 122; dbms_output.put_line('two int parameters ' || col2); return 0; end; / ; CREATE OR REPLACE FUNCTION package_func_overload(col int, col2 smallint) return integer package as declare col_type text; begin col := 122; dbms_output.put_line('two smallint parameters ' || col2); return 0; end; / |