]> git.evergreen-ils.org Git - working/Evergreen.git/blob - Open-ILS/src/sql/Pg/upgrade/1126.schema.vandelay-state-tracking.sql
LP1615805 No inputs after submit in patron search (AngularJS)
[working/Evergreen.git] / Open-ILS / src / sql / Pg / upgrade / 1126.schema.vandelay-state-tracking.sql
1 BEGIN;
2
3 SELECT evergreen.upgrade_deps_block_check('1126', :eg_version);
4
5 CREATE TABLE vandelay.session_tracker (
6     id          BIGSERIAL PRIMARY KEY,
7
8     -- string of characters (e.g. md5) used for linking trackers
9     -- of different actions into a series.  There can be multiple
10     -- session_keys of each action type, creating the opportunity
11     -- to link multiple action trackers into a single session.
12     session_key TEXT NOT NULL,
13
14     -- optional user-supplied name
15     name        TEXT NOT NULL, 
16
17     usr         INTEGER NOT NULL REFERENCES actor.usr(id)
18                 DEFERRABLE INITIALLY DEFERRED,
19
20     -- org unit can be derived from WS
21     workstation INTEGER NOT NULL REFERENCES actor.workstation(id)
22                 ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
23
24     -- bib/auth
25     record_type vandelay.bib_queue_queue_type NOT NULL DEFAULT 'bib',
26
27     -- Queue defines the source of the data, it does not necessarily
28     -- mean that an action is being performed against an entire queue.
29     -- E.g. some imports are misc. lists of record IDs, but they always 
30     -- come from one queue.
31     -- No foreign key -- could be auth or bib queue.
32     queue       BIGINT NOT NULL,
33
34     create_time TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
35     update_time TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
36
37     state       TEXT NOT NULL DEFAULT 'active',
38
39     action_type TEXT NOT NULL DEFAULT 'enqueue', -- import
40
41     -- total number of tasks to perform / loosely defined
42     -- could be # of recs to import or # of recs + # of copies 
43     -- depending on the import context
44     total_actions INTEGER NOT NULL DEFAULT 0,
45
46     -- total number of tasked performed so far
47     actions_performed INTEGER NOT NULL DEFAULT 0,
48
49     CONSTRAINT vand_tracker_valid_state 
50         CHECK (state IN ('active','error','complete')),
51
52     CONSTRAINT vand_tracker_valid_action_type
53         CHECK (action_type IN ('upload', 'enqueue', 'import'))
54 );
55
56
57 CREATE OR REPLACE FUNCTION actor.usr_merge( src_usr INT, dest_usr INT, del_addrs BOOLEAN, del_cards BOOLEAN, deactivate_cards BOOLEAN ) RETURNS VOID AS $$
58 DECLARE
59         suffix TEXT;
60         bucket_row RECORD;
61         picklist_row RECORD;
62         queue_row RECORD;
63         folder_row RECORD;
64 BEGIN
65
66     -- do some initial cleanup 
67     UPDATE actor.usr SET card = NULL WHERE id = src_usr;
68     UPDATE actor.usr SET mailing_address = NULL WHERE id = src_usr;
69     UPDATE actor.usr SET billing_address = NULL WHERE id = src_usr;
70
71     -- actor.*
72     IF del_cards THEN
73         DELETE FROM actor.card where usr = src_usr;
74     ELSE
75         IF deactivate_cards THEN
76             UPDATE actor.card SET active = 'f' WHERE usr = src_usr;
77         END IF;
78         UPDATE actor.card SET usr = dest_usr WHERE usr = src_usr;
79     END IF;
80
81
82     IF del_addrs THEN
83         DELETE FROM actor.usr_address WHERE usr = src_usr;
84     ELSE
85         UPDATE actor.usr_address SET usr = dest_usr WHERE usr = src_usr;
86     END IF;
87
88     UPDATE actor.usr_note SET usr = dest_usr WHERE usr = src_usr;
89     -- dupes are technically OK in actor.usr_standing_penalty, should manually delete them...
90     UPDATE actor.usr_standing_penalty SET usr = dest_usr WHERE usr = src_usr;
91     PERFORM actor.usr_merge_rows('actor.usr_org_unit_opt_in', 'usr', src_usr, dest_usr);
92     PERFORM actor.usr_merge_rows('actor.usr_setting', 'usr', src_usr, dest_usr);
93
94     -- permission.*
95     PERFORM actor.usr_merge_rows('permission.usr_perm_map', 'usr', src_usr, dest_usr);
96     PERFORM actor.usr_merge_rows('permission.usr_object_perm_map', 'usr', src_usr, dest_usr);
97     PERFORM actor.usr_merge_rows('permission.usr_grp_map', 'usr', src_usr, dest_usr);
98     PERFORM actor.usr_merge_rows('permission.usr_work_ou_map', 'usr', src_usr, dest_usr);
99
100
101     -- container.*
102         
103         -- For each *_bucket table: transfer every bucket belonging to src_usr
104         -- into the custody of dest_usr.
105         --
106         -- In order to avoid colliding with an existing bucket owned by
107         -- the destination user, append the source user's id (in parenthesese)
108         -- to the name.  If you still get a collision, add successive
109         -- spaces to the name and keep trying until you succeed.
110         --
111         FOR bucket_row in
112                 SELECT id, name
113                 FROM   container.biblio_record_entry_bucket
114                 WHERE  owner = src_usr
115         LOOP
116                 suffix := ' (' || src_usr || ')';
117                 LOOP
118                         BEGIN
119                                 UPDATE  container.biblio_record_entry_bucket
120                                 SET     owner = dest_usr, name = name || suffix
121                                 WHERE   id = bucket_row.id;
122                         EXCEPTION WHEN unique_violation THEN
123                                 suffix := suffix || ' ';
124                                 CONTINUE;
125                         END;
126                         EXIT;
127                 END LOOP;
128         END LOOP;
129
130         FOR bucket_row in
131                 SELECT id, name
132                 FROM   container.call_number_bucket
133                 WHERE  owner = src_usr
134         LOOP
135                 suffix := ' (' || src_usr || ')';
136                 LOOP
137                         BEGIN
138                                 UPDATE  container.call_number_bucket
139                                 SET     owner = dest_usr, name = name || suffix
140                                 WHERE   id = bucket_row.id;
141                         EXCEPTION WHEN unique_violation THEN
142                                 suffix := suffix || ' ';
143                                 CONTINUE;
144                         END;
145                         EXIT;
146                 END LOOP;
147         END LOOP;
148
149         FOR bucket_row in
150                 SELECT id, name
151                 FROM   container.copy_bucket
152                 WHERE  owner = src_usr
153         LOOP
154                 suffix := ' (' || src_usr || ')';
155                 LOOP
156                         BEGIN
157                                 UPDATE  container.copy_bucket
158                                 SET     owner = dest_usr, name = name || suffix
159                                 WHERE   id = bucket_row.id;
160                         EXCEPTION WHEN unique_violation THEN
161                                 suffix := suffix || ' ';
162                                 CONTINUE;
163                         END;
164                         EXIT;
165                 END LOOP;
166         END LOOP;
167
168         FOR bucket_row in
169                 SELECT id, name
170                 FROM   container.user_bucket
171                 WHERE  owner = src_usr
172         LOOP
173                 suffix := ' (' || src_usr || ')';
174                 LOOP
175                         BEGIN
176                                 UPDATE  container.user_bucket
177                                 SET     owner = dest_usr, name = name || suffix
178                                 WHERE   id = bucket_row.id;
179                         EXCEPTION WHEN unique_violation THEN
180                                 suffix := suffix || ' ';
181                                 CONTINUE;
182                         END;
183                         EXIT;
184                 END LOOP;
185         END LOOP;
186
187         UPDATE container.user_bucket_item SET target_user = dest_usr WHERE target_user = src_usr;
188
189     -- vandelay.*
190         -- transfer queues the same way we transfer buckets (see above)
191         FOR queue_row in
192                 SELECT id, name
193                 FROM   vandelay.queue
194                 WHERE  owner = src_usr
195         LOOP
196                 suffix := ' (' || src_usr || ')';
197                 LOOP
198                         BEGIN
199                                 UPDATE  vandelay.queue
200                                 SET     owner = dest_usr, name = name || suffix
201                                 WHERE   id = queue_row.id;
202                         EXCEPTION WHEN unique_violation THEN
203                                 suffix := suffix || ' ';
204                                 CONTINUE;
205                         END;
206                         EXIT;
207                 END LOOP;
208         END LOOP;
209
210     UPDATE vandelay.session_tracker SET usr = dest_usr WHERE usr = src_usr;
211
212     -- money.*
213     PERFORM actor.usr_merge_rows('money.collections_tracker', 'usr', src_usr, dest_usr);
214     PERFORM actor.usr_merge_rows('money.collections_tracker', 'collector', src_usr, dest_usr);
215     UPDATE money.billable_xact SET usr = dest_usr WHERE usr = src_usr;
216     UPDATE money.billing SET voider = dest_usr WHERE voider = src_usr;
217     UPDATE money.bnm_payment SET accepting_usr = dest_usr WHERE accepting_usr = src_usr;
218
219     -- action.*
220     UPDATE action.circulation SET usr = dest_usr WHERE usr = src_usr;
221     UPDATE action.circulation SET circ_staff = dest_usr WHERE circ_staff = src_usr;
222     UPDATE action.circulation SET checkin_staff = dest_usr WHERE checkin_staff = src_usr;
223     UPDATE action.usr_circ_history SET usr = dest_usr WHERE usr = src_usr;
224
225     UPDATE action.hold_request SET usr = dest_usr WHERE usr = src_usr;
226     UPDATE action.hold_request SET fulfillment_staff = dest_usr WHERE fulfillment_staff = src_usr;
227     UPDATE action.hold_request SET requestor = dest_usr WHERE requestor = src_usr;
228     UPDATE action.hold_notification SET notify_staff = dest_usr WHERE notify_staff = src_usr;
229
230     UPDATE action.in_house_use SET staff = dest_usr WHERE staff = src_usr;
231     UPDATE action.non_cataloged_circulation SET staff = dest_usr WHERE staff = src_usr;
232     UPDATE action.non_cataloged_circulation SET patron = dest_usr WHERE patron = src_usr;
233     UPDATE action.non_cat_in_house_use SET staff = dest_usr WHERE staff = src_usr;
234     UPDATE action.survey_response SET usr = dest_usr WHERE usr = src_usr;
235
236     -- acq.*
237     UPDATE acq.fund_allocation SET allocator = dest_usr WHERE allocator = src_usr;
238         UPDATE acq.fund_transfer SET transfer_user = dest_usr WHERE transfer_user = src_usr;
239
240         -- transfer picklists the same way we transfer buckets (see above)
241         FOR picklist_row in
242                 SELECT id, name
243                 FROM   acq.picklist
244                 WHERE  owner = src_usr
245         LOOP
246                 suffix := ' (' || src_usr || ')';
247                 LOOP
248                         BEGIN
249                                 UPDATE  acq.picklist
250                                 SET     owner = dest_usr, name = name || suffix
251                                 WHERE   id = picklist_row.id;
252                         EXCEPTION WHEN unique_violation THEN
253                                 suffix := suffix || ' ';
254                                 CONTINUE;
255                         END;
256                         EXIT;
257                 END LOOP;
258         END LOOP;
259
260     UPDATE acq.purchase_order SET owner = dest_usr WHERE owner = src_usr;
261     UPDATE acq.po_note SET creator = dest_usr WHERE creator = src_usr;
262     UPDATE acq.po_note SET editor = dest_usr WHERE editor = src_usr;
263     UPDATE acq.provider_note SET creator = dest_usr WHERE creator = src_usr;
264     UPDATE acq.provider_note SET editor = dest_usr WHERE editor = src_usr;
265     UPDATE acq.lineitem_note SET creator = dest_usr WHERE creator = src_usr;
266     UPDATE acq.lineitem_note SET editor = dest_usr WHERE editor = src_usr;
267     UPDATE acq.lineitem_usr_attr_definition SET usr = dest_usr WHERE usr = src_usr;
268
269     -- asset.*
270     UPDATE asset.copy SET creator = dest_usr WHERE creator = src_usr;
271     UPDATE asset.copy SET editor = dest_usr WHERE editor = src_usr;
272     UPDATE asset.copy_note SET creator = dest_usr WHERE creator = src_usr;
273     UPDATE asset.call_number SET creator = dest_usr WHERE creator = src_usr;
274     UPDATE asset.call_number SET editor = dest_usr WHERE editor = src_usr;
275     UPDATE asset.call_number_note SET creator = dest_usr WHERE creator = src_usr;
276
277     -- serial.*
278     UPDATE serial.record_entry SET creator = dest_usr WHERE creator = src_usr;
279     UPDATE serial.record_entry SET editor = dest_usr WHERE editor = src_usr;
280
281     -- reporter.*
282     -- It's not uncommon to define the reporter schema in a replica 
283     -- DB only, so don't assume these tables exist in the write DB.
284     BEGIN
285         UPDATE reporter.template SET owner = dest_usr WHERE owner = src_usr;
286     EXCEPTION WHEN undefined_table THEN
287         -- do nothing
288     END;
289     BEGIN
290         UPDATE reporter.report SET owner = dest_usr WHERE owner = src_usr;
291     EXCEPTION WHEN undefined_table THEN
292         -- do nothing
293     END;
294     BEGIN
295         UPDATE reporter.schedule SET runner = dest_usr WHERE runner = src_usr;
296     EXCEPTION WHEN undefined_table THEN
297         -- do nothing
298     END;
299     BEGIN
300                 -- transfer folders the same way we transfer buckets (see above)
301                 FOR folder_row in
302                         SELECT id, name
303                         FROM   reporter.template_folder
304                         WHERE  owner = src_usr
305                 LOOP
306                         suffix := ' (' || src_usr || ')';
307                         LOOP
308                                 BEGIN
309                                         UPDATE  reporter.template_folder
310                                         SET     owner = dest_usr, name = name || suffix
311                                         WHERE   id = folder_row.id;
312                                 EXCEPTION WHEN unique_violation THEN
313                                         suffix := suffix || ' ';
314                                         CONTINUE;
315                                 END;
316                                 EXIT;
317                         END LOOP;
318                 END LOOP;
319     EXCEPTION WHEN undefined_table THEN
320         -- do nothing
321     END;
322     BEGIN
323                 -- transfer folders the same way we transfer buckets (see above)
324                 FOR folder_row in
325                         SELECT id, name
326                         FROM   reporter.report_folder
327                         WHERE  owner = src_usr
328                 LOOP
329                         suffix := ' (' || src_usr || ')';
330                         LOOP
331                                 BEGIN
332                                         UPDATE  reporter.report_folder
333                                         SET     owner = dest_usr, name = name || suffix
334                                         WHERE   id = folder_row.id;
335                                 EXCEPTION WHEN unique_violation THEN
336                                         suffix := suffix || ' ';
337                                         CONTINUE;
338                                 END;
339                                 EXIT;
340                         END LOOP;
341                 END LOOP;
342     EXCEPTION WHEN undefined_table THEN
343         -- do nothing
344     END;
345     BEGIN
346                 -- transfer folders the same way we transfer buckets (see above)
347                 FOR folder_row in
348                         SELECT id, name
349                         FROM   reporter.output_folder
350                         WHERE  owner = src_usr
351                 LOOP
352                         suffix := ' (' || src_usr || ')';
353                         LOOP
354                                 BEGIN
355                                         UPDATE  reporter.output_folder
356                                         SET     owner = dest_usr, name = name || suffix
357                                         WHERE   id = folder_row.id;
358                                 EXCEPTION WHEN unique_violation THEN
359                                         suffix := suffix || ' ';
360                                         CONTINUE;
361                                 END;
362                                 EXIT;
363                         END LOOP;
364                 END LOOP;
365     EXCEPTION WHEN undefined_table THEN
366         -- do nothing
367     END;
368
369     -- propagate preferred name values from the source user to the
370     -- destination user, but only when values are not being replaced.
371     WITH susr AS (SELECT * FROM actor.usr WHERE id = src_usr)
372     UPDATE actor.usr SET 
373         pref_prefix = 
374             COALESCE(pref_prefix, (SELECT pref_prefix FROM susr)),
375         pref_first_given_name = 
376             COALESCE(pref_first_given_name, (SELECT pref_first_given_name FROM susr)),
377         pref_second_given_name = 
378             COALESCE(pref_second_given_name, (SELECT pref_second_given_name FROM susr)),
379         pref_family_name = 
380             COALESCE(pref_family_name, (SELECT pref_family_name FROM susr)),
381         pref_suffix = 
382             COALESCE(pref_suffix, (SELECT pref_suffix FROM susr))
383     WHERE id = dest_usr;
384
385     -- Copy and deduplicate name keywords
386     -- String -> array -> rows -> DISTINCT -> array -> string
387     WITH susr AS (SELECT * FROM actor.usr WHERE id = src_usr),
388          dusr AS (SELECT * FROM actor.usr WHERE id = dest_usr)
389     UPDATE actor.usr SET name_keywords = (
390         WITH keywords AS (
391             SELECT DISTINCT UNNEST(
392                 REGEXP_SPLIT_TO_ARRAY(
393                     COALESCE((SELECT name_keywords FROM susr), '') || ' ' ||
394                     COALESCE((SELECT name_keywords FROM dusr), ''),  E'\\s+'
395                 )
396             ) AS parts
397         ) SELECT ARRAY_TO_STRING(ARRAY_AGG(kw.parts), ' ') FROM keywords kw
398     ) WHERE id = dest_usr;
399
400     -- Finally, delete the source user
401     DELETE FROM actor.usr WHERE id = src_usr;
402
403 END;
404 $$ LANGUAGE plpgsql;
405
406
407 CREATE OR REPLACE FUNCTION actor.usr_purge_data(
408         src_usr  IN INTEGER,
409         specified_dest_usr IN INTEGER
410 ) RETURNS VOID AS $$
411 DECLARE
412         suffix TEXT;
413         renamable_row RECORD;
414         dest_usr INTEGER;
415 BEGIN
416
417         IF specified_dest_usr IS NULL THEN
418                 dest_usr := 1; -- Admin user on stock installs
419         ELSE
420                 dest_usr := specified_dest_usr;
421         END IF;
422
423         -- acq.*
424         UPDATE acq.fund_allocation SET allocator = dest_usr WHERE allocator = src_usr;
425         UPDATE acq.lineitem SET creator = dest_usr WHERE creator = src_usr;
426         UPDATE acq.lineitem SET editor = dest_usr WHERE editor = src_usr;
427         UPDATE acq.lineitem SET selector = dest_usr WHERE selector = src_usr;
428         UPDATE acq.lineitem_note SET creator = dest_usr WHERE creator = src_usr;
429         UPDATE acq.lineitem_note SET editor = dest_usr WHERE editor = src_usr;
430         DELETE FROM acq.lineitem_usr_attr_definition WHERE usr = src_usr;
431
432         -- Update with a rename to avoid collisions
433         FOR renamable_row in
434                 SELECT id, name
435                 FROM   acq.picklist
436                 WHERE  owner = src_usr
437         LOOP
438                 suffix := ' (' || src_usr || ')';
439                 LOOP
440                         BEGIN
441                                 UPDATE  acq.picklist
442                                 SET     owner = dest_usr, name = name || suffix
443                                 WHERE   id = renamable_row.id;
444                         EXCEPTION WHEN unique_violation THEN
445                                 suffix := suffix || ' ';
446                                 CONTINUE;
447                         END;
448                         EXIT;
449                 END LOOP;
450         END LOOP;
451
452         UPDATE acq.picklist SET creator = dest_usr WHERE creator = src_usr;
453         UPDATE acq.picklist SET editor = dest_usr WHERE editor = src_usr;
454         UPDATE acq.po_note SET creator = dest_usr WHERE creator = src_usr;
455         UPDATE acq.po_note SET editor = dest_usr WHERE editor = src_usr;
456         UPDATE acq.purchase_order SET owner = dest_usr WHERE owner = src_usr;
457         UPDATE acq.purchase_order SET creator = dest_usr WHERE creator = src_usr;
458         UPDATE acq.purchase_order SET editor = dest_usr WHERE editor = src_usr;
459         UPDATE acq.claim_event SET creator = dest_usr WHERE creator = src_usr;
460
461         -- action.*
462         DELETE FROM action.circulation WHERE usr = src_usr;
463         UPDATE action.circulation SET circ_staff = dest_usr WHERE circ_staff = src_usr;
464         UPDATE action.circulation SET checkin_staff = dest_usr WHERE checkin_staff = src_usr;
465         UPDATE action.hold_notification SET notify_staff = dest_usr WHERE notify_staff = src_usr;
466         UPDATE action.hold_request SET fulfillment_staff = dest_usr WHERE fulfillment_staff = src_usr;
467         UPDATE action.hold_request SET requestor = dest_usr WHERE requestor = src_usr;
468         DELETE FROM action.hold_request WHERE usr = src_usr;
469         UPDATE action.in_house_use SET staff = dest_usr WHERE staff = src_usr;
470         UPDATE action.non_cat_in_house_use SET staff = dest_usr WHERE staff = src_usr;
471         DELETE FROM action.non_cataloged_circulation WHERE patron = src_usr;
472         UPDATE action.non_cataloged_circulation SET staff = dest_usr WHERE staff = src_usr;
473         DELETE FROM action.survey_response WHERE usr = src_usr;
474         UPDATE action.fieldset SET owner = dest_usr WHERE owner = src_usr;
475         DELETE FROM action.usr_circ_history WHERE usr = src_usr;
476
477         -- actor.*
478         DELETE FROM actor.card WHERE usr = src_usr;
479         DELETE FROM actor.stat_cat_entry_usr_map WHERE target_usr = src_usr;
480
481         -- The following update is intended to avoid transient violations of a foreign
482         -- key constraint, whereby actor.usr_address references itself.  It may not be
483         -- necessary, but it does no harm.
484         UPDATE actor.usr_address SET replaces = NULL
485                 WHERE usr = src_usr AND replaces IS NOT NULL;
486         DELETE FROM actor.usr_address WHERE usr = src_usr;
487         DELETE FROM actor.usr_note WHERE usr = src_usr;
488         UPDATE actor.usr_note SET creator = dest_usr WHERE creator = src_usr;
489         DELETE FROM actor.usr_org_unit_opt_in WHERE usr = src_usr;
490         UPDATE actor.usr_org_unit_opt_in SET staff = dest_usr WHERE staff = src_usr;
491         DELETE FROM actor.usr_setting WHERE usr = src_usr;
492         DELETE FROM actor.usr_standing_penalty WHERE usr = src_usr;
493         UPDATE actor.usr_standing_penalty SET staff = dest_usr WHERE staff = src_usr;
494
495         -- asset.*
496         UPDATE asset.call_number SET creator = dest_usr WHERE creator = src_usr;
497         UPDATE asset.call_number SET editor = dest_usr WHERE editor = src_usr;
498         UPDATE asset.call_number_note SET creator = dest_usr WHERE creator = src_usr;
499         UPDATE asset.copy SET creator = dest_usr WHERE creator = src_usr;
500         UPDATE asset.copy SET editor = dest_usr WHERE editor = src_usr;
501         UPDATE asset.copy_note SET creator = dest_usr WHERE creator = src_usr;
502
503         -- auditor.*
504         DELETE FROM auditor.actor_usr_address_history WHERE id = src_usr;
505         DELETE FROM auditor.actor_usr_history WHERE id = src_usr;
506         UPDATE auditor.asset_call_number_history SET creator = dest_usr WHERE creator = src_usr;
507         UPDATE auditor.asset_call_number_history SET editor  = dest_usr WHERE editor  = src_usr;
508         UPDATE auditor.asset_copy_history SET creator = dest_usr WHERE creator = src_usr;
509         UPDATE auditor.asset_copy_history SET editor  = dest_usr WHERE editor  = src_usr;
510         UPDATE auditor.biblio_record_entry_history SET creator = dest_usr WHERE creator = src_usr;
511         UPDATE auditor.biblio_record_entry_history SET editor  = dest_usr WHERE editor  = src_usr;
512
513         -- biblio.*
514         UPDATE biblio.record_entry SET creator = dest_usr WHERE creator = src_usr;
515         UPDATE biblio.record_entry SET editor = dest_usr WHERE editor = src_usr;
516         UPDATE biblio.record_note SET creator = dest_usr WHERE creator = src_usr;
517         UPDATE biblio.record_note SET editor = dest_usr WHERE editor = src_usr;
518
519         -- container.*
520         -- Update buckets with a rename to avoid collisions
521         FOR renamable_row in
522                 SELECT id, name
523                 FROM   container.biblio_record_entry_bucket
524                 WHERE  owner = src_usr
525         LOOP
526                 suffix := ' (' || src_usr || ')';
527                 LOOP
528                         BEGIN
529                                 UPDATE  container.biblio_record_entry_bucket
530                                 SET     owner = dest_usr, name = name || suffix
531                                 WHERE   id = renamable_row.id;
532                         EXCEPTION WHEN unique_violation THEN
533                                 suffix := suffix || ' ';
534                                 CONTINUE;
535                         END;
536                         EXIT;
537                 END LOOP;
538         END LOOP;
539
540         FOR renamable_row in
541                 SELECT id, name
542                 FROM   container.call_number_bucket
543                 WHERE  owner = src_usr
544         LOOP
545                 suffix := ' (' || src_usr || ')';
546                 LOOP
547                         BEGIN
548                                 UPDATE  container.call_number_bucket
549                                 SET     owner = dest_usr, name = name || suffix
550                                 WHERE   id = renamable_row.id;
551                         EXCEPTION WHEN unique_violation THEN
552                                 suffix := suffix || ' ';
553                                 CONTINUE;
554                         END;
555                         EXIT;
556                 END LOOP;
557         END LOOP;
558
559         FOR renamable_row in
560                 SELECT id, name
561                 FROM   container.copy_bucket
562                 WHERE  owner = src_usr
563         LOOP
564                 suffix := ' (' || src_usr || ')';
565                 LOOP
566                         BEGIN
567                                 UPDATE  container.copy_bucket
568                                 SET     owner = dest_usr, name = name || suffix
569                                 WHERE   id = renamable_row.id;
570                         EXCEPTION WHEN unique_violation THEN
571                                 suffix := suffix || ' ';
572                                 CONTINUE;
573                         END;
574                         EXIT;
575                 END LOOP;
576         END LOOP;
577
578         FOR renamable_row in
579                 SELECT id, name
580                 FROM   container.user_bucket
581                 WHERE  owner = src_usr
582         LOOP
583                 suffix := ' (' || src_usr || ')';
584                 LOOP
585                         BEGIN
586                                 UPDATE  container.user_bucket
587                                 SET     owner = dest_usr, name = name || suffix
588                                 WHERE   id = renamable_row.id;
589                         EXCEPTION WHEN unique_violation THEN
590                                 suffix := suffix || ' ';
591                                 CONTINUE;
592                         END;
593                         EXIT;
594                 END LOOP;
595         END LOOP;
596
597         DELETE FROM container.user_bucket_item WHERE target_user = src_usr;
598
599         -- money.*
600         DELETE FROM money.billable_xact WHERE usr = src_usr;
601         DELETE FROM money.collections_tracker WHERE usr = src_usr;
602         UPDATE money.collections_tracker SET collector = dest_usr WHERE collector = src_usr;
603
604         -- permission.*
605         DELETE FROM permission.usr_grp_map WHERE usr = src_usr;
606         DELETE FROM permission.usr_object_perm_map WHERE usr = src_usr;
607         DELETE FROM permission.usr_perm_map WHERE usr = src_usr;
608         DELETE FROM permission.usr_work_ou_map WHERE usr = src_usr;
609
610         -- reporter.*
611         -- Update with a rename to avoid collisions
612         BEGIN
613                 FOR renamable_row in
614                         SELECT id, name
615                         FROM   reporter.output_folder
616                         WHERE  owner = src_usr
617                 LOOP
618                         suffix := ' (' || src_usr || ')';
619                         LOOP
620                                 BEGIN
621                                         UPDATE  reporter.output_folder
622                                         SET     owner = dest_usr, name = name || suffix
623                                         WHERE   id = renamable_row.id;
624                                 EXCEPTION WHEN unique_violation THEN
625                                         suffix := suffix || ' ';
626                                         CONTINUE;
627                                 END;
628                                 EXIT;
629                         END LOOP;
630                 END LOOP;
631         EXCEPTION WHEN undefined_table THEN
632                 -- do nothing
633         END;
634
635         BEGIN
636                 UPDATE reporter.report SET owner = dest_usr WHERE owner = src_usr;
637         EXCEPTION WHEN undefined_table THEN
638                 -- do nothing
639         END;
640
641         -- Update with a rename to avoid collisions
642         BEGIN
643                 FOR renamable_row in
644                         SELECT id, name
645                         FROM   reporter.report_folder
646                         WHERE  owner = src_usr
647                 LOOP
648                         suffix := ' (' || src_usr || ')';
649                         LOOP
650                                 BEGIN
651                                         UPDATE  reporter.report_folder
652                                         SET     owner = dest_usr, name = name || suffix
653                                         WHERE   id = renamable_row.id;
654                                 EXCEPTION WHEN unique_violation THEN
655                                         suffix := suffix || ' ';
656                                         CONTINUE;
657                                 END;
658                                 EXIT;
659                         END LOOP;
660                 END LOOP;
661         EXCEPTION WHEN undefined_table THEN
662                 -- do nothing
663         END;
664
665         BEGIN
666                 UPDATE reporter.schedule SET runner = dest_usr WHERE runner = src_usr;
667         EXCEPTION WHEN undefined_table THEN
668                 -- do nothing
669         END;
670
671         BEGIN
672                 UPDATE reporter.template SET owner = dest_usr WHERE owner = src_usr;
673         EXCEPTION WHEN undefined_table THEN
674                 -- do nothing
675         END;
676
677         -- Update with a rename to avoid collisions
678         BEGIN
679                 FOR renamable_row in
680                         SELECT id, name
681                         FROM   reporter.template_folder
682                         WHERE  owner = src_usr
683                 LOOP
684                         suffix := ' (' || src_usr || ')';
685                         LOOP
686                                 BEGIN
687                                         UPDATE  reporter.template_folder
688                                         SET     owner = dest_usr, name = name || suffix
689                                         WHERE   id = renamable_row.id;
690                                 EXCEPTION WHEN unique_violation THEN
691                                         suffix := suffix || ' ';
692                                         CONTINUE;
693                                 END;
694                                 EXIT;
695                         END LOOP;
696                 END LOOP;
697         EXCEPTION WHEN undefined_table THEN
698         -- do nothing
699         END;
700
701         -- vandelay.*
702         -- Update with a rename to avoid collisions
703         FOR renamable_row in
704                 SELECT id, name
705                 FROM   vandelay.queue
706                 WHERE  owner = src_usr
707         LOOP
708                 suffix := ' (' || src_usr || ')';
709                 LOOP
710                         BEGIN
711                                 UPDATE  vandelay.queue
712                                 SET     owner = dest_usr, name = name || suffix
713                                 WHERE   id = renamable_row.id;
714                         EXCEPTION WHEN unique_violation THEN
715                                 suffix := suffix || ' ';
716                                 CONTINUE;
717                         END;
718                         EXIT;
719                 END LOOP;
720         END LOOP;
721
722     UPDATE vandelay.session_tracker SET usr = dest_usr WHERE usr = src_usr;
723
724     -- NULL-ify addresses last so other cleanup (e.g. circ anonymization)
725     -- can access the information before deletion.
726         UPDATE actor.usr SET
727                 active = FALSE,
728                 card = NULL,
729                 mailing_address = NULL,
730                 billing_address = NULL
731         WHERE id = src_usr;
732
733 END;
734 $$ LANGUAGE plpgsql;
735
736
737 COMMIT;