]> git.evergreen-ils.org Git - working/Evergreen.git/blob - Open-ILS/src/sql/Pg/012.schema.vandelay.sql
adding authority version of the record merging and queue processing functions
[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 DEFERRABLE INITIALLY DEFERRED,
103     definition      BIGINT      NOT NULL REFERENCES vandelay.import_item_attr_definition (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
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 TABLE vandelay.merge_profile (
133     id              BIGSERIAL   PRIMARY KEY,
134     owner           INT         NOT NULL REFERENCES actor.org_unit (id) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
135     name            TEXT        NOT NULL,
136     add_spec        TEXT,
137     replace_spec    TEXT,
138     strip_spec      TEXT,
139     preserve_spec   TEXT,
140         CONSTRAINT vand_merge_prof_owner_name_idx UNIQUE (owner,name),
141         CONSTRAINT add_replace_strip_or_preserve CHECK ((preserve_spec IS NOT NULL OR replace_spec IS NOT NULL) OR (preserve_spec IS NULL AND replace_spec IS NULL))
142 );
143
144 CREATE OR REPLACE FUNCTION vandelay.add_field ( target_xml TEXT, source_xml TEXT, field TEXT ) RETURNS TEXT AS $_$
145
146     use MARC::Record;
147     use MARC::File::XML;
148
149     my $target_xml = shift;
150     my $source_xml = shift;
151     my $field_spec = shift;
152     $field_spec =~ s/\s+//sg;
153
154     my $target_r = MARC::Record->new_from_xml( $target_xml );
155     my $source_r = MARC::Record->new_from_xml( $source_xml );
156
157     return $target_xml unless ($target_r && $source_r);
158
159     my @field_list = split(',', $field_spec);
160
161     my %fields;
162     for my $f (@field_list) {
163         if ($f =~ /^(.{3})(.*)$/) {
164             $fields{$1} = [ split('', $2) ];
165         }
166     }
167
168     for my $f ( keys %fields) {
169         if ( @{$fields{$f}} ) {
170             for my $from_field ($source_r->field( $f )) {
171                 for my $to_field ($target_r->field( $f )) {
172                     my @new_sf = map { ($_ => $from_field->subfield($_)) } @{$fields{$f}};
173                     $to_field->add_subfields( @new_sf );
174                 }
175             }
176         } else {
177             my @new_fields = map { $_->clone } $source_r->field( $f );
178             $target_r->insert_fields_ordered( @new_fields );
179         }
180     }
181
182     $target_xml = $target_r->as_xml_record;
183     $target_xml =~ s/^<\?.+?\?>$//mo;
184     $target_xml =~ s/\n//sgo;
185     $target_xml =~ s/>\s+</></sgo;
186
187     return $target_xml;
188
189 $_$ LANGUAGE PLPERLU;
190
191 CREATE OR REPLACE FUNCTION vandelay.strip_field ( xml TEXT, field TEXT ) RETURNS TEXT AS $_$
192
193     use MARC::Record;
194     use MARC::File::XML;
195
196     my $xml = shift;
197     my $r = MARC::Record->new_from_xml( $xml );
198
199     return $xml unless ($r);
200
201     my $field_spec = shift;
202     $field_spec =~ s/\s+//sg;
203
204     my @field_list = split(',', $field_spec);
205
206     my %fields;
207     for my $f (@field_list) {
208         if ($f =~ /^(.{3})(.*)$/) {
209             $fields{$1} = [ split('', $2) ];
210         }
211     }
212
213     for my $f ( keys %fields) {
214         if ( @{$fields{$f}} ) {
215             $_->delete_subfield(code => $fields{$f}) for ($r->field( $f ));
216         } else {
217             $r->delete_field( $_ ) for ( $r->field( $f ) );
218         }
219     }
220
221     $xml = $r->as_xml_record;
222     $xml =~ s/^<\?.+?\?>$//mo;
223     $xml =~ s/\n//sgo;
224     $xml =~ s/>\s+</></sgo;
225
226     return $xml;
227
228 $_$ LANGUAGE PLPERLU;
229
230 CREATE OR REPLACE FUNCTION vandelay.replace_field ( target_xml TEXT, source_xml TEXT, field TEXT ) RETURNS TEXT AS $_$
231     SELECT vandelay.add_field( vandelay.strip_field( $1, $3), $2, $3 );
232 $_$ LANGUAGE SQL;
233
234 CREATE OR REPLACE FUNCTION vandelay.merge_record_xml ( target_xml TEXT, source_xml TEXT, add_rule TEXT, replace_preserve_rule TEXT, strip_rule TEXT ) RETURNS TEXT AS $_$
235     SELECT vandelay.replace_field( vandelay.add_field( vandelay.strip_field( $1, $5) , $2, $3 ), $2, $4);
236 $_$ LANGUAGE SQL;
237
238 CREATE TYPE vandelay.compile_profile AS (add_rule TEXT, replace_rule TEXT, preserve_rule TEXT, strip_rule TEXT);
239 CREATE OR REPLACE FUNCTION vandelay.compile_profile ( incoming_xml TEXT ) RETURNS vandelay.compile_profile AS $_$
240 DECLARE
241     output              vandelay.compile_profile%ROWTYPE;
242     profile             vandelay.merge_profile%ROWTYPE;
243     profile_tmpl        TEXT;
244     profile_tmpl_owner  TEXT;
245     add_rule            TEXT := '';
246     strip_rule          TEXT := '';
247     replace_rule        TEXT := '';
248     preserve_rule       TEXT := '';
249
250 BEGIN
251
252     profile_tmpl := (oils_xpath('//*[@tag="905"]/*[@code="t"]/text()',incoming_xml))[1];
253     profile_tmpl_owner := (oils_xpath('//*[@tag="905"]/*[@code="o"]/text()',incoming_xml))[1];
254
255     IF profile_tmpl IS NOT NULL AND profile_tmpl <> '' AND profile_tmpl_owner IS NOT NULL AND profile_tmpl_owner <> '' THEN
256         SELECT  p.* INTO profile
257           FROM  vandelay.merge_profile p
258                 JOIN actor.org_unit u ON (u.id = p.owner)
259           WHERE p.name = profile_tmpl
260                 AND u.shortname = profile_tmpl_owner;
261
262         IF profile.id IS NOT NULL THEN
263             add_rule := COALESCE(profile.add_spec,'');
264             strip_rule := COALESCE(profile.strip_spec,'');
265             replace_rule := COALESCE(profile.replace_spec,'');
266             preserve_rule := COALESCE(profile.preserve_spec,'');
267         END IF;
268     END IF;
269
270     add_rule := add_rule || ',' || COALESCE(ARRAY_TO_STRING(oils_xpath('//*[@tag="905"]/*[@code="a"]/text()',incoming_xml),''),'');
271     strip_rule := strip_rule || ',' || COALESCE(ARRAY_TO_STRING(oils_xpath('//*[@tag="905"]/*[@code="d"]/text()',incoming_xml),''),'');
272     replace_rule := replace_rule || ',' || COALESCE(ARRAY_TO_STRING(oils_xpath('//*[@tag="905"]/*[@code="r"]/text()',incoming_xml),''),'');
273     preserve_rule := preserve_rule || ',' || COALESCE(ARRAY_TO_STRING(oils_xpath('//*[@tag="905"]/*[@code="p"]/text()',incoming_xml),''),'');
274
275     output.add_rule := BTRIM(add_rule,',');
276     output.replace_rule := BTRIM(replace_rule,',');
277     output.strip_rule := BTRIM(strip_rule,',');
278     output.preserve_rule := BTRIM(preserve_rule,',');
279
280     RETURN output;
281 END;
282 $_$ LANGUAGE PLPGSQL;
283
284 CREATE OR REPLACE FUNCTION vandelay.overlay_bib_record ( import_id BIGINT, eg_id BIGINT, merge_profile_id INT ) RETURNS BOOL AS $$
285 DECLARE
286     merge_profile   vandelay.merge_profile%ROWTYPE;
287     dyn_profile     vandelay.compile_profile%ROWTYPE;
288     source_marc     TEXT;
289     target_marc     TEXT;
290     eg_marc         TEXT;
291     v_marc          TEXT;
292     replace_rule    TEXT;
293     match_count     INT;
294 BEGIN
295
296     SELECT  b.marc INTO eg_marc
297       FROM  biblio.record_entry b
298             JOIN vandelay.bib_match m ON (m.eg_record = b.id AND m.queued_record = import_id)
299       LIMIT 1;
300
301     SELECT  q.marc INTO v_marc
302       FROM  vandelay.queued_record q
303             JOIN vandelay.bib_match m ON (m.queued_record = q.id AND q.id = import_id)
304       LIMIT 1;
305
306     IF eg_marc IS NULL OR v_marc IS NULL THEN
307         -- RAISE NOTICE 'no marc for vandelay or bib record';
308         RETURN FALSE;
309     END IF;
310
311     dyn_profile := vandelay.compile_profile( v_marc );
312
313     IF merge_profile_id IS NOT NULL THEN
314         SELECT * INTO merge_profile FROM vandelay.merge_profile WHERE id = merge_profile_id;
315         IF FOUND THEN
316             dyn_profile.add_rule := BTRIM( dyn_profile.add_rule || ',' || COALESCE(merge_profile.add_spec,''), ',');
317             dyn_profile.strip_rule := BTRIM( dyn_profile.strip_rule || ',' || COALESCE(merge_profile.strip_spec,''), ',');
318             dyn_profile.replace_rule := BTRIM( dyn_profile.replace_rule || ',' || COALESCE(merge_profile.replace_spec,''), ',');
319             dyn_profile.preserve_rule := BTRIM( dyn_profile.preserve_rule || ',' || COALESCE(merge_profile.preserve_spec,''), ',');
320         END IF;
321     END IF;
322
323     IF dyn_profile.replace_rule <> '' AND dyn_profile.preserve_rule <> '' THEN
324         -- RAISE NOTICE 'both replace [%] and preserve [%] specified', dyn_profile.replace_rule, dyn_profile.preserve_rule;
325         RETURN FALSE;
326     END IF;
327
328     IF dyn_profile.replace_rule <> '' THEN
329         source_marc = v_marc;
330         target_marc = eg_marc;
331         replace_rule = dyn_profile.replace_rule;
332     ELSE
333         source_marc = eg_marc;
334         target_marc = v_marc;
335         replace_rule = dyn_profile.preserve_rule;
336     END IF;
337
338     UPDATE  biblio.record_entry
339       SET   marc = vandelay.merge_record_xml( target_marc, source_marc, dyn_profile.add_rule, replace_rule, dyn_profile.strip_rule )
340       WHERE id = eg_id;
341
342     IF FOUND THEN
343         UPDATE  vandelay.queued_bib_record
344           SET   imported_as = eg_id,
345                 import_time = NOW()
346           WHERE id = import_id;
347         RETURN TRUE;
348     END IF;
349
350     -- RAISE NOTICE 'update of biblio.record_entry failed';
351
352     RETURN FALSE;
353
354 END;
355 $$ LANGUAGE PLPGSQL;
356
357 CREATE OR REPLACE FUNCTION vandelay.auto_overlay_bib_record ( import_id BIGINT, merge_profile_id INT ) RETURNS BOOL AS $$
358 DECLARE
359     eg_id           BIGINT;
360     match_count     INT;
361 BEGIN
362     SELECT COUNT(*) INTO match_count FROM vandelay.bib_match WHERE queued_record = import_id;
363
364     IF match_count <> 1 THEN
365         -- RAISE NOTICE 'not an exact match';
366         RETURN FALSE;
367     END IF;
368
369     SELECT  m.eg_record INTO eg_id
370       FROM  vandelay.bib_match m
371       WHERE m.queued_record = import_id
372       LIMIT 1;
373
374     IF eg_id IS NULL THEN
375         RETURN FALSE;
376     END IF;
377
378     RETURN vandelay.overlay_bib_record( import_id, eg_id, merge_profile_id );
379 END;
380 $$ LANGUAGE PLPGSQL;
381
382 CREATE OR REPLACE FUNCTION vandelay.auto_overlay_bib_queue ( queue_id BIGINT, merge_profile_id INT ) RETURNS SETOF BIGINT AS $$
383 DECLARE
384     queued_record   vandelay.queued_bib_record%ROWTYPE;
385     success         BOOL;
386 BEGIN
387
388     FOR queued_record IN SELECT * FROM vandelay.queued_bib_record WHERE queue = queue_id AND import_time IS NULL LOOP
389         success := vandelay.auto_overlay_bib_record( queued_record.id, merge_profile_id );
390
391         IF success THEN
392             RETURN NEXT queued_record.id;
393         END IF;
394
395     END LOOP;
396
397     RETURN;
398     
399 END;
400 $$ LANGUAGE PLPGSQL;
401
402 CREATE OR REPLACE FUNCTION vandelay.auto_overlay_bib_queue ( queue_id BIGINT ) RETURNS SETOF BIGINT AS $$
403     SELECT * FROM vandelay.auto_overlay_bib_queue( $1, NULL );
404 $$ LANGUAGE SQL;
405
406 CREATE OR REPLACE FUNCTION vandelay.ingest_items ( import_id BIGINT, attr_def_id BIGINT ) RETURNS SETOF vandelay.import_item AS $$
407 DECLARE
408
409     owning_lib      TEXT;
410     circ_lib        TEXT;
411     call_number     TEXT;
412     copy_number     TEXT;
413     status          TEXT;
414     location        TEXT;
415     circulate       TEXT;
416     deposit         TEXT;
417     deposit_amount  TEXT;
418     ref             TEXT;
419     holdable        TEXT;
420     price           TEXT;
421     barcode         TEXT;
422     circ_modifier   TEXT;
423     circ_as_type    TEXT;
424     alert_message   TEXT;
425     opac_visible    TEXT;
426     pub_note        TEXT;
427     priv_note       TEXT;
428
429     attr_def        RECORD;
430     tmp_attr_set    RECORD;
431     attr_set        vandelay.import_item%ROWTYPE;
432
433     xpath           TEXT;
434
435 BEGIN
436
437     SELECT * INTO attr_def FROM vandelay.import_item_attr_definition WHERE id = attr_def_id;
438
439     IF FOUND THEN
440
441         attr_set.definition := attr_def.id; 
442     
443         -- Build the combined XPath
444     
445         owning_lib :=
446             CASE
447                 WHEN attr_def.owning_lib IS NULL THEN 'null()'
448                 WHEN LENGTH( attr_def.owning_lib ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.owning_lib || '"]'
449                 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.owning_lib
450             END;
451     
452         circ_lib :=
453             CASE
454                 WHEN attr_def.circ_lib IS NULL THEN 'null()'
455                 WHEN LENGTH( attr_def.circ_lib ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.circ_lib || '"]'
456                 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.circ_lib
457             END;
458     
459         call_number :=
460             CASE
461                 WHEN attr_def.call_number IS NULL THEN 'null()'
462                 WHEN LENGTH( attr_def.call_number ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.call_number || '"]'
463                 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.call_number
464             END;
465     
466         copy_number :=
467             CASE
468                 WHEN attr_def.copy_number IS NULL THEN 'null()'
469                 WHEN LENGTH( attr_def.copy_number ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.copy_number || '"]'
470                 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.copy_number
471             END;
472     
473         status :=
474             CASE
475                 WHEN attr_def.status IS NULL THEN 'null()'
476                 WHEN LENGTH( attr_def.status ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.status || '"]'
477                 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.status
478             END;
479     
480         location :=
481             CASE
482                 WHEN attr_def.location IS NULL THEN 'null()'
483                 WHEN LENGTH( attr_def.location ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.location || '"]'
484                 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.location
485             END;
486     
487         circulate :=
488             CASE
489                 WHEN attr_def.circulate IS NULL THEN 'null()'
490                 WHEN LENGTH( attr_def.circulate ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.circulate || '"]'
491                 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.circulate
492             END;
493     
494         deposit :=
495             CASE
496                 WHEN attr_def.deposit IS NULL THEN 'null()'
497                 WHEN LENGTH( attr_def.deposit ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.deposit || '"]'
498                 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.deposit
499             END;
500     
501         deposit_amount :=
502             CASE
503                 WHEN attr_def.deposit_amount IS NULL THEN 'null()'
504                 WHEN LENGTH( attr_def.deposit_amount ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.deposit_amount || '"]'
505                 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.deposit_amount
506             END;
507     
508         ref :=
509             CASE
510                 WHEN attr_def.ref IS NULL THEN 'null()'
511                 WHEN LENGTH( attr_def.ref ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.ref || '"]'
512                 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.ref
513             END;
514     
515         holdable :=
516             CASE
517                 WHEN attr_def.holdable IS NULL THEN 'null()'
518                 WHEN LENGTH( attr_def.holdable ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.holdable || '"]'
519                 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.holdable
520             END;
521     
522         price :=
523             CASE
524                 WHEN attr_def.price IS NULL THEN 'null()'
525                 WHEN LENGTH( attr_def.price ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.price || '"]'
526                 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.price
527             END;
528     
529         barcode :=
530             CASE
531                 WHEN attr_def.barcode IS NULL THEN 'null()'
532                 WHEN LENGTH( attr_def.barcode ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.barcode || '"]'
533                 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.barcode
534             END;
535     
536         circ_modifier :=
537             CASE
538                 WHEN attr_def.circ_modifier IS NULL THEN 'null()'
539                 WHEN LENGTH( attr_def.circ_modifier ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.circ_modifier || '"]'
540                 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.circ_modifier
541             END;
542     
543         circ_as_type :=
544             CASE
545                 WHEN attr_def.circ_as_type IS NULL THEN 'null()'
546                 WHEN LENGTH( attr_def.circ_as_type ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.circ_as_type || '"]'
547                 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.circ_as_type
548             END;
549     
550         alert_message :=
551             CASE
552                 WHEN attr_def.alert_message IS NULL THEN 'null()'
553                 WHEN LENGTH( attr_def.alert_message ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.alert_message || '"]'
554                 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.alert_message
555             END;
556     
557         opac_visible :=
558             CASE
559                 WHEN attr_def.opac_visible IS NULL THEN 'null()'
560                 WHEN LENGTH( attr_def.opac_visible ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.opac_visible || '"]'
561                 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.opac_visible
562             END;
563
564         pub_note :=
565             CASE
566                 WHEN attr_def.pub_note IS NULL THEN 'null()'
567                 WHEN LENGTH( attr_def.pub_note ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.pub_note || '"]'
568                 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.pub_note
569             END;
570         priv_note :=
571             CASE
572                 WHEN attr_def.priv_note IS NULL THEN 'null()'
573                 WHEN LENGTH( attr_def.priv_note ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.priv_note || '"]'
574                 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.priv_note
575             END;
576     
577     
578         xpath := 
579             owning_lib      || '|' || 
580             circ_lib        || '|' || 
581             call_number     || '|' || 
582             copy_number     || '|' || 
583             status          || '|' || 
584             location        || '|' || 
585             circulate       || '|' || 
586             deposit         || '|' || 
587             deposit_amount  || '|' || 
588             ref             || '|' || 
589             holdable        || '|' || 
590             price           || '|' || 
591             barcode         || '|' || 
592             circ_modifier   || '|' || 
593             circ_as_type    || '|' || 
594             alert_message   || '|' || 
595             pub_note        || '|' || 
596             priv_note       || '|' || 
597             opac_visible;
598
599         -- RAISE NOTICE 'XPath: %', xpath;
600         
601         FOR tmp_attr_set IN
602                 SELECT  *
603                   FROM  oils_xpath_table( 'id', 'marc', 'vandelay.queued_bib_record', xpath, 'id = ' || import_id )
604                             AS t( id INT, ol TEXT, clib TEXT, cn TEXT, cnum TEXT, cs TEXT, cl TEXT, circ TEXT,
605                                   dep TEXT, dep_amount TEXT, r TEXT, hold TEXT, pr TEXT, bc TEXT, circ_mod TEXT,
606                                   circ_as TEXT, amessage TEXT, note TEXT, pnote TEXT, opac_vis TEXT )
607         LOOP
608     
609             tmp_attr_set.pr = REGEXP_REPLACE(tmp_attr_set.pr, E'[^0-9\\.]', '', 'g');
610             tmp_attr_set.dep_amount = REGEXP_REPLACE(tmp_attr_set.dep_amount, E'[^0-9\\.]', '', 'g');
611
612             tmp_attr_set.pr := NULLIF( tmp_attr_set.pr, '' );
613             tmp_attr_set.dep_amount := NULLIF( tmp_attr_set.dep_amount, '' );
614     
615             SELECT id INTO attr_set.owning_lib FROM actor.org_unit WHERE shortname = UPPER(tmp_attr_set.ol); -- INT
616             SELECT id INTO attr_set.circ_lib FROM actor.org_unit WHERE shortname = UPPER(tmp_attr_set.clib); -- INT
617             SELECT id INTO attr_set.status FROM config.copy_status WHERE LOWER(name) = LOWER(tmp_attr_set.cs); -- INT
618     
619             SELECT  id INTO attr_set.location
620               FROM  asset.copy_location
621               WHERE LOWER(name) = LOWER(tmp_attr_set.cl)
622                     AND asset.copy_location.owning_lib = COALESCE(attr_set.owning_lib, attr_set.circ_lib); -- INT
623     
624             attr_set.circulate      :=
625                 LOWER( SUBSTRING( tmp_attr_set.circ, 1, 1)) IN ('t','y','1')
626                 OR LOWER(tmp_attr_set.circ) = 'circulating'; -- BOOL
627
628             attr_set.deposit        :=
629                 LOWER( SUBSTRING( tmp_attr_set.dep, 1, 1 ) ) IN ('t','y','1')
630                 OR LOWER(tmp_attr_set.dep) = 'deposit'; -- BOOL
631
632             attr_set.holdable       :=
633                 LOWER( SUBSTRING( tmp_attr_set.hold, 1, 1 ) ) IN ('t','y','1')
634                 OR LOWER(tmp_attr_set.hold) = 'holdable'; -- BOOL
635
636             attr_set.opac_visible   :=
637                 LOWER( SUBSTRING( tmp_attr_set.opac_vis, 1, 1 ) ) IN ('t','y','1')
638                 OR LOWER(tmp_attr_set.opac_vis) = 'visible'; -- BOOL
639
640             attr_set.ref            :=
641                 LOWER( SUBSTRING( tmp_attr_set.r, 1, 1 ) ) IN ('t','y','1')
642                 OR LOWER(tmp_attr_set.r) = 'reference'; -- BOOL
643     
644             attr_set.copy_number    := tmp_attr_set.cnum::INT; -- INT,
645             attr_set.deposit_amount := tmp_attr_set.dep_amount::NUMERIC(6,2); -- NUMERIC(6,2),
646             attr_set.price          := tmp_attr_set.pr::NUMERIC(8,2); -- NUMERIC(8,2),
647     
648             attr_set.call_number    := tmp_attr_set.cn; -- TEXT
649             attr_set.barcode        := tmp_attr_set.bc; -- TEXT,
650             attr_set.circ_modifier  := tmp_attr_set.circ_mod; -- TEXT,
651             attr_set.circ_as_type   := tmp_attr_set.circ_as; -- TEXT,
652             attr_set.alert_message  := tmp_attr_set.amessage; -- TEXT,
653             attr_set.pub_note       := tmp_attr_set.note; -- TEXT,
654             attr_set.priv_note      := tmp_attr_set.pnote; -- TEXT,
655             attr_set.alert_message  := tmp_attr_set.amessage; -- TEXT,
656     
657             RETURN NEXT attr_set;
658     
659         END LOOP;
660     
661     END IF;
662
663 END;
664 $$ LANGUAGE PLPGSQL;
665
666
667 CREATE OR REPLACE FUNCTION vandelay.ingest_bib_marc ( ) RETURNS TRIGGER AS $$
668 DECLARE
669     value   TEXT;
670     atype   TEXT;
671     adef    RECORD;
672 BEGIN
673     FOR adef IN SELECT * FROM vandelay.bib_attr_definition LOOP
674
675         SELECT extract_marc_field('vandelay.queued_bib_record', id, adef.xpath, adef.remove) INTO value FROM vandelay.queued_bib_record WHERE id = NEW.id;
676         IF (value IS NOT NULL AND value <> '') THEN
677             INSERT INTO vandelay.queued_bib_record_attr (record, field, attr_value) VALUES (NEW.id, adef.id, value);
678         END IF;
679
680     END LOOP;
681
682     RETURN NULL;
683 END;
684 $$ LANGUAGE PLPGSQL;
685
686 CREATE OR REPLACE FUNCTION vandelay.ingest_bib_items ( ) RETURNS TRIGGER AS $func$
687 DECLARE
688     attr_def    BIGINT;
689     item_data   vandelay.import_item%ROWTYPE;
690 BEGIN
691
692     SELECT item_attr_def INTO attr_def FROM vandelay.bib_queue WHERE id = NEW.queue;
693
694     FOR item_data IN SELECT * FROM vandelay.ingest_items( NEW.id::BIGINT, attr_def ) LOOP
695         INSERT INTO vandelay.import_item (
696             record,
697             definition,
698             owning_lib,
699             circ_lib,
700             call_number,
701             copy_number,
702             status,
703             location,
704             circulate,
705             deposit,
706             deposit_amount,
707             ref,
708             holdable,
709             price,
710             barcode,
711             circ_modifier,
712             circ_as_type,
713             alert_message,
714             pub_note,
715             priv_note,
716             opac_visible
717         ) VALUES (
718             NEW.id,
719             item_data.definition,
720             item_data.owning_lib,
721             item_data.circ_lib,
722             item_data.call_number,
723             item_data.copy_number,
724             item_data.status,
725             item_data.location,
726             item_data.circulate,
727             item_data.deposit,
728             item_data.deposit_amount,
729             item_data.ref,
730             item_data.holdable,
731             item_data.price,
732             item_data.barcode,
733             item_data.circ_modifier,
734             item_data.circ_as_type,
735             item_data.alert_message,
736             item_data.pub_note,
737             item_data.priv_note,
738             item_data.opac_visible
739         );
740     END LOOP;
741
742     RETURN NULL;
743 END;
744 $func$ LANGUAGE PLPGSQL;
745
746 CREATE OR REPLACE FUNCTION vandelay.match_bib_record ( ) RETURNS TRIGGER AS $func$
747 DECLARE
748     attr        RECORD;
749     eg_rec      RECORD;
750     id_value    TEXT;
751     exact_id    BIGINT;
752 BEGIN
753
754     DELETE FROM vandelay.bib_match WHERE queued_record = NEW.id;
755
756     SELECT * INTO attr FROM vandelay.bib_attr_definition WHERE xpath = '//*[@tag="901"]/*[@code="c"]' ORDER BY id LIMIT 1;
757
758     IF attr IS NOT NULL AND attr.id IS NOT NULL THEN
759         id_value := extract_marc_field('vandelay.queued_bib_record', NEW.id, attr.xpath, attr.remove);
760     
761         IF id_value IS NOT NULL AND id_value <> '' AND id_value ~ $r$^\d+$$r$ THEN
762             SELECT id INTO exact_id FROM biblio.record_entry WHERE id = id_value::BIGINT AND NOT deleted;
763             IF exact_id IS NOT NULL THEN
764                 INSERT INTO vandelay.bib_match (field_type, matched_attr, queued_record, eg_record) VALUES ('id', attr.id, NEW.id, exact_id);
765             END IF;
766         END IF;
767     END IF;
768
769     IF exact_id IS NULL THEN
770         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
771     
772                 -- All numbers? check for an id match
773                 IF (attr.attr_value ~ $r$^\d+$$r$) THEN
774                 FOR eg_rec IN SELECT * FROM biblio.record_entry WHERE id = attr.attr_value::BIGINT AND deleted IS FALSE LOOP
775                         INSERT INTO vandelay.bib_match (field_type, matched_attr, queued_record, eg_record) VALUES ('id', attr.id, NEW.id, eg_rec.id);
776                         END LOOP;
777                 END IF;
778     
779                 -- Looks like an ISBN? check for an isbn match
780                 IF (attr.attr_value ~* $r$^[0-9x]+$$r$ AND character_length(attr.attr_value) IN (10,13)) THEN
781                 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
782                                 PERFORM id FROM biblio.record_entry WHERE id = eg_rec.record AND deleted IS FALSE;
783                                 IF FOUND THEN
784                                 INSERT INTO vandelay.bib_match (field_type, matched_attr, queued_record, eg_record) VALUES ('isbn', attr.id, NEW.id, eg_rec.record);
785                                 END IF;
786                         END LOOP;
787     
788                         -- subcheck for isbn-as-tcn
789                     FOR eg_rec IN SELECT * FROM biblio.record_entry WHERE tcn_value = 'i' || attr.attr_value AND deleted IS FALSE LOOP
790                             INSERT INTO vandelay.bib_match (field_type, matched_attr, queued_record, eg_record) VALUES ('tcn_value', attr.id, NEW.id, eg_rec.id);
791                 END LOOP;
792                 END IF;
793     
794                 -- check for an OCLC tcn_value match
795                 IF (attr.attr_value ~ $r$^o\d+$$r$) THEN
796                     FOR eg_rec IN SELECT * FROM biblio.record_entry WHERE tcn_value = regexp_replace(attr.attr_value,'^o','ocm') AND deleted IS FALSE LOOP
797                             INSERT INTO vandelay.bib_match (field_type, matched_attr, queued_record, eg_record) VALUES ('tcn_value', attr.id, NEW.id, eg_rec.id);
798                 END LOOP;
799                 END IF;
800     
801                 -- check for a direct tcn_value match
802             FOR eg_rec IN SELECT * FROM biblio.record_entry WHERE tcn_value = attr.attr_value AND deleted IS FALSE LOOP
803                 INSERT INTO vandelay.bib_match (field_type, matched_attr, queued_record, eg_record) VALUES ('tcn_value', attr.id, NEW.id, eg_rec.id);
804             END LOOP;
805     
806                 -- check for a direct item barcode match
807             FOR eg_rec IN
808                     SELECT  DISTINCT b.*
809                       FROM  biblio.record_entry b
810                             JOIN asset.call_number cn ON (cn.record = b.id)
811                             JOIN asset.copy cp ON (cp.call_number = cn.id)
812                       WHERE cp.barcode = attr.attr_value AND cp.deleted IS FALSE
813             LOOP
814                 INSERT INTO vandelay.bib_match (field_type, matched_attr, queued_record, eg_record) VALUES ('id', attr.id, NEW.id, eg_rec.id);
815             END LOOP;
816     
817         END LOOP;
818     END IF;
819
820     RETURN NULL;
821 END;
822 $func$ LANGUAGE PLPGSQL;
823
824 CREATE OR REPLACE FUNCTION vandelay.cleanup_bib_marc ( ) RETURNS TRIGGER AS $$
825 BEGIN
826     DELETE FROM vandelay.queued_bib_record_attr WHERE record = OLD.id;
827     DELETE FROM vandelay.import_item WHERE record = OLD.id;
828
829     IF TG_OP = 'UPDATE' THEN
830         RETURN NEW;
831     END IF;
832     RETURN OLD;
833 END;
834 $$ LANGUAGE PLPGSQL;
835
836 CREATE TRIGGER cleanup_bib_trigger
837     BEFORE UPDATE OR DELETE ON vandelay.queued_bib_record
838     FOR EACH ROW EXECUTE PROCEDURE vandelay.cleanup_bib_marc();
839
840 CREATE TRIGGER ingest_bib_trigger
841     AFTER INSERT OR UPDATE ON vandelay.queued_bib_record
842     FOR EACH ROW EXECUTE PROCEDURE vandelay.ingest_bib_marc();
843
844 CREATE TRIGGER ingest_item_trigger
845     AFTER INSERT OR UPDATE ON vandelay.queued_bib_record
846     FOR EACH ROW EXECUTE PROCEDURE vandelay.ingest_bib_items();
847
848 CREATE TRIGGER zz_match_bibs_trigger
849     AFTER INSERT OR UPDATE ON vandelay.queued_bib_record
850     FOR EACH ROW EXECUTE PROCEDURE vandelay.match_bib_record();
851
852
853 /* Authority stuff down here */
854 ---------------------------------------
855 CREATE TABLE vandelay.authority_attr_definition (
856         id                      SERIAL  PRIMARY KEY,
857         code            TEXT    UNIQUE NOT NULL,
858         description     TEXT,
859         xpath           TEXT    NOT NULL,
860         remove          TEXT    NOT NULL DEFAULT '',
861         ident           BOOL    NOT NULL DEFAULT FALSE
862 );
863
864 CREATE TABLE vandelay.authority_queue (
865         queue_type      TEXT            NOT NULL DEFAULT 'authority' CHECK (queue_type = 'authority'),
866         CONSTRAINT vand_authority_queue_name_once_per_owner_const UNIQUE (owner,name,queue_type)
867 ) INHERITS (vandelay.queue);
868 ALTER TABLE vandelay.authority_queue ADD PRIMARY KEY (id);
869
870 CREATE TABLE vandelay.queued_authority_record (
871         queue           INT     NOT NULL REFERENCES vandelay.authority_queue (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
872         imported_as     INT     REFERENCES authority.record_entry (id) DEFERRABLE INITIALLY DEFERRED
873 ) INHERITS (vandelay.queued_record);
874 ALTER TABLE vandelay.queued_authority_record ADD PRIMARY KEY (id);
875
876 CREATE TABLE vandelay.queued_authority_record_attr (
877         id                      BIGSERIAL       PRIMARY KEY,
878         record          BIGINT          NOT NULL REFERENCES vandelay.queued_authority_record (id) DEFERRABLE INITIALLY DEFERRED,
879         field           INT                     NOT NULL REFERENCES vandelay.authority_attr_definition (id) DEFERRABLE INITIALLY DEFERRED,
880         attr_value      TEXT            NOT NULL
881 );
882
883 CREATE TABLE vandelay.authority_match (
884         id                              BIGSERIAL       PRIMARY KEY,
885         matched_attr    INT                     REFERENCES vandelay.queued_authority_record_attr (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
886         queued_record   BIGINT          REFERENCES vandelay.queued_authority_record (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
887         eg_record               BIGINT          REFERENCES authority.record_entry (id) DEFERRABLE INITIALLY DEFERRED
888 );
889
890 CREATE OR REPLACE FUNCTION vandelay.ingest_authority_marc ( ) RETURNS TRIGGER AS $$
891 DECLARE
892     value   TEXT;
893     atype   TEXT;
894     adef    RECORD;
895 BEGIN
896     FOR adef IN SELECT * FROM vandelay.authority_attr_definition LOOP
897
898         SELECT extract_marc_field('vandelay.queued_authority_record', id, adef.xpath, adef.remove) INTO value FROM vandelay.queued_authority_record WHERE id = NEW.id;
899         IF (value IS NOT NULL AND value <> '') THEN
900             INSERT INTO vandelay.queued_authority_record_attr (record, field, attr_value) VALUES (NEW.id, adef.id, value);
901         END IF;
902
903     END LOOP;
904
905     RETURN NULL;
906 END;
907 $$ LANGUAGE PLPGSQL;
908
909 CREATE OR REPLACE FUNCTION vandelay.cleanup_authority_marc ( ) RETURNS TRIGGER AS $$
910 BEGIN
911     DELETE FROM vandelay.queued_authority_record_attr WHERE record = OLD.id;
912     IF TG_OP = 'UPDATE' THEN
913         RETURN NEW;
914     END IF;
915     RETURN OLD;
916 END;
917 $$ LANGUAGE PLPGSQL;
918
919 CREATE TRIGGER cleanup_authority_trigger
920     BEFORE UPDATE OR DELETE ON vandelay.queued_authority_record
921     FOR EACH ROW EXECUTE PROCEDURE vandelay.cleanup_authority_marc();
922
923 CREATE TRIGGER ingest_authority_trigger
924     AFTER INSERT OR UPDATE ON vandelay.queued_authority_record
925     FOR EACH ROW EXECUTE PROCEDURE vandelay.ingest_authority_marc();
926
927 CREATE OR REPLACE FUNCTION vandelay.overlay_authority_record ( import_id BIGINT, eg_id BIGINT, merge_profile_id INT ) RETURNS BOOL AS $$
928 DECLARE
929     merge_profile   vandelay.merge_profile%ROWTYPE;
930     dyn_profile     vandelay.compile_profile%ROWTYPE;
931     source_marc     TEXT;
932     target_marc     TEXT;
933     eg_marc         TEXT;
934     v_marc          TEXT;
935     replace_rule    TEXT;
936     match_count     INT;
937 BEGIN
938
939     SELECT  b.marc INTO eg_marc
940       FROM  authority.record_entry b
941             JOIN vandelay.authority_match m ON (m.eg_record = b.id AND m.queued_record = import_id)
942       LIMIT 1;
943
944     SELECT  q.marc INTO v_marc
945       FROM  vandelay.queued_record q
946             JOIN vandelay.authority_match m ON (m.queued_record = q.id AND q.id = import_id)
947       LIMIT 1;
948
949     IF eg_marc IS NULL OR v_marc IS NULL THEN
950         -- RAISE NOTICE 'no marc for vandelay or authority record';
951         RETURN FALSE;
952     END IF;
953
954     dyn_profile := vandelay.compile_profile( v_marc );
955
956     IF merge_profile_id IS NOT NULL THEN
957         SELECT * INTO merge_profile FROM vandelay.merge_profile WHERE id = merge_profile_id;
958         IF FOUND THEN
959             dyn_profile.add_rule := BTRIM( dyn_profile.add_rule || ',' || COALESCE(merge_profile.add_spec,''), ',');
960             dyn_profile.strip_rule := BTRIM( dyn_profile.strip_rule || ',' || COALESCE(merge_profile.strip_spec,''), ',');
961             dyn_profile.replace_rule := BTRIM( dyn_profile.replace_rule || ',' || COALESCE(merge_profile.replace_spec,''), ',');
962             dyn_profile.preserve_rule := BTRIM( dyn_profile.preserve_rule || ',' || COALESCE(merge_profile.preserve_spec,''), ',');
963         END IF;
964     END IF;
965
966     IF dyn_profile.replace_rule <> '' AND dyn_profile.preserve_rule <> '' THEN
967         -- RAISE NOTICE 'both replace [%] and preserve [%] specified', dyn_profile.replace_rule, dyn_profile.preserve_rule;
968         RETURN FALSE;
969     END IF;
970
971     IF dyn_profile.replace_rule <> '' THEN
972         source_marc = v_marc;
973         target_marc = eg_marc;
974         replace_rule = dyn_profile.replace_rule;
975     ELSE
976         source_marc = eg_marc;
977         target_marc = v_marc;
978         replace_rule = dyn_profile.preserve_rule;
979     END IF;
980
981     UPDATE  authority.record_entry
982       SET   marc = vandelay.merge_record_xml( target_marc, source_marc, dyn_profile.add_rule, replace_rule, dyn_profile.strip_rule )
983       WHERE id = eg_id;
984
985     IF FOUND THEN
986         UPDATE  vandelay.queued_authority_record
987           SET   imported_as = eg_id,
988                 import_time = NOW()
989           WHERE id = import_id;
990         RETURN TRUE;
991     END IF;
992
993     -- RAISE NOTICE 'update of authority.record_entry failed';
994
995     RETURN FALSE;
996
997 END;
998 $$ LANGUAGE PLPGSQL;
999
1000 CREATE OR REPLACE FUNCTION vandelay.auto_overlay_authority_record ( import_id BIGINT, merge_profile_id INT ) RETURNS BOOL AS $$
1001 DECLARE
1002     eg_id           BIGINT;
1003     match_count     INT;
1004 BEGIN
1005     SELECT COUNT(*) INTO match_count FROM vandelay.authority_match WHERE queued_record = import_id;
1006
1007     IF match_count <> 1 THEN
1008         -- RAISE NOTICE 'not an exact match';
1009         RETURN FALSE;
1010     END IF;
1011
1012     SELECT  m.eg_record INTO eg_id
1013       FROM  vandelay.authority_match m
1014       WHERE m.queued_record = import_id
1015       LIMIT 1;
1016
1017     IF eg_id IS NULL THEN
1018         RETURN FALSE;
1019     END IF;
1020
1021     RETURN vandelay.overlay_authority_record( import_id, eg_id, merge_profile_id );
1022 END;
1023 $$ LANGUAGE PLPGSQL;
1024
1025 CREATE OR REPLACE FUNCTION vandelay.auto_overlay_authority_queue ( queue_id BIGINT, merge_profile_id INT ) RETURNS SETOF BIGINT AS $$
1026 DECLARE
1027     queued_record   vandelay.queued_authority_record%ROWTYPE;
1028     success         BOOL;
1029 BEGIN
1030
1031     FOR queued_record IN SELECT * FROM vandelay.queued_authority_record WHERE queue = queue_id AND import_time IS NULL LOOP
1032         success := vandelay.auto_overlay_authority_record( queued_record.id, merge_profile_id );
1033
1034         IF success THEN
1035             RETURN NEXT queued_record.id;
1036         END IF;
1037
1038     END LOOP;
1039
1040     RETURN;
1041     
1042 END;
1043 $$ LANGUAGE PLPGSQL;
1044
1045 CREATE OR REPLACE FUNCTION vandelay.auto_overlay_authority_queue ( queue_id BIGINT ) RETURNS SETOF BIGINT AS $$
1046     SELECT * FROM vandelay.auto_overlay_authority_queue( $1, NULL );
1047 $$ LANGUAGE SQL;
1048
1049
1050 -- Vandelay (for importing and exporting records) 012.schema.vandelay.sql 
1051 --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)]');
1052 --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)]');
1053 --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]');
1054 --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]');
1055 --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$);
1056 --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$);
1057 --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]');
1058 --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);
1059 --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);
1060 --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);
1061 --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);
1062 --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]');
1063 --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$);
1064 --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]');
1065 --
1066 --INSERT INTO vandelay.import_item_attr_definition (
1067 --    owner, name, tag, owning_lib, circ_lib, location,
1068 --    call_number, circ_modifier, barcode, price, copy_number,
1069 --    circulate, ref, holdable, opac_visible, status
1070 --) VALUES (
1071 --    1,
1072 --    'Evergreen 852 export format',
1073 --    '852',
1074 --    '[@code = "b"][1]',
1075 --    '[@code = "b"][2]',
1076 --    'c',
1077 --    'j',
1078 --    'g',
1079 --    'p',
1080 --    'y',
1081 --    't',
1082 --    '[@code = "x" and text() = "circulating"]',
1083 --    '[@code = "x" and text() = "reference"]',
1084 --    '[@code = "x" and text() = "holdable"]',
1085 --    '[@code = "x" and text() = "visible"]',
1086 --    'z'
1087 --);
1088 --
1089 --INSERT INTO vandelay.import_item_attr_definition (
1090 --    owner,
1091 --    name,
1092 --    tag,
1093 --    owning_lib,
1094 --    location,
1095 --    call_number,
1096 --    circ_modifier,
1097 --    barcode,
1098 --    price,
1099 --    status
1100 --) VALUES (
1101 --    1,
1102 --    'Unicorn Import format -- 999',
1103 --    '999',
1104 --    'm',
1105 --    'l',
1106 --    'a',
1107 --    't',
1108 --    'i',
1109 --    'p',
1110 --    'k'
1111 --);
1112 --
1113 --INSERT INTO vandelay.authority_attr_definition ( code, description, xpath, ident ) VALUES ('rec_identifier','Identifier','//*[@tag="001"]', TRUE);
1114
1115 COMMIT;
1116