schemas and triggers in postgres - postgresql

here is my script:
CREATE SCHEMA IF NOT EXISTS {accountId};
CREATE TABLE IF NOT EXISTS {accountId}.{tableCommandsName}
(
id int GENERATED ALWAYS AS IDENTITY,
ts timestamp WITHOUT TIME ZONE NOT NULL,
command varchar NOT NULL,
ts_executed timestamp WITHOUT TIME ZONE,
output varchar
);
CREATE INDEX IF NOT EXISTS idx_commands ON {accountId}.{tableCommandsName} (ts_executed) WHERE ts_executed IS NULL;
CREATE OR REPLACE VIEW {accountId}.pending_commands AS
SELECT id, ts, command from {accountId}.{tableCommandsName} WHERE ts_executed IS NULL ORDER BY ts ASC;
CREATE OR REPLACE FUNCTION {accountId}.on_commands_change ()
RETURNS trigger
LANGUAGE 'plpgsql'
AS $BODY$
DECLARE
notification JSON;
BEGIN
notification = json_build_object(
'table',TG_TABLE_NAME,
'data', row_to_json(NEW));
PERFORM pg_notify('datachange', notification::TEXT);
RETURN NEW;
END
$BODY$;
CREATE OR REPLACE TRIGGER {triggerName}
AFTER INSERT ON {accountId}.{tableCommandsName}
FOR EACH ROW EXECUTE PROCEDURE {accountId}.on_commands_change();
my focus is the line:
CREATE OR REPLACE TRIGGER {triggerName}
where
CREATE OR REPLACE TRIGGER {accountId}.{triggerName}
will not work, but
CREATE OR REPLACE TRIGGER "{accountId}.{triggerName}"
will work, and I don't understand why.
everything else has the schema name in front, but the trigger doesn't want it.
and if I don't put it, it's still created in the right schema. Is it because it's during the same execution as the create schema instruction?

Quote from the manual
The name cannot be schema-qualified — the trigger inherits the schema of its table

Related

Create notify trigger for specific JSON value in inserted row

I have a table with event entity
create table event_entity
(
id varchar(36) not null
constraint constraint_4
primary key,
details_json varchar(2550),
event_time bigint,
type varchar(255),
user_id varchar(255)
);
details_json has such data:
{
"custom_required_action":"VERIFY_EMAIL",
}
I need to create a trigger and notify on inserted row event_entity table with condition:
WHERE type = 'CUSTOM_REQUIRED_ACTION' AND details_json:custom_required_action = 'VERIFY_EMAIL'
I've made it with
CREATE OR REPLACE FUNCTION notify_verifyEmail()
RETURNS trigger AS $$
DECLARE
BEGIN
PERFORM pg_notify(
'verifyEmail',
row_to_json(NEW)::text);
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER notify_verifyEmail
AFTER INSERT ON event_entity
FOR EACH ROW
WHEN (new.type = 'CUSTOM_REQUIRED_ACTION')
EXECUTE PROCEDURE notify_verifyEmail();
But how to add second condition with details_json field?
First create the notification trigger function. In the example below it will notify on the_notification_channel and have the new row values JSON formatted as notification payload. The names of the trigger, the trigger function and the notification channel are such for illustration only.
create or replace function the_notification_trigger_function()
returns trigger language plpgsql as
$$
begin
perform pg_notify('the_notification_channel', to_json(new)::text);
return null;
end;
$$;
and then create the trigger with a condition
create trigger the_notification_trigger
after insert on event_entity for each row
when new.type = 'CUSTOM_REQUIRED_ACTION'
and new.details_json::json ->> 'custom_required_action' = 'VERIFY_EMAIL'
execute function the_notification_trigger_function();
Unrelated but it would be much better if your details_json field was of type jsonb instead of text and event_time was of type timestamp instead of bigint. What is constraint_4?
It might be a good idea to move the new.details_json::json ->> 'custom_required_action' = 'VERIFY_EMAIL' sub-condition into the trigger function so that the trigger fill fire on every 'CUSTOM_REQUIRED_ACTION' and the function would decide on how to react.
can u try with below once and let me if it works
CREATE TRIGGER notify_verifyEmail
AFTER INSERT ON event_entity
FOR EACH ROW
WHEN (new.type = 'CUSTOM_REQUIRED_ACTION' AND (new.details_json->>'custom_required_action') = 'VERIFY_EMAIL')
EXECUTE PROCEDURE notify_verifyEmail();

Postgresql update timestamp trigger in inherited table

I used this example and it worked well when all my tables were in public schema.
But trigger hasn't been working since I separated tables into different schemas and applied inheriting.
Here is example of my structure:
CREATE SCHEMA common;
CREATE SCHEMA video;
CREATE TABLE common.file (
file_id SERIAL PRIMARY KEY,
url VARCHAR(255) NOT NULL,
mime_type VARCHAR(31) DEFAULT '' NOT NULL,
size INTEGER NOT NULL,
modified TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL
);
CREATE TABLE video.file (
width INTEGER NOT NULL,
height INTEGER NOT NULL,
local_path VARCHAR(255) DEFAULT '' NOT NULL
)
INHERITS (common.file);
CREATE FUNCTION common.update_modified()
RETURNS trigger
LANGUAGE plpgsql
AS $$
BEGIN
NEW.modified = now();
RETURN NEW;
END;
CREATE TRIGGER update_modified
BEFORE UPDATE ON common.file
FOR EACH ROW EXECUTE PROCEDURE common.update_modified();
When I do UPDATE common.file ... or UPDATE video.file ... field common.file.modified doesn't change itself. It seems trigger doesn't run, but I don't understand why.
What should I do to repair the behavior?
In described issue trigger is set only on common.file, so UPDATE common.file ... doesn't work if row inserted in video.file
Documentation says: INSERT always inserts into exactly the table specified
So trigger should be applied to both common.file and video.file.
-- Creating schemas and tables the same
-- Let function be in public scheme
CREATE FUNCTION update_modified()
RETURNS trigger
LANGUAGE plpgsql
AS $$
BEGIN
NEW.modified = now();
RETURN NEW;
END;
CREATE TRIGGER update_modified
BEFORE UPDATE ON common.file
FOR EACH ROW EXECUTE PROCEDURE update_modified();
CREATE TRIGGER update_modified
BEFORE UPDATE ON video.file
FOR EACH ROW EXECUTE PROCEDURE update_modified();
In that case when we update rows inserted either in common.file or in video.file corresponding trigger will call.

Postgresql function return trigger

I am having a problem with a trigger. I created a trigger and a function
for when performing an INSERT update a field in the same table.
Is returning:
Error: function "loss_func" in FROM has return type trigger that is
not supported LINE 1: SELECT * FROM table.loss_func ()
Function
CREATE OR REPLACE FUNCTION loss_func()
RETURNS trigger AS $loss_func$
BEGIN
NEW.dt_creation := to_char(now(), 'YYYY-MM-DD');
RETURN NULL;
END;
$loss_func$ LANGUAGE plpgsql VOLATILE
COST 100;
ALTER FUNCTION loss_func()
OWNER TO postgres;
Trigger
CREATE TRIGGER tgr_loss
AFTER INSERT ON loss
FOR EACH ROW
EXECUTE PROCEDURE loss_func();
What am I doing wrong?
A working version of your code.
- The trigger now fires BEFORE insert and updates the value of dt_creation and returns the NEW version of the record :
drop table loss;
create table loss (
id int ,
dt_created varchar);
CREATE OR REPLACE FUNCTION loss_func()
RETURNS trigger AS $loss_func$
BEGIN
NEW.dt_created := to_char(now(), 'YYYY-MM-DD');
RETURN NEW;
END;
$loss_func$ LANGUAGE plpgsql VOLATILE
COST 100;
ALTER FUNCTION loss_func()
OWNER TO postgres;
CREATE TRIGGER tgr_loss
BEFORE INSERT ON loss
FOR EACH ROW
EXECUTE PROCEDURE loss_func();
insert into loss(id) values(1);
Another solution that i can propose to avoid the usage of a trigger is to use a default value for dt_creation when you create the table (and use timestamp instead of storing the date as varchar) :
...
dt_creation timestamp default now(),
...
or you can alter your table to set the default value to now() :
alter table loss
alter column dt_creation set default now();

Dynamic Change Column Values with NEW in Trigger Function

Is it possible to dynamically change column values in NEW with a Trigger Function?
Triggers:
CREATE TRIGGER lowercase_username_on_insert_trigger BEFORE INSERT OR UPDATE ON users
FOR EACH ROW EXECUTE PROCEDURE lowercase_on_insert('username');
CREATE TRIGGER lowercase_email_on_insert_trigger BEFORE INSERT OR UPDATE ON users
FOR EACH ROW EXECUTE PROCEDURE lowercase_on_insert('email');
Trigger Function:
CREATE OR REPLACE FUNCTION lowercase_on_insert()
RETURNS trigger
AS $lowercase_on_insert$
BEGIN
-- e.g., NEW.username = LOWER(NEW.username)
-- -OR- NEW.email = LOWER(NEW.email)
EXECUTE FORMAT('NEW.%s = LOWER(NEW.%s);', TG_ARGV[0], TG_ARGV[0]);
RETURN NEW;
END
$lowercase_on_insert$ LANGUAGE plpgsql;
I get an ERROR: syntax error at or new "NEW" whenever I run an INSERT.
This is my table:
CREATE TABLE users (
id BIGSERIAL PRIMARY KEY,
username VARCHAR(55) NOT NULL UNIQUE,
email VARCHAR(120) NOT NULL UNIQUE
);
Change your Trigger function as below
CREATE OR REPLACE FUNCTION lowercase() RETURNS trigger AS $$
BEGIN
NEW.username = LOWER(NEW.username);
NEW.email = LOWER(NEW.email);
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
and assign
CREATE TRIGGER tgrr BEFORE INSERT OR UPDATE ON users
FOR EACH ROW EXECUTE PROCEDURE lowercase();
OR
You can directly apply lower() function in Insert/Update like this
insert into users(username,email) values (lower('Jerry'),lower('JeRRY#mail.COM'));
update users set username=lower('JERRY'),email=lower('JERRY#MAIL.COM') where id =1

Infinite recursion detected when updating from function (psycopg2, python 2.7, postgres 9.3)

I have a simple table:
CREATE TABLE IF NOT EXISTS someTable (
row_id smallserial PRIMARY KEY,
name text NOT NULL,
creation_date timestamp with time zone DEFAULT current_timestamp,
last_updated_date timestamp with time zone DEFAULT current_timestamp,
created_by text DEFAULT "current_user"(),
last_updated_by text DEFAULT "current_user"()
);
with the following rule:
CREATE OR REPLACE RULE log_update_some_table AS
ON UPDATE TO someTable
DO ALSO
UPDATE someTable
SET last_updated_date = current_timestamp,
last_updated_by = current_user;
and a very simple function in plpgsql:
CREATE OR REPLACE FUNCTION test_update ()
RETURNS void AS $$
BEGIN
UPDATE someTable
SET name = 'test'
WHERE row_id = 1;
END;
$$ LANGUAGE plpgsql;
One would think the function would run just fine, but I get the following error:
psycopg2.ProgrammingError: infinite recursion detected in rules for relation "sometable"
CONTEXT: SQL statement "UPDATE someTable
SET name = 'test'
WHERE row_id = 1"
PL/pgSQL function test_update() line 3 at SQL statement
Why isn't this working and how do I fix it? Thanks!
So your update rule on someTable triggers an update on someTable which executes the rule which updates someTable which executes the rule...
I'd use a simple trigger instead, something like this:
create or replace function log_update_some_table() returns trigger as $$
begin
NEW.last_updated_date = current_timestamp;
NEW.last_updated_by = current_user;
return NEW;
end;
$$ language plpgsql;
create trigger log_update_some_table_trigger
before update on someTable
for each row execute procedure log_update_some_table();
should do the trick. That will modify the row before the update happens rather than adding another update (which triggers the recursion problem) to the queue.