CREATE TABLE example ( r DATE, p DATE ); CREATE FUNCTION example_part_name(DATE) RETURNS char(18) IMMUTABLE STRICT AS $$ SELECT 'example_' || to_char($1,'YYYY_MM_DD'); $$ LANGUAGE sql; CREATE FUNCTION example_part_exists(DATE) RETURNS BOOLEAN IMMUTABLE STRICT AS $$ SELECT count(tablename) > 0 FROM pg_catalog.pg_tables WHERE tablename = (SELECT example_part_name($1)) LIMIT 1; $$ LANGUAGE sql; CREATE FUNCTION example_create_part(for_day DATE) RETURNS BOOLEAN STRICT AS $$ DECLARE tname char(18); BEGIN tname := example_part_name(for_day); EXECUTE ' CREATE TABLE ' || tname || ' ( CHECK (r >= ' || quote_literal(for_day) || ' AND r < ' || quote_literal((for_day + interval '1 day')::date) || ') ) INHERITS (example); '; EXECUTE ' INSERT INTO ' || tname || ' VALUES (' || quote_literal(for_day) || ',' || quote_literal((for_day + interval '1 days')::date) || '); INSERT INTO ' || tname || ' VALUES (' || quote_literal(for_day) || ',' || quote_literal((for_day + interval '2 days')::date) || ');'; RETURN true; EXCEPTION WHEN OTHERS THEN RAISE NOTICE 'failed to create table %', tname; RAISE NOTICE '%', SQLERRM; RETURN false; END; $$ LANGUAGE plpgsql; CREATE FUNCTION example_drop_part(partname CHAR(18)) RETURNS BOOLEAN STRICT AS $$ BEGIN EXECUTE 'DROP TABLE ' || partname || ';'; RETURN true; EXCEPTION WHEN OTHERS THEN RAISE NOTICE 'failed to drop table %', partname; RAISE NOTICE '%', SQLERRM; RETURN false; END; $$ LANGUAGE plpgsql; CREATE FUNCTION example(TIMESTAMP WITH TIME ZONE) RETURNS SETOF TIMESTAMP WITH TIME ZONE STRICT AS $$ DECLARE tname char(18); c RECORD; BEGIN tname := example_part_name($1::date); IF NOT example_part_exists($1::date) THEN IF NOT example_create_part($1::date) THEN RETURN; END IF; END IF; FOR c IN EXECUTE ' SELECT r, (p + ' || quote_literal(($1 - date($1))::interval) || '::interval)::timestamptz AS p FROM ' || quote_ident(tname) || ' WHERE r = ' || quote_literal($1::date) || ';' LOOP RETURN NEXT c.p; END LOOP; RETURN; END; $$ LANGUAGE plpgsql; -- works not: relation is still open CREATE FUNCTION example_clean1(keep_from DATE, keep_to DATE) RETURNS SETOF DATE STRICT AS $$ DECLARE tname char(18); c RECORD; success boolean; BEGIN IF keep_to < keep_from THEN RETURN; END IF; FOR c IN EXECUTE ' SELECT DISTINCT r, 0 as i FROM example WHERE r NOT BETWEEN ' || quote_literal(keep_from) || ' AND ' || quote_literal(keep_to) || ';' LOOP BEGIN EXECUTE 'SELECT example_drop_part(example_part_name(' || quote_literal(c.r) || '));' INTO success; EXCEPTION WHEN OTHERS THEN RAISE NOTICE 'failed to drop table %', example_part_name(c.r); RAISE NOTICE '%', SQLERRM; RETURN; END; IF success THEN RETURN NEXT c.r; END IF; END LOOP; RETURN; END; $$ LANGUAGE plpgsql; -- works not: relation is still open CREATE FUNCTION example_clean2(DATE, DATE) RETURNS SETOF DATE STRICT AS $$ SELECT CASE WHEN example_drop_part(example_part_name(c.r)) THEN c.r END FROM (SELECT DISTINCT r FROM example WHERE r NOT BETWEEN $1 AND $2) c(r); $$ LANGUAGE sql; -- works CREATE OR REPLACE FUNCTION example_clean3(keep_from DATE, keep_to DATE) RETURNS SETOF DATE STRICT AS $$ DECLARE c RECORD; BEGIN IF keep_to < keep_from THEN RETURN; END IF; BEGIN EXECUTE ' CREATE TEMP TABLE tmp_example_clean AS SELECT DISTINCT r FROM example WHERE r NOT BETWEEN ' || quote_literal(keep_from) || ' AND ' || quote_literal(keep_to) || ';'; EXCEPTION WHEN OTHERS THEN RAISE NOTICE 'failed to create temp table tmp_example_clean'; RAISE NOTICE '%', SQLERRM; RETURN; END; FOR c IN EXECUTE ' SELECT t.r, example_drop_part(example_part_name(t.r)) AS success FROM tmp_example_clean t(r)' LOOP IF c.success THEN RETURN NEXT c.r; ELSE RETURN; END IF; END LOOP; BEGIN EXECUTE 'DROP TABLE tmp_example_clean;'; EXCEPTION WHEN OTHERS THEN RAISE NOTICE 'failed to drop table tmp_example_clean'; RAISE NOTICE '%', SQLERRM; RETURN; END; RETURN; END; $$ LANGUAGE plpgsql; -- create 3 parts with data select * from example((current_date - interval '1 days')::date); select * from example(current_date); select * from example((current_date + interval '1 days')::date); -- try to drop 2 parts select * from example_clean1(current_date, current_date); select * from example_clean2(current_date, current_date); -- drop all parts select * from example_clean3((current_date + interval '2 days')::date, (current_date + interval '2 days')::date); drop function example_clean1(date, date); drop function example_clean2(date, date); drop function example_clean3(date, date); drop function example(timestamptz); drop function example_drop_part(char(18)); drop function example_create_part(date); drop function example_part_exists(date); drop function example_part_name(date); drop table example;