14.64 SQL_MACRO Clause
The SQL_MACRO
clause marks a function as a SQL macro which
can be used as either a scalar expression or a table expression.
A TABLE
macro is a function annotated as a SQL_MACRO
and defined as a TABLE
type.
A SCALAR
macro is a function annotated as a SQL_MACRO
and defined as a SCALAR
type.
A SQL macro referenced in a view is always executed with the view owner's privileges.
The AUTHID
property cannot be specified. When a SQL macro is
invoked, the function body executes with definer's rights to construct the text to
return. The resulting expression is evaluated with invoker's rights. The SQL macro owner
must grant inherit privileges to the invoking function.
When a macro annotated function is used in PL/SQL, it works like a regular function returning character or CLOB
type with no macro expansion.
The SQL_MACRO annotation can appear in the following SQL statement:
- A
TABLE
macro can only appear inFROM
clause of a query table expression. - A
SCALAR
macro cannot appear inFROM
clause of a query table expression. It can appear in the select list or theWHERE
clause. - A scalar macro cannot have table arguments.
- A SQL macro cannot appear in a virtual column expression, functional index, editioning view or materialized view.
- Type methods cannot be annotated with
SQL_MACRO
.
Topics
Syntax
sql_macro_clause ::=
Semantics
sql_macro_clause
The sql_macro_clause can appear only once in the function. To make a SQL macro function, include the sql_macro_clause in the function definition. If you declare the SQL macro function before defining it, you must specify the sql_macro_clause in the function declaration.
If SCALAR
or TABLE
is not specified, TABLE
is the default.
SCALAR
Specify SCALAR
if the macro function can be used in scalar expressions.
TABLE (Default)
Specify TABLE
if the macro function can be used in table expressions.
Restrictions on sql_macro_clause
The SQL_MACRO
annotation is disallowed with RESULT_CACHE
, PARALLEL_ENABLE
, and PIPELINED
. Although the DETERMINISTIC
property cannot be specified, a SQL macro is always implicitly deterministic.
The SQL macro function must have a return type of VARCHAR2
, CHAR
, or CLOB
.
Examples
Example 14-36 Emp_doc: Using a Scalar Macro to Convert Columns into a JSON or XML Document
The macro is implemented as a tree of nested macros with the following call graph structure. emp_doc() ==> emp_json() ==> name_string() ==> email_string() ==> name_string() ==> date_string() ==> emp_xml ==> name_string() ==> email_string() ==> name_string() ==> date_string()
CREATE FUNCTION date_string(dat DATE) RETURN VARCHAR2 SQL_MACRO(SCALAR) IS BEGIN RETURN q'{ TO_CHAR(dat, 'YYYY-MM-DD') }'; END; /
CREATE FUNCTION name_string(first_name VARCHAR2, last_name VARCHAR2) RETURN VARCHAR2 SQL_MACRO(SCALAR) IS BEGIN RETURN q'{ TRIM(INITCAP(first_name) || ' ' || INITCAP(last_name)) }'; END; /
CREATE FUNCTION email_string(first_name VARCHAR2, last_name VARCHAR2, host_name VARCHAR2 DEFAULT 'example.com') RETURN VARCHAR2 SQL_MACRO(SCALAR) IS BEGIN RETURN q'{ REPLACE(LOWER(name_string(first_name, last_name)),' ','.') || '@' || host_name }'; END; /
CREATE FUNCTION emp_json(first_name VARCHAR2 DEFAULT NULL, last_name VARCHAR2 DEFAULT NULL, hire_date DATE DEFAULT NULL, phone_num VARCHAR2 DEFAULT NULL) RETURN VARCHAR2 SQL_MACRO(SCALAR) IS BEGIN RETURN q'{ JSON_OBJECT( 'name' : name_string(first_name, last_name), 'email' : email_string(first_name, last_name), 'phone' : phone_num, 'hire_date' : date_string(hire_date) ABSENT ON NULL) }'; END; /
CREATE FUNCTION emp_xml(first_name VARCHAR2 DEFAULT NULL, last_name VARCHAR2 DEFAULT NULL, hire_date DATE DEFAULT NULL, phone_num VARCHAR2 DEFAULT NULL) RETURN VARCHAR2 SQL_MACRO(SCALAR) IS BEGIN RETURN q'{ XMLELEMENT("xml", CASE WHEN first_name || last_name IS NOT NULL THEN XMLELEMENT("name", name_string(first_name, last_name)) END, CASE WHEN first_name || last_name IS NOT NULL THEN XMLELEMENT("email", email_string(first_name, last_name)) END, CASE WHEN hire_date IS NOT NULL THEN XMLELEMENT("hire_date", date_string(hire_date)) END, CASE WHEN phone_num IS NOT NULL THEN XMLELEMENT("phone", phone_num) END) }'; END; /
CREATE FUNCTION emp_doc(first_name VARCHAR2 DEFAULT NULL, last_name VARCHAR2 DEFAULT NULL, hire_date DATE DEFAULT NULL, phone_num VARCHAR2 DEFAULT NULL, doc_type VARCHAR2 DEFAULT 'json') RETURN VARCHAR2 SQL_MACRO(SCALAR) IS BEGIN RETURN q'{ DECODE(LOWER(doc_type), 'json', emp_json(first_name, last_name, hire_date, phone_num), 'xml', emp_xml(first_name, last_name, hire_date, phone_num)) }'; END; /
SELECT department_id, emp_doc(first_name => e.first_name, hire_date => e.hire_date) doc FROM hr.employees e WHERE department_id = 30 ORDER BY last_name;Result:
30 {"name":"Shelli","email":"shelli@example.com","hire_date":"2005-12-24"} 30 {"name":"Karen","email":"karen@example.com","hire_date":"2007-08-10"} 30 {"name":"Guy","email":"guy@example.com","hire_date":"2006-11-15"} 30 {"name":"Alexander","email":"alexander@example.com","hire_date":"2003-05-19"} 30 {"name":"Den","email":"den@example.com","hire_date":"2002-12-07"} 30 {"name":"Sigal","email":"sigal@example.com","hire_date":"2005-07-24"}
SELECT deptno, emp_doc(first_name => ename, hire_date => hiredate, doc_type => 'xml') doc FROM scott.emp ORDER BY ename;
20 <xml><name>Adams</name><email>adams@example.com</email><hire_date>1987-05-23</hire_date></xml> 30 <xml><name>Allen</name><email>allen@example.com</email><hire_date>1981-02-20</hire_date></xml> 30 <xml><name>Blake</name><email>blake@example.com</email><hire_date>1981-05-01</hire_date></xml> 10 <xml><name>Clark</name><email>clark@example.com</email><hire_date>1981-06-09</hire_date></xml> 20 <xml><name>Ford</name><email>ford@example.com</email><hire_date>1981-12-03</hire_date></xml> ... 30 <xml><name>Ward</name><email>ward@example.com</email><hire_date>1981-02-22</hire_date></xml>
VARIABLE surname VARCHAR2(100) EXEC :surname := 'ellison' WITH e AS (SELECT emp.*, :surname lname FROM emp WHERE deptno IN (10,20)) SELECT deptno, emp_doc(first_name => ename, last_name => lname, hire_date => hiredate) doc FROM e ORDER BY ename;
10 {"name":"Clark Ellison","email":"clark.ellison@example.com","hire_date":"1981-06-09"} 20 {"name":"Ford Ellison","email":"ford.ellison@example.com","hire_date":"1981-12-03"} 20 {"name":"Jones Ellison","email":"jones.ellison@example.com","hire_date":"1981-04-02"} 10 {"name":"King Ellison","email":"king.ellison@example.com","hire_date":"1981-11-17"} 10 {"name":"Miller Ellison","email":"miller.ellison@example.com","hire_date":"1982-01-23"} 20 {"name":"Scott Ellison","email":"scott.ellison@example.com","hire_date":"1987-04-19"} 20 {"name":"Smith Ellison","email":"smith.ellison@example.com","hire_date":"1980-12-17"}
Example 14-37 Env: Using a Scalar Macro in a Scalar Expression
The env SQL macro provides a wrapper for the value of the parameter associated with the context namespace USERENV which describes the current session.
CREATE PACKAGE env AS FUNCTION current_user RETURN VARCHAR2 SQL_MACRO(SCALAR); FUNCTION current_edition_name RETURN VARCHAR2 SQL_MACRO(SCALAR); FUNCTION module RETURN VARCHAR2 SQL_MACRO(SCALAR); FUNCTION action RETURN VARCHAR2 SQL_MACRO(SCALAR); END; / CREATE PACKAGE BODY env AS FUNCTION current_user RETURN VARCHAR2 SQL_MACRO(SCALAR) IS BEGIN RETURN q'{SYS_CONTEXT('userenv','SESSION_USER')}'; END; FUNCTION current_edition_name RETURN VARCHAR2 SQL_MACRO(SCALAR) IS BEGIN RETURN q'{SYS_CONTEXT('userenv','CURRENT_EDITION_NAME')}'; END; FUNCTION module RETURN VARCHAR2 SQL_MACRO(SCALAR) IS BEGIN RETURN q'{SYS_CONTEXT('userenv','MODULE')}'; END; FUNCTION action RETURN VARCHAR2 SQL_MACRO(SCALAR) IS BEGIN RETURN q'{SYS_CONTEXT('userenv','ACTION')}'; END; END; /
Select the current user info.
SELECT env.current_user, env.module, env.action FROM DUAL;
SCOTT SQL*PLUS
Example 14-38 Budget : Using a Table Macro in a Table Expression
This example shows the SQL macro named budget used in a table expression to return the total salary in each department for employees for a given job title.
CREATE FUNCTION budget(job VARCHAR2) RETURN VARCHAR2 SQL_MACRO IS
BEGIN
RETURN q'{SELECT deptno, SUM(sal) budget
FROM scott.emp
WHERE job = budget.job
GROUP BY deptno}';
END;
This query shows the SQL macro budget used in a table expression.
SELECT * FROM budget('MANAGER');
Result:
DEPTNO BUDGET ---------- ---------- 20 2975 30 2850 10 2450
Example 14-39 Take: Using a Table Macro with a Polymorphic View
CREATE FUNCTION take (n NUMBER, t DBMS_TF.table_t)
RETURN VARCHAR2 SQL_MACRO IS
BEGIN
RETURN 'SELECT * FROM t FETCH FIRST take.n ROWS ONLY';
END;
/
The query returns the first two rows from table dept.SELECT * FROM take(2, dept);
DEPTNO DNAME LOC ---------- -------------- ------------- 10 ACCOUNTING NEW YORK 20 RESEARCH DALLAS
VAR row_count NUMBER EXEC :row_count := 5 WITH t AS (SELECT * FROM emp NATURAL JOIN dept ORDER BY ename) SELECT ename, dname FROM take(:row_count, t);
ENAME DNAME ---------- -------------- ADAMS RESEARCH ALLEN SALES BLAKE SALES CLARK ACCOUNTING FORD RESEARCH
Example 14-40 Range : Using a Table Macro in a Table Expression
This example creates a SQL macro that generates an arithmetic progression of rows in the range [first, stop]. The first row start with the value first, and each subsequent row's value will be step more than the previous row's value.
- step < 0 and first < stop
- step = 0
- step > 0 and first > stop
/* PACKAGE NAME: GEN * SQL TABLE MACROS: * range(stop : number to generate starting from zero) * range(first : starting number of the sequence (default=0), * stop : generate numbers up to, but not including this number, * step : difference between each number in the sequence (default=1) ) */ CREATE PACKAGE gen IS FUNCTION range(stop NUMBER) RETURN VARCHAR2 SQL_MACRO(TABLE); FUNCTION range(first NUMBER DEFAULT 0, stop NUMBER, step NUMBER DEFAULT 1) RETURN VARCHAR2 SQL_MACRO(TABLE); FUNCTION tab(tab TABLE, replication_factor NATURAL) RETURN TABLE PIPELINED ROW POLYMORPHIC USING gen; FUNCTION describe(tab IN OUT DBMS_TF.TABLE_T, replication_factor NATURAL) RETURN DBMS_TF.DESCRIBE_T; PROCEDURE fetch_rows(replication_factor NATURALN); END gen; / CREATE PACKAGE BODY gen IS FUNCTION range(stop NUMBER) RETURN VARCHAR2 SQL_MACRO(TABLE) IS BEGIN RETURN q'{SELECT ROWNUM-1 n FROM gen.tab(DUAL, stop)}'; END; FUNCTION range(first NUMBER DEFAULT 0, stop NUMBER, step NUMBER DEFAULT 1) RETURN VARCHAR2 SQL_MACRO(TABLE) IS BEGIN RETURN q'{ SELECT first+n*step n FROM gen.range(ROUND((stop-first)/NULLIF(step,0))) }'; END; FUNCTION describe(tab IN OUT DBMS_TF.TABLE_T, replication_factor NATURAL) RETURN DBMS_TF.DESCRIBE_T AS BEGIN RETURN DBMS_TF.DESCRIBE_T(row_replication => true); END; PROCEDURE fetch_rows(replication_factor NATURALN) as BEGIN DBMS_TF.ROW_REPLICATION(replication_factor); END; END gen; /
The gen.get_range SQL macro is used in table expressions.
SELECT * FROM gen.range(5);
0 1 2 3 4
SELECT * FROM gen.range(5, 10);
5 6 7 8 9
SELECT * FROM gen.range(0, 1, step=>0.1);
0 .1 .2 .3 .4 .5 .6 .7 .8 .9
SELECT * FROM gen.range(+5,-6,-2);
5 3 1 -1 -3 -5
Related Topics
- Overview of Polymorphic Table Functions
-
Oracle Database PL/SQL Packages and Types Reference for more information about how to specify the PTF implementation package and use the
DBMS_TF
utilities - Oracle Database Reference for more information about the SQL_MACRO column in the ALL_PROCEDURES view