]> git.evergreen-ils.org Git - working/Evergreen.git/blob - Open-ILS/src/sql/Pg/version-upgrade/2.3.0-2.3.1-upgrade-db.sql
LP#1178377: Expose bib source in TPAC
[working/Evergreen.git] / Open-ILS / src / sql / Pg / version-upgrade / 2.3.0-2.3.1-upgrade-db.sql
1 --Upgrade Script for 2.3.0 to 2.3.1
2 \set eg_version '''2.3.1'''
3 BEGIN;
4 INSERT INTO config.upgrade_log (version, applied_to) VALUES ('2.3.1', :eg_version);
5
6 SELECT evergreen.upgrade_deps_block_check('0740', :eg_version);
7
8 CREATE OR REPLACE
9     FUNCTION metabib.suggest_browse_entries(
10         raw_query_text  TEXT,   -- actually typed by humans at the UI level
11         search_class    TEXT,   -- 'alias' or 'class' or 'class|field..', etc
12         headline_opts   TEXT,   -- markup options for ts_headline()
13         visibility_org  INTEGER,-- null if you don't want opac visibility test
14         query_limit     INTEGER,-- use in LIMIT clause of interal query
15         normalization   INTEGER -- argument to TS_RANK_CD()
16     ) RETURNS TABLE (
17         value                   TEXT,   -- plain
18         field                   INTEGER,
19         buoyant_and_class_match BOOL,
20         field_match             BOOL,
21         field_weight            INTEGER,
22         rank                    REAL,
23         buoyant                 BOOL,
24         match                   TEXT    -- marked up
25     ) AS $func$
26 DECLARE
27     prepared_query_texts    TEXT[];
28     query                   TSQUERY;
29     plain_query             TSQUERY;
30     opac_visibility_join    TEXT;
31     search_class_join       TEXT;
32     r_fields                RECORD;
33 BEGIN
34     prepared_query_texts := metabib.autosuggest_prepare_tsquery(raw_query_text);
35
36     query := TO_TSQUERY('keyword', prepared_query_texts[1]);
37     plain_query := TO_TSQUERY('keyword', prepared_query_texts[2]);
38
39     visibility_org := NULLIF(visibility_org,-1);
40     IF visibility_org IS NOT NULL THEN
41         opac_visibility_join := '
42     JOIN asset.opac_visible_copies aovc ON (
43         aovc.record = x.source AND
44         aovc.circ_lib IN (SELECT id FROM actor.org_unit_descendants($4))
45     )';
46     ELSE
47         opac_visibility_join := '';
48     END IF;
49
50     -- The following determines whether we only provide suggestsons matching
51     -- the user's selected search_class, or whether we show other suggestions
52     -- too. The reason for MIN() is that for search_classes like
53     -- 'title|proper|uniform' you would otherwise get multiple rows.  The
54     -- implication is that if title as a class doesn't have restrict,
55     -- nor does the proper field, but the uniform field does, you're going
56     -- to get 'false' for your overall evaluation of 'should we restrict?'
57     -- To invert that, change from MIN() to MAX().
58
59     SELECT
60         INTO r_fields
61             MIN(cmc.restrict::INT) AS restrict_class,
62             MIN(cmf.restrict::INT) AS restrict_field
63         FROM metabib.search_class_to_registered_components(search_class)
64             AS _registered (field_class TEXT, field INT)
65         JOIN
66             config.metabib_class cmc ON (cmc.name = _registered.field_class)
67         LEFT JOIN
68             config.metabib_field cmf ON (cmf.id = _registered.field);
69
70     -- evaluate 'should we restrict?'
71     IF r_fields.restrict_field::BOOL OR r_fields.restrict_class::BOOL THEN
72         search_class_join := '
73     JOIN
74         metabib.search_class_to_registered_components($2)
75         AS _registered (field_class TEXT, field INT) ON (
76             (_registered.field IS NULL AND
77                 _registered.field_class = cmf.field_class) OR
78             (_registered.field = cmf.id)
79         )
80     ';
81     ELSE
82         search_class_join := '
83     LEFT JOIN
84         metabib.search_class_to_registered_components($2)
85         AS _registered (field_class TEXT, field INT) ON (
86             _registered.field_class = cmc.name
87         )
88     ';
89     END IF;
90
91     RETURN QUERY EXECUTE '
92 SELECT  DISTINCT
93         x.value,
94         x.id,
95         x.push,
96         x.restrict,
97         x.weight,
98         x.ts_rank_cd,
99         x.buoyant,
100         TS_HEADLINE(value, $7, $3)
101   FROM  (SELECT DISTINCT
102                 mbe.value,
103                 cmf.id,
104                 cmc.buoyant AND _registered.field_class IS NOT NULL AS push,
105                 _registered.field = cmf.id AS restrict,
106                 cmf.weight,
107                 TS_RANK_CD(mbe.index_vector, $1, $6),
108                 cmc.buoyant,
109                 mbedm.source
110           FROM  metabib.browse_entry_def_map mbedm
111                 JOIN (SELECT * FROM metabib.browse_entry WHERE index_vector @@ $1 LIMIT 10000) mbe ON (mbe.id = mbedm.entry)
112                 JOIN config.metabib_field cmf ON (cmf.id = mbedm.def)
113                 JOIN config.metabib_class cmc ON (cmf.field_class = cmc.name)
114                 '  || search_class_join || '
115           ORDER BY 3 DESC, 4 DESC NULLS LAST, 5 DESC, 6 DESC, 7 DESC, 1 ASC
116           LIMIT 1000) AS x
117         ' || opac_visibility_join || '
118   ORDER BY 3 DESC, 4 DESC NULLS LAST, 5 DESC, 6 DESC, 7 DESC, 1 ASC
119   LIMIT $5
120 '   -- sic, repeat the order by clause in the outer select too
121     USING
122         query, search_class, headline_opts,
123         visibility_org, query_limit, normalization, plain_query
124         ;
125
126     -- sort order:
127     --  buoyant AND chosen class = match class
128     --  chosen field = match field
129     --  field weight
130     --  rank
131     --  buoyancy
132     --  value itself
133
134 END;
135 $func$ LANGUAGE PLPGSQL;
136
137
138 SELECT evergreen.upgrade_deps_block_check('0742', :eg_version);
139
140 -- Prepare for the July 2013 introduction of OCLC's "on" prefix
141 -- Per LP# 1049171
142
143 CREATE OR REPLACE FUNCTION maintain_control_numbers() RETURNS TRIGGER AS $func$
144 use strict;
145 use MARC::Record;
146 use MARC::File::XML (BinaryEncoding => 'UTF-8');
147 use MARC::Charset;
148 use Encode;
149 use Unicode::Normalize;
150
151 MARC::Charset->assume_unicode(1);
152
153 my $record = MARC::Record->new_from_xml($_TD->{new}{marc});
154 my $schema = $_TD->{table_schema};
155 my $rec_id = $_TD->{new}{id};
156
157 # Short-circuit if maintaining control numbers per MARC21 spec is not enabled
158 my $enable = spi_exec_query("SELECT enabled FROM config.global_flag WHERE name = 'cat.maintain_control_numbers'");
159 if (!($enable->{processed}) or $enable->{rows}[0]->{enabled} eq 'f') {
160     return;
161 }
162
163 # Get the control number identifier from an OU setting based on $_TD->{new}{owner}
164 my $ou_cni = 'EVRGRN';
165
166 my $owner;
167 if ($schema eq 'serial') {
168     $owner = $_TD->{new}{owning_lib};
169 } else {
170     # are.owner and bre.owner can be null, so fall back to the consortial setting
171     $owner = $_TD->{new}{owner} || 1;
172 }
173
174 my $ous_rv = spi_exec_query("SELECT value FROM actor.org_unit_ancestor_setting('cat.marc_control_number_identifier', $owner)");
175 if ($ous_rv->{processed}) {
176     $ou_cni = $ous_rv->{rows}[0]->{value};
177     $ou_cni =~ s/"//g; # Stupid VIM syntax highlighting"
178 } else {
179     # Fall back to the shortname of the OU if there was no OU setting
180     $ous_rv = spi_exec_query("SELECT shortname FROM actor.org_unit WHERE id = $owner");
181     if ($ous_rv->{processed}) {
182         $ou_cni = $ous_rv->{rows}[0]->{shortname};
183     }
184 }
185
186 my ($create, $munge) = (0, 0);
187
188 my @scns = $record->field('035');
189
190 foreach my $id_field ('001', '003') {
191     my $spec_value;
192     my @controls = $record->field($id_field);
193
194     if ($id_field eq '001') {
195         $spec_value = $rec_id;
196     } else {
197         $spec_value = $ou_cni;
198     }
199
200     # Create the 001/003 if none exist
201     if (scalar(@controls) == 1) {
202         # Only one field; check to see if we need to munge it
203         unless (grep $_->data() eq $spec_value, @controls) {
204             $munge = 1;
205         }
206     } else {
207         # Delete the other fields, as with more than 1 001/003 we do not know which 003/001 to match
208         foreach my $control (@controls) {
209             $record->delete_field($control);
210         }
211         $record->insert_fields_ordered(MARC::Field->new($id_field, $spec_value));
212         $create = 1;
213     }
214 }
215
216 my $cn = $record->field('001')->data();
217 # Special handling of OCLC numbers, often found in records that lack 003
218 if ($cn =~ /^o(c[nm]|n)\d/) {
219     $cn =~ s/^o(c[nm]|n)0*(\d+)/$2/;
220     $record->field('003')->data('OCoLC');
221     $create = 0;
222 }
223
224 # Now, if we need to munge the 001, we will first push the existing 001/003
225 # into the 035; but if the record did not have one (and one only) 001 and 003
226 # to begin with, skip this process
227 if ($munge and not $create) {
228
229     my $scn = "(" . $record->field('003')->data() . ")" . $cn;
230
231     # Do not create duplicate 035 fields
232     unless (grep $_->subfield('a') eq $scn, @scns) {
233         $record->insert_fields_ordered(MARC::Field->new('035', '', '', 'a' => $scn));
234     }
235 }
236
237 # Set the 001/003 and update the MARC
238 if ($create or $munge) {
239     $record->field('001')->data($rec_id);
240     $record->field('003')->data($ou_cni);
241
242     my $xml = $record->as_xml_record();
243     $xml =~ s/\n//sgo;
244     $xml =~ s/^<\?xml.+\?\s*>//go;
245     $xml =~ s/>\s+</></go;
246     $xml =~ s/\p{Cc}//go;
247
248     # Embed a version of OpenILS::Application::AppUtils->entityize()
249     # to avoid having to set PERL5LIB for PostgreSQL as well
250
251     # If we are going to convert non-ASCII characters to XML entities,
252     # we had better be dealing with a UTF8 string to begin with
253     $xml = decode_utf8($xml);
254
255     $xml = NFC($xml);
256
257     # Convert raw ampersands to entities
258     $xml =~ s/&(?!\S+;)/&amp;/gso;
259
260     # Convert Unicode characters to entities
261     $xml =~ s/([\x{0080}-\x{fffd}])/sprintf('&#x%X;',ord($1))/sgoe;
262
263     $xml =~ s/[\x00-\x1f]//go;
264     $_TD->{new}{marc} = $xml;
265
266     return "MODIFY";
267 }
268
269 return;
270 $func$ LANGUAGE PLPERLU;
271
272 COMMIT;