merging katipo changes
[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   => 'id',
854             type    => 'int(11)',
855             null    => 'NOT NULL',
856             key     => '',
857             default => '',
858             extra   => 'auto_increment',
859 },
860 {
861             field   => 'listprice',
862             type    => 'varchar(10)',
863             null    => 'NULL',
864             key     => '',
865             default => '',
866             extra   => '',
867 },
868 {
869                         field   => 'invoiceprice',
870                         type    => 'varchar(10)',
871                         null    => 'NULL',
872                         key             => '',
873                         default => '',
874                         extra   => '',
875                 },
876         ],
877         issues =>  [
878                 {
879                         field   => 'borrowernumber',
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                         field   => 'itemnumber',
888                         type    => 'int(11)',
889                         null    => 'NULL', # can be null when a borrower is deleted and the foreign key rule executed
890                         key             => '',
891                         default => '',
892                         extra   => '',
893                 },
894         ],
895         borrowers => [
896                 {       field => 'firstname',
897                         type => 'text',
898                         null => 'NULL',
899                  },
900                 {       field => 'initials',
901                         type => 'text',
902                         null => 'NULL',
903                  },
904                 {       field => 'B_email',
905                         type => 'text',
906                         null => 'NULL',
907                         after => 'B_zipcode',
908                  },
909                  {
910                         field => 'streetnumber', # street number (hidden if streettable table is empty)
911                         type => 'char(10)',
912                         null => 'NULL',
913                         after => 'initials',
914                 },
915                 {
916                         field => 'streettype', # street table, list builded from a system table
917                         type => 'char(50)',
918                         null => 'NULL',
919                         after => 'streetnumber',
920                 },
921                 {       field => 'phone',
922                         type => 'text',
923                         null => 'NULL',
924                  },                             
925                 {
926                         field => 'B_streetnumber', # street number (hidden if streettable table is empty)
927                         type => 'char(10)',
928                         null => 'NULL',
929                         after => 'fax',
930                 },
931                 {
932                         field => 'B_streettype', # street table, list builded from a system table
933                         type => 'char(50)',
934                         null => 'NULL',
935                         after => 'B_streetnumber',
936                 },
937                 {
938                         field => 'phonepro',
939                         type => 'text',
940                         null => 'NULL',
941                         after => 'fax',
942                 },
943                 {
944                         field => 'address2', # complement address
945                         type => 'text',
946                         null => 'NULL',
947                         after => 'address',
948                 },
949                 {
950                         field => 'emailpro',
951                         type => 'text',
952                         null => 'NULL',
953                         after => 'fax',
954                 },
955                 {
956                         field => 'contactfirstname', # contact's firstname
957                         type => 'text',
958                         null => 'NULL',
959                         after => 'contactname',
960                 },
961                 {
962                         field => 'contacttitle', # contact's title
963                         type => 'text',
964                         null => 'NULL',
965                         after => 'contactfirstname',
966                 },
967         ],
968         
969         deletedborrowers => [
970                 {       field => 'firstname',
971                         type => 'text',
972                         null => 'NULL',
973                  },
974                 {       field => 'initials',
975                         type => 'text',
976                         null => 'NULL',
977                  },
978                 {       field => 'B_email',
979                         type => 'text',
980                         null => 'NULL',
981                         after => 'B_zipcode',
982                  },
983                  {
984                         field => 'streetnumber', # street number (hidden if streettable table is empty)
985                         type => 'char(10)',
986                         null => 'NULL',
987                         after => 'initials',
988                 },
989                 {
990                         field => 'streettype', # street table, list builded from a system table
991                         type => 'char(50)',
992                         null => 'NULL',
993                         after => 'streetnumber',
994                 },
995                 {       field => 'phone',
996                         type => 'text',
997                         null => 'NULL',
998                  },             
999                  {
1000                         field => 'B_streetnumber', # street number (hidden if streettable table is empty)
1001                         type => 'char(10)',
1002                         null => 'NULL',
1003                         after => 'fax',
1004                 },
1005                 {
1006                         field => 'B_streettype', # street table, list builded from a system table
1007                         type => 'char(50)',
1008                         null => 'NULL',
1009                         after => 'B_streetnumber',
1010                 },
1011                 {
1012                         field => 'phonepro',
1013                         type => 'text',
1014                         null => 'NULL',
1015                         after => 'fax',
1016                 },
1017                 {
1018                         field => 'address2', # complement address
1019                         type => 'text',
1020                         null => 'NULL',
1021                         after => 'address',
1022                 },
1023                 {
1024                         field => 'emailpro',
1025                         type => 'text',
1026                         null => 'NULL',
1027                         after => 'fax',
1028                 },
1029                 {
1030                         field => 'contactfirstname', # contact's firstname
1031                         type => 'text',
1032                         null => 'NULL',
1033                         after => 'contactname',
1034                 },
1035                 {
1036                         field => 'contacttitle', # contact's title
1037                         type => 'text',
1038                         null => 'NULL',
1039                         after => 'contactfirstname',
1040                 },
1041         ],
1042         
1043         branches =>  [
1044                 {
1045                         field   => 'branchip',
1046                         type    => 'varchar(15)',
1047                         null    => 'NULL',
1048                         key             => '',
1049                         default => '',
1050                         extra   => '',
1051                 },
1052                 {
1053                         field   => 'branchprinter',
1054                         type    => 'varchar(100)',
1055                         null    => 'NULL',
1056                         key             => '',
1057                         default => '',
1058                         extra   => '',
1059                 },
1060         ],
1061         categories =>  [
1062                 {
1063                         field   => 'category_type',
1064                         type    => 'char(1)',
1065                         null    => 'NOT NULL',
1066                         key             => '',
1067                         default => 'A',
1068                         extra   => '',
1069                 },
1070         ],
1071         reserves =>  [
1072                 {
1073                         field   => 'waitingdate',
1074                         type    => 'date',
1075                         null    => 'NULL',
1076                         key             => '',
1077                         default => '',
1078                         extra   => '',
1079                 },
1080         ],
1081 );
1082
1083 my %indexes = (
1084 #       table => [
1085 #               {       indexname => 'index detail'
1086 #               }
1087 #       ],
1088         shelfcontents => [
1089                 {       indexname => 'shelfnumber',
1090                         content => 'shelfnumber',
1091                 },
1092                 {       indexname => 'itemnumber',
1093                         content => 'itemnumber',
1094                 }
1095         ],
1096         bibliosubject => [
1097                 {       indexname => 'biblionumber',
1098                         content => 'biblionumber',
1099                 }
1100         ],
1101         items => [
1102                 {       indexname => 'homebranch',
1103                         content => 'homebranch',
1104                 },
1105                 {       indexname => 'holdingbranch',
1106                         content => 'holdingbranch',
1107                 }
1108         ],
1109         aqbooksellers => [
1110                 {       indexname => 'PRIMARY',
1111                         content => 'id',
1112                         type => 'PRIMARY',
1113                 }
1114         ],
1115         aqbasket => [
1116                 {       indexname => 'booksellerid',
1117                         content => 'booksellerid',
1118                 },
1119         ],
1120         aqorders => [
1121                 {       indexname => 'basketno',
1122                         content => 'basketno',
1123                 },
1124         ],
1125         aqorderbreakdown => [
1126                 {       indexname => 'ordernumber',
1127                         content => 'ordernumber',
1128                 },
1129                 {       indexname => 'bookfundid',
1130                         content => 'bookfundid',
1131                 },
1132         ],
1133         currency => [
1134                 {       indexname => 'PRIMARY',
1135                         content => 'currency',
1136                         type => 'PRIMARY',
1137                 }
1138         ],
1139 );
1140
1141 my %foreign_keys = (
1142 #       table => [
1143 #               {       key => 'the key in table' (must be indexed)
1144 #                       foreigntable => 'the foreigntable name', # (the parent)
1145 #                       foreignkey => 'the foreign key column(s)' # (in the parent)
1146 #                       onUpdate => 'CASCADE|SET NULL|NO ACTION| RESTRICT',
1147 #                       onDelete => 'CASCADE|SET NULL|NO ACTION| RESTRICT',
1148 #               }
1149 #       ],
1150         shelfcontents => [
1151                 {       key => 'shelfnumber',
1152                         foreigntable => 'bookshelf',
1153                         foreignkey => 'shelfnumber',
1154                         onUpdate => 'CASCADE',
1155                         onDelete => 'CASCADE',
1156                 },
1157                 {       key => 'itemnumber',
1158                         foreigntable => 'items',
1159                         foreignkey => 'itemnumber',
1160                         onUpdate => 'CASCADE',
1161                         onDelete => 'CASCADE',
1162                 },
1163         ],
1164         # onDelete is RESTRICT on reference tables (branches, itemtype) as we don't want items to be 
1165         # easily deleted, but branches/itemtype not too easy to empty...
1166         biblioitems => [
1167                 {       key => 'biblionumber',
1168                         foreigntable => 'biblio',
1169                         foreignkey => 'biblionumber',
1170                         onUpdate => 'CASCADE',
1171                         onDelete => 'CASCADE',
1172                 },
1173                 {       key => 'itemtype',
1174                         foreigntable => 'itemtypes',
1175                         foreignkey => 'itemtype',
1176                         onUpdate => 'CASCADE',
1177                         onDelete => 'RESTRICT',
1178                 },
1179         ],
1180         items => [
1181                 {       key => 'biblioitemnumber',
1182                         foreigntable => 'biblioitems',
1183                         foreignkey => 'biblioitemnumber',
1184                         onUpdate => 'CASCADE',
1185                         onDelete => 'CASCADE',
1186                 },
1187                 {       key => 'homebranch',
1188                         foreigntable => 'branches',
1189                         foreignkey => 'branchcode',
1190                         onUpdate => 'CASCADE',
1191                         onDelete => 'RESTRICT',
1192                 },
1193                 {       key => 'holdingbranch',
1194                         foreigntable => 'branches',
1195                         foreignkey => 'branchcode',
1196                         onUpdate => 'CASCADE',
1197                         onDelete => 'RESTRICT',
1198                 },
1199         ],
1200         additionalauthors => [
1201                 {       key => 'biblionumber',
1202                         foreigntable => 'biblio',
1203                         foreignkey => 'biblionumber',
1204                         onUpdate => 'CASCADE',
1205                         onDelete => 'CASCADE',
1206                 },
1207         ],
1208         bibliosubject => [
1209                 {       key => 'biblionumber',
1210                         foreigntable => 'biblio',
1211                         foreignkey => 'biblionumber',
1212                         onUpdate => 'CASCADE',
1213                         onDelete => 'CASCADE',
1214                 },
1215         ],
1216         aqbasket => [
1217                 {       key => 'booksellerid',
1218                         foreigntable => 'aqbooksellers',
1219                         foreignkey => 'id',
1220                         onUpdate => 'CASCADE',
1221                         onDelete => 'RESTRICT',
1222                 },
1223         ],
1224         aqorders => [
1225                 {       key => 'basketno',
1226                         foreigntable => 'aqbasket',
1227                         foreignkey => 'basketno',
1228                         onUpdate => 'CASCADE',
1229                         onDelete => 'CASCADE',
1230                 },
1231                 {       key => 'biblionumber',
1232                         foreigntable => 'biblio',
1233                         foreignkey => 'biblionumber',
1234                         onUpdate => 'SET NULL',
1235                         onDelete => 'SET NULL',
1236                 },
1237         ],
1238         aqbooksellers => [
1239                 {       key => 'listprice',
1240                         foreigntable => 'currency',
1241                         foreignkey => 'currency',
1242                         onUpdate => 'CASCADE',
1243                         onDelete => 'CASCADE',
1244                 },
1245                 {       key => 'invoiceprice',
1246                         foreigntable => 'currency',
1247                         foreignkey => 'currency',
1248                         onUpdate => 'CASCADE',
1249                         onDelete => 'CASCADE',
1250                 },
1251         ],
1252         aqorderbreakdown => [
1253                 {       key => 'ordernumber',
1254                         foreigntable => 'aqorders',
1255                         foreignkey => 'ordernumber',
1256                         onUpdate => 'CASCADE',
1257                         onDelete => 'CASCADE',
1258                 },
1259                 {       key => 'bookfundid',
1260                         foreigntable => 'aqbookfund',
1261                         foreignkey => 'bookfundid',
1262                         onUpdate => 'CASCADE',
1263                         onDelete => 'CASCADE',
1264                 },
1265         ],
1266         branchtransfers => [
1267                 {       key => 'frombranch',
1268                         foreigntable => 'branches',
1269                         foreignkey => 'branchcode',
1270                         onUpdate => 'CASCADE',
1271                         onDelete => 'CASCADE',
1272                 },
1273                 {       key => 'tobranch',
1274                         foreigntable => 'branches',
1275                         foreignkey => 'branchcode',
1276                         onUpdate => 'CASCADE',
1277                         onDelete => 'CASCADE',
1278                 },
1279                 {       key => 'itemnumber',
1280                         foreigntable => 'items',
1281                         foreignkey => 'itemnumber',
1282                         onUpdate => 'CASCADE',
1283                         onDelete => 'CASCADE',
1284                 },
1285         ],
1286         issuingrules => [
1287                 {       key => 'categorycode',
1288                         foreigntable => 'categories',
1289                         foreignkey => 'categorycode',
1290                         onUpdate => 'CASCADE',
1291                         onDelete => 'CASCADE',
1292                 },
1293                 {       key => 'itemtype',
1294                         foreigntable => 'itemtypes',
1295                         foreignkey => 'itemtype',
1296                         onUpdate => 'CASCADE',
1297                         onDelete => 'CASCADE',
1298                 },
1299         ],
1300         issues => [     # constraint is SET NULL : when a borrower or an item is deleted, we keep the issuing record
1301         # for stat purposes
1302                 {       key => 'borrowernumber',
1303                         foreigntable => 'borrowers',
1304                         foreignkey => 'borrowernumber',
1305                         onUpdate => 'SET NULL',
1306                         onDelete => 'SET NULL',
1307                 },
1308                 {       key => 'itemnumber',
1309                         foreigntable => 'items',
1310                         foreignkey => 'itemnumber',
1311                         onUpdate => 'SET NULL',
1312                         onDelete => 'SET NULL',
1313                 },
1314         ],
1315         reserves => [
1316                 {       key => 'borrowernumber',
1317                         foreigntable => 'borrowers',
1318                         foreignkey => 'borrowernumber',
1319                         onUpdate => 'CASCADE',
1320                         onDelete => 'CASCADE',
1321                 },
1322                 {       key => 'biblionumber',
1323                         foreigntable => 'biblio',
1324                         foreignkey => 'biblionumber',
1325                         onUpdate => 'CASCADE',
1326                         onDelete => 'CASCADE',
1327                 },
1328                 {       key => 'itemnumber',
1329                         foreigntable => 'items',
1330                         foreignkey => 'itemnumber',
1331                         onUpdate => 'CASCADE',
1332                         onDelete => 'CASCADE',
1333                 },
1334                 {       key => 'branchcode',
1335                         foreigntable => 'branches',
1336                         foreignkey => 'branchcode',
1337                         onUpdate => 'CASCADE',
1338                         onDelete => 'CASCADE',
1339                 },
1340         ],
1341         borrowers => [ # foreign keys are RESTRICT as we don't want to delete borrowers when a branch is deleted
1342         # but prevent deleting a branch as soon as it has 1 borrower !
1343                 {       key => 'categorycode',
1344                         foreigntable => 'categories',
1345                         foreignkey => 'categorycode',
1346                         onUpdate => 'RESTRICT',
1347                         onDelete => 'RESTRICT',
1348                 },
1349                 {       key => 'branchcode',
1350                         foreigntable => 'branches',
1351                         foreignkey => 'branchcode',
1352                         onUpdate => 'RESTRICT',
1353                         onDelete => 'RESTRICT',
1354                 },
1355         ],
1356         deletedborrowers => [ # foreign keys are RESTRICT as we don't want to delete borrowers when a branch is deleted
1357         # but prevent deleting a branch as soon as it has 1 borrower !
1358                 {       key => 'categorycode',
1359                         foreigntable => 'categories',
1360                         foreignkey => 'categorycode',
1361                         onUpdate => 'RESTRICT',
1362                         onDelete => 'RESTRICT',
1363                 },
1364                 {       key => 'branchcode',
1365                         foreigntable => 'branches',
1366                         foreignkey => 'branchcode',
1367                         onUpdate => 'RESTRICT',
1368                         onDelete => 'RESTRICT',
1369                 },
1370         ],
1371         accountlines => [
1372                 {       key => 'borrowernumber',
1373                         foreigntable => 'borrowers',
1374                         foreignkey => 'borrowernumber',
1375                         onUpdate => 'CASCADE',
1376                         onDelete => 'CASCADE',
1377                 },
1378                 {       key => 'itemnumber',
1379                         foreigntable => 'items',
1380                         foreignkey => 'itemnumber',
1381                         onUpdate => 'SET NULL',
1382                         onDelete => 'SET NULL',
1383                 },
1384         ],
1385         auth_tag_structure => [
1386                 {       key => 'authtypecode',
1387                         foreigntable => 'auth_types',
1388                         foreignkey => 'authtypecode',
1389                         onUpdate => 'CASCADE',
1390                         onDelete => 'CASCADE',
1391                 },
1392         ],
1393         # FIXME : don't constraint auth_*_table and auth_word, as they may be replaced by zebra
1394 );
1395
1396
1397 # column changes
1398 my %column_change = (
1399         # table
1400         borrowers => [
1401                                 {
1402                                         from => 'emailaddress',
1403                                         to => 'email',
1404                                         after => 'city',
1405                                 },
1406                                 {
1407                                         from => 'streetaddress',
1408                                         to => 'address',
1409                                         after => 'initials',
1410                                 },
1411                                 {
1412                                         from => 'faxnumber',
1413                                         to => 'fax',
1414                                         after => 'phone',
1415                                 },
1416                                 {
1417                                         from => 'textmessaging',
1418                                         to => 'opacnote',
1419                                         after => 'userid',
1420                                 },
1421                                 {
1422                                         from => 'altnotes',
1423                                         to => 'contactnote',
1424                                         after => 'opacnote',
1425                                 },
1426                                 {
1427                                         from => 'physstreet',
1428                                         to => 'B_address',
1429                                         after => 'fax',
1430                                 },
1431                                 {
1432                                         from => 'streetcity',
1433                                         to => 'B_city',
1434                                         after => 'B_address',
1435                                 },
1436                                 {
1437                                         from => 'phoneday',
1438                                         to => 'mobile',
1439                                         after => 'phone',
1440                                 },
1441                                 {
1442                                         from => 'zipcode',
1443                                         to => 'zipcode',
1444                                         after => 'city',
1445                                 },
1446                                 {
1447                                         from => 'homezipcode',
1448                                         to => 'B_zipcode',
1449                                         after => 'B_city',
1450                                 },
1451                                 {
1452                                         from => 'altphone',
1453                                         to => 'B_phone',
1454                                         after => 'B_zipcode',
1455                                 },
1456                                 {
1457                                         from => 'expiry',
1458                                         to => 'dateexpiry',
1459                                         after => 'dateenrolled',
1460                                 },
1461                                 {
1462                                         from => 'guarantor',
1463                                         to => 'guarantorid',
1464                                         after => 'contactname',
1465                                 },
1466                                 {
1467                                         from => 'textmessaging',
1468                                         to => 'opacnotes',
1469                                         after => 'flags',
1470                                 },
1471                                 {
1472                                         from => 'altnotes',
1473                                         to => 'contactnotes',
1474                                         after => 'opacnotes',
1475                                 },
1476                                 {
1477                                         from => 'altrelationship',
1478                                         to => 'relationship',
1479                                         after => 'borrowernotes',
1480                                 },
1481                         ],
1482
1483         deletedborrowers => [
1484                                 {
1485                                         from => 'emailaddress',
1486                                         to => 'email',
1487                                         after => 'city',
1488                                 },
1489                                 {
1490                                         from => 'streetaddress',
1491                                         to => 'address',
1492                                         after => 'initials',
1493                                 },
1494                                 {
1495                                         from => 'faxnumber',
1496                                         to => 'fax',
1497                                         after => 'phone',
1498                                 },
1499                                 {
1500                                         from => 'textmessaging',
1501                                         to => 'opacnote',
1502                                         after => 'userid',
1503                                 },
1504                                 {
1505                                         from => 'altnotes',
1506                                         to => 'contactnote',
1507                                         after => 'opacnote',
1508                                 },
1509                                 {
1510                                         from => 'physstreet',
1511                                         to => 'B_address',
1512                                         after => 'fax',
1513                                 },
1514                                 {
1515                                         from => 'streetcity',
1516                                         to => 'B_city',
1517                                         after => 'B_address',
1518                                 },
1519                                 {
1520                                         from => 'phoneday',
1521                                         to => 'mobile',
1522                                         after => 'phone',
1523                                 },
1524                                 {
1525                                         from => 'zipcode',
1526                                         to => 'zipcode',
1527                                         after => 'city',
1528                                 },
1529                                 {
1530                                         from => 'homezipcode',
1531                                         to => 'B_zipcode',
1532                                         after => 'B_city',
1533                                 },
1534                                 {
1535                                         from => 'altphone',
1536                                         to => 'B_phone',
1537                                         after => 'B_zipcode',
1538                                 },
1539                                 {
1540                                         from => 'expiry',
1541                                         to => 'dateexpiry',
1542                                         after => 'dateenrolled',
1543                                 },
1544                                 {
1545                                         from => 'guarantor',
1546                                         to => 'guarantorid',
1547                                         after => 'contactname',
1548                                 },
1549                                 {
1550                                         from => 'textmessaging',
1551                                         to => 'opacnotes',
1552                                         after => 'flags',
1553                                 },
1554                                 {
1555                                         from => 'altnotes',
1556                                         to => 'contactnotes',
1557                                         after => 'opacnotes',
1558                                 },
1559                                 {
1560                                         from => 'altrelationship',
1561                                         to => 'relationship',
1562                                         after => 'borrowernotes',
1563                                 },
1564                         ],
1565
1566                 );
1567                 
1568 foreach my $table (keys %column_change) {
1569         $sth = $dbh->prepare("show columns from $table");
1570         $sth->execute();
1571         undef %types;
1572         while ( ( $column, $type, $null, $key, $default, $extra ) = $sth->fetchrow )
1573         {
1574                 $types{$column}->{type} ="$type";
1575                 $types{$column}->{null} = "$null";
1576                 $types{$column}->{key} = "$key";
1577                 $types{$column}->{default} = "$default";
1578                 $types{$column}->{extra} = "$extra";
1579         }    # while
1580         my $tablerows = $column_change{$table};
1581         foreach my $row ( @$tablerows ) {
1582                 if ($types{$row->{from}}->{type}) {
1583                         print "altering $table $row->{from} to $row->{to}\n";
1584                         # ALTER TABLE `borrowers` CHANGE `faxnumber` `fax` TEXT CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL 
1585 #                       alter table `borrowers` change `faxnumber` `fax` type text  null after phone
1586                         my $sql = 
1587                                 "alter table `$table` change `$row->{from}` `$row->{to}` $types{$row->{from}}->{type} ".
1588                                 ($types{$row->{from}}->{null} eq 'YES'?" NULL":" NOT NULL").
1589                                 ($types{$row->{from}}->{default}?" default ".$types{$row->{from}}->{default}:"").
1590                                 "$types{$row->{from}}->{extra} after $row->{after} ";
1591 #                       print "$sql";
1592                         $dbh->do($sql);
1593                 }
1594         }
1595 }
1596
1597 #-------------------
1598 # Initialize
1599
1600 # Start checking
1601
1602 # Get version of MySQL database engine.
1603 my $mysqlversion = `mysqld --version`;
1604 $mysqlversion =~ /Ver (\S*) /;
1605 $mysqlversion = $1;
1606 if ( $mysqlversion ge '3.23' ) {
1607     print "Could convert to MyISAM database tables...\n" unless $silent;
1608 }
1609
1610 #---------------------------------
1611 # Tables
1612
1613 # Collect all tables into a list
1614 $sth = $dbh->prepare("show tables");
1615 $sth->execute;
1616 while ( my ($table) = $sth->fetchrow ) {
1617     $existingtables{$table} = 1;
1618 }
1619
1620 # Now add any missing tables
1621 foreach $table ( keys %requiretables ) {
1622     unless ( $existingtables{$table} ) {
1623         print "Adding $table table...\n" unless $silent;
1624         my $sth = $dbh->prepare("create table $table $requiretables{$table}");
1625         $sth->execute;
1626         if ( $sth->err ) {
1627             print "Error : $sth->errstr \n";
1628             $sth->finish;
1629         }    # if error
1630     }    # unless exists
1631 }    # foreach
1632
1633 # now drop useless tables
1634 foreach $table ( keys %dropable_table ) {
1635     if ( $existingtables{$table} ) {
1636         print "Dropping unused table $table\n" if $debug and not $silent;
1637         $dbh->do("drop table $table");
1638         if ( $dbh->err ) {
1639             print "Error : $dbh->errstr \n";
1640         }
1641     }
1642 }
1643
1644 #---------------------------------
1645 # Columns
1646
1647 foreach $table ( keys %requirefields ) {
1648     print "Check table $table\n" if $debug and not $silent;
1649     $sth = $dbh->prepare("show columns from $table");
1650     $sth->execute();
1651     undef %types;
1652     while ( ( $column, $type, $null, $key, $default, $extra ) = $sth->fetchrow )
1653     {
1654         $types{$column} = $type;
1655     }    # while
1656     foreach $column ( keys %{ $requirefields{$table} } ) {
1657         print "  Check column $column  [$types{$column}]\n"
1658           if $debug and not $silent;
1659         if ( !$types{$column} ) {
1660
1661             # column doesn't exist
1662             print "Adding $column field to $table table...\n" unless $silent;
1663             $query = "alter table $table
1664                         add column $column " . $requirefields{$table}->{$column};
1665             print "Execute: $query\n" if $debug;
1666             my $sti = $dbh->prepare($query);
1667             $sti->execute;
1668             if ( $sti->err ) {
1669                 print "**Error : $sti->errstr \n";
1670                 $sti->finish;
1671             }    # if error
1672         }    # if column
1673     }    # foreach column
1674 }    # foreach table
1675
1676 foreach $table ( keys %fielddefinitions ) {
1677         print "Check table $table\n" if $debug;
1678         $sth = $dbh->prepare("show columns from $table");
1679         $sth->execute();
1680         my $definitions;
1681         while ( ( $column, $type, $null, $key, $default, $extra ) = $sth->fetchrow )
1682         {
1683                 $definitions->{$column}->{type}    = $type;
1684                 $definitions->{$column}->{null}    = $null;
1685                 $definitions->{$column}->{null}    = 'NULL' if $null eq 'YES';
1686                 $definitions->{$column}->{key}     = $key;
1687                 $definitions->{$column}->{default} = $default;
1688                 $definitions->{$column}->{extra}   = $extra;
1689         }    # while
1690         my $fieldrow = $fielddefinitions{$table};
1691         foreach my $row (@$fieldrow) {
1692                 my $field   = $row->{field};
1693                 my $type    = $row->{type};
1694                 my $null    = $row->{null};
1695 #               $null    = 'YES' if $row->{null} eq 'NULL';
1696                 my $key     = $row->{key};
1697                 my $default = $row->{default};
1698                 my $null    = $row->{null};
1699 #               $default="''" unless $default;
1700                 my $extra   = $row->{extra};
1701                 my $def     = $definitions->{$field};
1702                 my $after       = ($row->{after}?" after ".$row->{after}:"");
1703
1704                 unless ( $type eq $def->{type}
1705                         && $null eq $def->{null}
1706                         && $key eq $def->{key}
1707                         && $extra eq $def->{extra} )
1708                 {
1709                         if ( $null eq '' ) {
1710                                 $null = 'NOT NULL';
1711                         }
1712                         if ( $key eq 'PRI' ) {
1713                                 $key = 'PRIMARY KEY';
1714                         }
1715                         unless ( $extra eq 'auto_increment' ) {
1716                                 $extra = '';
1717                         }
1718
1719                         # if it's a new column use "add", if it's an old one, use "change".
1720                         my $action;
1721                         if ($definitions->{$field}->{type}) {
1722                                 $action="change $field"
1723                         } else {
1724                                 $action="add";
1725                         }
1726 # if it's a primary key, drop the previous pk, before altering the table
1727 #                       my $sth;
1728    my $request = "alter table $table ";
1729     $request.=" drop primary key" if $key eq 'PRIMARY KEY';
1730     $request.= " $action $field $type $null $key $extra ";
1731     $request.= "default ".$dbh->quote($default) if $default;
1732     $request.= " $after";
1733 #    print "REQ : $request";
1734                         $dbh->do($request);
1735                         print "  alter or create $field in $table\n" unless $silent;
1736                 }
1737         }
1738 }
1739
1740 # Populate tables with required data
1741
1742 # synch table and deletedtable.
1743 foreach my $table ( ( 'borrowers', 'items', 'biblio', 'biblioitems' ) ) {
1744     my %deletedborrowers;
1745     print "synch'ing $table\n";
1746     $sth = $dbh->prepare("show columns from deleted$table");
1747     $sth->execute;
1748     while ( my ( $column, $type, $null, $key, $default, $extra ) =
1749         $sth->fetchrow )
1750     {
1751         $deletedborrowers{$column} = 1;
1752     }
1753     $sth = $dbh->prepare("show columns from $table");
1754     $sth->execute;
1755     my $previous;
1756     while ( my ( $column, $type, $null, $key, $default, $extra ) =
1757         $sth->fetchrow )
1758     {
1759         unless ( $deletedborrowers{$column} ) {
1760             my $newcol = "alter table deleted$table add $column $type";
1761             if ( $null eq 'YES' ) {
1762                 $newcol .= " NULL ";
1763             }
1764             else {
1765                 $newcol .= " NOT NULL ";
1766             }
1767             $newcol .= "default $default" if $default;
1768             $newcol .= " after $previous" if $previous;
1769             $previous = $column;
1770             print "creating column $column\n";
1771             $dbh->do($newcol);
1772         }
1773     }
1774 }
1775
1776 foreach my $table ( keys %tabledata ) {
1777     print "Checking for data required in table $table...\n" unless $silent;
1778     my $tablerows = $tabledata{$table};
1779     foreach my $row (@$tablerows) {
1780         my $uniquefieldrequired = $row->{uniquefieldrequired};
1781         my $uniquevalue         = $row->{$uniquefieldrequired};
1782         my $forceupdate         = $row->{forceupdate};
1783         my $sth                 =
1784           $dbh->prepare(
1785 "select $uniquefieldrequired from $table where $uniquefieldrequired=?"
1786           );
1787         $sth->execute($uniquevalue);
1788                 if ($sth->rows) {
1789                         foreach my $field (keys %$forceupdate) {
1790                                 if ($forceupdate->{$field}) {
1791                                         my $sth=$dbh->prepare("update systempreferences set $field=? where $uniquefieldrequired=?");
1792                                         $sth->execute($row->{$field}, $uniquevalue);
1793                                 }
1794                 }
1795                 } else {
1796                         print "Adding row to $table: " unless $silent;
1797                         my @values;
1798                         my $fieldlist;
1799                         my $placeholders;
1800                         foreach my $field ( keys %$row ) {
1801                                 next if $field eq 'uniquefieldrequired';
1802                                 next if $field eq 'forceupdate';
1803                                 my $value = $row->{$field};
1804                                 push @values, $value;
1805                                 print "  $field => $value" unless $silent;
1806                                 $fieldlist .= "$field,";
1807                                 $placeholders .= "?,";
1808                         }
1809                         print "\n" unless $silent;
1810                         $fieldlist    =~ s/,$//;
1811                         $placeholders =~ s/,$//;
1812                         my $sth =
1813                         $dbh->prepare(
1814                                 "insert into $table ($fieldlist) values ($placeholders)");
1815                         $sth->execute(@values);
1816                 }
1817         }
1818 }
1819
1820 #
1821 # check indexes and create them when needed
1822 #
1823 print "Checking for index required...\n" unless $silent;
1824 foreach my $table ( keys %indexes ) {
1825         #
1826         # read all indexes from $table
1827         #
1828         $sth = $dbh->prepare("show index from $table");
1829         $sth->execute;
1830         my %existingindexes;
1831         while ( my ( $table, $non_unique, $key_name, $Seq_in_index, $Column_name, $Collation, $cardinality, $sub_part, $Packed, $comment ) = $sth->fetchrow ) {
1832                 $existingindexes{$key_name} = 1;
1833         }
1834         # read indexes to check
1835         my $tablerows = $indexes{$table};
1836         foreach my $row (@$tablerows) {
1837                 my $key_name=$row->{indexname};
1838                 if ($existingindexes{$key_name} eq 1) {
1839 #                       print "$key_name existing";
1840                 } else {
1841                         print "\tCreating index $key_name in $table\n";
1842                         my $sql;
1843                         if ($row->{indexname} eq 'PRIMARY') {
1844                                 $sql = "alter table $table ADD PRIMARY KEY ($row->{content})";
1845                         } else {
1846                                 $sql = "alter table $table ADD INDEX $key_name ($row->{content}) $row->{type}";
1847                         }
1848                         $dbh->do($sql);
1849             print "Error $sql : $dbh->err \n" if $dbh->err;
1850                 }
1851         }
1852 }
1853
1854 #
1855 # check foreign keys and create them when needed
1856 #
1857 print "Checking for foreign keys required...\n" unless $silent;
1858 foreach my $table ( keys %foreign_keys ) {
1859         #
1860         # read all indexes from $table
1861         #
1862         $sth = $dbh->prepare("show table status like '$table'");
1863         $sth->execute;
1864         my $stat = $sth->fetchrow_hashref;
1865         # read indexes to check
1866         my $tablerows = $foreign_keys{$table};
1867         foreach my $row (@$tablerows) {
1868                 my $foreign_table=$row->{foreigntable};
1869                 if ($stat->{'Comment'} =~/$foreign_table/) {
1870 #                       print "$foreign_table existing\n";
1871                 } else {
1872                         print "\tCreating foreign key $foreign_table in $table\n";
1873                         # first, drop any orphan value in child table
1874                         if ($row->{onDelete} ne "RESTRICT") {
1875                                 my $sql = "delete from $table where $row->{key} not in (select $row->{foreignkey} from $row->{foreigntable})";
1876                                 $dbh->do($sql);
1877                                 print "SQL ERROR: $sql : $dbh->err \n" if $dbh->err;
1878                         }
1879                         my $sql="alter table $table ADD FOREIGN KEY $row->{key} ($row->{key}) REFERENCES $row->{foreigntable} ($row->{foreignkey})";
1880                         $sql .= " on update ".$row->{onUpdate} if $row->{onUpdate};
1881                         $sql .= " on delete ".$row->{onDelete} if $row->{onDelete};
1882                         $dbh->do($sql);
1883                         if ($dbh->err) {
1884                                 print "====================
1885 An error occured during :
1886 \t$sql
1887 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).
1888 You can find those values with select
1889 \t$table.* from $table where $row->{key} not in (select $row->{foreignkey} from $row->{foreigntable})
1890 ====================\n
1891 ";
1892                         }
1893                 }
1894         }
1895 }
1896
1897 #
1898 # SPECIFIC STUFF
1899 #
1900 #
1901 # create frameworkcode row in biblio table & fill it with marc_biblio.frameworkcode.
1902 #
1903
1904 # 1st, get how many biblio we will have to do...
1905 $sth = $dbh->prepare('select count(*) from marc_biblio');
1906 $sth->execute;
1907 my ($totaltodo) = $sth->fetchrow;
1908
1909 $sth = $dbh->prepare("show columns from biblio");
1910 $sth->execute();
1911 my $definitions;
1912 my $bibliofwexist=0;
1913 while ( ( $column, $type, $null, $key, $default, $extra ) = $sth->fetchrow ){
1914         $bibliofwexist=1 if $column eq 'frameworkcode';
1915 }
1916 unless ($bibliofwexist) {
1917         print "moving biblioframework to biblio table\n";
1918         $dbh->do('ALTER TABLE `biblio` ADD `frameworkcode` VARCHAR( 4 ) NOT NULL AFTER `biblionumber`');
1919         $sth = $dbh->prepare('select biblionumber,frameworkcode from marc_biblio');
1920         $sth->execute;
1921         my $sth_update = $dbh->prepare('update biblio set frameworkcode=? where biblionumber=?');
1922         my $totaldone=0;
1923         while (my ($biblionumber,$frameworkcode) = $sth->fetchrow) {
1924                 $sth_update->execute($frameworkcode,$biblionumber);
1925                 $totaldone++;
1926                 print "\r$totaldone / $totaltodo" unless ($totaldone % 100);
1927         }
1928         print "\rdone\n";
1929 }
1930
1931 #
1932 # moving MARC data from marc_subfield_table to biblioitems.marc
1933 #
1934 $sth = $dbh->prepare("show columns from biblioitems");
1935 $sth->execute();
1936 my $definitions;
1937 my $marcdone=0;
1938 while ( ( $column, $type, $null, $key, $default, $extra ) = $sth->fetchrow ){
1939         $marcdone=1 if ($type eq 'blob' && $column eq 'marc') ;
1940 }
1941 unless ($marcdone) {
1942         print "moving MARC record to biblioitems table\n";
1943         # changing marc field type
1944         $dbh->do('ALTER TABLE `biblioitems` CHANGE `marc` `marc` BLOB NULL DEFAULT NULL ');
1945         # adding marc xml, just for convenience
1946         $dbh->do('ALTER TABLE `biblioitems` ADD `marcxml` TEXT CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL ');
1947         # moving data from marc_subfield_value to biblio
1948         $sth = $dbh->prepare('select bibid,biblionumber from marc_biblio');
1949         $sth->execute;
1950         my $sth_update = $dbh->prepare('update biblioitems set marc=?, marcxml=? where biblionumber=?');
1951         my $totaldone=0;
1952         while (my ($bibid,$biblionumber) = $sth->fetchrow) {
1953                 my $record = MARCgetbiblio($dbh,$bibid);
1954         #Force UTF-8 in record leader
1955                 $record->encoding('UTF-8');
1956                 print $record->as_formatted if ($biblionumber==3902);
1957                 $sth_update->execute($record->as_usmarc(),$record->as_xml_record(),$biblionumber);
1958                 $totaldone++;
1959                 print "\r$totaldone / $totaltodo" unless ($totaldone % 100);
1960         }
1961         print "\rdone\n";
1962 }
1963
1964
1965 # at last, remove useless fields
1966 foreach $table ( keys %uselessfields ) {
1967     my @fields = split /,/, $uselessfields{$table};
1968     my $fields;
1969     my $exists;
1970     foreach my $fieldtodrop (@fields) {
1971         $fieldtodrop =~ s/\t//g;
1972         $fieldtodrop =~ s/\n//g;
1973         $exists = 0;
1974         $sth    = $dbh->prepare("show columns from $table");
1975         $sth->execute;
1976         while ( my ( $column, $type, $null, $key, $default, $extra ) =
1977             $sth->fetchrow )
1978         {
1979             $exists = 1 if ( $column eq $fieldtodrop );
1980         }
1981         if ($exists) {
1982             print "deleting $fieldtodrop field in $table...\n" unless $silent;
1983             my $sth = $dbh->prepare("alter table $table drop $fieldtodrop");
1984             $sth->execute;
1985         }
1986     }
1987 }    # foreach
1988
1989
1990 # MOVE all tables TO UTF-8 and innoDB
1991 $sth = $dbh->prepare("show table status");
1992 $sth->execute;
1993 while ( my $table = $sth->fetchrow_hashref ) {
1994 #       if ($table->{Engine} ne 'InnoDB') {
1995 #               $dbh->do("ALTER TABLE $table->{Name} TYPE = innodb");
1996 #               print "moving $table->{Name} to InnoDB\n";
1997 #       }
1998         unless ($table->{Collation} =~ /^utf8/) {
1999                 $dbh->do("ALTER TABLE $table->{Name} CONVERT TO CHARACTER SET utf8");
2000                 $dbh->do("ALTER TABLE $table->{Name} DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci");
2001                 # 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 !
2002                 print "moving $table->{Name} to utf8\n";
2003         } else {
2004         }
2005 }
2006
2007 $sth->finish;
2008
2009 #
2010 # those 2 subs are a copy of Biblio.pm, version 2.2.4
2011 # they are useful only once, for moving from 2.2 to 3.0
2012 # the MARCgetbiblio & MARCgetitem subs in Biblio.pm
2013 # are still here, but uses other tables
2014 # (the ones that are filled by updatedatabase !)
2015 #
2016
2017 sub MARCgetbiblio {
2018
2019     # Returns MARC::Record of the biblio passed in parameter.
2020     my ( $dbh, $bibid ) = @_;
2021     my $record = MARC::Record->new();
2022 #       warn "". $bidid;
2023
2024     my $sth =
2025       $dbh->prepare(
2026 "select bibid,subfieldid,tag,tagorder,tag_indicator,subfieldcode,subfieldorder,subfieldvalue,valuebloblink
2027                                  from marc_subfield_table
2028                                  where bibid=? order by tag,tagorder,subfieldorder
2029                          "
2030     );
2031     my $sth2 =
2032       $dbh->prepare(
2033         "select subfieldvalue from marc_blob_subfield where blobidlink=?");
2034     $sth->execute($bibid);
2035     my $prevtagorder = 1;
2036     my $prevtag      = 'XXX';
2037     my $previndicator;
2038     my $field;        # for >=10 tags
2039     my $prevvalue;    # for <10 tags
2040     while ( my $row = $sth->fetchrow_hashref ) {
2041
2042         if ( $row->{'valuebloblink'} ) {    #---- search blob if there is one
2043             $sth2->execute( $row->{'valuebloblink'} );
2044             my $row2 = $sth2->fetchrow_hashref;
2045             $sth2->finish;
2046             $row->{'subfieldvalue'} = $row2->{'subfieldvalue'};
2047         }
2048         if ( $row->{tagorder} ne $prevtagorder || $row->{tag} ne $prevtag ) {
2049             $previndicator .= "  ";
2050             if ( $prevtag < 10 ) {
2051                                 if ($prevtag ne '000') {
2052                         $record->add_fields( ( sprintf "%03s", $prevtag ), $prevvalue ) unless $prevtag eq "XXX";    # ignore the 1st loop
2053                                 } else {
2054                                         $record->leader(sprintf("%24s",$prevvalue));
2055                                 }
2056             }
2057             else {
2058                 $record->add_fields($field) unless $prevtag eq "XXX";
2059             }
2060             undef $field;
2061             $prevtagorder  = $row->{tagorder};
2062             $prevtag       = $row->{tag};
2063             $previndicator = $row->{tag_indicator};
2064             if ( $row->{tag} < 10 ) {
2065                 $prevvalue = $row->{subfieldvalue};
2066             }
2067             else {
2068                 $field = MARC::Field->new(
2069                     ( sprintf "%03s", $prevtag ),
2070                     substr( $row->{tag_indicator} . '  ', 0, 1 ),
2071                     substr( $row->{tag_indicator} . '  ', 1, 1 ),
2072                     $row->{'subfieldcode'},
2073                     $row->{'subfieldvalue'}
2074                 );
2075             }
2076         }
2077         else {
2078             if ( $row->{tag} < 10 ) {
2079                 $record->add_fields( ( sprintf "%03s", $row->{tag} ),
2080                     $row->{'subfieldvalue'} );
2081             }
2082             else {
2083                 $field->add_subfields( $row->{'subfieldcode'},
2084                     $row->{'subfieldvalue'} );
2085             }
2086             $prevtag       = $row->{tag};
2087             $previndicator = $row->{tag_indicator};
2088         }
2089     }
2090
2091     # the last has not been included inside the loop... do it now !
2092     if ( $prevtag ne "XXX" )
2093     { # check that we have found something. Otherwise, prevtag is still XXX and we
2094          # must return an empty record, not make MARC::Record fail because we try to
2095          # create a record with XXX as field :-(
2096         if ( $prevtag < 10 ) {
2097             $record->add_fields( $prevtag, $prevvalue );
2098         }
2099         else {
2100
2101             #           my $field = MARC::Field->new( $prevtag, "", "", %subfieldlist);
2102             $record->add_fields($field);
2103         }
2104     }
2105     return $record;
2106 }
2107
2108 sub MARCgetitem {
2109
2110     # Returns MARC::Record of the biblio passed in parameter.
2111     my ( $dbh, $bibid, $itemnumber ) = @_;
2112     my $record = MARC::Record->new();
2113
2114     # search MARC tagorder
2115     my $sth2 =
2116       $dbh->prepare(
2117 "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=?"
2118     );
2119     $sth2->execute( $bibid, $itemnumber );
2120     my ($tagorder) = $sth2->fetchrow_array();
2121
2122     #---- TODO : the leader is missing
2123     my $sth =
2124       $dbh->prepare(
2125 "select bibid,subfieldid,tag,tagorder,tag_indicator,subfieldcode,subfieldorder,subfieldvalue,valuebloblink
2126                                  from marc_subfield_table
2127                                  where bibid=? and tagorder=? order by subfieldcode,subfieldorder
2128                          "
2129     );
2130     $sth2 =
2131       $dbh->prepare(
2132         "select subfieldvalue from marc_blob_subfield where blobidlink=?");
2133     $sth->execute( $bibid, $tagorder );
2134     while ( my $row = $sth->fetchrow_hashref ) {
2135         if ( $row->{'valuebloblink'} ) {    #---- search blob if there is one
2136             $sth2->execute( $row->{'valuebloblink'} );
2137             my $row2 = $sth2->fetchrow_hashref;
2138             $sth2->finish;
2139             $row->{'subfieldvalue'} = $row2->{'subfieldvalue'};
2140         }
2141         if ( $record->field( $row->{'tag'} ) ) {
2142             my $field;
2143
2144 #--- this test must stay as this, because of strange behaviour of mySQL/Perl DBI with char var containing a number...
2145             #--- sometimes, eliminates 0 at beginning, sometimes no ;-\\\
2146             if ( length( $row->{'tag'} ) < 3 ) {
2147                 $row->{'tag'} = "0" . $row->{'tag'};
2148             }
2149             $field = $record->field( $row->{'tag'} );
2150             if ($field) {
2151                 my $x =
2152                   $field->add_subfields( $row->{'subfieldcode'},
2153                     $row->{'subfieldvalue'} );
2154                 $record->delete_field($field);
2155                 $record->add_fields($field);
2156             }
2157         }
2158         else {
2159             if ( length( $row->{'tag'} ) < 3 ) {
2160                 $row->{'tag'} = "0" . $row->{'tag'};
2161             }
2162             my $temp =
2163               MARC::Field->new( $row->{'tag'}, " ", " ",
2164                 $row->{'subfieldcode'} => $row->{'subfieldvalue'} );
2165             $record->add_fields($temp);
2166         }
2167
2168     }
2169     return $record;
2170 }
2171
2172
2173 exit;
2174
2175 # $Log$
2176 # Revision 1.155  2006/07/17 12:51:48  toins
2177 # auto_increment id in aqbooksellers
2178 #
2179 # Revision 1.153  2006/07/04 14:36:52  toins
2180 # Head & rel_2_2 merged
2181 #
2182 # Revision 1.152  2006/06/27 09:26:37  btoumi
2183 # modify (initials,phone ) fields property in borrowers and deletedborrowers table
2184 #
2185 # Revision 1.151  2006/06/22 10:33:14  btoumi
2186 # sorry i forget deletedborrowers table
2187 # modify firstname field from deletedborrowers table
2188 #
2189 # Revision 1.149  2006/06/20 22:35:47  rangi
2190 # Code to allow the associated borrowers to work
2191 #
2192 # Revision 1.148  2006/06/17 22:12:01  rangi
2193 # Adding id field to reviews table
2194 #
2195 # Revision 1.147  2006/06/17 03:36:41  rangi
2196 # Table definition for the reviews table
2197 #
2198 # Revision 1.146  2006/06/17 03:29:41  rangi
2199 # Variable to allow librarians to switch reviews on or off
2200 #
2201 # Revision 1.145  2006/06/16 09:45:02  btoumi
2202 # updatedatabase.pl: add change of borrowers table to deletedborrowers table
2203 # deletemem.pl: delete use of warn function
2204 #
2205 # Revision 1.144  2006/06/08 15:36:31  alaurin
2206 # Add a new system preference 'AutomaticItemReturn' :
2207 #
2208 # 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) .
2209 #
2210 # switch off : the document stay in the holdingbranch ...
2211 #
2212 # correcting bugs :
2213 # - comment C4::acquisition (not using in request.pl).
2214 # - correcting date in request.pl
2215 # -add the new call of function getbranches in request.pl
2216 #
2217 # Revision 1.143  2006/06/07 02:02:47  bob_lyon
2218 # merging katipo changes...
2219 #
2220 # adding new preference IssuingInProcess
2221 #
2222 # Revision 1.142  2006/06/06 23:42:46  bob_lyon
2223 # Merging Katipo changes...
2224 #
2225 # Adding new system pref where one can still retrieve a correct reading
2226 # record history if one has moved older data from issues to oldissues table
2227 # to speed up issues speed
2228 #
2229 # Revision 1.141  2006/06/01 03:18:11  rangi
2230 # Adding a new column to the statistics table
2231 #
2232 # Revision 1.140  2006/05/22 22:40:45  rangi
2233 # Adding new systempreference allowing for the library to add borrowers to institutions (rest homes, parishes, schools, classes etc).
2234 #
2235 # Revision 1.139  2006/05/19 19:31:29  tgarip1957
2236 # Added new fields to auth_header and auth_subfield_table to allow ZEBRA use of authorities and new MARC framework like structure.
2237 # 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
2238 #
2239 # Revision 1.138  2006/05/19 16:51:44  alaurin
2240 # update database for :
2241 # - new feature ip and printer management
2242 # adding two fields in branches table (branchip,branchprinter)
2243 #
2244 # - 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
2245 #
2246 # new system preference :
2247 # - ReservesMaxPickUpDelay : Maximum delay to pick up a reserved document
2248 # TransfersMaxDaysWarning : Max delay before considering the transfer as potentialy a problem
2249 #
2250 # Revision 1.137  2006/04/18 09:36:36  plg
2251 # bug fixed: typo fixed in labels and labels_conf tables creation query.
2252 #
2253 # Revision 1.136  2006/04/17 21:55:33  sushi
2254 # Added 'labels' and 'labels_conf' tables, for spine lable tool.
2255 #
2256 # Revision 1.135  2006/04/15 02:37:03  tgarip1957
2257 # Marc record should be set to UTF-8 in leader.Force it.
2258 # XML should be with<record> wrappers
2259 #
2260 # Revision 1.134  2006/04/14 09:37:29  tipaul
2261 # improvements from SAN Ouest Provence :
2262 # * introducing a category_type into categories. It can be A (adult), C (children), P (Professionnal), I (institution/organisation).
2263 # * each category_type has it's own forms to create members.
2264 # * the borrowers table has been heavily modified (many fields changed), to get something more logic & readable
2265 # * reintroducing guarantor/guanrantee system that is now independant from hardcoded C/A for categories
2266 # * updating templates to fit template rules
2267 #
2268 # (see mail feb, 17 on koha-devel "new features for borrowers" for more details)
2269 #
2270 # Revision 1.133  2006/04/13 08:36:42  plg
2271 # new: function C4::Date::get_date_format_string_for_DHTMLcalendar based on
2272 # the system preference prefered date format.
2273 #
2274 # improvement: book fund list and budget list screen redesigned. Filters on
2275 # each field. Columns are not sortable yet. Using DHTML Calendar to fill date
2276 # fields instead of manual filling. Pagination system. From the book fund
2277 # list, you can reach the budget list, filtered on a book fund, or not. A
2278 # budget can be added only from book fund list screen.
2279 #
2280 # bug fixed: branchcode was missing in table aqbudget.
2281 #
2282 # bug fixed: when setting a branchcode to a book fund, all associated budgets
2283 # move to this branchcode.
2284 #
2285 # modification: when adding/modifying budget/fund, MySQL specific "REPLACE..."
2286 # statements replaced by standard SQL compliant statement.
2287 #
2288 # bug fixed: when adding/modifying a budget, if the book fund is associated to
2289 # a branch, the branch selection is disabled and set to the book fund branch.
2290 #
2291 # Revision 1.132  2006/04/06 12:37:05  hdl
2292 # Bugfixing : aqbookfund needed a field.
2293 #
2294 # Revision 1.131  2006/03/03 17:02:22  tipaul
2295 # commit for holidays and news management.
2296 # (some forgotten files)
2297 #
2298 # Revision 1.130  2006/03/03 16:35:21  tipaul
2299 # commit for holidays and news management.
2300 #
2301 # Contrib from Tümer Garip (from Turkey) :
2302 # * holiday :
2303 # 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 :
2304 # - single day : only this day is closed
2305 # - repet weekly (like "sunday") : the day is holiday every week
2306 # - repet yearly (like "July, 4") : this day is closed every year.
2307 #
2308 # You can also put exception :
2309 # - sunday is holiday, but "2006 March, 5th" the library will be open
2310 #
2311 # 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.
2312 #
2313 # Revision 1.129  2006/02/27 18:19:33  hdl
2314 # New table used in overduerules.pl tools page.
2315 #
2316 # Revision 1.128  2006/01/25 15:16:06  tipaul
2317 # updating DB :
2318 # * removing useless tables
2319 # * adding useful indexes
2320 # * altering some columns definitions
2321 # * The goal being to have updater working fine for foreign keys.
2322 #
2323 # 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
2324 #
2325 # Revision 1.127  2006/01/24 17:57:17  tipaul
2326 # DB improvements : adding foreign keys on some tables. partial stuff done.
2327 #
2328 # Revision 1.126  2006/01/06 16:39:42  tipaul
2329 # synch'ing head and rel_2_2 (from 2.2.5, including npl templates)
2330 # Seems not to break too many things, but i'm probably wrong here.
2331 # at least, new features/bugfixes from 2.2.5 are here (tested on some features on my head local copy)
2332 #
2333 # - removing useless directories (koha-html and koha-plucene)
2334 #
2335 # Revision 1.125  2006/01/04 15:54:55  tipaul
2336 # utf8 is a : go for beta test in HEAD.
2337 # some explanations :
2338 # - 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.
2339 # - *-top.inc will show the pages in utf8
2340 # - 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.
2341 # - using marcxml field and no more the iso2709 raw marc biblioitems.marc field.
2342 #
2343 # Revision 1.124  2005/10/27 12:09:05  tipaul
2344 # new features for serial module :
2345 # - 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")
2346 # - 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).
2347 #
2348 # Revision 1.123  2005/10/26 09:13:37  tipaul
2349 # big commit, still breaking things...
2350 #
2351 # * synch with rel_2_2. Probably the last non manual synch, as rel_2_2 should not be modified deeply.
2352 # * code cleaning (cleaning warnings from perl -w) continued
2353 #
2354 # Revision 1.122  2005/09/02 14:18:38  tipaul
2355 # new feature : image for itemtypes.
2356 #
2357 # * run updater/updatedatabase to create imageurl field in itemtypes.
2358 # * 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)
2359 # * go to OPAC, and search something. In the result list, you now have the picture instead of the text itemtype.
2360 #
2361 # Revision 1.121  2005/08/24 08:49:03  hdl
2362 # Adding a note field in serial table.
2363 # This will allow librarian to mention a note on a peculiar waiting serial number.
2364 #
2365 # Revision 1.120  2005/08/09 14:10:32  tipaul
2366 # 1st commit to go to zebra.
2367 # don't update your cvs if you want to have a working head...
2368 #
2369 # this commit contains :
2370 # * 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...
2371 # * 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.
2372 # * other files : get rid of bibid and use biblionumber instead.
2373 #
2374 # What is broken :
2375 # * does not do anything on zebra yet.
2376 # * if you rename marc_subfield_table, you can't search anymore.
2377 # * you can view a biblio & bibliodetails, go to MARC editor, but NOT save any modif.
2378 # * 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 ;-) )
2379 #
2380 # 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
2381 # 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.
2382 #
2383 # Revision 1.119  2005/08/04 16:07:58  tipaul
2384 # Synch really broke this script...
2385 #
2386 # Revision 1.118  2005/08/04 16:02:55  tipaul
2387 # oops... error in synch between 2.2 and head
2388 #
2389 # Revision 1.117  2005/08/04 14:24:39  tipaul
2390 # synch'ing 2.2 and head
2391 #
2392 # Revision 1.116  2005/08/04 08:55:54  tipaul
2393 # Letters / alert system, continuing...
2394 #
2395 # * adding a package Letters.pm, that manages Letters & alerts.
2396 # * 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)
2397 # * 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)
2398 # * 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.
2399 #
2400 # Note that the system should be generic enough to manage any type of alert.
2401 # 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 ;-) )
2402 #
2403 # Revision 1.115  2005/08/02 16:15:34  tipaul
2404 # adding 2 fields to letter system :
2405 # * module (acquisition, catalogue...) : it will be usefull to show the librarian only letters he may be interested by.
2406 # * title, that will be used as mail subject.
2407 #
2408 # Revision 1.114  2005/07/28 15:10:13  tipaul
2409 # 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.
2410 # the letter table contains 3 fields :
2411 # * code => the code of the letter
2412 # * name => the complete name of the letter
2413 # * content => the complete text. It's a TEXT field type, so has no limits.
2414 #
2415 # My next goal now is to work on point 2-I "serial issue alert"
2416 # 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.
2417 # (see mail on koha-devel, 2005/04/07)
2418 #
2419 # The "serial issue alert" will be the 1st to use this letter system that probably needs some tweaking ;-)
2420 #
2421 # Once it will be stabilised default letters (in any languages) could be added during installer to help the library begin with this new feature.
2422 #
2423 # Revision 1.113  2005/07/28 08:38:41  tipaul
2424 # 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 :
2425 # * ReturnBeforeExpiry = yes => return date can't be after expiry date
2426 # * ReturnBeforeExpiry = no  => return date can be after expiry date
2427 #
2428 # Revision 1.112  2005/07/26 08:19:47  hdl
2429 # Adding IndependantBranches System preference variable in order to manage Branch independancy.
2430 #
2431 # Revision 1.111  2005/07/25 15:35:38  tipaul
2432 # we have decided that moving to Koha 3.0 requires being already in Koha 2.2.x
2433 # So, the updatedatabase script can highly be cleaned (90% removed).
2434 # Let's play with the new Koha DB structure now ;-)
2435 #