internal/db: add set_once_columns function (#3117)

* internal/db: add set_once_columns function

Also replace existing before_soft_delete_credential_vault_store
function and trigger with the new function.

* internal/db/schema: add note to replaced definitions
This commit is contained in:
Johan Brandhorst-Satzkorn authored and GitHub committed 2023-03-28 13:35:26 -04:00
1 parent e4c656ae5d
commit bc31892f29
4 files changed
+71

No files matched your search

@@ -67,6 +67,8 @@ begin;
-- and an update contains a value for delete_time different from the current
-- value, this trigger will raise an error with error code 23602 which is a
-- class 23 integrity constraint violation: set_once_violation.
--
-- Deleted in 66/02_use_set_once_columns.up.sql
create function before_soft_delete_credential_vault_store() returns trigger
as $$
begin
@@ -83,6 +85,7 @@ begin;
end;
$$ language plpgsql;
-- Replaced in 66/02_use_set_once_columns.up.sql
create trigger before_soft_delete_credential_vault_store before update on credential_vault_store
for each row execute procedure before_soft_delete_credential_vault_store();
@@ -0,0 +1,30 @@
-- Copyright (c) HashiCorp, Inc.
-- SPDX-License-Identifier: MPL-2.0
begin;
create function set_once_columns() returns trigger
as $$
declare
col_name text;
new_value text;
old_value text;
begin
foreach col_name in array tg_argv loop
execute format('SELECT $1.%I', col_name) into new_value using new;
execute format('SELECT $1.%I', col_name) into old_value using old;
if old_value is not null and new_value is distinct from old_value then
raise exception 'set_once_violation: %.%', tg_table_name, col_name using
errcode = '23602',
schema = tg_table_schema,
table = tg_table_name,
column = col_name;
end if;
end loop;
return new;
end;
$$ language plpgsql;
comment on function set_once_columns() is
'set_once_columns asserts that a column cannot be updated after being set to a non-null value';
commit;
@@ -0,0 +1,12 @@
-- Copyright (c) HashiCorp, Inc.
-- SPDX-License-Identifier: MPL-2.0
begin;
drop trigger before_soft_delete_credential_vault_store on credential_vault_store;
drop function before_soft_delete_credential_vault_store;
create trigger set_once_columns before update on credential_vault_store
for each row execute procedure set_once_columns('delete_time');
commit;
@@ -0,0 +1,26 @@
-- Copyright (c) HashiCorp, Inc.
-- SPDX-License-Identifier: MPL-2.0
-- credential_vault_store tests the credential_vault_store table
begin;
select plan(3);
select wtt_load('widgets', 'iam', 'kms', 'auth', 'hosts', 'targets', 'credentials');
-- validate the setup data
select is(count(*), 1::bigint) from credential_vault_store where public_id = 'vs_______cvs1';
prepare soft_delete_credential_vault_store as
update credential_vault_store
set delete_time = clock_timestamp() -- Can't use now() or current_timestamp since constant within transaction
where public_id = 'vs_______cvs1';
-- First delete succeeds
select lives_ok('soft_delete_credential_vault_store');
-- Second delete fails because we can only set delete_time once
select throws_ok('soft_delete_credential_vault_store', 'set_once_violation: credential_vault_store.delete_time', '23602');
select * from finish();
rollback;