Skip to content

Instantly share code, notes, and snippets.

@CacheControl
Last active May 20, 2019 22:54
Show Gist options
  • Save CacheControl/1705b92e95fdf4af9bd7 to your computer and use it in GitHub Desktop.
Save CacheControl/1705b92e95fdf4af9bd7 to your computer and use it in GitHub Desktop.
Postgresql Table Partitioning Function
CREATE OR REPLACE FUNCTION
public.insert_comment()
RETURNS TRIGGER AS
$BODY$
DECLARE
_start_dt text;
_end_dt text;
_table_name text;
BEGIN
IF NEW.id IS NULL THEN
NEW.id := uuid_generate_v4(); -- or using auto incrementing: NEW.id := nextval('comments_id_seq');
END IF;
-- determine table name the insert should be routed to
_table_name := 'comments_' || to_char(NEW."created_at", 'YYYY_MM');
-- Does the partition table already exist?
PERFORM 1
FROM pg_catalog.pg_class c
JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
WHERE c.relkind = 'r'
AND c.relname = _table_name
AND n.nspname = 'public';
-- If not, create it before continuing with the insert
IF NOT FOUND THEN
-- start_dt determines the earliest records allowed in the table, using the beginning of the month e.g. >= 2016-01-01 00:00:00
-- end_dt determines the oldest records allowed in the table, using the beginning of the next month e.g. < 2016-02-01 :0000:00
_start_dt := to_char(date_trunc('month', NEW."created_at"), 'YYYY-MM-DD');
_end_dt:=_start_dt::timestamp + INTERVAL '1 month';
EXECUTE 'CREATE TABLE public.' || quote_ident(_table_name) || ' (
CHECK ( "created_at" >= ' || quote_literal(_start_dt) || '
AND "created_at" < ' || quote_literal(_end_dt) || ')
) INHERITS (public.comments_base)';
-- Indexes are not inherited from the parent
EXECUTE 'CREATE INDEX ' || quote_ident(_table_name||'_created_at_idx') || ' ON public.' || quote_ident(_table_name) || ' (created_at)';
-- Set table permissions
EXECUTE 'ALTER TABLE public.' || quote_ident(_table_name) || ' OWNER TO ' || quote_ident(current_user);
EXECUTE 'GRANT ALL ON TABLE public.' || quote_ident(_table_name) || ' TO ' || quote_ident(current_user);
END IF;
EXECUTE 'INSERT INTO public.' || quote_ident(_table_name) || ' VALUES ($1.*) RETURNING *' USING NEW;
RETURN NEW; -- Allows RETURNING to work
END;
$BODY$
LANGUAGE plpgsql;
-- Intercept the INSERT, relying on the insert_comment() function to route the row to the appropriate table
CREATE TRIGGER comments_insert_trigger
INSTEAD OF INSERT ON public.comments
FOR EACH ROW EXECUTE PROCEDURE insert_comment();
@MirkoRossini
Copy link

Looks to me like you would run into a race condition unless ran with "serializable" transaction isolation, as the pg_catalog table is not locked during a transaction with a DDL change.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment