

# `CREATE FUNCTION`
<a name="create-function-syntax-support"></a>

`CREATE FUNCTION` 定义一个新函数。

**重要**  
在 Amazon Aurora DSQL 中，`CREATE FUNCTION` 仅支持 SQL 语言函数（`LANGUAGE sql`）。

## 支持的语法
<a name="create-function-supported-syntax"></a>

```
CREATE [ OR REPLACE ] FUNCTION
    name ( [ [ argmode ] [ argname ] argtype [ { DEFAULT | = } default_expr ] [, ...] ] )
    [ RETURNS rettype
      | RETURNS TABLE ( column_name column_type [, ...] ) ]
  { LANGUAGE sql
    | { IMMUTABLE | STABLE | VOLATILE }
    | { 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
    | SET configuration_parameter { TO value | = value | FROM CURRENT }
    | AS 'definition'
    | RETURN expression
    | BEGIN ATOMIC statement; [ statement; ... ] END
  } ...

where LANGUAGE must be sql
```

## 说明
<a name="create-function-description"></a>

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

如果包含了架构名称，则在指定的架构中创建函数。否则，将在当前 schema 中创建该函数。新函数的名称不能与同一架构中具有相同输入参数类型的任何现有函数匹配。不过，不同参数类型的函数可以共用一个名称（这称为*重载*）。

要替换现有函数的当前定义，请使用 `CREATE OR REPLACE FUNCTION`。您不能通过这种方式更改函数的名称或参数类型（这实际上会创建一个新的、不同的函数）。此外，您不能使用 `CREATE OR REPLACE FUNCTION` 来更改现有函数的返回类型。对此，必须删除该函数并重新创建函数。

在使用 `CREATE OR REPLACE FUNCTION` 替换现有函数时，函数的所有权和权限不会更改。所有其他函数属性都被分配在命令中指定或隐含的值。您必须拥有该函数才能替换它（这包括成为拥有角色的成员）。

如果您删除该函数，然后重新创建函数，则新函数与旧函数的实体不同；您将需要删除引用旧函数的现有规则、视图等等。使用 `CREATE OR REPLACE FUNCTION` 可以在不破坏引用该函数的对象的情况下更改函数定义。

您成为自己创建的函数的所有者。

您必须拥有参数类型和返回类型的 `USAGE` 权限，才能创建函数。

`SQL` 函数将一个或多个 `SQL` 语句封装为一个可重复使用的可调用单元。使用函数可集中组织常用表达式或计算，在多个查询中应用一致的逻辑，并通过调用单个函数而不是重复相同的 `SQL` 来简化应用程序代码。

## 参数
<a name="create-function-parameters"></a>

**{{name}}**  
要创建的函数的名称（可有选择地设为架构限定）。

**{{argmode}}**  
参数的模式：`IN`、`OUT`、`INOUT` 或 `VARIADIC`。如果省略，则默认为 `IN`。`VARIADIC` 必须是最后一个输入参数；只有 `OUT` 参数可以跟在它后面。`OUT` 和 `INOUT` 参数不能与 `RETURNS TABLE` 标记组合使用。

**{{argname}}**  
参数的名称。在 SQL 函数中，您可以在函数体中通过名称引用参数。`OUT` 或 `INOUT` 参数的名称也非常重要，因为它在结果行类型中定义相应的列名。

**{{argtype}}**  
参数的数据类型。这可以是 Aurora DSQL 支持的任何基本类型、数组类型或域。有关更多信息，请参阅 [Aurora DSQL 中支持的数据类型](working-with-postgresql-compatibility-supported-data-types.md)。您也可以使用 `%TYPE` 标记来引用现有表列的类型。

**{{default\_expr}}**  
要在未指定参数的情况下用作默认值的表达式。该表达式必须可强制转换为参数的参数类型。只有 `IN` 和 `INOUT` 参数可以具有默认值。在具有默认值的参数后面的每个参数也必须具有默认值。

**{{rettype}}**  
返回数据类型。这可以是 Aurora DSQL 支持的任何基本类型、数组类型或域。有关更多信息，请参阅 [Aurora DSQL 中支持的数据类型](working-with-postgresql-compatibility-supported-data-types.md)。要返回一组行，请使用 `SETOF {{rettype}}` 或 `RETURNS TABLE`。如果该函数不应该返回值，请指定 `void` 作为返回类型。存在 `OUT` 或 `INOUT` 参数时，可以省略 `RETURNS` 子句，并且该函数会返回 `record` 或对应的行类型。

**`RETURNS TABLE ( {{column_name}} {{column_type}} [, ...] )`**  
指定函数返回一组具有命名列及其类型的行。这等效于声明一个或多个 `OUT` 参数并指定 `SETOF` 作为返回类型。

**`LANGUAGE sql`**  
用于实现该函数的语言。在 Aurora DSQL 中，这必须是 `sql`。如果您省略 `LANGUAGE` 子句并提供 `BEGIN ATOMIC` 或 `RETURN` 函数体，则采用 `sql`。字符串常量主体（`AS '{{definition}}'`）需要显式 `LANGUAGE sql` 子句；省略该子句会失败，并出现错误 `no language specified`。

**`IMMUTABLE` / `STABLE` / `VOLATILE`**  
这些属性会将有关函数的行为通知给查询优化程序。您最多可以指定一个。如果省略全部三个，则函数默认为 `VOLATILE`。  
+ `IMMUTABLE` 表示该函数不能修改数据库，并且在给定相同参数值时始终返回相同的结果。
+ `STABLE` 表示该函数不能修改数据库。在单个表扫描中，对于相同的参数值，它始终返回相同的结果，但结果可能会因 SQL 语句而异。
+ `VOLATILE` 表示即使在单个表扫描中，函数值也可能发生更改，因此无法执行优化。

**`CALLED ON NULL INPUT` / `RETURNS NULL ON NULL INPUT` / `STRICT`**  
`CALLED ON NULL INPUT`（默认）表示当函数的某些参数为空时，该函数会正常调用。`RETURNS NULL ON NULL INPUT` 或 `STRICT` 表示当函数的任何参数为空时，该函数始终会返回 null 值。如果指定此项，则当任何参数为 null 值时，Aurora DSQL 不会执行该函数；该函数而是会自动返回 null 值。

**`[ EXTERNAL ] SECURITY INVOKER` / `[ EXTERNAL ] SECURITY DEFINER`**  
`SECURITY INVOKER`（默认）表示将使用调用该函数的用户的权限来执行函数。`SECURITY DEFINER` 指定要以拥有该函数的用户的权限执行函数。允许使用关键字 `EXTERNAL` 来保证 SQL 的一致性，不过它是可选的。

**`PARALLEL { UNSAFE | RESTRICTED | SAFE }`**  
Aurora DSQL 接受并存储 `PARALLEL` 标记。不过，此标记不会影响查询的执行。

**`COST {{execution_cost}}`**  
使用正数来提供函数的估计执行成本。如果函数返回一个集，则此值为每个返回行的估计成本。当此估计值未内联函数时，查询计划器会使用此估计值。

**`ROWS {{result_rows}}`**  
一个正数，用于提供查询计划器预计函数返回的估计行数。只有当函数声明为返回集时，才允许使用此项。默认假设值为 1000 行。

**`SET {{configuration_parameter}} { TO {{value}} | = {{value}} | FROM CURRENT }`**  
进入函数时，`SET` 子句会将指定的配置参数设置为指定的值，然后在函数退出时恢复到之前的值。`SET FROM CURRENT` 将运行 `CREATE FUNCTION` 时参数的当前值保存为进入函数时要应用的值。

**`AS '{{definition}}'`**  
一个定义函数的字符串常量；具体含义取决于语言。对于 SQL 语言函数，这是构成函数体的 SQL 语句。

**`RETURN {{expression}}`**  
使用 SQL 标准语法为返回单个表达式的函数定义函数体。这是字符串常量形式的替代方式。

**`BEGIN ATOMIC {{statement}}; [ ... ] END`**  
使用 SQL 标准语法定义函数体。函数体由一个或多个 SQL 语句组成。此形式仅对 `LANGUAGE sql` 有效。原子函数体中的语句作为函数的依赖关系进行跟踪。例如，如果不同时删除函数或使用 `DROP ... CASCADE`，则无法删除函数体中引用的表。

## 备注
<a name="create-function-notes"></a>

使用 `DROP FUNCTION` 语句可以删除函数。您还可以使用 `GRANT` 和 `REVOKE` 授予和撤消函数的权限（例如，`REVOKE ALL ON FUNCTION`）。

任何读取或修改表中数据的函数，都需要遵循与直接运行等效 SQL 语句相同的事务和并发行为。已声明 `IMMUTABLE` 或 `STABLE` 的函数不能修改数据库。

要更新现有函数的定义，请使用 `CREATE OR REPLACE FUNCTION`。您不能以这种方式更改函数的参数类型或返回类型；请改为删除并重新创建函数。

要重命名函数、更改其所有者或将函数移至其他架构，请使用 `ALTER FUNCTION`。要更改任何其他属性，请对所需的更改使用 `CREATE OR REPLACE FUNCTION`。有关更多信息，请参阅 [`ALTER FUNCTION`](alter-function-syntax-support.md)。

支持函数重载：您可以定义多个具有相同名称的函数，只要这些函数的输入参数类型不同即可。用户定义的函数也可以调用其他函数，并且可以遮蔽具有相同名称和参数类型的内置函数。

对于返回其主体包含多个语句的集（`SETOF` 或 `RETURNS TABLE`）的函数，仅返回最后一个语句的结果。该函数仍会执行先前的语句，但会丢弃其结果。这是 SQL 语言函数的标准行为。构造函数，使其最后一个语句生成完整的结果集。

返回集的函数不能保证任何特定的行顺序。要控制行顺序，请在查询中包含 `ORDER BY` 子句。

## 注意事项
<a name="create-function-considerations"></a>

下面的 `CREATE FUNCTION` 功能返回所示的错误。

**`LEAKPROOF`**  
返回错误 `only superuser can define a leakproof function`。接受 `NOT LEAKPROOF`（默认）。

**`TRANSFORM FOR TYPE`**  
返回错误 `transform for type {{type}} language "sql" does not exist`。

**`SUPPORT`（计划器支持函数）**  
返回错误 `function {{name}}(internal) does not exist`。

**用户定义的复合类型（`CREATE TYPE ... AS`），包括返回复合类型的函数**  
返回错误 `unsupported statement: CompositeType`。

Aurora DSQL 不支持 `CREATE PROCEDURE`，该函数会失败，并出现错误 `PROCEDURE is not supported`。

`WINDOW` 属性在语法上是可接受的，但对 SQL 语言函数没有用处。避免使用此属性。

## 示例
<a name="create-function-examples"></a>

使用字符串常量体创建一个简单的 SQL 函数，该函数将两个整数相加：

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

使用 SQL 标准 `RETURN` 函数体和命名参数创建相同的函数：

```
CREATE FUNCTION add(a integer, b integer) RETURNS integer
    LANGUAGE sql
    IMMUTABLE
    RETURN a + b;
```

使用 `RETURNS TABLE` 和 `BEGIN ATOMIC` 函数体创建一个返回一组行的函数：

```
CREATE FUNCTION employees_in_dept(dept_id integer)
    RETURNS TABLE (id integer, name text)
    LANGUAGE sql
    STABLE
    BEGIN ATOMIC
        SELECT id, name FROM employees WHERE department_id = dept_id;
    END;
```

创建一个函数，该函数使用其所有者的权限运行，并在调用持续时间内设置配置参数：

```
CREATE FUNCTION current_department() RETURNS text
    LANGUAGE sql
    STABLE
    SECURITY DEFINER
    SET search_path = app
    AS 'SELECT current_setting(''app.department'');';
```

## 兼容性
<a name="create-function-compatibility"></a>

`CREATE FUNCTION` 命令是在 SQL 标准中定义的。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。