Skip to main content

CREATE MACRO

The CREATE MACRO statement can create a scalar or table macro (function) in the catalog.

For a scalar macro, CREATE MACRO is followed by the name of the macro, and optionally parameters within a set of parentheses. The keyword AS is next, followed by the text of the macro. By design, a scalar macro may only return a single value. For a table macro, the syntax is similar to a scalar macro except AS is replaced with AS TABLE. A table macro may return a table of arbitrary size and shape.

Examples

Scalar Macros

Create a macro that adds two expressions (a and b):

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

Create a macro, replacing possible existing definitions:

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

Create a macro if it does not already exist, else do nothing:

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

Create a macro for a CASE expression:

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

Create a macro that does a subquery:

Query
CREATE MACRO one() AS (SELECT 1);

Macros are schema-dependent, and have an alias, FUNCTION:

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

Create a macro with a default parameter:

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

Create a macro arr_append (with a functionality equivalent to array_append):

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

Create a macro with a typed parameter:

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

Table Macros

Create a table macro without parameters:

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

Create a table macro with parameters (that can be of any type):

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

Create a table macro that returns multiple rows. It will be replaced if it already exists, and it is temporary (will be automatically deleted when the connection ends):

Query
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;

Pass an argument as a list:

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

An example for how to use the get_users table macro is the following:

Query
SELECT * FROM get_users([1, 5]);
Result
 uid | name-----+------   1 | Ada   5 | Eve

To define macros on arbitrary tables, use the query_table function. For example, the following macro computes a column-wise checksum on a table:

Query
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');
Result
 x                                      | y----------------------------------------+--- 60609334165039584609948387465088153270 | 0

Overloading

It is possible to overload a macro based on the types or the number of its parameters; this works for both scalar and table macros.

By providing overloads we can have both add_x(a, b) and add_x(a, b, c) with different function bodies.

Query
CREATE MACRO add_x    (a, b) AS a + b,    (a, b, c) AS a + b + c;
Query
SELECT    add_x(21, 42) AS two_args,    add_x(21, 42, 21) AS three_args;
Result
 two_args | three_args----------+------------       63 |         84
Query
CREATE OR REPLACE MACRO is_maximal    (a TINYINT) AS a = 2^7 - 1,    (a INT) AS a = 2^31 - 1;
Query
SELECT    is_maximal(127::TINYINT) AS tiny,    is_maximal(127) AS regular;
Result
 tiny | regular------+--------- t    | f

Syntax

Macros allow you to create shortcuts for combinations of expressions.

Query
CREATE MACRO add(a) AS a + b;
Result
db error: ERROR: Referenced column "b" was not found because the FROM clause is missing

This works:

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

Usage example:

Query
SELECT add(1, 2) AS x;
Result
 x--- 3

However, this fails:

Query
SELECT add('hello', 3);
Result
db error: ERROR: Could not convert string 'hello' to INT32

Macros can have default parameters.

b is a default parameter:

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

The following will result in 42:

Query
SELECT add_default(37);
Result
 add_default-------------          42

The order of named parameters does not matter:

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

When macros are used, they are expanded (i.e., replaced with the original expression), and the parameters within the expanded expression are replaced with the supplied arguments. Step by step:

The add macro we defined above is used in a query:

Query
SELECT add(40, 2) AS x;
Result
 x---- 42

Internally, add is replaced with its definition of a + b:

Query
SELECT a + b AS x;
Result
db error: ERROR: Referenced column "a" was not found because the FROM clause is missing

Then, the parameters are replaced by the supplied arguments:

Query
SELECT 40 + 2 AS x;
Result
 x---- 42

Limitations

Using Subquery Macros

Table macros as well as scalar macros defined using scalar subqueries cannot be used in the arguments of table functions. SereneDB will return an error:

Query
SELECT * FROM range(scalar_sub());
Result
db error: ERROR: Table function cannot contain subqueries

Overloads

Overloads for macro functions have to be set at creation, it is not possible to define a macro by the same name twice without first removing the first definition.

Recursive Functions

Defining recursive functions is not supported. A recursive macro expands until it exceeds the expression-depth limit (max_expression_depth, 1000 by default). For example, the following macro – supposed to compute the nth number of the Fibonacci sequence – fails (the example lowers max_expression_depth so the failure is immediate):

Query
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);
Result
db error: ERROR: Max expression depth limit of 50 exceeded. Use "SET max_expression_depth TO x" to increase the maximum expression depth.

Function Chaining on the First Function

Macros support the dot operator for function chaining on the first function. For example, the following macro uses the lower function in the conventional form:

Query
CREATE OR REPLACE MACRO low(s) AS lower(s);
SELECT low('AA');
Result
 low----- aa

Rewriting lower(s) to use function chaining works as well:

Query
CREATE OR REPLACE MACRO low(s) AS s.lower();
SELECT low('AA');
Result
 low----- aa

Viewing the List of Macros and Table Macros

You can use the following query to display the list of macros and table macros:

Query
SELECT schema_name, function_name, function_type, parametersFROM duckdb_functions()WHERE function_type IN ('macro', 'table_macro');