--- /home/as/old/SpamAssassin/BayesStore/SQL.pm Sat Mar 19 01:06:25 2005 +++ /home/as/old/SpamAssassin/BayesStore/SQL.pm Wed Aug 3 11:44:47 2005 @@ -64,16 +64,40 @@ sub new { $self->{supported_db_version} = 3; - if (!$self->{bayes}->{conf}->{bayes_sql_dsn}) { - dbg("bayes: invalid config, must set bayes_sql_dsn config variable.\n"); - return undef; + #checks if bayes_read_write_support is enabled + my $bayes_read_write_support=$self->{bayes}->{conf}->{bayes_read_write_support}; + + $self->{bayes_read_write_support} = $self->{bayes}->{conf}->{bayes_read_write_support}; + + if($bayes_read_write_support) { + if (!$self->{bayes}->{conf}->{bayes_sql_dsn_read} || !$self->{bayes}->{conf}->{bayes_sql_dsn_write} || $self->{bayes}->{conf}->{bayes_sql_dsn} ) { + dbg("bayes: invalid config, must set bayes_sql_dsn_write and bayes_sql_dsn_read and not bayes_sql_dsn config variable.\n"); + return undef; + } + } + else + { + if (!$self->{bayes}->{conf}->{bayes_sql_dsn} || $self->{bayes}->{conf}->{bayes_sql_dsn_write} || $self->{bayes}->{conf}->{bayes_sql_dsn_read} ) { + dbg("bayes: invalid config, must set bayes_sql_dsn and not bayes_sql_dsn_write and not bayes_sql_dsn_read config variable.\n"); + return undef; + } } $self->{_dsn} = $self->{bayes}->{conf}->{bayes_sql_dsn}; $self->{_dbuser} = $self->{bayes}->{conf}->{bayes_sql_username}; $self->{_dbpass} = $self->{bayes}->{conf}->{bayes_sql_password}; + $self->{_dsn_write} = $self->{bayes}->{conf}->{bayes_sql_dsn_write}; + $self->{_dbuser_write} = $self->{bayes}->{conf}->{bayes_sql_username_write}; + $self->{_dbpass_write} = $self->{bayes}->{conf}->{bayes_sql_password_write}; + + $self->{_dsn_read} = $self->{bayes}->{conf}->{bayes_sql_dsn_read}; + $self->{_dbuser_read} = $self->{bayes}->{conf}->{bayes_sql_username_read}; + $self->{_dbpass_read} = $self->{bayes}->{conf}->{bayes_sql_password_read}; + $self->{_dbh} = undef; + $self->{_dbh_write} = undef; + $self->{_dbh_read} = undef; unless (HAS_DBI) { dbg("bayes: Unable to connect to database: DBI module not available: $!"); @@ -128,28 +152,70 @@ so that they can begin using the databas sub tie_db_writable { my ($self) = @_; + my $dbh; + my $dbh_read; + my $dbh_write; return 0 unless (HAS_DBI); - return 1 if ($self->{_dbh}); # already connected + if($self->{bayes_read_write_support}) { + if($self->{_dbh_write} && $self->{_dbh_read}) { + return 1;#already connected readable and writeable + } + } + else + { + return 1 if ($self->{_dbh}); # already connected + } my $main = $self->{bayes}->{main}; $self->read_db_configs(); - # Turn off PrintError and explicitly set AutoCommit to off - my $dbh = DBI->connect($self->{_dsn}, $self->{_dbuser}, $self->{_dbpass}, - {'PrintError' => 0, 'AutoCommit' => 1}); + if($self->{bayes_read_write_support}) { + # connect writeable / Turn off PrintError and explicitly set AutoCommit to off + $dbh_write = DBI->connect($self->{_dsn_write}, $self->{_dbuser_write}, $self->{_dbpass_write}, + {'PrintError' => 0, 'AutoCommit' => 1}); + + # connect readable / Turn off PrintError and explicitly set AutoCommit to off + $dbh_read = DBI->connect($self->{_dsn_read}, $self->{_dbuser_read}, $self->{_dbpass_read}, + {'PrintError' => 0, 'AutoCommit' => 1}); + } + else { + # Turn off PrintError and explicitly set AutoCommit to off + $dbh = DBI->connect($self->{_dsn}, $self->{_dbuser}, $self->{_dbpass}, + {'PrintError' => 0, 'AutoCommit' => 1}); + } + - if (!$dbh) { - dbg("bayes: Unable to connect to database: ".DBI->errstr()); - return 0; + if($self->{bayes_read_write_support}) { + if ($dbh_read && $dbh_write) { + dbg("bayes: Readable Database connection established with $self->{_dsn_read} "); + dbg("bayes: Writeable Database connection established with $self->{_dsn_write} "); + } + else { + dbg("bayes: Unable to connect to database: $self->{_dsn_read} "); + dbg("bayes: Unable to connect to database: $self->{_dsn_write} "); + return 0; + } } else { - dbg("bayes: Database connection established"); + if (!$dbh) { + dbg("bayes: Unable to connect to database: ".DBI->errstr()); + return 0; + } + else { + dbg("bayes: Database connection established with $self->{_dsn}"); + } } - $self->{_dbh} = $dbh; + if($self->{bayes_read_write_support}) { + $self->{_dbh_read} = $dbh_read; + $self->{_dbh_write} = $dbh_write; + } + else { + $self->{_dbh} = $dbh; + } # If the DB version is one we don't understand, abort! my $db_ver = $self->_get_db_version(); @@ -184,10 +250,19 @@ This method is unused for the SQL based sub untie_db { my ($self) = @_; - return unless (defined($self->{_dbh})); + if($self->{bayes_read_write_support}) { + return unless ( (defined($self->{_dbh_read})) && (defined($self->{_dbh_write})) ); + $self->{_dbh_write}->disconnect(); + $self->{_dbh_read}->disconnect(); + $self->{_dbh_read} = undef; + $self->{_dbh_write} = undef; + } + else { + return unless (defined($self->{_dbh})); + $self->{_dbh}->disconnect(); + $self->{_dbh} = undef; + } - $self->{_dbh}->disconnect(); - $self->{_dbh} = undef; } =head2 calculate_expire_delta @@ -204,24 +279,40 @@ atime for token expiration. sub calculate_expire_delta { my ($self, $newest_atime, $start, $max_expire_mult) = @_; - + my $sth; my %delta = (); # use a hash since an array is going to be very sparse - return %delta unless (defined($self->{_dbh})); + if($self->{bayes_read_write_support}) { + return %delta unless ( (defined($self->{_dbh_write})) && (defined($self->{_dbh_read})) ); + } + else { + return %delta unless (defined($self->{_dbh})); + } my $sql = "SELECT count(*) FROM bayes_token WHERE id = ? AND (? - atime) > ?"; - my $sth = $self->{_dbh}->prepare_cached($sql); + if($self->{bayes_read_write_support}) { + $sth = $self->{_dbh_read}->prepare_cached($sql); + } + else { + $sth = $self->{_dbh}->prepare_cached($sql); + } for (my $i = 1; $i <= $max_expire_mult; $i<<=1) { my $rc = $sth->execute($self->{_userid}, $newest_atime, $start * $i); unless ($rc) { - dbg("bayes: calculate_expire_delta: SQL Error: ".$self->{_dbh}->errstr()); - return undef; + if($self->{bayes_read_write_support}) { + dbg("bayes: calculate_expire_delta: SQL Error: ".$self->{_dbh_read}->errstr()); + return undef; + } + else { + dbg("bayes: calculate_expire_delta: SQL Error: ".$self->{_dbh}->errstr()); + return undef; + } } my ($count) = $sth->fetchrow_array(); @@ -252,6 +343,8 @@ sub token_expiration { my $num_hapaxes; my $num_lowfreq; my $deleted; + my $rows; + my $sth; # Figure out how old is too old... my $too_old = $vars[10] - $newdelta; # tooold = newest - delta @@ -261,10 +354,21 @@ sub token_expiration { WHERE id = ? AND atime > ?"; - my $rows = $self->{_dbh}->do($sql, undef, $vars[10], $self->{_userid}, $vars[10]); + if($self->{bayes_read_write_support}) { + $rows = $self->{_dbh_write}->do($sql, undef, $vars[10], $self->{_userid}, $vars[10]); + } + else { + $rows = $self->{_dbh}->do($sql, undef, $vars[10], $self->{_userid}, $vars[10]); + } unless (defined($rows)) { - dbg("bayes: token_expiration: SQL Error: ".$self->{_dbh}->errstr()); + if($self->{bayes_read_write_support}) { + dbg("bayes: token_expiration: SQL Error: ".$self->{_dbh_write}->errstr()); + } + else { + dbg("bayes: token_expiration: SQL Error: ".$self->{_dbh}->errstr()); + } + $deleted = 0; goto token_expiration_final; } @@ -274,10 +378,22 @@ sub token_expiration { WHERE id = ? AND atime < ?"; - my $sth = $self->{_dbh}->prepare_cached($sql); + + if($self->{bayes_read_write_support}) { + $sth = $self->{_dbh_read}->prepare_cached($sql); + } + else { + $sth = $self->{_dbh}->prepare_cached($sql); + } unless (defined($sth)) { - dbg("bayes: token_expiration: SQL Error: ".$self->{_dbh}->errstr()); + if($self->{bayes_read_write_support}) { + dbg("bayes: token_expiration: SQL Error: ".$self->{_dbh_read}->errstr()); + } + else { + dbg("bayes: token_expiration: SQL Error: ".$self->{_dbh}->errstr()); + } + $deleted = 0; goto token_expiration_final; } @@ -285,7 +401,13 @@ sub token_expiration { my $rc = $sth->execute($self->{_userid}, $too_old); unless ($rc) { - dbg("bayes: token_expiration: SQL Error: ".$self->{_dbh}->errstr()); + if($self->{bayes_read_write_support}) { + dbg("bayes: token_expiration: SQL Error: ".$self->{_dbh_read}->errstr()); + } + else { + dbg("bayes: token_expiration: SQL Error: ".$self->{_dbh}->errstr()); + } + $deleted = 0; goto token_expiration_final; } @@ -307,10 +429,21 @@ sub token_expiration { WHERE id = ? AND atime < ?"; - $rows = $self->{_dbh}->do($sql, undef, $self->{_userid}, $too_old); + if($self->{bayes_read_write_support}) { + $rows = $self->{_dbh_write}->do($sql, undef, $self->{_userid}, $too_old); + } + else { + $rows = $self->{_dbh}->do($sql, undef, $self->{_userid}, $too_old); + } unless (defined($rows)) { - dbg("bayes: token_expiration: SQL Error: ".$self->{_dbh}->errstr()); + if($self->{bayes_read_write_support}) { + dbg("bayes: token_expiration: SQL Error: ".$self->{_dbh_write}->errstr()); + } + else { + dbg("bayes: token_expiration: SQL Error: ".$self->{_dbh}->errstr()); + } + $deleted = 0; goto token_expiration_final; } @@ -325,12 +458,24 @@ sub token_expiration { last_expire_reduce = ? WHERE id = ?"; - $rows = $self->{_dbh}->do($sql, undef, $deleted, time(), $newdelta, $deleted, $self->{_userid}); + + if($self->{bayes_read_write_support}) { + $rows = $self->{_dbh_write}->do($sql, undef, $deleted, time(), $newdelta, $deleted, $self->{_userid}); + } + else { + $rows = $self->{_dbh}->do($sql, undef, $deleted, time(), $newdelta, $deleted, $self->{_userid}); + } unless (defined($rows)) { # Very bad, we actually deleted the tokens, but were unable to update # bayes_vars with the new data. - dbg("bayes: token_expiration: SQL Error: ".$self->{_dbh}->errstr()); + if($self->{bayes_read_write_support}) { + dbg("bayes: token_expiration: SQL Error: ".$self->{_dbh_write}->errstr()); + } + else { + dbg("bayes: token_expiration: SQL Error: ".$self->{_dbh}->errstr()); + } + dbg("bayes: Bayes database now in inconsistent state, suggest a backup/restore."); goto token_expiration_final; } @@ -344,11 +489,22 @@ sub token_expiration { $sql = "UPDATE bayes_vars SET oldest_token_age = ? WHERE id = ?"; - $rows = $self->{_dbh}->do($sql, undef, $oldest_token_age, $self->{_userid}); + if($self->{bayes_read_write_support}) { + $rows = $self->{_dbh_write}->do($sql, undef, $oldest_token_age, $self->{_userid}); + } + else { + $rows = $self->{_dbh}->do($sql, undef, $oldest_token_age, $self->{_userid}); + } unless (defined($rows)) { # not much more we can do here, so just warn the user and bail out - dbg("bayes: token_expiration: SQL Error: ".$self->{_dbh}->errstr()); + if($self->{bayes_read_write_support}) { + dbg("bayes: token_expiration: SQL Error: ".$self->{_dbh_write}->errstr()); + } + else { + dbg("bayes: token_expiration: SQL Error: ".$self->{_dbh}->errstr()); + } + # yeah I know it's the next thing anyway, but here in case someone adds # additional code below this block goto token_expiration_final; @@ -397,24 +553,47 @@ found. sub seen_get { my ($self, $msgid) = @_; + my $sth; - return undef unless (defined($self->{_dbh})); + if($self->{bayes_read_write_support}) { + return undef unless ( (defined($self->{_dbh_read})) && (defined($self->{_dbh_write})) ); + } + else { + return undef unless (defined($self->{_dbh})); + } my $sql = "SELECT flag FROM bayes_seen WHERE id = ? AND msgid = ?"; - my $sth = $self->{_dbh}->prepare_cached($sql); + if($self->{bayes_read_write_support}) { + $sth = $self->{_dbh_read}->prepare_cached($sql); + } + else { + $sth = $self->{_dbh}->prepare_cached($sql); + } unless (defined($sth)) { - dbg("bayes: seen_get: SQL Error: ".$self->{_dbh}->errstr()); + if($self->{bayes_read_write_support}) { + dbg("bayes: seen_get: SQL Error: ".$self->{_dbh_read}->errstr()); + } + else { + dbg("bayes: seen_get: SQL Error: ".$self->{_dbh}->errstr()); + } + return undef; } my $rc = $sth->execute($self->{_userid}, $msgid); unless ($rc) { - dbg("bayes: seen_get: SQL Error: ".$self->{_dbh}->errstr()); + if($self->{bayes_read_write_support}) { + dbg("bayes: seen_get: SQL Error: ".$self->{_dbh_read}->errstr()); + } + else { + dbg("bayes: seen_get: SQL Error: ".$self->{_dbh}->errstr()); + } + return undef; } @@ -437,21 +616,37 @@ two values 's' for spam and 'h' for ham. sub seen_put { my ($self, $msgid, $flag) = @_; + my $rows; return 0 if (!$msgid); return 0 if (!$flag); - return 0 unless (defined($self->{_dbh})); + + if($self->{bayes_read_write_support}) { + return 0 unless ( defined($self->{_dbh_read}) && defined($self->{_dbh_write}) ); + } + else { + return 0 unless (defined($self->{_dbh})); + } my $sql = "INSERT INTO bayes_seen (id, msgid, flag) VALUES (?,?,?)"; - - my $rows = $self->{_dbh}->do($sql, - undef, - $self->{_userid}, $msgid, $flag); - + + if($self->{bayes_read_write_support}) { + $rows = $self->{_dbh_write}->do($sql, undef, $self->{_userid}, $msgid, $flag); + } + else { + $rows = $self->{_dbh}->do($sql, undef, $self->{_userid}, $msgid, $flag); + } + unless (defined($rows)) { - dbg("bayes: seen_put: SQL Error: ".$self->{_dbh}->errstr()); + if($self->{bayes_read_write_support}) { + dbg("bayes: seen_put: SQL Error: ".$self->{_dbh_write}->errstr()); + } + else { + dbg("bayes: seen_put: SQL Error: ".$self->{_dbh}->errstr()); + } + return 0; } @@ -470,21 +665,35 @@ This method removes C<$msgid> from the d sub seen_delete { my ($self, $msgid) = @_; + my $rows; return 0 if (!$msgid); - return 0 unless (defined($self->{_dbh})); + if($self->{bayes_read_write_support}) { + return 0 unless ( defined($self->{_dbh_read}) && defined($self->{_dbh_write}) ); + } + else { + return 0 unless (defined($self->{_dbh})); + } my $sql = "DELETE FROM bayes_seen WHERE id = ? AND msgid = ?"; - my $rows = $self->{_dbh}->do($sql, - undef, - $self->{_userid}, $msgid); + if($self->{bayes_read_write_support}) { + $rows = $self->{_dbh_write}->do($sql, undef, $self->{_userid}, $msgid); + } + else { + $rows = $self->{_dbh}->do($sql, undef, $self->{_userid}, $msgid); + } unless (defined($rows)) { - dbg("bayes: seen_delete: SQL Error: ".$self->{_dbh}->errstr()); + if($self->{bayes_read_write_support}) { + dbg("bayes: seen_delete: SQL Error: ".$self->{_dbh_write}->errstr()); + } + else { + dbg("bayes: seen_delete: SQL Error: ".$self->{_dbh}->errstr()); + } return 0; } @@ -528,8 +737,14 @@ The values returned in the array are in sub get_storage_variables { my ($self) = @_; my @values; + my $sth; - return (0,0,0,0,0,0,0,0,0,0,0) unless (defined($self->{_dbh})); + if($self->{bayes_read_write_support}) { + return (0,0,0,0,0,0,0,0,0,0,0) unless ( defined($self->{_dbh_read}) && defined($self->{_dbh_write}) ); + } + else { + return (0,0,0,0,0,0,0,0,0,0,0) unless (defined($self->{_dbh})); + } my $sql = "SELECT spam_count, ham_count, token_count, last_expire, last_atime_delta, last_expire_reduce, oldest_token_age, @@ -537,17 +752,35 @@ sub get_storage_variables { FROM bayes_vars WHERE id = ?"; - my $sth = $self->{_dbh}->prepare_cached($sql); + if($self->{bayes_read_write_support}) { + $sth = $self->{_dbh_write}->prepare_cached($sql); + } + else { + $sth = $self->{_dbh}->prepare_cached($sql); + } + unless (defined($sth)) { - dbg("bayes: get_storage_variables: SQL Error: ".$self->{_dbh}->errstr()); + if($self->{bayes_read_write_support}) { + dbg("bayes: get_storage_variables: SQL Error: ".$self->{_dbh_write}->errstr()); + } + else { + dbg("bayes: get_storage_variables: SQL Error: ".$self->{_dbh}->errstr()); + } + return (0,0,0,0,0,0,0,0,0,0,0); } my $rc = $sth->execute($self->{_userid}); unless ($rc) { - dbg("bayes: get_storage_variables: SQL Error: ".$self->{_dbh}->errstr()); + if($self->{bayes_read_write_support}) { + dbg("bayes: get_storage_variables: SQL Error: ".$self->{_dbh_write}->errstr()); + } + else { + dbg("bayes: get_storage_variables: SQL Error: ".$self->{_dbh}->errstr()); + } + return (0,0,0,0,0,0,0,0,0,0,0); } @@ -588,8 +821,14 @@ printing it out according to the passed sub dump_db_toks { my ($self, $template, $regex, @vars) = @_; + my $sth; - return unless (defined($self->{_dbh})); + if($self->{bayes_read_write_support}) { + return unless ( defined($self->{_dbh_read}) && defined($self->{_dbh_write}) ); + } + else { + return unless (defined($self->{_dbh})); + } # 0/0 tokens don't count, but in theory we shouldn't have any # use RPAD to make sure we get trailing spaces in the token value @@ -598,17 +837,35 @@ sub dump_db_toks { WHERE id = ? AND (spam_count > 0 OR ham_count > 0)"; - my $sth = $self->{_dbh}->prepare($sql); + if($self->{bayes_read_write_support}) { + $sth = $self->{_dbh_read}->prepare($sql); + } + else { + $sth = $self->{_dbh}->prepare($sql); + } + unless (defined($sth)) { - dbg("bayes: dump_db_toks: SQL Error: ".$self->{_dbh}->errstr()); + if($self->{bayes_read_write_support}) { + dbg("bayes: dump_db_toks: SQL Error: ".$self->{_dbh_read}->errstr()); + } + else { + dbg("bayes: dump_db_toks: SQL Error: ".$self->{_dbh}->errstr()); + } + return; } my $rc = $sth->execute($self->{_userid}); unless ($rc) { - dbg("bayes: dump_db_toks: SQL Error: ".$self->{_dbh}->errstr()); + if($self->{bayes_read_write_support}) { + dbg("bayes: dump_db_toks: SQL Error: ".$self->{_dbh_read}->errstr()); + } + else { + dbg("bayes: dump_db_toks: SQL Error: ".$self->{_dbh}->errstr()); + } + return; } @@ -638,20 +895,34 @@ This method sets the last expire time. sub set_last_expire { my ($self, $time) = @_; + my $rows; return 0 unless (defined($time)); - return 0 unless (defined($self->{_dbh})); + if($self->{bayes_read_write_support}) { + return 0 unless ( defined($self->{_dbh_read}) && defined($self->{_dbh_write}) ); + } + else { + return 0 unless (defined($self->{_dbh})); + } my $sql = "UPDATE bayes_vars SET last_expire = ? WHERE id = ?"; - my $rows = $self->{_dbh}->do($sql, - undef, - $time, - $self->{_userid}); + if($self->{bayes_read_write_support}) { + $rows = $self->{_dbh_write}->do($sql, undef, $time, $self->{_userid}); + } + else { + $rows = $self->{_dbh}->do($sql, undef, $time, $self->{_userid}); + } unless (defined($rows)) { - dbg("bayes: set_last_expire: SQL Error: ".$self->{_dbh}->errstr()); + if($self->{bayes_read_write_support}) { + dbg("bayes: set_last_expire: SQL Error: ".$self->{_dbh_write}->errstr()); + } + else { + dbg("bayes: set_last_expire: SQL Error: ".$self->{_dbh}->errstr()); + } + return 0; } @@ -673,22 +944,45 @@ value. sub get_running_expire_tok { my ($self) = @_; + my $sth; - return 0 unless (defined($self->{_dbh})); + if($self->{bayes_read_write_support}) { + return 0 unless ( defined($self->{_dbh_read}) && defined($self->{_dbh_write}) ); + } + else { + return 0 unless (defined($self->{_dbh})); + } my $sql = "SELECT max(runtime) from bayes_expire WHERE id = ?"; - my $sth = $self->{_dbh}->prepare_cached($sql); + if($self->{bayes_read_write_support}) { + $sth = $self->{_dbh_read}->prepare_cached($sql); + } + else { + $sth = $self->{_dbh}->prepare_cached($sql); + } unless (defined($sth)) { - dbg("bayes: get_running_expire_tok: SQL Error: ".$self->{_dbh}->errstr()); + if($self->{bayes_read_write_support}) { + dbg("bayes: get_running_expire_tok: SQL Error: ".$self->{_dbh_read}->errstr()); + } + else { + dbg("bayes: get_running_expire_tok: SQL Error: ".$self->{_dbh}->errstr()); + } + return 0; } my $rc = $sth->execute($self->{_userid}); unless ($rc) { - dbg("bayes: get_running_expire_tok: SQL Error: ".$self->{_dbh}->errstr()); + if($self->{bayes_read_write_support}) { + dbg("bayes: get_running_expire_tok: SQL Error: ".$self->{_dbh_read}->errstr()); + } + else { + dbg("bayes: get_running_expire_tok: SQL Error: ".$self->{_dbh}->errstr()); + } + return 0; } @@ -710,18 +1004,34 @@ This method sets the time that an expire sub set_running_expire_tok { my ($self) = @_; + my $rows; - return 0 unless (defined($self->{_dbh})); + if($self->{bayes_read_write_support}) { + return 0 unless ( defined($self->{_dbh_read}) && defined($self->{_dbh_write}) ); + } + else { + return 0 unless (defined($self->{_dbh})); + } my $sql = "INSERT INTO bayes_expire (id,runtime) VALUES (?,?)"; my $time = time(); - my $rows = $self->{_dbh}->do($sql, - undef, - $self->{_userid}, $time); + if($self->{bayes_read_write_support}) { + $rows = $self->{_dbh_write}->do($sql, undef, $self->{_userid}, $time); + } + else { + $rows = $self->{_dbh}->do($sql, undef, $self->{_userid}, $time); + } + unless (defined($rows)) { - dbg("bayes: set_running_expire_tok: SQL Error: ".$self->{_dbh}->errstr()); + if($self->{bayes_read_write_support}) { + dbg("bayes: set_running_expire_tok: SQL Error: ".$self->{_dbh_write}->errstr()); + } + else { + dbg("bayes: set_running_expire_tok: SQL Error: ".$self->{_dbh}->errstr()); + } + return undef; } @@ -740,16 +1050,34 @@ and expire is currently running. sub remove_running_expire_tok { my ($self) = @_; + my $rows; - return 0 unless (defined($self->{_dbh})); + if($self->{bayes_read_write_support}) { + return 0 unless ( defined($self->{_dbh_read}) && defined($self->{_dbh_write}) ); + } + else { + return 0 unless (defined($self->{_dbh})); + } my $sql = "DELETE from bayes_expire WHERE id = ?"; - my $rows = $self->{_dbh}->do($sql, undef, $self->{_userid}); + + if($self->{bayes_read_write_support}) { + $rows = $self->{_dbh_write}->do($sql, undef, $self->{_userid}); + } + else { + $rows = $self->{_dbh}->do($sql, undef, $self->{_userid}); + } unless (defined($rows)) { - dbg("bayes: remove_running_expire_tok: SQL Error: ".$self->{_dbh}->errstr()); + if($self->{bayes_read_write_support}) { + dbg("bayes: remove_running_expire_tok: SQL Error: ".$self->{_dbh_write}->errstr()); + } + else { + dbg("bayes: remove_running_expire_tok: SQL Error: ".$self->{_dbh}->errstr()); + } + return 0; } @@ -768,25 +1096,48 @@ and returns it's spam_count, ham_count a sub tok_get { my ($self, $token) = @_; + my $sth; - return (0,0,0) unless (defined($self->{_dbh})); + if($self->{bayes_read_write_support}) { + return (0,0,0) unless ( defined($self->{_dbh_read}) && defined($self->{_dbh_write}) ); + } + else { + return (0,0,0) unless (defined($self->{_dbh})); + } my $sql = "SELECT spam_count, ham_count, atime FROM bayes_token WHERE id = ? AND token = ?"; - my $sth = $self->{_dbh}->prepare_cached($sql); + if($self->{bayes_read_write_support}) { + $sth = $self->{_dbh_read}->prepare_cached($sql); + } + else { + $sth = $self->{_dbh}->prepare_cached($sql); + } unless (defined($sth)) { - dbg("bayes: tok_get: SQL Error: ".$self->{_dbh}->errstr()); + if($self->{bayes_read_write_support}) { + dbg("bayes: tok_get: SQL Error: ".$self->{_dbh_read}->errstr()); + } + else { + dbg("bayes: tok_get: SQL Error: ".$self->{_dbh}->errstr()); + } + return (0,0,0); } my $rc = $sth->execute($self->{_userid}, $token); unless ($rc) { - dbg("bayes: tok_get: SQL Error: ".$self->{_dbh}->errstr()); + if($self->{bayes_read_write_support}) { + dbg("bayes: tok_get: SQL Error: ".$self->{_dbh_read}->errstr()); + } + else { + dbg("bayes: tok_get: SQL Error: ".$self->{_dbh}->errstr()); + } + return (0,0,0); } @@ -813,8 +1164,14 @@ an array ref of arrays spam count, ham a sub tok_get_all { my ($self, @tokens) = @_; + my $sth; - return [] unless (defined($self->{_dbh})); + if($self->{bayes_read_write_support}) { + return [] unless ( defined($self->{_dbh_read}) && defined($self->{_dbh_write}) ); + } + else { + return [] unless (defined($self->{_dbh})); + } my $token_list_size = scalar(@tokens); dbg("bayes: tok_get_all: Token Count: $token_list_size"); @@ -851,17 +1208,34 @@ sub tok_get_all { my $dynamic_sql = $multi_sql . $in_str; - my $sth = $self->{_dbh}->prepare($dynamic_sql); + if($self->{bayes_read_write_support}) { + $sth = $self->{_dbh_read}->prepare($dynamic_sql); + } + else { + $sth = $self->{_dbh}->prepare($dynamic_sql); + } unless (defined($sth)) { - dbg("bayes: tok_get_all: SQL Error: ".$self->{_dbh}->errstr()); + if($self->{bayes_read_write_support}) { + dbg("bayes: tok_get_all: SQL Error: ".$self->{_dbh_read}->errstr()); + } + else { + dbg("bayes: tok_get_all: SQL Error: ".$self->{_dbh}->errstr()); + } + return []; } my $rc = $sth->execute($self->{_userid}, @bindings); unless ($rc) { - dbg("bayes: tok_get_all: SQL Error: ".$self->{_dbh}->errstr()); + if($self->{bayes_read_write_support}) { + dbg("bayes: tok_get_all: SQL Error: ".$self->{_dbh_read}->errstr()); + } + else { + dbg("bayes: tok_get_all: SQL Error: ".$self->{_dbh}->errstr()); + } + return []; } @@ -881,17 +1255,35 @@ sub tok_get_all { } while ($search_index < $token_list_size) { - my $sth = $self->{_dbh}->prepare($single_sql); + if($self->{bayes_read_write_support}) { + $sth = $self->{_dbh_read}->prepare($single_sql); + } + else { + $sth = $self->{_dbh}->prepare($single_sql); + } + unless (defined($sth)) { - dbg("bayes: tok_get_all: SQL Error: ".$self->{_dbh}->errstr()); + if($self->{bayes_read_write_support}) { + dbg("bayes: tok_get_all: SQL Error: ".$self->{_dbh_read}->errstr()); + } + else { + dbg("bayes: tok_get_all: SQL Error: ".$self->{_dbh}->errstr()); + } + return []; } my $rc = $sth->execute($self->{_userid}, $tokens[$search_index++]); unless ($rc) { - dbg("bayes: tok_get_all: SQL Error: ".$self->{_dbh}->errstr()); + if($self->{bayes_read_write_support}) { + dbg("bayes: tok_get_all: SQL Error: ".$self->{_dbh_read}->errstr()); + } + else { + dbg("bayes: tok_get_all: SQL Error: ".$self->{_dbh}->errstr()); + } + return []; } @@ -946,7 +1338,12 @@ ham learned. sub nspam_nham_get { my ($self) = @_; - return (0,0) unless (defined($self->{_dbh})); + if($self->{bayes_read_write_support}) { + return (0,0) unless ( defined($self->{_dbh_read}) && defined($self->{_dbh_write}) ); + } + else { + return (0,0) unless (defined($self->{_dbh})); + } my @vars = $self->get_storage_variables(); @@ -966,9 +1363,15 @@ This method updates the number of spam a sub nspam_nham_change { my ($self, $num_spam, $num_ham) = @_; - return 0 unless (defined($self->{_dbh})); + if($self->{bayes_read_write_support}) { + return 0 unless ( defined($self->{_dbh_read}) && defined($self->{_dbh_write}) ); + } + else { + return 0 unless (defined($self->{_dbh})); + } my $sql; + my $rows; my @bindings; if ($num_spam != 0 && $num_ham != 0) { @@ -996,12 +1399,21 @@ sub nspam_nham_change { return 1; } - my $rows = $self->{_dbh}->do($sql, - undef, - @bindings); + if($self->{bayes_read_write_support}) { + $rows = $self->{_dbh_write}->do($sql, undef, @bindings); + } + else { + $rows = $self->{_dbh}->do($sql, undef, @bindings); + } unless (defined($rows)) { - dbg("bayes: nspam_nham_change: SQL Error: ".$self->{_dbh}->errstr()); + if($self->{bayes_read_write_support}) { + dbg("bayes: nspam_nham_change: SQL Error: ".$self->{_dbh_write}->errstr()); + } + else { + dbg("bayes: nspam_nham_change: SQL Error: ".$self->{_dbh}->errstr()); + } + return 0; } @@ -1022,8 +1434,14 @@ The assumption is that the token already sub tok_touch { my ($self, $token, $atime) = @_; + my $rows; - return 0 unless (defined($self->{_dbh})); + if($self->{bayes_read_write_support}) { + return 0 unless ( defined($self->{_dbh_read}) && defined($self->{_dbh_write}) ); + } + else { + return 0 unless (defined($self->{_dbh})); + } # shortcut, will only update atime for the token if the atime is less than # what we are updating to @@ -1033,11 +1451,21 @@ sub tok_touch { AND token = ? AND atime < ?"; - my $rows = $self->{_dbh}->do($sql, undef, $atime, $self->{_userid}, - $token, $atime); + if($self->{bayes_read_write_support}) { + $rows = $self->{_dbh_write}->do($sql, undef, $atime, $self->{_userid}, $token, $atime); + } + else { + $rows = $self->{_dbh}->do($sql, undef, $atime, $self->{_userid}, $token, $atime); + } unless (defined($rows)) { - dbg("bayes: tok_touch: SQL Error: ".$self->{_dbh}->errstr()); + if($self->{bayes_read_write_support}) { + dbg("bayes: tok_touch: SQL Error: ".$self->{_dbh_write}->errstr()); + } + else { + dbg("bayes: tok_touch: SQL Error: ".$self->{_dbh}->errstr()); + } + return 0; } @@ -1052,10 +1480,20 @@ sub tok_touch { WHERE id = ? AND newest_token_age < ?"; - $rows = $self->{_dbh}->do($sql, undef, $atime, $self->{_userid}, $atime); + if($self->{bayes_read_write_support}) { + $rows = $self->{_dbh_write}->do($sql, undef, $atime, $self->{_userid}, $atime); + } + else { + $rows = $self->{_dbh}->do($sql, undef, $atime, $self->{_userid}, $atime); + } unless (defined($rows)) { - dbg("bayes: tok_touch: SQL Error: ".$self->{_dbh}->errstr()); + if($self->{bayes_read_write_support}) { + dbg("bayes: tok_touch: SQL Error: ".$self->{_dbh_write}->errstr()); + } + else { + dbg("bayes: tok_touch: SQL Error: ".$self->{_dbh}->errstr()); + } return 0; } @@ -1081,8 +1519,14 @@ do in tok_get_all) sub tok_touch_all { my ($self, $tokens, $atime) = @_; + my $rows; - return 0 unless (defined($self->{_dbh})); + if($self->{bayes_read_write_support}) { + return 0 unless ( defined($self->{_dbh_read}) && defined($self->{_dbh_write}) ); + } + else { + return 0 unless (defined($self->{_dbh})); + } return 1 unless (scalar(@{$tokens})); @@ -1098,10 +1542,21 @@ sub tok_touch_all { $sql .= ") AND atime < ?"; push(@bindings, $atime); - my $rows = $self->{_dbh}->do($sql, undef, @bindings); + if($self->{bayes_read_write_support}) { + $rows = $self->{_dbh_write}->do($sql, undef, @bindings); + } + else { + $rows = $self->{_dbh}->do($sql, undef, @bindings); + } unless (defined($rows)) { - dbg("bayes: tok_touch_all: SQL Error: ".$self->{_dbh}->errstr()); + if($self->{bayes_read_write_support}) { + dbg("bayes: tok_touch_all: SQL Error: ".$self->{_dbh_write}->errstr()); + } + else { + dbg("bayes: tok_touch_all: SQL Error: ".$self->{_dbh}->errstr()); + } + return 0; } @@ -1116,10 +1571,22 @@ sub tok_touch_all { WHERE id = ? AND newest_token_age < ?"; - $rows = $self->{_dbh}->do($sql, undef, $atime, $self->{_userid}, $atime); + + if($self->{bayes_read_write_support}) { + $rows = $self->{_dbh_write}->do($sql, undef, $atime, $self->{_userid}, $atime); + } + else { + $rows = $self->{_dbh}->do($sql, undef, $atime, $self->{_userid}, $atime); + } unless (defined($rows)) { - dbg("bayes: tok_touch_all: SQL Error: ".$self->{_dbh}->errstr()); + if($self->{bayes_read_write_support}) { + dbg("bayes: tok_touch_all: SQL Error: ".$self->{_dbh_write}->errstr()); + } + else { + dbg("bayes: tok_touch_all: SQL Error: ".$self->{_dbh}->errstr()); + } + return 0; } @@ -1138,7 +1605,8 @@ operation. sub cleanup { my ($self) = @_; - + my $toks_deleted; + my $rows; return 1 unless ($self->{needs_cleanup}); @@ -1150,10 +1618,21 @@ sub cleanup { AND spam_count = 0 AND ham_count = 0"; - my $toks_deleted = $self->{_dbh}->do($sql, undef, $self->{_userid}); + if($self->{bayes_read_write_support}) { + $toks_deleted = $self->{_dbh_write}->do($sql, undef, $self->{_userid}); + } + else { + $toks_deleted = $self->{_dbh}->do($sql, undef, $self->{_userid}); + } unless (defined($toks_deleted)) { - dbg("bayes: cleanup: SQL Error: ".$self->{_dbh}->errstr()); + if($self->{bayes_read_write_support}) { + dbg("bayes: cleanup: SQL Error: ".$self->{_dbh_write}->errstr()); + } + else { + dbg("bayes: cleanup: SQL Error: ".$self->{_dbh}->errstr()); + } + return 0; } @@ -1163,10 +1642,22 @@ sub cleanup { $sql = "UPDATE bayes_vars SET token_count = token_count - $toks_deleted WHERE id = ?"; - my $rows = $self->{_dbh}->do($sql, undef, $self->{_userid}); + if($self->{bayes_read_write_support}) { + $rows = $self->{_dbh_write}->do($sql, undef, $self->{_userid}); + } + else { + $rows = $self->{_dbh}->do($sql, undef, $self->{_userid}); + } + unless (defined($rows)) { - dbg("bayes: cleanup: SQL Error: ".$self->{_dbh}->errstr()); + if($self->{bayes_read_write_support}) { + dbg("bayes: cleanup: SQL Error: ".$self->{_dbh_write}->errstr()); + } + else { + dbg("bayes: cleanup: SQL Error: ".$self->{_dbh}->errstr()); + } + return 0; } @@ -1236,32 +1727,69 @@ could causes the database to be inconsis sub clear_database { my ($self) = @_; + my $rows; $self->tie_db_writable(); - return 0 unless (defined($self->{_dbh})); + if($self->{bayes_read_write_support}) { + return 0 unless ( defined($self->{_dbh_read}) && defined($self->{_dbh_write}) ); + } + else { + return 0 unless (defined($self->{_dbh})); + } + + if($self->{bayes_read_write_support}) { + $rows = $self->{_dbh_write}->do("DELETE FROM bayes_vars WHERE id = ?", undef, $self->{_userid}); + } + else { + $rows = $self->{_dbh}->do("DELETE FROM bayes_vars WHERE id = ?", undef, $self->{_userid}); + } - my $rows = $self->{_dbh}->do("DELETE FROM bayes_vars WHERE id = ?", - undef, - $self->{_userid}); unless (defined($rows)) { - dbg("SQL Error removing user (bayes_vars) data: ".$self->{_dbh}->errstr()); + if($self->{bayes_read_write_support}) { + dbg("SQL Error removing user (bayes_vars) data: ".$self->{_dbh_write}->errstr()); + } + else { + dbg("SQL Error removing user (bayes_vars) data: ".$self->{_dbh}->errstr()); + } + return 0; } - $rows = $self->{_dbh}->do("DELETE FROM bayes_seen WHERE id = ?", - undef, - $self->{_userid}); + if($self->{bayes_read_write_support}) { + $rows = $self->{_dbh_write }->do("DELETE FROM bayes_seen WHERE id = ?", undef, $self->{_userid}); + } + else { + $rows = $self->{_dbh}->do("DELETE FROM bayes_seen WHERE id = ?", undef, $self->{_userid}); + } + + unless (defined($rows)) { - dbg("SQL Error removing seen data: ".$self->{_dbh}->errstr()); + if($self->{bayes_read_write_support}) { + dbg("SQL Error removing seen data: ".$self->{_dbh_write}->errstr()); + } + else { + dbg("SQL Error removing seen data: ".$self->{_dbh}->errstr()); + } + return 0; } - $rows = $self->{_dbh}->do("DELETE FROM bayes_token WHERE id = ?", - undef, - $self->{_userid}); + + if($self->{bayes_read_write_support}) { + $rows = $self->{_dbh_write}->do("DELETE FROM bayes_token WHERE id = ?", undef, $self->{_userid}); + } + else { + $rows = $self->{_dbh}->do("DELETE FROM bayes_token WHERE id = ?", undef, $self->{_userid}); + } + unless (defined($rows)) { - dbg("SQL Error removing token data: ".$self->{_dbh}->errstr()); + if($self->{bayes_read_write_support}) { + dbg("SQL Error removing token data: ".$self->{_dbh_write}->errstr()); + } + else { + dbg("SQL Error removing token data: ".$self->{_dbh}->errstr()); + } return 0; } @@ -1279,10 +1807,16 @@ This method will dump the users database sub backup_database { my ($self) = @_; + my $sth; return 0 unless ($self->tie_db_readonly()); - return 0 unless (defined($self->{_dbh})); + if($self->{bayes_read_write_support}) { + return 0 unless ( defined($self->{_dbh_read}) && defined($self->{_dbh_write}) ); + } + else { + return 0 unless (defined($self->{_dbh})); + } my @vars = $self->get_storage_variables(); @@ -1302,17 +1836,34 @@ sub backup_database { FROM bayes_seen WHERE id = ?"; - my $sth = $self->{_dbh}->prepare($token_sql); + if($self->{bayes_read_write_support}) { + $sth = $self->{_dbh_write}->prepare($token_sql); + } + else { + $sth = $self->{_dbh}->prepare($token_sql); + } unless (defined ($sth)) { - dbg("bayes: backup_database: SQL Error: ".$self->{_dbh}->errstr()); + if($self->{bayes_read_write_support}) { + dbg("bayes: backup_database: SQL Error: ".$self->{_dbh_read}->errstr()); + } + else { + dbg("bayes: backup_database: SQL Error: ".$self->{_dbh}->errstr()); + } + return 0; } my $rc = $sth->execute($self->{_userid}); unless ($rc) { - dbg("bayes: backup_database: SQL Error: ".$self->{_dbh}->errstr()); + if($self->{bayes_read_write_support}) { + dbg("bayes: backup_database: SQL Error: ".$self->{_dbh_read}->errstr()); + } + else { + dbg("bayes: backup_database: SQL Error: ".$self->{_dbh}->errstr()); + } + return 0; } @@ -1323,17 +1874,34 @@ sub backup_database { $sth->finish(); - $sth = $self->{_dbh}->prepare($seen_sql); + if($self->{bayes_read_write_support}) { + $sth = $self->{_dbh_read}->prepare($seen_sql); + } + else { + $sth = $self->{_dbh}->prepare($seen_sql); + } unless (defined ($sth)) { - dbg("bayes: backup_database: SQL Error: ".$self->{_dbh}->errstr()); + if($self->{bayes_read_write_support}) { + dbg("bayes: backup_database: SQL Error: ".$self->{_dbh_read}->errstr()); + } + else { + dbg("bayes: backup_database: SQL Error: ".$self->{_dbh}->errstr()); + } + return 0; } $rc = $sth->execute($self->{_userid}); unless ($rc) { - dbg("bayes: backup_database: SQL Error: ".$self->{_dbh}->errstr()); + if($self->{bayes_read_write_support}) { + dbg("bayes: backup_database: SQL Error: ".$self->{_dbh_read}->errstr()); + } + else { + dbg("bayes: backup_database: SQL Error: ".$self->{_dbh}->errstr()); + } + return 0; } @@ -1370,8 +1938,13 @@ sub restore_database { return 0 unless ($self->tie_db_writable()); - return 0 unless (defined($self->{_dbh})); - + if($self->{bayes_read_write_support}) { + return 0 unless ( defined($self->{_dbh_read}) && defined($self->{_dbh_write}) ); + } + else { + return 0 unless (defined($self->{_dbh})); + } + # This is the critical phase (moving sql around), so don't allow it # to be interrupted. local $SIG{'INT'} = 'IGNORE'; @@ -1578,24 +2151,47 @@ tables. sub _get_db_version { my ($self) = @_; + my $sth; - return 0 unless (defined($self->{_dbh})); + if($self->{bayes_read_write_support}) { + return 0 unless ( defined($self->{_dbh_read}) && defined($self->{_dbh_write}) ); + } + else { + return 0 unless (defined($self->{_dbh})); + } return ($self->{_db_version_cache}) if (defined($self->{_db_version_cache})); my $sql = "SELECT value FROM bayes_global_vars WHERE variable = 'VERSION'"; - my $sth = $self->{_dbh}->prepare_cached($sql); + if($self->{bayes_read_write_support}) { + $sth = $self->{_dbh_read}->prepare_cached($sql); + } + else { + $sth = $self->{_dbh}->prepare_cached($sql); + } unless (defined($sth)) { - dbg("bayes: _get_db_version: SQL Error: ".$self->{_dbh}->errstr()); + if($self->{bayes_read_write_support}) { + dbg("bayes: _get_db_version: SQL Error: ".$self->{_dbh_read}->errstr()); + } + else { + dbg("bayes: _get_db_version: SQL Error: ".$self->{_dbh}->errstr()); + } + return 0; } my $rc = $sth->execute(); unless ($rc) { - dbg("bayes: _get_db_version: SQL Error: ".$self->{_dbh}->errstr()); + if($self->{bayes_read_write_support}) { + dbg("bayes: _get_db_version: SQL Error: ".$self->{_dbh_read}->errstr()); + } + else { + dbg("bayes: _get_db_version: SQL Error: ".$self->{_dbh}->errstr()); + } + return 0; } @@ -1620,24 +2216,48 @@ initialized. If not then it will perform sub _initialize_db { my ($self) = @_; + my $sthselect; + my $rows; - return 0 unless (defined($self->{_dbh})); + if($self->{bayes_read_write_support}) { + return 0 unless ( defined($self->{_dbh_read}) && defined($self->{_dbh_write}) ); + } + else { + return 0 unless (defined($self->{_dbh})); + } return 0 if (!$self->{_username}); my $sqlselect = "SELECT id FROM bayes_vars WHERE username = ?"; - my $sthselect = $self->{_dbh}->prepare_cached($sqlselect); + if($self->{bayes_read_write_support}) { + $sthselect = $self->{_dbh_read}->prepare_cached($sqlselect); + } + else { + $sthselect = $self->{_dbh}->prepare_cached($sqlselect); + } unless (defined($sthselect)) { - dbg("bayes: _initialize_db: SQL Error: ".$self->{_dbh}->errstr()); + if($self->{bayes_read_write_support}) { + dbg("bayes: _initialize_db: SQL Error: ".$self->{_dbh_read}->errstr()); + } + else { + dbg("bayes: _initialize_db: SQL Error: ".$self->{_dbh}->errstr()); + } + return 0; } my $rc = $sthselect->execute($self->{_username}); unless ($rc) { - dbg("bayes: _initialize_db: SQL Error: ".$self->{_dbh}->errstr()); + if($self->{bayes_read_write_support}) { + dbg("bayes: _initialize_db: SQL Error: ".$self->{_dbh_read}->errstr()); + } + else { + dbg("bayes: _initialize_db: SQL Error: ".$self->{_dbh}->errstr()); + } + return 0; } @@ -1653,11 +2273,21 @@ sub _initialize_db { # For now let the database setup the other variables as defaults my $sqlinsert = "INSERT INTO bayes_vars (username) VALUES (?)"; - my $rows = $self->{_dbh}->do($sqlinsert, - undef, - $self->{_username}); + if($self->{bayes_read_write_support}) { + $rows = $self->{_dbh_write}->do($sqlinsert, undef, $self->{_username}); + } + else { + $rows = $self->{_dbh}->do($sqlinsert, undef, $self->{_username}); + } + unless (defined($rows)) { - dbg("bayes: _initialize_db: SQL Error: ".$self->{_dbh}->errstr()); + if($self->{bayes_read_write_support}) { + dbg("bayes: _initialize_db: SQL Error: ".$self->{_dbh_write}->errstr()); + } + else { + dbg("bayes: _initialize_db: SQL Error: ".$self->{_dbh}->errstr()); + } + return 0; } @@ -1668,7 +2298,13 @@ sub _initialize_db { $rc = $sthselect->execute($self->{_username}); unless ($rc) { - dbg("bayes: _initialize_db: SQL Error: ".$self->{_dbh}->errstr()); + if($self->{bayes_read_write_support}) { + dbg("bayes: _initialize_db: SQL Error: ".$self->{_dbh_read}->errstr()); + } + else { + dbg("bayes: _initialize_db: SQL Error: ".$self->{_dbh}->errstr()); + } + return 0; } @@ -1700,8 +2336,16 @@ the database. sub _put_token { my ($self, $token, $spam_count, $ham_count, $atime) = @_; + my $sth; + my $rows; + - return 0 unless (defined($self->{_dbh})); + if($self->{bayes_read_write_support}) { + return 0 unless ( defined($self->{_dbh_read}) && defined($self->{_dbh_write}) ); + } + else { + return 0 unless (defined($self->{_dbh})); + } $spam_count ||= 0; $ham_count ||= 0; @@ -1724,10 +2368,21 @@ sub _put_token { (id, token, spam_count, ham_count, atime) VALUES (?,?,?,?,?)"; - my $sth = $self->{_dbh}->prepare_cached($sql); + if($self->{bayes_read_write_support}) { + $sth = $self->{_dbh_write}->prepare_cached($sql); + } + else { + $sth = $self->{_dbh}->prepare_cached($sql); + } unless (defined($sth)) { - dbg("bayes: _put_token: SQL Error: ".$self->{_dbh}->errstr()); + if($self->{bayes_read_write_support}) { + dbg("bayes: _put_token: SQL Error: ".$self->{_dbh_write}->errstr()); + } + else { + dbg("bayes: _put_token: SQL Error: ".$self->{_dbh}->errstr()); + } + return 0; } @@ -1738,7 +2393,13 @@ sub _put_token { $atime); unless ($rc) { - dbg("bayes: _put_token: SQL Error: ".$self->{_dbh}->errstr()); + if($self->{bayes_read_write_support}) { + dbg("bayes: _put_token: SQL Error: ".$self->{_dbh_write}->errstr()); + } + else { + dbg("bayes: _put_token: SQL Error: ".$self->{_dbh}->errstr()); + } + return 0; } @@ -1747,20 +2408,42 @@ sub _put_token { $sql = "UPDATE bayes_vars SET token_count = token_count + 1 WHERE id = ?"; - my $rows = $self->{_dbh}->do($sql, undef, $self->{_userid}); + if($self->{bayes_read_write_support}) { + $rows = $self->{_dbh_write}->do($sql, undef, $self->{_userid}); + } + else { + $rows = $self->{_dbh}->do($sql, undef, $self->{_userid}); + } unless (defined($rows)) { - dbg("bayes: _put_token: SQL Error: ".$self->{_dbh}->errstr()); + if($self->{bayes_read_write_support}) { + dbg("bayes: _put_token: SQL Error: ".$self->{_dbh_write}->errstr()); + } + else { + dbg("bayes: _put_token: SQL Error: ".$self->{_dbh}->errstr()); + } + return 0; } $sql = "UPDATE bayes_vars SET newest_token_age = ? WHERE id = ? AND newest_token_age < ?"; - $rows = $self->{_dbh}->do($sql, undef, $atime, $self->{_userid}, $atime); + if($self->{bayes_read_write_support}) { + $rows = $self->{_dbh_write}->do($sql, undef, $atime, $self->{_userid}, $atime); + } + else { + $rows = $self->{_dbh}->do($sql, undef, $atime, $self->{_userid}, $atime); + } unless (defined($rows)) { - dbg("bayes: _put_token: SQL Error: ".$self->{_dbh}->errstr()); + if($self->{bayes_read_write_support}) { + dbg("bayes: _put_token: SQL Error: ".$self->{_dbh_write}->errstr()); + } + else { + dbg("bayes: _put_token: SQL Error: ".$self->{_dbh}->errstr()); + } + return 0; } @@ -1770,10 +2453,21 @@ sub _put_token { $sql = "UPDATE bayes_vars SET oldest_token_age = ? WHERE id = ? AND oldest_token_age > ?"; - $rows = $self->{_dbh}->do($sql, undef, $atime, $self->{_userid}, $atime); + if($self->{bayes_read_write_support}) { + $rows = $self->{_dbh_write}->do($sql, undef, $atime, $self->{_userid}, $atime); + } + else { + $rows = $self->{_dbh}->do($sql, undef, $atime, $self->{_userid}, $atime); + } unless (defined($rows)) { - dbg("bayes: _put_token: SQL Error: ".$self->{_dbh}->errstr()); + if($self->{bayes_read_write_support}) { + dbg("bayes: _put_token: SQL Error: ".$self->{_dbh_write}->errstr()); + } + else { + dbg("bayes: _put_token: SQL Error: ".$self->{_dbh}->errstr()); + } + return 0; } } @@ -1824,11 +2518,22 @@ sub _put_token { AND spam_count + ? >= 0"; @args = ($spam_count, $self->{_userid}, $token, $spam_count); } - - my $rows = $self->{_dbh}->do($sql, undef, @args); + + if($self->{bayes_read_write_support}) { + $rows = $self->{_dbh_write}->do($sql, undef, @args); + } + else { + $rows = $self->{_dbh}->do($sql, undef, @args); + } unless (defined($rows)) { - dbg("bayes: _put_token: SQL Error: ".$self->{_dbh}->errstr()); + if($self->{bayes_read_write_support}) { + dbg("bayes: _put_token: SQL Error: ".$self->{_dbh_write}->errstr()); + } + else { + dbg("bayes: _put_token: SQL Error: ".$self->{_dbh}->errstr()); + } + return 0; } } @@ -1854,11 +2559,22 @@ sub _put_token { AND ham_count + ? >= 0"; @args = ($ham_count, $self->{_userid}, $token, $ham_count); } - - my $rows = $self->{_dbh}->do($sql, undef, @args); + + if($self->{bayes_read_write_support}) { + $rows = $self->{_dbh_write}->do($sql, undef, @args); + } + else { + $rows = $self->{_dbh}->do($sql, undef, @args); + } unless (defined($rows)) { - dbg("bayes: _put_token: SQL Error: ".$self->{_dbh}->errstr()); + if($self->{bayes_read_write_support}) { + dbg("bayes: _put_token: SQL Error: ".$self->{_dbh_write}->errstr()); + } + else { + dbg("bayes: _put_token: SQL Error: ".$self->{_dbh}->errstr()); + } + return 0; } } @@ -1869,11 +2585,23 @@ sub _put_token { # only updated the atime if it was > the previous value my $sql = "UPDATE bayes_vars SET newest_token_age = ? WHERE id = ? AND newest_token_age < ?"; + + if($self->{bayes_read_write_support}) { + $rows = $self->{_dbh_write}->do($sql, undef, $atime, $self->{_userid}, $atime); + } + else { + $rows = $self->{_dbh}->do($sql, undef, $atime, $self->{_userid}, $atime); + } - my $rows = $self->{_dbh}->do($sql, undef, $atime, $self->{_userid}, $atime); unless (defined($rows)) { - dbg("bayes: _put_token: SQL Error: ".$self->{_dbh}->errstr()); + if($self->{bayes_read_write_support}) { + dbg("bayes: _put_token: SQL Error: ".$self->{_dbh_write}->errstr()); + } + else { + dbg("bayes: _put_token: SQL Error: ".$self->{_dbh}->errstr()); + } + return 0; } } @@ -1897,23 +2625,46 @@ only be called at expire time. sub _get_oldest_token_age { my ($self) = @_; + my $sth; - return 0 unless (defined($self->{_dbh})); + if($self->{bayes_read_write_support}) { + return 0 unless ( defined($self->{_dbh_read}) && defined($self->{_dbh_write}) ); + } + else { + return 0 unless (defined($self->{_dbh})); + } my $sql = "SELECT min(atime) FROM bayes_token WHERE id = ?"; - my $sth = $self->{_dbh}->prepare_cached($sql); + if($self->{bayes_read_write_support}) { + $sth = $self->{_dbh_read}->prepare_cached($sql); + } + else { + $sth = $self->{_dbh}->prepare_cached($sql); + } unless (defined($sth)) { - dbg("bayes: _get_oldest_token_age: SQL Error: ".$self->{_dbh}->errstr()); + if($self->{bayes_read_write_support}) { + dbg("bayes: _get_oldest_token_age: SQL Error: ".$self->{_dbh_read}->errstr()); + } + else { + dbg("bayes: _get_oldest_token_age: SQL Error: ".$self->{_dbh}->errstr()); + } + return 0; } my $rc = $sth->execute($self->{_userid}); unless ($rc) { - dbg("bayes: _get_oldest_token_age: SQL Error: ".$self->{_dbh}->errstr()); + if($self->{bayes_read_write_support}) { + dbg("bayes: _get_oldest_token_age: SQL Error: ".$self->{_dbh_read}->errstr()); + } + else { + dbg("bayes: _get_oldest_token_age: SQL Error: ".$self->{_dbh}->errstr()); + } + return 0; } @@ -1924,7 +2675,6 @@ sub _get_oldest_token_age { return $atime; } - =head2 _get_num_hapaxes private instance (Integer) _get_num_hapaxes () @@ -1937,25 +2687,48 @@ the token database for a user. sub _get_num_hapaxes { my ($self) = @_; + my $sth; - return 0 unless (defined($self->{_dbh})); - + if($self->{bayes_read_write_support}) { + return 0 unless ( defined($self->{_dbh_read}) && defined($self->{_dbh_write}) ); + } + else { + return 0 unless (defined($self->{_dbh})); + } + my $sql = "SELECT count(*) FROM bayes_token WHERE id = ? AND spam_count + ham_count = 1"; - my $sth = $self->{_dbh}->prepare_cached($sql); + if($self->{bayes_read_write_support}) { + $sth = $self->{_dbh_read}->prepare_cached($sql); + } + else { + $sth = $self->{_dbh}->prepare_cached($sql); + } unless (defined($sth)) { - dbg("bayes: _get_num_hapaxes: SQL Error: ".$self->{_dbh}->errstr()); + if($self->{bayes_read_write_support}) { + dbg("bayes: _get_num_hapaxes: SQL Error: ".$self->{_dbh_read}->errstr()); + } + else { + dbg("bayes: _get_num_hapaxes: SQL Error: ".$self->{_dbh}->errstr()); + } + return 0; } my $rc = $sth->execute($self->{_userid}); unless ($rc) { - dbg("bayes: _get_num_hapaxes: SQL Error: ".$self->{_dbh}->errstr()); + if($self->{bayes_read_write_support}) { + dbg("bayes: _get_num_hapaxes: SQL Error: ".$self->{_dbh_read}->errstr()); + } + else { + dbg("bayes: _get_num_hapaxes: SQL Error: ".$self->{_dbh}->errstr()); + } + return 0; } @@ -1979,8 +2752,14 @@ ham_count < 8) in the token database for sub _get_num_lowfreq { my ($self) = @_; + my $sth; - return 0 unless (defined($self->{_dbh})); + if($self->{bayes_read_write_support}) { + return 0 unless ( defined($self->{_dbh_read}) && defined($self->{_dbh_write}) ); + } + else { + return 0 unless (defined($self->{_dbh})); + } my $sql = "SELECT count(*) FROM bayes_token @@ -1989,17 +2768,34 @@ sub _get_num_lowfreq { AND (ham_count >= 0 AND ham_count < 8) AND spam_count + ham_count != 1"; - my $sth = $self->{_dbh}->prepare_cached($sql); + if($self->{bayes_read_write_support}) { + $sth = $self->{_dbh_read}->prepare_cached($sql); + } + else { + $sth = $self->{_dbh}->prepare_cached($sql); + } unless (defined($sth)) { - dbg("bayes: _get_num_lowfreq: SQL Error: ".$self->{_dbh}->errstr()); + if($self->{bayes_read_write_support}) { + dbg("bayes: _get_num_lowfreq: SQL Error: ".$self->{_dbh_read}->errstr()); + } + else { + dbg("bayes: _get_num_lowfreq: SQL Error: ".$self->{_dbh}->errstr()); + } + return 0; } my $rc = $sth->execute($self->{_userid}); unless ($rc) { - dbg("bayes: _get_num_lowfreq: SQL Error: ".$self->{_dbh}->errstr()); + if($self->{bayes_read_write_support}) { + dbg("bayes: _get_num_lowfreq: SQL Error: ".$self->{_dbh_read}->errstr()); + } + else { + dbg("bayes: _get_num_lowfreq: SQL Error: ".$self->{_dbh}->errstr()); + } + return 0; } @@ -2014,7 +2810,13 @@ sub db_readable { my($self) = @_; # if there's a database handle, we can read... - return defined $self->{_dbh}; + if($self->{bayes_read_write_support}) { + return ( defined($self->{_dbh_read}) && defined($self->{_dbh_write}) ); + } + else { + return defined($self->{_dbh}); + } + } sub db_writable { @@ -2022,7 +2824,12 @@ sub db_writable { # since in the SA SQL code, there is no difference between R/O and # R/W access, we just care if we have DB access. - return defined $self->{_dbh}; + if($self->{bayes_read_write_support}) { + return ( defined($self->{_dbh_read}) && defined($self->{_dbh_write}) ); + } + else { + return defined($self->{_dbh}); + } } sub dbg { Mail::SpamAssassin::dbg (@_); } --- /home/as/old/SpamAssassin/Conf.pm Mon Jun 6 03:31:23 2005 +++ /home/as/old/SpamAssassin/Conf.pm Wed Aug 3 09:54:56 2005 @@ -2778,6 +2778,80 @@ specification (see Mail::SpamAssassin::B =item bayes_sql_dsn DBI::databasetype:databasename:hostname:port +To use 2 different Databases (for example : You have mysql replication. Than you can write to the master and read from the slave + +in general localhost.) you can enable bayes_read_write_support. + +New Bayes Vars here : + +bayes_read_write_support BOOL + +bayes_sql_dsn_read + +bayes_sql_username_read + +bayes_sql_password_read + +bayes_sql_dsn_write + +bayes_sql_username_write + +bayes_sql_password_write + +=cut + + push (@cmds, { + setting => 'bayes_read_write_support', + is_admin => 1, + default => 0, + type => $CONF_TYPE_BOOL + }); + + push (@cmds, { + setting => 'bayes_sql_dsn_read', + is_admin => 1, + default => '', + type => $CONF_TYPE_STRING + }); + + push (@cmds, { + setting => 'bayes_sql_username_read', + is_admin => 1, + default => '', + type => $CONF_TYPE_STRING + }); + + push (@cmds, { + setting => 'bayes_sql_password_read', + is_admin => 1, + default => '', + type => $CONF_TYPE_STRING + }); + + push (@cmds, { + setting => 'bayes_sql_dsn_write', + is_admin => 1, + default => '', + type => $CONF_TYPE_STRING + }); + + push (@cmds, { + setting => 'bayes_sql_username_write', + is_admin => 1, + default => '', + type => $CONF_TYPE_STRING + }); + + push (@cmds, { + setting => 'bayes_sql_password_write', + is_admin => 1, + default => '', + type => $CONF_TYPE_STRING + }); + + +=item bayes_sql_dsn DBI::databasetype:databasename:hostname:port + Used for BayesStore::SQL storage implementation. This option give the connect string used to connect to the SQL based Bayes storage. @@ -2876,7 +2950,7 @@ The password for the database username, =item user_scores_sql_custom_query query This option gives you the ability to create a custom SQL query to -retrieve user scores and preferences. In order to work correctly your +Retrieve user scores and preferences. In order to work correctly your query should return two values, the preference name and value, in that order. In addition, there are several "variables" that you can use as part of your query, these variables will be substituted for the @@ -2982,6 +3056,65 @@ The table user auto-whitelists are store setting => 'user_awl_sql_table', is_admin => 1, default => 'awl', + type => $CONF_TYPE_STRING + }); + +=item user_awl_sql_read_write_support + +This patch make it possible to read from one host and write to another. +Very usefull for mysql replication. + +if user_awl_sql_read_write_support is true, you have following extra options : + +user_awl_dsn_read +user_awl_sql_username_read (optional) +user_awl_sql_password_read (optional) +user_awl_dsn_write +user_awl_sql_username_write (optional) +user_awl_sql_password_write (optional) + +=cut + + push (@cmds, { + setting => 'user_awl_read_write_support', + is_admin => 1, + default => 0, + type => $CONF_TYPE_BOOL + }); + + push (@cmds, { + setting => 'user_awl_dsn_read', + is_admin => 1, + type => $CONF_TYPE_STRING + }); + + push (@cmds, { + setting => 'user_awl_sql_username_read', + is_admin => 1, + type => $CONF_TYPE_STRING + }); + + push (@cmds, { + setting => 'user_awl_sql_password_read', + is_admin => 1, + type => $CONF_TYPE_STRING + }); + + push (@cmds, { + setting => 'user_awl_dsn_write', + is_admin => 1, + type => $CONF_TYPE_STRING + }); + + push (@cmds, { + setting => 'user_awl_sql_username_write', + is_admin => 1, + type => $CONF_TYPE_STRING + }); + + push (@cmds, { + setting => 'user_awl_sql_password_write', + is_admin => 1, type => $CONF_TYPE_STRING }); --- /home/as/old/SpamAssassin/SQLBasedAddrList.pm Sat Mar 19 01:06:27 2005 +++ /home/as/old/SpamAssassin/SQLBasedAddrList.pm Mon Aug 1 17:44:08 2005 @@ -51,6 +51,8 @@ CREATE TABLE awl ( You're table definition may change depending on which database driver you choose. There is a config option to override the table name. +The username and passwords for read /write support are optional. +If they are not set user_awl_sql_username and user_awl_sql_password is used. This module introduces several new config variables: @@ -62,6 +64,20 @@ user_awl_sql_password user_awl_sql_table +user_awl_read_write_support + +user_awl_dsn_read + +user_awl_sql_username_read (optional) + +user_awl_sql_password_read (optional) + +user_awl_dsn_write + +user_awl_sql_username_write (optional) + +user_awl_sql_password_write (optional) + see C for more information. @@ -116,30 +132,85 @@ sub new_checker { my $class = $self->{class}; - if (!$main->{conf}->{user_awl_dsn} || - !$main->{conf}->{user_awl_sql_table}) { - dbg("auto-whitelist (sql-based): invalid config"); + #checks if user_awl_read_write_support is enabled + my $user_awl_read_write_support=$main->{conf}->{user_awl_read_write_support}; + + if ($user_awl_read_write_support) { + if (!$main->{conf}->{user_awl_dsn_read} || !$main->{conf}->{user_awl_dsn_write} || !$main->{conf}->{user_awl_sql_table} || $main->{conf}->{user_awl_dsn}) { + dbg("auto-whitelist (sql-based): invalid config 1"); return undef; + } + } + else { + if (!$main->{conf}->{user_awl_dsn} || + !$main->{conf}->{user_awl_sql_table}) { + dbg("auto-whitelist (sql-based): invalid config 2"); + return undef; + } } + #original my $dsn = $main->{conf}->{user_awl_dsn}; my $dbuser = $main->{conf}->{user_awl_sql_username}; my $dbpass = $main->{conf}->{user_awl_sql_password}; - my $dbh = DBI->connect($dsn, $dbuser, $dbpass, {'PrintError' => 0}); + #new + my $dsn_read = $main->{conf}->{user_awl_dsn_read}; + my $dbuser_read = $main->{conf}->{user_awl_sql_username_read}; + my $dbpass_read = $main->{conf}->{user_awl_sql_password_read}; + + my $dsn_write = $main->{conf}->{user_awl_dsn_write}; + my $dbuser_write = $main->{conf}->{user_awl_sql_username_write}; + my $dbpass_write = $main->{conf}->{user_awl_sql_password_write}; + + if( !$user_awl_read_write_support ) { + my $dbh = DBI->connect($dsn, $dbuser, $dbpass, {'PrintError' => 0}); + if(!$dbh) { + dbg("auto-whitelist (sql-based): Unable to Connect to DB"); + return undef; + } - if(!$dbh) { - dbg("auto-whitelist (sql-based): Unable to Connect to DB"); - return undef; + $self = { 'main' => $main, + 'dsn' => $dsn, + 'dbh' => $dbh, + 'tablename' => $main->{conf}->{user_awl_sql_table}, + }; + dbg("SQL Based AWL: Connected to $dsn"); } + else + { + #use user_awl_sql_password and username if only one pass for both DBs (read/write) is used + if($dbpass && $dbuser && !$dbuser_read && !$dbpass_read && !$dbuser_write && !$dbpass_write) { + $dbuser_read=$dbuser; + $dbpass_read=$dbpass; + $dbuser_write=$dbuser; + $dbpass_write=$dbpass; + } - $self = { 'main' => $main, - 'dsn' => $dsn, - 'dbh' => $dbh, - 'tablename' => $main->{conf}->{user_awl_sql_table}, - }; + my $dbh_to_read = DBI->connect($dsn_read, $dbuser_read, $dbpass_read, {'PrintError' => 0}); + if(!$dbh_to_read){ + dbg("auto-whitelist (sql-based): Unable to Connect to read DB"); + return undef; + } + + my $dbh_to_write = DBI->connect($dsn_write, $dbuser_write, $dbpass_write, {'PrintError' => 0}); + if(!$dbh_to_write){ + dbg("auto-whitelist (sql-based): Unable to Connect to write DB"); + return undef; + } + + $self = { 'main' => $main, + 'dsn_read' => $dsn_read, + 'dsn_write' => $dsn_write, + 'tablename' => $main->{conf}->{user_awl_sql_table}, + 'dbh_to_read' => $dbh_to_read, + 'dbh_to_write'=> $dbh_to_write, + 'user_awl_read_write_support' => $user_awl_read_write_support, + }; - dbg("SQL Based AWL: Connected to $dsn"); + dbg("SQL Based AWL: Connected readable to $dsn_read"); + dbg("SQL Based AWL: Connected writeable to $dsn_write"); + } return bless ($self, $class); } @@ -161,6 +232,8 @@ otherwise it is set to 0. sub get_addr_entry { my ($self, $addr) = @_; + my $sth; + my $err; my $entry = { addr => $addr, exists_p => 0, @@ -176,11 +249,24 @@ sub get_addr_entry { my $sql = "SELECT count, totscore FROM $self->{tablename} WHERE username = ? AND email = ? AND ip = ?"; - my $sth = $self->{dbh}->prepare($sql); + + #new + if ( $self->{user_awl_read_write_support} ) { + $sth = $self->{dbh_to_read}->prepare($sql); + } + else { + $sth = $self->{dbh}->prepare($sql); + } + my $rc = $sth->execute($username, $email, $ip); if (!$rc) { # there was an error, but try to go on - my $err = $self->{dbh}->errstr; + if( $self->{user_awl_read_write_support} ) { + $err = $self->{dbh_to_read}->errstr; + } + else { + $err = $self->{dbh}->errstr; + } dbg("auto-whitelist (sql-based) get_addr_entry: SQL Error: $err"); $entry->{count} = 0; $entry->{totscore} = 0; @@ -222,7 +308,8 @@ itself a SQL database. sub add_score { my($self, $entry, $score) = @_; - + my $sth; + my $err; return if (!$entry->{addr}); my ($email, $ip) = $self->_unpack_addr($entry->{addr}); @@ -239,11 +326,23 @@ sub add_score { totscore = totscore + ? WHERE username = ? AND email = ? AND ip = ?"; - my $sth = $self->{dbh}->prepare($sql); + #new + if( $self->{user_awl_read_write_support} ) { + $sth = $self->{dbh_to_write}->prepare($sql); + } + else { + $sth = $self->{dbh}->prepare($sql); + } + my $rc = $sth->execute($score, $username, $email, $ip); if (!$rc) { - my $err = $self->{dbh}->errstr; + if( $self->{user_awl_read_write_support} ) { + $err = $self->{dbh_to_write}->errstr; + } + else { + $err = $self->{dbh}->errstr; + } dbg("auto-whitelist (sql-based) add_score: SQL Error: $err"); } else { @@ -253,10 +352,22 @@ sub add_score { } else { # no entry yet, so insert a new entry my $sql = "INSERT INTO $self->{tablename} (username,email,ip,count,totscore) VALUES (?,?,?,?,?)"; - my $sth = $self->{dbh}->prepare($sql); + #new + if( $self->{user_awl_read_write_support} ) { + $sth = $self->{dbh_to_write}->prepare($sql); + } + else { + $sth = $self->{dbh}->prepare($sql); + } + my $rc = $sth->execute($username,$email,$ip,1,$score); if (!$rc) { - my $err = $self->{dbh}->errstr; + if( $self->{user_awl_read_write_support} ) { + $err = $self->{dbh_to_write}->errstr; + } + else { + $err = $self->{dbh}->errstr; + } dbg("auto-whitelist (sql-based) add_score: SQL Error: $err"); } $entry->{exists_p} = 1; @@ -280,7 +391,8 @@ perl-IP entries for this address as well sub remove_entry { my ($self, $entry) = @_; - + my $sth; + my $err; my ($email, $ip) = $self->_unpack_addr($entry->{addr}); return unless ($email && $ip); @@ -304,11 +416,22 @@ sub remove_entry { dbg("auto-whitelist (sql-based) remove_entry: Removing single entry matching ".$entry->{addr}); } - my $sth = $self->{dbh}->prepare($sql); + #new + if( $self->{user_awl_read_write_support} ) { + $sth = $self->{dbh_to_write}->prepare($sql); + } + else { + $sth = $self->{dbh}->prepare($sql); + } my $rc = $sth->execute(@args); if (!$rc) { - my $err = $self->{dbh}->errstr; + if( $self->{user_awl_read_write_support} ) { + $err = $self->{dbh_to_write}->errstr; + } + else { + $err = $self->{dbh}->errstr; + } dbg("auto-whitelist (sql-based) remove_entry: SQL Error: $err"); } else { @@ -329,8 +452,17 @@ This method provides the necessary clean sub finish { my ($self) = @_; + if( $self->{user_awl_read_write_support} ) { + $self->{dbh_to_write}->disconnect(); + $self->{dbh_to_read}->disconnect(); + dbg("auto-whitelist (sql-based) finish: Disconnected from " . $self->{dsn_write}); + dbg("auto-whitelist (sql-based) finish: Disconnected from " . $self->{dsn_read}); + } + else { dbg("auto-whitelist (sql-based) finish: Disconnected from " . $self->{dsn}); $self->{dbh}->disconnect(); + } + } =head2 _unpack_addr