]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/src/sql/Pg/990.schema.unapi.sql
Add support for Multi-Homed Items (aka Foreign Bibs, aka Linked Items)
[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.array_remove_item_by_value(inp ANYARRAY, el ANYELEMENT) RETURNS anyarray AS $$ SELECT ARRAY_ACCUM(x.e) FROM UNNEST( $1 ) x(e) WHERE x.e <> $2; $$ LANGUAGE SQL;
7
8 CREATE TABLE unapi.bre_output_layout (
9     name                TEXT    PRIMARY KEY,
10     transform           TEXT    REFERENCES config.xml_transform (name) ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE INITIALLY DEFERRED,
11     mime_type           TEXT    NOT NULL,
12     feed_top            TEXT    NOT NULL,
13     holdings_element    TEXT,
14     title_element       TEXT,
15     description_element TEXT,
16     creator_element     TEXT,
17     update_ts_element   TEXT
18 );
19
20 INSERT INTO unapi.bre_output_layout
21     (name,           transform, mime_type,              holdings_element, feed_top,         title_element, description_element, creator_element, update_ts_element)
22         VALUES
23     ('holdings_xml', NULL,      'application/xml',      NULL,             'hxml',           NULL,          NULL,                NULL,            NULL),
24     ('marcxml',      'marcxml', 'application/marc+xml', 'record',         'collection',     NULL,          NULL,                NULL,            NULL),
25     ('mods32',       'mods32',  'application/mods+xml', 'mods',           'modsCollection', NULL,          NULL,                NULL,            NULL)
26 ;
27
28 -- Dummy functions, so we can create the real ones out of order
29 CREATE OR REPLACE FUNCTION unapi.aou    ( obj_id BIGINT, format TEXT, ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit INT DEFAULT NULL, soffset INT DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$ SELECT NULL::XML $F$ LANGUAGE SQL;
30 CREATE OR REPLACE FUNCTION unapi.acnp   ( obj_id BIGINT, format TEXT, ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit INT DEFAULT NULL, soffset INT DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$ SELECT NULL::XML $F$ LANGUAGE SQL;
31 CREATE OR REPLACE FUNCTION unapi.acns   ( obj_id BIGINT, format TEXT, ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit INT DEFAULT NULL, soffset INT DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$ SELECT NULL::XML $F$ LANGUAGE SQL;
32 CREATE OR REPLACE FUNCTION unapi.acn    ( obj_id BIGINT, format TEXT, ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit INT DEFAULT NULL, soffset INT DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$ SELECT NULL::XML $F$ LANGUAGE SQL;
33 CREATE OR REPLACE FUNCTION unapi.ssub   ( obj_id BIGINT, format TEXT, ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit INT DEFAULT NULL, soffset INT DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$ SELECT NULL::XML $F$ LANGUAGE SQL;
34 CREATE OR REPLACE FUNCTION unapi.sdist  ( obj_id BIGINT, format TEXT, ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit INT DEFAULT NULL, soffset INT DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$ SELECT NULL::XML $F$ LANGUAGE SQL;
35 CREATE OR REPLACE FUNCTION unapi.sstr   ( obj_id BIGINT, format TEXT, ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit INT DEFAULT NULL, soffset INT DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$ SELECT NULL::XML $F$ LANGUAGE SQL;
36 CREATE OR REPLACE FUNCTION unapi.sitem  ( obj_id BIGINT, format TEXT, ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit INT DEFAULT NULL, soffset INT DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$ SELECT NULL::XML $F$ LANGUAGE SQL;
37 CREATE OR REPLACE FUNCTION unapi.sunit  ( obj_id BIGINT, format TEXT, ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit INT DEFAULT NULL, soffset INT DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$ SELECT NULL::XML $F$ LANGUAGE SQL;
38 CREATE OR REPLACE FUNCTION unapi.sisum  ( obj_id BIGINT, format TEXT, ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit INT DEFAULT NULL, soffset INT DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$ SELECT NULL::XML $F$ LANGUAGE SQL;
39 CREATE OR REPLACE FUNCTION unapi.sbsum  ( obj_id BIGINT, format TEXT, ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit INT DEFAULT NULL, soffset INT DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$ SELECT NULL::XML $F$ LANGUAGE SQL;
40 CREATE OR REPLACE FUNCTION unapi.sssum  ( obj_id BIGINT, format TEXT, ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit INT DEFAULT NULL, soffset INT DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$ SELECT NULL::XML $F$ LANGUAGE SQL;
41 CREATE OR REPLACE FUNCTION unapi.siss   ( obj_id BIGINT, format TEXT, ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit INT DEFAULT NULL, soffset INT DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$ SELECT NULL::XML $F$ LANGUAGE SQL;
42 CREATE OR REPLACE FUNCTION unapi.auri   ( obj_id BIGINT, format TEXT, ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit INT DEFAULT NULL, soffset INT DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$ SELECT NULL::XML $F$ LANGUAGE SQL;
43 CREATE OR REPLACE FUNCTION unapi.acp    ( obj_id BIGINT, format TEXT, ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit INT DEFAULT NULL, soffset INT DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$ SELECT NULL::XML $F$ LANGUAGE SQL;
44 CREATE OR REPLACE FUNCTION unapi.acpn   ( obj_id BIGINT, format TEXT, ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit INT DEFAULT NULL, soffset INT DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$ SELECT NULL::XML $F$ LANGUAGE SQL;
45 CREATE OR REPLACE FUNCTION unapi.acl    ( obj_id BIGINT, format TEXT, ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit INT DEFAULT NULL, soffset INT DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$ SELECT NULL::XML $F$ LANGUAGE SQL;
46 CREATE OR REPLACE FUNCTION unapi.ccs    ( obj_id BIGINT, format TEXT, ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit INT DEFAULT NULL, soffset INT DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$ SELECT NULL::XML $F$ LANGUAGE SQL;
47 CREATE OR REPLACE FUNCTION unapi.ascecm ( obj_id BIGINT, format TEXT, ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit INT DEFAULT NULL, soffset INT DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$ SELECT NULL::XML $F$ LANGUAGE SQL;
48 CREATE OR REPLACE FUNCTION unapi.bre    ( obj_id BIGINT, format TEXT, ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit INT DEFAULT NULL, soffset INT DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$ SELECT NULL::XML $F$ LANGUAGE SQL;
49 CREATE OR REPLACE FUNCTION unapi.bmp    ( obj_id BIGINT, format TEXT, ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit INT DEFAULT NULL, soffset INT DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$ SELECT NULL::XML $F$ LANGUAGE SQL;
50
51 CREATE OR REPLACE FUNCTION unapi.holdings_xml ( bid BIGINT, ouid INT, org TEXT, depth INT DEFAULT NULL, includes TEXT[] DEFAULT NULL::TEXT[], slimit INT DEFAULT NULL, soffset INT DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE) RETURNS XML AS $F$ SELECT NULL::XML $F$ LANGUAGE SQL;
52 CREATE OR REPLACE FUNCTION unapi.biblio_record_entry_feed ( id_list BIGINT[], format TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit INT DEFAULT NULL, soffset INT 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;
53
54 CREATE OR REPLACE FUNCTION unapi.memoize (classname TEXT, obj_id BIGINT, format TEXT,  ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit INT DEFAULT NULL, soffset INT DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$
55 DECLARE
56     key     TEXT;
57     output  XML;
58 BEGIN
59     key :=
60         'id'        || COALESCE(obj_id::TEXT,'') ||
61         'format'    || COALESCE(format::TEXT,'') ||
62         'ename'     || COALESCE(ename::TEXT,'') ||
63         'includes'  || COALESCE(includes::TEXT,'{}'::TEXT[]::TEXT) ||
64         'org'       || COALESCE(org::TEXT,'') ||
65         'depth'     || COALESCE(depth::TEXT,'') ||
66         'slimit'    || COALESCE(slimit::TEXT,'') ||
67         'soffset'   || COALESCE(soffset::TEXT,'') ||
68         'include_xmlns'   || COALESCE(include_xmlns::TEXT,'');
69     -- RAISE NOTICE 'memoize key: %', key;
70
71     key := MD5(key);
72     -- RAISE NOTICE 'memoize hash: %', key;
73
74     -- XXX cache logic ... memcached? table?
75
76     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;
77     RETURN output;
78 END;
79 $F$ LANGUAGE PLPGSQL;
80
81 CREATE OR REPLACE FUNCTION unapi.biblio_record_entry_feed ( id_list BIGINT[], format TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit INT DEFAULT NULL, soffset INT 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$
82 DECLARE
83     layout          unapi.bre_output_layout%ROWTYPE;
84     transform       config.xml_transform%ROWTYPE;
85     item_format     TEXT;
86     tmp_xml         TEXT;
87     xmlns_uri       TEXT := 'http://open-ils.org/spec/feed-xml/v1';
88     ouid            INT;
89     element_list    TEXT[];
90 BEGIN
91
92     SELECT id INTO ouid FROM actor.org_unit WHERE shortname = org;
93     SELECT * INTO layout FROM unapi.bre_output_layout WHERE name = format;
94
95     IF layout.name IS NULL THEN
96         RETURN NULL::XML;
97     END IF;
98
99     SELECT * INTO transform FROM config.xml_transform WHERE name = layout.transform;
100     xmlns_uri := COALESCE(transform.namespace_uri,xmlns_uri);
101
102     -- Gather the bib xml
103     SELECT XMLAGG( unapi.bre(i, format, '', includes, org, depth, slimit, soffset, include_xmlns)) INTO tmp_xml FROM UNNEST( id_list ) i;
104
105     IF layout.title_element IS NOT NULL THEN
106         EXECUTE 'SELECT XMLCONCAT( XMLELEMENT( name '|| layout.title_element ||', CASE WHEN $4 THEN XMLATTRIBUTES( $1 AS xmlns) ELSE NULL END, $3), $2)' INTO tmp_xml USING xmlns_uri, tmp_xml::XML, title, include_xmlns;
107     END IF;
108
109     IF layout.description_element IS NOT NULL THEN
110         EXECUTE 'SELECT XMLCONCAT( XMLELEMENT( name '|| layout.description_element ||', CASE WHEN $4 THEN XMLATTRIBUTES( $1 AS xmlns) ELSE NULL END, $3), $2)' INTO tmp_xml USING xmlns_uri, tmp_xml::XML, description, include_xmlns;
111     END IF;
112
113     IF layout.creator_element IS NOT NULL THEN
114         EXECUTE 'SELECT XMLCONCAT( XMLELEMENT( name '|| layout.creator_element ||', CASE WHEN $4 THEN XMLATTRIBUTES( $1 AS xmlns) ELSE NULL END, $3), $2)' INTO tmp_xml USING xmlns_uri, tmp_xml::XML, creator, include_xmlns;
115     END IF;
116
117     IF layout.update_ts_element IS NOT NULL THEN
118         EXECUTE 'SELECT XMLCONCAT( XMLELEMENT( name '|| layout.update_ts_element ||', CASE WHEN $4 THEN XMLATTRIBUTES( $1 AS xmlns) ELSE NULL END, $3), $2)' INTO tmp_xml USING xmlns_uri, tmp_xml::XML, update_ts, include_xmlns;
119     END IF;
120
121     IF unapi_url IS NOT NULL THEN
122         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;
123     END IF;
124
125     IF header_xml IS NOT NULL THEN tmp_xml := XMLCONCAT(header_xml,tmp_xml::XML); END IF;
126
127     element_list := regexp_split_to_array(layout.feed_top,E'\\.');
128     FOR i IN REVERSE ARRAY_UPPER(element_list, 1) .. 1 LOOP
129         EXECUTE 'SELECT XMLELEMENT( name '|| quote_ident(element_list[i]) ||', CASE WHEN $4 THEN XMLATTRIBUTES( $1 AS xmlns) ELSE NULL END, $2)' INTO tmp_xml USING xmlns_uri, tmp_xml::XML, include_xmlns;
130     END LOOP;
131
132     RETURN tmp_xml::XML;
133 END;
134 $F$ LANGUAGE PLPGSQL;
135
136 CREATE OR REPLACE FUNCTION unapi.bre ( obj_id BIGINT, format TEXT,  ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit INT DEFAULT NULL, soffset INT DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$
137 DECLARE
138     me      biblio.record_entry%ROWTYPE;
139     layout  unapi.bre_output_layout%ROWTYPE;
140     xfrm    config.xml_transform%ROWTYPE;
141     ouid    INT;
142     tmp_xml TEXT;
143     top_el  TEXT;
144     output  XML;
145     hxml    XML;
146 BEGIN
147
148     SELECT id INTO ouid FROM actor.org_unit WHERE shortname = org;
149
150     IF ouid IS NULL THEN
151         RETURN NULL::XML;
152     END IF;
153
154     IF format = 'holdings_xml' THEN -- the special case
155         output := unapi.holdings_xml( obj_id, ouid, org, depth, includes, slimit, soffset, include_xmlns);
156         RETURN output;
157     END IF;
158
159     SELECT * INTO layout FROM unapi.bre_output_layout WHERE name = format;
160
161     IF layout.name IS NULL THEN
162         RETURN NULL::XML;
163     END IF;
164
165     SELECT * INTO xfrm FROM config.xml_transform WHERE name = layout.transform;
166
167     SELECT * INTO me FROM biblio.record_entry WHERE id = obj_id;
168
169     -- grab hodlings if we need them
170     IF ('holdings_xml' = ANY (includes)) THEN 
171         hxml := unapi.holdings_xml(obj_id, ouid, org, depth, evergreen.array_remove_item_by_value(includes,'holdings_xml'), slimit, soffset, include_xmlns);
172     ELSE
173         hxml := NULL::XML;
174     END IF;
175
176
177     -- generate our item node
178
179
180     IF format = 'marcxml' THEN
181         tmp_xml := me.marc;
182         IF tmp_xml !~ E'<marc:' THEN -- If we're not using the prefixed namespace in this record, then remove all declarations of it
183            tmp_xml := REGEXP_REPLACE(tmp_xml, ' xmlns:marc="http://www.loc.gov/MARC21/slim"', '', 'g');
184         END IF; 
185     ELSE
186         tmp_xml := oils_xslt_process(me.marc, xfrm.xslt)::XML;
187     END IF;
188
189     top_el := REGEXP_REPLACE(tmp_xml, E'^.*?<((?:\\S+:)?' || layout.holdings_element || ').*$', E'\\1');
190
191     IF hxml IS NOT NULL THEN -- XXX how do we configure the holdings position?
192         tmp_xml := REGEXP_REPLACE(tmp_xml, '</' || top_el || '>(.*?)$', hxml || '</' || top_el || E'>\\1');
193     END IF;
194
195     IF ('bre.unapi' = ANY (includes)) THEN 
196         output := REGEXP_REPLACE(
197             tmp_xml,
198             '</' || top_el || '>(.*?)',
199             XMLELEMENT(
200                 name abbr,
201                 XMLATTRIBUTES(
202                     'http://www.w3.org/1999/xhtml' AS xmlns,
203                     'unapi-id' AS class,
204                     'tag:open-ils.org:U2@bre/' || obj_id || '/' || org AS title
205                 )
206             )::TEXT || '</' || top_el || E'>\\1'
207         );
208     ELSE
209         output := tmp_xml;
210     END IF;
211
212     RETURN output;
213 END;
214 $F$ LANGUAGE PLPGSQL;
215
216 CREATE OR REPLACE FUNCTION unapi.holdings_xml (bid BIGINT, ouid INT, org TEXT, depth INT DEFAULT NULL, includes TEXT[] DEFAULT NULL::TEXT[], slimit INT DEFAULT NULL, soffset INT DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE) RETURNS XML AS $F$
217      SELECT  XMLELEMENT(
218                  name holdings,
219                  XMLATTRIBUTES(
220                     CASE WHEN $8 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
221                     CASE WHEN ('bre' = ANY ('{acn,auri}'::TEXT[] || $5)) THEN 'tag:open-ils.org:U2@bre/' || $1 || '/' || $3 ELSE NULL END AS id
222                  ),
223                  XMLELEMENT(
224                      name counts,
225                      (SELECT  XMLAGG(XMLELEMENT::XML) FROM (
226                          SELECT  XMLELEMENT(
227                                      name count,
228                                      XMLATTRIBUTES('public' as type, depth, org_unit, coalesce(transcendant,0) as transcendant, available, visible as count, unshadow)
229                                  )::text
230                            FROM  asset.opac_ou_record_copy_count($2,  $1)
231                                      UNION
232                          SELECT  XMLELEMENT(
233                                      name count,
234                                      XMLATTRIBUTES('staff' as type, depth, org_unit, coalesce(transcendant,0) as transcendant, available, visible as count, unshadow)
235                                  )::text
236                            FROM  asset.staff_ou_record_copy_count($2, $1)
237                                      ORDER BY 1
238                      )x)
239                  ),
240                  CASE 
241                      WHEN ('bmp' = ANY ($5)) THEN
242                         XMLELEMENT( name monograph_parts,
243                             XMLAGG((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) FROM biblio.monograph_part WHERE record = $1))
244                         )
245                      ELSE NULL
246                  END,
247                  CASE WHEN ('acn' = ANY ('{acn,auri}'::TEXT[] || $5)) THEN 
248                      XMLELEMENT(
249                          name volumes,
250                          (SELECT XMLAGG(acn) FROM (
251                             SELECT  unapi.acn(acn.id,'xml','volume',array_remove_item_by_value( evergreen.array_remove_item_by_value('{acn,auri}'::TEXT[] || $5,'holdings_xml'),'bre'), $3, $4, $6, $7, FALSE)
252                               FROM  asset.call_number acn
253                               WHERE acn.record = $1
254                                     AND EXISTS (
255                                         SELECT  1
256                                           FROM  asset.copy acp
257                                                 JOIN actor.org_unit_descendants(
258                                                     $2,
259                                                     (COALESCE(
260                                                         $4,
261                                                         (SELECT aout.depth
262                                                           FROM  actor.org_unit_type aout
263                                                                 JOIN actor.org_unit aou ON (aou.ou_type = aout.id AND aou.id = $2)
264                                                         )
265                                                     ))
266                                                 ) aoud ON (acp.circ_lib = aoud.id)
267                                           LIMIT 1
268                                     )
269                               ORDER BY label_sortkey
270                               LIMIT $6
271                               OFFSET $7
272                          )x)
273                      )
274                  ELSE NULL END,
275                  CASE WHEN ('ssub' = ANY ('{acn,auri}'::TEXT[] || $5)) THEN 
276                      XMLELEMENT(
277                          name subscriptions,
278                          (SELECT XMLAGG(ssub) FROM (
279                             SELECT  unapi.ssub(id,'xml','subscription','{}'::TEXT[], $3, $4, $6, $7, FALSE)
280                               FROM  serial.subscription
281                               WHERE record_entry = $1
282                         )x)
283                      )
284                  ELSE NULL END,
285                  CASE WHEN ('acp' = ANY ($5)) THEN 
286                      XMLELEMENT(
287                          name foreign_copies,
288                          (SELECT XMLAGG(acp) FROM (
289                             SELECT  unapi.acp(p.target_copy,'xml','copy','{}'::TEXT[], $3, $4, $6, $7, FALSE)
290                               FROM  biblio.peer_bib_copy_map p
291                                     JOIN asset.copy c ON (p.target_copy = c.id)
292                               WHERE NOT c.deleted AND peer_record = $1
293                         )x)
294                      )
295                  ELSE NULL END
296              );
297 $F$ LANGUAGE SQL;
298
299 CREATE OR REPLACE FUNCTION unapi.ssub ( obj_id BIGINT, format TEXT,  ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit INT DEFAULT NULL, soffset INT DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$
300         SELECT  XMLELEMENT(
301                     name subscription,
302                     XMLATTRIBUTES(
303                         CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
304                         'tag:open-ils.org:U2@ssub/' || id AS id,
305                         start_date AS start, end_date AS end, expected_date_offset
306                     ),
307                     unapi.aou( owning_lib, $2, 'owning_lib', evergreen.array_remove_item_by_value($4,'ssub'), $5, $6, $7, $8),
308                     XMLELEMENT( name distributions,
309                         CASE 
310                             WHEN ('sdist' = ANY ($4)) THEN
311                                 XMLAGG((SELECT unapi.sdist( id, 'xml', 'distribution', evergreen.array_remove_item_by_value($4,'ssub'), $5, $6, $7, $8, FALSE) FROM serial.distribution WHERE subscription = ssub.id))
312                             ELSE NULL
313                         END
314                     )
315                 )
316           FROM  serial.subscription ssub
317           WHERE id = $1
318           GROUP BY id, start_date, end_date, expected_date_offset, owning_lib;
319 $F$ LANGUAGE SQL;
320
321 CREATE OR REPLACE FUNCTION unapi.sdist ( obj_id BIGINT, format TEXT,  ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit INT DEFAULT NULL, soffset INT DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$
322         SELECT  XMLELEMENT(
323                     name distribution,
324                     XMLATTRIBUTES(
325                         CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
326                         'tag:open-ils.org:U2@sdist/' || id AS id,
327                         'tag:open-ils.org:U2@acn/' || receive_call_number AS receive_call_number,
328                         'tag:open-ils.org:U2@acn/' || bind_call_number AS bind_call_number,
329                         unit_label_prefix, label, unit_label_suffix, summary_method
330                     ),
331                     unapi.aou( holding_lib, $2, 'holding_lib', evergreen.array_remove_item_by_value($4,'sdist'), $5, $6, $7, $8),
332                     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,
333                     XMLELEMENT( name streams,
334                         CASE 
335                             WHEN ('sstr' = ANY ($4)) THEN
336                                 XMLAGG((SELECT unapi.sstr( id, 'xml', 'stream', evergreen.array_remove_item_by_value($4,'sdist'), $5, $6, $7, $8, FALSE) FROM serial.stream WHERE distribution = sdist.id))
337                             ELSE NULL
338                         END
339                     ),
340                     XMLELEMENT( name summaries,
341                         CASE 
342                             WHEN ('ssum' = ANY ($4)) THEN
343                                 XMLAGG((SELECT unapi.sbsum( id, 'xml', 'serial_summary', evergreen.array_remove_item_by_value($4,'sdist'), $5, $6, $7, $8, FALSE) FROM serial.basic_summary WHERE distribution = sdist.id))
344                             ELSE NULL
345                         END,
346                         CASE 
347                             WHEN ('ssum' = ANY ($4)) THEN
348                                 XMLAGG((SELECT unapi.sisum( id, 'xml', 'serial_summary', evergreen.array_remove_item_by_value($4,'sdist'), $5, $6, $7, $8, FALSE) FROM serial.index_summary WHERE distribution = sdist.id))
349                             ELSE NULL
350                         END,
351                         CASE 
352                             WHEN ('ssum' = ANY ($4)) THEN
353                                 XMLAGG((SELECT unapi.sssum( id, 'xml', 'serial_summary', evergreen.array_remove_item_by_value($4,'sdist'), $5, $6, $7, $8, FALSE) FROM serial.supplement_summary WHERE distribution = sdist.id))
354                             ELSE NULL
355                         END
356                     )
357                 )
358           FROM  serial.distribution sdist
359           WHERE id = $1
360           GROUP BY id, label, unit_label_prefix, unit_label_suffix, holding_lib, summary_method, subscription, receive_call_number, bind_call_number;
361 $F$ LANGUAGE SQL;
362
363 CREATE OR REPLACE FUNCTION unapi.sstr ( obj_id BIGINT, format TEXT,  ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit INT DEFAULT NULL, soffset INT DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$
364     SELECT  XMLELEMENT(
365                 name stream,
366                 XMLATTRIBUTES(
367                     CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
368                     'tag:open-ils.org:U2@sstr/' || id AS id,
369                     routing_label
370                 ),
371                 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,
372                 XMLELEMENT( name items,
373                     CASE 
374                         WHEN ('sitem' = ANY ($4)) THEN
375                             XMLAGG((SELECT unapi.sitem( id, 'xml', 'serial_item', evergreen.array_remove_item_by_value($4,'sstr'), $5, $6, $7, $8, FALSE) FROM serial.item WHERE stream = sstr.id))
376                         ELSE NULL
377                     END
378                 )
379             )
380       FROM  serial.stream sstr
381       WHERE id = $1
382       GROUP BY id, routing_label, distribution;
383 $F$ LANGUAGE SQL;
384
385 CREATE OR REPLACE FUNCTION unapi.siss ( obj_id BIGINT, format TEXT,  ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit INT DEFAULT NULL, soffset INT DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$
386     SELECT  XMLELEMENT(
387                 name issuance,
388                 XMLATTRIBUTES(
389                     CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
390                     'tag:open-ils.org:U2@siss/' || id AS id,
391                     create_date, edit_date, label, date_published,
392                     holding_code, holding_type, holding_link_id
393                 ),
394                 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,
395                 XMLELEMENT( name items,
396                     CASE 
397                         WHEN ('sitem' = ANY ($4)) THEN
398                             XMLAGG((SELECT unapi.sitem( id, 'xml', 'serial_item', evergreen.array_remove_item_by_value($4,'siss'), $5, $6, $7, $8, FALSE) FROM serial.item WHERE issuance = sstr.id))
399                         ELSE NULL
400                     END
401                 )
402             )
403       FROM  serial.issuance sstr
404       WHERE id = $1
405       GROUP BY id, create_date, edit_date, label, date_published, holding_code, holding_type, holding_link_id, subscription;
406 $F$ LANGUAGE SQL;
407
408 CREATE OR REPLACE FUNCTION unapi.sitem ( obj_id BIGINT, format TEXT,  ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit INT DEFAULT NULL, soffset INT DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$
409         SELECT  XMLELEMENT(
410                     name serial_item,
411                     XMLATTRIBUTES(
412                         CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
413                         'tag:open-ils.org:U2@sitem/' || id AS id,
414                         'tag:open-ils.org:U2@siss/' || issuance AS issuance,
415                         date_expected, date_received
416                     ),
417                     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,
418                     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,
419                     CASE WHEN unit IS NOT NULL AND ('sunit' = ANY ($4)) THEN unapi.sunit( stream, $2, 'serial_unit', evergreen.array_remove_item_by_value($4,'sitem'), $5, $6, $7, $8, FALSE) ELSE NULL END,
420                     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
421 --                    XMLELEMENT( name notes,
422 --                        CASE 
423 --                            WHEN ('acpn' = ANY ($4)) THEN
424 --                                XMLAGG((SELECT unapi.acpn( id, 'xml', 'copy_note', evergreen.array_remove_item_by_value($4,'acp'), $5, $6, $7, $8) FROM asset.copy_note WHERE owning_copy = cp.id AND pub))
425 --                            ELSE NULL
426 --                        END
427 --                    )
428                 )
429           FROM  serial.item sitem
430           WHERE id = $1;
431 $F$ LANGUAGE SQL;
432
433
434 CREATE OR REPLACE FUNCTION unapi.sssum ( obj_id BIGINT, format TEXT,  ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit INT DEFAULT NULL, soffset INT DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$
435     SELECT  XMLELEMENT(
436                 name serial_summary,
437                 XMLATTRIBUTES(
438                     CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
439                     'tag:open-ils.org:U2@sbsum/' || id AS id,
440                     'sssum' AS type, generated_coverage, textual_holdings, show_generated
441                 ),
442                 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
443             )
444       FROM  serial.supplement_summary ssum
445       WHERE id = $1
446       GROUP BY id, generated_coverage, textual_holdings, distribution, show_generated;
447 $F$ LANGUAGE SQL;
448
449 CREATE OR REPLACE FUNCTION unapi.sbsum ( obj_id BIGINT, format TEXT,  ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit INT DEFAULT NULL, soffset INT DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$
450     SELECT  XMLELEMENT(
451                 name serial_summary,
452                 XMLATTRIBUTES(
453                     CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
454                     'tag:open-ils.org:U2@sbsum/' || id AS id,
455                     'sbsum' AS type, generated_coverage, textual_holdings, show_generated
456                 ),
457                 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
458             )
459       FROM  serial.basic_summary ssum
460       WHERE id = $1
461       GROUP BY id, generated_coverage, textual_holdings, distribution, show_generated;
462 $F$ LANGUAGE SQL;
463
464 CREATE OR REPLACE FUNCTION unapi.sisum ( obj_id BIGINT, format TEXT,  ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit INT DEFAULT NULL, soffset INT DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$
465     SELECT  XMLELEMENT(
466                 name serial_summary,
467                 XMLATTRIBUTES(
468                     CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
469                     'tag:open-ils.org:U2@sbsum/' || id AS id,
470                     'sisum' AS type, generated_coverage, textual_holdings, show_generated
471                 ),
472                 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
473             )
474       FROM  serial.index_summary ssum
475       WHERE id = $1
476       GROUP BY id, generated_coverage, textual_holdings, distribution, show_generated;
477 $F$ LANGUAGE SQL;
478
479
480 CREATE OR REPLACE FUNCTION unapi.aou ( obj_id BIGINT, format TEXT,  ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit INT DEFAULT NULL, soffset INT DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$
481 DECLARE
482     output XML;
483 BEGIN
484     IF ename = 'circlib' THEN
485         SELECT  XMLELEMENT(
486                     name circlib,
487                     XMLATTRIBUTES(
488                         'http://open-ils.org/spec/actors/v1' AS xmlns,
489                         id AS ident
490                     ),
491                     name
492                 ) INTO output
493           FROM  actor.org_unit aou
494           WHERE id = obj_id;
495     ELSE
496         EXECUTE $$SELECT  XMLELEMENT(
497                     name $$ || ename || $$,
498                     XMLATTRIBUTES(
499                         'http://open-ils.org/spec/actors/v1' AS xmlns,
500                         'tag:open-ils.org:U2@aou/' || id AS id,
501                         shortname, name, opac_visible
502                     )
503                 )
504           FROM  actor.org_unit aou
505          WHERE id = $1 $$ INTO output USING obj_id;
506     END IF;
507
508     RETURN output;
509
510 END;
511 $F$ LANGUAGE PLPGSQL;
512
513 CREATE OR REPLACE FUNCTION unapi.acl ( obj_id BIGINT, format TEXT,  ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit INT DEFAULT NULL, soffset INT DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$
514     SELECT  XMLELEMENT(
515                 name location,
516                 XMLATTRIBUTES(
517                     CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
518                     id AS ident
519                 ),
520                 name
521             )
522       FROM  asset.copy_location
523       WHERE id = $1;
524 $F$ LANGUAGE SQL;
525
526 CREATE OR REPLACE FUNCTION unapi.ccs ( obj_id BIGINT, format TEXT,  ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit INT DEFAULT NULL, soffset INT DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$
527     SELECT  XMLELEMENT(
528                 name status,
529                 XMLATTRIBUTES(
530                     CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
531                     id AS ident
532                 ),
533                 name
534             )
535       FROM  config.copy_status
536       WHERE id = $1;
537 $F$ LANGUAGE SQL;
538
539 CREATE OR REPLACE FUNCTION unapi.acpn ( obj_id BIGINT, format TEXT,  ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit INT DEFAULT NULL, soffset INT DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$
540         SELECT  XMLELEMENT(
541                     name copy_note,
542                     XMLATTRIBUTES(
543                         CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
544                         create_date AS date,
545                         title
546                     ),
547                     value
548                 )
549           FROM  asset.copy_note
550           WHERE id = $1;
551 $F$ LANGUAGE SQL;
552
553 CREATE OR REPLACE FUNCTION unapi.ascecm ( obj_id BIGINT, format TEXT,  ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit INT DEFAULT NULL, soffset INT DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$
554         SELECT  XMLELEMENT(
555                     name statcat,
556                     XMLATTRIBUTES(
557                         CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
558                         sc.name,
559                         sc.opac_visible
560                     ),
561                     asce.value
562                 )
563           FROM  asset.stat_cat_entry asce
564                 JOIN asset.stat_cat sc ON (sc.id = asce.stat_cat)
565           WHERE asce.id = $1;
566 $F$ LANGUAGE SQL;
567
568 CREATE OR REPLACE FUNCTION unapi.bmp ( obj_id BIGINT, format TEXT,  ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit INT DEFAULT NULL, soffset INT DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$
569         SELECT  XMLELEMENT(
570                     name monograph_part,
571                     XMLATTRIBUTES(
572                         CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
573                         'tag:open-ils.org:U2@bmp/' || id AS id,
574                         id AS ident,
575                         label,
576                         label_sortkey,
577                         'tag:open-ils.org:U2@bre/' || record AS record
578                     ),
579                     CASE 
580                         WHEN ('acp' = ANY ($4)) THEN
581                             XMLELEMENT( name copies,
582                                 (SELECT XMLAGG(acp) FROM (
583                                     SELECT  unapi.acp( cp.id, 'xml', 'copy', evergreen.array_remove_item_by_value($4,'bmp'), $5, $6, $7, $8, FALSE)
584                                       FROM  asset.copy cp
585                                             JOIN asset.copy_part_map cpm ON (cpm.target_copy = cp.id)
586                                       WHERE cpm.part = $1
587                                       ORDER BY COALESCE(cp.copy_number,0), cp.barcode
588                                       LIMIT $7
589                                       OFFSET $8
590                                 )x)
591                             )
592                         ELSE NULL
593                     END,
594                     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
595                 )
596           FROM  biblio.monograph_part
597           WHERE id = $1
598           GROUP BY id, label, label_sortkey, record;
599 $F$ LANGUAGE SQL;
600
601 CREATE OR REPLACE FUNCTION unapi.acp ( obj_id BIGINT, format TEXT,  ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit INT DEFAULT NULL, soffset INT DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$
602         SELECT  XMLELEMENT(
603                     name copy,
604                     XMLATTRIBUTES(
605                         CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
606                         'tag:open-ils.org:U2@acp/' || id AS id,
607                         create_date, edit_date, copy_number, circulate, deposit,
608                         ref, holdable, deleted, deposit_amount, price, barcode,
609                         circ_modifier, circ_as_type, opac_visible
610                     ),
611                     unapi.ccs( status, $2, 'status', evergreen.array_remove_item_by_value($4,'acp'), $5, $6, $7, $8, FALSE),
612                     unapi.acl( location, $2, 'location', evergreen.array_remove_item_by_value($4,'acp'), $5, $6, $7, $8, FALSE),
613                     unapi.aou( circ_lib, $2, 'circ_lib', evergreen.array_remove_item_by_value($4,'acp'), $5, $6, $7, $8),
614                     unapi.aou( circ_lib, $2, 'circlib', evergreen.array_remove_item_by_value($4,'acp'), $5, $6, $7, $8),
615                     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,
616                     XMLELEMENT( name copy_notes,
617                         CASE 
618                             WHEN ('acpn' = ANY ($4)) THEN
619                                 XMLAGG((SELECT unapi.acpn( id, 'xml', 'copy_note', evergreen.array_remove_item_by_value($4,'acp'), $5, $6, $7, $8, FALSE) FROM asset.copy_note WHERE owning_copy = cp.id AND pub))
620                             ELSE NULL
621                         END
622                     ),
623                     XMLELEMENT( name statcats,
624                         CASE 
625                             WHEN ('ascecm' = ANY ($4)) THEN
626                                 XMLAGG((SELECT unapi.ascecm( stat_cat_entry, 'xml', 'statcat', evergreen.array_remove_item_by_value($4,'acp'), $5, $6, $7, $8, FALSE) FROM asset.stat_cat_entry_copy_map WHERE owning_copy = cp.id))
627                             ELSE NULL
628                         END
629                     ),
630                     XMLELEMENT( name foreign_records,
631                         CASE
632                             WHEN ('bre' = ANY ($4)) THEN
633                                 XMLAGG((SELECT unapi.bre(peer_record,'marcxml','record','{}'::TEXT[], $5, $6, $7, $8, FALSE) FROM biblio.peer_bib_copy_map WHERE target_copy = cp.id))
634                             ELSE NULL
635                         END
636
637                     ),
638                     CASE 
639                         WHEN ('bmp' = ANY ($4)) THEN
640                             XMLELEMENT( name monograph_parts,
641                                 XMLAGG((SELECT unapi.bmp( part, 'xml', 'monograph_part', evergreen.array_remove_item_by_value($4,'acp'), $5, $6, $7, $8, FALSE) FROM asset.copy_part_map WHERE target_copy = cp.id))
642                             )
643                         ELSE NULL
644                     END
645                 )
646           FROM  asset.copy cp
647           WHERE id = $1
648           GROUP BY id, status, location, circ_lib, call_number, create_date, edit_date, copy_number, circulate, deposit, ref, holdable, deleted, deposit_amount, price, barcode, circ_modifier, circ_as_type, opac_visible;
649 $F$ LANGUAGE SQL;
650
651 CREATE OR REPLACE FUNCTION unapi.sunit ( obj_id BIGINT, format TEXT,  ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit INT DEFAULT NULL, soffset INT DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$
652         SELECT  XMLELEMENT(
653                     name serial_unit,
654                     XMLATTRIBUTES(
655                         CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
656                         'tag:open-ils.org:U2@acp/' || id AS id,
657                         create_date, edit_date, copy_number, circulate, deposit,
658                         ref, holdable, deleted, deposit_amount, price, barcode,
659                         circ_modifier, circ_as_type, opac_visible, status_changed_time,
660                         floating, mint_condition, detailed_contents, sort_key, summary_contents, cost 
661                     ),
662                     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),
663                     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),
664                     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),
665                     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),
666                     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,
667                     XMLELEMENT( name copy_notes,
668                         CASE 
669                             WHEN ('acpn' = ANY ($4)) THEN
670                                 XMLAGG((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) FROM asset.copy_note WHERE owning_copy = cp.id AND pub))
671                             ELSE NULL
672                         END
673                     ),
674                     XMLELEMENT( name statcats,
675                         CASE 
676                             WHEN ('ascecm' = ANY ($4)) THEN
677                                 XMLAGG((SELECT unapi.acpn( stat_cat_entry, 'xml', 'statcat', evergreen.array_remove_item_by_value( evergreen.array_remove_item_by_value($4,'acp'),'sunit'), $5, $6, $7, $8, FALSE) FROM asset.stat_cat_entry_copy_map WHERE owning_copy = cp.id))
678                             ELSE NULL
679                         END
680                     )
681                 )
682           FROM  serial.unit cp
683           WHERE id = $1
684           GROUP BY  id, status, location, circ_lib, call_number, create_date, edit_date, copy_number, circulate, floating, mint_condition,
685                     deposit, ref, holdable, deleted, deposit_amount, price, barcode, circ_modifier, circ_as_type, opac_visible, status_changed_time, detailed_contents, sort_key, summary_contents, cost;
686 $F$ LANGUAGE SQL;
687
688 CREATE OR REPLACE FUNCTION unapi.acn ( obj_id BIGINT, format TEXT,  ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit INT DEFAULT NULL, soffset INT DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$
689         SELECT  XMLELEMENT(
690                     name volume,
691                     XMLATTRIBUTES(
692                         CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
693                         'tag:open-ils.org:U2@acn/' || acn.id AS id,
694                         o.shortname AS lib,
695                         o.opac_visible AS opac_visible,
696                         deleted, label, label_sortkey, label_class, record
697                     ),
698                     unapi.aou( owning_lib, $2, 'owning_lib', evergreen.array_remove_item_by_value($4,'acn'), $5, $6, $7, $8),
699                     XMLELEMENT( name copies,
700                         CASE 
701                             WHEN ('acp' = ANY ($4)) THEN
702                                 (SELECT XMLAGG(acp) FROM (
703                                     SELECT  unapi.acp( cp.id, 'xml', 'copy', evergreen.array_remove_item_by_value($4,'acn'), $5, $6, $7, $8, FALSE)
704                                       FROM  asset.copy cp
705                                             JOIN actor.org_unit_descendants(
706                                                 (SELECT id FROM actor.org_unit WHERE shortname = $5),
707                                                 (COALESCE($6,(SELECT aout.depth FROM actor.org_unit_type aout JOIN actor.org_unit aou ON (aou.ou_type = aout.id AND aou.shortname = $5))))
708                                             ) aoud ON (cp.circ_lib = aoud.id)
709                                       WHERE cp.call_number = acn.id
710                                       ORDER BY COALESCE(cp.copy_number,0), cp.barcode
711                                       LIMIT $7
712                                       OFFSET $8
713                                 )x)
714                             ELSE NULL
715                         END
716                     ),
717                     XMLELEMENT(
718                         name uris,
719                         (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)
720                     ),
721                     CASE WHEN ('acnp' = ANY ($4)) THEN unapi.acnp( acn.prefix, 'marcxml', 'prefix', evergreen.array_remove_item_by_value($4,'acn'), $5, $6, $7, $8, FALSE) ELSE NULL END,
722                     CASE WHEN ('acns' = ANY ($4)) THEN unapi.acns( acn.suffix, 'marcxml', 'suffix', evergreen.array_remove_item_by_value($4,'acn'), $5, $6, $7, $8, FALSE) ELSE NULL END,
723                     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
724                 ) AS x
725           FROM  asset.call_number acn
726                 JOIN actor.org_unit o ON (o.id = acn.owning_lib)
727           WHERE acn.id = $1
728           GROUP BY acn.id, o.shortname, o.opac_visible, deleted, label, label_sortkey, label_class, owning_lib, record, acn.prefix, acn.suffix;
729 $F$ LANGUAGE SQL;
730
731 CREATE OR REPLACE FUNCTION unapi.acnp ( obj_id BIGINT, format TEXT,  ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit INT DEFAULT NULL, soffset INT DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$
732         SELECT  XMLELEMENT(
733                     name call_number_prefix,
734                     XMLATTRIBUTES(
735                         CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
736                         id AS ident,
737                         label,
738                         label_sortkey
739                     ),
740                     unapi.aou( owning_lib, $2, 'owning_lib', evergreen.array_remove_item_by_value($4,'acnp'), $5, $6, $7, $8)
741                 )
742           FROM  asset.call_number_prefix
743           WHERE id = $1;
744 $F$ LANGUAGE SQL;
745
746 CREATE OR REPLACE FUNCTION unapi.acns ( obj_id BIGINT, format TEXT,  ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit INT DEFAULT NULL, soffset INT DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$
747         SELECT  XMLELEMENT(
748                     name call_number_suffix,
749                     XMLATTRIBUTES(
750                         CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
751                         id AS ident,
752                         label,
753                         label_sortkey
754                     ),
755                     unapi.aou( owning_lib, $2, 'owning_lib', evergreen.array_remove_item_by_value($4,'acns'), $5, $6, $7, $8)
756                 )
757           FROM  asset.call_number_suffix
758           WHERE id = $1;
759 $F$ LANGUAGE SQL;
760
761 CREATE OR REPLACE FUNCTION unapi.auri ( obj_id BIGINT, format TEXT,  ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit INT DEFAULT NULL, soffset INT DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$
762         SELECT  XMLELEMENT(
763                     name volume,
764                     XMLATTRIBUTES(
765                         CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
766                         'tag:open-ils.org:U2@auri/' || uri.id AS id,
767                         use_restriction,
768                         href,
769                         label
770                     ),
771                     XMLELEMENT( name copies,
772                         CASE 
773                             WHEN ('acn' = ANY ($4)) THEN
774                                 (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)
775                             ELSE NULL
776                         END
777                     )
778                 ) AS x
779           FROM  asset.uri uri
780           WHERE uri.id = $1
781           GROUP BY uri.id, use_restriction, href, label;
782 $F$ LANGUAGE SQL;
783
784 /*
785
786  -- Some test queries
787
788 SELECT unapi.memoize( 'bre', 1,'mods32','','{holdings_xml,acp}'::TEXT[], 'SYS1');
789 SELECT unapi.memoize( 'bre', 1,'marcxml','','{holdings_xml,acp}'::TEXT[], 'SYS1');
790 SELECT unapi.memoize( 'bre', 1,'holdings_xml','','{holdings_xml,acp}'::TEXT[], 'SYS1');
791
792 SELECT unapi.biblio_record_entry_feed('{1}'::BIGINT[],'mods32','{holdings_xml,acp}'::TEXT[],'SYS1',NULL,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>');
793
794 SELECT unapi.biblio_record_entry_feed('{7209,7394}'::BIGINT[],'marcxml','{}'::TEXT[],'SYS1',NULL,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>');
795 EXPLAIN ANALYZE SELECT unapi.biblio_record_entry_feed('{7209,7394}'::BIGINT[],'marcxml','{}'::TEXT[],'SYS1',NULL,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>');
796 EXPLAIN ANALYZE SELECT unapi.biblio_record_entry_feed('{7209,7394}'::BIGINT[],'marcxml','{holdings_xml}'::TEXT[],'SYS1',NULL,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>');
797 EXPLAIN ANALYZE SELECT unapi.biblio_record_entry_feed('{7209,7394}'::BIGINT[],'mods32','{holdings_xml}'::TEXT[],'SYS1',NULL,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>');
798
799 SELECT unapi.biblio_record_entry_feed('{216}'::BIGINT[],'marcxml','{}'::TEXT[], 'BR1');
800 EXPLAIN ANALYZE SELECT unapi.bre(216,'marcxml','record','{holdings_xml,bre.unapi}'::TEXT[], 'BR1');
801 EXPLAIN ANALYZE SELECT unapi.bre(216,'holdings_xml','record','{}'::TEXT[], 'BR1');
802 EXPLAIN ANALYZE SELECT unapi.holdings_xml(216,4,'BR1',2,'{bre}'::TEXT[]);
803 EXPLAIN ANALYZE SELECT unapi.bre(216,'mods32','record','{}'::TEXT[], 'BR1');
804
805 */
806
807 COMMIT;
808