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