{"id":1938,"date":"2024-01-03T10:30:20","date_gmt":"2024-01-03T08:30:20","guid":{"rendered":"https:\/\/blog.sqlora.com\/en\/?p=1938"},"modified":"2024-01-03T10:30:21","modified_gmt":"2024-01-03T08:30:21","slug":"sql-macros-what-is-in-the-cbo-trace","status":"publish","type":"post","link":"https:\/\/blog.sqlora.com\/en\/sql-macros-what-is-in-the-cbo-trace\/","title":{"rendered":"SQL Macros &#8211; what is in the CBO trace?"},"content":{"rendered":"\n<p>In the previous post I showed how to get to the final query after SQL macro expansion using CBO trace. But there are still a few interesting things in the trace file that are worth looking at.<\/p>\n\n\n\n<!--more-->\n\n\n\n<p>Here are three SQL macros we will test with: two scalar ones and one table macro:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: sql; title: ; notranslate\" title=\"\">\ncreate or replace function duration_years \n                    (p_date_from in date\n                   , p_date_to in date) \nreturn varchar2 sql_macro(scalar) as\nbegin\n   return q&#039;&#x5B; floor(months_between (p_date_to, p_date_from)\/12) ]&#039;;\nend;\n\ncreate or replace FUNCTION upper_macro (p_param in varchar2) \nreturn varchar2 sql_macro(scalar) as\nbegin\n   return &#039;UPPER(p_param)&#039;;\nend;\n\ncreate or replace function top_n (p_tab in dbms_tf.table_t, p_limit in number\n\t\t\t  , p_order in dbms_tf.columns_t) \nreturn varchar2 sql_macro is \nv_order_list varchar2(2000);\nbegin\n  -- turn PL\/SQL table to comma separated list for ORDER BY clause\n  select listagg(replace(column_value,&#039;&quot;&#039;),&#039;,&#039;) into v_order_list from table (p_order);\n\n  return &#039;select * from p_tab order by &#039;||v_order_list||\n         &#039; fetch first p_limit rows only&#039;;\nend;\n\n<\/pre><\/div>\n\n\n<p><strong><em>Listing 1<\/em><\/strong><\/p>\n\n\n\n<p>We&#8217;ll test with the following SQL: <\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: sql; highlight: [26]; title: ; notranslate\" title=\"\">\nselect ename\n,      duration_years(hiredate, sysdate) as years2\n,      duration_years(date &#039;2001-01-01&#039;, sysdate) as years_y2k1\n,      duration_years(:p_bind, sysdate) as years_bind1\n,      upper_macro(ename) as ename\n,      upper_macro(&#039;adams&#039;) as adams\nfrom   top_n(emp,10, columns(empno)) e;\n\n<\/pre><\/div>\n\n\n<p><strong><em>Listing 2<\/em><\/strong><\/p>\n\n\n\n<p>Now we can resolve to the final SQL statement using the function introduced in the <a href=\"https:\/\/blog.sqlora.com\/en\/how-to-get-the-final-sql-after-macro-expansion\/\" target=\"_blank\" rel=\"noreferrer noopener\">previous post<\/a>.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: sql; highlight: [18]; title: ; notranslate\" title=\"\">\nselect *\nfrom   resolve_sqm(\n            q&#039;&#x5B;select ename\n               ,      duration_years(hiredate, sysdate) as years2\n               ,      duration_years(date &#039;2001-01-01&#039;, sysdate) as years_y2k1\n               ,      duration_years(:p_bind, sysdate) as years_bind1\n               ,      upper_macro(ename) as ename\n               ,      upper_macro(&#039;adams&#039;) as adams\n               from   top_n(emp,10, columns(empno)) e]&#039;) k;\n\n-- final SQL (formatted)\nSELECT\n    &quot;from$_subquery$_009&quot;.&quot;ENAME&quot;     &quot;ENAME&quot;,\n    floor(months_between(sysdate@!, &quot;from$_subquery$_009&quot;.&quot;HIREDATE&quot;) \/ 12) &quot;YEARS&quot;,\n    floor(months_between(sysdate@!,TO_DATE(&#039; 2001-01-01 00:00:00&#039;, &#039;syyyy-mm-dd hh24:mi:ss&#039;)) \/ 12)  &quot;YEARS_Y2K&quot;,\n    floor(months_between(sysdate@!, :b1) \/ 12)                              &quot;YEARS_BIND&quot;,\n    upper(&quot;from$_subquery$_009&quot;.&quot;ENAME&quot;)  &quot;ENAME&quot;,\n    &#039;ADAMS&#039;                               &quot;ADAMS&quot;\nFROM\n    (\n        SELECT\n            &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            &quot;EMP&quot;.&quot;EMPNO&quot;    &quot;rowlimit_$_0&quot;,\n            ROW_NUMBER() OVER( ORDER BY &quot;EMP&quot;.&quot;EMPNO&quot;) &quot;rowlimit_$$_rownumber&quot;\n        FROM &quot;EMP&quot;\n    ) &quot;from$_subquery$_009&quot;\nWHERE &quot;from$_subquery$_009&quot;.&quot;rowlimit_$$_rownumber&quot; &lt;= 10\nORDER BY &quot;from$_subquery$_009&quot;.&quot;rowlimit_$_0&quot;  \n<\/pre><\/div>\n\n\n<p><strong><em>Listing 3<\/em><\/strong><\/p>\n\n\n\n<p>First, we can inspect the final query. There are some interesting points there. Notice that with the resolve macro from the previous post we get the &#8220;final&#8221; query, that means after ALL transformations. So we can expect some changes in the initial query that may have nothing to do with expanding of SQL macros at all. Here, we can see how Oracle actually implements the Row Limiting Clause (introducing an inline subquery with ROW_NUMBER() and filtering afterwards). Also look at the line 18: obviously Oracle has evaluated the expression returned from the SQL macro already at parse time.  <\/p>\n\n\n\n<p>Now let&#8217;s then look at the contents of the trace file:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: sql; title: ; notranslate\" title=\"\">\nselect  payload \nfrom    sys.V_$diag_trace_file_contents\nwhere  trace_filename = &#039;FREE_ora_4911_SQM_205253.trc&#039;;   \n<\/pre><\/div>\n\n\n<p>The first section that has something to do with SQL macros looks like this:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: sql; title: ; notranslate\" title=\"\">\nqksptfSQM_GetTxt(): Anonymous Block\n===================================\n                                                               \ndeclare                                                         \nt0 DBMS_TF.Table_t := DBMS_TF.Table_t(column =&gt; :0, table_schema_name =&gt; &#039;&quot;ONFTEST&quot;&#039;, table_name =&gt; &#039;&quot;EMP&quot;&#039;);\n                                                              \nbegin                                                           \n:macro_text := &quot;TOP_N&quot;(t0,10,:5 );                                          \nend; \n\nqksptfSQM_GetTxt(): Macro Text\n==============================\n\nSELECT * FROM p_tab ORDER BY EMPNO FETCH FIRST p_limit ROWS ONLY\n\nqksptfSQM_Template(): Template Text\n===================================\n\nwith  P_TAB as (select  \/*+ INLINE *\/ * from &quot;ONFTEST&quot;.&quot;EMP&quot;)\nselect &quot;SYS__$&quot;.* \nfrom (select NULL &quot;P_LIMIT&quot; from SYS.DUAL) &quot;TOP_N&quot;,\n     lateral(SELECT * FROM p_tab ORDER BY EMPNO FETCH FIRST p_limit ROWS ONLY) &quot;SYS__$&quot;\n\n<\/pre><\/div>\n\n\n<p><strong><em>Listing 4<\/em><\/strong><\/p>\n\n\n\n<h4 class=\"wp-block-heading\">qksptfSQM_GetTxt(): Anonymous Block<\/h4>\n\n\n\n<p>Obviously, the anonymous PL\/SQL block is how Oracle calls the table SQL macro to get the SQL fragment back. The variable <strong>t0<\/strong> of the type <strong>DBMS_TF.TABLE_T<\/strong> is defined and populated. A bind variable :0 is of type <strong>DBMS_TF.TABLE_COLUMNS_T<\/strong> and it is a collection of records containing column metadata. Oracle takes care of the correct population of the record structure behind this variable. The bind variable :5 is the column list.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">qksptfSQM_GetTxt(): Macro Text. <\/h4>\n\n\n\n<p>We can see the SQL text returned from the macro function &#8211; no surprises here.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">qksptfSQM_Template(): Template Text<\/h4>\n\n\n\n<p>I&#8217;m unsure about this: what exactly is this template for? We can see how the table used as a macro parameter is queried in the WITH-clause (CTE). Then we see the scalar parameter being selected from dual and then passed to the main query using a lateral join. But this subquery, selecting from dual, looks like a template where the actual parameter values have to be substituted for NULL&#8217;s.  Once that has happened, the whole query could already be the final one (let&#8217;s leave the scalar SQL macros out of the equation for now). But it is not! The final query which we will see later in the trace file doesn&#8217;t have any CTE or lateral joins!<\/p>\n\n\n\n<p><\/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<p>Next we can find similar blocks but for scalar macros this time (Listing 4). It starts the same: anonymous block to obtain the macro text, then showing it. But the template looks different this time. The macro expression is selected from a subquery which in turn selects all parameters from DUAL. Again all the parameters in this subquery are NULL.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: sql; title: ; notranslate\" title=\"\">\nqksptfSQM_GetTxt(): Anonymous Block\n===================================\n                                                               \ndeclare                                                         \n                                                              \nbegin                                                           \n:macro_text := &quot;UPPER_MACRO&quot;(:3 );                                          \nend;                                                            \n\nqksptfSQM_GetTxt(): Macro Text\n==============================\n\n\nUPPER(p_param)\n\nqksptfSQM_Template(): Template Text\n===================================\n\nselect (UPPER(p_param)) \nfrom (select NULL &quot;P_PARAM&quot; from SYS.DUAL) &quot;UPPER_MACRO&quot;\n<\/pre><\/div>\n\n\n<p><strong><em>Listing 5<\/em><\/strong><\/p>\n\n\n\n<p>The difference between the two calls of upper_macro is the parameter being passed once as bind variable and once as NULL (compare Listings 5 and 6). You may think, the bind variable stays for the invocation with a column name and NULL for a literal? &#8211; Wrong! It is the other way round. How do i know? I&#8217;ve also tested these two invocations separately. Weird enough: the &#8220;Peeked values of the binds in SQL statement&#8221; section is completely empty, but afterwards we see the result of the function evaluation directly in the final query.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: sql; title: ; notranslate\" title=\"\">\ndeclare                                                         \n                                                              \nbegin                                                           \n:macro_text := &quot;UPPER_MACRO&quot;(NULL);                                          \nend;\n<\/pre><\/div>\n\n\n<p><strong><em>Listing 6<\/em><\/strong><\/p>\n\n\n\n<p>The invocation with the column name passed uses NULL as parameter. What sounds weird is actually quite logical: we are currently parsing and don&#8217;t have any access to column values, you cannot inspect data at this phase.<\/p>\n\n\n\n<p>The following sections are looking similar, showing the invocations of the next macro DURATION_YEARS. There are three different invocations resulting in different <strong>qksptfSQM_GetTxt(): Anonymous Block<\/strong> sections, but resulting in identical  <strong>qksptfSQM_GetTxt(): Macro Text<\/strong> and <strong>qksptfSQM_Template():  Template Text<\/strong> sections. I placed my own comments how they relate to the invocations in the test SQL statement (Listing 7):<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: sql; title: ; notranslate\" title=\"\">\n-- Invocation duration_years(:p_bind, sysdate) as years_bind1\nqksptfSQM_GetTxt(): Anonymous Block\n===================================\ndeclare                                                         \n                                                              \nbegin                                                           \n:macro_text := &quot;DURATION_YEARS&quot;(:3 ,NULL);                                          \nend; \n\n...\n\n-- Invocation duration_years(date &#039;2001-01-01&#039;, sysdate) as years_y2k1\nqksptfSQM_GetTxt(): Anonymous Block\n===================================\ndeclare                                                         \n                                                              \nbegin                                                           \n:macro_text := &quot;DURATION_YEARS&quot;(TO_DATE(&#039; 2001-01-01 00:00:00&#039;, &#039;syyyy-mm-dd hh24:mi:ss&#039;),NULL);                                          \nend;\n\n...\n\n-- Invocation duration_years(hiredate, sysdate) as years2\nqksptfSQM_GetTxt(): Anonymous Block\n===================================\n                                                               \ndeclare                                                         \n                                                              \nbegin                                                           \n:macro_text := &quot;DURATION_YEARS&quot;(NULL,NULL);                                          \nend;                                                            \n\nqksptfSQM_GetTxt(): Macro Text\n==============================\n\n floor(months_between (p_date_to, p_date_from)\/12) \n\nqksptfSQM_Template(): Template Text\n===================================\n\nselect ( floor(months_between (p_date_to, p_date_from)\/12) ) \nfrom (select NULL &quot;P_DATE_FROM&quot;,NULL &quot;P_DATE_TO&quot; from SYS.DUAL) &quot;DURATION_YEARS&quot;\n\n<\/pre><\/div>\n\n\n<p><strong><em>Listing 7<\/em><\/strong><\/p>\n\n\n\n<p>Again, we can&#8217;t see what exactly happens with those identical &#8220;template texts&#8221;. The fact is, they do not become part of the final SQL in that form. Instead, the expression as we see under &#8220;Macro Text&#8221; section is there. Noteworthy how these three expressions are different in the final query:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: sql; title: ; notranslate\" title=\"\">\nfloor(months_between(sysdate@!, &quot;from$_subquery$_009&quot;.&quot;HIREDATE&quot;) \/ 12) &quot;YEARS&quot;,\nfloor(months_between(sysdate@!,TO_DATE(&#039; 2001-01-01 00:00:00&#039;, &#039;syyyy-mm-dd hh24:mi:ss&#039;)) \/ 12)  &quot;YEARS_Y2K&quot;,\nfloor(months_between(sysdate@!, :b1) \/ 12)                              &quot;YEARS_BIND&quot;,\n<\/pre><\/div>\n\n\n<p><strong><em>Listing 8<\/em><\/strong><\/p>\n\n\n\n<p>Of course, SYSDATE is not evaluated at parse time, but goes directly to the final query. Also we can see the column reference, the date literal and the bind variable in the final query &#8211; as we have used them for macro invocation &#8211; no surprise here.  <\/p>\n\n\n\n<p>It is worth mentioning, that we can already find the query after SQL macro expansion earlier in the trace file, being the part of the output sections for some (not all) query transformations. But it is harder to find the right one: sometimes we see only parts of the original query and so on. That&#8217;s why I&#8217;m looking at the section &#8220;<em>Final query after transformations<\/em>&#8220;.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">DML Statements<\/h4>\n\n\n\n<p>You can use SQL macros in DML statements, for example  like this <\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: sql; title: ; notranslate\" title=\"\">\nupdate emp set ename = upper_macro(ename);\n<\/pre><\/div>\n\n\n<p><strong><em>Listing 9<\/em><\/strong><\/p>\n\n\n\n<p>Unfortunately, we cannot see the final statement in the trace file in this case.  The UPDATE SQL can only be seen in the form as in the Listing 9 &#8211; with SQL macros. In the &#8220;Final query after transformations&#8221; section we just see this:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: sql; title: ; notranslate\" title=\"\">\nFinal query after transformations:******* UNPARSED QUERY IS *******\n\nSELECT 0 FROM &quot;ONFTEST&quot;.&quot;EMP&quot; &quot;EMP&quot;\n<\/pre><\/div>\n\n\n<h4 class=\"wp-block-heading\">Conclusion<\/h4>\n\n\n\n<p>Looking at the CBO trace file, we were able to get some insights how SQL macros are called to get back the macro text. Unfortunately,  we can&#8217;t get the final statement for DML like UPDATE.<\/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\/oracle-20c-sql-macros\/\" target=\"_blank\">Oracle 20c: SQL Macros<\/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-changing-the-query-shape-using-parameters\/\" target=\"_blank\">SQL Macros \u2013 Changing The Query Shape Using Parameters?<\/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><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-in-oracle-19c\/\" target=\"_blank\">SQL macros in Oracle 19c<\/a><\/div><\/li><\/ul><\/div>","protected":false},"excerpt":{"rendered":"<p>In the previous post I showed how to get to the final query after SQL macro expansion using CBO trace. But there are still a few interesting things in the trace file that are worth looking at.<\/p>\n","protected":false},"author":1,"featured_media":1998,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[2,3,100],"tags":[145,51,103,146],"class_list":["post-1938","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oracle","category-sql","category-sql-macros","tag-cbo","tag-sql","tag-sql-macros","tag-trace"],"_links":{"self":[{"href":"https:\/\/blog.sqlora.com\/en\/wp-json\/wp\/v2\/posts\/1938","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=1938"}],"version-history":[{"count":35,"href":"https:\/\/blog.sqlora.com\/en\/wp-json\/wp\/v2\/posts\/1938\/revisions"}],"predecessor-version":[{"id":2104,"href":"https:\/\/blog.sqlora.com\/en\/wp-json\/wp\/v2\/posts\/1938\/revisions\/2104"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/blog.sqlora.com\/en\/wp-json\/wp\/v2\/media\/1998"}],"wp:attachment":[{"href":"https:\/\/blog.sqlora.com\/en\/wp-json\/wp\/v2\/media?parent=1938"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.sqlora.com\/en\/wp-json\/wp\/v2\/categories?post=1938"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.sqlora.com\/en\/wp-json\/wp\/v2\/tags?post=1938"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}