From: Ben Shum Date: Mon, 10 Mar 2014 16:55:04 +0000 (-0400) Subject: LP1284864 - stamping upgrade for deleted metarecord function fixing X-Git-Tag: sprint4-merge-nov22~2220 X-Git-Url: https://old-git.evergreen-ils.org/?a=commitdiff_plain;h=b9f8c5bcd24ccbda14210d9700d3404cb78ede16;p=working%2FEvergreen.git LP1284864 - stamping upgrade for deleted metarecord function fixing Signed-off-by: Ben Shum --- diff --git a/Open-ILS/src/sql/Pg/002.schema.config.sql b/Open-ILS/src/sql/Pg/002.schema.config.sql index dfcbbcde31..688d9b01cb 100644 --- a/Open-ILS/src/sql/Pg/002.schema.config.sql +++ b/Open-ILS/src/sql/Pg/002.schema.config.sql @@ -91,7 +91,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 ('0871', :eg_version); -- berick/miker +INSERT INTO config.upgrade_log (version, applied_to) VALUES ('0872', :eg_version); -- miker/bshum CREATE TABLE config.bib_source ( id SERIAL PRIMARY KEY, diff --git a/Open-ILS/src/sql/Pg/upgrade/0872.functions.metarecord-deleted-constituents.sql b/Open-ILS/src/sql/Pg/upgrade/0872.functions.metarecord-deleted-constituents.sql new file mode 100644 index 0000000000..b32c4b2620 --- /dev/null +++ b/Open-ILS/src/sql/Pg/upgrade/0872.functions.metarecord-deleted-constituents.sql @@ -0,0 +1,355 @@ +BEGIN; + +SELECT evergreen.upgrade_deps_block_check('0872', :eg_version); + +CREATE OR REPLACE FUNCTION metabib.remap_metarecord_for_bib( bib_id BIGINT, fp TEXT, bib_is_deleted BOOL DEFAULT FALSE, retain_deleted BOOL DEFAULT FALSE ) RETURNS BIGINT AS $func$ +DECLARE + new_mapping BOOL := TRUE; + source_count INT; + old_mr BIGINT; + tmp_mr metabib.metarecord%ROWTYPE; + deleted_mrs BIGINT[]; +BEGIN + + -- We need to make sure we're not a deleted master record of an MR + IF bib_is_deleted THEN + FOR old_mr IN SELECT id FROM metabib.metarecord WHERE master_record = bib_id LOOP + + IF NOT retain_deleted THEN -- Go away for any MR that we're master of, unless retained + DELETE FROM metabib.metarecord_source_map WHERE source = bib_id; + END IF; + + -- Now, are there any more sources on this MR? + SELECT COUNT(*) INTO source_count FROM metabib.metarecord_source_map WHERE metarecord = old_mr; + + IF source_count = 0 AND NOT retain_deleted THEN -- No other records + deleted_mrs := ARRAY_APPEND(deleted_mrs, old_mr); -- Just in case... + DELETE FROM metabib.metarecord WHERE id = old_mr; + + ELSE -- indeed there are. Update it with a null cache and recalcualated master record + UPDATE metabib.metarecord + SET mods = NULL, + master_record = ( SELECT id FROM biblio.record_entry WHERE fingerprint = fp AND NOT deleted ORDER BY quality DESC LIMIT 1) + WHERE id = old_mr; + END IF; + END LOOP; + + ELSE -- insert or update + + FOR tmp_mr IN SELECT m.* FROM metabib.metarecord m JOIN metabib.metarecord_source_map s ON (s.metarecord = m.id) WHERE s.source = bib_id LOOP + + -- Find the first fingerprint-matching + IF old_mr IS NULL AND fp = tmp_mr.fingerprint THEN + old_mr := tmp_mr.id; + new_mapping := FALSE; + + ELSE -- Our fingerprint changed ... maybe remove the old MR + DELETE FROM metabib.metarecord_source_map WHERE metarecord = old_mr AND source = bib_id; -- remove the old source mapping + SELECT COUNT(*) INTO source_count FROM metabib.metarecord_source_map WHERE metarecord = tmp_mr.id; + IF source_count = 0 THEN -- No other records + deleted_mrs := ARRAY_APPEND(deleted_mrs, tmp_mr.id); + DELETE FROM metabib.metarecord WHERE id = tmp_mr.id; + END IF; + END IF; + + END LOOP; + + -- we found no suitable, preexisting MR based on old source maps + IF old_mr IS NULL THEN + SELECT id INTO old_mr FROM metabib.metarecord WHERE fingerprint = fp; -- is there one for our current fingerprint? + + IF old_mr IS NULL THEN -- nope, create one and grab its id + INSERT INTO metabib.metarecord ( fingerprint, master_record ) VALUES ( fp, bib_id ); + SELECT id INTO old_mr FROM metabib.metarecord WHERE fingerprint = fp; + + ELSE -- indeed there is. update it with a null cache and recalcualated master record + UPDATE metabib.metarecord + SET mods = NULL, + master_record = ( SELECT id FROM biblio.record_entry WHERE fingerprint = fp AND NOT deleted ORDER BY quality DESC LIMIT 1) + WHERE id = old_mr; + END IF; + + ELSE -- there was one we already attached to, update its mods cache and master_record + UPDATE metabib.metarecord + SET mods = NULL, + master_record = ( SELECT id FROM biblio.record_entry WHERE fingerprint = fp AND NOT deleted ORDER BY quality DESC LIMIT 1) + WHERE id = old_mr; + END IF; + + IF new_mapping THEN + INSERT INTO metabib.metarecord_source_map (metarecord, source) VALUES (old_mr, bib_id); -- new source mapping + END IF; + + END IF; + + IF ARRAY_UPPER(deleted_mrs,1) > 0 THEN + UPDATE action.hold_request SET target = old_mr WHERE target IN ( SELECT unnest(deleted_mrs) ) AND hold_type = 'M'; -- if we had to delete any MRs above, make sure their holds are moved + END IF; + + RETURN old_mr; + +END; +$func$ LANGUAGE PLPGSQL; + +DROP FUNCTION metabib.remap_metarecord_for_bib( bib_id BIGINT, fp TEXT ); + +CREATE OR REPLACE FUNCTION biblio.indexing_ingest_or_delete () RETURNS TRIGGER AS $func$ +DECLARE + tmp_bool BOOL; +BEGIN + + IF NEW.deleted THEN -- If this bib is deleted + + PERFORM * FROM config.internal_flag WHERE + name = 'ingest.metarecord_mapping.preserve_on_delete' AND enabled; + + tmp_bool := FOUND; -- Just in case this is changed by some other statement + + PERFORM metabib.remap_metarecord_for_bib( NEW.id, NEW.fingerprint, TRUE, tmp_bool ); + + IF NOT tmp_bool THEN + -- One needs to keep these around to support searches + -- with the #deleted modifier, so one should turn on the named + -- internal flag for that functionality. + DELETE FROM metabib.record_attr_vector_list WHERE source = NEW.id; + END IF; + + DELETE FROM authority.bib_linking WHERE bib = NEW.id; -- Avoid updating fields in bibs that are no longer visible + DELETE FROM biblio.peer_bib_copy_map WHERE peer_record = NEW.id; -- Separate any multi-homed items + DELETE FROM metabib.browse_entry_def_map WHERE source = NEW.id; -- Don't auto-suggest deleted bibs + RETURN NEW; -- and we're done + END IF; + + IF TG_OP = 'UPDATE' THEN -- re-ingest? + PERFORM * FROM config.internal_flag WHERE name = 'ingest.reingest.force_on_same_marc' AND enabled; + + IF NOT FOUND AND OLD.marc = NEW.marc THEN -- don't do anything if the MARC didn't change + RETURN NEW; + END IF; + END IF; + + -- Record authority linking + PERFORM * FROM config.internal_flag WHERE name = 'ingest.disable_authority_linking' AND enabled; + IF NOT FOUND THEN + PERFORM biblio.map_authority_linking( NEW.id, NEW.marc ); + END IF; + + -- Flatten and insert the mfr data + PERFORM * FROM config.internal_flag WHERE name = 'ingest.disable_metabib_full_rec' AND enabled; + IF NOT FOUND THEN + PERFORM metabib.reingest_metabib_full_rec(NEW.id); + + -- Now we pull out attribute data, which is dependent on the mfr for all but XPath-based fields + PERFORM * FROM config.internal_flag WHERE name = 'ingest.disable_metabib_rec_descriptor' AND enabled; + IF NOT FOUND THEN + PERFORM metabib.reingest_record_attributes(NEW.id, NULL, NEW.marc, TG_OP = 'INSERT' OR OLD.deleted); + END IF; + END IF; + + -- Gather and insert the field entry data + PERFORM metabib.reingest_metabib_field_entries(NEW.id); + + -- Located URI magic + IF TG_OP = 'INSERT' THEN + PERFORM * FROM config.internal_flag WHERE name = 'ingest.disable_located_uri' AND enabled; + IF NOT FOUND THEN + PERFORM biblio.extract_located_uris( NEW.id, NEW.marc, NEW.editor ); + END IF; + ELSE + PERFORM * FROM config.internal_flag WHERE name = 'ingest.disable_located_uri' AND enabled; + IF NOT FOUND THEN + PERFORM biblio.extract_located_uris( NEW.id, NEW.marc, NEW.editor ); + END IF; + END IF; + + -- (re)map metarecord-bib linking + IF TG_OP = 'INSERT' THEN -- if not deleted and performing an insert, check for the flag + PERFORM * FROM config.internal_flag WHERE name = 'ingest.metarecord_mapping.skip_on_insert' AND enabled; + IF NOT FOUND THEN + PERFORM metabib.remap_metarecord_for_bib( NEW.id, NEW.fingerprint ); + END IF; + ELSE -- we're doing an update, and we're not deleted, remap + PERFORM * FROM config.internal_flag WHERE name = 'ingest.metarecord_mapping.skip_on_update' AND enabled; + IF NOT FOUND THEN + PERFORM metabib.remap_metarecord_for_bib( NEW.id, NEW.fingerprint ); + END IF; + END IF; + + RETURN NEW; +END; +$func$ LANGUAGE PLPGSQL; + +CREATE OR REPLACE FUNCTION unapi.mmr ( + obj_id BIGINT, + format TEXT, + ename TEXT, + includes TEXT[], + org TEXT, + depth INT DEFAULT NULL, + slimit HSTORE DEFAULT NULL, + soffset HSTORE DEFAULT NULL, + include_xmlns BOOL DEFAULT TRUE, + pref_lib INT DEFAULT NULL +) +RETURNS XML AS $F$ +DECLARE + mmrec metabib.metarecord%ROWTYPE; + leadrec biblio.record_entry%ROWTYPE; + subrec biblio.record_entry%ROWTYPE; + layout unapi.bre_output_layout%ROWTYPE; + xfrm config.xml_transform%ROWTYPE; + ouid INT; + xml_buf TEXT; -- growing XML document + tmp_xml TEXT; -- single-use XML string + xml_frag TEXT; -- single-use XML fragment + top_el TEXT; + output XML; + hxml XML; + axml XML; + subxml XML; -- subordinate records elements + sub_xpath TEXT; + parts TEXT[]; +BEGIN + + -- xpath for extracting bre.marc values from subordinate records + -- so they may be appended to the MARC of the master record prior + -- to XSLT processing. + -- subjects, isbn, issn, upc -- anything else? + sub_xpath := + '//*[starts-with(@tag, "6") or @tag="020" or @tag="022" or @tag="024"]'; + + IF org = '-' OR org IS NULL THEN + SELECT shortname INTO org FROM evergreen.org_top(); + END IF; + + SELECT id INTO ouid FROM actor.org_unit WHERE shortname = org; + + IF ouid IS NULL THEN + RETURN NULL::XML; + END IF; + + SELECT INTO mmrec * FROM metabib.metarecord WHERE id = obj_id; + IF NOT FOUND THEN + RETURN NULL::XML; + END IF; + + -- TODO: aggregate holdings from constituent records + IF format = 'holdings_xml' THEN -- the special case + output := unapi.mmr_holdings_xml( + obj_id, ouid, org, depth, + evergreen.array_remove_item_by_value(includes,'holdings_xml'), + slimit, soffset, include_xmlns, pref_lib); + RETURN output; + END IF; + + SELECT * INTO layout FROM unapi.bre_output_layout WHERE name = format; + + IF layout.name IS NULL THEN + RETURN NULL::XML; + END IF; + + SELECT * INTO xfrm FROM config.xml_transform WHERE name = layout.transform; + + SELECT INTO leadrec * FROM biblio.record_entry WHERE id = mmrec.master_record; + + -- Grab distinct MVF for all records if requested + IF ('mra' = ANY (includes)) THEN + axml := unapi.mmr_mra(obj_id,NULL,NULL,NULL,org,depth,NULL,NULL,TRUE,pref_lib); + ELSE + axml := NULL::XML; + END IF; + + xml_buf = leadrec.marc; + + hxml := NULL::XML; + IF ('holdings_xml' = ANY (includes)) THEN + hxml := unapi.mmr_holdings_xml( + obj_id, ouid, org, depth, + evergreen.array_remove_item_by_value(includes,'holdings_xml'), + slimit, soffset, include_xmlns, pref_lib); + END IF; + + subxml := NULL::XML; + parts := '{}'::TEXT[]; + FOR subrec IN SELECT bre.* FROM biblio.record_entry bre + JOIN metabib.metarecord_source_map mmsm ON (mmsm.source = bre.id) + JOIN metabib.metarecord mmr ON (mmr.id = mmsm.metarecord) + WHERE mmr.id = obj_id AND NOT bre.deleted + ORDER BY CASE WHEN bre.id = mmr.master_record THEN 0 ELSE bre.id END + LIMIT COALESCE((slimit->'bre')::INT, 5) LOOP + + IF subrec.id = leadrec.id THEN CONTINUE; END IF; + -- Append choice data from the the non-lead records to the + -- the lead record document + + parts := parts || xpath(sub_xpath, subrec.marc::XML)::TEXT[]; + END LOOP; + + SELECT ARRAY_TO_STRING( ARRAY_AGG( DISTINCT p ), '' )::XML INTO subxml FROM UNNEST(parts) p; + + -- append data from the subordinate records to the + -- main record document before applying the XSLT + + IF subxml IS NOT NULL THEN + xml_buf := REGEXP_REPLACE(xml_buf, + '(.*?)$', subxml || '' || E'\\1'); + END IF; + + IF format = 'marcxml' THEN + -- If we're not using the prefixed namespace in + -- this record, then remove all declarations of it + IF xml_buf !~ E' mods) + top_el := REGEXP_REPLACE(xml_buf, E'^.*?<((?:\\S+:)?' || + layout.holdings_element || ').*$', E'\\1'); + + IF axml IS NOT NULL THEN + xml_buf := REGEXP_REPLACE(xml_buf, + '(.*?)$', axml || '\\1'); + END IF; + + IF hxml IS NOT NULL THEN + xml_buf := REGEXP_REPLACE(xml_buf, + '(.*?)$', hxml || '\\1'); + END IF; + + IF ('mmr.unapi' = ANY (includes)) THEN + output := REGEXP_REPLACE( + xml_buf, + '(.*?)', + XMLELEMENT( + name abbr, + XMLATTRIBUTES( + 'http://www.w3.org/1999/xhtml' AS xmlns, + 'unapi-id' AS class, + 'tag:open-ils.org:U2@mmr/' || obj_id || '/' || org AS title + ) + )::TEXT || '\\1' + ); + ELSE + output := xml_buf; + END IF; + + -- remove ignorable whitesace + output := REGEXP_REPLACE(output::TEXT,E'>\\s+<','><','gs')::XML; + RETURN output; +END; +$F$ LANGUAGE PLPGSQL STABLE; + +-- Forcibly remap deleted master records, retaining the linkage if so configured. +SELECT metabib.remap_metarecord_for_bib( bre.id, bre.fingerprint, TRUE, COALESCE(flag.enabled,FALSE)) + FROM metabib.metarecord metar + JOIN biblio.record_entry bre ON bre.id = metar.master_record, + config.internal_flag flag + WHERE bre.deleted = TRUE AND flag.name = 'ingest.metarecord_mapping.preserve_on_delete'; + +COMMIT; + diff --git a/Open-ILS/src/sql/Pg/upgrade/XXXX.functions.metarecord-deleted-constituents.sql b/Open-ILS/src/sql/Pg/upgrade/XXXX.functions.metarecord-deleted-constituents.sql deleted file mode 100644 index ab23ff58d1..0000000000 --- a/Open-ILS/src/sql/Pg/upgrade/XXXX.functions.metarecord-deleted-constituents.sql +++ /dev/null @@ -1,355 +0,0 @@ -BEGIN; - -SELECT evergreen.upgrade_deps_block_check('XXXX', :eg_version); - -CREATE OR REPLACE FUNCTION metabib.remap_metarecord_for_bib( bib_id BIGINT, fp TEXT, bib_is_deleted BOOL DEFAULT FALSE, retain_deleted BOOL DEFAULT FALSE ) RETURNS BIGINT AS $func$ -DECLARE - new_mapping BOOL := TRUE; - source_count INT; - old_mr BIGINT; - tmp_mr metabib.metarecord%ROWTYPE; - deleted_mrs BIGINT[]; -BEGIN - - -- We need to make sure we're not a deleted master record of an MR - IF bib_is_deleted THEN - FOR old_mr IN SELECT id FROM metabib.metarecord WHERE master_record = bib_id LOOP - - IF NOT retain_deleted THEN -- Go away for any MR that we're master of, unless retained - DELETE FROM metabib.metarecord_source_map WHERE source = bib_id; - END IF; - - -- Now, are there any more sources on this MR? - SELECT COUNT(*) INTO source_count FROM metabib.metarecord_source_map WHERE metarecord = old_mr; - - IF source_count = 0 AND NOT retain_deleted THEN -- No other records - deleted_mrs := ARRAY_APPEND(deleted_mrs, old_mr); -- Just in case... - DELETE FROM metabib.metarecord WHERE id = old_mr; - - ELSE -- indeed there are. Update it with a null cache and recalcualated master record - UPDATE metabib.metarecord - SET mods = NULL, - master_record = ( SELECT id FROM biblio.record_entry WHERE fingerprint = fp AND NOT deleted ORDER BY quality DESC LIMIT 1) - WHERE id = old_mr; - END IF; - END LOOP; - - ELSE -- insert or update - - FOR tmp_mr IN SELECT m.* FROM metabib.metarecord m JOIN metabib.metarecord_source_map s ON (s.metarecord = m.id) WHERE s.source = bib_id LOOP - - -- Find the first fingerprint-matching - IF old_mr IS NULL AND fp = tmp_mr.fingerprint THEN - old_mr := tmp_mr.id; - new_mapping := FALSE; - - ELSE -- Our fingerprint changed ... maybe remove the old MR - DELETE FROM metabib.metarecord_source_map WHERE metarecord = old_mr AND source = bib_id; -- remove the old source mapping - SELECT COUNT(*) INTO source_count FROM metabib.metarecord_source_map WHERE metarecord = tmp_mr.id; - IF source_count = 0 THEN -- No other records - deleted_mrs := ARRAY_APPEND(deleted_mrs, tmp_mr.id); - DELETE FROM metabib.metarecord WHERE id = tmp_mr.id; - END IF; - END IF; - - END LOOP; - - -- we found no suitable, preexisting MR based on old source maps - IF old_mr IS NULL THEN - SELECT id INTO old_mr FROM metabib.metarecord WHERE fingerprint = fp; -- is there one for our current fingerprint? - - IF old_mr IS NULL THEN -- nope, create one and grab its id - INSERT INTO metabib.metarecord ( fingerprint, master_record ) VALUES ( fp, bib_id ); - SELECT id INTO old_mr FROM metabib.metarecord WHERE fingerprint = fp; - - ELSE -- indeed there is. update it with a null cache and recalcualated master record - UPDATE metabib.metarecord - SET mods = NULL, - master_record = ( SELECT id FROM biblio.record_entry WHERE fingerprint = fp AND NOT deleted ORDER BY quality DESC LIMIT 1) - WHERE id = old_mr; - END IF; - - ELSE -- there was one we already attached to, update its mods cache and master_record - UPDATE metabib.metarecord - SET mods = NULL, - master_record = ( SELECT id FROM biblio.record_entry WHERE fingerprint = fp AND NOT deleted ORDER BY quality DESC LIMIT 1) - WHERE id = old_mr; - END IF; - - IF new_mapping THEN - INSERT INTO metabib.metarecord_source_map (metarecord, source) VALUES (old_mr, bib_id); -- new source mapping - END IF; - - END IF; - - IF ARRAY_UPPER(deleted_mrs,1) > 0 THEN - UPDATE action.hold_request SET target = old_mr WHERE target IN ( SELECT unnest(deleted_mrs) ) AND hold_type = 'M'; -- if we had to delete any MRs above, make sure their holds are moved - END IF; - - RETURN old_mr; - -END; -$func$ LANGUAGE PLPGSQL; - -DROP FUNCTION metabib.remap_metarecord_for_bib( bib_id BIGINT, fp TEXT ); - -CREATE OR REPLACE FUNCTION biblio.indexing_ingest_or_delete () RETURNS TRIGGER AS $func$ -DECLARE - tmp_bool BOOL; -BEGIN - - IF NEW.deleted THEN -- If this bib is deleted - - PERFORM * FROM config.internal_flag WHERE - name = 'ingest.metarecord_mapping.preserve_on_delete' AND enabled; - - tmp_bool := FOUND; -- Just in case this is changed by some other statement - - PERFORM metabib.remap_metarecord_for_bib( NEW.id, NEW.fingerprint, TRUE, tmp_bool ); - - IF NOT tmp_bool THEN - -- One needs to keep these around to support searches - -- with the #deleted modifier, so one should turn on the named - -- internal flag for that functionality. - DELETE FROM metabib.record_attr_vector_list WHERE source = NEW.id; - END IF; - - DELETE FROM authority.bib_linking WHERE bib = NEW.id; -- Avoid updating fields in bibs that are no longer visible - DELETE FROM biblio.peer_bib_copy_map WHERE peer_record = NEW.id; -- Separate any multi-homed items - DELETE FROM metabib.browse_entry_def_map WHERE source = NEW.id; -- Don't auto-suggest deleted bibs - RETURN NEW; -- and we're done - END IF; - - IF TG_OP = 'UPDATE' THEN -- re-ingest? - PERFORM * FROM config.internal_flag WHERE name = 'ingest.reingest.force_on_same_marc' AND enabled; - - IF NOT FOUND AND OLD.marc = NEW.marc THEN -- don't do anything if the MARC didn't change - RETURN NEW; - END IF; - END IF; - - -- Record authority linking - PERFORM * FROM config.internal_flag WHERE name = 'ingest.disable_authority_linking' AND enabled; - IF NOT FOUND THEN - PERFORM biblio.map_authority_linking( NEW.id, NEW.marc ); - END IF; - - -- Flatten and insert the mfr data - PERFORM * FROM config.internal_flag WHERE name = 'ingest.disable_metabib_full_rec' AND enabled; - IF NOT FOUND THEN - PERFORM metabib.reingest_metabib_full_rec(NEW.id); - - -- Now we pull out attribute data, which is dependent on the mfr for all but XPath-based fields - PERFORM * FROM config.internal_flag WHERE name = 'ingest.disable_metabib_rec_descriptor' AND enabled; - IF NOT FOUND THEN - PERFORM metabib.reingest_record_attributes(NEW.id, NULL, NEW.marc, TG_OP = 'INSERT' OR OLD.deleted); - END IF; - END IF; - - -- Gather and insert the field entry data - PERFORM metabib.reingest_metabib_field_entries(NEW.id); - - -- Located URI magic - IF TG_OP = 'INSERT' THEN - PERFORM * FROM config.internal_flag WHERE name = 'ingest.disable_located_uri' AND enabled; - IF NOT FOUND THEN - PERFORM biblio.extract_located_uris( NEW.id, NEW.marc, NEW.editor ); - END IF; - ELSE - PERFORM * FROM config.internal_flag WHERE name = 'ingest.disable_located_uri' AND enabled; - IF NOT FOUND THEN - PERFORM biblio.extract_located_uris( NEW.id, NEW.marc, NEW.editor ); - END IF; - END IF; - - -- (re)map metarecord-bib linking - IF TG_OP = 'INSERT' THEN -- if not deleted and performing an insert, check for the flag - PERFORM * FROM config.internal_flag WHERE name = 'ingest.metarecord_mapping.skip_on_insert' AND enabled; - IF NOT FOUND THEN - PERFORM metabib.remap_metarecord_for_bib( NEW.id, NEW.fingerprint ); - END IF; - ELSE -- we're doing an update, and we're not deleted, remap - PERFORM * FROM config.internal_flag WHERE name = 'ingest.metarecord_mapping.skip_on_update' AND enabled; - IF NOT FOUND THEN - PERFORM metabib.remap_metarecord_for_bib( NEW.id, NEW.fingerprint ); - END IF; - END IF; - - RETURN NEW; -END; -$func$ LANGUAGE PLPGSQL; - -CREATE OR REPLACE FUNCTION unapi.mmr ( - obj_id BIGINT, - format TEXT, - ename TEXT, - includes TEXT[], - org TEXT, - depth INT DEFAULT NULL, - slimit HSTORE DEFAULT NULL, - soffset HSTORE DEFAULT NULL, - include_xmlns BOOL DEFAULT TRUE, - pref_lib INT DEFAULT NULL -) -RETURNS XML AS $F$ -DECLARE - mmrec metabib.metarecord%ROWTYPE; - leadrec biblio.record_entry%ROWTYPE; - subrec biblio.record_entry%ROWTYPE; - layout unapi.bre_output_layout%ROWTYPE; - xfrm config.xml_transform%ROWTYPE; - ouid INT; - xml_buf TEXT; -- growing XML document - tmp_xml TEXT; -- single-use XML string - xml_frag TEXT; -- single-use XML fragment - top_el TEXT; - output XML; - hxml XML; - axml XML; - subxml XML; -- subordinate records elements - sub_xpath TEXT; - parts TEXT[]; -BEGIN - - -- xpath for extracting bre.marc values from subordinate records - -- so they may be appended to the MARC of the master record prior - -- to XSLT processing. - -- subjects, isbn, issn, upc -- anything else? - sub_xpath := - '//*[starts-with(@tag, "6") or @tag="020" or @tag="022" or @tag="024"]'; - - IF org = '-' OR org IS NULL THEN - SELECT shortname INTO org FROM evergreen.org_top(); - END IF; - - SELECT id INTO ouid FROM actor.org_unit WHERE shortname = org; - - IF ouid IS NULL THEN - RETURN NULL::XML; - END IF; - - SELECT INTO mmrec * FROM metabib.metarecord WHERE id = obj_id; - IF NOT FOUND THEN - RETURN NULL::XML; - END IF; - - -- TODO: aggregate holdings from constituent records - IF format = 'holdings_xml' THEN -- the special case - output := unapi.mmr_holdings_xml( - obj_id, ouid, org, depth, - evergreen.array_remove_item_by_value(includes,'holdings_xml'), - slimit, soffset, include_xmlns, pref_lib); - RETURN output; - END IF; - - SELECT * INTO layout FROM unapi.bre_output_layout WHERE name = format; - - IF layout.name IS NULL THEN - RETURN NULL::XML; - END IF; - - SELECT * INTO xfrm FROM config.xml_transform WHERE name = layout.transform; - - SELECT INTO leadrec * FROM biblio.record_entry WHERE id = mmrec.master_record; - - -- Grab distinct MVF for all records if requested - IF ('mra' = ANY (includes)) THEN - axml := unapi.mmr_mra(obj_id,NULL,NULL,NULL,org,depth,NULL,NULL,TRUE,pref_lib); - ELSE - axml := NULL::XML; - END IF; - - xml_buf = leadrec.marc; - - hxml := NULL::XML; - IF ('holdings_xml' = ANY (includes)) THEN - hxml := unapi.mmr_holdings_xml( - obj_id, ouid, org, depth, - evergreen.array_remove_item_by_value(includes,'holdings_xml'), - slimit, soffset, include_xmlns, pref_lib); - END IF; - - subxml := NULL::XML; - parts := '{}'::TEXT[]; - FOR subrec IN SELECT bre.* FROM biblio.record_entry bre - JOIN metabib.metarecord_source_map mmsm ON (mmsm.source = bre.id) - JOIN metabib.metarecord mmr ON (mmr.id = mmsm.metarecord) - WHERE mmr.id = obj_id AND NOT bre.deleted - ORDER BY CASE WHEN bre.id = mmr.master_record THEN 0 ELSE bre.id END - LIMIT COALESCE((slimit->'bre')::INT, 5) LOOP - - IF subrec.id = leadrec.id THEN CONTINUE; END IF; - -- Append choice data from the the non-lead records to the - -- the lead record document - - parts := parts || xpath(sub_xpath, subrec.marc::XML)::TEXT[]; - END LOOP; - - SELECT ARRAY_TO_STRING( ARRAY_AGG( DISTINCT p ), '' )::XML INTO subxml FROM UNNEST(parts) p; - - -- append data from the subordinate records to the - -- main record document before applying the XSLT - - IF subxml IS NOT NULL THEN - xml_buf := REGEXP_REPLACE(xml_buf, - '(.*?)$', subxml || '' || E'\\1'); - END IF; - - IF format = 'marcxml' THEN - -- If we're not using the prefixed namespace in - -- this record, then remove all declarations of it - IF xml_buf !~ E' mods) - top_el := REGEXP_REPLACE(xml_buf, E'^.*?<((?:\\S+:)?' || - layout.holdings_element || ').*$', E'\\1'); - - IF axml IS NOT NULL THEN - xml_buf := REGEXP_REPLACE(xml_buf, - '(.*?)$', axml || '\\1'); - END IF; - - IF hxml IS NOT NULL THEN - xml_buf := REGEXP_REPLACE(xml_buf, - '(.*?)$', hxml || '\\1'); - END IF; - - IF ('mmr.unapi' = ANY (includes)) THEN - output := REGEXP_REPLACE( - xml_buf, - '(.*?)', - XMLELEMENT( - name abbr, - XMLATTRIBUTES( - 'http://www.w3.org/1999/xhtml' AS xmlns, - 'unapi-id' AS class, - 'tag:open-ils.org:U2@mmr/' || obj_id || '/' || org AS title - ) - )::TEXT || '\\1' - ); - ELSE - output := xml_buf; - END IF; - - -- remove ignorable whitesace - output := REGEXP_REPLACE(output::TEXT,E'>\\s+<','><','gs')::XML; - RETURN output; -END; -$F$ LANGUAGE PLPGSQL STABLE; - --- Forcibly remap deleted master records, retaining the linkage if so configured. -SELECT metabib.remap_metarecord_for_bib( bre.id, bre.fingerprint, TRUE, COALESCE(flag.enabled,FALSE)) - FROM metabib.metarecord metar - JOIN biblio.record_entry bre ON bre.id = metar.master_record, - config.internal_flag flag - WHERE bre.deleted = TRUE AND flag.name = 'ingest.metarecord_mapping.preserve_on_delete'; - -COMMIT; -