The observations below is on a large sized sqlite3 database.
Setup:
I have a view which has a trigger for udpate of a field. This trigger of this view has multiple update statements on different underlying tables. These tables also have triggers for update of respective fields.
Also, there is a registered callback using sqlite_trace method on our production code. This method only prints the activity on this given database.
Observations:
When this view is updated for this given field, it updates the field of underlying tables.
Update of field on underlying tables trigger their respective triggers.
The registered callback method is called which prints that TRIGGER is called on the database with trigger name.
However, there are some triggers with no names. Or the callback method prints just TRIGGER without name. For example :
-- Update View V1
-- TRIGGER T1
-- TRIGGER T2
-- TRIGGER
-- TRIGGER T3
-- TRIGGER
-- TRIGGER T4
My question is : What are these un-named triggers ? When are they called ? Is this because some fields have UPDATE RESTRICT / DELETE RESTRICT / CASCADE on the tables ? I was unable to get any information from these triggers. Just trying to solve the mystery of these un-named triggers.
The un-named triggers are because of the referential integrity ( foreign key ) relationship of one table with another table.
Steps to reproduce:
Step1 : Create two tables where one table references other table and create some test rows in these tables. T1 can have CASCADE OR RESTRICT for delete or update.
CREATE TABLE T (id NUMBER);
CREATE TABLE T1 (id NUMBER REFERENCES T (id) DELETE (CASCADE/RESTRICT) UPDATE ( CASCADE /RESTRICT ));
Step 2: Write a test C++ program that creates a sqlite3 connection. Refer https://www.sqlite.org/cintro.html for more on SQLite C/C++ Interface.
Step 3: Enable following using sqlite3_exec
PRAGMA FOREIGN_KEY=ON .
Step 4: Register a callback with sqlite3_trace that prints the query in the callback. Refer : https://www.sqlite.org/c3ref/profile.html
Step 5: Call execute method to update id of table T.
Output: Above statement will execute UPDATE of table T and on referenced tables of T. In this case, its T1. The update on table T1 generates un-named trigger and a callback is generated on sqlite3_trace. The sql in callback has no information about this trigger and hence output is as below , i.e. trigger with no name:
TRIGGER -
Conclusion : The un-named triggers are seen because of the foreign key relationship. When a referenced table is modified, its associated
tables are attempted to change causing un-named triggers in sqlite3_trace callbacks.
Note : There will be an un-named trigger for each reference. So, if a field is referenced in n tables , you will see n un-named
triggers and n callbacks on sqlite3_trace. Also, the database should
have PRAGMA FOREIGN_KEY ON so that it enforces referential integrity.
You will not see this behavior if PRAGMA FOREIGN_KEY is OFF ( 0 ).
Related
I am working on a problem where I need to rename the table name. The table has few triggers and procedures attach to it.
My steps to do that:
Delete all the triggers and procedures of the table name employee.
Rename table name(employee) to user using ALTER command.
Then create all the triggers and procedures according to the new table.
Database I am using is postgresql.
Below query is used to delete the single trigger on a table.
DROP TRIGGER IF EXISTS update_employee_changes on employee;
But I want to delete all the triggers and procedures related to the given table in a single query.
You don't have to drop the triggers and functions. Just use a transaction:
BEGIN;
ALTER TABLE ... RENAME TO ...;
CREATE OR REPLACE FUNCTION /* function that uses the new name */
...
COMMIT;
You only have to replace the trigger functions and other functions. Triggers, views etc. don't have to be modified.
I have a table design where "notes" for various entities are handled using a relational table.
For example, the following tables exist
'notes' table having fields id and note
'knifes' table having as only field an id
'knife_notes' table having knife_id and note_id, being foreign keys to 'id' in knifes table
and notes 'id' in notes table respectively.
Update: the note_id field in the knife_notes tables is unique, so that each note can only be related to one particular knife.
When adding a note, i.e. a child, for a knife (parent), a note record is created and a record in table knife_notes is create too, thereby relating a notes id and a knifes id.
The two foreign keys are having 'On Delete Cascade'.
However, when a knife is deleted, only the records in knife_notes are 'cascade' deleted, not the records in the notes table.
Do I need a second query to delete the notes records when deleting a knife, or is there a better way?
What you did was create an n-to-m relationship between knives and notes. Are you sure that is what you need?
Because, the way your datamodel is set up now, it is actually desirable that notes aren't deleted. The current model allows (amongst others):
A. A knife that has 1 note
B. A specific knife that has more than 1 note (2 rows in knife_notes that point to the same knife and to different notes)
C. A specific note that is related to multiple knives
Because of scenario C the database can't just cascade from the knive_notes table to the notes table: there might be other tables that are dependent on a specific note.
To make it visual, think of this scenario in the knive_notes table:
id knife_id note_id
--------------------------------------
1 11 101
2 11 102
3 12 103
4 13 103
From a database point of view this is perfectly legal; note 103 is used for both knive 12 and 13. Now if you were to delete knive 12, the database can't simply remove note 103, because it could still be used by other knives (and in fact it is).
There are 2 sollutions I can think of:
1. simplify your datamodel
2. create a trigger or rule in PostgreSQL
Expanding on the options:
datamodel:
Why don't you create a datamodel that only has
Knive
Note (with a foreign key to knive)
This way a knive can have multiple notes, but a note always is related to a knive. This will not work, though, if a note is used multiple times or in multiple roles (ie. if you also have a table "guns" that also needs notes or if a specific note is used by multiple rows in the knive table ("the color of this knive is red")
triggers or rules
With rules you can rewrite your SQL query. With triggers you can execute a function just before or after an operation on a table.
Look up triggers & rewrite rules.
Myself, I'm most used to working with triggers. I would basically create an on-delete trigger that starts a trigger function. In that function I would check if the note isn't used anywhere else and if that is the case execute a delete on the note.
CREATE FUNCTION try_to_delete_note() RETURNS trigger AS ##
DECLARE
usage_found int;
BEGIN
-- Check that note is not used anywhere else
usage_found = (select count(*) from knive_notes where note_id = OLD.note_id)
IF (usage_found = 0) THEN
DELETE from note where id = OLD.note_id
END IF;
RETURN NULL; -- result is ignored since this is an after trigger
END;
## LANGUAGE plpgsql;
CREATE TRIGGER knife_notes_deleted_trigger
AFTER DELETE ON knife_notes
FOR EACH ROW
EXECUTE PROCEDURE try_to_delete_note();
Also see this page in postgresql docs with trigger examples.
I am new to PostgreSQL and I am currently working on triggers but am stuck at one point.
I have two tables Student and Room.
Room id is the primary key in Room and foreign key in Student.
If I am inserting in Student, then it should check in Room whether the new data exist or not.
This is a foreign key check constraint. I hope anyone can help me with it
I dont know your code and I dont get the meaning, but I can answer generally.
In PostgreSQL a trigger is normally defined in two steps:
Define a trigger function using the CREATE FUNCTION
Bind this created trigger function to a table using CREATE TRIGGER
A trigger function is a common function, except that is has a return value type trigger (in addition, it does not take any arguments).
CREATE OR REPLACE FUNCTION trigger_function()
RETURNS trigger
AS $$ ... $$;
binding:
CREATE TRIGGER trigger_name
AFTER INSERT ON table_name
FOR EACH ROW EXECUTE PROCEDURE trigger_function();
In addition, please also consult the excellent PG documentation at PostgreSQL 9.4 Triggers
I know that in plpgsql if one would want to refer to the new inserted row, you can use "NEW".
How can I do this in T-SQL (transact sql)?
The following is the trigger I am trying to create:
CREATE Trigger setAlertId on rules_table
FOR INSERT AS
DECLARE #max_id integer
SELECT #max_id = (select max(AlertId) from rules_table)
NEW.AlertId = #max_id+1
END
GO
I get the error message:
Incorrect syntax near 'NEW'
Thanks.
inserted and deleted pseudo tables:
DML trigger statements use two special tables: the deleted table and the inserted tables. SQL Server automatically creates and manages these tables. You can use these temporary, memory-resident tables to test the effects of certain data modifications and to set conditions for DML trigger actions. You cannot directly modify the data in the tables
In your case why dont you use an identity on the alertid field that increments itself?
If you want to do it in your trigger you will need to select your primary key from inserted and then do an update on rules tables.
Let T be a table with two columns: a and b, which reference, respectively, to tables A and B. Such references are of type "delete cascade", so if a row from A or B is deleted, the row in T which matches the origin reference will be also deleted.
Now, I want to set a trigger "on before delete row" on T: is there any way to detect which of the reference triggered the row deletion in T? In other words: can I know if the trigger was triggered by cascading from A or from B?
Thank you.
EDIT
OK, I've simplified the problem. I have the following tables:
users:
id: integer SERIAL primary key
name: character varying(128)
validatorchains:
id: integer SERIAL primary key
name: character varying(128)
validatorchainslinks:
chain: integer foreign key validatorchains.id on delete cascade
user: integer foreign key users.id on delete cascade
next: integer foreign key users.id on delete set null
prev: integer foreign key users.id on delete set null
The code of my "on before delete" trigger on validatorchainslinks is:
BEGIN
UPDATE validatorchainslinks SET next = OLD.next WHERE next = OLD.user;
UPDATE validatorchainslinks SET prev = OLD.prev WHERE prev = OLD.user;
RETURN OLD;
END;
So, the purpose is to create a linked list of users that can validate some sort of operation. In order to validate the operation, all users in the chain must agree. The problem arises when maintaining the linked list when a user is deleted. The trigger code above successfully relinks the elements of the chain. But, what happens if the deletion is triggered by deleting a row in validatorchains? I don't want the trigger to do the UPDATE stuff, but skip it and let the system delete all the rows that reference to the corresponding validator chain.
Hope this helps.
You can do this with a little work-around.
Since PG does not give you any relevant information in the trigger function that fires on the cascaded DELETE (in validatorchainslinks) you should record this information somewhere, immediately prior to the foreign record (in usersor validatorchains) being deleted. So you can write a BEFORE DELETE trigger that updates all records in validatorchainslinks with a value that you can then check in the trigger function of the latter table. This would require you to add a field to the table of course.
Alternatively, you can create a separate table that lists records from tables that are about to be deleted and any trigger function can then review that information. Write that table in a BEFORE DELETE trigger and remove the record in an AFTER DELETE trigger on the same table. This would be the more elegant solution if the same pattern (cascaded deletes) happens among multiple tables.
Cascaded deletes happen in a single transaction so you should be protected from race conditions.