Create dynamic query for search using Oracle - oracle

I need to search in my Oracle Database using filters based on the dropdown's. So there are total 3 dropdowns and one textbox for searching parameters. Now what I want is.
If User Selects APPLICATION from the first dropdown and Project Name from second dropdown and LIKE condition from third dropdown. A dynamic query should be created and based on that it should filter the record from the database. For that What I tried is below
PROCEDURE FILTER_SEARCH_DATA
(
P_SEARCH_TYPE IN NVARCHAR2,
P_PARAM_TYPE IN NVARCHAR2,
P_OPERATOR IN NVARCHAR2,
P_TEXTVAL IN NVARCHAR2,
P_RETURN OUT SYS_REFCURSOR
)
AS
STR NVARCHAR2(400):='';
STROP NVARCHAR2(400):='';
STREX NVARCHAR2(4000):='';
BEGIN
IF(P_OPERATOR ='LIKE') THEN
BEGIN
STR:=STR || ' WHERE AM.APPLICATIONNAME ' || P_OPERATOR || '''%' || P_TEXTVAL ||'%''';
END;
ELSE
BEGIN
STR:=STR || 'WHERE AM.APPLICATIONNAME ' || P_OPERATOR || P_TEXTVAL ;
END;
END IF;
DBMS_OUTPUT.PUT_LINE('STR'|| STR);
IF P_SEARCH_TYPE = 'APPLICATION' THEN
DBMS_OUTPUT.PUT_LINE('START APPLICATION');
STREX:='OPEN P_RETURN FOR SELECT AM.APPLICATIONNAME, AM.PROJECTNO, AM.VSS_FOLDER_LOC FROM APPLICATION_MASTER AM
INNER JOIN APPLICATION_DETAILS AD
ON AM.APP_MST_ID = AD.APP_MST_ID' || str ||';';
DBMS_OUTPUT.PUT_LINE('STREX '|| STREX);
END IF;
END FILTER_SEARCH_DATA;
But it is not working accordingly for what I want.
Let me know if you have any issues related to this.

Your problem is here:
STREX:='OPEN P_RETURN FOR SELECT AM.APPLICATIONNAME, AM.PROJECTNO, AM.VSS_FOLDER_LOC FROM APPLICATION_MASTER AM
INNER JOIN APPLICATION_DETAILS AD
ON AM.APP_MST_ID = AD.APP_MST_ID' || str ||';';
DBMS_OUTPUT.PUT_LINE('STREX '|| STREX);
This code just outputs a string into the standard output. You need to use this:
PROCEDURE FILTER_SEARCH_DATA
(P_SEARCH_TYPE IN NVARCHAR2,
P_PARAM_TYPE IN NVARCHAR2,
P_OPERATOR IN NVARCHAR2,
P_TEXTVAL IN NVARCHAR2,
P_RETURN OUT SYS_REFCURSOR ) AS
STR NVARCHAR2(400):='';
STROP NVARCHAR2(400):='';
STREX VARCHAR2(4000):='';
val NVARCHAR2(4000);
BEGIN
IF (P_OPERATOR ='LIKE') THEN
val := '%' || P_TEXTVAL ||'%';
ELSE
val := P_TEXTVAL;
END IF;
DBMS_OUTPUT.PUT_LINE('STR'|| STR);
IF P_SEARCH_TYPE = 'APPLICATION' THEN
DBMS_OUTPUT.PUT_LINE('START APPLICATION');
STREX:='SELECT AM.APPLICATIONNAME, AM.PROJECTNO, AM.VSS_FOLDER_LOC
FROM APPLICATION_MASTER AM
INNER JOIN APPLICATION_DETAILS AD
ON AM.APP_MST_ID = AD.APP_MST_ID
WHERE AM.APPLICATIONNAME ' || P_OPERATOR || ' :PARAM';
DBMS_OUTPUT.PUT_LINE('STREX '|| STREX);
open P_RETURN for STREX using val;
END IF;
END FILTER_SEARCH_DATA;
Also, I added using of parameters (:PARAM in the SQL code), it helps with performance and allows to avoid SQL injections.
Another one important thing: in your code you open the cursor only if P_SEARCH_TYPE equals to 'APPLICATION'. You need to do something in other cases, or your procedure will return closed cursor.
UPD
STREX should be VARCHAR2, not NVARCHAR2.

Related

Oracle 10g - Get list of tables from query i'm executing

I am working on internal tools of my organization, I have a automation need currently.
Example input query:
Select name,userid,url,address_line_1 from user join address on
user.user_id = address.user_id where userid = 'xxyy';
what I need is list of tables
user
address
Is there a built in way in Oracle 10G to get the list of tables from this query? Or Is there a python parser that can help me with the list of tables from the query?
Note: This is a basic example, my queries run in several lines and are more complex.
Interesting question.
You could build a little SQL analyzer in PL/SQL using DBMS_FGA. The idea would be:
Automatically modify the input SQL to also use a table with a FGA policy on it
In that FGA policy, you will have access to the current SQL (the first 32K of it, anyway. That's a limitation...)
Use the current SQL to build a throw-away view on the current SQL
Read the throw-away view's dependencies from USER_DEPENDENCIES
Drop the throw-away view.
Here is an example of how it would work:
(I apologize for putting my first name in all the objects; I share this database with others.)
-- Tester
BEGIN
matt_analysis_pkg.analyze_sql(p_sql =>
'WITH oel AS ( SELECT *
FROM oe_order_lines
WHERE ship_from_org_id = 88 )
SELECT oel.line_id, msi.segment1
FROM oel INNER JOIN mtl_system_items msi
ON msi.organization_id = 92 and msi.inventory_item_id = oel.inventory_item_id');
END;
/
Objects referenced by current SQL:
APPS.MTL_SYSTEM_ITEMS (SYNONYM)
APPS.OE_ORDER_LINES (SYNONYM)
The example (below) just reports the 1st level of dependencies. You could use DBA_DEPENDENCIES recursively to get more depth. Also, this version just writes to DBMS_OUTPUT.
As others have reported, just because a SQL depends on an object doesn't mean Oracle will actually access that object at run-time. Still, I think this is pretty close to what you were asking for.
Also, I noticed you tagged your question with Oracle 10g. I think the only thing in my solution that won't work in 10g is my direct access of a sequence. You'll have to replace that part with SELECT ... INTO to get the current sequence value.
Anyway, here is the source code for it (Oracle 12c):
-- This table doesn't do anything other than have a FGA policy on it.
CREATE TABLE matt_analysis_tab ( dummy varchar2(1) );
INSERT INTO matt_analysis_tab (dummy) VALUES ('X');
-- Sequence so we can create unique view names, in case two people analyze at the same time.
CREATE SEQUENCE matt_analysis_view_s;
-- Package to do the work.
CREATE OR REPLACE PACKAGE matt_analysis_pkg IS
PROCEDURE analyze_sql ( p_sql CLOB );
PROCEDURE analyze_current_sql (schema_name VARCHAR2, table_name VARCHAR2, policy_name VARCHAR2);
END matt_analysis_pkg;
/
CREATE OR REPLACE PACKAGE BODY matt_analysis_pkg AS
PROCEDURE analyze_sql (p_sql CLOB) IS
l_modified_sql CLOB := 'WITH v1$ AS ( SELECT /*+ MATERIALIZE */ dummy FROM matt_analysis_tab ) SELECT v1$.dummy, v2$.* FROM v1$, ( ' || p_sql || ') v2$';
BEGIN
DBMS_OUTPUT.PUT_LINE('l_modified_sql := ' || l_modified_sql);
EXECUTE IMMEDIATE l_modified_sql;
END analyze_sql;
PROCEDURE analyze_current_sql (schema_name VARCHAR2, table_name VARCHAR2, policy_name VARCHAR2) IS
PRAGMA AUTONOMOUS_TRANSACTION;
l_sql CLOB;
l_column_count INTEGER;
l_view_name VARCHAR2(30);
l_view_columns VARCHAR2(4000);
BEGIN
l_sql := SYS_CONTEXT ('userenv', 'CURRENT_SQL',4000)
|| SYS_CONTEXT ('userenv', 'CURRENT_SQL1',4000)
|| SYS_CONTEXT ('userenv', 'CURRENT_SQL2',4000)
|| SYS_CONTEXT ('userenv', 'CURRENT_SQL3',4000)
|| SYS_CONTEXT ('userenv', 'CURRENT_SQL4',4000)
|| SYS_CONTEXT ('userenv', 'CURRENT_SQL5',4000)
|| SYS_CONTEXT ('userenv', 'CURRENT_SQL6',4000)
|| SYS_CONTEXT ('userenv', 'CURRENT_SQL7',4000)
;
DBMS_OUTPUT.put_line ('Current SQL: ' || l_sql);
DBMS_OUTPUT.put_line ('Current SQL length (calc): ' || length(l_sql));
DBMS_OUTPUT.put_line ('Current SQL length (userenv): ' || SYS_CONTEXT('userenv','CURRENT_SQL_LENGTH'));
-- Parse the SQL to get the column count
DECLARE
l_cursor INTEGER;
l_column_descriptions SYS.DBMS_SQL.desc_tab;
BEGIN
l_cursor := sys.DBMS_SQL.open_cursor;
-- parse SQL
sys.DBMS_SQL.parse (c => l_cursor, statement => l_sql, language_flag => sys.DBMS_SQL.native);
-- Describe columns
sys.DBMS_SQL.describe_columns (c => l_cursor, col_cnt => l_column_count, desc_t => l_column_descriptions);
sys.DBMS_SQL.close_cursor (l_cursor);
END;
DBMS_OUTPUT.PUT_LINE('Column count = ' || l_column_count);
-- Build view columns. We need to do this because the column names in the SQL are not necessarily unique.
SELECT listagg('C' || lpad(rownum,4,'0'),',') within group ( order by rownum )
INTO l_view_columns
FROM dual
CONNECT BY rownum <= l_column_count;
DBMS_OUTPUT.PUT_LINE('l_view_columns = ' || l_view_columns);
l_view_name := 'matt_analysis_view_' || lpad(matt_analysis_view_s.nextval,6,'0') || '$';
DBMS_OUTPUT.PUT_LINE('l_view_name = ' || l_view_name);
l_sql := 'CREATE OR REPLACE FORCE VIEW ' || l_view_name || ' (' || l_view_columns || ') AS ' || l_sql;
EXECUTE IMMEDIATE l_sql;
DBMS_OUTPUT.PUT_LINE('Objects referenced by current SQL: ');
FOR r IN ( select referenced_owner || '.' || referenced_name || ' (' || referenced_type || ')' reference_info
from user_dependencies where name = upper(l_view_name)
AND referenced_name not like 'MATT_ANALYSIS%' ) LOOP
DBMS_OUTPUT.PUT_LINE(r.reference_info);
END LOOP;
EXECUTE IMMEDIATE 'DROP VIEW ' || l_view_name;
COMMIT;
END analyze_current_sql;
END matt_analysis_pkg;
/
-- Create the FGA policy
BEGIN
DBMS_FGA.add_policy (
object_schema => NULL, -- My current schema
object_name => 'MATT_ANALYSIS_TAB',
policy_name => 'MATT_ANALYSIS_POLICY',
audit_condition => NULL,
audit_column => NULL,
handler_schema => NULL, -- My current schema
handler_module => 'matt_analysis_pkg.analyze_current_sql',
enable => TRUE);
END;
/
-- Script to drop the policy, just in case
--EXEC DBMS_FGA.drop_policy (NULL, 'MATT_ANALYSIS_TAB', 'MATT_ANALYSIS_POLICY');
I am still waiting for the solution, but, for now, I came up with a quick and dirty way to get the table names along with aliases using the following python.
from copy import deepcopy
import cx_Oracle
############################################################################
####################### Database connection instantiation###################
############################################################################
#connObj = pyodbc.connect(connString,autocommit=True)
qry = """
Select name,userid,url,address_line_1 from user join address on
user.user_id = address.user_id where userid = 'xxyy'
"""
def getConn():
connObj = cx_Oracle.connect('asap', 'sdfssa', cx_Oracle.makedsn('DBLDEV03', 1521, '23432'))
return connObj
def destroy(connObj):
connObj.commit()
connObj.close()
del connObj
##################### Logic ########################################
import datetime
def getTablesFromQuery(qry):
listTables = []
listAliases = []
connObj = getConn()
cursor = connObj.cursor()
listSpaceItems = qry.split(" ")
found =False
for spaceItem in listSpaceItems:
if spaceItem != '' and spaceItem!='\n':
spaceItem = spaceItem.replace("\n",'')
listCommaItems = spaceItem.split(",")
if found == True:
##### We are assuming that the next item is always alias, the sql query should follow that rule to
##### get the aliases properly.
listAliases.append(spaceItem)
found = False
for commaItem in listCommaItems:
if commaItem != '':
item = commaItem
if "." in commaItem:
item=commaItem.split(".")[1]
cursor.execute('select * from all_tables where table_name=\''+item.upper()+'\'')
res = cursor.fetchall()
if res is not None and res.__len__()>0:
listTables.append(commaItem)
found = True
destroy(connObj)
return listTables,listAliases
try:
listTables, listAliases = getTablesFromQuery(qry)
for item in listTables:
print(''+item)
except:
print('Exception..')
############################################################################
####################### close database connection###########################
############################################################################
You could create a package procedure to accept a SQL statement as input. What it would do is wrap the SQL in a CREATE VIEW and then analyze the dependencies of the resulting view.
Here is the source code.
CREATE OR REPLACE PACKAGE matt_analysis_pkg IS
PROCEDURE analyze_sql ( p_sql CLOB );
END matt_analysis_pkg;
/
CREATE OR REPLACE PACKAGE BODY matt_analysis_pkg AS
PROCEDURE analyze_sql (p_sql CLOB) IS
PRAGMA AUTONOMOUS_TRANSACTION;
l_sql CLOB;
l_column_count INTEGER;
l_view_name VARCHAR2(30);
l_view_columns VARCHAR2(4000);
BEGIN
DBMS_OUTPUT.put_line ('Current SQL: ' || p_sql);
-- Parse the SQL to get the column count
DECLARE
l_cursor INTEGER;
l_column_descriptions SYS.DBMS_SQL.desc_tab;
BEGIN
l_cursor := sys.DBMS_SQL.open_cursor;
-- parse SQL
sys.DBMS_SQL.parse (c => l_cursor, statement => p_sql, language_flag => sys.DBMS_SQL.native);
-- Describe columns
sys.DBMS_SQL.describe_columns (c => l_cursor, col_cnt => l_column_count, desc_t => l_column_descriptions);
sys.DBMS_SQL.close_cursor (l_cursor);
END;
DBMS_OUTPUT.PUT_LINE('Column count = ' || l_column_count);
-- Build view columns. We need to do this because the column names in the SQL are not necessarily unique.
SELECT listagg('C' || lpad(rownum,4,'0'),',') within group ( order by rownum )
INTO l_view_columns
FROM dual
CONNECT BY rownum <= l_column_count;
DBMS_OUTPUT.PUT_LINE('l_view_columns = ' || l_view_columns);
l_view_name := 'matt_analysis_view_' || lpad(matt_analysis_view_s.nextval,6,'0') || '$';
DBMS_OUTPUT.PUT_LINE('l_view_name = ' || l_view_name);
l_sql := 'CREATE OR REPLACE FORCE VIEW ' || l_view_name || ' (' || l_view_columns || ') AS ' || p_sql;
EXECUTE IMMEDIATE l_sql;
DBMS_OUTPUT.PUT_LINE('Objects referenced by current SQL: ');
FOR r IN ( select referenced_owner || '.' || referenced_name || ' (' || referenced_type || ')' reference_info
from user_dependencies where name = upper(l_view_name)
AND referenced_name not like 'MATT_ANALYSIS%' ) LOOP
DBMS_OUTPUT.PUT_LINE(r.reference_info);
END LOOP;
EXECUTE IMMEDIATE 'DROP VIEW ' || l_view_name;
COMMIT;
END analyze_sql;
END matt_analysis_pkg;
/
Tester
BEGIN
matt_analysis_pkg.analyze_sql(p_sql =>
'WITH oel AS ( SELECT *
FROM oe_order_lines
WHERE ship_from_org_id = 88 )
SELECT oel.line_id, msi.segment1
FROM oel INNER JOIN mtl_system_items msi
ON msi.organization_id = 92 and msi.inventory_item_id = oel.inventory_item_id');
END;
Objects referenced by current SQL:
APPS.MTL_SYSTEM_ITEMS (SYNONYM)
APPS.OE_ORDER_LINES (SYNONYM)

How to use variables in Oracle PL/SQL Function

I'm sorry upfront because this question seems to easy.
I have this function:
CREATE OR REPLACE FUNCTION Costs_MK (VIEWNAME IN VARCHAR2 , WHERE_CLAUSE IN VARCHAR2)
RETURN VARCHAR2
IS
v_Costs VARCHAR2 (500);
BEGIN
Select Listagg(Costs, ';' ) WITHIN GROUP (ORDER BY Costs)
into v_Costs
from (select distinct (Costs)
from VIEWNAME
where WHERE_CLAUSE);
RETURN v_Costs;
END Costs_MK;
However I get the Error-Message:
Error(13,30): PL/SQL: ORA-00920: invalid relational operator
I even can't compile it. If I use the exact values for Viewname and Where_clause I get the desired result.
What am I doing wrong?
/edit: Line 13 is
from VIEWNAME
/edit #2:
Thanks guys. You helped me a lot. I didn't thought about dynamic sql in the first step, so thanks for the refresher ;).
I suggest you to add EXCEPTION BLOCK along with EXECUTE IMMEDIATE
I have created a PROCEDURE you can similary create FUNCTION
CREATE OR REPLACE procedure Costs_PK(VIEWNAME IN VARCHAR2 , WHERE_CLAUSE IN VARCHAR2 )
AS
v_Costs VARCHAR2 (500);
sql_stmnt varchar2(2000);
BEGIN
sql_stmnt := 'Select Listagg(Cost, '';'' ) WITHIN GROUP (ORDER BY Cost) from (select distinct (Cost) from ' || VIEWNAME || ' where ' || WHERE_CLAUSE || ' ) ';
--sql_stmnt := 'Select Listagg(Cost, '';'' ) WITHIN GROUP (ORDER BY Cost) from (select distinct (Cost) from cost_tab where cost >=123 ) ';
EXECUTE IMMEDIATE sql_stmnt INTO v_Costs ;
dbms_output.put_line ('query works -- ' || v_costs);
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE ('input :' || VIEWNAME || ' and ' || WHERE_CLAUSE );
dbms_output.put_line (sql_stmnt );
DBMS_OUTPUT.PUT_LINE ('ERROR MESSAGE : ' || sqlCODE || ' ' || SQLERRM );
END;
begin
Costs_PK('cost_tab','cost >= 123');
end;
NOTE: code has been Tested
output:
query works -- 123;456
This is one of the areas in PL/SQL where the most straightforward static SQL solution requires code duplication as there is no way to parametrize the table name in a query. Personally I usually favor duplicate code of static SQL over the increased complexity of dynamic SQL as I like PL/SQL compiler to check my SQL compile time. YMMV.
You don't tell us what kind of where statements the different views are having. In the example below I assume there is 1:1 relation between the view and where parameter(s) so I can easily build static SQL.
create or replace view foo_v (foo_id, cost) as
select level, level*10 from dual connect by level < 10
;
create or replace view bar_v (bar_id, cost) as
select level, level*100 from dual connect by level < 10
;
create or replace function cost_mk(
p_view in varchar2
,p_foo_id in number default null
,p_bar_id in number default null
) return varchar2 is
v_cost varchar2(32767);
begin
case lower(p_view)
when 'foo_v' then
select listagg(cost, ';' ) within group (order by cost)
into v_cost
from (select distinct cost
from foo_v
where foo_id < p_foo_id);
when 'bar_v' then
select listagg(cost, ';' ) within group (order by cost)
into v_cost
from (select distinct cost
from bar_v
where bar_id < p_bar_id);
end case;
return v_cost;
end;
/
show errors
Usage example
select cost_mk(p_view => 'foo_v', p_foo_id => 5) from dual;
select cost_mk(p_view => 'bar_v', p_bar_id => 5) from dual;
You would want to use EXECUTE IMMEDIATE as was hinted by the comments to your question, define a new variable sqlQuery VARCHAR2(200); or similar and rework your sql similar to the following:
sqlQuery := 'Select Listagg(Costs, '';'' ) WITHIN GROUP (ORDER BY Costs) ';
sqlQuery := sqlQuery || 'from (select distinct (Costs) from :1 where :2)';
EXECUTE IMMEDIATE sqlQuery INTO v_Costs USING VIEWNAME, WHERE_CLAUSE;

Errors in PL/SQL

I was trying to create a PL/SQL program to search for a value in entire database. Following is the code:
DECLARE
custom_query VARCHAR2(100);
user_input VARCHAR2(100);
i NUMBER (6);
TYPE temp_record
IS
varray(1000) OF VARCHAR2(100);
CURSOR t_rec IS
SELECT a.name AS t_name,
b.name AS c_name
FROM sys.TABLES a,
sys.COLUMNS b
WHERE a.object_id = b.object_id;
table_rec t_name%ROWTYPE;
BEGIN
IF NOT t_rec%isopen THEN
OPEN t_rec;
END IF;
FETCH t_rec
INTO table_rec;
WHILE t_rec%FOUND THEN
LOOP
custom_query := 'select'
|| table_rec.c_name
|| 'into temp_record from'
||table_rec.t_name ;
dbms_output.Put_line(custom_query);
EXECUTE IMMEDIATE custom_query;
FOR i IN 1 .. temp_record.count
LOOP
IF (Temp_record(i) = user_input) THEN
dbms_output.Put_line ('The value you are looking for is in '
|| table_rec.c_name
|| 'column of table'
|| table_rec.t_name);
END IF;
END LOOP;
END LOOP;
END;
Can anyone let me know what's wrong with this code?
Use ALL_TAB_COLUMNS to iterate all tables and columns in your dynamic query.

Dynamically assigning variables oracle sql

I have a table attribute_config with below columns:
table_name column_name key
Let us say is has below 2 rows
account accountphone accountnum
customer customernumber customerid
Key can be only accountnum or customerid.
I have to write code which will accept (i_accountnum,i_customerid) and;
fetch the respective values from columns mentioned in column_name in tables mentioned in table_name using the key in where condition.
For ex: select accountphone from account where accountnum = i_accountnum
select customernumber from customer where customerid = i_customerid
the complete query should be formed dynamically, whether to pass i_accountnum or i_customerid in the query also needs to be decided dynamically. if key - accountnum, i_accountnum will be passed to where condition.
I have been trying on these lines so far, this is not working, i know it is wrong.
declare
v_accountnum varchar2(20);
v_customerid varchar2(20);
v_attribute_value varchar2(20);
v_stmt varchar2(255);
begin
Account_Num := 'TestCustomer'; -- input to the function
v_customer_ref := 'TestAccount'; -- input to the function
for i in (Select * from attribute_config) loop
v_stmt := 'select ' || i.column_name || ' from ' || i.table_name ||' where ' || i.key|| ' = v_' || i.key;
execute immediate v_Stmt into v_attribute_value;
end loop;
end;
This will fix your code, but I do not see any advantage of using dynamic query when your code should accept 2 parameters(i_accountnum,i_customerid) - which is already static situation and fetch the relevant values, perhaps only in learning purposes.
declare
procedure fecth_values(i_accountnum account.accountnum%type,
i_customerid customer.customerid%type) return varchar2 is
v_attribute_value varchar2(20);
begin
for i in (select * from attribute_config) loop
execute immediate 'select ' || i.column_name || ' from ' ||
i.table_name || ' where ' || i.key || ' = ' || case when i.key = 'accountnum' then i_accountnum when i.key = 'customerid' then i_customerid end;
into v_attribute_value;
dbms_output.put_line(v_attribute_value);
end loop;
return null;
end;
begin
fecth_values(1, 1);
end;
Your where clause was wrong the i.key should be compared against the inputed values, not the 'v_' || i.key, which is undeclared when you execute your stmt.

How get copy of objects from oracle database

I have plan to do a release for our product. we are using Oracle forms to develop our software.
So I have two type of object for release
Front-end => Oracle forms
Back-end => Oracle Database Program (Procedure, function and Package)
One of the important part of release is we need to have a back up so if any thing happen we can use the older version.
By the way, for front end object I will use some batch file to create a back up for my front end objects but for Back-end object I dont know how I can get a copy of function, procedure or package(spec & body) automatically.
Any idea how I can do it automatically? please advice me.
we are using oracle 10g & 9i
"In my company sometimes developer apply some package or function
during the webbex so sometimes customer production is not same as our
pvcs."
So what is the point of storing code in a repository? What is the point of testing a configuration? What is the point of release management?
If these cowboy developers who muck around with your customers' production environments are your staff you need to discipline them. Enforce process to guarantee that only certified configurations are deployed. If emergency tweaks are required these should be retrofitted into the official build.
If these developers actually work for the customer, then I suppose you cannot stop them. But you don't have to support them, and you don't have to be responsible for their changes. (unless the sales contract says you have to, which wouldn't surprise me.)
(Apologies for the code-blob).
Here's some code I've used to handle issues like this:
PROCEDURE DUMP_CLOB(aCLOB IN CLOB,
hOutput_file IN UTL_FILE.FILE_TYPE) IS
nCLOB_length NUMBER;
nCLOB_offset NUMBER := 1;
nMax_chunk_size NUMBER := 32767;
strChunk VARCHAR2(32767);
BEGIN
nCLOB_length := DBMS_LOB.GETLENGTH(aCLOB);
WHILE nCLOB_offset <= nCLOB_length LOOP
strChunk := DBMS_LOB.SUBSTR(aCLOB, nMax_chunk_size, nCLOB_offset);
UTL_FILE.PUT(hOutput_file, strChunk);
nCLOB_offset := nCLOB_offset + LENGTH(strChunk);
END LOOP;
UTL_FILE.PUT_LINE(hOutput_file, ';');
END DUMP_CLOB;
PROCEDURE DUMP_PRIVS(strOwner IN VARCHAR2,
strObject_name IN VARCHAR2,
hOutput_file IN UTL_FILE.FILE_TYPE) IS
BEGIN
FOR pRow IN (SELECT *
FROM DBA_TAB_PRIVS p
WHERE p.OWNER = strOwner AND
p.TABLE_NAME = strObject_name)
LOOP
UTL_FILE.PUT_LINE(hOutput_file, 'GRANT ' || pRow.PRIVILEGE || ' ON ' ||
strOwner || '.' || strObject_name ||
' TO ' || pRow.GRANTEE || ';');
END LOOP;
END DUMP_PRIVS;
PROCEDURE DUMP_OBJECT(strOwner IN VARCHAR2,
strObject_name IN VARCHAR2,
hOutput_file IN UTL_FILE.FILE_TYPE)
IS
clobDDL CLOB;
strCurr_object_name VARCHAR2(100);
BEGIN
FOR rowObject IN (SELECT *
FROM SYS.DBA_OBJECTS o
WHERE o.OWNER = strOwner AND
o.OBJECT_NAME = strObject_name AND
o.OBJECT_TYPE <> 'TABLE PARTITION')
LOOP
strCurr_object_name := NVL(rowObject.SUBOBJECT_NAME, rowObject.OBJECT_NAME);
UTL_FILE.PUT_LINE(hOutput_file, '-- DDL for ' || LOWER(rowObject.OBJECT_TYPE) || ' ' ||
strOwner || '.' || strCurr_object_name);
SELECT DBMS_METADATA.GET_DDL(rowObject.OBJECT_TYPE, strCurr_object_name, strOwner) AS DDL
INTO clobDDL
FROM DUAL;
DUMP_CLOB(clobDDL, hOutput_file);
DUMP_PRIVS(strOwner, strCurr_object_name, hOutput_file);
IF rowObject.OBJECT_TYPE = 'TABLE' THEN
-- Indexes
FOR aRow IN (SELECT DBMS_METADATA.GET_DDL('INDEX', i.INDEX_NAME, i.OWNER) AS clobIndex
FROM DBA_INDEXES I
WHERE I.TABLE_OWNER = strOwner AND
I.TABLE_NAME = strCurr_object_name)
LOOP
DUMP_CLOB(aRow.clobIndex, hOutput_file);
END LOOP; -- Indexes
END IF;
IF rowObject.OBJECT_TYPE IN ('TABLE', 'VIEW') THEN
-- Triggers
FOR aRow IN (SELECT DBMS_METADATA.GET_DDL('TRIGGER', t.TRIGGER_NAME, t.OWNER) AS clobTrigger
FROM DBA_TRIGGERS t
WHERE TABLE_OWNER = strOwner AND
TABLE_NAME = strCurr_object_name)
LOOP
DUMP_CLOB(aRow.clobTrigger, hOutput_file);
END LOOP; -- Triggers
END IF;
END LOOP;
END DUMP_OBJECT;
PROCEDURE DUMP_OBJECT(strOwner IN VARCHAR2,
strObject_name IN VARCHAR2,
strDirectory_name IN VARCHAR2,
strFilename IN VARCHAR2,
strOpen_mode IN VARCHAR2 DEFAULT 'w')
IS
hOutput_file UTL_FILE.FILE_TYPE;
BEGIN
hOutput_file := UTL_FILE.FOPEN(location => strDirectory_name,
filename => strFilename,
open_mode => strOpen_mode);
DUMP_OBJECT(strOwner, strObject_name, hOutput_file);
UTL_FILE.FCLOSE(hOutput_file);
EXCEPTION
WHEN OTHERS THEN
UTL_FILE.FCLOSE(hOutput_file);
RAISE;
END DUMP_OBJECT;
I suggest putting these procedures into a package. Call DUMP_OBJECT for the things you want dumped.
Share and enjoy.

Resources