CREATE TABLE chunks ( id serial, relation_id oid not null, chunk_id int not null, PRIMARY KEY (id) ); /* Consistency Unique ID - CUID Format: - 41 bits for time in milliseconds (gives us 69 years) - 13 bits that represent chunk ID (allow to create 8192 chunks) - 10 bits that represent an auto-incrementing sequence, modulus 1024 (can be generated 1024 IDs, per chunk, per millisecond */ create type cuid as (created_at timestamp, chunk_id bigint, seq_id bigint); create or replace function next_id(chunk_id int, def_value text) returns bigint language 'plpgsql' as $$ declare result bigint; our_epoch bigint := 1451595600000; -- 2016 seq_id bigint; now_millis bigint; begin -- use def_value from base table (maybe will be better avoid this) execute format('select %s', def_value) into seq_id; select floor(extract(epoch from clock_timestamp()) * 1000) into now_millis; result := (now_millis - our_epoch) << 23; result := result | (chunk_id << 10); result := result | (seq_id); return result; end; $$; create or replace function unpack_cuid(id bigint) returns cuid language 'plpgsql' as $$ declare our_epoch bigint := 1451595600000; -- 2016 created_at timestamp; chunk_id bigint; seq_id bigint; begin select to_timestamp(((id >> 23) + our_epoch)* 0.001) into created_at; chunk_id = (id & ((1 << 23) - 1) ) >> 10; seq_id = id & ((1 << 10) - 1); return row(created_at, chunk_id, seq_id)::cuid; end; $$; create or replace function create_chunk(table_name text, chunk_id int) returns int language 'plpgsql' as $$ declare def_value text; query_tpl text := 'create table {table_name}_{chunk_id} ( like {table_name} including all, check( (unpack_cuid(id)).chunk_id = {chunk_id}) ); alter table {table_name}_{chunk_id} inherit {table_name}; alter table {table_name}_{chunk_id} alter column id set default next_id({chunk_id}, ''{def_value}'');'; begin if exists ( select * from chunks where chunks.relation_id = create_chunk.table_name::regclass and chunks.chunk_id = create_chunk.chunk_id ) then return null; end if; --- use default value from base table select column_default into def_value from information_schema.columns as is_columns where is_columns.table_name = create_chunk.table_name and column_name = 'id'; insert into chunks (relation_id, chunk_id) values(table_name::regclass, chunk_id); execute replace( replace( replace(query_tpl, '{table_name}', table_name), '{chunk_id}', trim(to_char(chunk_id, '000'))), '{def_value}', replace(def_value, '''', '''''')); return chunk_id; end; $$; create or replace function create_chunks( table_name text, from_number int, to_number int) returns int[] language 'plpgsql' as $$ declare result int[]; begin select array( select * from ( select create_chunk(table_name, num) as id from generate_series(from_number, to_number) as num ) as r where r.id is not null ) into result; return result; end; $$;