feat(sql): Add history tables for host catalogs

This adds history tables for `static_host_catalog` and
`host_plugin_catalog`. This also adds a base table for the host catalog
history tables and an immutable table with one row for when a target has
a direct address assigned to it.

This also adds a reusable trigger function for tables that are
immutable.
pull/3251/head
Michael Gaffney 3 years ago committed by Timothy Messier
parent 7ebe393aee
commit b97dc07bb0
No known key found for this signature in database
GPG Key ID: EFD2F184F7600572

@ -45,4 +45,18 @@ begin;
comment on function wt_url_safe_id is
'Returns a random ID of 14 characters containing URL safe characters only';
create function immutable_table() returns trigger
as $$
begin
raise exception 'immutable table: %', tg_table_name using
errcode = '23603',
schema = tg_table_schema,
table = tg_table_name;
return null;
end;
$$ language plpgsql;
comment on function immutable_table is
'immutable_table() is a trigger function that prevents all changes to a table. '
'It must be added as a trigger to a table before insert, update, and delete events.';
commit;

@ -0,0 +1,129 @@
-- Copyright (c) HashiCorp, Inc.
-- SPDX-License-Identifier: MPL-2.0
begin;
create table host_catalog_history_base (
history_id wt_url_safe_id primary key
);
comment on table host_catalog_history_base is
'host_catalog_history_base is a base history table '
'for host catalog history tables.';
create function insert_host_catalog_history_subtype() returns trigger
as $$
begin
insert into host_catalog_history_base
(history_id)
values
(new.history_id);
return new;
end;
$$ language plpgsql;
comment on function insert_host_catalog_history_subtype is
'insert_host_catalog_history_subtype is a before insert trigger '
'function for subtypes of host_catalog_history_base.';
create function delete_host_catalog_history_subtype() returns trigger
as $$
begin
delete
from host_catalog_history_base
where history_id = old.history_id;
return null; -- result is ignored since this is an after trigger
end;
$$ language plpgsql;
comment on function delete_host_catalog_history_subtype is
'delete_host_catalog_history_subtype() is an after delete trigger '
'function for subtypes of host_catalog_history_base.';
create table no_host_catalog_history (
history_id wt_url_safe_id primary key
constraint host_catalog_history_base_fkey
references host_catalog_history_base (history_id)
on delete restrict
on update restrict
);
comment on table no_host_catalog_history is
'no_host_catalog_history is a table with one row to represent '
'the case of a target with a direct address associated to it.';
insert into host_catalog_history_base values ('_______none');
insert into no_host_catalog_history values ('_______none');
create trigger immutable_table before insert or update or delete on no_host_catalog_history
for each row execute procedure immutable_table();
create table static_host_catalog_hst (
public_id wt_public_id not null,
name text null,
description text null,
project_id wt_scope_id not null,
history_id wt_url_safe_id default wt_url_safe_id() primary key
constraint host_catalog_history_base_fkey
references host_catalog_history_base (history_id)
on delete cascade
on update cascade,
valid_range tstzrange not null default tstzrange(current_timestamp, null),
constraint static_host_catalog_hst_valid_range_excl
exclude using gist (public_id with =, valid_range with &&)
);
comment on table static_host_catalog_hst is
'static_host_catalog_hst is a history table where each row contains the values from a row '
'in the static_host_catalog table during the time range in the valid_range column.';
create trigger insert_host_catalog_history_subtype before insert on static_host_catalog_hst
for each row execute function insert_host_catalog_history_subtype();
create trigger delete_host_catalog_history_subtype after delete on static_host_catalog_hst
for each row execute function delete_host_catalog_history_subtype();
create trigger hst_on_insert after insert on static_host_catalog
for each row execute function hst_on_insert();
create trigger hst_on_update after update on static_host_catalog
for each row execute function hst_on_update();
create trigger hst_on_delete after delete on static_host_catalog
for each row execute function hst_on_delete();
insert into static_host_catalog_hst
(public_id, name, description, project_id)
select public_id, name, description, project_id
from static_host_catalog;
create table host_plugin_catalog_hst (
public_id wt_public_id not null,
name wt_name null,
description text null,
project_id wt_scope_id not null,
plugin_id wt_plugin_id not null,
attributes bytea not null,
history_id wt_url_safe_id default wt_url_safe_id() primary key
constraint host_catalog_history_base_fkey
references host_catalog_history_base (history_id)
on delete cascade
on update cascade,
valid_range tstzrange not null default tstzrange(current_timestamp, null),
constraint host_plugin_catalog_hst_valid_range_excl
exclude using gist (public_id with =, valid_range with &&)
);
comment on table host_plugin_catalog_hst is
'host_plugin_catalog_hst is a history table where each row contains the values from a row '
'in the host_plugin_catalog table during the time range in the valid_range column.';
create trigger insert_host_catalog_history_subtype before insert on host_plugin_catalog_hst
for each row execute function insert_host_catalog_history_subtype();
create trigger delete_host_catalog_history_subtype after delete on host_plugin_catalog_hst
for each row execute function delete_host_catalog_history_subtype();
create trigger hst_on_insert after insert on host_plugin_catalog
for each row execute function hst_on_insert();
create trigger hst_on_update after update on host_plugin_catalog
for each row execute function hst_on_update();
create trigger hst_on_delete after delete on host_plugin_catalog
for each row execute function hst_on_delete();
insert into host_plugin_catalog_hst
(public_id, name, description, project_id, plugin_id, attributes)
select public_id, name, description, project_id, plugin_id, attributes
from host_plugin_catalog;
commit;

@ -29,6 +29,7 @@ TESTS ?= tests/setup/*.sql \
tests/hcp/*/*.sql \
tests/kms/*.sql \
tests/storage/*.sql \
tests/domain/*.sql \
tests/history/*.sql \
tests/recording/*.sql

@ -279,6 +279,22 @@ begin;
static_host_set as s
where h.catalog_id = s.catalog_id;
insert into plugin
(scope_id, public_id, name)
values
('global', 'plg_____chost', 'Colors Host Plugin');
insert into plugin_host_supported
(public_id)
values
('plg_____chost');
insert into host_plugin_catalog
(project_id, plugin_id, public_id, name, attributes)
values
('p____bcolors', 'plg_____chost', 'c___cb-plghcl', 'Blue Color Plugin Catalog', ''),
('p____rcolors', 'plg_____chost', 'c___cr-plghcl', 'Red Color Plugin Catalog', '');
insert into target_tcp
(project_id, public_id, name)
values

@ -0,0 +1,32 @@
-- Copyright (c) HashiCorp, Inc.
-- SPDX-License-Identifier: MPL-2.0
begin;
select plan(5);
create table test_t1 (
id text
);
insert into test_t1 (id) values ('one');
create trigger immutable_table before insert or update or delete
on test_t1 for each row execute procedure immutable_table();
select is(count(*), 1::bigint) from test_t1;
prepare insert_fail as insert into test_t1 (id) values ('two');
select throws_ok('insert_fail', '23603');
prepare update_fail as
update test_t1
set id = 'two'
where id = 'one';
select throws_ok('update_fail', '23603');
prepare delete_fail as delete from test_t1;
select throws_ok('delete_fail', '23603');
select is(count(*), 1::bigint) from test_t1;
select * from finish();
rollback;

@ -0,0 +1,36 @@
-- Copyright (c) HashiCorp, Inc.
-- SPDX-License-Identifier: MPL-2.0
begin;
select plan(15);
-- Verify the trigger functions exist and are declared properly
select has_function('insert_host_catalog_history_subtype');
select volatility_is('insert_host_catalog_history_subtype', 'volatile');
select isnt_strict('insert_host_catalog_history_subtype');
select has_function('delete_host_catalog_history_subtype');
select volatility_is('delete_host_catalog_history_subtype', 'volatile');
select isnt_strict('delete_host_catalog_history_subtype');
select has_trigger('static_host_catalog_hst', 'insert_host_catalog_history_subtype');
select has_trigger('static_host_catalog_hst', 'delete_host_catalog_history_subtype');
select fk_ok('static_host_catalog_hst', 'history_id', 'host_catalog_history_base' , 'history_id');
select has_trigger('host_plugin_catalog_hst', 'insert_host_catalog_history_subtype');
select has_trigger('host_plugin_catalog_hst', 'delete_host_catalog_history_subtype');
select fk_ok('host_plugin_catalog_hst', 'history_id', 'host_catalog_history_base' , 'history_id');
select is(count(*), 1::bigint) from no_host_catalog_history;
select fk_ok('no_host_catalog_history', 'history_id', 'host_catalog_history_base' , 'history_id');
select results_eq(
'select '
'(select count(*) from static_host_catalog_hst) + '
'(select count(*) from host_plugin_catalog_hst) + '
'(select count(*) from no_host_catalog_history)',
'select count(*) from host_catalog_history_base'
);
select * from finish();
rollback;
Loading…
Cancel
Save