Catch `concurrent update` error in PostgreSQL - sql

Assume I created a function that I execute in REPEATABLE_READ isolation level in PostgresSQL, e.g.
CREATE FUNCTION some_stuff() RETURNS void AS $$
BEGIN
-- do stuff that could throw an error
END;
$$ LANGUAGE plpgsql;
It's possible for this function internally to throw the following error:
ERROR: could not serialize access due to concurrent update. Is there a way to catch this error and repeat the function/transaction internally?
So have something like this:
CREATE FUNCTION some_stuff() RETURNS void AS $$
BEGIN
try {
-- do stuff that could throw an error
} catch (error) { call some_stuff(); }
END;
$$ LANGUAGE plpgsql;
Or has this error catching to be done in the application level, i.e. in Java that is calling this function?

It's possible for this function internally to throw the following error: ERROR: could not serialize access due to concurrent update. Is there a way to catch this error and repeat the function/transaction internally?
No, it is not.
PostgreSQL's functions are implicitly wrapped in a transaction if one isn't already in progress. There is no way to obtain a new transaction snapshot from within a function in repeatable read or serializable isolation. You can't rollback and begin a new transaction.
For this to work PostgreSQL would need to support top-level procedures with autonomous commit.
You must have the controlling code on a separate connection. This means doing it from a client, or (ab)using dblink for the purpose.

Related

What does $$ (double dollar) mean in Oracle SQL?

This Create package query executes in a SQLPlus util cli without error -
create or replace package PACKAGE2 as
$if $$install_ad_zd_sys $then
procedure LOG( X_MODULE varchar2, X_LEVEL varchar2, X_MESSAGE varchar2);
function LITERAL(X_VALUE varchar2) return varchar2;
else procedure LOG_VER;
$end
end;
/
Just want to know what does double dollar mean here?
Is it a sqlplus specific keyword?
I tried running this whole block as part of jdbc but it gives error 'Invalid column index'. So, I am suspecting the sign is specific to sqlplus, but not able to find the meaning of it.
This is how I am executing the above sql in java jdbc
plSqlstatement = connection.prepareCall(sqlBuffer.toString());
//sqlBuffer contains the whole create package block
//like sqlBuffer.append("CREATE OR REPLACE
//PACKAGE ....").append("/n").append(..next Line) and so on.
It's an inquiry directive:
An inquiry directive provides information about the compilation environment.
An inquiry directive typically appears in the boolean_static_expression of a selection directive, ...
... which is how you are using it, as it's within the $if conditional compilation directive.
You would need to assign a value, which you don't seem to be doing in either execution. But it will compile anyway, through SQL*Plus or JDBC (as in this db<>fiddle).
If you are getting an error from your JDBC call then you need to look at how you are running it - particularly if you are making get/set calls for arguments, as the error message suggests - since there are no arguments to set or retrieve.

OOP equivalent for a goto chain for releasing resources upon error?

In C I've used goto chains for releasing resources upon error, as recommended here. Working with Delphi I've encountered the following situation where I want to gracefully handle memory exhaustion and also prevent memory leaks:
New(A);
A.DoSomething;
New(A.B);
A.B.DoSomething;
New(A.C);
A.C.DoSomething;
As I understand it the way to check for memory exhaustion is by catching an exception thrown by New. Let's say the DoSomething functions all throw Exception on error. SEI CERT's coding standard recommends against in-band error checking and against using exceptions for control flow, at least for Java, which I find very reasonable. I'm unsure how to deal with this situation keeping these recommendations in mind. My idea was to do something like
function AllocStuff : TA;
begin
New(Result);
Result.B := nil;
Result.C := nil;
Result.DoSomething;
New(Result.B);
Result.B.DoSomething;
New(Result.C);
Result.C.DoSomething;
end;
Catching exceptions on the caller:
procedure QuestionableControlFlow;
var
A : TA;
begin
A := nil;
try
A := AllocStuff;
DoSomethingWith(A);
Dispose(A);
except on E : Exception do
begin
if (A <> nil) then
begin
if (A.B <> nil) then
begin
if (A.C <> nil) then
begin
Dispose(A.C);
end;
Dispose(A.B);
end;
Dispose(A);
end;
end;
end;
Is this as bad as it looks? Mixing goto with except seemed worse and this was all I could think of so far.
In Delphi you use try/finally for unmanaged resource lifetime.
For instance
obj := TObject.Create;
try
obj.DoSomething;
finally
obj.Free;
end;
You absolutely don't use try/except for this, although that is a common mistake. That's for handling exceptions which is different from guaranteeing finalisation.
When you need to deal with multiple unmanaged resources in one function you can nest your try/finally blocks. When the nesting is deep that can be messy. Some ideas for dealing with that can be found here: Avoiding nested try...finally blocks in Delphi
Your AllocStuff() should use try/except to catch errors, so it doesn't return invalid data:
function AllocStuff : TA;
begin
New(Result);
try
Result.B := nil;
Result.C := nil;
Result.DoSomething;
New(Result.B);
try
Result.B.DoSomething;
New(Result.C);
try
Result.C.DoSomething;
except
Dispose(Result.C);
raise;
end;
except
Dispose(Result.B);
raise;
end;
except
Dispose(Result);
raise;
end;
end;
And then the caller can use try/finally to release whatever AllocStuff() returns:
procedure QuestionableControlFlow;
var
A : TA;
begin
A := AllocStuff;
try
DoSomethingWith(A);
finally
Dispose(A.C);
Dispose(A.B);
Dispose(A);
end;
end;

Package got invalidated in oracle 11g [duplicate]

I am using oracle 10g and toad 11.5. I am trying to call an api from an anonymous block.
If I recompile the api after adding dbms_output.put_line and then try to execute the anonymous block, it shows error as:
"ORA-06508: PL/SQL: could not find program unit being called".
However if I end current session and open a new session, then the anonymous block will execute with out the error.
Due to this issue, i am made to reconnect the session everytime i make a change to API.
Can anyone help if this issue can be resolved by making any configurations in toad or database level.
I suspect you're only reporting the last error in a stack like this:
ORA-04068: existing state of packages has been discarded
ORA-04061: existing state of package body "schema.package" has been invalidated
ORA-04065: not executed, altered or dropped package body "schema.package"
ORA-06508: PL/SQL: could not find program unit being called: "schema.package"
If so, that's because your package is stateful:
The values of the variables, constants, and cursors that a package
declares (in either its specification or body) comprise its package
state. If a PL/SQL package declares at least one variable, constant,
or cursor, then the package is stateful; otherwise, it is stateless.
When you recompile the state is lost:
If the body of an instantiated, stateful package is recompiled (either
explicitly, with the "ALTER PACKAGE Statement", or implicitly), the
next invocation of a subprogram in the package causes Oracle Database
to discard the existing package state and raise the exception
ORA-04068.
After PL/SQL raises the exception, a reference to the package causes
Oracle Database to re-instantiate the package, which re-initializes
it...
You can't avoid this if your package has state. I think it's fairly rare to really need a package to be stateful though, so you should revisit anything you have declared in the package, but outside a function or procedure, to see if it's really needed at that level. Since you're on 10g though, that includes constants, not just variables and cursors.
But the last paragraph from the quoted documentation means that the next time you reference the package in the same session, you won't get the error and it will work as normal (until you recompile again).
seems like opening a new session is the key.
see this answer.
and here is an awesome explanation about this error
Based on previous answers. I resolved my issue by removing global variable at package level to procedure, since there was no impact in my case.
Original script was
create or replace PACKAGE BODY APPLICATION_VALIDATION AS
V_ERROR_NAME varchar2(200) := '';
PROCEDURE APP_ERROR_X47_VALIDATION ( PROCESS_ID IN VARCHAR2 ) AS BEGIN
------ rules for validation... END APP_ERROR_X47_VALIDATION ;
/* Some more code
*/
END APPLICATION_VALIDATION; /
Rewritten the same without global variable V_ERROR_NAME and moved to procedure under package level as
Modified Code
create or replace PACKAGE BODY APPLICATION_VALIDATION AS
PROCEDURE APP_ERROR_X47_VALIDATION ( PROCESS_ID IN VARCHAR2 ) AS
**V_ERROR_NAME varchar2(200) := '';**
BEGIN
------ rules for validation... END APP_ERROR_X47_VALIDATION ;
/* Some more code
*/
END APPLICATION_VALIDATION; /
I recompiled the package specification, even though the change was only in the package body. This resolved my issue

sql call function multiple times in a loop. postgres 8.3

I have a function that has 'key' variable as an argument.
I want to call this function for a range of key values.
I tried this, didn't work...
BEGIN
for i IN 773..775 LOOP
test_count(i);
end LOOP;
end;
SQL error:
ERROR: syntax error at or near "for"
LINE 2: for i IN 773..775 LOOP
#Mihai already explained that you cannot run procedural elements outside of a function or anonymous code block with DO.
Your syntax would still fail, because you cannot call a function anywhere without taking care of the returned value(s). If you want to discard possible return values(s) use PERFORM in a function like this (works with PostgreSQL 8.3):
CREATE OR REPLACE FUNCTION foo()
RETURNS void LANGUAGE plpgsql AS
$BODY$
BEGIN
FOR i IN 773 .. 775
LOOP
PERFORM test_count(i);
END LOOP;
END;
$BODY$;
PostgreSQL 8.3 cannot run anonymous procedures/ functions or create variables outside of a procedure/ function.
The DO construct was added as support for anonymous procedures as of version 9.0.
You should run your code inside a function. Because the error message you are receiving states that FOR is an unexpected keyword in a global context.
Use a record type for your keys
DO $BODY$
DECLARE tmp_row record;
BEGIN
FOR tmp_row IN (SELECT key from my_keys_table)
LOOP
PERFORM test_function(tmp_row.key);
END LOOP;
END;
$BODY$;

Declaring global exception in PL/SQL package

I have some procedures/functions in the package which catch the -2291 exception when a FOREIGN KEY constraint is violated.
I removed the exception from the different procedures and declared it in the package body like so:
e_ouder_niet_gevonden EXCEPTION;
PRAGMA EXCEPTION_INIT(e_ouder_niet_gevonden,-2291);
Now when I use one of the procedures in the package there is no problem. What I would like to achieve, however, is that ANY procedure/function can use that exception. How would I go about doing this?
SOLUTION:
Instead of putting the exception in the package body, you need to put in the package specification. If you then want to use the exception in a procedure outside of the package you can put this in the EXCEPTION block:
WHEN packagename.exception_name THEN
This works without problems.