1 --Upgrade Script for 2.1 to 2.2.0
3 -- Don't require use of -vegversion=something
4 \set eg_version '''2.2.0'''
6 -- DROP objects that might have existed from a prior run of 0526
8 DROP TABLE IF EXISTS config.db_patch_dependencies;
9 ALTER TABLE config.upgrade_log DROP COLUMN IF EXISTS applied_to;
10 DROP FUNCTION IF EXISTS evergreen.upgrade_list_applied_deprecates(TEXT);
11 DROP FUNCTION IF EXISTS evergreen.upgrade_list_applied_supersedes(TEXT);
14 INSERT INTO config.upgrade_log (version) VALUES ('2.2.0');
16 INSERT INTO config.upgrade_log (version) VALUES ('0526'); --miker
18 CREATE TABLE config.db_patch_dependencies (
19 db_patch TEXT PRIMARY KEY,
24 CREATE OR REPLACE FUNCTION evergreen.array_overlap_check (/* field */) RETURNS TRIGGER AS $$
30 EXECUTE 'SELECT COUNT(*) FROM '|| TG_TABLE_SCHEMA ||'.'|| TG_TABLE_NAME ||' WHERE '|| fld ||' && ($1).'|| fld INTO cnt USING NEW;
32 RAISE EXCEPTION 'Cannot insert duplicate array into field % of table %', fld, TG_TABLE_SCHEMA ||'.'|| TG_TABLE_NAME;
38 CREATE TRIGGER no_overlapping_sups
39 BEFORE INSERT OR UPDATE ON config.db_patch_dependencies
40 FOR EACH ROW EXECUTE PROCEDURE evergreen.array_overlap_check ('supersedes');
42 CREATE TRIGGER no_overlapping_deps
43 BEFORE INSERT OR UPDATE ON config.db_patch_dependencies
44 FOR EACH ROW EXECUTE PROCEDURE evergreen.array_overlap_check ('deprecates');
46 ALTER TABLE config.upgrade_log
47 ADD COLUMN applied_to TEXT;
49 -- Provide a named type for patching functions
50 CREATE TYPE evergreen.patch AS (patch TEXT);
52 -- List applied db patches that are deprecated by (and block the application of) my_db_patch
53 CREATE OR REPLACE FUNCTION evergreen.upgrade_list_applied_deprecates ( my_db_patch TEXT ) RETURNS SETOF evergreen.patch AS $$
54 SELECT DISTINCT l.version
55 FROM config.upgrade_log l
56 JOIN config.db_patch_dependencies d ON (l.version::TEXT[] && d.deprecates)
60 -- List applied db patches that are superseded by (and block the application of) my_db_patch
61 CREATE OR REPLACE FUNCTION evergreen.upgrade_list_applied_supersedes ( my_db_patch TEXT ) RETURNS SETOF evergreen.patch AS $$
62 SELECT DISTINCT l.version
63 FROM config.upgrade_log l
64 JOIN config.db_patch_dependencies d ON (l.version::TEXT[] && d.supersedes)
68 -- List applied db patches that deprecates (and block the application of) my_db_patch
69 CREATE OR REPLACE FUNCTION evergreen.upgrade_list_applied_deprecated ( my_db_patch TEXT ) RETURNS TEXT AS $$
71 FROM config.db_patch_dependencies
72 WHERE ARRAY[$1]::TEXT[] && deprecates
75 -- List applied db patches that supersedes (and block the application of) my_db_patch
76 CREATE OR REPLACE FUNCTION evergreen.upgrade_list_applied_superseded ( my_db_patch TEXT ) RETURNS TEXT AS $$
78 FROM config.db_patch_dependencies
79 WHERE ARRAY[$1]::TEXT[] && supersedes
82 -- Make sure that no deprecated or superseded db patches are currently applied
83 CREATE OR REPLACE FUNCTION evergreen.upgrade_verify_no_dep_conflicts ( my_db_patch TEXT ) RETURNS BOOL AS $$
85 FROM (SELECT * FROM evergreen.upgrade_list_applied_deprecates( $1 )
87 SELECT * FROM evergreen.upgrade_list_applied_supersedes( $1 )
89 SELECT * FROM evergreen.upgrade_list_applied_deprecated( $1 )
91 SELECT * FROM evergreen.upgrade_list_applied_superseded( $1 ))x
94 -- Raise an exception if there are, in fact, dep/sup confilct
95 CREATE OR REPLACE FUNCTION evergreen.upgrade_deps_block_check ( my_db_patch TEXT, my_applied_to TEXT ) RETURNS BOOL AS $$
100 IF NOT evergreen.upgrade_verify_no_dep_conflicts( my_db_patch ) THEN
101 SELECT STRING_AGG(patch, ', ') INTO deprecates FROM evergreen.upgrade_list_applied_deprecates(my_db_patch);
102 SELECT STRING_AGG(patch, ', ') INTO supersedes FROM evergreen.upgrade_list_applied_supersedes(my_db_patch);
104 Upgrade script % can not be applied:
105 applied deprecated scripts %
106 applied superseded scripts %
110 ARRAY_AGG(evergreen.upgrade_list_applied_deprecates(my_db_patch)),
111 ARRAY_AGG(evergreen.upgrade_list_applied_supersedes(my_db_patch)),
112 evergreen.upgrade_list_applied_deprecated(my_db_patch),
113 evergreen.upgrade_list_applied_superseded(my_db_patch);
116 INSERT INTO config.upgrade_log (version, applied_to) VALUES (my_db_patch, my_applied_to);
121 -- Evergreen DB patch 0536.schema.lazy_circ-barcode_lookup.sql
123 -- FIXME: insert description of change, if needed
126 -- check whether patch can be applied
127 INSERT INTO config.upgrade_log (version) VALUES ('0536');
129 INSERT INTO config.org_unit_setting_type ( name, label, description, datatype) VALUES ( 'circ.staff_client.actor_on_checkout', 'Load patron from Checkout', 'When scanning barcodes into Checkout auto-detect if a new patron barcode is scanned and auto-load the new patron.', 'bool');
131 CREATE TABLE config.barcode_completion (
132 id SERIAL PRIMARY KEY,
133 active BOOL NOT NULL DEFAULT true,
134 org_unit INT NOT NULL REFERENCES actor.org_unit (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
137 length INT NOT NULL DEFAULT 0,
139 padding_end BOOL NOT NULL DEFAULT false,
140 asset BOOL NOT NULL DEFAULT true,
141 actor BOOL NOT NULL DEFAULT true
144 CREATE TYPE evergreen.barcode_set AS (type TEXT, id BIGINT, barcode TEXT);
146 CREATE OR REPLACE FUNCTION evergreen.get_barcodes(select_ou INT, type TEXT, in_barcode TEXT) RETURNS SETOF evergreen.barcode_set AS $$
151 asset_barcodes TEXT[];
152 actor_barcodes TEXT[];
153 do_asset BOOL = false;
154 do_serial BOOL = false;
155 do_booking BOOL = false;
156 do_actor BOOL = false;
157 completion_set config.barcode_completion%ROWTYPE;
160 IF position('asset' in type) > 0 THEN
163 IF position('serial' in type) > 0 THEN
166 IF position('booking' in type) > 0 THEN
169 IF do_asset OR do_serial OR do_booking THEN
170 asset_barcodes = asset_barcodes || in_barcode;
172 IF position('actor' in type) > 0 THEN
174 actor_barcodes = actor_barcodes || in_barcode;
177 barcode_len := length(in_barcode);
179 FOR completion_set IN
180 SELECT * FROM config.barcode_completion
182 AND org_unit IN (SELECT aou.id FROM actor.org_unit_ancestors(select_ou) aou)
184 IF completion_set.prefix IS NULL THEN
185 completion_set.prefix := '';
187 IF completion_set.suffix IS NULL THEN
188 completion_set.suffix := '';
190 IF completion_set.length = 0 OR completion_set.padding IS NULL OR length(completion_set.padding) = 0 THEN
191 cur_barcode = completion_set.prefix || in_barcode || completion_set.suffix;
193 completion_len = completion_set.length - length(completion_set.prefix) - length(completion_set.suffix);
194 IF completion_len >= barcode_len THEN
195 IF completion_set.padding_end THEN
196 cur_barcode = rpad(in_barcode, completion_len, completion_set.padding);
198 cur_barcode = lpad(in_barcode, completion_len, completion_set.padding);
200 cur_barcode = completion_set.prefix || cur_barcode || completion_set.suffix;
203 IF completion_set.actor THEN
204 actor_barcodes = actor_barcodes || cur_barcode;
206 IF completion_set.asset THEN
207 asset_barcodes = asset_barcodes || cur_barcode;
211 IF do_asset AND do_serial THEN
212 RETURN QUERY SELECT 'asset'::TEXT, id, barcode FROM ONLY asset.copy WHERE barcode = ANY(asset_barcodes) AND deleted = false;
213 RETURN QUERY SELECT 'serial'::TEXT, id, barcode FROM serial.unit WHERE barcode = ANY(asset_barcodes) AND deleted = false;
215 RETURN QUERY SELECT 'asset'::TEXT, id, barcode FROM asset.copy WHERE barcode = ANY(asset_barcodes) AND deleted = false;
217 RETURN QUERY SELECT 'serial'::TEXT, id, barcode FROM serial.unit WHERE barcode = ANY(asset_barcodes) AND deleted = false;
220 RETURN QUERY SELECT 'booking'::TEXT, id::BIGINT, barcode FROM booking.resource WHERE barcode = ANY(asset_barcodes);
223 RETURN QUERY SELECT 'actor'::TEXT, c.usr::BIGINT, c.barcode FROM actor.card c JOIN actor.usr u ON c.usr = u.id WHERE c.barcode = ANY(actor_barcodes) AND c.active AND NOT u.deleted ORDER BY usr;
229 COMMENT ON FUNCTION evergreen.get_barcodes(INT, TEXT, TEXT) IS $$
230 Given user input, find an appropriate barcode in the proper class.
232 Will add prefix/suffix information to do so, and return all results.
237 INSERT INTO config.upgrade_log (version) VALUES ('0537'); --miker
239 DROP FUNCTION evergreen.upgrade_deps_block_check(text,text);
240 DROP FUNCTION evergreen.upgrade_verify_no_dep_conflicts(text);
241 DROP FUNCTION evergreen.upgrade_list_applied_deprecated(text);
242 DROP FUNCTION evergreen.upgrade_list_applied_superseded(text);
244 -- List applied db patches that deprecates (and block the application of) my_db_patch
245 CREATE FUNCTION evergreen.upgrade_list_applied_deprecated ( my_db_patch TEXT ) RETURNS SETOF TEXT AS $$
247 FROM config.db_patch_dependencies
248 WHERE ARRAY[$1]::TEXT[] && deprecates
251 -- List applied db patches that supersedes (and block the application of) my_db_patch
252 CREATE FUNCTION evergreen.upgrade_list_applied_superseded ( my_db_patch TEXT ) RETURNS SETOF TEXT AS $$
254 FROM config.db_patch_dependencies
255 WHERE ARRAY[$1]::TEXT[] && supersedes
258 -- Make sure that no deprecated or superseded db patches are currently applied
259 CREATE FUNCTION evergreen.upgrade_verify_no_dep_conflicts ( my_db_patch TEXT ) RETURNS BOOL AS $$
261 FROM (SELECT * FROM evergreen.upgrade_list_applied_deprecates( $1 )
263 SELECT * FROM evergreen.upgrade_list_applied_supersedes( $1 )
265 SELECT * FROM evergreen.upgrade_list_applied_deprecated( $1 )
267 SELECT * FROM evergreen.upgrade_list_applied_superseded( $1 ))x
270 -- Raise an exception if there are, in fact, dep/sup confilct
271 CREATE FUNCTION evergreen.upgrade_deps_block_check ( my_db_patch TEXT, my_applied_to TEXT ) RETURNS BOOL AS $$
273 IF NOT evergreen.upgrade_verify_no_dep_conflicts( my_db_patch ) THEN
275 Upgrade script % can not be applied:
276 applied deprecated scripts %
277 applied superseded scripts %
281 ARRAY_ACCUM(evergreen.upgrade_list_applied_deprecates(my_db_patch)),
282 ARRAY_ACCUM(evergreen.upgrade_list_applied_supersedes(my_db_patch)),
283 evergreen.upgrade_list_applied_deprecated(my_db_patch),
284 evergreen.upgrade_list_applied_superseded(my_db_patch);
287 INSERT INTO config.upgrade_log (version, applied_to) VALUES (my_db_patch, my_applied_to);
293 INSERT INTO config.upgrade_log (version) VALUES ('0544');
295 INSERT INTO config.usr_setting_type
296 ( name, opac_visible, label, description, datatype) VALUES
297 ( 'circ.collections.exempt',
299 oils_i18n_gettext('circ.collections.exempt', 'Collections: Exempt', 'cust', 'description'),
300 oils_i18n_gettext('circ.collections.exempt', 'User is exempt from collections tracking/processing', 'cust', 'description'),
306 SELECT evergreen.upgrade_deps_block_check('0545', :eg_version);
308 INSERT INTO permission.perm_list VALUES
309 (507, 'ABORT_TRANSIT_ON_LOST', oils_i18n_gettext(507, 'Allows a user to abort a transit on a copy with status of LOST', 'ppl', 'description')),
310 (508, 'ABORT_TRANSIT_ON_MISSING', oils_i18n_gettext(508, 'Allows a user to abort a transit on a copy with status of MISSING', 'ppl', 'description'));
312 --- stock Circulation Administrator group
314 INSERT INTO permission.grp_perm_map ( grp, perm, depth, grantable )
320 FROM permission.perm_list
321 WHERE code in ('ABORT_TRANSIT_ON_LOST', 'ABORT_TRANSIT_ON_MISSING');
323 -- Evergreen DB patch 0546.schema.sip_statcats.sql
326 -- check whether patch can be applied
327 SELECT evergreen.upgrade_deps_block_check('0546', :eg_version);
329 CREATE TABLE actor.stat_cat_sip_fields (
330 field CHAR(2) PRIMARY KEY,
332 one_only BOOL NOT NULL DEFAULT FALSE
334 COMMENT ON TABLE actor.stat_cat_sip_fields IS $$
335 Actor Statistical Category SIP Fields
337 Contains the list of valid SIP Field identifiers for
338 Statistical Categories.
340 ALTER TABLE actor.stat_cat
341 ADD COLUMN sip_field CHAR(2) REFERENCES actor.stat_cat_sip_fields(field) ON UPDATE CASCADE ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED,
342 ADD COLUMN sip_format TEXT;
344 CREATE FUNCTION actor.stat_cat_check() RETURNS trigger AS $func$
346 sipfield actor.stat_cat_sip_fields%ROWTYPE;
349 IF NEW.sip_field IS NOT NULL THEN
350 SELECT INTO sipfield * FROM actor.stat_cat_sip_fields WHERE field = NEW.sip_field;
351 IF sipfield.one_only THEN
352 SELECT INTO use_count count(id) FROM actor.stat_cat WHERE sip_field = NEW.sip_field AND id != NEW.id;
353 IF use_count > 0 THEN
354 RAISE EXCEPTION 'Sip field cannot be used twice';
360 $func$ LANGUAGE PLPGSQL;
362 CREATE TRIGGER actor_stat_cat_sip_update_trigger
363 BEFORE INSERT OR UPDATE ON actor.stat_cat FOR EACH ROW
364 EXECUTE PROCEDURE actor.stat_cat_check();
366 CREATE TABLE asset.stat_cat_sip_fields (
367 field CHAR(2) PRIMARY KEY,
369 one_only BOOL NOT NULL DEFAULT FALSE
371 COMMENT ON TABLE asset.stat_cat_sip_fields IS $$
372 Asset Statistical Category SIP Fields
374 Contains the list of valid SIP Field identifiers for
375 Statistical Categories.
378 ALTER TABLE asset.stat_cat
379 ADD COLUMN sip_field CHAR(2) REFERENCES asset.stat_cat_sip_fields(field) ON UPDATE CASCADE ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED,
380 ADD COLUMN sip_format TEXT;
382 CREATE FUNCTION asset.stat_cat_check() RETURNS trigger AS $func$
384 sipfield asset.stat_cat_sip_fields%ROWTYPE;
387 IF NEW.sip_field IS NOT NULL THEN
388 SELECT INTO sipfield * FROM asset.stat_cat_sip_fields WHERE field = NEW.sip_field;
389 IF sipfield.one_only THEN
390 SELECT INTO use_count count(id) FROM asset.stat_cat WHERE sip_field = NEW.sip_field AND id != NEW.id;
391 IF use_count > 0 THEN
392 RAISE EXCEPTION 'Sip field cannot be used twice';
398 $func$ LANGUAGE PLPGSQL;
400 CREATE TRIGGER asset_stat_cat_sip_update_trigger
401 BEFORE INSERT OR UPDATE ON asset.stat_cat FOR EACH ROW
402 EXECUTE PROCEDURE asset.stat_cat_check();
406 SELECT evergreen.upgrade_deps_block_check('0548', :eg_version); -- dbwells
408 \qecho This redoes the original part 1 of 0547 which did not apply to rel_2_1,
409 \qecho and is being added for the sake of clarity
411 -- delete errant inserts from 0545 (group 4 is NOT the circulation admin group)
412 DELETE FROM permission.grp_perm_map WHERE grp = 4 AND perm IN (
413 SELECT id FROM permission.perm_list
414 WHERE code in ('ABORT_TRANSIT_ON_LOST', 'ABORT_TRANSIT_ON_MISSING')
417 INSERT INTO permission.grp_perm_map (grp, perm, depth, grantable)
419 pgt.id, perm.id, aout.depth, TRUE
421 permission.grp_tree pgt,
422 permission.perm_list perm,
423 actor.org_unit_type aout
425 pgt.name = 'Circulation Administrator' AND
426 aout.name = 'Consortium' AND
428 'ABORT_TRANSIT_ON_LOST',
429 'ABORT_TRANSIT_ON_MISSING'
432 FROM permission.grp_perm_map AS map
435 AND map.perm = perm.id
438 -- Evergreen DB patch XXXX.data.transit-checkin-interval.sql
440 -- New org unit setting "circ.transit.min_checkin_interval"
441 -- New TRANSIT_CHECKIN_INTERVAL_BLOCK.override permission
445 -- check whether patch can be applied
446 SELECT evergreen.upgrade_deps_block_check('0549', :eg_version);
448 INSERT INTO config.org_unit_setting_type ( name, label, description, datatype ) VALUES (
449 'circ.transit.min_checkin_interval',
451 'circ.transit.min_checkin_interval',
452 'Circ: Minimum Transit Checkin Interval',
457 'circ.transit.min_checkin_interval',
458 'In-Transit items checked in this close to the transit start time will be prevented from checking in',
465 INSERT INTO permission.perm_list ( id, code, description ) VALUES (
467 'TRANSIT_CHECKIN_INTERVAL_BLOCK.override',
470 'Allows a user to override the TRANSIT_CHECKIN_INTERVAL_BLOCK event',
476 -- add the perm to the default circ admin group
477 INSERT INTO permission.grp_perm_map (grp, perm, depth, grantable)
479 pgt.id, perm.id, aout.depth, TRUE
481 permission.grp_tree pgt,
482 permission.perm_list perm,
483 actor.org_unit_type aout
485 pgt.name = 'Circulation Administrator' AND
486 aout.name = 'System' AND
487 perm.code IN ( 'TRANSIT_CHECKIN_INTERVAL_BLOCK.override' );
490 -- check whether patch can be applied
491 SELECT evergreen.upgrade_deps_block_check('0550', :eg_version);
493 INSERT INTO config.org_unit_setting_type ( name, label, description, datatype ) VALUES (
494 'org.patron_opt_boundary',
496 'org.patron_opt_boundary',
497 'Circ: Patron Opt-In Boundary',
502 'org.patron_opt_boundary',
503 'This determines at which depth above which patrons must be opted in, and below which patrons will be assumed to be opted in.',
510 INSERT INTO config.org_unit_setting_type ( name, label, description, datatype ) VALUES (
511 'org.patron_opt_default',
513 'org.patron_opt_default',
514 'Circ: Patron Opt-In Default',
519 'org.patron_opt_default',
520 'This is the default depth at which a patron is opted in; it is calculated as an org unit relative to the current workstation.',
527 -- Evergreen DB patch 0562.schema.copy_active_date.sql
532 -- check whether patch can be applied
533 SELECT evergreen.upgrade_deps_block_check('0562', :eg_version);
535 ALTER TABLE asset.copy
536 ADD COLUMN active_date TIMESTAMP WITH TIME ZONE;
538 ALTER TABLE auditor.asset_copy_history
539 ADD COLUMN active_date TIMESTAMP WITH TIME ZONE;
541 ALTER TABLE auditor.serial_unit_history
542 ADD COLUMN active_date TIMESTAMP WITH TIME ZONE;
544 ALTER TABLE config.copy_status
545 ADD COLUMN copy_active BOOL NOT NULL DEFAULT FALSE;
547 ALTER TABLE config.circ_matrix_weights
548 ADD COLUMN item_age NUMERIC(6,2) NOT NULL DEFAULT 0.0;
550 ALTER TABLE config.hold_matrix_weights
551 ADD COLUMN item_age NUMERIC(6,2) NOT NULL DEFAULT 0.0;
553 -- The two defaults above were to stop erroring on NOT NULL
555 ALTER TABLE config.circ_matrix_weights
556 ALTER COLUMN item_age DROP DEFAULT;
558 ALTER TABLE config.hold_matrix_weights
559 ALTER COLUMN item_age DROP DEFAULT;
561 ALTER TABLE config.circ_matrix_matchpoint
562 ADD COLUMN item_age INTERVAL;
564 ALTER TABLE config.hold_matrix_matchpoint
565 ADD COLUMN item_age INTERVAL;
567 --Removed dupe asset.acp_status_changed
569 CREATE OR REPLACE FUNCTION asset.acp_created()
570 RETURNS TRIGGER AS $$
572 IF NEW.active_date IS NULL AND NEW.status IN (SELECT id FROM config.copy_status WHERE copy_active = true) THEN
573 NEW.active_date := now();
575 IF NEW.status_changed_time IS NULL THEN
576 NEW.status_changed_time := now();
582 CREATE TRIGGER acp_created_trig
583 BEFORE INSERT ON asset.copy
584 FOR EACH ROW EXECUTE PROCEDURE asset.acp_created();
586 CREATE TRIGGER sunit_created_trig
587 BEFORE INSERT ON serial.unit
588 FOR EACH ROW EXECUTE PROCEDURE asset.acp_created();
590 --Removed dupe action.hold_request_permit_test
592 CREATE OR REPLACE FUNCTION action.find_circ_matrix_matchpoint( context_ou INT, item_object asset.copy, user_object actor.usr, renewal BOOL ) RETURNS action.found_circ_matrix_matchpoint AS $func$
594 cn_object asset.call_number%ROWTYPE;
595 rec_descriptor metabib.rec_descriptor%ROWTYPE;
596 cur_matchpoint config.circ_matrix_matchpoint%ROWTYPE;
597 matchpoint config.circ_matrix_matchpoint%ROWTYPE;
598 weights config.circ_matrix_weights%ROWTYPE;
600 my_item_age INTERVAL;
601 denominator NUMERIC(6,2);
603 result action.found_circ_matrix_matchpoint;
606 result.success = false;
609 SELECT INTO cn_object * FROM asset.call_number WHERE id = item_object.call_number;
610 SELECT INTO rec_descriptor * FROM metabib.rec_descriptor WHERE record = cn_object.record;
612 -- Pre-generate this so we only calc it once
613 IF user_object.dob IS NOT NULL THEN
614 SELECT INTO user_age age(user_object.dob);
618 SELECT INTO my_item_age age(coalesce(item_object.active_date, now()));
620 -- Grab the closest set circ weight setting.
621 SELECT INTO weights cw.*
622 FROM config.weight_assoc wa
623 JOIN config.circ_matrix_weights cw ON (cw.id = wa.circ_weights)
624 JOIN actor.org_unit_ancestors_distance( context_ou ) d ON (wa.org_unit = d.id)
629 -- No weights? Bad admin! Defaults to handle that anyway.
630 IF weights.id IS NULL THEN
632 weights.org_unit := 10.0;
633 weights.circ_modifier := 5.0;
634 weights.marc_type := 4.0;
635 weights.marc_form := 3.0;
636 weights.marc_bib_level := 2.0;
637 weights.marc_vr_format := 2.0;
638 weights.copy_circ_lib := 8.0;
639 weights.copy_owning_lib := 8.0;
640 weights.user_home_ou := 8.0;
641 weights.ref_flag := 1.0;
642 weights.juvenile_flag := 6.0;
643 weights.is_renewal := 7.0;
644 weights.usr_age_lower_bound := 0.0;
645 weights.usr_age_upper_bound := 0.0;
646 weights.item_age := 0.0;
649 -- Determine the max (expected) depth (+1) of the org tree and max depth of the permisson tree
650 -- If you break your org tree with funky parenting this may be wrong
651 -- Note: This CTE is duplicated in the find_hold_matrix_matchpoint function, and it may be a good idea to split it off to a function
652 -- We use one denominator for all tree-based checks for when permission groups and org units have the same weighting
653 WITH all_distance(distance) AS (
654 SELECT depth AS distance FROM actor.org_unit_type
656 SELECT distance AS distance FROM permission.grp_ancestors_distance((SELECT id FROM permission.grp_tree WHERE parent IS NULL))
658 SELECT INTO denominator MAX(distance) + 1 FROM all_distance;
660 -- Loop over all the potential matchpoints
661 FOR cur_matchpoint IN
663 FROM config.circ_matrix_matchpoint m
664 /*LEFT*/ JOIN permission.grp_ancestors_distance( user_object.profile ) upgad ON m.grp = upgad.id
665 /*LEFT*/ JOIN actor.org_unit_ancestors_distance( context_ou ) ctoua ON m.org_unit = ctoua.id
666 LEFT JOIN actor.org_unit_ancestors_distance( cn_object.owning_lib ) cnoua ON m.copy_owning_lib = cnoua.id
667 LEFT JOIN actor.org_unit_ancestors_distance( item_object.circ_lib ) iooua ON m.copy_circ_lib = iooua.id
668 LEFT JOIN actor.org_unit_ancestors_distance( user_object.home_ou ) uhoua ON m.user_home_ou = uhoua.id
671 -- AND (m.grp IS NULL OR upgad.id IS NOT NULL) -- Optional Permission Group?
673 -- AND (m.org_unit IS NULL OR ctoua.id IS NOT NULL) -- Optional Org Unit?
674 AND (m.copy_owning_lib IS NULL OR cnoua.id IS NOT NULL)
675 AND (m.copy_circ_lib IS NULL OR iooua.id IS NOT NULL)
676 AND (m.user_home_ou IS NULL OR uhoua.id IS NOT NULL)
678 AND (m.is_renewal IS NULL OR m.is_renewal = renewal)
679 -- Static User Checks
680 AND (m.juvenile_flag IS NULL OR m.juvenile_flag = user_object.juvenile)
681 AND (m.usr_age_lower_bound IS NULL OR (user_age IS NOT NULL AND m.usr_age_lower_bound < user_age))
682 AND (m.usr_age_upper_bound IS NULL OR (user_age IS NOT NULL AND m.usr_age_upper_bound > user_age))
683 -- Static Item Checks
684 AND (m.circ_modifier IS NULL OR m.circ_modifier = item_object.circ_modifier)
685 AND (m.marc_type IS NULL OR m.marc_type = COALESCE(item_object.circ_as_type, rec_descriptor.item_type))
686 AND (m.marc_form IS NULL OR m.marc_form = rec_descriptor.item_form)
687 AND (m.marc_bib_level IS NULL OR m.marc_bib_level = rec_descriptor.bib_level)
688 AND (m.marc_vr_format IS NULL OR m.marc_vr_format = rec_descriptor.vr_format)
689 AND (m.ref_flag IS NULL OR m.ref_flag = item_object.ref)
690 AND (m.item_age IS NULL OR (my_item_age IS NOT NULL AND m.item_age > my_item_age))
693 CASE WHEN upgad.distance IS NOT NULL THEN 2^(2*weights.grp - (upgad.distance/denominator)) ELSE 0.0 END +
695 CASE WHEN ctoua.distance IS NOT NULL THEN 2^(2*weights.org_unit - (ctoua.distance/denominator)) ELSE 0.0 END +
696 CASE WHEN cnoua.distance IS NOT NULL THEN 2^(2*weights.copy_owning_lib - (cnoua.distance/denominator)) ELSE 0.0 END +
697 CASE WHEN iooua.distance IS NOT NULL THEN 2^(2*weights.copy_circ_lib - (iooua.distance/denominator)) ELSE 0.0 END +
698 CASE WHEN uhoua.distance IS NOT NULL THEN 2^(2*weights.user_home_ou - (uhoua.distance/denominator)) ELSE 0.0 END +
699 -- Circ Type -- Note: 4^x is equiv to 2^(2*x)
700 CASE WHEN m.is_renewal IS NOT NULL THEN 4^weights.is_renewal ELSE 0.0 END +
701 -- Static User Checks
702 CASE WHEN m.juvenile_flag IS NOT NULL THEN 4^weights.juvenile_flag ELSE 0.0 END +
703 CASE WHEN m.usr_age_lower_bound IS NOT NULL THEN 4^weights.usr_age_lower_bound ELSE 0.0 END +
704 CASE WHEN m.usr_age_upper_bound IS NOT NULL THEN 4^weights.usr_age_upper_bound ELSE 0.0 END +
705 -- Static Item Checks
706 CASE WHEN m.circ_modifier IS NOT NULL THEN 4^weights.circ_modifier ELSE 0.0 END +
707 CASE WHEN m.marc_type IS NOT NULL THEN 4^weights.marc_type ELSE 0.0 END +
708 CASE WHEN m.marc_form IS NOT NULL THEN 4^weights.marc_form ELSE 0.0 END +
709 CASE WHEN m.marc_vr_format IS NOT NULL THEN 4^weights.marc_vr_format ELSE 0.0 END +
710 CASE WHEN m.ref_flag IS NOT NULL THEN 4^weights.ref_flag ELSE 0.0 END +
711 -- Item age has a slight adjustment to weight based on value.
712 -- This should ensure that a shorter age limit comes first when all else is equal.
713 -- NOTE: This assumes that intervals will normally be in days.
714 CASE WHEN m.item_age IS NOT NULL THEN 4^weights.item_age - 1 + 86400/EXTRACT(EPOCH FROM m.item_age) ELSE 0.0 END DESC,
715 -- Final sort on id, so that if two rules have the same sorting in the previous sort they have a defined order
716 -- This prevents "we changed the table order by updating a rule, and we started getting different results"
719 -- Record the full matching row list
720 row_list := row_list || cur_matchpoint.id;
722 -- No matchpoint yet?
723 IF matchpoint.id IS NULL THEN
724 -- Take the entire matchpoint as a starting point
725 matchpoint := cur_matchpoint;
726 CONTINUE; -- No need to look at this row any more.
729 -- Incomplete matchpoint?
730 IF matchpoint.circulate IS NULL THEN
731 matchpoint.circulate := cur_matchpoint.circulate;
733 IF matchpoint.duration_rule IS NULL THEN
734 matchpoint.duration_rule := cur_matchpoint.duration_rule;
736 IF matchpoint.recurring_fine_rule IS NULL THEN
737 matchpoint.recurring_fine_rule := cur_matchpoint.recurring_fine_rule;
739 IF matchpoint.max_fine_rule IS NULL THEN
740 matchpoint.max_fine_rule := cur_matchpoint.max_fine_rule;
742 IF matchpoint.hard_due_date IS NULL THEN
743 matchpoint.hard_due_date := cur_matchpoint.hard_due_date;
745 IF matchpoint.total_copy_hold_ratio IS NULL THEN
746 matchpoint.total_copy_hold_ratio := cur_matchpoint.total_copy_hold_ratio;
748 IF matchpoint.available_copy_hold_ratio IS NULL THEN
749 matchpoint.available_copy_hold_ratio := cur_matchpoint.available_copy_hold_ratio;
751 IF matchpoint.renewals IS NULL THEN
752 matchpoint.renewals := cur_matchpoint.renewals;
754 IF matchpoint.grace_period IS NULL THEN
755 matchpoint.grace_period := cur_matchpoint.grace_period;
759 -- Check required fields
760 IF matchpoint.circulate IS NOT NULL AND
761 matchpoint.duration_rule IS NOT NULL AND
762 matchpoint.recurring_fine_rule IS NOT NULL AND
763 matchpoint.max_fine_rule IS NOT NULL THEN
764 -- All there? We have a completed match.
765 result.success := true;
768 -- Include the assembled matchpoint, even if it isn't complete
769 result.matchpoint := matchpoint;
771 -- Include (for debugging) the full list of matching rows
772 result.buildrows := row_list;
774 -- Hand the result back to caller
777 $func$ LANGUAGE plpgsql;
779 CREATE OR REPLACE FUNCTION action.find_hold_matrix_matchpoint(pickup_ou integer, request_ou integer, match_item bigint, match_user integer, match_requestor integer)
783 requestor_object actor.usr%ROWTYPE;
784 user_object actor.usr%ROWTYPE;
785 item_object asset.copy%ROWTYPE;
786 item_cn_object asset.call_number%ROWTYPE;
787 my_item_age INTERVAL;
788 rec_descriptor metabib.rec_descriptor%ROWTYPE;
789 matchpoint config.hold_matrix_matchpoint%ROWTYPE;
790 weights config.hold_matrix_weights%ROWTYPE;
791 denominator NUMERIC(6,2);
793 SELECT INTO user_object * FROM actor.usr WHERE id = match_user;
794 SELECT INTO requestor_object * FROM actor.usr WHERE id = match_requestor;
795 SELECT INTO item_object * FROM asset.copy WHERE id = match_item;
796 SELECT INTO item_cn_object * FROM asset.call_number WHERE id = item_object.call_number;
797 SELECT INTO rec_descriptor * FROM metabib.rec_descriptor WHERE record = item_cn_object.record;
799 SELECT INTO my_item_age age(coalesce(item_object.active_date, now()));
801 -- The item's owner should probably be the one determining if the item is holdable
802 -- How to decide that is debatable. Decided to default to the circ library (where the item lives)
803 -- This flag will allow for setting it to the owning library (where the call number "lives")
804 PERFORM * FROM config.internal_flag WHERE name = 'circ.holds.weight_owner_not_circ' AND enabled;
806 -- Grab the closest set circ weight setting.
808 -- Default to circ library
809 SELECT INTO weights hw.*
810 FROM config.weight_assoc wa
811 JOIN config.hold_matrix_weights hw ON (hw.id = wa.hold_weights)
812 JOIN actor.org_unit_ancestors_distance( item_object.circ_lib ) d ON (wa.org_unit = d.id)
817 -- Flag is set, use owning library
818 SELECT INTO weights hw.*
819 FROM config.weight_assoc wa
820 JOIN config.hold_matrix_weights hw ON (hw.id = wa.hold_weights)
821 JOIN actor.org_unit_ancestors_distance( item_cn_object.owning_lib ) d ON (wa.org_unit = d.id)
827 -- No weights? Bad admin! Defaults to handle that anyway.
828 IF weights.id IS NULL THEN
829 weights.user_home_ou := 5.0;
830 weights.request_ou := 5.0;
831 weights.pickup_ou := 5.0;
832 weights.item_owning_ou := 5.0;
833 weights.item_circ_ou := 5.0;
834 weights.usr_grp := 7.0;
835 weights.requestor_grp := 8.0;
836 weights.circ_modifier := 4.0;
837 weights.marc_type := 3.0;
838 weights.marc_form := 2.0;
839 weights.marc_bib_level := 1.0;
840 weights.marc_vr_format := 1.0;
841 weights.juvenile_flag := 4.0;
842 weights.ref_flag := 0.0;
843 weights.item_age := 0.0;
846 -- Determine the max (expected) depth (+1) of the org tree and max depth of the permisson tree
847 -- If you break your org tree with funky parenting this may be wrong
848 -- Note: This CTE is duplicated in the find_circ_matrix_matchpoint function, and it may be a good idea to split it off to a function
849 -- We use one denominator for all tree-based checks for when permission groups and org units have the same weighting
850 WITH all_distance(distance) AS (
851 SELECT depth AS distance FROM actor.org_unit_type
853 SELECT distance AS distance FROM permission.grp_ancestors_distance((SELECT id FROM permission.grp_tree WHERE parent IS NULL))
855 SELECT INTO denominator MAX(distance) + 1 FROM all_distance;
857 -- To ATTEMPT to make this work like it used to, make it reverse the user/requestor profile ids.
858 -- This may be better implemented as part of the upgrade script?
859 -- Set usr_grp = requestor_grp, requestor_grp = 1 or something when this flag is already set
860 -- Then remove this flag, of course.
861 PERFORM * FROM config.internal_flag WHERE name = 'circ.holds.usr_not_requestor' AND enabled;
864 -- Note: This, to me, is REALLY hacky. I put it in anyway.
865 -- If you can't tell, this is a single call swap on two variables.
866 SELECT INTO user_object.profile, requestor_object.profile
867 requestor_object.profile, user_object.profile;
870 -- Select the winning matchpoint into the matchpoint variable for returning
871 SELECT INTO matchpoint m.*
872 FROM config.hold_matrix_matchpoint m
873 /*LEFT*/ JOIN permission.grp_ancestors_distance( requestor_object.profile ) rpgad ON m.requestor_grp = rpgad.id
874 LEFT JOIN permission.grp_ancestors_distance( user_object.profile ) upgad ON m.usr_grp = upgad.id
875 LEFT JOIN actor.org_unit_ancestors_distance( pickup_ou ) puoua ON m.pickup_ou = puoua.id
876 LEFT JOIN actor.org_unit_ancestors_distance( request_ou ) rqoua ON m.request_ou = rqoua.id
877 LEFT JOIN actor.org_unit_ancestors_distance( item_cn_object.owning_lib ) cnoua ON m.item_owning_ou = cnoua.id
878 LEFT JOIN actor.org_unit_ancestors_distance( item_object.circ_lib ) iooua ON m.item_circ_ou = iooua.id
879 LEFT JOIN actor.org_unit_ancestors_distance( user_object.home_ou ) uhoua ON m.user_home_ou = uhoua.id
882 -- AND (m.requestor_grp IS NULL OR upgad.id IS NOT NULL) -- Optional Requestor Group?
883 AND (m.usr_grp IS NULL OR upgad.id IS NOT NULL)
885 AND (m.pickup_ou IS NULL OR (puoua.id IS NOT NULL AND (puoua.distance = 0 OR NOT m.strict_ou_match)))
886 AND (m.request_ou IS NULL OR (rqoua.id IS NOT NULL AND (rqoua.distance = 0 OR NOT m.strict_ou_match)))
887 AND (m.item_owning_ou IS NULL OR (cnoua.id IS NOT NULL AND (cnoua.distance = 0 OR NOT m.strict_ou_match)))
888 AND (m.item_circ_ou IS NULL OR (iooua.id IS NOT NULL AND (iooua.distance = 0 OR NOT m.strict_ou_match)))
889 AND (m.user_home_ou IS NULL OR (uhoua.id IS NOT NULL AND (uhoua.distance = 0 OR NOT m.strict_ou_match)))
890 -- Static User Checks
891 AND (m.juvenile_flag IS NULL OR m.juvenile_flag = user_object.juvenile)
892 -- Static Item Checks
893 AND (m.circ_modifier IS NULL OR m.circ_modifier = item_object.circ_modifier)
894 AND (m.marc_type IS NULL OR m.marc_type = COALESCE(item_object.circ_as_type, rec_descriptor.item_type))
895 AND (m.marc_form IS NULL OR m.marc_form = rec_descriptor.item_form)
896 AND (m.marc_bib_level IS NULL OR m.marc_bib_level = rec_descriptor.bib_level)
897 AND (m.marc_vr_format IS NULL OR m.marc_vr_format = rec_descriptor.vr_format)
898 AND (m.ref_flag IS NULL OR m.ref_flag = item_object.ref)
899 AND (m.item_age IS NULL OR (my_item_age IS NOT NULL AND m.item_age > my_item_age))
902 CASE WHEN rpgad.distance IS NOT NULL THEN 2^(2*weights.requestor_grp - (rpgad.distance/denominator)) ELSE 0.0 END +
903 CASE WHEN upgad.distance IS NOT NULL THEN 2^(2*weights.usr_grp - (upgad.distance/denominator)) ELSE 0.0 END +
905 CASE WHEN puoua.distance IS NOT NULL THEN 2^(2*weights.pickup_ou - (puoua.distance/denominator)) ELSE 0.0 END +
906 CASE WHEN rqoua.distance IS NOT NULL THEN 2^(2*weights.request_ou - (rqoua.distance/denominator)) ELSE 0.0 END +
907 CASE WHEN cnoua.distance IS NOT NULL THEN 2^(2*weights.item_owning_ou - (cnoua.distance/denominator)) ELSE 0.0 END +
908 CASE WHEN iooua.distance IS NOT NULL THEN 2^(2*weights.item_circ_ou - (iooua.distance/denominator)) ELSE 0.0 END +
909 CASE WHEN uhoua.distance IS NOT NULL THEN 2^(2*weights.user_home_ou - (uhoua.distance/denominator)) ELSE 0.0 END +
910 -- Static User Checks -- Note: 4^x is equiv to 2^(2*x)
911 CASE WHEN m.juvenile_flag IS NOT NULL THEN 4^weights.juvenile_flag ELSE 0.0 END +
912 -- Static Item Checks
913 CASE WHEN m.circ_modifier IS NOT NULL THEN 4^weights.circ_modifier ELSE 0.0 END +
914 CASE WHEN m.marc_type IS NOT NULL THEN 4^weights.marc_type ELSE 0.0 END +
915 CASE WHEN m.marc_form IS NOT NULL THEN 4^weights.marc_form ELSE 0.0 END +
916 CASE WHEN m.marc_vr_format IS NOT NULL THEN 4^weights.marc_vr_format ELSE 0.0 END +
917 CASE WHEN m.ref_flag IS NOT NULL THEN 4^weights.ref_flag ELSE 0.0 END +
918 -- Item age has a slight adjustment to weight based on value.
919 -- This should ensure that a shorter age limit comes first when all else is equal.
920 -- NOTE: This assumes that intervals will normally be in days.
921 CASE WHEN m.item_age IS NOT NULL THEN 4^weights.item_age - 86400/EXTRACT(EPOCH FROM m.item_age) ELSE 0.0 END DESC,
922 -- Final sort on id, so that if two rules have the same sorting in the previous sort they have a defined order
923 -- This prevents "we changed the table order by updating a rule, and we started getting different results"
926 -- Return just the ID for now
927 RETURN matchpoint.id;
929 $func$ LANGUAGE 'plpgsql';
931 DROP INDEX IF EXISTS config.ccmm_once_per_paramset;
933 DROP INDEX IF EXISTS config.chmm_once_per_paramset;
935 CREATE UNIQUE INDEX ccmm_once_per_paramset ON config.circ_matrix_matchpoint (org_unit, grp, COALESCE(circ_modifier, ''), COALESCE(marc_type, ''), COALESCE(marc_form, ''), COALESCE(marc_bib_level,''), COALESCE(marc_vr_format, ''), COALESCE(copy_circ_lib::TEXT, ''), COALESCE(copy_owning_lib::TEXT, ''), COALESCE(user_home_ou::TEXT, ''), COALESCE(ref_flag::TEXT, ''), COALESCE(juvenile_flag::TEXT, ''), COALESCE(is_renewal::TEXT, ''), COALESCE(usr_age_lower_bound::TEXT, ''), COALESCE(usr_age_upper_bound::TEXT, ''), COALESCE(item_age::TEXT, '')) WHERE active;
937 CREATE UNIQUE INDEX chmm_once_per_paramset ON config.hold_matrix_matchpoint (COALESCE(user_home_ou::TEXT, ''), COALESCE(request_ou::TEXT, ''), COALESCE(pickup_ou::TEXT, ''), COALESCE(item_owning_ou::TEXT, ''), COALESCE(item_circ_ou::TEXT, ''), COALESCE(usr_grp::TEXT, ''), COALESCE(requestor_grp::TEXT, ''), COALESCE(circ_modifier, ''), COALESCE(marc_type, ''), COALESCE(marc_form, ''), COALESCE(marc_bib_level, ''), COALESCE(marc_vr_format, ''), COALESCE(juvenile_flag::TEXT, ''), COALESCE(ref_flag::TEXT, ''), COALESCE(item_age::TEXT, '')) WHERE active;
939 UPDATE config.copy_status SET copy_active = true WHERE id IN (0, 1, 7, 8, 10, 12, 15);
941 INSERT into config.org_unit_setting_type
942 ( name, label, description, datatype ) VALUES
943 ( 'circ.holds.age_protect.active_date', 'Holds: Use Active Date for Age Protection', 'When calculating age protection rules use the active date instead of the creation date.', 'bool');
945 -- Assume create date when item is in status we would update active date for anyway
946 UPDATE asset.copy SET active_date = create_date WHERE status IN (SELECT id FROM config.copy_status WHERE copy_active = true);
948 -- Assume create date for any item with circs
949 UPDATE asset.copy SET active_date = create_date WHERE id IN (SELECT id FROM extend_reporter.full_circ_count WHERE circ_count > 0);
951 -- Assume create date for status change time while we are at it. Because being created WAS a change in status.
952 UPDATE asset.copy SET status_changed_time = create_date WHERE status_changed_time IS NULL;
954 -- Evergreen DB patch 0564.data.delete_empty_volume.sql
956 -- New org setting cat.volume.delete_on_empty
959 -- check whether patch can be applied
960 SELECT evergreen.upgrade_deps_block_check('0564', :eg_version);
962 INSERT INTO config.org_unit_setting_type ( name, label, description, datatype )
964 'cat.volume.delete_on_empty',
965 oils_i18n_gettext('cat.volume.delete_on_empty', 'Cat: Delete volume with last copy', 'coust', 'label'),
966 oils_i18n_gettext('cat.volume.delete_on_empty', 'Automatically delete a volume when the last linked copy is deleted', 'coust', 'description'),
971 -- Evergreen DB patch 0565.schema.action-trigger.event_definition.hold-cancel-no-target-notification.sql
973 -- New action trigger event definition: Hold Cancelled (No Target) Email Notification
976 -- check whether patch can be applied
977 SELECT evergreen.upgrade_deps_block_check('0565', :eg_version);
979 INSERT INTO action_trigger.event_definition (id, active, owner, name, hook, validator, reactor, delay, delay_field, group_field, template)
980 VALUES (38, FALSE, 1,
981 'Hold Cancelled (No Target) Email Notification',
982 'hold_request.cancel.expire_no_target',
983 'HoldIsCancelled', 'SendEmail', '30 minutes', 'cancel_time', 'usr',
986 [%- user = target.0.usr -%]
987 To: [%- params.recipient_email || user.email %]
988 From: [%- params.sender_email || default_sender %]
989 Subject: Hold Request Cancelled
991 Dear [% user.family_name %], [% user.first_given_name %]
992 The following holds were cancelled because no items were found to fullfil the hold.
994 [% FOR hold IN target %]
995 Title: [% hold.bib_rec.bib_record.simple_record.title %]
996 Author: [% hold.bib_rec.bib_record.simple_record.author %]
997 Library: [% hold.pickup_lib.name %]
998 Request Date: [% date.format(helpers.format_date(hold.rrequest_time), '%Y-%m-%d') %]
1003 INSERT INTO action_trigger.environment (event_def, path) VALUES
1006 (38, 'bib_rec.bib_record.simple_record');
1008 -- Evergreen DB patch XXXX.data.ou_setting_generate_overdue_on_lost.sql.sql
1010 -- check whether patch can be applied
1011 SELECT evergreen.upgrade_deps_block_check('0567', :eg_version);
1013 INSERT INTO config.org_unit_setting_type ( name, label, description, datatype ) VALUES (
1014 'circ.lost.generate_overdue_on_checkin',
1016 'circ.lost.generate_overdue_on_checkin',
1017 'Circ: Lost Checkin Generates New Overdues',
1022 'circ.lost.generate_overdue_on_checkin',
1023 'Enabling this setting causes retroactive creation of not-yet-existing overdue fines on lost item checkin, up to the point of checkin time (or max fines is reached). This is different than "restore overdue on lost", because it only creates new overdue fines. Use both settings together to get the full complement of overdue fines for a lost item',
1030 -- Evergreen DB patch 0572.vandelay-record-matching-and-quality.sql
1034 -- check whether patch can be applied
1035 SELECT evergreen.upgrade_deps_block_check('0572', :eg_version);
1037 CREATE OR REPLACE FUNCTION evergreen.array_remove_item_by_value(inp ANYARRAY, el ANYELEMENT) RETURNS anyarray AS $$ SELECT ARRAY_ACCUM(x.e) FROM UNNEST( $1 ) x(e) WHERE x.e <> $2; $$ LANGUAGE SQL;
1039 CREATE TABLE vandelay.match_set (
1040 id SERIAL PRIMARY KEY,
1042 owner INT NOT NULL REFERENCES actor.org_unit (id) ON DELETE CASCADE,
1043 mtype TEXT NOT NULL DEFAULT 'biblio', -- 'biblio','authority','mfhd'?, others?
1044 CONSTRAINT name_once_per_owner_mtype UNIQUE (name, owner, mtype)
1047 -- Table to define match points, either FF via SVF or tag+subfield
1048 CREATE TABLE vandelay.match_set_point (
1049 id SERIAL PRIMARY KEY,
1050 match_set INT REFERENCES vandelay.match_set (id) ON DELETE CASCADE,
1051 parent INT REFERENCES vandelay.match_set_point (id),
1052 bool_op TEXT CHECK (bool_op IS NULL OR (bool_op IN ('AND','OR','NOT'))),
1053 svf TEXT REFERENCES config.record_attr_definition (name),
1056 negate BOOL DEFAULT FALSE,
1057 quality INT NOT NULL DEFAULT 1, -- higher is better
1058 CONSTRAINT vmsp_need_a_subfield_with_a_tag CHECK ((tag IS NOT NULL AND subfield IS NOT NULL) OR tag IS NULL),
1059 CONSTRAINT vmsp_need_a_tag_or_a_ff_or_a_bo CHECK (
1060 (tag IS NOT NULL AND svf IS NULL AND bool_op IS NULL) OR
1061 (tag IS NULL AND svf IS NOT NULL AND bool_op IS NULL) OR
1062 (tag IS NULL AND svf IS NULL AND bool_op IS NOT NULL)
1066 CREATE TABLE vandelay.match_set_quality (
1067 id SERIAL PRIMARY KEY,
1068 match_set INT NOT NULL REFERENCES vandelay.match_set (id) ON DELETE CASCADE,
1069 svf TEXT REFERENCES config.record_attr_definition,
1072 value TEXT NOT NULL,
1073 quality INT NOT NULL DEFAULT 1, -- higher is better
1074 CONSTRAINT vmsq_need_a_subfield_with_a_tag CHECK ((tag IS NOT NULL AND subfield IS NOT NULL) OR tag IS NULL),
1075 CONSTRAINT vmsq_need_a_tag_or_a_ff CHECK ((tag IS NOT NULL AND svf IS NULL) OR (tag IS NULL AND svf IS NOT NULL))
1077 CREATE UNIQUE INDEX vmsq_def_once_per_set ON vandelay.match_set_quality (match_set, COALESCE(tag,''), COALESCE(subfield,''), COALESCE(svf,''), value);
1081 ALTER TABLE vandelay.queue ADD COLUMN match_set INT REFERENCES vandelay.match_set (id) ON UPDATE CASCADE ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED;
1082 ALTER TABLE vandelay.queued_record ADD COLUMN quality INT NOT NULL DEFAULT 0;
1083 ALTER TABLE vandelay.bib_attr_definition DROP COLUMN ident;
1085 CREATE TABLE vandelay.import_error (
1086 code TEXT PRIMARY KEY,
1087 description TEXT NOT NULL -- i18n
1090 ALTER TABLE vandelay.queued_bib_record
1091 ADD COLUMN import_error TEXT REFERENCES vandelay.import_error (code) ON DELETE SET NULL ON UPDATE CASCADE DEFERRABLE INITIALLY DEFERRED,
1092 ADD COLUMN error_detail TEXT;
1094 ALTER TABLE vandelay.bib_match
1095 DROP COLUMN field_type,
1096 DROP COLUMN matched_attr,
1097 ADD COLUMN quality INT NOT NULL DEFAULT 1,
1098 ADD COLUMN match_score INT NOT NULL DEFAULT 0;
1100 ALTER TABLE vandelay.import_item
1101 ADD COLUMN import_error TEXT REFERENCES vandelay.import_error (code) ON DELETE SET NULL ON UPDATE CASCADE DEFERRABLE INITIALLY DEFERRED,
1102 ADD COLUMN error_detail TEXT,
1103 ADD COLUMN imported_as BIGINT REFERENCES asset.copy (id) DEFERRABLE INITIALLY DEFERRED,
1104 ADD COLUMN import_time TIMESTAMP WITH TIME ZONE;
1106 ALTER TABLE vandelay.merge_profile ADD COLUMN lwm_ratio NUMERIC;
1108 CREATE OR REPLACE FUNCTION vandelay.marc21_record_type( marc TEXT ) RETURNS config.marc21_rec_type_map AS $func$
1115 retval config.marc21_rec_type_map%ROWTYPE;
1117 ldr := oils_xpath_string( '//*[local-name()="leader"]', marc );
1119 IF ldr IS NULL OR ldr = '' THEN
1120 SELECT * INTO retval FROM config.marc21_rec_type_map WHERE code = 'BKS';
1124 SELECT * INTO tval_rec FROM config.marc21_ff_pos_map WHERE fixed_field = 'Type' LIMIT 1; -- They're all the same
1125 SELECT * INTO bval_rec FROM config.marc21_ff_pos_map WHERE fixed_field = 'BLvl' LIMIT 1; -- They're all the same
1128 tval := SUBSTRING( ldr, tval_rec.start_pos + 1, tval_rec.length );
1129 bval := SUBSTRING( ldr, bval_rec.start_pos + 1, bval_rec.length );
1131 -- RAISE NOTICE 'type %, blvl %, ldr %', tval, bval, ldr;
1133 SELECT * INTO retval FROM config.marc21_rec_type_map WHERE type_val LIKE '%' || tval || '%' AND blvl_val LIKE '%' || bval || '%';
1136 IF retval.code IS NULL THEN
1137 SELECT * INTO retval FROM config.marc21_rec_type_map WHERE code = 'BKS';
1142 $func$ LANGUAGE PLPGSQL;
1144 CREATE OR REPLACE FUNCTION vandelay.marc21_extract_fixed_field( marc TEXT, ff TEXT ) RETURNS TEXT AS $func$
1151 rtype := (vandelay.marc21_record_type( marc )).code;
1152 FOR ff_pos IN SELECT * FROM config.marc21_ff_pos_map WHERE fixed_field = ff AND rec_type = rtype ORDER BY tag DESC LOOP
1153 IF ff_pos.tag = 'ldr' THEN
1154 val := oils_xpath_string('//*[local-name()="leader"]', marc);
1155 IF val IS NOT NULL THEN
1156 val := SUBSTRING( val, ff_pos.start_pos + 1, ff_pos.length );
1160 FOR tag_data IN SELECT value FROM UNNEST( oils_xpath( '//*[@tag="' || UPPER(ff_pos.tag) || '"]/text()', marc ) ) x(value) LOOP
1161 val := SUBSTRING( tag_data.value, ff_pos.start_pos + 1, ff_pos.length );
1165 val := REPEAT( ff_pos.default_val, ff_pos.length );
1171 $func$ LANGUAGE PLPGSQL;
1173 CREATE OR REPLACE FUNCTION vandelay.marc21_extract_all_fixed_fields( marc TEXT ) RETURNS SETOF biblio.record_ff_map AS $func$
1178 output biblio.record_ff_map%ROWTYPE;
1180 rtype := (vandelay.marc21_record_type( marc )).code;
1182 FOR ff_pos IN SELECT * FROM config.marc21_ff_pos_map WHERE rec_type = rtype ORDER BY tag DESC LOOP
1183 output.ff_name := ff_pos.fixed_field;
1184 output.ff_value := NULL;
1186 IF ff_pos.tag = 'ldr' THEN
1187 output.ff_value := oils_xpath_string('//*[local-name()="leader"]', marc);
1188 IF output.ff_value IS NOT NULL THEN
1189 output.ff_value := SUBSTRING( output.ff_value, ff_pos.start_pos + 1, ff_pos.length );
1191 output.ff_value := NULL;
1194 FOR tag_data IN SELECT value FROM UNNEST( oils_xpath( '//*[@tag="' || UPPER(ff_pos.tag) || '"]/text()', marc ) ) x(value) LOOP
1195 output.ff_value := SUBSTRING( tag_data, ff_pos.start_pos + 1, ff_pos.length );
1196 IF output.ff_value IS NULL THEN output.ff_value := REPEAT( ff_pos.default_val, ff_pos.length ); END IF;
1198 output.ff_value := NULL;
1206 $func$ LANGUAGE PLPGSQL;
1208 CREATE OR REPLACE FUNCTION vandelay.marc21_physical_characteristics( marc TEXT) RETURNS SETOF biblio.marc21_physical_characteristics AS $func$
1212 ptype config.marc21_physical_characteristic_type_map%ROWTYPE;
1213 psf config.marc21_physical_characteristic_subfield_map%ROWTYPE;
1214 pval config.marc21_physical_characteristic_value_map%ROWTYPE;
1215 retval biblio.marc21_physical_characteristics%ROWTYPE;
1218 _007 := oils_xpath_string( '//*[@tag="007"]', marc );
1220 IF _007 IS NOT NULL AND _007 <> '' THEN
1221 SELECT * INTO ptype FROM config.marc21_physical_characteristic_type_map WHERE ptype_key = SUBSTRING( _007, 1, 1 );
1223 IF ptype.ptype_key IS NOT NULL THEN
1224 FOR psf IN SELECT * FROM config.marc21_physical_characteristic_subfield_map WHERE ptype_key = ptype.ptype_key LOOP
1225 SELECT * INTO pval FROM config.marc21_physical_characteristic_value_map WHERE ptype_subfield = psf.id AND value = SUBSTRING( _007, psf.start_pos + 1, psf.length );
1227 IF pval.id IS NOT NULL THEN
1230 retval.ptype := ptype.ptype_key;
1231 retval.subfield := psf.id;
1232 retval.value := pval.id;
1242 $func$ LANGUAGE PLPGSQL;
1244 CREATE TYPE vandelay.flat_marc AS ( tag CHAR(3), ind1 TEXT, ind2 TEXT, subfield TEXT, value TEXT );
1245 CREATE OR REPLACE FUNCTION vandelay.flay_marc ( TEXT ) RETURNS SETOF vandelay.flat_marc AS $func$
1248 use MARC::File::XML (BinaryEncoding => 'UTF-8');
1252 MARC::Charset->assume_unicode(1);
1255 my $r = MARC::Record->new_from_xml( $xml );
1257 return_next( { tag => 'LDR', value => $r->leader } );
1259 for my $f ( $r->fields ) {
1260 if ($f->is_control_field) {
1261 return_next({ tag => $f->tag, value => $f->data });
1263 for my $s ($f->subfields) {
1266 ind1 => $f->indicator(1),
1267 ind2 => $f->indicator(2),
1268 subfield => $s->[0],
1272 if ( $f->tag eq '245' and $s->[0] eq 'a' ) {
1273 my $trim = $f->indicator(2) || 0;
1276 ind1 => $f->indicator(1),
1277 ind2 => $f->indicator(2),
1279 value => substr( $s->[1], $trim )
1288 $func$ LANGUAGE PLPERLU;
1290 CREATE OR REPLACE FUNCTION vandelay.flatten_marc ( marc TEXT ) RETURNS SETOF vandelay.flat_marc AS $func$
1292 output vandelay.flat_marc%ROWTYPE;
1295 FOR field IN SELECT * FROM vandelay.flay_marc( marc ) LOOP
1296 output.ind1 := field.ind1;
1297 output.ind2 := field.ind2;
1298 output.tag := field.tag;
1299 output.subfield := field.subfield;
1300 IF field.subfield IS NOT NULL AND field.tag NOT IN ('020','022','024') THEN -- exclude standard numbers and control fields
1301 output.value := naco_normalize(field.value, field.subfield);
1303 output.value := field.value;
1306 CONTINUE WHEN output.value IS NULL;
1311 $func$ LANGUAGE PLPGSQL;
1313 CREATE OR REPLACE FUNCTION vandelay.extract_rec_attrs ( xml TEXT, attr_defs TEXT[]) RETURNS hstore AS $_$
1315 transformed_xml TEXT;
1318 xfrm config.xml_transform%ROWTYPE;
1320 new_attrs HSTORE := ''::HSTORE;
1321 attr_def config.record_attr_definition%ROWTYPE;
1324 FOR attr_def IN SELECT * FROM config.record_attr_definition WHERE name IN (SELECT * FROM UNNEST(attr_defs)) ORDER BY format LOOP
1326 IF attr_def.tag IS NOT NULL THEN -- tag (and optional subfield list) selection
1327 SELECT ARRAY_TO_STRING(ARRAY_ACCUM(x.value), COALESCE(attr_def.joiner,' ')) INTO attr_value
1328 FROM vandelay.flatten_marc(xml) AS x
1329 WHERE x.tag LIKE attr_def.tag
1331 WHEN attr_def.sf_list IS NOT NULL
1332 THEN POSITION(x.subfield IN attr_def.sf_list) > 0
1339 ELSIF attr_def.fixed_field IS NOT NULL THEN -- a named fixed field, see config.marc21_ff_pos_map.fixed_field
1340 attr_value := vandelay.marc21_extract_fixed_field(xml, attr_def.fixed_field);
1342 ELSIF attr_def.xpath IS NOT NULL THEN -- and xpath expression
1344 SELECT INTO xfrm * FROM config.xml_transform WHERE name = attr_def.format;
1346 -- See if we can skip the XSLT ... it's expensive
1347 IF prev_xfrm IS NULL OR prev_xfrm <> xfrm.name THEN
1348 -- Can't skip the transform
1349 IF xfrm.xslt <> '---' THEN
1350 transformed_xml := oils_xslt_process(xml,xfrm.xslt);
1352 transformed_xml := xml;
1355 prev_xfrm := xfrm.name;
1358 IF xfrm.name IS NULL THEN
1359 -- just grab the marcxml (empty) transform
1360 SELECT INTO xfrm * FROM config.xml_transform WHERE xslt = '---' LIMIT 1;
1361 prev_xfrm := xfrm.name;
1364 attr_value := oils_xpath_string(attr_def.xpath, transformed_xml, COALESCE(attr_def.joiner,' '), ARRAY[ARRAY[xfrm.prefix, xfrm.namespace_uri]]);
1366 ELSIF attr_def.phys_char_sf IS NOT NULL THEN -- a named Physical Characteristic, see config.marc21_physical_characteristic_*_map
1367 SELECT m.value::TEXT INTO attr_value
1368 FROM vandelay.marc21_physical_characteristics(xml) v
1369 JOIN config.marc21_physical_characteristic_value_map m ON (m.id = v.value)
1370 WHERE v.subfield = attr_def.phys_char_sf
1371 LIMIT 1; -- Just in case ...
1375 -- apply index normalizers to attr_value
1377 SELECT n.func AS func,
1378 n.param_count AS param_count,
1380 FROM config.index_normalizer n
1381 JOIN config.record_attr_index_norm_map m ON (m.norm = n.id)
1382 WHERE attr = attr_def.name
1384 EXECUTE 'SELECT ' || normalizer.func || '(' ||
1385 quote_literal( attr_value ) ||
1387 WHEN normalizer.param_count > 0
1388 THEN ',' || REPLACE(REPLACE(BTRIM(normalizer.params,'[]'),E'\'',E'\\\''),E'"',E'\'')
1391 ')' INTO attr_value;
1395 -- Add the new value to the hstore
1396 new_attrs := new_attrs || hstore( attr_def.name, attr_value );
1402 $_$ LANGUAGE PLPGSQL;
1404 CREATE OR REPLACE FUNCTION vandelay.extract_rec_attrs ( xml TEXT ) RETURNS hstore AS $_$
1405 SELECT vandelay.extract_rec_attrs( $1, (SELECT ARRAY_ACCUM(name) FROM config.record_attr_definition));
1408 -- Everything between this comment and the beginning of the definition of
1409 -- vandelay.match_bib_record() is strictly in service of that function.
1410 CREATE TYPE vandelay.match_set_test_result AS (record BIGINT, quality INTEGER);
1412 CREATE OR REPLACE FUNCTION vandelay.match_set_test_marcxml(
1413 match_set_id INTEGER, record_xml TEXT
1414 ) RETURNS SETOF vandelay.match_set_test_result AS $$
1425 tags_rstore := vandelay.flatten_marc_hstore(record_xml);
1426 svf_rstore := vandelay.extract_rec_attrs(record_xml);
1428 CREATE TEMPORARY TABLE _vandelay_tmp_qrows (q INTEGER);
1429 CREATE TEMPORARY TABLE _vandelay_tmp_jrows (j TEXT);
1431 -- generate the where clause and return that directly (into wq), and as
1432 -- a side-effect, populate the _vandelay_tmp_[qj]rows tables.
1433 wq := vandelay.get_expr_from_match_set(match_set_id);
1435 query_ := 'SELECT bre.id AS record, ';
1437 -- qrows table is for the quality bits we add to the SELECT clause
1438 SELECT ARRAY_TO_STRING(
1439 ARRAY_ACCUM('COALESCE(n' || q::TEXT || '.quality, 0)'), ' + '
1440 ) INTO coal FROM _vandelay_tmp_qrows;
1442 -- our query string so far is the SELECT clause and the inital FROM.
1443 -- no JOINs yet nor the WHERE clause
1444 query_ := query_ || coal || ' AS quality ' || E'\n' ||
1445 'FROM biblio.record_entry bre ';
1447 -- jrows table is for the joins we must make (and the real text conditions)
1448 SELECT ARRAY_TO_STRING(ARRAY_ACCUM(j), E'\n') INTO joins
1449 FROM _vandelay_tmp_jrows;
1451 -- add those joins and the where clause to our query.
1452 query_ := query_ || joins || E'\n' || 'WHERE ' || wq || ' AND not bre.deleted';
1454 -- this will return rows of record,quality
1455 FOR rec IN EXECUTE query_ USING tags_rstore, svf_rstore LOOP
1459 DROP TABLE _vandelay_tmp_qrows;
1460 DROP TABLE _vandelay_tmp_jrows;
1464 $$ LANGUAGE PLPGSQL;
1466 CREATE OR REPLACE FUNCTION vandelay.flatten_marc_hstore(
1468 ) RETURNS HSTORE AS $$
1472 ARRAY_ACCUM(tag || (COALESCE(subfield, ''))),
1476 SELECT tag, subfield, ARRAY_ACCUM(value)::TEXT AS value
1477 FROM vandelay.flatten_marc(record_xml)
1478 GROUP BY tag, subfield ORDER BY tag, subfield
1482 $$ LANGUAGE PLPGSQL;
1484 CREATE OR REPLACE FUNCTION vandelay.get_expr_from_match_set(
1485 match_set_id INTEGER
1486 ) RETURNS TEXT AS $$
1488 root vandelay.match_set_point;
1490 SELECT * INTO root FROM vandelay.match_set_point
1491 WHERE parent IS NULL AND match_set = match_set_id;
1493 RETURN vandelay.get_expr_from_match_set_point(root);
1495 $$ LANGUAGE PLPGSQL;
1497 CREATE OR REPLACE FUNCTION vandelay.get_expr_from_match_set_point(
1498 node vandelay.match_set_point
1499 ) RETURNS TEXT AS $$
1505 child vandelay.match_set_point;
1507 SELECT ARRAY_ACCUM(id) INTO children FROM vandelay.match_set_point
1508 WHERE parent = node.id;
1510 IF ARRAY_LENGTH(children, 1) > 0 THEN
1511 this_op := vandelay._get_expr_render_one(node);
1514 WHILE children[i] IS NOT NULL LOOP
1515 SELECT * INTO child FROM vandelay.match_set_point
1516 WHERE id = children[i];
1518 q := q || ' ' || this_op || ' ';
1521 q := q || vandelay.get_expr_from_match_set_point(child);
1525 ELSIF node.bool_op IS NULL THEN
1526 PERFORM vandelay._get_expr_push_qrow(node);
1527 PERFORM vandelay._get_expr_push_jrow(node);
1528 RETURN vandelay._get_expr_render_one(node);
1533 $$ LANGUAGE PLPGSQL;
1535 CREATE OR REPLACE FUNCTION vandelay._get_expr_push_qrow(
1536 node vandelay.match_set_point
1537 ) RETURNS VOID AS $$
1540 INSERT INTO _vandelay_tmp_qrows (q) VALUES (node.id);
1542 $$ LANGUAGE PLPGSQL;
1544 CREATE OR REPLACE FUNCTION vandelay._get_expr_push_jrow(
1545 node vandelay.match_set_point
1546 ) RETURNS VOID AS $$
1559 IF node.tag IS NOT NULL THEN
1561 IF node.subfield IS NOT NULL THEN
1562 tagkey := tagkey || node.subfield;
1566 my_alias := 'n' || node.id::TEXT;
1568 jrow := 'LEFT JOIN (SELECT *, ' || node.quality ||
1569 ' AS quality FROM metabib.';
1570 IF node.tag IS NOT NULL THEN
1571 jrow := jrow || 'full_rec) ' || my_alias || ' ON (' ||
1572 my_alias || '.record = bre.id AND ' || my_alias || '.tag = ''' ||
1574 IF node.subfield IS NOT NULL THEN
1575 jrow := jrow || ' AND ' || my_alias || '.subfield = ''' ||
1576 node.subfield || '''';
1578 jrow := jrow || ' AND (' || my_alias || '.value ' || op ||
1579 ' ANY(($1->''' || tagkey || ''')::TEXT[])))';
1581 jrow := jrow || 'record_attr) ' || my_alias || ' ON (' ||
1582 my_alias || '.id = bre.id AND (' ||
1583 my_alias || '.attrs->''' || node.svf ||
1584 ''' ' || op || ' $2->''' || node.svf || '''))';
1586 INSERT INTO _vandelay_tmp_jrows (j) VALUES (jrow);
1588 $$ LANGUAGE PLPGSQL;
1590 CREATE OR REPLACE FUNCTION vandelay._get_expr_render_one(
1591 node vandelay.match_set_point
1592 ) RETURNS TEXT AS $$
1596 IF node.bool_op IS NOT NULL THEN
1597 RETURN node.bool_op;
1599 RETURN '(n' || node.id::TEXT || '.id IS NOT NULL)';
1602 $$ LANGUAGE PLPGSQL;
1604 CREATE OR REPLACE FUNCTION vandelay.match_bib_record() RETURNS TRIGGER AS $func$
1606 incoming_existing_id TEXT;
1607 test_result vandelay.match_set_test_result%ROWTYPE;
1611 IF TG_OP IN ('INSERT','UPDATE') AND NEW.imported_as IS NOT NULL THEN
1615 DELETE FROM vandelay.bib_match WHERE queued_record = NEW.id;
1617 SELECT q.match_set INTO match_set FROM vandelay.bib_queue q WHERE q.id = NEW.queue;
1619 IF match_set IS NOT NULL THEN
1620 NEW.quality := vandelay.measure_record_quality( NEW.marc, match_set );
1623 -- Perfect matches on 901$c exit early with a match with high quality.
1624 incoming_existing_id :=
1625 oils_xpath_string('//*[@tag="901"]/*[@code="c"][1]', NEW.marc);
1627 IF incoming_existing_id IS NOT NULL AND incoming_existing_id != '' THEN
1628 SELECT id INTO tmp_rec FROM biblio.record_entry WHERE id = incoming_existing_id::bigint;
1629 IF tmp_rec IS NOT NULL THEN
1630 INSERT INTO vandelay.bib_match (queued_record, eg_record, match_score, quality)
1635 -- note: no match_set means quality==0
1636 vandelay.measure_record_quality( b.marc, match_set )
1637 FROM biblio.record_entry b
1638 WHERE id = incoming_existing_id::bigint;
1642 IF match_set IS NULL THEN
1646 FOR test_result IN SELECT * FROM
1647 vandelay.match_set_test_marcxml(match_set, NEW.marc) LOOP
1649 INSERT INTO vandelay.bib_match ( queued_record, eg_record, match_score, quality )
1653 test_result.quality,
1654 vandelay.measure_record_quality( b.marc, match_set )
1655 FROM biblio.record_entry b
1656 WHERE id = test_result.record;
1662 $func$ LANGUAGE PLPGSQL;
1664 CREATE OR REPLACE FUNCTION vandelay.measure_record_quality ( xml TEXT, match_set_id INT ) RETURNS INT AS $_$
1668 test vandelay.match_set_quality%ROWTYPE;
1671 FOR test IN SELECT * FROM vandelay.match_set_quality WHERE match_set = match_set_id LOOP
1672 IF test.tag IS NOT NULL THEN
1673 FOR rvalue IN SELECT value FROM vandelay.flatten_marc( xml ) WHERE tag = test.tag AND subfield = test.subfield LOOP
1674 IF test.value = rvalue THEN
1675 out_q := out_q + test.quality;
1679 IF test.value = vandelay.extract_rec_attrs(xml, ARRAY[test.svf]) -> test.svf THEN
1680 out_q := out_q + test.quality;
1687 $_$ LANGUAGE PLPGSQL;
1690 CREATE OR REPLACE FUNCTION vandelay.overlay_bib_record ( import_id BIGINT, eg_id BIGINT, merge_profile_id INT ) RETURNS BOOL AS $$
1692 merge_profile vandelay.merge_profile%ROWTYPE;
1693 dyn_profile vandelay.compile_profile%ROWTYPE;
1703 SELECT q.marc INTO v_marc
1704 FROM vandelay.queued_record q
1705 JOIN vandelay.bib_match m ON (m.queued_record = q.id AND q.id = import_id)
1708 IF v_marc IS NULL THEN
1709 -- RAISE NOTICE 'no marc for vandelay or bib record';
1713 IF vandelay.template_overlay_bib_record( v_marc, eg_id, merge_profile_id) THEN
1714 UPDATE vandelay.queued_bib_record
1715 SET imported_as = eg_id,
1717 WHERE id = import_id;
1719 editor_string := (oils_xpath('//*[@tag="905"]/*[@code="u"]/text()',v_marc))[1];
1721 IF editor_string IS NOT NULL AND editor_string <> '' THEN
1722 SELECT usr INTO editor_id FROM actor.card WHERE barcode = editor_string;
1724 IF editor_id IS NULL THEN
1725 SELECT id INTO editor_id FROM actor.usr WHERE usrname = editor_string;
1728 IF editor_id IS NOT NULL THEN
1729 UPDATE biblio.record_entry SET editor = editor_id WHERE id = eg_id;
1736 -- RAISE NOTICE 'update of biblio.record_entry failed';
1741 $$ LANGUAGE PLPGSQL;
1744 CREATE OR REPLACE FUNCTION vandelay.auto_overlay_bib_record_with_best ( import_id BIGINT, merge_profile_id INT, lwm_ratio_value_p NUMERIC ) RETURNS BOOL AS $$
1747 lwm_ratio_value NUMERIC;
1750 lwm_ratio_value := COALESCE(lwm_ratio_value_p, 0.0);
1752 PERFORM * FROM vandelay.queued_bib_record WHERE import_time IS NOT NULL AND id = import_id;
1755 -- RAISE NOTICE 'already imported, cannot auto-overlay'
1759 SELECT m.eg_record INTO eg_id
1760 FROM vandelay.bib_match m
1761 JOIN vandelay.queued_bib_record qr ON (m.queued_record = qr.id)
1762 JOIN vandelay.bib_queue q ON (qr.queue = q.id)
1763 JOIN biblio.record_entry r ON (r.id = m.eg_record)
1764 WHERE m.queued_record = import_id
1765 AND qr.quality::NUMERIC / COALESCE(NULLIF(m.quality,0),1)::NUMERIC >= lwm_ratio_value
1766 ORDER BY m.match_score DESC, -- required match score
1767 qr.quality::NUMERIC / COALESCE(NULLIF(m.quality,0),1)::NUMERIC DESC, -- quality tie breaker
1768 m.id -- when in doubt, use the first match
1771 IF eg_id IS NULL THEN
1772 -- RAISE NOTICE 'incoming record is not of high enough quality';
1776 RETURN vandelay.overlay_bib_record( import_id, eg_id, merge_profile_id );
1778 $$ LANGUAGE PLPGSQL;
1780 CREATE OR REPLACE FUNCTION vandelay.auto_overlay_bib_record_with_best ( import_id BIGINT, merge_profile_id INT, lwm_ratio_value_p NUMERIC ) RETURNS BOOL AS $$
1783 lwm_ratio_value NUMERIC;
1786 lwm_ratio_value := COALESCE(lwm_ratio_value_p, 0.0);
1788 PERFORM * FROM vandelay.queued_bib_record WHERE import_time IS NOT NULL AND id = import_id;
1791 -- RAISE NOTICE 'already imported, cannot auto-overlay'
1795 SELECT m.eg_record INTO eg_id
1796 FROM vandelay.bib_match m
1797 JOIN vandelay.queued_bib_record qr ON (m.queued_record = qr.id)
1798 JOIN vandelay.bib_queue q ON (qr.queue = q.id)
1799 JOIN biblio.record_entry r ON (r.id = m.eg_record)
1800 WHERE m.queued_record = import_id
1801 AND qr.quality::NUMERIC / COALESCE(NULLIF(m.quality,0),1)::NUMERIC >= lwm_ratio_value
1802 ORDER BY m.match_score DESC, -- required match score
1803 qr.quality::NUMERIC / COALESCE(NULLIF(m.quality,0),1)::NUMERIC DESC, -- quality tie breaker
1804 m.id -- when in doubt, use the first match
1807 IF eg_id IS NULL THEN
1808 -- RAISE NOTICE 'incoming record is not of high enough quality';
1812 RETURN vandelay.overlay_bib_record( import_id, eg_id, merge_profile_id );
1814 $$ LANGUAGE PLPGSQL;
1817 CREATE OR REPLACE FUNCTION vandelay.auto_overlay_bib_queue_with_best ( queue_id BIGINT, merge_profile_id INT, lwm_ratio_value NUMERIC ) RETURNS SETOF BIGINT AS $$
1819 queued_record vandelay.queued_bib_record%ROWTYPE;
1822 FOR queued_record IN SELECT * FROM vandelay.queued_bib_record WHERE queue = queue_id AND import_time IS NULL LOOP
1824 IF vandelay.auto_overlay_bib_record_with_best( queued_record.id, merge_profile_id, lwm_ratio_value ) THEN
1825 RETURN NEXT queued_record.id;
1833 $$ LANGUAGE PLPGSQL;
1835 CREATE OR REPLACE FUNCTION vandelay.auto_overlay_bib_queue_with_best ( import_id BIGINT, merge_profile_id INT ) RETURNS SETOF BIGINT AS $$
1836 SELECT vandelay.auto_overlay_bib_queue_with_best( $1, $2, p.lwm_ratio ) FROM vandelay.merge_profile p WHERE id = $2;
1839 CREATE OR REPLACE FUNCTION vandelay.ingest_bib_marc ( ) RETURNS TRIGGER AS $$
1845 IF TG_OP IN ('INSERT','UPDATE') AND NEW.imported_as IS NOT NULL THEN
1849 FOR adef IN SELECT * FROM vandelay.bib_attr_definition LOOP
1851 SELECT extract_marc_field('vandelay.queued_bib_record', id, adef.xpath, adef.remove) INTO value FROM vandelay.queued_bib_record WHERE id = NEW.id;
1852 IF (value IS NOT NULL AND value <> '') THEN
1853 INSERT INTO vandelay.queued_bib_record_attr (record, field, attr_value) VALUES (NEW.id, adef.id, value);
1860 $$ LANGUAGE PLPGSQL;
1862 CREATE OR REPLACE FUNCTION vandelay.ingest_bib_items ( ) RETURNS TRIGGER AS $func$
1865 item_data vandelay.import_item%ROWTYPE;
1868 IF TG_OP IN ('INSERT','UPDATE') AND NEW.imported_as IS NOT NULL THEN
1872 SELECT item_attr_def INTO attr_def FROM vandelay.bib_queue WHERE id = NEW.queue;
1874 FOR item_data IN SELECT * FROM vandelay.ingest_items( NEW.id::BIGINT, attr_def ) LOOP
1875 INSERT INTO vandelay.import_item (
1899 item_data.definition,
1900 item_data.owning_lib,
1902 item_data.call_number,
1903 item_data.copy_number,
1906 item_data.circulate,
1908 item_data.deposit_amount,
1913 item_data.circ_modifier,
1914 item_data.circ_as_type,
1915 item_data.alert_message,
1917 item_data.priv_note,
1918 item_data.opac_visible
1924 $func$ LANGUAGE PLPGSQL;
1926 CREATE OR REPLACE FUNCTION vandelay.cleanup_bib_marc ( ) RETURNS TRIGGER AS $$
1928 IF TG_OP IN ('INSERT','UPDATE') AND NEW.imported_as IS NOT NULL THEN
1932 DELETE FROM vandelay.queued_bib_record_attr WHERE record = OLD.id;
1933 DELETE FROM vandelay.import_item WHERE record = OLD.id;
1935 IF TG_OP = 'UPDATE' THEN
1940 $$ LANGUAGE PLPGSQL;
1944 DROP TRIGGER zz_match_bibs_trigger ON vandelay.queued_bib_record;
1945 CREATE TRIGGER zz_match_bibs_trigger
1946 BEFORE INSERT OR UPDATE ON vandelay.queued_bib_record
1947 FOR EACH ROW EXECUTE PROCEDURE vandelay.match_bib_record();
1949 CREATE OR REPLACE FUNCTION vandelay.ingest_authority_marc ( ) RETURNS TRIGGER AS $$
1955 IF TG_OP IN ('INSERT','UPDATE') AND NEW.imported_as IS NOT NULL THEN
1959 FOR adef IN SELECT * FROM vandelay.authority_attr_definition LOOP
1961 SELECT extract_marc_field('vandelay.queued_authority_record', id, adef.xpath, adef.remove) INTO value FROM vandelay.queued_authority_record WHERE id = NEW.id;
1962 IF (value IS NOT NULL AND value <> '') THEN
1963 INSERT INTO vandelay.queued_authority_record_attr (record, field, attr_value) VALUES (NEW.id, adef.id, value);
1970 $$ LANGUAGE PLPGSQL;
1972 ALTER TABLE vandelay.authority_attr_definition DROP COLUMN ident;
1973 ALTER TABLE vandelay.queued_authority_record
1974 ADD COLUMN import_error TEXT REFERENCES vandelay.import_error (code) ON DELETE SET NULL ON UPDATE CASCADE DEFERRABLE INITIALLY DEFERRED,
1975 ADD COLUMN error_detail TEXT;
1977 ALTER TABLE vandelay.authority_match DROP COLUMN matched_attr;
1978 ALTER TABLE vandelay.authority_match ADD COLUMN quality INTEGER NOT NULL DEFAULT 0;
1980 CREATE OR REPLACE FUNCTION vandelay.cleanup_authority_marc ( ) RETURNS TRIGGER AS $$
1982 IF TG_OP IN ('INSERT','UPDATE') AND NEW.imported_as IS NOT NULL THEN
1986 DELETE FROM vandelay.queued_authority_record_attr WHERE record = OLD.id;
1987 IF TG_OP = 'UPDATE' THEN
1992 $$ LANGUAGE PLPGSQL;
1994 CREATE OR REPLACE FUNCTION authority.flatten_marc ( rid BIGINT ) RETURNS SETOF authority.full_rec AS $func$
1996 auth authority.record_entry%ROWTYPE;
1997 output authority.full_rec%ROWTYPE;
2000 SELECT INTO auth * FROM authority.record_entry WHERE id = rid;
2002 FOR field IN SELECT * FROM vandelay.flatten_marc( auth.marc ) LOOP
2003 output.record := rid;
2004 output.ind1 := field.ind1;
2005 output.ind2 := field.ind2;
2006 output.tag := field.tag;
2007 output.subfield := field.subfield;
2008 output.value := field.value;
2013 $func$ LANGUAGE PLPGSQL;
2015 CREATE OR REPLACE FUNCTION biblio.flatten_marc ( rid BIGINT ) RETURNS SETOF metabib.full_rec AS $func$
2017 bib biblio.record_entry%ROWTYPE;
2018 output metabib.full_rec%ROWTYPE;
2021 SELECT INTO bib * FROM biblio.record_entry WHERE id = rid;
2023 FOR field IN SELECT * FROM vandelay.flatten_marc( bib.marc ) LOOP
2024 output.record := rid;
2025 output.ind1 := field.ind1;
2026 output.ind2 := field.ind2;
2027 output.tag := field.tag;
2028 output.subfield := field.subfield;
2029 output.value := field.value;
2034 $func$ LANGUAGE PLPGSQL;
2036 -----------------------------------------------
2037 -- Seed data for import errors
2038 -----------------------------------------------
2040 INSERT INTO vandelay.import_error ( code, description ) VALUES ( 'general.unknown', oils_i18n_gettext('general.unknown', 'Import or Overlay failed', 'vie', 'description') );
2041 INSERT INTO vandelay.import_error ( code, description ) VALUES ( 'import.item.duplicate.barcode', oils_i18n_gettext('import.item.duplicate.barcode', 'Import failed due to barcode collision', 'vie', 'description') );
2042 INSERT INTO vandelay.import_error ( code, description ) VALUES ( 'import.item.invalid.circ_modifier', oils_i18n_gettext('import.item.invalid.circ_modifier', 'Import failed due to invalid circulation modifier', 'vie', 'description') );
2043 INSERT INTO vandelay.import_error ( code, description ) VALUES ( 'import.item.invalid.location', oils_i18n_gettext('import.item.invalid.location', 'Import failed due to invalid copy location', 'vie', 'description') );
2044 INSERT INTO vandelay.import_error ( code, description ) VALUES ( 'import.duplicate.sysid', oils_i18n_gettext('import.duplicate.sysid', 'Import failed due to system id collision', 'vie', 'description') );
2045 INSERT INTO vandelay.import_error ( code, description ) VALUES ( 'import.duplicate.tcn', oils_i18n_gettext('import.duplicate.sysid', 'Import failed due to system id collision', 'vie', 'description') );
2046 INSERT INTO vandelay.import_error ( code, description ) VALUES ( 'overlay.missing.sysid', oils_i18n_gettext('overlay.missing.sysid', 'Overlay failed due to missing system id', 'vie', 'description') );
2047 INSERT INTO vandelay.import_error ( code, description ) VALUES ( 'import.auth.duplicate.acn', oils_i18n_gettext('import.auth.duplicate.acn', 'Import failed due to Accession Number collision', 'vie', 'description') );
2048 INSERT INTO vandelay.import_error ( code, description ) VALUES ( 'import.xml.malformed', oils_i18n_gettext('import.xml.malformed', 'Malformed record cause Import failure', 'vie', 'description') );
2049 INSERT INTO vandelay.import_error ( code, description ) VALUES ( 'overlay.xml.malformed', oils_i18n_gettext('overlay.xml.malformed', 'Malformed record cause Overlay failure', 'vie', 'description') );
2050 INSERT INTO vandelay.import_error ( code, description ) VALUES ( 'overlay.record.quality', oils_i18n_gettext('overlay.record.quality', 'New record had insufficient quality', 'vie', 'description') );
2053 ----------------------------------------------------------------
2054 -- Seed data for queued record/item exports
2055 ----------------------------------------------------------------
2057 INSERT INTO action_trigger.hook (key,core_type,description,passive) VALUES (
2058 'vandelay.queued_bib_record.print',
2061 'vandelay.queued_bib_record.print',
2062 'Print output has been requested for records in an Importer Bib Queue.',
2069 'vandelay.queued_bib_record.csv',
2072 'vandelay.queued_bib_record.csv',
2073 'CSV output has been requested for records in an Importer Bib Queue.',
2080 'vandelay.queued_bib_record.email',
2083 'vandelay.queued_bib_record.email',
2084 'An email has been requested for records in an Importer Bib Queue.',
2091 'vandelay.queued_auth_record.print',
2094 'vandelay.queued_auth_record.print',
2095 'Print output has been requested for records in an Importer Authority Queue.',
2102 'vandelay.queued_auth_record.csv',
2105 'vandelay.queued_auth_record.csv',
2106 'CSV output has been requested for records in an Importer Authority Queue.',
2113 'vandelay.queued_auth_record.email',
2116 'vandelay.queued_auth_record.email',
2117 'An email has been requested for records in an Importer Authority Queue.',
2124 'vandelay.import_items.print',
2127 'vandelay.import_items.print',
2128 'Print output has been requested for Import Items from records in an Importer Bib Queue.',
2135 'vandelay.import_items.csv',
2138 'vandelay.import_items.csv',
2139 'CSV output has been requested for Import Items from records in an Importer Bib Queue.',
2146 'vandelay.import_items.email',
2149 'vandelay.import_items.email',
2150 'An email has been requested for Import Items from records in an Importer Bib Queue.',
2158 INSERT INTO action_trigger.event_definition (
2173 'Print Output for Queued Bib Records',
2174 'vandelay.queued_bib_record.print',
2182 Queue ID: [% target.0.queue.id %]
2183 Queue Name: [% target.0.queue.name %]
2184 Queue Type: [% target.0.queue.queue_type %]
2185 Complete? [% target.0.queue.complete %]
2187 [% FOR vqbr IN target %]
2189 Title of work | [% helpers.get_queued_bib_attr('title',vqbr.attributes) %]
2190 Author of work | [% helpers.get_queued_bib_attr('author',vqbr.attributes) %]
2191 Language of work | [% helpers.get_queued_bib_attr('language',vqbr.attributes) %]
2192 Pagination | [% helpers.get_queued_bib_attr('pagination',vqbr.attributes) %]
2193 ISBN | [% helpers.get_queued_bib_attr('isbn',vqbr.attributes) %]
2194 ISSN | [% helpers.get_queued_bib_attr('issn',vqbr.attributes) %]
2195 Price | [% helpers.get_queued_bib_attr('price',vqbr.attributes) %]
2196 Accession Number | [% helpers.get_queued_bib_attr('rec_identifier',vqbr.attributes) %]
2197 TCN Value | [% helpers.get_queued_bib_attr('eg_tcn',vqbr.attributes) %]
2198 TCN Source | [% helpers.get_queued_bib_attr('eg_tcn_source',vqbr.attributes) %]
2199 Internal ID | [% helpers.get_queued_bib_attr('eg_identifier',vqbr.attributes) %]
2200 Publisher | [% helpers.get_queued_bib_attr('publisher',vqbr.attributes) %]
2201 Publication Date | [% helpers.get_queued_bib_attr('pubdate',vqbr.attributes) %]
2202 Edition | [% helpers.get_queued_bib_attr('edition',vqbr.attributes) %]
2203 Item Barcode | [% helpers.get_queued_bib_attr('item_barcode',vqbr.attributes) %]
2211 INSERT INTO action_trigger.environment ( event_def, path) VALUES (
2216 INSERT INTO action_trigger.event_definition (
2231 'CSV Output for Queued Bib Records',
2232 'vandelay.queued_bib_record.csv',
2239 "Title of work","Author of work","Language of work","Pagination","ISBN","ISSN","Price","Accession Number","TCN Value","TCN Source","Internal ID","Publisher","Publication Date","Edition","Item Barcode"
2240 [% FOR vqbr IN target %]"[% helpers.get_queued_bib_attr('title',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('author',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('language',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('pagination',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('isbn',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('issn',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('price',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('rec_identifier',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('eg_tcn',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('eg_tcn_source',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('eg_identifier',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('publisher',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('pubdate',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('edition',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('item_barcode',vqbr.attributes) | replace('"', '""') %]"
2246 INSERT INTO action_trigger.environment ( event_def, path) VALUES (
2251 INSERT INTO action_trigger.event_definition (
2266 'Email Output for Queued Bib Records',
2267 'vandelay.queued_bib_record.email',
2274 [%- SET user = target.0.queue.owner -%]
2275 To: [%- params.recipient_email || user.email || 'root@localhost' %]
2276 From: [%- params.sender_email || default_sender %]
2277 Subject: Bibs from Import Queue
2279 Queue ID: [% target.0.queue.id %]
2280 Queue Name: [% target.0.queue.name %]
2281 Queue Type: [% target.0.queue.queue_type %]
2282 Complete? [% target.0.queue.complete %]
2284 [% FOR vqbr IN target %]
2286 Title of work | [% helpers.get_queued_bib_attr('title',vqbr.attributes) %]
2287 Author of work | [% helpers.get_queued_bib_attr('author',vqbr.attributes) %]
2288 Language of work | [% helpers.get_queued_bib_attr('language',vqbr.attributes) %]
2289 Pagination | [% helpers.get_queued_bib_attr('pagination',vqbr.attributes) %]
2290 ISBN | [% helpers.get_queued_bib_attr('isbn',vqbr.attributes) %]
2291 ISSN | [% helpers.get_queued_bib_attr('issn',vqbr.attributes) %]
2292 Price | [% helpers.get_queued_bib_attr('price',vqbr.attributes) %]
2293 Accession Number | [% helpers.get_queued_bib_attr('rec_identifier',vqbr.attributes) %]
2294 TCN Value | [% helpers.get_queued_bib_attr('eg_tcn',vqbr.attributes) %]
2295 TCN Source | [% helpers.get_queued_bib_attr('eg_tcn_source',vqbr.attributes) %]
2296 Internal ID | [% helpers.get_queued_bib_attr('eg_identifier',vqbr.attributes) %]
2297 Publisher | [% helpers.get_queued_bib_attr('publisher',vqbr.attributes) %]
2298 Publication Date | [% helpers.get_queued_bib_attr('pubdate',vqbr.attributes) %]
2299 Edition | [% helpers.get_queued_bib_attr('edition',vqbr.attributes) %]
2300 Item Barcode | [% helpers.get_queued_bib_attr('item_barcode',vqbr.attributes) %]
2308 INSERT INTO action_trigger.environment ( event_def, path) VALUES (
2311 ,( 41, 'queue.owner')
2314 INSERT INTO action_trigger.event_definition (
2329 'Print Output for Queued Authority Records',
2330 'vandelay.queued_auth_record.print',
2338 Queue ID: [% target.0.queue.id %]
2339 Queue Name: [% target.0.queue.name %]
2340 Queue Type: [% target.0.queue.queue_type %]
2341 Complete? [% target.0.queue.complete %]
2343 [% FOR vqar IN target %]
2345 Record Identifier | [% helpers.get_queued_auth_attr('rec_identifier',vqar.attributes) %]
2353 INSERT INTO action_trigger.environment ( event_def, path) VALUES (
2358 INSERT INTO action_trigger.event_definition (
2373 'CSV Output for Queued Authority Records',
2374 'vandelay.queued_auth_record.csv',
2382 [% FOR vqar IN target %]"[% helpers.get_queued_auth_attr('rec_identifier',vqar.attributes) | replace('"', '""') %]"
2388 INSERT INTO action_trigger.environment ( event_def, path) VALUES (
2393 INSERT INTO action_trigger.event_definition (
2408 'Email Output for Queued Authority Records',
2409 'vandelay.queued_auth_record.email',
2416 [%- SET user = target.0.queue.owner -%]
2417 To: [%- params.recipient_email || user.email || 'root@localhost' %]
2418 From: [%- params.sender_email || default_sender %]
2419 Subject: Authorities from Import Queue
2421 Queue ID: [% target.0.queue.id %]
2422 Queue Name: [% target.0.queue.name %]
2423 Queue Type: [% target.0.queue.queue_type %]
2424 Complete? [% target.0.queue.complete %]
2426 [% FOR vqar IN target %]
2428 Record Identifier | [% helpers.get_queued_auth_attr('rec_identifier',vqar.attributes) %]
2436 INSERT INTO action_trigger.environment ( event_def, path) VALUES (
2439 ,( 44, 'queue.owner')
2442 INSERT INTO action_trigger.event_definition (
2457 'Print Output for Import Items from Queued Bib Records',
2458 'vandelay.import_items.print',
2461 'record.queue.owner',
2466 Queue ID: [% target.0.record.queue.id %]
2467 Queue Name: [% target.0.record.queue.name %]
2468 Queue Type: [% target.0.record.queue.queue_type %]
2469 Complete? [% target.0.record.queue.complete %]
2471 [% FOR vii IN target %]
2473 Import Item ID | [% vii.id %]
2474 Title of work | [% helpers.get_queued_bib_attr('title',vii.record.attributes) %]
2475 ISBN | [% helpers.get_queued_bib_attr('isbn',vii.record.attributes) %]
2476 Attribute Definition | [% vii.definition %]
2477 Import Error | [% vii.import_error %]
2478 Import Error Detail | [% vii.error_detail %]
2479 Owning Library | [% vii.owning_lib %]
2480 Circulating Library | [% vii.circ_lib %]
2481 Call Number | [% vii.call_number %]
2482 Copy Number | [% vii.copy_number %]
2483 Status | [% vii.status.name %]
2484 Shelving Location | [% vii.location.name %]
2485 Circulate | [% vii.circulate %]
2486 Deposit | [% vii.deposit %]
2487 Deposit Amount | [% vii.deposit_amount %]
2488 Reference | [% vii.ref %]
2489 Holdable | [% vii.holdable %]
2490 Price | [% vii.price %]
2491 Barcode | [% vii.barcode %]
2492 Circulation Modifier | [% vii.circ_modifier %]
2493 Circulate As MARC Type | [% vii.circ_as_type %]
2494 Alert Message | [% vii.alert_message %]
2495 Public Note | [% vii.pub_note %]
2496 Private Note | [% vii.priv_note %]
2497 OPAC Visible | [% vii.opac_visible %]
2505 INSERT INTO action_trigger.environment ( event_def, path) VALUES (
2507 ,( 45, 'record.attributes')
2508 ,( 45, 'record.queue')
2509 ,( 45, 'record.queue.owner')
2512 INSERT INTO action_trigger.event_definition (
2527 'CSV Output for Import Items from Queued Bib Records',
2528 'vandelay.import_items.csv',
2531 'record.queue.owner',
2535 "Import Item ID","Title of work","ISBN","Attribute Definition","Import Error","Import Error Detail","Owning Library","Circulating Library","Call Number","Copy Number","Status","Shelving Location","Circulate","Deposit","Deposit Amount","Reference","Holdable","Price","Barcode","Circulation Modifier","Circulate As MARC Type","Alert Message","Public Note","Private Note","OPAC Visible"
2536 [% FOR vii IN target %]"[% vii.id | replace('"', '""') %]","[% helpers.get_queued_bib_attr('title',vii.record.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('isbn',vii.record.attributes) | replace('"', '""') %]","[% vii.definition | replace('"', '""') %]","[% vii.import_error | replace('"', '""') %]","[% vii.error_detail | replace('"', '""') %]","[% vii.owning_lib | replace('"', '""') %]","[% vii.circ_lib | replace('"', '""') %]","[% vii.call_number | replace('"', '""') %]","[% vii.copy_number | replace('"', '""') %]","[% vii.status.name | replace('"', '""') %]","[% vii.location.name | replace('"', '""') %]","[% vii.circulate | replace('"', '""') %]","[% vii.deposit | replace('"', '""') %]","[% vii.deposit_amount | replace('"', '""') %]","[% vii.ref | replace('"', '""') %]","[% vii.holdable | replace('"', '""') %]","[% vii.price | replace('"', '""') %]","[% vii.barcode | replace('"', '""') %]","[% vii.circ_modifier | replace('"', '""') %]","[% vii.circ_as_type | replace('"', '""') %]","[% vii.alert_message | replace('"', '""') %]","[% vii.pub_note | replace('"', '""') %]","[% vii.priv_note | replace('"', '""') %]","[% vii.opac_visible | replace('"', '""') %]"
2542 INSERT INTO action_trigger.environment ( event_def, path) VALUES (
2544 ,( 46, 'record.attributes')
2545 ,( 46, 'record.queue')
2546 ,( 46, 'record.queue.owner')
2549 INSERT INTO action_trigger.event_definition (
2564 'Email Output for Import Items from Queued Bib Records',
2565 'vandelay.import_items.email',
2568 'record.queue.owner',
2572 [%- SET user = target.0.record.queue.owner -%]
2573 To: [%- params.recipient_email || user.email || 'root@localhost' %]
2574 From: [%- params.sender_email || default_sender %]
2575 Subject: Import Items from Import Queue
2577 Queue ID: [% target.0.record.queue.id %]
2578 Queue Name: [% target.0.record.queue.name %]
2579 Queue Type: [% target.0.record.queue.queue_type %]
2580 Complete? [% target.0.record.queue.complete %]
2582 [% FOR vii IN target %]
2584 Import Item ID | [% vii.id %]
2585 Title of work | [% helpers.get_queued_bib_attr('title',vii.record.attributes) %]
2586 ISBN | [% helpers.get_queued_bib_attr('isbn',vii.record.attributes) %]
2587 Attribute Definition | [% vii.definition %]
2588 Import Error | [% vii.import_error %]
2589 Import Error Detail | [% vii.error_detail %]
2590 Owning Library | [% vii.owning_lib %]
2591 Circulating Library | [% vii.circ_lib %]
2592 Call Number | [% vii.call_number %]
2593 Copy Number | [% vii.copy_number %]
2594 Status | [% vii.status.name %]
2595 Shelving Location | [% vii.location.name %]
2596 Circulate | [% vii.circulate %]
2597 Deposit | [% vii.deposit %]
2598 Deposit Amount | [% vii.deposit_amount %]
2599 Reference | [% vii.ref %]
2600 Holdable | [% vii.holdable %]
2601 Price | [% vii.price %]
2602 Barcode | [% vii.barcode %]
2603 Circulation Modifier | [% vii.circ_modifier %]
2604 Circulate As MARC Type | [% vii.circ_as_type %]
2605 Alert Message | [% vii.alert_message %]
2606 Public Note | [% vii.pub_note %]
2607 Private Note | [% vii.priv_note %]
2608 OPAC Visible | [% vii.opac_visible %]
2615 INSERT INTO action_trigger.environment ( event_def, path) VALUES (
2617 ,( 47, 'record.attributes')
2618 ,( 47, 'record.queue')
2619 ,( 47, 'record.queue.owner')
2624 SELECT evergreen.upgrade_deps_block_check('0574', :eg_version);
2626 UPDATE action_trigger.event_definition SET template =
2630 table { border-collapse: collapse; }
2631 td { padding: 5px; border-bottom: 1px solid #888; }
2632 th { font-weight: bold; }
2635 # Sort the holds into copy-location buckets
2636 # In the main print loop, sort each bucket by callnumber before printing
2637 SET holds_list = [];
2639 SET current_location = target.0.current_copy.location.id;
2641 IF current_location != hold.current_copy.location.id;
2642 SET current_location = hold.current_copy.location.id;
2643 holds_list.push(loc_data);
2648 'callnumber' => hold.current_copy.call_number.label
2650 loc_data.push(hold_data);
2652 holds_list.push(loc_data)
2659 <th>Shelving Location</th>
2660 <th>Call Number</th>
2661 <th>Barcode/Part</th>
2666 [% FOR loc_data IN holds_list %]
2667 [% FOR hold_data IN loc_data.sort('callnumber') %]
2669 SET hold = hold_data.hold;
2670 SET copy_data = helpers.get_copy_bib_basics(hold.current_copy.id);
2673 <td>[% copy_data.title | truncate %]</td>
2674 <td>[% copy_data.author | truncate %]</td>
2675 <td>[% hold.current_copy.location.name %]</td>
2676 <td>[% hold.current_copy.call_number.label %]</td>
2677 <td>[% hold.current_copy.barcode %]
2678 [% FOR part IN hold.current_copy.parts %]
2679 [% part.part.label %]
2682 <td>[% hold.usr.card.barcode %]</td>
2691 INSERT INTO action_trigger.environment (
2695 (35, 'current_copy.parts'),
2696 (35, 'current_copy.parts.part')
2700 -- Evergreen DB patch XXXX.schema.authority-control-sets.sql
2702 -- Schema upgrade to add Authority Control Set functionality
2706 -- check whether patch can be applied
2707 SELECT evergreen.upgrade_deps_block_check('0575', :eg_version);
2709 CREATE TABLE authority.control_set (
2710 id SERIAL PRIMARY KEY,
2711 name TEXT NOT NULL UNIQUE, -- i18n
2712 description TEXT -- i18n
2715 CREATE TABLE authority.control_set_authority_field (
2716 id SERIAL PRIMARY KEY,
2717 main_entry INT REFERENCES authority.control_set_authority_field (id) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
2718 control_set INT NOT NULL REFERENCES authority.control_set (id) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
2719 tag CHAR(3) NOT NULL,
2721 sf_list TEXT NOT NULL,
2722 name TEXT NOT NULL, -- i18n
2723 description TEXT -- i18n
2726 CREATE TABLE authority.control_set_bib_field (
2727 id SERIAL PRIMARY KEY,
2728 authority_field INT NOT NULL REFERENCES authority.control_set_authority_field (id) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
2729 tag CHAR(3) NOT NULL
2732 CREATE TABLE authority.thesaurus (
2733 code TEXT PRIMARY KEY, -- MARC21 thesaurus code
2734 control_set INT REFERENCES authority.control_set (id) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
2735 name TEXT NOT NULL UNIQUE, -- i18n
2736 description TEXT -- i18n
2739 CREATE TABLE authority.browse_axis (
2740 code TEXT PRIMARY KEY,
2741 name TEXT UNIQUE NOT NULL, -- i18n
2742 sorter TEXT REFERENCES config.record_attr_definition (name) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
2746 CREATE TABLE authority.browse_axis_authority_field_map (
2747 id SERIAL PRIMARY KEY,
2748 axis TEXT NOT NULL REFERENCES authority.browse_axis (code) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
2749 field INT NOT NULL REFERENCES authority.control_set_authority_field (id) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED
2752 ALTER TABLE authority.record_entry ADD COLUMN control_set INT REFERENCES authority.control_set (id) ON UPDATE CASCADE DEFERRABLE INITIALLY DEFERRED;
2753 ALTER TABLE authority.rec_descriptor DROP COLUMN char_encoding, ADD COLUMN encoding_level TEXT, ADD COLUMN thesaurus TEXT;
2755 CREATE INDEX authority_full_rec_value_index ON authority.full_rec (value);
2756 CREATE OR REPLACE RULE protect_authority_rec_delete AS ON DELETE TO authority.record_entry DO INSTEAD (UPDATE authority.record_entry SET deleted = TRUE WHERE OLD.id = authority.record_entry.id; DELETE FROM authority.full_rec WHERE record = OLD.id);
2758 CREATE OR REPLACE FUNCTION authority.normalize_heading( marcxml TEXT, no_thesaurus BOOL ) RETURNS TEXT AS $func$
2760 acsaf authority.control_set_authority_field%ROWTYPE;
2768 thes_code := vandelay.marc21_extract_fixed_field(marcxml,'Subj');
2769 IF thes_code IS NULL THEN
2773 SELECT control_set INTO cset FROM authority.thesaurus WHERE code = thes_code;
2779 FOR acsaf IN SELECT * FROM authority.control_set_authority_field WHERE control_set = cset AND main_entry IS NULL LOOP
2780 tag_used := acsaf.tag;
2781 FOR sf IN SELECT * FROM regexp_split_to_table(acsaf.sf_list,'') LOOP
2782 tmp_text := oils_xpath_string('//*[@tag="'||tag_used||'"]/*[@code="'||sf||'"]', marcxml);
2783 IF tmp_text IS NOT NULL AND tmp_text <> '' THEN
2784 heading_text := heading_text || E'\u2021' || sf || ' ' || tmp_text;
2787 EXIT WHEN heading_text <> '';
2790 IF thes_code = 'z' THEN
2791 thes_code := oils_xpath_string('//*[@tag="040"]/*[@code="f"][1]', marcxml);
2794 IF heading_text <> '' THEN
2795 IF no_thesaurus IS TRUE THEN
2796 heading_text := tag_used || ' ' || public.naco_normalize(heading_text);
2798 heading_text := tag_used || '_' || thes_code || ' ' || public.naco_normalize(heading_text);
2801 heading_text := 'NOHEADING_' || thes_code || ' ' || MD5(marcxml);
2804 RETURN heading_text;
2806 $func$ LANGUAGE PLPGSQL IMMUTABLE;
2808 CREATE OR REPLACE FUNCTION authority.simple_normalize_heading( marcxml TEXT ) RETURNS TEXT AS $func$
2809 SELECT authority.normalize_heading($1, TRUE);
2810 $func$ LANGUAGE SQL IMMUTABLE;
2812 CREATE OR REPLACE FUNCTION authority.normalize_heading( marcxml TEXT ) RETURNS TEXT AS $func$
2813 SELECT authority.normalize_heading($1, FALSE);
2814 $func$ LANGUAGE SQL IMMUTABLE;
2816 CREATE OR REPLACE VIEW authority.tracing_links AS
2817 SELECT main.record AS record,
2819 main.tag AS main_tag,
2820 oils_xpath_string('//*[@tag="'||main.tag||'"]/*[local-name()="subfield"]', are.marc) AS main_value,
2821 substr(link.value,1,1) AS relationship,
2822 substr(link.value,2,1) AS use_restriction,
2823 substr(link.value,3,1) AS deprecation,
2824 substr(link.value,4,1) AS display_restriction,
2826 link.tag AS link_tag,
2827 oils_xpath_string('//*[@tag="'||link.tag||'"]/*[local-name()="subfield"]', are.marc) AS link_value,
2828 authority.normalize_heading(are.marc) AS normalized_main_value
2829 FROM authority.full_rec main
2830 JOIN authority.record_entry are ON (main.record = are.id)
2831 JOIN authority.control_set_authority_field main_entry
2832 ON (main_entry.tag = main.tag
2833 AND main_entry.main_entry IS NULL
2834 AND main.subfield = 'a' )
2835 JOIN authority.control_set_authority_field sub_entry
2836 ON (main_entry.id = sub_entry.main_entry)
2837 JOIN authority.full_rec link
2838 ON (link.record = main.record
2839 AND link.tag = sub_entry.tag
2840 AND link.subfield = 'w' );
2842 CREATE OR REPLACE FUNCTION authority.generate_overlay_template (source_xml TEXT) RETURNS TEXT AS $f$
2845 main_entry authority.control_set_authority_field%ROWTYPE;
2846 bib_field authority.control_set_bib_field%ROWTYPE;
2847 auth_id INT DEFAULT oils_xpath_string('//*[@tag="901"]/*[local-name()="subfield" and @code="c"]', source_xml)::INT;
2848 replace_data XML[] DEFAULT '{}'::XML[];
2849 replace_rules TEXT[] DEFAULT '{}'::TEXT[];
2852 IF auth_id IS NULL THEN
2856 -- Default to the LoC controll set
2857 SELECT COALESCE(control_set,1) INTO cset FROM authority.record_entry WHERE id = auth_id;
2859 FOR main_entry IN SELECT * FROM authority.control_set_authority_field WHERE control_set = cset LOOP
2860 auth_field := XPATH('//*[@tag="'||main_entry.tag||'"][1]',source_xml::XML);
2861 IF ARRAY_LENGTH(auth_field,1) > 0 THEN
2862 FOR bib_field IN SELECT * FROM authority.control_set_bib_field WHERE authority_field = main_entry.id LOOP
2863 replace_data := replace_data || XMLELEMENT( name datafield, XMLATTRIBUTES(bib_field.tag AS tag), XPATH('//*[local-name()="subfield"]',auth_field[1])::XML[]);
2864 replace_rules := replace_rules || ( bib_field.tag || main_entry.sf_list || E'[0~\\)' || auth_id || '$]' );
2872 XMLATTRIBUTES('http://www.loc.gov/MARC21/slim' AS xmlns),
2873 XMLELEMENT( name leader, '00881nam a2200193 4500'),
2877 XMLATTRIBUTES( '905' AS tag, ' ' AS ind1, ' ' AS ind2),
2880 XMLATTRIBUTES('r' AS code),
2881 ARRAY_TO_STRING(replace_rules,',')
2886 $f$ STABLE LANGUAGE PLPGSQL;
2888 CREATE OR REPLACE FUNCTION authority.generate_overlay_template ( BIGINT ) RETURNS TEXT AS $func$
2889 SELECT authority.generate_overlay_template( marc ) FROM authority.record_entry WHERE id = $1;
2890 $func$ LANGUAGE SQL;
2892 CREATE OR REPLACE FUNCTION vandelay.add_field ( target_xml TEXT, source_xml TEXT, field TEXT, force_add INT ) RETURNS TEXT AS $_$
2895 use MARC::File::XML (BinaryEncoding => 'UTF-8');
2899 MARC::Charset->assume_unicode(1);
2901 my $target_xml = shift;
2902 my $source_xml = shift;
2903 my $field_spec = shift;
2904 my $force_add = shift || 0;
2906 my $target_r = MARC::Record->new_from_xml( $target_xml );
2907 my $source_r = MARC::Record->new_from_xml( $source_xml );
2909 return $target_xml unless ($target_r && $source_r);
2911 my @field_list = split(',', $field_spec);
2914 for my $f (@field_list) {
2915 $f =~ s/^\s*//; $f =~ s/\s*$//;
2916 if ($f =~ /^(.{3})(\w*)(?:\[([^]]*)\])?$/) {
2922 $match =~ s/^\s*//; $match =~ s/\s*$//;
2923 $fields{$field} = { sf => [ split('', $sf) ] };
2925 my ($msf,$mre) = split('~', $match);
2926 if (length($msf) > 0 and length($mre) > 0) {
2927 $msf =~ s/^\s*//; $msf =~ s/\s*$//;
2928 $mre =~ s/^\s*//; $mre =~ s/\s*$//;
2929 $fields{$field}{match} = { sf => $msf, re => qr/$mre/ };
2935 for my $f ( keys %fields) {
2936 if ( @{$fields{$f}{sf}} ) {
2937 for my $from_field ($source_r->field( $f )) {
2938 my @tos = $target_r->field( $f );
2940 next if (exists($fields{$f}{match}) and !$force_add);
2941 my @new_fields = map { $_->clone } $source_r->field( $f );
2942 $target_r->insert_fields_ordered( @new_fields );
2944 for my $to_field (@tos) {
2945 if (exists($fields{$f}{match})) {
2946 next unless (grep { $_ =~ $fields{$f}{match}{re} } $to_field->subfield($fields{$f}{match}{sf}));
2948 my @new_sf = map { ($_ => $from_field->subfield($_)) } grep { defined($from_field->subfield($_)) } @{$fields{$f}{sf}};
2949 $to_field->add_subfields( @new_sf );
2954 my @new_fields = map { $_->clone } $source_r->field( $f );
2955 $target_r->insert_fields_ordered( @new_fields );
2959 $target_xml = $target_r->as_xml_record;
2960 $target_xml =~ s/^<\?.+?\?>$//mo;
2961 $target_xml =~ s/\n//sgo;
2962 $target_xml =~ s/>\s+</></sgo;
2966 $_$ LANGUAGE PLPERLU;
2969 CREATE INDEX by_heading ON authority.record_entry (authority.simple_normalize_heading(marc)) WHERE deleted IS FALSE or deleted = FALSE;
2971 INSERT INTO config.metabib_field ( id, field_class, name, label, format, xpath, search_field, facet_field) VALUES
2972 (28, 'identifier', 'authority_id', oils_i18n_gettext(28, 'Authority Record ID', 'cmf', 'label'), 'marcxml', '//marc:datafield/marc:subfield[@code="0"]', FALSE, TRUE);
2974 INSERT INTO config.marc21_rec_type_map (code, type_val, blvl_val) VALUES ('AUT','z',' ');
2975 INSERT INTO config.marc21_rec_type_map (code, type_val, blvl_val) VALUES ('MFHD','uvxy',' ');
2977 INSERT INTO config.marc21_ff_pos_map (fixed_field, tag, rec_type,start_pos, length, default_val) VALUES ('ELvl', 'ldr', 'AUT', 17, 1, ' ');
2978 INSERT INTO config.marc21_ff_pos_map (fixed_field, tag, rec_type,start_pos, length, default_val) VALUES ('Subj', '008', 'AUT', 11, 1, '|');
2979 INSERT INTO config.marc21_ff_pos_map (fixed_field, tag, rec_type,start_pos, length, default_val) VALUES ('RecStat', 'ldr', 'AUT', 5, 1, 'n');
2981 INSERT INTO config.metabib_field_index_norm_map (field,norm,pos)
2985 FROM config.metabib_field m,
2986 config.index_normalizer i
2987 WHERE i.func = 'remove_paren_substring'
2990 SELECT SETVAL('authority.control_set_id_seq'::TEXT, 100);
2991 SELECT SETVAL('authority.control_set_authority_field_id_seq'::TEXT, 1000);
2992 SELECT SETVAL('authority.control_set_bib_field_id_seq'::TEXT, 1000);
2994 INSERT INTO authority.control_set (id, name, description) VALUES (
2996 oils_i18n_gettext('1','LoC','acs','name'),
2997 oils_i18n_gettext('1','Library of Congress standard authority record control semantics','acs','description')
3000 INSERT INTO authority.control_set_authority_field (id, control_set, main_entry, tag, sf_list, name) VALUES
3003 (1, 1, NULL, '100', 'abcdefklmnopqrstvxyz', oils_i18n_gettext('1','Heading -- Personal Name','acsaf','name')),
3004 (2, 1, NULL, '110', 'abcdefgklmnoprstvxyz', oils_i18n_gettext('2','Heading -- Corporate Name','acsaf','name')),
3005 (3, 1, NULL, '111', 'acdefgklnpqstvxyz', oils_i18n_gettext('3','Heading -- Meeting Name','acsaf','name')),
3006 (4, 1, NULL, '130', 'adfgklmnoprstvxyz', oils_i18n_gettext('4','Heading -- Uniform Title','acsaf','name')),
3007 (5, 1, NULL, '150', 'abvxyz', oils_i18n_gettext('5','Heading -- Topical Term','acsaf','name')),
3008 (6, 1, NULL, '151', 'avxyz', oils_i18n_gettext('6','Heading -- Geographic Name','acsaf','name')),
3009 (7, 1, NULL, '155', 'avxyz', oils_i18n_gettext('7','Heading -- Genre/Form Term','acsaf','name')),
3010 (8, 1, NULL, '180', 'vxyz', oils_i18n_gettext('8','Heading -- General Subdivision','acsaf','name')),
3011 (9, 1, NULL, '181', 'vxyz', oils_i18n_gettext('9','Heading -- Geographic Subdivision','acsaf','name')),
3012 (10, 1, NULL, '182', 'vxyz', oils_i18n_gettext('10','Heading -- Chronological Subdivision','acsaf','name')),
3013 (11, 1, NULL, '185', 'vxyz', oils_i18n_gettext('11','Heading -- Form Subdivision','acsaf','name')),
3014 (12, 1, NULL, '148', 'avxyz', oils_i18n_gettext('12','Heading -- Chronological Term','acsaf','name')),
3016 -- See Also From tracings
3017 (21, 1, 1, '500', 'abcdefiklmnopqrstvwxyz4', oils_i18n_gettext('21','See Also From Tracing -- Personal Name','acsaf','name')),
3018 (22, 1, 2, '510', 'abcdefgiklmnoprstvwxyz4', oils_i18n_gettext('22','See Also From Tracing -- Corporate Name','acsaf','name')),
3019 (23, 1, 3, '511', 'acdefgiklnpqstvwxyz4', oils_i18n_gettext('23','See Also From Tracing -- Meeting Name','acsaf','name')),
3020 (24, 1, 4, '530', 'adfgiklmnoprstvwxyz4', oils_i18n_gettext('24','See Also From Tracing -- Uniform Title','acsaf','name')),
3021 (25, 1, 5, '550', 'abivwxyz4', oils_i18n_gettext('25','See Also From Tracing -- Topical Term','acsaf','name')),
3022 (26, 1, 6, '551', 'aivwxyz4', oils_i18n_gettext('26','See Also From Tracing -- Geographic Name','acsaf','name')),
3023 (27, 1, 7, '555', 'aivwxyz4', oils_i18n_gettext('27','See Also From Tracing -- Genre/Form Term','acsaf','name')),
3024 (28, 1, 8, '580', 'ivwxyz4', oils_i18n_gettext('28','See Also From Tracing -- General Subdivision','acsaf','name')),
3025 (29, 1, 9, '581', 'ivwxyz4', oils_i18n_gettext('29','See Also From Tracing -- Geographic Subdivision','acsaf','name')),
3026 (30, 1, 10, '582', 'ivwxyz4', oils_i18n_gettext('30','See Also From Tracing -- Chronological Subdivision','acsaf','name')),
3027 (31, 1, 11, '585', 'ivwxyz4', oils_i18n_gettext('31','See Also From Tracing -- Form Subdivision','acsaf','name')),
3028 (32, 1, 12, '548', 'aivwxyz4', oils_i18n_gettext('32','See Also From Tracing -- Chronological Term','acsaf','name')),
3031 (41, 1, 1, '700', 'abcdefghjklmnopqrstvwxyz25', oils_i18n_gettext('41','Established Heading Linking Entry -- Personal Name','acsaf','name')),
3032 (42, 1, 2, '710', 'abcdefghklmnoprstvwxyz25', oils_i18n_gettext('42','Established Heading Linking Entry -- Corporate Name','acsaf','name')),
3033 (43, 1, 3, '711', 'acdefghklnpqstvwxyz25', oils_i18n_gettext('43','Established Heading Linking Entry -- Meeting Name','acsaf','name')),
3034 (44, 1, 4, '730', 'adfghklmnoprstvwxyz25', oils_i18n_gettext('44','Established Heading Linking Entry -- Uniform Title','acsaf','name')),
3035 (45, 1, 5, '750', 'abvwxyz25', oils_i18n_gettext('45','Established Heading Linking Entry -- Topical Term','acsaf','name')),
3036 (46, 1, 6, '751', 'avwxyz25', oils_i18n_gettext('46','Established Heading Linking Entry -- Geographic Name','acsaf','name')),
3037 (47, 1, 7, '755', 'avwxyz25', oils_i18n_gettext('47','Established Heading Linking Entry -- Genre/Form Term','acsaf','name')),
3038 (48, 1, 8, '780', 'vwxyz25', oils_i18n_gettext('48','Subdivision Linking Entry -- General Subdivision','acsaf','name')),
3039 (49, 1, 9, '781', 'vwxyz25', oils_i18n_gettext('49','Subdivision Linking Entry -- Geographic Subdivision','acsaf','name')),
3040 (50, 1, 10, '782', 'vwxyz25', oils_i18n_gettext('50','Subdivision Linking Entry -- Chronological Subdivision','acsaf','name')),
3041 (51, 1, 11, '785', 'vwxyz25', oils_i18n_gettext('51','Subdivision Linking Entry -- Form Subdivision','acsaf','name')),
3042 (52, 1, 12, '748', 'avwxyz25', oils_i18n_gettext('52','Established Heading Linking Entry -- Chronological Term','acsaf','name')),
3044 -- See From tracings
3045 (61, 1, 1, '400', 'abcdefiklmnopqrstvwxyz4', oils_i18n_gettext('61','See Also Tracing -- Personal Name','acsaf','name')),
3046 (62, 1, 2, '410', 'abcdefgiklmnoprstvwxyz4', oils_i18n_gettext('62','See Also Tracing -- Corporate Name','acsaf','name')),
3047 (63, 1, 3, '411', 'acdefgiklnpqstvwxyz4', oils_i18n_gettext('63','See Also Tracing -- Meeting Name','acsaf','name')),
3048 (64, 1, 4, '430', 'adfgiklmnoprstvwxyz4', oils_i18n_gettext('64','See Also Tracing -- Uniform Title','acsaf','name')),
3049 (65, 1, 5, '450', 'abivwxyz4', oils_i18n_gettext('65','See Also Tracing -- Topical Term','acsaf','name')),
3050 (66, 1, 6, '451', 'aivwxyz4', oils_i18n_gettext('66','See Also Tracing -- Geographic Name','acsaf','name')),
3051 (67, 1, 7, '455', 'aivwxyz4', oils_i18n_gettext('67','See Also Tracing -- Genre/Form Term','acsaf','name')),
3052 (68, 1, 8, '480', 'ivwxyz4', oils_i18n_gettext('68','See Also Tracing -- General Subdivision','acsaf','name')),
3053 (69, 1, 9, '481', 'ivwxyz4', oils_i18n_gettext('69','See Also Tracing -- Geographic Subdivision','acsaf','name')),
3054 (70, 1, 10, '482', 'ivwxyz4', oils_i18n_gettext('70','See Also Tracing -- Chronological Subdivision','acsaf','name')),
3055 (71, 1, 11, '485', 'ivwxyz4', oils_i18n_gettext('71','See Also Tracing -- Form Subdivision','acsaf','name')),
3056 (72, 1, 12, '448', 'aivwxyz4', oils_i18n_gettext('72','See Also Tracing -- Chronological Term','acsaf','name'));
3058 INSERT INTO authority.browse_axis (code,name,description,sorter) VALUES
3059 ('title','Title','Title axis','titlesort'),
3060 ('author','Author','Author axis','titlesort'),
3061 ('subject','Subject','Subject axis','titlesort'),
3062 ('topic','Topic','Topic Subject axis','titlesort');
3064 INSERT INTO authority.browse_axis_authority_field_map (axis,field) VALUES
3075 INSERT INTO authority.control_set_bib_field (tag, authority_field)
3076 SELECT '100', id FROM authority.control_set_authority_field WHERE tag IN ('100')
3078 SELECT '600', id FROM authority.control_set_authority_field WHERE tag IN ('100','180','181','182','185')
3080 SELECT '700', id FROM authority.control_set_authority_field WHERE tag IN ('100')
3082 SELECT '800', id FROM authority.control_set_authority_field WHERE tag IN ('100')
3085 SELECT '110', id FROM authority.control_set_authority_field WHERE tag IN ('110')
3087 SELECT '610', id FROM authority.control_set_authority_field WHERE tag IN ('110')
3089 SELECT '710', id FROM authority.control_set_authority_field WHERE tag IN ('110')
3091 SELECT '810', id FROM authority.control_set_authority_field WHERE tag IN ('110')
3094 SELECT '111', id FROM authority.control_set_authority_field WHERE tag IN ('111')
3096 SELECT '611', id FROM authority.control_set_authority_field WHERE tag IN ('111')
3098 SELECT '711', id FROM authority.control_set_authority_field WHERE tag IN ('111')
3100 SELECT '811', id FROM authority.control_set_authority_field WHERE tag IN ('111')
3103 SELECT '130', id FROM authority.control_set_authority_field WHERE tag IN ('130')
3105 SELECT '240', id FROM authority.control_set_authority_field WHERE tag IN ('130')
3107 SELECT '630', id FROM authority.control_set_authority_field WHERE tag IN ('130')
3109 SELECT '730', id FROM authority.control_set_authority_field WHERE tag IN ('130')
3111 SELECT '830', id FROM authority.control_set_authority_field WHERE tag IN ('130')
3114 SELECT '648', id FROM authority.control_set_authority_field WHERE tag IN ('148')
3117 SELECT '650', id FROM authority.control_set_authority_field WHERE tag IN ('150','180','181','182','185')
3119 SELECT '651', id FROM authority.control_set_authority_field WHERE tag IN ('151','180','181','182','185')
3121 SELECT '655', id FROM authority.control_set_authority_field WHERE tag IN ('155','180','181','182','185')
3124 INSERT INTO authority.thesaurus (code, name, control_set) VALUES
3125 ('a', oils_i18n_gettext('a','Library of Congress Subject Headings','at','name'), 1),
3126 ('b', oils_i18n_gettext('b',$$LC subject headings for children's literature$$,'at','name'), 1), -- silly vim '
3127 ('c', oils_i18n_gettext('c','Medical Subject Headings','at','name'), 1),
3128 ('d', oils_i18n_gettext('d','National Agricultural Library subject authority file','at','name'), 1),
3129 ('k', oils_i18n_gettext('k','Canadian Subject Headings','at','name'), 1),
3130 ('n', oils_i18n_gettext('n','Not applicable','at','name'), 1),
3131 ('r', oils_i18n_gettext('r','Art and Architecture Thesaurus','at','name'), 1),
3132 ('s', oils_i18n_gettext('s','Sears List of Subject Headings','at','name'), 1),
3133 ('v', oils_i18n_gettext('v','Repertoire de vedettes-matiere','at','name'), 1),
3134 ('z', oils_i18n_gettext('z','Other','at','name'), 1),
3135 ('|', oils_i18n_gettext('|','No attempt to code','at','name'), 1);
3137 CREATE OR REPLACE FUNCTION authority.map_thesaurus_to_control_set () RETURNS TRIGGER AS $func$
3139 IF NEW.control_set IS NULL THEN
3140 SELECT control_set INTO NEW.control_set
3141 FROM authority.thesaurus
3142 WHERE vandelay.marc21_extract_fixed_field(NEW.marc,'Subj') = code;
3147 $func$ LANGUAGE PLPGSQL;
3149 CREATE TRIGGER map_thesaurus_to_control_set BEFORE INSERT OR UPDATE ON authority.record_entry FOR EACH ROW EXECUTE PROCEDURE authority.map_thesaurus_to_control_set ();
3151 CREATE OR REPLACE FUNCTION authority.reingest_authority_rec_descriptor( auth_id BIGINT ) RETURNS VOID AS $func$
3153 DELETE FROM authority.rec_descriptor WHERE record = auth_id;
3154 INSERT INTO authority.rec_descriptor (record, record_status, encoding_level, thesaurus)
3156 vandelay.marc21_extract_fixed_field(marc,'RecStat'),
3157 vandelay.marc21_extract_fixed_field(marc,'ELvl'),
3158 vandelay.marc21_extract_fixed_field(marc,'Subj')
3159 FROM authority.record_entry
3163 $func$ LANGUAGE PLPGSQL;
3165 --Removed dupe authority.indexing_ingest_or_delete
3167 -- Evergreen DB patch 0577.schema.vandelay-item-import-copy-loc-ancestors.sql
3169 -- Ingest items copy location inheritance
3172 -- check whether patch can be applied
3173 SELECT evergreen.upgrade_deps_block_check('0577', :eg_version); -- berick
3175 CREATE OR REPLACE FUNCTION vandelay.ingest_items ( import_id BIGINT, attr_def_id BIGINT ) RETURNS SETOF vandelay.import_item AS $$
3186 deposit_amount TEXT;
3199 tmp_attr_set RECORD;
3200 attr_set vandelay.import_item%ROWTYPE;
3206 SELECT * INTO attr_def FROM vandelay.import_item_attr_definition WHERE id = attr_def_id;
3210 attr_set.definition := attr_def.id;
3212 -- Build the combined XPath
3216 WHEN attr_def.owning_lib IS NULL THEN 'null()'
3217 WHEN LENGTH( attr_def.owning_lib ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.owning_lib || '"]'
3218 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.owning_lib
3223 WHEN attr_def.circ_lib IS NULL THEN 'null()'
3224 WHEN LENGTH( attr_def.circ_lib ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.circ_lib || '"]'
3225 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.circ_lib
3230 WHEN attr_def.call_number IS NULL THEN 'null()'
3231 WHEN LENGTH( attr_def.call_number ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.call_number || '"]'
3232 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.call_number
3237 WHEN attr_def.copy_number IS NULL THEN 'null()'
3238 WHEN LENGTH( attr_def.copy_number ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.copy_number || '"]'
3239 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.copy_number
3244 WHEN attr_def.status IS NULL THEN 'null()'
3245 WHEN LENGTH( attr_def.status ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.status || '"]'
3246 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.status
3251 WHEN attr_def.location IS NULL THEN 'null()'
3252 WHEN LENGTH( attr_def.location ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.location || '"]'
3253 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.location
3258 WHEN attr_def.circulate IS NULL THEN 'null()'
3259 WHEN LENGTH( attr_def.circulate ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.circulate || '"]'
3260 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.circulate
3265 WHEN attr_def.deposit IS NULL THEN 'null()'
3266 WHEN LENGTH( attr_def.deposit ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.deposit || '"]'
3267 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.deposit
3272 WHEN attr_def.deposit_amount IS NULL THEN 'null()'
3273 WHEN LENGTH( attr_def.deposit_amount ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.deposit_amount || '"]'
3274 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.deposit_amount
3279 WHEN attr_def.ref IS NULL THEN 'null()'
3280 WHEN LENGTH( attr_def.ref ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.ref || '"]'
3281 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.ref
3286 WHEN attr_def.holdable IS NULL THEN 'null()'
3287 WHEN LENGTH( attr_def.holdable ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.holdable || '"]'
3288 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.holdable
3293 WHEN attr_def.price IS NULL THEN 'null()'
3294 WHEN LENGTH( attr_def.price ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.price || '"]'
3295 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.price
3300 WHEN attr_def.barcode IS NULL THEN 'null()'
3301 WHEN LENGTH( attr_def.barcode ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.barcode || '"]'
3302 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.barcode
3307 WHEN attr_def.circ_modifier IS NULL THEN 'null()'
3308 WHEN LENGTH( attr_def.circ_modifier ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.circ_modifier || '"]'
3309 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.circ_modifier
3314 WHEN attr_def.circ_as_type IS NULL THEN 'null()'
3315 WHEN LENGTH( attr_def.circ_as_type ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.circ_as_type || '"]'
3316 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.circ_as_type
3321 WHEN attr_def.alert_message IS NULL THEN 'null()'
3322 WHEN LENGTH( attr_def.alert_message ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.alert_message || '"]'
3323 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.alert_message
3328 WHEN attr_def.opac_visible IS NULL THEN 'null()'
3329 WHEN LENGTH( attr_def.opac_visible ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.opac_visible || '"]'
3330 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.opac_visible
3335 WHEN attr_def.pub_note IS NULL THEN 'null()'
3336 WHEN LENGTH( attr_def.pub_note ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.pub_note || '"]'
3337 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.pub_note
3341 WHEN attr_def.priv_note IS NULL THEN 'null()'
3342 WHEN LENGTH( attr_def.priv_note ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.priv_note || '"]'
3343 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.priv_note
3348 owning_lib || '|' ||
3350 call_number || '|' ||
3351 copy_number || '|' ||
3356 deposit_amount || '|' ||
3361 circ_modifier || '|' ||
3362 circ_as_type || '|' ||
3363 alert_message || '|' ||
3368 -- RAISE NOTICE 'XPath: %', xpath;
3372 FROM oils_xpath_table( 'id', 'marc', 'vandelay.queued_bib_record', xpath, 'id = ' || import_id )
3373 AS t( id INT, ol TEXT, clib TEXT, cn TEXT, cnum TEXT, cs TEXT, cl TEXT, circ TEXT,
3374 dep TEXT, dep_amount TEXT, r TEXT, hold TEXT, pr TEXT, bc TEXT, circ_mod TEXT,
3375 circ_as TEXT, amessage TEXT, note TEXT, pnote TEXT, opac_vis TEXT )
3378 tmp_attr_set.pr = REGEXP_REPLACE(tmp_attr_set.pr, E'[^0-9\\.]', '', 'g');
3379 tmp_attr_set.dep_amount = REGEXP_REPLACE(tmp_attr_set.dep_amount, E'[^0-9\\.]', '', 'g');
3381 tmp_attr_set.pr := NULLIF( tmp_attr_set.pr, '' );
3382 tmp_attr_set.dep_amount := NULLIF( tmp_attr_set.dep_amount, '' );
3384 SELECT id INTO attr_set.owning_lib FROM actor.org_unit WHERE shortname = UPPER(tmp_attr_set.ol); -- INT
3385 SELECT id INTO attr_set.circ_lib FROM actor.org_unit WHERE shortname = UPPER(tmp_attr_set.clib); -- INT
3386 SELECT id INTO attr_set.status FROM config.copy_status WHERE LOWER(name) = LOWER(tmp_attr_set.cs); -- INT
3389 -- search up the org unit tree for a matching copy location
3391 WITH RECURSIVE anscestor_depth AS (
3395 FROM actor.org_unit ou
3396 JOIN actor.org_unit_type out ON (out.id = ou.ou_type)
3397 WHERE ou.id = COALESCE(attr_set.owning_lib, attr_set.circ_lib)
3402 FROM actor.org_unit ou
3403 JOIN actor.org_unit_type out ON (out.id = ou.ou_type)
3404 JOIN anscestor_depth ot ON (ot.parent_ou = ou.id)
3405 ) SELECT cpl.id INTO attr_set.location
3406 FROM anscestor_depth a
3407 JOIN asset.copy_location cpl ON (cpl.owning_lib = a.id)
3408 WHERE LOWER(cpl.name) = LOWER(tmp_attr_set.cl)
3409 ORDER BY a.depth DESC
3412 attr_set.circulate :=
3413 LOWER( SUBSTRING( tmp_attr_set.circ, 1, 1)) IN ('t','y','1')
3414 OR LOWER(tmp_attr_set.circ) = 'circulating'; -- BOOL
3417 LOWER( SUBSTRING( tmp_attr_set.dep, 1, 1 ) ) IN ('t','y','1')
3418 OR LOWER(tmp_attr_set.dep) = 'deposit'; -- BOOL
3420 attr_set.holdable :=
3421 LOWER( SUBSTRING( tmp_attr_set.hold, 1, 1 ) ) IN ('t','y','1')
3422 OR LOWER(tmp_attr_set.hold) = 'holdable'; -- BOOL
3424 attr_set.opac_visible :=
3425 LOWER( SUBSTRING( tmp_attr_set.opac_vis, 1, 1 ) ) IN ('t','y','1')
3426 OR LOWER(tmp_attr_set.opac_vis) = 'visible'; -- BOOL
3429 LOWER( SUBSTRING( tmp_attr_set.r, 1, 1 ) ) IN ('t','y','1')
3430 OR LOWER(tmp_attr_set.r) = 'reference'; -- BOOL
3432 attr_set.copy_number := tmp_attr_set.cnum::INT; -- INT,
3433 attr_set.deposit_amount := tmp_attr_set.dep_amount::NUMERIC(6,2); -- NUMERIC(6,2),
3434 attr_set.price := tmp_attr_set.pr::NUMERIC(8,2); -- NUMERIC(8,2),
3436 attr_set.call_number := tmp_attr_set.cn; -- TEXT
3437 attr_set.barcode := tmp_attr_set.bc; -- TEXT,
3438 attr_set.circ_modifier := tmp_attr_set.circ_mod; -- TEXT,
3439 attr_set.circ_as_type := tmp_attr_set.circ_as; -- TEXT,
3440 attr_set.alert_message := tmp_attr_set.amessage; -- TEXT,
3441 attr_set.pub_note := tmp_attr_set.note; -- TEXT,
3442 attr_set.priv_note := tmp_attr_set.pnote; -- TEXT,
3443 attr_set.alert_message := tmp_attr_set.amessage; -- TEXT,
3445 RETURN NEXT attr_set;
3454 $$ LANGUAGE PLPGSQL;
3457 -- Evergreen DB patch XXXX.data.org-setting-ui.circ.billing.uncheck_bills_and_unfocus_payment_box.sql
3459 -- New org setting ui.circ.billing.uncheck_bills_and_unfocus_payment_box
3462 -- check whether patch can be applied
3463 SELECT evergreen.upgrade_deps_block_check('0584', :eg_version);
3465 INSERT INTO config.org_unit_setting_type ( name, label, description, datatype )
3467 'ui.circ.billing.uncheck_bills_and_unfocus_payment_box',
3469 'ui.circ.billing.uncheck_bills_and_unfocus_payment_box',
3470 'GUI: Uncheck bills by default in the patron billing interface',
3475 'ui.circ.billing.uncheck_bills_and_unfocus_payment_box',
3476 'Uncheck bills by default in the patron billing interface,'
3477 || ' and focus on the Uncheck All button instead of the'
3478 || ' Payment Received field.',
3486 -- check whether patch can be applied
3487 SELECT evergreen.upgrade_deps_block_check('0585', :eg_version);
3489 INSERT into config.org_unit_setting_type
3490 ( name, label, description, datatype ) VALUES
3491 ( 'circ.checkout_fills_related_hold_exact_match_only',
3492 'Checkout Fills Related Hold On Valid Copy Only',
3493 'When filling related holds on checkout only match on items that are valid for opportunistic capture for the hold. Without this set a Title or Volume hold could match when the item is not holdable. With this set only holdable items will match.',
3497 -- check whether patch can be applied
3498 SELECT evergreen.upgrade_deps_block_check('0586', :eg_version);
3500 INSERT INTO permission.perm_list (id, code, description) VALUES (
3505 'Allows a user to authenticate and get a long-lived session (length configured in opensrf.xml)',
3511 INSERT INTO permission.grp_perm_map (grp, perm, depth, grantable)
3513 pgt.id, perm.id, aout.depth, FALSE
3515 permission.grp_tree pgt,
3516 permission.perm_list perm,
3517 actor.org_unit_type aout
3519 pgt.name = 'Users' AND
3520 aout.name = 'Consortium' AND
3521 perm.code = 'PERSISTENT_LOGIN';
3524 \qecho If this transaction succeeded, your users (staff and patrons) now have
3525 \qecho the PERSISTENT_LOGIN permission by default.
3529 -- Evergreen DB patch XXXX.data.org-setting-circ.offline.skip_foo_if_newer_status_changed_time.sql
3531 -- New org setting circ.offline.skip_checkout_if_newer_status_changed_time
3532 -- New org setting circ.offline.skip_renew_if_newer_status_changed_time
3533 -- New org setting circ.offline.skip_checkin_if_newer_status_changed_time
3536 -- check whether patch can be applied
3537 SELECT evergreen.upgrade_deps_block_check('0593', :eg_version);
3539 INSERT INTO config.org_unit_setting_type ( name, label, description, datatype )
3541 'circ.offline.skip_checkout_if_newer_status_changed_time',
3543 'circ.offline.skip_checkout_if_newer_status_changed_time',
3544 'Offline: Skip offline checkout if newer item Status Changed Time.',
3549 'circ.offline.skip_checkout_if_newer_status_changed_time',
3550 'Skip offline checkout transaction (raise exception when'
3551 || ' processing) if item Status Changed Time is newer than the'
3552 || ' recorded transaction time. WARNING: The Reshelving to'
3553 || ' Available status rollover will trigger this.',
3559 'circ.offline.skip_renew_if_newer_status_changed_time',
3561 'circ.offline.skip_renew_if_newer_status_changed_time',
3562 'Offline: Skip offline renewal if newer item Status Changed Time.',
3567 'circ.offline.skip_renew_if_newer_status_changed_time',
3568 'Skip offline renewal transaction (raise exception when'
3569 || ' processing) if item Status Changed Time is newer than the'
3570 || ' recorded transaction time. WARNING: The Reshelving to'
3571 || ' Available status rollover will trigger this.',
3577 'circ.offline.skip_checkin_if_newer_status_changed_time',
3579 'circ.offline.skip_checkin_if_newer_status_changed_time',
3580 'Offline: Skip offline checkin if newer item Status Changed Time.',
3585 'circ.offline.skip_checkin_if_newer_status_changed_time',
3586 'Skip offline checkin transaction (raise exception when'
3587 || ' processing) if item Status Changed Time is newer than the'
3588 || ' recorded transaction time. WARNING: The Reshelving to'
3589 || ' Available status rollover will trigger this.',
3596 -- Evergreen DB patch YYYY.schema.acp_status_date_changed.sql
3598 -- Change trigger which updates copy status_changed_time to ignore the
3599 -- Reshelving->Available status rollover
3601 -- FIXME: 0039.schema.acp_status_date_changed.sql defines this the first time
3602 -- around, but along with the column itself, etc. And it gets modified with
3603 -- 0562.schema.copy_active_date.sql. Not sure how to use the supercedes /
3604 -- deprecate stuff for upgrade scripts, if it's even applicable when a given
3605 -- upgrade script is doing so much.
3607 -- check whether patch can be applied
3608 SELECT evergreen.upgrade_deps_block_check('0594', :eg_version);
3610 CREATE OR REPLACE FUNCTION asset.acp_status_changed()
3611 RETURNS TRIGGER AS $$
3613 IF NEW.status <> OLD.status AND NOT (NEW.status = 0 AND OLD.status = 7) THEN
3614 NEW.status_changed_time := now();
3615 IF NEW.active_date IS NULL AND NEW.status IN (SELECT id FROM config.copy_status WHERE copy_active = true) THEN
3616 NEW.active_date := now();
3621 $$ LANGUAGE plpgsql;
3623 -- Evergreen DB patch 0595.data.org-setting-ui.patron_search.result_cap.sql
3625 -- New org setting ui.patron_search.result_cap
3628 -- check whether patch can be applied
3629 SELECT evergreen.upgrade_deps_block_check('0595', :eg_version);
3631 INSERT INTO config.org_unit_setting_type ( name, label, description, datatype )
3633 'ui.patron_search.result_cap',
3635 'ui.patron_search.result_cap',
3636 'GUI: Cap results in Patron Search at this number.',
3641 'ui.patron_search.result_cap',
3642 'So for example, if you search for John Doe, normally you would get'
3643 || ' at most 50 results. This setting allows you to raise or lower'
3651 -- Evergreen DB patch 0596.schema.vandelay-item-import-error-detail.sql
3653 -- check whether patch can be applied
3654 SELECT evergreen.upgrade_deps_block_check('0596', :eg_version);
3656 INSERT INTO vandelay.import_error ( code, description ) VALUES (
3657 'import.item.invalid.status', oils_i18n_gettext('import.item.invalid.status', 'Invalid value for "status"', 'vie', 'description') );
3658 INSERT INTO vandelay.import_error ( code, description ) VALUES (
3659 'import.item.invalid.price', oils_i18n_gettext('import.item.invalid.price', 'Invalid value for "price"', 'vie', 'description') );
3660 INSERT INTO vandelay.import_error ( code, description ) VALUES (
3661 'import.item.invalid.deposit_amount', oils_i18n_gettext('import.item.invalid.deposit_amount', 'Invalid value for "deposit_amount"', 'vie', 'description') );
3662 INSERT INTO vandelay.import_error ( code, description ) VALUES (
3663 'import.item.invalid.owning_lib', oils_i18n_gettext('import.item.invalid.owning_lib', 'Invalid value for "owning_lib"', 'vie', 'description') );
3664 INSERT INTO vandelay.import_error ( code, description ) VALUES (
3665 'import.item.invalid.circ_lib', oils_i18n_gettext('import.item.invalid.circ_lib', 'Invalid value for "circ_lib"', 'vie', 'description') );
3666 INSERT INTO vandelay.import_error ( code, description ) VALUES (
3667 'import.item.invalid.copy_number', oils_i18n_gettext('import.item.invalid.copy_number', 'Invalid value for "copy_number"', 'vie', 'description') );
3668 INSERT INTO vandelay.import_error ( code, description ) VALUES (
3669 'import.item.invalid.circ_as_type', oils_i18n_gettext('import.item.invalid.circ_as_type', 'Invalid value for "circ_as_type"', 'vie', 'description') );
3671 CREATE OR REPLACE FUNCTION vandelay.ingest_items ( import_id BIGINT, attr_def_id BIGINT ) RETURNS SETOF vandelay.import_item AS $$
3682 deposit_amount TEXT;
3695 tmp_attr_set RECORD;
3696 attr_set vandelay.import_item%ROWTYPE;
3703 SELECT * INTO attr_def FROM vandelay.import_item_attr_definition WHERE id = attr_def_id;
3707 attr_set.definition := attr_def.id;
3709 -- Build the combined XPath
3713 WHEN attr_def.owning_lib IS NULL THEN 'null()'
3714 WHEN LENGTH( attr_def.owning_lib ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.owning_lib || '"]'
3715 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.owning_lib
3720 WHEN attr_def.circ_lib IS NULL THEN 'null()'
3721 WHEN LENGTH( attr_def.circ_lib ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.circ_lib || '"]'
3722 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.circ_lib
3727 WHEN attr_def.call_number IS NULL THEN 'null()'
3728 WHEN LENGTH( attr_def.call_number ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.call_number || '"]'
3729 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.call_number
3734 WHEN attr_def.copy_number IS NULL THEN 'null()'
3735 WHEN LENGTH( attr_def.copy_number ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.copy_number || '"]'
3736 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.copy_number
3741 WHEN attr_def.status IS NULL THEN 'null()'
3742 WHEN LENGTH( attr_def.status ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.status || '"]'
3743 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.status
3748 WHEN attr_def.location IS NULL THEN 'null()'
3749 WHEN LENGTH( attr_def.location ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.location || '"]'
3750 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.location
3755 WHEN attr_def.circulate IS NULL THEN 'null()'
3756 WHEN LENGTH( attr_def.circulate ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.circulate || '"]'
3757 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.circulate
3762 WHEN attr_def.deposit IS NULL THEN 'null()'
3763 WHEN LENGTH( attr_def.deposit ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.deposit || '"]'
3764 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.deposit
3769 WHEN attr_def.deposit_amount IS NULL THEN 'null()'
3770 WHEN LENGTH( attr_def.deposit_amount ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.deposit_amount || '"]'
3771 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.deposit_amount
3776 WHEN attr_def.ref IS NULL THEN 'null()'
3777 WHEN LENGTH( attr_def.ref ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.ref || '"]'
3778 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.ref
3783 WHEN attr_def.holdable IS NULL THEN 'null()'
3784 WHEN LENGTH( attr_def.holdable ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.holdable || '"]'
3785 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.holdable
3790 WHEN attr_def.price IS NULL THEN 'null()'
3791 WHEN LENGTH( attr_def.price ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.price || '"]'
3792 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.price
3797 WHEN attr_def.barcode IS NULL THEN 'null()'
3798 WHEN LENGTH( attr_def.barcode ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.barcode || '"]'
3799 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.barcode
3804 WHEN attr_def.circ_modifier IS NULL THEN 'null()'
3805 WHEN LENGTH( attr_def.circ_modifier ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.circ_modifier || '"]'
3806 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.circ_modifier
3811 WHEN attr_def.circ_as_type IS NULL THEN 'null()'
3812 WHEN LENGTH( attr_def.circ_as_type ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.circ_as_type || '"]'
3813 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.circ_as_type
3818 WHEN attr_def.alert_message IS NULL THEN 'null()'
3819 WHEN LENGTH( attr_def.alert_message ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.alert_message || '"]'
3820 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.alert_message
3825 WHEN attr_def.opac_visible IS NULL THEN 'null()'
3826 WHEN LENGTH( attr_def.opac_visible ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.opac_visible || '"]'
3827 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.opac_visible
3832 WHEN attr_def.pub_note IS NULL THEN 'null()'
3833 WHEN LENGTH( attr_def.pub_note ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.pub_note || '"]'
3834 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.pub_note
3838 WHEN attr_def.priv_note IS NULL THEN 'null()'
3839 WHEN LENGTH( attr_def.priv_note ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.priv_note || '"]'
3840 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.priv_note
3845 owning_lib || '|' ||
3847 call_number || '|' ||
3848 copy_number || '|' ||
3853 deposit_amount || '|' ||
3858 circ_modifier || '|' ||
3859 circ_as_type || '|' ||
3860 alert_message || '|' ||
3867 FROM oils_xpath_table( 'id', 'marc', 'vandelay.queued_bib_record', xpath, 'id = ' || import_id )
3868 AS t( id INT, ol TEXT, clib TEXT, cn TEXT, cnum TEXT, cs TEXT, cl TEXT, circ TEXT,
3869 dep TEXT, dep_amount TEXT, r TEXT, hold TEXT, pr TEXT, bc TEXT, circ_mod TEXT,
3870 circ_as TEXT, amessage TEXT, note TEXT, pnote TEXT, opac_vis TEXT )
3873 attr_set.import_error := NULL;
3874 attr_set.error_detail := NULL;
3875 attr_set.deposit_amount := NULL;
3876 attr_set.copy_number := NULL;
3877 attr_set.price := NULL;
3879 IF tmp_attr_set.pr != '' THEN
3880 tmp_str = REGEXP_REPLACE(tmp_attr_set.pr, E'[^0-9\\.]', '', 'g');
3881 IF tmp_str = '' THEN
3882 attr_set.import_error := 'import.item.invalid.price';
3883 attr_set.error_detail := tmp_attr_set.pr; -- original value
3884 RETURN NEXT attr_set; CONTINUE;
3886 attr_set.price := tmp_str::NUMERIC(8,2);
3889 IF tmp_attr_set.dep_amount != '' THEN
3890 tmp_str = REGEXP_REPLACE(tmp_attr_set.dep_amount, E'[^0-9\\.]', '', 'g');
3891 IF tmp_str = '' THEN
3892 attr_set.import_error := 'import.item.invalid.deposit_amount';
3893 attr_set.error_detail := tmp_attr_set.dep_amount;
3894 RETURN NEXT attr_set; CONTINUE;
3896 attr_set.deposit_amount := tmp_str::NUMERIC(8,2);
3899 IF tmp_attr_set.cnum != '' THEN
3900 tmp_str = REGEXP_REPLACE(tmp_attr_set.cnum, E'[^0-9]', '', 'g');
3901 IF tmp_str = '' THEN
3902 attr_set.import_error := 'import.item.invalid.copy_number';
3903 attr_set.error_detail := tmp_attr_set.cnum;
3904 RETURN NEXT attr_set; CONTINUE;
3906 attr_set.copy_number := tmp_str::INT;
3909 IF tmp_attr_set.ol != '' THEN
3910 SELECT id INTO attr_set.owning_lib FROM actor.org_unit WHERE shortname = UPPER(tmp_attr_set.ol); -- INT
3912 attr_set.import_error := 'import.item.invalid.owning_lib';
3913 attr_set.error_detail := tmp_attr_set.ol;
3914 RETURN NEXT attr_set; CONTINUE;
3918 IF tmp_attr_set.clib != '' THEN
3919 SELECT id INTO attr_set.circ_lib FROM actor.org_unit WHERE shortname = UPPER(tmp_attr_set.clib); -- INT
3921 attr_set.import_error := 'import.item.invalid.circ_lib';
3922 attr_set.error_detail := tmp_attr_set.clib;
3923 RETURN NEXT attr_set; CONTINUE;
3927 IF tmp_attr_set.cs != '' THEN
3928 SELECT id INTO attr_set.status FROM config.copy_status WHERE LOWER(name) = LOWER(tmp_attr_set.cs); -- INT
3930 attr_set.import_error := 'import.item.invalid.status';
3931 attr_set.error_detail := tmp_attr_set.cs;
3932 RETURN NEXT attr_set; CONTINUE;
3936 IF tmp_attr_set.circ_mod != '' THEN
3937 SELECT code INTO attr_set.circ_modifier FROM config.circ_modifier WHERE code = tmp_attr_set.circ_mod;
3939 attr_set.import_error := 'import.item.invalid.circ_modifier';
3940 attr_set.error_detail := tmp_attr_set.circ_mod;
3941 RETURN NEXT attr_set; CONTINUE;
3945 IF tmp_attr_set.circ_as != '' THEN
3946 SELECT code INTO attr_set.circ_as_type FROM config.coded_value_map WHERE ctype = 'item_type' AND code = tmp_attr_set.circ_as;
3948 attr_set.import_error := 'import.item.invalid.circ_as_type';
3949 attr_set.error_detail := tmp_attr_set.circ_as;
3950 RETURN NEXT attr_set; CONTINUE;
3954 IF tmp_attr_set.cl != '' THEN
3956 -- search up the org unit tree for a matching copy location
3957 WITH RECURSIVE anscestor_depth AS (
3961 FROM actor.org_unit ou
3962 JOIN actor.org_unit_type out ON (out.id = ou.ou_type)
3963 WHERE ou.id = COALESCE(attr_set.owning_lib, attr_set.circ_lib)
3968 FROM actor.org_unit ou
3969 JOIN actor.org_unit_type out ON (out.id = ou.ou_type)
3970 JOIN anscestor_depth ot ON (ot.parent_ou = ou.id)
3971 ) SELECT cpl.id INTO attr_set.location
3972 FROM anscestor_depth a
3973 JOIN asset.copy_location cpl ON (cpl.owning_lib = a.id)
3974 WHERE LOWER(cpl.name) = LOWER(tmp_attr_set.cl)
3975 ORDER BY a.depth DESC
3979 attr_set.import_error := 'import.item.invalid.location';
3980 attr_set.error_detail := tmp_attr_set.cs;
3981 RETURN NEXT attr_set; CONTINUE;
3985 attr_set.circulate :=
3986 LOWER( SUBSTRING( tmp_attr_set.circ, 1, 1)) IN ('t','y','1')
3987 OR LOWER(tmp_attr_set.circ) = 'circulating'; -- BOOL
3990 LOWER( SUBSTRING( tmp_attr_set.dep, 1, 1 ) ) IN ('t','y','1')
3991 OR LOWER(tmp_attr_set.dep) = 'deposit'; -- BOOL
3993 attr_set.holdable :=
3994 LOWER( SUBSTRING( tmp_attr_set.hold, 1, 1 ) ) IN ('t','y','1')
3995 OR LOWER(tmp_attr_set.hold) = 'holdable'; -- BOOL
3997 attr_set.opac_visible :=
3998 LOWER( SUBSTRING( tmp_attr_set.opac_vis, 1, 1 ) ) IN ('t','y','1')
3999 OR LOWER(tmp_attr_set.opac_vis) = 'visible'; -- BOOL
4002 LOWER( SUBSTRING( tmp_attr_set.r, 1, 1 ) ) IN ('t','y','1')
4003 OR LOWER(tmp_attr_set.r) = 'reference'; -- BOOL
4005 attr_set.call_number := tmp_attr_set.cn; -- TEXT
4006 attr_set.barcode := tmp_attr_set.bc; -- TEXT,
4007 attr_set.alert_message := tmp_attr_set.amessage; -- TEXT,
4008 attr_set.pub_note := tmp_attr_set.note; -- TEXT,
4009 attr_set.priv_note := tmp_attr_set.pnote; -- TEXT,
4010 attr_set.alert_message := tmp_attr_set.amessage; -- TEXT,
4012 RETURN NEXT attr_set;
4021 $$ LANGUAGE PLPGSQL;
4023 CREATE OR REPLACE FUNCTION vandelay.ingest_bib_items ( ) RETURNS TRIGGER AS $func$
4026 item_data vandelay.import_item%ROWTYPE;
4029 IF TG_OP IN ('INSERT','UPDATE') AND NEW.imported_as IS NOT NULL THEN
4033 SELECT item_attr_def INTO attr_def FROM vandelay.bib_queue WHERE id = NEW.queue;
4035 FOR item_data IN SELECT * FROM vandelay.ingest_items( NEW.id::BIGINT, attr_def ) LOOP
4036 INSERT INTO vandelay.import_item (
4062 item_data.definition,
4063 item_data.owning_lib,
4065 item_data.call_number,
4066 item_data.copy_number,
4069 item_data.circulate,
4071 item_data.deposit_amount,
4076 item_data.circ_modifier,
4077 item_data.circ_as_type,
4078 item_data.alert_message,
4080 item_data.priv_note,
4081 item_data.opac_visible,
4082 item_data.import_error,
4083 item_data.error_detail
4089 $func$ LANGUAGE PLPGSQL;
4091 -- Evergreen DB patch XXXX.schema.vandelay.bib_match_isxn_caseless.sql
4094 -- check whether patch can be applied
4095 SELECT evergreen.upgrade_deps_block_check('0597', :eg_version);
4097 CREATE INDEX metabib_full_rec_isxn_caseless_idx
4098 ON metabib.real_full_rec (LOWER(value))
4099 WHERE tag IN ('020', '022', '024');
4102 CREATE OR REPLACE FUNCTION vandelay.flatten_marc_hstore(
4104 ) RETURNS HSTORE AS $$
4108 ARRAY_ACCUM(tag || (COALESCE(subfield, ''))),
4114 CASE WHEN tag IN ('020', '022', '024') THEN -- caseless
4115 ARRAY_ACCUM(LOWER(value))::TEXT
4117 ARRAY_ACCUM(value)::TEXT
4119 FROM vandelay.flatten_marc(record_xml)
4120 GROUP BY tag, subfield ORDER BY tag, subfield
4124 $$ LANGUAGE PLPGSQL;
4126 CREATE OR REPLACE FUNCTION vandelay._get_expr_push_jrow(
4127 node vandelay.match_set_point
4128 ) RETURNS VOID AS $$
4136 -- remember $1 is tags_rstore, and $2 is svf_rstore
4146 IF node.tag IS NOT NULL THEN
4147 caseless := (node.tag IN ('020', '022', '024'));
4149 IF node.subfield IS NOT NULL THEN
4150 tagkey := tagkey || node.subfield;
4154 my_alias := 'n' || node.id::TEXT;
4156 jrow := 'LEFT JOIN (SELECT *, ' || node.quality ||
4157 ' AS quality FROM metabib.';
4158 IF node.tag IS NOT NULL THEN
4159 jrow := jrow || 'full_rec) ' || my_alias || ' ON (' ||
4160 my_alias || '.record = bre.id AND ' || my_alias || '.tag = ''' ||
4162 IF node.subfield IS NOT NULL THEN
4163 jrow := jrow || ' AND ' || my_alias || '.subfield = ''' ||
4164 node.subfield || '''';
4166 jrow := jrow || ' AND (';
4169 jrow := jrow || 'LOWER(' || my_alias || '.value) ' || op;
4171 jrow := jrow || my_alias || '.value ' || op;
4174 jrow := jrow || ' ANY(($1->''' || tagkey || ''')::TEXT[])))';
4176 jrow := jrow || 'record_attr) ' || my_alias || ' ON (' ||
4177 my_alias || '.id = bre.id AND (' ||
4178 my_alias || '.attrs->''' || node.svf ||
4179 ''' ' || op || ' $2->''' || node.svf || '''))';
4181 INSERT INTO _vandelay_tmp_jrows (j) VALUES (jrow);
4183 $$ LANGUAGE PLPGSQL;
4185 -- Evergreen DB patch 0598.schema.vandelay_one_match_per.sql
4189 -- check whether patch can be applied
4190 SELECT evergreen.upgrade_deps_block_check('0598', :eg_version);
4192 CREATE OR REPLACE FUNCTION vandelay.match_set_test_marcxml(
4193 match_set_id INTEGER, record_xml TEXT
4194 ) RETURNS SETOF vandelay.match_set_test_result AS $$
4205 tags_rstore := vandelay.flatten_marc_hstore(record_xml);
4206 svf_rstore := vandelay.extract_rec_attrs(record_xml);
4208 CREATE TEMPORARY TABLE _vandelay_tmp_qrows (q INTEGER);
4209 CREATE TEMPORARY TABLE _vandelay_tmp_jrows (j TEXT);
4211 -- generate the where clause and return that directly (into wq), and as
4212 -- a side-effect, populate the _vandelay_tmp_[qj]rows tables.
4213 wq := vandelay.get_expr_from_match_set(match_set_id);
4215 query_ := 'SELECT DISTINCT(bre.id) AS record, ';
4217 -- qrows table is for the quality bits we add to the SELECT clause
4218 SELECT ARRAY_TO_STRING(
4219 ARRAY_ACCUM('COALESCE(n' || q::TEXT || '.quality, 0)'), ' + '
4220 ) INTO coal FROM _vandelay_tmp_qrows;
4222 -- our query string so far is the SELECT clause and the inital FROM.
4223 -- no JOINs yet nor the WHERE clause
4224 query_ := query_ || coal || ' AS quality ' || E'\n' ||
4225 'FROM biblio.record_entry bre ';
4227 -- jrows table is for the joins we must make (and the real text conditions)
4228 SELECT ARRAY_TO_STRING(ARRAY_ACCUM(j), E'\n') INTO joins
4229 FROM _vandelay_tmp_jrows;
4231 -- add those joins and the where clause to our query.
4232 query_ := query_ || joins || E'\n' || 'WHERE ' || wq || ' AND not bre.deleted';
4234 -- this will return rows of record,quality
4235 FOR rec IN EXECUTE query_ USING tags_rstore, svf_rstore LOOP
4239 DROP TABLE _vandelay_tmp_qrows;
4240 DROP TABLE _vandelay_tmp_jrows;
4244 $$ LANGUAGE PLPGSQL;
4246 -- Evergreen DB patch 0606.schema.czs_use_perm_column.sql
4248 -- This adds a column to config.z3950_source called use_perm.
4249 -- The idea is that if a permission is set for a given source,
4250 -- then staff will need the referenced permission to use that
4254 -- check whether patch can be applied
4255 SELECT evergreen.upgrade_deps_block_check('0606', :eg_version);
4257 ALTER TABLE config.z3950_source
4258 ADD COLUMN use_perm INT REFERENCES permission.perm_list (id) ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED;
4260 COMMENT ON COLUMN config.z3950_source.use_perm IS $$
4261 If set, this permission is required for the source to be listed in the staff
4262 client Z39.50 interface. Similar to permission.grp_tree.application_perm.
4265 -- Evergreen DB patch 0608.data.vandelay-export-error-match-info.sql
4270 -- check whether patch can be applied
4271 SELECT evergreen.upgrade_deps_block_check('0608', :eg_version);
4273 -- Add vqbr.import_error, vqbr.error_detail, and vqbr.matches.size to queue print output
4275 UPDATE action_trigger.event_definition SET template = $$
4278 Queue ID: [% target.0.queue.id %]
4279 Queue Name: [% target.0.queue.name %]
4280 Queue Type: [% target.0.queue.queue_type %]
4281 Complete? [% target.0.queue.complete %]
4283 [% FOR vqbr IN target %]
4285 Title of work | [% helpers.get_queued_bib_attr('title',vqbr.attributes) %]
4286 Author of work | [% helpers.get_queued_bib_attr('author',vqbr.attributes) %]
4287 Language of work | [% helpers.get_queued_bib_attr('language',vqbr.attributes) %]
4288 Pagination | [% helpers.get_queued_bib_attr('pagination',vqbr.attributes) %]
4289 ISBN | [% helpers.get_queued_bib_attr('isbn',vqbr.attributes) %]
4290 ISSN | [% helpers.get_queued_bib_attr('issn',vqbr.attributes) %]
4291 Price | [% helpers.get_queued_bib_attr('price',vqbr.attributes) %]
4292 Accession Number | [% helpers.get_queued_bib_attr('rec_identifier',vqbr.attributes) %]
4293 TCN Value | [% helpers.get_queued_bib_attr('eg_tcn',vqbr.attributes) %]
4294 TCN Source | [% helpers.get_queued_bib_attr('eg_tcn_source',vqbr.attributes) %]
4295 Internal ID | [% helpers.get_queued_bib_attr('eg_identifier',vqbr.attributes) %]
4296 Publisher | [% helpers.get_queued_bib_attr('publisher',vqbr.attributes) %]
4297 Publication Date | [% helpers.get_queued_bib_attr('pubdate',vqbr.attributes) %]
4298 Edition | [% helpers.get_queued_bib_attr('edition',vqbr.attributes) %]
4299 Item Barcode | [% helpers.get_queued_bib_attr('item_barcode',vqbr.attributes) %]
4300 Import Error | [% vqbr.import_error %]
4301 Error Detail | [% vqbr.error_detail %]
4302 Match Count | [% vqbr.matches.size %]
4310 -- Do the same for the CVS version
4312 UPDATE action_trigger.event_definition SET template = $$
4314 "Title of work","Author of work","Language of work","Pagination","ISBN","ISSN","Price","Accession Number","TCN Value","TCN Source","Internal ID","Publisher","Publication Date","Edition","Item Barcode","Import Error","Error Detail","Match Count"
4315 [% FOR vqbr IN target %]"[% helpers.get_queued_bib_attr('title',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('author',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('language',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('pagination',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('isbn',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('issn',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('price',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('rec_identifier',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('eg_tcn',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('eg_tcn_source',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('eg_identifier',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('publisher',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('pubdate',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('edition',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('item_barcode',vqbr.attributes) | replace('"', '""') %]","[% vqbr.import_error | replace('"', '""') %]","[% vqbr.error_detail | replace('"', '""') %]","[% vqbr.matches.size %]"
4320 -- Add matches to the env for both
4321 INSERT INTO action_trigger.environment (event_def, path) VALUES (39, 'matches');
4322 INSERT INTO action_trigger.environment (event_def, path) VALUES (40, 'matches');
4325 -- Evergreen DB patch XXXX.data.acq-copy-creator-from-receiver.sql
4327 -- check whether patch can be applied
4328 SELECT evergreen.upgrade_deps_block_check('0609', :eg_version);
4330 ALTER TABLE acq.lineitem_detail
4331 ADD COLUMN receiver INT REFERENCES actor.usr (id) DEFERRABLE INITIALLY DEFERRED;
4334 -- Evergreen DB patch XXXX.data.acq-copy-creator-from-receiver.sql
4336 -- check whether patch can be applied
4337 SELECT evergreen.upgrade_deps_block_check('0610', :eg_version);
4339 INSERT INTO config.org_unit_setting_type ( name, label, description, datatype ) VALUES (
4340 'acq.copy_creator_uses_receiver',
4342 'acq.copy_creator_uses_receiver',
4343 'Acq: Set copy creator as receiver',
4348 'acq.copy_creator_uses_receiver',
4349 'When receiving a copy in acquisitions, set the copy "creator" to be the staff that received the copy',
4356 -- Evergreen DB patch 0611.data.magic_macros.sql
4358 -- check whether patch can be applied
4359 SELECT evergreen.upgrade_deps_block_check('0611', :eg_version);
4361 INSERT into config.org_unit_setting_type
4362 ( name, label, description, datatype ) VALUES
4364 'circ.staff_client.receipt.header_text',
4366 'circ.staff_client.receipt.header_text',
4367 'Receipt Template: Content of header_text include',
4372 'circ.staff_client.receipt.header_text',
4373 'Text/HTML/Macros to be inserted into receipt templates in place of %INCLUDE(header_text)%',
4380 'circ.staff_client.receipt.footer_text',
4382 'circ.staff_client.receipt.footer_text',
4383 'Receipt Template: Content of footer_text include',
4388 'circ.staff_client.receipt.footer_text',
4389 'Text/HTML/Macros to be inserted into receipt templates in place of %INCLUDE(footer_text)%',
4396 'circ.staff_client.receipt.notice_text',
4398 'circ.staff_client.receipt.notice_text',
4399 'Receipt Template: Content of notice_text include',
4404 'circ.staff_client.receipt.notice_text',
4405 'Text/HTML/Macros to be inserted into receipt templates in place of %INCLUDE(notice_text)%',
4412 'circ.staff_client.receipt.alert_text',
4414 'circ.staff_client.receipt.alert_text',
4415 'Receipt Template: Content of alert_text include',
4420 'circ.staff_client.receipt.alert_text',
4421 'Text/HTML/Macros to be inserted into receipt templates in place of %INCLUDE(alert_text)%',
4428 'circ.staff_client.receipt.event_text',
4430 'circ.staff_client.receipt.event_text',
4431 'Receipt Template: Content of event_text include',
4436 'circ.staff_client.receipt.event_text',
4437 'Text/HTML/Macros to be inserted into receipt templates in place of %INCLUDE(event_text)%',
4444 -- Evergreen DB patch 0612.schema.authority_overlay_protection.sql
4448 -- check whether patch can be applied
4449 SELECT evergreen.upgrade_deps_block_check('0612', :eg_version);
4451 -- FIXME: add/check SQL statements to perform the upgrade
4453 -- Function to generate an ephemeral overlay template from an authority record
4454 CREATE OR REPLACE FUNCTION authority.generate_overlay_template (source_xml TEXT) RETURNS TEXT AS $f$
4457 main_entry authority.control_set_authority_field%ROWTYPE;
4458 bib_field authority.control_set_bib_field%ROWTYPE;
4459 auth_id INT DEFAULT oils_xpath_string('//*[@tag="901"]/*[local-name()="subfield" and @code="c"]', source_xml)::INT;
4460 replace_data XML[] DEFAULT '{}'::XML[];
4461 replace_rules TEXT[] DEFAULT '{}'::TEXT[];
4464 IF auth_id IS NULL THEN
4468 -- Default to the LoC controll set
4469 SELECT control_set INTO cset FROM authority.record_entry WHERE id = auth_id;
4471 -- if none, make a best guess
4472 IF cset IS NULL THEN
4473 SELECT control_set INTO cset
4474 FROM authority.control_set_authority_field
4476 SELECT UNNEST(XPATH('//*[starts-with(@tag,"1")]/@tag',marc::XML)::TEXT[])
4477 FROM authority.record_entry
4483 -- if STILL none, no-op change
4484 IF cset IS NULL THEN
4487 XMLATTRIBUTES('http://www.loc.gov/MARC21/slim' AS xmlns),
4488 XMLELEMENT( name leader, '00881nam a2200193 4500'),
4491 XMLATTRIBUTES( '905' AS tag, ' ' AS ind1, ' ' AS ind2),
4494 XMLATTRIBUTES('d' AS code),
4501 FOR main_entry IN SELECT * FROM authority.control_set_authority_field WHERE control_set = cset LOOP
4502 auth_field := XPATH('//*[@tag="'||main_entry.tag||'"][1]',source_xml::XML);
4503 IF ARRAY_LENGTH(auth_field,1) > 0 THEN
4504 FOR bib_field IN SELECT * FROM authority.control_set_bib_field WHERE authority_field = main_entry.id LOOP
4505 replace_data := replace_data || XMLELEMENT( name datafield, XMLATTRIBUTES(bib_field.tag AS tag), XPATH('//*[local-name()="subfield"]',auth_field[1])::XML[]);
4506 replace_rules := replace_rules || ( bib_field.tag || main_entry.sf_list || E'[0~\\)' || auth_id || '$]' );
4514 XMLATTRIBUTES('http://www.loc.gov/MARC21/slim' AS xmlns),
4515 XMLELEMENT( name leader, '00881nam a2200193 4500'),
4519 XMLATTRIBUTES( '905' AS tag, ' ' AS ind1, ' ' AS ind2),
4522 XMLATTRIBUTES('r' AS code),
4523 ARRAY_TO_STRING(replace_rules,',')
4528 $f$ STABLE LANGUAGE PLPGSQL;
4532 -- Evergreen DB patch 0613.schema.vandelay_isxn_normalization.sql
4536 -- check whether patch can be applied
4537 SELECT evergreen.upgrade_deps_block_check('0613', :eg_version);
4539 CREATE OR REPLACE FUNCTION vandelay.flatten_marc_hstore(
4541 ) RETURNS HSTORE AS $func$
4545 ARRAY_ACCUM(tag || (COALESCE(subfield, ''))),
4549 SELECT tag, subfield, ARRAY_ACCUM(value)::TEXT AS value
4552 CASE WHEN tag = '020' THEN -- caseless -- isbn
4553 LOWER((REGEXP_MATCHES(value,$$^(\S{10,17})$$))[1] || '%')
4554 WHEN tag = '022' THEN -- caseless -- issn
4555 LOWER((REGEXP_MATCHES(value,$$^(\S{4}[- ]?\S{4})$$))[1] || '%')
4556 WHEN tag = '024' THEN -- caseless -- upc (other)
4561 FROM vandelay.flatten_marc(record_xml)) x
4562 GROUP BY tag, subfield ORDER BY tag, subfield
4566 $func$ LANGUAGE PLPGSQL;
4568 CREATE OR REPLACE FUNCTION vandelay._get_expr_push_jrow(
4569 node vandelay.match_set_point
4570 ) RETURNS VOID AS $$
4578 -- remember $1 is tags_rstore, and $2 is svf_rstore
4582 IF node.tag IS NOT NULL THEN
4583 caseless := (node.tag IN ('020', '022', '024'));
4585 IF node.subfield IS NOT NULL THEN
4586 tagkey := tagkey || node.subfield;
4604 my_alias := 'n' || node.id::TEXT;
4606 jrow := 'LEFT JOIN (SELECT *, ' || node.quality ||
4607 ' AS quality FROM metabib.';
4608 IF node.tag IS NOT NULL THEN
4609 jrow := jrow || 'full_rec) ' || my_alias || ' ON (' ||
4610 my_alias || '.record = bre.id AND ' || my_alias || '.tag = ''' ||
4612 IF node.subfield IS NOT NULL THEN
4613 jrow := jrow || ' AND ' || my_alias || '.subfield = ''' ||
4614 node.subfield || '''';
4616 jrow := jrow || ' AND (';
4619 jrow := jrow || 'LOWER(' || my_alias || '.value) ' || op;
4621 jrow := jrow || my_alias || '.value ' || op;
4624 jrow := jrow || ' ANY(($1->''' || tagkey || ''')::TEXT[])))';
4626 jrow := jrow || 'record_attr) ' || my_alias || ' ON (' ||
4627 my_alias || '.id = bre.id AND (' ||
4628 my_alias || '.attrs->''' || node.svf ||
4629 ''' ' || op || ' $2->''' || node.svf || '''))';
4631 INSERT INTO _vandelay_tmp_jrows (j) VALUES (jrow);
4633 $$ LANGUAGE PLPGSQL;
4637 -- Evergreen DB patch XXXX.schema.generic-mapping-index-normalizer.sql
4640 -- check whether patch can be applied
4641 SELECT evergreen.upgrade_deps_block_check('0615', :eg_version);
4643 -- evergreen.generic_map_normalizer
4645 CREATE OR REPLACE FUNCTION evergreen.generic_map_normalizer ( TEXT, TEXT ) RETURNS TEXT AS $f$
4649 my $default = $string;
4652 while (/^\s*?(.*?)\s*?=>\s*?(\S+)\s*/) {
4656 $map{$2} = [split(/\s*,\s*/, $1)];
4661 for my $key ( keys %map ) {
4662 return $key if (grep { $_ eq $string } @{ $map{$key} });
4667 $f$ LANGUAGE PLPERLU;
4669 -- evergreen.generic_map_normalizer
4671 INSERT INTO config.index_normalizer (name, description, func, param_count) VALUES (
4672 'Generic Mapping Normalizer',
4673 'Map values or sets of values to new values',
4674 'generic_map_normalizer',
4679 SELECT evergreen.upgrade_deps_block_check('0616', :eg_version);
4681 CREATE OR REPLACE FUNCTION actor.org_unit_prox_update () RETURNS TRIGGER as $$
4685 IF TG_OP = 'DELETE' THEN
4687 DELETE FROM actor.org_unit_proximity WHERE (from_org = OLD.id or to_org= OLD.id);
4691 IF TG_OP = 'UPDATE' THEN
4693 IF NEW.parent_ou <> OLD.parent_ou THEN
4695 DELETE FROM actor.org_unit_proximity WHERE (from_org = OLD.id or to_org= OLD.id);
4696 INSERT INTO actor.org_unit_proximity (from_org, to_org, prox)
4697 SELECT l.id, r.id, actor.org_unit_proximity(l.id,r.id)
4698 FROM actor.org_unit l, actor.org_unit r
4699 WHERE (l.id = NEW.id or r.id = NEW.id);
4705 IF TG_OP = 'INSERT' THEN
4707 INSERT INTO actor.org_unit_proximity (from_org, to_org, prox)
4708 SELECT l.id, r.id, actor.org_unit_proximity(l.id,r.id)
4709 FROM actor.org_unit l, actor.org_unit r
4710 WHERE (l.id = NEW.id or r.id = NEW.id);
4717 $$ LANGUAGE plpgsql;
4720 CREATE TRIGGER proximity_update_tgr AFTER INSERT OR UPDATE OR DELETE ON actor.org_unit FOR EACH ROW EXECUTE PROCEDURE actor.org_unit_prox_update ();
4723 SELECT evergreen.upgrade_deps_block_check('0617', :eg_version);
4725 -- add notify columns to booking.reservation
4726 ALTER TABLE booking.reservation
4727 ADD COLUMN email_notify BOOLEAN NOT NULL DEFAULT FALSE;
4729 -- create the hook and validator
4730 INSERT INTO action_trigger.hook (key, core_type, description, passive)
4731 VALUES ('reservation.available', 'bresv', 'A reservation is available for pickup', false);
4732 INSERT INTO action_trigger.validator (module, description)
4733 VALUES ('ReservationIsAvailable','Checked that a reserved resource is available for checkout');
4735 -- create org unit setting to toggle checkbox display
4736 INSERT INTO config.org_unit_setting_type (name, label, description, datatype)
4737 VALUES ('booking.allow_email_notify', 'booking.allow_email_notify', 'Permit email notification when a reservation is ready for pickup.', 'bool');
4740 SELECT evergreen.upgrade_deps_block_check('0618', :eg_version);
4742 UPDATE config.org_unit_setting_type SET description = E'The Regular Expression for validation on the day_phone field in patron registration. Note: The first capture group will be used for the "last 4 digits of phone number" feature, if enabled. Ex: "[2-9]\\d{2}-\\d{3}-(\\d{4})( x\\d+)?" will ignore the extension on a NANP number.' WHERE name = 'ui.patron.edit.au.day_phone.regex';
4744 UPDATE config.org_unit_setting_type SET description = 'The Regular Expression for validation on phone fields in patron registration. Applies to all phone fields without their own setting. NOTE: See description of the day_phone regex for important information about capture groups with it.' WHERE name = 'ui.patron.edit.phone.regex';
4746 UPDATE config.org_unit_setting_type SET description = oils_i18n_gettext('patron.password.use_phone', 'By default, use the last 4 alphanumeric characters of the patrons phone number as the default password when creating new users. The exact characters used may be configured via the "GUI: Regex for day_phone field on patron registration" setting.', 'coust', 'description') WHERE name = 'patron.password.use_phone';
4748 -- Evergreen DB patch 0619.schema.au_last_update_time.sql
4750 -- check whether patch can be applied
4751 SELECT evergreen.upgrade_deps_block_check('0619', :eg_version);
4753 -- Add new column last_update_time to actor.usr, with trigger to maintain it
4754 -- Add corresponding new column to auditor.actor_usr_history
4756 ALTER TABLE actor.usr
4757 ADD COLUMN last_update_time TIMESTAMPTZ;
4759 ALTER TABLE auditor.actor_usr_history
4760 ADD COLUMN last_update_time TIMESTAMPTZ;
4762 CREATE OR REPLACE FUNCTION actor.au_updated()
4763 RETURNS TRIGGER AS $$
4765 NEW.last_update_time := now();
4768 $$ LANGUAGE plpgsql;
4770 CREATE TRIGGER au_update_trig
4771 BEFORE INSERT OR UPDATE ON actor.usr
4772 FOR EACH ROW EXECUTE PROCEDURE actor.au_updated();
4774 -- Evergreen DB patch XXXX.data.opac_payment_history_age_limit.sql
4777 SELECT evergreen.upgrade_deps_block_check('0621', :eg_version);
4779 INSERT into config.org_unit_setting_type (name, label, description, datatype)
4781 'opac.payment_history_age_limit',
4782 oils_i18n_gettext('opac.payment_history_age_limit',
4783 'OPAC: Payment History Age Limit', 'coust', 'label'),
4784 oils_i18n_gettext('opac.payment_history_age_limit',
4785 'The OPAC should not display payments by patrons that are older than any interval defined here.', 'coust', 'label'),
4789 -- Updates config.org_unit_setting_type to remove the old tag prefixes for once
4790 -- groups have been added.
4793 SELECT evergreen.upgrade_deps_block_check('0622', :eg_version);
4795 INSERT INTO config.settings_group (name, label) VALUES
4796 ('sys', oils_i18n_gettext('config.settings_group.system', 'System', 'coust', 'label')),
4797 ('gui', oils_i18n_gettext('config.settings_group.gui', 'GUI', 'coust', 'label')),
4798 ('lib', oils_i18n_gettext('config.settings_group.lib', 'Library', 'coust', 'label')),
4799 ('sec', oils_i18n_gettext('config.settings_group.sec', 'Security', 'coust', 'label')),
4800 ('cat', oils_i18n_gettext('config.settings_group.cat', 'Cataloging', 'coust', 'label')),
4801 ('holds', oils_i18n_gettext('config.settings_group.holds', 'Holds', 'coust', 'label')),
4802 ('circ', oils_i18n_gettext('config.settings_group.circulation', 'Circulation', 'coust', 'label')),
4803 ('self', oils_i18n_gettext('config.settings_group.self', 'Self Check', 'coust', 'label')),
4804 ('opac', oils_i18n_gettext('config.settings_group.opac', 'OPAC', 'coust', 'label')),
4805 ('prog', oils_i18n_gettext('config.settings_group.program', 'Program', 'coust', 'label')),
4806 ('glob', oils_i18n_gettext('config.settings_group.global', 'Global', 'coust', 'label')),
4807 ('finance', oils_i18n_gettext('config.settings_group.finances', 'Finances', 'coust', 'label')),
4808 ('credit', oils_i18n_gettext('config.settings_group.ccp', 'Credit Card Processing', 'coust', 'label')),
4809 ('serial', oils_i18n_gettext('config.settings_group.serial', 'Serials', 'coust', 'label')),
4810 ('recall', oils_i18n_gettext('config.settings_group.recall', 'Recalls', 'coust', 'label')),
4811 ('booking', oils_i18n_gettext('config.settings_group.booking', 'Booking', 'coust', 'label')),
4812 ('offline', oils_i18n_gettext('config.settings_group.offline', 'Offline', 'coust', 'label')),
4813 ('receipt_template', oils_i18n_gettext('config.settings_group.receipt_template', 'Receipt Template', 'coust', 'label'));
4815 UPDATE config.org_unit_setting_type SET grp = 'lib', label='Set copy creator as receiver' WHERE name = 'acq.copy_creator_uses_receiver';
4816 UPDATE config.org_unit_setting_type SET grp = 'lib' WHERE name = 'acq.default_circ_modifier';
4817 UPDATE config.org_unit_setting_type SET grp = 'lib' WHERE name = 'acq.default_copy_location';
4818 UPDATE config.org_unit_setting_type SET grp = 'finance' WHERE name = 'acq.fund.balance_limit.block';
4819 UPDATE config.org_unit_setting_type SET grp = 'finance' WHERE name = 'acq.fund.balance_limit.warn';
4820 UPDATE config.org_unit_setting_type SET grp = 'lib' WHERE name = 'acq.holds.allow_holds_from_purchase_request';
4821 UPDATE config.org_unit_setting_type SET grp = 'lib' WHERE name = 'acq.tmp_barcode_prefix';
4822 UPDATE config.org_unit_setting_type SET grp = 'lib' WHERE name = 'acq.tmp_callnumber_prefix';
4823 UPDATE config.org_unit_setting_type SET grp = 'sec' WHERE name = 'auth.opac_timeout';
4824 UPDATE config.org_unit_setting_type SET grp = 'sec' WHERE name = 'auth.persistent_login_interval';
4825 UPDATE config.org_unit_setting_type SET grp = 'sec' WHERE name = 'auth.staff_timeout';
4826 UPDATE config.org_unit_setting_type SET grp = 'booking' WHERE name = 'booking.allow_email_notify';
4827 UPDATE config.org_unit_setting_type SET grp = 'gui' WHERE name = 'cat.bib.alert_on_empty';
4828 UPDATE config.org_unit_setting_type SET grp = 'cat', label='Delete bib if all copies are deleted via Acquisitions lineitem cancellation.' WHERE name = 'cat.bib.delete_on_no_copy_via_acq_lineitem_cancel';
4829 UPDATE config.org_unit_setting_type SET grp = 'prog' WHERE name = 'cat.bib.keep_on_empty';
4830 UPDATE config.org_unit_setting_type SET grp = 'cat', label='Default Classification Scheme' WHERE name = 'cat.default_classification_scheme';
4831 UPDATE config.org_unit_setting_type SET grp = 'cat', label='Default copy status (fast add)' WHERE name = 'cat.default_copy_status_fast';
4832 UPDATE config.org_unit_setting_type SET grp = 'cat', label='Default copy status (normal)' WHERE name = 'cat.default_copy_status_normal';
4833 UPDATE config.org_unit_setting_type SET grp = 'finance' WHERE name = 'cat.default_item_price';
4834 UPDATE config.org_unit_setting_type SET grp = 'cat', label='Spine and pocket label font family' WHERE name = 'cat.label.font.family';
4835 UPDATE config.org_unit_setting_type SET grp = 'cat', label='Spine and pocket label font size' WHERE name = 'cat.label.font.size';
4836 UPDATE config.org_unit_setting_type SET grp = 'cat', label='Spine and pocket label font weight' WHERE name = 'cat.label.font.weight';
4837 UPDATE config.org_unit_setting_type SET grp = 'cat', label='Defines the control number identifier used in 003 and 035 fields.' WHERE name = 'cat.marc_control_number_identifier';
4838 UPDATE config.org_unit_setting_type SET grp = 'cat', label='Spine label maximum lines' WHERE name = 'cat.spine.line.height';
4839 UPDATE config.org_unit_setting_type SET grp = 'cat', label='Spine label left margin' WHERE name = 'cat.spine.line.margin';
4840 UPDATE config.org_unit_setting_type SET grp = 'cat', label='Spine label line width' WHERE name = 'cat.spine.line.width';
4841 UPDATE config.org_unit_setting_type SET grp = 'cat', label='Delete volume with last copy' WHERE name = 'cat.volume.delete_on_empty';
4842 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Toggle off the patron summary sidebar after first view.' WHERE name = 'circ.auto_hide_patron_summary';
4843 UPDATE config.org_unit_setting_type SET grp = 'holds', label='Block Renewal of Items Needed for Holds' WHERE name = 'circ.block_renews_for_holds';
4844 UPDATE config.org_unit_setting_type SET grp = 'booking', label='Elbow room' WHERE name = 'circ.booking_reservation.default_elbow_room';
4845 UPDATE config.org_unit_setting_type SET grp = 'finance' WHERE name = 'circ.charge_lost_on_zero';
4846 UPDATE config.org_unit_setting_type SET grp = 'finance' WHERE name = 'circ.charge_on_damaged';
4847 UPDATE config.org_unit_setting_type SET grp = 'circ' WHERE name = 'circ.checkout_auto_renew_age';
4848 UPDATE config.org_unit_setting_type SET grp = 'circ' WHERE name = 'circ.checkout_fills_related_hold';
4849 UPDATE config.org_unit_setting_type SET grp = 'circ' WHERE name = 'circ.checkout_fills_related_hold_exact_match_only';
4850 UPDATE config.org_unit_setting_type SET grp = 'lib' WHERE name = 'circ.claim_never_checked_out.mark_missing';
4851 UPDATE config.org_unit_setting_type SET grp = 'lib' WHERE name = 'circ.claim_return.copy_status';
4852 UPDATE config.org_unit_setting_type SET grp = 'lib' WHERE name = 'circ.damaged.void_ovedue';
4853 UPDATE config.org_unit_setting_type SET grp = 'finance' WHERE name = 'circ.damaged_item_processing_fee';
4854 UPDATE config.org_unit_setting_type SET grp = 'circ', label='Do not include outstanding Claims Returned circulations in lump sum tallies in Patron Display.' WHERE name = 'circ.do_not_tally_claims_returned';
4855 UPDATE config.org_unit_setting_type SET grp = 'holds', label='Hard boundary' WHERE name = 'circ.hold_boundary.hard';
4856 UPDATE config.org_unit_setting_type SET grp = 'holds', label='Soft boundary' WHERE name = 'circ.hold_boundary.soft';
4857 UPDATE config.org_unit_setting_type SET grp = 'holds', label='Expire Alert Interval' WHERE name = 'circ.hold_expire_alert_interval';
4858 UPDATE config.org_unit_setting_type SET grp = 'holds', label='Expire Interval' WHERE name = 'circ.hold_expire_interval';
4859 UPDATE config.org_unit_setting_type SET grp = 'circ' WHERE name = 'circ.hold_shelf_status_delay';
4860 UPDATE config.org_unit_setting_type SET grp = 'holds', label='Soft stalling interval' WHERE name = 'circ.hold_stalling.soft';
4861 UPDATE config.org_unit_setting_type SET grp = 'holds', label='Hard stalling interval' WHERE name = 'circ.hold_stalling_hard';
4862 UPDATE config.org_unit_setting_type SET grp = 'holds', label='Use Active Date for Age Protection' WHERE name = 'circ.holds.age_protect.active_date';
4863 UPDATE config.org_unit_setting_type SET grp = 'holds', label='Behind Desk Pickup Supported' WHERE name = 'circ.holds.behind_desk_pickup_supported';
4864 UPDATE config.org_unit_setting_type SET grp = 'holds', label='Canceled holds display age' WHERE name = 'circ.holds.canceled.display_age';
4865 UPDATE config.org_unit_setting_type SET grp = 'holds', label='Canceled holds display count' WHERE name = 'circ.holds.canceled.display_count';
4866 UPDATE config.org_unit_setting_type SET grp = 'holds', label='Clear shelf copy status' WHERE name = 'circ.holds.clear_shelf.copy_status';
4867 UPDATE config.org_unit_setting_type SET grp = 'holds', label='Bypass hold capture during clear shelf process' WHERE name = 'circ.holds.clear_shelf.no_capture_holds';
4868 UPDATE config.org_unit_setting_type SET grp = 'holds', label='Default Estimated Wait' WHERE name = 'circ.holds.default_estimated_wait_interval';
4869 UPDATE config.org_unit_setting_type SET grp = 'holds' WHERE name = 'circ.holds.default_shelf_expire_interval';
4870 UPDATE config.org_unit_setting_type SET grp = 'circ', label='Block hold request if hold recipient privileges have expired' WHERE name = 'circ.holds.expired_patron_block';
4871 UPDATE config.org_unit_setting_type SET grp = 'holds', label='Has Local Copy Alert' WHERE name = 'circ.holds.hold_has_copy_at.alert';
4872 UPDATE config.org_unit_setting_type SET grp = 'holds', label='Has Local Copy Block' WHERE name = 'circ.holds.hold_has_copy_at.block';
4873 UPDATE config.org_unit_setting_type SET grp = 'holds', label='Maximum library target attempts' WHERE name = 'circ.holds.max_org_unit_target_loops';
4874 UPDATE config.org_unit_setting_type SET grp = 'holds', label='Minimum Estimated Wait' WHERE name = 'circ.holds.min_estimated_wait_interval';
4875 UPDATE config.org_unit_setting_type SET grp = 'holds', label='Org Unit Target Weight' WHERE name = 'circ.holds.org_unit_target_weight';
4876 UPDATE config.org_unit_setting_type SET grp = 'recall', label='An array of fine amount, fine interval, and maximum fine.' WHERE name = 'circ.holds.recall_fine_rules';
4877 UPDATE config.org_unit_setting_type SET grp = 'recall', label='Truncated loan period.' WHERE name = 'circ.holds.recall_return_interval';
4878 UPDATE config.org_unit_setting_type SET grp = 'recall', label='Circulation duration that triggers a recall.' WHERE name = 'circ.holds.recall_threshold';
4879 UPDATE config.org_unit_setting_type SET grp = 'holds', label='Use weight-based hold targeting' WHERE name = 'circ.holds.target_holds_by_org_unit_weight';
4880 UPDATE config.org_unit_setting_type SET grp = 'holds' WHERE name = 'circ.holds.target_skip_me';
4881 UPDATE config.org_unit_setting_type SET grp = 'holds', label='Reset request time on un-cancel' WHERE name = 'circ.holds.uncancel.reset_request_time';
4882 UPDATE config.org_unit_setting_type SET grp = 'holds', label='FIFO' WHERE name = 'circ.holds_fifo';
4883 UPDATE config.org_unit_setting_type SET grp = 'gui' WHERE name = 'circ.item_checkout_history.max';
4884 UPDATE config.org_unit_setting_type SET grp = 'circ', label='Lost Checkin Generates New Overdues' WHERE name = 'circ.lost.generate_overdue_on_checkin';
4885 UPDATE config.org_unit_setting_type SET grp = 'circ', label='Lost items usable on checkin' WHERE name = 'circ.lost_immediately_available';
4886 UPDATE config.org_unit_setting_type SET grp = 'finance' WHERE name = 'circ.lost_materials_processing_fee';
4887 UPDATE config.org_unit_setting_type SET grp = 'circ', label='Void lost max interval' WHERE name = 'circ.max_accept_return_of_lost';
4888 UPDATE config.org_unit_setting_type SET grp = 'circ', label='Cap Max Fine at Item Price' WHERE name = 'circ.max_fine.cap_at_price';
4889 UPDATE config.org_unit_setting_type SET grp = 'circ' WHERE name = 'circ.max_patron_claim_return_count';
4890 UPDATE config.org_unit_setting_type SET grp = 'circ', label='Item Status for Missing Pieces' WHERE name = 'circ.missing_pieces.copy_status';
4891 UPDATE config.org_unit_setting_type SET grp = 'sec' WHERE name = 'circ.obscure_dob';
4892 UPDATE config.org_unit_setting_type SET grp = 'offline', label='Skip offline checkin if newer item Status Changed Time.' WHERE name = 'circ.offline.skip_checkin_if_newer_status_changed_time';
4893 UPDATE config.org_unit_setting_type SET grp = 'offline', label='Skip offline checkout if newer item Status Changed Time.' WHERE name = 'circ.offline.skip_checkout_if_newer_status_changed_time';
4894 UPDATE config.org_unit_setting_type SET grp = 'offline', label='Skip offline renewal if newer item Status Changed Time.' WHERE name = 'circ.offline.skip_renew_if_newer_status_changed_time';
4895 UPDATE config.org_unit_setting_type SET grp = 'sec', label='Offline: Patron Usernames Allowed' WHERE name = 'circ.offline.username_allowed';
4896 UPDATE config.org_unit_setting_type SET grp = 'sec', label='Maximum concurrently active self-serve password reset requests per user' WHERE name = 'circ.password_reset_request_per_user_limit';
4897 UPDATE config.org_unit_setting_type SET grp = 'circ', label='Require matching email address for password reset requests' WHERE name = 'circ.password_reset_request_requires_matching_email';
4898 UPDATE config.org_unit_setting_type SET grp = 'sec', label='Maximum concurrently active self-serve password reset requests' WHERE name = 'circ.password_reset_request_throttle';
4899 UPDATE config.org_unit_setting_type SET grp = 'sec', label='Self-serve password reset request time-to-live' WHERE name = 'circ.password_reset_request_time_to_live';
4900 UPDATE config.org_unit_setting_type SET grp = 'circ', label='Patron Registration: Cloned patrons get address copy' WHERE name = 'circ.patron_edit.clone.copy_address';
4901 UPDATE config.org_unit_setting_type SET grp = 'circ' WHERE name = 'circ.patron_invalid_address_apply_penalty';
4902 UPDATE config.org_unit_setting_type SET grp = 'lib' WHERE name = 'circ.pre_cat_copy_circ_lib';
4903 UPDATE config.org_unit_setting_type SET grp = 'lib' WHERE name = 'circ.reshelving_complete.interval';
4904 UPDATE config.org_unit_setting_type SET grp = 'circ', label='Restore overdues on lost item return' WHERE name = 'circ.restore_overdue_on_lost_return';
4905 UPDATE config.org_unit_setting_type SET grp = 'self', label='Pop-up alert for errors' WHERE name = 'circ.selfcheck.alert.popup';
4906 UPDATE config.org_unit_setting_type SET grp = 'self', label='Audio Alerts' WHERE name = 'circ.selfcheck.alert.sound';
4907 UPDATE config.org_unit_setting_type SET grp = 'self' WHERE name = 'circ.selfcheck.auto_override_checkout_events';
4908 UPDATE config.org_unit_setting_type SET grp = 'self', label='Block copy checkout status' WHERE name = 'circ.selfcheck.block_checkout_on_copy_status';
4909 UPDATE config.org_unit_setting_type SET grp = 'self', label='Patron Login Timeout (in seconds)' WHERE name = 'circ.selfcheck.patron_login_timeout';
4910 UPDATE config.org_unit_setting_type SET grp = 'self', label='Require Patron Password' WHERE name = 'circ.selfcheck.patron_password_required';
4911 UPDATE config.org_unit_setting_type SET grp = 'self', label='Require patron password' WHERE name = 'circ.selfcheck.require_patron_password';
4912 UPDATE config.org_unit_setting_type SET grp = 'self', label='Workstation Required' WHERE name = 'circ.selfcheck.workstation_required';
4913 UPDATE config.org_unit_setting_type SET grp = 'circ' WHERE name = 'circ.staff_client.actor_on_checkout';
4914 UPDATE config.org_unit_setting_type SET grp = 'prog' WHERE name = 'circ.staff_client.do_not_auto_attempt_print';
4915 UPDATE config.org_unit_setting_type SET grp = 'receipt_template', label='Content of alert_text include' WHERE name = 'circ.staff_client.receipt.alert_text';
4916 UPDATE config.org_unit_setting_type SET grp = 'receipt_template', label='Content of event_text include' WHERE name = 'circ.staff_client.receipt.event_text';
4917 UPDATE config.org_unit_setting_type SET grp = 'receipt_template', label='Content of footer_text include' WHERE name = 'circ.staff_client.receipt.footer_text';
4918 UPDATE config.org_unit_setting_type SET grp = 'receipt_template', label='Content of header_text include' WHERE name = 'circ.staff_client.receipt.header_text';
4919 UPDATE config.org_unit_setting_type SET grp = 'receipt_template', label='Content of notice_text include' WHERE name = 'circ.staff_client.receipt.notice_text';
4920 UPDATE config.org_unit_setting_type SET grp = 'circ', label='Minimum Transit Checkin Interval' WHERE name = 'circ.transit.min_checkin_interval';
4921 UPDATE config.org_unit_setting_type SET grp = 'circ', label='Patron Merge Deactivate Card' WHERE name = 'circ.user_merge.deactivate_cards';
4922 UPDATE config.org_unit_setting_type SET grp = 'circ', label='Patron Merge Address Delete' WHERE name = 'circ.user_merge.delete_addresses';
4923 UPDATE config.org_unit_setting_type SET grp = 'circ', label='Patron Merge Barcode Delete' WHERE name = 'circ.user_merge.delete_cards';
4924 UPDATE config.org_unit_setting_type SET grp = 'circ', label='Void lost item billing when returned' WHERE name = 'circ.void_lost_on_checkin';
4925 UPDATE config.org_unit_setting_type SET grp = 'circ', label='Void processing fee on lost item return' WHERE name = 'circ.void_lost_proc_fee_on_checkin';
4926 UPDATE config.org_unit_setting_type SET grp = 'finance', label='Void overdue fines when items are marked lost' WHERE name = 'circ.void_overdue_on_lost';
4927 UPDATE config.org_unit_setting_type SET grp = 'finance' WHERE name = 'credit.payments.allow';
4928 UPDATE config.org_unit_setting_type SET grp = 'credit', label='Enable AuthorizeNet payments' WHERE name = 'credit.processor.authorizenet.enabled';
4929 UPDATE config.org_unit_setting_type SET grp = 'credit', label='AuthorizeNet login' WHERE name = 'credit.processor.authorizenet.login';
4930 UPDATE config.org_unit_setting_type SET grp = 'credit', label='AuthorizeNet password' WHERE name = 'credit.processor.authorizenet.password';
4931 UPDATE config.org_unit_setting_type SET grp = 'credit', label='AuthorizeNet server' WHERE name = 'credit.processor.authorizenet.server';
4932 UPDATE config.org_unit_setting_type SET grp = 'credit', label='AuthorizeNet test mode' WHERE name = 'credit.processor.authorizenet.testmode';
4933 UPDATE config.org_unit_setting_type SET grp = 'credit', label='Name default credit processor' WHERE name = 'credit.processor.default';
4934 UPDATE config.org_unit_setting_type SET grp = 'credit', label='Enable PayflowPro payments' WHERE name = 'credit.processor.payflowpro.enabled';
4935 UPDATE config.org_unit_setting_type SET grp = 'credit', label='PayflowPro login/merchant ID' WHERE name = 'credit.processor.payflowpro.login';
4936 UPDATE config.org_unit_setting_type SET grp = 'credit', label='PayflowPro partner' WHERE name = 'credit.processor.payflowpro.partner';
4937 UPDATE config.org_unit_setting_type SET grp = 'credit', label='PayflowPro password' WHERE name = 'credit.processor.payflowpro.password';
4938 UPDATE config.org_unit_setting_type SET grp = 'credit', label='PayflowPro test mode' WHERE name = 'credit.processor.payflowpro.testmode';
4939 UPDATE config.org_unit_setting_type SET grp = 'credit', label='PayflowPro vendor' WHERE name = 'credit.processor.payflowpro.vendor';
4940 UPDATE config.org_unit_setting_type SET grp = 'credit', label='Enable PayPal payments' WHERE name = 'credit.processor.paypal.enabled';
4941 UPDATE config.org_unit_setting_type SET grp = 'credit', label='PayPal login' WHERE name = 'credit.processor.paypal.login';
4942 UPDATE config.org_unit_setting_type SET grp = 'credit', label='PayPal password' WHERE name = 'credit.processor.paypal.password';
4943 UPDATE config.org_unit_setting_type SET grp = 'credit', label='PayPal signature' WHERE name = 'credit.processor.paypal.signature';
4944 UPDATE config.org_unit_setting_type SET grp = 'credit', label='PayPal test mode' WHERE name = 'credit.processor.paypal.testmode';
4945 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Format Dates with this pattern.' WHERE name = 'format.date';
4946 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Format Times with this pattern.' WHERE name = 'format.time';
4947 UPDATE config.org_unit_setting_type SET grp = 'glob' WHERE name = 'global.default_locale';
4948 UPDATE config.org_unit_setting_type SET grp = 'lib' WHERE name = 'global.juvenile_age_threshold';
4949 UPDATE config.org_unit_setting_type SET grp = 'glob' WHERE name = 'global.password_regex';
4950 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Disable the ability to save list column configurations locally.' WHERE name = 'gui.disable_local_save_columns';
4951 UPDATE config.org_unit_setting_type SET grp = 'lib', label='Courier Code' WHERE name = 'lib.courier_code';
4952 UPDATE config.org_unit_setting_type SET grp = 'lib' WHERE name = 'notice.telephony.callfile_lines';
4953 UPDATE config.org_unit_setting_type SET grp = 'opac', label='Allow pending addresses' WHERE name = 'opac.allow_pending_address';
4954 UPDATE config.org_unit_setting_type SET grp = 'glob' WHERE name = 'opac.barcode_regex';
4955 UPDATE config.org_unit_setting_type SET grp = 'opac', label='Use fully compressed serial holdings' WHERE name = 'opac.fully_compressed_serial_holdings';
4956 UPDATE config.org_unit_setting_type SET grp = 'opac', label='Org Unit Hiding Depth' WHERE name = 'opac.org_unit_hiding.depth';
4957 UPDATE config.org_unit_setting_type SET grp = 'opac', label='Payment History Age Limit' WHERE name = 'opac.payment_history_age_limit';
4958 UPDATE config.org_unit_setting_type SET grp = 'prog' WHERE name = 'org.bounced_emails';
4959 UPDATE config.org_unit_setting_type SET grp = 'sec', label='Patron Opt-In Boundary' WHERE name = 'org.patron_opt_boundary';
4960 UPDATE config.org_unit_setting_type SET grp = 'sec', label='Patron Opt-In Default' WHERE name = 'org.patron_opt_default';
4961 UPDATE config.org_unit_setting_type SET grp = 'sec' WHERE name = 'patron.password.use_phone';
4962 UPDATE config.org_unit_setting_type SET grp = 'serial', label='Previous Issuance Copy Location' WHERE name = 'serial.prev_issuance_copy_location';
4963 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Work Log: Maximum Patrons Logged' WHERE name = 'ui.admin.patron_log.max_entries';
4964 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Work Log: Maximum Actions Logged' WHERE name = 'ui.admin.work_log.max_entries';
4965 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Horizontal layout for Volume/Copy Creator/Editor.' WHERE name = 'ui.cat.volume_copy_editor.horizontal';
4966 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Uncheck bills by default in the patron billing interface' WHERE name = 'ui.circ.billing.uncheck_bills_and_unfocus_payment_box';
4967 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Record In-House Use: Maximum # of uses allowed per entry.' WHERE name = 'ui.circ.in_house_use.entry_cap';
4968 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Record In-House Use: # of uses threshold for Are You Sure? dialog.' WHERE name = 'ui.circ.in_house_use.entry_warn';
4969 UPDATE config.org_unit_setting_type SET grp = 'gui' WHERE name = 'ui.circ.patron_summary.horizontal';
4970 UPDATE config.org_unit_setting_type SET grp = 'gui' WHERE name = 'ui.circ.show_billing_tab_on_bills';
4971 UPDATE config.org_unit_setting_type SET grp = 'circ', label='Suppress popup-dialogs during check-in.' WHERE name = 'ui.circ.suppress_checkin_popups';
4972 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Button bar' WHERE name = 'ui.general.button_bar';
4973 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Default Hotkeyset' WHERE name = 'ui.general.hotkeyset';
4974 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Idle timeout' WHERE name = 'ui.general.idle_timeout';
4975 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Default Country for New Addresses in Patron Editor' WHERE name = 'ui.patron.default_country';
4976 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Default Ident Type for Patron Registration' WHERE name = 'ui.patron.default_ident_type';
4977 UPDATE config.org_unit_setting_type SET grp = 'sec', label='Default level of patrons'' internet access' WHERE name = 'ui.patron.default_inet_access_level';
4978 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Show active field on patron registration' WHERE name = 'ui.patron.edit.au.active.show';
4979 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Suggest active field on patron registration' WHERE name = 'ui.patron.edit.au.active.suggest';
4980 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Show alert_message field on patron registration' WHERE name = 'ui.patron.edit.au.alert_message.show';
4981 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Suggest alert_message field on patron registration' WHERE name = 'ui.patron.edit.au.alert_message.suggest';
4982 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Show alias field on patron registration' WHERE name = 'ui.patron.edit.au.alias.show';
4983 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Suggest alias field on patron registration' WHERE name = 'ui.patron.edit.au.alias.suggest';
4984 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Show barred field on patron registration' WHERE name = 'ui.patron.edit.au.barred.show';
4985 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Suggest barred field on patron registration' WHERE name = 'ui.patron.edit.au.barred.suggest';
4986 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Show claims_never_checked_out_count field on patron registration' WHERE name = 'ui.patron.edit.au.claims_never_checked_out_count.show';
4987 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Suggest claims_never_checked_out_count field on patron registration' WHERE name = 'ui.patron.edit.au.claims_never_checked_out_count.suggest';
4988 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Show claims_returned_count field on patron registration' WHERE name = 'ui.patron.edit.au.claims_returned_count.show';
4989 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Suggest claims_returned_count field on patron registration' WHERE name = 'ui.patron.edit.au.claims_returned_count.suggest';
4990 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Example for day_phone field on patron registration' WHERE name = 'ui.patron.edit.au.day_phone.example';
4991 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Regex for day_phone field on patron registration' WHERE name = 'ui.patron.edit.au.day_phone.regex';
4992 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Require day_phone field on patron registration' WHERE name = 'ui.patron.edit.au.day_phone.require';
4993 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Show day_phone field on patron registration' WHERE name = 'ui.patron.edit.au.day_phone.show';
4994 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Suggest day_phone field on patron registration' WHERE name = 'ui.patron.edit.au.day_phone.suggest';
4995 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Show calendar widget for dob field on patron registration' WHERE name = 'ui.patron.edit.au.dob.calendar';
4996 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Require dob field on patron registration' WHERE name = 'ui.patron.edit.au.dob.require';
4997 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Show dob field on patron registration' WHERE name = 'ui.patron.edit.au.dob.show';
4998 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Suggest dob field on patron registration' WHERE name = 'ui.patron.edit.au.dob.suggest';
4999 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Example for email field on patron registration' WHERE name = 'ui.patron.edit.au.email.example';
5000 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Regex for email field on patron registration' WHERE name = 'ui.patron.edit.au.email.regex';
5001 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Require email field on patron registration' WHERE name = 'ui.patron.edit.au.email.require';
5002 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Show email field on patron registration' WHERE name = 'ui.patron.edit.au.email.show';
5003 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Suggest email field on patron registration' WHERE name = 'ui.patron.edit.au.email.suggest';
5004 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Example for evening_phone field on patron registration' WHERE name = 'ui.patron.edit.au.evening_phone.example';
5005 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Regex for evening_phone field on patron registration' WHERE name = 'ui.patron.edit.au.evening_phone.regex';
5006 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Require evening_phone field on patron registration' WHERE name = 'ui.patron.edit.au.evening_phone.require';
5007 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Show evening_phone field on patron registration' WHERE name = 'ui.patron.edit.au.evening_phone.show';
5008 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Suggest evening_phone field on patron registration' WHERE name = 'ui.patron.edit.au.evening_phone.suggest';
5009 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Show ident_value field on patron registration' WHERE name = 'ui.patron.edit.au.ident_value.show';
5010 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Suggest ident_value field on patron registration' WHERE name = 'ui.patron.edit.au.ident_value.suggest';
5011 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Show ident_value2 field on patron registration' WHERE name = 'ui.patron.edit.au.ident_value2.show';
5012 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Suggest ident_value2 field on patron registration' WHERE name = 'ui.patron.edit.au.ident_value2.suggest';
5013 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Show juvenile field on patron registration' WHERE name = 'ui.patron.edit.au.juvenile.show';
5014 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Suggest juvenile field on patron registration' WHERE name = 'ui.patron.edit.au.juvenile.suggest';
5015 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Show master_account field on patron registration' WHERE name = 'ui.patron.edit.au.master_account.show';
5016 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Suggest master_account field on patron registration' WHERE name = 'ui.patron.edit.au.master_account.suggest';
5017 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Example for other_phone field on patron registration' WHERE name = 'ui.patron.edit.au.other_phone.example';
5018 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Regex for other_phone field on patron registration' WHERE name = 'ui.patron.edit.au.other_phone.regex';
5019 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Require other_phone field on patron registration' WHERE name = 'ui.patron.edit.au.other_phone.require';
5020 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Show other_phone field on patron registration' WHERE name = 'ui.patron.edit.au.other_phone.show';
5021 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Suggest other_phone field on patron registration' WHERE name = 'ui.patron.edit.au.other_phone.suggest';
5022 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Show second_given_name field on patron registration' WHERE name = 'ui.patron.edit.au.second_given_name.show';
5023 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Suggest second_given_name field on patron registration' WHERE name = 'ui.patron.edit.au.second_given_name.suggest';
5024 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Show suffix field on patron registration' WHERE name = 'ui.patron.edit.au.suffix.show';
5025 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Suggest suffix field on patron registration' WHERE name = 'ui.patron.edit.au.suffix.suggest';
5026 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Require county field on patron registration' WHERE name = 'ui.patron.edit.aua.county.require';
5027 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Example for post_code field on patron registration' WHERE name = 'ui.patron.edit.aua.post_code.example';
5028 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Regex for post_code field on patron registration' WHERE name = 'ui.patron.edit.aua.post_code.regex';
5029 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Default showing suggested patron registration fields' WHERE name = 'ui.patron.edit.default_suggested';
5030 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Example for phone fields on patron registration' WHERE name = 'ui.patron.edit.phone.example';
5031 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Regex for phone fields on patron registration' WHERE name = 'ui.patron.edit.phone.regex';
5032 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Require at least one address for Patron Registration' WHERE name = 'ui.patron.registration.require_address';
5033 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Cap results in Patron Search at this number.' WHERE name = 'ui.patron_search.result_cap';
5034 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Require staff initials for entry/edit of item/patron/penalty notes/messages.' WHERE name = 'ui.staff.require_initials';
5035 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Unified Volume/Item Creator/Editor' WHERE name = 'ui.unified_volume_copy_editor';
5036 UPDATE config.org_unit_setting_type SET grp = 'gui', label='URL for remote directory containing list column settings.' WHERE name = 'url.remote_column_settings';
5041 SELECT evergreen.upgrade_deps_block_check('0623', :eg_version);
5044 CREATE TABLE config.org_unit_setting_type_log (
5045 id BIGSERIAL PRIMARY KEY,
5046 date_applied TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
5047 org INT REFERENCES actor.org_unit (id),
5048 original_value TEXT,
5050 field_name TEXT REFERENCES config.org_unit_setting_type (name)
5053 -- Log each change in oust to oustl, so admins can see what they messed up if someting stops working.
5054 CREATE OR REPLACE FUNCTION ous_change_log() RETURNS TRIGGER AS $ous_change_log$
5058 -- Check for which setting is being updated, and log it.
5059 SELECT INTO original value FROM actor.org_unit_setting WHERE name = NEW.name AND org_unit = NEW.org_unit;
5061 INSERT INTO config.org_unit_setting_type_log (org,original_value,new_value,field_name) VALUES (NEW.org_unit, original, NEW.value, NEW.name);
5065 $ous_change_log$ LANGUAGE plpgsql;
5067 CREATE TRIGGER log_ous_change
5068 BEFORE INSERT OR UPDATE ON actor.org_unit_setting
5069 FOR EACH ROW EXECUTE PROCEDURE ous_change_log();
5071 CREATE OR REPLACE FUNCTION ous_delete_log() RETURNS TRIGGER AS $ous_delete_log$
5075 -- Check for which setting is being updated, and log it.
5076 SELECT INTO original value FROM actor.org_unit_setting WHERE name = OLD.name AND org_unit = OLD.org_unit;
5078 INSERT INTO config.org_unit_setting_type_log (org,original_value,new_value,field_name) VALUES (OLD.org_unit, original, 'null', OLD.name);
5082 $ous_delete_log$ LANGUAGE plpgsql;
5084 CREATE TRIGGER log_ous_del
5085 BEFORE DELETE ON actor.org_unit_setting
5086 FOR EACH ROW EXECUTE PROCEDURE ous_delete_log();
5088 -- Evergreen DB patch 0625.data.opac_staff_saved_search_size.sql
5091 SELECT evergreen.upgrade_deps_block_check('0625', :eg_version);
5093 INSERT into config.org_unit_setting_type (name, grp, label, description, datatype)
5095 'opac.staff_saved_search.size', 'opac',
5096 oils_i18n_gettext('opac.staff_saved_search.size',
5097 'OPAC: Number of staff client saved searches to display on left side of results and record details pages', 'coust', 'label'),
5098 oils_i18n_gettext('opac.staff_saved_search.size',
5099 'If unset, the OPAC (only when wrapped in the staff client!) will default to showing you your ten most recent searches on the left side of the results and record details pages. If you actually don''t want to see this feature at all, set this value to zero at the top of your organizational tree.', 'coust', 'description'),
5103 -- Evergreen DB patch 0626.schema.bookbag-goodies.sql
5106 SELECT evergreen.upgrade_deps_block_check('0626', :eg_version);
5108 ALTER TABLE container.biblio_record_entry_bucket
5109 ADD COLUMN description TEXT;
5111 ALTER TABLE container.call_number_bucket
5112 ADD COLUMN description TEXT;
5114 ALTER TABLE container.copy_bucket
5115 ADD COLUMN description TEXT;
5117 ALTER TABLE container.user_bucket
5118 ADD COLUMN description TEXT;
5120 INSERT INTO action_trigger.hook (key, core_type, description, passive)
5122 'container.biblio_record_entry_bucket.csv',
5125 'container.biblio_record_entry_bucket.csv',
5126 'Produce a CSV file representing a bookbag',
5133 INSERT INTO action_trigger.reactor (module, description)
5138 'Facilitates produce a CSV file representing a bookbag by introducing an "items" variable into the TT environment, sorted as dictated according to user params',
5144 INSERT INTO action_trigger.event_definition (
5146 name, hook, reactor,
5150 'Bookbag CSV', 'container.biblio_record_entry_bucket.csv', 'ContainerCSV',
5154 # target is the bookbag itself. The 'items' variable does not need to be in
5155 # the environment because a special reactor will take care of filling it in.
5158 bibxml = helpers.xml_doc(item.target_biblio_record_entry.marc);
5160 FOR part IN bibxml.findnodes('//*[@tag="245"]/*[@code="a" or @code="b"]');
5161 title = title _ part.textContent;
5163 author = bibxml.findnodes('//*[@tag="100"]/*[@code="a"]').textContent;
5165 helpers.csv_datum(title) %],[% helpers.csv_datum(author) %],[% FOR note IN item.notes; helpers.csv_datum(note.note); ","; END; "\n";
5170 -- Evergreen DB patch 0627.data.patron-password-reset-msg.sql
5172 -- Updates password reset template to match TPAC reset form
5175 -- check whether patch can be applied
5176 SELECT evergreen.upgrade_deps_block_check('0627', :eg_version);
5178 UPDATE action_trigger.event_definition SET template =
5181 [%- user = target.usr -%]
5182 To: [%- params.recipient_email || user.email %]
5183 From: [%- params.sender_email || user.home_ou.email || default_sender %]
5184 Subject: [% user.home_ou.name %]: library account password reset request
5186 You have received this message because you, or somebody else, requested a reset
5187 of your library system password. If you did not request a reset of your library
5188 system password, just ignore this message and your current password will
5191 If you did request a reset of your library system password, please perform
5192 the following steps to continue the process of resetting your password:
5194 1. Open the following link in a web browser: https://[% params.hostname %]/eg/opac/password_reset/[% target.uuid %]
5195 The browser displays a password reset form.
5197 2. Enter your new password in the password reset form in the browser. You must
5198 enter the password twice to ensure that you do not make a mistake. If the
5199 passwords match, you will then be able to log in to your library system account
5200 with the new password.
5203 WHERE id = 20; -- Password reset request notification
5206 SELECT evergreen.upgrade_deps_block_check('0630', :eg_version);
5208 INSERT into config.org_unit_setting_type (name, grp, label, description, datatype) VALUES
5209 ( 'circ.transit.suppress_hold', 'circ',
5210 oils_i18n_gettext('circ.transit.suppress_hold',
5211 'Suppress Hold Transits Group',
5213 oils_i18n_gettext('circ.transit.suppress_hold',
5214 'If set to a non-empty value, Hold Transits will be suppressed between this OU and others with the same value. If set to an empty value, transits will not be suppressed.',
5215 'coust', 'description'),
5217 ,( 'circ.transit.suppress_non_hold', 'circ',
5218 oils_i18n_gettext('circ.transit.suppress_non_hold',
5219 'Suppress Non-Hold Transits Group',
5221 oils_i18n_gettext('circ.transit.suppress_non_hold',
5222 'If set to a non-empty value, Non-Hold Transits will be suppressed between this OU and others with the same value. If set to an empty value, transits will not be suppressed.',
5223 'coust', 'description'),
5227 -- check whether patch can be applied
5228 SELECT evergreen.upgrade_deps_block_check('0632', :eg_version);
5230 INSERT INTO config.org_unit_setting_type (name, grp, label, description, datatype) VALUES
5231 ( 'opac.username_regex', 'glob',
5232 oils_i18n_gettext('opac.username_regex',
5233 'Patron username format',
5235 oils_i18n_gettext('opac.username_regex',
5236 'Regular expression defining the patron username format, used for patron registration and self-service username changing only',
5237 'coust', 'description'),
5239 ,( 'opac.lock_usernames', 'glob',
5240 oils_i18n_gettext('opac.lock_usernames',
5243 oils_i18n_gettext('opac.lock_usernames',
5244 'If enabled username changing via the OPAC will be disabled',
5245 'coust', 'description'),
5247 ,( 'opac.unlimit_usernames', 'glob',
5248 oils_i18n_gettext('opac.unlimit_usernames',
5249 'Allow multiple username changes',
5251 oils_i18n_gettext('opac.unlimit_usernames',
5252 'If enabled (and Lock Usernames is not set) patrons will be allowed to change their username when it does not look like a barcode. Otherwise username changing in the OPAC will only be allowed when the patron''s username looks like a barcode.',
5253 'coust', 'description'),
5257 -- Evergreen DB patch 0635.data.opac.jump-to-details-setting.sql
5261 -- check whether patch can be applied
5262 SELECT evergreen.upgrade_deps_block_check('0635', :eg_version);
5264 INSERT INTO config.org_unit_setting_type ( name, grp, label, description, datatype )
5266 'opac.staff.jump_to_details_on_single_hit',
5269 'opac.staff.jump_to_details_on_single_hit',
5270 'Jump to details on 1 hit (staff client)',
5275 'opac.staff.jump_to_details_on_single_hit',
5276 'When a search yields only 1 result, jump directly to the record details page. This setting only affects the OPAC within the staff client',
5282 'opac.patron.jump_to_details_on_single_hit',
5285 'opac.patron.jump_to_details_on_single_hit',
5286 'Jump to details on 1 hit (public)',
5291 'opac.patron.jump_to_details_on_single_hit',
5292 'When a search yields only 1 result, jump directly to the record details page. This setting only affects the public OPAC',
5299 -- Evergreen DB patch 0636.data.grace_period_extend.sql
5301 -- OU setting turns on grace period auto extension. By default they only do so
5302 -- when the grace period ends on a closed date, but there are two modifiers to
5305 -- The first modifier causes grace periods to extend for all closed dates that
5306 -- they intersect. This is "grace periods are only consumed by open days."
5308 -- The second modifier causes a grace period that ends just before a closed
5309 -- day, with or without extension having happened, to include the closed day
5310 -- (and any following it) as well. This is mainly so that a backdate into the
5311 -- closed period following the grace period will assume the "best case" of the
5312 -- item having been returned after hours on the last day of the closed date.
5316 -- check whether patch can be applied
5317 SELECT evergreen.upgrade_deps_block_check('0636', :eg_version);
5319 INSERT INTO config.org_unit_setting_type(name, grp, label, description, datatype) VALUES
5321 ( 'circ.grace.extend', 'circ',
5322 oils_i18n_gettext('circ.grace.extend',
5323 'Auto-Extend Grace Periods',
5325 oils_i18n_gettext('circ.grace.extend',
5326 'When enabled grace periods will auto-extend. By default this will be only when they are a full day or more and end on a closed date, though other options can alter this.',
5327 'coust', 'description'),
5330 ,( 'circ.grace.extend.all', 'circ',
5331 oils_i18n_gettext('circ.grace.extend.all',
5332 'Auto-Extending Grace Periods extend for all closed dates',
5334 oils_i18n_gettext('circ.grace.extend.all',
5335 'If enabled and Grace Periods auto-extending is turned on grace periods will extend past all closed dates they intersect, within hard-coded limits. This basically becomes "grace periods can only be consumed by closed dates".',
5336 'coust', 'description'),
5339 ,( 'circ.grace.extend.into_closed', 'circ',
5340 oils_i18n_gettext('circ.grace.extend.into_closed',
5341 'Auto-Extending Grace Periods include trailing closed dates',
5343 oils_i18n_gettext('circ.grace.extend.into_closed',
5344 'If enabled and Grace Periods auto-extending is turned on grace periods will include closed dates that directly follow the last day of the grace period, to allow a backdate into the closed dates to assume "returned after hours on the last day of the grace period, and thus still within it" automatically.',
5345 'coust', 'description'),
5349 -- XXXX.schema-acs-nfi.sql
5351 SELECT evergreen.upgrade_deps_block_check('0640', :eg_version);
5353 -- AFTER UPDATE OR INSERT trigger for authority.record_entry
5354 CREATE OR REPLACE FUNCTION authority.indexing_ingest_or_delete () RETURNS TRIGGER AS $func$
5357 IF NEW.deleted IS TRUE THEN -- If this authority is deleted
5358 DELETE FROM authority.bib_linking WHERE authority = NEW.id; -- Avoid updating fields in bibs that are no longer visible
5359 DELETE FROM authority.full_rec WHERE record = NEW.id; -- Avoid validating fields against deleted authority records
5360 DELETE FROM authority.simple_heading WHERE record = NEW.id;
5361 -- Should remove matching $0 from controlled fields at the same time?
5362 RETURN NEW; -- and we're done
5365 IF TG_OP = 'UPDATE' THEN -- re-ingest?
5366 PERFORM * FROM config.internal_flag WHERE name = 'ingest.reingest.force_on_same_marc' AND enabled;
5368 IF NOT FOUND AND OLD.marc = NEW.marc THEN -- don't do anything if the MARC didn't change
5372 -- Propagate these updates to any linked bib records
5373 PERFORM authority.propagate_changes(NEW.id) FROM authority.record_entry WHERE id = NEW.id;
5375 DELETE FROM authority.simple_heading WHERE record = NEW.id;
5378 INSERT INTO authority.simple_heading (record,atag,value,sort_value)
5379 SELECT record, atag, value, sort_value FROM authority.simple_heading_set(NEW.marc);
5381 -- Flatten and insert the afr data
5382 PERFORM * FROM config.internal_flag WHERE name = 'ingest.disable_authority_full_rec' AND enabled;
5384 PERFORM authority.reingest_authority_full_rec(NEW.id);
5385 PERFORM * FROM config.internal_flag WHERE name = 'ingest.disable_authority_rec_descriptor' AND enabled;
5387 PERFORM authority.reingest_authority_rec_descriptor(NEW.id);
5393 $func$ LANGUAGE PLPGSQL;
5395 -- Entries that need to respect an NFI
5396 UPDATE authority.control_set_authority_field SET nfi = '2'
5397 WHERE id IN (4,24,44,64);
5399 DROP TRIGGER authority_full_rec_fti_trigger ON authority.full_rec;
5400 CREATE TRIGGER authority_full_rec_fti_trigger
5401 BEFORE UPDATE OR INSERT ON authority.full_rec
5402 FOR EACH ROW EXECUTE PROCEDURE oils_tsearch2('keyword');
5404 CREATE OR REPLACE FUNCTION authority.normalize_heading( marcxml TEXT, no_thesaurus BOOL ) RETURNS TEXT AS $func$
5406 acsaf authority.control_set_authority_field%ROWTYPE;
5415 auth_id INT DEFAULT oils_xpath_string('//*[@tag="901"]/*[local-name()="subfield" and @code="c"]', marcxml)::INT;
5417 SELECT control_set INTO cset FROM authority.record_entry WHERE id = auth_id;
5419 IF cset IS NULL THEN
5420 SELECT control_set INTO cset
5421 FROM authority.control_set_authority_field
5422 WHERE tag IN ( SELECT UNNEST(XPATH('//*[starts-with(@tag,"1")]/@tag',marcxml::XML)::TEXT[]))
5426 thes_code := vandelay.marc21_extract_fixed_field(marcxml,'Subj');
5427 IF thes_code IS NULL THEN
5429 ELSIF thes_code = 'z' THEN
5430 thes_code := COALESCE( oils_xpath_string('//*[@tag="040"]/*[@code="f"][1]', marcxml), '' );
5434 FOR acsaf IN SELECT * FROM authority.control_set_authority_field WHERE control_set = cset AND main_entry IS NULL LOOP
5435 tag_used := acsaf.tag;
5436 nfi_used := acsaf.nfi;
5438 FOR sf IN SELECT * FROM regexp_split_to_table(acsaf.sf_list,'') LOOP
5439 tmp_text := oils_xpath_string('//*[@tag="'||tag_used||'"]/*[@code="'||sf||'"]', marcxml);
5441 IF first_sf AND tmp_text IS NOT NULL AND nfi_used IS NOT NULL THEN
5443 tmp_text := SUBSTRING(
5448 oils_xpath_string('//*[@tag="'||tag_used||'"]/@ind'||nfi_used, marcxml),
5463 IF tmp_text IS NOT NULL AND tmp_text <> '' THEN
5464 heading_text := heading_text || E'\u2021' || sf || ' ' || tmp_text;
5467 EXIT WHEN heading_text <> '';
5470 IF heading_text <> '' THEN
5471 IF no_thesaurus IS TRUE THEN
5472 heading_text := tag_used || ' ' || public.naco_normalize(heading_text);
5474 heading_text := tag_used || '_' || COALESCE(nfi_used,'-') || '_' || thes_code || ' ' || public.naco_normalize(heading_text);
5477 heading_text := 'NOHEADING_' || thes_code || ' ' || MD5(marcxml);
5480 RETURN heading_text;
5482 $func$ LANGUAGE PLPGSQL IMMUTABLE;
5484 CREATE OR REPLACE FUNCTION authority.simple_normalize_heading( marcxml TEXT ) RETURNS TEXT AS $func$
5485 SELECT authority.normalize_heading($1, TRUE);
5486 $func$ LANGUAGE SQL IMMUTABLE;
5488 CREATE OR REPLACE FUNCTION authority.normalize_heading( marcxml TEXT ) RETURNS TEXT AS $func$
5489 SELECT authority.normalize_heading($1, FALSE);
5490 $func$ LANGUAGE SQL IMMUTABLE;
5493 CREATE TABLE authority.simple_heading (
5494 id BIGSERIAL PRIMARY KEY,
5495 record BIGINT NOT NULL REFERENCES authority.record_entry (id),
5496 atag INT NOT NULL REFERENCES authority.control_set_authority_field (id),
5497 value TEXT NOT NULL,
5498 sort_value TEXT NOT NULL,
5499 index_vector tsvector NOT NULL
5501 CREATE TRIGGER authority_simple_heading_fti_trigger
5502 BEFORE UPDATE OR INSERT ON authority.simple_heading
5503 FOR EACH ROW EXECUTE PROCEDURE oils_tsearch2('keyword');
5505 CREATE INDEX authority_simple_heading_index_vector_idx ON authority.simple_heading USING GIST (index_vector);
5506 CREATE INDEX authority_simple_heading_value_idx ON authority.simple_heading (value);
5507 CREATE INDEX authority_simple_heading_sort_value_idx ON authority.simple_heading (sort_value);
5509 CREATE OR REPLACE FUNCTION authority.simple_heading_set( marcxml TEXT ) RETURNS SETOF authority.simple_heading AS $func$
5511 res authority.simple_heading%ROWTYPE;
5512 acsaf authority.control_set_authority_field%ROWTYPE;
5522 auth_id INT DEFAULT oils_xpath_string('//*[@tag="901"]/*[local-name()="subfield" and @code="c"]', marcxml)::INT;
5525 res.record := auth_id;
5527 SELECT control_set INTO cset
5528 FROM authority.control_set_authority_field
5529 WHERE tag IN ( SELECT UNNEST(XPATH('//*[starts-with(@tag,"1")]/@tag',marcxml::XML)::TEXT[]) )
5532 FOR acsaf IN SELECT * FROM authority.control_set_authority_field WHERE control_set = cset LOOP
5534 res.atag := acsaf.id;
5535 tag_used := acsaf.tag;
5536 nfi_used := acsaf.nfi;
5538 FOR tmp_xml IN SELECT UNNEST(XPATH('//*[@tag="'||tag_used||'"]', marcxml::XML)) LOOP
5541 FOR sf IN SELECT * FROM regexp_split_to_table(acsaf.sf_list,'') LOOP
5542 heading_text := heading_text || COALESCE( ' ' || oils_xpath_string('//*[@code="'||sf||'"]',tmp_xml::TEXT), '');
5545 heading_text := public.naco_normalize(heading_text);
5547 IF nfi_used IS NOT NULL THEN
5549 sort_text := SUBSTRING(
5554 oils_xpath_string('//*[@tag="'||tag_used||'"]/@ind'||nfi_used, marcxml),
5566 sort_text := heading_text;
5569 IF heading_text IS NOT NULL AND heading_text <> '' THEN
5570 res.value := heading_text;
5571 res.sort_value := sort_text;
5581 $func$ LANGUAGE PLPGSQL IMMUTABLE;
5583 -- Support function used to find the pivot for alpha-heading-browse style searching
5584 CREATE OR REPLACE FUNCTION authority.simple_heading_find_pivot( a INT[], q TEXT ) RETURNS TEXT AS $$
5586 sort_value_row RECORD;
5591 t_term := public.naco_normalize(q);
5593 SELECT CASE WHEN ash.sort_value LIKE t_term || '%' THEN 1 ELSE 0 END
5594 + CASE WHEN ash.value LIKE t_term || '%' THEN 1 ELSE 0 END AS rank,
5597 FROM authority.simple_heading ash
5598 WHERE ash.atag = ANY (a)
5599 AND ash.sort_value >= t_term
5600 ORDER BY rank DESC, ash.sort_value
5603 SELECT CASE WHEN ash.sort_value LIKE t_term || '%' THEN 1 ELSE 0 END
5604 + CASE WHEN ash.value LIKE t_term || '%' THEN 1 ELSE 0 END AS rank,
5607 FROM authority.simple_heading ash
5608 WHERE ash.atag = ANY (a)
5609 AND ash.value >= t_term
5610 ORDER BY rank DESC, ash.sort_value
5613 IF value_row.rank > sort_value_row.rank THEN
5614 RETURN value_row.sort_value;
5616 RETURN sort_value_row.sort_value;
5619 $$ LANGUAGE PLPGSQL;
5622 CREATE OR REPLACE FUNCTION authority.simple_heading_browse_center( atag_list INT[], q TEXT, page INT DEFAULT 0, pagesize INT DEFAULT 9 ) RETURNS SETOF BIGINT AS $$
5624 pivot_sort_value TEXT;
5625 boffset INT DEFAULT 0;
5626 aoffset INT DEFAULT 0;
5627 blimit INT DEFAULT 0;
5628 alimit INT DEFAULT 0;
5631 pivot_sort_value := authority.simple_heading_find_pivot(atag_list,q);
5634 blimit := pagesize / 2;
5637 IF pagesize % 2 <> 0 THEN
5638 alimit := alimit + 1;
5644 boffset := pagesize / 2;
5647 IF pagesize % 2 <> 0 THEN
5648 boffset := boffset + 1;
5654 -- "bottom" half of the browse results
5657 row_number() over ()
5658 FROM authority.simple_heading ash
5659 WHERE ash.atag = ANY (atag_list)
5660 AND ash.sort_value < pivot_sort_value
5661 ORDER BY ash.sort_value DESC
5663 OFFSET ABS(page) * pagesize - boffset
5664 ) x ORDER BY row_number DESC;
5669 -- "bottom" half of the browse results
5671 FROM authority.simple_heading ash
5672 WHERE ash.atag = ANY (atag_list)
5673 AND ash.sort_value >= pivot_sort_value
5674 ORDER BY ash.sort_value
5676 OFFSET ABS(page) * pagesize - aoffset;
5679 $$ LANGUAGE PLPGSQL ROWS 10;
5681 CREATE OR REPLACE FUNCTION authority.simple_heading_browse_top( atag_list INT[], q TEXT, page INT DEFAULT 0, pagesize INT DEFAULT 10 ) RETURNS SETOF BIGINT AS $$
5683 pivot_sort_value TEXT;
5686 pivot_sort_value := authority.simple_heading_find_pivot(atag_list,q);
5690 -- "bottom" half of the browse results
5693 row_number() over ()
5694 FROM authority.simple_heading ash
5695 WHERE ash.atag = ANY (atag_list)
5696 AND ash.sort_value < pivot_sort_value
5697 ORDER BY ash.sort_value DESC
5699 OFFSET (ABS(page) - 1) * pagesize
5700 ) x ORDER BY row_number DESC;
5705 -- "bottom" half of the browse results
5707 FROM authority.simple_heading ash
5708 WHERE ash.atag = ANY (atag_list)
5709 AND ash.sort_value >= pivot_sort_value
5710 ORDER BY ash.sort_value
5712 OFFSET ABS(page) * pagesize ;
5715 $$ LANGUAGE PLPGSQL ROWS 10;
5717 CREATE OR REPLACE FUNCTION authority.simple_heading_search_rank( atag_list INT[], q TEXT, page INT DEFAULT 0, pagesize INT DEFAULT 10 ) RETURNS SETOF BIGINT AS $$
5719 FROM authority.simple_heading ash,
5720 public.naco_normalize($2) t(term),
5721 plainto_tsquery('keyword'::regconfig,$2) ptsq(term)
5722 WHERE ash.atag = ANY ($1)
5723 AND ash.index_vector @@ ptsq.term
5724 ORDER BY ts_rank_cd(ash.index_vector,ptsq.term,14)::numeric
5725 + CASE WHEN ash.sort_value LIKE t.term || '%' THEN 2 ELSE 0 END
5726 + CASE WHEN ash.value LIKE t.term || '%' THEN 1 ELSE 0 END DESC
5729 $$ LANGUAGE SQL ROWS 10;
5731 CREATE OR REPLACE FUNCTION authority.simple_heading_search_heading( atag_list INT[], q TEXT, page INT DEFAULT 0, pagesize INT DEFAULT 10 ) RETURNS SETOF BIGINT AS $$
5733 FROM authority.simple_heading ash,
5734 public.naco_normalize($2) t(term),
5735 plainto_tsquery('keyword'::regconfig,$2) ptsq(term)
5736 WHERE ash.atag = ANY ($1)
5737 AND ash.index_vector @@ ptsq.term
5738 ORDER BY ash.sort_value
5741 $$ LANGUAGE SQL ROWS 10;
5744 CREATE OR REPLACE FUNCTION authority.axis_authority_tags(a TEXT) RETURNS INT[] AS $$
5745 SELECT ARRAY_ACCUM(field) FROM authority.browse_axis_authority_field_map WHERE axis = $1;
5748 CREATE OR REPLACE FUNCTION authority.axis_authority_tags_refs(a TEXT) RETURNS INT[] AS $$
5751 (SELECT ARRAY_ACCUM(x.id) FROM authority.control_set_authority_field x WHERE x.main_entry = a.field)
5753 FROM authority.browse_axis_authority_field_map a
5759 CREATE OR REPLACE FUNCTION authority.btag_authority_tags(btag TEXT) RETURNS INT[] AS $$
5760 SELECT ARRAY_ACCUM(authority_field) FROM authority.control_set_bib_field WHERE tag = $1
5763 CREATE OR REPLACE FUNCTION authority.btag_authority_tags_refs(btag TEXT) RETURNS INT[] AS $$
5765 ARRAY[a.authority_field],
5766 (SELECT ARRAY_ACCUM(x.id) FROM authority.control_set_authority_field x WHERE x.main_entry = a.authority_field)
5768 FROM authority.control_set_bib_field a
5774 CREATE OR REPLACE FUNCTION authority.atag_authority_tags(atag TEXT) RETURNS INT[] AS $$
5775 SELECT ARRAY_ACCUM(id) FROM authority.control_set_authority_field WHERE tag = $1
5778 CREATE OR REPLACE FUNCTION authority.atag_authority_tags_refs(atag TEXT) RETURNS INT[] AS $$
5781 (SELECT ARRAY_ACCUM(x.id) FROM authority.control_set_authority_field x WHERE x.main_entry = a.id)
5783 FROM authority.control_set_authority_field a
5788 CREATE OR REPLACE FUNCTION authority.axis_browse_center( a TEXT, q TEXT, page INT DEFAULT 0, pagesize INT DEFAULT 9 ) RETURNS SETOF BIGINT AS $$
5789 SELECT * FROM authority.simple_heading_browse_center(authority.axis_authority_tags($1), $2, $3, $4)
5790 $$ LANGUAGE SQL ROWS 10;
5792 CREATE OR REPLACE FUNCTION authority.btag_browse_center( a TEXT, q TEXT, page INT DEFAULT 0, pagesize INT DEFAULT 9 ) RETURNS SETOF BIGINT AS $$
5793 SELECT * FROM authority.simple_heading_browse_center(authority.btag_authority_tags($1), $2, $3, $4)
5794 $$ LANGUAGE SQL ROWS 10;
5796 CREATE OR REPLACE FUNCTION authority.atag_browse_center( a TEXT, q TEXT, page INT DEFAULT 0, pagesize INT DEFAULT 9 ) RETURNS SETOF BIGINT AS $$
5797 SELECT * FROM authority.simple_heading_browse_center(authority.atag_authority_tags($1), $2, $3, $4)
5798 $$ LANGUAGE SQL ROWS 10;
5800 CREATE OR REPLACE FUNCTION authority.axis_browse_center_refs( a TEXT, q TEXT, page INT DEFAULT 0, pagesize INT DEFAULT 9 ) RETURNS SETOF BIGINT AS $$
5801 SELECT * FROM authority.simple_heading_browse_center(authority.axis_authority_tags_refs($1), $2, $3, $4)
5802 $$ LANGUAGE SQL ROWS 10;
5804 CREATE OR REPLACE FUNCTION authority.btag_browse_center_refs( a TEXT, q TEXT, page INT DEFAULT 0, pagesize INT DEFAULT 9 ) RETURNS SETOF BIGINT AS $$
5805 SELECT * FROM authority.simple_heading_browse_center(authority.btag_authority_tags_refs($1), $2, $3, $4)
5806 $$ LANGUAGE SQL ROWS 10;
5808 CREATE OR REPLACE FUNCTION authority.atag_browse_center_refs( a TEXT, q TEXT, page INT DEFAULT 0, pagesize INT DEFAULT 9 ) RETURNS SETOF BIGINT AS $$
5809 SELECT * FROM authority.simple_heading_browse_center(authority.atag_authority_tags_refs($1), $2, $3, $4)
5810 $$ LANGUAGE SQL ROWS 10;
5813 CREATE OR REPLACE FUNCTION authority.axis_browse_top( a TEXT, q TEXT, page INT DEFAULT 0, pagesize INT DEFAULT 10 ) RETURNS SETOF BIGINT AS $$
5814 SELECT * FROM authority.simple_heading_browse_top(authority.axis_authority_tags($1), $2, $3, $4)
5815 $$ LANGUAGE SQL ROWS 10;
5817 CREATE OR REPLACE FUNCTION authority.btag_browse_top( a TEXT, q TEXT, page INT DEFAULT 0, pagesize INT DEFAULT 10 ) RETURNS SETOF BIGINT AS $$
5818 SELECT * FROM authority.simple_heading_browse_top(authority.btag_authority_tags($1), $2, $3, $4)
5819 $$ LANGUAGE SQL ROWS 10;
5821 CREATE OR REPLACE FUNCTION authority.atag_browse_top( a TEXT, q TEXT, page INT DEFAULT 0, pagesize INT DEFAULT 10 ) RETURNS SETOF BIGINT AS $$
5822 SELECT * FROM authority.simple_heading_browse_top(authority.atag_authority_tags($1), $2, $3, $4)
5823 $$ LANGUAGE SQL ROWS 10;
5825 CREATE OR REPLACE FUNCTION authority.axis_browse_top_refs( a TEXT, q TEXT, page INT DEFAULT 0, pagesize INT DEFAULT 10 ) RETURNS SETOF BIGINT AS $$
5826 SELECT * FROM authority.simple_heading_browse_top(authority.axis_authority_tags_refs($1), $2, $3, $4)
5827 $$ LANGUAGE SQL ROWS 10;
5829 CREATE OR REPLACE FUNCTION authority.btag_browse_top_refs( a TEXT, q TEXT, page INT DEFAULT 0, pagesize INT DEFAULT 10 ) RETURNS SETOF BIGINT AS $$
5830 SELECT * FROM authority.simple_heading_browse_top(authority.btag_authority_tags_refs($1), $2, $3, $4)
5831 $$ LANGUAGE SQL ROWS 10;
5833 CREATE OR REPLACE FUNCTION authority.atag_browse_top_refs( a TEXT, q TEXT, page INT DEFAULT 0, pagesize INT DEFAULT 10 ) RETURNS SETOF BIGINT AS $$
5834 SELECT * FROM authority.simple_heading_browse_top(authority.atag_authority_tags_refs($1), $2, $3, $4)
5835 $$ LANGUAGE SQL ROWS 10;
5838 CREATE OR REPLACE FUNCTION authority.axis_search_rank( a TEXT, q TEXT, page INT DEFAULT 0, pagesize INT DEFAULT 10 ) RETURNS SETOF BIGINT AS $$
5839 SELECT * FROM authority.simple_heading_search_rank(authority.axis_authority_tags($1), $2, $3, $4)
5840 $$ LANGUAGE SQL ROWS 10;
5842 CREATE OR REPLACE FUNCTION authority.btag_search_rank( a TEXT, q TEXT, page INT DEFAULT 0, pagesize INT DEFAULT 10 ) RETURNS SETOF BIGINT AS $$
5843 SELECT * FROM authority.simple_heading_search_rank(authority.btag_authority_tags($1), $2, $3, $4)
5844 $$ LANGUAGE SQL ROWS 10;
5846 CREATE OR REPLACE FUNCTION authority.atag_search_rank( a TEXT, q TEXT, page INT DEFAULT 0, pagesize INT DEFAULT 10 ) RETURNS SETOF BIGINT AS $$
5847 SELECT * FROM authority.simple_heading_search_rank(authority.atag_authority_tags($1), $2, $3, $4)
5848 $$ LANGUAGE SQL ROWS 10;
5850 CREATE OR REPLACE FUNCTION authority.axis_search_rank_refs( a TEXT, q TEXT, page INT DEFAULT 0, pagesize INT DEFAULT 10 ) RETURNS SETOF BIGINT AS $$
5851 SELECT * FROM authority.simple_heading_search_rank(authority.axis_authority_tags_refs($1), $2, $3, $4)
5852 $$ LANGUAGE SQL ROWS 10;
5854 CREATE OR REPLACE FUNCTION authority.btag_search_rank_refs( a TEXT, q TEXT, page INT DEFAULT 0, pagesize INT DEFAULT 10 ) RETURNS SETOF BIGINT AS $$
5855 SELECT * FROM authority.simple_heading_search_rank(authority.btag_authority_tags_refs($1), $2, $3, $4)
5856 $$ LANGUAGE SQL ROWS 10;
5858 CREATE OR REPLACE FUNCTION authority.atag_search_rank_refs( a TEXT, q TEXT, page INT DEFAULT 0, pagesize INT DEFAULT 10 ) RETURNS SETOF BIGINT AS $$
5859 SELECT * FROM authority.simple_heading_search_rank(authority.atag_authority_tags_refs($1), $2, $3, $4)
5860 $$ LANGUAGE SQL ROWS 10;
5863 CREATE OR REPLACE FUNCTION authority.axis_search_heading( a TEXT, q TEXT, page INT DEFAULT 0, pagesize INT DEFAULT 10 ) RETURNS SETOF BIGINT AS $$
5864 SELECT * FROM authority.simple_heading_search_heading(authority.axis_authority_tags($1), $2, $3, $4)
5865 $$ LANGUAGE SQL ROWS 10;
5867 CREATE OR REPLACE FUNCTION authority.btag_search_heading( a TEXT, q TEXT, page INT DEFAULT 0, pagesize INT DEFAULT 10 ) RETURNS SETOF BIGINT AS $$
5868 SELECT * FROM authority.simple_heading_search_heading(authority.btag_authority_tags($1), $2, $3, $4)
5869 $$ LANGUAGE SQL ROWS 10;
5871 CREATE OR REPLACE FUNCTION authority.atag_search_heading( a TEXT, q TEXT, page INT DEFAULT 0, pagesize INT DEFAULT 10 ) RETURNS SETOF BIGINT AS $$
5872 SELECT * FROM authority.simple_heading_search_heading(authority.atag_authority_tags($1), $2, $3, $4)
5873 $$ LANGUAGE SQL ROWS 10;
5875 CREATE OR REPLACE FUNCTION authority.axis_search_heading_refs( a TEXT, q TEXT, page INT DEFAULT 0, pagesize INT DEFAULT 10 ) RETURNS SETOF BIGINT AS $$
5876 SELECT * FROM authority.simple_heading_search_heading(authority.axis_authority_tags_refs($1), $2, $3, $4)
5877 $$ LANGUAGE SQL ROWS 10;
5879 CREATE OR REPLACE FUNCTION authority.btag_search_heading_refs( a TEXT, q TEXT, page INT DEFAULT 0, pagesize INT DEFAULT 10 ) RETURNS SETOF BIGINT AS $$
5880 SELECT * FROM authority.simple_heading_search_heading(authority.btag_authority_tags_refs($1), $2, $3, $4)
5881 $$ LANGUAGE SQL ROWS 10;
5883 CREATE OR REPLACE FUNCTION authority.atag_search_heading_refs( a TEXT, q TEXT, page INT DEFAULT 0, pagesize INT DEFAULT 10 ) RETURNS SETOF BIGINT AS $$
5884 SELECT * FROM authority.simple_heading_search_heading(authority.atag_authority_tags_refs($1), $2, $3, $4)
5885 $$ LANGUAGE SQL ROWS 10;
5889 -- Evergreen DB patch 0641.schema.org_unit_setting_json_check.sql
5893 -- check whether patch can be applied
5894 SELECT evergreen.upgrade_deps_block_check('0641', :eg_version);
5896 ALTER TABLE actor.org_unit_setting ADD CONSTRAINT aous_must_be_json CHECK ( is_json(value) );
5898 -- Evergreen DB patch 0642.data.acq-worksheet-hold-count.sql
5900 -- check whether patch can be applied
5901 SELECT evergreen.upgrade_deps_block_check('0642', :eg_version);
5903 UPDATE action_trigger.event_definition SET template =
5906 [%- SET li = target; -%]
5907 <div class="wrapper">
5908 <div class="summary" style='font-size:110%; font-weight:bold;'>
5910 <div>Title: [% helpers.get_li_attr("title", "", li.attributes) %]</div>
5911 <div>Author: [% helpers.get_li_attr("author", "", li.attributes) %]</div>
5912 <div class="count">Item Count: [% li.lineitem_details.size %]</div>
5913 <div class="lineid">Lineitem ID: [% li.id %]</div>
5914 <div>Open Holds: [% helpers.bre_open_hold_count(li.eg_bib_id) %]</div>
5916 [% IF li.distribution_formulas.size > 0 %]
5917 [% SET forms = [] %]
5918 [% FOREACH form IN li.distribution_formulas; forms.push(form.formula.name); END %]
5919 <div>Distribution Formulas: [% forms.join(',') %]</div>
5922 [% IF li.lineitem_notes.size > 0 %]
5925 [%- FOR note IN li.lineitem_notes -%]
5927 [% IF note.alert_text %]
5928 [% note.alert_text.code -%]
5929 [% IF note.value -%]
5946 <th>Call Number</th>
5948 <th>Shelving Location</th>
5954 [% FOREACH detail IN li.lineitem_details.sort('owning_lib') %]
5956 IF detail.eg_copy_id;
5957 SET copy = detail.eg_copy_id;
5958 SET cn_label = copy.call_number.label;
5961 SET cn_label = detail.cn_label;
5965 <!-- acq.lineitem_detail.id = [%- detail.id -%] -->
5966 <td style='padding:5px;'>[% detail.owning_lib.shortname %]</td>
5967 <td style='padding:5px;'>[% IF copy.barcode %]<span class="barcode" >[% detail.barcode %]</span>[% END %]</td>
5968 <td style='padding:5px;'>[% IF cn_label %]<span class="cn_label" >[% cn_label %]</span>[% END %]</td>
5969 <td style='padding:5px;'>[% IF detail.fund %]<span class="fund">[% detail.fund.code %] ([% detail.fund.year %])</span>[% END %]</td>
5970 <td style='padding:5px;'>[% copy.location.name %]</td>
5971 <td style='padding:5px;'>[% IF detail.recv_time %]<span class="recv_time">[% detail.recv_time %]</span>[% END %]</td>
5972 <td style='padding:5px;'>[% detail.note %]</td>
5982 SELECT evergreen.upgrade_deps_block_check('0643', :eg_version);
5990 FROM authority.record_entry
5993 AND id NOT IN (SELECT DISTINCT record FROM authority.simple_heading)
5995 INSERT INTO authority.simple_heading (record,atag,value,sort_value)
5996 SELECT record, atag, value, sort_value FROM authority.simple_heading_set(x);
6003 SELECT evergreen.upgrade_deps_block_check('0644', :eg_version);
6005 INSERT into config.org_unit_setting_type (name, grp, label, description, datatype) VALUES
6006 ( 'circ.holds.target_when_closed', 'circ',
6007 oils_i18n_gettext('circ.holds.target_when_closed',
6008 'Target copies for a hold even if copy''s circ lib is closed',
6010 oils_i18n_gettext('circ.holds.target_when_closed',
6011 'If this setting is true at a given org unit or one of its ancestors, the hold targeter will target copies from this org unit even if the org unit is closed (according to the actor.org_unit.closed_date table).',
6012 'coust', 'description'),
6014 ( 'circ.holds.target_when_closed_if_at_pickup_lib', 'circ',
6015 oils_i18n_gettext('circ.holds.target_when_closed_if_at_pickup_lib',
6016 'Target copies for a hold even if copy''s circ lib is closed IF the circ lib is the hold''s pickup lib',
6018 oils_i18n_gettext('circ.holds.target_when_closed_if_at_pickup_lib',
6019 'If this setting is true at a given org unit or one of its ancestors, the hold targeter will target copies from this org unit even if the org unit is closed (according to the actor.org_unit.closed_date table) IF AND ONLY IF the copy''s circ lib is the same as the hold''s pickup lib.',
6020 'coust', 'description'),
6024 -- Evergreen DB patch XXXX.data.hold-notification-cleanup-mod.sql
6026 -- check whether patch can be applied
6027 SELECT evergreen.upgrade_deps_block_check('0647', :eg_version);
6029 INSERT INTO action_trigger.cleanup ( module, description ) VALUES (
6030 'CreateHoldNotification',
6032 'CreateHoldNotification',
6033 'Creates a hold_notification record for each notified hold',
6039 UPDATE action_trigger.event_definition
6041 cleanup_success = 'CreateHoldNotification'
6043 id = 5 -- stock hold-ready email event_def
6044 AND cleanup_success IS NULL; -- don't clobber any existing cleanup mod
6046 -- Evergreen DB patch XXXX.schema.unnest-hold-permit-upgrade-script-repair.sql
6048 -- This patch makes no changes to the baseline schema and is
6049 -- only meant to repair a previous upgrade script.
6052 -- check whether patch can be applied
6053 SELECT evergreen.upgrade_deps_block_check('0651', :eg_version);
6055 --Removed dupe action.hold_request_permit_test
6057 -- Evergreen DB patch XXXX.data.vandelay-queue-bib-bucket-type.sql
6060 -- check whether patch can be applied
6061 SELECT evergreen.upgrade_deps_block_check('0652', :eg_version);
6063 INSERT INTO container.biblio_record_entry_bucket_type (code, label) VALUES (
6065 oils_i18n_gettext('vandelay_queue', 'Vandelay Queue', 'cbrebt', 'label')
6068 -- Evergreen DB patch XXXX.schema.unapi-indb-optional-org.sql
6070 -- check whether patch can be applied
6071 SELECT evergreen.upgrade_deps_block_check('0653', :eg_version);
6073 CREATE OR REPLACE FUNCTION evergreen.org_top() RETURNS SETOF actor.org_unit AS $$ SELECT * FROM actor.org_unit WHERE parent_ou IS NULL LIMIT 1; $$ LANGUAGE SQL ROWS 1;
6075 CREATE OR REPLACE FUNCTION unapi.biblio_record_entry_feed ( id_list BIGINT[], format TEXT, includes TEXT[], org TEXT DEFAULT '-', depth INT DEFAULT NULL, slimit INT DEFAULT NULL, soffset INT DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE, title TEXT DEFAULT NULL, description TEXT DEFAULT NULL, creator TEXT DEFAULT NULL, update_ts TEXT DEFAULT NULL, unapi_url TEXT DEFAULT NULL, header_xml XML DEFAULT NULL ) RETURNS XML AS $F$
6077 layout unapi.bre_output_layout%ROWTYPE;
6078 transform config.xml_transform%ROWTYPE;
6081 xmlns_uri TEXT := 'http://open-ils.org/spec/feed-xml/v1';
6083 element_list TEXT[];
6086 IF org = '-' OR org IS NULL THEN
6087 SELECT shortname INTO org FROM evergreen.org_top();
6090 SELECT id INTO ouid FROM actor.org_unit WHERE shortname = org;
6091 SELECT * INTO layout FROM unapi.bre_output_layout WHERE name = format;
6093 IF layout.name IS NULL THEN
6097 SELECT * INTO transform FROM config.xml_transform WHERE name = layout.transform;
6098 xmlns_uri := COALESCE(transform.namespace_uri,xmlns_uri);
6100 -- Gather the bib xml
6101 SELECT XMLAGG( unapi.bre(i, format, '', includes, org, depth, slimit, soffset, include_xmlns)) INTO tmp_xml FROM UNNEST( id_list ) i;
6103 IF layout.title_element IS NOT NULL THEN
6104 EXECUTE 'SELECT XMLCONCAT( XMLELEMENT( name '|| layout.title_element ||', CASE WHEN $4 THEN XMLATTRIBUTES( $1 AS xmlns) ELSE NULL END, $3), $2)' INTO tmp_xml USING xmlns_uri, tmp_xml::XML, title, include_xmlns;
6107 IF layout.description_element IS NOT NULL THEN
6108 EXECUTE 'SELECT XMLCONCAT( XMLELEMENT( name '|| layout.description_element ||', CASE WHEN $4 THEN XMLATTRIBUTES( $1 AS xmlns) ELSE NULL END, $3), $2)' INTO tmp_xml USING xmlns_uri, tmp_xml::XML, description, include_xmlns;
6111 IF layout.creator_element IS NOT NULL THEN
6112 EXECUTE 'SELECT XMLCONCAT( XMLELEMENT( name '|| layout.creator_element ||', CASE WHEN $4 THEN XMLATTRIBUTES( $1 AS xmlns) ELSE NULL END, $3), $2)' INTO tmp_xml USING xmlns_uri, tmp_xml::XML, creator, include_xmlns;
6115 IF layout.update_ts_element IS NOT NULL THEN
6116 EXECUTE 'SELECT XMLCONCAT( XMLELEMENT( name '|| layout.update_ts_element ||', CASE WHEN $4 THEN XMLATTRIBUTES( $1 AS xmlns) ELSE NULL END, $3), $2)' INTO tmp_xml USING xmlns_uri, tmp_xml::XML, update_ts, include_xmlns;
6119 IF unapi_url IS NOT NULL THEN
6120 EXECUTE $$SELECT XMLCONCAT( XMLELEMENT( name link, XMLATTRIBUTES( 'http://www.w3.org/1999/xhtml' AS xmlns, 'unapi-server' AS rel, $1 AS href, 'unapi' AS title)), $2)$$ INTO tmp_xml USING unapi_url, tmp_xml::XML;
6123 IF header_xml IS NOT NULL THEN tmp_xml := XMLCONCAT(header_xml,tmp_xml::XML); END IF;
6125 element_list := regexp_split_to_array(layout.feed_top,E'\\.');
6126 FOR i IN REVERSE ARRAY_UPPER(element_list, 1) .. 1 LOOP
6127 EXECUTE 'SELECT XMLELEMENT( name '|| quote_ident(element_list[i]) ||', CASE WHEN $4 THEN XMLATTRIBUTES( $1 AS xmlns) ELSE NULL END, $2)' INTO tmp_xml USING xmlns_uri, tmp_xml::XML, include_xmlns;
6130 RETURN tmp_xml::XML;
6132 $F$ LANGUAGE PLPGSQL;
6134 CREATE OR REPLACE FUNCTION unapi.bre ( obj_id BIGINT, format TEXT, ename TEXT, includes TEXT[], org TEXT DEFAULT '-', depth INT DEFAULT NULL, slimit INT DEFAULT NULL, soffset INT DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$
6136 me biblio.record_entry%ROWTYPE;
6137 layout unapi.bre_output_layout%ROWTYPE;
6138 xfrm config.xml_transform%ROWTYPE;
6147 IF org = '-' OR org IS NULL THEN
6148 SELECT shortname INTO org FROM evergreen.org_top();
6151 SELECT id INTO ouid FROM actor.org_unit WHERE shortname = org;
6153 IF ouid IS NULL THEN
6157 IF format = 'holdings_xml' THEN -- the special case
6158 output := unapi.holdings_xml( obj_id, ouid, org, depth, includes, slimit, soffset, include_xmlns);
6162 SELECT * INTO layout FROM unapi.bre_output_layout WHERE name = format;
6164 IF layout.name IS NULL THEN
6168 SELECT * INTO xfrm FROM config.xml_transform WHERE name = layout.transform;
6170 SELECT * INTO me FROM biblio.record_entry WHERE id = obj_id;
6172 -- grab SVF if we need them
6173 IF ('mra' = ANY (includes)) THEN
6174 axml := unapi.mra(obj_id,NULL,NULL,NULL,NULL);
6179 -- grab hodlings if we need them
6180 IF ('holdings_xml' = ANY (includes)) THEN
6181 hxml := unapi.holdings_xml(obj_id, ouid, org, depth, evergreen.array_remove_item_by_value(includes,'holdings_xml'), slimit, soffset, include_xmlns);
6187 -- generate our item node
6190 IF format = 'marcxml' THEN
6192 IF tmp_xml !~ E'<marc:' THEN -- If we're not using the prefixed namespace in this record, then remove all declarations of it
6193 tmp_xml := REGEXP_REPLACE(tmp_xml, ' xmlns:marc="http://www.loc.gov/MARC21/slim"', '', 'g');
6196 tmp_xml := oils_xslt_process(me.marc, xfrm.xslt)::XML;
6199 top_el := REGEXP_REPLACE(tmp_xml, E'^.*?<((?:\\S+:)?' || layout.holdings_element || ').*$', E'\\1');
6201 IF axml IS NOT NULL THEN
6202 tmp_xml := REGEXP_REPLACE(tmp_xml, '</' || top_el || '>(.*?)$', axml || '</' || top_el || E'>\\1');
6205 IF hxml IS NOT NULL THEN -- XXX how do we configure the holdings position?
6206 tmp_xml := REGEXP_REPLACE(tmp_xml, '</' || top_el || '>(.*?)$', hxml || '</' || top_el || E'>\\1');
6209 IF ('bre.unapi' = ANY (includes)) THEN
6210 output := REGEXP_REPLACE(
6212 '</' || top_el || '>(.*?)',
6216 'http://www.w3.org/1999/xhtml' AS xmlns,
6217 'unapi-id' AS class,
6218 'tag:open-ils.org:U2@bre/' || obj_id || '/' || org AS title
6220 )::TEXT || '</' || top_el || E'>\\1'
6226 output := REGEXP_REPLACE(output::TEXT,E'>\\s+<','><','gs')::XML;
6229 $F$ LANGUAGE PLPGSQL;
6234 SELECT evergreen.upgrade_deps_block_check('0654', :eg_version);
6236 INSERT INTO permission.perm_list ( id, code, description ) VALUES
6237 ( 514, 'UPDATE_PATRON_ACTIVE_CARD', oils_i18n_gettext( 514,
6238 'Allows a user to manually adjust a patron''s active cards', 'ppl', 'description')),
6239 ( 515, 'UPDATE_PATRON_PRIMARY_CARD', oils_i18n_gettext( 515,
6240 'Allows a user to manually adjust a patron''s primary card', 'ppl', 'description'));
6242 -- Evergreen DB patch 0655.config.bib_source.can_have_copies.sql
6244 -- This column introduces the ability to prevent bib records associated
6245 -- with specific bib sources from being able to have volumes or MFHD
6246 -- records attached to them.
6249 -- check whether patch can be applied
6250 SELECT evergreen.upgrade_deps_block_check('0655', :eg_version);
6252 ALTER TABLE config.bib_source
6253 ADD COLUMN can_have_copies BOOL NOT NULL DEFAULT TRUE;
6255 -- Evergreen DB patch XXXX.LP893315_schema.function.filter_deleted_acns_from_unapi.holdings_xml.sql
6257 -- Prevent deleted call numbers from hiding active call numbers / copies / URIs
6260 -- check whether patch can be applied
6261 SELECT evergreen.upgrade_deps_block_check('0656', :eg_version);
6263 CREATE OR REPLACE FUNCTION unapi.holdings_xml (bid BIGINT, ouid INT, org TEXT, depth INT DEFAULT NULL, includes TEXT[] DEFAULT NULL::TEXT[], slimit INT DEFAULT NULL, soffset INT DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE) RETURNS XML AS $F$
6267 CASE WHEN $8 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
6268 CASE WHEN ('bre' = ANY ($5)) THEN 'tag:open-ils.org:U2@bre/' || $1 || '/' || $3 ELSE NULL END AS id
6272 (SELECT XMLAGG(XMLELEMENT::XML) FROM (
6275 XMLATTRIBUTES('public' as type, depth, org_unit, coalesce(transcendant,0) as transcendant, available, visible as count, unshadow)
6277 FROM asset.opac_ou_record_copy_count($2, $1)
6281 XMLATTRIBUTES('staff' as type, depth, org_unit, coalesce(transcendant,0) as transcendant, available, visible as count, unshadow)
6283 FROM asset.staff_ou_record_copy_count($2, $1)
6288 WHEN ('bmp' = ANY ($5)) THEN
6290 name monograph_parts,
6291 (SELECT XMLAGG(bmp) FROM (
6292 SELECT unapi.bmp( id, 'xml', 'monograph_part', evergreen.array_remove_item_by_value( evergreen.array_remove_item_by_value($5,'bre'), 'holdings_xml'), $3, $4, $6, $7, FALSE)
6293 FROM biblio.monograph_part
6301 (SELECT XMLAGG(acn) FROM (
6302 SELECT unapi.acn(acn.id,'xml','volume',array_remove_item_by_value( evergreen.array_remove_item_by_value($5,'holdings_xml'),'bre'), $3, $4, $6, $7, FALSE)
6303 FROM asset.call_number acn
6304 WHERE acn.record = $1
6305 AND acn.deleted IS FALSE
6309 JOIN actor.org_unit_descendants(
6314 FROM actor.org_unit_type aout
6315 JOIN actor.org_unit aou ON (aou.ou_type = aout.id AND aou.id = $2)
6318 ) aoud ON (acp.circ_lib = aoud.id)
6321 ORDER BY label_sortkey
6326 CASE WHEN ('ssub' = ANY ($5)) THEN
6329 (SELECT XMLAGG(ssub) FROM (
6330 SELECT unapi.ssub(id,'xml','subscription','{}'::TEXT[], $3, $4, $6, $7, FALSE)
6331 FROM serial.subscription
6332 WHERE record_entry = $1
6336 CASE WHEN ('acp' = ANY ($5)) THEN
6338 name foreign_copies,
6339 (SELECT XMLAGG(acp) FROM (
6340 SELECT unapi.acp(p.target_copy,'xml','copy','{}'::TEXT[], $3, $4, $6, $7, FALSE)
6341 FROM biblio.peer_bib_copy_map p
6342 JOIN asset.copy c ON (p.target_copy = c.id)
6343 WHERE NOT c.deleted AND peer_record = $1
6350 -- Evergreen DB patch 0657.schema.address-alert.sql
6353 -- check whether patch can be applied
6354 SELECT evergreen.upgrade_deps_block_check('0657', :eg_version);
6356 CREATE TABLE actor.address_alert (
6357 id SERIAL PRIMARY KEY,
6358 owner INT NOT NULL REFERENCES actor.org_unit (id) DEFERRABLE INITIALLY DEFERRED,
6359 active BOOL NOT NULL DEFAULT TRUE,
6360 match_all BOOL NOT NULL DEFAULT TRUE,
6361 alert_message TEXT NOT NULL,
6369 mailing_address BOOL NOT NULL DEFAULT FALSE,
6370 billing_address BOOL NOT NULL DEFAULT FALSE
6373 CREATE OR REPLACE FUNCTION actor.address_alert_matches (
6382 mailing_address BOOL DEFAULT FALSE,
6383 billing_address BOOL DEFAULT FALSE
6384 ) RETURNS SETOF actor.address_alert AS $$
6387 FROM actor.address_alert
6390 AND owner IN (SELECT id FROM actor.org_unit_ancestors($1))
6392 (NOT mailing_address AND NOT billing_address)
6393 OR (mailing_address AND $9)
6394 OR (billing_address AND $10)
6399 AND COALESCE($2, '') ~* COALESCE(street1, '.*')
6400 AND COALESCE($3, '') ~* COALESCE(street2, '.*')
6401 AND COALESCE($4, '') ~* COALESCE(city, '.*')
6402 AND COALESCE($5, '') ~* COALESCE(county, '.*')
6403 AND COALESCE($6, '') ~* COALESCE(state, '.*')
6404 AND COALESCE($7, '') ~* COALESCE(country, '.*')
6405 AND COALESCE($8, '') ~* COALESCE(post_code, '.*')
6419 ORDER BY actor.org_unit_proximity(owner, $1)
6424 DROP FUNCTION actor.address_alert_matches(INT, TEXT, TEXT, TEXT, TEXT, TEXT, TEXT, TEXT, BOOL, BOOL);
6425 DROP TABLE actor.address_alert;
6427 -- Evergreen DB patch 0659.add_create_report_perms.sql
6429 -- Add a permission to control the ability to create report templates
6432 -- check whether patch can be applied
6433 SELECT evergreen.upgrade_deps_block_check('0659', :eg_version);
6435 -- FIXME: add/check SQL statements to perform the upgrade
6436 INSERT INTO permission.perm_list ( id, code, description ) VALUES
6437 ( 516, 'CREATE_REPORT_TEMPLATE', oils_i18n_gettext( 516,
6438 'Allows a user to create report templates', 'ppl', 'description' ));
6440 INSERT INTO permission.grp_perm_map (grp, perm, depth, grantable)
6441 SELECT grp, 516, depth, grantable
6442 FROM permission.grp_perm_map
6445 FROM permission.perm_list
6446 WHERE code = 'RUN_REPORTS'
6451 SELECT evergreen.upgrade_deps_block_check('0660', :eg_version);
6453 UPDATE action_trigger.event_definition SET template = $$
6455 # target is the bookbag itself. The 'items' variable does not need to be in
6456 # the environment because a special reactor will take care of filling it in.
6459 bibxml = helpers.unapi_bre(item.target_biblio_record_entry, {flesh => '{mra}'});
6461 FOR part IN bibxml.findnodes('//*[@tag="245"]/*[@code="a" or @code="b"]');
6462 title = title _ part.textContent;
6464 author = bibxml.findnodes('//*[@tag="100"]/*[@code="a"]').textContent;
6465 item_type = bibxml.findnodes('//*[local-name()="attributes"]/*[local-name()="field"][@name="item_type"]').getAttribute('coded-value');
6467 helpers.csv_datum(title) %],[% helpers.csv_datum(author) %],[% helpers.csv_datum(item_type) %],[% FOR note IN item.notes; helpers.csv_datum(note.note); ","; END; "\n";
6470 WHERE reactor = 'ContainerCSV';
6472 -- Evergreen DB patch 0661.data.yaous-opac-tag-circed-items.sql
6474 -- Add org unit setting that enables users who have opted in to
6475 -- tracking their circulation history to see which items they
6476 -- have previously checked out in search results.
6479 -- check whether patch can be applied
6480 SELECT evergreen.upgrade_deps_block_check('0661', :eg_version);
6482 INSERT into config.org_unit_setting_type
6483 (name, grp, label, description, datatype)
6485 'opac.search.tag_circulated_items',
6488 'opac.search.tag_circulated_items',
6489 'Tag Circulated Items in Results',
6494 'opac.search.tag_circulated_items',
6495 'When a user is both logged in and has opted in to circulation history tracking, turning on this setting will cause previous (or currently) circulated items to be highlighted in search results',
6503 -- Evergreen DB patch 0662.schema.coded-value-map-index-normalizer.sql
6506 -- check whether patch can be applied
6507 SELECT evergreen.upgrade_deps_block_check('0662', :eg_version);
6509 -- create the normalizer
6510 CREATE OR REPLACE FUNCTION evergreen.coded_value_map_normalizer( input TEXT, ctype TEXT )
6512 SELECT COALESCE(value,$1)
6513 FROM config.coded_value_map
6514 WHERE ctype = $2 AND code = $1;
6517 -- register the normalizer
6518 INSERT INTO config.index_normalizer (name, description, func, param_count) VALUES (
6519 'Coded Value Map Normalizer',
6520 'Applies coded_value_map mapping of values',
6521 'coded_value_map_normalizer',
6525 -- Evergreen DB patch 0663.schema.archive_circ_stat_cats.sql
6527 -- Enables users to set copy and patron stat cats to be archivable
6528 -- for the purposes of statistics even after the circs are aged.
6531 -- check whether patch can be applied
6532 SELECT evergreen.upgrade_deps_block_check('0663', :eg_version);
6536 CREATE TABLE action.archive_actor_stat_cat (
6537 id BIGSERIAL PRIMARY KEY,
6538 xact BIGINT NOT NULL,
6539 stat_cat INT NOT NULL,
6543 CREATE TABLE action.archive_asset_stat_cat (
6544 id BIGSERIAL PRIMARY KEY,
6545 xact BIGINT NOT NULL,
6546 stat_cat INT NOT NULL,
6550 -- Add columns to existing tables
6552 -- Archive Flag Columns
6553 ALTER TABLE actor.stat_cat
6554 ADD COLUMN checkout_archive BOOL NOT NULL DEFAULT FALSE;
6555 ALTER TABLE asset.stat_cat
6556 ADD COLUMN checkout_archive BOOL NOT NULL DEFAULT FALSE;
6558 -- Circulation copy column
6559 ALTER TABLE action.circulation
6560 ADD COLUMN copy_location INT NULL REFERENCES asset.copy_location(id) DEFERRABLE INITIALLY DEFERRED;
6562 -- Create trigger function to auto-fill the copy_location field
6563 CREATE OR REPLACE FUNCTION action.fill_circ_copy_location () RETURNS TRIGGER AS $$
6565 SELECT INTO NEW.copy_location location FROM asset.copy WHERE id = NEW.target_copy;
6568 $$ LANGUAGE PLPGSQL;
6570 -- Create trigger function to auto-archive stat cat entries
6571 CREATE OR REPLACE FUNCTION action.archive_stat_cats () RETURNS TRIGGER AS $$
6573 INSERT INTO action.archive_actor_stat_cat(xact, stat_cat, value)
6574 SELECT NEW.id, asceum.stat_cat, asceum.stat_cat_entry
6575 FROM actor.stat_cat_entry_usr_map asceum
6576 JOIN actor.stat_cat sc ON asceum.stat_cat = sc.id
6577 WHERE NEW.usr = asceum.target_usr AND sc.checkout_archive;
6578 INSERT INTO action.archive_asset_stat_cat(xact, stat_cat, value)
6579 SELECT NEW.id, ascecm.stat_cat, asce.value
6580 FROM asset.stat_cat_entry_copy_map ascecm
6581 JOIN asset.stat_cat sc ON ascecm.stat_cat = sc.id
6582 JOIN asset.stat_cat_entry asce ON ascecm.stat_cat_entry = asce.id
6583 WHERE NEW.target_copy = ascecm.owning_copy AND sc.checkout_archive;
6586 $$ LANGUAGE PLPGSQL;
6589 CREATE TRIGGER fill_circ_copy_location_tgr BEFORE INSERT ON action.circulation FOR EACH ROW EXECUTE PROCEDURE action.fill_circ_copy_location();
6590 CREATE TRIGGER archive_stat_cats_tgr AFTER INSERT ON action.circulation FOR EACH ROW EXECUTE PROCEDURE action.archive_stat_cats();
6592 -- Ensure all triggers are disabled for speedy updates!
6593 ALTER TABLE action.circulation DISABLE TRIGGER ALL;
6595 -- Update view to use circ's copy_location field instead of the copy's current copy_location field
6596 CREATE OR REPLACE VIEW action.all_circulation AS
6597 SELECT id,usr_post_code, usr_home_ou, usr_profile, usr_birth_year, copy_call_number, copy_location,
6598 copy_owning_lib, copy_circ_lib, copy_bib_record, xact_start, xact_finish, target_copy,
6599 circ_lib, circ_staff, checkin_staff, checkin_lib, renewal_remaining, grace_period, due_date,
6600 stop_fines_time, checkin_time, create_time, duration, fine_interval, recurring_fine,
6601 max_fine, phone_renewal, desk_renewal, opac_renewal, duration_rule, recurring_fine_rule,
6602 max_fine_rule, stop_fines, workstation, checkin_workstation, checkin_scan_time, parent_circ
6603 FROM action.aged_circulation
6605 SELECT DISTINCT circ.id,COALESCE(a.post_code,b.post_code) AS usr_post_code, p.home_ou AS usr_home_ou, p.profile AS usr_profile, EXTRACT(YEAR FROM p.dob)::INT AS usr_birth_year,
6606 cp.call_number AS copy_call_number, circ.copy_location, cn.owning_lib AS copy_owning_lib, cp.circ_lib AS copy_circ_lib,
6607 cn.record AS copy_bib_record, circ.xact_start, circ.xact_finish, circ.target_copy, circ.circ_lib, circ.circ_staff, circ.checkin_staff,
6608 circ.checkin_lib, circ.renewal_remaining, circ.grace_period, circ.due_date, circ.stop_fines_time, circ.checkin_time, circ.create_time, circ.duration,
6609 circ.fine_interval, circ.recurring_fine, circ.max_fine, circ.phone_renewal, circ.desk_renewal, circ.opac_renewal, circ.duration_rule,
6610 circ.recurring_fine_rule, circ.max_fine_rule, circ.stop_fines, circ.workstation, circ.checkin_workstation, circ.checkin_scan_time,
6612 FROM action.circulation circ
6613 JOIN asset.copy cp ON (circ.target_copy = cp.id)
6614 JOIN asset.call_number cn ON (cp.call_number = cn.id)
6615 JOIN actor.usr p ON (circ.usr = p.id)
6616 LEFT JOIN actor.usr_address a ON (p.mailing_address = a.id)
6617 LEFT JOIN actor.usr_address b ON (p.billing_address = b.id);
6619 -- Update action.circulation with real copy_location numbers instead of all NULL
6620 DO $$BEGIN RAISE WARNING 'We are about to do an update on every row in action.circulation. This may take a while. %', timeofday(); END;$$;
6621 UPDATE action.circulation circ SET copy_location = ac.location FROM asset.copy ac WHERE ac.id = circ.target_copy;
6623 -- Set not null/default on new column, re-enable triggers
6624 ALTER TABLE action.circulation
6625 ALTER COLUMN copy_location SET NOT NULL,
6626 ALTER COLUMN copy_location SET DEFAULT 1,
6629 -- Evergreen DB patch 0664.schema.hold-current-shelf-lib.sql
6634 -- check whether patch can be applied
6635 SELECT evergreen.upgrade_deps_block_check('0664', :eg_version);
6637 -- add the new column
6638 ALTER TABLE action.hold_request ADD COLUMN current_shelf_lib
6639 INT REFERENCES actor.org_unit DEFERRABLE INITIALLY DEFERRED;
6641 -- Add some others before the UPDATE we are about to do breaks our ability to add columns
6642 -- But we need this table first.
6643 CREATE TABLE config.sms_carrier (
6644 id SERIAL PRIMARY KEY,
6648 active BOOLEAN DEFAULT TRUE
6651 ALTER TABLE action.hold_request ADD COLUMN sms_notify TEXT;
6652 ALTER TABLE action.hold_request ADD COLUMN sms_carrier INT REFERENCES config.sms_carrier (id);
6653 ALTER TABLE action.hold_request ADD CONSTRAINT sms_check CHECK (
6655 OR sms_carrier IS NOT NULL -- and implied sms_notify IS NOT NULL
6660 -- set the value for current_shelf_lib on existing shelved holds
6661 UPDATE action.hold_request
6662 SET current_shelf_lib = pickup_lib
6665 action.hold_request.shelf_time IS NOT NULL
6666 AND action.hold_request.capture_time IS NOT NULL
6667 AND action.hold_request.current_copy IS NOT NULL
6668 AND action.hold_request.fulfillment_time IS NULL
6669 AND action.hold_request.cancel_time IS NULL
6670 AND asset.copy.id = action.hold_request.current_copy
6671 AND asset.copy.status = 8; -- on holds shelf
6674 SELECT evergreen.upgrade_deps_block_check('0666', :eg_version);
6676 -- 950.data.seed-values.sql
6677 INSERT INTO config.settings_group (name, label) VALUES
6682 'SMS Text Messages',
6689 INSERT INTO config.org_unit_setting_type (name, grp, label, description, datatype) VALUES
6695 'Enable features that send SMS text messages.',
6701 'Current features that use SMS include hold-ready-for-pickup notifications and a "Send Text" action for call numbers in the OPAC. If this setting is not enabled, the SMS options will not be offered to the user. Unless you are carefully silo-ing patrons and their use of the OPAC, the context org for this setting should be the top org in the org hierarchy, otherwise patrons can trample their user settings when jumping between orgs.',
6708 'sms.disable_authentication_requirement.callnumbers',
6711 'sms.disable_authentication_requirement.callnumbers',
6712 'Disable auth requirement for texting call numbers.',
6717 'sms.disable_authentication_requirement.callnumbers',
6718 'Disable authentication requirement for sending call number information via SMS from the OPAC.',
6726 -- 090.schema.action.sql
6727 -- 950.data.seed-values.sql
6728 INSERT INTO config.usr_setting_type (name,grp,opac_visible,label,description,datatype,fm_class) VALUES (
6729 'opac.default_sms_carrier',
6733 'opac.default_sms_carrier',
6734 'Default SMS/Text Carrier',
6739 'opac.default_sms_carrier',
6740 'Default SMS/Text Carrier',
6748 INSERT INTO config.usr_setting_type (name,grp,opac_visible,label,description,datatype) VALUES (
6749 'opac.default_sms_notify',
6753 'opac.default_sms_notify',
6754 'Default SMS/Text Number',
6759 'opac.default_sms_notify',
6760 'Default SMS/Text Number',
6767 INSERT INTO config.usr_setting_type (name,grp,opac_visible,label,description,datatype) VALUES (
6768 'opac.default_phone',
6772 'opac.default_phone',
6773 'Default Phone Number',
6778 'opac.default_phone',
6779 'Default Phone Number',
6786 SELECT setval( 'config.sms_carrier_id_seq', 1000 );
6787 INSERT INTO config.sms_carrier VALUES
6804 'opensrf+$number@localhost',
6823 '$number@pcs.rogers.com',
6836 'Rogers Wireless (Alternate)',
6840 '1$number@mms.rogers.com',
6857 '$number@msg.telus.com',
6876 '$number@msg.telus.com',
6906 'Bell Mobility & Solo Mobile',
6910 '$number@txt.bell.ca',
6923 'Bell Mobility & Solo Mobile (Alternate)',
6927 '$number@txt.bellmobility.ca',
6944 '$number@sms.wirefree.informe.ca',
6961 '$number@mobiletxt.ca',
6978 '$number@sms.sasktel.com',
6995 '$number@text.mtsmobility.com',
7012 '$number@vmobile.ca',
7031 '$number@msg.iridium.com',
7048 '$number@msg.globalstarusa.com',
7065 '$number@bulletinmessenger.net', -- International Formatted number
7082 '$number@api.panaceamobile.com',
7101 '$number@cbeyond.sprintpcs.com',
7114 'General Communications, Inc.',
7118 '$number@mobile.gci.net',
7131 'Golden State Cellular',
7135 '$number@gscsms.com',
7142 'Cincinnati, Ohio, USA',
7152 '$number@gocbw.com',
7165 'Hawaiian Telcom Wireless',
7169 '$number@hawaii.sprintpcs.com',
7182 'i wireless (T-Mobile)',
7186 '$number.iws@iwspcs.net',
7199 'i-wireless (Sprint PCS)',
7203 '$number@iwirelesshometext.com',
7220 '$number@mymetropcs.com',
7237 '$number@mobile.kajeet.net',
7254 '$number@SMS.elementmobile.net',
7271 '$number@echoemail.net',
7288 '$number@myboostmobile.com',
7305 '$number@bellsouth.com',
7318 'Bluegrass Cellular',
7322 '$number@sms.bluecell.com',
7335 'AT&T Enterprise Paging',
7339 '$number@page.att.net',
7352 'AT&T Mobility/Wireless',
7356 '$number@txt.att.net',
7369 'AT&T Global Smart Messaging Suite',
7373 '$number@sms.smartmessagingsuite.com',
7386 'Alltel (Allied Wireless)',
7390 '$number@sms.alltelwireless.com',
7403 'Alaska Communications',
7407 '$number@msg.acsalaska.com',
7424 '$number@paging.acswireless.com',
7437 'Cingular (GoPhone prepaid)',
7441 '$number@cingulartext.com',
7454 'Cingular (Postpaid)',
7458 '$number@cingular.com',
7471 'Cellular One (Dobson) / O2 / Orange',
7475 '$number@mobile.celloneusa.com',
7492 '$number@csouth1.com',
7509 '$number@cellcom.quiktxt.com',
7522 'Chariton Valley Wireless',
7526 '$number@sms.cvalley.net',
7543 '$number@sms.mycricket.com',
7556 'Cleartalk Wireless',
7560 '$number@sms.cleartalk.us',
7577 '$number@sms.edgewireless.com',
7594 '$number@rinasms.com',
7611 '$number@tmomail.net',
7624 'Straight Talk / PagePlus Cellular',
7628 '$number@vtext.com',
7641 'South Central Communications',
7645 '$number@rinasms.com',
7662 '$number@smtext.com',
7679 '$number@messaging.sprintpcs.com',
7696 '$number@messaging.nextel.com',
7713 '$number@zsend.com', -- nine digit number
7730 '$number@qwestmp.com',
7747 '$number@email.uscc.net',
7764 '$number@utext.com',
7781 '$number@teleflip.com',
7798 '$number@vmobl.com',
7815 '$number@vtext.com',
7832 '$number@usamobility.net',
7849 '$number@viaerosms.com',
7866 '$number@mmst5.tracfone.com',
7879 'Centennial Wireless',
7883 '$number@cwemail.com',
7887 -- South Korea and USA
7892 'South Korea and USA',
7902 '$number@myhelio.com',
7907 INSERT INTO permission.perm_list ( id, code, description ) VALUES
7910 'ADMIN_SMS_CARRIER',
7913 'Allows a user to add/create/delete SMS Carrier entries.',
7920 INSERT INTO permission.grp_perm_map (grp, perm, depth, grantable)
7922 pgt.id, perm.id, aout.depth, TRUE
7924 permission.grp_tree pgt,
7925 permission.perm_list perm,
7926 actor.org_unit_type aout
7928 pgt.name = 'Global Administrator' AND
7929 aout.name = 'Consortium' AND
7930 perm.code = 'ADMIN_SMS_CARRIER';
7932 INSERT INTO action_trigger.reactor (
7937 'Send an SMS text message based on a user-defined template'
7940 INSERT INTO action_trigger.event_definition (
7955 'Hold Ready for Pickup SMS Notification',
7959 'CreateHoldNotification',
7964 [%- user = target.0.usr -%]
7965 From: [%- params.sender_email || default_sender %]
7966 To: [%- params.recipient_email || helpers.get_sms_gateway_email(target.0.sms_carrier,target.0.sms_notify) %]
7967 Subject: [% target.size %] hold(s) ready
7969 [% FOR hold IN target %][%-
7970 bibxml = helpers.xml_doc( hold.current_copy.call_number.record.marc );
7972 FOR part IN bibxml.findnodes(''//*[@tag="245"]/*[@code="a"]'');
7973 title = title _ part.textContent;
7975 author = bibxml.findnodes(''//*[@tag="100"]/*[@code="a"]'').textContent;
7976 %][% hold.usr.first_given_name %]:[% title %] @ [% hold.pickup_lib.name %]
7981 INSERT INTO action_trigger.environment (
7985 currval('action_trigger.event_definition_id_seq'),
7986 'current_copy.call_number.record.simple_record'
7988 currval('action_trigger.event_definition_id_seq'),
7991 currval('action_trigger.event_definition_id_seq'),
7992 'pickup_lib.billing_address'
7995 INSERT INTO action_trigger.hook(
8001 'acn.format.sms_text',
8004 'acn.format.sms_text',
8005 'A text message has been requested for a call number.',
8012 INSERT INTO action_trigger.event_definition (
8024 'acn.format.sms_text',
8028 From: [%- params.sender_email || default_sender %]
8029 To: [%- params.recipient_email || helpers.get_sms_gateway_email(user_data.sms_carrier,user_data.sms_notify) %]
8030 Subject: Call Number
8033 bibxml = helpers.xml_doc( target.record.marc );
8035 FOR part IN bibxml.findnodes(''//*[@tag="245"]/*[@code="a" or @code="b"]'');
8036 title = title _ part.textContent;
8038 author = bibxml.findnodes(''//*[@tag="100"]/*[@code="a"]'').textContent;
8040 Call Number: [% target.label %]
8041 Location: [% helpers.get_most_populous_location( target.id ).name %]
8042 Library: [% target.owning_lib.name %]
8047 Author: [% author %]
8052 INSERT INTO action_trigger.environment (
8056 currval('action_trigger.event_definition_id_seq'),
8057 'record.simple_record'
8059 currval('action_trigger.event_definition_id_seq'),
8060 'owning_lib.billing_address'
8064 -- DELETE FROM actor.usr_setting WHERE name = 'opac.default_phone' OR name in ( SELECT name FROM config.usr_setting_type WHERE grp = 'sms' ); DELETE FROM config.usr_setting_type WHERE name = 'opac.default_phone' OR grp = 'sms'; DELETE FROM actor.org_unit_setting WHERE name in ( SELECT name FROM config.org_unit_setting_type WHERE grp = 'sms' ); DELETE FROM config.org_unit_setting_type_log WHERE field_name in ( SELECT name FROM config.org_unit_setting_type WHERE grp = 'sms' ); DELETE FROM config.org_unit_setting_type WHERE grp = 'sms'; DELETE FROM config.settings_group WHERE name = 'sms'; DELETE FROM permission.grp_perm_map WHERE perm = 519; DELETE FROM permission.perm_list WHERE id = 519; ALTER TABLE action.hold_request DROP CONSTRAINT sms_check; ALTER TABLE action.hold_request DROP COLUMN sms_notify; ALTER TABLE action.hold_request DROP COLUMN sms_carrier; DROP TABLE config.sms_carrier; DELETE FROM action_trigger.event WHERE event_def = ( SELECT id FROM action_trigger.event_definition WHERE name = 'Hold Ready for Pickup SMS Notification' ); DELETE FROM action_trigger.environment WHERE event_def = ( SELECT id FROM action_trigger.event_definition WHERE name = 'Hold Ready for Pickup SMS Notification' ); DELETE FROM action_trigger.event_definition WHERE name = 'Hold Ready for Pickup SMS Notification'; DELETE FROM action_trigger.event WHERE event_def IN ( SELECT id FROM action_trigger.event_definition WHERE hook = 'acn.format.sms_text' ); DELETE FROM action_trigger.environment WHERE event_def IN ( SELECT id FROM action_trigger.event_definition WHERE hook = 'acn.format.sms_text' ); DELETE FROM action_trigger.event_definition WHERE hook = 'acn.format.sms_text'; DELETE FROM action_trigger.hook WHERE key = 'acn.format.sms_text'; DELETE FROM action_trigger.reactor WHERE module = 'SendSMS'; DELETE FROM config.upgrade_log WHERE version = 'XXXX';
8067 SELECT evergreen.upgrade_deps_block_check('0667', :eg_version);
8069 ALTER TABLE config.standing_penalty ADD staff_alert BOOL NOT NULL DEFAULT FALSE;
8072 -- for backwards compat, set all blocking penalties to alerts
8073 UPDATE config.standing_penalty SET staff_alert = TRUE
8074 WHERE id = 20 OR block_list IS NOT NULL;
8076 -- Evergreen DB patch 0668.schema.fix_indb_hold_permit.sql
8078 -- FIXME: insert description of change, if needed
8082 -- check whether patch can be applied
8083 SELECT evergreen.upgrade_deps_block_check('0668', :eg_version);
8085 -- FIXME: add/check SQL statements to perform the upgrade
8086 CREATE OR REPLACE FUNCTION action.hold_request_permit_test( pickup_ou INT, request_ou INT, match_item BIGINT, match_user INT, match_requestor INT, retargetting BOOL ) RETURNS SETOF action.matrix_test_result AS $func$
8089 user_object actor.usr%ROWTYPE;
8090 age_protect_object config.rule_age_hold_protect%ROWTYPE;
8091 standing_penalty config.standing_penalty%ROWTYPE;
8092 transit_range_ou_type actor.org_unit_type%ROWTYPE;
8093 transit_source actor.org_unit%ROWTYPE;
8094 item_object asset.copy%ROWTYPE;
8095 item_cn_object asset.call_number%ROWTYPE;
8096 item_status_object config.copy_status%ROWTYPE;
8097 item_location_object asset.copy_location%ROWTYPE;
8098 ou_skip actor.org_unit_setting%ROWTYPE;
8099 result action.matrix_test_result;
8100 hold_test config.hold_matrix_matchpoint%ROWTYPE;
8101 use_active_date TEXT;
8102 age_protect_date TIMESTAMP WITH TIME ZONE;
8104 hold_transit_prox INT;
8105 frozen_hold_count INT;
8106 context_org_list INT[];
8109 SELECT INTO user_object * FROM actor.usr WHERE id = match_user;
8110 SELECT INTO context_org_list ARRAY_ACCUM(id) FROM actor.org_unit_full_path( pickup_ou );
8112 result.success := TRUE;
8114 -- Fail if we couldn't find a user
8115 IF user_object.id IS NULL THEN
8116 result.fail_part := 'no_user';
8117 result.success := FALSE;
8123 SELECT INTO item_object * FROM asset.copy WHERE id = match_item;
8125 -- Fail if we couldn't find a copy
8126 IF item_object.id IS NULL THEN
8127 result.fail_part := 'no_item';
8128 result.success := FALSE;
8134 SELECT INTO matchpoint_id action.find_hold_matrix_matchpoint(pickup_ou, request_ou, match_item, match_user, match_requestor);
8135 result.matchpoint := matchpoint_id;
8137 SELECT INTO ou_skip * FROM actor.org_unit_setting WHERE name = 'circ.holds.target_skip_me' AND org_unit = item_object.circ_lib;
8139 -- Fail if the circ_lib for the item has circ.holds.target_skip_me set to true
8140 IF ou_skip.id IS NOT NULL AND ou_skip.value = 'true' THEN
8141 result.fail_part := 'circ.holds.target_skip_me';
8142 result.success := FALSE;
8148 -- Fail if user is barred
8149 IF user_object.barred IS TRUE THEN
8150 result.fail_part := 'actor.usr.barred';
8151 result.success := FALSE;
8157 SELECT INTO item_cn_object * FROM asset.call_number WHERE id = item_object.call_number;
8158 SELECT INTO item_status_object * FROM config.copy_status WHERE id = item_object.status;
8159 SELECT INTO item_location_object * FROM asset.copy_location WHERE id = item_object.location;
8161 -- Fail if we couldn't find any matchpoint (requires a default)
8162 IF matchpoint_id IS NULL THEN
8163 result.fail_part := 'no_matchpoint';
8164 result.success := FALSE;
8170 SELECT INTO hold_test * FROM config.hold_matrix_matchpoint WHERE id = matchpoint_id;
8172 IF hold_test.holdable IS FALSE THEN
8173 result.fail_part := 'config.hold_matrix_test.holdable';
8174 result.success := FALSE;
8179 IF item_object.holdable IS FALSE THEN
8180 result.fail_part := 'item.holdable';
8181 result.success := FALSE;
8186 IF item_status_object.holdable IS FALSE THEN
8187 result.fail_part := 'status.holdable';
8188 result.success := FALSE;
8193 IF item_location_object.holdable IS FALSE THEN
8194 result.fail_part := 'location.holdable';
8195 result.success := FALSE;
8200 IF hold_test.transit_range IS NOT NULL THEN
8201 SELECT INTO transit_range_ou_type * FROM actor.org_unit_type WHERE id = hold_test.transit_range;
8202 IF hold_test.distance_is_from_owner THEN
8203 SELECT INTO transit_source ou.* FROM actor.org_unit ou JOIN asset.call_number cn ON (cn.owning_lib = ou.id) WHERE cn.id = item_object.call_number;
8205 SELECT INTO transit_source * FROM actor.org_unit WHERE id = item_object.circ_lib;
8208 PERFORM * FROM actor.org_unit_descendants( transit_source.id, transit_range_ou_type.depth ) WHERE id = pickup_ou;
8211 result.fail_part := 'transit_range';
8212 result.success := FALSE;
8218 FOR standing_penalty IN
8219 SELECT DISTINCT csp.*
8220 FROM actor.usr_standing_penalty usp
8221 JOIN config.standing_penalty csp ON (csp.id = usp.standing_penalty)
8222 WHERE usr = match_user
8223 AND usp.org_unit IN ( SELECT * FROM unnest(context_org_list) )
8224 AND (usp.stop_date IS NULL or usp.stop_date > NOW())
8225 AND csp.block_list LIKE '%HOLD%' LOOP
8227 result.fail_part := standing_penalty.name;
8228 result.success := FALSE;
8233 IF hold_test.stop_blocked_user IS TRUE THEN
8234 FOR standing_penalty IN
8235 SELECT DISTINCT csp.*
8236 FROM actor.usr_standing_penalty usp
8237 JOIN config.standing_penalty csp ON (csp.id = usp.standing_penalty)
8238 WHERE usr = match_user
8239 AND usp.org_unit IN ( SELECT * FROM unnest(context_org_list) )
8240 AND (usp.stop_date IS NULL or usp.stop_date > NOW())
8241 AND csp.block_list LIKE '%CIRC%' LOOP
8243 result.fail_part := standing_penalty.name;
8244 result.success := FALSE;
8250 IF hold_test.max_holds IS NOT NULL AND NOT retargetting THEN
8251 SELECT INTO hold_count COUNT(*)
8252 FROM action.hold_request
8253 WHERE usr = match_user
8254 AND fulfillment_time IS NULL
8255 AND cancel_time IS NULL
8256 AND CASE WHEN hold_test.include_frozen_holds THEN TRUE ELSE frozen IS FALSE END;
8258 IF hold_count >= hold_test.max_holds THEN
8259 result.fail_part := 'config.hold_matrix_test.max_holds';
8260 result.success := FALSE;
8266 IF item_object.age_protect IS NOT NULL THEN
8267 SELECT INTO age_protect_object * FROM config.rule_age_hold_protect WHERE id = item_object.age_protect;
8268 IF hold_test.distance_is_from_owner THEN
8269 SELECT INTO use_active_date value FROM actor.org_unit_ancestor_setting('circ.holds.age_protect.active_date', item_cn_object.owning_lib);
8271 SELECT INTO use_active_date value FROM actor.org_unit_ancestor_setting('circ.holds.age_protect.active_date', item_object.circ_lib);
8273 IF use_active_date = 'true' THEN
8274 age_protect_date := COALESCE(item_object.active_date, NOW());
8276 age_protect_date := item_object.create_date;
8278 IF age_protect_date + age_protect_object.age > NOW() THEN
8279 IF hold_test.distance_is_from_owner THEN
8280 SELECT INTO item_cn_object * FROM asset.call_number WHERE id = item_object.call_number;
8281 SELECT INTO hold_transit_prox prox FROM actor.org_unit_proximity WHERE from_org = item_cn_object.owning_lib AND to_org = pickup_ou;
8283 SELECT INTO hold_transit_prox prox FROM actor.org_unit_proximity WHERE from_org = item_object.circ_lib AND to_org = pickup_ou;
8286 IF hold_transit_prox > age_protect_object.prox THEN
8287 result.fail_part := 'config.rule_age_hold_protect.prox';
8288 result.success := FALSE;
8301 $func$ LANGUAGE plpgsql;
8304 -- Evergreen DB patch 0669.data.recall_and_force_holds.sql
8306 -- FIXME: insert description of change, if needed
8310 -- check whether patch can be applied
8311 SELECT evergreen.upgrade_deps_block_check('0669', :eg_version);
8313 -- FIXME: add/check SQL statements to perform the upgrade
8314 INSERT INTO permission.perm_list ( id, code, description ) VALUES
8315 ( 517, 'COPY_HOLDS_FORCE', oils_i18n_gettext( 517,
8316 'Allow a user to place a force hold on a specific copy', 'ppl', 'description' )),
8317 ( 518, 'COPY_HOLDS_RECALL', oils_i18n_gettext( 518,
8318 'Allow a user to place a cataloging recall on a specific copy', 'ppl', 'description' ));
8321 -- Evergreen DB patch 0670.data.mark-email-and-phone-invalid.sql
8323 -- Add org unit settings and standing penalty types to support
8324 -- the mark email/phone invalid features.
8327 -- check whether patch can be applied
8328 SELECT evergreen.upgrade_deps_block_check('0670', :eg_version);
8331 INSERT INTO config.standing_penalty (id, name, label, staff_alert, org_depth) VALUES
8334 'INVALID_PATRON_EMAIL_ADDRESS',
8337 'Patron had an invalid email address',
8346 'INVALID_PATRON_DAY_PHONE',
8349 'Patron had an invalid daytime phone number',
8358 'INVALID_PATRON_EVENING_PHONE',
8361 'Patron had an invalid evening phone number',
8370 'INVALID_PATRON_OTHER_PHONE',
8373 'Patron had an invalid other phone number',
8383 SELECT evergreen.upgrade_deps_block_check('0671', :eg_version);
8385 ALTER TABLE asset.copy_location
8386 ADD COLUMN checkin_alert BOOL NOT NULL DEFAULT FALSE;
8388 -- Evergreen DB patch 0673.data.acq-cancel-reason-cleanup.sql
8391 -- check whether patch can be applied
8392 SELECT evergreen.upgrade_deps_block_check('0673', :eg_version);
8397 -- any entries with id >= 2000 were added locally.
8400 -- these cancel_reason's are actively used by the system
8401 AND id NOT IN (1, 2, 3, 1002, 1003, 1004, 1005, 1010, 1024, 1211, 1221, 1246, 1283)
8403 -- don't delete any cancel_reason's that may be in use locally
8404 AND id NOT IN (SELECT DISTINCT(cancel_reason) FROM acq.user_request WHERE cancel_reason IS NOT NULL)
8405 AND id NOT IN (SELECT DISTINCT(cancel_reason) FROM acq.purchase_order WHERE cancel_reason IS NOT NULL)
8406 AND id NOT IN (SELECT DISTINCT(cancel_reason) FROM acq.lineitem WHERE cancel_reason IS NOT NULL)
8407 AND id NOT IN (SELECT DISTINCT(cancel_reason) FROM acq.lineitem_detail WHERE cancel_reason IS NOT NULL)
8408 AND id NOT IN (SELECT DISTINCT(cancel_reason) FROM acq.acq_lineitem_history WHERE cancel_reason IS NOT NULL)
8409 AND id NOT IN (SELECT DISTINCT(cancel_reason) FROM acq.acq_purchase_order_history WHERE cancel_reason IS NOT NULL);
8412 SELECT evergreen.upgrade_deps_block_check('0674', :eg_version);
8414 ALTER TABLE config.copy_status
8415 ADD COLUMN restrict_copy_delete BOOL NOT NULL DEFAULT FALSE;
8417 UPDATE config.copy_status
8418 SET restrict_copy_delete = TRUE
8419 WHERE id IN (1,3,6,8);
8421 INSERT INTO permission.perm_list (id, code, description) VALUES (
8423 'COPY_DELETE_WARNING.override',
8424 'Allow a user to override warnings about deleting copies in problematic situations.'
8428 SELECT evergreen.upgrade_deps_block_check('0675', :eg_version);
8430 -- set expected row count to low value to avoid problem
8431 -- where use of this function by the circ tagging feature
8432 -- results in full scans of asset.call_number
8433 CREATE OR REPLACE FUNCTION action.usr_visible_circ_copies( INTEGER ) RETURNS SETOF BIGINT AS $$
8434 SELECT DISTINCT(target_copy) FROM action.usr_visible_circs($1)
8435 $$ LANGUAGE SQL ROWS 10;
8438 SELECT evergreen.upgrade_deps_block_check('0676', :eg_version);
8440 INSERT INTO config.global_flag (name, label, enabled, value) VALUES (
8441 'opac.use_autosuggest',
8442 'OPAC: Show auto-completing suggestions dialog under basic search box (put ''opac_visible'' into the value field to limit suggestions to OPAC-visible items, or blank the field for a possible performance improvement)',
8447 CREATE TABLE metabib.browse_entry (
8448 id BIGSERIAL PRIMARY KEY,
8450 index_vector tsvector
8452 --Skip this, will be created differently later
8453 --CREATE INDEX metabib_browse_entry_index_vector_idx ON metabib.browse_entry USING GIST (index_vector);
8454 CREATE TRIGGER metabib_browse_entry_fti_trigger
8455 BEFORE INSERT OR UPDATE ON metabib.browse_entry
8456 FOR EACH ROW EXECUTE PROCEDURE oils_tsearch2('keyword');
8459 CREATE TABLE metabib.browse_entry_def_map (
8460 id BIGSERIAL PRIMARY KEY,
8461 entry BIGINT REFERENCES metabib.browse_entry (id),
8462 def INT REFERENCES config.metabib_field (id),
8463 source BIGINT REFERENCES biblio.record_entry (id)
8466 ALTER TABLE config.metabib_field ADD COLUMN browse_field BOOLEAN DEFAULT TRUE NOT NULL;
8467 ALTER TABLE config.metabib_field ADD COLUMN browse_xpath TEXT;
8469 ALTER TABLE config.metabib_class ADD COLUMN bouyant BOOLEAN DEFAULT FALSE NOT NULL;
8470 ALTER TABLE config.metabib_class ADD COLUMN restrict BOOLEAN DEFAULT FALSE NOT NULL;
8471 ALTER TABLE config.metabib_field ADD COLUMN restrict BOOLEAN DEFAULT FALSE NOT NULL;
8473 -- one good exception to default true:
8474 UPDATE config.metabib_field
8475 SET browse_field = FALSE
8476 WHERE (field_class = 'keyword' AND name = 'keyword') OR
8477 (field_class = 'subject' AND name = 'complete');
8479 -- AFTER UPDATE OR INSERT trigger for biblio.record_entry
8480 -- We're only touching it here to add a DELETE statement to the IF NEW.deleted
8483 CREATE OR REPLACE FUNCTION biblio.indexing_ingest_or_delete () RETURNS TRIGGER AS $func$
8485 transformed_xml TEXT;
8488 xfrm config.xml_transform%ROWTYPE;
8490 new_attrs HSTORE := ''::HSTORE;
8491 attr_def config.record_attr_definition%ROWTYPE;
8494 IF NEW.deleted IS TRUE THEN -- If this bib is deleted
8495 DELETE FROM metabib.metarecord_source_map WHERE source = NEW.id; -- Rid ourselves of the search-estimate-killing linkage
8496 DELETE FROM metabib.record_attr WHERE id = NEW.id; -- Kill the attrs hash, useless on deleted records
8497 DELETE FROM authority.bib_linking WHERE bib = NEW.id; -- Avoid updating fields in bibs that are no longer visible
8498 DELETE FROM biblio.peer_bib_copy_map WHERE peer_record = NEW.id; -- Separate any multi-homed items
8499 DELETE FROM metabib.browse_entry_def_map WHERE source = NEW.id; -- Don't auto-suggest deleted bibs
8500 RETURN NEW; -- and we're done
8503 IF TG_OP = 'UPDATE' THEN -- re-ingest?
8504 PERFORM * FROM config.internal_flag WHERE name = 'ingest.reingest.force_on_same_marc' AND enabled;
8506 IF NOT FOUND AND OLD.marc = NEW.marc THEN -- don't do anything if the MARC didn't change
8511 -- Record authority linking
8512 PERFORM * FROM config.internal_flag WHERE name = 'ingest.disable_authority_linking' AND enabled;
8514 PERFORM biblio.map_authority_linking( NEW.id, NEW.marc );
8517 -- Flatten and insert the mfr data
8518 PERFORM * FROM config.internal_flag WHERE name = 'ingest.disable_metabib_full_rec' AND enabled;
8520 PERFORM metabib.reingest_metabib_full_rec(NEW.id);
8522 -- Now we pull out attribute data, which is dependent on the mfr for all but XPath-based fields
8523 PERFORM * FROM config.internal_flag WHERE name = 'ingest.disable_metabib_rec_descriptor' AND enabled;
8525 FOR attr_def IN SELECT * FROM config.record_attr_definition ORDER BY format LOOP
8527 IF attr_def.tag IS NOT NULL THEN -- tag (and optional subfield list) selection
8528 SELECT ARRAY_TO_STRING(ARRAY_ACCUM(value), COALESCE(attr_def.joiner,' ')) INTO attr_value
8529 FROM (SELECT * FROM metabib.full_rec ORDER BY tag, subfield) AS x
8530 WHERE record = NEW.id
8531 AND tag LIKE attr_def.tag
8533 WHEN attr_def.sf_list IS NOT NULL
8534 THEN POSITION(subfield IN attr_def.sf_list) > 0
8541 ELSIF attr_def.fixed_field IS NOT NULL THEN -- a named fixed field, see config.marc21_ff_pos_map.fixed_field
8542 attr_value := biblio.marc21_extract_fixed_field(NEW.id, attr_def.fixed_field);
8544 ELSIF attr_def.xpath IS NOT NULL THEN -- and xpath expression
8546 SELECT INTO xfrm * FROM config.xml_transform WHERE name = attr_def.format;
8548 -- See if we can skip the XSLT ... it's expensive
8549 IF prev_xfrm IS NULL OR prev_xfrm <> xfrm.name THEN
8550 -- Can't skip the transform
8551 IF xfrm.xslt <> '---' THEN
8552 transformed_xml := oils_xslt_process(NEW.marc,xfrm.xslt);
8554 transformed_xml := NEW.marc;
8557 prev_xfrm := xfrm.name;
8560 IF xfrm.name IS NULL THEN
8561 -- just grab the marcxml (empty) transform
8562 SELECT INTO xfrm * FROM config.xml_transform WHERE xslt = '---' LIMIT 1;
8563 prev_xfrm := xfrm.name;
8566 attr_value := oils_xpath_string(attr_def.xpath, transformed_xml, COALESCE(attr_def.joiner,' '), ARRAY[ARRAY[xfrm.prefix, xfrm.namespace_uri]]);
8568 ELSIF attr_def.phys_char_sf IS NOT NULL THEN -- a named Physical Characteristic, see config.marc21_physical_characteristic_*_map
8569 SELECT m.value INTO attr_value
8570 FROM biblio.marc21_physical_characteristics(NEW.id) v
8571 JOIN config.marc21_physical_characteristic_value_map m ON (m.id = v.value)
8572 WHERE v.subfield = attr_def.phys_char_sf
8573 LIMIT 1; -- Just in case ...
8577 -- apply index normalizers to attr_value
8579 SELECT n.func AS func,
8580 n.param_count AS param_count,
8582 FROM config.index_normalizer n
8583 JOIN config.record_attr_index_norm_map m ON (m.norm = n.id)
8584 WHERE attr = attr_def.name
8586 EXECUTE 'SELECT ' || normalizer.func || '(' ||
8587 COALESCE( quote_literal( attr_value ), 'NULL' ) ||
8589 WHEN normalizer.param_count > 0
8590 THEN ',' || REPLACE(REPLACE(BTRIM(normalizer.params,'[]'),E'\'',E'\\\''),E'"',E'\'')
8593 ')' INTO attr_value;
8597 -- Add the new value to the hstore
8598 new_attrs := new_attrs || hstore( attr_def.name, attr_value );
8602 IF TG_OP = 'INSERT' OR OLD.deleted THEN -- initial insert OR revivication
8603 INSERT INTO metabib.record_attr (id, attrs) VALUES (NEW.id, new_attrs);
8605 UPDATE metabib.record_attr SET attrs = new_attrs WHERE id = NEW.id;
8611 -- Gather and insert the field entry data
8612 PERFORM metabib.reingest_metabib_field_entries(NEW.id);
8614 -- Located URI magic
8615 IF TG_OP = 'INSERT' THEN
8616 PERFORM * FROM config.internal_flag WHERE name = 'ingest.disable_located_uri' AND enabled;
8618 PERFORM biblio.extract_located_uris( NEW.id, NEW.marc, NEW.editor );
8621 PERFORM * FROM config.internal_flag WHERE name = 'ingest.disable_located_uri' AND enabled;
8623 PERFORM biblio.extract_located_uris( NEW.id, NEW.marc, NEW.editor );
8627 -- (re)map metarecord-bib linking
8628 IF TG_OP = 'INSERT' THEN -- if not deleted and performing an insert, check for the flag
8629 PERFORM * FROM config.internal_flag WHERE name = 'ingest.metarecord_mapping.skip_on_insert' AND enabled;
8631 PERFORM metabib.remap_metarecord_for_bib( NEW.id, NEW.fingerprint );
8633 ELSE -- we're doing an update, and we're not deleted, remap
8634 PERFORM * FROM config.internal_flag WHERE name = 'ingest.metarecord_mapping.skip_on_update' AND enabled;
8636 PERFORM metabib.remap_metarecord_for_bib( NEW.id, NEW.fingerprint );
8642 $func$ LANGUAGE PLPGSQL;
8644 CREATE OR REPLACE FUNCTION metabib.browse_normalize(facet_text TEXT, mapped_field INT) RETURNS TEXT AS $$
8650 SELECT n.func AS func,
8651 n.param_count AS param_count,
8653 FROM config.index_normalizer n
8654 JOIN config.metabib_field_index_norm_map m ON (m.norm = n.id)
8655 WHERE m.field = mapped_field AND m.pos < 0
8658 EXECUTE 'SELECT ' || normalizer.func || '(' ||
8659 quote_literal( facet_text ) ||
8661 WHEN normalizer.param_count > 0
8662 THEN ',' || REPLACE(REPLACE(BTRIM(normalizer.params,'[]'),E'\'',E'\\\''),E'"',E'\'')
8665 ')' INTO facet_text;
8672 $$ LANGUAGE PLPGSQL;
8674 DROP FUNCTION biblio.extract_metabib_field_entry(bigint, text);
8675 DROP FUNCTION biblio.extract_metabib_field_entry(bigint);
8677 DROP TYPE metabib.field_entry_template;
8678 CREATE TYPE metabib.field_entry_template AS (
8689 CREATE OR REPLACE FUNCTION biblio.extract_metabib_field_entry ( rid BIGINT, default_joiner TEXT ) RETURNS SETOF metabib.field_entry_template AS $func$
8691 bib biblio.record_entry%ROWTYPE;
8692 idx config.metabib_field%ROWTYPE;
8693 xfrm config.xml_transform%ROWTYPE;
8695 transformed_xml TEXT;
8697 xml_node_list TEXT[];
8702 joiner TEXT := default_joiner; -- XXX will index defs supply a joiner?
8703 output_row metabib.field_entry_template%ROWTYPE;
8707 SELECT INTO bib * FROM biblio.record_entry WHERE id = rid;
8709 -- Loop over the indexing entries
8710 FOR idx IN SELECT * FROM config.metabib_field ORDER BY format LOOP
8712 SELECT INTO xfrm * from config.xml_transform WHERE name = idx.format;
8714 -- See if we can skip the XSLT ... it's expensive
8715 IF prev_xfrm IS NULL OR prev_xfrm <> xfrm.name THEN
8716 -- Can't skip the transform
8717 IF xfrm.xslt <> '---' THEN
8718 transformed_xml := oils_xslt_process(bib.marc,xfrm.xslt);
8720 transformed_xml := bib.marc;
8723 prev_xfrm := xfrm.name;
8726 xml_node_list := oils_xpath( idx.xpath, transformed_xml, ARRAY[ARRAY[xfrm.prefix, xfrm.namespace_uri]] );
8729 FOR xml_node IN SELECT x FROM unnest(xml_node_list) AS x LOOP
8730 CONTINUE WHEN xml_node !~ E'^\\s*<';
8732 curr_text := ARRAY_TO_STRING(
8733 oils_xpath( '//text()',
8734 REGEXP_REPLACE( -- This escapes all &s not followed by "amp;". Data ise returned from oils_xpath (above) in UTF-8, not entity encoded
8735 REGEXP_REPLACE( -- This escapes embeded <s
8737 $re$(>[^<]+)(<)([^>]+<)$re$,
8749 CONTINUE WHEN curr_text IS NULL OR curr_text = '';
8751 IF raw_text IS NOT NULL THEN
8752 raw_text := raw_text || joiner;
8755 raw_text := COALESCE(raw_text,'') || curr_text;
8757 -- autosuggest/metabib.browse_entry
8758 IF idx.browse_field THEN
8760 IF idx.browse_xpath IS NOT NULL AND idx.browse_xpath <> '' THEN
8761 browse_text := oils_xpath_string( idx.browse_xpath, xml_node, joiner, ARRAY[ARRAY[xfrm.prefix, xfrm.namespace_uri]] );
8763 browse_text := curr_text;
8766 output_row.field_class = idx.field_class;
8767 output_row.field = idx.id;
8768 output_row.source = rid;
8769 output_row.value = BTRIM(REGEXP_REPLACE(browse_text, E'\\s+', ' ', 'g'));
8771 output_row.browse_field = TRUE;
8772 RETURN NEXT output_row;
8773 output_row.browse_field = FALSE;
8776 -- insert raw node text for faceting
8777 IF idx.facet_field THEN
8779 IF idx.facet_xpath IS NOT NULL AND idx.facet_xpath <> '' THEN
8780 facet_text := oils_xpath_string( idx.facet_xpath, xml_node, joiner, ARRAY[ARRAY[xfrm.prefix, xfrm.namespace_uri]] );
8782 facet_text := curr_text;
8785 output_row.field_class = idx.field_class;
8786 output_row.field = -1 * idx.id;
8787 output_row.source = rid;
8788 output_row.value = BTRIM(REGEXP_REPLACE(facet_text, E'\\s+', ' ', 'g'));
8790 output_row.facet_field = TRUE;
8791 RETURN NEXT output_row;
8792 output_row.facet_field = FALSE;
8797 CONTINUE WHEN raw_text IS NULL OR raw_text = '';
8799 -- insert combined node text for searching
8800 IF idx.search_field THEN
8801 output_row.field_class = idx.field_class;
8802 output_row.field = idx.id;
8803 output_row.source = rid;
8804 output_row.value = BTRIM(REGEXP_REPLACE(raw_text, E'\\s+', ' ', 'g'));
8806 output_row.search_field = TRUE;
8807 RETURN NEXT output_row;
8813 $func$ LANGUAGE PLPGSQL;
8815 -- default to a space joiner
8816 CREATE OR REPLACE FUNCTION biblio.extract_metabib_field_entry ( BIGINT ) RETURNS SETOF metabib.field_entry_template AS $func$
8817 SELECT * FROM biblio.extract_metabib_field_entry($1, ' ');
8818 $func$ LANGUAGE SQL;
8821 CREATE OR REPLACE FUNCTION metabib.reingest_metabib_field_entries( bib_id BIGINT ) RETURNS VOID AS $func$
8824 ind_data metabib.field_entry_template%ROWTYPE;
8825 mbe_row metabib.browse_entry%ROWTYPE;
8828 PERFORM * FROM config.internal_flag WHERE name = 'ingest.assume_inserts_only' AND enabled;
8830 FOR fclass IN SELECT * FROM config.metabib_class LOOP
8831 -- RAISE NOTICE 'Emptying out %', fclass.name;
8832 EXECUTE $$DELETE FROM metabib.$$ || fclass.name || $$_field_entry WHERE source = $$ || bib_id;
8834 DELETE FROM metabib.facet_entry WHERE source = bib_id;
8835 DELETE FROM metabib.browse_entry_def_map WHERE source = bib_id;
8838 FOR ind_data IN SELECT * FROM biblio.extract_metabib_field_entry( bib_id ) LOOP
8839 IF ind_data.field < 0 THEN
8840 ind_data.field = -1 * ind_data.field;
8843 IF ind_data.facet_field THEN
8844 INSERT INTO metabib.facet_entry (field, source, value)
8845 VALUES (ind_data.field, ind_data.source, ind_data.value);
8848 IF ind_data.browse_field THEN
8849 SELECT INTO mbe_row * FROM metabib.browse_entry WHERE value = ind_data.value;
8851 mbe_id := mbe_row.id;
8853 INSERT INTO metabib.browse_entry (value) VALUES
8854 (metabib.browse_normalize(ind_data.value, ind_data.field));
8855 mbe_id := CURRVAL('metabib.browse_entry_id_seq'::REGCLASS);
8858 INSERT INTO metabib.browse_entry_def_map (entry, def, source)
8859 VALUES (mbe_id, ind_data.field, ind_data.source);
8862 IF ind_data.search_field THEN
8864 INSERT INTO metabib.$$ || ind_data.field_class || $$_field_entry (field, source, value)
8866 quote_literal(ind_data.field) || $$, $$ ||
8867 quote_literal(ind_data.source) || $$, $$ ||
8868 quote_literal(ind_data.value) ||
8876 $func$ LANGUAGE PLPGSQL;
8878 -- This mimics a specific part of QueryParser, turning the first part of a
8879 -- classed search (search_class) into a set of classes and possibly fields.
8880 -- search_class might look like "author" or "title|proper" or "ti|uniform"
8881 -- or "au" or "au|corporate|personal" or anything like that, where the first
8882 -- element of the list you get by separating on the "|" character is either
8883 -- a registered class (config.metabib_class) or an alias
8884 -- (config.metabib_search_alias), and the rest of any such elements are
8885 -- fields (config.metabib_field).
8887 FUNCTION metabib.search_class_to_registered_components(search_class TEXT)
8888 RETURNS SETOF RECORD AS $func$
8890 search_parts TEXT[];
8892 search_part_count INTEGER;
8894 registered_class config.metabib_class%ROWTYPE;
8895 registered_alias config.metabib_search_alias%ROWTYPE;
8896 registered_field config.metabib_field%ROWTYPE;
8898 search_parts := REGEXP_SPLIT_TO_ARRAY(search_class, E'\\|');
8900 search_part_count := ARRAY_LENGTH(search_parts, 1);
8901 IF search_part_count = 0 THEN
8904 SELECT INTO registered_class
8905 * FROM config.metabib_class WHERE name = search_parts[1];
8907 IF search_part_count < 2 THEN -- all fields
8908 rec := (registered_class.name, NULL::INTEGER);
8912 FOR field_name IN SELECT *
8913 FROM UNNEST(search_parts[2:search_part_count]) LOOP
8914 SELECT INTO registered_field
8915 * FROM config.metabib_field
8916 WHERE name = field_name AND
8917 field_class = registered_class.name;
8919 rec := (registered_class.name, registered_field.id);
8924 -- maybe we have an alias?
8925 SELECT INTO registered_alias
8926 * FROM config.metabib_search_alias WHERE alias=search_parts[1];
8930 IF search_part_count < 2 THEN -- return w/e the alias says
8932 registered_alias.field_class, registered_alias.field
8937 FOR field_name IN SELECT *
8938 FROM UNNEST(search_parts[2:search_part_count]) LOOP
8939 SELECT INTO registered_field
8940 * FROM config.metabib_field
8941 WHERE name = field_name AND
8942 field_class = registered_alias.field_class;
8945 registered_alias.field_class,
8956 $func$ LANGUAGE PLPGSQL;
8960 FUNCTION metabib.suggest_browse_entries(
8961 query_text TEXT, -- 'foo' or 'foo & ba:*',ready for to_tsquery()
8962 search_class TEXT, -- 'alias' or 'class' or 'class|field..', etc
8963 headline_opts TEXT, -- markup options for ts_headline()
8964 visibility_org INTEGER,-- null if you don't want opac visibility test
8965 query_limit INTEGER,-- use in LIMIT clause of interal query
8966 normalization INTEGER -- argument to TS_RANK_CD()
8968 value TEXT, -- plain
8970 bouyant_and_class_match BOOL,
8972 field_weight INTEGER,
8975 match TEXT -- marked up
8979 opac_visibility_join TEXT;
8980 search_class_join TEXT;
8983 query := TO_TSQUERY('keyword', query_text);
8985 IF visibility_org IS NOT NULL THEN
8986 opac_visibility_join := '
8987 JOIN asset.opac_visible_copies aovc ON (
8988 aovc.record = mbedm.source AND
8989 aovc.circ_lib IN (SELECT id FROM actor.org_unit_descendants($4))
8992 opac_visibility_join := '';
8995 -- The following determines whether we only provide suggestsons matching
8996 -- the user's selected search_class, or whether we show other suggestions
8997 -- too. The reason for MIN() is that for search_classes like
8998 -- 'title|proper|uniform' you would otherwise get multiple rows. The
8999 -- implication is that if title as a class doesn't have restrict,
9000 -- nor does the proper field, but the uniform field does, you're going
9001 -- to get 'false' for your overall evaluation of 'should we restrict?'
9002 -- To invert that, change from MIN() to MAX().
9006 MIN(cmc.restrict::INT) AS restrict_class,
9007 MIN(cmf.restrict::INT) AS restrict_field
9008 FROM metabib.search_class_to_registered_components(search_class)
9009 AS _registered (field_class TEXT, field INT)
9011 config.metabib_class cmc ON (cmc.name = _registered.field_class)
9013 config.metabib_field cmf ON (cmf.id = _registered.field);
9015 -- evaluate 'should we restrict?'
9016 IF r_fields.restrict_field::BOOL OR r_fields.restrict_class::BOOL THEN
9017 search_class_join := '
9019 metabib.search_class_to_registered_components($2)
9020 AS _registered (field_class TEXT, field INT) ON (
9021 (_registered.field IS NULL AND
9022 _registered.field_class = cmf.field_class) OR
9023 (_registered.field = cmf.id)
9027 search_class_join := '
9029 metabib.search_class_to_registered_components($2)
9030 AS _registered (field_class TEXT, field INT) ON (
9031 _registered.field_class = cmc.name
9036 RETURN QUERY EXECUTE 'SELECT *, TS_HEADLINE(value, $1, $3) FROM (SELECT DISTINCT
9039 cmc.bouyant AND _registered.field_class IS NOT NULL,
9040 _registered.field = cmf.id,
9042 TS_RANK_CD(mbe.index_vector, $1, $6),
9044 FROM metabib.browse_entry_def_map mbedm
9045 JOIN metabib.browse_entry mbe ON (mbe.id = mbedm.entry)
9046 JOIN config.metabib_field cmf ON (cmf.id = mbedm.def)
9047 JOIN config.metabib_class cmc ON (cmf.field_class = cmc.name)
9048 ' || search_class_join || opac_visibility_join ||
9049 ' WHERE $1 @@ mbe.index_vector
9050 ORDER BY 3 DESC, 4 DESC NULLS LAST, 5 DESC, 6 DESC, 7 DESC, 1 ASC
9052 ORDER BY 3 DESC, 4 DESC NULLS LAST, 5 DESC, 6 DESC, 7 DESC, 1 ASC
9053 ' -- sic, repeat the order by clause in the outer select too
9055 query, search_class, headline_opts,
9056 visibility_org, query_limit, normalization
9060 -- bouyant AND chosen class = match class
9061 -- chosen field = match field
9068 $func$ LANGUAGE PLPGSQL;
9070 -- The advantage of this over the stock regexp_split_to_array() is that it
9071 -- won't degrade unicode strings.
9072 CREATE OR REPLACE FUNCTION evergreen.regexp_split_to_array(TEXT, TEXT)
9073 RETURNS TEXT[] AS $$
9074 return encode_array_literal([split $_[1], $_[0]]);
9075 $$ LANGUAGE PLPERLU STRICT IMMUTABLE;
9078 -- Adds some logic for browse_entry to split on non-word chars for index_vector, post-normalize
9079 CREATE OR REPLACE FUNCTION oils_tsearch2 () RETURNS TRIGGER AS $$
9087 IF TG_TABLE_NAME::TEXT ~ 'field_entry$' THEN
9089 SELECT n.func AS func,
9090 n.param_count AS param_count,
9092 FROM config.index_normalizer n
9093 JOIN config.metabib_field_index_norm_map m ON (m.norm = n.id)
9094 WHERE field = NEW.field AND m.pos < 0
9096 EXECUTE 'SELECT ' || normalizer.func || '(' ||
9097 quote_literal( value ) ||
9099 WHEN normalizer.param_count > 0
9100 THEN ',' || REPLACE(REPLACE(BTRIM(normalizer.params,'[]'),E'\'',E'\\\''),E'"',E'\'')
9110 IF NEW.index_vector = ''::tsvector THEN
9114 IF TG_TABLE_NAME::TEXT ~ 'field_entry$' THEN
9116 SELECT n.func AS func,
9117 n.param_count AS param_count,
9119 FROM config.index_normalizer n
9120 JOIN config.metabib_field_index_norm_map m ON (m.norm = n.id)
9121 WHERE field = NEW.field AND m.pos >= 0
9123 EXECUTE 'SELECT ' || normalizer.func || '(' ||
9124 quote_literal( value ) ||
9126 WHEN normalizer.param_count > 0
9127 THEN ',' || REPLACE(REPLACE(BTRIM(normalizer.params,'[]'),E'\'',E'\\\''),E'"',E'\'')
9135 IF TG_TABLE_NAME::TEXT ~ 'browse_entry$' THEN
9136 value := ARRAY_TO_STRING(
9137 evergreen.regexp_split_to_array(value, E'\\W+'), ' '
9141 NEW.index_vector = to_tsvector((TG_ARGV[0])::regconfig, value);
9145 $$ LANGUAGE PLPGSQL;
9147 -- Evergreen DB patch 0677.schema.circ_limits.sql
9149 -- FIXME: insert description of change, if needed
9153 -- check whether patch can be applied
9154 SELECT evergreen.upgrade_deps_block_check('0677', :eg_version);
9156 -- FIXME: add/check SQL statements to perform the upgrade
9157 -- Limit groups for circ counting
9158 CREATE TABLE config.circ_limit_group (
9159 id SERIAL PRIMARY KEY,
9160 name TEXT UNIQUE NOT NULL,
9165 CREATE TABLE config.circ_limit_set (
9166 id SERIAL PRIMARY KEY,
9167 name TEXT UNIQUE NOT NULL,
9168 owning_lib INT NOT NULL REFERENCES actor.org_unit (id) DEFERRABLE INITIALLY DEFERRED,
9169 items_out INT NOT NULL, -- Total current active circulations must be less than this. 0 means skip counting (always pass)
9170 depth INT NOT NULL DEFAULT 0, -- Depth count starts at
9171 global BOOL NOT NULL DEFAULT FALSE, -- If enabled, include everything below depth, otherwise ancestors/descendants only
9175 -- Linkage between matchpoints and limit sets
9176 CREATE TABLE config.circ_matrix_limit_set_map (
9177 id SERIAL PRIMARY KEY,
9178 matchpoint INT NOT NULL REFERENCES config.circ_matrix_matchpoint (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
9179 limit_set INT NOT NULL REFERENCES config.circ_limit_set (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
9180 fallthrough BOOL NOT NULL DEFAULT FALSE, -- If true fallthrough will grab this rule as it goes along
9181 active BOOL NOT NULL DEFAULT TRUE,
9182 CONSTRAINT circ_limit_set_once_per_matchpoint UNIQUE (matchpoint, limit_set)
9185 -- Linkage between limit sets and circ mods
9186 CREATE TABLE config.circ_limit_set_circ_mod_map (
9187 id SERIAL PRIMARY KEY,
9188 limit_set INT NOT NULL REFERENCES config.circ_limit_set (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
9189 circ_mod TEXT NOT NULL REFERENCES config.circ_modifier (code) ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE INITIALLY DEFERRED,
9190 CONSTRAINT cm_once_per_set UNIQUE (limit_set, circ_mod)
9193 -- Linkage between limit sets and limit groups
9194 CREATE TABLE config.circ_limit_set_group_map (
9195 id SERIAL PRIMARY KEY,
9196 limit_set INT NOT NULL REFERENCES config.circ_limit_set (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
9197 limit_group INT NOT NULL REFERENCES config.circ_limit_group (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
9198 check_only BOOL NOT NULL DEFAULT FALSE, -- If true, don't accumulate this limit_group for storing with the circulation
9199 CONSTRAINT clg_once_per_set UNIQUE (limit_set, limit_group)
9202 -- Linkage between limit groups and circulations
9203 CREATE TABLE action.circulation_limit_group_map (
9204 circ BIGINT NOT NULL REFERENCES action.circulation (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
9205 limit_group INT NOT NULL REFERENCES config.circ_limit_group (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
9206 PRIMARY KEY (circ, limit_group)
9209 -- Function for populating the circ/limit group mappings
9210 CREATE OR REPLACE FUNCTION action.link_circ_limit_groups ( BIGINT, INT[] ) RETURNS VOID AS $func$
9211 INSERT INTO action.circulation_limit_group_map(circ, limit_group) SELECT $1, id FROM config.circ_limit_group WHERE id IN (SELECT * FROM UNNEST($2));
9212 $func$ LANGUAGE SQL;
9214 DROP TYPE IF EXISTS action.circ_matrix_test_result CASCADE;
9215 CREATE TYPE action.circ_matrix_test_result AS ( success BOOL, fail_part TEXT, buildrows INT[], matchpoint INT, circulate BOOL, duration_rule INT, recurring_fine_rule INT, max_fine_rule INT, hard_due_date INT, renewals INT, grace_period INTERVAL, limit_groups INT[] );
9217 CREATE OR REPLACE FUNCTION action.item_user_circ_test( circ_ou INT, match_item BIGINT, match_user INT, renewal BOOL ) RETURNS SETOF action.circ_matrix_test_result AS $func$
9219 user_object actor.usr%ROWTYPE;
9220 standing_penalty config.standing_penalty%ROWTYPE;
9221 item_object asset.copy%ROWTYPE;
9222 item_status_object config.copy_status%ROWTYPE;
9223 item_location_object asset.copy_location%ROWTYPE;
9224 result action.circ_matrix_test_result;
9225 circ_test action.found_circ_matrix_matchpoint;
9226 circ_matchpoint config.circ_matrix_matchpoint%ROWTYPE;
9227 circ_limit_set config.circ_limit_set%ROWTYPE;
9228 hold_ratio action.hold_stats%ROWTYPE;
9231 context_org_list INT[];
9234 -- Assume success unless we hit a failure condition
9235 result.success := TRUE;
9237 -- Need user info to look up matchpoints
9238 SELECT INTO user_object * FROM actor.usr WHERE id = match_user AND NOT deleted;
9240 -- (Insta)Fail if we couldn't find the user
9241 IF user_object.id IS NULL THEN
9242 result.fail_part := 'no_user';
9243 result.success := FALSE;
9249 -- Need item info to look up matchpoints
9250 SELECT INTO item_object * FROM asset.copy WHERE id = match_item AND NOT deleted;
9252 -- (Insta)Fail if we couldn't find the item
9253 IF item_object.id IS NULL THEN
9254 result.fail_part := 'no_item';
9255 result.success := FALSE;
9261 SELECT INTO circ_test * FROM action.find_circ_matrix_matchpoint(circ_ou, item_object, user_object, renewal);
9263 circ_matchpoint := circ_test.matchpoint;
9264 result.matchpoint := circ_matchpoint.id;
9265 result.circulate := circ_matchpoint.circulate;
9266 result.duration_rule := circ_matchpoint.duration_rule;
9267 result.recurring_fine_rule := circ_matchpoint.recurring_fine_rule;
9268 result.max_fine_rule := circ_matchpoint.max_fine_rule;
9269 result.hard_due_date := circ_matchpoint.hard_due_date;
9270 result.renewals := circ_matchpoint.renewals;
9271 result.grace_period := circ_matchpoint.grace_period;
9272 result.buildrows := circ_test.buildrows;
9274 -- (Insta)Fail if we couldn't find a matchpoint
9275 IF circ_test.success = false THEN
9276 result.fail_part := 'no_matchpoint';
9277 result.success := FALSE;
9283 -- All failures before this point are non-recoverable
9284 -- Below this point are possibly overridable failures
9286 -- Fail if the user is barred
9287 IF user_object.barred IS TRUE THEN
9288 result.fail_part := 'actor.usr.barred';
9289 result.success := FALSE;
9294 -- Fail if the item can't circulate
9295 IF item_object.circulate IS FALSE THEN
9296 result.fail_part := 'asset.copy.circulate';
9297 result.success := FALSE;
9302 -- Fail if the item isn't in a circulateable status on a non-renewal
9303 IF NOT renewal AND item_object.status NOT IN ( 0, 7, 8 ) THEN
9304 result.fail_part := 'asset.copy.status';
9305 result.success := FALSE;
9308 -- Alternately, fail if the item isn't checked out on a renewal
9309 ELSIF renewal AND item_object.status <> 1 THEN
9310 result.fail_part := 'asset.copy.status';
9311 result.success := FALSE;
9316 -- Fail if the item can't circulate because of the shelving location
9317 SELECT INTO item_location_object * FROM asset.copy_location WHERE id = item_object.location;
9318 IF item_location_object.circulate IS FALSE THEN
9319 result.fail_part := 'asset.copy_location.circulate';
9320 result.success := FALSE;
9325 -- Use Circ OU for penalties and such
9326 SELECT INTO context_org_list ARRAY_AGG(id) FROM actor.org_unit_full_path( circ_ou );
9329 penalty_type = '%RENEW%';
9331 penalty_type = '%CIRC%';
9334 FOR standing_penalty IN
9335 SELECT DISTINCT csp.*
9336 FROM actor.usr_standing_penalty usp
9337 JOIN config.standing_penalty csp ON (csp.id = usp.standing_penalty)
9338 WHERE usr = match_user
9339 AND usp.org_unit IN ( SELECT * FROM unnest(context_org_list) )
9340 AND (usp.stop_date IS NULL or usp.stop_date > NOW())
9341 AND csp.block_list LIKE penalty_type LOOP
9343 result.fail_part := standing_penalty.name;
9344 result.success := FALSE;
9349 -- Fail if the test is set to hard non-circulating
9350 IF circ_matchpoint.circulate IS FALSE THEN
9351 result.fail_part := 'config.circ_matrix_test.circulate';
9352 result.success := FALSE;
9357 -- Fail if the total copy-hold ratio is too low
9358 IF circ_matchpoint.total_copy_hold_ratio IS NOT NULL THEN
9359 SELECT INTO hold_ratio * FROM action.copy_related_hold_stats(match_item);
9360 IF hold_ratio.total_copy_ratio IS NOT NULL AND hold_ratio.total_copy_ratio < circ_matchpoint.total_copy_hold_ratio THEN
9361 result.fail_part := 'config.circ_matrix_test.total_copy_hold_ratio';
9362 result.success := FALSE;
9368 -- Fail if the available copy-hold ratio is too low
9369 IF circ_matchpoint.available_copy_hold_ratio IS NOT NULL THEN
9370 IF hold_ratio.hold_count IS NULL THEN
9371 SELECT INTO hold_ratio * FROM action.copy_related_hold_stats(match_item);
9373 IF hold_ratio.available_copy_ratio IS NOT NULL AND hold_ratio.available_copy_ratio < circ_matchpoint.available_copy_hold_ratio THEN
9374 result.fail_part := 'config.circ_matrix_test.available_copy_hold_ratio';
9375 result.success := FALSE;
9381 -- Fail if the user has too many items out by defined limit sets
9382 FOR circ_limit_set IN SELECT ccls.* FROM config.circ_limit_set ccls
9383 JOIN config.circ_matrix_limit_set_map ccmlsm ON ccmlsm.limit_set = ccls.id
9384 WHERE ccmlsm.active AND ( ccmlsm.matchpoint = circ_matchpoint.id OR
9385 ( ccmlsm.matchpoint IN (SELECT * FROM unnest(result.buildrows)) AND ccmlsm.fallthrough )
9387 IF circ_limit_set.items_out > 0 AND NOT renewal THEN
9388 SELECT INTO context_org_list ARRAY_AGG(aou.id)
9389 FROM actor.org_unit_full_path( circ_ou ) aou
9390 JOIN actor.org_unit_type aout ON aou.ou_type = aout.id
9391 WHERE aout.depth >= circ_limit_set.depth;
9392 IF circ_limit_set.global THEN
9393 WITH RECURSIVE descendant_depth AS (
9396 FROM actor.org_unit ou
9397 WHERE ou.id IN (SELECT * FROM unnest(context_org_list))
9401 FROM actor.org_unit ou
9402 JOIN descendant_depth ot ON (ot.id = ou.parent_ou)
9403 ) SELECT INTO context_org_list ARRAY_AGG(ou.id) FROM actor.org_unit ou JOIN descendant_depth USING (id);
9405 SELECT INTO items_out COUNT(DISTINCT circ.id)
9406 FROM action.circulation circ
9407 JOIN asset.copy copy ON (copy.id = circ.target_copy)
9408 LEFT JOIN action.circulation_limit_group_map aclgm ON (circ.id = aclgm.circ)
9409 WHERE circ.usr = match_user
9410 AND circ.circ_lib IN (SELECT * FROM unnest(context_org_list))
9411 AND circ.checkin_time IS NULL
9412 AND (circ.stop_fines IN ('MAXFINES','LONGOVERDUE') OR circ.stop_fines IS NULL)
9413 AND (copy.circ_modifier IN (SELECT circ_mod FROM config.circ_limit_set_circ_mod_map WHERE limit_set = circ_limit_set.id)
9414 OR aclgm.limit_group IN (SELECT limit_group FROM config.circ_limit_set_group_map WHERE limit_set = circ_limit_set.id)
9416 IF items_out >= circ_limit_set.items_out THEN
9417 result.fail_part := 'config.circ_matrix_circ_mod_test';
9418 result.success := FALSE;
9423 SELECT INTO result.limit_groups result.limit_groups || ARRAY_AGG(limit_group) FROM config.circ_limit_set_group_map WHERE limit_set = circ_limit_set.id AND NOT check_only;
9426 -- If we passed everything, return the successful matchpoint
9433 $func$ LANGUAGE plpgsql;
9435 -- We need to re-create these, as they got dropped with the type above.
9436 CREATE OR REPLACE FUNCTION action.item_user_circ_test( INT, BIGINT, INT ) RETURNS SETOF action.circ_matrix_test_result AS $func$
9437 SELECT * FROM action.item_user_circ_test( $1, $2, $3, FALSE );
9438 $func$ LANGUAGE SQL;
9440 CREATE OR REPLACE FUNCTION action.item_user_renew_test( INT, BIGINT, INT ) RETURNS SETOF action.circ_matrix_test_result AS $func$
9441 SELECT * FROM action.item_user_circ_test( $1, $2, $3, TRUE );
9442 $func$ LANGUAGE SQL;
9444 -- Temp function for migrating circ mod limits.
9445 CREATE OR REPLACE FUNCTION evergreen.temp_migrate_circ_mod_limits() RETURNS VOID AS $func$
9447 circ_mod_group config.circ_matrix_circ_mod_test%ROWTYPE;
9451 FOR circ_mod_group IN SELECT * FROM config.circ_matrix_circ_mod_test LOOP
9452 INSERT INTO config.circ_limit_set(name, owning_lib, items_out, depth, global, description)
9453 SELECT org_unit || ' : Matchpoint ' || circ_mod_group.matchpoint || ' : Circ Mod Test ' || circ_mod_group.id, org_unit, circ_mod_group.items_out, 0, false, 'Migrated from Circ Mod Test System'
9454 FROM config.circ_matrix_matchpoint WHERE id = circ_mod_group.matchpoint
9455 RETURNING id INTO current_set;
9456 INSERT INTO config.circ_matrix_limit_set_map(matchpoint, limit_set, fallthrough, active) VALUES (circ_mod_group.matchpoint, current_set, false, true);
9457 INSERT INTO config.circ_limit_set_circ_mod_map(limit_set, circ_mod)
9458 SELECT current_set, circ_mod FROM config.circ_matrix_circ_mod_test_map WHERE circ_mod_test = circ_mod_group.id;
9459 SELECT INTO circ_mod_count count(id) FROM config.circ_limit_set_circ_mod_map WHERE limit_set = current_set;
9460 RAISE NOTICE 'Created limit set with id % and % circ modifiers attached to matchpoint %', current_set, circ_mod_count, circ_mod_group.matchpoint;
9463 $func$ LANGUAGE plpgsql;
9465 -- Run the temp function
9466 SELECT * FROM evergreen.temp_migrate_circ_mod_limits();
9468 -- Drop the temp function
9469 DROP FUNCTION evergreen.temp_migrate_circ_mod_limits();
9471 --Drop the old tables
9472 --Not sure we want to do this. Keeping them may help "something went wrong" correction.
9473 --DROP TABLE IF EXISTS config.circ_matrix_circ_mod_test_map, config.circ_matrix_circ_mod_test;
9476 -- Evergreen DB patch 0678.data.vandelay-default-merge-profiles.sql
9478 -- check whether patch can be applied
9479 SELECT evergreen.upgrade_deps_block_check('0678', :eg_version);
9481 INSERT INTO vandelay.merge_profile (owner, name, replace_spec)
9482 VALUES (1, 'Match-Only Merge', '901c');
9484 INSERT INTO vandelay.merge_profile (owner, name, preserve_spec)
9485 VALUES (1, 'Full Overlay', '901c');
9487 -- Evergreen DB patch 0681.schema.user-activity.sql
9490 -- check whether patch can be applied
9491 SELECT evergreen.upgrade_deps_block_check('0681', :eg_version);
9495 CREATE TYPE config.usr_activity_group AS ENUM ('authen','authz','circ','hold','search');
9497 CREATE TABLE config.usr_activity_type (
9498 id SERIAL PRIMARY KEY,
9502 label TEXT NOT NULL, -- i18n
9503 egroup config.usr_activity_group NOT NULL,
9504 enabled BOOL NOT NULL DEFAULT TRUE,
9505 transient BOOL NOT NULL DEFAULT FALSE,
9506 CONSTRAINT one_of_wwh CHECK (COALESCE(ewho,ewhat,ehow) IS NOT NULL)
9509 CREATE UNIQUE INDEX unique_wwh ON config.usr_activity_type
9510 (COALESCE(ewho,''), COALESCE (ewhat,''), COALESCE(ehow,''));
9512 CREATE TABLE actor.usr_activity (
9513 id BIGSERIAL PRIMARY KEY,
9514 usr INT REFERENCES actor.usr (id) ON DELETE SET NULL,
9515 etype INT NOT NULL REFERENCES config.usr_activity_type (id),
9516 event_time TIMESTAMPTZ NOT NULL DEFAULT NOW()
9519 -- remove transient activity entries on insert of new entries
9520 CREATE OR REPLACE FUNCTION actor.usr_activity_transient_trg () RETURNS TRIGGER AS $$
9522 DELETE FROM actor.usr_activity act USING config.usr_activity_type atype
9523 WHERE atype.transient AND
9524 NEW.etype = atype.id AND
9525 act.etype = atype.id AND
9529 $$ LANGUAGE PLPGSQL;
9531 CREATE TRIGGER remove_transient_usr_activity
9532 BEFORE INSERT ON actor.usr_activity
9533 FOR EACH ROW EXECUTE PROCEDURE actor.usr_activity_transient_trg();
9535 -- given a set of activity criteria, find the most approprate activity type
9536 CREATE OR REPLACE FUNCTION actor.usr_activity_get_type (
9540 ) RETURNS SETOF config.usr_activity_type AS $$
9541 SELECT * FROM config.usr_activity_type
9544 (ewho IS NULL OR ewho = $1) AND
9545 (ewhat IS NULL OR ewhat = $2) AND
9546 (ehow IS NULL OR ehow = $3)
9548 -- BOOL comparisons sort false to true
9549 COALESCE(ewho, '') != COALESCE($1, ''),
9550 COALESCE(ewhat,'') != COALESCE($2, ''),
9551 COALESCE(ehow, '') != COALESCE($3, '')
9555 -- given a set of activity criteria, finds the best
9556 -- activity type and inserts the activity entry
9557 CREATE OR REPLACE FUNCTION actor.insert_usr_activity (
9562 ) RETURNS SETOF actor.usr_activity AS $$
9564 new_row actor.usr_activity%ROWTYPE;
9566 SELECT id INTO new_row.etype FROM actor.usr_activity_get_type(ewho, ewhat, ehow);
9569 INSERT INTO actor.usr_activity (usr, etype)
9570 VALUES (usr, new_row.etype)
9571 RETURNING * INTO new_row;
9572 RETURN NEXT new_row;
9575 $$ LANGUAGE plpgsql;
9579 INSERT INTO config.usr_activity_type (id, ewho, ewhat, ehow, egroup, label) VALUES
9581 -- authen/authz actions
9582 -- note: "opensrf" is the default ingress/ehow
9583 (1, NULL, 'login', 'opensrf', 'authen', oils_i18n_gettext(1 , 'Login via opensrf', 'cuat', 'label'))
9584 ,(2, NULL, 'login', 'srfsh', 'authen', oils_i18n_gettext(2 , 'Login via srfsh', 'cuat', 'label'))
9585 ,(3, NULL, 'login', 'gateway-v1', 'authen', oils_i18n_gettext(3 , 'Login via gateway-v1', 'cuat', 'label'))
9586 ,(4, NULL, 'login', 'translator-v1','authen', oils_i18n_gettext(4 , 'Login via translator-v1', 'cuat', 'label'))
9587 ,(5, NULL, 'login', 'xmlrpc', 'authen', oils_i18n_gettext(5 , 'Login via xmlrpc', 'cuat', 'label'))
9588 ,(6, NULL, 'login', 'remoteauth', 'authen', oils_i18n_gettext(6 , 'Login via remoteauth', 'cuat', 'label'))
9589 ,(7, NULL, 'login', 'sip2', 'authen', oils_i18n_gettext(7 , 'SIP2 Proxy Login', 'cuat', 'label'))
9590 ,(8, NULL, 'login', 'apache', 'authen', oils_i18n_gettext(8 , 'Login via Apache module', 'cuat', 'label'))
9592 ,(9, NULL, 'verify', 'opensrf', 'authz', oils_i18n_gettext(9 , 'Verification via opensrf', 'cuat', 'label'))
9593 ,(10, NULL, 'verify', 'srfsh', 'authz', oils_i18n_gettext(10, 'Verification via srfsh', 'cuat', 'label'))
9594 ,(11, NULL, 'verify', 'gateway-v1', 'authz', oils_i18n_gettext(11, 'Verification via gateway-v1', 'cuat', 'label'))
9595 ,(12, NULL, 'verify', 'translator-v1','authz', oils_i18n_gettext(12, 'Verification via translator-v1', 'cuat', 'label'))
9596 ,(13, NULL, 'verify', 'xmlrpc', 'authz', oils_i18n_gettext(13, 'Verification via xmlrpc', 'cuat', 'label'))
9597 ,(14, NULL, 'verify', 'remoteauth', 'authz', oils_i18n_gettext(14, 'Verification via remoteauth', 'cuat', 'label'))
9598 ,(15, NULL, 'verify', 'sip2', 'authz', oils_i18n_gettext(15, 'SIP2 User Verification', 'cuat', 'label'))
9600 -- authen/authz actions w/ known uses of "who"
9601 ,(16, 'opac', 'login', 'gateway-v1', 'authen', oils_i18n_gettext(16, 'OPAC Login (jspac)', 'cuat', 'label'))
9602 ,(17, 'opac', 'login', 'apache', 'authen', oils_i18n_gettext(17, 'OPAC Login (tpac)', 'cuat', 'label'))
9603 ,(18, 'staffclient', 'login', 'gateway-v1', 'authen', oils_i18n_gettext(18, 'Staff Client Login', 'cuat', 'label'))
9604 ,(19, 'selfcheck', 'login', 'translator-v1','authen', oils_i18n_gettext(19, 'Self-Check Proxy Login', 'cuat', 'label'))
9605 ,(20, 'ums', 'login', 'xmlrpc', 'authen', oils_i18n_gettext(20, 'Unique Mgt Login', 'cuat', 'label'))
9606 ,(21, 'authproxy', 'login', 'apache', 'authen', oils_i18n_gettext(21, 'Apache Auth Proxy Login', 'cuat', 'label'))
9607 ,(22, 'libraryelf', 'login', 'xmlrpc', 'authz', oils_i18n_gettext(22, 'LibraryElf Login', 'cuat', 'label'))
9609 ,(23, 'selfcheck', 'verify', 'translator-v1','authz', oils_i18n_gettext(23, 'Self-Check User Verification', 'cuat', 'label'))
9610 ,(24, 'ezproxy', 'verify', 'remoteauth', 'authz', oils_i18n_gettext(24, 'EZProxy Verification', 'cuat', 'label'))
9614 -- reserve the first 1000 slots
9615 SELECT SETVAL('config.usr_activity_type_id_seq'::TEXT, 1000);
9617 INSERT INTO config.org_unit_setting_type
9618 (name, label, description, grp, datatype)
9620 'circ.patron.usr_activity_retrieve.max',
9622 'circ.patron.usr_activity_retrieve.max',
9623 'Max user activity entries to retrieve (staff client)',
9628 'circ.patron.usr_activity_retrieve.max',
9629 'Sets the maxinum number of recent user activity entries to retrieve for display in the staff client. 0 means show none, -1 means show all. Default is 1.',
9638 SELECT evergreen.upgrade_deps_block_check('0682', :eg_version);
9640 CREATE TABLE asset.copy_location_group (
9641 id SERIAL PRIMARY KEY,
9642 name TEXT NOT NULL, -- i18n
9643 owner INT NOT NULL REFERENCES actor.org_unit (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
9644 pos INT NOT NULL DEFAULT 0,
9645 top BOOL NOT NULL DEFAULT FALSE,
9646 opac_visible BOOL NOT NULL DEFAULT TRUE,
9647 CONSTRAINT lgroup_once_per_owner UNIQUE (owner,name)
9650 CREATE TABLE asset.copy_location_group_map (
9651 id SERIAL PRIMARY KEY,
9652 location INT NOT NULL REFERENCES asset.copy_location (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
9653 lgroup INT NOT NULL REFERENCES asset.copy_location_group (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
9654 CONSTRAINT lgroup_once_per_group UNIQUE (lgroup,location)
9657 -- check whether patch can be applied
9658 SELECT evergreen.upgrade_deps_block_check('0683', :eg_version);
9660 INSERT INTO action_trigger.event_params (event_def, param, value)
9661 VALUES (5, 'check_email_notify', 1);
9662 INSERT INTO action_trigger.event_params (event_def, param, value)
9663 VALUES (7, 'check_email_notify', 1);
9664 INSERT INTO action_trigger.event_params (event_def, param, value)
9665 VALUES (9, 'check_email_notify', 1);
9666 INSERT INTO action_trigger.validator (module,description) VALUES
9670 'Check Hold notification flag(s)',
9674 UPDATE action_trigger.event_definition SET validator = 'HoldNotifyCheck' WHERE id = 9;
9676 -- NOT COVERED: Adding check_sms_notify to the proper trigger. It doesn't have a static id.
9678 -- check whether patch can be applied
9679 SELECT evergreen.upgrade_deps_block_check('0684', :eg_version);
9683 -- Replace the constraints with more flexible ENUM's
9684 ALTER TABLE vandelay.queue DROP CONSTRAINT queue_queue_type_check;
9685 ALTER TABLE vandelay.bib_queue DROP CONSTRAINT bib_queue_queue_type_check;
9686 ALTER TABLE vandelay.authority_queue DROP CONSTRAINT authority_queue_queue_type_check;
9688 CREATE TYPE vandelay.bib_queue_queue_type AS ENUM ('bib', 'acq');
9689 CREATE TYPE vandelay.authority_queue_queue_type AS ENUM ('authority');
9691 -- dropped column is also implemented by the child tables
9692 ALTER TABLE vandelay.queue DROP COLUMN queue_type;
9694 -- to recover after using the undo sql from below
9695 -- alter table vandelay.bib_queue add column queue_type text default 'bib' not null;
9696 -- alter table vandelay.authority_queue add column queue_type text default 'authority' not null;
9698 -- modify the child tables to use the ENUMs
9699 ALTER TABLE vandelay.bib_queue
9700 ALTER COLUMN queue_type DROP DEFAULT,
9701 ALTER COLUMN queue_type TYPE vandelay.bib_queue_queue_type
9702 USING (queue_type::vandelay.bib_queue_queue_type),
9703 ALTER COLUMN queue_type SET DEFAULT 'bib';
9705 ALTER TABLE vandelay.authority_queue
9706 ALTER COLUMN queue_type DROP DEFAULT,
9707 ALTER COLUMN queue_type TYPE vandelay.authority_queue_queue_type
9708 USING (queue_type::vandelay.authority_queue_queue_type),
9709 ALTER COLUMN queue_type SET DEFAULT 'authority';
9711 -- give lineitems a pointer to their vandelay queued_record
9713 ALTER TABLE acq.lineitem ADD COLUMN queued_record BIGINT
9714 REFERENCES vandelay.queued_bib_record (id)
9715 ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED;
9717 ALTER TABLE acq.acq_lineitem_history ADD COLUMN queued_record BIGINT
9718 REFERENCES vandelay.queued_bib_record (id)
9719 ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED;
9723 INSERT INTO permission.perm_list ( id, code, description )
9726 'IMPORT_ACQ_LINEITEM_BIB_RECORD_UPLOAD',
9729 'Allows a user to create new bibs directly from an ACQ MARC file upload',
9736 INSERT INTO vandelay.import_error ( code, description )
9738 'import.record.perm_failure',
9740 'import.record.perm_failure',
9741 'Perm failure creating a record', 'vie', 'description')
9747 -- Evergreen DB patch 0685.data.bluray_vr_format.sql
9749 -- FIXME: insert description of change, if needed
9753 -- check whether patch can be applied
9754 SELECT evergreen.upgrade_deps_block_check('0685', :eg_version);
9756 -- FIXME: add/check SQL statements to perform the upgrade
9761 -- Check if it is already there
9762 PERFORM * FROM config.marc21_physical_characteristic_value_map v
9763 JOIN config.marc21_physical_characteristic_subfield_map s ON v.ptype_subfield = s.id
9764 WHERE s.ptype_key = 'v' AND s.subfield = 'e' AND s.start_pos = '4' AND s.length = '1'
9772 -- Otherwise, insert it
9773 INSERT INTO config.marc21_physical_characteristic_value_map (value,ptype_subfield,label)
9774 SELECT 's',id,'Blu-ray'
9775 FROM config.marc21_physical_characteristic_subfield_map
9776 WHERE ptype_key = 'v' AND subfield = 'e' AND start_pos = '4' AND length = '1';
9778 -- And reingest the blue-ray items so that things see the new value
9779 SELECT INTO same_marc enabled FROM config.internal_flag WHERE name = 'ingest.reingest.force_on_same_marc';
9780 UPDATE config.internal_flag SET enabled = true WHERE name = 'ingest.reingest.force_on_same_marc';
9781 UPDATE biblio.record_entry SET marc=marc WHERE id IN (SELECT record
9783 metabib.full_rec a JOIN metabib.full_rec b USING (record)
9785 a.tag = 'LDR' AND a.value LIKE '______g%'
9786 AND b.tag = '007' AND b.value LIKE 'v___s%');
9787 UPDATE config.internal_flag SET enabled = same_marc WHERE name = 'ingest.reingest.force_on_same_marc';
9792 -- Evergreen DB patch 0686.schema.auditor_boost.sql
9794 -- FIXME: insert description of change, if needed
9796 -- check whether patch can be applied
9797 SELECT evergreen.upgrade_deps_block_check('0686', :eg_version);
9799 -- FIXME: add/check SQL statements to perform the upgrade
9800 -- These three functions are for capturing, getting, and clearing user and workstation information
9802 -- Set the User AND workstation in one call. Tis faster. And less calls.
9803 -- First argument is user, second is workstation
9804 CREATE OR REPLACE FUNCTION auditor.set_audit_info(INT, INT) RETURNS VOID AS $$
9805 $_SHARED{"eg_audit_user"} = $_[0];
9806 $_SHARED{"eg_audit_ws"} = $_[1];
9809 -- Get the User AND workstation in one call. Less calls, useful for joins ;)
9810 CREATE OR REPLACE FUNCTION auditor.get_audit_info() RETURNS TABLE (eg_user INT, eg_ws INT) AS $$
9811 return [{eg_user => $_SHARED{"eg_audit_user"}, eg_ws => $_SHARED{"eg_audit_ws"}}];
9814 -- Clear the audit info, for whatever reason
9815 CREATE OR REPLACE FUNCTION auditor.clear_audit_info() RETURNS VOID AS $$
9816 delete($_SHARED{"eg_audit_user"});
9817 delete($_SHARED{"eg_audit_ws"});
9820 CREATE OR REPLACE FUNCTION auditor.create_auditor_history ( sch TEXT, tbl TEXT ) RETURNS BOOL AS $creator$
9823 CREATE TABLE auditor.$$ || sch || $$_$$ || tbl || $$_history (
9824 audit_id BIGINT PRIMARY KEY,
9825 audit_time TIMESTAMP WITH TIME ZONE NOT NULL,
9826 audit_action TEXT NOT NULL,
9829 LIKE $$ || sch || $$.$$ || tbl || $$
9834 $creator$ LANGUAGE 'plpgsql';
9836 CREATE OR REPLACE FUNCTION auditor.create_auditor_func ( sch TEXT, tbl TEXT ) RETURNS BOOL AS $creator$
9840 SELECT INTO column_list array_agg(a.attname)
9841 FROM pg_catalog.pg_attribute a
9842 JOIN pg_catalog.pg_class c ON a.attrelid = c.oid
9843 JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
9844 WHERE relkind = 'r' AND n.nspname = sch AND c.relname = tbl AND a.attnum > 0 AND NOT a.attisdropped;
9847 CREATE OR REPLACE FUNCTION auditor.audit_$$ || sch || $$_$$ || tbl || $$_func ()
9848 RETURNS TRIGGER AS $func$
9850 INSERT INTO auditor.$$ || sch || $$_$$ || tbl || $$_history ( audit_id, audit_time, audit_action, audit_user, audit_ws, $$
9851 || array_to_string(column_list, ', ') || $$ )
9852 SELECT nextval('auditor.$$ || sch || $$_$$ || tbl || $$_pkey_seq'),
9857 OLD.$$ || array_to_string(column_list, ', OLD.') || $$
9858 FROM auditor.get_audit_info();
9861 $func$ LANGUAGE 'plpgsql';
9865 $creator$ LANGUAGE 'plpgsql';
9867 CREATE OR REPLACE FUNCTION auditor.create_auditor_lifecycle ( sch TEXT, tbl TEXT ) RETURNS BOOL AS $creator$
9871 SELECT INTO column_list array_agg(a.attname)
9872 FROM pg_catalog.pg_attribute a
9873 JOIN pg_catalog.pg_class c ON a.attrelid = c.oid
9874 JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
9875 WHERE relkind = 'r' AND n.nspname = sch AND c.relname = tbl AND a.attnum > 0 AND NOT a.attisdropped;
9878 CREATE VIEW auditor.$$ || sch || $$_$$ || tbl || $$_lifecycle AS
9879 SELECT -1 AS audit_id,
9880 now() AS audit_time,
9881 '-' AS audit_action,
9884 $$ || array_to_string(column_list, ', ') || $$
9885 FROM $$ || sch || $$.$$ || tbl || $$
9887 SELECT audit_id, audit_time, audit_action, audit_user, audit_ws,
9888 $$ || array_to_string(column_list, ', ') || $$
9889 FROM auditor.$$ || sch || $$_$$ || tbl || $$_history;
9893 $creator$ LANGUAGE 'plpgsql';
9895 -- Corrects all column discrepencies between audit table and core table:
9896 -- Adds missing columns
9897 -- Removes leftover columns
9899 -- Also, ensures all core auditor columns exist.
9900 CREATE OR REPLACE FUNCTION auditor.fix_columns() RETURNS VOID AS $BODY$
9902 current_table TEXT = ''; -- Storage for post-loop main table name
9903 current_audit_table TEXT = ''; -- Storage for post-loop audit table name
9904 query TEXT = ''; -- Storage for built query
9905 cr RECORD; -- column record object
9906 alter_t BOOL = false; -- Has the alter table command been appended yet
9907 auditor_cores TEXT[] = ARRAY[]::TEXT[]; -- Core auditor function list (filled inside of loop)
9908 core_column TEXT; -- The current core column we are adding
9911 WITH audit_tables AS ( -- Basic grab of auditor tables. Anything in the auditor namespace, basically. With oids.
9912 SELECT c.oid AS audit_oid, c.relname AS audit_table
9913 FROM pg_catalog.pg_class c
9914 JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
9915 WHERE relkind='r' AND nspname = 'auditor'
9917 table_set AS ( -- Union of auditor tables with their "main" tables. With oids.
9918 SELECT a.audit_oid, a.audit_table, c.oid AS main_oid, n.nspname as main_namespace, c.relname as main_table
9919 FROM pg_catalog.pg_class c
9920 JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
9921 JOIN audit_tables a ON a.audit_table = n.nspname || '_' || c.relname || '_history'
9924 column_lists AS ( -- All columns associated with the auditor or main table, grouped by the main table's oid.
9925 SELECT DISTINCT ON (main_oid, attname) t.main_oid, a.attname
9927 JOIN pg_catalog.pg_attribute a ON a.attrelid IN (t.main_oid, t.audit_oid)
9928 WHERE attnum > 0 AND NOT attisdropped
9930 column_defs AS ( -- The motherload, every audit table and main table plus column names and defs.
9934 a.attname AS main_column, -- These two will be null for columns that have since been deleted, or for auditor core columns
9935 pg_catalog.format_type(a.atttypid, a.atttypmod) AS main_column_def,
9936 b.attname AS audit_column, -- These two will be null for columns that have since been added
9937 pg_catalog.format_type(b.atttypid, b.atttypmod) AS audit_column_def
9939 JOIN column_lists c USING (main_oid)
9940 LEFT JOIN pg_catalog.pg_attribute a ON a.attname = c.attname AND a.attrelid = t.main_oid AND a.attnum > 0 AND NOT a.attisdropped
9941 LEFT JOIN pg_catalog.pg_attribute b ON b.attname = c.attname AND b.attrelid = t.audit_oid AND b.attnum > 0 AND NOT b.attisdropped
9943 -- Nice sorted output from the above
9944 SELECT * FROM column_defs WHERE main_column_def IS DISTINCT FROM audit_column_def ORDER BY main_namespace, main_table, main_column, audit_column
9946 IF current_table <> (cr.main_namespace || '.' || cr.main_table) THEN -- New table?
9947 FOR core_column IN SELECT DISTINCT unnest(auditor_cores) LOOP -- Update missing core auditor columns
9948 IF NOT alter_t THEN -- Add ALTER TABLE if we haven't already
9949 query:=query || $$ALTER TABLE auditor.$$ || current_audit_table;
9952 query:=query || $$,$$;
9954 -- Bit of a sneaky bit here. Create audit_id as a bigserial so it gets automatic values and doesn't complain about nulls when becoming a PRIMARY KEY.
9955 query:=query || $$ ADD COLUMN $$ || CASE WHEN core_column = 'audit_id bigint' THEN $$audit_id bigserial PRIMARY KEY$$ ELSE core_column END;
9957 IF alter_t THEN -- Open alter table = needs a semicolon
9958 query:=query || $$; $$;
9960 IF 'audit_id bigint' = ANY(auditor_cores) THEN -- We added a primary key...
9961 -- Fun! Drop the default on audit_id, drop the auto-created sequence, create a new one, and set the current value
9962 -- For added fun, we have to execute in chunks due to the parser checking setval/currval arguments at parse time.
9964 EXECUTE $$ALTER TABLE auditor.$$ || current_audit_table || $$ ALTER COLUMN audit_id DROP DEFAULT; $$ ||
9965 $$CREATE SEQUENCE auditor.$$ || current_audit_table || $$_pkey_seq;$$;
9966 EXECUTE $$SELECT setval('auditor.$$ || current_audit_table || $$_pkey_seq', currval('auditor.$$ || current_audit_table || $$_audit_id_seq')); $$ ||
9967 $$DROP SEQUENCE auditor.$$ || current_audit_table || $$_audit_id_seq;$$;
9971 -- New table means we reset the list of needed auditor core columns
9972 auditor_cores = ARRAY['audit_id bigint', 'audit_time timestamp with time zone', 'audit_action text', 'audit_user integer', 'audit_ws integer'];
9973 -- And store some values for use later, because we can't rely on cr in all places.
9974 current_table:=cr.main_namespace || '.' || cr.main_table;
9975 current_audit_table:=cr.audit_table;
9977 IF cr.main_column IS NULL AND cr.audit_column LIKE 'audit_%' THEN -- Core auditor column?
9978 -- Remove core from list of cores
9979 SELECT INTO auditor_cores array_agg(core) FROM unnest(auditor_cores) AS core WHERE core != (cr.audit_column || ' ' || cr.audit_column_def);
9980 ELSIF cr.main_column IS NULL THEN -- Main column doesn't exist, and it isn't an auditor column. Needs dropping from the auditor.
9982 query:=query || $$ALTER TABLE auditor.$$ || current_audit_table;
9985 query:=query || $$,$$;
9987 query:=query || $$ DROP COLUMN $$ || cr.audit_column;
9988 ELSIF cr.audit_column IS NULL AND cr.main_column IS NOT NULL THEN -- New column auditor doesn't have. Add it.
9990 query:=query || $$ALTER TABLE auditor.$$ || current_audit_table;
9993 query:=query || $$,$$;
9995 query:=query || $$ ADD COLUMN $$ || cr.main_column || $$ $$ || cr.main_column_def;
9996 ELSIF cr.main_column IS NOT NULL AND cr.audit_column IS NOT NULL THEN -- Both sides have this column, but types differ. Fix that.
9998 query:=query || $$ALTER TABLE auditor.$$ || current_audit_table;
10001 query:=query || $$,$$;
10003 query:=query || $$ ALTER COLUMN $$ || cr.audit_column || $$ TYPE $$ || cr.main_column_def;
10006 FOR core_column IN SELECT DISTINCT unnest(auditor_cores) LOOP -- Repeat this outside of the loop to catch the last table
10007 IF NOT alter_t THEN
10008 query:=query || $$ALTER TABLE auditor.$$ || current_audit_table;
10011 query:=query || $$,$$;
10013 -- Bit of a sneaky bit here. Create audit_id as a bigserial so it gets automatic values and doesn't complain about nulls when becoming a PRIMARY KEY.
10014 query:=query || $$ ADD COLUMN $$ || CASE WHEN core_column = 'audit_id bigint' THEN $$audit_id bigserial PRIMARY KEY$$ ELSE core_column END;
10016 IF alter_t THEN -- Open alter table = needs a semicolon
10017 query:=query || $$;$$;
10018 IF 'audit_id bigint' = ANY(auditor_cores) THEN -- We added a primary key...
10019 -- Fun! Drop the default on audit_id, drop the auto-created sequence, create a new one, and set the current value
10020 -- For added fun, we have to execute in chunks due to the parser checking setval/currval arguments at parse time.
10022 EXECUTE $$ALTER TABLE auditor.$$ || current_audit_table || $$ ALTER COLUMN audit_id DROP DEFAULT; $$ ||
10023 $$CREATE SEQUENCE auditor.$$ || current_audit_table || $$_pkey_seq;$$;
10024 EXECUTE $$SELECT setval('auditor.$$ || current_audit_table || $$_pkey_seq', currval('auditor.$$ || current_audit_table || $$_audit_id_seq')); $$ ||
10025 $$DROP SEQUENCE auditor.$$ || current_audit_table || $$_audit_id_seq;$$;
10031 $BODY$ LANGUAGE plpgsql;
10033 -- Update it all routine
10034 CREATE OR REPLACE FUNCTION auditor.update_auditors() RETURNS boolean AS $BODY$
10040 -- Drop Lifecycle view(s) before potential column changes
10041 FOR auditor_name IN
10043 FROM pg_catalog.pg_class c
10044 JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
10045 WHERE relkind = 'v' AND n.nspname = 'auditor' LOOP
10046 EXECUTE $$ DROP VIEW auditor.$$ || auditor_name || $$;$$;
10048 -- Fix all column discrepencies
10049 PERFORM auditor.fix_columns();
10050 -- Re-create trigger functions and lifecycle views
10051 FOR table_schema, table_name IN
10052 WITH audit_tables AS (
10053 SELECT c.oid AS audit_oid, c.relname AS audit_table
10054 FROM pg_catalog.pg_class c
10055 JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
10056 WHERE relkind='r' AND nspname = 'auditor'
10059 SELECT a.audit_oid, a.audit_table, c.oid AS main_oid, n.nspname as main_namespace, c.relname as main_table
10060 FROM pg_catalog.pg_class c
10061 JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
10062 JOIN audit_tables a ON a.audit_table = n.nspname || '_' || c.relname || '_history'
10063 WHERE relkind = 'r'
10065 SELECT main_namespace, main_table FROM table_set LOOP
10067 PERFORM auditor.create_auditor_func(table_schema, table_name);
10068 PERFORM auditor.create_auditor_lifecycle(table_schema, table_name);
10072 $BODY$ LANGUAGE plpgsql;
10074 -- Go ahead and update them all now
10075 SELECT auditor.update_auditors();
10078 -- Evergreen DB patch 0687.schema.enhance_reingest.sql
10080 -- FIXME: insert description of change, if needed
10084 -- check whether patch can be applied
10085 SELECT evergreen.upgrade_deps_block_check('0687', :eg_version);
10086 SELECT evergreen.upgrade_deps_block_check('0711', :eg_version); -- introduces
10087 -- changes to metabib.reingest_metabib_field_entries() that must happen here
10088 -- rather than later in a separate CREATE OR REPLACE FUNCTION statement.
10090 -- FIXME: add/check SQL statements to perform the upgrade
10091 -- New function def
10092 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$
10095 ind_data metabib.field_entry_template%ROWTYPE;
10096 mbe_row metabib.browse_entry%ROWTYPE;
10098 normalized_value TEXT;
10100 PERFORM * FROM config.internal_flag WHERE name = 'ingest.assume_inserts_only' AND enabled;
10102 IF NOT skip_search THEN
10103 FOR fclass IN SELECT * FROM config.metabib_class LOOP
10104 -- RAISE NOTICE 'Emptying out %', fclass.name;
10105 EXECUTE $$DELETE FROM metabib.$$ || fclass.name || $$_field_entry WHERE source = $$ || bib_id;
10108 IF NOT skip_facet THEN
10109 DELETE FROM metabib.facet_entry WHERE source = bib_id;
10111 IF NOT skip_browse THEN
10112 DELETE FROM metabib.browse_entry_def_map WHERE source = bib_id;
10116 FOR ind_data IN SELECT * FROM biblio.extract_metabib_field_entry( bib_id ) LOOP
10117 IF ind_data.field < 0 THEN
10118 ind_data.field = -1 * ind_data.field;
10121 IF ind_data.facet_field AND NOT skip_facet THEN
10122 INSERT INTO metabib.facet_entry (field, source, value)
10123 VALUES (ind_data.field, ind_data.source, ind_data.value);
10126 IF ind_data.browse_field AND NOT skip_browse THEN
10127 -- A caveat about this SELECT: this should take care of replacing
10128 -- old mbe rows when data changes, but not if normalization (by
10129 -- which I mean specifically the output of
10130 -- evergreen.oils_tsearch2()) changes. It may or may not be
10131 -- expensive to add a comparison of index_vector to index_vector
10132 -- to the WHERE clause below.
10133 normalized_value := metabib.browse_normalize(
10134 ind_data.value, ind_data.field
10137 SELECT INTO mbe_row * FROM metabib.browse_entry WHERE value = normalized_value;
10139 mbe_id := mbe_row.id;
10141 INSERT INTO metabib.browse_entry (value) VALUES (normalized_value);
10142 mbe_id := CURRVAL('metabib.browse_entry_id_seq'::REGCLASS);
10145 INSERT INTO metabib.browse_entry_def_map (entry, def, source)
10146 VALUES (mbe_id, ind_data.field, ind_data.source);
10149 IF ind_data.search_field AND NOT skip_search THEN
10151 INSERT INTO metabib.$$ || ind_data.field_class || $$_field_entry (field, source, value)
10153 quote_literal(ind_data.field) || $$, $$ ||
10154 quote_literal(ind_data.source) || $$, $$ ||
10155 quote_literal(ind_data.value) ||
10163 $func$ LANGUAGE PLPGSQL;
10166 DROP FUNCTION IF EXISTS metabib.reingest_metabib_field_entries(BIGINT);
10168 -- Evergreen DB patch 0688.data.circ_history_export_csv.sql
10170 -- FIXME: insert description of change, if needed
10173 -- check whether patch can be applied
10174 SELECT evergreen.upgrade_deps_block_check('0688', :eg_version);
10176 INSERT INTO action_trigger.hook (key, core_type, description, passive)
10178 'circ.format.history.csv',
10181 'circ.format.history.csv',
10182 'Produce CSV of circulation history',
10189 INSERT INTO action_trigger.event_definition (
10190 active, owner, name, hook, reactor, validator, group_field, template)
10192 TRUE, 1, 'Circ History CSV', 'circ.format.history.csv', 'ProcessTemplate', 'NOOP_True', 'usr',
10194 Title,Author,Call Number,Barcode,Format
10196 FOR circ IN target;
10197 bibxml = helpers.unapi_bre(circ.target_copy.call_number.record, {flesh => '{mra}'});
10199 FOR part IN bibxml.findnodes('//*[@tag="245"]/*[@code="a" or @code="b"]');
10200 title = title _ part.textContent;
10202 author = bibxml.findnodes('//*[@tag="100"]/*[@code="a"]').textContent;
10203 item_type = bibxml.findnodes('//*[local-name()="attributes"]/*[local-name()="field"][@name="item_type"]').getAttribute('coded-value') %]
10205 [%- helpers.csv_datum(title) -%],
10206 [%- helpers.csv_datum(author) -%],
10207 [%- helpers.csv_datum(circ.target_copy.call_number.label) -%],
10208 [%- helpers.csv_datum(circ.target_copy.barcode) -%],
10209 [%- helpers.csv_datum(item_type) %]
10214 INSERT INTO action_trigger.environment (event_def, path)
10216 currval('action_trigger.event_definition_id_seq'),
10217 'target_copy.call_number'
10221 -- Evergreen DB patch 0689.data.record_print_format_update.sql
10223 -- Updates print and email templates for bib record actions
10226 -- check whether patch can be applied
10227 SELECT evergreen.upgrade_deps_block_check('0689', :eg_version);
10229 UPDATE action_trigger.event_definition SET template = $$
10231 <style> li { padding: 8px; margin 5px; }</style>
10233 [% FOR cbreb IN target %]
10234 [% FOR item IN cbreb.items;
10235 bre_id = item.target_biblio_record_entry;
10237 bibxml = helpers.unapi_bre(bre_id, {flesh => '{mra}'});
10238 FOR part IN bibxml.findnodes('//*[@tag="245"]/*[@code="a" or @code="b"]');
10239 title = title _ part.textContent;
10242 author = bibxml.findnodes('//*[@tag="100"]/*[@code="a"]').textContent;
10243 item_type = bibxml.findnodes('//*[local-name()="attributes"]/*[local-name()="field"][@name="item_type"]').getAttribute('coded-value');
10244 publisher = bibxml.findnodes('//*[@tag="260"]/*[@code="b"]').textContent;
10245 pubdate = bibxml.findnodes('//*[@tag="260"]/*[@code="c"]').textContent;
10246 isbn = bibxml.findnodes('//*[@tag="020"]/*[@code="a"]').textContent;
10247 issn = bibxml.findnodes('//*[@tag="022"]/*[@code="a"]').textContent;
10248 upc = bibxml.findnodes('//*[@tag="024"]/*[@code="a"]').textContent;
10252 Bib ID# [% bre_id %]<br/>
10253 [% IF isbn %]ISBN: [% isbn %]<br/>[% END %]
10254 [% IF issn %]ISSN: [% issn %]<br/>[% END %]
10255 [% IF upc %]UPC: [% upc %]<br/>[% END %]
10256 Title: [% title %]<br />
10257 Author: [% author %]<br />
10258 Publication Info: [% publisher %] [% pubdate %]<br/>
10259 Item Type: [% item_type %]
10266 WHERE hook = 'biblio.format.record_entry.print' AND id < 100; -- sample data
10269 UPDATE action_trigger.event_definition SET delay = '00:00:00', template = $$
10270 [%- SET user = target.0.owner -%]
10271 To: [%- params.recipient_email || user.email %]
10272 From: [%- params.sender_email || default_sender %]
10273 Subject: Bibliographic Records
10275 [% FOR cbreb IN target %]
10276 [% FOR item IN cbreb.items;
10277 bre_id = item.target_biblio_record_entry;
10279 bibxml = helpers.unapi_bre(bre_id, {flesh => '{mra}'});
10280 FOR part IN bibxml.findnodes('//*[@tag="245"]/*[@code="a" or @code="b"]');
10281 title = title _ part.textContent;
10284 author = bibxml.findnodes('//*[@tag="100"]/*[@code="a"]').textContent;
10285 item_type = bibxml.findnodes('//*[local-name()="attributes"]/*[local-name()="field"][@name="item_type"]').getAttribute('coded-value');
10286 publisher = bibxml.findnodes('//*[@tag="260"]/*[@code="b"]').textContent;
10287 pubdate = bibxml.findnodes('//*[@tag="260"]/*[@code="c"]').textContent;
10288 isbn = bibxml.findnodes('//*[@tag="020"]/*[@code="a"]').textContent;
10289 issn = bibxml.findnodes('//*[@tag="022"]/*[@code="a"]').textContent;
10290 upc = bibxml.findnodes('//*[@tag="024"]/*[@code="a"]').textContent;
10293 [% loop.count %]/[% loop.size %]. Bib ID# [% bre_id %]
10294 [% IF isbn %]ISBN: [% isbn _ "\n" %][% END -%]
10295 [% IF issn %]ISSN: [% issn _ "\n" %][% END -%]
10296 [% IF upc %]UPC: [% upc _ "\n" %] [% END -%]
10298 Author: [% author %]
10299 Publication Info: [% publisher %] [% pubdate %]
10300 Item Type: [% item_type %]
10305 WHERE hook = 'biblio.format.record_entry.email' AND id < 100; -- sample data
10307 -- remove a swath of unused environment entries
10309 DELETE FROM action_trigger.environment env
10310 USING action_trigger.event_definition def
10311 WHERE env.event_def = def.id AND
10312 env.path != 'items' AND
10313 def.hook = 'biblio.format.record_entry.print' AND
10314 def.id < 100; -- sample data
10316 DELETE FROM action_trigger.environment env
10317 USING action_trigger.event_definition def
10318 WHERE env.event_def = def.id AND
10319 env.path != 'items' AND
10320 env.path != 'owner' AND
10321 def.hook = 'biblio.format.record_entry.email' AND
10322 def.id < 100; -- sample data
10324 -- Evergreen DB patch 0690.schema.unapi_limit_rank.sql
10326 -- Rewrite the in-database unapi functions to include per-object limits and
10327 -- offsets, such as a maximum number of copies and call numbers for given
10328 -- bib record via the HSTORE syntax (for example, 'acn => 5, acp => 10' would
10329 -- limit to a maximum of 5 call numbers for the bib, with up to 10 copies per
10332 -- Add some notion of "preferred library" that will provide copy counts
10333 -- and optionally affect the sorting of returned copies.
10335 -- Sort copies by availability, preferring the most available copies.
10337 -- Return located URIs.
10341 -- check whether patch can be applied
10342 SELECT evergreen.upgrade_deps_block_check('0690', :eg_version);
10344 -- The simplest way to apply all of these changes is just to replace the unapi
10345 -- schema entirely -- the following is a copy of 990.schema.unapi.sql with
10346 -- the initial COMMIT in place in case the upgrade_deps_block_check fails;
10347 -- if it does, then the attempt to create the unapi schema in the following
10348 -- transaction will also fail. Not graceful, but safe!
10349 DROP SCHEMA IF EXISTS unapi CASCADE;
10351 CREATE SCHEMA unapi;
10353 CREATE OR REPLACE FUNCTION evergreen.org_top()
10354 RETURNS SETOF actor.org_unit AS $$
10355 SELECT * FROM actor.org_unit WHERE parent_ou IS NULL LIMIT 1;
10356 $$ LANGUAGE SQL STABLE
10359 CREATE OR REPLACE FUNCTION evergreen.array_remove_item_by_value(inp ANYARRAY, el ANYELEMENT)
10360 RETURNS anyarray AS $$
10361 SELECT ARRAY_ACCUM(x.e) FROM UNNEST( $1 ) x(e) WHERE x.e <> $2;
10362 $$ LANGUAGE SQL STABLE;
10364 CREATE OR REPLACE FUNCTION evergreen.rank_ou(lib INT, search_lib INT, pref_lib INT DEFAULT NULL)
10365 RETURNS INTEGER AS $$
10366 WITH search_libs AS (
10367 SELECT id, distance FROM actor.org_unit_descendants_distance($2)
10370 (SELECT -10000 FROM actor.org_unit
10371 WHERE $1 = $3 AND id = $3 AND $2 IN (
10372 SELECT id FROM actor.org_unit WHERE parent_ou IS NULL
10375 (SELECT distance FROM search_libs WHERE id = $1),
10378 $$ LANGUAGE SQL STABLE;
10380 CREATE OR REPLACE FUNCTION evergreen.rank_cp_status(status INT)
10381 RETURNS INTEGER AS $$
10382 WITH totally_available AS (
10383 SELECT id, 0 AS avail_rank
10384 FROM config.copy_status
10385 WHERE opac_visible IS TRUE
10386 AND copy_active IS TRUE
10387 AND id != 1 -- "Checked out"
10388 ), almost_available AS (
10389 SELECT id, 10 AS avail_rank
10390 FROM config.copy_status
10391 WHERE holdable IS TRUE
10392 AND opac_visible IS TRUE
10393 AND copy_active IS FALSE
10394 OR id = 1 -- "Checked out"
10397 (SELECT avail_rank FROM totally_available WHERE $1 IN (id)),
10398 (SELECT avail_rank FROM almost_available WHERE $1 IN (id)),
10401 $$ LANGUAGE SQL STABLE;
10403 CREATE OR REPLACE FUNCTION evergreen.ranked_volumes(
10406 depth INT DEFAULT NULL,
10407 slimit HSTORE DEFAULT NULL,
10408 soffset HSTORE DEFAULT NULL,
10409 pref_lib INT DEFAULT NULL
10410 ) RETURNS TABLE (id BIGINT, name TEXT, label_sortkey TEXT, rank BIGINT) AS $$
10411 SELECT ua.id, ua.name, ua.label_sortkey, MIN(ua.rank) AS rank FROM (
10412 SELECT acn.id, aou.name, acn.label_sortkey,
10413 evergreen.rank_ou(aou.id, $2, $6), evergreen.rank_cp_status(acp.status),
10415 FROM asset.call_number acn
10416 JOIN asset.copy acp ON (acn.id = acp.call_number)
10417 JOIN actor.org_unit_descendants( $2, COALESCE(
10420 FROM actor.org_unit_type aout
10421 INNER JOIN actor.org_unit ou ON ou_type = aout.id
10424 ) AS aou ON (acp.circ_lib = aou.id)
10425 WHERE acn.record = $1
10426 AND acn.deleted IS FALSE
10427 AND acp.deleted IS FALSE
10428 GROUP BY acn.id, acp.status, aou.name, acn.label_sortkey, aou.id
10430 ORDER BY evergreen.rank_ou(aou.id, $2, $6), evergreen.rank_cp_status(acp.status)
10433 GROUP BY ua.id, ua.name, ua.label_sortkey
10434 ORDER BY rank, ua.name, ua.label_sortkey
10435 LIMIT ($4 -> 'acn')::INT
10436 OFFSET ($5 -> 'acn')::INT;
10438 LANGUAGE SQL STABLE;
10440 CREATE OR REPLACE FUNCTION evergreen.located_uris (
10443 pref_lib INT DEFAULT NULL
10444 ) RETURNS TABLE (id BIGINT, name TEXT, label_sortkey TEXT, rank INT) AS $$
10445 SELECT acn.id, aou.name, acn.label_sortkey, evergreen.rank_ou(aou.id, $2, $3) AS pref_ou
10446 FROM asset.call_number acn
10447 INNER JOIN asset.uri_call_number_map auricnm ON acn.id = auricnm.call_number
10448 INNER JOIN asset.uri auri ON auri.id = auricnm.uri
10449 INNER JOIN actor.org_unit_ancestors( COALESCE($3, $2) ) aou ON (acn.owning_lib = aou.id)
10450 WHERE acn.record = $1
10451 AND acn.deleted IS FALSE
10452 AND auri.active IS TRUE
10454 SELECT acn.id, aou.name, acn.label_sortkey, evergreen.rank_ou(aou.id, $2, $3) AS pref_ou
10455 FROM asset.call_number acn
10456 INNER JOIN asset.uri_call_number_map auricnm ON acn.id = auricnm.call_number
10457 INNER JOIN asset.uri auri ON auri.id = auricnm.uri
10458 INNER JOIN actor.org_unit_ancestors( $2 ) aou ON (acn.owning_lib = aou.id)
10459 WHERE acn.record = $1
10460 AND acn.deleted IS FALSE
10461 AND auri.active IS TRUE;
10463 LANGUAGE SQL STABLE;
10465 CREATE TABLE unapi.bre_output_layout (
10466 name TEXT PRIMARY KEY,
10467 transform TEXT REFERENCES config.xml_transform (name) ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE INITIALLY DEFERRED,
10468 mime_type TEXT NOT NULL,
10469 feed_top TEXT NOT NULL,
10470 holdings_element TEXT,
10471 title_element TEXT,
10472 description_element TEXT,
10473 creator_element TEXT,
10474 update_ts_element TEXT
10477 INSERT INTO unapi.bre_output_layout
10478 (name, transform, mime_type, holdings_element, feed_top, title_element, description_element, creator_element, update_ts_element)
10480 ('holdings_xml', NULL, 'application/xml', NULL, 'hxml', NULL, NULL, NULL, NULL),
10481 ('marcxml', 'marcxml', 'application/marc+xml', 'record', 'collection', NULL, NULL, NULL, NULL),
10482 ('mods32', 'mods32', 'application/mods+xml', 'mods', 'modsCollection', NULL, NULL, NULL, NULL)
10485 -- Dummy functions, so we can create the real ones out of order
10486 CREATE OR REPLACE FUNCTION unapi.aou ( 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 ) RETURNS XML AS $F$ SELECT NULL::XML $F$ LANGUAGE SQL STABLE;
10487 CREATE OR REPLACE FUNCTION unapi.acnp ( 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 ) RETURNS XML AS $F$ SELECT NULL::XML $F$ LANGUAGE SQL STABLE;
10488 CREATE OR REPLACE FUNCTION unapi.acns ( 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 ) RETURNS XML AS $F$ SELECT NULL::XML $F$ LANGUAGE SQL STABLE;
10489 CREATE OR REPLACE FUNCTION unapi.acn ( 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 ) RETURNS XML AS $F$ SELECT NULL::XML $F$ LANGUAGE SQL STABLE;
10490 CREATE OR REPLACE FUNCTION unapi.ssub ( 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 ) RETURNS XML AS $F$ SELECT NULL::XML $F$ LANGUAGE SQL STABLE;
10491 CREATE OR REPLACE FUNCTION unapi.sdist ( 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 ) RETURNS XML AS $F$ SELECT NULL::XML $F$ LANGUAGE SQL STABLE;
10492 CREATE OR REPLACE FUNCTION unapi.sstr ( 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 ) RETURNS XML AS $F$ SELECT NULL::XML $F$ LANGUAGE SQL STABLE;
10493 CREATE OR REPLACE FUNCTION unapi.sitem ( 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 ) RETURNS XML AS $F$ SELECT NULL::XML $F$ LANGUAGE SQL STABLE;
10494 CREATE OR REPLACE FUNCTION unapi.sunit ( 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 ) RETURNS XML AS $F$ SELECT NULL::XML $F$ LANGUAGE SQL STABLE;
10495 CREATE OR REPLACE FUNCTION unapi.sisum ( 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 ) RETURNS XML AS $F$ SELECT NULL::XML $F$ LANGUAGE SQL STABLE;
10496 CREATE OR REPLACE FUNCTION unapi.sbsum ( 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 ) RETURNS XML AS $F$ SELECT NULL::XML $F$ LANGUAGE SQL STABLE;
10497 CREATE OR REPLACE FUNCTION unapi.sssum ( 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 ) RETURNS XML AS $F$ SELECT NULL::XML $F$ LANGUAGE SQL STABLE;
10498 CREATE OR REPLACE FUNCTION unapi.siss ( 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 ) RETURNS XML AS $F$ SELECT NULL::XML $F$ LANGUAGE SQL STABLE;
10499 CREATE OR REPLACE FUNCTION unapi.auri ( 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 ) RETURNS XML AS $F$ SELECT NULL::XML $F$ LANGUAGE SQL STABLE;
10500 CREATE OR REPLACE FUNCTION unapi.acp ( 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 ) RETURNS XML AS $F$ SELECT NULL::XML $F$ LANGUAGE SQL STABLE;
10501 CREATE OR REPLACE FUNCTION unapi.acpn ( 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 ) RETURNS XML AS $F$ SELECT NULL::XML $F$ LANGUAGE SQL STABLE;
10502 CREATE OR REPLACE FUNCTION unapi.acl ( 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 ) RETURNS XML AS $F$ SELECT NULL::XML $F$ LANGUAGE SQL STABLE;
10503 CREATE OR REPLACE FUNCTION unapi.ccs ( 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 ) RETURNS XML AS $F$ SELECT NULL::XML $F$ LANGUAGE SQL STABLE;
10504 CREATE OR REPLACE FUNCTION unapi.ascecm ( 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 ) RETURNS XML AS $F$ SELECT NULL::XML $F$ LANGUAGE SQL STABLE;
10505 CREATE OR REPLACE FUNCTION unapi.bre (
10511 depth INT DEFAULT NULL,
10512 slimit HSTORE DEFAULT NULL,
10513 soffset HSTORE DEFAULT NULL,
10514 include_xmlns BOOL DEFAULT TRUE,
10515 pref_lib INT DEFAULT NULL
10517 RETURNS XML AS $F$ SELECT NULL::XML $F$ LANGUAGE SQL STABLE;
10518 CREATE OR REPLACE FUNCTION unapi.bmp ( 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 ) RETURNS XML AS $F$ SELECT NULL::XML $F$ LANGUAGE SQL STABLE;
10519 CREATE OR REPLACE FUNCTION unapi.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 ) RETURNS XML AS $F$ SELECT NULL::XML $F$ LANGUAGE SQL STABLE;
10520 CREATE OR REPLACE FUNCTION unapi.circ ( obj_id BIGINT, format TEXT, ename TEXT, includes TEXT[], org TEXT DEFAULT '-', depth INT DEFAULT NULL, slimit HSTORE DEFAULT NULL, soffset HSTORE DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$ SELECT NULL::XML $F$ LANGUAGE SQL STABLE;
10522 CREATE OR REPLACE FUNCTION unapi.holdings_xml (
10526 depth INT DEFAULT NULL,
10527 includes TEXT[] DEFAULT NULL::TEXT[],
10528 slimit HSTORE DEFAULT NULL,
10529 soffset HSTORE DEFAULT NULL,
10530 include_xmlns BOOL DEFAULT TRUE,
10531 pref_lib INT DEFAULT NULL
10533 RETURNS XML AS $F$ SELECT NULL::XML $F$ LANGUAGE SQL STABLE;
10535 CREATE OR REPLACE FUNCTION unapi.biblio_record_entry_feed ( id_list BIGINT[], format TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit HSTORE DEFAULT NULL, soffset HSTORE DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE, title TEXT DEFAULT NULL, description TEXT DEFAULT NULL, creator TEXT DEFAULT NULL, update_ts TEXT DEFAULT NULL, unapi_url TEXT DEFAULT NULL, header_xml XML DEFAULT NULL ) RETURNS XML AS $F$ SELECT NULL::XML $F$ LANGUAGE SQL STABLE;
10537 CREATE OR REPLACE FUNCTION unapi.memoize (classname TEXT, 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 ) RETURNS XML AS $F$
10543 'id' || COALESCE(obj_id::TEXT,'') ||
10544 'format' || COALESCE(format::TEXT,'') ||
10545 'ename' || COALESCE(ename::TEXT,'') ||
10546 'includes' || COALESCE(includes::TEXT,'{}'::TEXT[]::TEXT) ||
10547 'org' || COALESCE(org::TEXT,'') ||
10548 'depth' || COALESCE(depth::TEXT,'') ||
10549 'slimit' || COALESCE(slimit::TEXT,'') ||
10550 'soffset' || COALESCE(soffset::TEXT,'') ||
10551 'include_xmlns' || COALESCE(include_xmlns::TEXT,'');
10552 -- RAISE NOTICE 'memoize key: %', key;
10555 -- RAISE NOTICE 'memoize hash: %', key;
10557 -- XXX cache logic ... memcached? table?
10559 EXECUTE $$SELECT unapi.$$ || classname || $$( $1, $2, $3, $4, $5, $6, $7, $8, $9);$$ INTO output USING obj_id, format, ename, includes, org, depth, slimit, soffset, include_xmlns;
10562 $F$ LANGUAGE PLPGSQL STABLE;
10564 CREATE OR REPLACE FUNCTION unapi.biblio_record_entry_feed ( id_list BIGINT[], format TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit HSTORE DEFAULT NULL, soffset HSTORE DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE, title TEXT DEFAULT NULL, description TEXT DEFAULT NULL, creator TEXT DEFAULT NULL, update_ts TEXT DEFAULT NULL, unapi_url TEXT DEFAULT NULL, header_xml XML DEFAULT NULL ) RETURNS XML AS $F$
10566 layout unapi.bre_output_layout%ROWTYPE;
10567 transform config.xml_transform%ROWTYPE;
10570 xmlns_uri TEXT := 'http://open-ils.org/spec/feed-xml/v1';
10572 element_list TEXT[];
10575 IF org = '-' OR org IS NULL THEN
10576 SELECT shortname INTO org FROM evergreen.org_top();
10579 SELECT id INTO ouid FROM actor.org_unit WHERE shortname = org;
10580 SELECT * INTO layout FROM unapi.bre_output_layout WHERE name = format;
10582 IF layout.name IS NULL THEN
10586 SELECT * INTO transform FROM config.xml_transform WHERE name = layout.transform;
10587 xmlns_uri := COALESCE(transform.namespace_uri,xmlns_uri);
10589 -- Gather the bib xml
10590 SELECT XMLAGG( unapi.bre(i, format, '', includes, org, depth, slimit, soffset, include_xmlns)) INTO tmp_xml FROM UNNEST( id_list ) i;
10592 IF layout.title_element IS NOT NULL THEN
10593 EXECUTE 'SELECT XMLCONCAT( XMLELEMENT( name '|| layout.title_element ||', XMLATTRIBUTES( $1 AS xmlns), $3), $2)' INTO tmp_xml USING xmlns_uri, tmp_xml::XML, title;
10596 IF layout.description_element IS NOT NULL THEN
10597 EXECUTE 'SELECT XMLCONCAT( XMLELEMENT( name '|| layout.description_element ||', XMLATTRIBUTES( $1 AS xmlns), $3), $2)' INTO tmp_xml USING xmlns_uri, tmp_xml::XML, description;
10600 IF layout.creator_element IS NOT NULL THEN
10601 EXECUTE 'SELECT XMLCONCAT( XMLELEMENT( name '|| layout.creator_element ||', XMLATTRIBUTES( $1 AS xmlns), $3), $2)' INTO tmp_xml USING xmlns_uri, tmp_xml::XML, creator;
10604 IF layout.update_ts_element IS NOT NULL THEN
10605 EXECUTE 'SELECT XMLCONCAT( XMLELEMENT( name '|| layout.update_ts_element ||', XMLATTRIBUTES( $1 AS xmlns), $3), $2)' INTO tmp_xml USING xmlns_uri, tmp_xml::XML, update_ts;
10608 IF unapi_url IS NOT NULL THEN
10609 EXECUTE $$SELECT XMLCONCAT( XMLELEMENT( name link, XMLATTRIBUTES( 'http://www.w3.org/1999/xhtml' AS xmlns, 'unapi-server' AS rel, $1 AS href, 'unapi' AS title)), $2)$$ INTO tmp_xml USING unapi_url, tmp_xml::XML;
10612 IF header_xml IS NOT NULL THEN tmp_xml := XMLCONCAT(header_xml,tmp_xml::XML); END IF;
10614 element_list := regexp_split_to_array(layout.feed_top,E'\\.');
10615 FOR i IN REVERSE ARRAY_UPPER(element_list, 1) .. 1 LOOP
10616 EXECUTE 'SELECT XMLELEMENT( name '|| quote_ident(element_list[i]) ||', XMLATTRIBUTES( $1 AS xmlns), $2)' INTO tmp_xml USING xmlns_uri, tmp_xml::XML;
10619 RETURN tmp_xml::XML;
10621 $F$ LANGUAGE PLPGSQL STABLE;
10623 CREATE OR REPLACE FUNCTION unapi.bre (
10629 depth INT DEFAULT NULL,
10630 slimit HSTORE DEFAULT NULL,
10631 soffset HSTORE DEFAULT NULL,
10632 include_xmlns BOOL DEFAULT TRUE,
10633 pref_lib INT DEFAULT NULL
10637 me biblio.record_entry%ROWTYPE;
10638 layout unapi.bre_output_layout%ROWTYPE;
10639 xfrm config.xml_transform%ROWTYPE;
10648 IF org = '-' OR org IS NULL THEN
10649 SELECT shortname INTO org FROM evergreen.org_top();
10652 SELECT id INTO ouid FROM actor.org_unit WHERE shortname = org;
10654 IF ouid IS NULL THEN
10658 IF format = 'holdings_xml' THEN -- the special case
10659 output := unapi.holdings_xml( obj_id, ouid, org, depth, includes, slimit, soffset, include_xmlns);
10663 SELECT * INTO layout FROM unapi.bre_output_layout WHERE name = format;
10665 IF layout.name IS NULL THEN
10669 SELECT * INTO xfrm FROM config.xml_transform WHERE name = layout.transform;
10671 SELECT * INTO me FROM biblio.record_entry WHERE id = obj_id;
10673 -- grab SVF if we need them
10674 IF ('mra' = ANY (includes)) THEN
10675 axml := unapi.mra(obj_id,NULL,NULL,NULL,NULL);
10680 -- grab holdings if we need them
10681 IF ('holdings_xml' = ANY (includes)) THEN
10682 hxml := unapi.holdings_xml(obj_id, ouid, org, depth, evergreen.array_remove_item_by_value(includes,'holdings_xml'), slimit, soffset, include_xmlns, pref_lib);
10688 -- generate our item node
10691 IF format = 'marcxml' THEN
10692 tmp_xml := me.marc;
10693 IF tmp_xml !~ E'<marc:' THEN -- If we're not using the prefixed namespace in this record, then remove all declarations of it
10694 tmp_xml := REGEXP_REPLACE(tmp_xml, ' xmlns:marc="http://www.loc.gov/MARC21/slim"', '', 'g');
10697 tmp_xml := oils_xslt_process(me.marc, xfrm.xslt)::XML;
10700 top_el := REGEXP_REPLACE(tmp_xml, E'^.*?<((?:\\S+:)?' || layout.holdings_element || ').*$', E'\\1');
10702 IF axml IS NOT NULL THEN
10703 tmp_xml := REGEXP_REPLACE(tmp_xml, '</' || top_el || '>(.*?)$', axml || '</' || top_el || E'>\\1');
10706 IF hxml IS NOT NULL THEN -- XXX how do we configure the holdings position?
10707 tmp_xml := REGEXP_REPLACE(tmp_xml, '</' || top_el || '>(.*?)$', hxml || '</' || top_el || E'>\\1');
10710 IF ('bre.unapi' = ANY (includes)) THEN
10711 output := REGEXP_REPLACE(
10713 '</' || top_el || '>(.*?)',
10717 'http://www.w3.org/1999/xhtml' AS xmlns,
10718 'unapi-id' AS class,
10719 'tag:open-ils.org:U2@bre/' || obj_id || '/' || org AS title
10721 )::TEXT || '</' || top_el || E'>\\1'
10727 output := REGEXP_REPLACE(output::TEXT,E'>\\s+<','><','gs')::XML;
10730 $F$ LANGUAGE PLPGSQL STABLE;
10732 CREATE OR REPLACE FUNCTION unapi.holdings_xml (
10736 depth INT DEFAULT NULL,
10737 includes TEXT[] DEFAULT NULL::TEXT[],
10738 slimit HSTORE DEFAULT NULL,
10739 soffset HSTORE DEFAULT NULL,
10740 include_xmlns BOOL DEFAULT TRUE,
10741 pref_lib INT DEFAULT NULL
10747 CASE WHEN $8 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
10748 CASE WHEN ('bre' = ANY ($5)) THEN 'tag:open-ils.org:U2@bre/' || $1 || '/' || $3 ELSE NULL END AS id
10752 (SELECT XMLAGG(XMLELEMENT::XML) FROM (
10755 XMLATTRIBUTES('public' as type, depth, org_unit, coalesce(transcendant,0) as transcendant, available, visible as count, unshadow)
10757 FROM asset.opac_ou_record_copy_count($2, $1)
10761 XMLATTRIBUTES('staff' as type, depth, org_unit, coalesce(transcendant,0) as transcendant, available, visible as count, unshadow)
10763 FROM asset.staff_ou_record_copy_count($2, $1)
10767 XMLATTRIBUTES('pref_lib' as type, depth, org_unit, coalesce(transcendant,0) as transcendant, available, visible as count, unshadow)
10769 FROM asset.opac_ou_record_copy_count($9, $1)
10774 WHEN ('bmp' = ANY ($5)) THEN
10776 name monograph_parts,
10777 (SELECT XMLAGG(bmp) FROM (
10778 SELECT unapi.bmp( id, 'xml', 'monograph_part', evergreen.array_remove_item_by_value( evergreen.array_remove_item_by_value($5,'bre'), 'holdings_xml'), $3, $4, $6, $7, FALSE)
10779 FROM biblio.monograph_part
10787 (SELECT XMLAGG(acn ORDER BY rank, name, label_sortkey) FROM (
10789 SELECT unapi.acn(y.id,'xml','volume',evergreen.array_remove_item_by_value( evergreen.array_remove_item_by_value($5,'holdings_xml'),'bre'), $3, $4, $6, $7, FALSE), y.rank, name, label_sortkey
10790 FROM evergreen.ranked_volumes($1, $2, $4, $6, $7, $9) AS y
10793 SELECT unapi.acn(uris.id,'xml','volume',evergreen.array_remove_item_by_value( evergreen.array_remove_item_by_value($5,'holdings_xml'),'bre'), $3, $4, $6, $7, FALSE), 0, name, label_sortkey
10794 FROM evergreen.located_uris($1, $2, $9) AS uris
10797 CASE WHEN ('ssub' = ANY ($5)) THEN
10799 name subscriptions,
10800 (SELECT XMLAGG(ssub) FROM (
10801 SELECT unapi.ssub(id,'xml','subscription','{}'::TEXT[], $3, $4, $6, $7, FALSE)
10802 FROM serial.subscription
10803 WHERE record_entry = $1
10807 CASE WHEN ('acp' = ANY ($5)) THEN
10809 name foreign_copies,
10810 (SELECT XMLAGG(acp) FROM (
10811 SELECT unapi.acp(p.target_copy,'xml','copy',evergreen.array_remove_item_by_value($5,'acp'), $3, $4, $6, $7, FALSE)
10812 FROM biblio.peer_bib_copy_map p
10813 JOIN asset.copy c ON (p.target_copy = c.id)
10814 WHERE NOT c.deleted AND p.peer_record = $1
10815 LIMIT ($6 -> 'acp')::INT
10816 OFFSET ($7 -> 'acp')::INT
10821 $F$ LANGUAGE SQL STABLE;
10823 CREATE OR REPLACE FUNCTION unapi.ssub ( 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 ) RETURNS XML AS $F$
10827 CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
10828 'tag:open-ils.org:U2@ssub/' || id AS id,
10829 'tag:open-ils.org:U2@aou/' || owning_lib AS owning_lib,
10830 start_date AS start, end_date AS end, expected_date_offset
10833 WHEN ('sdist' = ANY ($4)) THEN
10834 XMLELEMENT( name distributions,
10835 (SELECT XMLAGG(sdist) FROM (
10836 SELECT unapi.sdist( id, 'xml', 'distribution', evergreen.array_remove_item_by_value($4,'ssub'), $5, $6, $7, $8, FALSE)
10837 FROM serial.distribution
10838 WHERE subscription = ssub.id
10844 FROM serial.subscription ssub
10846 GROUP BY id, start_date, end_date, expected_date_offset, owning_lib;
10847 $F$ LANGUAGE SQL STABLE;
10849 CREATE OR REPLACE FUNCTION unapi.sdist ( 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 ) RETURNS XML AS $F$
10853 CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
10854 'tag:open-ils.org:U2@sdist/' || id AS id,
10855 'tag:open-ils.org:U2@acn/' || receive_call_number AS receive_call_number,
10856 'tag:open-ils.org:U2@acn/' || bind_call_number AS bind_call_number,
10857 unit_label_prefix, label, unit_label_suffix, summary_method
10859 unapi.aou( holding_lib, $2, 'holding_lib', evergreen.array_remove_item_by_value($4,'sdist'), $5, $6, $7, $8),
10860 CASE WHEN subscription IS NOT NULL AND ('ssub' = ANY ($4)) THEN unapi.ssub( subscription, 'xml', 'subscription', evergreen.array_remove_item_by_value($4,'sdist'), $5, $6, $7, $8, FALSE) ELSE NULL END,
10862 WHEN ('sstr' = ANY ($4)) THEN
10863 XMLELEMENT( name streams,
10864 (SELECT XMLAGG(sstr) FROM (
10865 SELECT unapi.sstr( id, 'xml', 'stream', evergreen.array_remove_item_by_value($4,'sdist'), $5, $6, $7, $8, FALSE)
10867 WHERE distribution = sdist.id
10872 XMLELEMENT( name summaries,
10874 WHEN ('sbsum' = ANY ($4)) THEN
10875 (SELECT XMLAGG(sbsum) FROM (
10876 SELECT unapi.sbsum( id, 'xml', 'serial_summary', evergreen.array_remove_item_by_value($4,'sdist'), $5, $6, $7, $8, FALSE)
10877 FROM serial.basic_summary
10878 WHERE distribution = sdist.id
10883 WHEN ('sisum' = ANY ($4)) THEN
10884 (SELECT XMLAGG(sisum) FROM (
10885 SELECT unapi.sisum( id, 'xml', 'serial_summary', evergreen.array_remove_item_by_value($4,'sdist'), $5, $6, $7, $8, FALSE)
10886 FROM serial.index_summary
10887 WHERE distribution = sdist.id
10892 WHEN ('sssum' = ANY ($4)) THEN
10893 (SELECT XMLAGG(sssum) FROM (
10894 SELECT unapi.sssum( id, 'xml', 'serial_summary', evergreen.array_remove_item_by_value($4,'sdist'), $5, $6, $7, $8, FALSE)
10895 FROM serial.supplement_summary
10896 WHERE distribution = sdist.id
10902 FROM serial.distribution sdist
10904 GROUP BY id, label, unit_label_prefix, unit_label_suffix, holding_lib, summary_method, subscription, receive_call_number, bind_call_number;
10905 $F$ LANGUAGE SQL STABLE;
10907 CREATE OR REPLACE FUNCTION unapi.sstr ( 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 ) RETURNS XML AS $F$
10911 CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
10912 'tag:open-ils.org:U2@sstr/' || id AS id,
10915 CASE WHEN distribution IS NOT NULL AND ('sdist' = ANY ($4)) THEN unapi.sssum( distribution, 'xml', 'distribtion', evergreen.array_remove_item_by_value($4,'sstr'), $5, $6, $7, $8, FALSE) ELSE NULL END,
10917 WHEN ('sitem' = ANY ($4)) THEN
10918 XMLELEMENT( name items,
10919 (SELECT XMLAGG(sitem) FROM (
10920 SELECT unapi.sitem( id, 'xml', 'serial_item', evergreen.array_remove_item_by_value($4,'sstr'), $5, $6, $7, $8, FALSE)
10922 WHERE stream = sstr.id
10928 FROM serial.stream sstr
10930 GROUP BY id, routing_label, distribution;
10931 $F$ LANGUAGE SQL STABLE;
10933 CREATE OR REPLACE FUNCTION unapi.siss ( 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 ) RETURNS XML AS $F$
10937 CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
10938 'tag:open-ils.org:U2@siss/' || id AS id,
10939 create_date, edit_date, label, date_published,
10940 holding_code, holding_type, holding_link_id
10942 CASE WHEN subscription IS NOT NULL AND ('ssub' = ANY ($4)) THEN unapi.ssub( subscription, 'xml', 'subscription', evergreen.array_remove_item_by_value($4,'siss'), $5, $6, $7, $8, FALSE) ELSE NULL END,
10944 WHEN ('sitem' = ANY ($4)) THEN
10945 XMLELEMENT( name items,
10946 (SELECT XMLAGG(sitem) FROM (
10947 SELECT unapi.sitem( id, 'xml', 'serial_item', evergreen.array_remove_item_by_value($4,'siss'), $5, $6, $7, $8, FALSE)
10949 WHERE issuance = sstr.id
10955 FROM serial.issuance sstr
10957 GROUP BY id, create_date, edit_date, label, date_published, holding_code, holding_type, holding_link_id, subscription;
10958 $F$ LANGUAGE SQL STABLE;
10960 CREATE OR REPLACE FUNCTION unapi.sitem ( 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 ) RETURNS XML AS $F$
10964 CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
10965 'tag:open-ils.org:U2@sitem/' || id AS id,
10966 'tag:open-ils.org:U2@siss/' || issuance AS issuance,
10967 date_expected, date_received
10969 CASE WHEN issuance IS NOT NULL AND ('siss' = ANY ($4)) THEN unapi.siss( issuance, $2, 'issuance', evergreen.array_remove_item_by_value($4,'sitem'), $5, $6, $7, $8, FALSE) ELSE NULL END,
10970 CASE WHEN stream IS NOT NULL AND ('sstr' = ANY ($4)) THEN unapi.sstr( stream, $2, 'stream', evergreen.array_remove_item_by_value($4,'sitem'), $5, $6, $7, $8, FALSE) ELSE NULL END,
10971 CASE WHEN unit IS NOT NULL AND ('sunit' = ANY ($4)) THEN unapi.sunit( unit, $2, 'serial_unit', evergreen.array_remove_item_by_value($4,'sitem'), $5, $6, $7, $8, FALSE) ELSE NULL END,
10972 CASE WHEN uri IS NOT NULL AND ('auri' = ANY ($4)) THEN unapi.auri( uri, $2, 'uri', evergreen.array_remove_item_by_value($4,'sitem'), $5, $6, $7, $8, FALSE) ELSE NULL END
10973 -- XMLELEMENT( name notes,
10975 -- WHEN ('acpn' = ANY ($4)) THEN
10976 -- (SELECT XMLAGG(acpn) FROM (
10977 -- SELECT unapi.acpn( id, 'xml', 'copy_note', evergreen.array_remove_item_by_value($4,'acp'), $5, $6, $7, $8)
10978 -- FROM asset.copy_note
10979 -- WHERE owning_copy = cp.id AND pub
10985 FROM serial.item sitem
10987 $F$ LANGUAGE SQL STABLE;
10990 CREATE OR REPLACE FUNCTION unapi.sssum ( 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 ) RETURNS XML AS $F$
10992 name serial_summary,
10994 CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
10995 'tag:open-ils.org:U2@sbsum/' || id AS id,
10996 'sssum' AS type, generated_coverage, textual_holdings, show_generated
10998 CASE WHEN ('sdist' = ANY ($4)) THEN unapi.sdist( distribution, 'xml', 'distribtion', evergreen.array_remove_item_by_value($4,'ssum'), $5, $6, $7, $8, FALSE) ELSE NULL END
11000 FROM serial.supplement_summary ssum
11002 GROUP BY id, generated_coverage, textual_holdings, distribution, show_generated;
11003 $F$ LANGUAGE SQL STABLE;
11005 CREATE OR REPLACE FUNCTION unapi.sbsum ( 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 ) RETURNS XML AS $F$
11007 name serial_summary,
11009 CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
11010 'tag:open-ils.org:U2@sbsum/' || id AS id,
11011 'sbsum' AS type, generated_coverage, textual_holdings, show_generated
11013 CASE WHEN ('sdist' = ANY ($4)) THEN unapi.sdist( distribution, 'xml', 'distribtion', evergreen.array_remove_item_by_value($4,'ssum'), $5, $6, $7, $8, FALSE) ELSE NULL END
11015 FROM serial.basic_summary ssum
11017 GROUP BY id, generated_coverage, textual_holdings, distribution, show_generated;
11018 $F$ LANGUAGE SQL STABLE;
11020 CREATE OR REPLACE FUNCTION unapi.sisum ( 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 ) RETURNS XML AS $F$
11022 name serial_summary,
11024 CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
11025 'tag:open-ils.org:U2@sbsum/' || id AS id,
11026 'sisum' AS type, generated_coverage, textual_holdings, show_generated
11028 CASE WHEN ('sdist' = ANY ($4)) THEN unapi.sdist( distribution, 'xml', 'distribtion', evergreen.array_remove_item_by_value($4,'ssum'), $5, $6, $7, $8, FALSE) ELSE NULL END
11030 FROM serial.index_summary ssum
11032 GROUP BY id, generated_coverage, textual_holdings, distribution, show_generated;
11033 $F$ LANGUAGE SQL STABLE;
11036 CREATE OR REPLACE FUNCTION unapi.aou ( 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 ) RETURNS XML AS $F$
11040 IF ename = 'circlib' THEN
11044 'http://open-ils.org/spec/actors/v1' AS xmlns,
11049 FROM actor.org_unit aou
11052 EXECUTE $$SELECT XMLELEMENT(
11053 name $$ || ename || $$,
11055 'http://open-ils.org/spec/actors/v1' AS xmlns,
11056 'tag:open-ils.org:U2@aou/' || id AS id,
11057 shortname, name, opac_visible
11060 FROM actor.org_unit aou
11061 WHERE id = $1 $$ INTO output USING obj_id;
11067 $F$ LANGUAGE PLPGSQL STABLE;
11069 CREATE OR REPLACE FUNCTION unapi.acl ( 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 ) RETURNS XML AS $F$
11073 CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
11077 label_prefix AS prefix,
11078 label_suffix AS suffix
11082 FROM asset.copy_location
11084 $F$ LANGUAGE SQL STABLE;
11086 CREATE OR REPLACE FUNCTION unapi.ccs ( 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 ) RETURNS XML AS $F$
11090 CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
11097 FROM config.copy_status
11099 $F$ LANGUAGE SQL STABLE;
11101 CREATE OR REPLACE FUNCTION unapi.acpn ( 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 ) RETURNS XML AS $F$
11105 CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
11106 create_date AS date,
11111 FROM asset.copy_note
11113 $F$ LANGUAGE SQL STABLE;
11115 CREATE OR REPLACE FUNCTION unapi.ascecm ( 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 ) RETURNS XML AS $F$
11119 CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
11125 FROM asset.stat_cat_entry asce
11126 JOIN asset.stat_cat sc ON (sc.id = asce.stat_cat)
11127 WHERE asce.id = $1;
11128 $F$ LANGUAGE SQL STABLE;
11130 CREATE OR REPLACE FUNCTION unapi.bmp ( 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 ) RETURNS XML AS $F$
11132 name monograph_part,
11134 CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
11135 'tag:open-ils.org:U2@bmp/' || id AS id,
11139 'tag:open-ils.org:U2@bre/' || record AS record
11142 WHEN ('acp' = ANY ($4)) THEN
11143 XMLELEMENT( name copies,
11144 (SELECT XMLAGG(acp) FROM (
11145 SELECT unapi.acp( cp.id, 'xml', 'copy', evergreen.array_remove_item_by_value($4,'bmp'), $5, $6, $7, $8, FALSE)
11147 JOIN asset.copy_part_map cpm ON (cpm.target_copy = cp.id)
11148 WHERE cpm.part = $1
11149 AND cp.deleted IS FALSE
11150 ORDER BY COALESCE(cp.copy_number,0), cp.barcode
11151 LIMIT ($7 -> 'acp')::INT
11152 OFFSET ($8 -> 'acp')::INT
11158 CASE WHEN ('bre' = ANY ($4)) THEN unapi.bre( record, 'marcxml', 'record', evergreen.array_remove_item_by_value($4,'bmp'), $5, $6, $7, $8, FALSE) ELSE NULL END
11160 FROM biblio.monograph_part
11162 GROUP BY id, label, label_sortkey, record;
11163 $F$ LANGUAGE SQL STABLE;
11165 CREATE OR REPLACE FUNCTION unapi.acp ( 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 ) RETURNS XML AS $F$
11169 CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
11170 'tag:open-ils.org:U2@acp/' || id AS id, id AS copy_id,
11171 create_date, edit_date, copy_number, circulate, deposit,
11172 ref, holdable, deleted, deposit_amount, price, barcode,
11173 circ_modifier, circ_as_type, opac_visible, age_protect
11175 unapi.ccs( status, $2, 'status', evergreen.array_remove_item_by_value($4,'acp'), $5, $6, $7, $8, FALSE),
11176 unapi.acl( location, $2, 'location', evergreen.array_remove_item_by_value($4,'acp'), $5, $6, $7, $8, FALSE),
11177 unapi.aou( circ_lib, $2, 'circ_lib', evergreen.array_remove_item_by_value($4,'acp'), $5, $6, $7, $8),
11178 unapi.aou( circ_lib, $2, 'circlib', evergreen.array_remove_item_by_value($4,'acp'), $5, $6, $7, $8),
11179 CASE WHEN ('acn' = ANY ($4)) THEN unapi.acn( call_number, $2, 'call_number', evergreen.array_remove_item_by_value($4,'acp'), $5, $6, $7, $8, FALSE) ELSE NULL END,
11181 WHEN ('acpn' = ANY ($4)) THEN
11182 XMLELEMENT( name copy_notes,
11183 (SELECT XMLAGG(acpn) FROM (
11184 SELECT unapi.acpn( id, 'xml', 'copy_note', evergreen.array_remove_item_by_value($4,'acp'), $5, $6, $7, $8, FALSE)
11185 FROM asset.copy_note
11186 WHERE owning_copy = cp.id AND pub
11192 WHEN ('ascecm' = ANY ($4)) THEN
11193 XMLELEMENT( name statcats,
11194 (SELECT XMLAGG(ascecm) FROM (
11195 SELECT unapi.ascecm( stat_cat_entry, 'xml', 'statcat', evergreen.array_remove_item_by_value($4,'acp'), $5, $6, $7, $8, FALSE)
11196 FROM asset.stat_cat_entry_copy_map
11197 WHERE owning_copy = cp.id
11203 WHEN ('bre' = ANY ($4)) THEN
11204 XMLELEMENT( name foreign_records,
11205 (SELECT XMLAGG(bre) FROM (
11206 SELECT unapi.bre(peer_record,'marcxml','record','{}'::TEXT[], $5, $6, $7, $8, FALSE)
11207 FROM biblio.peer_bib_copy_map
11208 WHERE target_copy = cp.id
11215 WHEN ('bmp' = ANY ($4)) THEN
11216 XMLELEMENT( name monograph_parts,
11217 (SELECT XMLAGG(bmp) FROM (
11218 SELECT unapi.bmp( part, 'xml', 'monograph_part', evergreen.array_remove_item_by_value($4,'acp'), $5, $6, $7, $8, FALSE)
11219 FROM asset.copy_part_map
11220 WHERE target_copy = cp.id
11226 WHEN ('circ' = ANY ($4)) THEN
11227 XMLELEMENT( name current_circulation,
11228 (SELECT XMLAGG(circ) FROM (
11229 SELECT unapi.circ( id, 'xml', 'circ', evergreen.array_remove_item_by_value($4,'circ'), $5, $6, $7, $8, FALSE)
11230 FROM action.circulation
11231 WHERE target_copy = cp.id
11232 AND checkin_time IS NULL
11240 AND cp.deleted IS FALSE
11241 GROUP BY id, status, location, circ_lib, call_number, create_date,
11242 edit_date, copy_number, circulate, deposit, ref, holdable,
11243 deleted, deposit_amount, price, barcode, circ_modifier,
11244 circ_as_type, opac_visible, age_protect;
11245 $F$ LANGUAGE SQL STABLE;
11247 CREATE OR REPLACE FUNCTION unapi.sunit ( 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 ) RETURNS XML AS $F$
11251 CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
11252 'tag:open-ils.org:U2@acp/' || id AS id, id AS copy_id,
11253 create_date, edit_date, copy_number, circulate, deposit,
11254 ref, holdable, deleted, deposit_amount, price, barcode,
11255 circ_modifier, circ_as_type, opac_visible, age_protect,
11256 status_changed_time, floating, mint_condition,
11257 detailed_contents, sort_key, summary_contents, cost
11259 unapi.ccs( status, $2, 'status', evergreen.array_remove_item_by_value( evergreen.array_remove_item_by_value($4,'acp'),'sunit'), $5, $6, $7, $8, FALSE),
11260 unapi.acl( location, $2, 'location', evergreen.array_remove_item_by_value( evergreen.array_remove_item_by_value($4,'acp'),'sunit'), $5, $6, $7, $8, FALSE),
11261 unapi.aou( circ_lib, $2, 'circ_lib', evergreen.array_remove_item_by_value( evergreen.array_remove_item_by_value($4,'acp'),'sunit'), $5, $6, $7, $8),
11262 unapi.aou( circ_lib, $2, 'circlib', evergreen.array_remove_item_by_value( evergreen.array_remove_item_by_value($4,'acp'),'sunit'), $5, $6, $7, $8),
11263 CASE WHEN ('acn' = ANY ($4)) THEN unapi.acn( call_number, $2, 'call_number', evergreen.array_remove_item_by_value($4,'acp'), $5, $6, $7, $8, FALSE) ELSE NULL END,
11264 XMLELEMENT( name copy_notes,
11266 WHEN ('acpn' = ANY ($4)) THEN
11267 (SELECT XMLAGG(acpn) FROM (
11268 SELECT unapi.acpn( id, 'xml', 'copy_note', evergreen.array_remove_item_by_value( evergreen.array_remove_item_by_value($4,'acp'),'sunit'), $5, $6, $7, $8, FALSE)
11269 FROM asset.copy_note
11270 WHERE owning_copy = cp.id AND pub
11275 XMLELEMENT( name statcats,
11277 WHEN ('ascecm' = ANY ($4)) THEN
11278 (SELECT XMLAGG(ascecm) FROM (
11279 SELECT unapi.ascecm( stat_cat_entry, 'xml', 'statcat', evergreen.array_remove_item_by_value($4,'acp'), $5, $6, $7, $8, FALSE)
11280 FROM asset.stat_cat_entry_copy_map
11281 WHERE owning_copy = cp.id
11286 XMLELEMENT( name foreign_records,
11288 WHEN ('bre' = ANY ($4)) THEN
11289 (SELECT XMLAGG(bre) FROM (
11290 SELECT unapi.bre(peer_record,'marcxml','record','{}'::TEXT[], $5, $6, $7, $8, FALSE)
11291 FROM biblio.peer_bib_copy_map
11292 WHERE target_copy = cp.id
11298 WHEN ('bmp' = ANY ($4)) THEN
11299 XMLELEMENT( name monograph_parts,
11300 (SELECT XMLAGG(bmp) FROM (
11301 SELECT unapi.bmp( part, 'xml', 'monograph_part', evergreen.array_remove_item_by_value($4,'acp'), $5, $6, $7, $8, FALSE)
11302 FROM asset.copy_part_map
11303 WHERE target_copy = cp.id
11309 WHEN ('circ' = ANY ($4)) THEN
11310 XMLELEMENT( name current_circulation,
11311 (SELECT XMLAGG(circ) FROM (
11312 SELECT unapi.circ( id, 'xml', 'circ', evergreen.array_remove_item_by_value($4,'circ'), $5, $6, $7, $8, FALSE)
11313 FROM action.circulation
11314 WHERE target_copy = cp.id
11315 AND checkin_time IS NULL
11321 FROM serial.unit cp
11323 AND cp.deleted IS FALSE
11324 GROUP BY id, status, location, circ_lib, call_number, create_date,
11325 edit_date, copy_number, circulate, floating, mint_condition,
11326 deposit, ref, holdable, deleted, deposit_amount, price,
11327 barcode, circ_modifier, circ_as_type, opac_visible,
11328 status_changed_time, detailed_contents, sort_key,
11329 summary_contents, cost, age_protect;
11330 $F$ LANGUAGE SQL STABLE;
11332 CREATE OR REPLACE FUNCTION unapi.acn ( 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 ) RETURNS XML AS $F$
11336 CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
11337 'tag:open-ils.org:U2@acn/' || acn.id AS id,
11338 acn.id AS vol_id, o.shortname AS lib,
11339 o.opac_visible AS opac_visible,
11340 deleted, label, label_sortkey, label_class, record
11342 unapi.aou( owning_lib, $2, 'owning_lib', evergreen.array_remove_item_by_value($4,'acn'), $5, $6, $7, $8),
11344 WHEN ('acp' = ANY ($4)) THEN
11345 CASE WHEN $6 IS NOT NULL THEN
11346 XMLELEMENT( name copies,
11347 (SELECT XMLAGG(acp ORDER BY rank_avail) FROM (
11348 SELECT unapi.acp( cp.id, 'xml', 'copy', evergreen.array_remove_item_by_value($4,'acn'), $5, $6, $7, $8, FALSE),
11349 evergreen.rank_cp_status(cp.status) AS rank_avail
11351 JOIN actor.org_unit_descendants( (SELECT id FROM actor.org_unit WHERE shortname = $5), $6) aoud ON (cp.circ_lib = aoud.id)
11352 WHERE cp.call_number = acn.id
11353 AND cp.deleted IS FALSE
11354 ORDER BY rank_avail, COALESCE(cp.copy_number,0), cp.barcode
11355 LIMIT ($7 -> 'acp')::INT
11356 OFFSET ($8 -> 'acp')::INT
11360 XMLELEMENT( name copies,
11361 (SELECT XMLAGG(acp ORDER BY rank_avail) FROM (
11362 SELECT unapi.acp( cp.id, 'xml', 'copy', evergreen.array_remove_item_by_value($4,'acn'), $5, $6, $7, $8, FALSE),
11363 evergreen.rank_cp_status(cp.status) AS rank_avail
11365 JOIN actor.org_unit_descendants( (SELECT id FROM actor.org_unit WHERE shortname = $5) ) aoud ON (cp.circ_lib = aoud.id)
11366 WHERE cp.call_number = acn.id
11367 AND cp.deleted IS FALSE
11368 ORDER BY rank_avail, COALESCE(cp.copy_number,0), cp.barcode
11369 LIMIT ($7 -> 'acp')::INT
11370 OFFSET ($8 -> 'acp')::INT
11378 (SELECT XMLAGG(auri) FROM (SELECT unapi.auri(uri,'xml','uri', evergreen.array_remove_item_by_value($4,'acn'), $5, $6, $7, $8, FALSE) FROM asset.uri_call_number_map WHERE call_number = acn.id)x)
11380 unapi.acnp( acn.prefix, 'marcxml', 'prefix', evergreen.array_remove_item_by_value($4,'acn'), $5, $6, $7, $8, FALSE),
11381 unapi.acns( acn.suffix, 'marcxml', 'suffix', evergreen.array_remove_item_by_value($4,'acn'), $5, $6, $7, $8, FALSE),
11382 CASE WHEN ('bre' = ANY ($4)) THEN unapi.bre( acn.record, 'marcxml', 'record', evergreen.array_remove_item_by_value($4,'acn'), $5, $6, $7, $8, FALSE) ELSE NULL END
11384 FROM asset.call_number acn
11385 JOIN actor.org_unit o ON (o.id = acn.owning_lib)
11387 AND acn.deleted IS FALSE
11388 GROUP BY acn.id, o.shortname, o.opac_visible, deleted, label, label_sortkey, label_class, owning_lib, record, acn.prefix, acn.suffix;
11389 $F$ LANGUAGE SQL STABLE;
11391 CREATE OR REPLACE FUNCTION unapi.acnp ( 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 ) RETURNS XML AS $F$
11393 name call_number_prefix,
11395 CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
11398 'tag:open-ils.org:U2@aou/' || owning_lib AS owning_lib,
11402 FROM asset.call_number_prefix
11404 $F$ LANGUAGE SQL STABLE;
11406 CREATE OR REPLACE FUNCTION unapi.acns ( 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 ) RETURNS XML AS $F$
11408 name call_number_suffix,
11410 CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
11413 'tag:open-ils.org:U2@aou/' || owning_lib AS owning_lib,
11417 FROM asset.call_number_suffix
11419 $F$ LANGUAGE SQL STABLE;
11421 CREATE OR REPLACE FUNCTION unapi.auri ( 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 ) RETURNS XML AS $F$
11425 CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
11426 'tag:open-ils.org:U2@auri/' || uri.id AS id,
11432 WHEN ('acn' = ANY ($4)) THEN
11433 XMLELEMENT( name copies,
11434 (SELECT XMLAGG(acn) FROM (SELECT unapi.acn( call_number, 'xml', 'copy', evergreen.array_remove_item_by_value($4,'auri'), $5, $6, $7, $8, FALSE) FROM asset.uri_call_number_map WHERE uri = uri.id)x)
11441 GROUP BY uri.id, use_restriction, href, label;
11442 $F$ LANGUAGE SQL STABLE;
11444 CREATE OR REPLACE FUNCTION unapi.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 ) RETURNS XML AS $F$
11448 CASE WHEN $9 THEN 'http://open-ils.org/spec/indexing/v1' ELSE NULL END AS xmlns,
11449 'tag:open-ils.org:U2@mra/' || mra.id AS id,
11450 'tag:open-ils.org:U2@bre/' || mra.id AS record
11452 (SELECT XMLAGG(foo.y)
11453 FROM (SELECT XMLELEMENT(
11457 cvm.value AS "coded-value",
11463 FROM EACH(mra.attrs) AS x
11464 JOIN config.record_attr_definition rad ON (x.key = rad.name)
11465 LEFT JOIN config.coded_value_map cvm ON (cvm.ctype = x.key AND code = x.value)
11469 FROM metabib.record_attr mra
11471 $F$ LANGUAGE SQL STABLE;
11473 CREATE OR REPLACE FUNCTION unapi.circ (obj_id BIGINT, format TEXT, ename TEXT, includes TEXT[], org TEXT DEFAULT '-', depth INT DEFAULT NULL, slimit HSTORE DEFAULT NULL, soffset HSTORE DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$
11477 CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
11478 'tag:open-ils.org:U2@circ/' || id AS id,
11482 CASE WHEN ('aou' = ANY ($4)) THEN unapi.aou( circ_lib, $2, 'circ_lib', evergreen.array_remove_item_by_value($4,'circ'), $5, $6, $7, $8, FALSE) ELSE NULL END,
11483 CASE WHEN ('acp' = ANY ($4)) THEN unapi.acp( circ_lib, $2, 'target_copy', evergreen.array_remove_item_by_value($4,'circ'), $5, $6, $7, $8, FALSE) ELSE NULL END
11485 FROM action.circulation
11487 $F$ LANGUAGE SQL STABLE;
11491 -- Some test queries
11493 SELECT unapi.memoize( 'bre', 1,'mods32','','{holdings_xml,acp}'::TEXT[], 'SYS1');
11494 SELECT unapi.memoize( 'bre', 1,'marcxml','','{holdings_xml,acp}'::TEXT[], 'SYS1');
11495 SELECT unapi.memoize( 'bre', 1,'holdings_xml','','{holdings_xml,acp}'::TEXT[], 'SYS1');
11497 SELECT unapi.biblio_record_entry_feed('{1}'::BIGINT[],'mods32','{holdings_xml,acp}'::TEXT[],'SYS1',NULL,'acn=>1',NULL, NULL,NULL,NULL,NULL,'http://c64/opac/extras/unapi', '<totalResults xmlns="http://a9.com/-/spec/opensearch/1.1/">2</totalResults><startIndex xmlns="http://a9.com/-/spec/opensearch/1.1/">1</startIndex><itemsPerPage xmlns="http://a9.com/-/spec/opensearch/1.1/">10</itemsPerPage>');
11499 SELECT unapi.biblio_record_entry_feed('{7209,7394}'::BIGINT[],'marcxml','{}'::TEXT[],'SYS1',NULL,'acn=>1',NULL, NULL,NULL,NULL,NULL,'http://fulfillment2.esilibrary.com/opac/extras/unapi', '<totalResults xmlns="http://a9.com/-/spec/opensearch/1.1/">2</totalResults><startIndex xmlns="http://a9.com/-/spec/opensearch/1.1/">1</startIndex><itemsPerPage xmlns="http://a9.com/-/spec/opensearch/1.1/">10</itemsPerPage>');
11500 EXPLAIN ANALYZE SELECT unapi.biblio_record_entry_feed('{7209,7394}'::BIGINT[],'marcxml','{}'::TEXT[],'SYS1',NULL,'acn=>1',NULL, NULL,NULL,NULL,NULL,'http://fulfillment2.esilibrary.com/opac/extras/unapi', '<totalResults xmlns="http://a9.com/-/spec/opensearch/1.1/">2</totalResults><startIndex xmlns="http://a9.com/-/spec/opensearch/1.1/">1</startIndex><itemsPerPage xmlns="http://a9.com/-/spec/opensearch/1.1/">10</itemsPerPage>');
11501 EXPLAIN ANALYZE SELECT unapi.biblio_record_entry_feed('{7209,7394}'::BIGINT[],'marcxml','{holdings_xml}'::TEXT[],'SYS1',NULL,'acn=>1',NULL, NULL,NULL,NULL,NULL,'http://fulfillment2.esilibrary.com/opac/extras/unapi', '<totalResults xmlns="http://a9.com/-/spec/opensearch/1.1/">2</totalResults><startIndex xmlns="http://a9.com/-/spec/opensearch/1.1/">1</startIndex><itemsPerPage xmlns="http://a9.com/-/spec/opensearch/1.1/">10</itemsPerPage>');
11502 EXPLAIN ANALYZE SELECT unapi.biblio_record_entry_feed('{7209,7394}'::BIGINT[],'mods32','{holdings_xml}'::TEXT[],'SYS1',NULL,'acn=>1',NULL, NULL,NULL,NULL,NULL,'http://fulfillment2.esilibrary.com/opac/extras/unapi', '<totalResults xmlns="http://a9.com/-/spec/opensearch/1.1/">2</totalResults><startIndex xmlns="http://a9.com/-/spec/opensearch/1.1/">1</startIndex><itemsPerPage xmlns="http://a9.com/-/spec/opensearch/1.1/">10</itemsPerPage>');
11504 SELECT unapi.biblio_record_entry_feed('{216}'::BIGINT[],'marcxml','{}'::TEXT[], 'BR1');
11505 EXPLAIN ANALYZE SELECT unapi.bre(216,'marcxml','record','{holdings_xml,bre.unapi}'::TEXT[], 'BR1');
11506 EXPLAIN ANALYZE SELECT unapi.bre(216,'holdings_xml','record','{}'::TEXT[], 'BR1');
11507 EXPLAIN ANALYZE SELECT unapi.holdings_xml(216,4,'BR1',2,'{bre}'::TEXT[]);
11508 EXPLAIN ANALYZE SELECT unapi.bre(216,'mods32','record','{}'::TEXT[], 'BR1');
11510 -- Limit to 5 call numbers, 5 copies, with a preferred library of 4 (BR1), in SYS2 at a depth of 0
11511 EXPLAIN ANALYZE SELECT unapi.bre(36,'marcxml','record','{holdings_xml,mra,acp,acnp,acns,bmp}','SYS2',0,'acn=>5,acp=>5',NULL,TRUE,4);
11516 SELECT evergreen.upgrade_deps_block_check('0692', :eg_version);
11518 INSERT INTO config.org_unit_setting_type
11519 (name, label, description, grp, datatype)
11521 'circ.fines.charge_when_closed',
11523 'circ.fines.charge_when_closed',
11524 'Charge fines on overdue circulations when closed',
11529 'circ.fines.charge_when_closed',
11530 'Normally, fines are not charged when a library is closed. When set to True, fines will be charged during scheduled closings and normal weekly closed days.',
11538 SELECT evergreen.upgrade_deps_block_check('0694', :eg_version);
11540 INSERT into config.org_unit_setting_type
11541 ( name, grp, label, description, datatype, fm_class ) VALUES
11543 ( 'ui.patron.edit.au.prefix.require', 'gui',
11544 oils_i18n_gettext('ui.patron.edit.au.prefix.require',
11545 'Require prefix field on patron registration',
11547 oils_i18n_gettext('ui.patron.edit.au.prefix.require',
11548 'The prefix field will be required on the patron registration screen.',
11549 'coust', 'description'),
11552 ,( 'ui.patron.edit.au.prefix.show', 'gui',
11553 oils_i18n_gettext('ui.patron.edit.au.prefix.show',
11554 'Show prefix field on patron registration',
11556 oils_i18n_gettext('ui.patron.edit.au.prefix.show',
11557 'The prefix field will be shown on the patron registration screen. Showing a field makes it appear with required fields even when not required. If the field is required this setting is ignored.',
11558 'coust', 'description'),
11561 ,( 'ui.patron.edit.au.prefix.suggest', 'gui',
11562 oils_i18n_gettext('ui.patron.edit.au.prefix.suggest',
11563 'Suggest prefix field on patron registration',
11565 oils_i18n_gettext('ui.patron.edit.au.prefix.suggest',
11566 'The prefix field will be suggested on the patron registration screen. Suggesting a field makes it appear when suggested fields are shown. If the field is shown or required this setting is ignored.',
11567 'coust', 'description'),
11572 -- Evergreen DB patch 0695.schema.custom_toolbars.sql
11574 -- FIXME: insert description of change, if needed
11577 -- check whether patch can be applied
11578 SELECT evergreen.upgrade_deps_block_check('0695', :eg_version);
11580 CREATE TABLE actor.toolbar (
11581 id BIGSERIAL PRIMARY KEY,
11582 ws INT REFERENCES actor.workstation (id) ON DELETE CASCADE,
11583 org INT REFERENCES actor.org_unit (id) ON DELETE CASCADE,
11584 usr INT REFERENCES actor.usr (id) ON DELETE CASCADE,
11585 label TEXT NOT NULL,
11586 layout TEXT NOT NULL,
11587 CONSTRAINT only_one_type CHECK (
11588 (ws IS NOT NULL AND COALESCE(org,usr) IS NULL) OR
11589 (org IS NOT NULL AND COALESCE(ws,usr) IS NULL) OR
11590 (usr IS NOT NULL AND COALESCE(org,ws) IS NULL)
11592 CONSTRAINT layout_must_be_json CHECK ( is_json(layout) )
11594 CREATE UNIQUE INDEX label_once_per_ws ON actor.toolbar (ws, label) WHERE ws IS NOT NULL;
11595 CREATE UNIQUE INDEX label_once_per_org ON actor.toolbar (org, label) WHERE org IS NOT NULL;
11596 CREATE UNIQUE INDEX label_once_per_usr ON actor.toolbar (usr, label) WHERE usr IS NOT NULL;
11598 -- this one unrelated to toolbars but is a gap in the upgrade scripts
11599 INSERT INTO permission.perm_list ( id, code, description )
11602 'IMPORT_AUTHORITY_MARC',
11605 'Allows a user to create new authority records',
11611 FROM permission.perm_list
11616 INSERT INTO permission.perm_list ( id, code, description ) VALUES (
11621 'Allows a user to create, edit, and delete custom toolbars',
11627 -- Don't want to assume stock perm groups in an upgrade script, but here for ease of testing
11628 -- INSERT INTO permission.grp_perm_map (grp, perm, depth, grantable) SELECT pgt.id, perm.id, aout.depth, FALSE FROM permission.grp_tree pgt, permission.perm_list perm, actor.org_unit_type aout WHERE pgt.name = 'Staff' AND aout.name = 'Branch' AND perm.code = 'ADMIN_TOOLBAR';
11630 INSERT INTO actor.toolbar(org,label,layout) VALUES
11631 ( 1, 'circ', '["circ_checkout","circ_checkin","toolbarseparator.1","search_opac","copy_status","toolbarseparator.2","patron_search","patron_register","toolbarspacer.3","hotkeys_toggle"]' ),
11632 ( 1, 'cat', '["circ_checkin","toolbarseparator.1","search_opac","copy_status","toolbarseparator.2","create_marc","authority_manage","retrieve_last_record","toolbarspacer.3","hotkeys_toggle"]' );
11634 -- delete from permission.grp_perm_map where perm in (select id from permission.perm_list where code ~ 'TOOLBAR'); delete from permission.perm_list where code ~ 'TOOLBAR'; drop table actor.toolbar ;
11636 -- Evergreen DB patch 0696.no_plperl.sql
11638 -- FIXME: insert description of change, if needed
11641 -- check whether patch can be applied
11642 SELECT evergreen.upgrade_deps_block_check('0696', :eg_version);
11644 -- Re-create these as plperlu instead of plperl
11645 CREATE OR REPLACE FUNCTION auditor.set_audit_info(INT, INT) RETURNS VOID AS $$
11646 $_SHARED{"eg_audit_user"} = $_[0];
11647 $_SHARED{"eg_audit_ws"} = $_[1];
11648 $$ LANGUAGE plperlu;
11650 CREATE OR REPLACE FUNCTION auditor.get_audit_info() RETURNS TABLE (eg_user INT, eg_ws INT) AS $$
11651 return [{eg_user => $_SHARED{"eg_audit_user"}, eg_ws => $_SHARED{"eg_audit_ws"}}];
11652 $$ LANGUAGE plperlu;
11654 CREATE OR REPLACE FUNCTION auditor.clear_audit_info() RETURNS VOID AS $$
11655 delete($_SHARED{"eg_audit_user"});
11656 delete($_SHARED{"eg_audit_ws"});
11657 $$ LANGUAGE plperlu;
11659 -- Evergreen DB patch 0697.data.place_currently_unfillable_hold.sql
11661 -- FIXME: insert description of change, if needed
11664 -- check whether patch can be applied
11665 SELECT evergreen.upgrade_deps_block_check('0697', :eg_version);
11667 -- FIXME: add/check SQL statements to perform the upgrade
11668 INSERT INTO permission.perm_list ( id, code, description ) VALUES
11669 ( 524, 'PLACE_UNFILLABLE_HOLD', oils_i18n_gettext( 524,
11670 'Allows a user to place a hold that cannot currently be filled.', 'ppl', 'description' ));
11672 -- Evergreen DB patch 0698.hold_default_pickup.sql
11674 -- FIXME: insert description of change, if needed
11677 -- check whether patch can be applied
11678 SELECT evergreen.upgrade_deps_block_check('0698', :eg_version);
11680 INSERT INTO config.usr_setting_type (name,opac_visible,label,description,datatype)
11681 VALUES ('opac.default_pickup_location', TRUE, 'Default Hold Pickup Location', 'Default location for holds pickup', 'integer');
11683 SELECT evergreen.upgrade_deps_block_check('0699', :eg_version);
11685 INSERT INTO config.org_unit_setting_type ( name, label, description, datatype, grp )
11687 'ui.hide_copy_editor_fields',
11689 'ui.hide_copy_editor_fields',
11690 'GUI: Hide these fields within the Item Attribute Editor',
11695 'ui.hide_copy_editor_fields',
11696 'This setting may be best maintained with the dedicated configuration'
11697 || ' interface within the Item Attribute Editor. However, here it'
11698 || ' shows up as comma separated list of field identifiers to hide.',
11707 SELECT evergreen.upgrade_deps_block_check('0700', :eg_version);
11708 SELECT evergreen.upgrade_deps_block_check('0706', :eg_version);
11709 SELECT evergreen.upgrade_deps_block_check('0710', :eg_version);
11712 CREATE OR REPLACE FUNCTION evergreen.could_be_serial_holding_code(TEXT) RETURNS BOOL AS $$
11717 my $holding_code = (new JSON::XS)->decode(shift);
11718 new MARC::Field('999', @$holding_code);
11721 $$ LANGUAGE PLPERLU;
11723 -- This throws away data, but only data that causes breakage anyway.
11724 UPDATE serial.issuance SET holding_code = NULL WHERE NOT could_be_serial_holding_code(holding_code);
11726 -- If we don't do this, we have unprocessed triggers and we can't alter the table
11727 SET CONSTRAINTS serial.issuance_caption_and_pattern_fkey IMMEDIATE;
11729 ALTER TABLE serial.issuance
11730 DROP CONSTRAINT IF EXISTS issuance_holding_code_check;
11732 ALTER TABLE serial.issuance ADD CHECK (holding_code IS NULL OR could_be_serial_holding_code(holding_code));
11734 INSERT INTO config.internal_flag (name, value, enabled) VALUES (
11735 'serial.rematerialize_on_same_holding_code', NULL, FALSE
11738 INSERT INTO config.org_unit_setting_type (
11739 name, label, grp, description, datatype
11741 'serial.default_display_grouping',
11742 'Default display grouping for serials distributions presented in the OPAC.',
11744 'Default display grouping for serials distributions presented in the OPAC. This can be "enum" or "chron".',
11748 ALTER TABLE serial.distribution
11749 ADD COLUMN display_grouping TEXT NOT NULL DEFAULT 'chron'
11750 CHECK (display_grouping IN ('enum', 'chron'));
11752 -- why didn't we just make one summary table in the first place?
11753 CREATE VIEW serial.any_summary AS
11755 'basic' AS summary_type, id, distribution,
11756 generated_coverage, textual_holdings, show_generated
11757 FROM serial.basic_summary
11760 'index' AS summary_type, id, distribution,
11761 generated_coverage, textual_holdings, show_generated
11762 FROM serial.index_summary
11765 'supplement' AS summary_type, id, distribution,
11766 generated_coverage, textual_holdings, show_generated
11767 FROM serial.supplement_summary ;
11770 -- Given the IDs of two rows in actor.org_unit, *the second being an ancestor
11771 -- of the first*, return in array form the path from the ancestor to the
11772 -- descendant, with each point in the path being an org_unit ID. This is
11773 -- useful for sorting org_units by their position in a depth-first (display
11774 -- order) representation of the tree.
11776 -- This breaks with the precedent set by actor.org_unit_full_path() and others,
11777 -- and gets the parameters "backwards," but otherwise this function would
11778 -- not be very usable within json_query.
11779 CREATE OR REPLACE FUNCTION actor.org_unit_simple_path(INT, INT)
11780 RETURNS INT[] AS $$
11781 WITH RECURSIVE descendant_depth(id, path) AS (
11784 FROM actor.org_unit aou
11785 JOIN actor.org_unit_type aout ON (aout.id = aou.ou_type)
11789 dd.path || ARRAY[aou.id]
11790 FROM actor.org_unit aou
11791 JOIN actor.org_unit_type aout ON (aout.id = aou.ou_type)
11792 JOIN descendant_depth dd ON (dd.id = aou.parent_ou)
11794 FROM actor.org_unit aou
11795 JOIN descendant_depth dd USING (id)
11796 WHERE aou.id = $1 ORDER BY dd.path;
11797 $$ LANGUAGE SQL STABLE;
11799 CREATE TABLE serial.materialized_holding_code (
11800 id BIGSERIAL PRIMARY KEY,
11801 issuance INTEGER NOT NULL REFERENCES serial.issuance (id) ON DELETE CASCADE,
11806 CREATE OR REPLACE FUNCTION serial.materialize_holding_code() RETURNS TRIGGER
11813 if (not defined $_TD->{new}{holding_code}) {
11814 elog(WARNING, 'NULL in "holding_code" column of serial.issuance allowed for now, but may not be useful');
11818 # Do nothing if holding_code has not changed...
11820 if ($_TD->{new}{holding_code} eq $_TD->{old}{holding_code}) {
11821 # ... unless the following internal flag is set.
11823 my $flag_rv = spi_exec_query(q{
11824 SELECT * FROM config.internal_flag
11825 WHERE name = 'serial.rematerialize_on_same_holding_code' AND enabled
11827 return unless $flag_rv->{processed};
11831 my $holding_code = (new JSON::XS)->decode($_TD->{new}{holding_code});
11833 my $field = new MARC::Field('999', @$holding_code); # tag doesnt matter
11835 my $dstmt = spi_prepare(
11836 'DELETE FROM serial.materialized_holding_code WHERE issuance = $1',
11839 spi_exec_prepared($dstmt, $_TD->{new}{id});
11841 my $istmt = spi_prepare(
11843 INSERT INTO serial.materialized_holding_code (
11844 issuance, subfield, value
11845 ) VALUES ($1, $2, $3)
11846 }, qw{INT CHAR TEXT}
11849 foreach ($field->subfields) {
11860 $func$ LANGUAGE 'plperlu';
11862 CREATE INDEX assist_holdings_display
11863 ON serial.materialized_holding_code (issuance, subfield);
11865 CREATE TRIGGER materialize_holding_code
11866 AFTER INSERT OR UPDATE ON serial.issuance
11867 FOR EACH ROW EXECUTE PROCEDURE serial.materialize_holding_code() ;
11869 -- starting here, we materialize all existing holding codes.
11871 UPDATE config.internal_flag
11873 WHERE name = 'serial.rematerialize_on_same_holding_code';
11875 UPDATE serial.issuance SET holding_code = holding_code;
11877 UPDATE config.internal_flag
11878 SET enabled = FALSE
11879 WHERE name = 'serial.rematerialize_on_same_holding_code';
11881 -- finish holding code materialization process
11883 -- fix up missing holding_code fields from serial.issuance
11884 UPDATE serial.issuance siss
11885 SET holding_type = scap.type
11886 FROM serial.caption_and_pattern scap
11887 WHERE scap.id = siss.caption_and_pattern AND siss.holding_type IS NULL;
11890 -- Evergreen DB patch 0701.schema.patron_stat_category_enhancements.sql
11892 -- Enables users to set patron statistical categories as required,
11893 -- whether or not users can input free text for the category value.
11894 -- Enables administrators to set an entry as the default for any
11895 -- given patron statistical category and org unit.
11898 -- check whether patch can be applied
11899 SELECT evergreen.upgrade_deps_block_check('0701', :eg_version);
11903 CREATE TABLE actor.stat_cat_entry_default (
11904 id SERIAL PRIMARY KEY,
11905 stat_cat_entry INT NOT NULL REFERENCES actor.stat_cat_entry (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
11906 stat_cat INT NOT NULL REFERENCES actor.stat_cat (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
11907 owner INT NOT NULL REFERENCES actor.org_unit (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
11908 CONSTRAINT sced_once_per_owner UNIQUE (stat_cat,owner)
11911 COMMENT ON TABLE actor.stat_cat_entry_default IS $$
11912 User Statistical Category Default Entry
11914 A library may choose one of the stat_cat entries to be the
11918 -- Add columns to existing tables
11920 -- Patron stat cat required column
11921 ALTER TABLE actor.stat_cat
11922 ADD COLUMN required BOOL NOT NULL DEFAULT FALSE;
11924 -- Patron stat cat allow_freetext column
11925 ALTER TABLE actor.stat_cat
11926 ADD COLUMN allow_freetext BOOL NOT NULL DEFAULT TRUE;
11930 INSERT INTO permission.perm_list ( id, code, description ) VALUES
11931 ( 525, 'CREATE_PATRON_STAT_CAT_ENTRY_DEFAULT', oils_i18n_gettext( 525,
11932 'User may set a default entry in a patron statistical category', 'ppl', 'description' )),
11933 ( 526, 'UPDATE_PATRON_STAT_CAT_ENTRY_DEFAULT', oils_i18n_gettext( 526,
11934 'User may reset a default entry in a patron statistical category', 'ppl', 'description' )),
11935 ( 527, 'DELETE_PATRON_STAT_CAT_ENTRY_DEFAULT', oils_i18n_gettext( 527,
11936 'User may unset a default entry in a patron statistical category', 'ppl', 'description' ));
11938 INSERT INTO permission.grp_perm_map (grp, perm, depth, grantable)
11940 pgt.id, perm.id, aout.depth, TRUE
11942 permission.grp_tree pgt,
11943 permission.perm_list perm,
11944 actor.org_unit_type aout
11946 pgt.name = 'Circulation Administrator' AND
11947 aout.name = 'System' AND
11948 perm.code IN ('CREATE_PATRON_STAT_CAT_ENTRY_DEFAULT', 'DELETE_PATRON_STAT_CAT_ENTRY_DEFAULT');
11951 SELECT evergreen.upgrade_deps_block_check('0702', :eg_version);
11953 INSERT INTO config.global_flag (name, enabled, label)
11955 'opac.org_unit.non_inherited_visibility',
11958 'opac.org_unit.non_inherited_visibility',
11959 'Org Units Do Not Inherit Visibility',
11965 CREATE TYPE actor.org_unit_custom_tree_purpose AS ENUM ('opac');
11967 CREATE TABLE actor.org_unit_custom_tree (
11968 id SERIAL PRIMARY KEY,
11969 active BOOLEAN DEFAULT FALSE,
11970 purpose actor.org_unit_custom_tree_purpose NOT NULL DEFAULT 'opac' UNIQUE
11973 CREATE TABLE actor.org_unit_custom_tree_node (
11974 id SERIAL PRIMARY KEY,
11975 tree INTEGER REFERENCES actor.org_unit_custom_tree (id) DEFERRABLE INITIALLY DEFERRED,
11976 org_unit INTEGER NOT NULL REFERENCES actor.org_unit (id) DEFERRABLE INITIALLY DEFERRED,
11977 parent_node INTEGER REFERENCES actor.org_unit_custom_tree_node (id) DEFERRABLE INITIALLY DEFERRED,
11978 sibling_order INTEGER NOT NULL DEFAULT 0,
11979 CONSTRAINT aouctn_once_per_org UNIQUE (tree, org_unit)
11985 DELETE FROM config.global_flag WHERE name = 'opac.org_unit.non_inheritied_visibility';
11986 DROP TABLE actor.org_unit_custom_tree_node;
11987 DROP TABLE actor.org_unit_custom_tree;
11988 DROP TYPE actor.org_unit_custom_tree_purpose;
11992 -- Evergreen DB patch 0704.schema.query_parser_fts.sql
11994 -- Add pref_ou query filter for preferred library searching
11997 -- check whether patch can be applied
11998 SELECT evergreen.upgrade_deps_block_check('0704', :eg_version);
12000 -- Create the new 11-parameter function, featuring param_pref_ou
12001 CREATE OR REPLACE FUNCTION search.query_parser_fts (
12003 param_search_ou INT,
12006 param_statuses INT[],
12007 param_locations INT[],
12013 param_pref_ou INT DEFAULT NULL
12014 ) RETURNS SETOF search.search_result AS $func$
12017 current_res search.search_result%ROWTYPE;
12018 search_org_list INT[];
12019 luri_org_list INT[];
12020 tmp_int_list INT[];
12027 core_result RECORD;
12028 core_cursor REFCURSOR;
12029 core_rel_query TEXT;
12031 total_count INT := 0;
12032 check_count INT := 0;
12033 deleted_count INT := 0;
12034 visible_count INT := 0;
12035 excluded_count INT := 0;
12039 check_limit := COALESCE( param_check, 1000 );
12040 core_limit := COALESCE( param_limit, 25000 );
12041 core_offset := COALESCE( param_offset, 0 );
12043 -- core_skip_chk := COALESCE( param_skip_chk, 1 );
12045 IF param_search_ou > 0 THEN
12046 IF param_depth IS NOT NULL THEN
12047 SELECT array_accum(distinct id) INTO search_org_list FROM actor.org_unit_descendants( param_search_ou, param_depth );
12049 SELECT array_accum(distinct id) INTO search_org_list FROM actor.org_unit_descendants( param_search_ou );
12052 SELECT array_accum(distinct id) INTO luri_org_list FROM actor.org_unit_ancestors( param_search_ou );
12054 ELSIF param_search_ou < 0 THEN
12055 SELECT array_accum(distinct org_unit) INTO search_org_list FROM actor.org_lasso_map WHERE lasso = -param_search_ou;
12057 FOR tmp_int IN SELECT * FROM UNNEST(search_org_list) LOOP
12058 SELECT array_accum(distinct id) INTO tmp_int_list FROM actor.org_unit_ancestors( tmp_int );
12059 luri_org_list := luri_org_list || tmp_int_list;
12062 SELECT array_accum(DISTINCT x.id) INTO luri_org_list FROM UNNEST(luri_org_list) x(id);
12064 ELSIF param_search_ou = 0 THEN
12065 -- reserved for user lassos (ou_buckets/type='lasso') with ID passed in depth ... hack? sure.
12068 IF param_pref_ou IS NOT NULL THEN
12069 SELECT array_accum(distinct id) INTO tmp_int_list FROM actor.org_unit_ancestors(param_pref_ou);
12070 luri_org_list := luri_org_list || tmp_int_list;
12073 OPEN core_cursor FOR EXECUTE param_query;
12077 FETCH core_cursor INTO core_result;
12078 EXIT WHEN NOT FOUND;
12079 EXIT WHEN total_count >= core_limit;
12081 total_count := total_count + 1;
12083 CONTINUE WHEN total_count NOT BETWEEN core_offset + 1 AND check_limit + core_offset;
12085 check_count := check_count + 1;
12087 PERFORM 1 FROM biblio.record_entry b WHERE NOT b.deleted AND b.id IN ( SELECT * FROM unnest( core_result.records ) );
12089 -- RAISE NOTICE ' % were all deleted ... ', core_result.records;
12090 deleted_count := deleted_count + 1;
12095 FROM biblio.record_entry b
12096 JOIN config.bib_source s ON (b.source = s.id)
12097 WHERE s.transcendant
12098 AND b.id IN ( SELECT * FROM unnest( core_result.records ) );
12101 -- RAISE NOTICE ' % were all transcendant ... ', core_result.records;
12102 visible_count := visible_count + 1;
12104 current_res.id = core_result.id;
12105 current_res.rel = core_result.rel;
12109 SELECT COUNT(DISTINCT s.source) INTO tmp_int FROM metabib.metarecord_source_map s WHERE s.metarecord = core_result.id;
12112 IF tmp_int = 1 THEN
12113 current_res.record = core_result.records[1];
12115 current_res.record = NULL;
12118 RETURN NEXT current_res;
12124 FROM asset.call_number cn
12125 JOIN asset.uri_call_number_map map ON (map.call_number = cn.id)
12126 JOIN asset.uri uri ON (map.uri = uri.id)
12127 WHERE NOT cn.deleted
12128 AND cn.label = '##URI##'
12130 AND ( param_locations IS NULL OR array_upper(param_locations, 1) IS NULL )
12131 AND cn.record IN ( SELECT * FROM unnest( core_result.records ) )
12132 AND cn.owning_lib IN ( SELECT * FROM unnest( luri_org_list ) )
12136 -- RAISE NOTICE ' % have at least one URI ... ', core_result.records;
12137 visible_count := visible_count + 1;
12139 current_res.id = core_result.id;
12140 current_res.rel = core_result.rel;
12144 SELECT COUNT(DISTINCT s.source) INTO tmp_int FROM metabib.metarecord_source_map s WHERE s.metarecord = core_result.id;
12147 IF tmp_int = 1 THEN
12148 current_res.record = core_result.records[1];
12150 current_res.record = NULL;
12153 RETURN NEXT current_res;
12158 IF param_statuses IS NOT NULL AND array_upper(param_statuses, 1) > 0 THEN
12161 FROM asset.call_number cn
12162 JOIN asset.copy cp ON (cp.call_number = cn.id)
12163 WHERE NOT cn.deleted
12165 AND cp.status IN ( SELECT * FROM unnest( param_statuses ) )
12166 AND cn.record IN ( SELECT * FROM unnest( core_result.records ) )
12167 AND cp.circ_lib IN ( SELECT * FROM unnest( search_org_list ) )
12172 FROM biblio.peer_bib_copy_map pr
12173 JOIN asset.copy cp ON (cp.id = pr.target_copy)
12174 WHERE NOT cp.deleted
12175 AND cp.status IN ( SELECT * FROM unnest( param_statuses ) )
12176 AND pr.peer_record IN ( SELECT * FROM unnest( core_result.records ) )
12177 AND cp.circ_lib IN ( SELECT * FROM unnest( search_org_list ) )
12181 -- RAISE NOTICE ' % and multi-home linked records were all status-excluded ... ', core_result.records;
12182 excluded_count := excluded_count + 1;
12189 IF param_locations IS NOT NULL AND array_upper(param_locations, 1) > 0 THEN
12192 FROM asset.call_number cn
12193 JOIN asset.copy cp ON (cp.call_number = cn.id)
12194 WHERE NOT cn.deleted
12196 AND cp.location IN ( SELECT * FROM unnest( param_locations ) )
12197 AND cn.record IN ( SELECT * FROM unnest( core_result.records ) )
12198 AND cp.circ_lib IN ( SELECT * FROM unnest( search_org_list ) )
12203 FROM biblio.peer_bib_copy_map pr
12204 JOIN asset.copy cp ON (cp.id = pr.target_copy)
12205 WHERE NOT cp.deleted
12206 AND cp.location IN ( SELECT * FROM unnest( param_locations ) )
12207 AND pr.peer_record IN ( SELECT * FROM unnest( core_result.records ) )
12208 AND cp.circ_lib IN ( SELECT * FROM unnest( search_org_list ) )
12212 -- RAISE NOTICE ' % and multi-home linked records were all copy_location-excluded ... ', core_result.records;
12213 excluded_count := excluded_count + 1;
12220 IF staff IS NULL OR NOT staff THEN
12223 FROM asset.opac_visible_copies
12224 WHERE circ_lib IN ( SELECT * FROM unnest( search_org_list ) )
12225 AND record IN ( SELECT * FROM unnest( core_result.records ) )
12230 FROM biblio.peer_bib_copy_map pr
12231 JOIN asset.opac_visible_copies cp ON (cp.copy_id = pr.target_copy)
12232 WHERE cp.circ_lib IN ( SELECT * FROM unnest( search_org_list ) )
12233 AND pr.peer_record IN ( SELECT * FROM unnest( core_result.records ) )
12238 -- RAISE NOTICE ' % and multi-home linked records were all visibility-excluded ... ', core_result.records;
12239 excluded_count := excluded_count + 1;
12247 FROM asset.call_number cn
12248 JOIN asset.copy cp ON (cp.call_number = cn.id)
12249 WHERE NOT cn.deleted
12251 AND cp.circ_lib IN ( SELECT * FROM unnest( search_org_list ) )
12252 AND cn.record IN ( SELECT * FROM unnest( core_result.records ) )
12258 FROM biblio.peer_bib_copy_map pr
12259 JOIN asset.copy cp ON (cp.id = pr.target_copy)
12260 WHERE NOT cp.deleted
12261 AND cp.circ_lib IN ( SELECT * FROM unnest( search_org_list ) )
12262 AND pr.peer_record IN ( SELECT * FROM unnest( core_result.records ) )
12268 FROM asset.call_number cn
12269 JOIN asset.copy cp ON (cp.call_number = cn.id)
12270 WHERE cn.record IN ( SELECT * FROM unnest( core_result.records ) )
12275 -- RAISE NOTICE ' % and multi-home linked records were all visibility-excluded ... ', core_result.records;
12276 excluded_count := excluded_count + 1;
12285 visible_count := visible_count + 1;
12287 current_res.id = core_result.id;
12288 current_res.rel = core_result.rel;
12292 SELECT COUNT(DISTINCT s.source) INTO tmp_int FROM metabib.metarecord_source_map s WHERE s.metarecord = core_result.id;
12295 IF tmp_int = 1 THEN
12296 current_res.record = core_result.records[1];
12298 current_res.record = NULL;
12301 RETURN NEXT current_res;
12303 IF visible_count % 1000 = 0 THEN
12304 -- RAISE NOTICE ' % visible so far ... ', visible_count;
12309 current_res.id = NULL;
12310 current_res.rel = NULL;
12311 current_res.record = NULL;
12312 current_res.total = total_count;
12313 current_res.checked = check_count;
12314 current_res.deleted = deleted_count;
12315 current_res.visible = visible_count;
12316 current_res.excluded = excluded_count;
12320 RETURN NEXT current_res;
12323 $func$ LANGUAGE PLPGSQL;
12325 -- Drop the old 10-parameter function
12326 DROP FUNCTION IF EXISTS search.query_parser_fts (
12327 INT, INT, TEXT, INT[], INT[], INT, INT, INT, BOOL, BOOL
12330 -- Evergreen DB patch 0705.data.custom-org-tree-perms.sql
12332 -- check whether patch can be applied
12333 SELECT evergreen.upgrade_deps_block_check('0705', :eg_version);
12335 INSERT INTO permission.perm_list (id, code, description)
12338 'ADMIN_ORG_UNIT_CUSTOM_TREE',
12341 'User may update custom org unit trees',
12347 -- Evergreen DB patch 0707.schema.acq-vandelay-integration.sql
12349 SELECT evergreen.upgrade_deps_block_check('0707', :eg_version);
12353 INSERT INTO permission.perm_list ( id, code, description )
12356 'ADMIN_IMPORT_MATCH_SET',
12359 'Allows a user to create/retrieve/update/delete vandelay match sets',
12365 'VIEW_IMPORT_MATCH_SET',
12368 'Allows a user to view vandelay match sets',
12374 -- This upgrade script fixed a typo in a previous one. It was corrected in the proper place in this file.
12375 -- Still, record the fact it has been "applied".
12376 SELECT evergreen.upgrade_deps_block_check('0708', :eg_version);
12378 -- Evergreen DB patch 0709.data.misc_missing_perms.sql
12380 SELECT evergreen.upgrade_deps_block_check('0709', :eg_version);
12382 INSERT INTO permission.perm_list ( id, code, description )
12385 'ADMIN_ADDRESS_ALERT',
12388 'Allows a user to create/retrieve/update/delete address alerts',
12394 'VIEW_ADDRESS_ALERT',
12397 'Allows a user to view address alerts',
12403 'ADMIN_COPY_LOCATION_GROUP',
12406 'Allows a user to create/retrieve/update/delete copy location groups',
12412 'ADMIN_USER_ACTIVITY_TYPE',
12415 'Allows a user to create/retrieve/update/delete user activity types',
12421 -- 0715.data.add_acq_config_group
12422 SELECT evergreen.upgrade_deps_block_check('0715', :eg_version);
12424 INSERT INTO config.settings_group (name, label) VALUES
12425 ('acq', oils_i18n_gettext('config.settings_group.system', 'Acquisitions', 'coust', 'label'));
12427 UPDATE config.org_unit_setting_type
12429 WHERE name LIKE 'acq%';
12431 -- Evergreen DB patch 0716.coded_value_map_id_seq_fix.sql
12433 SELECT evergreen.upgrade_deps_block_check('0716', :eg_version);
12435 SELECT SETVAL('config.coded_value_map_id_seq'::TEXT, (SELECT max(id) FROM config.coded_value_map));
12438 -- Evergreen DB patch 0717.data.safer-control-set-defaults.sql
12440 SELECT evergreen.upgrade_deps_block_check('0717', :eg_version);
12442 -- Allow un-mapped thesauri
12443 -- ALTER TABLE authority.thesaurus ALTER COLUMN control_set DROP NOT NULL;
12444 -- XXX The above line is now covered by changes to the
12445 -- "CREATE TABLE authority.thesaurus" statement further up.
12447 -- Don't tie "No attempt to code" to LoC
12448 UPDATE authority.thesaurus SET control_set = NULL WHERE code = '|';
12449 UPDATE authority.record_entry SET control_set = NULL WHERE id IN (SELECT record FROM authority.rec_descriptor WHERE thesaurus = '|');
12454 \qecho ************************************************************************
12455 \qecho The following transaction, wrapping upgrade 0672, may take a while. If
12456 \qecho it takes an unduly long time, try it outside of a transaction.
12457 \qecho ************************************************************************
12461 -- Evergreen DB patch 0672.fix-nonfiling-titles.sql
12463 -- Titles that begin with non-filing articles using apostrophes
12464 -- (for example, "L'armée") get spaces injected between the article
12465 -- and the subsequent text, which then breaks searching for titles
12466 -- beginning with those articles.
12468 -- This patch adds a nonfiling title element to MODS32 that can then
12469 -- be used to retrieve the title proper without affecting the spaces
12470 -- in the title. It's what we want, what we really really want, for
12475 -- check whether patch can be applied
12476 SELECT evergreen.upgrade_deps_block_check('0672', :eg_version);
12478 -- Update the XPath definition before the titleNonfiling element exists;
12479 -- but are you really going to read through the whole XSL below before
12480 -- seeing this important bit?
12481 UPDATE config.metabib_field
12482 SET xpath = $$//mods32:mods/mods32:titleNonfiling[mods32:title and not (@type)]$$,
12484 WHERE field_class = 'title' AND name = 'proper';
12486 UPDATE config.xml_transform SET xslt=$$<?xml version="1.0" encoding="UTF-8"?>
12487 <xsl:stylesheet xmlns="http://www.loc.gov/mods/v3" xmlns:marc="http://www.loc.gov/MARC21/slim" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" exclude-result-prefixes="xlink marc" version="1.0">
12488 <xsl:output encoding="UTF-8" indent="yes" method="xml"/>
12490 Revision 1.14 - Fixed template isValid and fields 010, 020, 022, 024, 028, and 037 to output additional identifier elements
12491 with corresponding @type and @invalid eq 'yes' when subfields z or y (in the case of 022) exist in the MARCXML ::: 2007/01/04 17:35:20 cred
12493 Revision 1.13 - Changed order of output under cartographics to reflect schema 2006/11/28 tmee
12495 Revision 1.12 - Updated to reflect MODS 3.2 Mapping 2006/10/11 tmee
12497 Revision 1.11 - The attribute objectPart moved from <languageTerm> to <language>
12500 Revision 1.10 MODS 3.1 revisions to language and classification elements
12501 (plus ability to find marc:collection embedded in wrapper elements such as SRU zs: wrappers)
12504 Revision 1.9 subfield $y was added to field 242 2004/09/02 10:57 jrad
12506 Revision 1.8 Subject chopPunctuation expanded and attribute fixes 2004/08/12 jrad
12508 Revision 1.7 2004/03/25 08:29 jrad
12510 Revision 1.6 various validation fixes 2004/02/20 ntra
12512 Revision 1.5 2003/10/02 16:18:58 ntra
12513 MODS2 to MODS3 updates, language unstacking and
12514 de-duping, chopPunctuation expanded
12516 Revision 1.3 2003/04/03 00:07:19 ntra
12517 Revision 1.3 Additional Changes not related to MODS Version 2.0 by ntra
12519 Revision 1.2 2003/03/24 19:37:42 ckeith
12523 <xsl:template match="/">
12525 <xsl:when test="//marc:collection">
12526 <modsCollection xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.loc.gov/mods/v3 http://www.loc.gov/standards/mods/v3/mods-3-2.xsd">
12527 <xsl:for-each select="//marc:collection/marc:record">
12528 <mods version="3.2">
12529 <xsl:call-template name="marcRecord"/>
12535 <mods xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="3.2" xsi:schemaLocation="http://www.loc.gov/mods/v3 http://www.loc.gov/standards/mods/v3/mods-3-2.xsd">
12536 <xsl:for-each select="//marc:record">
12537 <xsl:call-template name="marcRecord"/>
12543 <xsl:template name="marcRecord">
12544 <xsl:variable name="leader" select="marc:leader"/>
12545 <xsl:variable name="leader6" select="substring($leader,7,1)"/>
12546 <xsl:variable name="leader7" select="substring($leader,8,1)"/>
12547 <xsl:variable name="controlField008" select="marc:controlfield[@tag='008']"/>
12548 <xsl:variable name="typeOf008">
12550 <xsl:when test="$leader6='a'">
12552 <xsl:when test="$leader7='a' or $leader7='c' or $leader7='d' or $leader7='m'">BK</xsl:when>
12553 <xsl:when test="$leader7='b' or $leader7='i' or $leader7='s'">SE</xsl:when>
12556 <xsl:when test="$leader6='t'">BK</xsl:when>
12557 <xsl:when test="$leader6='p'">MM</xsl:when>
12558 <xsl:when test="$leader6='m'">CF</xsl:when>
12559 <xsl:when test="$leader6='e' or $leader6='f'">MP</xsl:when>
12560 <xsl:when test="$leader6='g' or $leader6='k' or $leader6='o' or $leader6='r'">VM</xsl:when>
12561 <xsl:when test="$leader6='c' or $leader6='d' or $leader6='i' or $leader6='j'">MU</xsl:when>
12564 <xsl:for-each select="marc:datafield[@tag='245']">
12566 <xsl:variable name="title">
12568 <xsl:when test="marc:subfield[@code='b']">
12569 <xsl:call-template name="specialSubfieldSelect">
12570 <xsl:with-param name="axis">b</xsl:with-param>
12571 <xsl:with-param name="beforeCodes">afgk</xsl:with-param>
12572 </xsl:call-template>
12575 <xsl:call-template name="subfieldSelect">
12576 <xsl:with-param name="codes">abfgk</xsl:with-param>
12577 </xsl:call-template>
12581 <xsl:variable name="titleChop">
12582 <xsl:call-template name="chopPunctuation">
12583 <xsl:with-param name="chopString">
12584 <xsl:value-of select="$title"/>
12586 </xsl:call-template>
12589 <xsl:when test="@ind2>0">
12591 <xsl:value-of select="substring($titleChop,1,@ind2)"/>
12594 <xsl:value-of select="substring($titleChop,@ind2+1)"/>
12599 <xsl:value-of select="$titleChop"/>
12603 <xsl:if test="marc:subfield[@code='b']">
12605 <xsl:call-template name="chopPunctuation">
12606 <xsl:with-param name="chopString">
12607 <xsl:call-template name="specialSubfieldSelect">
12608 <xsl:with-param name="axis">b</xsl:with-param>
12609 <xsl:with-param name="anyCodes">b</xsl:with-param>
12610 <xsl:with-param name="afterCodes">afgk</xsl:with-param>
12611 </xsl:call-template>
12613 </xsl:call-template>
12616 <xsl:call-template name="part"></xsl:call-template>
12618 <!-- A form of title that ignores non-filing characters; useful
12619 for not converting "L'Oreal" into "L' Oreal" at index time -->
12621 <xsl:variable name="title">
12623 <xsl:when test="marc:subfield[@code='b']">
12624 <xsl:call-template name="specialSubfieldSelect">
12625 <xsl:with-param name="axis">b</xsl:with-param>
12626 <xsl:with-param name="beforeCodes">afgk</xsl:with-param>
12627 </xsl:call-template>
12630 <xsl:call-template name="subfieldSelect">
12631 <xsl:with-param name="codes">abfgk</xsl:with-param>
12632 </xsl:call-template>
12637 <xsl:value-of select="$title"/>
12639 <xsl:if test="marc:subfield[@code='b']">
12641 <xsl:call-template name="chopPunctuation">
12642 <xsl:with-param name="chopString">
12643 <xsl:call-template name="specialSubfieldSelect">
12644 <xsl:with-param name="axis">b</xsl:with-param>
12645 <xsl:with-param name="anyCodes">b</xsl:with-param>
12646 <xsl:with-param name="afterCodes">afgk</xsl:with-param>
12647 </xsl:call-template>
12649 </xsl:call-template>
12652 <xsl:call-template name="part"></xsl:call-template>
12655 <xsl:for-each select="marc:datafield[@tag='210']">
12656 <titleInfo type="abbreviated">
12658 <xsl:call-template name="chopPunctuation">
12659 <xsl:with-param name="chopString">
12660 <xsl:call-template name="subfieldSelect">
12661 <xsl:with-param name="codes">a</xsl:with-param>
12662 </xsl:call-template>
12664 </xsl:call-template>
12666 <xsl:call-template name="subtitle"/>
12669 <xsl:for-each select="marc:datafield[@tag='242']">
12670 <titleInfo type="translated">
12671 <!--09/01/04 Added subfield $y-->
12672 <xsl:for-each select="marc:subfield[@code='y']">
12673 <xsl:attribute name="lang">
12674 <xsl:value-of select="text()"/>
12678 <xsl:call-template name="chopPunctuation">
12679 <xsl:with-param name="chopString">
12680 <xsl:call-template name="subfieldSelect">
12681 <!-- 1/04 removed $h, b -->
12682 <xsl:with-param name="codes">a</xsl:with-param>
12683 </xsl:call-template>
12685 </xsl:call-template>
12688 <xsl:call-template name="subtitle"/>
12689 <xsl:call-template name="part"/>
12692 <xsl:for-each select="marc:datafield[@tag='246']">
12693 <titleInfo type="alternative">
12694 <xsl:for-each select="marc:subfield[@code='i']">
12695 <xsl:attribute name="displayLabel">
12696 <xsl:value-of select="text()"/>
12700 <xsl:call-template name="chopPunctuation">
12701 <xsl:with-param name="chopString">
12702 <xsl:call-template name="subfieldSelect">
12703 <!-- 1/04 removed $h, $b -->
12704 <xsl:with-param name="codes">af</xsl:with-param>
12705 </xsl:call-template>
12707 </xsl:call-template>
12709 <xsl:call-template name="subtitle"/>
12710 <xsl:call-template name="part"/>
12713 <xsl:for-each select="marc:datafield[@tag='130']|marc:datafield[@tag='240']|marc:datafield[@tag='730'][@ind2!='2']">
12714 <titleInfo type="uniform">
12716 <xsl:variable name="str">
12717 <xsl:for-each select="marc:subfield">
12718 <xsl:if test="(contains('adfklmor',@code) and (not(../marc:subfield[@code='n' or @code='p']) or (following-sibling::marc:subfield[@code='n' or @code='p'])))">
12719 <xsl:value-of select="text()"/>
12720 <xsl:text> </xsl:text>
12724 <xsl:call-template name="chopPunctuation">
12725 <xsl:with-param name="chopString">
12726 <xsl:value-of select="substring($str,1,string-length($str)-1)"/>
12728 </xsl:call-template>
12730 <xsl:call-template name="part"/>
12733 <xsl:for-each select="marc:datafield[@tag='740'][@ind2!='2']">
12734 <titleInfo type="alternative">
12736 <xsl:call-template name="chopPunctuation">
12737 <xsl:with-param name="chopString">
12738 <xsl:call-template name="subfieldSelect">
12739 <xsl:with-param name="codes">ah</xsl:with-param>
12740 </xsl:call-template>
12742 </xsl:call-template>
12744 <xsl:call-template name="part"/>
12747 <xsl:for-each select="marc:datafield[@tag='100']">
12748 <name type="personal">
12749 <xsl:call-template name="nameABCDQ"/>
12750 <xsl:call-template name="affiliation"/>
12752 <roleTerm authority="marcrelator" type="text">creator</roleTerm>
12754 <xsl:call-template name="role"/>
12757 <xsl:for-each select="marc:datafield[@tag='110']">
12758 <name type="corporate">
12759 <xsl:call-template name="nameABCDN"/>
12761 <roleTerm authority="marcrelator" type="text">creator</roleTerm>
12763 <xsl:call-template name="role"/>
12766 <xsl:for-each select="marc:datafield[@tag='111']">
12767 <name type="conference">
12768 <xsl:call-template name="nameACDEQ"/>
12770 <roleTerm authority="marcrelator" type="text">creator</roleTerm>
12772 <xsl:call-template name="role"/>
12775 <xsl:for-each select="marc:datafield[@tag='700'][not(marc:subfield[@code='t'])]">
12776 <name type="personal">
12777 <xsl:call-template name="nameABCDQ"/>
12778 <xsl:call-template name="affiliation"/>
12779 <xsl:call-template name="role"/>
12782 <xsl:for-each select="marc:datafield[@tag='710'][not(marc:subfield[@code='t'])]">
12783 <name type="corporate">
12784 <xsl:call-template name="nameABCDN"/>
12785 <xsl:call-template name="role"/>
12788 <xsl:for-each select="marc:datafield[@tag='711'][not(marc:subfield[@code='t'])]">
12789 <name type="conference">
12790 <xsl:call-template name="nameACDEQ"/>
12791 <xsl:call-template name="role"/>
12794 <xsl:for-each select="marc:datafield[@tag='720'][not(marc:subfield[@code='t'])]">
12796 <xsl:if test="@ind1=1">
12797 <xsl:attribute name="type">
12798 <xsl:text>personal</xsl:text>
12802 <xsl:value-of select="marc:subfield[@code='a']"/>
12804 <xsl:call-template name="role"/>
12808 <xsl:if test="$leader7='c'">
12809 <xsl:attribute name="collection">yes</xsl:attribute>
12811 <xsl:if test="$leader6='d' or $leader6='f' or $leader6='p' or $leader6='t'">
12812 <xsl:attribute name="manuscript">yes</xsl:attribute>
12815 <xsl:when test="$leader6='a' or $leader6='t'">text</xsl:when>
12816 <xsl:when test="$leader6='e' or $leader6='f'">cartographic</xsl:when>
12817 <xsl:when test="$leader6='c' or $leader6='d'">notated music</xsl:when>
12818 <xsl:when test="$leader6='i'">sound recording-nonmusical</xsl:when>
12819 <xsl:when test="$leader6='j'">sound recording-musical</xsl:when>
12820 <xsl:when test="$leader6='k'">still image</xsl:when>
12821 <xsl:when test="$leader6='g'">moving image</xsl:when>
12822 <xsl:when test="$leader6='r'">three dimensional object</xsl:when>
12823 <xsl:when test="$leader6='m'">software, multimedia</xsl:when>
12824 <xsl:when test="$leader6='p'">mixed material</xsl:when>
12827 <xsl:if test="substring($controlField008,26,1)='d'">
12828 <genre authority="marc">globe</genre>
12830 <xsl:if test="marc:controlfield[@tag='007'][substring(text(),1,1)='a'][substring(text(),2,1)='r']">
12831 <genre authority="marc">remote sensing image</genre>
12833 <xsl:if test="$typeOf008='MP'">
12834 <xsl:variable name="controlField008-25" select="substring($controlField008,26,1)"></xsl:variable>
12836 <xsl:when test="$controlField008-25='a' or $controlField008-25='b' or $controlField008-25='c' or marc:controlfield[@tag=007][substring(text(),1,1)='a'][substring(text(),2,1)='j']">
12837 <genre authority="marc">map</genre>
12839 <xsl:when test="$controlField008-25='e' or marc:controlfield[@tag=007][substring(text(),1,1)='a'][substring(text(),2,1)='d']">
12840 <genre authority="marc">atlas</genre>
12844 <xsl:if test="$typeOf008='SE'">
12845 <xsl:variable name="controlField008-21" select="substring($controlField008,22,1)"></xsl:variable>
12847 <xsl:when test="$controlField008-21='d'">
12848 <genre authority="marc">database</genre>
12850 <xsl:when test="$controlField008-21='l'">
12851 <genre authority="marc">loose-leaf</genre>
12853 <xsl:when test="$controlField008-21='m'">
12854 <genre authority="marc">series</genre>
12856 <xsl:when test="$controlField008-21='n'">
12857 <genre authority="marc">newspaper</genre>
12859 <xsl:when test="$controlField008-21='p'">
12860 <genre authority="marc">periodical</genre>
12862 <xsl:when test="$controlField008-21='w'">
12863 <genre authority="marc">web site</genre>
12867 <xsl:if test="$typeOf008='BK' or $typeOf008='SE'">
12868 <xsl:variable name="controlField008-24" select="substring($controlField008,25,4)"></xsl:variable>
12870 <xsl:when test="contains($controlField008-24,'a')">
12871 <genre authority="marc">abstract or summary</genre>
12873 <xsl:when test="contains($controlField008-24,'b')">
12874 <genre authority="marc">bibliography</genre>
12876 <xsl:when test="contains($controlField008-24,'c')">
12877 <genre authority="marc">catalog</genre>
12879 <xsl:when test="contains($controlField008-24,'d')">
12880 <genre authority="marc">dictionary</genre>
12882 <xsl:when test="contains($controlField008-24,'e')">
12883 <genre authority="marc">encyclopedia</genre>
12885 <xsl:when test="contains($controlField008-24,'f')">
12886 <genre authority="marc">handbook</genre>
12888 <xsl:when test="contains($controlField008-24,'g')">
12889 <genre authority="marc">legal article</genre>
12891 <xsl:when test="contains($controlField008-24,'i')">
12892 <genre authority="marc">index</genre>
12894 <xsl:when test="contains($controlField008-24,'k')">
12895 <genre authority="marc">discography</genre>
12897 <xsl:when test="contains($controlField008-24,'l')">
12898 <genre authority="marc">legislation</genre>
12900 <xsl:when test="contains($controlField008-24,'m')">
12901 <genre authority="marc">theses</genre>
12903 <xsl:when test="contains($controlField008-24,'n')">
12904 <genre authority="marc">survey of literature</genre>
12906 <xsl:when test="contains($controlField008-24,'o')">
12907 <genre authority="marc">review</genre>
12909 <xsl:when test="contains($controlField008-24,'p')">
12910 <genre authority="marc">programmed text</genre>
12912 <xsl:when test="contains($controlField008-24,'q')">
12913 <genre authority="marc">filmography</genre>
12915 <xsl:when test="contains($controlField008-24,'r')">
12916 <genre authority="marc">directory</genre>
12918 <xsl:when test="contains($controlField008-24,'s')">
12919 <genre authority="marc">statistics</genre>
12921 <xsl:when test="contains($controlField008-24,'t')">
12922 <genre authority="marc">technical report</genre>
12924 <xsl:when test="contains($controlField008-24,'v')">
12925 <genre authority="marc">legal case and case notes</genre>
12927 <xsl:when test="contains($controlField008-24,'w')">
12928 <genre authority="marc">law report or digest</genre>
12930 <xsl:when test="contains($controlField008-24,'z')">
12931 <genre authority="marc">treaty</genre>
12934 <xsl:variable name="controlField008-29" select="substring($controlField008,30,1)"></xsl:variable>
12936 <xsl:when test="$controlField008-29='1'">
12937 <genre authority="marc">conference publication</genre>
12941 <xsl:if test="$typeOf008='CF'">
12942 <xsl:variable name="controlField008-26" select="substring($controlField008,27,1)"></xsl:variable>
12944 <xsl:when test="$controlField008-26='a'">
12945 <genre authority="marc">numeric data</genre>
12947 <xsl:when test="$controlField008-26='e'">
12948 <genre authority="marc">database</genre>
12950 <xsl:when test="$controlField008-26='f'">
12951 <genre authority="marc">font</genre>
12953 <xsl:when test="$controlField008-26='g'">
12954 <genre authority="marc">game</genre>
12958 <xsl:if test="$typeOf008='BK'">
12959 <xsl:if test="substring($controlField008,25,1)='j'">
12960 <genre authority="marc">patent</genre>
12962 <xsl:if test="substring($controlField008,31,1)='1'">
12963 <genre authority="marc">festschrift</genre>
12965 <xsl:variable name="controlField008-34" select="substring($controlField008,35,1)"></xsl:variable>
12966 <xsl:if test="$controlField008-34='a' or $controlField008-34='b' or $controlField008-34='c' or $controlField008-34='d'">
12967 <genre authority="marc">biography</genre>
12969 <xsl:variable name="controlField008-33" select="substring($controlField008,34,1)"></xsl:variable>
12971 <xsl:when test="$controlField008-33='e'">
12972 <genre authority="marc">essay</genre>
12974 <xsl:when test="$controlField008-33='d'">
12975 <genre authority="marc">drama</genre>
12977 <xsl:when test="$controlField008-33='c'">
12978 <genre authority="marc">comic strip</genre>
12980 <xsl:when test="$controlField008-33='l'">
12981 <genre authority="marc">fiction</genre>
12983 <xsl:when test="$controlField008-33='h'">
12984 <genre authority="marc">humor, satire</genre>
12986 <xsl:when test="$controlField008-33='i'">
12987 <genre authority="marc">letter</genre>
12989 <xsl:when test="$controlField008-33='f'">
12990 <genre authority="marc">novel</genre>
12992 <xsl:when test="$controlField008-33='j'">
12993 <genre authority="marc">short story</genre>
12995 <xsl:when test="$controlField008-33='s'">
12996 <genre authority="marc">speech</genre>
13000 <xsl:if test="$typeOf008='MU'">
13001 <xsl:variable name="controlField008-30-31" select="substring($controlField008,31,2)"></xsl:variable>
13002 <xsl:if test="contains($controlField008-30-31,'b')">
13003 <genre authority="marc">biography</genre>
13005 <xsl:if test="contains($controlField008-30-31,'c')">
13006 <genre authority="marc">conference publication</genre>
13008 <xsl:if test="contains($controlField008-30-31,'d')">
13009 <genre authority="marc">drama</genre>
13011 <xsl:if test="contains($controlField008-30-31,'e')">
13012 <genre authority="marc">essay</genre>
13014 <xsl:if test="contains($controlField008-30-31,'f')">
13015 <genre authority="marc">fiction</genre>
13017 <xsl:if test="contains($controlField008-30-31,'o')">
13018 <genre authority="marc">folktale</genre>
13020 <xsl:if test="contains($controlField008-30-31,'h')">
13021 <genre authority="marc">history</genre>
13023 <xsl:if test="contains($controlField008-30-31,'k')">
13024 <genre authority="marc">humor, satire</genre>
13026 <xsl:if test="contains($controlField008-30-31,'m')">
13027 <genre authority="marc">memoir</genre>
13029 <xsl:if test="contains($controlField008-30-31,'p')">
13030 <genre authority="marc">poetry</genre>
13032 <xsl:if test="contains($controlField008-30-31,'r')">
13033 <genre authority="marc">rehearsal</genre>
13035 <xsl:if test="contains($controlField008-30-31,'g')">
13036 <genre authority="marc">reporting</genre>
13038 <xsl:if test="contains($controlField008-30-31,'s')">
13039 <genre authority="marc">sound</genre>
13041 <xsl:if test="contains($controlField008-30-31,'l')">
13042 <genre authority="marc">speech</genre>
13045 <xsl:if test="$typeOf008='VM'">
13046 <xsl:variable name="controlField008-33" select="substring($controlField008,34,1)"></xsl:variable>
13048 <xsl:when test="$controlField008-33='a'">
13049 <genre authority="marc">art original</genre>
13051 <xsl:when test="$controlField008-33='b'">
13052 <genre authority="marc">kit</genre>
13054 <xsl:when test="$controlField008-33='c'">
13055 <genre authority="marc">art reproduction</genre>
13057 <xsl:when test="$controlField008-33='d'">
13058 <genre authority="marc">diorama</genre>
13060 <xsl:when test="$controlField008-33='f'">
13061 <genre authority="marc">filmstrip</genre>
13063 <xsl:when test="$controlField008-33='g'">
13064 <genre authority="marc">legal article</genre>
13066 <xsl:when test="$controlField008-33='i'">
13067 <genre authority="marc">picture</genre>
13069 <xsl:when test="$controlField008-33='k'">
13070 <genre authority="marc">graphic</genre>
13072 <xsl:when test="$controlField008-33='l'">
13073 <genre authority="marc">technical drawing</genre>
13075 <xsl:when test="$controlField008-33='m'">
13076 <genre authority="marc">motion picture</genre>
13078 <xsl:when test="$controlField008-33='n'">
13079 <genre authority="marc">chart</genre>
13081 <xsl:when test="$controlField008-33='o'">
13082 <genre authority="marc">flash card</genre>
13084 <xsl:when test="$controlField008-33='p'">
13085 <genre authority="marc">microscope slide</genre>
13087 <xsl:when test="$controlField008-33='q' or marc:controlfield[@tag=007][substring(text(),1,1)='a'][substring(text(),2,1)='q']">
13088 <genre authority="marc">model</genre>
13090 <xsl:when test="$controlField008-33='r'">
13091 <genre authority="marc">realia</genre>
13093 <xsl:when test="$controlField008-33='s'">
13094 <genre authority="marc">slide</genre>
13096 <xsl:when test="$controlField008-33='t'">
13097 <genre authority="marc">transparency</genre>
13099 <xsl:when test="$controlField008-33='v'">
13100 <genre authority="marc">videorecording</genre>
13102 <xsl:when test="$controlField008-33='w'">
13103 <genre authority="marc">toy</genre>
13107 <xsl:for-each select="marc:datafield[@tag=655]">
13108 <genre authority="marc">
13109 <xsl:attribute name="authority">
13110 <xsl:value-of select="marc:subfield[@code='2']"/>
13112 <xsl:call-template name="subfieldSelect">
13113 <xsl:with-param name="codes">abvxyz</xsl:with-param>
13114 <xsl:with-param name="delimeter">-</xsl:with-param>
13115 </xsl:call-template>
13119 <xsl:variable name="MARCpublicationCode" select="normalize-space(substring($controlField008,16,3))"></xsl:variable>
13120 <xsl:if test="translate($MARCpublicationCode,'|','')">
13123 <xsl:attribute name="type">code</xsl:attribute>
13124 <xsl:attribute name="authority">marccountry</xsl:attribute>
13125 <xsl:value-of select="$MARCpublicationCode"/>
13129 <xsl:for-each select="marc:datafield[@tag=044]/marc:subfield[@code='c']">
13132 <xsl:attribute name="type">code</xsl:attribute>
13133 <xsl:attribute name="authority">iso3166</xsl:attribute>
13134 <xsl:value-of select="."/>
13138 <xsl:for-each select="marc:datafield[@tag=260]/marc:subfield[@code='a']">
13141 <xsl:attribute name="type">text</xsl:attribute>
13142 <xsl:call-template name="chopPunctuationFront">
13143 <xsl:with-param name="chopString">
13144 <xsl:call-template name="chopPunctuation">
13145 <xsl:with-param name="chopString" select="."/>
13146 </xsl:call-template>
13148 </xsl:call-template>
13152 <xsl:for-each select="marc:datafield[@tag=046]/marc:subfield[@code='m']">
13153 <dateValid point="start">
13154 <xsl:value-of select="."/>
13157 <xsl:for-each select="marc:datafield[@tag=046]/marc:subfield[@code='n']">
13158 <dateValid point="end">
13159 <xsl:value-of select="."/>
13162 <xsl:for-each select="marc:datafield[@tag=046]/marc:subfield[@code='j']">
13164 <xsl:value-of select="."/>
13167 <xsl:for-each select="marc:datafield[@tag=260]/marc:subfield[@code='b' or @code='c' or @code='g']">
13169 <xsl:when test="@code='b'">
13171 <xsl:call-template name="chopPunctuation">
13172 <xsl:with-param name="chopString" select="."/>
13173 <xsl:with-param name="punctuation">
13174 <xsl:text>:,;/ </xsl:text>
13176 </xsl:call-template>
13179 <xsl:when test="@code='c'">
13181 <xsl:call-template name="chopPunctuation">
13182 <xsl:with-param name="chopString" select="."/>
13183 </xsl:call-template>
13186 <xsl:when test="@code='g'">
13188 <xsl:value-of select="."/>
13193 <xsl:variable name="dataField260c">
13194 <xsl:call-template name="chopPunctuation">
13195 <xsl:with-param name="chopString" select="marc:datafield[@tag=260]/marc:subfield[@code='c']"></xsl:with-param>
13196 </xsl:call-template>
13198 <xsl:variable name="controlField008-7-10" select="normalize-space(substring($controlField008, 8, 4))"></xsl:variable>
13199 <xsl:variable name="controlField008-11-14" select="normalize-space(substring($controlField008, 12, 4))"></xsl:variable>
13200 <xsl:variable name="controlField008-6" select="normalize-space(substring($controlField008, 7, 1))"></xsl:variable>
13201 <xsl:if test="$controlField008-6='e' or $controlField008-6='p' or $controlField008-6='r' or $controlField008-6='t' or $controlField008-6='s'">
13202 <xsl:if test="$controlField008-7-10 and ($controlField008-7-10 != $dataField260c)">
13203 <dateIssued encoding="marc">
13204 <xsl:value-of select="$controlField008-7-10"/>
13208 <xsl:if test="$controlField008-6='c' or $controlField008-6='d' or $controlField008-6='i' or $controlField008-6='k' or $controlField008-6='m' or $controlField008-6='q' or $controlField008-6='u'">
13209 <xsl:if test="$controlField008-7-10">
13210 <dateIssued encoding="marc" point="start">
13211 <xsl:value-of select="$controlField008-7-10"/>
13215 <xsl:if test="$controlField008-6='c' or $controlField008-6='d' or $controlField008-6='i' or $controlField008-6='k' or $controlField008-6='m' or $controlField008-6='q' or $controlField008-6='u'">
13216 <xsl:if test="$controlField008-11-14">
13217 <dateIssued encoding="marc" point="end">
13218 <xsl:value-of select="$controlField008-11-14"/>
13222 <xsl:if test="$controlField008-6='q'">
13223 <xsl:if test="$controlField008-7-10">
13224 <dateIssued encoding="marc" point="start" qualifier="questionable">
13225 <xsl:value-of select="$controlField008-7-10"/>
13229 <xsl:if test="$controlField008-6='q'">
13230 <xsl:if test="$controlField008-11-14">
13231 <dateIssued encoding="marc" point="end" qualifier="questionable">
13232 <xsl:value-of select="$controlField008-11-14"/>
13236 <xsl:if test="$controlField008-6='t'">
13237 <xsl:if test="$controlField008-11-14">
13238 <copyrightDate encoding="marc">
13239 <xsl:value-of select="$controlField008-11-14"/>
13243 <xsl:for-each select="marc:datafield[@tag=033][@ind1=0 or @ind1=1]/marc:subfield[@code='a']">
13244 <dateCaptured encoding="iso8601">
13245 <xsl:value-of select="."/>
13248 <xsl:for-each select="marc:datafield[@tag=033][@ind1=2]/marc:subfield[@code='a'][1]">
13249 <dateCaptured encoding="iso8601" point="start">
13250 <xsl:value-of select="."/>
13253 <xsl:for-each select="marc:datafield[@tag=033][@ind1=2]/marc:subfield[@code='a'][2]">
13254 <dateCaptured encoding="iso8601" point="end">
13255 <xsl:value-of select="."/>
13258 <xsl:for-each select="marc:datafield[@tag=250]/marc:subfield[@code='a']">
13260 <xsl:value-of select="."/>
13263 <xsl:for-each select="marc:leader">
13266 <xsl:when test="$leader7='a' or $leader7='c' or $leader7='d' or $leader7='m'">monographic</xsl:when>
13267 <xsl:when test="$leader7='b' or $leader7='i' or $leader7='s'">continuing</xsl:when>
13271 <xsl:for-each select="marc:datafield[@tag=310]|marc:datafield[@tag=321]">
13273 <xsl:call-template name="subfieldSelect">
13274 <xsl:with-param name="codes">ab</xsl:with-param>
13275 </xsl:call-template>
13279 <xsl:variable name="controlField008-35-37" select="normalize-space(translate(substring($controlField008,36,3),'|#',''))"></xsl:variable>
13280 <xsl:if test="$controlField008-35-37">
13282 <languageTerm authority="iso639-2b" type="code">
13283 <xsl:value-of select="substring($controlField008,36,3)"/>
13287 <xsl:for-each select="marc:datafield[@tag=041]">
13288 <xsl:for-each select="marc:subfield[@code='a' or @code='b' or @code='d' or @code='e' or @code='f' or @code='g' or @code='h']">
13289 <xsl:variable name="langCodes" select="."/>
13291 <xsl:when test="../marc:subfield[@code='2']='rfc3066'">
13292 <!-- not stacked but could be repeated -->
13293 <xsl:call-template name="rfcLanguages">
13294 <xsl:with-param name="nodeNum">
13295 <xsl:value-of select="1"/>
13297 <xsl:with-param name="usedLanguages">
13298 <xsl:text></xsl:text>
13300 <xsl:with-param name="controlField008-35-37">
13301 <xsl:value-of select="$controlField008-35-37"></xsl:value-of>
13303 </xsl:call-template>
13307 <xsl:variable name="allLanguages">
13308 <xsl:copy-of select="$langCodes"></xsl:copy-of>
13310 <xsl:variable name="currentLanguage">
13311 <xsl:value-of select="substring($allLanguages,1,3)"></xsl:value-of>
13313 <xsl:call-template name="isoLanguage">
13314 <xsl:with-param name="currentLanguage">
13315 <xsl:value-of select="substring($allLanguages,1,3)"></xsl:value-of>
13317 <xsl:with-param name="remainingLanguages">
13318 <xsl:value-of select="substring($allLanguages,4,string-length($allLanguages)-3)"></xsl:value-of>
13320 <xsl:with-param name="usedLanguages">
13321 <xsl:if test="$controlField008-35-37">
13322 <xsl:value-of select="$controlField008-35-37"></xsl:value-of>
13325 </xsl:call-template>
13330 <xsl:variable name="physicalDescription">
13331 <!--3.2 change tmee 007/11 -->
13332 <xsl:if test="$typeOf008='CF' and marc:controlfield[@tag=007][substring(.,12,1)='a']">
13333 <digitalOrigin>reformatted digital</digitalOrigin>
13335 <xsl:if test="$typeOf008='CF' and marc:controlfield[@tag=007][substring(.,12,1)='b']">
13336 <digitalOrigin>digitized microfilm</digitalOrigin>
13338 <xsl:if test="$typeOf008='CF' and marc:controlfield[@tag=007][substring(.,12,1)='d']">
13339 <digitalOrigin>digitized other analog</digitalOrigin>
13341 <xsl:variable name="controlField008-23" select="substring($controlField008,24,1)"></xsl:variable>
13342 <xsl:variable name="controlField008-29" select="substring($controlField008,30,1)"></xsl:variable>
13343 <xsl:variable name="check008-23">
13344 <xsl:if test="$typeOf008='BK' or $typeOf008='MU' or $typeOf008='SE' or $typeOf008='MM'">
13345 <xsl:value-of select="true()"></xsl:value-of>
13348 <xsl:variable name="check008-29">
13349 <xsl:if test="$typeOf008='MP' or $typeOf008='VM'">
13350 <xsl:value-of select="true()"></xsl:value-of>
13354 <xsl:when test="($check008-23 and $controlField008-23='f') or ($check008-29 and $controlField008-29='f')">
13355 <form authority="marcform">braille</form>
13357 <xsl:when test="($controlField008-23=' ' and ($leader6='c' or $leader6='d')) or (($typeOf008='BK' or $typeOf008='SE') and ($controlField008-23=' ' or $controlField008='r'))">
13358 <form authority="marcform">print</form>
13360 <xsl:when test="$leader6 = 'm' or ($check008-23 and $controlField008-23='s') or ($check008-29 and $controlField008-29='s')">
13361 <form authority="marcform">electronic</form>
13363 <xsl:when test="($check008-23 and $controlField008-23='b') or ($check008-29 and $controlField008-29='b')">
13364 <form authority="marcform">microfiche</form>
13366 <xsl:when test="($check008-23 and $controlField008-23='a') or ($check008-29 and $controlField008-29='a')">
13367 <form authority="marcform">microfilm</form>
13371 <xsl:if test="marc:datafield[@tag=130]/marc:subfield[@code='h']">
13372 <form authority="gmd">
13373 <xsl:call-template name="chopBrackets">
13374 <xsl:with-param name="chopString">
13375 <xsl:value-of select="marc:datafield[@tag=130]/marc:subfield[@code='h']"></xsl:value-of>
13377 </xsl:call-template>
13380 <xsl:if test="marc:datafield[@tag=240]/marc:subfield[@code='h']">
13381 <form authority="gmd">
13382 <xsl:call-template name="chopBrackets">
13383 <xsl:with-param name="chopString">
13384 <xsl:value-of select="marc:datafield[@tag=240]/marc:subfield[@code='h']"></xsl:value-of>
13386 </xsl:call-template>
13389 <xsl:if test="marc:datafield[@tag=242]/marc:subfield[@code='h']">
13390 <form authority="gmd">
13391 <xsl:call-template name="chopBrackets">
13392 <xsl:with-param name="chopString">
13393 <xsl:value-of select="marc:datafield[@tag=242]/marc:subfield[@code='h']"></xsl:value-of>
13395 </xsl:call-template>
13398 <xsl:if test="marc:datafield[@tag=245]/marc:subfield[@code='h']">
13399 <form authority="gmd">
13400 <xsl:call-template name="chopBrackets">
13401 <xsl:with-param name="chopString">
13402 <xsl:value-of select="marc:datafield[@tag=245]/marc:subfield[@code='h']"></xsl:value-of>
13404 </xsl:call-template>
13407 <xsl:if test="marc:datafield[@tag=246]/marc:subfield[@code='h']">
13408 <form authority="gmd">
13409 <xsl:call-template name="chopBrackets">
13410 <xsl:with-param name="chopString">
13411 <xsl:value-of select="marc:datafield[@tag=246]/marc:subfield[@code='h']"></xsl:value-of>
13413 </xsl:call-template>
13416 <xsl:if test="marc:datafield[@tag=730]/marc:subfield[@code='h']">
13417 <form authority="gmd">
13418 <xsl:call-template name="chopBrackets">
13419 <xsl:with-param name="chopString">
13420 <xsl:value-of select="marc:datafield[@tag=730]/marc:subfield[@code='h']"></xsl:value-of>
13422 </xsl:call-template>
13425 <xsl:for-each select="marc:datafield[@tag=256]/marc:subfield[@code='a']">
13427 <xsl:value-of select="."></xsl:value-of>
13430 <xsl:for-each select="marc:controlfield[@tag=007][substring(text(),1,1)='c']">
13432 <xsl:when test="substring(text(),14,1)='a'">
13433 <reformattingQuality>access</reformattingQuality>
13435 <xsl:when test="substring(text(),14,1)='p'">
13436 <reformattingQuality>preservation</reformattingQuality>
13438 <xsl:when test="substring(text(),14,1)='r'">
13439 <reformattingQuality>replacement</reformattingQuality>
13443 <!--3.2 change tmee 007/01 -->
13444 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='c'][substring(text(),2,1)='b']">
13445 <form authority="smd">chip cartridge</form>
13447 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='c'][substring(text(),2,1)='c']">
13448 <form authority="smd">computer optical disc cartridge</form>
13450 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='c'][substring(text(),2,1)='j']">
13451 <form authority="smd">magnetic disc</form>
13453 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='c'][substring(text(),2,1)='m']">
13454 <form authority="smd">magneto-optical disc</form>
13456 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='c'][substring(text(),2,1)='o']">
13457 <form authority="smd">optical disc</form>
13459 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='c'][substring(text(),2,1)='r']">
13460 <form authority="smd">remote</form>
13462 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='c'][substring(text(),2,1)='a']">
13463 <form authority="smd">tape cartridge</form>
13465 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='c'][substring(text(),2,1)='f']">
13466 <form authority="smd">tape cassette</form>
13468 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='c'][substring(text(),2,1)='h']">
13469 <form authority="smd">tape reel</form>
13472 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='d'][substring(text(),2,1)='a']">
13473 <form authority="smd">celestial globe</form>
13475 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='d'][substring(text(),2,1)='e']">
13476 <form authority="smd">earth moon globe</form>
13478 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='d'][substring(text(),2,1)='b']">
13479 <form authority="smd">planetary or lunar globe</form>
13481 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='d'][substring(text(),2,1)='c']">
13482 <form authority="smd">terrestrial globe</form>
13485 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='o'][substring(text(),2,1)='o']">
13486 <form authority="smd">kit</form>
13489 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='a'][substring(text(),2,1)='d']">
13490 <form authority="smd">atlas</form>
13492 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='a'][substring(text(),2,1)='g']">
13493 <form authority="smd">diagram</form>
13495 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='a'][substring(text(),2,1)='j']">
13496 <form authority="smd">map</form>
13498 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='a'][substring(text(),2,1)='q']">
13499 <form authority="smd">model</form>
13501 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='a'][substring(text(),2,1)='k']">
13502 <form authority="smd">profile</form>
13504 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='a'][substring(text(),2,1)='r']">
13505 <form authority="smd">remote-sensing image</form>
13507 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='a'][substring(text(),2,1)='s']">
13508 <form authority="smd">section</form>
13510 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='a'][substring(text(),2,1)='y']">
13511 <form authority="smd">view</form>
13514 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='h'][substring(text(),2,1)='a']">
13515 <form authority="smd">aperture card</form>
13517 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='h'][substring(text(),2,1)='e']">
13518 <form authority="smd">microfiche</form>
13520 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='h'][substring(text(),2,1)='f']">
13521 <form authority="smd">microfiche cassette</form>
13523 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='h'][substring(text(),2,1)='b']">
13524 <form authority="smd">microfilm cartridge</form>
13526 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='h'][substring(text(),2,1)='c']">
13527 <form authority="smd">microfilm cassette</form>
13529 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='h'][substring(text(),2,1)='d']">
13530 <form authority="smd">microfilm reel</form>
13532 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='h'][substring(text(),2,1)='g']">
13533 <form authority="smd">microopaque</form>
13536 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='m'][substring(text(),2,1)='c']">
13537 <form authority="smd">film cartridge</form>
13539 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='m'][substring(text(),2,1)='f']">
13540 <form authority="smd">film cassette</form>
13542 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='m'][substring(text(),2,1)='r']">
13543 <form authority="smd">film reel</form>
13546 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='k'][substring(text(),2,1)='n']">
13547 <form authority="smd">chart</form>
13549 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='k'][substring(text(),2,1)='c']">
13550 <form authority="smd">collage</form>
13552 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='k'][substring(text(),2,1)='d']">
13553 <form authority="smd">drawing</form>
13555 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='k'][substring(text(),2,1)='o']">
13556 <form authority="smd">flash card</form>
13558 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='k'][substring(text(),2,1)='e']">
13559 <form authority="smd">painting</form>
13561 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='k'][substring(text(),2,1)='f']">
13562 <form authority="smd">photomechanical print</form>
13564 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='k'][substring(text(),2,1)='g']">
13565 <form authority="smd">photonegative</form>
13567 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='k'][substring(text(),2,1)='h']">
13568 <form authority="smd">photoprint</form>
13570 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='k'][substring(text(),2,1)='i']">
13571 <form authority="smd">picture</form>
13573 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='k'][substring(text(),2,1)='j']">
13574 <form authority="smd">print</form>
13576 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='k'][substring(text(),2,1)='l']">
13577 <form authority="smd">technical drawing</form>
13580 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='q'][substring(text(),2,1)='q']">
13581 <form authority="smd">notated music</form>
13584 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='g'][substring(text(),2,1)='d']">
13585 <form authority="smd">filmslip</form>
13587 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='g'][substring(text(),2,1)='c']">
13588 <form authority="smd">filmstrip cartridge</form>
13590 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='g'][substring(text(),2,1)='o']">
13591 <form authority="smd">filmstrip roll</form>
13593 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='g'][substring(text(),2,1)='f']">
13594 <form authority="smd">other filmstrip type</form>
13596 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='g'][substring(text(),2,1)='s']">
13597 <form authority="smd">slide</form>
13599 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='g'][substring(text(),2,1)='t']">
13600 <form authority="smd">transparency</form>
13602 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='r'][substring(text(),2,1)='r']">
13603 <form authority="smd">remote-sensing image</form>
13605 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='s'][substring(text(),2,1)='e']">
13606 <form authority="smd">cylinder</form>
13608 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='s'][substring(text(),2,1)='q']">
13609 <form authority="smd">roll</form>
13611 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='s'][substring(text(),2,1)='g']">
13612 <form authority="smd">sound cartridge</form>
13614 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='s'][substring(text(),2,1)='s']">
13615 <form authority="smd">sound cassette</form>
13617 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='s'][substring(text(),2,1)='d']">
13618 <form authority="smd">sound disc</form>
13620 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='s'][substring(text(),2,1)='t']">
13621 <form authority="smd">sound-tape reel</form>
13623 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='s'][substring(text(),2,1)='i']">
13624 <form authority="smd">sound-track film</form>
13626 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='s'][substring(text(),2,1)='w']">
13627 <form authority="smd">wire recording</form>
13630 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='f'][substring(text(),2,1)='c']">
13631 <form authority="smd">braille</form>
13633 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='f'][substring(text(),2,1)='b']">
13634 <form authority="smd">combination</form>
13636 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='f'][substring(text(),2,1)='a']">
13637 <form authority="smd">moon</form>
13639 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='f'][substring(text(),2,1)='d']">
13640 <form authority="smd">tactile, with no writing system</form>
13643 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='t'][substring(text(),2,1)='c']">
13644 <form authority="smd">braille</form>
13646 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='t'][substring(text(),2,1)='b']">
13647 <form authority="smd">large print</form>
13649 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='t'][substring(text(),2,1)='a']">
13650 <form authority="smd">regular print</form>
13652 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='t'][substring(text(),2,1)='d']">
13653 <form authority="smd">text in looseleaf binder</form>
13656 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='v'][substring(text(),2,1)='c']">
13657 <form authority="smd">videocartridge</form>
13659 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='v'][substring(text(),2,1)='f']">
13660 <form authority="smd">videocassette</form>
13662 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='v'][substring(text(),2,1)='d']">
13663 <form authority="smd">videodisc</form>
13665 <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='v'][substring(text(),2,1)='r']">
13666 <form authority="smd">videoreel</form>
13669 <xsl:for-each select="marc:datafield[@tag=856]/marc:subfield[@code='q'][string-length(.)>1]">
13670 <internetMediaType>
13671 <xsl:value-of select="."></xsl:value-of>
13672 </internetMediaType>
13674 <xsl:for-each select="marc:datafield[@tag=300]">
13676 <xsl:call-template name="subfieldSelect">
13677 <xsl:with-param name="codes">abce</xsl:with-param>
13678 </xsl:call-template>
13682 <xsl:if test="string-length(normalize-space($physicalDescription))">
13683 <physicalDescription>
13684 <xsl:copy-of select="$physicalDescription"></xsl:copy-of>
13685 </physicalDescription>
13687 <xsl:for-each select="marc:datafield[@tag=520]">
13689 <xsl:call-template name="uri"></xsl:call-template>
13690 <xsl:call-template name="subfieldSelect">
13691 <xsl:with-param name="codes">ab</xsl:with-param>
13692 </xsl:call-template>
13695 <xsl:for-each select="marc:datafield[@tag=505]">
13697 <xsl:call-template name="uri"></xsl:call-template>
13698 <xsl:call-template name="subfieldSelect">
13699 <xsl:with-param name="codes">agrt</xsl:with-param>
13700 </xsl:call-template>
13703 <xsl:for-each select="marc:datafield[@tag=521]">
13705 <xsl:call-template name="subfieldSelect">
13706 <xsl:with-param name="codes">ab</xsl:with-param>
13707 </xsl:call-template>
13710 <xsl:if test="$typeOf008='BK' or $typeOf008='CF' or $typeOf008='MU' or $typeOf008='VM'">
13711 <xsl:variable name="controlField008-22" select="substring($controlField008,23,1)"></xsl:variable>
13714 <xsl:when test="$controlField008-22='d'">
13715 <targetAudience authority="marctarget">adolescent</targetAudience>
13717 <xsl:when test="$controlField008-22='e'">
13718 <targetAudience authority="marctarget">adult</targetAudience>
13720 <xsl:when test="$controlField008-22='g'">
13721 <targetAudience authority="marctarget">general</targetAudience>
13723 <xsl:when test="$controlField008-22='b' or $controlField008-22='c' or $controlField008-22='j'">
13724 <targetAudience authority="marctarget">juvenile</targetAudience>
13726 <xsl:when test="$controlField008-22='a'">
13727 <targetAudience authority="marctarget">preschool</targetAudience>
13729 <xsl:when test="$controlField008-22='f'">
13730 <targetAudience authority="marctarget">specialized</targetAudience>
13734 <xsl:for-each select="marc:datafield[@tag=245]/marc:subfield[@code='c']">
13735 <note type="statement of responsibility">
13736 <xsl:value-of select="."></xsl:value-of>
13739 <xsl:for-each select="marc:datafield[@tag=500]">
13741 <xsl:value-of select="marc:subfield[@code='a']"></xsl:value-of>
13742 <xsl:call-template name="uri"></xsl:call-template>
13746 <!--3.2 change tmee additional note fields-->
13748 <xsl:for-each select="marc:datafield[@tag=506]">
13749 <note type="restrictions">
13750 <xsl:call-template name="uri"></xsl:call-template>
13751 <xsl:variable name="str">
13752 <xsl:for-each select="marc:subfield[@code!='6' or @code!='8']">
13753 <xsl:value-of select="."></xsl:value-of>
13754 <xsl:text> </xsl:text>
13757 <xsl:value-of select="substring($str,1,string-length($str)-1)"></xsl:value-of>
13761 <xsl:for-each select="marc:datafield[@tag=510]">
13762 <note type="citation/reference">
13763 <xsl:call-template name="uri"></xsl:call-template>
13764 <xsl:variable name="str">
13765 <xsl:for-each select="marc:subfield[@code!='6' or @code!='8']">
13766 <xsl:value-of select="."></xsl:value-of>
13767 <xsl:text> </xsl:text>
13770 <xsl:value-of select="substring($str,1,string-length($str)-1)"></xsl:value-of>
13775 <xsl:for-each select="marc:datafield[@tag=511]">
13776 <note type="performers">
13777 <xsl:call-template name="uri"></xsl:call-template>
13778 <xsl:value-of select="marc:subfield[@code='a']"></xsl:value-of>
13781 <xsl:for-each select="marc:datafield[@tag=518]">
13782 <note type="venue">
13783 <xsl:call-template name="uri"></xsl:call-template>
13784 <xsl:value-of select="marc:subfield[@code='a']"></xsl:value-of>
13788 <xsl:for-each select="marc:datafield[@tag=530]">
13789 <note type="additional physical form">
13790 <xsl:call-template name="uri"></xsl:call-template>
13791 <xsl:variable name="str">
13792 <xsl:for-each select="marc:subfield[@code!='6' or @code!='8']">
13793 <xsl:value-of select="."></xsl:value-of>
13794 <xsl:text> </xsl:text>
13797 <xsl:value-of select="substring($str,1,string-length($str)-1)"></xsl:value-of>
13801 <xsl:for-each select="marc:datafield[@tag=533]">
13802 <note type="reproduction">
13803 <xsl:call-template name="uri"></xsl:call-template>
13804 <xsl:variable name="str">
13805 <xsl:for-each select="marc:subfield[@code!='6' or @code!='8']">
13806 <xsl:value-of select="."></xsl:value-of>
13807 <xsl:text> </xsl:text>
13810 <xsl:value-of select="substring($str,1,string-length($str)-1)"></xsl:value-of>
13814 <xsl:for-each select="marc:datafield[@tag=534]">
13815 <note type="original version">
13816 <xsl:call-template name="uri"></xsl:call-template>
13817 <xsl:variable name="str">
13818 <xsl:for-each select="marc:subfield[@code!='6' or @code!='8']">
13819 <xsl:value-of select="."></xsl:value-of>
13820 <xsl:text> </xsl:text>
13823 <xsl:value-of select="substring($str,1,string-length($str)-1)"></xsl:value-of>
13827 <xsl:for-each select="marc:datafield[@tag=538]">
13828 <note type="system details">
13829 <xsl:call-template name="uri"></xsl:call-template>
13830 <xsl:variable name="str">
13831 <xsl:for-each select="marc:subfield[@code!='6' or @code!='8']">
13832 <xsl:value-of select="."></xsl:value-of>
13833 <xsl:text> </xsl:text>
13836 <xsl:value-of select="substring($str,1,string-length($str)-1)"></xsl:value-of>
13840 <xsl:for-each select="marc:datafield[@tag=583]">
13841 <note type="action">
13842 <xsl:call-template name="uri"></xsl:call-template>
13843 <xsl:variable name="str">
13844 <xsl:for-each select="marc:subfield[@code!='6' or @code!='8']">
13845 <xsl:value-of select="."></xsl:value-of>
13846 <xsl:text> </xsl:text>
13849 <xsl:value-of select="substring($str,1,string-length($str)-1)"></xsl:value-of>
13857 <xsl:for-each select="marc:datafield[@tag=501 or @tag=502 or @tag=504 or @tag=507 or @tag=508 or @tag=513 or @tag=514 or @tag=515 or @tag=516 or @tag=522 or @tag=524 or @tag=525 or @tag=526 or @tag=535 or @tag=536 or @tag=540 or @tag=541 or @tag=544 or @tag=545 or @tag=546 or @tag=547 or @tag=550 or @tag=552 or @tag=555 or @tag=556 or @tag=561 or @tag=562 or @tag=565 or @tag=567 or @tag=580 or @tag=581 or @tag=584 or @tag=585 or @tag=586]">
13859 <xsl:call-template name="uri"></xsl:call-template>
13860 <xsl:variable name="str">
13861 <xsl:for-each select="marc:subfield[@code!='6' or @code!='8']">
13862 <xsl:value-of select="."></xsl:value-of>
13863 <xsl:text> </xsl:text>
13866 <xsl:value-of select="substring($str,1,string-length($str)-1)"></xsl:value-of>
13869 <xsl:for-each select="marc:datafield[@tag=034][marc:subfield[@code='d' or @code='e' or @code='f' or @code='g']]">
13873 <xsl:call-template name="subfieldSelect">
13874 <xsl:with-param name="codes">defg</xsl:with-param>
13875 </xsl:call-template>
13880 <xsl:for-each select="marc:datafield[@tag=043]">
13882 <xsl:for-each select="marc:subfield[@code='a' or @code='b' or @code='c']">
13884 <xsl:attribute name="authority">
13885 <xsl:if test="@code='a'">
13886 <xsl:text>marcgac</xsl:text>
13888 <xsl:if test="@code='b'">
13889 <xsl:value-of select="following-sibling::marc:subfield[@code=2]"></xsl:value-of>
13891 <xsl:if test="@code='c'">
13892 <xsl:text>iso3166</xsl:text>
13895 <xsl:value-of select="self::marc:subfield"></xsl:value-of>
13900 <!-- tmee 2006/11/27 -->
13901 <xsl:for-each select="marc:datafield[@tag=255]">
13903 <xsl:for-each select="marc:subfield[@code='a' or @code='b' or @code='c']">
13905 <xsl:if test="@code='a'">
13907 <xsl:value-of select="."></xsl:value-of>
13910 <xsl:if test="@code='b'">
13912 <xsl:value-of select="."></xsl:value-of>
13915 <xsl:if test="@code='c'">
13917 <xsl:value-of select="."></xsl:value-of>
13925 <xsl:apply-templates select="marc:datafield[653 >= @tag and @tag >= 600]"></xsl:apply-templates>
13926 <xsl:apply-templates select="marc:datafield[@tag=656]"></xsl:apply-templates>
13927 <xsl:for-each select="marc:datafield[@tag=752]">
13929 <hierarchicalGeographic>
13930 <xsl:for-each select="marc:subfield[@code='a']">
13932 <xsl:call-template name="chopPunctuation">
13933 <xsl:with-param name="chopString" select="."></xsl:with-param>
13934 </xsl:call-template>
13937 <xsl:for-each select="marc:subfield[@code='b']">
13939 <xsl:call-template name="chopPunctuation">
13940 <xsl:with-param name="chopString" select="."></xsl:with-param>
13941 </xsl:call-template>
13944 <xsl:for-each select="marc:subfield[@code='c']">
13946 <xsl:call-template name="chopPunctuation">
13947 <xsl:with-param name="chopString" select="."></xsl:with-param>
13948 </xsl:call-template>
13951 <xsl:for-each select="marc:subfield[@code='d']">
13953 <xsl:call-template name="chopPunctuation">
13954 <xsl:with-param name="chopString" select="."></xsl:with-param>
13955 </xsl:call-template>
13958 </hierarchicalGeographic>
13961 <xsl:for-each select="marc:datafield[@tag=045][marc:subfield[@code='b']]">
13964 <xsl:when test="@ind1=2">
13965 <temporal encoding="iso8601" point="start">
13966 <xsl:call-template name="chopPunctuation">
13967 <xsl:with-param name="chopString">
13968 <xsl:value-of select="marc:subfield[@code='b'][1]"></xsl:value-of>
13970 </xsl:call-template>
13972 <temporal encoding="iso8601" point="end">
13973 <xsl:call-template name="chopPunctuation">
13974 <xsl:with-param name="chopString">
13975 <xsl:value-of select="marc:subfield[@code='b'][2]"></xsl:value-of>
13977 </xsl:call-template>
13981 <xsl:for-each select="marc:subfield[@code='b']">
13982 <temporal encoding="iso8601">
13983 <xsl:call-template name="chopPunctuation">
13984 <xsl:with-param name="chopString" select="."></xsl:with-param>
13985 </xsl:call-template>
13992 <xsl:for-each select="marc:datafield[@tag=050]">
13993 <xsl:for-each select="marc:subfield[@code='b']">
13994 <classification authority="lcc">
13995 <xsl:if test="../marc:subfield[@code='3']">
13996 <xsl:attribute name="displayLabel">
13997 <xsl:value-of select="../marc:subfield[@code='3']"></xsl:value-of>
14000 <xsl:value-of select="preceding-sibling::marc:subfield[@code='a'][1]"></xsl:value-of>
14001 <xsl:text> </xsl:text>
14002 <xsl:value-of select="text()"></xsl:value-of>
14005 <xsl:for-each select="marc:subfield[@code='a'][not(following-sibling::marc:subfield[@code='b'])]">
14006 <classification authority="lcc">
14007 <xsl:if test="../marc:subfield[@code='3']">
14008 <xsl:attribute name="displayLabel">
14009 <xsl:value-of select="../marc:subfield[@code='3']"></xsl:value-of>
14012 <xsl:value-of select="text()"></xsl:value-of>
14016 <xsl:for-each select="marc:datafield[@tag=082]">
14017 <classification authority="ddc">
14018 <xsl:if test="marc:subfield[@code='2']">
14019 <xsl:attribute name="edition">
14020 <xsl:value-of select="marc:subfield[@code='2']"></xsl:value-of>
14023 <xsl:call-template name="subfieldSelect">
14024 <xsl:with-param name="codes">ab</xsl:with-param>
14025 </xsl:call-template>
14028 <xsl:for-each select="marc:datafield[@tag=080]">
14029 <classification authority="udc">
14030 <xsl:call-template name="subfieldSelect">
14031 <xsl:with-param name="codes">abx</xsl:with-param>
14032 </xsl:call-template>
14035 <xsl:for-each select="marc:datafield[@tag=060]">
14036 <classification authority="nlm">
14037 <xsl:call-template name="subfieldSelect">
14038 <xsl:with-param name="codes">ab</xsl:with-param>
14039 </xsl:call-template>
14042 <xsl:for-each select="marc:datafield[@tag=086][@ind1=0]">
14043 <classification authority="sudocs">
14044 <xsl:value-of select="marc:subfield[@code='a']"></xsl:value-of>
14047 <xsl:for-each select="marc:datafield[@tag=086][@ind1=1]">
14048 <classification authority="candoc">
14049 <xsl:value-of select="marc:subfield[@code='a']"></xsl:value-of>
14052 <xsl:for-each select="marc:datafield[@tag=086]">
14054 <xsl:attribute name="authority">
14055 <xsl:value-of select="marc:subfield[@code='2']"></xsl:value-of>
14057 <xsl:value-of select="marc:subfield[@code='a']"></xsl:value-of>
14060 <xsl:for-each select="marc:datafield[@tag=084]">
14062 <xsl:attribute name="authority">
14063 <xsl:value-of select="marc:subfield[@code='2']"></xsl:value-of>
14065 <xsl:call-template name="subfieldSelect">
14066 <xsl:with-param name="codes">ab</xsl:with-param>
14067 </xsl:call-template>
14070 <xsl:for-each select="marc:datafield[@tag=440]">
14071 <relatedItem type="series">
14074 <xsl:call-template name="chopPunctuation">
14075 <xsl:with-param name="chopString">
14076 <xsl:call-template name="subfieldSelect">
14077 <xsl:with-param name="codes">av</xsl:with-param>
14078 </xsl:call-template>
14080 </xsl:call-template>
14082 <xsl:call-template name="part"></xsl:call-template>
14086 <xsl:for-each select="marc:datafield[@tag=490][@ind1=0]">
14087 <relatedItem type="series">
14090 <xsl:call-template name="chopPunctuation">
14091 <xsl:with-param name="chopString">
14092 <xsl:call-template name="subfieldSelect">
14093 <xsl:with-param name="codes">av</xsl:with-param>
14094 </xsl:call-template>
14096 </xsl:call-template>
14098 <xsl:call-template name="part"></xsl:call-template>
14102 <xsl:for-each select="marc:datafield[@tag=510]">
14103 <relatedItem type="isReferencedBy">
14105 <xsl:call-template name="subfieldSelect">
14106 <xsl:with-param name="codes">abcx3</xsl:with-param>
14107 </xsl:call-template>
14111 <xsl:for-each select="marc:datafield[@tag=534]">
14112 <relatedItem type="original">
14113 <xsl:call-template name="relatedTitle"></xsl:call-template>
14114 <xsl:call-template name="relatedName"></xsl:call-template>
14115 <xsl:if test="marc:subfield[@code='b' or @code='c']">
14117 <xsl:for-each select="marc:subfield[@code='c']">
14119 <xsl:value-of select="."></xsl:value-of>
14122 <xsl:for-each select="marc:subfield[@code='b']">
14124 <xsl:value-of select="."></xsl:value-of>
14129 <xsl:call-template name="relatedIdentifierISSN"></xsl:call-template>
14130 <xsl:for-each select="marc:subfield[@code='z']">
14131 <identifier type="isbn">
14132 <xsl:value-of select="."></xsl:value-of>
14135 <xsl:call-template name="relatedNote"></xsl:call-template>
14138 <xsl:for-each select="marc:datafield[@tag=700][marc:subfield[@code='t']]">
14140 <xsl:call-template name="constituentOrRelatedType"></xsl:call-template>
14143 <xsl:call-template name="chopPunctuation">
14144 <xsl:with-param name="chopString">
14145 <xsl:call-template name="specialSubfieldSelect">
14146 <xsl:with-param name="anyCodes">tfklmorsv</xsl:with-param>
14147 <xsl:with-param name="axis">t</xsl:with-param>
14148 <xsl:with-param name="afterCodes">g</xsl:with-param>
14149 </xsl:call-template>
14151 </xsl:call-template>
14153 <xsl:call-template name="part"></xsl:call-template>
14155 <name type="personal">
14157 <xsl:call-template name="specialSubfieldSelect">
14158 <xsl:with-param name="anyCodes">aq</xsl:with-param>
14159 <xsl:with-param name="axis">t</xsl:with-param>
14160 <xsl:with-param name="beforeCodes">g</xsl:with-param>
14161 </xsl:call-template>
14163 <xsl:call-template name="termsOfAddress"></xsl:call-template>
14164 <xsl:call-template name="nameDate"></xsl:call-template>
14165 <xsl:call-template name="role"></xsl:call-template>
14167 <xsl:call-template name="relatedForm"></xsl:call-template>
14168 <xsl:call-template name="relatedIdentifierISSN"></xsl:call-template>
14171 <xsl:for-each select="marc:datafield[@tag=710][marc:subfield[@code='t']]">
14173 <xsl:call-template name="constituentOrRelatedType"></xsl:call-template>
14176 <xsl:call-template name="chopPunctuation">
14177 <xsl:with-param name="chopString">
14178 <xsl:call-template name="specialSubfieldSelect">
14179 <xsl:with-param name="anyCodes">tfklmorsv</xsl:with-param>
14180 <xsl:with-param name="axis">t</xsl:with-param>
14181 <xsl:with-param name="afterCodes">dg</xsl:with-param>
14182 </xsl:call-template>
14184 </xsl:call-template>
14186 <xsl:call-template name="relatedPartNumName"></xsl:call-template>
14188 <name type="corporate">
14189 <xsl:for-each select="marc:subfield[@code='a']">
14191 <xsl:value-of select="."></xsl:value-of>
14194 <xsl:for-each select="marc:subfield[@code='b']">
14196 <xsl:value-of select="."></xsl:value-of>
14199 <xsl:variable name="tempNamePart">
14200 <xsl:call-template name="specialSubfieldSelect">
14201 <xsl:with-param name="anyCodes">c</xsl:with-param>
14202 <xsl:with-param name="axis">t</xsl:with-param>
14203 <xsl:with-param name="beforeCodes">dgn</xsl:with-param>
14204 </xsl:call-template>
14206 <xsl:if test="normalize-space($tempNamePart)">
14208 <xsl:value-of select="$tempNamePart"></xsl:value-of>
14211 <xsl:call-template name="role"></xsl:call-template>
14213 <xsl:call-template name="relatedForm"></xsl:call-template>
14214 <xsl:call-template name="relatedIdentifierISSN"></xsl:call-template>
14217 <xsl:for-each select="marc:datafield[@tag=711][marc:subfield[@code='t']]">
14219 <xsl:call-template name="constituentOrRelatedType"></xsl:call-template>
14222 <xsl:call-template name="chopPunctuation">
14223 <xsl:with-param name="chopString">
14224 <xsl:call-template name="specialSubfieldSelect">
14225 <xsl:with-param name="anyCodes">tfklsv</xsl:with-param>
14226 <xsl:with-param name="axis">t</xsl:with-param>
14227 <xsl:with-param name="afterCodes">g</xsl:with-param>
14228 </xsl:call-template>
14230 </xsl:call-template>
14232 <xsl:call-template name="relatedPartNumName"></xsl:call-template>
14234 <name type="conference">
14236 <xsl:call-template name="specialSubfieldSelect">
14237 <xsl:with-param name="anyCodes">aqdc</xsl:with-param>
14238 <xsl:with-param name="axis">t</xsl:with-param>
14239 <xsl:with-param name="beforeCodes">gn</xsl:with-param>
14240 </xsl:call-template>
14243 <xsl:call-template name="relatedForm"></xsl:call-template>
14244 <xsl:call-template name="relatedIdentifierISSN"></xsl:call-template>
14247 <xsl:for-each select="marc:datafield[@tag=730][@ind2=2]">
14249 <xsl:call-template name="constituentOrRelatedType"></xsl:call-template>
14252 <xsl:call-template name="chopPunctuation">
14253 <xsl:with-param name="chopString">
14254 <xsl:call-template name="subfieldSelect">
14255 <xsl:with-param name="codes">adfgklmorsv</xsl:with-param>
14256 </xsl:call-template>
14258 </xsl:call-template>
14260 <xsl:call-template name="part"></xsl:call-template>
14262 <xsl:call-template name="relatedForm"></xsl:call-template>
14263 <xsl:call-template name="relatedIdentifierISSN"></xsl:call-template>
14266 <xsl:for-each select="marc:datafield[@tag=740][@ind2=2]">
14268 <xsl:call-template name="constituentOrRelatedType"></xsl:call-template>
14271 <xsl:call-template name="chopPunctuation">
14272 <xsl:with-param name="chopString">
14273 <xsl:value-of select="marc:subfield[@code='a']"></xsl:value-of>
14275 </xsl:call-template>
14277 <xsl:call-template name="part"></xsl:call-template>
14279 <xsl:call-template name="relatedForm"></xsl:call-template>
14282 <xsl:for-each select="marc:datafield[@tag=760]|marc:datafield[@tag=762]">
14283 <relatedItem type="series">
14284 <xsl:call-template name="relatedItem76X-78X"></xsl:call-template>
14287 <xsl:for-each select="marc:datafield[@tag=765]|marc:datafield[@tag=767]|marc:datafield[@tag=777]|marc:datafield[@tag=787]">
14289 <xsl:call-template name="relatedItem76X-78X"></xsl:call-template>
14292 <xsl:for-each select="marc:datafield[@tag=775]">
14293 <relatedItem type="otherVersion">
14294 <xsl:call-template name="relatedItem76X-78X"></xsl:call-template>
14297 <xsl:for-each select="marc:datafield[@tag=770]|marc:datafield[@tag=774]">
14298 <relatedItem type="constituent">
14299 <xsl:call-template name="relatedItem76X-78X"></xsl:call-template>
14302 <xsl:for-each select="marc:datafield[@tag=772]|marc:datafield[@tag=773]">
14303 <relatedItem type="host">
14304 <xsl:call-template name="relatedItem76X-78X"></xsl:call-template>
14307 <xsl:for-each select="marc:datafield[@tag=776]">
14308 <relatedItem type="otherFormat">
14309 <xsl:call-template name="relatedItem76X-78X"></xsl:call-template>
14312 <xsl:for-each select="marc:datafield[@tag=780]">
14313 <relatedItem type="preceding">
14314 <xsl:call-template name="relatedItem76X-78X"></xsl:call-template>
14317 <xsl:for-each select="marc:datafield[@tag=785]">
14318 <relatedItem type="succeeding">
14319 <xsl:call-template name="relatedItem76X-78X"></xsl:call-template>
14322 <xsl:for-each select="marc:datafield[@tag=786]">
14323 <relatedItem type="original">
14324 <xsl:call-template name="relatedItem76X-78X"></xsl:call-template>
14327 <xsl:for-each select="marc:datafield[@tag=800]">
14328 <relatedItem type="series">
14331 <xsl:call-template name="chopPunctuation">
14332 <xsl:with-param name="chopString">
14333 <xsl:call-template name="specialSubfieldSelect">
14334 <xsl:with-param name="anyCodes">tfklmorsv</xsl:with-param>
14335 <xsl:with-param name="axis">t</xsl:with-param>
14336 <xsl:with-param name="afterCodes">g</xsl:with-param>
14337 </xsl:call-template>
14339 </xsl:call-template>
14341 <xsl:call-template name="part"></xsl:call-template>
14343 <name type="personal">
14345 <xsl:call-template name="chopPunctuation">
14346 <xsl:with-param name="chopString">
14347 <xsl:call-template name="specialSubfieldSelect">
14348 <xsl:with-param name="anyCodes">aq</xsl:with-param>
14349 <xsl:with-param name="axis">t</xsl:with-param>
14350 <xsl:with-param name="beforeCodes">g</xsl:with-param>
14351 </xsl:call-template>
14353 </xsl:call-template>
14355 <xsl:call-template name="termsOfAddress"></xsl:call-template>
14356 <xsl:call-template name="nameDate"></xsl:call-template>
14357 <xsl:call-template name="role"></xsl:call-template>
14359 <xsl:call-template name="relatedForm"></xsl:call-template>
14362 <xsl:for-each select="marc:datafield[@tag=810]">
14363 <relatedItem type="series">
14366 <xsl:call-template name="chopPunctuation">
14367 <xsl:with-param name="chopString">
14368 <xsl:call-template name="specialSubfieldSelect">
14369 <xsl:with-param name="anyCodes">tfklmorsv</xsl:with-param>
14370 <xsl:with-param name="axis">t</xsl:with-param>
14371 <xsl:with-param name="afterCodes">dg</xsl:with-param>
14372 </xsl:call-template>
14374 </xsl:call-template>
14376 <xsl:call-template name="relatedPartNumName"></xsl:call-template>
14378 <name type="corporate">
14379 <xsl:for-each select="marc:subfield[@code='a']">
14381 <xsl:value-of select="."></xsl:value-of>
14384 <xsl:for-each select="marc:subfield[@code='b']">
14386 <xsl:value-of select="."></xsl:value-of>
14390 <xsl:call-template name="specialSubfieldSelect">
14391 <xsl:with-param name="anyCodes">c</xsl:with-param>
14392 <xsl:with-param name="axis">t</xsl:with-param>
14393 <xsl:with-param name="beforeCodes">dgn</xsl:with-param>
14394 </xsl:call-template>
14396 <xsl:call-template name="role"></xsl:call-template>
14398 <xsl:call-template name="relatedForm"></xsl:call-template>
14401 <xsl:for-each select="marc:datafield[@tag=811]">
14402 <relatedItem type="series">
14405 <xsl:call-template name="chopPunctuation">
14406 <xsl:with-param name="chopString">
14407 <xsl:call-template name="specialSubfieldSelect">
14408 <xsl:with-param name="anyCodes">tfklsv</xsl:with-param>
14409 <xsl:with-param name="axis">t</xsl:with-param>
14410 <xsl:with-param name="afterCodes">g</xsl:with-param>
14411 </xsl:call-template>
14413 </xsl:call-template>
14415 <xsl:call-template name="relatedPartNumName"/>
14417 <name type="conference">
14419 <xsl:call-template name="specialSubfieldSelect">
14420 <xsl:with-param name="anyCodes">aqdc</xsl:with-param>
14421 <xsl:with-param name="axis">t</xsl:with-param>
14422 <xsl:with-param name="beforeCodes">gn</xsl:with-param>
14423 </xsl:call-template>
14425 <xsl:call-template name="role"/>
14427 <xsl:call-template name="relatedForm"/>
14430 <xsl:for-each select="marc:datafield[@tag='830']">
14431 <relatedItem type="series">
14434 <xsl:call-template name="chopPunctuation">
14435 <xsl:with-param name="chopString">
14436 <xsl:call-template name="subfieldSelect">
14437 <xsl:with-param name="codes">adfgklmorsv</xsl:with-param>
14438 </xsl:call-template>
14440 </xsl:call-template>
14442 <xsl:call-template name="part"/>
14444 <xsl:call-template name="relatedForm"/>
14447 <xsl:for-each select="marc:datafield[@tag='856'][@ind2='2']/marc:subfield[@code='q']">
14449 <internetMediaType>
14450 <xsl:value-of select="."/>
14451 </internetMediaType>
14454 <xsl:for-each select="marc:datafield[@tag='020']">
14455 <xsl:call-template name="isInvalid">
14456 <xsl:with-param name="type">isbn</xsl:with-param>
14457 </xsl:call-template>
14458 <xsl:if test="marc:subfield[@code='a']">
14459 <identifier type="isbn">
14460 <xsl:value-of select="marc:subfield[@code='a']"/>
14464 <xsl:for-each select="marc:datafield[@tag='024'][@ind1='0']">
14465 <xsl:call-template name="isInvalid">
14466 <xsl:with-param name="type">isrc</xsl:with-param>
14467 </xsl:call-template>
14468 <xsl:if test="marc:subfield[@code='a']">
14469 <identifier type="isrc">
14470 <xsl:value-of select="marc:subfield[@code='a']"/>
14474 <xsl:for-each select="marc:datafield[@tag='024'][@ind1='2']">
14475 <xsl:call-template name="isInvalid">
14476 <xsl:with-param name="type">ismn</xsl:with-param>
14477 </xsl:call-template>
14478 <xsl:if test="marc:subfield[@code='a']">
14479 <identifier type="ismn">
14480 <xsl:value-of select="marc:subfield[@code='a']"/>
14484 <xsl:for-each select="marc:datafield[@tag='024'][@ind1='4']">
14485 <xsl:call-template name="isInvalid">
14486 <xsl:with-param name="type">sici</xsl:with-param>
14487 </xsl:call-template>
14488 <identifier type="sici">
14489 <xsl:call-template name="subfieldSelect">
14490 <xsl:with-param name="codes">ab</xsl:with-param>
14491 </xsl:call-template>
14494 <xsl:for-each select="marc:datafield[@tag='022']">
14495 <xsl:call-template name="isInvalid">
14496 <xsl:with-param name="type">issn</xsl:with-param>
14497 </xsl:call-template>
14498 <identifier type="issn">
14499 <xsl:value-of select="marc:subfield[@code='a']"/>
14502 <xsl:for-each select="marc:datafield[@tag='010']">
14503 <xsl:call-template name="isInvalid">
14504 <xsl:with-param name="type">lccn</xsl:with-param>
14505 </xsl:call-template>
14506 <identifier type="lccn">
14507 <xsl:value-of select="normalize-space(marc:subfield[@code='a'])"/>
14510 <xsl:for-each select="marc:datafield[@tag='028']">
14512 <xsl:attribute name="type">
14514 <xsl:when test="@ind1='0'">issue number</xsl:when>
14515 <xsl:when test="@ind1='1'">matrix number</xsl:when>
14516 <xsl:when test="@ind1='2'">music plate</xsl:when>
14517 <xsl:when test="@ind1='3'">music publisher</xsl:when>
14518 <xsl:when test="@ind1='4'">videorecording identifier</xsl:when>
14521 <!--<xsl:call-template name="isInvalid"/>--> <!-- no $z in 028 -->
14522 <xsl:call-template name="subfieldSelect">
14523 <xsl:with-param name="codes">
14525 <xsl:when test="@ind1='0'">ba</xsl:when>
14526 <xsl:otherwise>ab</xsl:otherwise>
14529 </xsl:call-template>
14532 <xsl:for-each select="marc:datafield[@tag='037']">
14533 <identifier type="stock number">
14534 <!--<xsl:call-template name="isInvalid"/>--> <!-- no $z in 037 -->
14535 <xsl:call-template name="subfieldSelect">
14536 <xsl:with-param name="codes">ab</xsl:with-param>
14537 </xsl:call-template>
14540 <xsl:for-each select="marc:datafield[@tag='856'][marc:subfield[@code='u']]">
14542 <xsl:attribute name="type">
14544 <xsl:when test="starts-with(marc:subfield[@code='u'],'urn:doi') or starts-with(marc:subfield[@code='u'],'doi')">doi</xsl:when>
14545 <xsl:when test="starts-with(marc:subfield[@code='u'],'urn:hdl') or starts-with(marc:subfield[@code='u'],'hdl') or starts-with(marc:subfield[@code='u'],'http://hdl.loc.gov')">hdl</xsl:when>
14546 <xsl:otherwise>uri</xsl:otherwise>
14550 <xsl:when test="starts-with(marc:subfield[@code='u'],'urn:hdl') or starts-with(marc:subfield[@code='u'],'hdl') or starts-with(marc:subfield[@code='u'],'http://hdl.loc.gov') ">
14551 <xsl:value-of select="concat('hdl:',substring-after(marc:subfield[@code='u'],'http://hdl.loc.gov/'))"></xsl:value-of>
14554 <xsl:value-of select="marc:subfield[@code='u']"></xsl:value-of>
14558 <xsl:if test="starts-with(marc:subfield[@code='u'],'urn:hdl') or starts-with(marc:subfield[@code='u'],'hdl')">
14559 <identifier type="hdl">
14560 <xsl:if test="marc:subfield[@code='y' or @code='3' or @code='z']">
14561 <xsl:attribute name="displayLabel">
14562 <xsl:call-template name="subfieldSelect">
14563 <xsl:with-param name="codes">y3z</xsl:with-param>
14564 </xsl:call-template>
14567 <xsl:value-of select="concat('hdl:',substring-after(marc:subfield[@code='u'],'http://hdl.loc.gov/'))"></xsl:value-of>
14571 <xsl:for-each select="marc:datafield[@tag=024][@ind1=1]">
14572 <identifier type="upc">
14573 <xsl:call-template name="isInvalid"/>
14574 <xsl:value-of select="marc:subfield[@code='a']"/>
14577 <!-- 1/04 fix added $y -->
14578 <xsl:for-each select="marc:datafield[@tag=856][marc:subfield[@code='u']]">
14581 <xsl:if test="marc:subfield[@code='y' or @code='3']">
14582 <xsl:attribute name="displayLabel">
14583 <xsl:call-template name="subfieldSelect">
14584 <xsl:with-param name="codes">y3</xsl:with-param>
14585 </xsl:call-template>
14588 <xsl:if test="marc:subfield[@code='z' ]">
14589 <xsl:attribute name="note">
14590 <xsl:call-template name="subfieldSelect">
14591 <xsl:with-param name="codes">z</xsl:with-param>
14592 </xsl:call-template>
14595 <xsl:value-of select="marc:subfield[@code='u']"></xsl:value-of>
14601 <!-- 3.2 change tmee 856z -->
14604 <xsl:for-each select="marc:datafield[@tag=852]">
14607 <xsl:call-template name="displayLabel"></xsl:call-template>
14608 <xsl:call-template name="subfieldSelect">
14609 <xsl:with-param name="codes">abje</xsl:with-param>
14610 </xsl:call-template>
14611 </physicalLocation>
14614 <xsl:for-each select="marc:datafield[@tag=506]">
14615 <accessCondition type="restrictionOnAccess">
14616 <xsl:call-template name="subfieldSelect">
14617 <xsl:with-param name="codes">abcd35</xsl:with-param>
14618 </xsl:call-template>
14621 <xsl:for-each select="marc:datafield[@tag=540]">
14622 <accessCondition type="useAndReproduction">
14623 <xsl:call-template name="subfieldSelect">
14624 <xsl:with-param name="codes">abcde35</xsl:with-param>
14625 </xsl:call-template>
14629 <xsl:for-each select="marc:datafield[@tag=040]">
14630 <recordContentSource authority="marcorg">
14631 <xsl:value-of select="marc:subfield[@code='a']"></xsl:value-of>
14632 </recordContentSource>
14634 <xsl:for-each select="marc:controlfield[@tag=008]">
14635 <recordCreationDate encoding="marc">
14636 <xsl:value-of select="substring(.,1,6)"></xsl:value-of>
14637 </recordCreationDate>
14639 <xsl:for-each select="marc:controlfield[@tag=005]">
14640 <recordChangeDate encoding="iso8601">
14641 <xsl:value-of select="."></xsl:value-of>
14642 </recordChangeDate>
14644 <xsl:for-each select="marc:controlfield[@tag=001]">
14646 <xsl:if test="../marc:controlfield[@tag=003]">
14647 <xsl:attribute name="source">
14648 <xsl:value-of select="../marc:controlfield[@tag=003]"></xsl:value-of>
14651 <xsl:value-of select="."></xsl:value-of>
14652 </recordIdentifier>
14654 <xsl:for-each select="marc:datafield[@tag=040]/marc:subfield[@code='b']">
14655 <languageOfCataloging>
14656 <languageTerm authority="iso639-2b" type="code">
14657 <xsl:value-of select="."></xsl:value-of>
14659 </languageOfCataloging>
14663 <xsl:template name="displayForm">
14664 <xsl:for-each select="marc:subfield[@code='c']">
14666 <xsl:value-of select="."></xsl:value-of>
14670 <xsl:template name="affiliation">
14671 <xsl:for-each select="marc:subfield[@code='u']">
14673 <xsl:value-of select="."></xsl:value-of>
14677 <xsl:template name="uri">
14678 <xsl:for-each select="marc:subfield[@code='u']">
14679 <xsl:attribute name="xlink:href">
14680 <xsl:value-of select="."></xsl:value-of>
14684 <xsl:template name="role">
14685 <xsl:for-each select="marc:subfield[@code='e']">
14687 <roleTerm type="text">
14688 <xsl:value-of select="."></xsl:value-of>
14692 <xsl:for-each select="marc:subfield[@code='4']">
14694 <roleTerm authority="marcrelator" type="code">
14695 <xsl:value-of select="."></xsl:value-of>
14700 <xsl:template name="part">
14701 <xsl:variable name="partNumber">
14702 <xsl:call-template name="specialSubfieldSelect">
14703 <xsl:with-param name="axis">n</xsl:with-param>
14704 <xsl:with-param name="anyCodes">n</xsl:with-param>
14705 <xsl:with-param name="afterCodes">fgkdlmor</xsl:with-param>
14706 </xsl:call-template>
14708 <xsl:variable name="partName">
14709 <xsl:call-template name="specialSubfieldSelect">
14710 <xsl:with-param name="axis">p</xsl:with-param>
14711 <xsl:with-param name="anyCodes">p</xsl:with-param>
14712 <xsl:with-param name="afterCodes">fgkdlmor</xsl:with-param>
14713 </xsl:call-template>
14715 <xsl:if test="string-length(normalize-space($partNumber))">
14717 <xsl:call-template name="chopPunctuation">
14718 <xsl:with-param name="chopString" select="$partNumber"></xsl:with-param>
14719 </xsl:call-template>
14722 <xsl:if test="string-length(normalize-space($partName))">
14724 <xsl:call-template name="chopPunctuation">
14725 <xsl:with-param name="chopString" select="$partName"></xsl:with-param>
14726 </xsl:call-template>
14730 <xsl:template name="relatedPart">
14731 <xsl:if test="@tag=773">
14732 <xsl:for-each select="marc:subfield[@code='g']">
14735 <xsl:value-of select="."></xsl:value-of>
14739 <xsl:for-each select="marc:subfield[@code='q']">
14741 <xsl:call-template name="parsePart"></xsl:call-template>
14746 <xsl:template name="relatedPartNumName">
14747 <xsl:variable name="partNumber">
14748 <xsl:call-template name="specialSubfieldSelect">
14749 <xsl:with-param name="axis">g</xsl:with-param>
14750 <xsl:with-param name="anyCodes">g</xsl:with-param>
14751 <xsl:with-param name="afterCodes">pst</xsl:with-param>
14752 </xsl:call-template>
14754 <xsl:variable name="partName">
14755 <xsl:call-template name="specialSubfieldSelect">
14756 <xsl:with-param name="axis">p</xsl:with-param>
14757 <xsl:with-param name="anyCodes">p</xsl:with-param>
14758 <xsl:with-param name="afterCodes">fgkdlmor</xsl:with-param>
14759 </xsl:call-template>
14761 <xsl:if test="string-length(normalize-space($partNumber))">
14763 <xsl:value-of select="$partNumber"></xsl:value-of>
14766 <xsl:if test="string-length(normalize-space($partName))">
14768 <xsl:value-of select="$partName"></xsl:value-of>
14772 <xsl:template name="relatedName">
14773 <xsl:for-each select="marc:subfield[@code='a']">
14776 <xsl:value-of select="."></xsl:value-of>
14781 <xsl:template name="relatedForm">
14782 <xsl:for-each select="marc:subfield[@code='h']">
14783 <physicalDescription>
14785 <xsl:value-of select="."></xsl:value-of>
14787 </physicalDescription>
14790 <xsl:template name="relatedExtent">
14791 <xsl:for-each select="marc:subfield[@code='h']">
14792 <physicalDescription>
14794 <xsl:value-of select="."></xsl:value-of>
14796 </physicalDescription>
14799 <xsl:template name="relatedNote">
14800 <xsl:for-each select="marc:subfield[@code='n']">
14802 <xsl:value-of select="."></xsl:value-of>
14806 <xsl:template name="relatedSubject">
14807 <xsl:for-each select="marc:subfield[@code='j']">
14809 <temporal encoding="iso8601">
14810 <xsl:call-template name="chopPunctuation">
14811 <xsl:with-param name="chopString" select="."></xsl:with-param>
14812 </xsl:call-template>
14817 <xsl:template name="relatedIdentifierISSN">
14818 <xsl:for-each select="marc:subfield[@code='x']">
14819 <identifier type="issn">
14820 <xsl:value-of select="."></xsl:value-of>
14824 <xsl:template name="relatedIdentifierLocal">
14825 <xsl:for-each select="marc:subfield[@code='w']">
14826 <identifier type="local">
14827 <xsl:value-of select="."></xsl:value-of>
14831 <xsl:template name="relatedIdentifier">
14832 <xsl:for-each select="marc:subfield[@code='o']">
14834 <xsl:value-of select="."></xsl:value-of>
14838 <xsl:template name="relatedItem76X-78X">
14839 <xsl:call-template name="displayLabel"></xsl:call-template>
14840 <xsl:call-template name="relatedTitle76X-78X"></xsl:call-template>
14841 <xsl:call-template name="relatedName"></xsl:call-template>
14842 <xsl:call-template name="relatedOriginInfo"></xsl:call-template>
14843 <xsl:call-template name="relatedLanguage"></xsl:call-template>
14844 <xsl:call-template name="relatedExtent"></xsl:call-template>
14845 <xsl:call-template name="relatedNote"></xsl:call-template>
14846 <xsl:call-template name="relatedSubject"></xsl:call-template>
14847 <xsl:call-template name="relatedIdentifier"></xsl:call-template>
14848 <xsl:call-template name="relatedIdentifierISSN"></xsl:call-template>
14849 <xsl:call-template name="relatedIdentifierLocal"></xsl:call-template>
14850 <xsl:call-template name="relatedPart"></xsl:call-template>
14852 <xsl:template name="subjectGeographicZ">
14854 <xsl:call-template name="chopPunctuation">
14855 <xsl:with-param name="chopString" select="."></xsl:with-param>
14856 </xsl:call-template>
14859 <xsl:template name="subjectTemporalY">
14861 <xsl:call-template name="chopPunctuation">
14862 <xsl:with-param name="chopString" select="."></xsl:with-param>
14863 </xsl:call-template>
14866 <xsl:template name="subjectTopic">
14868 <xsl:call-template name="chopPunctuation">
14869 <xsl:with-param name="chopString" select="."></xsl:with-param>
14870 </xsl:call-template>
14873 <!-- 3.2 change tmee 6xx $v genre -->
14874 <xsl:template name="subjectGenre">
14876 <xsl:call-template name="chopPunctuation">
14877 <xsl:with-param name="chopString" select="."></xsl:with-param>
14878 </xsl:call-template>
14882 <xsl:template name="nameABCDN">
14883 <xsl:for-each select="marc:subfield[@code='a']">
14885 <xsl:call-template name="chopPunctuation">
14886 <xsl:with-param name="chopString" select="."></xsl:with-param>
14887 </xsl:call-template>
14890 <xsl:for-each select="marc:subfield[@code='b']">
14892 <xsl:value-of select="."></xsl:value-of>
14895 <xsl:if test="marc:subfield[@code='c'] or marc:subfield[@code='d'] or marc:subfield[@code='n']">
14897 <xsl:call-template name="subfieldSelect">
14898 <xsl:with-param name="codes">cdn</xsl:with-param>
14899 </xsl:call-template>
14903 <xsl:template name="nameABCDQ">
14905 <xsl:call-template name="chopPunctuation">
14906 <xsl:with-param name="chopString">
14907 <xsl:call-template name="subfieldSelect">
14908 <xsl:with-param name="codes">aq</xsl:with-param>
14909 </xsl:call-template>
14911 <xsl:with-param name="punctuation">
14912 <xsl:text>:,;/ </xsl:text>
14914 </xsl:call-template>
14916 <xsl:call-template name="termsOfAddress"></xsl:call-template>
14917 <xsl:call-template name="nameDate"></xsl:call-template>
14919 <xsl:template name="nameACDEQ">
14921 <xsl:call-template name="subfieldSelect">
14922 <xsl:with-param name="codes">acdeq</xsl:with-param>
14923 </xsl:call-template>
14926 <xsl:template name="constituentOrRelatedType">
14927 <xsl:if test="@ind2=2">
14928 <xsl:attribute name="type">constituent</xsl:attribute>
14931 <xsl:template name="relatedTitle">
14932 <xsl:for-each select="marc:subfield[@code='t']">
14935 <xsl:call-template name="chopPunctuation">
14936 <xsl:with-param name="chopString">
14937 <xsl:value-of select="."></xsl:value-of>
14939 </xsl:call-template>
14944 <xsl:template name="relatedTitle76X-78X">
14945 <xsl:for-each select="marc:subfield[@code='t']">
14948 <xsl:call-template name="chopPunctuation">
14949 <xsl:with-param name="chopString">
14950 <xsl:value-of select="."></xsl:value-of>
14952 </xsl:call-template>
14954 <xsl:if test="marc:datafield[@tag!=773]and marc:subfield[@code='g']">
14955 <xsl:call-template name="relatedPartNumName"></xsl:call-template>
14959 <xsl:for-each select="marc:subfield[@code='p']">
14960 <titleInfo type="abbreviated">
14962 <xsl:call-template name="chopPunctuation">
14963 <xsl:with-param name="chopString">
14964 <xsl:value-of select="."></xsl:value-of>
14966 </xsl:call-template>
14968 <xsl:if test="marc:datafield[@tag!=773]and marc:subfield[@code='g']">
14969 <xsl:call-template name="relatedPartNumName"></xsl:call-template>
14973 <xsl:for-each select="marc:subfield[@code='s']">
14974 <titleInfo type="uniform">
14976 <xsl:call-template name="chopPunctuation">
14977 <xsl:with-param name="chopString">
14978 <xsl:value-of select="."></xsl:value-of>
14980 </xsl:call-template>
14982 <xsl:if test="marc:datafield[@tag!=773]and marc:subfield[@code='g']">
14983 <xsl:call-template name="relatedPartNumName"></xsl:call-template>
14988 <xsl:template name="relatedOriginInfo">
14989 <xsl:if test="marc:subfield[@code='b' or @code='d'] or marc:subfield[@code='f']">
14991 <xsl:if test="@tag=775">
14992 <xsl:for-each select="marc:subfield[@code='f']">
14995 <xsl:attribute name="type">code</xsl:attribute>
14996 <xsl:attribute name="authority">marcgac</xsl:attribute>
14997 <xsl:value-of select="."></xsl:value-of>
15002 <xsl:for-each select="marc:subfield[@code='d']">
15004 <xsl:value-of select="."></xsl:value-of>
15007 <xsl:for-each select="marc:subfield[@code='b']">
15009 <xsl:value-of select="."></xsl:value-of>
15015 <xsl:template name="relatedLanguage">
15016 <xsl:for-each select="marc:subfield[@code='e']">
15017 <xsl:call-template name="getLanguage">
15018 <xsl:with-param name="langString">
15019 <xsl:value-of select="."></xsl:value-of>
15021 </xsl:call-template>
15024 <xsl:template name="nameDate">
15025 <xsl:for-each select="marc:subfield[@code='d']">
15026 <namePart type="date">
15027 <xsl:call-template name="chopPunctuation">
15028 <xsl:with-param name="chopString" select="."></xsl:with-param>
15029 </xsl:call-template>
15033 <xsl:template name="subjectAuthority">
15034 <xsl:if test="@ind2!=4">
15035 <xsl:if test="@ind2!=' '">
15036 <xsl:if test="@ind2!=8">
15037 <xsl:if test="@ind2!=9">
15038 <xsl:attribute name="authority">
15040 <xsl:when test="@ind2=0">lcsh</xsl:when>
15041 <xsl:when test="@ind2=1">lcshac</xsl:when>
15042 <xsl:when test="@ind2=2">mesh</xsl:when>
15044 <xsl:when test="@ind2=3">nal</xsl:when>
15045 <xsl:when test="@ind2=5">csh</xsl:when>
15046 <xsl:when test="@ind2=6">rvm</xsl:when>
15047 <xsl:when test="@ind2=7">
15048 <xsl:value-of select="marc:subfield[@code='2']"></xsl:value-of>
15057 <xsl:template name="subjectAnyOrder">
15058 <xsl:for-each select="marc:subfield[@code='v' or @code='x' or @code='y' or @code='z']">
15060 <xsl:when test="@code='v'">
15061 <xsl:call-template name="subjectGenre"></xsl:call-template>
15063 <xsl:when test="@code='x'">
15064 <xsl:call-template name="subjectTopic"></xsl:call-template>
15066 <xsl:when test="@code='y'">
15067 <xsl:call-template name="subjectTemporalY"></xsl:call-template>
15069 <xsl:when test="@code='z'">
15070 <xsl:call-template name="subjectGeographicZ"></xsl:call-template>
15075 <xsl:template name="specialSubfieldSelect">
15076 <xsl:param name="anyCodes"></xsl:param>
15077 <xsl:param name="axis"></xsl:param>
15078 <xsl:param name="beforeCodes"></xsl:param>
15079 <xsl:param name="afterCodes"></xsl:param>
15080 <xsl:variable name="str">
15081 <xsl:for-each select="marc:subfield">
15082 <xsl:if test="contains($anyCodes, @code) or (contains($beforeCodes,@code) and following-sibling::marc:subfield[@code=$axis]) or (contains($afterCodes,@code) and preceding-sibling::marc:subfield[@code=$axis])">
15083 <xsl:value-of select="text()"></xsl:value-of>
15084 <xsl:text> </xsl:text>
15088 <xsl:value-of select="substring($str,1,string-length($str)-1)"></xsl:value-of>
15091 <!-- 3.2 change tmee 6xx $v genre -->
15092 <xsl:template match="marc:datafield[@tag=600]">
15094 <xsl:call-template name="subjectAuthority"></xsl:call-template>
15095 <name type="personal">
15096 <xsl:call-template name="termsOfAddress"></xsl:call-template>
15098 <xsl:call-template name="chopPunctuation">
15099 <xsl:with-param name="chopString">
15100 <xsl:call-template name="subfieldSelect">
15101 <xsl:with-param name="codes">aq</xsl:with-param>
15102 </xsl:call-template>
15104 </xsl:call-template>
15106 <xsl:call-template name="nameDate"></xsl:call-template>
15107 <xsl:call-template name="affiliation"></xsl:call-template>
15108 <xsl:call-template name="role"></xsl:call-template>
15110 <xsl:call-template name="subjectAnyOrder"></xsl:call-template>
15113 <xsl:template match="marc:datafield[@tag=610]">
15115 <xsl:call-template name="subjectAuthority"></xsl:call-template>
15116 <name type="corporate">
15117 <xsl:for-each select="marc:subfield[@code='a']">
15119 <xsl:value-of select="."></xsl:value-of>
15122 <xsl:for-each select="marc:subfield[@code='b']">
15124 <xsl:value-of select="."></xsl:value-of>
15127 <xsl:if test="marc:subfield[@code='c' or @code='d' or @code='n' or @code='p']">
15129 <xsl:call-template name="subfieldSelect">
15130 <xsl:with-param name="codes">cdnp</xsl:with-param>
15131 </xsl:call-template>
15134 <xsl:call-template name="role"></xsl:call-template>
15136 <xsl:call-template name="subjectAnyOrder"></xsl:call-template>
15139 <xsl:template match="marc:datafield[@tag=611]">
15141 <xsl:call-template name="subjectAuthority"></xsl:call-template>
15142 <name type="conference">
15144 <xsl:call-template name="subfieldSelect">
15145 <xsl:with-param name="codes">abcdeqnp</xsl:with-param>
15146 </xsl:call-template>
15148 <xsl:for-each select="marc:subfield[@code='4']">
15150 <roleTerm authority="marcrelator" type="code">
15151 <xsl:value-of select="."></xsl:value-of>
15156 <xsl:call-template name="subjectAnyOrder"></xsl:call-template>
15159 <xsl:template match="marc:datafield[@tag=630]">
15161 <xsl:call-template name="subjectAuthority"></xsl:call-template>
15164 <xsl:call-template name="chopPunctuation">
15165 <xsl:with-param name="chopString">
15166 <xsl:call-template name="subfieldSelect">
15167 <xsl:with-param name="codes">adfhklor</xsl:with-param>
15168 </xsl:call-template>
15170 </xsl:call-template>
15171 <xsl:call-template name="part"></xsl:call-template>
15174 <xsl:call-template name="subjectAnyOrder"></xsl:call-template>
15177 <xsl:template match="marc:datafield[@tag=650]">
15179 <xsl:call-template name="subjectAuthority"></xsl:call-template>
15181 <xsl:call-template name="chopPunctuation">
15182 <xsl:with-param name="chopString">
15183 <xsl:call-template name="subfieldSelect">
15184 <xsl:with-param name="codes">abcd</xsl:with-param>
15185 </xsl:call-template>
15187 </xsl:call-template>
15189 <xsl:call-template name="subjectAnyOrder"></xsl:call-template>
15192 <xsl:template match="marc:datafield[@tag=651]">
15194 <xsl:call-template name="subjectAuthority"></xsl:call-template>
15195 <xsl:for-each select="marc:subfield[@code='a']">
15197 <xsl:call-template name="chopPunctuation">
15198 <xsl:with-param name="chopString" select="."></xsl:with-param>
15199 </xsl:call-template>
15202 <xsl:call-template name="subjectAnyOrder"></xsl:call-template>
15205 <xsl:template match="marc:datafield[@tag=653]">
15207 <xsl:for-each select="marc:subfield[@code='a']">
15209 <xsl:value-of select="."></xsl:value-of>
15214 <xsl:template match="marc:datafield[@tag=656]">
15216 <xsl:if test="marc:subfield[@code=2]">
15217 <xsl:attribute name="authority">
15218 <xsl:value-of select="marc:subfield[@code=2]"></xsl:value-of>
15222 <xsl:call-template name="chopPunctuation">
15223 <xsl:with-param name="chopString">
15224 <xsl:value-of select="marc:subfield[@code='a']"></xsl:value-of>
15226 </xsl:call-template>
15230 <xsl:template name="termsOfAddress">
15231 <xsl:if test="marc:subfield[@code='b' or @code='c']">
15232 <namePart type="termsOfAddress">
15233 <xsl:call-template name="chopPunctuation">
15234 <xsl:with-param name="chopString">
15235 <xsl:call-template name="subfieldSelect">
15236 <xsl:with-param name="codes">bc</xsl:with-param>
15237 </xsl:call-template>
15239 </xsl:call-template>
15243 <xsl:template name="displayLabel">
15244 <xsl:if test="marc:subfield[@code='i']">
15245 <xsl:attribute name="displayLabel">
15246 <xsl:value-of select="marc:subfield[@code='i']"></xsl:value-of>
15249 <xsl:if test="marc:subfield[@code='3']">
15250 <xsl:attribute name="displayLabel">
15251 <xsl:value-of select="marc:subfield[@code='3']"></xsl:value-of>
15255 <xsl:template name="isInvalid">
15256 <xsl:param name="type"/>
15257 <xsl:if test="marc:subfield[@code='z'] or marc:subfield[@code='y']">
15259 <xsl:attribute name="type">
15260 <xsl:value-of select="$type"/>
15262 <xsl:attribute name="invalid">
15263 <xsl:text>yes</xsl:text>
15265 <xsl:if test="marc:subfield[@code='z']">
15266 <xsl:value-of select="marc:subfield[@code='z']"/>
15268 <xsl:if test="marc:subfield[@code='y']">
15269 <xsl:value-of select="marc:subfield[@code='y']"/>
15274 <xsl:template name="subtitle">
15275 <xsl:if test="marc:subfield[@code='b']">
15277 <xsl:call-template name="chopPunctuation">
15278 <xsl:with-param name="chopString">
15279 <xsl:value-of select="marc:subfield[@code='b']"/>
15280 <!--<xsl:call-template name="subfieldSelect">
15281 <xsl:with-param name="codes">b</xsl:with-param>
15282 </xsl:call-template>-->
15284 </xsl:call-template>
15288 <xsl:template name="script">
15289 <xsl:param name="scriptCode"></xsl:param>
15290 <xsl:attribute name="script">
15292 <xsl:when test="$scriptCode='(3'">Arabic</xsl:when>
15293 <xsl:when test="$scriptCode='(B'">Latin</xsl:when>
15294 <xsl:when test="$scriptCode='$1'">Chinese, Japanese, Korean</xsl:when>
15295 <xsl:when test="$scriptCode='(N'">Cyrillic</xsl:when>
15296 <xsl:when test="$scriptCode='(2'">Hebrew</xsl:when>
15297 <xsl:when test="$scriptCode='(S'">Greek</xsl:when>
15301 <xsl:template name="parsePart">
15302 <!-- assumes 773$q= 1:2:3<4
15303 with up to 3 levels and one optional start page
15305 <xsl:variable name="level1">
15307 <xsl:when test="contains(text(),':')">
15309 <xsl:value-of select="substring-before(text(),':')"></xsl:value-of>
15311 <xsl:when test="not(contains(text(),':'))">
15313 <xsl:if test="contains(text(),'<')">
15315 <xsl:value-of select="substring-before(text(),'<')"></xsl:value-of>
15317 <xsl:if test="not(contains(text(),'<'))">
15319 <xsl:value-of select="text()"></xsl:value-of>
15324 <xsl:variable name="sici2">
15326 <xsl:when test="starts-with(substring-after(text(),$level1),':')">
15327 <xsl:value-of select="substring(substring-after(text(),$level1),2)"></xsl:value-of>
15330 <xsl:value-of select="substring-after(text(),$level1)"></xsl:value-of>
15334 <xsl:variable name="level2">
15336 <xsl:when test="contains($sici2,':')">
15338 <xsl:value-of select="substring-before($sici2,':')"></xsl:value-of>
15340 <xsl:when test="contains($sici2,'<')">
15342 <xsl:value-of select="substring-before($sici2,'<')"></xsl:value-of>
15345 <xsl:value-of select="$sici2"></xsl:value-of>
15350 <xsl:variable name="sici3">
15352 <xsl:when test="starts-with(substring-after($sici2,$level2),':')">
15353 <xsl:value-of select="substring(substring-after($sici2,$level2),2)"></xsl:value-of>
15356 <xsl:value-of select="substring-after($sici2,$level2)"></xsl:value-of>
15360 <xsl:variable name="level3">
15362 <xsl:when test="contains($sici3,'<')">
15364 <xsl:value-of select="substring-before($sici3,'<')"></xsl:value-of>
15367 <xsl:value-of select="$sici3"></xsl:value-of>
15372 <xsl:variable name="page">
15373 <xsl:if test="contains(text(),'<')">
15374 <xsl:value-of select="substring-after(text(),'<')"></xsl:value-of>
15377 <xsl:if test="$level1">
15380 <xsl:value-of select="$level1"></xsl:value-of>
15384 <xsl:if test="$level2">
15387 <xsl:value-of select="$level2"></xsl:value-of>
15391 <xsl:if test="$level3">
15394 <xsl:value-of select="$level3"></xsl:value-of>
15398 <xsl:if test="$page">
15399 <extent unit="page">
15401 <xsl:value-of select="$page"></xsl:value-of>
15406 <xsl:template name="getLanguage">
15407 <xsl:param name="langString"></xsl:param>
15408 <xsl:param name="controlField008-35-37"></xsl:param>
15409 <xsl:variable name="length" select="string-length($langString)"></xsl:variable>
15411 <xsl:when test="$length=0"></xsl:when>
15412 <xsl:when test="$controlField008-35-37=substring($langString,1,3)">
15413 <xsl:call-template name="getLanguage">
15414 <xsl:with-param name="langString" select="substring($langString,4,$length)"></xsl:with-param>
15415 <xsl:with-param name="controlField008-35-37" select="$controlField008-35-37"></xsl:with-param>
15416 </xsl:call-template>
15420 <languageTerm authority="iso639-2b" type="code">
15421 <xsl:value-of select="substring($langString,1,3)"></xsl:value-of>
15424 <xsl:call-template name="getLanguage">
15425 <xsl:with-param name="langString" select="substring($langString,4,$length)"></xsl:with-param>
15426 <xsl:with-param name="controlField008-35-37" select="$controlField008-35-37"></xsl:with-param>
15427 </xsl:call-template>
15431 <xsl:template name="isoLanguage">
15432 <xsl:param name="currentLanguage"></xsl:param>
15433 <xsl:param name="usedLanguages"></xsl:param>
15434 <xsl:param name="remainingLanguages"></xsl:param>
15436 <xsl:when test="string-length($currentLanguage)=0"></xsl:when>
15437 <xsl:when test="not(contains($usedLanguages, $currentLanguage))">
15439 <xsl:if test="@code!='a'">
15440 <xsl:attribute name="objectPart">
15442 <xsl:when test="@code='b'">summary or subtitle</xsl:when>
15443 <xsl:when test="@code='d'">sung or spoken text</xsl:when>
15444 <xsl:when test="@code='e'">libretto</xsl:when>
15445 <xsl:when test="@code='f'">table of contents</xsl:when>
15446 <xsl:when test="@code='g'">accompanying material</xsl:when>
15447 <xsl:when test="@code='h'">translation</xsl:when>
15451 <languageTerm authority="iso639-2b" type="code">
15452 <xsl:value-of select="$currentLanguage"></xsl:value-of>
15455 <xsl:call-template name="isoLanguage">
15456 <xsl:with-param name="currentLanguage">
15457 <xsl:value-of select="substring($remainingLanguages,1,3)"></xsl:value-of>
15459 <xsl:with-param name="usedLanguages">
15460 <xsl:value-of select="concat($usedLanguages,$currentLanguage)"></xsl:value-of>
15462 <xsl:with-param name="remainingLanguages">
15463 <xsl:value-of select="substring($remainingLanguages,4,string-length($remainingLanguages))"></xsl:value-of>
15465 </xsl:call-template>
15468 <xsl:call-template name="isoLanguage">
15469 <xsl:with-param name="currentLanguage">
15470 <xsl:value-of select="substring($remainingLanguages,1,3)"></xsl:value-of>
15472 <xsl:with-param name="usedLanguages">
15473 <xsl:value-of select="concat($usedLanguages,$currentLanguage)"></xsl:value-of>
15475 <xsl:with-param name="remainingLanguages">
15476 <xsl:value-of select="substring($remainingLanguages,4,string-length($remainingLanguages))"></xsl:value-of>
15478 </xsl:call-template>
15482 <xsl:template name="chopBrackets">
15483 <xsl:param name="chopString"></xsl:param>
15484 <xsl:variable name="string">
15485 <xsl:call-template name="chopPunctuation">
15486 <xsl:with-param name="chopString" select="$chopString"></xsl:with-param>
15487 </xsl:call-template>
15489 <xsl:if test="substring($string, 1,1)='['">
15490 <xsl:value-of select="substring($string,2, string-length($string)-2)"></xsl:value-of>
15492 <xsl:if test="substring($string, 1,1)!='['">
15493 <xsl:value-of select="$string"></xsl:value-of>
15496 <xsl:template name="rfcLanguages">
15497 <xsl:param name="nodeNum"></xsl:param>
15498 <xsl:param name="usedLanguages"></xsl:param>
15499 <xsl:param name="controlField008-35-37"></xsl:param>
15500 <xsl:variable name="currentLanguage" select="."></xsl:variable>
15502 <xsl:when test="not($currentLanguage)"></xsl:when>
15503 <xsl:when test="$currentLanguage!=$controlField008-35-37 and $currentLanguage!='rfc3066'">
15504 <xsl:if test="not(contains($usedLanguages,$currentLanguage))">
15506 <xsl:if test="@code!='a'">
15507 <xsl:attribute name="objectPart">
15509 <xsl:when test="@code='b'">summary or subtitle</xsl:when>
15510 <xsl:when test="@code='d'">sung or spoken text</xsl:when>
15511 <xsl:when test="@code='e'">libretto</xsl:when>
15512 <xsl:when test="@code='f'">table of contents</xsl:when>
15513 <xsl:when test="@code='g'">accompanying material</xsl:when>
15514 <xsl:when test="@code='h'">translation</xsl:when>
15518 <languageTerm authority="rfc3066" type="code">
15519 <xsl:value-of select="$currentLanguage"/>
15528 <xsl:template name="datafield">
15529 <xsl:param name="tag"/>
15530 <xsl:param name="ind1"><xsl:text> </xsl:text></xsl:param>
15531 <xsl:param name="ind2"><xsl:text> </xsl:text></xsl:param>
15532 <xsl:param name="subfields"/>
15533 <xsl:element name="marc:datafield">
15534 <xsl:attribute name="tag">
15535 <xsl:value-of select="$tag"/>
15537 <xsl:attribute name="ind1">
15538 <xsl:value-of select="$ind1"/>
15540 <xsl:attribute name="ind2">
15541 <xsl:value-of select="$ind2"/>
15543 <xsl:copy-of select="$subfields"/>
15547 <xsl:template name="subfieldSelect">
15548 <xsl:param name="codes"/>
15549 <xsl:param name="delimeter"><xsl:text> </xsl:text></xsl:param>
15550 <xsl:variable name="str">
15551 <xsl:for-each select="marc:subfield">
15552 <xsl:if test="contains($codes, @code)">
15553 <xsl:value-of select="text()"/><xsl:value-of select="$delimeter"/>
15557 <xsl:value-of select="substring($str,1,string-length($str)-string-length($delimeter))"/>
15560 <xsl:template name="buildSpaces">
15561 <xsl:param name="spaces"/>
15562 <xsl:param name="char"><xsl:text> </xsl:text></xsl:param>
15563 <xsl:if test="$spaces>0">
15564 <xsl:value-of select="$char"/>
15565 <xsl:call-template name="buildSpaces">
15566 <xsl:with-param name="spaces" select="$spaces - 1"/>
15567 <xsl:with-param name="char" select="$char"/>
15568 </xsl:call-template>
15572 <xsl:template name="chopPunctuation">
15573 <xsl:param name="chopString"/>
15574 <xsl:param name="punctuation"><xsl:text>.:,;/ </xsl:text></xsl:param>
15575 <xsl:variable name="length" select="string-length($chopString)"/>
15577 <xsl:when test="$length=0"/>
15578 <xsl:when test="contains($punctuation, substring($chopString,$length,1))">
15579 <xsl:call-template name="chopPunctuation">
15580 <xsl:with-param name="chopString" select="substring($chopString,1,$length - 1)"/>
15581 <xsl:with-param name="punctuation" select="$punctuation"/>
15582 </xsl:call-template>
15584 <xsl:when test="not($chopString)"/>
15585 <xsl:otherwise><xsl:value-of select="$chopString"/></xsl:otherwise>
15589 <xsl:template name="chopPunctuationFront">
15590 <xsl:param name="chopString"/>
15591 <xsl:variable name="length" select="string-length($chopString)"/>
15593 <xsl:when test="$length=0"/>
15594 <xsl:when test="contains('.:,;/[ ', substring($chopString,1,1))">
15595 <xsl:call-template name="chopPunctuationFront">
15596 <xsl:with-param name="chopString" select="substring($chopString,2,$length - 1)"/>
15597 </xsl:call-template>
15599 <xsl:when test="not($chopString)"/>
15600 <xsl:otherwise><xsl:value-of select="$chopString"/></xsl:otherwise>
15603 </xsl:stylesheet>$$ WHERE name = 'mods32';
15605 -- Currently, the only difference from naco_normalize is that search_normalize
15606 -- turns apostrophes into spaces, while naco_normalize collapses them.
15607 CREATE OR REPLACE FUNCTION public.search_normalize( TEXT, TEXT ) RETURNS TEXT AS $func$
15610 use Unicode::Normalize;
15613 my $str = decode_utf8(shift);
15616 # Apply NACO normalization to input string; based on
15617 # http://www.loc.gov/catdir/pcc/naco/SCA_PccNormalization_Final_revised.pdf
15619 # Note that unlike a strict reading of the NACO normalization rules,
15620 # output is returned as lowercase instead of uppercase for compatibility
15621 # with previous versions of the Evergreen naco_normalize routine.
15623 # Convert to upper-case first; even though final output will be lowercase, doing this will
15624 # ensure that the German eszett (ß) and certain ligatures (ff, fi, ffl, etc.) will be handled correctly.
15625 # If there are any bugs in Perl's implementation of upcasing, they will be passed through here.
15628 # remove non-filing strings
15629 $str =~ s/\x{0098}.*?\x{009C}//g;
15633 # additional substitutions - 3.6.
15634 $str =~ s/\x{00C6}/AE/g;
15635 $str =~ s/\x{00DE}/TH/g;
15636 $str =~ s/\x{0152}/OE/g;
15637 $str =~ tr/\x{0110}\x{00D0}\x{00D8}\x{0141}\x{2113}\x{02BB}\x{02BC}][/DDOLl/d;
15639 # transformations based on Unicode category codes
15640 $str =~ s/[\p{Cc}\p{Cf}\p{Co}\p{Cs}\p{Lm}\p{Mc}\p{Me}\p{Mn}]//g;
15642 if ($sf && $sf =~ /^a/o) {
15643 my $commapos = index($str, ',');
15644 if ($commapos > -1) {
15645 if ($commapos != length($str) - 1) {
15646 $str =~ s/,/\x07/; # preserve first comma
15651 # since we've stripped out the control characters, we can now
15652 # use a few as placeholders temporarily
15653 $str =~ tr/+&@\x{266D}\x{266F}#/\x01\x02\x03\x04\x05\x06/;
15654 $str =~ s/[\p{Pc}\p{Pd}\p{Pe}\p{Pf}\p{Pi}\p{Po}\p{Ps}\p{Sk}\p{Sm}\p{So}\p{Zl}\p{Zp}\p{Zs}]/ /g;
15655 $str =~ tr/\x01\x02\x03\x04\x05\x06\x07/+&@\x{266D}\x{266F}#,/;
15658 $str =~ tr/\x{0660}-\x{0669}\x{06F0}-\x{06F9}\x{07C0}-\x{07C9}\x{0966}-\x{096F}\x{09E6}-\x{09EF}\x{0A66}-\x{0A6F}\x{0AE6}-\x{0AEF}\x{0B66}-\x{0B6F}\x{0BE6}-\x{0BEF}\x{0C66}-\x{0C6F}\x{0CE6}-\x{0CEF}\x{0D66}-\x{0D6F}\x{0E50}-\x{0E59}\x{0ED0}-\x{0ED9}\x{0F20}-\x{0F29}\x{1040}-\x{1049}\x{1090}-\x{1099}\x{17E0}-\x{17E9}\x{1810}-\x{1819}\x{1946}-\x{194F}\x{19D0}-\x{19D9}\x{1A80}-\x{1A89}\x{1A90}-\x{1A99}\x{1B50}-\x{1B59}\x{1BB0}-\x{1BB9}\x{1C40}-\x{1C49}\x{1C50}-\x{1C59}\x{A620}-\x{A629}\x{A8D0}-\x{A8D9}\x{A900}-\x{A909}\x{A9D0}-\x{A9D9}\x{AA50}-\x{AA59}\x{ABF0}-\x{ABF9}\x{FF10}-\x{FF19}/0-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-9/;
15660 # intentionally skipping step 8 of the NACO algorithm; if the string
15661 # gets normalized away, that's fine.
15663 # leading and trailing spaces
15669 $func$ LANGUAGE 'plperlu' STRICT IMMUTABLE;
15671 CREATE OR REPLACE FUNCTION public.search_normalize_keep_comma( TEXT ) RETURNS TEXT AS $func$
15672 SELECT public.search_normalize($1,'a');
15673 $func$ LANGUAGE SQL STRICT IMMUTABLE;
15675 CREATE OR REPLACE FUNCTION public.search_normalize( TEXT ) RETURNS TEXT AS $func$
15676 SELECT public.search_normalize($1,'');
15677 $func$ LANGUAGE 'sql' STRICT IMMUTABLE;
15679 INSERT INTO config.index_normalizer (name, description, func, param_count) VALUES (
15680 'Search Normalize',
15681 'Apply search normalization rules to the extracted text. A less extreme version of NACO normalization.',
15682 'search_normalize',
15686 UPDATE config.metabib_field_index_norm_map
15688 SELECT id FROM config.index_normalizer WHERE func = 'search_normalize'
15691 SELECT id FROM config.index_normalizer WHERE func = 'naco_normalize'
15696 -- This could take a long time if you have a very non-English bib database
15697 -- Run it outside of a transaction to avoid lock escalation
15698 SELECT metabib.reingest_metabib_field_entries(record)
15699 FROM metabib.full_rec
15702 AND value LIKE '%''%'
15707 -- This is split out because it takes forever to run on large bib collections.
15708 \qecho ************************************************************************
15709 \qecho The following transaction, wrapping upgrades 0679 and 0680, may take a
15710 \qecho *really* long time, and you might be able to run it by itself in
15711 \qecho parallel with other operations using a separate session.
15712 \qecho ************************************************************************
15715 SELECT evergreen.upgrade_deps_block_check('0679', :eg_version);
15717 -- Address typo in column name
15718 ALTER TABLE config.metabib_class ADD COLUMN buoyant BOOL DEFAULT FALSE NOT NULL;
15719 UPDATE config.metabib_class SET buoyant = bouyant;
15720 ALTER TABLE config.metabib_class DROP COLUMN bouyant;
15722 CREATE OR REPLACE FUNCTION oils_tsearch2 () RETURNS TRIGGER AS $$
15728 value := NEW.value;
15730 IF TG_TABLE_NAME::TEXT ~ 'field_entry$' THEN
15732 SELECT n.func AS func,
15733 n.param_count AS param_count,
15735 FROM config.index_normalizer n
15736 JOIN config.metabib_field_index_norm_map m ON (m.norm = n.id)
15737 WHERE field = NEW.field AND m.pos < 0
15738 ORDER BY m.pos LOOP
15739 EXECUTE 'SELECT ' || normalizer.func || '(' ||
15740 quote_literal( value ) ||
15742 WHEN normalizer.param_count > 0
15743 THEN ',' || REPLACE(REPLACE(BTRIM(normalizer.params,'[]'),E'\'',E'\\\''),E'"',E'\'')
15750 NEW.value := value;
15753 IF NEW.index_vector = ''::tsvector THEN
15757 IF TG_TABLE_NAME::TEXT ~ 'field_entry$' THEN
15759 SELECT n.func AS func,
15760 n.param_count AS param_count,
15762 FROM config.index_normalizer n
15763 JOIN config.metabib_field_index_norm_map m ON (m.norm = n.id)
15764 WHERE field = NEW.field AND m.pos >= 0
15765 ORDER BY m.pos LOOP
15766 EXECUTE 'SELECT ' || normalizer.func || '(' ||
15767 quote_literal( value ) ||
15769 WHEN normalizer.param_count > 0
15770 THEN ',' || REPLACE(REPLACE(BTRIM(normalizer.params,'[]'),E'\'',E'\\\''),E'"',E'\'')
15778 IF TG_TABLE_NAME::TEXT ~ 'browse_entry$' THEN
15779 value := ARRAY_TO_STRING(
15780 evergreen.regexp_split_to_array(value, E'\\W+'), ' '
15782 value := public.search_normalize(value);
15785 NEW.index_vector = to_tsvector((TG_ARGV[0])::regconfig, value);
15789 $$ LANGUAGE PLPGSQL;
15791 -- Given a string such as a user might type into a search box, prepare
15792 -- two changed variants for TO_TSQUERY(). See
15793 -- http://www.postgresql.org/docs/9.0/static/textsearch-controls.html
15794 -- The first variant is normalized to match indexed documents regardless
15795 -- of diacritics. The second variant keeps its diacritics for proper
15796 -- highlighting via TS_HEADLINE().
15798 FUNCTION metabib.autosuggest_prepare_tsquery(orig TEXT) RETURNS TEXT[] AS
15801 orig_ended_in_space BOOLEAN;
15806 orig_ended_in_space := orig ~ E'\\s$';
15808 orig := ARRAY_TO_STRING(
15809 evergreen.regexp_split_to_array(orig, E'\\W+'), ' '
15812 normalized := public.search_normalize(orig); -- also trim()s
15813 plain := trim(orig);
15815 IF NOT orig_ended_in_space THEN
15816 plain := plain || ':*';
15817 normalized := normalized || ':*';
15820 plain := ARRAY_TO_STRING(
15821 evergreen.regexp_split_to_array(plain, E'\\s+'), ' & '
15823 normalized := ARRAY_TO_STRING(
15824 evergreen.regexp_split_to_array(normalized, E'\\s+'), ' & '
15827 RETURN ARRAY[normalized, plain];
15829 $$ LANGUAGE PLPGSQL;
15832 -- Definition of OUT parameters changes, so must drop first
15833 DROP FUNCTION IF EXISTS metabib.suggest_browse_entries (TEXT, TEXT, TEXT, INTEGER, INTEGER, INTEGER);
15836 FUNCTION metabib.suggest_browse_entries(
15837 raw_query_text TEXT, -- actually typed by humans at the UI level
15838 search_class TEXT, -- 'alias' or 'class' or 'class|field..', etc
15839 headline_opts TEXT, -- markup options for ts_headline()
15840 visibility_org INTEGER,-- null if you don't want opac visibility test
15841 query_limit INTEGER,-- use in LIMIT clause of interal query
15842 normalization INTEGER -- argument to TS_RANK_CD()
15844 value TEXT, -- plain
15846 buoyant_and_class_match BOOL,
15848 field_weight INTEGER,
15851 match TEXT -- marked up
15854 prepared_query_texts TEXT[];
15856 plain_query TSQUERY;
15857 opac_visibility_join TEXT;
15858 search_class_join TEXT;
15861 prepared_query_texts := metabib.autosuggest_prepare_tsquery(raw_query_text);
15863 query := TO_TSQUERY('keyword', prepared_query_texts[1]);
15864 plain_query := TO_TSQUERY('keyword', prepared_query_texts[2]);
15866 IF visibility_org IS NOT NULL THEN
15867 opac_visibility_join := '
15868 JOIN asset.opac_visible_copies aovc ON (
15869 aovc.record = mbedm.source AND
15870 aovc.circ_lib IN (SELECT id FROM actor.org_unit_descendants($4))
15873 opac_visibility_join := '';
15876 -- The following determines whether we only provide suggestsons matching
15877 -- the user's selected search_class, or whether we show other suggestions
15878 -- too. The reason for MIN() is that for search_classes like
15879 -- 'title|proper|uniform' you would otherwise get multiple rows. The
15880 -- implication is that if title as a class doesn't have restrict,
15881 -- nor does the proper field, but the uniform field does, you're going
15882 -- to get 'false' for your overall evaluation of 'should we restrict?'
15883 -- To invert that, change from MIN() to MAX().
15887 MIN(cmc.restrict::INT) AS restrict_class,
15888 MIN(cmf.restrict::INT) AS restrict_field
15889 FROM metabib.search_class_to_registered_components(search_class)
15890 AS _registered (field_class TEXT, field INT)
15892 config.metabib_class cmc ON (cmc.name = _registered.field_class)
15894 config.metabib_field cmf ON (cmf.id = _registered.field);
15896 -- evaluate 'should we restrict?'
15897 IF r_fields.restrict_field::BOOL OR r_fields.restrict_class::BOOL THEN
15898 search_class_join := '
15900 metabib.search_class_to_registered_components($2)
15901 AS _registered (field_class TEXT, field INT) ON (
15902 (_registered.field IS NULL AND
15903 _registered.field_class = cmf.field_class) OR
15904 (_registered.field = cmf.id)
15908 search_class_join := '
15910 metabib.search_class_to_registered_components($2)
15911 AS _registered (field_class TEXT, field INT) ON (
15912 _registered.field_class = cmc.name
15917 RETURN QUERY EXECUTE 'SELECT *, TS_HEADLINE(value, $7, $3) FROM (SELECT DISTINCT
15920 cmc.buoyant AND _registered.field_class IS NOT NULL,
15921 _registered.field = cmf.id,
15923 TS_RANK_CD(mbe.index_vector, $1, $6),
15925 FROM metabib.browse_entry_def_map mbedm
15926 JOIN metabib.browse_entry mbe ON (mbe.id = mbedm.entry)
15927 JOIN config.metabib_field cmf ON (cmf.id = mbedm.def)
15928 JOIN config.metabib_class cmc ON (cmf.field_class = cmc.name)
15929 ' || search_class_join || opac_visibility_join ||
15930 ' WHERE $1 @@ mbe.index_vector
15931 ORDER BY 3 DESC, 4 DESC NULLS LAST, 5 DESC, 6 DESC, 7 DESC, 1 ASC
15933 ORDER BY 3 DESC, 4 DESC NULLS LAST, 5 DESC, 6 DESC, 7 DESC, 1 ASC
15934 ' -- sic, repeat the order by clause in the outer select too
15936 query, search_class, headline_opts,
15937 visibility_org, query_limit, normalization, plain_query
15941 -- buoyant AND chosen class = match class
15942 -- chosen field = match field
15949 $func$ LANGUAGE PLPGSQL;
15953 \qecho The following takes about a minute per 100,000 rows in
15954 \qecho metabib.browse_entry on my development system, which is only a VM with
15955 \qecho 4 GB of memory and 2 cores.
15957 \qecho The following is a very loose estimate of how long the next UPDATE
15958 \qecho statement would take to finish on MY machine, based on YOUR number
15959 \qecho of rows in metabib.browse_entry:
15962 SELECT (COUNT(id) / 100000.0) * INTERVAL '1 minute'
15963 AS "approximate duration of following UPDATE statement"
15964 FROM metabib.browse_entry;
15966 UPDATE metabib.browse_entry SET index_vector = TO_TSVECTOR(
15968 public.search_normalize(
15970 evergreen.regexp_split_to_array(value, E'\\W+'), ' '
15976 SELECT evergreen.upgrade_deps_block_check('0680', :eg_version);
15978 -- Not much use in having identifier-class fields be suggestions. Credit for the idea goes to Ben Shum.
15979 UPDATE config.metabib_field SET browse_field = FALSE WHERE id < 100 AND field_class = 'identifier';
15982 ---------------------------------------------------------------------------
15983 -- The rest of this was tested on Evergreen Indiana's dev server, which has
15984 -- a large data set of 2.6M bibs, and was instrumental in sussing out the
15985 -- needed adjustments. Thanks, EG-IN!
15986 ---------------------------------------------------------------------------
15988 -- GIN indexes are /much/ better for prefix matching, which is important for browse and autosuggest
15989 --Commented out the creation earlier, so we don't need to drop it here.
15990 --DROP INDEX metabib.metabib_browse_entry_index_vector_idx;
15991 CREATE INDEX metabib_browse_entry_index_vector_idx ON metabib.browse_entry USING GIN (index_vector);
15994 -- We need thes to make the autosuggest limiting joins fast
15995 CREATE INDEX browse_entry_def_map_def_idx ON metabib.browse_entry_def_map (def);
15996 CREATE INDEX browse_entry_def_map_entry_idx ON metabib.browse_entry_def_map (entry);
15997 CREATE INDEX browse_entry_def_map_source_idx ON metabib.browse_entry_def_map (source);
15999 -- In practice this will always be ~1 row, and the default of 1000 causes terrible plans
16000 ALTER FUNCTION metabib.search_class_to_registered_components(text) ROWS 1;
16002 -- Reworking of the generated query to act in a sane manner in the face of large datasets
16004 FUNCTION metabib.suggest_browse_entries(
16005 raw_query_text TEXT, -- actually typed by humans at the UI level
16006 search_class TEXT, -- 'alias' or 'class' or 'class|field..', etc
16007 headline_opts TEXT, -- markup options for ts_headline()
16008 visibility_org INTEGER,-- null if you don't want opac visibility test
16009 query_limit INTEGER,-- use in LIMIT clause of interal query
16010 normalization INTEGER -- argument to TS_RANK_CD()
16012 value TEXT, -- plain
16014 buoyant_and_class_match BOOL,
16016 field_weight INTEGER,
16019 match TEXT -- marked up
16022 prepared_query_texts TEXT[];
16024 plain_query TSQUERY;
16025 opac_visibility_join TEXT;
16026 search_class_join TEXT;
16029 prepared_query_texts := metabib.autosuggest_prepare_tsquery(raw_query_text);
16031 query := TO_TSQUERY('keyword', prepared_query_texts[1]);
16032 plain_query := TO_TSQUERY('keyword', prepared_query_texts[2]);
16034 IF visibility_org IS NOT NULL THEN
16035 opac_visibility_join := '
16036 JOIN asset.opac_visible_copies aovc ON (
16037 aovc.record = x.source AND
16038 aovc.circ_lib IN (SELECT id FROM actor.org_unit_descendants($4))
16041 opac_visibility_join := '';
16044 -- The following determines whether we only provide suggestsons matching
16045 -- the user's selected search_class, or whether we show other suggestions
16046 -- too. The reason for MIN() is that for search_classes like
16047 -- 'title|proper|uniform' you would otherwise get multiple rows. The
16048 -- implication is that if title as a class doesn't have restrict,
16049 -- nor does the proper field, but the uniform field does, you're going
16050 -- to get 'false' for your overall evaluation of 'should we restrict?'
16051 -- To invert that, change from MIN() to MAX().
16055 MIN(cmc.restrict::INT) AS restrict_class,
16056 MIN(cmf.restrict::INT) AS restrict_field
16057 FROM metabib.search_class_to_registered_components(search_class)
16058 AS _registered (field_class TEXT, field INT)
16060 config.metabib_class cmc ON (cmc.name = _registered.field_class)
16062 config.metabib_field cmf ON (cmf.id = _registered.field);
16064 -- evaluate 'should we restrict?'
16065 IF r_fields.restrict_field::BOOL OR r_fields.restrict_class::BOOL THEN
16066 search_class_join := '
16068 metabib.search_class_to_registered_components($2)
16069 AS _registered (field_class TEXT, field INT) ON (
16070 (_registered.field IS NULL AND
16071 _registered.field_class = cmf.field_class) OR
16072 (_registered.field = cmf.id)
16076 search_class_join := '
16078 metabib.search_class_to_registered_components($2)
16079 AS _registered (field_class TEXT, field INT) ON (
16080 _registered.field_class = cmc.name
16085 RETURN QUERY EXECUTE '
16094 TS_HEADLINE(value, $7, $3)
16095 FROM (SELECT DISTINCT
16098 cmc.buoyant AND _registered.field_class IS NOT NULL AS push,
16099 _registered.field = cmf.id AS restrict,
16101 TS_RANK_CD(mbe.index_vector, $1, $6),
16104 FROM metabib.browse_entry_def_map mbedm
16106 -- Start with a pre-limited set of 10k possible suggestions. More than that is not going to be useful anyway
16107 JOIN (SELECT * FROM metabib.browse_entry WHERE index_vector @@ $1 LIMIT 10000) mbe ON (mbe.id = mbedm.entry)
16109 JOIN config.metabib_field cmf ON (cmf.id = mbedm.def)
16110 JOIN config.metabib_class cmc ON (cmf.field_class = cmc.name)
16111 ' || search_class_join || '
16112 ORDER BY 3 DESC, 4 DESC NULLS LAST, 5 DESC, 6 DESC, 7 DESC, 1 ASC
16113 LIMIT 1000) AS x -- This outer limit makes testing for opac visibility usably fast
16114 ' || opac_visibility_join || '
16115 ORDER BY 3 DESC, 4 DESC NULLS LAST, 5 DESC, 6 DESC, 7 DESC, 1 ASC
16117 ' -- sic, repeat the order by clause in the outer select too
16119 query, search_class, headline_opts,
16120 visibility_org, query_limit, normalization, plain_query
16124 -- buoyant AND chosen class = match class
16125 -- chosen field = match field
16132 $func$ LANGUAGE PLPGSQL;
16136 -- This is split out because it was backported to 2.1, but may not exist before upgrades
16137 -- It can safely fail
16138 -- Also, lets say that. <_<
16140 \qecho *************************************************************************
16141 \qecho !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
16142 \qecho We are about to apply a patch that may not be needed. It can fail safely.
16143 \qecho !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
16144 \qecho *************************************************************************
16147 -- Evergreen DB patch 0693.schema.do_not_despace_issns.sql
16149 -- FIXME: insert description of change, if needed
16154 -- check whether patch can be applied
16155 SELECT evergreen.upgrade_deps_block_check('0693', :eg_version);
16157 -- FIXME: add/check SQL statements to perform the upgrade
16158 -- Delete the index normalizer that was meant to remove spaces from ISSNs
16159 -- but ended up breaking records with multiple ISSNs
16160 DELETE FROM config.metabib_field_index_norm_map WHERE id IN (
16161 SELECT map.id FROM config.metabib_field_index_norm_map map
16162 INNER JOIN config.metabib_field cmf ON cmf.id = map.field
16163 INNER JOIN config.index_normalizer cin ON cin.id = map.norm
16164 WHERE cin.func = 'replace'
16165 AND cmf.field_class = 'identifier'
16166 AND cmf.name = 'issn'
16167 AND map.params = $$[" ",""]$$
16170 -- Reindex records that have more than just a single ISSN
16171 -- to ensure that spaces are maintained
16172 SELECT metabib.reingest_metabib_field_entries(source)
16173 FROM metabib.identifier_field_entry mife
16174 INNER JOIN config.metabib_field cmf ON cmf.id = mife.field
16175 WHERE cmf.field_class = 'identifier'
16176 AND cmf.name = 'issn'
16177 AND char_length(value) > 9
16183 -- outside of any transaction
16185 \qecho ************************************************************************
16186 \qecho Failures from here down are okay!
16187 \qecho ************************************************************************
16189 SELECT evergreen.upgrade_deps_block_check('0691', :eg_version);
16191 CREATE INDEX poi_po_idx ON acq.po_item (purchase_order);
16193 CREATE INDEX ie_inv_idx on acq.invoice_entry (invoice);
16194 CREATE INDEX ie_po_idx on acq.invoice_entry (purchase_order);
16195 CREATE INDEX ie_li_idx on acq.invoice_entry (lineitem);
16197 CREATE INDEX ii_inv_idx on acq.invoice_item (invoice);
16198 CREATE INDEX ii_po_idx on acq.invoice_item (purchase_order);
16199 CREATE INDEX ii_poi_idx on acq.invoice_item (po_item);
16201 \qecho All Evergreen core database functions have been converted to
16202 \qecho use PLPERLU instead of PLPERL, so we are attempting to remove
16203 \qecho the PLPERL language here; but it is entirely possible that
16204 \qecho existing sites will have custom PLPERL functions that they
16205 \qecho will want to retain, so the following DROP LANGUAGE statement
16206 \qecho may fail, and that is okay.
16208 DROP LANGUAGE plperl;
16210 \qecho Evergreen depends heavily on each bibliographic record containing
16211 \qecho a 901 field with a subfield "c" to hold the record ID. The following
16212 \qecho query identifies the bibs that are missing 901s or whose first
16213 \qecho 901$c is not equal to the bib ID. This *will* take a long time in a
16214 \qecho big database; as the schema updates are over now, you can cancel this
16215 \qecho if you are in a rush.
16218 FROM biblio.record_entry
16220 (XPATH('//marc:datafield[@tag="901"][1]/marc:subfield[@code="c"]/text()', marc::XML, ARRAY[ARRAY['marc', 'http://www.loc.gov/MARC21/slim']]))[1]::TEXT IS NULL
16222 (XPATH('//marc:datafield[@tag="901"][1]/marc:subfield[@code="c"]/text()', marc::XML, ARRAY[ARRAY['marc', 'http://www.loc.gov/MARC21/slim']]))[1]::TEXT <> id::TEXT)
16225 \qecho If there are records with missing or incorrect 901$c values, you can
16226 \qecho generally rely on the triggers in the biblio.record_entry table to
16227 \qecho populate the 901$c properly; for each offending record, run:
16228 \qecho UPDATE biblio.record_entry SET marc = marc WHERE id = <id>;