{"id":1866,"date":"2023-11-06T16:38:14","date_gmt":"2023-11-06T14:38:14","guid":{"rendered":"https:\/\/blog.sqlora.com\/en\/?p=1866"},"modified":"2023-11-29T23:17:29","modified_gmt":"2023-11-29T21:17:29","slug":"sql-macros-and-invoker-rights","status":"publish","type":"post","link":"https:\/\/blog.sqlora.com\/en\/sql-macros-and-invoker-rights\/","title":{"rendered":"SQL-Macros and Invoker Rights"},"content":{"rendered":"\n<p>This note is meant more as an illustrative example for a question, because the behavior seems strange to me. If I want to do something in my SQL macro that needs additional privileges, how it will run then, when other users will call my SQL macro? <\/p>\n\n\n\n<!--more-->\n\n\n\n<p> That is what you can read in Oracle documentation about <a rel=\"noreferrer noopener\" href=\"https:\/\/docs.oracle.com\/en\/database\/oracle\/oracle-database\/23\/lnpls\/SQL_MACRO-clause.html#GUID-292C3A17-2A4B-4EFB-AD38-68DF6380E5F7\" target=\"_blank\">SQL_Macro Clause<\/a><\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><a href=\"https:\/\/blog.sqlora.com\/en\/wp-content\/uploads\/sites\/2\/2023\/11\/image.png\"><img loading=\"lazy\" decoding=\"async\" width=\"496\" height=\"83\" src=\"https:\/\/blog.sqlora.com\/en\/wp-content\/uploads\/sites\/2\/2023\/11\/image.png\" alt=\"\" class=\"wp-image-1867\" srcset=\"https:\/\/blog.sqlora.com\/en\/wp-content\/uploads\/sites\/2\/2023\/11\/image.png 496w, https:\/\/blog.sqlora.com\/en\/wp-content\/uploads\/sites\/2\/2023\/11\/image-300x50.png 300w\" sizes=\"auto, (max-width: 496px) 100vw, 496px\" \/><\/a><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p>How I read this: I cannot specify AUTHID CURRENT_USER, but it will anyway run with invoker rights. It is just supposed to always do so.<\/p>\n\n\n\n<p>Let&#8217;s test it. I&#8217;ll create two users: <strong>definer_user<\/strong> and <strong>invoker_user<\/strong><\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: sql; title: ; notranslate\" title=\"\">\nSQL&gt; drop user if exists invoker_user cascade;\nSQL&gt;\nSQL&gt; create user if not exists invoker_user identified by oracle;\nSQL&gt;\nSQL&gt; grant create session to invoker_user;\nSQL&gt;\nSQL&gt;\nSQL&gt; drop user if exists definer_user cascade;\nSQL&gt;\nSQL&gt; create user if not exists definer_user identified by oracle quota unlimited on users;\nSQL&gt;\nSQL&gt; grant create session, create table, create procedure to definer_user;\nSQL&gt;\n<\/pre><\/div>\n\n\n<p>As you can see, the <strong>invoker_user<\/strong> has no privileges except <strong>create session<\/strong>. Now let&#8217;s create a table as <strong>definer_user<\/strong> and use it inside a SQL macro. I will also do the same with a normal function (no SQL macro) but defined as invoker rights function (AUTHID CURRENT_USER). I expect the behavior to be more or less the same.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: sql; title: ; notranslate\" title=\"\">\nconnect definer_user\/...@localhost:1522\/freepdb1\n\ncreate table col_config (column_name varchar2(128 byte));\n\ninsert into col_config (column_name) values (&#039;ENAME&#039;);\n\n-- a sql macro function\ncreate or replace function get_column_macro return varchar2 \nsql_macro(scalar) as\n    v_column_name dbms_id;\nbegin\n    select column_name into v_column_name from col_config;\n    return  &#039;&#039;&#039;&#039;||v_column_name||&#039;&#039;&#039;&#039;;\nend;\n\/\n\n-- a normal but invoker-rights function\ncreate or replace function get_column_no_macro return varchar2 \nauthid current_user as\n    v_column_name dbms_id;\nbegin\n    select column_name into v_column_name from col_config;\n    return  &#039;&#039;&#039;&#039;||v_column_name||&#039;&#039;&#039;&#039;;\nend;\n\/\n\ngrant execute on get_column_macro to invoker_user;\n\ngrant execute on get_column_no_macro to invoker_user;\n<\/pre><\/div>\n\n\n<p>Now, the <strong>invoker_user<\/strong> only has the right to execute both functions, but no privileges on the table <strong>col_config<\/strong> which belongs <strong>definer_user<\/strong>. That&#8217;s why I expect a &#8220;table or view doesn&#8217;t exist&#8221; error while executing both functions as invoker_user. But see what happens: only &#8220;no-macro&#8221; function behaves so! Our SQL macro was executed without an error:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: sql; title: ; notranslate\" title=\"\">\nSQL&gt; connect invoker_user\/...@localhost:1522\/freepdb1\nSQL&gt;\nSQL&gt; select definer_user.get_column_no_macro;\n\nError starting at line : 1 in command -\nselect definer_user.get_column_no_macro\nError at Command Line : 1 Column : 8\nError report -\nSQL Error: ORA-00942: table or view does not exist\nORA-06512: at &quot;DEFINER_USER.GET_COLUMN_NO_MACRO&quot;, line 5\n00942. 00000 -  &quot;table or view%s does not exist&quot;\n*Cause:    The specified table or view did not exist, or a synonym\n           pointed to a table or view that did not exist.\n           To find existing user tables and views, query the\n           ALL_TABLES and ALL_VIEWS data dictionary views. Certain\n           privileges may be required to access the table. If an\n           application returned this message, then the table that the\n           application tried to access did not exist in the database, or\n           the application did not have access to it.\n*Action:   Check each of the following\n           - The spelling of the table or view name is correct.\n           - The referenced table or view name does exist.\n           - The synonym points to an existing table or view.\n\nMore Details :\nhttps:\/\/docs.oracle.com\/error-help\/db\/ora-00942\/\nhttps:\/\/docs.oracle.com\/error-help\/db\/ora-06512\/\nSQL&gt;\nSQL&gt; select definer_user.get_column_macro;\n\nGET_COLUMN_MACRO\n___________________\nENAME\nSQL&gt;\n<\/pre><\/div>\n\n\n<p>This tells me, my SQL macro was indeed executed with definer rights!<\/p>\n\n\n\n<p>Another thing I don&#8217;t really understood is the second sentence from the above documentation quote: &#8220;<em>The SQL macro owner must grant inherit privileges to the invoking function<\/em>.&#8221;  As far as I know INHERIT PRIVILEGES is about granting it from high-privileged user to a user who creates an invoker-right program units. But in this case it makes no sense to me&#8230; And what is <strong>invoking function<\/strong>? Even if knew that and wanted to grant INHERIT PRIVILEGES to this function, how can i do this? I thought, it is only possible to grant roles to program units, not system privileges? <\/p>\n\n\n\n<p>Maybe I am missing something or it is just a bug or a documentation bug or both . All comments are welcome!<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">UPDATE:<\/h2>\n\n\n\n<p>Thanks, Chris Saxon for the answer. The behavior we could see above is indeed intended. The body of a SQL Macro runs with <strong>definer privileges<\/strong>:<\/p>\n\n\n\n<figure class=\"wp-block-embed is-type-rich is-provider-twitter wp-block-embed-twitter\"><div class=\"wp-block-embed__wrapper\">\n<blockquote class=\"twitter-tweet\" data-width=\"550\" data-dnt=\"true\"><p lang=\"en\" dir=\"ltr\">I&#39;ve checked with development &#8211; the macro body executes with definer&#39;s privileges to construct the return text<br><br>The expression it returns runs with invoker&#39;s privs<br><br>We&#39;ll get the docs updated to reflect this<\/p>&mdash; Chris Saxon (@ChrisRSaxon) <a href=\"https:\/\/twitter.com\/ChrisRSaxon\/status\/1726607002053525560?ref_src=twsrc%5Etfw\">November 20, 2023<\/a><\/blockquote><script async src=\"https:\/\/platform.twitter.com\/widgets.js\" charset=\"utf-8\"><\/script>\n<\/div><\/figure>\n","protected":false},"excerpt":{"rendered":"<p>This note is meant more as an illustrative example for a question, because the behavior seems strange to me. If I want to do something in my SQL macro that needs additional privileges, how it will run then, when other users will call my SQL macro?<\/p>\n","protected":false},"author":1,"featured_media":1877,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1,2,3,100],"tags":[103],"class_list":["post-1866","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-general","category-oracle","category-sql","category-sql-macros","tag-sql-macros"],"_links":{"self":[{"href":"https:\/\/blog.sqlora.com\/en\/wp-json\/wp\/v2\/posts\/1866","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=1866"}],"version-history":[{"count":9,"href":"https:\/\/blog.sqlora.com\/en\/wp-json\/wp\/v2\/posts\/1866\/revisions"}],"predecessor-version":[{"id":1884,"href":"https:\/\/blog.sqlora.com\/en\/wp-json\/wp\/v2\/posts\/1866\/revisions\/1884"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/blog.sqlora.com\/en\/wp-json\/wp\/v2\/media\/1877"}],"wp:attachment":[{"href":"https:\/\/blog.sqlora.com\/en\/wp-json\/wp\/v2\/media?parent=1866"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.sqlora.com\/en\/wp-json\/wp\/v2\/categories?post=1866"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.sqlora.com\/en\/wp-json\/wp\/v2\/tags?post=1866"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}