{"id":2152,"date":"2024-08-07T08:08:26","date_gmt":"2024-08-07T06:08:26","guid":{"rendered":"https:\/\/blog.sqlora.com\/en\/?p=2152"},"modified":"2024-08-08T10:07:15","modified_gmt":"2024-08-08T08:07:15","slug":"sql-macros-changing-the-query-shape-using-parameters","status":"publish","type":"post","link":"https:\/\/blog.sqlora.com\/en\/sql-macros-changing-the-query-shape-using-parameters\/","title":{"rendered":"SQL Macros \u2013 Changing The Query Shape Using Parameters?"},"content":{"rendered":"\n<p>One thing I learned early on when I was getting to know SQL macros is that we cannot replace each and every part of a SQL statement with a macro. In the same way, working with table SQL macros, we cannot represent just any part of the result string by referencing parameters inside it. Sometimes we need to use string concatenation. But why it behaves this way? In this post I will look at the available trace information  about SQL macro expansion during hard parse and try to understand its impact on this restriction. After that we&#8217;ll also look how to overcome it and what to consider.<\/p>\n\n\n\n<!--more-->\n\n\n\n<p>In the previous posts about <a href=\"https:\/\/blog.sqlora.com\/en\/how-to-get-the-final-sql-after-macro-expansion\/\" target=\"_blank\" rel=\"noreferrer noopener\">finding the final SQL<\/a> and other information about <a href=\"https:\/\/blog.sqlora.com\/en\/sql-macros-what-is-in-the-cbo-trace\/\" target=\"_blank\" rel=\"noreferrer noopener\">SQM macro expansion in the CBO trace<\/a> we have already seen how to access CBO trace. Let&#8217;s first change the function introduced there to find the final SQL to be able to additionally output the relevant pieces to show what happens during SQM expansion (Listing 1). You don&#8217;t need to understand the function to follow this post, it is just used to generate the output of SQM expansion, which is indeed interesting. I published it just in case you want to do some tests on your own. And you can find the explanations in the <a href=\"https:\/\/blog.sqlora.com\/en\/how-to-get-the-final-sql-after-macro-expansion\/\" target=\"_blank\" rel=\"noreferrer noopener\">original post<\/a>.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: sql; title: ; notranslate\" title=\"\">\ncreate or replace function sqm_trace_info (P_query in varchar2) \n  return sys.odcivarchar2list pipelined is\n    v_result varchar2(4000);\n    v_tracefile varchar2(100) := &#039;SQM_&#039;||to_char(sysdate, &#039;HH24MISS&#039;);\n    v_dump_trace_cmd varchar2(500);\n    v_sql_id varchar2(20);\n    v_cur sys_refcursor;\n    v_child number;\n    v_trcline varchar2(32000);\nbegin\n  -- get sql_id for the query\n  v_sql_id := dbms_sql_translator.sql_id(p_query);\n  -- parse query \n  open v_cur for p_query;\n  select max(child_number) into v_child from sys.v_$sql where sql_id = v_sql_id;\n  close v_cur;\n  \n  if v_child is not null then  \n    v_dump_trace_cmd := \n       replace(\n          replace(\n             replace( q&#039;&#x5B;\n                begin \n                    DBMS_SQLDIAG.DUMP_TRACE( p_sql_id=&gt;&#039;#sql_id#&#039;\n                                           , p_child_number=&gt; &#039;#child#&#039;\n                                           , p_component=&gt;&#039;Compiler&#039;\n                                           , p_file_id=&gt;&#039;#v_trace_identifier#&#039;);\n                end;]&#039;,&#039;#sql_id#&#039;,v_sql_id)\n          , &#039;#v_trace_identifier#&#039;, v_tracefile)\n       , &#039;#child#&#039;, v_child) ;\n            \n    -- create a trace file            \n    execute immediate v_dump_trace_cmd;\n\n    -- return the query to find the relevant line \n    v_result := q&#039;&#x5B; select x.trcline from v$diag_trace_file_contents \n                match_recognize (\n                    partition by trace_filename\n                    order by line_number\n                    measures payload as trcline\n                        all rows per match\n                    pattern (a | b nc*|c |f n)\n                    define a as (payload like &#039;qksptfSQM_GetTxt(): Anonymous Block%&#039;)\n                    ,      b as (payload like &#039;qksptfSQM_GetTxt(): Macro Text%&#039;)\n                    ,      nc as (payload not like &#039;qksptfSQM_Template(): Template Text%&#039;)\n                    ,      c as (payload like &#039;qksptfSQM_Template(): Template Text%&#039;)  \n                    ,      f as (payload like &#039;Final query after%&#039;)\n                    ) x \n                  where trace_filename like &#039;%#TRACEFILENAME#%&#039; \n                ]&#039;;\n    open v_cur for replace (v_result,&#039;#TRACEFILENAME#&#039;, v_tracefile);\n    loop\n      fetch v_cur into v_trcline;\n        exit when v_cur%notfound;\n        pipe row(v_trcline);\n    end loop;\n  else\n    pipe row (replace(q&#039;&#x5B;The SQL_ID #sql_id# was not found!]&#039;, &#039;#sql_id#&#039;, v_sql_id ));    \n  end if;\nend;\n\/\n<\/pre><\/div>\n\n\n<p><strong><em>Listing 1: Function to show the content from CBO trace file about SQM expansion<\/em><\/strong><\/p>\n\n\n\n<p>Now let&#8217;s try to answer the question from the title of this post. Can I change the shape of the query using parameters? There is no definitive answer. I think, the way it is generally supposed to work, the answer would be NO! And here is why. Let&#8217;s define a table SQL macro as a UNION for two queries which use scalar parameters. Don&#8217;t expect any meaning behind this, it&#8217; just a silly example. And then let&#8217;s use the function defined above to see what happens behind the scene.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: sql; highlight: [35]; title: ; notranslate\" title=\"\">\nSQL&gt; create or replace function sqm_set_op(p_name1 varchar2, p_name2 varchar2) \nreturn varchar2  sql_macro (table) \nis\nbegin\n  return q&#039;&#x5B;SELECT * FROM emp WHERE ename = p_name1 \n                 UNION \n                 SELECT * FROM emp WHERE ename = p_name2]&#039;;\nend;\n\/\nFunction created.\n\nSQL&gt; select * from sqm_trace_info(q&#039;&#x5B;select * from sqm_set_op(&#039;SCOTT&#039;,&#039;KING&#039;)]&#039;)\n\nCOLUMN_VALUE                                                                    \n--------------------------------------------------------------------------------\nqksptfSQM_GetTxt(): Anonymous Block\n===================================\n                                                               \ndeclare                                                         \n                                                              \nbegin                                                           \n:macro_text := &quot;SQM_SET_OP&quot;(:3 ,:4 );                                          \nend;                                                            \n                                                                               \nqksptfSQM_GetTxt(): Macro Text\n==============================\nSELECT * FROM emp WHERE ename = p_name1 \nUNION \nSELECT * FROM emp WHERE ename = p_name2\n                                                                                \nqksptfSQM_Template(): Template Text\n===================================\n\nselect &quot;SYS__$&quot;.* \nfrom (select NULL &quot;P_NAME1&quot;,NULL &quot;P_NAME2&quot; from SYS.DUAL) &quot;SQM_SET_OP&quot;,\n     lateral(SELECT * FROM emp WHERE ename = p_name1 \n                 UNION \n                 SELECT * FROM emp WHERE ename = p_name2) &quot;SYS__$&quot;\n\nFinal query after transformations:******* UNPARSED QUERY IS *******\n\nSELECT &quot;SYS__$&quot;.&quot;EMPNO&quot;        &quot;EMPNO&quot;,\n       &quot;SYS__$&quot;.&quot;ENAME&quot;        &quot;ENAME&quot;,\n       &quot;SYS__$&quot;.&quot;JOB&quot;          &quot;JOB&quot;,\n       &quot;SYS__$&quot;.&quot;MGR&quot;          &quot;MGR&quot;,\n       &quot;SYS__$&quot;.&quot;HIREDATE&quot;     &quot;HIREDATE&quot;,\n       &quot;SYS__$&quot;.&quot;SAL&quot;          &quot;SAL&quot;,\n       &quot;SYS__$&quot;.&quot;COMM&quot;         &quot;COMM&quot;,\n       &quot;SYS__$&quot;.&quot;DEPTNO&quot;       &quot;DEPTNO&quot;\n  FROM ( (SELECT &quot;EMP&quot;.&quot;EMPNO&quot;        &quot;EMPNO&quot;,\n                 &quot;EMP&quot;.&quot;ENAME&quot;        &quot;ENAME&quot;,\n                 &quot;EMP&quot;.&quot;JOB&quot;          &quot;JOB&quot;,\n                 &quot;EMP&quot;.&quot;MGR&quot;          &quot;MGR&quot;,\n                 &quot;EMP&quot;.&quot;HIREDATE&quot;     &quot;HIREDATE&quot;,\n                 &quot;EMP&quot;.&quot;SAL&quot;          &quot;SAL&quot;,\n                 &quot;EMP&quot;.&quot;COMM&quot;         &quot;COMM&quot;,\n                 &quot;EMP&quot;.&quot;DEPTNO&quot;       &quot;DEPTNO&quot;\n            FROM &quot;SCOTT&quot;.&quot;EMP&quot; &quot;EMP&quot;\n           WHERE &quot;EMP&quot;.&quot;ENAME&quot; = &#039;SCOTT&#039;)\n        UNION\n        (SELECT &quot;EMP&quot;.&quot;EMPNO&quot;        &quot;EMPNO&quot;,\n                &quot;EMP&quot;.&quot;ENAME&quot;        &quot;ENAME&quot;,\n                &quot;EMP&quot;.&quot;JOB&quot;          &quot;JOB&quot;,\n                &quot;EMP&quot;.&quot;MGR&quot;          &quot;MGR&quot;,\n                &quot;EMP&quot;.&quot;HIREDATE&quot;     &quot;HIREDATE&quot;,\n                &quot;EMP&quot;.&quot;SAL&quot;          &quot;SAL&quot;,\n                &quot;EMP&quot;.&quot;COMM&quot;         &quot;COMM&quot;,\n                &quot;EMP&quot;.&quot;DEPTNO&quot;       &quot;DEPTNO&quot;\n           FROM &quot;SCOTT&quot;.&quot;EMP&quot; &quot;EMP&quot;\n          WHERE &quot;EMP&quot;.&quot;ENAME&quot; = &#039;KING&#039;)) &quot;SYS__$&quot;;\n                                                                               \n                                                       \n<\/pre><\/div>\n\n\n<p><strong><em>Listing 2: Table SQL macro and its expansion<\/em><\/strong><\/p>\n\n\n\n<p>First, we see an anonymous block to run our macro passing parameters as binds &#8211; nothing special here in our simple case. Second, we can see the macro text returned from our SQL macro. And finally, there is a query we see under the section &#8220;Template Text&#8221;. It is not exactly the final query, but it is a smart way to check that all passed parameters are actually scalar. Look at the line 35. All scalar parameters are selected from dual in one subquery which has the same name as the SQL macro. These parameters are then passed to the actual query (i.e. exactly the return string from SQL macro)  through a LATERAL join.<\/p>\n\n\n\n<p>In this second query inside the LATERAL clause, no parameter substitution has to take place, here we have just parameter names. It is no problem if they are prefixed with the SQL macro function name, because the subquery is also named the same. The only place for substitution is this single subquery selecting from dual.<\/p>\n\n\n\n<p>Now, what happens if we decided to make this macro more &#8220;generic&#8221; and pass the set operator we want to use as a parameter? That is, if we wanted to dynamically change UNION to UNION ALL or MINUS and so on. The Listing 3 shows this approach which is not working, of course.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: sql; title: ; notranslate\" title=\"\">\nSQL&gt; create or replace function sqm_set_op_generic(p_name1 varchar2\n                                            , p_name2 varchar2\n                                            , p_operator varchar2) \nreturn varchar2 sql_macro (table) \nis\nbegin\n  return q&#039;&#x5B;SELECT * FROM emp WHERE ename = p_name1 \n            p_operator \n            SELECT * FROM emp WHERE ename = p_name2]&#039;;\nend;\nFunction created.\nSQL&gt; select * from sqm_set_op_generic(&#039;SCOTT&#039;,&#039;KING&#039;, &#039;UNION&#039;)\n&gt;&gt; select * from sqm_set_op_generic(&#039;SCOTT&#039;,&#039;KING&#039;, &#039;UNION&#039;)\n  *\nError at line 15\nORA-64626: invalid SQL text returned from SQL macro: \nORA-00907: missing right parenthesis\n<\/pre><\/div>\n\n\n<p><strong><em>Listing 3: Using a parameter otherwise than for a scalar expression fails<\/em><\/strong><\/p>\n\n\n\n<p>We cannot generate a CBO trace file in this case because the statement is not valid and not parseable at all. But we can suggest why this happens (Listing 4). We will have all three parameters in the first subquery and the lateral-subquery will reference the parameter P_OPERATOR just as the two other ones. Of course, this go wrong and we see exactly the same error message ORA-00907 that we got when calling the SQM.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: sql; title: ; notranslate\" title=\"\">\n&gt;&gt; select &quot;SYS__$&quot;.* \nfrom (select NULL &quot;P_NAME1&quot;,NULL &quot;P_NAME2&quot;, NULL &quot;P_OPERATOR&quot; from SYS.DUAL) &quot;SQM_SET_OP2&quot;,\n     lateral(SELECT * FROM emp WHERE ename = p_name1 p_operator SELECT * FROM emp WHERE ename = p_name2) &quot;SYS__$&quot;\n                                                        *\nError at line 3\nORA-00907: missing right parenthesis\n<\/pre><\/div>\n\n\n<p><strong><em>Listing 4: The template SQL will fail if we just reference the parameter name.<\/em><\/strong><\/p>\n\n\n<script async src=\"\/\/pagead2.googlesyndication.com\/pagead\/js\/adsbygoogle.js?client=ca-pub-2925154690547867\" crossorigin=\"anonymous\"><\/script><ins class=\"adsbygoogle\" style=\"display:block; text-align:center;\" data-ad-client=\"ca-pub-2925154690547867\" \ndata-ad-slot=\"2727403138\" \ndata-ad-layout=\"in-article\"\ndata-ad-format=\"fluid\"><\/ins>\n<script> \n(adsbygoogle = window.adsbygoogle || []).push({}); \n<\/script>\n\n\n\n<p><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Can we workaround this?<\/h2>\n\n\n\n<p>If we cannot just use the parameter name inside the result string for this purpose, then we can probably concatenate it? No! This is because scalar parameters which can hold text (VARCHAR2, CHAR, CLOB, RAW, JSON &#8211; just what I&#8217;ve tested) are intentionally NULL inside the SQM body. Unfortunately this is not clearly mentioned in the documentation but explained on various blogs, including this one. Listing 5 confirms this behavior once again. Note that I have already used MINUS after the first query and then again concatenated my parameter between the two queries. So if I call the macro using the value &#8220;UNION&#8221; for p_operator, I may expect it to fail because we will have MINUS and UNION next to each other. But this doesn&#8217;t happen, P_OPERATOR is just NULL and can&#8217;t even get it into the query &#8211; we only see MINUS in the result string and the template query.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: sql; title: ; notranslate\" title=\"\">\nSQL&gt; create or replace function sqm_set_op_generic(p_name1 varchar2\n                                            , p_name2 varchar2\n                                            , p_operator varchar2) \nreturn varchar2 sql_macro (table) \nis\nbegin\n  return &#039;SELECT * FROM emp WHERE ename = p_name1 MINUS &#039; \n          ||  p_operator || \n         &#039;SELECT * FROM emp WHERE ename = p_name2&#039;;\nend;\nFunction created.\n\nSQL&gt; select * from sqm_trace_info(q&#039;&#x5B;select * from sqm_set_op_generic(&#039;SCOTT&#039;,&#039;KING&#039;, &#039;UNION&#039;)]&#039;)\n\nCOLUMN_VALUE                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            \n-----------------------------------------------------------\n...                                                         \nqksptfSQM_GetTxt(): Macro Text\n==============================\nSELECT * FROM emp WHERE ename = p_name1 MINUS SELECT * FROM emp WHERE ename = p_name2\n                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       \nqksptfSQM_Template(): Template Text\n===================================\n\nselect &quot;SYS__$&quot;.* \nfrom (select NULL &quot;P_NAME1&quot;,NULL &quot;P_NAME2&quot;,NULL &quot;P_OPERATOR&quot; from SYS.DUAL) &quot;SQM_SET_OP_GENERIC&quot;,\n     lateral(SELECT * FROM emp WHERE ename = p_name1 MINUS SELECT * FROM emp WHERE ename = p_name2) &quot;SYS__$&quot;                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       \n\n<\/pre><\/div>\n\n\n<p><strong><em>Listing 5: String parameters are NULL and not suitable for concatenation<\/em><\/strong><\/p>\n\n\n\n<p>We cannot evaluate or use (including concatenation) such parameters directly in the body. The only thing we can do, is to put them into the result string. The real parameter values are only substituted after the execution of the macro. The idea behind this &#8220;nullifying&#8221; of parameters is to mitigate the risks of SQL injection. There is at least one known workaround, which was suggested by <a href=\"https:\/\/stewashton.wordpress.com\/\" target=\"_blank\" rel=\"noreferrer noopener\">Stew Ashton<\/a> and can also be found on this site: using collection data types. But before I introduce it again, I would like to emphasize it once again: if you decide to use this workaround and to bypass this protection (like on the picture illustrating this post \ud83d\ude42 ), then you are yourself responsible for the risks! Check the input in you SQL macros!<\/p>\n\n\n\n<p>The Listing 6 shows this approach. Just define your own collection or use some of the predefined ones such as for example <strong><em>sys.odcivarchar2list<\/em><\/strong> and declare the parameter to be of this type. You can then concatenate the parameter value and it will work. Don&#8217;t forget to sanitize your input! <\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: sql; title: ; notranslate\" title=\"\">\nSQL&gt; create or replace type t_operator as varray(1) of varchar2(20);\n  2  \/\n\nType T_OPERATOR compiled\n\nSQL&gt; create or replace function sqm_set_op_generic(p_name1 varchar2\n  2                                              , p_name2 varchar2\n  3                                              , p_operator t_operator) \n  4  return varchar2 sql_macro (table) \n  5  is\n  6  begin\n  7    if not regexp_like(trim(upper(p_operator(1))), \t\t \n  8  \t &#039;^(UNION|MINUS|INTERSECT|EXCEPT){1}((\\s)+ALL){0,1}$&#039;) then \n  9        raise_application_error(-20003,&#039;Invalid set operator&#039;); \n 10    end if;\n 11  \n 12    return &#039;SELECT * FROM emp WHERE ename = p_name1 &#039; \n 13            ||  p_operator(1) || \n 14           &#039; SELECT * FROM emp WHERE ename = p_name2&#039;;\n 15  end;  \n 16  \/\n\nFunction SQM_SET_OP_GENERIC compiled\n\nSQL&gt; select * from sqm_trace_info(q&#039;&#x5B;select * from sqm_set_op_generic(&#039;SCOTT&#039;,&#039;KING&#039;, t_operator(&#039;UNION&#039;))]&#039;);\n\nCOLUMN_VALUE                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            \n-----------------------------------------------------\nqksptfSQM_GetTxt(): Anonymous Block\n===================================\n                                                               \ndeclare                                                         \n                                                              \nbegin                                                           \n:macro_text := &quot;SQM_SET_OP_GENERIC&quot;(:3 ,:4 ,&quot;T_OPERATOR&quot;(&#039;UNION&#039;));                                          \nend;                                                            \nqksptfSQM_GetTxt(): Macro Text\n==============================\nSELECT * FROM emp WHERE ename = p_name1 UNION SELECT * FROM emp WHERE ename = p_name2\n\nqksptfSQM_Template(): Template Text\n===================================\n\nselect &quot;SYS__$&quot;.* \nfrom (select NULL &quot;P_NAME1&quot;,NULL &quot;P_NAME2&quot;,NULL &quot;P_OPERATOR&quot; from SYS.DUAL) &quot;SQM_SET_OP_GENERIC&quot;,\n     lateral(SELECT * FROM emp WHERE ename = p_name1 UNION SELECT * FROM emp WHERE ename = p_name2) &quot;SYS__$&quot;\n<\/pre><\/div>\n\n\n<p><strong><em>Listing 6: Using collection data types at your own risk<\/em><\/strong><\/p>\n\n\n\n<p>The better approach would be in my opinion to make such generic SQL macro be a private function in a package body and offer multiple specific versions which don&#8217;t allow any additional parameters like in Listing 7. Once again, it doesn&#8217;t make any sense to define those macros with hard coded &#8220;select from emp&#8221; &#8211; it was just to illustrate the problem.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: sql; title: ; notranslate\" title=\"\">\nSQL&gt; create or replace package  sqm_set_ops as\n  2  \n  3  function set_operator_union(p_name1 varchar2, p_name2 varchar2) return clob sql_macro (table);\n  4  \n  5  function set_operator_union_all(p_name1 varchar2, p_name2 varchar2) return clob sql_macro (table);\n  6  \n  7  end;\n  8  \/\n\nPackage SQM_SET_OPS compiled\n\nSQL&gt; \nSQL&gt; create or replace package body sqm_set_ops as\n  2  \n  3  function sqm_set_op_generic(p_name1 varchar2\n  4                                              , p_name2 varchar2\n  5                                              , p_operator t_operator) \n  6  return varchar2 sql_macro (table) \n  7  is\n  8  begin\n  9    if not regexp_like(trim(upper(p_operator(1))), \t\t \n 10  \t &#039;^(UNION|MINUS|INTERSECT|EXCEPT){1}((\\s)+ALL){0,1}$&#039;) then \n 11        raise_application_error(-20003,&#039;Invalid set operator&#039;); \n 12    end if;\n 13  \n 14    return &#039;SELECT * FROM emp WHERE ename = p_name1 &#039; \n 15            ||  p_operator(1) || \n 16           &#039; SELECT * FROM emp WHERE ename = p_name2&#039;;\n 17  end; \n 18  \n 19  function set_operator_union(p_name1 varchar2, p_name2 varchar2) return clob sql_macro (table) is\n 20  begin\n 21    return sqm_set_op_generic(p_name1,p_name2,t_operator(&#039;UNION&#039;) );              \n 22  end;  \n 23  \n 24  function set_operator_union_all(p_name1 varchar2, p_name2 varchar2) return clob sql_macro (table) is\n 25  begin\n 26    return sqm_set_op_generic(p_name1,p_name2,t_operator(&#039;UNION ALL&#039;) );              \n 27  end;  \n 28  \n 29  end;\n 30  \/\n\nPackage Body SQM_SET_OPS compiled\n<\/pre><\/div>\n\n\n<p><strong><em>Listing 7:  Hiding generic implementations in a package body<\/em><\/strong><\/p>\n\n\n\n<p>Looking at the SQM expansion pieces in the CBO trace was quite useful for me to understand the nature of some limitations we have using SQL macros. In the next post I will again look at it and try to understand how it might be related to the limitations when using SQL macros and the WITH clause.<\/p>\n\n\n<script async src=\"\/\/pagead2.googlesyndication.com\/pagead\/js\/adsbygoogle.js?client=ca-pub-2925154690547867\" crossorigin=\"anonymous\"><\/script><ins class=\"adsbygoogle\" style=\"display:block; text-align:center;\" data-ad-client=\"ca-pub-2925154690547867\" \ndata-ad-slot=\"2727403138\" \ndata-ad-layout=\"in-article\"\ndata-ad-format=\"fluid\"><\/ins>\n<script> \n(adsbygoogle = window.adsbygoogle || []).push({}); \n<\/script>\n\n\n<div class=\"crp-list-container\"><h3 class=\"crp-list-title\">Related Posts<\/h3><ul class=\"crp-list\"><li class=\"crp-list-item crp-list-item-image-none\"><div class=\"crp-list-item-title\"><a href=\"https:\/\/blog.sqlora.com\/en\/how-to-get-the-final-sql-after-macro-expansion\/\" target=\"_blank\">How to get the final SQL after macro expansion<\/a><\/div><\/li><li class=\"crp-list-item crp-list-item-image-none\"><div class=\"crp-list-item-title\"><a href=\"https:\/\/blog.sqlora.com\/en\/sql-macros-what-is-in-the-cbo-trace\/\" target=\"_blank\">SQL Macros &#8211; what is in the CBO trace?<\/a><\/div><\/li><li class=\"crp-list-item crp-list-item-image-none\"><div class=\"crp-list-item-title\"><a href=\"https:\/\/blog.sqlora.com\/en\/sql-macros-and-the-with-clause\/\" target=\"_blank\">SQL macros and the WITH clause<\/a><\/div><\/li><\/ul><\/div>","protected":false},"excerpt":{"rendered":"<p>One thing I learned early on when I was getting to know SQL macros is that we cannot replace each and every part of a SQL statement with a macro. In the same way, working with table SQL macros, we cannot represent just any part of the result string by referencing parameters inside it. Sometimes [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":2241,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[2,3,100],"tags":[103],"class_list":["post-2152","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oracle","category-sql","category-sql-macros","tag-sql-macros"],"_links":{"self":[{"href":"https:\/\/blog.sqlora.com\/en\/wp-json\/wp\/v2\/posts\/2152","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blog.sqlora.com\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.sqlora.com\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.sqlora.com\/en\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.sqlora.com\/en\/wp-json\/wp\/v2\/comments?post=2152"}],"version-history":[{"count":36,"href":"https:\/\/blog.sqlora.com\/en\/wp-json\/wp\/v2\/posts\/2152\/revisions"}],"predecessor-version":[{"id":2276,"href":"https:\/\/blog.sqlora.com\/en\/wp-json\/wp\/v2\/posts\/2152\/revisions\/2276"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/blog.sqlora.com\/en\/wp-json\/wp\/v2\/media\/2241"}],"wp:attachment":[{"href":"https:\/\/blog.sqlora.com\/en\/wp-json\/wp\/v2\/media?parent=2152"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.sqlora.com\/en\/wp-json\/wp\/v2\/categories?post=2152"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.sqlora.com\/en\/wp-json\/wp\/v2\/tags?post=2152"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}