]> git.evergreen-ils.org Git - working/Evergreen.git/blob - Open-ILS/src/sql/Pg/012.schema.vandelay.sql
dbdd1575c953b40967dcebe294ef8e2a5c947f54
[working/Evergreen.git] / Open-ILS / src / sql / Pg / 012.schema.vandelay.sql
1 DROP SCHEMA vandelay CASCADE;
2
3 BEGIN;
4
5 CREATE SCHEMA vandelay;
6
7 CREATE TABLE vandelay.queue (
8         id                              BIGSERIAL       PRIMARY KEY,
9         owner                   INT                     NOT NULL REFERENCES actor.usr (id) DEFERRABLE INITIALLY DEFERRED,
10         name                    TEXT            NOT NULL,
11         complete                BOOL            NOT NULL DEFAULT FALSE,
12         queue_type              TEXT            NOT NULL DEFAULT 'bib' CHECK (queue_type IN ('bib','authority')),
13         CONSTRAINT vand_queue_name_once_per_owner_const UNIQUE (owner,name,queue_type)
14 );
15
16 CREATE TABLE vandelay.queued_record (
17     id                  BIGSERIAL                   PRIMARY KEY,
18     create_time TIMESTAMP WITH TIME ZONE    NOT NULL DEFAULT NOW(),
19     import_time TIMESTAMP WITH TIME ZONE,
20         purpose         TEXT                                            NOT NULL DEFAULT 'import' CHECK (purpose IN ('import','overlay')),
21     marc                TEXT                        NOT NULL
22 );
23
24
25
26 /* Bib stuff at the top */
27 ----------------------------------------------------
28
29 CREATE TABLE vandelay.bib_attr_definition (
30         id                      SERIAL  PRIMARY KEY,
31         code            TEXT    UNIQUE NOT NULL,
32         description     TEXT,
33         xpath           TEXT    NOT NULL,
34         remove          TEXT    NOT NULL DEFAULT '',
35         ident           BOOL    NOT NULL DEFAULT FALSE
36 );
37
38 -- Each TEXT field (other than 'name') should hold an XPath predicate for pulling the data needed
39 -- DROP TABLE vandelay.import_item_attr_definition CASCADE;
40 CREATE TABLE vandelay.import_item_attr_definition (
41     id              BIGSERIAL   PRIMARY KEY,
42     owner           INT         NOT NULL REFERENCES actor.org_unit (id) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
43     name            TEXT        NOT NULL,
44     tag             TEXT        NOT NULL,
45     keep            BOOL        NOT NULL DEFAULT FALSE,
46     owning_lib      TEXT,
47     circ_lib        TEXT,
48     call_number     TEXT,
49     copy_number     TEXT,
50     status          TEXT,
51     location        TEXT,
52     circulate       TEXT,
53     deposit         TEXT,
54     deposit_amount  TEXT,
55     ref             TEXT,
56     holdable        TEXT,
57     price           TEXT,
58     barcode         TEXT,
59     circ_modifier   TEXT,
60     circ_as_type    TEXT,
61     alert_message   TEXT,
62     opac_visible    TEXT,
63     pub_note_title  TEXT,
64     pub_note        TEXT,
65     priv_note_title TEXT,
66     priv_note       TEXT,
67         CONSTRAINT vand_import_item_attr_def_idx UNIQUE (owner,name)
68 );
69
70 CREATE TABLE vandelay.bib_queue (
71         queue_type          TEXT        NOT NULL DEFAULT 'bib' CHECK (queue_type = 'bib'),
72         item_attr_def   BIGINT REFERENCES vandelay.import_item_attr_definition (id) ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED,
73         CONSTRAINT vand_bib_queue_name_once_per_owner_const UNIQUE (owner,name,queue_type)
74 ) INHERITS (vandelay.queue);
75 ALTER TABLE vandelay.bib_queue ADD PRIMARY KEY (id);
76
77 CREATE TABLE vandelay.queued_bib_record (
78         queue           INT             NOT NULL REFERENCES vandelay.bib_queue (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
79         bib_source      INT             REFERENCES config.bib_source (id) DEFERRABLE INITIALLY DEFERRED,
80         imported_as     INT             REFERENCES biblio.record_entry (id) DEFERRABLE INITIALLY DEFERRED
81 ) INHERITS (vandelay.queued_record);
82 ALTER TABLE vandelay.queued_bib_record ADD PRIMARY KEY (id);
83
84 CREATE TABLE vandelay.queued_bib_record_attr (
85         id                      BIGSERIAL       PRIMARY KEY,
86         record          BIGINT          NOT NULL REFERENCES vandelay.queued_bib_record (id) DEFERRABLE INITIALLY DEFERRED,
87         field           INT                     NOT NULL REFERENCES vandelay.bib_attr_definition (id) DEFERRABLE INITIALLY DEFERRED,
88         attr_value      TEXT            NOT NULL
89 );
90
91 CREATE TABLE vandelay.bib_match (
92         id                              BIGSERIAL       PRIMARY KEY,
93         field_type              TEXT            NOT NULL CHECK (field_type in ('isbn','tcn_value','id')),
94         matched_attr    INT                     REFERENCES vandelay.queued_bib_record_attr (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
95         queued_record   BIGINT          REFERENCES vandelay.queued_bib_record (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
96         eg_record               BIGINT          REFERENCES biblio.record_entry (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED
97 );
98
99 -- DROP TABLE vandelay.import_item CASCADE;
100 CREATE TABLE vandelay.import_item (
101     id              BIGSERIAL   PRIMARY KEY,
102     record          BIGINT      NOT NULL REFERENCES vandelay.queued_bib_record (id) ON DELETE CASCADE,
103     definition      BIGINT      NOT NULL REFERENCES vandelay.import_item_attr_definition (id) ON DELETE CASCADE,
104     owning_lib      INT,
105     circ_lib        INT,
106     call_number     TEXT,
107     copy_number     INT,
108     status          INT,
109     location        INT,
110     circulate       BOOL,
111     deposit         BOOL,
112     deposit_amount  NUMERIC(8,2),
113     ref             BOOL,
114     holdable        BOOL,
115     price           NUMERIC(8,2),
116     barcode         TEXT,
117     circ_modifier   TEXT,
118     circ_as_type    TEXT,
119     alert_message   TEXT,
120     pub_note        TEXT,
121     priv_note       TEXT,
122     opac_visible    BOOL
123 );
124  
125 CREATE TABLE vandelay.import_bib_trash_fields (
126     id              BIGSERIAL   PRIMARY KEY,
127     owner           INT         NOT NULL REFERENCES actor.org_unit (id) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
128     field           TEXT        NOT NULL,
129         CONSTRAINT vand_import_bib_trash_fields_idx UNIQUE (owner,field)
130 );
131
132 CREATE OR REPLACE FUNCTION vandelay.strip_field ( xml TEXT, field TEXT ) RETURNS TEXT AS $_$
133
134     use MARC::Record;
135     use MARC::File::XML;
136
137     my $xml = shift;
138     my $field_spec = shift;
139
140     my $r = MARC::Record->new_from_xml( $xml );
141     $r->delete_field( $_ ) for ( $r->field( $field_spec ) );
142
143     $xml = $r->as_xml_record;
144     $xml =~ s/^<\?.+?\?>$//mo;
145     $xml =~ s/\n//sgo;
146     $xml =~ s/>\s+</></sgo;
147
148     return $xml;
149
150 $_$ LANGUAGE PLPERLU;
151
152
153 CREATE OR REPLACE FUNCTION vandelay.ingest_items ( import_id BIGINT, attr_def_id BIGINT ) RETURNS SETOF vandelay.import_item AS $$
154 DECLARE
155
156     owning_lib      TEXT;
157     circ_lib        TEXT;
158     call_number     TEXT;
159     copy_number     TEXT;
160     status          TEXT;
161     location        TEXT;
162     circulate       TEXT;
163     deposit         TEXT;
164     deposit_amount  TEXT;
165     ref             TEXT;
166     holdable        TEXT;
167     price           TEXT;
168     barcode         TEXT;
169     circ_modifier   TEXT;
170     circ_as_type    TEXT;
171     alert_message   TEXT;
172     opac_visible    TEXT;
173     pub_note        TEXT;
174     priv_note       TEXT;
175
176     attr_def        RECORD;
177     tmp_attr_set    RECORD;
178     attr_set        vandelay.import_item%ROWTYPE;
179
180     xpath           TEXT;
181
182 BEGIN
183
184     SELECT * INTO attr_def FROM vandelay.import_item_attr_definition WHERE id = attr_def_id;
185
186     IF FOUND THEN
187
188         attr_set.definition := attr_def.id; 
189     
190         -- Build the combined XPath
191     
192         owning_lib :=
193             CASE
194                 WHEN attr_def.owning_lib IS NULL THEN 'null()'
195                 WHEN LENGTH( attr_def.owning_lib ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.owning_lib || '"]'
196                 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.owning_lib
197             END;
198     
199         circ_lib :=
200             CASE
201                 WHEN attr_def.circ_lib IS NULL THEN 'null()'
202                 WHEN LENGTH( attr_def.circ_lib ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.circ_lib || '"]'
203                 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.circ_lib
204             END;
205     
206         call_number :=
207             CASE
208                 WHEN attr_def.call_number IS NULL THEN 'null()'
209                 WHEN LENGTH( attr_def.call_number ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.call_number || '"]'
210                 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.call_number
211             END;
212     
213         copy_number :=
214             CASE
215                 WHEN attr_def.copy_number IS NULL THEN 'null()'
216                 WHEN LENGTH( attr_def.copy_number ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.copy_number || '"]'
217                 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.copy_number
218             END;
219     
220         status :=
221             CASE
222                 WHEN attr_def.status IS NULL THEN 'null()'
223                 WHEN LENGTH( attr_def.status ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.status || '"]'
224                 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.status
225             END;
226     
227         location :=
228             CASE
229                 WHEN attr_def.location IS NULL THEN 'null()'
230                 WHEN LENGTH( attr_def.location ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.location || '"]'
231                 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.location
232             END;
233     
234         circulate :=
235             CASE
236                 WHEN attr_def.circulate IS NULL THEN 'null()'
237                 WHEN LENGTH( attr_def.circulate ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.circulate || '"]'
238                 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.circulate
239             END;
240     
241         deposit :=
242             CASE
243                 WHEN attr_def.deposit IS NULL THEN 'null()'
244                 WHEN LENGTH( attr_def.deposit ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.deposit || '"]'
245                 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.deposit
246             END;
247     
248         deposit_amount :=
249             CASE
250                 WHEN attr_def.deposit_amount IS NULL THEN 'null()'
251                 WHEN LENGTH( attr_def.deposit_amount ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.deposit_amount || '"]'
252                 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.deposit_amount
253             END;
254     
255         ref :=
256             CASE
257                 WHEN attr_def.ref IS NULL THEN 'null()'
258                 WHEN LENGTH( attr_def.ref ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.ref || '"]'
259                 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.ref
260             END;
261     
262         holdable :=
263             CASE
264                 WHEN attr_def.holdable IS NULL THEN 'null()'
265                 WHEN LENGTH( attr_def.holdable ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.holdable || '"]'
266                 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.holdable
267             END;
268     
269         price :=
270             CASE
271                 WHEN attr_def.price IS NULL THEN 'null()'
272                 WHEN LENGTH( attr_def.price ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.price || '"]'
273                 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.price
274             END;
275     
276         barcode :=
277             CASE
278                 WHEN attr_def.barcode IS NULL THEN 'null()'
279                 WHEN LENGTH( attr_def.barcode ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.barcode || '"]'
280                 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.barcode
281             END;
282     
283         circ_modifier :=
284             CASE
285                 WHEN attr_def.circ_modifier IS NULL THEN 'null()'
286                 WHEN LENGTH( attr_def.circ_modifier ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.circ_modifier || '"]'
287                 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.circ_modifier
288             END;
289     
290         circ_as_type :=
291             CASE
292                 WHEN attr_def.circ_as_type IS NULL THEN 'null()'
293                 WHEN LENGTH( attr_def.circ_as_type ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.circ_as_type || '"]'
294                 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.circ_as_type
295             END;
296     
297         alert_message :=
298             CASE
299                 WHEN attr_def.alert_message IS NULL THEN 'null()'
300                 WHEN LENGTH( attr_def.alert_message ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.alert_message || '"]'
301                 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.alert_message
302             END;
303     
304         opac_visible :=
305             CASE
306                 WHEN attr_def.opac_visible IS NULL THEN 'null()'
307                 WHEN LENGTH( attr_def.opac_visible ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.opac_visible || '"]'
308                 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.opac_visible
309             END;
310
311         pub_note :=
312             CASE
313                 WHEN attr_def.pub_note IS NULL THEN 'null()'
314                 WHEN LENGTH( attr_def.pub_note ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.pub_note || '"]'
315                 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.pub_note
316             END;
317         priv_note :=
318             CASE
319                 WHEN attr_def.priv_note IS NULL THEN 'null()'
320                 WHEN LENGTH( attr_def.priv_note ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.priv_note || '"]'
321                 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.priv_note
322             END;
323     
324     
325         xpath := 
326             owning_lib      || '|' || 
327             circ_lib        || '|' || 
328             call_number     || '|' || 
329             copy_number     || '|' || 
330             status          || '|' || 
331             location        || '|' || 
332             circulate       || '|' || 
333             deposit         || '|' || 
334             deposit_amount  || '|' || 
335             ref             || '|' || 
336             holdable        || '|' || 
337             price           || '|' || 
338             barcode         || '|' || 
339             circ_modifier   || '|' || 
340             circ_as_type    || '|' || 
341             alert_message   || '|' || 
342             pub_note        || '|' || 
343             priv_note       || '|' || 
344             opac_visible;
345
346         -- RAISE NOTICE 'XPath: %', xpath;
347         
348         FOR tmp_attr_set IN
349                 SELECT  *
350                   FROM  xpath_table( 'id', 'marc', 'vandelay.queued_bib_record', xpath, 'id = ' || import_id )
351                             AS t( id BIGINT, ol TEXT, clib TEXT, cn TEXT, cnum TEXT, cs TEXT, cl TEXT, circ TEXT,
352                                   dep TEXT, dep_amount TEXT, r TEXT, hold TEXT, pr TEXT, bc TEXT, circ_mod TEXT,
353                                   circ_as TEXT, amessage TEXT, note TEXT, pnote TEXT, opac_vis TEXT )
354         LOOP
355     
356             tmp_attr_set.pr = REGEXP_REPLACE(tmp_attr_set.pr, E'[^0-9\\.]', '', 'g');
357             tmp_attr_set.dep_amount = REGEXP_REPLACE(tmp_attr_set.dep_amount, E'[^0-9\\.]', '', 'g');
358
359             tmp_attr_set.pr := NULLIF( tmp_attr_set.pr, '' );
360             tmp_attr_set.dep_amount := NULLIF( tmp_attr_set.dep_amount, '' );
361     
362             SELECT id INTO attr_set.owning_lib FROM actor.org_unit WHERE shortname = UPPER(tmp_attr_set.ol); -- INT
363             SELECT id INTO attr_set.circ_lib FROM actor.org_unit WHERE shortname = UPPER(tmp_attr_set.clib); -- INT
364             SELECT id INTO attr_set.status FROM config.copy_status WHERE LOWER(name) = LOWER(tmp_attr_set.cs); -- INT
365     
366             SELECT  id INTO attr_set.location
367               FROM  asset.copy_location
368               WHERE LOWER(name) = LOWER(tmp_attr_set.cl)
369                     AND owning_lib = COALESCE(attr_set.owning_lib, attr_set.circ_lib); -- INT
370     
371             attr_set.circulate      :=
372                 LOWER( SUBSTRING( tmp_attr_set.circ, 1, 1)) IN ('t','y','1')
373                 OR LOWER(tmp_attr_set.circ) = 'circulating'; -- BOOL
374
375             attr_set.deposit        :=
376                 LOWER( SUBSTRING( tmp_attr_set.dep, 1, 1 ) ) IN ('t','y','1')
377                 OR LOWER(tmp_attr_set.dep) = 'deposit'; -- BOOL
378
379             attr_set.holdable       :=
380                 LOWER( SUBSTRING( tmp_attr_set.hold, 1, 1 ) ) IN ('t','y','1')
381                 OR LOWER(tmp_attr_set.hold) = 'holdable'; -- BOOL
382
383             attr_set.opac_visible   :=
384                 LOWER( SUBSTRING( tmp_attr_set.opac_vis, 1, 1 ) ) IN ('t','y','1')
385                 OR LOWER(tmp_attr_set.opac_vis) = 'visible'; -- BOOL
386
387             attr_set.ref            :=
388                 LOWER( SUBSTRING( tmp_attr_set.r, 1, 1 ) ) IN ('t','y','1')
389                 OR LOWER(tmp_attr_set.r) = 'reference'; -- BOOL
390     
391             attr_set.copy_number    := tmp_attr_set.cnum::INT; -- INT,
392             attr_set.deposit_amount := tmp_attr_set.dep_amount::NUMERIC(6,2); -- NUMERIC(6,2),
393             attr_set.price          := tmp_attr_set.pr::NUMERIC(8,2); -- NUMERIC(8,2),
394     
395             attr_set.call_number    := tmp_attr_set.cn; -- TEXT
396             attr_set.barcode        := tmp_attr_set.bc; -- TEXT,
397             attr_set.circ_modifier  := tmp_attr_set.circ_mod; -- TEXT,
398             attr_set.circ_as_type   := tmp_attr_set.circ_as; -- TEXT,
399             attr_set.alert_message  := tmp_attr_set.amessage; -- TEXT,
400             attr_set.pub_note       := tmp_attr_set.note; -- TEXT,
401             attr_set.priv_note      := tmp_attr_set.pnote; -- TEXT,
402             attr_set.alert_message  := tmp_attr_set.amessage; -- TEXT,
403     
404             RETURN NEXT attr_set;
405     
406         END LOOP;
407     
408     END IF;
409
410 END;
411 $$ LANGUAGE PLPGSQL;
412
413
414 CREATE OR REPLACE FUNCTION vandelay.ingest_bib_marc ( ) RETURNS TRIGGER AS $$
415 DECLARE
416     value   TEXT;
417     atype   TEXT;
418     adef    RECORD;
419 BEGIN
420     FOR adef IN SELECT * FROM vandelay.bib_attr_definition LOOP
421
422         SELECT extract_marc_field('vandelay.queued_bib_record', id, adef.xpath, adef.remove) INTO value FROM vandelay.queued_bib_record WHERE id = NEW.id;
423         IF (value IS NOT NULL AND value <> '') THEN
424             INSERT INTO vandelay.queued_bib_record_attr (record, field, attr_value) VALUES (NEW.id, adef.id, value);
425         END IF;
426
427     END LOOP;
428
429     RETURN NULL;
430 END;
431 $$ LANGUAGE PLPGSQL;
432
433 CREATE OR REPLACE FUNCTION vandelay.ingest_bib_items ( ) RETURNS TRIGGER AS $func$
434 DECLARE
435     queue_rec   RECORD;
436     item_rule   RECORD;
437     item_data   vandelay.import_item%ROWTYPE;
438 BEGIN
439
440     SELECT * INTO queue_rec FROM vandelay.bib_queue WHERE id = NEW.queue;
441
442     FOR item_rule IN SELECT r.* FROM actor.org_unit_ancestors( queue_rec.owner ) o JOIN vandelay.import_item_attr_definition r ON ( r.owner = o.id ) LOOP
443         FOR item_data IN SELECT * FROM vandelay.ingest_items( NEW.id::BIGINT, item_rule.id::BIGINT ) LOOP
444             INSERT INTO vandelay.import_item (
445                 record,
446                 definition,
447                 owning_lib,
448                 circ_lib,
449                 call_number,
450                 copy_number,
451                 status,
452                 location,
453                 circulate,
454                 deposit,
455                 deposit_amount,
456                 ref,
457                 holdable,
458                 price,
459                 barcode,
460                 circ_modifier,
461                 circ_as_type,
462                 alert_message,
463                 pub_note,
464                 priv_note,
465                 opac_visible
466             ) VALUES (
467                 NEW.id,
468                 item_data.definition,
469                 item_data.owning_lib,
470                 item_data.circ_lib,
471                 item_data.call_number,
472                 item_data.copy_number,
473                 item_data.status,
474                 item_data.location,
475                 item_data.circulate,
476                 item_data.deposit,
477                 item_data.deposit_amount,
478                 item_data.ref,
479                 item_data.holdable,
480                 item_data.price,
481                 item_data.barcode,
482                 item_data.circ_modifier,
483                 item_data.circ_as_type,
484                 item_data.alert_message,
485                 item_data.pub_note,
486                 item_data.priv_note,
487                 item_data.opac_visible
488             );
489         END LOOP;
490     END LOOP;
491
492     RETURN NULL;
493 END;
494 $func$ LANGUAGE PLPGSQL;
495
496 CREATE OR REPLACE FUNCTION vandelay.match_bib_record ( ) RETURNS TRIGGER AS $func$
497 DECLARE
498     attr    RECORD;
499     eg_rec  RECORD;
500 BEGIN
501     FOR attr IN SELECT a.* FROM vandelay.queued_bib_record_attr a JOIN vandelay.bib_attr_definition d ON (d.id = a.field) WHERE record = NEW.id AND d.ident IS TRUE LOOP
502
503                 -- All numbers? check for an id match
504                 IF (attr.attr_value ~ $r$^\d+$$r$) THEN
505                 FOR eg_rec IN SELECT * FROM biblio.record_entry WHERE id = attr.attr_value::BIGINT AND deleted IS FALSE LOOP
506                         INSERT INTO vandelay.bib_match (field_type, matched_attr, queued_record, eg_record) VALUES ('id', attr.id, NEW.id, eg_rec.id);
507                         END LOOP;
508                 END IF;
509
510                 -- Looks like an ISBN? check for an isbn match
511                 IF (attr.attr_value ~* $r$^[0-9x]+$$r$ AND character_length(attr.attr_value) IN (10,13)) THEN
512                 FOR eg_rec IN EXECUTE $$SELECT * FROM metabib.full_rec fr WHERE fr.value LIKE LOWER('$$ || attr.attr_value || $$%') AND fr.tag = '020' AND fr.subfield = 'a'$$ LOOP
513                                 PERFORM id FROM biblio.record_entry WHERE id = eg_rec.record AND deleted IS FALSE;
514                                 IF FOUND THEN
515                                 INSERT INTO vandelay.bib_match (field_type, matched_attr, queued_record, eg_record) VALUES ('isbn', attr.id, NEW.id, eg_rec.record);
516                                 END IF;
517                         END LOOP;
518
519                         -- subcheck for isbn-as-tcn
520                     FOR eg_rec IN SELECT * FROM biblio.record_entry WHERE tcn_value = 'i' || attr.attr_value AND deleted IS FALSE LOOP
521                             INSERT INTO vandelay.bib_match (field_type, matched_attr, queued_record, eg_record) VALUES ('tcn_value', attr.id, NEW.id, eg_rec.id);
522                 END LOOP;
523                 END IF;
524
525                 -- check for an OCLC tcn_value match
526                 IF (attr.attr_value ~ $r$^o\d+$$r$) THEN
527                     FOR eg_rec IN SELECT * FROM biblio.record_entry WHERE tcn_value = regexp_replace(attr.attr_value,'^o','ocm') AND deleted IS FALSE LOOP
528                             INSERT INTO vandelay.bib_match (field_type, matched_attr, queued_record, eg_record) VALUES ('tcn_value', attr.id, NEW.id, eg_rec.id);
529                 END LOOP;
530                 END IF;
531
532                 -- check for a direct tcn_value match
533         FOR eg_rec IN SELECT * FROM biblio.record_entry WHERE tcn_value = attr.attr_value AND deleted IS FALSE LOOP
534             INSERT INTO vandelay.bib_match (field_type, matched_attr, queued_record, eg_record) VALUES ('tcn_value', attr.id, NEW.id, eg_rec.id);
535         END LOOP;
536
537                 -- check for a direct item barcode match
538         FOR eg_rec IN
539                 SELECT  DISTINCT b.*
540                   FROM  biblio.record_entry b
541                         JOIN asset.call_number cn ON (cn.record = b.id)
542                         JOIN asset.copy cp ON (cp.call_number = cn.id)
543                   WHERE cp.barcode = attr.attr_value AND cp.deleted IS FALSE
544         LOOP
545             INSERT INTO vandelay.bib_match (field_type, matched_attr, queued_record, eg_record) VALUES ('id', attr.id, NEW.id, eg_rec.id);
546         END LOOP;
547
548     END LOOP;
549
550     RETURN NULL;
551 END;
552 $func$ LANGUAGE PLPGSQL;
553
554 CREATE OR REPLACE FUNCTION vandelay.cleanup_bib_marc ( ) RETURNS TRIGGER AS $$
555 BEGIN
556     DELETE FROM vandelay.queued_bib_record_attr WHERE record = OLD.id;
557     DELETE FROM vandelay.import_item WHERE record = OLD.id;
558
559     IF TG_OP = 'UPDATE' THEN
560         RETURN NEW;
561     END IF;
562     RETURN OLD;
563 END;
564 $$ LANGUAGE PLPGSQL;
565
566 CREATE TRIGGER cleanup_bib_trigger
567     BEFORE UPDATE OR DELETE ON vandelay.queued_bib_record
568     FOR EACH ROW EXECUTE PROCEDURE vandelay.cleanup_bib_marc();
569
570 CREATE TRIGGER ingest_bib_trigger
571     AFTER INSERT OR UPDATE ON vandelay.queued_bib_record
572     FOR EACH ROW EXECUTE PROCEDURE vandelay.ingest_bib_marc();
573
574 CREATE TRIGGER ingest_item_trigger
575     AFTER INSERT OR UPDATE ON vandelay.queued_bib_record
576     FOR EACH ROW EXECUTE PROCEDURE vandelay.ingest_bib_items();
577
578 CREATE TRIGGER zz_match_bibs_trigger
579     AFTER INSERT OR UPDATE ON vandelay.queued_bib_record
580     FOR EACH ROW EXECUTE PROCEDURE vandelay.match_bib_record();
581
582
583 /* Authority stuff down here */
584 ---------------------------------------
585 CREATE TABLE vandelay.authority_attr_definition (
586         id                      SERIAL  PRIMARY KEY,
587         code            TEXT    UNIQUE NOT NULL,
588         description     TEXT,
589         xpath           TEXT    NOT NULL,
590         remove          TEXT    NOT NULL DEFAULT '',
591         ident           BOOL    NOT NULL DEFAULT FALSE
592 );
593
594 CREATE TABLE vandelay.authority_queue (
595         queue_type      TEXT            NOT NULL DEFAULT 'authority' CHECK (queue_type = 'authority'),
596         CONSTRAINT vand_authority_queue_name_once_per_owner_const UNIQUE (owner,name,queue_type)
597 ) INHERITS (vandelay.queue);
598 ALTER TABLE vandelay.authority_queue ADD PRIMARY KEY (id);
599
600 CREATE TABLE vandelay.queued_authority_record (
601         queue           INT     NOT NULL REFERENCES vandelay.authority_queue (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
602         imported_as     INT     REFERENCES authority.record_entry (id) DEFERRABLE INITIALLY DEFERRED
603 ) INHERITS (vandelay.queued_record);
604 ALTER TABLE vandelay.queued_authority_record ADD PRIMARY KEY (id);
605
606 CREATE TABLE vandelay.queued_authority_record_attr (
607         id                      BIGSERIAL       PRIMARY KEY,
608         record          BIGINT          NOT NULL REFERENCES vandelay.queued_authority_record (id) DEFERRABLE INITIALLY DEFERRED,
609         field           INT                     NOT NULL REFERENCES vandelay.authority_attr_definition (id) DEFERRABLE INITIALLY DEFERRED,
610         attr_value      TEXT            NOT NULL
611 );
612
613 CREATE TABLE vandelay.authority_match (
614         id                              BIGSERIAL       PRIMARY KEY,
615         matched_attr    INT                     REFERENCES vandelay.queued_authority_record_attr (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
616         queued_record   BIGINT          REFERENCES vandelay.queued_authority_record (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
617         eg_record               BIGINT          REFERENCES authority.record_entry (id) DEFERRABLE INITIALLY DEFERRED
618 );
619
620 CREATE OR REPLACE FUNCTION vandelay.ingest_authority_marc ( ) RETURNS TRIGGER AS $$
621 DECLARE
622     value   TEXT;
623     atype   TEXT;
624     adef    RECORD;
625 BEGIN
626     FOR adef IN SELECT * FROM vandelay.authority_attr_definition LOOP
627
628         SELECT extract_marc_field('vandelay.queued_authority_record', id, adef.xpath, adef.remove) INTO value FROM vandelay.queued_authority_record WHERE id = NEW.id;
629         IF (value IS NOT NULL AND value <> '') THEN
630             INSERT INTO vandelay.queued_authority_record_attr (record, field, attr_value) VALUES (NEW.id, adef.id, value);
631         END IF;
632
633     END LOOP;
634
635     RETURN NULL;
636 END;
637 $$ LANGUAGE PLPGSQL;
638
639 CREATE OR REPLACE FUNCTION vandelay.cleanup_authority_marc ( ) RETURNS TRIGGER AS $$
640 BEGIN
641     DELETE FROM vandelay.queued_authority_record_attr WHERE record = OLD.id;
642     IF TG_OP = 'UPDATE' THEN
643         RETURN NEW;
644     END IF;
645     RETURN OLD;
646 END;
647 $$ LANGUAGE PLPGSQL;
648
649 CREATE TRIGGER cleanup_authority_trigger
650     BEFORE UPDATE OR DELETE ON vandelay.queued_authority_record
651     FOR EACH ROW EXECUTE PROCEDURE vandelay.cleanup_authority_marc();
652
653 CREATE TRIGGER ingest_authority_trigger
654     AFTER INSERT OR UPDATE ON vandelay.queued_authority_record
655     FOR EACH ROW EXECUTE PROCEDURE vandelay.ingest_authority_marc();
656
657 -- Vandelay (for importing and exporting records) 012.schema.vandelay.sql 
658 --INSERT INTO vandelay.bib_attr_definition ( id, code, description, xpath ) VALUES (1, 'title', oils_i18n_gettext(1, 'vqbrad', 'Title of work', 'description'),'//*[@tag="245"]/*[contains("abcmnopr",@code)]');
659 --INSERT INTO vandelay.bib_attr_definition ( id, code, description, xpath ) VALUES (2, 'author', oils_i18n_gettext(1, 'vqbrad', 'Author of work', 'description'),'//*[@tag="100" or @tag="110" or @tag="113"]/*[contains("ad",@code)]');
660 --INSERT INTO vandelay.bib_attr_definition ( id, code, description, xpath ) VALUES (3, 'language', oils_i18n_gettext(3, 'vqbrad', 'Language of work', 'description'),'//*[@tag="240"]/*[@code="l"][1]');
661 --INSERT INTO vandelay.bib_attr_definition ( id, code, description, xpath ) VALUES (4, 'pagination', oils_i18n_gettext(4, 'vqbrad', 'Pagination', 'description'),'//*[@tag="300"]/*[@code="a"][1]');
662 --INSERT INTO vandelay.bib_attr_definition ( id, code, description, xpath, ident, remove ) VALUES (5, 'isbn',oils_i18n_gettext(5, 'vqbrad', 'ISBN', 'description'),'//*[@tag="020"]/*[@code="a"]', TRUE, $r$(?:-|\s.+$)$r$);
663 --INSERT INTO vandelay.bib_attr_definition ( id, code, description, xpath, ident, remove ) VALUES (6, 'issn',oils_i18n_gettext(6, 'vqbrad', 'ISSN', 'description'),'//*[@tag="022"]/*[@code="a"]', TRUE, $r$(?:-|\s.+$)$r$);
664 --INSERT INTO vandelay.bib_attr_definition ( id, code, description, xpath ) VALUES (7, 'price',oils_i18n_gettext(7, 'vqbrad', 'Price', 'description'),'//*[@tag="020" or @tag="022"]/*[@code="c"][1]');
665 --INSERT INTO vandelay.bib_attr_definition ( id, code, description, xpath, ident ) VALUES (8, 'rec_identifier',oils_i18n_gettext(8, 'vqbrad', 'Accession Number', 'description'),'//*[@tag="001"]', TRUE);
666 --INSERT INTO vandelay.bib_attr_definition ( id, code, description, xpath, ident ) VALUES (9, 'eg_tcn',oils_i18n_gettext(9, 'vqbrad', 'TCN Value', 'description'),'//*[@tag="901"]/*[@code="a"]', TRUE);
667 --INSERT INTO vandelay.bib_attr_definition ( id, code, description, xpath, ident ) VALUES (10, 'eg_tcn_source',oils_i18n_gettext(10, 'vqbrad', 'TCN Source', 'description'),'//*[@tag="901"]/*[@code="b"]', TRUE);
668 --INSERT INTO vandelay.bib_attr_definition ( id, code, description, xpath, ident ) VALUES (11, 'eg_identifier',oils_i18n_gettext(11, 'vqbrad', 'Internal ID', 'description'),'//*[@tag="901"]/*[@code="c"]', TRUE);
669 --INSERT INTO vandelay.bib_attr_definition ( id, code, description, xpath ) VALUES (12, 'publisher',oils_i18n_gettext(12, 'vqbrad', 'Publisher', 'description'),'//*[@tag="260"]/*[@code="b"][1]');
670 --INSERT INTO vandelay.bib_attr_definition ( id, code, description, xpath, remove ) VALUES (13, 'pubdate',oils_i18n_gettext(13, 'vqbrad', 'Publication Date', 'description'),'//*[@tag="260"]/*[@code="c"][1]',$r$\D$r$);
671 --INSERT INTO vandelay.bib_attr_definition ( id, code, description, xpath ) VALUES (14, 'edition',oils_i18n_gettext(14, 'vqbrad', 'Edition', 'description'),'//*[@tag="250"]/*[@code="a"][1]');
672 --
673 --INSERT INTO vandelay.import_item_attr_definition (
674 --    owner, name, tag, owning_lib, circ_lib, location,
675 --    call_number, circ_modifier, barcode, price, copy_number,
676 --    circulate, ref, holdable, opac_visible, status
677 --) VALUES (
678 --    1,
679 --    'Evergreen 852 export format',
680 --    '852',
681 --    '[@code = "b"][1]',
682 --    '[@code = "b"][2]',
683 --    'c',
684 --    'j',
685 --    'g',
686 --    'p',
687 --    'y',
688 --    't',
689 --    '[@code = "x" and text() = "circulating"]',
690 --    '[@code = "x" and text() = "reference"]',
691 --    '[@code = "x" and text() = "holdable"]',
692 --    '[@code = "x" and text() = "visible"]',
693 --    'z'
694 --);
695 --
696 --INSERT INTO vandelay.import_item_attr_definition (
697 --    owner,
698 --    name,
699 --    tag,
700 --    owning_lib,
701 --    location,
702 --    call_number,
703 --    circ_modifier,
704 --    barcode,
705 --    price,
706 --    status
707 --) VALUES (
708 --    1,
709 --    'Unicorn Import format -- 999',
710 --    '999',
711 --    'm',
712 --    'l',
713 --    'a',
714 --    't',
715 --    'i',
716 --    'p',
717 --    'k'
718 --);
719 --
720 --INSERT INTO vandelay.authority_attr_definition ( code, description, xpath, ident ) VALUES ('rec_identifier','Identifier','//*[@tag="001"]', TRUE);
721
722 COMMIT;
723