String concatenation within an exception - string

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;

Related

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.

node-oracledb error when executing a stored procedure

I'm using Node 10.x,and Oracledb 3.0
I'm trying to execute a stored procedure in node.js
I've Exception block in the procedure,like below:
EXCEPTION
WHEN NO_DATA_FOUND THEN
OUT_STATUS:='FAILURE';
OUT_STATUS_DESC:='USER NOT MAPPED TO A GROUP';
WHEN OTHERS THEN
OUT_STATUS:='FAILURE';
OUT_STATUS_DESC:=dbms_utility.format_error_backtrace;
whenever control goes to Exception block throwing below error in node.js.
Error: ORA-24338: statement handle not executed
But,explicitly if i execute procedure in db with same IN parameters it's giving proper OUT parameters with error and CURSOR is closed.
These are the OUT parameter types in procedure.
OUT_STATUS OUT VARCHAR2,
OUT_STATUS_DESC OUT VARCHAR2,
OUT_MENU_NAME OUT SYS_REFCURSOR
What could be the issue?
All OUT variables must be set to something even if you don't intend to use them in the node-oracledb code, see https://github.com/oracle/node-oracledb/issues/886
It turns out this is true in all Oracle DB drivers (e.g. node-oracledb) based on Oracle's C 'OCI' API, but symptomatic problems are more commonly seen in node-oracledb.
A work-around is, if it has not been opened before then open the OUT_MENU_NAME cursor with zero rows in the exception blocks:
EXCEPTION
WHEN NO_DATA_FOUND THEN
OUT_STATUS:='FAILURE';
OUT_STATUS_DESC:='USER NOT MAPPED TO A GROUP';
-- Replicate your normal cursor output and filter on something that will never be true:
OPEN OUT_MENU_NAME FOR
SELECT 'Nothing' AS col1,
DATE '1970-01-01' AS col2,
0 AS col3
FROM DUAL
WHERE 1 = 0;
WHEN OTHERS THEN
OUT_STATUS:='FAILURE';
OUT_STATUS_DESC:=dbms_utility.format_error_backtrace;
-- Replicate your normal cursor output and filter on something that will never be true:
OPEN OUT_MENU_NAME FOR
SELECT 'Nothing' AS col1,
DATE '1970-01-01' AS col2,
0 AS col3
FROM DUAL
WHERE 1 = 0;

How to fetch entire multi line Statement using grep in Unix

--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

End_Error help, Using unbounded Strings in Ada

Can someone please tell me why I am getting an End_Error exception, I do not see how I am getting past the end of the file if I have a loop that opts out before it reaches this point. If there is an easy fix, I'd love to hear it, I've been stuck for a while and unbounded strings are not really my forte.
with Ada.Text_IO;
use Ada.Text_IO;
with Ada.Strings.Unbounded;
use Ada.Strings.Unbounded;
with Ada.Strings.Unbounded.Text_IO;
use Ada.Strings.UNbounded.Text_IO;
procedure checker is
InWord : Unbounded_String;
dictionary : File_Type;
count : Integer;
begin
Ada.Text_IO.Open
(File => dictionary, Mode => In_File, Name => "dictionary.txt");
loop
exit when End_of_File;
InWord := Get_Line(File => dictionary);
Put(InWord);
New_Line;
end loop;
end checker;
raised ADA.IO_EXCEPTIONS.END_ERROR : a-textio.adb:690
You have
exit when End_of_File;
but since you’re looking for EOF on the input file it should be
exit when End_Of_File(File => Dictionary);
I’m not sure why you see the effect you do - when I tried it, nothing happened until I typed a couple of RETs and then I got the End_Error exception.
As you see, it’s nothing to do with strings, unbounded or otherwise!
As stated in the ARM, End_Of_File without parameters refers to the current input file.
In your case, it just refers to the standard input and not to your file.
Considering Simon's answer and the behaviour you got, I think you use your program with a redirection of the standard input (something like "cat myFile.txt | checker"). Am I right ?

Delphi/Query component - assign a long string (800 char) to a sql.text property trim the sql to 326 char only

I use Delphi/NexusDB and I build SQL (about 800 char long) at run time then I pass it to the nexusdb query.sql.text property to execute it but I found error of invalid token on execution.
I pass SQL like this
Query.SQL.Text := VarStrSQL; // <<---- string variable holding the SQL
when I traced I found SQL string in the Query.SQL.Text is trimmed to 326 character !!
While the string variable that hold the SQL is complete and fine but when I assign that variable to query.sql.text only 326 character passed and of course this result in an error for invalid SQL syntax
Please advise why the SQL string trimmed like that ?
Update:
*I tried memo1.lines.text := VarStrSQL and the memo component also display the string trimmed !! is it possible a character in my string cause that !! a bug in Delphi 2010 that cause TStrings to trim my string ?*
Thanks
Sounds like a bug in DB provider itself. There is no such limitation in TQuery.
My advice shall be to use small SQL, but bound parameters to set the data.
Instead of
Query.SQL.Text := 'INSERT INTO Store_Information (store_name, Sales, Date)
VALUES ('Los Angeles ... ... ...', 900, '10-Jan-1999')';
code
Query.FieldByName('store').AsString := 'Los Angeles ... ... ...'; // here you should have no limitation
Query.FieldByName('sales').AsInteger := 900;
Query.FIeldByName('Date').AsDAteTime := Now;
Query.SQL.Text := 'INSERT INTO Store_Information (store_name, Sales, Date)
VALUES (:store,:sales,:date)';
And your request will be faster, since the statement could be preparated by the engine, then reused.
I found the problem:
It is nxtChar Fields when they are null they have the value #0 and that cause string trimming
however although I check for null like this varisnull() the char fields was able to skip this trap function !!! which makes me go around myself for hours finally I now check them like this
If <nxtChar field> = #0 then <nxtChar field> = '' (or <nxtChar field> = null)

Resources