CREATE [ OR REPLACE ] FUNCTION name ( [ [ argmode ] [ argname ] argtype [ { DEFAULT | = } default_expr ] [, ...] ] ) [ RETURNS rettype | RETURNS TABLE ( column_name column_type [, ...] ) ] { LANGUAGE lang_name | WINDOW | IMMUTABLE | STABLE | VOLATILE | [ NOT ] LEAKPROOF | CALLED ON NULL INPUT | RETURNS NULL ON NULL INPUT | STRICT | [ EXTERNAL ] SECURITY INVOKER | [ EXTERNAL ] SECURITY DEFINER | COST execution_cost | ROWS result_rows | SET configuration_parameter { TO value | = value | FROM CURRENT } | AS 'definition' | AS 'obj_file', 'link_symbol' } ... [ WITH ( attribute [, ...] ) ]
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 特权。
要创建的函数的名称(可以被模式限定)。
一个参数的模式:IN、OUT、 INOUT或者VARIADIC。如果省略,默认为 IN。只有OUT参数能跟在一个VARIADIC 参数后面。还有,OUT和INOUT参数不能和 RETURNS TABLE符号一起使用。
一个参数的名称。一些语言(包括 SQL 和 PL/pgSQL)让你在函数体中使用 该名称。对于其他语言,一个输入参数的名字只是额外的文字(就该函数本身 所关心的来说)。但是你可以在调用一个函数时使用输入参数名来提高可读性 (见Section 4.3)。在任何情况下,输出参 数的名称是有意义的,因为它定义了结果行类型中的列名(如果忽略一个输出 参数的名称,系统将选择一个默认的列名)。
该函数参数(如果有)的数据类型(可以是模式限定的)。参数类型可以是 基本类型、组合类型或者域类型,或者可以引用一个表列的类型。
根据实现语言,也可以允许指定cstring之类的 "伪类型"。伪类型表示实际参数类型没有被完整指定或者不属于 普通 SQL 数据类型集合。
可以写table_name.column_name%TYPE来 引用一列的类型。使用这种特性有时可以帮助创建一个不受表定义更改影响 的函数。
如果参数没有被指定值时要用作默认值的表达式。该表达式必须能被强制为该 参数的参数类型。只有输入(包括INOUT)参数可以具有默认值。 所有跟随在一个具有默认值的参数之后的输入参数也必须有默认值。
返回数据类型(可能被模式限定)。返回类型可以是一种基本类型、组合类型 或者域类型,也可以引用一个表列的类型。根据实现语言,也可以允许指定 cstring之类的"伪类型"。如果该函数不会返回一个值, 可以指定返回类型为void。
当有OUT或者INOUT参数时,可以省略 RETURNS子句。如果存在,该子句必须和输出参数所表示的结果 类型一致:如果有多个输出参数,则为RECORD,否则与单个输出 参数的类型相同。
SETOF修饰符表示该函数将返回一个项的集合而不是 一个单一项。
可以写table_name.column_name%TYPE来 引用一列的类型。
RETURNS TABLE语法中一个输出列的名称。这实际上是另一种声明 OUT参数的方法,不过RETURNS TABLE也隐含了 RETURNS SETOF。
RETURNS TABLE语法中的输出列的数据类型。
用以实现该函数的语言的名称。可以是sql、c、 internal或者一个用户定义的过程语言的名称,例如 plpgsql。不推荐用单引号包围该名称,并且要求区分大小写。
WINDOW表示该函数是一个 窗口函数而不是一个普通函数。当前只用于用 C 编写 的函数。在替换一个现有函数定义时,不能更改 WINDOW属性。
这些属性告知查询优化器该函数的行为。最多只能指定其中一个。 如果这些都不出现,则会默认为VOLATILE。
IMMUTABLE表示该函数不能修改数据库并且 对于给定的参数值总是会返回相同的值。也就是说,它不会做数据库查找 或者使用没有在其参数列表中直接出现的信息。如果给定合格选项,任何 用全常量参数对该函数的额调用可以立刻用该函数值替换。
STABLE表示该函数不能修改数据库,并且对于
相同的参数值,它在一次表扫描中将返回相同的结果。但是这种结果在不同
的 SQL 语句执行期间可能会变化。对于那些结果依赖于数据库查找、参数
变量(例如当前时区)等的函数来说,这是合适的(对希望查询被当前命令
修改的行的AFTER触发器不适合)。还要注意
current_timestamp
函数族适合被标记为稳定,因为它们的
值在一个事务内不会改变。
VOLATILE表示该函数的值在一次表扫描中都有 可能改变,因此不能做优化。在这种意义上,相对较少的数据库函数是不稳 定的,一些例子是random()、currval()、 timeofday()。但是注意任何有副作用的函数都必须被分类为 不稳定的,即便其结果是可以预测的,这是为了调用被优化掉。一个例子是 setval()。
更多细节可见Section 35.6。
LEAKPROOF表示该函数没有副作用。它不会泄露任何有关 其参数的信息(除了返回值)。例如,一个为某些参数值会抛出 错误消息(对其他参数则不抛出)的函数或者会把参数值包括在任意错误消息 中的函数不是防泄漏的。查询规划器可能会把防泄漏的函数(而不是其他函数) 推到用security_barrier选项创建的视图中。请参见 CREATE VIEW and Section 38.5。 这个选项只能被超级用户设置。
CALLED ON NULL INPUT(默认)表示在某些参数 为空值时应正常调用该函数。如果有必要,函数的作者应该负责检查空值并且 做出适当的相应。
RETURNS NULL ON NULL INPUT或 STRICT表示只要其任意参数为空值,该函数就会返回空值。 如果指定了这个参数,当有空值参数时该函数不会被执行,而是自动返回一个空值 结果。
SECURITY INVOKER表示要用调用该函数的用户的 特权来执行它。这是默认值。SECURITY DEFINER指定要用 创建函数的用户的特权来执行该函数。
为了符合 SQL,允许使用关键词EXTERNAL。但是它是 可选的,因为与 SQL 中不同,这个特性适用于所有函数而不仅是那些外部 函数。
一个给出该函数的估计执行代价的正数,单位是 cpu_operator_cost。如果该函数返回一个集合, 这就是每个被返回行的代价。如果没有指定代价,对 C 语言和内部函数会 指定为 1 个单位,对其他语言的函数则会指定为 100 单位。更大的值会 导致规划器尝试避免对该函数的不必要的过多计算。
一个正数,它给出规划器期望该函数返回的行数估计。只有当该函数被声明为 返回一个集合时才允许这个参数。默认假设为 1000 行。
SET子句导致进入该函数时指定配置参数将被设置为指定值。 并且在该函数退出时恢复到该参数之前的值。 SET FROM CURRENT会把该参数的当前会话值保存为进入 该函数时应该应用的值。
如果一个SET子句被附加到一个函数,那么在该函数内为 同一个变量执行的SET LOCAL命令会被限制于该函数: 在函数退出时该配置参数之前的值仍会被恢复。不过,一个普通的 SET命令(没有LOCAL)会覆盖 SET子句,更像一个之前的SET LOCAL命令所做的那样:这种命令的效果在函数退出后将会持续, 除非当前事务被回滚。
更多有关允许的参数名和参数值的信息请见SET和 Chapter 18。
一个定义该函数的字符串常量,其含义取决于语言。它可以是一个内部 函数名、一个对象文件的路径、一个 SQL 命令或者用一种过程语言编写 的文本。
美元引用Section 4.1.2.4通常对书写函数 定义字符串有所帮助,而普通单引号语法则不会有用。如果没有美元引用, 函数定义中的任何单引号或者反斜线必须用双写来转义。
当 C 语言源代码中该函数的名称与 SQL 函数的名称不同时,这种形式的 AS子句被用于动态可载入 C 语言函数。字符串 obj_file是包含动态 可载入对象的文件名,并且 link_symbol是该 函数的链接符号,也就是该函数在 C 语言源代码中的名称。如果省略链接 符号,它将被假定为要定义的 SQL 函数的名称。
在重复调用引用同一对象文件的 CREATE FUNCTION时,对每个会话该文件 只会被载入一次。要卸载并且重新装载该文件(可能是在开发期间),需要 开始一个新会话。
历史遗留的指定有关该函数可选信息的方法。可以出现下列属性:
等效于STRICT或者RETURNS NULL ON NULL INPUT。
isCachable是IMMUTABLE 的一个已被废弃的等效体。为了向后兼容的原因,它仍被接受。
属性名大小写无关。
编写函数的进一步信息可以参考Section 35.3。
PostgreSQL允许函数 重载,也就是说同一个名称可以被用于多个不同的 函数,只要它们具有可区分的输入参数类型。不过,所有函数的 C 名称必须 不同,因此你必须给被重载的 C 函数指定不同的 C 名称(例如,把参数类型 作为 C 名称的一部分)。
如果两个函数具有相同的名称和输入参数类型,它们被认为 相同(不考虑任何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替换一个现有函数时,对于更改参数名是有限制的。不能更改 已经分配给任何输入参数的名称(不过可以给之前没有名称的参数增加名称)。 如果有多于一个输出参数,不能更改输出参数的名称,因为可能会改变描述 函数结果的匿名组合类型的列名。这些限制是为了确保函数被替换时,已有的 对该函数的调用不会停止工作。
如果一个被声明为STRICT的函数带有一个VARIADIC 参数,会严格检查该可变数组作为一个整体是否为非空。如果 该数组有空值元素,该函数仍将被调用。
这里是一些小例子,它们可以帮你了解函数创建。更多信息和例子可见 Section 35.3。
CREATE FUNCTION add(integer, integer) RETURNS integer AS 'select $1 + $2;' LANGUAGE SQL IMMUTABLE RETURNS NULL ON NULL INPUT;
在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函数会被以创建它的用户 的特权来执行,需要小心地确保该函数不会被误用。为了安全, 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;
在PostgreSQL 版本 8.3 之前, SET选项不可用,因而较老的函数可能包含相当复杂的逻辑 来保存、设置以及恢复search_path。对于这种目的, SET选项更容易。
另一点要记住的是默认情况下,会为新创建的函数给PUBLIC 授予执行特权(详见GRANT)。你常常会希望把 安全定义器函数的使用限制在某些用户中。要这样做,你必须收回默认的 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;