Teach evergreen.located_uris() about the new URI visiblity flag
[working/Evergreen.git] / Open-ILS / src / sql / Pg / 990.schema.unapi.sql
1 DROP SCHEMA IF EXISTS unapi CASCADE;
2
3 BEGIN;
4 CREATE SCHEMA unapi;
5
6 CREATE OR REPLACE FUNCTION evergreen.org_top()
7 RETURNS SETOF actor.org_unit AS $$
8     SELECT * FROM actor.org_unit WHERE parent_ou IS NULL LIMIT 1;
9 $$ LANGUAGE SQL STABLE
10 ROWS 1;
11
12 CREATE OR REPLACE FUNCTION evergreen.array_remove_item_by_value(inp ANYARRAY, el ANYELEMENT)
13 RETURNS anyarray AS $$
14     SELECT ARRAY_AGG(x.e) FROM UNNEST( $1 ) x(e) WHERE x.e <> $2;
15 $$ LANGUAGE SQL STABLE;
16
17 CREATE OR REPLACE FUNCTION evergreen.rank_ou(lib INT, search_lib INT, pref_lib INT DEFAULT NULL)
18 RETURNS INTEGER AS $$
19     SELECT COALESCE(
20
21         -- lib matches search_lib
22         (SELECT CASE WHEN $1 = $2 THEN -20000 END),
23
24         -- lib matches pref_lib
25         (SELECT CASE WHEN $1 = $3 THEN -10000 END),
26
27
28         -- pref_lib is a child of search_lib and lib is a child of pref lib.  
29         -- For example, searching CONS, pref lib is SYS1, 
30         -- copies at BR1 and BR2 sort to the front.
31         (SELECT distance - 5000
32             FROM actor.org_unit_descendants_distance($3) 
33             WHERE id = $1 AND $3 IN (
34                 SELECT id FROM actor.org_unit_descendants($2))),
35
36         -- lib is a child of search_lib
37         (SELECT distance FROM actor.org_unit_descendants_distance($2) WHERE id = $1),
38
39         -- all others pay cash
40         1000
41     );
42 $$ LANGUAGE SQL STABLE;
43
44 CREATE OR REPLACE FUNCTION evergreen.rank_cp_status(status INT)
45 RETURNS INTEGER AS $$
46     WITH totally_available AS (
47         SELECT id, 0 AS avail_rank
48         FROM config.copy_status
49         WHERE opac_visible IS TRUE
50             AND copy_active IS TRUE
51             AND id != 1 -- "Checked out"
52     ), almost_available AS (
53         SELECT id, 10 AS avail_rank
54         FROM config.copy_status
55         WHERE holdable IS TRUE
56             AND opac_visible IS TRUE
57             AND copy_active IS FALSE
58             OR id = 1 -- "Checked out"
59     )
60     SELECT COALESCE(
61         (SELECT avail_rank FROM totally_available WHERE $1 IN (id)),
62         (SELECT avail_rank FROM almost_available WHERE $1 IN (id)),
63         100
64     );
65 $$ LANGUAGE SQL STABLE;
66
67 CREATE OR REPLACE FUNCTION evergreen.ranked_volumes(
68     bibid BIGINT, 
69     ouid INT,
70     depth INT DEFAULT NULL,
71     slimit HSTORE DEFAULT NULL,
72     soffset HSTORE DEFAULT NULL,
73     pref_lib INT DEFAULT NULL,
74     includes TEXT[] DEFAULT NULL::TEXT[]
75 ) RETURNS TABLE (id BIGINT, name TEXT, label_sortkey TEXT, rank BIGINT) AS $$
76     SELECT ua.id, ua.name, ua.label_sortkey, MIN(ua.rank) AS rank FROM (
77         SELECT acn.id, aou.name, acn.label_sortkey,
78             evergreen.rank_ou(aou.id, $2, $6), evergreen.rank_cp_status(acp.status),
79             RANK() OVER w
80         FROM asset.call_number acn
81             JOIN asset.copy acp ON (acn.id = acp.call_number)
82             JOIN actor.org_unit_descendants( $2, COALESCE(
83                 $3, (
84                     SELECT depth
85                     FROM actor.org_unit_type aout
86                         INNER JOIN actor.org_unit ou ON ou_type = aout.id
87                     WHERE ou.id = $2
88                 ), $6)
89             ) AS aou ON (acp.circ_lib = aou.id)
90         WHERE acn.record = $1
91             AND acn.deleted IS FALSE
92             AND acp.deleted IS FALSE
93             AND CASE WHEN ('exclude_invisible_acn' = ANY($7)) THEN 
94                 EXISTS (
95                     SELECT 1 
96                     FROM asset.opac_visible_copies 
97                     WHERE copy_id = acp.id AND record = acn.record
98                 ) ELSE TRUE END
99         GROUP BY acn.id, acp.status, aou.name, acn.label_sortkey, aou.id
100         WINDOW w AS (
101             ORDER BY evergreen.rank_ou(aou.id, $2, $6), evergreen.rank_cp_status(acp.status)
102         )
103     ) AS ua
104     GROUP BY ua.id, ua.name, ua.label_sortkey
105     ORDER BY rank, ua.name, ua.label_sortkey
106     LIMIT ($4 -> 'acn')::INT
107     OFFSET ($5 -> 'acn')::INT;
108 $$
109 LANGUAGE SQL STABLE;
110
111 CREATE OR REPLACE FUNCTION evergreen.located_uris (
112     bibid BIGINT,
113     ouid INT,
114     pref_lib INT DEFAULT NULL
115 ) RETURNS TABLE (id BIGINT, name TEXT, label_sortkey TEXT, rank INT) AS $$
116     WITH all_orgs AS (SELECT COALESCE( enabled, FALSE ) AS flag FROM config.global_flag WHERE name = 'opac.located_uri.act_as_copy')
117     SELECT DISTINCT ON (id) * FROM (
118     SELECT acn.id, COALESCE(aou.name,aoud.name), acn.label_sortkey, evergreen.rank_ou(aou.id, $2, $3) AS pref_ou
119       FROM asset.call_number acn
120            INNER JOIN asset.uri_call_number_map auricnm ON acn.id = auricnm.call_number
121            INNER JOIN asset.uri auri ON auri.id = auricnm.uri
122            LEFT JOIN actor.org_unit_ancestors( COALESCE($3, $2) ) aou ON (acn.owning_lib = aou.id)
123            LEFT JOIN actor.org_unit_descendants( COALESCE($3, $2) ) aoud ON (acn.owning_lib = aoud.id),
124            all_orgs
125       WHERE acn.record = $1
126           AND acn.deleted IS FALSE
127           AND auri.active IS TRUE
128           AND ((NOT all_orgs.flag AND aou.id IS NOT NULL) OR COALESCE(aou.id,aoud.id) IS NOT NULL)
129     UNION
130     SELECT acn.id, COALESCE(aou.name,aoud.name) AS name, acn.label_sortkey, evergreen.rank_ou(aou.id, $2, $3) AS pref_ou
131       FROM asset.call_number acn
132            INNER JOIN asset.uri_call_number_map auricnm ON acn.id = auricnm.call_number
133            INNER JOIN asset.uri auri ON auri.id = auricnm.uri
134            LEFT JOIN actor.org_unit_ancestors( $2 ) aou ON (acn.owning_lib = aou.id)
135            LEFT JOIN actor.org_unit_descendants( $2 ) aoud ON (acn.owning_lib = aoud.id),
136            all_orgs
137       WHERE acn.record = $1
138           AND acn.deleted IS FALSE
139           AND auri.active IS TRUE
140           AND ((NOT all_orgs.flag AND aou.id IS NOT NULL) OR COALESCE(aou.id,aoud.id) IS NOT NULL))x
141     ORDER BY id, pref_ou DESC;
142 $$
143 LANGUAGE SQL STABLE;
144
145 CREATE TABLE unapi.bre_output_layout (
146     name                TEXT    PRIMARY KEY,
147     transform           TEXT    REFERENCES config.xml_transform (name) ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE INITIALLY DEFERRED,
148     mime_type           TEXT    NOT NULL,
149     feed_top            TEXT    NOT NULL,
150     holdings_element    TEXT,
151     title_element       TEXT,
152     description_element TEXT,
153     creator_element     TEXT,
154     update_ts_element   TEXT
155 );
156
157 INSERT INTO unapi.bre_output_layout
158     (name,           transform, mime_type,              holdings_element, feed_top,         title_element, description_element, creator_element, update_ts_element)
159         VALUES
160     ('holdings_xml', NULL,      'application/xml',      NULL,             'hxml',           NULL,          NULL,                NULL,            NULL),
161     ('marcxml',      'marcxml', 'application/marc+xml', 'record',         'collection',     NULL,          NULL,                NULL,            NULL),
162     ('mods32',       'mods32',  'application/mods+xml', 'mods',           'modsCollection', NULL,          NULL,                NULL,            NULL)
163 ;
164
165 -- Dummy functions, so we can create the real ones out of order
166 CREATE OR REPLACE FUNCTION unapi.aou    ( obj_id BIGINT, format TEXT, ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit HSTORE DEFAULT NULL, soffset HSTORE DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$ SELECT NULL::XML $F$ LANGUAGE SQL STABLE;
167 CREATE OR REPLACE FUNCTION unapi.acnp   ( obj_id BIGINT, format TEXT, ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit HSTORE DEFAULT NULL, soffset HSTORE DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$ SELECT NULL::XML $F$ LANGUAGE SQL STABLE;
168 CREATE OR REPLACE FUNCTION unapi.acns   ( obj_id BIGINT, format TEXT, ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit HSTORE DEFAULT NULL, soffset HSTORE DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$ SELECT NULL::XML $F$ LANGUAGE SQL STABLE;
169 CREATE OR REPLACE FUNCTION unapi.acn    ( obj_id BIGINT, format TEXT, ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit HSTORE DEFAULT NULL, soffset HSTORE DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$ SELECT NULL::XML $F$ LANGUAGE SQL STABLE;
170 CREATE OR REPLACE FUNCTION unapi.ssub   ( obj_id BIGINT, format TEXT, ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit HSTORE DEFAULT NULL, soffset HSTORE DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$ SELECT NULL::XML $F$ LANGUAGE SQL STABLE;
171 CREATE OR REPLACE FUNCTION unapi.sdist  ( obj_id BIGINT, format TEXT, ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit HSTORE DEFAULT NULL, soffset HSTORE DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$ SELECT NULL::XML $F$ LANGUAGE SQL STABLE;
172 CREATE OR REPLACE FUNCTION unapi.sstr   ( obj_id BIGINT, format TEXT, ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit HSTORE DEFAULT NULL, soffset HSTORE DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$ SELECT NULL::XML $F$ LANGUAGE SQL STABLE;
173 CREATE OR REPLACE FUNCTION unapi.sitem  ( obj_id BIGINT, format TEXT, ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit HSTORE DEFAULT NULL, soffset HSTORE DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$ SELECT NULL::XML $F$ LANGUAGE SQL STABLE;
174 CREATE OR REPLACE FUNCTION unapi.sunit  ( obj_id BIGINT, format TEXT, ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit HSTORE DEFAULT NULL, soffset HSTORE DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$ SELECT NULL::XML $F$ LANGUAGE SQL STABLE;
175 CREATE OR REPLACE FUNCTION unapi.sisum  ( obj_id BIGINT, format TEXT, ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit HSTORE DEFAULT NULL, soffset HSTORE DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$ SELECT NULL::XML $F$ LANGUAGE SQL STABLE;
176 CREATE OR REPLACE FUNCTION unapi.sbsum  ( obj_id BIGINT, format TEXT, ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit HSTORE DEFAULT NULL, soffset HSTORE DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$ SELECT NULL::XML $F$ LANGUAGE SQL STABLE;
177 CREATE OR REPLACE FUNCTION unapi.sssum  ( obj_id BIGINT, format TEXT, ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit HSTORE DEFAULT NULL, soffset HSTORE DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$ SELECT NULL::XML $F$ LANGUAGE SQL STABLE;
178 CREATE OR REPLACE FUNCTION unapi.siss   ( obj_id BIGINT, format TEXT, ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit HSTORE DEFAULT NULL, soffset HSTORE DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$ SELECT NULL::XML $F$ LANGUAGE SQL STABLE;
179 CREATE OR REPLACE FUNCTION unapi.auri   ( obj_id BIGINT, format TEXT, ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit HSTORE DEFAULT NULL, soffset HSTORE DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$ SELECT NULL::XML $F$ LANGUAGE SQL STABLE;
180 CREATE OR REPLACE FUNCTION unapi.acp    ( obj_id BIGINT, format TEXT, ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit HSTORE DEFAULT NULL, soffset HSTORE DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$ SELECT NULL::XML $F$ LANGUAGE SQL STABLE;
181 CREATE OR REPLACE FUNCTION unapi.acpn   ( obj_id BIGINT, format TEXT, ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit HSTORE DEFAULT NULL, soffset HSTORE DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$ SELECT NULL::XML $F$ LANGUAGE SQL STABLE;
182 CREATE OR REPLACE FUNCTION unapi.acl    ( obj_id BIGINT, format TEXT, ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit HSTORE DEFAULT NULL, soffset HSTORE DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$ SELECT NULL::XML $F$ LANGUAGE SQL STABLE;
183 CREATE OR REPLACE FUNCTION unapi.ccs    ( obj_id BIGINT, format TEXT, ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit HSTORE DEFAULT NULL, soffset HSTORE DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$ SELECT NULL::XML $F$ LANGUAGE SQL STABLE;
184 CREATE OR REPLACE FUNCTION unapi.ascecm ( obj_id BIGINT, format TEXT, ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit HSTORE DEFAULT NULL, soffset HSTORE DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$ SELECT NULL::XML $F$ LANGUAGE SQL STABLE;
185 CREATE OR REPLACE FUNCTION unapi.bre (
186     obj_id BIGINT,
187     format TEXT,
188     ename TEXT,
189     includes TEXT[],
190     org TEXT,
191     depth INT DEFAULT NULL,
192     slimit HSTORE DEFAULT NULL,
193     soffset HSTORE DEFAULT NULL,
194     include_xmlns BOOL DEFAULT TRUE,
195     pref_lib INT DEFAULT NULL
196 )
197 RETURNS XML AS $F$ SELECT NULL::XML $F$ LANGUAGE SQL STABLE;
198 CREATE OR REPLACE FUNCTION unapi.bmp    ( obj_id BIGINT, format TEXT, ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit HSTORE DEFAULT NULL, soffset HSTORE DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$ SELECT NULL::XML $F$ LANGUAGE SQL STABLE;
199 CREATE OR REPLACE FUNCTION unapi.mra    ( obj_id BIGINT, format TEXT, ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit HSTORE DEFAULT NULL, soffset HSTORE DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$ SELECT NULL::XML $F$ LANGUAGE SQL STABLE;
200 CREATE OR REPLACE FUNCTION unapi.circ   ( obj_id BIGINT, format TEXT, ename TEXT, includes TEXT[], org TEXT DEFAULT '-', depth INT DEFAULT NULL, slimit HSTORE DEFAULT NULL, soffset HSTORE DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$ SELECT NULL::XML $F$ LANGUAGE SQL STABLE;
201
202 CREATE OR REPLACE FUNCTION unapi.holdings_xml (
203     bid BIGINT,
204     ouid INT,
205     org TEXT,
206     depth INT DEFAULT NULL,
207     includes TEXT[] DEFAULT NULL::TEXT[],
208     slimit HSTORE DEFAULT NULL,
209     soffset HSTORE DEFAULT NULL,
210     include_xmlns BOOL DEFAULT TRUE,
211     pref_lib INT DEFAULT NULL
212 )
213 RETURNS XML AS $F$ SELECT NULL::XML $F$ LANGUAGE SQL STABLE;
214
215 CREATE OR REPLACE FUNCTION unapi.biblio_record_entry_feed ( id_list BIGINT[], format TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit HSTORE DEFAULT NULL, soffset HSTORE DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE, title TEXT DEFAULT NULL, description TEXT DEFAULT NULL, creator TEXT DEFAULT NULL, update_ts TEXT DEFAULT NULL, unapi_url TEXT DEFAULT NULL, header_xml XML DEFAULT NULL ) RETURNS XML AS $F$ SELECT NULL::XML $F$ LANGUAGE SQL STABLE;
216
217 CREATE OR REPLACE FUNCTION unapi.memoize (classname TEXT, obj_id BIGINT, format TEXT,  ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit HSTORE DEFAULT NULL, soffset HSTORE DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$
218 DECLARE
219     key     TEXT;
220     output  XML;
221 BEGIN
222     key :=
223         'id'        || COALESCE(obj_id::TEXT,'') ||
224         'format'    || COALESCE(format::TEXT,'') ||
225         'ename'     || COALESCE(ename::TEXT,'') ||
226         'includes'  || COALESCE(includes::TEXT,'{}'::TEXT[]::TEXT) ||
227         'org'       || COALESCE(org::TEXT,'') ||
228         'depth'     || COALESCE(depth::TEXT,'') ||
229         'slimit'    || COALESCE(slimit::TEXT,'') ||
230         'soffset'   || COALESCE(soffset::TEXT,'') ||
231         'include_xmlns'   || COALESCE(include_xmlns::TEXT,'');
232     -- RAISE NOTICE 'memoize key: %', key;
233
234     key := MD5(key);
235     -- RAISE NOTICE 'memoize hash: %', key;
236
237     -- XXX cache logic ... memcached? table?
238
239     EXECUTE $$SELECT unapi.$$ || classname || $$( $1, $2, $3, $4, $5, $6, $7, $8, $9);$$ INTO output USING obj_id, format, ename, includes, org, depth, slimit, soffset, include_xmlns;
240     RETURN output;
241 END;
242 $F$ LANGUAGE PLPGSQL STABLE;
243
244 CREATE OR REPLACE FUNCTION unapi.biblio_record_entry_feed ( id_list BIGINT[], format TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit HSTORE DEFAULT NULL, soffset HSTORE DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE, title TEXT DEFAULT NULL, description TEXT DEFAULT NULL, creator TEXT DEFAULT NULL, update_ts TEXT DEFAULT NULL, unapi_url TEXT DEFAULT NULL, header_xml XML DEFAULT NULL ) RETURNS XML AS $F$
245 DECLARE
246     layout          unapi.bre_output_layout%ROWTYPE;
247     transform       config.xml_transform%ROWTYPE;
248     item_format     TEXT;
249     tmp_xml         TEXT;
250     xmlns_uri       TEXT := 'http://open-ils.org/spec/feed-xml/v1';
251     ouid            INT;
252     element_list    TEXT[];
253 BEGIN
254
255     IF org = '-' OR org IS NULL THEN
256         SELECT shortname INTO org FROM evergreen.org_top();
257     END IF;
258
259     SELECT id INTO ouid FROM actor.org_unit WHERE shortname = org;
260     SELECT * INTO layout FROM unapi.bre_output_layout WHERE name = format;
261
262     IF layout.name IS NULL THEN
263         RETURN NULL::XML;
264     END IF;
265
266     SELECT * INTO transform FROM config.xml_transform WHERE name = layout.transform;
267     xmlns_uri := COALESCE(transform.namespace_uri,xmlns_uri);
268
269     -- Gather the bib xml
270     SELECT XMLAGG( unapi.bre(i, format, '', includes, org, depth, slimit, soffset, include_xmlns)) INTO tmp_xml FROM UNNEST( id_list ) i;
271
272     IF layout.title_element IS NOT NULL THEN
273         EXECUTE 'SELECT XMLCONCAT( XMLELEMENT( name '|| layout.title_element ||', XMLATTRIBUTES( $1 AS xmlns), $3), $2)' INTO tmp_xml USING xmlns_uri, tmp_xml::XML, title;
274     END IF;
275
276     IF layout.description_element IS NOT NULL THEN
277         EXECUTE 'SELECT XMLCONCAT( XMLELEMENT( name '|| layout.description_element ||', XMLATTRIBUTES( $1 AS xmlns), $3), $2)' INTO tmp_xml USING xmlns_uri, tmp_xml::XML, description;
278     END IF;
279
280     IF layout.creator_element IS NOT NULL THEN
281         EXECUTE 'SELECT XMLCONCAT( XMLELEMENT( name '|| layout.creator_element ||', XMLATTRIBUTES( $1 AS xmlns), $3), $2)' INTO tmp_xml USING xmlns_uri, tmp_xml::XML, creator;
282     END IF;
283
284     IF layout.update_ts_element IS NOT NULL THEN
285         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;
286     END IF;
287
288     IF unapi_url IS NOT NULL THEN
289         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;
290     END IF;
291
292     IF header_xml IS NOT NULL THEN tmp_xml := XMLCONCAT(header_xml,tmp_xml::XML); END IF;
293
294     element_list := regexp_split_to_array(layout.feed_top,E'\\.');
295     FOR i IN REVERSE ARRAY_UPPER(element_list, 1) .. 1 LOOP
296         EXECUTE 'SELECT XMLELEMENT( name '|| quote_ident(element_list[i]) ||', XMLATTRIBUTES( $1 AS xmlns), $2)' INTO tmp_xml USING xmlns_uri, tmp_xml::XML;
297     END LOOP;
298
299     RETURN tmp_xml::XML;
300 END;
301 $F$ LANGUAGE PLPGSQL STABLE;
302
303 CREATE OR REPLACE FUNCTION unapi.bre (
304     obj_id BIGINT,
305     format TEXT,
306     ename TEXT,
307     includes TEXT[],
308     org TEXT,
309     depth INT DEFAULT NULL,
310     slimit HSTORE DEFAULT NULL,
311     soffset HSTORE DEFAULT NULL,
312     include_xmlns BOOL DEFAULT TRUE,
313     pref_lib INT DEFAULT NULL
314 )
315 RETURNS XML AS $F$
316 DECLARE
317     me      biblio.record_entry%ROWTYPE;
318     layout  unapi.bre_output_layout%ROWTYPE;
319     xfrm    config.xml_transform%ROWTYPE;
320     ouid    INT;
321     tmp_xml TEXT;
322     top_el  TEXT;
323     output  XML;
324     hxml    XML;
325     axml    XML;
326 BEGIN
327
328     IF org = '-' OR org IS NULL THEN
329         SELECT shortname INTO org FROM evergreen.org_top();
330     END IF;
331
332     SELECT id INTO ouid FROM actor.org_unit WHERE shortname = org;
333
334     IF ouid IS NULL THEN
335         RETURN NULL::XML;
336     END IF;
337
338     IF format = 'holdings_xml' THEN -- the special case
339         output := unapi.holdings_xml( obj_id, ouid, org, depth, includes, slimit, soffset, include_xmlns);
340         RETURN output;
341     END IF;
342
343     SELECT * INTO layout FROM unapi.bre_output_layout WHERE name = format;
344
345     IF layout.name IS NULL THEN
346         RETURN NULL::XML;
347     END IF;
348
349     SELECT * INTO xfrm FROM config.xml_transform WHERE name = layout.transform;
350
351     SELECT * INTO me FROM biblio.record_entry WHERE id = obj_id;
352
353     -- grab SVF if we need them
354     IF ('mra' = ANY (includes)) THEN 
355         axml := unapi.mra(obj_id,NULL,NULL,NULL,NULL);
356     ELSE
357         axml := NULL::XML;
358     END IF;
359
360     -- grab holdings if we need them
361     IF ('holdings_xml' = ANY (includes)) THEN 
362         hxml := unapi.holdings_xml(obj_id, ouid, org, depth, evergreen.array_remove_item_by_value(includes,'holdings_xml'), slimit, soffset, include_xmlns, pref_lib);
363     ELSE
364         hxml := NULL::XML;
365     END IF;
366
367
368     -- generate our item node
369
370
371     IF format = 'marcxml' THEN
372         tmp_xml := me.marc;
373         IF tmp_xml !~ E'<marc:' THEN -- If we're not using the prefixed namespace in this record, then remove all declarations of it
374            tmp_xml := REGEXP_REPLACE(tmp_xml, ' xmlns:marc="http://www.loc.gov/MARC21/slim"', '', 'g');
375         END IF; 
376     ELSE
377         tmp_xml := oils_xslt_process(me.marc, xfrm.xslt)::XML;
378     END IF;
379
380     top_el := REGEXP_REPLACE(tmp_xml, E'^.*?<((?:\\S+:)?' || layout.holdings_element || ').*$', E'\\1');
381
382     IF axml IS NOT NULL THEN 
383         tmp_xml := REGEXP_REPLACE(tmp_xml, '</' || top_el || '>(.*?)$', axml || '</' || top_el || E'>\\1');
384     END IF;
385
386     IF hxml IS NOT NULL THEN -- XXX how do we configure the holdings position?
387         tmp_xml := REGEXP_REPLACE(tmp_xml, '</' || top_el || '>(.*?)$', hxml || '</' || top_el || E'>\\1');
388     END IF;
389
390     IF ('bre.unapi' = ANY (includes)) THEN 
391         output := REGEXP_REPLACE(
392             tmp_xml,
393             '</' || top_el || '>(.*?)',
394             XMLELEMENT(
395                 name abbr,
396                 XMLATTRIBUTES(
397                     'http://www.w3.org/1999/xhtml' AS xmlns,
398                     'unapi-id' AS class,
399                     'tag:open-ils.org:U2@bre/' || obj_id || '/' || org AS title
400                 )
401             )::TEXT || '</' || top_el || E'>\\1'
402         );
403     ELSE
404         output := tmp_xml;
405     END IF;
406
407     output := REGEXP_REPLACE(output::TEXT,E'>\\s+<','><','gs')::XML;
408     RETURN output;
409 END;
410 $F$ LANGUAGE PLPGSQL STABLE;
411
412 CREATE OR REPLACE FUNCTION unapi.holdings_xml (
413     bid BIGINT,
414     ouid INT,
415     org TEXT,
416     depth INT DEFAULT NULL,
417     includes TEXT[] DEFAULT NULL::TEXT[],
418     slimit HSTORE DEFAULT NULL,
419     soffset HSTORE DEFAULT NULL,
420     include_xmlns BOOL DEFAULT TRUE,
421     pref_lib INT DEFAULT NULL
422 )
423 RETURNS XML AS $F$
424      SELECT  XMLELEMENT(
425                  name holdings,
426                  XMLATTRIBUTES(
427                     CASE WHEN $8 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
428                     CASE WHEN ('bre' = ANY ($5)) THEN 'tag:open-ils.org:U2@bre/' || $1 || '/' || $3 ELSE NULL END AS id,
429                     (SELECT record_has_holdable_copy FROM asset.record_has_holdable_copy($1)) AS has_holdable
430                  ),
431                  XMLELEMENT(
432                      name counts,
433                      (SELECT  XMLAGG(XMLELEMENT::XML) FROM (
434                          SELECT  XMLELEMENT(
435                                      name count,
436                                      XMLATTRIBUTES('public' as type, depth, org_unit, coalesce(transcendant,0) as transcendant, available, visible as count, unshadow)
437                                  )::text
438                            FROM  asset.opac_ou_record_copy_count($2,  $1)
439                                      UNION
440                          SELECT  XMLELEMENT(
441                                      name count,
442                                      XMLATTRIBUTES('staff' as type, depth, org_unit, coalesce(transcendant,0) as transcendant, available, visible as count, unshadow)
443                                  )::text
444                            FROM  asset.staff_ou_record_copy_count($2, $1)
445                                      UNION
446                          SELECT  XMLELEMENT(
447                                      name count,
448                                      XMLATTRIBUTES('pref_lib' as type, depth, org_unit, coalesce(transcendant,0) as transcendant, available, visible as count, unshadow)
449                                  )::text
450                            FROM  asset.opac_ou_record_copy_count($9,  $1)
451                                      ORDER BY 1
452                      )x)
453                  ),
454                  CASE 
455                      WHEN ('bmp' = ANY ($5)) THEN
456                         XMLELEMENT(
457                             name monograph_parts,
458                             (SELECT XMLAGG(bmp) FROM (
459                                 SELECT  unapi.bmp( id, 'xml', 'monograph_part', evergreen.array_remove_item_by_value( evergreen.array_remove_item_by_value($5,'bre'), 'holdings_xml'), $3, $4, $6, $7, FALSE)
460                                   FROM  biblio.monograph_part
461                                   WHERE record = $1
462                             )x)
463                         )
464                      ELSE NULL
465                  END,
466                  XMLELEMENT(
467                      name volumes,
468                      (SELECT XMLAGG(acn ORDER BY rank, name, label_sortkey) FROM (
469                         -- Physical copies
470                         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
471                         FROM evergreen.ranked_volumes($1, $2, $4, $6, $7, $9, $5) AS y
472                         UNION ALL
473                         -- Located URIs
474                         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
475                         FROM evergreen.located_uris($1, $2, $9) AS uris
476                      )x)
477                  ),
478                  CASE WHEN ('ssub' = ANY ($5)) THEN 
479                      XMLELEMENT(
480                          name subscriptions,
481                          (SELECT XMLAGG(ssub) FROM (
482                             SELECT  unapi.ssub(id,'xml','subscription','{}'::TEXT[], $3, $4, $6, $7, FALSE)
483                               FROM  serial.subscription
484                               WHERE record_entry = $1
485                         )x)
486                      )
487                  ELSE NULL END,
488                  CASE WHEN ('acp' = ANY ($5)) THEN 
489                      XMLELEMENT(
490                          name foreign_copies,
491                          (SELECT XMLAGG(acp) FROM (
492                             SELECT  unapi.acp(p.target_copy,'xml','copy',evergreen.array_remove_item_by_value($5,'acp'), $3, $4, $6, $7, FALSE)
493                               FROM  biblio.peer_bib_copy_map p
494                                     JOIN asset.copy c ON (p.target_copy = c.id)
495                               WHERE NOT c.deleted AND p.peer_record = $1
496                             LIMIT ($6 -> 'acp')::INT
497                             OFFSET ($7 -> 'acp')::INT
498                         )x)
499                      )
500                  ELSE NULL END
501              );
502 $F$ LANGUAGE SQL STABLE;
503
504 CREATE OR REPLACE FUNCTION unapi.ssub ( obj_id BIGINT, format TEXT,  ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit HSTORE DEFAULT NULL, soffset HSTORE DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$
505         SELECT  XMLELEMENT(
506                     name subscription,
507                     XMLATTRIBUTES(
508                         CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
509                         'tag:open-ils.org:U2@ssub/' || id AS id,
510                         'tag:open-ils.org:U2@aou/' || owning_lib AS owning_lib,
511                         start_date AS start, end_date AS end, expected_date_offset
512                     ),
513                     CASE 
514                         WHEN ('sdist' = ANY ($4)) THEN
515                             XMLELEMENT( name distributions,
516                                 (SELECT XMLAGG(sdist) FROM (
517                                     SELECT  unapi.sdist( id, 'xml', 'distribution', evergreen.array_remove_item_by_value($4,'ssub'), $5, $6, $7, $8, FALSE)
518                                       FROM  serial.distribution
519                                       WHERE subscription = ssub.id
520                                 )x)
521                             )
522                         ELSE NULL
523                     END
524                 )
525           FROM  serial.subscription ssub
526           WHERE id = $1
527           GROUP BY id, start_date, end_date, expected_date_offset, owning_lib;
528 $F$ LANGUAGE SQL STABLE;
529
530 CREATE OR REPLACE FUNCTION unapi.sdist ( obj_id BIGINT, format TEXT,  ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit HSTORE DEFAULT NULL, soffset HSTORE DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$
531         SELECT  XMLELEMENT(
532                     name distribution,
533                     XMLATTRIBUTES(
534                         CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
535                         'tag:open-ils.org:U2@sdist/' || id AS id,
536                                 'tag:open-ils.org:U2@acn/' || receive_call_number AS receive_call_number,
537                                     'tag:open-ils.org:U2@acn/' || bind_call_number AS bind_call_number,
538                         unit_label_prefix, label, unit_label_suffix, summary_method
539                     ),
540                     unapi.aou( holding_lib, $2, 'holding_lib', evergreen.array_remove_item_by_value($4,'sdist'), $5, $6, $7, $8),
541                     CASE WHEN subscription IS NOT NULL AND ('ssub' = ANY ($4)) THEN unapi.ssub( subscription, 'xml', 'subscription', evergreen.array_remove_item_by_value($4,'sdist'), $5, $6, $7, $8, FALSE) ELSE NULL END,
542                     CASE 
543                         WHEN ('sstr' = ANY ($4)) THEN
544                             XMLELEMENT( name streams,
545                                 (SELECT XMLAGG(sstr) FROM (
546                                     SELECT  unapi.sstr( id, 'xml', 'stream', evergreen.array_remove_item_by_value($4,'sdist'), $5, $6, $7, $8, FALSE)
547                                       FROM  serial.stream
548                                       WHERE distribution = sdist.id
549                                 )x)
550                             )
551                         ELSE NULL
552                     END,
553                     XMLELEMENT( name summaries,
554                         CASE 
555                             WHEN ('sbsum' = ANY ($4)) THEN
556                                 (SELECT XMLAGG(sbsum) FROM (
557                                     SELECT  unapi.sbsum( id, 'xml', 'serial_summary', evergreen.array_remove_item_by_value($4,'sdist'), $5, $6, $7, $8, FALSE)
558                                       FROM  serial.basic_summary
559                                       WHERE distribution = sdist.id
560                                 )x)
561                             ELSE NULL
562                         END,
563                         CASE 
564                             WHEN ('sisum' = ANY ($4)) THEN
565                                 (SELECT XMLAGG(sisum) FROM (
566                                     SELECT  unapi.sisum( id, 'xml', 'serial_summary', evergreen.array_remove_item_by_value($4,'sdist'), $5, $6, $7, $8, FALSE)
567                                       FROM  serial.index_summary
568                                       WHERE distribution = sdist.id
569                                 )x)
570                             ELSE NULL
571                         END,
572                         CASE 
573                             WHEN ('sssum' = ANY ($4)) THEN
574                                 (SELECT XMLAGG(sssum) FROM (
575                                     SELECT  unapi.sssum( id, 'xml', 'serial_summary', evergreen.array_remove_item_by_value($4,'sdist'), $5, $6, $7, $8, FALSE)
576                                       FROM  serial.supplement_summary
577                                       WHERE distribution = sdist.id
578                                 )x)
579                             ELSE NULL
580                         END
581                     )
582                 )
583           FROM  serial.distribution sdist
584           WHERE id = $1
585           GROUP BY id, label, unit_label_prefix, unit_label_suffix, holding_lib, summary_method, subscription, receive_call_number, bind_call_number;
586 $F$ LANGUAGE SQL STABLE;
587
588 CREATE OR REPLACE FUNCTION unapi.sstr ( obj_id BIGINT, format TEXT,  ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit HSTORE DEFAULT NULL, soffset HSTORE DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$
589     SELECT  XMLELEMENT(
590                 name stream,
591                 XMLATTRIBUTES(
592                     CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
593                     'tag:open-ils.org:U2@sstr/' || id AS id,
594                     routing_label
595                 ),
596                 CASE WHEN distribution IS NOT NULL AND ('sdist' = ANY ($4)) THEN unapi.sssum( distribution, 'xml', 'distribtion', evergreen.array_remove_item_by_value($4,'sstr'), $5, $6, $7, $8, FALSE) ELSE NULL END,
597                 CASE 
598                     WHEN ('sitem' = ANY ($4)) THEN
599                         XMLELEMENT( name items,
600                             (SELECT XMLAGG(sitem) FROM (
601                                 SELECT  unapi.sitem( id, 'xml', 'serial_item', evergreen.array_remove_item_by_value($4,'sstr'), $5, $6, $7, $8, FALSE)
602                                   FROM  serial.item
603                                   WHERE stream = sstr.id
604                             )x)
605                         )
606                     ELSE NULL
607                 END
608             )
609       FROM  serial.stream sstr
610       WHERE id = $1
611       GROUP BY id, routing_label, distribution;
612 $F$ LANGUAGE SQL STABLE;
613
614 CREATE OR REPLACE FUNCTION unapi.siss ( obj_id BIGINT, format TEXT,  ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit HSTORE DEFAULT NULL, soffset HSTORE DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$
615     SELECT  XMLELEMENT(
616                 name issuance,
617                 XMLATTRIBUTES(
618                     CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
619                     'tag:open-ils.org:U2@siss/' || id AS id,
620                     create_date, edit_date, label, date_published,
621                     holding_code, holding_type, holding_link_id
622                 ),
623                 CASE WHEN subscription IS NOT NULL AND ('ssub' = ANY ($4)) THEN unapi.ssub( subscription, 'xml', 'subscription', evergreen.array_remove_item_by_value($4,'siss'), $5, $6, $7, $8, FALSE) ELSE NULL END,
624                 CASE 
625                     WHEN ('sitem' = ANY ($4)) THEN
626                         XMLELEMENT( name items,
627                             (SELECT XMLAGG(sitem) FROM (
628                                 SELECT  unapi.sitem( id, 'xml', 'serial_item', evergreen.array_remove_item_by_value($4,'siss'), $5, $6, $7, $8, FALSE)
629                                   FROM  serial.item
630                                   WHERE issuance = sstr.id
631                             )x)
632                         )
633                     ELSE NULL
634                 END
635             )
636       FROM  serial.issuance sstr
637       WHERE id = $1
638       GROUP BY id, create_date, edit_date, label, date_published, holding_code, holding_type, holding_link_id, subscription;
639 $F$ LANGUAGE SQL STABLE;
640
641 CREATE OR REPLACE FUNCTION unapi.sitem ( obj_id BIGINT, format TEXT,  ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit HSTORE DEFAULT NULL, soffset HSTORE DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$
642         SELECT  XMLELEMENT(
643                     name serial_item,
644                     XMLATTRIBUTES(
645                         CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
646                         'tag:open-ils.org:U2@sitem/' || id AS id,
647                         'tag:open-ils.org:U2@siss/' || issuance AS issuance,
648                         date_expected, date_received
649                     ),
650                     CASE WHEN issuance IS NOT NULL AND ('siss' = ANY ($4)) THEN unapi.siss( issuance, $2, 'issuance', evergreen.array_remove_item_by_value($4,'sitem'), $5, $6, $7, $8, FALSE) ELSE NULL END,
651                     CASE WHEN stream IS NOT NULL AND ('sstr' = ANY ($4)) THEN unapi.sstr( stream, $2, 'stream', evergreen.array_remove_item_by_value($4,'sitem'), $5, $6, $7, $8, FALSE) ELSE NULL END,
652                     CASE WHEN unit IS NOT NULL AND ('sunit' = ANY ($4)) THEN unapi.sunit( unit, $2, 'serial_unit', evergreen.array_remove_item_by_value($4,'sitem'), $5, $6, $7, $8, FALSE) ELSE NULL END,
653                     CASE WHEN uri IS NOT NULL AND ('auri' = ANY ($4)) THEN unapi.auri( uri, $2, 'uri', evergreen.array_remove_item_by_value($4,'sitem'), $5, $6, $7, $8, FALSE) ELSE NULL END
654 --                    XMLELEMENT( name notes,
655 --                        CASE 
656 --                            WHEN ('acpn' = ANY ($4)) THEN
657 --                                (SELECT XMLAGG(acpn) FROM (
658 --                                    SELECT  unapi.acpn( id, 'xml', 'copy_note', evergreen.array_remove_item_by_value($4,'acp'), $5, $6, $7, $8)
659 --                                      FROM  asset.copy_note
660 --                                      WHERE owning_copy = cp.id AND pub
661 --                                )x)
662 --                            ELSE NULL
663 --                        END
664 --                    )
665                 )
666           FROM  serial.item sitem
667           WHERE id = $1;
668 $F$ LANGUAGE SQL STABLE;
669
670
671 CREATE OR REPLACE FUNCTION unapi.sssum ( obj_id BIGINT, format TEXT,  ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit HSTORE DEFAULT NULL, soffset HSTORE DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$
672     SELECT  XMLELEMENT(
673                 name serial_summary,
674                 XMLATTRIBUTES(
675                     CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
676                     'tag:open-ils.org:U2@sbsum/' || id AS id,
677                     'sssum' AS type, generated_coverage, textual_holdings, show_generated
678                 ),
679                 CASE WHEN ('sdist' = ANY ($4)) THEN unapi.sdist( distribution, 'xml', 'distribtion', evergreen.array_remove_item_by_value($4,'ssum'), $5, $6, $7, $8, FALSE) ELSE NULL END
680             )
681       FROM  serial.supplement_summary ssum
682       WHERE id = $1
683       GROUP BY id, generated_coverage, textual_holdings, distribution, show_generated;
684 $F$ LANGUAGE SQL STABLE;
685
686 CREATE OR REPLACE FUNCTION unapi.sbsum ( obj_id BIGINT, format TEXT,  ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit HSTORE DEFAULT NULL, soffset HSTORE DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$
687     SELECT  XMLELEMENT(
688                 name serial_summary,
689                 XMLATTRIBUTES(
690                     CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
691                     'tag:open-ils.org:U2@sbsum/' || id AS id,
692                     'sbsum' AS type, generated_coverage, textual_holdings, show_generated
693                 ),
694                 CASE WHEN ('sdist' = ANY ($4)) THEN unapi.sdist( distribution, 'xml', 'distribtion', evergreen.array_remove_item_by_value($4,'ssum'), $5, $6, $7, $8, FALSE) ELSE NULL END
695             )
696       FROM  serial.basic_summary ssum
697       WHERE id = $1
698       GROUP BY id, generated_coverage, textual_holdings, distribution, show_generated;
699 $F$ LANGUAGE SQL STABLE;
700
701 CREATE OR REPLACE FUNCTION unapi.sisum ( obj_id BIGINT, format TEXT,  ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit HSTORE DEFAULT NULL, soffset HSTORE DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$
702     SELECT  XMLELEMENT(
703                 name serial_summary,
704                 XMLATTRIBUTES(
705                     CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
706                     'tag:open-ils.org:U2@sbsum/' || id AS id,
707                     'sisum' AS type, generated_coverage, textual_holdings, show_generated
708                 ),
709                 CASE WHEN ('sdist' = ANY ($4)) THEN unapi.sdist( distribution, 'xml', 'distribtion', evergreen.array_remove_item_by_value($4,'ssum'), $5, $6, $7, $8, FALSE) ELSE NULL END
710             )
711       FROM  serial.index_summary ssum
712       WHERE id = $1
713       GROUP BY id, generated_coverage, textual_holdings, distribution, show_generated;
714 $F$ LANGUAGE SQL STABLE;
715
716
717 CREATE OR REPLACE FUNCTION unapi.aou ( obj_id BIGINT, format TEXT,  ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit HSTORE DEFAULT NULL, soffset HSTORE DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$
718 DECLARE
719     output XML;
720 BEGIN
721     IF ename = 'circlib' THEN
722         SELECT  XMLELEMENT(
723                     name circlib,
724                     XMLATTRIBUTES(
725                         'http://open-ils.org/spec/actors/v1' AS xmlns,
726                         id AS ident
727                     ),
728                     name
729                 ) INTO output
730           FROM  actor.org_unit aou
731           WHERE id = obj_id;
732     ELSE
733         EXECUTE $$SELECT  XMLELEMENT(
734                     name $$ || ename || $$,
735                     XMLATTRIBUTES(
736                         'http://open-ils.org/spec/actors/v1' AS xmlns,
737                         'tag:open-ils.org:U2@aou/' || id AS id,
738                         shortname, name, opac_visible
739                     )
740                 )
741           FROM  actor.org_unit aou
742          WHERE id = $1 $$ INTO output USING obj_id;
743     END IF;
744
745     RETURN output;
746
747 END;
748 $F$ LANGUAGE PLPGSQL STABLE;
749
750 CREATE OR REPLACE FUNCTION unapi.acl ( obj_id BIGINT, format TEXT,  ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit HSTORE DEFAULT NULL, soffset HSTORE DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$
751     SELECT  XMLELEMENT(
752                 name location,
753                 XMLATTRIBUTES(
754                     CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
755                     id AS ident,
756                     holdable,
757                     opac_visible,
758                     label_prefix AS prefix,
759                     label_suffix AS suffix
760                 ),
761                 name
762             )
763       FROM  asset.copy_location
764       WHERE id = $1;
765 $F$ LANGUAGE SQL STABLE;
766
767 CREATE OR REPLACE FUNCTION unapi.ccs ( obj_id BIGINT, format TEXT,  ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit HSTORE DEFAULT NULL, soffset HSTORE DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$
768     SELECT  XMLELEMENT(
769                 name status,
770                 XMLATTRIBUTES(
771                     CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
772                     id AS ident,
773                     holdable,
774                     opac_visible
775                 ),
776                 name
777             )
778       FROM  config.copy_status
779       WHERE id = $1;
780 $F$ LANGUAGE SQL STABLE;
781
782 CREATE OR REPLACE FUNCTION unapi.acpn ( obj_id BIGINT, format TEXT,  ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit HSTORE DEFAULT NULL, soffset HSTORE DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$
783         SELECT  XMLELEMENT(
784                     name copy_note,
785                     XMLATTRIBUTES(
786                         CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
787                         create_date AS date,
788                         title
789                     ),
790                     value
791                 )
792           FROM  asset.copy_note
793           WHERE id = $1;
794 $F$ LANGUAGE SQL STABLE;
795
796 CREATE OR REPLACE FUNCTION unapi.ascecm ( obj_id BIGINT, format TEXT,  ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit HSTORE DEFAULT NULL, soffset HSTORE DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$
797         SELECT  XMLELEMENT(
798                     name statcat,
799                     XMLATTRIBUTES(
800                         CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
801                         sc.name,
802                         sc.opac_visible
803                     ),
804                     asce.value
805                 )
806           FROM  asset.stat_cat_entry asce
807                 JOIN asset.stat_cat sc ON (sc.id = asce.stat_cat)
808           WHERE asce.id = $1;
809 $F$ LANGUAGE SQL STABLE;
810
811 CREATE OR REPLACE FUNCTION unapi.bmp ( obj_id BIGINT, format TEXT,  ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit HSTORE DEFAULT NULL, soffset HSTORE DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$
812         SELECT  XMLELEMENT(
813                     name monograph_part,
814                     XMLATTRIBUTES(
815                         CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
816                         'tag:open-ils.org:U2@bmp/' || id AS id,
817                         id AS ident,
818                         label,
819                         label_sortkey,
820                         'tag:open-ils.org:U2@bre/' || record AS record
821                     ),
822                     CASE 
823                         WHEN ('acp' = ANY ($4)) THEN
824                             XMLELEMENT( name copies,
825                                 (SELECT XMLAGG(acp) FROM (
826                                     SELECT  unapi.acp( cp.id, 'xml', 'copy', evergreen.array_remove_item_by_value($4,'bmp'), $5, $6, $7, $8, FALSE)
827                                       FROM  asset.copy cp
828                                             JOIN asset.copy_part_map cpm ON (cpm.target_copy = cp.id)
829                                       WHERE cpm.part = $1
830                                           AND cp.deleted IS FALSE
831                                       ORDER BY COALESCE(cp.copy_number,0), cp.barcode
832                                       LIMIT ($7 -> 'acp')::INT
833                                       OFFSET ($8 -> 'acp')::INT
834
835                                 )x)
836                             )
837                         ELSE NULL
838                     END,
839                     CASE WHEN ('bre' = ANY ($4)) THEN unapi.bre( record, 'marcxml', 'record', evergreen.array_remove_item_by_value($4,'bmp'), $5, $6, $7, $8, FALSE) ELSE NULL END
840                 )
841           FROM  biblio.monograph_part
842           WHERE id = $1
843           GROUP BY id, label, label_sortkey, record;
844 $F$ LANGUAGE SQL STABLE;
845
846 CREATE OR REPLACE FUNCTION unapi.acp ( obj_id BIGINT, format TEXT,  ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit HSTORE DEFAULT NULL, soffset HSTORE DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$
847         SELECT  XMLELEMENT(
848                     name copy,
849                     XMLATTRIBUTES(
850                         CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
851                         'tag:open-ils.org:U2@acp/' || id AS id, id AS copy_id,
852                         create_date, edit_date, copy_number, circulate, deposit,
853                         ref, holdable, deleted, deposit_amount, price, barcode,
854                         circ_modifier, circ_as_type, opac_visible, age_protect
855                     ),
856                     unapi.ccs( status, $2, 'status', evergreen.array_remove_item_by_value($4,'acp'), $5, $6, $7, $8, FALSE),
857                     unapi.acl( location, $2, 'location', evergreen.array_remove_item_by_value($4,'acp'), $5, $6, $7, $8, FALSE),
858                     unapi.aou( circ_lib, $2, 'circ_lib', evergreen.array_remove_item_by_value($4,'acp'), $5, $6, $7, $8),
859                     unapi.aou( circ_lib, $2, 'circlib', evergreen.array_remove_item_by_value($4,'acp'), $5, $6, $7, $8),
860                     CASE WHEN ('acn' = ANY ($4)) THEN unapi.acn( call_number, $2, 'call_number', evergreen.array_remove_item_by_value($4,'acp'), $5, $6, $7, $8, FALSE) ELSE NULL END,
861                     CASE 
862                         WHEN ('acpn' = ANY ($4)) THEN
863                             XMLELEMENT( name copy_notes,
864                                 (SELECT XMLAGG(acpn) FROM (
865                                     SELECT  unapi.acpn( id, 'xml', 'copy_note', evergreen.array_remove_item_by_value($4,'acp'), $5, $6, $7, $8, FALSE)
866                                       FROM  asset.copy_note
867                                       WHERE owning_copy = cp.id AND pub
868                                 )x)
869                             )
870                         ELSE NULL
871                     END,
872                     CASE 
873                         WHEN ('ascecm' = ANY ($4)) THEN
874                             XMLELEMENT( name statcats,
875                                 (SELECT XMLAGG(ascecm) FROM (
876                                     SELECT  unapi.ascecm( stat_cat_entry, 'xml', 'statcat', evergreen.array_remove_item_by_value($4,'acp'), $5, $6, $7, $8, FALSE)
877                                       FROM  asset.stat_cat_entry_copy_map
878                                       WHERE owning_copy = cp.id
879                                 )x)
880                             )
881                         ELSE NULL
882                     END,
883                     CASE
884                         WHEN ('bre' = ANY ($4)) THEN
885                             XMLELEMENT( name foreign_records,
886                                 (SELECT XMLAGG(bre) FROM (
887                                     SELECT  unapi.bre(peer_record,'marcxml','record','{}'::TEXT[], $5, $6, $7, $8, FALSE)
888                                       FROM  biblio.peer_bib_copy_map
889                                       WHERE target_copy = cp.id
890                                 )x)
891
892                             )
893                         ELSE NULL
894                     END,
895                     CASE 
896                         WHEN ('bmp' = ANY ($4)) THEN
897                             XMLELEMENT( name monograph_parts,
898                                 (SELECT XMLAGG(bmp) FROM (
899                                     SELECT  unapi.bmp( part, 'xml', 'monograph_part', evergreen.array_remove_item_by_value($4,'acp'), $5, $6, $7, $8, FALSE)
900                                       FROM  asset.copy_part_map
901                                       WHERE target_copy = cp.id
902                                 )x)
903                             )
904                         ELSE NULL
905                     END,
906                     CASE 
907                         WHEN ('circ' = ANY ($4)) THEN
908                             XMLELEMENT( name current_circulation,
909                                 (SELECT XMLAGG(circ) FROM (
910                                     SELECT  unapi.circ( id, 'xml', 'circ', evergreen.array_remove_item_by_value($4,'circ'), $5, $6, $7, $8, FALSE)
911                                       FROM  action.circulation
912                                       WHERE target_copy = cp.id
913                                             AND checkin_time IS NULL
914                                 )x)
915                             )
916                         ELSE NULL
917                     END
918                 )
919           FROM  asset.copy cp
920           WHERE id = $1
921               AND cp.deleted IS FALSE
922           GROUP BY id, status, location, circ_lib, call_number, create_date,
923               edit_date, copy_number, circulate, deposit, ref, holdable,
924               deleted, deposit_amount, price, barcode, circ_modifier,
925               circ_as_type, opac_visible, age_protect;
926 $F$ LANGUAGE SQL STABLE;
927
928 CREATE OR REPLACE FUNCTION unapi.sunit ( obj_id BIGINT, format TEXT,  ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit HSTORE DEFAULT NULL, soffset HSTORE DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$
929         SELECT  XMLELEMENT(
930                     name serial_unit,
931                     XMLATTRIBUTES(
932                         CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
933                         'tag:open-ils.org:U2@acp/' || id AS id, id AS copy_id,
934                         create_date, edit_date, copy_number, circulate, deposit,
935                         ref, holdable, deleted, deposit_amount, price, barcode,
936                         circ_modifier, circ_as_type, opac_visible, age_protect,
937                         status_changed_time, floating, mint_condition,
938                         detailed_contents, sort_key, summary_contents, cost 
939                     ),
940                     unapi.ccs( status, $2, 'status', evergreen.array_remove_item_by_value( evergreen.array_remove_item_by_value($4,'acp'),'sunit'), $5, $6, $7, $8, FALSE),
941                     unapi.acl( location, $2, 'location', evergreen.array_remove_item_by_value( evergreen.array_remove_item_by_value($4,'acp'),'sunit'), $5, $6, $7, $8, FALSE),
942                     unapi.aou( circ_lib, $2, 'circ_lib', evergreen.array_remove_item_by_value( evergreen.array_remove_item_by_value($4,'acp'),'sunit'), $5, $6, $7, $8),
943                     unapi.aou( circ_lib, $2, 'circlib', evergreen.array_remove_item_by_value( evergreen.array_remove_item_by_value($4,'acp'),'sunit'), $5, $6, $7, $8),
944                     CASE WHEN ('acn' = ANY ($4)) THEN unapi.acn( call_number, $2, 'call_number', evergreen.array_remove_item_by_value($4,'acp'), $5, $6, $7, $8, FALSE) ELSE NULL END,
945                     XMLELEMENT( name copy_notes,
946                         CASE 
947                             WHEN ('acpn' = ANY ($4)) THEN
948                                 (SELECT XMLAGG(acpn) FROM (
949                                     SELECT  unapi.acpn( id, 'xml', 'copy_note', evergreen.array_remove_item_by_value( evergreen.array_remove_item_by_value($4,'acp'),'sunit'), $5, $6, $7, $8, FALSE)
950                                       FROM  asset.copy_note
951                                       WHERE owning_copy = cp.id AND pub
952                                 )x)
953                             ELSE NULL
954                         END
955                     ),
956                     XMLELEMENT( name statcats,
957                         CASE 
958                             WHEN ('ascecm' = ANY ($4)) THEN
959                                 (SELECT XMLAGG(ascecm) FROM (
960                                     SELECT  unapi.ascecm( stat_cat_entry, 'xml', 'statcat', evergreen.array_remove_item_by_value($4,'acp'), $5, $6, $7, $8, FALSE)
961                                       FROM  asset.stat_cat_entry_copy_map
962                                       WHERE owning_copy = cp.id
963                                 )x)
964                             ELSE NULL
965                         END
966                     ),
967                     XMLELEMENT( name foreign_records,
968                         CASE
969                             WHEN ('bre' = ANY ($4)) THEN
970                                 (SELECT XMLAGG(bre) FROM (
971                                     SELECT  unapi.bre(peer_record,'marcxml','record','{}'::TEXT[], $5, $6, $7, $8, FALSE)
972                                       FROM  biblio.peer_bib_copy_map
973                                       WHERE target_copy = cp.id
974                                 )x)
975                             ELSE NULL
976                         END
977                     ),
978                     CASE 
979                         WHEN ('bmp' = ANY ($4)) THEN
980                             XMLELEMENT( name monograph_parts,
981                                 (SELECT XMLAGG(bmp) FROM (
982                                     SELECT  unapi.bmp( part, 'xml', 'monograph_part', evergreen.array_remove_item_by_value($4,'acp'), $5, $6, $7, $8, FALSE)
983                                       FROM  asset.copy_part_map
984                                       WHERE target_copy = cp.id
985                                 )x)
986                             )
987                         ELSE NULL
988                     END,
989                     CASE 
990                         WHEN ('circ' = ANY ($4)) THEN
991                             XMLELEMENT( name current_circulation,
992                                 (SELECT XMLAGG(circ) FROM (
993                                     SELECT  unapi.circ( id, 'xml', 'circ', evergreen.array_remove_item_by_value($4,'circ'), $5, $6, $7, $8, FALSE)
994                                       FROM  action.circulation
995                                       WHERE target_copy = cp.id
996                                             AND checkin_time IS NULL
997                                 )x)
998                             )
999                         ELSE NULL
1000                     END
1001                 )
1002           FROM  serial.unit cp
1003           WHERE id = $1
1004               AND cp.deleted IS FALSE
1005           GROUP BY id, status, location, circ_lib, call_number, create_date,
1006               edit_date, copy_number, circulate, floating, mint_condition,
1007               deposit, ref, holdable, deleted, deposit_amount, price,
1008               barcode, circ_modifier, circ_as_type, opac_visible,
1009               status_changed_time, detailed_contents, sort_key,
1010               summary_contents, cost, age_protect;
1011 $F$ LANGUAGE SQL STABLE;
1012
1013 CREATE OR REPLACE FUNCTION unapi.acn ( obj_id BIGINT, format TEXT,  ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit HSTORE DEFAULT NULL, soffset HSTORE DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$
1014         SELECT  XMLELEMENT(
1015                     name volume,
1016                     XMLATTRIBUTES(
1017                         CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
1018                         'tag:open-ils.org:U2@acn/' || acn.id AS id,
1019                         acn.id AS vol_id, o.shortname AS lib,
1020                         o.opac_visible AS opac_visible,
1021                         deleted, label, label_sortkey, label_class, record
1022                     ),
1023                     unapi.aou( owning_lib, $2, 'owning_lib', evergreen.array_remove_item_by_value($4,'acn'), $5, $6, $7, $8),
1024                     CASE 
1025                         WHEN ('acp' = ANY ($4)) THEN
1026                             CASE WHEN $6 IS NOT NULL THEN
1027                                 XMLELEMENT( name copies,
1028                                     (SELECT XMLAGG(acp ORDER BY rank_avail) FROM (
1029                                         SELECT  unapi.acp( cp.id, 'xml', 'copy', evergreen.array_remove_item_by_value($4,'acn'), $5, $6, $7, $8, FALSE),
1030                                             evergreen.rank_cp_status(cp.status) AS rank_avail
1031                                           FROM  asset.copy cp
1032                                                 JOIN actor.org_unit_descendants( (SELECT id FROM actor.org_unit WHERE shortname = $5), $6) aoud ON (cp.circ_lib = aoud.id)
1033                                           WHERE cp.call_number = acn.id
1034                                               AND cp.deleted IS FALSE
1035                                           ORDER BY rank_avail, COALESCE(cp.copy_number,0), cp.barcode
1036                                           LIMIT ($7 -> 'acp')::INT
1037                                           OFFSET ($8 -> 'acp')::INT
1038                                     )x)
1039                                 )
1040                             ELSE
1041                                 XMLELEMENT( name copies,
1042                                     (SELECT XMLAGG(acp ORDER BY rank_avail) FROM (
1043                                         SELECT  unapi.acp( cp.id, 'xml', 'copy', evergreen.array_remove_item_by_value($4,'acn'), $5, $6, $7, $8, FALSE),
1044                                             evergreen.rank_cp_status(cp.status) AS rank_avail
1045                                           FROM  asset.copy cp
1046                                                 JOIN actor.org_unit_descendants( (SELECT id FROM actor.org_unit WHERE shortname = $5) ) aoud ON (cp.circ_lib = aoud.id)
1047                                           WHERE cp.call_number = acn.id
1048                                               AND cp.deleted IS FALSE
1049                                           ORDER BY rank_avail, COALESCE(cp.copy_number,0), cp.barcode
1050                                           LIMIT ($7 -> 'acp')::INT
1051                                           OFFSET ($8 -> 'acp')::INT
1052                                     )x)
1053                                 )
1054                             END
1055                         ELSE NULL
1056                     END,
1057                     XMLELEMENT(
1058                         name uris,
1059                         (SELECT XMLAGG(auri) FROM (SELECT unapi.auri(uri,'xml','uri', evergreen.array_remove_item_by_value($4,'acn'), $5, $6, $7, $8, FALSE) FROM asset.uri_call_number_map WHERE call_number = acn.id)x)
1060                     ),
1061                     unapi.acnp( acn.prefix, 'marcxml', 'prefix', evergreen.array_remove_item_by_value($4,'acn'), $5, $6, $7, $8, FALSE),
1062                     unapi.acns( acn.suffix, 'marcxml', 'suffix', evergreen.array_remove_item_by_value($4,'acn'), $5, $6, $7, $8, FALSE),
1063                     CASE WHEN ('bre' = ANY ($4)) THEN unapi.bre( acn.record, 'marcxml', 'record', evergreen.array_remove_item_by_value($4,'acn'), $5, $6, $7, $8, FALSE) ELSE NULL END
1064                 ) AS x
1065           FROM  asset.call_number acn
1066                 JOIN actor.org_unit o ON (o.id = acn.owning_lib)
1067           WHERE acn.id = $1
1068               AND acn.deleted IS FALSE
1069           GROUP BY acn.id, o.shortname, o.opac_visible, deleted, label, label_sortkey, label_class, owning_lib, record, acn.prefix, acn.suffix;
1070 $F$ LANGUAGE SQL STABLE;
1071
1072 CREATE OR REPLACE FUNCTION unapi.acnp ( obj_id BIGINT, format TEXT,  ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit HSTORE DEFAULT NULL, soffset HSTORE DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$
1073         SELECT  XMLELEMENT(
1074                     name call_number_prefix,
1075                     XMLATTRIBUTES(
1076                         CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
1077                         id AS ident,
1078                         label,
1079                         'tag:open-ils.org:U2@aou/' || owning_lib AS owning_lib,
1080                         label_sortkey
1081                     )
1082                 )
1083           FROM  asset.call_number_prefix
1084           WHERE id = $1;
1085 $F$ LANGUAGE SQL STABLE;
1086
1087 CREATE OR REPLACE FUNCTION unapi.acns ( obj_id BIGINT, format TEXT,  ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit HSTORE DEFAULT NULL, soffset HSTORE DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$
1088         SELECT  XMLELEMENT(
1089                     name call_number_suffix,
1090                     XMLATTRIBUTES(
1091                         CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
1092                         id AS ident,
1093                         label,
1094                         'tag:open-ils.org:U2@aou/' || owning_lib AS owning_lib,
1095                         label_sortkey
1096                     )
1097                 )
1098           FROM  asset.call_number_suffix
1099           WHERE id = $1;
1100 $F$ LANGUAGE SQL STABLE;
1101
1102 CREATE OR REPLACE FUNCTION unapi.auri ( obj_id BIGINT, format TEXT,  ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit HSTORE DEFAULT NULL, soffset HSTORE DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$
1103         SELECT  XMLELEMENT(
1104                     name uri,
1105                     XMLATTRIBUTES(
1106                         CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
1107                         'tag:open-ils.org:U2@auri/' || uri.id AS id,
1108                         use_restriction,
1109                         href,
1110                         label
1111                     ),
1112                     CASE 
1113                         WHEN ('acn' = ANY ($4)) THEN
1114                             XMLELEMENT( name copies,
1115                                 (SELECT XMLAGG(acn) FROM (SELECT unapi.acn( call_number, 'xml', 'copy', evergreen.array_remove_item_by_value($4,'auri'), $5, $6, $7, $8, FALSE) FROM asset.uri_call_number_map WHERE uri = uri.id)x)
1116                             )
1117                         ELSE NULL
1118                     END
1119                 ) AS x
1120           FROM  asset.uri uri
1121           WHERE uri.id = $1
1122           GROUP BY uri.id, use_restriction, href, label;
1123 $F$ LANGUAGE SQL STABLE;
1124
1125 CREATE OR REPLACE FUNCTION unapi.mra ( obj_id BIGINT, format TEXT,  ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit HSTORE DEFAULT NULL, soffset HSTORE DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$
1126         SELECT  XMLELEMENT(
1127                     name attributes,
1128                     XMLATTRIBUTES(
1129                         CASE WHEN $9 THEN 'http://open-ils.org/spec/indexing/v1' ELSE NULL END AS xmlns,
1130                         'tag:open-ils.org:U2@mra/' || mra.id AS id,
1131                         'tag:open-ils.org:U2@bre/' || mra.id AS record
1132                     ),
1133                     (SELECT XMLAGG(foo.y)
1134                       FROM (SELECT XMLELEMENT(
1135                                 name field,
1136                                 XMLATTRIBUTES(
1137                                     key AS name,
1138                                     cvm.value AS "coded-value",
1139                                     cvm.id AS "cvmid",
1140                                     rad.filter,
1141                                     rad.sorter
1142                                 ),
1143                                 x.value
1144                             )
1145                            FROM EACH(mra.attrs) AS x
1146                                 JOIN config.record_attr_definition rad ON (x.key = rad.name)
1147                                 LEFT JOIN config.coded_value_map cvm ON (cvm.ctype = x.key AND code = x.value)
1148                         )foo(y)
1149                     )
1150                 )
1151           FROM  metabib.record_attr mra
1152           WHERE mra.id = $1;
1153 $F$ LANGUAGE SQL STABLE;
1154
1155 CREATE OR REPLACE FUNCTION unapi.circ (obj_id BIGINT, format TEXT, ename TEXT, includes TEXT[], org TEXT DEFAULT '-', depth INT DEFAULT NULL, slimit HSTORE DEFAULT NULL, soffset HSTORE DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$
1156     SELECT XMLELEMENT(
1157         name circ,
1158         XMLATTRIBUTES(
1159             CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
1160             'tag:open-ils.org:U2@circ/' || id AS id,
1161             xact_start,
1162             due_date
1163         ),
1164         CASE WHEN ('aou' = ANY ($4)) THEN unapi.aou( circ_lib, $2, 'circ_lib', evergreen.array_remove_item_by_value($4,'circ'), $5, $6, $7, $8, FALSE) ELSE NULL END,
1165         CASE WHEN ('acp' = ANY ($4)) THEN unapi.acp( circ_lib, $2, 'target_copy', evergreen.array_remove_item_by_value($4,'circ'), $5, $6, $7, $8, FALSE) ELSE NULL END
1166     )
1167     FROM action.circulation
1168     WHERE id = $1;
1169 $F$ LANGUAGE SQL STABLE;
1170
1171 /*
1172
1173  -- Some test queries
1174
1175 SELECT unapi.memoize( 'bre', 1,'mods32','','{holdings_xml,acp}'::TEXT[], 'SYS1');
1176 SELECT unapi.memoize( 'bre', 1,'marcxml','','{holdings_xml,acp}'::TEXT[], 'SYS1');
1177 SELECT unapi.memoize( 'bre', 1,'holdings_xml','','{holdings_xml,acp}'::TEXT[], 'SYS1');
1178
1179 SELECT unapi.biblio_record_entry_feed('{1}'::BIGINT[],'mods32','{holdings_xml,acp}'::TEXT[],'SYS1',NULL,'acn=>1',NULL, NULL,NULL,NULL,NULL,'http://c64/opac/extras/unapi', '<totalResults xmlns="http://a9.com/-/spec/opensearch/1.1/">2</totalResults><startIndex xmlns="http://a9.com/-/spec/opensearch/1.1/">1</startIndex><itemsPerPage xmlns="http://a9.com/-/spec/opensearch/1.1/">10</itemsPerPage>');
1180
1181 SELECT unapi.biblio_record_entry_feed('{7209,7394}'::BIGINT[],'marcxml','{}'::TEXT[],'SYS1',NULL,'acn=>1',NULL, NULL,NULL,NULL,NULL,'http://fulfillment2.esilibrary.com/opac/extras/unapi', '<totalResults xmlns="http://a9.com/-/spec/opensearch/1.1/">2</totalResults><startIndex xmlns="http://a9.com/-/spec/opensearch/1.1/">1</startIndex><itemsPerPage xmlns="http://a9.com/-/spec/opensearch/1.1/">10</itemsPerPage>');
1182 EXPLAIN ANALYZE SELECT unapi.biblio_record_entry_feed('{7209,7394}'::BIGINT[],'marcxml','{}'::TEXT[],'SYS1',NULL,'acn=>1',NULL, NULL,NULL,NULL,NULL,'http://fulfillment2.esilibrary.com/opac/extras/unapi', '<totalResults xmlns="http://a9.com/-/spec/opensearch/1.1/">2</totalResults><startIndex xmlns="http://a9.com/-/spec/opensearch/1.1/">1</startIndex><itemsPerPage xmlns="http://a9.com/-/spec/opensearch/1.1/">10</itemsPerPage>');
1183 EXPLAIN ANALYZE SELECT unapi.biblio_record_entry_feed('{7209,7394}'::BIGINT[],'marcxml','{holdings_xml}'::TEXT[],'SYS1',NULL,'acn=>1',NULL, NULL,NULL,NULL,NULL,'http://fulfillment2.esilibrary.com/opac/extras/unapi', '<totalResults xmlns="http://a9.com/-/spec/opensearch/1.1/">2</totalResults><startIndex xmlns="http://a9.com/-/spec/opensearch/1.1/">1</startIndex><itemsPerPage xmlns="http://a9.com/-/spec/opensearch/1.1/">10</itemsPerPage>');
1184 EXPLAIN ANALYZE SELECT unapi.biblio_record_entry_feed('{7209,7394}'::BIGINT[],'mods32','{holdings_xml}'::TEXT[],'SYS1',NULL,'acn=>1',NULL, NULL,NULL,NULL,NULL,'http://fulfillment2.esilibrary.com/opac/extras/unapi', '<totalResults xmlns="http://a9.com/-/spec/opensearch/1.1/">2</totalResults><startIndex xmlns="http://a9.com/-/spec/opensearch/1.1/">1</startIndex><itemsPerPage xmlns="http://a9.com/-/spec/opensearch/1.1/">10</itemsPerPage>');
1185
1186 SELECT unapi.biblio_record_entry_feed('{216}'::BIGINT[],'marcxml','{}'::TEXT[], 'BR1');
1187 EXPLAIN ANALYZE SELECT unapi.bre(216,'marcxml','record','{holdings_xml,bre.unapi}'::TEXT[], 'BR1');
1188 EXPLAIN ANALYZE SELECT unapi.bre(216,'holdings_xml','record','{}'::TEXT[], 'BR1');
1189 EXPLAIN ANALYZE SELECT unapi.holdings_xml(216,4,'BR1',2,'{bre}'::TEXT[]);
1190 EXPLAIN ANALYZE SELECT unapi.bre(216,'mods32','record','{}'::TEXT[], 'BR1');
1191
1192 -- Limit to 5 call numbers, 5 copies, with a preferred library of 4 (BR1), in SYS2 at a depth of 0
1193 EXPLAIN ANALYZE SELECT unapi.bre(36,'marcxml','record','{holdings_xml,mra,acp,acnp,acns,bmp}','SYS2',0,'acn=>5,acp=>5',NULL,TRUE,4);
1194
1195 */
1196
1197 COMMIT;