Convert SQLERRM( SQLCODE ) in Oracle to PostgreSQL - postgresql

I'm converting Oracle to PostgreSQL, I haven't alternative for SQLERRM( SQLCODE ) like Oracle in PostgreSQL.
DECLARE
name employees.last_name%TYPE;
v_code NUMBER;
v_errm VARCHAR2(64);
BEGIN
SELECT last_name INTO name FROM employees WHERE employee_id = 1000;
EXCEPTION
WHEN OTHERS THEN
v_code := SQLERRM( SQLCODE );
v_errm := SUBSTR(SQLERRM, 1 , 64);
DBMS_OUTPUT.PUT_LINE('The error code is ' || v_code || '- ' || v_errm);
END;

Look at the documentation:
Within an exception handler, the special variable SQLSTATE contains the error code that corresponds to the exception that was raised (refer to Table A.1 for a list of possible error codes). The special variable SQLERRM contains the error message associated with the exception. These variables are undefined outside exception handlers.
Within an exception handler, one may also retrieve information about the current exception by using the GET STACKED DIAGNOSTICS command, which has the form:
GET STACKED DIAGNOSTICS variable { = | := } item [ , ... ];
Each item is a key word identifying a status value to be assigned to the specified variable (which should be of the right data type to receive it). The currently available status items are shown in Table 43.2.
So that could be
v_code := SQLSTATE;
v_errm := substr(SQLERRM, 1, 64);

Related

Using a select in the set of an update pgsql

Hello i'm wondering why I can't use an SELECT in my SET of an UPDATE like this :
UPDATE "WU_Users" SET (SELECT "colonnesName" FROM colonnes WHERE "id" = i) = '0/0' WHERE "IDWU_Users" = (SELECT "idUser" FROM query WHERE "id" = i);
I get this error :
erreur de syntaxe sur ou près de « SELECT »
LIGNE 22 : UPDATE "WU_Users" SET (SELECT "colonnesName" FRO...
Apparently you are attempting to derive the column name to be updated from a column in another table. However, parsing an SQL statement requires the structural components (table names, column name, view names, ...) to be fixed before the statement can be parsed. What you are attempting would require parsing part of the statement inside parsing the statement. Currently, that is not going to happen. To accomplish what you are attempting will require dynamic SQL in order to generate the UPDATE statement at run time. Something like:
create or replace
procedure update_wu_users_from_idwu_users(i integer) -- assumes i as parameter as it is not defined
language plpgsql
as $$
declare
k_update_stmt constant text =
$stmt$ update wu_users set %I = '0/0'
where wu_users =
(select iduser
from query -- assumes query is a table, view, or materalized view
where id = i
)
$stmt$;
l_update_column text;
l_update_stmt text;
begin
select colonnesname
into l_update_column
from columns
where id = i;
if not found then
raise exception 'No column name found on table colonnes for id=%',i;
end if;
l_update_stmt = format(k_update_stmt,k_update_stmt);
raise notice '%', l_update_stmt; -- for debigging for prod raise_log might be better
execute l_update_stmt;
end;
$$;
NOTE: In the absence of table definition and test data the above IS NOT tested. Nor will I quarantine it complies. It is offered as example.

Exception when trying to execute a query from pascal

The following function takes the cuits (the cuit is like social security number) from a grid and inserts them into a temporary table. I'm using Delphi XE7 and Firebird 2.5.
function TfImportFileARBARetenPercep.fxListClientFromGrid(
pboClient: Boolean): WideString;
var
wsAux : WideString;
stTable, stCuit: String;
qCliProv, qCuitsExcl, qCuit : TFXQuery;
niRow : Integer;
begin
wsAux := '';
qCuitsExcl.SQL.Text := 'Create global temporary table TempExcl (cuitExcl varchar(13)) on commit delete rows';
qCuitsExcl.ExecSQL;
if pboClient then
begin
stTable := 'Client'
end
else
begin
stTable := 'Prov';
end;
for niRow := 1 to gDetails.RowCount - 1 do
if Trim(gDetails.Cells[3,niRow]) <> '' then
Begin
stCuit := QuotedStr(Trim(gDetails.Cells[3,niRow]));
qCuit.SQL.Text := 'Insert into TempExcl(:cuitExcl)';
qCuit.ParamByName('cuitExcl').AsString := stCuit;
qCuit.ExecSQL;//←←←←←←←←←←←←←←←←this line throws the exception
End;
qCuitsExcl.SQL.Text :='';
qCuitsExcl.SQL.Text := 'commit;';//to commit previous inserts
qCuitsExcl.SQL.Add('drop table TempExcl;');//in case the table is not deleted when the connection is closed
qCuitsExcl.SQL.Add('commit;');//commit previous drop
qCuitsExcl.ExecSQL;
//the rest of the code only returns the list of cuits that were loaded
↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
try
qCliProv.SQL.Text :=
' Select Code' +
' From ' + stTable +
' Where Active = 1 ';
if gDetails.RowCount > 0 then
begin
qCliProv.SQL.Add('And Cuit Not In (Select cuitExcl from TempExcl)');
end;
qCliProv.Open;
wsAux := '';
while not qCliProv.Eof do
begin
wsAux := wsAux + iif((wsAux = ''), '', ',') + qCliProv.FieldByName('Code').AsString;
qCliProv.Next;
end;
Result := wsAux;
finally
FreeAndNil(qCliProv);
FreeAndNil(qCuit);
FreeAndNil(qCuitsExcluidos);
end;
end;
When trying to execute the line qCuit.ExecSQL; the following exception is thrown:
Project ERP.exe raised exception class EIBNativeException with message
'[FireDAC][Phys][FB] Dynamic SQL Error SQL error code = -104 Token
unknown - line 1, column 27 ?'.
I don't know why it throws this exception
The problem is that this is wrong:
qCuit.SQL.Text := 'Insert into TempExcl(:cuitExcl)';
This will result in a generated query that is:
Insert into TempExcl(?)
Which is a syntax error (the "Token unknown"), because Firebird doesn't expect a question mark (parameter placeholder) in this position, it expects an identifier (column name).
A valid query would be either:
Insert into TempExcl (cuitExcl) values (?)
or:
Insert into TempExcl values (?)
The first form is preferred, as you explicitly specify the columns, though in this case not really necessary as the table has one column anyway.
In other words, you need to use:
qCuit.SQL.Text := 'Insert into TempExcl (cuitExcl) values (:cuitExcl)';
As an aside, I'm not familiar with Delphi or how and when queries are prepared, but it might be better to move this statement out of the loop, so it is prepared only once (in case Delphi prepares the statement again any time the qCuit.SQL.Text is assigned a value).
The comment "// in case the table is not deleted when the connection is closed" seems to indicate a lack of understanding of Global Temporary Tables. They are intended to be permanent objects, available for re-use by your application. That is on commit delete rows makes the content available only to your current transaction, while on commit preserve rows will make the content available for the remainder of your connection (and only your connection), deleting the data when the connection is closed. Creating and dropping a GTT in something that is executed more than once is an indication something is wrong in your design.

FOREACH expression must not be null

At declaration level i have:
sqlDel text := 'DELETE FROM %s WHERE %s IS NULL';
fields text[];
field_name text;
ptable := 'myTable';
Somewhere behind i fill in fields so it contains 3 items - i checked it's fine. Nevertheless down below i have this for loop statement which worked fine until i added this line :
EXECUTE format(sqlDel, ptable, field_name);
error says:
ERROR: FOREACH expression must not be null
Foreach loop:
FOREACH field_name IN ARRAY fields
LOOP
EXECUTE format(sqlDel, ptable, field_name);
raise notice 'Primary key column: %', field_name;
END LOOP;
The error message is clean - variable fields is null. You should to set it first.
fields = ARRAY['id'];
FOREACH ...

PostgreSQL Stored Procedures with text as parameters

I have searched and could not find an open post regarding this topic:
I'm working with stored procedures in PostgreSQL and I'm currently being asked to create one that will collect certain information from a publisher based on the only parameter passed, their publishercode.
This code is in the existing table as a "character" type and as such I'm passing it the same and calling the function with that same type input, but I'm only obtaining a "NULL" value from my input and I'm not sure why.
This is my code:
CREATE OR REPLACE FUNCTION uspGetPubStatsMAC (pubcode publisher.publishercode%TYPE) -- Publisher Code
RETURNS text AS
$$
DECLARE PubCode publisher.publishercode%TYPE;
PubName text;
NumOfAuth int;
NumOfPubBooks int;
HiValBook text;
NumHiValBook int;
TotNumHiValBook int;
BEGIN
-- Get the publisher's name from the given code
RAISE NOTICE 'Code: (%)', pubcode;
SELECT publishername INTO PubName
FROM publisher
WHERE publishercode = pubcode;
RAISE NOTICE 'Name: (%)', PubName;
-- check if a publisher exists with the given code
IF ( PubName is null )
THEN
RAISE NOTICE 'No publisher exists with the given code (%)', pubcode;
RETURN (-1);
END IF;
RAISE NOTICE 'Current Statistics of the publisher (%)', pubcode;
RAISE NOTICE ' The Name of the Publisher: (%)', trim (trailing ' ' from PubName);
SELECT count(distinct a.authornum) INTO NumOfAuth
FROM publisher p, book b, wrote w, author a
WHERE p.publishercode = b.publishercode
and b.bookcode = w.bookcode;
RAISE NOTICE ' The number of distinct authors who have written book(s) for this publisher: (%)', NumOfAuth;
END;
$$ language plpgsql;
SELECT uspGetPubStatsMAC('AH');
Where 'AH' is the code corresponding to publisher Arkham House in my table. Any idea of what I'm doing wrong?
You have at least two errors in your code:
pubcode is the parameter passed in to the function. Then you declare a local PubCode variable. Remove that declaration.
Your function should RETURN some text value representing PUBstatsMAC when it succeeds. Currently you only return -1 if there is an error. At that point you should RAISE an error with an appropriate message and forget about a return value, otherwise a bad return value may get cascaded into other code.
Furthermore, after a SELECT ... INTO ... statement you should test for the FOUND internal variable rather than a NULL value in a local variable:
IF NOT FOUND THEN
RAISE 'No publisher exists with the given code (%)', pubcode;
END IF;
And your query on number of distinct authors will give a bad result because you are missing a join clause on table author.
Then, you have a bunch of unused local variables declared.
Lastly, it is bad practice to return your information from a function in a bunch of RAISE NOTICE statements.
Thanks a lot for your feedback! I will test some of these fixes.
With respect to the return of -1 that is a requirement by the teacher. He wants a -1 if an error occurred at run time and a 0 if not. Hence why the output is formatted as raise notices. Also a requirement.
The extra local variables are there because that is an unfinished portion of the entire procedure code.
I tried what you suggested though and it worked like a charm. The extra declaration of PubCode was causing the problem.
Thanks so much!!!

In FirebirdSql, how to return exception message from procedure

I want to return the error message from a procedure when an exception happens. In SQL Server you would select the Error_Number() and Error_Message(). How would I do it in FirebirdSql
SET TERM ^ ;
CREATE PROCEDURE sprocname
( id int )
RETURNS
( gcode int, errmsg varchar(250) )
AS
BEGIN
gcode = 0;
errmsg = '';
-- do procedure code here
WHEN ANY DO
BEGIN
gcode = gdscode; -- ??
errmsg = ??;
END
SUSPEND;
END^
SET TERM ; ^
Unfortunately, you will need to do that at the client side, as it is currently not possible to obtain this in PSQL. There is a feature request in the Firebird tracker, which has been implemented for Firebird 4, which is expected to be released in 2019.
See Firebird 4 release notes, section System Function RDB$ERROR():
The function RDB$ERROR() takes a PSQL error context as input and
returns the specific context of the active exception. Its scope is
confined to the context of the exception-handling block in PSQL.
Outside the exception handling block, RDB$ERROR always contains
NULL.
The type of the return value depends on the context.
Syntax Rules
RDB$ERROR ( context )
context ::= { GDSCODE | SQLCODE | SQLSTATE | EXCEPTION | MESSAGE }
[..]
Example
BEGIN
...
WHEN ANY DO
EXECUTE PROCEDURE P_LOG_EXCEPTION(RDB$ERROR(MESSAGE));
END
CREATE PROCEDURE ADD_COUNTRY (
ACountryName COUNTRYNAME,
ACurrency VARCHAR(10) )
AS
BEGIN
INSERT INTO country (country,
currency)
VALUES (:ACountryName,
:ACurrency);
WHEN ANY DO
BEGIN
-- write an error in log
IN AUTONOMOUS TRANSACTION DO
INSERT INTO ERROR_LOG (PSQL_MODULE,
GDS_CODE,
SQL_CODE,
SQL_STATE)
VALUES ('ADD_COUNTRY',
GDSCODE,
SQLCODE,
SQLSTATE);
-- Re-throw exception
EXCEPTION;
END
END
http://www.firebirdsql.org/file/documentation/reference_manuals/fblangref25-en/html/fblangref25-psql-handleexceptions.html