定义一个新函数。
CREATE [OR REPLACE] FUNCTION name
( [ [argmode] [argname] argtype [ { DEFAULT | = } default_expr ] [, ...] ] )
[ RETURNS rettype
| RETURNS TABLE ( column_name column_type [, ...] ) ]
{ LANGUAGE langname
| WINDOW
| IMMUTABLE | STABLE | VOLATILE | [NOT] LEAKPROOF
| CALLED ON NULL INPUT | RETURNS NULL ON NULL INPUT | STRICT
| NO SQL | CONTAINS SQL | READS SQL DATA | MODIFIES SQL
| [EXTERNAL] SECURITY INVOKER | [EXTERNAL] SECURITY DEFINER
| EXECUTE ON { ANY | MASTER | ALL SEGMENTS | INITPLAN }
| COST execution_cost
| SET configuration_parameter { TO value | = value | FROM CURRENT }
| AS 'definition'
| AS 'obj_file', 'link_symbol' } ...
[ WITH ({ DESCRIBE = describe_function
} [, ...] ) ]
CREATE FUNCTION定义一个新函数。 CREATE OR REPLACE FUNCTION要么创建新函数,要么替换现有定义。
新函数的名称不得与任何在同一模式中具有相同输入参数类型的现有函数匹配。 但是,不同参数类型的函数可能共享一个名称(重载)。
要更新现有函数的定义,请使用CREATE OR REPLACE FUNCTION。 不能以这种方式更改函数的名称或参数类型(这实际上会创建一个新的,不同的函数)。 同样,CREATE OR REPLACE FUNCTION将不允许您更改现有函数的返回类型。 为此,必须删除并重新创建该函数。 使用OUT参数时,这意味着您不能更改任何OUT参数的类型,除非删除该函数。 如果删除然后重新创建函数,则必须删除引用旧函数的现有对象(规则,视图,触发器等)。 使用CREATE OR REPLACE FUNCTION来更改函数定义,而不会破坏引用该函数的对象。
创建函数的用户将成为该函数的所有者。
为了能够创建函数,您必须对参数类型和返回类型具有USAGE特权。
为了防止数据在 YMatrix 数据库的各个segment之间变得不同步, 如果分类为STABLE或VOLATILE的任何函数包含SQL或以任何方式修改了数据库, 则不能在segment级别执行该函数。 例如,不允许在 YMatrix 数据库中的分布式数据上执行诸如random()或timeofday()之类的函数, 因为它们可能会导致segment实例之间的数据不一致。
为了确保数据的一致性,可以在master数据库上评估并执行的语句中安全地使用VOLATILE和STABLE函数。 例如,以下语句始终在master数据库上执行(没有FROM子句的语句):
SELECT setval('myseq', 201);
SELECT foo();
如果语句的FROM子句包含一个分布式表, 并且FROM子句中使用的函数仅返回一组行,则可以在这些segment上执行:
SELECT * FROM foo();
此规则的一个例外是返回表引用(rangeFuncs)的函数或使用refCursor数据类型的函数。 请注意,您无法从YMatrix 数据库中的任何类型的函数返回refcursor。
易变性属性(IMMUTABLE,STABLE,VOLATILE)和EXECUTE ON属性指定函数执行的两个不同方面。 通常,易变性表示执行该函数的时间,EXECUTE ON表示执行该函数的位置。
例如,可以在查询计划时执行使用IMMUTABLE属性定义的函数, 而必须对查询中的每一行执行带有VOLATILE属性的函数。 具有EXECUTE ON MASTER属性的函数仅在master上执行, 具有EXECUTE ON ALL SEGMENTS属性的函数仅在所有primary实例(而不是master)上执行。
在复制表上仅执行SELECT命令的用户定义函数可以在segment上运行。 使用DISTRIBUTED REPLICATED子句创建的复制表将其所有行存储在每个segment上。 函数在segment上读取它们是安全的,但是对复制表的更新必须在master实例上执行。
name
argmode
argname
argtype
default_expr
rettype
column_name
column_type
langname
WINDOW
IMMUTABLE
STABLE
VOLATILE
LEAKPROOF
CALLED ON NULL INPUT
RETURNS NULL ON NULL INPUT
STRICT
NO SQL
CONTAINS SQL
READS SQL DATA
MODIFIES SQL
[EXTERNAL] SECURITY INVOKER
[EXTERNAL] SECURITY DEFINER
EXECUTE ON ANY
EXECUTE ON MASTER
EXECUTE ON ALL SEGMENTS
EXECUTE ON INITPLAN
COST execution_cost
configuration_parameter
value
definition
obj_file, link_symbol
describe_function
用于自定义函数的所有已编译代码(共享库文件)必须放在 YMatrix 数据库阵列(master和所有segment)中每个主机的相同位置。 此位置也必须位于LD_LIBRARY_PATH中,以便服务器可以找到文件。 建议在 YMatrix 阵列中的所有master实例上, 相对于$libdir(位于$GPHOME/lib)或通过动态库路径(由dynamic_library_path服务器配置参数设置)定位共享库。
输入参数和返回值允许使用完整的SQL类型语法。 但是,类型规范的某些详细信息(例如,类型为numeric的精度字段)是基础函数实现的职责, 而CREATE FUNCTION命令无法识别或强制执行。
YMatrix 数据库允许函数重载。 相同的名称可以用于多个不同的函数,只要它们具有不同的输入参数类型即可。 但是,所有函数的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)将失败。
当重复的CREATE FUNCTION调用引用相同的目标文件时,该文件仅被加载一次。 要卸载并重新加载文件,请使用LOAD命令。
您必须对一种语言具有USAGE特权,才能使用该语言定义函数。
使用美元引号而不是常规的单引号语法编写函数定义字符串通常会很有帮助。 如果不使用美元引号,则必须通过将它们加倍来转义函数定义中的任何单引号或反斜杠。 用美元引用的字符串常量由一个美元符号($), 一个零个或多个字符的可选标记,另一个美元符号,构成字符串内容的任意字符序列, 一个美元符号以及与开始此美元引用相同的标记和一个美元符号组成。 在用美元引用的字符串中,可以使用单引号,反斜杠或任何字符而无需转义。 字符串内容始终按原义书写。 例如,以下是两种使用美元引号指定字符串"Dianne's horse"的方法:
$$Dianne's horse$$
$SomeTag$Dianne's horse$SomeTag$
如果将SET子句附加到函数, 则在函数内部针对相同变量执行的SET LOCAL命令的作用仅限于该函数; 函数退出时,配置参数的先前值仍会恢复。 但是,普通的SET命令(不包含LOCAL)会覆盖CREATE FUNCTION SET子句, 就像以前的SET LOCAL命令所使用的一样。 除非当前事务回滚,否则该命令的效果将在函数退出后继续存在。
如果将带有VARIADIC参数的函数声明为STRICT, 则严格性检查将测试可变参数数组整体是否为非空。 如果数组具有空元素,PL/pgSQL仍将调用该函数。
用CREATE OR REPLACE FUNCTION替换现有函数时,更改参数名称存在限制。 您无法更改已分配给任何输入参数的名称(尽管您可以将名称添加到以前没有输入参数的参数中)。 如果有多个输出参数,则不能更改输出参数的名称,因为这将更改描述函数结果的匿名复合类型的列名称。 进行这些限制是为了确保函数的现有调用在被替换时不会停止工作。
在某些情况下,如在FROM子句中指定的表中的数据分布在 YMatrix 数据库segment上, YMatrix 数据库不支持在查询中使用函数。 例如,此SQL查询包含函数func():
SELECT func(a) FROM table1;
如果满足以下所有条件,则该函数不支持在查询中使用:
如果不满足任何条件,则支持该函数。 具体来说,如果满足以下任一条件,则支持该函数:
大多数执行查询以访问表的函数只能在master上执行。 但是,仅对复制表执行SELECT查询的函数可以在segment上运行。 如果函数访问哈希分布表或随机表,则应使用EXECUTE ON MASTER属性定义该函数。 否则,在复杂的查询中使用该函数时,该函数可能会返回错误的结果。 如果没有该属性,优化器优化可能会确定将函数调用推到segment实例将是有益的。
这些是使用EXECUTE ON MASTER或EXECUTE ON ALL SEGMENTS属性定义的函数的限制:
属性 EXECUTE ON INITPLAN 表示该函数包含一个 SQL 命令,该命令将查询分派到段实例,并且需要 在master上进行特殊处理。YMatrix 会以下列方式处理master上的函数。
这个简单的示例在 CTAS 命令中使用函数 get_data() 来使用来自表country的数据创建表。 该函数包含一个 SELECT 命令,该命令从表 country 检索数据并使用 EXECUTE ON INITPLAN 属性。
CREATE TABLE country(
c_id integer, c_name text, region int)
DISTRIBUTED RANDOMLY;
INSERT INTO country VALUES (11,'INDIA', 1 ), (22,'CANADA', 2), (33,'USA', 3);
CREATE OR REPLACE FUNCTION get_data()
RETURNS TABLE (
c_id integer, c_name text
)
AS $$
SELECT
c.c_id, c.c_name
FROM
country c;
$$
LANGUAGE SQL EXECUTE ON INITPLAN;
CREATE TABLE t AS SELECT * FROM get_data() DISTRIBUTED RANDOMLY;
如果您使用 EXPLAIN ANALYZE VERBOSE 查看 CTAS 命令的查询计划,该计划显示该函数作为 InitPlan 节点的一部分运行,并且列出的切片之一被标记为条目 db。 没有该函数的简单 CTAS 命令的查询计划没有 InitPlan 节点或entrydb slice。
如果函数不包含 EXECUTE ON INITPLAN 属性,CTAS 命令将返回错误,指出函数无法在 QE slice上执行。
当函数使用 EXECUTE ON INITPLAN 属性时,使用该函数的命令,例如 CREATE TABLE t AS SELECT * FROM get_data() 将函数的结果收集到master上,然后在插入数据时将结果重新分发到segment实例。 如果函数返回大量数据,master 可能会成为收集和重新分发数据时的瓶颈。 如果您重写函数以在用户定义的函数中运行 CTAS 命令并将表名用作输入参数,则性能可能会提高。 在此示例中,该函数运行 CTAS 命令并且不需要 EXECUTE ON INITPLAN 属性。 运行 SELECT 命令会使用运行 CTAS 命令的函数创建表 t1。
CREATE OR REPLACE FUNCTION my_ctas(_tbl text) RETURNS VOID AS
$$
BEGIN
EXECUTE format('CREATE TABLE %s AS SELECT c.c_id, c.c_name FROM country c DISTRIBUTED RANDOMLY', _tbl);
END
$$
LANGUAGE plpgsql;
SELECT my_ctas('t1');
一个非常简单的加法函数:
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;
为PL/pgSQL函数增加每个查询的默认segment主机内存:
CREATE OR REPLACE FUNCTION function_with_query() RETURNS
SETOF text AS $$
BEGIN
RETURN QUERY
EXPLAIN ANALYZE SELECT * FROM large_table;
END;
$$ LANGUAGE plpgsql
SET statement_mem='256MB';
使用多态类型返回ENUM数组:
CREATE TYPE rainbow AS ENUM('red','orange','yellow','green','blue','indigo','violet');
CREATE FUNCTION return_enum_as_array( anyenum, anyelement, anyelement )
RETURNS TABLE (ae anyenum, aa anyarray) AS $$
SELECT $1, array[$2, $3]
$$ LANGUAGE SQL STABLE;
SELECT * FROM return_enum_as_array('red'::rainbow, 'green'::rainbow, 'blue'::rainbow);
返回包含多个输出参数的记录:
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(4);
该函数在EXECUTE ON ALL SEGMENTS中定义,以在所有primary实例上运行。 SELECT命令执行返回在每个segment实例上运行时间的函数。
CREATE FUNCTION run_on_segs (text) returns setof text as $$
begin
return next ($1 || ' - ' || now()::text );
end;
$$ language plpgsql VOLATILE EXECUTE ON ALL SEGMENTS;
SELECT run_on_segs('my test');
此功能在parts表中查找part名称。 parts表是复制表,因此该函数可以在master或primary上执行。
CREATE OR REPLACE FUNCTION get_part_name(partno int) RETURNS text AS
$$
DECLARE
result text := ' ';
BEGIN
SELECT part_name INTO result FROM parts WHERE part_id = partno;
RETURN result;
END;
$$ LANGUAGE plpgsql;
如果在master上执行SELECT get_part_name(100);,该函数在master上执行。 (master实例将查询定向到单个primary。) 如果orders是分布式表,并且您执行以下查询,则get_part_name()函数将在primary上执行。
SELECT order_id, get_part_name(orders.part_no) FROM orders;
CREATE FUNCTION在SQL:1999及更高版本中定义。 YMatrix 数据库版本相似,但不完全兼容。 这些属性不是可移植的,不同的可用语言也不是。
为了与某些其他数据库系统兼容,可以在argname之前或之后写入argmode。 但是只有第一种方法是符合标准的。
对于参数默认值,SQL标准仅使用DEFAULT关键字指定语法。 在T-SQL和Firebird中使用=语法。