Transaction results in syntax error - postgresql

This function is created ok without the transaction block. With it as in the following I get the following error...
ERROR: syntax error at end of input LINE 27: $$
^
********** Error **********
ERROR: syntax error at end of input
If I take out the 'COMMIT' and corresponding 'BEGIN' then it runs ok.
Why is this?
CREATE OR REPLACE FUNCTION ContentDelete (
creatorUserId BIGINT,
toDeleteContentId BIGINT)
RETURNS VOID
AS $$
BEGIN
BEGIN
IF (SELECT EXISTS(SELECT
*
FROM
tbl_content
WHERE
ContentId = toDeleteContentId
AND
UserId = creatorUserId
LIMIT 1) AS "exists")
THEN
DELETE FROM tbl_content WHERE ParentContentId = toDeleteContentId;
DELETE FROM tbl_content WHERE ContentId = toDeleteContentId;
END IF;
COMMIT;
END
$$
LANGUAGE plpgsql;

Functions in PostgreSQL are running under transaction everywhere. It can be implicit transaction (started by Postgres) or explicit transaction (started by user). And you cannot to commit or rollback this transaction, because it is started outside function.
If your function doesn't raise a exception, then result will be committed (if somebody else doesn't raise a exception). If you would to enforce rollback - raise a exception. The only one solution is rollback of outer transaction. Your function can be strongly reduced in PostgreSQL (although your logic looks messy):
CREATE OR REPLACE FUNCTION ContentDelete (creatorUserId BIGINT,
toDeleteContentId BIGINT)
RETURNS VOID AS $$
BEGIN
DELETE FROM tbl_content
WHERE ContentId = toDeleteContentId
AND UserId = creatorUserId;
IF FOUND THEN
DELETE FROM tbl_content WHERE ParentContentId = toDeleteContentId;
END IF;
RETURN;
END;
$$ LANGUAGE plpgsql;

Related

Postgres function with nested IF & ELSE statement error

I have this function where it looks up all the users and if any one does not exists do not create the function. What is wrong?
RETURNS event_trigger AS $$
DECLARE
audit_query TEXT;
r RECORD;
BEGIN
IF tg_tag IN ('CREATE TABLE', 'CREATE TABLE AS')
THEN
IF EXISTS (SELECT FROM pg_catalog.pg_roles WHERE rolname = 'test' or rolname = 'testa')
THEN
FOR r IN
SELECT * FROM pg_event_trigger_ddl_commands() AS ddl WHERE ddl.schema_name IN ('testb','testc','testd')
LOOP
INSERT INTO user_monitor.ddl_history (ddl_date, ddl_tag, object_name) VALUES (statement_timestamp(), tg_tag, r.object_identity);
END LOOP;
else
RAISE EXCEPTION 'Not all users found';
END IF;
END;
$$ LANGUAGE plpgsql
SQL Error [42601]: ERROR: syntax error at or near ";"
SQL Error [42601]: ERROR: missing "THEN" at end of SQL expression
Position: 286
An excerpt from the manual:
Event triggers are created using the command CREATE EVENT TRIGGER. In order to create an event trigger, you must first create a function with the special return type event_trigger.
This function need not (and may not) return a value; the return type serves merely as a signal that the function is to be invoked as an event trigger.
Emphasis on the second sentence.
You should probably replace your RETURN with RAISE EXCEPTION instead.
https://www.postgresql.org/docs/current/event-trigger-table-rewrite-example.html

Why am I getting a syntax error when using an IF statement in my Postgres function?

I am creating a function that allow me to conditionally update specific columns in a table. However, I get an error indicating that there is a syntax error at or near "IF" when I try to run the following code. I'm a bit new to Postgres so it's quite possible. I can't understand some concept/syntax thing in Postgres. Can someone help me by pointing out the mistake I must be making?
CREATE OR REPLACE FUNCTION profiles.do_something(
p_id UUID,
p_condition1 BOOLEAN,
p_condition2 BOOLEAN,
p_condition3 BOOLEAN
)
RETURNS void AS $$
BEGIN
IF p_condition1 IS TRUE THEN
UPDATE tablename SET column1 = null WHERE member_id = p_id;
END IF;
IF p_condition2 IS TRUE THEN
UPDATE tablename SET column2 = null WHERE member_id = p_id;
END IF;
IF p_condition3 IS TRUE THEN
UPDATE tablename SET column3 = null WHERE member_id = p_id;
END IF;
END;
$$ LANGUAGE 'sql';
tl;dr $$ LANGUAGE 'plpgsql'
$$ LANGUAGE 'sql';
^^^^^
You're tell it to parse the body of the function as sql. In SQL, begin is a statement which starts a transaction.
create or replace function test1()
returns void
language sql
as $$
-- In SQL, begin starts a transaction.
-- note the ; to end the statement.
begin;
-- Do some valid SQL.
select 1;
-- In SQL, end ends the transaction.
end;
$$;
In SQL you wrote begin if ... which is a syntax error.
The language you're using is plpgsql. In plpgsql, begin is a keyword which starts a block.
create or replace function test1()
returns void
language plpgsql
as $$
-- In PL/pgSQL, begin starts a block
-- note the lack of ;
begin
-- Do some valid SQL.
select 1;
-- In PL/pgSQL, end ends the block
end;
$$;

PostgreSQL Error Codes, set a custom error code

I'm translating a Oracle trigger to Postgres; I have this so far translated in postgres
DROP TRIGGER IF EXISTS trg_test_biud ON mytable CASCADE;
CREATE OR REPLACE FUNCTION trigger_fct_trg_test_biud() RETURNS trigger AS $BODY$
DECLARE
id_ double precision := NULL;
hour_ varchar(10) := NULL;
BEGIN
/* INSERT */
IF TG_OP = 'INSERT' THEN
BEGIN
select nextval('myschema.id_audit_mytable_seq') into id_;
SELECT TO_CHAR(current_timestamp, 'HH24:MI:SS') INTO hour_;
INSERT INTO myschema.audit_mytable(id, id_mytable, user_name, event, myhour, hour, geometry)
VALUES (id_, NEW.code, NEW.user_name, 'INSERT', LOCALTIMESTAMP, hour_, NEW.GEOMETRY);
RETURN NEW;
EXCEPTION
WHEN OTHERS THEN
RAISE EXCEPTION '%', 'Error when insert into audit_mytable: ' || sqlerrm USING ERRCODE = '-20000';
END;
END IF;
END
$BODY$
LANGUAGE 'plpgsql';
CREATE TRIGGER trg_test_biud
BEFORE INSERT OR UPDATE OR DELETE ON myschema.mytable FOR EACH ROW
EXECUTE PROCEDURE trigger_fct_trg_test_biud();
When the exception is raised, I get this error:
ERROR: unrecognized exception condition «-20000»
SQL state: 42704
Does this has to do with the fact that in Oracle the 'custom error code' is a negative number? postgres does not recognize this? I checked this page, but it says nothing about negative numbers: https://www.postgresql.org/docs/current/static/errcodes-appendix.html
The Oracle number -20000 is not an SQLSTATE, but a proprietary error code.
You have to specify one of the 5-character SQLSTATEs defined in appendix A of the documentation.

Trigger run after hibernate insert returning error [duplicate]

Is it possible to cancel previous operations in a user defined function?
For example:
CREATE OR REPLACE FUNCTION transact_test () RETURNS BOOLEAN
AS $$
BEGIN
UPDATE table1 SET ...
UPDATE table2 SET ...
IF some_condition THEN
--Here is possible to cancel all above operations?
RETURN FALSE;
END IF;
RETURN TRUE;
END;
$$
LANGUAGE plpgsql;
Both answers so far are incorrect.
If you try to start a transaction or use a SAVEPOINT inside a plpgsql function you get an error message like this:
ERROR: cannot begin/end transactions in PL/pgSQL
HINT: Use a BEGIN block with an EXCEPTION clause instead.
CONTEXT: PL/pgSQL function "f_savepoint" line 6 at SQL statement
If you try a SAVEPOINT inside a plain SQL function:
ERROR: SAVEPOINT is not allowed in a SQL function
CONTEXT: SQL function "f_savepoint2" during startup
As the error message instructs, use a BEGIN block inside a plpgsql function instead. Your demo could look like this:
CREATE OR REPLACE FUNCTION transact_test(boolean)
RETURNS boolean AS
$func$
BEGIN -- start a nested BEGIN block
UPDATE t SET i = i+1 WHERE i = 1;
UPDATE t SET i = i+1 WHERE i = 3;
IF $1 THEN
RAISE EXCEPTION 'foo'; -- cancels all of the above
END IF;
RETURN TRUE;
EXCEPTION WHEN OTHERS THEN
RETURN FALSE;
-- do nothing
END
$func$ LANGUAGE plpgsql;
-> SQLfiddle demonstrating it all.

Commit, savepoint, rollback to in PostgreSQL?

Can someone please explain to me why does COMMIT in this function returns EXCEPTION ?
DECLARE
XNar CURSOR (forDATE Varchar) IS
SELECT NARUCENO, ISPORUKA_ID FROM XDATA_NARUDZBE
WHERE TO_CHAR(XDATA_NARUDZBE.DATUM, 'DD.MM.YYYY') = forDATE;
LastDate DATE;
OutResult INTEGER;
curNAR NUMERIC;
curISP VARCHAR;
RXNar RECORD;
BEGIN
OutResult := 1;
SELECT MAX(DATUM) INTO LastDate FROM XDATA_NARUDZBE;
FOR RXNar IN XNar(TO_CHAR(LastDate, 'DD.MM.YYYY')) LOOP
IF (RXNar.NARUCENO <> 0) AND (RXNar.ISPORUKA_ID = 'R01') THEN
UPDATE NARUDZBE SET ISPORUCENO = RXNar.NARUCENO
WHERE NARUDZBE.PP_ID = RXNar.PP_ID
AND NARUDZBE.ART_ID = RXNar.ART_ID
AND NARUDZBE.ISPORUKA_ID = 'R01';
END IF;
END LOOP;
COMMIT; <--- ????
RETURN OutResult;
EXCEPTION
WHEN OTHERS THEN
OUTRESULT := 0;
RAISE;
RETURN OutResult;
END;
and why I can not use ROLLBACK TO SavePoint when EXCEPTION block exists in function?
You can't use COMMIT in a stored procedure, the entire procedure is a transaction of it's own.
You can't commit in a plpgsql stored function/procedure using plpgsql as Frank Heikens answered. You can however work around this issue by using dblink(http://www.postgresql.org/docs/9.0/interactive/contrib-dblink-connect.html) or another store procedure language such as plperl(untrusted). Check out this link where this talked about.
http://postgresql.1045698.n5.nabble.com/Re-GENERAL-Transactions-within-a-function-body-td1992810.html
The high level is you open a new connection using one of these methods and issue a separate transaction on that connection. Works for most cases not ideal because you are opening a new connection, but may work fine for most use cases.
Begin with PostgreSQL11 there is an procedure module.
Demo based on Manual.
CREATE OR REPLACE PROCEDURE insert_data_drop(_a integer, _b integer)
LANGUAGE plpgsql
AS $$
begin
INSERT INTO tbl(a) VALUES (_a);
INSERT INTO tbl(b) VALUES (_b);
Rollback;
INSERT INTO tbl(a) VALUES (_a);
end
$$;
Now CALL insert_data_drop(1, 2); will insert 1 to column a, 2 will not be saved.
However, SAVEPOINT seems not working. It will show error like:
ERROR: unsupported transaction command in PL/pgSQL
CONTEXT: PL/pgSQL function insert_data_drop(integer,integer) line 5 at SQL stateme