]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/src/sql/Pg/upgrade/XXXX.schema.patron-alt-name.sql
LP#1776020 Deduplicate name keywords in patron merge
[Evergreen.git] / Open-ILS / src / sql / Pg / upgrade / XXXX.schema.patron-alt-name.sql
1
2 /**
3 update patron merge
4 */
5
6 BEGIN;
7
8 ALTER TABLE actor.usr 
9     ADD COLUMN pref_prefix TEXT,
10     ADD COLUMN pref_first_given_name TEXT,
11     ADD COLUMN pref_second_given_name TEXT,
12     ADD COLUMN pref_family_name TEXT,
13     ADD COLUMN pref_suffix TEXT,
14     ADD COLUMN name_keywords TEXT,
15     ADD COLUMN name_kw_tsvector TSVECTOR;
16
17 ALTER TABLE staging.user_stage
18     ADD COLUMN pref_first_given_name TEXT,
19     ADD COLUMN pref_second_given_name TEXT,
20     ADD COLUMN pref_family_name TEXT;
21
22 CREATE INDEX actor_usr_pref_first_given_name_idx 
23     ON actor.usr (evergreen.lowercase(pref_first_given_name));
24 CREATE INDEX actor_usr_pref_second_given_name_idx 
25     ON actor.usr (evergreen.lowercase(pref_second_given_name));
26 CREATE INDEX actor_usr_pref_family_name_idx 
27     ON actor.usr (evergreen.lowercase(pref_family_name));
28 CREATE INDEX actor_usr_pref_first_given_name_unaccent_idx 
29     ON actor.usr (evergreen.unaccent_and_squash(pref_first_given_name));
30 CREATE INDEX actor_usr_pref_second_given_name_unaccent_idx 
31     ON actor.usr (evergreen.unaccent_and_squash(pref_second_given_name));
32 CREATE INDEX actor_usr_pref_family_name_unaccent_idx 
33    ON actor.usr (evergreen.unaccent_and_squash(pref_family_name));
34
35 -- Update keyword indexes for existing patrons
36
37 UPDATE actor.usr SET name_kw_tsvector = 
38     TO_TSVECTOR(
39         COALESCE(prefix, '') || ' ' || 
40         COALESCE(first_given_name, '') || ' ' || 
41         COALESCE(evergreen.unaccent_and_squash(first_given_name), '') || ' ' || 
42         COALESCE(second_given_name, '') || ' ' || 
43         COALESCE(evergreen.unaccent_and_squash(second_given_name), '') || ' ' || 
44         COALESCE(family_name, '') || ' ' || 
45         COALESCE(evergreen.unaccent_and_squash(family_name), '') || ' ' || 
46         COALESCE(suffix, '')
47     );
48
49 CREATE OR REPLACE FUNCTION actor.user_ingest_name_keywords() 
50     RETURNS TRIGGER AS $func$
51 BEGIN
52     NEW.name_kw_tsvector := TO_TSVECTOR(
53         COALESCE(NEW.prefix, '')                || ' ' || 
54         COALESCE(NEW.first_given_name, '')      || ' ' || 
55         COALESCE(evergreen.unaccent_and_squash(NEW.first_given_name), '') || ' ' || 
56         COALESCE(NEW.second_given_name, '')     || ' ' || 
57         COALESCE(evergreen.unaccent_and_squash(NEW.second_given_name), '') || ' ' || 
58         COALESCE(NEW.family_name, '')           || ' ' || 
59         COALESCE(evergreen.unaccent_and_squash(NEW.family_name), '') || ' ' || 
60         COALESCE(NEW.suffix, '')                || ' ' || 
61         COALESCE(NEW.pref_prefix, '')            || ' ' || 
62         COALESCE(NEW.pref_first_given_name, '')  || ' ' || 
63         COALESCE(evergreen.unaccent_and_squash(NEW.pref_first_given_name), '') || ' ' || 
64         COALESCE(NEW.pref_second_given_name, '') || ' ' || 
65         COALESCE(evergreen.unaccent_and_squash(NEW.pref_second_given_name), '') || ' ' || 
66         COALESCE(NEW.pref_family_name, '')       || ' ' || 
67         COALESCE(evergreen.unaccent_and_squash(NEW.pref_family_name), '') || ' ' || 
68         COALESCE(NEW.pref_suffix, '')            || ' ' || 
69         COALESCE(NEW.name_keywords, '')
70     );
71     RETURN NEW;
72 END;
73 $func$ LANGUAGE PLPGSQL;
74
75 -- Add after the batch upate above to avoid duplicate updates.
76 CREATE TRIGGER user_ingest_name_keywords_tgr 
77     BEFORE INSERT OR UPDATE ON actor.usr 
78     FOR EACH ROW EXECUTE PROCEDURE actor.user_ingest_name_keywords();
79
80
81 -- merge pref names from source user to target user, except when
82 -- clobbering existing pref names.
83 CREATE OR REPLACE FUNCTION actor.usr_merge(src_usr INT, dest_usr INT, 
84     del_addrs BOOLEAN, del_cards BOOLEAN, deactivate_cards BOOLEAN ) 
85     RETURNS VOID AS $$
86 DECLARE
87         suffix TEXT;
88         bucket_row RECORD;
89         picklist_row RECORD;
90         queue_row RECORD;
91         folder_row RECORD;
92 BEGIN
93
94     -- do some initial cleanup 
95     UPDATE actor.usr SET card = NULL WHERE id = src_usr;
96     UPDATE actor.usr SET mailing_address = NULL WHERE id = src_usr;
97     UPDATE actor.usr SET billing_address = NULL WHERE id = src_usr;
98
99     -- actor.*
100     IF del_cards THEN
101         DELETE FROM actor.card where usr = src_usr;
102     ELSE
103         IF deactivate_cards THEN
104             UPDATE actor.card SET active = 'f' WHERE usr = src_usr;
105         END IF;
106         UPDATE actor.card SET usr = dest_usr WHERE usr = src_usr;
107     END IF;
108
109
110     IF del_addrs THEN
111         DELETE FROM actor.usr_address WHERE usr = src_usr;
112     ELSE
113         UPDATE actor.usr_address SET usr = dest_usr WHERE usr = src_usr;
114     END IF;
115
116     UPDATE actor.usr_note SET usr = dest_usr WHERE usr = src_usr;
117     -- dupes are technically OK in actor.usr_standing_penalty, should manually delete them...
118     UPDATE actor.usr_standing_penalty SET usr = dest_usr WHERE usr = src_usr;
119     PERFORM actor.usr_merge_rows('actor.usr_org_unit_opt_in', 'usr', src_usr, dest_usr);
120     PERFORM actor.usr_merge_rows('actor.usr_setting', 'usr', src_usr, dest_usr);
121
122     -- permission.*
123     PERFORM actor.usr_merge_rows('permission.usr_perm_map', 'usr', src_usr, dest_usr);
124     PERFORM actor.usr_merge_rows('permission.usr_object_perm_map', 'usr', src_usr, dest_usr);
125     PERFORM actor.usr_merge_rows('permission.usr_grp_map', 'usr', src_usr, dest_usr);
126     PERFORM actor.usr_merge_rows('permission.usr_work_ou_map', 'usr', src_usr, dest_usr);
127
128
129     -- container.*
130         
131         -- For each *_bucket table: transfer every bucket belonging to src_usr
132         -- into the custody of dest_usr.
133         --
134         -- In order to avoid colliding with an existing bucket owned by
135         -- the destination user, append the source user's id (in parenthesese)
136         -- to the name.  If you still get a collision, add successive
137         -- spaces to the name and keep trying until you succeed.
138         --
139         FOR bucket_row in
140                 SELECT id, name
141                 FROM   container.biblio_record_entry_bucket
142                 WHERE  owner = src_usr
143         LOOP
144                 suffix := ' (' || src_usr || ')';
145                 LOOP
146                         BEGIN
147                                 UPDATE  container.biblio_record_entry_bucket
148                                 SET     owner = dest_usr, name = name || suffix
149                                 WHERE   id = bucket_row.id;
150                         EXCEPTION WHEN unique_violation THEN
151                                 suffix := suffix || ' ';
152                                 CONTINUE;
153                         END;
154                         EXIT;
155                 END LOOP;
156         END LOOP;
157
158         FOR bucket_row in
159                 SELECT id, name
160                 FROM   container.call_number_bucket
161                 WHERE  owner = src_usr
162         LOOP
163                 suffix := ' (' || src_usr || ')';
164                 LOOP
165                         BEGIN
166                                 UPDATE  container.call_number_bucket
167                                 SET     owner = dest_usr, name = name || suffix
168                                 WHERE   id = bucket_row.id;
169                         EXCEPTION WHEN unique_violation THEN
170                                 suffix := suffix || ' ';
171                                 CONTINUE;
172                         END;
173                         EXIT;
174                 END LOOP;
175         END LOOP;
176
177         FOR bucket_row in
178                 SELECT id, name
179                 FROM   container.copy_bucket
180                 WHERE  owner = src_usr
181         LOOP
182                 suffix := ' (' || src_usr || ')';
183                 LOOP
184                         BEGIN
185                                 UPDATE  container.copy_bucket
186                                 SET     owner = dest_usr, name = name || suffix
187                                 WHERE   id = bucket_row.id;
188                         EXCEPTION WHEN unique_violation THEN
189                                 suffix := suffix || ' ';
190                                 CONTINUE;
191                         END;
192                         EXIT;
193                 END LOOP;
194         END LOOP;
195
196         FOR bucket_row in
197                 SELECT id, name
198                 FROM   container.user_bucket
199                 WHERE  owner = src_usr
200         LOOP
201                 suffix := ' (' || src_usr || ')';
202                 LOOP
203                         BEGIN
204                                 UPDATE  container.user_bucket
205                                 SET     owner = dest_usr, name = name || suffix
206                                 WHERE   id = bucket_row.id;
207                         EXCEPTION WHEN unique_violation THEN
208                                 suffix := suffix || ' ';
209                                 CONTINUE;
210                         END;
211                         EXIT;
212                 END LOOP;
213         END LOOP;
214
215         UPDATE container.user_bucket_item SET target_user = dest_usr WHERE target_user = src_usr;
216
217     -- vandelay.*
218         -- transfer queues the same way we transfer buckets (see above)
219         FOR queue_row in
220                 SELECT id, name
221                 FROM   vandelay.queue
222                 WHERE  owner = src_usr
223         LOOP
224                 suffix := ' (' || src_usr || ')';
225                 LOOP
226                         BEGIN
227                                 UPDATE  vandelay.queue
228                                 SET     owner = dest_usr, name = name || suffix
229                                 WHERE   id = queue_row.id;
230                         EXCEPTION WHEN unique_violation THEN
231                                 suffix := suffix || ' ';
232                                 CONTINUE;
233                         END;
234                         EXIT;
235                 END LOOP;
236         END LOOP;
237
238     -- money.*
239     PERFORM actor.usr_merge_rows('money.collections_tracker', 'usr', src_usr, dest_usr);
240     PERFORM actor.usr_merge_rows('money.collections_tracker', 'collector', src_usr, dest_usr);
241     UPDATE money.billable_xact SET usr = dest_usr WHERE usr = src_usr;
242     UPDATE money.billing SET voider = dest_usr WHERE voider = src_usr;
243     UPDATE money.bnm_payment SET accepting_usr = dest_usr WHERE accepting_usr = src_usr;
244
245     -- action.*
246     UPDATE action.circulation SET usr = dest_usr WHERE usr = src_usr;
247     UPDATE action.circulation SET circ_staff = dest_usr WHERE circ_staff = src_usr;
248     UPDATE action.circulation SET checkin_staff = dest_usr WHERE checkin_staff = src_usr;
249     UPDATE action.usr_circ_history SET usr = dest_usr WHERE usr = src_usr;
250
251     UPDATE action.hold_request SET usr = dest_usr WHERE usr = src_usr;
252     UPDATE action.hold_request SET fulfillment_staff = dest_usr WHERE fulfillment_staff = src_usr;
253     UPDATE action.hold_request SET requestor = dest_usr WHERE requestor = src_usr;
254     UPDATE action.hold_notification SET notify_staff = dest_usr WHERE notify_staff = src_usr;
255
256     UPDATE action.in_house_use SET staff = dest_usr WHERE staff = src_usr;
257     UPDATE action.non_cataloged_circulation SET staff = dest_usr WHERE staff = src_usr;
258     UPDATE action.non_cataloged_circulation SET patron = dest_usr WHERE patron = src_usr;
259     UPDATE action.non_cat_in_house_use SET staff = dest_usr WHERE staff = src_usr;
260     UPDATE action.survey_response SET usr = dest_usr WHERE usr = src_usr;
261
262     -- acq.*
263     UPDATE acq.fund_allocation SET allocator = dest_usr WHERE allocator = src_usr;
264         UPDATE acq.fund_transfer SET transfer_user = dest_usr WHERE transfer_user = src_usr;
265
266         -- transfer picklists the same way we transfer buckets (see above)
267         FOR picklist_row in
268                 SELECT id, name
269                 FROM   acq.picklist
270                 WHERE  owner = src_usr
271         LOOP
272                 suffix := ' (' || src_usr || ')';
273                 LOOP
274                         BEGIN
275                                 UPDATE  acq.picklist
276                                 SET     owner = dest_usr, name = name || suffix
277                                 WHERE   id = picklist_row.id;
278                         EXCEPTION WHEN unique_violation THEN
279                                 suffix := suffix || ' ';
280                                 CONTINUE;
281                         END;
282                         EXIT;
283                 END LOOP;
284         END LOOP;
285
286     UPDATE acq.purchase_order SET owner = dest_usr WHERE owner = src_usr;
287     UPDATE acq.po_note SET creator = dest_usr WHERE creator = src_usr;
288     UPDATE acq.po_note SET editor = dest_usr WHERE editor = src_usr;
289     UPDATE acq.provider_note SET creator = dest_usr WHERE creator = src_usr;
290     UPDATE acq.provider_note SET editor = dest_usr WHERE editor = src_usr;
291     UPDATE acq.lineitem_note SET creator = dest_usr WHERE creator = src_usr;
292     UPDATE acq.lineitem_note SET editor = dest_usr WHERE editor = src_usr;
293     UPDATE acq.lineitem_usr_attr_definition SET usr = dest_usr WHERE usr = src_usr;
294
295     -- asset.*
296     UPDATE asset.copy SET creator = dest_usr WHERE creator = src_usr;
297     UPDATE asset.copy SET editor = dest_usr WHERE editor = src_usr;
298     UPDATE asset.copy_note SET creator = dest_usr WHERE creator = src_usr;
299     UPDATE asset.call_number SET creator = dest_usr WHERE creator = src_usr;
300     UPDATE asset.call_number SET editor = dest_usr WHERE editor = src_usr;
301     UPDATE asset.call_number_note SET creator = dest_usr WHERE creator = src_usr;
302
303     -- serial.*
304     UPDATE serial.record_entry SET creator = dest_usr WHERE creator = src_usr;
305     UPDATE serial.record_entry SET editor = dest_usr WHERE editor = src_usr;
306
307     -- reporter.*
308     -- It's not uncommon to define the reporter schema in a replica 
309     -- DB only, so don't assume these tables exist in the write DB.
310     BEGIN
311         UPDATE reporter.template SET owner = dest_usr WHERE owner = src_usr;
312     EXCEPTION WHEN undefined_table THEN
313         -- do nothing
314     END;
315     BEGIN
316         UPDATE reporter.report SET owner = dest_usr WHERE owner = src_usr;
317     EXCEPTION WHEN undefined_table THEN
318         -- do nothing
319     END;
320     BEGIN
321         UPDATE reporter.schedule SET runner = dest_usr WHERE runner = src_usr;
322     EXCEPTION WHEN undefined_table THEN
323         -- do nothing
324     END;
325     BEGIN
326                 -- transfer folders the same way we transfer buckets (see above)
327                 FOR folder_row in
328                         SELECT id, name
329                         FROM   reporter.template_folder
330                         WHERE  owner = src_usr
331                 LOOP
332                         suffix := ' (' || src_usr || ')';
333                         LOOP
334                                 BEGIN
335                                         UPDATE  reporter.template_folder
336                                         SET     owner = dest_usr, name = name || suffix
337                                         WHERE   id = folder_row.id;
338                                 EXCEPTION WHEN unique_violation THEN
339                                         suffix := suffix || ' ';
340                                         CONTINUE;
341                                 END;
342                                 EXIT;
343                         END LOOP;
344                 END LOOP;
345     EXCEPTION WHEN undefined_table THEN
346         -- do nothing
347     END;
348     BEGIN
349                 -- transfer folders the same way we transfer buckets (see above)
350                 FOR folder_row in
351                         SELECT id, name
352                         FROM   reporter.report_folder
353                         WHERE  owner = src_usr
354                 LOOP
355                         suffix := ' (' || src_usr || ')';
356                         LOOP
357                                 BEGIN
358                                         UPDATE  reporter.report_folder
359                                         SET     owner = dest_usr, name = name || suffix
360                                         WHERE   id = folder_row.id;
361                                 EXCEPTION WHEN unique_violation THEN
362                                         suffix := suffix || ' ';
363                                         CONTINUE;
364                                 END;
365                                 EXIT;
366                         END LOOP;
367                 END LOOP;
368     EXCEPTION WHEN undefined_table THEN
369         -- do nothing
370     END;
371     BEGIN
372                 -- transfer folders the same way we transfer buckets (see above)
373                 FOR folder_row in
374                         SELECT id, name
375                         FROM   reporter.output_folder
376                         WHERE  owner = src_usr
377                 LOOP
378                         suffix := ' (' || src_usr || ')';
379                         LOOP
380                                 BEGIN
381                                         UPDATE  reporter.output_folder
382                                         SET     owner = dest_usr, name = name || suffix
383                                         WHERE   id = folder_row.id;
384                                 EXCEPTION WHEN unique_violation THEN
385                                         suffix := suffix || ' ';
386                                         CONTINUE;
387                                 END;
388                                 EXIT;
389                         END LOOP;
390                 END LOOP;
391     EXCEPTION WHEN undefined_table THEN
392         -- do nothing
393     END;
394
395     -- propagate preferred name values from the source user to the
396     -- destination user, but only when values are not being replaced.
397     WITH susr AS (SELECT * FROM actor.usr WHERE id = src_usr)
398     UPDATE actor.usr SET 
399         pref_prefix = 
400             COALESCE(pref_prefix, (SELECT pref_prefix FROM susr)),
401         pref_first_given_name = 
402             COALESCE(pref_first_given_name, (SELECT pref_first_given_name FROM susr)),
403         pref_second_given_name = 
404             COALESCE(pref_second_given_name, (SELECT pref_second_given_name FROM susr)),
405         pref_family_name = 
406             COALESCE(pref_family_name, (SELECT pref_family_name FROM susr)),
407         pref_suffix = 
408             COALESCE(pref_suffix, (SELECT pref_suffix FROM susr))
409     WHERE id = dest_usr;
410
411     -- Copy and deduplicate name keywords
412     -- String -> array -> rows -> DISTINCT -> array -> string
413     WITH susr AS (SELECT * FROM actor.usr WHERE id = src_usr),
414          dusr AS (SELECT * FROM actor.usr WHERE id = dest_usr)
415     UPDATE actor.usr SET name_keywords = (
416         WITH keywords AS (
417             SELECT DISTINCT UNNEST(
418                 REGEXP_SPLIT_TO_ARRAY(
419                     COALESCE((SELECT name_keywords FROM susr), '') || ' ' ||
420                     COALESCE((SELECT name_keywords FROM dusr), ''),  E'\\s+'
421                 )
422             ) AS parts
423         ) SELECT ARRAY_TO_STRING(ARRAY_AGG(kw.parts), ' ') FROM keywords kw
424     ) WHERE id = dest_usr;
425
426     -- Finally, delete the source user
427     DELETE FROM actor.usr WHERE id = src_usr;
428
429 END;
430 $$ LANGUAGE plpgsql;
431
432
433 COMMIT;
434