CREATE FUNCTION — 定义一个新函数
CREATE [ OR REPLACE ] FUNCTIONname
( [ [argmode
] [argname
]argtype
[ { DEFAULT | = }default_expr
] [, ...] ] ) [ RETURNSrettype
| RETURNS TABLE (column_name
column_type
[, ...] ) ] { LANGUAGElang_name
| TRANSFORM { FOR TYPEtype_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 } | COSTexecution_cost
| ROWSresult_rows
| SUPPORTsupport_function
| SETconfiguration_parameter
{ TOvalue
| =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
权限。
有关编写函数的更多信息,请参阅 第 36.3 节。
name
要创建的函数的名称(可选地具有模式限定)。
argmode
参数模式:IN
、OUT
、INOUT
或 VARIADIC
。如果省略,则默认值为 IN
。只有 OUT
参数才能跟在 VARIADIC
后面。此外, OUT
和 INOUT
参数不能与 RETURNS TABLE
符号一起使用。
argname
参数名称。一些语言(包括 SQL 和 PL/pgSQL)允许你在函数体中使用名称。对于其它语言,输入参数名称只是额外的文档,就该函数而言;但你可以在调用函数时使用输入参数名称以提高可读性(参见第 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_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()
。
有关其他详细信息,请参见 第 36.7 节。
LEAKPROOF
LEAKPROOF
表明函数没有任何副作用。除了通过返回值外,它不会透露其参数的任何信息。例如,针对一些参数值抛出错误消息或在任何错误消息中包含参数值的函数不是防泄漏的。这会影响系统如何对使用 security_barrier
选项创建的视图或启用行级安全性的表执行查询。系统将在任何包含非防泄漏函数的、来自查询本身的用户提供的条件前强制执行来自安全策略和安全障碍视图的条件,以防止无意间泄漏数据。标记为防泄漏的函数和运算符被认为是可信的,并且可以在安全策略和安全障碍视图的条件前执行。此外,不接受参数或未从安全障碍视图或表传递任何参数的函数不必标记为防泄漏,即可在安全条件前执行。请参见 CREATE VIEW 和 第 39.5 节。此选项只能由超级用户设置。
空输入时调用
空输入时返回空
严格
空输入时调用
(默认)表明在部分自变量为空时将正常调用该函数。然后,函数作者有责任根据需要检查空值并做出适当响应。
空输入时返回空
或严格
表明每当自变量为空时,该函数始终返回空。如果指定了此参数,则在存在空自变量时不执行该函数;而是自动假定一个空结果。
[外部] SECURITY INVOKER
[外部] SECURITY DEFINER
SECURITY INVOKER
表示该函数将以调用它的用户的权限执行。这是默认设置。SECURITY DEFINER
指定该函数将以拥有它的用户的权限执行。有关如何安全地编写SECURITY DEFINER
函数的信息,请参阅以下内容。
关键字EXTERNAL
允许用于SQL一致性,但它不是必需的,因为不同于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
要为此函数使用的 规划程序支持函数 的名称(可以是模式限定名称)。有关详细信息,请参阅 第 36.11 节。必须是超级用户才能使用此选项。
configuration_parameter
value
SET
从句会在函数进入时将指定的配置参数设置为指定的值,然后在函数退出时将其还原为先前的值。 SET FROM CURRENT
将在执行 CREATE FUNCTION
时当前的参数值存储起来,作为在函数进入时应用的值。
如果 SET
子句附加到函数上,则为同一变量在函数中执行的 SET LOCAL
命令的效果将仅限于该函数:在函数退出时,配置参数的先前值仍会恢复。但是,常规 SET
命令(不带 LOCAL
)会覆盖 SET
子句,就像它对以前的 SET LOCAL
命令所做的那样:除非回滚当前事务,否则此命令的效果将在函数退出后继续存在。
定义
一个字符串常量,用于定义函数;其含义取决于语言。它可以是内部函数名称、对象文件路径、SQL 命令或过程语言文本。
通常,使用美元引用(请参阅 第 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
这类似于将函数体的文本写入一个字符串常量(参见上面的定义
),但有一些差别:此形式仅适用于 LANGUAGE SQL
,字符串常量形式适用于所有语言。此形式在函数定义时分析,字符串常量形式在执行时分析;因此,此形式不支持多态参数类型和函数定义时无法解析的其他构造。此形式跟踪函数和函数体中使用的对象之间的依赖性,因此 DROP ... CASCADE
将正常工作,而使用字符串文本的形式可能会导致悬空函数。最后,此形式与 SQL 标准和其他 SQL 实现更兼容。
PostgreSQL 允许函数重载,即只要输入参数类型独特,就可以将同一名称用于多个函数。无论是否使用,当在不信任其他用户的数据库中调用函数时,此功能都需要采取安全预防措施;参见第 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 中使用参数名递增整数
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
的临时表来破坏该函数。
如果安全定义函数打算创建角色,并且以非超级用户身份运行,那么也应使用 SET
子句将 createrole_self_grant
设置为一个已知的值。
要记住的另一点是,默认情况下,对于新创建的功能,执行权限授予 PUBLIC
(有关更多信息,请参阅 第 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。