Stamped DB version
authorBill Erickson <berick@esilibrary.com>
Wed, 6 Jul 2011 19:18:48 +0000 (15:18 -0400)
committerBill Erickson <berick@esilibrary.com>
Wed, 6 Jul 2011 19:18:48 +0000 (15:18 -0400)
Signed-off-by: Bill Erickson <berick@esilibrary.com>
Open-ILS/src/sql/Pg/002.schema.config.sql
Open-ILS/src/sql/Pg/upgrade/0572.schema.vandelay-record-matching-and-quality.sql [new file with mode: 0644]
Open-ILS/src/sql/Pg/upgrade/XXXX.vandelay-record-matching-and-quality.sql [deleted file]

index d9ad349..6276a29 100644 (file)
@@ -86,7 +86,7 @@ CREATE TRIGGER no_overlapping_deps
     BEFORE INSERT OR UPDATE ON config.db_patch_dependencies
     FOR EACH ROW EXECUTE PROCEDURE evergreen.array_overlap_check ('deprecates');
 
-INSERT INTO config.upgrade_log (version, applied_to) VALUES ('0571', :eg_version); -- miker
+INSERT INTO config.upgrade_log (version, applied_to) VALUES ('0572', :eg_version); -- berick
 
 CREATE TABLE config.bib_source (
        id              SERIAL  PRIMARY KEY,
diff --git a/Open-ILS/src/sql/Pg/upgrade/0572.schema.vandelay-record-matching-and-quality.sql b/Open-ILS/src/sql/Pg/upgrade/0572.schema.vandelay-record-matching-and-quality.sql
new file mode 100644 (file)
index 0000000..3b986b0
--- /dev/null
@@ -0,0 +1,1594 @@
+-- Evergreen DB patch 0572.vandelay-record-matching-and-quality.sql
+--
+BEGIN;
+
+
+-- check whether patch can be applied
+SELECT evergreen.upgrade_deps_block_check('0572', :eg_version);
+
+CREATE OR REPLACE FUNCTION evergreen.array_remove_item_by_value(inp ANYARRAY, el ANYELEMENT) RETURNS anyarray AS $$ SELECT ARRAY_ACCUM(x.e) FROM UNNEST( $1 ) x(e) WHERE x.e <> $2; $$ LANGUAGE SQL;
+
+CREATE TABLE vandelay.match_set (
+    id      SERIAL  PRIMARY KEY,
+    name    TEXT        NOT NULL,
+    owner   INT     NOT NULL REFERENCES actor.org_unit (id) ON DELETE CASCADE,
+    mtype   TEXT        NOT NULL DEFAULT 'biblio', -- 'biblio','authority','mfhd'?, others?
+    CONSTRAINT name_once_per_owner_mtype UNIQUE (name, owner, mtype)
+);
+
+-- Table to define match points, either FF via SVF or tag+subfield
+CREATE TABLE vandelay.match_set_point (
+    id          SERIAL  PRIMARY KEY,
+    match_set   INT     REFERENCES vandelay.match_set (id) ON DELETE CASCADE,
+    parent      INT     REFERENCES vandelay.match_set_point (id),
+    bool_op     TEXT    CHECK (bool_op IS NULL OR (bool_op IN ('AND','OR','NOT'))),
+    svf         TEXT    REFERENCES config.record_attr_definition (name),
+    tag         TEXT,
+    subfield    TEXT,
+    negate      BOOL    DEFAULT FALSE,
+    quality     INT     NOT NULL DEFAULT 1, -- higher is better
+    CONSTRAINT vmsp_need_a_subfield_with_a_tag CHECK ((tag IS NOT NULL AND subfield IS NOT NULL) OR tag IS NULL),
+    CONSTRAINT vmsp_need_a_tag_or_a_ff_or_a_bo CHECK (
+        (tag IS NOT NULL AND svf IS NULL AND bool_op IS NULL) OR
+        (tag IS NULL AND svf IS NOT NULL AND bool_op IS NULL) OR
+        (tag IS NULL AND svf IS NULL AND bool_op IS NOT NULL)
+    )
+);
+
+CREATE TABLE vandelay.match_set_quality (
+    id          SERIAL  PRIMARY KEY,
+    match_set   INT     NOT NULL REFERENCES vandelay.match_set (id) ON DELETE CASCADE,
+    svf         TEXT    REFERENCES config.record_attr_definition,
+    tag         TEXT,
+    subfield    TEXT,
+    value       TEXT    NOT NULL,
+    quality     INT     NOT NULL DEFAULT 1, -- higher is better
+    CONSTRAINT vmsq_need_a_subfield_with_a_tag CHECK ((tag IS NOT NULL AND subfield IS NOT NULL) OR tag IS NULL),
+    CONSTRAINT vmsq_need_a_tag_or_a_ff CHECK ((tag IS NOT NULL AND svf IS NULL) OR (tag IS NULL AND svf IS NOT NULL))
+);
+CREATE UNIQUE INDEX vmsq_def_once_per_set ON vandelay.match_set_quality (match_set, COALESCE(tag,''), COALESCE(subfield,''), COALESCE(svf,''), value);
+
+
+-- ALTER TABLEs...
+ALTER TABLE vandelay.queue ADD COLUMN match_set INT REFERENCES vandelay.match_set (id) ON UPDATE CASCADE ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED;
+ALTER TABLE vandelay.queued_record ADD COLUMN quality INT NOT NULL DEFAULT 0;
+ALTER TABLE vandelay.bib_attr_definition DROP COLUMN ident;
+
+CREATE TABLE vandelay.import_error (
+    code        TEXT    PRIMARY KEY,
+    description TEXT    NOT NULL -- i18n
+);
+
+ALTER TABLE vandelay.queued_bib_record
+    ADD COLUMN import_error TEXT REFERENCES vandelay.import_error (code) ON DELETE SET NULL ON UPDATE CASCADE DEFERRABLE INITIALLY DEFERRED,
+    ADD COLUMN error_detail TEXT;
+
+ALTER TABLE vandelay.bib_match
+    DROP COLUMN field_type,
+    DROP COLUMN matched_attr,
+    ADD COLUMN quality INT NOT NULL DEFAULT 1,
+    ADD COLUMN match_score INT NOT NULL DEFAULT 0;
+
+ALTER TABLE vandelay.import_item
+    ADD COLUMN import_error TEXT REFERENCES vandelay.import_error (code) ON DELETE SET NULL ON UPDATE CASCADE DEFERRABLE INITIALLY DEFERRED,
+    ADD COLUMN error_detail TEXT,
+    ADD COLUMN imported_as BIGINT REFERENCES asset.copy (id) DEFERRABLE INITIALLY DEFERRED,
+    ADD COLUMN import_time TIMESTAMP WITH TIME ZONE;
+
+ALTER TABLE vandelay.merge_profile ADD COLUMN lwm_ratio NUMERIC;
+
+CREATE OR REPLACE FUNCTION vandelay.marc21_record_type( marc TEXT ) RETURNS config.marc21_rec_type_map AS $func$
+DECLARE
+    ldr         TEXT;
+    tval        TEXT;
+    tval_rec    RECORD;
+    bval        TEXT;
+    bval_rec    RECORD;
+    retval      config.marc21_rec_type_map%ROWTYPE;
+BEGIN
+    ldr := oils_xpath_string( '//*[local-name()="leader"]', marc );
+
+    IF ldr IS NULL OR ldr = '' THEN
+        SELECT * INTO retval FROM config.marc21_rec_type_map WHERE code = 'BKS';
+        RETURN retval;
+    END IF;
+
+    SELECT * INTO tval_rec FROM config.marc21_ff_pos_map WHERE fixed_field = 'Type' LIMIT 1; -- They're all the same
+    SELECT * INTO bval_rec FROM config.marc21_ff_pos_map WHERE fixed_field = 'BLvl' LIMIT 1; -- They're all the same
+
+
+    tval := SUBSTRING( ldr, tval_rec.start_pos + 1, tval_rec.length );
+    bval := SUBSTRING( ldr, bval_rec.start_pos + 1, bval_rec.length );
+
+    -- RAISE NOTICE 'type %, blvl %, ldr %', tval, bval, ldr;
+
+    SELECT * INTO retval FROM config.marc21_rec_type_map WHERE type_val LIKE '%' || tval || '%' AND blvl_val LIKE '%' || bval || '%';
+
+
+    IF retval.code IS NULL THEN
+        SELECT * INTO retval FROM config.marc21_rec_type_map WHERE code = 'BKS';
+    END IF;
+
+    RETURN retval;
+END;
+$func$ LANGUAGE PLPGSQL;
+
+CREATE OR REPLACE FUNCTION vandelay.marc21_extract_fixed_field( marc TEXT, ff TEXT ) RETURNS TEXT AS $func$
+DECLARE
+    rtype       TEXT;
+    ff_pos      RECORD;
+    tag_data    RECORD;
+    val         TEXT;
+BEGIN
+    rtype := (vandelay.marc21_record_type( marc )).code;
+    FOR ff_pos IN SELECT * FROM config.marc21_ff_pos_map WHERE fixed_field = ff AND rec_type = rtype ORDER BY tag DESC LOOP
+        IF ff_pos.tag = 'ldr' THEN
+            val := oils_xpath_string('//*[local-name()="leader"]', marc);
+            IF val IS NOT NULL THEN
+                val := SUBSTRING( val, ff_pos.start_pos + 1, ff_pos.length );
+                RETURN val;
+            END IF;
+        ELSE
+            FOR tag_data IN SELECT value FROM UNNEST( oils_xpath( '//*[@tag="' || UPPER(ff_pos.tag) || '"]/text()', marc ) ) x(value) LOOP
+                val := SUBSTRING( tag_data.value, ff_pos.start_pos + 1, ff_pos.length );
+                RETURN val;
+            END LOOP;
+        END IF;
+        val := REPEAT( ff_pos.default_val, ff_pos.length );
+        RETURN val;
+    END LOOP;
+
+    RETURN NULL;
+END;
+$func$ LANGUAGE PLPGSQL;
+
+CREATE OR REPLACE FUNCTION vandelay.marc21_extract_all_fixed_fields( marc TEXT ) RETURNS SETOF biblio.record_ff_map AS $func$
+DECLARE
+    tag_data    TEXT;
+    rtype       TEXT;
+    ff_pos      RECORD;
+    output      biblio.record_ff_map%ROWTYPE;
+BEGIN
+    rtype := (vandelay.marc21_record_type( marc )).code;
+
+    FOR ff_pos IN SELECT * FROM config.marc21_ff_pos_map WHERE rec_type = rtype ORDER BY tag DESC LOOP
+        output.ff_name  := ff_pos.fixed_field;
+        output.ff_value := NULL;
+
+        IF ff_pos.tag = 'ldr' THEN
+            output.ff_value := oils_xpath_string('//*[local-name()="leader"]', marc);
+            IF output.ff_value IS NOT NULL THEN
+                output.ff_value := SUBSTRING( output.ff_value, ff_pos.start_pos + 1, ff_pos.length );
+                RETURN NEXT output;
+                output.ff_value := NULL;
+            END IF;
+        ELSE
+            FOR tag_data IN SELECT value FROM UNNEST( oils_xpath( '//*[@tag="' || UPPER(ff_pos.tag) || '"]/text()', marc ) ) x(value) LOOP
+                output.ff_value := SUBSTRING( tag_data, ff_pos.start_pos + 1, ff_pos.length );
+                IF output.ff_value IS NULL THEN output.ff_value := REPEAT( ff_pos.default_val, ff_pos.length ); END IF;
+                RETURN NEXT output;
+                output.ff_value := NULL;
+            END LOOP;
+        END IF;
+    
+    END LOOP;
+
+    RETURN;
+END;
+$func$ LANGUAGE PLPGSQL;
+
+CREATE OR REPLACE FUNCTION vandelay.marc21_physical_characteristics( marc TEXT) RETURNS SETOF biblio.marc21_physical_characteristics AS $func$
+DECLARE
+    rowid   INT := 0;
+    _007    TEXT;
+    ptype   config.marc21_physical_characteristic_type_map%ROWTYPE;
+    psf     config.marc21_physical_characteristic_subfield_map%ROWTYPE;
+    pval    config.marc21_physical_characteristic_value_map%ROWTYPE;
+    retval  biblio.marc21_physical_characteristics%ROWTYPE;
+BEGIN
+
+    _007 := oils_xpath_string( '//*[@tag="007"]', marc );
+
+    IF _007 IS NOT NULL AND _007 <> '' THEN
+        SELECT * INTO ptype FROM config.marc21_physical_characteristic_type_map WHERE ptype_key = SUBSTRING( _007, 1, 1 );
+
+        IF ptype.ptype_key IS NOT NULL THEN
+            FOR psf IN SELECT * FROM config.marc21_physical_characteristic_subfield_map WHERE ptype_key = ptype.ptype_key LOOP
+                SELECT * INTO pval FROM config.marc21_physical_characteristic_value_map WHERE ptype_subfield = psf.id AND value = SUBSTRING( _007, psf.start_pos + 1, psf.length );
+
+                IF pval.id IS NOT NULL THEN
+                    rowid := rowid + 1;
+                    retval.id := rowid;
+                    retval.ptype := ptype.ptype_key;
+                    retval.subfield := psf.id;
+                    retval.value := pval.id;
+                    RETURN NEXT retval;
+                END IF;
+
+            END LOOP;
+        END IF;
+    END IF;
+
+    RETURN;
+END;
+$func$ LANGUAGE PLPGSQL;
+
+CREATE TYPE vandelay.flat_marc AS ( tag CHAR(3), ind1 TEXT, ind2 TEXT, subfield TEXT, value TEXT );
+CREATE OR REPLACE FUNCTION vandelay.flay_marc ( TEXT ) RETURNS SETOF vandelay.flat_marc AS $func$
+
+use MARC::Record;
+use MARC::File::XML (BinaryEncoding => 'UTF-8');
+use MARC::Charset;
+use strict;
+
+MARC::Charset->assume_unicode(1);
+
+my $xml = shift;
+my $r = MARC::Record->new_from_xml( $xml );
+
+return_next( { tag => 'LDR', value => $r->leader } );
+
+for my $f ( $r->fields ) {
+    if ($f->is_control_field) {
+        return_next({ tag => $f->tag, value => $f->data });
+    } else {
+        for my $s ($f->subfields) {
+            return_next({
+                tag      => $f->tag,
+                ind1     => $f->indicator(1),
+                ind2     => $f->indicator(2),
+                subfield => $s->[0],
+                value    => $s->[1]
+            });
+
+            if ( $f->tag eq '245' and $s->[0] eq 'a' ) {
+                my $trim = $f->indicator(2) || 0;
+                return_next({
+                    tag      => 'tnf',
+                    ind1     => $f->indicator(1),
+                    ind2     => $f->indicator(2),
+                    subfield => 'a',
+                    value    => substr( $s->[1], $trim )
+                });
+            }
+        }
+    }
+}
+
+return undef;
+
+$func$ LANGUAGE PLPERLU;
+
+CREATE OR REPLACE FUNCTION vandelay.flatten_marc ( marc TEXT ) RETURNS SETOF vandelay.flat_marc AS $func$
+DECLARE
+    output  vandelay.flat_marc%ROWTYPE;
+    field   RECORD;
+BEGIN
+    FOR field IN SELECT * FROM vandelay.flay_marc( marc ) LOOP
+        output.ind1 := field.ind1;
+        output.ind2 := field.ind2;
+        output.tag := field.tag;
+        output.subfield := field.subfield;
+        IF field.subfield IS NOT NULL AND field.tag NOT IN ('020','022','024') THEN -- exclude standard numbers and control fields
+            output.value := naco_normalize(field.value, field.subfield);
+        ELSE
+            output.value := field.value;
+        END IF;
+
+        CONTINUE WHEN output.value IS NULL;
+
+        RETURN NEXT output;
+    END LOOP;
+END;
+$func$ LANGUAGE PLPGSQL;
+
+CREATE OR REPLACE FUNCTION vandelay.extract_rec_attrs ( xml TEXT, attr_defs TEXT[]) RETURNS hstore AS $_$
+DECLARE
+    transformed_xml TEXT;
+    prev_xfrm       TEXT;
+    normalizer      RECORD;
+    xfrm            config.xml_transform%ROWTYPE;
+    attr_value      TEXT;
+    new_attrs       HSTORE := ''::HSTORE;
+    attr_def        config.record_attr_definition%ROWTYPE;
+BEGIN
+
+    FOR attr_def IN SELECT * FROM config.record_attr_definition WHERE name IN (SELECT * FROM UNNEST(attr_defs)) ORDER BY format LOOP
+
+        IF attr_def.tag IS NOT NULL THEN -- tag (and optional subfield list) selection
+            SELECT  ARRAY_TO_STRING(ARRAY_ACCUM(x.value), COALESCE(attr_def.joiner,' ')) INTO attr_value
+              FROM  vandelay.flatten_marc(xml) AS x
+              WHERE x.tag LIKE attr_def.tag
+                    AND CASE
+                        WHEN attr_def.sf_list IS NOT NULL
+                            THEN POSITION(x.subfield IN attr_def.sf_list) > 0
+                        ELSE TRUE
+                        END
+              GROUP BY x.tag
+              ORDER BY x.tag
+              LIMIT 1;
+
+        ELSIF attr_def.fixed_field IS NOT NULL THEN -- a named fixed field, see config.marc21_ff_pos_map.fixed_field
+            attr_value := vandelay.marc21_extract_fixed_field(xml, attr_def.fixed_field);
+
+        ELSIF attr_def.xpath IS NOT NULL THEN -- and xpath expression
+
+            SELECT INTO xfrm * FROM config.xml_transform WHERE name = attr_def.format;
+
+            -- See if we can skip the XSLT ... it's expensive
+            IF prev_xfrm IS NULL OR prev_xfrm <> xfrm.name THEN
+                -- Can't skip the transform
+                IF xfrm.xslt <> '---' THEN
+                    transformed_xml := oils_xslt_process(xml,xfrm.xslt);
+                ELSE
+                    transformed_xml := xml;
+                END IF;
+
+                prev_xfrm := xfrm.name;
+            END IF;
+
+            IF xfrm.name IS NULL THEN
+                -- just grab the marcxml (empty) transform
+                SELECT INTO xfrm * FROM config.xml_transform WHERE xslt = '---' LIMIT 1;
+                prev_xfrm := xfrm.name;
+            END IF;
+
+            attr_value := oils_xpath_string(attr_def.xpath, transformed_xml, COALESCE(attr_def.joiner,' '), ARRAY[ARRAY[xfrm.prefix, xfrm.namespace_uri]]);
+
+        ELSIF attr_def.phys_char_sf IS NOT NULL THEN -- a named Physical Characteristic, see config.marc21_physical_characteristic_*_map
+            SELECT  m.value::TEXT INTO attr_value
+              FROM  vandelay.marc21_physical_characteristics(xml) v
+                    JOIN config.marc21_physical_characteristic_value_map m ON (m.id = v.value)
+              WHERE v.subfield = attr_def.phys_char_sf
+              LIMIT 1; -- Just in case ...
+
+        END IF;
+
+        -- apply index normalizers to attr_value
+        FOR normalizer IN
+            SELECT  n.func AS func,
+                    n.param_count AS param_count,
+                    m.params AS params
+              FROM  config.index_normalizer n
+                    JOIN config.record_attr_index_norm_map m ON (m.norm = n.id)
+              WHERE attr = attr_def.name
+              ORDER BY m.pos LOOP
+                EXECUTE 'SELECT ' || normalizer.func || '(' ||
+                    quote_literal( attr_value ) ||
+                    CASE
+                        WHEN normalizer.param_count > 0
+                            THEN ',' || REPLACE(REPLACE(BTRIM(normalizer.params,'[]'),E'\'',E'\\\''),E'"',E'\'')
+                            ELSE ''
+                        END ||
+                    ')' INTO attr_value;
+
+        END LOOP;
+
+        -- Add the new value to the hstore
+        new_attrs := new_attrs || hstore( attr_def.name, attr_value );
+
+    END LOOP;
+
+    RETURN new_attrs;
+END;
+$_$ LANGUAGE PLPGSQL;
+
+CREATE OR REPLACE FUNCTION vandelay.extract_rec_attrs ( xml TEXT ) RETURNS hstore AS $_$
+    SELECT vandelay.extract_rec_attrs( $1, (SELECT ARRAY_ACCUM(name) FROM config.record_attr_definition));
+$_$ LANGUAGE SQL;
+
+-- Everything between this comment and the beginning of the definition of
+-- vandelay.match_bib_record() is strictly in service of that function.
+CREATE TYPE vandelay.match_set_test_result AS (record BIGINT, quality INTEGER);
+
+CREATE OR REPLACE FUNCTION vandelay.match_set_test_marcxml(
+    match_set_id INTEGER, record_xml TEXT
+) RETURNS SETOF vandelay.match_set_test_result AS $$
+DECLARE
+    tags_rstore HSTORE;
+    svf_rstore  HSTORE;
+    coal        TEXT;
+    joins       TEXT;
+    query_      TEXT;
+    wq          TEXT;
+    qvalue      INTEGER;
+    rec         RECORD;
+BEGIN
+    tags_rstore := vandelay.flatten_marc_hstore(record_xml);
+    svf_rstore := vandelay.extract_rec_attrs(record_xml);
+
+    CREATE TEMPORARY TABLE _vandelay_tmp_qrows (q INTEGER);
+    CREATE TEMPORARY TABLE _vandelay_tmp_jrows (j TEXT);
+
+    -- generate the where clause and return that directly (into wq), and as
+    -- a side-effect, populate the _vandelay_tmp_[qj]rows tables.
+    wq := vandelay.get_expr_from_match_set(match_set_id);
+
+    query_ := 'SELECT bre.id AS record, ';
+
+    -- qrows table is for the quality bits we add to the SELECT clause
+    SELECT ARRAY_TO_STRING(
+        ARRAY_ACCUM('COALESCE(n' || q::TEXT || '.quality, 0)'), ' + '
+    ) INTO coal FROM _vandelay_tmp_qrows;
+
+    -- our query string so far is the SELECT clause and the inital FROM.
+    -- no JOINs yet nor the WHERE clause
+    query_ := query_ || coal || ' AS quality ' || E'\n' ||
+        'FROM biblio.record_entry bre ';
+
+    -- jrows table is for the joins we must make (and the real text conditions)
+    SELECT ARRAY_TO_STRING(ARRAY_ACCUM(j), E'\n') INTO joins
+        FROM _vandelay_tmp_jrows;
+
+    -- add those joins and the where clause to our query.
+    query_ := query_ || joins || E'\n' || 'WHERE ' || wq || ' AND not bre.deleted';
+
+    -- this will return rows of record,quality
+    FOR rec IN EXECUTE query_ USING tags_rstore, svf_rstore LOOP
+        RETURN NEXT rec;
+    END LOOP;
+
+    DROP TABLE _vandelay_tmp_qrows;
+    DROP TABLE _vandelay_tmp_jrows;
+    RETURN;
+END;
+
+$$ LANGUAGE PLPGSQL;
+
+CREATE OR REPLACE FUNCTION vandelay.flatten_marc_hstore(
+    record_xml TEXT
+) RETURNS HSTORE AS $$
+BEGIN
+    RETURN (SELECT
+        HSTORE(
+            ARRAY_ACCUM(tag || (COALESCE(subfield, ''))),
+            ARRAY_ACCUM(value)
+        )
+        FROM (
+            SELECT tag, subfield, ARRAY_ACCUM(value)::TEXT AS value
+                FROM vandelay.flatten_marc(record_xml)
+                GROUP BY tag, subfield ORDER BY tag, subfield
+        ) subquery
+    );
+END;
+$$ LANGUAGE PLPGSQL;
+
+CREATE OR REPLACE FUNCTION vandelay.get_expr_from_match_set(
+    match_set_id INTEGER
+) RETURNS TEXT AS $$
+DECLARE
+    root    vandelay.match_set_point;
+BEGIN
+    SELECT * INTO root FROM vandelay.match_set_point
+        WHERE parent IS NULL AND match_set = match_set_id;
+
+    RETURN vandelay.get_expr_from_match_set_point(root);
+END;
+$$  LANGUAGE PLPGSQL;
+
+CREATE OR REPLACE FUNCTION vandelay.get_expr_from_match_set_point(
+    node vandelay.match_set_point
+) RETURNS TEXT AS $$
+DECLARE
+    q           TEXT;
+    i           INTEGER;
+    this_op     TEXT;
+    children    INTEGER[];
+    child       vandelay.match_set_point;
+BEGIN
+    SELECT ARRAY_ACCUM(id) INTO children FROM vandelay.match_set_point
+        WHERE parent = node.id;
+
+    IF ARRAY_LENGTH(children, 1) > 0 THEN
+        this_op := vandelay._get_expr_render_one(node);
+        q := '(';
+        i := 1;
+        WHILE children[i] IS NOT NULL LOOP
+            SELECT * INTO child FROM vandelay.match_set_point
+                WHERE id = children[i];
+            IF i > 1 THEN
+                q := q || ' ' || this_op || ' ';
+            END IF;
+            i := i + 1;
+            q := q || vandelay.get_expr_from_match_set_point(child);
+        END LOOP;
+        q := q || ')';
+        RETURN q;
+    ELSIF node.bool_op IS NULL THEN
+        PERFORM vandelay._get_expr_push_qrow(node);
+        PERFORM vandelay._get_expr_push_jrow(node);
+        RETURN vandelay._get_expr_render_one(node);
+    ELSE
+        RETURN '';
+    END IF;
+END;
+$$  LANGUAGE PLPGSQL;
+
+CREATE OR REPLACE FUNCTION vandelay._get_expr_push_qrow(
+    node vandelay.match_set_point
+) RETURNS VOID AS $$
+DECLARE
+BEGIN
+    INSERT INTO _vandelay_tmp_qrows (q) VALUES (node.id);
+END;
+$$ LANGUAGE PLPGSQL;
+
+CREATE OR REPLACE FUNCTION vandelay._get_expr_push_jrow(
+    node vandelay.match_set_point
+) RETURNS VOID AS $$
+DECLARE
+    jrow        TEXT;
+    my_alias    TEXT;
+    op          TEXT;
+    tagkey      TEXT;
+BEGIN
+    IF node.negate THEN
+        op := '<>';
+    ELSE
+        op := '=';
+    END IF;
+
+    IF node.tag IS NOT NULL THEN
+        tagkey := node.tag;
+        IF node.subfield IS NOT NULL THEN
+            tagkey := tagkey || node.subfield;
+        END IF;
+    END IF;
+
+    my_alias := 'n' || node.id::TEXT;
+
+    jrow := 'LEFT JOIN (SELECT *, ' || node.quality ||
+        ' AS quality FROM metabib.';
+    IF node.tag IS NOT NULL THEN
+        jrow := jrow || 'full_rec) ' || my_alias || ' ON (' ||
+            my_alias || '.record = bre.id AND ' || my_alias || '.tag = ''' ||
+            node.tag || '''';
+        IF node.subfield IS NOT NULL THEN
+            jrow := jrow || ' AND ' || my_alias || '.subfield = ''' ||
+                node.subfield || '''';
+        END IF;
+        jrow := jrow || ' AND (' || my_alias || '.value ' || op ||
+            ' ANY(($1->''' || tagkey || ''')::TEXT[])))';
+    ELSE    -- svf
+        jrow := jrow || 'record_attr) ' || my_alias || ' ON (' ||
+            my_alias || '.id = bre.id AND (' ||
+            my_alias || '.attrs->''' || node.svf ||
+            ''' ' || op || ' $2->''' || node.svf || '''))';
+    END IF;
+    INSERT INTO _vandelay_tmp_jrows (j) VALUES (jrow);
+END;
+$$ LANGUAGE PLPGSQL;
+
+CREATE OR REPLACE FUNCTION vandelay._get_expr_render_one(
+    node vandelay.match_set_point
+) RETURNS TEXT AS $$
+DECLARE
+    s           TEXT;
+BEGIN
+    IF node.bool_op IS NOT NULL THEN
+        RETURN node.bool_op;
+    ELSE
+        RETURN '(n' || node.id::TEXT || '.id IS NOT NULL)';
+    END IF;
+END;
+$$ LANGUAGE PLPGSQL;
+
+CREATE OR REPLACE FUNCTION vandelay.match_bib_record() RETURNS TRIGGER AS $func$
+DECLARE
+    incoming_existing_id    TEXT;
+    test_result             vandelay.match_set_test_result%ROWTYPE;
+    tmp_rec                 BIGINT;
+    match_set               INT;
+BEGIN
+    IF TG_OP IN ('INSERT','UPDATE') AND NEW.imported_as IS NOT NULL THEN
+        RETURN NEW;
+    END IF;
+
+    DELETE FROM vandelay.bib_match WHERE queued_record = NEW.id;
+
+    SELECT q.match_set INTO match_set FROM vandelay.bib_queue q WHERE q.id = NEW.queue;
+
+    IF match_set IS NOT NULL THEN
+        NEW.quality := vandelay.measure_record_quality( NEW.marc, match_set );
+    END IF;
+
+    -- Perfect matches on 901$c exit early with a match with high quality.
+    incoming_existing_id :=
+        oils_xpath_string('//*[@tag="901"]/*[@code="c"][1]', NEW.marc);
+
+    IF incoming_existing_id IS NOT NULL AND incoming_existing_id != '' THEN
+        SELECT id INTO tmp_rec FROM biblio.record_entry WHERE id = incoming_existing_id::bigint;
+        IF tmp_rec IS NOT NULL THEN
+            INSERT INTO vandelay.bib_match (queued_record, eg_record, match_score, quality) 
+                SELECT
+                    NEW.id, 
+                    b.id,
+                    9999,
+                    -- note: no match_set means quality==0
+                    vandelay.measure_record_quality( b.marc, match_set )
+                FROM biblio.record_entry b
+                WHERE id = incoming_existing_id::bigint;
+        END IF;
+    END IF;
+
+    IF match_set IS NULL THEN
+        RETURN NEW;
+    END IF;
+
+    FOR test_result IN SELECT * FROM
+        vandelay.match_set_test_marcxml(match_set, NEW.marc) LOOP
+
+        INSERT INTO vandelay.bib_match ( queued_record, eg_record, match_score, quality )
+            SELECT  
+                NEW.id,
+                test_result.record,
+                test_result.quality,
+                vandelay.measure_record_quality( b.marc, match_set )
+               FROM  biblio.record_entry b
+               WHERE id = test_result.record;
+
+    END LOOP;
+
+    RETURN NEW;
+END;
+$func$ LANGUAGE PLPGSQL;
+
+CREATE OR REPLACE FUNCTION vandelay.measure_record_quality ( xml TEXT, match_set_id INT ) RETURNS INT AS $_$
+DECLARE
+    out_q   INT := 0;
+    rvalue  TEXT;
+    test    vandelay.match_set_quality%ROWTYPE;
+BEGIN
+
+    FOR test IN SELECT * FROM vandelay.match_set_quality WHERE match_set = match_set_id LOOP
+        IF test.tag IS NOT NULL THEN
+            FOR rvalue IN SELECT value FROM vandelay.flatten_marc( xml ) WHERE tag = test.tag AND subfield = test.subfield LOOP
+                IF test.value = rvalue THEN
+                    out_q := out_q + test.quality;
+                END IF;
+            END LOOP;
+        ELSE
+            IF test.value = vandelay.extract_rec_attrs(xml, ARRAY[test.svf]) -> test.svf THEN
+                out_q := out_q + test.quality;
+            END IF;
+        END IF;
+    END LOOP;
+
+    RETURN out_q;
+END;
+$_$ LANGUAGE PLPGSQL;
+
+
+CREATE OR REPLACE FUNCTION vandelay.overlay_bib_record ( import_id BIGINT, eg_id BIGINT, merge_profile_id INT ) RETURNS BOOL AS $$
+DECLARE
+    merge_profile   vandelay.merge_profile%ROWTYPE;
+    dyn_profile     vandelay.compile_profile%ROWTYPE;
+    editor_string   TEXT;
+    editor_id       INT;
+    source_marc     TEXT;
+    target_marc     TEXT;
+    eg_marc         TEXT;
+    v_marc          TEXT;
+    replace_rule    TEXT;
+BEGIN
+
+    SELECT  q.marc INTO v_marc
+      FROM  vandelay.queued_record q
+            JOIN vandelay.bib_match m ON (m.queued_record = q.id AND q.id = import_id)
+      LIMIT 1;
+
+    IF v_marc IS NULL THEN
+        -- RAISE NOTICE 'no marc for vandelay or bib record';
+        RETURN FALSE;
+    END IF;
+
+    IF vandelay.template_overlay_bib_record( v_marc, eg_id, merge_profile_id) THEN
+        UPDATE  vandelay.queued_bib_record
+          SET   imported_as = eg_id,
+                import_time = NOW()
+          WHERE id = import_id;
+
+        editor_string := (oils_xpath('//*[@tag="905"]/*[@code="u"]/text()',v_marc))[1];
+
+        IF editor_string IS NOT NULL AND editor_string <> '' THEN
+            SELECT usr INTO editor_id FROM actor.card WHERE barcode = editor_string;
+
+            IF editor_id IS NULL THEN
+                SELECT id INTO editor_id FROM actor.usr WHERE usrname = editor_string;
+            END IF;
+
+            IF editor_id IS NOT NULL THEN
+                UPDATE biblio.record_entry SET editor = editor_id WHERE id = eg_id;
+            END IF;
+        END IF;
+
+        RETURN TRUE;
+    END IF;
+
+    -- RAISE NOTICE 'update of biblio.record_entry failed';
+
+    RETURN FALSE;
+
+END;
+$$ LANGUAGE PLPGSQL;
+
+
+CREATE OR REPLACE FUNCTION vandelay.auto_overlay_bib_record_with_best ( import_id BIGINT, merge_profile_id INT, lwm_ratio_value_p NUMERIC ) RETURNS BOOL AS $$
+DECLARE
+    eg_id           BIGINT;
+    lwm_ratio_value NUMERIC;
+BEGIN
+
+    lwm_ratio_value := COALESCE(lwm_ratio_value_p, 0.0);
+
+    PERFORM * FROM vandelay.queued_bib_record WHERE import_time IS NOT NULL AND id = import_id;
+
+    IF FOUND THEN
+        -- RAISE NOTICE 'already imported, cannot auto-overlay'
+        RETURN FALSE;
+    END IF;
+
+    SELECT  m.eg_record INTO eg_id
+      FROM  vandelay.bib_match m
+            JOIN vandelay.queued_bib_record qr ON (m.queued_record = qr.id)
+            JOIN vandelay.bib_queue q ON (qr.queue = q.id)
+            JOIN biblio.record_entry r ON (r.id = m.eg_record)
+      WHERE m.queued_record = import_id
+            AND qr.quality::NUMERIC / COALESCE(NULLIF(m.quality,0),1)::NUMERIC >= lwm_ratio_value
+      ORDER BY  m.match_score DESC, -- required match score
+                qr.quality::NUMERIC / COALESCE(NULLIF(m.quality,0),1)::NUMERIC DESC, -- quality tie breaker
+                m.id -- when in doubt, use the first match
+      LIMIT 1;
+
+    IF eg_id IS NULL THEN
+        -- RAISE NOTICE 'incoming record is not of high enough quality';
+        RETURN FALSE;
+    END IF;
+
+    RETURN vandelay.overlay_bib_record( import_id, eg_id, merge_profile_id );
+END;
+$$ LANGUAGE PLPGSQL;
+
+CREATE OR REPLACE FUNCTION vandelay.auto_overlay_bib_record_with_best ( import_id BIGINT, merge_profile_id INT, lwm_ratio_value_p NUMERIC ) RETURNS BOOL AS $$
+DECLARE
+    eg_id           BIGINT;
+    lwm_ratio_value NUMERIC;
+BEGIN
+
+    lwm_ratio_value := COALESCE(lwm_ratio_value_p, 0.0);
+
+    PERFORM * FROM vandelay.queued_bib_record WHERE import_time IS NOT NULL AND id = import_id;
+
+    IF FOUND THEN
+        -- RAISE NOTICE 'already imported, cannot auto-overlay'
+        RETURN FALSE;
+    END IF;
+
+    SELECT  m.eg_record INTO eg_id
+      FROM  vandelay.bib_match m
+            JOIN vandelay.queued_bib_record qr ON (m.queued_record = qr.id)
+            JOIN vandelay.bib_queue q ON (qr.queue = q.id)
+            JOIN biblio.record_entry r ON (r.id = m.eg_record)
+      WHERE m.queued_record = import_id
+            AND qr.quality::NUMERIC / COALESCE(NULLIF(m.quality,0),1)::NUMERIC >= lwm_ratio_value
+      ORDER BY  m.match_score DESC, -- required match score
+                qr.quality::NUMERIC / COALESCE(NULLIF(m.quality,0),1)::NUMERIC DESC, -- quality tie breaker
+                m.id -- when in doubt, use the first match
+      LIMIT 1;
+
+    IF eg_id IS NULL THEN
+        -- RAISE NOTICE 'incoming record is not of high enough quality';
+        RETURN FALSE;
+    END IF;
+
+    RETURN vandelay.overlay_bib_record( import_id, eg_id, merge_profile_id );
+END;
+$$ LANGUAGE PLPGSQL;
+
+
+CREATE OR REPLACE FUNCTION vandelay.auto_overlay_bib_queue_with_best ( queue_id BIGINT, merge_profile_id INT, lwm_ratio_value NUMERIC ) RETURNS SETOF BIGINT AS $$
+DECLARE
+    queued_record   vandelay.queued_bib_record%ROWTYPE;
+BEGIN
+
+    FOR queued_record IN SELECT * FROM vandelay.queued_bib_record WHERE queue = queue_id AND import_time IS NULL LOOP
+
+        IF vandelay.auto_overlay_bib_record_with_best( queued_record.id, merge_profile_id, lwm_ratio_value ) THEN
+            RETURN NEXT queued_record.id;
+        END IF;
+
+    END LOOP;
+
+    RETURN;
+    
+END;
+$$ LANGUAGE PLPGSQL;
+
+CREATE OR REPLACE FUNCTION vandelay.auto_overlay_bib_queue_with_best ( import_id BIGINT, merge_profile_id INT ) RETURNS SETOF BIGINT AS $$
+    SELECT vandelay.auto_overlay_bib_queue_with_best( $1, $2, p.lwm_ratio ) FROM vandelay.merge_profile p WHERE id = $2;
+$$ LANGUAGE SQL;
+
+CREATE OR REPLACE FUNCTION vandelay.ingest_bib_marc ( ) RETURNS TRIGGER AS $$
+DECLARE
+    value   TEXT;
+    atype   TEXT;
+    adef    RECORD;
+BEGIN
+    IF TG_OP IN ('INSERT','UPDATE') AND NEW.imported_as IS NOT NULL THEN
+        RETURN NEW;
+    END IF;
+
+    FOR adef IN SELECT * FROM vandelay.bib_attr_definition LOOP
+
+        SELECT extract_marc_field('vandelay.queued_bib_record', id, adef.xpath, adef.remove) INTO value FROM vandelay.queued_bib_record WHERE id = NEW.id;
+        IF (value IS NOT NULL AND value <> '') THEN
+            INSERT INTO vandelay.queued_bib_record_attr (record, field, attr_value) VALUES (NEW.id, adef.id, value);
+        END IF;
+
+    END LOOP;
+
+    RETURN NULL;
+END;
+$$ LANGUAGE PLPGSQL;
+
+CREATE OR REPLACE FUNCTION vandelay.ingest_bib_items ( ) RETURNS TRIGGER AS $func$
+DECLARE
+    attr_def    BIGINT;
+    item_data   vandelay.import_item%ROWTYPE;
+BEGIN
+
+    IF TG_OP IN ('INSERT','UPDATE') AND NEW.imported_as IS NOT NULL THEN
+        RETURN NEW;
+    END IF;
+
+    SELECT item_attr_def INTO attr_def FROM vandelay.bib_queue WHERE id = NEW.queue;
+
+    FOR item_data IN SELECT * FROM vandelay.ingest_items( NEW.id::BIGINT, attr_def ) LOOP
+        INSERT INTO vandelay.import_item (
+            record,
+            definition,
+            owning_lib,
+            circ_lib,
+            call_number,
+            copy_number,
+            status,
+            location,
+            circulate,
+            deposit,
+            deposit_amount,
+            ref,
+            holdable,
+            price,
+            barcode,
+            circ_modifier,
+            circ_as_type,
+            alert_message,
+            pub_note,
+            priv_note,
+            opac_visible
+        ) VALUES (
+            NEW.id,
+            item_data.definition,
+            item_data.owning_lib,
+            item_data.circ_lib,
+            item_data.call_number,
+            item_data.copy_number,
+            item_data.status,
+            item_data.location,
+            item_data.circulate,
+            item_data.deposit,
+            item_data.deposit_amount,
+            item_data.ref,
+            item_data.holdable,
+            item_data.price,
+            item_data.barcode,
+            item_data.circ_modifier,
+            item_data.circ_as_type,
+            item_data.alert_message,
+            item_data.pub_note,
+            item_data.priv_note,
+            item_data.opac_visible
+        );
+    END LOOP;
+
+    RETURN NULL;
+END;
+$func$ LANGUAGE PLPGSQL;
+
+CREATE OR REPLACE FUNCTION vandelay.cleanup_bib_marc ( ) RETURNS TRIGGER AS $$
+BEGIN
+    IF TG_OP IN ('INSERT','UPDATE') AND NEW.imported_as IS NOT NULL THEN
+        RETURN NEW;
+    END IF;
+
+    DELETE FROM vandelay.queued_bib_record_attr WHERE record = OLD.id;
+    DELETE FROM vandelay.import_item WHERE record = OLD.id;
+
+    IF TG_OP = 'UPDATE' THEN
+        RETURN NEW;
+    END IF;
+    RETURN OLD;
+END;
+$$ LANGUAGE PLPGSQL;
+
+-- ALTER TABLEs...
+
+DROP TRIGGER zz_match_bibs_trigger ON vandelay.queued_bib_record;
+CREATE TRIGGER zz_match_bibs_trigger
+    BEFORE INSERT OR UPDATE ON vandelay.queued_bib_record
+    FOR EACH ROW EXECUTE PROCEDURE vandelay.match_bib_record();
+
+CREATE OR REPLACE FUNCTION vandelay.ingest_authority_marc ( ) RETURNS TRIGGER AS $$
+DECLARE
+    value   TEXT;
+    atype   TEXT;
+    adef    RECORD;
+BEGIN
+    IF TG_OP IN ('INSERT','UPDATE') AND NEW.imported_as IS NOT NULL THEN
+        RETURN NEW;
+    END IF;
+
+    FOR adef IN SELECT * FROM vandelay.authority_attr_definition LOOP
+
+        SELECT extract_marc_field('vandelay.queued_authority_record', id, adef.xpath, adef.remove) INTO value FROM vandelay.queued_authority_record WHERE id = NEW.id;
+        IF (value IS NOT NULL AND value <> '') THEN
+            INSERT INTO vandelay.queued_authority_record_attr (record, field, attr_value) VALUES (NEW.id, adef.id, value);
+        END IF;
+
+    END LOOP;
+
+    RETURN NULL;
+END;
+$$ LANGUAGE PLPGSQL;
+
+ALTER TABLE vandelay.authority_attr_definition DROP COLUMN ident;
+ALTER TABLE vandelay.queued_authority_record
+    ADD COLUMN import_error TEXT REFERENCES vandelay.import_error (code) ON DELETE SET NULL ON UPDATE CASCADE DEFERRABLE INITIALLY DEFERRED,
+    ADD COLUMN error_detail TEXT;
+
+ALTER TABLE vandelay.authority_match DROP COLUMN matched_attr;
+
+CREATE OR REPLACE FUNCTION vandelay.cleanup_authority_marc ( ) RETURNS TRIGGER AS $$
+BEGIN
+    IF TG_OP IN ('INSERT','UPDATE') AND NEW.imported_as IS NOT NULL THEN
+        RETURN NEW;
+    END IF;
+
+    DELETE FROM vandelay.queued_authority_record_attr WHERE record = OLD.id;
+    IF TG_OP = 'UPDATE' THEN
+        RETURN NEW;
+    END IF;
+    RETURN OLD;
+END;
+$$ LANGUAGE PLPGSQL;
+
+CREATE OR REPLACE FUNCTION authority.flatten_marc ( rid BIGINT ) RETURNS SETOF authority.full_rec AS $func$
+DECLARE
+       auth    authority.record_entry%ROWTYPE;
+       output  authority.full_rec%ROWTYPE;
+       field   RECORD;
+BEGIN
+       SELECT INTO auth * FROM authority.record_entry WHERE id = rid;
+
+       FOR field IN SELECT * FROM vandelay.flatten_marc( auth.marc ) LOOP
+               output.record := rid;
+               output.ind1 := field.ind1;
+               output.ind2 := field.ind2;
+               output.tag := field.tag;
+               output.subfield := field.subfield;
+               output.value := field.value;
+
+               RETURN NEXT output;
+       END LOOP;
+END;
+$func$ LANGUAGE PLPGSQL;
+
+CREATE OR REPLACE FUNCTION biblio.flatten_marc ( rid BIGINT ) RETURNS SETOF metabib.full_rec AS $func$
+DECLARE
+       bib     biblio.record_entry%ROWTYPE;
+       output  metabib.full_rec%ROWTYPE;
+       field   RECORD;
+BEGIN
+       SELECT INTO bib * FROM biblio.record_entry WHERE id = rid;
+
+       FOR field IN SELECT * FROM vandelay.flatten_marc( bib.marc ) LOOP
+               output.record := rid;
+               output.ind1 := field.ind1;
+               output.ind2 := field.ind2;
+               output.tag := field.tag;
+               output.subfield := field.subfield;
+               output.value := field.value;
+
+               RETURN NEXT output;
+       END LOOP;
+END;
+$func$ LANGUAGE PLPGSQL;
+
+-----------------------------------------------
+-- Seed data for import errors
+-----------------------------------------------
+
+INSERT INTO vandelay.import_error ( code, description ) VALUES ( 'general.unknown', oils_i18n_gettext('general.unknown', 'Import or Overlay failed', 'vie', 'description') );
+INSERT INTO vandelay.import_error ( code, description ) VALUES ( 'import.item.duplicate.barcode', oils_i18n_gettext('import.item.duplicate.barcode', 'Import failed due to barcode collision', 'vie', 'description') );
+INSERT INTO vandelay.import_error ( code, description ) VALUES ( 'import.item.invalid.circ_modifier', oils_i18n_gettext('import.item.invalid.circ_modifier', 'Import failed due to invalid circulation modifier', 'vie', 'description') );
+INSERT INTO vandelay.import_error ( code, description ) VALUES ( 'import.item.invalid.location', oils_i18n_gettext('import.item.invalid.location', 'Import failed due to invalid copy location', 'vie', 'description') );
+INSERT INTO vandelay.import_error ( code, description ) VALUES ( 'import.duplicate.sysid', oils_i18n_gettext('import.duplicate.sysid', 'Import failed due to system id collision', 'vie', 'description') );
+INSERT INTO vandelay.import_error ( code, description ) VALUES ( 'import.duplicate.tcn', oils_i18n_gettext('import.duplicate.sysid', 'Import failed due to system id collision', 'vie', 'description') );
+INSERT INTO vandelay.import_error ( code, description ) VALUES ( 'overlay.missing.sysid', oils_i18n_gettext('overlay.missing.sysid', 'Overlay failed due to missing system id', 'vie', 'description') );
+INSERT INTO vandelay.import_error ( code, description ) VALUES ( 'import.auth.duplicate.acn', oils_i18n_gettext('import.auth.duplicate.acn', 'Import failed due to Accession Number collision', 'vie', 'description') );
+INSERT INTO vandelay.import_error ( code, description ) VALUES ( 'import.xml.malformed', oils_i18n_gettext('import.xml.malformed', 'Malformed record cause Import failure', 'vie', 'description') );
+INSERT INTO vandelay.import_error ( code, description ) VALUES ( 'overlay.xml.malformed', oils_i18n_gettext('overlay.xml.malformed', 'Malformed record cause Overlay failure', 'vie', 'description') );
+INSERT INTO vandelay.import_error ( code, description ) VALUES ( 'overlay.record.quality', oils_i18n_gettext('overlay.record.quality', 'New record had insufficient quality', 'vie', 'description') );
+
+
+----------------------------------------------------------------
+-- Seed data for queued record/item exports
+----------------------------------------------------------------
+
+INSERT INTO action_trigger.hook (key,core_type,description,passive) VALUES (
+        'vandelay.queued_bib_record.print',
+        'vqbr', 
+        oils_i18n_gettext(
+            'vandelay.queued_bib_record.print',
+            'Print output has been requested for records in an Importer Bib Queue.',
+            'ath',
+            'description'
+        ), 
+        FALSE
+    )
+    ,(
+        'vandelay.queued_bib_record.csv',
+        'vqbr', 
+        oils_i18n_gettext(
+            'vandelay.queued_bib_record.csv',
+            'CSV output has been requested for records in an Importer Bib Queue.',
+            'ath',
+            'description'
+        ), 
+        FALSE
+    )
+    ,(
+        'vandelay.queued_bib_record.email',
+        'vqbr', 
+        oils_i18n_gettext(
+            'vandelay.queued_bib_record.email',
+            'An email has been requested for records in an Importer Bib Queue.',
+            'ath',
+            'description'
+        ), 
+        FALSE
+    )
+    ,(
+        'vandelay.queued_auth_record.print',
+        'vqar', 
+        oils_i18n_gettext(
+            'vandelay.queued_auth_record.print',
+            'Print output has been requested for records in an Importer Authority Queue.',
+            'ath',
+            'description'
+        ), 
+        FALSE
+    )
+    ,(
+        'vandelay.queued_auth_record.csv',
+        'vqar', 
+        oils_i18n_gettext(
+            'vandelay.queued_auth_record.csv',
+            'CSV output has been requested for records in an Importer Authority Queue.',
+            'ath',
+            'description'
+        ), 
+        FALSE
+    )
+    ,(
+        'vandelay.queued_auth_record.email',
+        'vqar', 
+        oils_i18n_gettext(
+            'vandelay.queued_auth_record.email',
+            'An email has been requested for records in an Importer Authority Queue.',
+            'ath',
+            'description'
+        ), 
+        FALSE
+    )
+    ,(
+        'vandelay.import_items.print',
+        'vii', 
+        oils_i18n_gettext(
+            'vandelay.import_items.print',
+            'Print output has been requested for Import Items from records in an Importer Bib Queue.',
+            'ath',
+            'description'
+        ), 
+        FALSE
+    )
+    ,(
+        'vandelay.import_items.csv',
+        'vii', 
+        oils_i18n_gettext(
+            'vandelay.import_items.csv',
+            'CSV output has been requested for Import Items from records in an Importer Bib Queue.',
+            'ath',
+            'description'
+        ), 
+        FALSE
+    )
+    ,(
+        'vandelay.import_items.email',
+        'vii', 
+        oils_i18n_gettext(
+            'vandelay.import_items.email',
+            'An email has been requested for Import Items from records in an Importer Bib Queue.',
+            'ath',
+            'description'
+        ), 
+        FALSE
+    )
+;
+
+INSERT INTO action_trigger.event_definition (
+        id,
+        active,
+        owner,
+        name,
+        hook,
+        validator,
+        reactor,
+        group_field,
+        granularity,
+        template
+    ) VALUES (
+        39,
+        TRUE,
+        1,
+        'Print Output for Queued Bib Records',
+        'vandelay.queued_bib_record.print',
+        'NOOP_True',
+        'ProcessTemplate',
+        'queue.owner',
+        'print-on-demand',
+$$
+[%- USE date -%]
+<pre>
+Queue ID: [% target.0.queue.id %]
+Queue Name: [% target.0.queue.name %]
+Queue Type: [% target.0.queue.queue_type %]
+Complete? [% target.0.queue.complete %]
+
+    [% FOR vqbr IN target %]
+=-=-=
+ Title of work    | [% helpers.get_queued_bib_attr('title',vqbr.attributes) %]
+ Author of work   | [% helpers.get_queued_bib_attr('author',vqbr.attributes) %]
+ Language of work | [% helpers.get_queued_bib_attr('language',vqbr.attributes) %]
+ Pagination       | [% helpers.get_queued_bib_attr('pagination',vqbr.attributes) %]
+ ISBN             | [% helpers.get_queued_bib_attr('isbn',vqbr.attributes) %]
+ ISSN             | [% helpers.get_queued_bib_attr('issn',vqbr.attributes) %]
+ Price            | [% helpers.get_queued_bib_attr('price',vqbr.attributes) %]
+ Accession Number | [% helpers.get_queued_bib_attr('rec_identifier',vqbr.attributes) %]
+ TCN Value        | [% helpers.get_queued_bib_attr('eg_tcn',vqbr.attributes) %]
+ TCN Source       | [% helpers.get_queued_bib_attr('eg_tcn_source',vqbr.attributes) %]
+ Internal ID      | [% helpers.get_queued_bib_attr('eg_identifier',vqbr.attributes) %]
+ Publisher        | [% helpers.get_queued_bib_attr('publisher',vqbr.attributes) %]
+ Publication Date | [% helpers.get_queued_bib_attr('pubdate',vqbr.attributes) %]
+ Edition          | [% helpers.get_queued_bib_attr('edition',vqbr.attributes) %]
+ Item Barcode     | [% helpers.get_queued_bib_attr('item_barcode',vqbr.attributes) %]
+
+    [% END %]
+</pre>
+$$
+    )
+;
+
+INSERT INTO action_trigger.environment ( event_def, path) VALUES (
+    39, 'attributes')
+    ,( 39, 'queue')
+;
+
+INSERT INTO action_trigger.event_definition (
+        id,
+        active,
+        owner,
+        name,
+        hook,
+        validator,
+        reactor,
+        group_field,
+        granularity,
+        template
+    ) VALUES (
+        40,
+        TRUE,
+        1,
+        'CSV Output for Queued Bib Records',
+        'vandelay.queued_bib_record.csv',
+        'NOOP_True',
+        'ProcessTemplate',
+        'queue.owner',
+        'print-on-demand',
+$$
+[%- USE date -%]
+"Title of work","Author of work","Language of work","Pagination","ISBN","ISSN","Price","Accession Number","TCN Value","TCN Source","Internal ID","Publisher","Publication Date","Edition","Item Barcode"
+[% FOR vqbr IN target %]"[% helpers.get_queued_bib_attr('title',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('author',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('language',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('pagination',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('isbn',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('issn',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('price',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('rec_identifier',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('eg_tcn',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('eg_tcn_source',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('eg_identifier',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('publisher',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('pubdate',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('edition',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('item_barcode',vqbr.attributes) | replace('"', '""') %]"
+[% END %]
+$$
+    )
+;
+
+INSERT INTO action_trigger.environment ( event_def, path) VALUES (
+    40, 'attributes')
+    ,( 40, 'queue')
+;
+
+INSERT INTO action_trigger.event_definition (
+        id,
+        active,
+        owner,
+        name,
+        hook,
+        validator,
+        reactor,
+        group_field,
+        granularity,
+        template
+    ) VALUES (
+        41,
+        TRUE,
+        1,
+        'Email Output for Queued Bib Records',
+        'vandelay.queued_bib_record.email',
+        'NOOP_True',
+        'SendEmail',
+        'queue.owner',
+        NULL,
+$$
+[%- USE date -%]
+[%- SET user = target.0.queue.owner -%]
+To: [%- params.recipient_email || user.email || 'root@localhost' %]
+From: [%- params.sender_email || default_sender %]
+Subject: Bibs from Import Queue
+
+Queue ID: [% target.0.queue.id %]
+Queue Name: [% target.0.queue.name %]
+Queue Type: [% target.0.queue.queue_type %]
+Complete? [% target.0.queue.complete %]
+
+    [% FOR vqbr IN target %]
+=-=-=
+ Title of work    | [% helpers.get_queued_bib_attr('title',vqbr.attributes) %]
+ Author of work   | [% helpers.get_queued_bib_attr('author',vqbr.attributes) %]
+ Language of work | [% helpers.get_queued_bib_attr('language',vqbr.attributes) %]
+ Pagination       | [% helpers.get_queued_bib_attr('pagination',vqbr.attributes) %]
+ ISBN             | [% helpers.get_queued_bib_attr('isbn',vqbr.attributes) %]
+ ISSN             | [% helpers.get_queued_bib_attr('issn',vqbr.attributes) %]
+ Price            | [% helpers.get_queued_bib_attr('price',vqbr.attributes) %]
+ Accession Number | [% helpers.get_queued_bib_attr('rec_identifier',vqbr.attributes) %]
+ TCN Value        | [% helpers.get_queued_bib_attr('eg_tcn',vqbr.attributes) %]
+ TCN Source       | [% helpers.get_queued_bib_attr('eg_tcn_source',vqbr.attributes) %]
+ Internal ID      | [% helpers.get_queued_bib_attr('eg_identifier',vqbr.attributes) %]
+ Publisher        | [% helpers.get_queued_bib_attr('publisher',vqbr.attributes) %]
+ Publication Date | [% helpers.get_queued_bib_attr('pubdate',vqbr.attributes) %]
+ Edition          | [% helpers.get_queued_bib_attr('edition',vqbr.attributes) %]
+ Item Barcode     | [% helpers.get_queued_bib_attr('item_barcode',vqbr.attributes) %]
+
+    [% END %]
+
+$$
+    )
+;
+
+INSERT INTO action_trigger.environment ( event_def, path) VALUES (
+    41, 'attributes')
+    ,( 41, 'queue')
+    ,( 41, 'queue.owner')
+;
+
+INSERT INTO action_trigger.event_definition (
+        id,
+        active,
+        owner,
+        name,
+        hook,
+        validator,
+        reactor,
+        group_field,
+        granularity,
+        template
+    ) VALUES (
+        42,
+        TRUE,
+        1,
+        'Print Output for Queued Authority Records',
+        'vandelay.queued_auth_record.print',
+        'NOOP_True',
+        'ProcessTemplate',
+        'queue.owner',
+        'print-on-demand',
+$$
+[%- USE date -%]
+<pre>
+Queue ID: [% target.0.queue.id %]
+Queue Name: [% target.0.queue.name %]
+Queue Type: [% target.0.queue.queue_type %]
+Complete? [% target.0.queue.complete %]
+
+    [% FOR vqar IN target %]
+=-=-=
+ Record Identifier | [% helpers.get_queued_auth_attr('rec_identifier',vqar.attributes) %]
+
+    [% END %]
+</pre>
+$$
+    )
+;
+
+INSERT INTO action_trigger.environment ( event_def, path) VALUES (
+    42, 'attributes')
+    ,( 42, 'queue')
+;
+
+INSERT INTO action_trigger.event_definition (
+        id,
+        active,
+        owner,
+        name,
+        hook,
+        validator,
+        reactor,
+        group_field,
+        granularity,
+        template
+    ) VALUES (
+        43,
+        TRUE,
+        1,
+        'CSV Output for Queued Authority Records',
+        'vandelay.queued_auth_record.csv',
+        'NOOP_True',
+        'ProcessTemplate',
+        'queue.owner',
+        'print-on-demand',
+$$
+[%- USE date -%]
+"Record Identifier"
+[% FOR vqar IN target %]"[% helpers.get_queued_auth_attr('rec_identifier',vqar.attributes) | replace('"', '""') %]"
+[% END %]
+$$
+    )
+;
+
+INSERT INTO action_trigger.environment ( event_def, path) VALUES (
+    43, 'attributes')
+    ,( 43, 'queue')
+;
+
+INSERT INTO action_trigger.event_definition (
+        id,
+        active,
+        owner,
+        name,
+        hook,
+        validator,
+        reactor,
+        group_field,
+        granularity,
+        template
+    ) VALUES (
+        44,
+        TRUE,
+        1,
+        'Email Output for Queued Authority Records',
+        'vandelay.queued_auth_record.email',
+        'NOOP_True',
+        'SendEmail',
+        'queue.owner',
+        NULL,
+$$
+[%- USE date -%]
+[%- SET user = target.0.queue.owner -%]
+To: [%- params.recipient_email || user.email || 'root@localhost' %]
+From: [%- params.sender_email || default_sender %]
+Subject: Authorities from Import Queue
+
+Queue ID: [% target.0.queue.id %]
+Queue Name: [% target.0.queue.name %]
+Queue Type: [% target.0.queue.queue_type %]
+Complete? [% target.0.queue.complete %]
+
+    [% FOR vqar IN target %]
+=-=-=
+ Record Identifier | [% helpers.get_queued_auth_attr('rec_identifier',vqar.attributes) %]
+
+    [% END %]
+
+$$
+    )
+;
+
+INSERT INTO action_trigger.environment ( event_def, path) VALUES (
+    44, 'attributes')
+    ,( 44, 'queue')
+    ,( 44, 'queue.owner')
+;
+
+INSERT INTO action_trigger.event_definition (
+        id,
+        active,
+        owner,
+        name,
+        hook,
+        validator,
+        reactor,
+        group_field,
+        granularity,
+        template
+    ) VALUES (
+        45,
+        TRUE,
+        1,
+        'Print Output for Import Items from Queued Bib Records',
+        'vandelay.import_items.print',
+        'NOOP_True',
+        'ProcessTemplate',
+        'record.queue.owner',
+        'print-on-demand',
+$$
+[%- USE date -%]
+<pre>
+Queue ID: [% target.0.record.queue.id %]
+Queue Name: [% target.0.record.queue.name %]
+Queue Type: [% target.0.record.queue.queue_type %]
+Complete? [% target.0.record.queue.complete %]
+
+    [% FOR vii IN target %]
+=-=-=
+ Import Item ID         | [% vii.id %]
+ Title of work          | [% helpers.get_queued_bib_attr('title',vii.record.attributes) %]
+ ISBN                   | [% helpers.get_queued_bib_attr('isbn',vii.record.attributes) %]
+ Attribute Definition   | [% vii.definition %]
+ Import Error           | [% vii.import_error %]
+ Import Error Detail    | [% vii.error_detail %]
+ Owning Library         | [% vii.owning_lib %]
+ Circulating Library    | [% vii.circ_lib %]
+ Call Number            | [% vii.call_number %]
+ Copy Number            | [% vii.copy_number %]
+ Status                 | [% vii.status.name %]
+ Shelving Location      | [% vii.location.name %]
+ Circulate              | [% vii.circulate %]
+ Deposit                | [% vii.deposit %]
+ Deposit Amount         | [% vii.deposit_amount %]
+ Reference              | [% vii.ref %]
+ Holdable               | [% vii.holdable %]
+ Price                  | [% vii.price %]
+ Barcode                | [% vii.barcode %]
+ Circulation Modifier   | [% vii.circ_modifier %]
+ Circulate As MARC Type | [% vii.circ_as_type %]
+ Alert Message          | [% vii.alert_message %]
+ Public Note            | [% vii.pub_note %]
+ Private Note           | [% vii.priv_note %]
+ OPAC Visible           | [% vii.opac_visible %]
+
+    [% END %]
+</pre>
+$$
+    )
+;
+
+INSERT INTO action_trigger.environment ( event_def, path) VALUES (
+    45, 'record')
+    ,( 45, 'record.attributes')
+    ,( 45, 'record.queue')
+    ,( 45, 'record.queue.owner')
+;
+
+INSERT INTO action_trigger.event_definition (
+        id,
+        active,
+        owner,
+        name,
+        hook,
+        validator,
+        reactor,
+        group_field,
+        granularity,
+        template
+    ) VALUES (
+        46,
+        TRUE,
+        1,
+        'CSV Output for Import Items from Queued Bib Records',
+        'vandelay.import_items.csv',
+        'NOOP_True',
+        'ProcessTemplate',
+        'record.queue.owner',
+        'print-on-demand',
+$$
+[%- USE date -%]
+"Import Item ID","Title of work","ISBN","Attribute Definition","Import Error","Import Error Detail","Owning Library","Circulating Library","Call Number","Copy Number","Status","Shelving Location","Circulate","Deposit","Deposit Amount","Reference","Holdable","Price","Barcode","Circulation Modifier","Circulate As MARC Type","Alert Message","Public Note","Private Note","OPAC Visible"
+[% FOR vii IN target %]"[% vii.id | replace('"', '""') %]","[% helpers.get_queued_bib_attr('title',vii.record.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('isbn',vii.record.attributes) | replace('"', '""') %]","[% vii.definition | replace('"', '""') %]","[% vii.import_error | replace('"', '""') %]","[% vii.error_detail | replace('"', '""') %]","[% vii.owning_lib | replace('"', '""') %]","[% vii.circ_lib | replace('"', '""') %]","[% vii.call_number | replace('"', '""') %]","[% vii.copy_number | replace('"', '""') %]","[% vii.status.name | replace('"', '""') %]","[% vii.location.name | replace('"', '""') %]","[% vii.circulate | replace('"', '""') %]","[% vii.deposit | replace('"', '""') %]","[% vii.deposit_amount | replace('"', '""') %]","[% vii.ref | replace('"', '""') %]","[% vii.holdable | replace('"', '""') %]","[% vii.price | replace('"', '""') %]","[% vii.barcode | replace('"', '""') %]","[% vii.circ_modifier | replace('"', '""') %]","[% vii.circ_as_type | replace('"', '""') %]","[% vii.alert_message | replace('"', '""') %]","[% vii.pub_note | replace('"', '""') %]","[% vii.priv_note | replace('"', '""') %]","[% vii.opac_visible | replace('"', '""') %]"
+[% END %]
+$$
+    )
+;
+
+INSERT INTO action_trigger.environment ( event_def, path) VALUES (
+    46, 'record')
+    ,( 46, 'record.attributes')
+    ,( 46, 'record.queue')
+    ,( 46, 'record.queue.owner')
+;
+
+INSERT INTO action_trigger.event_definition (
+        id,
+        active,
+        owner,
+        name,
+        hook,
+        validator,
+        reactor,
+        group_field,
+        granularity,
+        template
+    ) VALUES (
+        47,
+        TRUE,
+        1,
+        'Email Output for Import Items from Queued Bib Records',
+        'vandelay.import_items.email',
+        'NOOP_True',
+        'SendEmail',
+        'record.queue.owner',
+        NULL,
+$$
+[%- USE date -%]
+[%- SET user = target.0.record.queue.owner -%]
+To: [%- params.recipient_email || user.email || 'root@localhost' %]
+From: [%- params.sender_email || default_sender %]
+Subject: Import Items from Import Queue
+
+Queue ID: [% target.0.record.queue.id %]
+Queue Name: [% target.0.record.queue.name %]
+Queue Type: [% target.0.record.queue.queue_type %]
+Complete? [% target.0.record.queue.complete %]
+
+    [% FOR vii IN target %]
+=-=-=
+ Import Item ID         | [% vii.id %]
+ Title of work          | [% helpers.get_queued_bib_attr('title',vii.record.attributes) %]
+ ISBN                   | [% helpers.get_queued_bib_attr('isbn',vii.record.attributes) %]
+ Attribute Definition   | [% vii.definition %]
+ Import Error           | [% vii.import_error %]
+ Import Error Detail    | [% vii.error_detail %]
+ Owning Library         | [% vii.owning_lib %]
+ Circulating Library    | [% vii.circ_lib %]
+ Call Number            | [% vii.call_number %]
+ Copy Number            | [% vii.copy_number %]
+ Status                 | [% vii.status.name %]
+ Shelving Location      | [% vii.location.name %]
+ Circulate              | [% vii.circulate %]
+ Deposit                | [% vii.deposit %]
+ Deposit Amount         | [% vii.deposit_amount %]
+ Reference              | [% vii.ref %]
+ Holdable               | [% vii.holdable %]
+ Price                  | [% vii.price %]
+ Barcode                | [% vii.barcode %]
+ Circulation Modifier   | [% vii.circ_modifier %]
+ Circulate As MARC Type | [% vii.circ_as_type %]
+ Alert Message          | [% vii.alert_message %]
+ Public Note            | [% vii.pub_note %]
+ Private Note           | [% vii.priv_note %]
+ OPAC Visible           | [% vii.opac_visible %]
+
+    [% END %]
+$$
+    )
+;
+
+INSERT INTO action_trigger.environment ( event_def, path) VALUES (
+    47, 'record')
+    ,( 47, 'record.attributes')
+    ,( 47, 'record.queue')
+    ,( 47, 'record.queue.owner')
+;
+
+
+COMMIT;
diff --git a/Open-ILS/src/sql/Pg/upgrade/XXXX.vandelay-record-matching-and-quality.sql b/Open-ILS/src/sql/Pg/upgrade/XXXX.vandelay-record-matching-and-quality.sql
deleted file mode 100644 (file)
index 34a0b90..0000000
+++ /dev/null
@@ -1,1596 +0,0 @@
--- Evergreen DB patch XXXX.vandelay-record-matching-and-quality.sql
---
--- FIXME: insert description of change, if needed
---
-BEGIN;
-
-
--- check whether patch can be applied
---SELECT evergreen.upgrade_deps_block_check('XXXX', :eg_version);
-
-CREATE OR REPLACE FUNCTION evergreen.array_remove_item_by_value(inp ANYARRAY, el ANYELEMENT) RETURNS anyarray AS $$ SELECT ARRAY_ACCUM(x.e) FROM UNNEST( $1 ) x(e) WHERE x.e <> $2; $$ LANGUAGE SQL;
-
-CREATE TABLE vandelay.match_set (
-    id      SERIAL  PRIMARY KEY,
-    name    TEXT        NOT NULL,
-    owner   INT     NOT NULL REFERENCES actor.org_unit (id) ON DELETE CASCADE,
-    mtype   TEXT        NOT NULL DEFAULT 'biblio', -- 'biblio','authority','mfhd'?, others?
-    CONSTRAINT name_once_per_owner_mtype UNIQUE (name, owner, mtype)
-);
-
--- Table to define match points, either FF via SVF or tag+subfield
-CREATE TABLE vandelay.match_set_point (
-    id          SERIAL  PRIMARY KEY,
-    match_set   INT     REFERENCES vandelay.match_set (id) ON DELETE CASCADE,
-    parent      INT     REFERENCES vandelay.match_set_point (id),
-    bool_op     TEXT    CHECK (bool_op IS NULL OR (bool_op IN ('AND','OR','NOT'))),
-    svf         TEXT    REFERENCES config.record_attr_definition (name),
-    tag         TEXT,
-    subfield    TEXT,
-    negate      BOOL    DEFAULT FALSE,
-    quality     INT     NOT NULL DEFAULT 1, -- higher is better
-    CONSTRAINT vmsp_need_a_subfield_with_a_tag CHECK ((tag IS NOT NULL AND subfield IS NOT NULL) OR tag IS NULL),
-    CONSTRAINT vmsp_need_a_tag_or_a_ff_or_a_bo CHECK (
-        (tag IS NOT NULL AND svf IS NULL AND bool_op IS NULL) OR
-        (tag IS NULL AND svf IS NOT NULL AND bool_op IS NULL) OR
-        (tag IS NULL AND svf IS NULL AND bool_op IS NOT NULL)
-    )
-);
-
-CREATE TABLE vandelay.match_set_quality (
-    id          SERIAL  PRIMARY KEY,
-    match_set   INT     NOT NULL REFERENCES vandelay.match_set (id) ON DELETE CASCADE,
-    svf         TEXT    REFERENCES config.record_attr_definition,
-    tag         TEXT,
-    subfield    TEXT,
-    value       TEXT    NOT NULL,
-    quality     INT     NOT NULL DEFAULT 1, -- higher is better
-    CONSTRAINT vmsq_need_a_subfield_with_a_tag CHECK ((tag IS NOT NULL AND subfield IS NOT NULL) OR tag IS NULL),
-    CONSTRAINT vmsq_need_a_tag_or_a_ff CHECK ((tag IS NOT NULL AND svf IS NULL) OR (tag IS NULL AND svf IS NOT NULL))
-);
-CREATE UNIQUE INDEX vmsq_def_once_per_set ON vandelay.match_set_quality (match_set, COALESCE(tag,''), COALESCE(subfield,''), COALESCE(svf,''), value);
-
-
--- ALTER TABLEs...
-ALTER TABLE vandelay.queue ADD COLUMN match_set INT REFERENCES vandelay.match_set (id) ON UPDATE CASCADE ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED;
-ALTER TABLE vandelay.queued_record ADD COLUMN quality INT NOT NULL DEFAULT 0;
-ALTER TABLE vandelay.bib_attr_definition DROP COLUMN ident;
-
-CREATE TABLE vandelay.import_error (
-    code        TEXT    PRIMARY KEY,
-    description TEXT    NOT NULL -- i18n
-);
-
-ALTER TABLE vandelay.queued_bib_record
-    ADD COLUMN import_error TEXT REFERENCES vandelay.import_error (code) ON DELETE SET NULL ON UPDATE CASCADE DEFERRABLE INITIALLY DEFERRED,
-    ADD COLUMN error_detail TEXT;
-
-ALTER TABLE vandelay.bib_match
-    DROP COLUMN field_type,
-    DROP COLUMN matched_attr,
-    ADD COLUMN quality INT NOT NULL DEFAULT 1,
-    ADD COLUMN match_score INT NOT NULL DEFAULT 0;
-
-ALTER TABLE vandelay.import_item
-    ADD COLUMN import_error TEXT REFERENCES vandelay.import_error (code) ON DELETE SET NULL ON UPDATE CASCADE DEFERRABLE INITIALLY DEFERRED,
-    ADD COLUMN error_detail TEXT,
-    ADD COLUMN imported_as BIGINT REFERENCES asset.copy (id) DEFERRABLE INITIALLY DEFERRED,
-    ADD COLUMN import_time TIMESTAMP WITH TIME ZONE;
-
-ALTER TABLE vandelay.merge_profile ADD COLUMN lwm_ratio NUMERIC;
-
-CREATE OR REPLACE FUNCTION vandelay.marc21_record_type( marc TEXT ) RETURNS config.marc21_rec_type_map AS $func$
-DECLARE
-    ldr         TEXT;
-    tval        TEXT;
-    tval_rec    RECORD;
-    bval        TEXT;
-    bval_rec    RECORD;
-    retval      config.marc21_rec_type_map%ROWTYPE;
-BEGIN
-    ldr := oils_xpath_string( '//*[local-name()="leader"]', marc );
-
-    IF ldr IS NULL OR ldr = '' THEN
-        SELECT * INTO retval FROM config.marc21_rec_type_map WHERE code = 'BKS';
-        RETURN retval;
-    END IF;
-
-    SELECT * INTO tval_rec FROM config.marc21_ff_pos_map WHERE fixed_field = 'Type' LIMIT 1; -- They're all the same
-    SELECT * INTO bval_rec FROM config.marc21_ff_pos_map WHERE fixed_field = 'BLvl' LIMIT 1; -- They're all the same
-
-
-    tval := SUBSTRING( ldr, tval_rec.start_pos + 1, tval_rec.length );
-    bval := SUBSTRING( ldr, bval_rec.start_pos + 1, bval_rec.length );
-
-    -- RAISE NOTICE 'type %, blvl %, ldr %', tval, bval, ldr;
-
-    SELECT * INTO retval FROM config.marc21_rec_type_map WHERE type_val LIKE '%' || tval || '%' AND blvl_val LIKE '%' || bval || '%';
-
-
-    IF retval.code IS NULL THEN
-        SELECT * INTO retval FROM config.marc21_rec_type_map WHERE code = 'BKS';
-    END IF;
-
-    RETURN retval;
-END;
-$func$ LANGUAGE PLPGSQL;
-
-CREATE OR REPLACE FUNCTION vandelay.marc21_extract_fixed_field( marc TEXT, ff TEXT ) RETURNS TEXT AS $func$
-DECLARE
-    rtype       TEXT;
-    ff_pos      RECORD;
-    tag_data    RECORD;
-    val         TEXT;
-BEGIN
-    rtype := (vandelay.marc21_record_type( marc )).code;
-    FOR ff_pos IN SELECT * FROM config.marc21_ff_pos_map WHERE fixed_field = ff AND rec_type = rtype ORDER BY tag DESC LOOP
-        IF ff_pos.tag = 'ldr' THEN
-            val := oils_xpath_string('//*[local-name()="leader"]', marc);
-            IF val IS NOT NULL THEN
-                val := SUBSTRING( val, ff_pos.start_pos + 1, ff_pos.length );
-                RETURN val;
-            END IF;
-        ELSE
-            FOR tag_data IN SELECT value FROM UNNEST( oils_xpath( '//*[@tag="' || UPPER(ff_pos.tag) || '"]/text()', marc ) ) x(value) LOOP
-                val := SUBSTRING( tag_data.value, ff_pos.start_pos + 1, ff_pos.length );
-                RETURN val;
-            END LOOP;
-        END IF;
-        val := REPEAT( ff_pos.default_val, ff_pos.length );
-        RETURN val;
-    END LOOP;
-
-    RETURN NULL;
-END;
-$func$ LANGUAGE PLPGSQL;
-
-CREATE OR REPLACE FUNCTION vandelay.marc21_extract_all_fixed_fields( marc TEXT ) RETURNS SETOF biblio.record_ff_map AS $func$
-DECLARE
-    tag_data    TEXT;
-    rtype       TEXT;
-    ff_pos      RECORD;
-    output      biblio.record_ff_map%ROWTYPE;
-BEGIN
-    rtype := (vandelay.marc21_record_type( marc )).code;
-
-    FOR ff_pos IN SELECT * FROM config.marc21_ff_pos_map WHERE rec_type = rtype ORDER BY tag DESC LOOP
-        output.ff_name  := ff_pos.fixed_field;
-        output.ff_value := NULL;
-
-        IF ff_pos.tag = 'ldr' THEN
-            output.ff_value := oils_xpath_string('//*[local-name()="leader"]', marc);
-            IF output.ff_value IS NOT NULL THEN
-                output.ff_value := SUBSTRING( output.ff_value, ff_pos.start_pos + 1, ff_pos.length );
-                RETURN NEXT output;
-                output.ff_value := NULL;
-            END IF;
-        ELSE
-            FOR tag_data IN SELECT value FROM UNNEST( oils_xpath( '//*[@tag="' || UPPER(ff_pos.tag) || '"]/text()', marc ) ) x(value) LOOP
-                output.ff_value := SUBSTRING( tag_data, ff_pos.start_pos + 1, ff_pos.length );
-                IF output.ff_value IS NULL THEN output.ff_value := REPEAT( ff_pos.default_val, ff_pos.length ); END IF;
-                RETURN NEXT output;
-                output.ff_value := NULL;
-            END LOOP;
-        END IF;
-    
-    END LOOP;
-
-    RETURN;
-END;
-$func$ LANGUAGE PLPGSQL;
-
-CREATE OR REPLACE FUNCTION vandelay.marc21_physical_characteristics( marc TEXT) RETURNS SETOF biblio.marc21_physical_characteristics AS $func$
-DECLARE
-    rowid   INT := 0;
-    _007    TEXT;
-    ptype   config.marc21_physical_characteristic_type_map%ROWTYPE;
-    psf     config.marc21_physical_characteristic_subfield_map%ROWTYPE;
-    pval    config.marc21_physical_characteristic_value_map%ROWTYPE;
-    retval  biblio.marc21_physical_characteristics%ROWTYPE;
-BEGIN
-
-    _007 := oils_xpath_string( '//*[@tag="007"]', marc );
-
-    IF _007 IS NOT NULL AND _007 <> '' THEN
-        SELECT * INTO ptype FROM config.marc21_physical_characteristic_type_map WHERE ptype_key = SUBSTRING( _007, 1, 1 );
-
-        IF ptype.ptype_key IS NOT NULL THEN
-            FOR psf IN SELECT * FROM config.marc21_physical_characteristic_subfield_map WHERE ptype_key = ptype.ptype_key LOOP
-                SELECT * INTO pval FROM config.marc21_physical_characteristic_value_map WHERE ptype_subfield = psf.id AND value = SUBSTRING( _007, psf.start_pos + 1, psf.length );
-
-                IF pval.id IS NOT NULL THEN
-                    rowid := rowid + 1;
-                    retval.id := rowid;
-                    retval.ptype := ptype.ptype_key;
-                    retval.subfield := psf.id;
-                    retval.value := pval.id;
-                    RETURN NEXT retval;
-                END IF;
-
-            END LOOP;
-        END IF;
-    END IF;
-
-    RETURN;
-END;
-$func$ LANGUAGE PLPGSQL;
-
-CREATE TYPE vandelay.flat_marc AS ( tag CHAR(3), ind1 TEXT, ind2 TEXT, subfield TEXT, value TEXT );
-CREATE OR REPLACE FUNCTION vandelay.flay_marc ( TEXT ) RETURNS SETOF vandelay.flat_marc AS $func$
-
-use MARC::Record;
-use MARC::File::XML (BinaryEncoding => 'UTF-8');
-use MARC::Charset;
-use strict;
-
-MARC::Charset->assume_unicode(1);
-
-my $xml = shift;
-my $r = MARC::Record->new_from_xml( $xml );
-
-return_next( { tag => 'LDR', value => $r->leader } );
-
-for my $f ( $r->fields ) {
-    if ($f->is_control_field) {
-        return_next({ tag => $f->tag, value => $f->data });
-    } else {
-        for my $s ($f->subfields) {
-            return_next({
-                tag      => $f->tag,
-                ind1     => $f->indicator(1),
-                ind2     => $f->indicator(2),
-                subfield => $s->[0],
-                value    => $s->[1]
-            });
-
-            if ( $f->tag eq '245' and $s->[0] eq 'a' ) {
-                my $trim = $f->indicator(2) || 0;
-                return_next({
-                    tag      => 'tnf',
-                    ind1     => $f->indicator(1),
-                    ind2     => $f->indicator(2),
-                    subfield => 'a',
-                    value    => substr( $s->[1], $trim )
-                });
-            }
-        }
-    }
-}
-
-return undef;
-
-$func$ LANGUAGE PLPERLU;
-
-CREATE OR REPLACE FUNCTION vandelay.flatten_marc ( marc TEXT ) RETURNS SETOF vandelay.flat_marc AS $func$
-DECLARE
-    output  vandelay.flat_marc%ROWTYPE;
-    field   RECORD;
-BEGIN
-    FOR field IN SELECT * FROM vandelay.flay_marc( marc ) LOOP
-        output.ind1 := field.ind1;
-        output.ind2 := field.ind2;
-        output.tag := field.tag;
-        output.subfield := field.subfield;
-        IF field.subfield IS NOT NULL AND field.tag NOT IN ('020','022','024') THEN -- exclude standard numbers and control fields
-            output.value := naco_normalize(field.value, field.subfield);
-        ELSE
-            output.value := field.value;
-        END IF;
-
-        CONTINUE WHEN output.value IS NULL;
-
-        RETURN NEXT output;
-    END LOOP;
-END;
-$func$ LANGUAGE PLPGSQL;
-
-CREATE OR REPLACE FUNCTION vandelay.extract_rec_attrs ( xml TEXT, attr_defs TEXT[]) RETURNS hstore AS $_$
-DECLARE
-    transformed_xml TEXT;
-    prev_xfrm       TEXT;
-    normalizer      RECORD;
-    xfrm            config.xml_transform%ROWTYPE;
-    attr_value      TEXT;
-    new_attrs       HSTORE := ''::HSTORE;
-    attr_def        config.record_attr_definition%ROWTYPE;
-BEGIN
-
-    FOR attr_def IN SELECT * FROM config.record_attr_definition WHERE name IN (SELECT * FROM UNNEST(attr_defs)) ORDER BY format LOOP
-
-        IF attr_def.tag IS NOT NULL THEN -- tag (and optional subfield list) selection
-            SELECT  ARRAY_TO_STRING(ARRAY_ACCUM(x.value), COALESCE(attr_def.joiner,' ')) INTO attr_value
-              FROM  vandelay.flatten_marc(xml) AS x
-              WHERE x.tag LIKE attr_def.tag
-                    AND CASE
-                        WHEN attr_def.sf_list IS NOT NULL
-                            THEN POSITION(x.subfield IN attr_def.sf_list) > 0
-                        ELSE TRUE
-                        END
-              GROUP BY x.tag
-              ORDER BY x.tag
-              LIMIT 1;
-
-        ELSIF attr_def.fixed_field IS NOT NULL THEN -- a named fixed field, see config.marc21_ff_pos_map.fixed_field
-            attr_value := vandelay.marc21_extract_fixed_field(xml, attr_def.fixed_field);
-
-        ELSIF attr_def.xpath IS NOT NULL THEN -- and xpath expression
-
-            SELECT INTO xfrm * FROM config.xml_transform WHERE name = attr_def.format;
-
-            -- See if we can skip the XSLT ... it's expensive
-            IF prev_xfrm IS NULL OR prev_xfrm <> xfrm.name THEN
-                -- Can't skip the transform
-                IF xfrm.xslt <> '---' THEN
-                    transformed_xml := oils_xslt_process(xml,xfrm.xslt);
-                ELSE
-                    transformed_xml := xml;
-                END IF;
-
-                prev_xfrm := xfrm.name;
-            END IF;
-
-            IF xfrm.name IS NULL THEN
-                -- just grab the marcxml (empty) transform
-                SELECT INTO xfrm * FROM config.xml_transform WHERE xslt = '---' LIMIT 1;
-                prev_xfrm := xfrm.name;
-            END IF;
-
-            attr_value := oils_xpath_string(attr_def.xpath, transformed_xml, COALESCE(attr_def.joiner,' '), ARRAY[ARRAY[xfrm.prefix, xfrm.namespace_uri]]);
-
-        ELSIF attr_def.phys_char_sf IS NOT NULL THEN -- a named Physical Characteristic, see config.marc21_physical_characteristic_*_map
-            SELECT  m.value::TEXT INTO attr_value
-              FROM  vandelay.marc21_physical_characteristics(xml) v
-                    JOIN config.marc21_physical_characteristic_value_map m ON (m.id = v.value)
-              WHERE v.subfield = attr_def.phys_char_sf
-              LIMIT 1; -- Just in case ...
-
-        END IF;
-
-        -- apply index normalizers to attr_value
-        FOR normalizer IN
-            SELECT  n.func AS func,
-                    n.param_count AS param_count,
-                    m.params AS params
-              FROM  config.index_normalizer n
-                    JOIN config.record_attr_index_norm_map m ON (m.norm = n.id)
-              WHERE attr = attr_def.name
-              ORDER BY m.pos LOOP
-                EXECUTE 'SELECT ' || normalizer.func || '(' ||
-                    quote_literal( attr_value ) ||
-                    CASE
-                        WHEN normalizer.param_count > 0
-                            THEN ',' || REPLACE(REPLACE(BTRIM(normalizer.params,'[]'),E'\'',E'\\\''),E'"',E'\'')
-                            ELSE ''
-                        END ||
-                    ')' INTO attr_value;
-
-        END LOOP;
-
-        -- Add the new value to the hstore
-        new_attrs := new_attrs || hstore( attr_def.name, attr_value );
-
-    END LOOP;
-
-    RETURN new_attrs;
-END;
-$_$ LANGUAGE PLPGSQL;
-
-CREATE OR REPLACE FUNCTION vandelay.extract_rec_attrs ( xml TEXT ) RETURNS hstore AS $_$
-    SELECT vandelay.extract_rec_attrs( $1, (SELECT ARRAY_ACCUM(name) FROM config.record_attr_definition));
-$_$ LANGUAGE SQL;
-
--- Everything between this comment and the beginning of the definition of
--- vandelay.match_bib_record() is strictly in service of that function.
-CREATE TYPE vandelay.match_set_test_result AS (record BIGINT, quality INTEGER);
-
-CREATE OR REPLACE FUNCTION vandelay.match_set_test_marcxml(
-    match_set_id INTEGER, record_xml TEXT
-) RETURNS SETOF vandelay.match_set_test_result AS $$
-DECLARE
-    tags_rstore HSTORE;
-    svf_rstore  HSTORE;
-    coal        TEXT;
-    joins       TEXT;
-    query_      TEXT;
-    wq          TEXT;
-    qvalue      INTEGER;
-    rec         RECORD;
-BEGIN
-    tags_rstore := vandelay.flatten_marc_hstore(record_xml);
-    svf_rstore := vandelay.extract_rec_attrs(record_xml);
-
-    CREATE TEMPORARY TABLE _vandelay_tmp_qrows (q INTEGER);
-    CREATE TEMPORARY TABLE _vandelay_tmp_jrows (j TEXT);
-
-    -- generate the where clause and return that directly (into wq), and as
-    -- a side-effect, populate the _vandelay_tmp_[qj]rows tables.
-    wq := vandelay.get_expr_from_match_set(match_set_id);
-
-    query_ := 'SELECT bre.id AS record, ';
-
-    -- qrows table is for the quality bits we add to the SELECT clause
-    SELECT ARRAY_TO_STRING(
-        ARRAY_ACCUM('COALESCE(n' || q::TEXT || '.quality, 0)'), ' + '
-    ) INTO coal FROM _vandelay_tmp_qrows;
-
-    -- our query string so far is the SELECT clause and the inital FROM.
-    -- no JOINs yet nor the WHERE clause
-    query_ := query_ || coal || ' AS quality ' || E'\n' ||
-        'FROM biblio.record_entry bre ';
-
-    -- jrows table is for the joins we must make (and the real text conditions)
-    SELECT ARRAY_TO_STRING(ARRAY_ACCUM(j), E'\n') INTO joins
-        FROM _vandelay_tmp_jrows;
-
-    -- add those joins and the where clause to our query.
-    query_ := query_ || joins || E'\n' || 'WHERE ' || wq || ' AND not bre.deleted';
-
-    -- this will return rows of record,quality
-    FOR rec IN EXECUTE query_ USING tags_rstore, svf_rstore LOOP
-        RETURN NEXT rec;
-    END LOOP;
-
-    DROP TABLE _vandelay_tmp_qrows;
-    DROP TABLE _vandelay_tmp_jrows;
-    RETURN;
-END;
-
-$$ LANGUAGE PLPGSQL;
-
-CREATE OR REPLACE FUNCTION vandelay.flatten_marc_hstore(
-    record_xml TEXT
-) RETURNS HSTORE AS $$
-BEGIN
-    RETURN (SELECT
-        HSTORE(
-            ARRAY_ACCUM(tag || (COALESCE(subfield, ''))),
-            ARRAY_ACCUM(value)
-        )
-        FROM (
-            SELECT tag, subfield, ARRAY_ACCUM(value)::TEXT AS value
-                FROM vandelay.flatten_marc(record_xml)
-                GROUP BY tag, subfield ORDER BY tag, subfield
-        ) subquery
-    );
-END;
-$$ LANGUAGE PLPGSQL;
-
-CREATE OR REPLACE FUNCTION vandelay.get_expr_from_match_set(
-    match_set_id INTEGER
-) RETURNS TEXT AS $$
-DECLARE
-    root    vandelay.match_set_point;
-BEGIN
-    SELECT * INTO root FROM vandelay.match_set_point
-        WHERE parent IS NULL AND match_set = match_set_id;
-
-    RETURN vandelay.get_expr_from_match_set_point(root);
-END;
-$$  LANGUAGE PLPGSQL;
-
-CREATE OR REPLACE FUNCTION vandelay.get_expr_from_match_set_point(
-    node vandelay.match_set_point
-) RETURNS TEXT AS $$
-DECLARE
-    q           TEXT;
-    i           INTEGER;
-    this_op     TEXT;
-    children    INTEGER[];
-    child       vandelay.match_set_point;
-BEGIN
-    SELECT ARRAY_ACCUM(id) INTO children FROM vandelay.match_set_point
-        WHERE parent = node.id;
-
-    IF ARRAY_LENGTH(children, 1) > 0 THEN
-        this_op := vandelay._get_expr_render_one(node);
-        q := '(';
-        i := 1;
-        WHILE children[i] IS NOT NULL LOOP
-            SELECT * INTO child FROM vandelay.match_set_point
-                WHERE id = children[i];
-            IF i > 1 THEN
-                q := q || ' ' || this_op || ' ';
-            END IF;
-            i := i + 1;
-            q := q || vandelay.get_expr_from_match_set_point(child);
-        END LOOP;
-        q := q || ')';
-        RETURN q;
-    ELSIF node.bool_op IS NULL THEN
-        PERFORM vandelay._get_expr_push_qrow(node);
-        PERFORM vandelay._get_expr_push_jrow(node);
-        RETURN vandelay._get_expr_render_one(node);
-    ELSE
-        RETURN '';
-    END IF;
-END;
-$$  LANGUAGE PLPGSQL;
-
-CREATE OR REPLACE FUNCTION vandelay._get_expr_push_qrow(
-    node vandelay.match_set_point
-) RETURNS VOID AS $$
-DECLARE
-BEGIN
-    INSERT INTO _vandelay_tmp_qrows (q) VALUES (node.id);
-END;
-$$ LANGUAGE PLPGSQL;
-
-CREATE OR REPLACE FUNCTION vandelay._get_expr_push_jrow(
-    node vandelay.match_set_point
-) RETURNS VOID AS $$
-DECLARE
-    jrow        TEXT;
-    my_alias    TEXT;
-    op          TEXT;
-    tagkey      TEXT;
-BEGIN
-    IF node.negate THEN
-        op := '<>';
-    ELSE
-        op := '=';
-    END IF;
-
-    IF node.tag IS NOT NULL THEN
-        tagkey := node.tag;
-        IF node.subfield IS NOT NULL THEN
-            tagkey := tagkey || node.subfield;
-        END IF;
-    END IF;
-
-    my_alias := 'n' || node.id::TEXT;
-
-    jrow := 'LEFT JOIN (SELECT *, ' || node.quality ||
-        ' AS quality FROM metabib.';
-    IF node.tag IS NOT NULL THEN
-        jrow := jrow || 'full_rec) ' || my_alias || ' ON (' ||
-            my_alias || '.record = bre.id AND ' || my_alias || '.tag = ''' ||
-            node.tag || '''';
-        IF node.subfield IS NOT NULL THEN
-            jrow := jrow || ' AND ' || my_alias || '.subfield = ''' ||
-                node.subfield || '''';
-        END IF;
-        jrow := jrow || ' AND (' || my_alias || '.value ' || op ||
-            ' ANY(($1->''' || tagkey || ''')::TEXT[])))';
-    ELSE    -- svf
-        jrow := jrow || 'record_attr) ' || my_alias || ' ON (' ||
-            my_alias || '.id = bre.id AND (' ||
-            my_alias || '.attrs->''' || node.svf ||
-            ''' ' || op || ' $2->''' || node.svf || '''))';
-    END IF;
-    INSERT INTO _vandelay_tmp_jrows (j) VALUES (jrow);
-END;
-$$ LANGUAGE PLPGSQL;
-
-CREATE OR REPLACE FUNCTION vandelay._get_expr_render_one(
-    node vandelay.match_set_point
-) RETURNS TEXT AS $$
-DECLARE
-    s           TEXT;
-BEGIN
-    IF node.bool_op IS NOT NULL THEN
-        RETURN node.bool_op;
-    ELSE
-        RETURN '(n' || node.id::TEXT || '.id IS NOT NULL)';
-    END IF;
-END;
-$$ LANGUAGE PLPGSQL;
-
-CREATE OR REPLACE FUNCTION vandelay.match_bib_record() RETURNS TRIGGER AS $func$
-DECLARE
-    incoming_existing_id    TEXT;
-    test_result             vandelay.match_set_test_result%ROWTYPE;
-    tmp_rec                 BIGINT;
-    match_set               INT;
-BEGIN
-    IF TG_OP IN ('INSERT','UPDATE') AND NEW.imported_as IS NOT NULL THEN
-        RETURN NEW;
-    END IF;
-
-    DELETE FROM vandelay.bib_match WHERE queued_record = NEW.id;
-
-    SELECT q.match_set INTO match_set FROM vandelay.bib_queue q WHERE q.id = NEW.queue;
-
-    IF match_set IS NOT NULL THEN
-        NEW.quality := vandelay.measure_record_quality( NEW.marc, match_set );
-    END IF;
-
-    -- Perfect matches on 901$c exit early with a match with high quality.
-    incoming_existing_id :=
-        oils_xpath_string('//*[@tag="901"]/*[@code="c"][1]', NEW.marc);
-
-    IF incoming_existing_id IS NOT NULL AND incoming_existing_id != '' THEN
-        SELECT id INTO tmp_rec FROM biblio.record_entry WHERE id = incoming_existing_id::bigint;
-        IF tmp_rec IS NOT NULL THEN
-            INSERT INTO vandelay.bib_match (queued_record, eg_record, match_score, quality) 
-                SELECT
-                    NEW.id, 
-                    b.id,
-                    9999,
-                    -- note: no match_set means quality==0
-                    vandelay.measure_record_quality( b.marc, match_set )
-                FROM biblio.record_entry b
-                WHERE id = incoming_existing_id::bigint;
-        END IF;
-    END IF;
-
-    IF match_set IS NULL THEN
-        RETURN NEW;
-    END IF;
-
-    FOR test_result IN SELECT * FROM
-        vandelay.match_set_test_marcxml(match_set, NEW.marc) LOOP
-
-        INSERT INTO vandelay.bib_match ( queued_record, eg_record, match_score, quality )
-            SELECT  
-                NEW.id,
-                test_result.record,
-                test_result.quality,
-                vandelay.measure_record_quality( b.marc, match_set )
-               FROM  biblio.record_entry b
-               WHERE id = test_result.record;
-
-    END LOOP;
-
-    RETURN NEW;
-END;
-$func$ LANGUAGE PLPGSQL;
-
-CREATE OR REPLACE FUNCTION vandelay.measure_record_quality ( xml TEXT, match_set_id INT ) RETURNS INT AS $_$
-DECLARE
-    out_q   INT := 0;
-    rvalue  TEXT;
-    test    vandelay.match_set_quality%ROWTYPE;
-BEGIN
-
-    FOR test IN SELECT * FROM vandelay.match_set_quality WHERE match_set = match_set_id LOOP
-        IF test.tag IS NOT NULL THEN
-            FOR rvalue IN SELECT value FROM vandelay.flatten_marc( xml ) WHERE tag = test.tag AND subfield = test.subfield LOOP
-                IF test.value = rvalue THEN
-                    out_q := out_q + test.quality;
-                END IF;
-            END LOOP;
-        ELSE
-            IF test.value = vandelay.extract_rec_attrs(xml, ARRAY[test.svf]) -> test.svf THEN
-                out_q := out_q + test.quality;
-            END IF;
-        END IF;
-    END LOOP;
-
-    RETURN out_q;
-END;
-$_$ LANGUAGE PLPGSQL;
-
-
-CREATE OR REPLACE FUNCTION vandelay.overlay_bib_record ( import_id BIGINT, eg_id BIGINT, merge_profile_id INT ) RETURNS BOOL AS $$
-DECLARE
-    merge_profile   vandelay.merge_profile%ROWTYPE;
-    dyn_profile     vandelay.compile_profile%ROWTYPE;
-    editor_string   TEXT;
-    editor_id       INT;
-    source_marc     TEXT;
-    target_marc     TEXT;
-    eg_marc         TEXT;
-    v_marc          TEXT;
-    replace_rule    TEXT;
-BEGIN
-
-    SELECT  q.marc INTO v_marc
-      FROM  vandelay.queued_record q
-            JOIN vandelay.bib_match m ON (m.queued_record = q.id AND q.id = import_id)
-      LIMIT 1;
-
-    IF v_marc IS NULL THEN
-        -- RAISE NOTICE 'no marc for vandelay or bib record';
-        RETURN FALSE;
-    END IF;
-
-    IF vandelay.template_overlay_bib_record( v_marc, eg_id, merge_profile_id) THEN
-        UPDATE  vandelay.queued_bib_record
-          SET   imported_as = eg_id,
-                import_time = NOW()
-          WHERE id = import_id;
-
-        editor_string := (oils_xpath('//*[@tag="905"]/*[@code="u"]/text()',v_marc))[1];
-
-        IF editor_string IS NOT NULL AND editor_string <> '' THEN
-            SELECT usr INTO editor_id FROM actor.card WHERE barcode = editor_string;
-
-            IF editor_id IS NULL THEN
-                SELECT id INTO editor_id FROM actor.usr WHERE usrname = editor_string;
-            END IF;
-
-            IF editor_id IS NOT NULL THEN
-                UPDATE biblio.record_entry SET editor = editor_id WHERE id = eg_id;
-            END IF;
-        END IF;
-
-        RETURN TRUE;
-    END IF;
-
-    -- RAISE NOTICE 'update of biblio.record_entry failed';
-
-    RETURN FALSE;
-
-END;
-$$ LANGUAGE PLPGSQL;
-
-
-CREATE OR REPLACE FUNCTION vandelay.auto_overlay_bib_record_with_best ( import_id BIGINT, merge_profile_id INT, lwm_ratio_value_p NUMERIC ) RETURNS BOOL AS $$
-DECLARE
-    eg_id           BIGINT;
-    lwm_ratio_value NUMERIC;
-BEGIN
-
-    lwm_ratio_value := COALESCE(lwm_ratio_value_p, 0.0);
-
-    PERFORM * FROM vandelay.queued_bib_record WHERE import_time IS NOT NULL AND id = import_id;
-
-    IF FOUND THEN
-        -- RAISE NOTICE 'already imported, cannot auto-overlay'
-        RETURN FALSE;
-    END IF;
-
-    SELECT  m.eg_record INTO eg_id
-      FROM  vandelay.bib_match m
-            JOIN vandelay.queued_bib_record qr ON (m.queued_record = qr.id)
-            JOIN vandelay.bib_queue q ON (qr.queue = q.id)
-            JOIN biblio.record_entry r ON (r.id = m.eg_record)
-      WHERE m.queued_record = import_id
-            AND qr.quality::NUMERIC / COALESCE(NULLIF(m.quality,0),1)::NUMERIC >= lwm_ratio_value
-      ORDER BY  m.match_score DESC, -- required match score
-                qr.quality::NUMERIC / COALESCE(NULLIF(m.quality,0),1)::NUMERIC DESC, -- quality tie breaker
-                m.id -- when in doubt, use the first match
-      LIMIT 1;
-
-    IF eg_id IS NULL THEN
-        -- RAISE NOTICE 'incoming record is not of high enough quality';
-        RETURN FALSE;
-    END IF;
-
-    RETURN vandelay.overlay_bib_record( import_id, eg_id, merge_profile_id );
-END;
-$$ LANGUAGE PLPGSQL;
-
-CREATE OR REPLACE FUNCTION vandelay.auto_overlay_bib_record_with_best ( import_id BIGINT, merge_profile_id INT, lwm_ratio_value_p NUMERIC ) RETURNS BOOL AS $$
-DECLARE
-    eg_id           BIGINT;
-    lwm_ratio_value NUMERIC;
-BEGIN
-
-    lwm_ratio_value := COALESCE(lwm_ratio_value_p, 0.0);
-
-    PERFORM * FROM vandelay.queued_bib_record WHERE import_time IS NOT NULL AND id = import_id;
-
-    IF FOUND THEN
-        -- RAISE NOTICE 'already imported, cannot auto-overlay'
-        RETURN FALSE;
-    END IF;
-
-    SELECT  m.eg_record INTO eg_id
-      FROM  vandelay.bib_match m
-            JOIN vandelay.queued_bib_record qr ON (m.queued_record = qr.id)
-            JOIN vandelay.bib_queue q ON (qr.queue = q.id)
-            JOIN biblio.record_entry r ON (r.id = m.eg_record)
-      WHERE m.queued_record = import_id
-            AND qr.quality::NUMERIC / COALESCE(NULLIF(m.quality,0),1)::NUMERIC >= lwm_ratio_value
-      ORDER BY  m.match_score DESC, -- required match score
-                qr.quality::NUMERIC / COALESCE(NULLIF(m.quality,0),1)::NUMERIC DESC, -- quality tie breaker
-                m.id -- when in doubt, use the first match
-      LIMIT 1;
-
-    IF eg_id IS NULL THEN
-        -- RAISE NOTICE 'incoming record is not of high enough quality';
-        RETURN FALSE;
-    END IF;
-
-    RETURN vandelay.overlay_bib_record( import_id, eg_id, merge_profile_id );
-END;
-$$ LANGUAGE PLPGSQL;
-
-
-CREATE OR REPLACE FUNCTION vandelay.auto_overlay_bib_queue_with_best ( queue_id BIGINT, merge_profile_id INT, lwm_ratio_value NUMERIC ) RETURNS SETOF BIGINT AS $$
-DECLARE
-    queued_record   vandelay.queued_bib_record%ROWTYPE;
-BEGIN
-
-    FOR queued_record IN SELECT * FROM vandelay.queued_bib_record WHERE queue = queue_id AND import_time IS NULL LOOP
-
-        IF vandelay.auto_overlay_bib_record_with_best( queued_record.id, merge_profile_id, lwm_ratio_value ) THEN
-            RETURN NEXT queued_record.id;
-        END IF;
-
-    END LOOP;
-
-    RETURN;
-    
-END;
-$$ LANGUAGE PLPGSQL;
-
-CREATE OR REPLACE FUNCTION vandelay.auto_overlay_bib_queue_with_best ( import_id BIGINT, merge_profile_id INT ) RETURNS SETOF BIGINT AS $$
-    SELECT vandelay.auto_overlay_bib_queue_with_best( $1, $2, p.lwm_ratio ) FROM vandelay.merge_profile p WHERE id = $2;
-$$ LANGUAGE SQL;
-
-CREATE OR REPLACE FUNCTION vandelay.ingest_bib_marc ( ) RETURNS TRIGGER AS $$
-DECLARE
-    value   TEXT;
-    atype   TEXT;
-    adef    RECORD;
-BEGIN
-    IF TG_OP IN ('INSERT','UPDATE') AND NEW.imported_as IS NOT NULL THEN
-        RETURN NEW;
-    END IF;
-
-    FOR adef IN SELECT * FROM vandelay.bib_attr_definition LOOP
-
-        SELECT extract_marc_field('vandelay.queued_bib_record', id, adef.xpath, adef.remove) INTO value FROM vandelay.queued_bib_record WHERE id = NEW.id;
-        IF (value IS NOT NULL AND value <> '') THEN
-            INSERT INTO vandelay.queued_bib_record_attr (record, field, attr_value) VALUES (NEW.id, adef.id, value);
-        END IF;
-
-    END LOOP;
-
-    RETURN NULL;
-END;
-$$ LANGUAGE PLPGSQL;
-
-CREATE OR REPLACE FUNCTION vandelay.ingest_bib_items ( ) RETURNS TRIGGER AS $func$
-DECLARE
-    attr_def    BIGINT;
-    item_data   vandelay.import_item%ROWTYPE;
-BEGIN
-
-    IF TG_OP IN ('INSERT','UPDATE') AND NEW.imported_as IS NOT NULL THEN
-        RETURN NEW;
-    END IF;
-
-    SELECT item_attr_def INTO attr_def FROM vandelay.bib_queue WHERE id = NEW.queue;
-
-    FOR item_data IN SELECT * FROM vandelay.ingest_items( NEW.id::BIGINT, attr_def ) LOOP
-        INSERT INTO vandelay.import_item (
-            record,
-            definition,
-            owning_lib,
-            circ_lib,
-            call_number,
-            copy_number,
-            status,
-            location,
-            circulate,
-            deposit,
-            deposit_amount,
-            ref,
-            holdable,
-            price,
-            barcode,
-            circ_modifier,
-            circ_as_type,
-            alert_message,
-            pub_note,
-            priv_note,
-            opac_visible
-        ) VALUES (
-            NEW.id,
-            item_data.definition,
-            item_data.owning_lib,
-            item_data.circ_lib,
-            item_data.call_number,
-            item_data.copy_number,
-            item_data.status,
-            item_data.location,
-            item_data.circulate,
-            item_data.deposit,
-            item_data.deposit_amount,
-            item_data.ref,
-            item_data.holdable,
-            item_data.price,
-            item_data.barcode,
-            item_data.circ_modifier,
-            item_data.circ_as_type,
-            item_data.alert_message,
-            item_data.pub_note,
-            item_data.priv_note,
-            item_data.opac_visible
-        );
-    END LOOP;
-
-    RETURN NULL;
-END;
-$func$ LANGUAGE PLPGSQL;
-
-CREATE OR REPLACE FUNCTION vandelay.cleanup_bib_marc ( ) RETURNS TRIGGER AS $$
-BEGIN
-    IF TG_OP IN ('INSERT','UPDATE') AND NEW.imported_as IS NOT NULL THEN
-        RETURN NEW;
-    END IF;
-
-    DELETE FROM vandelay.queued_bib_record_attr WHERE record = OLD.id;
-    DELETE FROM vandelay.import_item WHERE record = OLD.id;
-
-    IF TG_OP = 'UPDATE' THEN
-        RETURN NEW;
-    END IF;
-    RETURN OLD;
-END;
-$$ LANGUAGE PLPGSQL;
-
--- ALTER TABLEs...
-
-DROP TRIGGER zz_match_bibs_trigger ON vandelay.queued_bib_record;
-CREATE TRIGGER zz_match_bibs_trigger
-    BEFORE INSERT OR UPDATE ON vandelay.queued_bib_record
-    FOR EACH ROW EXECUTE PROCEDURE vandelay.match_bib_record();
-
-CREATE OR REPLACE FUNCTION vandelay.ingest_authority_marc ( ) RETURNS TRIGGER AS $$
-DECLARE
-    value   TEXT;
-    atype   TEXT;
-    adef    RECORD;
-BEGIN
-    IF TG_OP IN ('INSERT','UPDATE') AND NEW.imported_as IS NOT NULL THEN
-        RETURN NEW;
-    END IF;
-
-    FOR adef IN SELECT * FROM vandelay.authority_attr_definition LOOP
-
-        SELECT extract_marc_field('vandelay.queued_authority_record', id, adef.xpath, adef.remove) INTO value FROM vandelay.queued_authority_record WHERE id = NEW.id;
-        IF (value IS NOT NULL AND value <> '') THEN
-            INSERT INTO vandelay.queued_authority_record_attr (record, field, attr_value) VALUES (NEW.id, adef.id, value);
-        END IF;
-
-    END LOOP;
-
-    RETURN NULL;
-END;
-$$ LANGUAGE PLPGSQL;
-
-ALTER TABLE vandelay.authority_attr_definition DROP COLUMN ident;
-ALTER TABLE vandelay.queued_authority_record
-    ADD COLUMN import_error TEXT REFERENCES vandelay.import_error (code) ON DELETE SET NULL ON UPDATE CASCADE DEFERRABLE INITIALLY DEFERRED,
-    ADD COLUMN error_detail TEXT;
-
-ALTER TABLE vandelay.authority_match DROP COLUMN matched_attr;
-
-CREATE OR REPLACE FUNCTION vandelay.cleanup_authority_marc ( ) RETURNS TRIGGER AS $$
-BEGIN
-    IF TG_OP IN ('INSERT','UPDATE') AND NEW.imported_as IS NOT NULL THEN
-        RETURN NEW;
-    END IF;
-
-    DELETE FROM vandelay.queued_authority_record_attr WHERE record = OLD.id;
-    IF TG_OP = 'UPDATE' THEN
-        RETURN NEW;
-    END IF;
-    RETURN OLD;
-END;
-$$ LANGUAGE PLPGSQL;
-
-CREATE OR REPLACE FUNCTION authority.flatten_marc ( rid BIGINT ) RETURNS SETOF authority.full_rec AS $func$
-DECLARE
-       auth    authority.record_entry%ROWTYPE;
-       output  authority.full_rec%ROWTYPE;
-       field   RECORD;
-BEGIN
-       SELECT INTO auth * FROM authority.record_entry WHERE id = rid;
-
-       FOR field IN SELECT * FROM vandelay.flatten_marc( auth.marc ) LOOP
-               output.record := rid;
-               output.ind1 := field.ind1;
-               output.ind2 := field.ind2;
-               output.tag := field.tag;
-               output.subfield := field.subfield;
-               output.value := field.value;
-
-               RETURN NEXT output;
-       END LOOP;
-END;
-$func$ LANGUAGE PLPGSQL;
-
-CREATE OR REPLACE FUNCTION biblio.flatten_marc ( rid BIGINT ) RETURNS SETOF metabib.full_rec AS $func$
-DECLARE
-       bib     biblio.record_entry%ROWTYPE;
-       output  metabib.full_rec%ROWTYPE;
-       field   RECORD;
-BEGIN
-       SELECT INTO bib * FROM biblio.record_entry WHERE id = rid;
-
-       FOR field IN SELECT * FROM vandelay.flatten_marc( bib.marc ) LOOP
-               output.record := rid;
-               output.ind1 := field.ind1;
-               output.ind2 := field.ind2;
-               output.tag := field.tag;
-               output.subfield := field.subfield;
-               output.value := field.value;
-
-               RETURN NEXT output;
-       END LOOP;
-END;
-$func$ LANGUAGE PLPGSQL;
-
------------------------------------------------
--- Seed data for import errors
------------------------------------------------
-
-INSERT INTO vandelay.import_error ( code, description ) VALUES ( 'general.unknown', oils_i18n_gettext('general.unknown', 'Import or Overlay failed', 'vie', 'description') );
-INSERT INTO vandelay.import_error ( code, description ) VALUES ( 'import.item.duplicate.barcode', oils_i18n_gettext('import.item.duplicate.barcode', 'Import failed due to barcode collision', 'vie', 'description') );
-INSERT INTO vandelay.import_error ( code, description ) VALUES ( 'import.item.invalid.circ_modifier', oils_i18n_gettext('import.item.invalid.circ_modifier', 'Import failed due to invalid circulation modifier', 'vie', 'description') );
-INSERT INTO vandelay.import_error ( code, description ) VALUES ( 'import.item.invalid.location', oils_i18n_gettext('import.item.invalid.location', 'Import failed due to invalid copy location', 'vie', 'description') );
-INSERT INTO vandelay.import_error ( code, description ) VALUES ( 'import.duplicate.sysid', oils_i18n_gettext('import.duplicate.sysid', 'Import failed due to system id collision', 'vie', 'description') );
-INSERT INTO vandelay.import_error ( code, description ) VALUES ( 'import.duplicate.tcn', oils_i18n_gettext('import.duplicate.sysid', 'Import failed due to system id collision', 'vie', 'description') );
-INSERT INTO vandelay.import_error ( code, description ) VALUES ( 'overlay.missing.sysid', oils_i18n_gettext('overlay.missing.sysid', 'Overlay failed due to missing system id', 'vie', 'description') );
-INSERT INTO vandelay.import_error ( code, description ) VALUES ( 'import.auth.duplicate.acn', oils_i18n_gettext('import.auth.duplicate.acn', 'Import failed due to Accession Number collision', 'vie', 'description') );
-INSERT INTO vandelay.import_error ( code, description ) VALUES ( 'import.xml.malformed', oils_i18n_gettext('import.xml.malformed', 'Malformed record cause Import failure', 'vie', 'description') );
-INSERT INTO vandelay.import_error ( code, description ) VALUES ( 'overlay.xml.malformed', oils_i18n_gettext('overlay.xml.malformed', 'Malformed record cause Overlay failure', 'vie', 'description') );
-INSERT INTO vandelay.import_error ( code, description ) VALUES ( 'overlay.record.quality', oils_i18n_gettext('overlay.record.quality', 'New record had insufficient quality', 'vie', 'description') );
-
-
-----------------------------------------------------------------
--- Seed data for queued record/item exports
-----------------------------------------------------------------
-
-INSERT INTO action_trigger.hook (key,core_type,description,passive) VALUES (
-        'vandelay.queued_bib_record.print',
-        'vqbr', 
-        oils_i18n_gettext(
-            'vandelay.queued_bib_record.print',
-            'Print output has been requested for records in an Importer Bib Queue.',
-            'ath',
-            'description'
-        ), 
-        FALSE
-    )
-    ,(
-        'vandelay.queued_bib_record.csv',
-        'vqbr', 
-        oils_i18n_gettext(
-            'vandelay.queued_bib_record.csv',
-            'CSV output has been requested for records in an Importer Bib Queue.',
-            'ath',
-            'description'
-        ), 
-        FALSE
-    )
-    ,(
-        'vandelay.queued_bib_record.email',
-        'vqbr', 
-        oils_i18n_gettext(
-            'vandelay.queued_bib_record.email',
-            'An email has been requested for records in an Importer Bib Queue.',
-            'ath',
-            'description'
-        ), 
-        FALSE
-    )
-    ,(
-        'vandelay.queued_auth_record.print',
-        'vqar', 
-        oils_i18n_gettext(
-            'vandelay.queued_auth_record.print',
-            'Print output has been requested for records in an Importer Authority Queue.',
-            'ath',
-            'description'
-        ), 
-        FALSE
-    )
-    ,(
-        'vandelay.queued_auth_record.csv',
-        'vqar', 
-        oils_i18n_gettext(
-            'vandelay.queued_auth_record.csv',
-            'CSV output has been requested for records in an Importer Authority Queue.',
-            'ath',
-            'description'
-        ), 
-        FALSE
-    )
-    ,(
-        'vandelay.queued_auth_record.email',
-        'vqar', 
-        oils_i18n_gettext(
-            'vandelay.queued_auth_record.email',
-            'An email has been requested for records in an Importer Authority Queue.',
-            'ath',
-            'description'
-        ), 
-        FALSE
-    )
-    ,(
-        'vandelay.import_items.print',
-        'vii', 
-        oils_i18n_gettext(
-            'vandelay.import_items.print',
-            'Print output has been requested for Import Items from records in an Importer Bib Queue.',
-            'ath',
-            'description'
-        ), 
-        FALSE
-    )
-    ,(
-        'vandelay.import_items.csv',
-        'vii', 
-        oils_i18n_gettext(
-            'vandelay.import_items.csv',
-            'CSV output has been requested for Import Items from records in an Importer Bib Queue.',
-            'ath',
-            'description'
-        ), 
-        FALSE
-    )
-    ,(
-        'vandelay.import_items.email',
-        'vii', 
-        oils_i18n_gettext(
-            'vandelay.import_items.email',
-            'An email has been requested for Import Items from records in an Importer Bib Queue.',
-            'ath',
-            'description'
-        ), 
-        FALSE
-    )
-;
-
-INSERT INTO action_trigger.event_definition (
-        id,
-        active,
-        owner,
-        name,
-        hook,
-        validator,
-        reactor,
-        group_field,
-        granularity,
-        template
-    ) VALUES (
-        39,
-        TRUE,
-        1,
-        'Print Output for Queued Bib Records',
-        'vandelay.queued_bib_record.print',
-        'NOOP_True',
-        'ProcessTemplate',
-        'queue.owner',
-        'print-on-demand',
-$$
-[%- USE date -%]
-<pre>
-Queue ID: [% target.0.queue.id %]
-Queue Name: [% target.0.queue.name %]
-Queue Type: [% target.0.queue.queue_type %]
-Complete? [% target.0.queue.complete %]
-
-    [% FOR vqbr IN target %]
-=-=-=
- Title of work    | [% helpers.get_queued_bib_attr('title',vqbr.attributes) %]
- Author of work   | [% helpers.get_queued_bib_attr('author',vqbr.attributes) %]
- Language of work | [% helpers.get_queued_bib_attr('language',vqbr.attributes) %]
- Pagination       | [% helpers.get_queued_bib_attr('pagination',vqbr.attributes) %]
- ISBN             | [% helpers.get_queued_bib_attr('isbn',vqbr.attributes) %]
- ISSN             | [% helpers.get_queued_bib_attr('issn',vqbr.attributes) %]
- Price            | [% helpers.get_queued_bib_attr('price',vqbr.attributes) %]
- Accession Number | [% helpers.get_queued_bib_attr('rec_identifier',vqbr.attributes) %]
- TCN Value        | [% helpers.get_queued_bib_attr('eg_tcn',vqbr.attributes) %]
- TCN Source       | [% helpers.get_queued_bib_attr('eg_tcn_source',vqbr.attributes) %]
- Internal ID      | [% helpers.get_queued_bib_attr('eg_identifier',vqbr.attributes) %]
- Publisher        | [% helpers.get_queued_bib_attr('publisher',vqbr.attributes) %]
- Publication Date | [% helpers.get_queued_bib_attr('pubdate',vqbr.attributes) %]
- Edition          | [% helpers.get_queued_bib_attr('edition',vqbr.attributes) %]
- Item Barcode     | [% helpers.get_queued_bib_attr('item_barcode',vqbr.attributes) %]
-
-    [% END %]
-</pre>
-$$
-    )
-;
-
-INSERT INTO action_trigger.environment ( event_def, path) VALUES (
-    39, 'attributes')
-    ,( 39, 'queue')
-;
-
-INSERT INTO action_trigger.event_definition (
-        id,
-        active,
-        owner,
-        name,
-        hook,
-        validator,
-        reactor,
-        group_field,
-        granularity,
-        template
-    ) VALUES (
-        40,
-        TRUE,
-        1,
-        'CSV Output for Queued Bib Records',
-        'vandelay.queued_bib_record.csv',
-        'NOOP_True',
-        'ProcessTemplate',
-        'queue.owner',
-        'print-on-demand',
-$$
-[%- USE date -%]
-"Title of work","Author of work","Language of work","Pagination","ISBN","ISSN","Price","Accession Number","TCN Value","TCN Source","Internal ID","Publisher","Publication Date","Edition","Item Barcode"
-[% FOR vqbr IN target %]"[% helpers.get_queued_bib_attr('title',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('author',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('language',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('pagination',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('isbn',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('issn',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('price',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('rec_identifier',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('eg_tcn',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('eg_tcn_source',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('eg_identifier',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('publisher',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('pubdate',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('edition',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('item_barcode',vqbr.attributes) | replace('"', '""') %]"
-[% END %]
-$$
-    )
-;
-
-INSERT INTO action_trigger.environment ( event_def, path) VALUES (
-    40, 'attributes')
-    ,( 40, 'queue')
-;
-
-INSERT INTO action_trigger.event_definition (
-        id,
-        active,
-        owner,
-        name,
-        hook,
-        validator,
-        reactor,
-        group_field,
-        granularity,
-        template
-    ) VALUES (
-        41,
-        TRUE,
-        1,
-        'Email Output for Queued Bib Records',
-        'vandelay.queued_bib_record.email',
-        'NOOP_True',
-        'SendEmail',
-        'queue.owner',
-        NULL,
-$$
-[%- USE date -%]
-[%- SET user = target.0.queue.owner -%]
-To: [%- params.recipient_email || user.email || 'root@localhost' %]
-From: [%- params.sender_email || default_sender %]
-Subject: Bibs from Import Queue
-
-Queue ID: [% target.0.queue.id %]
-Queue Name: [% target.0.queue.name %]
-Queue Type: [% target.0.queue.queue_type %]
-Complete? [% target.0.queue.complete %]
-
-    [% FOR vqbr IN target %]
-=-=-=
- Title of work    | [% helpers.get_queued_bib_attr('title',vqbr.attributes) %]
- Author of work   | [% helpers.get_queued_bib_attr('author',vqbr.attributes) %]
- Language of work | [% helpers.get_queued_bib_attr('language',vqbr.attributes) %]
- Pagination       | [% helpers.get_queued_bib_attr('pagination',vqbr.attributes) %]
- ISBN             | [% helpers.get_queued_bib_attr('isbn',vqbr.attributes) %]
- ISSN             | [% helpers.get_queued_bib_attr('issn',vqbr.attributes) %]
- Price            | [% helpers.get_queued_bib_attr('price',vqbr.attributes) %]
- Accession Number | [% helpers.get_queued_bib_attr('rec_identifier',vqbr.attributes) %]
- TCN Value        | [% helpers.get_queued_bib_attr('eg_tcn',vqbr.attributes) %]
- TCN Source       | [% helpers.get_queued_bib_attr('eg_tcn_source',vqbr.attributes) %]
- Internal ID      | [% helpers.get_queued_bib_attr('eg_identifier',vqbr.attributes) %]
- Publisher        | [% helpers.get_queued_bib_attr('publisher',vqbr.attributes) %]
- Publication Date | [% helpers.get_queued_bib_attr('pubdate',vqbr.attributes) %]
- Edition          | [% helpers.get_queued_bib_attr('edition',vqbr.attributes) %]
- Item Barcode     | [% helpers.get_queued_bib_attr('item_barcode',vqbr.attributes) %]
-
-    [% END %]
-
-$$
-    )
-;
-
-INSERT INTO action_trigger.environment ( event_def, path) VALUES (
-    41, 'attributes')
-    ,( 41, 'queue')
-    ,( 41, 'queue.owner')
-;
-
-INSERT INTO action_trigger.event_definition (
-        id,
-        active,
-        owner,
-        name,
-        hook,
-        validator,
-        reactor,
-        group_field,
-        granularity,
-        template
-    ) VALUES (
-        42,
-        TRUE,
-        1,
-        'Print Output for Queued Authority Records',
-        'vandelay.queued_auth_record.print',
-        'NOOP_True',
-        'ProcessTemplate',
-        'queue.owner',
-        'print-on-demand',
-$$
-[%- USE date -%]
-<pre>
-Queue ID: [% target.0.queue.id %]
-Queue Name: [% target.0.queue.name %]
-Queue Type: [% target.0.queue.queue_type %]
-Complete? [% target.0.queue.complete %]
-
-    [% FOR vqar IN target %]
-=-=-=
- Record Identifier | [% helpers.get_queued_auth_attr('rec_identifier',vqar.attributes) %]
-
-    [% END %]
-</pre>
-$$
-    )
-;
-
-INSERT INTO action_trigger.environment ( event_def, path) VALUES (
-    42, 'attributes')
-    ,( 42, 'queue')
-;
-
-INSERT INTO action_trigger.event_definition (
-        id,
-        active,
-        owner,
-        name,
-        hook,
-        validator,
-        reactor,
-        group_field,
-        granularity,
-        template
-    ) VALUES (
-        43,
-        TRUE,
-        1,
-        'CSV Output for Queued Authority Records',
-        'vandelay.queued_auth_record.csv',
-        'NOOP_True',
-        'ProcessTemplate',
-        'queue.owner',
-        'print-on-demand',
-$$
-[%- USE date -%]
-"Record Identifier"
-[% FOR vqar IN target %]"[% helpers.get_queued_auth_attr('rec_identifier',vqar.attributes) | replace('"', '""') %]"
-[% END %]
-$$
-    )
-;
-
-INSERT INTO action_trigger.environment ( event_def, path) VALUES (
-    43, 'attributes')
-    ,( 43, 'queue')
-;
-
-INSERT INTO action_trigger.event_definition (
-        id,
-        active,
-        owner,
-        name,
-        hook,
-        validator,
-        reactor,
-        group_field,
-        granularity,
-        template
-    ) VALUES (
-        44,
-        TRUE,
-        1,
-        'Email Output for Queued Authority Records',
-        'vandelay.queued_auth_record.email',
-        'NOOP_True',
-        'SendEmail',
-        'queue.owner',
-        NULL,
-$$
-[%- USE date -%]
-[%- SET user = target.0.queue.owner -%]
-To: [%- params.recipient_email || user.email || 'root@localhost' %]
-From: [%- params.sender_email || default_sender %]
-Subject: Authorities from Import Queue
-
-Queue ID: [% target.0.queue.id %]
-Queue Name: [% target.0.queue.name %]
-Queue Type: [% target.0.queue.queue_type %]
-Complete? [% target.0.queue.complete %]
-
-    [% FOR vqar IN target %]
-=-=-=
- Record Identifier | [% helpers.get_queued_auth_attr('rec_identifier',vqar.attributes) %]
-
-    [% END %]
-
-$$
-    )
-;
-
-INSERT INTO action_trigger.environment ( event_def, path) VALUES (
-    44, 'attributes')
-    ,( 44, 'queue')
-    ,( 44, 'queue.owner')
-;
-
-INSERT INTO action_trigger.event_definition (
-        id,
-        active,
-        owner,
-        name,
-        hook,
-        validator,
-        reactor,
-        group_field,
-        granularity,
-        template
-    ) VALUES (
-        45,
-        TRUE,
-        1,
-        'Print Output for Import Items from Queued Bib Records',
-        'vandelay.import_items.print',
-        'NOOP_True',
-        'ProcessTemplate',
-        'record.queue.owner',
-        'print-on-demand',
-$$
-[%- USE date -%]
-<pre>
-Queue ID: [% target.0.record.queue.id %]
-Queue Name: [% target.0.record.queue.name %]
-Queue Type: [% target.0.record.queue.queue_type %]
-Complete? [% target.0.record.queue.complete %]
-
-    [% FOR vii IN target %]
-=-=-=
- Import Item ID         | [% vii.id %]
- Title of work          | [% helpers.get_queued_bib_attr('title',vii.record.attributes) %]
- ISBN                   | [% helpers.get_queued_bib_attr('isbn',vii.record.attributes) %]
- Attribute Definition   | [% vii.definition %]
- Import Error           | [% vii.import_error %]
- Import Error Detail    | [% vii.error_detail %]
- Owning Library         | [% vii.owning_lib %]
- Circulating Library    | [% vii.circ_lib %]
- Call Number            | [% vii.call_number %]
- Copy Number            | [% vii.copy_number %]
- Status                 | [% vii.status.name %]
- Shelving Location      | [% vii.location.name %]
- Circulate              | [% vii.circulate %]
- Deposit                | [% vii.deposit %]
- Deposit Amount         | [% vii.deposit_amount %]
- Reference              | [% vii.ref %]
- Holdable               | [% vii.holdable %]
- Price                  | [% vii.price %]
- Barcode                | [% vii.barcode %]
- Circulation Modifier   | [% vii.circ_modifier %]
- Circulate As MARC Type | [% vii.circ_as_type %]
- Alert Message          | [% vii.alert_message %]
- Public Note            | [% vii.pub_note %]
- Private Note           | [% vii.priv_note %]
- OPAC Visible           | [% vii.opac_visible %]
-
-    [% END %]
-</pre>
-$$
-    )
-;
-
-INSERT INTO action_trigger.environment ( event_def, path) VALUES (
-    45, 'record')
-    ,( 45, 'record.attributes')
-    ,( 45, 'record.queue')
-    ,( 45, 'record.queue.owner')
-;
-
-INSERT INTO action_trigger.event_definition (
-        id,
-        active,
-        owner,
-        name,
-        hook,
-        validator,
-        reactor,
-        group_field,
-        granularity,
-        template
-    ) VALUES (
-        46,
-        TRUE,
-        1,
-        'CSV Output for Import Items from Queued Bib Records',
-        'vandelay.import_items.csv',
-        'NOOP_True',
-        'ProcessTemplate',
-        'record.queue.owner',
-        'print-on-demand',
-$$
-[%- USE date -%]
-"Import Item ID","Title of work","ISBN","Attribute Definition","Import Error","Import Error Detail","Owning Library","Circulating Library","Call Number","Copy Number","Status","Shelving Location","Circulate","Deposit","Deposit Amount","Reference","Holdable","Price","Barcode","Circulation Modifier","Circulate As MARC Type","Alert Message","Public Note","Private Note","OPAC Visible"
-[% FOR vii IN target %]"[% vii.id | replace('"', '""') %]","[% helpers.get_queued_bib_attr('title',vii.record.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('isbn',vii.record.attributes) | replace('"', '""') %]","[% vii.definition | replace('"', '""') %]","[% vii.import_error | replace('"', '""') %]","[% vii.error_detail | replace('"', '""') %]","[% vii.owning_lib | replace('"', '""') %]","[% vii.circ_lib | replace('"', '""') %]","[% vii.call_number | replace('"', '""') %]","[% vii.copy_number | replace('"', '""') %]","[% vii.status.name | replace('"', '""') %]","[% vii.location.name | replace('"', '""') %]","[% vii.circulate | replace('"', '""') %]","[% vii.deposit | replace('"', '""') %]","[% vii.deposit_amount | replace('"', '""') %]","[% vii.ref | replace('"', '""') %]","[% vii.holdable | replace('"', '""') %]","[% vii.price | replace('"', '""') %]","[% vii.barcode | replace('"', '""') %]","[% vii.circ_modifier | replace('"', '""') %]","[% vii.circ_as_type | replace('"', '""') %]","[% vii.alert_message | replace('"', '""') %]","[% vii.pub_note | replace('"', '""') %]","[% vii.priv_note | replace('"', '""') %]","[% vii.opac_visible | replace('"', '""') %]"
-[% END %]
-$$
-    )
-;
-
-INSERT INTO action_trigger.environment ( event_def, path) VALUES (
-    46, 'record')
-    ,( 46, 'record.attributes')
-    ,( 46, 'record.queue')
-    ,( 46, 'record.queue.owner')
-;
-
-INSERT INTO action_trigger.event_definition (
-        id,
-        active,
-        owner,
-        name,
-        hook,
-        validator,
-        reactor,
-        group_field,
-        granularity,
-        template
-    ) VALUES (
-        47,
-        TRUE,
-        1,
-        'Email Output for Import Items from Queued Bib Records',
-        'vandelay.import_items.email',
-        'NOOP_True',
-        'SendEmail',
-        'record.queue.owner',
-        NULL,
-$$
-[%- USE date -%]
-[%- SET user = target.0.record.queue.owner -%]
-To: [%- params.recipient_email || user.email || 'root@localhost' %]
-From: [%- params.sender_email || default_sender %]
-Subject: Import Items from Import Queue
-
-Queue ID: [% target.0.record.queue.id %]
-Queue Name: [% target.0.record.queue.name %]
-Queue Type: [% target.0.record.queue.queue_type %]
-Complete? [% target.0.record.queue.complete %]
-
-    [% FOR vii IN target %]
-=-=-=
- Import Item ID         | [% vii.id %]
- Title of work          | [% helpers.get_queued_bib_attr('title',vii.record.attributes) %]
- ISBN                   | [% helpers.get_queued_bib_attr('isbn',vii.record.attributes) %]
- Attribute Definition   | [% vii.definition %]
- Import Error           | [% vii.import_error %]
- Import Error Detail    | [% vii.error_detail %]
- Owning Library         | [% vii.owning_lib %]
- Circulating Library    | [% vii.circ_lib %]
- Call Number            | [% vii.call_number %]
- Copy Number            | [% vii.copy_number %]
- Status                 | [% vii.status.name %]
- Shelving Location      | [% vii.location.name %]
- Circulate              | [% vii.circulate %]
- Deposit                | [% vii.deposit %]
- Deposit Amount         | [% vii.deposit_amount %]
- Reference              | [% vii.ref %]
- Holdable               | [% vii.holdable %]
- Price                  | [% vii.price %]
- Barcode                | [% vii.barcode %]
- Circulation Modifier   | [% vii.circ_modifier %]
- Circulate As MARC Type | [% vii.circ_as_type %]
- Alert Message          | [% vii.alert_message %]
- Public Note            | [% vii.pub_note %]
- Private Note           | [% vii.priv_note %]
- OPAC Visible           | [% vii.opac_visible %]
-
-    [% END %]
-$$
-    )
-;
-
-INSERT INTO action_trigger.environment ( event_def, path) VALUES (
-    47, 'record')
-    ,( 47, 'record.attributes')
-    ,( 47, 'record.queue')
-    ,( 47, 'record.queue.owner')
-;
-
-
-COMMIT;