更新时间:2024-05-07 GMT+08:00

CREATE FUNCTION

功能描述

创建一个函数。

注意事项

  • 如果创建函数时参数或返回值带有精度,不进行精度检测。
  • 创建函数时,函数定义中对表对象的操作建议都显式指定模式,否则可能会导致函数执行异常。
  • 创建存储过程时,仅对CREATE的存储过程或PACKAGE本身加写锁,仅对执行过程中编译、执行会对函数和函数依赖的PACKAGE均加读锁。
  • 在创建函数时,函数内部通过SET语句设置current_schema和search_path无效。执行完函数search_path和current_schema与执行函数前的search_path和current_schema保持一致。
  • 如果函数参数中带有出参,想要出参生效,必须打开guc参数 set behavior_compat_options = 'proc_outparam_override'; SELECT、CALL调用函数时,必须要在出参位置提供实参进行调用,否则函数调用失败。
  • 兼容PostgreSQL风格的函数或者带有PACKAGE属性的函数支持重载。在指定REPLACE的时候,如果参数个数、类型、返回值有变化,不会替换原有函数,而是会建立新的函数。
  • 不能创建仅形参名字不同(函数名和参数列表类型都一样)的重载函数。
  • 不能创建与存储过程拥有相同名称和参数列表的函数。
  • 不支持形式参数仅在自定义ref cursor类型和sys_refcursor类型不同的重载。
  • 不支持仅返回的数据类型不同的函数重载。
  • 不支持仅默认值不同的函数重载。
  • 重载的函数在调用时变量需要明确具体的类型。
  • 在函数内部使用未声明的变量,函数被调用时会报错。
  • SELECT调用可以指定不同参数来进行同名函数调用。由于语法不支持调用不带有PACKAGE属性的同名函数。
  • 在创建function时,不能在avg函数外面嵌套其他agg函数,或者其他系统函数。
  • 新创建的函数默认会给PUBLIC授予执行权限(详见GRANT)。用户默认继承PUBLIC角色权限,因此其他用户也会有函数的执行权限并可以查看函数的定义,另外执行函数时还需要具备函数所在schema的USAGE权限。用户在创建函数时可以选择收回PUBLIC默认执行权限,然后根据需要将执行权限授予其他用户,为了避免出现新函数能被所有人访问的时间窗口,应在一个事务中创建函数并且设置函数执行权限。开启数据库对象隔离属性后,普通用户只能查看有权限执行的函数定义。
  • 在函数内部调用其它无参数的函数时,可以省略括号,直接使用函数名进行调用。
  • 在函数内部调用其他有出参的函数,如果在赋值表达式中调用时,需要打开guc参数 set behavior_compat_options = 'proc_outparam_override' ,并提前定义与出参类型相同的变量,然后将变量作为出参调用带有出参的其他函数,出参才能生效。否则,被调函数的出参会被忽略。
  • 兼容Oracle风格的函数支持参数注释的查看与导出、导入。
  • 兼容Oracle风格的函数支持介于IS/AS与plsql_body之间的注释的查看与导出、导入。
  • 被授予CREATE ANY FUNCTION权限的用户,可以在用户模式下创建/替换函数。
  • 函数默认为SECURITY INVOKER权限,如果想将默认行为改为SECURITY DEFINER权限,需要设置guc参数behavior_compat_options='plsql_security_definer'。
  • 不打开参数set behavior_compat_options = 'proc_outparam_override'时,被匿名块或存储过程直接调用的函数的OUT、IN OUT出参不能使用复合类型,并且RETURN值会被当做OUT出参的第一个值导致调用失败,想正确使用OUT、IN OUT出参,需打开参数set behavior_compat_options = 'proc_outparam_override',见示例
  • 对于PL/SQL函数,打开参数behavior_compat_options='proc_outparam_override'后,out/inout的行为会改变,函数中如果return和out/inout,可以同时返回,参数打开前只会返回return,见示例
  • 对于PL/SQL函数,打开参数behavior_compat_options='proc_outparam_override'后,有以下限制:
    1. 如果同一schema和package中已存在带有out/inout参数函数,不能再次创建带有out/inout参数的同名函数。
    2. 无论使用select还是call调用存储过程,都必须加上out参数。
    3. 部分场景不支持函数参与表达式(与参数打开前相比),如存储过程中左赋值,call function等,见示例
    4. 不支持调用无return的函数,perform function调用。
    5. 存储过程中调用函数,不支持out/inout参数传入常量,可以传入变量,见示例
    6. 打开GUC参数proc_outparam_override后,函数返回值为setof类型时,out出参不会生效。
  • 函数创建时依赖未定义对象,如参数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下,则会以其他用户的权限执行该函数,有越权风险,请谨慎使用。
  • 在运维管理员的Schema下,只允许初始用户和Schema的属主自己创建对象,不允许其他用户在运维管理员的Schema下创建对象或修改对象的Schema为运维管理员的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未生效等。
  • 调用带out出参的存储过程,设置GUC参数set behavior_compat_options = 'proc_outparam_transfer_length'后可以传递参数长度。规格限制如下:
    1. 支持的基本类型包括:CHAR(n)、CHARACTER(n)、NCHAR(n)、VARCHAR(n)、VARYING(n)、VARCHAR2(n)、NVARCHAR2(n)类型的参数。
    2. out出参不生效的情况下(比如perform)不需要传递长度。
    3. 不支持精度传递的基本类型包括: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。
    4. GUC参数set behavior_compat_options是否设置为proc_outparam_override时都支持传递参数长度。
    5. 如果需要传递集合类型的元素长度和被集合类型嵌套的数组类型的元素长度,则需要在GUC参数behavior_compat_options里开启tableof_elem_constraints选项。

语法格式

  • 兼容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}
            | {PACKAGE}
            | COST execution_cost
            | ROWS result_rows
            | SET configuration_parameter { {TO | =} value | FROM CURRENT }}
         ][...]
        {
            AS 'definition'
        }
    
  • 兼容A模式数据库风格的创建自定义函数的语法。
    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
            | LANGUAGE lang_name
        ][...] 
    
        { 
          IS  | AS
    } plsql_body
    /

参数说明

  • function_name

    要创建的函数名称(可以用模式修饰)。

    取值范围:字符串,要符合标识符命名规范。且最多为63个字符。若超过63个字符,数据库会截断并保留前63个字符当做函数名称。

  • argname

    函数参数的名称。

    取值范围:字符串,要符合标识符命名规范。且最多为63个字符。若超过63个字符,数据库会截断并保留前63个字符当做函数参数名称。

  • argmode

    函数参数的模式。

    取值范围:IN,OUT,INOUT或VARIADIC。缺省值是IN。只有OUT模式的参数后面能跟VARIADIC。并且OUT和INOUT模式的参数不能用在RETURNS TABLE的函数定义中。

    VARIADIC用于声明数组类型的参数。

  • argtype

    函数参数的类型。可以使用%TYPE或%ROWTYPE间接引用变量或表的类型,详细可参考存储过程章节定义变量

  • 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

    函数返回值的数据类型。

    如果存在OUT或INOUT参数,可以省略RETURNS子句。如果没有省略,则该子句必须和输出参数所表示的结果类型一致。如果有多个输出参数,则该子句为RECORD类型,否则该子句与单个输出参数的类型相同。

    SETOF修饰词表示该函数将返回一个集合,而不是单独一项。

    与argtype相同,同样可以使用%TYPE或%ROWTYPE间接引用类型。

    PACKAGE外FUNCTION argtype和rettype中%TYPE不支持引用PACKAGE变量的类型。

  • column_name

    字段名称。

  • column_type

    字段类型。

  • definition

    一个定义函数的字符串常量,含义取决于语言。它可以是一个内部函数名称、一个指向某个目标文件的路径、一个SQL查询、一个过程语言文本。

  • DETERMINISTIC

    SQL语法兼容接口,未实现功能,不推荐使用。

  • LANGUAGE lang_name

    用以实现函数的语言的名称。可以是SQL,internal,或者是用户定义的过程语言名称。为了保证向下兼容,该名称可以用单引号(包围)。若采用单引号,则引号内必须为大写。

    由于兼容性问题,A数据库风格的语法无论指定任何语言,最终创建的语言都为plpgsql。

    • internel函数在定义时,如果AS指定为内部系统函数,则新创建函数的参数类型,参数个数,与返回值类型需要与内部系统函数保持一致,且需要有执行此内部系统函数的权限。
    • internal函数只支持拥有sysadmin权限的用户创建。
  • WINDOW

    表示该函数是窗口函数。替换函数定义时不能改变WINDOW属性。

    自定义窗口函数只支持LANGUAGE是internal,并且引用的内部函数必须是窗口函数。

  • IMMUTABLE

    表示该函数在给出同样的参数值时总是返回同样的结果。

  • STABLE

    表示该函数不能修改数据库,对相同参数值,在同一次表扫描里,该函数的返回值不变,但是返回值可能在不同SQL语句之间变化。

  • VOLATILE

    表示该函数值可以在一次表扫描内改变,因此不会做任何优化。

  • SHIPPABLE|NOT SHIPPABLE

    表示该函数是否可以下推执行。预留接口,不推荐使用。

  • PACKAGE
    表示该函数是否支持重载。PostgreSQL风格的函数本身就支持重载,此参数主要是针对其它风格的函数。
    • 不允许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的功能相同。

  • 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的值。

  • plsql_body

    PL/SQL存储过程体。

    当在函数体中进行创建用户、修改密码或加解密等涉及密码或密钥相关操作时,系统表及日志中会记录密码或密钥的明文信息。为防止敏感信息泄露,不建议用户在函数体中进行涉及密码或密钥等敏感信息的相关操作。

示例

--定义函数为SQL查询。
gaussdb=# CREATE FUNCTION func_add_sql(integer, integer) RETURNS integer
    AS 'select $1 + $2;'
    LANGUAGE SQL
    IMMUTABLE
    RETURNS NULL ON NULL INPUT;

--利用参数名用 plpgsql 自增一个整数。
gaussdb=# CREATE OR REPLACE FUNCTION func_increment_plsql(i integer) RETURNS integer AS $$
        BEGIN
                RETURN i + 1;
        END;
$$ LANGUAGE plpgsql;

--返回RECORD类型。
gaussdb=# CREATE OR REPLACE FUNCTION func_increment_sql(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;

--返回一个包含多个输出参数的记录。
gaussdb=# CREATE FUNCTION func_dup_sql(in int, out f1 int, out f2 text)
    AS $$ SELECT $1, CAST($1 AS text) || ' is text' $$
    LANGUAGE SQL;

gaussdb=# SELECT * FROM func_dup_sql(42);

--计算两个整数的和,并返回结果。如果输入为null,则返回null。
gaussdb=# CREATE FUNCTION func_add_sql2(num1 integer, num2 integer) RETURN integer
AS
BEGIN PAC
RETURN num1 + num2;
END;
/
--修改函数func_add_sql2的执行规则为IMMUTABLE,即参数不变时返回相同结果。
gaussdb=# ALTER FUNCTION func_add_sql2(INTEGER, INTEGER) IMMUTABLE;

--将函数func_add_sql2的名称修改为add_two_number。
gaussdb=# ALTER FUNCTION func_add_sql2(INTEGER, INTEGER) RENAME TO add_two_number;

--将函数add_two_number的属者改为ommgaussdb=# ALTER FUNCTION omm(INTEGER, INTEGER) OWNER TO omm;

--删除函数。
gaussdb=# DROP FUNCTION add_two_number;
gaussdb=# DROP FUNCTION func_increment_sql;
gaussdb=# DROP FUNCTION func_dup_sql;
gaussdb=# DROP FUNCTION func_increment_plsql;
gaussdb=# DROP FUNCTION func_add_sql;

--设置参数。
gaussdb=# set behavior_compat_options='proc_outparam_override';
--创建函数。
gaussdb=# CREATE or replace FUNCTION func1(in a integer, out b integer)
RETURNS int
AS $$
DECLARE
    c int;
    BEGIN
        c := 1;
        b := a + c;
        return c;
    END; $$
LANGUAGE 'plpgsql' NOT FENCED;
--同时返回return和出参。
gaussdb=# declare
    result integer;
    a integer := 2;
    b integer := NULL;
begin
    result := func1(a => a, b => b);
    raise info 'b is: %', b;
    raise info 'result is: %', result;
end;
/
INFO:  b is: 3
INFO:  result is: 1
ANONYMOUS BLOCK EXECUTE
--不支持左赋值表达式。
gaussdb=# declare
    result integer;
    a integer := 2;
    b integer := NULL;
begin
    result := func1(a => a, b => b) + 1;
    raise info 'b is: %', b;
    raise info 'result is: %', result;
end;
/
ERROR:  when invoking function func1, maybe input something superfluous.
CONTEXT:  compilation of PL/SQL function "inline_code_block" near line 3
--存储过程中部分场景不支持出参赋值(目前仅va := f1(x,y);以及return f1(x,y);方式支持出参赋值)。
gaussdb=# create or replace procedure p1 as
x int := 10;
y int;
begin
if func1(a => x, b => y) = 11 then  --出参y无法赋值
raise info 'case1';
raise info 'y is:%',y;
else
raise info 'case2';
raise info 'y is:%',y;
end if;
end;
/
call p1();
INFO:  case2
INFO:  y is:<NULL>
 p1
----

(1 row)
--考虑到前向兼容,此场景未报错,在设置参数 plsql_compile_check_options ='plsql_expression_check'后,此用例执行会报错。
gaussdb=# set plsql_compile_check_options ='plsql_expression_check';
call p1();
ERROR:  function func1 with out parameters cannot be called in another expression.
CONTEXT:  SQL statement "SELECT func1(a => x, b => y)= 11"
PL/SQL function p1() line 5 at IF
--存储过程中不支持out/inout传入常量。
gaussdb=# declare
    result integer;
    a integer := 2;
    b integer := NULL;
begin
    result := func1(a => a, b => 10);
    raise info 'b is: %', b;
    raise info 'result is: %', result;
end;
/
ERROR:  when invoking function func1, no destination for argments "b"
CONTEXT:  compilation of PL/SQL function "inline_code_block" near line 3
--存储过程中支持out/inout传入变量。
gaussdb=# declare
    result integer;
    a integer := 2;
    b integer := NULL;
begin
    result := func1(a,b);
    raise info 'b is: %', b;
    raise info 'result is: %', result;
end;
/
INFO:  b is: 3
INFO:  result is: 1
ANONYMOUS BLOCK EXECUTE

--不打开参数set behavior_compat_options = 'proc_outparam_override'时,被匿名块或存储过程直接调用的函数的OUT、IN OUT出参不能使用复合类型,并且RETURN值会被当做OUT出参的第一个值导致调用失败。
gaussdb=# create type rec as(c1 int, c2 int);
gaussdb=# 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;
/
gaussdb=# 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

gaussdb=# create or replace package pkg_type as
type table_of_index_int is table of integer index by integer; --创建integer类型。
type table_of_index_int01 is table of table_of_index_int index by integer; --创建嵌套integer类型。
type table_of_index_var is table of integer index by varchar(5); --创建varchar类型。
type table_of_index_var01 is table of table_of_index_var index by varchar(5); --创建嵌套varchar类型。
end pkg_type;
/

gaussdb=# create or replace function func_001(a in out pkg_type.table_of_index_int, b in out pkg_type.table_of_index_var) --#add in & inout #defult value
return pkg_type.table_of_index_int
as
table_of_index_int_val pkg_type.table_of_index_int;
table_of_index_var_val pkg_type.table_of_index_var;
begin
for i in 1..2 loop
table_of_index_int_val(i) := i;
a(i) := i;
table_of_index_var_val(i) := i;
b(i) := i;
end loop;
raise info '%',table_of_index_int_val;
raise info '%',table_of_index_var_val;
raise info '%',a;
raise info '%',b;
return table_of_index_int_val;
end;
/
gaussdb=# declare
table_of_index_int_val pkg_type.table_of_index_int;
table_of_index_var_val pkg_type.table_of_index_var;
begin
func_001(table_of_index_int_val,table_of_index_var_val);
end;
/

INFO:  {indexbyType:int,1=>1,2=>2}
CONTEXT:  PL/SQL function inline_code_block line 5 at SQL statement
INFO:  {indexbyType:varchar,"1"=>1,"2"=>2}
CONTEXT:  PL/SQL function inline_code_block line 5 at SQL statement
INFO:  {indexbyType:int,1=>1,2=>2}
CONTEXT:  PL/SQL function inline_code_block line 5 at SQL statement
INFO:  {indexbyType:varchar,"1"=>1,"2"=>2}
CONTEXT:  PL/SQL function inline_code_block line 5 at SQL statement
ERROR:  expression is of wrong type
CONTEXT:  PL/SQL function inline_code_block line 5 at SQL statement

gaussdb=# 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;
/
gaussdb=# 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

gaussdb=# 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;
/
gaussdb=# 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

相关链接

ALTER FUNCTIONDROP FUNCTION