Applying the fix for bug 1416 from paul
[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 # Koha modules
21 use C4::Context;
22
23 use MARC::Record;
24 use MARC::File::XML ( BinaryEncoding => 'utf8' );
25  
26 # FIXME - The user might be installing a new database, so can't rely
27 # on /etc/koha.conf anyway.
28
29 my $debug = 0;
30
31 my (
32     $sth, $sti,
33     $query,
34     %existingtables,    # tables already in database
35     %types,
36     $table,
37     $column,
38     $type, $null, $key, $default, $extra,
39     $prefitem,          # preference item in systempreferences table
40 );
41
42 my $silent;
43 GetOptions(
44     's' =>\$silent
45     );
46 my $dbh = C4::Context->dbh;
47 $|=1; # flushes output
48
49 my $DBversion = "3.00.00.000";
50 # if we are upgrading from Koha 2.2, then we need to run the complete & long updatedatabase
51 if (C4::Context->preference("Version") < TransformToNum($DBversion) ) {
52     # Tables to add if they don't exist
53     my %requiretables = (
54         action_logs     => "(
55                         `timestamp` TIMESTAMP NOT NULL ,
56                         `user` INT( 11 ) NOT NULL default '0' ,
57                         `module` TEXT default '',
58                         `action` TEXT default '' ,
59                         `object` INT(11) NULL ,
60                         `info` TEXT default '' ,
61                         PRIMARY KEY ( `timestamp` , `user` )
62                     )",
63         letter        => "(
64                         module varchar(20) NOT NULL default '',
65                         code varchar(20) NOT NULL default '',
66                         name varchar(100) NOT NULL default '',
67                         title varchar(200) NOT NULL default '',
68                         content text,
69                         PRIMARY KEY  (module,code)
70                     )",
71         alert        =>"(
72                         alertid int(11) NOT NULL auto_increment,
73                         borrowernumber int(11) NOT NULL default '0',
74                         type varchar(10) NOT NULL default '',
75                         externalid varchar(20) NOT NULL default '',
76                         PRIMARY KEY  (alertid),
77                         KEY borrowernumber (borrowernumber),
78                         KEY type (type,externalid)
79                     )",
80         opac_news => "(
81                     `idnew` int(10) unsigned NOT NULL auto_increment,
82                     `title` varchar(250) NOT NULL default '',
83                     `new` text NOT NULL,
84                     `lang` varchar(4) NOT NULL default '',
85                     `timestamp` timestamp NOT NULL default CURRENT_TIMESTAMP,
86                     PRIMARY KEY  (`idnew`)
87                     )",
88         repeatable_holidays => "(
89                     `id` int(11) NOT NULL auto_increment,
90                     `branchcode` varchar(4) NOT NULL default '',
91                     `weekday` smallint(6) default NULL,
92                     `day` smallint(6) default NULL,
93                     `month` smallint(6) default NULL,
94                     `title` varchar(50) NOT NULL default '',
95                     `description` text NOT NULL,
96                     PRIMARY KEY  (`id`)
97                     )",
98         special_holidays => "(
99                     `id` int(11) NOT NULL auto_increment,
100                     `branchcode` varchar(4) NOT NULL default '',
101                     `day` smallint(6) NOT NULL default '0',
102                     `month` smallint(6) NOT NULL default '0',
103                     `year` smallint(6) NOT NULL default '0',
104                     `isexception` smallint(1) NOT NULL default '1',
105                     `title` varchar(50) NOT NULL default '',
106                     `description` text NOT NULL,
107                     PRIMARY KEY  (`id`)
108                     )",
109         overduerules    =>"(`branchcode` varchar(255) NOT NULL default '',
110                         `categorycode` char(2) NOT NULL default '',
111                         `delay1` int(4) default '0',
112                         `letter1` varchar(20) default NULL,
113                         `debarred1` char(1) default '0',
114                         `delay2` int(4) default '0',
115                         `debarred2` char(1) default '0',
116                         `letter2` varchar(20) default NULL,
117                         `delay3` int(4) default '0',
118                         `letter3` varchar(20) default NULL,
119                         `debarred3` int(1) default '0',
120                         PRIMARY KEY  (`branchcode`,`categorycode`)
121                         )",
122         cities            => "(`cityid` int auto_increment,
123                             `city_name` char(100) NOT NULL,
124                             `city_zipcode` char(20),
125                             PRIMARY KEY (`cityid`)
126                         )",
127         roadtype            => "(`roadtypeid` int auto_increment,
128                             `road_type` char(100) NOT NULL,
129                             PRIMARY KEY (`roadtypeid`)
130                         )",
131     
132         labels                     => "(
133                     labelid int(11) NOT NULL auto_increment,
134                                 itemnumber varchar(100) NOT NULL default '',
135                                 timestamp timestamp(14) NOT NULL,
136                                 PRIMARY KEY  (labelid)
137                                 )",
138     
139         labels_conf                => "(
140                     id int(4) NOT NULL auto_increment,
141                                 barcodetype char(100) default '',
142                                 title tinyint(1) default '0',
143                                 isbn tinyint(1) default '0',
144                                 itemtype tinyint(1) default '0',
145                                 barcode tinyint(1) default '0',
146                                 dewey tinyint(1) default '0',
147                                 class tinyint(1) default '0',
148                                 author tinyint(1) default '0',
149                                 papertype char(100) default '',
150                                 startrow int(2) default NULL,
151                                 PRIMARY KEY  (id)
152                                 )",
153         reviews                  => "(
154                                 reviewid integer NOT NULL auto_increment,
155                                 borrowernumber integer,
156                                 biblionumber integer,
157                                 review text,
158                                 approved tinyint,
159                                 datereviewed datetime,
160                                 PRIMARY KEY (reviewid)
161                                 )",
162         subscriptionroutinglist=>"(
163                                 routingid integer NOT NULL auto_increment,
164                                 borrowernumber integer,
165                                 ranking integer,
166                                 subscriptionid integer,
167                                 PRIMARY KEY (routingid)
168                                 )",
169     
170         notifys    => "(
171                 notify_id int(11) NOT NULL default '0',
172                     `borrowernumber` int(11) NOT NULL default '0',
173                 `itemnumber` int(11) NOT NULL default '0',
174                 `notify_date` date NOT NULL default '0000-00-00',
175                         `notify_send_date` date default NULL,
176                         `notify_level` int(1) NOT NULL default '0',
177                         `method` varchar(20) NOT NULL default ''
178                 )",
179     
180     charges    => "(
181                 `charge_id` varchar(5) NOT NULL default '',
182                     `description` text NOT NULL,
183                     `amount` decimal(28,6) NOT NULL default '0.000000',
184                             `min` int(4) NOT NULL default '0',
185                     `max` int(4) NOT NULL default '0',
186                             `level` int(1) NOT NULL default '0',
187                             PRIMARY KEY  (`charge_id`)
188                 )",
189         tags => "(
190             `entry` varchar(255) NOT NULL default '',
191             `weight` bigint(20) NOT NULL default '0',
192             PRIMARY KEY  (`entry`)
193         )
194         ",
195     zebraqueue    => "(
196                     `id` int NOT NULL auto_increment,
197                     `biblio_auth_number` int NOT NULL,
198                     `operation` char(20) NOT NULL,
199                     `server` char(20) NOT NULL ,
200                     PRIMARY KEY  (`id`)
201                 ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci AUTO_INCREMENT=1",
202     
203     );
204     
205     my %requirefields = (
206         subscription => { 'letter' => 'char(20) NULL', 'distributedto' => 'text NULL', 'firstacquidate'=>'date NOT NULL','irregularity'=>'TEXT NULL default \'\'','numberpattern'=>'TINYINT(3) NULL default 0', 'callnumber'=>'text NULL', 'hemisphere' =>'TINYINT(3) NULL default 0', 'issuesatonce'=>'TINYINT(3) NOT NULL default 1',  'branchcode' =>'varchar(12) NOT NULL default \'\'', 'manualhistory'=>'TINYINT(1) NOT NULL default 0','internalnotes'=>'LONGTEXT NULL default \'\''},
207         itemtypes => { 'imageurl' => 'char(200) NULL'},
208         aqbookfund => { 'branchcode' => 'varchar(4) NULL'},
209         aqbudget => { 'branchcode' => 'varchar(4) NULL'},
210         auth_header => { 'marc' => 'BLOB NOT NULL', 'linkid' => 'BIGINT(20) NULL'},
211         auth_subfield_structure =>{ 'hidden' => 'TINYINT(3) NOT NULL default 0', 'kohafield' => 'VARCHAR(45) NOT NULL', 'linkid' =>  'TINYINT(1) NOT NULL default 0', 'isurl' => 'TINYINT(1)', 'frameworkcode'=>'VARCHAR(8) NOT  NULL'},
212         marc_breeding => { 'isbn' => 'varchar(13) NOT NULL'},
213         serial =>{ 'publisheddate' => 'date', 'claimdate' => 'date', 'itemnumber'=>'text NULL','routingnotes'=>'text NULL',},
214         statistics => { 'associatedborrower' => 'integer'},
215         z3950servers =>{  "name" =>"text",  "description" => "text NOT NULL",
216                         "position" =>"enum('primary','secondary','') NOT NULL default 'primary'",  "icon" =>"text",
217                         "type" =>"enum('zed','opensearch') NOT NULL default 'zed'",
218                         },
219         issues =>{ 'issuedate'=>"date NOT NULL default '0000-00-00'", },
220     
221     #    tablename        => { 'field' => 'fieldtype' },
222     );
223     
224     # Enter here the table to delete.
225     my @TableToDelete = qw(
226         additionalauthors
227         bibliosubject
228         bibliosubtitle
229         bibliothesaurus
230     );
231     
232     my %uselessfields = (
233     # tablename => "field1,field2",
234         borrowers => "suburb,altstreetaddress,altsuburb,altcity,studentnumber,school,area,preferredcont,altcp",
235         deletedborrowers=> "suburb,altstreetaddress,altsuburb,altcity,studentnumber,school,area,preferredcont,altcp",
236         );
237     # the other hash contains other actions that can't be done elsewhere. they are done
238     # either BEFORE of AFTER everything else, depending on "when" entry (default => AFTER)
239     
240     # The tabledata hash contains data that should be in the tables.
241     # The uniquefieldrequired hash entry is used to determine which (if any) fields
242     # must not exist in the table for this row to be inserted.  If the
243     # uniquefieldrequired entry is already in the table, the existing data is not
244     # modified, unless the forceupdate hash entry is also set.  Fields in the
245     # anonymous "forceupdate" hash will be forced to be updated to the default
246     # values given in the %tabledata hash.
247     
248     my %tabledata = (
249     # tablename => [
250     #    {    uniquefielrequired => 'fieldname', # the primary key in the table
251     #        fieldname => fieldvalue,
252     #        fieldname2 => fieldvalue2,
253     #    },
254     # ],
255         systempreferences => [
256             {
257                 uniquefieldrequired => 'variable',
258                 variable            => 'useDaysMode',
259                 value               => 'Calendar',
260                 forceupdate         => { 'explanation' => 1,
261                                         'type' => 1},
262                 explanation            => 'How to calculate return dates : Calendar means holidays will be controled, Days means the return date don\'t depend on holidays',
263                 type        => 'Choice',
264                 options        => 'Calendar|Days'
265             },
266             {
267                 uniquefieldrequired => 'variable',
268                 variable            => 'BorrowersTitles',
269                 value               => 'Mr|Mrs|Miss|Ms',
270                 forceupdate         => { 'explanation' => 1,
271                                         'type' => 1},
272                 explanation         => 'List all Titles for borrowers',
273                 type                => 'free',
274             },
275             {
276                 uniquefieldrequired => 'variable',
277                 variable            => 'BorrowerMandatoryField',
278                 value               => 'cardnumber|surname|address',
279                 forceupdate         => { 'explanation' => 1,
280                                         'type' => 1},
281                 explanation         => 'List all mandatory fields for borrowers',
282                 type                => 'free',
283             },
284             {
285                 uniquefieldrequired => 'variable',
286                 variable            => 'borrowerRelationship',
287                 value               => 'father|mother,grand-mother',
288                 forceupdate         => { 'explanation' => 1,
289                                         'type' => 1},
290                 explanation         => 'The relationships between a guarantor & a guarantee (separated by | or ,)',
291                 type                => 'free',
292             },
293             {
294                 uniquefieldrequired => 'variable',
295                 variable            => 'ReservesMaxPickUpDelay',
296                 value               => '10',
297                 forceupdate         => { 'explanation' => 1,
298                                         'type' => 1},
299                 explanation         => 'Maximum delay to pick up a reserved document',
300                 type                => 'free',
301             },
302             {
303                 uniquefieldrequired => 'variable',
304                 variable            => 'TransfersMaxDaysWarning',
305                 value               => '3',
306                 forceupdate         => { 'explanation' => 1,
307                                         'type' => 1},
308                 explanation         => 'Max delay before considering the transfer has potentialy a problem',
309                 type                => 'free',
310             },
311             {
312                 uniquefieldrequired => 'variable',
313                 variable            => 'memberofinstitution',
314                 value               => '0',
315                 forceupdate         => { 'explanation' => 1,
316                                         'type' => 1},
317                 explanation         => 'Are your patrons members of institutions',
318                 type                => 'YesNo',
319             },
320         {
321                 uniquefieldrequired => 'variable',
322                 variable            => 'ReadingHistory',
323                 value               => '0',
324                 forceupdate         => { 'explanation' => 1,
325                                         'type' => 1},
326                 explanation         => 'Allow reading record info retrievable from issues and oldissues tables',
327                 type                => 'YesNo',
328             },
329         {
330                 uniquefieldrequired => 'variable',
331                 variable            => 'IssuingInProcess',
332                 value               => '0',
333                 forceupdate         => { 'explanation' => 1,
334                                         'type' => 1},
335                 explanation         => 'Allow no debt alert if the patron is issuing item that accumulate debt',
336                 type                => 'YesNo',
337             },
338         {
339                 uniquefieldrequired => 'variable',
340                 variable            => 'AutomaticItemReturn',
341                 value               => '1',
342                 forceupdate         => { 'explanation' => 1,
343                                         'type' => 1},
344                 explanation         => 'This Variable allow or not to return automaticly to his homebranch',
345                 type                => 'YesNo',
346             },
347         {
348                 uniquefieldrequired => 'variable',
349                 variable            => 'reviewson',
350                 value               => '0',
351                 forceupdate         => { 'explanation' => 1,
352                                         'type' => 1},
353                 explanation         => 'Allows patrons to submit reviews from the opac',
354                 type                => 'YesNo',
355             },
356         {
357                 uniquefieldrequired => 'variable',
358                 variable            => 'intranet_includes',
359                 value               => 'includes',
360                 forceupdate         => { 'explanation' => 1,
361                                         'type' => 1},
362                 explanation         => 'The includes directory you want for specific look of Koha (includes or includes_npl for example)',
363                 type                => 'Free',
364             },
365             {
366                 uniquefieldrequired => 'variable',
367                 variable            => 'AutoLocation',
368                 value               => '0',
369                 forceupdate         => { 'explanation' => 1,
370                                         'type' => 1},
371                 explanation         => 'switch to activate or not Autolocation, if Yes, the Librarian can\'t change his location, it\'s defined by branchip',
372                 type                => 'YesNo',
373             },
374             {
375                 uniquefieldrequired => 'variable',
376                 variable            => 'serialsadditems',
377                 value               => '0',
378                 forceupdate         => {
379                     'explanation' => 1,
380                     'type' => 1
381                 },
382                 explanation => 'If set, a new item will be automatically added when receiving an issue',
383                 type => 'YesNo',
384             },
385             {
386                 uniquefieldrequired => 'variable',
387                 variable            => 'expandedSearchOption',
388                 value               => '0',
389                 forceupdate         => {
390                     'explanation' => 1,
391                     'type' => 1
392                 },
393                 explanation => 'search among marc field',
394                 type => 'YesNo',
395             },
396         {
397                 uniquefieldrequired => 'variable',
398                 variable            => 'RequestOnOpac',
399                 value               => '1',
400                 forceupdate         => { 'explanation' => 1,
401                                         'type' => 1},
402                 explanation         => 'option to allow reserves on opac',
403                 type                => 'YesNo',
404             },
405         {
406                 uniquefieldrequired => 'variable',
407                 variable            => 'OpacCloud',
408                 value               => '1',
409                 forceupdate         => { 'explanation' => 1,
410                                         'type' => 1},
411                 explanation         => 'Enable / Disable cloud link on OPAC (Require to run misc/cronjobs/build_browser_and_cloud.pl on the server)',
412                 type                => 'YesNo',
413             },
414         {
415                 uniquefieldrequired => 'variable',
416                 variable            => 'OpacBrowser',
417                 value               => '1',
418                 forceupdate         => { 'explanation' => 1,
419                                         'type' => 1},
420                 explanation         => 'Enable/Disable browser link on OPAC (Require to run misc/cronjobs/build_browser_and_cloud.pl on the server)',
421                 type                => 'YesNo',
422             },
423         {
424                 uniquefieldrequired => 'variable',
425                 variable            => 'OpacTopissue',
426                 value               => '1',
427                 forceupdate         => { 'explanation' => 1,
428                                         'type' => 1},
429                 explanation         => 'Enable / Disable the top issue link on OPAC',
430                 type                => 'YesNo',
431             },
432         {
433                 uniquefieldrequired => 'variable',
434                 variable            => 'OpacAuthorities',
435                 value               => '1',
436                 forceupdate         => { 'explanation' => 1,
437                                         'type' => 1},
438                 explanation         => 'Enable / Disable the search authority link on OPAC',
439                 type                => 'YesNo',
440             },
441             {
442                 uniquefieldrequired => 'variable',
443                 variable            => 'CataloguingLog',
444                 value               => '0',
445                 forceupdate         => {'explanation' => 1, 'type' => 1},
446                 explanation         => 'Active this if you want to log cataloguing action.',
447                 type                => 'YesNo',
448             },
449             {
450                 uniquefieldrequired => 'variable',
451                 variable            => 'BorrowersLog',
452                 value               => '0',
453                 forceupdate         => {'explanation' => 1, 'type' => 1},
454                 explanation         => 'Active this if you want to log borrowers edition/creation/deletion...',
455                 type                => 'YesNo',
456             },
457             {
458                 uniquefieldrequired => 'variable',
459                 variable            => 'SubscriptionLog',
460                 value               => '0',
461                 forceupdate         => {'explanation' => 1, 'type' => 1},
462                 explanation         => 'Active this if you want to log Subscription action',
463                 type                => 'YesNo',
464             },
465             {
466                 uniquefieldrequired => 'variable',
467                 variable            => 'IssueLog',
468                 value               => '0',
469                 forceupdate         => {'explanation' => 1, 'type' => 1},
470                 explanation         => 'Active this if you want to log issue.',
471                 type                => 'YesNo',
472             },
473             {
474                 uniquefieldrequired => 'variable',
475                 variable            => 'ReturnLog',
476                 value               => '0',
477                 forceupdate         => {'explanation' => 1, 'type' => 1},
478                 explanation         => 'Active this if you want to log the circulation return',
479                 type                => 'YesNo',
480             },
481             {
482                 uniquefieldrequired => 'variable',
483                 variable            => 'Version',
484                 value               => '3.0',
485                 forceupdate         => {'explanation' => 1, 'type' => 1},
486                 explanation         => 'Koha Version',
487                 type                => 'Free',
488             },
489             {   
490                 uniquefieldrequired => 'variable',
491                 variable            => 'LetterLog',
492                 value               => '0',
493                 forceupdate         => {'explanation' => 1, 'type' => 1},
494                 explanation         => 'Active this if you want to log all the letter sent',
495                 type                => 'YesNo',
496             },
497             {
498                 uniquefieldrequired => 'variable',
499                 variable            => 'FinesLog',
500                 value               => '0',
501                 forceupdate         => {'explanation' => 1, 'type' => 1},
502                 explanation         => 'Active this if you want to log fines',
503                 type                => 'YesNo',
504             },
505             {
506                 uniquefieldrequired => 'variable',
507                 variable            => 'NoZebra',
508                 value               => '0',
509                 forceupdate         => {'explanation' => 1, 'type' => 1},
510                 explanation         => 'Active this if you want NOT to use zebra (large libraries should avoid this parameters)',
511                 type                => 'YesNo',
512             },
513             {
514                 uniquefieldrequired => 'variable',
515                 variable            => 'NoZebraIndexes',
516                 value               => '0',
517                 forceupdate         => {'explanation' => 1, 'type' => 1},
518                 explanation         => "Enter a specific hash for NoZebra indexes. Enter : 'indexname' => '100a,245a,500*','index2' => '...'",
519                 type                => 'Free',
520             },
521             {
522                 uniquefieldrequired => 'variable',
523                 variable            => 'uppercasesurnames',
524                 value               => '0',
525                 forceupdate         => {'explanation' => 1, 'type' => 1},
526                 explanation         => "Force Surnames to be uppercase",
527                 type                => 'YesNo',
528             },
529         ],
530         userflags => [
531             {
532                 uniquefieldrequired => 'bit',
533                 bit                 => '14',
534                 flag                => 'editauthorities',
535                 flagdesc            => 'allow to edit authorities',
536                 defaulton           => '0',
537             },
538             {
539                 uniquefieldrequired => 'bit',
540                 bit                 => '15',
541                 flag                 => 'serials',
542                 flagdesc            => 'allow to manage serials subscriptions',
543                 defaulton           => '0',
544             },
545             {
546                 uniquefieldrequired => 'bit',
547                 bit                 => '16',
548                 flag                 => 'reports',
549                 flagdesc            => 'allow to access to the reports module',
550                 defaulton           => '0',
551             },
552         ],
553         authorised_values => [
554             {
555                 uniquefieldrequired => 'id',
556                 category            => 'SUGGEST',
557                 authorised_value    => 'Not enough budget',
558                 lib                 => 'This book it too much expensive',
559             }
560         ],
561     );
562     
563     my %fielddefinitions = (
564     # fieldname => [
565     #    {          field => 'fieldname',
566     #             type    => 'fieldtype',
567     #             null    => '',
568     #             key     => '',
569     #             default => ''
570     #         },
571     #     ],
572         aqbasket =>  [
573             {
574                 field    => 'booksellerid',
575                 type    => 'int(11)',
576                 null    => 'NOT NULL',
577                 key        => '',
578                 default    => '1',
579                 extra    => '',
580             },
581         ],
582                 aqbookfund => [
583                         {
584                                 field  => 'bookfundid',
585                                 type   => 'varchar(5)',
586                                 null   => 'NOT NULL',
587                                 key    => 'PRI',
588                                 default => '',
589                                 extra  => '',
590                         },
591                 ],
592   
593         aqbooksellers =>  [
594             {
595                 field    => 'id',
596                 type    => 'int(11)',
597                 null    => 'NOT NULL',
598                 key        => 'PRI',
599                 default    => '',
600                 extra    => 'auto_increment',
601             },
602             {
603                 field    => 'listprice',
604                 type    => 'varchar(10)',
605                 null    => 'NULL',
606                 key        => '',
607                 default    => '',
608                 extra    => '',
609             },
610             {
611                 field    => 'invoiceprice',
612                 type    => 'varchar(10)',
613                 null    => 'NULL',
614                 key        => '',
615                 default    => '',
616                 extra    => '',
617             },
618                         {
619                                 field   => 'invoicedisc',
620                                 type    => 'float(6,4)',
621                                 null    => 'NULL',
622                                 key     => '',
623                                 default => 'NULL',
624                                 extra   => '',
625                         },
626                                 
627         ],
628         
629                 aqbudget     =>  [
630                         {
631                                 field    => 'bookfundid',
632                                 type     => 'varchar(5)',
633                                 null     => 'NOT NULL',
634                                 key      => '',
635                                 default  => '',
636                                 exra     => '',
637                          },
638                 ],
639                 
640         accountlines =>  [
641             {
642                 field    => 'notify_id',
643                 type    => 'int(11)',
644                 null    => 'NOT NULL',
645                 key        => '',
646                 default    => '0',
647                 extra    => '',
648             },
649             {
650                 field    => 'notify_level',
651                 type    => 'int(2)',
652                 null    => 'NOT NULL',
653                 key        => '',
654                 default    => '0',
655                 extra    => '',
656             },
657                         {
658                                 field   => 'accountno',
659                                 type    => 'smallint(6)',
660                                 null    => 'NOT NULL',
661                                 key     => '',
662                                 default => '0',
663                                 extra   => '',
664                         },
665                         {
666                                 field   => 'description',
667                                 type    => 'mediumtext',
668                                 null    => 'NULL',
669                         },
670                         {
671                                 field   => 'dispute',
672                                 type    => 'mediumtext',
673                                 null    => 'NULL',
674                     },
675         
676         ],
677         
678         borrowers => [
679             {    field => 'firstname',
680                 type => 'text',
681                 null => 'NULL',
682             },
683             {    field => 'initials',
684                 type => 'text',
685                 null => 'NULL',
686             },
687             {    field => 'B_email',
688                 type => 'text',
689                 null => 'NULL',
690                 after => 'B_zipcode',
691             },
692             {
693                 field => 'streetnumber', # street number (hidden if streettable table is empty)
694                 type => 'char(10)',
695                 null => 'NULL',
696                 after => 'initials',
697             },
698             {
699                 field => 'streettype', # street table, list builded from a system table
700                 type => 'char(50)',
701                 null => 'NULL',
702                 after => 'streetnumber',
703             },
704             {    field => 'phone',
705                 type => 'text',
706                 null => 'NULL',
707             },
708             {
709                 field => 'B_streetnumber', # street number (hidden if streettable table is empty)
710                 type => 'char(10)',
711                 null => 'NULL',
712                 after => 'fax',
713             },
714             {
715                 field => 'B_streettype', # street table, list builded from a system table
716                 type => 'char(50)',
717                 null => 'NULL',
718                 after => 'B_streetnumber',
719             },
720             {
721                 field => 'phonepro',
722                 type => 'text',
723                 null => 'NULL',
724                 after => 'fax',
725             },
726             {
727                 field => 'address2', # complement address
728                 type => 'text',
729                 null => 'NULL',
730                 after => 'address',
731             },
732             {
733                 field => 'emailpro',
734                 type => 'text',
735                 null => 'NULL',
736                 after => 'fax',
737             },
738             {
739                 field => 'contactfirstname', # contact's firstname
740                 type => 'text',
741                 null => 'NULL',
742                 after => 'contactname',
743             },
744             {
745                 field => 'contacttitle', # contact's title
746                 type => 'text',
747                 null => 'NULL',
748                 after => 'contactfirstname',
749             },
750             {
751                 field => 'branchcode',
752                 type  => 'varchar(10)',
753                 null  => 'NOT NULL',
754                 default    => '',
755                 extra => '',
756             },
757             {
758                 field => 'categorycode',
759                 type  => 'varchar(10)',
760                 null  => 'NOT NULL',
761                 default    => '',
762                 extra => '',
763             }
764         ],
765         
766         biblioitems =>  [
767             {
768                 field    => 'lcsort',
769                 type    => 'varchar(25)',
770                 null    => 'NULL',
771                 key        => '',
772                 default    => '',
773                 extra    => '',
774             },
775             {
776                 field    => 'ccode',
777                 type    => 'varchar(4)',
778                 null    => 'NULL',
779                 key        => '',
780                 default    => '',
781                 extra    => '',
782             },
783             {
784                 field   => 'dewey',
785                 type    => 'varchar(30)',
786                 null    => 'null',
787                 default => '',
788                 extra   => '',
789             },
790             {
791                 field   => 'publicationyear',
792                 type    => 'text',
793                 null    => 'null',
794                 default => '',
795                 extra   => '',
796             },
797             {
798                 field   => 'collectiontitle',
799                 type    => 'mediumtext',
800                 null    => 'null',
801                 default => '',
802                 extra   => '',
803             },
804             {
805                 field   => 'collectionissn',
806                 type    => 'mediumtext',
807                 null    => 'null',
808                 default => '',
809                 extra   => '',
810             },
811             {
812                 field   => 'collectionvolume',
813                 type    => 'mediumtext',
814                 null    => 'null',
815                 default => '',
816                 extra   => '',
817             },
818             {
819                 field   => 'editionstatement',
820                 type    => 'text',
821                 null    => 'null',
822                 default => '',
823                 extra   => '',
824             },
825             {
826                 field   => 'editionresponsability',
827                 type    => 'text',
828                 null    => 'null',
829                 default => '',
830                 extra   => '',
831             },
832                 
833         ],
834         deletedbiblioitems => [
835             {
836                 field   => 'dewey',
837                 type    => 'varchar(30)',
838                 null    => 'null',
839                 default => '',
840                 extra   => '',
841             },
842         ],
843         branches =>  [
844             {
845                 field    => 'branchip',
846                 type    => 'varchar(15)',
847                 null    => 'NULL',
848                 key        => '',
849                 default    => '',
850                 extra    => '',
851             },
852             {
853                 field    => 'branchprinter',
854                 type    => 'varchar(100)',
855                 null    => 'NULL',
856                 key        => '',
857                 default    => '',
858                 extra    => '',
859             },
860             {
861                 field   => 'branchcode',
862                 type    => 'varchar(10)',
863                 null    => 'NOT NULL',
864                 default => '',
865                 extra   => '',
866             }
867         ],
868         branchtransfers =>[
869             {
870                 field   => 'frombranch',
871                 type    => 'VARCHAR(10)',
872                 null    => 'NOT NULL',
873                 key     => '',
874                 default => '',
875                 extra   => '',
876             },
877             {
878                 field   => 'tobranch',
879                 type    => 'VARCHAR(10)',
880                 null    => 'NOT NULL',
881                 key     => '',
882                 default => '',
883             }
884         ],
885         
886         categories =>  [
887             {
888                 field    => 'category_type',
889                 type    => 'char(1)',
890                 null    => 'NOT NULL',
891                 key        => '',
892                 default    => 'A',
893                 extra    => '',
894             },
895             {
896                 field   => 'categorycode',
897                 type    => 'varchar(10)',
898                 null    => 'NOT NULL',
899                 key     => 'PRI',
900                 default => '',
901                 extra   => '',
902             },
903         ],
904         
905         deletedborrowers => [
906             {    field => 'firstname',
907                 type => 'text',
908                 null => 'NULL',
909             },
910             {    field => 'initials',
911                 type => 'text',
912                 null => 'NULL',
913             },
914             {    field => 'B_email',
915                 type => 'text',
916                 null => 'NULL',
917                 after => 'B_zipcode',
918             },
919             {
920                 field => 'streetnumber', # street number (hidden if streettable table is empty)
921                 type => 'char(10)',
922                 null => 'NULL',
923                 after => 'initials',
924             },
925             {
926                 field => 'streettype', # street table, list builded from a system table
927                 type => 'char(50)',
928                 null => 'NULL',
929                 after => 'streetnumber',
930             },
931             {    field => 'phone',
932                 type => 'text',
933                 null => 'NULL',
934             },
935             {
936                 field => 'B_streetnumber', # street number (hidden if streettable table is empty)
937                 type => 'char(10)',
938                 null => 'NULL',
939                 after => 'fax',
940             },
941             {
942                 field => 'B_streettype', # street table, list builded from a system table
943                 type => 'char(50)',
944                 null => 'NULL',
945                 after => 'B_streetnumber',
946             },
947             {
948                 field => 'phonepro',
949                 type => 'text',
950                 null => 'NULL',
951                 after => 'fax',
952             },
953             {
954                 field => 'address2', # complement address
955                 type => 'text',
956                 null => 'NULL',
957                 after => 'address',
958             },
959             {
960                 field => 'emailpro',
961                 type => 'text',
962                 null => 'NULL',
963                 after => 'fax',
964             },
965             {
966                 field => 'contactfirstname', # contact's firstname
967                 type => 'text',
968                 null => 'NULL',
969                 after => 'contactname',
970             },
971             {
972                 field => 'contacttitle', # contact's title
973                 type => 'text',
974                 null => 'NULL',
975                 after => 'contactfirstname',
976             },
977         ],
978         
979         issues =>  [
980             {
981                 field    => 'borrowernumber',
982                 type    => 'int(11)',
983                 null    => 'NULL', # can be null when a borrower is deleted and the foreign key rule executed
984                 key        => '',
985                 default    => '',
986                 extra    => '',
987             },
988             {
989                 field    => 'itemnumber',
990                 type    => 'int(11)',
991                 null    => 'NULL', # can be null when a borrower is deleted and the foreign key rule executed
992                 key        => '',
993                 default    => '',
994                 extra    => '',
995             },
996             {
997                 field   => 'branchcode',
998                 type    => 'varchar(10)',
999                 null    => 'NULL',
1000                 key     => '',
1001                 default => '',
1002                 extra   => '',
1003             },
1004             {
1005                 field   => 'issuedate',
1006                 type    => 'date',
1007                 null    => '',
1008                 key     => '',
1009                 default => '0000-00-00',
1010                 extra   => '',
1011             },
1012         ],
1013         
1014         items => [
1015             {
1016                 field    => 'onloan',
1017                 type    => 'date',
1018                 null    => 'NULL',
1019                 key        => '',
1020                 default    => '0000-00-00',
1021                 extra    => '',
1022             },
1023             {
1024                 field    => 'cutterextra',
1025                 type    => 'varchar(45)',
1026                 null    => 'NULL',
1027                 key        => '',
1028                 default    => '',
1029                 extra    => '',
1030             },
1031             {
1032                 field    => 'issue_date',
1033                 type    => 'date',
1034                 null    => 'NULL',
1035                 key        => '',
1036                 default    => '',
1037                 extra    => '',
1038             },
1039             {
1040                 field    => 'homebranch',
1041                 type    => 'varchar(10)',
1042                 null    => 'NULL',
1043                 key        => '',
1044                 default    => '',
1045                 extra    => '',
1046             },
1047             {
1048                 field    => 'holdingbranch',
1049                 type    => 'varchar(10)',
1050                 null    => 'NULL',
1051                 key        => '',
1052                 default    => '',
1053                 extra    => '',
1054             },
1055             {
1056                 field    => 'itype',
1057                 type    => 'varchar(10)',
1058                 null    => 'NULL',
1059                 key        => '',
1060                 default    => '',
1061                 extra    => '',
1062             },
1063         ],
1064         itemtypes => [
1065             {
1066                 field  => 'itemtype',
1067                 type   => 'varchar(10)',
1068                 default    => '',
1069                 null   => 'NOT NULL',
1070                 key    => 'PRI',
1071                 extra  => 'UNIQUE',
1072             },
1073             {
1074                 field  => 'summary',
1075                 type   => 'TEXT',
1076                 null   => 'NULL',
1077                 key    => '',
1078                 extra  => '',
1079             },
1080         ],
1081         marc_breeding => [
1082             {
1083                 field => 'marc',
1084                 type  => 'LONGBLOB',
1085                 null  => 'NULL',
1086                 key    => '',
1087                 extra  => '',
1088             }
1089         ],
1090         marc_subfield_structure => [
1091             {
1092                 field => 'defaultvalue',
1093                 type  => 'TEXT',
1094                 null  => 'NULL',
1095                 key    => '',
1096                 extra  => '',
1097             }
1098         ],
1099         opac_news => [
1100             {
1101                 field  => 'expirationdate',
1102                 type   => 'date',
1103                 null   => 'null',
1104                 key    => '',
1105                 extra  => '',
1106             },
1107             {
1108                 field   => 'number',
1109                 type    => 'int(11)',
1110                 null    => 'NULL',
1111                 key     => '',
1112                 default => '0',
1113                 extra   => '',
1114             },
1115         ],
1116         reserves =>  [
1117             {
1118                 field    => 'waitingdate',
1119                 type    => 'date',
1120                 null    => 'NULL',
1121                 key        => '',
1122                 default    => '',
1123                 extra    => '',
1124             },
1125         ],
1126         serial => [
1127             {
1128                 field   => 'notes',
1129                 type    => 'TEXT',
1130                 null    => 'NULL',
1131                 key     => '',
1132                 default => '',
1133                 extra   => ''
1134             },
1135         ],
1136         shelfcontents => [
1137             {
1138                 field => 'dateadded',
1139                 type => 'timestamp',
1140                 null    => 'NULL',
1141             },
1142         ],
1143         systempreferences =>  [
1144             {
1145                 field    => 'value',
1146                 type    => 'text',
1147                 null    => 'NULL',
1148                 key        => '',
1149                 default    => '',
1150                 extra    => '',
1151             },
1152             {
1153                 field    => 'explanation',
1154                 type    => 'text',
1155                 null    => 'NULL',
1156                 key        => '',
1157                 default    => '',
1158                 extra    => '',
1159             },
1160         ],
1161         suggestions => [
1162             {
1163                 field   => 'reason',
1164                 type    => 'text',
1165                 null    => 'NULL',
1166                 key     => ''  ,
1167                 default => '',
1168                 extra   =>    '',
1169             }
1170         ],
1171     );
1172     
1173     my %indexes = (
1174     #    table => [
1175     #         {    indexname => 'index detail'
1176     #         }
1177     #    ],
1178         aqbooksellers => [
1179             {    indexname => 'PRIMARY',
1180                 content => 'id',
1181                 type => 'PRI',
1182             }
1183         ],
1184         aqbasket => [
1185             {    indexname => 'booksellerid',
1186                 content => 'booksellerid',
1187             },
1188         ],
1189         aqorders => [
1190             {    indexname => 'basketno',
1191                 content => 'basketno',
1192             },
1193         ],
1194         aqorderbreakdown => [
1195             {    indexname => 'ordernumber',
1196                 content => 'ordernumber',
1197             },
1198             {    indexname => 'bookfundid',
1199                 content => 'bookfundid',
1200             },
1201         ],
1202         biblioitems => [
1203             {    indexname => 'isbn',
1204                 content => 'isbn',
1205             },
1206             {    indexname => 'publishercode',
1207                 content => 'publishercode',
1208             },
1209         ],
1210         branches => [
1211             {
1212                 indexname => 'branchcode',
1213                 content   => 'branchcode',
1214                 type => 'PRI',
1215             }
1216         ],
1217         branchrelations => [
1218             {
1219                 indexname => 'PRIMARY',
1220                 content => 'categorycode',
1221                 type => 'PRI',
1222             }
1223         ],
1224         branchrelations => [
1225             {    indexname => 'PRIMARY',
1226                 content => 'branchcode,categorycode',
1227                 type => 'PRI',
1228             },
1229             {    indexname => 'branchcode',
1230                 content => 'branchcode',
1231             },
1232             {    indexname => 'categorycode',
1233                 content => 'categorycode',
1234             }
1235         ],
1236         currency => [
1237             {    indexname => 'PRIMARY',
1238                 content => 'currency',
1239                 type => 'PRI',
1240             }
1241         ],
1242         categories => [
1243             {
1244                 indexname => 'categorycode',
1245                 content   => 'categorycode',
1246             }
1247         ],
1248         items => [
1249             {    indexname => 'homebranch',
1250                 content => 'homebranch',
1251             },
1252             {    indexname => 'holdingbranch',
1253                 content => 'holdingbranch',
1254             }
1255         ],
1256         itemtypes => [
1257             {
1258                 indexname => 'itemtype',
1259                 content   => 'itemtype',
1260             }
1261         ],
1262         shelfcontents => [
1263             {    indexname => 'shelfnumber',
1264                 content => 'shelfnumber',
1265             },
1266             {    indexname => 'itemnumber',
1267                 content => 'itemnumber',
1268             }
1269         ],
1270             userflags => [
1271                     {   indexname => 'PRIMARY',
1272                             content => 'bit',
1273                             type => 'PRI',
1274                     }
1275             ]
1276     );
1277     
1278     my %foreign_keys = (
1279     #    table => [
1280     #         {    key => 'the key in table' (must be indexed)
1281     #            foreigntable => 'the foreigntable name', # (the parent)
1282     #            foreignkey => 'the foreign key column(s)' # (in the parent)
1283     #            onUpdate => 'CASCADE|SET NULL|NO ACTION| RESTRICT',
1284     #            onDelete => 'CASCADE|SET NULL|NO ACTION| RESTRICT',
1285     #         }
1286     #    ],
1287         branchrelations => [
1288             {    key => 'branchcode',
1289                 foreigntable => 'branches',
1290                 foreignkey => 'branchcode',
1291                 onUpdate => 'CASCADE',
1292                 onDelete => 'CASCADE',
1293             },
1294             {    key => 'categorycode',
1295                 foreigntable => 'branchcategories',
1296                 foreignkey => 'categorycode',
1297                 onUpdate => 'CASCADE',
1298                 onDelete => 'CASCADE',
1299             },
1300         ],
1301         shelfcontents => [
1302             {    key => 'shelfnumber',
1303                 foreigntable => 'virtualshelf',
1304                 foreignkey => 'shelfnumber',
1305                 onUpdate => 'CASCADE',
1306                 onDelete => 'CASCADE',
1307             },
1308             {    key => 'itemnumber',
1309                 foreigntable => 'items',
1310                 foreignkey => 'itemnumber',
1311                 onUpdate => 'CASCADE',
1312                 onDelete => 'CASCADE',
1313             },
1314         ],
1315         # onDelete is RESTRICT on reference tables (branches, itemtype) as we don't want items to be
1316         # easily deleted, but branches/itemtype not too easy to empty...
1317         biblioitems => [
1318             {    key => 'biblionumber',
1319                 foreigntable => 'biblio',
1320                 foreignkey => 'biblionumber',
1321                 onUpdate => 'CASCADE',
1322                 onDelete => 'CASCADE',
1323             },
1324             {    key => 'itemtype',
1325                 foreigntable => 'itemtypes',
1326                 foreignkey => 'itemtype',
1327                 onUpdate => 'CASCADE',
1328                 onDelete => 'RESTRICT',
1329             },
1330         ],
1331         items => [
1332             {    key => 'biblioitemnumber',
1333                 foreigntable => 'biblioitems',
1334                 foreignkey => 'biblioitemnumber',
1335                 onUpdate => 'CASCADE',
1336                 onDelete => 'CASCADE',
1337             },
1338             {    key => 'homebranch',
1339                 foreigntable => 'branches',
1340                 foreignkey => 'branchcode',
1341                 onUpdate => 'CASCADE',
1342                 onDelete => 'RESTRICT',
1343             },
1344             {    key => 'holdingbranch',
1345                 foreigntable => 'branches',
1346                 foreignkey => 'branchcode',
1347                 onUpdate => 'CASCADE',
1348                 onDelete => 'RESTRICT',
1349             },
1350         ],
1351         aqbasket => [
1352             {    key => 'booksellerid',
1353                 foreigntable => 'aqbooksellers',
1354                 foreignkey => 'id',
1355                 onUpdate => 'CASCADE',
1356                 onDelete => 'RESTRICT',
1357             },
1358         ],
1359         aqorders => [
1360             {    key => 'basketno',
1361                 foreigntable => 'aqbasket',
1362                 foreignkey => 'basketno',
1363                 onUpdate => 'CASCADE',
1364                 onDelete => 'CASCADE',
1365             },
1366             {    key => 'biblionumber',
1367                 foreigntable => 'biblio',
1368                 foreignkey => 'biblionumber',
1369                 onUpdate => 'SET NULL',
1370                 onDelete => 'SET NULL',
1371             },
1372         ],
1373         aqbooksellers => [
1374             {    key => 'listprice',
1375                 foreigntable => 'currency',
1376                 foreignkey => 'currency',
1377                 onUpdate => 'CASCADE',
1378                 onDelete => 'CASCADE',
1379             },
1380             {    key => 'invoiceprice',
1381                 foreigntable => 'currency',
1382                 foreignkey => 'currency',
1383                 onUpdate => 'CASCADE',
1384                 onDelete => 'CASCADE',
1385             },
1386         ],
1387         aqorderbreakdown => [
1388             {    key => 'ordernumber',
1389                 foreigntable => 'aqorders',
1390                 foreignkey => 'ordernumber',
1391                 onUpdate => 'CASCADE',
1392                 onDelete => 'CASCADE',
1393             },
1394             {    key => 'bookfundid',
1395                 foreigntable => 'aqbookfund',
1396                 foreignkey => 'bookfundid',
1397                 onUpdate => 'CASCADE',
1398                 onDelete => 'CASCADE',
1399             },
1400         ],
1401         branchtransfers => [
1402             {    key => 'frombranch',
1403                 foreigntable => 'branches',
1404                 foreignkey => 'branchcode',
1405                 onUpdate => 'CASCADE',
1406                 onDelete => 'CASCADE',
1407             },
1408             {    key => 'tobranch',
1409                 foreigntable => 'branches',
1410                 foreignkey => 'branchcode',
1411                 onUpdate => 'CASCADE',
1412                 onDelete => 'CASCADE',
1413             },
1414             {    key => 'itemnumber',
1415                 foreigntable => 'items',
1416                 foreignkey => 'itemnumber',
1417                 onUpdate => 'CASCADE',
1418                 onDelete => 'CASCADE',
1419             },
1420         ],
1421         issuingrules => [
1422             {    key => 'categorycode',
1423                 foreigntable => 'categories',
1424                 foreignkey => 'categorycode',
1425                 onUpdate => 'CASCADE',
1426                 onDelete => 'CASCADE',
1427             },
1428             {    key => 'itemtype',
1429                 foreigntable => 'itemtypes',
1430                 foreignkey => 'itemtype',
1431                 onUpdate => 'CASCADE',
1432                 onDelete => 'CASCADE',
1433             },
1434         ],
1435         issues => [    # constraint is SET NULL : when a borrower or an item is deleted, we keep the issuing record
1436         # for stat purposes
1437             {    key => 'borrowernumber',
1438                 foreigntable => 'borrowers',
1439                 foreignkey => 'borrowernumber',
1440                 onUpdate => 'SET NULL',
1441                 onDelete => 'SET NULL',
1442             },
1443             {    key => 'itemnumber',
1444                 foreigntable => 'items',
1445                 foreignkey => 'itemnumber',
1446                 onUpdate => 'SET NULL',
1447                 onDelete => 'SET NULL',
1448             },
1449         ],
1450         reserves => [
1451             {    key => 'borrowernumber',
1452                 foreigntable => 'borrowers',
1453                 foreignkey => 'borrowernumber',
1454                 onUpdate => 'CASCADE',
1455                 onDelete => 'CASCADE',
1456             },
1457             {    key => 'biblionumber',
1458                 foreigntable => 'biblio',
1459                 foreignkey => 'biblionumber',
1460                 onUpdate => 'CASCADE',
1461                 onDelete => 'CASCADE',
1462             },
1463             {    key => 'itemnumber',
1464                 foreigntable => 'items',
1465                 foreignkey => 'itemnumber',
1466                 onUpdate => 'CASCADE',
1467                 onDelete => 'CASCADE',
1468             },
1469             {    key => 'branchcode',
1470                 foreigntable => 'branches',
1471                 foreignkey => 'branchcode',
1472                 onUpdate => 'CASCADE',
1473                 onDelete => 'CASCADE',
1474             },
1475         ],
1476         borrowers => [ # foreign keys are RESTRICT as we don't want to delete borrowers when a branch is deleted
1477         # but prevent deleting a branch as soon as it has 1 borrower !
1478             {    key => 'categorycode',
1479                 foreigntable => 'categories',
1480                 foreignkey => 'categorycode',
1481                 onUpdate => 'RESTRICT',
1482                 onDelete => 'RESTRICT',
1483             },
1484             {    key => 'branchcode',
1485                 foreigntable => 'branches',
1486                 foreignkey => 'branchcode',
1487                 onUpdate => 'RESTRICT',
1488                 onDelete => 'RESTRICT',
1489             },
1490         ],
1491         deletedborrowers => [ # foreign keys are RESTRICT as we don't want to delete borrowers when a branch is deleted
1492         # but prevent deleting a branch as soon as it has 1 borrower !
1493             {    key => 'categorycode',
1494                 foreigntable => 'categories',
1495                 foreignkey => 'categorycode',
1496                 onUpdate => 'RESTRICT',
1497                 onDelete => 'RESTRICT',
1498             },
1499             {    key => 'branchcode',
1500                 foreigntable => 'branches',
1501                 foreignkey => 'branchcode',
1502                 onUpdate => 'RESTRICT',
1503                 onDelete => 'RESTRICT',
1504             },
1505         ],
1506         accountlines => [
1507             {    key => 'borrowernumber',
1508                 foreigntable => 'borrowers',
1509                 foreignkey => 'borrowernumber',
1510                 onUpdate => 'CASCADE',
1511                 onDelete => 'CASCADE',
1512             },
1513             {    key => 'itemnumber',
1514                 foreigntable => 'items',
1515                 foreignkey => 'itemnumber',
1516                 onUpdate => 'SET NULL',
1517                 onDelete => 'SET NULL',
1518             },
1519         ],
1520         auth_tag_structure => [
1521             {    key => 'authtypecode',
1522                 foreigntable => 'auth_types',
1523                 foreignkey => 'authtypecode',
1524                 onUpdate => 'CASCADE',
1525                 onDelete => 'CASCADE',
1526             },
1527         ],
1528         # FIXME : don't constraint auth_*_table and auth_word, as they may be replaced by zebra
1529     );
1530     
1531     
1532     # column changes
1533     my %column_change = (
1534         # table
1535         borrowers => [
1536                     {
1537                         from => 'emailaddress',
1538                         to => 'email',
1539                         after => 'city',
1540                     },
1541                     {
1542                         from => 'streetaddress',
1543                         to => 'address',
1544                         after => 'initials',
1545                     },
1546                     {
1547                         from => 'faxnumber',
1548                         to => 'fax',
1549                         after => 'phone',
1550                     },
1551                     {
1552                         from => 'textmessaging',
1553                         to => 'opacnote',
1554                         after => 'userid',
1555                     },
1556                     {
1557                         from => 'altnotes',
1558                         to => 'contactnote',
1559                         after => 'opacnote',
1560                     },
1561                     {
1562                         from => 'physstreet',
1563                         to => 'B_address',
1564                         after => 'fax',
1565                     },
1566                     {
1567                         from => 'streetcity',
1568                         to => 'B_city',
1569                         after => 'B_address',
1570                     },
1571                     {
1572                         from => 'phoneday',
1573                         to => 'mobile',
1574                         after => 'phone',
1575                     },
1576                     {
1577                         from => 'zipcode',
1578                         to => 'zipcode',
1579                         after => 'city',
1580                     },
1581                     {
1582                         from => 'homezipcode',
1583                         to => 'B_zipcode',
1584                         after => 'B_city',
1585                     },
1586                     {
1587                         from => 'altphone',
1588                         to => 'B_phone',
1589                         after => 'B_zipcode',
1590                     },
1591                     {
1592                         from => 'expiry',
1593                         to => 'dateexpiry',
1594                         after => 'dateenrolled',
1595                     },
1596                     {
1597                         from => 'guarantor',
1598                         to => 'guarantorid',
1599                         after => 'contactname',
1600                     },
1601                     {
1602                         from => 'altrelationship',
1603                         to => 'relationship',
1604                         after => 'borrowernotes',
1605                     },
1606                 ],
1607     
1608         deletedborrowers => [
1609                     {
1610                         from => 'emailaddress',
1611                         to => 'email',
1612                         after => 'city',
1613                     },
1614                     {
1615                         from => 'streetaddress',
1616                         to => 'address',
1617                         after => 'initials',
1618                     },
1619                     {
1620                         from => 'faxnumber',
1621                         to => 'fax',
1622                         after => 'phone',
1623                     },
1624                     {
1625                         from => 'textmessaging',
1626                         to => 'opacnote',
1627                         after => 'userid',
1628                     },
1629                     {
1630                         from => 'altnotes',
1631                         to => 'contactnote',
1632                         after => 'opacnote',
1633                     },
1634                     {
1635                         from => 'physstreet',
1636                         to => 'B_address',
1637                         after => 'fax',
1638                     },
1639                     {
1640                         from => 'streetcity',
1641                         to => 'B_city',
1642                         after => 'B_address',
1643                     },
1644                     {
1645                         from => 'phoneday',
1646                         to => 'mobile',
1647                         after => 'phone',
1648                     },
1649                     {
1650                         from => 'zipcode',
1651                         to => 'zipcode',
1652                         after => 'city',
1653                     },
1654                     {
1655                         from => 'homezipcode',
1656                         to => 'B_zipcode',
1657                         after => 'B_city',
1658                     },
1659                     {
1660                         from => 'altphone',
1661                         to => 'B_phone',
1662                         after => 'B_zipcode',
1663                     },
1664                     {
1665                         from => 'expiry',
1666                         to => 'dateexpiry',
1667                         after => 'dateenrolled',
1668                     },
1669                     {
1670                         from => 'guarantor',
1671                         to => 'guarantorid',
1672                         after => 'contactname',
1673                     },
1674                     {
1675                         from => 'altrelationship',
1676                         to => 'relationship',
1677                         after => 'borrowernotes',
1678                     },
1679                 ],
1680             );
1681         
1682     
1683     # MOVE all tables TO UTF-8 and innoDB
1684     $sth = $dbh->prepare("show table status");
1685     $sth->execute;
1686     while ( my $table = $sth->fetchrow_hashref ) {
1687         next if $table->{Name} eq 'marc_word';
1688         next if $table->{Name} eq 'marc_subfield_table';
1689         next if $table->{Name} eq 'auth_word';
1690         next if $table->{Name} eq 'auth_subfield_table';
1691         if ($table->{Engine} ne 'InnoDB') {
1692             print "moving $table->{Name} to InnoDB\n";
1693             $dbh->do("ALTER TABLE $table->{Name} TYPE = innodb");
1694         }
1695         unless ($table->{Collation} =~ /^utf8/) {
1696             print "moving $table->{Name} to utf8\n";
1697             $dbh->do("ALTER TABLE $table->{Name} CONVERT TO CHARACTER SET utf8");
1698             $dbh->do("ALTER TABLE $table->{Name} DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci");
1699             # 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 !
1700         } else {
1701         }
1702     }
1703     
1704     
1705     foreach my $table (keys %column_change) {
1706         $sth = $dbh->prepare("show columns from $table");
1707         $sth->execute();
1708         undef %types;
1709         while ( ( $column, $type, $null, $key, $default, $extra ) = $sth->fetchrow )
1710         {
1711             $types{$column}->{type} ="$type";
1712             $types{$column}->{null} = "$null";
1713             $types{$column}->{key} = "$key";
1714             $types{$column}->{default} = "$default";
1715             $types{$column}->{extra} = "$extra";
1716         }    # while
1717         my $tablerows = $column_change{$table};
1718         foreach my $row ( @$tablerows ) {
1719             if ($types{$row->{from}}->{type}) {
1720                 print "altering $table $row->{from} to $row->{to}\n";
1721                 # ALTER TABLE `borrowers` CHANGE `faxnumber` `fax` TEXT CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL
1722     #             alter table `borrowers` change `faxnumber` `fax` type text  null after phone
1723                 my $sql =
1724                     "alter table `$table` change `$row->{from}` `$row->{to}` $types{$row->{from}}->{type} ".
1725                     ($types{$row->{from}}->{null} eq 'YES'?" NULL":" NOT NULL").
1726                     ($types{$row->{from}}->{default}?" default ".$types{$row->{from}}->{default}:"").
1727                     "$types{$row->{from}}->{extra} after $row->{after} ";
1728     #             print "$sql";
1729                 $dbh->do($sql);
1730             }
1731         }
1732     }
1733     
1734     # Enter here the field you want to delete from DB.
1735     # FIXME :: there is a %uselessfield before which seems doing the same things.
1736     my %fieldtodelete = (
1737         # tablename => [fieldname1,fieldname2,...]
1738     
1739     ); # %fielddelete
1740     
1741     print "removing some unused fields...\n";
1742     foreach my $table ( keys %fieldtodelete ) {
1743         foreach my $field ( @{$fieldtodelete{$table}} ){
1744             print "removing ".$field." from ".$table;
1745             my $sth = $dbh->prepare("ALTER TABLE $table DROP $field");
1746             $sth->execute;
1747             if ( $sth->err ) {
1748                 print "Error : $sth->errstr \n";
1749             }
1750         }
1751     }
1752     
1753     # Enter here the line you want to remove from DB.
1754     my %linetodelete = (
1755         # table name => where clause.
1756         userflags => "bit = 8", # delete the 'reserveforself' flags
1757         
1758     ); # %linetodelete
1759     
1760     #-------------------
1761     # Initialize
1762     
1763     # Start checking
1764     
1765     # Get version of MySQL database engine.
1766     my $mysqlversion = `mysqld --version`;
1767     $mysqlversion =~ /Ver (\S*) /;
1768     $mysqlversion = $1;
1769     if ( $mysqlversion ge '3.23' ) {
1770         print "Could convert to MyISAM database tables...\n" unless $silent;
1771     }
1772     
1773     #---------------------------------
1774     # Tables
1775     
1776     # Collect all tables into a list
1777     $sth = $dbh->prepare("show tables");
1778     $sth->execute;
1779     while ( my ($table) = $sth->fetchrow ) {
1780         $existingtables{$table} = 1;
1781     }
1782     
1783     
1784     # Now add any missing tables
1785     foreach $table ( keys %requiretables ) {
1786         unless ( $existingtables{$table} ) {
1787         print "Adding $table table...\n" unless $silent;
1788             my $sth = $dbh->prepare("create table $table $requiretables{$table}");
1789             $sth->execute;
1790             if ( $sth->err ) {
1791                 print "Error : $sth->errstr \n";
1792                 $sth->finish;
1793             }    # if error
1794         }    # unless exists
1795     }    # foreach
1796     
1797     #---------------------------------
1798     # Columns
1799     
1800     foreach $table ( keys %requirefields ) {
1801         print "Check table $table\n" if $debug and not $silent;
1802         $sth = $dbh->prepare("show columns from $table");
1803         $sth->execute();
1804         undef %types;
1805         while ( ( $column, $type, $null, $key, $default, $extra ) = $sth->fetchrow )
1806         {
1807             $types{$column} = $type;
1808         }    # while
1809         foreach $column ( keys %{ $requirefields{$table} } ) {
1810             print "  Check column $column  [$types{$column}]\n" if $debug and not $silent;
1811             if ( !$types{$column} ) {
1812     
1813                 # column doesn't exist
1814                 print "Adding $column field to $table table...\n" unless $silent;
1815                 $query = "alter table $table
1816                 add column $column " . $requirefields{$table}->{$column};
1817                 print "Execute: $query\n" if $debug;
1818                 my $sti = $dbh->prepare($query);
1819                 $sti->execute;
1820                 if ( $sti->err ) {
1821                     print "**Error : $sti->errstr \n";
1822                     $sti->finish;
1823                 }    # if error
1824             }    # if column
1825         }    # foreach column
1826     }    # foreach table
1827     
1828     foreach $table ( keys %fielddefinitions ) {
1829         print "Check table $table\n" if $debug;
1830         $sth = $dbh->prepare("show columns from $table");
1831         $sth->execute();
1832         my $definitions;
1833         while ( ( $column, $type, $null, $key, $default, $extra ) = $sth->fetchrow )
1834         {
1835             $definitions->{$column}->{type}    = $type;
1836             $definitions->{$column}->{null}    = $null;
1837             $definitions->{$column}->{null}    = 'NULL' if $null eq 'YES';
1838             $definitions->{$column}->{key}     = $key;
1839             $definitions->{$column}->{default} = $default;
1840             $definitions->{$column}->{extra}   = $extra;
1841         }    # while
1842         my $fieldrow = $fielddefinitions{$table};
1843         foreach my $row (@$fieldrow) {
1844             my $field   = $row->{field};
1845             my $type    = $row->{type};
1846             my $null    = $row->{null};
1847     #         $null    = 'YES' if $row->{null} eq 'NULL';
1848             my $key     = $row->{key};
1849             my $default = $row->{default};
1850     #         $default="''" unless $default;
1851             my $extra   = $row->{extra};
1852             my $def     = $definitions->{$field};
1853             my $after    = ($row->{after}?" after ".$row->{after}:"");
1854     
1855             unless ( $type eq $def->{type}
1856                 && $null eq $def->{null}
1857                 && $key eq $def->{key}
1858                 && $extra eq $def->{extra} )
1859             {
1860                 if ( $null eq '' ) {
1861                     $null = 'NOT NULL';
1862                 }
1863                 if ( $key eq 'PRI' ) {
1864                     $key = 'PRIMARY KEY';
1865                 }
1866                 unless ( $extra eq 'auto_increment' ) {
1867                     $extra = '';
1868                 }
1869         
1870                 # if it's a new column use "add", if it's an old one, use "change".
1871                 my $action;
1872                 if ($definitions->{$field}->{type}) {
1873                     $action="change $field"
1874                 } else {
1875                     $action="add";
1876                 }
1877     # if it's a primary key, drop the previous pk, before altering the table
1878                 print "  alter or create $field in $table\n" unless $silent;
1879                 my $query;
1880                 if ($key ne 'PRIMARY KEY') {
1881     #                 warn "alter table $table $action $field $type $null $key $extra default $default $after";
1882                     $query = "alter table $table $action $field $type $null $key $extra ".($default?"default ".$dbh->quote($default):"")." $after";
1883                 } else {
1884     #             warn "alter table $table drop primary key, $action $field $type $null $key $extra default $default $after";
1885                     # something strange : for indexes UNIQUE, they are reported as primary key here.
1886                     # but if you try to run with drop primary key, it fails.
1887                     # thus, we run the query twice, one will fail, one will succeed.
1888                     # strange...
1889                     $query="alter table $table drop primary key, $action $field $type $null $key $extra ".($default?"default ".$dbh->quote($default):"")." $after";
1890                     $query="alter table $table $action $field $type $null $key $extra ".($default?"default ".$dbh->quote($default):"")." $after";
1891                 }
1892                 $dbh->do($query);
1893             }
1894         }
1895     }
1896     
1897     print "removing some unused data...\n";
1898     foreach my $table ( keys %linetodelete ) {
1899         foreach my $where ( @{linetodelete{$table}} ){
1900             print "DELETE FROM ".$table." where ".$where;
1901             print "\n";
1902             my $sth = $dbh->prepare("DELETE FROM $table where $where");
1903             $sth->execute;
1904             if ( $sth->err ) {
1905                 print "Error : $sth->errstr \n";
1906             }
1907         }
1908     }
1909     
1910     # Populate tables with required data
1911     
1912     # synch table and deletedtable.
1913     foreach my $table (('borrowers','items','biblio','biblioitems')) {
1914         my %deletedborrowers;
1915         print "synch'ing $table and deleted$table\n";
1916         $sth = $dbh->prepare("show columns from deleted$table");
1917         $sth->execute;
1918         while ( my ( $column, $type, $null, $key, $default, $extra ) = $sth->fetchrow ) {
1919             $deletedborrowers{$column}=1;
1920         }
1921         $sth = $dbh->prepare("show columns from $table");
1922         $sth->execute;
1923         my $previous;
1924         while ( my ( $column, $type, $null, $key, $default, $extra ) = $sth->fetchrow ) {
1925             unless ($deletedborrowers{$column}) {
1926                 my $newcol="alter table deleted$table add $column $type";
1927                 if ($null eq 'YES') {
1928                     $newcol .= " NULL ";
1929                 } else {
1930                     $newcol .= " NOT NULL ";
1931                 }
1932                 $newcol .= "default ".$dbh->quote($default) if $default;
1933                 $newcol .= " after $previous" if $previous;
1934                 $previous=$column;
1935                 print "creating column $column\n";
1936                 $dbh->do($newcol);
1937             }
1938         }
1939     }
1940     #
1941     # update publisheddate 
1942     #
1943     $sth = $dbh->prepare("select count(*) from serial where publisheddate is NULL");
1944     $sth->execute;
1945     my ($emptypublished) = $sth->fetchrow;
1946     if ($emptypublished) {
1947         print "Updating publisheddate\n";
1948         $dbh->do("update serial set publisheddate=planneddate where publisheddate is NULL");
1949     }
1950     foreach my $table ( keys %tabledata ) {
1951         print "Checking for data required in table $table...\n" unless $silent;
1952         my $tablerows = $tabledata{$table};
1953         foreach my $row (@$tablerows) {
1954             my $uniquefieldrequired = $row->{uniquefieldrequired};
1955             my $uniquevalue         = $row->{$uniquefieldrequired};
1956             my $forceupdate         = $row->{forceupdate};
1957             my $sth                 =
1958             $dbh->prepare(
1959     "select $uniquefieldrequired from $table where $uniquefieldrequired=?"
1960             );
1961             $sth->execute($uniquevalue);
1962             if ($sth->rows) {
1963                 foreach my $field (keys %$forceupdate) {
1964                     if ($forceupdate->{$field}) {
1965                         my $sth=$dbh->prepare("update systempreferences set $field=? where $uniquefieldrequired=?");
1966                         $sth->execute($row->{$field}, $uniquevalue);
1967                     }
1968                 }
1969             } else {
1970                 print "Adding row to $table: " unless $silent;
1971                 my @values;
1972                 my $fieldlist;
1973                 my $placeholders;
1974                 foreach my $field ( keys %$row ) {
1975                     next if $field eq 'uniquefieldrequired';
1976                     next if $field eq 'forceupdate';
1977                     my $value = $row->{$field};
1978                     push @values, $value;
1979                     print "  $field => $value" unless $silent;
1980                     $fieldlist .= "$field,";
1981                     $placeholders .= "?,";
1982                 }
1983                 print "\n" unless $silent;
1984                 $fieldlist    =~ s/,$//;
1985                 $placeholders =~ s/,$//;
1986                 print "insert into $table ($fieldlist) values ($placeholders)";
1987                 my $sth =
1988                 $dbh->prepare(
1989                     "insert into $table ($fieldlist) values ($placeholders)");
1990                 $sth->execute(@values);
1991             }
1992         }
1993     }
1994     
1995     #
1996     # check indexes and create them when needed
1997     #
1998     print "Checking for index required...\n" unless $silent;
1999     foreach my $table ( keys %indexes ) {
2000         #
2001         # read all indexes from $table
2002         #
2003         $sth = $dbh->prepare("show index from $table");
2004         $sth->execute;
2005         my %existingindexes;
2006         while ( my ( $table, $non_unique, $key_name, $Seq_in_index, $Column_name, $Collation, $cardinality, $sub_part, $Packed, $comment ) = $sth->fetchrow ) {
2007             $existingindexes{$key_name} = 1;
2008         }
2009         # read indexes to check
2010         my $tablerows = $indexes{$table};
2011         foreach my $row (@$tablerows) {
2012             my $key_name=$row->{indexname};
2013             if ($existingindexes{$key_name} eq 1) {
2014     #             print "$key_name existing";
2015             } else {
2016                 print "\tCreating index $key_name in $table\n";
2017                 my $sql;
2018                 if ($row->{indexname} eq 'PRIMARY') {
2019                     $sql = "alter table $table ADD PRIMARY KEY ($row->{content})";
2020                 } else {
2021                     $sql = "alter table $table ADD INDEX $key_name ($row->{content}) $row->{type}";
2022                 }
2023                 $dbh->do($sql);
2024                 print "Error $sql : $dbh->err \n" if $dbh->err;
2025             }
2026         }
2027     }
2028     
2029     #
2030     # check foreign keys and create them when needed
2031     #
2032     print "Checking for foreign keys required...\n" unless $silent;
2033     foreach my $table ( keys %foreign_keys ) {
2034         #
2035         # read all indexes from $table
2036         #
2037         $sth = $dbh->prepare("show table status like '$table'");
2038         $sth->execute;
2039         my $stat = $sth->fetchrow_hashref;
2040         # read indexes to check
2041         my $tablerows = $foreign_keys{$table};
2042         foreach my $row (@$tablerows) {
2043             my $foreign_table=$row->{foreigntable};
2044             if ($stat->{'Comment'} =~/$foreign_table/) {
2045     #             print "$foreign_table existing\n";
2046             } else {
2047                 print "\tCreating foreign key $foreign_table in $table\n";
2048                 # first, drop any orphan value in child table
2049                 if ($row->{onDelete} ne "RESTRICT") {
2050                     my $sql = "delete from $table where $row->{key} not in (select $row->{foreignkey} from $row->{foreigntable})";
2051                     $dbh->do($sql);
2052                     print "SQL ERROR: $sql : $dbh->err \n" if $dbh->err;
2053                 }
2054                 my $sql="alter table $table ADD FOREIGN KEY $row->{key} ($row->{key}) REFERENCES $row->{foreigntable} ($row->{foreignkey})";
2055                 $sql .= " on update ".$row->{onUpdate} if $row->{onUpdate};
2056                 $sql .= " on delete ".$row->{onDelete} if $row->{onDelete};
2057                 $dbh->do($sql);
2058                 if ($dbh->err) {
2059                     print "====================
2060     An error occured during :
2061     \t$sql
2062     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).
2063     You can find those values with select
2064     \t$table.* from $table where $row->{key} not in (select $row->{foreignkey} from $row->{foreigntable})
2065     ====================\n
2066     ";
2067                 }
2068             }
2069         }
2070     }
2071     # now drop useless tables
2072     foreach $table ( @TableToDelete ) {
2073         if ( $existingtables{$table} ) {
2074             print "Dropping unused table $table\n" if $debug and not $silent;
2075             $dbh->do("drop table $table");
2076             if ( $dbh->err ) {
2077                 print "Error : $dbh->errstr \n";
2078             }
2079         }
2080     }
2081     
2082     #
2083     # SPECIFIC STUFF
2084     #
2085     #
2086     # create frameworkcode row in biblio table & fill it with marc_biblio.frameworkcode.
2087     #
2088     
2089     # 1st, get how many biblio we will have to do...
2090     $sth = $dbh->prepare('select count(*) from marc_biblio');
2091     $sth->execute;
2092     my ($totaltodo) = $sth->fetchrow;
2093     
2094     $sth = $dbh->prepare("show columns from biblio");
2095     $sth->execute();
2096     my $definitions;
2097     my $bibliofwexist=0;
2098     while ( ( $column, $type, $null, $key, $default, $extra ) = $sth->fetchrow ){
2099         $bibliofwexist=1 if $column eq 'frameworkcode';
2100     }
2101     unless ($bibliofwexist) {
2102         print "moving biblioframework to biblio table\n";
2103         $dbh->do('ALTER TABLE `biblio` ADD `frameworkcode` VARCHAR( 4 ) NOT NULL AFTER `biblionumber`');
2104         $sth = $dbh->prepare('select biblionumber,frameworkcode from marc_biblio');
2105         $sth->execute;
2106         my $sth_update = $dbh->prepare('update biblio set frameworkcode=? where biblionumber=?');
2107         my $totaldone=0;
2108         while (my ($biblionumber,$frameworkcode) = $sth->fetchrow) {
2109             $sth_update->execute($frameworkcode,$biblionumber);
2110             $totaldone++;
2111             print "\r$totaldone / $totaltodo" unless ($totaldone % 100);
2112         }
2113         print "\rdone\n";
2114     }
2115     
2116     # at last, remove useless fields
2117     foreach $table ( keys %uselessfields ) {
2118         my @fields = split /,/,$uselessfields{$table};
2119         my $fields;
2120         my $exists;
2121         foreach my $fieldtodrop (@fields) {
2122             $fieldtodrop =~ s/\t//g;
2123             $fieldtodrop =~ s/\n//g;
2124             $exists =0;
2125             $sth = $dbh->prepare("show columns from $table");
2126             $sth->execute;
2127             while ( my ( $column, $type, $null, $key, $default, $extra ) = $sth->fetchrow )
2128             {
2129                 $exists =1 if ($column eq $fieldtodrop);
2130             }
2131             if ($exists) {
2132                 print "deleting $fieldtodrop field in $table...\n" unless $silent;
2133                 my $sth = $dbh->prepare("alter table $table drop $fieldtodrop");
2134                 $sth->execute;
2135             }
2136         }
2137     }    # foreach
2138     
2139     #
2140     # Changing aqbookfund's primary key 
2141     #
2142     $sth=$dbh->prepare("ALTER TABLE `aqbookfund` DROP PRIMARY KEY , ADD PRIMARY KEY ( `bookfundid` , `branchcode` ) ;");
2143     $sth->execute;
2144     
2145     $sth->finish;
2146     print "upgrade to Koha 3.0 done\n";
2147     SetVersion ($DBversion);
2148 } # 3.0000000
2149
2150
2151
2152 =item
2153     Deal with virtualshelves
2154 =cut
2155
2156 $DBversion = "3.00.00.001";
2157 if (C4::Context->preference("Version") < TransformToNum($DBversion)) {
2158     # update virtualshelves table to
2159     # 
2160     $dbh->do("ALTER TABLE `bookshelf` RENAME `virtualshelves`");
2161     $dbh->do("ALTER TABLE `shelfcontents` RENAME `virtualshelfcontents`");
2162     $dbh->do("ALTER TABLE `virtualshelfcontents` ADD `biblionumber` INT( 11 ) NOT NULL");
2163     $dbh->do("UPDATE `virtualshelfcontents` SET biblionumber=(SELECT biblionumber FROM items WHERE items.itemnumber=virtualshelfcontents.itemnumber)");
2164     # drop all foreign keys : otherwise, we can't drop itemnumber field.
2165     DropAllForeignKeys('virtualshelfcontents');
2166     # create the new foreign keys (on biblionumber)
2167     $dbh->do("ALTER TABLE `virtualshelfcontents` ADD FOREIGN KEY biblionumber_fk (biblionumber) REFERENCES biblio (biblionumber) ON UPDATE CASCADE ON DELETE CASCADE");
2168     # re-create the foreign key on virtualshelf
2169     $dbh->do("ALTER TABLE `virtualshelfcontents` ADD FOREIGN KEY shelfnumber_fk (shelfnumber) REFERENCES virtualshelves (shelfnumber) ON UPDATE CASCADE ON DELETE CASCADE");
2170     # now we can drop the itemnumber column
2171     $dbh->do("ALTER TABLE `virtualshelfcontents` DROP `itemnumber`");
2172     print "Upgrade to $DBversion done (virtualshelves)\n";
2173     SetVersion ($DBversion);
2174 }
2175
2176 =item
2177     Deal with the sessions table that uses CGI::Session
2178 =cut
2179
2180 $DBversion = "3.00.00.002";
2181 if (C4::Context->preference("Version") < TransformToNum($DBversion)) {
2182     $dbh->do("DROP TABLE sessions");
2183     $dbh->do("CREATE TABLE `sessions` (
2184   `id` char(32) NOT NULL,
2185   `a_session` text NOT NULL,
2186   UNIQUE KEY `id` (`id`)
2187 ) ENGINE=InnoDB DEFAULT CHARSET=utf8;");
2188     print "Upgrade to $DBversion done (sessions uses CGI::session, new table structure for sessions)\n";
2189     SetVersion ($DBversion);
2190 }
2191
2192 =item
2193     Deal with the sessions table that uses CGI::Session
2194 =cut
2195
2196 $DBversion = "3.00.00.003";
2197 if (C4::Context->preference("Version") < TransformToNum($DBversion)) {
2198     if (C4::Context->preference("opaclanguage") eq "fr") {
2199         $dbh->do("INSERT INTO `systempreferences` (variable,value,explanation,options,type) VALUES('ReservesNeedReturns','0','Si ce paramètre est mis à 1, une réservation posée sur un exemplaire présent sur le site devra être passée en retour pour être disponible. Sinon, elle sera automatiquement disponible, Koha considère que le bibliothécaire place la réservation en ayant le document en mains','','YesNo')");
2200     } else {
2201         $dbh->do("INSERT INTO `systempreferences` (variable,value,explanation,options,type) VALUES('ReservesNeedReturns','0','If set, a reserve done on an item available in this branch need a check-in, otherwise, a reserve on a specific item, that is on the branch & available is considered as available','','YesNo')");
2202     }
2203     print "Upgrade to $DBversion done (adding ReservesNeedReturns systempref, in circulation)\n";
2204     SetVersion ($DBversion);
2205 }
2206
2207 =item DropAllForeignKeys($table)
2208
2209   Drop all foreign keys of the table $table
2210   
2211 =cut
2212
2213 sub DropAllForeignKeys {
2214     my ($table) = @_;
2215     # get the table description
2216     my $sth = $dbh->prepare("SHOW CREATE TABLE $table");
2217     $sth->execute;
2218     my $vsc_structure = $sth->fetchrow;
2219     # split on CONSTRAINT keyword
2220     my @fks = split /CONSTRAINT /,$vsc_structure;
2221     # parse each entry
2222     foreach (@fks) {
2223         # isolate what is before FOREIGN KEY, if there is something, it's a foreign key to drop
2224         $_ = /(.*) FOREIGN KEY.*/;
2225         my $id = $1;
2226         if ($id) {
2227             # we have found 1 foreign, drop it
2228             $dbh->do("ALTER TABLE $table DROP FOREIGN KEY $id");
2229             $id="";
2230         }
2231     }
2232 }
2233
2234
2235
2236
2237
2238
2239
2240 =item TransformToNum
2241
2242   Transform the Koha version from a 4 parts string
2243   to a number, with just 1 .
2244   
2245 =cut
2246
2247 sub TransformToNum {
2248     my $version = shift;
2249     # remove the 3 last . to have a Perl number
2250     $version =~ s/(.*\..*)\.(.*)\.(.*)/$1$2$3/;
2251     return $version;
2252 }
2253
2254 =item SetVersion
2255     set the DBversion in the systempreferences
2256 =cut
2257
2258 sub SetVersion {
2259     my $kohaversion = TransformToNum(shift);
2260     if (C4::Context->preference('Version')) {
2261       my $finish=$dbh->prepare("UPDATE systempreferences SET value=? WHERE variable='Version'");
2262       $finish->execute($kohaversion);
2263     } else {
2264       my $finish=$dbh->prepare("INSERT into systempreferences (variable,value,explanation) values ('Version',?,'The Koha database version. Don t change this value manually, it s holded by the webinstaller')");
2265       $finish->execute($kohaversion);
2266     }
2267 }
2268 exit;
2269
2270 # $Log$
2271 # Revision 1.172  2007/07/19 10:21:22  hdl