We have automated refresh mechanisms for our materialized views in PostgreSQL.
If data issues occur and the refresh fails, we would like to get notified about this.
Is there an option to monitor the refresh logs or have a direct callback in an error case?
You could use an event trigger to log the event in a table:
CREATE TABLE log (
id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
refresh_time timestamp with time zone DEFAULT current_timestamp NOT NULL,
view_name text NOT NULL
);
CREATE FUNCTION log_refresh() RETURNS event_trigger
LANGUAGE plpgsql AS
$$BEGIN
INSERT INTO log (view_name)
SELECT objid::regclass::text
FROM pg_event_trigger_ddl_commands()
WHERE classid = 'pg_class'::regclass
AND object_type = 'materialized view';
END;$$;
CREATE EVENT TRIGGER log_refresh ON ddl_command_end
WHEN TAG IN ('REFRESH MATERIALIZED VIEW')
EXECUTE FUNCTION log_refresh();
Instead of logging the event, you could do other things, like NOTIFY a listener.
Related
I am using Microsoft Access as a front-end to my PostgreSQL database. My workflow is pretty simple:
Create a linked table using the ODBC driver
Build a form using form wizard for data entry
Inserting data works really well if i submit the data directly on the table or using the form. However, i can update the data that was submitted directly to the table, but i cannot update the data submitted through the form as i get a write.conflict error.
I checked many previous answers and one of the issues was to do with the timestamp precision. This helped with updating the data submitted directly on the table as it didn't work before.
Now i just need to resolve updating data that was submitted using the form. I tried using Me.Dirty as follows:
Private Sub Form_Dirty(Cancel As Integer)
Me.Dirty = False
End Sub
That didn't work for me unfortunately. It really does look like something to do with the form as entering data using the table works perfectly. Is it how the form saves the data? How can i get it off editing mode? I really don't know and i tried various things.
I would really appreciate a hand on this as i have been on it for days and i can't resolve it.
Thank you.
Here is a simplified version of the code i used to create my table. I just have more VARCHAR and numeric columns in the table i am using.
I also created a logs table that would update if any changes are made to the main table. This logs table is populate via a trigger as shown in the code.
CREATE TABLE table_1 (
id INT PRIMARY KEY DEFAULT to_char(now(), 'YYMMDDHH24MI') :: INT,
column_1 VARCHAR(50),
column_2 VARCHAR(100),
column_3 BOOLEAN,
last_updated timestamp(0));
CREATE TABLE logs_table
(like table_1 EXCLUDING CONSTRAINTS,
operation char(10) not null,
date_operated timestamp(0) default current_timestamp
);
create function logs_function()
returns trigger as $$
BEGIN
insert into logs_table (id, column_1, column_2, column_3, last_updated, operation)
values (old.id, old.column_1, old.column_2, old.column_3, old.last_updated, TG_OP);
IF TG_OP = 'UPDATE'
THEN
new.last_updated := current_timestamp;
RETURN NEW;
ELSIF TG_OP = 'DELETE'
THEN
RETURN OLD;
END IF;
end;
$$ LANGUAGE plpgsql;
CREATE TRIGGER logs_trigger
BEFORE UPDATE OR DELETE ON table_1
FOR EACH ROW EXECUTE PROCEDURE logs_function();
I'm trying to, somehow, trigger a automatic function drop when a table is dropped and I can't figure out how to do it.
TL;DR: Is there a way to trigger a function drop when a specific table is dropped? (POSTGRESQL 11.7)
Detailed explanation
I'll try to explain my problem using a simplified use case with dummy names.
I have three tables: sensor1, sensor2 and sumSensors;
A FUNCTION (sumdata) was created to INSERT data on sumSensors table. Inside this function I'll fetch data from sensor1 and sensor2 tables and insert its sum on table sumSensors;
A trigger was created for each sensor table which like this:
CREATE TRIGGER trig1
AFTER INSERT ON sensor1
FOR EACH ROW EXECUTE
FUNCTION sumdata();
Now, when a new row is inserted on tables sensor1 OR sensor2, the function sumdata will be executed and insert the sum of last values from both on table sumSensors
If I wanted to DROP FUNTION sumdata CASCADE;, the triggers would be automatically removed from tables sensor1 and sensor2. Until now that's everything fine! But that's not what I want.
My problem is:
Q: And if I just DROP TABLE sumSensors CASCADE;? What would happen to the function which was meant to insert on this table?
A: As expected, since there's no association between sumSensors table and sumdata function, the function won't be dropped (still exist)! The same happens to the triggers which use it (still exist). This means that when a new row is inserted on sensor tables, the function sumdata will be executed and corrupted, leading to a failure (even the INSERT which triggered the function execution won't be actually inserted).
Is there a way to trigger a function drop when a specific table is dropped?
Thank you in advance
There is no dependency tracking for functions in PostgreSQL (as of version 12).
You can use event triggers to maintain the dependencies yourself.
Full example follows.
More information: documentation of event triggers feature, support functions.
BEGIN;
CREATE TABLE _testtable ( id serial primary key, payload text );
INSERT INTO _testtable (payload) VALUES ('Test data');
CREATE FUNCTION _testfunc(integer) RETURNS integer
LANGUAGE SQL AS $$ SELECT $1 + count(*)::integer FROM _testtable; $$;
SELECT _testfunc(100);
CREATE FUNCTION trg_drop_dependent_functions()
RETURNS event_trigger
LANGUAGE plpgsql AS $$
DECLARE
_dropped record;
BEGIN
FOR _dropped IN
SELECT schema_name, object_name
FROM pg_catalog.pg_event_trigger_dropped_objects()
WHERE object_type = 'table'
LOOP
IF _dropped.schema_name = 'public' AND _dropped.object_name = '_testtable' THEN
EXECUTE 'DROP FUNCTION IF EXISTS _testfunc(integer)';
END IF;
END LOOP;
END;
$$;
CREATE EVENT TRIGGER trg_drop_dependent_functions ON sql_drop
EXECUTE FUNCTION trg_drop_dependent_functions();
DROP TABLE _testtable;
ROLLBACK;
I am trying to set up triggers for insert and update events for the master table of some partition tables in PostgreSQL. Each time an insertion is made into the master table, the insert trigger event will redirect it into the correct partition table. Consequently, I will need to return NULL from this function call, since I don't want the master table to be populated as well. If the master table receives an update event, it will update a timestamp before making the change in the table. The problem is that the update trigger is never fired. I am using PostgreSQL version 9.6.
I have tried to combine the trigger functions into one, and merged the called trigger procedures into one as well, but the results are the same. The update trigger is only triggered if I return NEW from the insertion trigger function (which populates the master table), or if I comment out the insertion trigger function altogether.
DROP SCHEMA IF EXISTS test CASCADE;
CREATE SCHEMA test;
SET SCHEMA 'test';
CREATE TYPE test_type AS ENUM ('unit', 'performance');
CREATE TABLE test (
type test_type NOT NULL,
score INTEGER NOT NULL CHECK (score > 0),
id SERIAL PRIMARY KEY,
updated_at TIMESTAMP DEFAULT current_timestamp
);
CREATE TABLE performance_test (
CHECK (type = 'performance')
) INHERITS (test);
CREATE FUNCTION insert_test()
RETURNS trigger AS
$$
BEGIN
INSERT INTO performance_test VALUES (NEW.*);
RETURN NULL;
END;
$$ LANGUAGE plpgsql;
CREATE FUNCTION update_timestamp()
RETURNS trigger AS
$$
BEGIN
RAISE NOTICE 'This is never reached.';
UPDATE performance_test
SET updated_at = current_timestamp
WHERE id = NEW.id;
RETURN NULL;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER test_insertion BEFORE INSERT ON test
FOR EACH ROW EXECUTE PROCEDURE insert_test();
CREATE TRIGGER test_update BEFORE UPDATE ON test
FOR EACH ROW EXECUTE PROCEDURE update_timestamp();
---------------------------------------------------------------------------
INSERT INTO test VALUES ('performance', 10);
SELECT * FROM performance_test;
UPDATE test SET score = 20 WHERE id = 1;
SELECT * FROM performance_test;
I am not sure if it is possible to achieve what I want with this method, so I'm reaching out here for any advice. Thanks in advance!
/ Hampus
Row triggers must be defined on individual partitions, not the partitioned table. See https://www.postgresql.org/docs/10/ddl-partitioning.html#DDL-PARTITIONING-DECLARATIVE-LIMITATIONS
I don't know why the documentation for 9.6 doesn't mention this
working update trigger:
CREATE FUNCTION update_timestamp()
RETURNS trigger AS
$$
BEGIN
NEW.updated_at = now();
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER test_update BEFORE UPDATE ON performance_test
FOR EACH ROW EXECUTE PROCEDURE update_timestamp();
if you do UPDATE test SET score = 30, updated_at=DEFAULT; or UPDATE test SET score = 30, updated_at=current_timestamp; you might not need the update trigger.
Partitioning is not a free lunch because it has non-obvious effects on both behavior and performance, as you noticed by the trigger not behaving as you expected. If you make a mistake it can easily lead to failing queries and even bad data.
If you are really sure you need it you should make sure you understand it in detail and otherwise I'd recommend you to avoid it, most issues with slow queries can be solved by making sure the table statistics is up to date, using the right indexes, optimizing queries, changing Postgres configuration or adding more hardware.
Wrote this trigger 5-6 months ago, and it ran fine on the db I deployed it to. Just took it off the shelf, and pretty much dropped it on the same database which lives on a different box. Sucker isn't executing anymore.
Essentially, this trigger just copies the "before" row to an audit table. The audit table lives in a different db on the same server, and I link it back to the source database as a FOREIGN Table:
Audit db:
CREATE TABLE next_gen_permissions_change
(
id serial NOT NULL,
authorizable_type character varying(64),
authorizable_id integer,
grantee_id integer,
grantee_type character varying(255),
capability_id integer NOT NULL,
permission integer DEFAULT 0,
event_time timestamp without time zone,
event_type character varying(10)
)
GRANT ALL ON TABLE next_gen_permissions_change TO foo;
I create the foreign table in the "source" database:
CREATE EXTENSION postgres_fdw;
CREATE SERVER myserver FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host 'localhost', dbname 'audit', port '5432');
CREATE USER MAPPING FOR foo SERVER myserver OPTIONS (user 'foo', password 'secret');
CREATE foreign TABLE next_gen_permissions_change
(
id serial NOT NULL,
authorizable_type character varying(64),
authorizable_id integer,
grantee_id integer,
grantee_type character varying(255),
capability_id integer NOT NULL,
permission integer DEFAULT 0,
event_time timestamp without time zone,
event_type character varying(10))
SERVER myserver;
GRANT ALL ON TABLE next_gen_permissions_change TO foo;
When I INSERT rows into source.next_gen_permissions_change, I see them show up in audit.next_gen_permissions_change...so I got that going for me.
Here's the function:
CREATE OR REPLACE FUNCTION audit_permissions()
RETURNS trigger AS
$BODY$
DECLARE
trigger_event_time timestamp;
BEGIN
trigger_event_time := now();
IF (TG_OP = 'INSERT') THEN
INSERT INTO next_gen_permissions_change(
id,
authorizable_type,
authorizable_id,
grantee_id,
grantee_type,
capability_id,
permission,
event_time,
event_type)
VALUES (NEW.id,
NEW.authorizable_type,
NEW.authorizable_id,
NEW.grantee_id,
NEW.grantee_type,
NEW.capability_id,
NEW.permission,
trigger_event_time,
'insert');
RETURN NEW;
ELSIF (TG_OP = 'DELETE') THEN
INSERT INTO next_gen_permissions_change(
id,
authorizable_type,
authorizable_id,
grantee_id,
grantee_type,
capability_id,
permission,
event_time,
event_type)
VALUES (OLD.id,
OLD.authorizable_type,
OLD.authorizable_id,
OLD.grantee_id,
OLD.grantee_type,
OLD.capability_id,
OLD.permission,
trigger_event_time,
'delete');
RETURN OLD;
END IF;
RETURN null;
END;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
And now, I'm adding the function as a trigger:
CREATE TRIGGER next_gen_permissions_audit
AFTER INSERT OR DELETE
ON next_gen_permissions
FOR EACH ROW
EXECUTE PROCEDURE audit_permissions();
No errors, all seems well:
As you can see, I added RAISE NOTICE as well, so I could hopefully see this sucker executing in pgadmin.
Here are all of the properties of the trigger (parent window) and function (child window embedded inside the parent):
WHEN executing something like
UPDATE next_gen_permissions SET f1=f1
...I don't see anything in the messages.
Also,
EXPLAIN ANALYZE UPDATE next_gen_permissions SET f1=f1
doesn't return anything interesting:
Can anyone see what I'm doing wrong here? I'm not getting any error messages, and when I look at the logs, the UPDATE goes right through without ANY mention that the trigger is getting fired.
Thanks much.
I've created the following table:
CREATE TABLE updates
(
"table" text,
last_update timestamp without time zone
)
I want to update it whenever any table is updated, the problem is I don't know how, could someone please help me turn this pseudocode into a trigger?
this = current table on whitch operation is performed
ON ALTER,INSERT,DELETE {
IF (SELECT COUNT(*) FROM updates where table = this) = 1
THEN
UPDATE updates SET last_update = timeofday()::timestamp WHERE `table`=this
ELSE
INSERT INTO updates VALUES (this,timeofday()::timestamp);
}
You need a trigger function that is called whenever one of your tables is "updated", assuming that you mean that an INSERT, UPDATE, or DELETE is successfully executed. That trigger function would look like this:
CREATE FUNCTION log_update() RETURNS trigger AS $$
BEGIN
UPDATE updates SET last_update = now() WHERE "table" = TG_TABLE_NAME;
IF NOT FOUND THEN
INSERT INTO updates VALUES (TG_TABLE_NAME, now());
END IF;
IF (TG_OP = 'DELETE') THEN
RETURN OLD;
ELSE
RETURN NEW;
END IF;
END; $$ LANGUAGE PLPGSQL;
Every table that has to be logged this way needs to have a trigger associated with it like this:
CREATE TRIGGER ZZZ_mytable_log_updates
AFTER INSERT OR UPDATE OR DELETE ON mytable
FOR EACH ROW EXECUTE PROCEDURE log_update();
A few comments:
Trigger functions are created with PL/PgSQL; see chapter 40 in the documentation. Trigger functions come with some automatic parameters such as TG_TABLE_NAME.
Don't use reserved words ("table" in your case) as column names. Actually, in this case you are better off using the oid of the table, with the associated TG_RELID automatic parameter. It takes up less storage, it is faster, and it avoids confusion between tables with the same name in different schemas of your database. You can use the pg_tables system catalog table to look up the table name from the oid.
You must return the proper value depending on the operation, or the operation may fail. INSERT and UPDATE operations need to have NEW returned; DELETE needs to have OLD returned.
The name of the trigger starts with "ZZZ" to make sure that it fires after any other triggers on the same table have succeeded (they are fired in alphabetical order). If a prior trigger fails, this trigger function will not be called, which is the proper behaviour because the insert, update or delete will not take place either.