build(repo): structure the repo into postgresql subdirectory with separate template and internal tests
This commit is contained in:
194
postgresql/tests/docker/init_scripts/01_30_maintenance.sql
Normal file
194
postgresql/tests/docker/init_scripts/01_30_maintenance.sql
Normal file
@@ -0,0 +1,194 @@
|
||||
-- ============================================================================
|
||||
-- Core functions for Zabbix partitioning (Create, Drop, Maintain).
|
||||
-- ============================================================================
|
||||
|
||||
-- Function to check if a partition exists
|
||||
CREATE OR REPLACE FUNCTION partitions.partition_exists(p_partition_name text)
|
||||
RETURNS boolean AS $$
|
||||
BEGIN
|
||||
RETURN EXISTS (
|
||||
SELECT 1 FROM pg_class c
|
||||
JOIN pg_namespace n ON n.oid = c.relnamespace
|
||||
WHERE c.relname = p_partition_name
|
||||
);
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
-- Function to create a partition
|
||||
CREATE OR REPLACE PROCEDURE partitions.create_partition(
|
||||
p_parent_table text,
|
||||
p_start_time timestamp with time zone,
|
||||
p_end_time timestamp with time zone,
|
||||
p_period text
|
||||
) LANGUAGE plpgsql AS $$
|
||||
DECLARE
|
||||
v_partition_name text;
|
||||
v_start_ts bigint;
|
||||
v_end_ts bigint;
|
||||
v_suffix text;
|
||||
v_parent_schema text;
|
||||
BEGIN
|
||||
-- Determine the schema of the parent table
|
||||
SELECT n.nspname INTO v_parent_schema
|
||||
FROM pg_class c
|
||||
JOIN pg_namespace n ON n.oid = c.relnamespace
|
||||
WHERE c.relname = p_parent_table;
|
||||
|
||||
IF NOT FOUND THEN
|
||||
RAISE EXCEPTION 'Parent table % not found', p_parent_table;
|
||||
END IF;
|
||||
-- (No changes needed for time here as passed params are already UTC-adjusted in caller)
|
||||
v_start_ts := extract(epoch from p_start_time)::bigint;
|
||||
v_end_ts := extract(epoch from p_end_time)::bigint;
|
||||
|
||||
IF p_period = 'month' THEN
|
||||
v_suffix := to_char(p_start_time, 'YYYYMM');
|
||||
ELSE
|
||||
v_suffix := to_char(p_start_time, 'YYYYMMDD');
|
||||
END IF;
|
||||
|
||||
v_partition_name := p_parent_table || '_p' || v_suffix;
|
||||
|
||||
IF NOT partitions.partition_exists(v_partition_name) THEN
|
||||
EXECUTE format(
|
||||
'CREATE TABLE %I.%I PARTITION OF %I.%I FOR VALUES FROM (%s) TO (%s)',
|
||||
v_parent_schema, v_partition_name, v_parent_schema, p_parent_table, v_start_ts, v_end_ts
|
||||
);
|
||||
END IF;
|
||||
END;
|
||||
$$;
|
||||
|
||||
-- Function to drop old partitions
|
||||
CREATE OR REPLACE PROCEDURE partitions.drop_old_partitions(
|
||||
p_parent_table text,
|
||||
p_retention interval,
|
||||
p_period text
|
||||
) LANGUAGE plpgsql AS $$
|
||||
DECLARE
|
||||
v_cutoff_ts bigint;
|
||||
v_partition record;
|
||||
v_partition_date timestamp with time zone;
|
||||
v_suffix text;
|
||||
v_partition_schema text;
|
||||
BEGIN
|
||||
-- Calculate cutoff timestamp
|
||||
v_cutoff_ts := extract(epoch from (now() - p_retention))::bigint;
|
||||
|
||||
FOR v_partition IN
|
||||
SELECT
|
||||
child.relname AS partition_name,
|
||||
n.nspname AS partition_schema
|
||||
FROM pg_inherits
|
||||
JOIN pg_class parent ON pg_inherits.inhparent = parent.oid
|
||||
JOIN pg_class child ON pg_inherits.inhrelid = child.oid
|
||||
JOIN pg_namespace n ON child.relnamespace = n.oid
|
||||
WHERE parent.relname = p_parent_table
|
||||
LOOP
|
||||
-- Parse partition suffix to determine age
|
||||
-- Format: parent_pYYYYMM or parent_pYYYYMMDD
|
||||
v_suffix := substring(v_partition.partition_name from length(p_parent_table) + 3);
|
||||
|
||||
BEGIN
|
||||
IF length(v_suffix) = 6 THEN -- YYYYMM
|
||||
v_partition_date := to_timestamp(v_suffix || '01', 'YYYYMMDD') AT TIME ZONE 'UTC';
|
||||
-- For monthly, we check if the END of the month is older than retention?
|
||||
-- Or just strict retention.
|
||||
-- To be safe, adding 1 month to check vs cutoff.
|
||||
IF extract(epoch from (v_partition_date + '1 month'::interval)) < v_cutoff_ts THEN
|
||||
RAISE NOTICE 'Dropping old partition %', v_partition.partition_name;
|
||||
EXECUTE format('DROP TABLE %I.%I', v_partition.partition_schema, v_partition.partition_name);
|
||||
COMMIT; -- Release lock immediately
|
||||
END IF;
|
||||
ELSIF length(v_suffix) = 8 THEN -- YYYYMMDD
|
||||
v_partition_date := to_timestamp(v_suffix, 'YYYYMMDD') AT TIME ZONE 'UTC';
|
||||
IF extract(epoch from (v_partition_date + '1 day'::interval)) < v_cutoff_ts THEN
|
||||
RAISE NOTICE 'Dropping old partition %', v_partition.partition_name;
|
||||
EXECUTE format('DROP TABLE %I.%I', v_partition.partition_schema, v_partition.partition_name);
|
||||
COMMIT; -- Release lock immediately
|
||||
END IF;
|
||||
END IF;
|
||||
EXCEPTION WHEN OTHERS THEN
|
||||
-- Ignore parsing errors for non-standard partitions
|
||||
NULL;
|
||||
END;
|
||||
END LOOP;
|
||||
END;
|
||||
$$;
|
||||
|
||||
-- MAIN Procedure to maintain a single table
|
||||
CREATE OR REPLACE PROCEDURE partitions.maintain_table(
|
||||
p_table_name text,
|
||||
p_period text,
|
||||
p_keep_history interval,
|
||||
p_future_partitions integer DEFAULT 5
|
||||
) LANGUAGE plpgsql AS $$
|
||||
DECLARE
|
||||
v_start_time timestamp with time zone;
|
||||
v_period_interval interval;
|
||||
i integer;
|
||||
v_past_iterations integer;
|
||||
BEGIN
|
||||
IF p_period = 'day' THEN
|
||||
v_period_interval := '1 day'::interval;
|
||||
v_start_time := date_trunc('day', now() AT TIME ZONE 'UTC');
|
||||
-- Calculate how many past days cover the retention period (86400 seconds = 1 day)
|
||||
v_past_iterations := ceil(extract(epoch from p_keep_history) / 86400)::integer;
|
||||
|
||||
ELSIF p_period = 'week' THEN
|
||||
v_period_interval := '1 week'::interval;
|
||||
v_start_time := date_trunc('week', now() AT TIME ZONE 'UTC');
|
||||
-- 604800 seconds = 1 week
|
||||
v_past_iterations := ceil(extract(epoch from p_keep_history) / 604800)::integer;
|
||||
|
||||
ELSIF p_period = 'month' THEN
|
||||
v_period_interval := '1 month'::interval;
|
||||
v_start_time := date_trunc('month', now() AT TIME ZONE 'UTC');
|
||||
-- Approximate 30 days per month (2592000 seconds)
|
||||
v_past_iterations := ceil(extract(epoch from p_keep_history) / 2592000)::integer;
|
||||
ELSE
|
||||
RETURN;
|
||||
END IF;
|
||||
|
||||
-- 1. Create Future Partitions (Current + Buffer)
|
||||
FOR i IN 0..p_future_partitions LOOP
|
||||
CALL partitions.create_partition(
|
||||
p_table_name,
|
||||
v_start_time + (i * v_period_interval),
|
||||
v_start_time + ((i + 1) * v_period_interval),
|
||||
p_period
|
||||
);
|
||||
COMMIT; -- Release lock immediately
|
||||
END LOOP;
|
||||
|
||||
-- 2. Create Past Partitions (Covering retention period)
|
||||
IF v_past_iterations > 0 THEN
|
||||
FOR i IN 1..v_past_iterations LOOP
|
||||
CALL partitions.create_partition(
|
||||
p_table_name,
|
||||
v_start_time - (i * v_period_interval),
|
||||
v_start_time - ((i - 1) * v_period_interval),
|
||||
p_period
|
||||
);
|
||||
COMMIT; -- Release lock immediately
|
||||
END LOOP;
|
||||
END IF;
|
||||
|
||||
-- 3. Drop Old Partitions
|
||||
CALL partitions.drop_old_partitions(p_table_name, p_keep_history, p_period);
|
||||
|
||||
-- 4. Update Metadata
|
||||
UPDATE partitions.config SET last_updated = now() WHERE table_name = p_table_name;
|
||||
END;
|
||||
$$;
|
||||
|
||||
-- Global Maintenance Procedure
|
||||
CREATE OR REPLACE PROCEDURE partitions.run_maintenance()
|
||||
LANGUAGE plpgsql AS $$
|
||||
DECLARE
|
||||
v_row record;
|
||||
BEGIN
|
||||
FOR v_row IN SELECT * FROM partitions.config LOOP
|
||||
CALL partitions.maintain_table(v_row.table_name, v_row.period, v_row.keep_history, v_row.future_partitions);
|
||||
END LOOP;
|
||||
END;
|
||||
$$;
|
||||
Reference in New Issue
Block a user