Is it possible to declare a trigger in SQLite3 which forces the user to explictly provide a value within an UPDATE statement?
Lets assume we have an Article
table:
CREATE TABLE Article (
Id INTEGER PRIMARY KEY,
Title TEXT NOT NULL UNIQUE,
Content TEXT,
UserInserted TEXT NOT NULL,
UserUpdated TEXT
);
I can declare the following trigger which prohibits empty values for the column UserUpdated
:
CREATE TRIGGER IF NOT EXISTS Trig_Article_BEFORE_UPDATE
BEFORE UPDATE OF Title, Content ON Article
BEGIN
SELECT
CASE
WHEN new.UserUpdated IS NULL THEN RAISE(ABORT, 'UserUpdated must not be NULL.')
WHEN length(new.UserUpdated) = 0 THEN RAISE(ABORT, 'UserUpdated must not be NULL.')
END;
END;
Insertion works as expected:
INSERT INTO Article(Title, Content, UserInserted) VALUES('Foo', '', '<user_A>');
Updating without providing a UserUpdated
in the first place works as well:
UPDATE Article SET Content = 'Bar' WHERE Id = 1;
-- Error: UserUpdated must not be NULL.
UPDATE Article SET Content = 'Bar', UserUpdated = '' WHERE Id = 1;
-- Error: UserUpdated must not be NULL.
But once a UserUpdated
has been set it is no longer required to provide the column explicitly.
UPDATE Article SET Content = 'Bar', UserUpdated = '<user_B>' WHERE Id = 1;
UPDATE Article SET Content = 'Foo Bar' WHERE Id = 1;
-- No error
Is there a way to declare a trigger so that the last statement throws an error as well?
Update 22.11.2019
Thanks to C Perkins' answer I came up with a solution using an extra column.
An extra column CurrentUser
is added to Article
:
CREATE TABLE Article (
-- ...
CurrentUser TEXT
);
A BEFORE UPDATE
trigger ensures that this column is set:
CREATE TRIGGER IF NOT EXISTS Trig_Article_BEFORE_UPDATE
BEFORE UPDATE ON Article
WHEN old.Title <> new.Title OR
old.Content <> new.Content OR
old.CurrentUser <> new.CurrentUser
BEGIN
SELECT
CASE
WHEN new.CurrentUser IS NULL THEN RAISE(ABORT, 'CurrentUser must not be NULL.')
WHEN length(new.CurrentUser) = 0 THEN RAISE(ABORT, 'CurrentUser must not be NULL.')
END;
END;
An AFTER UPDATE
Trigger (if CurrentUser
is not null) copies the value from CurrentUser
to UserUpdated
and clears CurrentUser
again.
CREATE TRIGGER IF NOT EXISTS Trig_Article_AFTER_UPDATE
AFTER UPDATE ON Article
WHEN new.CurrentUser IS NOT NULL
BEGIN
UPDATE Article SET UserUpdated = new.CurrentUser, CurrentUser = NULL WHERE Id = new.Id;
END;
To prevent direct updates of UserUpdated
another trigger is used:
CREATE TRIGGER IF NOT EXISTS Trig_Article_UserUpdated_BEFORE_UPDATE
BEFORE UPDATE ON Article
WHEN old.UserUpdated <> new.UserUpdated AND
old.CurrentUser IS NULL
BEGIN
SELECT RAISE(ABORT, 'You must not UPDATE UserUpdated.');
END;
After all I get the desired behaviour. Every time Content
or Title
are updated the Column CurrentUser
has to be provided explicitly within the update statemant and UserUpdated
reflects the last user who updated the values.