]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/src/sql/Pg/version-upgrade/2.11.3-2.12.0-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.11.3-2.12.0-upgrade-db.sql
1 --Upgrade Script for 2.11.3 to 2.12.0
2 \set eg_version '''2.12.0'''
3 BEGIN;
4
5 SELECT evergreen.upgrade_deps_block_check('1006', :eg_version);
6
7 -- This function is used to help clean up facet labels. Due to quirks in
8 -- MARC parsing, some facet labels may be generated with periods or commas
9 -- at the end.  This will strip a trailing commas off all the time, and
10 -- periods when they don't look like they are part of initials.
11 --      Smith, John    =>  no change
12 --      Smith, John,   =>  Smith, John
13 --      Smith, John.   =>  Smith, John
14 --      Public, John Q. => no change
15 CREATE OR REPLACE FUNCTION metabib.trim_trailing_punctuation ( TEXT ) RETURNS TEXT AS $$
16 DECLARE
17     result    TEXT;
18     last_char TEXT;
19 BEGIN
20     result := $1;
21     last_char = substring(result from '.$');
22
23     IF last_char = ',' THEN
24         result := substring(result from '^(.*),$');
25
26     ELSIF last_char = '.' THEN
27         IF substring(result from ' \w\.$') IS NULL THEN
28             result := substring(result from '^(.*)\.$');
29         END IF;
30     END IF;
31
32     RETURN result;
33
34 END;
35 $$ language 'plpgsql';
36
37 INSERT INTO config.index_normalizer (name, description, func, param_count) VALUES (
38         'Trim Trailing Punctuation',
39         'Eliminate extraneous trailing commas and periods in text',
40         'metabib.trim_trailing_punctuation',
41         0
42 );
43
44 INSERT INTO config.metabib_field_index_norm_map (field,norm,pos)
45     SELECT  m.id,
46             i.id,
47             -1
48       FROM  config.metabib_field m,
49             config.index_normalizer i
50       WHERE i.func = 'metabib.trim_trailing_punctuation'
51             AND m.id IN (7,8,9,10);
52
53 SELECT evergreen.upgrade_deps_block_check('1007', :eg_version);
54
55 UPDATE config.record_attr_definition
56 SET description = oils_i18n_gettext('audience', 'Audience', 'crad', 'label')
57 WHERE description IS NULL
58 AND name = 'audience';
59 UPDATE config.record_attr_definition
60 SET description = oils_i18n_gettext('bib_level', 'Bib Level', 'crad', 'label')
61 WHERE description IS NULL
62 AND name = 'bib_level';
63 UPDATE config.record_attr_definition
64 SET description = oils_i18n_gettext('item_form', 'Item Form', 'crad', 'label')
65 WHERE description IS NULL
66 AND name = 'item_form';
67 UPDATE config.record_attr_definition
68 SET description = oils_i18n_gettext('item_lang', 'Language', 'crad', 'label')
69 WHERE description IS NULL
70 AND name = 'item_lang';
71 UPDATE config.record_attr_definition
72 SET description = oils_i18n_gettext('lit_form', 'Literary Form', 'crad', 'label')
73 WHERE description IS NULL
74 AND name = 'lit_form';
75 UPDATE config.record_attr_definition
76 SET description = oils_i18n_gettext('item_type', 'Item Type', 'crad', 'label')
77 WHERE description IS NULL
78 AND name = 'item_type';
79 UPDATE config.record_attr_definition
80 SET description = oils_i18n_gettext('vr_format', 'Video Format', 'crad', 'label')
81 WHERE description IS NULL
82 AND name = 'vr_format';
83
84 SELECT evergreen.upgrade_deps_block_check('1008', :eg_version);
85
86 CREATE OR REPLACE FUNCTION evergreen.unaccent_and_squash ( IN arg text) RETURNS text
87     IMMUTABLE STRICT AS $$
88         BEGIN
89         RETURN evergreen.lowercase(unaccent(regexp_replace(arg, '[\s[:punct:]]','','g')));
90         END;
91 $$ LANGUAGE PLPGSQL;
92
93 SELECT evergreen.upgrade_deps_block_check('1009', :eg_version);
94
95 INSERT into config.org_unit_setting_type
96 ( name, grp, label, description, datatype, fm_class ) VALUES
97 ( 'acq.copy_status_on_receiving', 'acq',
98     oils_i18n_gettext('acq.copy_status_on_receiving',
99         'Initial status for received items',
100         'coust', 'label'),
101     oils_i18n_gettext('acq.copy_status_on_receiving',
102         'Allows staff to designate a custom copy status on received lineitems.  Default status is "In Process".',
103         'coust', 'description'),
104     'link', 'ccs');
105
106 -- remove unused org unit setting for self checkout interface
107 SELECT evergreen.upgrade_deps_block_check('1010', :eg_version);
108
109 DELETE FROM actor.org_unit_setting WHERE name = 'circ.selfcheck.require_patron_password';
110
111 DELETE FROM config.org_unit_setting_type WHERE name = 'circ.selfcheck.require_patron_password';
112
113 DELETE FROM config.org_unit_setting_type_log WHERE field_name = 'circ.selfcheck.require_patron_password';
114
115 DELETE FROM permission.usr_perm_map WHERE perm IN (SELECT id FROM permission.perm_list WHERE code = 'UPDATE_ORG_UNIT_SETTING.circ.selfcheck.require_patron_password');
116
117 DELETE FROM permission.grp_perm_map WHERE perm IN (SELECT id FROM permission.perm_list WHERE code = 'UPDATE_ORG_UNIT_SETTING.circ.selfcheck.require_patron_password');
118
119 DELETE FROM permission.perm_list WHERE code = 'UPDATE_ORG_UNIT_SETTING.circ.selfcheck.require_patron_password';
120
121 SELECT evergreen.upgrade_deps_block_check('1011', :eg_version);
122
123 INSERT INTO config.org_unit_setting_type
124     (name, grp, label, description, datatype)
125     VALUES
126         ('circ.in_house_use.copy_alert',
127          'circ',
128          oils_i18n_gettext('circ.in_house_use.copy_alert',
129              'Display copy alert for in-house-use',
130              'coust', 'label'),
131          oils_i18n_gettext('circ.in_house_use.copy_alert',
132              'Display copy alert for in-house-use',
133              'coust', 'description'),
134          'bool'),
135         ('circ.in_house_use.checkin_alert',
136          'circ',
137          oils_i18n_gettext('circ.in_house_use.checkin_alert',
138              'Display copy location checkin alert for in-house-use',
139              'coust', 'label'),
140          oils_i18n_gettext('circ.in_house_use.checkin_alert',
141              'Display copy location checkin alert for in-house-use',
142              'coust', 'description'),
143          'bool');
144
145 SELECT evergreen.upgrade_deps_block_check('1014', :eg_version);
146 -- this update of unapi.mmr_mra() removed since 1015 has a newer version
147   
148 SELECT evergreen.upgrade_deps_block_check('1015', :eg_version);
149
150 CREATE OR REPLACE FUNCTION unapi.mmr_mra (
151     obj_id BIGINT,
152     format TEXT,
153     ename TEXT,
154     includes TEXT[],
155     org TEXT,
156     depth INT DEFAULT NULL,
157     slimit HSTORE DEFAULT NULL,
158     soffset HSTORE DEFAULT NULL,
159     include_xmlns BOOL DEFAULT TRUE,
160     pref_lib INT DEFAULT NULL
161 ) RETURNS XML AS $F$
162     SELECT  XMLELEMENT(
163         name attributes,
164         XMLATTRIBUTES(
165             CASE WHEN $9 THEN 'http://open-ils.org/spec/indexing/v1' ELSE NULL END AS xmlns,
166             'tag:open-ils.org:U2@mmr/' || $1 AS metarecord
167         ),
168         (SELECT XMLAGG(foo.y)
169           FROM (
170             WITH sourcelist AS (
171                 WITH aou AS (SELECT COALESCE(id, (evergreen.org_top()).id) AS id
172                     FROM actor.org_unit WHERE shortname = $5 LIMIT 1)
173                 SELECT source
174                 FROM metabib.metarecord_source_map mmsm, aou
175                 WHERE metarecord = $1 AND (
176                     EXISTS (
177                         SELECT 1 FROM asset.opac_visible_copies
178                         WHERE record = source AND circ_lib IN (
179                             SELECT id FROM actor.org_unit_descendants(aou.id, $6))
180                         LIMIT 1
181                     )
182                     OR EXISTS (SELECT 1 FROM located_uris(source, aou.id, $10) LIMIT 1)
183                     OR EXISTS (SELECT 1 FROM biblio.record_entry b JOIN config.bib_source src ON (b.source = src.id) WHERE src.transcendant AND b.id = mmsm.source LIMIT 1)
184                 )
185             )
186             SELECT  cmra.aid,
187                     XMLELEMENT(
188                         name field,
189                         XMLATTRIBUTES(
190                             cmra.attr AS name,
191                             cmra.value AS "coded-value",
192                             cmra.aid AS "cvmid",
193                             rad.composite,
194                             rad.multi,
195                             rad.filter,
196                             rad.sorter,
197                             cmra.source_list
198                         ),
199                         cmra.value
200                     )
201               FROM  (
202                 SELECT DISTINCT aid, attr, value, STRING_AGG(x.id::TEXT, ',') AS source_list
203                   FROM (
204                     SELECT  v.source AS id,
205                             c.id AS aid,
206                             c.ctype AS attr,
207                             c.code AS value
208                       FROM  metabib.record_attr_vector_list v
209                             JOIN config.coded_value_map c ON ( c.id = ANY( v.vlist ) )
210                     ) AS x
211                     JOIN sourcelist ON (x.id = sourcelist.source)
212                     GROUP BY 1, 2, 3
213                 ) AS cmra
214                 JOIN config.record_attr_definition rad ON (cmra.attr = rad.name)
215                 UNION ALL
216             SELECT  umra.aid,
217                     XMLELEMENT(
218                         name field,
219                         XMLATTRIBUTES(
220                             umra.attr AS name,
221                             rad.composite,
222                             rad.multi,
223                             rad.filter,
224                             rad.sorter
225                         ),
226                         umra.value
227                     )
228               FROM  (
229                 SELECT DISTINCT aid, attr, value
230                   FROM (
231                     SELECT  v.source AS id,
232                             m.id AS aid,
233                             m.attr AS attr,
234                             m.value AS value
235                       FROM  metabib.record_attr_vector_list v
236                             JOIN metabib.uncontrolled_record_attr_value m ON ( m.id = ANY( v.vlist ) )
237                     ) AS x
238                     JOIN sourcelist ON (x.id = sourcelist.source)
239                 ) AS umra
240                 JOIN config.record_attr_definition rad ON (umra.attr = rad.name)
241                 ORDER BY 1
242
243             )foo(id,y)
244         )
245     )
246 $F$ LANGUAGE SQL STABLE;
247   
248 SELECT evergreen.upgrade_deps_block_check('1016', :eg_version);
249
250 INSERT INTO config.biblio_fingerprint (name, xpath, format)
251     VALUES (
252         'PartName',
253         '//mods32:mods/mods32:titleInfo/mods32:partName',
254         'mods32'
255     );
256
257 INSERT INTO config.biblio_fingerprint (name, xpath, format)
258     VALUES (
259         'PartNumber',
260         '//mods32:mods/mods32:titleInfo/mods32:partNumber',
261         'mods32'
262     );
263
264 SELECT evergreen.upgrade_deps_block_check('1017', :eg_version);
265
266 CREATE OR REPLACE FUNCTION biblio.extract_fingerprint ( marc text ) RETURNS TEXT AS $func$
267 DECLARE
268         idx             config.biblio_fingerprint%ROWTYPE;
269         xfrm            config.xml_transform%ROWTYPE;
270         prev_xfrm       TEXT;
271         transformed_xml TEXT;
272         xml_node        TEXT;
273         xml_node_list   TEXT[];
274         raw_text        TEXT;
275     output_text TEXT := '';
276 BEGIN
277
278     IF marc IS NULL OR marc = '' THEN
279         RETURN NULL;
280     END IF;
281
282         -- Loop over the indexing entries
283         FOR idx IN SELECT * FROM config.biblio_fingerprint ORDER BY format, id LOOP
284
285                 SELECT INTO xfrm * from config.xml_transform WHERE name = idx.format;
286
287                 -- See if we can skip the XSLT ... it's expensive
288                 IF prev_xfrm IS NULL OR prev_xfrm <> xfrm.name THEN
289                         -- Can't skip the transform
290                         IF xfrm.xslt <> '---' THEN
291                                 transformed_xml := oils_xslt_process(marc,xfrm.xslt);
292                         ELSE
293                                 transformed_xml := marc;
294                         END IF;
295
296                         prev_xfrm := xfrm.name;
297                 END IF;
298
299                 raw_text := COALESCE(
300             naco_normalize(
301                 ARRAY_TO_STRING(
302                     oils_xpath(
303                         '//text()',
304                         (oils_xpath(
305                             idx.xpath,
306                             transformed_xml,
307                             ARRAY[ARRAY[xfrm.prefix, xfrm.namespace_uri]] 
308                         ))[1]
309                     ),
310                     ''
311                 )
312             ),
313             ''
314         );
315
316         raw_text := REGEXP_REPLACE(raw_text, E'\\[.+?\\]', E'');
317         raw_text := REGEXP_REPLACE(raw_text, E'\\mthe\\M|\\man?d?d\\M', E'', 'g'); -- arg! the pain!
318
319         IF idx.first_word IS TRUE THEN
320             raw_text := REGEXP_REPLACE(raw_text, E'^(\\w+).*?$', E'\\1');
321         END IF;
322
323                 output_text := output_text || idx.name || ':' ||
324                                            REGEXP_REPLACE(raw_text, E'\\s+', '', 'g') || ' ';
325
326         END LOOP;
327
328     RETURN BTRIM(output_text);
329
330 END;
331 $func$ LANGUAGE PLPGSQL;
332
333 SELECT evergreen.upgrade_deps_block_check('1019', :eg_version);
334
335 CREATE OR REPLACE FUNCTION
336     action.hold_request_regen_copy_maps(
337         hold_id INTEGER, copy_ids INTEGER[]) RETURNS VOID AS $$
338     DELETE FROM action.hold_copy_map WHERE hold = $1;
339     INSERT INTO action.hold_copy_map (hold, target_copy) SELECT $1, UNNEST($2);
340 $$ LANGUAGE SQL;
341
342 -- DATA
343
344 INSERT INTO config.global_flag (name, label, value, enabled) VALUES (
345     'circ.holds.retarget_interval',
346     oils_i18n_gettext(
347         'circ.holds.retarget_interval',
348         'Holds Retarget Interval', 
349         'cgf',
350         'label'
351     ),
352     '24h',
353     TRUE
354 );
355
356 SELECT evergreen.upgrade_deps_block_check('1020', :eg_version);
357
358 CREATE OR REPLACE FUNCTION actor.org_unit_ancestor_setting_batch_by_org(
359     setting_name TEXT, org_ids INTEGER[]) 
360     RETURNS SETOF actor.org_unit_setting AS 
361 $FUNK$
362 DECLARE
363     setting RECORD;
364     org_id INTEGER;
365 BEGIN
366     /*  Returns one actor.org_unit_setting row per org unit ID provided.
367         When no setting exists for a given org unit, the setting row
368         will contain all empty values. */
369     FOREACH org_id IN ARRAY org_ids LOOP
370         SELECT INTO setting * FROM 
371             actor.org_unit_ancestor_setting(setting_name, org_id);
372         RETURN NEXT setting;
373     END LOOP;
374     RETURN;
375 END;
376 $FUNK$ LANGUAGE plpgsql STABLE;
377
378 SELECT evergreen.upgrade_deps_block_check('1021', :eg_version);
379
380 -- Add missing permissions noted in LP 1517137 adjusting those added manually and ignoring those already in place.
381
382 DO $$
383 DECLARE fixperm TEXT[3];
384 DECLARE modify BOOLEAN;
385 DECLARE permid BIGINT;
386 DECLARE oldid BIGINT;
387 BEGIN
388
389 FOREACH fixperm SLICE 1 IN ARRAY ARRAY[
390   ['564', 'MARK_ITEM_CATALOGING', 'Allow a user to mark an item status as ''cataloging'''],
391   ['565', 'MARK_ITEM_DAMAGED', 'Allow a user to mark an item status as ''damaged'''],
392   ['566', 'MARK_ITEM_DISCARD', 'Allow a user to mark an item status as ''discard'''],
393   ['567', 'MARK_ITEM_RESERVES', 'Allow a user to mark an item status as ''reserves'''],
394   ['568', 'ADMIN_ORG_UNIT_SETTING_TYPE_LOG', 'Allow a user to modify the org unit settings log'],
395   ['570', 'CREATE_POP_BADGE', 'Allow a user to create a new popularity badge'],
396   ['571', 'DELETE_POP_BADGE', 'Allow a user to delete a popularity badge'],
397   ['572', 'UPDATE_POP_BADGE', 'Allow a user to modify a popularity badge'],
398   ['573', 'CREATE_POP_PARAMETER', 'Allow a user to create a popularity badge parameter'],
399   ['574', 'DELETE_POP_PARAMETER', 'Allow a user to delete a popularity badge parameter'],
400   ['575', 'UPDATE_POP_PARAMETER', 'Allow a user to modify a popularity badge parameter'],
401   ['576', 'CREATE_AUTHORITY_RECORD', 'Allow a user to create an authority record'],
402   ['577', 'DELETE_AUTHORITY_RECORD', 'Allow a user to delete an authority record'],
403   ['578', 'UPDATE_AUTHORITY_RECORD', 'Allow a user to modify an authority record'],
404   ['579', 'CREATE_AUTHORITY_CONTROL_SET', 'Allow a user to create an authority control set'],
405   ['580', 'DELETE_AUTHORITY_CONTROL_SET', 'Allow a user to delete an authority control set'],
406   ['581', 'UPDATE_AUTHORITY_CONTROL_SET', 'Allow a user to modify an authority control set'],
407   ['582', 'ACTOR_USER_DELETE_OPEN_XACTS.override', 'Override the ACTOR_USER_DELETE_OPEN_XACTS event'],
408   ['583', 'PATRON_EXCEEDS_LOST_COUNT.override', 'Override the PATRON_EXCEEDS_LOST_COUNT event'],
409   ['584', 'MAX_HOLDS.override', 'Override the MAX_HOLDS event'],
410   ['585', 'ITEM_DEPOSIT_REQUIRED.override', 'Override the ITEM_DEPOSIT_REQUIRED event'],
411   ['586', 'ITEM_DEPOSIT_PAID.override', 'Override the ITEM_DEPOSIT_PAID event'],
412   ['587', 'COPY_STATUS_LOST_AND_PAID.override', 'Override the COPY_STATUS_LOST_AND_PAID event'],
413   ['588', 'ITEM_NOT_HOLDABLE.override', 'Override the ITEM_NOT_HOLDABLE event'],
414   ['589', 'ITEM_RENTAL_FEE_REQUIRED.override', 'Override the ITEM_RENTAL_FEE_REQUIRED event']
415 ]
416 LOOP
417   permid := CAST (fixperm[1] AS BIGINT);
418   -- Has this permission already been manually applied at the expected id?
419   PERFORM * FROM permission.perm_list WHERE id = permid;
420   IF NOT FOUND THEN
421     UPDATE permission.perm_list SET code = code || '_local' WHERE code = fixperm[2] AND id > 1000 RETURNING id INTO oldid;
422     modify := FOUND;
423
424     INSERT INTO permission.perm_list (id, code, description) VALUES (permid, fixperm[2], fixperm[3]);
425
426     -- Several of these are rather unlikely for these particular permissions but safer > sorry.
427     IF modify THEN
428       UPDATE permission.grp_perm_map SET perm = permid WHERE perm = oldid;
429       UPDATE config.org_unit_setting_type SET update_perm = permid WHERE update_perm = oldid;
430       UPDATE permission.usr_object_perm_map SET perm = permid WHERE perm = oldid;
431       UPDATE permission.usr_perm_map SET perm = permid WHERE perm = oldid;
432       UPDATE config.org_unit_setting_type SET view_perm = permid WHERE view_perm = oldid;
433       UPDATE config.z3950_source SET use_perm = permid WHERE use_perm = oldid;
434       DELETE FROM permission.perm_list WHERE id = oldid;
435     END IF;
436   END IF;
437 END LOOP;
438
439 END$$;
440
441 SELECT evergreen.upgrade_deps_block_check('1022', :eg_version);
442
443 CREATE OR REPLACE FUNCTION vandelay.merge_record_xml_using_profile ( incoming_marc TEXT, existing_marc TEXT, merge_profile_id BIGINT ) RETURNS TEXT AS $$
444 DECLARE
445     merge_profile   vandelay.merge_profile%ROWTYPE;
446     dyn_profile     vandelay.compile_profile%ROWTYPE;
447     target_marc     TEXT;
448     source_marc     TEXT;
449     replace_rule    TEXT;
450     match_count     INT;
451 BEGIN
452
453     IF existing_marc IS NULL OR incoming_marc IS NULL THEN
454         -- RAISE NOTICE 'no marc for source or target records';
455         RETURN NULL;
456     END IF;
457
458     IF merge_profile_id IS NOT NULL THEN
459         SELECT * INTO merge_profile FROM vandelay.merge_profile WHERE id = merge_profile_id;
460         IF FOUND THEN
461             dyn_profile.add_rule := COALESCE(merge_profile.add_spec,'');
462             dyn_profile.strip_rule := COALESCE(merge_profile.strip_spec,'');
463             dyn_profile.replace_rule := COALESCE(merge_profile.replace_spec,'');
464             dyn_profile.preserve_rule := COALESCE(merge_profile.preserve_spec,'');
465         ELSE
466             -- RAISE NOTICE 'merge profile not found';
467             RETURN NULL;
468         END IF;
469     ELSE
470         -- RAISE NOTICE 'no merge profile specified';
471         RETURN NULL;
472     END IF;
473
474     IF dyn_profile.replace_rule <> '' AND dyn_profile.preserve_rule <> '' THEN
475         -- RAISE NOTICE 'both replace [%] and preserve [%] specified', dyn_profile.replace_rule, dyn_profile.preserve_rule;
476         RETURN NULL;
477     END IF;
478
479     IF dyn_profile.replace_rule = '' AND dyn_profile.preserve_rule = '' AND dyn_profile.add_rule = '' AND dyn_profile.strip_rule = '' THEN
480         -- Since we have nothing to do, just return a target record as is
481         RETURN existing_marc;
482     ELSIF dyn_profile.preserve_rule <> '' THEN
483         source_marc = existing_marc;
484         target_marc = incoming_marc;
485         replace_rule = dyn_profile.preserve_rule;
486     ELSE
487         source_marc = incoming_marc;
488         target_marc = existing_marc;
489         replace_rule = dyn_profile.replace_rule;
490     END IF;
491
492     RETURN vandelay.merge_record_xml( target_marc, source_marc, dyn_profile.add_rule, replace_rule, dyn_profile.strip_rule );
493
494 END;
495 $$ LANGUAGE PLPGSQL;
496
497 SELECT evergreen.upgrade_deps_block_check('1023', :eg_version);
498
499 INSERT into config.org_unit_setting_type
500 ( name, grp, label, description, datatype, fm_class ) VALUES
501 (
502     'cat.default_merge_profile', 'cat',
503     oils_i18n_gettext(
504         'cat.default_merge_profile',
505         'Default Merge Profile (Z39.50 and Record Buckets)',
506         'coust',
507         'label'
508     ),
509      oils_i18n_gettext(
510         'cat.default_merge_profile',
511         'Default merge profile to use during Z39.50 imports and record bucket merges',
512         'coust',
513         'description'
514     ),
515     'link',
516     'vmp'
517 );
518
519 SELECT evergreen.upgrade_deps_block_check('1024', :eg_version);
520
521 -- Add new column "rtl" with default of false
522 ALTER TABLE config.i18n_locale ADD COLUMN rtl BOOL NOT NULL DEFAULT FALSE;
523
524 SELECT evergreen.upgrade_deps_block_check('1025', :eg_version);
525
526 -- Add Arabic (Jordan) to i18n_locale table as a stock language option
527 INSERT INTO config.i18n_locale (code,marc_code,name,description,rtl)
528     VALUES ('ar-JO', 'ara', oils_i18n_gettext('ar-JO', 'Arabic (Jordan)', 'i18n_l', 'name'),
529         oils_i18n_gettext('ar-JO', 'Arabic (Jordan)', 'i18n_l', 'description'), 'true');
530
531 SELECT evergreen.upgrade_deps_block_check('1026', :eg_version);
532
533 INSERT INTO config.metabib_field ( id, field_class, name, label, 
534      format, xpath, search_field, browse_field, authority_xpath, joiner ) VALUES
535     (34, 'subject', 'topic_browse', oils_i18n_gettext(34, 'Topic Browse', 'cmf', 'label'), 
536      'mods32', $$//mods32:mods/mods32:subject[local-name(./*[1]) = "topic"]$$, FALSE, TRUE, '//@xlink:href', ' -- ' ); -- /* to fool vim */;
537
538 INSERT INTO config.metabib_field ( id, field_class, name, label, 
539      format, xpath, search_field, browse_field, authority_xpath, joiner ) VALUES
540     (35, 'subject', 'geographic_browse', oils_i18n_gettext(35, 'Geographic Name Browse', 'cmf', 'label'), 
541      'mods32', $$//mods32:mods/mods32:subject[local-name(./*[1]) = "geographic"]$$, FALSE, TRUE, '//@xlink:href', ' -- ' ); -- /* to fool vim */;
542
543 INSERT INTO config.metabib_field ( id, field_class, name, label, 
544      format, xpath, search_field, browse_field, authority_xpath, joiner ) VALUES
545     (36, 'subject', 'temporal_browse', oils_i18n_gettext(36, 'Temporal Term Browse', 'cmf', 'label'), 
546      'mods32', $$//mods32:mods/mods32:subject[local-name(./*[1]) = "temporal"]$$, FALSE, TRUE, '//@xlink:href', ' -- ' ); -- /* to fool vim */;
547
548 INSERT INTO config.metabib_field_index_norm_map (field,norm)
549     SELECT  m.id,
550             i.id
551       FROM  config.metabib_field m,
552         config.index_normalizer i
553       WHERE i.func IN ('naco_normalize')
554             AND m.id IN (34, 35, 36);
555
556 UPDATE config.metabib_field
557 SET browse_field = FALSE
558 WHERE field_class = 'subject' AND name = 'topic'
559 AND id = 14;
560 UPDATE config.metabib_field
561 SET browse_field = FALSE
562 WHERE field_class = 'subject' AND name = 'geographic'
563 AND id = 13;
564 UPDATE config.metabib_field
565 SET browse_field = FALSE
566 WHERE field_class = 'subject' AND name = 'temporal'
567 AND id = 11;
568
569 UPDATE authority.control_set_bib_field_metabib_field_map
570 SET metabib_field = 34
571 WHERE metabib_field = 14;
572 UPDATE authority.control_set_bib_field_metabib_field_map
573 SET metabib_field = 35
574 WHERE metabib_field = 13;
575 UPDATE authority.control_set_bib_field_metabib_field_map
576 SET metabib_field = 36
577 WHERE metabib_field = 11;
578
579 SELECT evergreen.upgrade_deps_block_check('1027', :eg_version);
580
581 INSERT INTO config.settings_group (name, label)
582     VALUES ('ebook_api', 'Ebook API Integration');
583
584 INSERT INTO config.org_unit_setting_type
585     (name, label, description, grp, datatype) 
586 VALUES (
587     'ebook_api.oneclickdigital.library_id',
588     oils_i18n_gettext(
589         'ebook_api.oneclickdigital.library_id',
590         'OneClickdigital Library ID',
591         'coust',
592         'label'
593     ),
594     oils_i18n_gettext(
595         'ebook_api.oneclickdigital.library_id',
596         'Identifier assigned to this library by OneClickdigital',
597         'coust',
598         'description'
599     ),
600     'ebook_api',
601     'string'
602 ),(
603     'ebook_api.oneclickdigital.basic_token',
604     oils_i18n_gettext(
605         'ebook_api.oneclickdigital.basic_token',
606         'OneClickdigital Basic Token',
607         'coust',
608         'label'
609     ),
610     oils_i18n_gettext(
611         'ebook_api.oneclickdigital.basic_token',
612         'Basic token for client authentication with OneClickdigital API (supplied by OneClickdigital)',
613         'coust',
614         'description'
615     ),
616     'ebook_api',
617     'string'
618 );
619
620 INSERT INTO config.org_unit_setting_type
621     (name, label, description, grp, datatype) 
622 VALUES (
623     'ebook_api.overdrive.discovery_base_uri',
624     oils_i18n_gettext(
625         'ebook_api.overdrive.discovery_base_uri',
626         'OverDrive Discovery API Base URI',
627         'coust',
628         'label'
629     ),
630     oils_i18n_gettext(
631         'ebook_api.overdrive.discovery_base_uri',
632         'Base URI for OverDrive Discovery API (defaults to https://api.overdrive.com/v1). Using HTTPS here is strongly encouraged.',
633         'coust',
634         'description'
635     ),
636     'ebook_api',
637     'string'
638 ),(
639     'ebook_api.overdrive.circulation_base_uri',
640     oils_i18n_gettext(
641         'ebook_api.overdrive.circulation_base_uri',
642         'OverDrive Circulation API Base URI',
643         'coust',
644         'label'
645     ),
646     oils_i18n_gettext(
647         'ebook_api.overdrive.circulation_base_uri',
648         'Base URI for OverDrive Circulation API (defaults to https://patron.api.overdrive.com/v1). Using HTTPS here is strongly encouraged.',
649         'coust',
650         'description'
651     ),
652     'ebook_api',
653     'string'
654 ),(
655     'ebook_api.overdrive.account_id',
656     oils_i18n_gettext(
657         'ebook_api.overdrive.account_id',
658         'OverDrive Account ID',
659         'coust',
660         'label'
661     ),
662     oils_i18n_gettext(
663         'ebook_api.overdrive.account_id',
664         'Account ID (a.k.a. Library ID) for this library, as assigned by OverDrive',
665         'coust',
666         'description'
667     ),
668     'ebook_api',
669     'string'
670 ),(
671     'ebook_api.overdrive.websiteid',
672     oils_i18n_gettext(
673         'ebook_api.overdrive.websiteid',
674         'OverDrive Website ID',
675         'coust',
676         'label'
677     ),
678     oils_i18n_gettext(
679         'ebook_api.overdrive.websiteid',
680         'Website ID for this library, as assigned by OverDrive',
681         'coust',
682         'description'
683     ),
684     'ebook_api',
685     'string'
686 ),(
687     'ebook_api.overdrive.authorizationname',
688     oils_i18n_gettext(
689         'ebook_api.overdrive.authorizationname',
690         'OverDrive Authorization Name',
691         'coust',
692         'label'
693     ),
694     oils_i18n_gettext(
695         'ebook_api.overdrive.authorizationname',
696         'Authorization name for this library, as assigned by OverDrive',
697         'coust',
698         'description'
699     ),
700     'ebook_api',
701     'string'
702 ),(
703     'ebook_api.overdrive.basic_token',
704     oils_i18n_gettext(
705         'ebook_api.overdrive.basic_token',
706         'OverDrive Basic Token',
707         'coust',
708         'label'
709     ),
710     oils_i18n_gettext(
711         'ebook_api.overdrive.basic_token',
712         'Basic token for client authentication with OverDrive API (supplied by OverDrive)',
713         'coust',
714         'description'
715     ),
716     'ebook_api',
717     'string'
718 ),(
719     'ebook_api.overdrive.granted_auth_redirect_uri',
720     oils_i18n_gettext(
721         'ebook_api.overdrive.granted_auth_redirect_uri',
722         'OverDrive Granted Authorization Redirect URI',
723         'coust',
724         'label'
725     ),
726     oils_i18n_gettext(
727         'ebook_api.overdrive.granted_auth_redirect_uri',
728         'URI provided to OverDrive for use with granted authorization',
729         'coust',
730         'description'
731     ),
732     'ebook_api',
733     'string'
734 ),(
735     'ebook_api.overdrive.password_required',
736     oils_i18n_gettext(
737         'ebook_api.overdrive.password_required',
738         'OverDrive Password Required',
739         'coust',
740         'label'
741     ),
742     oils_i18n_gettext(
743         'ebook_api.overdrive.password_required',
744         'Does this library require a password when authenticating patrons with the OverDrive API?',
745         'coust',
746         'description'
747     ),
748     'ebook_api',
749     'bool'
750 );
751
752 SELECT evergreen.upgrade_deps_block_check('1029', :eg_version); -- csharp/gmcharlt
753
754 UPDATE config.index_normalizer SET description = 'Apply NACO normalization rules to the extracted text.  See https://www.loc.gov/aba/pcc/naco/normrule-2.html for details.' WHERE func = 'naco_normalize';
755 UPDATE config.index_normalizer SET description = 'Apply NACO normalization rules to the extracted text, retaining the first comma.  See https://www.loc.gov/aba/pcc/naco/normrule-2.html for details.' WHERE func = 'naco_normalize_keep_comma';
756
757 CREATE OR REPLACE FUNCTION public.naco_normalize( TEXT, TEXT ) RETURNS TEXT AS $func$
758
759     use strict;
760     use Unicode::Normalize;
761     use Encode;
762
763     my $str = shift;
764     my $sf = shift;
765
766     # Apply NACO normalization to input string; based on
767     # https://www.loc.gov/aba/pcc/naco/documents/SCA_PccNormalization_Final_revised.pdf
768     #
769     # Note that unlike a strict reading of the NACO normalization rules,
770     # output is returned as lowercase instead of uppercase for compatibility
771     # with previous versions of the Evergreen naco_normalize routine.
772
773     # Convert to upper-case first; even though final output will be lowercase, doing this will
774     # ensure that the German eszett (ß) and certain ligatures (ff, fi, ffl, etc.) will be handled correctly.
775     # If there are any bugs in Perl's implementation of upcasing, they will be passed through here.
776     $str = uc $str;
777
778     # remove non-filing strings
779     $str =~ s/\x{0098}.*?\x{009C}//g;
780
781     $str = NFKD($str);
782
783     # additional substitutions - 3.6.
784     $str =~ s/\x{00C6}/AE/g;
785     $str =~ s/\x{00DE}/TH/g;
786     $str =~ s/\x{0152}/OE/g;
787     $str =~ tr/\x{0110}\x{00D0}\x{00D8}\x{0141}\x{2113}\x{02BB}\x{02BC}]['/DDOLl/d;
788
789     # transformations based on Unicode category codes
790     $str =~ s/[\p{Cc}\p{Cf}\p{Co}\p{Cs}\p{Lm}\p{Mc}\p{Me}\p{Mn}]//g;
791
792         if ($sf && $sf =~ /^a/o) {
793                 my $commapos = index($str, ',');
794                 if ($commapos > -1) {
795                         if ($commapos != length($str) - 1) {
796                 $str =~ s/,/\x07/; # preserve first comma
797                         }
798                 }
799         }
800
801     # since we've stripped out the control characters, we can now
802     # use a few as placeholders temporarily
803     $str =~ tr/+&@\x{266D}\x{266F}#/\x01\x02\x03\x04\x05\x06/;
804     $str =~ s/[\p{Pc}\p{Pd}\p{Pe}\p{Pf}\p{Pi}\p{Po}\p{Ps}\p{Sk}\p{Sm}\p{So}\p{Zl}\p{Zp}\p{Zs}]/ /g;
805     $str =~ tr/\x01\x02\x03\x04\x05\x06\x07/+&@\x{266D}\x{266F}#,/;
806
807     # decimal digits
808     $str =~ tr/\x{0660}-\x{0669}\x{06F0}-\x{06F9}\x{07C0}-\x{07C9}\x{0966}-\x{096F}\x{09E6}-\x{09EF}\x{0A66}-\x{0A6F}\x{0AE6}-\x{0AEF}\x{0B66}-\x{0B6F}\x{0BE6}-\x{0BEF}\x{0C66}-\x{0C6F}\x{0CE6}-\x{0CEF}\x{0D66}-\x{0D6F}\x{0E50}-\x{0E59}\x{0ED0}-\x{0ED9}\x{0F20}-\x{0F29}\x{1040}-\x{1049}\x{1090}-\x{1099}\x{17E0}-\x{17E9}\x{1810}-\x{1819}\x{1946}-\x{194F}\x{19D0}-\x{19D9}\x{1A80}-\x{1A89}\x{1A90}-\x{1A99}\x{1B50}-\x{1B59}\x{1BB0}-\x{1BB9}\x{1C40}-\x{1C49}\x{1C50}-\x{1C59}\x{A620}-\x{A629}\x{A8D0}-\x{A8D9}\x{A900}-\x{A909}\x{A9D0}-\x{A9D9}\x{AA50}-\x{AA59}\x{ABF0}-\x{ABF9}\x{FF10}-\x{FF19}/0-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-9/;
809
810     # intentionally skipping step 8 of the NACO algorithm; if the string
811     # gets normalized away, that's fine.
812
813     # leading and trailing spaces
814     $str =~ s/\s+/ /g;
815     $str =~ s/^\s+//;
816     $str =~ s/\s+$//g;
817
818     return lc $str;
819 $func$ LANGUAGE 'plperlu' STRICT IMMUTABLE;
820
821 -- Currently, the only difference from naco_normalize is that search_normalize
822 -- turns apostrophes into spaces, while naco_normalize collapses them.
823 CREATE OR REPLACE FUNCTION public.search_normalize( TEXT, TEXT ) RETURNS TEXT AS $func$
824
825     use strict;
826     use Unicode::Normalize;
827     use Encode;
828
829     my $str = shift;
830     my $sf = shift;
831
832     # Apply NACO normalization to input string; based on
833     # https://www.loc.gov/aba/pcc/naco/documents/SCA_PccNormalization_Final_revised.pdf
834     #
835     # Note that unlike a strict reading of the NACO normalization rules,
836     # output is returned as lowercase instead of uppercase for compatibility
837     # with previous versions of the Evergreen naco_normalize routine.
838
839     # Convert to upper-case first; even though final output will be lowercase, doing this will
840     # ensure that the German eszett (ß) and certain ligatures (ff, fi, ffl, etc.) will be handled correctly.
841     # If there are any bugs in Perl's implementation of upcasing, they will be passed through here.
842     $str = uc $str;
843
844     # remove non-filing strings
845     $str =~ s/\x{0098}.*?\x{009C}//g;
846
847     $str = NFKD($str);
848
849     # additional substitutions - 3.6.
850     $str =~ s/\x{00C6}/AE/g;
851     $str =~ s/\x{00DE}/TH/g;
852     $str =~ s/\x{0152}/OE/g;
853     $str =~ tr/\x{0110}\x{00D0}\x{00D8}\x{0141}\x{2113}\x{02BB}\x{02BC}][/DDOLl/d;
854
855     # transformations based on Unicode category codes
856     $str =~ s/[\p{Cc}\p{Cf}\p{Co}\p{Cs}\p{Lm}\p{Mc}\p{Me}\p{Mn}]//g;
857
858         if ($sf && $sf =~ /^a/o) {
859                 my $commapos = index($str, ',');
860                 if ($commapos > -1) {
861                         if ($commapos != length($str) - 1) {
862                 $str =~ s/,/\x07/; # preserve first comma
863                         }
864                 }
865         }
866
867     # since we've stripped out the control characters, we can now
868     # use a few as placeholders temporarily
869     $str =~ tr/+&@\x{266D}\x{266F}#/\x01\x02\x03\x04\x05\x06/;
870     $str =~ s/[\p{Pc}\p{Pd}\p{Pe}\p{Pf}\p{Pi}\p{Po}\p{Ps}\p{Sk}\p{Sm}\p{So}\p{Zl}\p{Zp}\p{Zs}]/ /g;
871     $str =~ tr/\x01\x02\x03\x04\x05\x06\x07/+&@\x{266D}\x{266F}#,/;
872
873     # decimal digits
874     $str =~ tr/\x{0660}-\x{0669}\x{06F0}-\x{06F9}\x{07C0}-\x{07C9}\x{0966}-\x{096F}\x{09E6}-\x{09EF}\x{0A66}-\x{0A6F}\x{0AE6}-\x{0AEF}\x{0B66}-\x{0B6F}\x{0BE6}-\x{0BEF}\x{0C66}-\x{0C6F}\x{0CE6}-\x{0CEF}\x{0D66}-\x{0D6F}\x{0E50}-\x{0E59}\x{0ED0}-\x{0ED9}\x{0F20}-\x{0F29}\x{1040}-\x{1049}\x{1090}-\x{1099}\x{17E0}-\x{17E9}\x{1810}-\x{1819}\x{1946}-\x{194F}\x{19D0}-\x{19D9}\x{1A80}-\x{1A89}\x{1A90}-\x{1A99}\x{1B50}-\x{1B59}\x{1BB0}-\x{1BB9}\x{1C40}-\x{1C49}\x{1C50}-\x{1C59}\x{A620}-\x{A629}\x{A8D0}-\x{A8D9}\x{A900}-\x{A909}\x{A9D0}-\x{A9D9}\x{AA50}-\x{AA59}\x{ABF0}-\x{ABF9}\x{FF10}-\x{FF19}/0-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-9/;
875
876     # intentionally skipping step 8 of the NACO algorithm; if the string
877     # gets normalized away, that's fine.
878
879     # leading and trailing spaces
880     $str =~ s/\s+/ /g;
881     $str =~ s/^\s+//;
882     $str =~ s/\s+$//g;
883
884     return lc $str;
885 $func$ LANGUAGE 'plperlu' STRICT IMMUTABLE;
886
887 SELECT evergreen.upgrade_deps_block_check('1030', :eg_version);
888
889 CREATE OR REPLACE FUNCTION evergreen.oils_xslt_process(TEXT, TEXT) RETURNS TEXT AS $func$
890   use strict;
891
892   use XML::LibXSLT;
893   use XML::LibXML;
894
895   my $doc = shift;
896   my $xslt = shift;
897
898   # The following approach uses the older XML::LibXML 1.69 / XML::LibXSLT 1.68
899   # methods of parsing XML documents and stylesheets, in the hopes of broader
900   # compatibility with distributions
901   my $parser = $_SHARED{'_xslt_process'}{parsers}{xml} || XML::LibXML->new();
902
903   # Cache the XML parser, if we do not already have one
904   $_SHARED{'_xslt_process'}{parsers}{xml} = $parser
905     unless ($_SHARED{'_xslt_process'}{parsers}{xml});
906
907   my $xslt_parser = $_SHARED{'_xslt_process'}{parsers}{xslt} || XML::LibXSLT->new();
908
909   # Cache the XSLT processor, if we do not already have one
910   $_SHARED{'_xslt_process'}{parsers}{xslt} = $xslt_parser
911     unless ($_SHARED{'_xslt_process'}{parsers}{xslt});
912
913   my $stylesheet = $_SHARED{'_xslt_process'}{stylesheets}{$xslt} ||
914     $xslt_parser->parse_stylesheet( $parser->parse_string($xslt) );
915
916   $_SHARED{'_xslt_process'}{stylesheets}{$xslt} = $stylesheet
917     unless ($_SHARED{'_xslt_process'}{stylesheets}{$xslt});
918
919   return $stylesheet->output_as_chars(
920     $stylesheet->transform(
921       $parser->parse_string($doc)
922     )
923   );
924
925 $func$ LANGUAGE 'plperlu' STRICT IMMUTABLE;
926
927 SELECT evergreen.upgrade_deps_block_check('1031', :eg_version);
928
929 INSERT INTO config.org_unit_setting_type
930     (name, label, description, grp, datatype) 
931 VALUES (
932     'ebook_api.oneclickdigital.base_uri',
933     oils_i18n_gettext(
934         'ebook_api.oneclickdigital.base_uri',
935         'OneClickdigital Base URI',
936         'coust',
937         'label'
938     ),
939     oils_i18n_gettext(
940         'ebook_api.oneclickdigital.base_uri',
941         'Base URI for OneClickdigital API (defaults to https://api.oneclickdigital.com/v1). Using HTTPS here is strongly encouraged.',
942         'coust',
943         'description'
944     ),
945     'ebook_api',
946     'string'
947 );
948
949 COMMIT;
950
951 \qecho Running some data updates outside of the main transaction
952 \qecho =========================================================
953 \qecho Update some indexes on actor.usr
954 REINDEX INDEX actor.actor_usr_first_given_name_unaccent_idx;
955 REINDEX INDEX actor.actor_usr_second_given_name_unaccent_idx;
956 REINDEX INDEX actor.actor_usr_family_name_unaccent_idx;
957
958 \qecho Recalculating bib fingerprints; this may take a while
959 ALTER TABLE biblio.record_entry DISABLE TRIGGER USER;
960 UPDATE biblio.record_entry SET fingerprint = biblio.extract_fingerprint(marc) WHERE NOT deleted;
961 ALTER TABLE biblio.record_entry ENABLE TRIGGER USER;
962
963 \qecho Remapping metarecords
964 SELECT metabib.remap_metarecord_for_bib(id, fingerprint)
965 FROM biblio.record_entry
966 WHERE NOT deleted;
967
968 \qecho Running a browse and reingest of your bib records. It may take a while.
969 \qecho You may cancel now without losing the effect of the rest of the
970 \qecho upgrade script, and arrange the reingest later.
971 \qecho .
972 SELECT metabib.reingest_metabib_field_entries(id, FALSE, FALSE, TRUE)
973     FROM biblio.record_entry;
974