From: Dan Scott Date: Tue, 5 Jun 2012 13:17:52 +0000 (-0400) Subject: Move 2.0.11 upgrade script into version_upgrade dir X-Git-Url: https://old-git.evergreen-ils.org/?a=commitdiff_plain;h=f9773d70c5a7d273fa3a92df73ff2d2da0883278;p=contrib%2FConifer.git Move 2.0.11 upgrade script into version_upgrade dir Signed-off-by: Dan Scott --- diff --git a/Open-ILS/src/sql/Pg/2.0.10-2.0.11-upgrade-db.sql b/Open-ILS/src/sql/Pg/2.0.10-2.0.11-upgrade-db.sql deleted file mode 100644 index 63800e7b10..0000000000 --- a/Open-ILS/src/sql/Pg/2.0.10-2.0.11-upgrade-db.sql +++ /dev/null @@ -1,501 +0,0 @@ -BEGIN; - -INSERT INTO config.upgrade_log (version) VALUES ('2.0.11'); - - --- Evergreen DB patch XXXX.fix_author_other_index.sql --- --- Fix author|other index so that it doesn't exclude 700 --- fields that contain relator values in the $e or $4. --- - --- check whether patch can be applied -INSERT INTO config.upgrade_log (version) VALUES ('0599'); -- miker/gmc - -UPDATE config.metabib_field -SET xpath = $$//mods32:mods/mods32:name[@type='personal' and not(mods32:role/mods32:roleTerm[text()='creator'])]$$ -WHERE field_class = 'author' -AND name = 'other' -AND xpath = $$//mods32:mods/mods32:name[@type='personal' and not(mods32:role)]$$ -AND format = 'mods32'; - --- To reindex the affected bibs, you can run something like this: --- --- SELECT metabib.reingest_metabib_field_entries(record) --- FROM ( --- SELECT DISTINCT record --- FROM metabib.real_full_rec --- WHERE tag IN ('600', '700', '720', '800') --- AND subfield IN ('4', 'e') --- ) a; - --- Evergreen DB patch XXXX.schema.asset_merge_record_assets.sql --- --- - -INSERT INTO config.upgrade_log (version) VALUES ('0639'); - -CREATE OR REPLACE FUNCTION asset.merge_record_assets( target_record BIGINT, source_record BIGINT ) RETURNS INT AS $func$ -DECLARE - moved_objects INT := 0; - source_cn asset.call_number%ROWTYPE; - target_cn asset.call_number%ROWTYPE; - metarec metabib.metarecord%ROWTYPE; - hold action.hold_request%ROWTYPE; - ser_rec serial.record_entry%ROWTYPE; - uri_count INT := 0; - counter INT := 0; - uri_datafield TEXT; - uri_text TEXT := ''; -BEGIN - - -- move any 856 entries on records that have at least one MARC-mapped URI entry - SELECT INTO uri_count COUNT(*) - FROM asset.uri_call_number_map m - JOIN asset.call_number cn ON (m.call_number = cn.id) - WHERE cn.record = source_record; - - IF uri_count > 0 THEN - - -- This returns more nodes than you might expect: - -- 7 instead of 1 for an 856 with $u $y $9 - SELECT COUNT(*) INTO counter - FROM oils_xpath_table( - 'id', - 'marc', - 'biblio.record_entry', - '//*[@tag="856"]', - 'id=' || source_record - ) as t(i int,c text); - - FOR i IN 1 .. counter LOOP - SELECT '' || - array_to_string( - array_accum( - '' || - regexp_replace( - regexp_replace( - regexp_replace(data,'&','&','g'), - '>', '>', 'g' - ), - '<', '<', 'g' - ) || '' - ), '' - ) || '' INTO uri_datafield - FROM oils_xpath_table( - 'id', - 'marc', - 'biblio.record_entry', - '//*[@tag="856"][position()=' || i || ']/@ind1|' || - '//*[@tag="856"][position()=' || i || ']/@ind2|' || - '//*[@tag="856"][position()=' || i || ']/*/@code|' || - '//*[@tag="856"][position()=' || i || ']/*[@code]', - 'id=' || source_record - ) as t(id int,ind1 text, ind2 text,subfield text,data text); - - -- As most of the results will be NULL, protect against NULLifying - -- the valid content that we do generate - uri_text := uri_text || COALESCE(uri_datafield, ''); - END LOOP; - - IF uri_text <> '' THEN - UPDATE biblio.record_entry - SET marc = regexp_replace(marc,'(]*record>)', uri_text || E'\\1') - WHERE id = target_record; - END IF; - - END IF; - - -- Find and move metarecords to the target record - SELECT INTO metarec * - FROM metabib.metarecord - WHERE master_record = source_record; - - IF FOUND THEN - UPDATE metabib.metarecord - SET master_record = target_record, - mods = NULL - WHERE id = metarec.id; - - moved_objects := moved_objects + 1; - END IF; - - -- Find call numbers attached to the source ... - FOR source_cn IN SELECT * FROM asset.call_number WHERE record = source_record LOOP - - SELECT INTO target_cn * - FROM asset.call_number - WHERE label = source_cn.label - AND owning_lib = source_cn.owning_lib - AND record = target_record; - - -- ... and if there's a conflicting one on the target ... - IF FOUND THEN - - -- ... move the copies to that, and ... - UPDATE asset.copy - SET call_number = target_cn.id - WHERE call_number = source_cn.id; - - -- ... move V holds to the move-target call number - FOR hold IN SELECT * FROM action.hold_request WHERE target = source_cn.id AND hold_type = 'V' LOOP - - UPDATE action.hold_request - SET target = target_cn.id - WHERE id = hold.id; - - moved_objects := moved_objects + 1; - END LOOP; - - -- ... if not ... - ELSE - -- ... just move the call number to the target record - UPDATE asset.call_number - SET record = target_record - WHERE id = source_cn.id; - END IF; - - moved_objects := moved_objects + 1; - END LOOP; - - -- Find T holds targeting the source record ... - FOR hold IN SELECT * FROM action.hold_request WHERE target = source_record AND hold_type = 'T' LOOP - - -- ... and move them to the target record - UPDATE action.hold_request - SET target = target_record - WHERE id = hold.id; - - moved_objects := moved_objects + 1; - END LOOP; - - -- Find serial records targeting the source record ... - FOR ser_rec IN SELECT * FROM serial.record_entry WHERE record = source_record LOOP - -- ... and move them to the target record - UPDATE serial.record_entry - SET record = target_record - WHERE id = ser_rec.id; - - moved_objects := moved_objects + 1; - END LOOP; - - -- Finally, "delete" the source record - DELETE FROM biblio.record_entry WHERE id = source_record; - - -- That's all, folks! - RETURN moved_objects; -END; -$func$ LANGUAGE plpgsql; - - -INSERT INTO config.upgrade_log (version) VALUES ('0646'); - -CREATE OR REPLACE FUNCTION asset.staff_ou_record_copy_count (org INT, rid BIGINT) RETURNS TABLE (depth INT, org_unit INT, visible BIGINT, available BIGINT, unshadow BIGINT, transcendant INT) AS $f$ -DECLARE - ans RECORD; - trans INT; -BEGIN - SELECT 1 INTO trans FROM biblio.record_entry b JOIN config.bib_source src ON (b.source = src.id) WHERE src.transcendant AND b.id = rid; - - FOR ans IN SELECT u.id, t.depth FROM actor.org_unit_ancestors(org) AS u JOIN actor.org_unit_type t ON (u.ou_type = t.id) LOOP - RETURN QUERY - SELECT ans.depth, - ans.id, - COUNT( cp.id ), - SUM( CASE WHEN cp.status IN (0,7,12) THEN 1 ELSE 0 END ), - SUM( CASE WHEN cl.opac_visible AND cp.opac_visible THEN 1 ELSE 0 END), - trans - FROM - actor.org_unit_descendants(ans.id) d - JOIN asset.copy cp ON (cp.circ_lib = d.id AND NOT cp.deleted) - JOIN asset.copy_location cl ON (cp.location = cl.id) - JOIN asset.call_number cn ON (cn.record = rid AND cn.id = cp.call_number AND NOT cn.deleted) - GROUP BY 1,2,6; - - IF NOT FOUND THEN - RETURN QUERY SELECT ans.depth, ans.id, 0::BIGINT, 0::BIGINT, 0::BIGINT, trans; - END IF; - - END LOOP; - - RETURN; -END; -$f$ LANGUAGE PLPGSQL; - -CREATE OR REPLACE FUNCTION asset.staff_lasso_record_copy_count (i_lasso INT, rid BIGINT) RETURNS TABLE (depth INT, org_unit INT, visible BIGINT, available BIGINT, unshadow BIGINT, transcendant INT) AS $f$ -DECLARE - ans RECORD; - trans INT; -BEGIN - SELECT 1 INTO trans FROM biblio.record_entry b JOIN config.bib_source src ON (b.source = src.id) WHERE src.transcendant AND b.id = rid; - - FOR ans IN SELECT u.org_unit AS id FROM actor.org_lasso_map AS u WHERE lasso = i_lasso LOOP - RETURN QUERY - SELECT -1, - ans.id, - COUNT( cp.id ), - SUM( CASE WHEN cp.status IN (0,7,12) THEN 1 ELSE 0 END ), - SUM( CASE WHEN cl.opac_visible AND cp.opac_visible THEN 1 ELSE 0 END), - trans - FROM - actor.org_unit_descendants(ans.id) d - JOIN asset.copy cp ON (cp.circ_lib = d.id AND NOT cp.deleted) - JOIN asset.copy_location cl ON (cp.location = cl.id) - JOIN asset.call_number cn ON (cn.record = rid AND cn.id = cp.call_number AND NOT cn.deleted) - GROUP BY 1,2,6; - - IF NOT FOUND THEN - RETURN QUERY SELECT ans.depth, ans.id, 0::BIGINT, 0::BIGINT, 0::BIGINT, trans; - END IF; - - END LOOP; - - RETURN; -END; -$f$ LANGUAGE PLPGSQL; - - -INSERT INTO config.upgrade_log (version) VALUES ('0649'); - -CREATE OR REPLACE VIEW extend_reporter.full_circ_count AS - SELECT cp.id, COALESCE(c.circ_count, 0::bigint) + COALESCE(count(DISTINCT circ.id), 0::bigint) + COALESCE(count(DISTINCT acirc.id), 0::bigint) AS circ_count - FROM asset."copy" cp - LEFT JOIN extend_reporter.legacy_circ_count c USING (id) - LEFT JOIN "action".circulation circ ON circ.target_copy = cp.id - LEFT JOIN "action".aged_circulation acirc ON acirc.target_copy = cp.id - GROUP BY cp.id, c.circ_count; - - - -INSERT INTO config.upgrade_log (version) VALUES ('0658'); - -CREATE OR REPLACE FUNCTION asset.label_normalizer_dewey(TEXT) RETURNS TEXT AS $func$ - # Derived from the Koha C4::ClassSortRoutine::Dewey module - # Copyright (C) 2007 LibLime - # Licensed under the GPL v2 or later - - use strict; - use warnings; - - my $init = uc(shift); - $init =~ s/^\s+//; - $init =~ s/\s+$//; - $init =~ s!/!!g; - $init =~ s/^([\p{IsAlpha}]+)/$1 /; - my @tokens = split /\.|\s+/, $init; - my $digit_group_count = 0; - for (my $i = 0; $i <= $#tokens; $i++) { - if ($tokens[$i] =~ /^\d+$/) { - $digit_group_count++; - if (2 == $digit_group_count) { - $tokens[$i] = sprintf("%-15.15s", $tokens[$i]); - $tokens[$i] =~ tr/ /0/; - } - } - } - # Pad the first digit_group if there was only one - if (1 == $digit_group_count) { - $tokens[0] .= '_000000000000000' - } - my $key = join("_", @tokens); - $key =~ s/[^\p{IsAlnum}_]//g; - - return $key; - -$func$ LANGUAGE PLPERLU; - --- regenerate sort keys for any dewey call numbers -UPDATE asset.call_number SET id = id WHERE label_class = 2; - - -INSERT INTO config.upgrade_log (version) VALUES ('0665'); - -CREATE OR REPLACE FUNCTION action.item_user_circ_test( circ_ou INT, match_item BIGINT, match_user INT, renewal BOOL ) RETURNS SETOF action.matrix_test_result AS $func$ DECLARE - user_object actor.usr%ROWTYPE; - standing_penalty config.standing_penalty%ROWTYPE; - item_object asset.copy%ROWTYPE; - item_status_object config.copy_status%ROWTYPE; - item_location_object asset.copy_location%ROWTYPE; - result action.matrix_test_result; - circ_test config.circ_matrix_matchpoint%ROWTYPE; - out_by_circ_mod config.circ_matrix_circ_mod_test%ROWTYPE; - circ_mod_map config.circ_matrix_circ_mod_test_map%ROWTYPE; - hold_ratio action.hold_stats%ROWTYPE; - penalty_type TEXT; - tmp_grp INT; - items_out INT; - context_org_list INT[]; - done BOOL := FALSE; -BEGIN - result.success := TRUE; - - -- Fail if the user is BARRED - SELECT INTO user_object * FROM actor.usr WHERE id = match_user; - - -- Fail if we couldn't find the user - IF user_object.id IS NULL THEN - result.fail_part := 'no_user'; - result.success := FALSE; - done := TRUE; - RETURN NEXT result; - RETURN; - END IF; - - SELECT INTO item_object * FROM asset.copy WHERE id = match_item; - - -- Fail if we couldn't find the item - IF item_object.id IS NULL THEN - result.fail_part := 'no_item'; - result.success := FALSE; - done := TRUE; - RETURN NEXT result; - RETURN; - END IF; - - SELECT INTO circ_test * FROM action.find_circ_matrix_matchpoint(circ_ou, match_item, match_user, renewal); - result.matchpoint := circ_test.id; - - -- Fail if we couldn't find a matchpoint - IF result.matchpoint IS NULL THEN - result.fail_part := 'no_matchpoint'; - result.success := FALSE; - done := TRUE; - RETURN NEXT result; - END IF; - - IF user_object.barred IS TRUE THEN - result.fail_part := 'actor.usr.barred'; - result.success := FALSE; - done := TRUE; - RETURN NEXT result; - END IF; - - -- Fail if the item can't circulate - IF item_object.circulate IS FALSE THEN - result.fail_part := 'asset.copy.circulate'; - result.success := FALSE; - done := TRUE; - RETURN NEXT result; - END IF; - - -- Fail if the item isn't in a circulateable status on a non-renewal - IF NOT renewal AND item_object.status NOT IN ( 0, 7, 8 ) THEN - result.fail_part := 'asset.copy.status'; - result.success := FALSE; - done := TRUE; - RETURN NEXT result; - ELSIF renewal AND item_object.status <> 1 THEN - result.fail_part := 'asset.copy.status'; - result.success := FALSE; - done := TRUE; - RETURN NEXT result; - END IF; - - -- Fail if the item can't circulate because of the shelving location - SELECT INTO item_location_object * FROM asset.copy_location WHERE id = item_object.location; - IF item_location_object.circulate IS FALSE THEN - result.fail_part := 'asset.copy_location.circulate'; - result.success := FALSE; - done := TRUE; - RETURN NEXT result; - END IF; - - SELECT INTO context_org_list ARRAY_ACCUM(id) FROM actor.org_unit_full_path( circ_test.org_unit ); - - -- Fail if the test is set to hard non-circulating - IF circ_test.circulate IS FALSE THEN - result.fail_part := 'config.circ_matrix_test.circulate'; - result.success := FALSE; - done := TRUE; - RETURN NEXT result; - END IF; - - -- Fail if the total copy-hold ratio is too low - IF circ_test.total_copy_hold_ratio IS NOT NULL THEN - SELECT INTO hold_ratio * FROM action.copy_related_hold_stats(match_item); - IF hold_ratio.total_copy_ratio IS NOT NULL AND hold_ratio.total_copy_ratio < circ_test.total_copy_hold_ratio THEN - result.fail_part := 'config.circ_matrix_test.total_copy_hold_ratio'; - result.success := FALSE; - done := TRUE; - RETURN NEXT result; - END IF; - END IF; - - -- Fail if the available copy-hold ratio is too low - IF circ_test.available_copy_hold_ratio IS NOT NULL THEN - SELECT INTO hold_ratio * FROM action.copy_related_hold_stats(match_item); - IF hold_ratio.available_copy_ratio IS NOT NULL AND hold_ratio.available_copy_ratio < circ_test.available_copy_hold_ratio THEN - result.fail_part := 'config.circ_matrix_test.available_copy_hold_ratio'; - result.success := FALSE; - done := TRUE; - RETURN NEXT result; - END IF; - END IF; - - IF renewal THEN - penalty_type = '%RENEW%'; - ELSE - penalty_type = '%CIRC%'; - END IF; - - FOR standing_penalty IN - SELECT DISTINCT csp.* - FROM actor.usr_standing_penalty usp - JOIN config.standing_penalty csp ON (csp.id = usp.standing_penalty) - WHERE usr = match_user - AND usp.org_unit IN ( SELECT * FROM unnest(context_org_list) ) - AND (usp.stop_date IS NULL or usp.stop_date > NOW()) - AND csp.block_list LIKE penalty_type LOOP - - result.fail_part := standing_penalty.name; - result.success := FALSE; - done := TRUE; - RETURN NEXT result; - END LOOP; - - -- Fail if the user has too many items with specific circ_modifiers checked out - IF NOT renewal THEN - FOR out_by_circ_mod IN SELECT * FROM config.circ_matrix_circ_mod_test WHERE matchpoint = circ_test.id LOOP - SELECT INTO items_out COUNT(*) - FROM action.circulation circ - JOIN asset.copy cp ON (cp.id = circ.target_copy) - WHERE circ.usr = match_user - AND circ.circ_lib IN ( SELECT * FROM unnest(context_org_list) ) - AND circ.checkin_time IS NULL - AND (circ.stop_fines IN ('MAXFINES','LONGOVERDUE') OR circ.stop_fines IS NULL) - AND cp.circ_modifier IN (SELECT circ_mod FROM config.circ_matrix_circ_mod_test_map WHERE circ_mod_test = out_by_circ_mod.id); - IF items_out >= out_by_circ_mod.items_out THEN - result.fail_part := 'config.circ_matrix_circ_mod_test'; - result.success := FALSE; - done := TRUE; - RETURN NEXT result; - END IF; - END LOOP; - END IF; - - -- If we passed everything, return the successful matchpoint id - IF NOT done THEN - RETURN NEXT result; - END IF; - - RETURN; -END; -$func$ LANGUAGE plpgsql; - - -INSERT INTO config.upgrade_log (version) VALUES ('0691'); - -CREATE INDEX poi_po_idx ON acq.po_item (purchase_order); - -CREATE INDEX ie_inv_idx on acq.invoice_entry (invoice); -CREATE INDEX ie_po_idx on acq.invoice_entry (purchase_order); -CREATE INDEX ie_li_idx on acq.invoice_entry (lineitem); - -CREATE INDEX ii_inv_idx on acq.invoice_item (invoice); -CREATE INDEX ii_po_idx on acq.invoice_item (purchase_order); -CREATE INDEX ii_poi_idx on acq.invoice_item (po_item); - -COMMIT; diff --git a/Open-ILS/src/sql/Pg/version-upgrade/2.0.10-2.0.11-upgrade-db.sql b/Open-ILS/src/sql/Pg/version-upgrade/2.0.10-2.0.11-upgrade-db.sql new file mode 100644 index 0000000000..63800e7b10 --- /dev/null +++ b/Open-ILS/src/sql/Pg/version-upgrade/2.0.10-2.0.11-upgrade-db.sql @@ -0,0 +1,501 @@ +BEGIN; + +INSERT INTO config.upgrade_log (version) VALUES ('2.0.11'); + + +-- Evergreen DB patch XXXX.fix_author_other_index.sql +-- +-- Fix author|other index so that it doesn't exclude 700 +-- fields that contain relator values in the $e or $4. +-- + +-- check whether patch can be applied +INSERT INTO config.upgrade_log (version) VALUES ('0599'); -- miker/gmc + +UPDATE config.metabib_field +SET xpath = $$//mods32:mods/mods32:name[@type='personal' and not(mods32:role/mods32:roleTerm[text()='creator'])]$$ +WHERE field_class = 'author' +AND name = 'other' +AND xpath = $$//mods32:mods/mods32:name[@type='personal' and not(mods32:role)]$$ +AND format = 'mods32'; + +-- To reindex the affected bibs, you can run something like this: +-- +-- SELECT metabib.reingest_metabib_field_entries(record) +-- FROM ( +-- SELECT DISTINCT record +-- FROM metabib.real_full_rec +-- WHERE tag IN ('600', '700', '720', '800') +-- AND subfield IN ('4', 'e') +-- ) a; + +-- Evergreen DB patch XXXX.schema.asset_merge_record_assets.sql +-- +-- + +INSERT INTO config.upgrade_log (version) VALUES ('0639'); + +CREATE OR REPLACE FUNCTION asset.merge_record_assets( target_record BIGINT, source_record BIGINT ) RETURNS INT AS $func$ +DECLARE + moved_objects INT := 0; + source_cn asset.call_number%ROWTYPE; + target_cn asset.call_number%ROWTYPE; + metarec metabib.metarecord%ROWTYPE; + hold action.hold_request%ROWTYPE; + ser_rec serial.record_entry%ROWTYPE; + uri_count INT := 0; + counter INT := 0; + uri_datafield TEXT; + uri_text TEXT := ''; +BEGIN + + -- move any 856 entries on records that have at least one MARC-mapped URI entry + SELECT INTO uri_count COUNT(*) + FROM asset.uri_call_number_map m + JOIN asset.call_number cn ON (m.call_number = cn.id) + WHERE cn.record = source_record; + + IF uri_count > 0 THEN + + -- This returns more nodes than you might expect: + -- 7 instead of 1 for an 856 with $u $y $9 + SELECT COUNT(*) INTO counter + FROM oils_xpath_table( + 'id', + 'marc', + 'biblio.record_entry', + '//*[@tag="856"]', + 'id=' || source_record + ) as t(i int,c text); + + FOR i IN 1 .. counter LOOP + SELECT '' || + array_to_string( + array_accum( + '' || + regexp_replace( + regexp_replace( + regexp_replace(data,'&','&','g'), + '>', '>', 'g' + ), + '<', '<', 'g' + ) || '' + ), '' + ) || '' INTO uri_datafield + FROM oils_xpath_table( + 'id', + 'marc', + 'biblio.record_entry', + '//*[@tag="856"][position()=' || i || ']/@ind1|' || + '//*[@tag="856"][position()=' || i || ']/@ind2|' || + '//*[@tag="856"][position()=' || i || ']/*/@code|' || + '//*[@tag="856"][position()=' || i || ']/*[@code]', + 'id=' || source_record + ) as t(id int,ind1 text, ind2 text,subfield text,data text); + + -- As most of the results will be NULL, protect against NULLifying + -- the valid content that we do generate + uri_text := uri_text || COALESCE(uri_datafield, ''); + END LOOP; + + IF uri_text <> '' THEN + UPDATE biblio.record_entry + SET marc = regexp_replace(marc,'(]*record>)', uri_text || E'\\1') + WHERE id = target_record; + END IF; + + END IF; + + -- Find and move metarecords to the target record + SELECT INTO metarec * + FROM metabib.metarecord + WHERE master_record = source_record; + + IF FOUND THEN + UPDATE metabib.metarecord + SET master_record = target_record, + mods = NULL + WHERE id = metarec.id; + + moved_objects := moved_objects + 1; + END IF; + + -- Find call numbers attached to the source ... + FOR source_cn IN SELECT * FROM asset.call_number WHERE record = source_record LOOP + + SELECT INTO target_cn * + FROM asset.call_number + WHERE label = source_cn.label + AND owning_lib = source_cn.owning_lib + AND record = target_record; + + -- ... and if there's a conflicting one on the target ... + IF FOUND THEN + + -- ... move the copies to that, and ... + UPDATE asset.copy + SET call_number = target_cn.id + WHERE call_number = source_cn.id; + + -- ... move V holds to the move-target call number + FOR hold IN SELECT * FROM action.hold_request WHERE target = source_cn.id AND hold_type = 'V' LOOP + + UPDATE action.hold_request + SET target = target_cn.id + WHERE id = hold.id; + + moved_objects := moved_objects + 1; + END LOOP; + + -- ... if not ... + ELSE + -- ... just move the call number to the target record + UPDATE asset.call_number + SET record = target_record + WHERE id = source_cn.id; + END IF; + + moved_objects := moved_objects + 1; + END LOOP; + + -- Find T holds targeting the source record ... + FOR hold IN SELECT * FROM action.hold_request WHERE target = source_record AND hold_type = 'T' LOOP + + -- ... and move them to the target record + UPDATE action.hold_request + SET target = target_record + WHERE id = hold.id; + + moved_objects := moved_objects + 1; + END LOOP; + + -- Find serial records targeting the source record ... + FOR ser_rec IN SELECT * FROM serial.record_entry WHERE record = source_record LOOP + -- ... and move them to the target record + UPDATE serial.record_entry + SET record = target_record + WHERE id = ser_rec.id; + + moved_objects := moved_objects + 1; + END LOOP; + + -- Finally, "delete" the source record + DELETE FROM biblio.record_entry WHERE id = source_record; + + -- That's all, folks! + RETURN moved_objects; +END; +$func$ LANGUAGE plpgsql; + + +INSERT INTO config.upgrade_log (version) VALUES ('0646'); + +CREATE OR REPLACE FUNCTION asset.staff_ou_record_copy_count (org INT, rid BIGINT) RETURNS TABLE (depth INT, org_unit INT, visible BIGINT, available BIGINT, unshadow BIGINT, transcendant INT) AS $f$ +DECLARE + ans RECORD; + trans INT; +BEGIN + SELECT 1 INTO trans FROM biblio.record_entry b JOIN config.bib_source src ON (b.source = src.id) WHERE src.transcendant AND b.id = rid; + + FOR ans IN SELECT u.id, t.depth FROM actor.org_unit_ancestors(org) AS u JOIN actor.org_unit_type t ON (u.ou_type = t.id) LOOP + RETURN QUERY + SELECT ans.depth, + ans.id, + COUNT( cp.id ), + SUM( CASE WHEN cp.status IN (0,7,12) THEN 1 ELSE 0 END ), + SUM( CASE WHEN cl.opac_visible AND cp.opac_visible THEN 1 ELSE 0 END), + trans + FROM + actor.org_unit_descendants(ans.id) d + JOIN asset.copy cp ON (cp.circ_lib = d.id AND NOT cp.deleted) + JOIN asset.copy_location cl ON (cp.location = cl.id) + JOIN asset.call_number cn ON (cn.record = rid AND cn.id = cp.call_number AND NOT cn.deleted) + GROUP BY 1,2,6; + + IF NOT FOUND THEN + RETURN QUERY SELECT ans.depth, ans.id, 0::BIGINT, 0::BIGINT, 0::BIGINT, trans; + END IF; + + END LOOP; + + RETURN; +END; +$f$ LANGUAGE PLPGSQL; + +CREATE OR REPLACE FUNCTION asset.staff_lasso_record_copy_count (i_lasso INT, rid BIGINT) RETURNS TABLE (depth INT, org_unit INT, visible BIGINT, available BIGINT, unshadow BIGINT, transcendant INT) AS $f$ +DECLARE + ans RECORD; + trans INT; +BEGIN + SELECT 1 INTO trans FROM biblio.record_entry b JOIN config.bib_source src ON (b.source = src.id) WHERE src.transcendant AND b.id = rid; + + FOR ans IN SELECT u.org_unit AS id FROM actor.org_lasso_map AS u WHERE lasso = i_lasso LOOP + RETURN QUERY + SELECT -1, + ans.id, + COUNT( cp.id ), + SUM( CASE WHEN cp.status IN (0,7,12) THEN 1 ELSE 0 END ), + SUM( CASE WHEN cl.opac_visible AND cp.opac_visible THEN 1 ELSE 0 END), + trans + FROM + actor.org_unit_descendants(ans.id) d + JOIN asset.copy cp ON (cp.circ_lib = d.id AND NOT cp.deleted) + JOIN asset.copy_location cl ON (cp.location = cl.id) + JOIN asset.call_number cn ON (cn.record = rid AND cn.id = cp.call_number AND NOT cn.deleted) + GROUP BY 1,2,6; + + IF NOT FOUND THEN + RETURN QUERY SELECT ans.depth, ans.id, 0::BIGINT, 0::BIGINT, 0::BIGINT, trans; + END IF; + + END LOOP; + + RETURN; +END; +$f$ LANGUAGE PLPGSQL; + + +INSERT INTO config.upgrade_log (version) VALUES ('0649'); + +CREATE OR REPLACE VIEW extend_reporter.full_circ_count AS + SELECT cp.id, COALESCE(c.circ_count, 0::bigint) + COALESCE(count(DISTINCT circ.id), 0::bigint) + COALESCE(count(DISTINCT acirc.id), 0::bigint) AS circ_count + FROM asset."copy" cp + LEFT JOIN extend_reporter.legacy_circ_count c USING (id) + LEFT JOIN "action".circulation circ ON circ.target_copy = cp.id + LEFT JOIN "action".aged_circulation acirc ON acirc.target_copy = cp.id + GROUP BY cp.id, c.circ_count; + + + +INSERT INTO config.upgrade_log (version) VALUES ('0658'); + +CREATE OR REPLACE FUNCTION asset.label_normalizer_dewey(TEXT) RETURNS TEXT AS $func$ + # Derived from the Koha C4::ClassSortRoutine::Dewey module + # Copyright (C) 2007 LibLime + # Licensed under the GPL v2 or later + + use strict; + use warnings; + + my $init = uc(shift); + $init =~ s/^\s+//; + $init =~ s/\s+$//; + $init =~ s!/!!g; + $init =~ s/^([\p{IsAlpha}]+)/$1 /; + my @tokens = split /\.|\s+/, $init; + my $digit_group_count = 0; + for (my $i = 0; $i <= $#tokens; $i++) { + if ($tokens[$i] =~ /^\d+$/) { + $digit_group_count++; + if (2 == $digit_group_count) { + $tokens[$i] = sprintf("%-15.15s", $tokens[$i]); + $tokens[$i] =~ tr/ /0/; + } + } + } + # Pad the first digit_group if there was only one + if (1 == $digit_group_count) { + $tokens[0] .= '_000000000000000' + } + my $key = join("_", @tokens); + $key =~ s/[^\p{IsAlnum}_]//g; + + return $key; + +$func$ LANGUAGE PLPERLU; + +-- regenerate sort keys for any dewey call numbers +UPDATE asset.call_number SET id = id WHERE label_class = 2; + + +INSERT INTO config.upgrade_log (version) VALUES ('0665'); + +CREATE OR REPLACE FUNCTION action.item_user_circ_test( circ_ou INT, match_item BIGINT, match_user INT, renewal BOOL ) RETURNS SETOF action.matrix_test_result AS $func$ DECLARE + user_object actor.usr%ROWTYPE; + standing_penalty config.standing_penalty%ROWTYPE; + item_object asset.copy%ROWTYPE; + item_status_object config.copy_status%ROWTYPE; + item_location_object asset.copy_location%ROWTYPE; + result action.matrix_test_result; + circ_test config.circ_matrix_matchpoint%ROWTYPE; + out_by_circ_mod config.circ_matrix_circ_mod_test%ROWTYPE; + circ_mod_map config.circ_matrix_circ_mod_test_map%ROWTYPE; + hold_ratio action.hold_stats%ROWTYPE; + penalty_type TEXT; + tmp_grp INT; + items_out INT; + context_org_list INT[]; + done BOOL := FALSE; +BEGIN + result.success := TRUE; + + -- Fail if the user is BARRED + SELECT INTO user_object * FROM actor.usr WHERE id = match_user; + + -- Fail if we couldn't find the user + IF user_object.id IS NULL THEN + result.fail_part := 'no_user'; + result.success := FALSE; + done := TRUE; + RETURN NEXT result; + RETURN; + END IF; + + SELECT INTO item_object * FROM asset.copy WHERE id = match_item; + + -- Fail if we couldn't find the item + IF item_object.id IS NULL THEN + result.fail_part := 'no_item'; + result.success := FALSE; + done := TRUE; + RETURN NEXT result; + RETURN; + END IF; + + SELECT INTO circ_test * FROM action.find_circ_matrix_matchpoint(circ_ou, match_item, match_user, renewal); + result.matchpoint := circ_test.id; + + -- Fail if we couldn't find a matchpoint + IF result.matchpoint IS NULL THEN + result.fail_part := 'no_matchpoint'; + result.success := FALSE; + done := TRUE; + RETURN NEXT result; + END IF; + + IF user_object.barred IS TRUE THEN + result.fail_part := 'actor.usr.barred'; + result.success := FALSE; + done := TRUE; + RETURN NEXT result; + END IF; + + -- Fail if the item can't circulate + IF item_object.circulate IS FALSE THEN + result.fail_part := 'asset.copy.circulate'; + result.success := FALSE; + done := TRUE; + RETURN NEXT result; + END IF; + + -- Fail if the item isn't in a circulateable status on a non-renewal + IF NOT renewal AND item_object.status NOT IN ( 0, 7, 8 ) THEN + result.fail_part := 'asset.copy.status'; + result.success := FALSE; + done := TRUE; + RETURN NEXT result; + ELSIF renewal AND item_object.status <> 1 THEN + result.fail_part := 'asset.copy.status'; + result.success := FALSE; + done := TRUE; + RETURN NEXT result; + END IF; + + -- Fail if the item can't circulate because of the shelving location + SELECT INTO item_location_object * FROM asset.copy_location WHERE id = item_object.location; + IF item_location_object.circulate IS FALSE THEN + result.fail_part := 'asset.copy_location.circulate'; + result.success := FALSE; + done := TRUE; + RETURN NEXT result; + END IF; + + SELECT INTO context_org_list ARRAY_ACCUM(id) FROM actor.org_unit_full_path( circ_test.org_unit ); + + -- Fail if the test is set to hard non-circulating + IF circ_test.circulate IS FALSE THEN + result.fail_part := 'config.circ_matrix_test.circulate'; + result.success := FALSE; + done := TRUE; + RETURN NEXT result; + END IF; + + -- Fail if the total copy-hold ratio is too low + IF circ_test.total_copy_hold_ratio IS NOT NULL THEN + SELECT INTO hold_ratio * FROM action.copy_related_hold_stats(match_item); + IF hold_ratio.total_copy_ratio IS NOT NULL AND hold_ratio.total_copy_ratio < circ_test.total_copy_hold_ratio THEN + result.fail_part := 'config.circ_matrix_test.total_copy_hold_ratio'; + result.success := FALSE; + done := TRUE; + RETURN NEXT result; + END IF; + END IF; + + -- Fail if the available copy-hold ratio is too low + IF circ_test.available_copy_hold_ratio IS NOT NULL THEN + SELECT INTO hold_ratio * FROM action.copy_related_hold_stats(match_item); + IF hold_ratio.available_copy_ratio IS NOT NULL AND hold_ratio.available_copy_ratio < circ_test.available_copy_hold_ratio THEN + result.fail_part := 'config.circ_matrix_test.available_copy_hold_ratio'; + result.success := FALSE; + done := TRUE; + RETURN NEXT result; + END IF; + END IF; + + IF renewal THEN + penalty_type = '%RENEW%'; + ELSE + penalty_type = '%CIRC%'; + END IF; + + FOR standing_penalty IN + SELECT DISTINCT csp.* + FROM actor.usr_standing_penalty usp + JOIN config.standing_penalty csp ON (csp.id = usp.standing_penalty) + WHERE usr = match_user + AND usp.org_unit IN ( SELECT * FROM unnest(context_org_list) ) + AND (usp.stop_date IS NULL or usp.stop_date > NOW()) + AND csp.block_list LIKE penalty_type LOOP + + result.fail_part := standing_penalty.name; + result.success := FALSE; + done := TRUE; + RETURN NEXT result; + END LOOP; + + -- Fail if the user has too many items with specific circ_modifiers checked out + IF NOT renewal THEN + FOR out_by_circ_mod IN SELECT * FROM config.circ_matrix_circ_mod_test WHERE matchpoint = circ_test.id LOOP + SELECT INTO items_out COUNT(*) + FROM action.circulation circ + JOIN asset.copy cp ON (cp.id = circ.target_copy) + WHERE circ.usr = match_user + AND circ.circ_lib IN ( SELECT * FROM unnest(context_org_list) ) + AND circ.checkin_time IS NULL + AND (circ.stop_fines IN ('MAXFINES','LONGOVERDUE') OR circ.stop_fines IS NULL) + AND cp.circ_modifier IN (SELECT circ_mod FROM config.circ_matrix_circ_mod_test_map WHERE circ_mod_test = out_by_circ_mod.id); + IF items_out >= out_by_circ_mod.items_out THEN + result.fail_part := 'config.circ_matrix_circ_mod_test'; + result.success := FALSE; + done := TRUE; + RETURN NEXT result; + END IF; + END LOOP; + END IF; + + -- If we passed everything, return the successful matchpoint id + IF NOT done THEN + RETURN NEXT result; + END IF; + + RETURN; +END; +$func$ LANGUAGE plpgsql; + + +INSERT INTO config.upgrade_log (version) VALUES ('0691'); + +CREATE INDEX poi_po_idx ON acq.po_item (purchase_order); + +CREATE INDEX ie_inv_idx on acq.invoice_entry (invoice); +CREATE INDEX ie_po_idx on acq.invoice_entry (purchase_order); +CREATE INDEX ie_li_idx on acq.invoice_entry (lineitem); + +CREATE INDEX ii_inv_idx on acq.invoice_item (invoice); +CREATE INDEX ii_po_idx on acq.invoice_item (purchase_order); +CREATE INDEX ii_poi_idx on acq.invoice_item (po_item); + +COMMIT;