LP#1719726: Baseline trigger definition consistency
[working/Evergreen.git] / Open-ILS / src / sql / Pg / 210.schema.serials.sql
1
2
3 DROP SCHEMA IF EXISTS serial CASCADE;
4
5 BEGIN;
6
7 CREATE SCHEMA serial;
8
9 CREATE TABLE serial.record_entry (
10         id              BIGSERIAL       PRIMARY KEY,
11         record          BIGINT          REFERENCES biblio.record_entry (id) ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED,
12         owning_lib      INT             NOT NULL DEFAULT 1 REFERENCES actor.org_unit (id) ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED,
13         creator         INT             NOT NULL DEFAULT 1,
14         editor          INT             NOT NULL DEFAULT 1,
15         source          INT,
16         create_date     TIMESTAMP WITH TIME ZONE        NOT NULL DEFAULT now(),
17         edit_date       TIMESTAMP WITH TIME ZONE        NOT NULL DEFAULT now(),
18         active          BOOL            NOT NULL DEFAULT TRUE,
19         deleted         BOOL            NOT NULL DEFAULT FALSE,
20         marc            TEXT,
21         last_xact_id    TEXT            NOT NULL
22 );
23 CREATE INDEX serial_record_entry_record_idx ON serial.record_entry ( record );
24 CREATE INDEX serial_record_entry_creator_idx ON serial.record_entry ( creator );
25 CREATE INDEX serial_record_entry_editor_idx ON serial.record_entry ( editor );
26 CREATE INDEX serial_record_entry_owning_lib_idx ON serial.record_entry ( owning_lib, deleted );
27 CREATE TRIGGER b_maintain_901 BEFORE INSERT OR UPDATE ON serial.record_entry FOR EACH ROW EXECUTE PROCEDURE evergreen.maintain_901();
28 CREATE TRIGGER c_maintain_control_numbers BEFORE INSERT OR UPDATE ON serial.record_entry FOR EACH ROW EXECUTE PROCEDURE evergreen.maintain_control_numbers();
29
30 CREATE RULE protect_mfhd_delete AS ON DELETE TO serial.record_entry DO INSTEAD UPDATE serial.record_entry SET deleted = true WHERE old.id = serial.record_entry.id;
31
32 CREATE TABLE serial.subscription (
33         id                     SERIAL       PRIMARY KEY,
34         owning_lib             INT          NOT NULL DEFAULT 1 REFERENCES actor.org_unit (id) ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED,
35         start_date             TIMESTAMP WITH TIME ZONE     NOT NULL,
36         end_date               TIMESTAMP WITH TIME ZONE,    -- interpret NULL as current subscription
37         record_entry           BIGINT       REFERENCES biblio.record_entry (id)
38                                             ON DELETE SET NULL
39                                             DEFERRABLE INITIALLY DEFERRED,
40         expected_date_offset   INTERVAL
41         -- acquisitions/business-side tables link to here
42 );
43 CREATE INDEX serial_subscription_record_idx ON serial.subscription (record_entry);
44 CREATE INDEX serial_subscription_owner_idx ON serial.subscription (owning_lib);
45
46 CREATE TABLE serial.subscription_note (
47         id           SERIAL PRIMARY KEY,
48         subscription INT    NOT NULL
49                             REFERENCES serial.subscription (id)
50                             ON DELETE CASCADE
51                             DEFERRABLE INITIALLY DEFERRED,
52         creator      INT    NOT NULL
53                             REFERENCES actor.usr (id)
54                             DEFERRABLE INITIALLY DEFERRED,
55         create_date  TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
56         pub          BOOL   NOT NULL DEFAULT FALSE,
57         alert        BOOL   NOT NULL DEFAULT FALSE,
58         title        TEXT   NOT NULL,
59         value        TEXT   NOT NULL
60 );
61 CREATE INDEX serial_subscription_note_sub_idx ON serial.subscription_note (subscription);
62
63 CREATE TABLE serial.caption_and_pattern (
64         id           SERIAL       PRIMARY KEY,
65         subscription INT          NOT NULL
66                                   REFERENCES serial.subscription (id)
67                                   ON DELETE CASCADE
68                                   DEFERRABLE INITIALLY DEFERRED,
69         type         TEXT         NOT NULL
70                                   CONSTRAINT cap_type CHECK ( type in
71                                   ( 'basic', 'supplement', 'index' )),
72         create_date  TIMESTAMPTZ  NOT NULL DEFAULT now(),
73         start_date   TIMESTAMPTZ  NOT NULL DEFAULT now(),
74         end_date     TIMESTAMPTZ,
75         active       BOOL         NOT NULL DEFAULT FALSE,
76         pattern_code TEXT         NOT NULL,       -- must contain JSON
77         enum_1       TEXT,
78         enum_2       TEXT,
79         enum_3       TEXT,
80         enum_4       TEXT,
81         enum_5       TEXT,
82         enum_6       TEXT,
83         chron_1      TEXT,
84         chron_2      TEXT,
85         chron_3      TEXT,
86         chron_4      TEXT,
87         chron_5      TEXT
88 );
89 CREATE INDEX serial_caption_and_pattern_sub_idx ON serial.caption_and_pattern (subscription);
90
91 --at least one distribution per org_unit holding issues
92 CREATE TABLE serial.distribution (
93         id                    SERIAL  PRIMARY KEY,
94         record_entry          BIGINT  REFERENCES serial.record_entry (id)
95                                                                   ON DELETE SET NULL
96                                                                   DEFERRABLE INITIALLY DEFERRED,
97         summary_method        TEXT    CONSTRAINT sdist_summary_method_check
98                                       CHECK (summary_method IS NULL
99                                       OR summary_method IN ( 'add_to_sre',
100                                       'merge_with_sre', 'use_sre_only',
101                                       'use_sdist_only')),
102         subscription          INT     NOT NULL
103                                       REFERENCES serial.subscription (id)
104                                                                   ON DELETE CASCADE
105                                                                   DEFERRABLE INITIALLY DEFERRED,
106         holding_lib           INT     NOT NULL
107                                       REFERENCES actor.org_unit (id)
108                                                                   DEFERRABLE INITIALLY DEFERRED,
109         label                 TEXT    NOT NULL,
110         display_grouping      TEXT    NOT NULL DEFAULT 'chron'
111                                       CHECK (display_grouping IN ('enum', 'chron')),
112         receive_call_number   BIGINT  REFERENCES asset.call_number (id)
113                                       DEFERRABLE INITIALLY DEFERRED,
114         receive_unit_template INT     REFERENCES asset.copy_template (id)
115                                       DEFERRABLE INITIALLY DEFERRED,
116         bind_call_number      BIGINT  REFERENCES asset.call_number (id)
117                                       DEFERRABLE INITIALLY DEFERRED,
118         bind_unit_template    INT     REFERENCES asset.copy_template (id)
119                                       DEFERRABLE INITIALLY DEFERRED,
120         unit_label_prefix     TEXT,
121         unit_label_suffix     TEXT
122 );
123 CREATE UNIQUE INDEX one_dist_per_sre_idx ON serial.distribution (record_entry);
124 CREATE INDEX serial_distribution_sub_idx ON serial.distribution (subscription);
125 CREATE INDEX serial_distribution_holding_lib_idx ON serial.distribution (holding_lib);
126
127 CREATE TABLE serial.distribution_note (
128         id           SERIAL PRIMARY KEY,
129         distribution INT    NOT NULL
130                             REFERENCES serial.distribution (id)
131                             ON DELETE CASCADE
132                             DEFERRABLE INITIALLY DEFERRED,
133         creator      INT    NOT NULL
134                             REFERENCES actor.usr (id)
135                             DEFERRABLE INITIALLY DEFERRED,
136         create_date  TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
137         pub          BOOL   NOT NULL DEFAULT FALSE,
138         alert        BOOL   NOT NULL DEFAULT FALSE,
139         title        TEXT   NOT NULL,
140         value        TEXT   NOT NULL
141 );
142 CREATE INDEX serial_distribution_note_dist_idx ON serial.distribution_note (distribution);
143
144 CREATE TABLE serial.stream (
145         id              SERIAL  PRIMARY KEY,
146         distribution    INT     NOT NULL
147                                 REFERENCES serial.distribution (id)
148                                 ON DELETE CASCADE
149                                 DEFERRABLE INITIALLY DEFERRED,
150         routing_label   TEXT
151 );
152 CREATE INDEX serial_stream_dist_idx ON serial.stream (distribution);
153
154 CREATE UNIQUE INDEX label_once_per_dist
155         ON serial.stream (distribution, routing_label)
156         WHERE routing_label IS NOT NULL;
157
158 CREATE TABLE serial.routing_list_user (
159         id             SERIAL       PRIMARY KEY,
160         stream         INT          NOT NULL
161                                     REFERENCES serial.stream
162                                     ON DELETE CASCADE
163                                     DEFERRABLE INITIALLY DEFERRED,
164         pos            INT          NOT NULL DEFAULT 1,
165         reader         INT          REFERENCES actor.usr
166                                     ON DELETE CASCADE
167                                     DEFERRABLE INITIALLY DEFERRED,
168         department     TEXT,
169         note           TEXT,
170         CONSTRAINT one_pos_per_routing_list UNIQUE ( stream, pos ),
171         CONSTRAINT reader_or_dept CHECK
172         (
173             -- Recipient is a person or a department, but not both
174                 (reader IS NOT NULL AND department IS NULL) OR
175                 (reader IS NULL AND department IS NOT NULL)
176         )
177 );
178 CREATE INDEX serial_routing_list_user_stream_idx ON serial.routing_list_user (stream);
179 CREATE INDEX serial_routing_list_user_reader_idx ON serial.routing_list_user (reader);
180
181 CREATE TABLE serial.issuance (
182         id              SERIAL    PRIMARY KEY,
183         creator         INT       NOT NULL
184                                   REFERENCES actor.usr (id)
185                                                           DEFERRABLE INITIALLY DEFERRED,
186         editor          INT       NOT NULL
187                                   REFERENCES actor.usr (id)
188                                   DEFERRABLE INITIALLY DEFERRED,
189         create_date     TIMESTAMP WITH TIME ZONE        NOT NULL DEFAULT now(),
190         edit_date       TIMESTAMP WITH TIME ZONE        NOT NULL DEFAULT now(),
191         subscription    INT       NOT NULL
192                                   REFERENCES serial.subscription (id)
193                                   ON DELETE CASCADE
194                                   DEFERRABLE INITIALLY DEFERRED,
195         label           TEXT,
196         date_published  TIMESTAMP WITH TIME ZONE,
197         caption_and_pattern INT   REFERENCES serial.caption_and_pattern (id)
198                               ON DELETE CASCADE
199                                   DEFERRABLE INITIALLY DEFERRED,
200         holding_code    TEXT      CONSTRAINT issuance_holding_code_check CHECK (
201                                     holding_code IS NULL OR could_be_serial_holding_code(holding_code)
202                                   ),
203         holding_type    TEXT      CONSTRAINT valid_holding_type CHECK
204                                   (
205                                       holding_type IS NULL
206                                       OR holding_type IN ('basic','supplement','index')
207                                   ),
208         holding_link_id INT -- probably defunct
209         -- TODO: add columns for separate enumeration/chronology values
210 );
211 ALTER TABLE serial.issuance ADD CHECK (holding_code IS NULL OR evergreen.is_json(holding_code));
212 CREATE INDEX serial_issuance_sub_idx ON serial.issuance (subscription);
213 CREATE INDEX serial_issuance_caption_and_pattern_idx ON serial.issuance (caption_and_pattern);
214 CREATE INDEX serial_issuance_date_published_idx ON serial.issuance (date_published);
215
216 CREATE TABLE serial.unit (
217         sort_key          TEXT,
218         detailed_contents TEXT    NOT NULL,
219         summary_contents  TEXT    NOT NULL
220 ) INHERITS (asset.copy);
221 ALTER TABLE serial.unit ADD PRIMARY KEY (id);
222 CREATE UNIQUE INDEX unit_barcode_key ON serial.unit (barcode) WHERE deleted = FALSE OR deleted IS FALSE;
223 CREATE INDEX unit_cn_idx ON serial.unit (call_number);
224 CREATE INDEX unit_avail_cn_idx ON serial.unit (call_number);
225 CREATE INDEX unit_creator_idx  ON serial.unit ( creator );
226 CREATE INDEX unit_editor_idx   ON serial.unit ( editor );
227
228 -- must create this rule explicitly; it is not inherited from asset.copy
229 CREATE RULE protect_serial_unit_delete AS ON DELETE TO serial.unit DO INSTEAD UPDATE serial.unit SET deleted = TRUE WHERE OLD.id = serial.unit.id;
230
231 -- must create this trigger explicitly; it is not inherited from asset.copy
232 CREATE TRIGGER autogenerate_placeholder_barcode
233    BEFORE INSERT OR UPDATE ON serial.unit 
234    FOR EACH ROW EXECUTE PROCEDURE asset.autogenerate_placeholder_barcode()
235 ;
236
237 -- must create this trigger explicitly; it is not inherited from asset.copy
238 CREATE TRIGGER sunit_status_changed_trig
239     BEFORE UPDATE ON serial.unit
240     FOR EACH ROW EXECUTE PROCEDURE asset.acp_status_changed();
241
242 -- ditto
243 CREATE TRIGGER sunit_created_trig
244     BEFORE INSERT ON serial.unit
245     FOR EACH ROW EXECUTE PROCEDURE asset.acp_created();
246
247 CREATE TABLE serial.item (
248         id              SERIAL  PRIMARY KEY,
249         creator         INT     NOT NULL
250                                 REFERENCES actor.usr (id)
251                                 DEFERRABLE INITIALLY DEFERRED,
252         editor          INT     NOT NULL
253                                 REFERENCES actor.usr (id)
254                                 DEFERRABLE INITIALLY DEFERRED,
255         create_date     TIMESTAMP WITH TIME ZONE        NOT NULL DEFAULT now(),
256         edit_date       TIMESTAMP WITH TIME ZONE        NOT NULL DEFAULT now(),
257         issuance        INT     NOT NULL
258                                 REFERENCES serial.issuance (id)
259                                 ON DELETE CASCADE
260                                 DEFERRABLE INITIALLY DEFERRED,
261         stream          INT     NOT NULL
262                                 REFERENCES serial.stream (id)
263                                 ON DELETE CASCADE
264                                 DEFERRABLE INITIALLY DEFERRED,
265         unit            INT     REFERENCES serial.unit (id)
266                                 ON DELETE SET NULL
267                                 DEFERRABLE INITIALLY DEFERRED,
268         uri             INT     REFERENCES asset.uri (id)
269                                 ON DELETE SET NULL
270                                 DEFERRABLE INITIALLY DEFERRED,
271         date_expected   TIMESTAMP WITH TIME ZONE,
272         date_received   TIMESTAMP WITH TIME ZONE,
273         status          TEXT    CONSTRAINT valid_status CHECK
274                                 (
275                                     status IN ('Bindery', 'Bound', 'Claimed', 'Discarded', 'Expected', 'Not Held', 'Not Published', 'Received')
276                                 ) DEFAULT 'Expected',
277         shadowed        BOOL    NOT NULL DEFAULT FALSE -- ignore when generating summaries/labels
278 );
279 CREATE INDEX serial_item_stream_idx ON serial.item (stream);
280 CREATE INDEX serial_item_issuance_idx ON serial.item (issuance);
281 CREATE INDEX serial_item_unit_idx ON serial.item (unit);
282 CREATE INDEX serial_item_uri_idx ON serial.item (uri);
283 CREATE INDEX serial_item_date_received_idx ON serial.item (date_received);
284 CREATE INDEX serial_item_status_idx ON serial.item (status);
285
286 CREATE TABLE serial.item_note (
287         id          SERIAL  PRIMARY KEY,
288         item        INT     NOT NULL
289                             REFERENCES serial.item (id)
290                             ON DELETE CASCADE
291                             DEFERRABLE INITIALLY DEFERRED,
292         creator     INT     NOT NULL
293                             REFERENCES actor.usr (id)
294                             DEFERRABLE INITIALLY DEFERRED,
295         create_date TIMESTAMP WITH TIME ZONE    DEFAULT NOW(),
296         pub         BOOL    NOT NULL    DEFAULT FALSE,
297         alert       BOOL    NOT NULL    DEFAULT FALSE,
298         title       TEXT    NOT NULL,
299         value       TEXT    NOT NULL
300 );
301 CREATE INDEX serial_item_note_item_idx ON serial.item_note (item);
302
303 CREATE TABLE serial.basic_summary (
304         id                  SERIAL  PRIMARY KEY,
305         distribution        INT     NOT NULL
306                                     REFERENCES serial.distribution (id)
307                                     ON DELETE CASCADE
308                                     DEFERRABLE INITIALLY DEFERRED,
309         generated_coverage  TEXT    NOT NULL,
310         textual_holdings    TEXT,
311         show_generated      BOOL    NOT NULL DEFAULT TRUE
312 );
313 CREATE INDEX serial_basic_summary_dist_idx ON serial.basic_summary (distribution);
314
315 CREATE TABLE serial.supplement_summary (
316         id                  SERIAL  PRIMARY KEY,
317         distribution        INT     NOT NULL
318                                     REFERENCES serial.distribution (id)
319                                     ON DELETE CASCADE
320                                     DEFERRABLE INITIALLY DEFERRED,
321         generated_coverage  TEXT    NOT NULL,
322         textual_holdings    TEXT,
323         show_generated      BOOL    NOT NULL DEFAULT TRUE
324 );
325 CREATE INDEX serial_supplement_summary_dist_idx ON serial.supplement_summary (distribution);
326
327 CREATE TABLE serial.index_summary (
328         id                  SERIAL  PRIMARY KEY,
329         distribution        INT     NOT NULL
330                                     REFERENCES serial.distribution (id)
331                                     ON DELETE CASCADE
332                                     DEFERRABLE INITIALLY DEFERRED,
333         generated_coverage  TEXT    NOT NULL,
334         textual_holdings    TEXT,
335         show_generated      BOOL    NOT NULL DEFAULT TRUE
336 );
337 CREATE INDEX serial_index_summary_dist_idx ON serial.index_summary (distribution);
338
339 CREATE VIEW serial.any_summary AS
340     SELECT
341         'basic' AS summary_type, id, distribution,
342         generated_coverage, textual_holdings, show_generated
343     FROM serial.basic_summary
344     UNION
345     SELECT
346         'index' AS summary_type, id, distribution,
347         generated_coverage, textual_holdings, show_generated
348     FROM serial.index_summary
349     UNION
350     SELECT
351         'supplement' AS summary_type, id, distribution,
352         generated_coverage, textual_holdings, show_generated
353     FROM serial.supplement_summary ;
354
355
356 CREATE TABLE serial.materialized_holding_code (
357     id BIGSERIAL PRIMARY KEY,
358     issuance INTEGER NOT NULL REFERENCES serial.issuance (id) ON DELETE CASCADE,
359     subfield CHAR,
360     value TEXT
361 );
362
363 CREATE OR REPLACE FUNCTION serial.materialize_holding_code() RETURNS TRIGGER
364 AS $func$ 
365 use strict;
366
367 use MARC::Field;
368 use JSON::XS;
369
370 if (not defined $_TD->{new}{holding_code}) {
371     elog(WARNING, 'NULL in "holding_code" column of serial.issuance allowed for now, but may not be useful');
372     return;
373 }
374
375 # Do nothing if holding_code has not changed...
376
377 if ($_TD->{new}{holding_code} eq $_TD->{old}{holding_code}) {
378     # ... unless the following internal flag is set.
379
380     my $flag_rv = spi_exec_query(q{
381         SELECT * FROM config.internal_flag
382         WHERE name = 'serial.rematerialize_on_same_holding_code' AND enabled
383     }, 1);
384     return unless $flag_rv->{processed};
385 }
386
387
388 my $holding_code = (new JSON::XS)->decode($_TD->{new}{holding_code});
389
390 my $field = new MARC::Field('999', @$holding_code); # tag doesnt matter
391
392 my $dstmt = spi_prepare(
393     'DELETE FROM serial.materialized_holding_code WHERE issuance = $1',
394     'INT'
395 );
396 spi_exec_prepared($dstmt, $_TD->{new}{id});
397
398 my $istmt = spi_prepare(
399     q{
400         INSERT INTO serial.materialized_holding_code (
401             issuance, subfield, value
402         ) VALUES ($1, $2, $3)
403     }, qw{INT CHAR TEXT}
404 );
405
406 foreach ($field->subfields) {
407     spi_exec_prepared(
408         $istmt,
409         $_TD->{new}{id},
410         $_->[0],
411         $_->[1]
412     );
413 }
414
415 return;
416
417 $func$ LANGUAGE 'plperlu';
418
419 CREATE INDEX assist_holdings_display
420     ON serial.materialized_holding_code (issuance, subfield);
421
422 CREATE TRIGGER materialize_holding_code
423     AFTER INSERT OR UPDATE ON serial.issuance
424     FOR EACH ROW EXECUTE PROCEDURE serial.materialize_holding_code() ;
425
426 CREATE TABLE serial.pattern_template (
427     id            SERIAL PRIMARY KEY,
428     name          TEXT NOT NULL,
429     pattern_code  TEXT NOT NULL,
430     owning_lib    INTEGER REFERENCES actor.org_unit(id) DEFERRABLE INITIALLY DEFERRED,
431     share_depth   INTEGER NOT NULL DEFAULT 0
432 );
433 CREATE INDEX serial_pattern_template_name_idx ON serial.pattern_template (evergreen.lowercase(name));
434
435 CREATE OR REPLACE FUNCTION serial.pattern_templates_visible_to(org_unit INT) RETURNS SETOF serial.pattern_template AS $func$
436 BEGIN
437     RETURN QUERY SELECT *
438            FROM serial.pattern_template spt
439            WHERE (
440              SELECT ARRAY_AGG(id)
441              FROM actor.org_unit_descendants(spt.owning_lib, spt.share_depth)
442            ) @@ org_unit::TEXT::QUERY_INT;
443 END;
444 $func$ LANGUAGE PLPGSQL;
445
446 COMMIT;
447