SQL macros are ridiculously powerful and I think as time goes on, we are going to see more and more innovative usages of this super cool tech. It may seem a strange analogy, but I liken them to analytic window functions from way back in version 8.1.6, in that the syntax arrives, people scratch their head for a while wondering how it might be useful, and suddenly BOOM, thousands of previously complex problems are become trivially solved. I don’t think we have even touched the tip of the iceberg when it comes to SQL macros.However, because they are in essence a string “masequerading” as a SQL query, one facet of SQL macros you need to take care with is dependency tracking. For example, here’s a simple SQL macro that refers to the EMP table.


SQL> create or replace
  2  function f1 return varchar2 sql_macro(table) is
  3  begin
  4    return q'{select empno, ename from emp where deptno = 10}';
  5  end;
  6  /

Function created.

If some maintenance work was planned for the EMP table, I might delve into DBA_DEPENDENCIES to see what might be impacted, and as you can see, the SQL Macro is nowhere to be seen.


SQL> select referenced_owner||'.'||referenced_name dep
  2  from dba_dependencies
  3  where name = 'F1';

DEP
-------------------------------------------------------------------
SYS.STANDARD

I must admit, I’d love to the have RELIES_ON style clause for such cases, but unfortunately that is limited to the result cache.


SQL> create or replace
  2  function f1 return varchar2 sql_macro(table) result_cache relies_on (emp)  is
  3  begin
  4    return q'{select empno, ename from emp where deptno = 10}';
  5  end;
  6  /

Warning: Function created with compilation errors.

SQL> sho err
Errors for FUNCTION F1:

LINE/COL ERROR
-------- -----------------------------------------------------------------
1/10     PLS-00778: PARTITION BY, ORDER BY, CLUSTER BY, or PARALLEL_ENABLE
         are not allowed for SQL macros

So I thought I’d propose a couple of options that you might want to consider if you’d like to track dependencies in the usual way.One option would be to create a view which references the macro. You could come up with whatever naming scheme you want to indicate that this is probably not a view that is used in normally operations, and similarly, I’ve thrown in a WHERE 1=0 so that even if someone did execute a statement against it, there is no chance of any resource impact since the optimizer will short circuit the entire querty into a NO-OP due to the always false condition.


SQL> create or replace
  2  view never_to_be_used as
  3  select * from f1() where 1=0;

View created.

Even though the view references the macro, which in turn is just a string, in order to parse out the columns for the view definition, you can see that we have picked up the desired dependency information against the EMP table


SQL> select referenced_owner||'.'||referenced_name dep
  2  from dba_dependencies
  3  where name = 'NEVER_TO_BE_USED';

DEP
-----------------------------------------------------------
SCOTT.F1
SCOTT.EMP

Another option you could consider is to add an appropriate dependency into the SQL macro function itself. In the example below, I’ve put a reference to EMP via a ROWTYPE variable, which is unused in the function.


SQL> create or replace
  2  function f1 return varchar2 sql_macro(table) is
  3    l_noop emp%rowtype;
  4  begin
  5    return q'{select empno, ename from emp where deptno = 10}';
  6  end;
  7  /

Function created.

SQL> select referenced_owner||'.'||referenced_name dep
  2  from dba_dependencies
  3  where name = 'F1';

DEP
-------------------------------------------------------------------
SYS.STANDARD
SCOTT.EMP

Of course, one of the cool things with macros is that even the table can be provided at run time, so knowing the appropriate dependencies at compilation time would not be possible. For example:


SQL> create or replace
  2  function f1(tname dbms_tf.table_t) return varchar2 sql_macro(table) is
  3  begin
  4    return q'{select empno, ename from tname where deptno = 10}';
  5  end;
  6  /

Function created.

SQL> select * from f1(emp);

     EMPNO ENAME
---------- ----------
      7782 CLARK
      7839 KING
      7934 MILLER

However, for such macros, DBMS_TF would be a dependency, which could serve as a trigger that any references elsewhere in the code to the function F1 need to be checked for dependency analysis.


SQL> select referenced_owner||'.'||referenced_name dep
  2  from dba_dependencies
  3  where name = 'F1';

DEP
--------------------------------------------------------
SYS.STANDARD
PUBLIC.DBMS_TF

If can think of other means to track the dependencies easily, please drop me a comment.

6 responses to “Tracking SQL macro dependencies”

  1. But the biggest problem with SQL macros is you cannot use them in WITH clauses, or use WITH within them. (If that makes sense).

    Change that, and they’re a winner.

  2. iudithd5bf8e4d8d Avatar
    iudithd5bf8e4d8d

    Hi Connor,

    As the SQL Macro feature was created exactly for being able “to repeat” the same functionality for a bunch of objects,
    both existing ones are not yet created ones, I don’t think that we can say that the SQL Macro function “statically depends” on any specific object
    that is passed as a parameter to the function.

    The dependency is only “created” at run time, when parsing a query.

    One possible way to get “kind of” a dependency table is to create your own dependency table, and “log” into that table
    the relevant table name(s) from inside the SQL Macro, aka, while the SQL Macro is running, by calling a procedure defined as autonomous transaction.
    This way you will have a “dependency list” of the SQL Macro on all the tables on which it was effectively used.

    Following is a short demo:

    — loging table dependencies for SQL MACRO

    create table sql_macro_dependencies (
    function_owner varchar2(30),
    function_name varchar2(30),
    table_owner varchar2(30),
    table_name varchar2(30),
    primary key ( function_owner, function_name, table_owner, table_name )
    )
    /

    — logging the SQL Macro usage

    create or replace procedure p_log_sql_macro (
    p_function_owner in varchar2, p_function_name in varchar2, p_table_owner in varchar2, p_table_name in varchar2)
    as
    pragma autonomous_transaction;
    begin

    merge into sql_macro_dependencies t
    using (select *
    from dual) s
    on (
    t.function_owner = p_function_owner
    and t.function_name = p_function_name
    and t.table_owner = p_table_owner
    and t.table_name = p_table_name
    )
    when not matched then
    insert ( function_owner, function_name, table_owner, table_name )
    values (p_function_owner, p_function_name, p_table_owner, p_table_name) ;

    commit;
    end;
    /

    — a simple call to the log procedure

    create or replace function my_macro (p_tab dbms_tf.table_t)
    return varchar2 sql_macro
    as
    p_log_sql_macro ($$PLSQL_UNIT_OWNER, $$PLSQL_UNIT, p_tab.table_schema_name, p_tab.table_name);

    return q'{select * from p_tab}’;
    end;
    /

    A small and annoying remark, however, regarding this code:

    The fields “table_schema_name” and “table_name” of record type DBMS_TF.table_t
    are not recognized in 19c (ex. in LiveSQL – now 19.17), where it produces a strange compilation error, unrelated to the problem …

    But, the same works ok in 23c and probably in 21c as well, though, the two missing fields still *do not* appear in Oracle documentation
    of package DBMS_TF.

    In 23c I could test successfully the entire example.

    Maybe this problem can be corrected in 19c as well …

    I made on purpose the procedure p_log_sql_macro to be “dbms_tf agnostic”, so that it could be easily used for other types of dependencies
    as well (ex. “static” ones, that are probably also recorded by DBA_DEPENDENCIES).

    @Fatmartinr,
    Regarding the WITH clause, a SQL Macro *can” return a query that contains a WITH clause.
    What is NOT allowed, at least as by now, is to call a SQL Macro from a WITH clause of a query.

    Cheers & Best Regards,
    Iudith Mentzel

    1. Excellent points ludith, although I think would challenge the statement: “As the SQL Macro feature was created exactly for being able “to repeat” the same functionality for a bunch of objects”.

      My prediction is that SQL macros will overwhelmingly be used for primarily for parameterised views in the short term, and only once they gain more popularity though this usage will they expand to other areas. That’s why I think the dependency mechanism at *compile* time will be useful.

  3. Erik van Roon Avatar
    Erik van Roon

    First tthing that comes to mind is to use annotations (as of 23ai) for that.

    Obviously that won’t make it show up in dba_dependencies, but if coding standards and peer review ensure that it’s done when needed and properly than it’s as easy as adding a union of an annotations query to your standard dependencies query to find everything that depends on table emp.

  4. Erik van Roon Avatar
    Erik van Roon

    First tthing that comes to mind is to use annotations (as of 23ai) for that.

    Obviously that won’t make it show up in dba_dependencies, but if coding standards and peer review ensure that it’s done when needed and properly than it’s as easy as adding a union of an annotations query to your standard dependencies query to find everything that depends on table emp.

  5. iudithd5bf8e4d8d Avatar
    iudithd5bf8e4d8d

    Hi Connor,

    There were recently two very good posts published on the topic of using SQL macros for parametrised views,
    written by two of the topmost level gurus.

    The ideas are somehow similar, in the sense that they use/keep a “classic” view in place,
    with the SQL Macro being used for “pushing” the real parameters into the view,
    so, the dependency tracking stays as it is for “static” objects.

    https://blog.sqlora.com/en/parameterized-views-with-sql-macros-part-2/

    https://stewashton.wordpress.com/2024/04/24/sql-table-macros-14-parameterized-views/

    … and, for that matter … we have already used “parametrised views” far back in the past, from the very first versions of Oracle …
    by using a parameter table, having as primary key the current SESSIONID and including it in the view definition,
    or, some versions later, by using CONTEXT variables.

    The SQL Macro feature is however much more powerful than just such a usage.

    Cheers & Best Regards,
    Iudith Mentzel

Got some thoughts? Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Trending

Blog at WordPress.com.