Add a new system preference 'AutomaticItemReturn' :
[koha_fer] / updater / updatedatabase
index 6a74470..133bc54 100755 (executable)
 # - Would also be a good idea to offer to do a backup at this time...
 
 # NOTE:  If you do something more than once in here, make it table driven.
-
 use strict;
 
 # CPAN modules
 use DBI;
-
+use Getopt::Long;
 # Koha modules
-use C4::Context("/etc/koha.conf.tmp");
-
-# FIXME - /etc/koha.conf might not exist, so shouldn't use
-# C4::Context.
+use C4::Context;
 
+use MARC::Record;
+use MARC::File::XML ( BinaryEncoding => 'utf8' );
 # FIXME - The user might be installing a new database, so can't rely
 # on /etc/koha.conf anyway.
 
@@ -40,454 +39,1007 @@ my (
     $prefitem,          # preference item in systempreferences table
 );
 
+my $silent;
+GetOptions(
+       's' =>\$silent
+       );
 my $dbh = C4::Context->dbh;
+print "connected to your DB. Checking & modifying it\n" unless $silent;
+$|=1; # flushes output
 
 #-------------------
 # Defines
 
 # Tables to add if they don't exist
 my %requiretables = (
-    shelfcontents => "( shelfnumber int not null,
-                                                       itemnumber int not null,
-                                                       flags int)",
-    bookshelf => "( shelfnumber int auto_increment primary key,
-                                               shelfname char(255))",
-    z3950queue => "( id int auto_increment primary key,
-                                               term text,
-                                               type char(10),
-                                               startdate int,
-                                               enddate int,
-                                               done smallint,
-                                               results longblob,
-                                               numrecords int,
-                                               servers text,
-                                               identifier char(30))",
-    z3950results => "( id int auto_increment primary key,
-                                               queryid int,
-                                               server char(255),
-                                               startdate int,
-                                               enddate int,
-                                               results longblob,
-                                               numrecords int,
-                                               numdownloaded int,
-                                               highestseen int,
-                                               active smallint)",
-    branchrelations => "( branchcode varchar(4),
-                                                       categorycode varchar(4))",
-    websites => "( websitenumber int(11) NOT NULL auto_increment,
-                                               biblionumber int(11) NOT NULL default '0',
-                                               title text,
-                                               description text,
-                                               url varchar(255),
-                                               PRIMARY KEY (websitenumber) )",
-    marcrecorddone => "( isbn char(40),
-                                                               issn char(40),
-                                                               lccn char(40),
-                                                               controlnumber char(40))",
-    uploadedmarc => "( id int(11) NOT NULL auto_increment PRIMARY KEY,
-                                                       marc longblob,
-                                                       hidden smallint(6) default NULL,
-                                                       name varchar(255) default NULL)",
-    ethnicity => "( code varchar(10) NOT NULL default '',
-                                       name varchar(255) default NULL,
-                                       PRIMARY KEY  (code)   )",
-    sessions => "( sessionID varchar(255) NOT NULL default '',
-                                               userid varchar(255) default NULL,
-                                               ip varchar(16) default NULL,
-                                               lasttime int,
-                                               PRIMARY KEY (sessionID)   )",
-    sessionqueries => "( sessionID varchar(255) NOT NULL default '',
-                                                               userid char(100) NOT NULL default '',
-                                                               ip char(18) NOT NULL default '',
-                                                               url text NOT NULL default ''  )",
-    bibliothesaurus => "( id bigint(20) NOT NULL auto_increment,
-                                                       freelib char(255) NOT NULL default '',
-                                                       stdlib char(255) NOT NULL default '',
-                                                       category char(10) NOT NULL default '',
-                                                       level tinyint(4) NOT NULL default '1',
-                                                       hierarchy char(80) NOT NULL default '',
-                                                       father bigint(20) NOT NULL default '',
-                                                       PRIMARY KEY  (id),
-                                                       KEY freelib (freelib),
-                                                       KEY stdlib (stdlib),
-                                                       KEY category (category),
-                                                       KEY hierarchy (hierarchy)
-                                                       )",
-    marc_biblio => "(
-                                               bibid bigint(20) unsigned NOT NULL auto_increment,
-                                               biblionumber int(11) NOT NULL default '0',
-                                               datecreated date NOT NULL default '0000-00-00',
-                                               datemodified date default NULL,
-                                               origincode char(20) default NULL,
-                                               PRIMARY KEY  (bibid),
-                                               KEY origincode (origincode),
-                                               KEY biblionumber (biblionumber)
-                                               ) ",
-    marc_blob_subfield => "(
-                                       blobidlink bigint(20) NOT NULL auto_increment,
-                                       subfieldvalue longtext NOT NULL,
-                                       PRIMARY KEY  (blobidlink)
-                                       ) ",
-    marc_subfield_structure => "(
-                                               tagfield char(3) NOT NULL default '',
-                                               tagsubfield char(1) NOT NULL default '',
-                                               liblibrarian char(255) NOT NULL default '',
-                                               libopac char(255) NOT NULL default '',
-                                               repeatable tinyint(4) NOT NULL default '0',
-                                               mandatory tinyint(4) NOT NULL default '0',
-                                               kohafield char(40)  default NULL,
-                                               tab tinyint(1) default NULL,
-                                               authorised_value char(10) default NULL,
-                                               thesaurus_category char(10) default NULL,
-                                               value_builder char(80) default NULL,
-                                               PRIMARY KEY  (tagfield,tagsubfield),
-                                               KEY kohafield (kohafield),
-                                               KEY tab (tab)
-                                               )",
-    marc_subfield_table => "(
-                                               subfieldid bigint(20) unsigned NOT NULL auto_increment,
-                                               bibid bigint(20) unsigned NOT NULL default '0',
-                                               tag char(3) NOT NULL default '',
-                                               tagorder tinyint(4) NOT NULL default '1',
-                                               tag_indicator char(2) NOT NULL default '',
-                                               subfieldcode char(1) NOT NULL default '',
-                                               subfieldorder tinyint(4) NOT NULL default '1',
-                                               subfieldvalue varchar(255) default NULL,
-                                               valuebloblink bigint(20) default NULL,
-                                               PRIMARY KEY  (subfieldid),
-                                               KEY bibid (bibid),
-                                               KEY tag (tag),
-                                               KEY tag_indicator (tag_indicator),
-                                               KEY subfieldorder (subfieldorder),
-                                               KEY subfieldcode (subfieldcode),
-                                               KEY subfieldvalue (subfieldvalue),
-                                               KEY tagorder (tagorder)
+    categorytable       => "(categorycode char(5) NOT NULL default '',
+                             description text default '',
+                             itemtypecodes text default '',
+                             PRIMARY KEY (categorycode)
+                            )",
+    subcategorytable       => "(subcategorycode char(5) NOT NULL default '',
+                             description text default '',
+                             itemtypecodes text default '',
+                             PRIMARY KEY (subcategorycode)
+                            )",
+    mediatypetable       => "(mediatypecode char(5) NOT NULL default '',
+                             description text default '',
+                             itemtypecodes text default '',
+                             PRIMARY KEY (mediatypecode)
+                            )",
+    action_logs        => "(
+                                   `timestamp` TIMESTAMP NOT NULL ,
+                                   `user` INT( 11 ) NOT NULL ,
+                                   `module` TEXT default '',
+                                   `action` TEXT default '' ,
+                                   `object` INT(11) default '' ,
+                                   `info` TEXT default '' ,
+                                   PRIMARY KEY ( `timestamp` , `user` )
+                           )",
+       letter          => "(
+                                       module varchar(20) NOT NULL default '',
+                                       code varchar(20) NOT NULL default '',
+                                       name varchar(100) NOT NULL default '',
+                                       title varchar(200) NOT NULL default '',
+                                       content text,
+                                       PRIMARY KEY  (module,code)
+                               )",
+       alert           =>"(
+                                       alertid int(11) NOT NULL auto_increment,
+                                       borrowernumber int(11) NOT NULL default '0',
+                                       type varchar(10) NOT NULL default '',
+                                       externalid varchar(20) NOT NULL default '',
+                                       PRIMARY KEY  (alertid),
+                                       KEY borrowernumber (borrowernumber),
+                                       KEY type (type,externalid)
+                               )",
+       opac_news => "(
+                               `idnew` int(10) unsigned NOT NULL auto_increment,
+                               `title` varchar(250) NOT NULL default '',
+                               `new` text NOT NULL,
+                               `lang` varchar(4) NOT NULL default '',
+                               `timestamp` timestamp NOT NULL default CURRENT_TIMESTAMP,
+                               PRIMARY KEY  (`idnew`)
+                               )",
+       repeatable_holidays => "(
+                               `id` int(11) NOT NULL auto_increment,
+                               `branchcode` varchar(4) NOT NULL default '',
+                               `weekday` smallint(6) default NULL,
+                               `day` smallint(6) default NULL,
+                               `month` smallint(6) default NULL,
+                               `title` varchar(50) NOT NULL default '',
+                               `description` text NOT NULL,
+                               PRIMARY KEY  (`id`)
+                               )",
+       special_holidays => "(
+                               `id` int(11) NOT NULL auto_increment,
+                               `branchcode` varchar(4) NOT NULL default '',
+                               `day` smallint(6) NOT NULL default '0',
+                               `month` smallint(6) NOT NULL default '0',
+                               `year` smallint(6) NOT NULL default '0',
+                               `isexception` smallint(1) NOT NULL default '1',
+                               `title` varchar(50) NOT NULL default '',
+                               `description` text NOT NULL,
+                               PRIMARY KEY  (`id`)
+                               )",
+       overduerules    =>"(`branchcode` varchar(255) NOT NULL default '',
+                                       `categorycode` char(2) NOT NULL default '',
+                                       `delay1` int(4) default '0',
+                                       `letter1` varchar(20) default NULL,
+                                       `debarred1` char(1) default '0',
+                                       `delay2` int(4) default '0',
+                                       `debarred2` char(1) default '0',
+                                       `letter2` varchar(20) default NULL,
+                                       `delay3` int(4) default '0',
+                                       `letter3` varchar(20) default NULL,
+                                       `debarred3` int(1) default '0',
+                                       PRIMARY KEY  (`branchcode`,`categorycode`)
                                        )",
-    marc_tag_structure => "(
-                                       tagfield char(3) NOT NULL default '',
-                                       liblibrarian char(255) NOT NULL default '',
-                                       libopac char(255) NOT NULL default '',
-                                       repeatable tinyint(4) NOT NULL default '0',
-                                       mandatory tinyint(4) NOT NULL default '0',
-                                       authorised_value char(10) default NULL,
-                                       PRIMARY KEY  (tagfield)
+       cities                  => "(`cityid` int auto_increment,
+                                               `city_name` char(100) NOT NULL,
+                                               `city_zipcode` char(20),
+                                               PRIMARY KEY (`cityid`)
                                        )",
-    marc_word => "(
-                               bibid bigint(20) NOT NULL default '0',
-                               tag char(3) NOT NULL default '',
-                               tagorder tinyint(4) NOT NULL default '1',
-                               subfieldid char(1) NOT NULL default '',
-                               subfieldorder tinyint(4) NOT NULL default '1',
-                               word varchar(255) NOT NULL default '',
-                               sndx_word varchar(255) NOT NULL default '',
-                               KEY bibid (bibid),
-                               KEY tag (tag),
-                               KEY tagorder (tagorder),
-                               KEY subfieldid (subfieldid),
-                               KEY subfieldorder (subfieldorder),
-                               KEY word (word),
-                               KEY sndx_word (sndx_word)
-                       )",
-    marc_breeding => "(  id bigint(20) NOT NULL auto_increment,
-                               file varchar(80) NOT NULL default '',
-                               isbn varchar(10) NOT NULL default '',
-                               title varchar(128) default NULL,
-                               author varchar(80) default NULL,
-                               marc text NOT NULL,
-                               encoding varchar(40) default NULL,
-                               PRIMARY KEY  (id),
-                               KEY title (title),
-                               KEY isbn (isbn)
-                       )",
-    authorised_values => "(id int(11) NOT NULL auto_increment,
-                               category char(10) NOT NULL default '',
-                               authorised_value char(80) NOT NULL default '',
-                               lib char(80) NULL,
-                               PRIMARY KEY  (id),
-                               KEY name (category)
-                       )",
-    userflags => "( bit int(11) NOT NULL default '0',
-                               flag char(30), flagdesc char(255),
-                               defaulton int(11)
-                       )",
+       roadtype                        => "(`roadtypeid` int auto_increment,
+                                               `road_type` char(100) NOT NULL,
+                                               PRIMARY KEY (`roadtypeid`)
+                                       )",
+
+       labels                     => "(
+                               labelid int(11) NOT NULL auto_increment,
+                               itemnumber varchar(100) NOT NULL default '',
+                               timestamp timestamp(14) NOT NULL,
+                               PRIMARY KEY  (labelid)
+                               )",
+
+       labels_conf                => "(
+                               id int(4) NOT NULL auto_increment,
+                               barcodetype char(100) default '',
+                               title tinyint(1) default '0',
+                               isbn tinyint(1) default '0',
+                               itemtype tinyint(1) default '0',
+                               barcode tinyint(1) default '0',
+                               dewey tinyint(1) default '0',
+                               class tinyint(1) default '0',
+                               author tinyint(1) default '0',
+                               papertype char(100) default '',
+                               startrow int(2) default NULL,
+                               PRIMARY KEY  (id)
+                               )",
+
 );
 
 my %requirefields = (
-    biblio        => { 'abstract' => 'text' },
-    deletedbiblio => { 'abstract' => 'text' },
-    biblioitems   => {
-        'lccn' => 'char(25)',
-        'url'  => 'varchar(255)',
-        'marc' => 'text'
-    },
-    deletedbiblioitems => {
-        'lccn' => 'char(25)',
-        'url'  => 'varchar(255)',
-        'marc' => 'text'
-    },
-    branchtransfers => { 'datearrived'    => 'datetime' },
-    statistics      => { 'borrowernumber' => 'int(11)' },
-    aqbooksellers   => {
-        'invoicedisc' => 'float(6,4)',
-        'nocalc'      => 'int(11)'
-    },
-    borrowers => {
-        'userid'        => 'char(30)',
-        'password'      => 'char(30)',
-        'flags'         => 'int(11)',
-        'textmessaging' => 'varchar(30)'
-    },
-    aqorders => { 'budgetdate' => 'date' },
-
-    #added so that reference items are not available for reserves...
-    itemtypes         => { 'notforloan'  => 'smallint(6)' },
-    systempreferences => { 'explanation' => 'char(80)' },
-    z3950servers      => { 'syntax'      => 'char(80)' },
+       subscription => { 'letter' => 'char(20) NULL', 'distributedto' => 'text NULL'},
+       itemtypes => { 'imageurl' => 'char(200) NULL'},
+       aqbookfund => { 'branchcode' => 'varchar(4) NULL'},
+       aqbudget => { 'branchcode' => 'varchar(4) NULL'},
+       auth_header => { 'marc' => 'BLOB NOT NULL', 'linkid' => 'BIGINT(20) NULL'},
+       auth_subfield_structure =>{ 'hidden' => 'TINYINT(3) NOT NULL UNSIGNED ZEROFILL', 'kohafield' => 'VARCHAR(45) NOT NULL', 'linkid' =>  'TINYINT(1) NOT NULL UNSIGNED', 'isurl' => 'TINYINT(1) UNSIGNED'},
+        statistics => { 'associatedborrower' => 'integer'},
+#    tablename        => { 'field' => 'fieldtype' },
 );
 
 my %dropable_table = (
-    classification => 'classification',
-    multipart      => 'multipart',
-    multivolume    => 'multivolume',
-    newitems       => 'newitems',
-    procedures     => 'procedures',
-    publisher      => 'publisher',
-    searchstats    => 'searchstats',
-    serialissues   => 'serialissues',
+       sessionqueries  => 'sessionqueries',
+       marcrecorddone  => 'marcrecorddone',
+       users                   => 'users',
+       itemsprices             => 'itemsprices',
+       biblioanalysis  => 'biblioanalysis',
+       borexp                  => 'borexp',
+# tablename => 'tablename',
 );
 
+my %uselessfields = (
+# tablename => "field1,field2",
+       borrowers => "suburb,altstreetaddress,altsuburb,altcity,studentnumber,school,area,preferredcont,altcp",
+       );
+# the other hash contains other actions that can't be done elsewhere. they are done
+# either BEFORE of AFTER everything else, depending on "when" entry (default => AFTER)
+
 # The tabledata hash contains data that should be in the tables.
 # The uniquefieldrequired hash entry is used to determine which (if any) fields
 # must not exist in the table for this row to be inserted.  If the
 # uniquefieldrequired entry is already in the table, the existing data is not
-# modified.
+# modified, unless the forceupdate hash entry is also set.  Fields in the
+# anonymous "forceupdate" hash will be forced to be updated to the default
+# values given in the %tabledata hash.
 
 my %tabledata = (
-    userflags => [
-        {
-            uniquefieldrequired => 'bit',
-            bit                 => 0,
-            flag                => 'superlibrarian',
-            flagdesc            => 'Access to all librarian functions',
-            defaulton           => 0
-        },
-        {
-            uniquefieldrequired => 'bit',
-            bit                 => 1,
-            flag                => 'circulate',
-            flagdesc            => 'Circulate books',
-            defaulton           => 0
-        },
-        {
-            uniquefieldrequired => 'bit',
-            bit                 => 2,
-            flag                => 'catalogue',
-            flagdesc            => 'View Catalogue (Librarian Interface)',
-            defaulton           => 0
+# tablename => [
+#      {       uniquefielrequired => 'fieldname', # the primary key in the table
+#              fieldname => fieldvalue,
+#              fieldname2 => fieldvalue2,
+#      },
+# ],
+    systempreferences => [
+               {
+            uniquefieldrequired => 'variable',
+            variable            => 'Activate_Log',
+            value               => 'On',
+           forceupdate         => { 'explanation' => 1,
+                                    'type' => 1},
+            explanation         => 'Turn Log Actions on DB On an Off',
+           type                => 'YesNo',
         },
         {
-            uniquefieldrequired => 'bit',
-            bit                 => 3,
-            flag                => 'parameters',
-            flagdesc            => 'Set Koha system paramters',
-            defaulton           => 0
+            uniquefieldrequired => 'variable',
+            variable            => 'IndependantBranches',
+            value               => 0,
+           forceupdate         => { 'explanation' => 1,
+                                    'type' => 1},
+            explanation         => 'Turn Branch independancy management On an Off',
+           type                => 'YesNo',
         },
-        {
-            uniquefieldrequired => 'bit',
-            bit                 => 4,
-            flag                => 'borrowers',
-            flagdesc            => 'Add or modify borrowers',
-            defaulton           => 0
+               {
+            uniquefieldrequired => 'variable',
+            variable            => 'ReturnBeforeExpiry',
+            value               => 'Off',
+           forceupdate         => { 'explanation' => 1,
+                                    'type' => 1},
+            explanation         => 'If Yes, Returndate on issuing can\'t be after borrower card expiry',
+           type                => 'YesNo',
         },
         {
-            uniquefieldrequired => 'bit',
-            bit                 => 5,
-            flag                => 'permissions',
-            flagdesc            => 'Set user permissions',
-            defaulton           => 0
+            uniquefieldrequired => 'variable',
+            variable            => 'opacstylesheet',
+            value               => '',
+           forceupdate         => { 'explanation' => 1,
+                                    'type' => 1},
+            explanation         => 'Enter a complete URL to use an alternate stylesheet in OPAC',
+           type                => 'free',
         },
         {
-            uniquefieldrequired => 'bit',
-            bit                 => 6,
-            flag                => 'reserveforothers',
-            flagdesc            => 'Reserve books for patrons',
-            defaulton           => 0
+            uniquefieldrequired => 'variable',
+            variable            => 'opacsmallimage',
+            value               => '',
+           forceupdate         => { 'explanation' => 1,
+                                    'type' => 1},
+            explanation         => 'Enter a complete URL to an image, will be on top/left instead of the Koha logo',
+           type                => 'free',
         },
         {
-            uniquefieldrequired => 'bit',
-            bit                 => 7,
-            flag                => 'borrow',
-            flagdesc            => 'Borrow books',
-            defaulton           => 1
+            uniquefieldrequired => 'variable',
+            variable            => 'opaclargeimage',
+            value               => '',
+           forceupdate         => { 'explanation' => 1,
+                                    'type' => 1},
+            explanation         => 'Enter a complete URL to an image, will be on the main page, instead of the Koha logo',
+           type                => 'free',
         },
         {
-            uniquefieldrequired => 'bit',
-            bit                 => 8,
-            flag                => 'reserveforself',
-            flagdesc            => 'Reserve books for self',
-            defaulton           => 0
+            uniquefieldrequired => 'variable',
+            variable            => 'delimiter',
+            value               => ';',
+           forceupdate         => { 'explanation' => 1,
+                                    'type' => 1},
+            explanation         => 'separator for reports exported to spreadsheet',
+           type                => 'free',
         },
         {
-            uniquefieldrequired => 'bit',
-            bit                 => 9,
-            flag                => 'editcatalogue',
-            flagdesc  => 'Edit Catalogue (Modify bibliographic/holdings data)',
-            defaulton => 0
+            uniquefieldrequired => 'variable',
+            variable            => 'MIME',
+            value               => 'OPENOFFICE.ORG',
+            forceupdate                => { 'explanation' => 1,
+                                    'type' => 1,
+                                    'options' => 1},
+            explanation         => 'Define the default application for report exportations into files',
+               type            => 'Choice',
+               options         => 'EXCEL|OPENOFFICE.ORG'
         },
         {
-            uniquefieldrequired => 'bit',
-            bit                 => 10,
-            flag                => 'updatecharges',
-            flagdesc            => 'Update borrower charges',
-            defaulton           => 0
+            uniquefieldrequired => 'variable',
+            variable            => 'Delimiter',
+            value               => ';',
+               forceupdate             => { 'explanation' => 1,
+                                    'type' => 1,
+                                    'options' => 1},
+            explanation         => 'Define the default separator character for report exportations into files',
+               type            => 'Choice',
+               options         => ';|tabulation|,|/|\|#'
         },
-    ],
-    systempreferences => [
         {
             uniquefieldrequired => 'variable',
-            variable            => 'autoMemberNum',
-            value               => '1',
-            explanation         => '1 or 0. If 1, Member number is auto-calculated'
+            variable            => 'SubscriptionHistory',
+            value               => ';',
+               forceupdate             => { 'explanation' => 1,
+                                    'type' => 1,
+                                    'options' => 1},
+            explanation         => 'Define the information level for serials history in OPAC',
+               type            => 'Choice',
+               options         => 'simplified|full'
         },
         {
             uniquefieldrequired => 'variable',
-            variable            => 'acquisitions',
-            value               => 'simple',
-            explanation         =>
-'normal or simple : whether to use "acqui" or "acqui.simple" acquisition system'
+            variable            => 'hidelostitems',
+            value               => 'No',
+           forceupdate         => { 'explanation' => 1,
+                                    'type' => 1},
+            explanation         => 'show or hide "lost" items in OPAC.',
+           type                => 'YesNo',
         },
-        {
+                {
             uniquefieldrequired => 'variable',
-            variable            => 'dateformat',
-            value               => 'metric',
-            explanation         => 'metric, us, or iso'
+            variable            => 'IndependantBranches',
+            value               => '0',
+            forceupdate         => { 'explanation' => 1,
+                                     'type' => 1},
+            explanation         => 'Turn Branch independancy management On an Off',
+            type                => 'YesNo',
         },
-        {
+                {
             uniquefieldrequired => 'variable',
-            variable            => 'template',
-            value               => 'default',
-            explanation         => 'template default name'
+            variable            => 'ReturnBeforeExpiry',
+            value               => '0',
+            forceupdate         => { 'explanation' => 1,
+                                     'type' => 1},
+            explanation         => 'If Yes, Returndate on issuing can\'t be after borrower card expiry',
+            type                => 'YesNo',
         },
         {
             uniquefieldrequired => 'variable',
-            variable            => 'autoBarcode',
-            value               => '1',
-            explanation         => '1 or 0. If 1, Barcode is auto-calculated'
+            variable            => 'Disable_Dictionary',
+            value               => '0',
+            forceupdate         => { 'explanation' => 1,
+                                     'type' => 1},
+            explanation         => 'Disables Dictionary buttons if set to yes',
+            type                => 'YesNo',
         },
         {
             uniquefieldrequired => 'variable',
-            variable            => 'insecure',
-            value               => 'NO',
-            explanation         =>
-'if YES, no auth at all is needed. Be careful if you set this to yes !'
+            variable            => 'hide_marc',
+            value               => '0',
+            forceupdate         => { 'explanation' => 1,
+                                     'type' => 1},
+            explanation         => 'hide marc specific datas like subfield code & indicators to library',
+            type                => 'YesNo',
         },
         {
             uniquefieldrequired => 'variable',
-            variable            => 'authoritysep',
-            value               => '--',
-            explanation         =>
-            'the separator used in authority/thesaurus. Usually --'
+            variable            => 'NotifyBorrowerDeparture',
+            value               => '0',
+            forceupdate         => { 'explanation' => 1,
+                                     'type' => 1},
+            explanation         => 'Delay before expiry where a notice is sent when issuing',
+            type                => 'Integer',
         },
         {
             uniquefieldrequired => 'variable',
-            variable            => 'opaclanguages',
-            value               => 'en',
-            explanation         => 'languages'
+            variable            => 'OpacPasswordChange',
+            value               => '1',
+            forceupdate         => { 'explanation' => 1,
+                                     'type' => 1},
+            explanation         => 'Enable/Disable password change in OPAC (disable it when using LDAP auth)',
+            type                => 'YesNo',
         },
         {
             uniquefieldrequired => 'variable',
-            variable            => 'opacthemes',
-            value               => 'default',
-            explanation         => 'theme'
+            variable            => 'useDaysMode',
+            value               => 'Calendar',
+            forceupdate         => { 'explanation' => 1,
+                                     'type' => 1},
+            explanation                        => 'How to calculate return dates : Calendar means holidays will be controled, Days means the return date don\'t depend on holidays',
+               type            => 'Choice',
+               options         => 'Calendar|Days'
         },
         {
             uniquefieldrequired => 'variable',
-            variable            => 'timeout',
-            value               => '12000000',
-            explanation         => 'login timeout'
+            variable            => 'borrowerMandatoryField',
+            value               => 'zipcode|surname',
+            forceupdate         => { 'explanation' => 1,
+                                     'type' => 1},
+            explanation         => 'List all mandatory fields for borrowers',
+            type                => 'free',
         },
         {
             uniquefieldrequired => 'variable',
-            variable            => 'marc',
-            value               => 'ON',
-            explanation         => 'MARC support (ON or OFF)'
+            variable            => 'borrowerRelationship',
+            value               => 'father|mother,grand-mother',
+            forceupdate         => { 'explanation' => 1,
+                                     'type' => 1},
+            explanation         => 'The relationships between a guarantor & a guarantee (separated by | or ,)',
+            type                => 'free',
         },
         {
             uniquefieldrequired => 'variable',
-            variable            => 'marcflavour',
-            value               => 'MARC21',
-            explanation         =>
-            'your MARC flavor (MARC21 or UNIMARC) used for character encoding'
+            variable            => 'ReservesMaxPickUpDelay',
+            value               => '10',
+            forceupdate         => { 'explanation' => 1,
+                                     'type' => 1},
+            explanation         => 'Maximum delay to pick up a reserved document',
+            type                => 'free',
         },
         {
             uniquefieldrequired => 'variable',
-            variable            => 'checkdigit',
-            value               => 'katipo',
-            explanation         =>
-            'none= no check on member cardnumber. katipo= katipo check'
+            variable            => 'TransfersMaxDaysWarning',
+            value               => '3',
+            forceupdate         => { 'explanation' => 1,
+                                     'type' => 1},
+            explanation         => 'Max delay before considering the transfer has potentialy a problem',
+            type                => 'free',
         },
         {
             uniquefieldrequired => 'variable',
-            variable            => 'dateformat',
-            value               => 'ISO',
-            explanation         =>
-            'date format (US mm/dd/yyyy, metric dd/mm/yyy, ISO yyyy/mm/dd) '
+            variable            => 'memberofinstitution',
+            value               => '0',
+            forceupdate         => { 'explanation' => 1,
+                                     'type' => 1},
+            explanation         => 'Are your patrons members of institutions',
+            type                => 'YesNo',
         },
-        {
+       {
             uniquefieldrequired => 'variable',
-            variable            => 'maxoutstanding',
-            value               => '5',
-            explanation         =>
-            'maximum amount withstanding to be able make reserves '
+            variable            => 'ReadingHistory',
+            value               => '0',
+            forceupdate         => { 'explanation' => 1,
+                                     'type' => 1},
+            explanation         => 'Allow reading record info retrievable from issues and oldissues tables',
+            type                => 'YesNo',
         },
-        {
+       {
             uniquefieldrequired => 'variable',
-            variable            => 'maxreserves',
-            value               => '5',
-            explanation         =>
-            'maximum number of reserves a member can make '
+            variable            => 'IssuingInProcess',
+            value               => '0',
+            forceupdate         => { 'explanation' => 1,
+                                     'type' => 1},
+            explanation         => 'Allow no debt alert if the patron is issuing item that accumulate debt',
+            type                => 'YesNo',
         },
-        {
+       {
             uniquefieldrequired => 'variable',
-            variable            => 'KohaAdminEmailAddress',
-            value               => 'your.mail@here',
-            explanation => 'the email adress where borrowers modifs are sent'
+            variable            => 'AutomaticItemReturn',
+            value               => '1',
+            forceupdate         => { 'explanation' => 1,
+                                     'type' => 1},
+            explanation         => 'This Variable allow or not to return automaticly to his homebranch',
+            type                => 'YesNo',
         },
     ],
 
 );
 
 my %fielddefinitions = (
-    printers => [
-        {
-            field   => 'printername',
-            type    => 'char(40)',
-            null    => '',
-            key     => 'PRI',
-            default => ''
-        },
-    ],
-    aqbookfund => [
+# fieldname => [
+#      {                 field => 'fieldname',
+#             type    => 'fieldtype',
+#             null    => '',
+#             key     => '',
+#             default => ''
+#         },
+#     ],
+       serial => [
         {
-            field   => 'bookfundid',
-            type    => 'char(5)',
-            null    => '',
-            key     => 'PRI',
-            default => ''
-        },
-    ],
-    z3950servers => [
-        {
-            field   => 'id',
-            type    => 'int',
-            null    => '',
-            key     => 'PRI',
+            field   => 'notes',
+            type    => 'TEXT',
+            null    => 'NULL',
+            key     => '',
             default => '',
-            extra   => 'auto_increment'
+            extra   => ''
         },
     ],
+       aqbasket =>  [
+               {
+                       field   => 'booksellerid',
+                       type    => 'int(11)',
+                       null    => 'NOT NULL',
+                       key             => '',
+                       default => '1',
+                       extra   => '',
+               },
+       ],
+       aqbooksellers =>  [
+               {
+                       field   => 'listprice',
+                       type    => 'varchar(10)',
+                       null    => 'NULL',
+                       key             => '',
+                       default => '',
+                       extra   => '',
+               },
+               {
+                       field   => 'invoiceprice',
+                       type    => 'varchar(10)',
+                       null    => 'NULL',
+                       key             => '',
+                       default => '',
+                       extra   => '',
+               },
+       ],
+       issues =>  [
+               {
+                       field   => 'borrowernumber',
+                       type    => 'int(11)',
+                       null    => 'NULL', # can be null when a borrower is deleted and the foreign key rule executed
+                       key             => '',
+                       default => '',
+                       extra   => '',
+               },
+               {
+                       field   => 'itemnumber',
+                       type    => 'int(11)',
+                       null    => 'NULL', # can be null when a borrower is deleted and the foreign key rule executed
+                       key             => '',
+                       default => '',
+                       extra   => '',
+               },
+       ],
+       borrowers => [
+               {       field => 'B_email',
+                       type => 'text',
+                       null => 'NULL',
+                       after => 'B_zipcode',
+                },
+                {
+                       field => 'streetnumber', # street number (hidden if streettable table is empty)
+                       type => 'char(10)',
+                       null => 'NULL',
+                       after => 'initials',
+               },
+               {
+                       field => 'streettype', # street table, list builded from a system table
+                       type => 'char(50)',
+                       null => 'NULL',
+                       after => 'streetnumber',
+               },
+                {
+                       field => 'B_streetnumber', # street number (hidden if streettable table is empty)
+                       type => 'char(10)',
+                       null => 'NULL',
+                       after => 'fax',
+               },
+               {
+                       field => 'B_streettype', # street table, list builded from a system table
+                       type => 'char(50)',
+                       null => 'NULL',
+                       after => 'B_streetnumber',
+               },
+               {
+                       field => 'phonepro',
+                       type => 'text',
+                       null => 'NULL',
+                       after => 'fax',
+               },
+               {
+                       field => 'address2', # complement address
+                       type => 'text',
+                       null => 'NULL',
+                       after => 'address',
+               },
+               {
+                       field => 'emailpro',
+                       type => 'text',
+                       null => 'NULL',
+                       after => 'fax',
+               },
+               {
+                       field => 'contactfirstname', # contact's firstname
+                       type => 'text',
+                       null => 'NULL',
+                       after => 'contactname',
+               },
+               {
+                       field => 'contacttitle', # contact's title
+                       type => 'text',
+                       null => 'NULL',
+                       after => 'contactfirstname',
+               },
+       ],
+       
+       branches =>  [
+               {
+                       field   => 'branchip',
+                       type    => 'varchar(15)',
+                       null    => 'NULL',
+                       key             => '',
+                       default => '',
+                       extra   => '',
+               },
+               {
+                       field   => 'branchprinter',
+                       type    => 'varchar(100)',
+                       null    => 'NULL',
+                       key             => '',
+                       default => '',
+                       extra   => '',
+               },
+       ],
+       categories =>  [
+               {
+                       field   => 'category_type',
+                       type    => 'char(1)',
+                       null    => 'NOT NULL',
+                       key             => '',
+                       default => 'A',
+                       extra   => '',
+               },
+       ],
+       reserves =>  [
+               {
+                       field   => 'waitingdate',
+                       type    => 'date',
+                       null    => 'NULL',
+                       key             => '',
+                       default => '',
+                       extra   => '',
+               },
+       ],
+);
+
+my %indexes = (
+#      table => [
+#              {       indexname => 'index detail'
+#              }
+#      ],
+       shelfcontents => [
+               {       indexname => 'shelfnumber',
+                       content => 'shelfnumber',
+               },
+               {       indexname => 'itemnumber',
+                       content => 'itemnumber',
+               }
+       ],
+       bibliosubject => [
+               {       indexname => 'biblionumber',
+                       content => 'biblionumber',
+               }
+       ],
+       items => [
+               {       indexname => 'homebranch',
+                       content => 'homebranch',
+               },
+               {       indexname => 'holdingbranch',
+                       content => 'holdingbranch',
+               }
+       ],
+       aqbooksellers => [
+               {       indexname => 'PRIMARY',
+                       content => 'id',
+                       type => 'PRIMARY',
+               }
+       ],
+       aqbasket => [
+               {       indexname => 'booksellerid',
+                       content => 'booksellerid',
+               },
+       ],
+       aqorders => [
+               {       indexname => 'basketno',
+                       content => 'basketno',
+               },
+       ],
+       aqorderbreakdown => [
+               {       indexname => 'ordernumber',
+                       content => 'ordernumber',
+               },
+               {       indexname => 'bookfundid',
+                       content => 'bookfundid',
+               },
+       ],
+       currency => [
+               {       indexname => 'PRIMARY',
+                       content => 'currency',
+                       type => 'PRIMARY',
+               }
+       ],
 );
 
+my %foreign_keys = (
+#      table => [
+#              {       key => 'the key in table' (must be indexed)
+#                      foreigntable => 'the foreigntable name', # (the parent)
+#                      foreignkey => 'the foreign key column(s)' # (in the parent)
+#                      onUpdate => 'CASCADE|SET NULL|NO ACTION| RESTRICT',
+#                      onDelete => 'CASCADE|SET NULL|NO ACTION| RESTRICT',
+#              }
+#      ],
+       shelfcontents => [
+               {       key => 'shelfnumber',
+                       foreigntable => 'bookshelf',
+                       foreignkey => 'shelfnumber',
+                       onUpdate => 'CASCADE',
+                       onDelete => 'CASCADE',
+               },
+               {       key => 'itemnumber',
+                       foreigntable => 'items',
+                       foreignkey => 'itemnumber',
+                       onUpdate => 'CASCADE',
+                       onDelete => 'CASCADE',
+               },
+       ],
+       # onDelete is RESTRICT on reference tables (branches, itemtype) as we don't want items to be 
+       # easily deleted, but branches/itemtype not too easy to empty...
+       biblioitems => [
+               {       key => 'biblionumber',
+                       foreigntable => 'biblio',
+                       foreignkey => 'biblionumber',
+                       onUpdate => 'CASCADE',
+                       onDelete => 'CASCADE',
+               },
+               {       key => 'itemtype',
+                       foreigntable => 'itemtypes',
+                       foreignkey => 'itemtype',
+                       onUpdate => 'CASCADE',
+                       onDelete => 'RESTRICT',
+               },
+       ],
+       items => [
+               {       key => 'biblioitemnumber',
+                       foreigntable => 'biblioitems',
+                       foreignkey => 'biblioitemnumber',
+                       onUpdate => 'CASCADE',
+                       onDelete => 'CASCADE',
+               },
+               {       key => 'homebranch',
+                       foreigntable => 'branches',
+                       foreignkey => 'branchcode',
+                       onUpdate => 'CASCADE',
+                       onDelete => 'RESTRICT',
+               },
+               {       key => 'holdingbranch',
+                       foreigntable => 'branches',
+                       foreignkey => 'branchcode',
+                       onUpdate => 'CASCADE',
+                       onDelete => 'RESTRICT',
+               },
+       ],
+       additionalauthors => [
+               {       key => 'biblionumber',
+                       foreigntable => 'biblio',
+                       foreignkey => 'biblionumber',
+                       onUpdate => 'CASCADE',
+                       onDelete => 'CASCADE',
+               },
+       ],
+       bibliosubject => [
+               {       key => 'biblionumber',
+                       foreigntable => 'biblio',
+                       foreignkey => 'biblionumber',
+                       onUpdate => 'CASCADE',
+                       onDelete => 'CASCADE',
+               },
+       ],
+       aqbasket => [
+               {       key => 'booksellerid',
+                       foreigntable => 'aqbooksellers',
+                       foreignkey => 'id',
+                       onUpdate => 'CASCADE',
+                       onDelete => 'RESTRICT',
+               },
+       ],
+       aqorders => [
+               {       key => 'basketno',
+                       foreigntable => 'aqbasket',
+                       foreignkey => 'basketno',
+                       onUpdate => 'CASCADE',
+                       onDelete => 'CASCADE',
+               },
+               {       key => 'biblionumber',
+                       foreigntable => 'biblio',
+                       foreignkey => 'biblionumber',
+                       onUpdate => 'SET NULL',
+                       onDelete => 'SET NULL',
+               },
+       ],
+       aqbooksellers => [
+               {       key => 'listprice',
+                       foreigntable => 'currency',
+                       foreignkey => 'currency',
+                       onUpdate => 'CASCADE',
+                       onDelete => 'CASCADE',
+               },
+               {       key => 'invoiceprice',
+                       foreigntable => 'currency',
+                       foreignkey => 'currency',
+                       onUpdate => 'CASCADE',
+                       onDelete => 'CASCADE',
+               },
+       ],
+       aqorderbreakdown => [
+               {       key => 'ordernumber',
+                       foreigntable => 'aqorders',
+                       foreignkey => 'ordernumber',
+                       onUpdate => 'CASCADE',
+                       onDelete => 'CASCADE',
+               },
+               {       key => 'bookfundid',
+                       foreigntable => 'aqbookfund',
+                       foreignkey => 'bookfundid',
+                       onUpdate => 'CASCADE',
+                       onDelete => 'CASCADE',
+               },
+       ],
+       branchtransfers => [
+               {       key => 'frombranch',
+                       foreigntable => 'branches',
+                       foreignkey => 'branchcode',
+                       onUpdate => 'CASCADE',
+                       onDelete => 'CASCADE',
+               },
+               {       key => 'tobranch',
+                       foreigntable => 'branches',
+                       foreignkey => 'branchcode',
+                       onUpdate => 'CASCADE',
+                       onDelete => 'CASCADE',
+               },
+               {       key => 'itemnumber',
+                       foreigntable => 'items',
+                       foreignkey => 'itemnumber',
+                       onUpdate => 'CASCADE',
+                       onDelete => 'CASCADE',
+               },
+       ],
+       issuingrules => [
+               {       key => 'categorycode',
+                       foreigntable => 'categories',
+                       foreignkey => 'categorycode',
+                       onUpdate => 'CASCADE',
+                       onDelete => 'CASCADE',
+               },
+               {       key => 'itemtype',
+                       foreigntable => 'itemtypes',
+                       foreignkey => 'itemtype',
+                       onUpdate => 'CASCADE',
+                       onDelete => 'CASCADE',
+               },
+       ],
+       issues => [     # constraint is SET NULL : when a borrower or an item is deleted, we keep the issuing record
+       # for stat purposes
+               {       key => 'borrowernumber',
+                       foreigntable => 'borrowers',
+                       foreignkey => 'borrowernumber',
+                       onUpdate => 'SET NULL',
+                       onDelete => 'SET NULL',
+               },
+               {       key => 'itemnumber',
+                       foreigntable => 'items',
+                       foreignkey => 'itemnumber',
+                       onUpdate => 'SET NULL',
+                       onDelete => 'SET NULL',
+               },
+       ],
+       reserves => [
+               {       key => 'borrowernumber',
+                       foreigntable => 'borrowers',
+                       foreignkey => 'borrowernumber',
+                       onUpdate => 'CASCADE',
+                       onDelete => 'CASCADE',
+               },
+               {       key => 'biblionumber',
+                       foreigntable => 'biblio',
+                       foreignkey => 'biblionumber',
+                       onUpdate => 'CASCADE',
+                       onDelete => 'CASCADE',
+               },
+               {       key => 'itemnumber',
+                       foreigntable => 'items',
+                       foreignkey => 'itemnumber',
+                       onUpdate => 'CASCADE',
+                       onDelete => 'CASCADE',
+               },
+               {       key => 'branchcode',
+                       foreigntable => 'branches',
+                       foreignkey => 'branchcode',
+                       onUpdate => 'CASCADE',
+                       onDelete => 'CASCADE',
+               },
+       ],
+       borrowers => [ # foreign keys are RESTRICT as we don't want to delete borrowers when a branch is deleted
+       # but prevent deleting a branch as soon as it has 1 borrower !
+               {       key => 'categorycode',
+                       foreigntable => 'categories',
+                       foreignkey => 'categorycode',
+                       onUpdate => 'RESTRICT',
+                       onDelete => 'RESTRICT',
+               },
+               {       key => 'branchcode',
+                       foreigntable => 'branches',
+                       foreignkey => 'branchcode',
+                       onUpdate => 'RESTRICT',
+                       onDelete => 'RESTRICT',
+               },
+       ],
+       accountlines => [
+               {       key => 'borrowernumber',
+                       foreigntable => 'borrowers',
+                       foreignkey => 'borrowernumber',
+                       onUpdate => 'CASCADE',
+                       onDelete => 'CASCADE',
+               },
+               {       key => 'itemnumber',
+                       foreigntable => 'items',
+                       foreignkey => 'itemnumber',
+                       onUpdate => 'SET NULL',
+                       onDelete => 'SET NULL',
+               },
+       ],
+       auth_tag_structure => [
+               {       key => 'authtypecode',
+                       foreigntable => 'auth_types',
+                       foreignkey => 'authtypecode',
+                       onUpdate => 'CASCADE',
+                       onDelete => 'CASCADE',
+               },
+       ],
+       # FIXME : don't constraint auth_*_table and auth_word, as they may be replaced by zebra
+);
+
+
+# column changes
+my %column_change = (
+       # table
+       borrowers => [
+                               {
+                                       from => 'emailaddress',
+                                       to => 'email',
+                                       after => 'city',
+                               },
+                               {
+                                       from => 'streetaddress',
+                                       to => 'address',
+                                       after => 'initials',
+                               },
+                               {
+                                       from => 'faxnumber',
+                                       to => 'fax',
+                                       after => 'phone',
+                               },
+                               {
+                                       from => 'textmessaging',
+                                       to => 'opacnote',
+                                       after => 'userid',
+                               },
+                               {
+                                       from => 'altnotes',
+                                       to => 'contactnote',
+                                       after => 'opacnote',
+                               },
+                               {
+                                       from => 'physstreet',
+                                       to => 'B_address',
+                                       after => 'fax',
+                               },
+                               {
+                                       from => 'streetcity',
+                                       to => 'B_city',
+                                       after => 'B_address',
+                               },
+                               {
+                                       from => 'phoneday',
+                                       to => 'mobile',
+                                       after => 'phone',
+                               },
+                               {
+                                       from => 'zipcode',
+                                       to => 'zipcode',
+                                       after => 'city',
+                               },
+                               {
+                                       from => 'homezipcode',
+                                       to => 'B_zipcode',
+                                       after => 'B_city',
+                               },
+                               {
+                                       from => 'altphone',
+                                       to => 'B_phone',
+                                       after => 'B_zipcode',
+                               },
+                               {
+                                       from => 'expiry',
+                                       to => 'dateexpiry',
+                                       after => 'dateenrolled',
+                               },
+                               {
+                                       from => 'guarantor',
+                                       to => 'guarantorid',
+                                       after => 'contactname',
+                               },
+                               {
+                                       from => 'textmessaging',
+                                       to => 'opacnotes',
+                                       after => 'flags',
+                               },
+                               {
+                                       from => 'altnotes',
+                                       to => 'contactnotes',
+                                       after => 'opacnotes',
+                               },
+                               {
+                                       from => 'altrelationship',
+                                       to => 'relationship',
+                                       after => 'borrowernotes',
+                               },
+                       ],
+               );
+               
+foreach my $table (keys %column_change) {
+       $sth = $dbh->prepare("show columns from $table");
+       $sth->execute();
+       undef %types;
+       while ( ( $column, $type, $null, $key, $default, $extra ) = $sth->fetchrow )
+       {
+               $types{$column}->{type} ="$type";
+               $types{$column}->{null} = "$null";
+               $types{$column}->{key} = "$key";
+               $types{$column}->{default} = "$default";
+               $types{$column}->{extra} = "$extra";
+       }    # while
+       my $tablerows = $column_change{$table};
+       foreach my $row ( @$tablerows ) {
+               if ($types{$row->{from}}->{type}) {
+                       print "altering $table $row->{from} to $row->{to}\n";
+                       # ALTER TABLE `borrowers` CHANGE `faxnumber` `fax` TEXT CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL 
+#                      alter table `borrowers` change `faxnumber` `fax` type text  null after phone
+                       my $sql = 
+                               "alter table `$table` change `$row->{from}` `$row->{to}` $types{$row->{from}}->{type} ".
+                               ($types{$row->{from}}->{null} eq 'YES'?" NULL":" NOT NULL").
+                               ($types{$row->{from}}->{default}?" default ".$types{$row->{from}}->{default}:"").
+                               "$types{$row->{from}}->{extra} after $row->{after} ";
+#                      print "$sql";
+                       $dbh->do($sql);
+               }
+       }
+}
+
 #-------------------
 # Initialize
 
@@ -498,7 +1050,7 @@ my $mysqlversion = `mysqld --version`;
 $mysqlversion =~ /Ver (\S*) /;
 $mysqlversion = $1;
 if ( $mysqlversion ge '3.23' ) {
-    print "Could convert to MyISAM database tables...\n";
+    print "Could convert to MyISAM database tables...\n" unless $silent;
 }
 
 #---------------------------------
@@ -511,11 +1063,11 @@ while ( my ($table) = $sth->fetchrow ) {
     $existingtables{$table} = 1;
 }
 
+
 # Now add any missing tables
 foreach $table ( keys %requiretables ) {
-    print "Checking $table table...\n" if $debug;
     unless ( $existingtables{$table} ) {
-        print "Adding $table table...\n";
+       print "Adding $table table...\n" unless $silent;
         my $sth = $dbh->prepare("create table $table $requiretables{$table}");
         $sth->execute;
         if ( $sth->err ) {
@@ -527,44 +1079,20 @@ foreach $table ( keys %requiretables ) {
 
 # now drop useless tables
 foreach $table ( keys %dropable_table ) {
-    print "Dropping unused tables...\n" if $debug;
-    if ( $existingtables{$table} ) {
-        $dbh->do("drop table $table");
-        if ( $dbh->err ) {
-            print "Error : $dbh->errstr \n";
-        }
-    }
-}
-unless ( $existingtables{'z3950servers'} ) {
-    print "Adding z3950servers table...\n";
-    my $sti = $dbh->prepare( "create table z3950servers (
-                                                                               host char(255),
-                                                                               port int,
-                                                                               db char(255),
-                                                                               userid char(255),
-                                                                               password char(255),
-                                                                               name text,
-                                                                               id int,
-                                                                               checked smallint,
-                                                                               rank int)"
-    );
-    $sti->execute;
-    $sti = $dbh->prepare( "insert into z3950servers
-                                                               values ('z3950.loc.gov',
-                                                               7090,
-                                                               'voyager',
-                                                               '', '',
-                                                               'Library of Congress',
-                                                               1, 1, 1)"
-    );
-    $sti->execute;
+       if ( $existingtables{$table} ) {
+               print "Dropping unused table $table\n" if $debug and not $silent;
+               $dbh->do("drop table $table");
+               if ( $dbh->err ) {
+                       print "Error : $dbh->errstr \n";
+               }
+       }
 }
 
 #---------------------------------
 # Columns
 
 foreach $table ( keys %requirefields ) {
-    print "Check table $table\n" if $debug;
+    print "Check table $table\n" if $debug and not $silent;
     $sth = $dbh->prepare("show columns from $table");
     $sth->execute();
     undef %types;
@@ -573,11 +1101,11 @@ foreach $table ( keys %requirefields ) {
         $types{$column} = $type;
     }    # while
     foreach $column ( keys %{ $requirefields{$table} } ) {
-        print "  Check column $column\n" if $debug;
+        print "  Check column $column  [$types{$column}]\n" if $debug and not $silent;
         if ( !$types{$column} ) {
 
             # column doesn't exist
-            print "Adding $column field to $table table...\n";
+            print "Adding $column field to $table table...\n" unless $silent;
             $query = "alter table $table
                        add column $column " . $requirefields{$table}->{$column};
             print "Execute: $query\n" if $debug;
@@ -592,337 +1120,727 @@ foreach $table ( keys %requirefields ) {
 }    # foreach table
 
 foreach $table ( keys %fielddefinitions ) {
-    print "Check table $table\n" if $debug;
-    $sth = $dbh->prepare("show columns from $table");
-    $sth->execute();
-    my $definitions;
-    while ( ( $column, $type, $null, $key, $default, $extra ) = $sth->fetchrow )
-    {
-        $definitions->{$column}->{type}    = $type;
-        $definitions->{$column}->{null}    = $null;
-        $definitions->{$column}->{key}     = $key;
-        $definitions->{$column}->{default} = $default;
-        $definitions->{$column}->{extra}   = $extra;
-    }    # while
-    my $fieldrow = $fielddefinitions{$table};
-    foreach my $row (@$fieldrow) {
-        my $field   = $row->{field};
-        my $type    = $row->{type};
-        my $null    = $row->{null};
-        my $key     = $row->{key};
-        my $default = $row->{default};
-        my $extra   = $row->{extra};
-        my $def     = $definitions->{$field};
-        unless ( $type eq $def->{type}
-            && $null eq $def->{null}
-            && $key eq $def->{key}
-            && $default eq $def->{default}
-            && $extra eq $def->{extra} )
-        {
+       print "Check table $table\n" if $debug;
+       $sth = $dbh->prepare("show columns from $table");
+       $sth->execute();
+       my $definitions;
+       while ( ( $column, $type, $null, $key, $default, $extra ) = $sth->fetchrow )
+       {
+               $definitions->{$column}->{type}    = $type;
+               $definitions->{$column}->{null}    = $null;
+               $definitions->{$column}->{null}    = 'NULL' if $null eq 'YES';
+               $definitions->{$column}->{key}     = $key;
+               $definitions->{$column}->{default} = $default;
+               $definitions->{$column}->{extra}   = $extra;
+       }    # while
+       my $fieldrow = $fielddefinitions{$table};
+       foreach my $row (@$fieldrow) {
+               my $field   = $row->{field};
+               my $type    = $row->{type};
+               my $null    = $row->{null};
+#              $null    = 'YES' if $row->{null} eq 'NULL';
+               my $key     = $row->{key};
+               my $default = $row->{default};
+               my $null    = $row->{null};
+#              $default="''" unless $default;
+               my $extra   = $row->{extra};
+               my $def     = $definitions->{$field};
+               my $after       = ($row->{after}?" after ".$row->{after}:"");
 
-            if ( $null eq '' ) {
-                $null = 'NOT NULL';
-            }
-            if ( $key eq 'PRI' ) {
-                $key = 'PRIMARY KEY';
-            }
-            unless ( $extra eq 'auto_increment' ) {
-                $extra = '';
-            }
-            my $sth =
-              $dbh->prepare(
-"alter table $table change $field $field $type $null $key $extra default ?"
-            );
-            $sth->execute($default);
-            print "  Alter $field in $table\n";
-        }
-    }
+               unless ( $type eq $def->{type}
+                       && $null eq $def->{null}
+                       && $key eq $def->{key}
+                       && $extra eq $def->{extra} )
+               {
+                       if ( $null eq '' ) {
+                               $null = 'NOT NULL';
+                       }
+                       if ( $key eq 'PRI' ) {
+                               $key = 'PRIMARY KEY';
+                       }
+                       unless ( $extra eq 'auto_increment' ) {
+                               $extra = '';
+                       }
+
+                       # if it's a new column use "add", if it's an old one, use "change".
+                       my $action;
+                       if ($definitions->{$field}->{type}) {
+                               $action="change $field"
+                       } else {
+                               $action="add";
+                       }
+# if it's a primary key, drop the previous pk, before altering the table
+                       my $sth;
+                       if ($key ne 'PRIMARY KEY') {
+                               $sth =$dbh->prepare("alter table $table $action $field $type $null $key $extra default ? $after");
+                       } else {
+                               $sth =$dbh->prepare("alter table $table drop primary key, $action $field $type $null $key $extra default ? $after");
+                       }
+                       $sth->execute($default);
+                       print "  alter or create $field in $table\n" unless $silent;
+               }
+       }
 }
 
-# Get list of columns from items table
-my %itemtypes;
+# Populate tables with required data
 
-$sth = $dbh->prepare("show columns from items");
-$sth->execute;
-while ( my ( $column, $type, $null, $key, $default, $extra ) = $sth->fetchrow )
-{
-    $itemtypes{$column} = $type;
+
+# synch table and deletedtable.
+foreach my $table (('borrowers','items','biblio','biblioitems')) {
+       my %deletedborrowers;
+       print "synch'ing $table\n";
+       $sth = $dbh->prepare("show columns from deleted$table");
+       $sth->execute;
+       while ( my ( $column, $type, $null, $key, $default, $extra ) = $sth->fetchrow ) {
+               $deletedborrowers{$column}=1;
+       }
+       $sth = $dbh->prepare("show columns from $table");
+       $sth->execute;
+       my $previous;
+       while ( my ( $column, $type, $null, $key, $default, $extra ) = $sth->fetchrow ) {
+               unless ($deletedborrowers{$column}) {
+                       my $newcol="alter table deleted$table add $column $type";
+                       if ($null eq 'YES') {
+                               $newcol .= " NULL ";
+                       } else {
+                               $newcol .= " NOT NULL ";
+                       }
+                       $newcol .= "default $default" if $default;
+                       $newcol .= " after $previous" if $previous;
+                       $previous=$column;
+                       print "creating column $column\n";
+                       $dbh->do($newcol);
+               }
+       }
 }
 
-unless ( $itemtypes{'barcode'} eq 'varchar(20)' ) {
-    $itemtypes{'barcode'} =~ /varchar\((\d+)\)/;
-    my $oldlength = $1;
-    if ( $oldlength < 20 ) {
-        print "Setting maximum barcode length to 20 (was $oldlength).\n";
-        my $sti =
+foreach my $table ( keys %tabledata ) {
+    print "Checking for data required in table $table...\n" unless $silent;
+    my $tablerows = $tabledata{$table};
+    foreach my $row (@$tablerows) {
+        my $uniquefieldrequired = $row->{uniquefieldrequired};
+        my $uniquevalue         = $row->{$uniquefieldrequired};
+        my $forceupdate         = $row->{forceupdate};
+        my $sth                 =
           $dbh->prepare(
-            "alter table items change barcode barcode varchar(20) not null");
-        $sti->execute;
-    }
+"select $uniquefieldrequired from $table where $uniquefieldrequired=?"
+        );
+        $sth->execute($uniquevalue);
+               if ($sth->rows) {
+                       foreach my $field (keys %$forceupdate) {
+                               if ($forceupdate->{$field}) {
+                                       my $sth=$dbh->prepare("update systempreferences set $field=? where $uniquefieldrequired=?");
+                                       $sth->execute($row->{$field}, $uniquevalue);
+                               }
+               }
+               } else {
+                       print "Adding row to $table: " unless $silent;
+                       my @values;
+                       my $fieldlist;
+                       my $placeholders;
+                       foreach my $field ( keys %$row ) {
+                               next if $field eq 'uniquefieldrequired';
+                               next if $field eq 'forceupdate';
+                               my $value = $row->{$field};
+                               push @values, $value;
+                               print "  $field => $value" unless $silent;
+                               $fieldlist .= "$field,";
+                               $placeholders .= "?,";
+                       }
+                       print "\n" unless $silent;
+                       $fieldlist    =~ s/,$//;
+                       $placeholders =~ s/,$//;
+                       my $sth =
+                       $dbh->prepare(
+                               "insert into $table ($fieldlist) values ($placeholders)");
+                       $sth->execute(@values);
+               }
+       }
 }
 
-# extending the timestamp in branchtransfers...
-my %branchtransfers;
+#
+# check indexes and create them when needed
+#
+print "Checking for index required...\n" unless $silent;
+foreach my $table ( keys %indexes ) {
+       #
+       # read all indexes from $table
+       #
+       $sth = $dbh->prepare("show index from $table");
+       $sth->execute;
+       my %existingindexes;
+       while ( my ( $table, $non_unique, $key_name, $Seq_in_index, $Column_name, $Collation, $cardinality, $sub_part, $Packed, $comment ) = $sth->fetchrow ) {
+               $existingindexes{$key_name} = 1;
+       }
+       # read indexes to check
+       my $tablerows = $indexes{$table};
+       foreach my $row (@$tablerows) {
+               my $key_name=$row->{indexname};
+               if ($existingindexes{$key_name} eq 1) {
+#                      print "$key_name existing";
+               } else {
+                       print "\tCreating index $key_name in $table\n";
+                       my $sql;
+                       if ($row->{indexname} eq 'PRIMARY') {
+                               $sql = "alter table $table ADD PRIMARY KEY ($row->{content})";
+                       } else {
+                               $sql = "alter table $table ADD INDEX $key_name ($row->{content}) $row->{type}";
+                       }
+                       $dbh->do($sql);
+            print "Error $sql : $dbh->err \n" if $dbh->err;
+               }
+       }
+}
 
-$sth = $dbh->prepare("show columns from branchtransfers");
-$sth->execute;
-while ( my ( $column, $type, $null, $key, $default, $extra ) = $sth->fetchrow )
-{
-    $branchtransfers{$column} = $type;
+#
+# check foreign keys and create them when needed
+#
+print "Checking for foreign keys required...\n" unless $silent;
+foreach my $table ( keys %foreign_keys ) {
+       #
+       # read all indexes from $table
+       #
+       $sth = $dbh->prepare("show table status like '$table'");
+       $sth->execute;
+       my $stat = $sth->fetchrow_hashref;
+       # read indexes to check
+       my $tablerows = $foreign_keys{$table};
+       foreach my $row (@$tablerows) {
+               my $foreign_table=$row->{foreigntable};
+               if ($stat->{'Comment'} =~/$foreign_table/) {
+#                      print "$foreign_table existing\n";
+               } else {
+                       print "\tCreating foreign key $foreign_table in $table\n";
+                       # first, drop any orphan value in child table
+                       if ($row->{onDelete} ne "RESTRICT") {
+                               my $sql = "delete from $table where $row->{key} not in (select $row->{foreignkey} from $row->{foreigntable})";
+                               $dbh->do($sql);
+                               print "SQL ERROR: $sql : $dbh->err \n" if $dbh->err;
+                       }
+                       my $sql="alter table $table ADD FOREIGN KEY $row->{key} ($row->{key}) REFERENCES $row->{foreigntable} ($row->{foreignkey})";
+                       $sql .= " on update ".$row->{onUpdate} if $row->{onUpdate};
+                       $sql .= " on delete ".$row->{onDelete} if $row->{onDelete};
+                       $dbh->do($sql);
+                       if ($dbh->err) {
+                               print "====================
+An error occured during :
+\t$sql
+It probably means there is something wrong in your DB : a row ($table.$row->{key}) refers to a value in $row->{foreigntable}.$row->{foreignkey} that does not exist. solve the problem and run updater again (or just the previous SQL statement).
+You can find those values with select
+\t$table.* from $table where $row->{key} not in (select $row->{foreignkey} from $row->{foreigntable})
+====================\n
+";
+                       }
+               }
+       }
 }
 
-unless ( $branchtransfers{'datesent'} eq 'datetime' ) {
-    print "Setting type of datesent in branchtransfers to datetime.\n";
-    my $sti =
-      $dbh->prepare(
-        "alter table branchtransfers change datesent datesent datetime");
-    $sti->execute;
+#
+# SPECIFIC STUFF
+#
+#
+# create frameworkcode row in biblio table & fill it with marc_biblio.frameworkcode.
+#
+
+# 1st, get how many biblio we will have to do...
+$sth = $dbh->prepare('select count(*) from marc_biblio');
+$sth->execute;
+my ($totaltodo) = $sth->fetchrow;
+
+$sth = $dbh->prepare("show columns from biblio");
+$sth->execute();
+my $definitions;
+my $bibliofwexist=0;
+while ( ( $column, $type, $null, $key, $default, $extra ) = $sth->fetchrow ){
+       $bibliofwexist=1 if $column eq 'frameworkcode';
+}
+unless ($bibliofwexist) {
+       print "moving biblioframework to biblio table\n";
+       $dbh->do('ALTER TABLE `biblio` ADD `frameworkcode` VARCHAR( 4 ) NOT NULL AFTER `biblionumber`');
+       $sth = $dbh->prepare('select biblionumber,frameworkcode from marc_biblio');
+       $sth->execute;
+       my $sth_update = $dbh->prepare('update biblio set frameworkcode=? where biblionumber=?');
+       my $totaldone=0;
+       while (my ($biblionumber,$frameworkcode) = $sth->fetchrow) {
+               $sth_update->execute($frameworkcode,$biblionumber);
+               $totaldone++;
+               print "\r$totaldone / $totaltodo" unless ($totaldone % 100);
+       }
+       print "\rdone\n";
 }
 
-unless ( $branchtransfers{'datearrived'} eq 'datetime' ) {
-    print "Setting type of datearrived in branchtransfers to datetime.\n";
-    my $sti =
-      $dbh->prepare(
-        "alter table branchtransfers change datearrived datearrived datetime");
-    $sti->execute;
+#
+# moving MARC data from marc_subfield_table to biblioitems.marc
+#
+$sth = $dbh->prepare("show columns from biblioitems");
+$sth->execute();
+my $definitions;
+my $marcdone=0;
+while ( ( $column, $type, $null, $key, $default, $extra ) = $sth->fetchrow ){
+       $marcdone=1 if ($type eq 'blob' && $column eq 'marc') ;
 }
+unless ($marcdone) {
+       print "moving MARC record to biblioitems table\n";
+       # changing marc field type
+       $dbh->do('ALTER TABLE `biblioitems` CHANGE `marc` `marc` BLOB NULL DEFAULT NULL ');
+       # adding marc xml, just for convenience
+       $dbh->do('ALTER TABLE `biblioitems` ADD `marcxml` TEXT CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL ');
+       # moving data from marc_subfield_value to biblio
+       $sth = $dbh->prepare('select bibid,biblionumber from marc_biblio');
+       $sth->execute;
+       my $sth_update = $dbh->prepare('update biblioitems set marc=?, marcxml=? where biblionumber=?');
+       my $totaldone=0;
+       while (my ($bibid,$biblionumber) = $sth->fetchrow) {
+               my $record = MARCgetbiblio($dbh,$bibid);
+       #Force UTF-8 in record leader
+               $record->encoding('UTF-8');
+               print $record->as_formatted if ($biblionumber==3902);
+               $sth_update->execute($record->as_usmarc(),$record->as_xml_record(),$biblionumber);
+               $totaldone++;
+               print "\r$totaldone / $totaltodo" unless ($totaldone % 100);
+       }
+       print "\rdone\n";
+}
+
+
+# at last, remove useless fields
+foreach $table ( keys %uselessfields ) {
+       my @fields = split /,/,$uselessfields{$table};
+       my $fields;
+       my $exists;
+       foreach my $fieldtodrop (@fields) {
+               $fieldtodrop =~ s/\t//g;
+               $fieldtodrop =~ s/\n//g;
+               $exists =0;
+               $sth = $dbh->prepare("show columns from $table");
+               $sth->execute;
+               while ( my ( $column, $type, $null, $key, $default, $extra ) = $sth->fetchrow )
+               {
+                       $exists =1 if ($column eq $fieldtodrop);
+               }
+               if ($exists) {
+                       print "deleting $fieldtodrop field in $table...\n" unless $silent;
+                       my $sth = $dbh->prepare("alter table $table drop $fieldtodrop");
+                       $sth->execute;
+               }
+       }
+}    # foreach
 
-# changing the branchcategories table around...
-my %branchcategories;
 
-$sth = $dbh->prepare("show columns from branchcategories");
+# MOVE all tables TO UTF-8 and innoDB
+$sth = $dbh->prepare("show table status");
 $sth->execute;
-while ( my ( $column, $type, $null, $key, $default, $extra ) = $sth->fetchrow )
-{
-    $branchcategories{$column} = $type;
+while ( my $table = $sth->fetchrow_hashref ) {
+#      if ($table->{Engine} ne 'InnoDB') {
+#              $dbh->do("ALTER TABLE $table->{Name} TYPE = innodb");
+#              print "moving $table->{Name} to InnoDB\n";
+#      }
+       unless ($table->{Collation} =~ /^utf8/) {
+               $dbh->do("ALTER TABLE $table->{Name} CONVERT TO CHARACTER SET utf8");
+               $dbh->do("ALTER TABLE $table->{Name} DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci");
+               # FIXME : maybe a ALTER TABLE tbl_name CONVERT TO CHARACTER SET utf8 would be better, def char set seems to work fine. If any problem encountered, let's try with convert !
+               print "moving $table->{Name} to utf8\n";
+       } else {
+       }
 }
 
-unless ( $branchcategories{'categorycode'} eq 'varchar(4)' ) {
-    print
-"Setting type of categorycode in branchcategories to varchar(4),\n and making the primary key.\n";
-    my $sti =
+$sth->finish;
+
+#
+# those 2 subs are a copy of Biblio.pm, version 2.2.4
+# they are useful only once, for moving from 2.2 to 3.0
+# the MARCgetbiblio & MARCgetitem subs in Biblio.pm
+# are still here, but uses other tables
+# (the ones that are filled by updatedatabase !)
+#
+
+sub MARCgetbiblio {
+
+    # Returns MARC::Record of the biblio passed in parameter.
+    my ( $dbh, $bibid ) = @_;
+    my $record = MARC::Record->new();
+#      warn "". $bidid;
+
+    my $sth =
       $dbh->prepare(
-"alter table branchcategories change categorycode categorycode varchar(4) not null"
+"select bibid,subfieldid,tag,tagorder,tag_indicator,subfieldcode,subfieldorder,subfieldvalue,valuebloblink
+                                from marc_subfield_table
+                                where bibid=? order by tag,tagorder,subfieldorder
+                        "
     );
-    $sti->execute;
-    $sti =
+    my $sth2 =
       $dbh->prepare(
-        "alter table branchcategories add primary key (categorycode)");
-    $sti->execute;
+        "select subfieldvalue from marc_blob_subfield where blobidlink=?");
+    $sth->execute($bibid);
+    my $prevtagorder = 1;
+    my $prevtag      = 'XXX';
+    my $previndicator;
+    my $field;        # for >=10 tags
+    my $prevvalue;    # for <10 tags
+    while ( my $row = $sth->fetchrow_hashref ) {
+
+        if ( $row->{'valuebloblink'} ) {    #---- search blob if there is one
+            $sth2->execute( $row->{'valuebloblink'} );
+            my $row2 = $sth2->fetchrow_hashref;
+            $sth2->finish;
+            $row->{'subfieldvalue'} = $row2->{'subfieldvalue'};
+        }
+        if ( $row->{tagorder} ne $prevtagorder || $row->{tag} ne $prevtag ) {
+            $previndicator .= "  ";
+            if ( $prevtag < 10 ) {
+                               if ($prevtag ne '000') {
+                       $record->add_fields( ( sprintf "%03s", $prevtag ), $prevvalue ) unless $prevtag eq "XXX";    # ignore the 1st loop
+                               } else {
+                                       $record->leader(sprintf("%24s",$prevvalue));
+                               }
+            }
+            else {
+                $record->add_fields($field) unless $prevtag eq "XXX";
+            }
+            undef $field;
+            $prevtagorder  = $row->{tagorder};
+            $prevtag       = $row->{tag};
+            $previndicator = $row->{tag_indicator};
+            if ( $row->{tag} < 10 ) {
+                $prevvalue = $row->{subfieldvalue};
+            }
+            else {
+                $field = MARC::Field->new(
+                    ( sprintf "%03s", $prevtag ),
+                    substr( $row->{tag_indicator} . '  ', 0, 1 ),
+                    substr( $row->{tag_indicator} . '  ', 1, 1 ),
+                    $row->{'subfieldcode'},
+                    $row->{'subfieldvalue'}
+                );
+            }
+        }
+        else {
+            if ( $row->{tag} < 10 ) {
+                $record->add_fields( ( sprintf "%03s", $row->{tag} ),
+                    $row->{'subfieldvalue'} );
+            }
+            else {
+                $field->add_subfields( $row->{'subfieldcode'},
+                    $row->{'subfieldvalue'} );
+            }
+            $prevtag       = $row->{tag};
+            $previndicator = $row->{tag_indicator};
+        }
+    }
+
+    # the last has not been included inside the loop... do it now !
+    if ( $prevtag ne "XXX" )
+    { # check that we have found something. Otherwise, prevtag is still XXX and we
+         # must return an empty record, not make MARC::Record fail because we try to
+         # create a record with XXX as field :-(
+        if ( $prevtag < 10 ) {
+            $record->add_fields( $prevtag, $prevvalue );
+        }
+        else {
+
+            #                  my $field = MARC::Field->new( $prevtag, "", "", %subfieldlist);
+            $record->add_fields($field);
+        }
+    }
+    return $record;
 }
 
-unless ( $branchcategories{'categoryname'} eq 'text' ) {
-    print "Changing branchcode in branchcategories to categoryname text.\n";
-    my $sth =
+sub MARCgetitem {
+
+    # Returns MARC::Record of the biblio passed in parameter.
+    my ( $dbh, $bibid, $itemnumber ) = @_;
+    my $record = MARC::Record->new();
+
+    # search MARC tagorder
+    my $sth2 =
       $dbh->prepare(
-        "alter table branchcategories change branchcode categoryname text");
-    $sth->execute;
-}
+"select tagorder from marc_subfield_table,marc_subfield_structure where marc_subfield_table.tag=marc_subfield_structure.tagfield and marc_subfield_table.subfieldcode=marc_subfield_structure.tagsubfield and bibid=? and kohafield='items.itemnumber' and subfieldvalue=?"
+    );
+    $sth2->execute( $bibid, $itemnumber );
+    my ($tagorder) = $sth2->fetchrow_array();
 
-unless ( $branchcategories{'codedescription'} eq 'text' ) {
-    print
-"Replacing branchholding in branchcategories with codedescription text.\n";
+    #---- TODO : the leader is missing
     my $sth =
       $dbh->prepare(
-        "alter table branchcategories change branchholding codedescription text"
+"select bibid,subfieldid,tag,tagorder,tag_indicator,subfieldcode,subfieldorder,subfieldvalue,valuebloblink
+                                from marc_subfield_table
+                                where bibid=? and tagorder=? order by subfieldcode,subfieldorder
+                        "
     );
-    $sth->execute;
-}
-
-# Populate tables with required data
+    $sth2 =
+      $dbh->prepare(
+        "select subfieldvalue from marc_blob_subfield where blobidlink=?");
+    $sth->execute( $bibid, $tagorder );
+    while ( my $row = $sth->fetchrow_hashref ) {
+        if ( $row->{'valuebloblink'} ) {    #---- search blob if there is one
+            $sth2->execute( $row->{'valuebloblink'} );
+            my $row2 = $sth2->fetchrow_hashref;
+            $sth2->finish;
+            $row->{'subfieldvalue'} = $row2->{'subfieldvalue'};
+        }
+        if ( $record->field( $row->{'tag'} ) ) {
+            my $field;
 
-foreach my $table ( keys %tabledata ) {
-    print "Checking for data required in table $table...\n";
-    my $tablerows = $tabledata{$table};
-    foreach my $row (@$tablerows) {
-        my $uniquefieldrequired = $row->{uniquefieldrequired};
-        my $uniquevalue         = $row->{$uniquefieldrequired};
-        my $sth                 =
-          $dbh->prepare(
-"select $uniquefieldrequired from $table where $uniquefieldrequired=?"
-        );
-        $sth->execute($uniquevalue);
-        unless ( $sth->rows ) {
-            print "Adding row to $table: ";
-            my @values;
-            my $fieldlist;
-            my $placeholders;
-            foreach my $field ( keys %$row ) {
-                (next) if ( $field eq 'uniquefieldrequired' );
-                my $value = $row->{$field};
-                push @values, $value;
-                print "  $field => $value";
-                $fieldlist .= "$field,";
-                $placeholders .= "?,";
+#--- this test must stay as this, because of strange behaviour of mySQL/Perl DBI with char var containing a number...
+            #--- sometimes, eliminates 0 at beginning, sometimes no ;-\\\
+            if ( length( $row->{'tag'} ) < 3 ) {
+                $row->{'tag'} = "0" . $row->{'tag'};
             }
-            print "\n";
-            $fieldlist    =~ s/,$//;
-            $placeholders =~ s/,$//;
-            my $sth =
-              $dbh->prepare(
-                "insert into $table ($fieldlist) values ($placeholders)");
-            $sth->execute(@values);
+            $field = $record->field( $row->{'tag'} );
+            if ($field) {
+                my $x =
+                  $field->add_subfields( $row->{'subfieldcode'},
+                    $row->{'subfieldvalue'} );
+                $record->delete_field($field);
+                $record->add_fields($field);
+            }
+        }
+        else {
+            if ( length( $row->{'tag'} ) < 3 ) {
+                $row->{'tag'} = "0" . $row->{'tag'};
+            }
+            my $temp =
+              MARC::Field->new( $row->{'tag'}, " ", " ",
+                $row->{'subfieldcode'} => $row->{'subfieldvalue'} );
+            $record->add_fields($temp);
         }
+
     }
+    return $record;
 }
 
-$sth->finish;
 
 exit;
 
 # $Log$
-# Revision 1.45  2003/05/08 12:26:16  wolfpac444
-# Bug fixes
+# Revision 1.144  2006/06/08 15:36:31  alaurin
+# Add a new system preference 'AutomaticItemReturn' :
+#
+# if this prefence is switched on: the document returned in another library than homebranch, the system automaticly transfer the document to his homebranch (with notification for librarian in returns.tmpl) .
+#
+# switch off : the document stay in the holdingbranch ...
+#
+# correcting bugs :
+# - comment C4::acquisition (not using in request.pl).
+# - correcting date in request.pl
+# -add the new call of function getbranches in request.pl
+#
+# Revision 1.143  2006/06/07 02:02:47  bob_lyon
+# merging katipo changes...
+#
+# adding new preference IssuingInProcess
+#
+# Revision 1.142  2006/06/06 23:42:46  bob_lyon
+# Merging Katipo changes...
+#
+# Adding new system pref where one can still retrieve a correct reading
+# record history if one has moved older data from issues to oldissues table
+# to speed up issues speed
+#
+# Revision 1.141  2006/06/01 03:18:11  rangi
+# Adding a new column to the statistics table
+#
+# Revision 1.140  2006/05/22 22:40:45  rangi
+# Adding new systempreference allowing for the library to add borrowers to institutions (rest homes, parishes, schools, classes etc).
+#
+# Revision 1.139  2006/05/19 19:31:29  tgarip1957
+# Added new fields to auth_header and auth_subfield_table to allow ZEBRA use of authorities and new MARC framework like structure.
+# Authority tables are modified to be compatible with new MARC frameworks. This change is part of Authority Linking & Zebra authorities. Requires change in Mysql database. It will break head unless all changes regarding this is implemented. This warning will take place on all commits regarding this
+#
+# Revision 1.138  2006/05/19 16:51:44  alaurin
+# update database for :
+# - new feature ip and printer management
+# adding two fields in branches table (branchip,branchprinter)
+#
+# - waiting date : adding one field in reserves table(waiting date) to calculate the Maximum delay to pick up a reserved document when it's available
+#
+# new system preference :
+# - ReservesMaxPickUpDelay : Maximum delay to pick up a reserved document
+# TransfersMaxDaysWarning : Max delay before considering the transfer as potentialy a problem
+#
+# Revision 1.137  2006/04/18 09:36:36  plg
+# bug fixed: typo fixed in labels and labels_conf tables creation query.
+#
+# Revision 1.136  2006/04/17 21:55:33  sushi
+# Added 'labels' and 'labels_conf' tables, for spine lable tool.
+#
+# Revision 1.135  2006/04/15 02:37:03  tgarip1957
+# Marc record should be set to UTF-8 in leader.Force it.
+# XML should be with<record> wrappers
+#
+# Revision 1.134  2006/04/14 09:37:29  tipaul
+# improvements from SAN Ouest Provence :
+# * introducing a category_type into categories. It can be A (adult), C (children), P (Professionnal), I (institution/organisation).
+# * each category_type has it's own forms to create members.
+# * the borrowers table has been heavily modified (many fields changed), to get something more logic & readable
+# * reintroducing guarantor/guanrantee system that is now independant from hardcoded C/A for categories
+# * updating templates to fit template rules
+#
+# (see mail feb, 17 on koha-devel "new features for borrowers" for more details)
+#
+# Revision 1.133  2006/04/13 08:36:42  plg
+# new: function C4::Date::get_date_format_string_for_DHTMLcalendar based on
+# the system preference prefered date format.
+#
+# improvement: book fund list and budget list screen redesigned. Filters on
+# each field. Columns are not sortable yet. Using DHTML Calendar to fill date
+# fields instead of manual filling. Pagination system. From the book fund
+# list, you can reach the budget list, filtered on a book fund, or not. A
+# budget can be added only from book fund list screen.
+#
+# bug fixed: branchcode was missing in table aqbudget.
 #
-# Revision 1.44  2003/05/03 05:39:57  rangi
-# Fixing bug 429
-# (Wording changes in the explanation fields in system preferences)
+# bug fixed: when setting a branchcode to a book fund, all associated budgets
+# move to this branchcode.
 #
-# Revision 1.43  2003/05/02 23:01:09  rangi
-# Adding the textmessaging column to the borrowers table.
-# insertdata.pl is expecting this to exist, and hence modifying/adding
-# borrowers was broken.
+# modification: when adding/modifying budget/fund, MySQL specific "REPLACE..."
+# statements replaced by standard SQL compliant statement.
 #
-# Also ran they script thru perltidy
+# bug fixed: when adding/modifying a budget, if the book fund is associated to
+# a branch, the branch selection is disabled and set to the book fund branch.
 #
-# Revision 1.42  2003/04/29 16:53:25  tipaul
-# really proud of this commit :-)
-# z3950 search and import seems to works fine.
-# Let me explain how :
-# * a "search z3950" button is added in the addbiblio template.
-# * when clicked, a popup appears and z3950/search.pl is called
-# * z3950/search.pl calls addz3950search in the DB
-# * the z3950 daemon retrieve the records and stores them in z3950results AND in marc_breeding table.
-# * as long as there as searches pending, the popup auto refresh every 2 seconds, and says how many searches are pending.
-# * when the user clicks on a z3950 result => the parent popup is called with the requested biblio, and auto-filled
+# Revision 1.132  2006/04/06 12:37:05  hdl
+# Bugfixing : aqbookfund needed a field.
 #
-# Note :
-# * 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.
-# * 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.
+# Revision 1.131  2006/03/03 17:02:22  tipaul
+# commit for holidays and news management.
+# (some forgotten files)
 #
-# Revision 1.41  2003/04/29 08:09:44  tipaul
-# z3950 support is coming...
-# * 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.
-# Note this is a 1st draft. More to follow (today ? I hope).
+# Revision 1.130  2006/03/03 16:35:21  tipaul
+# commit for holidays and news management.
 #
-# Revision 1.40  2003/04/22 10:48:27  wolfpac444
-# Added "father" column to bibliothesaurus table
+# Contrib from Tümer Garip (from Turkey) :
+# * holiday :
+# in /tools/ the holiday.pl script let you define holidays (days where the library is closed), branch by branch. You can define 3 types of holidays :
+# - single day : only this day is closed
+# - repet weekly (like "sunday") : the day is holiday every week
+# - repet yearly (like "July, 4") : this day is closed every year.
 #
-# Revision 1.39  2003/04/04 08:45:00  tipaul
-# last commits before 1.9.1
+# You can also put exception :
+# - sunday is holiday, but "2006 March, 5th" the library will be open
 #
-# Revision 1.38  2003/03/18 10:58:19  tipaul
-# adding checkdigit parameter that choose how to check the members cardnumber.
-# At the moment :
-# * none = no checking
-# * katipo = checked as before
+# The holidays are used for return date calculation : the return date is set to the next date where the library is open. A systempreference (useDaysMode) set ON (Calendar) or OFF (Normal) the calendar calculation.
 #
-# Revision 1.37  2003/01/30 01:47:48  acli
-# Corrected syntax error reported by Benedict
+# Revision 1.129  2006/02/27 18:19:33  hdl
+# New table used in overduerules.pl tools page.
 #
-# Made the indentation somewhat easier to read; the messiness probably caused
-# the original syntax error.
+# Revision 1.128  2006/01/25 15:16:06  tipaul
+# updating DB :
+# * removing useless tables
+# * adding useful indexes
+# * altering some columns definitions
+# * The goal being to have updater working fine for foreign keys.
 #
-# Revision 1.36  2003/01/28 15:13:30  tipaul
-# userflag table now created in upgrade script (bugfix #171)
+# For me it's done, let me know if it works for you. You can see an updated schema of the DB (with constraints) on the wiki
 #
-# Revision 1.35  2003/01/27 03:12:49  acli
-# Reworded the description for "acquisitions" to make it fit on the screen
+# Revision 1.127  2006/01/24 17:57:17  tipaul
+# DB improvements : adding foreign keys on some tables. partial stuff done.
 #
-# Added "iso" to dateformat, since dateformat is not yet being used anyway
+# Revision 1.126  2006/01/06 16:39:42  tipaul
+# synch'ing head and rel_2_2 (from 2.2.5, including npl templates)
+# Seems not to break too many things, but i'm probably wrong here.
+# at least, new features/bugfixes from 2.2.5 are here (tested on some features on my head local copy)
 #
-# Revision 1.34  2003/01/23 12:30:02  tipaul
-# introducint marcflavour in systempref file : used for character decoding
+# - removing useless directories (koha-html and koha-plucene)
 #
-# Revision 1.33  2003/01/21 09:03:27  tipaul
-# bugfix (NOTE : this bugs makes installation of the 1.3.3 a little fuzzy. Please fix your DB if you installed 1.3.3)
+# Revision 1.125  2006/01/04 15:54:55  tipaul
+# utf8 is a : go for beta test in HEAD.
+# some explanations :
+# - updater/updatedatabase => will transform all tables in innoDB (not related to utf8, just to warn you) AND collate them in utf8 / utf8_general_ci. The SQL command is : ALTER TABLE tablename DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci.
+# - *-top.inc will show the pages in utf8
+# - THE HARD THING : for me, mysql-client and mysql-server were set up to communicate in iso8859-1, whatever the mysql collation ! Thus, pages were improperly shown, as datas were transmitted in iso8859-1 format ! After a full day of investigation, someone on usenet pointed "set NAMES 'utf8'" to explain that I wanted utf8. I could put this in my.cnf, but if I do that, ALL databases will "speak" in utf8, that's not what we want. Thus, I added a line in Context.pm : everytime a DB handle is opened, the communication is set to utf8.
+# - using marcxml field and no more the iso2709 raw marc biblioitems.marc field.
 #
-# Revision 1.32  2003/01/16 10:29:45  tipaul
-# adding a MARC parameter in systempref ( which is ON or OFF)
-# the search will be a marc search if MARC=ON
-# and a standard (v1.2) search if MARC=OFF
+# Revision 1.124  2005/10/27 12:09:05  tipaul
+# new features for serial module :
+# - the last 5 issues are now shown, and their status can be changed (but not reverted to "waited", as there can be only one "waited")
+# - the library can create a "distribution list". this paper contains a list of borrowers (selected from the borrower list, or manually entered), and print it for a given issue. once printed, the sheet can be put on the issue and distributed to every reader on the list (one by one).
 #
-# Revision 1.31  2003/01/06 13:32:43  tipaul
-# *** empty log message ***
+# Revision 1.123  2005/10/26 09:13:37  tipaul
+# big commit, still breaking things...
 #
-# Revision 1.29  2003/01/06 11:14:11  tipaul
-# last bugfixes before 1.3.3 : systempref table correctly filled
+# * synch with rel_2_2. Probably the last non manual synch, as rel_2_2 should not be modified deeply.
+# * code cleaning (cleaning warnings from perl -w) continued
 #
-# Revision 1.28  2002/12/10 13:27:47  tipaul
-# bugfixes (davide mails in koha-dev)
+# Revision 1.122  2005/09/02 14:18:38  tipaul
+# new feature : image for itemtypes.
 #
-# Revision 1.27  2002/11/26 15:04:54  tipaul
-# road to 1.3.2. Updating db structure during installation
+# * run updater/updatedatabase to create imageurl field in itemtypes.
+# * go to Koha >> parameters >> itemtypes >> modify (or add) an itemtype. You will see around 20 nice images to choose between (thanks to owen). If you prefer your own image, you also can type a complete url (http://www.myserver.lib/path/to/my/image.gif)
+# * go to OPAC, and search something. In the result list, you now have the picture instead of the text itemtype.
 #
-# Revision 1.26  2002/11/12 17:42:40  tonnesen
-# Merged some features over from rel-1-2, including primary key checking.
+# Revision 1.121  2005/08/24 08:49:03  hdl
+# Adding a note field in serial table.
+# This will allow librarian to mention a note on a peculiar waiting serial number.
 #
-# Revision 1.25  2002/11/12 16:44:38  tipaul
-# road to 1.3.2 :
-# * many bugfixes
-# * 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)
+# Revision 1.120  2005/08/09 14:10:32  tipaul
+# 1st commit to go to zebra.
+# don't update your cvs if you want to have a working head...
 #
-# Revision 1.24  2002/10/30 14:00:23  arensb
-# (bug fix): Fixed typo.
+# this commit contains :
+# * updater/updatedatabase : get rid with marc_* tables, but DON'T remove them. As a lot of things uses them, it would not be a good idea for instance to drop them. If you really want to play, you can rename them to test head without them but being still able to reintroduce them...
+# * Biblio.pm : modify MARCgetbiblio to find the raw marc record in biblioitems.marc field, not from marc_subfield_table, modify MARCfindframeworkcode to find frameworkcode in biblio.frameworkcode, modify some other subs to use biblio.biblionumber & get rid of bibid.
+# * other files : get rid of bibid and use biblionumber instead.
 #
-# Revision 1.23  2002/10/25 10:55:46  tipaul
-# Road to 1.3.2
-# * bugfixes and improvements
-# * manage mandatory MARC subfields
-# * 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.
-# this submit contains everything needed :
-# * updatedatabase
-# * admin screens
-# * "links" management
-# * creation of a html-list if a subfield is mapped to an authorised value.
+# What is broken :
+# * does not do anything on zebra yet.
+# * if you rename marc_subfield_table, you can't search anymore.
+# * you can view a biblio & bibliodetails, go to MARC editor, but NOT save any modif.
+# * don't try to add a biblio, it would add data poorly... (don't try to delete either, it may work, but that would be a surprise ;-) )
 #
-# Note this is different from authorities support, which will come soon.
-# 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...
+# IMPORTANT NOTE : you need MARC::XML package (http://search.cpan.org/~esummers/MARC-XML-0.7/lib/MARC/File/XML.pm), that requires a recent version of MARC::Record
+# Updatedatabase stores the iso2709 data in biblioitems.marc field & an xml version in biblioitems.marcxml Not sure we will keep it when releasing the stable version, but I think it's a good idea to have something readable in sql, at least for development stage.
 #
-# Revision 1.22  2002/10/15 10:08:19  tipaul
-# fixme corrected, re-indent and adding the marc_breeding table (see commit of marcimport.pl for more explanations about breeding)
+# Revision 1.119  2005/08/04 16:07:58  tipaul
+# Synch really broke this script...
 #
-# Revision 1.21  2002/10/14 11:48:59  tipaul
-# bugfix
+# Revision 1.118  2005/08/04 16:02:55  tipaul
+# oops... error in synch between 2.2 and head
 #
-# Revision 1.20  2002/10/10 04:49:41  arensb
-# Added some FIXME comments.
+# Revision 1.117  2005/08/04 14:24:39  tipaul
+# synch'ing 2.2 and head
 #
-# Revision 1.19  2002/10/05 10:17:17  arensb
-# Merged with arensb-context branch: use C4::Context->dbh instead of
-# &C4Connect, and generally prefer C4::Context over C4::Database.
+# Revision 1.116  2005/08/04 08:55:54  tipaul
+# Letters / alert system, continuing...
 #
-# Revision 1.18.2.2  2002/10/05 06:18:43  arensb
-# Added a whole mess of FIXME comments.
+# * adding a package Letters.pm, that manages Letters & alerts.
+# * adding feature : it's now possible to define a "letter" for any subscription created. If a letter is defined, users in OPAC can put an alert on the subscription. When an issue is marked "arrived", all users in the alert will recieve a mail (as defined in the "letter"). This last part (= send the mail) is not yet developped. (Should be done this week)
+# * adding feature : it's now possible to "put to an alert" in OPAC, for any serial subscription. The alert is stored in a new table, called alert. An alert can be put only if the librarian has activated them in subscription (and they activate it just by choosing a "letter" to sent to borrowers on new issues)
+# * adding feature : librarian can see in borrower detail which alerts they have put, and a user can see in opac-detail which alert they have put too.
 #
-# Revision 1.18.2.1  2002/10/04 02:46:00  arensb
-# Use C4::Connect instead of C4::Database, C4::Connect->dbh instead
-# C4Connect.
+# Note that the system should be generic enough to manage any type of alert.
+# I plan to extend it soon to virtual shelves : a borrower will be able to put an alert on a virtual shelf, to be warned when something is changed in the virtual shelf (mail being sent once a day by cron, or manually by the shelf owner. Anyway, a mail won't be sent on every change, users would be spammed by Koha ;-) )
 #
-# Revision 1.18  2002/09/24 13:50:55  tipaul
-# long WAS the road to 1.3.0...
-# coming VERY SOON NOW...
-# modifying installer and buildrelease to update the DB
+# Revision 1.115  2005/08/02 16:15:34  tipaul
+# adding 2 fields to letter system :
+# * module (acquisition, catalogue...) : it will be usefull to show the librarian only letters he may be interested by.
+# * title, that will be used as mail subject.
 #
-# Revision 1.17  2002/09/24 12:57:35  tipaul
-# long WAS the road to 1.3.0...
-# coming VERY SOON NOW...
-# modifying installer and buildrelease to update the DB
+# Revision 1.114  2005/07/28 15:10:13  tipaul
+# Introducing new "Letters" system : Letters will be used everytime you want to sent something to someone (through mail or paper). For example, sending a mail for overdues use letter that you can put as parameters. Sending a mail to a borrower when a suggestion is validated uses a letter too.
+# the letter table contains 3 fields :
+# * code => the code of the letter
+# * name => the complete name of the letter
+# * content => the complete text. It's a TEXT field type, so has no limits.
 #
-# Revision 1.16  2002/07/31 02:34:27  finlayt
+# My next goal now is to work on point 2-I "serial issue alert"
+# With this feature, in serials, a user can subscribe the "issue alert". For every issue arrived/missing, a mail is sent to all subscribers of this list. The mail warns the user that the issue is arrive or missing. Will be in head.
+# (see mail on koha-devel, 2005/04/07)
 #
-# added "notforloan" field to the itemtypes table.
+# The "serial issue alert" will be the 1st to use this letter system that probably needs some tweaking ;-)
 #
-# Revision 1.15  2002/07/20 22:30:06  rangi
-# Making sure fix makes it into the main branch as well
-# Fix for bug 69
+# Once it will be stabilised default letters (in any languages) could be added during installer to help the library begin with this new feature.
 #
-# Revision 1.14  2002/07/08 16:20:26  tonnesen
-# Added sessionqueries table and password/userid fields to borrowers table
+# Revision 1.113  2005/07/28 08:38:41  tipaul
+# For instance, the return date does not rely on the borrower expiration date. A systempref will be added in Koha, to modify return date calculation schema :
+# * ReturnBeforeExpiry = yes => return date can't be after expiry date
+# * ReturnBeforeExpiry = no  => return date can be after expiry date
 #
-# Revision 1.13  2002/07/04 18:05:36  tonnesen
-# bug fix
+# Revision 1.112  2005/07/26 08:19:47  hdl
+# Adding IndependantBranches System preference variable in order to manage Branch independancy.
 #
-# Revision 1.12  2002/07/04 16:41:06  tonnesen
-# Merged changes from rel-1-2.  Abstracted table structure changes by alan.
+# Revision 1.111  2005/07/25 15:35:38  tipaul
+# we have decided that moving to Koha 3.0 requires being already in Koha 2.2.x
+# So, the updatedatabase script can highly be cleaned (90% removed).
+# Let's play with the new Koha DB structure now ;-)
 #