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