]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/src/sql/Pg/version-upgrade/2.7.2-2.7.3-upgrade-db.sql
LP#1772028 Add some FK violation functions just in case they are missing
[Evergreen.git] / Open-ILS / src / sql / Pg / version-upgrade / 2.7.2-2.7.3-upgrade-db.sql
1 --Upgrade Script for 2.7.2 to 2.7.3
2 \set eg_version '''2.7.3'''
3 BEGIN;
4 INSERT INTO config.upgrade_log (version, applied_to) VALUES ('2.7.3', :eg_version);
5
6 SELECT evergreen.upgrade_deps_block_check('0901', :eg_version);
7
8 CREATE OR REPLACE FUNCTION actor.usr_purge_data(
9         src_usr  IN INTEGER,
10         specified_dest_usr IN INTEGER
11 ) RETURNS VOID AS $$
12 DECLARE
13         suffix TEXT;
14         renamable_row RECORD;
15         dest_usr INTEGER;
16 BEGIN
17
18         IF specified_dest_usr IS NULL THEN
19                 dest_usr := 1; -- Admin user on stock installs
20         ELSE
21                 dest_usr := specified_dest_usr;
22         END IF;
23
24         -- acq.*
25         UPDATE acq.fund_allocation SET allocator = dest_usr WHERE allocator = src_usr;
26         UPDATE acq.lineitem SET creator = dest_usr WHERE creator = src_usr;
27         UPDATE acq.lineitem SET editor = dest_usr WHERE editor = src_usr;
28         UPDATE acq.lineitem SET selector = dest_usr WHERE selector = src_usr;
29         UPDATE acq.lineitem_note SET creator = dest_usr WHERE creator = src_usr;
30         UPDATE acq.lineitem_note SET editor = dest_usr WHERE editor = src_usr;
31         DELETE FROM acq.lineitem_usr_attr_definition WHERE usr = src_usr;
32
33         -- Update with a rename to avoid collisions
34         FOR renamable_row in
35                 SELECT id, name
36                 FROM   acq.picklist
37                 WHERE  owner = src_usr
38         LOOP
39                 suffix := ' (' || src_usr || ')';
40                 LOOP
41                         BEGIN
42                                 UPDATE  acq.picklist
43                                 SET     owner = dest_usr, name = name || suffix
44                                 WHERE   id = renamable_row.id;
45                         EXCEPTION WHEN unique_violation THEN
46                                 suffix := suffix || ' ';
47                                 CONTINUE;
48                         END;
49                         EXIT;
50                 END LOOP;
51         END LOOP;
52
53         UPDATE acq.picklist SET creator = dest_usr WHERE creator = src_usr;
54         UPDATE acq.picklist SET editor = dest_usr WHERE editor = src_usr;
55         UPDATE acq.po_note SET creator = dest_usr WHERE creator = src_usr;
56         UPDATE acq.po_note SET editor = dest_usr WHERE editor = src_usr;
57         UPDATE acq.purchase_order SET owner = dest_usr WHERE owner = src_usr;
58         UPDATE acq.purchase_order SET creator = dest_usr WHERE creator = src_usr;
59         UPDATE acq.purchase_order SET editor = dest_usr WHERE editor = src_usr;
60         UPDATE acq.claim_event SET creator = dest_usr WHERE creator = src_usr;
61
62         -- action.*
63         DELETE FROM action.circulation WHERE usr = src_usr;
64         UPDATE action.circulation SET circ_staff = dest_usr WHERE circ_staff = src_usr;
65         UPDATE action.circulation SET checkin_staff = dest_usr WHERE checkin_staff = src_usr;
66         UPDATE action.hold_notification SET notify_staff = dest_usr WHERE notify_staff = src_usr;
67         UPDATE action.hold_request SET fulfillment_staff = dest_usr WHERE fulfillment_staff = src_usr;
68         UPDATE action.hold_request SET requestor = dest_usr WHERE requestor = src_usr;
69         DELETE FROM action.hold_request WHERE usr = src_usr;
70         UPDATE action.in_house_use SET staff = dest_usr WHERE staff = src_usr;
71         UPDATE action.non_cat_in_house_use SET staff = dest_usr WHERE staff = src_usr;
72         DELETE FROM action.non_cataloged_circulation WHERE patron = src_usr;
73         UPDATE action.non_cataloged_circulation SET staff = dest_usr WHERE staff = src_usr;
74         DELETE FROM action.survey_response WHERE usr = src_usr;
75         UPDATE action.fieldset SET owner = dest_usr WHERE owner = src_usr;
76
77         -- actor.*
78         DELETE FROM actor.card WHERE usr = src_usr;
79         DELETE FROM actor.stat_cat_entry_usr_map WHERE target_usr = src_usr;
80
81         -- The following update is intended to avoid transient violations of a foreign
82         -- key constraint, whereby actor.usr_address references itself.  It may not be
83         -- necessary, but it does no harm.
84         UPDATE actor.usr_address SET replaces = NULL
85                 WHERE usr = src_usr AND replaces IS NOT NULL;
86         DELETE FROM actor.usr_address WHERE usr = src_usr;
87         DELETE FROM actor.usr_note WHERE usr = src_usr;
88         UPDATE actor.usr_note SET creator = dest_usr WHERE creator = src_usr;
89         DELETE FROM actor.usr_org_unit_opt_in WHERE usr = src_usr;
90         UPDATE actor.usr_org_unit_opt_in SET staff = dest_usr WHERE staff = src_usr;
91         DELETE FROM actor.usr_setting WHERE usr = src_usr;
92         DELETE FROM actor.usr_standing_penalty WHERE usr = src_usr;
93         UPDATE actor.usr_standing_penalty SET staff = dest_usr WHERE staff = src_usr;
94
95         -- asset.*
96         UPDATE asset.call_number SET creator = dest_usr WHERE creator = src_usr;
97         UPDATE asset.call_number SET editor = dest_usr WHERE editor = src_usr;
98         UPDATE asset.call_number_note SET creator = dest_usr WHERE creator = src_usr;
99         UPDATE asset.copy SET creator = dest_usr WHERE creator = src_usr;
100         UPDATE asset.copy SET editor = dest_usr WHERE editor = src_usr;
101         UPDATE asset.copy_note SET creator = dest_usr WHERE creator = src_usr;
102
103         -- auditor.*
104         DELETE FROM auditor.actor_usr_address_history WHERE id = src_usr;
105         DELETE FROM auditor.actor_usr_history WHERE id = src_usr;
106         UPDATE auditor.asset_call_number_history SET creator = dest_usr WHERE creator = src_usr;
107         UPDATE auditor.asset_call_number_history SET editor  = dest_usr WHERE editor  = src_usr;
108         UPDATE auditor.asset_copy_history SET creator = dest_usr WHERE creator = src_usr;
109         UPDATE auditor.asset_copy_history SET editor  = dest_usr WHERE editor  = src_usr;
110         UPDATE auditor.biblio_record_entry_history SET creator = dest_usr WHERE creator = src_usr;
111         UPDATE auditor.biblio_record_entry_history SET editor  = dest_usr WHERE editor  = src_usr;
112
113         -- biblio.*
114         UPDATE biblio.record_entry SET creator = dest_usr WHERE creator = src_usr;
115         UPDATE biblio.record_entry SET editor = dest_usr WHERE editor = src_usr;
116         UPDATE biblio.record_note SET creator = dest_usr WHERE creator = src_usr;
117         UPDATE biblio.record_note SET editor = dest_usr WHERE editor = src_usr;
118
119         -- container.*
120         -- Update buckets with a rename to avoid collisions
121         FOR renamable_row in
122                 SELECT id, name
123                 FROM   container.biblio_record_entry_bucket
124                 WHERE  owner = src_usr
125         LOOP
126                 suffix := ' (' || src_usr || ')';
127                 LOOP
128                         BEGIN
129                                 UPDATE  container.biblio_record_entry_bucket
130                                 SET     owner = dest_usr, name = name || suffix
131                                 WHERE   id = renamable_row.id;
132                         EXCEPTION WHEN unique_violation THEN
133                                 suffix := suffix || ' ';
134                                 CONTINUE;
135                         END;
136                         EXIT;
137                 END LOOP;
138         END LOOP;
139
140         FOR renamable_row in
141                 SELECT id, name
142                 FROM   container.call_number_bucket
143                 WHERE  owner = src_usr
144         LOOP
145                 suffix := ' (' || src_usr || ')';
146                 LOOP
147                         BEGIN
148                                 UPDATE  container.call_number_bucket
149                                 SET     owner = dest_usr, name = name || suffix
150                                 WHERE   id = renamable_row.id;
151                         EXCEPTION WHEN unique_violation THEN
152                                 suffix := suffix || ' ';
153                                 CONTINUE;
154                         END;
155                         EXIT;
156                 END LOOP;
157         END LOOP;
158
159         FOR renamable_row in
160                 SELECT id, name
161                 FROM   container.copy_bucket
162                 WHERE  owner = src_usr
163         LOOP
164                 suffix := ' (' || src_usr || ')';
165                 LOOP
166                         BEGIN
167                                 UPDATE  container.copy_bucket
168                                 SET     owner = dest_usr, name = name || suffix
169                                 WHERE   id = renamable_row.id;
170                         EXCEPTION WHEN unique_violation THEN
171                                 suffix := suffix || ' ';
172                                 CONTINUE;
173                         END;
174                         EXIT;
175                 END LOOP;
176         END LOOP;
177
178         FOR renamable_row in
179                 SELECT id, name
180                 FROM   container.user_bucket
181                 WHERE  owner = src_usr
182         LOOP
183                 suffix := ' (' || src_usr || ')';
184                 LOOP
185                         BEGIN
186                                 UPDATE  container.user_bucket
187                                 SET     owner = dest_usr, name = name || suffix
188                                 WHERE   id = renamable_row.id;
189                         EXCEPTION WHEN unique_violation THEN
190                                 suffix := suffix || ' ';
191                                 CONTINUE;
192                         END;
193                         EXIT;
194                 END LOOP;
195         END LOOP;
196
197         DELETE FROM container.user_bucket_item WHERE target_user = src_usr;
198
199         -- money.*
200         DELETE FROM money.billable_xact WHERE usr = src_usr;
201         DELETE FROM money.collections_tracker WHERE usr = src_usr;
202         UPDATE money.collections_tracker SET collector = dest_usr WHERE collector = src_usr;
203
204         -- permission.*
205         DELETE FROM permission.usr_grp_map WHERE usr = src_usr;
206         DELETE FROM permission.usr_object_perm_map WHERE usr = src_usr;
207         DELETE FROM permission.usr_perm_map WHERE usr = src_usr;
208         DELETE FROM permission.usr_work_ou_map WHERE usr = src_usr;
209
210         -- reporter.*
211         -- Update with a rename to avoid collisions
212         BEGIN
213                 FOR renamable_row in
214                         SELECT id, name
215                         FROM   reporter.output_folder
216                         WHERE  owner = src_usr
217                 LOOP
218                         suffix := ' (' || src_usr || ')';
219                         LOOP
220                                 BEGIN
221                                         UPDATE  reporter.output_folder
222                                         SET     owner = dest_usr, name = name || suffix
223                                         WHERE   id = renamable_row.id;
224                                 EXCEPTION WHEN unique_violation THEN
225                                         suffix := suffix || ' ';
226                                         CONTINUE;
227                                 END;
228                                 EXIT;
229                         END LOOP;
230                 END LOOP;
231         EXCEPTION WHEN undefined_table THEN
232                 -- do nothing
233         END;
234
235         BEGIN
236                 UPDATE reporter.report SET owner = dest_usr WHERE owner = src_usr;
237         EXCEPTION WHEN undefined_table THEN
238                 -- do nothing
239         END;
240
241         -- Update with a rename to avoid collisions
242         BEGIN
243                 FOR renamable_row in
244                         SELECT id, name
245                         FROM   reporter.report_folder
246                         WHERE  owner = src_usr
247                 LOOP
248                         suffix := ' (' || src_usr || ')';
249                         LOOP
250                                 BEGIN
251                                         UPDATE  reporter.report_folder
252                                         SET     owner = dest_usr, name = name || suffix
253                                         WHERE   id = renamable_row.id;
254                                 EXCEPTION WHEN unique_violation THEN
255                                         suffix := suffix || ' ';
256                                         CONTINUE;
257                                 END;
258                                 EXIT;
259                         END LOOP;
260                 END LOOP;
261         EXCEPTION WHEN undefined_table THEN
262                 -- do nothing
263         END;
264
265         BEGIN
266                 UPDATE reporter.schedule SET runner = dest_usr WHERE runner = src_usr;
267         EXCEPTION WHEN undefined_table THEN
268                 -- do nothing
269         END;
270
271         BEGIN
272                 UPDATE reporter.template SET owner = dest_usr WHERE owner = src_usr;
273         EXCEPTION WHEN undefined_table THEN
274                 -- do nothing
275         END;
276
277         -- Update with a rename to avoid collisions
278         BEGIN
279                 FOR renamable_row in
280                         SELECT id, name
281                         FROM   reporter.template_folder
282                         WHERE  owner = src_usr
283                 LOOP
284                         suffix := ' (' || src_usr || ')';
285                         LOOP
286                                 BEGIN
287                                         UPDATE  reporter.template_folder
288                                         SET     owner = dest_usr, name = name || suffix
289                                         WHERE   id = renamable_row.id;
290                                 EXCEPTION WHEN unique_violation THEN
291                                         suffix := suffix || ' ';
292                                         CONTINUE;
293                                 END;
294                                 EXIT;
295                         END LOOP;
296                 END LOOP;
297         EXCEPTION WHEN undefined_table THEN
298         -- do nothing
299         END;
300
301         -- vandelay.*
302         -- Update with a rename to avoid collisions
303         FOR renamable_row in
304                 SELECT id, name
305                 FROM   vandelay.queue
306                 WHERE  owner = src_usr
307         LOOP
308                 suffix := ' (' || src_usr || ')';
309                 LOOP
310                         BEGIN
311                                 UPDATE  vandelay.queue
312                                 SET     owner = dest_usr, name = name || suffix
313                                 WHERE   id = renamable_row.id;
314                         EXCEPTION WHEN unique_violation THEN
315                                 suffix := suffix || ' ';
316                                 CONTINUE;
317                         END;
318                         EXIT;
319                 END LOOP;
320         END LOOP;
321
322     -- NULL-ify addresses last so other cleanup (e.g. circ anonymization)
323     -- can access the information before deletion.
324         UPDATE actor.usr SET
325                 active = FALSE,
326                 card = NULL,
327                 mailing_address = NULL,
328                 billing_address = NULL
329         WHERE id = src_usr;
330
331 END;
332 $$ LANGUAGE plpgsql;
333
334
335 -- Evergreen DB patch 0904.schema.allow_spaces_as_ff_attr_values.sql
336 --
337 -- LP#1414112 - don't over-normalize record attribute values to
338 --              exclude all values that contain only blanks
339 --
340
341 -- check whether patch can be applied
342 SELECT evergreen.upgrade_deps_block_check('0904', :eg_version);
343
344 -- The code for "uncoded" audience is a space, but upgraded DBs may have a blank.
345 UPDATE  config.coded_value_map
346   SET   code = ' '
347   WHERE ctype = 'audience'
348         AND code = ''
349         AND (
350           SELECT  COUNT(*)
351             FROM  config.coded_value_map
352             WHERE ctype = 'audience'
353             AND code = ' ') = 0;
354
355 WITH ccvm AS (
356     SELECT  id
357       FROM  config.coded_value_map
358       WHERE ctype = 'audience'
359             AND code = ' '
360 ) UPDATE  metabib.record_attr_vector_list
361     SET   vlist = vlist + intset(ccvm.id)
362     FROM ccvm
363     WHERE source IN (
364             SELECT  record
365               FROM  metabib.real_full_rec
366               WHERE tag = '008'
367                     AND substring(value,23,1) = ' '
368           );
369
370 CREATE OR REPLACE FUNCTION metabib.reingest_record_attributes (rid BIGINT, pattr_list TEXT[] DEFAULT NULL, prmarc TEXT DEFAULT NULL, rdeleted BOOL DEFAULT TRUE) RETURNS VOID AS $func$
371 DECLARE
372     transformed_xml TEXT;
373     rmarc           TEXT := prmarc;
374     tmp_val         TEXT;
375     prev_xfrm       TEXT;
376     normalizer      RECORD;
377     xfrm            config.xml_transform%ROWTYPE;
378     attr_vector     INT[] := '{}'::INT[];
379     attr_vector_tmp INT[];
380     attr_list       TEXT[] := pattr_list;
381     attr_value      TEXT[];
382     norm_attr_value TEXT[];
383     tmp_xml         TEXT;
384     attr_def        config.record_attr_definition%ROWTYPE;
385     ccvm_row        config.coded_value_map%ROWTYPE;
386 BEGIN
387
388     IF attr_list IS NULL OR rdeleted THEN -- need to do the full dance on INSERT or undelete
389         SELECT ARRAY_AGG(name) INTO attr_list FROM config.record_attr_definition;
390     END IF;
391
392     IF rmarc IS NULL THEN
393         SELECT marc INTO rmarc FROM biblio.record_entry WHERE id = rid;
394     END IF;
395
396     FOR attr_def IN SELECT * FROM config.record_attr_definition WHERE NOT composite AND name = ANY( attr_list ) ORDER BY format LOOP
397
398         attr_value := '{}'::TEXT[];
399         norm_attr_value := '{}'::TEXT[];
400         attr_vector_tmp := '{}'::INT[];
401
402         SELECT * INTO ccvm_row FROM config.coded_value_map c WHERE c.ctype = attr_def.name LIMIT 1; 
403
404         -- tag+sf attrs only support SVF
405         IF attr_def.tag IS NOT NULL THEN -- tag (and optional subfield list) selection
406             SELECT  ARRAY[ARRAY_TO_STRING(ARRAY_AGG(value), COALESCE(attr_def.joiner,' '))] INTO attr_value
407               FROM  (SELECT * FROM metabib.full_rec ORDER BY tag, subfield) AS x
408               WHERE record = rid
409                     AND tag LIKE attr_def.tag
410                     AND CASE
411                         WHEN attr_def.sf_list IS NOT NULL 
412                             THEN POSITION(subfield IN attr_def.sf_list) > 0
413                         ELSE TRUE
414                     END
415               GROUP BY tag
416               ORDER BY tag
417               LIMIT 1;
418
419         ELSIF attr_def.fixed_field IS NOT NULL THEN -- a named fixed field, see config.marc21_ff_pos_map.fixed_field
420             attr_value := vandelay.marc21_extract_fixed_field_list(rmarc, attr_def.fixed_field);
421
422             IF NOT attr_def.multi THEN
423                 attr_value := ARRAY[attr_value[1]];
424             END IF;
425
426         ELSIF attr_def.xpath IS NOT NULL THEN -- and xpath expression
427
428             SELECT INTO xfrm * FROM config.xml_transform WHERE name = attr_def.format;
429         
430             -- See if we can skip the XSLT ... it's expensive
431             IF prev_xfrm IS NULL OR prev_xfrm <> xfrm.name THEN
432                 -- Can't skip the transform
433                 IF xfrm.xslt <> '---' THEN
434                     transformed_xml := oils_xslt_process(rmarc,xfrm.xslt);
435                 ELSE
436                     transformed_xml := rmarc;
437                 END IF;
438     
439                 prev_xfrm := xfrm.name;
440             END IF;
441
442             IF xfrm.name IS NULL THEN
443                 -- just grab the marcxml (empty) transform
444                 SELECT INTO xfrm * FROM config.xml_transform WHERE xslt = '---' LIMIT 1;
445                 prev_xfrm := xfrm.name;
446             END IF;
447
448             FOR tmp_xml IN SELECT oils_xpath(attr_def.xpath, transformed_xml, ARRAY[ARRAY[xfrm.prefix, xfrm.namespace_uri]]) LOOP
449                 tmp_val := oils_xpath_string(
450                                 '//*',
451                                 tmp_xml,
452                                 COALESCE(attr_def.joiner,' '),
453                                 ARRAY[ARRAY[xfrm.prefix, xfrm.namespace_uri]]
454                             );
455                 IF tmp_val IS NOT NULL AND BTRIM(tmp_val) <> '' THEN
456                     attr_value := attr_value || tmp_val;
457                     EXIT WHEN NOT attr_def.multi;
458                 END IF;
459             END LOOP;
460
461         ELSIF attr_def.phys_char_sf IS NOT NULL THEN -- a named Physical Characteristic, see config.marc21_physical_characteristic_*_map
462             SELECT  ARRAY_AGG(m.value) INTO attr_value
463               FROM  vandelay.marc21_physical_characteristics(rmarc) v
464                     LEFT JOIN config.marc21_physical_characteristic_value_map m ON (m.id = v.value)
465               WHERE v.subfield = attr_def.phys_char_sf AND (m.value IS NOT NULL AND BTRIM(m.value) <> '')
466                     AND ( ccvm_row.id IS NULL OR ( ccvm_row.id IS NOT NULL AND v.id IS NOT NULL) );
467
468             IF NOT attr_def.multi THEN
469                 attr_value := ARRAY[attr_value[1]];
470             END IF;
471
472         END IF;
473
474                 -- apply index normalizers to attr_value
475         FOR tmp_val IN SELECT value FROM UNNEST(attr_value) x(value) LOOP
476             FOR normalizer IN
477                 SELECT  n.func AS func,
478                         n.param_count AS param_count,
479                         m.params AS params
480                   FROM  config.index_normalizer n
481                         JOIN config.record_attr_index_norm_map m ON (m.norm = n.id)
482                   WHERE attr = attr_def.name
483                   ORDER BY m.pos LOOP
484                     EXECUTE 'SELECT ' || normalizer.func || '(' ||
485                     COALESCE( quote_literal( tmp_val ), 'NULL' ) ||
486                         CASE
487                             WHEN normalizer.param_count > 0
488                                 THEN ',' || REPLACE(REPLACE(BTRIM(normalizer.params,'[]'),E'\'',E'\\\''),E'"',E'\'')
489                                 ELSE ''
490                             END ||
491                     ')' INTO tmp_val;
492
493             END LOOP;
494             IF tmp_val IS NOT NULL AND tmp_val <> '' THEN
495                 -- note that a string that contains only blanks
496                 -- is a valid value for some attributes
497                 norm_attr_value := norm_attr_value || tmp_val;
498             END IF;
499         END LOOP;
500         
501         IF attr_def.filter THEN
502             -- Create unknown uncontrolled values and find the IDs of the values
503             IF ccvm_row.id IS NULL THEN
504                 FOR tmp_val IN SELECT value FROM UNNEST(norm_attr_value) x(value) LOOP
505                     IF tmp_val IS NOT NULL AND BTRIM(tmp_val) <> '' THEN
506                         BEGIN -- use subtransaction to isolate unique constraint violations
507                             INSERT INTO metabib.uncontrolled_record_attr_value ( attr, value ) VALUES ( attr_def.name, tmp_val );
508                         EXCEPTION WHEN unique_violation THEN END;
509                     END IF;
510                 END LOOP;
511
512                 SELECT ARRAY_AGG(id) INTO attr_vector_tmp FROM metabib.uncontrolled_record_attr_value WHERE attr = attr_def.name AND value = ANY( norm_attr_value );
513             ELSE
514                 SELECT ARRAY_AGG(id) INTO attr_vector_tmp FROM config.coded_value_map WHERE ctype = attr_def.name AND code = ANY( norm_attr_value );
515             END IF;
516
517             -- Add the new value to the vector
518             attr_vector := attr_vector || attr_vector_tmp;
519         END IF;
520
521         IF attr_def.sorter AND norm_attr_value[1] IS NOT NULL THEN
522             DELETE FROM metabib.record_sorter WHERE source = rid AND attr = attr_def.name;
523             INSERT INTO metabib.record_sorter (source, attr, value) VALUES (rid, attr_def.name, norm_attr_value[1]);
524         END IF;
525
526     END LOOP;
527
528 /* We may need to rewrite the vlist to contain
529    the intersection of new values for requested
530    attrs and old values for ignored attrs. To
531    do this, we take the old attr vlist and
532    subtract any values that are valid for the
533    requested attrs, and then add back the new
534    set of attr values. */
535
536     IF ARRAY_LENGTH(pattr_list, 1) > 0 THEN 
537         SELECT vlist INTO attr_vector_tmp FROM metabib.record_attr_vector_list WHERE source = rid;
538         SELECT attr_vector_tmp - ARRAY_AGG(id::INT) INTO attr_vector_tmp FROM metabib.full_attr_id_map WHERE attr = ANY (pattr_list);
539         attr_vector := attr_vector || attr_vector_tmp;
540     END IF;
541
542     -- On to composite attributes, now that the record attrs have been pulled.  Processed in name order, so later composite
543     -- attributes can depend on earlier ones.
544     PERFORM metabib.compile_composite_attr_cache_init();
545     FOR attr_def IN SELECT * FROM config.record_attr_definition WHERE composite AND name = ANY( attr_list ) ORDER BY name LOOP
546
547         FOR ccvm_row IN SELECT * FROM config.coded_value_map c WHERE c.ctype = attr_def.name ORDER BY value LOOP
548
549             tmp_val := metabib.compile_composite_attr( ccvm_row.id );
550             CONTINUE WHEN tmp_val IS NULL OR tmp_val = ''; -- nothing to do
551
552             IF attr_def.filter THEN
553                 IF attr_vector @@ tmp_val::query_int THEN
554                     attr_vector = attr_vector + intset(ccvm_row.id);
555                     EXIT WHEN NOT attr_def.multi;
556                 END IF;
557             END IF;
558
559             IF attr_def.sorter THEN
560                 IF attr_vector @@ tmp_val THEN
561                     DELETE FROM metabib.record_sorter WHERE source = rid AND attr = attr_def.name;
562                     INSERT INTO metabib.record_sorter (source, attr, value) VALUES (rid, attr_def.name, ccvm_row.code);
563                 END IF;
564             END IF;
565
566         END LOOP;
567
568     END LOOP;
569
570     IF ARRAY_LENGTH(attr_vector, 1) > 0 THEN
571         IF rdeleted THEN -- initial insert OR revivication
572             DELETE FROM metabib.record_attr_vector_list WHERE source = rid;
573             INSERT INTO metabib.record_attr_vector_list (source, vlist) VALUES (rid, attr_vector);
574         ELSE
575             UPDATE metabib.record_attr_vector_list SET vlist = attr_vector WHERE source = rid;
576         END IF;
577     END IF;
578
579 END;
580
581 $func$ LANGUAGE PLPGSQL;
582
583 -- Evergreen DB patch 0905.schema.use_current_normalize_heading.sql
584 --
585 -- LP#1415572: ensure current version of authority.normalize_heading() is in place
586 --
587
588
589 -- check whether patch can be applied
590 SELECT evergreen.upgrade_deps_block_check('0905', :eg_version);
591
592 CREATE OR REPLACE FUNCTION authority.normalize_heading( marcxml TEXT, no_thesaurus BOOL ) RETURNS TEXT AS $func$
593 DECLARE
594     acsaf           authority.control_set_authority_field%ROWTYPE;
595     tag_used        TEXT;
596     nfi_used        TEXT;
597     sf              TEXT;
598     sf_node         TEXT;
599     tag_node        TEXT;
600     thes_code       TEXT;
601     cset            INT;
602     heading_text    TEXT;
603     tmp_text        TEXT;
604     first_sf        BOOL;
605     auth_id         INT DEFAULT COALESCE(NULLIF(oils_xpath_string('//*[@tag="901"]/*[local-name()="subfield" and @code="c"]', marcxml), ''), '0')::INT; 
606 BEGIN
607     SELECT control_set INTO cset FROM authority.record_entry WHERE id = auth_id;
608
609     IF cset IS NULL THEN
610         SELECT  control_set INTO cset
611           FROM  authority.control_set_authority_field
612           WHERE tag IN ( SELECT  UNNEST(XPATH('//*[starts-with(@tag,"1")]/@tag',marcxml::XML)::TEXT[]))
613           LIMIT 1;
614     END IF;
615
616     thes_code := vandelay.marc21_extract_fixed_field(marcxml,'Subj');
617     IF thes_code IS NULL THEN
618         thes_code := '|';
619     ELSIF thes_code = 'z' THEN
620         thes_code := COALESCE( oils_xpath_string('//*[@tag="040"]/*[@code="f"][1]', marcxml), '' );
621     END IF;
622
623     heading_text := '';
624     FOR acsaf IN SELECT * FROM authority.control_set_authority_field WHERE control_set = cset AND main_entry IS NULL LOOP
625         tag_used := acsaf.tag;
626         nfi_used := acsaf.nfi;
627         first_sf := TRUE;
628
629         FOR tag_node IN SELECT unnest(oils_xpath('//*[@tag="'||tag_used||'"]',marcxml)) LOOP
630             FOR sf_node IN SELECT unnest(oils_xpath('./*[contains("'||acsaf.sf_list||'",@code)]',tag_node)) LOOP
631
632                 tmp_text := oils_xpath_string('.', sf_node);
633                 sf := oils_xpath_string('./@code', sf_node);
634
635                 IF first_sf AND tmp_text IS NOT NULL AND nfi_used IS NOT NULL THEN
636
637                     tmp_text := SUBSTRING(
638                         tmp_text FROM
639                         COALESCE(
640                             NULLIF(
641                                 REGEXP_REPLACE(
642                                     oils_xpath_string('./@ind'||nfi_used, tag_node),
643                                     $$\D+$$,
644                                     '',
645                                     'g'
646                                 ),
647                                 ''
648                             )::INT,
649                             0
650                         ) + 1
651                     );
652
653                 END IF;
654
655                 first_sf := FALSE;
656
657                 IF tmp_text IS NOT NULL AND tmp_text <> '' THEN
658                     heading_text := heading_text || E'\u2021' || sf || ' ' || tmp_text;
659                 END IF;
660             END LOOP;
661
662             EXIT WHEN heading_text <> '';
663         END LOOP;
664
665         EXIT WHEN heading_text <> '';
666     END LOOP;
667
668     IF heading_text <> '' THEN
669         IF no_thesaurus IS TRUE THEN
670             heading_text := tag_used || ' ' || public.naco_normalize(heading_text);
671         ELSE
672             heading_text := tag_used || '_' || COALESCE(nfi_used,'-') || '_' || thes_code || ' ' || public.naco_normalize(heading_text);
673         END IF;
674     ELSE
675         heading_text := 'NOHEADING_' || thes_code || ' ' || MD5(marcxml);
676     END IF;
677
678     RETURN heading_text;
679 END;
680 $func$ LANGUAGE PLPGSQL STABLE STRICT;
681
682 -- fix heading and simple_headings columns without
683 -- causing a full authority reingest
684 ALTER TABLE authority.record_entry DISABLE TRIGGER a_marcxml_is_well_formed;
685 ALTER TABLE authority.record_entry DISABLE TRIGGER aaa_auth_ingest_or_delete;
686 ALTER TABLE authority.record_entry DISABLE TRIGGER b_maintain_901;
687 ALTER TABLE authority.record_entry DISABLE TRIGGER c_maintain_control_numbers;
688 ALTER TABLE authority.record_entry DISABLE TRIGGER map_thesaurus_to_control_set;
689
690 UPDATE authority.record_entry SET id = id WHERE heading LIKE 'NOHEADING%';
691
692
693 -- check whether patch can be applied
694 SELECT evergreen.upgrade_deps_block_check('0906', :eg_version);
695
696 ALTER FUNCTION evergreen.z3950_attr_name_is_valid (TEXT) STABLE;
697
698 COMMIT;
699
700 -- These need to happen outside of the transaction to avoid this:
701 -- ERROR: cannot ALTER TABLE "record_entry" because it has pending trigger
702 -- events
703 ALTER TABLE authority.record_entry ENABLE TRIGGER a_marcxml_is_well_formed;
704 ALTER TABLE authority.record_entry ENABLE TRIGGER aaa_auth_ingest_or_delete;
705 ALTER TABLE authority.record_entry ENABLE TRIGGER b_maintain_901;
706 ALTER TABLE authority.record_entry ENABLE TRIGGER c_maintain_control_numbers;
707 ALTER TABLE authority.record_entry ENABLE TRIGGER map_thesaurus_to_control_set;
708
709 \qecho
710 \qecho
711 \qecho **** Certain improvements in this upgrade series require a partial reingest of
712 \qecho **** your bib records.  In order to allow this to continue without locking
713 \qecho **** your entire bibliographic data set, consider generating an SQL script
714 \qecho **** with the following queries:
715 \qecho
716 \qecho
717 \qecho '\\t'
718 \qecho '\\o /tmp/partial_reingest_bib_recs.sql'
719 \qecho 'SELECT ''select metabib.reingest_record_attributes('' || id || '');'' FROM biblio.record_entry WHERE NOT DELETED AND id > 0;'
720 \qecho '\\o'
721 \qecho '\\t'
722 \qecho
723 \qecho
724 \qecho **** then running it via psql:
725 \qecho
726 \qecho
727 \qecho '\\i /tmp/partial_reingest_bib_recs.sql'
728 \qecho
729 \qecho
730 \qecho **** If you require a more responsive catalog/database while reingesting,
731 \qecho **** consider adding 'pg_sleep()' calls between each reingest select or
732 \qecho **** update.