-- -- This file is part of TALER -- Copyright (C) 2014--2022 Taler Systems SA -- -- TALER is free software; you can redistribute it and/or modify it under the -- terms of the GNU General Public License as published by the Free Software -- Foundation; either version 3, or (at your option) any later version. -- -- TALER is distributed in the hope that it will be useful, but WITHOUT ANY -- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR -- A PARTICULAR PURPOSE. See the GNU General Public License for more details. -- -- You should have received a copy of the GNU General Public License along with -- TALER; see the file COPYING. If not, see -- --------------------------------------------------------------------------- -- Main DB setup loop --------------------------------------------------------------------------- CREATE OR REPLACE FUNCTION exchange_do_create_tables( num_partitions INTEGER -- NULL: no partitions, add foreign constraints -- 0: no partitions, no foreign constraints -- 1: only 1 default partition -- > 1: normal partitions ) RETURNS VOID LANGUAGE plpgsql AS $$ DECLARE tc CURSOR FOR SELECT table_serial_id ,name ,action ,partitioned ,by_range FROM exchange.exchange_tables WHERE NOT finished ORDER BY table_serial_id ASC; BEGIN FOR rec IN tc LOOP CASE rec.action -- "create" actions apply to master and partitions, providing the partition ID to the creation function (if any) WHEN 'create' THEN IF (rec.partitioned AND (num_partitions IS NOT NULL)) THEN -- Create master table with partitioning. EXECUTE FORMAT( 'SELECT exchange.create_table_%s (%s)'::text ,rec.name ,quote_literal('0') ); IF (rec.by_range OR (num_partitions = 0)) THEN -- Create default partition. IF (rec.by_range) THEN -- Range partition EXECUTE FORMAT( 'CREATE TABLE exchange.%s_default' ' PARTITION OF %s' ' DEFAULT' ,rec.name ,rec.name ); ELSE -- Hash partition EXECUTE FORMAT( 'CREATE TABLE exchange.%s_default' ' PARTITION OF %s' ' FOR VALUES WITH (MODULUS 1, REMAINDER 0)' ,rec.name ,rec.name ); END IF; ELSE FOR i IN 1..num_partitions LOOP -- Create num_partitions EXECUTE FORMAT( 'CREATE TABLE exchange.%I' ' PARTITION OF %I' ' FOR VALUES WITH (MODULUS %s, REMAINDER %s)' ,rec.name || '_' || i ,rec.name ,num_partitions ,i-1 ); END LOOP; END IF; ELSE -- Only create master table. No partitions. EXECUTE FORMAT( 'SELECT exchange.create_table_%s ()'::text ,rec.name ); END IF; EXECUTE FORMAT( 'DROP FUNCTION exchange.create_table_%s'::text ,rec.name ); -- "alter" actions apply to master and partitions (but are called without partition ID, as altering master applies to partitions for these); use when changing table layouts (adding or removing columns). WHEN 'alter' THEN -- Alter master table. EXECUTE FORMAT( 'SELECT exchange.alter_table_%s ()'::text ,rec.name ); EXECUTE FORMAT( 'DROP FUNCTION exchange.alter_table_%s'::text ,rec.name ); -- Constrain action apply to master OR each partition (but not on master if we have partitions); use to create (or remove) indices or constraints that apply to a partition and may not be aligned with the partition key WHEN 'constrain' THEN ASSERT rec.partitioned, 'constrain action only applies to partitioned tables'; IF (num_partitions IS NULL) THEN -- Constrain master table EXECUTE FORMAT( 'SELECT exchange.constrain_table_%s (NULL)'::text ,rec.name ); ELSE IF ( (num_partitions = 0) OR (rec.by_range) ) THEN -- Constrain default table EXECUTE FORMAT( 'SELECT exchange.constrain_table_%s (%s)'::text ,rec.name ,quote_literal('default') ); ELSE -- Constrain each partition FOR i IN 1..num_partitions LOOP EXECUTE FORMAT( 'SELECT exchange.constrain_table_%s (%s)'::text ,rec.name ,quote_literal(i) ); END LOOP; END IF; END IF; EXECUTE FORMAT( 'DROP FUNCTION exchange.constrain_table_%s'::text ,rec.name ); -- Foreign actions only apply if partitioning is off; use for foreign-key constraints that may span partitions WHEN 'foreign' THEN IF (num_partitions IS NULL) THEN -- Add foreign constraints EXECUTE FORMAT( 'SELECT exchange.foreign_table_%s (%s)'::text ,rec.name ,NULL ); END IF; EXECUTE FORMAT( 'DROP FUNCTION exchange.foreign_table_%s'::text ,rec.name ); -- "master" actions only apply to the master table WHEN 'master' THEN EXECUTE FORMAT( 'SELECT exchange.master_table_%s ()'::text ,rec.name ); EXECUTE FORMAT( 'DROP FUNCTION exchange.master_table_%s'::text ,rec.name ); ELSE ASSERT FALSE, 'unsupported action type: ' || rec.action; END CASE; -- END CASE (rec.action) -- Mark as finished UPDATE exchange.exchange_tables SET finished=TRUE WHERE table_serial_id=rec.table_serial_id; END LOOP; -- create/alter/drop actions END $$; COMMENT ON FUNCTION exchange_do_create_tables IS 'Creates all tables for the given number of partitions that need creating. Does NOT support sharding.';