Code

Created trunk inside of 2.6-lhm
[gosa.git] / trunk / gosa-si / modules / DBsqlite.pm
1 package GOSA::DBsqlite;
3 use strict;
4 use warnings;
5 use Carp;
6 use DBI;
7 use Data::Dumper;
8 use GOSA::GosaSupportDaemon;
9 use Time::HiRes qw(usleep);
10 use Fcntl ':flock'; # import LOCK_* constants
12 my $col_names = {};
14 sub new {
15         my $class = shift;
16         my $db_name = shift;
18         my $lock = $db_name.".si.lock";
19         # delete existing lock - instance should be running only once
20         if(stat($lock)) {
21                 &main::daemon_log("DEBUG: Removed existing lock file $lock.", 7);
22                 unlink($lock);
23         }
24         my $self = {dbh=>undef,db_name=>undef,db_lock=>undef,db_lock_handle=>undef};
25         my $dbh = DBI->connect("dbi:SQLite:dbname=$db_name", "", "", {RaiseError => 1, AutoCommit => 1});
26         $self->{dbh} = $dbh;
27         $self->{db_name} = $db_name;
28         $self->{db_lock} = $lock;
29         bless($self,$class);
30         return($self);
31 }
34 sub lock {
35         my $self = shift;
36         open($self->{db_lock_handle}, ">>".($self->{db_lock})) unless ref $self->{db_lock_handle};
37         flock($self->{db_lock_handle},LOCK_EX);
38         seek($self->{db_lock_handle}, 0, 2);
39 }
42 sub unlock {
43         my $self = shift;
44         flock($self->{db_lock_handle},LOCK_UN);
45 }
48 sub create_table {
49         my $self = shift;
50         my $table_name = shift;
51         my $col_names_ref = shift;
52         my @col_names;
53         foreach my $col_name (@$col_names_ref) {
54                 my @t = split(" ", $col_name);
55                 $col_name = $t[0];
56                 push(@col_names, $col_name);
57         }
59         $col_names->{ $table_name } = $col_names_ref;
60         my $col_names_string = join("', '", @col_names);
61         my $sql_statement = "CREATE TABLE IF NOT EXISTS $table_name ( '$col_names_string' )"; 
62         $self->lock();
63         eval {
64                 my $res = $self->{dbh}->do($sql_statement);
65         };
66         if($@) {
67                 $self->{dbh}->do("ANALYZE");
68                 eval {
69                         my $res = $self->{dbh}->do($sql_statement);
70                 };
71                 if($@) {
72                         &main::daemon_log("ERROR: $sql_statement failed with $@", 1);
73                 }
74         }
75         $self->unlock();
77         return 0;
78 }
81 sub add_dbentry {
82         my $self = shift;
83         my $arg = shift;
84         my $res = 0;   # default value
86         # if dbh not specified, return errorflag 1
87         my $table = $arg->{table};
88         if( not defined $table ) { 
89                 return 1 ; 
90         }
92         # if timestamp is not provided, add timestamp   
93         if( not exists $arg->{timestamp} ) {
94                 $arg->{timestamp} = &get_time;
95         }
97         # check primkey and run insert or update
98         my $primkeys = $arg->{'primkey'};
99         my $prim_statement="";
100         if( 0 != @$primkeys ) {   # more than one primkey exist in list
101                 my @prim_list;
102                 foreach my $primkey (@$primkeys) {
103                         if( not exists $arg->{$primkey} ) {
104                                 return (3, "primkey '$primkey' has no value for add_dbentry");
105                         }
106                         push(@prim_list, "$primkey='".$arg->{$primkey}."'");
107                 }
108                 $prim_statement = "WHERE ".join(" AND ", @prim_list);
110                 # check wether primkey is unique in table, otherwise return errorflag
111                 my $sql_statement = "SELECT * FROM $table $prim_statement";
112                 $self->lock();
113                 eval {
114                         $res = @{ $self->{dbh}->selectall_arrayref($sql_statement) };
115                 };
116                 if($@) {
117                         $self->{dbh}->do("ANALYZE");
118                         eval {
119                                 $res = @{ $self->{dbh}->selectall_arrayref($sql_statement) };
120                         };
121                         if($@) {
122                                 &main::daemon_log("ERROR: $sql_statement failed with $@", 1);
123                         }
124                 }
125                 $self->unlock();
127         }
129         # primkey is unique or no primkey specified -> run insert
130         if ($res == 0) {
131                 # fetch column names of table
132                 my $col_names = &get_table_columns($self, $table);
134                 my $create_id=0;
135                 foreach my $col_name (@{$col_names}) {
136                         if($col_name eq "id" && (! exists $arg->{$col_name})) {
137                                 #&main::daemon_log("0 DEBUG: id field found without value! Creating autoincrement statement!", 7);
138                                 $create_id=1;
139                         }
140                 }
142                 # assign values to column name variables
143                 my @col_list;
144                 my @val_list;
145                 foreach my $col_name (@{$col_names}) {
146                         # use function parameter for column values
147                         if (exists $arg->{$col_name}) {
148                                 push(@col_list, "'".$col_name."'");
149                                 push(@val_list, "'".$arg->{$col_name}."'");
150                         }
151                 }    
153                 my $sql_statement;
154                 if($create_id==1) {
155                         $sql_statement = "INSERT INTO $table ('id', ".join(", ", @col_list).") VALUES ((select coalesce(max(id), 0)+1 from $table), ".join(", ", @val_list).")";
156                 } else {
157                         $sql_statement = "INSERT INTO $table (".join(", ", @col_list).") VALUES (".join(", ", @val_list).")";
158                 }
159                 my $db_res;
160                 $self->lock();
161                 eval {
162                         $db_res = $self->{dbh}->do($sql_statement);
163                 };
164                 if($@) {
165                         $self->{dbh}->do("ANALYZE");
166                         eval {
167                                 $db_res = $self->{dbh}->do($sql_statement);
168                         };
169                         if($@) {
170                                 &main::daemon_log("ERROR: $sql_statement failed with $@", 1);
171                         }
172                 }
173                 $self->unlock();
175                 if( $db_res != 1 ) {
176                         return (4, $sql_statement);
177                 } 
179                 # entry already exists -> run update
180         } else  {
181                 my @update_l;
182                 while( my ($pram, $val) = each %{$arg} ) {
183                         if( $pram eq 'table' ) { next; }
184                         if( $pram eq 'primkey' ) { next; }
185                         push(@update_l, "$pram='$val'");
186                 }
187                 my $update_str= join(", ", @update_l);
188                 $update_str= " SET $update_str";
190                 my $sql_statement= "UPDATE $table $update_str $prim_statement";
191                 my $db_res = &update_dbentry($self, $sql_statement );
192         }
194         return 0;
198 sub update_dbentry {
199         my ($self, $sql)= @_;
200         my $db_answer= &exec_statement($self, $sql); 
201         return $db_answer;
205 sub del_dbentry {
206         my ($self, $sql)= @_;;
207         my $db_res= &exec_statement($self, $sql);
208         return $db_res;
212 sub get_table_columns {
213         my $self = shift;
214         my $table = shift;
215         my @column_names;
217         if(exists $col_names->{$table}) {
218                 @column_names = @{$col_names->{$table}};
219         } else {
220                 my @res;
221                 $self->lock();
222                 eval {
223                         @res = @{$self->{dbh}->selectall_arrayref("pragma table_info('$table')")};
224                 };
225                 if($@) {
226                         $self->{dbh}->do("ANALYZE");
227                         eval {
228                                 @res = @{$self->{dbh}->selectall_arrayref("pragma table_info('$table')")};
229                         };
230                         if($@) {
231                                 &main::daemon_log("ERROR: pragma table_info('$table') failed with $@", 1);
232                         }
233                 }
234                 $self->unlock();
236                 foreach my $column (@res) {
237                         push(@column_names, @$column[1]);
238                 }
239         }
240         return \@column_names;
245 sub select_dbentry {
246         my ($self, $sql)= @_;
247         my $error= 0;
248         my $answer= {};
249         my $db_answer= &exec_statement($self, $sql); 
250         my @column_list;
252         # fetch column list of db and create a hash with column_name->column_value of the select query
253         $sql =~ /SELECT ([\S\s]*?) FROM ([\S]*?)( |$)/g;
254         my $selected_cols = $1;
255         my $table = $2;
257         # all columns are used for creating answer
258         if ($selected_cols eq '*') {
259                 @column_list = @{ &get_table_columns($self, $table) };    
261                 # specific columns are used for creating answer
262         } else {
263                 # remove all blanks and split string to list of column names
264                 $selected_cols =~ s/ //g;          
265                 @column_list = split(/,/, $selected_cols);
266         }
268         # create answer
269         my $hit_counter = 0;
270         my $list_len = @column_list;
271         foreach my $hit ( @{$db_answer} ){
272                 $hit_counter++;
273                 for ( my $i = 0; $i < $list_len; $i++) {
274                         $answer->{ $hit_counter }->{ $column_list[$i] } = @{ $hit }[$i];
275                 }
276         }
278         return $answer;  
282 sub show_table {
283         my $self = shift;
284         my $table_name = shift;
286         my $sql_statement= "SELECT * FROM $table_name ORDER BY timestamp";
287         my $res= &exec_statement($self, $sql_statement);
288         my @answer;
289         foreach my $hit (@{$res}) {
290                 push(@answer, "hit: ".join(', ', @{$hit}));
291         }
293         return join("\n", @answer);
297 sub exec_statement {
298         my $self = shift;
299         my $sql_statement = shift;
300         my @db_answer;
302         $self->lock();
303         eval {
304                 @db_answer = @{$self->{dbh}->selectall_arrayref($sql_statement)};
305         };
306         if($@) {
307                 $self->{dbh}->do("ANALYZE");
308                 eval {
309                         @db_answer = @{$self->{dbh}->selectall_arrayref($sql_statement)};
310                 };
311                 if($@) {
312                         &main::daemon_log("ERROR: $sql_statement failed with $@", 1);
313                 }
314         }
315         $self->unlock();
316         # TODO : maybe an error handling and an erro feedback to invoking function
317         #my $error = @$self->{dbh}->err;
318         #if ($error) {
319         #       my $error_string = @$self->{dbh}->errstr;
320         #}
322         return \@db_answer;
326 sub exec_statementlist {
327         my $self = shift;
328         my $sql_list = shift;
329         my @db_answer;
331         foreach my $sql (@$sql_list) {
332                 if(defined($sql) && length($sql) > 0) {
333                         $self->lock();
334                         eval {
335                                 my @answer = @{$self->{dbh}->selectall_arrayref($sql)};
336                                 push @db_answer, @answer;
337                         };
338                         if($@) {
339                                 $self->{dbh}->do("ANALYZE");
340                                 eval {
341                                         my @answer = @{$self->{dbh}->selectall_arrayref($sql)};
342                                         push @db_answer, @answer;
343                                 };
344                                 if($@) {
345                                         &main::daemon_log("ERROR: $sql failed with $@", 1);
346                                 }
347                         }
348                         $self->unlock();
349                 } else {
350                         next;
351                 }
352         }
354         return \@db_answer;
358 sub count_dbentries {
359         my ($self, $table)= @_;
360         my $error= 0;
361         my $answer= -1;
363         my $sql_statement= "SELECT * FROM $table";
364         my $db_answer= &select_dbentry($self, $sql_statement); 
366         my $count = keys(%{$db_answer});
367         return $count;
371 sub move_table {
372         my ($self, $from, $to) = @_;
374         my $sql_statement_drop = "DROP TABLE IF EXISTS $to";
375         my $sql_statement_alter = "ALTER TABLE $from RENAME TO $to";
377         eval {
378                 $self->{dbh}->do($sql_statement_drop);
379         };
380         if($@) {
381                 $self->{dbh}->do("ANALYZE");
382                 eval {
383                         $self->{dbh}->do($sql_statement_drop);
384                 };
385                 if($@) {
386                         &main::daemon_log("ERROR: $sql_statement_drop failed with $@", 1);
387                 }
388         }
390         eval {
391                 $self->{dbh}->do($sql_statement_alter);
392         };
393         if($@) {
394                 $self->{dbh}->do("ANALYZE");
395                 eval {
396                         $self->{dbh}->do($sql_statement_alter);
397                 };
398                 if($@) {
399                         &main::daemon_log("ERROR: $sql_statement_alter failed with $@", 1);
400                 }
401         }
403         return;
404
407 1;