]> 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
LP#1718300: fold update into 2.12.5-3.0-beta schema update
[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
1871 SELECT evergreen.upgrade_deps_block_check('1076', :eg_version); -- miker/gmcharlt
1872
1873 CREATE OR REPLACE FUNCTION asset.cache_copy_visibility () RETURNS TRIGGER as $func$
1874 DECLARE
1875     ocn     asset.call_number%ROWTYPE;
1876     ncn     asset.call_number%ROWTYPE;
1877     cid     BIGINT;
1878 BEGIN
1879
1880     IF TG_TABLE_NAME = 'peer_bib_copy_map' THEN -- Only needs ON INSERT OR DELETE, so handle separately
1881         IF TG_OP = 'INSERT' THEN
1882             INSERT INTO asset.copy_vis_attr_cache (record, target_copy, vis_attr_vector) VALUES (
1883                 NEW.peer_record,
1884                 NEW.target_copy,
1885                 asset.calculate_copy_visibility_attribute_set(NEW.target_copy)
1886             );
1887
1888             RETURN NEW;
1889         ELSIF TG_OP = 'DELETE' THEN
1890             DELETE FROM asset.copy_vis_attr_cache
1891               WHERE record = NEW.peer_record AND target_copy = NEW.target_copy;
1892
1893             RETURN OLD;
1894         END IF;
1895     END IF;
1896
1897     IF TG_OP = 'INSERT' THEN -- Handles ON INSERT. ON UPDATE is below.
1898         IF TG_TABLE_NAME IN ('copy', 'unit') THEN
1899             SELECT * INTO ncn FROM asset.call_number cn WHERE id = NEW.call_number;
1900             INSERT INTO asset.copy_vis_attr_cache (record, target_copy, vis_attr_vector) VALUES (
1901                 ncn.record,
1902                 NEW.id,
1903                 asset.calculate_copy_visibility_attribute_set(NEW.id)
1904             );
1905         ELSIF TG_TABLE_NAME = 'record_entry' THEN
1906             NEW.vis_attr_vector := biblio.calculate_bib_visibility_attribute_set(NEW.id);
1907         END IF;
1908
1909         RETURN NEW;
1910     END IF;
1911
1912     -- handle items first, since with circulation activity
1913     -- their statuses change frequently
1914     IF TG_TABLE_NAME IN ('copy', 'unit') THEN -- This handles ON UPDATE OR DELETE. ON INSERT above
1915
1916         IF TG_OP = 'DELETE' THEN -- Shouldn't get here, normally
1917             DELETE FROM asset.copy_vis_attr_cache WHERE target_copy = OLD.id;
1918             RETURN OLD;
1919         END IF;
1920
1921         SELECT * INTO ncn FROM asset.call_number cn WHERE id = NEW.call_number;
1922
1923         IF OLD.deleted <> NEW.deleted THEN
1924             IF NEW.deleted THEN
1925                 DELETE FROM asset.copy_vis_attr_cache WHERE target_copy = OLD.id;
1926             ELSE
1927                 INSERT INTO asset.copy_vis_attr_cache (record, target_copy, vis_attr_vector) VALUES (
1928                     ncn.record,
1929                     NEW.id,
1930                     asset.calculate_copy_visibility_attribute_set(NEW.id)
1931                 );
1932             END IF;
1933
1934             RETURN NEW;
1935         ELSIF OLD.call_number  <> NEW.call_number THEN
1936             SELECT * INTO ocn FROM asset.call_number cn WHERE id = OLD.call_number;
1937
1938             IF ncn.record <> ocn.record THEN
1939                 UPDATE  biblio.record_entry
1940                   SET   vis_attr_vector = biblio.calculate_bib_visibility_attribute_set(ncn.record)
1941                   WHERE id = ocn.record;
1942
1943                 -- We have to use a record-specific WHERE clause
1944                 -- to avoid modifying the entries for peer-bib copies.
1945                 UPDATE  asset.copy_vis_attr_cache
1946                   SET   target_copy = NEW.id,
1947                         record = ncn.record
1948                   WHERE target_copy = OLD.id
1949                         AND record = ocn.record;
1950             END IF;
1951         END IF;
1952
1953         IF OLD.location     <> NEW.location OR
1954            OLD.status       <> NEW.status OR
1955            OLD.opac_visible <> NEW.opac_visible OR
1956            OLD.circ_lib     <> NEW.circ_lib
1957         THEN
1958             -- Any of these could change visibility, but
1959             -- we'll save some queries and not try to calculate
1960             -- the change directly.  We want to update peer-bib
1961             -- entries in this case, unlike above.
1962             UPDATE  asset.copy_vis_attr_cache
1963               SET   target_copy = NEW.id,
1964                     vis_attr_vector = asset.calculate_copy_visibility_attribute_set(NEW.id)
1965               WHERE target_copy = OLD.id;
1966
1967         END IF;
1968
1969     ELSIF TG_TABLE_NAME = 'call_number' THEN -- Only ON UPDATE. Copy handler will deal with ON INSERT OR DELETE.
1970
1971         IF OLD.record <> NEW.record THEN
1972             IF NEW.label = '##URI##' THEN
1973                 UPDATE  biblio.record_entry
1974                   SET   vis_attr_vector = biblio.calculate_bib_visibility_attribute_set(OLD.record)
1975                   WHERE id = OLD.record;
1976
1977                 UPDATE  biblio.record_entry
1978                   SET   vis_attr_vector = biblio.calculate_bib_visibility_attribute_set(NEW.record)
1979                   WHERE id = NEW.record;
1980             END IF;
1981
1982             UPDATE  asset.copy_vis_attr_cache
1983               SET   record = NEW.record,
1984                     vis_attr_vector = asset.calculate_copy_visibility_attribute_set(target_copy)
1985               WHERE target_copy IN (SELECT id FROM asset.copy WHERE call_number = NEW.id)
1986                     AND record = OLD.record;
1987
1988         ELSIF OLD.owning_lib <> NEW.owning_lib THEN
1989             UPDATE  asset.copy_vis_attr_cache
1990               SET   vis_attr_vector = asset.calculate_copy_visibility_attribute_set(target_copy)
1991               WHERE target_copy IN (SELECT id FROM asset.copy WHERE call_number = NEW.id)
1992                     AND record = NEW.record;
1993
1994             IF NEW.label = '##URI##' THEN
1995                 UPDATE  biblio.record_entry
1996                   SET   vis_attr_vector = biblio.calculate_bib_visibility_attribute_set(OLD.record)
1997                   WHERE id = OLD.record;
1998             END IF;
1999         END IF;
2000
2001     ELSIF TG_TABLE_NAME = 'record_entry' THEN -- Only handles ON UPDATE OR DELETE
2002
2003         IF TG_OP = 'DELETE' THEN -- Shouldn't get here, normally
2004             DELETE FROM asset.copy_vis_attr_cache WHERE record = OLD.id;
2005             RETURN OLD;
2006         ELSIF OLD.source <> NEW.source THEN
2007             NEW.vis_attr_vector := biblio.calculate_bib_visibility_attribute_set(NEW.id);
2008         END IF;
2009
2010     END IF;
2011
2012     RETURN NEW;
2013 END;
2014 $func$ LANGUAGE PLPGSQL;
2015
2016
2017 -- Helper functions for use in constructing searches --
2018
2019 CREATE OR REPLACE FUNCTION asset.all_visible_flags () RETURNS TEXT AS $f$
2020     SELECT  '(' || ARRAY_TO_STRING(ARRAY_AGG(search.calculate_visibility_attribute(1 << x, 'copy_flags')),'&') || ')'
2021       FROM  GENERATE_SERIES(0,0) AS x; -- increment as new flags are added.
2022 $f$ LANGUAGE SQL STABLE;
2023
2024 CREATE OR REPLACE FUNCTION asset.visible_orgs (otype TEXT) RETURNS TEXT AS $f$
2025     SELECT  '(' || ARRAY_TO_STRING(ARRAY_AGG(search.calculate_visibility_attribute(id, $1)),'|') || ')'
2026       FROM  actor.org_unit
2027       WHERE opac_visible;
2028 $f$ LANGUAGE SQL STABLE;
2029
2030 CREATE OR REPLACE FUNCTION asset.invisible_orgs (otype TEXT) RETURNS TEXT AS $f$
2031     SELECT  '!(' || ARRAY_TO_STRING(ARRAY_AGG(search.calculate_visibility_attribute(id, $1)),'|') || ')'
2032       FROM  actor.org_unit
2033       WHERE NOT opac_visible;
2034 $f$ LANGUAGE SQL STABLE;
2035
2036 -- Bib-oriented defaults for search
2037 CREATE OR REPLACE FUNCTION asset.bib_source_default () RETURNS TEXT AS $f$
2038     SELECT  '(' || ARRAY_TO_STRING(ARRAY_AGG(search.calculate_visibility_attribute(id, 'bib_source')),'|') || ')'
2039       FROM  config.bib_source
2040       WHERE transcendant;
2041 $f$ LANGUAGE SQL IMMUTABLE;
2042
2043 CREATE OR REPLACE FUNCTION asset.luri_org_default () RETURNS TEXT AS $f$
2044     SELECT  * FROM asset.invisible_orgs('luri_org');
2045 $f$ LANGUAGE SQL STABLE;
2046
2047 -- Copy-oriented defaults for search
2048 CREATE OR REPLACE FUNCTION asset.location_group_default () RETURNS TEXT AS $f$
2049     SELECT  '!(' || ARRAY_TO_STRING(ARRAY_AGG(search.calculate_visibility_attribute(id, 'location_group')),'|') || ')'
2050       FROM  asset.copy_location_group
2051       WHERE NOT opac_visible;
2052 $f$ LANGUAGE SQL STABLE;
2053
2054 CREATE OR REPLACE FUNCTION asset.location_default () RETURNS TEXT AS $f$
2055     SELECT  '!(' || ARRAY_TO_STRING(ARRAY_AGG(search.calculate_visibility_attribute(id, 'location')),'|') || ')'
2056       FROM  asset.copy_location
2057       WHERE NOT opac_visible;
2058 $f$ LANGUAGE SQL STABLE;
2059
2060 CREATE OR REPLACE FUNCTION asset.status_default () RETURNS TEXT AS $f$
2061     SELECT  '!(' || ARRAY_TO_STRING(ARRAY_AGG(search.calculate_visibility_attribute(id, 'status')),'|') || ')'
2062       FROM  config.copy_status
2063       WHERE NOT opac_visible;
2064 $f$ LANGUAGE SQL STABLE;
2065
2066 CREATE OR REPLACE FUNCTION asset.owning_lib_default () RETURNS TEXT AS $f$
2067     SELECT  * FROM asset.invisible_orgs('owning_lib');
2068 $f$ LANGUAGE SQL STABLE;
2069
2070 CREATE OR REPLACE FUNCTION asset.circ_lib_default () RETURNS TEXT AS $f$
2071     SELECT  * FROM asset.invisible_orgs('circ_lib');
2072 $f$ LANGUAGE SQL STABLE;
2073
2074 CREATE OR REPLACE FUNCTION asset.patron_default_visibility_mask () RETURNS TABLE (b_attrs TEXT, c_attrs TEXT)  AS $f$
2075 DECLARE
2076     copy_flags      TEXT; -- "c" attr
2077
2078     owning_lib      TEXT; -- "c" attr
2079     circ_lib        TEXT; -- "c" attr
2080     status          TEXT; -- "c" attr
2081     location        TEXT; -- "c" attr
2082     location_group  TEXT; -- "c" attr
2083
2084     luri_org        TEXT; -- "b" attr
2085     bib_sources     TEXT; -- "b" attr
2086 BEGIN
2087     copy_flags      := asset.all_visible_flags(); -- Will always have at least one
2088
2089     owning_lib      := NULLIF(asset.owning_lib_default(),'!()');
2090     
2091     circ_lib        := NULLIF(asset.circ_lib_default(),'!()');
2092     status          := NULLIF(asset.status_default(),'!()');
2093     location        := NULLIF(asset.location_default(),'!()');
2094     location_group  := NULLIF(asset.location_group_default(),'!()');
2095
2096     luri_org        := NULLIF(asset.luri_org_default(),'!()');
2097     bib_sources     := NULLIF(asset.bib_source_default(),'()');
2098
2099     RETURN QUERY SELECT
2100         '('||ARRAY_TO_STRING(
2101             ARRAY[luri_org,bib_sources],
2102             '|'
2103         )||')',
2104         '('||ARRAY_TO_STRING(
2105             ARRAY[copy_flags,owning_lib,circ_lib,status,location,location_group]::TEXT[],
2106             '&'
2107         )||')';
2108 END;
2109 $f$ LANGUAGE PLPGSQL STABLE ROWS 1;
2110
2111 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)
2112  RETURNS TABLE(value text, field integer, buoyant_and_class_match boolean, field_match boolean, field_weight integer, rank real, buoyant boolean, match text)
2113 AS $f$
2114 DECLARE
2115     prepared_query_texts    TEXT[];
2116     query                   TSQUERY;
2117     plain_query             TSQUERY;
2118     opac_visibility_join    TEXT;
2119     search_class_join       TEXT;
2120     r_fields                RECORD;
2121 BEGIN
2122     prepared_query_texts := metabib.autosuggest_prepare_tsquery(raw_query_text);
2123
2124     query := TO_TSQUERY('keyword', prepared_query_texts[1]);
2125     plain_query := TO_TSQUERY('keyword', prepared_query_texts[2]);
2126
2127     visibility_org := NULLIF(visibility_org,-1);
2128     IF visibility_org IS NOT NULL THEN
2129         PERFORM FROM actor.org_unit WHERE id = visibility_org AND parent_ou IS NULL;
2130         IF FOUND THEN
2131             opac_visibility_join := '';
2132         ELSE
2133             opac_visibility_join := '
2134     JOIN asset.copy_vis_attr_cache acvac ON (acvac.record = x.source)
2135     JOIN vm ON (acvac.vis_attr_vector @@
2136             (vm.c_attrs || $$&$$ ||
2137                 search.calculate_visibility_attribute_test(
2138                     $$circ_lib$$,
2139                     (SELECT ARRAY_AGG(id) FROM actor.org_unit_descendants($4))
2140                 )
2141             )::query_int
2142          )
2143 ';
2144         END IF;
2145     ELSE
2146         opac_visibility_join := '';
2147     END IF;
2148
2149     -- The following determines whether we only provide suggestsons matching
2150     -- the user's selected search_class, or whether we show other suggestions
2151     -- too. The reason for MIN() is that for search_classes like
2152     -- 'title|proper|uniform' you would otherwise get multiple rows.  The
2153     -- implication is that if title as a class doesn't have restrict,
2154     -- nor does the proper field, but the uniform field does, you're going
2155     -- to get 'false' for your overall evaluation of 'should we restrict?'
2156     -- To invert that, change from MIN() to MAX().
2157
2158     SELECT
2159         INTO r_fields
2160             MIN(cmc.restrict::INT) AS restrict_class,
2161             MIN(cmf.restrict::INT) AS restrict_field
2162         FROM metabib.search_class_to_registered_components(search_class)
2163             AS _registered (field_class TEXT, field INT)
2164         JOIN
2165             config.metabib_class cmc ON (cmc.name = _registered.field_class)
2166         LEFT JOIN
2167             config.metabib_field cmf ON (cmf.id = _registered.field);
2168
2169     -- evaluate 'should we restrict?'
2170     IF r_fields.restrict_field::BOOL OR r_fields.restrict_class::BOOL THEN
2171         search_class_join := '
2172     JOIN
2173         metabib.search_class_to_registered_components($2)
2174         AS _registered (field_class TEXT, field INT) ON (
2175             (_registered.field IS NULL AND
2176                 _registered.field_class = cmf.field_class) OR
2177             (_registered.field = cmf.id)
2178         )
2179     ';
2180     ELSE
2181         search_class_join := '
2182     LEFT JOIN
2183         metabib.search_class_to_registered_components($2)
2184         AS _registered (field_class TEXT, field INT) ON (
2185             _registered.field_class = cmc.name
2186         )
2187     ';
2188     END IF;
2189
2190     RETURN QUERY EXECUTE '
2191 WITH vm AS ( SELECT * FROM asset.patron_default_visibility_mask() ),
2192      mbe AS (SELECT * FROM metabib.browse_entry WHERE index_vector @@ $1 LIMIT 10000)
2193 SELECT  DISTINCT
2194         x.value,
2195         x.id,
2196         x.push,
2197         x.restrict,
2198         x.weight,
2199         x.ts_rank_cd,
2200         x.buoyant,
2201         TS_HEADLINE(value, $7, $3)
2202   FROM  (SELECT DISTINCT
2203                 mbe.value,
2204                 cmf.id,
2205                 cmc.buoyant AND _registered.field_class IS NOT NULL AS push,
2206                 _registered.field = cmf.id AS restrict,
2207                 cmf.weight,
2208                 TS_RANK_CD(mbe.index_vector, $1, $6),
2209                 cmc.buoyant,
2210                 mbedm.source
2211           FROM  metabib.browse_entry_def_map mbedm
2212                 JOIN mbe ON (mbe.id = mbedm.entry)
2213                 JOIN config.metabib_field cmf ON (cmf.id = mbedm.def)
2214                 JOIN config.metabib_class cmc ON (cmf.field_class = cmc.name)
2215                 '  || search_class_join || '
2216           ORDER BY 3 DESC, 4 DESC NULLS LAST, 5 DESC, 6 DESC, 7 DESC, 1 ASC
2217           LIMIT 1000) AS x
2218         ' || opac_visibility_join || '
2219   ORDER BY 3 DESC, 4 DESC NULLS LAST, 5 DESC, 6 DESC, 7 DESC, 1 ASC
2220   LIMIT $5
2221 '   -- sic, repeat the order by clause in the outer select too
2222     USING
2223         query, search_class, headline_opts,
2224         visibility_org, query_limit, normalization, plain_query
2225         ;
2226
2227     -- sort order:
2228     --  buoyant AND chosen class = match class
2229     --  chosen field = match field
2230     --  field weight
2231     --  rank
2232     --  buoyancy
2233     --  value itself
2234
2235 END;
2236 $f$ LANGUAGE plpgsql ROWS 10;
2237
2238 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)
2239  RETURNS SETOF metabib.flat_browse_entry_appearance
2240 AS $f$
2241 DECLARE
2242     core_query              TEXT;
2243     back_query              TEXT;
2244     forward_query           TEXT;
2245     pivot_sort_value        TEXT;
2246     pivot_sort_fallback     TEXT;
2247     context_locations       INT[];
2248     browse_superpage_size   INT;
2249     results_skipped         INT := 0;
2250     back_limit              INT;
2251     back_to_pivot           INT;
2252     forward_limit           INT;
2253     forward_to_pivot        INT;
2254 BEGIN
2255     -- First, find the pivot if we were given a browse term but not a pivot.
2256     IF pivot_id IS NULL THEN
2257         pivot_id := metabib.browse_pivot(search_field, browse_term);
2258     END IF;
2259
2260     SELECT INTO pivot_sort_value, pivot_sort_fallback
2261         sort_value, value FROM metabib.browse_entry WHERE id = pivot_id;
2262
2263     -- Bail if we couldn't find a pivot.
2264     IF pivot_sort_value IS NULL THEN
2265         RETURN;
2266     END IF;
2267
2268     -- Transform the context_loc_group argument (if any) (logc at the
2269     -- TPAC layer) into a form we'll be able to use.
2270     IF context_loc_group IS NOT NULL THEN
2271         SELECT INTO context_locations ARRAY_AGG(location)
2272             FROM asset.copy_location_group_map
2273             WHERE lgroup = context_loc_group;
2274     END IF;
2275
2276     -- Get the configured size of browse superpages.
2277     SELECT INTO browse_superpage_size COALESCE(value::INT,100)     -- NULL ok
2278         FROM config.global_flag
2279         WHERE enabled AND name = 'opac.browse.holdings_visibility_test_limit';
2280
2281     -- First we're going to search backward from the pivot, then we're going
2282     -- to search forward.  In each direction, we need two limits.  At the
2283     -- lesser of the two limits, we delineate the edge of the result set
2284     -- we're going to return.  At the greater of the two limits, we find the
2285     -- pivot value that would represent an offset from the current pivot
2286     -- at a distance of one "page" in either direction, where a "page" is a
2287     -- result set of the size specified in the "result_limit" argument.
2288     --
2289     -- The two limits in each direction make four derived values in total,
2290     -- and we calculate them now.
2291     back_limit := CEIL(result_limit::FLOAT / 2);
2292     back_to_pivot := result_limit;
2293     forward_limit := result_limit / 2;
2294     forward_to_pivot := result_limit - 1;
2295
2296     -- This is the meat of the SQL query that finds browse entries.  We'll
2297     -- pass this to a function which uses it with a cursor, so that individual
2298     -- rows may be fetched in a loop until some condition is satisfied, without
2299     -- waiting for a result set of fixed size to be collected all at once.
2300     core_query := '
2301 SELECT  mbe.id,
2302         mbe.value,
2303         mbe.sort_value
2304   FROM  metabib.browse_entry mbe
2305   WHERE (
2306             EXISTS ( -- are there any bibs using this mbe via the requested fields?
2307                 SELECT  1
2308                   FROM  metabib.browse_entry_def_map mbedm
2309                   WHERE mbedm.entry = mbe.id AND mbedm.def = ANY(' || quote_literal(search_field) || ')
2310             ) OR EXISTS ( -- are there any authorities using this mbe via the requested fields?
2311                 SELECT  1
2312                   FROM  metabib.browse_entry_simple_heading_map mbeshm
2313                         JOIN authority.simple_heading ash ON ( mbeshm.simple_heading = ash.id )
2314                         JOIN authority.control_set_auth_field_metabib_field_map_refs map ON (
2315                             ash.atag = map.authority_field
2316                             AND map.metabib_field = ANY(' || quote_literal(search_field) || ')
2317                         )
2318                   WHERE mbeshm.entry = mbe.id
2319             )
2320         ) AND ';
2321
2322     -- This is the variant of the query for browsing backward.
2323     back_query := core_query ||
2324         ' mbe.sort_value <= ' || quote_literal(pivot_sort_value) ||
2325     ' ORDER BY mbe.sort_value DESC, mbe.value DESC LIMIT 1000';
2326
2327     -- This variant browses forward.
2328     forward_query := core_query ||
2329         ' mbe.sort_value > ' || quote_literal(pivot_sort_value) ||
2330     ' ORDER BY mbe.sort_value, mbe.value LIMIT 1000';
2331
2332     -- We now call the function which applies a cursor to the provided
2333     -- queries, stopping at the appropriate limits and also giving us
2334     -- the next page's pivot.
2335     RETURN QUERY
2336         SELECT * FROM metabib.staged_browse(
2337             back_query, search_field, context_org, context_locations,
2338             staff, browse_superpage_size, TRUE, back_limit, back_to_pivot
2339         ) UNION
2340         SELECT * FROM metabib.staged_browse(
2341             forward_query, search_field, context_org, context_locations,
2342             staff, browse_superpage_size, FALSE, forward_limit, forward_to_pivot
2343         ) ORDER BY row_number DESC;
2344
2345 END;
2346 $f$ LANGUAGE plpgsql ROWS 10;
2347
2348 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)
2349  RETURNS SETOF metabib.flat_browse_entry_appearance
2350 AS $f$
2351 DECLARE
2352     curs                    REFCURSOR;
2353     rec                     RECORD;
2354     qpfts_query             TEXT;
2355     aqpfts_query            TEXT;
2356     afields                 INT[];
2357     bfields                 INT[];
2358     result_row              metabib.flat_browse_entry_appearance%ROWTYPE;
2359     results_skipped         INT := 0;
2360     row_counter             INT := 0;
2361     row_number              INT;
2362     slice_start             INT;
2363     slice_end               INT;
2364     full_end                INT;
2365     all_records             BIGINT[];
2366     all_brecords             BIGINT[];
2367     all_arecords            BIGINT[];
2368     superpage_of_records    BIGINT[];
2369     superpage_size          INT;
2370     c_tests                 TEXT := '';
2371     b_tests                 TEXT := '';
2372     c_orgs                  INT[];
2373 BEGIN
2374     IF count_up_from_zero THEN
2375         row_number := 0;
2376     ELSE
2377         row_number := -1;
2378     END IF;
2379
2380     IF NOT staff THEN
2381         SELECT x.c_attrs, x.b_attrs INTO c_tests, b_tests FROM asset.patron_default_visibility_mask() x;
2382     END IF;
2383
2384     IF c_tests <> '' THEN c_tests := c_tests || '&'; END IF;
2385     IF b_tests <> '' THEN b_tests := b_tests || '&'; END IF;
2386
2387     SELECT ARRAY_AGG(id) INTO c_orgs FROM actor.org_unit_descendants(context_org);
2388     
2389     c_tests := c_tests || search.calculate_visibility_attribute_test('circ_lib',c_orgs)
2390                || '&' || search.calculate_visibility_attribute_test('owning_lib',c_orgs);
2391     
2392     PERFORM 1 FROM config.internal_flag WHERE enabled AND name = 'opac.located_uri.act_as_copy';
2393     IF FOUND THEN
2394         b_tests := b_tests || search.calculate_visibility_attribute_test(
2395             'luri_org',
2396             (SELECT ARRAY_AGG(id) FROM actor.org_unit_full_path(context_org) x)
2397         );
2398     ELSE
2399         b_tests := b_tests || search.calculate_visibility_attribute_test(
2400             'luri_org',
2401             (SELECT ARRAY_AGG(id) FROM actor.org_unit_ancestors(context_org) x)
2402         );
2403     END IF;
2404
2405     IF context_locations THEN
2406         IF c_tests <> '' THEN c_tests := c_tests || '&'; END IF;
2407         c_tests := c_tests || search.calculate_visibility_attribute_test('location',context_locations);
2408     END IF;
2409
2410     OPEN curs NO SCROLL FOR EXECUTE query;
2411
2412     LOOP
2413         FETCH curs INTO rec;
2414         IF NOT FOUND THEN
2415             IF result_row.pivot_point IS NOT NULL THEN
2416                 RETURN NEXT result_row;
2417             END IF;
2418             RETURN;
2419         END IF;
2420
2421         -- Gather aggregate data based on the MBE row we're looking at now, authority axis
2422         SELECT INTO all_arecords, result_row.sees, afields
2423                 ARRAY_AGG(DISTINCT abl.bib), -- bibs to check for visibility
2424                 STRING_AGG(DISTINCT aal.source::TEXT, $$,$$), -- authority record ids
2425                 ARRAY_AGG(DISTINCT map.metabib_field) -- authority-tag-linked CMF rows
2426
2427           FROM  metabib.browse_entry_simple_heading_map mbeshm
2428                 JOIN authority.simple_heading ash ON ( mbeshm.simple_heading = ash.id )
2429                 JOIN authority.authority_linking aal ON ( ash.record = aal.source )
2430                 JOIN authority.bib_linking abl ON ( aal.target = abl.authority )
2431                 JOIN authority.control_set_auth_field_metabib_field_map_refs map ON (
2432                     ash.atag = map.authority_field
2433                     AND map.metabib_field = ANY(fields)
2434                 )
2435           WHERE mbeshm.entry = rec.id;
2436
2437         -- Gather aggregate data based on the MBE row we're looking at now, bib axis
2438         SELECT INTO all_brecords, result_row.authorities, bfields
2439                 ARRAY_AGG(DISTINCT source),
2440                 STRING_AGG(DISTINCT authority::TEXT, $$,$$),
2441                 ARRAY_AGG(DISTINCT def)
2442           FROM  metabib.browse_entry_def_map
2443           WHERE entry = rec.id
2444                 AND def = ANY(fields);
2445
2446         SELECT INTO result_row.fields STRING_AGG(DISTINCT x::TEXT, $$,$$) FROM UNNEST(afields || bfields) x;
2447
2448         result_row.sources := 0;
2449         result_row.asources := 0;
2450
2451         -- Bib-linked vis checking
2452         IF ARRAY_UPPER(all_brecords,1) IS NOT NULL THEN
2453
2454             SELECT  INTO result_row.sources COUNT(DISTINCT b.id)
2455               FROM  biblio.record_entry b
2456                     JOIN asset.copy_vis_attr_cache acvac ON (acvac.record = b.id)
2457               WHERE b.id = ANY(all_brecords[1:browse_superpage_size])
2458                     AND (
2459                         acvac.vis_attr_vector @@ c_tests::query_int
2460                         OR b.vis_attr_vector @@ b_tests::query_int
2461                     );
2462
2463             result_row.accurate := TRUE;
2464
2465         END IF;
2466
2467         -- Authority-linked vis checking
2468         IF ARRAY_UPPER(all_arecords,1) IS NOT NULL THEN
2469
2470             SELECT  INTO result_row.asources COUNT(DISTINCT b.id)
2471               FROM  biblio.record_entry b
2472                     JOIN asset.copy_vis_attr_cache acvac ON (acvac.record = b.id)
2473               WHERE b.id = ANY(all_arecords[1:browse_superpage_size])
2474                     AND (
2475                         acvac.vis_attr_vector @@ c_tests::query_int
2476                         OR b.vis_attr_vector @@ b_tests::query_int
2477                     );
2478
2479             result_row.aaccurate := TRUE;
2480
2481         END IF;
2482
2483         IF result_row.sources > 0 OR result_row.asources > 0 THEN
2484
2485             -- The function that calls this function needs row_number in order
2486             -- to correctly order results from two different runs of this
2487             -- functions.
2488             result_row.row_number := row_number;
2489
2490             -- Now, if row_counter is still less than limit, return a row.  If
2491             -- not, but it is less than next_pivot_pos, continue on without
2492             -- returning actual result rows until we find
2493             -- that next pivot, and return it.
2494
2495             IF row_counter < result_limit THEN
2496                 result_row.browse_entry := rec.id;
2497                 result_row.value := rec.value;
2498
2499                 RETURN NEXT result_row;
2500             ELSE
2501                 result_row.browse_entry := NULL;
2502                 result_row.authorities := NULL;
2503                 result_row.fields := NULL;
2504                 result_row.value := NULL;
2505                 result_row.sources := NULL;
2506                 result_row.sees := NULL;
2507                 result_row.accurate := NULL;
2508                 result_row.aaccurate := NULL;
2509                 result_row.pivot_point := rec.id;
2510
2511                 IF row_counter >= next_pivot_pos THEN
2512                     RETURN NEXT result_row;
2513                     RETURN;
2514                 END IF;
2515             END IF;
2516
2517             IF count_up_from_zero THEN
2518                 row_number := row_number + 1;
2519             ELSE
2520                 row_number := row_number - 1;
2521             END IF;
2522
2523             -- row_counter is different from row_number.
2524             -- It simply counts up from zero so that we know when
2525             -- we've reached our limit.
2526             row_counter := row_counter + 1;
2527         END IF;
2528     END LOOP;
2529 END;
2530 $f$ LANGUAGE plpgsql ROWS 10;
2531
2532 DROP TRIGGER IF EXISTS a_opac_vis_mat_view_tgr ON biblio.peer_bib_copy_map;
2533 DROP TRIGGER IF EXISTS a_opac_vis_mat_view_tgr ON biblio.record_entry;
2534 DROP TRIGGER IF EXISTS a_opac_vis_mat_view_tgr ON asset.copy;
2535 DROP TRIGGER IF EXISTS a_opac_vis_mat_view_tgr ON asset.call_number;
2536 DROP TRIGGER IF EXISTS a_opac_vis_mat_view_tgr ON asset.copy_location;
2537 DROP TRIGGER IF EXISTS a_opac_vis_mat_view_tgr ON serial.unit;
2538 DROP TRIGGER IF EXISTS a_opac_vis_mat_view_tgr ON config.copy_status;
2539 DROP TRIGGER IF EXISTS a_opac_vis_mat_view_tgr ON actor.org_unit;
2540
2541 -- Upgrade the data!
2542 INSERT INTO asset.copy_vis_attr_cache (target_copy, record, vis_attr_vector)
2543     SELECT  cp.id,
2544             cn.record,
2545             asset.calculate_copy_visibility_attribute_set(cp.id)
2546       FROM  asset.copy cp
2547             JOIN asset.call_number cn ON (cp.call_number = cn.id);
2548
2549 UPDATE biblio.record_entry SET vis_attr_vector = biblio.calculate_bib_visibility_attribute_set(id);
2550
2551 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();
2552 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();
2553 CREATE TRIGGER z_opac_vis_mat_view_tgr AFTER UPDATE ON asset.call_number FOR EACH ROW EXECUTE PROCEDURE asset.cache_copy_visibility();
2554 CREATE TRIGGER z_opac_vis_mat_view_del_tgr BEFORE DELETE ON asset.copy FOR EACH ROW EXECUTE PROCEDURE asset.cache_copy_visibility();
2555 CREATE TRIGGER z_opac_vis_mat_view_del_tgr BEFORE DELETE ON serial.unit FOR EACH ROW EXECUTE PROCEDURE asset.cache_copy_visibility();
2556 CREATE TRIGGER z_opac_vis_mat_view_tgr AFTER INSERT OR UPDATE ON asset.copy FOR EACH ROW EXECUTE PROCEDURE asset.cache_copy_visibility();
2557 CREATE TRIGGER z_opac_vis_mat_view_tgr AFTER INSERT OR UPDATE ON serial.unit FOR EACH ROW EXECUTE PROCEDURE asset.cache_copy_visibility();
2558
2559 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$
2560 DECLARE
2561     ans RECORD;
2562     trans INT;
2563 BEGIN
2564     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;
2565
2566     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
2567         RETURN QUERY
2568         WITH org_list AS (SELECT ARRAY_AGG(id)::BIGINT[] AS orgs FROM actor.org_unit_descendants(ans.id) x),
2569              available_statuses AS (SELECT ARRAY_AGG(id) AS ids FROM config.copy_status WHERE is_available),
2570              mask AS (SELECT c_attrs FROM asset.patron_default_visibility_mask() x)
2571         SELECT  ans.depth,
2572                 ans.id,
2573                 COUNT( av.id ),
2574                 SUM( (cp.status = ANY (available_statuses.ids))::INT ),
2575                 COUNT( av.id ),
2576                 trans
2577           FROM  mask,
2578                 available_statuses,
2579                 org_list,
2580                 asset.copy_vis_attr_cache av
2581                 JOIN asset.copy cp ON (cp.id = av.target_copy AND av.record = rid)
2582           WHERE cp.circ_lib = ANY (org_list.orgs) AND av.vis_attr_vector @@ mask.c_attrs::query_int
2583           GROUP BY 1,2,6;
2584
2585         IF NOT FOUND THEN
2586             RETURN QUERY SELECT ans.depth, ans.id, 0::BIGINT, 0::BIGINT, 0::BIGINT, trans;
2587         END IF;
2588
2589     END LOOP;
2590
2591     RETURN;
2592 END;
2593 $f$ LANGUAGE PLPGSQL;
2594
2595 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$
2596 DECLARE
2597     ans RECORD;
2598     trans INT;
2599 BEGIN
2600     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;
2601
2602     FOR ans IN SELECT u.org_unit AS id FROM actor.org_lasso_map AS u WHERE lasso = i_lasso LOOP
2603         RETURN QUERY
2604         WITH org_list AS (SELECT ARRAY_AGG(id)::BIGINT[] AS orgs FROM actor.org_unit_descendants(ans.id) x),
2605              available_statuses AS (SELECT ARRAY_AGG(id) AS ids FROM config.copy_status WHERE is_available),
2606              mask AS (SELECT c_attrs FROM asset.patron_default_visibility_mask() x)
2607         SELECT  -1,
2608                 ans.id,
2609                 COUNT( av.id ),
2610                 SUM( (cp.status = ANY (available_statuses.ids))::INT ),
2611                 COUNT( av.id ),
2612                 trans
2613           FROM  mask,
2614                 org_list,
2615                 asset.copy_vis_attr_cache av
2616                 JOIN asset.copy cp ON (cp.id = av.target_copy AND av.record = rid)
2617           WHERE cp.circ_lib = ANY (org_list.orgs) AND av.vis_attr_vector @@ mask.c_attrs::query_int
2618           GROUP BY 1,2,6;
2619
2620         IF NOT FOUND THEN
2621             RETURN QUERY SELECT -1, ans.id, 0::BIGINT, 0::BIGINT, 0::BIGINT, trans;
2622         END IF;
2623
2624     END LOOP;
2625
2626     RETURN;
2627 END;
2628 $f$ LANGUAGE PLPGSQL;
2629
2630 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$
2631 DECLARE
2632     ans RECORD;
2633     trans INT;
2634 BEGIN
2635     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;
2636
2637     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
2638         RETURN QUERY
2639         WITH org_list AS (SELECT ARRAY_AGG(id)::BIGINT[] AS orgs FROM actor.org_unit_descendants(ans.id) x),
2640              available_statuses AS (SELECT ARRAY_AGG(id) AS ids FROM config.copy_status WHERE is_available),
2641              mask AS (SELECT c_attrs FROM asset.patron_default_visibility_mask() x)
2642         SELECT  ans.depth,
2643                 ans.id,
2644                 COUNT( av.id ),
2645                 SUM( (cp.status = ANY (available_statuses.ids))::INT ),
2646                 COUNT( av.id ),
2647                 trans
2648           FROM  mask,
2649                 org_list,
2650                 available_statuses,
2651                 asset.copy_vis_attr_cache av
2652                 JOIN asset.copy cp ON (cp.id = av.target_copy)
2653                 JOIN metabib.metarecord_source_map m ON (m.metarecord = rid AND m.source = av.record)
2654           WHERE cp.circ_lib = ANY (org_list.orgs) AND av.vis_attr_vector @@ mask.c_attrs::query_int
2655           GROUP BY 1,2,6;
2656
2657         IF NOT FOUND THEN
2658             RETURN QUERY SELECT ans.depth, ans.id, 0::BIGINT, 0::BIGINT, 0::BIGINT, trans;
2659         END IF;
2660
2661     END LOOP;
2662
2663     RETURN;
2664 END;
2665 $f$ LANGUAGE PLPGSQL;
2666
2667 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$
2668 DECLARE
2669     ans RECORD;
2670     trans INT;
2671 BEGIN
2672     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;
2673
2674     FOR ans IN SELECT u.org_unit AS id FROM actor.org_lasso_map AS u WHERE lasso = i_lasso LOOP
2675         RETURN QUERY
2676         WITH org_list AS (SELECT ARRAY_AGG(id)::BIGINT[] AS orgs FROM actor.org_unit_descendants(ans.id) x),
2677              available_statuses AS (SELECT ARRAY_AGG(id) AS ids FROM config.copy_status WHERE is_available),
2678              mask AS (SELECT c_attrs FROM asset.patron_default_visibility_mask() x)
2679         SELECT  -1,
2680                 ans.id,
2681                 COUNT( av.id ),
2682                 SUM( (cp.status = ANY (available_statuses.ids))::INT ),
2683                 COUNT( av.id ),
2684                 trans
2685           FROM  mask,
2686                 org_list,
2687                 available_statuses,
2688                 asset.copy_vis_attr_cache av
2689                 JOIN asset.copy cp ON (cp.id = av.target_copy)
2690                 JOIN metabib.metarecord_source_map m ON (m.metarecord = rid AND m.source = av.record)
2691           WHERE cp.circ_lib = ANY (org_list.orgs) AND av.vis_attr_vector @@ mask.c_attrs::query_int
2692           GROUP BY 1,2,6;
2693
2694         IF NOT FOUND THEN
2695             RETURN QUERY SELECT ans.depth, ans.id, 0::BIGINT, 0::BIGINT, 0::BIGINT, trans;
2696         END IF;
2697
2698     END LOOP;
2699
2700     RETURN;
2701 END;
2702 $f$ LANGUAGE PLPGSQL;
2703
2704 CREATE OR REPLACE FUNCTION unapi.mmr_mra (
2705     obj_id BIGINT,
2706     format TEXT,
2707     ename TEXT,
2708     includes TEXT[],
2709     org TEXT,
2710     depth INT DEFAULT NULL,
2711     slimit HSTORE DEFAULT NULL,
2712     soffset HSTORE DEFAULT NULL,
2713     include_xmlns BOOL DEFAULT TRUE,
2714     pref_lib INT DEFAULT NULL
2715 ) RETURNS XML AS $F$
2716     SELECT  XMLELEMENT(
2717         name attributes,
2718         XMLATTRIBUTES(
2719             CASE WHEN $9 THEN 'http://open-ils.org/spec/indexing/v1' ELSE NULL END AS xmlns,
2720             'tag:open-ils.org:U2@mmr/' || $1 AS metarecord
2721         ),
2722         (SELECT XMLAGG(foo.y)
2723           FROM (
2724             WITH sourcelist AS (
2725                 WITH aou AS (SELECT COALESCE(id, (evergreen.org_top()).id) AS id FROM actor.org_unit WHERE shortname = $5 LIMIT 1),
2726                      basevm AS (SELECT c_attrs FROM  asset.patron_default_visibility_mask()),
2727                      circvm AS (SELECT search.calculate_visibility_attribute_test('circ_lib', ARRAY_AGG(aoud.id)) AS mask
2728                                   FROM aou, LATERAL actor.org_unit_descendants(aou.id, $6) aoud)
2729                 SELECT  source
2730                   FROM  aou, circvm, basevm, metabib.metarecord_source_map mmsm
2731                   WHERE mmsm.metarecord = $1 AND (
2732                     EXISTS (
2733                         SELECT  1
2734                           FROM  circvm, basevm, asset.copy_vis_attr_cache acvac
2735                           WHERE acvac.vis_attr_vector @@ (basevm.c_attrs || '&' || circvm.mask)::query_int
2736                                 AND acvac.record = mmsm.source
2737                     )
2738                     OR EXISTS (SELECT 1 FROM evergreen.located_uris(source, aou.id, $10) LIMIT 1)
2739                     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)
2740                 )
2741             )
2742             SELECT  cmra.aid,
2743                     XMLELEMENT(
2744                         name field,
2745                         XMLATTRIBUTES(
2746                             cmra.attr AS name,
2747                             cmra.value AS "coded-value",
2748                             cmra.aid AS "cvmid",
2749                             rad.composite,
2750                             rad.multi,
2751                             rad.filter,
2752                             rad.sorter,
2753                             cmra.source_list
2754                         ),
2755                         cmra.value
2756                     )
2757               FROM  (
2758                 SELECT DISTINCT aid, attr, value, STRING_AGG(x.id::TEXT, ',') AS source_list
2759                   FROM (
2760                     SELECT  v.source AS id,
2761                             c.id AS aid,
2762                             c.ctype AS attr,
2763                             c.code AS value
2764                       FROM  metabib.record_attr_vector_list v
2765                             JOIN config.coded_value_map c ON ( c.id = ANY( v.vlist ) )
2766                     ) AS x
2767                     JOIN sourcelist ON (x.id = sourcelist.source)
2768                     GROUP BY 1, 2, 3
2769                 ) AS cmra
2770                 JOIN config.record_attr_definition rad ON (cmra.attr = rad.name)
2771                 UNION ALL
2772             SELECT  umra.aid,
2773                     XMLELEMENT(
2774                         name field,
2775                         XMLATTRIBUTES(
2776                             umra.attr AS name,
2777                             rad.composite,
2778                             rad.multi,
2779                             rad.filter,
2780                             rad.sorter
2781                         ),
2782                         umra.value
2783                     )
2784               FROM  (
2785                 SELECT DISTINCT aid, attr, value
2786                   FROM (
2787                     SELECT  v.source AS id,
2788                             m.id AS aid,
2789                             m.attr AS attr,
2790                             m.value AS value
2791                       FROM  metabib.record_attr_vector_list v
2792                             JOIN metabib.uncontrolled_record_attr_value m ON ( m.id = ANY( v.vlist ) )
2793                     ) AS x
2794                     JOIN sourcelist ON (x.id = sourcelist.source)
2795                 ) AS umra
2796                 JOIN config.record_attr_definition rad ON (umra.attr = rad.name)
2797                 ORDER BY 1
2798
2799             )foo(id,y)
2800         )
2801     )
2802 $F$ LANGUAGE SQL STABLE;
2803
2804 CREATE OR REPLACE FUNCTION evergreen.ranked_volumes(
2805     bibid BIGINT[],
2806     ouid INT,
2807     depth INT DEFAULT NULL,
2808     slimit HSTORE DEFAULT NULL,
2809     soffset HSTORE DEFAULT NULL,
2810     pref_lib INT DEFAULT NULL,
2811     includes TEXT[] DEFAULT NULL::TEXT[]
2812 ) RETURNS TABLE(id BIGINT, name TEXT, label_sortkey TEXT, rank BIGINT) AS $$
2813     WITH RECURSIVE ou_depth AS (
2814         SELECT COALESCE(
2815             $3,
2816             (
2817                 SELECT depth
2818                 FROM actor.org_unit_type aout
2819                     INNER JOIN actor.org_unit ou ON ou_type = aout.id
2820                 WHERE ou.id = $2
2821             )
2822         ) AS depth
2823     ), descendant_depth AS (
2824         SELECT  ou.id,
2825                 ou.parent_ou,
2826                 out.depth
2827         FROM  actor.org_unit ou
2828                 JOIN actor.org_unit_type out ON (out.id = ou.ou_type)
2829                 JOIN anscestor_depth ad ON (ad.id = ou.id),
2830                 ou_depth
2831         WHERE ad.depth = ou_depth.depth
2832             UNION ALL
2833         SELECT  ou.id,
2834                 ou.parent_ou,
2835                 out.depth
2836         FROM  actor.org_unit ou
2837                 JOIN actor.org_unit_type out ON (out.id = ou.ou_type)
2838                 JOIN descendant_depth ot ON (ot.id = ou.parent_ou)
2839     ), anscestor_depth AS (
2840         SELECT  ou.id,
2841                 ou.parent_ou,
2842                 out.depth
2843         FROM  actor.org_unit ou
2844                 JOIN actor.org_unit_type out ON (out.id = ou.ou_type)
2845         WHERE ou.id = $2
2846             UNION ALL
2847         SELECT  ou.id,
2848                 ou.parent_ou,
2849                 out.depth
2850         FROM  actor.org_unit ou
2851                 JOIN actor.org_unit_type out ON (out.id = ou.ou_type)
2852                 JOIN anscestor_depth ot ON (ot.parent_ou = ou.id)
2853     ), descendants as (
2854         SELECT ou.* FROM actor.org_unit ou JOIN descendant_depth USING (id)
2855     )
2856
2857     SELECT ua.id, ua.name, ua.label_sortkey, MIN(ua.rank) AS rank FROM (
2858         SELECT acn.id, owning_lib.name, acn.label_sortkey,
2859             evergreen.rank_cp(acp),
2860             RANK() OVER w
2861         FROM asset.call_number acn
2862             JOIN asset.copy acp ON (acn.id = acp.call_number)
2863             JOIN descendants AS aou ON (acp.circ_lib = aou.id)
2864             JOIN actor.org_unit AS owning_lib ON (acn.owning_lib = owning_lib.id)
2865         WHERE acn.record = ANY ($1)
2866             AND acn.deleted IS FALSE
2867             AND acp.deleted IS FALSE
2868             AND CASE WHEN ('exclude_invisible_acn' = ANY($7)) THEN
2869                 EXISTS (
2870                     WITH basevm AS (SELECT c_attrs FROM  asset.patron_default_visibility_mask()),
2871                          circvm AS (SELECT search.calculate_visibility_attribute_test('circ_lib', ARRAY[acp.circ_lib]) AS mask)
2872                     SELECT  1
2873                       FROM  basevm, circvm, asset.copy_vis_attr_cache acvac
2874                       WHERE acvac.vis_attr_vector @@ (basevm.c_attrs || '&' || circvm.mask)::query_int
2875                             AND acvac.target_copy = acp.id
2876                             AND acvac.record = acn.record
2877                 ) ELSE TRUE END
2878         GROUP BY acn.id, evergreen.rank_cp(acp), owning_lib.name, acn.label_sortkey, aou.id
2879         WINDOW w AS (
2880             ORDER BY
2881                 COALESCE(
2882                     CASE WHEN aou.id = $2 THEN -20000 END,
2883                     CASE WHEN aou.id = $6 THEN -10000 END,
2884                     (SELECT distance - 5000
2885                         FROM actor.org_unit_descendants_distance($6) as x
2886                         WHERE x.id = aou.id AND $6 IN (
2887                             SELECT q.id FROM actor.org_unit_descendants($2) as q)),
2888                     (SELECT e.distance FROM actor.org_unit_descendants_distance($2) as e WHERE e.id = aou.id),
2889                     1000
2890                 ),
2891                 evergreen.rank_cp(acp)
2892         )
2893     ) AS ua
2894     GROUP BY ua.id, ua.name, ua.label_sortkey
2895     ORDER BY rank, ua.name, ua.label_sortkey
2896     LIMIT ($4 -> 'acn')::INT
2897     OFFSET ($5 -> 'acn')::INT;
2898 $$ LANGUAGE SQL STABLE ROWS 10;
2899
2900
2901 -- Evergreen DB patch XXXX.schema.action-trigger.event_definition.sms_preminder.sql
2902 --
2903 -- New action trigger event definition: 3 Day Courtesy Notice by SMS
2904 --
2905
2906 -- check whether patch can be applied
2907 SELECT evergreen.upgrade_deps_block_check('1058', :eg_version); -- mccanna/csharp/gmcharlt
2908
2909 INSERT INTO action_trigger.event_definition (id, active, owner, name, hook,
2910         validator, reactor, delay, max_delay, delay_field, group_field, template)
2911     VALUES (54, FALSE, 1,
2912         '3 Day Courtesy Notice by SMS',
2913         'checkout.due',
2914         'CircIsOpen', 'SendSMS', '-3 days', '-2 days', 'due_date', 'usr',
2915 $$
2916 [%- USE date -%]
2917 [%- user = target.0.usr -%]
2918 [%- homelib = user.home_ou -%]
2919 [%- sms_number = helpers.get_user_setting(user.id, 'opac.default_sms_notify') -%]
2920 [%- sms_carrier = helpers.get_user_setting(user.id, 'opac.default_sms_carrier') -%]
2921 From: [%- helpers.get_org_setting(homelib.id, 'org.bounced_emails') || homelib.email || params.sender_email || default_sender %]
2922 To: [%- helpers.get_sms_gateway_email(sms_carrier,sms_number) %]
2923 Subject: Library Materials Due Soon
2924
2925 You have items due soon:
2926
2927 [% FOR circ IN target %]
2928 [%- copy_details = helpers.get_copy_bib_basics(circ.target_copy.id) -%]
2929 [% copy_details.title FILTER ucfirst %] by [% copy_details.author FILTER ucfirst %] due on [% date.format(helpers.format_date(circ.due_date), '%m-%d-%Y') %]
2930
2931 [% END %]
2932
2933 $$);
2934
2935 INSERT INTO action_trigger.environment (event_def, path) VALUES
2936     (54, 'circ_lib.billing_address'),
2937     (54, 'target_copy.call_number'),
2938     (54, 'usr'),
2939     (54, 'usr.home_ou');
2940
2941
2942 -- check whether patch can be applied
2943 SELECT evergreen.upgrade_deps_block_check('1059', :eg_version); --Stompro/DPearl/kmlussier
2944
2945 CREATE OR REPLACE VIEW reporter.old_super_simple_record AS
2946 SELECT  r.id,
2947     r.fingerprint,
2948     r.quality,
2949     r.tcn_source,
2950     r.tcn_value,
2951     CONCAT_WS(' ', FIRST(title.value),FIRST(title_np.val)) AS title,
2952     FIRST(author.value) AS author,
2953     STRING_AGG(DISTINCT publisher.value, ', ') AS publisher,
2954     STRING_AGG(DISTINCT SUBSTRING(pubdate.value FROM $$\d+$$), ', ') AS pubdate,
2955     CASE WHEN ARRAY_AGG( DISTINCT REPLACE(SUBSTRING(isbn.value FROM $$^\S+$$), '-', '') ) = '{NULL}'
2956         THEN NULL
2957         ELSE ARRAY_AGG( DISTINCT REPLACE(SUBSTRING(isbn.value FROM $$^\S+$$), '-', '') )
2958     END AS isbn,
2959     CASE WHEN ARRAY_AGG( DISTINCT REGEXP_REPLACE(issn.value, E'^\\S*(\\d{4})[-\\s](\\d{3,4}x?)', E'\\1 \\2') ) = '{NULL}'
2960         THEN NULL
2961         ELSE ARRAY_AGG( DISTINCT REGEXP_REPLACE(issn.value, E'^\\S*(\\d{4})[-\\s](\\d{3,4}x?)', E'\\1 \\2') )
2962     END AS issn
2963   FROM  biblio.record_entry r
2964     LEFT JOIN metabib.full_rec title ON (r.id = title.record AND title.tag = '245' AND title.subfield = 'a')
2965     LEFT JOIN ( -- Grab 245 N and P subfields in the order that they appear.
2966       SELECT b.record, string_agg(val, ' ') AS val FROM (
2967              SELECT title_np.record, title_np.value AS val
2968               FROM metabib.full_rec title_np
2969               WHERE
2970               title_np.tag = '245'
2971                         AND title_np.subfield IN ('p','n')                      
2972                         ORDER BY title_np.id
2973                 ) b
2974                 GROUP BY 1
2975          ) title_np ON (title_np.record=r.id) 
2976     LEFT JOIN metabib.full_rec author ON (r.id = author.record AND author.tag IN ('100','110','111') AND author.subfield = 'a')
2977     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')
2978     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')
2979     LEFT JOIN metabib.full_rec isbn ON (r.id = isbn.record AND isbn.tag IN ('024', '020') AND isbn.subfield IN ('a','z'))
2980     LEFT JOIN metabib.full_rec issn ON (r.id = issn.record AND issn.tag = '022' AND issn.subfield = 'a')
2981   GROUP BY 1,2,3,4,5;
2982
2983   
2984   -- Remove trigger on biblio.record_entry
2985   SELECT reporter.disable_materialized_simple_record_trigger();
2986   
2987   -- Rebuild reporter.materialized_simple_record
2988   SELECT reporter.enable_materialized_simple_record_trigger();
2989   
2990
2991 SELECT evergreen.upgrade_deps_block_check('1060', :eg_version);
2992
2993 DROP VIEW IF EXISTS extend_reporter.copy_count_per_org;
2994
2995
2996 CREATE OR REPLACE VIEW extend_reporter.copy_count_per_org AS
2997  SELECT acn.record AS bibid,
2998     ac.circ_lib,
2999     acn.owning_lib,
3000     max(ac.edit_date) AS last_edit_time,
3001     min(ac.deleted::integer) AS has_only_deleted_copies,
3002     count(
3003         CASE
3004             WHEN ac.deleted THEN ac.id
3005             ELSE NULL::bigint
3006         END) AS deleted_count,
3007     count(
3008         CASE
3009             WHEN NOT ac.deleted THEN ac.id
3010             ELSE NULL::bigint
3011         END) AS visible_count,
3012     count(*) AS total_count
3013    FROM asset.call_number acn,
3014     asset.copy ac
3015   WHERE ac.call_number = acn.id
3016   GROUP BY acn.record, acn.owning_lib, ac.circ_lib;
3017
3018
3019
3020 SELECT evergreen.upgrade_deps_block_check('1061', :eg_version);
3021
3022 INSERT INTO config.org_unit_setting_type
3023     (name, label, description, grp, datatype)
3024 VALUES (
3025     'ui.staff.max_recent_patrons',
3026     oils_i18n_gettext(
3027         'ui.staff.max_recent_patrons',
3028         'Number of Retrievable Recent Patrons',
3029         'coust',
3030         'label'
3031     ),
3032     oils_i18n_gettext(
3033         'ui.staff.max_recent_patrons',
3034         'Number of most recently accessed patrons that can be re-retrieved ' ||
3035         'in the staff client.  A value of 0 or less disables the feature. Defaults to 1.',
3036         'coust',
3037         'description'
3038     ),
3039     'circ',
3040     'integer'
3041 );
3042
3043
3044 SELECT evergreen.upgrade_deps_block_check('1062', :eg_version);
3045
3046 CREATE TABLE acq.edi_attr (
3047     key     TEXT PRIMARY KEY,
3048     label   TEXT NOT NULL UNIQUE
3049 );
3050
3051 CREATE TABLE acq.edi_attr_set (
3052     id      SERIAL  PRIMARY KEY,
3053     label   TEXT NOT NULL UNIQUE
3054 );
3055
3056 CREATE TABLE acq.edi_attr_set_map (
3057     id          SERIAL  PRIMARY KEY,
3058     attr_set    INTEGER NOT NULL REFERENCES acq.edi_attr_set(id) 
3059                 ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
3060     attr        TEXT NOT NULL REFERENCES acq.edi_attr(key) 
3061                 ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
3062     CONSTRAINT edi_attr_set_map_attr_once UNIQUE (attr_set, attr)
3063 );
3064
3065 -- An attr_set is not strictly required, since some edi_accounts/vendors 
3066 -- may not need to apply any attributes.
3067 ALTER TABLE acq.edi_account 
3068     ADD COLUMN attr_set INTEGER REFERENCES acq.edi_attr_set(id),
3069     ADD COLUMN use_attrs BOOLEAN NOT NULL DEFAULT FALSE;
3070
3071
3072
3073
3074 SELECT evergreen.upgrade_deps_block_check('1063', :eg_version);
3075
3076 DO $temp$
3077 DECLARE
3078         r RECORD;
3079 BEGIN
3080
3081         FOR r IN SELECT t.table_schema AS sname,
3082                         t.table_name AS tname,
3083                         t.column_name AS colname,
3084                         t.constraint_name
3085                   FROM  information_schema.referential_constraints ref
3086                         JOIN information_schema.key_column_usage t USING (constraint_schema,constraint_name)
3087                   WHERE ref.unique_constraint_schema = 'asset'
3088                         AND ref.unique_constraint_name = 'copy_pkey'
3089         LOOP
3090
3091                 EXECUTE 'ALTER TABLE '||r.sname||'.'||r.tname||' DROP CONSTRAINT '||r.constraint_name||';';
3092
3093                 EXECUTE '
3094                         CREATE OR REPLACE FUNCTION evergreen.'||r.sname||'_'||r.tname||'_'||r.colname||'_inh_fkey() RETURNS TRIGGER AS $f$
3095                         BEGIN
3096                                 PERFORM 1 FROM asset.copy WHERE id = NEW.'||r.colname||';
3097                                 IF NOT FOUND THEN
3098                                         RAISE foreign_key_violation USING MESSAGE = FORMAT(
3099                                                 $$Referenced asset.copy id not found, '||r.colname||':%s$$, NEW.'||r.colname||'
3100                                         );
3101                                 END IF;
3102                                 RETURN NEW;
3103                         END;
3104                         $f$ LANGUAGE PLPGSQL VOLATILE COST 50;
3105                 ';
3106
3107                 EXECUTE '
3108                         CREATE CONSTRAINT TRIGGER inherit_'||r.constraint_name||'
3109                                 AFTER UPDATE OR INSERT OR DELETE ON '||r.sname||'.'||r.tname||'
3110                                 DEFERRABLE FOR EACH ROW EXECUTE PROCEDURE evergreen.'||r.sname||'_'||r.tname||'_'||r.colname||'_inh_fkey();
3111                 ';
3112         END LOOP;
3113 END
3114 $temp$;
3115
3116
3117
3118 SELECT evergreen.upgrade_deps_block_check('1064', :eg_version);
3119
3120 ALTER TABLE serial.issuance DROP CONSTRAINT IF EXISTS issuance_caption_and_pattern_fkey;
3121
3122 -- Using NOT VALID and VALIDATE CONSTRAINT limits the impact to concurrent work.
3123 -- For details, see: https://www.postgresql.org/docs/current/static/sql-altertable.html
3124
3125 ALTER TABLE serial.issuance ADD CONSTRAINT issuance_caption_and_pattern_fkey
3126     FOREIGN KEY (caption_and_pattern)
3127     REFERENCES serial.caption_and_pattern (id)
3128     ON DELETE CASCADE
3129     DEFERRABLE INITIALLY DEFERRED
3130     NOT VALID;
3131
3132 ALTER TABLE serial.issuance VALIDATE CONSTRAINT issuance_caption_and_pattern_fkey;
3133
3134
3135
3136 SELECT evergreen.upgrade_deps_block_check('1065', :eg_version);
3137
3138 CREATE TABLE serial.pattern_template (
3139     id            SERIAL PRIMARY KEY,
3140     name          TEXT NOT NULL,
3141     pattern_code  TEXT NOT NULL,
3142     owning_lib    INTEGER REFERENCES actor.org_unit(id) DEFERRABLE INITIALLY DEFERRED,
3143     share_depth   INTEGER NOT NULL DEFAULT 0
3144 );
3145 CREATE INDEX serial_pattern_template_name_idx ON serial.pattern_template (evergreen.lowercase(name));
3146
3147 CREATE OR REPLACE FUNCTION serial.pattern_templates_visible_to(org_unit INT) RETURNS SETOF serial.pattern_template AS $func$
3148 BEGIN
3149     RETURN QUERY SELECT *
3150            FROM serial.pattern_template spt
3151            WHERE (
3152              SELECT ARRAY_AGG(id)
3153              FROM actor.org_unit_descendants(spt.owning_lib, spt.share_depth)
3154            ) @@ org_unit::TEXT::QUERY_INT;
3155 END;
3156 $func$ LANGUAGE PLPGSQL;
3157
3158
3159 SELECT evergreen.upgrade_deps_block_check('1066', :eg_version);
3160
3161 INSERT INTO permission.perm_list ( id, code, description ) VALUES
3162  ( 593, 'ADMIN_SERIAL_PATTERN_TEMPLATE', oils_i18n_gettext( 593,
3163     'Administer serial prediction pattern templates', 'ppl', 'description' ))
3164 ;
3165
3166 INSERT INTO permission.grp_perm_map (grp, perm, depth, grantable)
3167     SELECT
3168         pgt.id, perm.id, aout.depth, FALSE
3169     FROM
3170         permission.grp_tree pgt,
3171         permission.perm_list perm,
3172         actor.org_unit_type aout
3173     WHERE
3174         pgt.name = 'Serials' AND
3175         aout.name = 'System' AND
3176         perm.code IN (
3177             'ADMIN_SERIAL_PATTERN_TEMPLATE'
3178         );
3179
3180
3181 SELECT evergreen.upgrade_deps_block_check('1067', :eg_version);
3182
3183 INSERT INTO acq.edi_attr (key, label) VALUES
3184     ('INCLUDE_PO_NAME', 
3185         oils_i18n_gettext('INCLUDE_PO_NAME', 
3186         'Orders Include PO Name', 'aea', 'label')),
3187     ('INCLUDE_COPIES', 
3188         oils_i18n_gettext('INCLUDE_COPIES', 
3189         'Orders Include Copy Data', 'aea', 'label')),
3190     ('INCLUDE_FUND', 
3191         oils_i18n_gettext('INCLUDE_FUND', 
3192         'Orders Include Copy Funds', 'aea', 'label')),
3193     ('INCLUDE_CALL_NUMBER', 
3194         oils_i18n_gettext('INCLUDE_CALL_NUMBER', 
3195         'Orders Include Copy Call Numbers', 'aea', 'label')),
3196     ('INCLUDE_ITEM_TYPE', 
3197         oils_i18n_gettext('INCLUDE_ITEM_TYPE', 
3198         'Orders Include Copy Item Types', 'aea', 'label')),
3199     ('INCLUDE_ITEM_BARCODE',
3200         oils_i18n_gettext('INCLUDE_ITEM_BARCODE',
3201         'Orders Include Copy Barcodes', 'aea', 'label')),
3202     ('INCLUDE_LOCATION', 
3203         oils_i18n_gettext('INCLUDE_LOCATION', 
3204         'Orders Include Copy Locations', 'aea', 'label')),
3205     ('INCLUDE_COLLECTION_CODE', 
3206         oils_i18n_gettext('INCLUDE_COLLECTION_CODE', 
3207         'Orders Include Copy Collection Codes', 'aea', 'label')),
3208     ('INCLUDE_OWNING_LIB', 
3209         oils_i18n_gettext('INCLUDE_OWNING_LIB', 
3210         'Orders Include Copy Owning Library', 'aea', 'label')),
3211     ('USE_ID_FOR_OWNING_LIB',
3212         oils_i18n_gettext('USE_ID_FOR_OWNING_LIB',
3213         'Emit Owning Library ID Rather Than Short Name. Takes effect only if INCLUDE_OWNING_LIB is in use', 'aea', 'label')),
3214     ('INCLUDE_QUANTITY', 
3215         oils_i18n_gettext('INCLUDE_QUANTITY', 
3216         'Orders Include Copy Quantities', 'aea', 'label')),
3217     ('INCLUDE_COPY_ID', 
3218         oils_i18n_gettext('INCLUDE_COPY_ID', 
3219         'Orders Include Copy IDs', 'aea', 'label')),
3220     ('BUYER_ID_INCLUDE_VENDCODE', 
3221         oils_i18n_gettext('BUYER_ID_INCLUDE_VENDCODE', 
3222         'Buyer ID Qualifier Includes Vendcode', 'aea', 'label')),
3223     ('BUYER_ID_ONLY_VENDCODE', 
3224         oils_i18n_gettext('BUYER_ID_ONLY_VENDCODE', 
3225         'Buyer ID Qualifier Only Contains Vendcode', 'aea', 'label')),
3226     ('INCLUDE_BIB_EDITION', 
3227         oils_i18n_gettext('INCLUDE_BIB_EDITION', 
3228         'Order Lineitems Include Edition Info', 'aea', 'label')),
3229     ('INCLUDE_BIB_AUTHOR', 
3230         oils_i18n_gettext('INCLUDE_BIB_AUTHOR', 
3231         'Order Lineitems Include Author Info', 'aea', 'label')),
3232     ('INCLUDE_BIB_PAGINATION', 
3233         oils_i18n_gettext('INCLUDE_BIB_PAGINATION', 
3234         'Order Lineitems Include Pagination Info', 'aea', 'label')),
3235     ('COPY_SPEC_CODES', 
3236         oils_i18n_gettext('COPY_SPEC_CODES', 
3237         'Order Lineitem Notes Include Copy Spec Codes', 'aea', 'label')),
3238     ('INCLUDE_EMPTY_IMD_VALUES', 
3239         oils_i18n_gettext('INCLUDE_EMPTY_IMD_VALUES',
3240         'Lineitem Title, Author, etc. Fields Are Present Even if Empty', 'aea', 'label')),
3241     ('INCLUDE_EMPTY_LI_NOTE', 
3242         oils_i18n_gettext('INCLUDE_EMPTY_LI_NOTE', 
3243         'Order Lineitem Notes Always Present (Even if Empty)', 'aea', 'label')),
3244     ('INCLUDE_EMPTY_CALL_NUMBER', 
3245         oils_i18n_gettext('INCLUDE_EMPTY_CALL_NUMBER', 
3246         'Order Copies Always Include Call Number (Even if Empty)', 'aea', 'label')),
3247     ('INCLUDE_EMPTY_ITEM_TYPE', 
3248         oils_i18n_gettext('INCLUDE_EMPTY_ITEM_TYPE', 
3249         'Order Copies Always Include Item Type (Even if Empty)', 'aea', 'label')),
3250     ('INCLUDE_EMPTY_LOCATION', 
3251         oils_i18n_gettext('INCLUDE_EMPTY_LOCATION', 
3252         'Order Copies Always Include Location (Even if Empty)', 'aea', 'label')),
3253     ('INCLUDE_EMPTY_COLLECTION_CODE', 
3254         oils_i18n_gettext('INCLUDE_EMPTY_COLLECTION_CODE', 
3255         'Order Copies Always Include Collection Code (Even if Empty)', 'aea', 'label')),
3256     ('LINEITEM_IDENT_VENDOR_NUMBER',
3257         oils_i18n_gettext('LINEITEM_IDENT_VENDOR_NUMBER',
3258         'Lineitem Identifier Fields (LIN/PIA) Use Vendor-Encoded ID Value When Available', 'aea', 'label')),
3259     ('LINEITEM_REF_ID_ONLY',
3260         oils_i18n_gettext('LINEITEM_REF_ID_ONLY',
3261         'Lineitem Reference Field (RFF) Uses Lineitem ID Only', 'aea', 'label'))
3262
3263 ;
3264
3265 INSERT INTO acq.edi_attr_set (id, label) VALUES (1, 'Ingram Default');
3266 INSERT INTO acq.edi_attr_set (id, label) VALUES (2, 'Baker & Taylor Default');
3267 INSERT INTO acq.edi_attr_set (id, label) VALUES (3, 'Brodart Default');
3268 INSERT INTO acq.edi_attr_set (id, label) VALUES (4, 'Midwest Tape Default');
3269 INSERT INTO acq.edi_attr_set (id, label) VALUES (5, 'ULS Default');
3270 INSERT INTO acq.edi_attr_set (id, label) VALUES (6, 'Recorded Books Default');
3271 INSERT INTO acq.edi_attr_set (id, label) VALUES (7, 'Midwest Library Service');
3272
3273 -- carve out space for mucho defaults
3274 SELECT SETVAL('acq.edi_attr_set_id_seq'::TEXT, 1000);
3275
3276 INSERT INTO acq.edi_attr_set_map (attr_set, attr) VALUES
3277
3278     -- Ingram
3279     (1, 'INCLUDE_PO_NAME'),
3280     (1, 'INCLUDE_COPIES'),
3281     (1, 'INCLUDE_ITEM_TYPE'),
3282     (1, 'INCLUDE_COLLECTION_CODE'),
3283     (1, 'INCLUDE_OWNING_LIB'),
3284     (1, 'INCLUDE_QUANTITY'),
3285     (1, 'INCLUDE_BIB_PAGINATION'),
3286
3287     -- B&T
3288     (2, 'INCLUDE_COPIES'),
3289     (2, 'INCLUDE_ITEM_TYPE'),
3290     (2, 'INCLUDE_COLLECTION_CODE'),
3291     (2, 'INCLUDE_CALL_NUMBER'),
3292     (2, 'INCLUDE_OWNING_LIB'),
3293     (2, 'INCLUDE_QUANTITY'),
3294     (2, 'INCLUDE_BIB_PAGINATION'),
3295     (2, 'BUYER_ID_INCLUDE_VENDCODE'),
3296     (2, 'INCLUDE_EMPTY_LI_NOTE'),
3297     (2, 'INCLUDE_EMPTY_CALL_NUMBER'),
3298     (2, 'INCLUDE_EMPTY_ITEM_TYPE'),
3299     (2, 'INCLUDE_EMPTY_COLLECTION_CODE'),
3300     (2, 'INCLUDE_EMPTY_LOCATION'),
3301     (2, 'LINEITEM_IDENT_VENDOR_NUMBER'),
3302     (2, 'LINEITEM_REF_ID_ONLY'),
3303
3304     -- Brodart
3305     (3, 'INCLUDE_COPIES'),
3306     (3, 'INCLUDE_FUND'),
3307     (3, 'INCLUDE_ITEM_TYPE'),
3308     (3, 'INCLUDE_COLLECTION_CODE'),
3309     (3, 'INCLUDE_OWNING_LIB'),
3310     (3, 'INCLUDE_QUANTITY'),
3311     (3, 'INCLUDE_BIB_PAGINATION'),
3312     (3, 'COPY_SPEC_CODES'),
3313
3314     -- Midwest
3315     (4, 'INCLUDE_COPIES'),
3316     (4, 'INCLUDE_FUND'),
3317     (4, 'INCLUDE_OWNING_LIB'),
3318     (4, 'INCLUDE_QUANTITY'),
3319     (4, 'INCLUDE_BIB_PAGINATION'),
3320
3321     -- ULS
3322     (5, 'INCLUDE_COPIES'),
3323     (5, 'INCLUDE_ITEM_TYPE'),
3324     (5, 'INCLUDE_COLLECTION_CODE'),
3325     (5, 'INCLUDE_OWNING_LIB'),
3326     (5, 'INCLUDE_QUANTITY'),
3327     (5, 'INCLUDE_BIB_AUTHOR'),
3328     (5, 'INCLUDE_BIB_EDITION'),
3329     (5, 'INCLUDE_EMPTY_LI_NOTE'),
3330
3331     -- Recorded Books
3332     (6, 'INCLUDE_COPIES'),
3333     (6, 'INCLUDE_ITEM_TYPE'),
3334     (6, 'INCLUDE_COLLECTION_CODE'),
3335     (6, 'INCLUDE_OWNING_LIB'),
3336     (6, 'INCLUDE_QUANTITY'),
3337     (6, 'INCLUDE_BIB_PAGINATION'),
3338
3339     -- Midwest Library Service
3340     (7, 'INCLUDE_BIB_AUTHOR'),
3341     (7, 'INCLUDE_BIB_EDITION'),
3342     (7, 'BUYER_ID_ONLY_VENDCODE'),
3343     (7, 'INCLUDE_EMPTY_IMD_VALUES')
3344 ;
3345
3346
3347
3348
3349
3350 SELECT evergreen.upgrade_deps_block_check('1068', :eg_version); --miker/gmcharlt/kmlussier
3351
3352 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"?>
3353 <xsl:stylesheet version="1.0" xmlns:mads="http://www.loc.gov/mads/v2"
3354         xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:marc="http://www.loc.gov/MARC21/slim"
3355         xmlns:xsl="http://www.w3.org/1999/XSL/Transform" exclude-result-prefixes="marc">
3356         <xsl:output method="xml" indent="yes" encoding="UTF-8"/>
3357         <xsl:strip-space elements="*"/>
3358
3359         <xsl:variable name="ascii">
3360                 <xsl:text> !"#$%&amp;'()*+,-./0123456789:;&lt;=&gt;?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~</xsl:text>
3361         </xsl:variable>
3362
3363         <xsl:variable name="latin1">
3364                 <xsl:text> ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ</xsl:text>
3365         </xsl:variable>
3366         <!-- Characters that usually don't need to be escaped -->
3367         <xsl:variable name="safe">
3368                 <xsl:text>!'()*-.0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz~</xsl:text>
3369         </xsl:variable>
3370
3371         <xsl:variable name="hex">0123456789ABCDEF</xsl:variable>
3372
3373
3374         <xsl:template name="datafield">
3375                 <xsl:param name="tag"/>
3376                 <xsl:param name="ind1">
3377                         <xsl:text> </xsl:text>
3378                 </xsl:param>
3379                 <xsl:param name="ind2">
3380                         <xsl:text> </xsl:text>
3381                 </xsl:param>
3382                 <xsl:param name="subfields"/>
3383                 <xsl:element name="marc:datafield">
3384                         <xsl:attribute name="tag">
3385                                 <xsl:value-of select="$tag"/>
3386                         </xsl:attribute>
3387                         <xsl:attribute name="ind1">
3388                                 <xsl:value-of select="$ind1"/>
3389                         </xsl:attribute>
3390                         <xsl:attribute name="ind2">
3391                                 <xsl:value-of select="$ind2"/>
3392                         </xsl:attribute>
3393                         <xsl:copy-of select="$subfields"/>
3394                 </xsl:element>
3395         </xsl:template>
3396
3397         <xsl:template name="subfieldSelect">
3398                 <xsl:param name="codes">abcdefghijklmnopqrstuvwxyz</xsl:param>
3399                 <xsl:param name="delimeter">
3400                         <xsl:text> </xsl:text>
3401                 </xsl:param>
3402                 <xsl:variable name="str">
3403                         <xsl:for-each select="marc:subfield">
3404                                 <xsl:if test="contains($codes, @code)">
3405                                         <xsl:value-of select="text()"/>
3406                                         <xsl:value-of select="$delimeter"/>
3407                                 </xsl:if>
3408                         </xsl:for-each>
3409                 </xsl:variable>
3410                 <xsl:value-of select="substring($str,1,string-length($str)-string-length($delimeter))"/>
3411         </xsl:template>
3412
3413         <xsl:template name="buildSpaces">
3414                 <xsl:param name="spaces"/>
3415                 <xsl:param name="char">
3416                         <xsl:text> </xsl:text>
3417                 </xsl:param>
3418                 <xsl:if test="$spaces>0">
3419                         <xsl:value-of select="$char"/>
3420                         <xsl:call-template name="buildSpaces">
3421                                 <xsl:with-param name="spaces" select="$spaces - 1"/>
3422                                 <xsl:with-param name="char" select="$char"/>
3423                         </xsl:call-template>
3424                 </xsl:if>
3425         </xsl:template>
3426
3427         <xsl:template name="chopPunctuation">
3428                 <xsl:param name="chopString"/>
3429                 <xsl:param name="punctuation">
3430                         <xsl:text>.:,;/ </xsl:text>
3431                 </xsl:param>
3432                 <xsl:variable name="length" select="string-length($chopString)"/>
3433                 <xsl:choose>
3434                         <xsl:when test="$length=0"/>
3435                         <xsl:when test="contains($punctuation, substring($chopString,$length,1))">
3436                                 <xsl:call-template name="chopPunctuation">
3437                                         <xsl:with-param name="chopString" select="substring($chopString,1,$length - 1)"/>
3438                                         <xsl:with-param name="punctuation" select="$punctuation"/>
3439                                 </xsl:call-template>
3440                         </xsl:when>
3441                         <xsl:when test="not($chopString)"/>
3442                         <xsl:otherwise>
3443                                 <xsl:value-of select="$chopString"/>
3444                         </xsl:otherwise>
3445                 </xsl:choose>
3446         </xsl:template>
3447
3448         <xsl:template name="chopPunctuationFront">
3449                 <xsl:param name="chopString"/>
3450                 <xsl:variable name="length" select="string-length($chopString)"/>
3451                 <xsl:choose>
3452                         <xsl:when test="$length=0"/>
3453                         <xsl:when test="contains('.:,;/[ ', substring($chopString,1,1))">
3454                                 <xsl:call-template name="chopPunctuationFront">
3455                                         <xsl:with-param name="chopString" select="substring($chopString,2,$length - 1)"
3456                                         />
3457                                 </xsl:call-template>
3458                         </xsl:when>
3459                         <xsl:when test="not($chopString)"/>
3460                         <xsl:otherwise>
3461                                 <xsl:value-of select="$chopString"/>
3462                         </xsl:otherwise>
3463                 </xsl:choose>
3464         </xsl:template>
3465
3466         <xsl:template name="chopPunctuationBack">
3467                 <xsl:param name="chopString"/>
3468                 <xsl:param name="punctuation">
3469                         <xsl:text>.:,;/] </xsl:text>
3470                 </xsl:param>
3471                 <xsl:variable name="length" select="string-length($chopString)"/>
3472                 <xsl:choose>
3473                         <xsl:when test="$length=0"/>
3474                         <xsl:when test="contains($punctuation, substring($chopString,$length,1))">
3475                                 <xsl:call-template name="chopPunctuation">
3476                                         <xsl:with-param name="chopString" select="substring($chopString,1,$length - 1)"/>
3477                                         <xsl:with-param name="punctuation" select="$punctuation"/>
3478                                 </xsl:call-template>
3479                         </xsl:when>
3480                         <xsl:when test="not($chopString)"/>
3481                         <xsl:otherwise>
3482                                 <xsl:value-of select="$chopString"/>
3483                         </xsl:otherwise>
3484                 </xsl:choose>
3485         </xsl:template>
3486
3487         <!-- nate added 12/14/2007 for lccn.loc.gov: url encode ampersand, etc. -->
3488         <xsl:template name="url-encode">
3489
3490                 <xsl:param name="str"/>
3491
3492                 <xsl:if test="$str">
3493                         <xsl:variable name="first-char" select="substring($str,1,1)"/>
3494                         <xsl:choose>
3495                                 <xsl:when test="contains($safe,$first-char)">
3496                                         <xsl:value-of select="$first-char"/>
3497                                 </xsl:when>
3498                                 <xsl:otherwise>
3499                                         <xsl:variable name="codepoint">
3500                                                 <xsl:choose>
3501                                                         <xsl:when test="contains($ascii,$first-char)">
3502                                                                 <xsl:value-of
3503                                                                         select="string-length(substring-before($ascii,$first-char)) + 32"
3504                                                                 />
3505                                                         </xsl:when>
3506                                                         <xsl:when test="contains($latin1,$first-char)">
3507                                                                 <xsl:value-of
3508                                                                         select="string-length(substring-before($latin1,$first-char)) + 160"/>
3509                                                                 <!-- was 160 -->
3510                                                         </xsl:when>
3511                                                         <xsl:otherwise>
3512                                                                 <xsl:message terminate="no">Warning: string contains a character
3513                                                                         that is out of range! Substituting "?".</xsl:message>
3514                                                                 <xsl:text>63</xsl:text>
3515                                                         </xsl:otherwise>
3516                                                 </xsl:choose>
3517                                         </xsl:variable>
3518                                         <xsl:variable name="hex-digit1"
3519                                                 select="substring($hex,floor($codepoint div 16) + 1,1)"/>
3520                                         <xsl:variable name="hex-digit2" select="substring($hex,$codepoint mod 16 + 1,1)"/>
3521                                         <!-- <xsl:value-of select="concat('%',$hex-digit2)"/> -->
3522                                         <xsl:value-of select="concat('%',$hex-digit1,$hex-digit2)"/>
3523                                 </xsl:otherwise>
3524                         </xsl:choose>
3525                         <xsl:if test="string-length($str) &gt; 1">
3526                                 <xsl:call-template name="url-encode">
3527                                         <xsl:with-param name="str" select="substring($str,2)"/>
3528                                 </xsl:call-template>
3529                         </xsl:if>
3530                 </xsl:if>
3531         </xsl:template>
3532
3533
3534 <!--
3535 2.14    Fixed bug in mads:geographic attributes syntax                                      ws   05/04/2016             
3536 2.13    fixed repeating <geographic>                                                                                                            tmee 01/31/2014
3537 2.12    added $2 authority for <classification>                                                                                         tmee 09/18/2012
3538 2.11    added delimiters between <classification> subfields                                                                     tmee 09/18/2012
3539 2.10    fixed type="other" and type="otherType" for mads:related                                                        tmee 09/16/2011
3540 2.09    fixed professionTerm and genreTerm empty tag error                                                                      tmee 09/16/2011
3541 2.08    fixed marc:subfield @code='i' matching error                                                                            tmee 09/16/2011
3542 2.07    fixed 555 duplication error                                                                                                                     tmee 08/10/2011 
3543 2.06    fixed topic subfield error                                                                                                                      tmee 08/10/2011 
3544 2.05    fixed title subfield error                                                                                                                      tmee 06/20/2011 
3545 2.04    fixed geographicSubdivision mapping for authority element                                                       tmee 06/16/2011
3546 2.03    added classification for 053, 055, 060, 065, 070, 080, 082, 083, 086, 087                       tmee 06/03/2011         
3547 2.02    added descriptionStandard for 008/10                                                                                            tmee 04/27/2011
3548 2.01    added extensions for 046, 336, 370, 374, 375, 376                                                                       tmee 04/08/2011
3549 2.00    redefined imported MODS elements in version 1.0 to MADS elements in version 2.0         tmee 02/08/2011
3550 1.08    added 372 subfields $a $s $t for <fieldOfActivity>                                                                      tmee 06/24/2010
3551 1.07    removed role/roleTerm 100, 110, 111, 400, 410, 411, 500, 510, 511, 700, 710, 711        tmee 06/24/2010
3552 1.06    added strip-space                                                                                                                                       tmee 06/24/2010
3553 1.05    added subfield $a for 130, 430, 530                                                                                                     tmee 06/21/2010
3554 1.04    fixed 550 z omission                                                                                                                            ntra 08/11/2008
3555 1.03    removed duplication of 550 $a text                                                                                                      tmee 11/01/2006
3556 1.02    fixed namespace references between mads and mods                                                                        ntra 10/06/2006
3557 1.01    revised                                                                                                                                                         rgue/jrad 11/29/05
3558 1.00    adapted from MARC21Slim2MODS3.xsl                                                                                                       ntra 07/06/05
3559 -->
3560
3561         <!-- authority attribute defaults to 'naf' if not set using this authority parameter, for <authority> descriptors: name, titleInfo, geographic -->
3562         <xsl:param name="authority"/>
3563         <xsl:variable name="auth">
3564                 <xsl:choose>
3565                         <xsl:when test="$authority">
3566                                 <xsl:value-of select="$authority"/>
3567                         </xsl:when>
3568                         <xsl:otherwise>naf</xsl:otherwise>
3569                 </xsl:choose>
3570         </xsl:variable>
3571         <xsl:variable name="controlField008" select="marc:controlfield[@tag='008']"/>
3572         <xsl:variable name="controlField008-06"
3573                 select="substring(descendant-or-self::marc:controlfield[@tag=008],7,1)"/>
3574         <xsl:variable name="controlField008-11"
3575                 select="substring(descendant-or-self::marc:controlfield[@tag=008],12,1)"/>
3576         <xsl:variable name="controlField008-14"
3577                 select="substring(descendant-or-self::marc:controlfield[@tag=008],15,1)"/>
3578         <xsl:template match="/">
3579                 <xsl:choose>
3580                         <xsl:when test="descendant-or-self::marc:collection">
3581                                 <mads:madsCollection xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3582                                         xsi:schemaLocation="http://www.loc.gov/mads/v2 http://www.loc.gov/standards/mads/v2/mads-2-0.xsd">
3583                                         <xsl:for-each select="descendant-or-self::marc:collection/marc:record">
3584                                                 <mads:mads version="2.0">
3585                                                         <xsl:call-template name="marcRecord"/>
3586                                                 </mads:mads>
3587                                         </xsl:for-each>
3588                                 </mads:madsCollection>
3589                         </xsl:when>
3590                         <xsl:otherwise>
3591                                 <mads:mads version="2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3592                                         xsi:schemaLocation="http://www.loc.gov/mads/v2 http://www.loc.gov/standards/mads/mads-2-0.xsd">
3593                                         <xsl:for-each select="descendant-or-self::marc:record">
3594                                                 <xsl:call-template name="marcRecord"/>
3595                                         </xsl:for-each>
3596                                 </mads:mads>
3597                         </xsl:otherwise>
3598                 </xsl:choose>
3599         </xsl:template>
3600
3601         <xsl:template name="marcRecord">
3602                 <mads:authority>
3603                         <!-- 2.04 -->
3604                         <xsl:choose>
3605                                 <xsl:when test="$controlField008-06='d'">
3606                                         <xsl:attribute name="geographicSubdivision">
3607                                                 <xsl:text>direct</xsl:text>
3608                                         </xsl:attribute>
3609                                 </xsl:when>
3610                                 <xsl:when test="$controlField008-06='i'">
3611                                         <xsl:attribute name="geographicSubdivision">
3612                                                 <xsl:text>indirect</xsl:text>
3613                                         </xsl:attribute>
3614                                 </xsl:when>
3615                                 <xsl:when test="$controlField008-06='n'">
3616                                         <xsl:attribute name="geographicSubdivision">
3617                                                 <xsl:text>not applicable</xsl:text>
3618                                         </xsl:attribute>
3619                                 </xsl:when>
3620                         </xsl:choose>
3621                         
3622                         <xsl:apply-templates select="marc:datafield[100 &lt;= @tag  and @tag &lt; 200]"/>               
3623                 </mads:authority>
3624
3625                 <!-- related -->
3626                 <xsl:apply-templates
3627                         select="marc:datafield[500 &lt;= @tag and @tag &lt;= 585]|marc:datafield[700 &lt;= @tag and @tag &lt;= 785]"/>
3628
3629                 <!-- variant -->
3630                 <xsl:apply-templates select="marc:datafield[400 &lt;= @tag and @tag &lt;= 485]"/>
3631
3632                 <!-- notes -->
3633                 <xsl:apply-templates select="marc:datafield[667 &lt;= @tag and @tag &lt;= 688]"/>
3634
3635                 <!-- url -->
3636                 <xsl:apply-templates select="marc:datafield[@tag=856]"/>
3637                 <xsl:apply-templates select="marc:datafield[@tag=010]"/>
3638                 <xsl:apply-templates select="marc:datafield[@tag=024]"/>
3639                 <xsl:apply-templates select="marc:datafield[@tag=372]"/>
3640                 
3641                 <!-- classification -->
3642                 <xsl:apply-templates select="marc:datafield[@tag=053]"/>
3643                 <xsl:apply-templates select="marc:datafield[@tag=055]"/>
3644                 <xsl:apply-templates select="marc:datafield[@tag=060]"/>
3645                 <xsl:apply-templates select="marc:datafield[@tag=065]"/>
3646                 <xsl:apply-templates select="marc:datafield[@tag=070]"/>
3647                 <xsl:apply-templates select="marc:datafield[@tag=080]"/>
3648                 <xsl:apply-templates select="marc:datafield[@tag=082]"/>
3649                 <xsl:apply-templates select="marc:datafield[@tag=083]"/>
3650                 <xsl:apply-templates select="marc:datafield[@tag=086]"/>
3651                 <xsl:apply-templates select="marc:datafield[@tag=087]"/>
3652
3653                 <!-- affiliation-->
3654                 <xsl:for-each select="marc:datafield[@tag=373]">
3655                         <mads:affiliation>
3656                                 <mads:position>
3657                                         <xsl:value-of select="marc:subfield[@code='a']"/>
3658                                 </mads:position>
3659                                 <mads:dateValid point="start">
3660                                         <xsl:value-of select="marc:subfield[@code='s']"/>
3661                                 </mads:dateValid>
3662                                 <mads:dateValid point="end">
3663                                         <xsl:value-of select="marc:subfield[@code='t']"/>
3664                                 </mads:dateValid>
3665                         </mads:affiliation>
3666                 </xsl:for-each>
3667                 <xsl:for-each select="marc:datafield[@tag=371]">
3668                         <mads:affiliation>
3669                                 <mads:address>
3670                                         <mads:street>
3671                                                 <xsl:value-of select="marc:subfield[@code='a']"/>
3672                                         </mads:street>
3673                                         <mads:city>
3674                                                 <xsl:value-of select="marc:subfield[@code='b']"/>
3675                                         </mads:city>
3676                                         <mads:state>
3677                                                 <xsl:value-of select="marc:subfield[@code='c']"/>
3678                                         </mads:state>
3679                                         <mads:country>
3680                                                 <xsl:value-of select="marc:subfield[@code='d']"/>
3681                                         </mads:country>
3682                                         <mads:postcode>
3683                                                 <xsl:value-of select="marc:subfield[@code='e']"/>
3684                                         </mads:postcode>
3685                                 </mads:address>
3686                                 <mads:email>
3687                                         <xsl:value-of select="marc:subfield[@code='m']"/>
3688                                 </mads:email>
3689                         </mads:affiliation>
3690                 </xsl:for-each>
3691
3692                 <!-- extension-->
3693                 <xsl:for-each select="marc:datafield[@tag=336]">
3694                         <mads:extension>
3695                                 <mads:contentType>
3696                                         <mads:contentType type="text">
3697                                                 <xsl:value-of select="marc:subfield[@code='a']"/>
3698                                         </mads:contentType>
3699                                         <mads:contentType type="code">
3700                                                 <xsl:value-of select="marc:subfield[@code='b']"/>
3701                                         </mads:contentType>
3702                                 </mads:contentType>
3703                         </mads:extension>
3704                 </xsl:for-each>
3705
3706                 <xsl:for-each select="marc:datafield[@tag=374]">
3707                         <mads:extension>
3708                                 <mads:profession>
3709                                         <xsl:choose>
3710                                                 <xsl:when test="marc:subfield[@code='a']">
3711                                                         <mads:professionTerm>
3712                                                                 <xsl:value-of select="marc:subfield[@code='a']"/>
3713                                                         </mads:professionTerm>
3714                                                 </xsl:when>
3715                                                 <xsl:when test="marc:subfield[@code='s']">
3716                                                         <mads:dateValid point="start">
3717                                                                 <xsl:value-of select="marc:subfield[@code='s']"/>
3718                                                         </mads:dateValid>
3719                                                 </xsl:when>
3720                                                 <xsl:when test="marc:subfield[@code='t']">
3721                                                         <mads:dateValid point="end">
3722                                                                 <xsl:value-of select="marc:subfield[@code='t']"/>
3723                                                         </mads:dateValid>
3724                                                 </xsl:when>
3725                                         </xsl:choose>
3726                                 </mads:profession>
3727                         </mads:extension>
3728                 </xsl:for-each>
3729                 
3730                 <xsl:for-each select="marc:datafield[@tag=375]">
3731                         <mads:extension>
3732                                 <mads:gender>
3733                                         <xsl:choose>
3734                                                 <xsl:when test="marc:subfield[@code='a']">
3735                                                         <mads:genderTerm>
3736                                                                 <xsl:value-of select="marc:subfield[@code='a']"/>
3737                                                         </mads:genderTerm>
3738                                                 </xsl:when>
3739                                                 <xsl:when test="marc:subfield[@code='s']">
3740                                                         <mads:dateValid point="start">
3741                                                                 <xsl:value-of select="marc:subfield[@code='s']"/>
3742                                                         </mads:dateValid>
3743                                                 </xsl:when>
3744                                                 <xsl:when test="marc:subfield[@code='t']">
3745                                                         <mads:dateValid point="end">
3746                                                                 <xsl:value-of select="marc:subfield[@code='t']"/>
3747                                                         </mads:dateValid>
3748                                                 </xsl:when>
3749                                         </xsl:choose>
3750                                 </mads:gender>
3751                         </mads:extension>
3752                 </xsl:for-each>
3753
3754                 <xsl:for-each select="marc:datafield[@tag=376]">
3755                         <mads:extension>
3756                                 <mads:familyInformation>
3757                                         <mads:typeOfFamily>
3758                                                 <xsl:value-of select="marc:subfield[@code='a']"/>
3759                                         </mads:typeOfFamily>
3760                                         <mads:nameOfProminentMember>
3761                                                 <xsl:value-of select="marc:subfield[@code='b']"/>
3762                                         </mads:nameOfProminentMember>
3763                                         <mads:hereditaryTitle>
3764                                                 <xsl:value-of select="marc:subfield[@code='c']"/>
3765                                         </mads:hereditaryTitle>
3766                                         <mads:dateValid point="start">
3767                                                 <xsl:value-of select="marc:subfield[@code='s']"/>
3768                                         </mads:dateValid>
3769                                         <mads:dateValid point="end">
3770                                                 <xsl:value-of select="marc:subfield[@code='t']"/>
3771                                         </mads:dateValid>
3772                                 </mads:familyInformation>
3773                         </mads:extension>
3774                 </xsl:for-each>
3775
3776                 <mads:recordInfo>
3777                         <mads:recordOrigin>Converted from MARCXML to MADS version 2.0 (Revision 2.13)</mads:recordOrigin>
3778                         <!-- <xsl:apply-templates select="marc:datafield[@tag=024]"/> -->
3779
3780                         <xsl:apply-templates select="marc:datafield[@tag=040]/marc:subfield[@code='a']"/>
3781                         <xsl:apply-templates select="marc:controlfield[@tag=005]"/>
3782                         <xsl:apply-templates select="marc:controlfield[@tag=001]"/>
3783                         <xsl:apply-templates select="marc:datafield[@tag=040]/marc:subfield[@code='b']"/>
3784                         <xsl:apply-templates select="marc:datafield[@tag=040]/marc:subfield[@code='e']"/>
3785                         <xsl:for-each select="marc:controlfield[@tag=008]">
3786                                 <xsl:if test="substring(.,11,1)='a'">
3787                                         <mads:descriptionStandard>
3788                                                 <xsl:text>earlier rules</xsl:text>
3789                                         </mads:descriptionStandard>
3790                                 </xsl:if>
3791                                 <xsl:if test="substring(.,11,1)='b'">
3792                                         <mads:descriptionStandard>
3793                                                 <xsl:text>aacr1</xsl:text>
3794                                         </mads:descriptionStandard>
3795                                 </xsl:if>
3796                                 <xsl:if test="substring(.,11,1)='c'">
3797                                         <mads:descriptionStandard>
3798                                                 <xsl:text>aacr2</xsl:text>
3799                                         </mads:descriptionStandard>
3800                                 </xsl:if>
3801                                 <xsl:if test="substring(.,11,1)='d'">
3802                                         <mads:descriptionStandard>
3803                                                 <xsl:text>aacr2 compatible</xsl:text>
3804                                         </mads:descriptionStandard>
3805                                 </xsl:if>
3806                                 <xsl:if test="substring(.,11,1)='z'">
3807                                         <mads:descriptionStandard>
3808                                                 <xsl:text>other rules</xsl:text>
3809                                         </mads:descriptionStandard>
3810                                 </xsl:if>
3811                         </xsl:for-each>
3812                 </mads:recordInfo>
3813         </xsl:template>
3814
3815         <!-- start of secondary templates -->
3816
3817         <!-- ======== xlink ======== -->
3818
3819         <!-- <xsl:template name="uri"> 
3820     <xsl:for-each select="marc:subfield[@code='0']">
3821       <xsl:attribute name="xlink:href">
3822         <xsl:value-of select="."/>
3823       </xsl:attribute>
3824     </xsl:for-each>
3825      </xsl:template> 
3826    -->
3827         <xsl:template match="marc:subfield[@code='i']">
3828                 <xsl:attribute name="otherType">
3829                         <xsl:value-of select="."/>
3830                 </xsl:attribute>
3831         </xsl:template>
3832
3833         <!-- No role/roleTerm mapped in MADS 06/24/2010
3834         <xsl:template name="role">
3835                 <xsl:for-each select="marc:subfield[@code='e']">
3836                         <mads:role>
3837                                 <mads:roleTerm type="text">
3838                                         <xsl:value-of select="."/>
3839                                 </mads:roleTerm>
3840                         </mads:role>
3841                 </xsl:for-each>
3842         </xsl:template>
3843 -->
3844
3845         <xsl:template name="part">
3846                 <xsl:variable name="partNumber">
3847                         <xsl:call-template name="specialSubfieldSelect">
3848                                 <xsl:with-param name="axis">n</xsl:with-param>
3849                                 <xsl:with-param name="anyCodes">n</xsl:with-param>
3850                                 <xsl:with-param name="afterCodes">fghkdlmor</xsl:with-param>
3851                         </xsl:call-template>
3852                 </xsl:variable>
3853                 <xsl:variable name="partName">
3854                         <xsl:call-template name="specialSubfieldSelect">
3855                                 <xsl:with-param name="axis">p</xsl:with-param>
3856                                 <xsl:with-param name="anyCodes">p</xsl:with-param>
3857                                 <xsl:with-param name="afterCodes">fghkdlmor</xsl:with-param>
3858                         </xsl:call-template>
3859                 </xsl:variable>
3860                 <xsl:if test="string-length(normalize-space($partNumber))">
3861                         <mads:partNumber>
3862                                 <xsl:call-template name="chopPunctuation">
3863                                         <xsl:with-param name="chopString" select="$partNumber"/>
3864                                 </xsl:call-template>
3865                         </mads:partNumber>
3866                 </xsl:if>
3867                 <xsl:if test="string-length(normalize-space($partName))">
3868                         <mads:partName>
3869                                 <xsl:call-template name="chopPunctuation">
3870                                         <xsl:with-param name="chopString" select="$partName"/>
3871                                 </xsl:call-template>
3872                         </mads:partName>
3873                 </xsl:if>
3874         </xsl:template>
3875
3876         <xsl:template name="nameABCDN">
3877                 <xsl:for-each select="marc:subfield[@code='a']">
3878                         <mads:namePart>
3879                                 <xsl:call-template name="chopPunctuation">
3880                                         <xsl:with-param name="chopString" select="."/>
3881                                 </xsl:call-template>
3882                         </mads:namePart>
3883                 </xsl:for-each>
3884                 <xsl:for-each select="marc:subfield[@code='b']">
3885                         <mads:namePart>
3886                                 <xsl:value-of select="."/>
3887                         </mads:namePart>
3888                 </xsl:for-each>
3889                 <xsl:if
3890                         test="marc:subfield[@code='c'] or marc:subfield[@code='d'] or marc:subfield[@code='n']">
3891                         <mads:namePart>
3892                                 <xsl:call-template name="subfieldSelect">
3893                                         <xsl:with-param name="codes">cdn</xsl:with-param>
3894                                 </xsl:call-template>
3895                         </mads:namePart>
3896                 </xsl:if>
3897         </xsl:template>
3898
3899         <xsl:template name="nameABCDQ">
3900                 <mads:namePart>
3901                         <xsl:call-template name="chopPunctuation">
3902                                 <xsl:with-param name="chopString">
3903                                         <xsl:call-template name="subfieldSelect">
3904                                                 <xsl:with-param name="codes">aq</xsl:with-param>
3905                                         </xsl:call-template>
3906                                 </xsl:with-param>
3907                         </xsl:call-template>
3908                 </mads:namePart>
3909                 <xsl:call-template name="termsOfAddress"/>
3910                 <xsl:call-template name="nameDate"/>
3911         </xsl:template>
3912
3913         <xsl:template name="nameACDENQ">
3914                 <mads:namePart>
3915                         <xsl:call-template name="subfieldSelect">
3916                                 <xsl:with-param name="codes">acdenq</xsl:with-param>
3917                         </xsl:call-template>
3918                 </mads:namePart>
3919         </xsl:template>
3920
3921         <xsl:template name="nameDate">
3922                 <xsl:for-each select="marc:subfield[@code='d']">
3923                         <mads:namePart type="date">
3924                                 <xsl:call-template name="chopPunctuation">
3925                                         <xsl:with-param name="chopString" select="."/>
3926                                 </xsl:call-template>
3927                         </mads:namePart>
3928                 </xsl:for-each>
3929         </xsl:template>
3930
3931         <xsl:template name="specialSubfieldSelect">
3932                 <xsl:param name="anyCodes"/>
3933                 <xsl:param name="axis"/>
3934                 <xsl:param name="beforeCodes"/>
3935                 <xsl:param name="afterCodes"/>
3936                 <xsl:variable name="str">
3937                         <xsl:for-each select="marc:subfield">
3938                                 <xsl:if
3939                                         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])">
3940                                         <xsl:value-of select="text()"/>
3941                                         <xsl:text> </xsl:text>
3942                                 </xsl:if>
3943                         </xsl:for-each>
3944                 </xsl:variable>
3945                 <xsl:value-of select="substring($str,1,string-length($str)-1)"/>
3946         </xsl:template>
3947
3948         <xsl:template name="termsOfAddress">
3949                 <xsl:if test="marc:subfield[@code='b' or @code='c']">
3950                         <mads:namePart type="termsOfAddress">
3951                                 <xsl:call-template name="chopPunctuation">
3952                                         <xsl:with-param name="chopString">
3953                                                 <xsl:call-template name="subfieldSelect">
3954                                                         <xsl:with-param name="codes">bc</xsl:with-param>
3955                                                 </xsl:call-template>
3956                                         </xsl:with-param>
3957                                 </xsl:call-template>
3958                         </mads:namePart>
3959                 </xsl:if>
3960         </xsl:template>
3961
3962         <xsl:template name="displayLabel">
3963                 <xsl:if test="marc:subfield[@code='z']">
3964                         <xsl:attribute name="displayLabel">
3965                                 <xsl:value-of select="marc:subfield[@code='z']"/>
3966                         </xsl:attribute>
3967                 </xsl:if>
3968                 <xsl:if test="marc:subfield[@code='3']">
3969                         <xsl:attribute name="displayLabel">
3970                                 <xsl:value-of select="marc:subfield[@code='3']"/>
3971                         </xsl:attribute>
3972                 </xsl:if>
3973         </xsl:template>
3974
3975         <xsl:template name="isInvalid">
3976                 <xsl:if test="@code='z'">
3977                         <xsl:attribute name="invalid">yes</xsl:attribute>
3978                 </xsl:if>
3979         </xsl:template>
3980
3981         <xsl:template name="sub2Attribute">
3982                 <!-- 024 -->
3983                 <xsl:if test="../marc:subfield[@code='2']">
3984                         <xsl:attribute name="type">
3985                                 <xsl:value-of select="../marc:subfield[@code='2']"/>
3986                         </xsl:attribute>
3987                 </xsl:if>
3988         </xsl:template>
3989
3990         <xsl:template match="marc:controlfield[@tag=001]">
3991                 <mads:recordIdentifier>
3992                         <xsl:if test="../marc:controlfield[@tag=003]">
3993                                 <xsl:attribute name="source">
3994                                         <xsl:value-of select="../marc:controlfield[@tag=003]"/>
3995                                 </xsl:attribute>
3996                         </xsl:if>
3997                         <xsl:value-of select="."/>
3998                 </mads:recordIdentifier>
3999         </xsl:template>
4000
4001         <xsl:template match="marc:controlfield[@tag=005]">
4002                 <mads:recordChangeDate encoding="iso8601">
4003                         <xsl:value-of select="."/>
4004                 </mads:recordChangeDate>
4005         </xsl:template>
4006
4007         <xsl:template match="marc:controlfield[@tag=008]">
4008                 <mads:recordCreationDate encoding="marc">
4009                         <xsl:value-of select="substring(.,1,6)"/>
4010                 </mads:recordCreationDate>
4011         </xsl:template>
4012
4013         <xsl:template match="marc:datafield[@tag=010]">
4014                 <xsl:for-each select="marc:subfield">
4015                         <mads:identifier type="lccn">
4016                                 <xsl:call-template name="isInvalid"/>
4017                                 <xsl:value-of select="."/>
4018                         </mads:identifier>
4019                 </xsl:for-each>
4020         </xsl:template>
4021
4022         <xsl:template match="marc:datafield[@tag=024]">
4023                 <xsl:for-each select="marc:subfield[not(@code=2)]">
4024                         <mads:identifier>
4025                                 <xsl:call-template name="isInvalid"/>
4026                                 <xsl:call-template name="sub2Attribute"/>
4027                                 <xsl:value-of select="."/>
4028                         </mads:identifier>
4029                 </xsl:for-each>
4030         </xsl:template>
4031
4032         <!-- ========== 372 ========== -->
4033         <xsl:template match="marc:datafield[@tag=372]">
4034                 <mads:fieldOfActivity>
4035                         <xsl:call-template name="subfieldSelect">
4036                                 <xsl:with-param name="codes">a</xsl:with-param>
4037                         </xsl:call-template>
4038                         <xsl:text>-</xsl:text>
4039                         <xsl:call-template name="subfieldSelect">
4040                                 <xsl:with-param name="codes">st</xsl:with-param>
4041                         </xsl:call-template>
4042                 </mads:fieldOfActivity>
4043         </xsl:template>
4044
4045
4046         <!-- ========== 040 ========== -->
4047         <xsl:template match="marc:datafield[@tag=040]/marc:subfield[@code='a']">
4048                 <mads:recordContentSource authority="marcorg">
4049                         <xsl:value-of select="."/>
4050                 </mads:recordContentSource>
4051         </xsl:template>
4052
4053         <xsl:template match="marc:datafield[@tag=040]/marc:subfield[@code='b']">
4054                 <mads:languageOfCataloging>
4055                         <mads:languageTerm authority="iso639-2b" type="code">
4056                                 <xsl:value-of select="."/>
4057                         </mads:languageTerm>
4058                 </mads:languageOfCataloging>
4059         </xsl:template>
4060
4061         <xsl:template match="marc:datafield[@tag=040]/marc:subfield[@code='e']">
4062                 <mads:descriptionStandard>
4063                         <xsl:value-of select="."/>
4064                 </mads:descriptionStandard>
4065         </xsl:template>
4066         
4067         <!-- ========== classification 2.03 ========== -->
4068         
4069         <xsl:template match="marc:datafield[@tag=053]">
4070                 <mads:classification>
4071                         <xsl:call-template name="subfieldSelect">
4072                                 <xsl:with-param name="codes">abcdxyz</xsl:with-param>
4073                                 <xsl:with-param name="delimeter">-</xsl:with-param>
4074                         </xsl:call-template>
4075                 </mads:classification>
4076         </xsl:template>
4077         
4078         <xsl:template match="marc:datafield[@tag=055]">
4079                 <mads:classification>
4080                         <xsl:call-template name="subfieldSelect">
4081                                 <xsl:with-param name="codes">abcdxyz</xsl:with-param>
4082                                 <xsl:with-param name="delimeter">-</xsl:with-param>
4083                         </xsl:call-template>
4084                 </mads:classification>
4085         </xsl:template>
4086         
4087         <xsl:template match="marc:datafield[@tag=060]">
4088                 <mads:classification>
4089                         <xsl:call-template name="subfieldSelect">
4090                                 <xsl:with-param name="codes">abcdxyz</xsl:with-param>
4091                                 <xsl:with-param name="delimeter">-</xsl:with-param>
4092                         </xsl:call-template>
4093                 </mads:classification>
4094         </xsl:template>
4095         <xsl:template match="marc:datafield[@tag=065]">
4096                 <mads:classification>
4097                         <xsl:attribute name="authority">
4098                                 <xsl:value-of select="marc:subfield[@code='2']"/>
4099                         </xsl:attribute>
4100                         <xsl:call-template name="subfieldSelect">
4101                                 <xsl:with-param name="codes">abcdxyz</xsl:with-param>
4102                                 <xsl:with-param name="delimeter">-</xsl:with-param>
4103                         </xsl:call-template>
4104                 </mads:classification>
4105         </xsl:template>
4106         <xsl:template match="marc:datafield[@tag=070]">
4107                 <mads:classification>
4108                         <xsl:call-template name="subfieldSelect">
4109                                 <xsl:with-param name="codes">abcdxyz5</xsl:with-param>
4110                                 <xsl:with-param name="delimeter">-</xsl:with-param>
4111                         </xsl:call-template>
4112                 </mads:classification>
4113         </xsl:template>
4114         <xsl:template match="marc:datafield[@tag=080]">
4115                 <mads:classification>
4116                         <xsl:attribute name="authority">
4117                                 <xsl:value-of select="marc:subfield[@code='2']"/>
4118                         </xsl:attribute>
4119                         <xsl:call-template name="subfieldSelect">
4120                                 <xsl:with-param name="codes">abcdxyz5</xsl:with-param>
4121                                 <xsl:with-param name="delimeter">-</xsl:with-param>
4122                         </xsl:call-template>
4123                 </mads:classification>
4124         </xsl:template>
4125         <xsl:template match="marc:datafield[@tag=082]">
4126                 <mads:classification>
4127                         <xsl:attribute name="authority">
4128                                 <xsl:value-of select="marc:subfield[@code='2']"/>
4129                         </xsl:attribute>
4130                         <xsl:call-template name="subfieldSelect">
4131                                 <xsl:with-param name="codes">abcdxyz5</xsl:with-param>
4132                                 <xsl:with-param name="delimeter">-</xsl:with-param>
4133                         </xsl:call-template>
4134                 </mads:classification>
4135         </xsl:template>
4136         <xsl:template match="marc:datafield[@tag=083]">
4137                 <mads:classification>
4138                         <xsl:attribute name="authority">
4139                                 <xsl:value-of select="marc:subfield[@code='2']"/>
4140                         </xsl:attribute>
4141                         <xsl:call-template name="subfieldSelect">
4142                                 <xsl:with-param name="codes">abcdxyz5</xsl:with-param>
4143                                 <xsl:with-param name="delimeter">-</xsl:with-param>
4144                         </xsl:call-template>
4145                 </mads:classification>
4146         </xsl:template>
4147         <xsl:template match="marc:datafield[@tag=086]">
4148                 <mads:classification>
4149                         <xsl:attribute name="authority">
4150                                 <xsl:value-of select="marc:subfield[@code='2']"/>
4151                         </xsl:attribute>
4152                         <xsl:call-template name="subfieldSelect">
4153                                 <xsl:with-param name="codes">abcdxyz5</xsl:with-param>
4154                                 <xsl:with-param name="delimeter">-</xsl:with-param>
4155                         </xsl:call-template>
4156                 </mads:classification>
4157         </xsl:template>
4158         <xsl:template match="marc:datafield[@tag=087]">
4159                 <mads:classification>
4160                         <xsl:attribute name="authority">
4161                                 <xsl:value-of select="marc:subfield[@code='2']"/>
4162                         </xsl:attribute>
4163                         <xsl:call-template name="subfieldSelect">
4164                                 <xsl:with-param name="codes">abcdxyz5</xsl:with-param>
4165                                 <xsl:with-param name="delimeter">-</xsl:with-param>
4166                         </xsl:call-template>
4167                 </mads:classification>
4168         </xsl:template>
4169         
4170
4171         <!-- ========== names  ========== -->
4172         <xsl:template match="marc:datafield[@tag=100]">
4173                 <mads:name type="personal">
4174                         <xsl:call-template name="setAuthority"/>
4175                         <xsl:call-template name="nameABCDQ"/>
4176                 </mads:name>
4177                 <xsl:apply-templates select="*[marc:subfield[not(contains('abcdeq',@code))]]"/>
4178                 <xsl:call-template name="title"/>
4179                 <xsl:apply-templates select="marc:subfield[@code!='i']"/>
4180         </xsl:template>
4181
4182         <xsl:template match="marc:datafield[@tag=110]">
4183                 <mads:name type="corporate">
4184                         <xsl:call-template name="setAuthority"/>
4185                         <xsl:call-template name="nameABCDN"/>
4186                 </mads:name>
4187                 <xsl:apply-templates select="marc:subfield[@code!='i']"/>
4188         </xsl:template>
4189
4190         <xsl:template match="marc:datafield[@tag=111]">
4191                 <mads:name type="conference">
4192                         <xsl:call-template name="setAuthority"/>
4193                         <xsl:call-template name="nameACDENQ"/>
4194                 </mads:name>
4195                 <xsl:apply-templates select="marc:subfield[@code!='i']"/>
4196         </xsl:template>
4197
4198         <xsl:template match="marc:datafield[@tag=400]">
4199                 <mads:variant>
4200                         <xsl:call-template name="variantTypeAttribute"/>
4201                         <mads:name type="personal">
4202                                 <xsl:call-template name="nameABCDQ"/>
4203                         </mads:name>
4204                         <xsl:apply-templates select="marc:subfield[@code!='i']"/>
4205                         <xsl:call-template name="title"/>
4206                 </mads:variant>
4207         </xsl:template>
4208
4209         <xsl:template match="marc:datafield[@tag=410]">
4210                 <mads:variant>
4211                         <xsl:call-template name="variantTypeAttribute"/>
4212                         <mads:name type="corporate">
4213                                 <xsl:call-template name="nameABCDN"/>
4214                         </mads:name>
4215                         <xsl:apply-templates select="marc:subfield[@code!='i']"/>
4216                 </mads:variant>
4217         </xsl:template>
4218
4219         <xsl:template match="marc:datafield[@tag=411]">
4220                 <mads:variant>
4221                         <xsl:call-template name="variantTypeAttribute"/>
4222                         <mads:name type="conference">
4223                                 <xsl:call-template name="nameACDENQ"/>
4224                         </mads:name>
4225                         <xsl:apply-templates select="marc:subfield[@code!='i']"/>
4226                 </mads:variant>
4227         </xsl:template>
4228
4229         <xsl:template match="marc:datafield[@tag=500]|marc:datafield[@tag=700]">
4230                 <mads:related>
4231                         <xsl:call-template name="relatedTypeAttribute"/>
4232                         <!-- <xsl:call-template name="uri"/> -->
4233                         <mads:name type="personal">
4234                                 <xsl:call-template name="setAuthority"/>
4235                                 <xsl:call-template name="nameABCDQ"/>
4236                         </mads:name>
4237                         <xsl:call-template name="title"/>
4238                         <xsl:apply-templates select="marc:subfield[@code!='i']"/>
4239                 </mads:related>
4240         </xsl:template>
4241
4242         <xsl:template match="marc:datafield[@tag=510]|marc:datafield[@tag=710]">
4243                 <mads:related>
4244                         <xsl:call-template name="relatedTypeAttribute"/>
4245                         <!-- <xsl:call-template name="uri"/> -->
4246                         <mads:name type="corporate">
4247                                 <xsl:call-template name="setAuthority"/>
4248                                 <xsl:call-template name="nameABCDN"/>
4249                         </mads:name>
4250                         <xsl:apply-templates select="marc:subfield[@code!='i']"/>
4251                 </mads:related>
4252         </xsl:template>
4253
4254         <xsl:template match="marc:datafield[@tag=511]|marc:datafield[@tag=711]">
4255                 <mads:related>
4256                         <xsl:call-template name="relatedTypeAttribute"/>
4257                         <!-- <xsl:call-template name="uri"/> -->
4258                         <mads:name type="conference">
4259                                 <xsl:call-template name="setAuthority"/>
4260                                 <xsl:call-template name="nameACDENQ"/>
4261                         </mads:name>
4262                         <xsl:apply-templates select="marc:subfield[@code!='i']"/>
4263                 </mads:related>
4264         </xsl:template>
4265
4266         <!-- ========== titles  ========== -->
4267         <xsl:template match="marc:datafield[@tag=130]">
4268                 <xsl:call-template name="uniform-title"/>
4269                 <xsl:apply-templates select="marc:subfield[@code!='i']"/>
4270         </xsl:template>
4271
4272         <xsl:template match="marc:datafield[@tag=430]">
4273                 <mads:variant>
4274                         <xsl:call-template name="variantTypeAttribute"/>
4275                         <xsl:call-template name="uniform-title"/>
4276                         <xsl:apply-templates select="marc:subfield[@code!='i']"/>
4277                 </mads:variant>
4278         </xsl:template>
4279
4280         <xsl:template match="marc:datafield[@tag=530]|marc:datafield[@tag=730]">
4281                 <mads:related>
4282                         <xsl:call-template name="relatedTypeAttribute"/>
4283                         <xsl:call-template name="uniform-title"/>
4284                         <xsl:apply-templates select="marc:subfield[@code!='i']"/>
4285                 </mads:related>
4286         </xsl:template>
4287
4288         <xsl:template name="title">
4289                 <xsl:variable name="hasTitle">
4290                         <xsl:for-each select="marc:subfield">
4291                                 <xsl:if test="(contains('tfghklmors',@code) )">
4292                                         <xsl:value-of select="@code"/>
4293                                 </xsl:if>
4294                         </xsl:for-each>
4295                 </xsl:variable>
4296                 <xsl:if test="string-length($hasTitle) &gt; 0 ">
4297                         <mads:titleInfo>
4298                                 <xsl:call-template name="setAuthority"/>
4299                                 <mads:title>
4300                                         <xsl:variable name="str">
4301                                                 <xsl:for-each select="marc:subfield">
4302                                                         <xsl:if test="(contains('atfghklmors',@code) )">
4303                                                                 <xsl:value-of select="text()"/>
4304                                                                 <xsl:text> </xsl:text>
4305                                                         </xsl:if>
4306                                                 </xsl:for-each>
4307                                         </xsl:variable>
4308                                         <xsl:call-template name="chopPunctuation">
4309                                                 <xsl:with-param name="chopString">
4310                                                         <xsl:value-of select="substring($str,1,string-length($str)-1)"/>
4311                                                 </xsl:with-param>
4312                                         </xsl:call-template>
4313                                 </mads:title>
4314                                 <xsl:call-template name="part"/>
4315                                 <!-- <xsl:call-template name="uri"/> -->
4316                         </mads:titleInfo>
4317                 </xsl:if>
4318         </xsl:template>
4319
4320         <xsl:template name="uniform-title">
4321                 <xsl:variable name="hasTitle">
4322                         <xsl:for-each select="marc:subfield">
4323                                 <xsl:if test="(contains('atfghklmors',@code) )">
4324                                         <xsl:value-of select="@code"/>
4325                                 </xsl:if>
4326                         </xsl:for-each>
4327                 </xsl:variable>
4328                 <xsl:if test="string-length($hasTitle) &gt; 0 ">
4329                         <mads:titleInfo>
4330                                 <xsl:call-template name="setAuthority"/>
4331                                 <mads:title>
4332                                         <xsl:variable name="str">
4333                                                 <xsl:for-each select="marc:subfield">
4334                                                         <xsl:if test="(contains('adfghklmors',@code) )">
4335                                                                 <xsl:value-of select="text()"/>
4336                                                                 <xsl:text> </xsl:text>
4337                                                         </xsl:if>
4338                                                 </xsl:for-each>
4339                                         </xsl:variable>
4340                                         <xsl:call-template name="chopPunctuation">
4341                                                 <xsl:with-param name="chopString">
4342                                                         <xsl:value-of select="substring($str,1,string-length($str)-1)"/>
4343                                                 </xsl:with-param>
4344                                         </xsl:call-template>
4345                                 </mads:title>
4346                                 <xsl:call-template name="part"/>
4347                                 <!-- <xsl:call-template name="uri"/> -->
4348                         </mads:titleInfo>
4349                 </xsl:if>
4350         </xsl:template>
4351
4352
4353         <!-- ========== topics  ========== -->
4354         <xsl:template match="marc:subfield[@code='x']">
4355                 <mads:topic>
4356                         <xsl:call-template name="chopPunctuation">
4357                                 <xsl:with-param name="chopString">
4358                                         <xsl:value-of select="."/>
4359                                 </xsl:with-param>
4360                         </xsl:call-template>
4361                 </mads:topic>
4362         </xsl:template>
4363         
4364         <!-- 2.06 fix -->
4365         <xsl:template
4366                 match="marc:datafield[@tag=150][marc:subfield[@code='a' or @code='b']]|marc:datafield[@tag=180][marc:subfield[@code='x']]">
4367                 <xsl:call-template name="topic"/>
4368                 <xsl:apply-templates select="marc:subfield[@code!='i']"/>
4369         </xsl:template>
4370         <xsl:template
4371                 match="marc:datafield[@tag=450][marc:subfield[@code='a' or @code='b']]|marc:datafield[@tag=480][marc:subfield[@code='x']]">
4372                 <mads:variant>
4373                         <xsl:call-template name="variantTypeAttribute"/>
4374                         <xsl:call-template name="topic"/>
4375                 </mads:variant>
4376         </xsl:template>
4377         <xsl:template
4378                 match="marc:datafield[@tag=550 or @tag=750][marc:subfield[@code='a' or @code='b']]">
4379                 <mads:related>
4380                         <xsl:call-template name="relatedTypeAttribute"/>
4381                         <!-- <xsl:call-template name="uri"/> -->
4382                         <xsl:call-template name="topic"/>
4383                         <xsl:apply-templates select="marc:subfield[@code='z']"/>
4384                 </mads:related>
4385         </xsl:template>
4386         <xsl:template name="topic">
4387                 <mads:topic>
4388                         <xsl:call-template name="setAuthority"/>
4389                         <!-- tmee2006 dedupe 550a
4390                         <xsl:if test="@tag=550 or @tag=750">
4391                                 <xsl:call-template name="subfieldSelect">
4392                                         <xsl:with-param name="codes">ab</xsl:with-param>
4393                                 </xsl:call-template>
4394                         </xsl:if>       
4395                         -->
4396                         <xsl:choose>
4397                                 <xsl:when test="@tag=180 or @tag=480 or @tag=580 or @tag=780">
4398                                         <xsl:call-template name="chopPunctuation">
4399                                                 <xsl:with-param name="chopString">
4400                                                         <xsl:apply-templates select="marc:subfield[@code='x']"/>
4401                                                 </xsl:with-param>
4402                                         </xsl:call-template>
4403                                 </xsl:when>
4404                         </xsl:choose>
4405                         <xsl:call-template name="chopPunctuation">
4406                                 <xsl:with-param name="chopString">
4407                                         <xsl:choose>
4408                                                 <xsl:when test="@tag=180 or @tag=480 or @tag=580 or @tag=780">
4409                                                         <xsl:apply-templates select="marc:subfield[@code='x']"/>
4410                                                 </xsl:when>
4411                                                 <xsl:otherwise>
4412                                                         <xsl:call-template name="subfieldSelect">
4413                                                                 <xsl:with-param name="codes">ab</xsl:with-param>
4414                                                         </xsl:call-template>
4415                                                 </xsl:otherwise>
4416                                         </xsl:choose>
4417                                 </xsl:with-param>
4418                         </xsl:call-template>
4419                 </mads:topic>
4420         </xsl:template>
4421
4422         <!-- ========= temporals  ========== -->
4423         <xsl:template match="marc:subfield[@code='y']">
4424                 <mads:temporal>
4425                         <xsl:call-template name="chopPunctuation">
4426                                 <xsl:with-param name="chopString">
4427                                         <xsl:value-of select="."/>
4428                                 </xsl:with-param>
4429                         </xsl:call-template>
4430                 </mads:temporal>
4431         </xsl:template>
4432         <xsl:template
4433                 match="marc:datafield[@tag=148][marc:subfield[@code='a']]|marc:datafield[@tag=182 ][marc:subfield[@code='y']]">
4434                 <xsl:call-template name="temporal"/>
4435         </xsl:template>
4436         <xsl:template
4437                 match="marc:datafield[@tag=448][marc:subfield[@code='a']]|marc:datafield[@tag=482][marc:subfield[@code='y']]">
4438                 <mads:variant>
4439                         <xsl:call-template name="variantTypeAttribute"/>
4440                         <xsl:call-template name="temporal"/>
4441                 </mads:variant>
4442         </xsl:template>
4443         <xsl:template
4444                 match="marc:datafield[@tag=548 or @tag=748][marc:subfield[@code='a']]|marc:datafield[@tag=582 or @tag=782][marc:subfield[@code='y']]">
4445                 <mads:related>
4446                         <xsl:call-template name="relatedTypeAttribute"/>
4447                         <!-- <xsl:call-template name="uri"/> -->
4448                         <xsl:call-template name="temporal"/>
4449                 </mads:related>
4450         </xsl:template>
4451         <xsl:template name="temporal">
4452                 <mads:temporal>
4453                         <xsl:call-template name="setAuthority"/>
4454                         <xsl:if test="@tag=548 or @tag=748">
4455                                 <xsl:value-of select="marc:subfield[@code='a']"/>
4456                         </xsl:if>
4457                         <xsl:call-template name="chopPunctuation">
4458                                 <xsl:with-param name="chopString">
4459                                         <xsl:choose>
4460                                                 <xsl:when test="@tag=182 or @tag=482 or @tag=582 or @tag=782">
4461                                                         <xsl:apply-templates select="marc:subfield[@code='y']"/>
4462                                                 </xsl:when>
4463                                                 <xsl:otherwise>
4464                                                         <xsl:value-of select="marc:subfield[@code='a']"/>
4465                                                 </xsl:otherwise>
4466                                         </xsl:choose>
4467                                 </xsl:with-param>
4468                         </xsl:call-template>
4469                 </mads:temporal>
4470                 <xsl:apply-templates select="marc:subfield[@code!='i']"/>
4471         </xsl:template>
4472
4473         <!-- ========== genre  ========== -->
4474         <xsl:template match="marc:subfield[@code='v']">
4475                 <mads:genre>
4476                         <xsl:call-template name="chopPunctuation">
4477                                 <xsl:with-param name="chopString">
4478                                         <xsl:value-of select="."/>
4479                                 </xsl:with-param>
4480                         </xsl:call-template>
4481                 </mads:genre>
4482         </xsl:template>
4483         <xsl:template
4484                 match="marc:datafield[@tag=155][marc:subfield[@code='a']]|marc:datafield[@tag=185][marc:subfield[@code='v']]">
4485                 <xsl:call-template name="genre"/>
4486         </xsl:template>
4487         <xsl:template
4488                 match="marc:datafield[@tag=455][marc:subfield[@code='a']]|marc:datafield[@tag=485 ][marc:subfield[@code='v']]">
4489                 <mads:variant>
4490                         <xsl:call-template name="variantTypeAttribute"/>
4491                         <xsl:call-template name="genre"/>
4492                 </mads:variant>
4493         </xsl:template>
4494         <!--
4495         <xsl:template match="marc:datafield[@tag=555]">
4496                 <mads:related>
4497                         <xsl:call-template name="relatedTypeAttribute"/>
4498                         <xsl:call-template name="uri"/>
4499                         <xsl:call-template name="genre"/>
4500                 </mads:related>
4501         </xsl:template>
4502         -->
4503         <xsl:template
4504                 match="marc:datafield[@tag=555 or @tag=755][marc:subfield[@code='a']]|marc:datafield[@tag=585][marc:subfield[@code='v']]">
4505                 <mads:related>
4506                         <xsl:call-template name="relatedTypeAttribute"/>
4507                         <xsl:call-template name="genre"/>
4508                 </mads:related>
4509         </xsl:template>
4510         <xsl:template name="genre">
4511                 <mads:genre>
4512                         <xsl:if test="@tag=555">
4513                                 <xsl:value-of select="marc:subfield[@code='a']"/>
4514                         </xsl:if>
4515                         <xsl:call-template name="setAuthority"/>
4516                         <xsl:call-template name="chopPunctuation">
4517                                 <xsl:with-param name="chopString">
4518                                         <xsl:choose>
4519                                                 <!-- 2.07 fix -->
4520                                                 <xsl:when test="@tag='555'"/>
4521                                                 <xsl:when test="@tag=185 or @tag=485 or @tag=585">
4522                                                         <xsl:apply-templates select="marc:subfield[@code='v']"/>
4523                                                 </xsl:when>
4524                                                 <xsl:otherwise>
4525                                                         <xsl:value-of select="marc:subfield[@code='a']"/>
4526                                                 </xsl:otherwise>
4527                                         </xsl:choose>
4528                                 </xsl:with-param>
4529                         </xsl:call-template>
4530                 </mads:genre>
4531                 <xsl:apply-templates/>
4532         </xsl:template>
4533
4534         <!-- ========= geographic  ========== -->
4535         <xsl:template match="marc:subfield[@code='z']">
4536                 <mads:geographic>
4537                         <xsl:call-template name="chopPunctuation">
4538                                 <xsl:with-param name="chopString">
4539                                         <xsl:value-of select="."/>
4540                                 </xsl:with-param>
4541                         </xsl:call-template>
4542                 </mads:geographic>
4543         </xsl:template>
4544         <xsl:template name="geographic">
4545                 <mads:geographic>
4546                         <!-- 2.14 -->
4547                         <xsl:call-template name="setAuthority"/>
4548                         <!-- 2.13 -->
4549                         <xsl:if test="@tag=151 or @tag=551">
4550                                 <xsl:value-of select="marc:subfield[@code='a']"/>
4551                         </xsl:if>
4552                         <xsl:call-template name="chopPunctuation">
4553                                 <xsl:with-param name="chopString">
4554                                                 <xsl:if test="@tag=181 or @tag=481 or @tag=581">
4555                                                                 <xsl:apply-templates select="marc:subfield[@code='z']"/>
4556                                                 </xsl:if>
4557                                                 <!-- 2.13
4558                                                         <xsl:choose>
4559                                                 <xsl:when test="@tag=181 or @tag=481 or @tag=581">
4560                                                         <xsl:apply-templates select="marc:subfield[@code='z']"/>
4561                                                 </xsl:when>
4562                                         
4563                                                 <xsl:otherwise>
4564                                                         <xsl:value-of select="marc:subfield[@code='a']"/>
4565                                                 </xsl:otherwise>
4566                                                 </xsl:choose>
4567                                                 -->
4568                                 </xsl:with-param>
4569                         </xsl:call-template>
4570                 </mads:geographic>
4571                 <xsl:apply-templates select="marc:subfield[@code!='i']"/>
4572         </xsl:template>
4573         <xsl:template
4574                 match="marc:datafield[@tag=151][marc:subfield[@code='a']]|marc:datafield[@tag=181][marc:subfield[@code='z']]">
4575                 <xsl:call-template name="geographic"/>
4576         </xsl:template>
4577         <xsl:template
4578                 match="marc:datafield[@tag=451][marc:subfield[@code='a']]|marc:datafield[@tag=481][marc:subfield[@code='z']]">
4579                 <mads:variant>
4580                         <xsl:call-template name="variantTypeAttribute"/>
4581                         <xsl:call-template name="geographic"/>
4582                 </mads:variant>
4583         </xsl:template>
4584         <xsl:template
4585                 match="marc:datafield[@tag=551]|marc:datafield[@tag=581][marc:subfield[@code='z']]">
4586                 <mads:related>
4587                         <xsl:call-template name="relatedTypeAttribute"/>
4588                         <!-- <xsl:call-template name="uri"/> -->
4589                         <xsl:call-template name="geographic"/>
4590                 </mads:related>
4591         </xsl:template>
4592         <xsl:template match="marc:datafield[@tag=580]">
4593                 <mads:related>
4594                         <xsl:call-template name="relatedTypeAttribute"/>
4595                         <xsl:apply-templates select="marc:subfield[@code!='i']"/>
4596                 </mads:related>
4597         </xsl:template>
4598         <xsl:template
4599                 match="marc:datafield[@tag=751][marc:subfield[@code='z']]|marc:datafield[@tag=781][marc:subfield[@code='z']]">
4600                 <mads:related>
4601                         <xsl:call-template name="relatedTypeAttribute"/>
4602                         <xsl:call-template name="geographic"/>
4603                 </mads:related>
4604         </xsl:template>
4605         <xsl:template match="marc:datafield[@tag=755]">
4606                 <mads:related>
4607                         <xsl:call-template name="relatedTypeAttribute"/>
4608                         <xsl:call-template name="genre"/>
4609                         <xsl:call-template name="setAuthority"/>
4610                         <xsl:apply-templates select="marc:subfield[@code!='i']"/>
4611                 </mads:related>
4612         </xsl:template>
4613         <xsl:template match="marc:datafield[@tag=780]">
4614                 <mads:related>
4615                         <xsl:call-template name="relatedTypeAttribute"/>
4616                         <xsl:apply-templates select="marc:subfield[@code!='i']"/>
4617                 </mads:related>
4618         </xsl:template>
4619         <xsl:template match="marc:datafield[@tag=785]">
4620                 <mads:related>
4621                         <xsl:call-template name="relatedTypeAttribute"/>
4622                         <xsl:apply-templates select="marc:subfield[@code!='i']"/>
4623                 </mads:related>
4624         </xsl:template>
4625
4626         <!-- ========== notes  ========== -->
4627         <xsl:template match="marc:datafield[667 &lt;= @tag and @tag &lt;= 688]">
4628                 <mads:note>
4629                         <xsl:choose>
4630                                 <xsl:when test="@tag=667">
4631                                         <xsl:attribute name="type">nonpublic</xsl:attribute>
4632                                 </xsl:when>
4633                                 <xsl:when test="@tag=670">
4634                                         <xsl:attribute name="type">source</xsl:attribute>
4635                                 </xsl:when>
4636                                 <xsl:when test="@tag=675">
4637                                         <xsl:attribute name="type">notFound</xsl:attribute>
4638                                 </xsl:when>
4639                                 <xsl:when test="@tag=678">
4640                                         <xsl:attribute name="type">history</xsl:attribute>
4641                                 </xsl:when>
4642                                 <xsl:when test="@tag=681">
4643                                         <xsl:attribute name="type">subject example</xsl:attribute>
4644                                 </xsl:when>
4645                                 <xsl:when test="@tag=682">
4646                                         <xsl:attribute name="type">deleted heading information</xsl:attribute>
4647                                 </xsl:when>
4648                                 <xsl:when test="@tag=688">
4649                                         <xsl:attribute name="type">application history</xsl:attribute>
4650                                 </xsl:when>
4651                         </xsl:choose>
4652                         <xsl:call-template name="chopPunctuation">
4653                                 <xsl:with-param name="chopString">
4654                                         <xsl:choose>
4655                                                 <xsl:when test="@tag=667 or @tag=675">
4656                                                         <xsl:value-of select="marc:subfield[@code='a']"/>
4657                                                 </xsl:when>
4658                                                 <xsl:when test="@tag=670 or @tag=678">
4659                                                         <xsl:call-template name="subfieldSelect">
4660                                                                 <xsl:with-param name="codes">ab</xsl:with-param>
4661                                                         </xsl:call-template>
4662                                                 </xsl:when>
4663                                                 <xsl:when test="680 &lt;= @tag and @tag &lt;=688">
4664                                                         <xsl:call-template name="subfieldSelect">
4665                                                                 <xsl:with-param name="codes">ai</xsl:with-param>
4666                                                         </xsl:call-template>
4667                                                 </xsl:when>
4668                                         </xsl:choose>
4669                                 </xsl:with-param>
4670                         </xsl:call-template>
4671                 </mads:note>
4672         </xsl:template>
4673
4674         <!-- ========== url  ========== -->
4675         <xsl:template match="marc:datafield[@tag=856][marc:subfield[@code='u']]">
4676                 <mads:url>
4677                         <xsl:if test="marc:subfield[@code='z' or @code='3']">
4678                                 <xsl:attribute name="displayLabel">
4679                                         <xsl:call-template name="subfieldSelect">
4680                                                 <xsl:with-param name="codes">z3</xsl:with-param>
4681                                         </xsl:call-template>
4682                                 </xsl:attribute>
4683                         </xsl:if>
4684                         <xsl:value-of select="marc:subfield[@code='u']"/>
4685                 </mads:url>
4686         </xsl:template>
4687
4688         <xsl:template name="relatedTypeAttribute">
4689                 <xsl:choose>
4690                         <xsl:when
4691                                 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">
4692                                 <xsl:if test="substring(marc:subfield[@code='w'],1,1)='a'">
4693                                         <xsl:attribute name="type">earlier</xsl:attribute>
4694                                 </xsl:if>
4695                                 <xsl:if test="substring(marc:subfield[@code='w'],1,1)='b'">
4696                                         <xsl:attribute name="type">later</xsl:attribute>
4697                                 </xsl:if>
4698                                 <xsl:if test="substring(marc:subfield[@code='w'],1,1)='t'">
4699                                         <xsl:attribute name="type">parentOrg</xsl:attribute>
4700                                 </xsl:if>
4701                                 <xsl:if test="substring(marc:subfield[@code='w'],1,1)='g'">
4702                                         <xsl:attribute name="type">broader</xsl:attribute>
4703                                 </xsl:if>
4704                                 <xsl:if test="substring(marc:subfield[@code='w'],1,1)='h'">
4705                                         <xsl:attribute name="type">narrower</xsl:attribute>
4706                                 </xsl:if>
4707                                 <xsl:if test="substring(marc:subfield[@code='w'],1,1)='r'">
4708                                         <xsl:attribute name="type">other</xsl:attribute>
4709                                 </xsl:if>
4710                                 <xsl:if test="contains('fin|', substring(marc:subfield[@code='w'],1,1))">
4711                                         <xsl:attribute name="type">other</xsl:attribute>
4712                                 </xsl:if>
4713                         </xsl:when>
4714                         <xsl:when test="@tag=530 or @tag=730">
4715                                 <xsl:attribute name="type">other</xsl:attribute>
4716                         </xsl:when>
4717                         <xsl:otherwise>
4718                                 <!-- 7xx -->
4719                                 <xsl:attribute name="type">equivalent</xsl:attribute>
4720                         </xsl:otherwise>
4721                 </xsl:choose>
4722                 <xsl:apply-templates select="marc:subfield[@code='i']"/>
4723         </xsl:template>
4724         
4725
4726
4727         <xsl:template name="variantTypeAttribute">
4728                 <xsl:choose>
4729                         <xsl:when
4730                                 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">
4731                                 <xsl:if test="substring(marc:subfield[@code='w'],1,1)='d'">
4732                                         <xsl:attribute name="type">acronym</xsl:attribute>
4733                                 </xsl:if>
4734                                 <xsl:if test="substring(marc:subfield[@code='w'],1,1)='n'">
4735                                         <xsl:attribute name="type">other</xsl:attribute>
4736                                 </xsl:if>
4737                                 <xsl:if test="contains('fit', substring(marc:subfield[@code='w'],1,1))">
4738                                         <xsl:attribute name="type">other</xsl:attribute>
4739                                 </xsl:if>
4740                         </xsl:when>
4741                         <xsl:otherwise>
4742                                 <!-- 430  -->
4743                                 <xsl:attribute name="type">other</xsl:attribute>
4744                         </xsl:otherwise>
4745                 </xsl:choose>
4746                 <xsl:apply-templates select="marc:subfield[@code='i']"/>
4747         </xsl:template>
4748
4749         <xsl:template name="setAuthority">
4750                 <xsl:choose>
4751                         <!-- can be called from the datafield or subfield level, so "..//@tag" means
4752                         the tag can be at the subfield's parent level or at the datafields own level -->
4753
4754                         <xsl:when
4755                                 test="ancestor-or-self::marc:datafield/@tag=100 and (@ind1=0 or @ind1=1) and $controlField008-11='a' and $controlField008-14='a'">
4756                                 <xsl:attribute name="authority">
4757                                         <xsl:text>naf</xsl:text>
4758                                 </xsl:attribute>
4759                         </xsl:when>
4760                         <xsl:when
4761                                 test="ancestor-or-self::marc:datafield/@tag=100 and (@ind1=0 or @ind1=1) 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=0 or @ind1=1) and $controlField008-11='k'">
4768                                 <xsl:attribute name="authority">
4769                                         <xsl:text>lacnaf</xsl:text>
4770                                 </xsl:attribute>
4771                         </xsl:when>
4772                         <xsl:when
4773                                 test="ancestor-or-self::marc:datafield/@tag=100 and @ind1=3 and $controlField008-11='a' and $controlField008-14='b'">
4774                                 <xsl:attribute name="authority">
4775                                         <xsl:text>lcsh</xsl:text>
4776                                 </xsl:attribute>
4777                         </xsl:when>
4778                         <xsl:when
4779                                 test="ancestor-or-self::marc:datafield/@tag=100 and @ind1=3 and $controlField008-11='k' and $controlField008-14='b'">
4780                                 <xsl:attribute name="authority">cash</xsl:attribute>
4781                         </xsl:when>
4782                         <xsl:when
4783                                 test="ancestor-or-self::marc:datafield/@tag=110 and $controlField008-11='a' and $controlField008-14='a'">
4784                                 <xsl:attribute name="authority">naf</xsl:attribute>
4785                         </xsl:when>
4786                         <xsl:when
4787                                 test="ancestor-or-self::marc:datafield/@tag=110 and $controlField008-11='a' and $controlField008-14='b'">
4788                                 <xsl:attribute name="authority">lcsh</xsl:attribute>
4789                         </xsl:when>
4790                         <xsl:when
4791                                 test="ancestor-or-self::marc:datafield/@tag=110 and $controlField008-11='k' and $controlField008-14='a'">
4792                                 <xsl:attribute name="authority">
4793                                         <xsl:text>lacnaf</xsl:text>
4794                                 </xsl:attribute>
4795                         </xsl:when>
4796                         <xsl:when
4797                                 test="ancestor-or-self::marc:datafield/@tag=110 and $controlField008-11='k' and $controlField008-14='b'">
4798                                 <xsl:attribute name="authority">
4799                                         <xsl:text>cash</xsl:text>
4800                                 </xsl:attribute>
4801                         </xsl:when>
4802                         <xsl:when
4803                                 test="100 &lt;= ancestor-or-self::marc:datafield/@tag and ancestor-or-self::marc:datafield/@tag &lt;= 155 and $controlField008-11='b'">
4804                                 <xsl:attribute name="authority">
4805                                         <xsl:text>lcshcl</xsl:text>
4806                                 </xsl:attribute>
4807                         </xsl:when>
4808                         <xsl:when
4809                                 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'">
4810                                 <xsl:attribute name="authority">
4811                                         <xsl:text>nlmnaf</xsl:text>
4812                                 </xsl:attribute>
4813                         </xsl:when>
4814                         <xsl:when
4815                                 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'">
4816                                 <xsl:attribute name="authority">
4817                                         <xsl:text>nalnaf</xsl:text>
4818                                 </xsl:attribute>
4819                         </xsl:when>
4820                         <xsl:when
4821                                 test="100 &lt;= ancestor-or-self::marc:datafield/@tag and ancestor-or-self::marc:datafield/@tag &lt;= 155 and $controlField008-11='r'">
4822                                 <xsl:attribute name="authority">
4823                                         <xsl:text>aat</xsl:text>
4824                                 </xsl:attribute>
4825                         </xsl:when>
4826                         <xsl:when
4827                                 test="100 &lt;= ancestor-or-self::marc:datafield/@tag and ancestor-or-self::marc:datafield/@tag &lt;= 155 and $controlField008-11='s'">
4828                                 <xsl:attribute name="authority">sears</xsl:attribute>
4829                         </xsl:when>
4830                         <xsl:when
4831                                 test="100 &lt;= ancestor-or-self::marc:datafield/@tag and ancestor-or-self::marc:datafield/@tag &lt;= 155 and $controlField008-11='v'">
4832                                 <xsl:attribute name="authority">rvm</xsl:attribute>
4833                         </xsl:when>
4834                         <xsl:when
4835                                 test="100 &lt;= ancestor-or-self::marc:datafield/@tag and ancestor-or-self::marc:datafield/@tag &lt;= 155 and $controlField008-11='z'">
4836                                 <xsl:attribute name="authority">
4837                                         <xsl:value-of
4838                                                 select="../marc:datafield[ancestor-or-self::marc:datafield/@tag=040]/marc:subfield[@code='f']"
4839                                         />
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='a' and $controlField008-14='a'">
4844                                 <xsl:attribute name="authority">
4845                                         <xsl:text>naf</xsl:text>
4846                                 </xsl:attribute>
4847                         </xsl:when>
4848                         <xsl:when
4849                                 test="(ancestor-or-self::marc:datafield/@tag=111 or ancestor-or-self::marc:datafield/@tag=130) and $controlField008-11='a' and $controlField008-14='b'">
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=111 or ancestor-or-self::marc:datafield/@tag=130) and $controlField008-11='k' ">
4856                                 <xsl:attribute name="authority">
4857                                         <xsl:text>lacnaf</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='a' ">
4862                                 <xsl:attribute name="authority">
4863                                         <xsl:text>lcsh</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='a' ">
4868                                 <xsl:attribute name="authority">
4869                                         <xsl:text>lcsh</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='c' ">
4874                                 <xsl:attribute name="authority">
4875                                         <xsl:text>mesh</xsl:text>
4876                                 </xsl:attribute>
4877                         </xsl:when>
4878                         <xsl:when
4879                                 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' ">
4880                                 <xsl:attribute name="authority">
4881                                         <xsl:text>nal</xsl:text>
4882                                 </xsl:attribute>
4883                         </xsl:when>
4884                         <xsl:when
4885                                 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' ">
4886                                 <xsl:attribute name="authority">
4887                                         <xsl:text>cash</xsl:text>
4888                                 </xsl:attribute>
4889                         </xsl:when>
4890                         <xsl:when
4891                                 test="ancestor-or-self::marc:datafield/@tag=151 and $controlField008-11='a' and $controlField008-14='a'">
4892                                 <xsl:attribute name="authority">
4893                                         <xsl:text>naf</xsl:text>
4894                                 </xsl:attribute>
4895                         </xsl:when>
4896                         <xsl:when
4897                                 test="ancestor-or-self::marc:datafield/@tag=151 and $controlField008-11='a' and $controlField008-14='b'">
4898                                 <xsl:attribute name="authority">lcsh</xsl:attribute>
4899                         </xsl:when>
4900                         <xsl:when
4901                                 test="ancestor-or-self::marc:datafield/@tag=151 and $controlField008-11='k' and $controlField008-14='a'">
4902                                 <xsl:attribute name="authority">lacnaf</xsl:attribute>
4903                         </xsl:when>
4904                         <xsl:when
4905                                 test="ancestor-or-self::marc:datafield/@tag=151 and $controlField008-11='k' and $controlField008-14='b'">
4906                                 <xsl:attribute name="authority">cash</xsl:attribute>
4907                         </xsl:when>
4908                         <xsl:when
4909                                 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'">
4910                                 <xsl:attribute name="authority">lcsh</xsl:attribute>
4911                         </xsl:when>
4912                         <xsl:when
4913                                 test="ancestor-or-self::marc:datafield/@tag=700 and (@ind1='0' or @ind1='1') and @ind2='0'">
4914                                 <xsl:attribute name="authority">naf</xsl:attribute>
4915                         </xsl:when>
4916                         <xsl:when
4917                                 test="ancestor-or-self::marc:datafield/@tag=700 and (@ind1='0' or @ind1='1') and @ind2='5'">
4918                                 <xsl:attribute name="authority">lacnaf</xsl:attribute>
4919                         </xsl:when>
4920                         <xsl:when test="ancestor-or-self::marc:datafield/@tag=700 and @ind1='3' and @ind2='0'">
4921                                 <xsl:attribute name="authority">lcsh</xsl:attribute>
4922                         </xsl:when>
4923                         <xsl:when test="ancestor-or-self::marc:datafield/@tag=700 and @ind1='3' and @ind2='5'">
4924                                 <xsl:attribute name="authority">cash</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='1'">
4928                                 <xsl:attribute name="authority">lcshcl</xsl:attribute>
4929                         </xsl:when>
4930                         <xsl:when
4931                                 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'">
4932                                 <xsl:attribute name="authority">nlmnaf</xsl:attribute>
4933                         </xsl:when>
4934                         <xsl:when
4935                                 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'">
4936                                 <xsl:attribute name="authority">nalnaf</xsl:attribute>
4937                         </xsl:when>
4938                         <xsl:when
4939                                 test="(700 &lt;= ancestor-or-self::marc:datafield/@tag and ancestor-or-self::marc:datafield/@tag &lt;= 755 ) and @ind2='6'">
4940                                 <xsl:attribute name="authority">rvm</xsl:attribute>
4941                         </xsl:when>
4942                         <xsl:when
4943                                 test="(700 &lt;= ancestor-or-self::marc:datafield/@tag and ancestor-or-self::marc:datafield/@tag &lt;= 755 ) and @ind2='7'">
4944                                 <xsl:attribute name="authority">
4945                                         <xsl:value-of select="marc:subfield[@code='2']"/>
4946                                 </xsl:attribute>
4947                         </xsl:when>
4948                         <xsl:when
4949                                 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'">
4950                                 <xsl:attribute name="authority">lacnaf</xsl:attribute>
4951                         </xsl:when>
4952                         <xsl:when
4953                                 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'">
4954                                 <xsl:attribute name="authority">naf</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='0'">
4958                                 <xsl:attribute name="authority">lcsh</xsl:attribute>
4959                         </xsl:when>
4960                         <xsl:when
4961                                 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'">
4962                                 <xsl:attribute name="authority">mesh</xsl:attribute>
4963                         </xsl:when>
4964                         <xsl:when
4965                                 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'">
4966                                 <xsl:attribute name="authority">nal</xsl:attribute>
4967                         </xsl:when>
4968                         <xsl:when
4969                                 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'">
4970                                 <xsl:attribute name="authority">cash</xsl:attribute>
4971                         </xsl:when>
4972                 </xsl:choose>
4973         </xsl:template>
4974         <xsl:template match="*"/>
4975 </xsl:stylesheet>$XSLT$);
4976
4977
4978 SELECT evergreen.upgrade_deps_block_check('1069', :eg_version); --gmcharlt/kmlussier
4979
4980 -- subset of types listed in https://www.loc.gov/marc/authority/ad1xx3xx.html
4981 -- for now, ignoring subdivisions
4982 CREATE TYPE authority.heading_type AS ENUM (
4983     'personal_name',
4984     'corporate_name',
4985     'meeting_name',
4986     'uniform_title',
4987     'named_event',
4988     'chronological_term',
4989     'topical_term',
4990     'geographic_name',
4991     'genre_form_term',
4992     'medium_of_performance_term'
4993 );
4994
4995 CREATE TYPE authority.variant_heading_type AS ENUM (
4996     'abbreviation',
4997     'acronym',
4998     'translation',
4999     'expansion',
5000     'other',
5001     'hidden'
5002 );
5003
5004 CREATE TYPE authority.related_heading_type AS ENUM (
5005     'earlier',
5006     'later',
5007     'parent organization',
5008     'broader',
5009     'narrower',
5010     'equivalent',
5011     'other'
5012 );
5013
5014 CREATE TYPE authority.heading_purpose AS ENUM (
5015     'main',
5016     'variant',
5017     'related'
5018 );
5019
5020 CREATE TABLE authority.heading_field (
5021     id              SERIAL                      PRIMARY KEY,
5022     heading_type    authority.heading_type      NOT NULL,
5023     heading_purpose authority.heading_purpose   NOT NULL,
5024     label           TEXT                        NOT NULL,
5025     format          TEXT                        NOT NULL REFERENCES config.xml_transform (name) DEFAULT 'mads21',
5026     heading_xpath   TEXT                        NOT NULL,
5027     component_xpath TEXT                        NOT NULL,
5028     type_xpath      TEXT                        NULL, -- to extract related or variant type
5029     thesaurus_xpath TEXT                        NULL,
5030     thesaurus_override_xpath TEXT               NULL,
5031     joiner          TEXT                        NULL
5032 );
5033
5034 CREATE TABLE authority.heading_field_norm_map (
5035         id      SERIAL  PRIMARY KEY,
5036         field   INT     NOT NULL REFERENCES authority.heading_field (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
5037         norm    INT     NOT NULL REFERENCES config.index_normalizer (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
5038         params  TEXT,
5039         pos     INT     NOT NULL DEFAULT 0
5040 );
5041
5042 INSERT INTO authority.heading_field(heading_type, heading_purpose, label, heading_xpath, component_xpath, type_xpath, thesaurus_xpath, thesaurus_override_xpath) VALUES
5043  ( 'topical_term', 'main',    'Main Topical Term',    '/mads21:mads/mads21:authority', '//mads21:topic', NULL, '/mads21:mads/mads21:authority/mads21:topic[1]/@authority', NULL )
5044 ,( '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')
5045 ,( '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')
5046 ,( 'personal_name', 'main', 'Main Personal Name',     '/mads21:mads/mads21:authority', '//mads21:name[@type="personal"]', NULL, NULL, NULL )
5047 ,( 'personal_name', 'variant', 'Variant Personal Name',     '/mads21:mads/mads21:variant', '//mads21:name[@type="personal"]', NULL, NULL, NULL )
5048 ,( 'personal_name', 'related', 'Related Personal Name',     '/mads21:mads/mads21:related', '//mads21:name[@type="personal"]', '/mads21:related/@type', NULL, NULL )
5049 ,( 'corporate_name', 'main', 'Main Corporate name',     '/mads21:mads/mads21:authority', '//mads21:name[@type="corporate"]', NULL, NULL, NULL )
5050 ,( 'corporate_name', 'variant', 'Variant Corporate Name',     '/mads21:mads/mads21:variant', '//mads21:name[@type="corporate"]', NULL, NULL, NULL )
5051 ,( 'corporate_name', 'related', 'Related Corporate Name',     '/mads21:mads/mads21:related', '//mads21:name[@type="corporate"]', '/mads21:related/@type', NULL, NULL )
5052 ,( 'meeting_name', 'main', 'Main Meeting name',     '/mads21:mads/mads21:authority', '//mads21:name[@type="conference"]', NULL, NULL, NULL )
5053 ,( 'meeting_name', 'variant', 'Variant Meeting Name',     '/mads21:mads/mads21:variant', '//mads21:name[@type="conference"]', NULL, NULL, NULL )
5054 ,( 'meeting_name', 'related', 'Related Meeting Name',     '/mads21:mads/mads21:related', '//mads21:name[@type="meeting"]', '/mads21:related/@type', NULL, NULL )
5055 ,( 'geographic_name', 'main',    'Main Geographic Term',    '/mads21:mads/mads21:authority', '//mads21:geographic', NULL, '/mads21:mads/mads21:authority/mads21:geographic[1]/@authority', NULL )
5056 ,( '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')
5057 ,( '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')
5058 ,( 'genre_form_term', 'main',    'Main Genre/Form Term',    '/mads21:mads/mads21:authority', '//mads21:genre', NULL, '/mads21:mads/mads21:authority/mads21:genre[1]/@authority', NULL )
5059 ,( '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')
5060 ,( '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')
5061 ,( 'chronological_term', 'main',    'Main Chronological Term',    '/mads21:mads/mads21:authority', '//mads21:temporal', NULL, '/mads21:mads/mads21:authority/mads21:temporal[1]/@authority', NULL )
5062 ,( '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')
5063 ,( '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')
5064 ,( 'uniform_title', 'main',    'Main Uniform Title',    '/mads21:mads/mads21:authority', '//mads21:title', NULL, '/mads21:mads/mads21:authority/mads21:title[1]/@authority', NULL )
5065 ,( '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')
5066 ,( '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')
5067 ;
5068
5069 -- NACO normalize all the things
5070 INSERT INTO authority.heading_field_norm_map (field, norm, pos)
5071 SELECT id, 1, 0
5072 FROM authority.heading_field;
5073
5074 CREATE TYPE authority.heading AS (
5075     field               INT,
5076     type                authority.heading_type,
5077     purpose             authority.heading_purpose,
5078     variant_type        authority.variant_heading_type,
5079     related_type        authority.related_heading_type,
5080     thesaurus           TEXT,
5081     heading             TEXT,
5082     normalized_heading  TEXT
5083 );
5084
5085 CREATE OR REPLACE FUNCTION authority.extract_headings(marc TEXT, restrict INT[] DEFAULT NULL) RETURNS SETOF authority.heading AS $func$
5086 DECLARE
5087     idx         authority.heading_field%ROWTYPE;
5088     xfrm        config.xml_transform%ROWTYPE;
5089     prev_xfrm   TEXT;
5090     transformed_xml TEXT;
5091     heading_node    TEXT;
5092     heading_node_list   TEXT[];
5093     component_node    TEXT;
5094     component_node_list   TEXT[];
5095     raw_text    TEXT;
5096     normalized_text    TEXT;
5097     normalizer  RECORD;
5098     curr_text   TEXT;
5099     joiner      TEXT;
5100     type_value  TEXT;
5101     base_thesaurus TEXT := NULL;
5102     output_row  authority.heading;
5103 BEGIN
5104
5105     -- Loop over the indexing entries
5106     FOR idx IN SELECT * FROM authority.heading_field WHERE restrict IS NULL OR id = ANY (restrict) ORDER BY format LOOP
5107
5108         output_row.field   := idx.id;
5109         output_row.type    := idx.heading_type;
5110         output_row.purpose := idx.heading_purpose;
5111
5112         joiner := COALESCE(idx.joiner, ' ');
5113
5114         SELECT INTO xfrm * from config.xml_transform WHERE name = idx.format;
5115
5116         -- See if we can skip the XSLT ... it's expensive
5117         IF prev_xfrm IS NULL OR prev_xfrm <> xfrm.name THEN
5118             -- Can't skip the transform
5119             IF xfrm.xslt <> '---' THEN
5120                 transformed_xml := oils_xslt_process(marc, xfrm.xslt);
5121             ELSE
5122                 transformed_xml := marc;
5123             END IF;
5124
5125             prev_xfrm := xfrm.name;
5126         END IF;
5127
5128         IF idx.thesaurus_xpath IS NOT NULL THEN
5129             base_thesaurus := ARRAY_TO_STRING(oils_xpath(idx.thesaurus_xpath, transformed_xml, ARRAY[ARRAY[xfrm.prefix, xfrm.namespace_uri]]), '');
5130         END IF;
5131
5132         heading_node_list := oils_xpath( idx.heading_xpath, transformed_xml, ARRAY[ARRAY[xfrm.prefix, xfrm.namespace_uri]] );
5133
5134         FOR heading_node IN SELECT x FROM unnest(heading_node_list) AS x LOOP
5135
5136             CONTINUE WHEN heading_node !~ E'^\\s*<';
5137
5138             output_row.variant_type := NULL;
5139             output_row.related_type := NULL;
5140             output_row.thesaurus    := NULL;
5141             output_row.heading      := NULL;
5142
5143             IF idx.heading_purpose = 'variant' AND idx.type_xpath IS NOT NULL THEN
5144                 type_value := ARRAY_TO_STRING(oils_xpath(idx.type_xpath, heading_node, ARRAY[ARRAY[xfrm.prefix, xfrm.namespace_uri]]), '');
5145                 BEGIN
5146                     output_row.variant_type := type_value;
5147                 EXCEPTION WHEN invalid_text_representation THEN
5148                     RAISE NOTICE 'Do not recognize variant heading type %', type_value;
5149                 END;
5150             END IF;
5151             IF idx.heading_purpose = 'related' AND idx.type_xpath IS NOT NULL THEN
5152                 type_value := ARRAY_TO_STRING(oils_xpath(idx.type_xpath, heading_node, ARRAY[ARRAY[xfrm.prefix, xfrm.namespace_uri]]), '');
5153                 BEGIN
5154                     output_row.related_type := type_value;
5155                 EXCEPTION WHEN invalid_text_representation THEN
5156                     RAISE NOTICE 'Do not recognize related heading type %', type_value;
5157                 END;
5158             END IF;
5159  
5160             IF idx.thesaurus_override_xpath IS NOT NULL THEN
5161                 output_row.thesaurus := ARRAY_TO_STRING(oils_xpath(idx.thesaurus_override_xpath, heading_node, ARRAY[ARRAY[xfrm.prefix, xfrm.namespace_uri]]), '');
5162             END IF;
5163             IF output_row.thesaurus IS NULL THEN
5164                 output_row.thesaurus := base_thesaurus;
5165             END IF;
5166
5167             raw_text := NULL;
5168
5169             -- now iterate over components of heading
5170             component_node_list := oils_xpath( idx.component_xpath, heading_node, ARRAY[ARRAY[xfrm.prefix, xfrm.namespace_uri]] );
5171             FOR component_node IN SELECT x FROM unnest(component_node_list) AS x LOOP
5172             -- XXX much of this should be moved into oils_xpath_string...
5173                 curr_text := ARRAY_TO_STRING(evergreen.array_remove_item_by_value(evergreen.array_remove_item_by_value(
5174                     oils_xpath( '//text()', -- get the content of all the nodes within the main selected node
5175                         REGEXP_REPLACE( component_node, E'\\s+', ' ', 'g' ) -- Translate adjacent whitespace to a single space
5176                     ), ' '), ''),  -- throw away morally empty (bankrupt?) strings
5177                     joiner
5178                 );
5179
5180                 CONTINUE WHEN curr_text IS NULL OR curr_text = '';
5181
5182                 IF raw_text IS NOT NULL THEN
5183                     raw_text := raw_text || joiner;
5184                 END IF;
5185
5186                 raw_text := COALESCE(raw_text,'') || curr_text;
5187             END LOOP;
5188
5189             IF raw_text IS NOT NULL THEN
5190                 output_row.heading := raw_text;
5191                 normalized_text := raw_text;
5192
5193                 FOR normalizer IN
5194                     SELECT  n.func AS func,
5195                             n.param_count AS param_count,
5196                             m.params AS params
5197                     FROM  config.index_normalizer n
5198                             JOIN authority.heading_field_norm_map m ON (m.norm = n.id)
5199                     WHERE m.field = idx.id
5200                     ORDER BY m.pos LOOP
5201             
5202                         EXECUTE 'SELECT ' || normalizer.func || '(' ||
5203                             quote_literal( normalized_text ) ||
5204                             CASE
5205                                 WHEN normalizer.param_count > 0
5206                                     THEN ',' || REPLACE(REPLACE(BTRIM(normalizer.params,'[]'),E'\'',E'\\\''),E'"',E'\'')
5207                                     ELSE ''
5208                                 END ||
5209                             ')' INTO normalized_text;
5210             
5211                 END LOOP;
5212             
5213                 output_row.normalized_heading := normalized_text;
5214             
5215                 RETURN NEXT output_row;
5216             END IF;
5217         END LOOP;
5218
5219     END LOOP;
5220 END;
5221 $func$ LANGUAGE PLPGSQL;
5222
5223 CREATE OR REPLACE FUNCTION authority.extract_headings(rid BIGINT, restrict INT[] DEFAULT NULL) RETURNS SETOF authority.heading AS $func$
5224 DECLARE
5225     auth        authority.record_entry%ROWTYPE;
5226     output_row  authority.heading;
5227 BEGIN
5228     -- Get the record
5229     SELECT INTO auth * FROM authority.record_entry WHERE id = rid;
5230
5231     RETURN QUERY SELECT * FROM authority.extract_headings(auth.marc, restrict);
5232 END;
5233 $func$ LANGUAGE PLPGSQL;
5234
5235 CREATE OR REPLACE FUNCTION authority.simple_heading_set( marcxml TEXT ) RETURNS SETOF authority.simple_heading AS $func$
5236 DECLARE
5237     res             authority.simple_heading%ROWTYPE;
5238     acsaf           authority.control_set_authority_field%ROWTYPE;
5239     heading_row     authority.heading%ROWTYPE;
5240     tag_used        TEXT;
5241     nfi_used        TEXT;
5242     sf              TEXT;
5243     cset            INT;
5244     heading_text    TEXT;
5245     joiner_text     TEXT;
5246     sort_text       TEXT;
5247     tmp_text        TEXT;
5248     tmp_xml         TEXT;
5249     first_sf        BOOL;
5250     auth_id         INT DEFAULT COALESCE(NULLIF(oils_xpath_string('//*[@tag="901"]/*[local-name()="subfield" and @code="c"]', marcxml), ''), '0')::INT; 
5251 BEGIN
5252
5253     SELECT control_set INTO cset FROM authority.record_entry WHERE id = auth_id;
5254
5255     IF cset IS NULL THEN
5256         SELECT  control_set INTO cset
5257           FROM  authority.control_set_authority_field
5258           WHERE tag IN ( SELECT  UNNEST(XPATH('//*[starts-with(@tag,"1")]/@tag',marcxml::XML)::TEXT[]))
5259           LIMIT 1;
5260     END IF;
5261
5262     res.record := auth_id;
5263     res.thesaurus := authority.extract_thesaurus(marcxml);
5264
5265     FOR acsaf IN SELECT * FROM authority.control_set_authority_field WHERE control_set = cset LOOP
5266         res.atag := acsaf.id;
5267
5268         IF acsaf.heading_field IS NULL THEN
5269             tag_used := acsaf.tag;
5270             nfi_used := acsaf.nfi;
5271             joiner_text := COALESCE(acsaf.joiner, ' ');
5272     
5273             FOR tmp_xml IN SELECT UNNEST(XPATH('//*[@tag="'||tag_used||'"]', marcxml::XML)::TEXT[]) LOOP
5274     
5275                 heading_text := COALESCE(
5276                     oils_xpath_string('./*[contains("'||acsaf.display_sf_list||'",@code)]', tmp_xml, joiner_text),
5277                     ''
5278                 );
5279     
5280                 IF nfi_used IS NOT NULL THEN
5281     
5282                     sort_text := SUBSTRING(
5283                         heading_text FROM
5284                         COALESCE(
5285                             NULLIF(
5286                                 REGEXP_REPLACE(
5287                                     oils_xpath_string('./@ind'||nfi_used, tmp_xml::TEXT),
5288                                     $$\D+$$,
5289                                     '',
5290                                     'g'
5291                                 ),
5292                                 ''
5293                             )::INT,
5294                             0
5295                         ) + 1
5296                     );
5297     
5298                 ELSE
5299                     sort_text := heading_text;
5300                 END IF;
5301     
5302                 IF heading_text IS NOT NULL AND heading_text <> '' THEN
5303                     res.value := heading_text;
5304                     res.sort_value := public.naco_normalize(sort_text);
5305                     res.index_vector = to_tsvector('keyword'::regconfig, res.sort_value);
5306                     RETURN NEXT res;
5307                 END IF;
5308     
5309             END LOOP;
5310         ELSE
5311             FOR heading_row IN SELECT * FROM authority.extract_headings(marcxml, ARRAY[acsaf.heading_field]) LOOP
5312                 res.value := heading_row.heading;
5313                 res.sort_value := heading_row.normalized_heading;
5314                 res.index_vector = to_tsvector('keyword'::regconfig, res.sort_value);
5315                 RETURN NEXT res;
5316             END LOOP;
5317         END IF;
5318     END LOOP;
5319
5320     RETURN;
5321 END;
5322 $func$ LANGUAGE PLPGSQL STABLE STRICT;
5323
5324 ALTER TABLE authority.control_set_authority_field ADD COLUMN heading_field INTEGER REFERENCES authority.heading_field(id);
5325
5326 UPDATE authority.control_set_authority_field acsaf
5327 SET heading_field = ahf.id
5328 FROM authority.heading_field ahf
5329 WHERE tag = '100'
5330 AND control_set = 1
5331 AND ahf.heading_purpose = 'main'
5332 AND ahf.heading_type = 'personal_name';
5333 UPDATE authority.control_set_authority_field acsaf
5334 SET heading_field = ahf.id
5335 FROM authority.heading_field ahf
5336 WHERE tag = '400'
5337 AND control_set = 1
5338 AND ahf.heading_purpose = 'variant'
5339 AND ahf.heading_type = 'personal_name';
5340 UPDATE authority.control_set_authority_field acsaf
5341 SET heading_field = ahf.id
5342 FROM authority.heading_field ahf
5343 WHERE tag = '500'
5344 AND control_set = 1
5345 AND ahf.heading_purpose = 'related'
5346 AND ahf.heading_type = 'personal_name';
5347
5348 UPDATE authority.control_set_authority_field acsaf
5349 SET heading_field = ahf.id
5350 FROM authority.heading_field ahf
5351 WHERE tag = '110'
5352 AND control_set = 1
5353 AND ahf.heading_purpose = 'main'
5354 AND ahf.heading_type = 'corporate_name';
5355 UPDATE authority.control_set_authority_field acsaf
5356 SET heading_field = ahf.id
5357 FROM authority.heading_field ahf
5358 WHERE tag = '410'
5359 AND control_set = 1
5360 AND ahf.heading_purpose = 'variant'
5361 AND ahf.heading_type = 'corporate_name';
5362 UPDATE authority.control_set_authority_field acsaf
5363 SET heading_field = ahf.id
5364 FROM authority.heading_field ahf
5365 WHERE tag = '510'
5366 AND control_set = 1
5367 AND ahf.heading_purpose = 'related'
5368 AND ahf.heading_type = 'corporate_name';
5369
5370 UPDATE authority.control_set_authority_field acsaf
5371 SET heading_field = ahf.id
5372 FROM authority.heading_field ahf
5373 WHERE tag = '111'
5374 AND control_set = 1
5375 AND ahf.heading_purpose = 'main'
5376 AND ahf.heading_type = 'meeting_name';
5377 UPDATE authority.control_set_authority_field acsaf
5378 SET heading_field = ahf.id
5379 FROM authority.heading_field ahf
5380 WHERE tag = '411'
5381 AND control_set = 1
5382 AND ahf.heading_purpose = 'variant'
5383 AND ahf.heading_type = 'meeting_name';
5384 UPDATE authority.control_set_authority_field acsaf
5385 SET heading_field = ahf.id
5386 FROM authority.heading_field ahf
5387 WHERE tag = '511'
5388 AND control_set = 1
5389 AND ahf.heading_purpose = 'related'
5390 AND ahf.heading_type = 'meeting_name';
5391
5392 UPDATE authority.control_set_authority_field acsaf
5393 SET heading_field = ahf.id
5394 FROM authority.heading_field ahf
5395 WHERE tag = '130'
5396 AND control_set = 1
5397 AND ahf.heading_purpose = 'main'
5398 AND ahf.heading_type = 'uniform_title';
5399 UPDATE authority.control_set_authority_field acsaf
5400 SET heading_field = ahf.id
5401 FROM authority.heading_field ahf
5402 WHERE tag = '430'
5403 AND control_set = 1
5404 AND ahf.heading_purpose = 'variant'
5405 AND ahf.heading_type = 'uniform_title';
5406 UPDATE authority.control_set_authority_field acsaf
5407 SET heading_field = ahf.id
5408 FROM authority.heading_field ahf
5409 WHERE tag = '530'
5410 AND control_set = 1
5411 AND ahf.heading_purpose = 'related'
5412 AND ahf.heading_type = 'uniform_title';
5413
5414 UPDATE authority.control_set_authority_field acsaf
5415 SET heading_field = ahf.id
5416 FROM authority.heading_field ahf
5417 WHERE tag = '150'
5418 AND control_set = 1
5419 AND ahf.heading_purpose = 'main'
5420 AND ahf.heading_type = 'topical_term';
5421 UPDATE authority.control_set_authority_field acsaf
5422 SET heading_field = ahf.id
5423 FROM authority.heading_field ahf
5424 WHERE tag = '450'
5425 AND control_set = 1
5426 AND ahf.heading_purpose = 'variant'
5427 AND ahf.heading_type = 'topical_term';
5428 UPDATE authority.control_set_authority_field acsaf
5429 SET heading_field = ahf.id
5430 FROM authority.heading_field ahf
5431 WHERE tag = '550'
5432 AND control_set = 1
5433 AND ahf.heading_purpose = 'related'
5434 AND ahf.heading_type = 'topical_term';
5435
5436 UPDATE authority.control_set_authority_field acsaf
5437 SET heading_field = ahf.id
5438 FROM authority.heading_field ahf
5439 WHERE tag = '151'
5440 AND control_set = 1
5441 AND ahf.heading_purpose = 'main'
5442 AND ahf.heading_type = 'geographic_name';
5443 UPDATE authority.control_set_authority_field acsaf
5444 SET heading_field = ahf.id
5445 FROM authority.heading_field ahf
5446 WHERE tag = '451'
5447 AND control_set = 1
5448 AND ahf.heading_purpose = 'variant'
5449 AND ahf.heading_type = 'geographic_name';
5450 UPDATE authority.control_set_authority_field acsaf
5451 SET heading_field = ahf.id
5452 FROM authority.heading_field ahf
5453 WHERE tag = '551'
5454 AND control_set = 1
5455 AND ahf.heading_purpose = 'related'
5456 AND ahf.heading_type = 'geographic_name';
5457
5458 UPDATE authority.control_set_authority_field acsaf
5459 SET heading_field = ahf.id
5460 FROM authority.heading_field ahf
5461 WHERE tag = '155'
5462 AND control_set = 1
5463 AND ahf.heading_purpose = 'main'
5464 AND ahf.heading_type = 'genre_form_term';
5465 UPDATE authority.control_set_authority_field acsaf
5466 SET heading_field = ahf.id
5467 FROM authority.heading_field ahf
5468 WHERE tag = '455'
5469 AND control_set = 1
5470 AND ahf.heading_purpose = 'variant'
5471 AND ahf.heading_type = 'genre_form_term';
5472 UPDATE authority.control_set_authority_field acsaf
5473 SET heading_field = ahf.id
5474 FROM authority.heading_field ahf
5475 WHERE tag = '555'
5476 AND control_set = 1
5477 AND ahf.heading_purpose = 'related'
5478 AND ahf.heading_type = 'genre_form_term';
5479
5480
5481 SELECT evergreen.upgrade_deps_block_check('1070', :eg_version); --miker/gmcharlt/kmlussier
5482
5483 CREATE TRIGGER thes_code_tracking_trigger
5484     AFTER UPDATE ON authority.thesaurus
5485     FOR EACH ROW EXECUTE PROCEDURE oils_i18n_code_tracking('at');
5486
5487 ALTER TABLE authority.thesaurus ADD COLUMN short_code TEXT, ADD COLUMN uri TEXT;
5488
5489 DELETE FROM authority.thesaurus WHERE control_set = 1 AND code NOT IN ('n',' ','|');
5490 UPDATE authority.thesaurus SET short_code = code;
5491
5492 CREATE TEMP TABLE thesauri (code text, uri text, name text, xlate hstore);
5493 COPY thesauri (code, uri, name, xlate) FROM STDIN;
5494 migfg   http://id.loc.gov/vocabulary/genreFormSchemes/migfg     Moving image genre-form guide   
5495 reveal  http://id.loc.gov/vocabulary/genreFormSchemes/reveal    REVEAL: fiction indexing and genre headings     
5496 dct     http://id.loc.gov/vocabulary/genreFormSchemes/dct       Dublin Core list of resource types      
5497 gmgpc   http://id.loc.gov/vocabulary/genreFormSchemes/gmgpc     Thesaurus for graphic materials: TGM II, Genre and physical characteristic terms        
5498 rbgenr  http://id.loc.gov/vocabulary/genreFormSchemes/rbgenr    Genre terms: a thesaurus for use in rare book and special collections cataloguing       
5499 sgp     http://id.loc.gov/vocabulary/genreFormSchemes/sgp       Svenska genrebeteckningar fr periodika  "sv"=>"Svenska genrebeteckningar fr periodika"
5500 estc    http://id.loc.gov/vocabulary/genreFormSchemes/estc      Eighteenth century short title catalogue, the cataloguing rules. New ed.        
5501 ftamc   http://id.loc.gov/vocabulary/genreFormSchemes/ftamc     Form terms for archival and manuscripts control 
5502 alett   http://id.loc.gov/vocabulary/genreFormSchemes/alett     An alphabetical list of English text types      
5503 gtlm    http://id.loc.gov/vocabulary/genreFormSchemes/gtlm      Genre terms for law materials: a thesaurus      
5504 rbprov  http://id.loc.gov/vocabulary/genreFormSchemes/rbprov    Provenance evidence: a thesaurus for use in rare book and special collections cataloging        
5505 rbbin   http://id.loc.gov/vocabulary/genreFormSchemes/rbbin     Binding terms: a thesaurus for use in rare book and special collections cataloguing     
5506 fbg     http://id.loc.gov/vocabulary/genreFormSchemes/fbg       Films by genre /dd>     
5507 isbdmedia       http://id.loc.gov/vocabulary/genreFormSchemes/isbdmedia ISBD Area 0 [media]     
5508 marccategory    http://id.loc.gov/vocabulary/genreFormSchemes/marccategory      MARC form category term list    
5509 gnd-music       http://id.loc.gov/vocabulary/genreFormSchemes/gnd-music Gemeinsame Normdatei: Musikalische Ausgabeform  
5510 proysen http://id.loc.gov/vocabulary/genreFormSchemes/proysen   Prøysen: emneord for Prøysen-bibliografien        
5511 rdacarrier      http://id.loc.gov/vocabulary/genreFormSchemes/rdacarrier        Term and code list for RDA carrier types        
5512 gnd     http://id.loc.gov/vocabulary/genreFormSchemes/gnd       Gemeinsame Normdatei    
5513 cjh     http://id.loc.gov/vocabulary/genreFormSchemes/cjh       Center for Jewish History thesaurus     
5514 rbpri   http://id.loc.gov/vocabulary/genreFormSchemes/rbpri     Printing & publishing evidence: a thesaurus for use in rare book and special collections cataloging     
5515 fgtpcm  http://id.loc.gov/vocabulary/genreFormSchemes/fgtpcm    Form/genre terms for printed cartoon material   
5516 rbpub   http://id.loc.gov/vocabulary/genreFormSchemes/rbpub     Printing and publishing evidence: a thesaurus for use in rare book and special collections cataloging   
5517 gmd     http://id.loc.gov/vocabulary/genreFormSchemes/gmd       Anglo-American Cataloguing Rules general material designation   
5518 rbpap   http://id.loc.gov/vocabulary/genreFormSchemes/rbpap     Paper terms: a thesaurus for use in rare book and special collections cataloging        
5519 rdamedia        http://id.loc.gov/vocabulary/genreFormSchemes/rdamedia  Term and code list for RDA media types  
5520 marcsmd http://id.loc.gov/vocabulary/genreFormSchemes/marcsmd   MARC specific material form term list   
5521 saogf   http://id.loc.gov/vocabulary/genreFormSchemes/saogf     Svenska Ã¤mnesord - Genre/Form        "sv"=>"Svenska Ã¤mnesord - Genre/Form"
5522 lcgft   http://id.loc.gov/vocabulary/genreFormSchemes/lcgft     Library of Congress genre/form terms for library and archival materials 
5523 muzeukv http://id.loc.gov/vocabulary/genreFormSchemes/muzeukv   MuzeVideo UK DVD and UMD film genre classification      
5524 mim     http://id.loc.gov/vocabulary/genreFormSchemes/mim       Moving image materials: genre terms     
5525 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      
5526 gnd-content     http://id.loc.gov/vocabulary/genreFormSchemes/gnd-content       Gemeinsame Normdatei: Beschreibung des Inhalts  
5527 bgtchm  http://id.loc.gov/vocabulary/genreFormSchemes/bgtchm    Basic genre terms for cultural heritage materials       
5528 gsafd   http://id.loc.gov/vocabulary/genreFormSchemes/gsafd     Guidelines on subject access to individual works of fiction, drama, etc 
5529 marcform        http://id.loc.gov/vocabulary/genreFormSchemes/marcform  MARC form of item term list     
5530 marcgt  http://id.loc.gov/vocabulary/genreFormSchemes/marcgt    MARC genre terms        
5531 barngf  http://id.loc.gov/vocabulary/genreFormSchemes/barngf    Svenska Ã¤mnesord för barn - Genre/Form    "sv"=>"Svenska Ã¤mnesord för barn - Genre/Form"
5532 ngl     http://id.loc.gov/vocabulary/genreFormSchemes/ngl       Newspaper genre list    
5533 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"
5534 tgfbne  http://id.loc.gov/vocabulary/genreFormSchemes/tgfbne    Términos de género/forma de la Biblioteca Nacional de España   
5535 nbdbgf  http://id.loc.gov/vocabulary/genreFormSchemes/nbdbgf    NBD Biblion Genres Fictie       
5536 rbtyp   http://id.loc.gov/vocabulary/genreFormSchemes/rbtyp     Type evidence: a thesaurus for use in rare book and special collections cataloging      
5537 radfg   http://id.loc.gov/vocabulary/genreFormSchemes/radfg     Radio form / genre terms guide  
5538 gnd-carrier     http://id.loc.gov/vocabulary/genreFormSchemes/gnd-carrier       Gemeinsame Normdatei: Datenträgertyp 
5539 gatbeg  http://id.loc.gov/vocabulary/genreFormSchemes/gatbeg    Gattungsbegriffe        "de"=>"Gattungsbegriffe"
5540 rdacontent      http://id.loc.gov/vocabulary/genreFormSchemes/rdacontent        Term and code list for RDA content types        
5541 isbdcontent     http://id.loc.gov/vocabulary/genreFormSchemes/isbdcontent       ISBD Area 0 [content]   
5542 nimafc  http://id.loc.gov/vocabulary/genreFormSchemes/nimafc    NIMA form codes 
5543 amg     http://id.loc.gov/vocabulary/genreFormSchemes/amg       Audiovisual material glossary   
5544 local   http://id.loc.gov/vocabulary/subjectSchemes/local       Locally assigned term   
5545 taika   http://id.loc.gov/vocabulary/subjectSchemes/taika       Taideteollisuuden asiasanasto   "fi"=>"Taideteollisuuden asiasanasto"
5546 nasat   http://id.loc.gov/vocabulary/subjectSchemes/nasat       NASA thesaurus  
5547 rswkaf  http://id.loc.gov/vocabulary/subjectSchemes/rswkaf      Alternativform zum Hauptschlagwort      "de"=>"Alternativform zum Hauptschlagwort"
5548 jhpk    http://id.loc.gov/vocabulary/subjectSchemes/jhpk        JÄ\99zyk haseÅ\82 przedmiotowych KABA   "pl"=>"JÄ\99zyk haseÅ\82 przedmiotowych KABA"
5549 asrcrfcd        http://id.loc.gov/vocabulary/subjectSchemes/asrcrfcd    Australian Standard Research Classification: Research Fields, Courses and Disciplines (RFCD) classification     
5550 bt      http://id.loc.gov/vocabulary/subjectSchemes/bt  Bioethics thesaurus     
5551 lcstt   http://id.loc.gov/vocabulary/subjectSchemes/lcstt       List of Chinese subject terms   
5552 netc    http://id.loc.gov/vocabulary/subjectSchemes/netc        National Emergency Training Center Thesaurus (NETC)     
5553 aat     http://id.loc.gov/vocabulary/subjectSchemes/aat Art & architecture thesaurus    
5554 bet     http://id.loc.gov/vocabulary/subjectSchemes/bet British education thesaurus     
5555 ncjt    http://id.loc.gov/vocabulary/subjectSchemes/ncjt        National criminal justice thesaurus     
5556 samisk  http://id.loc.gov/vocabulary/subjectSchemes/samisk      Sami bibliography       "no"=>"Sámi bibliografia = Samisk bibliografi (Norge)"
5557 tips    http://id.loc.gov/vocabulary/subjectSchemes/tips        Tesauro ISOC de psicología   "es"=>"Tesauro ISOC de psicología"
5558 ukslc   http://id.loc.gov/vocabulary/subjectSchemes/ukslc       UK Standard Library Categories  
5559 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"
5560 umitrist        http://id.loc.gov/vocabulary/subjectSchemes/umitrist    University of Michigan Transportation Research Institute structured thesaurus   
5561 wgst    http://id.loc.gov/vocabulary/subjectSchemes/wgst        Washington GILS Subject Tree    
5562 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"
5563 ntids   http://id.loc.gov/vocabulary/subjectSchemes/ntids       Norske tidsskrifter 1700-1820: emneord  "no"=>"Norske tidsskrifter 1700-1820: emneord"
5564 kaa     http://id.loc.gov/vocabulary/subjectSchemes/kaa Kasvatusalan asiasanasto        "fi"=>"Kasvatusalan asiasanasto"
5565 yso     http://id.loc.gov/vocabulary/subjectSchemes/yso YSO - Yleinen suomalainen ontologia     "fi"=>"YSO - Yleinen suomalainen ontologia"
5566 gcipmedia       http://id.loc.gov/vocabulary/subjectSchemes/gcipmedia   GAMECIP - Computer Game Media Formats (GAMECIP (Game Metadata and Citation Project))    
5567 inspect http://id.loc.gov/vocabulary/subjectSchemes/inspect     INSPEC thesaurus        
5568 ordnok  http://id.loc.gov/vocabulary/subjectSchemes/ordnok      Ordnokkelen: tesaurus for kulturminnevern       "no"=>"Ordnokkelen: tesaurus for kulturminnevern"
5569 helecon http://id.loc.gov/vocabulary/subjectSchemes/helecon     Asiasanasto HELECON-tietikantoihin      "fi"=>"Asiasanasto HELECON-tietikantoihin"
5570 dltlt   http://id.loc.gov/vocabulary/subjectSchemes/dltlt       Cuddon, J. A. A dictionary of literary terms and literary theory        
5571 csapa   http://id.loc.gov/vocabulary/subjectSchemes/csapa       "Controlled vocabulary" in Pollution abstracts  
5572 gtt     http://id.loc.gov/vocabulary/subjectSchemes/gtt GOO-trefwoorden thesaurus       "nl"=>"GOO-trefwoorden thesaurus"
5573 iescs   http://id.loc.gov/vocabulary/subjectSchemes/iescs       International energy subject categories and scope       
5574 itrt    http://id.loc.gov/vocabulary/subjectSchemes/itrt        International Thesaurus of Refugee Terminology  
5575 sanb    http://id.loc.gov/vocabulary/subjectSchemes/sanb        South African national bibliography authority file      
5576 blmlsh  http://id.loc.gov/vocabulary/subjectSchemes/blmlsh      British Library - Map library subject headings  
5577 bhb     http://id.loc.gov/vocabulary/subjectSchemes/bhb Bibliography of the Hebrew Book 
5578 csh     http://id.loc.gov/vocabulary/subjectSchemes/csh Kapsner, Oliver Leonard. Catholic subject headings      
5579 fire    http://id.loc.gov/vocabulary/subjectSchemes/fire        FireTalk, IFSI thesaurus        
5580 jlabsh  http://id.loc.gov/vocabulary/subjectSchemes/jlabsh      Basic subject headings  "ja"=>"Kihon kenmei hyômokuhyô"
5581 udc     http://id.loc.gov/vocabulary/subjectSchemes/udc Universal decimal classification        
5582 lcshac  http://id.loc.gov/vocabulary/subjectSchemes/lcshac      Children's subject headings in Library of Congress subject headings: supplementary vocabularies 
5583 geonet  http://id.loc.gov/vocabulary/subjectSchemes/geonet      NGA GEOnet Names Server (GNS)   
5584 humord  http://id.loc.gov/vocabulary/subjectSchemes/humord      HUMORD  "no"=>"HUMORD"
5585 no-ubo-mr       http://id.loc.gov/vocabulary/subjectSchemes/no-ubo-mr   Menneskerettighets-tesaurus     "no"=>"Menneskerettighets-tesaurus"
5586 sgce    http://id.loc.gov/vocabulary/subjectSchemes/sgce        COBISS.SI General List of subject headings (English subject headings)   "sl"=>"SploÅ¡ni geslovnik COBISS.SI"
5587 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"
5588 thesoz  http://id.loc.gov/vocabulary/subjectSchemes/thesoz      Thesaurus for the Social Sciences       
5589 asth    http://id.loc.gov/vocabulary/subjectSchemes/asth        Astronomy thesaurus     
5590 muzeukc http://id.loc.gov/vocabulary/subjectSchemes/muzeukc     MuzeMusic UK classical music classification     
5591 norbok  http://id.loc.gov/vocabulary/subjectSchemes/norbok      Norbok: emneord i Norsk bokfortegnelse  "no"=>"Norbok: emneord i Norsk bokfortegnelse"
5592 masa    http://id.loc.gov/vocabulary/subjectSchemes/masa        Museoalan asiasanasto   "fi"=>"Museoalan asiasanasto"
5593 conorsi http://id.loc.gov/vocabulary/subjectSchemes/conorsi     CONOR.SI (name authority file) (Maribor, Slovenia: Institut informacijskih znanosti (IZUM))     
5594 eurovocen       http://id.loc.gov/vocabulary/subjectSchemes/eurovocen   Eurovoc thesaurus (English)     
5595 kto     http://id.loc.gov/vocabulary/subjectSchemes/kto KTO - Kielitieteen ontologia    "fi"=>"KTO - Kielitieteen ontologia"
5596 muzvukci        http://id.loc.gov/vocabulary/subjectSchemes/muzvukci    MuzeVideo UK contributor index  
5597 kaunokki        http://id.loc.gov/vocabulary/subjectSchemes/kaunokki    Kaunokki: kaunokirjallisuuden asiasanasto       "fi"=>"Kaunokki: kaunokirjallisuuden asiasanasto"
5598 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"
5599 psychit http://id.loc.gov/vocabulary/subjectSchemes/psychit     Thesaurus of psychological index terms. 
5600 tlsh    http://id.loc.gov/vocabulary/subjectSchemes/tlsh        Subject heading authority list  
5601 csalsct http://id.loc.gov/vocabulary/subjectSchemes/csalsct     CSA life sciences collection thesaurus  
5602 ciesiniv        http://id.loc.gov/vocabulary/subjectSchemes/ciesiniv    CIESIN indexing vocabulary      
5603 ebfem   http://id.loc.gov/vocabulary/subjectSchemes/ebfem       Encabezamientos bilingües de la Fundación Educativa Ana G. Mendez 
5604 mero    http://id.loc.gov/vocabulary/subjectSchemes/mero        MERO - Merenkulkualan ontologia "fi"=>"MERO - Merenkulkualan ontologia"
5605 mmm     http://id.loc.gov/vocabulary/subjectSchemes/mmm "Subject key" in Marxism and the mass media     
5606 pascal  http://id.loc.gov/vocabulary/subjectSchemes/pascal      PASCAL database classification scheme   "fr"=>"Base de donneés PASCAL: plan de classement"
5607 chirosh http://id.loc.gov/vocabulary/subjectSchemes/chirosh     Chiropractic Subject Headings   
5608 cilla   http://id.loc.gov/vocabulary/subjectSchemes/cilla       Cilla: specialtesaurus för musik     "fi"=>"Cilla: specialtesaurus för musik"
5609 aiatsisl        http://id.loc.gov/vocabulary/subjectSchemes/aiatsisl    AIATSIS language thesaurus      
5610 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"
5611 lctgm   http://id.loc.gov/vocabulary/subjectSchemes/lctgm       Thesaurus for graphic materials: TGM I, Subject terms   
5612 muso    http://id.loc.gov/vocabulary/subjectSchemes/muso        MUSO - Ontologi för musik    "fi"=>"MUSO - Ontologi för musik"
5613 blcpss  http://id.loc.gov/vocabulary/subjectSchemes/blcpss      COMPASS subject authority system        
5614 fast    http://id.loc.gov/vocabulary/subjectSchemes/fast        Faceted application of subject terminology      
5615 bisacmt http://id.loc.gov/vocabulary/subjectSchemes/bisacmt     BISAC Merchandising Themes      
5616 lapponica       http://id.loc.gov/vocabulary/subjectSchemes/lapponica   Lapponica       "fi"=>"Lapponica"
5617 juho    http://id.loc.gov/vocabulary/subjectSchemes/juho        JUHO - Julkishallinnon ontologia        "fi"=>"JUHO - Julkishallinnon ontologia"
5618 idas    http://id.loc.gov/vocabulary/subjectSchemes/idas        ID-Archivschlüssel   "de"=>"ID-Archivschlüssel"
5619 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."
5620 test    http://id.loc.gov/vocabulary/subjectSchemes/test        Thesaurus of engineering and scientific terms   
5621 finmesh http://id.loc.gov/vocabulary/subjectSchemes/finmesh     FinMeSH "fi"=>"FinMeSH"
5622 kssbar  http://id.loc.gov/vocabulary/subjectSchemes/kssbar      Klassifikationssystem for svenska bibliotek. Ã\84mnesordregister. Alfabetisk del        "sv"=>"Klassifikationssystem for svenska bibliotek. Ã\84mnesordregister. Alfabetisk del"
5623 kupu    http://id.loc.gov/vocabulary/subjectSchemes/kupu        Maori Wordnet   "mi"=>"He puna kupu"
5624 rpe     http://id.loc.gov/vocabulary/subjectSchemes/rpe Rubricator on economics "ru"=>"Rubrikator po ekonomike"
5625 dit     http://id.loc.gov/vocabulary/subjectSchemes/dit Defense intelligence thesaurus  
5626 she     http://id.loc.gov/vocabulary/subjectSchemes/she SHE: subject headings for engineering   
5627 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"
5628 msc     http://id.loc.gov/vocabulary/subjectSchemes/msc Mathematical subject classification     
5629 muzeukn http://id.loc.gov/vocabulary/subjectSchemes/muzeukn     MuzeMusic UK non-classical music classification 
5630 ipsp    http://id.loc.gov/vocabulary/subjectSchemes/ipsp        Defense intelligence production schedule.       
5631 sthus   http://id.loc.gov/vocabulary/subjectSchemes/sthus       Subject Taxonomy of the History of U.S. Foreign Relations       
5632 poliscit        http://id.loc.gov/vocabulary/subjectSchemes/poliscit    Political science thesaurus II  
5633 qtglit  http://id.loc.gov/vocabulary/subjectSchemes/qtglit      A queer thesaurus : an international thesaurus of gay and lesbian index terms   
5634 unbist  http://id.loc.gov/vocabulary/subjectSchemes/unbist      UNBIS thesaurus 
5635 gcipplatform    http://id.loc.gov/vocabulary/subjectSchemes/gcipplatform        GAMECIP - Computer Game Platforms (GAMECIP (Game Metadata and Citation Project))        
5636 puho    http://id.loc.gov/vocabulary/subjectSchemes/puho        PUHO - Puolustushallinnon ontologia     "fi"=>"PUHO - Puolustushallinnon ontologia"
5637 thub    http://id.loc.gov/vocabulary/subjectSchemes/thub        Thesaurus de la Universitat de Barcelona        "ca"=>"Thesaurus de la Universitat de Barcelona"
5638 ndlsh   http://id.loc.gov/vocabulary/subjectSchemes/ndlsh       National Diet Library list of subject headings  "ja"=>"Koktsu Kokkai Toshokan kenmei hyômokuhyô"
5639 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"
5640 idszbzzh        http://id.loc.gov/vocabulary/subjectSchemes/idszbzzh    Thesaurus IDS Nebis Zentralbibliothek Zürich, Handschriftenabteilung "de"=>"Thesaurus IDS Nebis Zentralbibliothek Zürich, Handschriftenabteilung"
5641 unbisn  http://id.loc.gov/vocabulary/subjectSchemes/unbisn      UNBIS name authority list (New York, NY: Dag Hammarskjld Library, United Nations; : Chadwyck-Healey)    
5642 rswk    http://id.loc.gov/vocabulary/subjectSchemes/rswk        Regeln für den Schlagwortkatalog     "de"=>"Regeln für den Schlagwortkatalog"
5643 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"
5644 biccbmc http://id.loc.gov/vocabulary/subjectSchemes/biccbmc     BIC Children's Books Marketing Classifications  
5645 kulo    http://id.loc.gov/vocabulary/subjectSchemes/kulo        KULO - Kulttuurien tutkimuksen ontologia        "fi"=>"KULO - Kulttuurien tutkimuksen ontologia"
5646 popinte http://id.loc.gov/vocabulary/subjectSchemes/popinte     POPIN thesaurus: population multilingual thesaurus      
5647 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"
5648 atg     http://id.loc.gov/vocabulary/subjectSchemes/atg Agricultural thesaurus and glossary     
5649 eflch   http://id.loc.gov/vocabulary/subjectSchemes/eflch       E4Libraries Category Headings   
5650 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"
5651 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"
5652 csahssa http://id.loc.gov/vocabulary/subjectSchemes/csahssa     "Controlled vocabulary" in Health and safety science abstracts  
5653 sigle   http://id.loc.gov/vocabulary/subjectSchemes/sigle       SIGLE manual, Part 2, Subject category list     
5654 blnpn   http://id.loc.gov/vocabulary/subjectSchemes/blnpn       British Library newspaper place names   
5655 asrctoa http://id.loc.gov/vocabulary/subjectSchemes/asrctoa     Australian Standard Research Classification: Type of Activity (TOA) classification      
5656 lcdgt   http://id.loc.gov/vocabulary/subjectSchemes/lcdgt       Library of Congress demographic group term and code List        
5657 bokbas  http://id.loc.gov/vocabulary/subjectSchemes/bokbas      Bokbasen        "no"=>"Bokbasen"
5658 gnis    http://id.loc.gov/vocabulary/subjectSchemes/gnis        Geographic Names Information System (GNIS)      
5659 nbiemnfag       http://id.loc.gov/vocabulary/subjectSchemes/nbiemnfag   NBIs emneordsliste for faglitteratur    "no"=>"NBIs emneordsliste for faglitteratur"
5660 nlgaf   http://id.loc.gov/vocabulary/subjectSchemes/nlgaf       Archeio KathierÅ\8dmenÅ\8dn EpikephalidÅ\8dn    "el"=>"Archeio KathierÅ\8dmenÅ\8dn EpikephalidÅ\8dn"
5661 bhashe  http://id.loc.gov/vocabulary/subjectSchemes/bhashe      BHA, Bibliography of the history of art, subject headings/English       
5662 tsht    http://id.loc.gov/vocabulary/subjectSchemes/tsht        Thesaurus of subject headings for television    
5663 scbi    http://id.loc.gov/vocabulary/subjectSchemes/scbi        Soggettario per i cataloghi delle biblioteche italiane  "it"=>"Soggettario per i cataloghi delle biblioteche italiane"
5664 valo    http://id.loc.gov/vocabulary/subjectSchemes/valo        VALO - Fotografiska ontologin   "fi"=>"VALO - Fotografiska ontologin"
5665 wpicsh  http://id.loc.gov/vocabulary/subjectSchemes/wpicsh      WPIC Library thesaurus of subject headings      
5666 aktp    http://id.loc.gov/vocabulary/subjectSchemes/aktp        AlphavÄ\93tikos Katalogos ThematikÅ\8dn PerigrapheÅ\8dn "el"=>"AlphavÄ\93tikos Katalogos ThematikÅ\8dn PerigrapheÅ\8dn"
5667 stw     http://id.loc.gov/vocabulary/subjectSchemes/stw STW Thesaurus for Economics     "de"=>"Standard-Thesaurus Wirtschaft"
5668 mesh    http://id.loc.gov/vocabulary/subjectSchemes/mesh        Medical subject headings        
5669 ica     http://id.loc.gov/vocabulary/subjectSchemes/ica Index of Christian art  
5670 emnmus  http://id.loc.gov/vocabulary/subjectSchemes/emnmus      Emneord for musikkdokument i EDB-kataloger      "no"=>"Emneord for musikkdokument i EDB-kataloger"
5671 sao     http://id.loc.gov/vocabulary/subjectSchemes/sao Svenska Ã¤mnesord     "sv"=>"Svenska Ã¤mnesord"
5672 sgc     http://id.loc.gov/vocabulary/subjectSchemes/sgc COBISS.SI General List of subject headings (Slovenian subject headings) "sl"=>"SploÅ¡ni geslovnik COBISS.SI"
5673 bib1814 http://id.loc.gov/vocabulary/subjectSchemes/bib1814     1814-bibliografi: emneord for 1814-bibliografi  "no"=>"1814-bibliografi: emneord for 1814-bibliografi"
5674 bjornson        http://id.loc.gov/vocabulary/subjectSchemes/bjornson    Bjornson: emneord for Bjornsonbibliografien     "no"=>"Bjornson: emneord for Bjornsonbibliografien"
5675 liito   http://id.loc.gov/vocabulary/subjectSchemes/liito       LIITO - Liiketoimintaontologia  "fi"=>"LIITO - Liiketoimintaontologia"
5676 apaist  http://id.loc.gov/vocabulary/subjectSchemes/apaist      APAIS thesaurus: a list of subject terms used in the Australian Public Affairs Information Service      
5677 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)        
5678 ntcsd   http://id.loc.gov/vocabulary/subjectSchemes/ntcsd       "National Translations Center secondary descriptors" in National Translation Center primary subject classification and secondary descriptor     
5679 scisshl http://id.loc.gov/vocabulary/subjectSchemes/scisshl     SCIS subject headings   
5680 opms    http://id.loc.gov/vocabulary/subjectSchemes/opms        Opetusministeriön asiasanasto        "fi"=>"Opetusministeriön asiasanasto"
5681 ttka    http://id.loc.gov/vocabulary/subjectSchemes/ttka        Teologisen tiedekunnan kirjaston asiasanasto    "fi"=>"Teologisen tiedekunnan kirjaston asiasanasto"
5682 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 
5683 ysa     http://id.loc.gov/vocabulary/subjectSchemes/ysa Yleinen suomalainen asiasanasto "fi"=>"Yleinen suomalainen asiasanasto"
5684 kitu    http://id.loc.gov/vocabulary/subjectSchemes/kitu        Kirjallisuudentutkimuksen asiasanasto   "fi"=>"Kirjallisuudentutkimuksen asiasanasto"
5685 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"
5686 aiatsisp        http://id.loc.gov/vocabulary/subjectSchemes/aiatsisp    AIATSIS place thesaurus 
5687 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é"
5688 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"
5689 ated    http://id.loc.gov/vocabulary/subjectSchemes/ated        Australian Thesaurus of Education Descriptors (ATED)    
5690 cabt    http://id.loc.gov/vocabulary/subjectSchemes/cabt        CAB thesaurus (Slough [England]: Commonwealth Agricultural Bureaux)     
5691 kassu   http://id.loc.gov/vocabulary/subjectSchemes/kassu       Kassu - Kasvien suomenkieliset nimet    "fi"=>"Kassu - Kasvien suomenkieliset nimet"
5692 nbdbt   http://id.loc.gov/vocabulary/subjectSchemes/nbdbt       NBD Biblion Trefwoordenthesaurus        "nl"=>"NBD Biblion Trefwoordenthesaurus"
5693 jhpb    http://id.loc.gov/vocabulary/subjectSchemes/jhpb        JÄ\99zyk haseÅ\82 przedmiotowych Biblioteki Narodowej   "pl"=>"JÄ\99zyk haseÅ\82 przedmiotowych Biblioteki Narodowej"
5694 bidex   http://id.loc.gov/vocabulary/subjectSchemes/bidex       Bilindex: a bilingual Spanish-English subject heading list      
5695 ccsa    http://id.loc.gov/vocabulary/subjectSchemes/ccsa        Catalogue collectif suisse des affiches "fr"=>"Catalogue collectif suisse des affiches"
5696 noraf   http://id.loc.gov/vocabulary/subjectSchemes/noraf       Norwegian Authority File        
5697 kito    http://id.loc.gov/vocabulary/subjectSchemes/kito        KITO - Kirjallisuudentutkimuksen ontologia      "fi"=>"KITO - Kirjallisuudentutkimuksen ontologia"
5698 tho     http://id.loc.gov/vocabulary/subjectSchemes/tho Thesauros HellÄ\93nikÅ\8dn Oron "el"=>"Thesauros HellÄ\93nikÅ\8dn Oron"
5699 pmont   http://id.loc.gov/vocabulary/subjectSchemes/pmont       Powerhouse Museum Object Name Thesaurus 
5700 ssg     http://id.loc.gov/vocabulary/subjectSchemes/ssg SploÅ¡ni slovenski geslovnik  "sl"=>"SploÅ¡ni slovenski geslovnik"
5701 huc     http://id.loc.gov/vocabulary/subjectSchemes/huc U.S. Geological Survey water-supply paper 2294: hydrologic basins unit codes    
5702 isis    http://id.loc.gov/vocabulary/subjectSchemes/isis        "Classification scheme" in Isis 
5703 ibsen   http://id.loc.gov/vocabulary/subjectSchemes/ibsen       Ibsen: emneord for Den internasjonale Ibsen-bibliografien       "no"=>"Ibsen: emneord for Den internasjonale Ibsen-bibliografien"
5704 lacnaf  http://id.loc.gov/vocabulary/subjectSchemes/lacnaf      Library and Archives Canada name authority file 
5705 swemesh http://id.loc.gov/vocabulary/subjectSchemes/swemesh     Swedish MeSH    "sv"=>"Svenska MeSH"
5706 hamsun  http://id.loc.gov/vocabulary/subjectSchemes/hamsun      Hamsun: emneord for Hamsunbibliografien "no"=>"Hamsun: emneord for Hamsunbibliografien"
5707 qrma    http://id.loc.gov/vocabulary/subjectSchemes/qrma        List of Arabic subject headings "ar"=>"Qâ'imat ru'ûs al-mawdûât al-'Arabîyah"
5708 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"
5709 ceeus   http://id.loc.gov/vocabulary/subjectSchemes/ceeus       Counties and equivalent entities of the United States its possessions, and associated areas     
5710 taxhs   http://id.loc.gov/vocabulary/subjectSchemes/taxhs       A taxonomy or human services: a conceptual framework with standardized terminology and definitions for the field        
5711 noram   http://id.loc.gov/vocabulary/subjectSchemes/noram       Noram: emneord for Norsk-amerikansk samling     "no"=>"Noram: emneord for Norsk-amerikansk samling"
5712 eurovocfr       http://id.loc.gov/vocabulary/subjectSchemes/eurovocfr   Eurovoc thesaurus (French)      
5713 jurivoc http://id.loc.gov/vocabulary/subjectSchemes/jurivoc     JURIVOC 
5714 agrifors        http://id.loc.gov/vocabulary/subjectSchemes/agrifors    AGRIFOREST-sanasto      "fi"=>"AGRIFOREST-sanasto"
5715 noubojur        http://id.loc.gov/vocabulary/subjectSchemes/noubojur    Thesaurus of Law        "no"=>"Thesaurus of Law"
5716 pha     http://id.loc.gov/vocabulary/subjectSchemes/pha Puolostushallinnon asiasanasto  "fi"=>"Puolostushallinnon asiasanasto"
5717 ddcrit  http://id.loc.gov/vocabulary/subjectSchemes/ddcrit      DDC retrieval and indexing terminology; posting terms with hierarchy and KWOC   
5718 mar     http://id.loc.gov/vocabulary/subjectSchemes/mar Merenkulun asiasanasto  "fi"=>"Merenkulun asiasanasto"
5719 sbt     http://id.loc.gov/vocabulary/subjectSchemes/sbt Soggettario Sistema Bibliotecario Ticinese      "it"=>"Soggettario Sistema Bibliotecario Ticinese"
5720 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))    
5721 kta     http://id.loc.gov/vocabulary/subjectSchemes/kta Kielitieteen asiasanasto        "fi"=>"Kielitieteen asiasanasto"
5722 snt     http://id.loc.gov/vocabulary/subjectSchemes/snt Sexual nomenclature : a thesaurus       
5723 francis http://id.loc.gov/vocabulary/subjectSchemes/francis     FRANCIS database classification scheme  "fr"=>"Base de donneés FRANCIS: plan de classement"
5724 eurovocsl       http://id.loc.gov/vocabulary/subjectSchemes/eurovocsl   Eurovoc thesaurus       "sl"=>"Eurovoc thesaurus"
5725 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"
5726 nlmnaf  http://id.loc.gov/vocabulary/subjectSchemes/nlmnaf      National Library of Medicine name authority file        
5727 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"
5728 sipri   http://id.loc.gov/vocabulary/subjectSchemes/sipri       SIPRI library thesaurus 
5729 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"
5730 tucua   http://id.loc.gov/vocabulary/subjectSchemes/tucua       Thesaurus for use in college and university archives    
5731 pmbok   http://id.loc.gov/vocabulary/subjectSchemes/pmbok       Guide to the project management body of knowledge (PMBOK Guide) 
5732 agrovoc http://id.loc.gov/vocabulary/subjectSchemes/agrovoc     AGROVOC multilingual agricultural thesaurus     
5733 nal     http://id.loc.gov/vocabulary/subjectSchemes/nal National Agricultural Library subject headings  
5734 lnmmbr  http://id.loc.gov/vocabulary/subjectSchemes/lnmmbr      Lietuvos nacionalines Martyno Mazvydo bibliotekos rubrikynas    "lt"=>"Lietuvos nacionalines Martyno Mazvydo bibliotekos rubrikynas"
5735 vmj     http://id.loc.gov/vocabulary/subjectSchemes/vmj Vedettes-matière jeunesse    "fr"=>"Vedettes-matière jeunesse"
5736 ddcut   http://id.loc.gov/vocabulary/subjectSchemes/ddcut       Dewey Decimal Classification user terms 
5737 eks     http://id.loc.gov/vocabulary/subjectSchemes/eks Eduskunnan kirjaston asiasanasto        "fi"=>"Eduskunnan kirjaston asiasanasto"
5738 wot     http://id.loc.gov/vocabulary/subjectSchemes/wot A Women's thesaurus     
5739 noubomn http://id.loc.gov/vocabulary/subjectSchemes/noubomn     University of Oslo Library Thesaurus of Science "no"=>"University of Oslo Library Thesaurus of Science"
5740 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"
5741 precis  http://id.loc.gov/vocabulary/subjectSchemes/precis      PRECIS: a manual of concept analysis and subject indexing       
5742 cstud   http://id.loc.gov/vocabulary/subjectSchemes/cstud       Classificatieschema's Bibliotheek TU Delft      "nl"=>"Classificatieschema's Bibliotheek TU Delft"
5743 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"
5744 pmt     http://id.loc.gov/vocabulary/subjectSchemes/pmt Project management terminology. Newtown Square, PA: Project Management Institute        
5745 ericd   http://id.loc.gov/vocabulary/subjectSchemes/ericd       Thesaurus of ERIC descriptors   
5746 rvm     http://id.loc.gov/vocabulary/subjectSchemes/rvm Répertoire de vedettes-matière    "fr"=>"Répertoire de vedettes-matière"
5747 sfit    http://id.loc.gov/vocabulary/subjectSchemes/sfit        Svenska filminstitutets tesaurus        "sv"=>"Svenska filminstitutets tesaurus"
5748 trtsa   http://id.loc.gov/vocabulary/subjectSchemes/trtsa       Teatterin ja tanssin asiasanasto        "fi"=>"Teatterin ja tanssin asiasanasto"
5749 ulan    http://id.loc.gov/vocabulary/subjectSchemes/ulan        Union list of artist names      
5750 unescot http://id.loc.gov/vocabulary/subjectSchemes/unescot     UNESCO thesaurus        "fr"=>"Thésaurus de l'UNESCO","es"=>"Tesauro de la UNESCO"
5751 koko    http://id.loc.gov/vocabulary/subjectSchemes/koko        KOKO-ontologia  "fi"=>"KOKO-ontologia"
5752 msh     http://id.loc.gov/vocabulary/subjectSchemes/msh Trimboli, T., and Martyn S. Marianist subject headings  
5753 trt     http://id.loc.gov/vocabulary/subjectSchemes/trt Transportation resource thesaurus       
5754 agrovocf        http://id.loc.gov/vocabulary/subjectSchemes/agrovocf    AGROVOC thésaurus agricole multilingue       "fr"=>"AGROVOC thésaurus agricole multilingue"
5755 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"
5756 ddcri   http://id.loc.gov/vocabulary/subjectSchemes/ddcri       Dewey Decimal Classification Relative Index     
5757 est     http://id.loc.gov/vocabulary/subjectSchemes/est International energy: subject thesaurus (: International Energy Agency, Energy Technology Data Exchange)        
5758 lua     http://id.loc.gov/vocabulary/subjectSchemes/lua Liikunnan ja urheilun asiasanasto       "fi"=>"Liikunnan ja urheilun asiasanasto"
5759 mipfesd http://id.loc.gov/vocabulary/subjectSchemes/mipfesd     Macrothesaurus for information processing in the field of economic and social development       
5760 rurkp   http://id.loc.gov/vocabulary/subjectSchemes/rurkp       Predmetnye rubriki Rossiiskoi knizhnoi palaty   "ru"=>"Predmetnye rubriki Rossiiskoi knizhnoi palaty"
5761 albt    http://id.loc.gov/vocabulary/subjectSchemes/albt        Arbetslivsbibliotekets tesaurus "sv"=>"Arbetslivsbibliotekets tesaurus"
5762 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"
5763 bicssc  http://id.loc.gov/vocabulary/subjectSchemes/bicssc      BIC standard subject categories 
5764 cctf    http://id.loc.gov/vocabulary/subjectSchemes/cctf        Carto-Canadiana thésaurus - Français      "fr"=>"Carto-Canadiana thésaurus - Français"
5765 reo     http://id.loc.gov/vocabulary/subjectSchemes/reo Māori Subject Headings thesaurus       "mi"=>"Ngā Åªpoko Tukutuku"
5766 icpsr   http://id.loc.gov/vocabulary/subjectSchemes/icpsr       ICPSR controlled vocabulary system      
5767 kao     http://id.loc.gov/vocabulary/subjectSchemes/kao KVINNSAM Ã¤mnesordsregister   "sv"=>"KVINNSAM Ã¤mnesordsregister"
5768 asrcseo http://id.loc.gov/vocabulary/subjectSchemes/asrcseo     Australian Standard Research Classification: Socio-Economic Objective (SEO) classification      
5769 georeft http://id.loc.gov/vocabulary/subjectSchemes/georeft     GeoRef thesaurus        
5770 cct     http://id.loc.gov/vocabulary/subjectSchemes/cct Chinese Classified Thesaurus    "zh"=>"Zhong guo fen lei zhu ti ci biao"
5771 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"
5772 musa    http://id.loc.gov/vocabulary/subjectSchemes/musa        Musiikin asiasanasto: erikoissanasto    "fi"=>"Musiikin asiasanasto: erikoissanasto"
5773 ntissc  http://id.loc.gov/vocabulary/subjectSchemes/ntissc      NTIS subject categories 
5774 idszbz  http://id.loc.gov/vocabulary/subjectSchemes/idszbz      Thesaurus IDS Nebis Zentralbibliothek Zürich "de"=>"Thesaurus IDS Nebis Zentralbibliothek Zürich"
5775 tlka    http://id.loc.gov/vocabulary/subjectSchemes/tlka        Investigació, Procés Tècnicn kirjaston asiasanasto     "fi"=>"Investigació, Procés Tècnicn kirjaston asiasanasto"
5776 usaidt  http://id.loc.gov/vocabulary/subjectSchemes/usaidt      USAID thesaurus: Keywords used to index documents included in the USAID Development Experience System.  
5777 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"
5778 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"
5779 ntcpsc  http://id.loc.gov/vocabulary/subjectSchemes/ntcpsc      "National Translations Center primary subject classification" in National Translations Center primary subject classification and secondary descriptors  
5780 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"
5781 allars  http://id.loc.gov/vocabulary/subjectSchemes/allars      Allärs: allmän tesaurus pä svenska     "fi"=>"Allärs: allmän tesaurus pä svenska"
5782 ogst    http://id.loc.gov/vocabulary/subjectSchemes/ogst        Oregon GILS Subject Tree (Oregon: Oregon State Library and Oregon Information Resource Management Division (IRMD))      
5783 bella   http://id.loc.gov/vocabulary/subjectSchemes/bella       Bella: specialtesaurus för skönlitteratur "fi"=>"Bella: specialtesaurus för skönlitteratur"
5784 bibalex http://id.loc.gov/vocabulary/subjectSchemes/bibalex     Bibliotheca Alexandrina name and subject authority file 
5785 pepp    http://id.loc.gov/vocabulary/subjectSchemes/pepp        The Princeton encyclopedia of poetry and poetics        
5786 hkcan   http://id.loc.gov/vocabulary/subjectSchemes/hkcan       Hong Kong Chinese Authority File (Name) - HKCAN 
5787 dissao  http://id.loc.gov/vocabulary/subjectSchemes/dissao      "Dissertation abstracts online" in Search tools: the guide to UNI/Data Courier Online   
5788 ltcsh   http://id.loc.gov/vocabulary/subjectSchemes/ltcsh       Land Tenure Center Library list of subject headings     
5789 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"
5790 asft    http://id.loc.gov/vocabulary/subjectSchemes/asft        Aquatic sciences and fisheries thesaurus        
5791 naf     http://id.loc.gov/vocabulary/subjectSchemes/naf NACO authority file     
5792 nimacsc http://id.loc.gov/vocabulary/subjectSchemes/nimacsc     NIMA cartographic subject categories    
5793 khib    http://id.loc.gov/vocabulary/subjectSchemes/khib        Emneord, KHiB Biblioteket       "no"=>"Emneord, KHiB Biblioteket"
5794 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"
5795 afset   http://id.loc.gov/vocabulary/subjectSchemes/afset       American Folklore Society Ethnographic Thesaurus        
5796 erfemn  http://id.loc.gov/vocabulary/subjectSchemes/erfemn      Erfaringskompetanses emneord    "no"=>"Erfaringskompetanses emneord"
5797 sbiao   http://id.loc.gov/vocabulary/subjectSchemes/sbiao       Svenska barnboksinstitutets Ã¤mnesordslista   "sv"=>"Svenska barnboksinstitutets Ã¤mnesordslista"
5798 socio   http://id.loc.gov/vocabulary/subjectSchemes/socio       Sociological Abstracts Thesaurus        
5799 bisacrt http://id.loc.gov/vocabulary/subjectSchemes/bisacrt     BISAC Regional Themes   
5800 eum     http://id.loc.gov/vocabulary/subjectSchemes/eum Eesti uldine märksonastik    "et"=>"Eesti uldine märksonastik"
5801 kula    http://id.loc.gov/vocabulary/subjectSchemes/kula        Kulttuurien tutkimuksen asiasanasto     "fi"=>"Kulttuurien tutkimuksen asiasanasto"
5802 odlt    http://id.loc.gov/vocabulary/subjectSchemes/odlt        Baldick, C. The Oxford dictionary of literary terms     
5803 rerovoc http://id.loc.gov/vocabulary/subjectSchemes/rerovoc     Indexation matiéres RERO autoritès        "fr"=>"Indexation matiéres RERO autoritès"
5804 tsr     http://id.loc.gov/vocabulary/subjectSchemes/tsr TSR-ontologia   "fi"=>"TSR-ontologia"
5805 czmesh  http://id.loc.gov/vocabulary/subjectSchemes/czmesh      Czech MeSH      "cs"=>"Czech MeSH"
5806 dltt    http://id.loc.gov/vocabulary/subjectSchemes/dltt        Quinn, E. A dictionary of literary and thematic terms   
5807 idsbb   http://id.loc.gov/vocabulary/subjectSchemes/idsbb       Thesaurus IDS Basel Bern        "de"=>"Thesaurus IDS Basel Bern"
5808 inist   http://id.loc.gov/vocabulary/subjectSchemes/inist       INIS: thesaurus 
5809 idszbzzk        http://id.loc.gov/vocabulary/subjectSchemes/idszbzzk    Thesaurus IDS Nebis Zentralbibliothek Zürich, Kartensammlung "de"=>"Thesaurus IDS Nebis Zentralbibliothek Zürich, Kartensammlung"
5810 tesa    http://id.loc.gov/vocabulary/subjectSchemes/tesa        Tesauro Agrícola     "es"=>"Tesauro Agrícola"
5811 liv     http://id.loc.gov/vocabulary/subjectSchemes/liv Legislative indexing vocabulary 
5812 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"
5813 nsbncf  http://id.loc.gov/vocabulary/subjectSchemes/nsbncf      Nuovo Soggettario       "it"=>"Nuovo Soggettario"
5814 ipat    http://id.loc.gov/vocabulary/subjectSchemes/ipat        IPA thesaurus and frequency list        
5815 skon    http://id.loc.gov/vocabulary/subjectSchemes/skon        Att indexera skönlitteratur: Ã\84mnesordslista, vuxenlitteratur      "sv"=>"Att indexera skönlitteratur: Ã\84mnesordslista, vuxenlitteratur"
5816 renib   http://id.loc.gov/vocabulary/subjectSchemes/renib       Renib   "es"=>"Renib"
5817 hrvmesh http://id.loc.gov/vocabulary/subjectSchemes/hrvmesh     Croatian MeSH / Hrvatski MeSH   "no"=>"Croatian MeSH / Hrvatski MeSH"
5818 swd     http://id.loc.gov/vocabulary/subjectSchemes/swd Schlagwortnormdatei     "de"=>"Schlagwortnormdatei"
5819 aass    http://id.loc.gov/vocabulary/subjectSchemes/aass        "Asian American Studies Library subject headings" in A Guide for establishing Asian American core collections   
5820 cht     http://id.loc.gov/vocabulary/subjectSchemes/cht Chicano thesaurus for indexing Chicano materials in Chicano periodical index    
5821 galestne        http://id.loc.gov/vocabulary/subjectSchemes/galestne    Gale Group subject thesaurus and named entity vocabulary        
5822 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"
5823 hoidokki        http://id.loc.gov/vocabulary/subjectSchemes/hoidokki    Hoitotieteellinen asiasanasto   
5824 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"
5825 kubikat http://id.loc.gov/vocabulary/subjectSchemes/kubikat     kubikat "de"=>"kubikat"
5826 waqaf   http://id.loc.gov/vocabulary/subjectSchemes/waqaf       Maknas Uloom Al Waqaf   "ar"=>"Maknas Uloom Al Waqaf"
5827 hapi    http://id.loc.gov/vocabulary/subjectSchemes/hapi        HAPI thesaurus and name authority, 1970-2000    
5828 drama   http://id.loc.gov/vocabulary/subjectSchemes/drama       Drama: specialtesaurus för teater och dans   
5829 sosa    http://id.loc.gov/vocabulary/subjectSchemes/sosa        Sociaalialan asiasanasto        "fi"=>"Sociaalialan asiasanasto"
5830 ilpt    http://id.loc.gov/vocabulary/subjectSchemes/ilpt        Index to legal periodicals: thesaurus   
5831 nicem   http://id.loc.gov/vocabulary/subjectSchemes/nicem       NICEM subject headings and classification system        
5832 qlsp    http://id.loc.gov/vocabulary/subjectSchemes/qlsp        Queens Library Spanish language subject headings        
5833 eet     http://id.loc.gov/vocabulary/subjectSchemes/eet European education thesaurus    
5834 nalnaf  http://id.loc.gov/vocabulary/subjectSchemes/nalnaf      National Agricultural Library name authority file       
5835 eclas   http://id.loc.gov/vocabulary/subjectSchemes/eclas       ECLAS thesaurus 
5836 agrovocs        http://id.loc.gov/vocabulary/subjectSchemes/agrovocs    AGROVOC tesauro agrícola multilingée      "es"=>"AGROVOC tesauro agrícola multilingée"
5837 shbe    http://id.loc.gov/vocabulary/subjectSchemes/shbe        Subject headings in business and economics      "sv"=>"Subject headings in business and economics"
5838 barn    http://id.loc.gov/vocabulary/subjectSchemes/barn        Svenska Ã¤mnesord för barn "sv"=>"Svenska Ã¤mnesord för barn"
5839 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"
5840 gccst   http://id.loc.gov/vocabulary/subjectSchemes/gccst       Government of Canada core subject thesaurus (Gatineau : Library and Archives Canada)    
5841 fnhl    http://id.loc.gov/vocabulary/subjectSchemes/fnhl        First Nations House of Learning Subject Headings        
5842 kauno   http://id.loc.gov/vocabulary/subjectSchemes/kauno       KAUNO - Kaunokki-ontologin      "fi"=>"KAUNO - Kaunokki-ontologin"
5843 dtict   http://id.loc.gov/vocabulary/subjectSchemes/dtict       Defense Technical Information Center thesaurus  
5844 mech    http://id.loc.gov/vocabulary/subjectSchemes/mech        Iskanje po zbirki MECH  "sl"=>"Iskanje po zbirki MECH"
5845 jupo    http://id.loc.gov/vocabulary/subjectSchemes/jupo        JUPO - Julkisen hallinnon palveluontologia      "fi"=>"JUPO - Julkisen hallinnon palveluontologia"
5846 ktpt    http://id.loc.gov/vocabulary/subjectSchemes/ktpt        Kirjasto- ja tietopalvelualan tesaurus  "fi"=>"Kirjasto- ja tietopalvelualan tesaurus"
5847 aiatsiss        http://id.loc.gov/vocabulary/subjectSchemes/aiatsiss    AIATSIS subject Thesaurus       
5848 lcac    http://id.loc.gov/vocabulary/subjectSchemes/lcac        Library of Congress Annotated Children's Cataloging Program subject headings    
5849 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à"
5850 lemb    http://id.loc.gov/vocabulary/subjectSchemes/lemb        Lista de encabezamientos de materia para bibliotecas    "es"=>"Lista de encabezamientos de materia para bibliotecas"
5851 henn    http://id.loc.gov/vocabulary/subjectSchemes/henn        Hennepin County Library cumulative authority list       
5852 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"
5853 cash    http://id.loc.gov/vocabulary/subjectSchemes/cash        Canadian subject headings       
5854 nznb    http://id.loc.gov/vocabulary/subjectSchemes/nznb        New Zealand national bibliographic      
5855 prvt    http://id.loc.gov/vocabulary/subjectSchemes/prvt        Patent- och registreringsverkets tesaurus       "sv"=>"Patent- och registreringsverkets tesaurus"
5856 scgdst  http://id.loc.gov/vocabulary/subjectSchemes/scgdst      Subject categorization guide for defense science and technology 
5857 gem     http://id.loc.gov/vocabulary/subjectSchemes/gem GEM controlled vocabularies     
5858 lcsh    http://id.loc.gov/vocabulary/subjectSchemes/lcsh        Library of Congress subject headings    
5859 rero    http://id.loc.gov/vocabulary/subjectSchemes/rero        Indexation matires RERO "fr"=>"Indexation matires RERO"
5860 peri    http://id.loc.gov/vocabulary/subjectSchemes/peri        Perinnetieteiden asiasanasto    "fi"=>"Perinnetieteiden asiasanasto"
5861 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"
5862 slem    http://id.loc.gov/vocabulary/subjectSchemes/slem        Sears: lista de encabezamientos de materia      "es"=>"Sears: lista de encabezamientos de materia"
5863 afo     http://id.loc.gov/vocabulary/subjectSchemes/afo AFO - Viikin kampuskirjaston ontologia  "fi"=>"AFO - Viikin kampuskirjaston ontologia"
5864 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       
5865 hlasstg http://id.loc.gov/vocabulary/subjectSchemes/hlasstg     HLAS subject term glossary      
5866 iest    http://id.loc.gov/vocabulary/subjectSchemes/iest        International energy: subject thesaurus 
5867 pkk     http://id.loc.gov/vocabulary/subjectSchemes/pkk Predmetnik za katoliÅ¡ke knjižnice "sl"=>"Predmetnik za katoliÅ¡ke knjižnice"
5868 atla    http://id.loc.gov/vocabulary/subjectSchemes/atla        Religion indexes: thesaurus     
5869 scot    http://id.loc.gov/vocabulary/subjectSchemes/scot        Schools Online Thesaurus (ScOT) 
5870 smda    http://id.loc.gov/vocabulary/subjectSchemes/smda        Smithsonian National Air and Space Museum Directory of Airplanes        
5871 solstad http://id.loc.gov/vocabulary/subjectSchemes/solstad     Solstad: emneord for Solstadbibliografien       "no"=>"Solstad: emneord for Solstadbibliografien"
5872 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"
5873 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"
5874 ktta    http://id.loc.gov/vocabulary/subjectSchemes/ktta        Käsi - ja taideteollisuuden asiasanasto      "fi"=>"Käsi - ja taideteollisuuden asiasanasto"
5875 ccte    http://id.loc.gov/vocabulary/subjectSchemes/ccte        Carto-Canadiana thesaurus - English     
5876 pmcsg   http://id.loc.gov/vocabulary/subjectSchemes/pmcsg       Combined standards glossary     
5877 bisacsh http://id.loc.gov/vocabulary/subjectSchemes/bisacsh     BISAC Subject Headings  
5878 fssh    http://id.loc.gov/vocabulary/subjectSchemes/fssh        FamilySearch Subject Headings (FamilySearch)    
5879 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"
5880 tero    http://id.loc.gov/vocabulary/subjectSchemes/tero        TERO - Terveyden ja hyvinvoinnin ontologia      "fi"=>"TERO - Terveyden ja hyvinvoinnin ontologia"
5881 rma     http://id.loc.gov/vocabulary/subjectSchemes/rma Ru'us al-mawdu'at al-'Arabiyah  "ar"=>"Ru'us al-mawdu'at al-'Arabiyah"
5882 tgn     http://id.loc.gov/vocabulary/subjectSchemes/tgn Getty thesaurus of geographic names     
5883 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"
5884 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"
5885 sears   http://id.loc.gov/vocabulary/subjectSchemes/sears       Sears list of subject headings  
5886 csht    http://id.loc.gov/vocabulary/subjectSchemes/csht        Chinese subject headings        
5887 \.
5888
5889 -- ' ...blah
5890
5891 INSERT INTO authority.thesaurus (code, uri, name, control_set)
5892   SELECT code, uri, name, 1 FROM thesauri;
5893
5894 UPDATE authority.thesaurus SET short_code = 'a' WHERE code = 'lcsh';
5895 UPDATE authority.thesaurus SET short_code = 'b' WHERE code = 'lcshac';
5896 UPDATE authority.thesaurus SET short_code = 'c' WHERE code = 'mesh';
5897 UPDATE authority.thesaurus SET short_code = 'd' WHERE code = 'nal';
5898 UPDATE authority.thesaurus SET short_code = 'k' WHERE code = 'cash';
5899 UPDATE authority.thesaurus SET short_code = 'r' WHERE code = 'aat';
5900 UPDATE authority.thesaurus SET short_code = 's' WHERE code = 'sears';
5901 UPDATE authority.thesaurus SET short_code = 'v' WHERE code = 'rvm';
5902
5903 UPDATE  authority.thesaurus
5904   SET   short_code = 'z'
5905   WHERE short_code IS NULL
5906         AND control_set = 1;
5907
5908 INSERT INTO config.i18n_core (fq_field, identity_value, translation, string )
5909   SELECT  'at.name', t.code, xlate->key, xlate->value
5910     FROM  thesauri t
5911           JOIN LATERAL each(t.xlate) AS xlate ON TRUE
5912     WHERE NOT EXISTS
5913             (SELECT id
5914               FROM  config.i18n_core
5915               WHERE fq_field = 'at.name'
5916                     AND identity_value = t.code
5917                     AND translation = xlate->key)
5918           AND t.xlate IS NOT NULL
5919           AND t.name <> (xlate->value);
5920
5921 CREATE OR REPLACE FUNCTION authority.extract_thesaurus( marcxml TEXT ) RETURNS TEXT AS $func$
5922 DECLARE
5923     thes_code TEXT;
5924 BEGIN
5925     thes_code := vandelay.marc21_extract_fixed_field(marcxml,'Subj');
5926     IF thes_code IS NULL THEN
5927         thes_code := '|';
5928     ELSIF thes_code = 'z' THEN
5929         thes_code := COALESCE( oils_xpath_string('//*[@tag="040"]/*[@code="f"][1]', marcxml), 'z' );
5930     ELSE
5931         SELECT code INTO thes_code FROM authority.thesaurus WHERE short_code = thes_code;
5932         IF NOT FOUND THEN
5933             thes_code := '|'; -- default
5934         END IF;
5935     END IF;
5936     RETURN thes_code;
5937 END;
5938 $func$ LANGUAGE PLPGSQL STABLE STRICT;
5939
5940 CREATE OR REPLACE FUNCTION authority.map_thesaurus_to_control_set () RETURNS TRIGGER AS $func$
5941 BEGIN
5942     IF NEW.control_set IS NULL THEN
5943         SELECT control_set INTO NEW.control_set
5944         FROM authority.thesaurus
5945         WHERE code = authority.extract_thesaurus(NEW.marc);
5946     END IF;
5947
5948     RETURN NEW;
5949 END;
5950 $func$ LANGUAGE PLPGSQL;
5951
5952 CREATE OR REPLACE FUNCTION authority.reingest_authority_rec_descriptor( auth_id BIGINT ) RETURNS VOID AS $func$
5953 BEGIN
5954     DELETE FROM authority.rec_descriptor WHERE record = auth_id;
5955     INSERT INTO authority.rec_descriptor (record, record_status, encoding_level, thesaurus)
5956         SELECT  auth_id,
5957                 vandelay.marc21_extract_fixed_field(marc,'RecStat'),
5958                 vandelay.marc21_extract_fixed_field(marc,'ELvl'),
5959                 authority.extract_thesaurus(marc)
5960           FROM  authority.record_entry
5961           WHERE id = auth_id;
5962     RETURN;
5963 END;
5964 $func$ LANGUAGE PLPGSQL;
5965
5966
5967
5968 SELECT evergreen.upgrade_deps_block_check('1071', :eg_version); --gmcharlt/kmlussier
5969
5970 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)
5971  RETURNS SETOF metabib.flat_browse_entry_appearance
5972 AS $f$
5973 DECLARE
5974     curs                    REFCURSOR;
5975     rec                     RECORD;
5976     qpfts_query             TEXT;
5977     aqpfts_query            TEXT;
5978     afields                 INT[];
5979     bfields                 INT[];
5980     result_row              metabib.flat_browse_entry_appearance%ROWTYPE;
5981     results_skipped         INT := 0;
5982     row_counter             INT := 0;
5983     row_number              INT;
5984     slice_start             INT;
5985     slice_end               INT;
5986     full_end                INT;
5987     all_records             BIGINT[];
5988     all_brecords             BIGINT[];
5989     all_arecords            BIGINT[];
5990     superpage_of_records    BIGINT[];
5991     superpage_size          INT;
5992     c_tests                 TEXT := '';
5993     b_tests                 TEXT := '';
5994     c_orgs                  INT[];
5995     unauthorized_entry      RECORD;
5996 BEGIN
5997     IF count_up_from_zero THEN
5998         row_number := 0;
5999     ELSE
6000         row_number := -1;
6001     END IF;
6002
6003     IF NOT staff THEN
6004         SELECT x.c_attrs, x.b_attrs INTO c_tests, b_tests FROM asset.patron_default_visibility_mask() x;
6005     END IF;
6006
6007     IF c_tests <> '' THEN c_tests := c_tests || '&'; END IF;
6008     IF b_tests <> '' THEN b_tests := b_tests || '&'; END IF;
6009
6010     SELECT ARRAY_AGG(id) INTO c_orgs FROM actor.org_unit_descendants(context_org);
6011
6012     c_tests := c_tests || search.calculate_visibility_attribute_test('circ_lib',c_orgs)
6013                || '&' || search.calculate_visibility_attribute_test('owning_lib',c_orgs);
6014
6015     PERFORM 1 FROM config.internal_flag WHERE enabled AND name = 'opac.located_uri.act_as_copy';
6016     IF FOUND THEN
6017         b_tests := b_tests || search.calculate_visibility_attribute_test(
6018             'luri_org',
6019             (SELECT ARRAY_AGG(id) FROM actor.org_unit_full_path(context_org) x)
6020         );
6021     ELSE
6022         b_tests := b_tests || search.calculate_visibility_attribute_test(
6023             'luri_org',
6024             (SELECT ARRAY_AGG(id) FROM actor.org_unit_ancestors(context_org) x)
6025         );
6026     END IF;
6027
6028     IF context_locations THEN
6029         IF c_tests <> '' THEN c_tests := c_tests || '&'; END IF;
6030         c_tests := c_tests || search.calculate_visibility_attribute_test('location',context_locations);
6031     END IF;
6032
6033     OPEN curs NO SCROLL FOR EXECUTE query;
6034
6035     LOOP
6036         FETCH curs INTO rec;
6037         IF NOT FOUND THEN
6038             IF result_row.pivot_point IS NOT NULL THEN
6039                 RETURN NEXT result_row;
6040             END IF;
6041             RETURN;
6042         END IF;
6043
6044         --Is unauthorized?
6045         SELECT INTO unauthorized_entry *
6046         FROM metabib.browse_entry_simple_heading_map mbeshm
6047         INNER JOIN authority.simple_heading ash ON ( mbeshm.simple_heading = ash.id )
6048         INNER JOIN authority.control_set_authority_field acsaf ON ( acsaf.id = ash.atag )
6049         JOIN authority.heading_field ahf ON (ahf.id = acsaf.heading_field)
6050         WHERE mbeshm.entry = rec.id
6051         AND   ahf.heading_purpose = 'variant';
6052
6053         -- Gather aggregate data based on the MBE row we're looking at now, authority axis
6054         IF (unauthorized_entry.record IS NOT NULL) THEN
6055             --unauthorized term belongs to an auth linked to a bib?
6056             SELECT INTO all_arecords, result_row.sees, afields
6057                     ARRAY_AGG(DISTINCT abl.bib),
6058                     STRING_AGG(DISTINCT abl.authority::TEXT, $$,$$),
6059                     ARRAY_AGG(DISTINCT map.metabib_field)
6060             FROM authority.bib_linking abl
6061             INNER JOIN authority.control_set_auth_field_metabib_field_map_refs map ON (
6062                     map.authority_field = unauthorized_entry.atag
6063                     AND map.metabib_field = ANY(fields)
6064             )
6065             WHERE abl.authority = unauthorized_entry.record;
6066         ELSE
6067             --do usual procedure
6068             SELECT INTO all_arecords, result_row.sees, afields
6069                     ARRAY_AGG(DISTINCT abl.bib), -- bibs to check for visibility
6070                     STRING_AGG(DISTINCT aal.source::TEXT, $$,$$), -- authority record ids
6071                     ARRAY_AGG(DISTINCT map.metabib_field) -- authority-tag-linked CMF rows
6072
6073             FROM  metabib.browse_entry_simple_heading_map mbeshm
6074                     JOIN authority.simple_heading ash ON ( mbeshm.simple_heading = ash.id )
6075                     JOIN authority.authority_linking aal ON ( ash.record = aal.source )
6076                     JOIN authority.bib_linking abl ON ( aal.target = abl.authority )
6077                     JOIN authority.control_set_auth_field_metabib_field_map_refs map ON (
6078                         ash.atag = map.authority_field
6079                         AND map.metabib_field = ANY(fields)
6080                     )
6081                     JOIN authority.control_set_authority_field acsaf ON (
6082                         map.authority_field = acsaf.id
6083                     )
6084                     JOIN authority.heading_field ahf ON (ahf.id = acsaf.heading_field)
6085               WHERE mbeshm.entry = rec.id
6086               AND   ahf.heading_purpose = 'variant';
6087
6088         END IF;
6089
6090         -- Gather aggregate data based on the MBE row we're looking at now, bib axis
6091         SELECT INTO all_brecords, result_row.authorities, bfields
6092                 ARRAY_AGG(DISTINCT source),
6093                 STRING_AGG(DISTINCT authority::TEXT, $$,$$),
6094                 ARRAY_AGG(DISTINCT def)
6095           FROM  metabib.browse_entry_def_map
6096           WHERE entry = rec.id
6097                 AND def = ANY(fields);
6098
6099         SELECT INTO result_row.fields STRING_AGG(DISTINCT x::TEXT, $$,$$) FROM UNNEST(afields || bfields) x;
6100
6101         result_row.sources := 0;
6102         result_row.asources := 0;
6103
6104         -- Bib-linked vis checking
6105         IF ARRAY_UPPER(all_brecords,1) IS NOT NULL THEN
6106
6107             SELECT  INTO result_row.sources COUNT(DISTINCT b.id)
6108               FROM  biblio.record_entry b
6109                     JOIN asset.copy_vis_attr_cache acvac ON (acvac.record = b.id)
6110               WHERE b.id = ANY(all_brecords[1:browse_superpage_size])
6111                     AND (
6112                         acvac.vis_attr_vector @@ c_tests::query_int
6113                         OR b.vis_attr_vector @@ b_tests::query_int
6114                     );
6115
6116             result_row.accurate := TRUE;
6117
6118         END IF;
6119
6120         -- Authority-linked vis checking
6121         IF ARRAY_UPPER(all_arecords,1) IS NOT NULL THEN
6122
6123             SELECT  INTO result_row.asources COUNT(DISTINCT b.id)
6124               FROM  biblio.record_entry b
6125                     JOIN asset.copy_vis_attr_cache acvac ON (acvac.record = b.id)
6126               WHERE b.id = ANY(all_arecords[1:browse_superpage_size])
6127                     AND (
6128                         acvac.vis_attr_vector @@ c_tests::query_int
6129                         OR b.vis_attr_vector @@ b_tests::query_int
6130                     );
6131
6132             result_row.aaccurate := TRUE;
6133
6134         END IF;
6135
6136         IF result_row.sources > 0 OR result_row.asources > 0 THEN
6137
6138             -- The function that calls this function needs row_number in order
6139             -- to correctly order results from two different runs of this
6140             -- functions.
6141             result_row.row_number := row_number;
6142
6143             -- Now, if row_counter is still less than limit, return a row.  If
6144             -- not, but it is less than next_pivot_pos, continue on without
6145             -- returning actual result rows until we find
6146             -- that next pivot, and return it.
6147
6148             IF row_counter < result_limit THEN
6149                 result_row.browse_entry := rec.id;
6150                 result_row.value := rec.value;
6151
6152                 RETURN NEXT result_row;
6153             ELSE
6154                 result_row.browse_entry := NULL;
6155                 result_row.authorities := NULL;
6156                 result_row.fields := NULL;
6157                 result_row.value := NULL;
6158                 result_row.sources := NULL;
6159                 result_row.sees := NULL;
6160                 result_row.accurate := NULL;
6161                 result_row.aaccurate := NULL;
6162                 result_row.pivot_point := rec.id;
6163
6164                 IF row_counter >= next_pivot_pos THEN
6165                     RETURN NEXT result_row;
6166                     RETURN;
6167                 END IF;
6168             END IF;
6169
6170             IF count_up_from_zero THEN
6171                 row_number := row_number + 1;
6172             ELSE
6173                 row_number := row_number - 1;
6174             END IF;
6175
6176             -- row_counter is different from row_number.
6177             -- It simply counts up from zero so that we know when
6178             -- we've reached our limit.
6179             row_counter := row_counter + 1;
6180         END IF;
6181     END LOOP;
6182 END;
6183 $f$ LANGUAGE plpgsql ROWS 10;
6184
6185 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)
6186  RETURNS SETOF metabib.flat_browse_entry_appearance
6187 AS $f$
6188 DECLARE
6189     core_query              TEXT;
6190     back_query              TEXT;
6191     forward_query           TEXT;
6192     pivot_sort_value        TEXT;
6193     pivot_sort_fallback     TEXT;
6194     context_locations       INT[];
6195     browse_superpage_size   INT;
6196     results_skipped         INT := 0;
6197     back_limit              INT;
6198     back_to_pivot           INT;
6199     forward_limit           INT;
6200     forward_to_pivot        INT;
6201 BEGIN
6202     -- First, find the pivot if we were given a browse term but not a pivot.
6203     IF pivot_id IS NULL THEN
6204         pivot_id := metabib.browse_pivot(search_field, browse_term);
6205     END IF;
6206
6207     SELECT INTO pivot_sort_value, pivot_sort_fallback
6208         sort_value, value FROM metabib.browse_entry WHERE id = pivot_id;
6209
6210     -- Bail if we couldn't find a pivot.
6211     IF pivot_sort_value IS NULL THEN
6212         RETURN;
6213     END IF;
6214
6215     -- Transform the context_loc_group argument (if any) (logc at the
6216     -- TPAC layer) into a form we'll be able to use.
6217     IF context_loc_group IS NOT NULL THEN
6218         SELECT INTO context_locations ARRAY_AGG(location)
6219             FROM asset.copy_location_group_map
6220             WHERE lgroup = context_loc_group;
6221     END IF;
6222
6223     -- Get the configured size of browse superpages.
6224     SELECT INTO browse_superpage_size COALESCE(value::INT,100)     -- NULL ok
6225         FROM config.global_flag
6226         WHERE enabled AND name = 'opac.browse.holdings_visibility_test_limit';
6227
6228     -- First we're going to search backward from the pivot, then we're going
6229     -- to search forward.  In each direction, we need two limits.  At the
6230     -- lesser of the two limits, we delineate the edge of the result set
6231     -- we're going to return.  At the greater of the two limits, we find the
6232     -- pivot value that would represent an offset from the current pivot
6233     -- at a distance of one "page" in either direction, where a "page" is a
6234     -- result set of the size specified in the "result_limit" argument.
6235     --
6236     -- The two limits in each direction make four derived values in total,
6237     -- and we calculate them now.
6238     back_limit := CEIL(result_limit::FLOAT / 2);
6239     back_to_pivot := result_limit;
6240     forward_limit := result_limit / 2;
6241     forward_to_pivot := result_limit - 1;
6242
6243     -- This is the meat of the SQL query that finds browse entries.  We'll
6244     -- pass this to a function which uses it with a cursor, so that individual
6245     -- rows may be fetched in a loop until some condition is satisfied, without
6246     -- waiting for a result set of fixed size to be collected all at once.
6247     core_query := '
6248 SELECT  mbe.id,
6249         mbe.value,
6250         mbe.sort_value
6251   FROM  metabib.browse_entry mbe
6252   WHERE (
6253             EXISTS ( -- are there any bibs using this mbe via the requested fields?
6254                 SELECT  1
6255                   FROM  metabib.browse_entry_def_map mbedm
6256                   WHERE mbedm.entry = mbe.id AND mbedm.def = ANY(' || quote_literal(search_field) || ')
6257             ) OR EXISTS ( -- are there any authorities using this mbe via the requested fields?
6258                 SELECT  1
6259                   FROM  metabib.browse_entry_simple_heading_map mbeshm
6260                         JOIN authority.simple_heading ash ON ( mbeshm.simple_heading = ash.id )
6261                         JOIN authority.control_set_auth_field_metabib_field_map_refs map ON (
6262                             ash.atag = map.authority_field
6263                             AND map.metabib_field = ANY(' || quote_literal(search_field) || ')
6264                         )
6265                         JOIN authority.control_set_authority_field acsaf ON (
6266                             map.authority_field = acsaf.id
6267                         )
6268                         JOIN authority.heading_field ahf ON (ahf.id = acsaf.heading_field)
6269                   WHERE mbeshm.entry = mbe.id
6270                     AND ahf.heading_purpose IN (' || $$'variant'$$ || ')
6271                     -- and authority that variant is coming from is linked to a bib
6272                     AND EXISTS (
6273                         SELECT  1
6274                         FROM  metabib.browse_entry_def_map mbedm2
6275                         WHERE mbedm2.authority = ash.record AND mbedm2.def = ANY(' || quote_literal(search_field) || ')
6276                     )
6277             )
6278         ) AND ';
6279
6280     -- This is the variant of the query for browsing backward.
6281     back_query := core_query ||
6282         ' mbe.sort_value <= ' || quote_literal(pivot_sort_value) ||
6283     ' ORDER BY mbe.sort_value DESC, mbe.value DESC LIMIT 1000';
6284
6285     -- This variant browses forward.
6286     forward_query := core_query ||
6287         ' mbe.sort_value > ' || quote_literal(pivot_sort_value) ||
6288     ' ORDER BY mbe.sort_value, mbe.value LIMIT 1000';
6289
6290     -- We now call the function which applies a cursor to the provided
6291     -- queries, stopping at the appropriate limits and also giving us
6292     -- the next page's pivot.
6293     RETURN QUERY
6294         SELECT * FROM metabib.staged_browse(
6295             back_query, search_field, context_org, context_locations,
6296             staff, browse_superpage_size, TRUE, back_limit, back_to_pivot
6297         ) UNION
6298         SELECT * FROM metabib.staged_browse(
6299             forward_query, search_field, context_org, context_locations,
6300             staff, browse_superpage_size, FALSE, forward_limit, forward_to_pivot
6301         ) ORDER BY row_number DESC;
6302
6303 END;
6304 $f$ LANGUAGE plpgsql ROWS 10;
6305
6306
6307 SELECT evergreen.upgrade_deps_block_check('1072', :eg_version); --gmcharlt/kmlussier
6308
6309 INSERT INTO config.global_flag (name, label, enabled) VALUES (
6310     'opac.show_related_headings_in_browse',
6311     oils_i18n_gettext(
6312         'opac.show_related_headings_in_browse',
6313         'Display related headings (see-also) in browse',
6314         'cgf',
6315         'label'
6316     ),
6317     TRUE
6318 );
6319
6320
6321
6322 SELECT evergreen.upgrade_deps_block_check('1073', :eg_version);
6323
6324 ALTER TABLE config.metabib_field 
6325     ADD COLUMN display_xpath TEXT, 
6326     ADD COLUMN display_field BOOL NOT NULL DEFAULT FALSE;
6327
6328 CREATE TABLE config.display_field_map (
6329     name    TEXT   PRIMARY KEY,
6330     field   INTEGER REFERENCES config.metabib_field (id),
6331     multi   BOOLEAN DEFAULT FALSE
6332 );
6333
6334 CREATE TABLE metabib.display_entry (
6335     id      BIGSERIAL  PRIMARY KEY,
6336     source  BIGINT     NOT NULL REFERENCES biblio.record_entry (id),
6337     field   INT        NOT NULL REFERENCES config.metabib_field (id),
6338     value   TEXT       NOT NULL
6339 );
6340
6341 CREATE INDEX metabib_display_entry_field_idx ON metabib.display_entry (field);
6342 CREATE INDEX metabib_display_entry_source_idx ON metabib.display_entry (source);
6343
6344 -- one row per display entry fleshed with field info
6345 CREATE VIEW metabib.flat_display_entry AS
6346     SELECT
6347         mde.source,
6348         cdfm.name,
6349         cdfm.multi,
6350         cmf.label,
6351         cmf.id AS field,
6352         mde.value
6353     FROM metabib.display_entry mde
6354     JOIN config.metabib_field cmf ON (cmf.id = mde.field)
6355     JOIN config.display_field_map cdfm ON (cdfm.field = mde.field)
6356 ;
6357
6358 -- like flat_display_entry except values are compressed 
6359 -- into one row per display_field_map and JSON-ified.
6360 CREATE VIEW metabib.compressed_display_entry AS
6361     SELECT 
6362         source,
6363         name,
6364         multi,
6365         label,
6366         field,
6367         CASE WHEN multi THEN
6368             TO_JSON(ARRAY_AGG(value))
6369         ELSE
6370             TO_JSON(MIN(value))
6371         END AS value
6372     FROM metabib.flat_display_entry
6373     GROUP BY 1, 2, 3, 4, 5
6374 ;
6375
6376 -- TODO: expand to encompass all well-known fields
6377 CREATE VIEW metabib.wide_display_entry AS
6378     SELECT 
6379         bre.id AS source,
6380         COALESCE(mcde_title.value, 'null') AS title,
6381         COALESCE(mcde_author.value, 'null') AS author,
6382         COALESCE(mcde_subject.value, 'null') AS subject,
6383         COALESCE(mcde_creators.value, 'null') AS creators,
6384         COALESCE(mcde_isbn.value, 'null') AS isbn
6385     -- ensure one row per bre regardless of any display fields
6386     FROM biblio.record_entry bre 
6387     LEFT JOIN metabib.compressed_display_entry mcde_title 
6388         ON (bre.id = mcde_title.source AND mcde_title.name = 'title')
6389     LEFT JOIN metabib.compressed_display_entry mcde_author 
6390         ON (bre.id = mcde_author.source AND mcde_author.name = 'author')
6391     LEFT JOIN metabib.compressed_display_entry mcde_subject 
6392         ON (bre.id = mcde_subject.source AND mcde_subject.name = 'subject')
6393     LEFT JOIN metabib.compressed_display_entry mcde_creators 
6394         ON (bre.id = mcde_creators.source AND mcde_creators.name = 'creators')
6395     LEFT JOIN metabib.compressed_display_entry mcde_isbn 
6396         ON (bre.id = mcde_isbn.source AND mcde_isbn.name = 'isbn')
6397 ;
6398
6399
6400 CREATE OR REPLACE FUNCTION metabib.display_field_normalize_trigger () 
6401     RETURNS TRIGGER AS $$
6402 DECLARE
6403     normalizer  RECORD;
6404     display_field_text  TEXT;
6405 BEGIN
6406     display_field_text := NEW.value;
6407
6408     FOR normalizer IN
6409         SELECT  n.func AS func,
6410                 n.param_count AS param_count,
6411                 m.params AS params
6412           FROM  config.index_normalizer n
6413                 JOIN config.metabib_field_index_norm_map m ON (m.norm = n.id)
6414           WHERE m.field = NEW.field AND m.pos < 0
6415           ORDER BY m.pos LOOP
6416
6417             EXECUTE 'SELECT ' || normalizer.func || '(' ||
6418                 quote_literal( display_field_text ) ||
6419                 CASE
6420                     WHEN normalizer.param_count > 0
6421                         THEN ',' || REPLACE(REPLACE(BTRIM(
6422                             normalizer.params,'[]'),E'\'',E'\\\''),E'"',E'\'')
6423                         ELSE ''
6424                     END ||
6425                 ')' INTO display_field_text;
6426
6427     END LOOP;
6428
6429     NEW.value = display_field_text;
6430
6431     RETURN NEW;
6432 END;
6433 $$ LANGUAGE PLPGSQL;
6434
6435 CREATE TRIGGER display_field_normalize_tgr
6436         BEFORE UPDATE OR INSERT ON metabib.display_entry
6437         FOR EACH ROW EXECUTE PROCEDURE metabib.display_field_normalize_trigger();
6438
6439 CREATE OR REPLACE FUNCTION evergreen.display_field_force_nfc() 
6440     RETURNS TRIGGER AS $$
6441 BEGIN
6442     NEW.value := force_unicode_normal_form(NEW.value,'NFC');
6443     RETURN NEW;
6444 END;
6445 $$ LANGUAGE PLPGSQL;
6446
6447 CREATE TRIGGER display_field_force_nfc_tgr
6448         BEFORE UPDATE OR INSERT ON metabib.display_entry
6449         FOR EACH ROW EXECUTE PROCEDURE evergreen.display_field_force_nfc();
6450
6451 ALTER TYPE metabib.field_entry_template ADD ATTRIBUTE display_field BOOL;
6452
6453 DROP FUNCTION metabib.reingest_metabib_field_entries(BIGINT, BOOL, BOOL, BOOL);
6454 DROP FUNCTION biblio.extract_metabib_field_entry(BIGINT);
6455 DROP FUNCTION biblio.extract_metabib_field_entry(BIGINT, TEXT);
6456
6457 CREATE OR REPLACE FUNCTION biblio.extract_metabib_field_entry (
6458     rid BIGINT,
6459     default_joiner TEXT,
6460     field_types TEXT[],
6461     only_fields INT[]
6462 ) RETURNS SETOF metabib.field_entry_template AS $func$
6463 DECLARE
6464     bib     biblio.record_entry%ROWTYPE;
6465     idx     config.metabib_field%ROWTYPE;
6466     xfrm        config.xml_transform%ROWTYPE;
6467     prev_xfrm   TEXT;
6468     transformed_xml TEXT;
6469     xml_node    TEXT;
6470     xml_node_list   TEXT[];
6471     facet_text  TEXT;
6472     display_text TEXT;
6473     browse_text TEXT;
6474     sort_value  TEXT;
6475     raw_text    TEXT;
6476     curr_text   TEXT;
6477     joiner      TEXT := default_joiner; -- XXX will index defs supply a joiner?
6478     authority_text TEXT;
6479     authority_link BIGINT;
6480     output_row  metabib.field_entry_template%ROWTYPE;
6481     process_idx BOOL;
6482 BEGIN
6483
6484     -- Start out with no field-use bools set
6485     output_row.browse_field = FALSE;
6486     output_row.facet_field = FALSE;
6487     output_row.display_field = FALSE;
6488     output_row.search_field = FALSE;
6489
6490     -- Get the record
6491     SELECT INTO bib * FROM biblio.record_entry WHERE id = rid;
6492
6493     -- Loop over the indexing entries
6494     FOR idx IN SELECT * FROM config.metabib_field WHERE id = ANY (only_fields) ORDER BY format LOOP
6495
6496         process_idx := FALSE;
6497         IF idx.display_field AND 'display' = ANY (field_types) THEN process_idx = TRUE; END IF;
6498         IF idx.browse_field AND 'browse' = ANY (field_types) THEN process_idx = TRUE; END IF;
6499         IF idx.search_field AND 'search' = ANY (field_types) THEN process_idx = TRUE; END IF;
6500         IF idx.facet_field AND 'facet' = ANY (field_types) THEN process_idx = TRUE; END IF;
6501         CONTINUE WHEN process_idx = FALSE;
6502
6503         joiner := COALESCE(idx.joiner, default_joiner);
6504
6505         SELECT INTO xfrm * from config.xml_transform WHERE name = idx.format;
6506
6507         -- See if we can skip the XSLT ... it's expensive
6508         IF prev_xfrm IS NULL OR prev_xfrm <> xfrm.name THEN
6509             -- Can't skip the transform
6510             IF xfrm.xslt <> '---' THEN
6511                 transformed_xml := oils_xslt_process(bib.marc,xfrm.xslt);
6512             ELSE
6513                 transformed_xml := bib.marc;
6514             END IF;
6515
6516             prev_xfrm := xfrm.name;
6517         END IF;
6518
6519         xml_node_list := oils_xpath( idx.xpath, transformed_xml, ARRAY[ARRAY[xfrm.prefix, xfrm.namespace_uri]] );
6520
6521         raw_text := NULL;
6522         FOR xml_node IN SELECT x FROM unnest(xml_node_list) AS x LOOP
6523             CONTINUE WHEN xml_node !~ E'^\\s*<';
6524
6525             -- XXX much of this should be moved into oils_xpath_string...
6526             curr_text := ARRAY_TO_STRING(evergreen.array_remove_item_by_value(evergreen.array_remove_item_by_value(
6527                 oils_xpath( '//text()', -- get the content of all the nodes within the main selected node
6528                     REGEXP_REPLACE( xml_node, E'\\s+', ' ', 'g' ) -- Translate adjacent whitespace to a single space
6529                 ), ' '), ''),  -- throw away morally empty (bankrupt?) strings
6530                 joiner
6531             );
6532
6533             CONTINUE WHEN curr_text IS NULL OR curr_text = '';
6534
6535             IF raw_text IS NOT NULL THEN
6536                 raw_text := raw_text || joiner;
6537             END IF;
6538
6539             raw_text := COALESCE(raw_text,'') || curr_text;
6540
6541             -- autosuggest/metabib.browse_entry
6542             IF idx.browse_field THEN
6543
6544                 IF idx.browse_xpath IS NOT NULL AND idx.browse_xpath <> '' THEN
6545                     browse_text := oils_xpath_string( idx.browse_xpath, xml_node, joiner, ARRAY[ARRAY[xfrm.prefix, xfrm.namespace_uri]] );
6546                 ELSE
6547                     browse_text := curr_text;
6548                 END IF;
6549
6550                 IF idx.browse_sort_xpath IS NOT NULL AND
6551                     idx.browse_sort_xpath <> '' THEN
6552
6553                     sort_value := oils_xpath_string(
6554                         idx.browse_sort_xpath, xml_node, joiner,
6555                         ARRAY[ARRAY[xfrm.prefix, xfrm.namespace_uri]]
6556                     );
6557                 ELSE
6558                     sort_value := browse_text;
6559                 END IF;
6560
6561                 output_row.field_class = idx.field_class;
6562                 output_row.field = idx.id;
6563                 output_row.source = rid;
6564                 output_row.value = BTRIM(REGEXP_REPLACE(browse_text, E'\\s+', ' ', 'g'));
6565                 output_row.sort_value :=
6566                     public.naco_normalize(sort_value);
6567
6568                 output_row.authority := NULL;
6569
6570                 IF idx.authority_xpath IS NOT NULL AND idx.authority_xpath <> '' THEN
6571                     authority_text := oils_xpath_string(
6572                         idx.authority_xpath, xml_node, joiner,
6573                         ARRAY[
6574                             ARRAY[xfrm.prefix, xfrm.namespace_uri],
6575                             ARRAY['xlink','http://www.w3.org/1999/xlink']
6576                         ]
6577                     );
6578
6579                     IF authority_text ~ '^\d+$' THEN
6580                         authority_link := authority_text::BIGINT;
6581                         PERFORM * FROM authority.record_entry WHERE id = authority_link;
6582                         IF FOUND THEN
6583                             output_row.authority := authority_link;
6584                         END IF;
6585                     END IF;
6586
6587                 END IF;
6588
6589                 output_row.browse_field = TRUE;
6590                 -- Returning browse rows with search_field = true for search+browse
6591                 -- configs allows us to retain granularity of being able to search
6592                 -- browse fields with "starts with" type operators (for example, for
6593                 -- titles of songs in music albums)
6594                 IF idx.search_field THEN
6595                     output_row.search_field = TRUE;
6596                 END IF;
6597                 RETURN NEXT output_row;
6598                 output_row.browse_field = FALSE;
6599                 output_row.search_field = FALSE;
6600                 output_row.sort_value := NULL;
6601             END IF;
6602
6603             -- insert raw node text for faceting
6604             IF idx.facet_field THEN
6605
6606                 IF idx.facet_xpath IS NOT NULL AND idx.facet_xpath <> '' THEN
6607                     facet_text := oils_xpath_string( idx.facet_xpath, xml_node, joiner, ARRAY[ARRAY[xfrm.prefix, xfrm.namespace_uri]] );
6608                 ELSE
6609                     facet_text := curr_text;
6610                 END IF;
6611
6612                 output_row.field_class = idx.field_class;
6613                 output_row.field = -1 * idx.id;
6614                 output_row.source = rid;
6615                 output_row.value = BTRIM(REGEXP_REPLACE(facet_text, E'\\s+', ' ', 'g'));
6616
6617                 output_row.facet_field = TRUE;
6618                 RETURN NEXT output_row;
6619                 output_row.facet_field = FALSE;
6620             END IF;
6621
6622             -- insert raw node text for display
6623             IF idx.display_field THEN
6624
6625                 IF idx.display_xpath IS NOT NULL AND idx.display_xpath <> '' THEN
6626                     display_text := oils_xpath_string( idx.display_xpath, xml_node, joiner, ARRAY[ARRAY[xfrm.prefix, xfrm.namespace_uri]] );
6627                 ELSE
6628                     display_text := curr_text;
6629                 END IF;
6630
6631                 output_row.field_class = idx.field_class;
6632                 output_row.field = -1 * idx.id;
6633                 output_row.source = rid;
6634                 output_row.value = BTRIM(REGEXP_REPLACE(display_text, E'\\s+', ' ', 'g'));
6635
6636                 output_row.display_field = TRUE;
6637                 RETURN NEXT output_row;
6638                 output_row.display_field = FALSE;
6639             END IF;
6640
6641         END LOOP;
6642
6643         CONTINUE WHEN raw_text IS NULL OR raw_text = '';
6644
6645         -- insert combined node text for searching
6646         IF idx.search_field THEN
6647             output_row.field_class = idx.field_class;
6648             output_row.field = idx.id;
6649             output_row.source = rid;
6650             output_row.value = BTRIM(REGEXP_REPLACE(raw_text, E'\\s+', ' ', 'g'));
6651
6652             output_row.search_field = TRUE;
6653             RETURN NEXT output_row;
6654             output_row.search_field = FALSE;
6655         END IF;
6656
6657     END LOOP;
6658
6659 END;
6660
6661 $func$ LANGUAGE PLPGSQL;
6662
6663 CREATE OR REPLACE FUNCTION metabib.reingest_metabib_field_entries( 
6664     bib_id BIGINT,
6665     skip_facet BOOL DEFAULT FALSE, 
6666     skip_display BOOL DEFAULT FALSE,
6667     skip_browse BOOL DEFAULT FALSE, 
6668     skip_search BOOL DEFAULT FALSE,
6669     only_fields INT[] DEFAULT '{}'::INT[]
6670 ) RETURNS VOID AS $func$
6671 DECLARE
6672     fclass          RECORD;
6673     ind_data        metabib.field_entry_template%ROWTYPE;
6674     mbe_row         metabib.browse_entry%ROWTYPE;
6675     mbe_id          BIGINT;
6676     b_skip_facet    BOOL;
6677     b_skip_display    BOOL;
6678     b_skip_browse   BOOL;
6679     b_skip_search   BOOL;
6680     value_prepped   TEXT;
6681     field_list      INT[] := only_fields;
6682     field_types     TEXT[] := '{}'::TEXT[];
6683 BEGIN
6684
6685     IF field_list = '{}'::INT[] THEN
6686         SELECT ARRAY_AGG(id) INTO field_list FROM config.metabib_field;
6687     END IF;
6688
6689     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;
6690     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;
6691     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;
6692     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;
6693
6694     IF NOT b_skip_facet THEN field_types := field_types || '{facet}'; END IF;
6695     IF NOT b_skip_display THEN field_types := field_types || '{display}'; END IF;
6696     IF NOT b_skip_browse THEN field_types := field_types || '{browse}'; END IF;
6697     IF NOT b_skip_search THEN field_types := field_types || '{search}'; END IF;
6698
6699     PERFORM * FROM config.internal_flag WHERE name = 'ingest.assume_inserts_only' AND enabled;
6700     IF NOT FOUND THEN
6701         IF NOT b_skip_search THEN
6702             FOR fclass IN SELECT * FROM config.metabib_class LOOP
6703                 -- RAISE NOTICE 'Emptying out %', fclass.name;
6704                 EXECUTE $$DELETE FROM metabib.$$ || fclass.name || $$_field_entry WHERE source = $$ || bib_id;
6705             END LOOP;
6706         END IF;
6707         IF NOT b_skip_facet THEN
6708             DELETE FROM metabib.facet_entry WHERE source = bib_id;
6709         END IF;
6710         IF NOT b_skip_display THEN
6711             DELETE FROM metabib.display_entry WHERE source = bib_id;
6712         END IF;
6713         IF NOT b_skip_browse THEN
6714             DELETE FROM metabib.browse_entry_def_map WHERE source = bib_id;
6715         END IF;
6716     END IF;
6717
6718     FOR ind_data IN SELECT * FROM biblio.extract_metabib_field_entry( bib_id, ' ', field_types, field_list ) LOOP
6719
6720         -- don't store what has been normalized away
6721         CONTINUE WHEN ind_data.value IS NULL;
6722
6723         IF ind_data.field < 0 THEN
6724             ind_data.field = -1 * ind_data.field;
6725         END IF;
6726
6727         IF ind_data.facet_field AND NOT b_skip_facet THEN
6728             INSERT INTO metabib.facet_entry (field, source, value)
6729                 VALUES (ind_data.field, ind_data.source, ind_data.value);
6730         END IF;
6731
6732         IF ind_data.display_field AND NOT b_skip_display THEN
6733             INSERT INTO metabib.display_entry (field, source, value)
6734                 VALUES (ind_data.field, ind_data.source, ind_data.value);
6735         END IF;
6736
6737
6738         IF ind_data.browse_field AND NOT b_skip_browse THEN
6739             -- A caveat about this SELECT: this should take care of replacing
6740             -- old mbe rows when data changes, but not if normalization (by
6741             -- which I mean specifically the output of
6742             -- evergreen.oils_tsearch2()) changes.  It may or may not be
6743             -- expensive to add a comparison of index_vector to index_vector
6744             -- to the WHERE clause below.
6745
6746             CONTINUE WHEN ind_data.sort_value IS NULL;
6747
6748             value_prepped := metabib.browse_normalize(ind_data.value, ind_data.field);
6749             SELECT INTO mbe_row * FROM metabib.browse_entry
6750                 WHERE value = value_prepped AND sort_value = ind_data.sort_value;
6751
6752             IF FOUND THEN
6753                 mbe_id := mbe_row.id;
6754             ELSE
6755                 INSERT INTO metabib.browse_entry
6756                     ( value, sort_value ) VALUES
6757                     ( value_prepped, ind_data.sort_value );
6758
6759                 mbe_id := CURRVAL('metabib.browse_entry_id_seq'::REGCLASS);
6760             END IF;
6761
6762             INSERT INTO metabib.browse_entry_def_map (entry, def, source, authority)
6763                 VALUES (mbe_id, ind_data.field, ind_data.source, ind_data.authority);
6764         END IF;
6765
6766         IF ind_data.search_field AND NOT b_skip_search THEN
6767             -- Avoid inserting duplicate rows
6768             EXECUTE 'SELECT 1 FROM metabib.' || ind_data.field_class ||
6769                 '_field_entry WHERE field = $1 AND source = $2 AND value = $3'
6770                 INTO mbe_id USING ind_data.field, ind_data.source, ind_data.value;
6771                 -- RAISE NOTICE 'Search for an already matching row returned %', mbe_id;
6772             IF mbe_id IS NULL THEN
6773                 EXECUTE $$
6774                 INSERT INTO metabib.$$ || ind_data.field_class || $$_field_entry (field, source, value)
6775                     VALUES ($$ ||
6776                         quote_literal(ind_data.field) || $$, $$ ||
6777                         quote_literal(ind_data.source) || $$, $$ ||
6778                         quote_literal(ind_data.value) ||
6779                     $$);$$;
6780             END IF;
6781         END IF;
6782
6783     END LOOP;
6784
6785     IF NOT b_skip_search THEN
6786         PERFORM metabib.update_combined_index_vectors(bib_id);
6787     END IF;
6788
6789     RETURN;
6790 END;
6791 $func$ LANGUAGE PLPGSQL;
6792
6793 -- AFTER UPDATE OR INSERT trigger for biblio.record_entry
6794 CREATE OR REPLACE FUNCTION biblio.indexing_ingest_or_delete () RETURNS TRIGGER AS $func$
6795 DECLARE
6796     tmp_bool BOOL;
6797 BEGIN
6798
6799     IF NEW.deleted THEN -- If this bib is deleted
6800
6801         PERFORM * FROM config.internal_flag WHERE
6802             name = 'ingest.metarecord_mapping.preserve_on_delete' AND enabled;
6803
6804         tmp_bool := FOUND; -- Just in case this is changed by some other statement
6805
6806         PERFORM metabib.remap_metarecord_for_bib( NEW.id, NEW.fingerprint, TRUE, tmp_bool );
6807
6808         IF NOT tmp_bool THEN
6809             -- One needs to keep these around to support searches
6810             -- with the #deleted modifier, so one should turn on the named
6811             -- internal flag for that functionality.
6812             DELETE FROM metabib.record_attr_vector_list WHERE source = NEW.id;
6813         END IF;
6814
6815         DELETE FROM authority.bib_linking WHERE bib = NEW.id; -- Avoid updating fields in bibs that are no longer visible
6816         DELETE FROM biblio.peer_bib_copy_map WHERE peer_record = NEW.id; -- Separate any multi-homed items
6817         DELETE FROM metabib.browse_entry_def_map WHERE source = NEW.id; -- Don't auto-suggest deleted bibs
6818         RETURN NEW; -- and we're done
6819     END IF;
6820
6821     IF TG_OP = 'UPDATE' THEN -- re-ingest?
6822         PERFORM * FROM config.internal_flag WHERE name = 'ingest.reingest.force_on_same_marc' AND enabled;
6823
6824         IF NOT FOUND AND OLD.marc = NEW.marc THEN -- don't do anything if the MARC didn't change
6825             RETURN NEW;
6826         END IF;
6827     END IF;
6828
6829     -- Record authority linking
6830     PERFORM * FROM config.internal_flag WHERE name = 'ingest.disable_authority_linking' AND enabled;
6831     IF NOT FOUND THEN
6832         PERFORM biblio.map_authority_linking( NEW.id, NEW.marc );
6833     END IF;
6834
6835     -- Flatten and insert the mfr data
6836     PERFORM * FROM config.internal_flag WHERE name = 'ingest.disable_metabib_full_rec' AND enabled;
6837     IF NOT FOUND THEN
6838         PERFORM metabib.reingest_metabib_full_rec(NEW.id);
6839
6840         -- Now we pull out attribute data, which is dependent on the mfr for all but XPath-based fields
6841         PERFORM * FROM config.internal_flag WHERE name = 'ingest.disable_metabib_rec_descriptor' AND enabled;
6842         IF NOT FOUND THEN
6843             PERFORM metabib.reingest_record_attributes(NEW.id, NULL, NEW.marc, TG_OP = 'INSERT' OR OLD.deleted);
6844         END IF;
6845     END IF;
6846
6847     -- Gather and insert the field entry data
6848     PERFORM metabib.reingest_metabib_field_entries(NEW.id);
6849
6850     -- Located URI magic
6851     PERFORM * FROM config.internal_flag WHERE name = 'ingest.disable_located_uri' AND enabled;
6852     IF NOT FOUND THEN PERFORM biblio.extract_located_uris( NEW.id, NEW.marc, NEW.editor ); END IF;
6853
6854     -- (re)map metarecord-bib linking
6855     IF TG_OP = 'INSERT' THEN -- if not deleted and performing an insert, check for the flag
6856         PERFORM * FROM config.internal_flag WHERE name = 'ingest.metarecord_mapping.skip_on_insert' AND enabled;
6857         IF NOT FOUND THEN
6858             PERFORM metabib.remap_metarecord_for_bib( NEW.id, NEW.fingerprint );
6859         END IF;
6860     ELSE -- we're doing an update, and we're not deleted, remap
6861         PERFORM * FROM config.internal_flag WHERE name = 'ingest.metarecord_mapping.skip_on_update' AND enabled;
6862         IF NOT FOUND THEN
6863             PERFORM metabib.remap_metarecord_for_bib( NEW.id, NEW.fingerprint );
6864         END IF;
6865     END IF;
6866
6867     RETURN NEW;
6868 END;
6869 $func$ LANGUAGE PLPGSQL;
6870
6871
6872
6873
6874 SELECT evergreen.upgrade_deps_block_check('1074', :eg_version);
6875
6876 INSERT INTO config.internal_flag (name, enabled) 
6877     VALUES ('ingest.skip_display_indexing', FALSE);
6878
6879 -- Adds seed data to replace (for now) values from the 'mvr' class
6880
6881 UPDATE config.metabib_field SET display_field = TRUE WHERE id IN (6, 8, 16, 18);
6882
6883 INSERT INTO config.metabib_field ( id, field_class, name, label,
6884     format, xpath, display_field, display_xpath ) VALUES
6885     (37, 'author', 'creator', oils_i18n_gettext(37, 'All Creators', 'cmf', 'label'),
6886      'mods32', $$//mods32:mods/mods32:name[mods32:role/mods32:roleTerm[text()='creator']]$$, 
6887      TRUE, $$//*[local-name()='namePart']$$ ); -- /* to fool vim */;
6888
6889 -- 'author' field
6890 UPDATE config.metabib_field SET display_xpath = 
6891     $$//*[local-name()='namePart']$$ -- /* to fool vim */
6892     WHERE id = 8;
6893
6894 INSERT INTO config.display_field_map (name, field, multi) VALUES
6895     ('title', 6, FALSE),
6896     ('author', 8, FALSE),
6897     ('creators', 37, TRUE),
6898     ('subject', 16, TRUE),
6899     ('isbn', 18, TRUE)
6900 ;
6901
6902
6903
6904 SELECT evergreen.upgrade_deps_block_check('1075', :eg_version);
6905
6906 CREATE OR REPLACE FUNCTION evergreen.vandelay_import_item_imported_as_inh_fkey() RETURNS TRIGGER AS $f$
6907 BEGIN   
6908         IF NEW.imported_as IS NULL THEN
6909                 RETURN NEW;
6910         END IF;
6911         PERFORM 1 FROM asset.copy WHERE id = NEW.imported_as;
6912         IF NOT FOUND THEN
6913                 RAISE foreign_key_violation USING MESSAGE = FORMAT(
6914                         $$Referenced asset.copy id not found, imported_as:%s$$, NEW.imported_as
6915                 );
6916         END IF;
6917         RETURN NEW;
6918 END;
6919 $f$ LANGUAGE PLPGSQL VOLATILE COST 50;
6920
6921
6922 COMMIT;
6923
6924 \echo ---------------------------------------------------------------------
6925 \echo Reingest display fields.  This can ban canceled via Ctrl-C and run at
6926 \echo a later time with the following (or similar) SQL:
6927 \echo
6928 \echo 'SELECT metabib.reingest_metabib_field_entries(id, TRUE, FALSE, TRUE, TRUE, '
6929 \echo '    (SELECT ARRAY_AGG(id)::INT[] FROM config.metabib_field WHERE display_field))'
6930 \echo '    FROM biblio.record_entry WHERE NOT deleted AND id > 0;'
6931 \echo
6932
6933 -- REINGEST DISPLAY ENTRIES
6934 SELECT metabib.reingest_metabib_field_entries(id, TRUE, FALSE, TRUE, TRUE, 
6935     (SELECT ARRAY_AGG(id)::INT[] FROM config.metabib_field WHERE display_field))
6936     FROM biblio.record_entry WHERE NOT deleted AND id > 0;
6937
6938