syncdb_object_refcounts.sql (4205B)
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 -- 18 -- Helper to update object refcounts for a given account and parallel 19 -- arrays of object uuids and increments. 20 -- 21 -- Increments are aggregated per UID before they are applied, so the 22 -- outcome does not depend on the order UIDs appear in the arrays, and 23 -- agrees with sync_check_object_refcounts_pre() by construction. 24 -- 25 DROP FUNCTION IF EXISTS sync_update_object_refcounts; 26 CREATE FUNCTION sync_update_object_refcounts( 27 IN in_account_pub BYTEA, 28 IN ina_object_uids BYTEA[], 29 IN ina_object_incs INT2[]) 30 RETURNS TABLE ( 31 out_negative_refcount BOOLEAN, 32 out_refs_updated BOOLEAN) 33 LANGUAGE plpgsql 34 AS $$ 35 DECLARE 36 my_rec RECORD; 37 my_new_refcount INT8; 38 BEGIN 39 out_negative_refcount := FALSE; 40 out_refs_updated := TRUE; 41 42 IF array_length(ina_object_uids,1) IS DISTINCT FROM array_length(ina_object_incs,1) 43 THEN 44 RAISE EXCEPTION 'uuid and inc arrays must have same length'; 45 END IF; 46 47 IF COALESCE(array_length(ina_object_uids, 1), 0) = 0 48 THEN 49 RETURN NEXT; 50 RETURN; 51 END IF; 52 53 FOR my_rec IN 54 SELECT uid, SUM(inc)::INT8 AS total_inc 55 FROM unnest(ina_object_uids, ina_object_incs) AS t(uid, inc) 56 GROUP BY uid 57 LOOP 58 UPDATE objects o 59 SET refcount = o.refcount + my_rec.total_inc 60 WHERE o.uid = my_rec.uid 61 AND o.account_pub = in_account_pub 62 RETURNING o.refcount INTO my_new_refcount; 63 64 IF NOT FOUND 65 THEN 66 -- No such object for this account 67 out_refs_updated := FALSE; 68 RETURN NEXT; 69 RETURN; 70 END IF; 71 72 IF my_new_refcount < 0 73 THEN 74 out_negative_refcount := TRUE; 75 out_refs_updated := FALSE; 76 RETURN NEXT; 77 RETURN; 78 END IF; 79 80 -- Collect the object when its last reference goes away. A 81 -- freshly uploaded object also sits at refcount 0, hence the 82 -- decrement check. 83 IF (my_new_refcount = 0) AND 84 (my_rec.total_inc < 0) 85 THEN 86 DELETE FROM objects 87 WHERE uid = my_rec.uid 88 AND account_pub = in_account_pub; 89 END IF; 90 END LOOP; 91 92 RETURN NEXT; 93 RETURN; 94 END $$; 95 96 -- 97 -- Checks, without modifying anything, whether the given increments can 98 -- be applied: every referenced UID must exist for the account, and no 99 -- aggregated decrement may drive a refcount below zero. To be called 100 -- before any state is mutated. 101 -- 102 DROP FUNCTION IF EXISTS sync_check_object_refcounts_pre; 103 CREATE FUNCTION sync_check_object_refcounts_pre( 104 IN in_account_pub BYTEA, 105 IN ina_object_uids BYTEA[], 106 IN ina_object_incs INT2[]) 107 RETURNS TABLE ( 108 out_negative_refcount BOOLEAN, 109 out_refs_missing BOOLEAN) 110 LANGUAGE plpgsql 111 AS $$ 112 BEGIN 113 out_negative_refcount := FALSE; 114 out_refs_missing := FALSE; 115 116 IF array_length(ina_object_uids,1) IS DISTINCT FROM array_length(ina_object_incs,1) 117 THEN 118 RAISE EXCEPTION 'uuid and inc arrays must have same length'; 119 END IF; 120 121 IF COALESCE(array_length(ina_object_uids, 1), 0) = 0 122 THEN 123 RETURN NEXT; 124 RETURN; 125 END IF; 126 127 SELECT 128 bool_or(o.uid IS NOT NULL AND a.total_inc < 0 AND o.refcount < (- a.total_inc)) 129 ,bool_or(o.uid IS NULL) 130 INTO out_negative_refcount 131 ,out_refs_missing 132 FROM ( 133 SELECT uid, SUM(inc)::INT8 AS total_inc 134 FROM unnest(ina_object_uids, ina_object_incs) AS t(uid, inc) 135 GROUP BY uid 136 ) a 137 LEFT JOIN objects o 138 ON o.uid = a.uid 139 AND o.account_pub = in_account_pub; 140 141 out_negative_refcount := COALESCE(out_negative_refcount, FALSE); 142 out_refs_missing := COALESCE(out_refs_missing, FALSE); 143 RETURN NEXT; 144 RETURN; 145 END $$;