Postgres function with nested IF & ELSE statement error - postgresql

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

Related

Function's final statement must be SELECT or INSERT UPDATE DELETE RETURNING

CREATE FUNCTION retrieve_add_friends(user_id text[])
RETURNS SETOF user_rows AS
$BODY$
BEGIN
FOR user_rows IN EXECUTE SELECT * FROM user_details where user_id= $1
LOOP
FOR user_friends IN EXECUTE SELECT * FROM user_add_friends where user_id= $1
LOOP
IF user_rows.user_id!=user_friends.user_friend_id THEN
RETURN NEXT user_rows;
END IF;
END LOOP;
RETURN;
END LOOP;
RETURN;
END
$BODY$
language plpgsql VOLATILE;
When I execute this I get following error:
ERROR: return type mismatch in function declared to return user_details
DETAIL: Function's final statement must be SELECT or INSERT/UPDATE/DELETE RETURNING.
CONTEXT: SQL function "retrieve_add_friends"
Can anyone help me out with this?
The function displayed is a PL/pgSQL function.
You are calling a different function, an SQL function, obviously (for which the error msg would make sense):
SQL function "retrieve_add_friends"
Same function name, but different arguments (and possibly in a different database schema). Are you aware of function overloading and its implications?
Related:
ERROR: function addgeometrycolumn is not unique
For a quick diagnosis:
SELECT oid::regprocedure AS function_signature, *
FROM pg_proc
WHERE proname = 'retrieve_add_friends';
All that aside, the function displayed has multiple errors and can be replaced with plain SELECT.

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.

how to create event trigger for create table or select into

i want create event trigger for create table or select into,
eg:
when create table xxxx must table name bigen with 'temp'
my code
CREATE OR REPLACE FUNCTION create_table_func()
RETURNS event_trigger
AS
$$
DECLARE
V_TABLE name := TG_TABLE_NAME;
BEGIN
if V_TABLE !~ '^temp'
then
RAISE EXCEPTION 'must bigen with temp';
end if;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;
CREATE EVENT TRIGGER create_table_1 ON ddl_command_start
WHEN TAG IN ('SELECT INTO')
EXECUTE PROCEDURE create_table_func();
but when execute
select * into test11 from test_bak
[Err] ERROR: column "tg_table_name" does not exist
this is my code ,it's meet my needs
code:
CREATE OR REPLACE FUNCTION trg_create_table_func()
RETURNS event_trigger
LANGUAGE plpgsql
AS $$
DECLARE
obj record;
BEGIN
FOR obj IN SELECT * FROM pg_event_trigger_ddl_commands() WHERE command_tag in ('SELECT INTO','CREATE TABLE','CREATE TABLE AS')
LOOP
if obj.object_identity !~ 'public.temp_'
THEN
raise EXCEPTION 'The table name must begin with temp_';
end if;
END LOOP;
END;
$$;
CREATE EVENT TRIGGER trg_create_table ON ddl_command_end
WHEN TAG IN ('SELECT INTO','CREATE TABLE','CREATE TABLE AS')
EXECUTE PROCEDURE trg_create_table_func();
out recods
[Err] ERROR: The table name must begin with temp_
CONTEXT: PL/pgSQL function trg_create_table_func() line 10 at RAISE
it's cool ~
The special variable TG_TABLE_NAME is only supported in normal triggers, not in event triggers (there is not always an associated table!).
The documentation has a list of functions that can return context information in an event trigger.
You could use pg_event_trigger_ddl_commands() to get the information you need, but that only works in ddl_command_end event triggers. That should work for you; I don't see a reason why the trigger should not run at the end of the statement.

Transaction results in syntax error

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;

pgsql sql functions sequential execution

If I have these two Postgres function definitions saved in two seperate .sql files:
CREATE OR REPLACE FUNCTION column_exists(tablename text, colname text) RETURNS boolean AS
$BODY$
DECLARE
q text;
field_name text;
onerow record;
BEGIN
q = 'SELECT column_name FROM information_schema.columns WHERE table_name='''||tablename||''' AND table_schema =''public''';
FOR onerow IN EXECUTE q
LOOP
field_name := onerow.column_name;
IF ((field_name = colname)) then
RETURN true;
END IF;
END LOOP;
RETURN false;
END;
$BODY$
LANGUAGE plpgsql
CREATE OR REPLACE FUNCTION correct_col_names() RETURNS VOID AS
$BODY$
DECLARE
q boolean;
BEGIN
-- rename name column to Name
select column_exists('National_Parks', 'name') as q;
IF q = TRUE THEN
alter table "National_Parks"
rename column name to "Name";
END IF;
-- remance descriptio column to description
select column_exists('National_Parks', 'descriptio') as q;
IF q = TRUE THEN
alter table "Natioanl_Parks"
rename column descriptio to "Description";
END IF;
END
$BODY$
LANGUAGE plpgsql
What is the syntax I need to use to call the sequentially, say in another script? I tried
select correct_col_names()
and this returns the following error:
ERROR: query has no destination for result data
HINT: If you want to discard the results of a SELECT, use PERFORM instead.
CONTEXT: PL/pgSQL function "correct_col_names" line 7 at SQL statement
********** Error **********
ERROR: query has no destination for result data
SQL state: 42601
Hint: If you want to discard the results of a SELECT, use PERFORM instead.
Context: PL/pgSQL function "correct_col_names" line 7 at SQL statement
TIA.
The problem is that you have SELECT statements that aren't doing anything with the data. Your
select column_exists('National_Parks', 'name') as q;
should be
select column_exists('National_Parks', 'name') INTO q;
The as simply aliases the result as "q" for that query, it doesn't actually put it into the q variable.
Your syntax for calling the functions (select correct_col_names()) is correct for SQL. Once you fix the two errors in that function, it should work.
However, if you were to try select correct_col_names() inside another PL/PGSQL function, you would get the same error, because the select statement isn't actually doing anything with the results. perform correct_col_names() would run without error, because PERFORM is PL/PGSQL syntax for calling something when you don't want to save the result.