Code

* change daemon_log level of db statements
authorrettenbe <rettenbe@594d385d-05f5-0310-b6e9-bd551577e9d8>
Tue, 24 Mar 2009 12:40:29 +0000 (12:40 +0000)
committerrettenbe <rettenbe@594d385d-05f5-0310-b6e9-bd551577e9d8>
Tue, 24 Mar 2009 12:40:29 +0000 (12:40 +0000)
* change db lock

git-svn-id: https://oss.gonicus.de/repositories/gosa/trunk@13563 594d385d-05f5-0310-b6e9-bd551577e9d8

gosa-si/modules/DBsqlite.pm

index ab66fce438b6ebe7811e51f0e23b42d4e1c6828f..5c9ae0c6f249fac2c7ab3d489b1b5b150c056e5f 100644 (file)
@@ -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,27 +41,48 @@ sub new {
 
 sub lock {
        my $self = shift;
+       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|O_CREAT, 0600) or &main::daemon_log("0 ERROR: Opening the lockfile for database ".$self->{db_name}." failed with $!", 1);
+               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);
        }
-       flock($self->{db_lock_handle}, LOCK_EX);
-       seek($self->{db_lock_handle}, 0, 2);
+       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}, 9);
+       } else {
+               &main::daemon_log("0 ERROR: Could not acquire lock for database ".$self->{db_name}, 1);
+       }
+       return;
 }
 
 
 sub unlock {
        my $self = shift;
+       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 databse ".$self->{db_name}."got closed within critical section!", 1);
+               &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}, 9);
+       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) {
@@ -75,20 +97,14 @@ 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;
 }
@@ -101,8 +117,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   
@@ -125,21 +141,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
@@ -150,11 +152,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;
@@ -164,7 +164,7 @@ sub add_dbentry {
                                push(@col_list, "'".$col_name."'");
                                push(@val_list, "'".$arg->{$col_name}."'");
                        }
-               }    
+               }
 
                my $sql_statement;
                if($create_id==1) {
@@ -173,24 +173,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($sql_statement);
+                       $db_res = $sth->execute();
+                       $sth->finish();
+                       &main::daemon_log("0 DEBUG: Execution of statement '$sql_statement' succeeded!", 9);
+                       $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($sql_statement);
+                               $db_res = $sth->execute();
+                               $sth->finish();
+                               &main::daemon_log("0 DEBUG: Execution of statement '$sql_statement' succeeded!", 9);
+                               $success = 1;
+                       };
+                       if($@) {
+                               eval {
+                                       $self->{dbh}->do("ANALYZE");
+                                       $self->{dbh}->do("VACUUM");
+                               };
+                       }
+               }
+               if($success==0) {
+                       eval {
+                               my $sth = $self->{dbh}->prepare($sql_statement);
+                               $db_res = $sth->execute();
+                               $sth->finish();
+                               &main::daemon_log("0 DEBUG: Execution of statement '$sql_statement' succeeded!", 9);
+                               $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  {
@@ -209,24 +238,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;
 
@@ -234,22 +273,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]);
                }
        }
@@ -260,9 +284,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
@@ -272,7 +300,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 {
@@ -297,10 +325,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}));
@@ -313,28 +345,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($sql_statement);
+               my $res = $sth->execute();
+               @db_answer = @{$sth->fetchall_arrayref()};
+               $sth->finish();
+               $success=1;
+               &main::daemon_log("0 DEBUG: $sql_statement succeeded.", 9);
        };
        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(6) if($main::verbose >= 7);
+               my $sth = $self->{dbh}->prepare($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.", 9);
+       };
+       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(6) if($main::verbose >= 7);
+               my $sth = $self->{dbh}->prepare($sql_statement);
+               my $res = $sth->execute();
+               @db_answer = @{$sth->fetchall_arrayref()};
+               $sth->finish();
+               DBI->trace(0);
+               &main::daemon_log("0 DEBUG: $sql_statement succeeded.", 9);
+       };
+       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;
 }
 
@@ -342,27 +430,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;
                }
@@ -374,11 +450,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()'};
        }
@@ -389,36 +469,79 @@ 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";
+       my $success = 0;
 
        $self->lock();
        eval {
+               $self->{dbh}->begin_work();
                $self->{dbh}->do($sql_statement_drop);
+               $self->{dbh}->do($sql_statement_alter);
+               $self->{dbh}->commit();
+               $success = 1;
        };
        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}->do($sql_statement_alter);
-       };
-       if($@) {
-               $self->{dbh}->do("ANALYZE");
+       if($success == 0) {
                eval {
+                       $self->{dbh}->begin_work();
+                       $self->{dbh}->do($sql_statement_drop);
                        $self->{dbh}->do($sql_statement_alter);
+                       $self->{dbh}->commit();
+                       $success = 1;
                };
                if($@) {
-                       &main::daemon_log("ERROR: $sql_statement_alter failed with $@", 1);
+                       $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("VACUUM");
+                       };
+                       if($@) {
+                               &main::daemon_log("ERROR: 'VACUUM' on database '".$self->{db_name}."' failed with $@", 1);
+                       }
+               }
+       }
+       
+       if($success == 0) {
+               eval {
+                       $self->{dbh}->begin_work();
+                       $self->{dbh}->do($sql_statement_drop);
+                       $self->{dbh}->do($sql_statement_alter);
+                       $self->{dbh}->commit();
+                       $success = 1;
+               };
+               if($@) {
+                       $self->{dbh}->rollback();
+                       &main::daemon_log("0 ERROR: GOSA::DBsqlite::move_table crashed! Operation failed with $@", 1);
                }
        }
+
+       &main::daemon_log("0 INFO: GOSA::DBsqlite::move_table: Operation successful!", 7);
        $self->unlock();
 
        return;