Skip to content

Commit 3688f4e

Browse files
pashagolub0xgouda
authored andcommitted
[*] allow Postgres sink schemas to exist before init/upgrade
This change aims to ease v4 -> v5 migration be having `subpartitions` schema untouched. User will empty/drop `admin` schema and re-init it with pgwatch. Existing `subpartitions` and `public` tables should be used as is.
1 parent e8de600 commit 3688f4e

File tree

2 files changed

+73
-71
lines changed

2 files changed

+73
-71
lines changed

internal/sinks/sql/admin_functions.sql

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,30 @@
1+
/*
2+
creates a top level metric table if not already existing (non-existing tables show ugly warnings in Grafana).
3+
expects the "metrics_template" table to exist.
4+
*/
5+
CREATE OR REPLACE FUNCTION admin.ensure_dummy_metrics_table(metric text) RETURNS boolean AS
6+
$sql$
7+
DECLARE
8+
l_schema_type text;
9+
BEGIN
10+
SELECT schema_type INTO l_schema_type FROM admin.storage_schema_type;
11+
12+
IF to_regclass(format('public.%I', metric)) IS NOT NULL THEN
13+
RETURN false;
14+
END IF;
15+
16+
IF l_schema_type = 'postgres' THEN
17+
EXECUTE format('CREATE TABLE public.%I (LIKE admin.metrics_template INCLUDING INDEXES) PARTITION BY LIST (dbname)', metric);
18+
ELSIF l_schema_type = 'timescale' THEN
19+
PERFORM admin.ensure_partition_timescale(metric);
20+
END IF;
21+
22+
EXECUTE format('COMMENT ON TABLE public.%I IS $$pgwatch-generated-metric-lvl$$', metric);
23+
24+
RETURN true;
25+
END;
26+
$sql$ LANGUAGE plpgsql;
27+
128
/*
229
get_top_level_metric_tables() returns names of all top-level metric tables
330
*/

internal/sinks/sql/admin_schema.sql

Lines changed: 46 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -4,113 +4,88 @@
44
"subpartitions" schema - subpartitions of "public" schema top level metric tables (if using time / dbname-time partitioning)
55
*/
66

7-
create schema "admin";
8-
create schema "subpartitions";
7+
CREATE SCHEMA IF NOT EXISTS "admin";
8+
CREATE SCHEMA IF NOT EXISTS "subpartitions";
99

10-
create extension if not exists btree_gin;
10+
CREATE EXTENSION IF NOT EXISTS btree_gin;
1111

12-
create function admin.get_default_storage_type() returns text as
12+
CREATE OR REPLACE FUNCTION admin.get_default_storage_type() RETURNS text as
1313
$$
14-
select case
15-
when exists(select 1 from pg_extension where extname = 'timescaledb') then
14+
SELECT CASE
15+
WHEN EXISTS(SELECT 1 FROM pg_extension WHERE extname = 'timescaledb') THEN
1616
'timescale'
17-
else
17+
ELSE
1818
'postgres'
19-
end;
19+
END;
2020
$$
21-
language sql;
21+
LANGUAGE SQL;
2222

23-
create table admin.storage_schema_type (
24-
schema_type text not null default admin.get_default_storage_type(),
25-
initialized_on timestamptz not null default now(),
23+
CREATE TABLE admin.storage_schema_type (
24+
schema_type text NOT NULL DEFAULT admin.get_default_storage_type(),
25+
initialized_on timestamptz NOT NULL DEFAULT now(),
2626
check (schema_type in ('postgres', 'timescale'))
2727
);
2828

29-
insert into admin.storage_schema_type default values;
29+
INSERT INTO admin.storage_schema_type DEFAULT values;
3030

31-
comment on table admin.storage_schema_type is 'identifies storage schema for other pgwatch components';
31+
COMMENT ON TABLE admin.storage_schema_type IS 'identifies storage schema for other pgwatch components';
3232

33-
create unique index max_one_row on admin.storage_schema_type ((1));
33+
CREATE UNIQUE INDEX max_one_row ON admin.storage_schema_type ((1));
3434

3535
/* for the Grafana drop-down. managed by the gatherer */
36-
create table admin.all_distinct_dbname_metrics (
37-
dbname text not null,
38-
metric text not null,
39-
created_on timestamptz not null default now(),
40-
primary key (dbname, metric)
36+
CREATE TABLE admin.all_distinct_dbname_metrics (
37+
dbname text NOT NULL,
38+
metric text NOT NULL,
39+
created_on timestamptz NOT NULL DEFAULT now(),
40+
PRIMARY KEY (dbname, metric)
4141
);
4242

4343
/* currently only used to store TimescaleDB chunk interval */
44-
create table admin.config (
45-
key text not null primary key,
46-
value text not null,
47-
created_on timestamptz not null default now(),
44+
CREATE TABLE admin.config (
45+
key text NOT NULL PRIMARY KEY,
46+
value text NOT NULL,
47+
created_on timestamptz NOT NULL DEFAULT now(),
4848
last_modified_on timestamptz
4949
);
5050

5151
-- to later change the value call the admin.change_timescale_chunk_interval(interval) function!
5252
-- as changing the row directly will only be effective for completely new tables (metrics).
53-
insert into admin.config (key, value) values
53+
INSERT INTO admin.config (key, value) VALUES
5454
('timescale_chunk_interval', '2 days'),
5555
('timescale_compress_interval', '1 day');
5656

57-
create or replace function trg_config_modified() returns trigger
58-
as $$
59-
begin
57+
CREATE OR REPLACE FUNCTION trg_config_modified() RETURNS trigger
58+
AS $$
59+
BEGIN
6060
new.last_modified_on = now();
61-
return new;
62-
end;
61+
RETURN new;
62+
END;
6363
$$
64-
language plpgsql;
64+
LANGUAGE plpgsql;
6565

66-
create trigger config_modified before update on admin.config
67-
for each row execute function trg_config_modified();
66+
CREATE TRIGGER config_modified BEFORE UPDATE ON admin.config
67+
FOR EACH ROW EXECUTE FUNCTION trg_config_modified();
6868

69-
/*
70-
creates a top level metric table if not already existing (non-existing tables show ugly warnings in Grafana).
71-
expects the "metrics_template" table to exist.
72-
*/
73-
create or replace function admin.ensure_dummy_metrics_table(metric text) returns boolean as
74-
$sql$
75-
declare
76-
l_schema_type text;
77-
begin
78-
select schema_type into l_schema_type from admin.storage_schema_type;
79-
80-
if to_regclass(format('public.%I', metric)) is not null then
81-
return false;
82-
end if;
83-
84-
if l_schema_type = 'postgres' then
85-
execute format('CREATE TABLE public.%I (LIKE admin.metrics_template INCLUDING INDEXES) PARTITION BY LIST (dbname)', metric);
86-
elsif l_schema_type = 'timescale' then
87-
perform admin.ensure_partition_timescale(metric);
88-
end if;
89-
90-
execute format('COMMENT ON TABLE public.%I IS $$pgwatch-generated-metric-lvl$$', metric);
91-
92-
return true;
93-
end;
94-
$sql$ language plpgsql;
95-
96-
create table admin.metrics_template (
97-
time timestamptz not null default now(),
98-
dbname text not null,
99-
data jsonb not null,
69+
CREATE TABLE admin.metrics_template (
70+
time timestamptz NOT NULL DEFAULT now(),
71+
dbname text NOT NULL,
72+
data jsonb NOT NULL,
10073
tag_data jsonb,
101-
check (false)
74+
CHECK (false)
10275
);
10376

104-
comment on table admin.metrics_template IS 'used as a template for all new metric definitions';
77+
COMMENT ON TABLE admin.metrics_template IS 'used as a template for all new metric definitions';
10578

106-
create index on admin.metrics_template (dbname, time);
79+
CREATE INDEX ON admin.metrics_template (dbname, time);
10780
-- create index on admin.metrics_template using brin (dbname, time); /* consider BRIN instead for large data amounts */
10881
-- create index on admin.metrics_template using gin (tag_data) where tag_data notnull;
10982

110-
-- define migrations you need to apply
111-
-- every change to the database schema should populate this table.
112-
-- Version value should contain issue number zero padded followed by
113-
-- short description of the issue\feature\bug implemented\resolved
83+
/*
84+
Define migrations you need to apply. Every change to the
85+
database schema should populate this table.
86+
Version value should contain issue number zero padded followed by
87+
short description of the issue\feature\bug implemented\resolved
88+
*/
11489
CREATE TABLE admin.migration(
11590
id bigint PRIMARY KEY,
11691
version text NOT NULL

0 commit comments

Comments
 (0)