sync with rel_2_2
[koha.git] / updater / updatedatabase
1 #!/usr/bin/perl
2
3 # $Id$
4
5 # Database Updater
6 # This script checks for required updates to the database.
7
8 # Part of the Koha Library Software www.koha.org
9 # Licensed under the GPL.
10
11 # Bugs/ToDo:
12 # - Would also be a good idea to offer to do a backup at this time...
13
14 # NOTE:  If you do something more than once in here, make it table driven.
15 use strict;
16
17 # CPAN modules
18 use DBI;
19 use Getopt::Long;
20
21 # Koha modules
22 use C4::Context;
23
24 use MARC::Record;
25 use MARC::File::XML ( BinaryEncoding => 'utf8' );
26  
27 # FIXME - The user might be installing a new database, so can't rely
28 # on /etc/koha.conf anyway.
29
30 my $debug = 0;
31
32 my (
33     $sth, $sti,
34     $query,
35     %existingtables,    # tables already in database
36     %types,
37     $table,
38     $column,
39     $type, $null, $key, $default, $extra,
40     $prefitem,          # preference item in systempreferences table
41 );
42
43 my $silent;
44 GetOptions( 's' => \$silent );
45 my $dbh = C4::Context->dbh;
46 print "connected to your DB. Checking & modifying it\n" unless $silent;
47 $|=1; # flushes output
48
49 #-------------------
50 # Defines
51
52 # Tables to add if they don't exist
53 my %requiretables = (
54     categorytable       => "(categorycode char(5) NOT NULL default '',
55                              description text default '',
56                              itemtypecodes text default '',
57                              PRIMARY KEY (categorycode)
58                             )",
59     subcategorytable       => "(subcategorycode char(5) NOT NULL default '',
60                              description text default '',
61                              itemtypecodes text default '',
62                              PRIMARY KEY (subcategorycode)
63                             )",
64     mediatypetable       => "(mediatypecode char(5) NOT NULL default '',
65                              description text default '',
66                              itemtypecodes text default '',
67                              PRIMARY KEY (mediatypecode)
68                             )",
69     action_logs         => "(
70                                     `timestamp` TIMESTAMP NOT NULL ,
71                                     `user` INT( 11 ) NOT NULL ,
72                                     `module` TEXT default '',
73                                     `action` TEXT default '' ,
74                                     `object` INT(11) default '' ,
75                                     `info` TEXT default '' ,
76                                     PRIMARY KEY ( `timestamp` , `user` )
77                             )",
78         letter          => "(
79                                         module varchar(20) NOT NULL default '',
80                                         code varchar(20) NOT NULL default '',
81                                         name varchar(100) NOT NULL default '',
82                                         title varchar(200) NOT NULL default '',
83                                         content text,
84                                         PRIMARY KEY  (module,code)
85                                 )",
86         alert           =>"(
87                                         alertid int(11) NOT NULL auto_increment,
88                                         borrowernumber int(11) NOT NULL default '0',
89                                         type varchar(10) NOT NULL default '',
90                                         externalid varchar(20) NOT NULL default '',
91                                         PRIMARY KEY  (alertid),
92                                         KEY borrowernumber (borrowernumber),
93                                         KEY type (type,externalid)
94                                 )",
95         opac_news => "(
96                                 `idnew` int(10) unsigned NOT NULL auto_increment,
97                                 `title` varchar(250) NOT NULL default '',
98                                 `new` text NOT NULL,
99                                 `lang` varchar(4) NOT NULL default '',
100                                 `timestamp` timestamp NOT NULL default CURRENT_TIMESTAMP,
101                                 PRIMARY KEY  (`idnew`)
102                                 )",
103         repeatable_holidays => "(
104                                 `id` int(11) NOT NULL auto_increment,
105                                 `branchcode` varchar(4) NOT NULL default '',
106                                 `weekday` smallint(6) default NULL,
107                                 `day` smallint(6) default NULL,
108                                 `month` smallint(6) default NULL,
109                                 `title` varchar(50) NOT NULL default '',
110                                 `description` text NOT NULL,
111                                 PRIMARY KEY  (`id`)
112                                 )",
113         special_holidays => "(
114                                 `id` int(11) NOT NULL auto_increment,
115                                 `branchcode` varchar(4) NOT NULL default '',
116                                 `day` smallint(6) NOT NULL default '0',
117                                 `month` smallint(6) NOT NULL default '0',
118                                 `year` smallint(6) NOT NULL default '0',
119                                 `isexception` smallint(1) NOT NULL default '1',
120                                 `title` varchar(50) NOT NULL default '',
121                                 `description` text NOT NULL,
122                                 PRIMARY KEY  (`id`)
123                                 )",
124         overduerules    =>"(`branchcode` varchar(255) NOT NULL default '',
125                                         `categorycode` char(2) NOT NULL default '',
126                                         `delay1` int(4) default '0',
127                                         `letter1` varchar(20) default NULL,
128                                         `debarred1` char(1) default '0',
129                                         `delay2` int(4) default '0',
130                                         `debarred2` char(1) default '0',
131                                         `letter2` varchar(20) default NULL,
132                                         `delay3` int(4) default '0',
133                                         `letter3` varchar(20) default NULL,
134                                         `debarred3` int(1) default '0',
135                                         PRIMARY KEY  (`branchcode`,`categorycode`)
136                                         )",
137         cities                  => "(`cityid` int auto_increment,
138                                                 `city_name` char(100) NOT NULL,
139                                                 `city_zipcode` char(20),
140                                                 PRIMARY KEY (`cityid`)
141                                         )",
142         roadtype                        => "(`roadtypeid` int auto_increment,
143                                                 `road_type` char(100) NOT NULL,
144                                                 PRIMARY KEY (`roadtypeid`)
145                                         )",
146
147         labels                     => "(
148                                 labelid int(11) NOT NULL auto_increment,
149                                 itemnumber varchar(100) NOT NULL default '',
150                                 timestamp timestamp(14) NOT NULL,
151                                 PRIMARY KEY  (labelid)
152                                 )",
153
154         labels_conf                => "(
155                                 id int(4) NOT NULL auto_increment,
156                                 barcodetype char(100) default '',
157                                 title tinyint(1) default '0',
158                                 isbn tinyint(1) default '0',
159                                 itemtype tinyint(1) default '0',
160                                 barcode tinyint(1) default '0',
161                                 dewey tinyint(1) default '0',
162                                 class tinyint(1) default '0',
163                                 author tinyint(1) default '0',
164                                 papertype char(100) default '',
165                                 startrow int(2) default NULL,
166                                 PRIMARY KEY  (id)
167                                 )",
168        reviews                  => "(
169                                 reviewid integer NOT NULL auto_increment,
170                                 borrowernumber integer,
171                                 biblionumber integer,
172                                 review text,
173                                 approved tinyint,
174                                 datereviewed datetime,
175                                 PRIMARY KEY (reviewid)
176                                 )",
177        borrowers_to_borrowers  => "(
178                                 borrower1 integer,
179                                 borrower2 integer
180                                 )",
181
182 );
183
184 my %requirefields = (
185         subscription => { 'letter' => 'char(20) NULL', 'distributedto' => 'text NULL'},
186         itemtypes => { 'imageurl' => 'char(200) NULL'},
187         aqbookfund => { 'branchcode' => 'varchar(4) NULL'},
188         aqbudget => { 'branchcode' => 'varchar(4) NULL'},
189         auth_header => { 'marc' => 'BLOB NOT NULL', 'linkid' => 'BIGINT(20) NULL'},
190         auth_subfield_structure =>{ 'hidden' => 'TINYINT(3) NOT NULL UNSIGNED ZEROFILL', 'kohafield' => 'VARCHAR(45) NOT NULL', 'linkid' =>  'TINYINT(1) NOT NULL UNSIGNED', 'isurl' => 'TINYINT(1) UNSIGNED'},
191         statistics => { 'associatedborrower' => 'integer'},
192 #    tablename        => { 'field' => 'fieldtype' },
193 );
194
195 my %dropable_table = (
196         sessionqueries  => 'sessionqueries',
197         marcrecorddone  => 'marcrecorddone',
198         users                   => 'users',
199         itemsprices             => 'itemsprices',
200         biblioanalysis  => 'biblioanalysis',
201         borexp                  => 'borexp',
202 # tablename => 'tablename',
203 );
204
205 my %uselessfields = (
206 # tablename => "field1,field2",
207         borrowers => "suburb,altstreetaddress,altsuburb,altcity,studentnumber,school,area,preferredcont,altcp",
208         deletedborrowers=> "suburb,altstreetaddress,altsuburb,altcity,studentnumber,school,area,preferredcont,altcp",
209         );
210 # the other hash contains other actions that can't be done elsewhere. they are done
211 # either BEFORE of AFTER everything else, depending on "when" entry (default => AFTER)
212
213 # The tabledata hash contains data that should be in the tables.
214 # The uniquefieldrequired hash entry is used to determine which (if any) fields
215 # must not exist in the table for this row to be inserted.  If the
216 # uniquefieldrequired entry is already in the table, the existing data is not
217 # modified, unless the forceupdate hash entry is also set.  Fields in the
218 # anonymous "forceupdate" hash will be forced to be updated to the default
219 # values given in the %tabledata hash.
220
221 my %tabledata = (
222 # tablename => [
223 #       {       uniquefielrequired => 'fieldname', # the primary key in the table
224 #               fieldname => fieldvalue,
225 #               fieldname2 => fieldvalue2,
226 #       },
227 # ],
228     systempreferences => [
229                 {
230             uniquefieldrequired => 'variable',
231             variable            => 'Activate_Log',
232             value               => 'On',
233             forceupdate         => { 'explanation' => 1,
234                                      'type' => 1},
235             explanation         => 'Turn Log Actions on DB On an Off',
236             type                => 'YesNo',
237         },
238         {
239             uniquefieldrequired => 'variable',
240             variable            => 'IndependantBranches',
241             value               => 0,
242             forceupdate         => { 'explanation' => 1,
243                                      'type' => 1},
244             explanation         => 'Turn Branch independancy management On an Off',
245             type                => 'YesNo',
246         },
247                 {
248             uniquefieldrequired => 'variable',
249             variable            => 'ReturnBeforeExpiry',
250             value               => 'Off',
251             forceupdate         => { 'explanation' => 1,
252                                      'type' => 1},
253             explanation         => 'If Yes, Returndate on issuing can\'t be after borrower card expiry',
254             type                => 'YesNo',
255         },
256
257         {
258             uniquefieldrequired => 'variable',
259             variable            => 'opacstylesheet',
260             value               => '',
261             forceupdate         => {
262                 'explanation' => 1,
263                 'type'        => 1
264             },
265             explanation =>
266 'Enter a complete URL to use an alternate layout stylesheet in OPAC',
267             type => 'free',
268         },
269         {
270             uniquefieldrequired => 'variable',
271             variable            => 'opaccolorstylesheet',
272             value               => '',
273             forceupdate         => {
274                 'explanation' => 1,
275                 'type'        => 1
276             },
277             explanation =>
278               'Enter the name of the color stylesheet to use in the OPAC',
279             type => 'free',
280         },
281         {
282             uniquefieldrequired => 'variable',
283             variable            => 'opaclayoutstylesheet',
284             value               => '',
285             forceupdate         => {
286                 'explanation' => 1,
287                 'type'        => 1
288             },
289             explanation =>
290               'Enter the name of the layout stylesheet to use in the OPAC',
291             type => 'free',
292         },
293
294         {
295             uniquefieldrequired => 'variable',
296             variable            => 'opacreadinghistory',
297             value               => '1',
298             forceupdate         => {
299                 'explanation' => 1,
300                 'type'        => 1
301             },
302             explanation =>
303               'Turn on/off display of Patron Reading History in OPAC',
304             type => 'YesNo',
305         },
306         {
307             uniquefieldrequired => 'variable',
308             variable            => 'opaclanguagesdisplay',
309             value               => '1',
310             forceupdate         => {
311                 'explanation' => 1,
312                 'type'        => 1
313             },
314             explanation =>
315               'Turn on/off display of Change Language feature on OPAC',
316             type => 'YesNo',
317         },
318         {
319             uniquefieldrequired => 'variable',
320             variable            => 'patronimages',
321             value               => '0',
322             forceupdate         => {
323                 'explanation' => 1,
324                 'type'        => 1
325             },
326             explanation =>
327 'Turn on/off display of patron images in Intranet and specify a file extension for images',
328             type => 'free',
329         },
330         {
331             uniquefieldrequired => 'variable',
332             variable            => 'intranetstylesheet',
333             value               => '',
334             forceupdate         => {
335                 'explanation' => 1,
336                 'type'        => 1
337             },
338             explanation =>
339 'Enter a complete URL to use an alternate layout stylesheet in Intranet',
340             type => 'free',
341         },
342         {
343             uniquefieldrequired => 'variable',
344             variable            => 'intranetcolorstylesheet',
345             value               => '',
346             forceupdate         => {
347                 'explanation' => 1,
348                 'type'        => 1
349             },
350             explanation =>
351               'Enter the name of the color stylesheet to use in Intranet',
352             type => 'free',
353         },
354         {
355             uniquefieldrequired => 'variable',
356             variable            => 'opacsmallimage',
357             value               => '',
358             forceupdate         => {
359                 'explanation' => 1,
360                 'type'        => 1
361             },
362             explanation =>
363 'Enter a complete URL to an image, will be on top/left instead of the Koha logo',
364             type => 'free',
365         },
366         {
367             uniquefieldrequired => 'variable',
368             variable            => 'opaclargeimage',
369             value               => '',
370             forceupdate         => {
371                 'explanation' => 1,
372                 'type'        => 1
373             },
374             explanation =>
375 'Enter a complete URL to an image, will be on the main page, instead of the Koha logo',
376             type => 'free',
377         },
378         {
379             uniquefieldrequired => 'variable',
380             variable            => 'delimiter',
381             value               => ';',
382             forceupdate         => {
383                 'explanation' => 1,
384                 'type'        => 1
385             },
386             explanation => 'separator for reports exported to spreadsheet',
387             type        => 'free',
388         },
389         {
390             uniquefieldrequired => 'variable',
391             variable            => 'MIME',
392             value               => 'OPENOFFICE.ORG',
393             forceupdate         => {
394                 'explanation' => 1,
395                 'type'        => 1,
396                 'options'     => 1
397             },
398             explanation =>
399 'Define the default application for report exportations into files',
400             type    => 'Choice',
401             options => 'EXCEL|OPENOFFICE.ORG'
402         },
403         {
404             uniquefieldrequired => 'variable',
405             variable            => 'Delimiter',
406             value               => ';',
407             forceupdate         => {
408                 'explanation' => 1,
409                 'type'        => 1,
410                 'options'     => 1
411             },
412             explanation =>
413 'Define the default separator character for report exportations into files',
414             type    => 'Choice',
415             options => ';|tabulation|,|/|\|#'
416         },
417         {
418             uniquefieldrequired => 'variable',
419             variable            => 'SubscriptionHistory',
420             value               => ';',
421             forceupdate         => {
422                 'explanation' => 1,
423                 'type'        => 1,
424                 'options'     => 1
425             },
426             explanation =>
427               'Define the information level for serials history in OPAC',
428             type    => 'Choice',
429             options => 'simplified|full'
430         },
431         {
432             uniquefieldrequired => 'variable',
433             variable            => 'hidelostitems',
434             value               => 'No',
435             forceupdate         => {
436                 'explanation' => 1,
437                 'type'        => 1
438             },
439             explanation => 'show or hide "lost" items in OPAC.',
440             type        => 'YesNo',
441         },
442         {
443             uniquefieldrequired => 'variable',
444             variable            => 'IndependantBranches',
445             value               => '0',
446             forceupdate         => {
447                 'explanation' => 1,
448                 'type'        => 1
449             },
450             explanation => 'Turn Branch independency management On and Off',
451             type        => 'YesNo',
452         },
453         {
454             uniquefieldrequired => 'variable',
455             variable            => 'ReturnBeforeExpiry',
456             value               => '0',
457             forceupdate         => {
458                 'explanation' => 1,
459                 'type'        => 1
460             },
461             explanation =>
462 'If Yes, Returndate on issuing can\'t be after borrower card expiry',
463             type => 'YesNo',
464         },
465         {
466             uniquefieldrequired => 'variable',
467             variable            => 'Disable_Dictionary',
468             value               => '0',
469             forceupdate         => {
470                 'explanation' => 1,
471                 'type'        => 1
472             },
473             explanation => 'Disables Dictionary buttons if set to yes',
474             type        => 'YesNo',
475         },
476         {
477             uniquefieldrequired => 'variable',
478             variable            => 'hide_marc',
479             value               => '0',
480             forceupdate         => {
481                 'explanation' => 1,
482                 'type'        => 1
483             },
484             explanation =>
485 'hide marc specific datas like subfield code & indicators to library',
486             type => 'YesNo',
487         },
488         {
489             uniquefieldrequired => 'variable',
490             variable            => 'NotifyBorrowerDeparture',
491             value               => '0',
492             forceupdate         => {
493                 'explanation' => 1,
494                 'type'        => 1
495             },
496             explanation =>
497               'Delay before expiry where a notice is sent when issuing',
498             type => 'Integer',
499         },
500         {
501             uniquefieldrequired => 'variable',
502             variable            => 'OpacPasswordChange',
503             value               => '1',
504             forceupdate         => {
505                 'explanation' => 1,
506                 'type'        => 1
507             },
508             explanation =>
509 'Enable/Disable password change in OPAC (disable it when using LDAP auth)',
510             type => 'YesNo',
511         },
512         {
513             uniquefieldrequired => 'variable',
514             variable            => 'OpacNav',
515             value               => '',
516             forceupdate         => {
517                 'explanation' => 1,
518                 'type'        => 1
519             },
520             explanation =>
521 'Use HTML tabs to add navigational links to the left-hand navigational bar in OPAC',
522             type    => 'Textarea',
523             options => '70|10'
524         },
525         {
526             uniquefieldrequired => 'variable',
527             variable            => 'IntranetNav',
528             value               => '',
529             forceupdate         => {
530                 'explanation' => 1,
531                 'type'        => 1
532             },
533             explanation =>
534 'Use HTML tabs to add navigational links to the left-hand navigational bar in Intranet',
535             type    => 'Textarea',
536             options => '70|10'
537         },
538
539         {
540             uniquefieldrequired => 'variable',
541             variable            => 'AnonSuggestions',
542             value               => '0',
543             forceupdate         => {
544                 'explanation' => 1,
545                 '
546                                         type' => 1
547             },
548             explanation =>
549               'Set to anonymous borrowernumber to enable Anonymous suggestions',
550             type => 'free',
551         },
552         {
553             uniquefieldrequired => 'variable',
554             variable            => 'MARCOrgCode',
555             value               => '0',
556             forceupdate         => {
557                 'explanation' => 1,
558                 '
559                         type' => 1
560             },
561             explanation =>
562 'Your MARC Organization Code - http://www.loc.gov/marc/organizations/orgshome.html',
563             type => 'free',
564         },
565         {
566             uniquefieldrequired => 'variable',
567             variable            => 'AmazonContent',
568             value               => '0',
569             forceupdate         => {
570                 'explanation' => 1,
571                 '
572                                         type' => 1
573             },
574             explanation =>
575 'Turn On Amazon Content - You MUST set AmazonDevKey and AmazonAssocTag if enabled',
576             type => 'YesNo',
577         },
578         {
579             uniquefieldrequired => 'variable',
580             variable            => 'AmazonDevKey',
581             value               => '',
582             forceupdate         => {
583                 'explanation' => 1,
584                 '
585                                         type' => 1
586             },
587             explanation =>
588 'see: aws-portal.amazon.com/gp/aws/developer/registration/index.html',
589             type => 'free',
590         },
591         {
592             uniquefieldrequired => 'variable',
593             variable            => 'AmazonAssocTag',
594             value               => '',
595             forceupdate         => {
596                 'explanation' => 1,
597                 '
598                                         type' => 1
599             },
600             explanation =>
601               'see: associates.amazon.com/gp/flex/associates/apply-login.html',
602             type => 'free',
603         },
604         {
605             uniquefieldrequired => 'variable',
606             forceupdate         => {
607                 'explanation' => 1,
608                 'type'        => 1,
609                 'options'     => 1
610             },
611             variable    => 'TemplateEncoding',
612             value       => 'iso-8859-1',
613             explanation => 'Specify the encoding to use in Templates',
614             type        => 'Choice',
615             options     => 'iso-8859-1|utf-8'
616         },
617
618         {
619             uniquefieldrequired => 'variable',
620             variable            => 'opaccredits',
621             value               => '',
622             forceupdate         => {
623                 'explanation' => 1,
624                 '
625                                         type' => 1
626             },
627             explanation =>
628               'Put any HTML Credits at the bottom of the OPAC page',
629             type    => 'Textarea',
630             options => '70|10'
631         },
632                 
633 {
634             uniquefieldrequired => 'variable',
635             variable            => 'opacheader',
636             value               => '',
637             forceupdate         => { 'explanation' => 1,                                                                        
638                                                         'type' => 1},
639             explanation         => 'Enter HTML to be included as a custom header in the OPAC',
640             type                => 'Textarea',
641                         options                         => '30|10'
642         },
643
644                 {
645             uniquefieldrequired => 'variable',
646             variable            => 'IntranetBiblioDefaultView',
647             value               => 'marc',
648             forceupdate         => { 'explanation' => 1,                                                             
649                                                                 'type' => 1},
650             explanation         => 'Define the default view of a biblio in the intranet. Can be either normal, marc, or ISBD',
651             type                => 'Choice',
652                         options                         => 'normal|marc|isbd'
653         },
654                 
655                                 {
656             uniquefieldrequired => 'variable',
657             variable            => 'opacbookbag',
658             value               => '1',
659             forceupdate         => { 'explanation' => 1,                                                             
660                                                                 'type' => 1},
661             explanation         => 'Enable or disable display of biblio basket (book bag)',
662             type                => 'YesNo'
663         },
664                 
665                                 {
666             uniquefieldrequired => 'variable',
667             variable            => 'opacuserlogin',
668             value               => '1',
669             forceupdate         => { 'explanation' => 1,                                                             
670                                                                 'type' => 1},
671             explanation         => 'Enable or disable display of user login features',
672             type                => 'YesNo'
673         },
674                 
675         {
676             uniquefieldrequired => 'variable',
677             variable            => 'serialsadditems',
678             value               => '0',
679             forceupdate         => {
680                 'explanation' => 1,
681                 '
682                                         type' => 1
683             },
684             explanation =>
685 'If set, a new item will be automatically added when receiving an issue',
686             type => 'YesNo',
687         },
688         {
689             uniquefieldrequired => 'variable',
690             variable            => 'advancedMARCeditor',
691             value               => '0',
692             forceupdate         => {
693                 'explanation' => 1,
694                 '
695                                         type' => 1
696             },
697             explanation =>
698 "If set, the MARC editor won't show you tag/subfields description",
699             type => 'YesNo',
700         },
701         {
702             uniquefieldrequired => 'variable',
703             variable            => 'z3950NormalizeAuthor',
704             value               => '0',
705             forceupdate         => {
706                 'explanation' => 1,
707                 '
708                                         type' => 1
709             },
710             explanation =>
711 "If set, Personnal Authorities will replace authors in biblio.author",
712             type => 'YesNo',
713         },
714         {
715             uniquefieldrequired => 'variable',
716             variable            => 'z3950AuthorAuthFields',
717             value               => '701,702,700',
718             forceupdate         => {
719                 'explanation' => 1,
720                 '
721                                         type' => 1
722             },
723             explanation =>
724 "contains the MARC biblio tags of person authorities to fill biblio.author with when importing biblio",
725             type => 'free',
726         },
727         {
728             uniquefieldrequired => 'variable',
729             variable            => 'useDaysMode',
730             value               => 'Calendar',
731             forceupdate         => { 'explanation' => 1,
732                                      'type' => 1},
733             explanation                 => 'How to calculate return dates : Calendar means holidays will be controled, Days means the return date don\'t depend on holidays',
734                 type            => 'Choice',
735                 options         => 'Calendar|Days'
736         },
737         {
738             uniquefieldrequired => 'variable',
739             variable            => 'borrowerMandatoryField',
740             value               => 'zipcode|surname',
741             forceupdate         => { 'explanation' => 1,
742                                      'type' => 1},
743             explanation         => 'List all mandatory fields for borrowers',
744             type                => 'free',
745         },
746         {
747             uniquefieldrequired => 'variable',
748             variable            => 'borrowerRelationship',
749             value               => 'father|mother,grand-mother',
750             forceupdate         => { 'explanation' => 1,
751                                      'type' => 1},
752             explanation         => 'The relationships between a guarantor & a guarantee (separated by | or ,)',
753             type                => 'free',
754         },
755         {
756             uniquefieldrequired => 'variable',
757             variable            => 'ReservesMaxPickUpDelay',
758             value               => '10',
759             forceupdate         => { 'explanation' => 1,
760                                      'type' => 1},
761             explanation         => 'Maximum delay to pick up a reserved document',
762             type                => 'free',
763         },
764         {
765             uniquefieldrequired => 'variable',
766             variable            => 'TransfersMaxDaysWarning',
767             value               => '3',
768             forceupdate         => { 'explanation' => 1,
769                                      'type' => 1},
770             explanation         => 'Max delay before considering the transfer has potentialy a problem',
771             type                => 'free',
772         },
773         {
774             uniquefieldrequired => 'variable',
775             variable            => 'memberofinstitution',
776             value               => '0',
777             forceupdate         => { 'explanation' => 1,
778                                      'type' => 1},
779             explanation         => 'Are your patrons members of institutions',
780             type                => 'YesNo',
781         },
782         {
783             uniquefieldrequired => 'variable',
784             variable            => 'ReadingHistory',
785             value               => '0',
786             forceupdate         => { 'explanation' => 1,
787                                      'type' => 1},
788             explanation         => 'Allow reading record info retrievable from issues and oldissues tables',
789             type                => 'YesNo',
790         },
791         {
792             uniquefieldrequired => 'variable',
793             variable            => 'IssuingInProcess',
794             value               => '0',
795             forceupdate         => { 'explanation' => 1,
796                                      'type' => 1},
797             explanation         => 'Allow no debt alert if the patron is issuing item that accumulate debt',
798             type                => 'YesNo',
799         },
800         {
801             uniquefieldrequired => 'variable',
802             variable            => 'AutomaticItemReturn',
803             value               => '1',
804             forceupdate         => { 'explanation' => 1,
805                                      'type' => 1},
806             explanation         => 'This Variable allow or not to return automaticly to his homebranch',
807             type                => 'YesNo',
808         },
809         {
810             uniquefieldrequired => 'variable',
811             variable            => 'reviewson',
812             value               => '0',
813             forceupdate         => { 'explanation' => 1,
814                                      'type' => 1},
815             explanation         => 'Allows patrons to submit reviews from the opac',
816             type                => 'YesNo',
817         },
818     ],
819
820 );
821
822 my %fielddefinitions = (
823 # fieldname => [
824 #       {                 field => 'fieldname',
825 #             type    => 'fieldtype',
826 #             null    => '',
827 #             key     => '',
828 #             default => ''
829 #         },
830 #     ],
831         serial => [
832         {
833             field   => 'notes',
834             type    => 'TEXT',
835             null    => 'NULL',
836             key     => '',
837             default => '',
838             extra   => ''
839         },
840     ],
841         aqbasket =>  [
842                 {
843                         field   => 'booksellerid',
844                         type    => 'int(11)',
845                         null    => 'NOT NULL',
846                         key             => '',
847                         default => '1',
848                         extra   => '',
849                 },
850         ],
851         aqbooksellers =>  [
852                 {
853                         field   => 'listprice',
854                         type    => 'varchar(10)',
855                         null    => 'NULL',
856                         key             => '',
857                         default => '',
858                         extra   => '',
859                 },
860                 {
861                         field   => 'invoiceprice',
862                         type    => 'varchar(10)',
863                         null    => 'NULL',
864                         key             => '',
865                         default => '',
866                         extra   => '',
867                 },
868         ],
869         issues =>  [
870                 {
871                         field   => 'borrowernumber',
872                         type    => 'int(11)',
873                         null    => 'NULL', # can be null when a borrower is deleted and the foreign key rule executed
874                         key             => '',
875                         default => '',
876                         extra   => '',
877                 },
878                 {
879                         field   => 'itemnumber',
880                         type    => 'int(11)',
881                         null    => 'NULL', # can be null when a borrower is deleted and the foreign key rule executed
882                         key             => '',
883                         default => '',
884                         extra   => '',
885                 },
886         ],
887         borrowers => [
888                 {       field => 'firstname',
889                         type => 'text',
890                         null => 'NULL',
891                  },
892                 {       field => 'initials',
893                         type => 'text',
894                         null => 'NULL',
895                  },
896                 {       field => 'B_email',
897                         type => 'text',
898                         null => 'NULL',
899                         after => 'B_zipcode',
900                  },
901                  {
902                         field => 'streetnumber', # street number (hidden if streettable table is empty)
903                         type => 'char(10)',
904                         null => 'NULL',
905                         after => 'initials',
906                 },
907                 {
908                         field => 'streettype', # street table, list builded from a system table
909                         type => 'char(50)',
910                         null => 'NULL',
911                         after => 'streetnumber',
912                 },
913                 {       field => 'phone',
914                         type => 'text',
915                         null => 'NULL',
916                  },                             
917                 {
918                         field => 'B_streetnumber', # street number (hidden if streettable table is empty)
919                         type => 'char(10)',
920                         null => 'NULL',
921                         after => 'fax',
922                 },
923                 {
924                         field => 'B_streettype', # street table, list builded from a system table
925                         type => 'char(50)',
926                         null => 'NULL',
927                         after => 'B_streetnumber',
928                 },
929                 {
930                         field => 'phonepro',
931                         type => 'text',
932                         null => 'NULL',
933                         after => 'fax',
934                 },
935                 {
936                         field => 'address2', # complement address
937                         type => 'text',
938                         null => 'NULL',
939                         after => 'address',
940                 },
941                 {
942                         field => 'emailpro',
943                         type => 'text',
944                         null => 'NULL',
945                         after => 'fax',
946                 },
947                 {
948                         field => 'contactfirstname', # contact's firstname
949                         type => 'text',
950                         null => 'NULL',
951                         after => 'contactname',
952                 },
953                 {
954                         field => 'contacttitle', # contact's title
955                         type => 'text',
956                         null => 'NULL',
957                         after => 'contactfirstname',
958                 },
959         ],
960         
961         deletedborrowers => [
962                 {       field => 'firstname',
963                         type => 'text',
964                         null => 'NULL',
965                  },
966                 {       field => 'initials',
967                         type => 'text',
968                         null => 'NULL',
969                  },
970                 {       field => 'B_email',
971                         type => 'text',
972                         null => 'NULL',
973                         after => 'B_zipcode',
974                  },
975                  {
976                         field => 'streetnumber', # street number (hidden if streettable table is empty)
977                         type => 'char(10)',
978                         null => 'NULL',
979                         after => 'initials',
980                 },
981                 {
982                         field => 'streettype', # street table, list builded from a system table
983                         type => 'char(50)',
984                         null => 'NULL',
985                         after => 'streetnumber',
986                 },
987                 {       field => 'phone',
988                         type => 'text',
989                         null => 'NULL',
990                  },             
991                  {
992                         field => 'B_streetnumber', # street number (hidden if streettable table is empty)
993                         type => 'char(10)',
994                         null => 'NULL',
995                         after => 'fax',
996                 },
997                 {
998                         field => 'B_streettype', # street table, list builded from a system table
999                         type => 'char(50)',
1000                         null => 'NULL',
1001                         after => 'B_streetnumber',
1002                 },
1003                 {
1004                         field => 'phonepro',
1005                         type => 'text',
1006                         null => 'NULL',
1007                         after => 'fax',
1008                 },
1009                 {
1010                         field => 'address2', # complement address
1011                         type => 'text',
1012                         null => 'NULL',
1013                         after => 'address',
1014                 },
1015                 {
1016                         field => 'emailpro',
1017                         type => 'text',
1018                         null => 'NULL',
1019                         after => 'fax',
1020                 },
1021                 {
1022                         field => 'contactfirstname', # contact's firstname
1023                         type => 'text',
1024                         null => 'NULL',
1025                         after => 'contactname',
1026                 },
1027                 {
1028                         field => 'contacttitle', # contact's title
1029                         type => 'text',
1030                         null => 'NULL',
1031                         after => 'contactfirstname',
1032                 },
1033         ],
1034         
1035         branches =>  [
1036                 {
1037                         field   => 'branchip',
1038                         type    => 'varchar(15)',
1039                         null    => 'NULL',
1040                         key             => '',
1041                         default => '',
1042                         extra   => '',
1043                 },
1044                 {
1045                         field   => 'branchprinter',
1046                         type    => 'varchar(100)',
1047                         null    => 'NULL',
1048                         key             => '',
1049                         default => '',
1050                         extra   => '',
1051                 },
1052         ],
1053         categories =>  [
1054                 {
1055                         field   => 'category_type',
1056                         type    => 'char(1)',
1057                         null    => 'NOT NULL',
1058                         key             => '',
1059                         default => 'A',
1060                         extra   => '',
1061                 },
1062         ],
1063         reserves =>  [
1064                 {
1065                         field   => 'waitingdate',
1066                         type    => 'date',
1067                         null    => 'NULL',
1068                         key             => '',
1069                         default => '',
1070                         extra   => '',
1071                 },
1072         ],
1073 );
1074
1075 my %indexes = (
1076 #       table => [
1077 #               {       indexname => 'index detail'
1078 #               }
1079 #       ],
1080         shelfcontents => [
1081                 {       indexname => 'shelfnumber',
1082                         content => 'shelfnumber',
1083                 },
1084                 {       indexname => 'itemnumber',
1085                         content => 'itemnumber',
1086                 }
1087         ],
1088         bibliosubject => [
1089                 {       indexname => 'biblionumber',
1090                         content => 'biblionumber',
1091                 }
1092         ],
1093         items => [
1094                 {       indexname => 'homebranch',
1095                         content => 'homebranch',
1096                 },
1097                 {       indexname => 'holdingbranch',
1098                         content => 'holdingbranch',
1099                 }
1100         ],
1101         aqbooksellers => [
1102                 {       indexname => 'PRIMARY',
1103                         content => 'id',
1104                         type => 'PRIMARY',
1105                 }
1106         ],
1107         aqbasket => [
1108                 {       indexname => 'booksellerid',
1109                         content => 'booksellerid',
1110                 },
1111         ],
1112         aqorders => [
1113                 {       indexname => 'basketno',
1114                         content => 'basketno',
1115                 },
1116         ],
1117         aqorderbreakdown => [
1118                 {       indexname => 'ordernumber',
1119                         content => 'ordernumber',
1120                 },
1121                 {       indexname => 'bookfundid',
1122                         content => 'bookfundid',
1123                 },
1124         ],
1125         currency => [
1126                 {       indexname => 'PRIMARY',
1127                         content => 'currency',
1128                         type => 'PRIMARY',
1129                 }
1130         ],
1131 );
1132
1133 my %foreign_keys = (
1134 #       table => [
1135 #               {       key => 'the key in table' (must be indexed)
1136 #                       foreigntable => 'the foreigntable name', # (the parent)
1137 #                       foreignkey => 'the foreign key column(s)' # (in the parent)
1138 #                       onUpdate => 'CASCADE|SET NULL|NO ACTION| RESTRICT',
1139 #                       onDelete => 'CASCADE|SET NULL|NO ACTION| RESTRICT',
1140 #               }
1141 #       ],
1142         shelfcontents => [
1143                 {       key => 'shelfnumber',
1144                         foreigntable => 'bookshelf',
1145                         foreignkey => 'shelfnumber',
1146                         onUpdate => 'CASCADE',
1147                         onDelete => 'CASCADE',
1148                 },
1149                 {       key => 'itemnumber',
1150                         foreigntable => 'items',
1151                         foreignkey => 'itemnumber',
1152                         onUpdate => 'CASCADE',
1153                         onDelete => 'CASCADE',
1154                 },
1155         ],
1156         # onDelete is RESTRICT on reference tables (branches, itemtype) as we don't want items to be 
1157         # easily deleted, but branches/itemtype not too easy to empty...
1158         biblioitems => [
1159                 {       key => 'biblionumber',
1160                         foreigntable => 'biblio',
1161                         foreignkey => 'biblionumber',
1162                         onUpdate => 'CASCADE',
1163                         onDelete => 'CASCADE',
1164                 },
1165                 {       key => 'itemtype',
1166                         foreigntable => 'itemtypes',
1167                         foreignkey => 'itemtype',
1168                         onUpdate => 'CASCADE',
1169                         onDelete => 'RESTRICT',
1170                 },
1171         ],
1172         items => [
1173                 {       key => 'biblioitemnumber',
1174                         foreigntable => 'biblioitems',
1175                         foreignkey => 'biblioitemnumber',
1176                         onUpdate => 'CASCADE',
1177                         onDelete => 'CASCADE',
1178                 },
1179                 {       key => 'homebranch',
1180                         foreigntable => 'branches',
1181                         foreignkey => 'branchcode',
1182                         onUpdate => 'CASCADE',
1183                         onDelete => 'RESTRICT',
1184                 },
1185                 {       key => 'holdingbranch',
1186                         foreigntable => 'branches',
1187                         foreignkey => 'branchcode',
1188                         onUpdate => 'CASCADE',
1189                         onDelete => 'RESTRICT',
1190                 },
1191         ],
1192         additionalauthors => [
1193                 {       key => 'biblionumber',
1194                         foreigntable => 'biblio',
1195                         foreignkey => 'biblionumber',
1196                         onUpdate => 'CASCADE',
1197                         onDelete => 'CASCADE',
1198                 },
1199         ],
1200         bibliosubject => [
1201                 {       key => 'biblionumber',
1202                         foreigntable => 'biblio',
1203                         foreignkey => 'biblionumber',
1204                         onUpdate => 'CASCADE',
1205                         onDelete => 'CASCADE',
1206                 },
1207         ],
1208         aqbasket => [
1209                 {       key => 'booksellerid',
1210                         foreigntable => 'aqbooksellers',
1211                         foreignkey => 'id',
1212                         onUpdate => 'CASCADE',
1213                         onDelete => 'RESTRICT',
1214                 },
1215         ],
1216         aqorders => [
1217                 {       key => 'basketno',
1218                         foreigntable => 'aqbasket',
1219                         foreignkey => 'basketno',
1220                         onUpdate => 'CASCADE',
1221                         onDelete => 'CASCADE',
1222                 },
1223                 {       key => 'biblionumber',
1224                         foreigntable => 'biblio',
1225                         foreignkey => 'biblionumber',
1226                         onUpdate => 'SET NULL',
1227                         onDelete => 'SET NULL',
1228                 },
1229         ],
1230         aqbooksellers => [
1231                 {       key => 'listprice',
1232                         foreigntable => 'currency',
1233                         foreignkey => 'currency',
1234                         onUpdate => 'CASCADE',
1235                         onDelete => 'CASCADE',
1236                 },
1237                 {       key => 'invoiceprice',
1238                         foreigntable => 'currency',
1239                         foreignkey => 'currency',
1240                         onUpdate => 'CASCADE',
1241                         onDelete => 'CASCADE',
1242                 },
1243         ],
1244         aqorderbreakdown => [
1245                 {       key => 'ordernumber',
1246                         foreigntable => 'aqorders',
1247                         foreignkey => 'ordernumber',
1248                         onUpdate => 'CASCADE',
1249                         onDelete => 'CASCADE',
1250                 },
1251                 {       key => 'bookfundid',
1252                         foreigntable => 'aqbookfund',
1253                         foreignkey => 'bookfundid',
1254                         onUpdate => 'CASCADE',
1255                         onDelete => 'CASCADE',
1256                 },
1257         ],
1258         branchtransfers => [
1259                 {       key => 'frombranch',
1260                         foreigntable => 'branches',
1261                         foreignkey => 'branchcode',
1262                         onUpdate => 'CASCADE',
1263                         onDelete => 'CASCADE',
1264                 },
1265                 {       key => 'tobranch',
1266                         foreigntable => 'branches',
1267                         foreignkey => 'branchcode',
1268                         onUpdate => 'CASCADE',
1269                         onDelete => 'CASCADE',
1270                 },
1271                 {       key => 'itemnumber',
1272                         foreigntable => 'items',
1273                         foreignkey => 'itemnumber',
1274                         onUpdate => 'CASCADE',
1275                         onDelete => 'CASCADE',
1276                 },
1277         ],
1278         issuingrules => [
1279                 {       key => 'categorycode',
1280                         foreigntable => 'categories',
1281                         foreignkey => 'categorycode',
1282                         onUpdate => 'CASCADE',
1283                         onDelete => 'CASCADE',
1284                 },
1285                 {       key => 'itemtype',
1286                         foreigntable => 'itemtypes',
1287                         foreignkey => 'itemtype',
1288                         onUpdate => 'CASCADE',
1289                         onDelete => 'CASCADE',
1290                 },
1291         ],
1292         issues => [     # constraint is SET NULL : when a borrower or an item is deleted, we keep the issuing record
1293         # for stat purposes
1294                 {       key => 'borrowernumber',
1295                         foreigntable => 'borrowers',
1296                         foreignkey => 'borrowernumber',
1297                         onUpdate => 'SET NULL',
1298                         onDelete => 'SET NULL',
1299                 },
1300                 {       key => 'itemnumber',
1301                         foreigntable => 'items',
1302                         foreignkey => 'itemnumber',
1303                         onUpdate => 'SET NULL',
1304                         onDelete => 'SET NULL',
1305                 },
1306         ],
1307         reserves => [
1308                 {       key => 'borrowernumber',
1309                         foreigntable => 'borrowers',
1310                         foreignkey => 'borrowernumber',
1311                         onUpdate => 'CASCADE',
1312                         onDelete => 'CASCADE',
1313                 },
1314                 {       key => 'biblionumber',
1315                         foreigntable => 'biblio',
1316                         foreignkey => 'biblionumber',
1317                         onUpdate => 'CASCADE',
1318                         onDelete => 'CASCADE',
1319                 },
1320                 {       key => 'itemnumber',
1321                         foreigntable => 'items',
1322                         foreignkey => 'itemnumber',
1323                         onUpdate => 'CASCADE',
1324                         onDelete => 'CASCADE',
1325                 },
1326                 {       key => 'branchcode',
1327                         foreigntable => 'branches',
1328                         foreignkey => 'branchcode',
1329                         onUpdate => 'CASCADE',
1330                         onDelete => 'CASCADE',
1331                 },
1332         ],
1333         borrowers => [ # foreign keys are RESTRICT as we don't want to delete borrowers when a branch is deleted
1334         # but prevent deleting a branch as soon as it has 1 borrower !
1335                 {       key => 'categorycode',
1336                         foreigntable => 'categories',
1337                         foreignkey => 'categorycode',
1338                         onUpdate => 'RESTRICT',
1339                         onDelete => 'RESTRICT',
1340                 },
1341                 {       key => 'branchcode',
1342                         foreigntable => 'branches',
1343                         foreignkey => 'branchcode',
1344                         onUpdate => 'RESTRICT',
1345                         onDelete => 'RESTRICT',
1346                 },
1347         ],
1348         deletedborrowers => [ # foreign keys are RESTRICT as we don't want to delete borrowers when a branch is deleted
1349         # but prevent deleting a branch as soon as it has 1 borrower !
1350                 {       key => 'categorycode',
1351                         foreigntable => 'categories',
1352                         foreignkey => 'categorycode',
1353                         onUpdate => 'RESTRICT',
1354                         onDelete => 'RESTRICT',
1355                 },
1356                 {       key => 'branchcode',
1357                         foreigntable => 'branches',
1358                         foreignkey => 'branchcode',
1359                         onUpdate => 'RESTRICT',
1360                         onDelete => 'RESTRICT',
1361                 },
1362         ],
1363         accountlines => [
1364                 {       key => 'borrowernumber',
1365                         foreigntable => 'borrowers',
1366                         foreignkey => 'borrowernumber',
1367                         onUpdate => 'CASCADE',
1368                         onDelete => 'CASCADE',
1369                 },
1370                 {       key => 'itemnumber',
1371                         foreigntable => 'items',
1372                         foreignkey => 'itemnumber',
1373                         onUpdate => 'SET NULL',
1374                         onDelete => 'SET NULL',
1375                 },
1376         ],
1377         auth_tag_structure => [
1378                 {       key => 'authtypecode',
1379                         foreigntable => 'auth_types',
1380                         foreignkey => 'authtypecode',
1381                         onUpdate => 'CASCADE',
1382                         onDelete => 'CASCADE',
1383                 },
1384         ],
1385         # FIXME : don't constraint auth_*_table and auth_word, as they may be replaced by zebra
1386 );
1387
1388
1389 # column changes
1390 my %column_change = (
1391         # table
1392         borrowers => [
1393                                 {
1394                                         from => 'emailaddress',
1395                                         to => 'email',
1396                                         after => 'city',
1397                                 },
1398                                 {
1399                                         from => 'streetaddress',
1400                                         to => 'address',
1401                                         after => 'initials',
1402                                 },
1403                                 {
1404                                         from => 'faxnumber',
1405                                         to => 'fax',
1406                                         after => 'phone',
1407                                 },
1408                                 {
1409                                         from => 'textmessaging',
1410                                         to => 'opacnote',
1411                                         after => 'userid',
1412                                 },
1413                                 {
1414                                         from => 'altnotes',
1415                                         to => 'contactnote',
1416                                         after => 'opacnote',
1417                                 },
1418                                 {
1419                                         from => 'physstreet',
1420                                         to => 'B_address',
1421                                         after => 'fax',
1422                                 },
1423                                 {
1424                                         from => 'streetcity',
1425                                         to => 'B_city',
1426                                         after => 'B_address',
1427                                 },
1428                                 {
1429                                         from => 'phoneday',
1430                                         to => 'mobile',
1431                                         after => 'phone',
1432                                 },
1433                                 {
1434                                         from => 'zipcode',
1435                                         to => 'zipcode',
1436                                         after => 'city',
1437                                 },
1438                                 {
1439                                         from => 'homezipcode',
1440                                         to => 'B_zipcode',
1441                                         after => 'B_city',
1442                                 },
1443                                 {
1444                                         from => 'altphone',
1445                                         to => 'B_phone',
1446                                         after => 'B_zipcode',
1447                                 },
1448                                 {
1449                                         from => 'expiry',
1450                                         to => 'dateexpiry',
1451                                         after => 'dateenrolled',
1452                                 },
1453                                 {
1454                                         from => 'guarantor',
1455                                         to => 'guarantorid',
1456                                         after => 'contactname',
1457                                 },
1458                                 {
1459                                         from => 'textmessaging',
1460                                         to => 'opacnotes',
1461                                         after => 'flags',
1462                                 },
1463                                 {
1464                                         from => 'altnotes',
1465                                         to => 'contactnotes',
1466                                         after => 'opacnotes',
1467                                 },
1468                                 {
1469                                         from => 'altrelationship',
1470                                         to => 'relationship',
1471                                         after => 'borrowernotes',
1472                                 },
1473                         ],
1474
1475         deletedborrowers => [
1476                                 {
1477                                         from => 'emailaddress',
1478                                         to => 'email',
1479                                         after => 'city',
1480                                 },
1481                                 {
1482                                         from => 'streetaddress',
1483                                         to => 'address',
1484                                         after => 'initials',
1485                                 },
1486                                 {
1487                                         from => 'faxnumber',
1488                                         to => 'fax',
1489                                         after => 'phone',
1490                                 },
1491                                 {
1492                                         from => 'textmessaging',
1493                                         to => 'opacnote',
1494                                         after => 'userid',
1495                                 },
1496                                 {
1497                                         from => 'altnotes',
1498                                         to => 'contactnote',
1499                                         after => 'opacnote',
1500                                 },
1501                                 {
1502                                         from => 'physstreet',
1503                                         to => 'B_address',
1504                                         after => 'fax',
1505                                 },
1506                                 {
1507                                         from => 'streetcity',
1508                                         to => 'B_city',
1509                                         after => 'B_address',
1510                                 },
1511                                 {
1512                                         from => 'phoneday',
1513                                         to => 'mobile',
1514                                         after => 'phone',
1515                                 },
1516                                 {
1517                                         from => 'zipcode',
1518                                         to => 'zipcode',
1519                                         after => 'city',
1520                                 },
1521                                 {
1522                                         from => 'homezipcode',
1523                                         to => 'B_zipcode',
1524                                         after => 'B_city',
1525                                 },
1526                                 {
1527                                         from => 'altphone',
1528                                         to => 'B_phone',
1529                                         after => 'B_zipcode',
1530                                 },
1531                                 {
1532                                         from => 'expiry',
1533                                         to => 'dateexpiry',
1534                                         after => 'dateenrolled',
1535                                 },
1536                                 {
1537                                         from => 'guarantor',
1538                                         to => 'guarantorid',
1539                                         after => 'contactname',
1540                                 },
1541                                 {
1542                                         from => 'textmessaging',
1543                                         to => 'opacnotes',
1544                                         after => 'flags',
1545                                 },
1546                                 {
1547                                         from => 'altnotes',
1548                                         to => 'contactnotes',
1549                                         after => 'opacnotes',
1550                                 },
1551                                 {
1552                                         from => 'altrelationship',
1553                                         to => 'relationship',
1554                                         after => 'borrowernotes',
1555                                 },
1556                         ],
1557
1558                 );
1559                 
1560 foreach my $table (keys %column_change) {
1561         $sth = $dbh->prepare("show columns from $table");
1562         $sth->execute();
1563         undef %types;
1564         while ( ( $column, $type, $null, $key, $default, $extra ) = $sth->fetchrow )
1565         {
1566                 $types{$column}->{type} ="$type";
1567                 $types{$column}->{null} = "$null";
1568                 $types{$column}->{key} = "$key";
1569                 $types{$column}->{default} = "$default";
1570                 $types{$column}->{extra} = "$extra";
1571         }    # while
1572         my $tablerows = $column_change{$table};
1573         foreach my $row ( @$tablerows ) {
1574                 if ($types{$row->{from}}->{type}) {
1575                         print "altering $table $row->{from} to $row->{to}\n";
1576                         # ALTER TABLE `borrowers` CHANGE `faxnumber` `fax` TEXT CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL 
1577 #                       alter table `borrowers` change `faxnumber` `fax` type text  null after phone
1578                         my $sql = 
1579                                 "alter table `$table` change `$row->{from}` `$row->{to}` $types{$row->{from}}->{type} ".
1580                                 ($types{$row->{from}}->{null} eq 'YES'?" NULL":" NOT NULL").
1581                                 ($types{$row->{from}}->{default}?" default ".$types{$row->{from}}->{default}:"").
1582                                 "$types{$row->{from}}->{extra} after $row->{after} ";
1583 #                       print "$sql";
1584                         $dbh->do($sql);
1585                 }
1586         }
1587 }
1588
1589 #-------------------
1590 # Initialize
1591
1592 # Start checking
1593
1594 # Get version of MySQL database engine.
1595 my $mysqlversion = `mysqld --version`;
1596 $mysqlversion =~ /Ver (\S*) /;
1597 $mysqlversion = $1;
1598 if ( $mysqlversion ge '3.23' ) {
1599     print "Could convert to MyISAM database tables...\n" unless $silent;
1600 }
1601
1602 #---------------------------------
1603 # Tables
1604
1605 # Collect all tables into a list
1606 $sth = $dbh->prepare("show tables");
1607 $sth->execute;
1608 while ( my ($table) = $sth->fetchrow ) {
1609     $existingtables{$table} = 1;
1610 }
1611
1612 # Now add any missing tables
1613 foreach $table ( keys %requiretables ) {
1614     unless ( $existingtables{$table} ) {
1615         print "Adding $table table...\n" unless $silent;
1616         my $sth = $dbh->prepare("create table $table $requiretables{$table}");
1617         $sth->execute;
1618         if ( $sth->err ) {
1619             print "Error : $sth->errstr \n";
1620             $sth->finish;
1621         }    # if error
1622     }    # unless exists
1623 }    # foreach
1624
1625 # now drop useless tables
1626 foreach $table ( keys %dropable_table ) {
1627     if ( $existingtables{$table} ) {
1628         print "Dropping unused table $table\n" if $debug and not $silent;
1629         $dbh->do("drop table $table");
1630         if ( $dbh->err ) {
1631             print "Error : $dbh->errstr \n";
1632         }
1633     }
1634 }
1635
1636 #---------------------------------
1637 # Columns
1638
1639 foreach $table ( keys %requirefields ) {
1640     print "Check table $table\n" if $debug and not $silent;
1641     $sth = $dbh->prepare("show columns from $table");
1642     $sth->execute();
1643     undef %types;
1644     while ( ( $column, $type, $null, $key, $default, $extra ) = $sth->fetchrow )
1645     {
1646         $types{$column} = $type;
1647     }    # while
1648     foreach $column ( keys %{ $requirefields{$table} } ) {
1649         print "  Check column $column  [$types{$column}]\n"
1650           if $debug and not $silent;
1651         if ( !$types{$column} ) {
1652
1653             # column doesn't exist
1654             print "Adding $column field to $table table...\n" unless $silent;
1655             $query = "alter table $table
1656                         add column $column " . $requirefields{$table}->{$column};
1657             print "Execute: $query\n" if $debug;
1658             my $sti = $dbh->prepare($query);
1659             $sti->execute;
1660             if ( $sti->err ) {
1661                 print "**Error : $sti->errstr \n";
1662                 $sti->finish;
1663             }    # if error
1664         }    # if column
1665     }    # foreach column
1666 }    # foreach table
1667
1668 foreach $table ( keys %fielddefinitions ) {
1669         print "Check table $table\n" if $debug;
1670         $sth = $dbh->prepare("show columns from $table");
1671         $sth->execute();
1672         my $definitions;
1673         while ( ( $column, $type, $null, $key, $default, $extra ) = $sth->fetchrow )
1674         {
1675                 $definitions->{$column}->{type}    = $type;
1676                 $definitions->{$column}->{null}    = $null;
1677                 $definitions->{$column}->{null}    = 'NULL' if $null eq 'YES';
1678                 $definitions->{$column}->{key}     = $key;
1679                 $definitions->{$column}->{default} = $default;
1680                 $definitions->{$column}->{extra}   = $extra;
1681         }    # while
1682         my $fieldrow = $fielddefinitions{$table};
1683         foreach my $row (@$fieldrow) {
1684                 my $field   = $row->{field};
1685                 my $type    = $row->{type};
1686                 my $null    = $row->{null};
1687 #               $null    = 'YES' if $row->{null} eq 'NULL';
1688                 my $key     = $row->{key};
1689                 my $default = $row->{default};
1690                 my $null    = $row->{null};
1691 #               $default="''" unless $default;
1692                 my $extra   = $row->{extra};
1693                 my $def     = $definitions->{$field};
1694                 my $after       = ($row->{after}?" after ".$row->{after}:"");
1695
1696                 unless ( $type eq $def->{type}
1697                         && $null eq $def->{null}
1698                         && $key eq $def->{key}
1699                         && $extra eq $def->{extra} )
1700                 {
1701                         if ( $null eq '' ) {
1702                                 $null = 'NOT NULL';
1703                         }
1704                         if ( $key eq 'PRI' ) {
1705                                 $key = 'PRIMARY KEY';
1706                         }
1707                         unless ( $extra eq 'auto_increment' ) {
1708                                 $extra = '';
1709                         }
1710
1711                         # if it's a new column use "add", if it's an old one, use "change".
1712                         my $action;
1713                         if ($definitions->{$field}->{type}) {
1714                                 $action="change $field"
1715                         } else {
1716                                 $action="add";
1717                         }
1718 # if it's a primary key, drop the previous pk, before altering the table
1719                         my $sth;
1720                         if ($key ne 'PRIMARY KEY') {
1721                                 $sth =$dbh->prepare("alter table $table $action $field $type $null $key $extra default ? $after");
1722                         } else {
1723                                 $sth =$dbh->prepare("alter table $table drop primary key, $action $field $type $null $key $extra default ? $after");
1724                         }
1725                         $sth->execute($default);
1726                         print "  alter or create $field in $table\n" unless $silent;
1727                 }
1728         }
1729 }
1730
1731 # Populate tables with required data
1732
1733 # synch table and deletedtable.
1734 foreach my $table ( ( 'borrowers', 'items', 'biblio', 'biblioitems' ) ) {
1735     my %deletedborrowers;
1736     print "synch'ing $table\n";
1737     $sth = $dbh->prepare("show columns from deleted$table");
1738     $sth->execute;
1739     while ( my ( $column, $type, $null, $key, $default, $extra ) =
1740         $sth->fetchrow )
1741     {
1742         $deletedborrowers{$column} = 1;
1743     }
1744     $sth = $dbh->prepare("show columns from $table");
1745     $sth->execute;
1746     my $previous;
1747     while ( my ( $column, $type, $null, $key, $default, $extra ) =
1748         $sth->fetchrow )
1749     {
1750         unless ( $deletedborrowers{$column} ) {
1751             my $newcol = "alter table deleted$table add $column $type";
1752             if ( $null eq 'YES' ) {
1753                 $newcol .= " NULL ";
1754             }
1755             else {
1756                 $newcol .= " NOT NULL ";
1757             }
1758             $newcol .= "default $default" if $default;
1759             $newcol .= " after $previous" if $previous;
1760             $previous = $column;
1761             print "creating column $column\n";
1762             $dbh->do($newcol);
1763         }
1764     }
1765 }
1766
1767 foreach my $table ( keys %tabledata ) {
1768     print "Checking for data required in table $table...\n" unless $silent;
1769     my $tablerows = $tabledata{$table};
1770     foreach my $row (@$tablerows) {
1771         my $uniquefieldrequired = $row->{uniquefieldrequired};
1772         my $uniquevalue         = $row->{$uniquefieldrequired};
1773         my $forceupdate         = $row->{forceupdate};
1774         my $sth                 =
1775           $dbh->prepare(
1776 "select $uniquefieldrequired from $table where $uniquefieldrequired=?"
1777           );
1778         $sth->execute($uniquevalue);
1779                 if ($sth->rows) {
1780                         foreach my $field (keys %$forceupdate) {
1781                                 if ($forceupdate->{$field}) {
1782                                         my $sth=$dbh->prepare("update systempreferences set $field=? where $uniquefieldrequired=?");
1783                                         $sth->execute($row->{$field}, $uniquevalue);
1784                                 }
1785                 }
1786                 } else {
1787                         print "Adding row to $table: " unless $silent;
1788                         my @values;
1789                         my $fieldlist;
1790                         my $placeholders;
1791                         foreach my $field ( keys %$row ) {
1792                                 next if $field eq 'uniquefieldrequired';
1793                                 next if $field eq 'forceupdate';
1794                                 my $value = $row->{$field};
1795                                 push @values, $value;
1796                                 print "  $field => $value" unless $silent;
1797                                 $fieldlist .= "$field,";
1798                                 $placeholders .= "?,";
1799                         }
1800                         print "\n" unless $silent;
1801                         $fieldlist    =~ s/,$//;
1802                         $placeholders =~ s/,$//;
1803                         my $sth =
1804                         $dbh->prepare(
1805                                 "insert into $table ($fieldlist) values ($placeholders)");
1806                         $sth->execute(@values);
1807                 }
1808         }
1809 }
1810
1811 #
1812 # check indexes and create them when needed
1813 #
1814 print "Checking for index required...\n" unless $silent;
1815 foreach my $table ( keys %indexes ) {
1816         #
1817         # read all indexes from $table
1818         #
1819         $sth = $dbh->prepare("show index from $table");
1820         $sth->execute;
1821         my %existingindexes;
1822         while ( my ( $table, $non_unique, $key_name, $Seq_in_index, $Column_name, $Collation, $cardinality, $sub_part, $Packed, $comment ) = $sth->fetchrow ) {
1823                 $existingindexes{$key_name} = 1;
1824         }
1825         # read indexes to check
1826         my $tablerows = $indexes{$table};
1827         foreach my $row (@$tablerows) {
1828                 my $key_name=$row->{indexname};
1829                 if ($existingindexes{$key_name} eq 1) {
1830 #                       print "$key_name existing";
1831                 } else {
1832                         print "\tCreating index $key_name in $table\n";
1833                         my $sql;
1834                         if ($row->{indexname} eq 'PRIMARY') {
1835                                 $sql = "alter table $table ADD PRIMARY KEY ($row->{content})";
1836                         } else {
1837                                 $sql = "alter table $table ADD INDEX $key_name ($row->{content}) $row->{type}";
1838                         }
1839                         $dbh->do($sql);
1840             print "Error $sql : $dbh->err \n" if $dbh->err;
1841                 }
1842         }
1843 }
1844
1845 #
1846 # check foreign keys and create them when needed
1847 #
1848 print "Checking for foreign keys required...\n" unless $silent;
1849 foreach my $table ( keys %foreign_keys ) {
1850         #
1851         # read all indexes from $table
1852         #
1853         $sth = $dbh->prepare("show table status like '$table'");
1854         $sth->execute;
1855         my $stat = $sth->fetchrow_hashref;
1856         # read indexes to check
1857         my $tablerows = $foreign_keys{$table};
1858         foreach my $row (@$tablerows) {
1859                 my $foreign_table=$row->{foreigntable};
1860                 if ($stat->{'Comment'} =~/$foreign_table/) {
1861 #                       print "$foreign_table existing\n";
1862                 } else {
1863                         print "\tCreating foreign key $foreign_table in $table\n";
1864                         # first, drop any orphan value in child table
1865                         if ($row->{onDelete} ne "RESTRICT") {
1866                                 my $sql = "delete from $table where $row->{key} not in (select $row->{foreignkey} from $row->{foreigntable})";
1867                                 $dbh->do($sql);
1868                                 print "SQL ERROR: $sql : $dbh->err \n" if $dbh->err;
1869                         }
1870                         my $sql="alter table $table ADD FOREIGN KEY $row->{key} ($row->{key}) REFERENCES $row->{foreigntable} ($row->{foreignkey})";
1871                         $sql .= " on update ".$row->{onUpdate} if $row->{onUpdate};
1872                         $sql .= " on delete ".$row->{onDelete} if $row->{onDelete};
1873                         $dbh->do($sql);
1874                         if ($dbh->err) {
1875                                 print "====================
1876 An error occured during :
1877 \t$sql
1878 It probably means there is something wrong in your DB : a row ($table.$row->{key}) refers to a value in $row->{foreigntable}.$row->{foreignkey} that does not exist. solve the problem and run updater again (or just the previous SQL statement).
1879 You can find those values with select
1880 \t$table.* from $table where $row->{key} not in (select $row->{foreignkey} from $row->{foreigntable})
1881 ====================\n
1882 ";
1883                         }
1884                 }
1885         }
1886 }
1887
1888 #
1889 # SPECIFIC STUFF
1890 #
1891 #
1892 # create frameworkcode row in biblio table & fill it with marc_biblio.frameworkcode.
1893 #
1894
1895 # 1st, get how many biblio we will have to do...
1896 $sth = $dbh->prepare('select count(*) from marc_biblio');
1897 $sth->execute;
1898 my ($totaltodo) = $sth->fetchrow;
1899
1900 $sth = $dbh->prepare("show columns from biblio");
1901 $sth->execute();
1902 my $definitions;
1903 my $bibliofwexist=0;
1904 while ( ( $column, $type, $null, $key, $default, $extra ) = $sth->fetchrow ){
1905         $bibliofwexist=1 if $column eq 'frameworkcode';
1906 }
1907 unless ($bibliofwexist) {
1908         print "moving biblioframework to biblio table\n";
1909         $dbh->do('ALTER TABLE `biblio` ADD `frameworkcode` VARCHAR( 4 ) NOT NULL AFTER `biblionumber`');
1910         $sth = $dbh->prepare('select biblionumber,frameworkcode from marc_biblio');
1911         $sth->execute;
1912         my $sth_update = $dbh->prepare('update biblio set frameworkcode=? where biblionumber=?');
1913         my $totaldone=0;
1914         while (my ($biblionumber,$frameworkcode) = $sth->fetchrow) {
1915                 $sth_update->execute($frameworkcode,$biblionumber);
1916                 $totaldone++;
1917                 print "\r$totaldone / $totaltodo" unless ($totaldone % 100);
1918         }
1919         print "\rdone\n";
1920 }
1921
1922 #
1923 # moving MARC data from marc_subfield_table to biblioitems.marc
1924 #
1925 $sth = $dbh->prepare("show columns from biblioitems");
1926 $sth->execute();
1927 my $definitions;
1928 my $marcdone=0;
1929 while ( ( $column, $type, $null, $key, $default, $extra ) = $sth->fetchrow ){
1930         $marcdone=1 if ($type eq 'blob' && $column eq 'marc') ;
1931 }
1932 unless ($marcdone) {
1933         print "moving MARC record to biblioitems table\n";
1934         # changing marc field type
1935         $dbh->do('ALTER TABLE `biblioitems` CHANGE `marc` `marc` BLOB NULL DEFAULT NULL ');
1936         # adding marc xml, just for convenience
1937         $dbh->do('ALTER TABLE `biblioitems` ADD `marcxml` TEXT CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL ');
1938         # moving data from marc_subfield_value to biblio
1939         $sth = $dbh->prepare('select bibid,biblionumber from marc_biblio');
1940         $sth->execute;
1941         my $sth_update = $dbh->prepare('update biblioitems set marc=?, marcxml=? where biblionumber=?');
1942         my $totaldone=0;
1943         while (my ($bibid,$biblionumber) = $sth->fetchrow) {
1944                 my $record = MARCgetbiblio($dbh,$bibid);
1945         #Force UTF-8 in record leader
1946                 $record->encoding('UTF-8');
1947                 print $record->as_formatted if ($biblionumber==3902);
1948                 $sth_update->execute($record->as_usmarc(),$record->as_xml_record(),$biblionumber);
1949                 $totaldone++;
1950                 print "\r$totaldone / $totaltodo" unless ($totaldone % 100);
1951         }
1952         print "\rdone\n";
1953 }
1954
1955
1956 # at last, remove useless fields
1957 foreach $table ( keys %uselessfields ) {
1958     my @fields = split /,/, $uselessfields{$table};
1959     my $fields;
1960     my $exists;
1961     foreach my $fieldtodrop (@fields) {
1962         $fieldtodrop =~ s/\t//g;
1963         $fieldtodrop =~ s/\n//g;
1964         $exists = 0;
1965         $sth    = $dbh->prepare("show columns from $table");
1966         $sth->execute;
1967         while ( my ( $column, $type, $null, $key, $default, $extra ) =
1968             $sth->fetchrow )
1969         {
1970             $exists = 1 if ( $column eq $fieldtodrop );
1971         }
1972         if ($exists) {
1973             print "deleting $fieldtodrop field in $table...\n" unless $silent;
1974             my $sth = $dbh->prepare("alter table $table drop $fieldtodrop");
1975             $sth->execute;
1976         }
1977     }
1978 }    # foreach
1979
1980
1981 # MOVE all tables TO UTF-8 and innoDB
1982 $sth = $dbh->prepare("show table status");
1983 $sth->execute;
1984 while ( my $table = $sth->fetchrow_hashref ) {
1985 #       if ($table->{Engine} ne 'InnoDB') {
1986 #               $dbh->do("ALTER TABLE $table->{Name} TYPE = innodb");
1987 #               print "moving $table->{Name} to InnoDB\n";
1988 #       }
1989         unless ($table->{Collation} =~ /^utf8/) {
1990                 $dbh->do("ALTER TABLE $table->{Name} CONVERT TO CHARACTER SET utf8");
1991                 $dbh->do("ALTER TABLE $table->{Name} DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci");
1992                 # FIXME : maybe a ALTER TABLE tbl_name CONVERT TO CHARACTER SET utf8 would be better, def char set seems to work fine. If any problem encountered, let's try with convert !
1993                 print "moving $table->{Name} to utf8\n";
1994         } else {
1995         }
1996 }
1997
1998 $sth->finish;
1999
2000 #
2001 # those 2 subs are a copy of Biblio.pm, version 2.2.4
2002 # they are useful only once, for moving from 2.2 to 3.0
2003 # the MARCgetbiblio & MARCgetitem subs in Biblio.pm
2004 # are still here, but uses other tables
2005 # (the ones that are filled by updatedatabase !)
2006 #
2007
2008 sub MARCgetbiblio {
2009
2010     # Returns MARC::Record of the biblio passed in parameter.
2011     my ( $dbh, $bibid ) = @_;
2012     my $record = MARC::Record->new();
2013 #       warn "". $bidid;
2014
2015     my $sth =
2016       $dbh->prepare(
2017 "select bibid,subfieldid,tag,tagorder,tag_indicator,subfieldcode,subfieldorder,subfieldvalue,valuebloblink
2018                                  from marc_subfield_table
2019                                  where bibid=? order by tag,tagorder,subfieldorder
2020                          "
2021     );
2022     my $sth2 =
2023       $dbh->prepare(
2024         "select subfieldvalue from marc_blob_subfield where blobidlink=?");
2025     $sth->execute($bibid);
2026     my $prevtagorder = 1;
2027     my $prevtag      = 'XXX';
2028     my $previndicator;
2029     my $field;        # for >=10 tags
2030     my $prevvalue;    # for <10 tags
2031     while ( my $row = $sth->fetchrow_hashref ) {
2032
2033         if ( $row->{'valuebloblink'} ) {    #---- search blob if there is one
2034             $sth2->execute( $row->{'valuebloblink'} );
2035             my $row2 = $sth2->fetchrow_hashref;
2036             $sth2->finish;
2037             $row->{'subfieldvalue'} = $row2->{'subfieldvalue'};
2038         }
2039         if ( $row->{tagorder} ne $prevtagorder || $row->{tag} ne $prevtag ) {
2040             $previndicator .= "  ";
2041             if ( $prevtag < 10 ) {
2042                                 if ($prevtag ne '000') {
2043                         $record->add_fields( ( sprintf "%03s", $prevtag ), $prevvalue ) unless $prevtag eq "XXX";    # ignore the 1st loop
2044                                 } else {
2045                                         $record->leader(sprintf("%24s",$prevvalue));
2046                                 }
2047             }
2048             else {
2049                 $record->add_fields($field) unless $prevtag eq "XXX";
2050             }
2051             undef $field;
2052             $prevtagorder  = $row->{tagorder};
2053             $prevtag       = $row->{tag};
2054             $previndicator = $row->{tag_indicator};
2055             if ( $row->{tag} < 10 ) {
2056                 $prevvalue = $row->{subfieldvalue};
2057             }
2058             else {
2059                 $field = MARC::Field->new(
2060                     ( sprintf "%03s", $prevtag ),
2061                     substr( $row->{tag_indicator} . '  ', 0, 1 ),
2062                     substr( $row->{tag_indicator} . '  ', 1, 1 ),
2063                     $row->{'subfieldcode'},
2064                     $row->{'subfieldvalue'}
2065                 );
2066             }
2067         }
2068         else {
2069             if ( $row->{tag} < 10 ) {
2070                 $record->add_fields( ( sprintf "%03s", $row->{tag} ),
2071                     $row->{'subfieldvalue'} );
2072             }
2073             else {
2074                 $field->add_subfields( $row->{'subfieldcode'},
2075                     $row->{'subfieldvalue'} );
2076             }
2077             $prevtag       = $row->{tag};
2078             $previndicator = $row->{tag_indicator};
2079         }
2080     }
2081
2082     # the last has not been included inside the loop... do it now !
2083     if ( $prevtag ne "XXX" )
2084     { # check that we have found something. Otherwise, prevtag is still XXX and we
2085          # must return an empty record, not make MARC::Record fail because we try to
2086          # create a record with XXX as field :-(
2087         if ( $prevtag < 10 ) {
2088             $record->add_fields( $prevtag, $prevvalue );
2089         }
2090         else {
2091
2092             #           my $field = MARC::Field->new( $prevtag, "", "", %subfieldlist);
2093             $record->add_fields($field);
2094         }
2095     }
2096     return $record;
2097 }
2098
2099 sub MARCgetitem {
2100
2101     # Returns MARC::Record of the biblio passed in parameter.
2102     my ( $dbh, $bibid, $itemnumber ) = @_;
2103     my $record = MARC::Record->new();
2104
2105     # search MARC tagorder
2106     my $sth2 =
2107       $dbh->prepare(
2108 "select tagorder from marc_subfield_table,marc_subfield_structure where marc_subfield_table.tag=marc_subfield_structure.tagfield and marc_subfield_table.subfieldcode=marc_subfield_structure.tagsubfield and bibid=? and kohafield='items.itemnumber' and subfieldvalue=?"
2109     );
2110     $sth2->execute( $bibid, $itemnumber );
2111     my ($tagorder) = $sth2->fetchrow_array();
2112
2113     #---- TODO : the leader is missing
2114     my $sth =
2115       $dbh->prepare(
2116 "select bibid,subfieldid,tag,tagorder,tag_indicator,subfieldcode,subfieldorder,subfieldvalue,valuebloblink
2117                                  from marc_subfield_table
2118                                  where bibid=? and tagorder=? order by subfieldcode,subfieldorder
2119                          "
2120     );
2121     $sth2 =
2122       $dbh->prepare(
2123         "select subfieldvalue from marc_blob_subfield where blobidlink=?");
2124     $sth->execute( $bibid, $tagorder );
2125     while ( my $row = $sth->fetchrow_hashref ) {
2126         if ( $row->{'valuebloblink'} ) {    #---- search blob if there is one
2127             $sth2->execute( $row->{'valuebloblink'} );
2128             my $row2 = $sth2->fetchrow_hashref;
2129             $sth2->finish;
2130             $row->{'subfieldvalue'} = $row2->{'subfieldvalue'};
2131         }
2132         if ( $record->field( $row->{'tag'} ) ) {
2133             my $field;
2134
2135 #--- this test must stay as this, because of strange behaviour of mySQL/Perl DBI with char var containing a number...
2136             #--- sometimes, eliminates 0 at beginning, sometimes no ;-\\\
2137             if ( length( $row->{'tag'} ) < 3 ) {
2138                 $row->{'tag'} = "0" . $row->{'tag'};
2139             }
2140             $field = $record->field( $row->{'tag'} );
2141             if ($field) {
2142                 my $x =
2143                   $field->add_subfields( $row->{'subfieldcode'},
2144                     $row->{'subfieldvalue'} );
2145                 $record->delete_field($field);
2146                 $record->add_fields($field);
2147             }
2148         }
2149         else {
2150             if ( length( $row->{'tag'} ) < 3 ) {
2151                 $row->{'tag'} = "0" . $row->{'tag'};
2152             }
2153             my $temp =
2154               MARC::Field->new( $row->{'tag'}, " ", " ",
2155                 $row->{'subfieldcode'} => $row->{'subfieldvalue'} );
2156             $record->add_fields($temp);
2157         }
2158
2159     }
2160     return $record;
2161 }
2162
2163
2164 exit;
2165
2166 # $Log$
2167 # Revision 1.154  2006/07/17 12:28:45  toins
2168 # sync with rel_2_2
2169 #
2170 # Revision 1.153  2006/07/04 14:36:52  toins
2171 # Head & rel_2_2 merged
2172 #
2173 # Revision 1.152  2006/06/27 09:26:37  btoumi
2174 # modify (initials,phone ) fields property in borrowers and deletedborrowers table
2175 #
2176 # Revision 1.151  2006/06/22 10:33:14  btoumi
2177 # sorry i forget deletedborrowers table
2178 # modify firstname field from deletedborrowers table
2179 #
2180 # Revision 1.149  2006/06/20 22:35:47  rangi
2181 # Code to allow the associated borrowers to work
2182 #
2183 # Revision 1.148  2006/06/17 22:12:01  rangi
2184 # Adding id field to reviews table
2185 #
2186 # Revision 1.147  2006/06/17 03:36:41  rangi
2187 # Table definition for the reviews table
2188 #
2189 # Revision 1.146  2006/06/17 03:29:41  rangi
2190 # Variable to allow librarians to switch reviews on or off
2191 #
2192 # Revision 1.145  2006/06/16 09:45:02  btoumi
2193 # updatedatabase.pl: add change of borrowers table to deletedborrowers table
2194 # deletemem.pl: delete use of warn function
2195 #
2196 # Revision 1.144  2006/06/08 15:36:31  alaurin
2197 # Add a new system preference 'AutomaticItemReturn' :
2198 #
2199 # if this prefence is switched on: the document returned in another library than homebranch, the system automaticly transfer the document to his homebranch (with notification for librarian in returns.tmpl) .
2200 #
2201 # switch off : the document stay in the holdingbranch ...
2202 #
2203 # correcting bugs :
2204 # - comment C4::acquisition (not using in request.pl).
2205 # - correcting date in request.pl
2206 # -add the new call of function getbranches in request.pl
2207 #
2208 # Revision 1.143  2006/06/07 02:02:47  bob_lyon
2209 # merging katipo changes...
2210 #
2211 # adding new preference IssuingInProcess
2212 #
2213 # Revision 1.142  2006/06/06 23:42:46  bob_lyon
2214 # Merging Katipo changes...
2215 #
2216 # Adding new system pref where one can still retrieve a correct reading
2217 # record history if one has moved older data from issues to oldissues table
2218 # to speed up issues speed
2219 #
2220 # Revision 1.141  2006/06/01 03:18:11  rangi
2221 # Adding a new column to the statistics table
2222 #
2223 # Revision 1.140  2006/05/22 22:40:45  rangi
2224 # Adding new systempreference allowing for the library to add borrowers to institutions (rest homes, parishes, schools, classes etc).
2225 #
2226 # Revision 1.139  2006/05/19 19:31:29  tgarip1957
2227 # Added new fields to auth_header and auth_subfield_table to allow ZEBRA use of authorities and new MARC framework like structure.
2228 # Authority tables are modified to be compatible with new MARC frameworks. This change is part of Authority Linking & Zebra authorities. Requires change in Mysql database. It will break head unless all changes regarding this is implemented. This warning will take place on all commits regarding this
2229 #
2230 # Revision 1.138  2006/05/19 16:51:44  alaurin
2231 # update database for :
2232 # - new feature ip and printer management
2233 # adding two fields in branches table (branchip,branchprinter)
2234 #
2235 # - waiting date : adding one field in reserves table(waiting date) to calculate the Maximum delay to pick up a reserved document when it's available
2236 #
2237 # new system preference :
2238 # - ReservesMaxPickUpDelay : Maximum delay to pick up a reserved document
2239 # TransfersMaxDaysWarning : Max delay before considering the transfer as potentialy a problem
2240 #
2241 # Revision 1.137  2006/04/18 09:36:36  plg
2242 # bug fixed: typo fixed in labels and labels_conf tables creation query.
2243 #
2244 # Revision 1.136  2006/04/17 21:55:33  sushi
2245 # Added 'labels' and 'labels_conf' tables, for spine lable tool.
2246 #
2247 # Revision 1.135  2006/04/15 02:37:03  tgarip1957
2248 # Marc record should be set to UTF-8 in leader.Force it.
2249 # XML should be with<record> wrappers
2250 #
2251 # Revision 1.134  2006/04/14 09:37:29  tipaul
2252 # improvements from SAN Ouest Provence :
2253 # * introducing a category_type into categories. It can be A (adult), C (children), P (Professionnal), I (institution/organisation).
2254 # * each category_type has it's own forms to create members.
2255 # * the borrowers table has been heavily modified (many fields changed), to get something more logic & readable
2256 # * reintroducing guarantor/guanrantee system that is now independant from hardcoded C/A for categories
2257 # * updating templates to fit template rules
2258 #
2259 # (see mail feb, 17 on koha-devel "new features for borrowers" for more details)
2260 #
2261 # Revision 1.133  2006/04/13 08:36:42  plg
2262 # new: function C4::Date::get_date_format_string_for_DHTMLcalendar based on
2263 # the system preference prefered date format.
2264 #
2265 # improvement: book fund list and budget list screen redesigned. Filters on
2266 # each field. Columns are not sortable yet. Using DHTML Calendar to fill date
2267 # fields instead of manual filling. Pagination system. From the book fund
2268 # list, you can reach the budget list, filtered on a book fund, or not. A
2269 # budget can be added only from book fund list screen.
2270 #
2271 # bug fixed: branchcode was missing in table aqbudget.
2272 #
2273 # bug fixed: when setting a branchcode to a book fund, all associated budgets
2274 # move to this branchcode.
2275 #
2276 # modification: when adding/modifying budget/fund, MySQL specific "REPLACE..."
2277 # statements replaced by standard SQL compliant statement.
2278 #
2279 # bug fixed: when adding/modifying a budget, if the book fund is associated to
2280 # a branch, the branch selection is disabled and set to the book fund branch.
2281 #
2282 # Revision 1.132  2006/04/06 12:37:05  hdl
2283 # Bugfixing : aqbookfund needed a field.
2284 #
2285 # Revision 1.131  2006/03/03 17:02:22  tipaul
2286 # commit for holidays and news management.
2287 # (some forgotten files)
2288 #
2289 # Revision 1.130  2006/03/03 16:35:21  tipaul
2290 # commit for holidays and news management.
2291 #
2292 # Contrib from Tümer Garip (from Turkey) :
2293 # * holiday :
2294 # in /tools/ the holiday.pl script let you define holidays (days where the library is closed), branch by branch. You can define 3 types of holidays :
2295 # - single day : only this day is closed
2296 # - repet weekly (like "sunday") : the day is holiday every week
2297 # - repet yearly (like "July, 4") : this day is closed every year.
2298 #
2299 # You can also put exception :
2300 # - sunday is holiday, but "2006 March, 5th" the library will be open
2301 #
2302 # The holidays are used for return date calculation : the return date is set to the next date where the library is open. A systempreference (useDaysMode) set ON (Calendar) or OFF (Normal) the calendar calculation.
2303 #
2304 # Revision 1.129  2006/02/27 18:19:33  hdl
2305 # New table used in overduerules.pl tools page.
2306 #
2307 # Revision 1.128  2006/01/25 15:16:06  tipaul
2308 # updating DB :
2309 # * removing useless tables
2310 # * adding useful indexes
2311 # * altering some columns definitions
2312 # * The goal being to have updater working fine for foreign keys.
2313 #
2314 # For me it's done, let me know if it works for you. You can see an updated schema of the DB (with constraints) on the wiki
2315 #
2316 # Revision 1.127  2006/01/24 17:57:17  tipaul
2317 # DB improvements : adding foreign keys on some tables. partial stuff done.
2318 #
2319 # Revision 1.126  2006/01/06 16:39:42  tipaul
2320 # synch'ing head and rel_2_2 (from 2.2.5, including npl templates)
2321 # Seems not to break too many things, but i'm probably wrong here.
2322 # at least, new features/bugfixes from 2.2.5 are here (tested on some features on my head local copy)
2323 #
2324 # - removing useless directories (koha-html and koha-plucene)
2325 #
2326 # Revision 1.125  2006/01/04 15:54:55  tipaul
2327 # utf8 is a : go for beta test in HEAD.
2328 # some explanations :
2329 # - updater/updatedatabase => will transform all tables in innoDB (not related to utf8, just to warn you) AND collate them in utf8 / utf8_general_ci. The SQL command is : ALTER TABLE tablename DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci.
2330 # - *-top.inc will show the pages in utf8
2331 # - THE HARD THING : for me, mysql-client and mysql-server were set up to communicate in iso8859-1, whatever the mysql collation ! Thus, pages were improperly shown, as datas were transmitted in iso8859-1 format ! After a full day of investigation, someone on usenet pointed "set NAMES 'utf8'" to explain that I wanted utf8. I could put this in my.cnf, but if I do that, ALL databases will "speak" in utf8, that's not what we want. Thus, I added a line in Context.pm : everytime a DB handle is opened, the communication is set to utf8.
2332 # - using marcxml field and no more the iso2709 raw marc biblioitems.marc field.
2333 #
2334 # Revision 1.124  2005/10/27 12:09:05  tipaul
2335 # new features for serial module :
2336 # - the last 5 issues are now shown, and their status can be changed (but not reverted to "waited", as there can be only one "waited")
2337 # - the library can create a "distribution list". this paper contains a list of borrowers (selected from the borrower list, or manually entered), and print it for a given issue. once printed, the sheet can be put on the issue and distributed to every reader on the list (one by one).
2338 #
2339 # Revision 1.123  2005/10/26 09:13:37  tipaul
2340 # big commit, still breaking things...
2341 #
2342 # * synch with rel_2_2. Probably the last non manual synch, as rel_2_2 should not be modified deeply.
2343 # * code cleaning (cleaning warnings from perl -w) continued
2344 #
2345 # Revision 1.122  2005/09/02 14:18:38  tipaul
2346 # new feature : image for itemtypes.
2347 #
2348 # * run updater/updatedatabase to create imageurl field in itemtypes.
2349 # * go to Koha >> parameters >> itemtypes >> modify (or add) an itemtype. You will see around 20 nice images to choose between (thanks to owen). If you prefer your own image, you also can type a complete url (http://www.myserver.lib/path/to/my/image.gif)
2350 # * go to OPAC, and search something. In the result list, you now have the picture instead of the text itemtype.
2351 #
2352 # Revision 1.121  2005/08/24 08:49:03  hdl
2353 # Adding a note field in serial table.
2354 # This will allow librarian to mention a note on a peculiar waiting serial number.
2355 #
2356 # Revision 1.120  2005/08/09 14:10:32  tipaul
2357 # 1st commit to go to zebra.
2358 # don't update your cvs if you want to have a working head...
2359 #
2360 # this commit contains :
2361 # * updater/updatedatabase : get rid with marc_* tables, but DON'T remove them. As a lot of things uses them, it would not be a good idea for instance to drop them. If you really want to play, you can rename them to test head without them but being still able to reintroduce them...
2362 # * Biblio.pm : modify MARCgetbiblio to find the raw marc record in biblioitems.marc field, not from marc_subfield_table, modify MARCfindframeworkcode to find frameworkcode in biblio.frameworkcode, modify some other subs to use biblio.biblionumber & get rid of bibid.
2363 # * other files : get rid of bibid and use biblionumber instead.
2364 #
2365 # What is broken :
2366 # * does not do anything on zebra yet.
2367 # * if you rename marc_subfield_table, you can't search anymore.
2368 # * you can view a biblio & bibliodetails, go to MARC editor, but NOT save any modif.
2369 # * don't try to add a biblio, it would add data poorly... (don't try to delete either, it may work, but that would be a surprise ;-) )
2370 #
2371 # IMPORTANT NOTE : you need MARC::XML package (http://search.cpan.org/~esummers/MARC-XML-0.7/lib/MARC/File/XML.pm), that requires a recent version of MARC::Record
2372 # Updatedatabase stores the iso2709 data in biblioitems.marc field & an xml version in biblioitems.marcxml Not sure we will keep it when releasing the stable version, but I think it's a good idea to have something readable in sql, at least for development stage.
2373 #
2374 # Revision 1.119  2005/08/04 16:07:58  tipaul
2375 # Synch really broke this script...
2376 #
2377 # Revision 1.118  2005/08/04 16:02:55  tipaul
2378 # oops... error in synch between 2.2 and head
2379 #
2380 # Revision 1.117  2005/08/04 14:24:39  tipaul
2381 # synch'ing 2.2 and head
2382 #
2383 # Revision 1.116  2005/08/04 08:55:54  tipaul
2384 # Letters / alert system, continuing...
2385 #
2386 # * adding a package Letters.pm, that manages Letters & alerts.
2387 # * adding feature : it's now possible to define a "letter" for any subscription created. If a letter is defined, users in OPAC can put an alert on the subscription. When an issue is marked "arrived", all users in the alert will recieve a mail (as defined in the "letter"). This last part (= send the mail) is not yet developped. (Should be done this week)
2388 # * adding feature : it's now possible to "put to an alert" in OPAC, for any serial subscription. The alert is stored in a new table, called alert. An alert can be put only if the librarian has activated them in subscription (and they activate it just by choosing a "letter" to sent to borrowers on new issues)
2389 # * adding feature : librarian can see in borrower detail which alerts they have put, and a user can see in opac-detail which alert they have put too.
2390 #
2391 # Note that the system should be generic enough to manage any type of alert.
2392 # I plan to extend it soon to virtual shelves : a borrower will be able to put an alert on a virtual shelf, to be warned when something is changed in the virtual shelf (mail being sent once a day by cron, or manually by the shelf owner. Anyway, a mail won't be sent on every change, users would be spammed by Koha ;-) )
2393 #
2394 # Revision 1.115  2005/08/02 16:15:34  tipaul
2395 # adding 2 fields to letter system :
2396 # * module (acquisition, catalogue...) : it will be usefull to show the librarian only letters he may be interested by.
2397 # * title, that will be used as mail subject.
2398 #
2399 # Revision 1.114  2005/07/28 15:10:13  tipaul
2400 # Introducing new "Letters" system : Letters will be used everytime you want to sent something to someone (through mail or paper). For example, sending a mail for overdues use letter that you can put as parameters. Sending a mail to a borrower when a suggestion is validated uses a letter too.
2401 # the letter table contains 3 fields :
2402 # * code => the code of the letter
2403 # * name => the complete name of the letter
2404 # * content => the complete text. It's a TEXT field type, so has no limits.
2405 #
2406 # My next goal now is to work on point 2-I "serial issue alert"
2407 # With this feature, in serials, a user can subscribe the "issue alert". For every issue arrived/missing, a mail is sent to all subscribers of this list. The mail warns the user that the issue is arrive or missing. Will be in head.
2408 # (see mail on koha-devel, 2005/04/07)
2409 #
2410 # The "serial issue alert" will be the 1st to use this letter system that probably needs some tweaking ;-)
2411 #
2412 # Once it will be stabilised default letters (in any languages) could be added during installer to help the library begin with this new feature.
2413 #
2414 # Revision 1.113  2005/07/28 08:38:41  tipaul
2415 # For instance, the return date does not rely on the borrower expiration date. A systempref will be added in Koha, to modify return date calculation schema :
2416 # * ReturnBeforeExpiry = yes => return date can't be after expiry date
2417 # * ReturnBeforeExpiry = no  => return date can be after expiry date
2418 #
2419 # Revision 1.112  2005/07/26 08:19:47  hdl
2420 # Adding IndependantBranches System preference variable in order to manage Branch independancy.
2421 #
2422 # Revision 1.111  2005/07/25 15:35:38  tipaul
2423 # we have decided that moving to Koha 3.0 requires being already in Koha 2.2.x
2424 # So, the updatedatabase script can highly be cleaned (90% removed).
2425 # Let's play with the new Koha DB structure now ;-)
2426 #