Last active
May 20, 2019 22:54
-
-
Save CacheControl/1705b92e95fdf4af9bd7 to your computer and use it in GitHub Desktop.
Postgresql Table Partitioning Function
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.