changing Session table structure (due to CGI::Sessions)
[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 ,
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         aqbooksellers =>  [
583             {
584                 field    => 'id',
585                 type    => 'int(11)',
586                 null    => 'NOT NULL',
587                 key        => 'PRI',
588                 default    => '',
589                 extra    => 'auto_increment',
590             },
591             {
592                 field    => 'listprice',
593                 type    => 'varchar(10)',
594                 null    => 'NULL',
595                 key        => '',
596                 default    => '',
597                 extra    => '',
598             },
599             {
600                 field    => 'invoiceprice',
601                 type    => 'varchar(10)',
602                 null    => 'NULL',
603                 key        => '',
604                 default    => '',
605                 extra    => '',
606             },
607         ],
608         
609         accountlines =>  [
610             {
611                 field    => 'notify_id',
612                 type    => 'int(11)',
613                 null    => 'NOT NULL',
614                 key        => '',
615                 default    => '0',
616                 extra    => '',
617             },
618             {
619                 field    => 'notify_level',
620                 type    => 'int(2)',
621                 null    => 'NOT NULL',
622                 key        => '',
623                 default    => '0',
624                 extra    => '',
625             },
626         
627         ],
628         
629         borrowers => [
630             {    field => 'firstname',
631                 type => 'text',
632                 null => 'NULL',
633             },
634             {    field => 'initials',
635                 type => 'text',
636                 null => 'NULL',
637             },
638             {    field => 'B_email',
639                 type => 'text',
640                 null => 'NULL',
641                 after => 'B_zipcode',
642             },
643             {
644                 field => 'streetnumber', # street number (hidden if streettable table is empty)
645                 type => 'char(10)',
646                 null => 'NULL',
647                 after => 'initials',
648             },
649             {
650                 field => 'streettype', # street table, list builded from a system table
651                 type => 'char(50)',
652                 null => 'NULL',
653                 after => 'streetnumber',
654             },
655             {    field => 'phone',
656                 type => 'text',
657                 null => 'NULL',
658             },
659             {
660                 field => 'B_streetnumber', # street number (hidden if streettable table is empty)
661                 type => 'char(10)',
662                 null => 'NULL',
663                 after => 'fax',
664             },
665             {
666                 field => 'B_streettype', # street table, list builded from a system table
667                 type => 'char(50)',
668                 null => 'NULL',
669                 after => 'B_streetnumber',
670             },
671             {
672                 field => 'phonepro',
673                 type => 'text',
674                 null => 'NULL',
675                 after => 'fax',
676             },
677             {
678                 field => 'address2', # complement address
679                 type => 'text',
680                 null => 'NULL',
681                 after => 'address',
682             },
683             {
684                 field => 'emailpro',
685                 type => 'text',
686                 null => 'NULL',
687                 after => 'fax',
688             },
689             {
690                 field => 'contactfirstname', # contact's firstname
691                 type => 'text',
692                 null => 'NULL',
693                 after => 'contactname',
694             },
695             {
696                 field => 'contacttitle', # contact's title
697                 type => 'text',
698                 null => 'NULL',
699                 after => 'contactfirstname',
700             },
701             {
702                 field => 'branchcode',
703                 type  => 'varchar(10)',
704                 null  => 'NOT NULL',
705                 default    => '',
706                 extra => '',
707             },
708             {
709                 field => 'categorycode',
710                 type  => 'varchar(10)',
711                 null  => 'NOT NULL',
712                 default    => '',
713                 extra => '',
714             }
715         ],
716         
717         biblioitems =>  [
718             {
719                 field    => 'lcsort',
720                 type    => 'varchar(25)',
721                 null    => 'NULL',
722                 key        => '',
723                 default    => '',
724                 extra    => '',
725             },
726             {
727                 field    => 'ccode',
728                 type    => 'varchar(4)',
729                 null    => 'NULL',
730                 key        => '',
731                 default    => '',
732                 extra    => '',
733             },
734             {
735                 field   => 'dewey',
736                 type    => 'varchar(30)',
737                 null    => 'null',
738                 default => '',
739                 extra   => '',
740             },
741             {
742                 field   => 'publicationyear',
743                 type    => 'text',
744                 null    => 'null',
745                 default => '',
746                 extra   => '',
747             },
748             {
749                 field   => 'collectiontitle',
750                 type    => 'mediumtext',
751                 null    => 'null',
752                 default => '',
753                 extra   => '',
754             },
755             {
756                 field   => 'collectionissn',
757                 type    => 'mediumtext',
758                 null    => 'null',
759                 default => '',
760                 extra   => '',
761             },
762             {
763                 field   => 'collectionvolume',
764                 type    => 'mediumtext',
765                 null    => 'null',
766                 default => '',
767                 extra   => '',
768             },
769             {
770                 field   => 'editionstatement',
771                 type    => 'text',
772                 null    => 'null',
773                 default => '',
774                 extra   => '',
775             },
776             {
777                 field   => 'editionresponsability',
778                 type    => 'text',
779                 null    => 'null',
780                 default => '',
781                 extra   => '',
782             },
783                 
784         ],
785         deletedbiblioitems => [
786             {
787                 field   => 'dewey',
788                 type    => 'varchar(30)',
789                 null    => 'null',
790                 default => '',
791                 extra   => '',
792             },
793         ],
794         branches =>  [
795             {
796                 field    => 'branchip',
797                 type    => 'varchar(15)',
798                 null    => 'NULL',
799                 key        => '',
800                 default    => '',
801                 extra    => '',
802             },
803             {
804                 field    => 'branchprinter',
805                 type    => 'varchar(100)',
806                 null    => 'NULL',
807                 key        => '',
808                 default    => '',
809                 extra    => '',
810             },
811             {
812                 field   => 'branchcode',
813                 type    => 'varchar(10)',
814                 null    => 'NOT NULL',
815                 default => '',
816                 extra   => '',
817             }
818         ],
819         branchtransfers =>[
820             {
821                 field   => 'frombranch',
822                 type    => 'VARCHAR(10)',
823                 null    => 'NOT NULL',
824                 key     => '',
825                 default => '',
826                 extra   => '',
827             },
828             {
829                 field   => 'tobranch',
830                 type    => 'VARCHAR(10)',
831                 null    => 'NOT NULL',
832                 key     => '',
833                 default => '',
834             }
835         ],
836         
837         categories =>  [
838             {
839                 field    => 'category_type',
840                 type    => 'char(1)',
841                 null    => 'NOT NULL',
842                 key        => '',
843                 default    => 'A',
844                 extra    => '',
845             },
846             {
847                 field   => 'categorycode',
848                 type    => 'varchar(10)',
849                 null    => 'NOT NULL',
850                 key     => 'PRI',
851                 default => '',
852                 extra   => '',
853             },
854         ],
855         
856         deletedborrowers => [
857             {    field => 'firstname',
858                 type => 'text',
859                 null => 'NULL',
860             },
861             {    field => 'initials',
862                 type => 'text',
863                 null => 'NULL',
864             },
865             {    field => 'B_email',
866                 type => 'text',
867                 null => 'NULL',
868                 after => 'B_zipcode',
869             },
870             {
871                 field => 'streetnumber', # street number (hidden if streettable table is empty)
872                 type => 'char(10)',
873                 null => 'NULL',
874                 after => 'initials',
875             },
876             {
877                 field => 'streettype', # street table, list builded from a system table
878                 type => 'char(50)',
879                 null => 'NULL',
880                 after => 'streetnumber',
881             },
882             {    field => 'phone',
883                 type => 'text',
884                 null => 'NULL',
885             },
886             {
887                 field => 'B_streetnumber', # street number (hidden if streettable table is empty)
888                 type => 'char(10)',
889                 null => 'NULL',
890                 after => 'fax',
891             },
892             {
893                 field => 'B_streettype', # street table, list builded from a system table
894                 type => 'char(50)',
895                 null => 'NULL',
896                 after => 'B_streetnumber',
897             },
898             {
899                 field => 'phonepro',
900                 type => 'text',
901                 null => 'NULL',
902                 after => 'fax',
903             },
904             {
905                 field => 'address2', # complement address
906                 type => 'text',
907                 null => 'NULL',
908                 after => 'address',
909             },
910             {
911                 field => 'emailpro',
912                 type => 'text',
913                 null => 'NULL',
914                 after => 'fax',
915             },
916             {
917                 field => 'contactfirstname', # contact's firstname
918                 type => 'text',
919                 null => 'NULL',
920                 after => 'contactname',
921             },
922             {
923                 field => 'contacttitle', # contact's title
924                 type => 'text',
925                 null => 'NULL',
926                 after => 'contactfirstname',
927             },
928         ],
929         
930         issues =>  [
931             {
932                 field    => 'borrowernumber',
933                 type    => 'int(11)',
934                 null    => 'NULL', # can be null when a borrower is deleted and the foreign key rule executed
935                 key        => '',
936                 default    => '',
937                 extra    => '',
938             },
939             {
940                 field    => 'itemnumber',
941                 type    => 'int(11)',
942                 null    => 'NULL', # can be null when a borrower is deleted and the foreign key rule executed
943                 key        => '',
944                 default    => '',
945                 extra    => '',
946             },
947             {
948                 field   => 'branchcode',
949                 type    => 'varchar(10)',
950                 null    => 'NULL',
951                 key     => '',
952                 default => '',
953                 extra   => '',
954             },
955             {
956                 field   => 'issuedate',
957                 type    => 'date',
958                 null    => '',
959                 key     => '',
960                 default => '0000-00-00',
961                 extra   => '',
962             },
963         ],
964         
965         items => [
966             {
967                 field    => 'onloan',
968                 type    => 'date',
969                 null    => 'NULL',
970                 key        => '',
971                 default    => '0000-00-00',
972                 extra    => '',
973             },
974             {
975                 field    => 'cutterextra',
976                 type    => 'varchar(45)',
977                 null    => 'NULL',
978                 key        => '',
979                 default    => '',
980                 extra    => '',
981             },
982             {
983                 field    => 'issue_date',
984                 type    => 'date',
985                 null    => 'NULL',
986                 key        => '',
987                 default    => '',
988                 extra    => '',
989             },
990             {
991                 field    => 'homebranch',
992                 type    => 'varchar(10)',
993                 null    => 'NULL',
994                 key        => '',
995                 default    => '',
996                 extra    => '',
997             },
998             {
999                 field    => 'holdingbranch',
1000                 type    => 'varchar(10)',
1001                 null    => 'NULL',
1002                 key        => '',
1003                 default    => '',
1004                 extra    => '',
1005             },
1006             {
1007                 field    => 'itype',
1008                 type    => 'varchar(10)',
1009                 null    => 'NULL',
1010                 key        => '',
1011                 default    => '',
1012                 extra    => '',
1013             },
1014         ],
1015         itemtypes => [
1016             {
1017                 field  => 'itemtype',
1018                 type   => 'varchar(10)',
1019                 default    => '',
1020                 null   => 'NOT NULL',
1021                 key    => 'PRI',
1022                 extra  => 'UNIQUE',
1023             },
1024             {
1025                 field  => 'summary',
1026                 type   => 'TEXT',
1027                 null   => 'NULL',
1028                 key    => '',
1029                 extra  => '',
1030             },
1031         ],
1032         marc_breeding => [
1033             {
1034                 field => 'marc',
1035                 type  => 'LONGBLOB',
1036                 null  => 'NULL',
1037                 key    => '',
1038                 extra  => '',
1039             }
1040         ],
1041         marc_subfield_structure => [
1042             {
1043                 field => 'defaultvalue',
1044                 type  => 'TEXT',
1045                 null  => 'NULL',
1046                 key    => '',
1047                 extra  => '',
1048             }
1049         ],
1050         opac_news => [
1051             {
1052                 field  => 'expirationdate',
1053                 type   => 'date',
1054                 null   => 'null',
1055                 key    => '',
1056                 extra  => '',
1057             },
1058             {
1059                 field   => 'number',
1060                 type    => 'int(11)',
1061                 null    => 'NULL',
1062                 key     => '',
1063                 default => '0',
1064                 extra   => '',
1065             },
1066         ],
1067         reserves =>  [
1068             {
1069                 field    => 'waitingdate',
1070                 type    => 'date',
1071                 null    => 'NULL',
1072                 key        => '',
1073                 default    => '',
1074                 extra    => '',
1075             },
1076         ],
1077         serial => [
1078             {
1079                 field   => 'notes',
1080                 type    => 'TEXT',
1081                 null    => 'NULL',
1082                 key     => '',
1083                 default => '',
1084                 extra   => ''
1085             },
1086         ],
1087         shelfcontents => [
1088             {
1089                 field => 'dateadded',
1090                 type => 'timestamp',
1091                 null    => 'NULL',
1092             },
1093         ],
1094         systempreferences =>  [
1095             {
1096                 field    => 'value',
1097                 type    => 'text',
1098                 null    => 'NULL',
1099                 key        => '',
1100                 default    => '',
1101                 extra    => '',
1102             },
1103             {
1104                 field    => 'explanation',
1105                 type    => 'text',
1106                 null    => 'NULL',
1107                 key        => '',
1108                 default    => '',
1109                 extra    => '',
1110             },
1111         ],
1112         suggestions => [
1113             {
1114                 field   => 'reason',
1115                 type    => 'text',
1116                 null    => 'NULL',
1117                 key     => ''  ,
1118                 default => '',
1119                 extra   =>    '',
1120             }
1121         ],
1122     );
1123     
1124     my %indexes = (
1125     #    table => [
1126     #         {    indexname => 'index detail'
1127     #         }
1128     #    ],
1129         aqbooksellers => [
1130             {    indexname => 'PRIMARY',
1131                 content => 'id',
1132                 type => 'PRI',
1133             }
1134         ],
1135         aqbasket => [
1136             {    indexname => 'booksellerid',
1137                 content => 'booksellerid',
1138             },
1139         ],
1140         aqorders => [
1141             {    indexname => 'basketno',
1142                 content => 'basketno',
1143             },
1144         ],
1145         aqorderbreakdown => [
1146             {    indexname => 'ordernumber',
1147                 content => 'ordernumber',
1148             },
1149             {    indexname => 'bookfundid',
1150                 content => 'bookfundid',
1151             },
1152         ],
1153         biblioitems => [
1154             {    indexname => 'isbn',
1155                 content => 'isbn',
1156             },
1157             {    indexname => 'publishercode',
1158                 content => 'publishercode',
1159             },
1160         ],
1161         branches => [
1162             {
1163                 indexname => 'branchcode',
1164                 content   => 'branchcode',
1165                 type => 'PRI',
1166             }
1167         ],
1168         branchrelations => [
1169             {
1170                 indexname => 'PRIMARY',
1171                 content => 'categorycode',
1172                 type => 'PRI',
1173             }
1174         ],
1175         branchrelations => [
1176             {    indexname => 'PRIMARY',
1177                 content => 'branchcode,categorycode',
1178                 type => 'PRI',
1179             },
1180             {    indexname => 'branchcode',
1181                 content => 'branchcode',
1182             },
1183             {    indexname => 'categorycode',
1184                 content => 'categorycode',
1185             }
1186         ],
1187         currency => [
1188             {    indexname => 'PRIMARY',
1189                 content => 'currency',
1190                 type => 'PRI',
1191             }
1192         ],
1193         categories => [
1194             {
1195                 indexname => 'categorycode',
1196                 content   => 'categorycode',
1197             }
1198         ],
1199         items => [
1200             {    indexname => 'homebranch',
1201                 content => 'homebranch',
1202             },
1203             {    indexname => 'holdingbranch',
1204                 content => 'holdingbranch',
1205             }
1206         ],
1207         itemtypes => [
1208             {
1209                 indexname => 'itemtype',
1210                 content   => 'itemtype',
1211             }
1212         ],
1213         shelfcontents => [
1214             {    indexname => 'shelfnumber',
1215                 content => 'shelfnumber',
1216             },
1217             {    indexname => 'itemnumber',
1218                 content => 'itemnumber',
1219             }
1220         ],
1221             userflags => [
1222                     {   indexname => 'PRIMARY',
1223                             content => 'bit',
1224                             type => 'PRI',
1225                     }
1226             ]
1227     );
1228     
1229     my %foreign_keys = (
1230     #    table => [
1231     #         {    key => 'the key in table' (must be indexed)
1232     #            foreigntable => 'the foreigntable name', # (the parent)
1233     #            foreignkey => 'the foreign key column(s)' # (in the parent)
1234     #            onUpdate => 'CASCADE|SET NULL|NO ACTION| RESTRICT',
1235     #            onDelete => 'CASCADE|SET NULL|NO ACTION| RESTRICT',
1236     #         }
1237     #    ],
1238         branchrelations => [
1239             {    key => 'branchcode',
1240                 foreigntable => 'branches',
1241                 foreignkey => 'branchcode',
1242                 onUpdate => 'CASCADE',
1243                 onDelete => 'CASCADE',
1244             },
1245             {    key => 'categorycode',
1246                 foreigntable => 'branchcategories',
1247                 foreignkey => 'categorycode',
1248                 onUpdate => 'CASCADE',
1249                 onDelete => 'CASCADE',
1250             },
1251         ],
1252         shelfcontents => [
1253             {    key => 'shelfnumber',
1254                 foreigntable => 'virtualshelf',
1255                 foreignkey => 'shelfnumber',
1256                 onUpdate => 'CASCADE',
1257                 onDelete => 'CASCADE',
1258             },
1259             {    key => 'itemnumber',
1260                 foreigntable => 'items',
1261                 foreignkey => 'itemnumber',
1262                 onUpdate => 'CASCADE',
1263                 onDelete => 'CASCADE',
1264             },
1265         ],
1266         # onDelete is RESTRICT on reference tables (branches, itemtype) as we don't want items to be
1267         # easily deleted, but branches/itemtype not too easy to empty...
1268         biblioitems => [
1269             {    key => 'biblionumber',
1270                 foreigntable => 'biblio',
1271                 foreignkey => 'biblionumber',
1272                 onUpdate => 'CASCADE',
1273                 onDelete => 'CASCADE',
1274             },
1275             {    key => 'itemtype',
1276                 foreigntable => 'itemtypes',
1277                 foreignkey => 'itemtype',
1278                 onUpdate => 'CASCADE',
1279                 onDelete => 'RESTRICT',
1280             },
1281         ],
1282         items => [
1283             {    key => 'biblioitemnumber',
1284                 foreigntable => 'biblioitems',
1285                 foreignkey => 'biblioitemnumber',
1286                 onUpdate => 'CASCADE',
1287                 onDelete => 'CASCADE',
1288             },
1289             {    key => 'homebranch',
1290                 foreigntable => 'branches',
1291                 foreignkey => 'branchcode',
1292                 onUpdate => 'CASCADE',
1293                 onDelete => 'RESTRICT',
1294             },
1295             {    key => 'holdingbranch',
1296                 foreigntable => 'branches',
1297                 foreignkey => 'branchcode',
1298                 onUpdate => 'CASCADE',
1299                 onDelete => 'RESTRICT',
1300             },
1301         ],
1302         aqbasket => [
1303             {    key => 'booksellerid',
1304                 foreigntable => 'aqbooksellers',
1305                 foreignkey => 'id',
1306                 onUpdate => 'CASCADE',
1307                 onDelete => 'RESTRICT',
1308             },
1309         ],
1310         aqorders => [
1311             {    key => 'basketno',
1312                 foreigntable => 'aqbasket',
1313                 foreignkey => 'basketno',
1314                 onUpdate => 'CASCADE',
1315                 onDelete => 'CASCADE',
1316             },
1317             {    key => 'biblionumber',
1318                 foreigntable => 'biblio',
1319                 foreignkey => 'biblionumber',
1320                 onUpdate => 'SET NULL',
1321                 onDelete => 'SET NULL',
1322             },
1323         ],
1324         aqbooksellers => [
1325             {    key => 'listprice',
1326                 foreigntable => 'currency',
1327                 foreignkey => 'currency',
1328                 onUpdate => 'CASCADE',
1329                 onDelete => 'CASCADE',
1330             },
1331             {    key => 'invoiceprice',
1332                 foreigntable => 'currency',
1333                 foreignkey => 'currency',
1334                 onUpdate => 'CASCADE',
1335                 onDelete => 'CASCADE',
1336             },
1337         ],
1338         aqorderbreakdown => [
1339             {    key => 'ordernumber',
1340                 foreigntable => 'aqorders',
1341                 foreignkey => 'ordernumber',
1342                 onUpdate => 'CASCADE',
1343                 onDelete => 'CASCADE',
1344             },
1345             {    key => 'bookfundid',
1346                 foreigntable => 'aqbookfund',
1347                 foreignkey => 'bookfundid',
1348                 onUpdate => 'CASCADE',
1349                 onDelete => 'CASCADE',
1350             },
1351         ],
1352         branchtransfers => [
1353             {    key => 'frombranch',
1354                 foreigntable => 'branches',
1355                 foreignkey => 'branchcode',
1356                 onUpdate => 'CASCADE',
1357                 onDelete => 'CASCADE',
1358             },
1359             {    key => 'tobranch',
1360                 foreigntable => 'branches',
1361                 foreignkey => 'branchcode',
1362                 onUpdate => 'CASCADE',
1363                 onDelete => 'CASCADE',
1364             },
1365             {    key => 'itemnumber',
1366                 foreigntable => 'items',
1367                 foreignkey => 'itemnumber',
1368                 onUpdate => 'CASCADE',
1369                 onDelete => 'CASCADE',
1370             },
1371         ],
1372         issuingrules => [
1373             {    key => 'categorycode',
1374                 foreigntable => 'categories',
1375                 foreignkey => 'categorycode',
1376                 onUpdate => 'CASCADE',
1377                 onDelete => 'CASCADE',
1378             },
1379             {    key => 'itemtype',
1380                 foreigntable => 'itemtypes',
1381                 foreignkey => 'itemtype',
1382                 onUpdate => 'CASCADE',
1383                 onDelete => 'CASCADE',
1384             },
1385         ],
1386         issues => [    # constraint is SET NULL : when a borrower or an item is deleted, we keep the issuing record
1387         # for stat purposes
1388             {    key => 'borrowernumber',
1389                 foreigntable => 'borrowers',
1390                 foreignkey => 'borrowernumber',
1391                 onUpdate => 'SET NULL',
1392                 onDelete => 'SET NULL',
1393             },
1394             {    key => 'itemnumber',
1395                 foreigntable => 'items',
1396                 foreignkey => 'itemnumber',
1397                 onUpdate => 'SET NULL',
1398                 onDelete => 'SET NULL',
1399             },
1400         ],
1401         reserves => [
1402             {    key => 'borrowernumber',
1403                 foreigntable => 'borrowers',
1404                 foreignkey => 'borrowernumber',
1405                 onUpdate => 'CASCADE',
1406                 onDelete => 'CASCADE',
1407             },
1408             {    key => 'biblionumber',
1409                 foreigntable => 'biblio',
1410                 foreignkey => 'biblionumber',
1411                 onUpdate => 'CASCADE',
1412                 onDelete => 'CASCADE',
1413             },
1414             {    key => 'itemnumber',
1415                 foreigntable => 'items',
1416                 foreignkey => 'itemnumber',
1417                 onUpdate => 'CASCADE',
1418                 onDelete => 'CASCADE',
1419             },
1420             {    key => 'branchcode',
1421                 foreigntable => 'branches',
1422                 foreignkey => 'branchcode',
1423                 onUpdate => 'CASCADE',
1424                 onDelete => 'CASCADE',
1425             },
1426         ],
1427         borrowers => [ # foreign keys are RESTRICT as we don't want to delete borrowers when a branch is deleted
1428         # but prevent deleting a branch as soon as it has 1 borrower !
1429             {    key => 'categorycode',
1430                 foreigntable => 'categories',
1431                 foreignkey => 'categorycode',
1432                 onUpdate => 'RESTRICT',
1433                 onDelete => 'RESTRICT',
1434             },
1435             {    key => 'branchcode',
1436                 foreigntable => 'branches',
1437                 foreignkey => 'branchcode',
1438                 onUpdate => 'RESTRICT',
1439                 onDelete => 'RESTRICT',
1440             },
1441         ],
1442         deletedborrowers => [ # foreign keys are RESTRICT as we don't want to delete borrowers when a branch is deleted
1443         # but prevent deleting a branch as soon as it has 1 borrower !
1444             {    key => 'categorycode',
1445                 foreigntable => 'categories',
1446                 foreignkey => 'categorycode',
1447                 onUpdate => 'RESTRICT',
1448                 onDelete => 'RESTRICT',
1449             },
1450             {    key => 'branchcode',
1451                 foreigntable => 'branches',
1452                 foreignkey => 'branchcode',
1453                 onUpdate => 'RESTRICT',
1454                 onDelete => 'RESTRICT',
1455             },
1456         ],
1457         accountlines => [
1458             {    key => 'borrowernumber',
1459                 foreigntable => 'borrowers',
1460                 foreignkey => 'borrowernumber',
1461                 onUpdate => 'CASCADE',
1462                 onDelete => 'CASCADE',
1463             },
1464             {    key => 'itemnumber',
1465                 foreigntable => 'items',
1466                 foreignkey => 'itemnumber',
1467                 onUpdate => 'SET NULL',
1468                 onDelete => 'SET NULL',
1469             },
1470         ],
1471         auth_tag_structure => [
1472             {    key => 'authtypecode',
1473                 foreigntable => 'auth_types',
1474                 foreignkey => 'authtypecode',
1475                 onUpdate => 'CASCADE',
1476                 onDelete => 'CASCADE',
1477             },
1478         ],
1479         # FIXME : don't constraint auth_*_table and auth_word, as they may be replaced by zebra
1480     );
1481     
1482     
1483     # column changes
1484     my %column_change = (
1485         # table
1486         borrowers => [
1487                     {
1488                         from => 'emailaddress',
1489                         to => 'email',
1490                         after => 'city',
1491                     },
1492                     {
1493                         from => 'streetaddress',
1494                         to => 'address',
1495                         after => 'initials',
1496                     },
1497                     {
1498                         from => 'faxnumber',
1499                         to => 'fax',
1500                         after => 'phone',
1501                     },
1502                     {
1503                         from => 'textmessaging',
1504                         to => 'opacnote',
1505                         after => 'userid',
1506                     },
1507                     {
1508                         from => 'altnotes',
1509                         to => 'contactnote',
1510                         after => 'opacnote',
1511                     },
1512                     {
1513                         from => 'physstreet',
1514                         to => 'B_address',
1515                         after => 'fax',
1516                     },
1517                     {
1518                         from => 'streetcity',
1519                         to => 'B_city',
1520                         after => 'B_address',
1521                     },
1522                     {
1523                         from => 'phoneday',
1524                         to => 'mobile',
1525                         after => 'phone',
1526                     },
1527                     {
1528                         from => 'zipcode',
1529                         to => 'zipcode',
1530                         after => 'city',
1531                     },
1532                     {
1533                         from => 'homezipcode',
1534                         to => 'B_zipcode',
1535                         after => 'B_city',
1536                     },
1537                     {
1538                         from => 'altphone',
1539                         to => 'B_phone',
1540                         after => 'B_zipcode',
1541                     },
1542                     {
1543                         from => 'expiry',
1544                         to => 'dateexpiry',
1545                         after => 'dateenrolled',
1546                     },
1547                     {
1548                         from => 'guarantor',
1549                         to => 'guarantorid',
1550                         after => 'contactname',
1551                     },
1552                     {
1553                         from => 'altrelationship',
1554                         to => 'relationship',
1555                         after => 'borrowernotes',
1556                     },
1557                 ],
1558     
1559         deletedborrowers => [
1560                     {
1561                         from => 'emailaddress',
1562                         to => 'email',
1563                         after => 'city',
1564                     },
1565                     {
1566                         from => 'streetaddress',
1567                         to => 'address',
1568                         after => 'initials',
1569                     },
1570                     {
1571                         from => 'faxnumber',
1572                         to => 'fax',
1573                         after => 'phone',
1574                     },
1575                     {
1576                         from => 'textmessaging',
1577                         to => 'opacnote',
1578                         after => 'userid',
1579                     },
1580                     {
1581                         from => 'altnotes',
1582                         to => 'contactnote',
1583                         after => 'opacnote',
1584                     },
1585                     {
1586                         from => 'physstreet',
1587                         to => 'B_address',
1588                         after => 'fax',
1589                     },
1590                     {
1591                         from => 'streetcity',
1592                         to => 'B_city',
1593                         after => 'B_address',
1594                     },
1595                     {
1596                         from => 'phoneday',
1597                         to => 'mobile',
1598                         after => 'phone',
1599                     },
1600                     {
1601                         from => 'zipcode',
1602                         to => 'zipcode',
1603                         after => 'city',
1604                     },
1605                     {
1606                         from => 'homezipcode',
1607                         to => 'B_zipcode',
1608                         after => 'B_city',
1609                     },
1610                     {
1611                         from => 'altphone',
1612                         to => 'B_phone',
1613                         after => 'B_zipcode',
1614                     },
1615                     {
1616                         from => 'expiry',
1617                         to => 'dateexpiry',
1618                         after => 'dateenrolled',
1619                     },
1620                     {
1621                         from => 'guarantor',
1622                         to => 'guarantorid',
1623                         after => 'contactname',
1624                     },
1625                     {
1626                         from => 'altrelationship',
1627                         to => 'relationship',
1628                         after => 'borrowernotes',
1629                     },
1630                 ],
1631             );
1632         
1633     
1634     # MOVE all tables TO UTF-8 and innoDB
1635     $sth = $dbh->prepare("show table status");
1636     $sth->execute;
1637     while ( my $table = $sth->fetchrow_hashref ) {
1638         next if $table->{Name} eq 'marc_word';
1639         next if $table->{Name} eq 'marc_subfield_table';
1640         next if $table->{Name} eq 'auth_word';
1641         next if $table->{Name} eq 'auth_subfield_table';
1642         if ($table->{Engine} ne 'InnoDB') {
1643             $dbh->do("ALTER TABLE $table->{Name} TYPE = innodb");
1644             print "moving $table->{Name} to InnoDB\n";
1645         }
1646         unless ($table->{Collation} =~ /^utf8/) {
1647             print "moving $table->{Name} to utf8\n";
1648             $dbh->do("ALTER TABLE $table->{Name} CONVERT TO CHARACTER SET utf8");
1649             $dbh->do("ALTER TABLE $table->{Name} DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci");
1650             # 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 !
1651         } else {
1652         }
1653     }
1654     
1655     
1656     foreach my $table (keys %column_change) {
1657         $sth = $dbh->prepare("show columns from $table");
1658         $sth->execute();
1659         undef %types;
1660         while ( ( $column, $type, $null, $key, $default, $extra ) = $sth->fetchrow )
1661         {
1662             $types{$column}->{type} ="$type";
1663             $types{$column}->{null} = "$null";
1664             $types{$column}->{key} = "$key";
1665             $types{$column}->{default} = "$default";
1666             $types{$column}->{extra} = "$extra";
1667         }    # while
1668         my $tablerows = $column_change{$table};
1669         foreach my $row ( @$tablerows ) {
1670             if ($types{$row->{from}}->{type}) {
1671                 print "altering $table $row->{from} to $row->{to}\n";
1672                 # ALTER TABLE `borrowers` CHANGE `faxnumber` `fax` TEXT CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL
1673     #             alter table `borrowers` change `faxnumber` `fax` type text  null after phone
1674                 my $sql =
1675                     "alter table `$table` change `$row->{from}` `$row->{to}` $types{$row->{from}}->{type} ".
1676                     ($types{$row->{from}}->{null} eq 'YES'?" NULL":" NOT NULL").
1677                     ($types{$row->{from}}->{default}?" default ".$types{$row->{from}}->{default}:"").
1678                     "$types{$row->{from}}->{extra} after $row->{after} ";
1679     #             print "$sql";
1680                 $dbh->do($sql);
1681             }
1682         }
1683     }
1684     
1685     # Enter here the field you want to delete from DB.
1686     # FIXME :: there is a %uselessfield before which seems doing the same things.
1687     my %fieldtodelete = (
1688         # tablename => [fieldname1,fieldname2,...]
1689     
1690     ); # %fielddelete
1691     
1692     print "removing some unused fields...\n";
1693     foreach my $table ( keys %fieldtodelete ) {
1694         foreach my $field ( @{$fieldtodelete{$table}} ){
1695             print "removing ".$field." from ".$table;
1696             my $sth = $dbh->prepare("ALTER TABLE $table DROP $field");
1697             $sth->execute;
1698             if ( $sth->err ) {
1699                 print "Error : $sth->errstr \n";
1700             }
1701         }
1702     }
1703     
1704     # Enter here the line you want to remove from DB.
1705     my %linetodelete = (
1706         # table name => where clause.
1707         userflags => "bit = 8", # delete the 'reserveforself' flags
1708         
1709     ); # %linetodelete
1710     
1711     #-------------------
1712     # Initialize
1713     
1714     # Start checking
1715     
1716     # Get version of MySQL database engine.
1717     my $mysqlversion = `mysqld --version`;
1718     $mysqlversion =~ /Ver (\S*) /;
1719     $mysqlversion = $1;
1720     if ( $mysqlversion ge '3.23' ) {
1721         print "Could convert to MyISAM database tables...\n" unless $silent;
1722     }
1723     
1724     #---------------------------------
1725     # Tables
1726     
1727     # Collect all tables into a list
1728     $sth = $dbh->prepare("show tables");
1729     $sth->execute;
1730     while ( my ($table) = $sth->fetchrow ) {
1731         $existingtables{$table} = 1;
1732     }
1733     
1734     
1735     # Now add any missing tables
1736     foreach $table ( keys %requiretables ) {
1737         unless ( $existingtables{$table} ) {
1738         print "Adding $table table...\n" unless $silent;
1739             my $sth = $dbh->prepare("create table $table $requiretables{$table}");
1740             $sth->execute;
1741             if ( $sth->err ) {
1742                 print "Error : $sth->errstr \n";
1743                 $sth->finish;
1744             }    # if error
1745         }    # unless exists
1746     }    # foreach
1747     
1748     #---------------------------------
1749     # Columns
1750     
1751     foreach $table ( keys %requirefields ) {
1752         print "Check table $table\n" if $debug and not $silent;
1753         $sth = $dbh->prepare("show columns from $table");
1754         $sth->execute();
1755         undef %types;
1756         while ( ( $column, $type, $null, $key, $default, $extra ) = $sth->fetchrow )
1757         {
1758             $types{$column} = $type;
1759         }    # while
1760         foreach $column ( keys %{ $requirefields{$table} } ) {
1761             print "  Check column $column  [$types{$column}]\n" if $debug and not $silent;
1762             if ( !$types{$column} ) {
1763     
1764                 # column doesn't exist
1765                 print "Adding $column field to $table table...\n" unless $silent;
1766                 $query = "alter table $table
1767                 add column $column " . $requirefields{$table}->{$column};
1768                 print "Execute: $query\n" if $debug;
1769                 my $sti = $dbh->prepare($query);
1770                 $sti->execute;
1771                 if ( $sti->err ) {
1772                     print "**Error : $sti->errstr \n";
1773                     $sti->finish;
1774                 }    # if error
1775             }    # if column
1776         }    # foreach column
1777     }    # foreach table
1778     
1779     foreach $table ( keys %fielddefinitions ) {
1780         print "Check table $table\n" if $debug;
1781         $sth = $dbh->prepare("show columns from $table");
1782         $sth->execute();
1783         my $definitions;
1784         while ( ( $column, $type, $null, $key, $default, $extra ) = $sth->fetchrow )
1785         {
1786             $definitions->{$column}->{type}    = $type;
1787             $definitions->{$column}->{null}    = $null;
1788             $definitions->{$column}->{null}    = 'NULL' if $null eq 'YES';
1789             $definitions->{$column}->{key}     = $key;
1790             $definitions->{$column}->{default} = $default;
1791             $definitions->{$column}->{extra}   = $extra;
1792         }    # while
1793         my $fieldrow = $fielddefinitions{$table};
1794         foreach my $row (@$fieldrow) {
1795             my $field   = $row->{field};
1796             my $type    = $row->{type};
1797             my $null    = $row->{null};
1798     #         $null    = 'YES' if $row->{null} eq 'NULL';
1799             my $key     = $row->{key};
1800             my $default = $row->{default};
1801     #         $default="''" unless $default;
1802             my $extra   = $row->{extra};
1803             my $def     = $definitions->{$field};
1804             my $after    = ($row->{after}?" after ".$row->{after}:"");
1805     
1806             unless ( $type eq $def->{type}
1807                 && $null eq $def->{null}
1808                 && $key eq $def->{key}
1809                 && $extra eq $def->{extra} )
1810             {
1811                 if ( $null eq '' ) {
1812                     $null = 'NOT NULL';
1813                 }
1814                 if ( $key eq 'PRI' ) {
1815                     $key = 'PRIMARY KEY';
1816                 }
1817                 unless ( $extra eq 'auto_increment' ) {
1818                     $extra = '';
1819                 }
1820         
1821                 # if it's a new column use "add", if it's an old one, use "change".
1822                 my $action;
1823                 if ($definitions->{$field}->{type}) {
1824                     $action="change $field"
1825                 } else {
1826                     $action="add";
1827                 }
1828     # if it's a primary key, drop the previous pk, before altering the table
1829                 print "  alter or create $field in $table\n" unless $silent;
1830                 my $query;
1831                 if ($key ne 'PRIMARY KEY') {
1832     #                 warn "alter table $table $action $field $type $null $key $extra default $default $after";
1833                     $query = "alter table $table $action $field $type $null $key $extra ".($default?"default ".$dbh->quote($default):"")." $after";
1834                 } else {
1835     #             warn "alter table $table drop primary key, $action $field $type $null $key $extra default $default $after";
1836                     # something strange : for indexes UNIQUE, they are reported as primary key here.
1837                     # but if you try to run with drop primary key, it fails.
1838                     # thus, we run the query twice, one will fail, one will succeed.
1839                     # strange...
1840                     $query="alter table $table drop primary key, $action $field $type $null $key $extra ".($default?"default ".$dbh->quote($default):"")." $after";
1841                     $query="alter table $table $action $field $type $null $key $extra ".($default?"default ".$dbh->quote($default):"")." $after";
1842                 }
1843                 $dbh->do($query);
1844             }
1845         }
1846     }
1847     
1848     print "removing some unused data...\n";
1849     foreach my $table ( keys %linetodelete ) {
1850         foreach my $where ( @{linetodelete{$table}} ){
1851             print "DELETE FROM ".$table." where ".$where;
1852             print "\n";
1853             my $sth = $dbh->prepare("DELETE FROM $table where $where");
1854             $sth->execute;
1855             if ( $sth->err ) {
1856                 print "Error : $sth->errstr \n";
1857             }
1858         }
1859     }
1860     
1861     # Populate tables with required data
1862     
1863     # synch table and deletedtable.
1864     foreach my $table (('borrowers','items','biblio','biblioitems')) {
1865         my %deletedborrowers;
1866         print "synch'ing $table and deleted$table\n";
1867         $sth = $dbh->prepare("show columns from deleted$table");
1868         $sth->execute;
1869         while ( my ( $column, $type, $null, $key, $default, $extra ) = $sth->fetchrow ) {
1870             $deletedborrowers{$column}=1;
1871         }
1872         $sth = $dbh->prepare("show columns from $table");
1873         $sth->execute;
1874         my $previous;
1875         while ( my ( $column, $type, $null, $key, $default, $extra ) = $sth->fetchrow ) {
1876             unless ($deletedborrowers{$column}) {
1877                 my $newcol="alter table deleted$table add $column $type";
1878                 if ($null eq 'YES') {
1879                     $newcol .= " NULL ";
1880                 } else {
1881                     $newcol .= " NOT NULL ";
1882                 }
1883                 $newcol .= "default ".$dbh->quote($default) if $default;
1884                 $newcol .= " after $previous" if $previous;
1885                 $previous=$column;
1886                 print "creating column $column\n";
1887                 $dbh->do($newcol);
1888             }
1889         }
1890     }
1891     #
1892     # update publisheddate 
1893     #
1894     $sth = $dbh->prepare("select count(*) from serial where publisheddate is NULL");
1895     $sth->execute;
1896     my ($emptypublished) = $sth->fetchrow;
1897     if ($emptypublished) {
1898         print "Updating publisheddate\n";
1899         $dbh->do("update serial set publisheddate=planneddate where publisheddate is NULL");
1900     }
1901     foreach my $table ( keys %tabledata ) {
1902         print "Checking for data required in table $table...\n" unless $silent;
1903         my $tablerows = $tabledata{$table};
1904         foreach my $row (@$tablerows) {
1905             my $uniquefieldrequired = $row->{uniquefieldrequired};
1906             my $uniquevalue         = $row->{$uniquefieldrequired};
1907             my $forceupdate         = $row->{forceupdate};
1908             my $sth                 =
1909             $dbh->prepare(
1910     "select $uniquefieldrequired from $table where $uniquefieldrequired=?"
1911             );
1912             $sth->execute($uniquevalue);
1913             if ($sth->rows) {
1914                 foreach my $field (keys %$forceupdate) {
1915                     if ($forceupdate->{$field}) {
1916                         my $sth=$dbh->prepare("update systempreferences set $field=? where $uniquefieldrequired=?");
1917                         $sth->execute($row->{$field}, $uniquevalue);
1918                     }
1919                 }
1920             } else {
1921                 print "Adding row to $table: " unless $silent;
1922                 my @values;
1923                 my $fieldlist;
1924                 my $placeholders;
1925                 foreach my $field ( keys %$row ) {
1926                     next if $field eq 'uniquefieldrequired';
1927                     next if $field eq 'forceupdate';
1928                     my $value = $row->{$field};
1929                     push @values, $value;
1930                     print "  $field => $value" unless $silent;
1931                     $fieldlist .= "$field,";
1932                     $placeholders .= "?,";
1933                 }
1934                 print "\n" unless $silent;
1935                 $fieldlist    =~ s/,$//;
1936                 $placeholders =~ s/,$//;
1937                 print "insert into $table ($fieldlist) values ($placeholders)";
1938                 my $sth =
1939                 $dbh->prepare(
1940                     "insert into $table ($fieldlist) values ($placeholders)");
1941                 $sth->execute(@values);
1942             }
1943         }
1944     }
1945     
1946     #
1947     # check indexes and create them when needed
1948     #
1949     print "Checking for index required...\n" unless $silent;
1950     foreach my $table ( keys %indexes ) {
1951         #
1952         # read all indexes from $table
1953         #
1954         $sth = $dbh->prepare("show index from $table");
1955         $sth->execute;
1956         my %existingindexes;
1957         while ( my ( $table, $non_unique, $key_name, $Seq_in_index, $Column_name, $Collation, $cardinality, $sub_part, $Packed, $comment ) = $sth->fetchrow ) {
1958             $existingindexes{$key_name} = 1;
1959         }
1960         # read indexes to check
1961         my $tablerows = $indexes{$table};
1962         foreach my $row (@$tablerows) {
1963             my $key_name=$row->{indexname};
1964             if ($existingindexes{$key_name} eq 1) {
1965     #             print "$key_name existing";
1966             } else {
1967                 print "\tCreating index $key_name in $table\n";
1968                 my $sql;
1969                 if ($row->{indexname} eq 'PRIMARY') {
1970                     $sql = "alter table $table ADD PRIMARY KEY ($row->{content})";
1971                 } else {
1972                     $sql = "alter table $table ADD INDEX $key_name ($row->{content}) $row->{type}";
1973                 }
1974                 $dbh->do($sql);
1975                 print "Error $sql : $dbh->err \n" if $dbh->err;
1976             }
1977         }
1978     }
1979     
1980     #
1981     # check foreign keys and create them when needed
1982     #
1983     print "Checking for foreign keys required...\n" unless $silent;
1984     foreach my $table ( keys %foreign_keys ) {
1985         #
1986         # read all indexes from $table
1987         #
1988         $sth = $dbh->prepare("show table status like '$table'");
1989         $sth->execute;
1990         my $stat = $sth->fetchrow_hashref;
1991         # read indexes to check
1992         my $tablerows = $foreign_keys{$table};
1993         foreach my $row (@$tablerows) {
1994             my $foreign_table=$row->{foreigntable};
1995             if ($stat->{'Comment'} =~/$foreign_table/) {
1996     #             print "$foreign_table existing\n";
1997             } else {
1998                 print "\tCreating foreign key $foreign_table in $table\n";
1999                 # first, drop any orphan value in child table
2000                 if ($row->{onDelete} ne "RESTRICT") {
2001                     my $sql = "delete from $table where $row->{key} not in (select $row->{foreignkey} from $row->{foreigntable})";
2002                     $dbh->do($sql);
2003                     print "SQL ERROR: $sql : $dbh->err \n" if $dbh->err;
2004                 }
2005                 my $sql="alter table $table ADD FOREIGN KEY $row->{key} ($row->{key}) REFERENCES $row->{foreigntable} ($row->{foreignkey})";
2006                 $sql .= " on update ".$row->{onUpdate} if $row->{onUpdate};
2007                 $sql .= " on delete ".$row->{onDelete} if $row->{onDelete};
2008                 $dbh->do($sql);
2009                 if ($dbh->err) {
2010                     print "====================
2011     An error occured during :
2012     \t$sql
2013     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).
2014     You can find those values with select
2015     \t$table.* from $table where $row->{key} not in (select $row->{foreignkey} from $row->{foreigntable})
2016     ====================\n
2017     ";
2018                 }
2019             }
2020         }
2021     }
2022     # now drop useless tables
2023     foreach $table ( @TableToDelete ) {
2024         if ( $existingtables{$table} ) {
2025             print "Dropping unused table $table\n" if $debug and not $silent;
2026             $dbh->do("drop table $table");
2027             if ( $dbh->err ) {
2028                 print "Error : $dbh->errstr \n";
2029             }
2030         }
2031     }
2032     
2033     #
2034     # SPECIFIC STUFF
2035     #
2036     #
2037     # create frameworkcode row in biblio table & fill it with marc_biblio.frameworkcode.
2038     #
2039     
2040     # 1st, get how many biblio we will have to do...
2041     $sth = $dbh->prepare('select count(*) from marc_biblio');
2042     $sth->execute;
2043     my ($totaltodo) = $sth->fetchrow;
2044     
2045     $sth = $dbh->prepare("show columns from biblio");
2046     $sth->execute();
2047     my $definitions;
2048     my $bibliofwexist=0;
2049     while ( ( $column, $type, $null, $key, $default, $extra ) = $sth->fetchrow ){
2050         $bibliofwexist=1 if $column eq 'frameworkcode';
2051     }
2052     unless ($bibliofwexist) {
2053         print "moving biblioframework to biblio table\n";
2054         $dbh->do('ALTER TABLE `biblio` ADD `frameworkcode` VARCHAR( 4 ) NOT NULL AFTER `biblionumber`');
2055         $sth = $dbh->prepare('select biblionumber,frameworkcode from marc_biblio');
2056         $sth->execute;
2057         my $sth_update = $dbh->prepare('update biblio set frameworkcode=? where biblionumber=?');
2058         my $totaldone=0;
2059         while (my ($biblionumber,$frameworkcode) = $sth->fetchrow) {
2060             $sth_update->execute($frameworkcode,$biblionumber);
2061             $totaldone++;
2062             print "\r$totaldone / $totaltodo" unless ($totaldone % 100);
2063         }
2064         print "\rdone\n";
2065     }
2066     
2067     # at last, remove useless fields
2068     foreach $table ( keys %uselessfields ) {
2069         my @fields = split /,/,$uselessfields{$table};
2070         my $fields;
2071         my $exists;
2072         foreach my $fieldtodrop (@fields) {
2073             $fieldtodrop =~ s/\t//g;
2074             $fieldtodrop =~ s/\n//g;
2075             $exists =0;
2076             $sth = $dbh->prepare("show columns from $table");
2077             $sth->execute;
2078             while ( my ( $column, $type, $null, $key, $default, $extra ) = $sth->fetchrow )
2079             {
2080                 $exists =1 if ($column eq $fieldtodrop);
2081             }
2082             if ($exists) {
2083                 print "deleting $fieldtodrop field in $table...\n" unless $silent;
2084                 my $sth = $dbh->prepare("alter table $table drop $fieldtodrop");
2085                 $sth->execute;
2086             }
2087         }
2088     }    # foreach
2089     
2090     #
2091     # Changing aqbookfund's primary key 
2092     #
2093     $sth=$dbh->prepare("ALTER TABLE `aqbookfund` DROP PRIMARY KEY , ADD PRIMARY KEY ( `bookfundid` , `branchcode` ) ;");
2094     $sth->execute;
2095     
2096     $sth->finish;
2097     print "upgrade to Koha 3.0 done\n";
2098     SetVersion ($DBversion);
2099 } # 3.0000000
2100
2101
2102
2103 =item
2104     Deal with virtualshelves
2105 =cut
2106
2107 $DBversion = "3.00.00.001";
2108 if (C4::Context->preference("Version") < TransformToNum($DBversion)) {
2109     # update virtualshelves table to
2110     # 
2111     $dbh->do("ALTER TABLE `bookshelf` RENAME `virtualshelves`");
2112     $dbh->do("ALTER TABLE `shelfcontents` RENAME `virtualshelfcontents`");
2113     $dbh->do("ALTER TABLE `virtualshelfcontents` ADD `biblionumber` INT( 11 ) NOT NULL");
2114     $dbh->do("UPDATE `virtualshelfcontents` SET biblionumber=(SELECT biblionumber FROM items WHERE items.itemnumber=virtualshelfcontents.itemnumber)");
2115     # drop all foreign keys : otherwise, we can't drop itemnumber field.
2116     DropAllForeignKeys('virtualshelfcontents');
2117     # create the new foreign keys (on biblionumber)
2118     $dbh->do("ALTER TABLE `virtualshelfcontents` ADD FOREIGN KEY biblionumber_fk (biblionumber) REFERENCES biblio (biblionumber) ON UPDATE CASCADE ON DELETE CASCADE");
2119     # re-create the foreign key on virtualshelf
2120     $dbh->do("ALTER TABLE `virtualshelfcontents` ADD FOREIGN KEY shelfnumber_fk (shelfnumber) REFERENCES virtualshelves (shelfnumber) ON UPDATE CASCADE ON DELETE CASCADE");
2121     # now we can drop the itemnumber column
2122     $dbh->do("ALTER TABLE `virtualshelfcontents` DROP `itemnumber`");
2123     print "Upgrade to $DBversion done (virtualshelves)\n";
2124     SetVersion ($DBversion);
2125 }
2126
2127 =item
2128     Deal with the sessions table that uses CGI::Session
2129 =cut
2130
2131 $DBversion = "3.00.00.002";
2132 if (C4::Context->preference("Version") < TransformToNum($DBversion)) {
2133     $dbh->do("DROP TABLE sessions");
2134     $dbh->do("CREATE TABLE `sessions` (
2135   `id` char(32) NOT NULL,
2136   `a_session` text NOT NULL,
2137   UNIQUE KEY `id` (`id`)
2138 ) ENGINE=InnoDB DEFAULT CHARSET=utf8;");
2139     print "Upgrade to $DBversion done (sessions uses CGI::session, new table structure for sessions)\n";
2140     SetVersion ($DBversion);
2141 }
2142
2143
2144 =item DropAllForeignKeys($table)
2145
2146   Drop all foreign keys of the table $table
2147   
2148 =cut
2149
2150 sub DropAllForeignKeys {
2151     my ($table) = @_;
2152     # get the table description
2153     my $sth = $dbh->prepare("SHOW CREATE TABLE $table");
2154     $sth->execute;
2155     my $vsc_structure = $sth->fetchrow;
2156     # split on CONSTRAINT keyword
2157     my @fks = split /CONSTRAINT /,$vsc_structure;
2158     # parse each entry
2159     foreach (@fks) {
2160         # isolate what is before FOREIGN KEY, if there is something, it's a foreign key to drop
2161         $_ = /(.*) FOREIGN KEY.*/;
2162         my $id = $1;
2163         if ($id) {
2164             # we have found 1 foreign, drop it
2165             $dbh->do("ALTER TABLE $table DROP FOREIGN KEY $id");
2166             $id="";
2167         }
2168     }
2169 }
2170
2171
2172
2173
2174
2175
2176
2177 =item TransformToNum
2178
2179   Transform the Koha version from a 4 parts string
2180   to a number, with just 1 .
2181   
2182 =cut
2183
2184 sub TransformToNum {
2185     my $version = shift;
2186     # remove the 3 last . to have a Perl number
2187     $version =~ s/(.*\..*)\.(.*)\.(.*)/$1$2$3/;
2188     return $version;
2189 }
2190
2191 =item SetVersion
2192     set the DBversion in the systempreferences
2193 =cut
2194
2195 sub SetVersion {
2196     my $kohaversion = TransformToNum(shift);
2197     if (C4::Context->preference('Version')) {
2198       my $finish=$dbh->prepare("UPDATE systempreferences SET value=? WHERE variable='Version'");
2199       $finish->execute($kohaversion);
2200     } else {
2201       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')");
2202       $finish->execute($kohaversion);
2203     }
2204 }
2205 exit;
2206
2207 # $Log$
2208 # Revision 1.172  2007/07/19 10:21:22  hdl