How to fetch entire multi line Statement using grep in Unix - linux

--Oracle 11g
--Linux
I am trying to create missing objects, and want to fetch the ddl for any particular Object from the ddl of all objects in a file.
I want to fetch the ddl for one object using grep, but the create statement is broken into multi-line, and through grep only one line is getting fetched.
How to fetch complete create statement using grep or any other method to do this?
Let's say, below is the file containing many create statements for all objects, I want to fetch only create statements for only missing objects, so i grep missing object_name from the ddl_file.
file containing all create_statement dll : create_statement_ddl.txt
cat create_statement_ddl.txt
CREATE OR REPLACE TRIGGER pt_set_session_tz
AFTER LOGON ON SCHEMA
DECLARE
v_tz VARCHAR2(100);
v_no_data BOOLEAN:=FALSE;
BEGIN
v_tz:=pkg_tool.get_db_timezone;
IF (v_tz IS NOT NULL) THEN
EXECUTE IMMEDIATE 'ALTER SESSION SET TIME_ZONE=' || ''''||v_tz||'''';
END IF;
END;
/
create or replace trigger PT_AI_CMPL_STAT after INSERT on PA_CMPL_STAT for each row
begin
INSERT /*+ B1805_DDL */ INTO PH_CMPL_STAT
(CMPL_STAT_ID,
CPNT_TYP_ID,
CMPL_STAT_DESC,
PROVIDE_CRDT,
TAP_DEF_ID,
REQUIRE_ESIG,
LST_UPD_USR,
LST_UPD_TSTMP,
ACTION
)
VALUES
(:new.CMPL_STAT_ID,
:new.CPNT_TYP_ID,
:new.CMPL_STAT_DESC,
:new.PROVIDE_CRDT,
:new.TAP_DEF_ID,
:new.REQUIRE_ESIG,
:new.LST_UPD_USR,
:new.LST_UPD_TSTMP,
'I'
);
end;
/
create or replace trigger PT_AU_CMPL_STAT after UPDATE on PA_CMPL_STAT for each row
begin
INSERT /*+ B1805_DDL */ INTO PH_CMPL_STAT
(CMPL_STAT_ID,
CPNT_TYP_ID,
CMPL_STAT_DESC,
PROVIDE_CRDT,
TAP_DEF_ID,
REQUIRE_ESIG,
LST_UPD_USR,
LST_UPD_TSTMP,
ACTION
)
VALUES
(:new.CMPL_STAT_ID,
:new.CPNT_TYP_ID,
:new.CMPL_STAT_DESC,
:new.PROVIDE_CRDT,
:new.TAP_DEF_ID,
:new.REQUIRE_ESIG,
:new.LST_UPD_USR,
:new.LST_UPD_TSTMP,
'M'
);
end;
/
grep missing_object_name `cat create_statement_ddl.txt` > create_object.sql
grep pt_set_session_tz `cat create_statement_ddl.txt` > create_object.sql
What I am getting is
CREATE OR REPLACE TRIGGER pt_set_session_tz
What i want is below:
CREATE OR REPLACE TRIGGER pt_set_session_tz
AFTER LOGON ON SCHEMA
DECLARE
v_tz VARCHAR2(100);
v_no_data BOOLEAN:=FALSE;
BEGIN
v_tz:=pkg_tool.get_db_timezone;
IF (v_tz IS NOT NULL) THEN
EXECUTE IMMEDIATE 'ALTER SESSION SET TIME_ZONE=' || ''''||v_tz||'''';
END IF;
END;
/

grep is for finding regexp on individual lines and printing the result, it's the wrong tool for this. If I understand you correctly then with awk it'd just be:
awk '/pt_set_session_tz/{f=1} f; /^\//{f=0}' create_statement_ddl.txt

Related

variadic function and parametised safe queries

Using node-pg and Postgres 11.
I have a variadic function in postgres
CALL schema.function(('1'),('2'))
In order to prevent sql injection I need to do something like
await client.query('CALL schema.function($1::smallint);', XXX);
Where XXX is what the call uses for substitutions.
The problem is that my function expects a list of records, not an array ('1'),('2').
Has anyone else encountered this?
Stored procedure:
CREATE OR REPLACE PROCEDURE update_events_variadic(
VARIADIC _events_array event_array[]
)
LANGUAGE plpgsql AS
$$
DECLARE
EVENT_RECORD RECORD;
BEGIN
FOR EVENT_RECORD IN
SELECT
metadata,
payload
FROM unnest(_events_array) as t(
metadata,
payload
)
LOOP
INSERT INTO events
(
metadata,
payload
)
VALUES
(
EVENT_RECORD.metadata,
EVENT_RECORD.payload
);
END LOOP;
END;
$$;
If that's the only type of call you expect, variadic is just syntax noise. You can redefine your procedure to accept a regular array of type that fits into table events, that you're trying to insert into. Also, you don't have to loop over a select from unnest() - there's a FORACH loop that lets you iterate over the array directly.
CREATE OR REPLACE PROCEDURE update_events ( events_array events[] )
LANGUAGE plpgsql AS $$
DECLARE
event_record events;
BEGIN
FOREACH event_record IN ARRAY events_array LOOP
INSERT INTO events
( metadata,
payload
)
VALUES
( event_record.metadata,
event_record.payload
);
END LOOP;
END; $$;
You can also insert directly from a select without any loop, gaining some performance from that and from switching to plain language SQL that doesn't have the PLpgSQL overhead:
CREATE OR REPLACE PROCEDURE update_events ( events_array events[] )
LANGUAGE SQL AS $$
INSERT INTO events
( metadata,
payload
)
select
event_record.metadata,
event_record.payload
from unnest(events_array)
as event_record
( metadata,
payload
)
$$;
You can either cast each element in the array, or cast the whole array at once:
await client.query('CALL schema.function( ARRAY[$1]::events[] );', XXX);
Demo. If you happen to initially get a malformed record literal error after these changes, it means that some or all of what you're supplying as XXX isn't a valid set of values that can make up events record. Another demo, showing some examples.
I'm naively assuming your events table has only those two columns - if it has more, you'll have to define an intermediate type or table and use that as your array type in procedure definition and call argument cast. Example
In this case variadic would be useful if you wanted to be able to use both types of calls below:
call update_events( --not an array in a strict sense, just a bunch of arguments
('metadata1','payload1')::events,
('metadata2','payload2')::events,
('metadata3','payload3')::events);
The above isn't passing an array in a strict sense, just a bunch of arguments that the function will internally collect and make available as a single, actual array.
call update_events( VARIADIC
ARRAY[('metadata1','payload1'),
('metadata2','payload2'),
('metadata3','payload3')
]::events[] );
VARIADIC informs the function that nothing has to be collected into an array as the argument list is already provided as one.

How to create a complex PL/SQL procedure with Python, cx_Oracle

I have a multi-line PL/SQL procedure, which I have to create.
The SQL procedure is similar to the one below,
CREATE OR REPLACE PROCEDURE HELLO AS
TYPE cur_cur is REF CURSOR;
v_cur_cur cur_cur;
age NUMBER;
day VARCHAR2(10);
date DATE;
BEGIN
<Some Execute Immediate stmts>
<Some insert stmts>
commit;
END;
Currently what I am doing is,
host= "localhost"
port= 1521
sid= "abcbcadacsw.com"
user= "groups"
password= "hello!bye1209"
dsn_tns = oracledb.makedsn(host, port, service_name=sid)
print(dsn_tns)
db_conn = oracledb.connect(user=user, password=password, dsn=dsn_tns)
curs= db_conn.cursor()
curs.execute("""
CREATE OR REPLACE PROCEDURE HELLO AS
TYPE cur_cur is REF CURSOR;
v_cur_cur cur_cur;
age NUMBER;
day VARCHAR2(10);
date DATE;
BEGIN
<Some Execute Immediate stmts>
<Some insert stmts>
commit;
END;
""")
The thing is the code runs without any issues, there are not runtime errors or anything ... but when i log into the DB to check for the created procedure, its not present. When i try to execute the procedure, it says 'identifier must be declared ... '.
I have tried converting it into a single line
curs.execute("""CREATE OR REPLACE PROCEDURE HELLO AS TYPE cur_cur is REF CURSOR; v_cur_cur cur_cur; age NUMBER; day VARCHAR2(10); date DATE; BEGIN <Some Execute Immediate stmts> <Some insert stmts> commit; END;""")
This also does not work.
Please assist, ignore the correctness of the above shown procedure, i cannot put the original here, and i dont know much of SQL, i just need to know how to successfully create it in Python.
The driver doesn't (yet) return Oracle DB's 'success with info' errors so if there is a problem with the PL/SQL code you won't find out about it unless you explicitly query the error view.
In SQL*Plus:
create or replace procedure fred as
begin
f();
end;
/
would give:
Warning: Procedure created with compilation errors.
and a subsequent show errors will give
Errors for PROCEDURE FRED:
LINE/COL ERROR
-------- -----------------------------------------------------------------
3/3 PL/SQL: Statement ignored
3/3 PLS-00201: identifier 'F' must be declared
With cx_Oracle (and its new version python-oracledb) you don't get the initial indication there was a problem so you always should do the equivalent of the show errors command to check the error view. Try something like:
with connection.cursor() as cursor:
sql = """create or replace procedure fred as
begin
f();
end;"""
cursor.execute(sql)
sql = """select name, type, line, position, text
from user_errors
order by name, type, line, position"""
for r in cursor.execute(sql):
print(r)
which will show output like:
('FRED', 'PROCEDURE', 3, 20, 'PL/SQL: Statement ignored')
('FRED', 'PROCEDURE', 3, 20, "PLS-00201: identifier 'F' must be declared")
This is shown in the documentation Creating Stored Procedures and Packages.

How to export query "DESCRIBE table" ORACLE to .xls?

How to export query of "DESCRIBE table" to excel file?
For example :
I have query like that "DESCRIBE Mst_Fi_Bond_Issuers;" and i have result like that :
I want export that automatically to excel table. Please help me. Thanks..
You could use an SQL block like this: [please edit as you see fit]
DECLARE
createdir VARCHAR2(2000);
directory NUMBER;
filen VARCHAR2(50);
dirn VARCHAR2(50);
filedat UTL_FILE.file_type;
BEGIN
dirn := 'DESC';
filen := 'yourfilename.csv';
createdir := q'{create directory DESC as '[your directory]'}';
execute immediate (createdir);
filedat := UTL_FILE.fopen(dirn, filen, 'W', 32767);
UTL_FILE.put_line (filedat, 'NAME;TYPE');
for rowdat in (select column_name || ';' || data_type currow from dba_tab_columns)
LOOP
UTL_FILE.put_line (filedat, rowdat.currow);
END LOOP;
UTL_FILE.fclose (filedat);
END;
/
As jera already said, it´s much better to do a query and write results to a file, because you have much more flexibility in gathering informations and formatting output.
But maybe due to restrictions, your not able to access the dba_... Objects.
Instead of that, have a look on the USER_... - Views, which are always available to a oracle-user
Everything you want to know can be found there (f.e. like USER_TABLES. USER_TAB_COLS, USER_PROCEDURES, etc.).

column value can be inserted

I know I can use CASE statement inside VALUES part of an insert statement but I am a bit confused.
I have a statement like,
You can try also a procedure:
create or replace procedure insert_XYZ (P_ED_MSISDN IN VARCHAR2,
P_ED_OTHER_PARTY IN VARCHAR2) is
begin
INSERT INTO TABLE_XYZ ( ED_MSISDN,
ED_OTHER_PARTY,
ED_DURATION)
VALUES (P_ED_MSISDN ,
P_ED_OTHER_PARTY ,
CASE
WHEN P_ED_OTHER_PARTY = '6598898745' THEN
'9999999'
ELSE
'88888'
END);
END;
Here's a query structure that you can use (using JohnnyBeGoody's suggestion of using a SELECT statement to select the values).
INSERT INTO TABLE_XYZ (ED_MSISDN, ED_OTHER_PARTY, ED_DURATION)
SELECT
'2054896545' ED_MSISDN,
'6598898745' ED_OTHER_PARTY,
CASE
WHEN ED_OTHER_PARTY = '6598898745' THEN '9999999'
ELSE '88888'
END ED_DURATION
FROM DUAL;
You cannot self-reference a column in an insert statement - that would cause an "ORA-00984: column not allowed here" error.
You could, however, use a before insert trigger to achieve the same functionality:
CREATE OR REPLACE TRIGGER table_xyz_tr
BEFORE INSERT ON table_xyz
FOR EACH ROW
NEW.ed_duration = CASE NEW.ed_other_party
WHEN '6598898745' THEN '9999999'
ELSE '88888' END;
END;

String concatenation within an exception

In my trigger procedures I use RAISE EXCEPTION for messages. I have no problem with simple messages, but if I want to give the user some more complex feedback, I face a problem: the concatenation operator doesn't work within RAISE EXCEPTION statement.
First, I tried this:
CREATE OR REPLACE FUNCTION hlidej_datum_kon() RETURNS trigger AS $$
DECLARE
od date;
BEGIN
SELECT a.datum_od FROM akce AS a WHERE a.kod_akce = (
SELECT b.kod_akce FROM sj AS b WHERE b.kod_sj = NEW.kod_sj
) INTO od;
IF NEW.datum < od THEN
RAISE EXCEPTION 'Kontext nemohl být odkryt před začátkem akce ('||TO_CHAR(od)||')!'
ROLLBACK;
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
Didn't work. So I tried to put the whole text to a text variable, but I didn't find how to put the variable's contents to the exception statement so that it would be printed as a message.
My question is: how to print a message containing variables in a PostgreSQL trigger function?
Just for sake of completeness, here is my trigger:
CREATE TRIGGER hlidej_datum_kon
AFTER INSERT OR UPDATE ON kontext
FOR EACH ROW
EXECUTE PROCEDURE hlidej_datum_kon();
END;
You not need to use concat. You can use wildcards instead:
RAISE EXCEPTION 'Kontext nemohl být odkryt před začátkem akce (%)!', od;
There are two bugs
first parameter of RAISE statement is format string - this string should be constant. It can contains a substitution symbols '%' and values for these symbols are places as others parameters of RAISE statement.
There should not be used ROLLBACK statement. RAISE EXCEPTION throws exceptions and ROLLBACK statement is newer executed. You cannot control transaction explicitly in PL/pgSQL - so you cannot use ROLLBACK or COMMIT statement in plpgsql ever.You can use a exception trapping
BEGIN
RAISE EXCEPTION 'blabla';
EXCEPTION WHEN some_exception_identif_see_list_of_exception_in_doc THEN
.. do some or do nothing
END;

Resources