跳到主要内容

CREATE MACRO 语句

CREATE MACRO 语句可在目录中创建标量宏或表宏(函数)。

对于标量宏,CREATE MACRO 后接宏名称,并可选地在括号中声明参数。接着是关键字 AS,其后为宏的文本定义。按设计,标量宏只能返回单个值。 对于表宏,语法与标量宏类似,但会将 AS 替换为 AS TABLE。表宏可返回任意大小和结构的表。

如果 MACRO 是临时的,则只能在同一个数据库连接中使用,并会在连接关闭时删除。

示例

标量宏

创建一个将两个表达式(ab)相加的宏:

CREATE MACRO add(a, b) AS a + b;

创建一个宏,并替换可能已存在的定义:

CREATE OR REPLACE MACRO add(a, b) AS a + b;

如果宏尚不存在则创建,否则不执行任何操作:

CREATE MACRO IF NOT EXISTS add(a, b) AS a + b;

CASE 表达式创建宏:

CREATE MACRO ifelse(a, b, c) AS CASE WHEN a THEN b ELSE c END;

创建一个执行子查询的宏:

CREATE MACRO one() AS (SELECT 1);

宏依赖于 schema,并且有一个别名 FUNCTION

CREATE FUNCTION main.my_avg(x) AS sum(x) / count(x);

创建一个带默认参数的宏:

CREATE MACRO add_default(a, b := 5) AS a + b;

创建宏 arr_append(其功能等价于 array_append):

CREATE MACRO arr_append(l, e) AS list_concat(l, list_value(e));

创建一个带类型参数的宏:

CREATE MACRO is_maximal(a INTEGER) AS a = 2^31 - 1;

表宏

创建一个不带参数的表宏:

CREATE MACRO static_table() AS TABLE
SELECT 'Hello' AS column1, 'World' AS column2;

创建一个带参数(可为任意类型)的表宏:

CREATE MACRO dynamic_table(col1_value, col2_value) AS TABLE
SELECT col1_value AS column1, col2_value AS column2;

创建一个返回多行的表宏。若已存在则会被替换,且它是临时宏(连接结束时会自动删除):

CREATE OR REPLACE TEMP MACRO dynamic_table(col1_value, col2_value) AS TABLE
SELECT col1_value AS column1, col2_value AS column2
UNION ALL
SELECT 'Hello' AS col1_value, 456 AS col2_value;

将参数作为列表传入:

CREATE MACRO get_users(i) AS TABLE
SELECT * FROM users WHERE uid IN (SELECT unnest(i));

下面是使用 get_users 表宏的示例:

CREATE TABLE users AS
SELECT *
FROM (VALUES (1, 'Ada'), (2, 'Bob'), (3, 'Carl'), (4, 'Dan'), (5, 'Eve')) t(uid, name);
SELECT * FROM get_users([1, 5]);

要在任意表上定义宏,请使用 query_table function。例如,下面的宏会计算表按列的校验和:

CREATE MACRO checksum(tbl) AS TABLE
SELECT bit_xor(md5_number(COLUMNS(*)::VARCHAR))
FROM query_table(tbl);

CREATE TABLE tbl AS SELECT unnest([42, 43]) AS x, 100 AS y;
SELECT * FROM checksum('tbl');

重载

可以根据参数类型或参数个数对宏进行重载;这同时适用于标量宏和表宏。

通过提供重载,我们可以同时拥有 add_x(a, b)add_x(a, b, c),并让它们使用不同的函数体。

CREATE MACRO add_x
(a, b) AS a + b,
(a, b, c) AS a + b + c;
SELECT
add_x(21, 42) AS two_args,
add_x(21, 42, 21) AS three_args;
two_argsthree_args
6384
CREATE OR REPLACE MACRO is_maximal
(a TINYINT) AS a = 2^7 - 1,
(a INT) AS a = 2^31 - 1;
SELECT
is_maximal(127::TINYINT) AS tiny,
is_maximal(127) AS regular;
tinyregular
truefalse

语法

宏允许你为表达式组合创建快捷写法。

CREATE MACRO add(a) AS a + b;
Binder Error:
Referenced column "b" not found in FROM clause!

这样可以正常工作:

CREATE MACRO add(a, b) AS a + b;

使用示例:

SELECT add(1, 2) AS x;
x
3

但是,下面会失败:

SELECT add('hello', 3);
Binder Error:
Could not choose a best candidate function for the function call "add(STRING_LITERAL, INTEGER_LITERAL)". In order to select one, please add explicit type casts.
Candidate functions:
add(DATE, INTEGER) -> DATE
add(INTEGER, INTEGER) -> INTEGER

宏可以带默认参数。

b 是默认参数:

CREATE MACRO add_default(a, b := 5) AS a + b;

以下语句将得到 42:

SELECT add_default(37);

命名参数的顺序不重要:

CREATE MACRO triple_add(a, b := 5, c := 10) AS a + b + c;
SELECT triple_add(40, c := 1, b := 1) AS x;
x
42

当使用宏时,它们会被展开(即替换为原始表达式),并且展开后表达式中的参数会被传入的实参替换。分步如下:

在查询中使用上面定义的 add 宏:

SELECT add(40, 2) AS x;

在内部,add 会被替换为其定义 a + b

SELECT a + b AS x;

然后,参数会被传入的实参替换:

SELECT 40 + 2 AS x;

限制

使用子查询宏

表宏以及使用标量子查询定义的标量宏,不能用于表函数的参数中。Goose 会返回以下错误:

Binder Error:
Table function cannot contain subqueries

重载

宏函数的重载必须在创建时一并设置;若不先删除第一次定义,则无法用同名再次定义宏。

递归函数

不支持定义递归函数。 例如,下面这个本应计算斐波那契数列第 n 项的宏会失败:

CREATE OR REPLACE FUNCTION fibo(n) AS (SELECT 1);
CREATE OR REPLACE FUNCTION fibo(n) AS (
CASE
WHEN n <= 1 THEN 1
ELSE fibo(n - 1)
END
);
SELECT fibo(3);
Binder Error:
Max expression depth limit of 1000 exceeded. Use "SET max_expression_depth TO x" to increase the maximum expression depth.

首个函数不支持函数链式调用

宏不支持在首个函数上使用点操作符进行函数链式调用。 为说明这一点,先看一个可正常工作的 lower 函数示例:

CREATE OR REPLACE MACRO low(s) AS lower(s);
SELECT low('AA');

但是,将 lower(s) 改写为函数链式调用后将无法工作:

CREATE OR REPLACE MACRO low(s) AS s.lower();
SELECT low('AA');
Binder Error:
Referenced column "s" not found in FROM clause!