syncdb_update_block_TR.sql (4277B)
1 -- 2 -- This file is part of TALER 3 -- Copyright (C) 2026 Taler Systems SA 4 -- 5 -- TALER is free software; you can redistribute it and/or modify it under the 6 -- terms of the GNU General Public License as published by the Free Software 7 -- Foundation; either version 3, or (at your option) any later version. 8 -- 9 -- TALER is distributed in the hope that it will be useful, but WITHOUT ANY 10 -- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 11 -- A PARTICULAR PURPOSE. See the GNU General Public License for more details. 12 -- 13 -- You should have received a copy of the GNU General Public License along with 14 -- TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> 15 -- 16 17 DROP FUNCTION IF EXISTS sync_do_update_block; 18 CREATE FUNCTION sync_do_update_block ( 19 IN in_account_pub BYTEA, 20 IN in_block_nonce BYTEA, 21 IN in_prev_nonce BYTEA, 22 IN in_next_nonce BYTEA, 23 IN in_old_data_hash BYTEA, 24 IN in_new_data_hash BYTEA, 25 IN in_data_blob BYTEA, 26 IN ina_object_uids BYTEA[], 27 IN ina_object_incs INT2[], 28 IN in_upload_sig BYTEA, 29 IN in_refs_hash BYTEA, 30 OUT out_account_missing BOOLEAN, 31 OUT out_block_missing BOOLEAN, 32 OUT out_outdated_write BOOLEAN, 33 OUT out_negative_refcount BOOLEAN, 34 OUT out_refs_missing BOOLEAN, 35 OUT out_updated BOOLEAN, 36 OUT out_no_change BOOLEAN, 37 OUT out_refs_updated BOOLEAN) 38 LANGUAGE plpgsql 39 AS $$ 40 DECLARE 41 my_prev_nonce BYTEA; 42 my_next_nonce BYTEA; 43 my_existing_hash BYTEA; 44 BEGIN 45 out_account_missing := FALSE; 46 out_block_missing := FALSE; 47 out_outdated_write := FALSE; 48 out_negative_refcount := FALSE; 49 out_refs_missing := FALSE; 50 out_updated := FALSE; 51 out_no_change := FALSE; 52 out_refs_updated := FALSE; 53 54 -- Check if account exists and has valid payment 55 SELECT a.out_account_missing 56 INTO out_account_missing 57 FROM sync_check_account_payment(in_account_pub) AS a; 58 IF out_account_missing 59 THEN 60 RETURN; 61 END IF; 62 63 -- Check if old block exists 64 SELECT prev_nonce 65 ,next_nonce 66 ,block_hash 67 INTO my_prev_nonce 68 ,my_next_nonce 69 ,my_existing_hash 70 FROM blocks 71 WHERE account_pub = in_account_pub 72 AND nonce = in_block_nonce; 73 74 IF NOT FOUND 75 THEN 76 out_block_missing := TRUE; 77 RETURN; 78 END IF; 79 80 -- Is the new hash identical? 81 IF my_existing_hash = in_new_data_hash 82 THEN 83 out_no_change := TRUE; 84 RETURN; 85 END IF; 86 87 -- Does existing hash matches expected old hash? 88 -- Does previous and next nonces match expectations? 89 IF my_existing_hash IS DISTINCT FROM in_old_data_hash OR 90 my_prev_nonce IS DISTINCT FROM in_prev_nonce OR 91 my_next_nonce IS DISTINCT FROM in_next_nonce 92 THEN 93 out_outdated_write := TRUE; 94 RETURN; 95 END IF; 96 97 -- Check referenced objects exist and no increment underflows, 98 -- before we touch anything 99 SELECT c.out_negative_refcount 100 ,c.out_refs_missing 101 INTO out_negative_refcount 102 ,out_refs_missing 103 FROM sync_check_object_refcounts_pre(in_account_pub, 104 ina_object_uids, 105 ina_object_incs) AS c; 106 IF out_negative_refcount OR out_refs_missing 107 THEN 108 RETURN; 109 END IF; 110 111 -- Try to update existing block in place; the upload signature and 112 -- its signed context are stored with the block 113 UPDATE blocks 114 SET block_hash = in_new_data_hash 115 ,data_blob = in_data_blob 116 ,upload_sig = in_upload_sig 117 ,old_hash = in_old_data_hash 118 ,refs_hash = in_refs_hash 119 WHERE account_pub = in_account_pub 120 AND nonce = in_block_nonce; 121 122 IF NOT FOUND 123 THEN 124 RAISE EXCEPTION 'block % vanished for account %', 125 in_block_nonce, in_account_pub; 126 END IF; 127 128 -- Update object refcounts and delete unreferenced objects; the 129 -- pre-check above means this can only fail on a concurrent change 130 SELECT u.out_negative_refcount 131 ,u.out_refs_updated 132 INTO out_negative_refcount 133 ,out_refs_updated 134 FROM sync_update_object_refcounts(in_account_pub, 135 ina_object_uids, 136 ina_object_incs) AS u; 137 IF NOT out_refs_updated 138 THEN 139 RAISE EXCEPTION 'object refcounts changed concurrently for account %', 140 in_account_pub; 141 END IF; 142 143 out_updated := TRUE; 144 END $$;