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 sdb_functions()WHERE function_type IN ('macro', 'table_macro');