]> git.evergreen-ils.org Git - working/Evergreen.git/blob - Open-ILS/src/sql/Pg/version-upgrade/2.12.5-3.0-beta1-upgrade-db.sql
Incorporate view update from LP#1714589
[working/Evergreen.git] / Open-ILS / src / sql / Pg / version-upgrade / 2.12.5-3.0-beta1-upgrade-db.sql
1 --Upgrade Script for 2.12.5 to 3.0-beta1
2 \set eg_version '''3.0-beta1'''
3 BEGIN;
4 INSERT INTO config.upgrade_log (version, applied_to) VALUES ('3.0-beta1', :eg_version);
5
6 SELECT evergreen.upgrade_deps_block_check('1032', :eg_version); -- Bmagic/csharp/gmcharlt
7
8 CREATE OR REPLACE VIEW action.all_circulation_combined_types AS 
9  SELECT acirc.id AS id,
10     acirc.xact_start,
11     acirc.circ_lib,
12     acirc.circ_staff,
13     acirc.create_time,
14     ac_acirc.circ_modifier AS item_type,
15     'regular_circ'::text AS circ_type
16    FROM action.circulation acirc,
17     asset.copy ac_acirc
18   WHERE acirc.target_copy = ac_acirc.id
19 UNION ALL
20  SELECT ancc.id::BIGINT AS id,
21     ancc.circ_time AS xact_start,
22     ancc.circ_lib,
23     ancc.staff AS circ_staff,
24     ancc.circ_time AS create_time,
25     cnct_ancc.name AS item_type,
26     'non-cat_circ'::text AS circ_type
27    FROM action.non_cataloged_circulation ancc,
28     config.non_cataloged_type cnct_ancc
29   WHERE ancc.item_type = cnct_ancc.id
30 UNION ALL
31  SELECT aihu.id::BIGINT AS id,
32     aihu.use_time AS xact_start,
33     aihu.org_unit AS circ_lib,
34     aihu.staff AS circ_staff,
35     aihu.use_time AS create_time,
36     ac_aihu.circ_modifier AS item_type,
37     'in-house_use'::text AS circ_type
38    FROM action.in_house_use aihu,
39     asset.copy ac_aihu
40   WHERE aihu.item = ac_aihu.id
41 UNION ALL
42  SELECT ancihu.id::BIGINT AS id,
43     ancihu.use_time AS xact_start,
44     ancihu.org_unit AS circ_lib,
45     ancihu.staff AS circ_staff,
46     ancihu.use_time AS create_time,
47     cnct_ancihu.name AS item_type,
48     'non-cat_circ'::text AS circ_type
49    FROM action.non_cat_in_house_use ancihu,
50     config.non_cataloged_type cnct_ancihu
51   WHERE ancihu.item_type = cnct_ancihu.id
52 UNION ALL
53  SELECT aacirc.id AS id,
54     aacirc.xact_start,
55     aacirc.circ_lib,
56     aacirc.circ_staff,
57     aacirc.create_time,
58     ac_aacirc.circ_modifier AS item_type,
59     'aged_circ'::text AS circ_type
60    FROM action.aged_circulation aacirc,
61     asset.copy ac_aacirc
62   WHERE aacirc.target_copy = ac_aacirc.id;
63
64
65 SELECT evergreen.upgrade_deps_block_check('1034', :eg_version);
66
67 ALTER TABLE config.hold_matrix_matchpoint
68     ADD COLUMN description TEXT;
69
70 ALTER TABLE config.circ_matrix_matchpoint
71     ADD COLUMN description TEXT;
72
73
74 INSERT INTO config.upgrade_log (version, applied_to) VALUES ('1035', :eg_version); -- dyrcona/gmcharlt
75
76 -- Per Lp bug 1684984, the config.internal_flag,
77 -- ingest.disable_metabib_field_entry, was made obsolete by the
78 -- addition of the ingest.skip_browse_indexing,
79 -- ingest.skip_search_indexing, and ingest.skip_facet_indexing flags.
80 -- Since it is not used in the database, we delete it.
81 DELETE FROM config.internal_flag
82 WHERE name = 'ingest.disable_metabib_field_entry';
83
84
85 SELECT evergreen.upgrade_deps_block_check('1036', :eg_version);
86
87 CREATE OR REPLACE FUNCTION config.update_hard_due_dates () RETURNS INT AS $func$
88 DECLARE
89     temp_value  config.hard_due_date_values%ROWTYPE;
90     updated     INT := 0;
91 BEGIN
92     FOR temp_value IN
93       SELECT  DISTINCT ON (hard_due_date) *
94         FROM  config.hard_due_date_values
95         WHERE active_date <= NOW() -- We've passed (or are at) the rollover time
96         ORDER BY hard_due_date, active_date DESC -- Latest (nearest to us) active time
97    LOOP
98         UPDATE  config.hard_due_date
99           SET   ceiling_date = temp_value.ceiling_date
100           WHERE id = temp_value.hard_due_date
101                 AND ceiling_date <> temp_value.ceiling_date -- Time is equal if we've already updated the chdd
102                 AND temp_value.ceiling_date >= NOW(); -- Don't update ceiling dates to the past
103
104         IF FOUND THEN
105             updated := updated + 1;
106         END IF;
107     END LOOP;
108
109     RETURN updated;
110 END;
111 $func$ LANGUAGE plpgsql;
112
113
114 INSERT INTO config.upgrade_log (version, applied_to) VALUES ('1041', :eg_version); -- stompro/csharp/gmcharlt
115
116 --delete all instances from permission.grp_perm_map first
117 DELETE FROM permission.grp_perm_map where perm in 
118 (select id from permission.perm_list where code='SET_CIRC_MISSING');
119
120 --delete all instances from permission.usr_perm_map too
121 DELETE FROM permission.usr_perm_map where perm in
122 (select id from permission.perm_list where code='SET_CIRC_MISSING');
123
124 --delete from permission.perm_list
125 DELETE FROM permission.perm_list where code='SET_CIRC_MISSING';
126
127
128 INSERT INTO config.upgrade_log (version, applied_to) VALUES ('1042', :eg_version); -- mmorgan/gmcharlt
129
130 ALTER TABLE asset.copy_location
131           ADD COLUMN url TEXT;
132
133
134 SELECT evergreen.upgrade_deps_block_check('1043', :eg_version);
135
136 ALTER TABLE action_trigger.event_definition
137     ADD COLUMN retention_interval INTERVAL;
138
139 CREATE OR REPLACE FUNCTION action_trigger.check_valid_retention_interval() 
140     RETURNS TRIGGER AS $_$
141 BEGIN
142
143     /*
144      * 1. Retention intervals are alwyas allowed on active hooks.
145      * 2. On passive hooks, retention intervals are only allowed
146      *    when the event definition has a max_delay value and the
147      *    retention_interval value is greater than the difference 
148      *    beteween the delay and max_delay values.
149      */ 
150     PERFORM TRUE FROM action_trigger.hook 
151         WHERE key = NEW.hook AND NOT passive;
152
153     IF FOUND THEN
154         RETURN NEW;
155     END IF;
156
157     IF NEW.max_delay IS NOT NULL THEN
158         IF EXTRACT(EPOCH FROM NEW.retention_interval) > 
159             ABS(EXTRACT(EPOCH FROM (NEW.max_delay - NEW.delay))) THEN
160             RETURN NEW; -- all good
161         ELSE
162             RAISE EXCEPTION 'retention_interval is too short';
163         END IF;
164     ELSE
165         RAISE EXCEPTION 'retention_interval requires max_delay';
166     END IF;
167 END;
168 $_$ LANGUAGE PLPGSQL;
169
170 CREATE TRIGGER is_valid_retention_interval 
171     BEFORE INSERT OR UPDATE ON action_trigger.event_definition
172     FOR EACH ROW WHEN (NEW.retention_interval IS NOT NULL)
173     EXECUTE PROCEDURE action_trigger.check_valid_retention_interval();
174
175 CREATE OR REPLACE FUNCTION action_trigger.purge_events() RETURNS VOID AS $_$
176 /**
177   * Deleting expired events without simultaneously deleting their outputs
178   * creates orphaned outputs.  Deleting their outputs and all of the events 
179   * linking back to them, plus any outputs those events link to is messy and 
180   * inefficient.  It's simpler to handle them in 2 sweeping steps.
181   *
182   * 1. Delete expired events.
183   * 2. Delete orphaned event outputs.
184   *
185   * This has the added benefit of removing outputs that may have been
186   * orphaned by some other process.  Such outputs are not usuable by
187   * the system.
188   *
189   * This does not guarantee that all events within an event group are
190   * purged at the same time.  In such cases, the remaining events will
191   * be purged with the next instance of the purge (or soon thereafter).
192   * This is another nod toward efficiency over completeness of old 
193   * data that's circling the bit bucket anyway.
194   */
195 BEGIN
196
197     DELETE FROM action_trigger.event WHERE id IN (
198         SELECT evt.id
199         FROM action_trigger.event evt
200         JOIN action_trigger.event_definition def ON (def.id = evt.event_def)
201         WHERE def.retention_interval IS NOT NULL 
202             AND evt.state <> 'pending'
203             AND evt.update_time < (NOW() - def.retention_interval)
204     );
205
206     WITH linked_outputs AS (
207         SELECT templates.id AS id FROM (
208             SELECT DISTINCT(template_output) AS id
209                 FROM action_trigger.event WHERE template_output IS NOT NULL
210             UNION
211             SELECT DISTINCT(error_output) AS id
212                 FROM action_trigger.event WHERE error_output IS NOT NULL
213             UNION
214             SELECT DISTINCT(async_output) AS id
215                 FROM action_trigger.event WHERE async_output IS NOT NULL
216         ) templates
217     ) DELETE FROM action_trigger.event_output
218         WHERE id NOT IN (SELECT id FROM linked_outputs);
219
220 END;
221 $_$ LANGUAGE PLPGSQL;
222
223
224 /* -- UNDO --
225
226 DROP FUNCTION IF EXISTS action_trigger.purge_events();
227 DROP TRIGGER IF EXISTS is_valid_retention_interval ON action_trigger.event_definition;
228 DROP FUNCTION IF EXISTS action_trigger.check_valid_retention_interval();
229 ALTER TABLE action_trigger.event_definition DROP COLUMN retention_interval;
230
231 */
232
233
234
235 SELECT evergreen.upgrade_deps_block_check('1044', :eg_version);
236
237 UPDATE action_trigger.hook SET passive = FALSE WHERE key IN (
238     'format.po.html',
239     'format.po.pdf',
240     'format.selfcheck.checkout',
241     'format.selfcheck.items_out',
242     'format.selfcheck.holds',
243     'format.selfcheck.fines',
244     'format.acqcle.html',
245     'format.acqinv.html',
246     'format.acqli.html',
247     'aur.ordered',
248     'aur.received',
249     'aur.cancelled',
250     'aur.created',
251     'aur.rejected'
252 );
253
254
255 INSERT INTO config.upgrade_log (version, applied_to) VALUES ('1045', :eg_version); -- csharp/berick/gmcharlt
256
257 ALTER TABLE action.transit_copy
258         ADD COLUMN cancel_time TIMESTAMPTZ;
259
260 -- change "abort" to "cancel" in stock perm descriptions
261 UPDATE permission.perm_list 
262         SET description = 'Allow a user to cancel a copy transit if the user is at the transit destination or source' 
263         WHERE code = 'ABORT_TRANSIT'
264         AND description = 'Allow a user to abort a copy transit if the user is at the transit destination or source';
265 UPDATE permission.perm_list 
266         SET description = 'Allow a user to cancel a copy transit if the user is not at the transit source or dest' 
267         WHERE code = 'ABORT_REMOTE_TRANSIT'
268         AND description = 'Allow a user to abort a copy transit if the user is not at the transit source or dest';
269 UPDATE permission.perm_list 
270         SET description = 'Allows a user to cancel a transit on a copy with status of LOST' 
271         WHERE code = 'ABORT_TRANSIT_ON_LOST'
272         AND description = 'Allows a user to abort a transit on a copy with status of LOST';
273 UPDATE permission.perm_list 
274         SET description = 'Allows a user to cancel a transit on a copy with status of MISSING' 
275         WHERE code = 'ABORT_TRANSIT_ON_MISSING'
276         AND description = 'Allows a user to abort a transit on a copy with status of MISSING';
277
278 SELECT evergreen.upgrade_deps_block_check('1046', :eg_version); -- phasefx/berick/gmcharlt
279
280 INSERT into config.org_unit_setting_type (
281      name
282     ,grp
283     ,label
284     ,description
285     ,datatype
286 ) VALUES ( ----------------------------------------
287      'webstaff.format.dates'
288     ,'gui'
289     ,oils_i18n_gettext(
290          'webstaff.format.dates'
291         ,'Format Dates with this pattern'
292         ,'coust'
293         ,'label'
294     )
295     ,oils_i18n_gettext(
296          'webstaff.format.dates'
297         ,'Format Dates with this pattern (examples: "yyyy-MM-dd" for "2010-04-26", "MMM d, yyyy" for "Apr 26, 2010").  This will be used in areas where a date without a timestamp is sufficient, like Date of Birth.'
298         ,'coust'
299         ,'description'
300     )
301     ,'string'
302 ), ( ----------------------------------------
303      'webstaff.format.date_and_time'
304     ,'gui'
305     ,oils_i18n_gettext(
306          'webstaff.format.date_and_time'
307         ,'Format Date+Time with this pattern'
308         ,'coust'
309         ,'label'
310     )
311     ,oils_i18n_gettext(
312          'webstaff.format.date_and_time'
313         ,'Format Date+Time with this pattern (examples: "yy-MM-dd h:m:s.SSS a" for "16-04-05 2:07:20.666 PM", "yyyy-dd-MMM HH:mm" for "2016-05-Apr 14:07").  This will be used in areas of the client where a date with a timestamp is needed, like Checkout, Due Date, or Record Created.'
314         ,'coust'
315         ,'description'
316     )
317     ,'string'
318 );
319
320 UPDATE
321     config.org_unit_setting_type
322 SET
323     label = 'Deprecated: ' || label -- FIXME: Is this okay?
324 WHERE
325     name IN ('format.date','format.time')
326 ;
327
328
329 SELECT evergreen.upgrade_deps_block_check('1047', :eg_version); -- gmcharlt/stompro
330
331 CREATE TABLE config.copy_tag_type (
332     code            TEXT NOT NULL PRIMARY KEY,
333     label           TEXT NOT NULL,
334     owner           INTEGER NOT NULL REFERENCES actor.org_unit (id) DEFERRABLE INITIALLY DEFERRED
335 );
336
337 CREATE INDEX config_copy_tag_type_owner_idx
338     ON config.copy_tag_type (owner);
339
340 CREATE TABLE asset.copy_tag (
341     id              SERIAL PRIMARY KEY,
342     tag_type        TEXT REFERENCES config.copy_tag_type (code)
343                     ON UPDATE CASCADE ON DELETE CASCADE,
344     label           TEXT NOT NULL,
345     value           TEXT NOT NULL,
346     index_vector    tsvector NOT NULL,
347     staff_note      TEXT,
348     pub             BOOLEAN DEFAULT TRUE,
349     owner           INTEGER NOT NULL REFERENCES actor.org_unit (id)
350 );
351
352 CREATE INDEX asset_copy_tag_label_idx
353     ON asset.copy_tag (label);
354 CREATE INDEX asset_copy_tag_label_lower_idx
355     ON asset.copy_tag (evergreen.lowercase(label));
356 CREATE INDEX asset_copy_tag_index_vector_idx
357     ON asset.copy_tag
358     USING GIN(index_vector);
359 CREATE INDEX asset_copy_tag_tag_type_idx
360     ON asset.copy_tag (tag_type);
361 CREATE INDEX asset_copy_tag_owner_idx
362     ON asset.copy_tag (owner);
363
364 CREATE OR REPLACE FUNCTION asset.set_copy_tag_value () RETURNS TRIGGER AS $$
365 BEGIN
366     IF NEW.value IS NULL THEN
367         NEW.value = NEW.label;        
368     END IF;
369
370     RETURN NEW;
371 END;
372 $$ LANGUAGE 'plpgsql';
373
374 -- name of following trigger chosen to ensure it runs first
375 CREATE TRIGGER asset_copy_tag_do_value
376     BEFORE INSERT OR UPDATE ON asset.copy_tag
377     FOR EACH ROW EXECUTE PROCEDURE asset.set_copy_tag_value();
378 CREATE TRIGGER asset_copy_tag_fti_trigger
379     BEFORE UPDATE OR INSERT ON asset.copy_tag
380     FOR EACH ROW EXECUTE PROCEDURE oils_tsearch2('default');
381
382 CREATE TABLE asset.copy_tag_copy_map (
383     id              BIGSERIAL PRIMARY KEY,
384     copy            BIGINT REFERENCES asset.copy (id)
385                     ON UPDATE CASCADE ON DELETE CASCADE,
386     tag             INTEGER REFERENCES asset.copy_tag (id)
387                     ON UPDATE CASCADE ON DELETE CASCADE
388 );
389
390 CREATE INDEX asset_copy_tag_copy_map_copy_idx
391     ON asset.copy_tag_copy_map (copy);
392 CREATE INDEX asset_copy_tag_copy_map_tag_idx
393     ON asset.copy_tag_copy_map (tag);
394
395 INSERT INTO config.copy_tag_type (code, label, owner) VALUES ('bookplate', 'Digital Bookplate', 1);
396
397 INSERT INTO permission.perm_list ( id, code, description ) VALUES
398  ( 590, 'ADMIN_COPY_TAG_TYPES', oils_i18n_gettext( 590,
399     'Administer copy tag types', 'ppl', 'description' )),
400  ( 591, 'ADMIN_COPY_TAG', oils_i18n_gettext( 591,
401     'Administer copy tag', 'ppl', 'description' ))
402 ;
403
404 INSERT INTO config.org_unit_setting_type
405     (name, label, description, grp, datatype)
406 VALUES (
407     'opac.search.enable_bookplate_search',
408     oils_i18n_gettext(
409         'opac.search.enable_bookplate_search',
410         'Enable Digital Bookplate Search',
411         'coust',
412         'label'
413     ),
414     oils_i18n_gettext(
415         'opac.search.enable_bookplate_search',
416         'If enabled, adds a "Digital Bookplate" option to the query type selectors in the public catalog for search on copy tags.',   
417         'coust',
418         'description'
419     ),
420     'opac',
421     'bool'
422 );
423
424
425 SELECT evergreen.upgrade_deps_block_check('1048', :eg_version);
426
427 INSERT into config.org_unit_setting_type (
428      name
429     ,grp
430     ,label
431     ,description
432     ,datatype
433 ) VALUES ( ----------------------------------------
434      'webstaff.cat.label.font.family'
435     ,'cat'
436     ,oils_i18n_gettext(
437          'webstaff.cat.label.font.family'
438         ,'Item Print Label Font Family'
439         ,'coust'
440         ,'label'
441     )
442     ,oils_i18n_gettext(
443          'webstaff.cat.label.font.family'
444         ,'Set the preferred font family for item print labels. You can specify a list of CSS fonts, separated by commas, in order of preference; the system will use the first font it finds with a matching name. For example, "Arial, Helvetica, serif"'
445         ,'coust'
446         ,'description'
447     )
448     ,'string'
449 ), ( ----------------------------------------
450      'webstaff.cat.label.font.size'
451     ,'cat'
452     ,oils_i18n_gettext(
453          'webstaff.cat.label.font.size'
454         ,'Item Print Label Font Size'
455         ,'coust'
456         ,'label'
457     )
458     ,oils_i18n_gettext(
459          'webstaff.cat.label.font.size'
460         ,'Set the default font size for item print labels. Please include a unit of measurement that is valid CSS. For example, "12pt" or "16px" or "1em"'
461         ,'coust'
462         ,'description'
463     )
464     ,'string'
465 ), ( ----------------------------------------
466      'webstaff.cat.label.font.weight'
467     ,'cat'
468     ,oils_i18n_gettext(
469          'webstaff.cat.label.font.weight'
470         ,'Item Print Label Font Weight'
471         ,'coust'
472         ,'label'
473     )
474     ,oils_i18n_gettext(
475          'webstaff.cat.label.font.weight'
476         ,'Set the default font weight for item print labels. Please use the CSS specification for values for font-weight.  For example, "normal", "bold", "bolder", or "lighter"'
477         ,'coust'
478         ,'description'
479     )
480     ,'string'
481 ), ( ----------------------------------------
482      'webstaff.cat.label.left_label.left_margin'
483     ,'cat'
484     ,oils_i18n_gettext(
485          'webstaff.cat.label.left_label.left_margin'
486         ,'Item Print Label - Left Margin for Left Label'
487         ,'coust'
488         ,'label'
489     )
490     ,oils_i18n_gettext(
491          'webstaff.cat.label.left_label.left_margin'
492         ,'Set the default left margin for the leftmost item print Label. Please include a unit of measurement that is valid CSS. For example, "1in" or "2.5cm"'
493         ,'coust'
494         ,'description'
495     )
496     ,'string'
497 ), ( ----------------------------------------
498      'webstaff.cat.label.right_label.left_margin'
499     ,'cat'
500     ,oils_i18n_gettext(
501          'webstaff.cat.label.right_label.left_margin'
502         ,'Item Print Label - Left Margin for Right Label'
503         ,'coust'
504         ,'label'
505     )
506     ,oils_i18n_gettext(
507          'webstaff.cat.label.right_label.left_margin'
508         ,'Set the default left margin for the rightmost item print label (or in other words, the desired space between the two labels). Please include a unit of measurement that is valid CSS. For example, "1in" or "2.5cm"'
509         ,'coust'
510         ,'description'
511     )
512     ,'string'
513 ), ( ----------------------------------------
514      'webstaff.cat.label.left_label.height'
515     ,'cat'
516     ,oils_i18n_gettext(
517          'webstaff.cat.label.left_label.height'
518         ,'Item Print Label - Height for Left Label'
519         ,'coust'
520         ,'label'
521     )
522     ,oils_i18n_gettext(
523          'webstaff.cat.label.left_label.height'
524         ,'Set the default height for the leftmost item print label. Please include a unit of measurement that is valid CSS. For example, "1in" or "2.5cm"'
525         ,'coust'
526         ,'description'
527     )
528     ,'string'
529 ), ( ----------------------------------------
530      'webstaff.cat.label.left_label.width'
531     ,'cat'
532     ,oils_i18n_gettext(
533          'webstaff.cat.label.left_label.width'
534         ,'Item Print Label - Width for Left Label'
535         ,'coust'
536         ,'label'
537     )
538     ,oils_i18n_gettext(
539          'webstaff.cat.label.left_label.width'
540         ,'Set the default width for the leftmost item print label. Please include a unit of measurement that is valid CSS. For example, "1in" or "2.5cm"'
541         ,'coust'
542         ,'description'
543     )
544     ,'string'
545 ), ( ----------------------------------------
546      'webstaff.cat.label.right_label.height'
547     ,'cat'
548     ,oils_i18n_gettext(
549          'webstaff.cat.label.right_label.height'
550         ,'Item Print Label - Height for Right Label'
551         ,'coust'
552         ,'label'
553     )
554     ,oils_i18n_gettext(
555          'webstaff.cat.label.right_label.height'
556         ,'Set the default height for the rightmost item print label. Please include a unit of measurement that is valid CSS. For example, "1in" or "2.5cm"'
557         ,'coust'
558         ,'description'
559     )
560     ,'string'
561 ), ( ----------------------------------------
562      'webstaff.cat.label.right_label.width'
563     ,'cat'
564     ,oils_i18n_gettext(
565          'webstaff.cat.label.right_label.width'
566         ,'Item Print Label - Width for Right Label'
567         ,'coust'
568         ,'label'
569     )
570     ,oils_i18n_gettext(
571          'webstaff.cat.label.right_label.width'
572         ,'Set the default width for the rightmost item print label. Please include a unit of measurement that is valid CSS. For example, "1in" or "2.5cm"'
573         ,'coust'
574         ,'description'
575     )
576     ,'string'
577 ), (
578      'webstaff.cat.label.inline_css'
579     ,'cat'
580     ,oils_i18n_gettext(
581          'webstaff.cat.label.inline_css'
582         ,'Item Print Label - Inline CSS'
583         ,'coust'
584         ,'label'
585     )
586     ,oils_i18n_gettext(
587          'webstaff.cat.label.inline_css'
588         ,'This setting allows you to inject arbitrary CSS into the item print label template.  For example, ".printlabel { text-transform: uppercase; }"'
589         ,'coust'
590         ,'description'
591     )
592     ,'string'
593 ), (
594      'webstaff.cat.label.call_number_wrap_filter_height'
595     ,'cat'
596     ,oils_i18n_gettext(
597          'webstaff.cat.label.call_number_wrap_filter_height'
598         ,'Item Print Label - Call Number Wrap Filter Height'
599         ,'coust'
600         ,'label'
601     )
602     ,oils_i18n_gettext(
603          'webstaff.cat.label.call_number_wrap_filter_height'
604         ,'This setting is used to set the default height (in number of lines) to use for call number wrapping in the left print label.'
605         ,'coust'
606         ,'description'
607     )
608     ,'integer'
609 ), (
610      'webstaff.cat.label.call_number_wrap_filter_width'
611     ,'cat'
612     ,oils_i18n_gettext(
613          'webstaff.cat.label.call_number_wrap_filter_width'
614         ,'Item Print Label - Call Number Wrap Filter Width'
615         ,'coust'
616         ,'label'
617     )
618     ,oils_i18n_gettext(
619          'webstaff.cat.label.call_number_wrap_filter_width'
620         ,'This setting is used to set the default width (in number of characters) to use for call number wrapping in the left print label.'
621         ,'coust'
622         ,'description'
623     )
624     ,'integer'
625
626
627 );
628
629 -- for testing, setting removal:
630 --DELETE FROM actor.org_unit_setting WHERE name IN (
631 --     'webstaff.cat.label.font.family'
632 --    ,'webstaff.cat.label.font.size'
633 --    ,'webstaff.cat.label.font.weight'
634 --    ,'webstaff.cat.label.left_label.height'
635 --    ,'webstaff.cat.label.left_label.width'
636 --    ,'webstaff.cat.label.left_label.left_margin'
637 --    ,'webstaff.cat.label.right_label.height'
638 --    ,'webstaff.cat.label.right_label.width'
639 --    ,'webstaff.cat.label.right_label.left_margin'
640 --    ,'webstaff.cat.label.inline_css'
641 --    ,'webstaff.cat.label.call_number_wrap_filter_height'
642 --    ,'webstaff.cat.label.call_number_wrap_filter_width'
643 --);
644 --DELETE FROM config.org_unit_setting_type_log WHERE field_name IN (
645 --     'webstaff.cat.label.font.family'
646 --    ,'webstaff.cat.label.font.size'
647 --    ,'webstaff.cat.label.font.weight'
648 --    ,'webstaff.cat.label.left_label.height'
649 --    ,'webstaff.cat.label.left_label.width'
650 --    ,'webstaff.cat.label.left_label.left_margin'
651 --    ,'webstaff.cat.label.right_label.height'
652 --    ,'webstaff.cat.label.right_label.width'
653 --    ,'webstaff.cat.label.right_label.left_margin'
654 --    ,'webstaff.cat.label.inline_css'
655 --    ,'webstaff.cat.label.call_number_wrap_filter_height'
656 --    ,'webstaff.cat.label.call_number_wrap_filter_width'
657 --);
658 --DELETE FROM config.org_unit_setting_type WHERE name IN (
659 --     'webstaff.cat.label.font.family'
660 --    ,'webstaff.cat.label.font.size'
661 --    ,'webstaff.cat.label.font.weight'
662 --    ,'webstaff.cat.label.left_label.height'
663 --    ,'webstaff.cat.label.left_label.width'
664 --    ,'webstaff.cat.label.left_label.left_margin'
665 --    ,'webstaff.cat.label.right_label.height'
666 --    ,'webstaff.cat.label.right_label.width'
667 --    ,'webstaff.cat.label.right_label.left_margin'
668 --    ,'webstaff.cat.label.inline_css'
669 --    ,'webstaff.cat.label.call_number_wrap_filter_height'
670 --    ,'webstaff.cat.label.call_number_wrap_filter_width'
671 --);
672
673
674
675 SELECT evergreen.upgrade_deps_block_check('1049', :eg_version); -- mmorgan/stompro/gmcharlt
676
677 \echo -----------------------------------------------------------
678 \echo Setting invalid age_protect and circ_as_type entries to NULL,
679 \echo otherwise they will break the Serial Copy Templates editor.
680 \echo Please review any Serial Copy Templates listed below.
681 \echo
682 UPDATE asset.copy_template act
683 SET age_protect = NULL
684 FROM actor.org_unit aou
685 WHERE aou.id=act.owning_lib
686    AND act.age_protect NOT IN
687    (
688    SELECT id FROM config.rule_age_hold_protect
689    )
690 RETURNING act.id "Template ID", act.name "Template Name",
691           aou.shortname "Owning Lib",
692           'Age Protection value reset to null.' "Description";
693
694 UPDATE asset.copy_template act
695 SET circ_as_type = NULL
696 FROM actor.org_unit aou
697 WHERE aou.id=act.owning_lib
698    AND act.circ_as_type NOT IN
699    (
700    SELECT code FROM config.item_type_map
701    )
702 RETURNING act.id "Template ID", act.name "Template Name",
703           aou.shortname "Owning Lib",
704           'Circ as Type value reset to null.' as "Description";
705
706 \echo -----------End Serial Template Fix----------------
707
708 SELECT evergreen.upgrade_deps_block_check('1050', :eg_version); -- mmorgan/cesardv/gmcharlt
709
710 CREATE OR REPLACE FUNCTION permission.usr_perms ( INT ) RETURNS SETOF permission.usr_perm_map AS $$
711     SELECT      DISTINCT ON (usr,perm) *
712           FROM  (
713                         (SELECT * FROM permission.usr_perm_map WHERE usr = $1)
714             UNION ALL
715                         (SELECT -p.id, $1 AS usr, p.perm, p.depth, p.grantable
716                           FROM  permission.grp_perm_map p
717                           WHERE p.grp IN (
718       SELECT    (permission.grp_ancestors(
719       (SELECT profile FROM actor.usr WHERE id = $1)
720                                         )).id
721                                 )
722                         )
723             UNION ALL
724                         (SELECT -p.id, $1 AS usr, p.perm, p.depth, p.grantable
725                           FROM  permission.grp_perm_map p
726                           WHERE p.grp IN (SELECT (permission.grp_ancestors(m.grp)).id FROM permission.usr_grp_map m WHERE usr = $1))
727                 ) AS x
728           ORDER BY 2, 3, 4 ASC, 5 DESC ;
729 $$ LANGUAGE SQL STABLE ROWS 10;
730
731 SELECT evergreen.upgrade_deps_block_check('1051', :eg_version);
732
733 CREATE OR REPLACE VIEW action.all_circulation_slim AS
734     SELECT
735         id,
736         usr,
737         xact_start,
738         xact_finish,
739         unrecovered,
740         target_copy,
741         circ_lib,
742         circ_staff,
743         checkin_staff,
744         checkin_lib,
745         renewal_remaining,
746         grace_period,
747         due_date,
748         stop_fines_time,
749         checkin_time,
750         create_time,
751         duration,
752         fine_interval,
753         recurring_fine,
754         max_fine,
755         phone_renewal,
756         desk_renewal,
757         opac_renewal,
758         duration_rule,
759         recurring_fine_rule,
760         max_fine_rule,
761         stop_fines,
762         workstation,
763         checkin_workstation,
764         copy_location,
765         checkin_scan_time,
766         parent_circ
767     FROM action.circulation
768 UNION ALL
769     SELECT
770         id,
771         NULL AS usr,
772         xact_start,
773         xact_finish,
774         unrecovered,
775         target_copy,
776         circ_lib,
777         circ_staff,
778         checkin_staff,
779         checkin_lib,
780         renewal_remaining,
781         grace_period,
782         due_date,
783         stop_fines_time,
784         checkin_time,
785         create_time,
786         duration,
787         fine_interval,
788         recurring_fine,
789         max_fine,
790         phone_renewal,
791         desk_renewal,
792         opac_renewal,
793         duration_rule,
794         recurring_fine_rule,
795         max_fine_rule,
796         stop_fines,
797         workstation,
798         checkin_workstation,
799         copy_location,
800         checkin_scan_time,
801         parent_circ
802     FROM action.aged_circulation
803 ;
804
805 DROP FUNCTION action.summarize_all_circ_chain(INTEGER);
806 DROP FUNCTION action.all_circ_chain(INTEGER);
807
808 CREATE OR REPLACE FUNCTION action.all_circ_chain (ctx_circ_id INTEGER) 
809     RETURNS SETOF action.all_circulation_slim AS $$
810 DECLARE
811     tmp_circ action.all_circulation_slim%ROWTYPE;
812     circ_0 action.all_circulation_slim%ROWTYPE;
813 BEGIN
814
815     SELECT INTO tmp_circ * FROM action.all_circulation_slim WHERE id = ctx_circ_id;
816
817     IF tmp_circ IS NULL THEN
818         RETURN NEXT tmp_circ;
819     END IF;
820     circ_0 := tmp_circ;
821
822     -- find the front of the chain
823     WHILE TRUE LOOP
824         SELECT INTO tmp_circ * FROM action.all_circulation_slim 
825             WHERE id = tmp_circ.parent_circ;
826         IF tmp_circ IS NULL THEN
827             EXIT;
828         END IF;
829         circ_0 := tmp_circ;
830     END LOOP;
831
832     -- now send the circs to the caller, oldest to newest
833     tmp_circ := circ_0;
834     WHILE TRUE LOOP
835         IF tmp_circ IS NULL THEN
836             EXIT;
837         END IF;
838         RETURN NEXT tmp_circ;
839         SELECT INTO tmp_circ * FROM action.all_circulation_slim 
840             WHERE parent_circ = tmp_circ.id;
841     END LOOP;
842
843 END;
844 $$ LANGUAGE 'plpgsql';
845
846 CREATE OR REPLACE FUNCTION action.summarize_all_circ_chain 
847     (ctx_circ_id INTEGER) RETURNS action.circ_chain_summary AS $$
848
849 DECLARE
850
851     -- first circ in the chain
852     circ_0 action.all_circulation_slim%ROWTYPE;
853
854     -- last circ in the chain
855     circ_n action.all_circulation_slim%ROWTYPE;
856
857     -- circ chain under construction
858     chain action.circ_chain_summary;
859     tmp_circ action.all_circulation_slim%ROWTYPE;
860
861 BEGIN
862     
863     chain.num_circs := 0;
864     FOR tmp_circ IN SELECT * FROM action.all_circ_chain(ctx_circ_id) LOOP
865
866         IF chain.num_circs = 0 THEN
867             circ_0 := tmp_circ;
868         END IF;
869
870         chain.num_circs := chain.num_circs + 1;
871         circ_n := tmp_circ;
872     END LOOP;
873
874     chain.start_time := circ_0.xact_start;
875     chain.last_stop_fines := circ_n.stop_fines;
876     chain.last_stop_fines_time := circ_n.stop_fines_time;
877     chain.last_checkin_time := circ_n.checkin_time;
878     chain.last_checkin_scan_time := circ_n.checkin_scan_time;
879     SELECT INTO chain.checkout_workstation name FROM actor.workstation WHERE id = circ_0.workstation;
880     SELECT INTO chain.last_checkin_workstation name FROM actor.workstation WHERE id = circ_n.checkin_workstation;
881
882     IF chain.num_circs > 1 THEN
883         chain.last_renewal_time := circ_n.xact_start;
884         SELECT INTO chain.last_renewal_workstation name FROM actor.workstation WHERE id = circ_n.workstation;
885     END IF;
886
887     RETURN chain;
888
889 END;
890 $$ LANGUAGE 'plpgsql';
891
892 CREATE OR REPLACE FUNCTION rating.percent_time_circulating(badge_id INT)
893     RETURNS TABLE (record BIGINT, value NUMERIC) AS $f$
894 DECLARE
895     badge   rating.badge_with_orgs%ROWTYPE;
896 BEGIN
897
898     SELECT * INTO badge FROM rating.badge_with_orgs WHERE id = badge_id;
899
900     PERFORM rating.precalc_bibs_by_copy(badge_id);
901
902     DELETE FROM precalc_copy_filter_bib_list WHERE id NOT IN (
903         SELECT id FROM precalc_filter_bib_list
904             INTERSECT
905         SELECT id FROM precalc_bibs_by_copy_list
906     );
907
908     ANALYZE precalc_copy_filter_bib_list;
909
910     RETURN QUERY
911      SELECT bib,
912             SUM(COALESCE(circ_time,0))::NUMERIC / SUM(age)::NUMERIC
913       FROM  (SELECT cn.record AS bib,
914                     cp.id,
915                     EXTRACT( EPOCH FROM AGE(cp.active_date) ) + 1 AS age,
916                     SUM(  -- time copy spent circulating
917                         EXTRACT(
918                             EPOCH FROM
919                             AGE(
920                                 COALESCE(circ.checkin_time, circ.stop_fines_time, NOW()),
921                                 circ.xact_start
922                             )
923                         )
924                     )::NUMERIC AS circ_time
925               FROM  asset.copy cp
926                     JOIN precalc_copy_filter_bib_list c ON (cp.id = c.copy)
927                     JOIN asset.call_number cn ON (cn.id = cp.call_number)
928                     LEFT JOIN action.all_circulation_slim circ ON (
929                         circ.target_copy = cp.id
930                         AND stop_fines NOT IN (
931                             'LOST',
932                             'LONGOVERDUE',
933                             'CLAIMSRETURNED',
934                             'LONGOVERDUE'
935                         )
936                         AND NOT (
937                             checkin_time IS NULL AND
938                             stop_fines = 'MAXFINES'
939                         )
940                     )
941               WHERE cn.owning_lib = ANY (badge.orgs)
942                     AND cp.active_date IS NOT NULL
943                     -- Next line requires that copies with no circs (circ.id IS NULL) also not be deleted
944                     AND ((circ.id IS NULL AND NOT cp.deleted) OR circ.id IS NOT NULL)
945               GROUP BY 1,2,3
946             ) x
947       GROUP BY 1;
948 END;
949 $f$ LANGUAGE PLPGSQL STRICT;
950
951
952 -- ROLLBACK;
953
954
955 SELECT evergreen.upgrade_deps_block_check('1052', :eg_version);
956
957 CREATE OR REPLACE FUNCTION rating.inhouse_over_time(badge_id INT)
958     RETURNS TABLE (record BIGINT, value NUMERIC) AS $f$
959 DECLARE
960     badge   rating.badge_with_orgs%ROWTYPE;
961     iage    INT     := 1;
962     iint    INT     := NULL;
963     iscale  NUMERIC := NULL;
964 BEGIN
965
966     SELECT * INTO badge FROM rating.badge_with_orgs WHERE id = badge_id;
967
968     IF badge.horizon_age IS NULL THEN
969         RAISE EXCEPTION 'Badge "%" with id % requires a horizon age but has none.',
970             badge.name,
971             badge.id;
972     END IF;
973
974     PERFORM rating.precalc_bibs_by_copy(badge_id);
975
976     DELETE FROM precalc_copy_filter_bib_list WHERE id NOT IN (
977         SELECT id FROM precalc_filter_bib_list
978             INTERSECT
979         SELECT id FROM precalc_bibs_by_copy_list
980     );
981
982     ANALYZE precalc_copy_filter_bib_list;
983
984     iint := EXTRACT(EPOCH FROM badge.importance_interval);
985     IF badge.importance_age IS NOT NULL THEN
986         iage := (EXTRACT(EPOCH FROM badge.importance_age) / iint)::INT;
987     END IF;
988
989     -- if iscale is smaller than 1, scaling slope will be shallow ... BEWARE!
990     iscale := COALESCE(badge.importance_scale, 1.0);
991
992     RETURN QUERY
993      SELECT bib,
994             SUM( uses * GREATEST( iscale * (iage - cage), 1.0 ))
995       FROM (
996          SELECT cn.record AS bib,
997                 (1 + EXTRACT(EPOCH FROM AGE(u.use_time)) / iint)::INT AS cage,
998                 COUNT(u.id)::INT AS uses
999           FROM  action.in_house_use u
1000                 JOIN precalc_copy_filter_bib_list cf ON (u.item = cf.copy)
1001                 JOIN asset.copy cp ON (cp.id = u.item)
1002                 JOIN asset.call_number cn ON (cn.id = cp.call_number)
1003           WHERE u.use_time >= NOW() - badge.horizon_age
1004                 AND cn.owning_lib = ANY (badge.orgs)
1005           GROUP BY 1, 2
1006       ) x
1007       GROUP BY 1;
1008 END;
1009 $f$ LANGUAGE PLPGSQL STRICT;
1010
1011 INSERT INTO rating.popularity_parameter (id, name, func, require_horizon,require_percentile) VALUES
1012     (18,'In-House Use Over Time', 'rating.inhouse_over_time', TRUE, TRUE);
1013
1014
1015
1016 SELECT evergreen.upgrade_deps_block_check('1053', :eg_version);
1017
1018 CREATE OR REPLACE FUNCTION rating.org_unit_count(badge_id INT)
1019     RETURNS TABLE (record INT, value NUMERIC) AS $f$
1020 DECLARE
1021     badge   rating.badge_with_orgs%ROWTYPE;
1022 BEGIN
1023
1024     SELECT * INTO badge FROM rating.badge_with_orgs WHERE id = badge_id;
1025
1026     PERFORM rating.precalc_bibs_by_copy(badge_id);
1027
1028     DELETE FROM precalc_copy_filter_bib_list WHERE id NOT IN (
1029         SELECT id FROM precalc_filter_bib_list
1030             INTERSECT
1031         SELECT id FROM precalc_bibs_by_copy_list
1032     );
1033     ANALYZE precalc_copy_filter_bib_list;
1034
1035     -- Use circ rather than owning lib here as that means "on the shelf at..."
1036     RETURN QUERY
1037      SELECT f.id::INT AS bib,
1038             COUNT(DISTINCT cp.circ_lib)::NUMERIC
1039      FROM asset.copy cp
1040           JOIN precalc_copy_filter_bib_list f ON (cp.id = f.copy)
1041      WHERE cp.circ_lib = ANY (badge.orgs) GROUP BY 1;
1042
1043 END;
1044 $f$ LANGUAGE PLPGSQL STRICT;
1045
1046 INSERT INTO rating.popularity_parameter (id, name, func, require_percentile) VALUES
1047     (17,'Circulation Library Count', 'rating.org_unit_count', TRUE);
1048
1049
1050
1051 SELECT evergreen.upgrade_deps_block_check('1054', :eg_version);
1052
1053 INSERT into config.org_unit_setting_type
1054 ( name, grp, label, description, datatype ) VALUES
1055
1056 ( 'lib.timezone', 'lib',
1057     oils_i18n_gettext('lib.timezone',
1058         'Library time zone',
1059         'coust', 'label'),
1060     oils_i18n_gettext('lib.timezone',
1061         'Define the time zone in which a library physically resides',
1062         'coust', 'description'),
1063     'string');
1064
1065 ALTER TABLE actor.org_unit_closed ADD COLUMN full_day BOOLEAN DEFAULT FALSE;
1066 ALTER TABLE actor.org_unit_closed ADD COLUMN multi_day BOOLEAN DEFAULT FALSE;
1067
1068 UPDATE actor.org_unit_closed SET multi_day = TRUE
1069   WHERE close_start::DATE <> close_end::DATE;
1070
1071 UPDATE actor.org_unit_closed SET full_day = TRUE
1072   WHERE close_start::DATE = close_end::DATE
1073         AND SUBSTRING(close_start::time::text FROM 1 FOR 8) = '00:00:00'
1074         AND SUBSTRING(close_end::time::text FROM 1 FOR 8) = '23:59:59';
1075
1076 CREATE OR REPLACE FUNCTION action.push_circ_due_time () RETURNS TRIGGER AS $$
1077 DECLARE
1078     proper_tz TEXT := COALESCE(
1079         oils_json_to_text((
1080             SELECT value
1081               FROM  actor.org_unit_ancestor_setting('lib.timezone',NEW.circ_lib)
1082               LIMIT 1
1083         )),
1084         CURRENT_SETTING('timezone')
1085     );
1086 BEGIN
1087
1088     IF (EXTRACT(EPOCH FROM NEW.duration)::INT % EXTRACT(EPOCH FROM '1 day'::INTERVAL)::INT) = 0 -- day-granular duration
1089         AND SUBSTRING((NEW.due_date AT TIME ZONE proper_tz)::TIME::TEXT FROM 1 FOR 8) <> '23:59:59' THEN -- has not yet been pushed
1090         NEW.due_date = ((NEW.due_date AT TIME ZONE proper_tz)::DATE + '1 day'::INTERVAL - '1 second'::INTERVAL) || ' ' || proper_tz;
1091     END IF;
1092
1093     RETURN NEW;
1094 END;
1095 $$ LANGUAGE PLPGSQL;
1096
1097
1098 \qecho The following query will adjust all historical, unaged circulations so
1099 \qecho that if their due date field is pushed to the end of the day, it is done
1100 \qecho in the circulating library''''s time zone, and not the server time zone.
1101 \qecho 
1102 \qecho It is safe to run this after any change to library time zones.
1103 \qecho 
1104 \qecho Running this is not required, as no code before this change has
1105 \qecho depended on the time string of '''23:59:59'''.  It is also not necessary
1106 \qecho if all of your libraries are in the same time zone, and that time zone
1107 \qecho is the same as the database''''s configured time zone.
1108 \qecho 
1109 \qecho 'DO $$'
1110 \qecho 'declare'
1111 \qecho '    new_tz  text;'
1112 \qecho '    ou_id   int;'
1113 \qecho 'begin'
1114 \qecho '    for ou_id in select id from actor.org_unit loop'
1115 \qecho '        for new_tz in select oils_json_to_text(value) from actor.org_unit_ancestor_setting('''lib.timezone''',ou_id) loop'
1116 \qecho '            if new_tz is not null then'
1117 \qecho '                update  action.circulation'
1118 \qecho '                  set   due_date = (due_date::timestamp || ''' ''' || new_tz)::timestamptz'
1119 \qecho '                  where circ_lib = ou_id'
1120 \qecho '                        and substring((due_date at time zone new_tz)::time::text from 1 for 8) <> '''23:59:59''';'
1121 \qecho '            end if;'
1122 \qecho '        end loop;'
1123 \qecho '    end loop;'
1124 \qecho 'end;'
1125 \qecho '$$;'
1126 \qecho 
1127
1128 SELECT evergreen.upgrade_deps_block_check('1055', :eg_version);
1129
1130 CREATE OR REPLACE FUNCTION actor.usr_merge( src_usr INT, dest_usr INT, del_addrs BOOLEAN, del_cards BOOLEAN, deactivate_cards BOOLEAN ) RETURNS VOID AS $$
1131 DECLARE
1132         suffix TEXT;
1133         bucket_row RECORD;
1134         picklist_row RECORD;
1135         queue_row RECORD;
1136         folder_row RECORD;
1137 BEGIN
1138
1139     -- do some initial cleanup 
1140     UPDATE actor.usr SET card = NULL WHERE id = src_usr;
1141     UPDATE actor.usr SET mailing_address = NULL WHERE id = src_usr;
1142     UPDATE actor.usr SET billing_address = NULL WHERE id = src_usr;
1143
1144     -- actor.*
1145     IF del_cards THEN
1146         DELETE FROM actor.card where usr = src_usr;
1147     ELSE
1148         IF deactivate_cards THEN
1149             UPDATE actor.card SET active = 'f' WHERE usr = src_usr;
1150         END IF;
1151         UPDATE actor.card SET usr = dest_usr WHERE usr = src_usr;
1152     END IF;
1153
1154
1155     IF del_addrs THEN
1156         DELETE FROM actor.usr_address WHERE usr = src_usr;
1157     ELSE
1158         UPDATE actor.usr_address SET usr = dest_usr WHERE usr = src_usr;
1159     END IF;
1160
1161     UPDATE actor.usr_note SET usr = dest_usr WHERE usr = src_usr;
1162     -- dupes are technically OK in actor.usr_standing_penalty, should manually delete them...
1163     UPDATE actor.usr_standing_penalty SET usr = dest_usr WHERE usr = src_usr;
1164     PERFORM actor.usr_merge_rows('actor.usr_org_unit_opt_in', 'usr', src_usr, dest_usr);
1165     PERFORM actor.usr_merge_rows('actor.usr_setting', 'usr', src_usr, dest_usr);
1166
1167     -- permission.*
1168     PERFORM actor.usr_merge_rows('permission.usr_perm_map', 'usr', src_usr, dest_usr);
1169     PERFORM actor.usr_merge_rows('permission.usr_object_perm_map', 'usr', src_usr, dest_usr);
1170     PERFORM actor.usr_merge_rows('permission.usr_grp_map', 'usr', src_usr, dest_usr);
1171     PERFORM actor.usr_merge_rows('permission.usr_work_ou_map', 'usr', src_usr, dest_usr);
1172
1173
1174     -- container.*
1175         
1176         -- For each *_bucket table: transfer every bucket belonging to src_usr
1177         -- into the custody of dest_usr.
1178         --
1179         -- In order to avoid colliding with an existing bucket owned by
1180         -- the destination user, append the source user's id (in parenthesese)
1181         -- to the name.  If you still get a collision, add successive
1182         -- spaces to the name and keep trying until you succeed.
1183         --
1184         FOR bucket_row in
1185                 SELECT id, name
1186                 FROM   container.biblio_record_entry_bucket
1187                 WHERE  owner = src_usr
1188         LOOP
1189                 suffix := ' (' || src_usr || ')';
1190                 LOOP
1191                         BEGIN
1192                                 UPDATE  container.biblio_record_entry_bucket
1193                                 SET     owner = dest_usr, name = name || suffix
1194                                 WHERE   id = bucket_row.id;
1195                         EXCEPTION WHEN unique_violation THEN
1196                                 suffix := suffix || ' ';
1197                                 CONTINUE;
1198                         END;
1199                         EXIT;
1200                 END LOOP;
1201         END LOOP;
1202
1203         FOR bucket_row in
1204                 SELECT id, name
1205                 FROM   container.call_number_bucket
1206                 WHERE  owner = src_usr
1207         LOOP
1208                 suffix := ' (' || src_usr || ')';
1209                 LOOP
1210                         BEGIN
1211                                 UPDATE  container.call_number_bucket
1212                                 SET     owner = dest_usr, name = name || suffix
1213                                 WHERE   id = bucket_row.id;
1214                         EXCEPTION WHEN unique_violation THEN
1215                                 suffix := suffix || ' ';
1216                                 CONTINUE;
1217                         END;
1218                         EXIT;
1219                 END LOOP;
1220         END LOOP;
1221
1222         FOR bucket_row in
1223                 SELECT id, name
1224                 FROM   container.copy_bucket
1225                 WHERE  owner = src_usr
1226         LOOP
1227                 suffix := ' (' || src_usr || ')';
1228                 LOOP
1229                         BEGIN
1230                                 UPDATE  container.copy_bucket
1231                                 SET     owner = dest_usr, name = name || suffix
1232                                 WHERE   id = bucket_row.id;
1233                         EXCEPTION WHEN unique_violation THEN
1234                                 suffix := suffix || ' ';
1235                                 CONTINUE;
1236                         END;
1237                         EXIT;
1238                 END LOOP;
1239         END LOOP;
1240
1241         FOR bucket_row in
1242                 SELECT id, name
1243                 FROM   container.user_bucket
1244                 WHERE  owner = src_usr
1245         LOOP
1246                 suffix := ' (' || src_usr || ')';
1247                 LOOP
1248                         BEGIN
1249                                 UPDATE  container.user_bucket
1250                                 SET     owner = dest_usr, name = name || suffix
1251                                 WHERE   id = bucket_row.id;
1252                         EXCEPTION WHEN unique_violation THEN
1253                                 suffix := suffix || ' ';
1254                                 CONTINUE;
1255                         END;
1256                         EXIT;
1257                 END LOOP;
1258         END LOOP;
1259
1260         UPDATE container.user_bucket_item SET target_user = dest_usr WHERE target_user = src_usr;
1261
1262     -- vandelay.*
1263         -- transfer queues the same way we transfer buckets (see above)
1264         FOR queue_row in
1265                 SELECT id, name
1266                 FROM   vandelay.queue
1267                 WHERE  owner = src_usr
1268         LOOP
1269                 suffix := ' (' || src_usr || ')';
1270                 LOOP
1271                         BEGIN
1272                                 UPDATE  vandelay.queue
1273                                 SET     owner = dest_usr, name = name || suffix
1274                                 WHERE   id = queue_row.id;
1275                         EXCEPTION WHEN unique_violation THEN
1276                                 suffix := suffix || ' ';
1277                                 CONTINUE;
1278                         END;
1279                         EXIT;
1280                 END LOOP;
1281         END LOOP;
1282
1283     -- money.*
1284     PERFORM actor.usr_merge_rows('money.collections_tracker', 'usr', src_usr, dest_usr);
1285     PERFORM actor.usr_merge_rows('money.collections_tracker', 'collector', src_usr, dest_usr);
1286     UPDATE money.billable_xact SET usr = dest_usr WHERE usr = src_usr;
1287     UPDATE money.billing SET voider = dest_usr WHERE voider = src_usr;
1288     UPDATE money.bnm_payment SET accepting_usr = dest_usr WHERE accepting_usr = src_usr;
1289
1290     -- action.*
1291     UPDATE action.circulation SET usr = dest_usr WHERE usr = src_usr;
1292     UPDATE action.circulation SET circ_staff = dest_usr WHERE circ_staff = src_usr;
1293     UPDATE action.circulation SET checkin_staff = dest_usr WHERE checkin_staff = src_usr;
1294     UPDATE action.usr_circ_history SET usr = dest_usr WHERE usr = src_usr;
1295
1296     UPDATE action.hold_request SET usr = dest_usr WHERE usr = src_usr;
1297     UPDATE action.hold_request SET fulfillment_staff = dest_usr WHERE fulfillment_staff = src_usr;
1298     UPDATE action.hold_request SET requestor = dest_usr WHERE requestor = src_usr;
1299     UPDATE action.hold_notification SET notify_staff = dest_usr WHERE notify_staff = src_usr;
1300
1301     UPDATE action.in_house_use SET staff = dest_usr WHERE staff = src_usr;
1302     UPDATE action.non_cataloged_circulation SET staff = dest_usr WHERE staff = src_usr;
1303     UPDATE action.non_cataloged_circulation SET patron = dest_usr WHERE patron = src_usr;
1304     UPDATE action.non_cat_in_house_use SET staff = dest_usr WHERE staff = src_usr;
1305     UPDATE action.survey_response SET usr = dest_usr WHERE usr = src_usr;
1306
1307     -- acq.*
1308     UPDATE acq.fund_allocation SET allocator = dest_usr WHERE allocator = src_usr;
1309         UPDATE acq.fund_transfer SET transfer_user = dest_usr WHERE transfer_user = src_usr;
1310
1311         -- transfer picklists the same way we transfer buckets (see above)
1312         FOR picklist_row in
1313                 SELECT id, name
1314                 FROM   acq.picklist
1315                 WHERE  owner = src_usr
1316         LOOP
1317                 suffix := ' (' || src_usr || ')';
1318                 LOOP
1319                         BEGIN
1320                                 UPDATE  acq.picklist
1321                                 SET     owner = dest_usr, name = name || suffix
1322                                 WHERE   id = picklist_row.id;
1323                         EXCEPTION WHEN unique_violation THEN
1324                                 suffix := suffix || ' ';
1325                                 CONTINUE;
1326                         END;
1327                         EXIT;
1328                 END LOOP;
1329         END LOOP;
1330
1331     UPDATE acq.purchase_order SET owner = dest_usr WHERE owner = src_usr;
1332     UPDATE acq.po_note SET creator = dest_usr WHERE creator = src_usr;
1333     UPDATE acq.po_note SET editor = dest_usr WHERE editor = src_usr;
1334     UPDATE acq.provider_note SET creator = dest_usr WHERE creator = src_usr;
1335     UPDATE acq.provider_note SET editor = dest_usr WHERE editor = src_usr;
1336     UPDATE acq.lineitem_note SET creator = dest_usr WHERE creator = src_usr;
1337     UPDATE acq.lineitem_note SET editor = dest_usr WHERE editor = src_usr;
1338     UPDATE acq.lineitem_usr_attr_definition SET usr = dest_usr WHERE usr = src_usr;
1339
1340     -- asset.*
1341     UPDATE asset.copy SET creator = dest_usr WHERE creator = src_usr;
1342     UPDATE asset.copy SET editor = dest_usr WHERE editor = src_usr;
1343     UPDATE asset.copy_note SET creator = dest_usr WHERE creator = src_usr;
1344     UPDATE asset.call_number SET creator = dest_usr WHERE creator = src_usr;
1345     UPDATE asset.call_number SET editor = dest_usr WHERE editor = src_usr;
1346     UPDATE asset.call_number_note SET creator = dest_usr WHERE creator = src_usr;
1347
1348     -- serial.*
1349     UPDATE serial.record_entry SET creator = dest_usr WHERE creator = src_usr;
1350     UPDATE serial.record_entry SET editor = dest_usr WHERE editor = src_usr;
1351
1352     -- reporter.*
1353     -- It's not uncommon to define the reporter schema in a replica 
1354     -- DB only, so don't assume these tables exist in the write DB.
1355     BEGIN
1356         UPDATE reporter.template SET owner = dest_usr WHERE owner = src_usr;
1357     EXCEPTION WHEN undefined_table THEN
1358         -- do nothing
1359     END;
1360     BEGIN
1361         UPDATE reporter.report SET owner = dest_usr WHERE owner = src_usr;
1362     EXCEPTION WHEN undefined_table THEN
1363         -- do nothing
1364     END;
1365     BEGIN
1366         UPDATE reporter.schedule SET runner = dest_usr WHERE runner = src_usr;
1367     EXCEPTION WHEN undefined_table THEN
1368         -- do nothing
1369     END;
1370     BEGIN
1371                 -- transfer folders the same way we transfer buckets (see above)
1372                 FOR folder_row in
1373                         SELECT id, name
1374                         FROM   reporter.template_folder
1375                         WHERE  owner = src_usr
1376                 LOOP
1377                         suffix := ' (' || src_usr || ')';
1378                         LOOP
1379                                 BEGIN
1380                                         UPDATE  reporter.template_folder
1381                                         SET     owner = dest_usr, name = name || suffix
1382                                         WHERE   id = folder_row.id;
1383                                 EXCEPTION WHEN unique_violation THEN
1384                                         suffix := suffix || ' ';
1385                                         CONTINUE;
1386                                 END;
1387                                 EXIT;
1388                         END LOOP;
1389                 END LOOP;
1390     EXCEPTION WHEN undefined_table THEN
1391         -- do nothing
1392     END;
1393     BEGIN
1394                 -- transfer folders the same way we transfer buckets (see above)
1395                 FOR folder_row in
1396                         SELECT id, name
1397                         FROM   reporter.report_folder
1398                         WHERE  owner = src_usr
1399                 LOOP
1400                         suffix := ' (' || src_usr || ')';
1401                         LOOP
1402                                 BEGIN
1403                                         UPDATE  reporter.report_folder
1404                                         SET     owner = dest_usr, name = name || suffix
1405                                         WHERE   id = folder_row.id;
1406                                 EXCEPTION WHEN unique_violation THEN
1407                                         suffix := suffix || ' ';
1408                                         CONTINUE;
1409                                 END;
1410                                 EXIT;
1411                         END LOOP;
1412                 END LOOP;
1413     EXCEPTION WHEN undefined_table THEN
1414         -- do nothing
1415     END;
1416     BEGIN
1417                 -- transfer folders the same way we transfer buckets (see above)
1418                 FOR folder_row in
1419                         SELECT id, name
1420                         FROM   reporter.output_folder
1421                         WHERE  owner = src_usr
1422                 LOOP
1423                         suffix := ' (' || src_usr || ')';
1424                         LOOP
1425                                 BEGIN
1426                                         UPDATE  reporter.output_folder
1427                                         SET     owner = dest_usr, name = name || suffix
1428                                         WHERE   id = folder_row.id;
1429                                 EXCEPTION WHEN unique_violation THEN
1430                                         suffix := suffix || ' ';
1431                                         CONTINUE;
1432                                 END;
1433                                 EXIT;
1434                         END LOOP;
1435                 END LOOP;
1436     EXCEPTION WHEN undefined_table THEN
1437         -- do nothing
1438     END;
1439
1440     -- Finally, delete the source user
1441     DELETE FROM actor.usr WHERE id = src_usr;
1442
1443 END;
1444 $$ LANGUAGE plpgsql;
1445
1446
1447
1448
1449
1450 INSERT INTO config.upgrade_log (version, applied_to) VALUES ('1056', :eg_version); -- miker/gmcharlt
1451
1452 INSERT INTO permission.perm_list (id,code,description) VALUES (592,'CONTAINER_BATCH_UPDATE','Allow batch update via buckets');
1453
1454 INSERT INTO container.user_bucket_type (code,label) SELECT code,label FROM container.copy_bucket_type where code = 'staff_client';
1455
1456 CREATE TABLE action.fieldset_group (
1457     id              SERIAL  PRIMARY KEY,
1458     name            TEXT        NOT NULL,
1459     create_time     TIMESTAMPTZ NOT NULL DEFAULT NOW(),
1460     complete_time   TIMESTAMPTZ,
1461     container       INT,        -- Points to a container of some type ...
1462     container_type  TEXT,       -- One of 'biblio_record_entry', 'user', 'call_number', 'copy'
1463     can_rollback    BOOL        DEFAULT TRUE,
1464     rollback_group  INT         REFERENCES action.fieldset_group (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
1465     rollback_time   TIMESTAMPTZ,
1466     creator         INT         NOT NULL REFERENCES actor.usr (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
1467     owning_lib      INT         NOT NULL REFERENCES actor.org_unit (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED
1468 );
1469
1470 ALTER TABLE action.fieldset ADD COLUMN fieldset_group INT REFERENCES action.fieldset_group (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED;
1471 ALTER TABLE action.fieldset ADD COLUMN error_msg TEXT;
1472 ALTER TABLE container.biblio_record_entry_bucket ADD COLUMN owning_lib INT REFERENCES actor.org_unit (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED;
1473 ALTER TABLE container.user_bucket ADD COLUMN owning_lib INT REFERENCES actor.org_unit (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED;
1474 ALTER TABLE container.call_number_bucket ADD COLUMN owning_lib INT REFERENCES actor.org_unit (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED;
1475 ALTER TABLE container.copy_bucket ADD COLUMN owning_lib INT REFERENCES actor.org_unit (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED;
1476
1477 UPDATE query.stored_query SET id = id + 1000 WHERE id < 1000;
1478 UPDATE query.from_relation SET id = id + 1000 WHERE id < 1000;
1479 UPDATE query.expression SET id = id + 1000 WHERE id < 1000;
1480
1481 SELECT SETVAL('query.stored_query_id_seq', 1, FALSE);
1482 SELECT SETVAL('query.from_relation_id_seq', 1, FALSE);
1483 SELECT SETVAL('query.expression_id_seq', 1, FALSE);
1484
1485 INSERT INTO query.bind_variable (name,type,description,label)
1486     SELECT  'bucket','number','ID of the bucket to pull items from','Bucket ID'
1487       WHERE NOT EXISTS (SELECT 1 FROM query.bind_variable WHERE name = 'bucket');
1488
1489 -- Assumes completely empty 'query' schema
1490 INSERT INTO query.stored_query (type, use_distinct) VALUES ('SELECT', TRUE); -- 1
1491
1492 INSERT INTO query.from_relation (type, table_name, class_name, table_alias) VALUES ('RELATION', 'container.user_bucket_item', 'cubi', 'cubi'); -- 1
1493 UPDATE query.stored_query SET from_clause = 1;
1494
1495 INSERT INTO query.expr_xcol (table_alias, column_name) VALUES ('cubi', 'target_user'); -- 1
1496 INSERT INTO query.select_item (stored_query,seq_no,expression) VALUES (1,1,1);
1497
1498 INSERT INTO query.expr_xcol (table_alias, column_name) VALUES ('cubi', 'bucket'); -- 2
1499 INSERT INTO query.expr_xbind (bind_variable) VALUES ('bucket'); -- 3
1500
1501 INSERT INTO query.expr_xop (left_operand, operator, right_operand) VALUES (2, '=', 3); -- 4
1502 UPDATE query.stored_query SET where_clause = 4;
1503
1504 SELECT SETVAL('query.stored_query_id_seq', 1000, TRUE) FROM query.stored_query;
1505 SELECT SETVAL('query.from_relation_id_seq', 1000, TRUE) FROM query.from_relation;
1506 SELECT SETVAL('query.expression_id_seq', 10000, TRUE) FROM query.expression;
1507
1508 CREATE OR REPLACE FUNCTION action.apply_fieldset(
1509     fieldset_id IN INT,        -- id from action.fieldset
1510     table_name  IN TEXT,       -- table to be updated
1511     pkey_name   IN TEXT,       -- name of primary key column in that table
1512     query       IN TEXT        -- query constructed by qstore (for query-based
1513                                --    fieldsets only; otherwise null
1514 )
1515 RETURNS TEXT AS $$
1516 DECLARE
1517     statement TEXT;
1518     where_clause TEXT;
1519     fs_status TEXT;
1520     fs_pkey_value TEXT;
1521     fs_query TEXT;
1522     sep CHAR;
1523     status_code TEXT;
1524     msg TEXT;
1525     fs_id INT;
1526     fsg_id INT;
1527     update_count INT;
1528     cv RECORD;
1529     fs_obj action.fieldset%ROWTYPE;
1530     fs_group action.fieldset_group%ROWTYPE;
1531     rb_row RECORD;
1532 BEGIN
1533     -- Sanity checks
1534     IF fieldset_id IS NULL THEN
1535         RETURN 'Fieldset ID parameter is NULL';
1536     END IF;
1537     IF table_name IS NULL THEN
1538         RETURN 'Table name parameter is NULL';
1539     END IF;
1540     IF pkey_name IS NULL THEN
1541         RETURN 'Primary key name parameter is NULL';
1542     END IF;
1543
1544     SELECT
1545         status,
1546         quote_literal( pkey_value )
1547     INTO
1548         fs_status,
1549         fs_pkey_value
1550     FROM
1551         action.fieldset
1552     WHERE
1553         id = fieldset_id;
1554
1555     --
1556     -- Build the WHERE clause.  This differs according to whether it's a
1557     -- single-row fieldset or a query-based fieldset.
1558     --
1559     IF query IS NULL        AND fs_pkey_value IS NULL THEN
1560         RETURN 'Incomplete fieldset: neither a primary key nor a query available';
1561     ELSIF query IS NOT NULL AND fs_pkey_value IS NULL THEN
1562         fs_query := rtrim( query, ';' );
1563         where_clause := 'WHERE ' || pkey_name || ' IN ( '
1564                      || fs_query || ' )';
1565     ELSIF query IS NULL     AND fs_pkey_value IS NOT NULL THEN
1566         where_clause := 'WHERE ' || pkey_name || ' = ';
1567         IF pkey_name = 'id' THEN
1568             where_clause := where_clause || fs_pkey_value;
1569         ELSIF pkey_name = 'code' THEN
1570             where_clause := where_clause || quote_literal(fs_pkey_value);
1571         ELSE
1572             RETURN 'Only know how to handle "id" and "code" pkeys currently, received ' || pkey_name;
1573         END IF;
1574     ELSE  -- both are not null
1575         RETURN 'Ambiguous fieldset: both a primary key and a query provided';
1576     END IF;
1577
1578     IF fs_status IS NULL THEN
1579         RETURN 'No fieldset found for id = ' || fieldset_id;
1580     ELSIF fs_status = 'APPLIED' THEN
1581         RETURN 'Fieldset ' || fieldset_id || ' has already been applied';
1582     END IF;
1583
1584     SELECT * INTO fs_obj FROM action.fieldset WHERE id = fieldset_id;
1585     SELECT * INTO fs_group FROM action.fieldset_group WHERE id = fs_obj.fieldset_group;
1586
1587     IF fs_group.can_rollback THEN
1588         -- This is part of a non-rollback group.  We need to record the current values for future rollback.
1589
1590         INSERT INTO action.fieldset_group (can_rollback, name, creator, owning_lib, container, container_type)
1591             VALUES (FALSE, 'ROLLBACK: '|| fs_group.name, fs_group.creator, fs_group.owning_lib, fs_group.container, fs_group.container_type);
1592
1593         fsg_id := CURRVAL('action.fieldset_group_id_seq');
1594
1595         FOR rb_row IN EXECUTE 'SELECT * FROM ' || table_name || ' ' || where_clause LOOP
1596             IF pkey_name = 'id' THEN
1597                 fs_pkey_value := rb_row.id;
1598             ELSIF pkey_name = 'code' THEN
1599                 fs_pkey_value := rb_row.code;
1600             ELSE
1601                 RETURN 'Only know how to handle "id" and "code" pkeys currently, received ' || pkey_name;
1602             END IF;
1603             INSERT INTO action.fieldset (fieldset_group,owner,owning_lib,status,classname,name,pkey_value)
1604                 VALUES (fsg_id, fs_obj.owner, fs_obj.owning_lib, 'PENDING', fs_obj.classname, fs_obj.name || ' ROLLBACK FOR ' || fs_pkey_value, fs_pkey_value);
1605
1606             fs_id := CURRVAL('action.fieldset_id_seq');
1607             sep := '';
1608             FOR cv IN
1609                 SELECT  DISTINCT col
1610                 FROM    action.fieldset_col_val
1611                 WHERE   fieldset = fieldset_id
1612             LOOP
1613                 EXECUTE 'INSERT INTO action.fieldset_col_val (fieldset, col, val) ' || 
1614                     'SELECT '|| fs_id || ', '||quote_literal(cv.col)||', '||cv.col||' FROM '||table_name||' WHERE '||pkey_name||' = '||fs_pkey_value;
1615             END LOOP;
1616         END LOOP;
1617     END IF;
1618
1619     statement := 'UPDATE ' || table_name || ' SET';
1620
1621     sep := '';
1622     FOR cv IN
1623         SELECT  col,
1624                 val
1625         FROM    action.fieldset_col_val
1626         WHERE   fieldset = fieldset_id
1627     LOOP
1628         statement := statement || sep || ' ' || cv.col
1629                      || ' = ' || coalesce( quote_literal( cv.val ), 'NULL' );
1630         sep := ',';
1631     END LOOP;
1632
1633     IF sep = '' THEN
1634         RETURN 'Fieldset ' || fieldset_id || ' has no column values defined';
1635     END IF;
1636     statement := statement || ' ' || where_clause;
1637
1638     --
1639     -- Execute the update
1640     --
1641     BEGIN
1642         EXECUTE statement;
1643         GET DIAGNOSTICS update_count = ROW_COUNT;
1644
1645         IF update_count = 0 THEN
1646             RAISE data_exception;
1647         END IF;
1648
1649         IF fsg_id IS NOT NULL THEN
1650             UPDATE action.fieldset_group SET rollback_group = fsg_id WHERE id = fs_group.id;
1651         END IF;
1652
1653         IF fs_group.id IS NOT NULL THEN
1654             UPDATE action.fieldset_group SET complete_time = now() WHERE id = fs_group.id;
1655         END IF;
1656
1657         UPDATE action.fieldset SET status = 'APPLIED', applied_time = now() WHERE id = fieldset_id;
1658
1659     EXCEPTION WHEN data_exception THEN
1660         msg := 'No eligible rows found for fieldset ' || fieldset_id;
1661         UPDATE action.fieldset SET status = 'ERROR', applied_time = now() WHERE id = fieldset_id;
1662         RETURN msg;
1663
1664     END;
1665
1666     RETURN msg;
1667
1668 EXCEPTION WHEN OTHERS THEN
1669     msg := 'Unable to apply fieldset ' || fieldset_id || ': ' || sqlerrm;
1670     UPDATE action.fieldset SET status = 'ERROR', applied_time = now() WHERE id = fieldset_id;
1671     RETURN msg;
1672
1673 END;
1674 $$ LANGUAGE plpgsql;
1675
1676
1677
1678 INSERT INTO config.upgrade_log (version, applied_to) VALUES ('1057', :eg_version); -- miker/gmcharlt/kmlussier
1679
1680 -- Thist change drops a needless join and saves 10-15% in time cost
1681 CREATE OR REPLACE FUNCTION search.facets_for_record_set(ignore_facet_classes text[], hits bigint[]) RETURNS TABLE(id integer, value text, count bigint)
1682 AS $f$
1683     SELECT id, value, count
1684       FROM (
1685         SELECT  mfae.field AS id,
1686                 mfae.value,
1687                 COUNT(DISTINCT mfae.source),
1688                 row_number() OVER (
1689                     PARTITION BY mfae.field ORDER BY COUNT(DISTINCT mfae.source) DESC
1690                 ) AS rownum
1691           FROM  metabib.facet_entry mfae
1692                 JOIN config.metabib_field cmf ON (cmf.id = mfae.field)
1693           WHERE mfae.source = ANY ($2)
1694                 AND cmf.facet_field
1695                 AND cmf.field_class NOT IN (SELECT * FROM unnest($1))
1696           GROUP by 1, 2
1697       ) all_facets
1698       WHERE rownum <= (
1699         SELECT COALESCE(
1700             (SELECT value::INT FROM config.global_flag WHERE name = 'search.max_facets_per_field' AND enabled),
1701             1000
1702         )
1703       );
1704 $f$ LANGUAGE SQL;
1705
1706 CREATE OR REPLACE FUNCTION unapi.metabib_virtual_record_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$
1707 DECLARE
1708     layout          unapi.bre_output_layout%ROWTYPE;
1709     transform       config.xml_transform%ROWTYPE;
1710     item_format     TEXT;
1711     tmp_xml         TEXT;
1712     xmlns_uri       TEXT := 'http://open-ils.org/spec/feed-xml/v1';
1713     ouid            INT;
1714     element_list    TEXT[];
1715 BEGIN
1716
1717     IF org = '-' OR org IS NULL THEN
1718         SELECT shortname INTO org FROM evergreen.org_top();
1719     END IF;
1720
1721     SELECT id INTO ouid FROM actor.org_unit WHERE shortname = org;
1722     SELECT * INTO layout FROM unapi.bre_output_layout WHERE name = format;
1723
1724     IF layout.name IS NULL THEN
1725         RETURN NULL::XML;
1726     END IF;
1727
1728     SELECT * INTO transform FROM config.xml_transform WHERE name = layout.transform;
1729     xmlns_uri := COALESCE(transform.namespace_uri,xmlns_uri);
1730
1731     -- Gather the bib xml
1732     SELECT XMLAGG( unapi.mmr(i, format, '', includes, org, depth, slimit, soffset, include_xmlns)) INTO tmp_xml FROM UNNEST( id_list ) i;
1733
1734     IF layout.title_element IS NOT NULL THEN
1735         EXECUTE 'SELECT XMLCONCAT( XMLELEMENT( name '|| layout.title_element ||', XMLATTRIBUTES( $1 AS xmlns), $3), $2)' INTO tmp_xml USING xmlns_uri, tmp_xml::XML, title;
1736     END IF;
1737
1738     IF layout.description_element IS NOT NULL THEN
1739         EXECUTE 'SELECT XMLCONCAT( XMLELEMENT( name '|| layout.description_element ||', XMLATTRIBUTES( $1 AS xmlns), $3), $2)' INTO tmp_xml USING xmlns_uri, tmp_xml::XML, description;
1740     END IF;
1741
1742     IF layout.creator_element IS NOT NULL THEN
1743         EXECUTE 'SELECT XMLCONCAT( XMLELEMENT( name '|| layout.creator_element ||', XMLATTRIBUTES( $1 AS xmlns), $3), $2)' INTO tmp_xml USING xmlns_uri, tmp_xml::XML, creator;
1744     END IF;
1745
1746     IF layout.update_ts_element IS NOT NULL THEN
1747         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;
1748     END IF;
1749
1750     IF unapi_url IS NOT NULL THEN
1751         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;
1752     END IF;
1753
1754     IF header_xml IS NOT NULL THEN tmp_xml := XMLCONCAT(header_xml,tmp_xml::XML); END IF;
1755
1756     element_list := regexp_split_to_array(layout.feed_top,E'\\.');
1757     FOR i IN REVERSE ARRAY_UPPER(element_list, 1) .. 1 LOOP
1758         EXECUTE 'SELECT XMLELEMENT( name '|| quote_ident(element_list[i]) ||', XMLATTRIBUTES( $1 AS xmlns), $2)' INTO tmp_xml USING xmlns_uri, tmp_xml::XML;
1759     END LOOP;
1760
1761     RETURN tmp_xml::XML;
1762 END;
1763 $F$ LANGUAGE PLPGSQL STABLE;
1764
1765 CREATE TABLE asset.copy_vis_attr_cache (
1766     id              BIGSERIAL   PRIMARY KEY,
1767     record          BIGINT      NOT NULL, -- No FKEYs, managed by user triggers.
1768     target_copy     BIGINT      NOT NULL,
1769     vis_attr_vector INT[]
1770 );
1771 CREATE INDEX copy_vis_attr_cache_record_idx ON asset.copy_vis_attr_cache (record);
1772 CREATE INDEX copy_vis_attr_cache_copy_idx ON asset.copy_vis_attr_cache (target_copy);
1773
1774 ALTER TABLE biblio.record_entry ADD COLUMN vis_attr_vector INT[];
1775
1776 CREATE OR REPLACE FUNCTION search.calculate_visibility_attribute ( value INT, attr TEXT ) RETURNS INT AS $f$
1777 SELECT  ((CASE $2
1778
1779             WHEN 'luri_org'         THEN 0 -- "b" attr
1780             WHEN 'bib_source'       THEN 1 -- "b" attr
1781
1782             WHEN 'copy_flags'       THEN 0 -- "c" attr
1783             WHEN 'owning_lib'       THEN 1 -- "c" attr
1784             WHEN 'circ_lib'         THEN 2 -- "c" attr
1785             WHEN 'status'           THEN 3 -- "c" attr
1786             WHEN 'location'         THEN 4 -- "c" attr
1787             WHEN 'location_group'   THEN 5 -- "c" attr
1788
1789         END) << 28 ) | $1;
1790
1791 /* copy_flags bit positions, LSB-first:
1792
1793  0: asset.copy.opac_visible
1794
1795
1796    When adding flags, you must update asset.all_visible_flags()
1797
1798    Because bib and copy values are stored separately, we can reuse
1799    shifts, saving us some space. We could probably take back a bit
1800    too, but I'm not sure its worth squeezing that last one out. We'd
1801    be left with just 2 slots for copy attrs, rather than 10.
1802 */
1803
1804 $f$ LANGUAGE SQL IMMUTABLE;
1805
1806 CREATE OR REPLACE FUNCTION search.calculate_visibility_attribute_list ( attr TEXT, value INT[] ) RETURNS INT[] AS $f$
1807     SELECT ARRAY_AGG(search.calculate_visibility_attribute(x, $1)) FROM UNNEST($2) AS X;
1808 $f$ LANGUAGE SQL IMMUTABLE;
1809
1810 CREATE OR REPLACE FUNCTION search.calculate_visibility_attribute_test ( attr TEXT, value INT[], negate BOOL DEFAULT FALSE ) RETURNS TEXT AS $f$
1811     SELECT  CASE WHEN $3 THEN '!' ELSE '' END || '(' || ARRAY_TO_STRING(search.calculate_visibility_attribute_list($1,$2),'|') || ')';
1812 $f$ LANGUAGE SQL IMMUTABLE;
1813
1814 CREATE OR REPLACE FUNCTION asset.calculate_copy_visibility_attribute_set ( copy_id BIGINT ) RETURNS INT[] AS $f$
1815 DECLARE
1816     copy_row    asset.copy%ROWTYPE;
1817     lgroup_map  asset.copy_location_group_map%ROWTYPE;
1818     attr_set    INT[];
1819 BEGIN
1820     SELECT * INTO copy_row FROM asset.copy WHERE id = copy_id;
1821
1822     attr_set := attr_set || search.calculate_visibility_attribute(copy_row.opac_visible::INT, 'copy_flags');
1823     attr_set := attr_set || search.calculate_visibility_attribute(copy_row.circ_lib, 'circ_lib');
1824     attr_set := attr_set || search.calculate_visibility_attribute(copy_row.status, 'status');
1825     attr_set := attr_set || search.calculate_visibility_attribute(copy_row.location, 'location');
1826
1827     SELECT  ARRAY_APPEND(
1828                 attr_set,
1829                 search.calculate_visibility_attribute(owning_lib, 'owning_lib')
1830             ) INTO attr_set
1831       FROM  asset.call_number
1832       WHERE id = copy_row.call_number;
1833
1834     FOR lgroup_map IN SELECT * FROM asset.copy_location_group_map WHERE location = copy_row.location LOOP
1835         attr_set := attr_set || search.calculate_visibility_attribute(lgroup_map.lgroup, 'location_group');
1836     END LOOP;
1837
1838     RETURN attr_set;
1839 END;
1840 $f$ LANGUAGE PLPGSQL;
1841
1842 CREATE OR REPLACE FUNCTION biblio.calculate_bib_visibility_attribute_set ( bib_id BIGINT ) RETURNS INT[] AS $f$
1843 DECLARE
1844     bib_row     biblio.record_entry%ROWTYPE;
1845     cn_row      asset.call_number%ROWTYPE;
1846     attr_set    INT[];
1847 BEGIN
1848     SELECT * INTO bib_row FROM biblio.record_entry WHERE id = bib_id;
1849
1850     IF bib_row.source IS NOT NULL THEN
1851         attr_set := attr_set || search.calculate_visibility_attribute(bib_row.source, 'bib_source');
1852     END IF;
1853
1854     FOR cn_row IN
1855         SELECT  cn.*
1856           FROM  asset.call_number cn
1857                 JOIN asset.uri_call_number_map m ON (cn.id = m.call_number)
1858                 JOIN asset.uri u ON (u.id = m.uri)
1859           WHERE cn.record = bib_id
1860                 AND cn.label = '##URI##'
1861                 AND u.active
1862     LOOP
1863         attr_set := attr_set || search.calculate_visibility_attribute(cn_row.owning_lib, 'luri_org');
1864     END LOOP;
1865
1866     RETURN attr_set;
1867 END;
1868 $f$ LANGUAGE PLPGSQL;
1869
1870 CREATE OR REPLACE FUNCTION asset.cache_copy_visibility () RETURNS TRIGGER as $func$
1871 DECLARE
1872     ocn     asset.call_number%ROWTYPE;
1873     ncn     asset.call_number%ROWTYPE;
1874     cid     BIGINT;
1875 BEGIN
1876
1877     IF TG_TABLE_NAME = 'peer_bib_copy_map' THEN -- Only needs ON INSERT OR DELETE, so handle separately
1878         IF TG_OP = 'INSERT' THEN
1879             INSERT INTO asset.copy_vis_attr_cache (record, target_copy, vis_attr_vector) VALUES (
1880                 NEW.peer_record,
1881                 NEW.target_copy,
1882                 asset.calculate_copy_visibility_attribute_set(NEW.target_copy)
1883             );
1884
1885             RETURN NEW;
1886         ELSIF TG_OP = 'DELETE' THEN
1887             DELETE FROM asset.copy_vis_attr_cache
1888               WHERE record = NEW.peer_record AND target_copy = NEW.target_copy;
1889
1890             RETURN OLD;
1891         END IF;
1892     END IF;
1893
1894     IF TG_OP = 'INSERT' THEN -- Handles ON INSERT. ON UPDATE is below.
1895         IF TG_TABLE_NAME IN ('copy', 'unit') THEN
1896             SELECT * INTO ncn FROM asset.call_number cn WHERE id = NEW.call_number;
1897             INSERT INTO asset.copy_vis_attr_cache (record, target_copy, vis_attr_vector) VALUES (
1898                 ncn.record,
1899                 NEW.id,
1900                 asset.calculate_copy_visibility_attribute_set(NEW.id)
1901             );
1902         ELSIF TG_TABLE_NAME = 'record_entry' THEN
1903             NEW.vis_attr_vector := biblio.calculate_bib_visibility_attribute_set(NEW.id);
1904         END IF;
1905
1906         RETURN NEW;
1907     END IF;
1908
1909     -- handle items first, since with circulation activity
1910     -- their statuses change frequently
1911     IF TG_TABLE_NAME IN ('copy', 'unit') THEN -- This handles ON UPDATE OR DELETE. ON INSERT above
1912
1913         IF TG_OP = 'DELETE' THEN -- Shouldn't get here, normally
1914             DELETE FROM asset.copy_vis_attr_cache WHERE target_copy = OLD.id;
1915             RETURN OLD;
1916         END IF;
1917
1918         SELECT * INTO ncn FROM asset.call_number cn WHERE id = NEW.call_number;
1919
1920         IF OLD.deleted <> NEW.deleted THEN
1921             IF NEW.deleted THEN
1922                 DELETE FROM asset.copy_vis_attr_cache WHERE target_copy = OLD.id;
1923             ELSE
1924                 INSERT INTO asset.copy_vis_attr_cache (record, target_copy, vis_attr_vector) VALUES (
1925                     ncn.record,
1926                     NEW.id,
1927                     asset.calculate_copy_visibility_attribute_set(NEW.id)
1928                 );
1929             END IF;
1930
1931             RETURN NEW;
1932         ELSIF OLD.call_number  <> NEW.call_number THEN
1933             SELECT * INTO ocn FROM asset.call_number cn WHERE id = OLD.call_number;
1934
1935             IF ncn.record <> ocn.record THEN
1936                 UPDATE  biblio.record_entry
1937                   SET   vis_attr_vector = biblio.calculate_bib_visibility_attribute_set(ncn.record)
1938                   WHERE id = ocn.record;
1939             END IF;
1940         END IF;
1941
1942         IF OLD.location     <> NEW.location OR
1943            OLD.status       <> NEW.status OR
1944            OLD.opac_visible <> NEW.opac_visible OR
1945            OLD.circ_lib     <> NEW.circ_lib
1946         THEN
1947             -- any of these could change visibility, but
1948             -- we'll save some queries and not try to calculate
1949             -- the change directly
1950             UPDATE  asset.copy_vis_attr_cache
1951               SET   target_copy = NEW.id,
1952                     vis_attr_vector = asset.calculate_copy_visibility_attribute_set(NEW.id)
1953               WHERE target_copy = OLD.id;
1954
1955         END IF;
1956
1957     ELSIF TG_TABLE_NAME = 'call_number' THEN -- Only ON UPDATE. Copy handler will deal with ON INSERT OR DELETE.
1958
1959         IF OLD.record <> NEW.record THEN
1960             IF NEW.label = '##URI##' THEN
1961                 UPDATE  biblio.record_entry
1962                   SET   vis_attr_vector = biblio.calculate_bib_visibility_attribute_set(OLD.record)
1963                   WHERE id = OLD.record;
1964
1965                 UPDATE  biblio.record_entry
1966                   SET   vis_attr_vector = biblio.calculate_bib_visibility_attribute_set(NEW.record)
1967                   WHERE id = NEW.record;
1968             END IF;
1969
1970             UPDATE  asset.copy_vis_attr_cache
1971               SET   record = NEW.record,
1972                     vis_attr_vector = asset.calculate_copy_visibility_attribute_set(target_copy)
1973               WHERE target_copy IN (SELECT id FROM asset.copy WHERE call_number = NEW.id)
1974                     AND record = OLD.record;
1975
1976         ELSIF OLD.owning_lib <> NEW.owning_lib THEN
1977             UPDATE  asset.copy_vis_attr_cache
1978               SET   vis_attr_vector = asset.calculate_copy_visibility_attribute_set(target_copy)
1979               WHERE target_copy IN (SELECT id FROM asset.copy WHERE call_number = NEW.id)
1980                     AND record = NEW.record;
1981
1982             IF NEW.label = '##URI##' THEN
1983                 UPDATE  biblio.record_entry
1984                   SET   vis_attr_vector = biblio.calculate_bib_visibility_attribute_set(OLD.record)
1985                   WHERE id = OLD.record;
1986             END IF;
1987         END IF;
1988
1989     ELSIF TG_TABLE_NAME = 'record_entry' THEN -- Only handles ON UPDATE OR DELETE
1990
1991         IF TG_OP = 'DELETE' THEN -- Shouldn't get here, normally
1992             DELETE FROM asset.copy_vis_attr_cache WHERE record = OLD.id;
1993             RETURN OLD;
1994         ELSIF OLD.source <> NEW.source THEN
1995             NEW.vis_attr_vector := biblio.calculate_bib_visibility_attribute_set(NEW.id);
1996         END IF;
1997
1998     END IF;
1999
2000     RETURN NEW;
2001 END;
2002 $func$ LANGUAGE PLPGSQL;
2003
2004
2005 -- Helper functions for use in constructing searches --
2006
2007 CREATE OR REPLACE FUNCTION asset.all_visible_flags () RETURNS TEXT AS $f$
2008     SELECT  '(' || ARRAY_TO_STRING(ARRAY_AGG(search.calculate_visibility_attribute(1 << x, 'copy_flags')),'&') || ')'
2009       FROM  GENERATE_SERIES(0,0) AS x; -- increment as new flags are added.
2010 $f$ LANGUAGE SQL STABLE;
2011
2012 CREATE OR REPLACE FUNCTION asset.visible_orgs (otype TEXT) RETURNS TEXT AS $f$
2013     SELECT  '(' || ARRAY_TO_STRING(ARRAY_AGG(search.calculate_visibility_attribute(id, $1)),'|') || ')'
2014       FROM  actor.org_unit
2015       WHERE opac_visible;
2016 $f$ LANGUAGE SQL STABLE;
2017
2018 CREATE OR REPLACE FUNCTION asset.invisible_orgs (otype TEXT) RETURNS TEXT AS $f$
2019     SELECT  '!(' || ARRAY_TO_STRING(ARRAY_AGG(search.calculate_visibility_attribute(id, $1)),'|') || ')'
2020       FROM  actor.org_unit
2021       WHERE NOT opac_visible;
2022 $f$ LANGUAGE SQL STABLE;
2023
2024 -- Bib-oriented defaults for search
2025 CREATE OR REPLACE FUNCTION asset.bib_source_default () RETURNS TEXT AS $f$
2026     SELECT  '(' || ARRAY_TO_STRING(ARRAY_AGG(search.calculate_visibility_attribute(id, 'bib_source')),'|') || ')'
2027       FROM  config.bib_source
2028       WHERE transcendant;
2029 $f$ LANGUAGE SQL IMMUTABLE;
2030
2031 CREATE OR REPLACE FUNCTION asset.luri_org_default () RETURNS TEXT AS $f$
2032     SELECT  * FROM asset.invisible_orgs('luri_org');
2033 $f$ LANGUAGE SQL STABLE;
2034
2035 -- Copy-oriented defaults for search
2036 CREATE OR REPLACE FUNCTION asset.location_group_default () RETURNS TEXT AS $f$
2037     SELECT  '!(' || ARRAY_TO_STRING(ARRAY_AGG(search.calculate_visibility_attribute(id, 'location_group')),'|') || ')'
2038       FROM  asset.copy_location_group
2039       WHERE NOT opac_visible;
2040 $f$ LANGUAGE SQL STABLE;
2041
2042 CREATE OR REPLACE FUNCTION asset.location_default () RETURNS TEXT AS $f$
2043     SELECT  '!(' || ARRAY_TO_STRING(ARRAY_AGG(search.calculate_visibility_attribute(id, 'location')),'|') || ')'
2044       FROM  asset.copy_location
2045       WHERE NOT opac_visible;
2046 $f$ LANGUAGE SQL STABLE;
2047
2048 CREATE OR REPLACE FUNCTION asset.status_default () RETURNS TEXT AS $f$
2049     SELECT  '!(' || ARRAY_TO_STRING(ARRAY_AGG(search.calculate_visibility_attribute(id, 'status')),'|') || ')'
2050       FROM  config.copy_status
2051       WHERE NOT opac_visible;
2052 $f$ LANGUAGE SQL STABLE;
2053
2054 CREATE OR REPLACE FUNCTION asset.owning_lib_default () RETURNS TEXT AS $f$
2055     SELECT  * FROM asset.invisible_orgs('owning_lib');
2056 $f$ LANGUAGE SQL STABLE;
2057
2058 CREATE OR REPLACE FUNCTION asset.circ_lib_default () RETURNS TEXT AS $f$
2059     SELECT  * FROM asset.invisible_orgs('circ_lib');
2060 $f$ LANGUAGE SQL STABLE;
2061
2062 CREATE OR REPLACE FUNCTION asset.patron_default_visibility_mask () RETURNS TABLE (b_attrs TEXT, c_attrs TEXT)  AS $f$
2063 DECLARE
2064     copy_flags      TEXT; -- "c" attr
2065
2066     owning_lib      TEXT; -- "c" attr
2067     circ_lib        TEXT; -- "c" attr
2068     status          TEXT; -- "c" attr
2069     location        TEXT; -- "c" attr
2070     location_group  TEXT; -- "c" attr
2071
2072     luri_org        TEXT; -- "b" attr
2073     bib_sources     TEXT; -- "b" attr
2074 BEGIN
2075     copy_flags      := asset.all_visible_flags(); -- Will always have at least one
2076
2077     owning_lib      := NULLIF(asset.owning_lib_default(),'!()');
2078     
2079     circ_lib        := NULLIF(asset.circ_lib_default(),'!()');
2080     status          := NULLIF(asset.status_default(),'!()');
2081     location        := NULLIF(asset.location_default(),'!()');
2082     location_group  := NULLIF(asset.location_group_default(),'!()');
2083
2084     luri_org        := NULLIF(asset.luri_org_default(),'!()');
2085     bib_sources     := NULLIF(asset.bib_source_default(),'()');
2086
2087     RETURN QUERY SELECT
2088         '('||ARRAY_TO_STRING(
2089             ARRAY[luri_org,bib_sources],
2090             '|'
2091         )||')',
2092         '('||ARRAY_TO_STRING(
2093             ARRAY[copy_flags,owning_lib,circ_lib,status,location,location_group]::TEXT[],
2094             '&'
2095         )||')';
2096 END;
2097 $f$ LANGUAGE PLPGSQL STABLE ROWS 1;
2098
2099 CREATE OR REPLACE FUNCTION metabib.suggest_browse_entries(raw_query_text text, search_class text, headline_opts text, visibility_org integer, query_limit integer, normalization integer)
2100  RETURNS TABLE(value text, field integer, buoyant_and_class_match boolean, field_match boolean, field_weight integer, rank real, buoyant boolean, match text)
2101 AS $f$
2102 DECLARE
2103     prepared_query_texts    TEXT[];
2104     query                   TSQUERY;
2105     plain_query             TSQUERY;
2106     opac_visibility_join    TEXT;
2107     search_class_join       TEXT;
2108     r_fields                RECORD;
2109 BEGIN
2110     prepared_query_texts := metabib.autosuggest_prepare_tsquery(raw_query_text);
2111
2112     query := TO_TSQUERY('keyword', prepared_query_texts[1]);
2113     plain_query := TO_TSQUERY('keyword', prepared_query_texts[2]);
2114
2115     visibility_org := NULLIF(visibility_org,-1);
2116     IF visibility_org IS NOT NULL THEN
2117         PERFORM FROM actor.org_unit WHERE id = visibility_org AND parent_ou IS NULL;
2118         IF FOUND THEN
2119             opac_visibility_join := '';
2120         ELSE
2121             opac_visibility_join := '
2122     JOIN asset.copy_vis_attr_cache acvac ON (acvac.record = x.source)
2123     JOIN vm ON (acvac.vis_attr_vector @@
2124             (vm.c_attrs || $$&$$ ||
2125                 search.calculate_visibility_attribute_test(
2126                     $$circ_lib$$,
2127                     (SELECT ARRAY_AGG(id) FROM actor.org_unit_descendants($4))
2128                 )
2129             )::query_int
2130          )
2131 ';
2132         END IF;
2133     ELSE
2134         opac_visibility_join := '';
2135     END IF;
2136
2137     -- The following determines whether we only provide suggestsons matching
2138     -- the user's selected search_class, or whether we show other suggestions
2139     -- too. The reason for MIN() is that for search_classes like
2140     -- 'title|proper|uniform' you would otherwise get multiple rows.  The
2141     -- implication is that if title as a class doesn't have restrict,
2142     -- nor does the proper field, but the uniform field does, you're going
2143     -- to get 'false' for your overall evaluation of 'should we restrict?'
2144     -- To invert that, change from MIN() to MAX().
2145
2146     SELECT
2147         INTO r_fields
2148             MIN(cmc.restrict::INT) AS restrict_class,
2149             MIN(cmf.restrict::INT) AS restrict_field
2150         FROM metabib.search_class_to_registered_components(search_class)
2151             AS _registered (field_class TEXT, field INT)
2152         JOIN
2153             config.metabib_class cmc ON (cmc.name = _registered.field_class)
2154         LEFT JOIN
2155             config.metabib_field cmf ON (cmf.id = _registered.field);
2156
2157     -- evaluate 'should we restrict?'
2158     IF r_fields.restrict_field::BOOL OR r_fields.restrict_class::BOOL THEN
2159         search_class_join := '
2160     JOIN
2161         metabib.search_class_to_registered_components($2)
2162         AS _registered (field_class TEXT, field INT) ON (
2163             (_registered.field IS NULL AND
2164                 _registered.field_class = cmf.field_class) OR
2165             (_registered.field = cmf.id)
2166         )
2167     ';
2168     ELSE
2169         search_class_join := '
2170     LEFT JOIN
2171         metabib.search_class_to_registered_components($2)
2172         AS _registered (field_class TEXT, field INT) ON (
2173             _registered.field_class = cmc.name
2174         )
2175     ';
2176     END IF;
2177
2178     RETURN QUERY EXECUTE '
2179 WITH vm AS ( SELECT * FROM asset.patron_default_visibility_mask() ),
2180      mbe AS (SELECT * FROM metabib.browse_entry WHERE index_vector @@ $1 LIMIT 10000)
2181 SELECT  DISTINCT
2182         x.value,
2183         x.id,
2184         x.push,
2185         x.restrict,
2186         x.weight,
2187         x.ts_rank_cd,
2188         x.buoyant,
2189         TS_HEADLINE(value, $7, $3)
2190   FROM  (SELECT DISTINCT
2191                 mbe.value,
2192                 cmf.id,
2193                 cmc.buoyant AND _registered.field_class IS NOT NULL AS push,
2194                 _registered.field = cmf.id AS restrict,
2195                 cmf.weight,
2196                 TS_RANK_CD(mbe.index_vector, $1, $6),
2197                 cmc.buoyant,
2198                 mbedm.source
2199           FROM  metabib.browse_entry_def_map mbedm
2200                 JOIN mbe ON (mbe.id = mbedm.entry)
2201                 JOIN config.metabib_field cmf ON (cmf.id = mbedm.def)
2202                 JOIN config.metabib_class cmc ON (cmf.field_class = cmc.name)
2203                 '  || search_class_join || '
2204           ORDER BY 3 DESC, 4 DESC NULLS LAST, 5 DESC, 6 DESC, 7 DESC, 1 ASC
2205           LIMIT 1000) AS x
2206         ' || opac_visibility_join || '
2207   ORDER BY 3 DESC, 4 DESC NULLS LAST, 5 DESC, 6 DESC, 7 DESC, 1 ASC
2208   LIMIT $5
2209 '   -- sic, repeat the order by clause in the outer select too
2210     USING
2211         query, search_class, headline_opts,
2212         visibility_org, query_limit, normalization, plain_query
2213         ;
2214
2215     -- sort order:
2216     --  buoyant AND chosen class = match class
2217     --  chosen field = match field
2218     --  field weight
2219     --  rank
2220     --  buoyancy
2221     --  value itself
2222
2223 END;
2224 $f$ LANGUAGE plpgsql ROWS 10;
2225
2226 CREATE OR REPLACE FUNCTION metabib.browse(search_field integer[], browse_term text, context_org integer DEFAULT NULL::integer, context_loc_group integer DEFAULT NULL::integer, staff boolean DEFAULT false, pivot_id bigint DEFAULT NULL::bigint, result_limit integer DEFAULT 10)
2227  RETURNS SETOF metabib.flat_browse_entry_appearance
2228 AS $f$
2229 DECLARE
2230     core_query              TEXT;
2231     back_query              TEXT;
2232     forward_query           TEXT;
2233     pivot_sort_value        TEXT;
2234     pivot_sort_fallback     TEXT;
2235     context_locations       INT[];
2236     browse_superpage_size   INT;
2237     results_skipped         INT := 0;
2238     back_limit              INT;
2239     back_to_pivot           INT;
2240     forward_limit           INT;
2241     forward_to_pivot        INT;
2242 BEGIN
2243     -- First, find the pivot if we were given a browse term but not a pivot.
2244     IF pivot_id IS NULL THEN
2245         pivot_id := metabib.browse_pivot(search_field, browse_term);
2246     END IF;
2247
2248     SELECT INTO pivot_sort_value, pivot_sort_fallback
2249         sort_value, value FROM metabib.browse_entry WHERE id = pivot_id;
2250
2251     -- Bail if we couldn't find a pivot.
2252     IF pivot_sort_value IS NULL THEN
2253         RETURN;
2254     END IF;
2255
2256     -- Transform the context_loc_group argument (if any) (logc at the
2257     -- TPAC layer) into a form we'll be able to use.
2258     IF context_loc_group IS NOT NULL THEN
2259         SELECT INTO context_locations ARRAY_AGG(location)
2260             FROM asset.copy_location_group_map
2261             WHERE lgroup = context_loc_group;
2262     END IF;
2263
2264     -- Get the configured size of browse superpages.
2265     SELECT INTO browse_superpage_size COALESCE(value::INT,100)     -- NULL ok
2266         FROM config.global_flag
2267         WHERE enabled AND name = 'opac.browse.holdings_visibility_test_limit';
2268
2269     -- First we're going to search backward from the pivot, then we're going
2270     -- to search forward.  In each direction, we need two limits.  At the
2271     -- lesser of the two limits, we delineate the edge of the result set
2272     -- we're going to return.  At the greater of the two limits, we find the
2273     -- pivot value that would represent an offset from the current pivot
2274     -- at a distance of one "page" in either direction, where a "page" is a
2275     -- result set of the size specified in the "result_limit" argument.
2276     --
2277     -- The two limits in each direction make four derived values in total,
2278     -- and we calculate them now.
2279     back_limit := CEIL(result_limit::FLOAT / 2);
2280     back_to_pivot := result_limit;
2281     forward_limit := result_limit / 2;
2282     forward_to_pivot := result_limit - 1;
2283
2284     -- This is the meat of the SQL query that finds browse entries.  We'll
2285     -- pass this to a function which uses it with a cursor, so that individual
2286     -- rows may be fetched in a loop until some condition is satisfied, without
2287     -- waiting for a result set of fixed size to be collected all at once.
2288     core_query := '
2289 SELECT  mbe.id,
2290         mbe.value,
2291         mbe.sort_value
2292   FROM  metabib.browse_entry mbe
2293   WHERE (
2294             EXISTS ( -- are there any bibs using this mbe via the requested fields?
2295                 SELECT  1
2296                   FROM  metabib.browse_entry_def_map mbedm
2297                   WHERE mbedm.entry = mbe.id AND mbedm.def = ANY(' || quote_literal(search_field) || ')
2298             ) OR EXISTS ( -- are there any authorities using this mbe via the requested fields?
2299                 SELECT  1
2300                   FROM  metabib.browse_entry_simple_heading_map mbeshm
2301                         JOIN authority.simple_heading ash ON ( mbeshm.simple_heading = ash.id )
2302                         JOIN authority.control_set_auth_field_metabib_field_map_refs map ON (
2303                             ash.atag = map.authority_field
2304                             AND map.metabib_field = ANY(' || quote_literal(search_field) || ')
2305                         )
2306                   WHERE mbeshm.entry = mbe.id
2307             )
2308         ) AND ';
2309
2310     -- This is the variant of the query for browsing backward.
2311     back_query := core_query ||
2312         ' mbe.sort_value <= ' || quote_literal(pivot_sort_value) ||
2313     ' ORDER BY mbe.sort_value DESC, mbe.value DESC LIMIT 1000';
2314
2315     -- This variant browses forward.
2316     forward_query := core_query ||
2317         ' mbe.sort_value > ' || quote_literal(pivot_sort_value) ||
2318     ' ORDER BY mbe.sort_value, mbe.value LIMIT 1000';
2319
2320     -- We now call the function which applies a cursor to the provided
2321     -- queries, stopping at the appropriate limits and also giving us
2322     -- the next page's pivot.
2323     RETURN QUERY
2324         SELECT * FROM metabib.staged_browse(
2325             back_query, search_field, context_org, context_locations,
2326             staff, browse_superpage_size, TRUE, back_limit, back_to_pivot
2327         ) UNION
2328         SELECT * FROM metabib.staged_browse(
2329             forward_query, search_field, context_org, context_locations,
2330             staff, browse_superpage_size, FALSE, forward_limit, forward_to_pivot
2331         ) ORDER BY row_number DESC;
2332
2333 END;
2334 $f$ LANGUAGE plpgsql ROWS 10;
2335
2336 CREATE OR REPLACE FUNCTION metabib.staged_browse(query text, fields integer[], context_org integer, context_locations integer[], staff boolean, browse_superpage_size integer, count_up_from_zero boolean, result_limit integer, next_pivot_pos integer)
2337  RETURNS SETOF metabib.flat_browse_entry_appearance
2338 AS $f$
2339 DECLARE
2340     curs                    REFCURSOR;
2341     rec                     RECORD;
2342     qpfts_query             TEXT;
2343     aqpfts_query            TEXT;
2344     afields                 INT[];
2345     bfields                 INT[];
2346     result_row              metabib.flat_browse_entry_appearance%ROWTYPE;
2347     results_skipped         INT := 0;
2348     row_counter             INT := 0;
2349     row_number              INT;
2350     slice_start             INT;
2351     slice_end               INT;
2352     full_end                INT;
2353     all_records             BIGINT[];
2354     all_brecords             BIGINT[];
2355     all_arecords            BIGINT[];
2356     superpage_of_records    BIGINT[];
2357     superpage_size          INT;
2358     c_tests                 TEXT := '';
2359     b_tests                 TEXT := '';
2360     c_orgs                  INT[];
2361 BEGIN
2362     IF count_up_from_zero THEN
2363         row_number := 0;
2364     ELSE
2365         row_number := -1;
2366     END IF;
2367
2368     IF NOT staff THEN
2369         SELECT x.c_attrs, x.b_attrs INTO c_tests, b_tests FROM asset.patron_default_visibility_mask() x;
2370     END IF;
2371
2372     IF c_tests <> '' THEN c_tests := c_tests || '&'; END IF;
2373     IF b_tests <> '' THEN b_tests := b_tests || '&'; END IF;
2374
2375     SELECT ARRAY_AGG(id) INTO c_orgs FROM actor.org_unit_descendants(context_org);
2376     
2377     c_tests := c_tests || search.calculate_visibility_attribute_test('circ_lib',c_orgs)
2378                || '&' || search.calculate_visibility_attribute_test('owning_lib',c_orgs);
2379     
2380     PERFORM 1 FROM config.internal_flag WHERE enabled AND name = 'opac.located_uri.act_as_copy';
2381     IF FOUND THEN
2382         b_tests := b_tests || search.calculate_visibility_attribute_test(
2383             'luri_org',
2384             (SELECT ARRAY_AGG(id) FROM actor.org_unit_full_path(context_org) x)
2385         );
2386     ELSE
2387         b_tests := b_tests || search.calculate_visibility_attribute_test(
2388             'luri_org',
2389             (SELECT ARRAY_AGG(id) FROM actor.org_unit_ancestors(context_org) x)
2390         );
2391     END IF;
2392
2393     IF context_locations THEN
2394         IF c_tests <> '' THEN c_tests := c_tests || '&'; END IF;
2395         c_tests := c_tests || search.calculate_visibility_attribute_test('location',context_locations);
2396     END IF;
2397
2398     OPEN curs NO SCROLL FOR EXECUTE query;
2399
2400     LOOP
2401         FETCH curs INTO rec;
2402         IF NOT FOUND THEN
2403             IF result_row.pivot_point IS NOT NULL THEN
2404                 RETURN NEXT result_row;
2405             END IF;
2406             RETURN;
2407         END IF;
2408
2409         -- Gather aggregate data based on the MBE row we're looking at now, authority axis
2410         SELECT INTO all_arecords, result_row.sees, afields
2411                 ARRAY_AGG(DISTINCT abl.bib), -- bibs to check for visibility
2412                 STRING_AGG(DISTINCT aal.source::TEXT, $$,$$), -- authority record ids
2413                 ARRAY_AGG(DISTINCT map.metabib_field) -- authority-tag-linked CMF rows
2414
2415           FROM  metabib.browse_entry_simple_heading_map mbeshm
2416                 JOIN authority.simple_heading ash ON ( mbeshm.simple_heading = ash.id )
2417                 JOIN authority.authority_linking aal ON ( ash.record = aal.source )
2418                 JOIN authority.bib_linking abl ON ( aal.target = abl.authority )
2419                 JOIN authority.control_set_auth_field_metabib_field_map_refs map ON (
2420                     ash.atag = map.authority_field
2421                     AND map.metabib_field = ANY(fields)
2422                 )
2423           WHERE mbeshm.entry = rec.id;
2424
2425         -- Gather aggregate data based on the MBE row we're looking at now, bib axis
2426         SELECT INTO all_brecords, result_row.authorities, bfields
2427                 ARRAY_AGG(DISTINCT source),
2428                 STRING_AGG(DISTINCT authority::TEXT, $$,$$),
2429                 ARRAY_AGG(DISTINCT def)
2430           FROM  metabib.browse_entry_def_map
2431           WHERE entry = rec.id
2432                 AND def = ANY(fields);
2433
2434         SELECT INTO result_row.fields STRING_AGG(DISTINCT x::TEXT, $$,$$) FROM UNNEST(afields || bfields) x;
2435
2436         result_row.sources := 0;
2437         result_row.asources := 0;
2438
2439         -- Bib-linked vis checking
2440         IF ARRAY_UPPER(all_brecords,1) IS NOT NULL THEN
2441
2442             SELECT  INTO result_row.sources COUNT(DISTINCT b.id)
2443               FROM  biblio.record_entry b
2444                     JOIN asset.copy_vis_attr_cache acvac ON (acvac.record = b.id)
2445               WHERE b.id = ANY(all_brecords[1:browse_superpage_size])
2446                     AND (
2447                         acvac.vis_attr_vector @@ c_tests::query_int
2448                         OR b.vis_attr_vector @@ b_tests::query_int
2449                     );
2450
2451             result_row.accurate := TRUE;
2452
2453         END IF;
2454
2455         -- Authority-linked vis checking
2456         IF ARRAY_UPPER(all_arecords,1) IS NOT NULL THEN
2457
2458             SELECT  INTO result_row.asources COUNT(DISTINCT b.id)
2459               FROM  biblio.record_entry b
2460                     JOIN asset.copy_vis_attr_cache acvac ON (acvac.record = b.id)
2461               WHERE b.id = ANY(all_arecords[1:browse_superpage_size])
2462                     AND (
2463                         acvac.vis_attr_vector @@ c_tests::query_int
2464                         OR b.vis_attr_vector @@ b_tests::query_int
2465                     );
2466
2467             result_row.aaccurate := TRUE;
2468
2469         END IF;
2470
2471         IF result_row.sources > 0 OR result_row.asources > 0 THEN
2472
2473             -- The function that calls this function needs row_number in order
2474             -- to correctly order results from two different runs of this
2475             -- functions.
2476             result_row.row_number := row_number;
2477
2478             -- Now, if row_counter is still less than limit, return a row.  If
2479             -- not, but it is less than next_pivot_pos, continue on without
2480             -- returning actual result rows until we find
2481             -- that next pivot, and return it.
2482
2483             IF row_counter < result_limit THEN
2484                 result_row.browse_entry := rec.id;
2485                 result_row.value := rec.value;
2486
2487                 RETURN NEXT result_row;
2488             ELSE
2489                 result_row.browse_entry := NULL;
2490                 result_row.authorities := NULL;
2491                 result_row.fields := NULL;
2492                 result_row.value := NULL;
2493                 result_row.sources := NULL;
2494                 result_row.sees := NULL;
2495                 result_row.accurate := NULL;
2496                 result_row.aaccurate := NULL;
2497                 result_row.pivot_point := rec.id;
2498
2499                 IF row_counter >= next_pivot_pos THEN
2500                     RETURN NEXT result_row;
2501                     RETURN;
2502                 END IF;
2503             END IF;
2504
2505             IF count_up_from_zero THEN
2506                 row_number := row_number + 1;
2507             ELSE
2508                 row_number := row_number - 1;
2509             END IF;
2510
2511             -- row_counter is different from row_number.
2512             -- It simply counts up from zero so that we know when
2513             -- we've reached our limit.
2514             row_counter := row_counter + 1;
2515         END IF;
2516     END LOOP;
2517 END;
2518 $f$ LANGUAGE plpgsql ROWS 10;
2519
2520 DROP TRIGGER IF EXISTS a_opac_vis_mat_view_tgr ON biblio.peer_bib_copy_map;
2521 DROP TRIGGER IF EXISTS a_opac_vis_mat_view_tgr ON biblio.record_entry;
2522 DROP TRIGGER IF EXISTS a_opac_vis_mat_view_tgr ON asset.copy;
2523 DROP TRIGGER IF EXISTS a_opac_vis_mat_view_tgr ON asset.call_number;
2524 DROP TRIGGER IF EXISTS a_opac_vis_mat_view_tgr ON asset.copy_location;
2525 DROP TRIGGER IF EXISTS a_opac_vis_mat_view_tgr ON serial.unit;
2526 DROP TRIGGER IF EXISTS a_opac_vis_mat_view_tgr ON config.copy_status;
2527 DROP TRIGGER IF EXISTS a_opac_vis_mat_view_tgr ON actor.org_unit;
2528
2529 -- Upgrade the data!
2530 INSERT INTO asset.copy_vis_attr_cache (target_copy, record, vis_attr_vector)
2531     SELECT  cp.id,
2532             cn.record,
2533             asset.calculate_copy_visibility_attribute_set(cp.id)
2534       FROM  asset.copy cp
2535             JOIN asset.call_number cn ON (cp.call_number = cn.id);
2536
2537 UPDATE biblio.record_entry SET vis_attr_vector = biblio.calculate_bib_visibility_attribute_set(id);
2538
2539 CREATE TRIGGER z_opac_vis_mat_view_tgr BEFORE INSERT OR UPDATE ON biblio.record_entry FOR EACH ROW EXECUTE PROCEDURE asset.cache_copy_visibility();
2540 CREATE TRIGGER z_opac_vis_mat_view_tgr AFTER INSERT OR DELETE ON biblio.peer_bib_copy_map FOR EACH ROW EXECUTE PROCEDURE asset.cache_copy_visibility();
2541 CREATE TRIGGER z_opac_vis_mat_view_tgr AFTER UPDATE ON asset.call_number FOR EACH ROW EXECUTE PROCEDURE asset.cache_copy_visibility();
2542 CREATE TRIGGER z_opac_vis_mat_view_del_tgr BEFORE DELETE ON asset.copy FOR EACH ROW EXECUTE PROCEDURE asset.cache_copy_visibility();
2543 CREATE TRIGGER z_opac_vis_mat_view_del_tgr BEFORE DELETE ON serial.unit FOR EACH ROW EXECUTE PROCEDURE asset.cache_copy_visibility();
2544 CREATE TRIGGER z_opac_vis_mat_view_tgr AFTER INSERT OR UPDATE ON asset.copy FOR EACH ROW EXECUTE PROCEDURE asset.cache_copy_visibility();
2545 CREATE TRIGGER z_opac_vis_mat_view_tgr AFTER INSERT OR UPDATE ON serial.unit FOR EACH ROW EXECUTE PROCEDURE asset.cache_copy_visibility();
2546
2547 CREATE OR REPLACE FUNCTION asset.opac_ou_record_copy_count (org INT, rid BIGINT) RETURNS TABLE (depth INT, org_unit INT, visible BIGINT, available BIGINT, unshadow BIGINT, transcendant INT) AS $f$
2548 DECLARE
2549     ans RECORD;
2550     trans INT;
2551 BEGIN
2552     SELECT 1 INTO trans FROM biblio.record_entry b JOIN config.bib_source src ON (b.source = src.id) WHERE src.transcendant AND b.id = rid;
2553
2554     FOR ans IN SELECT u.id, t.depth FROM actor.org_unit_ancestors(org) AS u JOIN actor.org_unit_type t ON (u.ou_type = t.id) LOOP
2555         RETURN QUERY
2556         WITH org_list AS (SELECT ARRAY_AGG(id)::BIGINT[] AS orgs FROM actor.org_unit_descendants(ans.id) x),
2557              available_statuses AS (SELECT ARRAY_AGG(id) AS ids FROM config.copy_status WHERE is_available),
2558              mask AS (SELECT c_attrs FROM asset.patron_default_visibility_mask() x)
2559         SELECT  ans.depth,
2560                 ans.id,
2561                 COUNT( av.id ),
2562                 SUM( (cp.status = ANY (available_statuses.ids))::INT ),
2563                 COUNT( av.id ),
2564                 trans
2565           FROM  mask,
2566                 available_statuses,
2567                 org_list,
2568                 asset.copy_vis_attr_cache av
2569                 JOIN asset.copy cp ON (cp.id = av.target_copy AND av.record = rid)
2570           WHERE cp.circ_lib = ANY (org_list.orgs) AND av.vis_attr_vector @@ mask.c_attrs::query_int
2571           GROUP BY 1,2,6;
2572
2573         IF NOT FOUND THEN
2574             RETURN QUERY SELECT ans.depth, ans.id, 0::BIGINT, 0::BIGINT, 0::BIGINT, trans;
2575         END IF;
2576
2577     END LOOP;
2578
2579     RETURN;
2580 END;
2581 $f$ LANGUAGE PLPGSQL;
2582
2583 CREATE OR REPLACE FUNCTION asset.opac_lasso_record_copy_count (i_lasso INT, rid BIGINT) RETURNS TABLE (depth INT, org_unit INT, visible BIGINT, available BIGINT, unshadow BIGINT, transcendant INT) AS $f$
2584 DECLARE
2585     ans RECORD;
2586     trans INT;
2587 BEGIN
2588     SELECT 1 INTO trans FROM biblio.record_entry b JOIN config.bib_source src ON (b.source = src.id) WHERE src.transcendant AND b.id = rid;
2589
2590     FOR ans IN SELECT u.org_unit AS id FROM actor.org_lasso_map AS u WHERE lasso = i_lasso LOOP
2591         RETURN QUERY
2592         WITH org_list AS (SELECT ARRAY_AGG(id)::BIGINT[] AS orgs FROM actor.org_unit_descendants(ans.id) x),
2593              available_statuses AS (SELECT ARRAY_AGG(id) AS ids FROM config.copy_status WHERE is_available),
2594              mask AS (SELECT c_attrs FROM asset.patron_default_visibility_mask() x)
2595         SELECT  -1,
2596                 ans.id,
2597                 COUNT( av.id ),
2598                 SUM( (cp.status = ANY (available_statuses.ids))::INT ),
2599                 COUNT( av.id ),
2600                 trans
2601           FROM  mask,
2602                 org_list,
2603                 asset.copy_vis_attr_cache av
2604                 JOIN asset.copy cp ON (cp.id = av.target_copy AND av.record = rid)
2605           WHERE cp.circ_lib = ANY (org_list.orgs) AND av.vis_attr_vector @@ mask.c_attrs::query_int
2606           GROUP BY 1,2,6;
2607
2608         IF NOT FOUND THEN
2609             RETURN QUERY SELECT -1, ans.id, 0::BIGINT, 0::BIGINT, 0::BIGINT, trans;
2610         END IF;
2611
2612     END LOOP;
2613
2614     RETURN;
2615 END;
2616 $f$ LANGUAGE PLPGSQL;
2617
2618 CREATE OR REPLACE FUNCTION asset.opac_ou_metarecord_copy_count (org INT, rid BIGINT) RETURNS TABLE (depth INT, org_unit INT, visible BIGINT, available BIGINT, unshadow BIGINT, transcendant INT) AS $f$
2619 DECLARE
2620     ans RECORD;
2621     trans INT;
2622 BEGIN
2623     SELECT 1 INTO trans FROM biblio.record_entry b JOIN config.bib_source src ON (b.source = src.id) JOIN metabib.metarecord_source_map m ON (m.source = b.id) WHERE src.transcendant AND m.metarecord = rid;
2624
2625     FOR ans IN SELECT u.id, t.depth FROM actor.org_unit_ancestors(org) AS u JOIN actor.org_unit_type t ON (u.ou_type = t.id) LOOP
2626         RETURN QUERY
2627         WITH org_list AS (SELECT ARRAY_AGG(id)::BIGINT[] AS orgs FROM actor.org_unit_descendants(ans.id) x),
2628              available_statuses AS (SELECT ARRAY_AGG(id) AS ids FROM config.copy_status WHERE is_available),
2629              mask AS (SELECT c_attrs FROM asset.patron_default_visibility_mask() x)
2630         SELECT  ans.depth,
2631                 ans.id,
2632                 COUNT( av.id ),
2633                 SUM( (cp.status = ANY (available_statuses.ids))::INT ),
2634                 COUNT( av.id ),
2635                 trans
2636           FROM  mask,
2637                 org_list,
2638                 available_statuses,
2639                 asset.copy_vis_attr_cache av
2640                 JOIN asset.copy cp ON (cp.id = av.target_copy)
2641                 JOIN metabib.metarecord_source_map m ON (m.metarecord = rid AND m.source = av.record)
2642           WHERE cp.circ_lib = ANY (org_list.orgs) AND av.vis_attr_vector @@ mask.c_attrs::query_int
2643           GROUP BY 1,2,6;
2644
2645         IF NOT FOUND THEN
2646             RETURN QUERY SELECT ans.depth, ans.id, 0::BIGINT, 0::BIGINT, 0::BIGINT, trans;
2647         END IF;
2648
2649     END LOOP;
2650
2651     RETURN;
2652 END;
2653 $f$ LANGUAGE PLPGSQL;
2654
2655 CREATE OR REPLACE FUNCTION asset.opac_lasso_metarecord_copy_count (i_lasso INT, rid BIGINT) RETURNS TABLE (depth INT, org_unit INT, visible BIGINT, available BIGINT, unshadow BIGINT, transcendant INT) AS $f$
2656 DECLARE
2657     ans RECORD;
2658     trans INT;
2659 BEGIN
2660     SELECT 1 INTO trans FROM biblio.record_entry b JOIN config.bib_source src ON (b.source = src.id) JOIN metabib.metarecord_source_map m ON (m.source = b.id) WHERE src.transcendant AND m.metarecord = rid;
2661
2662     FOR ans IN SELECT u.org_unit AS id FROM actor.org_lasso_map AS u WHERE lasso = i_lasso LOOP
2663         RETURN QUERY
2664         WITH org_list AS (SELECT ARRAY_AGG(id)::BIGINT[] AS orgs FROM actor.org_unit_descendants(ans.id) x),
2665              available_statuses AS (SELECT ARRAY_AGG(id) AS ids FROM config.copy_status WHERE is_available),
2666              mask AS (SELECT c_attrs FROM asset.patron_default_visibility_mask() x)
2667         SELECT  -1,
2668                 ans.id,
2669                 COUNT( av.id ),
2670                 SUM( (cp.status = ANY (available_statuses.ids))::INT ),
2671                 COUNT( av.id ),
2672                 trans
2673           FROM  mask,
2674                 org_list,
2675                 available_statuses,
2676                 asset.copy_vis_attr_cache av
2677                 JOIN asset.copy cp ON (cp.id = av.target_copy)
2678                 JOIN metabib.metarecord_source_map m ON (m.metarecord = rid AND m.source = av.record)
2679           WHERE cp.circ_lib = ANY (org_list.orgs) AND av.vis_attr_vector @@ mask.c_attrs::query_int
2680           GROUP BY 1,2,6;
2681
2682         IF NOT FOUND THEN
2683             RETURN QUERY SELECT ans.depth, ans.id, 0::BIGINT, 0::BIGINT, 0::BIGINT, trans;
2684         END IF;
2685
2686     END LOOP;
2687
2688     RETURN;
2689 END;
2690 $f$ LANGUAGE PLPGSQL;
2691
2692 CREATE OR REPLACE FUNCTION unapi.mmr_mra (
2693     obj_id BIGINT,
2694     format TEXT,
2695     ename TEXT,
2696     includes TEXT[],
2697     org TEXT,
2698     depth INT DEFAULT NULL,
2699     slimit HSTORE DEFAULT NULL,
2700     soffset HSTORE DEFAULT NULL,
2701     include_xmlns BOOL DEFAULT TRUE,
2702     pref_lib INT DEFAULT NULL
2703 ) RETURNS XML AS $F$
2704     SELECT  XMLELEMENT(
2705         name attributes,
2706         XMLATTRIBUTES(
2707             CASE WHEN $9 THEN 'http://open-ils.org/spec/indexing/v1' ELSE NULL END AS xmlns,
2708             'tag:open-ils.org:U2@mmr/' || $1 AS metarecord
2709         ),
2710         (SELECT XMLAGG(foo.y)
2711           FROM (
2712             WITH sourcelist AS (
2713                 WITH aou AS (SELECT COALESCE(id, (evergreen.org_top()).id) AS id FROM actor.org_unit WHERE shortname = $5 LIMIT 1),
2714                      basevm AS (SELECT c_attrs FROM  asset.patron_default_visibility_mask()),
2715                      circvm AS (SELECT search.calculate_visibility_attribute_test('circ_lib', ARRAY_AGG(aoud.id)) AS mask
2716                                   FROM aou, LATERAL actor.org_unit_descendants(aou.id, $6) aoud)
2717                 SELECT  source
2718                   FROM  aou, circvm, basevm, metabib.metarecord_source_map mmsm
2719                   WHERE mmsm.metarecord = $1 AND (
2720                     EXISTS (
2721                         SELECT  1
2722                           FROM  circvm, basevm, asset.copy_vis_attr_cache acvac
2723                           WHERE acvac.vis_attr_vector @@ (basevm.c_attrs || '&' || circvm.mask)::query_int
2724                                 AND acvac.record = mmsm.source
2725                     )
2726                     OR EXISTS (SELECT 1 FROM evergreen.located_uris(source, aou.id, $10) LIMIT 1)
2727                     OR EXISTS (SELECT 1 FROM biblio.record_entry b JOIN config.bib_source src ON (b.source = src.id) WHERE src.transcendant AND b.id = mmsm.source)
2728                 )
2729             )
2730             SELECT  cmra.aid,
2731                     XMLELEMENT(
2732                         name field,
2733                         XMLATTRIBUTES(
2734                             cmra.attr AS name,
2735                             cmra.value AS "coded-value",
2736                             cmra.aid AS "cvmid",
2737                             rad.composite,
2738                             rad.multi,
2739                             rad.filter,
2740                             rad.sorter,
2741                             cmra.source_list
2742                         ),
2743                         cmra.value
2744                     )
2745               FROM  (
2746                 SELECT DISTINCT aid, attr, value, STRING_AGG(x.id::TEXT, ',') AS source_list
2747                   FROM (
2748                     SELECT  v.source AS id,
2749                             c.id AS aid,
2750                             c.ctype AS attr,
2751                             c.code AS value
2752                       FROM  metabib.record_attr_vector_list v
2753                             JOIN config.coded_value_map c ON ( c.id = ANY( v.vlist ) )
2754                     ) AS x
2755                     JOIN sourcelist ON (x.id = sourcelist.source)
2756                     GROUP BY 1, 2, 3
2757                 ) AS cmra
2758                 JOIN config.record_attr_definition rad ON (cmra.attr = rad.name)
2759                 UNION ALL
2760             SELECT  umra.aid,
2761                     XMLELEMENT(
2762                         name field,
2763                         XMLATTRIBUTES(
2764                             umra.attr AS name,
2765                             rad.composite,
2766                             rad.multi,
2767                             rad.filter,
2768                             rad.sorter
2769                         ),
2770                         umra.value
2771                     )
2772               FROM  (
2773                 SELECT DISTINCT aid, attr, value
2774                   FROM (
2775                     SELECT  v.source AS id,
2776                             m.id AS aid,
2777                             m.attr AS attr,
2778                             m.value AS value
2779                       FROM  metabib.record_attr_vector_list v
2780                             JOIN metabib.uncontrolled_record_attr_value m ON ( m.id = ANY( v.vlist ) )
2781                     ) AS x
2782                     JOIN sourcelist ON (x.id = sourcelist.source)
2783                 ) AS umra
2784                 JOIN config.record_attr_definition rad ON (umra.attr = rad.name)
2785                 ORDER BY 1
2786
2787             )foo(id,y)
2788         )
2789     )
2790 $F$ LANGUAGE SQL STABLE;
2791
2792 CREATE OR REPLACE FUNCTION evergreen.ranked_volumes(
2793     bibid BIGINT[],
2794     ouid INT,
2795     depth INT DEFAULT NULL,
2796     slimit HSTORE DEFAULT NULL,
2797     soffset HSTORE DEFAULT NULL,
2798     pref_lib INT DEFAULT NULL,
2799     includes TEXT[] DEFAULT NULL::TEXT[]
2800 ) RETURNS TABLE(id BIGINT, name TEXT, label_sortkey TEXT, rank BIGINT) AS $$
2801     WITH RECURSIVE ou_depth AS (
2802         SELECT COALESCE(
2803             $3,
2804             (
2805                 SELECT depth
2806                 FROM actor.org_unit_type aout
2807                     INNER JOIN actor.org_unit ou ON ou_type = aout.id
2808                 WHERE ou.id = $2
2809             )
2810         ) AS depth
2811     ), descendant_depth AS (
2812         SELECT  ou.id,
2813                 ou.parent_ou,
2814                 out.depth
2815         FROM  actor.org_unit ou
2816                 JOIN actor.org_unit_type out ON (out.id = ou.ou_type)
2817                 JOIN anscestor_depth ad ON (ad.id = ou.id),
2818                 ou_depth
2819         WHERE ad.depth = ou_depth.depth
2820             UNION ALL
2821         SELECT  ou.id,
2822                 ou.parent_ou,
2823                 out.depth
2824         FROM  actor.org_unit ou
2825                 JOIN actor.org_unit_type out ON (out.id = ou.ou_type)
2826                 JOIN descendant_depth ot ON (ot.id = ou.parent_ou)
2827     ), anscestor_depth AS (
2828         SELECT  ou.id,
2829                 ou.parent_ou,
2830                 out.depth
2831         FROM  actor.org_unit ou
2832                 JOIN actor.org_unit_type out ON (out.id = ou.ou_type)
2833         WHERE ou.id = $2
2834             UNION ALL
2835         SELECT  ou.id,
2836                 ou.parent_ou,
2837                 out.depth
2838         FROM  actor.org_unit ou
2839                 JOIN actor.org_unit_type out ON (out.id = ou.ou_type)
2840                 JOIN anscestor_depth ot ON (ot.parent_ou = ou.id)
2841     ), descendants as (
2842         SELECT ou.* FROM actor.org_unit ou JOIN descendant_depth USING (id)
2843     )
2844
2845     SELECT ua.id, ua.name, ua.label_sortkey, MIN(ua.rank) AS rank FROM (
2846         SELECT acn.id, owning_lib.name, acn.label_sortkey,
2847             evergreen.rank_cp(acp),
2848             RANK() OVER w
2849         FROM asset.call_number acn
2850             JOIN asset.copy acp ON (acn.id = acp.call_number)
2851             JOIN descendants AS aou ON (acp.circ_lib = aou.id)
2852             JOIN actor.org_unit AS owning_lib ON (acn.owning_lib = owning_lib.id)
2853         WHERE acn.record = ANY ($1)
2854             AND acn.deleted IS FALSE
2855             AND acp.deleted IS FALSE
2856             AND CASE WHEN ('exclude_invisible_acn' = ANY($7)) THEN
2857                 EXISTS (
2858                     WITH basevm AS (SELECT c_attrs FROM  asset.patron_default_visibility_mask()),
2859                          circvm AS (SELECT search.calculate_visibility_attribute_test('circ_lib', ARRAY[acp.circ_lib]) AS mask)
2860                     SELECT  1
2861                       FROM  basevm, circvm, asset.copy_vis_attr_cache acvac
2862                       WHERE acvac.vis_attr_vector @@ (basevm.c_attrs || '&' || circvm.mask)::query_int
2863                             AND acvac.target_copy = acp.id
2864                             AND acvac.record = acn.record
2865                 ) ELSE TRUE END
2866         GROUP BY acn.id, evergreen.rank_cp(acp), owning_lib.name, acn.label_sortkey, aou.id
2867         WINDOW w AS (
2868             ORDER BY
2869                 COALESCE(
2870                     CASE WHEN aou.id = $2 THEN -20000 END,
2871                     CASE WHEN aou.id = $6 THEN -10000 END,
2872                     (SELECT distance - 5000
2873                         FROM actor.org_unit_descendants_distance($6) as x
2874                         WHERE x.id = aou.id AND $6 IN (
2875                             SELECT q.id FROM actor.org_unit_descendants($2) as q)),
2876                     (SELECT e.distance FROM actor.org_unit_descendants_distance($2) as e WHERE e.id = aou.id),
2877                     1000
2878                 ),
2879                 evergreen.rank_cp(acp)
2880         )
2881     ) AS ua
2882     GROUP BY ua.id, ua.name, ua.label_sortkey
2883     ORDER BY rank, ua.name, ua.label_sortkey
2884     LIMIT ($4 -> 'acn')::INT
2885     OFFSET ($5 -> 'acn')::INT;
2886 $$ LANGUAGE SQL STABLE ROWS 10;
2887
2888
2889 -- Evergreen DB patch XXXX.schema.action-trigger.event_definition.sms_preminder.sql
2890 --
2891 -- New action trigger event definition: 3 Day Courtesy Notice by SMS
2892 --
2893
2894 -- check whether patch can be applied
2895 SELECT evergreen.upgrade_deps_block_check('1058', :eg_version); -- mccanna/csharp/gmcharlt
2896
2897 INSERT INTO action_trigger.event_definition (id, active, owner, name, hook,
2898         validator, reactor, delay, max_delay, delay_field, group_field, template)
2899     VALUES (54, FALSE, 1,
2900         '3 Day Courtesy Notice by SMS',
2901         'checkout.due',
2902         'CircIsOpen', 'SendSMS', '-3 days', '-2 days', 'due_date', 'usr',
2903 $$
2904 [%- USE date -%]
2905 [%- user = target.0.usr -%]
2906 [%- homelib = user.home_ou -%]
2907 [%- sms_number = helpers.get_user_setting(user.id, 'opac.default_sms_notify') -%]
2908 [%- sms_carrier = helpers.get_user_setting(user.id, 'opac.default_sms_carrier') -%]
2909 From: [%- helpers.get_org_setting(homelib.id, 'org.bounced_emails') || homelib.email || params.sender_email || default_sender %]
2910 To: [%- helpers.get_sms_gateway_email(sms_carrier,sms_number) %]
2911 Subject: Library Materials Due Soon
2912
2913 You have items due soon:
2914
2915 [% FOR circ IN target %]
2916 [%- copy_details = helpers.get_copy_bib_basics(circ.target_copy.id) -%]
2917 [% copy_details.title FILTER ucfirst %] by [% copy_details.author FILTER ucfirst %] due on [% date.format(helpers.format_date(circ.due_date), '%m-%d-%Y') %]
2918
2919 [% END %]
2920
2921 $$);
2922
2923 INSERT INTO action_trigger.environment (event_def, path) VALUES
2924     (54, 'circ_lib.billing_address'),
2925     (54, 'target_copy.call_number'),
2926     (54, 'usr'),
2927     (54, 'usr.home_ou');
2928
2929
2930 -- check whether patch can be applied
2931 SELECT evergreen.upgrade_deps_block_check('1059', :eg_version); --Stompro/DPearl/kmlussier
2932
2933 CREATE OR REPLACE VIEW reporter.old_super_simple_record AS
2934 SELECT  r.id,
2935     r.fingerprint,
2936     r.quality,
2937     r.tcn_source,
2938     r.tcn_value,
2939     CONCAT_WS(' ', FIRST(title.value),FIRST(title_np.val)) AS title,
2940     FIRST(author.value) AS author,
2941     STRING_AGG(DISTINCT publisher.value, ', ') AS publisher,
2942     STRING_AGG(DISTINCT SUBSTRING(pubdate.value FROM $$\d+$$), ', ') AS pubdate,
2943     CASE WHEN ARRAY_AGG( DISTINCT REPLACE(SUBSTRING(isbn.value FROM $$^\S+$$), '-', '') ) = '{NULL}'
2944         THEN NULL
2945         ELSE ARRAY_AGG( DISTINCT REPLACE(SUBSTRING(isbn.value FROM $$^\S+$$), '-', '') )
2946     END AS isbn,
2947     CASE WHEN ARRAY_AGG( DISTINCT REGEXP_REPLACE(issn.value, E'^\\S*(\\d{4})[-\\s](\\d{3,4}x?)', E'\\1 \\2') ) = '{NULL}'
2948         THEN NULL
2949         ELSE ARRAY_AGG( DISTINCT REGEXP_REPLACE(issn.value, E'^\\S*(\\d{4})[-\\s](\\d{3,4}x?)', E'\\1 \\2') )
2950     END AS issn
2951   FROM  biblio.record_entry r
2952     LEFT JOIN metabib.full_rec title ON (r.id = title.record AND title.tag = '245' AND title.subfield = 'a')
2953     LEFT JOIN ( -- Grab 245 N and P subfields in the order that they appear.
2954       SELECT b.record, string_agg(val, ' ') AS val FROM (
2955              SELECT title_np.record, title_np.value AS val
2956               FROM metabib.full_rec title_np
2957               WHERE
2958               title_np.tag = '245'
2959                         AND title_np.subfield IN ('p','n')                      
2960                         ORDER BY title_np.id
2961                 ) b
2962                 GROUP BY 1
2963          ) title_np ON (title_np.record=r.id) 
2964     LEFT JOIN metabib.full_rec author ON (r.id = author.record AND author.tag IN ('100','110','111') AND author.subfield = 'a')
2965     LEFT JOIN metabib.full_rec publisher ON (r.id = publisher.record AND (publisher.tag = '260' OR (publisher.tag = '264' AND publisher.ind2 = '1')) AND publisher.subfield = 'b')
2966     LEFT JOIN metabib.full_rec pubdate ON (r.id = pubdate.record AND (pubdate.tag = '260' OR (pubdate.tag = '264' AND pubdate.ind2 = '1')) AND pubdate.subfield = 'c')
2967     LEFT JOIN metabib.full_rec isbn ON (r.id = isbn.record AND isbn.tag IN ('024', '020') AND isbn.subfield IN ('a','z'))
2968     LEFT JOIN metabib.full_rec issn ON (r.id = issn.record AND issn.tag = '022' AND issn.subfield = 'a')
2969   GROUP BY 1,2,3,4,5;
2970
2971   
2972   -- Remove trigger on biblio.record_entry
2973   SELECT reporter.disable_materialized_simple_record_trigger();
2974   
2975   -- Rebuild reporter.materialized_simple_record
2976   SELECT reporter.enable_materialized_simple_record_trigger();
2977   
2978
2979 SELECT evergreen.upgrade_deps_block_check('1060', :eg_version);
2980
2981 DROP VIEW IF EXISTS extend_reporter.copy_count_per_org;
2982
2983
2984 CREATE OR REPLACE VIEW extend_reporter.copy_count_per_org AS
2985  SELECT acn.record AS bibid,
2986     ac.circ_lib,
2987     acn.owning_lib,
2988     max(ac.edit_date) AS last_edit_time,
2989     min(ac.deleted::integer) AS has_only_deleted_copies,
2990     count(
2991         CASE
2992             WHEN ac.deleted THEN ac.id
2993             ELSE NULL::bigint
2994         END) AS deleted_count,
2995     count(
2996         CASE
2997             WHEN NOT ac.deleted THEN ac.id
2998             ELSE NULL::bigint
2999         END) AS visible_count,
3000     count(*) AS total_count
3001    FROM asset.call_number acn,
3002     asset.copy ac
3003   WHERE ac.call_number = acn.id
3004   GROUP BY acn.record, acn.owning_lib, ac.circ_lib;
3005
3006
3007
3008 SELECT evergreen.upgrade_deps_block_check('1061', :eg_version);
3009
3010 INSERT INTO config.org_unit_setting_type
3011     (name, label, description, grp, datatype)
3012 VALUES (
3013     'ui.staff.max_recent_patrons',
3014     oils_i18n_gettext(
3015         'ui.staff.max_recent_patrons',
3016         'Number of Retrievable Recent Patrons',
3017         'coust',
3018         'label'
3019     ),
3020     oils_i18n_gettext(
3021         'ui.staff.max_recent_patrons',
3022         'Number of most recently accessed patrons that can be re-retrieved ' ||
3023         'in the staff client.  A value of 0 or less disables the feature. Defaults to 1.',
3024         'coust',
3025         'description'
3026     ),
3027     'circ',
3028     'integer'
3029 );
3030
3031
3032 SELECT evergreen.upgrade_deps_block_check('1062', :eg_version);
3033
3034 CREATE TABLE acq.edi_attr (
3035     key     TEXT PRIMARY KEY,
3036     label   TEXT NOT NULL UNIQUE
3037 );
3038
3039 CREATE TABLE acq.edi_attr_set (
3040     id      SERIAL  PRIMARY KEY,
3041     label   TEXT NOT NULL UNIQUE
3042 );
3043
3044 CREATE TABLE acq.edi_attr_set_map (
3045     id          SERIAL  PRIMARY KEY,
3046     attr_set    INTEGER NOT NULL REFERENCES acq.edi_attr_set(id) 
3047                 ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
3048     attr        TEXT NOT NULL REFERENCES acq.edi_attr(key) 
3049                 ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
3050     CONSTRAINT edi_attr_set_map_attr_once UNIQUE (attr_set, attr)
3051 );
3052
3053 -- An attr_set is not strictly required, since some edi_accounts/vendors 
3054 -- may not need to apply any attributes.
3055 ALTER TABLE acq.edi_account 
3056     ADD COLUMN attr_set INTEGER REFERENCES acq.edi_attr_set(id),
3057     ADD COLUMN use_attrs BOOLEAN NOT NULL DEFAULT FALSE;
3058
3059
3060
3061
3062 SELECT evergreen.upgrade_deps_block_check('1063', :eg_version);
3063
3064 DO $temp$
3065 DECLARE
3066         r RECORD;
3067 BEGIN
3068
3069         FOR r IN SELECT t.table_schema AS sname,
3070                         t.table_name AS tname,
3071                         t.column_name AS colname,
3072                         t.constraint_name
3073                   FROM  information_schema.referential_constraints ref
3074                         JOIN information_schema.key_column_usage t USING (constraint_schema,constraint_name)
3075                   WHERE ref.unique_constraint_schema = 'asset'
3076                         AND ref.unique_constraint_name = 'copy_pkey'
3077         LOOP
3078
3079                 EXECUTE 'ALTER TABLE '||r.sname||'.'||r.tname||' DROP CONSTRAINT '||r.constraint_name||';';
3080
3081                 EXECUTE '
3082                         CREATE OR REPLACE FUNCTION evergreen.'||r.sname||'_'||r.tname||'_'||r.colname||'_inh_fkey() RETURNS TRIGGER AS $f$
3083                         BEGIN
3084                                 PERFORM 1 FROM asset.copy WHERE id = NEW.'||r.colname||';
3085                                 IF NOT FOUND THEN
3086                                         RAISE foreign_key_violation USING MESSAGE = FORMAT(
3087                                                 $$Referenced asset.copy id not found, '||r.colname||':%s$$, NEW.'||r.colname||'
3088                                         );
3089                                 END IF;
3090                                 RETURN NEW;
3091                         END;
3092                         $f$ LANGUAGE PLPGSQL VOLATILE COST 50;
3093                 ';
3094
3095                 EXECUTE '
3096                         CREATE CONSTRAINT TRIGGER inherit_'||r.constraint_name||'
3097                                 AFTER UPDATE OR INSERT OR DELETE ON '||r.sname||'.'||r.tname||'
3098                                 DEFERRABLE FOR EACH ROW EXECUTE PROCEDURE evergreen.'||r.sname||'_'||r.tname||'_'||r.colname||'_inh_fkey();
3099                 ';
3100         END LOOP;
3101 END
3102 $temp$;
3103
3104
3105
3106 SELECT evergreen.upgrade_deps_block_check('1064', :eg_version);
3107
3108 ALTER TABLE serial.issuance DROP CONSTRAINT IF EXISTS issuance_caption_and_pattern_fkey;
3109
3110 -- Using NOT VALID and VALIDATE CONSTRAINT limits the impact to concurrent work.
3111 -- For details, see: https://www.postgresql.org/docs/current/static/sql-altertable.html
3112
3113 ALTER TABLE serial.issuance ADD CONSTRAINT issuance_caption_and_pattern_fkey
3114     FOREIGN KEY (caption_and_pattern)
3115     REFERENCES serial.caption_and_pattern (id)
3116     ON DELETE CASCADE
3117     DEFERRABLE INITIALLY DEFERRED
3118     NOT VALID;
3119
3120 ALTER TABLE serial.issuance VALIDATE CONSTRAINT issuance_caption_and_pattern_fkey;
3121
3122
3123
3124 SELECT evergreen.upgrade_deps_block_check('1065', :eg_version);
3125
3126 CREATE TABLE serial.pattern_template (
3127     id            SERIAL PRIMARY KEY,
3128     name          TEXT NOT NULL,
3129     pattern_code  TEXT NOT NULL,
3130     owning_lib    INTEGER REFERENCES actor.org_unit(id) DEFERRABLE INITIALLY DEFERRED,
3131     share_depth   INTEGER NOT NULL DEFAULT 0
3132 );
3133 CREATE INDEX serial_pattern_template_name_idx ON serial.pattern_template (evergreen.lowercase(name));
3134
3135 CREATE OR REPLACE FUNCTION serial.pattern_templates_visible_to(org_unit INT) RETURNS SETOF serial.pattern_template AS $func$
3136 BEGIN
3137     RETURN QUERY SELECT *
3138            FROM serial.pattern_template spt
3139            WHERE (
3140              SELECT ARRAY_AGG(id)
3141              FROM actor.org_unit_descendants(spt.owning_lib, spt.share_depth)
3142            ) @@ org_unit::TEXT::QUERY_INT;
3143 END;
3144 $func$ LANGUAGE PLPGSQL;
3145
3146
3147 SELECT evergreen.upgrade_deps_block_check('1066', :eg_version);
3148
3149 INSERT INTO permission.perm_list ( id, code, description ) VALUES
3150  ( 593, 'ADMIN_SERIAL_PATTERN_TEMPLATE', oils_i18n_gettext( 593,
3151     'Administer serial prediction pattern templates', 'ppl', 'description' ))
3152 ;
3153
3154 INSERT INTO permission.grp_perm_map (grp, perm, depth, grantable)
3155     SELECT
3156         pgt.id, perm.id, aout.depth, FALSE
3157     FROM
3158         permission.grp_tree pgt,
3159         permission.perm_list perm,
3160         actor.org_unit_type aout
3161     WHERE
3162         pgt.name = 'Serials' AND
3163         aout.name = 'System' AND
3164         perm.code IN (
3165             'ADMIN_SERIAL_PATTERN_TEMPLATE'
3166         );
3167
3168
3169 SELECT evergreen.upgrade_deps_block_check('1067', :eg_version);
3170
3171 INSERT INTO acq.edi_attr (key, label) VALUES
3172     ('INCLUDE_PO_NAME', 
3173         oils_i18n_gettext('INCLUDE_PO_NAME', 
3174         'Orders Include PO Name', 'aea', 'label')),
3175     ('INCLUDE_COPIES', 
3176         oils_i18n_gettext('INCLUDE_COPIES', 
3177         'Orders Include Copy Data', 'aea', 'label')),
3178     ('INCLUDE_FUND', 
3179         oils_i18n_gettext('INCLUDE_FUND', 
3180         'Orders Include Copy Funds', 'aea', 'label')),
3181     ('INCLUDE_CALL_NUMBER', 
3182         oils_i18n_gettext('INCLUDE_CALL_NUMBER', 
3183         'Orders Include Copy Call Numbers', 'aea', 'label')),
3184     ('INCLUDE_ITEM_TYPE', 
3185         oils_i18n_gettext('INCLUDE_ITEM_TYPE', 
3186         'Orders Include Copy Item Types', 'aea', 'label')),
3187     ('INCLUDE_ITEM_BARCODE',
3188         oils_i18n_gettext('INCLUDE_ITEM_BARCODE',
3189         'Orders Include Copy Barcodes', 'aea', 'label')),
3190     ('INCLUDE_LOCATION', 
3191         oils_i18n_gettext('INCLUDE_LOCATION', 
3192         'Orders Include Copy Locations', 'aea', 'label')),
3193     ('INCLUDE_COLLECTION_CODE', 
3194         oils_i18n_gettext('INCLUDE_COLLECTION_CODE', 
3195         'Orders Include Copy Collection Codes', 'aea', 'label')),
3196     ('INCLUDE_OWNING_LIB', 
3197         oils_i18n_gettext('INCLUDE_OWNING_LIB', 
3198         'Orders Include Copy Owning Library', 'aea', 'label')),
3199     ('USE_ID_FOR_OWNING_LIB',
3200         oils_i18n_gettext('USE_ID_FOR_OWNING_LIB',
3201         'Emit Owning Library ID Rather Than Short Name. Takes effect only if INCLUDE_OWNING_LIB is in use', 'aea', 'label')),
3202     ('INCLUDE_QUANTITY', 
3203         oils_i18n_gettext('INCLUDE_QUANTITY', 
3204         'Orders Include Copy Quantities', 'aea', 'label')),
3205     ('INCLUDE_COPY_ID', 
3206         oils_i18n_gettext('INCLUDE_COPY_ID', 
3207         'Orders Include Copy IDs', 'aea', 'label')),
3208     ('BUYER_ID_INCLUDE_VENDCODE', 
3209         oils_i18n_gettext('BUYER_ID_INCLUDE_VENDCODE', 
3210         'Buyer ID Qualifier Includes Vendcode', 'aea', 'label')),
3211     ('BUYER_ID_ONLY_VENDCODE', 
3212         oils_i18n_gettext('BUYER_ID_ONLY_VENDCODE', 
3213         'Buyer ID Qualifier Only Contains Vendcode', 'aea', 'label')),
3214     ('INCLUDE_BIB_EDITION', 
3215         oils_i18n_gettext('INCLUDE_BIB_EDITION', 
3216         'Order Lineitems Include Edition Info', 'aea', 'label')),
3217     ('INCLUDE_BIB_AUTHOR', 
3218         oils_i18n_gettext('INCLUDE_BIB_AUTHOR', 
3219         'Order Lineitems Include Author Info', 'aea', 'label')),
3220     ('INCLUDE_BIB_PAGINATION', 
3221         oils_i18n_gettext('INCLUDE_BIB_PAGINATION', 
3222         'Order Lineitems Include Pagination Info', 'aea', 'label')),
3223     ('COPY_SPEC_CODES', 
3224         oils_i18n_gettext('COPY_SPEC_CODES', 
3225         'Order Lineitem Notes Include Copy Spec Codes', 'aea', 'label')),
3226     ('INCLUDE_EMPTY_IMD_VALUES', 
3227         oils_i18n_gettext('INCLUDE_EMPTY_IMD_VALUES',
3228         'Lineitem Title, Author, etc. Fields Are Present Even if Empty', 'aea', 'label')),
3229     ('INCLUDE_EMPTY_LI_NOTE', 
3230         oils_i18n_gettext('INCLUDE_EMPTY_LI_NOTE', 
3231         'Order Lineitem Notes Always Present (Even if Empty)', 'aea', 'label')),
3232     ('INCLUDE_EMPTY_CALL_NUMBER', 
3233         oils_i18n_gettext('INCLUDE_EMPTY_CALL_NUMBER', 
3234         'Order Copies Always Include Call Number (Even if Empty)', 'aea', 'label')),
3235     ('INCLUDE_EMPTY_ITEM_TYPE', 
3236         oils_i18n_gettext('INCLUDE_EMPTY_ITEM_TYPE', 
3237         'Order Copies Always Include Item Type (Even if Empty)', 'aea', 'label')),
3238     ('INCLUDE_EMPTY_LOCATION', 
3239         oils_i18n_gettext('INCLUDE_EMPTY_LOCATION', 
3240         'Order Copies Always Include Location (Even if Empty)', 'aea', 'label')),
3241     ('INCLUDE_EMPTY_COLLECTION_CODE', 
3242         oils_i18n_gettext('INCLUDE_EMPTY_COLLECTION_CODE', 
3243         'Order Copies Always Include Collection Code (Even if Empty)', 'aea', 'label')),
3244     ('LINEITEM_IDENT_VENDOR_NUMBER',
3245         oils_i18n_gettext('LINEITEM_IDENT_VENDOR_NUMBER',
3246         'Lineitem Identifier Fields (LIN/PIA) Use Vendor-Encoded ID Value When Available', 'aea', 'label')),
3247     ('LINEITEM_REF_ID_ONLY',
3248         oils_i18n_gettext('LINEITEM_REF_ID_ONLY',
3249         'Lineitem Reference Field (RFF) Uses Lineitem ID Only', 'aea', 'label'))
3250
3251 ;
3252
3253 INSERT INTO acq.edi_attr_set (id, label) VALUES (1, 'Ingram Default');
3254 INSERT INTO acq.edi_attr_set (id, label) VALUES (2, 'Baker & Taylor Default');
3255 INSERT INTO acq.edi_attr_set (id, label) VALUES (3, 'Brodart Default');
3256 INSERT INTO acq.edi_attr_set (id, label) VALUES (4, 'Midwest Tape Default');
3257 INSERT INTO acq.edi_attr_set (id, label) VALUES (5, 'ULS Default');
3258 INSERT INTO acq.edi_attr_set (id, label) VALUES (6, 'Recorded Books Default');
3259 INSERT INTO acq.edi_attr_set (id, label) VALUES (7, 'Midwest Library Service');
3260
3261 -- carve out space for mucho defaults
3262 SELECT SETVAL('acq.edi_attr_set_id_seq'::TEXT, 1000);
3263
3264 INSERT INTO acq.edi_attr_set_map (attr_set, attr) VALUES
3265
3266     -- Ingram
3267     (1, 'INCLUDE_PO_NAME'),
3268     (1, 'INCLUDE_COPIES'),
3269     (1, 'INCLUDE_ITEM_TYPE'),
3270     (1, 'INCLUDE_COLLECTION_CODE'),
3271     (1, 'INCLUDE_OWNING_LIB'),
3272     (1, 'INCLUDE_QUANTITY'),
3273     (1, 'INCLUDE_BIB_PAGINATION'),
3274
3275     -- B&T
3276     (2, 'INCLUDE_COPIES'),
3277     (2, 'INCLUDE_ITEM_TYPE'),
3278     (2, 'INCLUDE_COLLECTION_CODE'),
3279     (2, 'INCLUDE_CALL_NUMBER'),
3280     (2, 'INCLUDE_OWNING_LIB'),
3281     (2, 'INCLUDE_QUANTITY'),
3282     (2, 'INCLUDE_BIB_PAGINATION'),
3283     (2, 'BUYER_ID_INCLUDE_VENDCODE'),
3284     (2, 'INCLUDE_EMPTY_LI_NOTE'),
3285     (2, 'INCLUDE_EMPTY_CALL_NUMBER'),
3286     (2, 'INCLUDE_EMPTY_ITEM_TYPE'),
3287     (2, 'INCLUDE_EMPTY_COLLECTION_CODE'),
3288     (2, 'INCLUDE_EMPTY_LOCATION'),
3289     (2, 'LINEITEM_IDENT_VENDOR_NUMBER'),
3290     (2, 'LINEITEM_REF_ID_ONLY'),
3291
3292     -- Brodart
3293     (3, 'INCLUDE_COPIES'),
3294     (3, 'INCLUDE_FUND'),
3295     (3, 'INCLUDE_ITEM_TYPE'),
3296     (3, 'INCLUDE_COLLECTION_CODE'),
3297     (3, 'INCLUDE_OWNING_LIB'),
3298     (3, 'INCLUDE_QUANTITY'),
3299     (3, 'INCLUDE_BIB_PAGINATION'),
3300     (3, 'COPY_SPEC_CODES'),
3301
3302     -- Midwest
3303     (4, 'INCLUDE_COPIES'),
3304     (4, 'INCLUDE_FUND'),
3305     (4, 'INCLUDE_OWNING_LIB'),
3306     (4, 'INCLUDE_QUANTITY'),
3307     (4, 'INCLUDE_BIB_PAGINATION'),
3308
3309     -- ULS
3310     (5, 'INCLUDE_COPIES'),
3311     (5, 'INCLUDE_ITEM_TYPE'),
3312     (5, 'INCLUDE_COLLECTION_CODE'),
3313     (5, 'INCLUDE_OWNING_LIB'),
3314     (5, 'INCLUDE_QUANTITY'),
3315     (5, 'INCLUDE_BIB_AUTHOR'),
3316     (5, 'INCLUDE_BIB_EDITION'),
3317     (5, 'INCLUDE_EMPTY_LI_NOTE'),
3318
3319     -- Recorded Books
3320     (6, 'INCLUDE_COPIES'),
3321     (6, 'INCLUDE_ITEM_TYPE'),
3322     (6, 'INCLUDE_COLLECTION_CODE'),
3323     (6, 'INCLUDE_OWNING_LIB'),
3324     (6, 'INCLUDE_QUANTITY'),
3325     (6, 'INCLUDE_BIB_PAGINATION'),
3326
3327     -- Midwest Library Service
3328     (7, 'INCLUDE_BIB_AUTHOR'),
3329     (7, 'INCLUDE_BIB_EDITION'),
3330     (7, 'BUYER_ID_ONLY_VENDCODE'),
3331     (7, 'INCLUDE_EMPTY_IMD_VALUES')
3332 ;
3333
3334
3335
3336
3337
3338 SELECT evergreen.upgrade_deps_block_check('1068', :eg_version); --miker/gmcharlt/kmlussier
3339
3340 INSERT INTO config.xml_transform (name,namespace_uri,prefix,xslt) VALUES ('mads21','http://www.loc.gov/mads/v2','mads21',$XSLT$<?xml version="1.0" encoding="UTF-8"?>
3341 <xsl:stylesheet version="1.0" xmlns:mads="http://www.loc.gov/mads/v2"
3342         xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:marc="http://www.loc.gov/MARC21/slim"
3343         xmlns:xsl="http://www.w3.org/1999/XSL/Transform" exclude-result-prefixes="marc">
3344         <xsl:output method="xml" indent="yes" encoding="UTF-8"/>
3345         <xsl:strip-space elements="*"/>
3346
3347         <xsl:variable name="ascii">
3348                 <xsl:text> !"#$%&amp;'()*+,-./0123456789:;&lt;=&gt;?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~</xsl:text>
3349         </xsl:variable>
3350
3351         <xsl:variable name="latin1">
3352                 <xsl:text> ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ</xsl:text>
3353         </xsl:variable>
3354         <!-- Characters that usually don't need to be escaped -->
3355         <xsl:variable name="safe">
3356                 <xsl:text>!'()*-.0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz~</xsl:text>
3357         </xsl:variable>
3358
3359         <xsl:variable name="hex">0123456789ABCDEF</xsl:variable>
3360
3361
3362         <xsl:template name="datafield">
3363                 <xsl:param name="tag"/>
3364                 <xsl:param name="ind1">
3365                         <xsl:text> </xsl:text>
3366                 </xsl:param>
3367                 <xsl:param name="ind2">
3368                         <xsl:text> </xsl:text>
3369                 </xsl:param>
3370                 <xsl:param name="subfields"/>
3371                 <xsl:element name="marc:datafield">
3372                         <xsl:attribute name="tag">
3373                                 <xsl:value-of select="$tag"/>
3374                         </xsl:attribute>
3375                         <xsl:attribute name="ind1">
3376                                 <xsl:value-of select="$ind1"/>
3377                         </xsl:attribute>
3378                         <xsl:attribute name="ind2">
3379                                 <xsl:value-of select="$ind2"/>
3380                         </xsl:attribute>
3381                         <xsl:copy-of select="$subfields"/>
3382                 </xsl:element>
3383         </xsl:template>
3384
3385         <xsl:template name="subfieldSelect">
3386                 <xsl:param name="codes">abcdefghijklmnopqrstuvwxyz</xsl:param>
3387                 <xsl:param name="delimeter">
3388                         <xsl:text> </xsl:text>
3389                 </xsl:param>
3390                 <xsl:variable name="str">
3391                         <xsl:for-each select="marc:subfield">
3392                                 <xsl:if test="contains($codes, @code)">
3393                                         <xsl:value-of select="text()"/>
3394                                         <xsl:value-of select="$delimeter"/>
3395                                 </xsl:if>
3396                         </xsl:for-each>
3397                 </xsl:variable>
3398                 <xsl:value-of select="substring($str,1,string-length($str)-string-length($delimeter))"/>
3399         </xsl:template>
3400
3401         <xsl:template name="buildSpaces">
3402                 <xsl:param name="spaces"/>
3403                 <xsl:param name="char">
3404                         <xsl:text> </xsl:text>
3405                 </xsl:param>
3406                 <xsl:if test="$spaces>0">
3407                         <xsl:value-of select="$char"/>
3408                         <xsl:call-template name="buildSpaces">
3409                                 <xsl:with-param name="spaces" select="$spaces - 1"/>
3410                                 <xsl:with-param name="char" select="$char"/>
3411                         </xsl:call-template>
3412                 </xsl:if>
3413         </xsl:template>
3414
3415         <xsl:template name="chopPunctuation">
3416                 <xsl:param name="chopString"/>
3417                 <xsl:param name="punctuation">
3418                         <xsl:text>.:,;/ </xsl:text>
3419                 </xsl:param>
3420                 <xsl:variable name="length" select="string-length($chopString)"/>
3421                 <xsl:choose>
3422                         <xsl:when test="$length=0"/>
3423                         <xsl:when test="contains($punctuation, substring($chopString,$length,1))">
3424                                 <xsl:call-template name="chopPunctuation">
3425                                         <xsl:with-param name="chopString" select="substring($chopString,1,$length - 1)"/>
3426                                         <xsl:with-param name="punctuation" select="$punctuation"/>
3427                                 </xsl:call-template>
3428                         </xsl:when>
3429                         <xsl:when test="not($chopString)"/>
3430                         <xsl:otherwise>
3431                                 <xsl:value-of select="$chopString"/>
3432                         </xsl:otherwise>
3433                 </xsl:choose>
3434         </xsl:template>
3435
3436         <xsl:template name="chopPunctuationFront">
3437                 <xsl:param name="chopString"/>
3438                 <xsl:variable name="length" select="string-length($chopString)"/>
3439                 <xsl:choose>
3440                         <xsl:when test="$length=0"/>
3441                         <xsl:when test="contains('.:,;/[ ', substring($chopString,1,1))">
3442                                 <xsl:call-template name="chopPunctuationFront">
3443                                         <xsl:with-param name="chopString" select="substring($chopString,2,$length - 1)"
3444                                         />
3445                                 </xsl:call-template>
3446                         </xsl:when>
3447                         <xsl:when test="not($chopString)"/>
3448                         <xsl:otherwise>
3449                                 <xsl:value-of select="$chopString"/>
3450                         </xsl:otherwise>
3451                 </xsl:choose>
3452         </xsl:template>
3453
3454         <xsl:template name="chopPunctuationBack">
3455                 <xsl:param name="chopString"/>
3456                 <xsl:param name="punctuation">
3457                         <xsl:text>.:,;/] </xsl:text>
3458                 </xsl:param>
3459                 <xsl:variable name="length" select="string-length($chopString)"/>
3460                 <xsl:choose>
3461                         <xsl:when test="$length=0"/>
3462                         <xsl:when test="contains($punctuation, substring($chopString,$length,1))">
3463                                 <xsl:call-template name="chopPunctuation">
3464                                         <xsl:with-param name="chopString" select="substring($chopString,1,$length - 1)"/>
3465                                         <xsl:with-param name="punctuation" select="$punctuation"/>
3466                                 </xsl:call-template>
3467                         </xsl:when>
3468                         <xsl:when test="not($chopString)"/>
3469                         <xsl:otherwise>
3470                                 <xsl:value-of select="$chopString"/>
3471                         </xsl:otherwise>
3472                 </xsl:choose>
3473         </xsl:template>
3474
3475         <!-- nate added 12/14/2007 for lccn.loc.gov: url encode ampersand, etc. -->
3476         <xsl:template name="url-encode">
3477
3478                 <xsl:param name="str"/>
3479
3480                 <xsl:if test="$str">
3481                         <xsl:variable name="first-char" select="substring($str,1,1)"/>
3482                         <xsl:choose>
3483                                 <xsl:when test="contains($safe,$first-char)">
3484                                         <xsl:value-of select="$first-char"/>
3485                                 </xsl:when>
3486                                 <xsl:otherwise>
3487                                         <xsl:variable name="codepoint">
3488                                                 <xsl:choose>
3489                                                         <xsl:when test="contains($ascii,$first-char)">
3490                                                                 <xsl:value-of
3491                                                                         select="string-length(substring-before($ascii,$first-char)) + 32"
3492                                                                 />
3493                                                         </xsl:when>
3494                                                         <xsl:when test="contains($latin1,$first-char)">
3495                                                                 <xsl:value-of
3496                                                                         select="string-length(substring-before($latin1,$first-char)) + 160"/>
3497                                                                 <!-- was 160 -->
3498                                                         </xsl:when>
3499                                                         <xsl:otherwise>
3500                                                                 <xsl:message terminate="no">Warning: string contains a character
3501                                                                         that is out of range! Substituting "?".</xsl:message>
3502                                                                 <xsl:text>63</xsl:text>
3503                                                         </xsl:otherwise>
3504                                                 </xsl:choose>
3505                                         </xsl:variable>
3506                                         <xsl:variable name="hex-digit1"
3507                                                 select="substring($hex,floor($codepoint div 16) + 1,1)"/>
3508                                         <xsl:variable name="hex-digit2" select="substring($hex,$codepoint mod 16 + 1,1)"/>
3509                                         <!-- <xsl:value-of select="concat('%',$hex-digit2)"/> -->
3510                                         <xsl:value-of select="concat('%',$hex-digit1,$hex-digit2)"/>
3511                                 </xsl:otherwise>
3512                         </xsl:choose>
3513                         <xsl:if test="string-length($str) &gt; 1">
3514                                 <xsl:call-template name="url-encode">
3515                                         <xsl:with-param name="str" select="substring($str,2)"/>
3516                                 </xsl:call-template>
3517                         </xsl:if>
3518                 </xsl:if>
3519         </xsl:template>
3520
3521
3522 <!--
3523 2.14    Fixed bug in mads:geographic attributes syntax                                      ws   05/04/2016             
3524 2.13    fixed repeating <geographic>                                                                                                            tmee 01/31/2014
3525 2.12    added $2 authority for <classification>                                                                                         tmee 09/18/2012
3526 2.11    added delimiters between <classification> subfields                                                                     tmee 09/18/2012
3527 2.10    fixed type="other" and type="otherType" for mads:related                                                        tmee 09/16/2011
3528 2.09    fixed professionTerm and genreTerm empty tag error                                                                      tmee 09/16/2011
3529 2.08    fixed marc:subfield @code='i' matching error                                                                            tmee 09/16/2011
3530 2.07    fixed 555 duplication error                                                                                                                     tmee 08/10/2011 
3531 2.06    fixed topic subfield error                                                                                                                      tmee 08/10/2011 
3532 2.05    fixed title subfield error                                                                                                                      tmee 06/20/2011 
3533 2.04    fixed geographicSubdivision mapping for authority element                                                       tmee 06/16/2011
3534 2.03    added classification for 053, 055, 060, 065, 070, 080, 082, 083, 086, 087                       tmee 06/03/2011         
3535 2.02    added descriptionStandard for 008/10                                                                                            tmee 04/27/2011
3536 2.01    added extensions for 046, 336, 370, 374, 375, 376                                                                       tmee 04/08/2011
3537 2.00    redefined imported MODS elements in version 1.0 to MADS elements in version 2.0         tmee 02/08/2011
3538 1.08    added 372 subfields $a $s $t for <fieldOfActivity>                                                                      tmee 06/24/2010
3539 1.07    removed role/roleTerm 100, 110, 111, 400, 410, 411, 500, 510, 511, 700, 710, 711        tmee 06/24/2010
3540 1.06    added strip-space                                                                                                                                       tmee 06/24/2010
3541 1.05    added subfield $a for 130, 430, 530                                                                                                     tmee 06/21/2010
3542 1.04    fixed 550 z omission                                                                                                                            ntra 08/11/2008
3543 1.03    removed duplication of 550 $a text                                                                                                      tmee 11/01/2006
3544 1.02    fixed namespace references between mads and mods                                                                        ntra 10/06/2006
3545 1.01    revised                                                                                                                                                         rgue/jrad 11/29/05
3546 1.00    adapted from MARC21Slim2MODS3.xsl                                                                                                       ntra 07/06/05
3547 -->
3548
3549         <!-- authority attribute defaults to 'naf' if not set using this authority parameter, for <authority> descriptors: name, titleInfo, geographic -->
3550         <xsl:param name="authority"/>
3551         <xsl:variable name="auth">
3552                 <xsl:choose>
3553                         <xsl:when test="$authority">
3554                                 <xsl:value-of select="$authority"/>
3555                         </xsl:when>
3556                         <xsl:otherwise>naf</xsl:otherwise>
3557                 </xsl:choose>
3558         </xsl:variable>
3559         <xsl:variable name="controlField008" select="marc:controlfield[@tag='008']"/>
3560         <xsl:variable name="controlField008-06"
3561                 select="substring(descendant-or-self::marc:controlfield[@tag=008],7,1)"/>
3562         <xsl:variable name="controlField008-11"
3563                 select="substring(descendant-or-self::marc:controlfield[@tag=008],12,1)"/>
3564         <xsl:variable name="controlField008-14"
3565                 select="substring(descendant-or-self::marc:controlfield[@tag=008],15,1)"/>
3566         <xsl:template match="/">
3567                 <xsl:choose>
3568                         <xsl:when test="descendant-or-self::marc:collection">
3569                                 <mads:madsCollection xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3570                                         xsi:schemaLocation="http://www.loc.gov/mads/v2 http://www.loc.gov/standards/mads/v2/mads-2-0.xsd">
3571                                         <xsl:for-each select="descendant-or-self::marc:collection/marc:record">
3572                                                 <mads:mads version="2.0">
3573                                                         <xsl:call-template name="marcRecord"/>
3574                                                 </mads:mads>
3575                                         </xsl:for-each>
3576                                 </mads:madsCollection>
3577                         </xsl:when>
3578                         <xsl:otherwise>
3579                                 <mads:mads version="2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3580                                         xsi:schemaLocation="http://www.loc.gov/mads/v2 http://www.loc.gov/standards/mads/mads-2-0.xsd">
3581                                         <xsl:for-each select="descendant-or-self::marc:record">
3582                                                 <xsl:call-template name="marcRecord"/>
3583                                         </xsl:for-each>
3584                                 </mads:mads>
3585                         </xsl:otherwise>
3586                 </xsl:choose>
3587         </xsl:template>
3588
3589         <xsl:template name="marcRecord">
3590                 <mads:authority>
3591                         <!-- 2.04 -->
3592                         <xsl:choose>
3593                                 <xsl:when test="$controlField008-06='d'">
3594                                         <xsl:attribute name="geographicSubdivision">
3595                                                 <xsl:text>direct</xsl:text>
3596                                         </xsl:attribute>
3597                                 </xsl:when>
3598                                 <xsl:when test="$controlField008-06='i'">
3599                                         <xsl:attribute name="geographicSubdivision">
3600                                                 <xsl:text>indirect</xsl:text>
3601                                         </xsl:attribute>
3602                                 </xsl:when>
3603                                 <xsl:when test="$controlField008-06='n'">
3604                                         <xsl:attribute name="geographicSubdivision">
3605                                                 <xsl:text>not applicable</xsl:text>
3606                                         </xsl:attribute>
3607                                 </xsl:when>
3608                         </xsl:choose>
3609                         
3610                         <xsl:apply-templates select="marc:datafield[100 &lt;= @tag  and @tag &lt; 200]"/>               
3611                 </mads:authority>
3612
3613                 <!-- related -->
3614                 <xsl:apply-templates
3615                         select="marc:datafield[500 &lt;= @tag and @tag &lt;= 585]|marc:datafield[700 &lt;= @tag and @tag &lt;= 785]"/>
3616
3617                 <!-- variant -->
3618                 <xsl:apply-templates select="marc:datafield[400 &lt;= @tag and @tag &lt;= 485]"/>
3619
3620                 <!-- notes -->
3621                 <xsl:apply-templates select="marc:datafield[667 &lt;= @tag and @tag &lt;= 688]"/>
3622
3623                 <!-- url -->
3624                 <xsl:apply-templates select="marc:datafield[@tag=856]"/>
3625                 <xsl:apply-templates select="marc:datafield[@tag=010]"/>
3626                 <xsl:apply-templates select="marc:datafield[@tag=024]"/>
3627                 <xsl:apply-templates select="marc:datafield[@tag=372]"/>
3628                 
3629                 <!-- classification -->
3630                 <xsl:apply-templates select="marc:datafield[@tag=053]"/>
3631                 <xsl:apply-templates select="marc:datafield[@tag=055]"/>
3632                 <xsl:apply-templates select="marc:datafield[@tag=060]"/>
3633                 <xsl:apply-templates select="marc:datafield[@tag=065]"/>
3634                 <xsl:apply-templates select="marc:datafield[@tag=070]"/>
3635                 <xsl:apply-templates select="marc:datafield[@tag=080]"/>
3636                 <xsl:apply-templates select="marc:datafield[@tag=082]"/>
3637                 <xsl:apply-templates select="marc:datafield[@tag=083]"/>
3638                 <xsl:apply-templates select="marc:datafield[@tag=086]"/>
3639                 <xsl:apply-templates select="marc:datafield[@tag=087]"/>
3640
3641                 <!-- affiliation-->
3642                 <xsl:for-each select="marc:datafield[@tag=373]">
3643                         <mads:affiliation>
3644                                 <mads:position>
3645                                         <xsl:value-of select="marc:subfield[@code='a']"/>
3646                                 </mads:position>
3647                                 <mads:dateValid point="start">
3648                                         <xsl:value-of select="marc:subfield[@code='s']"/>
3649                                 </mads:dateValid>
3650                                 <mads:dateValid point="end">
3651                                         <xsl:value-of select="marc:subfield[@code='t']"/>
3652                                 </mads:dateValid>
3653                         </mads:affiliation>
3654                 </xsl:for-each>
3655                 <xsl:for-each select="marc:datafield[@tag=371]">
3656                         <mads:affiliation>
3657                                 <mads:address>
3658                                         <mads:street>
3659                                                 <xsl:value-of select="marc:subfield[@code='a']"/>
3660                                         </mads:street>
3661                                         <mads:city>
3662                                                 <xsl:value-of select="marc:subfield[@code='b']"/>
3663                                         </mads:city>
3664                                         <mads:state>
3665                                                 <xsl:value-of select="marc:subfield[@code='c']"/>
3666                                         </mads:state>
3667                                         <mads:country>
3668                                                 <xsl:value-of select="marc:subfield[@code='d']"/>
3669                                         </mads:country>
3670                                         <mads:postcode>
3671                                                 <xsl:value-of select="marc:subfield[@code='e']"/>
3672                                         </mads:postcode>
3673                                 </mads:address>
3674                                 <mads:email>
3675                                         <xsl:value-of select="marc:subfield[@code='m']"/>
3676                                 </mads:email>
3677                         </mads:affiliation>
3678                 </xsl:for-each>
3679
3680                 <!-- extension-->
3681                 <xsl:for-each select="marc:datafield[@tag=336]">
3682                         <mads:extension>
3683                                 <mads:contentType>
3684                                         <mads:contentType type="text">
3685                                                 <xsl:value-of select="marc:subfield[@code='a']"/>
3686                                         </mads:contentType>
3687                                         <mads:contentType type="code">
3688                                                 <xsl:value-of select="marc:subfield[@code='b']"/>
3689                                         </mads:contentType>
3690                                 </mads:contentType>
3691                         </mads:extension>
3692                 </xsl:for-each>
3693
3694                 <xsl:for-each select="marc:datafield[@tag=374]">
3695                         <mads:extension>
3696                                 <mads:profession>
3697                                         <xsl:choose>
3698                                                 <xsl:when test="marc:subfield[@code='a']">
3699                                                         <mads:professionTerm>
3700                                                                 <xsl:value-of select="marc:subfield[@code='a']"/>
3701                                                         </mads:professionTerm>
3702                                                 </xsl:when>
3703                                                 <xsl:when test="marc:subfield[@code='s']">
3704                                                         <mads:dateValid point="start">
3705                                                                 <xsl:value-of select="marc:subfield[@code='s']"/>
3706                                                         </mads:dateValid>
3707                                                 </xsl:when>
3708                                                 <xsl:when test="marc:subfield[@code='t']">
3709                                                         <mads:dateValid point="end">
3710                                                                 <xsl:value-of select="marc:subfield[@code='t']"/>
3711                                                         </mads:dateValid>
3712                                                 </xsl:when>
3713                                         </xsl:choose>
3714                                 </mads:profession>
3715                         </mads:extension>
3716                 </xsl:for-each>
3717                 
3718                 <xsl:for-each select="marc:datafield[@tag=375]">
3719                         <mads:extension>
3720                                 <mads:gender>
3721                                         <xsl:choose>
3722                                                 <xsl:when test="marc:subfield[@code='a']">
3723                                                         <mads:genderTerm>
3724                                                                 <xsl:value-of select="marc:subfield[@code='a']"/>
3725                                                         </mads:genderTerm>
3726                                                 </xsl:when>
3727                                                 <xsl:when test="marc:subfield[@code='s']">
3728                                                         <mads:dateValid point="start">
3729                                                                 <xsl:value-of select="marc:subfield[@code='s']"/>
3730                                                         </mads:dateValid>
3731                                                 </xsl:when>
3732                                                 <xsl:when test="marc:subfield[@code='t']">
3733                                                         <mads:dateValid point="end">
3734                                                                 <xsl:value-of select="marc:subfield[@code='t']"/>
3735                                                         </mads:dateValid>
3736                                                 </xsl:when>
3737                                         </xsl:choose>
3738                                 </mads:gender>
3739                         </mads:extension>
3740                 </xsl:for-each>
3741
3742                 <xsl:for-each select="marc:datafield[@tag=376]">
3743                         <mads:extension>
3744                                 <mads:familyInformation>
3745                                         <mads:typeOfFamily>
3746                                                 <xsl:value-of select="marc:subfield[@code='a']"/>
3747                                         </mads:typeOfFamily>
3748                                         <mads:nameOfProminentMember>
3749                                                 <xsl:value-of select="marc:subfield[@code='b']"/>
3750                                         </mads:nameOfProminentMember>
3751                                         <mads:hereditaryTitle>
3752                                                 <xsl:value-of select="marc:subfield[@code='c']"/>
3753                                         </mads:hereditaryTitle>
3754                                         <mads:dateValid point="start">
3755                                                 <xsl:value-of select="marc:subfield[@code='s']"/>
3756                                         </mads:dateValid>
3757                                         <mads:dateValid point="end">
3758                                                 <xsl:value-of select="marc:subfield[@code='t']"/>
3759                                         </mads:dateValid>
3760                                 </mads:familyInformation>
3761                         </mads:extension>
3762                 </xsl:for-each>
3763
3764                 <mads:recordInfo>
3765                         <mads:recordOrigin>Converted from MARCXML to MADS version 2.0 (Revision 2.13)</mads:recordOrigin>
3766                         <!-- <xsl:apply-templates select="marc:datafield[@tag=024]"/> -->
3767
3768                         <xsl:apply-templates select="marc:datafield[@tag=040]/marc:subfield[@code='a']"/>
3769                         <xsl:apply-templates select="marc:controlfield[@tag=005]"/>
3770                         <xsl:apply-templates select="marc:controlfield[@tag=001]"/>
3771                         <xsl:apply-templates select="marc:datafield[@tag=040]/marc:subfield[@code='b']"/>
3772                         <xsl:apply-templates select="marc:datafield[@tag=040]/marc:subfield[@code='e']"/>
3773                         <xsl:for-each select="marc:controlfield[@tag=008]">
3774                                 <xsl:if test="substring(.,11,1)='a'">
3775                                         <mads:descriptionStandard>
3776                                                 <xsl:text>earlier rules</xsl:text>
3777                                         </mads:descriptionStandard>
3778                                 </xsl:if>
3779                                 <xsl:if test="substring(.,11,1)='b'">
3780                                         <mads:descriptionStandard>
3781                                                 <xsl:text>aacr1</xsl:text>
3782                                         </mads:descriptionStandard>
3783                                 </xsl:if>
3784                                 <xsl:if test="substring(.,11,1)='c'">
3785                                         <mads:descriptionStandard>
3786                                                 <xsl:text>aacr2</xsl:text>
3787                                         </mads:descriptionStandard>
3788                                 </xsl:if>
3789                                 <xsl:if test="substring(.,11,1)='d'">
3790                                         <mads:descriptionStandard>
3791                                                 <xsl:text>aacr2 compatible</xsl:text>
3792                                         </mads:descriptionStandard>
3793                                 </xsl:if>
3794                                 <xsl:if test="substring(.,11,1)='z'">
3795                                         <mads:descriptionStandard>
3796                                                 <xsl:text>other rules</xsl:text>
3797                                         </mads:descriptionStandard>
3798                                 </xsl:if>
3799                         </xsl:for-each>
3800                 </mads:recordInfo>
3801         </xsl:template>
3802
3803         <!-- start of secondary templates -->
3804
3805         <!-- ======== xlink ======== -->
3806
3807         <!-- <xsl:template name="uri"> 
3808     <xsl:for-each select="marc:subfield[@code='0']">
3809       <xsl:attribute name="xlink:href">
3810         <xsl:value-of select="."/>
3811       </xsl:attribute>
3812     </xsl:for-each>
3813      </xsl:template> 
3814    -->
3815         <xsl:template match="marc:subfield[@code='i']">
3816                 <xsl:attribute name="otherType">
3817                         <xsl:value-of select="."/>
3818                 </xsl:attribute>
3819         </xsl:template>
3820
3821         <!-- No role/roleTerm mapped in MADS 06/24/2010
3822         <xsl:template name="role">
3823                 <xsl:for-each select="marc:subfield[@code='e']">
3824                         <mads:role>
3825                                 <mads:roleTerm type="text">
3826                                         <xsl:value-of select="."/>
3827                                 </mads:roleTerm>
3828                         </mads:role>
3829                 </xsl:for-each>
3830         </xsl:template>
3831 -->
3832
3833         <xsl:template name="part">
3834                 <xsl:variable name="partNumber">
3835                         <xsl:call-template name="specialSubfieldSelect">
3836                                 <xsl:with-param name="axis">n</xsl:with-param>
3837                                 <xsl:with-param name="anyCodes">n</xsl:with-param>
3838                                 <xsl:with-param name="afterCodes">fghkdlmor</xsl:with-param>
3839                         </xsl:call-template>
3840                 </xsl:variable>
3841                 <xsl:variable name="partName">
3842                         <xsl:call-template name="specialSubfieldSelect">
3843                                 <xsl:with-param name="axis">p</xsl:with-param>
3844                                 <xsl:with-param name="anyCodes">p</xsl:with-param>
3845                                 <xsl:with-param name="afterCodes">fghkdlmor</xsl:with-param>
3846                         </xsl:call-template>
3847                 </xsl:variable>
3848                 <xsl:if test="string-length(normalize-space($partNumber))">
3849                         <mads:partNumber>
3850                                 <xsl:call-template name="chopPunctuation">
3851                                         <xsl:with-param name="chopString" select="$partNumber"/>
3852                                 </xsl:call-template>
3853                         </mads:partNumber>
3854                 </xsl:if>
3855                 <xsl:if test="string-length(normalize-space($partName))">
3856                         <mads:partName>
3857                                 <xsl:call-template name="chopPunctuation">
3858                                         <xsl:with-param name="chopString" select="$partName"/>
3859                                 </xsl:call-template>
3860                         </mads:partName>
3861                 </xsl:if>
3862         </xsl:template>
3863
3864         <xsl:template name="nameABCDN">
3865                 <xsl:for-each select="marc:subfield[@code='a']">
3866                         <mads:namePart>
3867                                 <xsl:call-template name="chopPunctuation">
3868                                         <xsl:with-param name="chopString" select="."/>
3869                                 </xsl:call-template>
3870                         </mads:namePart>
3871                 </xsl:for-each>
3872                 <xsl:for-each select="marc:subfield[@code='b']">
3873                         <mads:namePart>
3874                                 <xsl:value-of select="."/>
3875                         </mads:namePart>
3876                 </xsl:for-each>
3877                 <xsl:if
3878                         test="marc:subfield[@code='c'] or marc:subfield[@code='d'] or marc:subfield[@code='n']">
3879                         <mads:namePart>
3880                                 <xsl:call-template name="subfieldSelect">
3881                                         <xsl:with-param name="codes">cdn</xsl:with-param>
3882                                 </xsl:call-template>
3883                         </mads:namePart>
3884                 </xsl:if>
3885         </xsl:template>
3886
3887         <xsl:template name="nameABCDQ">
3888                 <mads:namePart>
3889                         <xsl:call-template name="chopPunctuation">
3890                                 <xsl:with-param name="chopString">
3891                                         <xsl:call-template name="subfieldSelect">
3892                                                 <xsl:with-param name="codes">aq</xsl:with-param>
3893                                         </xsl:call-template>
3894                                 </xsl:with-param>
3895                         </xsl:call-template>
3896                 </mads:namePart>
3897                 <xsl:call-template name="termsOfAddress"/>
3898                 <xsl:call-template name="nameDate"/>
3899         </xsl:template>
3900
3901         <xsl:template name="nameACDENQ">
3902                 <mads:namePart>
3903                         <xsl:call-template name="subfieldSelect">
3904                                 <xsl:with-param name="codes">acdenq</xsl:with-param>
3905                         </xsl:call-template>
3906                 </mads:namePart>
3907         </xsl:template>
3908
3909         <xsl:template name="nameDate">
3910                 <xsl:for-each select="marc:subfield[@code='d']">
3911                         <mads:namePart type="date">
3912                                 <xsl:call-template name="chopPunctuation">
3913                                         <xsl:with-param name="chopString" select="."/>
3914                                 </xsl:call-template>
3915                         </mads:namePart>
3916                 </xsl:for-each>
3917         </xsl:template>
3918
3919         <xsl:template name="specialSubfieldSelect">
3920                 <xsl:param name="anyCodes"/>
3921                 <xsl:param name="axis"/>
3922                 <xsl:param name="beforeCodes"/>
3923                 <xsl:param name="afterCodes"/>
3924                 <xsl:variable name="str">
3925                         <xsl:for-each select="marc:subfield">
3926                                 <xsl:if
3927                                         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])">
3928                                         <xsl:value-of select="text()"/>
3929                                         <xsl:text> </xsl:text>
3930                                 </xsl:if>
3931                         </xsl:for-each>
3932                 </xsl:variable>
3933                 <xsl:value-of select="substring($str,1,string-length($str)-1)"/>
3934         </xsl:template>
3935
3936         <xsl:template name="termsOfAddress">
3937                 <xsl:if test="marc:subfield[@code='b' or @code='c']">
3938                         <mads:namePart type="termsOfAddress">
3939                                 <xsl:call-template name="chopPunctuation">
3940                                         <xsl:with-param name="chopString">
3941                                                 <xsl:call-template name="subfieldSelect">
3942                                                         <xsl:with-param name="codes">bc</xsl:with-param>
3943                                                 </xsl:call-template>
3944                                         </xsl:with-param>
3945                                 </xsl:call-template>
3946                         </mads:namePart>
3947                 </xsl:if>
3948         </xsl:template>
3949
3950         <xsl:template name="displayLabel">
3951                 <xsl:if test="marc:subfield[@code='z']">
3952                         <xsl:attribute name="displayLabel">
3953                                 <xsl:value-of select="marc:subfield[@code='z']"/>
3954                         </xsl:attribute>
3955                 </xsl:if>
3956                 <xsl:if test="marc:subfield[@code='3']">
3957                         <xsl:attribute name="displayLabel">
3958                                 <xsl:value-of select="marc:subfield[@code='3']"/>
3959                         </xsl:attribute>
3960                 </xsl:if>
3961         </xsl:template>
3962
3963         <xsl:template name="isInvalid">
3964                 <xsl:if test="@code='z'">
3965                         <xsl:attribute name="invalid">yes</xsl:attribute>
3966                 </xsl:if>
3967         </xsl:template>
3968
3969         <xsl:template name="sub2Attribute">
3970                 <!-- 024 -->
3971                 <xsl:if test="../marc:subfield[@code='2']">
3972                         <xsl:attribute name="type">
3973                                 <xsl:value-of select="../marc:subfield[@code='2']"/>
3974                         </xsl:attribute>
3975                 </xsl:if>
3976         </xsl:template>
3977
3978         <xsl:template match="marc:controlfield[@tag=001]">
3979                 <mads:recordIdentifier>
3980                         <xsl:if test="../marc:controlfield[@tag=003]">
3981                                 <xsl:attribute name="source">
3982                                         <xsl:value-of select="../marc:controlfield[@tag=003]"/>
3983                                 </xsl:attribute>
3984                         </xsl:if>
3985                         <xsl:value-of select="."/>
3986                 </mads:recordIdentifier>
3987         </xsl:template>
3988
3989         <xsl:template match="marc:controlfield[@tag=005]">
3990                 <mads:recordChangeDate encoding="iso8601">
3991                         <xsl:value-of select="."/>
3992                 </mads:recordChangeDate>
3993         </xsl:template>
3994
3995         <xsl:template match="marc:controlfield[@tag=008]">
3996                 <mads:recordCreationDate encoding="marc">
3997                         <xsl:value-of select="substring(.,1,6)"/>
3998                 </mads:recordCreationDate>
3999         </xsl:template>
4000
4001         <xsl:template match="marc:datafield[@tag=010]">
4002                 <xsl:for-each select="marc:subfield">
4003                         <mads:identifier type="lccn">
4004                                 <xsl:call-template name="isInvalid"/>
4005                                 <xsl:value-of select="."/>
4006                         </mads:identifier>
4007                 </xsl:for-each>
4008         </xsl:template>
4009
4010         <xsl:template match="marc:datafield[@tag=024]">
4011                 <xsl:for-each select="marc:subfield[not(@code=2)]">
4012                         <mads:identifier>
4013                                 <xsl:call-template name="isInvalid"/>
4014                                 <xsl:call-template name="sub2Attribute"/>
4015                                 <xsl:value-of select="."/>
4016                         </mads:identifier>
4017                 </xsl:for-each>
4018         </xsl:template>
4019
4020         <!-- ========== 372 ========== -->
4021         <xsl:template match="marc:datafield[@tag=372]">
4022                 <mads:fieldOfActivity>
4023                         <xsl:call-template name="subfieldSelect">
4024                                 <xsl:with-param name="codes">a</xsl:with-param>
4025                         </xsl:call-template>
4026                         <xsl:text>-</xsl:text>
4027                         <xsl:call-template name="subfieldSelect">
4028                                 <xsl:with-param name="codes">st</xsl:with-param>
4029                         </xsl:call-template>
4030                 </mads:fieldOfActivity>
4031         </xsl:template>
4032
4033
4034         <!-- ========== 040 ========== -->
4035         <xsl:template match="marc:datafield[@tag=040]/marc:subfield[@code='a']">
4036                 <mads:recordContentSource authority="marcorg">
4037                         <xsl:value-of select="."/>
4038                 </mads:recordContentSource>
4039         </xsl:template>
4040
4041         <xsl:template match="marc:datafield[@tag=040]/marc:subfield[@code='b']">
4042                 <mads:languageOfCataloging>
4043                         <mads:languageTerm authority="iso639-2b" type="code">
4044                                 <xsl:value-of select="."/>
4045                         </mads:languageTerm>
4046                 </mads:languageOfCataloging>
4047         </xsl:template>
4048
4049         <xsl:template match="marc:datafield[@tag=040]/marc:subfield[@code='e']">
4050                 <mads:descriptionStandard>
4051                         <xsl:value-of select="."/>
4052                 </mads:descriptionStandard>
4053         </xsl:template>
4054         
4055         <!-- ========== classification 2.03 ========== -->
4056         
4057         <xsl:template match="marc:datafield[@tag=053]">
4058                 <mads:classification>
4059                         <xsl:call-template name="subfieldSelect">
4060                                 <xsl:with-param name="codes">abcdxyz</xsl:with-param>
4061                                 <xsl:with-param name="delimeter">-</xsl:with-param>
4062                         </xsl:call-template>
4063                 </mads:classification>
4064         </xsl:template>
4065         
4066         <xsl:template match="marc:datafield[@tag=055]">
4067                 <mads:classification>
4068                         <xsl:call-template name="subfieldSelect">
4069                                 <xsl:with-param name="codes">abcdxyz</xsl:with-param>
4070                                 <xsl:with-param name="delimeter">-</xsl:with-param>
4071                         </xsl:call-template>
4072                 </mads:classification>
4073         </xsl:template>
4074         
4075         <xsl:template match="marc:datafield[@tag=060]">
4076                 <mads:classification>
4077                         <xsl:call-template name="subfieldSelect">
4078                                 <xsl:with-param name="codes">abcdxyz</xsl:with-param>
4079                                 <xsl:with-param name="delimeter">-</xsl:with-param>
4080                         </xsl:call-template>
4081                 </mads:classification>
4082         </xsl:template>
4083         <xsl:template match="marc:datafield[@tag=065]">
4084                 <mads:classification>
4085                         <xsl:attribute name="authority">
4086                                 <xsl:value-of select="marc:subfield[@code='2']"/>
4087                         </xsl:attribute>
4088                         <xsl:call-template name="subfieldSelect">
4089                                 <xsl:with-param name="codes">abcdxyz</xsl:with-param>
4090                                 <xsl:with-param name="delimeter">-</xsl:with-param>
4091                         </xsl:call-template>
4092                 </mads:classification>
4093         </xsl:template>
4094         <xsl:template match="marc:datafield[@tag=070]">
4095                 <mads:classification>
4096                         <xsl:call-template name="subfieldSelect">
4097                                 <xsl:with-param name="codes">abcdxyz5</xsl:with-param>
4098                                 <xsl:with-param name="delimeter">-</xsl:with-param>
4099                         </xsl:call-template>
4100                 </mads:classification>
4101         </xsl:template>
4102         <xsl:template match="marc:datafield[@tag=080]">
4103                 <mads:classification>
4104                         <xsl:attribute name="authority">
4105                                 <xsl:value-of select="marc:subfield[@code='2']"/>
4106                         </xsl:attribute>
4107                         <xsl:call-template name="subfieldSelect">
4108                                 <xsl:with-param name="codes">abcdxyz5</xsl:with-param>
4109                                 <xsl:with-param name="delimeter">-</xsl:with-param>
4110                         </xsl:call-template>
4111                 </mads:classification>
4112         </xsl:template>
4113         <xsl:template match="marc:datafield[@tag=082]">
4114                 <mads:classification>
4115                         <xsl:attribute name="authority">
4116                                 <xsl:value-of select="marc:subfield[@code='2']"/>
4117                         </xsl:attribute>
4118                         <xsl:call-template name="subfieldSelect">
4119                                 <xsl:with-param name="codes">abcdxyz5</xsl:with-param>
4120                                 <xsl:with-param name="delimeter">-</xsl:with-param>
4121                         </xsl:call-template>
4122                 </mads:classification>
4123         </xsl:template>
4124         <xsl:template match="marc:datafield[@tag=083]">
4125                 <mads:classification>
4126                         <xsl:attribute name="authority">
4127                                 <xsl:value-of select="marc:subfield[@code='2']"/>
4128                         </xsl:attribute>
4129                         <xsl:call-template name="subfieldSelect">
4130                                 <xsl:with-param name="codes">abcdxyz5</xsl:with-param>
4131                                 <xsl:with-param name="delimeter">-</xsl:with-param>
4132                         </xsl:call-template>
4133                 </mads:classification>
4134         </xsl:template>
4135         <xsl:template match="marc:datafield[@tag=086]">
4136                 <mads:classification>
4137                         <xsl:attribute name="authority">
4138                                 <xsl:value-of select="marc:subfield[@code='2']"/>
4139                         </xsl:attribute>
4140                         <xsl:call-template name="subfieldSelect">
4141                                 <xsl:with-param name="codes">abcdxyz5</xsl:with-param>
4142                                 <xsl:with-param name="delimeter">-</xsl:with-param>
4143                         </xsl:call-template>
4144                 </mads:classification>
4145         </xsl:template>
4146         <xsl:template match="marc:datafield[@tag=087]">
4147                 <mads:classification>
4148                         <xsl:attribute name="authority">
4149                                 <xsl:value-of select="marc:subfield[@code='2']"/>
4150                         </xsl:attribute>
4151                         <xsl:call-template name="subfieldSelect">
4152                                 <xsl:with-param name="codes">abcdxyz5</xsl:with-param>
4153                                 <xsl:with-param name="delimeter">-</xsl:with-param>
4154                         </xsl:call-template>
4155                 </mads:classification>
4156         </xsl:template>
4157         
4158
4159         <!-- ========== names  ========== -->
4160         <xsl:template match="marc:datafield[@tag=100]">
4161                 <mads:name type="personal">
4162                         <xsl:call-template name="setAuthority"/>
4163                         <xsl:call-template name="nameABCDQ"/>
4164                 </mads:name>
4165                 <xsl:apply-templates select="*[marc:subfield[not(contains('abcdeq',@code))]]"/>
4166                 <xsl:call-template name="title"/>
4167                 <xsl:apply-templates select="marc:subfield[@code!='i']"/>
4168         </xsl:template>
4169
4170         <xsl:template match="marc:datafield[@tag=110]">
4171                 <mads:name type="corporate">
4172                         <xsl:call-template name="setAuthority"/>
4173                         <xsl:call-template name="nameABCDN"/>
4174                 </mads:name>
4175                 <xsl:apply-templates select="marc:subfield[@code!='i']"/>
4176         </xsl:template>
4177
4178         <xsl:template match="marc:datafield[@tag=111]">
4179                 <mads:name type="conference">
4180                         <xsl:call-template name="setAuthority"/>
4181                         <xsl:call-template name="nameACDENQ"/>
4182                 </mads:name>
4183                 <xsl:apply-templates select="marc:subfield[@code!='i']"/>
4184         </xsl:template>
4185
4186         <xsl:template match="marc:datafield[@tag=400]">
4187                 <mads:variant>
4188                         <xsl:call-template name="variantTypeAttribute"/>
4189                         <mads:name type="personal">
4190                                 <xsl:call-template name="nameABCDQ"/>
4191                         </mads:name>
4192                         <xsl:apply-templates select="marc:subfield[@code!='i']"/>
4193                         <xsl:call-template name="title"/>
4194                 </mads:variant>
4195         </xsl:template>
4196
4197         <xsl:template match="marc:datafield[@tag=410]">
4198                 <mads:variant>
4199                         <xsl:call-template name="variantTypeAttribute"/>
4200                         <mads:name type="corporate">
4201                                 <xsl:call-template name="nameABCDN"/>
4202                         </mads:name>
4203                         <xsl:apply-templates select="marc:subfield[@code!='i']"/>
4204                 </mads:variant>
4205         </xsl:template>
4206
4207         <xsl:template match="marc:datafield[@tag=411]">
4208                 <mads:variant>
4209                         <xsl:call-template name="variantTypeAttribute"/>
4210                         <mads:name type="conference">
4211                                 <xsl:call-template name="nameACDENQ"/>
4212                         </mads:name>
4213                         <xsl:apply-templates select="marc:subfield[@code!='i']"/>
4214                 </mads:variant>
4215         </xsl:template>
4216
4217         <xsl:template match="marc:datafield[@tag=500]|marc:datafield[@tag=700]">
4218                 <mads:related>
4219                         <xsl:call-template name="relatedTypeAttribute"/>
4220                         <!-- <xsl:call-template name="uri"/> -->
4221                         <mads:name type="personal">
4222                                 <xsl:call-template name="setAuthority"/>
4223                                 <xsl:call-template name="nameABCDQ"/>
4224                         </mads:name>
4225                         <xsl:call-template name="title"/>
4226                         <xsl:apply-templates select="marc:subfield[@code!='i']"/>
4227                 </mads:related>
4228         </xsl:template>
4229
4230         <xsl:template match="marc:datafield[@tag=510]|marc:datafield[@tag=710]">
4231                 <mads:related>
4232                         <xsl:call-template name="relatedTypeAttribute"/>
4233                         <!-- <xsl:call-template name="uri"/> -->
4234                         <mads:name type="corporate">
4235                                 <xsl:call-template name="setAuthority"/>
4236                                 <xsl:call-template name="nameABCDN"/>
4237                         </mads:name>
4238                         <xsl:apply-templates select="marc:subfield[@code!='i']"/>
4239                 </mads:related>
4240         </xsl:template>
4241
4242         <xsl:template match="marc:datafield[@tag=511]|marc:datafield[@tag=711]">
4243                 <mads:related>
4244                         <xsl:call-template name="relatedTypeAttribute"/>
4245                         <!-- <xsl:call-template name="uri"/> -->
4246                         <mads:name type="conference">
4247                                 <xsl:call-template name="setAuthority"/>
4248                                 <xsl:call-template name="nameACDENQ"/>
4249                         </mads:name>
4250                         <xsl:apply-templates select="marc:subfield[@code!='i']"/>
4251                 </mads:related>
4252         </xsl:template>
4253
4254         <!-- ========== titles  ========== -->
4255         <xsl:template match="marc:datafield[@tag=130]">
4256                 <xsl:call-template name="uniform-title"/>
4257                 <xsl:apply-templates select="marc:subfield[@code!='i']"/>
4258         </xsl:template>
4259
4260         <xsl:template match="marc:datafield[@tag=430]">
4261                 <mads:variant>
4262                         <xsl:call-template name="variantTypeAttribute"/>
4263                         <xsl:call-template name="uniform-title"/>
4264                         <xsl:apply-templates select="marc:subfield[@code!='i']"/>
4265                 </mads:variant>
4266         </xsl:template>
4267
4268         <xsl:template match="marc:datafield[@tag=530]|marc:datafield[@tag=730]">
4269                 <mads:related>
4270                         <xsl:call-template name="relatedTypeAttribute"/>
4271                         <xsl:call-template name="uniform-title"/>
4272                         <xsl:apply-templates select="marc:subfield[@code!='i']"/>
4273                 </mads:related>
4274         </xsl:template>
4275
4276         <xsl:template name="title">
4277                 <xsl:variable name="hasTitle">
4278                         <xsl:for-each select="marc:subfield">
4279                                 <xsl:if test="(contains('tfghklmors',@code) )">
4280                                         <xsl:value-of select="@code"/>
4281                                 </xsl:if>
4282                         </xsl:for-each>
4283                 </xsl:variable>
4284                 <xsl:if test="string-length($hasTitle) &gt; 0 ">
4285                         <mads:titleInfo>
4286                                 <xsl:call-template name="setAuthority"/>
4287                                 <mads:title>
4288                                         <xsl:variable name="str">
4289                                                 <xsl:for-each select="marc:subfield">
4290                                                         <xsl:if test="(contains('atfghklmors',@code) )">
4291                                                                 <xsl:value-of select="text()"/>
4292                                                                 <xsl:text> </xsl:text>
4293                                                         </xsl:if>
4294                                                 </xsl:for-each>
4295                                         </xsl:variable>
4296                                         <xsl:call-template name="chopPunctuation">
4297                                                 <xsl:with-param name="chopString">
4298                                                         <xsl:value-of select="substring($str,1,string-length($str)-1)"/>
4299                                                 </xsl:with-param>
4300                                         </xsl:call-template>
4301                                 </mads:title>
4302                                 <xsl:call-template name="part"/>
4303                                 <!-- <xsl:call-template name="uri"/> -->
4304                         </mads:titleInfo>
4305                 </xsl:if>
4306         </xsl:template>
4307
4308         <xsl:template name="uniform-title">
4309                 <xsl:variable name="hasTitle">
4310                         <xsl:for-each select="marc:subfield">
4311                                 <xsl:if test="(contains('atfghklmors',@code) )">
4312                                         <xsl:value-of select="@code"/>
4313                                 </xsl:if>
4314                         </xsl:for-each>
4315                 </xsl:variable>
4316                 <xsl:if test="string-length($hasTitle) &gt; 0 ">
4317                         <mads:titleInfo>
4318                                 <xsl:call-template name="setAuthority"/>
4319                                 <mads:title>
4320                                         <xsl:variable name="str">
4321                                                 <xsl:for-each select="marc:subfield">
4322                                                         <xsl:if test="(contains('adfghklmors',@code) )">
4323                                                                 <xsl:value-of select="text()"/>
4324                                                                 <xsl:text> </xsl:text>
4325                                                         </xsl:if>
4326                                                 </xsl:for-each>
4327                                         </xsl:variable>
4328                                         <xsl:call-template name="chopPunctuation">
4329                                                 <xsl:with-param name="chopString">
4330                                                         <xsl:value-of select="substring($str,1,string-length($str)-1)"/>
4331                                                 </xsl:with-param>
4332                                         </xsl:call-template>
4333                                 </mads:title>
4334                                 <xsl:call-template name="part"/>
4335                                 <!-- <xsl:call-template name="uri"/> -->
4336                         </mads:titleInfo>
4337                 </xsl:if>
4338         </xsl:template>
4339
4340
4341         <!-- ========== topics  ========== -->
4342         <xsl:template match="marc:subfield[@code='x']">
4343                 <mads:topic>
4344                         <xsl:call-template name="chopPunctuation">
4345                                 <xsl:with-param name="chopString">
4346                                         <xsl:value-of select="."/>
4347                                 </xsl:with-param>
4348                         </xsl:call-template>
4349                 </mads:topic>
4350         </xsl:template>
4351         
4352         <!-- 2.06 fix -->
4353         <xsl:template
4354                 match="marc:datafield[@tag=150][marc:subfield[@code='a' or @code='b']]|marc:datafield[@tag=180][marc:subfield[@code='x']]">
4355                 <xsl:call-template name="topic"/>
4356                 <xsl:apply-templates select="marc:subfield[@code!='i']"/>
4357         </xsl:template>
4358         <xsl:template
4359                 match="marc:datafield[@tag=450][marc:subfield[@code='a' or @code='b']]|marc:datafield[@tag=480][marc:subfield[@code='x']]">
4360                 <mads:variant>
4361                         <xsl:call-template name="variantTypeAttribute"/>
4362                         <xsl:call-template name="topic"/>
4363                 </mads:variant>
4364         </xsl:template>
4365         <xsl:template
4366                 match="marc:datafield[@tag=550 or @tag=750][marc:subfield[@code='a' or @code='b']]">
4367                 <mads:related>
4368                         <xsl:call-template name="relatedTypeAttribute"/>
4369                         <!-- <xsl:call-template name="uri"/> -->
4370                         <xsl:call-template name="topic"/>
4371                         <xsl:apply-templates select="marc:subfield[@code='z']"/>
4372                 </mads:related>
4373         </xsl:template>
4374         <xsl:template name="topic">
4375                 <mads:topic>
4376                         <xsl:call-template name="setAuthority"/>
4377                         <!-- tmee2006 dedupe 550a
4378                         <xsl:if test="@tag=550 or @tag=750">
4379                                 <xsl:call-template name="subfieldSelect">
4380                                         <xsl:with-param name="codes">ab</xsl:with-param>
4381                                 </xsl:call-template>
4382                         </xsl:if>       
4383                         -->
4384                         <xsl:choose>
4385                                 <xsl:when test="@tag=180 or @tag=480 or @tag=580 or @tag=780">
4386                                         <xsl:call-template name="chopPunctuation">
4387                                                 <xsl:with-param name="chopString">
4388                                                         <xsl:apply-templates select="marc:subfield[@code='x']"/>
4389                                                 </xsl:with-param>
4390                                         </xsl:call-template>
4391                                 </xsl:when>
4392                         </xsl:choose>
4393                         <xsl:call-template name="chopPunctuation">
4394                                 <xsl:with-param name="chopString">
4395                                         <xsl:choose>
4396                                                 <xsl:when test="@tag=180 or @tag=480 or @tag=580 or @tag=780">
4397                                                         <xsl:apply-templates select="marc:subfield[@code='x']"/>
4398                                                 </xsl:when>
4399                                                 <xsl:otherwise>
4400                                                         <xsl:call-template name="subfieldSelect">
4401                                                                 <xsl:with-param name="codes">ab</xsl:with-param>
4402                                                         </xsl:call-template>
4403                                                 </xsl:otherwise>
4404                                         </xsl:choose>
4405                                 </xsl:with-param>
4406                         </xsl:call-template>
4407                 </mads:topic>
4408         </xsl:template>
4409
4410         <!-- ========= temporals  ========== -->
4411         <xsl:template match="marc:subfield[@code='y']">
4412                 <mads:temporal>
4413                         <xsl:call-template name="chopPunctuation">
4414                                 <xsl:with-param name="chopString">
4415                                         <xsl:value-of select="."/>
4416                                 </xsl:with-param>
4417                         </xsl:call-template>
4418                 </mads:temporal>
4419         </xsl:template>
4420         <xsl:template
4421                 match="marc:datafield[@tag=148][marc:subfield[@code='a']]|marc:datafield[@tag=182 ][marc:subfield[@code='y']]">
4422                 <xsl:call-template name="temporal"/>
4423         </xsl:template>
4424         <xsl:template
4425                 match="marc:datafield[@tag=448][marc:subfield[@code='a']]|marc:datafield[@tag=482][marc:subfield[@code='y']]">
4426                 <mads:variant>
4427                         <xsl:call-template name="variantTypeAttribute"/>
4428                         <xsl:call-template name="temporal"/>
4429                 </mads:variant>
4430         </xsl:template>
4431         <xsl:template
4432                 match="marc:datafield[@tag=548 or @tag=748][marc:subfield[@code='a']]|marc:datafield[@tag=582 or @tag=782][marc:subfield[@code='y']]">
4433                 <mads:related>
4434                         <xsl:call-template name="relatedTypeAttribute"/>
4435                         <!-- <xsl:call-template name="uri"/> -->
4436                         <xsl:call-template name="temporal"/>
4437                 </mads:related>
4438         </xsl:template>
4439         <xsl:template name="temporal">
4440                 <mads:temporal>
4441                         <xsl:call-template name="setAuthority"/>
4442                         <xsl:if test="@tag=548 or @tag=748">
4443                                 <xsl:value-of select="marc:subfield[@code='a']"/>
4444                         </xsl:if>
4445                         <xsl:call-template name="chopPunctuation">
4446                                 <xsl:with-param name="chopString">
4447                                         <xsl:choose>
4448                                                 <xsl:when test="@tag=182 or @tag=482 or @tag=582 or @tag=782">
4449                                                         <xsl:apply-templates select="marc:subfield[@code='y']"/>
4450                                                 </xsl:when>
4451                                                 <xsl:otherwise>
4452                                                         <xsl:value-of select="marc:subfield[@code='a']"/>
4453                                                 </xsl:otherwise>
4454                                         </xsl:choose>
4455                                 </xsl:with-param>
4456                         </xsl:call-template>
4457                 </mads:temporal>
4458                 <xsl:apply-templates select="marc:subfield[@code!='i']"/>
4459         </xsl:template>
4460
4461         <!-- ========== genre  ========== -->
4462         <xsl:template match="marc:subfield[@code='v']">
4463                 <mads:genre>
4464                         <xsl:call-template name="chopPunctuation">
4465                                 <xsl:with-param name="chopString">
4466                                         <xsl:value-of select="."/>
4467                                 </xsl:with-param>
4468                         </xsl:call-template>
4469                 </mads:genre>
4470         </xsl:template>
4471         <xsl:template
4472                 match="marc:datafield[@tag=155][marc:subfield[@code='a']]|marc:datafield[@tag=185][marc:subfield[@code='v']]">
4473                 <xsl:call-template name="genre"/>
4474         </xsl:template>
4475         <xsl:template
4476                 match="marc:datafield[@tag=455][marc:subfield[@code='a']]|marc:datafield[@tag=485 ][marc:subfield[@code='v']]">
4477                 <mads:variant>
4478                         <xsl:call-template name="variantTypeAttribute"/>
4479                         <xsl:call-template name="genre"/>
4480                 </mads:variant>
4481         </xsl:template>
4482         <!--
4483         <xsl:template match="marc:datafield[@tag=555]">
4484                 <mads:related>
4485                         <xsl:call-template name="relatedTypeAttribute"/>
4486                         <xsl:call-template name="uri"/>
4487                         <xsl:call-template name="genre"/>
4488                 </mads:related>
4489         </xsl:template>
4490         -->
4491         <xsl:template
4492                 match="marc:datafield[@tag=555 or @tag=755][marc:subfield[@code='a']]|marc:datafield[@tag=585][marc:subfield[@code='v']]">
4493                 <mads:related>
4494                         <xsl:call-template name="relatedTypeAttribute"/>
4495                         <xsl:call-template name="genre"/>
4496                 </mads:related>
4497         </xsl:template>
4498         <xsl:template name="genre">
4499                 <mads:genre>
4500                         <xsl:if test="@tag=555">
4501                                 <xsl:value-of select="marc:subfield[@code='a']"/>
4502                         </xsl:if>
4503                         <xsl:call-template name="setAuthority"/>
4504                         <xsl:call-template name="chopPunctuation">
4505                                 <xsl:with-param name="chopString">
4506                                         <xsl:choose>
4507                                                 <!-- 2.07 fix -->
4508                                                 <xsl:when test="@tag='555'"/>
4509                                                 <xsl:when test="@tag=185 or @tag=485 or @tag=585">
4510                                                         <xsl:apply-templates select="marc:subfield[@code='v']"/>
4511                                                 </xsl:when>
4512                                                 <xsl:otherwise>
4513                                                         <xsl:value-of select="marc:subfield[@code='a']"/>
4514                                                 </xsl:otherwise>
4515                                         </xsl:choose>
4516                                 </xsl:with-param>
4517                         </xsl:call-template>
4518                 </mads:genre>
4519                 <xsl:apply-templates/>
4520         </xsl:template>
4521
4522         <!-- ========= geographic  ========== -->
4523         <xsl:template match="marc:subfield[@code='z']">
4524                 <mads:geographic>
4525                         <xsl:call-template name="chopPunctuation">
4526                                 <xsl:with-param name="chopString">
4527                                         <xsl:value-of select="."/>
4528                                 </xsl:with-param>
4529                         </xsl:call-template>
4530                 </mads:geographic>
4531         </xsl:template>
4532         <xsl:template name="geographic">
4533                 <mads:geographic>
4534                         <!-- 2.14 -->
4535                         <xsl:call-template name="setAuthority"/>
4536                         <!-- 2.13 -->
4537                         <xsl:if test="@tag=151 or @tag=551">
4538                                 <xsl:value-of select="marc:subfield[@code='a']"/>
4539                         </xsl:if>
4540                         <xsl:call-template name="chopPunctuation">
4541                                 <xsl:with-param name="chopString">
4542                                                 <xsl:if test="@tag=181 or @tag=481 or @tag=581">
4543                                                                 <xsl:apply-templates select="marc:subfield[@code='z']"/>
4544                                                 </xsl:if>
4545                                                 <!-- 2.13
4546                                                         <xsl:choose>
4547                                                 <xsl:when test="@tag=181 or @tag=481 or @tag=581">
4548                                                         <xsl:apply-templates select="marc:subfield[@code='z']"/>
4549                                                 </xsl:when>
4550                                         
4551                                                 <xsl:otherwise>
4552                                                         <xsl:value-of select="marc:subfield[@code='a']"/>
4553                                                 </xsl:otherwise>
4554                                                 </xsl:choose>
4555                                                 -->
4556                                 </xsl:with-param>
4557                         </xsl:call-template>
4558                 </mads:geographic>
4559                 <xsl:apply-templates select="marc:subfield[@code!='i']"/>
4560         </xsl:template>
4561         <xsl:template
4562                 match="marc:datafield[@tag=151][marc:subfield[@code='a']]|marc:datafield[@tag=181][marc:subfield[@code='z']]">
4563                 <xsl:call-template name="geographic"/>
4564         </xsl:template>
4565         <xsl:template
4566                 match="marc:datafield[@tag=451][marc:subfield[@code='a']]|marc:datafield[@tag=481][marc:subfield[@code='z']]">
4567                 <mads:variant>
4568                         <xsl:call-template name="variantTypeAttribute"/>
4569                         <xsl:call-template name="geographic"/>
4570                 </mads:variant>
4571         </xsl:template>
4572         <xsl:template
4573                 match="marc:datafield[@tag=551]|marc:datafield[@tag=581][marc:subfield[@code='z']]">
4574                 <mads:related>
4575                         <xsl:call-template name="relatedTypeAttribute"/>
4576                         <!-- <xsl:call-template name="uri"/> -->
4577                         <xsl:call-template name="geographic"/>
4578                 </mads:related>
4579         </xsl:template>
4580         <xsl:template match="marc:datafield[@tag=580]">
4581                 <mads:related>
4582                         <xsl:call-template name="relatedTypeAttribute"/>
4583                         <xsl:apply-templates select="marc:subfield[@code!='i']"/>
4584                 </mads:related>
4585         </xsl:template>
4586         <xsl:template
4587                 match="marc:datafield[@tag=751][marc:subfield[@code='z']]|marc:datafield[@tag=781][marc:subfield[@code='z']]">
4588                 <mads:related>
4589                         <xsl:call-template name="relatedTypeAttribute"/>
4590                         <xsl:call-template name="geographic"/>
4591                 </mads:related>
4592         </xsl:template>
4593         <xsl:template match="marc:datafield[@tag=755]">
4594                 <mads:related>
4595                         <xsl:call-template name="relatedTypeAttribute"/>
4596                         <xsl:call-template name="genre"/>
4597                         <xsl:call-template name="setAuthority"/>
4598                         <xsl:apply-templates select="marc:subfield[@code!='i']"/>
4599                 </mads:related>
4600         </xsl:template>
4601         <xsl:template match="marc:datafield[@tag=780]">
4602                 <mads:related>
4603                         <xsl:call-template name="relatedTypeAttribute"/>
4604                         <xsl:apply-templates select="marc:subfield[@code!='i']"/>
4605                 </mads:related>
4606         </xsl:template>
4607         <xsl:template match="marc:datafield[@tag=785]">
4608                 <mads:related>
4609                         <xsl:call-template name="relatedTypeAttribute"/>
4610                         <xsl:apply-templates select="marc:subfield[@code!='i']"/>
4611                 </mads:related>
4612         </xsl:template>
4613
4614         <!-- ========== notes  ========== -->
4615         <xsl:template match="marc:datafield[667 &lt;= @tag and @tag &lt;= 688]">
4616                 <mads:note>
4617                         <xsl:choose>
4618                                 <xsl:when test="@tag=667">
4619                                         <xsl:attribute name="type">nonpublic</xsl:attribute>
4620                                 </xsl:when>
4621                                 <xsl:when test="@tag=670">
4622                                         <xsl:attribute name="type">source</xsl:attribute>
4623                                 </xsl:when>
4624                                 <xsl:when test="@tag=675">
4625                                         <xsl:attribute name="type">notFound</xsl:attribute>
4626                                 </xsl:when>
4627                                 <xsl:when test="@tag=678">
4628                                         <xsl:attribute name="type">history</xsl:attribute>
4629                                 </xsl:when>
4630                                 <xsl:when test="@tag=681">
4631                                         <xsl:attribute name="type">subject example</xsl:attribute>
4632                                 </xsl:when>
4633                                 <xsl:when test="@tag=682">
4634                                         <xsl:attribute name="type">deleted heading information</xsl:attribute>
4635                                 </xsl:when>
4636                                 <xsl:when test="@tag=688">
4637                                         <xsl:attribute name="type">application history</xsl:attribute>
4638                                 </xsl:when>
4639                         </xsl:choose>
4640                         <xsl:call-template name="chopPunctuation">
4641                                 <xsl:with-param name="chopString">
4642                                         <xsl:choose>
4643                                                 <xsl:when test="@tag=667 or @tag=675">
4644                                                         <xsl:value-of select="marc:subfield[@code='a']"/>
4645                                                 </xsl:when>
4646                                                 <xsl:when test="@tag=670 or @tag=678">
4647                                                         <xsl:call-template name="subfieldSelect">
4648                                                                 <xsl:with-param name="codes">ab</xsl:with-param>
4649                                                         </xsl:call-template>
4650                                                 </xsl:when>
4651                                                 <xsl:when test="680 &lt;= @tag and @tag &lt;=688">
4652                                                         <xsl:call-template name="subfieldSelect">
4653                                                                 <xsl:with-param name="codes">ai</xsl:with-param>
4654                                                         </xsl:call-template>
4655                                                 </xsl:when>
4656                                         </xsl:choose>
4657                                 </xsl:with-param>
4658                         </xsl:call-template>
4659                 </mads:note>
4660         </xsl:template>
4661
4662         <!-- ========== url  ========== -->
4663         <xsl:template match="marc:datafield[@tag=856][marc:subfield[@code='u']]">
4664                 <mads:url>
4665                         <xsl:if test="marc:subfield[@code='z' or @code='3']">
4666                                 <xsl:attribute name="displayLabel">
4667                                         <xsl:call-template name="subfieldSelect">
4668                                                 <xsl:with-param name="codes">z3</xsl:with-param>
4669                                         </xsl:call-template>
4670                                 </xsl:attribute>
4671                         </xsl:if>
4672                         <xsl:value-of select="marc:subfield[@code='u']"/>
4673                 </mads:url>
4674         </xsl:template>
4675
4676         <xsl:template name="relatedTypeAttribute">
4677                 <xsl:choose>
4678                         <xsl:when
4679                                 test="@tag=500 or @tag=510 or @tag=511 or @tag=548 or @tag=550 or @tag=551 or @tag=555 or @tag=580 or @tag=581 or @tag=582 or @tag=585">
4680                                 <xsl:if test="substring(marc:subfield[@code='w'],1,1)='a'">
4681                                         <xsl:attribute name="type">earlier</xsl:attribute>
4682                                 </xsl:if>
4683                                 <xsl:if test="substring(marc:subfield[@code='w'],1,1)='b'">
4684                                         <xsl:attribute name="type">later</xsl:attribute>
4685                                 </xsl:if>
4686                                 <xsl:if test="substring(marc:subfield[@code='w'],1,1)='t'">
4687                                         <xsl:attribute name="type">parentOrg</xsl:attribute>
4688                                 </xsl:if>
4689                                 <xsl:if test="substring(marc:subfield[@code='w'],1,1)='g'">
4690                                         <xsl:attribute name="type">broader</xsl:attribute>
4691                                 </xsl:if>
4692                                 <xsl:if test="substring(marc:subfield[@code='w'],1,1)='h'">
4693                                         <xsl:attribute name="type">narrower</xsl:attribute>
4694                                 </xsl:if>
4695                                 <xsl:if test="substring(marc:subfield[@code='w'],1,1)='r'">
4696                                         <xsl:attribute name="type">other</xsl:attribute>
4697                                 </xsl:if>
4698                                 <xsl:if test="contains('fin|', substring(marc:subfield[@code='w'],1,1))">
4699                                         <xsl:attribute name="type">other</xsl:attribute>
4700                                 </xsl:if>
4701                         </xsl:when>
4702                         <xsl:when test="@tag=530 or @tag=730">
4703                                 <xsl:attribute name="type">other</xsl:attribute>
4704                         </xsl:when>
4705                         <xsl:otherwise>
4706                                 <!-- 7xx -->
4707                                 <xsl:attribute name="type">equivalent</xsl:attribute>
4708                         </xsl:otherwise>
4709                 </xsl:choose>
4710                 <xsl:apply-templates select="marc:subfield[@code='i']"/>
4711         </xsl:template>
4712         
4713
4714
4715         <xsl:template name="variantTypeAttribute">
4716                 <xsl:choose>
4717                         <xsl:when
4718                                 test="@tag=400 or @tag=410 or @tag=411 or @tag=451 or @tag=455 or @tag=480 or @tag=481 or @tag=482 or @tag=485">
4719                                 <xsl:if test="substring(marc:subfield[@code='w'],1,1)='d'">
4720                                         <xsl:attribute name="type">acronym</xsl:attribute>
4721                                 </xsl:if>
4722                                 <xsl:if test="substring(marc:subfield[@code='w'],1,1)='n'">
4723                                         <xsl:attribute name="type">other</xsl:attribute>
4724                                 </xsl:if>
4725                                 <xsl:if test="contains('fit', substring(marc:subfield[@code='w'],1,1))">
4726                                         <xsl:attribute name="type">other</xsl:attribute>
4727                                 </xsl:if>
4728                         </xsl:when>
4729                         <xsl:otherwise>
4730                                 <!-- 430  -->
4731                                 <xsl:attribute name="type">other</xsl:attribute>
4732                         </xsl:otherwise>
4733                 </xsl:choose>
4734                 <xsl:apply-templates select="marc:subfield[@code='i']"/>
4735         </xsl:template>
4736
4737         <xsl:template name="setAuthority">
4738                 <xsl:choose>
4739                         <!-- can be called from the datafield or subfield level, so "..//@tag" means
4740                         the tag can be at the subfield's parent level or at the datafields own level -->
4741
4742                         <xsl:when
4743                                 test="ancestor-or-self::marc:datafield/@tag=100 and (@ind1=0 or @ind1=1) and $controlField008-11='a' and $controlField008-14='a'">
4744                                 <xsl:attribute name="authority">
4745                                         <xsl:text>naf</xsl:text>
4746                                 </xsl:attribute>
4747                         </xsl:when>
4748                         <xsl:when
4749                                 test="ancestor-or-self::marc:datafield/@tag=100 and (@ind1=0 or @ind1=1) and $controlField008-11='a' and $controlField008-14='b'">
4750                                 <xsl:attribute name="authority">
4751                                         <xsl:text>lcsh</xsl:text>
4752                                 </xsl:attribute>
4753                         </xsl:when>
4754                         <xsl:when
4755                                 test="ancestor-or-self::marc:datafield/@tag=100 and (@ind1=0 or @ind1=1) and $controlField008-11='k'">
4756                                 <xsl:attribute name="authority">
4757                                         <xsl:text>lacnaf</xsl:text>
4758                                 </xsl:attribute>
4759                         </xsl:when>
4760                         <xsl:when
4761                                 test="ancestor-or-self::marc:datafield/@tag=100 and @ind1=3 and $controlField008-11='a' and $controlField008-14='b'">
4762                                 <xsl:attribute name="authority">
4763                                         <xsl:text>lcsh</xsl:text>
4764                                 </xsl:attribute>
4765                         </xsl:when>
4766                         <xsl:when
4767                                 test="ancestor-or-self::marc:datafield/@tag=100 and @ind1=3 and $controlField008-11='k' and $controlField008-14='b'">
4768                                 <xsl:attribute name="authority">cash</xsl:attribute>
4769                         </xsl:when>
4770                         <xsl:when
4771                                 test="ancestor-or-self::marc:datafield/@tag=110 and $controlField008-11='a' and $controlField008-14='a'">
4772                                 <xsl:attribute name="authority">naf</xsl:attribute>
4773                         </xsl:when>
4774                         <xsl:when
4775                                 test="ancestor-or-self::marc:datafield/@tag=110 and $controlField008-11='a' and $controlField008-14='b'">
4776                                 <xsl:attribute name="authority">lcsh</xsl:attribute>
4777                         </xsl:when>
4778                         <xsl:when
4779                                 test="ancestor-or-self::marc:datafield/@tag=110 and $controlField008-11='k' and $controlField008-14='a'">
4780                                 <xsl:attribute name="authority">
4781                                         <xsl:text>lacnaf</xsl:text>
4782                                 </xsl:attribute>
4783                         </xsl:when>
4784                         <xsl:when
4785                                 test="ancestor-or-self::marc:datafield/@tag=110 and $controlField008-11='k' and $controlField008-14='b'">
4786                                 <xsl:attribute name="authority">
4787                                         <xsl:text>cash</xsl:text>
4788                                 </xsl:attribute>
4789                         </xsl:when>
4790                         <xsl:when
4791                                 test="100 &lt;= ancestor-or-self::marc:datafield/@tag and ancestor-or-self::marc:datafield/@tag &lt;= 155 and $controlField008-11='b'">
4792                                 <xsl:attribute name="authority">
4793                                         <xsl:text>lcshcl</xsl:text>
4794                                 </xsl:attribute>
4795                         </xsl:when>
4796                         <xsl:when
4797                                 test="(ancestor-or-self::marc:datafield/@tag=100 or ancestor-or-self::marc:datafield/@tag=110 or ancestor-or-self::marc:datafield/@tag=111 or ancestor-or-self::marc:datafield/@tag=130 or ancestor-or-self::marc:datafield/@tag=151) and $controlField008-11='c'">
4798                                 <xsl:attribute name="authority">
4799                                         <xsl:text>nlmnaf</xsl:text>
4800                                 </xsl:attribute>
4801                         </xsl:when>
4802                         <xsl:when
4803                                 test="(ancestor-or-self::marc:datafield/@tag=100 or ancestor-or-self::marc:datafield/@tag=110 or ancestor-or-self::marc:datafield/@tag=111 or ancestor-or-self::marc:datafield/@tag=130 or ancestor-or-self::marc:datafield/@tag=151) and $controlField008-11='d'">
4804                                 <xsl:attribute name="authority">
4805                                         <xsl:text>nalnaf</xsl:text>
4806                                 </xsl:attribute>
4807                         </xsl:when>
4808                         <xsl:when
4809                                 test="100 &lt;= ancestor-or-self::marc:datafield/@tag and ancestor-or-self::marc:datafield/@tag &lt;= 155 and $controlField008-11='r'">
4810                                 <xsl:attribute name="authority">
4811                                         <xsl:text>aat</xsl:text>
4812                                 </xsl:attribute>
4813                         </xsl:when>
4814                         <xsl:when
4815                                 test="100 &lt;= ancestor-or-self::marc:datafield/@tag and ancestor-or-self::marc:datafield/@tag &lt;= 155 and $controlField008-11='s'">
4816                                 <xsl:attribute name="authority">sears</xsl:attribute>
4817                         </xsl:when>
4818                         <xsl:when
4819                                 test="100 &lt;= ancestor-or-self::marc:datafield/@tag and ancestor-or-self::marc:datafield/@tag &lt;= 155 and $controlField008-11='v'">
4820                                 <xsl:attribute name="authority">rvm</xsl:attribute>
4821                         </xsl:when>
4822                         <xsl:when
4823                                 test="100 &lt;= ancestor-or-self::marc:datafield/@tag and ancestor-or-self::marc:datafield/@tag &lt;= 155 and $controlField008-11='z'">
4824                                 <xsl:attribute name="authority">
4825                                         <xsl:value-of
4826                                                 select="../marc:datafield[ancestor-or-self::marc:datafield/@tag=040]/marc:subfield[@code='f']"
4827                                         />
4828                                 </xsl:attribute>
4829                         </xsl:when>
4830                         <xsl:when
4831                                 test="(ancestor-or-self::marc:datafield/@tag=111 or ancestor-or-self::marc:datafield/@tag=130) and $controlField008-11='a' and $controlField008-14='a'">
4832                                 <xsl:attribute name="authority">
4833                                         <xsl:text>naf</xsl:text>
4834                                 </xsl:attribute>
4835                         </xsl:when>
4836                         <xsl:when
4837                                 test="(ancestor-or-self::marc:datafield/@tag=111 or ancestor-or-self::marc:datafield/@tag=130) and $controlField008-11='a' and $controlField008-14='b'">
4838                                 <xsl:attribute name="authority">
4839                                         <xsl:text>lcsh</xsl:text>
4840                                 </xsl:attribute>
4841                         </xsl:when>
4842                         <xsl:when
4843                                 test="(ancestor-or-self::marc:datafield/@tag=111 or ancestor-or-self::marc:datafield/@tag=130) and $controlField008-11='k' ">
4844                                 <xsl:attribute name="authority">
4845                                         <xsl:text>lacnaf</xsl:text>
4846                                 </xsl:attribute>
4847                         </xsl:when>
4848                         <xsl:when
4849                                 test="(ancestor-or-self::marc:datafield/@tag=148 or ancestor-or-self::marc:datafield/@tag=150  or ancestor-or-self::marc:datafield/@tag=155) and $controlField008-11='a' ">
4850                                 <xsl:attribute name="authority">
4851                                         <xsl:text>lcsh</xsl:text>
4852                                 </xsl:attribute>
4853                         </xsl:when>
4854                         <xsl:when
4855                                 test="(ancestor-or-self::marc:datafield/@tag=148 or ancestor-or-self::marc:datafield/@tag=150  or ancestor-or-self::marc:datafield/@tag=155) and $controlField008-11='a' ">
4856                                 <xsl:attribute name="authority">
4857                                         <xsl:text>lcsh</xsl:text>
4858                                 </xsl:attribute>
4859                         </xsl:when>
4860                         <xsl:when
4861                                 test="(ancestor-or-self::marc:datafield/@tag=148 or ancestor-or-self::marc:datafield/@tag=150  or ancestor-or-self::marc:datafield/@tag=155) and $controlField008-11='c' ">
4862                                 <xsl:attribute name="authority">
4863                                         <xsl:text>mesh</xsl:text>
4864                                 </xsl:attribute>
4865                         </xsl:when>
4866                         <xsl:when
4867                                 test="(ancestor-or-self::marc:datafield/@tag=148 or ancestor-or-self::marc:datafield/@tag=150  or ancestor-or-self::marc:datafield/@tag=155) and $controlField008-11='d' ">
4868                                 <xsl:attribute name="authority">
4869                                         <xsl:text>nal</xsl:text>
4870                                 </xsl:attribute>
4871                         </xsl:when>
4872                         <xsl:when
4873                                 test="(ancestor-or-self::marc:datafield/@tag=148 or ancestor-or-self::marc:datafield/@tag=150  or ancestor-or-self::marc:datafield/@tag=155) and $controlField008-11='k' ">
4874                                 <xsl:attribute name="authority">
4875                                         <xsl:text>cash</xsl:text>
4876                                 </xsl:attribute>
4877                         </xsl:when>
4878                         <xsl:when
4879                                 test="ancestor-or-self::marc:datafield/@tag=151 and $controlField008-11='a' and $controlField008-14='a'">
4880                                 <xsl:attribute name="authority">
4881                                         <xsl:text>naf</xsl:text>
4882                                 </xsl:attribute>
4883                         </xsl:when>
4884                         <xsl:when
4885                                 test="ancestor-or-self::marc:datafield/@tag=151 and $controlField008-11='a' and $controlField008-14='b'">
4886                                 <xsl:attribute name="authority">lcsh</xsl:attribute>
4887                         </xsl:when>
4888                         <xsl:when
4889                                 test="ancestor-or-self::marc:datafield/@tag=151 and $controlField008-11='k' and $controlField008-14='a'">
4890                                 <xsl:attribute name="authority">lacnaf</xsl:attribute>
4891                         </xsl:when>
4892                         <xsl:when
4893                                 test="ancestor-or-self::marc:datafield/@tag=151 and $controlField008-11='k' and $controlField008-14='b'">
4894                                 <xsl:attribute name="authority">cash</xsl:attribute>
4895                         </xsl:when>
4896                         <xsl:when
4897                                 test="(..//ancestor-or-self::marc:datafield/@tag=180 or ..//ancestor-or-self::marc:datafield/@tag=181 or ..//ancestor-or-self::marc:datafield/@tag=182 or ..//ancestor-or-self::marc:datafield/@tag=185) and $controlField008-11='a'">
4898                                 <xsl:attribute name="authority">lcsh</xsl:attribute>
4899                         </xsl:when>
4900                         <xsl:when
4901                                 test="ancestor-or-self::marc:datafield/@tag=700 and (@ind1='0' or @ind1='1') and @ind2='0'">
4902                                 <xsl:attribute name="authority">naf</xsl:attribute>
4903                         </xsl:when>
4904                         <xsl:when
4905                                 test="ancestor-or-self::marc:datafield/@tag=700 and (@ind1='0' or @ind1='1') and @ind2='5'">
4906                                 <xsl:attribute name="authority">lacnaf</xsl:attribute>
4907                         </xsl:when>
4908                         <xsl:when test="ancestor-or-self::marc:datafield/@tag=700 and @ind1='3' and @ind2='0'">
4909                                 <xsl:attribute name="authority">lcsh</xsl:attribute>
4910                         </xsl:when>
4911                         <xsl:when test="ancestor-or-self::marc:datafield/@tag=700 and @ind1='3' and @ind2='5'">
4912                                 <xsl:attribute name="authority">cash</xsl:attribute>
4913                         </xsl:when>
4914                         <xsl:when
4915                                 test="(700 &lt;= ancestor-or-self::marc:datafield/@tag and ancestor-or-self::marc:datafield/@tag &lt;= 755 ) and @ind2='1'">
4916                                 <xsl:attribute name="authority">lcshcl</xsl:attribute>
4917                         </xsl:when>
4918                         <xsl:when
4919                                 test="(ancestor-or-self::marc:datafield/@tag=700 or ancestor-or-self::marc:datafield/@tag=710 or ancestor-or-self::marc:datafield/@tag=711 or ancestor-or-self::marc:datafield/@tag=730 or ancestor-or-self::marc:datafield/@tag=751)  and @ind2='2'">
4920                                 <xsl:attribute name="authority">nlmnaf</xsl:attribute>
4921                         </xsl:when>
4922                         <xsl:when
4923                                 test="(ancestor-or-self::marc:datafield/@tag=700 or ancestor-or-self::marc:datafield/@tag=710 or ancestor-or-self::marc:datafield/@tag=711 or ancestor-or-self::marc:datafield/@tag=730 or ancestor-or-self::marc:datafield/@tag=751)  and @ind2='3'">
4924                                 <xsl:attribute name="authority">nalnaf</xsl:attribute>
4925                         </xsl:when>
4926                         <xsl:when
4927                                 test="(700 &lt;= ancestor-or-self::marc:datafield/@tag and ancestor-or-self::marc:datafield/@tag &lt;= 755 ) and @ind2='6'">
4928                                 <xsl:attribute name="authority">rvm</xsl:attribute>
4929                         </xsl:when>
4930                         <xsl:when
4931                                 test="(700 &lt;= ancestor-or-self::marc:datafield/@tag and ancestor-or-self::marc:datafield/@tag &lt;= 755 ) and @ind2='7'">
4932                                 <xsl:attribute name="authority">
4933                                         <xsl:value-of select="marc:subfield[@code='2']"/>
4934                                 </xsl:attribute>
4935                         </xsl:when>
4936                         <xsl:when
4937                                 test="(ancestor-or-self::marc:datafield/@tag=710 or ancestor-or-self::marc:datafield/@tag=711 or ancestor-or-self::marc:datafield/@tag=730 or ancestor-or-self::marc:datafield/@tag=751)  and @ind2='5'">
4938                                 <xsl:attribute name="authority">lacnaf</xsl:attribute>
4939                         </xsl:when>
4940                         <xsl:when
4941                                 test="(ancestor-or-self::marc:datafield/@tag=710 or ancestor-or-self::marc:datafield/@tag=711 or ancestor-or-self::marc:datafield/@tag=730 or ancestor-or-self::marc:datafield/@tag=751)  and @ind2='0'">
4942                                 <xsl:attribute name="authority">naf</xsl:attribute>
4943                         </xsl:when>
4944                         <xsl:when
4945                                 test="(ancestor-or-self::marc:datafield/@tag=748 or ancestor-or-self::marc:datafield/@tag=750 or ancestor-or-self::marc:datafield/@tag=755)  and @ind2='0'">
4946                                 <xsl:attribute name="authority">lcsh</xsl:attribute>
4947                         </xsl:when>
4948                         <xsl:when
4949                                 test="(ancestor-or-self::marc:datafield/@tag=748 or ancestor-or-self::marc:datafield/@tag=750 or ancestor-or-self::marc:datafield/@tag=755)  and @ind2='2'">
4950                                 <xsl:attribute name="authority">mesh</xsl:attribute>
4951                         </xsl:when>
4952                         <xsl:when
4953                                 test="(ancestor-or-self::marc:datafield/@tag=748 or ancestor-or-self::marc:datafield/@tag=750 or ancestor-or-self::marc:datafield/@tag=755)  and @ind2='3'">
4954                                 <xsl:attribute name="authority">nal</xsl:attribute>
4955                         </xsl:when>
4956                         <xsl:when
4957                                 test="(ancestor-or-self::marc:datafield/@tag=748 or ancestor-or-self::marc:datafield/@tag=750 or ancestor-or-self::marc:datafield/@tag=755)  and @ind2='5'">
4958                                 <xsl:attribute name="authority">cash</xsl:attribute>
4959                         </xsl:when>
4960                 </xsl:choose>
4961         </xsl:template>
4962         <xsl:template match="*"/>
4963 </xsl:stylesheet>$XSLT$);
4964
4965
4966 SELECT evergreen.upgrade_deps_block_check('1069', :eg_version); --gmcharlt/kmlussier
4967
4968 -- subset of types listed in https://www.loc.gov/marc/authority/ad1xx3xx.html
4969 -- for now, ignoring subdivisions
4970 CREATE TYPE authority.heading_type AS ENUM (
4971     'personal_name',
4972     'corporate_name',
4973     'meeting_name',
4974     'uniform_title',
4975     'named_event',
4976     'chronological_term',
4977     'topical_term',
4978     'geographic_name',
4979     'genre_form_term',
4980     'medium_of_performance_term'
4981 );
4982
4983 CREATE TYPE authority.variant_heading_type AS ENUM (
4984     'abbreviation',
4985     'acronym',
4986     'translation',
4987     'expansion',
4988     'other',
4989     'hidden'
4990 );
4991
4992 CREATE TYPE authority.related_heading_type AS ENUM (
4993     'earlier',
4994     'later',
4995     'parent organization',
4996     'broader',
4997     'narrower',
4998     'equivalent',
4999     'other'
5000 );
5001
5002 CREATE TYPE authority.heading_purpose AS ENUM (
5003     'main',
5004     'variant',
5005     'related'
5006 );
5007
5008 CREATE TABLE authority.heading_field (
5009     id              SERIAL                      PRIMARY KEY,
5010     heading_type    authority.heading_type      NOT NULL,
5011     heading_purpose authority.heading_purpose   NOT NULL,
5012     label           TEXT                        NOT NULL,
5013     format          TEXT                        NOT NULL REFERENCES config.xml_transform (name) DEFAULT 'mads21',
5014     heading_xpath   TEXT                        NOT NULL,
5015     component_xpath TEXT                        NOT NULL,
5016     type_xpath      TEXT                        NULL, -- to extract related or variant type
5017     thesaurus_xpath TEXT                        NULL,
5018     thesaurus_override_xpath TEXT               NULL,
5019     joiner          TEXT                        NULL
5020 );
5021
5022 CREATE TABLE authority.heading_field_norm_map (
5023         id      SERIAL  PRIMARY KEY,
5024         field   INT     NOT NULL REFERENCES authority.heading_field (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
5025         norm    INT     NOT NULL REFERENCES config.index_normalizer (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
5026         params  TEXT,
5027         pos     INT     NOT NULL DEFAULT 0
5028 );
5029
5030 INSERT INTO authority.heading_field(heading_type, heading_purpose, label, heading_xpath, component_xpath, type_xpath, thesaurus_xpath, thesaurus_override_xpath) VALUES
5031  ( 'topical_term', 'main',    'Main Topical Term',    '/mads21:mads/mads21:authority', '//mads21:topic', NULL, '/mads21:mads/mads21:authority/mads21:topic[1]/@authority', NULL )
5032 ,( 'topical_term', 'variant', 'Variant Topical Term', '/mads21:mads/mads21:variant',   '//mads21:topic', '/mads21:variant/@type', '/mads21:mads/mads21:authority/mads21:topic[1]/@authority', '//mads21:topic[1]/@authority')
5033 ,( 'topical_term', 'related', 'Related Topical Term', '/mads21:mads/mads21:related',   '//mads21:topic', '/mads21:related/@type', '/mads21:mads/mads21:authority/mads21:topic[1]/@authority', '//mads21:topic[1]/@authority')
5034 ,( 'personal_name', 'main', 'Main Personal Name',     '/mads21:mads/mads21:authority', '//mads21:name[@type="personal"]', NULL, NULL, NULL )
5035 ,( 'personal_name', 'variant', 'Variant Personal Name',     '/mads21:mads/mads21:variant', '//mads21:name[@type="personal"]', NULL, NULL, NULL )
5036 ,( 'personal_name', 'related', 'Related Personal Name',     '/mads21:mads/mads21:related', '//mads21:name[@type="personal"]', '/mads21:related/@type', NULL, NULL )
5037 ,( 'corporate_name', 'main', 'Main Corporate name',     '/mads21:mads/mads21:authority', '//mads21:name[@type="corporate"]', NULL, NULL, NULL )
5038 ,( 'corporate_name', 'variant', 'Variant Corporate Name',     '/mads21:mads/mads21:variant', '//mads21:name[@type="corporate"]', NULL, NULL, NULL )
5039 ,( 'corporate_name', 'related', 'Related Corporate Name',     '/mads21:mads/mads21:related', '//mads21:name[@type="corporate"]', '/mads21:related/@type', NULL, NULL )
5040 ,( 'meeting_name', 'main', 'Main Meeting name',     '/mads21:mads/mads21:authority', '//mads21:name[@type="conference"]', NULL, NULL, NULL )
5041 ,( 'meeting_name', 'variant', 'Variant Meeting Name',     '/mads21:mads/mads21:variant', '//mads21:name[@type="conference"]', NULL, NULL, NULL )
5042 ,( 'meeting_name', 'related', 'Related Meeting Name',     '/mads21:mads/mads21:related', '//mads21:name[@type="meeting"]', '/mads21:related/@type', NULL, NULL )
5043 ,( 'geographic_name', 'main',    'Main Geographic Term',    '/mads21:mads/mads21:authority', '//mads21:geographic', NULL, '/mads21:mads/mads21:authority/mads21:geographic[1]/@authority', NULL )
5044 ,( 'geographic_name', 'variant', 'Variant Geographic Term', '/mads21:mads/mads21:variant',   '//mads21:geographic', '/mads21:variant/@type', '/mads21:mads/mads21:authority/mads21:geographic[1]/@authority', '//mads21:geographic[1]/@authority')
5045 ,( 'geographic_name', 'related', 'Related Geographic Term', '/mads21:mads/mads21:related',   '//mads21:geographic', '/mads21:related/@type', '/mads21:mads/mads21:authority/mads21:geographic[1]/@authority', '//mads21:geographic[1]/@authority')
5046 ,( 'genre_form_term', 'main',    'Main Genre/Form Term',    '/mads21:mads/mads21:authority', '//mads21:genre', NULL, '/mads21:mads/mads21:authority/mads21:genre[1]/@authority', NULL )
5047 ,( 'genre_form_term', 'variant', 'Variant Genre/Form Term', '/mads21:mads/mads21:variant',   '//mads21:genre', '/mads21:variant/@type', '/mads21:mads/mads21:authority/mads21:genre[1]/@authority', '//mads21:genre[1]/@authority')
5048 ,( 'genre_form_term', 'related', 'Related Genre/Form Term', '/mads21:mads/mads21:related',   '//mads21:genre', '/mads21:related/@type', '/mads21:mads/mads21:authority/mads21:genre[1]/@authority', '//mads21:genre[1]/@authority')
5049 ,( 'chronological_term', 'main',    'Main Chronological Term',    '/mads21:mads/mads21:authority', '//mads21:temporal', NULL, '/mads21:mads/mads21:authority/mads21:temporal[1]/@authority', NULL )
5050 ,( 'chronological_term', 'variant', 'Variant Chronological Term', '/mads21:mads/mads21:variant',   '//mads21:temporal', '/mads21:variant/@type', '/mads21:mads/mads21:authority/mads21:temporal[1]/@authority', '//mads21:temporal[1]/@authority')
5051 ,( 'chronological_term', 'related', 'Related Chronological Term', '/mads21:mads/mads21:related',   '//mads21:temporal', '/mads21:related/@type', '/mads21:mads/mads21:authority/mads21:temporal[1]/@authority', '//mads21:temporal[1]/@authority')
5052 ,( 'uniform_title', 'main',    'Main Uniform Title',    '/mads21:mads/mads21:authority', '//mads21:title', NULL, '/mads21:mads/mads21:authority/mads21:title[1]/@authority', NULL )
5053 ,( 'uniform_title', 'variant', 'Variant Uniform Title', '/mads21:mads/mads21:variant',   '//mads21:title', '/mads21:variant/@type', '/mads21:mads/mads21:authority/mads21:title[1]/@authority', '//mads21:title[1]/@authority')
5054 ,( 'uniform_title', 'related', 'Related Uniform Title', '/mads21:mads/mads21:related',   '//mads21:title', '/mads21:related/@type', '/mads21:mads/mads21:authority/mads21:title[1]/@authority', '//mads21:title[1]/@authority')
5055 ;
5056
5057 -- NACO normalize all the things
5058 INSERT INTO authority.heading_field_norm_map (field, norm, pos)
5059 SELECT id, 1, 0
5060 FROM authority.heading_field;
5061
5062 CREATE TYPE authority.heading AS (
5063     field               INT,
5064     type                authority.heading_type,
5065     purpose             authority.heading_purpose,
5066     variant_type        authority.variant_heading_type,
5067     related_type        authority.related_heading_type,
5068     thesaurus           TEXT,
5069     heading             TEXT,
5070     normalized_heading  TEXT
5071 );
5072
5073 CREATE OR REPLACE FUNCTION authority.extract_headings(marc TEXT, restrict INT[] DEFAULT NULL) RETURNS SETOF authority.heading AS $func$
5074 DECLARE
5075     idx         authority.heading_field%ROWTYPE;
5076     xfrm        config.xml_transform%ROWTYPE;
5077     prev_xfrm   TEXT;
5078     transformed_xml TEXT;
5079     heading_node    TEXT;
5080     heading_node_list   TEXT[];
5081     component_node    TEXT;
5082     component_node_list   TEXT[];
5083     raw_text    TEXT;
5084     normalized_text    TEXT;
5085     normalizer  RECORD;
5086     curr_text   TEXT;
5087     joiner      TEXT;
5088     type_value  TEXT;
5089     base_thesaurus TEXT := NULL;
5090     output_row  authority.heading;
5091 BEGIN
5092
5093     -- Loop over the indexing entries
5094     FOR idx IN SELECT * FROM authority.heading_field WHERE restrict IS NULL OR id = ANY (restrict) ORDER BY format LOOP
5095
5096         output_row.field   := idx.id;
5097         output_row.type    := idx.heading_type;
5098         output_row.purpose := idx.heading_purpose;
5099
5100         joiner := COALESCE(idx.joiner, ' ');
5101
5102         SELECT INTO xfrm * from config.xml_transform WHERE name = idx.format;
5103
5104         -- See if we can skip the XSLT ... it's expensive
5105         IF prev_xfrm IS NULL OR prev_xfrm <> xfrm.name THEN
5106             -- Can't skip the transform
5107             IF xfrm.xslt <> '---' THEN
5108                 transformed_xml := oils_xslt_process(marc, xfrm.xslt);
5109             ELSE
5110                 transformed_xml := marc;
5111             END IF;
5112
5113             prev_xfrm := xfrm.name;
5114         END IF;
5115
5116         IF idx.thesaurus_xpath IS NOT NULL THEN
5117             base_thesaurus := ARRAY_TO_STRING(oils_xpath(idx.thesaurus_xpath, transformed_xml, ARRAY[ARRAY[xfrm.prefix, xfrm.namespace_uri]]), '');
5118         END IF;
5119
5120         heading_node_list := oils_xpath( idx.heading_xpath, transformed_xml, ARRAY[ARRAY[xfrm.prefix, xfrm.namespace_uri]] );
5121
5122         FOR heading_node IN SELECT x FROM unnest(heading_node_list) AS x LOOP
5123
5124             CONTINUE WHEN heading_node !~ E'^\\s*<';
5125
5126             output_row.variant_type := NULL;
5127             output_row.related_type := NULL;
5128             output_row.thesaurus    := NULL;
5129             output_row.heading      := NULL;
5130
5131             IF idx.heading_purpose = 'variant' AND idx.type_xpath IS NOT NULL THEN
5132                 type_value := ARRAY_TO_STRING(oils_xpath(idx.type_xpath, heading_node, ARRAY[ARRAY[xfrm.prefix, xfrm.namespace_uri]]), '');
5133                 BEGIN
5134                     output_row.variant_type := type_value;
5135                 EXCEPTION WHEN invalid_text_representation THEN
5136                     RAISE NOTICE 'Do not recognize variant heading type %', type_value;
5137                 END;
5138             END IF;
5139             IF idx.heading_purpose = 'related' AND idx.type_xpath IS NOT NULL THEN
5140                 type_value := ARRAY_TO_STRING(oils_xpath(idx.type_xpath, heading_node, ARRAY[ARRAY[xfrm.prefix, xfrm.namespace_uri]]), '');
5141                 BEGIN
5142                     output_row.related_type := type_value;
5143                 EXCEPTION WHEN invalid_text_representation THEN
5144                     RAISE NOTICE 'Do not recognize related heading type %', type_value;
5145                 END;
5146             END IF;
5147  
5148             IF idx.thesaurus_override_xpath IS NOT NULL THEN
5149                 output_row.thesaurus := ARRAY_TO_STRING(oils_xpath(idx.thesaurus_override_xpath, heading_node, ARRAY[ARRAY[xfrm.prefix, xfrm.namespace_uri]]), '');
5150             END IF;
5151             IF output_row.thesaurus IS NULL THEN
5152                 output_row.thesaurus := base_thesaurus;
5153             END IF;
5154
5155             raw_text := NULL;
5156
5157             -- now iterate over components of heading
5158             component_node_list := oils_xpath( idx.component_xpath, heading_node, ARRAY[ARRAY[xfrm.prefix, xfrm.namespace_uri]] );
5159             FOR component_node IN SELECT x FROM unnest(component_node_list) AS x LOOP
5160             -- XXX much of this should be moved into oils_xpath_string...
5161                 curr_text := ARRAY_TO_STRING(evergreen.array_remove_item_by_value(evergreen.array_remove_item_by_value(
5162                     oils_xpath( '//text()', -- get the content of all the nodes within the main selected node
5163                         REGEXP_REPLACE( component_node, E'\\s+', ' ', 'g' ) -- Translate adjacent whitespace to a single space
5164                     ), ' '), ''),  -- throw away morally empty (bankrupt?) strings
5165                     joiner
5166                 );
5167
5168                 CONTINUE WHEN curr_text IS NULL OR curr_text = '';
5169
5170                 IF raw_text IS NOT NULL THEN
5171                     raw_text := raw_text || joiner;
5172                 END IF;
5173
5174                 raw_text := COALESCE(raw_text,'') || curr_text;
5175             END LOOP;
5176
5177             IF raw_text IS NOT NULL THEN
5178                 output_row.heading := raw_text;
5179                 normalized_text := raw_text;
5180
5181                 FOR normalizer IN
5182                     SELECT  n.func AS func,
5183                             n.param_count AS param_count,
5184                             m.params AS params
5185                     FROM  config.index_normalizer n
5186                             JOIN authority.heading_field_norm_map m ON (m.norm = n.id)
5187                     WHERE m.field = idx.id
5188                     ORDER BY m.pos LOOP
5189             
5190                         EXECUTE 'SELECT ' || normalizer.func || '(' ||
5191                             quote_literal( normalized_text ) ||
5192                             CASE
5193                                 WHEN normalizer.param_count > 0
5194                                     THEN ',' || REPLACE(REPLACE(BTRIM(normalizer.params,'[]'),E'\'',E'\\\''),E'"',E'\'')
5195                                     ELSE ''
5196                                 END ||
5197                             ')' INTO normalized_text;
5198             
5199                 END LOOP;
5200             
5201                 output_row.normalized_heading := normalized_text;
5202             
5203                 RETURN NEXT output_row;
5204             END IF;
5205         END LOOP;
5206
5207     END LOOP;
5208 END;
5209 $func$ LANGUAGE PLPGSQL;
5210
5211 CREATE OR REPLACE FUNCTION authority.extract_headings(rid BIGINT, restrict INT[] DEFAULT NULL) RETURNS SETOF authority.heading AS $func$
5212 DECLARE
5213     auth        authority.record_entry%ROWTYPE;
5214     output_row  authority.heading;
5215 BEGIN
5216     -- Get the record
5217     SELECT INTO auth * FROM authority.record_entry WHERE id = rid;
5218
5219     RETURN QUERY SELECT * FROM authority.extract_headings(auth.marc, restrict);
5220 END;
5221 $func$ LANGUAGE PLPGSQL;
5222
5223 CREATE OR REPLACE FUNCTION authority.simple_heading_set( marcxml TEXT ) RETURNS SETOF authority.simple_heading AS $func$
5224 DECLARE
5225     res             authority.simple_heading%ROWTYPE;
5226     acsaf           authority.control_set_authority_field%ROWTYPE;
5227     heading_row     authority.heading%ROWTYPE;
5228     tag_used        TEXT;
5229     nfi_used        TEXT;
5230     sf              TEXT;
5231     cset            INT;
5232     heading_text    TEXT;
5233     joiner_text     TEXT;
5234     sort_text       TEXT;
5235     tmp_text        TEXT;
5236     tmp_xml         TEXT;
5237     first_sf        BOOL;
5238     auth_id         INT DEFAULT COALESCE(NULLIF(oils_xpath_string('//*[@tag="901"]/*[local-name()="subfield" and @code="c"]', marcxml), ''), '0')::INT; 
5239 BEGIN
5240
5241     SELECT control_set INTO cset FROM authority.record_entry WHERE id = auth_id;
5242
5243     IF cset IS NULL THEN
5244         SELECT  control_set INTO cset
5245           FROM  authority.control_set_authority_field
5246           WHERE tag IN ( SELECT  UNNEST(XPATH('//*[starts-with(@tag,"1")]/@tag',marcxml::XML)::TEXT[]))
5247           LIMIT 1;
5248     END IF;
5249
5250     res.record := auth_id;
5251     res.thesaurus := authority.extract_thesaurus(marcxml);
5252
5253     FOR acsaf IN SELECT * FROM authority.control_set_authority_field WHERE control_set = cset LOOP
5254         res.atag := acsaf.id;
5255
5256         IF acsaf.heading_field IS NULL THEN
5257             tag_used := acsaf.tag;
5258             nfi_used := acsaf.nfi;
5259             joiner_text := COALESCE(acsaf.joiner, ' ');
5260     
5261             FOR tmp_xml IN SELECT UNNEST(XPATH('//*[@tag="'||tag_used||'"]', marcxml::XML)::TEXT[]) LOOP
5262     
5263                 heading_text := COALESCE(
5264                     oils_xpath_string('./*[contains("'||acsaf.display_sf_list||'",@code)]', tmp_xml, joiner_text),
5265                     ''
5266                 );
5267     
5268                 IF nfi_used IS NOT NULL THEN
5269     
5270                     sort_text := SUBSTRING(
5271                         heading_text FROM
5272                         COALESCE(
5273                             NULLIF(
5274                                 REGEXP_REPLACE(
5275                                     oils_xpath_string('./@ind'||nfi_used, tmp_xml::TEXT),
5276                                     $$\D+$$,
5277                                     '',
5278                                     'g'
5279                                 ),
5280                                 ''
5281                             )::INT,
5282                             0
5283                         ) + 1
5284                     );
5285     
5286                 ELSE
5287                     sort_text := heading_text;
5288                 END IF;
5289     
5290                 IF heading_text IS NOT NULL AND heading_text <> '' THEN
5291                     res.value := heading_text;
5292                     res.sort_value := public.naco_normalize(sort_text);
5293                     res.index_vector = to_tsvector('keyword'::regconfig, res.sort_value);
5294                     RETURN NEXT res;
5295                 END IF;
5296     
5297             END LOOP;
5298         ELSE
5299             FOR heading_row IN SELECT * FROM authority.extract_headings(marcxml, ARRAY[acsaf.heading_field]) LOOP
5300                 res.value := heading_row.heading;
5301                 res.sort_value := heading_row.normalized_heading;
5302                 res.index_vector = to_tsvector('keyword'::regconfig, res.sort_value);
5303                 RETURN NEXT res;
5304             END LOOP;
5305         END IF;
5306     END LOOP;
5307
5308     RETURN;
5309 END;
5310 $func$ LANGUAGE PLPGSQL STABLE STRICT;
5311
5312 ALTER TABLE authority.control_set_authority_field ADD COLUMN heading_field INTEGER REFERENCES authority.heading_field(id);
5313
5314 UPDATE authority.control_set_authority_field acsaf
5315 SET heading_field = ahf.id
5316 FROM authority.heading_field ahf
5317 WHERE tag = '100'
5318 AND control_set = 1
5319 AND ahf.heading_purpose = 'main'
5320 AND ahf.heading_type = 'personal_name';
5321 UPDATE authority.control_set_authority_field acsaf
5322 SET heading_field = ahf.id
5323 FROM authority.heading_field ahf
5324 WHERE tag = '400'
5325 AND control_set = 1
5326 AND ahf.heading_purpose = 'variant'
5327 AND ahf.heading_type = 'personal_name';
5328 UPDATE authority.control_set_authority_field acsaf
5329 SET heading_field = ahf.id
5330 FROM authority.heading_field ahf
5331 WHERE tag = '500'
5332 AND control_set = 1
5333 AND ahf.heading_purpose = 'related'
5334 AND ahf.heading_type = 'personal_name';
5335
5336 UPDATE authority.control_set_authority_field acsaf
5337 SET heading_field = ahf.id
5338 FROM authority.heading_field ahf
5339 WHERE tag = '110'
5340 AND control_set = 1
5341 AND ahf.heading_purpose = 'main'
5342 AND ahf.heading_type = 'corporate_name';
5343 UPDATE authority.control_set_authority_field acsaf
5344 SET heading_field = ahf.id
5345 FROM authority.heading_field ahf
5346 WHERE tag = '410'
5347 AND control_set = 1
5348 AND ahf.heading_purpose = 'variant'
5349 AND ahf.heading_type = 'corporate_name';
5350 UPDATE authority.control_set_authority_field acsaf
5351 SET heading_field = ahf.id
5352 FROM authority.heading_field ahf
5353 WHERE tag = '510'
5354 AND control_set = 1
5355 AND ahf.heading_purpose = 'related'
5356 AND ahf.heading_type = 'corporate_name';
5357
5358 UPDATE authority.control_set_authority_field acsaf
5359 SET heading_field = ahf.id
5360 FROM authority.heading_field ahf
5361 WHERE tag = '111'
5362 AND control_set = 1
5363 AND ahf.heading_purpose = 'main'
5364 AND ahf.heading_type = 'meeting_name';
5365 UPDATE authority.control_set_authority_field acsaf
5366 SET heading_field = ahf.id
5367 FROM authority.heading_field ahf
5368 WHERE tag = '411'
5369 AND control_set = 1
5370 AND ahf.heading_purpose = 'variant'
5371 AND ahf.heading_type = 'meeting_name';
5372 UPDATE authority.control_set_authority_field acsaf
5373 SET heading_field = ahf.id
5374 FROM authority.heading_field ahf
5375 WHERE tag = '511'
5376 AND control_set = 1
5377 AND ahf.heading_purpose = 'related'
5378 AND ahf.heading_type = 'meeting_name';
5379
5380 UPDATE authority.control_set_authority_field acsaf
5381 SET heading_field = ahf.id
5382 FROM authority.heading_field ahf
5383 WHERE tag = '130'
5384 AND control_set = 1
5385 AND ahf.heading_purpose = 'main'
5386 AND ahf.heading_type = 'uniform_title';
5387 UPDATE authority.control_set_authority_field acsaf
5388 SET heading_field = ahf.id
5389 FROM authority.heading_field ahf
5390 WHERE tag = '430'
5391 AND control_set = 1
5392 AND ahf.heading_purpose = 'variant'
5393 AND ahf.heading_type = 'uniform_title';
5394 UPDATE authority.control_set_authority_field acsaf
5395 SET heading_field = ahf.id
5396 FROM authority.heading_field ahf
5397 WHERE tag = '530'
5398 AND control_set = 1
5399 AND ahf.heading_purpose = 'related'
5400 AND ahf.heading_type = 'uniform_title';
5401
5402 UPDATE authority.control_set_authority_field acsaf
5403 SET heading_field = ahf.id
5404 FROM authority.heading_field ahf
5405 WHERE tag = '150'
5406 AND control_set = 1
5407 AND ahf.heading_purpose = 'main'
5408 AND ahf.heading_type = 'topical_term';
5409 UPDATE authority.control_set_authority_field acsaf
5410 SET heading_field = ahf.id
5411 FROM authority.heading_field ahf
5412 WHERE tag = '450'
5413 AND control_set = 1
5414 AND ahf.heading_purpose = 'variant'
5415 AND ahf.heading_type = 'topical_term';
5416 UPDATE authority.control_set_authority_field acsaf
5417 SET heading_field = ahf.id
5418 FROM authority.heading_field ahf
5419 WHERE tag = '550'
5420 AND control_set = 1
5421 AND ahf.heading_purpose = 'related'
5422 AND ahf.heading_type = 'topical_term';
5423
5424 UPDATE authority.control_set_authority_field acsaf
5425 SET heading_field = ahf.id
5426 FROM authority.heading_field ahf
5427 WHERE tag = '151'
5428 AND control_set = 1
5429 AND ahf.heading_purpose = 'main'
5430 AND ahf.heading_type = 'geographic_name';
5431 UPDATE authority.control_set_authority_field acsaf
5432 SET heading_field = ahf.id
5433 FROM authority.heading_field ahf
5434 WHERE tag = '451'
5435 AND control_set = 1
5436 AND ahf.heading_purpose = 'variant'
5437 AND ahf.heading_type = 'geographic_name';
5438 UPDATE authority.control_set_authority_field acsaf
5439 SET heading_field = ahf.id
5440 FROM authority.heading_field ahf
5441 WHERE tag = '551'
5442 AND control_set = 1
5443 AND ahf.heading_purpose = 'related'
5444 AND ahf.heading_type = 'geographic_name';
5445
5446 UPDATE authority.control_set_authority_field acsaf
5447 SET heading_field = ahf.id
5448 FROM authority.heading_field ahf
5449 WHERE tag = '155'
5450 AND control_set = 1
5451 AND ahf.heading_purpose = 'main'
5452 AND ahf.heading_type = 'genre_form_term';
5453 UPDATE authority.control_set_authority_field acsaf
5454 SET heading_field = ahf.id
5455 FROM authority.heading_field ahf
5456 WHERE tag = '455'
5457 AND control_set = 1
5458 AND ahf.heading_purpose = 'variant'
5459 AND ahf.heading_type = 'genre_form_term';
5460 UPDATE authority.control_set_authority_field acsaf
5461 SET heading_field = ahf.id
5462 FROM authority.heading_field ahf
5463 WHERE tag = '555'
5464 AND control_set = 1
5465 AND ahf.heading_purpose = 'related'
5466 AND ahf.heading_type = 'genre_form_term';
5467
5468
5469 SELECT evergreen.upgrade_deps_block_check('1070', :eg_version); --miker/gmcharlt/kmlussier
5470
5471 CREATE TRIGGER thes_code_tracking_trigger
5472     AFTER UPDATE ON authority.thesaurus
5473     FOR EACH ROW EXECUTE PROCEDURE oils_i18n_code_tracking('at');
5474
5475 ALTER TABLE authority.thesaurus ADD COLUMN short_code TEXT, ADD COLUMN uri TEXT;
5476
5477 DELETE FROM authority.thesaurus WHERE control_set = 1 AND code NOT IN ('n',' ','|');
5478 UPDATE authority.thesaurus SET short_code = code;
5479
5480 CREATE TEMP TABLE thesauri (code text, uri text, name text, xlate hstore);
5481 COPY thesauri (code, uri, name, xlate) FROM STDIN;
5482 migfg   http://id.loc.gov/vocabulary/genreFormSchemes/migfg     Moving image genre-form guide   
5483 reveal  http://id.loc.gov/vocabulary/genreFormSchemes/reveal    REVEAL: fiction indexing and genre headings     
5484 dct     http://id.loc.gov/vocabulary/genreFormSchemes/dct       Dublin Core list of resource types      
5485 gmgpc   http://id.loc.gov/vocabulary/genreFormSchemes/gmgpc     Thesaurus for graphic materials: TGM II, Genre and physical characteristic terms        
5486 rbgenr  http://id.loc.gov/vocabulary/genreFormSchemes/rbgenr    Genre terms: a thesaurus for use in rare book and special collections cataloguing       
5487 sgp     http://id.loc.gov/vocabulary/genreFormSchemes/sgp       Svenska genrebeteckningar fr periodika  "sv"=>"Svenska genrebeteckningar fr periodika"
5488 estc    http://id.loc.gov/vocabulary/genreFormSchemes/estc      Eighteenth century short title catalogue, the cataloguing rules. New ed.        
5489 ftamc   http://id.loc.gov/vocabulary/genreFormSchemes/ftamc     Form terms for archival and manuscripts control 
5490 alett   http://id.loc.gov/vocabulary/genreFormSchemes/alett     An alphabetical list of English text types      
5491 gtlm    http://id.loc.gov/vocabulary/genreFormSchemes/gtlm      Genre terms for law materials: a thesaurus      
5492 rbprov  http://id.loc.gov/vocabulary/genreFormSchemes/rbprov    Provenance evidence: a thesaurus for use in rare book and special collections cataloging        
5493 rbbin   http://id.loc.gov/vocabulary/genreFormSchemes/rbbin     Binding terms: a thesaurus for use in rare book and special collections cataloguing     
5494 fbg     http://id.loc.gov/vocabulary/genreFormSchemes/fbg       Films by genre /dd>     
5495 isbdmedia       http://id.loc.gov/vocabulary/genreFormSchemes/isbdmedia ISBD Area 0 [media]     
5496 marccategory    http://id.loc.gov/vocabulary/genreFormSchemes/marccategory      MARC form category term list    
5497 gnd-music       http://id.loc.gov/vocabulary/genreFormSchemes/gnd-music Gemeinsame Normdatei: Musikalische Ausgabeform  
5498 proysen http://id.loc.gov/vocabulary/genreFormSchemes/proysen   Prøysen: emneord for Prøysen-bibliografien        
5499 rdacarrier      http://id.loc.gov/vocabulary/genreFormSchemes/rdacarrier        Term and code list for RDA carrier types        
5500 gnd     http://id.loc.gov/vocabulary/genreFormSchemes/gnd       Gemeinsame Normdatei    
5501 cjh     http://id.loc.gov/vocabulary/genreFormSchemes/cjh       Center for Jewish History thesaurus     
5502 rbpri   http://id.loc.gov/vocabulary/genreFormSchemes/rbpri     Printing & publishing evidence: a thesaurus for use in rare book and special collections cataloging     
5503 fgtpcm  http://id.loc.gov/vocabulary/genreFormSchemes/fgtpcm    Form/genre terms for printed cartoon material   
5504 rbpub   http://id.loc.gov/vocabulary/genreFormSchemes/rbpub     Printing and publishing evidence: a thesaurus for use in rare book and special collections cataloging   
5505 gmd     http://id.loc.gov/vocabulary/genreFormSchemes/gmd       Anglo-American Cataloguing Rules general material designation   
5506 rbpap   http://id.loc.gov/vocabulary/genreFormSchemes/rbpap     Paper terms: a thesaurus for use in rare book and special collections cataloging        
5507 rdamedia        http://id.loc.gov/vocabulary/genreFormSchemes/rdamedia  Term and code list for RDA media types  
5508 marcsmd http://id.loc.gov/vocabulary/genreFormSchemes/marcsmd   MARC specific material form term list   
5509 saogf   http://id.loc.gov/vocabulary/genreFormSchemes/saogf     Svenska Ã¤mnesord - Genre/Form        "sv"=>"Svenska Ã¤mnesord - Genre/Form"
5510 lcgft   http://id.loc.gov/vocabulary/genreFormSchemes/lcgft     Library of Congress genre/form terms for library and archival materials 
5511 muzeukv http://id.loc.gov/vocabulary/genreFormSchemes/muzeukv   MuzeVideo UK DVD and UMD film genre classification      
5512 mim     http://id.loc.gov/vocabulary/genreFormSchemes/mim       Moving image materials: genre terms     
5513 nmc     http://id.loc.gov/vocabulary/genreFormSchemes/nmc       Revised nomenclature for museum cataloging: a revised and expanded version of Robert C. Chenhall's system for classifying man-made objects      
5514 gnd-content     http://id.loc.gov/vocabulary/genreFormSchemes/gnd-content       Gemeinsame Normdatei: Beschreibung des Inhalts  
5515 bgtchm  http://id.loc.gov/vocabulary/genreFormSchemes/bgtchm    Basic genre terms for cultural heritage materials       
5516 gsafd   http://id.loc.gov/vocabulary/genreFormSchemes/gsafd     Guidelines on subject access to individual works of fiction, drama, etc 
5517 marcform        http://id.loc.gov/vocabulary/genreFormSchemes/marcform  MARC form of item term list     
5518 marcgt  http://id.loc.gov/vocabulary/genreFormSchemes/marcgt    MARC genre terms        
5519 barngf  http://id.loc.gov/vocabulary/genreFormSchemes/barngf    Svenska Ã¤mnesord för barn - Genre/Form    "sv"=>"Svenska Ã¤mnesord för barn - Genre/Form"
5520 ngl     http://id.loc.gov/vocabulary/genreFormSchemes/ngl       Newspaper genre list    
5521 rvmgf   http://id.loc.gov/vocabulary/genreFormSchemes/rvmgf     Thésaurus des descripteurs de genre/forme de l'Université Laval   "fr"=>"Thésaurus des descripteurs de genre/forme de l'Université Laval"
5522 tgfbne  http://id.loc.gov/vocabulary/genreFormSchemes/tgfbne    Términos de género/forma de la Biblioteca Nacional de España   
5523 nbdbgf  http://id.loc.gov/vocabulary/genreFormSchemes/nbdbgf    NBD Biblion Genres Fictie       
5524 rbtyp   http://id.loc.gov/vocabulary/genreFormSchemes/rbtyp     Type evidence: a thesaurus for use in rare book and special collections cataloging      
5525 radfg   http://id.loc.gov/vocabulary/genreFormSchemes/radfg     Radio form / genre terms guide  
5526 gnd-carrier     http://id.loc.gov/vocabulary/genreFormSchemes/gnd-carrier       Gemeinsame Normdatei: Datenträgertyp 
5527 gatbeg  http://id.loc.gov/vocabulary/genreFormSchemes/gatbeg    Gattungsbegriffe        "de"=>"Gattungsbegriffe"
5528 rdacontent      http://id.loc.gov/vocabulary/genreFormSchemes/rdacontent        Term and code list for RDA content types        
5529 isbdcontent     http://id.loc.gov/vocabulary/genreFormSchemes/isbdcontent       ISBD Area 0 [content]   
5530 nimafc  http://id.loc.gov/vocabulary/genreFormSchemes/nimafc    NIMA form codes 
5531 amg     http://id.loc.gov/vocabulary/genreFormSchemes/amg       Audiovisual material glossary   
5532 local   http://id.loc.gov/vocabulary/subjectSchemes/local       Locally assigned term   
5533 taika   http://id.loc.gov/vocabulary/subjectSchemes/taika       Taideteollisuuden asiasanasto   "fi"=>"Taideteollisuuden asiasanasto"
5534 nasat   http://id.loc.gov/vocabulary/subjectSchemes/nasat       NASA thesaurus  
5535 rswkaf  http://id.loc.gov/vocabulary/subjectSchemes/rswkaf      Alternativform zum Hauptschlagwort      "de"=>"Alternativform zum Hauptschlagwort"
5536 jhpk    http://id.loc.gov/vocabulary/subjectSchemes/jhpk        JÄ\99zyk haseÅ\82 przedmiotowych KABA   "pl"=>"JÄ\99zyk haseÅ\82 przedmiotowych KABA"
5537 asrcrfcd        http://id.loc.gov/vocabulary/subjectSchemes/asrcrfcd    Australian Standard Research Classification: Research Fields, Courses and Disciplines (RFCD) classification     
5538 bt      http://id.loc.gov/vocabulary/subjectSchemes/bt  Bioethics thesaurus     
5539 lcstt   http://id.loc.gov/vocabulary/subjectSchemes/lcstt       List of Chinese subject terms   
5540 netc    http://id.loc.gov/vocabulary/subjectSchemes/netc        National Emergency Training Center Thesaurus (NETC)     
5541 aat     http://id.loc.gov/vocabulary/subjectSchemes/aat Art & architecture thesaurus    
5542 bet     http://id.loc.gov/vocabulary/subjectSchemes/bet British education thesaurus     
5543 ncjt    http://id.loc.gov/vocabulary/subjectSchemes/ncjt        National criminal justice thesaurus     
5544 samisk  http://id.loc.gov/vocabulary/subjectSchemes/samisk      Sami bibliography       "no"=>"Sámi bibliografia = Samisk bibliografi (Norge)"
5545 tips    http://id.loc.gov/vocabulary/subjectSchemes/tips        Tesauro ISOC de psicología   "es"=>"Tesauro ISOC de psicología"
5546 ukslc   http://id.loc.gov/vocabulary/subjectSchemes/ukslc       UK Standard Library Categories  
5547 tekord  http://id.loc.gov/vocabulary/subjectSchemes/tekord      TEK-ord : UBiTs emneordliste for arkitektur, realfag, og teknolog       "no"=>"TEK-ord : UBiTs emneordliste for arkitektur, realfag, og teknolog"
5548 umitrist        http://id.loc.gov/vocabulary/subjectSchemes/umitrist    University of Michigan Transportation Research Institute structured thesaurus   
5549 wgst    http://id.loc.gov/vocabulary/subjectSchemes/wgst        Washington GILS Subject Tree    
5550 rasuqam http://id.loc.gov/vocabulary/subjectSchemes/rasuqam     Répertoire d'autorités-sujet de l'UQAM    "fr"=>"Répertoire d'autorités-sujet de l'UQAM"
5551 ntids   http://id.loc.gov/vocabulary/subjectSchemes/ntids       Norske tidsskrifter 1700-1820: emneord  "no"=>"Norske tidsskrifter 1700-1820: emneord"
5552 kaa     http://id.loc.gov/vocabulary/subjectSchemes/kaa Kasvatusalan asiasanasto        "fi"=>"Kasvatusalan asiasanasto"
5553 yso     http://id.loc.gov/vocabulary/subjectSchemes/yso YSO - Yleinen suomalainen ontologia     "fi"=>"YSO - Yleinen suomalainen ontologia"
5554 gcipmedia       http://id.loc.gov/vocabulary/subjectSchemes/gcipmedia   GAMECIP - Computer Game Media Formats (GAMECIP (Game Metadata and Citation Project))    
5555 inspect http://id.loc.gov/vocabulary/subjectSchemes/inspect     INSPEC thesaurus        
5556 ordnok  http://id.loc.gov/vocabulary/subjectSchemes/ordnok      Ordnokkelen: tesaurus for kulturminnevern       "no"=>"Ordnokkelen: tesaurus for kulturminnevern"
5557 helecon http://id.loc.gov/vocabulary/subjectSchemes/helecon     Asiasanasto HELECON-tietikantoihin      "fi"=>"Asiasanasto HELECON-tietikantoihin"
5558 dltlt   http://id.loc.gov/vocabulary/subjectSchemes/dltlt       Cuddon, J. A. A dictionary of literary terms and literary theory        
5559 csapa   http://id.loc.gov/vocabulary/subjectSchemes/csapa       "Controlled vocabulary" in Pollution abstracts  
5560 gtt     http://id.loc.gov/vocabulary/subjectSchemes/gtt GOO-trefwoorden thesaurus       "nl"=>"GOO-trefwoorden thesaurus"
5561 iescs   http://id.loc.gov/vocabulary/subjectSchemes/iescs       International energy subject categories and scope       
5562 itrt    http://id.loc.gov/vocabulary/subjectSchemes/itrt        International Thesaurus of Refugee Terminology  
5563 sanb    http://id.loc.gov/vocabulary/subjectSchemes/sanb        South African national bibliography authority file      
5564 blmlsh  http://id.loc.gov/vocabulary/subjectSchemes/blmlsh      British Library - Map library subject headings  
5565 bhb     http://id.loc.gov/vocabulary/subjectSchemes/bhb Bibliography of the Hebrew Book 
5566 csh     http://id.loc.gov/vocabulary/subjectSchemes/csh Kapsner, Oliver Leonard. Catholic subject headings      
5567 fire    http://id.loc.gov/vocabulary/subjectSchemes/fire        FireTalk, IFSI thesaurus        
5568 jlabsh  http://id.loc.gov/vocabulary/subjectSchemes/jlabsh      Basic subject headings  "ja"=>"Kihon kenmei hyômokuhyô"
5569 udc     http://id.loc.gov/vocabulary/subjectSchemes/udc Universal decimal classification        
5570 lcshac  http://id.loc.gov/vocabulary/subjectSchemes/lcshac      Children's subject headings in Library of Congress subject headings: supplementary vocabularies 
5571 geonet  http://id.loc.gov/vocabulary/subjectSchemes/geonet      NGA GEOnet Names Server (GNS)   
5572 humord  http://id.loc.gov/vocabulary/subjectSchemes/humord      HUMORD  "no"=>"HUMORD"
5573 no-ubo-mr       http://id.loc.gov/vocabulary/subjectSchemes/no-ubo-mr   Menneskerettighets-tesaurus     "no"=>"Menneskerettighets-tesaurus"
5574 sgce    http://id.loc.gov/vocabulary/subjectSchemes/sgce        COBISS.SI General List of subject headings (English subject headings)   "sl"=>"SploÅ¡ni geslovnik COBISS.SI"
5575 kdm     http://id.loc.gov/vocabulary/subjectSchemes/kdm Khung dê muc hê thông thông tin khoa hoc và ky thuât quôc gia      "vi"=>"Khung dê muc hê thông thông tin khoa hoc và ky thuât quôc gia"
5576 thesoz  http://id.loc.gov/vocabulary/subjectSchemes/thesoz      Thesaurus for the Social Sciences       
5577 asth    http://id.loc.gov/vocabulary/subjectSchemes/asth        Astronomy thesaurus     
5578 muzeukc http://id.loc.gov/vocabulary/subjectSchemes/muzeukc     MuzeMusic UK classical music classification     
5579 norbok  http://id.loc.gov/vocabulary/subjectSchemes/norbok      Norbok: emneord i Norsk bokfortegnelse  "no"=>"Norbok: emneord i Norsk bokfortegnelse"
5580 masa    http://id.loc.gov/vocabulary/subjectSchemes/masa        Museoalan asiasanasto   "fi"=>"Museoalan asiasanasto"
5581 conorsi http://id.loc.gov/vocabulary/subjectSchemes/conorsi     CONOR.SI (name authority file) (Maribor, Slovenia: Institut informacijskih znanosti (IZUM))     
5582 eurovocen       http://id.loc.gov/vocabulary/subjectSchemes/eurovocen   Eurovoc thesaurus (English)     
5583 kto     http://id.loc.gov/vocabulary/subjectSchemes/kto KTO - Kielitieteen ontologia    "fi"=>"KTO - Kielitieteen ontologia"
5584 muzvukci        http://id.loc.gov/vocabulary/subjectSchemes/muzvukci    MuzeVideo UK contributor index  
5585 kaunokki        http://id.loc.gov/vocabulary/subjectSchemes/kaunokki    Kaunokki: kaunokirjallisuuden asiasanasto       "fi"=>"Kaunokki: kaunokirjallisuuden asiasanasto"
5586 maotao  http://id.loc.gov/vocabulary/subjectSchemes/maotao      MAO/TAO - Ontologi för museibranschen och Konstindustriella ontologin        "fi"=>"MAO/TAO - Ontologi för museibranschen och Konstindustriella ontologin"
5587 psychit http://id.loc.gov/vocabulary/subjectSchemes/psychit     Thesaurus of psychological index terms. 
5588 tlsh    http://id.loc.gov/vocabulary/subjectSchemes/tlsh        Subject heading authority list  
5589 csalsct http://id.loc.gov/vocabulary/subjectSchemes/csalsct     CSA life sciences collection thesaurus  
5590 ciesiniv        http://id.loc.gov/vocabulary/subjectSchemes/ciesiniv    CIESIN indexing vocabulary      
5591 ebfem   http://id.loc.gov/vocabulary/subjectSchemes/ebfem       Encabezamientos bilingües de la Fundación Educativa Ana G. Mendez 
5592 mero    http://id.loc.gov/vocabulary/subjectSchemes/mero        MERO - Merenkulkualan ontologia "fi"=>"MERO - Merenkulkualan ontologia"
5593 mmm     http://id.loc.gov/vocabulary/subjectSchemes/mmm "Subject key" in Marxism and the mass media     
5594 pascal  http://id.loc.gov/vocabulary/subjectSchemes/pascal      PASCAL database classification scheme   "fr"=>"Base de donneés PASCAL: plan de classement"
5595 chirosh http://id.loc.gov/vocabulary/subjectSchemes/chirosh     Chiropractic Subject Headings   
5596 cilla   http://id.loc.gov/vocabulary/subjectSchemes/cilla       Cilla: specialtesaurus för musik     "fi"=>"Cilla: specialtesaurus för musik"
5597 aiatsisl        http://id.loc.gov/vocabulary/subjectSchemes/aiatsisl    AIATSIS language thesaurus      
5598 nskps   http://id.loc.gov/vocabulary/subjectSchemes/nskps       PriruÄ\8dnik za izradu predmetnog kataloga u Nacionalnoj i sveuÄ\8diliÅ¡noj knjiÄ\8dnici u Zagrebu    "hr"=>"PriruÄ\8dnik za izradu predmetnog kataloga u Nacionalnoj i sveuÄ\8diliÅ¡noj knjiÄ\8dnici u Zagrebu"
5599 lctgm   http://id.loc.gov/vocabulary/subjectSchemes/lctgm       Thesaurus for graphic materials: TGM I, Subject terms   
5600 muso    http://id.loc.gov/vocabulary/subjectSchemes/muso        MUSO - Ontologi för musik    "fi"=>"MUSO - Ontologi för musik"
5601 blcpss  http://id.loc.gov/vocabulary/subjectSchemes/blcpss      COMPASS subject authority system        
5602 fast    http://id.loc.gov/vocabulary/subjectSchemes/fast        Faceted application of subject terminology      
5603 bisacmt http://id.loc.gov/vocabulary/subjectSchemes/bisacmt     BISAC Merchandising Themes      
5604 lapponica       http://id.loc.gov/vocabulary/subjectSchemes/lapponica   Lapponica       "fi"=>"Lapponica"
5605 juho    http://id.loc.gov/vocabulary/subjectSchemes/juho        JUHO - Julkishallinnon ontologia        "fi"=>"JUHO - Julkishallinnon ontologia"
5606 idas    http://id.loc.gov/vocabulary/subjectSchemes/idas        ID-Archivschlüssel   "de"=>"ID-Archivschlüssel"
5607 tbjvp   http://id.loc.gov/vocabulary/subjectSchemes/tbjvp       Tesauro de la Biblioteca Dr. Jorge Villalobos Padilla, S.J.     "es"=>"Tesauro de la Biblioteca Dr. Jorge Villalobos Padilla, S.J."
5608 test    http://id.loc.gov/vocabulary/subjectSchemes/test        Thesaurus of engineering and scientific terms   
5609 finmesh http://id.loc.gov/vocabulary/subjectSchemes/finmesh     FinMeSH "fi"=>"FinMeSH"
5610 kssbar  http://id.loc.gov/vocabulary/subjectSchemes/kssbar      Klassifikationssystem for svenska bibliotek. Ã\84mnesordregister. Alfabetisk del        "sv"=>"Klassifikationssystem for svenska bibliotek. Ã\84mnesordregister. Alfabetisk del"
5611 kupu    http://id.loc.gov/vocabulary/subjectSchemes/kupu        Maori Wordnet   "mi"=>"He puna kupu"
5612 rpe     http://id.loc.gov/vocabulary/subjectSchemes/rpe Rubricator on economics "ru"=>"Rubrikator po ekonomike"
5613 dit     http://id.loc.gov/vocabulary/subjectSchemes/dit Defense intelligence thesaurus  
5614 she     http://id.loc.gov/vocabulary/subjectSchemes/she SHE: subject headings for engineering   
5615 idszbzna        http://id.loc.gov/vocabulary/subjectSchemes/idszbzna    Thesaurus IDS Nebis Zentralbibliothek Zürich, Nordamerika-Bibliothek "de"=>"Thesaurus IDS Nebis Zentralbibliothek Zürich, Nordamerika-Bibliothek"
5616 msc     http://id.loc.gov/vocabulary/subjectSchemes/msc Mathematical subject classification     
5617 muzeukn http://id.loc.gov/vocabulary/subjectSchemes/muzeukn     MuzeMusic UK non-classical music classification 
5618 ipsp    http://id.loc.gov/vocabulary/subjectSchemes/ipsp        Defense intelligence production schedule.       
5619 sthus   http://id.loc.gov/vocabulary/subjectSchemes/sthus       Subject Taxonomy of the History of U.S. Foreign Relations       
5620 poliscit        http://id.loc.gov/vocabulary/subjectSchemes/poliscit    Political science thesaurus II  
5621 qtglit  http://id.loc.gov/vocabulary/subjectSchemes/qtglit      A queer thesaurus : an international thesaurus of gay and lesbian index terms   
5622 unbist  http://id.loc.gov/vocabulary/subjectSchemes/unbist      UNBIS thesaurus 
5623 gcipplatform    http://id.loc.gov/vocabulary/subjectSchemes/gcipplatform        GAMECIP - Computer Game Platforms (GAMECIP (Game Metadata and Citation Project))        
5624 puho    http://id.loc.gov/vocabulary/subjectSchemes/puho        PUHO - Puolustushallinnon ontologia     "fi"=>"PUHO - Puolustushallinnon ontologia"
5625 thub    http://id.loc.gov/vocabulary/subjectSchemes/thub        Thesaurus de la Universitat de Barcelona        "ca"=>"Thesaurus de la Universitat de Barcelona"
5626 ndlsh   http://id.loc.gov/vocabulary/subjectSchemes/ndlsh       National Diet Library list of subject headings  "ja"=>"Koktsu Kokkai Toshokan kenmei hyômokuhyô"
5627 czenas  http://id.loc.gov/vocabulary/subjectSchemes/czenas      CZENAS thesaurus: a list of subject terms used in the National Library of the Czech Republic    "cs"=>"Soubor vÄ\95cných autorit Národní knihovny Ä\8cR"
5628 idszbzzh        http://id.loc.gov/vocabulary/subjectSchemes/idszbzzh    Thesaurus IDS Nebis Zentralbibliothek Zürich, Handschriftenabteilung "de"=>"Thesaurus IDS Nebis Zentralbibliothek Zürich, Handschriftenabteilung"
5629 unbisn  http://id.loc.gov/vocabulary/subjectSchemes/unbisn      UNBIS name authority list (New York, NY: Dag Hammarskjld Library, United Nations; : Chadwyck-Healey)    
5630 rswk    http://id.loc.gov/vocabulary/subjectSchemes/rswk        Regeln für den Schlagwortkatalog     "de"=>"Regeln für den Schlagwortkatalog"
5631 larpcal http://id.loc.gov/vocabulary/subjectSchemes/larpcal     Lista de assuntos referente ao programa de cadastramento automatizado de livros da USP  "pt"=>"Lista de assuntos referente ao programa de cadastramento automatizado de livros da USP"
5632 biccbmc http://id.loc.gov/vocabulary/subjectSchemes/biccbmc     BIC Children's Books Marketing Classifications  
5633 kulo    http://id.loc.gov/vocabulary/subjectSchemes/kulo        KULO - Kulttuurien tutkimuksen ontologia        "fi"=>"KULO - Kulttuurien tutkimuksen ontologia"
5634 popinte http://id.loc.gov/vocabulary/subjectSchemes/popinte     POPIN thesaurus: population multilingual thesaurus      
5635 tisa    http://id.loc.gov/vocabulary/subjectSchemes/tisa        Villagrá Rubio, Angel. Tesauro ISOC de sociología autores "es"=>"Villagrá Rubio, Angel. Tesauro ISOC de sociología autores"
5636 atg     http://id.loc.gov/vocabulary/subjectSchemes/atg Agricultural thesaurus and glossary     
5637 eflch   http://id.loc.gov/vocabulary/subjectSchemes/eflch       E4Libraries Category Headings   
5638 maaq    http://id.loc.gov/vocabulary/subjectSchemes/maaq        Madâkhil al-asmâ' al-'arabîyah al-qadîmah   "ar"=>"Madâkhil al-asmâ' al-'arabîyah al-qadîmah"
5639 rvmgd   http://id.loc.gov/vocabulary/subjectSchemes/rvmgd       Thésaurus des descripteurs de groupes démographiques de l'Université Laval     "fr"=>"Thésaurus des descripteurs de groupes démographiques de l'Université Laval"
5640 csahssa http://id.loc.gov/vocabulary/subjectSchemes/csahssa     "Controlled vocabulary" in Health and safety science abstracts  
5641 sigle   http://id.loc.gov/vocabulary/subjectSchemes/sigle       SIGLE manual, Part 2, Subject category list     
5642 blnpn   http://id.loc.gov/vocabulary/subjectSchemes/blnpn       British Library newspaper place names   
5643 asrctoa http://id.loc.gov/vocabulary/subjectSchemes/asrctoa     Australian Standard Research Classification: Type of Activity (TOA) classification      
5644 lcdgt   http://id.loc.gov/vocabulary/subjectSchemes/lcdgt       Library of Congress demographic group term and code List        
5645 bokbas  http://id.loc.gov/vocabulary/subjectSchemes/bokbas      Bokbasen        "no"=>"Bokbasen"
5646 gnis    http://id.loc.gov/vocabulary/subjectSchemes/gnis        Geographic Names Information System (GNIS)      
5647 nbiemnfag       http://id.loc.gov/vocabulary/subjectSchemes/nbiemnfag   NBIs emneordsliste for faglitteratur    "no"=>"NBIs emneordsliste for faglitteratur"
5648 nlgaf   http://id.loc.gov/vocabulary/subjectSchemes/nlgaf       Archeio KathierÅ\8dmenÅ\8dn EpikephalidÅ\8dn    "el"=>"Archeio KathierÅ\8dmenÅ\8dn EpikephalidÅ\8dn"
5649 bhashe  http://id.loc.gov/vocabulary/subjectSchemes/bhashe      BHA, Bibliography of the history of art, subject headings/English       
5650 tsht    http://id.loc.gov/vocabulary/subjectSchemes/tsht        Thesaurus of subject headings for television    
5651 scbi    http://id.loc.gov/vocabulary/subjectSchemes/scbi        Soggettario per i cataloghi delle biblioteche italiane  "it"=>"Soggettario per i cataloghi delle biblioteche italiane"
5652 valo    http://id.loc.gov/vocabulary/subjectSchemes/valo        VALO - Fotografiska ontologin   "fi"=>"VALO - Fotografiska ontologin"
5653 wpicsh  http://id.loc.gov/vocabulary/subjectSchemes/wpicsh      WPIC Library thesaurus of subject headings      
5654 aktp    http://id.loc.gov/vocabulary/subjectSchemes/aktp        AlphavÄ\93tikos Katalogos ThematikÅ\8dn PerigrapheÅ\8dn "el"=>"AlphavÄ\93tikos Katalogos ThematikÅ\8dn PerigrapheÅ\8dn"
5655 stw     http://id.loc.gov/vocabulary/subjectSchemes/stw STW Thesaurus for Economics     "de"=>"Standard-Thesaurus Wirtschaft"
5656 mesh    http://id.loc.gov/vocabulary/subjectSchemes/mesh        Medical subject headings        
5657 ica     http://id.loc.gov/vocabulary/subjectSchemes/ica Index of Christian art  
5658 emnmus  http://id.loc.gov/vocabulary/subjectSchemes/emnmus      Emneord for musikkdokument i EDB-kataloger      "no"=>"Emneord for musikkdokument i EDB-kataloger"
5659 sao     http://id.loc.gov/vocabulary/subjectSchemes/sao Svenska Ã¤mnesord     "sv"=>"Svenska Ã¤mnesord"
5660 sgc     http://id.loc.gov/vocabulary/subjectSchemes/sgc COBISS.SI General List of subject headings (Slovenian subject headings) "sl"=>"SploÅ¡ni geslovnik COBISS.SI"
5661 bib1814 http://id.loc.gov/vocabulary/subjectSchemes/bib1814     1814-bibliografi: emneord for 1814-bibliografi  "no"=>"1814-bibliografi: emneord for 1814-bibliografi"
5662 bjornson        http://id.loc.gov/vocabulary/subjectSchemes/bjornson    Bjornson: emneord for Bjornsonbibliografien     "no"=>"Bjornson: emneord for Bjornsonbibliografien"
5663 liito   http://id.loc.gov/vocabulary/subjectSchemes/liito       LIITO - Liiketoimintaontologia  "fi"=>"LIITO - Liiketoimintaontologia"
5664 apaist  http://id.loc.gov/vocabulary/subjectSchemes/apaist      APAIS thesaurus: a list of subject terms used in the Australian Public Affairs Information Service      
5665 itglit  http://id.loc.gov/vocabulary/subjectSchemes/itglit      International thesaurus of gay and lesbian index terms (Chicago?: Thesaurus Committee, Gay and Lesbian Task Force, American Library Association)        
5666 ntcsd   http://id.loc.gov/vocabulary/subjectSchemes/ntcsd       "National Translations Center secondary descriptors" in National Translation Center primary subject classification and secondary descriptor     
5667 scisshl http://id.loc.gov/vocabulary/subjectSchemes/scisshl     SCIS subject headings   
5668 opms    http://id.loc.gov/vocabulary/subjectSchemes/opms        Opetusministeriön asiasanasto        "fi"=>"Opetusministeriön asiasanasto"
5669 ttka    http://id.loc.gov/vocabulary/subjectSchemes/ttka        Teologisen tiedekunnan kirjaston asiasanasto    "fi"=>"Teologisen tiedekunnan kirjaston asiasanasto"
5670 watrest http://id.loc.gov/vocabulary/subjectSchemes/watrest     Thesaurus of water resources terms: a collection of water resources and related terms for use in indexing technical information 
5671 ysa     http://id.loc.gov/vocabulary/subjectSchemes/ysa Yleinen suomalainen asiasanasto "fi"=>"Yleinen suomalainen asiasanasto"
5672 kitu    http://id.loc.gov/vocabulary/subjectSchemes/kitu        Kirjallisuudentutkimuksen asiasanasto   "fi"=>"Kirjallisuudentutkimuksen asiasanasto"
5673 sk      http://id.loc.gov/vocabulary/subjectSchemes/sk  'Zhong guo gu ji shan ban shu zong mu' fen lei biao     "zh"=>"'Zhong guo gu ji shan ban shu zong mu' fen lei biao"
5674 aiatsisp        http://id.loc.gov/vocabulary/subjectSchemes/aiatsisp    AIATSIS place thesaurus 
5675 ram     http://id.loc.gov/vocabulary/subjectSchemes/ram RAMEAU: répertoire d'authorité de matières encyclopédique unifié "fr"=>"RAMEAU: répertoire d'authorité de matières encyclopédique unifié"
5676 aedoml  http://id.loc.gov/vocabulary/subjectSchemes/aedoml      Listado de encabezamientos de materia de música      "es"=>"Listado de encabezamientos de materia de música"
5677 ated    http://id.loc.gov/vocabulary/subjectSchemes/ated        Australian Thesaurus of Education Descriptors (ATED)    
5678 cabt    http://id.loc.gov/vocabulary/subjectSchemes/cabt        CAB thesaurus (Slough [England]: Commonwealth Agricultural Bureaux)     
5679 kassu   http://id.loc.gov/vocabulary/subjectSchemes/kassu       Kassu - Kasvien suomenkieliset nimet    "fi"=>"Kassu - Kasvien suomenkieliset nimet"
5680 nbdbt   http://id.loc.gov/vocabulary/subjectSchemes/nbdbt       NBD Biblion Trefwoordenthesaurus        "nl"=>"NBD Biblion Trefwoordenthesaurus"
5681 jhpb    http://id.loc.gov/vocabulary/subjectSchemes/jhpb        JÄ\99zyk haseÅ\82 przedmiotowych Biblioteki Narodowej   "pl"=>"JÄ\99zyk haseÅ\82 przedmiotowych Biblioteki Narodowej"
5682 bidex   http://id.loc.gov/vocabulary/subjectSchemes/bidex       Bilindex: a bilingual Spanish-English subject heading list      
5683 ccsa    http://id.loc.gov/vocabulary/subjectSchemes/ccsa        Catalogue collectif suisse des affiches "fr"=>"Catalogue collectif suisse des affiches"
5684 noraf   http://id.loc.gov/vocabulary/subjectSchemes/noraf       Norwegian Authority File        
5685 kito    http://id.loc.gov/vocabulary/subjectSchemes/kito        KITO - Kirjallisuudentutkimuksen ontologia      "fi"=>"KITO - Kirjallisuudentutkimuksen ontologia"
5686 tho     http://id.loc.gov/vocabulary/subjectSchemes/tho Thesauros HellÄ\93nikÅ\8dn Oron "el"=>"Thesauros HellÄ\93nikÅ\8dn Oron"
5687 pmont   http://id.loc.gov/vocabulary/subjectSchemes/pmont       Powerhouse Museum Object Name Thesaurus 
5688 ssg     http://id.loc.gov/vocabulary/subjectSchemes/ssg SploÅ¡ni slovenski geslovnik  "sl"=>"SploÅ¡ni slovenski geslovnik"
5689 huc     http://id.loc.gov/vocabulary/subjectSchemes/huc U.S. Geological Survey water-supply paper 2294: hydrologic basins unit codes    
5690 isis    http://id.loc.gov/vocabulary/subjectSchemes/isis        "Classification scheme" in Isis 
5691 ibsen   http://id.loc.gov/vocabulary/subjectSchemes/ibsen       Ibsen: emneord for Den internasjonale Ibsen-bibliografien       "no"=>"Ibsen: emneord for Den internasjonale Ibsen-bibliografien"
5692 lacnaf  http://id.loc.gov/vocabulary/subjectSchemes/lacnaf      Library and Archives Canada name authority file 
5693 swemesh http://id.loc.gov/vocabulary/subjectSchemes/swemesh     Swedish MeSH    "sv"=>"Svenska MeSH"
5694 hamsun  http://id.loc.gov/vocabulary/subjectSchemes/hamsun      Hamsun: emneord for Hamsunbibliografien "no"=>"Hamsun: emneord for Hamsunbibliografien"
5695 qrma    http://id.loc.gov/vocabulary/subjectSchemes/qrma        List of Arabic subject headings "ar"=>"Qâ'imat ru'ûs al-mawdûât al-'Arabîyah"
5696 qrmak   http://id.loc.gov/vocabulary/subjectSchemes/qrmak       Qâ'imat ru'ûs al-mawdû'ât al-'Arabîyah al-qiyâsîyah al-maktabât wa-marâkaz al-ma'lûmât wa-qawâid al-bayânât   "ar"=>"Qâ'imat ru'ûs al-mawdû'ât al-'Arabîyah al-qiyâsîyah al-maktabât wa-marâkaz al-ma'lûmât wa-qawâid al-bayânât"
5697 ceeus   http://id.loc.gov/vocabulary/subjectSchemes/ceeus       Counties and equivalent entities of the United States its possessions, and associated areas     
5698 taxhs   http://id.loc.gov/vocabulary/subjectSchemes/taxhs       A taxonomy or human services: a conceptual framework with standardized terminology and definitions for the field        
5699 noram   http://id.loc.gov/vocabulary/subjectSchemes/noram       Noram: emneord for Norsk-amerikansk samling     "no"=>"Noram: emneord for Norsk-amerikansk samling"
5700 eurovocfr       http://id.loc.gov/vocabulary/subjectSchemes/eurovocfr   Eurovoc thesaurus (French)      
5701 jurivoc http://id.loc.gov/vocabulary/subjectSchemes/jurivoc     JURIVOC 
5702 agrifors        http://id.loc.gov/vocabulary/subjectSchemes/agrifors    AGRIFOREST-sanasto      "fi"=>"AGRIFOREST-sanasto"
5703 noubojur        http://id.loc.gov/vocabulary/subjectSchemes/noubojur    Thesaurus of Law        "no"=>"Thesaurus of Law"
5704 pha     http://id.loc.gov/vocabulary/subjectSchemes/pha Puolostushallinnon asiasanasto  "fi"=>"Puolostushallinnon asiasanasto"
5705 ddcrit  http://id.loc.gov/vocabulary/subjectSchemes/ddcrit      DDC retrieval and indexing terminology; posting terms with hierarchy and KWOC   
5706 mar     http://id.loc.gov/vocabulary/subjectSchemes/mar Merenkulun asiasanasto  "fi"=>"Merenkulun asiasanasto"
5707 sbt     http://id.loc.gov/vocabulary/subjectSchemes/sbt Soggettario Sistema Bibliotecario Ticinese      "it"=>"Soggettario Sistema Bibliotecario Ticinese"
5708 nzggn   http://id.loc.gov/vocabulary/subjectSchemes/nzggn       New Zealand gazetteer of official geographic names (New Zealand Geographic Board Ngā Pou Taunaha o Aotearoa (NZGB))    
5709 kta     http://id.loc.gov/vocabulary/subjectSchemes/kta Kielitieteen asiasanasto        "fi"=>"Kielitieteen asiasanasto"
5710 snt     http://id.loc.gov/vocabulary/subjectSchemes/snt Sexual nomenclature : a thesaurus       
5711 francis http://id.loc.gov/vocabulary/subjectSchemes/francis     FRANCIS database classification scheme  "fr"=>"Base de donneés FRANCIS: plan de classement"
5712 eurovocsl       http://id.loc.gov/vocabulary/subjectSchemes/eurovocsl   Eurovoc thesaurus       "sl"=>"Eurovoc thesaurus"
5713 idszbzes        http://id.loc.gov/vocabulary/subjectSchemes/idszbzes    Thesaurus IDS Nebis Bibliothek Englisches Seminar der Universität Zürich  "de"=>"Thesaurus IDS Nebis Bibliothek Englisches Seminar der Universität Zürich"
5714 nlmnaf  http://id.loc.gov/vocabulary/subjectSchemes/nlmnaf      National Library of Medicine name authority file        
5715 rugeo   http://id.loc.gov/vocabulary/subjectSchemes/rugeo       Natsional'nyi normativnyi fail geograficheskikh nazvanii Rossiiskoi Federatsii  "ru"=>"Natsional'nyi normativnyi fail geograficheskikh nazvanii Rossiiskoi Federatsii"
5716 sipri   http://id.loc.gov/vocabulary/subjectSchemes/sipri       SIPRI library thesaurus 
5717 kkts    http://id.loc.gov/vocabulary/subjectSchemes/kkts        Katalogos KathierÅ\8dmenÅ\8dn TypÅ\8dn Syllogikou Katalogou Demosion Vivliothekon       "el"=>"Katalogos KathierÅ\8dmenÅ\8dn TypÅ\8dn Syllogikou Katalogou Demosion Vivliothekon"
5718 tucua   http://id.loc.gov/vocabulary/subjectSchemes/tucua       Thesaurus for use in college and university archives    
5719 pmbok   http://id.loc.gov/vocabulary/subjectSchemes/pmbok       Guide to the project management body of knowledge (PMBOK Guide) 
5720 agrovoc http://id.loc.gov/vocabulary/subjectSchemes/agrovoc     AGROVOC multilingual agricultural thesaurus     
5721 nal     http://id.loc.gov/vocabulary/subjectSchemes/nal National Agricultural Library subject headings  
5722 lnmmbr  http://id.loc.gov/vocabulary/subjectSchemes/lnmmbr      Lietuvos nacionalines Martyno Mazvydo bibliotekos rubrikynas    "lt"=>"Lietuvos nacionalines Martyno Mazvydo bibliotekos rubrikynas"
5723 vmj     http://id.loc.gov/vocabulary/subjectSchemes/vmj Vedettes-matière jeunesse    "fr"=>"Vedettes-matière jeunesse"
5724 ddcut   http://id.loc.gov/vocabulary/subjectSchemes/ddcut       Dewey Decimal Classification user terms 
5725 eks     http://id.loc.gov/vocabulary/subjectSchemes/eks Eduskunnan kirjaston asiasanasto        "fi"=>"Eduskunnan kirjaston asiasanasto"
5726 wot     http://id.loc.gov/vocabulary/subjectSchemes/wot A Women's thesaurus     
5727 noubomn http://id.loc.gov/vocabulary/subjectSchemes/noubomn     University of Oslo Library Thesaurus of Science "no"=>"University of Oslo Library Thesaurus of Science"
5728 idszbzzg        http://id.loc.gov/vocabulary/subjectSchemes/idszbzzg    Thesaurus IDS Nebis Zentralbibliothek Zürich, Graphische Sammlung    "de"=>"Thesaurus IDS Nebis Zentralbibliothek Zürich, Graphische Sammlung"
5729 precis  http://id.loc.gov/vocabulary/subjectSchemes/precis      PRECIS: a manual of concept analysis and subject indexing       
5730 cstud   http://id.loc.gov/vocabulary/subjectSchemes/cstud       Classificatieschema's Bibliotheek TU Delft      "nl"=>"Classificatieschema's Bibliotheek TU Delft"
5731 nlgkk   http://id.loc.gov/vocabulary/subjectSchemes/nlgkk       Katalogos kathierÅ\8dmenÅ\8dn onomatÅ\8dn physikÅ\8dn prosÅ\8d\8dn    "el"=>"Katalogos kathierÅ\8dmenÅ\8dn onomatÅ\8dn physikÅ\8dn prosÅ\8d\8dn"
5732 pmt     http://id.loc.gov/vocabulary/subjectSchemes/pmt Project management terminology. Newtown Square, PA: Project Management Institute        
5733 ericd   http://id.loc.gov/vocabulary/subjectSchemes/ericd       Thesaurus of ERIC descriptors   
5734 rvm     http://id.loc.gov/vocabulary/subjectSchemes/rvm Répertoire de vedettes-matière    "fr"=>"Répertoire de vedettes-matière"
5735 sfit    http://id.loc.gov/vocabulary/subjectSchemes/sfit        Svenska filminstitutets tesaurus        "sv"=>"Svenska filminstitutets tesaurus"
5736 trtsa   http://id.loc.gov/vocabulary/subjectSchemes/trtsa       Teatterin ja tanssin asiasanasto        "fi"=>"Teatterin ja tanssin asiasanasto"
5737 ulan    http://id.loc.gov/vocabulary/subjectSchemes/ulan        Union list of artist names      
5738 unescot http://id.loc.gov/vocabulary/subjectSchemes/unescot     UNESCO thesaurus        "fr"=>"Thésaurus de l'UNESCO","es"=>"Tesauro de la UNESCO"
5739 koko    http://id.loc.gov/vocabulary/subjectSchemes/koko        KOKO-ontologia  "fi"=>"KOKO-ontologia"
5740 msh     http://id.loc.gov/vocabulary/subjectSchemes/msh Trimboli, T., and Martyn S. Marianist subject headings  
5741 trt     http://id.loc.gov/vocabulary/subjectSchemes/trt Transportation resource thesaurus       
5742 agrovocf        http://id.loc.gov/vocabulary/subjectSchemes/agrovocf    AGROVOC thésaurus agricole multilingue       "fr"=>"AGROVOC thésaurus agricole multilingue"
5743 aucsh   http://id.loc.gov/vocabulary/subjectSchemes/aucsh       Arabic Union Catalog Subject Headings   "ar"=>"Qâ'imat ru'ûs mawdû'ât al-fahras al-'Arabîyah al-mowahad"
5744 ddcri   http://id.loc.gov/vocabulary/subjectSchemes/ddcri       Dewey Decimal Classification Relative Index     
5745 est     http://id.loc.gov/vocabulary/subjectSchemes/est International energy: subject thesaurus (: International Energy Agency, Energy Technology Data Exchange)        
5746 lua     http://id.loc.gov/vocabulary/subjectSchemes/lua Liikunnan ja urheilun asiasanasto       "fi"=>"Liikunnan ja urheilun asiasanasto"
5747 mipfesd http://id.loc.gov/vocabulary/subjectSchemes/mipfesd     Macrothesaurus for information processing in the field of economic and social development       
5748 rurkp   http://id.loc.gov/vocabulary/subjectSchemes/rurkp       Predmetnye rubriki Rossiiskoi knizhnoi palaty   "ru"=>"Predmetnye rubriki Rossiiskoi knizhnoi palaty"
5749 albt    http://id.loc.gov/vocabulary/subjectSchemes/albt        Arbetslivsbibliotekets tesaurus "sv"=>"Arbetslivsbibliotekets tesaurus"
5750 fmesh   http://id.loc.gov/vocabulary/subjectSchemes/fmesh       Liste systématique et liste permutée des descripteurs français MeSH    "fr"=>"Liste systématique et liste permutée des descripteurs français MeSH"
5751 bicssc  http://id.loc.gov/vocabulary/subjectSchemes/bicssc      BIC standard subject categories 
5752 cctf    http://id.loc.gov/vocabulary/subjectSchemes/cctf        Carto-Canadiana thésaurus - Français      "fr"=>"Carto-Canadiana thésaurus - Français"
5753 reo     http://id.loc.gov/vocabulary/subjectSchemes/reo Māori Subject Headings thesaurus       "mi"=>"Ngā Åªpoko Tukutuku"
5754 icpsr   http://id.loc.gov/vocabulary/subjectSchemes/icpsr       ICPSR controlled vocabulary system      
5755 kao     http://id.loc.gov/vocabulary/subjectSchemes/kao KVINNSAM Ã¤mnesordsregister   "sv"=>"KVINNSAM Ã¤mnesordsregister"
5756 asrcseo http://id.loc.gov/vocabulary/subjectSchemes/asrcseo     Australian Standard Research Classification: Socio-Economic Objective (SEO) classification      
5757 georeft http://id.loc.gov/vocabulary/subjectSchemes/georeft     GeoRef thesaurus        
5758 cct     http://id.loc.gov/vocabulary/subjectSchemes/cct Chinese Classified Thesaurus    "zh"=>"Zhong guo fen lei zhu ti ci biao"
5759 dcs     http://id.loc.gov/vocabulary/subjectSchemes/dcs Health Sciences Descriptors     "es"=>"Descriptores en Ciencias de la Salud","pt"=>"Descritores em Ciências da Saúde"
5760 musa    http://id.loc.gov/vocabulary/subjectSchemes/musa        Musiikin asiasanasto: erikoissanasto    "fi"=>"Musiikin asiasanasto: erikoissanasto"
5761 ntissc  http://id.loc.gov/vocabulary/subjectSchemes/ntissc      NTIS subject categories 
5762 idszbz  http://id.loc.gov/vocabulary/subjectSchemes/idszbz      Thesaurus IDS Nebis Zentralbibliothek Zürich "de"=>"Thesaurus IDS Nebis Zentralbibliothek Zürich"
5763 tlka    http://id.loc.gov/vocabulary/subjectSchemes/tlka        Investigació, Procés Tècnicn kirjaston asiasanasto     "fi"=>"Investigació, Procés Tècnicn kirjaston asiasanasto"
5764 usaidt  http://id.loc.gov/vocabulary/subjectSchemes/usaidt      USAID thesaurus: Keywords used to index documents included in the USAID Development Experience System.  
5765 embne   http://id.loc.gov/vocabulary/subjectSchemes/embne       Encabezamientos de Materia de la Biblioteca Nacional de España       "es"=>"Encabezamientos de Materia de la Biblioteca Nacional de España"
5766 vcaadu  http://id.loc.gov/vocabulary/subjectSchemes/vcaadu      Vocabulario controlado de arquitectura, arte, diseño y urbanismo     "es"=>"Vocabulario controlado de arquitectura, arte, diseño y urbanismo"
5767 ntcpsc  http://id.loc.gov/vocabulary/subjectSchemes/ntcpsc      "National Translations Center primary subject classification" in National Translations Center primary subject classification and secondary descriptors  
5768 quiding http://id.loc.gov/vocabulary/subjectSchemes/quiding     Quiding, Nils Herman. Svenskt allmänt författningsregister för tiden frÃ¥n Ã¥r 1522 till och med Ã¥r 1862        "sv"=>"Quiding, Nils Herman. Svenskt allmänt författningsregister för tiden frÃ¥n Ã¥r 1522 till och med Ã¥r 1862"
5769 allars  http://id.loc.gov/vocabulary/subjectSchemes/allars      Allärs: allmän tesaurus pä svenska     "fi"=>"Allärs: allmän tesaurus pä svenska"
5770 ogst    http://id.loc.gov/vocabulary/subjectSchemes/ogst        Oregon GILS Subject Tree (Oregon: Oregon State Library and Oregon Information Resource Management Division (IRMD))      
5771 bella   http://id.loc.gov/vocabulary/subjectSchemes/bella       Bella: specialtesaurus för skönlitteratur "fi"=>"Bella: specialtesaurus för skönlitteratur"
5772 bibalex http://id.loc.gov/vocabulary/subjectSchemes/bibalex     Bibliotheca Alexandrina name and subject authority file 
5773 pepp    http://id.loc.gov/vocabulary/subjectSchemes/pepp        The Princeton encyclopedia of poetry and poetics        
5774 hkcan   http://id.loc.gov/vocabulary/subjectSchemes/hkcan       Hong Kong Chinese Authority File (Name) - HKCAN 
5775 dissao  http://id.loc.gov/vocabulary/subjectSchemes/dissao      "Dissertation abstracts online" in Search tools: the guide to UNI/Data Courier Online   
5776 ltcsh   http://id.loc.gov/vocabulary/subjectSchemes/ltcsh       Land Tenure Center Library list of subject headings     
5777 mpirdes http://id.loc.gov/vocabulary/subjectSchemes/mpirdes     Macrothesaurus para el procesamiento de la información relativa al desarrollo económico y social  "es"=>"Macrothesaurus para el procesamiento de la información relativa al desarrollo económico y social"
5778 asft    http://id.loc.gov/vocabulary/subjectSchemes/asft        Aquatic sciences and fisheries thesaurus        
5779 naf     http://id.loc.gov/vocabulary/subjectSchemes/naf NACO authority file     
5780 nimacsc http://id.loc.gov/vocabulary/subjectSchemes/nimacsc     NIMA cartographic subject categories    
5781 khib    http://id.loc.gov/vocabulary/subjectSchemes/khib        Emneord, KHiB Biblioteket       "no"=>"Emneord, KHiB Biblioteket"
5782 cdcng   http://id.loc.gov/vocabulary/subjectSchemes/cdcng       Catalogage des documents cartographiques: forme et structure des vedettes noms géographiques - NF Z 44-081   "fr"=>"Catalogage des documents cartographiques: forme et structure des vedettes noms géographiques - NF Z 44-081"
5783 afset   http://id.loc.gov/vocabulary/subjectSchemes/afset       American Folklore Society Ethnographic Thesaurus        
5784 erfemn  http://id.loc.gov/vocabulary/subjectSchemes/erfemn      Erfaringskompetanses emneord    "no"=>"Erfaringskompetanses emneord"
5785 sbiao   http://id.loc.gov/vocabulary/subjectSchemes/sbiao       Svenska barnboksinstitutets Ã¤mnesordslista   "sv"=>"Svenska barnboksinstitutets Ã¤mnesordslista"
5786 socio   http://id.loc.gov/vocabulary/subjectSchemes/socio       Sociological Abstracts Thesaurus        
5787 bisacrt http://id.loc.gov/vocabulary/subjectSchemes/bisacrt     BISAC Regional Themes   
5788 eum     http://id.loc.gov/vocabulary/subjectSchemes/eum Eesti uldine märksonastik    "et"=>"Eesti uldine märksonastik"
5789 kula    http://id.loc.gov/vocabulary/subjectSchemes/kula        Kulttuurien tutkimuksen asiasanasto     "fi"=>"Kulttuurien tutkimuksen asiasanasto"
5790 odlt    http://id.loc.gov/vocabulary/subjectSchemes/odlt        Baldick, C. The Oxford dictionary of literary terms     
5791 rerovoc http://id.loc.gov/vocabulary/subjectSchemes/rerovoc     Indexation matiéres RERO autoritès        "fr"=>"Indexation matiéres RERO autoritès"
5792 tsr     http://id.loc.gov/vocabulary/subjectSchemes/tsr TSR-ontologia   "fi"=>"TSR-ontologia"
5793 czmesh  http://id.loc.gov/vocabulary/subjectSchemes/czmesh      Czech MeSH      "cs"=>"Czech MeSH"
5794 dltt    http://id.loc.gov/vocabulary/subjectSchemes/dltt        Quinn, E. A dictionary of literary and thematic terms   
5795 idsbb   http://id.loc.gov/vocabulary/subjectSchemes/idsbb       Thesaurus IDS Basel Bern        "de"=>"Thesaurus IDS Basel Bern"
5796 inist   http://id.loc.gov/vocabulary/subjectSchemes/inist       INIS: thesaurus 
5797 idszbzzk        http://id.loc.gov/vocabulary/subjectSchemes/idszbzzk    Thesaurus IDS Nebis Zentralbibliothek Zürich, Kartensammlung "de"=>"Thesaurus IDS Nebis Zentralbibliothek Zürich, Kartensammlung"
5798 tesa    http://id.loc.gov/vocabulary/subjectSchemes/tesa        Tesauro Agrícola     "es"=>"Tesauro Agrícola"
5799 liv     http://id.loc.gov/vocabulary/subjectSchemes/liv Legislative indexing vocabulary 
5800 collett http://id.loc.gov/vocabulary/subjectSchemes/collett     Collett-bibliografi: litteratur av og om Camilla Collett        "no"=>"Collett-bibliografi: litteratur av og om Camilla Collett"
5801 nsbncf  http://id.loc.gov/vocabulary/subjectSchemes/nsbncf      Nuovo Soggettario       "it"=>"Nuovo Soggettario"
5802 ipat    http://id.loc.gov/vocabulary/subjectSchemes/ipat        IPA thesaurus and frequency list        
5803 skon    http://id.loc.gov/vocabulary/subjectSchemes/skon        Att indexera skönlitteratur: Ã\84mnesordslista, vuxenlitteratur      "sv"=>"Att indexera skönlitteratur: Ã\84mnesordslista, vuxenlitteratur"
5804 renib   http://id.loc.gov/vocabulary/subjectSchemes/renib       Renib   "es"=>"Renib"
5805 hrvmesh http://id.loc.gov/vocabulary/subjectSchemes/hrvmesh     Croatian MeSH / Hrvatski MeSH   "no"=>"Croatian MeSH / Hrvatski MeSH"
5806 swd     http://id.loc.gov/vocabulary/subjectSchemes/swd Schlagwortnormdatei     "de"=>"Schlagwortnormdatei"
5807 aass    http://id.loc.gov/vocabulary/subjectSchemes/aass        "Asian American Studies Library subject headings" in A Guide for establishing Asian American core collections   
5808 cht     http://id.loc.gov/vocabulary/subjectSchemes/cht Chicano thesaurus for indexing Chicano materials in Chicano periodical index    
5809 galestne        http://id.loc.gov/vocabulary/subjectSchemes/galestne    Gale Group subject thesaurus and named entity vocabulary        
5810 nlgsh   http://id.loc.gov/vocabulary/subjectSchemes/nlgsh       Katalogos HellÄ\93nikÅ\8dn thematikÅ\8dn epikephalidÅ\8dn       "el"=>"Katalogos HellÄ\93nikÅ\8dn thematikÅ\8dn epikephalidÅ\8dn"
5811 hoidokki        http://id.loc.gov/vocabulary/subjectSchemes/hoidokki    Hoitotieteellinen asiasanasto   
5812 vffyl   http://id.loc.gov/vocabulary/subjectSchemes/vffyl       Vocabulario de la Biblioteca Central de la FFyL "es"=>"Vocabulario de la Biblioteca Central de la FFyL"
5813 kubikat http://id.loc.gov/vocabulary/subjectSchemes/kubikat     kubikat "de"=>"kubikat"
5814 waqaf   http://id.loc.gov/vocabulary/subjectSchemes/waqaf       Maknas Uloom Al Waqaf   "ar"=>"Maknas Uloom Al Waqaf"
5815 hapi    http://id.loc.gov/vocabulary/subjectSchemes/hapi        HAPI thesaurus and name authority, 1970-2000    
5816 drama   http://id.loc.gov/vocabulary/subjectSchemes/drama       Drama: specialtesaurus för teater och dans   
5817 sosa    http://id.loc.gov/vocabulary/subjectSchemes/sosa        Sociaalialan asiasanasto        "fi"=>"Sociaalialan asiasanasto"
5818 ilpt    http://id.loc.gov/vocabulary/subjectSchemes/ilpt        Index to legal periodicals: thesaurus   
5819 nicem   http://id.loc.gov/vocabulary/subjectSchemes/nicem       NICEM subject headings and classification system        
5820 qlsp    http://id.loc.gov/vocabulary/subjectSchemes/qlsp        Queens Library Spanish language subject headings        
5821 eet     http://id.loc.gov/vocabulary/subjectSchemes/eet European education thesaurus    
5822 nalnaf  http://id.loc.gov/vocabulary/subjectSchemes/nalnaf      National Agricultural Library name authority file       
5823 eclas   http://id.loc.gov/vocabulary/subjectSchemes/eclas       ECLAS thesaurus 
5824 agrovocs        http://id.loc.gov/vocabulary/subjectSchemes/agrovocs    AGROVOC tesauro agrícola multilingée      "es"=>"AGROVOC tesauro agrícola multilingée"
5825 shbe    http://id.loc.gov/vocabulary/subjectSchemes/shbe        Subject headings in business and economics      "sv"=>"Subject headings in business and economics"
5826 barn    http://id.loc.gov/vocabulary/subjectSchemes/barn        Svenska Ã¤mnesord för barn "sv"=>"Svenska Ã¤mnesord för barn"
5827 bhammf  http://id.loc.gov/vocabulary/subjectSchemes/bhammf      BHA, Bibliographie d'histoire de l'art, mots-matière/français     "fr"=>"BHA, Bibliographie d'histoire de l'art, mots-matière/français"
5828 gccst   http://id.loc.gov/vocabulary/subjectSchemes/gccst       Government of Canada core subject thesaurus (Gatineau : Library and Archives Canada)    
5829 fnhl    http://id.loc.gov/vocabulary/subjectSchemes/fnhl        First Nations House of Learning Subject Headings        
5830 kauno   http://id.loc.gov/vocabulary/subjectSchemes/kauno       KAUNO - Kaunokki-ontologin      "fi"=>"KAUNO - Kaunokki-ontologin"
5831 dtict   http://id.loc.gov/vocabulary/subjectSchemes/dtict       Defense Technical Information Center thesaurus  
5832 mech    http://id.loc.gov/vocabulary/subjectSchemes/mech        Iskanje po zbirki MECH  "sl"=>"Iskanje po zbirki MECH"
5833 jupo    http://id.loc.gov/vocabulary/subjectSchemes/jupo        JUPO - Julkisen hallinnon palveluontologia      "fi"=>"JUPO - Julkisen hallinnon palveluontologia"
5834 ktpt    http://id.loc.gov/vocabulary/subjectSchemes/ktpt        Kirjasto- ja tietopalvelualan tesaurus  "fi"=>"Kirjasto- ja tietopalvelualan tesaurus"
5835 aiatsiss        http://id.loc.gov/vocabulary/subjectSchemes/aiatsiss    AIATSIS subject Thesaurus       
5836 lcac    http://id.loc.gov/vocabulary/subjectSchemes/lcac        Library of Congress Annotated Children's Cataloging Program subject headings    
5837 lemac   http://id.loc.gov/vocabulary/subjectSchemes/lemac       Llista d'encapçalaments de matèria en català   "ca"=>"Llista d'encapçalaments de matèria en català"
5838 lemb    http://id.loc.gov/vocabulary/subjectSchemes/lemb        Lista de encabezamientos de materia para bibliotecas    "es"=>"Lista de encabezamientos de materia para bibliotecas"
5839 henn    http://id.loc.gov/vocabulary/subjectSchemes/henn        Hennepin County Library cumulative authority list       
5840 mtirdes http://id.loc.gov/vocabulary/subjectSchemes/mtirdes     Macrothésaurus pour le traitement de l'information relative au développement Ã©conomique et social      "fr"=>"Macrothésaurus pour le traitement de l'information relative au développement Ã©conomique et social"
5841 cash    http://id.loc.gov/vocabulary/subjectSchemes/cash        Canadian subject headings       
5842 nznb    http://id.loc.gov/vocabulary/subjectSchemes/nznb        New Zealand national bibliographic      
5843 prvt    http://id.loc.gov/vocabulary/subjectSchemes/prvt        Patent- och registreringsverkets tesaurus       "sv"=>"Patent- och registreringsverkets tesaurus"
5844 scgdst  http://id.loc.gov/vocabulary/subjectSchemes/scgdst      Subject categorization guide for defense science and technology 
5845 gem     http://id.loc.gov/vocabulary/subjectSchemes/gem GEM controlled vocabularies     
5846 lcsh    http://id.loc.gov/vocabulary/subjectSchemes/lcsh        Library of Congress subject headings    
5847 rero    http://id.loc.gov/vocabulary/subjectSchemes/rero        Indexation matires RERO "fr"=>"Indexation matires RERO"
5848 peri    http://id.loc.gov/vocabulary/subjectSchemes/peri        Perinnetieteiden asiasanasto    "fi"=>"Perinnetieteiden asiasanasto"
5849 shsples http://id.loc.gov/vocabulary/subjectSchemes/shsples     Encabezamientos de materia para bibliotecas escolares y públicas     "es"=>"Encabezamientos de materia para bibliotecas escolares y públicas"
5850 slem    http://id.loc.gov/vocabulary/subjectSchemes/slem        Sears: lista de encabezamientos de materia      "es"=>"Sears: lista de encabezamientos de materia"
5851 afo     http://id.loc.gov/vocabulary/subjectSchemes/afo AFO - Viikin kampuskirjaston ontologia  "fi"=>"AFO - Viikin kampuskirjaston ontologia"
5852 gst     http://id.loc.gov/vocabulary/subjectSchemes/gst Gay studies thesaurus: a controlled vocabulary for indexing and accessing materials of relevance to gay culture, history, politics and psychology       
5853 hlasstg http://id.loc.gov/vocabulary/subjectSchemes/hlasstg     HLAS subject term glossary      
5854 iest    http://id.loc.gov/vocabulary/subjectSchemes/iest        International energy: subject thesaurus 
5855 pkk     http://id.loc.gov/vocabulary/subjectSchemes/pkk Predmetnik za katoliÅ¡ke knjižnice "sl"=>"Predmetnik za katoliÅ¡ke knjižnice"
5856 atla    http://id.loc.gov/vocabulary/subjectSchemes/atla        Religion indexes: thesaurus     
5857 scot    http://id.loc.gov/vocabulary/subjectSchemes/scot        Schools Online Thesaurus (ScOT) 
5858 smda    http://id.loc.gov/vocabulary/subjectSchemes/smda        Smithsonian National Air and Space Museum Directory of Airplanes        
5859 solstad http://id.loc.gov/vocabulary/subjectSchemes/solstad     Solstad: emneord for Solstadbibliografien       "no"=>"Solstad: emneord for Solstadbibliografien"
5860 abne    http://id.loc.gov/vocabulary/subjectSchemes/abne        Autoridades de la Biblioteca Nacional de España      "es"=>"Autoridades de la Biblioteca Nacional de España"
5861 spines  http://id.loc.gov/vocabulary/subjectSchemes/spines      Tesauro SPINES: un vocabulario controlado y estructurado para el tratamiento de información sobre ciencia y tecnología para el desarrollo "es"=>"Tesauro SPINES: un vocabulario controlado y estructurado para el tratamiento de información sobre ciencia y tecnología para el desarrollo"
5862 ktta    http://id.loc.gov/vocabulary/subjectSchemes/ktta        Käsi - ja taideteollisuuden asiasanasto      "fi"=>"Käsi - ja taideteollisuuden asiasanasto"
5863 ccte    http://id.loc.gov/vocabulary/subjectSchemes/ccte        Carto-Canadiana thesaurus - English     
5864 pmcsg   http://id.loc.gov/vocabulary/subjectSchemes/pmcsg       Combined standards glossary     
5865 bisacsh http://id.loc.gov/vocabulary/subjectSchemes/bisacsh     BISAC Subject Headings  
5866 fssh    http://id.loc.gov/vocabulary/subjectSchemes/fssh        FamilySearch Subject Headings (FamilySearch)    
5867 tasmas  http://id.loc.gov/vocabulary/subjectSchemes/tasmas      Tesaurus de Asuntos Sociales del Ministerio de Asuntos Sociales de España    "es"=>"Tesaurus de Asuntos Sociales del Ministerio de Asuntos Sociales de España"
5868 tero    http://id.loc.gov/vocabulary/subjectSchemes/tero        TERO - Terveyden ja hyvinvoinnin ontologia      "fi"=>"TERO - Terveyden ja hyvinvoinnin ontologia"
5869 rma     http://id.loc.gov/vocabulary/subjectSchemes/rma Ru'us al-mawdu'at al-'Arabiyah  "ar"=>"Ru'us al-mawdu'at al-'Arabiyah"
5870 tgn     http://id.loc.gov/vocabulary/subjectSchemes/tgn Getty thesaurus of geographic names     
5871 tha     http://id.loc.gov/vocabulary/subjectSchemes/tha Barcala de Moyano, Graciela G., Cristina Voena. Tesauro de Historia Argentina   "es"=>"Barcala de Moyano, Graciela G., Cristina Voena. Tesauro de Historia Argentina"
5872 ttll    http://id.loc.gov/vocabulary/subjectSchemes/ttll        Roggau, Zunilda. Tell. Tesauro de lengua y literatura   "es"=>"Roggau, Zunilda. Tell. Tesauro de lengua y literatura"
5873 sears   http://id.loc.gov/vocabulary/subjectSchemes/sears       Sears list of subject headings  
5874 csht    http://id.loc.gov/vocabulary/subjectSchemes/csht        Chinese subject headings        
5875 \.
5876
5877 -- ' ...blah
5878
5879 INSERT INTO authority.thesaurus (code, uri, name, control_set)
5880   SELECT code, uri, name, 1 FROM thesauri;
5881
5882 UPDATE authority.thesaurus SET short_code = 'a' WHERE code = 'lcsh';
5883 UPDATE authority.thesaurus SET short_code = 'b' WHERE code = 'lcshac';
5884 UPDATE authority.thesaurus SET short_code = 'c' WHERE code = 'mesh';
5885 UPDATE authority.thesaurus SET short_code = 'd' WHERE code = 'nal';
5886 UPDATE authority.thesaurus SET short_code = 'k' WHERE code = 'cash';
5887 UPDATE authority.thesaurus SET short_code = 'r' WHERE code = 'aat';
5888 UPDATE authority.thesaurus SET short_code = 's' WHERE code = 'sears';
5889 UPDATE authority.thesaurus SET short_code = 'v' WHERE code = 'rvm';
5890
5891 UPDATE  authority.thesaurus
5892   SET   short_code = 'z'
5893   WHERE short_code IS NULL
5894         AND control_set = 1;
5895
5896 INSERT INTO config.i18n_core (fq_field, identity_value, translation, string )
5897   SELECT  'at.name', t.code, xlate->key, xlate->value
5898     FROM  thesauri t
5899           JOIN LATERAL each(t.xlate) AS xlate ON TRUE
5900     WHERE NOT EXISTS
5901             (SELECT id
5902               FROM  config.i18n_core
5903               WHERE fq_field = 'at.name'
5904                     AND identity_value = t.code
5905                     AND translation = xlate->key)
5906           AND t.xlate IS NOT NULL
5907           AND t.name <> (xlate->value);
5908
5909 CREATE OR REPLACE FUNCTION authority.extract_thesaurus( marcxml TEXT ) RETURNS TEXT AS $func$
5910 DECLARE
5911     thes_code TEXT;
5912 BEGIN
5913     thes_code := vandelay.marc21_extract_fixed_field(marcxml,'Subj');
5914     IF thes_code IS NULL THEN
5915         thes_code := '|';
5916     ELSIF thes_code = 'z' THEN
5917         thes_code := COALESCE( oils_xpath_string('//*[@tag="040"]/*[@code="f"][1]', marcxml), 'z' );
5918     ELSE
5919         SELECT code INTO thes_code FROM authority.thesaurus WHERE short_code = thes_code;
5920         IF NOT FOUND THEN
5921             thes_code := '|'; -- default
5922         END IF;
5923     END IF;
5924     RETURN thes_code;
5925 END;
5926 $func$ LANGUAGE PLPGSQL STABLE STRICT;
5927
5928 CREATE OR REPLACE FUNCTION authority.map_thesaurus_to_control_set () RETURNS TRIGGER AS $func$
5929 BEGIN
5930     IF NEW.control_set IS NULL THEN
5931         SELECT control_set INTO NEW.control_set
5932         FROM authority.thesaurus
5933         WHERE code = authority.extract_thesaurus(NEW.marc);
5934     END IF;
5935
5936     RETURN NEW;
5937 END;
5938 $func$ LANGUAGE PLPGSQL;
5939
5940 CREATE OR REPLACE FUNCTION authority.reingest_authority_rec_descriptor( auth_id BIGINT ) RETURNS VOID AS $func$
5941 BEGIN
5942     DELETE FROM authority.rec_descriptor WHERE record = auth_id;
5943     INSERT INTO authority.rec_descriptor (record, record_status, encoding_level, thesaurus)
5944         SELECT  auth_id,
5945                 vandelay.marc21_extract_fixed_field(marc,'RecStat'),
5946                 vandelay.marc21_extract_fixed_field(marc,'ELvl'),
5947                 authority.extract_thesaurus(marc)
5948           FROM  authority.record_entry
5949           WHERE id = auth_id;
5950     RETURN;
5951 END;
5952 $func$ LANGUAGE PLPGSQL;
5953
5954
5955
5956 SELECT evergreen.upgrade_deps_block_check('1071', :eg_version); --gmcharlt/kmlussier
5957
5958 CREATE OR REPLACE FUNCTION metabib.staged_browse(query text, fields integer[], context_org integer, context_locations integer[], staff boolean, browse_superpage_size integer, count_up_from_zero boolean, result_limit integer, next_pivot_pos integer)
5959  RETURNS SETOF metabib.flat_browse_entry_appearance
5960 AS $f$
5961 DECLARE
5962     curs                    REFCURSOR;
5963     rec                     RECORD;
5964     qpfts_query             TEXT;
5965     aqpfts_query            TEXT;
5966     afields                 INT[];
5967     bfields                 INT[];
5968     result_row              metabib.flat_browse_entry_appearance%ROWTYPE;
5969     results_skipped         INT := 0;
5970     row_counter             INT := 0;
5971     row_number              INT;
5972     slice_start             INT;
5973     slice_end               INT;
5974     full_end                INT;
5975     all_records             BIGINT[];
5976     all_brecords             BIGINT[];
5977     all_arecords            BIGINT[];
5978     superpage_of_records    BIGINT[];
5979     superpage_size          INT;
5980     c_tests                 TEXT := '';
5981     b_tests                 TEXT := '';
5982     c_orgs                  INT[];
5983     unauthorized_entry      RECORD;
5984 BEGIN
5985     IF count_up_from_zero THEN
5986         row_number := 0;
5987     ELSE
5988         row_number := -1;
5989     END IF;
5990
5991     IF NOT staff THEN
5992         SELECT x.c_attrs, x.b_attrs INTO c_tests, b_tests FROM asset.patron_default_visibility_mask() x;
5993     END IF;
5994
5995     IF c_tests <> '' THEN c_tests := c_tests || '&'; END IF;
5996     IF b_tests <> '' THEN b_tests := b_tests || '&'; END IF;
5997
5998     SELECT ARRAY_AGG(id) INTO c_orgs FROM actor.org_unit_descendants(context_org);
5999
6000     c_tests := c_tests || search.calculate_visibility_attribute_test('circ_lib',c_orgs)
6001                || '&' || search.calculate_visibility_attribute_test('owning_lib',c_orgs);
6002
6003     PERFORM 1 FROM config.internal_flag WHERE enabled AND name = 'opac.located_uri.act_as_copy';
6004     IF FOUND THEN
6005         b_tests := b_tests || search.calculate_visibility_attribute_test(
6006             'luri_org',
6007             (SELECT ARRAY_AGG(id) FROM actor.org_unit_full_path(context_org) x)
6008         );
6009     ELSE
6010         b_tests := b_tests || search.calculate_visibility_attribute_test(
6011             'luri_org',
6012             (SELECT ARRAY_AGG(id) FROM actor.org_unit_ancestors(context_org) x)
6013         );
6014     END IF;
6015
6016     IF context_locations THEN
6017         IF c_tests <> '' THEN c_tests := c_tests || '&'; END IF;
6018         c_tests := c_tests || search.calculate_visibility_attribute_test('location',context_locations);
6019     END IF;
6020
6021     OPEN curs NO SCROLL FOR EXECUTE query;
6022
6023     LOOP
6024         FETCH curs INTO rec;
6025         IF NOT FOUND THEN
6026             IF result_row.pivot_point IS NOT NULL THEN
6027                 RETURN NEXT result_row;
6028             END IF;
6029             RETURN;
6030         END IF;
6031
6032         --Is unauthorized?
6033         SELECT INTO unauthorized_entry *
6034         FROM metabib.browse_entry_simple_heading_map mbeshm
6035         INNER JOIN authority.simple_heading ash ON ( mbeshm.simple_heading = ash.id )
6036         INNER JOIN authority.control_set_authority_field acsaf ON ( acsaf.id = ash.atag )
6037         JOIN authority.heading_field ahf ON (ahf.id = acsaf.heading_field)
6038         WHERE mbeshm.entry = rec.id
6039         AND   ahf.heading_purpose = 'variant';
6040
6041         -- Gather aggregate data based on the MBE row we're looking at now, authority axis
6042         IF (unauthorized_entry.record IS NOT NULL) THEN
6043             --unauthorized term belongs to an auth linked to a bib?
6044             SELECT INTO all_arecords, result_row.sees, afields
6045                     ARRAY_AGG(DISTINCT abl.bib),
6046                     STRING_AGG(DISTINCT abl.authority::TEXT, $$,$$),
6047                     ARRAY_AGG(DISTINCT map.metabib_field)
6048             FROM authority.bib_linking abl
6049             INNER JOIN authority.control_set_auth_field_metabib_field_map_refs map ON (
6050                     map.authority_field = unauthorized_entry.atag
6051                     AND map.metabib_field = ANY(fields)
6052             )
6053             WHERE abl.authority = unauthorized_entry.record;
6054         ELSE
6055             --do usual procedure
6056             SELECT INTO all_arecords, result_row.sees, afields
6057                     ARRAY_AGG(DISTINCT abl.bib), -- bibs to check for visibility
6058                     STRING_AGG(DISTINCT aal.source::TEXT, $$,$$), -- authority record ids
6059                     ARRAY_AGG(DISTINCT map.metabib_field) -- authority-tag-linked CMF rows
6060
6061             FROM  metabib.browse_entry_simple_heading_map mbeshm
6062                     JOIN authority.simple_heading ash ON ( mbeshm.simple_heading = ash.id )
6063                     JOIN authority.authority_linking aal ON ( ash.record = aal.source )
6064                     JOIN authority.bib_linking abl ON ( aal.target = abl.authority )
6065                     JOIN authority.control_set_auth_field_metabib_field_map_refs map ON (
6066                         ash.atag = map.authority_field
6067                         AND map.metabib_field = ANY(fields)
6068                     )
6069                     JOIN authority.control_set_authority_field acsaf ON (
6070                         map.authority_field = acsaf.id
6071                     )
6072                     JOIN authority.heading_field ahf ON (ahf.id = acsaf.heading_field)
6073               WHERE mbeshm.entry = rec.id
6074               AND   ahf.heading_purpose = 'variant';
6075
6076         END IF;
6077
6078         -- Gather aggregate data based on the MBE row we're looking at now, bib axis
6079         SELECT INTO all_brecords, result_row.authorities, bfields
6080                 ARRAY_AGG(DISTINCT source),
6081                 STRING_AGG(DISTINCT authority::TEXT, $$,$$),
6082                 ARRAY_AGG(DISTINCT def)
6083           FROM  metabib.browse_entry_def_map
6084           WHERE entry = rec.id
6085                 AND def = ANY(fields);
6086
6087         SELECT INTO result_row.fields STRING_AGG(DISTINCT x::TEXT, $$,$$) FROM UNNEST(afields || bfields) x;
6088
6089         result_row.sources := 0;
6090         result_row.asources := 0;
6091
6092         -- Bib-linked vis checking
6093         IF ARRAY_UPPER(all_brecords,1) IS NOT NULL THEN
6094
6095             SELECT  INTO result_row.sources COUNT(DISTINCT b.id)
6096               FROM  biblio.record_entry b
6097                     JOIN asset.copy_vis_attr_cache acvac ON (acvac.record = b.id)
6098               WHERE b.id = ANY(all_brecords[1:browse_superpage_size])
6099                     AND (
6100                         acvac.vis_attr_vector @@ c_tests::query_int
6101                         OR b.vis_attr_vector @@ b_tests::query_int
6102                     );
6103
6104             result_row.accurate := TRUE;
6105
6106         END IF;
6107
6108         -- Authority-linked vis checking
6109         IF ARRAY_UPPER(all_arecords,1) IS NOT NULL THEN
6110
6111             SELECT  INTO result_row.asources COUNT(DISTINCT b.id)
6112               FROM  biblio.record_entry b
6113                     JOIN asset.copy_vis_attr_cache acvac ON (acvac.record = b.id)
6114               WHERE b.id = ANY(all_arecords[1:browse_superpage_size])
6115                     AND (
6116                         acvac.vis_attr_vector @@ c_tests::query_int
6117                         OR b.vis_attr_vector @@ b_tests::query_int
6118                     );
6119
6120             result_row.aaccurate := TRUE;
6121
6122         END IF;
6123
6124         IF result_row.sources > 0 OR result_row.asources > 0 THEN
6125
6126             -- The function that calls this function needs row_number in order
6127             -- to correctly order results from two different runs of this
6128             -- functions.
6129             result_row.row_number := row_number;
6130
6131             -- Now, if row_counter is still less than limit, return a row.  If
6132             -- not, but it is less than next_pivot_pos, continue on without
6133             -- returning actual result rows until we find
6134             -- that next pivot, and return it.
6135
6136             IF row_counter < result_limit THEN
6137                 result_row.browse_entry := rec.id;
6138                 result_row.value := rec.value;
6139
6140                 RETURN NEXT result_row;
6141             ELSE
6142                 result_row.browse_entry := NULL;
6143                 result_row.authorities := NULL;
6144                 result_row.fields := NULL;
6145                 result_row.value := NULL;
6146                 result_row.sources := NULL;
6147                 result_row.sees := NULL;
6148                 result_row.accurate := NULL;
6149                 result_row.aaccurate := NULL;
6150                 result_row.pivot_point := rec.id;
6151
6152                 IF row_counter >= next_pivot_pos THEN
6153                     RETURN NEXT result_row;
6154                     RETURN;
6155                 END IF;
6156             END IF;
6157
6158             IF count_up_from_zero THEN
6159                 row_number := row_number + 1;
6160             ELSE
6161                 row_number := row_number - 1;
6162             END IF;
6163
6164             -- row_counter is different from row_number.
6165             -- It simply counts up from zero so that we know when
6166             -- we've reached our limit.
6167             row_counter := row_counter + 1;
6168         END IF;
6169     END LOOP;
6170 END;
6171 $f$ LANGUAGE plpgsql ROWS 10;
6172
6173 CREATE OR REPLACE FUNCTION metabib.browse(search_field integer[], browse_term text, context_org integer DEFAULT NULL::integer, context_loc_group integer DEFAULT NULL::integer, staff boolean DEFAULT false, pivot_id bigint DEFAULT NULL::bigint, result_limit integer DEFAULT 10)
6174  RETURNS SETOF metabib.flat_browse_entry_appearance
6175 AS $f$
6176 DECLARE
6177     core_query              TEXT;
6178     back_query              TEXT;
6179     forward_query           TEXT;
6180     pivot_sort_value        TEXT;
6181     pivot_sort_fallback     TEXT;
6182     context_locations       INT[];
6183     browse_superpage_size   INT;
6184     results_skipped         INT := 0;
6185     back_limit              INT;
6186     back_to_pivot           INT;
6187     forward_limit           INT;
6188     forward_to_pivot        INT;
6189 BEGIN
6190     -- First, find the pivot if we were given a browse term but not a pivot.
6191     IF pivot_id IS NULL THEN
6192         pivot_id := metabib.browse_pivot(search_field, browse_term);
6193     END IF;
6194
6195     SELECT INTO pivot_sort_value, pivot_sort_fallback
6196         sort_value, value FROM metabib.browse_entry WHERE id = pivot_id;
6197
6198     -- Bail if we couldn't find a pivot.
6199     IF pivot_sort_value IS NULL THEN
6200         RETURN;
6201     END IF;
6202
6203     -- Transform the context_loc_group argument (if any) (logc at the
6204     -- TPAC layer) into a form we'll be able to use.
6205     IF context_loc_group IS NOT NULL THEN
6206         SELECT INTO context_locations ARRAY_AGG(location)
6207             FROM asset.copy_location_group_map
6208             WHERE lgroup = context_loc_group;
6209     END IF;
6210
6211     -- Get the configured size of browse superpages.
6212     SELECT INTO browse_superpage_size COALESCE(value::INT,100)     -- NULL ok
6213         FROM config.global_flag
6214         WHERE enabled AND name = 'opac.browse.holdings_visibility_test_limit';
6215
6216     -- First we're going to search backward from the pivot, then we're going
6217     -- to search forward.  In each direction, we need two limits.  At the
6218     -- lesser of the two limits, we delineate the edge of the result set
6219     -- we're going to return.  At the greater of the two limits, we find the
6220     -- pivot value that would represent an offset from the current pivot
6221     -- at a distance of one "page" in either direction, where a "page" is a
6222     -- result set of the size specified in the "result_limit" argument.
6223     --
6224     -- The two limits in each direction make four derived values in total,
6225     -- and we calculate them now.
6226     back_limit := CEIL(result_limit::FLOAT / 2);
6227     back_to_pivot := result_limit;
6228     forward_limit := result_limit / 2;
6229     forward_to_pivot := result_limit - 1;
6230
6231     -- This is the meat of the SQL query that finds browse entries.  We'll
6232     -- pass this to a function which uses it with a cursor, so that individual
6233     -- rows may be fetched in a loop until some condition is satisfied, without
6234     -- waiting for a result set of fixed size to be collected all at once.
6235     core_query := '
6236 SELECT  mbe.id,
6237         mbe.value,
6238         mbe.sort_value
6239   FROM  metabib.browse_entry mbe
6240   WHERE (
6241             EXISTS ( -- are there any bibs using this mbe via the requested fields?
6242                 SELECT  1
6243                   FROM  metabib.browse_entry_def_map mbedm
6244                   WHERE mbedm.entry = mbe.id AND mbedm.def = ANY(' || quote_literal(search_field) || ')
6245             ) OR EXISTS ( -- are there any authorities using this mbe via the requested fields?
6246                 SELECT  1
6247                   FROM  metabib.browse_entry_simple_heading_map mbeshm
6248                         JOIN authority.simple_heading ash ON ( mbeshm.simple_heading = ash.id )
6249                         JOIN authority.control_set_auth_field_metabib_field_map_refs map ON (
6250                             ash.atag = map.authority_field
6251                             AND map.metabib_field = ANY(' || quote_literal(search_field) || ')
6252                         )
6253                         JOIN authority.control_set_authority_field acsaf ON (
6254                             map.authority_field = acsaf.id
6255                         )
6256                         JOIN authority.heading_field ahf ON (ahf.id = acsaf.heading_field)
6257                   WHERE mbeshm.entry = mbe.id
6258                     AND ahf.heading_purpose IN (' || $$'variant'$$ || ')
6259                     -- and authority that variant is coming from is linked to a bib
6260                     AND EXISTS (
6261                         SELECT  1
6262                         FROM  metabib.browse_entry_def_map mbedm2
6263                         WHERE mbedm2.authority = ash.record AND mbedm2.def = ANY(' || quote_literal(search_field) || ')
6264                     )
6265             )
6266         ) AND ';
6267
6268     -- This is the variant of the query for browsing backward.
6269     back_query := core_query ||
6270         ' mbe.sort_value <= ' || quote_literal(pivot_sort_value) ||
6271     ' ORDER BY mbe.sort_value DESC, mbe.value DESC LIMIT 1000';
6272
6273     -- This variant browses forward.
6274     forward_query := core_query ||
6275         ' mbe.sort_value > ' || quote_literal(pivot_sort_value) ||
6276     ' ORDER BY mbe.sort_value, mbe.value LIMIT 1000';
6277
6278     -- We now call the function which applies a cursor to the provided
6279     -- queries, stopping at the appropriate limits and also giving us
6280     -- the next page's pivot.
6281     RETURN QUERY
6282         SELECT * FROM metabib.staged_browse(
6283             back_query, search_field, context_org, context_locations,
6284             staff, browse_superpage_size, TRUE, back_limit, back_to_pivot
6285         ) UNION
6286         SELECT * FROM metabib.staged_browse(
6287             forward_query, search_field, context_org, context_locations,
6288             staff, browse_superpage_size, FALSE, forward_limit, forward_to_pivot
6289         ) ORDER BY row_number DESC;
6290
6291 END;
6292 $f$ LANGUAGE plpgsql ROWS 10;
6293
6294
6295 SELECT evergreen.upgrade_deps_block_check('1072', :eg_version); --gmcharlt/kmlussier
6296
6297 INSERT INTO config.global_flag (name, label, enabled) VALUES (
6298     'opac.show_related_headings_in_browse',
6299     oils_i18n_gettext(
6300         'opac.show_related_headings_in_browse',
6301         'Display related headings (see-also) in browse',
6302         'cgf',
6303         'label'
6304     ),
6305     TRUE
6306 );
6307
6308
6309
6310 SELECT evergreen.upgrade_deps_block_check('1073', :eg_version);
6311
6312 ALTER TABLE config.metabib_field 
6313     ADD COLUMN display_xpath TEXT, 
6314     ADD COLUMN display_field BOOL NOT NULL DEFAULT FALSE;
6315
6316 CREATE TABLE config.display_field_map (
6317     name    TEXT   PRIMARY KEY,
6318     field   INTEGER REFERENCES config.metabib_field (id),
6319     multi   BOOLEAN DEFAULT FALSE
6320 );
6321
6322 CREATE TABLE metabib.display_entry (
6323     id      BIGSERIAL  PRIMARY KEY,
6324     source  BIGINT     NOT NULL REFERENCES biblio.record_entry (id),
6325     field   INT        NOT NULL REFERENCES config.metabib_field (id),
6326     value   TEXT       NOT NULL
6327 );
6328
6329 CREATE INDEX metabib_display_entry_field_idx ON metabib.display_entry (field);
6330 CREATE INDEX metabib_display_entry_source_idx ON metabib.display_entry (source);
6331
6332 -- one row per display entry fleshed with field info
6333 CREATE VIEW metabib.flat_display_entry AS
6334     SELECT
6335         mde.source,
6336         cdfm.name,
6337         cdfm.multi,
6338         cmf.label,
6339         cmf.id AS field,
6340         mde.value
6341     FROM metabib.display_entry mde
6342     JOIN config.metabib_field cmf ON (cmf.id = mde.field)
6343     JOIN config.display_field_map cdfm ON (cdfm.field = mde.field)
6344 ;
6345
6346 -- like flat_display_entry except values are compressed 
6347 -- into one row per display_field_map and JSON-ified.
6348 CREATE VIEW metabib.compressed_display_entry AS
6349     SELECT 
6350         source,
6351         name,
6352         multi,
6353         label,
6354         field,
6355         CASE WHEN multi THEN
6356             TO_JSON(ARRAY_AGG(value))
6357         ELSE
6358             TO_JSON(MIN(value))
6359         END AS value
6360     FROM metabib.flat_display_entry
6361     GROUP BY 1, 2, 3, 4, 5
6362 ;
6363
6364 -- TODO: expand to encompass all well-known fields
6365 CREATE VIEW metabib.wide_display_entry AS
6366     SELECT 
6367         bre.id AS source,
6368         COALESCE(mcde_title.value, 'null') AS title,
6369         COALESCE(mcde_author.value, 'null') AS author,
6370         COALESCE(mcde_subject.value, 'null') AS subject,
6371         COALESCE(mcde_creators.value, 'null') AS creators,
6372         COALESCE(mcde_isbn.value, 'null') AS isbn
6373     -- ensure one row per bre regardless of any display fields
6374     FROM biblio.record_entry bre 
6375     LEFT JOIN metabib.compressed_display_entry mcde_title 
6376         ON (bre.id = mcde_title.source AND mcde_title.name = 'title')
6377     LEFT JOIN metabib.compressed_display_entry mcde_author 
6378         ON (bre.id = mcde_author.source AND mcde_author.name = 'author')
6379     LEFT JOIN metabib.compressed_display_entry mcde_subject 
6380         ON (bre.id = mcde_subject.source AND mcde_subject.name = 'subject')
6381     LEFT JOIN metabib.compressed_display_entry mcde_creators 
6382         ON (bre.id = mcde_creators.source AND mcde_creators.name = 'creators')
6383     LEFT JOIN metabib.compressed_display_entry mcde_isbn 
6384         ON (bre.id = mcde_isbn.source AND mcde_isbn.name = 'isbn')
6385 ;
6386
6387
6388 CREATE OR REPLACE FUNCTION metabib.display_field_normalize_trigger () 
6389     RETURNS TRIGGER AS $$
6390 DECLARE
6391     normalizer  RECORD;
6392     display_field_text  TEXT;
6393 BEGIN
6394     display_field_text := NEW.value;
6395
6396     FOR normalizer IN
6397         SELECT  n.func AS func,
6398                 n.param_count AS param_count,
6399                 m.params AS params
6400           FROM  config.index_normalizer n
6401                 JOIN config.metabib_field_index_norm_map m ON (m.norm = n.id)
6402           WHERE m.field = NEW.field AND m.pos < 0
6403           ORDER BY m.pos LOOP
6404
6405             EXECUTE 'SELECT ' || normalizer.func || '(' ||
6406                 quote_literal( display_field_text ) ||
6407                 CASE
6408                     WHEN normalizer.param_count > 0
6409                         THEN ',' || REPLACE(REPLACE(BTRIM(
6410                             normalizer.params,'[]'),E'\'',E'\\\''),E'"',E'\'')
6411                         ELSE ''
6412                     END ||
6413                 ')' INTO display_field_text;
6414
6415     END LOOP;
6416
6417     NEW.value = display_field_text;
6418
6419     RETURN NEW;
6420 END;
6421 $$ LANGUAGE PLPGSQL;
6422
6423 CREATE TRIGGER display_field_normalize_tgr
6424         BEFORE UPDATE OR INSERT ON metabib.display_entry
6425         FOR EACH ROW EXECUTE PROCEDURE metabib.display_field_normalize_trigger();
6426
6427 CREATE OR REPLACE FUNCTION evergreen.display_field_force_nfc() 
6428     RETURNS TRIGGER AS $$
6429 BEGIN
6430     NEW.value := force_unicode_normal_form(NEW.value,'NFC');
6431     RETURN NEW;
6432 END;
6433 $$ LANGUAGE PLPGSQL;
6434
6435 CREATE TRIGGER display_field_force_nfc_tgr
6436         BEFORE UPDATE OR INSERT ON metabib.display_entry
6437         FOR EACH ROW EXECUTE PROCEDURE evergreen.display_field_force_nfc();
6438
6439 ALTER TYPE metabib.field_entry_template ADD ATTRIBUTE display_field BOOL;
6440
6441 DROP FUNCTION metabib.reingest_metabib_field_entries(BIGINT, BOOL, BOOL, BOOL);
6442 DROP FUNCTION biblio.extract_metabib_field_entry(BIGINT);
6443 DROP FUNCTION biblio.extract_metabib_field_entry(BIGINT, TEXT);
6444
6445 CREATE OR REPLACE FUNCTION biblio.extract_metabib_field_entry (
6446     rid BIGINT,
6447     default_joiner TEXT,
6448     field_types TEXT[],
6449     only_fields INT[]
6450 ) RETURNS SETOF metabib.field_entry_template AS $func$
6451 DECLARE
6452     bib     biblio.record_entry%ROWTYPE;
6453     idx     config.metabib_field%ROWTYPE;
6454     xfrm        config.xml_transform%ROWTYPE;
6455     prev_xfrm   TEXT;
6456     transformed_xml TEXT;
6457     xml_node    TEXT;
6458     xml_node_list   TEXT[];
6459     facet_text  TEXT;
6460     display_text TEXT;
6461     browse_text TEXT;
6462     sort_value  TEXT;
6463     raw_text    TEXT;
6464     curr_text   TEXT;
6465     joiner      TEXT := default_joiner; -- XXX will index defs supply a joiner?
6466     authority_text TEXT;
6467     authority_link BIGINT;
6468     output_row  metabib.field_entry_template%ROWTYPE;
6469     process_idx BOOL;
6470 BEGIN
6471
6472     -- Start out with no field-use bools set
6473     output_row.browse_field = FALSE;
6474     output_row.facet_field = FALSE;
6475     output_row.display_field = FALSE;
6476     output_row.search_field = FALSE;
6477
6478     -- Get the record
6479     SELECT INTO bib * FROM biblio.record_entry WHERE id = rid;
6480
6481     -- Loop over the indexing entries
6482     FOR idx IN SELECT * FROM config.metabib_field WHERE id = ANY (only_fields) ORDER BY format LOOP
6483
6484         process_idx := FALSE;
6485         IF idx.display_field AND 'display' = ANY (field_types) THEN process_idx = TRUE; END IF;
6486         IF idx.browse_field AND 'browse' = ANY (field_types) THEN process_idx = TRUE; END IF;
6487         IF idx.search_field AND 'search' = ANY (field_types) THEN process_idx = TRUE; END IF;
6488         IF idx.facet_field AND 'facet' = ANY (field_types) THEN process_idx = TRUE; END IF;
6489         CONTINUE WHEN process_idx = FALSE;
6490
6491         joiner := COALESCE(idx.joiner, default_joiner);
6492
6493         SELECT INTO xfrm * from config.xml_transform WHERE name = idx.format;
6494
6495         -- See if we can skip the XSLT ... it's expensive
6496         IF prev_xfrm IS NULL OR prev_xfrm <> xfrm.name THEN
6497             -- Can't skip the transform
6498             IF xfrm.xslt <> '---' THEN
6499                 transformed_xml := oils_xslt_process(bib.marc,xfrm.xslt);
6500             ELSE
6501                 transformed_xml := bib.marc;
6502             END IF;
6503
6504             prev_xfrm := xfrm.name;
6505         END IF;
6506
6507         xml_node_list := oils_xpath( idx.xpath, transformed_xml, ARRAY[ARRAY[xfrm.prefix, xfrm.namespace_uri]] );
6508
6509         raw_text := NULL;
6510         FOR xml_node IN SELECT x FROM unnest(xml_node_list) AS x LOOP
6511             CONTINUE WHEN xml_node !~ E'^\\s*<';
6512
6513             -- XXX much of this should be moved into oils_xpath_string...
6514             curr_text := ARRAY_TO_STRING(evergreen.array_remove_item_by_value(evergreen.array_remove_item_by_value(
6515                 oils_xpath( '//text()', -- get the content of all the nodes within the main selected node
6516                     REGEXP_REPLACE( xml_node, E'\\s+', ' ', 'g' ) -- Translate adjacent whitespace to a single space
6517                 ), ' '), ''),  -- throw away morally empty (bankrupt?) strings
6518                 joiner
6519             );
6520
6521             CONTINUE WHEN curr_text IS NULL OR curr_text = '';
6522
6523             IF raw_text IS NOT NULL THEN
6524                 raw_text := raw_text || joiner;
6525             END IF;
6526
6527             raw_text := COALESCE(raw_text,'') || curr_text;
6528
6529             -- autosuggest/metabib.browse_entry
6530             IF idx.browse_field THEN
6531
6532                 IF idx.browse_xpath IS NOT NULL AND idx.browse_xpath <> '' THEN
6533                     browse_text := oils_xpath_string( idx.browse_xpath, xml_node, joiner, ARRAY[ARRAY[xfrm.prefix, xfrm.namespace_uri]] );
6534                 ELSE
6535                     browse_text := curr_text;
6536                 END IF;
6537
6538                 IF idx.browse_sort_xpath IS NOT NULL AND
6539                     idx.browse_sort_xpath <> '' THEN
6540
6541                     sort_value := oils_xpath_string(
6542                         idx.browse_sort_xpath, xml_node, joiner,
6543                         ARRAY[ARRAY[xfrm.prefix, xfrm.namespace_uri]]
6544                     );
6545                 ELSE
6546                     sort_value := browse_text;
6547                 END IF;
6548
6549                 output_row.field_class = idx.field_class;
6550                 output_row.field = idx.id;
6551                 output_row.source = rid;
6552                 output_row.value = BTRIM(REGEXP_REPLACE(browse_text, E'\\s+', ' ', 'g'));
6553                 output_row.sort_value :=
6554                     public.naco_normalize(sort_value);
6555
6556                 output_row.authority := NULL;
6557
6558                 IF idx.authority_xpath IS NOT NULL AND idx.authority_xpath <> '' THEN
6559                     authority_text := oils_xpath_string(
6560                         idx.authority_xpath, xml_node, joiner,
6561                         ARRAY[
6562                             ARRAY[xfrm.prefix, xfrm.namespace_uri],
6563                             ARRAY['xlink','http://www.w3.org/1999/xlink']
6564                         ]
6565                     );
6566
6567                     IF authority_text ~ '^\d+$' THEN
6568                         authority_link := authority_text::BIGINT;
6569                         PERFORM * FROM authority.record_entry WHERE id = authority_link;
6570                         IF FOUND THEN
6571                             output_row.authority := authority_link;
6572                         END IF;
6573                     END IF;
6574
6575                 END IF;
6576
6577                 output_row.browse_field = TRUE;
6578                 -- Returning browse rows with search_field = true for search+browse
6579                 -- configs allows us to retain granularity of being able to search
6580                 -- browse fields with "starts with" type operators (for example, for
6581                 -- titles of songs in music albums)
6582                 IF idx.search_field THEN
6583                     output_row.search_field = TRUE;
6584                 END IF;
6585                 RETURN NEXT output_row;
6586                 output_row.browse_field = FALSE;
6587                 output_row.search_field = FALSE;
6588                 output_row.sort_value := NULL;
6589             END IF;
6590
6591             -- insert raw node text for faceting
6592             IF idx.facet_field THEN
6593
6594                 IF idx.facet_xpath IS NOT NULL AND idx.facet_xpath <> '' THEN
6595                     facet_text := oils_xpath_string( idx.facet_xpath, xml_node, joiner, ARRAY[ARRAY[xfrm.prefix, xfrm.namespace_uri]] );
6596                 ELSE
6597                     facet_text := curr_text;
6598                 END IF;
6599
6600                 output_row.field_class = idx.field_class;
6601                 output_row.field = -1 * idx.id;
6602                 output_row.source = rid;
6603                 output_row.value = BTRIM(REGEXP_REPLACE(facet_text, E'\\s+', ' ', 'g'));
6604
6605                 output_row.facet_field = TRUE;
6606                 RETURN NEXT output_row;
6607                 output_row.facet_field = FALSE;
6608             END IF;
6609
6610             -- insert raw node text for display
6611             IF idx.display_field THEN
6612
6613                 IF idx.display_xpath IS NOT NULL AND idx.display_xpath <> '' THEN
6614                     display_text := oils_xpath_string( idx.display_xpath, xml_node, joiner, ARRAY[ARRAY[xfrm.prefix, xfrm.namespace_uri]] );
6615                 ELSE
6616                     display_text := curr_text;
6617                 END IF;
6618
6619                 output_row.field_class = idx.field_class;
6620                 output_row.field = -1 * idx.id;
6621                 output_row.source = rid;
6622                 output_row.value = BTRIM(REGEXP_REPLACE(display_text, E'\\s+', ' ', 'g'));
6623
6624                 output_row.display_field = TRUE;
6625                 RETURN NEXT output_row;
6626                 output_row.display_field = FALSE;
6627             END IF;
6628
6629         END LOOP;
6630
6631         CONTINUE WHEN raw_text IS NULL OR raw_text = '';
6632
6633         -- insert combined node text for searching
6634         IF idx.search_field THEN
6635             output_row.field_class = idx.field_class;
6636             output_row.field = idx.id;
6637             output_row.source = rid;
6638             output_row.value = BTRIM(REGEXP_REPLACE(raw_text, E'\\s+', ' ', 'g'));
6639
6640             output_row.search_field = TRUE;
6641             RETURN NEXT output_row;
6642             output_row.search_field = FALSE;
6643         END IF;
6644
6645     END LOOP;
6646
6647 END;
6648
6649 $func$ LANGUAGE PLPGSQL;
6650
6651 CREATE OR REPLACE FUNCTION metabib.reingest_metabib_field_entries( 
6652     bib_id BIGINT,
6653     skip_facet BOOL DEFAULT FALSE, 
6654     skip_display BOOL DEFAULT FALSE,
6655     skip_browse BOOL DEFAULT FALSE, 
6656     skip_search BOOL DEFAULT FALSE,
6657     only_fields INT[] DEFAULT '{}'::INT[]
6658 ) RETURNS VOID AS $func$
6659 DECLARE
6660     fclass          RECORD;
6661     ind_data        metabib.field_entry_template%ROWTYPE;
6662     mbe_row         metabib.browse_entry%ROWTYPE;
6663     mbe_id          BIGINT;
6664     b_skip_facet    BOOL;
6665     b_skip_display    BOOL;
6666     b_skip_browse   BOOL;
6667     b_skip_search   BOOL;
6668     value_prepped   TEXT;
6669     field_list      INT[] := only_fields;
6670     field_types     TEXT[] := '{}'::TEXT[];
6671 BEGIN
6672
6673     IF field_list = '{}'::INT[] THEN
6674         SELECT ARRAY_AGG(id) INTO field_list FROM config.metabib_field;
6675     END IF;
6676
6677     SELECT COALESCE(NULLIF(skip_facet, FALSE), EXISTS (SELECT enabled FROM config.internal_flag WHERE name =  'ingest.skip_facet_indexing' AND enabled)) INTO b_skip_facet;
6678     SELECT COALESCE(NULLIF(skip_display, FALSE), EXISTS (SELECT enabled FROM config.internal_flag WHERE name =  'ingest.skip_display_indexing' AND enabled)) INTO b_skip_display;
6679     SELECT COALESCE(NULLIF(skip_browse, FALSE), EXISTS (SELECT enabled FROM config.internal_flag WHERE name =  'ingest.skip_browse_indexing' AND enabled)) INTO b_skip_browse;
6680     SELECT COALESCE(NULLIF(skip_search, FALSE), EXISTS (SELECT enabled FROM config.internal_flag WHERE name =  'ingest.skip_search_indexing' AND enabled)) INTO b_skip_search;
6681
6682     IF NOT b_skip_facet THEN field_types := field_types || '{facet}'; END IF;
6683     IF NOT b_skip_display THEN field_types := field_types || '{display}'; END IF;
6684     IF NOT b_skip_browse THEN field_types := field_types || '{browse}'; END IF;
6685     IF NOT b_skip_search THEN field_types := field_types || '{search}'; END IF;
6686
6687     PERFORM * FROM config.internal_flag WHERE name = 'ingest.assume_inserts_only' AND enabled;
6688     IF NOT FOUND THEN
6689         IF NOT b_skip_search THEN
6690             FOR fclass IN SELECT * FROM config.metabib_class LOOP
6691                 -- RAISE NOTICE 'Emptying out %', fclass.name;
6692                 EXECUTE $$DELETE FROM metabib.$$ || fclass.name || $$_field_entry WHERE source = $$ || bib_id;
6693             END LOOP;
6694         END IF;
6695         IF NOT b_skip_facet THEN
6696             DELETE FROM metabib.facet_entry WHERE source = bib_id;
6697         END IF;
6698         IF NOT b_skip_display THEN
6699             DELETE FROM metabib.display_entry WHERE source = bib_id;
6700         END IF;
6701         IF NOT b_skip_browse THEN
6702             DELETE FROM metabib.browse_entry_def_map WHERE source = bib_id;
6703         END IF;
6704     END IF;
6705
6706     FOR ind_data IN SELECT * FROM biblio.extract_metabib_field_entry( bib_id, ' ', field_types, field_list ) LOOP
6707
6708         -- don't store what has been normalized away
6709         CONTINUE WHEN ind_data.value IS NULL;
6710
6711         IF ind_data.field < 0 THEN
6712             ind_data.field = -1 * ind_data.field;
6713         END IF;
6714
6715         IF ind_data.facet_field AND NOT b_skip_facet THEN
6716             INSERT INTO metabib.facet_entry (field, source, value)
6717                 VALUES (ind_data.field, ind_data.source, ind_data.value);
6718         END IF;
6719
6720         IF ind_data.display_field AND NOT b_skip_display THEN
6721             INSERT INTO metabib.display_entry (field, source, value)
6722                 VALUES (ind_data.field, ind_data.source, ind_data.value);
6723         END IF;
6724
6725
6726         IF ind_data.browse_field AND NOT b_skip_browse THEN
6727             -- A caveat about this SELECT: this should take care of replacing
6728             -- old mbe rows when data changes, but not if normalization (by
6729             -- which I mean specifically the output of
6730             -- evergreen.oils_tsearch2()) changes.  It may or may not be
6731             -- expensive to add a comparison of index_vector to index_vector
6732             -- to the WHERE clause below.
6733
6734             CONTINUE WHEN ind_data.sort_value IS NULL;
6735
6736             value_prepped := metabib.browse_normalize(ind_data.value, ind_data.field);
6737             SELECT INTO mbe_row * FROM metabib.browse_entry
6738                 WHERE value = value_prepped AND sort_value = ind_data.sort_value;
6739
6740             IF FOUND THEN
6741                 mbe_id := mbe_row.id;
6742             ELSE
6743                 INSERT INTO metabib.browse_entry
6744                     ( value, sort_value ) VALUES
6745                     ( value_prepped, ind_data.sort_value );
6746
6747                 mbe_id := CURRVAL('metabib.browse_entry_id_seq'::REGCLASS);
6748             END IF;
6749
6750             INSERT INTO metabib.browse_entry_def_map (entry, def, source, authority)
6751                 VALUES (mbe_id, ind_data.field, ind_data.source, ind_data.authority);
6752         END IF;
6753
6754         IF ind_data.search_field AND NOT b_skip_search THEN
6755             -- Avoid inserting duplicate rows
6756             EXECUTE 'SELECT 1 FROM metabib.' || ind_data.field_class ||
6757                 '_field_entry WHERE field = $1 AND source = $2 AND value = $3'
6758                 INTO mbe_id USING ind_data.field, ind_data.source, ind_data.value;
6759                 -- RAISE NOTICE 'Search for an already matching row returned %', mbe_id;
6760             IF mbe_id IS NULL THEN
6761                 EXECUTE $$
6762                 INSERT INTO metabib.$$ || ind_data.field_class || $$_field_entry (field, source, value)
6763                     VALUES ($$ ||
6764                         quote_literal(ind_data.field) || $$, $$ ||
6765                         quote_literal(ind_data.source) || $$, $$ ||
6766                         quote_literal(ind_data.value) ||
6767                     $$);$$;
6768             END IF;
6769         END IF;
6770
6771     END LOOP;
6772
6773     IF NOT b_skip_search THEN
6774         PERFORM metabib.update_combined_index_vectors(bib_id);
6775     END IF;
6776
6777     RETURN;
6778 END;
6779 $func$ LANGUAGE PLPGSQL;
6780
6781 -- AFTER UPDATE OR INSERT trigger for biblio.record_entry
6782 CREATE OR REPLACE FUNCTION biblio.indexing_ingest_or_delete () RETURNS TRIGGER AS $func$
6783 DECLARE
6784     tmp_bool BOOL;
6785 BEGIN
6786
6787     IF NEW.deleted THEN -- If this bib is deleted
6788
6789         PERFORM * FROM config.internal_flag WHERE
6790             name = 'ingest.metarecord_mapping.preserve_on_delete' AND enabled;
6791
6792         tmp_bool := FOUND; -- Just in case this is changed by some other statement
6793
6794         PERFORM metabib.remap_metarecord_for_bib( NEW.id, NEW.fingerprint, TRUE, tmp_bool );
6795
6796         IF NOT tmp_bool THEN
6797             -- One needs to keep these around to support searches
6798             -- with the #deleted modifier, so one should turn on the named
6799             -- internal flag for that functionality.
6800             DELETE FROM metabib.record_attr_vector_list WHERE source = NEW.id;
6801         END IF;
6802
6803         DELETE FROM authority.bib_linking WHERE bib = NEW.id; -- Avoid updating fields in bibs that are no longer visible
6804         DELETE FROM biblio.peer_bib_copy_map WHERE peer_record = NEW.id; -- Separate any multi-homed items
6805         DELETE FROM metabib.browse_entry_def_map WHERE source = NEW.id; -- Don't auto-suggest deleted bibs
6806         RETURN NEW; -- and we're done
6807     END IF;
6808
6809     IF TG_OP = 'UPDATE' THEN -- re-ingest?
6810         PERFORM * FROM config.internal_flag WHERE name = 'ingest.reingest.force_on_same_marc' AND enabled;
6811
6812         IF NOT FOUND AND OLD.marc = NEW.marc THEN -- don't do anything if the MARC didn't change
6813             RETURN NEW;
6814         END IF;
6815     END IF;
6816
6817     -- Record authority linking
6818     PERFORM * FROM config.internal_flag WHERE name = 'ingest.disable_authority_linking' AND enabled;
6819     IF NOT FOUND THEN
6820         PERFORM biblio.map_authority_linking( NEW.id, NEW.marc );
6821     END IF;
6822
6823     -- Flatten and insert the mfr data
6824     PERFORM * FROM config.internal_flag WHERE name = 'ingest.disable_metabib_full_rec' AND enabled;
6825     IF NOT FOUND THEN
6826         PERFORM metabib.reingest_metabib_full_rec(NEW.id);
6827
6828         -- Now we pull out attribute data, which is dependent on the mfr for all but XPath-based fields
6829         PERFORM * FROM config.internal_flag WHERE name = 'ingest.disable_metabib_rec_descriptor' AND enabled;
6830         IF NOT FOUND THEN
6831             PERFORM metabib.reingest_record_attributes(NEW.id, NULL, NEW.marc, TG_OP = 'INSERT' OR OLD.deleted);
6832         END IF;
6833     END IF;
6834
6835     -- Gather and insert the field entry data
6836     PERFORM metabib.reingest_metabib_field_entries(NEW.id);
6837
6838     -- Located URI magic
6839     PERFORM * FROM config.internal_flag WHERE name = 'ingest.disable_located_uri' AND enabled;
6840     IF NOT FOUND THEN PERFORM biblio.extract_located_uris( NEW.id, NEW.marc, NEW.editor ); END IF;
6841
6842     -- (re)map metarecord-bib linking
6843     IF TG_OP = 'INSERT' THEN -- if not deleted and performing an insert, check for the flag
6844         PERFORM * FROM config.internal_flag WHERE name = 'ingest.metarecord_mapping.skip_on_insert' AND enabled;
6845         IF NOT FOUND THEN
6846             PERFORM metabib.remap_metarecord_for_bib( NEW.id, NEW.fingerprint );
6847         END IF;
6848     ELSE -- we're doing an update, and we're not deleted, remap
6849         PERFORM * FROM config.internal_flag WHERE name = 'ingest.metarecord_mapping.skip_on_update' AND enabled;
6850         IF NOT FOUND THEN
6851             PERFORM metabib.remap_metarecord_for_bib( NEW.id, NEW.fingerprint );
6852         END IF;
6853     END IF;
6854
6855     RETURN NEW;
6856 END;
6857 $func$ LANGUAGE PLPGSQL;
6858
6859
6860
6861
6862 SELECT evergreen.upgrade_deps_block_check('1074', :eg_version);
6863
6864 INSERT INTO config.internal_flag (name, enabled) 
6865     VALUES ('ingest.skip_display_indexing', FALSE);
6866
6867 -- Adds seed data to replace (for now) values from the 'mvr' class
6868
6869 UPDATE config.metabib_field SET display_field = TRUE WHERE id IN (6, 8, 16, 18);
6870
6871 INSERT INTO config.metabib_field ( id, field_class, name, label,
6872     format, xpath, display_field, display_xpath ) VALUES
6873     (37, 'author', 'creator', oils_i18n_gettext(37, 'All Creators', 'cmf', 'label'),
6874      'mods32', $$//mods32:mods/mods32:name[mods32:role/mods32:roleTerm[text()='creator']]$$, 
6875      TRUE, $$//*[local-name()='namePart']$$ ); -- /* to fool vim */;
6876
6877 -- 'author' field
6878 UPDATE config.metabib_field SET display_xpath = 
6879     $$//*[local-name()='namePart']$$ -- /* to fool vim */
6880     WHERE id = 8;
6881
6882 INSERT INTO config.display_field_map (name, field, multi) VALUES
6883     ('title', 6, FALSE),
6884     ('author', 8, FALSE),
6885     ('creators', 37, TRUE),
6886     ('subject', 16, TRUE),
6887     ('isbn', 18, TRUE)
6888 ;
6889
6890
6891
6892 SELECT evergreen.upgrade_deps_block_check('1075', :eg_version);
6893
6894 CREATE OR REPLACE FUNCTION evergreen.vandelay_import_item_imported_as_inh_fkey() RETURNS TRIGGER AS $f$
6895 BEGIN   
6896         IF NEW.imported_as IS NULL THEN
6897                 RETURN NEW;
6898         END IF;
6899         PERFORM 1 FROM asset.copy WHERE id = NEW.imported_as;
6900         IF NOT FOUND THEN
6901                 RAISE foreign_key_violation USING MESSAGE = FORMAT(
6902                         $$Referenced asset.copy id not found, imported_as:%s$$, NEW.imported_as
6903                 );
6904         END IF;
6905         RETURN NEW;
6906 END;
6907 $f$ LANGUAGE PLPGSQL VOLATILE COST 50;
6908
6909
6910 COMMIT;
6911
6912 \echo ---------------------------------------------------------------------
6913 \echo Reingest display fields.  This can ban canceled via Ctrl-C and run at
6914 \echo a later time with the following (or similar) SQL:
6915 \echo
6916 \echo 'SELECT metabib.reingest_metabib_field_entries(id, TRUE, FALSE, TRUE, TRUE, '
6917 \echo '    (SELECT ARRAY_AGG(id)::INT[] FROM config.metabib_field WHERE display_field))'
6918 \echo '    FROM biblio.record_entry WHERE NOT deleted AND id > 0;'
6919 \echo
6920
6921 -- REINGEST DISPLAY ENTRIES
6922 SELECT metabib.reingest_metabib_field_entries(id, TRUE, FALSE, TRUE, TRUE, 
6923     (SELECT ARRAY_AGG(id)::INT[] FROM config.metabib_field WHERE display_field))
6924     FROM biblio.record_entry WHERE NOT deleted AND id > 0;
6925
6926