I needed a function to automatically manage partitions in a DB. I found a function that created 2 months at a time and adapted it to work with quarters and multiple tables with different primary keys and creation date keys.
Now I need to change it from dropping the last quarter´s partition. Instead I only want it to drop a quarter´s partitions if they are from the previous year. How do I change the code for previous quarters to do that?
Relevant code:
-- check if the partition for the previous quarter exists
v_date_from := date_trunc('quarter', v_current_date - '3 month'::interval);
v_partition_name := master_table || '_Q' || EXTRACT(QUARTER FROM v_date_from) || '_' || EXTRACT(YEAR FROM v_date_from);
v_rule_name := 'rule_' || master_table || '_Q' || EXTRACT(QUARTER FROM v_date_from) || '_' || EXTRACT(YEAR FROM v_date_from);
SELECT COUNT(*) = 1 INTO v_exists FROM pg_tables WHERE schemaname = 'public' AND tablename = v_partition_name;
IF (v_exists) THEN
    EXECUTE 'DROP RULE ' || v_rule_name || ' ON ' || master_table;
    EXECUTE 'DROP TABLE ' || v_partition_name;
END IF;
Complete function:
CREATE OR REPLACE FUNCTION manage_partitions(timestamp without time zone, master_table character varying, prime_key character varying, prime_date character varying) RETURNS void AS
$BODY$
DECLARE
    -- name of the next partition and rule (and interval boundaries)
    v_partition_name    VARCHAR(32);
    v_rule_name         VARCHAR(32);
    v_date_from         TIMESTAMP;
    v_date_to           TIMESTAMP;
    -- current date (if NULL, a current timestamp is used)
    v_date              ALIAS FOR $1;
    v_current_date      TIMESTAMP;
    -- used just for checking existence of the partitions
    v_exists            BOOLEAN;
BEGIN
    IF (v_date IS NULL) THEN
        v_current_date := current_timestamp;
    ELSE
        v_current_date := v_date;
    END IF;
    -- check if the partition for the previous quarter exists
    v_date_from := date_trunc('quarter', v_current_date - '3 month'::interval);
    v_partition_name := master_table || '_Q' || EXTRACT(QUARTER FROM v_date_from) || '_' || EXTRACT(YEAR FROM v_date_from);
    v_rule_name := 'rule_' || master_table || '_Q' || EXTRACT(QUARTER FROM v_date_from) || '_' || EXTRACT(YEAR FROM v_date_from);
    SELECT COUNT(*) = 1 INTO v_exists FROM pg_tables WHERE schemaname = 'public' AND tablename = v_partition_name;
    IF (v_exists) THEN
        EXECUTE 'DROP RULE ' || v_rule_name || ' ON ' || master_table;
        EXECUTE 'DROP TABLE ' || v_partition_name;
    END IF;
    -- create a partition for this quarter
    v_date_from := date_trunc('quarter', v_current_date);
    v_date_to := v_date_from + '3 month';
    v_partition_name := master_table || '_Q' || EXTRACT(QUARTER FROM v_date_from) || '_' || EXTRACT(YEAR FROM v_date_from);
    v_rule_name := 'rule_' || master_table || '_Q' || EXTRACT(QUARTER FROM v_date_from) || '_' || EXTRACT(YEAR FROM v_date_from);
    SELECT COUNT(*) = 1 INTO v_exists FROM pg_tables WHERE schemaname = 'public' AND tablename = v_partition_name;
    IF (NOT v_exists) THEN
        EXECUTE 'CREATE TABLE ' || v_partition_name || ' (PRIMARY KEY (' || prime_key || '), CHECK (' || prime_date || ' >= ''' || v_date_from || ''' AND ' || prime_date || ' < ''' || v_date_to || ''')) INHERITS (' || master_table || ')';
        EXECUTE 'CREATE RULE ' || v_rule_name || ' AS ON INSERT TO ' || master_table || ' DO INSTEAD INSERT INTO ' || v_partition_name || ' VALUES (NEW.*)';
        -- if you need to create indexes / foreign keys / whatever on the partition, you may do it here
    END IF;
    -- create a partition for next quarter
    v_date_from := date_trunc('quarter', v_current_date + '3 month'::interval);
    v_date_to := v_date_from + '3 month';
    v_partition_name := master_table || '_Q' || EXTRACT(QUARTER FROM v_date_from) || '_' || EXTRACT(YEAR FROM v_date_from);
    v_rule_name := 'rule_' || master_table || '_Q' || EXTRACT(QUARTER FROM v_date_from) || '_' || EXTRACT(YEAR FROM v_date_from);
    SELECT COUNT(*) = 1 INTO v_exists FROM pg_tables WHERE schemaname = 'public' AND tablename = v_partition_name;
    IF (NOT v_exists) THEN
        EXECUTE 'CREATE TABLE ' || v_partition_name || ' (PRIMARY KEY (' || prime_key || '), CHECK (' || prime_date || ' >= ''' || v_date_from || ''' AND ' || prime_date || ' < ''' || v_date_to || ''')) INHERITS (' || master_table || ')';
        EXECUTE 'CREATE RULE ' || v_rule_name || ' AS ON INSERT TO ' || master_table || ' DO INSTEAD INSERT INTO ' || v_partition_name || ' VALUES (NEW.*)';
        -- if you need to create indexes / foreign keys / whatever on the partition, you may do it here
    END IF;
END;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
 
     
    