$$ LANGUAGE PLPGSQL;
+-- These are simple utility functions to support tests and transformations on
+-- MARC-as-JSON (henceforth "jsonmarc") that we use in a place or two in
+-- Evergreen, particularly in serials.
+--
+-- For a MARC field that would look like this on some traditional displays:
+--
+-- 853 20 ‡81‡av.‡bno.‡i(year)‡j(month)‡u12‡vr‡wm‡x01
+--
+-- the jsonmarc looks like this:
+--
+-- ["2","0","8","1","a","v.","b","no.","u","12","v","r","i","(year)","j","(month)","w","m","x","01"]
+--
+-- Note that jsonmarc does NOT include the MARC field tag, which is implied
+-- in context by whatever Evergreen functions are actually *using* a given
+-- piece of jsonmarc. But jsonmarc does contain the indicators and all the
+-- subfields.
+--
+-- These functions have no dependencies beyond what is already required for
+-- Evergreen.
+--
+
+-- return one value for given subfield as TEXT
+CREATE OR REPLACE FUNCTION evergreen.jsonmarc_get_subfield ( jsonmarc TEXT, subfield CHAR ) RETURNS TEXT AS $p$
+use MARC::Field;
+use JSON::XS;
+
+my ($jsonmarc, $subfield) = @_;
+
+my $json = new JSON::XS;
+my $field = new MARC::Field(999, @{ $json->decode($jsonmarc) });
+
+return $field->subfield($subfield);
+
+$p$ LANGUAGE PLPERLU;
+
+-- return multiple values for given subfield as TEXT[]
+CREATE OR REPLACE FUNCTION evergreen.jsonmarc_get_subfields ( jsonmarc TEXT, subfield CHAR ) RETURNS TEXT[] AS $p$
+use MARC::Field;
+use JSON::XS;
+
+my ($jsonmarc, $subfield) = @_;
+
+my $json = new JSON::XS;
+my $field = new MARC::Field(999, @{ $json->decode($jsonmarc) });
+
+return [ $field->subfield($subfield) ];
+
+$p$ LANGUAGE PLPERLU;
+
+-- return same jsonmarc minus the given subfield (all occurences)
+CREATE OR REPLACE FUNCTION evergreen.jsonmarc_delete_subfields ( jsonmarc TEXT, subfield CHAR ) RETURNS TEXT AS $p$
+use MARC::Field;
+use JSON::XS;
+
+my ($jsonmarc, $subfield) = @_;
+
+my $json = new JSON::XS;
+my $field = new MARC::Field(999, @{ $json->decode($jsonmarc) });
+
+$field->delete_subfield(code => $subfield);
+
+return $json->encode(
+ [
+ $field->indicator(1), $field->indicator(2),
+ map { @$_ } $field->subfields
+ ]
+);
+
+$p$ LANGUAGE PLPERLU;
+
+-- return same jsonmarc plus the given subfield and value
+CREATE OR REPLACE FUNCTION evergreen.jsonmarc_add_subfield ( jsonmarc TEXT, subfield CHAR, value TEXT ) RETURNS TEXT AS $p$
+use MARC::Field;
+use JSON::XS;
+
+my ($jsonmarc, $subfield, $value) = @_;
+
+my $json = new JSON::XS;
+my $field = new MARC::Field(999, @{ $json->decode($jsonmarc) });
+
+$field->add_subfields($subfield, $value);
+
+return $json->encode(
+ [
+ $field->indicator(1), $field->indicator(2),
+ map { @$_ } $field->subfields
+ ]
+);
+
+$p$ LANGUAGE PLPERLU;
+
+-- return same jsonmarc with the given subfield updated to given value
+-- (first occurrence)
+CREATE OR REPLACE FUNCTION evergreen.jsonmarc_update_subfield ( jsonmarc TEXT, subfield CHAR, value TEXT ) RETURNS TEXT AS $p$
+use MARC::Field;
+use JSON::XS;
+
+my ($jsonmarc, $subfield, $value) = @_;
+
+my $json = new JSON::XS;
+my $field = new MARC::Field(999, @{ $json->decode($jsonmarc) });
+
+$field->update($subfield, $value);
+
+return $json->encode(
+ [
+ $field->indicator(1), $field->indicator(2),
+ map { @$_ } $field->subfields
+ ]
+);
+
+$p$ LANGUAGE PLPERLU;
+
+-- return same jsonmarc with subfields sorted by code
+CREATE OR REPLACE FUNCTION evergreen.jsonmarc_sort_subfields ( jsonmarc TEXT ) RETURNS TEXT AS $p$
+use MARC::Field;
+use JSON::XS;
+
+my ($jsonmarc) = @_;
+
+my $json = new JSON::XS;
+my $field = new MARC::Field(999, @{ $json->decode($jsonmarc) });
+
+return $json->encode(
+ [
+ $field->indicator(1), $field->indicator(2),
+ map { @$_ } sort { $a->[0] cmp $b->[0] } $field->subfields
+ ]
+);
+
+$p$ LANGUAGE PLPERLU;
--- /dev/null
+BEGIN;
+
+-- SELECT evergreen.upgrade_deps_block_check('XXXX', :eg_version);
+
+-- These are simple utility functions to support tests and transformations on
+-- MARC-as-JSON (henceforth "jsonmarc") that we use in a place or two in
+-- Evergreen, particularly in serials.
+--
+-- For a MARC field that would look like this on some traditional displays:
+--
+-- 853 20 ‡81‡av.‡bno.‡i(year)‡j(month)‡u12‡vr‡wm‡x01
+--
+-- the jsonmarc looks like this:
+--
+-- ["2","0","8","1","a","v.","b","no.","u","12","v","r","i","(year)","j","(month)","w","m","x","01"]
+--
+-- Note that jsonmarc does NOT include the MARC field tag, which is implied
+-- in context by whatever Evergreen functions are actually *using* a given
+-- piece of jsonmarc. But jsonmarc does contain the indicators and all the
+-- subfields.
+--
+-- These functions have no dependencies beyond what is already required for
+-- Evergreen.
+--
+
+-- return one value for given subfield as TEXT
+
+CREATE OR REPLACE FUNCTION evergreen.jsonmarc_get_subfield ( jsonmarc TEXT, subfield CHAR ) RETURNS TEXT AS $p$
+use MARC::Field;
+use JSON::XS;
+
+my ($jsonmarc, $subfield) = @_;
+
+my $json = new JSON::XS;
+my $field = new MARC::Field(999, @{ $json->decode($jsonmarc) });
+
+return $field->subfield($subfield);
+
+$p$ LANGUAGE PLPERLU;
+
+-- return multiple values for given subfield as TEXT[]
+CREATE OR REPLACE FUNCTION evergreen.jsonmarc_get_subfields ( jsonmarc TEXT, subfield CHAR ) RETURNS TEXT[] AS $p$
+use MARC::Field;
+use JSON::XS;
+
+my ($jsonmarc, $subfield) = @_;
+
+my $json = new JSON::XS;
+my $field = new MARC::Field(999, @{ $json->decode($jsonmarc) });
+
+return [ $field->subfield($subfield) ];
+
+$p$ LANGUAGE PLPERLU;
+
+-- return same jsonmarc minus the given subfield (all occurences)
+CREATE OR REPLACE FUNCTION evergreen.jsonmarc_delete_subfields ( jsonmarc TEXT, subfield CHAR ) RETURNS TEXT AS $p$
+use MARC::Field;
+use JSON::XS;
+
+my ($jsonmarc, $subfield) = @_;
+
+my $json = new JSON::XS;
+my $field = new MARC::Field(999, @{ $json->decode($jsonmarc) });
+
+$field->delete_subfield(code => $subfield);
+
+return $json->encode(
+ [
+ $field->indicator(1), $field->indicator(2),
+ map { @$_ } $field->subfields
+ ]
+);
+
+$p$ LANGUAGE PLPERLU;
+
+-- return same jsonmarc plus the given subfield and value
+CREATE OR REPLACE FUNCTION evergreen.jsonmarc_add_subfield ( jsonmarc TEXT, subfield CHAR, value TEXT ) RETURNS TEXT AS $p$
+use MARC::Field;
+use JSON::XS;
+
+my ($jsonmarc, $subfield, $value) = @_;
+
+my $json = new JSON::XS;
+my $field = new MARC::Field(999, @{ $json->decode($jsonmarc) });
+
+$field->add_subfields($subfield, $value);
+
+return $json->encode(
+ [
+ $field->indicator(1), $field->indicator(2),
+ map { @$_ } $field->subfields
+ ]
+);
+
+$p$ LANGUAGE PLPERLU;
+
+-- return same jsonmarc with the given subfield updated to given value
+-- (first occurrence)
+CREATE OR REPLACE FUNCTION evergreen.jsonmarc_update_subfield ( jsonmarc TEXT, subfield CHAR, value TEXT ) RETURNS TEXT AS $p$
+use MARC::Field;
+use JSON::XS;
+
+my ($jsonmarc, $subfield, $value) = @_;
+
+my $json = new JSON::XS;
+my $field = new MARC::Field(999, @{ $json->decode($jsonmarc) });
+
+$field->update($subfield, $value);
+
+return $json->encode(
+ [
+ $field->indicator(1), $field->indicator(2),
+ map { @$_ } $field->subfields
+ ]
+);
+
+$p$ LANGUAGE PLPERLU;
+
+-- return same jsonmarc with subfields sorted by code
+CREATE OR REPLACE FUNCTION evergreen.jsonmarc_sort_subfields ( jsonmarc TEXT ) RETURNS TEXT AS $p$
+use MARC::Field;
+use JSON::XS;
+
+my ($jsonmarc) = @_;
+
+my $json = new JSON::XS;
+my $field = new MARC::Field(999, @{ $json->decode($jsonmarc) });
+
+return $json->encode(
+ [
+ $field->indicator(1), $field->indicator(2),
+ map { @$_ } sort { $a->[0] cmp $b->[0] } $field->subfields
+ ]
+);
+
+$p$ LANGUAGE PLPERLU;
+
+COMMIT;