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