* removing too verbose messages.
[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
16 use strict;
17
18 # CPAN modules
19 use DBI;
20
21 # Koha modules
22 use C4::Context;
23
24 # FIXME - The user might be installing a new database, so can't rely
25 # on /etc/koha.conf anyway.
26
27 my $debug = 0;
28
29 my (
30     $sth, $sti,
31     $query,
32     %existingtables,    # tables already in database
33     %types,
34     $table,
35     $column,
36     $type, $null, $key, $default, $extra,
37     $prefitem,          # preference item in systempreferences table
38 );
39
40 my $dbh = C4::Context->dbh;
41 print "connected to your DB. Checking & modifying it\n";
42
43 #-------------------
44 # Defines
45
46 # Tables to add if they don't exist
47 my %requiretables = (
48     shelfcontents => "( shelfnumber int not null,
49                                                         itemnumber int not null,
50                                                         flags int)",
51     bookshelf => "( shelfnumber int auto_increment primary key,
52                                                 shelfname char(255))",
53     z3950queue => "( id int auto_increment primary key,
54                                                 term text,
55                                                 type char(10),
56                                                 startdate int,
57                                                 enddate int,
58                                                 done smallint,
59                                                 results longblob,
60                                                 numrecords int,
61                                                 servers text,
62                                                 identifier char(30))",
63     z3950results => "( id int auto_increment primary key,
64                                                 queryid int,
65                                                 server char(255),
66                                                 startdate int,
67                                                 enddate int,
68                                                 results longblob,
69                                                 numrecords int,
70                                                 numdownloaded int,
71                                                 highestseen int,
72                                                 active smallint)",
73     branchrelations => "( branchcode varchar(4),
74                                                         categorycode varchar(4))",
75     websites => "( websitenumber int(11) NOT NULL auto_increment,
76                                                 biblionumber int(11) NOT NULL default '0',
77                                                 title text,
78                                                 description text,
79                                                 url varchar(255),
80                                                 PRIMARY KEY (websitenumber) )",
81     marcrecorddone => "( isbn char(40),
82                                                                 issn char(40),
83                                                                 lccn char(40),
84                                                                 controlnumber char(40))",
85     uploadedmarc => "( id int(11) NOT NULL auto_increment PRIMARY KEY,
86                                                         marc longblob,
87                                                         hidden smallint(6) default NULL,
88                                                         name varchar(255) default NULL)",
89     ethnicity => "( code varchar(10) NOT NULL default '',
90                                         name varchar(255) default NULL,
91                                         PRIMARY KEY  (code)   )",
92     sessions => "( sessionID varchar(255) NOT NULL default '',
93                                                 userid varchar(255) default NULL,
94                                                 ip varchar(16) default NULL,
95                                                 lasttime int,
96                                                 PRIMARY KEY (sessionID)   )",
97     sessionqueries => "( sessionID varchar(255) NOT NULL default '',
98                                                                 userid char(100) NOT NULL default '',
99                                                                 ip char(18) NOT NULL default '',
100                                                                 url text NOT NULL default ''  )",
101     bibliothesaurus => "( id bigint(20) NOT NULL auto_increment,
102                                                         freelib char(255) NOT NULL default '',
103                                                         stdlib char(255) NOT NULL default '',
104                                                         category char(10) NOT NULL default '',
105                                                         level tinyint(4) NOT NULL default '1',
106                                                         hierarchy char(80) NOT NULL default '',
107                                                         father char(80) NOT NULL default '',
108                                                         PRIMARY KEY  (id),
109                                                         KEY freelib (freelib),
110                                                         KEY stdlib (stdlib),
111                                                         KEY category (category),
112                                                         KEY hierarchy (hierarchy)
113                                                         )",
114     marc_biblio => "(
115                                                 bibid bigint(20) unsigned NOT NULL auto_increment,
116                                                 biblionumber int(11) NOT NULL default '0',
117                                                 datecreated date NOT NULL default '0000-00-00',
118                                                 datemodified date default NULL,
119                                                 origincode char(20) default NULL,
120                                                 PRIMARY KEY  (bibid),
121                                                 KEY origincode (origincode),
122                                                 KEY biblionumber (biblionumber)
123                                                 ) ",
124     marc_blob_subfield => "(
125                                         blobidlink bigint(20) NOT NULL auto_increment,
126                                         subfieldvalue longtext NOT NULL,
127                                         PRIMARY KEY  (blobidlink)
128                                         ) ",
129     marc_subfield_structure => "(
130                                                 tagfield char(3) NOT NULL default '',
131                                                 tagsubfield char(1) NOT NULL default '',
132                                                 liblibrarian char(255) NOT NULL default '',
133                                                 libopac char(255) NOT NULL default '',
134                                                 repeatable tinyint(4) NOT NULL default '0',
135                                                 mandatory tinyint(4) NOT NULL default '0',
136                                                 kohafield char(40)  default NULL,
137                                                 tab tinyint(1) default NULL,
138                                                 authorised_value char(10) default NULL,
139                                                 thesaurus_category char(10) default NULL,
140                                                 value_builder char(80) default NULL,
141                                                 PRIMARY KEY  (tagfield,tagsubfield),
142                                                 KEY kohafield (kohafield),
143                                                 KEY tab (tab)
144                                                 )",
145     marc_subfield_table => "(
146                                                 subfieldid bigint(20) unsigned NOT NULL auto_increment,
147                                                 bibid bigint(20) unsigned NOT NULL default '0',
148                                                 tag char(3) NOT NULL default '',
149                                                 tagorder tinyint(4) NOT NULL default '1',
150                                                 tag_indicator char(2) NOT NULL default '',
151                                                 subfieldcode char(1) NOT NULL default '',
152                                                 subfieldorder tinyint(4) NOT NULL default '1',
153                                                 subfieldvalue varchar(255) default NULL,
154                                                 valuebloblink bigint(20) default NULL,
155                                                 PRIMARY KEY  (subfieldid),
156                                                 KEY bibid (bibid),
157                                                 KEY tag (tag),
158                                                 KEY tag_indicator (tag_indicator),
159                                                 KEY subfieldorder (subfieldorder),
160                                                 KEY subfieldcode (subfieldcode),
161                                                 KEY subfieldvalue (subfieldvalue),
162                                                 KEY tagorder (tagorder)
163                                         )",
164     marc_tag_structure => "(
165                                         tagfield char(3) NOT NULL default '',
166                                         liblibrarian char(255) NOT NULL default '',
167                                         libopac char(255) NOT NULL default '',
168                                         repeatable tinyint(4) NOT NULL default '0',
169                                         mandatory tinyint(4) NOT NULL default '0',
170                                         authorised_value char(10) default NULL,
171                                         PRIMARY KEY  (tagfield)
172                                         )",
173     marc_word => "(
174                                 bibid bigint(20) NOT NULL default '0',
175                                 tag char(3) NOT NULL default '',
176                                 tagorder tinyint(4) NOT NULL default '1',
177                                 subfieldid char(1) NOT NULL default '',
178                                 subfieldorder tinyint(4) NOT NULL default '1',
179                                 word varchar(255) NOT NULL default '',
180                                 sndx_word varchar(255) NOT NULL default '',
181                                 KEY bibid (bibid),
182                                 KEY tag (tag),
183                                 KEY tagorder (tagorder),
184                                 KEY subfieldid (subfieldid),
185                                 KEY subfieldorder (subfieldorder),
186                                 KEY word (word),
187                                 KEY sndx_word (sndx_word)
188                         )",
189     marc_breeding => "(  id bigint(20) NOT NULL auto_increment,
190                                 file varchar(80) NOT NULL default '',
191                                 isbn varchar(10) NOT NULL default '',
192                                 title varchar(128) default NULL,
193                                 author varchar(80) default NULL,
194                                 marc text NOT NULL,
195                                 encoding varchar(40) default NULL,
196                                 PRIMARY KEY  (id),
197                                 KEY title (title),
198                                 KEY isbn (isbn)
199                         )",
200     authorised_values => "(id int(11) NOT NULL auto_increment,
201                                 category char(10) NOT NULL default '',
202                                 authorised_value char(80) NOT NULL default '',
203                                 lib char(80) NULL,
204                                 PRIMARY KEY  (id),
205                                 KEY name (category)
206                         )",
207     userflags => "( bit int(11) NOT NULL default '0',
208                                 flag char(30), flagdesc char(255),
209                                 defaulton int(11)
210                         )",
211 );
212
213 my %requirefields = (
214     biblio        => { 'abstract' => 'text' },
215     deletedbiblio => { 'abstract' => 'text', 'marc' => 'blob' },
216     deleteditems => { 'marc' => 'blob' },
217     biblioitems   => {
218         'lccn' => 'char(25)',
219         'url'  => 'varchar(255)',
220         'marc' => 'text'
221     },
222     deletedbiblioitems => {
223         'lccn' => 'char(25)',
224         'url'  => 'varchar(255)',
225         'marc' => 'text'
226     },
227     branchtransfers => { 'datearrived'    => 'datetime' },
228     statistics      => { 'borrowernumber' => 'int(11)' },
229     aqbooksellers   => {
230         'invoicedisc' => 'float(6,4)',
231         'nocalc'      => 'int(11)'
232     },
233     borrowers => {
234         'userid'        => 'char(30)',
235         'password'      => 'char(30)',
236         'flags'         => 'int(11)',
237         'textmessaging' => 'varchar(30)',
238            'zipcode' => 'varchar(25)',
239                         'homezipcode' => 'varchar(25)',
240     },
241     aqorders => { 'budgetdate' => 'date' },
242     aqbudget => {'aqbudgetid' => 'tinyint(4) auto_increment primary key'},
243
244     #added so that reference items are not available for reserves...
245     itemtypes         => { 'notforloan'  => 'smallint(6)' },
246     systempreferences => { 'explanation' => 'char(80)',
247                            'type' => 'char(20)',
248                            'options' => 'text' },
249     z3950servers      => { 'syntax'      => 'char(80)' },
250 );
251
252 my %dropable_table = (
253     classification => 'classification',
254     multipart      => 'multipart',
255     multivolume    => 'multivolume',
256     newitems       => 'newitems',
257     procedures     => 'procedures',
258     publisher      => 'publisher',
259     searchstats    => 'searchstats',
260     serialissues   => 'serialissues',
261 );
262
263 # the other hash contains other actions that can't be done elsewhere. they are done
264 # either BEFORE of AFTER everything else, depending on "when" entry (default => AFTER)
265
266 # The tabledata hash contains data that should be in the tables.
267 # The uniquefieldrequired hash entry is used to determine which (if any) fields
268 # must not exist in the table for this row to be inserted.  If the
269 # uniquefieldrequired entry is already in the table, the existing data is not
270 # modified, unless the forceupdate hash entry is also set.  Fields in the
271 # anonymous "forceupdate" hash will be forced to be updated to the default
272 # values given in the %tabledata hash.
273
274 my %tabledata = (
275     userflags => [
276         {
277             uniquefieldrequired => 'bit',
278             bit                 => 0,
279             flag                => 'superlibrarian',
280             flagdesc            => 'Access to all librarian functions',
281             defaulton           => 0
282         },
283         {
284             uniquefieldrequired => 'bit',
285             bit                 => 1,
286             flag                => 'circulate',
287             flagdesc            => 'Circulate books',
288             defaulton           => 0
289         },
290         {
291             uniquefieldrequired => 'bit',
292             bit                 => 2,
293             flag                => 'catalogue',
294             flagdesc            => 'View Catalogue (Librarian Interface)',
295             defaulton           => 0
296         },
297         {
298             uniquefieldrequired => 'bit',
299             bit                 => 3,
300             flag                => 'parameters',
301             flagdesc            => 'Set Koha system paramters',
302             defaulton           => 0
303         },
304         {
305             uniquefieldrequired => 'bit',
306             bit                 => 4,
307             flag                => 'borrowers',
308             flagdesc            => 'Add or modify borrowers',
309             defaulton           => 0
310         },
311         {
312             uniquefieldrequired => 'bit',
313             bit                 => 5,
314             flag                => 'permissions',
315             flagdesc            => 'Set user permissions',
316             defaulton           => 0
317         },
318         {
319             uniquefieldrequired => 'bit',
320             bit                 => 6,
321             flag                => 'reserveforothers',
322             flagdesc            => 'Reserve books for patrons',
323             defaulton           => 0
324         },
325         {
326             uniquefieldrequired => 'bit',
327             bit                 => 7,
328             flag                => 'borrow',
329             flagdesc            => 'Borrow books',
330             defaulton           => 1
331         },
332         {
333             uniquefieldrequired => 'bit',
334             bit                 => 8,
335             flag                => 'reserveforself',
336             flagdesc            => 'Reserve books for self',
337             defaulton           => 0
338         },
339         {
340             uniquefieldrequired => 'bit',
341             bit                 => 9,
342             flag                => 'editcatalogue',
343             flagdesc  => 'Edit Catalogue (Modify bibliographic/holdings data)',
344             defaulton => 0
345         },
346         {
347             uniquefieldrequired => 'bit',
348             bit                 => 10,
349             flag                => 'updatecharges',
350             flagdesc            => 'Update borrower charges',
351             defaulton           => 0
352         },
353     ],
354     systempreferences => [
355         {
356             uniquefieldrequired => 'variable',
357             forceupdate         => { 'explanation' => 1,
358                                      'type' => 1 },
359             variable            => 'autoMemberNum',
360             value               => '1',
361             explanation         => 'Member number is auto-calculated',
362             type                => 'YesNo'
363
364         },
365         {
366             uniquefieldrequired => 'variable',
367             forceupdate         => { 'explanation' => 1,
368                                      'type' => 1,
369                                      'options' => 1 },
370             variable            => 'acquisitions',
371             value               => 'simple',
372             explanation         =>
373 'Normal, budget-based acquisitions, or Simple bibliographic-data acquisitions',
374             type                => 'Choice',
375             options             => 'simple|normal'
376         },
377         {
378             uniquefieldrequired => 'variable',
379             forceupdate         => { 'explanation' => 1,
380                                      'type' => 1,
381                                      'options' => 1 },
382             variable            => 'dateformat',
383             value               => 'metric',
384             explanation         =>
385             'date format (US mm/dd/yyyy, metric dd/mm/yyy, ISO yyyy/mm/dd)',
386             type                => 'Choice',
387             options             => 'metric|us|iso'
388         },
389         {
390             uniquefieldrequired => 'variable',
391             variable            => 'template',
392             forceupdate         => { 'explanation' => 1,
393                                      'type' => 1 },
394             value               => 'default',
395             explanation         => 'Preference order for intranet interface templates',
396             type                => 'Themes'
397         },
398         {
399             uniquefieldrequired => 'variable',
400             variable            => 'autoBarcode',
401             forceupdate         => { 'explanation' => 1,
402                                      'type' => 1 },
403             value               => 'yes',
404             explanation         => 'Barcode is auto-calculated',
405             type                => 'YesNo'
406         },
407         {
408             uniquefieldrequired => 'variable',
409             variable            => 'insecure',
410             forceupdate         => { 'explanation' => 1,
411                                      'type' => 1 },
412             value               => 'no',
413             explanation         =>
414 'If YES, no auth at all is needed. Be careful if you set this to yes!',
415             type                => 'YesNo'
416         },
417         {
418             uniquefieldrequired => 'variable',
419             variable            => 'authoritysep',
420             forceupdate         => { 'explanation' => 1,
421                                      'type' => 1,
422                                      'options' => 1 },
423             value               => '--',
424             explanation         =>
425             'the separator used in authority/thesaurus. Usually --',
426             type                => 'free',
427             options             => '10'
428         },
429         {
430             uniquefieldrequired => 'variable',
431             variable            => 'opaclanguages',
432             forceupdate         => { 'explanation' => 1,
433                                      'type' => 1 },
434             value               => 'en',
435             explanation         => 'Set the preferred order for translations.  The top language will be tried first.',
436             type                => 'Languages'
437         },
438         {
439             uniquefieldrequired => 'variable',
440             variable            => 'opacthemes',
441             forceupdate         => { 'explanation' => 1,
442                                      'type' => 1 },
443             value               => 'default',
444             explanation         => 'Set the preferred order for themes.  The top theme will be tried first.',
445             type                => 'Themes'
446         },
447         {
448             uniquefieldrequired => 'variable',
449             variable            => 'timeout',
450             forceupdate         => { 'explanation' => 1,
451                                      'type' => 1 },
452             value               => '1200',
453             explanation         => 'Inactivity timeout for cookies authentication (in seconds)',
454             type                => 'Integer'
455         },
456         {
457             uniquefieldrequired => 'variable',
458             variable            => 'marc',
459             forceupdate         => { 'explanation' => 1,
460                                      'type' => 1 },
461             value               => 'yes',
462             explanation         => 'Turn on MARC support',
463             type                => 'YesNo'
464         },
465         {
466             uniquefieldrequired => 'variable',
467             variable            => 'marcflavour',
468             forceupdate         => { 'explanation' => 1,
469                                      'type' => 1,
470                                      'options' => 1},
471             value               => 'MARC21',
472             explanation         =>
473             'your MARC flavor (MARC21 or UNIMARC) used for character encoding',
474             type                => 'Choice',
475             options             => 'MARC21|UNIMARC'
476         },
477         {
478             uniquefieldrequired => 'variable',
479             variable            => 'checkdigit',
480             value               => 'none',
481             forceupdate         => { 'explanation' => 1,
482                                      'type' => 1,
483                                      'options' => 1},
484             explanation         => 'Validity checks on membership number: none or "Katipo" style checks',
485             type                => 'Choice',
486             options             => 'none|katipo'
487         },
488         {
489             uniquefieldrequired => 'variable',
490             variable            => 'maxoutstanding',
491             forceupdate         => { 'explanation' => 1,
492                                      'type' => 1 },
493             value               => '5',
494             explanation         =>
495             'maximum amount withstanding to be able make reserves ',
496             type                => 'Integer'
497         },
498         {
499             uniquefieldrequired => 'variable',
500             variable            => 'maxreserves',
501             forceupdate         => { 'explanation' => 1,
502                                      'type' => 1 },
503             value               => '5',
504             explanation         =>
505             'maximum number of reserves a member can make',
506             type                => 'Integer'
507
508         },
509         {
510             uniquefieldrequired => 'variable',
511             variable            => 'noissuescharge',
512             forceupdate         => { 'explanation' => 1,
513                                      'type' => 1 },
514             value               => '5',
515             explanation         =>
516             'maximum amount withstanding to be able to check out an item',
517             type                => 'Integer'
518
519         },
520         {
521             uniquefieldrequired => 'variable',
522             variable            => 'KohaAdminEmailAddress',
523             forceupdate         => { 'explanation' => 1,
524                                      'type' => 1 },
525             value               => 'your.mail@here',
526             explanation => 'the email address where borrowers modifs are sent',
527             type                => 'free'
528         },
529         {
530             uniquefieldrequired => 'variable',
531             variable            => 'gist',
532             forceupdate         => { 'explanation' => 1,
533                                      'type' => 1 },
534             value               => '0.125',
535             explanation => 'the gist rate. NOT in %, but in numeric form (0.12 for 12%)',
536             type                => 'free'
537         },
538     ],
539
540 );
541
542 my %fielddefinitions = (
543     printers => [
544         {
545             field   => 'printername',
546             type    => 'char(40)',
547             null    => '',
548             key     => 'PRI',
549             default => ''
550         },
551     ],
552     aqbookfund => [
553         {
554             field   => 'bookfundid',
555             type    => 'char(5)',
556             null    => '',
557             key     => 'PRI',
558             default => ''
559         },
560     ],
561     aqbudget => [
562         {
563             field   => 'aqbudgetid',
564             type    => 'tinyint(4)',
565             null    => '',
566             key     => 'PRI',
567                   default =>'',
568             extra => 'auto_increment'
569         },
570     ],
571     z3950servers => [
572         {
573             field   => 'id',
574             type    => 'int',
575             null    => '',
576             key     => 'PRI',
577             default => '',
578             extra   => 'auto_increment'
579         },
580     ],
581         marc_breeding => [
582         {
583             field   => 'z3950random',
584             type    => 'varchar(40)',
585             null    => 'NULL',
586             key     => '',
587             default => '',
588             extra   => ''
589         },
590         {
591             field   => 'encoding',
592             type    => 'varchar(40)',
593             null    => '',
594             key     => '',
595             default => '',
596             extra   => ''
597         },
598     ],
599 );
600
601 #-------------------
602 # Initialize
603
604 # Start checking
605
606 # Get version of MySQL database engine.
607 my $mysqlversion = `mysqld --version`;
608 $mysqlversion =~ /Ver (\S*) /;
609 $mysqlversion = $1;
610 if ( $mysqlversion ge '3.23' ) {
611     print "Could convert to MyISAM database tables...\n";
612 }
613
614 #---------------------------------
615 # Tables
616
617 # Collect all tables into a list
618 $sth = $dbh->prepare("show tables");
619 $sth->execute;
620 while ( my ($table) = $sth->fetchrow ) {
621     $existingtables{$table} = 1;
622 }
623
624
625 # Now add any missing tables
626 foreach $table ( keys %requiretables ) {
627     unless ( $existingtables{$table} ) {
628         print "Adding $table table...\n";
629         my $sth = $dbh->prepare("create table $table $requiretables{$table}");
630         $sth->execute;
631         if ( $sth->err ) {
632             print "Error : $sth->errstr \n";
633             $sth->finish;
634         }    # if error
635     }    # unless exists
636 }    # foreach
637
638 # now drop useless tables
639 foreach $table ( keys %dropable_table ) {
640         if ( $existingtables{$table} ) {
641                 print "Dropping unused table $table\n" if $debug;
642                 $dbh->do("drop table $table");
643                 if ( $dbh->err ) {
644                         print "Error : $dbh->errstr \n";
645                 }
646         }
647 }
648 unless ( $existingtables{'z3950servers'} ) {
649         #MJR: added syntax entries to close bug 624
650     print "Adding z3950servers table...\n";
651     my $sti = $dbh->prepare( "create table z3950servers (
652                                                                                 host char(255),
653                                                                                 port int,
654                                                                                 db char(255),
655                                                                                 userid char(255),
656                                                                                 password char(255),
657                                                                                 name text,
658                                                                                 id int,
659                                                                                 checked smallint,
660                                                                                 rank int,
661                                                                                 syntax char(80))"
662     );
663     $sti->execute;
664     $sti = $dbh->prepare( "insert into z3950servers
665                                                                 values ('z3950.loc.gov',
666                                                                 7090,
667                                                                 'voyager',
668                                                                 '', '',
669                                                                 'Library of Congress',
670                                                                 1, 1, 1, 'USMARC')"
671     );
672     $sti->execute;
673 }
674
675 #---------------------------------
676 # Columns
677
678 foreach $table ( keys %requirefields ) {
679     print "Check table $table\n" if $debug;
680     $sth = $dbh->prepare("show columns from $table");
681     $sth->execute();
682     undef %types;
683     while ( ( $column, $type, $null, $key, $default, $extra ) = $sth->fetchrow )
684     {
685         $types{$column} = $type;
686     }    # while
687     foreach $column ( keys %{ $requirefields{$table} } ) {
688         print "  Check column $column  [$types{$column}]\n" if $debug;
689         if ( !$types{$column} ) {
690
691             # column doesn't exist
692             print "Adding $column field to $table table...\n";
693             $query = "alter table $table
694                         add column $column " . $requirefields{$table}->{$column};
695             print "Execute: $query\n" if $debug;
696             my $sti = $dbh->prepare($query);
697             $sti->execute;
698             if ( $sti->err ) {
699                 print "**Error : $sti->errstr \n";
700                 $sti->finish;
701             }    # if error
702         }    # if column
703     }    # foreach column
704 }    # foreach table
705
706 foreach $table ( keys %fielddefinitions ) {
707         print "Check table $table\n" if $debug;
708         $sth = $dbh->prepare("show columns from $table");
709         $sth->execute();
710         my $definitions;
711         while ( ( $column, $type, $null, $key, $default, $extra ) = $sth->fetchrow )
712         {
713                 $definitions->{$column}->{type}    = $type;
714                 $definitions->{$column}->{null}    = $null;
715                 $definitions->{$column}->{key}     = $key;
716                 $definitions->{$column}->{default} = $default;
717                 $definitions->{$column}->{extra}   = $extra;
718         }    # while
719         my $fieldrow = $fielddefinitions{$table};
720         foreach my $row (@$fieldrow) {
721                 my $field   = $row->{field};
722                 my $type    = $row->{type};
723                 my $null    = $row->{null};
724                 my $key     = $row->{key};
725                 my $default = $row->{default};
726                 $default="''" unless $default;
727                 my $extra   = $row->{extra};
728                 my $def     = $definitions->{$field};
729                 unless ( $type eq $def->{type}
730                         && $null eq $def->{null}
731                         && $key eq $def->{key}
732                         && $default eq $def->{default}
733                         && $extra eq $def->{extra} )
734                 {
735
736                         if ( $null eq '' ) {
737                                 $null = 'NOT NULL';
738                         }
739                         if ( $key eq 'PRI' ) {
740                                 $key = 'PRIMARY KEY';
741                         }
742                         unless ( $extra eq 'auto_increment' ) {
743                                 $extra = '';
744                         }
745                         # if it's a new column use "add", if it's an old one, use "change".
746                         my $action;
747                         if ($definitions->{$field}->{type}) {
748                                 $action="change $field"
749                         } else {
750                                 $action="add";
751                         }
752 # if it's a primary key, drop the previous pk, before altering the table
753                         my $sth;
754                         if ($key ne 'PRIMARY KEY') {
755                                 $sth =$dbh->prepare("alter table $table $action $field $type $null $key $extra default ?");
756                         } else {
757                                 $sth =$dbh->prepare("alter table $table drop primary key, $action $field $type $null $key $extra default ?");
758                         }
759                         $sth->execute($default);
760                         print "  Alter $field in $table\n";
761                 }
762         }
763 }
764
765 # Get list of columns from borrowers table
766 my %itemtypes;
767 my %nullenabled;
768 $sth = $dbh->prepare("show columns from borrowers");
769 $sth->execute;
770 while ( my ( $column, $type, $null, $key, $default, $extra ) = $sth->fetchrow )
771 {
772     $itemtypes{$column} = $type;
773     $nullenabled{$column} = $null;
774 }
775
776 unless ( $itemtypes{'cardnumber'} eq 'varchar(20)' ) {
777     $itemtypes{'cardnumber'} =~ /varchar\((\d+)\)/;
778     my $oldlength = $1;
779     if ( $oldlength < 16 ) {
780         print "Setting maximum cardnumber length to 16 (was $oldlength) and marking unique.\n";
781         my $sti =
782           $dbh->prepare(
783             "alter table borrowers change cardnumber cardnumber varchar(16)");
784         $sti->execute;
785         $sti->finish;
786         $sti =
787           $dbh->prepare(
788             "alter table borrowers drop index cardnumber");
789         $sti->execute;
790         $sti->finish;
791         $sti =
792           $dbh->prepare(
793             "alter table borrowers add unique(cardnumber)");
794         $sti->execute;
795         $sti->finish;
796     }
797 }
798 #
799 # Get list of columns from items table
800 $sth = $dbh->prepare("show columns from items");
801 $sth->execute;
802 while ( my ( $column, $type, $null, $key, $default, $extra ) = $sth->fetchrow )
803 {
804     $itemtypes{$column} = $type;
805     $nullenabled{$column} = $null;
806 }
807
808 unless ( $itemtypes{'barcode'} eq 'varchar(20)' ) {
809     $itemtypes{'barcode'} =~ /varchar\((\d+)\)/;
810     my $oldlength = $1;
811     if ( $oldlength < 20 ) {
812         print "Setting maximum barcode length to 20 (was $oldlength).\n";
813         my $sti =
814           $dbh->prepare(
815             "alter table items change barcode barcode varchar(20)");
816         $sti->execute;
817     }
818 }
819 #
820 # dropping unique barcode index & setting barcode to null allowed.
821 #
822 $sth = $dbh->prepare("show index from items");
823 $sth->execute;
824 while ( my ( $table, $non_unique, $key_name, $Seq_in_index, $Column_name, $Collation, $cardinality, $sub_part, $Packed, $comment ) = $sth->fetchrow )
825 {
826         if ($key_name eq 'barcode' && $non_unique eq 0) {
827                 print "dropping BARCODE index to enable empty barcodes\n";
828                 $dbh->do("ALTER TABLE `items` DROP INDEX `barcode`");
829         }
830 }
831 $dbh->do("ALTER TABLE `items` CHANGE `barcode` `barcode` VARCHAR( 20 )") unless ($nullenabled{barcode} eq 'YES');
832
833 #
834 # creating fulltext index in bibliothesaurus if needed
835 #
836 $sth = $dbh->prepare("show index from bibliothesaurus");
837 $sth->execute;
838 my $exists=0;
839 while ( my ( $table, $non_unique, $key_name, $Seq_in_index, $Column_name, $Collation, $cardinality, $sub_part, $Packed, $comment ) = $sth->fetchrow )
840 {
841         if ($key_name eq 'category_2') {
842                 $exists=1;
843         }
844 }
845 print "Creating fulltext index on bibliothesaurus\n" unless $exists;
846 $dbh->do('create fulltext index category_2 on bibliothesaurus (category,freelib)') unless $exists;
847
848 # changing z3950daemon field to NULL in marc_breeding
849 $dbh->do("ALTER TABLE `marc_breeding` CHANGE `z3950random` `z3950random` VARCHAR( 40 )");
850
851 # making borrowernumber an auto_increment field
852 $dbh->do("ALTER TABLE `borrowers` CHANGE `borrowernumber` `borrowernumber` INTEGER auto_increment");
853
854 # extending the timestamp in branchtransfers...
855 my %branchtransfers;
856
857 $sth = $dbh->prepare("show columns from branchtransfers");
858 $sth->execute;
859 while ( my ( $column, $type, $null, $key, $default, $extra ) = $sth->fetchrow )
860 {
861     $branchtransfers{$column} = $type;
862 }
863
864 unless ( $branchtransfers{'datesent'} eq 'datetime' ) {
865     print "Setting type of datesent in branchtransfers to datetime.\n";
866     my $sti =
867       $dbh->prepare(
868         "alter table branchtransfers change datesent datesent datetime");
869     $sti->execute;
870 }
871
872 unless ( $branchtransfers{'datearrived'} eq 'datetime' ) {
873     print "Setting type of datearrived in branchtransfers to datetime.\n";
874     my $sti =
875       $dbh->prepare(
876         "alter table branchtransfers change datearrived datearrived datetime");
877     $sti->execute;
878 }
879
880 # changing the branchcategories table around...
881 my %branchcategories;
882
883 $sth = $dbh->prepare("show columns from branchcategories");
884 $sth->execute;
885 while ( my ( $column, $type, $null, $key, $default, $extra ) = $sth->fetchrow )
886 {
887     $branchcategories{$column} = $type;
888 }
889
890 unless ( $branchcategories{'categorycode'} eq 'varchar(4)' ) {
891     print
892 "Setting type of categorycode in branchcategories to varchar(4),\n and making the primary key.\n";
893     my $sti =
894       $dbh->prepare(
895 "alter table branchcategories change categorycode categorycode varchar(4) not null"
896     );
897     $sti->execute;
898     $sti =
899       $dbh->prepare(
900         "alter table branchcategories add primary key (categorycode)");
901     $sti->execute;
902 }
903
904 unless ( $branchcategories{'categoryname'} eq 'text' ) {
905     print "Changing branchcode in branchcategories to categoryname text.\n";
906     my $sth =
907       $dbh->prepare(
908         "alter table branchcategories change branchcode categoryname text");
909     $sth->execute;
910 }
911
912 unless ( $branchcategories{'codedescription'} eq 'text' ) {
913     print
914 "Replacing branchholding in branchcategories with codedescription text.\n";
915     my $sth =
916       $dbh->prepare(
917         "alter table branchcategories change branchholding codedescription text"
918     );
919     $sth->execute;
920 }
921
922 # Populate tables with required data
923
924 foreach my $table ( keys %tabledata ) {
925     print "Checking for data required in table $table...\n";
926     my $tablerows = $tabledata{$table};
927     foreach my $row (@$tablerows) {
928         my $uniquefieldrequired = $row->{uniquefieldrequired};
929         my $uniquevalue         = $row->{$uniquefieldrequired};
930         my $forceupdate         = $row->{forceupdate};
931         my $sth                 =
932           $dbh->prepare(
933 "select $uniquefieldrequired from $table where $uniquefieldrequired=?"
934         );
935         $sth->execute($uniquevalue);
936         if ($sth->rows) {
937             foreach my $field (keys %$forceupdate) {
938                 if ($forceupdate->{$field}) {
939                     my $sth=$dbh->prepare("update systempreferences set $field=? where $uniquefieldrequired=?");
940                     $sth->execute($row->{$field}, $uniquevalue);
941                 }
942             }
943         } else {
944             print "Adding row to $table: ";
945             my @values;
946             my $fieldlist;
947             my $placeholders;
948             foreach my $field ( keys %$row ) {
949                 next if $field eq 'uniquefieldrequired';
950                 next if $field eq 'forceupdate';
951                 my $value = $row->{$field};
952                 push @values, $value;
953                 print "  $field => $value";
954                 $fieldlist .= "$field,";
955                 $placeholders .= "?,";
956             }
957             print "\n";
958             $fieldlist    =~ s/,$//;
959             $placeholders =~ s/,$//;
960             my $sth =
961               $dbh->prepare(
962                 "insert into $table ($fieldlist) values ($placeholders)");
963             $sth->execute(@values);
964         }
965     }
966 }
967
968 $sth->finish;
969
970 exit;
971
972 # $Log$
973 # Revision 1.67  2003/11/28 10:08:33  tipaul
974 # * removing too verbose messages.
975 # * creating a fulltext index on bibliothesaurus
976 #
977 # Revision 1.66  2003/11/12 16:14:42  slef
978 # lengthen cardnumber to 16 and make it unique
979 #
980 # Revision 1.65  2003/11/06 15:07:11  tipaul
981 # adding marc fields in deletedbiblio & deleteditems
982 #
983 # Revision 1.64  2003/10/23 20:33:53  rangi
984 # Making the borrowenumber an auto_increment field
985 #
986 # Revision 1.63  2003/10/20 16:13:01  slef
987 # Omitted annotation added. Closes: 624
988 #
989 # Revision 1.62  2003/10/20 16:10:19  slef
990 # Adding USMARC to LOC z3950 entry
991 #
992 # Revision 1.61  2003/10/01 15:03:45  tipaul
993 # oups... typo fix in z3950random field definition
994 #
995 # Revision 1.59  2003/09/30 16:22:05  tipaul
996 # adding barcode NOT mandatory feature. Just run updatedatabase to get it.
997 # Note it's impossible to issue an item without barcode, as issue/returns is based on barcode...
998 #
999 # Revision 1.58  2003/07/16 04:08:29  acli
1000 # Minor spelling correction
1001 #
1002 # Revision 1.57  2003/07/11 11:50:29  tipaul
1003 # fixing a bug that occured when adding a field into a table.
1004 #
1005 # Revision 1.56  2003/07/07 15:37:20  tipaul
1006 # *** empty log message ***
1007 #
1008 # Revision 1.53  2003/07/07 14:11:16  tipaul
1009 # fixing bug #526 : gst rate is now calculated through systempref gist entry.
1010 # Before this fix :
1011 # * was harcoded to 12,5%
1012 # * some bugs in template parameters prevented the javascript to work.
1013 # * some bugs prevented some calculations to be done properly.
1014 #
1015 # Revision 1.52  2003/06/23 15:54:32  tipaul
1016 # *** empty log message ***
1017 #
1018 # Revision 1.51  2003/06/23 11:27:29  tipaul
1019 # *** empty log message ***
1020 #
1021 # Revision 1.50  2003/06/11 21:28:22  tonnesen
1022 # Added modifications required to the systempreferences table by the new
1023 # systempreferences.pl script.  The systempreferences.pl script will not work
1024 # properly until this table is updated.
1025 #
1026 # Revision 1.49  2003/05/26 10:41:53  tipaul
1027 # bugfix : borrowers modifs overwritten by stupid hash entry existing twice.
1028 #
1029 # Revision 1.48  2003/05/20 19:50:45  slef
1030 # Initial fix to bug 456: hardwired paths
1031 #
1032 # Revision 1.47  2003/05/15 12:23:33  tipaul
1033 # adding zipcode and homezipcode into borrowers table (bug #246
1034 #
1035 # Revision 1.46  2003/05/08 12:48:24  wolfpac444
1036 # Added "noissuescharge" parameter
1037 #
1038 # Revision 1.45  2003/05/08 12:26:16  wolfpac444
1039 # Bug fixes
1040 #
1041 # Revision 1.44  2003/05/03 05:39:57  rangi
1042 # Fixing bug 429
1043 # (Wording changes in the explanation fields in system preferences)
1044 #
1045 # Revision 1.43  2003/05/02 23:01:09  rangi
1046 # Adding the textmessaging column to the borrowers table.
1047 # insertdata.pl is expecting this to exist, and hence modifying/adding
1048 # borrowers was broken.
1049 #
1050 # Also ran they script thru perltidy
1051 #
1052 # Revision 1.42  2003/04/29 16:53:25  tipaul
1053 # really proud of this commit :-)
1054 # z3950 search and import seems to works fine.
1055 # Let me explain how :
1056 # * a "search z3950" button is added in the addbiblio template.
1057 # * when clicked, a popup appears and z3950/search.pl is called
1058 # * z3950/search.pl calls addz3950search in the DB
1059 # * the z3950 daemon retrieve the records and stores them in z3950results AND in marc_breeding table.
1060 # * as long as there as searches pending, the popup auto refresh every 2 seconds, and says how many searches are pending.
1061 # * when the user clicks on a z3950 result => the parent popup is called with the requested biblio, and auto-filled
1062 #
1063 # Note :
1064 # * character encoding support : (It's a nightmare...) In the z3950servers table, a "encoding" column has been added. You can put "UNIMARC" or "USMARC" in this column. Depending on this, the char_decode in C4::Biblio.pm replaces marc-char-encode by an iso 8859-1 encoding. Note that in the breeding import this value has been added too, for a better support.
1065 # * the marc_breeding and z3950* tables have been modified : they have an encoding column and the random z3950 number is stored too for convenience => it's the key I use to list only requested biblios in the popup.
1066 #
1067 # Revision 1.41  2003/04/29 08:09:44  tipaul
1068 # z3950 support is coming...
1069 # * adding a syntax column in z3950 table = this column will say wether the z3950 must be called with PerferedRecordsyntax => USMARC or PerferedRecordsyntax => UNIMARC. I tried some french UNIMARC z3950 servers, and some only send USMARC, some only UNIMARC, some can answer with both.
1070 # Note this is a 1st draft. More to follow (today ? I hope).
1071 #
1072 # Revision 1.40  2003/04/22 10:48:27  wolfpac444
1073 # Added "father" column to bibliothesaurus table
1074 #
1075 # Revision 1.39  2003/04/04 08:45:00  tipaul
1076 # last commits before 1.9.1
1077 #
1078 # Revision 1.38  2003/03/18 10:58:19  tipaul
1079 # adding checkdigit parameter that choose how to check the members cardnumber.
1080 # At the moment :
1081 # * none = no checking
1082 # * katipo = checked as before
1083 #
1084 # Revision 1.37  2003/01/30 01:47:48  acli
1085 # Corrected syntax error reported by Benedict
1086 #
1087 # Made the indentation somewhat easier to read; the messiness probably caused
1088 # the original syntax error.
1089 #
1090 # Revision 1.36  2003/01/28 15:13:30  tipaul
1091 # userflag table now created in upgrade script (bugfix #171)
1092 #
1093 # Revision 1.35  2003/01/27 03:12:49  acli
1094 # Reworded the description for "acquisitions" to make it fit on the screen
1095 #
1096 # Added "iso" to dateformat, since dateformat is not yet being used anyway
1097 #
1098 # Revision 1.34  2003/01/23 12:30:02  tipaul
1099 # introducint marcflavour in systempref file : used for character decoding
1100 #
1101 # Revision 1.33  2003/01/21 09:03:27  tipaul
1102 # bugfix (NOTE : this bugs makes installation of the 1.3.3 a little fuzzy. Please fix your DB if you installed 1.3.3)
1103 #
1104 # Revision 1.32  2003/01/16 10:29:45  tipaul
1105 # adding a MARC parameter in systempref ( which is ON or OFF)
1106 # the search will be a marc search if MARC=ON
1107 # and a standard (v1.2) search if MARC=OFF
1108 #
1109 # Revision 1.31  2003/01/06 13:32:43  tipaul
1110 # *** empty log message ***
1111 #
1112 # Revision 1.29  2003/01/06 11:14:11  tipaul
1113 # last bugfixes before 1.3.3 : systempref table correctly filled
1114 #
1115 # Revision 1.28  2002/12/10 13:27:47  tipaul
1116 # bugfixes (davide mails in koha-dev)
1117 #
1118 # Revision 1.27  2002/11/26 15:04:54  tipaul
1119 # road to 1.3.2. Updating db structure during installation
1120 #
1121 # Revision 1.26  2002/11/12 17:42:40  tonnesen
1122 # Merged some features over from rel-1-2, including primary key checking.
1123 #
1124 # Revision 1.25  2002/11/12 16:44:38  tipaul
1125 # road to 1.3.2 :
1126 # * many bugfixes
1127 # * adding value_builder : you can map a subfield in the marc_subfield_structure to a sub stored in "value_builder" directory. In this directory you can create screen used to build values with any method. In this commit is a 1st draft of the builder for 100$a unimarc french subfield, which is composed of 35 digits, with 12 differents values (only the 4th first are provided for instance)
1128 #
1129 # Revision 1.24  2002/10/30 14:00:23  arensb
1130 # (bug fix): Fixed typo.
1131 #
1132 # Revision 1.23  2002/10/25 10:55:46  tipaul
1133 # Road to 1.3.2
1134 # * bugfixes and improvements
1135 # * manage mandatory MARC subfields
1136 # * new table : authorised_values. this table contains categories and authorised values for the category. On MARC management, you can map a subfield to a authorised_values category. If you do this, the subfield can only be filled with a authorised_value of the selected category.
1137 # this submit contains everything needed :
1138 # * updatedatabase
1139 # * admin screens
1140 # * "links" management
1141 # * creation of a html-list if a subfield is mapped to an authorised value.
1142 #
1143 # Note this is different from authorities support, which will come soon.
1144 # The authorised_values is supposed to contains a "small" number of authorised values for a category (less than 50-100). If you enter more authorised values than this, it should be hard to find what you want in a BIG list...
1145 #
1146 # Revision 1.22  2002/10/15 10:08:19  tipaul
1147 # fixme corrected, re-indent and adding the marc_breeding table (see commit of marcimport.pl for more explanations about breeding)
1148 #
1149 # Revision 1.21  2002/10/14 11:48:59  tipaul
1150 # bugfix
1151 #
1152 # Revision 1.20  2002/10/10 04:49:41  arensb
1153 # Added some FIXME comments.
1154 #
1155 # Revision 1.19  2002/10/05 10:17:17  arensb
1156 # Merged with arensb-context branch: use C4::Context->dbh instead of
1157 # &C4Connect, and generally prefer C4::Context over C4::Database.
1158 #
1159 # Revision 1.18.2.2  2002/10/05 06:18:43  arensb
1160 # Added a whole mess of FIXME comments.
1161 #
1162 # Revision 1.18.2.1  2002/10/04 02:46:00  arensb
1163 # Use C4::Connect instead of C4::Database, C4::Connect->dbh instead
1164 # C4Connect.
1165 #
1166 # Revision 1.18  2002/09/24 13:50:55  tipaul
1167 # long WAS the road to 1.3.0...
1168 # coming VERY SOON NOW...
1169 # modifying installer and buildrelease to update the DB
1170 #
1171 # Revision 1.17  2002/09/24 12:57:35  tipaul
1172 # long WAS the road to 1.3.0...
1173 # coming VERY SOON NOW...
1174 # modifying installer and buildrelease to update the DB
1175 #
1176 # Revision 1.16  2002/07/31 02:34:27  finlayt
1177 #
1178 # added "notforloan" field to the itemtypes table.
1179 #
1180 # Revision 1.15  2002/07/20 22:30:06  rangi
1181 # Making sure fix makes it into the main branch as well
1182 # Fix for bug 69
1183 #
1184 # Revision 1.14  2002/07/08 16:20:26  tonnesen
1185 # Added sessionqueries table and password/userid fields to borrowers table
1186 #
1187 # Revision 1.13  2002/07/04 18:05:36  tonnesen
1188 # bug fix
1189 #
1190 # Revision 1.12  2002/07/04 16:41:06  tonnesen
1191 # Merged changes from rel-1-2.  Abstracted table structure changes by alan.
1192 #