fixes in DB structure
[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 use Getopt::Long;
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 $silent;
41 GetOptions(
42         's' =>\$silent
43         );
44 my $dbh = C4::Context->dbh;
45 print "connected to your DB. Checking & modifying it\n" unless $silent;
46
47 #-------------------
48 # Defines
49
50 # Tables to add if they don't exist
51 my %requiretables = (
52     shelfcontents => "( shelfnumber int not null,
53                                                         itemnumber int not null,
54                                                         flags int)",
55     bookshelf => "( shelfnumber int auto_increment primary key,
56                                                 shelfname char(255))",
57     z3950queue => "( id int auto_increment primary key,
58                                                 term text,
59                                                 type char(10),
60                                                 startdate int,
61                                                 enddate int,
62                                                 done smallint,
63                                                 results longblob,
64                                                 numrecords int,
65                                                 servers text,
66                                                 identifier char(30))",
67     z3950results => "( id int auto_increment primary key,
68                                                 queryid int,
69                                                 server char(255),
70                                                 startdate int,
71                                                 enddate int,
72                                                 results longblob,
73                                                 numrecords int,
74                                                 numdownloaded int,
75                                                 highestseen int,
76                                                 active smallint)",
77     branchrelations => "( branchcode varchar(4),
78                                                         categorycode varchar(4))",
79     websites => "( websitenumber int(11) NOT NULL auto_increment,
80                                                 biblionumber int(11) NOT NULL default '0',
81                                                 title text,
82                                                 description text,
83                                                 url varchar(255),
84                                                 PRIMARY KEY (websitenumber) )",
85     marcrecorddone => "( isbn char(40),
86                                                                 issn char(40),
87                                                                 lccn char(40),
88                                                                 controlnumber char(40))",
89     uploadedmarc => "( id int(11) NOT NULL auto_increment PRIMARY KEY,
90                                                         marc longblob,
91                                                         hidden smallint(6) default NULL,
92                                                         name varchar(255) default NULL)",
93     ethnicity => "( code varchar(10) NOT NULL default '',
94                                         name varchar(255) default NULL,
95                                         PRIMARY KEY  (code)   )",
96     sessions => "( sessionID varchar(255) NOT NULL default '',
97                                                 userid varchar(255) default NULL,
98                                                 ip varchar(16) default NULL,
99                                                 lasttime int,
100                                                 PRIMARY KEY (sessionID)   )",
101     sessionqueries => "( sessionID varchar(255) NOT NULL default '',
102                                                                 userid char(100) NOT NULL default '',
103                                                                 ip char(18) NOT NULL default '',
104                                                                 url text NOT NULL default ''  )",
105     bibliothesaurus => "( id bigint(20) NOT NULL auto_increment,
106                                                         freelib char(255) NOT NULL default '',
107                                                         stdlib char(255) NOT NULL default '',
108                                                         category char(10) NOT NULL default '',
109                                                         level tinyint(4) NOT NULL default '1',
110                                                         hierarchy char(80) NOT NULL default '',
111                                                         father char(80) NOT NULL default '',
112                                                         PRIMARY KEY  (id),
113                                                         KEY freelib (freelib),
114                                                         KEY stdlib (stdlib),
115                                                         KEY category (category),
116                                                         KEY hierarchy (hierarchy)
117                                                         )",
118     marc_biblio => "(
119                                                 bibid bigint(20) unsigned NOT NULL auto_increment,
120                                                 biblionumber int(11) NOT NULL default '0',
121                                                 datecreated date NOT NULL default '0000-00-00',
122                                                 datemodified date default NULL,
123                                                 origincode char(20) default NULL,
124                                                 PRIMARY KEY  (bibid),
125                                                 KEY origincode (origincode),
126                                                 KEY biblionumber (biblionumber)
127                                                 ) ",
128     marc_blob_subfield => "(
129                                         blobidlink bigint(20) NOT NULL auto_increment,
130                                         subfieldvalue longtext NOT NULL,
131                                         PRIMARY KEY  (blobidlink)
132                                         ) ",
133     marc_subfield_structure => "(
134                                                 tagfield char(3) NOT NULL default '',
135                                                 tagsubfield char(1) NOT NULL default '',
136                                                 liblibrarian char(255) NOT NULL default '',
137                                                 libopac char(255) NOT NULL default '',
138                                                 repeatable tinyint(4) NOT NULL default '0',
139                                                 mandatory tinyint(4) NOT NULL default '0',
140                                                 kohafield char(40)  default NULL,
141                                                 tab tinyint(1) default NULL,
142                                                 authorised_value char(10) default NULL,
143                                                 thesaurus_category char(10) default NULL,
144                                                 value_builder char(80) default NULL,
145                                                 PRIMARY KEY  (tagfield,tagsubfield),
146                                                 KEY kohafield (kohafield),
147                                                 KEY tab (tab)
148                                                 )",
149     marc_subfield_table => "(
150                                                 subfieldid bigint(20) unsigned NOT NULL auto_increment,
151                                                 bibid bigint(20) unsigned NOT NULL default '0',
152                                                 tag char(3) NOT NULL default '',
153                                                 tagorder tinyint(4) NOT NULL default '1',
154                                                 tag_indicator char(2) NOT NULL default '',
155                                                 subfieldcode char(1) NOT NULL default '',
156                                                 subfieldorder tinyint(4) NOT NULL default '1',
157                                                 subfieldvalue varchar(255) default NULL,
158                                                 valuebloblink bigint(20) default NULL,
159                                                 PRIMARY KEY  (subfieldid),
160                                                 KEY bibid (bibid),
161                                                 KEY tag (tag),
162                                                 KEY tag_indicator (tag_indicator),
163                                                 KEY subfieldorder (subfieldorder),
164                                                 KEY subfieldcode (subfieldcode),
165                                                 KEY subfieldvalue (subfieldvalue),
166                                                 KEY tagorder (tagorder)
167                                         )",
168     marc_tag_structure => "(
169                                         tagfield char(3) NOT NULL default '',
170                                         liblibrarian char(255) NOT NULL default '',
171                                         libopac char(255) NOT NULL default '',
172                                         repeatable tinyint(4) NOT NULL default '0',
173                                         mandatory tinyint(4) NOT NULL default '0',
174                                         authorised_value char(10) default NULL,
175                                         PRIMARY KEY  (tagfield)
176                                         )",
177     marc_word => "(
178                                 bibid bigint(20) NOT NULL default '0',
179                                 tag char(3) NOT NULL default '',
180                                 tagorder tinyint(4) NOT NULL default '1',
181                                 subfieldid char(1) NOT NULL default '',
182                                 subfieldorder tinyint(4) NOT NULL default '1',
183                                 word varchar(255) NOT NULL default '',
184                                 sndx_word varchar(255) NOT NULL default '',
185                                 KEY bibid (bibid),
186                                 KEY tag (tag),
187                                 KEY tagorder (tagorder),
188                                 KEY subfieldid (subfieldid),
189                                 KEY subfieldorder (subfieldorder),
190                                 KEY word (word),
191                                 KEY sndx_word (sndx_word)
192                         )",
193     marc_breeding => "(  id bigint(20) NOT NULL auto_increment,
194                                 file varchar(80) NOT NULL default '',
195                                 isbn varchar(10) NOT NULL default '',
196                                 title varchar(128) default NULL,
197                                 author varchar(80) default NULL,
198                                 marc text NOT NULL,
199                                 encoding varchar(40) default NULL,
200                                 PRIMARY KEY  (id),
201                                 KEY title (title),
202                                 KEY isbn (isbn)
203                         )",
204     authorised_values => "(id int(11) NOT NULL auto_increment,
205                                 category char(10) NOT NULL default '',
206                                 authorised_value char(80) NOT NULL default '',
207                                 lib char(80) NULL,
208                                 PRIMARY KEY  (id),
209                                 KEY name (category)
210                         )",
211     userflags => "( bit int(11) NOT NULL default '0',
212                                 flag char(30), flagdesc char(255),
213                                 defaulton int(11)
214                         )",
215         auth_types => "(
216                                         authtypecode char(10) not NULL,
217                                         authtypetext char(255) not NULL,
218                                         auth_tag_to_report char(3) not NULL,
219                                         summary text not NULL,
220                                         PRIMARY KEY (authtypecode)
221                         )",
222         biblio_framework => "(
223                                         frameworkcode char(4) not NULL,
224                                         frameworktext char(255) not NULL,
225                                         PRIMARY KEY (frameworkcode)
226                         )",
227     auth_subfield_structure => "(
228                                         authtypecode char(10) NOT NULL default '',
229                                         tagfield char(3) NOT NULL default '',
230                                         tagsubfield char(1) NOT NULL default '',
231                                         liblibrarian char(255) NOT NULL default '',
232                                         libopac char(255) NOT NULL default '',
233                                         repeatable tinyint(4) NOT NULL default '0',
234                                         mandatory tinyint(4) NOT NULL default '0',
235                                         tab tinyint(1) default NULL,
236                                         authorised_value char(10) default NULL,
237                                         value_builder char(80) default NULL,
238                                         seealso char(255) default NULL,
239                                         PRIMARY KEY  (authtypecode,tagfield,tagsubfield),
240                                         KEY tab (authtypecode,tab)
241                                         )",
242     auth_tag_structure => "(
243                                         authtypecode char(10) NOT NULL default '',
244                                         tagfield char(3) NOT NULL default '',
245                                         liblibrarian char(255) NOT NULL default '',
246                                         libopac char(255) NOT NULL default '',
247                                         repeatable tinyint(4) NOT NULL default '0',
248                                         mandatory tinyint(4) NOT NULL default '0',
249                                         authorised_value char(10) default NULL,
250                                         PRIMARY KEY  (authtypecode,tagfield)
251                                         )",
252     auth_header => "(
253                                                 authid bigint(20) unsigned NOT NULL auto_increment,
254                                                 authtypecode char(10) NOT NULL default '',
255                                                 datecreated date NOT NULL default '0000-00-00',
256                                                 datemodified date default NULL,
257                                                 origincode char(20) default NULL,
258                                                 PRIMARY KEY  (authid),
259                                                 KEY origincode (origincode),
260                                                 ) ",
261     auth_subfield_table => "(
262                                                 subfieldid bigint(20) unsigned NOT NULL auto_increment,
263                                                 authid bigint(20) unsigned NOT NULL default '0',
264                                                 tag char(3) NOT NULL default '',
265                                                 tagorder tinyint(4) NOT NULL default '1',
266                                                 tag_indicator char(2) NOT NULL default '',
267                                                 subfieldcode char(1) NOT NULL default '',
268                                                 subfieldorder tinyint(4) NOT NULL default '1',
269                                                 subfieldvalue varchar(255) default NULL,
270                                                 PRIMARY KEY  (subfieldid),
271                                                 KEY authid (authid),
272                                                 KEY tag (tag),
273                                                 KEY subfieldcode (subfieldcode),
274                                                 KEY subfieldvalue (subfieldvalue)
275                                         )",
276     auth_word => "(
277                                 authid bigint(20) NOT NULL default '0',
278                                 tagsubfield char(4) NOT NULL default '',
279                                 tagorder tinyint(4) NOT NULL default '1',
280                                 subfieldorder tinyint(4) NOT NULL default '1',
281                                 word varchar(255) NOT NULL default '',
282                                 sndx_word varchar(255) NOT NULL default '',
283                                 KEY authid (authid),
284                                 KEY marc_search (tagsubfield,word),
285                                 KEY word (word),
286                                 KEY sndx_word (sndx_word)
287                         )",
288         suggestions => "(
289                                 suggestionnumber int(8) NOT NULL auto_increment,
290                                 suggestedby int(11) NOT NULL default '0',
291                                 managedby int(11) default NULL,
292                                 status varchar(10) NOT NULL default '',
293                                 note text,
294                                 author varchar(80) default NULL,
295                                 title varchar(80) default NULL,
296                                 copyrightdate smallint(6) default NULL,
297                                 publishercode varchar(255) default NULL,
298                                 date timestamp(8) NOT NULL,
299                                 mailoverseeing smallint(1) default 0,
300                                 PRIMARY KEY  (suggestionnumber),
301                                 KEY suggestedby (suggestedby),
302                                 KEY managedby (managedby)
303                         )",
304 );
305
306 my %requirefields = (
307     biblio        => { 'abstract' => 'text' },
308     deletedbiblio => { 'abstract' => 'text', 'marc' => 'blob' },
309     deleteditems => { 'marc' => 'blob', 'paidfor' => 'text' },
310     biblioitems   => {
311         'lccn' => 'char(25)',
312         'url'  => 'varchar(255)',
313         'marc' => 'text'
314     },
315     deletedbiblioitems => {
316         'lccn' => 'char(25)',
317         'url'  => 'varchar(255)',
318         'marc' => 'text'
319     },
320     branchtransfers => { 'datearrived'    => 'datetime' },
321     statistics      => { 'borrowernumber' => 'int(11)' },
322     aqbooksellers   => {
323         'invoicedisc' => 'float(6,4)',
324         'nocalc'      => 'int(11)'
325     },
326     borrowers => {
327         'userid'        => 'char(30)',
328         'password'      => 'char(30)',
329         'flags'         => 'int(11)',
330         'textmessaging' => 'varchar(30)',
331            'zipcode' => 'varchar(25)',
332                         'homezipcode' => 'varchar(25)',
333     },
334     aqorders => { 'budgetdate' => 'date' },
335     aqbudget => {'aqbudgetid' => 'tinyint(4) auto_increment primary key'},
336     items => {'paidfor' => 'text'},
337
338     #added so that reference items are not available for reserves...
339     itemtypes         => { 'notforloan'  => 'smallint(6)' },
340     systempreferences => { 'explanation' => 'char(80)',
341                            'type' => 'char(20)',
342                            'options' => 'text' },
343     z3950servers      => { 'syntax'      => 'char(80)' },
344         marc_tag_structure =>{
345                                                         'frameworkcode' => 'char(4) not NULL default \'\''},
346     marc_subfield_structure =>{'seealso'  => 'char(255)',
347                                                         'frameworkcode' => 'char(4) not NULL default \'\'',
348                                                         'hidden' => 'tinyint(1)',
349                                                         'isurl' => 'tinyint(1)',
350                                                         },
351     bookshelf => {'owner' => 'char(80)',
352                                         'category' => 'char(1)',
353                                 },
354     marc_biblio        => { 'frameworkcode' => 'char(4) not NULL default \'\'' },
355 );
356
357 my %dropable_table = (
358     classification => 'classification',
359     multipart      => 'multipart',
360     multivolume    => 'multivolume',
361     newitems       => 'newitems',
362     procedures     => 'procedures',
363     publisher      => 'publisher',
364     searchstats    => 'searchstats',
365     serialissues   => 'serialissues',
366 );
367
368 # the other hash contains other actions that can't be done elsewhere. they are done
369 # either BEFORE of AFTER everything else, depending on "when" entry (default => AFTER)
370
371 # The tabledata hash contains data that should be in the tables.
372 # The uniquefieldrequired hash entry is used to determine which (if any) fields
373 # must not exist in the table for this row to be inserted.  If the
374 # uniquefieldrequired entry is already in the table, the existing data is not
375 # modified, unless the forceupdate hash entry is also set.  Fields in the
376 # anonymous "forceupdate" hash will be forced to be updated to the default
377 # values given in the %tabledata hash.
378
379 my %tabledata = (
380     userflags => [
381         {
382             uniquefieldrequired => 'bit',
383             bit                 => 0,
384             flag                => 'superlibrarian',
385             flagdesc            => 'Access to all librarian functions',
386             defaulton           => 0
387         },
388         {
389             uniquefieldrequired => 'bit',
390             bit                 => 1,
391             flag                => 'circulate',
392             flagdesc            => 'Circulate books',
393             defaulton           => 0
394         },
395         {
396             uniquefieldrequired => 'bit',
397             bit                 => 2,
398             flag                => 'catalogue',
399             flagdesc            => 'View Catalogue (Librarian Interface)',
400             defaulton           => 0
401         },
402         {
403             uniquefieldrequired => 'bit',
404             bit                 => 3,
405             flag                => 'parameters',
406             flagdesc            => 'Set Koha system paramters',
407             defaulton           => 0
408         },
409         {
410             uniquefieldrequired => 'bit',
411             bit                 => 4,
412             flag                => 'borrowers',
413             flagdesc            => 'Add or modify borrowers',
414             defaulton           => 0
415         },
416         {
417             uniquefieldrequired => 'bit',
418             bit                 => 5,
419             flag                => 'permissions',
420             flagdesc            => 'Set user permissions',
421             defaulton           => 0
422         },
423         {
424             uniquefieldrequired => 'bit',
425             bit                 => 6,
426             flag                => 'reserveforothers',
427             flagdesc            => 'Reserve books for patrons',
428             defaulton           => 0
429         },
430         {
431             uniquefieldrequired => 'bit',
432             bit                 => 7,
433             flag                => 'borrow',
434             flagdesc            => 'Borrow books',
435             defaulton           => 1
436         },
437         {
438             uniquefieldrequired => 'bit',
439             bit                 => 8,
440             flag                => 'reserveforself',
441             flagdesc            => 'Reserve books for self',
442             defaulton           => 0
443         },
444         {
445             uniquefieldrequired => 'bit',
446             bit                 => 9,
447             flag                => 'editcatalogue',
448             flagdesc  => 'Edit Catalogue (Modify bibliographic/holdings data)',
449             defaulton => 0
450         },
451         {
452             uniquefieldrequired => 'bit',
453             bit                 => 10,
454             flag                => 'updatecharges',
455             flagdesc            => 'Update borrower charges',
456             defaulton           => 0
457         },
458     ],
459     systempreferences => [
460         {
461             uniquefieldrequired => 'variable',
462             forceupdate         => { 'explanation' => 1,
463                                      'type' => 1 },
464             variable            => 'LibraryName',
465             value               => '<i><b>Koha<br/>Free Software ILS<br/><br/></b>Koha : a gift, a contribution<br/> in Maori</i>',
466             explanation         => 'Library name as shown on main opac page',
467             type                => ''
468
469         },
470         {
471             uniquefieldrequired => 'variable',
472             forceupdate         => { 'explanation' => 1,
473                                      'type' => 1 },
474             variable            => 'autoMemberNum',
475             value               => '1',
476             explanation         => 'Member number is auto-calculated',
477             type                => 'YesNo'
478
479         },
480         {
481             uniquefieldrequired => 'variable',
482             forceupdate         => { 'explanation' => 1,
483                                      'type' => 1,
484                                      'options' => 1 },
485             variable            => 'acquisitions',
486             value               => 'normal',
487             explanation         =>
488 'Normal, budget-based acquisitions, or Simple bibliographic-data acquisitions',
489             type                => 'Choice',
490             options             => 'simple|normal'
491         },
492         {
493             uniquefieldrequired => 'variable',
494             forceupdate         => { 'explanation' => 1,
495                                      'type' => 1,
496                                      'options' => 1 },
497             variable            => 'dateformat',
498             value               => 'metric',
499             explanation         =>
500             'date format (us mm/dd/yyyy, metric dd/mm/yyy, ISO yyyy/mm/dd)',
501             type                => 'Choice',
502             options             => 'metric|us|iso'
503         },
504         {
505             uniquefieldrequired => 'variable',
506             variable            => 'template',
507             forceupdate         => { 'explanation' => 1,
508                                      'type' => 1 },
509             value               => 'default',
510             explanation         => 'Preference order for intranet interface templates',
511             type                => 'Themes'
512         },
513         {
514             uniquefieldrequired => 'variable',
515             variable            => 'autoBarcode',
516             forceupdate         => { 'explanation' => 1,
517                                      'type' => 1 },
518             value               => 'yes',
519             explanation         => 'Barcode is auto-calculated',
520             type                => 'YesNo'
521         },
522         {
523             uniquefieldrequired => 'variable',
524             variable            => 'insecure',
525             forceupdate         => { 'explanation' => 1,
526                                      'type' => 1 },
527             value               => 'no',
528             explanation         =>
529 'If YES, no auth at all is needed. Be careful if you set this to yes!',
530             type                => 'YesNo'
531         },
532         {
533             uniquefieldrequired => 'variable',
534             variable            => 'authoritysep',
535             forceupdate         => { 'explanation' => 1,
536                                      'type' => 1,
537                                      'options' => 1 },
538             value               => '--',
539             explanation         =>
540             'the separator used in authority/thesaurus. Usually --',
541             type                => 'free',
542             options             => '10'
543         },
544         {
545             uniquefieldrequired => 'variable',
546             variable            => 'opaclanguages',
547             forceupdate         => { 'explanation' => 1,
548                                      'type' => 1 },
549             value               => 'en',
550             explanation         => 'Set the preferred order for translations.  The top language will be tried first.',
551             type                => 'Languages'
552         },
553         {
554             uniquefieldrequired => 'variable',
555             variable            => 'opacthemes',
556             forceupdate         => { 'explanation' => 1,
557                                      'type' => 1 },
558             value               => 'css',
559             explanation         => 'Set the preferred order for themes.  The top theme will be tried first.',
560             type                => 'Themes'
561         },
562         {
563             uniquefieldrequired => 'variable',
564             variable            => 'timeout',
565             forceupdate         => { 'explanation' => 1,
566                                      'type' => 1 },
567             value               => '1200',
568             explanation         => 'Inactivity timeout for cookies authentication (in seconds)',
569             type                => 'Integer'
570         },
571         {
572             uniquefieldrequired => 'variable',
573             variable            => 'marc',
574             forceupdate         => { 'explanation' => 1,
575                                      'type' => 1 },
576             value               => 'yes',
577             explanation         => 'Turn on MARC support',
578             type                => 'YesNo'
579         },
580         {
581             uniquefieldrequired => 'variable',
582             variable            => 'marcflavour',
583             forceupdate         => { 'explanation' => 1,
584                                      'type' => 1,
585                                      'options' => 1},
586             value               => 'MARC21',
587             explanation         =>
588             'your MARC flavor (MARC21 or UNIMARC) used for character encoding',
589             type                => 'Choice',
590             options             => 'MARC21|UNIMARC'
591         },
592         {
593             uniquefieldrequired => 'variable',
594             variable            => 'checkdigit',
595             value               => 'none',
596             forceupdate         => { 'explanation' => 1,
597                                      'type' => 1,
598                                      'options' => 1},
599             explanation         => 'Validity checks on membership number: none or "Katipo" style checks',
600             type                => 'Choice',
601             options             => 'none|katipo'
602         },
603         {
604             uniquefieldrequired => 'variable',
605             variable            => 'maxoutstanding',
606             forceupdate         => { 'explanation' => 1,
607                                      'type' => 1 },
608             value               => '5',
609             explanation         =>
610             'maximum amount withstanding to be able make reserves ',
611             type                => 'Integer'
612         },
613         {
614             uniquefieldrequired => 'variable',
615             variable            => 'maxreserves',
616             forceupdate         => { 'explanation' => 1,
617                                      'type' => 1 },
618             value               => '5',
619             explanation         =>
620             'maximum number of reserves a member can make',
621             type                => 'Integer'
622
623         },
624         {
625             uniquefieldrequired => 'variable',
626             variable            => 'noissuescharge',
627             forceupdate         => { 'explanation' => 1,
628                                      'type' => 1 },
629             value               => '5',
630             explanation         =>
631             'maximum amount withstanding to be able to check out an item',
632             type                => 'Integer'
633
634         },
635         {
636             uniquefieldrequired => 'variable',
637             variable            => 'KohaAdminEmailAddress',
638             forceupdate         => { 'explanation' => 1,
639                                      'type' => 1 },
640             value               => 'your.mail@here',
641             explanation => 'the email address where borrowers modifs are sent',
642             type                => 'free'
643         },
644         {
645             uniquefieldrequired => 'variable',
646             variable            => 'gist',
647             forceupdate         => { 'explanation' => 1,
648                                      'type' => 1 },
649             value               => '0.125',
650             explanation => 'the gist rate. NOT in %, but in numeric form (0.12 for 12%)',
651             type                => 'free'
652         },
653         {
654             uniquefieldrequired => 'variable',
655             variable            => 'ldapserver',
656             forceupdate         => { 'explanation' => 1,
657                                      'type' => 1 },
658             value               => '',
659             explanation => 'your ldap server',
660             type                => 'free'
661         },
662         {
663             uniquefieldrequired => 'variable',
664             variable            => 'ldapinfos',
665             forceupdate         => { 'explanation' => 1,
666                                      'type' => 1 },
667             value               => '',
668             explanation => 'ldap info. The ldap will be used in dn : uid=xxx, <ldapinfos>',
669             type                => 'free'
670         },
671         {
672             uniquefieldrequired => 'variable',
673             variable            => 'printcirculationslips',
674             forceupdate         => { 'explanation' => 1,
675                                      'type' => 1 },
676             value               => '0',
677             explanation => 'if set to 1, print circulation slips. If set to 0, don\'t',
678             type                => 'free'
679         },
680         {
681             uniquefieldrequired => 'variable',
682             variable            => 'suggestion',
683             forceupdate         => { 'explanation' => 1,
684                                      'type' => 1 },
685             value               => '0',
686             explanation => 'if set to 1, suggestions are activated in OPAC',
687             type                => 'free'
688         },
689         {
690             uniquefieldrequired => 'variable',
691             variable            => 'ISBD',
692             forceupdate         => { 'explanation' => 1,
693                                      'type' => 1 },
694             value               => 'Fill with appropriate value...',
695             explanation => 'ISBD',
696             type                => 'free'
697         },
698         {
699             uniquefieldrequired => 'variable',
700             variable            => 'virtualshelves',
701             forceupdate         => { 'explanation' => 1,
702                                      'type' => 1 },
703             value               => '0',
704             explanation => 'Set virtual shelves management ON or OFF',
705             type                => 'YesNo'
706         },
707     ],
708
709 );
710
711 my %fielddefinitions = (
712     printers => [
713         {
714             field   => 'printername',
715             type    => 'char(40)',
716             null    => '',
717             key     => 'PRI',
718             default => ''
719         },
720     ],
721     aqbookfund => [
722         {
723             field   => 'bookfundid',
724             type    => 'char(5)',
725             null    => '',
726             key     => 'PRI',
727             default => ''
728         },
729     ],
730     aqbudget => [
731         {
732             field   => 'aqbudgetid',
733             type    => 'tinyint(4)',
734             null    => '',
735             key     => 'PRI',
736                   default =>'',
737             extra => 'auto_increment'
738         },
739     ],
740     z3950servers => [
741         {
742             field   => 'id',
743             type    => 'int',
744             null    => '',
745             key     => 'PRI',
746             default => '',
747             extra   => 'auto_increment'
748         },
749     ],
750         marc_breeding => [
751         {
752             field   => 'z3950random',
753             type    => 'varchar(40)',
754             null    => 'NULL',
755             key     => '',
756             default => '',
757             extra   => ''
758         },
759         {
760             field   => 'encoding',
761             type    => 'varchar(40)',
762             null    => '',
763             key     => '',
764             default => '',
765             extra   => ''
766         },
767     ],
768 );
769
770 #-------------------
771 # Initialize
772
773 # Start checking
774
775 # Get version of MySQL database engine.
776 my $mysqlversion = `mysqld --version`;
777 $mysqlversion =~ /Ver (\S*) /;
778 $mysqlversion = $1;
779 if ( $mysqlversion ge '3.23' ) {
780     print "Could convert to MyISAM database tables...\n" unless $silent;
781 }
782
783 #---------------------------------
784 # Tables
785
786 # Collect all tables into a list
787 $sth = $dbh->prepare("show tables");
788 $sth->execute;
789 while ( my ($table) = $sth->fetchrow ) {
790     $existingtables{$table} = 1;
791 }
792
793
794 # Now add any missing tables
795 foreach $table ( keys %requiretables ) {
796     unless ( $existingtables{$table} ) {
797         print "Adding $table table...\n" unless $silent;
798         my $sth = $dbh->prepare("create table $table $requiretables{$table}");
799         $sth->execute;
800         if ( $sth->err ) {
801             print "Error : $sth->errstr \n";
802             $sth->finish;
803         }    # if error
804     }    # unless exists
805 }    # foreach
806
807 # now drop useless tables
808 foreach $table ( keys %dropable_table ) {
809         if ( $existingtables{$table} ) {
810                 print "Dropping unused table $table\n" if $debug and not $silent;
811                 $dbh->do("drop table $table");
812                 if ( $dbh->err ) {
813                         print "Error : $dbh->errstr \n";
814                 }
815         }
816 }
817 unless ( $existingtables{'z3950servers'} ) {
818         #MJR: added syntax entries to close bug 624
819     print "Adding z3950servers table...\n" unless $silent;
820     my $sti = $dbh->prepare( "create table z3950servers (
821                                                                                 host char(255),
822                                                                                 port int,
823                                                                                 db char(255),
824                                                                                 userid char(255),
825                                                                                 password char(255),
826                                                                                 name text,
827                                                                                 id int,
828                                                                                 checked smallint,
829                                                                                 rank int,
830                                                                                 syntax char(80))"
831     );
832     $sti->execute;
833     $sti = $dbh->prepare( "insert into z3950servers
834                                                                 values ('z3950.loc.gov',
835                                                                 7090,
836                                                                 'voyager',
837                                                                 '', '',
838                                                                 'Library of Congress',
839                                                                 1, 1, 1, 'USMARC')"
840     );
841     $sti->execute;
842 }
843 unless ( $existingtables{'issuingrules'} ) {
844         $dbh->do("alter table categoryitem rename issuingrules");
845         $dbh->do("ALTER TABLE issuingrules ADD maxissueqty int(4) default NULL");
846         $dbh->do("ALTER TABLE issuingrules ADD issuelength int(4) detault NULL");
847         $dbh->do("ALTER TABLE issuingrules ADD branchcode varchar(4) NOT NULL default ''");
848         print "renaming categoryitem\n" unless $silent;
849 }
850
851
852 #---------------------------------
853 # Columns
854
855 foreach $table ( keys %requirefields ) {
856     print "Check table $table\n" if $debug and not $silent;
857     $sth = $dbh->prepare("show columns from $table");
858     $sth->execute();
859     undef %types;
860     while ( ( $column, $type, $null, $key, $default, $extra ) = $sth->fetchrow )
861     {
862         $types{$column} = $type;
863     }    # while
864     foreach $column ( keys %{ $requirefields{$table} } ) {
865         print "  Check column $column  [$types{$column}]\n" if $debug and not $silent;
866         if ( !$types{$column} ) {
867
868             # column doesn't exist
869             print "Adding $column field to $table table...\n" unless $silent;
870             $query = "alter table $table
871                         add column $column " . $requirefields{$table}->{$column};
872             print "Execute: $query\n" if $debug;
873             my $sti = $dbh->prepare($query);
874             $sti->execute;
875             if ( $sti->err ) {
876                 print "**Error : $sti->errstr \n";
877                 $sti->finish;
878             }    # if error
879         }    # if column
880     }    # foreach column
881 }    # foreach table
882
883 foreach $table ( keys %fielddefinitions ) {
884         print "Check table $table\n" if $debug;
885         $sth = $dbh->prepare("show columns from $table");
886         $sth->execute();
887         my $definitions;
888         while ( ( $column, $type, $null, $key, $default, $extra ) = $sth->fetchrow )
889         {
890                 $definitions->{$column}->{type}    = $type;
891                 $definitions->{$column}->{null}    = $null;
892                 $definitions->{$column}->{key}     = $key;
893                 $definitions->{$column}->{default} = $default;
894                 $definitions->{$column}->{extra}   = $extra;
895         }    # while
896         my $fieldrow = $fielddefinitions{$table};
897         foreach my $row (@$fieldrow) {
898                 my $field   = $row->{field};
899                 my $type    = $row->{type};
900                 my $null    = $row->{null};
901                 my $key     = $row->{key};
902                 my $default = $row->{default};
903                 $default="''" unless $default;
904                 my $extra   = $row->{extra};
905                 my $def     = $definitions->{$field};
906                 unless ( $type eq $def->{type}
907                         && $null eq $def->{null}
908                         && $key eq $def->{key}
909                         && $default eq $def->{default}
910                         && $extra eq $def->{extra} )
911                 {
912
913                         if ( $null eq '' ) {
914                                 $null = 'NOT NULL';
915                         }
916                         if ( $key eq 'PRI' ) {
917                                 $key = 'PRIMARY KEY';
918                         }
919                         unless ( $extra eq 'auto_increment' ) {
920                                 $extra = '';
921                         }
922                         # if it's a new column use "add", if it's an old one, use "change".
923                         my $action;
924                         if ($definitions->{$field}->{type}) {
925                                 $action="change $field"
926                         } else {
927                                 $action="add";
928                         }
929 # if it's a primary key, drop the previous pk, before altering the table
930                         my $sth;
931                         if ($key ne 'PRIMARY KEY') {
932                                 $sth =$dbh->prepare("alter table $table $action $field $type $null $key $extra default ?");
933                         } else {
934                                 $sth =$dbh->prepare("alter table $table drop primary key, $action $field $type $null $key $extra default ?");
935                         }
936                         $sth->execute($default);
937                         print "  Alter $field in $table\n" unless $silent;
938                 }
939         }
940 }
941
942 # Get list of columns from borrowers table
943 my %itemtypes;
944 my %nullenabled;
945 $sth = $dbh->prepare("show columns from borrowers");
946 $sth->execute;
947 while ( my ( $column, $type, $null, $key, $default, $extra ) = $sth->fetchrow )
948 {
949     $itemtypes{$column} = $type;
950     $nullenabled{$column} = $null;
951 }
952
953 unless ( $itemtypes{'cardnumber'} eq 'varchar(20)' ) {
954     $itemtypes{'cardnumber'} =~ /varchar\((\d+)\)/;
955     my $oldlength = $1;
956     if ( $oldlength < 16 ) {
957         print "Setting maximum cardnumber length to 16 (was $oldlength) and marking unique.\n" unless $silent;
958         my $sti =
959           $dbh->prepare(
960             "alter table borrowers change cardnumber cardnumber varchar(16)");
961         $sti->execute;
962         $sti->finish;
963         $sti =
964           $dbh->prepare(
965             "alter table borrowers drop index cardnumber");
966         $sti->execute;
967         $sti->finish;
968         $sti =
969           $dbh->prepare(
970             "alter table borrowers add unique(cardnumber)");
971         $sti->execute;
972         $sti->finish;
973     }
974 }
975 #
976 # Get list of columns from items table
977 $sth = $dbh->prepare("show columns from items");
978 $sth->execute;
979 while ( my ( $column, $type, $null, $key, $default, $extra ) = $sth->fetchrow )
980 {
981     $itemtypes{$column} = $type;
982     $nullenabled{$column} = $null;
983 }
984
985 unless ( $itemtypes{'barcode'} eq 'varchar(20)' ) {
986     $itemtypes{'barcode'} =~ /varchar\((\d+)\)/;
987     my $oldlength = $1;
988     if ( $oldlength < 20 ) {
989         print "Setting maximum barcode length to 20 (was $oldlength).\n" unless $silent;
990         my $sti =
991           $dbh->prepare(
992             "alter table items change barcode barcode varchar(20)");
993         $sti->execute;
994     }
995 }
996 #
997 # dropping unique barcode index & setting barcode to null allowed.
998 #
999 $sth = $dbh->prepare("show index from items");
1000 $sth->execute;
1001 while ( my ( $table, $non_unique, $key_name, $Seq_in_index, $Column_name, $Collation, $cardinality, $sub_part, $Packed, $comment ) = $sth->fetchrow )
1002 {
1003         if ($key_name eq 'barcode' && $non_unique eq 0) {
1004                 print "dropping BARCODE index to enable empty barcodes\n" unless $silent;
1005                 $dbh->do("ALTER TABLE `items` DROP INDEX `barcode`");
1006         }
1007 }
1008 $dbh->do("ALTER TABLE `items` CHANGE `barcode` `barcode` VARCHAR( 20 )") unless ($nullenabled{barcode} eq 'YES');
1009
1010 #
1011 # creating fulltext index in bibliothesaurus if needed
1012 #
1013 $sth = $dbh->prepare("show index from bibliothesaurus");
1014 $sth->execute;
1015 my $exists=0;
1016 while ( my ( $table, $non_unique, $key_name, $Seq_in_index, $Column_name, $Collation, $cardinality, $sub_part, $Packed, $comment ) = $sth->fetchrow )
1017 {
1018         if ($key_name eq 'category_2') {
1019                 $exists=1;
1020         }
1021 }
1022 print "Creating fulltext index on bibliothesaurus\n" unless $exists or $silent;
1023 $dbh->do('create fulltext index category_2 on bibliothesaurus (category,freelib)') unless $exists;
1024
1025 #
1026 # creating  index in z3950results if needed
1027 #
1028 $sth = $dbh->prepare("show index from z3950results");
1029 $sth->execute;
1030 my $exists=0;
1031 while ( my ( $table, $non_unique, $key_name, $Seq_in_index, $Column_name, $Collation, $cardinality, $sub_part, $Packed, $comment ) = $sth->fetchrow )
1032 {
1033         if ($key_name eq 'query_server') {
1034                 $exists=1;
1035         }
1036 }
1037 print "Creating  index on z3950results\n" unless $exists or $silent;
1038 $dbh->do('create unique index query_server on z3950results (queryid,server)') unless $exists;
1039
1040 # changing z3950daemon field to NULL in marc_breeding
1041 $dbh->do("ALTER TABLE `marc_breeding` CHANGE `z3950random` `z3950random` VARCHAR( 40 )");
1042
1043 # making borrowernumber an auto_increment field
1044 $dbh->do("ALTER TABLE `borrowers` CHANGE `borrowernumber` `borrowernumber` INTEGER auto_increment");
1045
1046 # changing indexes in marc_*_structure to use frameworkcode
1047 $dbh->do('alter table marc_subfield_structure drop index tab');
1048 $dbh->do('create index tab on marc_subfield_structure (frameworkcode,tab)');
1049 $dbh->do('alter table marc_subfield_structure drop index kohafield');
1050 $dbh->do('create index kohafield on marc_subfield_structure (frameworkcode,kohafield)');
1051
1052
1053 # extending the timestamp in branchtransfers...
1054 my %branchtransfers;
1055
1056 $sth = $dbh->prepare("show columns from branchtransfers");
1057 $sth->execute;
1058 while ( my ( $column, $type, $null, $key, $default, $extra ) = $sth->fetchrow )
1059 {
1060     $branchtransfers{$column} = $type;
1061 }
1062
1063 unless ( $branchtransfers{'datesent'} eq 'datetime' ) {
1064     print "Setting type of datesent in branchtransfers to datetime.\n" unless $silent;
1065     my $sti =
1066       $dbh->prepare(
1067         "alter table branchtransfers change datesent datesent datetime");
1068     $sti->execute;
1069 }
1070
1071 unless ( $branchtransfers{'datearrived'} eq 'datetime' ) {
1072     print "Setting type of datearrived in branchtransfers to datetime.\n" unless $silent;
1073     my $sti =
1074       $dbh->prepare(
1075         "alter table branchtransfers change datearrived datearrived datetime");
1076     $sti->execute;
1077 }
1078
1079 # changing the branchcategories table around...
1080 my %branchcategories;
1081
1082 $sth = $dbh->prepare("show columns from branchcategories");
1083 $sth->execute;
1084 while ( my ( $column, $type, $null, $key, $default, $extra ) = $sth->fetchrow )
1085 {
1086     $branchcategories{$column} = $type;
1087 }
1088
1089 unless ( $branchcategories{'categorycode'} eq 'varchar(4)' ) {
1090     print
1091 "Setting type of categorycode in branchcategories to varchar(4),\n and making the primary key.\n" unless $silent;
1092     my $sti =
1093       $dbh->prepare(
1094 "alter table branchcategories change categorycode categorycode varchar(4) not null"
1095     );
1096     $sti->execute;
1097     $sti =
1098       $dbh->prepare(
1099         "alter table branchcategories add primary key (categorycode)");
1100     $sti->execute;
1101 }
1102
1103 unless ( $branchcategories{'categoryname'} eq 'text' ) {
1104     print "Changing branchcode in branchcategories to categoryname text.\n" unless $silent;
1105     my $sth =
1106       $dbh->prepare(
1107         "alter table branchcategories change branchcode categoryname text");
1108     $sth->execute;
1109 }
1110
1111 unless ( $branchcategories{'codedescription'} eq 'text' ) {
1112     print
1113 "Replacing branchholding in branchcategories with codedescription text.\n" unless $silent;
1114     my $sth =
1115       $dbh->prepare(
1116         "alter table branchcategories change branchholding codedescription text"
1117     );
1118     $sth->execute;
1119 }
1120
1121 # changing the items table around...
1122 my %items;
1123
1124 $sth = $dbh->prepare("show columns from items");
1125 $sth->execute;
1126 while ( my ( $column, $type, $null, $key, $default, $extra ) = $sth->fetchrow )
1127 {
1128     $items{$column} = $type;
1129 }
1130
1131 if ($items{'bulk'} eq "varchar(30)") {
1132     print "  Setting callnumber in items table\n" unless $silent;
1133     my $sti =
1134       $dbh->prepare("ALTER TABLE `items` CHANGE `bulk` `itemcallnumber` VARCHAR( 30 ) DEFAULT NULL");
1135     $sti->execute;
1136     $sti = $dbh->prepare("update marc_subfield_structure set kohafield=\"items.itemcallnumber\" where kohafield=\"items.bulk\"");
1137     $sti->execute;
1138 }
1139
1140 # changing the marc_subfield_structure table around...
1141 my %marc_subfield_structure;
1142
1143 $sth = $dbh->prepare("show columns from marc_subfield_structure");
1144 $sth->execute;
1145 while ( my ( $column, $type, $null, $key, $default, $extra ) = $sth->fetchrow )
1146 {
1147     $marc_subfield_structure{$column} = $type;
1148 }
1149
1150 if ($marc_subfield_structure{thesaurus_category}) {
1151     print "  changing thesaurus_category in marc_subfield_structure table\n" unless $silent;
1152     my $sti =
1153       $dbh->prepare("ALTER TABLE marc_subfield_structure CHANGE `thesaurus_category` `authtypecode` VARCHAR(10 ) DEFAULT NULL");
1154     $sti->execute;
1155 }
1156
1157 #
1158 # creating  index in issuingrules if needed
1159 #
1160 $sth = $dbh->prepare("show index from issuingrules");
1161 $sth->execute;
1162 my $exists=0;
1163 while ( my ( $table, $non_unique, $key_name, $Seq_in_index, $Column_name, $Collation, $cardinality, $sub_part, $Packed, $comment ) = $sth->fetchrow )
1164 {
1165         if ($key_name eq 'PRIMARY') {
1166                 $exists=1;
1167         }
1168 }
1169 print "Creating  index on issuing rules\n" unless $exists or $silent;
1170 $dbh->do('ALTER TABLE issuingrules ADD PRIMARY KEY ( branchcode, categorycode, itemtype )') unless $exists;
1171
1172 $dbh->do('ALTER TABLE marc_tag_structure drop primary key');
1173 $dbh->do('ALTER TABLE marc_tag_structure ADD PRIMARY KEY ( frameworkcode, tagfield )');
1174
1175 $dbh->do('ALTER TABLE marc_subfield_structure drop primary key');
1176 $dbh->do('ALTER TABLE marc_subfield_structure ADD PRIMARY KEY ( frameworkcode, tagfield, tagsubfield )');
1177
1178 # Get list of columns from marc_word table
1179 my %marc_word;
1180 my %nullenabled;
1181 $sth = $dbh->prepare("show columns from marc_word");
1182 $sth->execute;
1183 while ( my ( $column, $type, $null, $key, $default, $extra ) = $sth->fetchrow )
1184 {
1185     $marc_word{$column} = $type;
1186     $nullenabled{$column} = $null;
1187 }
1188 if ($marc_word{subfieldid}) {
1189         #create field tagsubfield, copy tag+subfieldid, then drop tag and subfieldid
1190         print "Modifying marc_word (concat on tag and subfield for better perfs)\n" unless $silent;
1191         $dbh->do("ALTER TABLE `marc_word` ADD `tagsubfield` CHAR( 4 ) NOT NULL AFTER `bibid`");
1192         $dbh->do("update marc_word set tagsubfield=concat(tag,subfieldid)");
1193         $dbh->do("alter table marc_word drop tag");
1194         $dbh->do("alter table marc_word drop subfieldid");
1195         $dbh->do("create index Search_Marc on marc_word (tagsubfield,word)");
1196 }
1197 # Populate tables with required data
1198
1199 foreach my $table ( keys %tabledata ) {
1200     print "Checking for data required in table $table...\n" unless $silent;
1201     my $tablerows = $tabledata{$table};
1202     foreach my $row (@$tablerows) {
1203         my $uniquefieldrequired = $row->{uniquefieldrequired};
1204         my $uniquevalue         = $row->{$uniquefieldrequired};
1205         my $forceupdate         = $row->{forceupdate};
1206         my $sth                 =
1207           $dbh->prepare(
1208 "select $uniquefieldrequired from $table where $uniquefieldrequired=?"
1209         );
1210         $sth->execute($uniquevalue);
1211         if ($sth->rows) {
1212             foreach my $field (keys %$forceupdate) {
1213                 if ($forceupdate->{$field}) {
1214                     my $sth=$dbh->prepare("update systempreferences set $field=? where $uniquefieldrequired=?");
1215                     $sth->execute($row->{$field}, $uniquevalue);
1216                 }
1217             }
1218         } else {
1219             print "Adding row to $table: " unless $silent;
1220             my @values;
1221             my $fieldlist;
1222             my $placeholders;
1223             foreach my $field ( keys %$row ) {
1224                 next if $field eq 'uniquefieldrequired';
1225                 next if $field eq 'forceupdate';
1226                 my $value = $row->{$field};
1227                 push @values, $value;
1228                 print "  $field => $value" unless $silent;
1229                 $fieldlist .= "$field,";
1230                 $placeholders .= "?,";
1231             }
1232             print "\n" unless $silent;
1233             $fieldlist    =~ s/,$//;
1234             $placeholders =~ s/,$//;
1235             my $sth =
1236               $dbh->prepare(
1237                 "insert into $table ($fieldlist) values ($placeholders)");
1238             $sth->execute(@values);
1239         }
1240     }
1241 }
1242
1243 $sth->finish;
1244
1245 exit;
1246
1247 # $Log$
1248 # Revision 1.87  2004/06/23 13:03:09  tipaul
1249 # fixes in DB structure
1250 #
1251 # Revision 1.86  2004/06/22 11:30:57  tipaul
1252 # adding -s (silent) flag, to have a silent install.
1253 # only updater will be verbose
1254 #
1255 # Revision 1.85  2004/06/17 15:19:44  tipaul
1256 # missing Marc_Search index on marc_word
1257 #
1258 # Revision 1.84  2004/06/17 08:25:21  tipaul
1259 # DB modifs : merging tag & subfield in marc_word table
1260 #
1261 # Revision 1.83  2004/06/10 08:32:02  tipaul
1262 # MARC authority management (continued)
1263 #
1264 # Revision 1.82  2004/06/03 12:46:58  tipaul
1265 # * frameworks and itemtypes are independant
1266 #
1267 # WARNING : will work only if applied to a 2.0 base. some modifs have been done since last commit that will NOT be applied if you run updatedatabase again.
1268 #
1269 # Revision 1.81  2004/05/28 09:56:21  tipaul
1270 # bugfix
1271 #
1272 # Revision 1.80  2004/05/28 08:32:00  tipaul
1273 # adding :
1274 # * MARC authority file
1275 # * seealso & hidden in MARC biblio structure.
1276 #
1277 # Revision 1.79  2004/05/18 09:50:07  tipaul
1278 # *** empty log message ***
1279 #
1280 # Revision 1.78  2004/05/10 09:29:33  tipaul
1281 # css is now the default theme for OPAC.
1282 # It will be the theme used for improvements and new things in OPAC.
1283 #
1284 # Revision 1.77  2004/05/06 14:56:51  tipaul
1285 # adding table issuingrules (previously called categoryitem
1286 #
1287 # Revision 1.76  2004/05/03 09:32:25  tipaul
1288 # adding printcirculationsplit parameter (already existed, but was not in systempref by defaul)
1289 #
1290 # Revision 1.75  2004/04/14 19:49:00  tipaul
1291 # seealso field set to 255 chars
1292 #
1293 # Revision 1.74  2004/03/11 16:10:16  tipaul
1294 # *** empty log message ***
1295 #
1296 # Revision 1.73  2004/03/06 20:26:13  tipaul
1297 # adding seealso feature in MARC searches
1298 #