move everythign else out of base.sql
[migration-tools.git] / sql / base / 99-deprecated.sql
1 CREATE OR REPLACE FUNCTION migration_tools.attempt_phone (TEXT,TEXT) RETURNS TEXT AS $$
2   DECLARE
3     phone TEXT := $1;
4     areacode TEXT := $2;
5     temp TEXT := '';
6     output TEXT := '';
7     n_digits INTEGER := 0;
8   BEGIN
9     temp := phone;
10     temp := REGEXP_REPLACE(temp, '^1*[^0-9]*(?=[0-9])', '');
11     temp := REGEXP_REPLACE(temp, '[^0-9]*([0-9]{3})[^0-9]*([0-9]{3})[^0-9]*([0-9]{4})', E'\\1-\\2-\\3');
12     n_digits := LENGTH(REGEXP_REPLACE(REGEXP_REPLACE(temp, '(.*)?[a-zA-Z].*', E'\\1') , '[^0-9]', '', 'g'));
13     IF n_digits = 7 AND areacode <> '' THEN
14       temp := REGEXP_REPLACE(temp, '[^0-9]*([0-9]{3})[^0-9]*([0-9]{4})', E'\\1-\\2');
15       output := (areacode || '-' || temp);
16     ELSE
17       output := temp;
18     END IF;
19     RETURN output;
20   END;
21
22 $$ LANGUAGE PLPGSQL STRICT VOLATILE;
23
24 CREATE OR REPLACE FUNCTION migration_tools.is_staff_profile (INT) RETURNS BOOLEAN AS $$
25   DECLARE
26     profile ALIAS FOR $1;
27   BEGIN
28     RETURN CASE WHEN 'Staff' IN (select (permission.grp_ancestors(profile)).name) THEN TRUE ELSE FALSE END;
29   END;
30 $$ LANGUAGE PLPGSQL STRICT STABLE;
31
32 CREATE OR REPLACE FUNCTION migration_tools.base_item_dynamic_field_map (TEXT) RETURNS TEXT AS $$
33     DECLARE
34         migration_schema ALIAS FOR $1;
35         output TEXT;
36     BEGIN
37         FOR output IN
38             EXECUTE 'SELECT ''' || migration_schema || '.'' || value FROM ' || migration_schema || '.config WHERE key = ''base_item_dynamic_field_map'';'
39         LOOP
40             RETURN output;
41         END LOOP;
42     END;
43 $$ LANGUAGE PLPGSQL STRICT STABLE;
44
45 CREATE OR REPLACE FUNCTION migration_tools.base_copy_location_map (TEXT) RETURNS TEXT AS $$
46     DECLARE
47         migration_schema ALIAS FOR $1;
48         output TEXT;
49     BEGIN
50         FOR output IN
51             EXECUTE 'SELECT ''' || migration_schema || '.'' || value FROM ' || migration_schema || '.config WHERE key = ''base_copy_location_map'';'
52         LOOP
53             RETURN output;
54         END LOOP;
55     END;
56 $$ LANGUAGE PLPGSQL STRICT STABLE;
57
58 CREATE OR REPLACE FUNCTION migration_tools.base_circ_field_map (TEXT) RETURNS TEXT AS $$
59     DECLARE
60         migration_schema ALIAS FOR $1;
61         output TEXT;
62     BEGIN
63         FOR output IN
64             EXECUTE 'SELECT ''' || migration_schema || '.'' || value FROM ' || migration_schema || '.config WHERE key = ''base_circ_field_map'';'
65         LOOP
66             RETURN output;
67         END LOOP;
68     END;
69 $$ LANGUAGE PLPGSQL STRICT STABLE;
70
71 CREATE OR REPLACE FUNCTION migration_tools.map_base_patron_profile (TEXT,TEXT,INTEGER) RETURNS VOID AS $$
72     DECLARE
73         migration_schema ALIAS FOR $1;
74         profile_map TEXT;
75         patron_table ALIAS FOR $2;
76         default_patron_profile ALIAS FOR $3;
77         sql TEXT;
78         sql_update TEXT;
79         sql_where1 TEXT := '';
80         sql_where2 TEXT := '';
81         sql_where3 TEXT := '';
82         output RECORD;
83     BEGIN
84         SELECT migration_tools.base_profile_map(migration_schema) INTO STRICT profile_map;
85         FOR output IN 
86             EXECUTE 'SELECT * FROM ' || profile_map || E' ORDER BY id;'
87         LOOP
88             sql_update := 'UPDATE ' || patron_table || ' AS u SET profile = perm_grp_id FROM ' || profile_map || ' AS m WHERE ';
89             sql_where1 := NULLIF(output.legacy_field1,'') || ' = ' || quote_literal( output.legacy_value1 ) || ' AND legacy_field1 = ' || quote_literal(output.legacy_field1) || ' AND legacy_value1 = ' || quote_literal(output.legacy_value1);
90             sql_where2 := NULLIF(output.legacy_field2,'') || ' = ' || quote_literal( output.legacy_value2 ) || ' AND legacy_field2 = ' || quote_literal(output.legacy_field2) || ' AND legacy_value2 = ' || quote_literal(output.legacy_value2);
91             sql_where3 := NULLIF(output.legacy_field3,'') || ' = ' || quote_literal( output.legacy_value3 ) || ' AND legacy_field3 = ' || quote_literal(output.legacy_field3) || ' AND legacy_value3 = ' || quote_literal(output.legacy_value3);
92             sql := sql_update || COALESCE(sql_where1,'') || CASE WHEN sql_where1 <> '' AND sql_where2<> ''  THEN ' AND ' ELSE '' END || COALESCE(sql_where2,'') || CASE WHEN sql_where2 <> '' AND sql_where3 <> '' THEN ' AND ' ELSE '' END || COALESCE(sql_where3,'') || ';';
93             --RAISE INFO 'sql = %', sql;
94             PERFORM migration_tools.exec( $1, sql );
95         END LOOP;
96         PERFORM migration_tools.exec( $1, 'UPDATE ' || patron_table || ' AS u SET profile = ' || quote_literal(default_patron_profile) || ' WHERE profile IS NULL;'  );
97         BEGIN
98             PERFORM migration_tools.exec( $1, 'INSERT INTO ' || migration_schema || '.config (key,value) VALUES ( ''last_base_patron_mapping_profile'', now() );' );
99         EXCEPTION
100             WHEN OTHERS THEN PERFORM migration_tools.exec( $1, 'UPDATE ' || migration_schema || '.config SET value = now() WHERE key = ''last_base_patron_mapping_profile'';' );
101         END;
102     END;
103 $$ LANGUAGE PLPGSQL STRICT STABLE;
104
105 CREATE OR REPLACE FUNCTION migration_tools.map_base_item_table_dynamic (TEXT,TEXT) RETURNS VOID AS $$
106     DECLARE
107         migration_schema ALIAS FOR $1;
108         field_map TEXT;
109         item_table ALIAS FOR $2;
110         sql TEXT;
111         sql_update TEXT;
112         sql_where1 TEXT := '';
113         sql_where2 TEXT := '';
114         sql_where3 TEXT := '';
115         output RECORD;
116     BEGIN
117         SELECT migration_tools.base_item_dynamic_field_map(migration_schema) INTO STRICT field_map;
118         FOR output IN 
119             EXECUTE 'SELECT * FROM ' || field_map || E' ORDER BY id;'
120         LOOP
121             sql_update := 'UPDATE ' || item_table || ' AS i SET ' || output.evergreen_field || E' = ' || quote_literal(output.evergreen_value) || '::' || output.evergreen_datatype || E' FROM ' || field_map || ' AS m WHERE ';
122             sql_where1 := NULLIF(output.legacy_field1,'') || ' = ' || quote_literal( output.legacy_value1 ) || ' AND legacy_field1 = ' || quote_literal(output.legacy_field1) || ' AND legacy_value1 = ' || quote_literal(output.legacy_value1);
123             sql_where2 := NULLIF(output.legacy_field2,'') || ' = ' || quote_literal( output.legacy_value2 ) || ' AND legacy_field2 = ' || quote_literal(output.legacy_field2) || ' AND legacy_value2 = ' || quote_literal(output.legacy_value2);
124             sql_where3 := NULLIF(output.legacy_field3,'') || ' = ' || quote_literal( output.legacy_value3 ) || ' AND legacy_field3 = ' || quote_literal(output.legacy_field3) || ' AND legacy_value3 = ' || quote_literal(output.legacy_value3);
125             sql := sql_update || COALESCE(sql_where1,'') || CASE WHEN sql_where1 <> '' AND sql_where2<> ''  THEN ' AND ' ELSE '' END || COALESCE(sql_where2,'') || CASE WHEN sql_where2 <> '' AND sql_where3 <> '' THEN ' AND ' ELSE '' END || COALESCE(sql_where3,'') || ';';
126             --RAISE INFO 'sql = %', sql;
127             PERFORM migration_tools.exec( $1, sql );
128         END LOOP;
129         BEGIN
130             PERFORM migration_tools.exec( $1, 'INSERT INTO ' || migration_schema || '.config (key,value) VALUES ( ''last_base_item_mapping_dynamic'', now() );' );
131         EXCEPTION
132             WHEN OTHERS THEN PERFORM migration_tools.exec( $1, 'UPDATE ' || migration_schema || '.config SET value = now() WHERE key = ''last_base_item_mapping_dynamic'';' );
133         END;
134     END;
135 $$ LANGUAGE PLPGSQL STRICT VOLATILE;
136
137 CREATE OR REPLACE FUNCTION migration_tools.map_base_item_table_locations (TEXT,TEXT) RETURNS VOID AS $$
138     DECLARE
139         migration_schema ALIAS FOR $1;
140         base_copy_location_map TEXT;
141         item_table ALIAS FOR $2;
142         sql TEXT;
143         sql_update TEXT;
144         sql_where1 TEXT := '';
145         sql_where2 TEXT := '';
146         sql_where3 TEXT := '';
147         output RECORD;
148     BEGIN
149         SELECT migration_tools.base_copy_location_map(migration_schema) INTO STRICT base_copy_location_map;
150         FOR output IN 
151             EXECUTE 'SELECT * FROM ' || base_copy_location_map || E' ORDER BY id;'
152         LOOP
153             sql_update := 'UPDATE ' || item_table || ' AS i SET location = m.location FROM ' || base_copy_location_map || ' AS m WHERE ';
154             sql_where1 := NULLIF(output.legacy_field1,'') || ' = ' || quote_literal( output.legacy_value1 ) || ' AND legacy_field1 = ' || quote_literal(output.legacy_field1) || ' AND legacy_value1 = ' || quote_literal(output.legacy_value1);
155             sql_where2 := NULLIF(output.legacy_field2,'') || ' = ' || quote_literal( output.legacy_value2 ) || ' AND legacy_field2 = ' || quote_literal(output.legacy_field2) || ' AND legacy_value2 = ' || quote_literal(output.legacy_value2);
156             sql_where3 := NULLIF(output.legacy_field3,'') || ' = ' || quote_literal( output.legacy_value3 ) || ' AND legacy_field3 = ' || quote_literal(output.legacy_field3) || ' AND legacy_value3 = ' || quote_literal(output.legacy_value3);
157             sql := sql_update || COALESCE(sql_where1,'') || CASE WHEN sql_where1 <> '' AND sql_where2<> ''  THEN ' AND ' ELSE '' END || COALESCE(sql_where2,'') || CASE WHEN sql_where2 <> '' AND sql_where3 <> '' THEN ' AND ' ELSE '' END || COALESCE(sql_where3,'') || ';';
158             --RAISE INFO 'sql = %', sql;
159             PERFORM migration_tools.exec( $1, sql );
160         END LOOP;
161         BEGIN
162             PERFORM migration_tools.exec( $1, 'INSERT INTO ' || migration_schema || '.config (key,value) VALUES ( ''last_base_item_mapping_locations'', now() );' );
163         EXCEPTION
164             WHEN OTHERS THEN PERFORM migration_tools.exec( $1, 'UPDATE ' || migration_schema || '.config SET value = now() WHERE key = ''last_base_item_mapping_locations'';' );
165         END;
166     END;
167 $$ LANGUAGE PLPGSQL STRICT VOLATILE;
168
169 -- circulate       loan period     max renewals    max out fine amount     fine interval   max fine        item field 1    item value 1    item field 2    item value 2    patron field 1  patron value 1  patron field 2  patron value 2
170 CREATE OR REPLACE FUNCTION migration_tools.map_base_circ_table_dynamic (TEXT,TEXT,TEXT,TEXT) RETURNS VOID AS $$
171     DECLARE
172         migration_schema ALIAS FOR $1;
173         field_map TEXT;
174         circ_table ALIAS FOR $2;
175         item_table ALIAS FOR $3;
176         patron_table ALIAS FOR $4;
177         sql TEXT;
178         sql_update TEXT;
179         sql_where1 TEXT := '';
180         sql_where2 TEXT := '';
181         sql_where3 TEXT := '';
182         sql_where4 TEXT := '';
183         output RECORD;
184     BEGIN
185         SELECT migration_tools.base_circ_field_map(migration_schema) INTO STRICT field_map;
186         FOR output IN 
187             EXECUTE 'SELECT * FROM ' || field_map || E' ORDER BY id;'
188         LOOP
189             sql_update := 'UPDATE ' || circ_table || ' AS c SET duration = ' || quote_literal(output.loan_period) || '::INTERVAL, renewal_remaining = ' || quote_literal(output.max_renewals) || '::INTEGER, recuring_fine = ' || quote_literal(output.fine_amount) || '::NUMERIC(6,2), fine_interval = ' || quote_literal(output.fine_interval) || '::INTERVAL, max_fine = ' || quote_literal(output.max_fine) || '::NUMERIC(6,2) FROM ' || field_map || ' AS m, ' || item_table || ' AS i, ' || patron_table || ' AS u WHERE c.usr = u.id AND c.target_copy = i.id AND ';
190             sql_where1 := NULLIF(output.item_field1,'') || ' = ' || quote_literal( output.item_value1 ) || ' AND item_field1 = ' || quote_literal(output.item_field1) || ' AND item_value1 = ' || quote_literal(output.item_value1);
191             sql_where2 := NULLIF(output.item_field2,'') || ' = ' || quote_literal( output.item_value2 ) || ' AND item_field2 = ' || quote_literal(output.item_field2) || ' AND item_value2 = ' || quote_literal(output.item_value2);
192             sql_where3 := NULLIF(output.patron_field1,'') || ' = ' || quote_literal( output.patron_value1 ) || ' AND patron_field1 = ' || quote_literal(output.patron_field1) || ' AND patron_value1 = ' || quote_literal(output.patron_value1);
193             sql_where4 := NULLIF(output.patron_field2,'') || ' = ' || quote_literal( output.patron_value2 ) || ' AND patron_field2 = ' || quote_literal(output.patron_field2) || ' AND patron_value2 = ' || quote_literal(output.patron_value2);
194             sql := sql_update || COALESCE(sql_where1,'') || CASE WHEN sql_where1 <> '' AND sql_where2<> ''  THEN ' AND ' ELSE '' END || COALESCE(sql_where2,'') || CASE WHEN sql_where2 <> '' AND sql_where3 <> '' THEN ' AND ' ELSE '' END || COALESCE(sql_where3,'') || CASE WHEN sql_where3 <> '' AND sql_where4 <> '' THEN ' AND ' ELSE '' END || COALESCE(sql_where4,'') || ';';
195             --RAISE INFO 'sql = %', sql;
196             PERFORM migration_tools.exec( $1, sql );
197         END LOOP;
198         BEGIN
199             PERFORM migration_tools.exec( $1, 'INSERT INTO ' || migration_schema || '.config (key,value) VALUES ( ''last_base_circ_field_mapping'', now() );' );
200         EXCEPTION
201             WHEN OTHERS THEN PERFORM migration_tools.exec( $1, 'UPDATE ' || migration_schema || '.config SET value = now() WHERE key = ''last_base_circ_field_mapping'';' );
202         END;
203     END;
204 $$ LANGUAGE PLPGSQL STRICT VOLATILE;
205
206 CREATE OR REPLACE FUNCTION migration_tools.apply_circ_matrix_before_20( tablename TEXT ) RETURNS VOID AS $$
207
208 -- Usage:
209 --
210 --   First make sure the circ matrix is loaded and the circulations
211 --   have been staged to the extent possible (but at the very least
212 --   circ_lib, target_copy, usr, and *_renewal).  User profiles and
213 --   circ modifiers must also be in place.
214 --
215 --   SELECT migration_tools.apply_circ_matrix('m_pioneer.action_circulation');
216 --
217
218 DECLARE
219   circ_lib             INT;
220   target_copy          INT;
221   usr                  INT;
222   is_renewal           BOOLEAN;
223   this_duration_rule   INT;
224   this_fine_rule       INT;
225   this_max_fine_rule   INT;
226   rcd                  config.rule_circ_duration%ROWTYPE;
227   rrf                  config.rule_recurring_fine%ROWTYPE;
228   rmf                  config.rule_max_fine%ROWTYPE;
229   circ                 INT;
230   n                    INT := 0;
231   n_circs              INT;
232   
233 BEGIN
234
235   EXECUTE 'SELECT COUNT(*) FROM ' || tablename || ';' INTO n_circs;
236
237   FOR circ IN EXECUTE ('SELECT id FROM ' || tablename) LOOP
238
239     -- Fetch the correct rules for this circulation
240     EXECUTE ('
241       SELECT
242         circ_lib,
243         target_copy,
244         usr,
245         CASE
246           WHEN phone_renewal OR desk_renewal OR opac_renewal THEN TRUE
247           ELSE FALSE
248         END
249       FROM ' || tablename || ' WHERE id = ' || circ || ';')
250       INTO circ_lib, target_copy, usr, is_renewal ;
251     SELECT
252       INTO this_duration_rule,
253            this_fine_rule,
254            this_max_fine_rule
255       duration_rule,
256       recuring_fine_rule,
257       max_fine_rule
258       FROM action.find_circ_matrix_matchpoint(
259         circ_lib,
260         target_copy,
261         usr,
262         is_renewal
263         );
264     SELECT INTO rcd * FROM config.rule_circ_duration
265       WHERE id = this_duration_rule;
266     SELECT INTO rrf * FROM config.rule_recurring_fine
267       WHERE id = this_fine_rule;
268     SELECT INTO rmf * FROM config.rule_max_fine
269       WHERE id = this_max_fine_rule;
270
271     -- Apply the rules to this circulation
272     EXECUTE ('UPDATE ' || tablename || ' c
273     SET
274       duration_rule = rcd.name,
275       recuring_fine_rule = rrf.name,
276       max_fine_rule = rmf.name,
277       duration = rcd.normal,
278       recuring_fine = rrf.normal,
279       max_fine =
280         CASE rmf.is_percent
281           WHEN TRUE THEN (rmf.amount / 100.0) * ac.price
282           ELSE rmf.amount
283         END,
284       renewal_remaining = rcd.max_renewals
285     FROM
286       config.rule_circ_duration rcd,
287       config.rule_recuring_fine rrf,
288       config.rule_max_fine rmf,
289                         asset.copy ac
290     WHERE
291       rcd.id = ' || this_duration_rule || ' AND
292       rrf.id = ' || this_fine_rule || ' AND
293       rmf.id = ' || this_max_fine_rule || ' AND
294                         ac.id = c.target_copy AND
295       c.id = ' || circ || ';');
296
297     -- Keep track of where we are in the process
298     n := n + 1;
299     IF (n % 100 = 0) THEN
300       RAISE INFO '%', n || ' of ' || n_circs
301         || ' (' || (100*n/n_circs) || '%) circs updated.';
302     END IF;
303
304   END LOOP;
305
306   RETURN;
307 END;
308
309 $$ LANGUAGE plpgsql;
310
311 CREATE OR REPLACE FUNCTION migration_tools.apply_circ_matrix_after_20( tablename TEXT ) RETURNS VOID AS $$
312
313 -- Usage:
314 --
315 --   First make sure the circ matrix is loaded and the circulations
316 --   have been staged to the extent possible (but at the very least
317 --   circ_lib, target_copy, usr, and *_renewal).  User profiles and
318 --   circ modifiers must also be in place.
319 --
320 --   SELECT migration_tools.apply_circ_matrix('m_pioneer.action_circulation');
321 --
322
323 DECLARE
324   circ_lib             INT;
325   target_copy          INT;
326   usr                  INT;
327   is_renewal           BOOLEAN;
328   this_duration_rule   INT;
329   this_fine_rule       INT;
330   this_max_fine_rule   INT;
331   rcd                  config.rule_circ_duration%ROWTYPE;
332   rrf                  config.rule_recurring_fine%ROWTYPE;
333   rmf                  config.rule_max_fine%ROWTYPE;
334   circ                 INT;
335   n                    INT := 0;
336   n_circs              INT;
337   
338 BEGIN
339
340   EXECUTE 'SELECT COUNT(*) FROM ' || tablename || ';' INTO n_circs;
341
342   FOR circ IN EXECUTE ('SELECT id FROM ' || tablename) LOOP
343
344     -- Fetch the correct rules for this circulation
345     EXECUTE ('
346       SELECT
347         circ_lib,
348         target_copy,
349         usr,
350         CASE
351           WHEN phone_renewal OR desk_renewal OR opac_renewal THEN TRUE
352           ELSE FALSE
353         END
354       FROM ' || tablename || ' WHERE id = ' || circ || ';')
355       INTO circ_lib, target_copy, usr, is_renewal ;
356     SELECT
357       INTO this_duration_rule,
358            this_fine_rule,
359            this_max_fine_rule
360       (matchpoint).duration_rule,
361       (matchpoint).recurring_fine_rule,
362       (matchpoint).max_fine_rule
363       FROM action.find_circ_matrix_matchpoint(
364         circ_lib,
365         target_copy,
366         usr,
367         is_renewal
368         );
369     SELECT INTO rcd * FROM config.rule_circ_duration
370       WHERE id = this_duration_rule;
371     SELECT INTO rrf * FROM config.rule_recurring_fine
372       WHERE id = this_fine_rule;
373     SELECT INTO rmf * FROM config.rule_max_fine
374       WHERE id = this_max_fine_rule;
375
376     -- Apply the rules to this circulation
377     EXECUTE ('UPDATE ' || tablename || ' c
378     SET
379       duration_rule = rcd.name,
380       recurring_fine_rule = rrf.name,
381       max_fine_rule = rmf.name,
382       duration = rcd.normal,
383       recurring_fine = rrf.normal,
384       max_fine =
385         CASE rmf.is_percent
386           WHEN TRUE THEN (rmf.amount / 100.0) * ac.price
387           ELSE rmf.amount
388         END,
389       renewal_remaining = rcd.max_renewals,
390       grace_period = rrf.grace_period
391     FROM
392       config.rule_circ_duration rcd,
393       config.rule_recurring_fine rrf,
394       config.rule_max_fine rmf,
395                         asset.copy ac
396     WHERE
397       rcd.id = ' || this_duration_rule || ' AND
398       rrf.id = ' || this_fine_rule || ' AND
399       rmf.id = ' || this_max_fine_rule || ' AND
400                         ac.id = c.target_copy AND
401       c.id = ' || circ || ';');
402
403     -- Keep track of where we are in the process
404     n := n + 1;
405     IF (n % 100 = 0) THEN
406       RAISE INFO '%', n || ' of ' || n_circs
407         || ' (' || (100*n/n_circs) || '%) circs updated.';
408     END IF;
409
410   END LOOP;
411
412   RETURN;
413 END;
414
415 $$ LANGUAGE plpgsql;
416
417 CREATE OR REPLACE FUNCTION migration_tools.insert_856_9_conditional (TEXT, TEXT) RETURNS TEXT AS $$
418
419   ## USAGE: UPDATE biblio.record_entry SET marc = migration_tools.insert_856_9(marc, 'ABC') WHERE [...];
420
421   my ($marcxml, $shortname) = @_;
422
423   use MARC::Record;
424   use MARC::File::XML;
425
426   my $xml = $marcxml;
427
428   eval {
429     my $marc = MARC::Record->new_from_xml($marcxml, 'UTF-8');
430
431     foreach my $field ( $marc->field('856') ) {
432       if ( scalar(grep( /(contentreserve|netlibrary|overdrive)\.com/i, $field->subfield('u'))) > 0 &&
433            ! ( $field->as_string('9') =~ m/$shortname/ ) ) {
434         $field->add_subfields( '9' => $shortname );
435                                 $field->update( ind2 => '0');
436       }
437     }
438
439     $xml = $marc->as_xml_record;
440     $xml =~ s/^<\?.+?\?>$//mo;
441     $xml =~ s/\n//sgo;
442     $xml =~ s/>\s+</></sgo;
443   };
444
445   return $xml;
446
447 $$ LANGUAGE PLPERLU STABLE;
448
449 CREATE OR REPLACE FUNCTION migration_tools.insert_856_9 (TEXT, TEXT) RETURNS TEXT AS $$
450
451   ## USAGE: UPDATE biblio.record_entry SET marc = migration_tools.insert_856_9(marc, 'ABC') WHERE [...];
452
453   my ($marcxml, $shortname) = @_;
454
455   use MARC::Record;
456   use MARC::File::XML;
457
458   my $xml = $marcxml;
459
460   eval {
461     my $marc = MARC::Record->new_from_xml($marcxml, 'UTF-8');
462
463     foreach my $field ( $marc->field('856') ) {
464       if ( ! $field->as_string('9') ) {
465         $field->add_subfields( '9' => $shortname );
466       }
467     }
468
469     $xml = $marc->as_xml_record;
470     $xml =~ s/^<\?.+?\?>$//mo;
471     $xml =~ s/\n//sgo;
472     $xml =~ s/>\s+</></sgo;
473   };
474
475   return $xml;
476
477 $$ LANGUAGE PLPERLU STABLE;
478
479 CREATE OR REPLACE FUNCTION migration_tools.refresh_opac_visible_copies ( ) RETURNS VOID AS $$
480
481 BEGIN   
482
483         DELETE FROM asset.opac_visible_copies;
484
485         INSERT INTO asset.opac_visible_copies (id, circ_lib, record)
486                 SELECT DISTINCT
487                         cp.id, cp.circ_lib, cn.record
488                 FROM
489                         asset.copy cp
490                         JOIN asset.call_number cn ON (cn.id = cp.call_number)
491                         JOIN actor.org_unit a ON (cp.circ_lib = a.id)
492                         JOIN asset.copy_location cl ON (cp.location = cl.id)
493                         JOIN config.copy_status cs ON (cp.status = cs.id)
494                         JOIN biblio.record_entry b ON (cn.record = b.id)
495                 WHERE 
496                         NOT cp.deleted AND
497                         NOT cn.deleted AND
498                         NOT b.deleted AND
499                         cs.opac_visible AND
500                         cl.opac_visible AND
501                         cp.opac_visible AND
502                         a.opac_visible AND
503                         cp.id NOT IN (SELECT id FROM asset.opac_visible_copies);
504
505 END;
506
507 $$ LANGUAGE plpgsql;
508
509 CREATE OR REPLACE FUNCTION migration_tools.log (TEXT,TEXT,INTEGER) RETURNS VOID AS $$
510     DECLARE
511         migration_schema ALIAS FOR $1;
512         sql ALIAS FOR $2;
513         nrows ALIAS FOR $3;
514     BEGIN
515         EXECUTE 'INSERT INTO ' || migration_schema || '.sql_log ( sql, row_count ) VALUES ( ' || quote_literal(sql) || ', ' || nrows || ' );';
516     END;
517 $$ LANGUAGE PLPGSQL STRICT VOLATILE;
518
519 CREATE OR REPLACE FUNCTION migration_tools.exec (TEXT,TEXT) RETURNS VOID AS $$
520     DECLARE
521         migration_schema ALIAS FOR $1;
522         sql ALIAS FOR $2;
523         nrows INTEGER;
524     BEGIN
525         EXECUTE 'UPDATE ' || migration_schema || '.sql_current SET sql = ' || quote_literal(sql) || ';';
526         --RAISE INFO '%', sql;
527         EXECUTE sql;
528         GET DIAGNOSTICS nrows = ROW_COUNT;
529         PERFORM migration_tools.log(migration_schema,sql,nrows);
530     EXCEPTION
531         WHEN OTHERS THEN 
532             RAISE EXCEPTION '!!!!!!!!!!! state = %, msg = %, sql = %', SQLSTATE, SQLERRM, sql;
533     END;
534 $$ LANGUAGE PLPGSQL STRICT VOLATILE;
535
536 CREATE OR REPLACE FUNCTION migration_tools.debug_exec (TEXT,TEXT) RETURNS VOID AS $$
537     DECLARE
538         migration_schema ALIAS FOR $1;
539         sql ALIAS FOR $2;
540         nrows INTEGER;
541     BEGIN
542         EXECUTE 'UPDATE ' || migration_schema || '.sql_current SET sql = ' || quote_literal(sql) || ';';
543         RAISE INFO 'debug_exec sql = %', sql;
544         EXECUTE sql;
545         GET DIAGNOSTICS nrows = ROW_COUNT;
546         PERFORM migration_tools.log(migration_schema,sql,nrows);
547     EXCEPTION
548         WHEN OTHERS THEN 
549             RAISE EXCEPTION '!!!!!!!!!!! state = %, msg = %, sql = %', SQLSTATE, SQLERRM, sql;
550     END;
551 $$ LANGUAGE PLPGSQL STRICT VOLATILE;
552
553 CREATE OR REPLACE FUNCTION migration_tools.insert_base_into_production (TEXT) RETURNS VOID AS $$
554     DECLARE
555         migration_schema ALIAS FOR $1;
556         production_tables TEXT[];
557     BEGIN
558         --RAISE INFO 'In migration_tools.insert_into_production(%)', migration_schema;
559         SELECT migration_tools.production_tables(migration_schema) INTO STRICT production_tables;
560         FOR i IN array_lower(production_tables,1) .. array_upper(production_tables,1) LOOP
561             PERFORM migration_tools.insert_into_production(migration_schema,production_tables[i]);
562         END LOOP;
563     END;
564 $$ LANGUAGE PLPGSQL STRICT VOLATILE;
565
566 CREATE OR REPLACE FUNCTION migration_tools.insert_into_production (TEXT,TEXT) RETURNS VOID AS $$
567     DECLARE
568         migration_schema ALIAS FOR $1;
569         production_table ALIAS FOR $2;
570         base_staging_table TEXT;
571         columns RECORD;
572     BEGIN
573         base_staging_table = REPLACE( production_table, '.', '_' );
574         --RAISE INFO 'In migration_tools.insert_into_production(%,%) -> %', migration_schema, production_table, base_staging_table;
575         EXECUTE 'INSERT INTO ' || production_table || ' SELECT * FROM ' || migration_schema || '.' || base_staging_table || ';';
576     END;
577 $$ LANGUAGE PLPGSQL STRICT VOLATILE;
578
579 -- push bib sequence and return starting value for reserved range
580 CREATE OR REPLACE FUNCTION migration_tools.push_bib_sequence(INTEGER) RETURNS BIGINT AS $$
581     DECLARE
582         bib_count ALIAS FOR $1;
583         output BIGINT;
584     BEGIN
585         PERFORM setval('biblio.record_entry_id_seq',(SELECT MAX(id) FROM biblio.record_entry) + bib_count + 2000);
586         FOR output IN
587             SELECT CEIL(MAX(id)/1000)*1000+1000 FROM biblio.record_entry WHERE id < (SELECT last_value FROM biblio.record_entry_id_seq)
588         LOOP
589             RETURN output;
590         END LOOP;
591     END;
592 $$ LANGUAGE PLPGSQL STRICT VOLATILE;
593
594 -- convenience function for linking two tables
595 -- e.g. select migration_tools.handle_link(:'migschema','asset_copy','barcode','test_foo','l_barcode','x_acp_id',false);
596 CREATE OR REPLACE FUNCTION migration_tools.handle_link (TEXT,TEXT,TEXT,TEXT,TEXT,TEXT,BOOLEAN) RETURNS VOID AS $$
597     DECLARE
598         table_schema ALIAS FOR $1;
599         table_a ALIAS FOR $2;
600         column_a ALIAS FOR $3;
601         table_b ALIAS FOR $4;
602         column_b ALIAS FOR $5;
603         column_x ALIAS FOR $6;
604         btrim_desired ALIAS FOR $7;
605         proceed BOOLEAN;
606     BEGIN
607         EXECUTE 'SELECT EXISTS (
608             SELECT 1
609             FROM information_schema.columns
610             WHERE table_schema = $1
611             AND table_name = $2
612             and column_name = $3
613         )' INTO proceed USING table_schema, table_a, column_a;
614         IF NOT proceed THEN
615             RAISE EXCEPTION '%.% missing column %', table_schema, table_a, column_a; 
616         END IF;
617
618         EXECUTE 'SELECT EXISTS (
619             SELECT 1
620             FROM information_schema.columns
621             WHERE table_schema = $1
622             AND table_name = $2
623             and column_name = $3
624         )' INTO proceed USING table_schema, table_b, column_b;
625         IF NOT proceed THEN
626             RAISE EXCEPTION '%.% missing column %', table_schema, table_b, column_b; 
627         END IF;
628
629         EXECUTE 'ALTER TABLE '
630             || quote_ident(table_b)
631             || ' DROP COLUMN IF EXISTS ' || quote_ident(column_x);
632         EXECUTE 'ALTER TABLE '
633             || quote_ident(table_b)
634             || ' ADD COLUMN ' || quote_ident(column_x) || ' BIGINT';
635
636         IF btrim_desired THEN
637             EXECUTE 'UPDATE ' || quote_ident(table_b) || ' b'
638                 || ' SET ' || quote_ident(column_x) || ' = a.id FROM ' || quote_ident(table_a) || ' a'
639                 || ' WHERE BTRIM(a.' || quote_ident(column_a)
640                 || ') = BTRIM(b.' || quote_ident(column_b) || ')';
641         ELSE
642             EXECUTE 'UPDATE ' || quote_ident(table_b) || ' b'
643                 || ' SET ' || quote_ident(column_x) || ' = a.id FROM ' || quote_ident(table_a) || ' a'
644                 || ' WHERE a.' || quote_ident(column_a)
645                 || ' = b.' || quote_ident(column_b);
646         END IF;
647
648     END;
649 $$ LANGUAGE PLPGSQL STRICT VOLATILE;
650
651 -- convenience function for linking two tables, but copying column w into column x instead of "id"
652 -- e.g. select migration_tools.handle_link2(:'migschema','asset_copy','barcode','test_foo','l_barcode','id','x_acp_id',false);
653 CREATE OR REPLACE FUNCTION migration_tools.handle_link2 (TEXT,TEXT,TEXT,TEXT,TEXT,TEXT,TEXT,BOOLEAN) RETURNS VOID AS $$
654     DECLARE
655         table_schema ALIAS FOR $1;
656         table_a ALIAS FOR $2;
657         column_a ALIAS FOR $3;
658         table_b ALIAS FOR $4;
659         column_b ALIAS FOR $5;
660         column_w ALIAS FOR $6;
661         column_x ALIAS FOR $7;
662         btrim_desired ALIAS FOR $8;
663         proceed BOOLEAN;
664     BEGIN
665         EXECUTE 'SELECT EXISTS (
666             SELECT 1
667             FROM information_schema.columns
668             WHERE table_schema = $1
669             AND table_name = $2
670             and column_name = $3
671         )' INTO proceed USING table_schema, table_a, column_a;
672         IF NOT proceed THEN
673             RAISE EXCEPTION '%.% missing column %', table_schema, table_a, column_a; 
674         END IF;
675
676         EXECUTE 'SELECT EXISTS (
677             SELECT 1
678             FROM information_schema.columns
679             WHERE table_schema = $1
680             AND table_name = $2
681             and column_name = $3
682         )' INTO proceed USING table_schema, table_b, column_b;
683         IF NOT proceed THEN
684             RAISE EXCEPTION '%.% missing column %', table_schema, table_b, column_b; 
685         END IF;
686
687         EXECUTE 'ALTER TABLE '
688             || quote_ident(table_b)
689             || ' DROP COLUMN IF EXISTS ' || quote_ident(column_x);
690         EXECUTE 'ALTER TABLE '
691             || quote_ident(table_b)
692             || ' ADD COLUMN ' || quote_ident(column_x) || ' TEXT';
693
694         IF btrim_desired THEN
695             EXECUTE 'UPDATE ' || quote_ident(table_b) || ' b'
696                 || ' SET ' || quote_ident(column_x) || ' = a.' || quote_ident(column_w) || ' FROM ' || quote_ident(table_a) || ' a'
697                 || ' WHERE BTRIM(a.' || quote_ident(column_a)
698                 || ') = BTRIM(b.' || quote_ident(column_b) || ')';
699         ELSE
700             EXECUTE 'UPDATE ' || quote_ident(table_b) || ' b'
701                 || ' SET ' || quote_ident(column_x) || ' = a.' || quote_ident(column_w) || ' FROM ' || quote_ident(table_a) || ' a'
702                 || ' WHERE a.' || quote_ident(column_a)
703                 || ' = b.' || quote_ident(column_b);
704         END IF;
705
706     END;
707 $$ LANGUAGE PLPGSQL STRICT VOLATILE;
708
709 -- convenience function for linking two tables, but copying column w into column x instead of "id". Unlike handle_link2, this one won't drop the target column, and it also doesn't have a final boolean argument for btrim
710 -- e.g. select migration_tools.handle_link3(:'migschema','asset_copy','barcode','test_foo','l_barcode','id','x_acp_id');
711 CREATE OR REPLACE FUNCTION migration_tools.handle_link3 (TEXT,TEXT,TEXT,TEXT,TEXT,TEXT,TEXT) RETURNS VOID AS $$
712     DECLARE
713         table_schema ALIAS FOR $1;
714         table_a ALIAS FOR $2;
715         column_a ALIAS FOR $3;
716         table_b ALIAS FOR $4;
717         column_b ALIAS FOR $5;
718         column_w ALIAS FOR $6;
719         column_x ALIAS FOR $7;
720         proceed BOOLEAN;
721     BEGIN
722         EXECUTE 'SELECT EXISTS (
723             SELECT 1
724             FROM information_schema.columns
725             WHERE table_schema = $1
726             AND table_name = $2
727             and column_name = $3
728         )' INTO proceed USING table_schema, table_a, column_a;
729         IF NOT proceed THEN
730             RAISE EXCEPTION '%.% missing column %', table_schema, table_a, column_a; 
731         END IF;
732
733         EXECUTE 'SELECT EXISTS (
734             SELECT 1
735             FROM information_schema.columns
736             WHERE table_schema = $1
737             AND table_name = $2
738             and column_name = $3
739         )' INTO proceed USING table_schema, table_b, column_b;
740         IF NOT proceed THEN
741             RAISE EXCEPTION '%.% missing column %', table_schema, table_b, column_b; 
742         END IF;
743
744         EXECUTE 'UPDATE ' || quote_ident(table_b) || ' b'
745             || ' SET ' || quote_ident(column_x) || ' = a.' || quote_ident(column_w) || ' FROM ' || quote_ident(table_a) || ' a'
746             || ' WHERE a.' || quote_ident(column_a)
747             || ' = b.' || quote_ident(column_b);
748
749     END;
750 $$ LANGUAGE PLPGSQL STRICT VOLATILE;
751
752 CREATE OR REPLACE FUNCTION migration_tools.handle_link3_skip_null_or_empty_string (TEXT,TEXT,TEXT,TEXT,TEXT,TEXT,TEXT) RETURNS VOID AS $$
753     DECLARE
754         table_schema ALIAS FOR $1;
755         table_a ALIAS FOR $2;
756         column_a ALIAS FOR $3;
757         table_b ALIAS FOR $4;
758         column_b ALIAS FOR $5;
759         column_w ALIAS FOR $6;
760         column_x ALIAS FOR $7;
761         proceed BOOLEAN;
762     BEGIN
763         EXECUTE 'SELECT EXISTS (
764             SELECT 1
765             FROM information_schema.columns
766             WHERE table_schema = $1
767             AND table_name = $2
768             and column_name = $3
769         )' INTO proceed USING table_schema, table_a, column_a;
770         IF NOT proceed THEN
771             RAISE EXCEPTION '%.% missing column %', table_schema, table_a, column_a; 
772         END IF;
773
774         EXECUTE 'SELECT EXISTS (
775             SELECT 1
776             FROM information_schema.columns
777             WHERE table_schema = $1
778             AND table_name = $2
779             and column_name = $3
780         )' INTO proceed USING table_schema, table_b, column_b;
781         IF NOT proceed THEN
782             RAISE EXCEPTION '%.% missing column %', table_schema, table_b, column_b; 
783         END IF;
784
785         EXECUTE 'UPDATE ' || quote_ident(table_b) || ' b'
786             || ' SET ' || quote_ident(column_x) || ' = a.' || quote_ident(column_w) || ' FROM ' || quote_ident(table_a) || ' a'
787             || ' WHERE a.' || quote_ident(column_a)
788             || ' = b.' || quote_ident(column_b)
789             || ' AND NULLIF(a.' || quote_ident(column_w) || ','''') IS NOT NULL';
790
791     END;
792 $$ LANGUAGE PLPGSQL STRICT VOLATILE;
793
794 CREATE OR REPLACE FUNCTION migration_tools.handle_link3_skip_null (TEXT,TEXT,TEXT,TEXT,TEXT,TEXT,TEXT) RETURNS VOID AS $$
795     DECLARE
796         table_schema ALIAS FOR $1;
797         table_a ALIAS FOR $2;
798         column_a ALIAS FOR $3;
799         table_b ALIAS FOR $4;
800         column_b ALIAS FOR $5;
801         column_w ALIAS FOR $6;
802         column_x ALIAS FOR $7;
803         proceed BOOLEAN;
804     BEGIN
805         EXECUTE 'SELECT EXISTS (
806             SELECT 1
807             FROM information_schema.columns
808             WHERE table_schema = $1
809             AND table_name = $2
810             and column_name = $3
811         )' INTO proceed USING table_schema, table_a, column_a;
812         IF NOT proceed THEN
813             RAISE EXCEPTION '%.% missing column %', table_schema, table_a, column_a; 
814         END IF;
815
816         EXECUTE 'SELECT EXISTS (
817             SELECT 1
818             FROM information_schema.columns
819             WHERE table_schema = $1
820             AND table_name = $2
821             and column_name = $3
822         )' INTO proceed USING table_schema, table_b, column_b;
823         IF NOT proceed THEN
824             RAISE EXCEPTION '%.% missing column %', table_schema, table_b, column_b; 
825         END IF;
826
827         EXECUTE 'UPDATE ' || quote_ident(table_b) || ' b'
828             || ' SET ' || quote_ident(column_x) || ' = a.' || quote_ident(column_w) || ' FROM ' || quote_ident(table_a) || ' a'
829             || ' WHERE a.' || quote_ident(column_a)
830             || ' = b.' || quote_ident(column_b)
831             || ' AND a.' || quote_ident(column_w) || ' IS NOT NULL';
832
833     END;
834 $$ LANGUAGE PLPGSQL STRICT VOLATILE;
835
836 CREATE OR REPLACE FUNCTION migration_tools.handle_link3_skip_true (TEXT,TEXT,TEXT,TEXT,TEXT,TEXT,TEXT) RETURNS VOID AS $$
837     DECLARE
838         table_schema ALIAS FOR $1;
839         table_a ALIAS FOR $2;
840         column_a ALIAS FOR $3;
841         table_b ALIAS FOR $4;
842         column_b ALIAS FOR $5;
843         column_w ALIAS FOR $6;
844         column_x ALIAS FOR $7;
845         proceed BOOLEAN;
846     BEGIN
847         EXECUTE 'SELECT EXISTS (
848             SELECT 1
849             FROM information_schema.columns
850             WHERE table_schema = $1
851             AND table_name = $2
852             and column_name = $3
853         )' INTO proceed USING table_schema, table_a, column_a;
854         IF NOT proceed THEN
855             RAISE EXCEPTION '%.% missing column %', table_schema, table_a, column_a; 
856         END IF;
857
858         EXECUTE 'SELECT EXISTS (
859             SELECT 1
860             FROM information_schema.columns
861             WHERE table_schema = $1
862             AND table_name = $2
863             and column_name = $3
864         )' INTO proceed USING table_schema, table_b, column_b;
865         IF NOT proceed THEN
866             RAISE EXCEPTION '%.% missing column %', table_schema, table_b, column_b; 
867         END IF;
868
869         EXECUTE 'UPDATE ' || quote_ident(table_b) || ' b'
870             || ' SET ' || quote_ident(column_x) || ' = a.' || quote_ident(column_w) || ' FROM ' || quote_ident(table_a) || ' a'
871             || ' WHERE a.' || quote_ident(column_a)
872             || ' = b.' || quote_ident(column_b)
873             || ' AND a.' || quote_ident(column_w) || ' IS NOT TRUE';
874
875     END;
876 $$ LANGUAGE PLPGSQL STRICT VOLATILE;
877
878 CREATE OR REPLACE FUNCTION migration_tools.handle_link3_skip_false (TEXT,TEXT,TEXT,TEXT,TEXT,TEXT,TEXT) RETURNS VOID AS $$
879     DECLARE
880         table_schema ALIAS FOR $1;
881         table_a ALIAS FOR $2;
882         column_a ALIAS FOR $3;
883         table_b ALIAS FOR $4;
884         column_b ALIAS FOR $5;
885         column_w ALIAS FOR $6;
886         column_x ALIAS FOR $7;
887         proceed BOOLEAN;
888     BEGIN
889         EXECUTE 'SELECT EXISTS (
890             SELECT 1
891             FROM information_schema.columns
892             WHERE table_schema = $1
893             AND table_name = $2
894             and column_name = $3
895         )' INTO proceed USING table_schema, table_a, column_a;
896         IF NOT proceed THEN
897             RAISE EXCEPTION '%.% missing column %', table_schema, table_a, column_a; 
898         END IF;
899
900         EXECUTE 'SELECT EXISTS (
901             SELECT 1
902             FROM information_schema.columns
903             WHERE table_schema = $1
904             AND table_name = $2
905             and column_name = $3
906         )' INTO proceed USING table_schema, table_b, column_b;
907         IF NOT proceed THEN
908             RAISE EXCEPTION '%.% missing column %', table_schema, table_b, column_b; 
909         END IF;
910
911         EXECUTE 'UPDATE ' || quote_ident(table_b) || ' b'
912             || ' SET ' || quote_ident(column_x) || ' = a.' || quote_ident(column_w) || ' FROM ' || quote_ident(table_a) || ' a'
913             || ' WHERE a.' || quote_ident(column_a)
914             || ' = b.' || quote_ident(column_b)
915             || ' AND a.' || quote_ident(column_w) || ' IS NOT FALSE';
916
917     END;
918 $$ LANGUAGE PLPGSQL STRICT VOLATILE;
919
920 CREATE OR REPLACE FUNCTION migration_tools.handle_link3_concat_skip_null (TEXT,TEXT,TEXT,TEXT,TEXT,TEXT,TEXT) RETURNS VOID AS $$
921     DECLARE
922         table_schema ALIAS FOR $1;
923         table_a ALIAS FOR $2;
924         column_a ALIAS FOR $3;
925         table_b ALIAS FOR $4;
926         column_b ALIAS FOR $5;
927         column_w ALIAS FOR $6;
928         column_x ALIAS FOR $7;
929         proceed BOOLEAN;
930     BEGIN
931         EXECUTE 'SELECT EXISTS (
932             SELECT 1
933             FROM information_schema.columns
934             WHERE table_schema = $1
935             AND table_name = $2
936             and column_name = $3
937         )' INTO proceed USING table_schema, table_a, column_a;
938         IF NOT proceed THEN
939             RAISE EXCEPTION '%.% missing column %', table_schema, table_a, column_a; 
940         END IF;
941
942         EXECUTE 'SELECT EXISTS (
943             SELECT 1
944             FROM information_schema.columns
945             WHERE table_schema = $1
946             AND table_name = $2
947             and column_name = $3
948         )' INTO proceed USING table_schema, table_b, column_b;
949         IF NOT proceed THEN
950             RAISE EXCEPTION '%.% missing column %', table_schema, table_b, column_b; 
951         END IF;
952
953         EXECUTE 'UPDATE ' || quote_ident(table_b) || ' b'
954             || ' SET ' || quote_ident(column_x) || ' = CONCAT_WS('' ; '',b.' || quote_ident(column_x) || ',a.' || quote_ident(column_w) || ') FROM ' || quote_ident(table_a) || ' a'
955             || ' WHERE a.' || quote_ident(column_a)
956             || ' = b.' || quote_ident(column_b)
957             || ' AND NULLIF(a.' || quote_ident(column_w) || ','''') IS NOT NULL';
958
959     END;
960 $$ LANGUAGE PLPGSQL STRICT VOLATILE;