X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;f=trunk%2Fgosa-si%2Fmodules%2FDBsqlite.pm;h=f4e442e41e6a66ffd795b6d6c42a1ac3a8569310;hb=d924450468fc506a26573eb09f2f63404087be29;hp=c9ebc440f562bc4d91405895236e40f4125f6e9c;hpb=05f3e3b0fdd8f805e365f56e0690c7478d6f8151;p=gosa.git diff --git a/trunk/gosa-si/modules/DBsqlite.pm b/trunk/gosa-si/modules/DBsqlite.pm index c9ebc440f..f4e442e41 100644 --- a/trunk/gosa-si/modules/DBsqlite.pm +++ b/trunk/gosa-si/modules/DBsqlite.pm @@ -7,7 +7,7 @@ use DBI; use Data::Dumper; use GOSA::GosaSupportDaemon; use Time::HiRes qw(usleep); -use Fcntl ':flock'; # import LOCK_* constants +use Fcntl qw/:DEFAULT :flock/; # import LOCK_* constants my $col_names = {}; @@ -15,20 +15,21 @@ sub new { my $class = shift; my $db_name = shift; - my $lock = $db_name.".si.lock"; + #my $lock = $db_name.".si.lock"; + my $lock = $db_name; my $self = {dbh=>undef,db_name=>undef,db_lock=>undef,db_lock_handle=>undef}; - my $dbh = DBI->connect("dbi:SQLite:dbname=$db_name", "", "", {RaiseError => 1, AutoCommit => 1}); + my $dbh = DBI->connect("dbi:SQLite:dbname=$db_name", "", "", {RaiseError => 1, AutoCommit => 1, PrintError => 0}); my $sth = $dbh->prepare("pragma integrity_check"); - $sth->execute(); + $sth->execute(); my @ret = $sth->fetchall_arrayref(); + $sth->finish(); if(length(@ret)==1 && $ret[0][0][0] eq 'ok') { &main::daemon_log("DEBUG: Database image $db_name is ok", 7); } else { &main::daemon_log("ERROR: Database image $db_name is malformed, creating new database.", 1); - $sth->finish(); $dbh->disconnect(); unlink($db_name); - $dbh = DBI->connect("dbi:SQLite:dbname=$db_name", "", "", {RaiseError => 1, AutoCommit => 1}); + $dbh = DBI->connect("dbi:SQLite:dbname=$db_name", "", "", {RaiseError => 1, AutoCommit => 1}); } $self->{dbh} = $dbh; $self->{db_name} = $db_name; @@ -40,22 +41,48 @@ sub new { sub lock { my $self = shift; - open($self->{db_lock_handle}, ">>".($self->{db_lock})) unless ref $self->{db_lock_handle}; - flock($self->{db_lock_handle},LOCK_EX); - seek($self->{db_lock_handle}, 0, 2); + if(not defined($self) or ref($self) ne 'GOSA::DBsqlite') { + &main::daemon_log("0 ERROR: GOSA::DBsqlite::lock was called static! Statement was '$self'!", 1); + return; + } + if(not ref $self->{db_lock_handle}) { + sysopen($self->{db_lock_handle}, $self->{db_lock}, O_RDWR) or &main::daemon_log("0 ERROR: Opening the database ".$self->{db_name}." failed with $!", 1); + } + my $lock_result = flock($self->{db_lock_handle}, LOCK_EX); + if($lock_result==1) { + seek($self->{db_lock_handle}, 0, 2); + &main::daemon_log("0 DEBUG: Acquired lock for database ".$self->{db_name}, 7); + } else { + &main::daemon_log("0 ERROR: Could not acquire lock for database ".$self->{db_name}, 1); + } + return; } sub unlock { my $self = shift; - flock($self->{db_lock_handle},LOCK_UN); + if(not defined($self) or ref($self) ne 'GOSA::DBsqlite') { + &main::daemon_log("0 ERROR: GOSA::DBsqlite::unlock was called static! Statement was '$self'!", 1); + return; + } + if(not ref $self->{db_lock_handle}) { + &main::daemon_log("0 BIG ERROR: Lockfile for database ".$self->{db_name}."got closed within critical section!", 1); + } + flock($self->{db_lock_handle}, LOCK_UN); + &main::daemon_log("0 DEBUG: Released lock for database ".$self->{db_name}, 7); + return; } sub create_table { my $self = shift; + if(not defined($self) or ref($self) ne 'GOSA::DBsqlite') { + &main::daemon_log("0 ERROR: GOSA::DBsqlite::create_table was called static! Statement was '$self'!", 1); + return; + } my $table_name = shift; my $col_names_ref = shift; + my $index_names_ref = shift || undef; my @col_names; my @col_names_creation; foreach my $col_name (@$col_names_ref) { @@ -70,20 +97,15 @@ sub create_table { $col_names->{ $table_name } = \@col_names; my $col_names_string = join(", ", @col_names_creation); my $sql_statement = "CREATE TABLE IF NOT EXISTS $table_name ( $col_names_string )"; - $self->lock(); - eval { - my $res = $self->{dbh}->do($sql_statement); - }; - if($@) { - $self->{dbh}->do("ANALYZE"); - eval { - my $res = $self->{dbh}->do($sql_statement); - }; - if($@) { - &main::daemon_log("ERROR: $sql_statement failed with $@", 1); + my $res = $self->exec_statement($sql_statement); + + # Add indices + if(defined($index_names_ref) and ref($index_names_ref) eq 'ARRAY') { + foreach my $index_name (@$index_names_ref) { + $self->exec_statement("CREATE ".(($index_name eq 'id')?'UNIQUE':'')." INDEX IF NOT EXISTS $index_name on $table_name ($index_name);"); } } - $self->unlock(); + return 0; } @@ -96,8 +118,8 @@ sub add_dbentry { # if dbh not specified, return errorflag 1 my $table = $arg->{table}; - if( not defined $table ) { - return 1 ; + if( not defined $table ) { + return 1 ; } # if timestamp is not provided, add timestamp @@ -120,21 +142,7 @@ sub add_dbentry { # check wether primkey is unique in table, otherwise return errorflag my $sql_statement = "SELECT * FROM $table $prim_statement"; - $self->lock(); - eval { - $res = @{ $self->{dbh}->selectall_arrayref($sql_statement) }; - }; - if($@) { - $self->{dbh}->do("ANALYZE"); - eval { - $res = @{ $self->{dbh}->selectall_arrayref($sql_statement) }; - }; - if($@) { - &main::daemon_log("ERROR: $sql_statement failed with $@", 1); - } - } - $self->unlock(); - + $res = @{ $self->exec_statement($sql_statement) }; } # primkey is unique or no primkey specified -> run insert @@ -145,11 +153,9 @@ sub add_dbentry { my $create_id=0; foreach my $col_name (@{$col_names}) { if($col_name eq "id" && (! exists $arg->{$col_name})) { - #&main::daemon_log("0 DEBUG: id field found without value! Creating autoincrement statement!", 7); $create_id=1; } } - # assign values to column name variables my @col_list; my @val_list; @@ -159,7 +165,7 @@ sub add_dbentry { push(@col_list, "'".$col_name."'"); push(@val_list, "'".$arg->{$col_name}."'"); } - } + } my $sql_statement; if($create_id==1) { @@ -168,24 +174,53 @@ sub add_dbentry { $sql_statement = "INSERT INTO $table (".join(", ", @col_list).") VALUES (".join(", ", @val_list).")"; } my $db_res; + my $success=0; $self->lock(); eval { - $db_res = $self->{dbh}->do($sql_statement); + my $sth = $self->{dbh}->prepare_cached($sql_statement); + $db_res = $sth->execute(); + $sth->finish(); + &main::daemon_log("0 DEBUG: Execution of statement '$sql_statement' succeeded!", 7); + $success = 1; }; if($@) { - $self->{dbh}->do("ANALYZE"); eval { - $db_res = $self->{dbh}->do($sql_statement); + $self->{dbh}->do("ANALYZE"); + $self->{dbh}->do("VACUUM"); + }; + } + if($success==0) { + eval { + my $sth = $self->{dbh}->prepare_cached($sql_statement); + $db_res = $sth->execute(); + $sth->finish(); + &main::daemon_log("0 DEBUG: Execution of statement '$sql_statement' succeeded!", 7); + $success = 1; + }; + if($@) { + eval { + $self->{dbh}->do("ANALYZE"); + $self->{dbh}->do("VACUUM"); + }; + } + } + if($success==0) { + eval { + my $sth = $self->{dbh}->prepare_cached($sql_statement); + $db_res = $sth->execute(); + $sth->finish(); + &main::daemon_log("0 DEBUG: Execution of statement '$sql_statement' succeeded!", 7); + $success = 1; }; if($@) { - &main::daemon_log("ERROR: $sql_statement failed with $@", 1); + &main::daemon_log("0 ERROR: Execution of statement '$sql_statement' failed with $@", 1); } } $self->unlock(); if( $db_res != 1 ) { return (4, $sql_statement); - } + } # entry already exists -> run update } else { @@ -204,24 +239,34 @@ sub add_dbentry { return 0; } - - sub update_dbentry { my ($self, $sql)= @_; - my $db_answer= &exec_statement($self, $sql); + if(not defined($self) or ref($self) ne 'GOSA::DBsqlite') { + &main::daemon_log("0 ERROR: GOSA::DBsqlite::update_dbentry was called static! Statement was '$self'!", 1); + return; + } + my $db_answer= $self->exec_statement($sql); return $db_answer; } sub del_dbentry { my ($self, $sql)= @_;; - my $db_res= &exec_statement($self, $sql); + if(not defined($self) or ref($self) ne 'GOSA::DBsqlite') { + &main::daemon_log("0 ERROR: GOSA::DBsqlite::del_dbentry was called static! Statement was '$self'!", 1); + return; + } + my $db_res= $self->exec_statement($sql); return $db_res; } sub get_table_columns { my $self = shift; + if(not defined($self) or ref($self) ne 'GOSA::DBsqlite') { + &main::daemon_log("0 ERROR: GOSA::DBsqlite::get_table_columns was called static! Statement was '$self'!", 1); + return; + } my $table = shift; my @column_names; @@ -229,22 +274,7 @@ sub get_table_columns { @column_names = @{$col_names->{$table}}; } else { my @res; - $self->lock(); - eval { - @res = @{$self->{dbh}->selectall_arrayref("pragma table_info('$table')")}; - }; - if($@) { - $self->{dbh}->do("ANALYZE"); - eval { - @res = @{$self->{dbh}->selectall_arrayref("pragma table_info('$table')")}; - }; - if($@) { - &main::daemon_log("ERROR: pragma table_info('$table') failed with $@", 1); - } - } - $self->unlock(); - - foreach my $column (@res) { + foreach my $column ( $self->exec_statement ( "pragma table_info('$table')" ) ) { push(@column_names, @$column[1]); } } @@ -255,9 +285,13 @@ sub get_table_columns { sub select_dbentry { my ($self, $sql)= @_; + if(not defined($self) or ref($self) ne 'GOSA::DBsqlite') { + &main::daemon_log("0 ERROR: GOSA::DBsqlite::select_dbentry was called static! Statement was '$self'!", 1); + return; + } my $error= 0; my $answer= {}; - my $db_answer= &exec_statement($self, $sql); + my $db_answer= $self->exec_statement($sql); my @column_list; # fetch column list of db and create a hash with column_name->column_value of the select query @@ -267,7 +301,7 @@ sub select_dbentry { # all columns are used for creating answer if ($selected_cols eq '*') { - @column_list = @{ &get_table_columns($self, $table) }; + @column_list = @{ $self->get_table_columns($table) }; # specific columns are used for creating answer } else { @@ -292,10 +326,14 @@ sub select_dbentry { sub show_table { my $self = shift; + if(not defined($self) or ref($self) ne 'GOSA::DBsqlite') { + &main::daemon_log("0 ERROR: GOSA::DBsqlite::show_table was called static! Statement was '$self'!", 1); + return; + } my $table_name = shift; my $sql_statement= "SELECT * FROM $table_name ORDER BY timestamp"; - my $res= &exec_statement($self, $sql_statement); + my $res= $self->exec_statement($sql_statement); my @answer; foreach my $hit (@{$res}) { push(@answer, "hit: ".join(', ', @{$hit})); @@ -308,28 +346,84 @@ sub show_table { sub exec_statement { my $self = shift; my $sql_statement = shift; - my @db_answer; + if(not defined($self) or ref($self) ne 'GOSA::DBsqlite') { + &main::daemon_log("0 ERROR: GOSA::DBsqlite::exec_statement was called static! Statement was '$self'!", 1); + return; + } + if(not defined($sql_statement) or length($sql_statement) == 0) { + &main::daemon_log("0 ERROR: GOSA::DBsqlite::exec_statement was called with empty statement!", 1); + return; + } + + my @db_answer; + my $success= 0; $self->lock(); + # Give three chances to the sqlite database + # 1st chance eval { - @db_answer = @{$self->{dbh}->selectall_arrayref($sql_statement)}; + my $sth = $self->{dbh}->prepare_cached($sql_statement); + my $res = $sth->execute(); + @db_answer = @{$sth->fetchall_arrayref()}; + $sth->finish(); + $success=1; + &main::daemon_log("0 DEBUG: $sql_statement succeeded.", 7); }; if($@) { - $self->{dbh}->do("ANALYZE"); eval { - @db_answer = @{$self->{dbh}->selectall_arrayref($sql_statement)}; + $self->{dbh}->do("ANALYZE"); + $self->{dbh}->do("VACUUM"); }; - if($@) { - &main::daemon_log("ERROR: $sql_statement failed with $@", 1); - } } - $self->unlock(); + if($success) { + $self->unlock(); + return \@db_answer ; + } + + # 2nd chance + eval { + DBI->trace(2) if($main::verbose >= 7); + my $sth = $self->{dbh}->prepare_cached($sql_statement); + my $res = $sth->execute(); + @db_answer = @{$sth->fetchall_arrayref()}; + $sth->finish(); + DBI->trace(0); + $success=1; + &main::daemon_log("0 DEBUG: $sql_statement succeeded.", 7); + }; + if($@) { + eval { + $self->{dbh}->do("ANALYZE"); + $self->{dbh}->do("VACUUM"); + }; + DBI->trace(0); + } + if($success) { + $self->unlock(); + return \@db_answer ; + } + + # 3rd chance + eval { + DBI->trace(2) if($main::verbose >= 7); + my $sth = $self->{dbh}->prepare_cached($sql_statement); + my $res = $sth->execute(); + @db_answer = @{$sth->fetchall_arrayref()}; + $sth->finish(); + DBI->trace(0); + &main::daemon_log("0 DEBUG: $sql_statement succeeded.", 7); + }; + if($@) { + DBI->trace(0); + &main::daemon_log("ERROR: $sql_statement failed with $@", 1); + } # TODO : maybe an error handling and an erro feedback to invoking function #my $error = @$self->{dbh}->err; #if ($error) { # my $error_string = @$self->{dbh}->errstr; #} + $self->unlock(); return \@db_answer; } @@ -337,27 +431,15 @@ sub exec_statement { sub exec_statementlist { my $self = shift; my $sql_list = shift; + if(not defined($self) or ref($self) ne 'GOSA::DBsqlite') { + &main::daemon_log("0 ERROR: GOSA::DBsqlite::exec_statementlist was called static!", 1); + return; + } my @db_answer; - foreach my $sql (@$sql_list) { - if(defined($sql) && length($sql) > 0) { - # Obtain a new lock for each statement to not block the db for a too long time - $self->lock(); - eval { - my @answer = @{$self->{dbh}->selectall_arrayref($sql)}; - push @db_answer, @answer; - }; - if($@) { - $self->{dbh}->do("ANALYZE"); - eval { - my @answer = @{$self->{dbh}->selectall_arrayref($sql)}; - push @db_answer, @answer; - }; - if($@) { - &main::daemon_log("ERROR: $sql failed with $@", 1); - } - } - $self->unlock(); + foreach my $sql_statement (@$sql_list) { + if(defined($sql_statement) && length($sql_statement) > 0) { + push @db_answer, $self->exec_statement($sql_statement); } else { next; } @@ -369,11 +451,15 @@ sub exec_statementlist { sub count_dbentries { my ($self, $table)= @_; + if(not defined($self) or ref($self) ne 'GOSA::DBsqlite') { + &main::daemon_log("0 ERROR: GOSA::DBsqlite::count_dbentries was called static!", 1); + return; + } my $error= 0; my $count= -1; my $sql_statement= "SELECT count() FROM $table"; - my $db_answer= &select_dbentry($self, $sql_statement); + my $db_answer= $self->select_dbentry($sql_statement); if(defined($db_answer) && defined($db_answer->{1}) && defined($db_answer->{1}->{'count()'})) { $count = $db_answer->{1}->{'count()'}; } @@ -384,36 +470,71 @@ sub count_dbentries { sub move_table { my ($self, $from, $to) = @_; + if(not defined($self) or ref($self) ne 'GOSA::DBsqlite') { + &main::daemon_log("0 ERROR: GOSA::DBsqlite::move_table was called static!", 1); + return; + } my $sql_statement_drop = "DROP TABLE IF EXISTS $to"; my $sql_statement_alter = "ALTER TABLE $from RENAME TO $to"; + &main::daemon_log("INFO move_table called successfully! Exiting!",1 ); + $self->lock(); eval { + $self->{dbh}->begin_work(); $self->{dbh}->do($sql_statement_drop); + $self->{dbh}->do($sql_statement_alter); + $self->{dbh}->commit(); }; if($@) { - $self->{dbh}->do("ANALYZE"); + $self->{dbh}->rollback(); eval { - $self->{dbh}->do($sql_statement_drop); + $self->{dbh}->do("ANALYZE"); }; if($@) { - &main::daemon_log("ERROR: $sql_statement_drop failed with $@", 1); + &main::daemon_log("ERROR: 'ANALYZE' on database '".$self->{db_name}."' failed with $@", 1); + } + eval { + $self->{dbh}->do("VACUUM"); + }; + if($@) { + &main::daemon_log("ERROR: 'VACUUM' on database '".$self->{db_name}."' failed with $@", 1); } } eval { + $self->{dbh}->begin_work(); + $self->{dbh}->do($sql_statement_drop); $self->{dbh}->do($sql_statement_alter); + $self->{dbh}->commit(); }; if($@) { - $self->{dbh}->do("ANALYZE"); + $self->{dbh}->rollback(); + eval { + $self->{dbh}->do("ANALYZE"); + }; + if($@) { + &main::daemon_log("ERROR: 'ANALYZE' on database '".$self->{db_name}."' failed with $@", 1); + } eval { - $self->{dbh}->do($sql_statement_alter); + $self->{dbh}->do("VACUUM"); }; if($@) { - &main::daemon_log("ERROR: $sql_statement_alter failed with $@", 1); + &main::daemon_log("ERROR: 'VACUUM' on database '".$self->{db_name}."' failed with $@", 1); } } + + eval { + $self->{dbh}->begin_work(); + $self->{dbh}->do($sql_statement_drop); + $self->{dbh}->do($sql_statement_alter); + $self->{dbh}->commit(); + }; + if($@) { + $self->{dbh}->rollback(); + &main::daemon_log("0 ERROR: GOSA::DBsqlite::move_table crashed! Operation failed with $@", 1); + } $self->unlock(); return;