受支持版本: 当前版本 (18) / 17 / 16 / 15 / 14
开发版本: devel

CREATE FUNCTION

CREATE FUNCTION — 定义一个新函数

Synopsis

CREATE [ OR REPLACE ] FUNCTION
    name ( [ [ argmode ] [ argname ] argtype [ { DEFAULT | = } default_expr ] [, ...] ] )
    [ RETURNS rettype
      | RETURNS TABLE ( column_name column_type [, ...] ) ]
  { LANGUAGE lang_name
    | TRANSFORM { FOR TYPE type_name } [, ... ]
    | WINDOW
    | { IMMUTABLE | STABLE | VOLATILE }
    | [ NOT ] LEAKPROOF
    | { CALLED ON NULL INPUT | RETURNS NULL ON NULL INPUT | STRICT }
    | { [ EXTERNAL ] SECURITY INVOKER | [ EXTERNAL ] SECURITY DEFINER }
    | PARALLEL { UNSAFE | RESTRICTED | SAFE }
    | COST execution_cost
    | ROWS result_rows
    | SUPPORT support_function
    | SET configuration_parameter { TO value | = value | FROM CURRENT }
    | AS 'definition'
    | AS 'obj_file', 'link_symbol'
    | sql_body
  } ...

描述

CREATE FUNCTION定义一个新函数。CREATE OR REPLACE FUNCTION将创建一个新函数,或者替换现有定义。要定义函数,用户必须具有该语言上的USAGE权限。

如果包含模式名,那么该函数会被创建在指定的模式中。否则,它会被创建在当前模式中。新函数的名称不能匹配同一模式中任何具有相同输入参数类型的现有函数或过程。不过,不同参数类型的函数和过程能够共享一个名字(这被称为重载)。

要替换一个现有函数的当前定义,可以使用CREATE OR REPLACE FUNCTION。但不能用这种方式更改函数的名称或者参数类型(如果尝试这样做,实际上就会创建一个新的不同函数)。此外,CREATE OR REPLACE FUNCTION也不允许更改现有函数的返回类型。要做到这一点,必须删除该函数并重新创建。(使用OUT参数时,这意味着除非删除该函数,否则不能更改任何OUT参数的类型。)

CREATE OR REPLACE FUNCTION被用来替换一个现有函数时,该函数的拥有权和权限不会改变。所有其他的函数属性会按照该命令中指定的或者隐含的值赋值。必须拥有(包括成为拥有角色的成员)该函数才能替换它。

如果删除函数后再重新创建,新函数就不再是旧函数的同一实体;你将必须删除引用旧函数的现有规则、视图、触发器等。使用CREATE OR REPLACE FUNCTION可以在不破坏引用该函数的对象的情况下更改函数定义。此外,ALTER FUNCTION还可用于更改现有函数的大多数辅助属性。

创建该函数的用户将成为该函数的拥有者。

要创建一个函数,你必须拥有参数类型和返回类型上的USAGE权限。

有关编写函数的详细信息,请参阅Section 36.3

参数

name #

要创建的函数名称(可以被模式限定)。

argmode #

参数的模式可以是:INOUTINOUT或者VARIADIC。如果省略,则默认为IN。只有OUT参数可以跟在VARIADIC参数之后。此外,OUTINOUT参数不能与RETURNS TABLE记法一起使用。

argname #

参数的名称。某些语言(包括 SQL 和 PL/pgSQL)允许在函数体中使用该名称。对于其他语言,就函数本身而言,输入参数的名称只是额外文档;但你可以在调用函数时使用输入参数名来提高可读性(见Section 4.3)。无论如何,输出参数的名称很重要,因为它定义了结果行类型中的列名。(如果省略输出参数的名称,系统将选择一个默认列名。)

argtype #

该函数参数(如果有)的数据类型(可以是模式限定的)。参数类型可以是基础类型、复合类型或者域类型,也可以引用一个表列的类型。

根据实现语言的不同,也可能允许指定诸如cstring这样的伪类型。伪类型表示实际参数类型要么没有被完整指定,要么不属于普通 SQL 数据类型集合。

写成table_name.column_name%TYPE即可引用一个列的类型。使用这种特性有时有助于让函数独立于表定义的变化。

default_expr #

如果未指定该参数,则用作默认值的表达式。该表达式必须能被强制转换为该参数的类型。只有输入参数(包括INOUT)才能有默认值。所有跟在具有默认值参数之后的输入参数也都必须有默认值。

rettype #

该函数的返回数据类型(可以是模式限定的)。返回类型可以是基础类型、复合类型或者域类型,也可以引用一个表列的类型。根据实现语言的不同,也可能允许指定诸如cstring这样的伪类型。如果函数不应该返回值,请把返回类型指定为void

当存在OUTINOUT参数时,可以省略RETURNS子句。如果写出该子句,它必须与输出参数所隐含的结果类型一致:如果有多个输出参数,则为RECORD;如果只有一个输出参数,则为该输出参数的类型。

SETOF修饰符表示该函数将返回一组项,而不是单个项。

写成table_name.column_name%TYPE即可引用一个列的类型。

column_name #

RETURNS TABLE语法中输出列的名称。这实际上是声明一个具名OUT参数的另一种方式,只不过RETURNS TABLE还隐含了RETURNS SETOF

column_type #

RETURNS TABLE语法中的输出列的数据类型。

lang_name #

用以实现该函数的语言的名称。可以是sqlcinternal或者一个用户定义的过程语言的名称,例如plpgsql。 如果指定了sql_body, 则默认值为sql。使用单引号将名称括起来已废弃,并要求大小写匹配。

TRANSFORM { FOR TYPE type_name } [, ... ] } #

列出对该函数调用时应应用的转换。转换在 SQL 类型和语言相关的数据类型之间进行变换,详见CREATE TRANSFORM。过程语言实现通常把有关内置类型的知识硬编码在代码中,因此那些不需要列举在这里。如果一种过程语言实现不知道如何处理某种类型且没有提供转换,它将回退到默认的数据类型转换行为,但这取决于具体实现。

WINDOW #

WINDOW表示该函数是窗口函数而不是普通函数。目前这只对用 C 编写的函数有用。在替换现有函数定义时,不能更改WINDOW属性。

IMMUTABLE
STABLE
VOLATILE #

这些属性会告诉查询优化器该函数的行为。最多只能指定其中一个。如果这些属性都没有出现,则默认假定为VOLATILE

IMMUTABLE表示该函数不能修改数据库,并且在给定相同参数值时总会返回相同结果;也就是说,它不会执行数据库查找,也不会以其他方式使用未直接出现在其参数列表中的信息。如果给出此选项,任何使用全常量参数对该函数的调用都可以立即替换为该函数值。

STABLE表示该函数不能修改数据库,并且在一次表扫描内,对于相同参数值会一致地返回相同结果,但其结果可能在不同 SQL 语句之间发生变化。这适用于结果依赖于数据库查找、参数变量(例如当前时区)等的函数。(对于希望查询由当前命令修改过的行的AFTER触发器,这样做并不合适。)另请注意,current_timestamp函数族也属于稳定函数,因为它们的值在一个事务内不会变化。

VOLATILE表示该函数的值即使在一次表扫描内也可能发生变化,因此无法进行任何优化。从这个意义上说,真正不稳定的数据库函数相对较少;一些例子是random()currval()timeofday()。但请注意,任何有副作用的函数都必须归类为不稳定,即使其结果相当可预测,也必须如此,以防其调用被优化掉;例如setval()

更多细节见Section 36.7

LEAKPROOF #

LEAKPROOF表示该函数没有副作用。除返回值外,它不会泄露其参数的任何信息。例如,对某些参数值会抛出错误而对另一些不会,或者在错误消息中包含参数值的函数,都不是防泄漏的。这会影响系统如何执行针对使用security_barrier选项创建的视图或启用了行级安全的表的查询。为了防止数据被无意暴露,系统会先强制执行安全策略和安全屏障视图中的条件,再执行查询本身中包含非防泄漏函数的用户提供条件。被标记为防泄漏的函数和操作符被视为可信,因此可以在安全策略和安全屏障视图的条件之前执行。此外,不接受参数的函数,或者没有从安全屏障视图或表中接收到任何参数的函数,即使未标记为防泄漏,也可以在安全条件之前执行。参见CREATE VIEWSection 39.5。此选项只能由超级用户设置。

CALLED ON NULL INPUT
RETURNS NULL ON NULL INPUT
STRICT #

CALLED ON NULL INPUT(默认)表示当某些参数为空值时,仍会正常调用该函数。如果有需要,则由函数作者负责检查空值并作出适当响应。

RETURNS NULL ON NULL INPUTSTRICT表示只要任一参数为空值,该函数总是返回空值。如果指定了这个参数,那么在参数中出现空值时不会执行该函数,而是自动假定结果为空值。

[EXTERNAL] SECURITY INVOKER
[EXTERNAL] SECURITY DEFINER #

SECURITY INVOKER表示要以调用它的用户的权限来执行该函数。这是默认值。SECURITY DEFINER指定要以拥有它的用户的权限来执行该函数。有关如何安全地编写SECURITY DEFINER函数的信息,见下文

为了符合 SQL,允许使用关键字EXTERNAL。但它是可选的,因为与 SQL 不同,这个特性适用于所有函数,而不仅仅是外部函数。

PARALLEL #

PARALLEL UNSAFE表示该函数不能在并行模式中执行;SQL 语句中只要出现这类函数就会强制使用串行执行计划。这是默认选项。PARALLEL RESTRICTED表示该函数可以在并行模式中执行,但只能在并行组领导者进程中执行。PARALLEL SAFE表示该函数可以在并行模式下不受限制地执行,包括在并行工作进程中执行。

如果函数会修改任何数据库状态、改变事务状态(使用子事务进行错误恢复除外)、访问序列(例如调用currval),或者对设置做持久性更改,则应标记为并行不安全。如果函数访问临时表、客户端连接状态、游标、预备语句,或者系统无法在并行模式下同步的其他后端本地状态,则应标记为并行受限(例如,setseed只能由组领导者执行,因为其他进程所做的更改不会反映到领导者中)。一般来说,如果函数实际上是受限或不安全却被标记为安全,或者实际上不安全却被标记为受限,那么在并行查询中使用它时可能会抛出错误或产生错误结果。C 语言函数若被错误标记,理论上甚至可能表现出完全未定义的行为,因为系统无法保护自己不受任意 C 代码的影响;不过在大多数情况下,结果通常也不会比其他函数更糟。如果拿不准,函数就应标记为UNSAFE,这也是默认值。

COST execution_cost #

一个正数,给出该函数的估计执行代价,单位为cpu_operator_cost。如果该函数返回一个集合,则这是每个返回行的代价。如果未指定代价,则对 C 语言和内部函数假定为 1 个单位,对其他所有语言的函数假定为 100 个单位。较大的值会让规划器尽量避免对该函数进行不必要的频繁求值。

ROWS result_rows #

一个正数,给出规划器应预计该函数返回的行数。只有函数被声明为返回集合时才允许使用此项。默认假定为 1000 行。

SUPPORT support_function #

用于此函数的规划器支持函数的名称(可选模式限定)。 详见Section 36.11。使用此选项必须是超级用户。

configuration_parameter
value #

SET子句会在进入函数时将指定的配置参数设为给定值,并在函数退出时恢复为先前的值。SET FROM CURRENT会把执行CREATE FUNCTION时该参数的当前值保存下来,作为进入函数时要应用的值。

如果函数附带了SET子句,那么在函数内针对同一变量执行的SET LOCAL命令,其效果会被限制在该函数内部:函数退出时,配置参数先前的值仍会被恢复。不过,普通的SET命令(不带LOCAL)会覆盖SET子句,就像它会覆盖先前的SET LOCAL命令一样:这类命令的效果会在函数退出后继续保持,除非当前事务被回滚。

关于允许的参数名和值的更多信息,见SETChapter 19

definition #

一个定义该函数的字符串常量,其含义取决于所用语言。它可以是一个内部函数名称、一个对象文件的路径、一个 SQL 命令,或者用一种过程语言编写的文本。

使用美元引用(见Section 4.1.2.4)来书写函数定义字符串通常会更有帮助,而不是使用普通的单引号语法。如果没有美元引用,函数定义中的任何单引号或者反斜线都必须用双写来转义。

obj_file, link_symbol #

当 C 语言源代码中的函数名与 SQL 函数的名称不同时,这种形式的AS子句用于动态可加载的 C 语言函数。字符串obj_file是包含已编译 C 函数的共享库文件名,其解释方式与LOAD命令相同。字符串link_symbol是该函数的链接符号,也就是该函数在 C 语言源代码中的名称。如果省略链接符号,则假定它与正在定义的 SQL 函数名称相同。所有函数的 C 名称都必须不同,因此必须为重载的 C 函数指定不同的 C 名称(例如把参数类型作为 C 名称的一部分)。

当重复的CREATE FUNCTION调用引用同一个对象文件时,该文件在每个会话中只装载一次。要卸载并重新装载该文件(例如在开发期间),请启动一个新会话。

sql_body #

LANGUAGE SQL函数的主体。它可以是单个语句

RETURN expression

或者一个语句块

BEGIN ATOMIC
  statement;
  statement;
  ...
  statement;
END

这类似于将函数体的文本写成字符串常量(见上面的definition), 但有一些不同:此形式仅适用于LANGUAGE SQL,字符串常量形式适用于所有语言。 此形式在函数定义时解析,字符串常量形式在执行时解析;因此,此形式不能支持多态参数类型以及其他 在函数定义时无法解析的构造。此形式跟踪函数和函数体中使用的对象之间的依赖关系,因此 DROP ... CASCADE将正常工作,而使用字符串文本的形式可能会留下悬空函数。最后, 此形式与 SQL 标准和其他 SQL 实现更加兼容。

重载

PostgreSQL允许函数重载;也就是说,只要输入参数类型不同,同一个名称就可以用于多个不同的函数。无论你是否使用这一能力,在某些用户不信任其他用户的数据库中调用函数时,都需要采取安全预防措施;参见Section 10.3

如果两个函数具有相同的名称和输入参数类型,它们被认为相同(不考虑任何OUT参数)。因此这些声明会冲突:

CREATE FUNCTION foo(int) ...
CREATE FUNCTION foo(int, out text) ...

参数类型列表不同的函数在创建时不会被视为冲突,但如果提供了默认值,则在使用时可能发生冲突。例如,考虑下面这些声明:

CREATE FUNCTION foo(int) ...
CREATE FUNCTION foo(int, int default 42) ...

调用foo(10)会失败,因为系统无法确定应该调用哪个函数。

注解

允许使用完整的SQL类型语法来声明函数参数和返回值。不过,CREATE FUNCTION会丢弃带圆括号的类型修饰符(例如numeric类型的精度字段)。因此,CREATE FUNCTION foo (varchar(10)) ...CREATE FUNCTION foo (varchar) ...完全等同。

在用CREATE OR REPLACE FUNCTION替换现有函数时,更改参数名会受到限制。不能更改已经分配给任何输入参数的名称(但可以给先前没有名称的参数补上名称)。如果输出参数多于一个,也不能更改输出参数的名称,因为那会改变描述函数结果的匿名复合类型的列名。这些限制是为了确保函数被替换时,已有的函数调用不会停止工作。

如果一个函数被声明为带有VARIADIC参数的STRICT函数,则严格性检查测试的是可变参数数组作为一个整体是否非空。如果该数组包含空值元素,仍会调用该函数。

示例

使用 SQL 函数把两个整数相加:

CREATE FUNCTION add(integer, integer) RETURNS integer
    AS 'select $1 + $2;'
    LANGUAGE SQL
    IMMUTABLE
    RETURNS NULL ON NULL INPUT;

同一函数也可以用更符合 SQL 标准的风格来编写,使用参数名和不加引号的函数体:

CREATE FUNCTION add(a integer, b integer) RETURNS integer
    LANGUAGE SQL
    IMMUTABLE
    RETURNS NULL ON NULL INPUT
    RETURN a + b;

PL/pgSQL中,使用参数名把一个整数加 1:

CREATE OR REPLACE FUNCTION increment(i integer) RETURNS integer AS $$
        BEGIN
                RETURN i + 1;
        END;
$$ LANGUAGE plpgsql;

返回一个包含多个输出参数的记录:

CREATE FUNCTION dup(in int, out f1 int, out f2 text)
    AS $$ SELECT $1, CAST($1 AS text) || ' is text' $$
    LANGUAGE SQL;

SELECT * FROM dup(42);

你也可以用一个显式命名的复合类型,更详细地表达同样的意思:

CREATE TYPE dup_result AS (f1 int, f2 text);

CREATE FUNCTION dup(int) RETURNS dup_result
    AS $$ SELECT $1, CAST($1 AS text) || ' is text' $$
    LANGUAGE SQL;

SELECT * FROM dup(42);

另一种返回多列的方法是使用TABLE函数:

CREATE FUNCTION dup(int) RETURNS TABLE(f1 int, f2 text)
    AS $$ SELECT $1, CAST($1 AS text) || ' is text' $$
    LANGUAGE SQL;

SELECT * FROM dup(42);

不过,TABLE函数与前面的示例不同,因为它实际返回的是一记录,而不只是单条记录。

安全地编写 SECURITY DEFINER函数

因为SECURITY DEFINER函数要以拥有它的用户的权限执行,所以必须小心确保该函数不会被滥用。出于安全考虑,应将search_path设置为排除任何可被不受信任用户写入的模式。这可以防止恶意用户创建对象(例如表、函数和操作符)来遮蔽该函数原本打算使用的对象。在这方面尤其重要的是临时表模式;默认情况下它最先被搜索,而且通常任何人都可写。一个安全的安排是强制把临时模式放到搜索顺序的最后。要做到这一点,应把pg_temp写成search_path中的最后一项。下面这个函数展示了安全用法:

CREATE FUNCTION check_password(uname TEXT, pass TEXT)
RETURNS BOOLEAN AS $$
DECLARE passed BOOLEAN;
BEGIN
        SELECT  (pwd = $2) INTO passed
        FROM    pwds
        WHERE   username = $1;

        RETURN passed;
END;
$$  LANGUAGE plpgsql
    SECURITY DEFINER
    -- 设置一个安全的 search_path:受信的模式,然后是 'pg_temp'。
    SET search_path = admin, pg_temp;

这个函数的意图是访问admin.pwds表。但如果没有SET子句,或者SET子句只提到admin,那么该函数就可能因为有人创建一个名为pwds的临时表而被利用。

如果该SECURITY DEFINER函数打算创建角色,并且以非超级用户身份运行,那么还应使用SET子句将createrole_self_grant设置为一个已知值。

另一点需要记住的是,默认情况下,新创建的函数会把执行权限授予PUBLIC(详见Section 5.8)。通常你会希望只允许某些用户使用SECURITY DEFINER函数。要做到这一点,必须先撤销默认的PUBLIC权限,然后有选择地授予执行权限。为了避免新函数在一段时间窗口内对所有人都可访问,应在同一个事务中创建该函数并设置权限。例如:

BEGIN;
CREATE FUNCTION check_password(uname TEXT, pass TEXT) ... SECURITY DEFINER;
REVOKE ALL ON FUNCTION check_password(uname TEXT, pass TEXT) FROM PUBLIC;
GRANT EXECUTE ON FUNCTION check_password(uname TEXT, pass TEXT) TO admins;
COMMIT;

兼容性

CREATE FUNCTION命令由 SQL 标准定义。PostgreSQL的实现可以按兼容方式使用,但也包含许多扩展。反过来,SQL 标准还规定了一些PostgreSQL尚未实现的可选特性。

以下是重要的兼容性问题:

  • OR REPLACE是 PostgreSQL 扩展。

  • 为与某些其他数据库系统兼容,argmode可以写在argname之前或之后,但只有前一种写法符合标准。

  • 对于参数默认值,SQL 标准只规定了带有DEFAULT关键字的语法。带=的语法用于 T-SQL 和 Firebird。

  • SETOF修饰符是 PostgreSQL 扩展。

  • 只有SQL被标准化为一种语言。

  • CALLED ON NULL INPUTRETURNS NULL ON NULL INPUT之外的所有其他属性都未标准化。

  • 对于LANGUAGE SQL函数的主体,SQL 标准只规定了sql_body形式。

简单的LANGUAGE SQL函数可以写成既符合标准、又能移植到其他实现的形式。更复杂的函数若使用高级特性、优化属性或其他语言,就不可避免地在很大程度上是 PostgreSQL 特有的。

提交更正

如果您发现文档中有不正确的内容、与您使用特定功能的经验不符或需要进一步说明,请使用此表单来报告文档问题。