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