
# 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调用函数时，必须要在出参位置提供实参进行调用，否则函数调用失败。
- 在指定REPLACE的时候，如果参数个数、类型、返回值有变化，不会替换原有函数，而是会建立新的函数。
- 不能创建仅形参名字不同（函数名和参数列表类型都一样）的重载函数。
- 不能创建与存储过程拥有相同名称和参数列表的函数。
- 不支持形参仅在自定义ref cursor类型和sys_refcursor类型不同的重载。
- 不支持仅返回的数据类型不同的函数重载。
- 不支持仅默认值不同的函数重载。
- 重载的函数在调用时变量需要明确具体的类型。
- 在函数内部使用未声明的变量，函数被调用时会报错。
- SELECT调用可以指定不同参数来进行同名函数调用。
- 在创建function时，不能在avg函数外面嵌套其他agg函数，或者其他系统函数。
- 在普通集群模式下，暂不支持将返回值、参数以及变量设置为建在非系统默认安装Node Group的表，sql function内部语句暂不支持对建在非系统默认安装Node Group的表操作。
- 新创建的函数默认会给PUBLIC授予执行权限（详见[GRANT](https://support.huaweicloud.com/distributed-devg-v3-gaussdb/gaussdb-12-0604.html#ZH-CN_TOPIC_0000002235390465)）。用户默认继承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'时，被匿名块或存储过程直接调用的函数的OUT，并且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下，则会以其他用户的权限执行该函数，有越权风险，请谨慎使用。
- 在运维管理员的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时都支持传递参数长度。
   
#### 语法格式
- 兼容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'
      };
  ```
  
- 兼容ORA模式数据库风格的创建自定义函数的语法。
  ```
  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**
  要创建的函数名称（可以用模式修饰）。
  取值范围：字符串，要符合[标识符命名规范](https://support.huaweicloud.com/distributed-devg-v3-gaussdb/gaussdb-12-0319.html#ZH-CN_TOPIC_0000002235270221__zh-cn_topic_0000002235098221_section14873217506)。且最多为63个字符。若超过63个字符，数据库会截断并保留前63个字符当做函数名称。
  
- **argname**
  函数参数的名称。
  取值范围：字符串，要符合[标识符命名规范](https://support.huaweicloud.com/distributed-devg-v3-gaussdb/gaussdb-12-0319.html#ZH-CN_TOPIC_0000002235270221__zh-cn_topic_0000002235098221_section14873217506)。且最多为63个字符。若超过63个字符，数据库会截断并保留前63个字符当做函数参数名称。
  
- **argmode**
  函数参数的模式。
  取值范围：IN，OUT，INOUT或VARIADIC。缺省值是IN。只有OUT模式的参数后面能跟VARIADIC。并且OUT和INOUT模式的参数不能用在RETURNS TABLE的函数定义中。
  ![](https://support.huaweicloud.com/distributed-devg-v3-gaussdb/public_sys-resources/note_3.0-zh-cn.png)
  VARIADIC用于声明数组类型的参数。
  
- **argtype**
  函数参数的类型。可以使用%ROWTYPE间接引用表的类型。详细可参考存储过程章节[定义变量](https://support.huaweicloud.com/distributed-devg-v3-gaussdb/gaussdb-12-0717.html#ZH-CN_TOPIC_0000002235390609)。
  
- **expression**
  参数的默认表达式。
  ![](https://support.huaweicloud.com/distributed-devg-v3-gaussdb/public_sys-resources/note_3.0-zh-cn.png)
  - 在参数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或IN OUT参数，可以省略RETURNS子句，如果没有省略，则该子句必须和输出参数所表示的结果类型一致。该子句与单个输出参数的类型相同。
  SETOF修饰词表示该函数将返回一个集合，而不是单独一项。
  
- **column_name**
  字段名称。
  
- **column_type**
  字段类型。
  
- **definition**
  一个定义函数的字符串常量，含义取决于语言。它可以是一个内部函数名称、一个指向某个目标文件的路径、一个SQL查询、一个过程语言文本。
  
- **DETERMINISTIC**
  SQL语法兼容接口，未实现功能，不推荐使用。
  
- **LANGUAGE lang_name**
  用以实现函数的语言的名称。可以是SQL，C，internal，或者是用户定义的过程语言名称。为了保证向下兼容，该名称可以用单引号（包围）。若采用单引号，则引号内必须为大写。
  由于兼容性问题，ORA风格的语法无论指定任何语言，最终创建的语言都为plpgsql。
  ![](https://support.huaweicloud.com/distributed-devg-v3-gaussdb/public_sys-resources/note_3.0-zh-cn.png)
  - internal函数在定义时，如果AS指定为内部系统函数，则新创建函数的参数类型、参数个数、和返回值类型需要与内部系统函数保持一致，且需要有执行此内部系统函数的权限。
  
  - internal函数只支持拥有sysadmin权限的用户创建。
    
- **WINDOW**
  表示该函数是窗口函数，通常只用于C语言编写的函数。替换函数定义时不能改变WINDOW属性。
  ![](https://support.huaweicloud.com/distributed-devg-v3-gaussdb/public_sys-resources/notice_3.0-zh-cn.png)
  自定义窗口函数只支持LANGUAGE是internal，并且引用的内部函数必须是窗口函数。
  
- **IMMUTABLE**
  表示该函数在给出同样的参数值时总是返回同样的结果。
  
- **STABLE**
  表示该函数不能修改数据库，对相同参数值，在同一次表扫描里，该函数的返回值不变，但是返回值可能在不同SQL语句之间变化。
  
- **VOLATILE**
  表示该函数值可以在一次表扫描内改变，因此不会做任何优化。
  
- **SHIPPABLE**
  **NOT SHIPPABLE**
  表示该函数是否可以下推到DN上执行。
  - 对于IMMUTABLE类型的函数，函数始终可以下推到DN上执行。
  
  - 对于STABLE/VOLATILE类型的函数，仅当函数的属性是SHIPPABLE的时候，函数可以下推到DN执行。
  
  
  ![](https://support.huaweicloud.com/distributed-devg-v3-gaussdb/public_sys-resources/notice_3.0-zh-cn.png)
  对于指定了SHIPPABLE/IMMUTABLE的函数或者存储过程，其不能包含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存储过程体。
  ![](https://support.huaweicloud.com/distributed-devg-v3-gaussdb/public_sys-resources/notice_3.0-zh-cn.png)
  当在函数体中进行创建用户、修改密码或加解密等涉及密码或密钥相关操作时，系统表及日志中会记录密码或密钥的明文信息。为防止敏感信息泄露，不建议用户在函数体中进行涉及密码或密钥等敏感信息的相关操作。
  
 
#### 示例
```
--创建一个兼容性为ORA的数据库。
gaussdb=# CREATE DATABASE ora_compatible_db DBCOMPATIBILITY 'ORA';
CREATE DATABASE
--切换数据库
gaussdb=# \c ora_compatible_db
--定义函数为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;
。
--返回一个包含多个输出参数的记录。
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 
RETURN num1 + num2;
END;
/
--修改函数add的执行规则为IMMUTABLE，即参数不变时返回相同结果。
gaussdb=# ALTER FUNCTION func_add_sql2(INTEGER, INTEGER) IMMUTABLE;
--将函数add的名称修改为add_two_number。
gaussdb=# ALTER FUNCTION func_add_sql2(INTEGER, INTEGER) RENAME TO add_two_number;
--将函数add的属者改为omm。
gaussdb=# ALTER FUNCTION add_two_number(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;
--不打开参数set behavior_compat_options = 'proc_outparam_override'时，被匿名块或存储过程直接调用的函数的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;
/
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 FUNCTION func_001(a in out date, b in out date) --#add in & inout #default 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 #default 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 FUNCTION](https://support.huaweicloud.com/distributed-devg-v3-gaussdb/gaussdb-12-0466.html#ZH-CN_TOPIC_0000002200190210)，[DROP FUNCTION](https://support.huaweicloud.com/distributed-devg-v3-gaussdb/gaussdb-12-0566.html#ZH-CN_TOPIC_0000002200190334)
#### 优化建议
- analyse \| analyze
  - 不支持在事务或匿名块中执行analyze。
  
  - 不支持在函数或存储过程中执行analyze操作。
   
 
