Sooner or later, many face the need to make bulk changes to table entries. I've already and what to avoid. Today, I will talk about the second aspect of bulk updates — trigger firing..
For example, a malicious trigger is hanging over the table where you need to make adjustments, ON UPDATE,transferring all changes to some aggregates. You need to update everything (initialize a new field, for instance) carefully, so that these aggregates are not affected.
Let's just disable the triggers!
BEGIN;
ALTER TABLE ... DISABLE TRIGGER ...;
UPDATE ...; -- this will take a while
ALTER TABLE ... ENABLE TRIGGER ...;
COMMIT;That's basically it — everything is already hanging..
Because ALTER TABLE imposes AccessExclusive- the lock, under which no one performing parallel operations, even a simple one, SELECT, will be able to read anything from the table. This means that until this transaction is completed, everyone wishing to 'just read' will have to wait. And we remember that UPDATE it's going to be a l-o-o-o-ng wait…
So let's quickly disable it, then quickly enable it!
BEGIN;
ALTER TABLE ... DISABLE TRIGGER ...;
COMMIT;
UPDATE ...;
BEGIN;
ALTER TABLE ... ENABLE TRIGGER ...;
COMMIT;The situation is already better here, the wait time is significantly reduced. But two problems spoil the beauty:
ALTER TABLEit still waits for all other operations on the table, including long ones.SELECT- While the trigger is off, any change to the table, even if it's not ours, will 'fly by'. And it won’t make its way to the aggregates, although it should. That’s a problem!
Session Variable Management
So, in the previous option, we encountered a fundamental point — we need to teach the trigger to distinguish 'our' changes in the table from 'not ours'. 'Our' changes should pass as is, while 'not ours' should trigger. To achieve this, we can use .
session_replication_role.
Let's read :
The trigger firing mechanism is also influenced by the configuration variable . Triggers enabled without further specifications (by default) will fire when the replication role is 'origin' (by default) or 'local'. Triggers enabled with the specification
ENABLE REPLICA, will only fire if the current session mode is 'replica', and triggers enabled with the specificationENABLE ALWAYS, will fire regardless of the current replication mode.
I want to emphasize that this setting does not apply to all at once, as ALTER TABLE, but only to our separate special connect. In total, to ensure that no application triggers are activated:
SET session_replication_role = replica; -- disabled triggers
UPDATE ...;
SET session_replication_role = DEFAULT; -- reverted to the initial stateThe condition inside the trigger
However, the above option works for all triggers at once (or you need to 'alter' in advance the triggers you do not want to disable). But if we need to disable one specific trigger?
This will be aided by :
Extension parameter names are recorded as follows: the name of the extension, a dot, and then the actual name of the parameter, similar to fully qualified object names in SQL. For example: plpgsql.variable_conflict.
Since non-system parameters can be set in processes that do not load the corresponding extension module, PostgreSQL accepts values for any names with two components.
First, we improve the trigger, approximately as follows:
BEGIN
-- the conversion process can do everything
IF current_setting('mycfg.my_table_convert_process') = 'TRUE' THEN
IF TG_OP IN ('INSERT', 'UPDATE') THEN
RETURN NEW;
ELSE
RETURN OLD;
END IF;
END IF;
... By the way, this can be done 'live', without locks, through CREATE OR REPLACE for the trigger function. Then in the special connect we set our variable:
SET mycfg.my_table_convert_process = 'TRUE';
UPDATE ...;
SET mycfg.my_table_convert_process = ''; -- reverted to the initial state
Do you know other methods? Share in the comments.
Source: habr.com
