How to test an if statement in PostgreSQL? - sql

Question: I want to test an if statement in PostgreSQL:
IF (SELECT COUNT(*) FROM pg_language WHERE lanname = 'plpgsql') > 0 THEN
PRINT 'Good'
ELSE
PRINT 'Bad'
END IF;
Now this throws an error at IF.
As far as I have read, this is because I need to use plpgsql to be able to use if, print, and variables.
So far, I probably also have to use SELECT instead of print as well.
How can I switch the language before executing this statement to plpgsql ?
I want to test it first, BEFORE I put it in a stored procedure.
To test code with variables etc.
Edit:
Solved by:
DO LANGUAGE plpgsql $$
BEGIN
IF (SELECT COUNT(*) FROM pg_language WHERE lanname = 'plpgsql') > 0 THEN
RAISE NOTICE 'GOOD';
ELSE
RAISE NOTICE 'BAD';
END IF;
END;
$$;

If you just want to test code snippets without going through all the hassle of building and dropping a function, then you can use DO:
=> do language plpgsql $$
begin
-- Yes, I have a table called pancakes in my playpen database.
if (select count(*) from pancakes) > 0 then
raise notice 'Got some';
else
raise notice 'Got none';
end if;
end;
$$;
You'll need 9.0+ to use DO.

Related

IF condition evaluation in AFTER TRIGGER function on PostgreSQL

I'm writing a plpgsql function in PostgreSQL (version 10) which is called by a TRIGGER after an UPDATE
CREATE TRIGGER trigger_1
AFTER UPDATE
ON entities
FOR EACH ROW
WHEN ( condition_on_new_and_old )
EXECUTE PROCEDURE function_1();
entities is a table which has a column data of type JSONB.
The code of function_1 is essentially (I modified the code to isolate the RAISE in the IF, originally the condition was the inverse):
CREATE OR REPLACE FUNCTION function_1() RETURNS TRIGGER AS
$$
BEGIN
IF (
TG_OP <> 'UPDATE'
OR TG_WHEN <> 'AFTER'
OR NOT (NEW.data ? 'XXX')
OR NOT (OLD.data ? 'XXX')
OR NOT (NEW.object_id = OLD.object_id)
OR NOT (NEW.workspace = OLD.workspace)) THEN
RAISE EXCEPTION 'XXX not found';
END IF;
-- SOME INSERTs
-- SOME DELETEs
RETURN NULL;
END
$$ LANGUAGE plpgsql;
As everyone can expect if the condition in the IF is true we raise the exception. The problem is the RAISE is always executed despite the value of the condition. In all my tests the values of OLD and NEW have always been the same.
Even more surprising was the fact that if I did something like
CREATE OR REPLACE FUNCTION function_1() RETURNS TRIGGER AS
$$
BEGIN
IF (
TG_OP <> 'UPDATE'
OR TG_WHEN <> 'AFTER'
OR NOT (NEW.data ? 'XXX')
OR NOT (OLD.data ? 'XXX')
OR NOT (NEW.object_id = OLD.object_id)
OR NOT (NEW.workspace = OLD.workspace)) THEN
RAISE EXCEPTION 'XXX not found';
END IF;
RAISE EXCEPTION 'TEST'
-- SOME INSERTs
-- SOME DELETEs
RETURN NULL;
END
$$ LANGUAGE plpgsql;
I had the exception 'TEST' raised but if I wrote:
CREATE OR REPLACE FUNCTION function_1() RETURNS TRIGGER AS
$$
BEGIN
IF (
TG_OP <> 'UPDATE'
OR TG_WHEN <> 'AFTER'
OR NOT (NEW.data ? 'XXX')
OR NOT (OLD.data ? 'XXX')
OR NOT (NEW.object_id = OLD.object_id)
OR NOT (NEW.workspace = OLD.workspace)) THEN
RAISE EXCEPTION 'XXX not found';
END IF;
-- RAISE EXCEPTION 'TEST' (I commented the RAISE)
-- SOME INSERTs
-- SOME DELETEs
RETURN NULL;
END
$$ LANGUAGE plpgsql;
I had the exception 'XXX not found' thrown.
To notice the trigger used to be on BEFORE UPDATE and it did work as expected, the problem arrived when we set it to AFTER.
I'm quite sure I'm missing something about how AFTER triggers behave. Do you have any ideas?
Thank you all in advance
Thanks for the comments on the question. I'm sorry but I didn't notice in the logs that there was actually a problem in the WHEN condition of the TRIGGER.
After fixing it it's working.

Conditionally drop an insert in a before insert trigger without returning error

I have the following function in a before insert trigger:
CREATE OR REPLACE FUNCTION schema.table_somefun()
RETURNS trigger AS
LANGUAGE 'plpgsql';
$BODY$
BEGIN
IF NEW.col2 NOT NULL THEN
NEW.col1 := CASE NEW.col1
WHEN '121432' THEN '321123'
ELSE <command> END CASE; --there should be a command aborting insertion without error or exception
END IF;
RETURN NEW;
END;
$BODY$
The ELSE statement should abort insertion. Is there a command which drops the query without telling it to the client and leaving the table untouched?
Just use
RETURN NULL;
instead of
RETURN NEW;
to cancel the INSERT for the row and do nothing instead.
But you cannot execute a PL/pgSQL statement inside an SQL CASE expression. (Don't confuse SQL CASE with the similar control structure CASE of PL/pgSQL!)
Could look like this:
CREATE OR REPLACE FUNCTION schema.table_somefun()
RETURNS trigger AS
$func$
BEGIN
IF NEW.col2 NOT NULL THEN
IF NEW.col1 = '121432' THEN -- could also be plpgsql CASE ...
NEW.col1 := '321123';
ELSE
RETURN NULL;
END IF;
END IF;
RETURN NEW;
END
$func$ LANGUAGE plpgsql;

Creating Trigger function is psql "syntax error at or near "CREATE""

Here is my postgreSQL code
CREATE FUNCTION connectedExhibitionFunction()
RETURNS trigger AS
$$
BEGIN
IF (SELECT COUNT("exName") FROM Exhibitions WHERE NEW."exName" = "exName") > 0 THEN
IF (SELECT Count(doorLoc1) FROM Doors, ExhibitionLocations WHERE (dorLoc1=NEW.elLocation AND dorLoc2=elLocations) OR (dorLoc2=NEW.elLocation AND dorLoc1=elLocations) > 0 THEN
RAISE EXCEPTION 'You can't have the exhibition there, the same exhibition is in an unconnected room';
END IF;
END IF;
END;
$$
LANGUAGE plpgsql;
CREATE TRIGGER connectedExhibitionTrigger
BEFORE INSERT
ON ExhibitionsLocations
EXECUTE PROCEDURE connectedExhibitionFunction();
And this is the error I'm getting
psql:file.txt:62: ERROR: syntax error at or near "CREATE"
LINE 8: CREATE FUNCTION connectedExhibitionFunction()
^
psql:file.txt:67: ERROR: current transaction is aborted, commands ignored until end of transaction block
I can't seem to figure out the error, can anybody spot anything here?
I guess you missed Select ")" and raise exception clause couldnt be "can't"
And you can use just END instead of END IF. As far as I know your problem is these.
Try this please.
IF (SELECT Count(doorLoc1) FROM Doors, ExhibitionLocations
WHERE (dorLoc1=NEW.elLocation AND dorLoc2=elLocations) OR
(dorLoc2=NEW.elLocation AND dorLoc1=elLocations)) > 0 THEN
RAISE EXCEPTION 'You cant have the exhibition there, the same exhibition is in an unconnected room';
END
You should not use the count() function to test for the presence of data, use the PERFORM command instead. You should also RETURN NEW or RETURN NULL from the trigger function or your insert will fail by definition. With some other improvements you will end up with this:
CREATE FUNCTION connectedExhibitionFunction() RETURNS trigger AS $$
BEGIN
PERFORM * FROM Exhibitions WHERE "exName" = NEW."exName";
IF FOUND THEN
PERFORM * FROM Doors, ExhibitionLocations
WHERE (dorLoc1 = NEW.elLocation AND dorLoc2 = elLocations)
OR (dorLoc2 = NEW.elLocation AND dorLoc1 = elLocations);
IF FOUND THEN
RAISE EXCEPTION 'You can''t have the exhibition there, the same exhibition is in an unconnected room';
RETURN NULL; -- Make the INSERT fail
END IF;
END IF;
RETURN NEW; -- Make the INSERT succeed
END;
$$ LANGUAGE plpgsql;

How to do a batch commit in plpgsql?

When I do the below function, some errors occurs:
**ddddl=# select sssss(1,10);
ERROR: cannot begin/end transactions in PL/pgSQL
HINT: Use a BEGIN block with an EXCEPTION clause instead.
CONTEXT: PL/pgSQL function sssss(integer,integer) line 8 at SQL statement**
Here is my sample code.
CREATE OR REPLACE FUNCTION sssss(
IN c_1 int,
IN f_i int
) returns void as
$$
DECLARE t_c INT;
BEGIN
t_c := f_i;
WHILE c_1 <= t_c
loop
IF MOD(c_1, 4) = 1 THEN
start transaction;
END IF;
-- My statements here.
IF MOD(c_1, 4) = 0 THEN
COMMIT;
END IF;
c_1 := c_1 + 1;
END loop;
COMMIT;
END;
$$ language plpgsql;
So could anyone give me a sample of how to finish my job as the above code?
I'm using PostgreSQL 9.2.
In general, I think there are three approaches you can take:
Do without. Just let the whole thing be committed at once, like PostgreSQL wants.
Write a wrapper in application code that sets up a transaction and calls your function. (This is simple to do in a shell script, for example.)
Use a hack. One hack that exists is, you can have your function create a database-link to your own database, and perform subtransactions via remote sessions. [More information.] (This is essentially equivalent to the "wrapper in application code" approach, except that the application code is still in PL/pgSQL.)
From Postgresql 12 onwards you can do batch commits if you use procedures instead of functions, invoked by CALL command. Therefore you're function will be rewritten like below and invoked with CALL command:
CREATE OR REPLACE PROCEDURE sssss(IN c_1 int,IN f_i int)
LANGUAGE plpgsql
AS $$
DECLARE
t_c INT;
BEGIN
t_c := f_i;
WHILE c_1 <= t_c
loop
-- My statements here.
IF MOD(c_1, 4) = 0 THEN
COMMIT;
END IF;
c_1 := c_1 + 1;
END loop;
COMMIT;
END;
$$;
CALL sssss(1,10);
More details about transaction management regarding Postgres are available here:
https://www.postgresql.org/docs/12/plpgsql-transactions.html

PostgreSQL IF-THEN-ELSE control structure

Why do I always get the following error from Postgres?
syntax error at or near "IF"
I read PostgreSQL: Documentation: 8.3: Control Structures. First I tried to execute a difficult query (with subquery), but then I tried to execute a simple one like this:
IF 2 <> 0 THEN select * from users; END IF;
The error is still the same. What am I doing wrong?
IF 2 <> 0 THEN select * from users; END IF;
You cannot use PL/pgSQL statements outside plpgsql functions. And if this fragment is from plpgsql function, then it is nonsense too. You cannot directly return result of query like T-SQL does.
CREATE OR REPLACE FUNCTION test(p int)
RETURNS SETOF users AS $$
BEGIN
IF p = 1 THEN
RETURN QUERY SELECT * FROM users;
END IF;
RETURN;
END;
$$ LANGUAGE plpgsql;
When you would get some result from function, you have to use RETURN statement - plpgsql knows only function, it doesn't support procedures - so unbounded SELECT has not sense.
You're not enclosing that PL/pgSQL control structure in an anonymous block or a PL/pgSQL function.
For the SQL version of this control structure see the docs for CASE.
You're not enclosing that PL/pgSQL. They need to be enclosed with anonymous code block. Example for your code:
DO $$ BEGIN
IF 2 <> 0 THEN select * from users; END IF;
END$$;