diff --git a/src/exchangedb/0002-denomination_revocations.sql b/src/exchangedb/0002-denomination_revocations.sql index 57668b358..96e13cd15 100644 --- a/src/exchangedb/0002-denomination_revocations.sql +++ b/src/exchangedb/0002-denomination_revocations.sql @@ -21,17 +21,3 @@ CREATE TABLE IF NOT EXISTS denomination_revocations ); COMMENT ON TABLE denomination_revocations IS 'remembering which denomination keys have been revoked'; - - -INSERT INTO exchange_tables - (name - ,version - ,action - ,partitioned - ,by_range) - VALUES - ('denomination_revocations' - ,'exchange-0002' - ,'create' - ,FALSE - ,FALSE); diff --git a/src/exchangedb/0002-denominations.sql b/src/exchangedb/0002-denominations.sql index a18a752b6..d468a3875 100644 --- a/src/exchangedb/0002-denominations.sql +++ b/src/exchangedb/0002-denominations.sql @@ -14,13 +14,7 @@ -- TALER; see the file COPYING. If not, see -- -CREATE OR REPLACE FUNCTION create_table_denominations() -RETURNS VOID -LANGUAGE plpgsql -AS $$ -BEGIN - -CREATE TABLE IF NOT EXISTS denominations +CREATE TABLE denominations (denominations_serial BIGINT GENERATED BY DEFAULT AS IDENTITY UNIQUE ,denom_pub_hash BYTEA PRIMARY KEY CHECK (LENGTH(denom_pub_hash)=64) ,denom_type INT4 NOT NULL DEFAULT (1) -- 1 == RSA (for now, remove default later!) @@ -51,22 +45,6 @@ COMMENT ON COLUMN denominations.age_mask COMMENT ON COLUMN denominations.denominations_serial IS 'needed for exchange-auditor replication logic'; -CREATE INDEX IF NOT EXISTS denominations_by_expire_legal_index +CREATE INDEX denominations_by_expire_legal_index ON denominations (expire_legal); - -END -$$; - -INSERT INTO exchange_tables - (name - ,version - ,action - ,partitioned - ,by_range) - VALUES - ('denominations' - ,'exchange-0002' - ,'create' - ,FALSE - ,FALSE); diff --git a/src/exchangedb/0002-deposits.sql b/src/exchangedb/0002-deposits.sql index 874b33ccc..2be51903a 100644 --- a/src/exchangedb/0002-deposits.sql +++ b/src/exchangedb/0002-deposits.sql @@ -49,7 +49,7 @@ BEGIN ,partition_suffix ); PERFORM comment_partitioned_table( - 'Deposits we have received and for which we need to make (aggregate) wire transfers (and manage refunds).'; + 'Deposits we have received and for which we need to make (aggregate) wire transfers (and manage refunds).' ,table_name ,partition_suffix ); diff --git a/src/exchangedb/0002-history_requests.sql b/src/exchangedb/0002-history_requests.sql index 978dea640..a8dbeb6a7 100644 --- a/src/exchangedb/0002-history_requests.sql +++ b/src/exchangedb/0002-history_requests.sql @@ -15,7 +15,7 @@ -- -CREATE OR create_table_history_requests( +CREATE FUNCTION create_table_history_requests( IN shard_suffix VARCHAR DEFAULT NULL ) RETURNS VOID @@ -64,7 +64,7 @@ BEGIN END $$; -CREATE OR foreign_table_history_requests() +CREATE FUNCTION foreign_table_history_requests() RETURNS VOID LANGUAGE plpgsql AS $$ diff --git a/src/exchangedb/0002-refresh_revealed_coins.sql b/src/exchangedb/0002-refresh_revealed_coins.sql index a7d4d4395..998b0dc94 100644 --- a/src/exchangedb/0002-refresh_revealed_coins.sql +++ b/src/exchangedb/0002-refresh_revealed_coins.sql @@ -39,48 +39,48 @@ BEGIN ,'PARTITION BY HASH (melt_serial_id)' ,shard_suffix ); - PEFORM comment_partitioned_table( + PERFORM comment_partitioned_table( 'Revelations about the new coins that are to be created during a melting session.' ,table_name ,shard_suffix ); - PEFORM comment_partitioned_column( + PERFORM comment_partitioned_column( 'needed for exchange-auditor replication logic' ,'rrc_serial' ,table_name ,shard_suffix ); - PEFORM comment_partitioned_column( + PERFORM comment_partitioned_column( 'Identifies the refresh commitment (rc) of the melt operation.' ,'melt_serial_id' ,table_name ,shard_suffix ); - PEFORM comment_partitioned_column( + PERFORM comment_partitioned_column( 'index of the fresh coin being created (one melt operation may result in multiple fresh coins)' ,'freshcoin_index' ,table_name ,shard_suffix ); - PEFORM comment_partitioned_column( + PERFORM comment_partitioned_column( 'envelope of the new coin to be signed' ,'coin_ev' ,table_name ,shard_suffix ); - PEFORM comment_partitioned_column( + PERFORM comment_partitioned_column( 'exchange contributed values in the creation of the fresh coin (see /csr)' ,'ewv' ,table_name ,shard_suffix ); - PEFORM comment_partitioned_column( + PERFORM comment_partitioned_column( 'hash of the envelope of the new coin to be signed (for lookups)' ,'h_coin_ev' ,table_name ,shard_suffix ); - PEFORM comment_partitioned_column( + PERFORM comment_partitioned_column( 'exchange signature over the envelope' ,'ev_sig' ,table_name diff --git a/src/exchangedb/0002-wads_in.sql b/src/exchangedb/0002-wads_in.sql index dbbb02a7d..013b16350 100644 --- a/src/exchangedb/0002-wads_in.sql +++ b/src/exchangedb/0002-wads_in.sql @@ -15,7 +15,7 @@ -- CREATE FUNCTION create_table_wads_in( - IN shard_suffix VARCHAR DEFAULT NULL + IN partition_suffix VARCHAR DEFAULT NULL ) RETURNS VOID LANGUAGE plpgsql @@ -35,36 +35,36 @@ BEGIN ') %s ;' ,table_name ,'PARTITION BY HASH (wad_id)' - ,shard_suffix + ,partition_suffix ); PERFORM comment_partitioned_table( 'Incoming exchange-to-exchange wad wire transfers' ,table_name - ,shard_suffix + ,partition_suffix ); PERFORM comment_partitioned_column( 'Unique identifier of the wad, part of the wire transfer subject' ,'wad_id' ,table_name - ,shard_suffix + ,partition_suffix ); PERFORM comment_partitioned_column( 'Base URL of the originating URL, also part of the wire transfer subject' ,'origin_exchange_url' ,table_name - ,shard_suffix + ,partition_suffix ); PERFORM comment_partitioned_column( 'Actual amount that was received by our exchange' ,'amount_val' ,table_name - ,shard_suffix + ,partition_suffix ); PERFORM comment_partitioned_column( 'Time when the wad was received' ,'arrival_time' ,table_name - ,shard_suffix + ,partition_suffix ); END $$; diff --git a/src/exchangedb/0002-wire_targets.sql b/src/exchangedb/0002-wire_targets.sql index 08bc468d3..afb9197af 100644 --- a/src/exchangedb/0002-wire_targets.sql +++ b/src/exchangedb/0002-wire_targets.sql @@ -14,7 +14,7 @@ -- TALER; see the file COPYING. If not, see -- -CREATE OR REPLACE FUNCTION create_table_wire_targets( +CREATE FUNCTION create_table_wire_targets( IN shard_suffix VARCHAR DEFAULT NULL ) RETURNS VOID @@ -22,7 +22,7 @@ LANGUAGE plpgsql AS $$ BEGIN PERFORM create_partitioned_table( - 'CREATE TABLE IF NOT EXISTS %I' + 'CREATE TABLE %I' '(wire_target_serial_id BIGINT GENERATED BY DEFAULT AS IDENTITY' ',wire_target_h_payto BYTEA PRIMARY KEY CHECK (LENGTH(wire_target_h_payto)=32)' ',payto_uri VARCHAR NOT NULL' @@ -47,12 +47,10 @@ BEGIN ,'wire_target_h_payto' ,shard_suffix ); - -END -$$; +END $$; -CREATE OR REPLACE FUNCTION constrain_table_wire_targets( +CREATE FUNCTION constrain_table_wire_targets( IN partition_suffix VARCHAR ) RETURNS void diff --git a/src/exchangedb/drop.sql b/src/exchangedb/drop.sql index 4a4dafb1e..ff383d743 100644 --- a/src/exchangedb/drop.sql +++ b/src/exchangedb/drop.sql @@ -19,6 +19,7 @@ BEGIN; SELECT _v.unregister_patch('exchange-0001'); +SELECT _v.unregister_patch('exchange-0002'); DROP SCHEMA exchange CASCADE; diff --git a/src/exchangedb/exchange-0001.sql b/src/exchangedb/exchange-0001.sql index 208e81965..fad27adda 100644 --- a/src/exchangedb/exchange-0001.sql +++ b/src/exchangedb/exchange-0001.sql @@ -14,9 +14,13 @@ -- TALER; see the file COPYING. If not, see -- --- Everything in one big transaction BEGIN; +SELECT _v.register_patch('exchange-0001', NULL, NULL); + +CREATE SCHEMA exchange; +COMMENT ON SCHEMA exchange IS 'taler-exchange data'; + SET search_path TO exchange; --------------------------------------------------------------------------- @@ -38,7 +42,7 @@ COMMENT ON COLUMN exchange_tables.name COMMENT ON COLUMN exchange_tables.version IS 'Version of the DB in which the given action happened'; COMMENT ON COLUMN exchange_tables.action - IS 'Action to take on the table (e.g. create, alter, constrain, foreign, or drop). Create, alter and drop are done for master and each partition; constrain is only for partitions or for master if there are no partitions; master only on master (takes no argument); foreign only on master if there are no partitions.'; + IS 'Action to take on the table (e.g. create, constrain, foreign, or drop). Create is done for the master table and each partition; constrain is only for partitions or for master if there are no partitions; master only on master (takes no argument); foreign only on master if there are no partitions.'; COMMENT ON COLUMN exchange_tables.partitioned IS 'TRUE if the table is partitioned'; COMMENT ON COLUMN exchange_tables.by_range @@ -48,51 +52,58 @@ COMMENT ON COLUMN exchange_tables.finished CREATE FUNCTION create_partitioned_table( - IN table_definition VARCHAR - ,IN table_name VARCHAR - ,IN main_table_partition_str VARCHAR -- Used only when it is the main table - we do not partition shard tables - ,IN shard_suffix VARCHAR DEFAULT NULL + IN table_definition VARCHAR -- SQL template for table creation + ,IN table_name VARCHAR -- base name of the table + ,IN main_table_partition_str VARCHAR -- declaration for how to partition the table + ,IN partition_suffix VARCHAR DEFAULT NULL -- NULL: no partitioning, 0: yes partitioning, no sharding, >0: sharding ) RETURNS VOID LANGUAGE plpgsql AS $$ BEGIN - IF shard_suffix IS NOT NULL THEN - table_name=table_name || '_' || shard_suffix; + IF partition_suffix IS NULL + THEN + -- no partitioning, disable option main_table_partition_str = ''; + ELSE + IF partition_suffix > 0 + THEN + -- sharding, add shard name + table_name=table_name || '_' || partition_suffix; + END IF; END IF; EXECUTE FORMAT( table_definition, table_name, main_table_partition_str ); -END -$$; +END $$; COMMENT ON FUNCTION create_partitioned_table - IS 'Generic function to create a table that is partitioned.'; + IS 'Generic function to create a table that is partitioned or sharded.'; CREATE FUNCTION comment_partitioned_table( IN table_comment VARCHAR ,IN table_name VARCHAR - ,IN shard_suffix VARCHAR DEFAULT NULL + ,IN partition_suffix VARCHAR DEFAULT NULL ) RETURNS VOID LANGUAGE plpgsql AS $$ BEGIN - IF shard_suffix IS NOT NULL THEN - table_name=table_name || '_' || shard_suffix; - main_table_partition_str = ''; + IF ( (partition_suffix IS NOT NULL) AND + (partition_suffix > 0) ) + THEN + -- sharding, add shard name + table_name=table_name || '_' || partition_suffix; END IF; EXECUTE FORMAT( - COMMENT ON TABLE %s IS '%s' + 'COMMENT ON TABLE %s IS %s' ,table_name - ,table_comment + ,quote_literal(table_comment) ); -END -$$; +END $$; COMMENT ON FUNCTION comment_partitioned_table IS 'Generic function to create a comment on table that is partitioned.'; @@ -102,34 +113,37 @@ CREATE FUNCTION comment_partitioned_column( IN table_comment VARCHAR ,IN column_name VARCHAR ,IN table_name VARCHAR - ,IN shard_suffix VARCHAR DEFAULT NULL + ,IN partition_suffix VARCHAR DEFAULT NULL ) RETURNS VOID LANGUAGE plpgsql AS $$ BEGIN - IF shard_suffix IS NOT NULL THEN - table_name=table_name || '_' || shard_suffix; - main_table_partition_str = ''; + IF ( (partition_suffix IS NOT NULL) AND + (partition_suffix > 0) ) + THEN + -- sharding, add shard name + table_name=table_name || '_' || partition_suffix; END IF; EXECUTE FORMAT( - COMMENT ON COLUMN %s.%s IS '%s' + 'COMMENT ON COLUMN %s.%s IS %s' ,table_name ,column_name - ,table_comment + ,quote_literal(table_comment) ); -END -$$; +END $$; COMMENT ON FUNCTION comment_partitioned_column IS 'Generic function to create a comment on column of a table that is partitioned.'; +--------------------------------------------------------------------------- +-- Main DB setup loop +--------------------------------------------------------------------------- CREATE FUNCTION create_tables( num_partitions INTEGER --- FIXME: not implemented like this, but likely good: -- NULL: no partitions, add foreign constraints -- 0: no partitions, no foreign constraints -- 1: only 1 default partition @@ -139,176 +153,146 @@ CREATE FUNCTION create_tables( LANGUAGE plpgsql AS $$ DECLARE - -- FIXME: use only ONE cursor and then switch on action! tc CURSOR FOR SELECT table_serial_id ,name ,action + ,partitioned ,by_range FROM exchange_tables WHERE NOT finished - AND partitioned - AND (action='create' - OR action='alter' - OR action='drop') - ORDER BY table_serial_id ASC; -DECLARE - ta CURSOR FOR - SELECT table_serial_id - ,name - ,action - ,by_range - FROM exchange_tables - WHERE NOT finished - AND partitioned - AND action='constrain' - ORDER BY table_serial_id ASC; -DECLARE - tf CURSOR FOR - SELECT table_serial_id - ,name - ,action - ,by_range - FROM exchange_tables - WHERE NOT finished - AND partitioned - AND action='foreign' - ORDER BY table_serial_id ASC; -DECLARE - tm CURSOR FOR - SELECT table_serial_id - ,name - ,action - ,by_range - FROM exchange_tables - WHERE NOT finished - AND partitioned - AND action='master' ORDER BY table_serial_id ASC; BEGIN - - -- run create/alter/drop actions FOR rec IN tc LOOP - -- First create the master table, either - -- completely unpartitioned, or with one - -- master and the 'default' partition - IF IS NULL num_partitions + CASE rec.action + -- "create" actions apply to master and partitions + WHEN "create" THEN - -- No partitions at all. - EXECUTE FORMAT( - 'PERFORM %s_table_%s (%s)'::text - ,rec.action - ,rec.name - ,NULL - ); - ELSE - -- One default partition only. - EXECUTE FORMAT( - 'PERFORM %s_table_%s (%s)'::text - ,rec.action - ,rec.name - ,0 - ); - - IF NOT IS NULL num_partitions - THEN - IF rec.by_range + IF (rec.partitioned AND + (num_partitions IS NOT NULL)) THEN - -- range partitions (only create default) - -- Create default partition. + -- Create master table with partitioning. EXECUTE FORMAT( - 'CREATE TABLE %s_default PARTITION OF %s DEFAULT' - ,rec.name + 'PERFORM %s_table_%s (%s)'::text + ,rec.action ,rec.name + ,0 ); - ELSE - -- hash partitions - IF 0=num_partitions + IF (rec.by_range OR + (num_partitions = 0)) THEN -- Create default partition. - EXECUTE FORMAT( - 'CREATE TABLE IF NOT EXISTS %s_default PARTITION OF %s FOR VALUES WITH (MODULUS 1, REMAINDER 0)' - ,rec.name - ,rec.name - ); - END IF - FOR i IN 1..num_partitions LOOP - -- Create num_partitions - EXECUTE FORMAT( - 'CREATE TABLE IF NOT EXISTS %I' - ' PARTITION OF %I' - ' FOR VALUES WITH (MODULUS %s, REMAINDER %s)' - ,rec.name || '_' || i - ,rec.name - ,num_partitions - ,i-1 - ); - END LOOP; + IF (rec.by_range) + THEN + -- Range partition + EXECUTE FORMAT( + 'CREATE TABLE IF NOT EXISTS %s_default' + ' PARTITION OF %s' + ' FOR DEFAULT' + ,rec.name + ,rec.name + ); + ELSE + -- Hash partition + EXECUTE FORMAT( + 'CREATE TABLE IF NOT EXISTS %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 IF NOT EXISTS %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( + 'PERFORM %s_table_%s (%s)'::text + ,rec.action + ,rec.name + ,NULL + ); END IF; - END IF; + -- Constrain action apply to master OR each partition + WHEN "constrain" + THEN + ASSERT rec.partitioned, 'constrain action only applies to partitioned tables'; + IF (num_partitions IS NULL) + THEN + -- Constrain master table + EXECUTE FORMAT( + 'PERFORM %s_table_%s (%s)'::text + ,rec.action + ,rec.name + ,NULL + ); + ELSE + IF (num_partitions = 0) + THEN + -- Constrain default table + EXECUTE FORMAT( + 'PERFORM %s_table_%s (%s)'::text + ,rec.action + ,rec.name + ,'default' + ); + ELSE + -- Constrain each partition + FOR i IN 1..num_partitions LOOP + EXECUTE FORMAT( + 'PERFORM %s_table_%s (%s)'::text + ,rec.action + ,rec.name + ,i + ); + END LOOP; + END IF; + END IF; + -- Foreign actions only apply if partitioning is off + WHEN "foreign" + THEN + IF (num_partitions IS NULL) + THEN + -- Only create master table. No partitions. + EXECUTE FORMAT( + 'PERFORM %s_table_%s (%s)'::text + ,rec.action + ,rec.name + ,NULL + ); + END IF; + WHEN "master" + THEN + EXECUTE FORMAT( + 'PERFORM %s_table_%s'::text + ,rec.action + ,rec.name + ); + ELSE + ASSERT FALSE, 'unsupported action type: ' || rec.action; + END CASE; -- END CASE (rec.action) + -- Mark as finished UPDATE exchange_tables SET finished=TRUE WHERE table_serial_id=rec.table_serial_id; END LOOP; -- create/alter/drop actions - - -- Run constrain actions - FOR rec IN ta - LOOP - IF IS NULL num_partitions - THEN - -- Constrain master - EXECUTE FORMAT( - 'PERFORM %s_table_%s (%s)'::text - ,rec.action - ,rec.name - ,NULL - ); - END IF - - IF 0=num_partitions - THEN - -- constrain default partition - EXECUTE FORMAT( - 'PERFORM %s_table_%s (%s)'::text - ,rec.action - ,rec.name - ,0 - ); - END IF - FOR i IN 1..num_partitions LOOP - -- constrain each partition - EXECUTE FORMAT( - 'PERFORM %s_table_%s (%s)'::text - ,rec.action - ,rec.name - ,i::varchar - ); - END LOOP; - UPDATE exchange_tables - SET finished=TRUE - WHERE table_serial_id=rec.table_serial_id; - END LOOP; - - -- run foreign actions - FOR rec IN tf - LOOP - IF IS NULL num_partitions - THEN - -- Add foreign constraints - EXECUTE FORMAT( - 'PERFORM %s_table_%s ()'::text - ,rec.action - ,rec.name - ); - END IF - UPDATE exchange_tables - SET finished=TRUE - WHERE table_serial_id=rec.table_serial_id; - END LOOP; - -END -$$; +END $$; COMMENT ON FUNCTION create_tables - IS 'Creates all tables for the given number of partitions that need creating.'; + IS 'Creates all tables for the given number of partitions that need creating. Does NOT support sharding.'; + + +COMMIT; diff --git a/src/exchangedb/exchange-0002.sql.in b/src/exchangedb/exchange-0002.sql.in index 8ecfc9604..1d28f63a4 100644 --- a/src/exchangedb/exchange-0002.sql.in +++ b/src/exchangedb/exchange-0002.sql.in @@ -14,14 +14,9 @@ -- TALER; see the file COPYING. If not, see -- --- Everything in one big transaction BEGIN; --- Check patch versioning is in place. SELECT _v.register_patch('exchange-0002', NULL, NULL); - --------------------- Schema ---------------------------- - SET search_path TO exchange; #include "0002-denominations.sql" diff --git a/src/exchangedb/exchange-0003.sql.in b/src/exchangedb/exchange-0003.sql.in index ba77d4d12..7f0a9ef94 100644 --- a/src/exchangedb/exchange-0003.sql.in +++ b/src/exchangedb/exchange-0003.sql.in @@ -14,20 +14,11 @@ -- TALER; see the file COPYING. If not, see -- --- Everything in one big transaction BEGIN; --- Check patch versioning is in place. SELECT _v.register_patch('exchange-0003', NULL, NULL); - --------------------- Schema ---------------------------- - -CREATE SCHEMA exchange; -COMMENT ON SCHEMA exchange IS 'taler-exchange data'; - SET search_path TO exchange; - #include "0003-purse_actions.sql" #include "0003-purse_deletion.sql" diff --git a/src/exchangedb/procedures.sql b/src/exchangedb/procedures.sql index 9783d2b13..4bd24dde1 100644 --- a/src/exchangedb/procedures.sql +++ b/src/exchangedb/procedures.sql @@ -14,15 +14,10 @@ -- TALER; see the file COPYING. If not, see -- --- Everything in one big transaction BEGIN; SET search_path TO exchange; ---------------------------------------------------------------------------- --- Stored procedures ---------------------------------------------------------------------------- - CREATE OR REPLACE FUNCTION exchange_do_withdraw( IN cs_nonce BYTEA,