]> git.evergreen-ils.org Git - working/Evergreen.git/blob - Open-ILS/src/sql/Pg/990.schema.unapi.sql
Merge branch 'master' of git://git.evergreen-ils.org/Evergreen into ttopac
[working/Evergreen.git] / Open-ILS / src / sql / Pg / 990.schema.unapi.sql
1 DROP SCHEMA IF EXISTS unapi CASCADE;
2
3 BEGIN;
4 CREATE SCHEMA unapi;
5
6 CREATE OR REPLACE FUNCTION evergreen.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                     holdable,
534                     opac_visible,
535                     label_prefix AS prefix,
536                     label_suffix AS suffix
537                 ),
538                 name
539             )
540       FROM  asset.copy_location
541       WHERE id = $1;
542 $F$ LANGUAGE SQL;
543
544 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$
545     SELECT  XMLELEMENT(
546                 name status,
547                 XMLATTRIBUTES(
548                     CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
549                     id AS ident,
550                     holdable,
551                     opac_visible
552                 ),
553                 name
554             )
555       FROM  config.copy_status
556       WHERE id = $1;
557 $F$ LANGUAGE SQL;
558
559 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$
560         SELECT  XMLELEMENT(
561                     name copy_note,
562                     XMLATTRIBUTES(
563                         CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
564                         create_date AS date,
565                         title
566                     ),
567                     value
568                 )
569           FROM  asset.copy_note
570           WHERE id = $1;
571 $F$ LANGUAGE SQL;
572
573 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$
574         SELECT  XMLELEMENT(
575                     name statcat,
576                     XMLATTRIBUTES(
577                         CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
578                         sc.name,
579                         sc.opac_visible
580                     ),
581                     asce.value
582                 )
583           FROM  asset.stat_cat_entry asce
584                 JOIN asset.stat_cat sc ON (sc.id = asce.stat_cat)
585           WHERE asce.id = $1;
586 $F$ LANGUAGE SQL;
587
588 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$
589         SELECT  XMLELEMENT(
590                     name monograph_part,
591                     XMLATTRIBUTES(
592                         CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
593                         'tag:open-ils.org:U2@bmp/' || id AS id,
594                         id AS ident,
595                         label,
596                         label_sortkey,
597                         'tag:open-ils.org:U2@bre/' || record AS record
598                     ),
599                     CASE 
600                         WHEN ('acp' = ANY ($4)) THEN
601                             XMLELEMENT( name copies,
602                                 (SELECT XMLAGG(acp) FROM (
603                                     SELECT  unapi.acp( cp.id, 'xml', 'copy', evergreen.array_remove_item_by_value($4,'bmp'), $5, $6, $7, $8, FALSE)
604                                       FROM  asset.copy cp
605                                             JOIN asset.copy_part_map cpm ON (cpm.target_copy = cp.id)
606                                       WHERE cpm.part = $1
607                                       ORDER BY COALESCE(cp.copy_number,0), cp.barcode
608                                       LIMIT $7
609                                       OFFSET $8
610                                 )x)
611                             )
612                         ELSE NULL
613                     END,
614                     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
615                 )
616           FROM  biblio.monograph_part
617           WHERE id = $1
618           GROUP BY id, label, label_sortkey, record;
619 $F$ LANGUAGE SQL;
620
621 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$
622         SELECT  XMLELEMENT(
623                     name copy,
624                     XMLATTRIBUTES(
625                         CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
626                         'tag:open-ils.org:U2@acp/' || id AS id,
627                         create_date, edit_date, copy_number, circulate, deposit,
628                         ref, holdable, deleted, deposit_amount, price, barcode,
629                         circ_modifier, circ_as_type, opac_visible
630                     ),
631                     unapi.ccs( status, $2, 'status', evergreen.array_remove_item_by_value($4,'acp'), $5, $6, $7, $8, FALSE),
632                     unapi.acl( location, $2, 'location', evergreen.array_remove_item_by_value($4,'acp'), $5, $6, $7, $8, FALSE),
633                     unapi.aou( circ_lib, $2, 'circ_lib', evergreen.array_remove_item_by_value($4,'acp'), $5, $6, $7, $8),
634                     unapi.aou( circ_lib, $2, 'circlib', evergreen.array_remove_item_by_value($4,'acp'), $5, $6, $7, $8),
635                     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,
636                     XMLELEMENT( name copy_notes,
637                         CASE 
638                             WHEN ('acpn' = ANY ($4)) THEN
639                                 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))
640                             ELSE NULL
641                         END
642                     ),
643                     XMLELEMENT( name statcats,
644                         CASE 
645                             WHEN ('ascecm' = ANY ($4)) THEN
646                                 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))
647                             ELSE NULL
648                         END
649                     ),
650                     XMLELEMENT( name foreign_records,
651                         CASE
652                             WHEN ('bre' = ANY ($4)) THEN
653                                 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))
654                             ELSE NULL
655                         END
656
657                     ),
658                     CASE 
659                         WHEN ('bmp' = ANY ($4)) THEN
660                             XMLELEMENT( name monograph_parts,
661                                 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))
662                             )
663                         ELSE NULL
664                     END
665                 )
666           FROM  asset.copy cp
667           WHERE id = $1
668           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;
669 $F$ LANGUAGE SQL;
670
671 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$
672         SELECT  XMLELEMENT(
673                     name serial_unit,
674                     XMLATTRIBUTES(
675                         CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
676                         'tag:open-ils.org:U2@acp/' || id AS id,
677                         create_date, edit_date, copy_number, circulate, deposit,
678                         ref, holdable, deleted, deposit_amount, price, barcode,
679                         circ_modifier, circ_as_type, opac_visible, status_changed_time,
680                         floating, mint_condition, detailed_contents, sort_key, summary_contents, cost 
681                     ),
682                     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),
683                     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),
684                     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),
685                     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),
686                     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,
687                     XMLELEMENT( name copy_notes,
688                         CASE 
689                             WHEN ('acpn' = ANY ($4)) THEN
690                                 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))
691                             ELSE NULL
692                         END
693                     ),
694                     XMLELEMENT( name statcats,
695                         CASE 
696                             WHEN ('ascecm' = ANY ($4)) THEN
697                                 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))
698                             ELSE NULL
699                         END
700                     )
701                 )
702           FROM  serial.unit cp
703           WHERE id = $1
704           GROUP BY  id, status, location, circ_lib, call_number, create_date, edit_date, copy_number, circulate, floating, mint_condition,
705                     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;
706 $F$ LANGUAGE SQL;
707
708 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$
709         SELECT  XMLELEMENT(
710                     name volume,
711                     XMLATTRIBUTES(
712                         CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
713                         'tag:open-ils.org:U2@acn/' || acn.id AS id,
714                         o.shortname AS lib,
715                         o.opac_visible AS opac_visible,
716                         deleted, label, label_sortkey, label_class, record
717                     ),
718                     unapi.aou( owning_lib, $2, 'owning_lib', evergreen.array_remove_item_by_value($4,'acn'), $5, $6, $7, $8),
719                     XMLELEMENT( name copies,
720                         CASE 
721                             WHEN ('acp' = ANY ($4)) THEN
722                                 (SELECT XMLAGG(acp) FROM (
723                                     SELECT  unapi.acp( cp.id, 'xml', 'copy', evergreen.array_remove_item_by_value($4,'acn'), $5, $6, $7, $8, FALSE)
724                                       FROM  asset.copy cp
725                                             JOIN actor.org_unit_descendants(
726                                                 (SELECT id FROM actor.org_unit WHERE shortname = $5),
727                                                 (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))))
728                                             ) aoud ON (cp.circ_lib = aoud.id)
729                                       WHERE cp.call_number = acn.id
730                                       ORDER BY COALESCE(cp.copy_number,0), cp.barcode
731                                       LIMIT $7
732                                       OFFSET $8
733                                 )x)
734                             ELSE NULL
735                         END
736                     ),
737                     XMLELEMENT(
738                         name uris,
739                         (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)
740                     ),
741                     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,
742                     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,
743                     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
744                 ) AS x
745           FROM  asset.call_number acn
746                 JOIN actor.org_unit o ON (o.id = acn.owning_lib)
747           WHERE acn.id = $1
748           GROUP BY acn.id, o.shortname, o.opac_visible, deleted, label, label_sortkey, label_class, owning_lib, record, acn.prefix, acn.suffix;
749 $F$ LANGUAGE SQL;
750
751 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$
752         SELECT  XMLELEMENT(
753                     name call_number_prefix,
754                     XMLATTRIBUTES(
755                         CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
756                         id AS ident,
757                         label,
758                         label_sortkey
759                     ),
760                     unapi.aou( owning_lib, $2, 'owning_lib', evergreen.array_remove_item_by_value($4,'acnp'), $5, $6, $7, $8)
761                 )
762           FROM  asset.call_number_prefix
763           WHERE id = $1;
764 $F$ LANGUAGE SQL;
765
766 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$
767         SELECT  XMLELEMENT(
768                     name call_number_suffix,
769                     XMLATTRIBUTES(
770                         CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
771                         id AS ident,
772                         label,
773                         label_sortkey
774                     ),
775                     unapi.aou( owning_lib, $2, 'owning_lib', evergreen.array_remove_item_by_value($4,'acns'), $5, $6, $7, $8)
776                 )
777           FROM  asset.call_number_suffix
778           WHERE id = $1;
779 $F$ LANGUAGE SQL;
780
781 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$
782         SELECT  XMLELEMENT(
783                     name volume,
784                     XMLATTRIBUTES(
785                         CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
786                         'tag:open-ils.org:U2@auri/' || uri.id AS id,
787                         use_restriction,
788                         href,
789                         label
790                     ),
791                     XMLELEMENT( name copies,
792                         CASE 
793                             WHEN ('acn' = ANY ($4)) THEN
794                                 (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)
795                             ELSE NULL
796                         END
797                     )
798                 ) AS x
799           FROM  asset.uri uri
800           WHERE uri.id = $1
801           GROUP BY uri.id, use_restriction, href, label;
802 $F$ LANGUAGE SQL;
803
804 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$
805         SELECT  XMLELEMENT(
806                     name attributes,
807                     XMLATTRIBUTES(
808                         CASE WHEN $9 THEN 'http://open-ils.org/spec/indexing/v1' ELSE NULL END AS xmlns,
809                         'tag:open-ils.org:U2@mra/' || mra.id AS id,
810                         'tag:open-ils.org:U2@bre/' || mra.id AS record
811                     ),
812                     (SELECT XMLAGG(foo.y)
813                       FROM (SELECT XMLELEMENT(
814                                 name field,
815                                 XMLATTRIBUTES(
816                                     key AS name,
817                                     cvm.value AS "coded-value",
818                                     rad.filter,
819                                     rad.sorter
820                                 ),
821                                 x.value
822                             )
823                            FROM EACH(mra.attrs) AS x
824                                 JOIN config.record_attr_definition rad ON (x.key = rad.name)
825                                 LEFT JOIN config.coded_value_map cvm ON (cvm.ctype = x.key AND code = x.value)
826                         )foo(y)
827                     )
828                 )
829           FROM  metabib.record_attr mra
830           WHERE mra.id = $1;
831 $F$ LANGUAGE SQL;
832
833 /*
834
835  -- Some test queries
836
837 SELECT unapi.memoize( 'bre', 1,'mods32','','{holdings_xml,acp}'::TEXT[], 'SYS1');
838 SELECT unapi.memoize( 'bre', 1,'marcxml','','{holdings_xml,acp}'::TEXT[], 'SYS1');
839 SELECT unapi.memoize( 'bre', 1,'holdings_xml','','{holdings_xml,acp}'::TEXT[], 'SYS1');
840
841 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>');
842
843 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>');
844 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>');
845 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>');
846 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>');
847
848 SELECT unapi.biblio_record_entry_feed('{216}'::BIGINT[],'marcxml','{}'::TEXT[], 'BR1');
849 EXPLAIN ANALYZE SELECT unapi.bre(216,'marcxml','record','{holdings_xml,bre.unapi}'::TEXT[], 'BR1');
850 EXPLAIN ANALYZE SELECT unapi.bre(216,'holdings_xml','record','{}'::TEXT[], 'BR1');
851 EXPLAIN ANALYZE SELECT unapi.holdings_xml(216,4,'BR1',2,'{bre}'::TEXT[]);
852 EXPLAIN ANALYZE SELECT unapi.bre(216,'mods32','record','{}'::TEXT[], 'BR1');
853
854 */
855
856 COMMIT;
857