LP#1205061: Add IF EXISTS to some DROP statements in 2.3-2.4 upgrade script
[working/Evergreen.git] / Open-ILS / src / sql / Pg / version-upgrade / 2.3-2.4.alpha1-upgrade-db.sql
1 --Upgrade Script for 2.3 to 2.4.alpha1
2 \set eg_version '''2.4.alpha1'''
3 BEGIN;
4 INSERT INTO config.upgrade_log (version, applied_to) VALUES ('2.4.alpha1', :eg_version);
5 -- remove the Bypass hold capture during clear shelf process setting
6
7 SELECT evergreen.upgrade_deps_block_check('0739', :eg_version);
8
9
10 DELETE FROM actor.org_unit_setting WHERE name = 'circ.holds.clear_shelf.no_capture_holds';
11 DELETE FROM config.org_unit_setting_type_log WHERE field_name = 'circ.holds.clear_shelf.no_capture_holds';
12
13
14 DELETE FROM config.org_unit_setting_type WHERE name = 'circ.holds.clear_shelf.no_capture_holds';
15
16
17 SELECT evergreen.upgrade_deps_block_check('0741', :eg_version);
18
19 INSERT INTO permission.perm_list ( id, code, description ) VALUES (
20     540,
21     'ADMIN_TOOLBAR_FOR_ORG',
22     oils_i18n_gettext(
23         540,
24         'Allows a user to create, edit, and delete custom toolbars for org units',
25         'ppl',
26         'description'
27     )
28 ), (
29     541,
30     'ADMIN_TOOLBAR_FOR_WORKSTATION',
31     oils_i18n_gettext(
32         541,
33         'Allows a user to create, edit, and delete custom toolbars for workstations',
34         'ppl',
35         'description'
36     )
37 ), (
38     542,
39     'ADMIN_TOOLBAR_FOR_USER',
40     oils_i18n_gettext(
41         542,
42         'Allows a user to create, edit, and delete custom toolbars for users',
43         'ppl',
44         'description'
45     )
46 );
47
48
49 -- Evergreen DB patch 0743.schema.remove_tsearch2.sql
50 --
51 -- Enable native full-text search to be used, and drop TSearch2 extension
52 --
53
54 -- check whether patch can be applied
55 SELECT evergreen.upgrade_deps_block_check('0743', :eg_version);
56
57 -- FIXME: add/check SQL statements to perform the upgrade
58 -- First up, these functions depend on metabib.full_rec. They have to go for now.
59 DROP FUNCTION IF EXISTS biblio.flatten_marc(bigint);
60 DROP FUNCTION IF EXISTS biblio.flatten_marc(text);
61
62 -- These views depend on metabib.full_rec as well. Bye-bye!
63 DROP VIEW IF EXISTS reporter.old_super_simple_record;
64 DROP VIEW IF EXISTS reporter.simple_record;
65
66 -- Now we can drop metabib.full_rec.
67 DROP VIEW IF EXISTS metabib.full_rec;
68
69 -- These indexes have to go. BEFORE we alter the tables, otherwise things take extra time when we alter the tables.
70 DROP INDEX metabib.metabib_author_field_entry_value_idx;
71 DROP INDEX metabib.metabib_identifier_field_entry_value_idx;
72 DROP INDEX metabib.metabib_keyword_field_entry_value_idx;
73 DROP INDEX metabib.metabib_series_field_entry_value_idx;
74 DROP INDEX metabib.metabib_subject_field_entry_value_idx;
75 DROP INDEX metabib.metabib_title_field_entry_value_idx;
76
77 -- Now grab all of the tsvector-enabled columns and switch them to the non-wrapper version of the type.
78 ALTER TABLE authority.full_rec ALTER COLUMN index_vector TYPE pg_catalog.tsvector;
79 ALTER TABLE authority.simple_heading ALTER COLUMN index_vector TYPE pg_catalog.tsvector;
80 ALTER TABLE metabib.real_full_rec ALTER COLUMN index_vector TYPE pg_catalog.tsvector;
81 ALTER TABLE metabib.author_field_entry ALTER COLUMN index_vector TYPE pg_catalog.tsvector;
82 ALTER TABLE metabib.browse_entry ALTER COLUMN index_vector TYPE pg_catalog.tsvector;
83 ALTER TABLE metabib.identifier_field_entry ALTER COLUMN index_vector TYPE pg_catalog.tsvector;
84 ALTER TABLE metabib.keyword_field_entry ALTER COLUMN index_vector TYPE pg_catalog.tsvector;
85 ALTER TABLE metabib.series_field_entry ALTER COLUMN index_vector TYPE pg_catalog.tsvector;
86 ALTER TABLE metabib.subject_field_entry ALTER COLUMN index_vector TYPE pg_catalog.tsvector;
87 ALTER TABLE metabib.title_field_entry ALTER COLUMN index_vector TYPE pg_catalog.tsvector;
88
89 -- Halfway there! Goodbye tsearch2 extension!
90 DROP EXTENSION tsearch2;
91
92 -- Next up, re-creating all of the stuff we just dropped.
93
94 -- Indexes! Note to whomever: Do we even need these anymore?
95 CREATE INDEX metabib_author_field_entry_value_idx ON metabib.author_field_entry (SUBSTRING(value,1,1024)) WHERE index_vector = ''::TSVECTOR;
96 CREATE INDEX metabib_identifier_field_entry_value_idx ON metabib.identifier_field_entry (SUBSTRING(value,1,1024)) WHERE index_vector = ''::TSVECTOR;
97 CREATE INDEX metabib_keyword_field_entry_value_idx ON metabib.keyword_field_entry (SUBSTRING(value,1,1024)) WHERE index_vector = ''::TSVECTOR;
98 CREATE INDEX metabib_series_field_entry_value_idx ON metabib.series_field_entry (SUBSTRING(value,1,1024)) WHERE index_vector = ''::TSVECTOR;
99 CREATE INDEX metabib_subject_field_entry_value_idx ON metabib.subject_field_entry (SUBSTRING(value,1,1024)) WHERE index_vector = ''::TSVECTOR;
100 CREATE INDEX metabib_title_field_entry_value_idx ON metabib.title_field_entry (SUBSTRING(value,1,1024)) WHERE index_vector = ''::TSVECTOR;
101
102 -- metabib.full_rec, with insert/update/delete rules
103 CREATE OR REPLACE VIEW metabib.full_rec AS
104     SELECT  id,
105             record,
106             tag,
107             ind1,
108             ind2,
109             subfield,
110             SUBSTRING(value,1,1024) AS value,
111             index_vector
112       FROM  metabib.real_full_rec;
113
114 CREATE OR REPLACE RULE metabib_full_rec_insert_rule
115     AS ON INSERT TO metabib.full_rec
116     DO INSTEAD
117     INSERT INTO metabib.real_full_rec VALUES (
118         COALESCE(NEW.id, NEXTVAL('metabib.full_rec_id_seq'::REGCLASS)),
119         NEW.record,
120         NEW.tag,
121         NEW.ind1,
122         NEW.ind2,
123         NEW.subfield,
124         NEW.value,
125         NEW.index_vector
126     );
127
128 CREATE OR REPLACE RULE metabib_full_rec_update_rule
129     AS ON UPDATE TO metabib.full_rec
130     DO INSTEAD
131     UPDATE  metabib.real_full_rec SET
132         id = NEW.id,
133         record = NEW.record,
134         tag = NEW.tag,
135         ind1 = NEW.ind1,
136         ind2 = NEW.ind2,
137         subfield = NEW.subfield,
138         value = NEW.value,
139         index_vector = NEW.index_vector
140       WHERE id = OLD.id;
141
142 CREATE OR REPLACE RULE metabib_full_rec_delete_rule
143     AS ON DELETE TO metabib.full_rec
144     DO INSTEAD
145     DELETE FROM metabib.real_full_rec WHERE id = OLD.id;
146
147 -- reporter views that depended on metabib.full_rec are up next
148 CREATE OR REPLACE VIEW reporter.simple_record AS
149 SELECT  r.id,
150     s.metarecord,
151     r.fingerprint,
152     r.quality,
153     r.tcn_source,
154     r.tcn_value,
155     title.value AS title,
156     uniform_title.value AS uniform_title,
157     author.value AS author,
158     publisher.value AS publisher,
159     SUBSTRING(pubdate.value FROM $$\d+$$) AS pubdate,
160     series_title.value AS series_title,
161     series_statement.value AS series_statement,
162     summary.value AS summary,
163     ARRAY_ACCUM( DISTINCT REPLACE(SUBSTRING(isbn.value FROM $$^\S+$$), '-', '') ) AS isbn,
164     ARRAY_ACCUM( DISTINCT REGEXP_REPLACE(issn.value, E'^\\S*(\\d{4})[-\\s](\\d{3,4}x?)', E'\\1 \\2') ) AS issn,
165     ARRAY((SELECT DISTINCT value FROM metabib.full_rec WHERE tag = '650' AND subfield = 'a' AND record = r.id)) AS topic_subject,
166     ARRAY((SELECT DISTINCT value FROM metabib.full_rec WHERE tag = '651' AND subfield = 'a' AND record = r.id)) AS geographic_subject,
167     ARRAY((SELECT DISTINCT value FROM metabib.full_rec WHERE tag = '655' AND subfield = 'a' AND record = r.id)) AS genre,
168     ARRAY((SELECT DISTINCT value FROM metabib.full_rec WHERE tag = '600' AND subfield = 'a' AND record = r.id)) AS name_subject,
169     ARRAY((SELECT DISTINCT value FROM metabib.full_rec WHERE tag = '610' AND subfield = 'a' AND record = r.id)) AS corporate_subject,
170     ARRAY((SELECT value FROM metabib.full_rec WHERE tag = '856' AND subfield IN ('3','y','u') AND record = r.id ORDER BY CASE WHEN subfield IN ('3','y') THEN 0 ELSE 1 END)) AS external_uri
171   FROM  biblio.record_entry r
172     JOIN metabib.metarecord_source_map s ON (s.source = r.id)
173     LEFT JOIN metabib.full_rec uniform_title ON (r.id = uniform_title.record AND uniform_title.tag = '240' AND uniform_title.subfield = 'a')
174     LEFT JOIN metabib.full_rec title ON (r.id = title.record AND title.tag = '245' AND title.subfield = 'a')
175     LEFT JOIN metabib.full_rec author ON (r.id = author.record AND author.tag = '100' AND author.subfield = 'a')
176     LEFT JOIN metabib.full_rec publisher ON (r.id = publisher.record AND publisher.tag = '260' AND publisher.subfield = 'b')
177     LEFT JOIN metabib.full_rec pubdate ON (r.id = pubdate.record AND pubdate.tag = '260' AND pubdate.subfield = 'c')
178     LEFT JOIN metabib.full_rec isbn ON (r.id = isbn.record AND isbn.tag IN ('024', '020') AND isbn.subfield IN ('a','z'))
179     LEFT JOIN metabib.full_rec issn ON (r.id = issn.record AND issn.tag = '022' AND issn.subfield = 'a')
180     LEFT JOIN metabib.full_rec series_title ON (r.id = series_title.record AND series_title.tag IN ('830','440') AND series_title.subfield = 'a')
181     LEFT JOIN metabib.full_rec series_statement ON (r.id = series_statement.record AND series_statement.tag = '490' AND series_statement.subfield = 'a')
182     LEFT JOIN metabib.full_rec summary ON (r.id = summary.record AND summary.tag = '520' AND summary.subfield = 'a')
183   GROUP BY 1,2,3,4,5,6,7,8,9,10,11,12,13,14;
184
185 CREATE OR REPLACE VIEW reporter.old_super_simple_record AS
186 SELECT  r.id,
187     r.fingerprint,
188     r.quality,
189     r.tcn_source,
190     r.tcn_value,
191     FIRST(title.value) AS title,
192     FIRST(author.value) AS author,
193     ARRAY_TO_STRING(ARRAY_ACCUM( DISTINCT publisher.value), ', ') AS publisher,
194     ARRAY_TO_STRING(ARRAY_ACCUM( DISTINCT SUBSTRING(pubdate.value FROM $$\d+$$) ), ', ') AS pubdate,
195     ARRAY_ACCUM( DISTINCT REPLACE(SUBSTRING(isbn.value FROM $$^\S+$$), '-', '') ) AS isbn,
196     ARRAY_ACCUM( DISTINCT REGEXP_REPLACE(issn.value, E'^\\S*(\\d{4})[-\\s](\\d{3,4}x?)', E'\\1 \\2') ) AS issn
197   FROM  biblio.record_entry r
198     LEFT JOIN metabib.full_rec title ON (r.id = title.record AND title.tag = '245' AND title.subfield = 'a')
199     LEFT JOIN metabib.full_rec author ON (r.id = author.record AND author.tag IN ('100','110','111') AND author.subfield = 'a')
200     LEFT JOIN metabib.full_rec publisher ON (r.id = publisher.record AND publisher.tag = '260' AND publisher.subfield = 'b')
201     LEFT JOIN metabib.full_rec pubdate ON (r.id = pubdate.record AND pubdate.tag = '260' AND pubdate.subfield = 'c')
202     LEFT JOIN metabib.full_rec isbn ON (r.id = isbn.record AND isbn.tag IN ('024', '020') AND isbn.subfield IN ('a','z'))
203     LEFT JOIN metabib.full_rec issn ON (r.id = issn.record AND issn.tag = '022' AND issn.subfield = 'a')
204   GROUP BY 1,2,3,4,5;
205
206 -- And finally, the biblio functions. NOTE: I can't find the original source of the second one, so I skipped it as old cruft that was in our production DB.
207 CREATE OR REPLACE FUNCTION biblio.flatten_marc ( rid BIGINT ) RETURNS SETOF metabib.full_rec AS $func$
208 DECLARE
209     bib biblio.record_entry%ROWTYPE;
210     output  metabib.full_rec%ROWTYPE;
211     field   RECORD;
212 BEGIN
213     SELECT INTO bib * FROM biblio.record_entry WHERE id = rid;
214
215     FOR field IN SELECT * FROM vandelay.flatten_marc( bib.marc ) LOOP
216         output.record := rid;
217         output.ind1 := field.ind1;
218         output.ind2 := field.ind2;
219         output.tag := field.tag;
220         output.subfield := field.subfield;
221         output.value := field.value;
222
223         RETURN NEXT output;
224     END LOOP;
225 END;
226 $func$ LANGUAGE PLPGSQL;
227
228 -- Evergreen DB patch 0745.data.prewarn_expire_setting.sql
229 --
230 -- Configuration setting to warn staff when an account is about to expire
231 --
232
233 -- check whether patch can be applied
234 SELECT evergreen.upgrade_deps_block_check('0745', :eg_version);
235
236 INSERT INTO config.org_unit_setting_type
237     (name, grp, label, description, datatype)
238     VALUES (
239         'circ.patron_expires_soon_warning',
240         'circ',
241         oils_i18n_gettext(
242             'circ.patron_expires_soon_warning',
243             'Warn when patron account is about to expire',
244             'coust',
245             'label'
246         ),
247         oils_i18n_gettext(
248             'circ.patron_expires_soon_warning',
249             'Warn when patron account is about to expire. If set, the staff client displays a warning this many days before the expiry of a patron account. Value is in number of days, for example: 3 for 3 days.',
250             'coust',
251             'description'
252         ),
253         'integer'
254     );
255
256 -- LP1076399: Prevent reactivated holds from canceling immediately.
257 -- Set the expire_time to NULL on all frozen/suspended holds.
258
259 SELECT evergreen.upgrade_deps_block_check('0747', :eg_version);
260
261 UPDATE action.hold_request
262 SET expire_time = NULL
263 WHERE frozen = 't'; 
264
265
266 SELECT evergreen.upgrade_deps_block_check('0752', :eg_version);
267
268 INSERT INTO container.biblio_record_entry_bucket_type (code, label) VALUES ('url_verify', 'URL Verification Queue');
269
270 DROP SCHEMA IF EXISTS url_verify CASCADE;
271
272 CREATE SCHEMA url_verify;
273
274 CREATE TABLE url_verify.session (
275     id          SERIAL                      PRIMARY KEY,
276     name        TEXT                        NOT NULL,
277     owning_lib  INT                         NOT NULL REFERENCES actor.org_unit (id) DEFERRABLE INITIALLY DEFERRED,
278     creator     INT                         NOT NULL REFERENCES actor.usr (id) DEFERRABLE INITIALLY DEFERRED,
279     container   INT                         NOT NULL REFERENCES container.biblio_record_entry_bucket (id) DEFERRABLE INITIALLY DEFERRED,
280     create_time TIMESTAMP WITH TIME ZONE    NOT NULL DEFAULT NOW(),
281     search      TEXT                        NOT NULL,
282     CONSTRAINT uvs_name_once_per_lib UNIQUE (name, owning_lib)
283 );
284
285 CREATE TABLE url_verify.url_selector (
286     id      SERIAL  PRIMARY KEY,
287     xpath   TEXT    NOT NULL,
288     session INT     NOT NULL REFERENCES url_verify.session (id) DEFERRABLE INITIALLY DEFERRED,
289     CONSTRAINT tag_once_per_sess UNIQUE (xpath, session)
290 );
291
292 CREATE TABLE url_verify.url (
293     id              SERIAL  PRIMARY KEY,
294     redirect_from   INT     REFERENCES url_verify.url(id) DEFERRABLE INITIALLY DEFERRED,
295     item            INT     REFERENCES container.biblio_record_entry_bucket_item (id) DEFERRABLE INITIALLY DEFERRED,
296     url_selector    INT     REFERENCES url_verify.url_selector (id) DEFERRABLE INITIALLY DEFERRED,
297     session         INT     REFERENCES url_verify.session (id) DEFERRABLE INITIALLY DEFERRED,
298     tag             TEXT,
299     subfield        TEXT,
300     ord             INT,
301     full_url        TEXT    NOT NULL,
302     scheme          TEXT,
303     username        TEXT,
304     password        TEXT,
305     host            TEXT,
306     domain          TEXT,
307     tld             TEXT,
308     port            TEXT,
309     path            TEXT,
310     page            TEXT,
311     query           TEXT,
312     fragment        TEXT,
313     CONSTRAINT redirect_or_from_item CHECK (
314         redirect_from IS NOT NULL OR (
315             item         IS NOT NULL AND
316             url_selector IS NOT NULL AND
317             tag          IS NOT NULL AND
318             subfield     IS NOT NULL AND
319             ord          IS NOT NULL
320         )
321     )
322 );
323
324 CREATE TABLE url_verify.verification_attempt (
325     id          SERIAL                      PRIMARY KEY,
326     usr         INT                         NOT NULL REFERENCES actor.usr (id) DEFERRABLE INITIALLY DEFERRED,
327     session     INT                         NOT NULL REFERENCES url_verify.session (id) DEFERRABLE INITIALLY DEFERRED,
328     start_time  TIMESTAMP WITH TIME ZONE    NOT NULL DEFAULT NOW(),
329     finish_time TIMESTAMP WITH TIME ZONE
330 );
331  
332 CREATE TABLE url_verify.url_verification (
333     id          SERIAL                      PRIMARY KEY,
334     url         INT                         NOT NULL REFERENCES url_verify.url (id) DEFERRABLE INITIALLY DEFERRED,
335     attempt     INT                         NOT NULL REFERENCES url_verify.verification_attempt (id) DEFERRABLE INITIALLY DEFERRED,
336     req_time    TIMESTAMP WITH TIME ZONE    NOT NULL DEFAULT NOW(),
337     res_time    TIMESTAMP WITH TIME ZONE, 
338     res_code    INT                         CHECK (res_code BETWEEN 100 AND 999), -- we know > 599 will never be valid HTTP code, but we use 9XX for other stuff
339     res_text    TEXT, 
340     redirect_to INT                         REFERENCES url_verify.url (id) DEFERRABLE INITIALLY DEFERRED -- if redirected
341 );
342
343 CREATE TABLE config.filter_dialog_interface (
344     key         TEXT                        PRIMARY KEY,
345     description TEXT
346 );
347
348 CREATE TABLE config.filter_dialog_filter_set (
349     id          SERIAL                      PRIMARY KEY,
350     name        TEXT                        NOT NULL,
351     owning_lib  INT                         NOT NULL REFERENCES actor.org_unit (id) DEFERRABLE INITIALLY DEFERRED,
352     creator     INT                         NOT NULL REFERENCES actor.usr (id) DEFERRABLE INITIALLY DEFERRED,
353     create_time TIMESTAMP WITH TIME ZONE    NOT NULL DEFAULT NOW(),
354     interface   TEXT                        NOT NULL REFERENCES config.filter_dialog_interface (key) DEFERRABLE INITIALLY DEFERRED,
355     filters     TEXT                        NOT NULL CHECK (is_json(filters)),
356     CONSTRAINT cfdfs_name_once_per_lib UNIQUE (name, owning_lib)
357 );
358  
359
360 SELECT evergreen.upgrade_deps_block_check('0753', :eg_version);
361
362 CREATE OR REPLACE FUNCTION url_verify.parse_url (url_in TEXT) RETURNS url_verify.url AS $$
363
364 use Rose::URI;
365
366 my $url_in = shift;
367 my $url = Rose::URI->new($url_in);
368
369 my %parts = map { $_ => $url->$_ } qw/scheme username password host port path query fragment/;
370
371 $parts{full_url} = $url_in;
372 ($parts{domain} = $parts{host}) =~ s/^[^.]+\.//;
373 ($parts{tld} = $parts{domain}) =~ s/(?:[^.]+\.)+//;
374 ($parts{page} = $parts{path}) =~ s#(?:[^/]*/)+##;
375
376 return \%parts;
377
378 $$ LANGUAGE PLPERLU;
379
380 CREATE OR REPLACE FUNCTION url_verify.ingest_url () RETURNS TRIGGER AS $$
381 DECLARE
382     tmp_row url_verify.url%ROWTYPE;
383 BEGIN
384     SELECT * INTO tmp_row FROM url_verify.parse_url(NEW.full_url);
385
386     NEW.scheme          := tmp_row.scheme;
387     NEW.username        := tmp_row.username;
388     NEW.password        := tmp_row.password;
389     NEW.host            := tmp_row.host;
390     NEW.domain          := tmp_row.domain;
391     NEW.tld             := tmp_row.tld;
392     NEW.port            := tmp_row.port;
393     NEW.path            := tmp_row.path;
394     NEW.page            := tmp_row.page;
395     NEW.query           := tmp_row.query;
396     NEW.fragment        := tmp_row.fragment;
397
398     RETURN NEW;
399 END;
400 $$ LANGUAGE PLPGSQL;
401
402 CREATE TRIGGER ingest_url_tgr
403     BEFORE INSERT ON url_verify.url
404     FOR EACH ROW EXECUTE PROCEDURE url_verify.ingest_url(); 
405
406 CREATE OR REPLACE FUNCTION url_verify.extract_urls ( session_id INT, item_id INT ) RETURNS INT AS $$
407 DECLARE
408     last_seen_tag TEXT;
409     current_tag TEXT;
410     current_sf TEXT;
411     current_url TEXT;
412     current_ord INT;
413     current_url_pos INT;
414     current_selector url_verify.url_selector%ROWTYPE;
415 BEGIN
416     current_ord := 1;
417
418     FOR current_selector IN SELECT * FROM url_verify.url_selector s WHERE s.session = session_id LOOP
419         current_url_pos := 1;
420         LOOP
421             SELECT  (XPATH(current_selector.xpath || '/text()', b.marc::XML))[current_url_pos]::TEXT INTO current_url
422               FROM  biblio.record_entry b
423                     JOIN container.biblio_record_entry_bucket_item c ON (c.target_biblio_record_entry = b.id)
424               WHERE c.id = item_id;
425
426             EXIT WHEN current_url IS NULL;
427
428             SELECT  (XPATH(current_selector.xpath || '/../@tag', b.marc::XML))[current_url_pos]::TEXT INTO current_tag
429               FROM  biblio.record_entry b
430                     JOIN container.biblio_record_entry_bucket_item c ON (c.target_biblio_record_entry = b.id)
431               WHERE c.id = item_id;
432
433             IF current_tag IS NULL THEN
434                 current_tag := last_seen_tag;
435             ELSE
436                 last_seen_tag := current_tag;
437             END IF;
438
439             SELECT  (XPATH(current_selector.xpath || '/@code', b.marc::XML))[current_url_pos]::TEXT INTO current_sf
440               FROM  biblio.record_entry b
441                     JOIN container.biblio_record_entry_bucket_item c ON (c.target_biblio_record_entry = b.id)
442               WHERE c.id = item_id;
443
444             INSERT INTO url_verify.url (session, item, url_selector, tag, subfield, ord, full_url)
445               VALUES ( session_id, item_id, current_selector.id, current_tag, current_sf, current_ord, current_url);
446
447             current_url_pos := current_url_pos + 1;
448             current_ord := current_ord + 1;
449         END LOOP;
450     END LOOP;
451
452     RETURN current_ord - 1;
453 END;
454 $$ LANGUAGE PLPGSQL;
455
456
457
458 -- NOTE: beware the use of bare perm IDs in the update_perm's below and in 
459 -- the 950 seed data file.  Update before merge to match current perm IDs! XXX
460
461
462 SELECT evergreen.upgrade_deps_block_check('0754', :eg_version);
463
464 INSERT INTO permission.perm_list (id, code, description) 
465     VALUES ( 
466         543, 
467         'URL_VERIFY',
468         oils_i18n_gettext(
469             543, 
470             'Allows a user to process and verify ULSs', 
471             'ppl', 
472             'description'
473         )
474     );
475
476
477 INSERT INTO permission.perm_list (id, code, description) 
478     VALUES ( 
479         544, 
480         'URL_VERIFY_UPDATE_SETTINGS',
481         oils_i18n_gettext(
482             544, 
483             'Allows a user to configure URL verification org unit settings',
484             'ppl', 
485             'description'
486         )
487     );
488
489
490 INSERT INTO permission.perm_list (id, code, description) 
491     VALUES ( 
492         545, 
493         'SAVED_FILTER_DIALOG_FILTERS',
494         oils_i18n_gettext(
495             545, 
496             'Allows users to save and load sets of filters for filter dialogs, available in certain staff interfaces',
497             'ppl', 
498             'description'
499         )
500     );
501
502
503 INSERT INTO config.settings_group (name, label)
504     VALUES (
505         'url_verify',
506         oils_i18n_gettext(
507             'url_verify',
508             'URL Verify',
509             'csg',
510             'label'
511         )
512     );
513
514 INSERT INTO config.org_unit_setting_type
515     (name, grp, label, description, datatype, update_perm)
516     VALUES (
517         'url_verify.url_verification_delay',
518         'url_verify',
519         oils_i18n_gettext(
520             'url_verify.url_verification_delay',
521             'Number of seconds to wait between URL test attempts.',
522             'coust',
523             'label'
524         ),
525         oils_i18n_gettext(
526             'url_verify.url_verification_delay',
527             'Throttling mechanism for batch URL verification runs.  Each running process will wait this number of seconds after a URL test before performing the next.',
528             'coust',
529             'description'
530         ),
531         'integer',
532         544
533     );
534
535 INSERT INTO config.org_unit_setting_type
536     (name, grp, label, description, datatype, update_perm)
537     VALUES (
538         'url_verify.url_verification_max_redirects',
539         'url_verify',
540         oils_i18n_gettext(
541             'url_verify.url_verification_max_redirects',
542             'Maximum redirect lookups',
543             'coust',
544             'label'
545         ),
546         oils_i18n_gettext(
547             'url_verify.url_verification_max_redirects',
548             'For URLs returning 3XX redirects, this is the maximum number of redirects we will follow before giving up.',
549             'coust',
550             'description'
551         ),
552         'integer',
553         544
554     );
555
556 INSERT INTO config.org_unit_setting_type
557     (name, grp, label, description, datatype, update_perm)
558     VALUES (
559         'url_verify.url_verification_max_wait',
560         'url_verify',
561         oils_i18n_gettext(
562             'url_verify.url_verification_max_wait',
563             'Maximum wait time (in seconds) for a URL to lookup',
564             'coust',
565             'label'
566         ),
567         oils_i18n_gettext(
568             'url_verify.url_verification_max_wait',
569             'If we exceed the wait time, the URL is marked as a "timeout" and the system moves on to the next URL',
570             'coust',
571             'description'
572         ),
573         'integer',
574         544
575     );
576
577
578 INSERT INTO config.org_unit_setting_type
579     (name, grp, label, description, datatype, update_perm)
580     VALUES (
581         'url_verify.verification_batch_size',
582         'url_verify',
583         oils_i18n_gettext(
584             'url_verify.verification_batch_size',
585             'Number of URLs to test in parallel',
586             'coust',
587             'label'
588         ),
589         oils_i18n_gettext(
590             'url_verify.verification_batch_size',
591             'URLs are tested in batches.  This number defines the size of each batch and it directly relates to the number of back-end processes performing URL verification.',
592             'coust',
593             'description'
594         ),
595         'integer',
596         544
597     );
598
599
600 INSERT INTO config.filter_dialog_interface (key, description) VALUES (
601     'url_verify',
602     oils_i18n_gettext(
603         'url_verify',
604         'All Link Checker filter dialogs',
605         'cfdi',
606         'description'
607     )
608 );
609
610
611 INSERT INTO config.usr_setting_type (name,grp,opac_visible,label,description,datatype) VALUES (
612     'ui.grid_columns.url_verify.select_urls',
613     'gui',
614     FALSE,
615     oils_i18n_gettext(
616         'ui.grid_columns.url_verify.select_urls',
617         'Link Checker''s URL Selection interface''s saved columns',
618         'cust',
619         'label'
620     ),
621     oils_i18n_gettext(
622         'ui.grid_columns.url_verify.select_urls',
623         'Link Checker''s URL Selection interface''s saved columns',
624         'cust',
625         'description'
626     ),
627     'string'
628 );
629
630 INSERT INTO config.usr_setting_type (name,grp,opac_visible,label,description,datatype) VALUES (
631     'ui.grid_columns.url_verify.review_attempt',
632     'gui',
633     FALSE,
634     oils_i18n_gettext(
635         'ui.grid_columns.url_verify.review_attempt',
636         'Link Checker''s Review Attempt interface''s saved columns',
637         'cust',
638         'label'
639     ),
640     oils_i18n_gettext(
641         'ui.grid_columns.url_verify.review_attempt',
642         'Link Checker''s Review Attempt interface''s saved columns',
643         'cust',
644         'description'
645     ),
646     'string'
647 );
648
649
650
651
652 SELECT evergreen.upgrade_deps_block_check('0755', :eg_version);
653
654 INSERT INTO config.org_unit_setting_type
655     (name, label, description, grp, datatype, fm_class) VALUES
656 (
657     'acq.upload.default.create_po',
658     oils_i18n_gettext(
659         'acq.upload.default.create_po',
660         'Upload Create PO',
661         'coust',
662         'label'
663     ),
664      oils_i18n_gettext(
665         'acq.upload.default.create_po',
666         'Create a purchase order by default during ACQ file upload',
667         'coust',
668         'description'
669     ),
670    'acq',
671     'bool',
672     NULL
673 ), (
674     'acq.upload.default.activate_po',
675     oils_i18n_gettext(
676         'acq.upload.default.activate_po',
677         'Upload Activate PO',
678         'coust',
679         'label'
680     ),
681      oils_i18n_gettext(
682         'acq.upload.default.activate_po',
683         'Activate the purchase order by default during ACQ file upload',
684         'coust',
685         'description'
686     ),
687     'acq',
688     'bool',
689     NULL
690 ), (
691     'acq.upload.default.provider',
692     oils_i18n_gettext(
693         'acq.upload.default.provider',
694         'Upload Default Provider',
695         'coust',
696         'label'
697     ),
698      oils_i18n_gettext(
699         'acq.upload.default.provider',
700         'Default provider to use during ACQ file upload',
701         'coust',
702         'description'
703     ),
704     'acq',
705     'link',
706     'acqpro'
707 ), (
708     'acq.upload.default.vandelay.match_set',
709     oils_i18n_gettext(
710         'acq.upload.default.vandelay.match_set',
711         'Upload Default Match Set',
712         'coust',
713         'label'
714     ),
715      oils_i18n_gettext(
716         'acq.upload.default.vandelay.match_set',
717         'Default match set to use during ACQ file upload',
718         'coust',
719         'description'
720     ),
721     'acq',
722     'link',
723     'vms'
724 ), (
725     'acq.upload.default.vandelay.merge_profile',
726     oils_i18n_gettext(
727         'acq.upload.default.vandelay.merge_profile',
728         'Upload Default Merge Profile',
729         'coust',
730         'label'
731     ),
732      oils_i18n_gettext(
733         'acq.upload.default.vandelay.merge_profile',
734         'Default merge profile to use during ACQ file upload',
735         'coust',
736         'description'
737     ),
738     'acq',
739     'link',
740     'vmp'
741 ), (
742     'acq.upload.default.vandelay.import_non_matching',
743     oils_i18n_gettext(
744         'acq.upload.default.vandelay.import_non_matching',
745         'Upload Import Non Matching by Default',
746         'coust',
747         'label'
748     ),
749      oils_i18n_gettext(
750         'acq.upload.default.vandelay.import_non_matching',
751         'Import non-matching records by default during ACQ file upload',
752         'coust',
753         'description'
754     ),
755     'acq',
756     'bool',
757     NULL
758 ), (
759     'acq.upload.default.vandelay.merge_on_exact',
760     oils_i18n_gettext(
761         'acq.upload.default.vandelay.merge_on_exact',
762         'Upload Merge on Exact Match by Default',
763         'coust',
764         'label'
765     ),
766      oils_i18n_gettext(
767         'acq.upload.default.vandelay.merge_on_exact',
768         'Merge records on exact match by default during ACQ file upload',
769         'coust',
770         'description'
771     ),
772     'acq',
773     'bool',
774     NULL
775 ), (
776     'acq.upload.default.vandelay.merge_on_best',
777     oils_i18n_gettext(
778         'acq.upload.default.vandelay.merge_on_best',
779         'Upload Merge on Best Match by Default',
780         'coust',
781         'label'
782     ),
783      oils_i18n_gettext(
784         'acq.upload.default.vandelay.merge_on_best',
785         'Merge records on best match by default during ACQ file upload',
786         'coust',
787         'description'
788     ),
789     'acq',
790     'bool',
791     NULL
792 ), (
793     'acq.upload.default.vandelay.merge_on_single',
794     oils_i18n_gettext(
795         'acq.upload.default.vandelay.merge_on_single',
796         'Upload Merge on Single Match by Default',
797         'coust',
798         'label'
799     ),
800      oils_i18n_gettext(
801         'acq.upload.default.vandelay.merge_on_single',
802         'Merge records on single match by default during ACQ file upload',
803         'coust',
804         'description'
805     ),
806     'acq',
807     'bool',
808     NULL
809 ), (
810     'acq.upload.default.vandelay.quality_ratio',
811     oils_i18n_gettext(
812         'acq.upload.default.vandelay.quality_ratio',
813         'Upload Default Min. Quality Ratio',
814         'coust',
815         'label'
816     ),
817      oils_i18n_gettext(
818         'acq.upload.default.vandelay.quality_ratio',
819         'Default minimum quality ratio used during ACQ file upload',
820         'coust',
821         'description'
822     ),
823     'acq',
824     'integer',
825     NULL
826 ), (
827     'acq.upload.default.vandelay.low_quality_fall_thru_profile',
828     oils_i18n_gettext(
829         'acq.upload.default.vandelay.low_quality_fall_thru_profile',
830         'Upload Default Insufficient Quality Fall-Thru Profile',
831         'coust',
832         'label'
833     ),
834      oils_i18n_gettext(
835         'acq.upload.default.vandelay.low_quality_fall_thru_profile',
836         'Default low-quality fall through profile used during ACQ file upload',
837         'coust',
838         'description'
839     ),
840     'acq',
841     'link',
842     'vmp'
843 ), (
844     'acq.upload.default.vandelay.load_item_for_imported',
845     oils_i18n_gettext(
846         'acq.upload.default.vandelay.load_item_for_imported',
847         'Upload Load Items for Imported Records by Default',
848         'coust',
849         'label'
850     ),
851      oils_i18n_gettext(
852         'acq.upload.default.vandelay.load_item_for_imported',
853         'Load items for imported records by default during ACQ file upload',
854         'coust',
855         'description'
856     ),
857     'acq',
858     'bool',
859     NULL
860 );
861
862
863 SELECT evergreen.upgrade_deps_block_check('0756', :eg_version);
864
865 -- Drop some lingering old functions in search schema
866 DROP FUNCTION IF EXISTS search.staged_fts(INT,INT,TEXT,INT[],INT[],TEXT[],TEXT[],TEXT[],TEXT[],TEXT[],TEXT[],TEXT[],TEXT,TEXT,TEXT,TEXT[],TEXT,REAL,TEXT,BOOL,BOOL,BOOL,INT,INT,INT);
867 DROP FUNCTION IF EXISTS search.parse_search_args(TEXT);
868 DROP FUNCTION IF EXISTS search.explode_array(ANYARRAY);
869 DROP FUNCTION IF EXISTS search.pick_table(TEXT);
870
871 -- Now drop query_parser_fts and related
872 DROP FUNCTION IF EXISTS search.query_parser_fts(INT,INT,TEXT,INT[],INT[],INT,INT,INT,BOOL,BOOL,INT);
873 DROP TYPE IF EXISTS search.search_result;
874 DROP TYPE IF EXISTS search.search_args;
875
876
877 SELECT evergreen.upgrade_deps_block_check('0757', :eg_version);
878
879 SET search_path = public, pg_catalog;
880
881 DO $$
882 DECLARE
883 lang TEXT;
884 BEGIN
885 FOR lang IN SELECT substring(pptsd.dictname from '(.*)_stem$') AS lang FROM pg_catalog.pg_ts_dict pptsd JOIN pg_catalog.pg_namespace ppn ON ppn.oid = pptsd.dictnamespace
886 WHERE ppn.nspname = 'pg_catalog' AND pptsd.dictname LIKE '%_stem' LOOP
887 RAISE NOTICE 'FOUND LANGUAGE %', lang;
888
889 EXECUTE 'DROP TEXT SEARCH DICTIONARY IF EXISTS ' || lang || '_nostop CASCADE;
890 CREATE TEXT SEARCH DICTIONARY ' || lang || '_nostop (TEMPLATE=pg_catalog.snowball, language=''' || lang || ''');
891 COMMENT ON TEXT SEARCH DICTIONARY ' || lang || '_nostop IS ''' ||lang || ' snowball stemmer with no stopwords for ASCII words only.'';
892 CREATE TEXT SEARCH CONFIGURATION ' || lang || '_nostop ( COPY = pg_catalog.' || lang || ' );
893 ALTER TEXT SEARCH CONFIGURATION ' || lang || '_nostop ALTER MAPPING FOR word, hword, hword_part WITH pg_catalog.simple;
894 ALTER TEXT SEARCH CONFIGURATION ' || lang || '_nostop ALTER MAPPING FOR asciiword, asciihword, hword_asciipart WITH ' || lang || '_nostop;';
895
896 END LOOP;
897 END;
898 $$;
899 CREATE TEXT SEARCH CONFIGURATION keyword ( COPY = english_nostop );
900 CREATE TEXT SEARCH CONFIGURATION "default" ( COPY = english_nostop );
901
902 SET search_path = evergreen, public, pg_catalog;
903
904 ALTER TABLE config.metabib_class
905     ADD COLUMN a_weight NUMERIC  DEFAULT 1.0 NOT NULL,
906     ADD COLUMN b_weight NUMERIC  DEFAULT 0.4 NOT NULL,
907     ADD COLUMN c_weight NUMERIC  DEFAULT 0.2 NOT NULL,
908     ADD COLUMN d_weight NUMERIC  DEFAULT 0.1 NOT NULL;
909
910 CREATE TABLE config.ts_config_list (
911     id      TEXT PRIMARY KEY,
912     name    TEXT NOT NULL
913 );
914 COMMENT ON TABLE config.ts_config_list IS $$
915 Full Text Configs
916
917 A list of full text configs with names and descriptions.
918 $$;
919
920 CREATE TABLE config.metabib_class_ts_map (
921     id              SERIAL PRIMARY KEY,
922     field_class     TEXT NOT NULL REFERENCES config.metabib_class (name),
923     ts_config       TEXT NOT NULL REFERENCES config.ts_config_list (id),
924     active          BOOL NOT NULL DEFAULT TRUE,
925     index_weight    CHAR(1) NOT NULL DEFAULT 'C' CHECK (index_weight IN ('A','B','C','D')),
926     index_lang      TEXT NULL,
927     search_lang     TEXT NULL,
928     always          BOOL NOT NULL DEFAULT true
929 );
930 COMMENT ON TABLE config.metabib_class_ts_map IS $$
931 Text Search Configs for metabib class indexing
932
933 This table contains text search config definitions for
934 storing index_vector values.
935 $$;
936
937 CREATE TABLE config.metabib_field_ts_map (
938     id              SERIAL PRIMARY KEY,
939     metabib_field   INT NOT NULL REFERENCES config.metabib_field (id),
940     ts_config       TEXT NOT NULL REFERENCES config.ts_config_list (id),
941     active          BOOL NOT NULL DEFAULT TRUE,
942     index_weight    CHAR(1) NOT NULL DEFAULT 'C' CHECK (index_weight IN ('A','B','C','D')),
943     index_lang      TEXT NULL,
944     search_lang     TEXT NULL
945 );
946 COMMENT ON TABLE config.metabib_field_ts_map IS $$
947 Text Search Configs for metabib field indexing
948
949 This table contains text search config definitions for
950 storing index_vector values.
951 $$;
952
953 CREATE TABLE metabib.combined_identifier_field_entry (
954     record          BIGINT      NOT NULL,
955     metabib_field   INT         NULL,
956     index_vector    tsvector    NOT NULL
957 );
958 CREATE UNIQUE INDEX metabib_combined_identifier_field_entry_fakepk_idx ON metabib.combined_identifier_field_entry (record, COALESCE(metabib_field::TEXT,''));
959 CREATE INDEX metabib_combined_identifier_field_entry_index_vector_idx ON metabib.combined_identifier_field_entry USING GIST (index_vector);
960 CREATE INDEX metabib_combined_identifier_field_source_idx ON metabib.combined_identifier_field_entry (metabib_field);
961
962 CREATE TABLE metabib.combined_title_field_entry (
963         record          BIGINT          NOT NULL,
964         metabib_field           INT             NULL,
965         index_vector    tsvector        NOT NULL
966 );
967 CREATE UNIQUE INDEX metabib_combined_title_field_entry_fakepk_idx ON metabib.combined_title_field_entry (record, COALESCE(metabib_field::TEXT,''));
968 CREATE INDEX metabib_combined_title_field_entry_index_vector_idx ON metabib.combined_title_field_entry USING GIST (index_vector);
969 CREATE INDEX metabib_combined_title_field_source_idx ON metabib.combined_title_field_entry (metabib_field);
970
971 CREATE TABLE metabib.combined_author_field_entry (
972         record          BIGINT          NOT NULL,
973         metabib_field           INT             NULL,
974         index_vector    tsvector        NOT NULL
975 );
976 CREATE UNIQUE INDEX metabib_combined_author_field_entry_fakepk_idx ON metabib.combined_author_field_entry (record, COALESCE(metabib_field::TEXT,''));
977 CREATE INDEX metabib_combined_author_field_entry_index_vector_idx ON metabib.combined_author_field_entry USING GIST (index_vector);
978 CREATE INDEX metabib_combined_author_field_source_idx ON metabib.combined_author_field_entry (metabib_field);
979
980 CREATE TABLE metabib.combined_subject_field_entry (
981         record          BIGINT          NOT NULL,
982         metabib_field           INT             NULL,
983         index_vector    tsvector        NOT NULL
984 );
985 CREATE UNIQUE INDEX metabib_combined_subject_field_entry_fakepk_idx ON metabib.combined_subject_field_entry (record, COALESCE(metabib_field::TEXT,''));
986 CREATE INDEX metabib_combined_subject_field_entry_index_vector_idx ON metabib.combined_subject_field_entry USING GIST (index_vector);
987 CREATE INDEX metabib_combined_subject_field_source_idx ON metabib.combined_subject_field_entry (metabib_field);
988
989 CREATE TABLE metabib.combined_keyword_field_entry (
990         record          BIGINT          NOT NULL,
991         metabib_field           INT             NULL,
992         index_vector    tsvector        NOT NULL
993 );
994 CREATE UNIQUE INDEX metabib_combined_keyword_field_entry_fakepk_idx ON metabib.combined_keyword_field_entry (record, COALESCE(metabib_field::TEXT,''));
995 CREATE INDEX metabib_combined_keyword_field_entry_index_vector_idx ON metabib.combined_keyword_field_entry USING GIST (index_vector);
996 CREATE INDEX metabib_combined_keyword_field_source_idx ON metabib.combined_keyword_field_entry (metabib_field);
997
998 CREATE TABLE metabib.combined_series_field_entry (
999         record          BIGINT          NOT NULL,
1000         metabib_field           INT             NULL,
1001         index_vector    tsvector        NOT NULL
1002 );
1003 CREATE UNIQUE INDEX metabib_combined_series_field_entry_fakepk_idx ON metabib.combined_series_field_entry (record, COALESCE(metabib_field::TEXT,''));
1004 CREATE INDEX metabib_combined_series_field_entry_index_vector_idx ON metabib.combined_series_field_entry USING GIST (index_vector);
1005 CREATE INDEX metabib_combined_series_field_source_idx ON metabib.combined_series_field_entry (metabib_field);
1006
1007 CREATE OR REPLACE FUNCTION metabib.update_combined_index_vectors(bib_id BIGINT) RETURNS VOID AS $func$
1008 BEGIN
1009     DELETE FROM metabib.combined_keyword_field_entry WHERE record = bib_id;
1010     INSERT INTO metabib.combined_keyword_field_entry(record, metabib_field, index_vector)
1011         SELECT bib_id, field, strip(COALESCE(string_agg(index_vector::TEXT,' '),'')::tsvector)
1012         FROM metabib.keyword_field_entry WHERE source = bib_id GROUP BY field;
1013     INSERT INTO metabib.combined_keyword_field_entry(record, metabib_field, index_vector)
1014         SELECT bib_id, NULL, strip(COALESCE(string_agg(index_vector::TEXT,' '),'')::tsvector)
1015         FROM metabib.keyword_field_entry WHERE source = bib_id;
1016
1017     DELETE FROM metabib.combined_title_field_entry WHERE record = bib_id;
1018     INSERT INTO metabib.combined_title_field_entry(record, metabib_field, index_vector)
1019         SELECT bib_id, field, strip(COALESCE(string_agg(index_vector::TEXT,' '),'')::tsvector)
1020         FROM metabib.title_field_entry WHERE source = bib_id GROUP BY field;
1021     INSERT INTO metabib.combined_title_field_entry(record, metabib_field, index_vector)
1022         SELECT bib_id, NULL, strip(COALESCE(string_agg(index_vector::TEXT,' '),'')::tsvector)
1023         FROM metabib.title_field_entry WHERE source = bib_id;
1024
1025     DELETE FROM metabib.combined_author_field_entry WHERE record = bib_id;
1026     INSERT INTO metabib.combined_author_field_entry(record, metabib_field, index_vector)
1027         SELECT bib_id, field, strip(COALESCE(string_agg(index_vector::TEXT,' '),'')::tsvector)
1028         FROM metabib.author_field_entry WHERE source = bib_id GROUP BY field;
1029     INSERT INTO metabib.combined_author_field_entry(record, metabib_field, index_vector)
1030         SELECT bib_id, NULL, strip(COALESCE(string_agg(index_vector::TEXT,' '),'')::tsvector)
1031         FROM metabib.author_field_entry WHERE source = bib_id;
1032
1033     DELETE FROM metabib.combined_subject_field_entry WHERE record = bib_id;
1034     INSERT INTO metabib.combined_subject_field_entry(record, metabib_field, index_vector)
1035         SELECT bib_id, field, strip(COALESCE(string_agg(index_vector::TEXT,' '),'')::tsvector)
1036         FROM metabib.subject_field_entry WHERE source = bib_id GROUP BY field;
1037     INSERT INTO metabib.combined_subject_field_entry(record, metabib_field, index_vector)
1038         SELECT bib_id, NULL, strip(COALESCE(string_agg(index_vector::TEXT,' '),'')::tsvector)
1039         FROM metabib.subject_field_entry WHERE source = bib_id;
1040
1041     DELETE FROM metabib.combined_series_field_entry WHERE record = bib_id;
1042     INSERT INTO metabib.combined_series_field_entry(record, metabib_field, index_vector)
1043         SELECT bib_id, field, strip(COALESCE(string_agg(index_vector::TEXT,' '),'')::tsvector)
1044         FROM metabib.series_field_entry WHERE source = bib_id GROUP BY field;
1045     INSERT INTO metabib.combined_series_field_entry(record, metabib_field, index_vector)
1046         SELECT bib_id, NULL, strip(COALESCE(string_agg(index_vector::TEXT,' '),'')::tsvector)
1047         FROM metabib.series_field_entry WHERE source = bib_id;
1048
1049     DELETE FROM metabib.combined_identifier_field_entry WHERE record = bib_id;
1050     INSERT INTO metabib.combined_identifier_field_entry(record, metabib_field, index_vector)
1051         SELECT bib_id, field, strip(COALESCE(string_agg(index_vector::TEXT,' '),'')::tsvector)
1052         FROM metabib.identifier_field_entry WHERE source = bib_id GROUP BY field;
1053     INSERT INTO metabib.combined_identifier_field_entry(record, metabib_field, index_vector)
1054         SELECT bib_id, NULL, strip(COALESCE(string_agg(index_vector::TEXT,' '),'')::tsvector)
1055         FROM metabib.identifier_field_entry WHERE source = bib_id;
1056
1057 END;
1058 $func$ LANGUAGE PLPGSQL;
1059
1060 CREATE OR REPLACE FUNCTION metabib.reingest_metabib_field_entries( bib_id BIGINT, skip_facet BOOL DEFAULT FALSE, skip_browse BOOL DEFAULT FALSE, skip_search BOOL DEFAULT FALSE ) RETURNS VOID AS $func$
1061 DECLARE
1062     fclass          RECORD;
1063     ind_data        metabib.field_entry_template%ROWTYPE;
1064     mbe_row         metabib.browse_entry%ROWTYPE;
1065     mbe_id          BIGINT;
1066 BEGIN
1067     PERFORM * FROM config.internal_flag WHERE name = 'ingest.assume_inserts_only' AND enabled;
1068     IF NOT FOUND THEN
1069         IF NOT skip_search THEN
1070             FOR fclass IN SELECT * FROM config.metabib_class LOOP
1071                 -- RAISE NOTICE 'Emptying out %', fclass.name;
1072                 EXECUTE $$DELETE FROM metabib.$$ || fclass.name || $$_field_entry WHERE source = $$ || bib_id;
1073             END LOOP;
1074         END IF;
1075         IF NOT skip_facet THEN
1076             DELETE FROM metabib.facet_entry WHERE source = bib_id;
1077         END IF;
1078         IF NOT skip_browse THEN
1079             DELETE FROM metabib.browse_entry_def_map WHERE source = bib_id;
1080         END IF;
1081     END IF;
1082
1083     FOR ind_data IN SELECT * FROM biblio.extract_metabib_field_entry( bib_id ) LOOP
1084         IF ind_data.field < 0 THEN
1085             ind_data.field = -1 * ind_data.field;
1086         END IF;
1087
1088         IF ind_data.facet_field AND NOT skip_facet THEN
1089             INSERT INTO metabib.facet_entry (field, source, value)
1090                 VALUES (ind_data.field, ind_data.source, ind_data.value);
1091         END IF;
1092
1093         IF ind_data.browse_field AND NOT skip_browse THEN
1094             -- A caveat about this SELECT: this should take care of replacing
1095             -- old mbe rows when data changes, but not if normalization (by
1096             -- which I mean specifically the output of
1097             -- evergreen.oils_tsearch2()) changes.  It may or may not be
1098             -- expensive to add a comparison of index_vector to index_vector
1099             -- to the WHERE clause below.
1100             SELECT INTO mbe_row * FROM metabib.browse_entry WHERE value = ind_data.value;
1101             IF FOUND THEN
1102                 mbe_id := mbe_row.id;
1103             ELSE
1104                 INSERT INTO metabib.browse_entry (value) VALUES
1105                     (metabib.browse_normalize(ind_data.value, ind_data.field));
1106                 mbe_id := CURRVAL('metabib.browse_entry_id_seq'::REGCLASS);
1107             END IF;
1108
1109             INSERT INTO metabib.browse_entry_def_map (entry, def, source)
1110                 VALUES (mbe_id, ind_data.field, ind_data.source);
1111         END IF;
1112
1113         IF ind_data.search_field AND NOT skip_search THEN
1114             EXECUTE $$
1115                 INSERT INTO metabib.$$ || ind_data.field_class || $$_field_entry (field, source, value)
1116                     VALUES ($$ ||
1117                         quote_literal(ind_data.field) || $$, $$ ||
1118                         quote_literal(ind_data.source) || $$, $$ ||
1119                         quote_literal(ind_data.value) ||
1120                     $$);$$;
1121         END IF;
1122
1123     END LOOP;
1124
1125     IF NOT skip_search THEN
1126         PERFORM metabib.update_combined_index_vectors(bib_id);
1127     END IF;
1128
1129     RETURN;
1130 END;
1131 $func$ LANGUAGE PLPGSQL;
1132
1133 DROP FUNCTION IF EXISTS evergreen.oils_tsearch2() CASCADE;
1134 DROP FUNCTION IF EXISTS public.oils_tsearch2() CASCADE;
1135
1136 CREATE OR REPLACE FUNCTION public.oils_tsearch2 () RETURNS TRIGGER AS $$
1137 DECLARE
1138     normalizer      RECORD;
1139     value           TEXT := '';
1140     temp_vector     TEXT := '';
1141     ts_rec          RECORD;
1142     cur_weight      "char";
1143 BEGIN
1144     value := NEW.value;
1145     NEW.index_vector = ''::tsvector;
1146
1147     IF TG_TABLE_NAME::TEXT ~ 'field_entry$' THEN
1148         FOR normalizer IN
1149             SELECT  n.func AS func,
1150                     n.param_count AS param_count,
1151                     m.params AS params
1152               FROM  config.index_normalizer n
1153                     JOIN config.metabib_field_index_norm_map m ON (m.norm = n.id)
1154               WHERE field = NEW.field
1155               ORDER BY m.pos LOOP
1156                 EXECUTE 'SELECT ' || normalizer.func || '(' ||
1157                     quote_literal( value ) ||
1158                     CASE
1159                         WHEN normalizer.param_count > 0
1160                             THEN ',' || REPLACE(REPLACE(BTRIM(normalizer.params,'[]'),E'\'',E'\\\''),E'"',E'\'')
1161                             ELSE ''
1162                         END ||
1163                     ')' INTO value;
1164
1165         END LOOP;
1166         NEW.value = value;
1167     END IF;
1168
1169     IF TG_TABLE_NAME::TEXT ~ 'browse_entry$' THEN
1170         value :=  ARRAY_TO_STRING(
1171             evergreen.regexp_split_to_array(value, E'\\W+'), ' '
1172         );
1173         value := public.search_normalize(value);
1174         NEW.index_vector = to_tsvector(TG_ARGV[0]::regconfig, value);
1175     ELSIF TG_TABLE_NAME::TEXT ~ 'field_entry$' THEN
1176         FOR ts_rec IN
1177             SELECT ts_config, index_weight
1178             FROM config.metabib_class_ts_map
1179             WHERE field_class = TG_ARGV[0]
1180                 AND index_lang IS NULL OR EXISTS (SELECT 1 FROM metabib.record_attr WHERE id = NEW.source AND index_lang IN(attrs->'item_lang',attrs->'language'))
1181                 AND always OR NOT EXISTS (SELECT 1 FROM config.metabib_field_ts_map WHERE metabib_field = NEW.field)
1182             UNION
1183             SELECT ts_config, index_weight
1184             FROM config.metabib_field_ts_map
1185             WHERE metabib_field = NEW.field
1186                AND index_lang IS NULL OR EXISTS (SELECT 1 FROM metabib.record_attr WHERE id = NEW.source AND index_lang IN(attrs->'item_lang',attrs->'language'))
1187             ORDER BY index_weight ASC
1188         LOOP
1189             IF cur_weight IS NOT NULL AND cur_weight != ts_rec.index_weight THEN
1190                 NEW.index_vector = NEW.index_vector || setweight(temp_vector::tsvector,cur_weight);
1191                 temp_vector = '';
1192             END IF;
1193             cur_weight = ts_rec.index_weight;
1194             SELECT INTO temp_vector temp_vector || ' ' || to_tsvector(ts_rec.ts_config::regconfig, value)::TEXT;
1195         END LOOP;
1196         NEW.index_vector = NEW.index_vector || setweight(temp_vector::tsvector,cur_weight);
1197     ELSE
1198         NEW.index_vector = to_tsvector(TG_ARGV[0]::regconfig, value);
1199     END IF;
1200
1201     RETURN NEW;
1202 END;
1203 $$ LANGUAGE PLPGSQL;
1204
1205 CREATE TRIGGER authority_full_rec_fti_trigger
1206     BEFORE UPDATE OR INSERT ON authority.full_rec
1207     FOR EACH ROW EXECUTE PROCEDURE oils_tsearch2('keyword');
1208
1209 CREATE TRIGGER authority_simple_heading_fti_trigger
1210     BEFORE UPDATE OR INSERT ON authority.simple_heading
1211     FOR EACH ROW EXECUTE PROCEDURE oils_tsearch2('keyword');
1212
1213 CREATE TRIGGER metabib_identifier_field_entry_fti_trigger
1214     BEFORE UPDATE OR INSERT ON metabib.identifier_field_entry
1215     FOR EACH ROW EXECUTE PROCEDURE oils_tsearch2('identifier');
1216
1217 CREATE TRIGGER metabib_title_field_entry_fti_trigger
1218     BEFORE UPDATE OR INSERT ON metabib.title_field_entry
1219     FOR EACH ROW EXECUTE PROCEDURE oils_tsearch2('title');
1220
1221 CREATE TRIGGER metabib_author_field_entry_fti_trigger
1222     BEFORE UPDATE OR INSERT ON metabib.author_field_entry
1223     FOR EACH ROW EXECUTE PROCEDURE oils_tsearch2('author');
1224
1225 CREATE TRIGGER metabib_subject_field_entry_fti_trigger
1226     BEFORE UPDATE OR INSERT ON metabib.subject_field_entry
1227     FOR EACH ROW EXECUTE PROCEDURE oils_tsearch2('subject');
1228
1229 CREATE TRIGGER metabib_keyword_field_entry_fti_trigger
1230     BEFORE UPDATE OR INSERT ON metabib.keyword_field_entry
1231     FOR EACH ROW EXECUTE PROCEDURE oils_tsearch2('keyword');
1232
1233 CREATE TRIGGER metabib_series_field_entry_fti_trigger
1234     BEFORE UPDATE OR INSERT ON metabib.series_field_entry
1235     FOR EACH ROW EXECUTE PROCEDURE oils_tsearch2('series');
1236
1237 CREATE TRIGGER metabib_browse_entry_fti_trigger
1238     BEFORE INSERT OR UPDATE ON metabib.browse_entry
1239     FOR EACH ROW EXECUTE PROCEDURE oils_tsearch2('keyword');
1240
1241 CREATE TRIGGER metabib_full_rec_fti_trigger
1242     BEFORE UPDATE OR INSERT ON metabib.real_full_rec
1243     FOR EACH ROW EXECUTE PROCEDURE oils_tsearch2('default');
1244
1245 INSERT INTO config.ts_config_list(id, name) VALUES
1246     ('simple','Non-Stemmed Simple'),
1247     ('danish_nostop','Danish Stemmed'),
1248     ('dutch_nostop','Dutch Stemmed'),
1249     ('english_nostop','English Stemmed'),
1250     ('finnish_nostop','Finnish Stemmed'),
1251     ('french_nostop','French Stemmed'),
1252     ('german_nostop','German Stemmed'),
1253     ('hungarian_nostop','Hungarian Stemmed'),
1254     ('italian_nostop','Italian Stemmed'),
1255     ('norwegian_nostop','Norwegian Stemmed'),
1256     ('portuguese_nostop','Portuguese Stemmed'),
1257     ('romanian_nostop','Romanian Stemmed'),
1258     ('russian_nostop','Russian Stemmed'),
1259     ('spanish_nostop','Spanish Stemmed'),
1260     ('swedish_nostop','Swedish Stemmed'),
1261     ('turkish_nostop','Turkish Stemmed');
1262
1263 INSERT INTO config.metabib_class_ts_map(field_class, ts_config, index_weight, always) VALUES
1264     ('keyword','simple','A',true),
1265     ('keyword','english_nostop','C',true),
1266     ('title','simple','A',true),
1267     ('title','english_nostop','C',true),
1268     ('author','simple','A',true),
1269     ('author','english_nostop','C',true),
1270     ('series','simple','A',true),
1271     ('series','english_nostop','C',true),
1272     ('subject','simple','A',true),
1273     ('subject','english_nostop','C',true),
1274     ('identifier','simple','A',true);
1275
1276 CREATE OR REPLACE FUNCTION evergreen.rel_bump(terms TEXT[], value TEXT, bumps TEXT[], mults NUMERIC[]) RETURNS NUMERIC AS
1277 $BODY$
1278 use strict;
1279 my ($terms,$value,$bumps,$mults) = @_;
1280
1281 my $retval = 1;
1282
1283 for (my $id = 0; $id < @$bumps; $id++) {
1284         if ($bumps->[$id] eq 'first_word') {
1285                 $retval *= $mults->[$id] if ($value =~ /^$terms->[0]/);
1286         } elsif ($bumps->[$id] eq 'full_match') {
1287                 my $fullmatch = join(' ', @$terms);
1288                 $retval *= $mults->[$id] if ($value =~ /^$fullmatch$/);
1289         } elsif ($bumps->[$id] eq 'word_order') {
1290                 my $wordorder = join('.*', @$terms);
1291                 $retval *= $mults->[$id] if ($value =~ /$wordorder/);
1292         }
1293 }
1294 return $retval;
1295 $BODY$ LANGUAGE plperlu IMMUTABLE STRICT COST 100;
1296
1297 UPDATE metabib.identifier_field_entry set value = value;
1298 UPDATE metabib.title_field_entry set value = value;
1299 UPDATE metabib.author_field_entry set value = value;
1300 UPDATE metabib.subject_field_entry set value = value;
1301 UPDATE metabib.keyword_field_entry set value = value;
1302 UPDATE metabib.series_field_entry set value = value;
1303
1304 SELECT metabib.update_combined_index_vectors(id)
1305     FROM biblio.record_entry
1306     WHERE NOT deleted;
1307
1308
1309 SELECT evergreen.upgrade_deps_block_check('0758', :eg_version);
1310
1311 INSERT INTO config.settings_group (name, label) VALUES
1312     ('vandelay', 'Vandelay');
1313
1314 INSERT INTO config.org_unit_setting_type (name, grp, label, datatype, fm_class) VALUES
1315     ('vandelay.default_match_set', 'vandelay', 'Default Record Match Set', 'link', 'vms');
1316
1317
1318 SELECT evergreen.upgrade_deps_block_check('0759', :eg_version);
1319
1320 CREATE TABLE actor.org_unit_proximity_adjustment (
1321     id                  SERIAL   PRIMARY KEY,
1322     item_circ_lib       INT         REFERENCES actor.org_unit (id),
1323     item_owning_lib     INT         REFERENCES actor.org_unit (id),
1324     copy_location       INT         REFERENCES asset.copy_location (id),
1325     hold_pickup_lib     INT         REFERENCES actor.org_unit (id),
1326     hold_request_lib    INT         REFERENCES actor.org_unit (id),
1327     pos                 INT         NOT NULL DEFAULT 0,
1328     absolute_adjustment BOOL        NOT NULL DEFAULT FALSE,
1329     prox_adjustment     NUMERIC,
1330     circ_mod            TEXT,       -- REFERENCES config.circ_modifier (code),
1331     CONSTRAINT prox_adj_criterium CHECK (COALESCE(item_circ_lib::TEXT,item_owning_lib::TEXT,copy_location::TEXT,hold_pickup_lib::TEXT,hold_request_lib::TEXT,circ_mod) IS NOT NULL)
1332 );
1333 CREATE UNIQUE INDEX prox_adj_once_idx ON actor.org_unit_proximity_adjustment (item_circ_lib,item_owning_lib,copy_location,hold_pickup_lib,hold_request_lib,circ_mod);
1334 CREATE INDEX prox_adj_circ_lib_idx ON actor.org_unit_proximity_adjustment (item_circ_lib);
1335 CREATE INDEX prox_adj_owning_lib_idx ON actor.org_unit_proximity_adjustment (item_owning_lib);
1336 CREATE INDEX prox_adj_copy_location_idx ON actor.org_unit_proximity_adjustment (copy_location);
1337 CREATE INDEX prox_adj_pickup_lib_idx ON actor.org_unit_proximity_adjustment (hold_pickup_lib);
1338 CREATE INDEX prox_adj_request_lib_idx ON actor.org_unit_proximity_adjustment (hold_request_lib);
1339 CREATE INDEX prox_adj_circ_mod_idx ON actor.org_unit_proximity_adjustment (circ_mod);
1340
1341 CREATE OR REPLACE FUNCTION actor.org_unit_ancestors_distance( INT ) RETURNS TABLE (id INT, distance INT) AS $$
1342     WITH RECURSIVE org_unit_ancestors_distance(id, distance) AS (
1343             SELECT $1, 0
1344         UNION
1345             SELECT ou.parent_ou, ouad.distance+1
1346             FROM actor.org_unit ou JOIN org_unit_ancestors_distance ouad ON (ou.id = ouad.id)
1347             WHERE ou.parent_ou IS NOT NULL
1348     )
1349     SELECT * FROM org_unit_ancestors_distance;
1350 $$ LANGUAGE SQL STABLE ROWS 1;
1351
1352 CREATE OR REPLACE FUNCTION action.hold_copy_calculated_proximity(
1353     ahr_id INT,
1354     acp_id BIGINT,
1355     copy_context_ou INT DEFAULT NULL
1356     -- TODO maybe? hold_context_ou INT DEFAULT NULL.  This would optionally
1357     -- support an "ahprox" measurement: adjust prox between copy circ lib and
1358     -- hold request lib, but I'm unsure whether to use this theoretical
1359     -- argument only in the baseline calculation or later in the other
1360     -- queries in this function.
1361 ) RETURNS NUMERIC AS $f$
1362 DECLARE
1363     aoupa           actor.org_unit_proximity_adjustment%ROWTYPE;
1364     ahr             action.hold_request%ROWTYPE;
1365     acp             asset.copy%ROWTYPE;
1366     acn             asset.call_number%ROWTYPE;
1367     acl             asset.copy_location%ROWTYPE;
1368     baseline_prox   NUMERIC;
1369
1370     icl_list        INT[];
1371     iol_list        INT[];
1372     isl_list        INT[];
1373     hpl_list        INT[];
1374     hrl_list        INT[];
1375
1376 BEGIN
1377
1378     SELECT * INTO ahr FROM action.hold_request WHERE id = ahr_id;
1379     SELECT * INTO acp FROM asset.copy WHERE id = acp_id;
1380     SELECT * INTO acn FROM asset.call_number WHERE id = acp.call_number;
1381     SELECT * INTO acl FROM asset.copy_location WHERE id = acp.location;
1382
1383     IF copy_context_ou IS NULL THEN
1384         copy_context_ou := acp.circ_lib;
1385     END IF;
1386
1387     -- First, gather the baseline proximity of "here" to pickup lib
1388     SELECT prox INTO baseline_prox FROM actor.org_unit_proximity WHERE from_org = copy_context_ou AND to_org = ahr.pickup_lib;
1389
1390     -- Find any absolute adjustments, and set the baseline prox to that
1391     SELECT  adj.* INTO aoupa
1392       FROM  actor.org_unit_proximity_adjustment adj
1393             LEFT JOIN actor.org_unit_ancestors_distance(copy_context_ou) acp_cl ON (acp_cl.id = adj.item_circ_lib)
1394             LEFT JOIN actor.org_unit_ancestors_distance(acn.owning_lib) acn_ol ON (acn_ol.id = adj.item_owning_lib)
1395             LEFT JOIN actor.org_unit_ancestors_distance(acl.owning_lib) acl_ol ON (acn_ol.id = adj.copy_location)
1396             LEFT JOIN actor.org_unit_ancestors_distance(ahr.pickup_lib) ahr_pl ON (ahr_pl.id = adj.hold_pickup_lib)
1397             LEFT JOIN actor.org_unit_ancestors_distance(ahr.request_lib) ahr_rl ON (ahr_rl.id = adj.hold_request_lib)
1398       WHERE (adj.circ_mod IS NULL OR adj.circ_mod = acp.circ_modifier) AND
1399         absolute_adjustment AND
1400         COALESCE(acp_cl.id, acn_ol.id, acl_ol.id, ahr_pl.id, ahr_rl.id) IS NOT NULL
1401       ORDER BY
1402             COALESCE(acp_cl.distance,999)
1403                 + COALESCE(acn_ol.distance,999)
1404                 + COALESCE(acl_ol.distance,999)
1405                 + COALESCE(ahr_pl.distance,999)
1406                 + COALESCE(ahr_rl.distance,999),
1407             adj.pos
1408       LIMIT 1;
1409
1410     IF FOUND THEN
1411         baseline_prox := aoupa.prox_adjustment;
1412     END IF;
1413
1414     -- Now find any relative adjustments, and change the baseline prox based on them
1415     FOR aoupa IN
1416         SELECT  adj.* 
1417           FROM  actor.org_unit_proximity_adjustment adj
1418                 LEFT JOIN actor.org_unit_ancestors_distance(copy_context_ou) acp_cl ON (acp_cl.id = adj.item_circ_lib)
1419                 LEFT JOIN actor.org_unit_ancestors_distance(acn.owning_lib) acn_ol ON (acn_ol.id = adj.item_owning_lib)
1420                 LEFT JOIN actor.org_unit_ancestors_distance(acl.owning_lib) acl_ol ON (acn_ol.id = adj.copy_location)
1421                 LEFT JOIN actor.org_unit_ancestors_distance(ahr.pickup_lib) ahr_pl ON (ahr_pl.id = adj.hold_pickup_lib)
1422                 LEFT JOIN actor.org_unit_ancestors_distance(ahr.request_lib) ahr_rl ON (ahr_rl.id = adj.hold_request_lib)
1423           WHERE (adj.circ_mod IS NULL OR adj.circ_mod = acp.circ_modifier) AND
1424             NOT absolute_adjustment AND
1425             COALESCE(acp_cl.id, acn_ol.id, acl_ol.id, ahr_pl.id, ahr_rl.id) IS NOT NULL
1426     LOOP
1427         baseline_prox := baseline_prox + aoupa.prox_adjustment;
1428     END LOOP;
1429
1430     RETURN baseline_prox;
1431 END;
1432 $f$ LANGUAGE PLPGSQL;
1433
1434 ALTER TABLE actor.org_unit_proximity_adjustment
1435     ADD CONSTRAINT actor_org_unit_proximity_adjustment_circ_mod_fkey
1436     FOREIGN KEY (circ_mod) REFERENCES config.circ_modifier (code)
1437     DEFERRABLE INITIALLY DEFERRED;
1438
1439 ALTER TABLE action.hold_copy_map ADD COLUMN proximity NUMERIC;
1440
1441
1442 SELECT evergreen.upgrade_deps_block_check('0760', :eg_version);
1443
1444 CREATE TABLE config.best_hold_order(
1445     id          SERIAL      PRIMARY KEY,    -- (metadata)
1446     name        TEXT        UNIQUE,   -- i18n (metadata)
1447     pprox       INT, -- copy capture <-> pickup lib prox
1448     hprox       INT, -- copy circ lib <-> request lib prox
1449     aprox       INT, -- copy circ lib <-> pickup lib ADJUSTED prox on ahcm
1450     approx      INT, -- copy capture <-> pickup lib ADJUSTED prox from function
1451     priority    INT, -- group hold priority
1452     cut         INT, -- cut-in-line
1453     depth       INT, -- selection depth
1454     htime       INT, -- time since last home-lib circ exceeds org-unit setting
1455     rtime       INT, -- request time
1456     shtime      INT  -- time since copy last trip home exceeds org-unit setting
1457 );
1458
1459 -- At least one of these columns must contain a non-null value
1460 ALTER TABLE config.best_hold_order ADD CHECK ((
1461     pprox IS NOT NULL OR
1462     hprox IS NOT NULL OR
1463     aprox IS NOT NULL OR
1464     priority IS NOT NULL OR
1465     cut IS NOT NULL OR
1466     depth IS NOT NULL OR
1467     htime IS NOT NULL OR
1468     rtime IS NOT NULL
1469 ));
1470
1471 INSERT INTO config.best_hold_order (
1472     name,
1473     pprox, aprox, priority, cut, depth, rtime, htime, hprox
1474 ) VALUES (
1475     'Traditional',
1476     1, 2, 3, 4, 5, 6, 7, 8
1477 );
1478
1479 INSERT INTO config.best_hold_order (
1480     name,
1481     hprox, pprox, aprox, priority, cut, depth, rtime, htime
1482 ) VALUES (
1483     'Traditional with Holds-always-go-home',
1484     1, 2, 3, 4, 5, 6, 7, 8
1485 );
1486
1487 INSERT INTO config.best_hold_order (
1488     name,
1489     htime, hprox, pprox, aprox, priority, cut, depth, rtime
1490 ) VALUES (
1491     'Traditional with Holds-go-home',
1492     1, 2, 3, 4, 5, 6, 7, 8
1493 );
1494
1495 INSERT INTO config.best_hold_order (
1496     name,
1497     priority, cut, rtime, depth, pprox, hprox, aprox, htime
1498 ) VALUES (
1499     'FIFO',
1500     1, 2, 3, 4, 5, 6, 7, 8
1501 );
1502
1503 INSERT INTO config.best_hold_order (
1504     name,
1505     hprox, priority, cut, rtime, depth, pprox, aprox, htime
1506 ) VALUES (
1507     'FIFO with Holds-always-go-home',
1508     1, 2, 3, 4, 5, 6, 7, 8
1509 );
1510
1511 INSERT INTO config.best_hold_order (
1512     name,
1513     htime, priority, cut, rtime, depth, pprox, aprox, hprox
1514 ) VALUES (
1515     'FIFO with Holds-go-home',
1516     1, 2, 3, 4, 5, 6, 7, 8
1517 );
1518
1519 INSERT INTO permission.perm_list (
1520     id, code, description
1521 ) VALUES (
1522     546,
1523     'ADMIN_HOLD_CAPTURE_SORT',
1524     oils_i18n_gettext(
1525         546,
1526         'Allows a user to make changes to best-hold selection sort order',
1527         'ppl',
1528         'description'
1529     )
1530 );
1531
1532 INSERT INTO config.org_unit_setting_type (
1533     name, label, description, datatype, fm_class, update_perm, grp
1534 ) VALUES (
1535     'circ.hold_capture_order',
1536     oils_i18n_gettext(
1537         'circ.hold_capture_order',
1538         'Best-hold selection sort order',
1539         'coust',
1540         'label'
1541     ),
1542     oils_i18n_gettext(
1543         'circ.hold_capture_order',
1544         'Defines the sort order of holds when selecting a hold to fill using a given copy at capture time',
1545         'coust',
1546         'description'
1547     ),
1548     'link',
1549     'cbho',
1550     546,
1551     'holds'
1552 );
1553
1554 INSERT INTO config.org_unit_setting_type (
1555     name, label, description, datatype, update_perm, grp
1556 ) VALUES (
1557     'circ.hold_go_home_interval',
1558     oils_i18n_gettext(
1559         'circ.hold_go_home_interval',
1560         'Max foreign-circulation time',
1561         'coust',
1562         'label'
1563     ),
1564     oils_i18n_gettext(
1565         'circ.hold_go_home_interval',
1566         'Time a copy can spend circulating away from its circ lib before returning there to fill a hold (if one exists there)',
1567         'coust',
1568         'description'
1569     ),
1570     'interval',
1571     546,
1572     'holds'
1573 );
1574
1575 INSERT INTO actor.org_unit_setting (
1576     org_unit, name, value
1577 ) VALUES (
1578     (SELECT id FROM actor.org_unit WHERE parent_ou IS NULL),
1579     'circ.hold_go_home_interval',
1580     '"6 months"'
1581 );
1582
1583 UPDATE actor.org_unit_setting SET
1584     name = 'circ.hold_capture_order',
1585     value = (SELECT id FROM config.best_hold_order WHERE name = 'FIFO')
1586 WHERE
1587     name = 'circ.holds_fifo' AND value ILIKE '%true%';
1588
1589
1590 SELECT evergreen.upgrade_deps_block_check('0762', :eg_version);
1591
1592 INSERT INTO config.internal_flag (name) VALUES ('ingest.skip_browse_indexing');
1593 INSERT INTO config.internal_flag (name) VALUES ('ingest.skip_search_indexing');
1594 INSERT INTO config.internal_flag (name) VALUES ('ingest.skip_facet_indexing');
1595
1596 CREATE OR REPLACE FUNCTION metabib.reingest_metabib_field_entries( bib_id BIGINT, skip_facet BOOL DEFAULT FALSE, skip_browse BOOL DEFAULT FALSE, skip_search BOOL DEFAULT FALSE ) RETURNS VOID AS $func$
1597 DECLARE
1598     fclass          RECORD;
1599     ind_data        metabib.field_entry_template%ROWTYPE;
1600     mbe_row         metabib.browse_entry%ROWTYPE;
1601     mbe_id          BIGINT;
1602     b_skip_facet    BOOL;
1603     b_skip_browse   BOOL;
1604     b_skip_search   BOOL;
1605 BEGIN
1606
1607     SELECT COALESCE(NULLIF(skip_facet, FALSE), EXISTS (SELECT enabled FROM config.internal_flag WHERE name =  'ingest.skip_facet_indexing' AND enabled)) INTO b_skip_facet;
1608     SELECT COALESCE(NULLIF(skip_browse, FALSE), EXISTS (SELECT enabled FROM config.internal_flag WHERE name =  'ingest.skip_browse_indexing' AND enabled)) INTO b_skip_browse;
1609     SELECT COALESCE(NULLIF(skip_search, FALSE), EXISTS (SELECT enabled FROM config.internal_flag WHERE name =  'ingest.skip_search_indexing' AND enabled)) INTO b_skip_search;
1610
1611     PERFORM * FROM config.internal_flag WHERE name = 'ingest.assume_inserts_only' AND enabled;
1612     IF NOT FOUND THEN
1613         IF NOT b_skip_search THEN
1614             FOR fclass IN SELECT * FROM config.metabib_class LOOP
1615                 -- RAISE NOTICE 'Emptying out %', fclass.name;
1616                 EXECUTE $$DELETE FROM metabib.$$ || fclass.name || $$_field_entry WHERE source = $$ || bib_id;
1617             END LOOP;
1618         END IF;
1619         IF NOT b_skip_facet THEN
1620             DELETE FROM metabib.facet_entry WHERE source = bib_id;
1621         END IF;
1622         IF NOT b_skip_browse THEN
1623             DELETE FROM metabib.browse_entry_def_map WHERE source = bib_id;
1624         END IF;
1625     END IF;
1626
1627     FOR ind_data IN SELECT * FROM biblio.extract_metabib_field_entry( bib_id ) LOOP
1628         IF ind_data.field < 0 THEN
1629             ind_data.field = -1 * ind_data.field;
1630         END IF;
1631
1632         IF ind_data.facet_field AND NOT b_skip_facet THEN
1633             INSERT INTO metabib.facet_entry (field, source, value)
1634                 VALUES (ind_data.field, ind_data.source, ind_data.value);
1635         END IF;
1636
1637         IF ind_data.browse_field AND NOT b_skip_browse THEN
1638             -- A caveat about this SELECT: this should take care of replacing
1639             -- old mbe rows when data changes, but not if normalization (by
1640             -- which I mean specifically the output of
1641             -- evergreen.oils_tsearch2()) changes.  It may or may not be
1642             -- expensive to add a comparison of index_vector to index_vector
1643             -- to the WHERE clause below.
1644             SELECT INTO mbe_row * FROM metabib.browse_entry WHERE value = ind_data.value;
1645             IF FOUND THEN
1646                 mbe_id := mbe_row.id;
1647             ELSE
1648                 INSERT INTO metabib.browse_entry (value) VALUES
1649                     (metabib.browse_normalize(ind_data.value, ind_data.field));
1650                 mbe_id := CURRVAL('metabib.browse_entry_id_seq'::REGCLASS);
1651             END IF;
1652
1653             INSERT INTO metabib.browse_entry_def_map (entry, def, source)
1654                 VALUES (mbe_id, ind_data.field, ind_data.source);
1655         END IF;
1656
1657         IF ind_data.search_field AND NOT b_skip_search THEN
1658             EXECUTE $$
1659                 INSERT INTO metabib.$$ || ind_data.field_class || $$_field_entry (field, source, value)
1660                     VALUES ($$ ||
1661                         quote_literal(ind_data.field) || $$, $$ ||
1662                         quote_literal(ind_data.source) || $$, $$ ||
1663                         quote_literal(ind_data.value) ||
1664                     $$);$$;
1665         END IF;
1666
1667     END LOOP;
1668
1669     IF NOT b_skip_search THEN
1670         PERFORM metabib.update_combined_index_vectors(bib_id);
1671     END IF;
1672
1673     RETURN;
1674 END;
1675 $func$ LANGUAGE PLPGSQL;
1676
1677 COMMIT;