SQL Trigger signature capturing information on user creating/dropping an object - sql

I'm completely new to SQL, and am currently taking a class in it right now for databases. I am told to create a trigger signature that would capture information when anyone creates or drops an object.
I have no idea where to start, anything will help! I'm looking into trigger signatures right now but how do I know when someone creates/drops an object? So far I have been thinking it might be something like:
BEFORE CREATE OR DROP
Thanks!

In your sys or system schema, you can try trigger below;
create or replace trigger trg_adm_ddl before ddl on database
declare
begin
pr_ddl_oper;
end;
*where* pr_ddl_oper is ;
create or replace procedure pr_ddl_oper as
v_oty varchar2(75) := ora_dict_obj_type;
v_don varchar2(75) := ora_dict_obj_name;
v_evt varchar2(75) := ora_sysevent;
v_olu varchar2(75) := nvl(ora_login_user,'Unknown Schema');
v_sql ora_name_list_t;
v_stm clob;
v_sct owa.vc_arr;
n pls_integer;
n_max pls_integer := 10000;
begin
v_sct(1) := 'SESSIONID';
v_sct(2) := 'IP_ADDRESS';
v_sct(3) := 'TERMINAL';
v_sct(4) := 'OS_USER';
v_sct(5) := 'AUTHENTICATION_TYPE';
v_sct(6) := 'CLIENT_INFO';
v_sct(7) := 'MODULE';
for i in 1..7
loop
v_sct(i) := sys_context('USERENV',v_sct(i));
end loop;
select decode(v_sct(1),0,null,v_sct(1)),decode(upper(v_sct(3)),'UNKNOWN',null,v_sct(3)) into v_sct(1),v_sct(3) from dual;
n := ora_sql_txt( v_sql );
if n > n_max then
n := n_max;
end if;
for i in 1..n
loop
v_stm := v_stm || v_sql(i);
end loop;
insert into usr_audit.log_ddl(col_datetime,col_user,col_evnt,col_statement,col_sessionid,col_ip,col_terminal,col_osuser,col_auttype,col_objecttype,col_objectname,col_clientinfo,col_moduleinfo)
values(sysdate,v_olu,v_evt,v_stm,v_sct(1),v_sct(2),v_sct(3),v_sct(4),v_sct(5),v_oty,v_don,v_sct(6),v_sct(7));
end;
after constructing this mechanism, you may enquiry the results with a sql like this one;
select * from usr_audit.log_ddl t
where lower(t.stmt) like '%alter%table%modify%'
order by t.col_datetime desc
or you may use the below way easily (if your db is at least 11g);
$ alter system set enable_ddl_logging=true;
your DDL log file data is written in XML format to a file in your OS.

Related

Handle a very large string in pl/sql script

I am trying to run below code which reads the index definition for table A so that it can be created again after I delete/create that in this script. This script runs fine when the returned value(ddl) is small but in other environments where the value is large with 140K characters in one row this script fails with below mentioned error. Please note that I cannot use spool in this case due to some restrictions. Could someone help on how to resolve this issue or suggest some another approach?
Thanks in advance.
"An arithmetic, numeric, string, conversion, or constraint error
occurred. For example, this error occurs if an attempt is made to
assign the value NULL to a variable declared NOT NULL, or if an
attempt is made to assign an integer larger than 99 to a variable
declared NUMBER(2)."
SET SERVEROUTPUT ON;
DECLARE
my_cursor SYS_REFCURSOR;
TYPE clob_array IS VARRAY(15) OF CLOB;
index_array clob_array := clob_array();
v_clob CLOB;
--index_array SYS.ODCIVARCHAR2LIST := SYS.ODCIVARCHAR2LIST();
BEGIN
OPEN my_cursor FOR 'select replace(dbms_metadata.get_ddl (''INDEX'', index_name), ''"C",'', '''')
from user_indexes
where table_name = ''A''';
LOOP FETCH my_cursor INTO v_clob;
EXIT WHEN my_cursor%NOTFOUND;
index_array.extend;
index_array(index_array.count) := v_clob;
dbms_output.put_line(index_array(index_array.count));
END LOOP;
CLOSE my_cursor;
END;
/
I simulated this issue you are getting this error because of the dbms_output.put_line which displays the output.Try switching to UTL_FILE at the server side OR Try for any alternatives
By the way, the code can be simplified to:
declare
type clob_array is table of clob;
index_array clob_array := clob_array();
begin
for r in (
select replace(dbms_metadata.get_ddl('INDEX', index_name), '"C",') as index_ddl
from user_indexes
where table_name = 'A'
)
loop
index_array.extend;
index_array(index_array.count) := r.index_ddl;
dbms_output.put_line(substr(index_array(index_array.count), 1, 32767));
end loop;
end;
I used substr() to limit the value passed to dbms_output.put_line to its documented limit. You could probably work around it by splitting the text into smaller chunks, and maybe finding the position of the last blank space before position 32767 in order to avoid splitting a word.
Here's what I came up with:
declare
type clob_array is table of clob;
index_array clob_array := clob_array();
procedure put_line
( p_text clob )
is
max_len constant simple_integer := 32767;
line varchar2(max_len);
remainder clob := p_text;
begin
while dbms_lob.getlength(remainder) > max_len loop
line := dbms_lob.substr(remainder,max_len);
line := substr(line, 1, instr(line, ' ', -1));
remainder := substr(remainder, length(line) +1);
dbms_output.put_line(line);
end loop;
if length(trim(remainder)) > 0 then
dbms_output.put_line(remainder);
end if;
end put_line;
begin
for r in (
select replace(dbms_metadata.get_ddl('INDEX', index_name), '"C",') as index_ddl
from user_indexes
where table_name = 'A'
)
loop
index_array.extend;
index_array(index_array.count) := r.index_ddl;
put_line(index_array(index_array.count));
end loop;
end;

dbms_xmldom - creating an xml from an oracle database

I am trying to create an xml file from an oracle database.
I am not able to get the correct format and was wondering if I could get some assistance.
This is part of the script:
l_record_element := dbms_xmldom.createElement(l_domdoc, 'record_type');
dbms_xmldom.setAttribute(l_record_element,'desc', r_dept.public1);
l_record_node := dbms_xmldom.appendChild(l_dept_node,dbms_xmldom.makeNode(l_record_element));
my output:
<record_type desc="Public"/>
The output I need:
<record_type desc="Public">PUBLIC</record_type>
Thanks!
You need to create and append a text node with the PUBIC value.
Demo showing that coming from the same cursor that provides the record type:
set serveroutput on
declare
l_domdoc dbms_xmldom.domdocument;
l_dept_node dbms_xmldom.domnode;
l_record_node dbms_xmldom.domnode;
l_record_element dbms_xmldom.domelement;
l_record_text dbms_xmldom.domtext;
l_tmp_node dbms_xmldom.domnode;
l_xmltype xmltype;
l_buffer varchar2(32767);
begin
l_domdoc := dbms_xmldom.newDOMDocument; --(xmltype('<data />'));
for r_dept in (select 'Public' as public1, 'PUBLIC' as public2 from dual) loop
l_dept_node := dbms_xmldom.makeNode(l_domdoc);
-- code you showed
l_record_element := dbms_xmldom.createElement(l_domdoc, 'record_type');
dbms_xmldom.setAttribute(l_record_element,'desc', r_dept.public1);
l_record_node := dbms_xmldom.appendChild(l_dept_node, dbms_xmldom.makeNode(l_record_element));
-- add a text node
l_record_text := dbms_xmldom.createTextNode(l_domdoc, r_dept.public2);
l_tmp_node := dbms_xmldom.appendChild(l_record_node, dbms_xmldom.makeNode(l_record_text));
-- display the node for demo
l_xmltype := dbms_xmldom.getXmlType(l_domdoc);
dbms_xmldom.freeDocument(l_domdoc);
dbms_output.put_line(l_xmltype.getClobVal);
end loop;
end;
/
<record_type desc="Public">PUBLIC</record_type>
PL/SQL procedure successfully completed.

How to create tables and User-Defined Records in PostgreSQL?

I have the following table in Oracle
create table x(
x_id number,
x_description VARCHAR2(40),
x_date DATE
)
For example, a PL/SQL table of x names is modeled as a database table with three columns, which store a number and character data and date respectively. Although you cannot use SQL statements to manipulate a PL/SQL table, its primary key gives you array-like access to rows.
declare
type tab_x is table of x%rowtype;
row x%rowtype;
list tab_x;
begin
begin
list.delete;
exception
when collection_is_null then
list := tab_x();
end;
row.observacion := 'Jorge';
row.numero := '1';
row.fch_ins := sysdate;
list.extend;
list(list.last) := row;
row.observacion := 'Andrea';
row.numero := '2';
row.fch_ins := sysdate;
list.extend;
list(list.last) := row;
row.observacion := 'Jose';
row.numero := '3';
row.fch_ins := sysdate;
list.extend;
list(list.last) := row;
row.observacion := 'Lucas';
row.numero := '4';
row.fch_ins := sysdate;
list.extend;
list(list.last) := row;
for i in list.first .. list.last loop
row := list(i);
dbms_output.put_line(row.x_id ||' - '|| row.x_description ||' - '|| row.x_date);
end loop;
end;
Output:
1 - Jorge - 13/12/16
2 - Jose - 13/12/16
3 - Andrea - 13/12/16
4 - Lucas - 13/12/16
How can I do this in PostgreSQL?
AFAIK "table of" does not exist in pg but you can use temporary tables for it. You work with them with usual INSERT/ UPDATE/ DELETE commands. Also %rowtype works on them as usual. Temp table lives only untill session exists.
Plus - you probably know "dbms_output.put_line" is "raise NOTICE" in pg.
Plus to run it as anonymous block use "DO $$ ...here your code ... $$"

Oracle Sql selecting all data from a list of views

I am trying to write a sql to select all data from a list of views for a particular view.
I'm getting all the user view this way:
select view_name from user_views
Say the output is:
emp_v
dept_v
countries_v
jobs_v
Is it possible for me to pass one of these views as parameter in the original sql(which is pulling all the views) so i can get all the data in this view?
Thanks
This might be what you are looking for. This will loop those views and select everything in them. NOTE: This can cause a lot of DBMS_OUTPUT. I suggest hardcoding the cursor for a specific view first to make sure it is what you are looking for.
(1) Create this procedure...
CREATE OR REPLACE procedure print_view(p_query in varchar2) is
l_theCursor integer default dbms_sql.open_cursor;
l_columnValue varchar2(4000);
l_status integer;
l_descTbl dbms_sql.desc_tab;
l_rowCnt number := 0;
l_colCnt number;
begin
dbms_sql.parse(l_theCursor, p_query, dbms_sql.native);
dbms_sql.describe_columns(l_theCursor, l_colCnt, l_descTbl);
dbms_output.put_line(l_colCnt);
for i in 1 .. l_colCnt loop
dbms_sql.define_column(l_theCursor, i, l_columnValue, 4000);
end loop;
l_status := dbms_sql.execute(l_theCursor);
while(dbms_sql.fetch_rows(l_theCursor) > 0) loop
l_rowCnt := l_rowCnt +1;
dbms_output.put_line('========== ROW '||l_rowCnt||' ==========');
for i in 1 .. l_colCnt loop
dbms_sql.column_value(l_theCursor, i, l_columnValue);
dbms_output.put_line(rpad(l_descTbl(i).col_name, 30)||': '||substr(l_columnValue, 1, 200));
end loop;
end loop;
end print_view;
/
(2) Then run this...
declare
/*
NOTE: Edit the where clause. You should try this with just a single view first to make sure this is what you want.
*/
cursor cursor1 is
select view_name from user_views
where view_name in ('emp_v','dept_v','countries_v','jobs_v');
begin
for c1 in cursor1 loop
print_view('select * from '|| c1.view_name); /* Pass the view as a parameter like requested. */
end loop;
end;
/

Dynamically Identify Table (Table Identified by Variable)

I'm trying to create a procedure that will allow me to write an existing row to another table dynamically but the row declaration and insert-statement in the following snippet don't work. The error message indicates that the view hasn't been identified although outputting the target_table.table_name works just fine.
More will be added to the block later on - such as a column with the operation (e.g. INSERT or UPDATE). This is just a simple example and the last procedure (pass_reference) is used to trigger the procedure.
Any help would be much appreciated.
CREATE OR REPLACE PROCEDURE denormalize (new_cursor sys_refcursor, target_table_name varchar)
IS
target_table user_tables%rowtype;
sql_target_table varchar(200) := 'select * from user_tables where table_name = :target_table_name';
row target_table%rowtype;
BEGIN
execute immediate sql_target_table into target_table using target_table_name;
LOOP
fetch new_cursor into row;
exit when new_cursor%notfound;
insert into target_table values row;
commit;
END LOOP;
END denormalize;
/
CREATE OR REPLACE PROCEDURE pass_reference
AS
new_cursor sys_refcursor;
BEGIN
open new_cursor for select * from sales where sales_id=1;
denormalize(new_cursor, 'NEW_SALES');
END;
/
please check this code, it's not working only as for example, as you see for working columns in your cursor should be named as columns in destination table.
I take this code from package that create html table in mail base on view, hope you found this example useful
good luck
declare
in_view_name varchar2(30);
in_table_name varchar2(30) := 'your_new_table';
out_rc number;
out_rc_txt varchar2(1000);
l_cursor number;
l_sql varchar2(50) := 'select * from ' || in_view_name;
l_col_cnt binary_integer;
l_col_tab dbms_sql.desc_tab;
l_column_value varchar2(4000);
l_is_empty boolean := true;
l_insert_header varchar2(1000);
l_insert varchar2(32000);
begin
out_rc := 0;
out_rc_txt := 'OK';
l_cursor := dbms_sql.open_cursor;
dbms_sql.parse(l_cursor, l_sql, dbms_sql.native);
l_col_cnt := dbms_sql.execute(l_cursor);
dbms_sql.describe_columns(l_cursor, l_col_cnt, l_col_tab);
l_insert_header := 'insert into '||in_table_name||'(';
if l_col_cnt > 0 then
-- header
for i in l_col_tab.first .. l_col_tab.last loop
dbms_lob.append(l_insert_header, l_col_tab(i).col_name);
if i != l_col_tab.last then
dbms_lob.append(l_insert_header, ',');
end if;
dbms_sql.define_column(l_cursor, i, l_column_value, 4000);
end loop;
l_insert_header := l_insert_header || ') values(';
-- data
while dbms_sql.fetch_rows(l_cursor) > 0 loop
l_is_empty := false;
l_insert := l_insert_header;
for i in l_col_tab.first .. l_col_tab.last loop
dbms_sql.column_value(l_cursor, i, l_column_value);
l_insert := l_insert || '''' || l_column_value || ''','
if not in_attachment then
dbms_lob.append(out_table, l_td);
end if;
if (not in_attachment) or (l_column_value is not null) then
dbms_lob.append(out_table, nvl(l_column_value, l_nbsp));
end if;
if (not in_attachment) or (i != l_col_tab.last) then
dbms_lob.append(out_table, l_tdc);
end if;
end loop;
l_insert := substr(l_insert, 1, length(l_insert) - 1) || ')';
execute immediate l_insert;
end loop;
end if;
dbms_sql.close_cursor(l_cursor);
end;