2.11.3-2.12-beta schema upgrade script
authorGalen Charlton <gmc@equinoxinitiative.org>
Wed, 22 Feb 2017 16:46:17 +0000 (11:46 -0500)
committerGalen Charlton <gmc@equinoxinitiative.org>
Wed, 22 Feb 2017 16:46:17 +0000 (11:46 -0500)
Signed-off-by: Galen Charlton <gmc@equinoxinitiative.org>
Open-ILS/src/sql/Pg/version-upgrade/2.11.3-2.12-beta-upgrade-db.sql [new file with mode: 0644]

diff --git a/Open-ILS/src/sql/Pg/version-upgrade/2.11.3-2.12-beta-upgrade-db.sql b/Open-ILS/src/sql/Pg/version-upgrade/2.11.3-2.12-beta-upgrade-db.sql
new file mode 100644 (file)
index 0000000..b7140e2
--- /dev/null
@@ -0,0 +1,777 @@
+--Upgrade Script for 2.11.3 to 2.12-beta
+\set eg_version '''2.12-beta'''
+BEGIN;
+
+SELECT evergreen.upgrade_deps_block_check('1006', :eg_version);
+
+-- This function is used to help clean up facet labels. Due to quirks in
+-- MARC parsing, some facet labels may be generated with periods or commas
+-- at the end.  This will strip a trailing commas off all the time, and
+-- periods when they don't look like they are part of initials.
+--      Smith, John    =>  no change
+--      Smith, John,   =>  Smith, John
+--      Smith, John.   =>  Smith, John
+--      Public, John Q. => no change
+CREATE OR REPLACE FUNCTION metabib.trim_trailing_punctuation ( TEXT ) RETURNS TEXT AS $$
+DECLARE
+    result    TEXT;
+    last_char TEXT;
+BEGIN
+    result := $1;
+    last_char = substring(result from '.$');
+
+    IF last_char = ',' THEN
+        result := substring(result from '^(.*),$');
+
+    ELSIF last_char = '.' THEN
+        IF substring(result from ' \w\.$') IS NULL THEN
+            result := substring(result from '^(.*)\.$');
+        END IF;
+    END IF;
+
+    RETURN result;
+
+END;
+$$ language 'plpgsql';
+
+INSERT INTO config.index_normalizer (name, description, func, param_count) VALUES (
+       'Trim Trailing Punctuation',
+       'Eliminate extraneous trailing commas and periods in text',
+       'metabib.trim_trailing_punctuation',
+       0
+);
+
+INSERT INTO config.metabib_field_index_norm_map (field,norm,pos)
+    SELECT  m.id,
+            i.id,
+            -1
+      FROM  config.metabib_field m,
+            config.index_normalizer i
+      WHERE i.func = 'metabib.trim_trailing_punctuation'
+            AND m.id IN (7,8,9,10);
+
+SELECT evergreen.upgrade_deps_block_check('1007', :eg_version);
+
+UPDATE config.record_attr_definition
+SET description = oils_i18n_gettext('audience', 'Audience', 'crad', 'label')
+WHERE description IS NULL
+AND name = 'audience';
+UPDATE config.record_attr_definition
+SET description = oils_i18n_gettext('bib_level', 'Bib Level', 'crad', 'label')
+WHERE description IS NULL
+AND name = 'bib_level';
+UPDATE config.record_attr_definition
+SET description = oils_i18n_gettext('item_form', 'Item Form', 'crad', 'label')
+WHERE description IS NULL
+AND name = 'item_form';
+UPDATE config.record_attr_definition
+SET description = oils_i18n_gettext('item_lang', 'Language', 'crad', 'label')
+WHERE description IS NULL
+AND name = 'item_lang';
+UPDATE config.record_attr_definition
+SET description = oils_i18n_gettext('lit_form', 'Literary Form', 'crad', 'label')
+WHERE description IS NULL
+AND name = 'lit_form';
+UPDATE config.record_attr_definition
+SET description = oils_i18n_gettext('item_type', 'Item Type', 'crad', 'label')
+WHERE description IS NULL
+AND name = 'item_type';
+UPDATE config.record_attr_definition
+SET description = oils_i18n_gettext('vr_format', 'Video Format', 'crad', 'label')
+WHERE description IS NULL
+AND name = 'vr_format';
+
+SELECT evergreen.upgrade_deps_block_check('1008', :eg_version);
+
+CREATE OR REPLACE FUNCTION evergreen.unaccent_and_squash ( IN arg text) RETURNS text
+    IMMUTABLE STRICT AS $$
+       BEGIN
+       RETURN evergreen.lowercase(unaccent(regexp_replace(arg, '[\s[:punct:]]','','g')));
+       END;
+$$ LANGUAGE PLPGSQL;
+
+SELECT evergreen.upgrade_deps_block_check('1009', :eg_version);
+
+INSERT into config.org_unit_setting_type
+( name, grp, label, description, datatype, fm_class ) VALUES
+( 'acq.copy_status_on_receiving', 'acq',
+    oils_i18n_gettext('acq.copy_status_on_receiving',
+        'Initial status for received items',
+        'coust', 'label'),
+    oils_i18n_gettext('acq.copy_status_on_receiving',
+        'Allows staff to designate a custom copy status on received lineitems.  Default status is "In Process".',
+        'coust', 'description'),
+    'link', 'ccs');
+
+-- remove unused org unit setting for self checkout interface
+SELECT evergreen.upgrade_deps_block_check('1010', :eg_version);
+
+DELETE FROM actor.org_unit_setting WHERE name = 'circ.selfcheck.require_patron_password';
+
+DELETE FROM config.org_unit_setting_type WHERE name = 'circ.selfcheck.require_patron_password';
+
+DELETE FROM config.org_unit_setting_type_log WHERE field_name = 'circ.selfcheck.require_patron_password';
+
+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');
+
+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');
+
+DELETE FROM permission.perm_list WHERE code = 'UPDATE_ORG_UNIT_SETTING.circ.selfcheck.require_patron_password';
+
+SELECT evergreen.upgrade_deps_block_check('1011', :eg_version);
+
+INSERT INTO config.org_unit_setting_type
+    (name, grp, label, description, datatype)
+    VALUES
+        ('circ.in_house_use.copy_alert',
+         'circ',
+         oils_i18n_gettext('circ.in_house_use.copy_alert',
+             'Display copy alert for in-house-use',
+             'coust', 'label'),
+         oils_i18n_gettext('circ.in_house_use.copy_alert',
+             'Display copy alert for in-house-use',
+             'coust', 'description'),
+         'bool'),
+        ('circ.in_house_use.checkin_alert',
+         'circ',
+         oils_i18n_gettext('circ.in_house_use.checkin_alert',
+             'Display copy location checkin alert for in-house-use',
+             'coust', 'label'),
+         oils_i18n_gettext('circ.in_house_use.checkin_alert',
+             'Display copy location checkin alert for in-house-use',
+             'coust', 'description'),
+         'bool');
+
+SELECT evergreen.upgrade_deps_block_check('1014', :eg_version);
+-- this update of unapi.mmr_mra() removed since 1015 has a newer version
+  
+SELECT evergreen.upgrade_deps_block_check('1015', :eg_version);
+
+CREATE OR REPLACE FUNCTION unapi.mmr_mra (
+    obj_id BIGINT,
+    format TEXT,
+    ename TEXT,
+    includes TEXT[],
+    org TEXT,
+    depth INT DEFAULT NULL,
+    slimit HSTORE DEFAULT NULL,
+    soffset HSTORE DEFAULT NULL,
+    include_xmlns BOOL DEFAULT TRUE,
+    pref_lib INT DEFAULT NULL
+) RETURNS XML AS $F$
+    SELECT  XMLELEMENT(
+        name attributes,
+        XMLATTRIBUTES(
+            CASE WHEN $9 THEN 'http://open-ils.org/spec/indexing/v1' ELSE NULL END AS xmlns,
+            'tag:open-ils.org:U2@mmr/' || $1 AS metarecord
+        ),
+        (SELECT XMLAGG(foo.y)
+          FROM (
+            WITH sourcelist AS (
+                WITH aou AS (SELECT COALESCE(id, (evergreen.org_top()).id) AS id
+                    FROM actor.org_unit WHERE shortname = $5 LIMIT 1)
+                SELECT source
+                FROM metabib.metarecord_source_map mmsm, aou
+                WHERE metarecord = $1 AND (
+                    EXISTS (
+                        SELECT 1 FROM asset.opac_visible_copies
+                        WHERE record = source AND circ_lib IN (
+                            SELECT id FROM actor.org_unit_descendants(aou.id, $6))
+                        LIMIT 1
+                    )
+                    OR EXISTS (SELECT 1 FROM located_uris(source, aou.id, $10) LIMIT 1)
+                    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)
+                )
+            )
+            SELECT  cmra.aid,
+                    XMLELEMENT(
+                        name field,
+                        XMLATTRIBUTES(
+                            cmra.attr AS name,
+                            cmra.value AS "coded-value",
+                            cmra.aid AS "cvmid",
+                            rad.composite,
+                            rad.multi,
+                            rad.filter,
+                            rad.sorter,
+                            cmra.source_list
+                        ),
+                        cmra.value
+                    )
+              FROM  (
+                SELECT DISTINCT aid, attr, value, STRING_AGG(x.id::TEXT, ',') AS source_list
+                  FROM (
+                    SELECT  v.source AS id,
+                            c.id AS aid,
+                            c.ctype AS attr,
+                            c.code AS value
+                      FROM  metabib.record_attr_vector_list v
+                            JOIN config.coded_value_map c ON ( c.id = ANY( v.vlist ) )
+                    ) AS x
+                    JOIN sourcelist ON (x.id = sourcelist.source)
+                    GROUP BY 1, 2, 3
+                ) AS cmra
+                JOIN config.record_attr_definition rad ON (cmra.attr = rad.name)
+                UNION ALL
+            SELECT  umra.aid,
+                    XMLELEMENT(
+                        name field,
+                        XMLATTRIBUTES(
+                            umra.attr AS name,
+                            rad.composite,
+                            rad.multi,
+                            rad.filter,
+                            rad.sorter
+                        ),
+                        umra.value
+                    )
+              FROM  (
+                SELECT DISTINCT aid, attr, value
+                  FROM (
+                    SELECT  v.source AS id,
+                            m.id AS aid,
+                            m.attr AS attr,
+                            m.value AS value
+                      FROM  metabib.record_attr_vector_list v
+                            JOIN metabib.uncontrolled_record_attr_value m ON ( m.id = ANY( v.vlist ) )
+                    ) AS x
+                    JOIN sourcelist ON (x.id = sourcelist.source)
+                ) AS umra
+                JOIN config.record_attr_definition rad ON (umra.attr = rad.name)
+                ORDER BY 1
+
+            )foo(id,y)
+        )
+    )
+$F$ LANGUAGE SQL STABLE;
+  
+SELECT evergreen.upgrade_deps_block_check('1016', :eg_version);
+
+INSERT INTO config.biblio_fingerprint (name, xpath, format)
+    VALUES (
+        'PartName',
+        '//mods32:mods/mods32:titleInfo/mods32:partName',
+        'mods32'
+    );
+
+INSERT INTO config.biblio_fingerprint (name, xpath, format)
+    VALUES (
+        'PartNumber',
+        '//mods32:mods/mods32:titleInfo/mods32:partNumber',
+        'mods32'
+    );
+
+SELECT evergreen.upgrade_deps_block_check('1017', :eg_version);
+
+CREATE OR REPLACE FUNCTION biblio.extract_fingerprint ( marc text ) RETURNS TEXT AS $func$
+DECLARE
+       idx             config.biblio_fingerprint%ROWTYPE;
+       xfrm            config.xml_transform%ROWTYPE;
+       prev_xfrm       TEXT;
+       transformed_xml TEXT;
+       xml_node        TEXT;
+       xml_node_list   TEXT[];
+       raw_text        TEXT;
+    output_text TEXT := '';
+BEGIN
+
+    IF marc IS NULL OR marc = '' THEN
+        RETURN NULL;
+    END IF;
+
+       -- Loop over the indexing entries
+       FOR idx IN SELECT * FROM config.biblio_fingerprint ORDER BY format, id LOOP
+
+               SELECT INTO xfrm * from config.xml_transform WHERE name = idx.format;
+
+               -- See if we can skip the XSLT ... it's expensive
+               IF prev_xfrm IS NULL OR prev_xfrm <> xfrm.name THEN
+                       -- Can't skip the transform
+                       IF xfrm.xslt <> '---' THEN
+                               transformed_xml := oils_xslt_process(marc,xfrm.xslt);
+                       ELSE
+                               transformed_xml := marc;
+                       END IF;
+
+                       prev_xfrm := xfrm.name;
+               END IF;
+
+               raw_text := COALESCE(
+            naco_normalize(
+                ARRAY_TO_STRING(
+                    oils_xpath(
+                        '//text()',
+                        (oils_xpath(
+                            idx.xpath,
+                            transformed_xml,
+                            ARRAY[ARRAY[xfrm.prefix, xfrm.namespace_uri]] 
+                        ))[1]
+                    ),
+                    ''
+                )
+            ),
+            ''
+        );
+
+        raw_text := REGEXP_REPLACE(raw_text, E'\\[.+?\\]', E'');
+        raw_text := REGEXP_REPLACE(raw_text, E'\\mthe\\M|\\man?d?d\\M', E'', 'g'); -- arg! the pain!
+
+        IF idx.first_word IS TRUE THEN
+            raw_text := REGEXP_REPLACE(raw_text, E'^(\\w+).*?$', E'\\1');
+        END IF;
+
+               output_text := output_text || idx.name || ':' ||
+                                          REGEXP_REPLACE(raw_text, E'\\s+', '', 'g') || ' ';
+
+       END LOOP;
+
+    RETURN BTRIM(output_text);
+
+END;
+$func$ LANGUAGE PLPGSQL;
+
+SELECT evergreen.upgrade_deps_block_check('1019', :eg_version);
+
+CREATE OR REPLACE FUNCTION
+    action.hold_request_regen_copy_maps(
+        hold_id INTEGER, copy_ids INTEGER[]) RETURNS VOID AS $$
+    DELETE FROM action.hold_copy_map WHERE hold = $1;
+    INSERT INTO action.hold_copy_map (hold, target_copy) SELECT $1, UNNEST($2);
+$$ LANGUAGE SQL;
+
+-- DATA
+
+INSERT INTO config.global_flag (name, label, value, enabled) VALUES (
+    'circ.holds.retarget_interval',
+    oils_i18n_gettext(
+        'circ.holds.retarget_interval',
+        'Holds Retarget Interval', 
+        'cgf',
+        'label'
+    ),
+    '24h',
+    TRUE
+);
+
+SELECT evergreen.upgrade_deps_block_check('1020', :eg_version);
+
+CREATE OR REPLACE FUNCTION actor.org_unit_ancestor_setting_batch_by_org(
+    setting_name TEXT, org_ids INTEGER[]) 
+    RETURNS SETOF actor.org_unit_setting AS 
+$FUNK$
+DECLARE
+    setting RECORD;
+    org_id INTEGER;
+BEGIN
+    /*  Returns one actor.org_unit_setting row per org unit ID provided.
+        When no setting exists for a given org unit, the setting row
+        will contain all empty values. */
+    FOREACH org_id IN ARRAY org_ids LOOP
+        SELECT INTO setting * FROM 
+            actor.org_unit_ancestor_setting(setting_name, org_id);
+        RETURN NEXT setting;
+    END LOOP;
+    RETURN;
+END;
+$FUNK$ LANGUAGE plpgsql STABLE;
+
+SELECT evergreen.upgrade_deps_block_check('1021', :eg_version);
+
+-- Add missing permissions noted in LP 1517137 adjusting those added manually and ignoring those already in place.
+
+DO $$
+DECLARE fixperm TEXT[3];
+DECLARE modify BOOLEAN;
+DECLARE permid BIGINT;
+DECLARE oldid BIGINT;
+BEGIN
+
+FOREACH fixperm SLICE 1 IN ARRAY ARRAY[
+  ['564', 'MARK_ITEM_CATALOGING', 'Allow a user to mark an item status as ''cataloging'''],
+  ['565', 'MARK_ITEM_DAMAGED', 'Allow a user to mark an item status as ''damaged'''],
+  ['566', 'MARK_ITEM_DISCARD', 'Allow a user to mark an item status as ''discard'''],
+  ['567', 'MARK_ITEM_RESERVES', 'Allow a user to mark an item status as ''reserves'''],
+  ['568', 'ADMIN_ORG_UNIT_SETTING_TYPE_LOG', 'Allow a user to modify the org unit settings log'],
+  ['570', 'CREATE_POP_BADGE', 'Allow a user to create a new popularity badge'],
+  ['571', 'DELETE_POP_BADGE', 'Allow a user to delete a popularity badge'],
+  ['572', 'UPDATE_POP_BADGE', 'Allow a user to modify a popularity badge'],
+  ['573', 'CREATE_POP_PARAMETER', 'Allow a user to create a popularity badge parameter'],
+  ['574', 'DELETE_POP_PARAMETER', 'Allow a user to delete a popularity badge parameter'],
+  ['575', 'UPDATE_POP_PARAMETER', 'Allow a user to modify a popularity badge parameter'],
+  ['576', 'CREATE_AUTHORITY_RECORD', 'Allow a user to create an authority record'],
+  ['577', 'DELETE_AUTHORITY_RECORD', 'Allow a user to delete an authority record'],
+  ['578', 'UPDATE_AUTHORITY_RECORD', 'Allow a user to modify an authority record'],
+  ['579', 'CREATE_AUTHORITY_CONTROL_SET', 'Allow a user to create an authority control set'],
+  ['580', 'DELETE_AUTHORITY_CONTROL_SET', 'Allow a user to delete an authority control set'],
+  ['581', 'UPDATE_AUTHORITY_CONTROL_SET', 'Allow a user to modify an authority control set'],
+  ['582', 'ACTOR_USER_DELETE_OPEN_XACTS.override', 'Override the ACTOR_USER_DELETE_OPEN_XACTS event'],
+  ['583', 'PATRON_EXCEEDS_LOST_COUNT.override', 'Override the PATRON_EXCEEDS_LOST_COUNT event'],
+  ['584', 'MAX_HOLDS.override', 'Override the MAX_HOLDS event'],
+  ['585', 'ITEM_DEPOSIT_REQUIRED.override', 'Override the ITEM_DEPOSIT_REQUIRED event'],
+  ['586', 'ITEM_DEPOSIT_PAID.override', 'Override the ITEM_DEPOSIT_PAID event'],
+  ['587', 'COPY_STATUS_LOST_AND_PAID.override', 'Override the COPY_STATUS_LOST_AND_PAID event'],
+  ['588', 'ITEM_NOT_HOLDABLE.override', 'Override the ITEM_NOT_HOLDABLE event'],
+  ['589', 'ITEM_RENTAL_FEE_REQUIRED.override', 'Override the ITEM_RENTAL_FEE_REQUIRED event']
+]
+LOOP
+  permid := CAST (fixperm[1] AS BIGINT);
+  -- Has this permission already been manually applied at the expected id?
+  PERFORM * FROM permission.perm_list WHERE id = permid;
+  IF NOT FOUND THEN
+    UPDATE permission.perm_list SET code = code || '_local' WHERE code = fixperm[2] AND id > 1000 RETURNING id INTO oldid;
+    modify := FOUND;
+
+    INSERT INTO permission.perm_list (id, code, description) VALUES (permid, fixperm[2], fixperm[3]);
+
+    -- Several of these are rather unlikely for these particular permissions but safer > sorry.
+    IF modify THEN
+      UPDATE permission.grp_perm_map SET perm = permid WHERE perm = oldid;
+      UPDATE config.org_unit_setting_type SET update_perm = permid WHERE update_perm = oldid;
+      UPDATE permission.usr_object_perm_map SET perm = permid WHERE perm = oldid;
+      UPDATE permission.usr_perm_map SET perm = permid WHERE perm = oldid;
+      UPDATE config.org_unit_setting_type SET view_perm = permid WHERE view_perm = oldid;
+      UPDATE config.z3950_source SET use_perm = permid WHERE use_perm = oldid;
+      DELETE FROM permission.perm_list WHERE id = oldid;
+    END IF;
+  END IF;
+END LOOP;
+
+END$$;
+
+SELECT evergreen.upgrade_deps_block_check('1022', :eg_version);
+
+CREATE OR REPLACE FUNCTION vandelay.merge_record_xml_using_profile ( incoming_marc TEXT, existing_marc TEXT, merge_profile_id BIGINT ) RETURNS TEXT AS $$
+DECLARE
+    merge_profile   vandelay.merge_profile%ROWTYPE;
+    dyn_profile     vandelay.compile_profile%ROWTYPE;
+    target_marc     TEXT;
+    source_marc     TEXT;
+    replace_rule    TEXT;
+    match_count     INT;
+BEGIN
+
+    IF existing_marc IS NULL OR incoming_marc IS NULL THEN
+        -- RAISE NOTICE 'no marc for source or target records';
+        RETURN NULL;
+    END IF;
+
+    IF merge_profile_id IS NOT NULL THEN
+        SELECT * INTO merge_profile FROM vandelay.merge_profile WHERE id = merge_profile_id;
+        IF FOUND THEN
+            dyn_profile.add_rule := COALESCE(merge_profile.add_spec,'');
+            dyn_profile.strip_rule := COALESCE(merge_profile.strip_spec,'');
+            dyn_profile.replace_rule := COALESCE(merge_profile.replace_spec,'');
+            dyn_profile.preserve_rule := COALESCE(merge_profile.preserve_spec,'');
+        ELSE
+            -- RAISE NOTICE 'merge profile not found';
+            RETURN NULL;
+        END IF;
+    ELSE
+        -- RAISE NOTICE 'no merge profile specified';
+        RETURN NULL;
+    END IF;
+
+    IF dyn_profile.replace_rule <> '' AND dyn_profile.preserve_rule <> '' THEN
+        -- RAISE NOTICE 'both replace [%] and preserve [%] specified', dyn_profile.replace_rule, dyn_profile.preserve_rule;
+        RETURN NULL;
+    END IF;
+
+    IF dyn_profile.replace_rule = '' AND dyn_profile.preserve_rule = '' AND dyn_profile.add_rule = '' AND dyn_profile.strip_rule = '' THEN
+        -- Since we have nothing to do, just return a target record as is
+        RETURN existing_marc;
+    ELSIF dyn_profile.preserve_rule <> '' THEN
+        source_marc = existing_marc;
+        target_marc = incoming_marc;
+        replace_rule = dyn_profile.preserve_rule;
+    ELSE
+        source_marc = incoming_marc;
+        target_marc = existing_marc;
+        replace_rule = dyn_profile.replace_rule;
+    END IF;
+
+    RETURN vandelay.merge_record_xml( target_marc, source_marc, dyn_profile.add_rule, replace_rule, dyn_profile.strip_rule );
+
+END;
+$$ LANGUAGE PLPGSQL;
+
+SELECT evergreen.upgrade_deps_block_check('1023', :eg_version);
+
+INSERT into config.org_unit_setting_type
+( name, grp, label, description, datatype, fm_class ) VALUES
+(
+    'cat.default_merge_profile', 'cat',
+    oils_i18n_gettext(
+        'cat.default_merge_profile',
+        'Default Merge Profile (Z39.50 and Record Buckets)',
+        'coust',
+        'label'
+    ),
+     oils_i18n_gettext(
+        'cat.default_merge_profile',
+        'Default merge profile to use during Z39.50 imports and record bucket merges',
+        'coust',
+        'description'
+    ),
+    'link',
+    'vmp'
+);
+
+SELECT evergreen.upgrade_deps_block_check('1024', :eg_version);
+
+-- Add new column "rtl" with default of false
+ALTER TABLE config.i18n_locale ADD COLUMN rtl BOOL NOT NULL DEFAULT FALSE;
+
+SELECT evergreen.upgrade_deps_block_check('1025', :eg_version);
+
+-- Add Arabic (Jordan) to i18n_locale table as a stock language option
+INSERT INTO config.i18n_locale (code,marc_code,name,description,rtl)
+    VALUES ('ar-JO', 'ara', oils_i18n_gettext('ar-JO', 'Arabic (Jordan)', 'i18n_l', 'name'),
+        oils_i18n_gettext('ar-JO', 'Arabic (Jordan)', 'i18n_l', 'description'), 'true');
+
+SELECT evergreen.upgrade_deps_block_check('1026', :eg_version);
+
+INSERT INTO config.metabib_field ( id, field_class, name, label, 
+     format, xpath, search_field, browse_field, authority_xpath, joiner ) VALUES
+    (34, 'subject', 'topic_browse', oils_i18n_gettext(34, 'Topic Browse', 'cmf', 'label'), 
+     'mods32', $$//mods32:mods/mods32:subject[local-name(./*[1]) = "topic"]$$, FALSE, TRUE, '//@xlink:href', ' -- ' ); -- /* to fool vim */;
+
+INSERT INTO config.metabib_field ( id, field_class, name, label, 
+     format, xpath, search_field, browse_field, authority_xpath, joiner ) VALUES
+    (35, 'subject', 'geographic_browse', oils_i18n_gettext(35, 'Geographic Name Browse', 'cmf', 'label'), 
+     'mods32', $$//mods32:mods/mods32:subject[local-name(./*[1]) = "geographic"]$$, FALSE, TRUE, '//@xlink:href', ' -- ' ); -- /* to fool vim */;
+
+INSERT INTO config.metabib_field ( id, field_class, name, label, 
+     format, xpath, search_field, browse_field, authority_xpath, joiner ) VALUES
+    (36, 'subject', 'temporal_browse', oils_i18n_gettext(36, 'Temporal Term Browse', 'cmf', 'label'), 
+     'mods32', $$//mods32:mods/mods32:subject[local-name(./*[1]) = "temporal"]$$, FALSE, TRUE, '//@xlink:href', ' -- ' ); -- /* to fool vim */;
+
+INSERT INTO config.metabib_field_index_norm_map (field,norm)
+    SELECT  m.id,
+            i.id
+      FROM  config.metabib_field m,
+        config.index_normalizer i
+      WHERE i.func IN ('naco_normalize')
+            AND m.id IN (34, 35, 36);
+
+UPDATE config.metabib_field
+SET browse_field = FALSE
+WHERE field_class = 'subject' AND name = 'topic'
+AND id = 14;
+UPDATE config.metabib_field
+SET browse_field = FALSE
+WHERE field_class = 'subject' AND name = 'geographic'
+AND id = 13;
+UPDATE config.metabib_field
+SET browse_field = FALSE
+WHERE field_class = 'subject' AND name = 'temporal'
+AND id = 11;
+
+UPDATE authority.control_set_bib_field_metabib_field_map
+SET metabib_field = 34
+WHERE metabib_field = 14;
+UPDATE authority.control_set_bib_field_metabib_field_map
+SET metabib_field = 35
+WHERE metabib_field = 13;
+UPDATE authority.control_set_bib_field_metabib_field_map
+SET metabib_field = 36
+WHERE metabib_field = 11;
+
+SELECT evergreen.upgrade_deps_block_check('1027', :eg_version);
+
+INSERT INTO config.settings_group (name, label)
+    VALUES ('ebook_api', 'Ebook API Integration');
+
+INSERT INTO config.org_unit_setting_type
+    (name, label, description, grp, datatype) 
+VALUES (
+    'ebook_api.oneclickdigital.library_id',
+    oils_i18n_gettext(
+        'ebook_api.oneclickdigital.library_id',
+        'OneClickdigital Library ID',
+        'coust',
+        'label'
+    ),
+    oils_i18n_gettext(
+        'ebook_api.oneclickdigital.library_id',
+        'Identifier assigned to this library by OneClickdigital',
+        'coust',
+        'description'
+    ),
+    'ebook_api',
+    'string'
+),(
+    'ebook_api.oneclickdigital.basic_token',
+    oils_i18n_gettext(
+        'ebook_api.oneclickdigital.basic_token',
+        'OneClickdigital Basic Token',
+        'coust',
+        'label'
+    ),
+    oils_i18n_gettext(
+        'ebook_api.oneclickdigital.basic_token',
+        'Basic token for client authentication with OneClickdigital API (supplied by OneClickdigital)',
+        'coust',
+        'description'
+    ),
+    'ebook_api',
+    'string'
+);
+
+INSERT INTO config.org_unit_setting_type
+    (name, label, description, grp, datatype) 
+VALUES (
+    'ebook_api.overdrive.discovery_base_uri',
+    oils_i18n_gettext(
+        'ebook_api.overdrive.discovery_base_uri',
+        'OverDrive Discovery API Base URI',
+        'coust',
+        'label'
+    ),
+    oils_i18n_gettext(
+        'ebook_api.overdrive.discovery_base_uri',
+        'Base URI for OverDrive Discovery API (defaults to https://api.overdrive.com/v1). Using HTTPS here is strongly encouraged.',
+        'coust',
+        'description'
+    ),
+    'ebook_api',
+    'string'
+),(
+    'ebook_api.overdrive.circulation_base_uri',
+    oils_i18n_gettext(
+        'ebook_api.overdrive.circulation_base_uri',
+        'OverDrive Circulation API Base URI',
+        'coust',
+        'label'
+    ),
+    oils_i18n_gettext(
+        'ebook_api.overdrive.circulation_base_uri',
+        'Base URI for OverDrive Circulation API (defaults to https://patron.api.overdrive.com/v1). Using HTTPS here is strongly encouraged.',
+        'coust',
+        'description'
+    ),
+    'ebook_api',
+    'string'
+),(
+    'ebook_api.overdrive.account_id',
+    oils_i18n_gettext(
+        'ebook_api.overdrive.account_id',
+        'OverDrive Account ID',
+        'coust',
+        'label'
+    ),
+    oils_i18n_gettext(
+        'ebook_api.overdrive.account_id',
+        'Account ID (a.k.a. Library ID) for this library, as assigned by OverDrive',
+        'coust',
+        'description'
+    ),
+    'ebook_api',
+    'string'
+),(
+    'ebook_api.overdrive.websiteid',
+    oils_i18n_gettext(
+        'ebook_api.overdrive.websiteid',
+        'OverDrive Website ID',
+        'coust',
+        'label'
+    ),
+    oils_i18n_gettext(
+        'ebook_api.overdrive.websiteid',
+        'Website ID for this library, as assigned by OverDrive',
+        'coust',
+        'description'
+    ),
+    'ebook_api',
+    'string'
+),(
+    'ebook_api.overdrive.authorizationname',
+    oils_i18n_gettext(
+        'ebook_api.overdrive.authorizationname',
+        'OverDrive Authorization Name',
+        'coust',
+        'label'
+    ),
+    oils_i18n_gettext(
+        'ebook_api.overdrive.authorizationname',
+        'Authorization name for this library, as assigned by OverDrive',
+        'coust',
+        'description'
+    ),
+    'ebook_api',
+    'string'
+),(
+    'ebook_api.overdrive.basic_token',
+    oils_i18n_gettext(
+        'ebook_api.overdrive.basic_token',
+        'OverDrive Basic Token',
+        'coust',
+        'label'
+    ),
+    oils_i18n_gettext(
+        'ebook_api.overdrive.basic_token',
+        'Basic token for client authentication with OverDrive API (supplied by OverDrive)',
+        'coust',
+        'description'
+    ),
+    'ebook_api',
+    'string'
+),(
+    'ebook_api.overdrive.granted_auth_redirect_uri',
+    oils_i18n_gettext(
+        'ebook_api.overdrive.granted_auth_redirect_uri',
+        'OverDrive Granted Authorization Redirect URI',
+        'coust',
+        'label'
+    ),
+    oils_i18n_gettext(
+        'ebook_api.overdrive.granted_auth_redirect_uri',
+        'URI provided to OverDrive for use with granted authorization',
+        'coust',
+        'description'
+    ),
+    'ebook_api',
+    'string'
+),(
+    'ebook_api.overdrive.password_required',
+    oils_i18n_gettext(
+        'ebook_api.overdrive.password_required',
+        'OverDrive Password Required',
+        'coust',
+        'label'
+    ),
+    oils_i18n_gettext(
+        'ebook_api.overdrive.password_required',
+        'Does this library require a password when authenticating patrons with the OverDrive API?',
+        'coust',
+        'description'
+    ),
+    'ebook_api',
+    'bool'
+);
+
+COMMIT;
+
+\qecho Running some data updates outside of the main transaction
+\qecho =========================================================
+\qecho Update some indexes on actor.usr
+REINDEX INDEX actor.actor_usr_first_given_name_unaccent_idx;
+REINDEX INDEX actor.actor_usr_second_given_name_unaccent_idx;
+REINDEX INDEX actor.actor_usr_family_name_unaccent_idx;
+
+\qecho Recalculating bib fingerprints; this may take a while
+ALTER TABLE biblio.record_entry DISABLE TRIGGER USER;
+UPDATE biblio.record_entry SET fingerprint = biblio.extract_fingerprint(marc) WHERE NOT deleted;
+ALTER TABLE biblio.record_entry ENABLE TRIGGER USER;
+
+\qecho Remapping metarecords
+SELECT metabib.remap_metarecord_for_bib(id, fingerprint)
+FROM biblio.record_entry
+WHERE NOT deleted;
+
+\qecho Running a browse-only reingest of your bib records. It may take a while.
+\qecho You may cancel now without losing the effect of the rest of the
+\qecho upgrade script, and arrange the reingest later.
+\qecho .
+SELECT metabib.reingest_metabib_field_entries(id, TRUE, FALSE, TRUE)
+    FROM biblio.record_entry;
+