From bc31892f291562ab0912301b395bc092fe692183 Mon Sep 17 00:00:00 2001 From: Johan Brandhorst-Satzkorn Date: Tue, 28 Mar 2023 10:35:26 -0700 Subject: [PATCH] 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 --- .../postgres/10/04_vault_credential.up.sql | 3 ++ .../66/01_add_set_once_columns.up.sql | 30 +++++++++++++++++++ .../66/02_use_set_once_columns.up.sql | 12 ++++++++ .../vault/credential_vault_store.sql | 26 ++++++++++++++++ 4 files changed, 71 insertions(+) create mode 100644 internal/db/schema/migrations/oss/postgres/66/01_add_set_once_columns.up.sql create mode 100644 internal/db/schema/migrations/oss/postgres/66/02_use_set_once_columns.up.sql create mode 100644 internal/db/sqltest/tests/credential/vault/credential_vault_store.sql diff --git a/internal/db/schema/migrations/oss/postgres/10/04_vault_credential.up.sql b/internal/db/schema/migrations/oss/postgres/10/04_vault_credential.up.sql index 88202b3e5..a0e7c09c4 100644 --- a/internal/db/schema/migrations/oss/postgres/10/04_vault_credential.up.sql +++ b/internal/db/schema/migrations/oss/postgres/10/04_vault_credential.up.sql @@ -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(); diff --git a/internal/db/schema/migrations/oss/postgres/66/01_add_set_once_columns.up.sql b/internal/db/schema/migrations/oss/postgres/66/01_add_set_once_columns.up.sql new file mode 100644 index 000000000..e46b1c45a --- /dev/null +++ b/internal/db/schema/migrations/oss/postgres/66/01_add_set_once_columns.up.sql @@ -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; diff --git a/internal/db/schema/migrations/oss/postgres/66/02_use_set_once_columns.up.sql b/internal/db/schema/migrations/oss/postgres/66/02_use_set_once_columns.up.sql new file mode 100644 index 000000000..c15358976 --- /dev/null +++ b/internal/db/schema/migrations/oss/postgres/66/02_use_set_once_columns.up.sql @@ -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; diff --git a/internal/db/sqltest/tests/credential/vault/credential_vault_store.sql b/internal/db/sqltest/tests/credential/vault/credential_vault_store.sql new file mode 100644 index 000000000..150dadf44 --- /dev/null +++ b/internal/db/sqltest/tests/credential/vault/credential_vault_store.sql @@ -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;