89c938dbbcc6a650a178c60f32e42ef5cd29e396
[working/Evergreen.git] / Open-ILS / src / sql / Pg / upgrade / ZZZZ.schema.unapi-mmr.sql
1 BEGIN;
2
3 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$
4 DECLARE
5     ans RECORD;
6     trans INT;
7 BEGIN
8     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;
9
10     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
11         RETURN QUERY
12         SELECT  ans.depth,
13                 ans.id,
14                 COUNT( av.id ),
15                 SUM( CASE WHEN cp.status IN (0,7,12) THEN 1 ELSE 0 END ),
16                 COUNT( av.id ),
17                 trans
18           FROM  
19                 actor.org_unit_descendants(ans.id) d
20                 JOIN asset.opac_visible_copies av ON (av.circ_lib = d.id)
21                 JOIN asset.copy cp ON (cp.id = av.copy_id)
22                 JOIN metabib.metarecord_source_map m ON (m.metarecord = rid AND m.source = av.record)
23           GROUP BY 1,2,6;
24
25         IF NOT FOUND THEN
26             RETURN QUERY SELECT ans.depth, ans.id, 0::BIGINT, 0::BIGINT, 0::BIGINT, trans;
27         END IF;
28
29     END LOOP;
30
31     RETURN;
32 END;
33 $f$ LANGUAGE PLPGSQL;
34
35 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$
36 DECLARE
37     ans RECORD;
38     trans INT;
39 BEGIN
40     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;
41
42     FOR ans IN SELECT u.org_unit AS id FROM actor.org_lasso_map AS u WHERE lasso = i_lasso LOOP
43         RETURN QUERY
44         SELECT  -1,
45                 ans.id,
46                 COUNT( av.id ),
47                 SUM( CASE WHEN cp.status IN (0,7,12) THEN 1 ELSE 0 END ),
48                 COUNT( av.id ),
49                 trans
50           FROM
51                 actor.org_unit_descendants(ans.id) d
52                 JOIN asset.opac_visible_copies av ON (av.circ_lib = d.id)
53                 JOIN asset.copy cp ON (cp.id = av.copy_id)
54                 JOIN metabib.metarecord_source_map m ON (m.metarecord = rid AND m.source = av.record)
55           GROUP BY 1,2,6;
56
57         IF NOT FOUND THEN
58             RETURN QUERY SELECT ans.depth, ans.id, 0::BIGINT, 0::BIGINT, 0::BIGINT, trans;
59         END IF;
60
61     END LOOP;   
62                 
63     RETURN;     
64 END;            
65 $f$ LANGUAGE PLPGSQL;
66
67 CREATE OR REPLACE FUNCTION asset.staff_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$
68 DECLARE         
69     ans RECORD; 
70     trans INT;
71 BEGIN
72     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;
73
74     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
75         RETURN QUERY
76         SELECT  ans.depth,
77                 ans.id,
78                 COUNT( cp.id ),
79                 SUM( CASE WHEN cp.status IN (0,7,12) THEN 1 ELSE 0 END ),
80                 COUNT( cp.id ),
81                 trans
82           FROM
83                 actor.org_unit_descendants(ans.id) d
84                 JOIN asset.copy cp ON (cp.circ_lib = d.id AND NOT cp.deleted)
85                 JOIN asset.call_number cn ON (cn.id = cp.call_number AND NOT cn.deleted)
86                 JOIN metabib.metarecord_source_map m ON (m.metarecord = rid AND m.source = cn.record)
87           GROUP BY 1,2,6;
88
89         IF NOT FOUND THEN
90             RETURN QUERY SELECT ans.depth, ans.id, 0::BIGINT, 0::BIGINT, 0::BIGINT, trans;
91         END IF;
92
93     END LOOP;
94
95     RETURN;
96 END;
97 $f$ LANGUAGE PLPGSQL;
98
99 CREATE OR REPLACE FUNCTION asset.staff_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$
100 DECLARE
101     ans RECORD;
102     trans INT;
103 BEGIN
104     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;
105
106     FOR ans IN SELECT u.org_unit AS id FROM actor.org_lasso_map AS u WHERE lasso = i_lasso LOOP
107         RETURN QUERY
108         SELECT  -1,
109                 ans.id,
110                 COUNT( cp.id ),
111                 SUM( CASE WHEN cp.status IN (0,7,12) THEN 1 ELSE 0 END ),
112                 COUNT( cp.id ),
113                 trans
114           FROM
115                 actor.org_unit_descendants(ans.id) d
116                 JOIN asset.copy cp ON (cp.circ_lib = d.id AND NOT cp.deleted)
117                 JOIN asset.call_number cn ON (cn.id = cp.call_number AND NOT cn.deleted)
118                 JOIN metabib.metarecord_source_map m ON (m.metarecord = rid AND m.source = cn.record)
119           GROUP BY 1,2,6;
120
121         IF NOT FOUND THEN
122             RETURN QUERY SELECT ans.depth, ans.id, 0::BIGINT, 0::BIGINT, 0::BIGINT, trans;
123         END IF;
124
125     END LOOP;
126
127     RETURN;
128 END;
129 $f$ LANGUAGE PLPGSQL;
130
131 CREATE OR REPLACE FUNCTION unapi.mmr_mra (
132     obj_id BIGINT,
133     format TEXT,
134     ename TEXT,
135     includes TEXT[],
136     org TEXT,
137     depth INT DEFAULT NULL,
138     slimit HSTORE DEFAULT NULL,
139     soffset HSTORE DEFAULT NULL,
140     include_xmlns BOOL DEFAULT TRUE,
141     pref_lib INT DEFAULT NULL
142 ) RETURNS XML AS $F$
143     SELECT  XMLELEMENT(
144         name attributes,
145         XMLATTRIBUTES(
146             CASE WHEN $9 THEN 'http://open-ils.org/spec/indexing/v1' ELSE NULL END AS xmlns,
147             'tag:open-ils.org:U2@mmr/' || $1 AS metarecord
148         ),
149         (SELECT XMLAGG(foo.y)
150           FROM (
151             SELECT  DISTINCT ON (COALESCE(cvm.id,uvm.id))
152                     COALESCE(cvm.id,uvm.id),
153                     XMLELEMENT(
154                         name field,
155                         XMLATTRIBUTES(
156                             mra.attr AS name,
157                             cvm.value AS "coded-value",
158                             cvm.id AS "cvmid",
159                             rad.composite,
160                             rad.multi,
161                             rad.filter,
162                             rad.sorter
163                         ),
164                         mra.value
165                     )
166               FROM  metabib.record_attr_flat mra
167                     JOIN config.record_attr_definition rad ON (mra.attr = rad.name)
168                     LEFT JOIN config.coded_value_map cvm ON (cvm.ctype = mra.attr AND code = mra.value)
169                     LEFT JOIN metabib.uncontrolled_record_attr_value uvm ON (uvm.attr = mra.attr AND uvm.value = mra.value)
170               WHERE mra.id IN (
171                     WITH aou AS (SELECT COALESCE(id, (evergreen.org_top()).id) AS id 
172                         FROM actor.org_unit WHERE shortname = $5 LIMIT 1)
173                     SELECT source 
174                     FROM metabib.metarecord_source_map, aou
175                     WHERE metarecord = $1 AND (
176                         EXISTS (
177                             SELECT 1 FROM asset.opac_visible_copies 
178                             WHERE record = source AND circ_lib IN (
179                                 SELECT id FROM actor.org_unit_descendants(aou.id, $6)) 
180                             LIMIT 1
181                         )
182                         OR EXISTS (SELECT 1 FROM located_uris(source, aou.id, $10) LIMIT 1)
183                     )
184                 )
185               ORDER BY 1
186             )foo(id,y)
187         )
188     )
189 $F$ LANGUAGE SQL STABLE;
190
191 CREATE OR REPLACE FUNCTION evergreen.ranked_volumes(
192     bibid BIGINT[],
193     ouid INT,
194     depth INT DEFAULT NULL,
195     slimit HSTORE DEFAULT NULL,
196     soffset HSTORE DEFAULT NULL,
197     pref_lib INT DEFAULT NULL,
198     includes TEXT[] DEFAULT NULL::TEXT[]
199 ) RETURNS TABLE (id BIGINT, name TEXT, label_sortkey TEXT, rank BIGINT) AS $$
200     SELECT ua.id, ua.name, ua.label_sortkey, MIN(ua.rank) AS rank FROM (
201         SELECT acn.id, aou.name, acn.label_sortkey,
202             evergreen.rank_ou(aou.id, $2, $6), evergreen.rank_cp_status(acp.status),
203             RANK() OVER w
204         FROM asset.call_number acn
205             JOIN asset.copy acp ON (acn.id = acp.call_number)
206             JOIN actor.org_unit_descendants( $2, COALESCE(
207                 $3, (
208                     SELECT depth
209                     FROM actor.org_unit_type aout
210                         INNER JOIN actor.org_unit ou ON ou_type = aout.id
211                     WHERE ou.id = $2
212                 ), $6)
213             ) AS aou ON (acp.circ_lib = aou.id)
214         WHERE acn.record = ANY ($1)
215             AND acn.deleted IS FALSE
216             AND acp.deleted IS FALSE
217             AND CASE WHEN ('exclude_invisible_acn' = ANY($7)) THEN
218                 EXISTS (
219                     SELECT 1
220                     FROM asset.opac_visible_copies
221                     WHERE copy_id = acp.id AND record = acn.record
222                 ) ELSE TRUE END
223         GROUP BY acn.id, acp.status, aou.name, acn.label_sortkey, aou.id
224         WINDOW w AS (
225             ORDER BY evergreen.rank_ou(aou.id, $2, $6), evergreen.rank_cp_status(acp.status)
226         )
227     ) AS ua
228     GROUP BY ua.id, ua.name, ua.label_sortkey
229     ORDER BY rank, ua.name, ua.label_sortkey
230     LIMIT ($4 -> 'acn')::INT
231     OFFSET ($5 -> 'acn')::INT;
232 $$
233 LANGUAGE SQL STABLE;
234
235 CREATE OR REPLACE FUNCTION evergreen.ranked_volumes
236     ( bibid BIGINT, ouid INT, depth INT DEFAULT NULL, slimit HSTORE DEFAULT NULL, soffset HSTORE DEFAULT NULL, pref_lib INT DEFAULT NULL, includes TEXT[] DEFAULT NULL::TEXT[] )
237     RETURNS TABLE (id BIGINT, name TEXT, label_sortkey TEXT, rank BIGINT)
238     AS $$ SELECT * FROM evergreen.ranked_volumes(ARRAY[$1],$2,$3,$4,$5,$6,$7) $$ LANGUAGE SQL STABLE;
239
240
241 CREATE OR REPLACE FUNCTION evergreen.located_uris (
242     bibid BIGINT[],
243     ouid INT,
244     pref_lib INT DEFAULT NULL
245 ) RETURNS TABLE (id BIGINT, name TEXT, label_sortkey TEXT, rank INT) AS $$
246     WITH all_orgs AS (SELECT COALESCE( enabled, FALSE ) AS flag FROM config.global_flag WHERE name = 'opac.located_uri.act_as_copy')
247     SELECT DISTINCT ON (id) * FROM (
248     SELECT acn.id, COALESCE(aou.name,aoud.name), acn.label_sortkey, evergreen.rank_ou(aou.id, $2, $3) AS pref_ou
249       FROM asset.call_number acn
250            INNER JOIN asset.uri_call_number_map auricnm ON acn.id = auricnm.call_number
251            INNER JOIN asset.uri auri ON auri.id = auricnm.uri
252            LEFT JOIN actor.org_unit_ancestors( COALESCE($3, $2) ) aou ON (acn.owning_lib = aou.id)
253            LEFT JOIN actor.org_unit_descendants( COALESCE($3, $2) ) aoud ON (acn.owning_lib = aoud.id),
254            all_orgs
255       WHERE acn.record = ANY ($1)
256           AND acn.deleted IS FALSE
257           AND auri.active IS TRUE
258           AND ((NOT all_orgs.flag AND aou.id IS NOT NULL) OR COALESCE(aou.id,aoud.id) IS NOT NULL)
259     UNION
260     SELECT acn.id, COALESCE(aou.name,aoud.name) AS name, acn.label_sortkey, evergreen.rank_ou(aou.id, $2, $3) AS pref_ou
261       FROM asset.call_number acn
262            INNER JOIN asset.uri_call_number_map auricnm ON acn.id = auricnm.call_number
263            INNER JOIN asset.uri auri ON auri.id = auricnm.uri
264            LEFT JOIN actor.org_unit_ancestors( $2 ) aou ON (acn.owning_lib = aou.id)
265            LEFT JOIN actor.org_unit_descendants( $2 ) aoud ON (acn.owning_lib = aoud.id),
266            all_orgs
267       WHERE acn.record = ANY ($1)
268           AND acn.deleted IS FALSE
269           AND auri.active IS TRUE
270           AND ((NOT all_orgs.flag AND aou.id IS NOT NULL) OR COALESCE(aou.id,aoud.id) IS NOT NULL))x
271     ORDER BY id, pref_ou DESC;
272 $$
273 LANGUAGE SQL STABLE;
274
275 CREATE OR REPLACE FUNCTION evergreen.located_uris ( bibid BIGINT, ouid INT, pref_lib INT DEFAULT NULL)
276     RETURNS TABLE (id BIGINT, name TEXT, label_sortkey TEXT, rank INT)
277     AS $$ SELECT * FROM evergreen.located_uris(ARRAY[$1],$2,$3) $$ LANGUAGE SQL STABLE;
278
279
280 CREATE OR REPLACE FUNCTION unapi.mmr_holdings_xml (
281     mid BIGINT,
282     ouid INT,
283     org TEXT,
284     depth INT DEFAULT NULL,
285     includes TEXT[] DEFAULT NULL::TEXT[],
286     slimit HSTORE DEFAULT NULL,
287     soffset HSTORE DEFAULT NULL,
288     include_xmlns BOOL DEFAULT TRUE,
289     pref_lib INT DEFAULT NULL
290 )
291 RETURNS XML AS $F$
292      SELECT  XMLELEMENT(
293                  name holdings,
294                  XMLATTRIBUTES(
295                     CASE WHEN $8 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
296                     CASE WHEN ('mmr' = ANY ($5)) THEN 'tag:open-ils.org:U2@mmr/' || $1 || '/' || $3 ELSE NULL END AS id,
297                     (SELECT metarecord_has_holdable_copy FROM asset.metarecord_has_holdable_copy($1)) AS has_holdable
298                  ),
299                  XMLELEMENT(
300                      name counts,
301                      (SELECT  XMLAGG(XMLELEMENT::XML) FROM (
302                          SELECT  XMLELEMENT(
303                                      name count,
304                                      XMLATTRIBUTES('public' as type, depth, org_unit, coalesce(transcendant,0) as transcendant, available, visible as count, unshadow)
305                                  )::text
306                            FROM  asset.opac_ou_metarecord_copy_count($2,  $1)
307                                      UNION
308                          SELECT  XMLELEMENT(
309                                      name count,
310                                      XMLATTRIBUTES('staff' as type, depth, org_unit, coalesce(transcendant,0) as transcendant, available, visible as count, unshadow)
311                                  )::text
312                            FROM  asset.staff_ou_metarecord_copy_count($2, $1)
313                                      UNION
314                          SELECT  XMLELEMENT(
315                                      name count,
316                                      XMLATTRIBUTES('pref_lib' as type, depth, org_unit, coalesce(transcendant,0) as transcendant, available, visible as count, unshadow)
317                                  )::text
318                            FROM  asset.opac_ou_metarecord_copy_count($9,  $1)
319                                      ORDER BY 1
320                      )x)
321                  ),
322                  -- XXX monograph_parts and foreign_copies are skipped in MRs ... put them back some day?
323                  XMLELEMENT(
324                      name volumes,
325                      (SELECT XMLAGG(acn ORDER BY rank, name, label_sortkey) FROM (
326                         -- Physical copies
327                         SELECT  unapi.acn(y.id,'xml','volume',evergreen.array_remove_item_by_value( evergreen.array_remove_item_by_value($5,'holdings_xml'),'bre'), $3, $4, $6, $7, FALSE), y.rank, name, label_sortkey
328                         FROM evergreen.ranked_volumes((SELECT ARRAY_AGG(source) FROM metabib.metarecord_source_map WHERE metarecord = $1), $2, $4, $6, $7, $9, $5) AS y
329                         UNION ALL
330                         -- Located URIs
331                         SELECT unapi.acn(uris.id,'xml','volume',evergreen.array_remove_item_by_value( evergreen.array_remove_item_by_value($5,'holdings_xml'),'bre'), $3, $4, $6, $7, FALSE), uris.rank, name, label_sortkey
332                         FROM evergreen.located_uris((SELECT ARRAY_AGG(source) FROM metabib.metarecord_source_map WHERE metarecord = $1), $2, $9) AS uris
333                      )x)
334                  ),
335                  CASE WHEN ('ssub' = ANY ($5)) THEN
336                      XMLELEMENT(
337                          name subscriptions,
338                          (SELECT XMLAGG(ssub) FROM (
339                             SELECT  unapi.ssub(id,'xml','subscription','{}'::TEXT[], $3, $4, $6, $7, FALSE)
340                               FROM  serial.subscription
341                               WHERE record_entry IN (SELECT source FROM metabib.metarecord_source_map WHERE metarecord = $1)
342                         )x)
343                      )
344                  ELSE NULL END
345              );
346 $F$ LANGUAGE SQL STABLE;
347
348 CREATE OR REPLACE FUNCTION unapi.mmr (
349     obj_id BIGINT,
350     format TEXT,
351     ename TEXT,
352     includes TEXT[],
353     org TEXT,
354     depth INT DEFAULT NULL,
355     slimit HSTORE DEFAULT NULL,
356     soffset HSTORE DEFAULT NULL,
357     include_xmlns BOOL DEFAULT TRUE,
358     pref_lib INT DEFAULT NULL
359 )
360 RETURNS XML AS $F$
361 DECLARE
362     mmrec   metabib.metarecord%ROWTYPE;
363     leadrec biblio.record_entry%ROWTYPE;
364     subrec biblio.record_entry%ROWTYPE;
365     layout  unapi.bre_output_layout%ROWTYPE;
366     xfrm    config.xml_transform%ROWTYPE;
367     ouid    INT;
368     xml_buf TEXT; -- growing XML document
369     tmp_xml TEXT; -- single-use XML string
370     xml_frag TEXT; -- single-use XML fragment
371     top_el  TEXT;
372     output  XML;
373     hxml    XML;
374     axml    XML;
375     subxml  XML; -- subordinate records elements
376     sub_xpath TEXT; 
377     parts   TEXT[]; 
378 BEGIN
379
380     -- xpath for extracting bre.marc values from subordinate records 
381     -- so they may be appended to the MARC of the master record prior
382     -- to XSLT processing.
383     -- subjects, isbn, issn, upc -- anything else?
384     sub_xpath := 
385       '//*[starts-with(@tag, "6") or @tag="020" or @tag="022" or @tag="024"]';
386
387     IF org = '-' OR org IS NULL THEN
388         SELECT shortname INTO org FROM evergreen.org_top();
389     END IF;
390
391     SELECT id INTO ouid FROM actor.org_unit WHERE shortname = org;
392
393     IF ouid IS NULL THEN
394         RETURN NULL::XML;
395     END IF;
396
397     SELECT INTO mmrec * FROM metabib.metarecord WHERE id = obj_id;
398     IF NOT FOUND THEN
399         RETURN NULL::XML;
400     END IF;
401
402     -- TODO: aggregate holdings from constituent records
403     IF format = 'holdings_xml' THEN -- the special case
404         output := unapi.mmr_holdings_xml(
405             obj_id, ouid, org, depth,
406             evergreen.array_remove_item_by_value(includes,'holdings_xml'),
407             slimit, soffset, include_xmlns);
408         RETURN output;
409     END IF;
410
411     SELECT * INTO layout FROM unapi.bre_output_layout WHERE name = format;
412
413     IF layout.name IS NULL THEN
414         RETURN NULL::XML;
415     END IF;
416
417     SELECT * INTO xfrm FROM config.xml_transform WHERE name = layout.transform;
418
419     SELECT INTO leadrec * FROM biblio.record_entry WHERE id = mmrec.master_record;
420
421     -- Grab distinct MVF for all records if requested
422     IF ('mra' = ANY (includes)) THEN 
423         axml := unapi.mmr_mra(obj_id,NULL,NULL,NULL,org,depth,NULL,NULL,TRUE,pref_lib);
424     ELSE
425         axml := NULL::XML;
426     END IF;
427
428     xml_buf = leadrec.marc;
429
430     hxml := NULL::XML;
431     IF ('holdings_xml' = ANY (includes)) THEN
432         hxml := unapi.mmr_holdings_xml(
433                     obj_id, ouid, org, depth,
434                     evergreen.array_remove_item_by_value(includes,'holdings_xml'),
435                     slimit, soffset, include_xmlns, pref_lib);
436     END IF;
437
438     subxml := NULL::XML;
439     parts := '{}'::TEXT[];
440     FOR subrec IN SELECT bre.* FROM biblio.record_entry bre
441          JOIN metabib.metarecord_source_map mmsm ON (mmsm.source = bre.id)
442          JOIN metabib.metarecord mmr ON (mmr.id = mmsm.metarecord)
443          WHERE mmr.id = obj_id
444          ORDER BY CASE WHEN bre.id = mmr.master_record THEN 0 ELSE bre.id END
445          LIMIT COALESCE((slimit->'bre')::INT, 5) LOOP
446
447         IF subrec.id = leadrec.id THEN CONTINUE; END IF;
448         -- Append choice data from the the non-lead records to the 
449         -- the lead record document
450
451         parts := parts || xpath(sub_xpath, subrec.marc::XML)::TEXT[];
452     END LOOP;
453
454     SELECT ARRAY_TO_STRING( ARRAY_AGG( DISTINCT p ), '' )::XML INTO subxml FROM UNNEST(parts) p;
455
456     -- append data from the subordinate records to the 
457     -- main record document before applying the XSLT
458
459     IF subxml IS NOT NULL THEN 
460         xml_buf := REGEXP_REPLACE(xml_buf, 
461             '</record>(.*?)$', subxml || '</record>' || E'\\1');
462     END IF;
463
464     IF format = 'marcxml' THEN
465          -- If we're not using the prefixed namespace in 
466          -- this record, then remove all declarations of it
467         IF xml_buf !~ E'<marc:' THEN
468            xml_buf := REGEXP_REPLACE(xml_buf, 
469             ' xmlns:marc="http://www.loc.gov/MARC21/slim"', '', 'g');
470         END IF; 
471     ELSE
472         xml_buf := oils_xslt_process(xml_buf, xfrm.xslt)::XML;
473     END IF;
474
475     -- update top_el to reflect the change in xml_buf, which may
476     -- now be a different type of document (e.g. record -> mods)
477     top_el := REGEXP_REPLACE(xml_buf, E'^.*?<((?:\\S+:)?' || 
478         layout.holdings_element || ').*$', E'\\1');
479
480     IF axml IS NOT NULL THEN 
481         xml_buf := REGEXP_REPLACE(xml_buf, 
482             '</' || top_el || '>(.*?)$', axml || '</' || top_el || E'>\\1');
483     END IF;
484
485     IF hxml IS NOT NULL THEN
486         xml_buf := REGEXP_REPLACE(xml_buf, 
487             '</' || top_el || '>(.*?)$', hxml || '</' || top_el || E'>\\1');
488     END IF;
489
490     IF ('mmr.unapi' = ANY (includes)) THEN 
491         output := REGEXP_REPLACE(
492             xml_buf,
493             '</' || top_el || '>(.*?)',
494             XMLELEMENT(
495                 name abbr,
496                 XMLATTRIBUTES(
497                     'http://www.w3.org/1999/xhtml' AS xmlns,
498                     'unapi-id' AS class,
499                     'tag:open-ils.org:U2@mmr/' || obj_id || '/' || org AS title
500                 )
501             )::TEXT || '</' || top_el || E'>\\1'
502         );
503     ELSE
504         output := xml_buf;
505     END IF;
506
507     -- remove ignorable whitesace
508     output := REGEXP_REPLACE(output::TEXT,E'>\\s+<','><','gs')::XML;
509     RETURN output;
510 END;
511 $F$ LANGUAGE PLPGSQL STABLE;
512
513
514
515 COMMIT;
516