]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/src/sql/Pg/upgrade/0857.function.located_uris_act_as_copies.sql
LP1894131 Sticky catalog holdings org select
[Evergreen.git] / Open-ILS / src / sql / Pg / upgrade / 0857.function.located_uris_act_as_copies.sql
1 /*
2  * Copyright (C) 2014  Equinox Software, Inc.
3  * Mike Rylander <miker@esilibrary.com>
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  */
16
17
18 BEGIN;
19
20 SELECT evergreen.upgrade_deps_block_check('0857', :eg_version);
21
22 INSERT INTO config.global_flag (name, enabled, label)
23 VALUES (
24     'opac.located_uri.act_as_copy',
25     FALSE,
26     oils_i18n_gettext(
27         'opac.located_uri.act_as_copy',
28         'When enabled, Located URIs will provide visiblity behavior identical to copies.',
29         'cgf',
30         'label'
31     )
32 );
33
34 CREATE OR REPLACE FUNCTION evergreen.located_uris (
35     bibid BIGINT,
36     ouid INT,
37     pref_lib INT DEFAULT NULL
38 ) RETURNS TABLE (id BIGINT, name TEXT, label_sortkey TEXT, rank INT) AS $$
39     WITH all_orgs AS (SELECT COALESCE( enabled, FALSE ) AS flag FROM config.global_flag WHERE name = 'opac.located_uri.act_as_copy')
40     SELECT DISTINCT ON (id) * FROM (
41     SELECT acn.id, COALESCE(aou.name,aoud.name), acn.label_sortkey, evergreen.rank_ou(aou.id, $2, $3) AS pref_ou
42       FROM asset.call_number acn
43            INNER JOIN asset.uri_call_number_map auricnm ON acn.id = auricnm.call_number
44            INNER JOIN asset.uri auri ON auri.id = auricnm.uri
45            LEFT JOIN actor.org_unit_ancestors( COALESCE($3, $2) ) aou ON (acn.owning_lib = aou.id)
46            LEFT JOIN actor.org_unit_descendants( COALESCE($3, $2) ) aoud ON (acn.owning_lib = aoud.id),
47            all_orgs
48       WHERE acn.record = $1
49           AND acn.deleted IS FALSE
50           AND auri.active IS TRUE
51           AND ((NOT all_orgs.flag AND aou.id IS NOT NULL) OR COALESCE(aou.id,aoud.id) IS NOT NULL)
52     UNION
53     SELECT acn.id, COALESCE(aou.name,aoud.name) AS name, acn.label_sortkey, evergreen.rank_ou(aou.id, $2, $3) AS pref_ou
54       FROM asset.call_number acn
55            INNER JOIN asset.uri_call_number_map auricnm ON acn.id = auricnm.call_number
56            INNER JOIN asset.uri auri ON auri.id = auricnm.uri
57            LEFT JOIN actor.org_unit_ancestors( $2 ) aou ON (acn.owning_lib = aou.id)
58            LEFT JOIN actor.org_unit_descendants( $2 ) aoud ON (acn.owning_lib = aoud.id),
59            all_orgs
60       WHERE acn.record = $1
61           AND acn.deleted IS FALSE
62           AND auri.active IS TRUE
63           AND ((NOT all_orgs.flag AND aou.id IS NOT NULL) OR COALESCE(aou.id,aoud.id) IS NOT NULL))x
64     ORDER BY id, pref_ou DESC;
65 $$
66 LANGUAGE SQL STABLE;
67
68 CREATE OR REPLACE FUNCTION search.query_parser_fts (
69
70     param_search_ou INT,
71     param_depth     INT,
72     param_query     TEXT,
73     param_statuses  INT[],
74     param_locations INT[],
75     param_offset    INT,
76     param_check     INT,
77     param_limit     INT,
78     metarecord      BOOL,
79     staff           BOOL,
80     deleted_search  BOOL,
81     param_pref_ou   INT DEFAULT NULL
82 ) RETURNS SETOF search.search_result AS $func$
83 DECLARE
84
85     current_res         search.search_result%ROWTYPE;
86     search_org_list     INT[];
87     luri_org_list       INT[];
88     tmp_int_list        INT[];
89
90     check_limit         INT;
91     core_limit          INT;
92     core_offset         INT;
93     tmp_int             INT;
94
95     core_result         RECORD;
96     core_cursor         REFCURSOR;
97     core_rel_query      TEXT;
98
99     total_count         INT := 0;
100     check_count         INT := 0;
101     deleted_count       INT := 0;
102     visible_count       INT := 0;
103     excluded_count      INT := 0;
104
105     luri_as_copy        BOOL;
106 BEGIN
107
108     check_limit := COALESCE( param_check, 1000 );
109     core_limit  := COALESCE( param_limit, 25000 );
110     core_offset := COALESCE( param_offset, 0 );
111
112     SELECT COALESCE( enabled, FALSE ) INTO luri_as_copy FROM config.global_flag WHERE name = 'opac.located_uri.act_as_copy';
113
114     -- core_skip_chk := COALESCE( param_skip_chk, 1 );
115
116     IF param_search_ou > 0 THEN
117         IF param_depth IS NOT NULL THEN
118             SELECT ARRAY_AGG(distinct id) INTO search_org_list FROM actor.org_unit_descendants( param_search_ou, param_depth );
119         ELSE
120             SELECT ARRAY_AGG(distinct id) INTO search_org_list FROM actor.org_unit_descendants( param_search_ou );
121         END IF;
122
123         IF luri_as_copy THEN
124             SELECT ARRAY_AGG(distinct id) INTO luri_org_list FROM actor.org_unit_full_path( param_search_ou );
125         ELSE
126             SELECT ARRAY_AGG(distinct id) INTO luri_org_list FROM actor.org_unit_ancestors( param_search_ou );
127         END IF;
128
129     ELSIF param_search_ou < 0 THEN
130         SELECT ARRAY_AGG(distinct org_unit) INTO search_org_list FROM actor.org_lasso_map WHERE lasso = -param_search_ou;
131
132         FOR tmp_int IN SELECT * FROM UNNEST(search_org_list) LOOP
133
134             IF luri_as_copy THEN
135                 SELECT ARRAY_AGG(distinct id) INTO tmp_int_list FROM actor.org_unit_full_path( tmp_int );
136             ELSE
137                 SELECT ARRAY_AGG(distinct id) INTO tmp_int_list FROM actor.org_unit_ancestors( tmp_int );
138             END IF;
139
140             luri_org_list := luri_org_list || tmp_int_list;
141         END LOOP;
142
143         SELECT ARRAY_AGG(DISTINCT x.id) INTO luri_org_list FROM UNNEST(luri_org_list) x(id);
144
145     ELSIF param_search_ou = 0 THEN
146         -- reserved for user lassos (ou_buckets/type='lasso') with ID passed in depth ... hack? sure.
147     END IF;
148
149     IF param_pref_ou IS NOT NULL THEN
150             IF luri_as_copy THEN
151                 SELECT ARRAY_AGG(distinct id) INTO tmp_int_list FROM actor.org_unit_full_path( param_pref_ou );
152             ELSE
153                 SELECT ARRAY_AGG(distinct id) INTO tmp_int_list FROM actor.org_unit_ancestors( param_pref_ou );
154             END IF;
155
156         luri_org_list := luri_org_list || tmp_int_list;
157     END IF;
158
159     OPEN core_cursor FOR EXECUTE param_query;
160
161     LOOP
162
163         FETCH core_cursor INTO core_result;
164         EXIT WHEN NOT FOUND;
165         EXIT WHEN total_count >= core_limit;
166
167         total_count := total_count + 1;
168
169         CONTINUE WHEN total_count NOT BETWEEN  core_offset + 1 AND check_limit + core_offset;
170
171         check_count := check_count + 1;
172
173         IF NOT deleted_search THEN
174
175             PERFORM 1 FROM biblio.record_entry b WHERE NOT b.deleted AND b.id IN ( SELECT * FROM unnest( core_result.records ) );
176             IF NOT FOUND THEN
177                 -- RAISE NOTICE ' % were all deleted ... ', core_result.records;
178                 deleted_count := deleted_count + 1;
179                 CONTINUE;
180             END IF;
181
182             PERFORM 1
183               FROM  biblio.record_entry b
184                     JOIN config.bib_source s ON (b.source = s.id)
185               WHERE s.transcendant
186                     AND b.id IN ( SELECT * FROM unnest( core_result.records ) );
187
188             IF FOUND THEN
189                 -- RAISE NOTICE ' % were all transcendant ... ', core_result.records;
190                 visible_count := visible_count + 1;
191
192                 current_res.id = core_result.id;
193                 current_res.rel = core_result.rel;
194
195                 tmp_int := 1;
196                 IF metarecord THEN
197                     SELECT COUNT(DISTINCT s.source) INTO tmp_int FROM metabib.metarecord_source_map s WHERE s.metarecord = core_result.id;
198                 END IF;
199
200                 IF tmp_int = 1 THEN
201                     current_res.record = core_result.records[1];
202                 ELSE
203                     current_res.record = NULL;
204                 END IF;
205
206                 RETURN NEXT current_res;
207
208                 CONTINUE;
209             END IF;
210
211             PERFORM 1
212               FROM  asset.call_number cn
213                     JOIN asset.uri_call_number_map map ON (map.call_number = cn.id)
214                     JOIN asset.uri uri ON (map.uri = uri.id)
215               WHERE NOT cn.deleted
216                     AND cn.label = '##URI##'
217                     AND uri.active
218                     AND ( param_locations IS NULL OR array_upper(param_locations, 1) IS NULL )
219                     AND cn.record IN ( SELECT * FROM unnest( core_result.records ) )
220                     AND cn.owning_lib IN ( SELECT * FROM unnest( luri_org_list ) )
221               LIMIT 1;
222
223             IF FOUND THEN
224                 -- RAISE NOTICE ' % have at least one URI ... ', core_result.records;
225                 visible_count := visible_count + 1;
226
227                 current_res.id = core_result.id;
228                 current_res.rel = core_result.rel;
229
230                 tmp_int := 1;
231                 IF metarecord THEN
232                     SELECT COUNT(DISTINCT s.source) INTO tmp_int FROM metabib.metarecord_source_map s WHERE s.metarecord = core_result.id;
233                 END IF;
234
235                 IF tmp_int = 1 THEN
236                     current_res.record = core_result.records[1];
237                 ELSE
238                     current_res.record = NULL;
239                 END IF;
240
241                 RETURN NEXT current_res;
242
243                 CONTINUE;
244             END IF;
245
246             IF param_statuses IS NOT NULL AND array_upper(param_statuses, 1) > 0 THEN
247
248                 PERFORM 1
249                   FROM  asset.call_number cn
250                         JOIN asset.copy cp ON (cp.call_number = cn.id)
251                   WHERE NOT cn.deleted
252                         AND NOT cp.deleted
253                         AND cp.status IN ( SELECT * FROM unnest( param_statuses ) )
254                         AND cn.record IN ( SELECT * FROM unnest( core_result.records ) )
255                         AND cp.circ_lib IN ( SELECT * FROM unnest( search_org_list ) )
256                   LIMIT 1;
257
258                 IF NOT FOUND THEN
259                     PERFORM 1
260                       FROM  biblio.peer_bib_copy_map pr
261                             JOIN asset.copy cp ON (cp.id = pr.target_copy)
262                       WHERE NOT cp.deleted
263                             AND cp.status IN ( SELECT * FROM unnest( param_statuses ) )
264                             AND pr.peer_record IN ( SELECT * FROM unnest( core_result.records ) )
265                             AND cp.circ_lib IN ( SELECT * FROM unnest( search_org_list ) )
266                       LIMIT 1;
267
268                     IF NOT FOUND THEN
269                     -- RAISE NOTICE ' % and multi-home linked records were all status-excluded ... ', core_result.records;
270                         excluded_count := excluded_count + 1;
271                         CONTINUE;
272                     END IF;
273                 END IF;
274
275             END IF;
276
277             IF param_locations IS NOT NULL AND array_upper(param_locations, 1) > 0 THEN
278
279                 PERFORM 1
280                   FROM  asset.call_number cn
281                         JOIN asset.copy cp ON (cp.call_number = cn.id)
282                   WHERE NOT cn.deleted
283                         AND NOT cp.deleted
284                         AND cp.location IN ( SELECT * FROM unnest( param_locations ) )
285                         AND cn.record IN ( SELECT * FROM unnest( core_result.records ) )
286                         AND cp.circ_lib IN ( SELECT * FROM unnest( search_org_list ) )
287                   LIMIT 1;
288
289                 IF NOT FOUND THEN
290                     PERFORM 1
291                       FROM  biblio.peer_bib_copy_map pr
292                             JOIN asset.copy cp ON (cp.id = pr.target_copy)
293                       WHERE NOT cp.deleted
294                             AND cp.location IN ( SELECT * FROM unnest( param_locations ) )
295                             AND pr.peer_record IN ( SELECT * FROM unnest( core_result.records ) )
296                             AND cp.circ_lib IN ( SELECT * FROM unnest( search_org_list ) )
297                       LIMIT 1;
298
299                     IF NOT FOUND THEN
300                         -- RAISE NOTICE ' % and multi-home linked records were all copy_location-excluded ... ', core_result.records;
301                         excluded_count := excluded_count + 1;
302                         CONTINUE;
303                     END IF;
304                 END IF;
305
306             END IF;
307
308             IF staff IS NULL OR NOT staff THEN
309
310                 PERFORM 1
311                   FROM  asset.opac_visible_copies
312                   WHERE circ_lib IN ( SELECT * FROM unnest( search_org_list ) )
313                         AND record IN ( SELECT * FROM unnest( core_result.records ) )
314                   LIMIT 1;
315
316                 IF NOT FOUND THEN
317                     PERFORM 1
318                       FROM  biblio.peer_bib_copy_map pr
319                             JOIN asset.opac_visible_copies cp ON (cp.copy_id = pr.target_copy)
320                       WHERE cp.circ_lib IN ( SELECT * FROM unnest( search_org_list ) )
321                             AND pr.peer_record IN ( SELECT * FROM unnest( core_result.records ) )
322                       LIMIT 1;
323
324                     IF NOT FOUND THEN
325
326                         -- RAISE NOTICE ' % and multi-home linked records were all visibility-excluded ... ', core_result.records;
327                         excluded_count := excluded_count + 1;
328                         CONTINUE;
329                     END IF;
330                 END IF;
331
332             ELSE
333
334                 PERFORM 1
335                   FROM  asset.call_number cn
336                         JOIN asset.copy cp ON (cp.call_number = cn.id)
337                   WHERE NOT cn.deleted
338                         AND NOT cp.deleted
339                         AND cp.circ_lib IN ( SELECT * FROM unnest( search_org_list ) )
340                         AND cn.record IN ( SELECT * FROM unnest( core_result.records ) )
341                   LIMIT 1;
342
343                 IF NOT FOUND THEN
344
345                     PERFORM 1
346                       FROM  biblio.peer_bib_copy_map pr
347                             JOIN asset.copy cp ON (cp.id = pr.target_copy)
348                       WHERE NOT cp.deleted
349                             AND cp.circ_lib IN ( SELECT * FROM unnest( search_org_list ) )
350                             AND pr.peer_record IN ( SELECT * FROM unnest( core_result.records ) )
351                       LIMIT 1;
352
353                     IF NOT FOUND THEN
354
355                         PERFORM 1
356                           FROM  asset.call_number cn
357                                 JOIN asset.copy cp ON (cp.call_number = cn.id)
358                           WHERE cn.record IN ( SELECT * FROM unnest( core_result.records ) )
359                                 AND NOT cp.deleted
360                           LIMIT 1;
361
362                         IF FOUND THEN
363                             -- RAISE NOTICE ' % and multi-home linked records were all visibility-excluded ... ', core_result.records;
364                             excluded_count := excluded_count + 1;
365                             CONTINUE;
366                         END IF;
367                     END IF;
368
369                 END IF;
370
371             END IF;
372
373         END IF;
374
375         visible_count := visible_count + 1;
376
377         current_res.id = core_result.id;
378         current_res.rel = core_result.rel;
379
380         tmp_int := 1;
381         IF metarecord THEN
382             SELECT COUNT(DISTINCT s.source) INTO tmp_int FROM metabib.metarecord_source_map s WHERE s.metarecord = core_result.id;
383         END IF;
384
385         IF tmp_int = 1 THEN
386             current_res.record = core_result.records[1];
387         ELSE
388             current_res.record = NULL;
389         END IF;
390
391         RETURN NEXT current_res;
392
393         IF visible_count % 1000 = 0 THEN
394             -- RAISE NOTICE ' % visible so far ... ', visible_count;
395         END IF;
396
397     END LOOP;
398
399     current_res.id = NULL;
400     current_res.rel = NULL;
401     current_res.record = NULL;
402     current_res.total = total_count;
403     current_res.checked = check_count;
404     current_res.deleted = deleted_count;
405     current_res.visible = visible_count;
406     current_res.excluded = excluded_count;
407
408     CLOSE core_cursor;
409
410     RETURN NEXT current_res;
411
412 END;
413 $func$ LANGUAGE PLPGSQL;
414
415 CREATE OR REPLACE FUNCTION unapi.holdings_xml (
416     bid BIGINT,
417     ouid INT,
418     org TEXT,
419     depth INT DEFAULT NULL,
420     includes TEXT[] DEFAULT NULL::TEXT[],
421     slimit HSTORE DEFAULT NULL,
422     soffset HSTORE DEFAULT NULL,
423     include_xmlns BOOL DEFAULT TRUE,
424     pref_lib INT DEFAULT NULL
425 )
426 RETURNS XML AS $F$
427      SELECT  XMLELEMENT(
428                  name holdings,
429                  XMLATTRIBUTES(
430                     CASE WHEN $8 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
431                     CASE WHEN ('bre' = ANY ($5)) THEN 'tag:open-ils.org:U2@bre/' || $1 || '/' || $3 ELSE NULL END AS id,
432                     (SELECT record_has_holdable_copy FROM asset.record_has_holdable_copy($1)) AS has_holdable
433                  ),
434                  XMLELEMENT(
435                      name counts,
436                      (SELECT  XMLAGG(XMLELEMENT::XML) FROM (
437                          SELECT  XMLELEMENT(
438                                      name count,
439                                      XMLATTRIBUTES('public' as type, depth, org_unit, coalesce(transcendant,0) as transcendant, available, visible as count, unshadow)
440                                  )::text
441                            FROM  asset.opac_ou_record_copy_count($2,  $1)
442                                      UNION
443                          SELECT  XMLELEMENT(
444                                      name count,
445                                      XMLATTRIBUTES('staff' as type, depth, org_unit, coalesce(transcendant,0) as transcendant, available, visible as count, unshadow)
446                                  )::text
447                            FROM  asset.staff_ou_record_copy_count($2, $1)
448                                      UNION
449                          SELECT  XMLELEMENT(
450                                      name count,
451                                      XMLATTRIBUTES('pref_lib' as type, depth, org_unit, coalesce(transcendant,0) as transcendant, available, visible as count, unshadow)
452                                  )::text
453                            FROM  asset.opac_ou_record_copy_count($9,  $1)
454                                      ORDER BY 1
455                      )x)
456                  ),
457                  CASE
458                      WHEN ('bmp' = ANY ($5)) THEN
459                         XMLELEMENT(
460                             name monograph_parts,
461                             (SELECT XMLAGG(bmp) FROM (
462                                 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)
463                                   FROM  biblio.monograph_part
464                                   WHERE record = $1
465                             )x)
466                         )
467                      ELSE NULL
468                  END,
469                  XMLELEMENT(
470                      name volumes,
471                      (SELECT XMLAGG(acn ORDER BY rank, name, label_sortkey) FROM (
472                         -- Physical copies
473                         SELECT  unapi.acn(y.id,'xml','volume',evergreen.array_remove_item_by_value( evergreen.array_remove_item_by_value($5,'holdings_xml'),'bre'), $3, $4, $6, $7, FALSE), y.rank, name, label_sortkey
474                         FROM evergreen.ranked_volumes($1, $2, $4, $6, $7, $9, $5) AS y
475                         UNION ALL
476                         -- Located URIs
477                         SELECT unapi.acn(uris.id,'xml','volume',evergreen.array_remove_item_by_value( evergreen.array_remove_item_by_value($5,'holdings_xml'),'bre'), $3, $4, $6, $7, FALSE), uris.rank, name, label_sortkey
478                         FROM evergreen.located_uris($1, $2, $9) AS uris
479                      )x)
480                  ),
481                  CASE WHEN ('ssub' = ANY ($5)) THEN
482                      XMLELEMENT(
483                          name subscriptions,
484                          (SELECT XMLAGG(ssub) FROM (
485                             SELECT  unapi.ssub(id,'xml','subscription','{}'::TEXT[], $3, $4, $6, $7, FALSE)
486                               FROM  serial.subscription
487                               WHERE record_entry = $1
488                         )x)
489                      )
490                  ELSE NULL END,
491                  CASE WHEN ('acp' = ANY ($5)) THEN
492                      XMLELEMENT(
493                          name foreign_copies,
494                          (SELECT XMLAGG(acp) FROM (
495                             SELECT  unapi.acp(p.target_copy,'xml','copy',evergreen.array_remove_item_by_value($5,'acp'), $3, $4, $6, $7, FALSE)
496                               FROM  biblio.peer_bib_copy_map p
497                                     JOIN asset.copy c ON (p.target_copy = c.id)
498                               WHERE NOT c.deleted AND p.peer_record = $1
499                             LIMIT ($6 -> 'acp')::INT
500                             OFFSET ($7 -> 'acp')::INT
501                         )x)
502                      )
503                  ELSE NULL END
504              );
505 $F$ LANGUAGE SQL STABLE;
506
507 COMMIT;
508