From 3e85c9e97f1dc46af7e69e4da80e647356f8210e Mon Sep 17 00:00:00 2001 From: tipaul Date: Wed, 2 May 2007 16:44:31 +0000 Subject: [PATCH] NoZebra SQL index management : * adding 3 subs in Biblio.pm - GetNoZebraIndexes, that get the index structure in a new systempreference (added with this commit) - _DelBiblioNoZebra, that retrieve all index entries for a biblio and remove in a variable the biblio reference - _AddBiblioNoZebra, that add index entries for a biblio. Note that the 2 _Add and _Del subs work only in a hash variable, to speed up things in case of a modif (ie : delete+add). The effective SQL update is done in the ModZebra sub (that existed before, and dealed with zebra index). I think the code has to be more deeply tested, but it works at least partially. --- C4/Biblio.pm | 347 ++++++++++++++++++------ C4/Search.pm | 5 - misc/migration_tools/rebuild_nozebra.pl | 47 ++-- updater/updatedatabase | 17 ++ 4 files changed, 293 insertions(+), 123 deletions(-) diff --git a/C4/Biblio.pm b/C4/Biblio.pm index 1c777bac64..9be88a7d0a 100644 --- a/C4/Biblio.pm +++ b/C4/Biblio.pm @@ -113,6 +113,7 @@ push @EXPORT, qw( &TransformHtmlToXml &PrepareItemrecordDisplay &char_decode + &GetNoZebraIndexes ); =head1 NAME @@ -607,7 +608,11 @@ sub DelBiblio { } return $error if $error; - # Delete in Zebra + # Delete in Zebra. Be careful NOT to move this line after _koha_delete_biblio + # for at least 2 reasons : + # - we need to read the biblio if NoZebra is set (to remove it from the indexes + # - if something goes wrong, the biblio may be deleted from Koha but not from zebra + # and we would have no way to remove it (except manually in zebra, but I bet it would be very hard to handle the problem) ModZebra($biblionumber,"delete_record","biblioserver"); # delete biblio from Koha tables and save in deletedbiblio @@ -2705,97 +2710,254 @@ sub ModZebra { # replaced by a zebraqueue table, that is filled with ModZebra to run. # the table is emptied by misc/cronjobs/zebraqueue_start.pl script -my $sth=$dbh->prepare("insert into zebraqueue (biblio_auth_number ,server,operation) values(?,?,?)"); -$sth->execute($biblionumber,$server,$op); -$sth->finish; + if (C4::Context->preference("NoZebra")) { + # lock the nozebra table : we will read index lines, update them in Perl process + # and write everything in 1 transaction. + # lock the table to avoid someone else overwriting what we are doing + $dbh->do('LOCK TABLES nozebra WRITE,biblio WRITE,biblioitems WRITE, systempreferences WRITE'); + my %result; # the result hash that will be builded by deletion / add, and written on mySQL at the end, to improve speed + my $record= GetMarcBiblio($biblionumber); + if ($op eq 'specialUpdate') { + # OK, we have to add or update the record + # 1st delete (virtually, in indexes) ... + %result = _DelBiblioNoZebra($biblionumber,$record); + # ... add the record + %result=_AddBiblioNoZebra($biblionumber,$record, %result); + } else { + # it's a deletion, delete the record... + %result=_DelBiblioNoZebra($biblionumber,$record); + } + # ok, now update the database... + my $sth = $dbh->prepare("UPDATE nozebra SET biblionumbers=? WHERE indexname=? AND value=?"); + foreach my $key (keys %result) { + foreach my $index (keys %{$result{$key}}) { + $sth->execute($result{$key}->{$index},$key,$index); + } + } + $dbh->do('UNLOCK TABLES'); -# -# my @Zconnbiblio; -# my $tried = 0; -# my $recon = 0; -# my $reconnect = 0; -# my $record; -# my $shadow; -# -# reconnect: -# $Zconnbiblio[0] = C4::Context->Zconn( $server, 0, 1 ); -# -# if ( $server eq "biblioserver" ) { -# -# # it's unclear to me whether this should be in xml or MARC format -# # but it is clear it should be nabbed from zebra rather than from -# # the koha tables -# $record = GetMarcBiblio($biblionumber); -# $record = $record->as_xml_record() if $record; -# # warn "RECORD $biblionumber => ".$record; -# $shadow="biblioservershadow"; -# -# # warn "RECORD $biblionumber => ".$record; -# $shadow = "biblioservershadow"; -# -# } -# elsif ( $server eq "authorityserver" ) { -# $record = C4::AuthoritiesMarc::XMLgetauthority( $dbh, $biblionumber ); -# $shadow = "authorityservershadow"; -# } ## Add other servers as necessary -# -# my $Zpackage = $Zconnbiblio[0]->package(); -# $Zpackage->option( action => $op ); -# $Zpackage->option( record => $record ); -# -# retry: -# $Zpackage->send("update"); -# my $i; -# my $event; -# -# while ( ( $i = ZOOM::event( \@Zconnbiblio ) ) != 0 ) { -# $event = $Zconnbiblio[0]->last_event(); -# last if $event == ZOOM::Event::ZEND; -# } -# -# my ( $error, $errmsg, $addinfo, $diagset ) = $Zconnbiblio[0]->error_x(); -# if ( $error == 10000 && $reconnect == 0 ) -# { ## This is serious ZEBRA server is not available -reconnect -# warn "problem with zebra server connection"; -# $reconnect = 1; -# my $res = system('sc start "Z39.50 Server" >c:/zebraserver/error.log'); -# -# #warn "Trying to restart ZEBRA Server"; -# #goto "reconnect"; -# } -# elsif ( $error == 10007 && $tried < 2 ) -# { ## timeout --another 30 looonng seconds for this update -# $tried = $tried + 1; -# warn "warn: timeout, trying again"; -# goto "retry"; -# } -# elsif ( $error == 10004 && $recon == 0 ) { ##Lost connection -reconnect -# $recon = 1; -# warn "error: reconnecting to zebra"; -# goto "reconnect"; -# -# # as a last resort, we save the data to the filesystem to be indexed in batch -# } -# elsif ($error) { -# warn -# "Error-$server $op $biblionumber /errcode:, $error, /MSG:,$errmsg,$addinfo \n"; -# $Zpackage->destroy(); -# $Zconnbiblio[0]->destroy(); -# ModZebrafiles( $dbh, $biblionumber, $record, $op, $server ); -# return; -# } -# if ( C4::Context->$shadow ) { -# $Zpackage->send('commit'); -# while ( ( $i = ZOOM::event( \@Zconnbiblio ) ) != 0 ) { -# -# #waiting zebra to finish; -# } -# } -# $Zpackage->destroy(); + } else { + # + # we use zebra, just fill zebraqueue table + # + my $sth=$dbh->prepare("insert into zebraqueue (biblio_auth_number ,server,operation) values(?,?,?)"); + $sth->execute($biblionumber,$server,$op); + $sth->finish; + } +} + +=head2 GetNoZebraIndexes + +=cut + +sub GetNoZebraIndexes { + my $index = C4::Context->preference('NoZebraIndexes'); + my %indexes; + foreach my $line (split /('|"),/,$index) { + $line =~ /(.*)=>(.*)/; + my $index = substr($1,1); # get the index, don't forget to remove initial ' or " + my $fields = $2; + $index =~ s/'|"| //g; + $fields =~ s/'|"| //g; + $indexes{$index}=$fields; + } + return %indexes; } =head1 INTERNAL FUNCTIONS +=head2 _DelBiblioNoZebra($biblionumber,$record); + + function to delete a biblio in NoZebra indexes + This function does NOT delete anything in database : it reads all the indexes entries + that have to be deleted & delete them in the hash + The SQL part is done either : + - after the Add if we are modifying a biblio (delete + add again) + - immediatly after this sub if we are doing a true deletion. + +=cut + + +sub _DelBiblioNoZebra { + my ($biblionumber,$record)=@_; + # Get the indexes + my $dbh = C4::Context->dbh; + # Get the indexes + my %index=GetNoZebraIndexes; + # get title of the record (to store the 10 first letters with the index) + my ($titletag,$titlesubfield) = GetMarcFromKohaField('biblio.title'); + my $title = lc($record->subfield($titletag,$titlesubfield)); + + my %result; + # remove blancks comma (that could cause problem when decoding the string for CQL retrieval) and regexp specific values + $title =~ s/ |,|;|\[|\]|\(|\)|\*|-|'|=//g; + # limit to 10 char, should be enough, and limit the DB size + $title = substr($title,0,10); + #parse each field + my $sth2=$dbh->prepare('SELECT biblionumbers FROM nozebra WHERE indexname=? AND value=?'); + foreach my $field ($record->fields()) { + #parse each subfield + next if $field->tag <10; + foreach my $subfield ($field->subfields()) { + my $tag = $field->tag(); + my $subfieldcode = $subfield->[0]; + my $indexed=0; + # check each index to see if the subfield is stored somewhere + # otherwise, store it in __RAW__ index + foreach my $key (keys %index) { +# warn "examining $key index : ".$index{$key}." for $tag $subfieldcode"; + if ($index{$key} =~ /$tag\*/ or $index{$key} =~ /$tag$subfieldcode/) { + $indexed=1; + my $line= lc $subfield->[1]; + # remove meaningless value in the field... + $line =~ s/-|\.|\?|,|;|!|'|\(|\)|\[|\]|{|}|"|<|>|&|\+|\*|\/|=/ /g; + # ... and split in words + foreach (split / /,$line) { + next unless $_; # skip empty values (multiple spaces) + # if the entry is already here, do nothing, the biblionumber has already be removed + unless ($result{$key}->{$_} =~ /$biblionumber,$title\-(\d);/) { + # get the index value if it exist in the nozebra table and remove the entry, otherwise, do nothing + $sth2->execute($key,$_); + my $existing_biblionumbers = $sth2->fetchrow; + # it exists + if ($existing_biblionumbers) { + warn " existing for $key $_: $existing_biblionumbers"; + $result{$key}->{$_} =$existing_biblionumbers; + $result{$key}->{$_} =~ s/$biblionumber,$title\-(\d);//; + } + } + } + } + } + # the subfield is not indexed, store it in __RAW__ index anyway + unless ($indexed) { + my $line= lc $subfield->[1]; + $line =~ s/-|\.|\?|,|;|!|'|\(|\)|\[|\]|{|}|"|<|>|&|\+|\*|\/|=/ /g; + # ... and split in words + foreach (split / /,$line) { + next unless $_; # skip empty values (multiple spaces) + # if the entry is already here, do nothing, the biblionumber has already be removed + unless ($result{'__RAW__'}->{$_} =~ /$biblionumber,$title\-(\d);/) { + # get the index value if it exist in the nozebra table and remove the entry, otherwise, do nothing + $sth2->execute('__RAW__',$_); + my $existing_biblionumbers = $sth2->fetchrow; + # it exists + if ($existing_biblionumbers) { + warn " existing for __RAW__ $_ : $existing_biblionumbers"; + $result{'__RAW__'}->{$_} =$existing_biblionumbers; + $result{'__RAW__'}->{$_} =~ s/$biblionumber,$title\-(\d);//; + } + } + } + } + } + } + return %result; +} + +=head2 _DelBiblioNoZebra($biblionumber,$record); + + function to delete a biblio in NoZebra indexes + +=cut + + +sub _AddBiblioNoZebra { + my ($biblionumber,$record,%result)=@_; + my $dbh = C4::Context->dbh; + # Get the indexes + my %index=GetNoZebraIndexes; + # get title of the record (to store the 10 first letters with the index) + my ($titletag,$titlesubfield) = GetMarcFromKohaField('biblio.title'); + my $title = lc($record->subfield($titletag,$titlesubfield)); + + # remove blancks comma (that could cause problem when decoding the string for CQL retrieval) and regexp specific values + $title =~ s/ |,|;|\[|\]|\(|\)|\*|-|'|=//g; + # limit to 10 char, should be enough, and limit the DB size + $title = substr($title,0,10); + #parse each field + my $sth2=$dbh->prepare('SELECT biblionumbers FROM nozebra WHERE indexname=? AND value=?'); + foreach my $field ($record->fields()) { + #parse each subfield + next if $field->tag <10; + foreach my $subfield ($field->subfields()) { + my $tag = $field->tag(); + my $subfieldcode = $subfield->[0]; + my $indexed=0; + # check each index to see if the subfield is stored somewhere + # otherwise, store it in __RAW__ index + foreach my $key (keys %index) { +# warn "examining $key index : ".$index{$key}." for $tag $subfieldcode"; + if ($index{$key} =~ /$tag\*/ or $index{$key} =~ /$tag$subfieldcode/) { + $indexed=1; + my $line= lc $subfield->[1]; + # remove meaningless value in the field... + $line =~ s/-|\.|\?|,|;|!|'|\(|\)|\[|\]|{|}|"|<|>|&|\+|\*|\/|=/ /g; + # ... and split in words + foreach (split / /,$line) { + next unless $_; # skip empty values (multiple spaces) + # if the entry is already here, improve weight +# warn "managing $_"; + if ($result{$key}->{$_} =~ /$biblionumber,$title\-(\d);/) { + my $weight=$1+1; + $result{$key}->{$_} =~ s/$biblionumber,$title\-(\d);//; + $result{$key}->{$_} .= "$biblionumber,$title-$weight;"; + } else { + # get the value if it exist in the nozebra table, otherwise, create it + $sth2->execute($key,$_); + my $existing_biblionumbers = $sth2->fetchrow; + # it exists + if ($existing_biblionumbers) { + warn" existing : $existing_biblionumbers"; + $result{$key}->{$_} =$existing_biblionumbers; + my $weight=$1+1; + $result{$key}->{$_} =~ s/$biblionumber,$title\-(\d);//; + $result{$key}->{$_} .= "$biblionumber,$title-$weight;"; + # create a new ligne for this entry + } else { + $dbh->do('INSERT INTO nozebra SET indexname='.$dbh->quote($key).',value='.$dbh->quote($_)); + $result{$key}->{$_}.="$biblionumber,$title-1;"; + } + } + } + } + } + # the subfield is not indexed, store it in __RAW__ index anyway + unless ($indexed) { + my $line= lc $subfield->[1]; + $line =~ s/-|\.|\?|,|;|!|'|\(|\)|\[|\]|{|}|"|<|>|&|\+|\*|\/|=/ /g; + # ... and split in words + foreach (split / /,$line) { + next unless $_; # skip empty values (multiple spaces) + # if the entry is already here, improve weight + if ($result{'__RAW__'}->{$_} =~ /$biblionumber,$title\-(\d);/) { + my $weight=$1+1; + $result{'__RAW__'}->{$_} =~ s/$biblionumber,$title\-(\d);//; + $result{'__RAW__'}->{$_} .= "$biblionumber,$title-$weight;"; + } else { + # get the value if it exist in the nozebra table, otherwise, create it + $sth2->execute('__RAW__',$_); + my $existing_biblionumbers = $sth2->fetchrow; + # it exists + if ($existing_biblionumbers) { + $result{'__RAW__'}->{$_} =$existing_biblionumbers; + my $weight=$1+1; + $result{'__RAW__'}->{$_} =~ s/$biblionumber,$title\-(\d);//; + $result{'__RAW__'}->{$_} .= "$biblionumber,$title-$weight;"; + # create a new ligne for this entry + } else { + $dbh->do('INSERT INTO nozebra SET indexname="__RAW__",value='.$dbh->quote($_)); + $result{'__RAW__'}->{$_}.="$biblionumber,$title-1;"; + } + } + } + } + } + } + return %result; +} + + =head2 MARCitemchange =over 4 @@ -3505,7 +3667,7 @@ sub ModBiblioMarc { # deal with UNIMARC field 100 (encoding) : create it if needed & set encoding to unicode if ( $encoding eq "UNIMARC" ) { my $string; - if ( $record->subfield( 100, "a" ) ) { + if ( length($record->subfield( 100, "a" )) == 35 ) { $string = $record->subfield( 100, "a" ); my $f100 = $record->field(100); $record->delete_field($f100); @@ -3691,6 +3853,15 @@ Joshua Ferraro jmf@liblime.com # $Id$ # $Log$ +# Revision 1.202 2007/05/02 16:44:31 tipaul +# NoZebra SQL index management : +# * adding 3 subs in Biblio.pm +# - GetNoZebraIndexes, that get the index structure in a new systempreference (added with this commit) +# - _DelBiblioNoZebra, that retrieve all index entries for a biblio and remove in a variable the biblio reference +# - _AddBiblioNoZebra, that add index entries for a biblio. +# Note that the 2 _Add and _Del subs work only in a hash variable, to speed up things in case of a modif (ie : delete+add). The effective SQL update is done in the ModZebra sub (that existed before, and dealed with zebra index). +# I think the code has to be more deeply tested, but it works at least partially. +# # Revision 1.201 2007/04/27 14:00:49 hdl # Removing $dbh from GetMarcFromKohaField (dbh is not used in this function.) # diff --git a/C4/Search.pm b/C4/Search.pm index 66d2257f38..e8fe6e4068 100755 --- a/C4/Search.pm +++ b/C4/Search.pm @@ -1454,26 +1454,21 @@ sub NZorder { $title =~ /(.*)-(\d)/; # get weight my $ranking =$2; - # hint : the result is sorted by title.biblionumber because we can have X biblios with the same title - # and we don't want to get only 1 result for each of them !!! # note that we + the ranking because ranking is calculated on weight of EACH term requested. # if we ask for "two towers", and "two" has weight 2 in biblio N, and "towers" has weight 4 in biblio N # biblio N has ranking = 6 - $count_ranking{$biblionumber}=0 unless $count_ranking{$biblionumber}; $count_ranking{$biblionumber} =+ $ranking; } # build the result by "inverting" the count_ranking hash # hing : as usual, we don't order by ranking only, to avoid having only 1 result for each rank. We build an hash on concat(ranking,biblionumber) instead # warn "counting"; foreach (keys %count_ranking) { - warn "$_ =".sprintf("%10d",$count_ranking{$_}).'-'.$_; $result{sprintf("%10d",$count_ranking{$_}).'-'.$_} = $_; } # sort the hash and return the same structure as GetRecords (Zebra querying) my $result_hash; my $numbers=0; foreach my $key (sort {$b <=> $a} (keys %result)) { - warn "KEY : $key = ".$result{$key}; $result_hash->{'RECORDS'}[$numbers++] = $result{$key}; } # for the requested page, replace biblionumber by the complete record diff --git a/misc/migration_tools/rebuild_nozebra.pl b/misc/migration_tools/rebuild_nozebra.pl index 7e88ef4f0f..bf34b6d65b 100755 --- a/misc/migration_tools/rebuild_nozebra.pl +++ b/misc/migration_tools/rebuild_nozebra.pl @@ -14,7 +14,7 @@ use strict; $|=1; # flushes output # limit for database dumping -my $limit;# = "LIMIT 1000"; +my $limit = "LIMIT 100"; my $directory; my $skip_export; my $keep_export; @@ -47,22 +47,7 @@ $sth->execute(); my $i=0; my %result; -my %index = ( - 'title' => '200a,200c,200d,200e,225a,225d,225e,225f,225h,225i,225v,500*,501*,503*,510*,512*,513*,514*,515*,516*,517*,518*,519*,520*,530*,531*,532*,540*,541*,545*,604t,610t,605a', - 'author' =>'200f,600a,601a,604a,700a,700b,700c,700d,700a,701b,701c,701d,702a,702b,702c,702d,710a,710b,710c,710d,711a,711b,711c,711d,712a,712b,712c,712d', - 'isbn' => '010a', - 'issn' => '011a', - 'biblionumber' =>'0909', - 'itemtype' => '200b', - 'language' => '010a', - 'publisher' => '210x', - 'date' => '210d', - 'note' => '300a,301a,302a,303a,304a,305a,306az,307a,308a,309a,310a,311a,312a,313a,314a,315a,316a,317a,318a,319a,320a,321a,322a,323a,324a,325a,326a,327a,328a,330a,332a,333a,336a,337a,345a', - 'Koha-Auth-Number' => '6009,6019,6029,6039,6049,6059,6069,6109', - 'subject' => '600*,601*,606*,610*', - 'dewey' => '676a', - 'host-item' => '995a,995c', - ); +my %index = GetNoZebraIndexes(); $|=1; while (my ($biblionumber) = $sth->fetchrow) { @@ -71,14 +56,11 @@ while (my ($biblionumber) = $sth->fetchrow) { my $record = GetMarcBiblio($biblionumber); # get title of the record (to store the 10 first letters with the index) - my $title; - if (C4::Context->preference('marcflavour') eq 'UNIMARC') { - $title = lc($record->subfield('200','a')); - } else { - $title = lc($record->subfield('245','a')); - } + my ($titletag,$titlesubfield) = GetMarcFromKohaField('biblio.title'); + my $title = lc($record->subfield($titletag,$titlesubfield)); + # remove blancks comma (that could cause problem when decoding the string for CQL retrieval) and regexp specific values - $title =~ s/ |,|;|\[|\]|\(|\)|\*//g; + $title =~ s/ |,|;|\[|\]|\(|\)|\*|-|'|=//g; # limit to 10 char, should be enough, and limit the DB size $title = substr($title,0,10); #parse each field @@ -92,16 +74,20 @@ while (my ($biblionumber) = $sth->fetchrow) { # check each index to see if the subfield is stored somewhere # otherwise, store it in __RAW__ index foreach my $key (keys %index) { - if ($index{$key} =~ /$tag\*/ or $index{$key} =~ /$tag$subfield/) { + if ($index{$key} =~ /$tag\*/ or $index{$key} =~ /$tag$subfieldcode/) { $indexed=1; my $line= lc $subfield->[1]; - $line =~ s/-|\.|\?|,|;|!|'|\(|\)|\[|\]|{|}|"|<|>|&|\+|\*|\// /g; + # remove meaningless value in the field... + $line =~ s/-|\.|\?|,|;|!|'|\(|\)|\[|\]|{|}|"|<|>|&|\+|\*|\/|=/ /g; + # ... and split in words foreach (split / /,$line) { - # see if the entry is already here + next unless $_; # skip empty values (multiple spaces) + # if the entry is already here, improve weight if ($result{$key}->{$_} =~ /$biblionumber,$title\-(\d);/) { my $weight=$1+1; $result{$key}->{$_} =~ s/$biblionumber,$title\-(\d);//; $result{$key}->{$_} .= "$biblionumber,$title-$weight;"; + # otherwise, create it, with weight=1 } else { $result{$key}->{$_}.="$biblionumber,$title-1;"; } @@ -111,8 +97,9 @@ while (my ($biblionumber) = $sth->fetchrow) { # the subfield is not indexed, store it in __RAW__ index anyway unless ($indexed) { my $line= lc $subfield->[1]; - $line =~ s/-|\.|\?|,|;|!|'|\(|\)|\[|\]|{|}|"|<|>|&|\+|\*|\// /g; + $line =~ s/-|\.|\?|,|;|!|'|\(|\)|\[|\]|{|}|"|<|>|&|\+|\*|\/|=/ /g; foreach (split / /,$line) { + next unless $_; # warn $record->as_formatted."$_ =>".$title; if ($result{__RAW__}->{$_} =~ /$biblionumber,$title\-(\d);/) { my $weight=$1+1; @@ -131,8 +118,8 @@ my $sth = $dbh->prepare("INSERT INTO nozebra (indexname,value,biblionumbers) VAL foreach my $key (keys %result) { foreach my $index (keys %{$result{$key}}) { $sth->execute($key,$index,$result{$key}->{$index}); - if (length($result{$key}->{$index}) >40000) { - print length($result{$key}->{$index})." for $key / $index"; + if (length($result{$key}->{$index}) > 40000) { + print length($result{$key}->{$index})."\n for $key / $index\n"; } } } diff --git a/updater/updatedatabase b/updater/updatedatabase index d717da7d39..f7ac5cc683 100755 --- a/updater/updatedatabase +++ b/updater/updatedatabase @@ -530,6 +530,14 @@ my %tabledata = ( explanation => 'Active this if you want NOT to use zebra (large libraries should avoid this parameters)', type => 'YesNo', }, + { + uniquefieldrequired => 'variable', + variable => 'NoZebraIndexes', + value => '0', + forceupdate => {'explanation' => 1, 'type' => 1}, + explanation => "Enter a specific hash for NoZebra indexes. Enter : 'indexname' => '100a,245a,500*','index2' => '...'", + type => 'Free', + }, ], userflags => [ { @@ -1993,6 +2001,15 @@ $sth->finish; exit; # $Log$ +# Revision 1.163 2007/05/02 16:44:31 tipaul +# NoZebra SQL index management : +# * adding 3 subs in Biblio.pm +# - GetNoZebraIndexes, that get the index structure in a new systempreference (added with this commit) +# - _DelBiblioNoZebra, that retrieve all index entries for a biblio and remove in a variable the biblio reference +# - _AddBiblioNoZebra, that add index entries for a biblio. +# Note that the 2 _Add and _Del subs work only in a hash variable, to speed up things in case of a modif (ie : delete+add). The effective SQL update is done in the ModZebra sub (that existed before, and dealed with zebra index). +# I think the code has to be more deeply tested, but it works at least partially. +# # Revision 1.162 2007/04/30 16:16:50 tipaul # bugfix for updatedatabase : when there is no default value (NULL fields) + removing bibliothesaurus table+adding NoZebra systempref (False by default) # -- 2.39.5