CREATE OR REPLACE PROCEDURE pg_catalog.drop_old_time_partitions( table_name regclass, older_than timestamptz) LANGUAGE plpgsql AS $$ DECLARE -- properties of the partitioned table number_of_partition_columns int; partition_column_index int; partition_column_type regtype; -- used to support dynamic type casting between the partition column type and timestamptz custom_cast text; is_partition_column_castable boolean; older_partitions_query text; r record; BEGIN -- check whether the table is time partitioned table, if not error out SELECT partnatts, partattrs[0] INTO number_of_partition_columns, partition_column_index FROM pg_catalog.pg_partitioned_table WHERE partrelid = table_name; IF NOT FOUND THEN RAISE '% is not partitioned', table_name::text; ELSIF number_of_partition_columns <> 1 THEN RAISE 'partitioned tables with multiple partition columns are not supported'; END IF; -- get datatype here to check interval-table type SELECT atttypid INTO partition_column_type FROM pg_attribute WHERE attrelid = table_name::oid AND attnum = partition_column_index; -- we currently only support partitioning by date, timestamp, and timestamptz custom_cast = ''; IF partition_column_type <> 'date'::regtype AND partition_column_type <> 'timestamp'::regtype AND partition_column_type <> 'timestamptz'::regtype THEN SELECT EXISTS(SELECT OID FROM pg_cast WHERE castsource = partition_column_type AND casttarget = 'timestamptz'::regtype) AND EXISTS(SELECT OID FROM pg_cast WHERE castsource = 'timestamptz'::regtype AND casttarget = partition_column_type) INTO is_partition_column_castable; IF not is_partition_column_castable THEN RAISE 'type of the partition column of the table % must be date, timestamp or timestamptz', table_name; END IF; custom_cast = format('::%s', partition_column_type); END IF; older_partitions_query = format('SELECT partition, nspname AS schema_name, relname AS table_name, from_value, to_value FROM pg_catalog.time_partitions, pg_catalog.pg_class c, pg_catalog.pg_namespace n WHERE parent_table = $1 AND partition = c.oid AND c.relnamespace = n.oid AND to_value IS NOT NULL AND to_value%1$s::timestamptz <= $2 ORDER BY to_value%1$s::timestamptz', custom_cast); FOR r IN EXECUTE older_partitions_query USING table_name, older_than LOOP RAISE NOTICE 'dropping % with start time % and end time %', r.partition, r.from_value, r.to_value; EXECUTE format('DROP TABLE %I.%I', r.schema_name, r.table_name); END LOOP; END; $$; COMMENT ON PROCEDURE pg_catalog.drop_old_time_partitions( table_name regclass, older_than timestamptz) IS 'drop old partitions of a time-partitioned table';