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