3 # Copyright 2009 Biblibre SARL
5 # This file is part of Koha.
7 # Koha is free software; you can redistribute it and/or modify it under the
8 # terms of the GNU General Public License as published by the Free Software
9 # Foundation; either version 2 of the License, or (at your option) any later
12 # Koha is distributed in the hope that it will be useful, but WITHOUT ANY
13 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
14 # A PARTICULAR PURPOSE. See the GNU General Public License for more details.
16 # You should have received a copy of the GNU General Public License along with
17 # Koha; if not, write to the Free Software Foundation, Inc., 59 Temple Place,
18 # Suite 330, Boston, MA 02111-1307 USA
23 use List::MoreUtils qw(first_value any);
25 use C4::Dates qw(format_date_in_iso);
29 use vars qw($VERSION @ISA @EXPORT_OK %EXPORT_TAGS);
32 # set the version for version checking
43 %EXPORT_TAGS = ( all =>[qw( InsertInTable SearchInTable UpdateInTable GetPrimaryKeys)]
52 C4::SQLHelper - Perl Module containing convenience functions for SQL Handling
60 This module contains routines for adding, modifying and Searching Data in MysqlDB
73 $hashref = &SearchInTable($tablename,$data, $orderby, $limit, $columns_out, $filtercolumns, $searchtype);
77 $tablename Name of the table (string)
80 - data_hashref : will be considered as an AND of all the data searched
81 - data_array_ref on hashrefs : Will be considered as an OR of Datahasref elements
83 $orderby is an arrayref of hashref with fieldnames as key and 0 or 1 as values (ASCENDING or DESCENDING order)
84 $limit is an array ref on 2 values
85 $columns_out is an array ref on field names is used to limit results on those fields (* by default)
86 $filtercolums is an array ref on field names : is used to limit expansion of research for strings
87 $searchtype is string Can be "wide" or "exact"
92 my ($tablename,$filters,$orderby, $limit, $columns_out, $filter_columns,$searchtype) = @_;
94 my $dbh = C4::Context->dbh;
96 my $sql = do { local $"=', ';
97 qq{ SELECT @$columns_out from $tablename}
101 my ($keys,$values)=_filter_fields($filters,$tablename, $searchtype,$filter_columns);
102 my @criteria=grep{defined($_) && $_ !~/^\W$/ }@$keys;
104 $sql.= do { local $"=') AND (';
105 qq{ WHERE (@criteria) }
109 my @orders=map{ "$_".($$orderby{$_}? " DESC" : "") } keys %$orderby;
110 $sql.= do { local $"=', ';
111 qq{ ORDER BY @orders}
115 $sql.=qq{ LIMIT }.join(",",@$limit);
118 $debug && $values && warn $sql," ",join(",",@$values);
119 $sth = $dbh->prepare($sql);
120 $sth->execute(@$values);
121 my $results = $sth->fetchall_arrayref( {} );
129 $data_id_in_table = &InsertInTable($tablename,$data_hashref);
134 and returns the id of the row inserted
138 my ($tablename,$data) = @_;
139 my $dbh = C4::Context->dbh;
140 my ($keys,$values)=_filter_fields($data,$tablename,0);
141 map{$_=~s/\(|\)//g; $_=~s/ AND /, /g}@$keys;
142 my $query = do { local $"=',';
144 INSERT INTO $tablename
149 $debug && warn $query, join(",",@$values);
150 my $sth = $dbh->prepare($query);
151 $sth->execute( @$values);
153 return $dbh->last_insert_id(undef, undef, $tablename, undef);
160 $status = &UpdateInTable($tablename,$data_hashref);
165 and returns the status of the operation
169 my ($tablename,$data) = @_;
170 my @field_ids=GetPrimaryKeys($tablename);
171 my @ids=@$data{@field_ids};
172 my $dbh = C4::Context->dbh;
173 my ($keys,$values)=_filter_fields($data,$tablename,0);
174 map{$_=~s/\(|\)//g; $_=~s/ AND /, /g}@$keys;
175 my $query = do { local $"=',';
179 WHERE }.join (" AND ",map{ "$_=?" }@field_ids);
181 $debug && warn $query, join(",",@$values,@ids);
183 my $sth = $dbh->prepare($query);
184 return $sth->execute( @$values,@ids);
192 $status = &DeleteInTable($tablename,$data_hashref);
197 and returns the status of the operation
201 my ($tablename,$data) = @_;
202 my @field_ids=GetPrimaryKeys($tablename);
203 my @ids=$$data{@field_ids};
204 my $dbh = C4::Context->dbh;
205 my ($keys,$values)=_filter_fields($data,$tablename,0);
207 my $query = do { local $"=' AND ';
209 DELETE FROM $tablename
210 WHERE }.map{" $_=? "}@field_ids;
212 $debug && warn $query, join(",",@$values,@ids);
214 my $sth = $dbh->prepare($query);
215 return $sth->execute( @$values,@ids);
219 =head2 GetPrimaryKeys
223 @primarykeys = &GetPrimaryKeys($tablename)
227 Get the Primary Key field names of the table
230 sub GetPrimaryKeys($) {
232 my $hash_columns=_get_columns($tablename);
233 return grep { $$hash_columns{$_}{'Key'} =~/PRI/i} keys %$hash_columns;
240 _get_columns($tablename)
245 Returns a hashref of all the fieldnames of the table
253 sub _get_columns($) {
255 my $dbh=C4::Context->dbh;
256 my $sth=$dbh->prepare(qq{SHOW COLUMNS FROM $tablename });
258 my $columns= $sth->fetchall_hashref(qw(Field));
261 =head2 _filter_columns
265 _filter_columns($tablename,$research, $filtercolumns)
271 - indicator on purpose whether it is a research or not
272 - array_ref to columns to limit to
274 Returns an array of all the fieldnames of the table
275 If it is not for research purpose, filter primary keys
279 sub _filter_columns ($$;$) {
280 my ($tablename,$research, $filtercolumns)=@_;
282 return (@$filtercolumns);
285 my $columns=_get_columns($tablename);
287 return keys %$columns;
290 return grep {my $column=$_; any {$_ ne $column }GetPrimaryKeys($tablename) } keys %$columns;
294 =head2 _filter_fields
304 - a string or a hashref (containing, fieldnames and datatofilter) or an arrayref to one of those elements
305 - an indicator of operation whether it is a wide research or a narrow one
306 - an array ref to columns to restrict string filter to.
308 Returns a ref of key array to use in SQL functions
309 and a ref to value array
314 my ($filter_input,$tablename,$searchtype,$filtercolumns)=@_;
317 if (ref($filter_input) eq "HASH"){
318 return _filter_hash($filter_input,$tablename, $searchtype);
319 } elsif (ref($filter_input) eq "ARRAY"){
320 foreach my $element_data (@$filter_input){
321 my ($localkeys,$localvalues)=_filter_fields($element_data,$tablename,$searchtype,$filtercolumns);
323 @$localkeys=grep{defined($_) && $_ !~/^\W*$/}@$localkeys;
329 push @values, @$localvalues;
334 return _filter_string($filter_input,$tablename, $searchtype,$filtercolumns);
337 return (\@keys,\@values);
341 my ($filter_input, $tablename,$searchtype)=@_;
343 my $columns= _get_columns($tablename);
344 my @columns_filtered= _filter_columns($tablename,$searchtype);
346 #Filter Primary Keys of table
347 my $elements=join "|",@columns_filtered;
348 foreach my $field (grep {/\b($elements)\b/} keys %$filter_input){
349 ## supposed to be a hash of simple values, hashes of arrays could be implemented
350 $$filter_input{$field}=format_date_in_iso($$filter_input{$field}) if ($$columns{$field}{Type}=~/date/ && $$filter_input{$field} !~C4::Dates->regexp("iso"));
351 my ($tmpkeys, $localvalues)=_Process_Operands($$filter_input{$field},$field,$searchtype,$columns);
353 push @values, @$localvalues;
354 push @keys, @$tmpkeys;
362 return ([$string],\@values);
370 my ($filter_input,$tablename, $searchtype,$filtercolumns)=@_;
371 my @columns_filtered= _filter_columns($tablename,$searchtype,$filtercolumns);
372 my $columns= _get_columns($tablename);
373 my @operands=split / /,$filter_input;
376 foreach my $operand (@operands){
378 foreach my $field (@columns_filtered){
379 my ($tmpkeys, $localvalues)=_Process_Operands($operand,$field,$searchtype,$columns);
381 push @values,@$localvalues;
382 push @localkeys,@$tmpkeys;
386 my $sql= do { local $"=' OR ';
393 return (\@keys,\@values);
399 sub _Process_Operands{
400 my ($operand, $field, $searchtype,$columns)=@_;
404 my @localvaluesextended=("\% $operand\%","$operand\%") ;
405 $strkeys= " $field = ? ";
406 if ($searchtype eq "wide"){
407 if ($field=~/(?<!zip)code|number/ ){
408 $strkeys="( $field='' OR $field IS NULL OR $strkeys ) ";
409 } elsif ($$columns{$field}{Type}=~/varchar|text/){
410 $strkeys="( $field LIKE ? OR $field LIKE ? OR $strkeys ) ";
411 push @values,@localvaluesextended;
413 push @tmpkeys, $strkeys;
416 $strkeys= " $field = ? ";
417 push @tmpkeys, $strkeys;
419 push @values, $operand;
420 return (\@tmpkeys,\@values);