LP#800706: add index to serial.record_entry.record
[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 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                                   DEFERRABLE INITIALLY DEFERRED,
199         holding_code    TEXT      CONSTRAINT issuance_holding_code_check CHECK (
200                                     holding_code IS NULL OR could_be_serial_holding_code(holding_code)
201                                   ),
202         holding_type    TEXT      CONSTRAINT valid_holding_type CHECK
203                                   (
204                                       holding_type IS NULL
205                                       OR holding_type IN ('basic','supplement','index')
206                                   ),
207         holding_link_id INT -- probably defunct
208         -- TODO: add columns for separate enumeration/chronology values
209 );
210 ALTER TABLE serial.issuance ADD CHECK (holding_code IS NULL OR evergreen.is_json(holding_code));
211 CREATE INDEX serial_issuance_sub_idx ON serial.issuance (subscription);
212 CREATE INDEX serial_issuance_caption_and_pattern_idx ON serial.issuance (caption_and_pattern);
213 CREATE INDEX serial_issuance_date_published_idx ON serial.issuance (date_published);
214
215 CREATE TABLE serial.unit (
216         sort_key          TEXT,
217         detailed_contents TEXT    NOT NULL,
218         summary_contents  TEXT    NOT NULL
219 ) INHERITS (asset.copy);
220 ALTER TABLE serial.unit ADD PRIMARY KEY (id);
221 CREATE UNIQUE INDEX unit_barcode_key ON serial.unit (barcode) WHERE deleted = FALSE OR deleted IS FALSE;
222 CREATE INDEX unit_cn_idx ON serial.unit (call_number);
223 CREATE INDEX unit_avail_cn_idx ON serial.unit (call_number);
224 CREATE INDEX unit_creator_idx  ON serial.unit ( creator );
225 CREATE INDEX unit_editor_idx   ON serial.unit ( editor );
226
227 -- must create this rule explicitly; it is not inherited from asset.copy
228 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;
229
230 -- must create this trigger explicitly; it is not inherited from asset.copy
231 CREATE TRIGGER autogenerate_placeholder_barcode
232    BEFORE INSERT OR UPDATE ON serial.unit 
233    FOR EACH ROW EXECUTE PROCEDURE asset.autogenerate_placeholder_barcode()
234 ;
235
236 -- must create this trigger explicitly; it is not inherited from asset.copy
237 CREATE TRIGGER sunit_status_changed_trig
238     BEFORE UPDATE ON serial.unit
239     FOR EACH ROW EXECUTE PROCEDURE asset.acp_status_changed();
240
241 -- ditto
242 CREATE TRIGGER sunit_created_trig
243     BEFORE INSERT ON serial.unit
244     FOR EACH ROW EXECUTE PROCEDURE asset.acp_created();
245
246 CREATE TABLE serial.item (
247         id              SERIAL  PRIMARY KEY,
248         creator         INT     NOT NULL
249                                 REFERENCES actor.usr (id)
250                                 DEFERRABLE INITIALLY DEFERRED,
251         editor          INT     NOT NULL
252                                 REFERENCES actor.usr (id)
253                                 DEFERRABLE INITIALLY DEFERRED,
254         create_date     TIMESTAMP WITH TIME ZONE        NOT NULL DEFAULT now(),
255         edit_date       TIMESTAMP WITH TIME ZONE        NOT NULL DEFAULT now(),
256         issuance        INT     NOT NULL
257                                 REFERENCES serial.issuance (id)
258                                 ON DELETE CASCADE
259                                 DEFERRABLE INITIALLY DEFERRED,
260         stream          INT     NOT NULL
261                                 REFERENCES serial.stream (id)
262                                 ON DELETE CASCADE
263                                 DEFERRABLE INITIALLY DEFERRED,
264         unit            INT     REFERENCES serial.unit (id)
265                                 ON DELETE SET NULL
266                                 DEFERRABLE INITIALLY DEFERRED,
267         uri             INT     REFERENCES asset.uri (id)
268                                 ON DELETE SET NULL
269                                 DEFERRABLE INITIALLY DEFERRED,
270         date_expected   TIMESTAMP WITH TIME ZONE,
271         date_received   TIMESTAMP WITH TIME ZONE,
272         status          TEXT    CONSTRAINT valid_status CHECK
273                                 (
274                                     status IN ('Bindery', 'Bound', 'Claimed', 'Discarded', 'Expected', 'Not Held', 'Not Published', 'Received')
275                                 ) DEFAULT 'Expected',
276         shadowed        BOOL    NOT NULL DEFAULT FALSE -- ignore when generating summaries/labels
277 );
278 CREATE INDEX serial_item_stream_idx ON serial.item (stream);
279 CREATE INDEX serial_item_issuance_idx ON serial.item (issuance);
280 CREATE INDEX serial_item_unit_idx ON serial.item (unit);
281 CREATE INDEX serial_item_uri_idx ON serial.item (uri);
282 CREATE INDEX serial_item_date_received_idx ON serial.item (date_received);
283 CREATE INDEX serial_item_status_idx ON serial.item (status);
284
285 CREATE TABLE serial.item_note (
286         id          SERIAL  PRIMARY KEY,
287         item        INT     NOT NULL
288                             REFERENCES serial.item (id)
289                             ON DELETE CASCADE
290                             DEFERRABLE INITIALLY DEFERRED,
291         creator     INT     NOT NULL
292                             REFERENCES actor.usr (id)
293                             DEFERRABLE INITIALLY DEFERRED,
294         create_date TIMESTAMP WITH TIME ZONE    DEFAULT NOW(),
295         pub         BOOL    NOT NULL    DEFAULT FALSE,
296         alert       BOOL    NOT NULL    DEFAULT FALSE,
297         title       TEXT    NOT NULL,
298         value       TEXT    NOT NULL
299 );
300 CREATE INDEX serial_item_note_item_idx ON serial.item_note (item);
301
302 CREATE TABLE serial.basic_summary (
303         id                  SERIAL  PRIMARY KEY,
304         distribution        INT     NOT NULL
305                                     REFERENCES serial.distribution (id)
306                                     ON DELETE CASCADE
307                                     DEFERRABLE INITIALLY DEFERRED,
308         generated_coverage  TEXT    NOT NULL,
309         textual_holdings    TEXT,
310         show_generated      BOOL    NOT NULL DEFAULT TRUE
311 );
312 CREATE INDEX serial_basic_summary_dist_idx ON serial.basic_summary (distribution);
313
314 CREATE TABLE serial.supplement_summary (
315         id                  SERIAL  PRIMARY KEY,
316         distribution        INT     NOT NULL
317                                     REFERENCES serial.distribution (id)
318                                     ON DELETE CASCADE
319                                     DEFERRABLE INITIALLY DEFERRED,
320         generated_coverage  TEXT    NOT NULL,
321         textual_holdings    TEXT,
322         show_generated      BOOL    NOT NULL DEFAULT TRUE
323 );
324 CREATE INDEX serial_supplement_summary_dist_idx ON serial.supplement_summary (distribution);
325
326 CREATE TABLE serial.index_summary (
327         id                  SERIAL  PRIMARY KEY,
328         distribution        INT     NOT NULL
329                                     REFERENCES serial.distribution (id)
330                                     ON DELETE CASCADE
331                                     DEFERRABLE INITIALLY DEFERRED,
332         generated_coverage  TEXT    NOT NULL,
333         textual_holdings    TEXT,
334         show_generated      BOOL    NOT NULL DEFAULT TRUE
335 );
336 CREATE INDEX serial_index_summary_dist_idx ON serial.index_summary (distribution);
337
338 CREATE VIEW serial.any_summary AS
339     SELECT
340         'basic' AS summary_type, id, distribution,
341         generated_coverage, textual_holdings, show_generated
342     FROM serial.basic_summary
343     UNION
344     SELECT
345         'index' AS summary_type, id, distribution,
346         generated_coverage, textual_holdings, show_generated
347     FROM serial.index_summary
348     UNION
349     SELECT
350         'supplement' AS summary_type, id, distribution,
351         generated_coverage, textual_holdings, show_generated
352     FROM serial.supplement_summary ;
353
354
355 CREATE TABLE serial.materialized_holding_code (
356     id BIGSERIAL PRIMARY KEY,
357     issuance INTEGER NOT NULL REFERENCES serial.issuance (id) ON DELETE CASCADE,
358     subfield CHAR,
359     value TEXT
360 );
361
362 CREATE OR REPLACE FUNCTION serial.materialize_holding_code() RETURNS TRIGGER
363 AS $func$ 
364 use strict;
365
366 use MARC::Field;
367 use JSON::XS;
368
369 if (not defined $_TD->{new}{holding_code}) {
370     elog(WARNING, 'NULL in "holding_code" column of serial.issuance allowed for now, but may not be useful');
371     return;
372 }
373
374 # Do nothing if holding_code has not changed...
375
376 if ($_TD->{new}{holding_code} eq $_TD->{old}{holding_code}) {
377     # ... unless the following internal flag is set.
378
379     my $flag_rv = spi_exec_query(q{
380         SELECT * FROM config.internal_flag
381         WHERE name = 'serial.rematerialize_on_same_holding_code' AND enabled
382     }, 1);
383     return unless $flag_rv->{processed};
384 }
385
386
387 my $holding_code = (new JSON::XS)->decode($_TD->{new}{holding_code});
388
389 my $field = new MARC::Field('999', @$holding_code); # tag doesnt matter
390
391 my $dstmt = spi_prepare(
392     'DELETE FROM serial.materialized_holding_code WHERE issuance = $1',
393     'INT'
394 );
395 spi_exec_prepared($dstmt, $_TD->{new}{id});
396
397 my $istmt = spi_prepare(
398     q{
399         INSERT INTO serial.materialized_holding_code (
400             issuance, subfield, value
401         ) VALUES ($1, $2, $3)
402     }, qw{INT CHAR TEXT}
403 );
404
405 foreach ($field->subfields) {
406     spi_exec_prepared(
407         $istmt,
408         $_TD->{new}{id},
409         $_->[0],
410         $_->[1]
411     );
412 }
413
414 return;
415
416 $func$ LANGUAGE 'plperlu';
417
418 CREATE INDEX assist_holdings_display
419     ON serial.materialized_holding_code (issuance, subfield);
420
421 CREATE TRIGGER materialize_holding_code
422     AFTER INSERT OR UPDATE ON serial.issuance
423     FOR EACH ROW EXECUTE PROCEDURE serial.materialize_holding_code() ;
424 COMMIT;
425