You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@spamassassin.apache.org by pa...@apache.org on 2004/09/16 07:47:56 UTC
svn commit: rev 46160 - spamassassin/trunk/lib/Mail/SpamAssassin/BayesStore
Author: parker
Date: Wed Sep 15 22:47:55 2004
New Revision: 46160
Modified:
spamassassin/trunk/lib/Mail/SpamAssassin/BayesStore/SQL.pm
Log:
Bug 3380: Give BayesSQL smarts enough to have readable/writable connections. Also, do not create a new user entry when tying read only.
Modified: spamassassin/trunk/lib/Mail/SpamAssassin/BayesStore/SQL.pm
==============================================================================
--- spamassassin/trunk/lib/Mail/SpamAssassin/BayesStore/SQL.pm (original)
+++ spamassassin/trunk/lib/Mail/SpamAssassin/BayesStore/SQL.pm Wed Sep 15 22:47:55 2004
@@ -63,6 +63,7 @@
my $self = $class->SUPER::new(@_);
$self->{supported_db_version} = 3;
+ $self->{db_writable_p} = 0;
if (!$self->{bayes}->{conf}->{bayes_sql_dsn}) {
dbg("bayes: invalid config, must set bayes_sql_dsn config variable.\n");
@@ -110,9 +111,37 @@
sub tie_db_readonly {
my ($self) = @_;
- my $ret = $self->tie_db_writable();
+ return 0 unless (HAS_DBI);
+
+ if ($self->{_dbh}) {
+ # already connected, but connection has now become readonly
+ $self->{db_writable_p} = 0;
+ return 1;
+ }
+
+ my $main = $self->{bayes}->{main};
+
+ $self->read_db_configs();
+
+ return 0 unless ($self->_connect_db());
+
+ my $db_ver = $self->_get_db_version();
+ $self->{db_version} = $db_ver;
+ dbg("bayes: found bayes db version ".$self->{db_version});
+
+ if ( $db_ver != $self->DB_VERSION ) {
+ warn("bayes: Database version $db_ver is different than we understand (".$self->DB_VERSION."), aborting!");
+ $self->untie_db();
+ return 0;
+ }
- return $ret;
+ unless ($self->_initialize_db()) {
+ dbg("bayes: database entry for ".$self->{_username}." not found");
+ $self->untie_db();
+ return 0;
+ }
+
+ return 1;
}
=head2 tie_db_writable
@@ -131,27 +160,18 @@
return 0 unless (HAS_DBI);
- return 1 if ($self->{_dbh}); # already connected
+ if ($self->{_dbh}) {
+ # already connected, but now it will be writable
+ $self->{db_writable_p} = 1;
+ return 1;
+ }
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 (!$dbh) {
- dbg("bayes: Unable to connect to database: ".DBI->errstr());
- return 0;
- }
- else {
- dbg("bayes: Database connection established");
- }
+ return 0 unless ($self->_connect_db());
- $self->{_dbh} = $dbh;
-
- # If the DB version is one we don't understand, abort!
my $db_ver = $self->_get_db_version();
$self->{db_version} = $db_ver;
dbg("bayes: found bayes db version ".$self->{db_version});
@@ -162,12 +182,14 @@
return 0;
}
- unless ($self->_initialize_db()) {
+ unless ($self->_initialize_db(1)) {
dbg("bayes: unable to initialize database for ".$self->{_username}." user, aborting!");
$self->untie_db();
return 0;
}
+ $self->{db_writable_p} = 1;
+
return 1;
}
@@ -186,6 +208,8 @@
return unless (defined($self->{_dbh}));
+ $self->{db_writable_p} = 0;
+
$self->{_dbh}->disconnect();
$self->{_dbh} = undef;
}
@@ -1237,7 +1261,18 @@
sub clear_database {
my ($self) = @_;
- $self->tie_db_writable();
+ # We want to open readonly first, because if they don't already have
+ # a db entry, we want to avoid creating one, just to delete it in a few secs
+ if ($self->tie_db_readonly()) {
+ # Ok, they must have had a db entry, so now make the connection writable
+ $self->tie_db_writable();
+ }
+ else {
+ # If we were unable to create a readonly connection then they must
+ # not have a db entry, so no need to clear.
+ # But it should be considered a success.
+ return 1;
+ }
return 0 unless (defined($self->{_dbh}));
@@ -1368,10 +1403,6 @@
return 0;
}
- return 0 unless ($self->tie_db_writable());
-
- 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';
@@ -1379,12 +1410,10 @@
local $SIG{'TERM'} = 'IGNORE';
unless ($self->clear_database()) {
- dbg("bayes: Database now in inconsistent state for ".$self->{_username});
return 0;
}
- unless ($self->_initialize_db()) {
- dbg("bayes: Unable to re-initialize database for ".$self->{_username});
+ unless ($self->tie_db_writable()) {
return 0;
}
@@ -1525,19 +1554,17 @@
}
if ($error_p) {
- dbg("bayes: Error(s) while attempting to load $filename, correct and Re-Run");
-
+ dbg("bayes: Error(s) while attempting to load $filename, clearing database, correct and Re-Run");
$self->clear_database();
-
- dbg("bayes: Database now in inconsistent state for ".$self->{_username});
return 0;
}
- unless ($self->nspam_nham_change($num_spam, $num_ham)) {
- dbg("bayes: Error updating num spam and num ham.");
- $self->clear_database();
- dbg("bayes; Database now in inconsistent state for ".$self->{_username});
- return 0;
+ if ($num_spam || $num_ham) {
+ unless ($self->nspam_nham_change($num_spam, $num_ham)) {
+ dbg("bayes: Error updating num spam and num ham, clearing database.");
+ $self->clear_database();
+ return 0;
+ }
}
dbg("bayes: Parsed $line_count lines.");
@@ -1548,9 +1575,72 @@
return 1;
}
+=head2 db_readable
+
+public instance (Boolean) db_readable()
+
+Description:
+This method returns a boolean value indicating if the database is in a
+readable state.
+
+=cut
+
+sub db_readable {
+ my ($self) = @_;
+
+ # if there's a database handle, we can read...
+ return defined $self->{_dbh};
+}
+
+=head2 db_writable
+
+public instance (Boolean) db_writeable()
+
+Description:
+This method returns a boolean value indicating if the database is in a
+writable state.
+
+=cut
+
+sub db_writable {
+ my ($self) = @_;
+
+ return (defined $self->{_dbh} && $self->{db_writable_p})
+}
=head1 Private Methods
+=head2 _connect_db
+
+private instance (Boolean) _connect_db ()
+
+Description:
+This method connects to the SQL database.
+
+=cut
+
+sub _connect_db {
+ my ($self) = @_;
+
+ $self->{_dbh} = undef;
+
+ # Turn off PrintError and explicitly set AutoCommit to off
+ my $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;
+ }
+ else {
+ dbg("bayes: Database connection established");
+ }
+
+ $self->{_dbh} = $dbh;
+
+ return 1;
+}
+
=head2 _get_db_version
private instance (Integer) _get_db_version ()
@@ -1592,7 +1682,7 @@
return $version;
}
-
+
=head2 _initialize_db
private instance (Boolean) _initialize_db ()
@@ -1604,7 +1694,7 @@
=cut
sub _initialize_db {
- my ($self) = @_;
+ my ($self, $create_entry_p) = @_;
return 0 unless (defined($self->{_dbh}));
@@ -1635,6 +1725,9 @@
return 1;
}
+ # Do not create an entry for this user unless we were specifically asked to
+ return 0 unless ($create_entry_p);
+
# For now let the database setup the other variables as defaults
my $sqlinsert = "INSERT INTO bayes_vars (username) VALUES (?)";
@@ -1993,21 +2086,6 @@
$sth->finish();
return $num_lowfreq;
-}
-
-sub db_readable {
- my($self) = @_;
-
- # if there's a database handle, we can read...
- return defined $self->{_dbh};
-}
-
-sub db_writable {
- my($self) = @_;
-
- # 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};
}
sub dbg { Mail::SpamAssassin::dbg (@_); }