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