Create a fake DBMS_METADATA
on the DEBEZIUM schema that calls a definer's rights function on INVENTORY that calls the real DBMS_METADATA. This workaround assumes that Debezium calls the package DBMS_METADATA
instead of using the fully-qualified name SYS.DBMS_METADATA
. Oracle uses schema objects before public synonyms, so this custom package let's you hijack some calls to the real SYS.DBMS_METADATA.
Yes, this is an overly complicated mess that should only be used as a last resort. Perhaps instead of actually using this code, you can just show it to the DBA to convince them that you need the right privilege.
As INVENTORY, create a package that mimics part of SYS.DBMS_METADTATA, and grant it to DEBEZIUM:
create or replace package inventory.dbms_metadata_without_privs authid definer is
/*
This package exists because DEBEZIUM cannot be granted SELECT_CATALOG_ROLE because
[enter reason here]
Note that this package is "DEFINER" instead of the "CURRENT_USER" of the real DBMS_METADATA.
*/
FUNCTION get_ddl (
object_type IN VARCHAR2,
name IN VARCHAR2,
schema IN VARCHAR2 DEFAULT NULL,
version IN VARCHAR2 DEFAULT 'COMPATIBLE',
model IN VARCHAR2 DEFAULT 'ORACLE',
transform IN VARCHAR2 DEFAULT 'DDL')
RETURN CLOB;
PROCEDURE set_transform_param (
transform_handle IN NUMBER,
name IN VARCHAR2,
value IN BOOLEAN DEFAULT TRUE,
object_type IN VARCHAR2 DEFAULT NULL,
flags IN NUMBER DEFAULT 0);
end dbms_metadata_without_privs;
/
create or replace package body inventory.dbms_metadata_without_privs is
FUNCTION get_ddl (
object_type IN VARCHAR2,
name IN VARCHAR2,
schema IN VARCHAR2 DEFAULT NULL,
version IN VARCHAR2 DEFAULT 'COMPATIBLE',
model IN VARCHAR2 DEFAULT 'ORACLE',
transform IN VARCHAR2 DEFAULT 'DDL')
RETURN CLOB IS
BEGIN
RETURN SYS.DBMS_METADATA.get_ddl(object_type => object_type,
name => name,
schema => schema,
version => version,
model => model,
transform => transform);
END;
PROCEDURE set_transform_param (
transform_handle IN NUMBER,
name IN VARCHAR2,
value IN BOOLEAN DEFAULT TRUE,
object_type IN VARCHAR2 DEFAULT NULL,
flags IN NUMBER DEFAULT 0) IS
BEGIN
SYS.DBMS_METADATA.set_transform_param(transform_handle => transform_handle,
name => name,
value => value,
object_type => object_type,
flags => flags);
END;
end dbms_metadata_without_privs;
/
grant execute on inventory.dbms_metadata_without_privs to debezium;
As DEBEZIUM, create a fake DBMS_METADATA
that simply calls the package in INVENTORY:
CREATE OR REPLACE NONEDITIONABLE PACKAGE DEBEZIUM.dbms_metadata AUTHID CURRENT_USER AS
/*
This fake DBMS_METADATA exists because DEBEZIUM cannot be granted SELECT_CATALOG_ROLE because
[enter reason here]
Yes, this is a stupid idea for many reasons...
*/
--------------------
-- PUBLIC CONSTANTS
--
SESSION_TRANSFORM CONSTANT BINARY_INTEGER := -1;
FUNCTION get_ddl (
object_type IN VARCHAR2,
name IN VARCHAR2,
schema IN VARCHAR2 DEFAULT NULL,
version IN VARCHAR2 DEFAULT 'COMPATIBLE',
model IN VARCHAR2 DEFAULT 'ORACLE',
transform IN VARCHAR2 DEFAULT 'DDL')
RETURN CLOB;
PROCEDURE set_transform_param (
transform_handle IN NUMBER,
name IN VARCHAR2,
value IN BOOLEAN DEFAULT TRUE,
object_type IN VARCHAR2 DEFAULT NULL,
flags IN NUMBER DEFAULT 0);
END DBMS_METADATA;
/
CREATE OR REPLACE NONEDITIONABLE PACKAGE BODY DEBEZIUM.dbms_metadata AS
FUNCTION get_ddl (
object_type IN VARCHAR2,
name IN VARCHAR2,
schema IN VARCHAR2 DEFAULT NULL,
version IN VARCHAR2 DEFAULT 'COMPATIBLE',
model IN VARCHAR2 DEFAULT 'ORACLE',
transform IN VARCHAR2 DEFAULT 'DDL')
RETURN CLOB
IS
BEGIN
RETURN inventory.dbms_metadata_without_privs.get_ddl(object_type => object_type,
name => name,
schema => schema,
version => version,
model => model,
transform => transform);
END;
PROCEDURE set_transform_param (
transform_handle IN NUMBER,
name IN VARCHAR2,
value IN BOOLEAN DEFAULT TRUE,
object_type IN VARCHAR2 DEFAULT NULL,
flags IN NUMBER DEFAULT 0)
IS
BEGIN
inventory.dbms_metadata_without_privs.set_transform_param(transform_handle => transform_handle,
name => name,
value => value,
object_type => object_type,
flags => flags);
END;
END DBMS_METADATA;
/
As DEBEZIUM, you can now run these statements:
select dbms_metadata.get_ddl('TABLE', 'PRODUCT', 'INVENTORY') from dual;
begin dbms_metadata.set_transform_param(DBMS_METADATA.SESSION_TRANSFORM, 'DEFAULT'); end;
/
Most of the above code was taken straight from the DBMS_METADATA
package. If you need to implement more functionality from that package, you can use the below query to copy and paste the procedure definitions. (Oddly, DBMS_METADATA
does not work correctly on DBMS_METADATA
.)
select *
from all_source
where owner = 'SYS'
and name = 'DBMS_METADATA'
and type = 'PACKAGE'
order by line;