CREATE FUNCTION — 定义一个新函数
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 参数的模式:IN、OUT、INOUT 或 VARIADIC。如果省略,默认为 IN。只有 OUT 参数可以跟在 VARIADIC 参数之后。此外,OUT 和 INOUT 参数不能与 RETURNS TABLE 记法一起使用。
argname参数的名称。某些语言(包括 SQL 和 PL/pgSQL)允许你在函数体中使用该名称。对于其他语言,就函数本身而言,输入参数的名称只是额外的文档;但是你可以在调用函数时使用输入参数名称以提高可读性(参见 Section 4.3)。无论如何,输出参数的名称都是有意义的,因为它定义了结果行类型中的列名。(如果你省略了输出参数的名称,系统将选择一个默认的列名。)
argtype函数参数的数据类型(可以选择用模式限定)(如果有的话)。参数类型可以是基本类型、复合类型或域类型,也可以引用表列的类型。
根据实现语言的不同,可能还允许指定“伪类型”,例如 cstring。伪类型表示实际的参数类型要么没有完全指定,要么超出了普通 SQL 数据类型的范围。
通过书写 来引用列的类型。使用这个特性有时有助于使函数不受表定义更改的影响。 table_name.column_name%TYPE
default_expr 在未指定该参数时要用作默认值的表达式。该表达式必须能够被强制转换为该参数的类型。只有输入(包括 INOUT)参数才能有默认值。跟在带有默认值的参数之后的所有输入参数也必须具有默认值。
rettype 返回的数据类型(可以选择用模式限定)。返回类型可以是基本类型、复合类型或域类型,也可以引用表列的类型。根据实现语言的不同,可能还允许指定“伪类型”,例如 cstring。如果函数不应返回值,请指定 void 作为返回类型。
当存在 OUT 或 INOUT 参数时,RETURNS 子句可以省略。如果存在,它必须与输出参数所隐含的结果类型一致:如果有多个输出参数则为 RECORD,否则与单一输出参数的类型相同。
SETOF 修饰符表示该函数将返回一组条目,而不是单个条目。
通过书写 来引用列的类型。 table_name.column_name%TYPE
column_name RETURNS TABLE 语法中输出列的名称。这实际上是声明命名 OUT 参数的另一种方式,区别在于 RETURNS TABLE 还隐含着 RETURNS SETOF。
column_type RETURNS TABLE 语法中输出列的数据类型。
lang_name 实现该函数所用的语言的名称。它可以是 sql、c、internal 或用户定义的过程语言的名称,例如 plpgsql。默认值为 sql(如果指定了 sql_body)。用单引号把名称括起来的做法已被弃用,并且要求大小写完全匹配。
TRANSFORM { FOR TYPE type_name } [, ... ] }列出对函数的调用应应用哪些转换。转换在 SQL 类型和语言特定的数据类型之间进行转换;参见 CREATE TRANSFORM。过程语言的实现通常对内建类型有硬编码的了解,因此不需要在这里列出它们。如果过程语言的实现不知道如何处理某种类型,并且没有提供转换,它将回退到转换数据类型的默认行为,但这取决于具体的实现。
WINDOWWINDOW 表示该函数是窗口函数而不是普通函数。这目前只对用 C 语言编写的函数有用。在替换现有函数定义时,WINDOW 属性不能被更改。
IMMUTABLESTABLEVOLATILE 这些属性向查询优化器告知函数的行为。最多只能指定其中一个选项。如果这些选项都没有出现,则默认假定 VOLATILE。
IMMUTABLE(不可变)表示该函数不能修改数据库,并且在给定相同参数值时总是返回相同的结果;也就是说,它不会进行数据库查找,也不会以其他方式使用其参数列表中没有直接出现的信息。如果给定这个选项,任何以全常量参数对函数的调用都可以立即被替换为函数值。
STABLE(稳定)表示该函数不能修改数据库,并且在单次表扫描内,对于相同的参数值它会一致地返回相同的结果,但其结果在不同的 SQL 语句之间可能会改变。对于其结果取决于数据库查找、参数变量(例如当前时区)等的函数,这是合适的选择。(对于希望查询被当前命令修改的行的 AFTER 触发器来说,它并不合适。)还要注意,current_timestamp 系列函数可归类为稳定,因为它们的值在一个事务内不会改变。
VOLATILE(易变)表示函数值即使在单次表扫描内也可能改变,因此无法进行任何优化。在这个意义上属于易变的数据库函数相对较少;一些例子是 random()、currval()、timeofday()。但请注意,任何具有副作用的函数都必须被归类为易变,即使其结果是相当可预测的,以防止调用被优化掉;一个例子是 setval()。
更多细节请参见 Section 36.7。
LEAKPROOF LEAKPROOF(防泄漏)表示该函数没有副作用。除了通过返回值之外,它不会透露关于其参数的任何信息。例如,对某些参数值抛出错误消息而对其他参数值不抛出的函数,或者在任何错误消息中包含参数值的函数,都不是防泄漏的。这会影响系统对使用 security_barrier 选项创建的视图或者启用了行级安全性的表执行查询的方式。为了防止数据被意外暴露,系统会先强制执行来自安全策略和安全屏障视图的条件,然后才执行查询本身提供的包含非防泄漏函数的任何用户条件。被标记为防泄漏的函数和操作符被假定为值得信任,可以在来自安全策略和安全屏障视图的条件之前执行。此外,不接受参数或者没有从安全屏障视图或表接收任何参数的函数不必被标记为防泄漏,也可以在安全条件之前执行。参见 CREATE VIEW 和 Section 39.5。这个选项只能由超级用户设置。
CALLED ON NULL INPUTRETURNS NULL ON NULL INPUTSTRICTCALLED ON NULL INPUT(默认值)表示当函数的某些参数为空时,该函数仍将被正常调用。这样,函数作者就有责任在必要时检查空值并做出适当的响应。
RETURNS NULL ON NULL INPUT 或 STRICT 表示只要函数的任何一个参数为空,该函数就总是返回空。如果指定了这个参数,那么当存在空参数时将不会执行该函数;而是会自动假定结果为空。
[EXTERNAL] SECURITY INVOKER[EXTERNAL] SECURITY DEFINERSECURITY INVOKER 表示该函数将以调用它的用户的特权执行。这是默认值。SECURITY DEFINER 规定该函数将以拥有它的用户的特权执行。有关如何安全地编写 SECURITY DEFINER 函数的信息,see below。
为了符合 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_parametervalue SET 子句使得在进入函数时把指定的配置参数设置为指定的值,然后在函数退出时将其恢复为先前的值。SET FROM CURRENT 把执行 CREATE FUNCTION 时该参数的当前值保存下来,作为进入函数时要应用的值。
如果函数附加了 SET 子句,那么在函数内部对同一变量执行的 SET LOCAL 命令的效果将被限制在该函数内:配置参数先前的值仍会在函数退出时恢复。但是,普通的 SET 命令(不带 LOCAL)会覆盖 SET 子句,就像它对先前的 SET LOCAL 命令所做的那样:这样的命令的效果在函数退出后仍将持续,除非当前事务被回滚。
关于允许的参数名称和值的更多信息,请参见 SET 和 Chapter 19。
definition定义该函数的字符串常量;其含义取决于语言。它可以是内部函数名、目标文件的路径、SQL 命令或者过程语言中的文本。
使用美元符引用(参见 Section 4.1.2.4)来书写函数定义字符串通常比使用普通的单引号语法更有帮助。如果不使用美元符引用,函数定义中的任何单引号或反斜线都必须通过双写来转义。
obj_file, link_symbol 这种形式的 AS 子句用于动态可加载的 C 语言函数,此时 C 语言源代码中的函数名与 SQL 函数的名称不同。字符串 obj_file 是包含已编译 C 函数的共享库文件的名称,其解释方式与 LOAD 命令的相同。字符串 link_symbol 是该函数的链接符号,也就是 C 语言源代码中的函数名。如果省略了链接符号,则假定它与被定义的 SQL 函数的名称相同。所有函数的 C 名称必须互不相同,因此你必须为重载的 C 函数给出不同的 C 名称(例如,把参数类型用作 C 名称的一部分)。
当重复的 CREATE FUNCTION 调用引用同一个目标文件时,该文件在每个会话中只加载一次。要卸载并重新加载该文件(可能在开发期间),请启动一个新会话。
sql_body LANGUAGE SQL 函数的函数体。它可以是一条单一语句
RETURN expression
,也可以是一个块
BEGIN ATOMICstatement;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 类型语法。但是,带括号的类型修饰符(例如类型 numeric 的精度字段)会被 CREATE FUNCTION 丢弃。因此例如 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 中利用参数名称来递增一个整数:
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
-- Set a secure search_path: trusted schema(s), then 'pg_temp'.
SET search_path = admin, pg_temp;
这个函数的意图是访问表 admin.pwds。但是如果没有 SET 子句,或者 SET 子句中只提到了 admin,那么该函数就可能被通过创建名为 pwds 的临时表的方式所破坏。
如果安全定义者函数打算创建角色,并且它以非超级用户身份运行,那么 createrole_self_grant 也应当通过 SET 子句被设置为一个已知的值。
需要记住的另一点是,默认情况下,新创建的函数会被授予 PUBLIC 执行特权(更多信息请参见 Section 5.8)。你经常会希望把安全定义者函数的使用限制为只有某些用户。要做到这一点,你必须撤销默认的 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;
SQL 标准中定义了 CREATE FUNCTION 命令。PostgreSQL 的实现可以以兼容的方式使用,但有许多扩展。反过来,SQL 标准规定了 PostgreSQL 未实现的许多可选特性。
以下是一些重要的兼容性问题:
OR REPLACE 是 PostgreSQL 的扩展。
为了与某些其他数据库系统兼容,argmode 可以写在 argname 之前或之后。但只有第一种方式符合标准。
对于参数默认值,SQL 标准只规定了使用 DEFAULT 关键字的语法。使用 = 的语法用于 T-SQL 和 Firebird。
SETOF 修饰符是 PostgreSQL 的扩展。
只有 SQL 是被标准化的语言。
除 CALLED ON NULL INPUT 和 RETURNS NULL ON NULL INPUT 之外的所有其他属性都未被标准化。
对于 LANGUAGE SQL 函数的函数体,SQL 标准只规定了 sql_body 形式。
简单的 LANGUAGE SQL 函数可以写成既符合标准又可移植到其他实现的形式。使用高级特性、优化属性或其他语言的更复杂的函数必然在很大程度上是 PostgreSQL 特有的。