]> git.evergreen-ils.org Git - working/Evergreen.git/blob - Open-ILS/src/sql/Pg/990.schema.unapi.sql
92e7fc8c853ed43461d73409ba4bde17f1fcdbd1
[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     WITH RECURSIVE ou_depth AS (
77         SELECT COALESCE(
78             $3,
79             (
80                 SELECT depth
81                 FROM actor.org_unit_type aout
82                     INNER JOIN actor.org_unit ou ON ou_type = aout.id
83                 WHERE ou.id = $2
84             )
85         ) AS depth
86     ), descendant_depth AS (
87         SELECT  ou.id,
88                 ou.parent_ou,
89                 out.depth
90         FROM  actor.org_unit ou
91                 JOIN actor.org_unit_type out ON (out.id = ou.ou_type)
92                 JOIN anscestor_depth ad ON (ad.id = ou.id),
93                 ou_depth
94         WHERE ad.depth = ou_depth.depth
95             UNION ALL
96         SELECT  ou.id,
97                 ou.parent_ou,
98                 out.depth
99         FROM  actor.org_unit ou
100                 JOIN actor.org_unit_type out ON (out.id = ou.ou_type)
101                 JOIN descendant_depth ot ON (ot.id = ou.parent_ou)
102     ), anscestor_depth AS (
103         SELECT  ou.id,
104                 ou.parent_ou,
105                 out.depth
106         FROM  actor.org_unit ou
107                 JOIN actor.org_unit_type out ON (out.id = ou.ou_type)
108         WHERE ou.id = $2
109             UNION ALL
110         SELECT  ou.id,
111                 ou.parent_ou,
112                 out.depth
113         FROM  actor.org_unit ou
114                 JOIN actor.org_unit_type out ON (out.id = ou.ou_type)
115                 JOIN anscestor_depth ot ON (ot.parent_ou = ou.id)
116     ), descendants as (
117         SELECT ou.* FROM actor.org_unit ou JOIN descendant_depth USING (id)
118     )
119
120     SELECT ua.id, ua.name, ua.label_sortkey, MIN(ua.rank) AS rank FROM (
121         SELECT acn.id, aou.name, acn.label_sortkey,
122             RANK() OVER w
123         FROM asset.call_number acn
124             JOIN asset.copy acp ON (acn.id = acp.call_number)
125             JOIN descendants AS aou ON (acp.circ_lib = aou.id)
126         WHERE acn.record = ANY ($1)
127             AND acn.deleted IS FALSE
128             AND acp.deleted IS FALSE
129             AND CASE WHEN ('exclude_invisible_acn' = ANY($7)) THEN 
130                 EXISTS (
131                     SELECT 1 
132                     FROM asset.opac_visible_copies 
133                     WHERE copy_id = acp.id AND record = acn.record
134                 ) ELSE TRUE END
135         GROUP BY acn.id, acp.status, aou.name, acn.label_sortkey, aou.id
136         WINDOW w AS (
137             ORDER BY 
138                 COALESCE(
139                     CASE WHEN aou.id = $2 THEN -20000 END,
140                     CASE WHEN aou.id = $6 THEN -10000 END,
141                     (SELECT distance - 5000
142                         FROM actor.org_unit_descendants_distance($6) as x
143                         WHERE x.id = aou.id AND $6 IN (
144                             SELECT q.id FROM actor.org_unit_descendants($2) as q)),
145                     (SELECT e.distance FROM actor.org_unit_descendants_distance($2) as e WHERE e.id = aou.id),
146                     1000
147                 ),
148                 evergreen.rank_cp_status(acp.status)
149         )
150     ) AS ua
151     GROUP BY ua.id, ua.name, ua.label_sortkey
152     ORDER BY rank, ua.name, ua.label_sortkey
153     LIMIT ($4 -> 'acn')::INT
154     OFFSET ($5 -> 'acn')::INT;
155 $$ LANGUAGE SQL STABLE ROWS 10;
156
157 CREATE OR REPLACE FUNCTION evergreen.ranked_volumes
158     ( bibid BIGINT, ouid INT, depth INT DEFAULT NULL, slimit HSTORE DEFAULT NULL, soffset HSTORE DEFAULT NULL, pref_lib INT DEFAULT NULL, includes TEXT[] DEFAULT NULL::TEXT[] )
159     RETURNS TABLE (id BIGINT, name TEXT, label_sortkey TEXT, rank BIGINT)
160     AS $$ SELECT * FROM evergreen.ranked_volumes(ARRAY[$1],$2,$3,$4,$5,$6,$7) $$ LANGUAGE SQL STABLE;
161
162
163 CREATE OR REPLACE FUNCTION evergreen.located_uris (
164     bibid BIGINT[], 
165     ouid INT,
166     pref_lib INT DEFAULT NULL
167 ) RETURNS TABLE (id BIGINT, name TEXT, label_sortkey TEXT, rank INT) AS $$
168     WITH all_orgs AS (SELECT COALESCE( enabled, FALSE ) AS flag FROM config.global_flag WHERE name = 'opac.located_uri.act_as_copy')
169     SELECT DISTINCT ON (id) * FROM (
170     SELECT acn.id, COALESCE(aou.name,aoud.name), acn.label_sortkey, evergreen.rank_ou(aou.id, $2, $3) AS pref_ou
171       FROM asset.call_number acn
172            INNER JOIN asset.uri_call_number_map auricnm ON acn.id = auricnm.call_number
173            INNER JOIN asset.uri auri ON auri.id = auricnm.uri
174            LEFT JOIN actor.org_unit_ancestors( COALESCE($3, $2) ) aou ON (acn.owning_lib = aou.id)
175            LEFT JOIN actor.org_unit_descendants( COALESCE($3, $2) ) aoud ON (acn.owning_lib = aoud.id),
176            all_orgs
177       WHERE acn.record = ANY ($1)
178           AND acn.deleted IS FALSE
179           AND auri.active IS TRUE
180           AND ((NOT all_orgs.flag AND aou.id IS NOT NULL) OR (all_orgs.flag AND COALESCE(aou.id,aoud.id) IS NOT NULL))
181     UNION
182     SELECT acn.id, COALESCE(aou.name,aoud.name) AS name, acn.label_sortkey, evergreen.rank_ou(aou.id, $2, $3) AS pref_ou
183       FROM asset.call_number acn
184            INNER JOIN asset.uri_call_number_map auricnm ON acn.id = auricnm.call_number
185            INNER JOIN asset.uri auri ON auri.id = auricnm.uri
186            LEFT JOIN actor.org_unit_ancestors( $2 ) aou ON (acn.owning_lib = aou.id)
187            LEFT JOIN actor.org_unit_descendants( $2 ) aoud ON (acn.owning_lib = aoud.id),
188            all_orgs
189       WHERE acn.record = ANY ($1)
190           AND acn.deleted IS FALSE
191           AND auri.active IS TRUE
192           AND ((NOT all_orgs.flag AND aou.id IS NOT NULL) OR (all_orgs.flag AND COALESCE(aou.id,aoud.id) IS NOT NULL)))x
193     ORDER BY id, pref_ou DESC;
194 $$
195 LANGUAGE SQL STABLE;
196
197 CREATE OR REPLACE FUNCTION evergreen.located_uris ( bibid BIGINT, ouid INT, pref_lib INT DEFAULT NULL)
198     RETURNS TABLE (id BIGINT, name TEXT, label_sortkey TEXT, rank INT)
199     AS $$ SELECT * FROM evergreen.located_uris(ARRAY[$1],$2,$3) $$ LANGUAGE SQL STABLE;
200
201 CREATE TABLE unapi.bre_output_layout (
202     name                TEXT    PRIMARY KEY,
203     transform           TEXT    REFERENCES config.xml_transform (name) ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE INITIALLY DEFERRED,
204     mime_type           TEXT    NOT NULL,
205     feed_top            TEXT    NOT NULL,
206     holdings_element    TEXT,
207     title_element       TEXT,
208     description_element TEXT,
209     creator_element     TEXT,
210     update_ts_element   TEXT
211 );
212
213 INSERT INTO unapi.bre_output_layout
214     (name,           transform, mime_type,              holdings_element, feed_top,         title_element, description_element, creator_element, update_ts_element)
215         VALUES
216     ('holdings_xml', NULL,      'application/xml',      NULL,             'hxml',           NULL,          NULL,                NULL,            NULL),
217     ('marcxml',      'marcxml', 'application/marc+xml', 'record',         'collection',     NULL,          NULL,                NULL,            NULL),
218     ('mods32',       'mods32',  'application/mods+xml', 'mods',           'modsCollection', NULL,          NULL,                NULL,            NULL)
219 ;
220
221 -- Dummy functions, so we can create the real ones out of order
222 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;
223 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;
224 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;
225 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;
226 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;
227 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;
228 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;
229 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;
230 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;
231 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;
232 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;
233 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;
234 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;
235 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;
236 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;
237 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;
238 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;
239 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;
240 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;
241 CREATE OR REPLACE FUNCTION unapi.bre (
242     obj_id BIGINT,
243     format TEXT,
244     ename TEXT,
245     includes TEXT[],
246     org TEXT,
247     depth INT DEFAULT NULL,
248     slimit HSTORE DEFAULT NULL,
249     soffset HSTORE DEFAULT NULL,
250     include_xmlns BOOL DEFAULT TRUE,
251     pref_lib INT DEFAULT NULL
252 )
253 RETURNS XML AS $F$ SELECT NULL::XML $F$ LANGUAGE SQL STABLE;
254 CREATE OR REPLACE FUNCTION unapi.mmr (
255     obj_id BIGINT,
256     format TEXT,
257     ename TEXT,
258     includes TEXT[],
259     org TEXT,
260     depth INT DEFAULT NULL,
261     slimit HSTORE DEFAULT NULL,
262     soffset HSTORE DEFAULT NULL,
263     include_xmlns BOOL DEFAULT TRUE,
264     pref_lib INT DEFAULT NULL
265 )
266 RETURNS XML AS $F$ SELECT NULL::XML $F$ LANGUAGE SQL STABLE;
267 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;
268 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;
269 CREATE OR REPLACE FUNCTION unapi.mmr_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, pref_lib INT DEFAULT NULL) RETURNS XML AS $F$ SELECT NULL::XML $F$ LANGUAGE SQL STABLE;
270 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;
271
272 CREATE OR REPLACE FUNCTION unapi.holdings_xml (
273     bid BIGINT,
274     ouid INT,
275     org TEXT,
276     depth INT DEFAULT NULL,
277     includes TEXT[] DEFAULT NULL::TEXT[],
278     slimit HSTORE DEFAULT NULL,
279     soffset HSTORE DEFAULT NULL,
280     include_xmlns BOOL DEFAULT TRUE,
281     pref_lib INT DEFAULT NULL
282 )
283 RETURNS XML AS $F$ SELECT NULL::XML $F$ LANGUAGE SQL STABLE;
284
285 CREATE OR REPLACE FUNCTION unapi.mmr_holdings_xml (
286     mid BIGINT,
287     ouid INT,
288     org TEXT,
289     depth INT DEFAULT NULL,
290     includes TEXT[] DEFAULT NULL::TEXT[],
291     slimit HSTORE DEFAULT NULL,
292     soffset HSTORE DEFAULT NULL,
293     include_xmlns BOOL DEFAULT TRUE,
294     pref_lib INT DEFAULT NULL
295 )
296 RETURNS XML AS $F$ SELECT NULL::XML $F$ LANGUAGE SQL STABLE;
297
298 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;
299
300 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$
301 DECLARE
302     key     TEXT;
303     output  XML;
304 BEGIN
305     key :=
306         'id'        || COALESCE(obj_id::TEXT,'') ||
307         'format'    || COALESCE(format::TEXT,'') ||
308         'ename'     || COALESCE(ename::TEXT,'') ||
309         'includes'  || COALESCE(includes::TEXT,'{}'::TEXT[]::TEXT) ||
310         'org'       || COALESCE(org::TEXT,'') ||
311         'depth'     || COALESCE(depth::TEXT,'') ||
312         'slimit'    || COALESCE(slimit::TEXT,'') ||
313         'soffset'   || COALESCE(soffset::TEXT,'') ||
314         'include_xmlns'   || COALESCE(include_xmlns::TEXT,'');
315     -- RAISE NOTICE 'memoize key: %', key;
316
317     key := MD5(key);
318     -- RAISE NOTICE 'memoize hash: %', key;
319
320     -- XXX cache logic ... memcached? table?
321
322     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;
323     RETURN output;
324 END;
325 $F$ LANGUAGE PLPGSQL STABLE;
326
327 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$
328 DECLARE
329     layout          unapi.bre_output_layout%ROWTYPE;
330     transform       config.xml_transform%ROWTYPE;
331     item_format     TEXT;
332     tmp_xml         TEXT;
333     xmlns_uri       TEXT := 'http://open-ils.org/spec/feed-xml/v1';
334     ouid            INT;
335     element_list    TEXT[];
336 BEGIN
337
338     IF org = '-' OR org IS NULL THEN
339         SELECT shortname INTO org FROM evergreen.org_top();
340     END IF;
341
342     SELECT id INTO ouid FROM actor.org_unit WHERE shortname = org;
343     SELECT * INTO layout FROM unapi.bre_output_layout WHERE name = format;
344
345     IF layout.name IS NULL THEN
346         RETURN NULL::XML;
347     END IF;
348
349     SELECT * INTO transform FROM config.xml_transform WHERE name = layout.transform;
350     xmlns_uri := COALESCE(transform.namespace_uri,xmlns_uri);
351
352     -- Gather the bib xml
353     SELECT XMLAGG( unapi.bre(i, format, '', includes, org, depth, slimit, soffset, include_xmlns)) INTO tmp_xml FROM UNNEST( id_list ) i;
354
355     IF layout.title_element IS NOT NULL THEN
356         EXECUTE 'SELECT XMLCONCAT( XMLELEMENT( name '|| layout.title_element ||', XMLATTRIBUTES( $1 AS xmlns), $3), $2)' INTO tmp_xml USING xmlns_uri, tmp_xml::XML, title;
357     END IF;
358
359     IF layout.description_element IS NOT NULL THEN
360         EXECUTE 'SELECT XMLCONCAT( XMLELEMENT( name '|| layout.description_element ||', XMLATTRIBUTES( $1 AS xmlns), $3), $2)' INTO tmp_xml USING xmlns_uri, tmp_xml::XML, description;
361     END IF;
362
363     IF layout.creator_element IS NOT NULL THEN
364         EXECUTE 'SELECT XMLCONCAT( XMLELEMENT( name '|| layout.creator_element ||', XMLATTRIBUTES( $1 AS xmlns), $3), $2)' INTO tmp_xml USING xmlns_uri, tmp_xml::XML, creator;
365     END IF;
366
367     IF layout.update_ts_element IS NOT NULL THEN
368         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;
369     END IF;
370
371     IF unapi_url IS NOT NULL THEN
372         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;
373     END IF;
374
375     IF header_xml IS NOT NULL THEN tmp_xml := XMLCONCAT(header_xml,tmp_xml::XML); END IF;
376
377     element_list := regexp_split_to_array(layout.feed_top,E'\\.');
378     FOR i IN REVERSE ARRAY_UPPER(element_list, 1) .. 1 LOOP
379         EXECUTE 'SELECT XMLELEMENT( name '|| quote_ident(element_list[i]) ||', XMLATTRIBUTES( $1 AS xmlns), $2)' INTO tmp_xml USING xmlns_uri, tmp_xml::XML;
380     END LOOP;
381
382     RETURN tmp_xml::XML;
383 END;
384 $F$ LANGUAGE PLPGSQL STABLE;
385
386 CREATE OR REPLACE FUNCTION unapi.bre (
387     obj_id BIGINT,
388     format TEXT,
389     ename TEXT,
390     includes TEXT[],
391     org TEXT,
392     depth INT DEFAULT NULL,
393     slimit HSTORE DEFAULT NULL,
394     soffset HSTORE DEFAULT NULL,
395     include_xmlns BOOL DEFAULT TRUE,
396     pref_lib INT DEFAULT NULL
397 )
398 RETURNS XML AS $F$
399 DECLARE
400     me      biblio.record_entry%ROWTYPE;
401     layout  unapi.bre_output_layout%ROWTYPE;
402     xfrm    config.xml_transform%ROWTYPE;
403     ouid    INT;
404     tmp_xml TEXT;
405     top_el  TEXT;
406     output  XML;
407     hxml    XML;
408     axml    XML;
409 BEGIN
410
411     IF org = '-' OR org IS NULL THEN
412         SELECT shortname INTO org FROM evergreen.org_top();
413     END IF;
414
415     SELECT id INTO ouid FROM actor.org_unit WHERE shortname = org;
416
417     IF ouid IS NULL THEN
418         RETURN NULL::XML;
419     END IF;
420
421     IF format = 'holdings_xml' THEN -- the special case
422         output := unapi.holdings_xml( obj_id, ouid, org, depth, includes, slimit, soffset, include_xmlns);
423         RETURN output;
424     END IF;
425
426     SELECT * INTO layout FROM unapi.bre_output_layout WHERE name = format;
427
428     IF layout.name IS NULL THEN
429         RETURN NULL::XML;
430     END IF;
431
432     SELECT * INTO xfrm FROM config.xml_transform WHERE name = layout.transform;
433
434     SELECT * INTO me FROM biblio.record_entry WHERE id = obj_id;
435
436     -- grab SVF if we need them
437     IF ('mra' = ANY (includes)) THEN 
438         axml := unapi.mra(obj_id,NULL,NULL,NULL,NULL);
439     ELSE
440         axml := NULL::XML;
441     END IF;
442
443     -- grab holdings if we need them
444     IF ('holdings_xml' = ANY (includes)) THEN 
445         hxml := unapi.holdings_xml(obj_id, ouid, org, depth, evergreen.array_remove_item_by_value(includes,'holdings_xml'), slimit, soffset, include_xmlns, pref_lib);
446     ELSE
447         hxml := NULL::XML;
448     END IF;
449
450
451     -- generate our item node
452
453
454     IF format = 'marcxml' THEN
455         tmp_xml := me.marc;
456         IF tmp_xml !~ E'<marc:' THEN -- If we're not using the prefixed namespace in this record, then remove all declarations of it
457            tmp_xml := REGEXP_REPLACE(tmp_xml, ' xmlns:marc="http://www.loc.gov/MARC21/slim"', '', 'g');
458         END IF; 
459     ELSE
460         tmp_xml := oils_xslt_process(me.marc, xfrm.xslt)::XML;
461     END IF;
462
463     top_el := REGEXP_REPLACE(tmp_xml, E'^.*?<((?:\\S+:)?' || layout.holdings_element || ').*$', E'\\1');
464
465     IF axml IS NOT NULL THEN 
466         tmp_xml := REGEXP_REPLACE(tmp_xml, '</' || top_el || '>(.*?)$', axml || '</' || top_el || E'>\\1');
467     END IF;
468
469     IF hxml IS NOT NULL THEN -- XXX how do we configure the holdings position?
470         tmp_xml := REGEXP_REPLACE(tmp_xml, '</' || top_el || '>(.*?)$', hxml || '</' || top_el || E'>\\1');
471     END IF;
472
473     IF ('bre.unapi' = ANY (includes)) THEN 
474         output := REGEXP_REPLACE(
475             tmp_xml,
476             '</' || top_el || '>(.*?)',
477             XMLELEMENT(
478                 name abbr,
479                 XMLATTRIBUTES(
480                     'http://www.w3.org/1999/xhtml' AS xmlns,
481                     'unapi-id' AS class,
482                     'tag:open-ils.org:U2@bre/' || obj_id || '/' || org AS title
483                 )
484             )::TEXT || '</' || top_el || E'>\\1'
485         );
486     ELSE
487         output := tmp_xml;
488     END IF;
489
490     output := REGEXP_REPLACE(output::TEXT,E'>\\s+<','><','gs')::XML;
491     RETURN output;
492 END;
493 $F$ LANGUAGE PLPGSQL STABLE;
494
495 CREATE OR REPLACE FUNCTION unapi.holdings_xml (
496     bid BIGINT,
497     ouid INT,
498     org TEXT,
499     depth INT DEFAULT NULL,
500     includes TEXT[] DEFAULT NULL::TEXT[],
501     slimit HSTORE DEFAULT NULL,
502     soffset HSTORE DEFAULT NULL,
503     include_xmlns BOOL DEFAULT TRUE,
504     pref_lib INT DEFAULT NULL
505 )
506 RETURNS XML AS $F$
507      SELECT  XMLELEMENT(
508                  name holdings,
509                  XMLATTRIBUTES(
510                     CASE WHEN $8 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
511                     CASE WHEN ('bre' = ANY ($5)) THEN 'tag:open-ils.org:U2@bre/' || $1 || '/' || $3 ELSE NULL END AS id,
512                     (SELECT record_has_holdable_copy FROM asset.record_has_holdable_copy($1)) AS has_holdable
513                  ),
514                  XMLELEMENT(
515                      name counts,
516                      (SELECT  XMLAGG(XMLELEMENT::XML) FROM (
517                          SELECT  XMLELEMENT(
518                                      name count,
519                                      XMLATTRIBUTES('public' as type, depth, org_unit, coalesce(transcendant,0) as transcendant, available, visible as count, unshadow)
520                                  )::text
521                            FROM  asset.opac_ou_record_copy_count($2,  $1)
522                                      UNION
523                          SELECT  XMLELEMENT(
524                                      name count,
525                                      XMLATTRIBUTES('staff' as type, depth, org_unit, coalesce(transcendant,0) as transcendant, available, visible as count, unshadow)
526                                  )::text
527                            FROM  asset.staff_ou_record_copy_count($2, $1)
528                                      UNION
529                          SELECT  XMLELEMENT(
530                                      name count,
531                                      XMLATTRIBUTES('pref_lib' as type, depth, org_unit, coalesce(transcendant,0) as transcendant, available, visible as count, unshadow)
532                                  )::text
533                            FROM  asset.opac_ou_record_copy_count($9,  $1)
534                                      ORDER BY 1
535                      )x)
536                  ),
537                  CASE 
538                      WHEN ('bmp' = ANY ($5)) THEN
539                         XMLELEMENT(
540                             name monograph_parts,
541                             (SELECT XMLAGG(bmp) FROM (
542                                 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)
543                                   FROM  biblio.monograph_part
544                                   WHERE record = $1
545                             )x)
546                         )
547                      ELSE NULL
548                  END,
549                  XMLELEMENT(
550                      name volumes,
551                      (SELECT XMLAGG(acn ORDER BY rank, name, label_sortkey) FROM (
552                         -- Physical copies
553                         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
554                         FROM evergreen.ranked_volumes($1, $2, $4, $6, $7, $9, $5) AS y
555                         UNION ALL
556                         -- Located URIs
557                         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
558                         FROM evergreen.located_uris($1, $2, $9) AS uris
559                      )x)
560                  ),
561                  CASE WHEN ('ssub' = ANY ($5)) THEN 
562                      XMLELEMENT(
563                          name subscriptions,
564                          (SELECT XMLAGG(ssub) FROM (
565                             SELECT  unapi.ssub(id,'xml','subscription','{}'::TEXT[], $3, $4, $6, $7, FALSE)
566                               FROM  serial.subscription
567                               WHERE record_entry = $1
568                         )x)
569                      )
570                  ELSE NULL END,
571                  CASE WHEN ('acp' = ANY ($5)) THEN 
572                      XMLELEMENT(
573                          name foreign_copies,
574                          (SELECT XMLAGG(acp) FROM (
575                             SELECT  unapi.acp(p.target_copy,'xml','copy',evergreen.array_remove_item_by_value($5,'acp'), $3, $4, $6, $7, FALSE)
576                               FROM  biblio.peer_bib_copy_map p
577                                     JOIN asset.copy c ON (p.target_copy = c.id)
578                               WHERE NOT c.deleted AND p.peer_record = $1
579                             LIMIT ($6 -> 'acp')::INT
580                             OFFSET ($7 -> 'acp')::INT
581                         )x)
582                      )
583                  ELSE NULL END
584              );
585 $F$ LANGUAGE SQL STABLE;
586
587 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$
588         SELECT  XMLELEMENT(
589                     name subscription,
590                     XMLATTRIBUTES(
591                         CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
592                         'tag:open-ils.org:U2@ssub/' || id AS id,
593                         'tag:open-ils.org:U2@aou/' || owning_lib AS owning_lib,
594                         start_date AS start, end_date AS end, expected_date_offset
595                     ),
596                     CASE 
597                         WHEN ('sdist' = ANY ($4)) THEN
598                             XMLELEMENT( name distributions,
599                                 (SELECT XMLAGG(sdist) FROM (
600                                     SELECT  unapi.sdist( id, 'xml', 'distribution', evergreen.array_remove_item_by_value($4,'ssub'), $5, $6, $7, $8, FALSE)
601                                       FROM  serial.distribution
602                                       WHERE subscription = ssub.id
603                                 )x)
604                             )
605                         ELSE NULL
606                     END
607                 )
608           FROM  serial.subscription ssub
609           WHERE id = $1
610           GROUP BY id, start_date, end_date, expected_date_offset, owning_lib;
611 $F$ LANGUAGE SQL STABLE;
612
613 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$
614         SELECT  XMLELEMENT(
615                     name distribution,
616                     XMLATTRIBUTES(
617                         CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
618                         'tag:open-ils.org:U2@sdist/' || id AS id,
619                                 'tag:open-ils.org:U2@acn/' || receive_call_number AS receive_call_number,
620                                     'tag:open-ils.org:U2@acn/' || bind_call_number AS bind_call_number,
621                         unit_label_prefix, label, unit_label_suffix, summary_method
622                     ),
623                     unapi.aou( holding_lib, $2, 'holding_lib', evergreen.array_remove_item_by_value($4,'sdist'), $5, $6, $7, $8),
624                     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,
625                     CASE 
626                         WHEN ('sstr' = ANY ($4)) THEN
627                             XMLELEMENT( name streams,
628                                 (SELECT XMLAGG(sstr) FROM (
629                                     SELECT  unapi.sstr( id, 'xml', 'stream', evergreen.array_remove_item_by_value($4,'sdist'), $5, $6, $7, $8, FALSE)
630                                       FROM  serial.stream
631                                       WHERE distribution = sdist.id
632                                 )x)
633                             )
634                         ELSE NULL
635                     END,
636                     XMLELEMENT( name summaries,
637                         CASE 
638                             WHEN ('sbsum' = ANY ($4)) THEN
639                                 (SELECT XMLAGG(sbsum) FROM (
640                                     SELECT  unapi.sbsum( id, 'xml', 'serial_summary', evergreen.array_remove_item_by_value($4,'sdist'), $5, $6, $7, $8, FALSE)
641                                       FROM  serial.basic_summary
642                                       WHERE distribution = sdist.id
643                                 )x)
644                             ELSE NULL
645                         END,
646                         CASE 
647                             WHEN ('sisum' = ANY ($4)) THEN
648                                 (SELECT XMLAGG(sisum) FROM (
649                                     SELECT  unapi.sisum( id, 'xml', 'serial_summary', evergreen.array_remove_item_by_value($4,'sdist'), $5, $6, $7, $8, FALSE)
650                                       FROM  serial.index_summary
651                                       WHERE distribution = sdist.id
652                                 )x)
653                             ELSE NULL
654                         END,
655                         CASE 
656                             WHEN ('sssum' = ANY ($4)) THEN
657                                 (SELECT XMLAGG(sssum) FROM (
658                                     SELECT  unapi.sssum( id, 'xml', 'serial_summary', evergreen.array_remove_item_by_value($4,'sdist'), $5, $6, $7, $8, FALSE)
659                                       FROM  serial.supplement_summary
660                                       WHERE distribution = sdist.id
661                                 )x)
662                             ELSE NULL
663                         END
664                     )
665                 )
666           FROM  serial.distribution sdist
667           WHERE id = $1
668           GROUP BY id, label, unit_label_prefix, unit_label_suffix, holding_lib, summary_method, subscription, receive_call_number, bind_call_number;
669 $F$ LANGUAGE SQL STABLE;
670
671 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$
672     SELECT  XMLELEMENT(
673                 name stream,
674                 XMLATTRIBUTES(
675                     CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
676                     'tag:open-ils.org:U2@sstr/' || id AS id,
677                     routing_label
678                 ),
679                 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,
680                 CASE 
681                     WHEN ('sitem' = ANY ($4)) THEN
682                         XMLELEMENT( name items,
683                             (SELECT XMLAGG(sitem) FROM (
684                                 SELECT  unapi.sitem( id, 'xml', 'serial_item', evergreen.array_remove_item_by_value($4,'sstr'), $5, $6, $7, $8, FALSE)
685                                   FROM  serial.item
686                                   WHERE stream = sstr.id
687                             )x)
688                         )
689                     ELSE NULL
690                 END
691             )
692       FROM  serial.stream sstr
693       WHERE id = $1
694       GROUP BY id, routing_label, distribution;
695 $F$ LANGUAGE SQL STABLE;
696
697 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$
698     SELECT  XMLELEMENT(
699                 name issuance,
700                 XMLATTRIBUTES(
701                     CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
702                     'tag:open-ils.org:U2@siss/' || id AS id,
703                     create_date, edit_date, label, date_published,
704                     holding_code, holding_type, holding_link_id
705                 ),
706                 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,
707                 CASE 
708                     WHEN ('sitem' = ANY ($4)) THEN
709                         XMLELEMENT( name items,
710                             (SELECT XMLAGG(sitem) FROM (
711                                 SELECT  unapi.sitem( id, 'xml', 'serial_item', evergreen.array_remove_item_by_value($4,'siss'), $5, $6, $7, $8, FALSE)
712                                   FROM  serial.item
713                                   WHERE issuance = sstr.id
714                             )x)
715                         )
716                     ELSE NULL
717                 END
718             )
719       FROM  serial.issuance sstr
720       WHERE id = $1
721       GROUP BY id, create_date, edit_date, label, date_published, holding_code, holding_type, holding_link_id, subscription;
722 $F$ LANGUAGE SQL STABLE;
723
724 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$
725         SELECT  XMLELEMENT(
726                     name serial_item,
727                     XMLATTRIBUTES(
728                         CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
729                         'tag:open-ils.org:U2@sitem/' || id AS id,
730                         'tag:open-ils.org:U2@siss/' || issuance AS issuance,
731                         date_expected, date_received
732                     ),
733                     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,
734                     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,
735                     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,
736                     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
737 --                    XMLELEMENT( name notes,
738 --                        CASE 
739 --                            WHEN ('acpn' = ANY ($4)) THEN
740 --                                (SELECT XMLAGG(acpn) FROM (
741 --                                    SELECT  unapi.acpn( id, 'xml', 'copy_note', evergreen.array_remove_item_by_value($4,'acp'), $5, $6, $7, $8)
742 --                                      FROM  asset.copy_note
743 --                                      WHERE owning_copy = cp.id AND pub
744 --                                )x)
745 --                            ELSE NULL
746 --                        END
747 --                    )
748                 )
749           FROM  serial.item sitem
750           WHERE id = $1;
751 $F$ LANGUAGE SQL STABLE;
752
753
754 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$
755     SELECT  XMLELEMENT(
756                 name serial_summary,
757                 XMLATTRIBUTES(
758                     CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
759                     'tag:open-ils.org:U2@sbsum/' || id AS id,
760                     'sssum' AS type, generated_coverage, textual_holdings, show_generated
761                 ),
762                 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
763             )
764       FROM  serial.supplement_summary ssum
765       WHERE id = $1
766       GROUP BY id, generated_coverage, textual_holdings, distribution, show_generated;
767 $F$ LANGUAGE SQL STABLE;
768
769 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$
770     SELECT  XMLELEMENT(
771                 name serial_summary,
772                 XMLATTRIBUTES(
773                     CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
774                     'tag:open-ils.org:U2@sbsum/' || id AS id,
775                     'sbsum' AS type, generated_coverage, textual_holdings, show_generated
776                 ),
777                 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
778             )
779       FROM  serial.basic_summary ssum
780       WHERE id = $1
781       GROUP BY id, generated_coverage, textual_holdings, distribution, show_generated;
782 $F$ LANGUAGE SQL STABLE;
783
784 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$
785     SELECT  XMLELEMENT(
786                 name serial_summary,
787                 XMLATTRIBUTES(
788                     CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
789                     'tag:open-ils.org:U2@sbsum/' || id AS id,
790                     'sisum' AS type, generated_coverage, textual_holdings, show_generated
791                 ),
792                 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
793             )
794       FROM  serial.index_summary ssum
795       WHERE id = $1
796       GROUP BY id, generated_coverage, textual_holdings, distribution, show_generated;
797 $F$ LANGUAGE SQL STABLE;
798
799
800 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$
801 DECLARE
802     output XML;
803 BEGIN
804     IF ename = 'circlib' THEN
805         SELECT  XMLELEMENT(
806                     name circlib,
807                     XMLATTRIBUTES(
808                         'http://open-ils.org/spec/actors/v1' AS xmlns,
809                         id AS ident
810                     ),
811                     name
812                 ) INTO output
813           FROM  actor.org_unit aou
814           WHERE id = obj_id;
815     ELSE
816         EXECUTE $$SELECT  XMLELEMENT(
817                     name $$ || ename || $$,
818                     XMLATTRIBUTES(
819                         'http://open-ils.org/spec/actors/v1' AS xmlns,
820                         'tag:open-ils.org:U2@aou/' || id AS id,
821                         shortname, name, opac_visible
822                     )
823                 )
824           FROM  actor.org_unit aou
825          WHERE id = $1 $$ INTO output USING obj_id;
826     END IF;
827
828     RETURN output;
829
830 END;
831 $F$ LANGUAGE PLPGSQL STABLE;
832
833 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$
834     SELECT  XMLELEMENT(
835                 name location,
836                 XMLATTRIBUTES(
837                     CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
838                     id AS ident,
839                     holdable,
840                     opac_visible,
841                     label_prefix AS prefix,
842                     label_suffix AS suffix
843                 ),
844                 name
845             )
846       FROM  asset.copy_location
847       WHERE id = $1;
848 $F$ LANGUAGE SQL STABLE;
849
850 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$
851     SELECT  XMLELEMENT(
852                 name status,
853                 XMLATTRIBUTES(
854                     CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
855                     id AS ident,
856                     holdable,
857                     opac_visible
858                 ),
859                 name
860             )
861       FROM  config.copy_status
862       WHERE id = $1;
863 $F$ LANGUAGE SQL STABLE;
864
865 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$
866         SELECT  XMLELEMENT(
867                     name copy_note,
868                     XMLATTRIBUTES(
869                         CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
870                         create_date AS date,
871                         title
872                     ),
873                     value
874                 )
875           FROM  asset.copy_note
876           WHERE id = $1;
877 $F$ LANGUAGE SQL STABLE;
878
879 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$
880         SELECT  XMLELEMENT(
881                     name statcat,
882                     XMLATTRIBUTES(
883                         CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
884                         sc.name,
885                         sc.opac_visible
886                     ),
887                     asce.value
888                 )
889           FROM  asset.stat_cat_entry asce
890                 JOIN asset.stat_cat sc ON (sc.id = asce.stat_cat)
891           WHERE asce.id = $1;
892 $F$ LANGUAGE SQL STABLE;
893
894 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$
895         SELECT  XMLELEMENT(
896                     name monograph_part,
897                     XMLATTRIBUTES(
898                         CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
899                         'tag:open-ils.org:U2@bmp/' || id AS id,
900                         id AS ident,
901                         label,
902                         label_sortkey,
903                         'tag:open-ils.org:U2@bre/' || record AS record
904                     ),
905                     CASE 
906                         WHEN ('acp' = ANY ($4)) THEN
907                             XMLELEMENT( name copies,
908                                 (SELECT XMLAGG(acp) FROM (
909                                     SELECT  unapi.acp( cp.id, 'xml', 'copy', evergreen.array_remove_item_by_value($4,'bmp'), $5, $6, $7, $8, FALSE)
910                                       FROM  asset.copy cp
911                                             JOIN asset.copy_part_map cpm ON (cpm.target_copy = cp.id)
912                                       WHERE cpm.part = $1
913                                           AND cp.deleted IS FALSE
914                                       ORDER BY COALESCE(cp.copy_number,0), cp.barcode
915                                       LIMIT ($7 -> 'acp')::INT
916                                       OFFSET ($8 -> 'acp')::INT
917
918                                 )x)
919                             )
920                         ELSE NULL
921                     END,
922                     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
923                 )
924           FROM  biblio.monograph_part
925           WHERE id = $1
926           GROUP BY id, label, label_sortkey, record;
927 $F$ LANGUAGE SQL STABLE;
928
929 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$
930         SELECT  XMLELEMENT(
931                     name copy,
932                     XMLATTRIBUTES(
933                         CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
934                         'tag:open-ils.org:U2@acp/' || id AS id, id AS copy_id,
935                         create_date, edit_date, copy_number, circulate, deposit,
936                         ref, holdable, deleted, deposit_amount, price, barcode,
937                         circ_modifier, circ_as_type, opac_visible, age_protect
938                     ),
939                     unapi.ccs( status, $2, 'status', evergreen.array_remove_item_by_value($4,'acp'), $5, $6, $7, $8, FALSE),
940                     unapi.acl( location, $2, 'location', evergreen.array_remove_item_by_value($4,'acp'), $5, $6, $7, $8, FALSE),
941                     unapi.aou( circ_lib, $2, 'circ_lib', evergreen.array_remove_item_by_value($4,'acp'), $5, $6, $7, $8),
942                     unapi.aou( circ_lib, $2, 'circlib', evergreen.array_remove_item_by_value($4,'acp'), $5, $6, $7, $8),
943                     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,
944                     CASE 
945                         WHEN ('acpn' = ANY ($4)) THEN
946                             XMLELEMENT( name copy_notes,
947                                 (SELECT XMLAGG(acpn) FROM (
948                                     SELECT  unapi.acpn( id, 'xml', 'copy_note', evergreen.array_remove_item_by_value($4,'acp'), $5, $6, $7, $8, FALSE)
949                                       FROM  asset.copy_note
950                                       WHERE owning_copy = cp.id AND pub
951                                 )x)
952                             )
953                         ELSE NULL
954                     END,
955                     CASE 
956                         WHEN ('ascecm' = ANY ($4)) THEN
957                             XMLELEMENT( name statcats,
958                                 (SELECT XMLAGG(ascecm) FROM (
959                                     SELECT  unapi.ascecm( stat_cat_entry, 'xml', 'statcat', evergreen.array_remove_item_by_value($4,'acp'), $5, $6, $7, $8, FALSE)
960                                       FROM  asset.stat_cat_entry_copy_map
961                                       WHERE owning_copy = cp.id
962                                 )x)
963                             )
964                         ELSE NULL
965                     END,
966                     CASE
967                         WHEN ('bre' = ANY ($4)) THEN
968                             XMLELEMENT( name foreign_records,
969                                 (SELECT XMLAGG(bre) FROM (
970                                     SELECT  unapi.bre(peer_record,'marcxml','record','{}'::TEXT[], $5, $6, $7, $8, FALSE)
971                                       FROM  biblio.peer_bib_copy_map
972                                       WHERE target_copy = cp.id
973                                 )x)
974
975                             )
976                         ELSE NULL
977                     END,
978                     CASE 
979                         WHEN ('bmp' = ANY ($4)) THEN
980                             XMLELEMENT( name monograph_parts,
981                                 (SELECT XMLAGG(bmp) FROM (
982                                     SELECT  unapi.bmp( part, 'xml', 'monograph_part', evergreen.array_remove_item_by_value($4,'acp'), $5, $6, $7, $8, FALSE)
983                                       FROM  asset.copy_part_map
984                                       WHERE target_copy = cp.id
985                                 )x)
986                             )
987                         ELSE NULL
988                     END,
989                     CASE 
990                         WHEN ('circ' = ANY ($4)) THEN
991                             XMLELEMENT( name current_circulation,
992                                 (SELECT XMLAGG(circ) FROM (
993                                     SELECT  unapi.circ( id, 'xml', 'circ', evergreen.array_remove_item_by_value($4,'circ'), $5, $6, $7, $8, FALSE)
994                                       FROM  action.circulation
995                                       WHERE target_copy = cp.id
996                                             AND checkin_time IS NULL
997                                 )x)
998                             )
999                         ELSE NULL
1000                     END
1001                 )
1002           FROM  asset.copy cp
1003           WHERE id = $1
1004               AND cp.deleted IS FALSE
1005           GROUP BY id, status, location, circ_lib, call_number, create_date,
1006               edit_date, copy_number, circulate, deposit, ref, holdable,
1007               deleted, deposit_amount, price, barcode, circ_modifier,
1008               circ_as_type, opac_visible, age_protect;
1009 $F$ LANGUAGE SQL STABLE;
1010
1011 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$
1012         SELECT  XMLELEMENT(
1013                     name serial_unit,
1014                     XMLATTRIBUTES(
1015                         CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
1016                         'tag:open-ils.org:U2@acp/' || id AS id, id AS copy_id,
1017                         create_date, edit_date, copy_number, circulate, deposit,
1018                         ref, holdable, deleted, deposit_amount, price, barcode,
1019                         circ_modifier, circ_as_type, opac_visible, age_protect,
1020                         status_changed_time, floating, mint_condition,
1021                         detailed_contents, sort_key, summary_contents, cost 
1022                     ),
1023                     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),
1024                     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),
1025                     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),
1026                     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),
1027                     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,
1028                     XMLELEMENT( name copy_notes,
1029                         CASE 
1030                             WHEN ('acpn' = ANY ($4)) THEN
1031                                 (SELECT XMLAGG(acpn) FROM (
1032                                     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)
1033                                       FROM  asset.copy_note
1034                                       WHERE owning_copy = cp.id AND pub
1035                                 )x)
1036                             ELSE NULL
1037                         END
1038                     ),
1039                     XMLELEMENT( name statcats,
1040                         CASE 
1041                             WHEN ('ascecm' = ANY ($4)) THEN
1042                                 (SELECT XMLAGG(ascecm) FROM (
1043                                     SELECT  unapi.ascecm( stat_cat_entry, 'xml', 'statcat', evergreen.array_remove_item_by_value($4,'acp'), $5, $6, $7, $8, FALSE)
1044                                       FROM  asset.stat_cat_entry_copy_map
1045                                       WHERE owning_copy = cp.id
1046                                 )x)
1047                             ELSE NULL
1048                         END
1049                     ),
1050                     XMLELEMENT( name foreign_records,
1051                         CASE
1052                             WHEN ('bre' = ANY ($4)) THEN
1053                                 (SELECT XMLAGG(bre) FROM (
1054                                     SELECT  unapi.bre(peer_record,'marcxml','record','{}'::TEXT[], $5, $6, $7, $8, FALSE)
1055                                       FROM  biblio.peer_bib_copy_map
1056                                       WHERE target_copy = cp.id
1057                                 )x)
1058                             ELSE NULL
1059                         END
1060                     ),
1061                     CASE 
1062                         WHEN ('bmp' = ANY ($4)) THEN
1063                             XMLELEMENT( name monograph_parts,
1064                                 (SELECT XMLAGG(bmp) FROM (
1065                                     SELECT  unapi.bmp( part, 'xml', 'monograph_part', evergreen.array_remove_item_by_value($4,'acp'), $5, $6, $7, $8, FALSE)
1066                                       FROM  asset.copy_part_map
1067                                       WHERE target_copy = cp.id
1068                                 )x)
1069                             )
1070                         ELSE NULL
1071                     END,
1072                     CASE 
1073                         WHEN ('circ' = ANY ($4)) THEN
1074                             XMLELEMENT( name current_circulation,
1075                                 (SELECT XMLAGG(circ) FROM (
1076                                     SELECT  unapi.circ( id, 'xml', 'circ', evergreen.array_remove_item_by_value($4,'circ'), $5, $6, $7, $8, FALSE)
1077                                       FROM  action.circulation
1078                                       WHERE target_copy = cp.id
1079                                             AND checkin_time IS NULL
1080                                 )x)
1081                             )
1082                         ELSE NULL
1083                     END
1084                 )
1085           FROM  serial.unit cp
1086           WHERE id = $1
1087               AND cp.deleted IS FALSE
1088           GROUP BY id, status, location, circ_lib, call_number, create_date,
1089               edit_date, copy_number, circulate, floating, mint_condition,
1090               deposit, ref, holdable, deleted, deposit_amount, price,
1091               barcode, circ_modifier, circ_as_type, opac_visible,
1092               status_changed_time, detailed_contents, sort_key,
1093               summary_contents, cost, age_protect;
1094 $F$ LANGUAGE SQL STABLE;
1095
1096 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$
1097         SELECT  XMLELEMENT(
1098                     name volume,
1099                     XMLATTRIBUTES(
1100                         CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
1101                         'tag:open-ils.org:U2@acn/' || acn.id AS id,
1102                         acn.id AS vol_id, o.shortname AS lib,
1103                         o.opac_visible AS opac_visible,
1104                         deleted, label, label_sortkey, label_class, record
1105                     ),
1106                     unapi.aou( owning_lib, $2, 'owning_lib', evergreen.array_remove_item_by_value($4,'acn'), $5, $6, $7, $8),
1107                     CASE 
1108                         WHEN ('acp' = ANY ($4)) THEN
1109                             CASE WHEN $6 IS NOT NULL THEN
1110                                 XMLELEMENT( name copies,
1111                                     (SELECT XMLAGG(acp ORDER BY rank_avail) FROM (
1112                                         SELECT  unapi.acp( cp.id, 'xml', 'copy', evergreen.array_remove_item_by_value($4,'acn'), $5, $6, $7, $8, FALSE),
1113                                             evergreen.rank_cp_status(cp.status) AS rank_avail
1114                                           FROM  asset.copy cp
1115                                                 JOIN actor.org_unit_descendants( (SELECT id FROM actor.org_unit WHERE shortname = $5), $6) aoud ON (cp.circ_lib = aoud.id)
1116                                           WHERE cp.call_number = acn.id
1117                                               AND cp.deleted IS FALSE
1118                                           ORDER BY rank_avail, COALESCE(cp.copy_number,0), cp.barcode
1119                                           LIMIT ($7 -> 'acp')::INT
1120                                           OFFSET ($8 -> 'acp')::INT
1121                                     )x)
1122                                 )
1123                             ELSE
1124                                 XMLELEMENT( name copies,
1125                                     (SELECT XMLAGG(acp ORDER BY rank_avail) FROM (
1126                                         SELECT  unapi.acp( cp.id, 'xml', 'copy', evergreen.array_remove_item_by_value($4,'acn'), $5, $6, $7, $8, FALSE),
1127                                             evergreen.rank_cp_status(cp.status) AS rank_avail
1128                                           FROM  asset.copy cp
1129                                                 JOIN actor.org_unit_descendants( (SELECT id FROM actor.org_unit WHERE shortname = $5) ) aoud ON (cp.circ_lib = aoud.id)
1130                                           WHERE cp.call_number = acn.id
1131                                               AND cp.deleted IS FALSE
1132                                           ORDER BY rank_avail, COALESCE(cp.copy_number,0), cp.barcode
1133                                           LIMIT ($7 -> 'acp')::INT
1134                                           OFFSET ($8 -> 'acp')::INT
1135                                     )x)
1136                                 )
1137                             END
1138                         ELSE NULL
1139                     END,
1140                     XMLELEMENT(
1141                         name uris,
1142                         (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)
1143                     ),
1144                     unapi.acnp( acn.prefix, 'marcxml', 'prefix', evergreen.array_remove_item_by_value($4,'acn'), $5, $6, $7, $8, FALSE),
1145                     unapi.acns( acn.suffix, 'marcxml', 'suffix', evergreen.array_remove_item_by_value($4,'acn'), $5, $6, $7, $8, FALSE),
1146                     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
1147                 ) AS x
1148           FROM  asset.call_number acn
1149                 JOIN actor.org_unit o ON (o.id = acn.owning_lib)
1150           WHERE acn.id = $1
1151               AND acn.deleted IS FALSE
1152           GROUP BY acn.id, o.shortname, o.opac_visible, deleted, label, label_sortkey, label_class, owning_lib, record, acn.prefix, acn.suffix;
1153 $F$ LANGUAGE SQL STABLE;
1154
1155 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$
1156         SELECT  XMLELEMENT(
1157                     name call_number_prefix,
1158                     XMLATTRIBUTES(
1159                         CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
1160                         id AS ident,
1161                         label,
1162                         'tag:open-ils.org:U2@aou/' || owning_lib AS owning_lib,
1163                         label_sortkey
1164                     )
1165                 )
1166           FROM  asset.call_number_prefix
1167           WHERE id = $1;
1168 $F$ LANGUAGE SQL STABLE;
1169
1170 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$
1171         SELECT  XMLELEMENT(
1172                     name call_number_suffix,
1173                     XMLATTRIBUTES(
1174                         CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
1175                         id AS ident,
1176                         label,
1177                         'tag:open-ils.org:U2@aou/' || owning_lib AS owning_lib,
1178                         label_sortkey
1179                     )
1180                 )
1181           FROM  asset.call_number_suffix
1182           WHERE id = $1;
1183 $F$ LANGUAGE SQL STABLE;
1184
1185 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$
1186         SELECT  XMLELEMENT(
1187                     name uri,
1188                     XMLATTRIBUTES(
1189                         CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
1190                         'tag:open-ils.org:U2@auri/' || uri.id AS id,
1191                         use_restriction,
1192                         href,
1193                         label
1194                     ),
1195                     CASE 
1196                         WHEN ('acn' = ANY ($4)) THEN
1197                             XMLELEMENT( name copies,
1198                                 (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)
1199                             )
1200                         ELSE NULL
1201                     END
1202                 ) AS x
1203           FROM  asset.uri uri
1204           WHERE uri.id = $1
1205           GROUP BY uri.id, use_restriction, href, label;
1206 $F$ LANGUAGE SQL STABLE;
1207
1208 CREATE OR REPLACE FUNCTION unapi.mra (
1209     obj_id BIGINT,
1210     format TEXT,
1211     ename TEXT,
1212     includes TEXT[],
1213     org TEXT,
1214     depth INT DEFAULT NULL,
1215     slimit HSTORE DEFAULT NULL,
1216     soffset HSTORE DEFAULT NULL,
1217     include_xmlns BOOL DEFAULT TRUE
1218 ) RETURNS XML AS $F$
1219     SELECT  XMLELEMENT(
1220         name attributes,
1221         XMLATTRIBUTES(
1222             CASE WHEN $9 THEN 'http://open-ils.org/spec/indexing/v1' ELSE NULL END AS xmlns,
1223             'tag:open-ils.org:U2@mra/' || $1 AS id, 
1224             'tag:open-ils.org:U2@bre/' || $1 AS record 
1225         ),  
1226         (SELECT XMLAGG(foo.y)
1227           FROM (
1228             SELECT  XMLELEMENT(
1229                         name field,
1230                         XMLATTRIBUTES(
1231                             mra.attr AS name,
1232                             cvm.value AS "coded-value",
1233                             cvm.id AS "cvmid",
1234                             rad.composite,
1235                             rad.multi,
1236                             rad.filter,
1237                             rad.sorter
1238                         ),
1239                         mra.value
1240                     )
1241               FROM  metabib.record_attr_flat mra
1242                     JOIN config.record_attr_definition rad ON (mra.attr = rad.name)
1243                     LEFT JOIN config.coded_value_map cvm ON (cvm.ctype = mra.attr AND code = mra.value)
1244               WHERE mra.id = $1
1245             )foo(y)
1246         )   
1247     )   
1248 $F$ LANGUAGE SQL STABLE;
1249
1250 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$
1251     SELECT XMLELEMENT(
1252         name circ,
1253         XMLATTRIBUTES(
1254             CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
1255             'tag:open-ils.org:U2@circ/' || id AS id,
1256             xact_start,
1257             due_date
1258         ),
1259         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,
1260         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
1261     )
1262     FROM action.circulation
1263     WHERE id = $1;
1264 $F$ LANGUAGE SQL STABLE;
1265
1266 CREATE OR REPLACE FUNCTION unapi.mmr_mra (
1267     obj_id BIGINT,
1268     format TEXT,
1269     ename TEXT,
1270     includes TEXT[],
1271     org TEXT,
1272     depth INT DEFAULT NULL,
1273     slimit HSTORE DEFAULT NULL,
1274     soffset HSTORE DEFAULT NULL,
1275     include_xmlns BOOL DEFAULT TRUE,
1276     pref_lib INT DEFAULT NULL
1277 ) RETURNS XML AS $F$
1278     SELECT  XMLELEMENT(
1279         name attributes,
1280         XMLATTRIBUTES(
1281             CASE WHEN $9 THEN 'http://open-ils.org/spec/indexing/v1' ELSE NULL END AS xmlns,
1282             'tag:open-ils.org:U2@mmr/' || $1 AS metarecord
1283         ),
1284         (SELECT XMLAGG(foo.y)
1285           FROM (
1286             SELECT  DISTINCT ON (COALESCE(cvm.id,uvm.id))
1287                     COALESCE(cvm.id,uvm.id),
1288                     XMLELEMENT(
1289                         name field,
1290                         XMLATTRIBUTES(
1291                             mra.attr AS name,
1292                             cvm.value AS "coded-value",
1293                             cvm.id AS "cvmid",
1294                             rad.composite,
1295                             rad.multi,
1296                             rad.filter,
1297                             rad.sorter
1298                         ),
1299                         mra.value
1300                     )
1301               FROM  metabib.record_attr_flat mra
1302                     JOIN config.record_attr_definition rad ON (mra.attr = rad.name)
1303                     LEFT JOIN config.coded_value_map cvm ON (cvm.ctype = mra.attr AND code = mra.value)
1304                     LEFT JOIN metabib.uncontrolled_record_attr_value uvm ON (uvm.attr = mra.attr AND uvm.value = mra.value)
1305               WHERE mra.id IN (
1306                     WITH aou AS (SELECT COALESCE(id, (evergreen.org_top()).id) AS id 
1307                         FROM actor.org_unit WHERE shortname = $5 LIMIT 1)
1308                     SELECT source 
1309                     FROM metabib.metarecord_source_map, aou
1310                     WHERE metarecord = $1 AND (
1311                         EXISTS (
1312                             SELECT 1 FROM asset.opac_visible_copies 
1313                             WHERE record = source AND circ_lib IN (
1314                                 SELECT id FROM actor.org_unit_descendants(aou.id, $6)) 
1315                             LIMIT 1
1316                         )
1317                         OR EXISTS (SELECT 1 FROM located_uris(source, aou.id, $10) LIMIT 1)
1318                     )
1319                 )
1320               ORDER BY 1
1321             )foo(id,y)
1322         )
1323     )
1324 $F$ LANGUAGE SQL STABLE;
1325
1326 CREATE OR REPLACE FUNCTION unapi.mmr_holdings_xml (
1327     mid BIGINT,
1328     ouid INT,
1329     org TEXT,
1330     depth INT DEFAULT NULL,
1331     includes TEXT[] DEFAULT NULL::TEXT[],
1332     slimit HSTORE DEFAULT NULL,
1333     soffset HSTORE DEFAULT NULL,
1334     include_xmlns BOOL DEFAULT TRUE,
1335     pref_lib INT DEFAULT NULL
1336 )
1337 RETURNS XML AS $F$
1338      SELECT  XMLELEMENT(
1339                  name holdings,
1340                  XMLATTRIBUTES(
1341                     CASE WHEN $8 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
1342                     CASE WHEN ('mmr' = ANY ($5)) THEN 'tag:open-ils.org:U2@mmr/' || $1 || '/' || $3 ELSE NULL END AS id,
1343                     (SELECT metarecord_has_holdable_copy FROM asset.metarecord_has_holdable_copy($1)) AS has_holdable
1344                  ),
1345                  XMLELEMENT(
1346                      name counts,
1347                      (SELECT  XMLAGG(XMLELEMENT::XML) FROM (
1348                          SELECT  XMLELEMENT(
1349                                      name count,
1350                                      XMLATTRIBUTES('public' as type, depth, org_unit, coalesce(transcendant,0) as transcendant, available, visible as count, unshadow)
1351                                  )::text
1352                            FROM  asset.opac_ou_metarecord_copy_count($2,  $1)
1353                                      UNION
1354                          SELECT  XMLELEMENT(
1355                                      name count,
1356                                      XMLATTRIBUTES('staff' as type, depth, org_unit, coalesce(transcendant,0) as transcendant, available, visible as count, unshadow)
1357                                  )::text
1358                            FROM  asset.staff_ou_metarecord_copy_count($2, $1)
1359                                      UNION
1360                          SELECT  XMLELEMENT(
1361                                      name count,
1362                                      XMLATTRIBUTES('pref_lib' as type, depth, org_unit, coalesce(transcendant,0) as transcendant, available, visible as count, unshadow)
1363                                  )::text
1364                            FROM  asset.opac_ou_metarecord_copy_count($9,  $1)
1365                                      ORDER BY 1
1366                      )x)
1367                  ),
1368                  -- XXX monograph_parts and foreign_copies are skipped in MRs ... put them back some day?
1369                  XMLELEMENT(
1370                      name volumes,
1371                      (SELECT XMLAGG(acn ORDER BY rank, name, label_sortkey) FROM (
1372                         -- Physical copies
1373                         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
1374                         FROM evergreen.ranked_volumes((SELECT ARRAY_AGG(source) FROM metabib.metarecord_source_map WHERE metarecord = $1), $2, $4, $6, $7, $9, $5) AS y
1375                         UNION ALL
1376                         -- Located URIs
1377                         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
1378                         FROM evergreen.located_uris((SELECT ARRAY_AGG(source) FROM metabib.metarecord_source_map WHERE metarecord = $1), $2, $9) AS uris
1379                      )x)
1380                  ),
1381                  CASE WHEN ('ssub' = ANY ($5)) THEN
1382                      XMLELEMENT(
1383                          name subscriptions,
1384                          (SELECT XMLAGG(ssub) FROM (
1385                             SELECT  unapi.ssub(id,'xml','subscription','{}'::TEXT[], $3, $4, $6, $7, FALSE)
1386                               FROM  serial.subscription
1387                               WHERE record_entry IN (SELECT source FROM metabib.metarecord_source_map WHERE metarecord = $1)
1388                         )x)
1389                      )
1390                  ELSE NULL END
1391              );
1392 $F$ LANGUAGE SQL STABLE;
1393
1394 CREATE OR REPLACE FUNCTION unapi.mmr (
1395     obj_id BIGINT,
1396     format TEXT,
1397     ename TEXT,
1398     includes TEXT[],
1399     org TEXT,
1400     depth INT DEFAULT NULL,
1401     slimit HSTORE DEFAULT NULL,
1402     soffset HSTORE DEFAULT NULL,
1403     include_xmlns BOOL DEFAULT TRUE,
1404     pref_lib INT DEFAULT NULL
1405 )
1406 RETURNS XML AS $F$
1407 DECLARE
1408     mmrec   metabib.metarecord%ROWTYPE;
1409     leadrec biblio.record_entry%ROWTYPE;
1410     subrec biblio.record_entry%ROWTYPE;
1411     layout  unapi.bre_output_layout%ROWTYPE;
1412     xfrm    config.xml_transform%ROWTYPE;
1413     ouid    INT;
1414     xml_buf TEXT; -- growing XML document
1415     tmp_xml TEXT; -- single-use XML string
1416     xml_frag TEXT; -- single-use XML fragment
1417     top_el  TEXT;
1418     output  XML;
1419     hxml    XML;
1420     axml    XML;
1421     subxml  XML; -- subordinate records elements
1422     sub_xpath TEXT; 
1423     parts   TEXT[]; 
1424 BEGIN
1425
1426     -- xpath for extracting bre.marc values from subordinate records 
1427     -- so they may be appended to the MARC of the master record prior
1428     -- to XSLT processing.
1429     -- subjects, isbn, issn, upc -- anything else?
1430     sub_xpath := 
1431       '//*[starts-with(@tag, "6") or @tag="020" or @tag="022" or @tag="024"]';
1432
1433     IF org = '-' OR org IS NULL THEN
1434         SELECT shortname INTO org FROM evergreen.org_top();
1435     END IF;
1436
1437     SELECT id INTO ouid FROM actor.org_unit WHERE shortname = org;
1438
1439     IF ouid IS NULL THEN
1440         RETURN NULL::XML;
1441     END IF;
1442
1443     SELECT INTO mmrec * FROM metabib.metarecord WHERE id = obj_id;
1444     IF NOT FOUND THEN
1445         RETURN NULL::XML;
1446     END IF;
1447
1448     -- TODO: aggregate holdings from constituent records
1449     IF format = 'holdings_xml' THEN -- the special case
1450         output := unapi.mmr_holdings_xml(
1451             obj_id, ouid, org, depth,
1452             evergreen.array_remove_item_by_value(includes,'holdings_xml'),
1453             slimit, soffset, include_xmlns, pref_lib);
1454         RETURN output;
1455     END IF;
1456
1457     SELECT * INTO layout FROM unapi.bre_output_layout WHERE name = format;
1458
1459     IF layout.name IS NULL THEN
1460         RETURN NULL::XML;
1461     END IF;
1462
1463     SELECT * INTO xfrm FROM config.xml_transform WHERE name = layout.transform;
1464
1465     SELECT INTO leadrec * FROM biblio.record_entry WHERE id = mmrec.master_record;
1466
1467     -- Grab distinct MVF for all records if requested
1468     IF ('mra' = ANY (includes)) THEN 
1469         axml := unapi.mmr_mra(obj_id,NULL,NULL,NULL,org,depth,NULL,NULL,TRUE,pref_lib);
1470     ELSE
1471         axml := NULL::XML;
1472     END IF;
1473
1474     xml_buf = leadrec.marc;
1475
1476     hxml := NULL::XML;
1477     IF ('holdings_xml' = ANY (includes)) THEN
1478         hxml := unapi.mmr_holdings_xml(
1479                     obj_id, ouid, org, depth,
1480                     evergreen.array_remove_item_by_value(includes,'holdings_xml'),
1481                     slimit, soffset, include_xmlns, pref_lib);
1482     END IF;
1483
1484     subxml := NULL::XML;
1485     parts := '{}'::TEXT[];
1486     FOR subrec IN SELECT bre.* FROM biblio.record_entry bre
1487          JOIN metabib.metarecord_source_map mmsm ON (mmsm.source = bre.id)
1488          JOIN metabib.metarecord mmr ON (mmr.id = mmsm.metarecord)
1489          WHERE mmr.id = obj_id AND NOT bre.deleted
1490          ORDER BY CASE WHEN bre.id = mmr.master_record THEN 0 ELSE bre.id END
1491          LIMIT COALESCE((slimit->'bre')::INT, 5) LOOP
1492
1493         IF subrec.id = leadrec.id THEN CONTINUE; END IF;
1494         -- Append choice data from the the non-lead records to the 
1495         -- the lead record document
1496
1497         parts := parts || xpath(sub_xpath, subrec.marc::XML)::TEXT[];
1498     END LOOP;
1499
1500     SELECT ARRAY_TO_STRING( ARRAY_AGG( DISTINCT p ), '' )::XML INTO subxml FROM UNNEST(parts) p;
1501
1502     -- append data from the subordinate records to the 
1503     -- main record document before applying the XSLT
1504
1505     IF subxml IS NOT NULL THEN 
1506         xml_buf := REGEXP_REPLACE(xml_buf, 
1507             '</record>(.*?)$', subxml || '</record>' || E'\\1');
1508     END IF;
1509
1510     IF format = 'marcxml' THEN
1511          -- If we're not using the prefixed namespace in 
1512          -- this record, then remove all declarations of it
1513         IF xml_buf !~ E'<marc:' THEN
1514            xml_buf := REGEXP_REPLACE(xml_buf, 
1515             ' xmlns:marc="http://www.loc.gov/MARC21/slim"', '', 'g');
1516         END IF; 
1517     ELSE
1518         xml_buf := oils_xslt_process(xml_buf, xfrm.xslt)::XML;
1519     END IF;
1520
1521     -- update top_el to reflect the change in xml_buf, which may
1522     -- now be a different type of document (e.g. record -> mods)
1523     top_el := REGEXP_REPLACE(xml_buf, E'^.*?<((?:\\S+:)?' || 
1524         layout.holdings_element || ').*$', E'\\1');
1525
1526     IF axml IS NOT NULL THEN 
1527         xml_buf := REGEXP_REPLACE(xml_buf, 
1528             '</' || top_el || '>(.*?)$', axml || '</' || top_el || E'>\\1');
1529     END IF;
1530
1531     IF hxml IS NOT NULL THEN
1532         xml_buf := REGEXP_REPLACE(xml_buf, 
1533             '</' || top_el || '>(.*?)$', hxml || '</' || top_el || E'>\\1');
1534     END IF;
1535
1536     IF ('mmr.unapi' = ANY (includes)) THEN 
1537         output := REGEXP_REPLACE(
1538             xml_buf,
1539             '</' || top_el || '>(.*?)',
1540             XMLELEMENT(
1541                 name abbr,
1542                 XMLATTRIBUTES(
1543                     'http://www.w3.org/1999/xhtml' AS xmlns,
1544                     'unapi-id' AS class,
1545                     'tag:open-ils.org:U2@mmr/' || obj_id || '/' || org AS title
1546                 )
1547             )::TEXT || '</' || top_el || E'>\\1'
1548         );
1549     ELSE
1550         output := xml_buf;
1551     END IF;
1552
1553     -- remove ignorable whitesace
1554     output := REGEXP_REPLACE(output::TEXT,E'>\\s+<','><','gs')::XML;
1555     RETURN output;
1556 END;
1557 $F$ LANGUAGE PLPGSQL STABLE;
1558
1559
1560 /*
1561
1562  -- Some test queries
1563
1564 SELECT unapi.memoize( 'bre', 1,'mods32','','{holdings_xml,acp}'::TEXT[], 'SYS1');
1565 SELECT unapi.memoize( 'bre', 1,'marcxml','','{holdings_xml,acp}'::TEXT[], 'SYS1');
1566 SELECT unapi.memoize( 'bre', 1,'holdings_xml','','{holdings_xml,acp}'::TEXT[], 'SYS1');
1567
1568 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>');
1569
1570 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>');
1571 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>');
1572 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>');
1573 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>');
1574
1575 SELECT unapi.biblio_record_entry_feed('{216}'::BIGINT[],'marcxml','{}'::TEXT[], 'BR1');
1576 EXPLAIN ANALYZE SELECT unapi.bre(216,'marcxml','record','{holdings_xml,bre.unapi}'::TEXT[], 'BR1');
1577 EXPLAIN ANALYZE SELECT unapi.bre(216,'holdings_xml','record','{}'::TEXT[], 'BR1');
1578 EXPLAIN ANALYZE SELECT unapi.holdings_xml(216,4,'BR1',2,'{bre}'::TEXT[]);
1579 EXPLAIN ANALYZE SELECT unapi.bre(216,'mods32','record','{}'::TEXT[], 'BR1');
1580
1581 -- Limit to 5 call numbers, 5 copies, with a preferred library of 4 (BR1), in SYS2 at a depth of 0
1582 EXPLAIN ANALYZE SELECT unapi.bre(36,'marcxml','record','{holdings_xml,mra,acp,acnp,acns,bmp}','SYS2',0,'acn=>5,acp=>5',NULL,TRUE,4);
1583
1584 */
1585
1586 COMMIT;
1587