CREATE TABLE tab(id bigint GENERATED ALWAYS AS IDENTITY,ts timestamp NOT NULL,data text) PARTITION BY LIST ((ts::date));CREATE TABLE tab_def PARTITION OF tab DEFAULT;
cat > /tmp/create_part.sh <<EOFdateStr=\\$(date -d '+1 days' +%Y%m%d);psql -c "CREATE TABLE tab_\\$dateStr (LIKE tab INCLUDING INDEXES); ALTER TABLE tab ATTACH PARTITION tab_\\$dateStr FOR VALUES IN ('\\$dateStr')";EOF(crontab -l 2>/dev/null; echo "0 14 * * * bash /tmp/create_part.sh ") | crontab -
CREATE OR REPLACE FUNCTION create_tab_part() RETURNS integerLANGUAGE plpgsql AS$$DECLAREdateStr varchar;BEGINSELECT to_char(DATE 'tomorrow', 'YYYYMMDD') INTO dateStr;EXECUTEformat('CREATE TABLE tab_%s (LIKE tab INCLUDING INDEXES)', dateStr);EXECUTEformat('ALTER TABLE tab ATTACH PARTITION tab_%s FOR VALUES IN (%L)', dateStr, dateStr);RETURN 1;END;$$;CREATE EXTENSION pg_cron;SELECT cron.schedule('0 14 * * *', $$SELECT create_tab_part();$$);
CREATE EXTENSION pg_partman;SELECT partman.create_parent(p_parent_table => 'public.tab',p_control => 'ts',p_type => 'native',p_interval=> 'daily',p_premake => 1);
ERROR: "tab" is a partitioned tableDETAIL: Partitioned tables cannot have BEFORE / FOR EACH ROW triggers.
ERROR: cannot CREATE TABLE .. PARTITION OF "tab"because it is being used by active queries in this sessionOrERROR: cannot ALTER TABLE "tab"because it is being used by active queries in this session
CREATE FUNCTION part_trig() RETURNS triggerLANGUAGE plpgsql AS$$BEGINBEGIN/* try to create a table for the new partition */EXECUTEformat('CREATE TABLE %I (LIKE tab INCLUDING INDEXES)', 'tab_' || to_char(NEW.ts, 'YYYYMMDD'));/** tell listener to attach the partition* (only if a new table was created)*/EXECUTEformat('NOTIFY tab, %L', to_char(NEW.ts, 'YYYYMMDD'));EXCEPTIONWHEN duplicate_table THENNULL; -- ignoreEND;/* insert into the new partition */EXECUTEformat('INSERT INTO %I VALUES ($1.*)', 'tab_' || to_char(NEW.ts, 'YYYYMMDD'))USING NEW;/* skip insert into the partitioned table */RETURN NULL;END;$$;CREATE TRIGGER part_trigBEFORE INSERTON TABFOR EACH ROWWHEN (pg_trigger_depth() < 1)EXECUTE FUNCTION part_trig();Code (implementing LISTEN and ATTACH for child tables)#!/usr/bin/env python3.9# encoding:utf8import asyncioimport psycopg2from psycopg2.extensions import ISOLATION_LEVEL_AUTOCOMMITconn = psycopg2.connect('application_name=listener')conn.set_isolation_level(ISOLATION_LEVEL_AUTOCOMMIT)cursor = conn.cursor()cursor.execute(f'LISTEN tab;')def attach_partition(table, date):with conn.cursor() as cs:cs.execute('ALTER TABLE "%s" ATTACH PARTITION "%s_%s" FOR VALUES IN (\\'%s\\')' % (table, table, date, date))def handle_notify():conn.poll()for notify in conn.notifies:print(notify.payload)attach_partition(notify.channel, notify.payload)conn.notifies.clear()loop = asyncio.get_event_loop()loop.add_reader(conn, handle_notify)loop.run_forever()
Scenario | Version | Implementation | Need of System Scheduler or Extension Required | Need of Additional Connection Mechanism Required | Cost |
Scheduled partition creation | PostgreSQL 10 | Easy | Yes | No | High |
On-demand real-time partition creation | PostgreSQL 13 or later | Complicated | No | Yes | Low |
Was this page helpful?