our
@ISA
=
qw( Mail::SpamAssassin::BayesStore::SQL )
;
sub
token_expiration {
my
(
$self
,
$opts
,
$newdelta
,
@vars
) =
@_
;
my
$num_hapaxes
;
my
$num_lowfreq
;
my
$deleted
;
my
$too_old
=
$vars
[10] -
$newdelta
;
my
$sql
= "UPDATE bayes_token SET atime = ?
WHERE id = ?
AND atime > ?";
my
$rows
=
$self
->{_dbh}->
do
(
$sql
,
undef
,
$vars
[10],
$self
->{_userid},
$vars
[10]);
unless
(
defined
(
$rows
)) {
dbg(
"bayes: token_expiration: SQL error: "
.
$self
->{_dbh}->errstr());
$deleted
= 0;
$self
->{_dbh}->rollback();
goto
token_expiration_final;
}
$sql
= "SELECT count(token) FROM bayes_token
WHERE id = ?
AND atime < ?";
my
$sth
=
$self
->{_dbh}->prepare_cached(
$sql
);
unless
(
defined
(
$sth
)) {
dbg(
"bayes: token_expiration: SQL error: "
.
$self
->{_dbh}->errstr());
$deleted
= 0;
$self
->{_dbh}->rollback();
goto
token_expiration_final;
}
my
$rc
=
$sth
->execute(
$self
->{_userid},
$too_old
);
unless
(
$rc
) {
dbg(
"bayes: token_expiration: SQL error: "
.
$self
->{_dbh}->errstr());
$deleted
= 0;
$self
->{_dbh}->rollback();
goto
token_expiration_final;
}
my
(
$count
) =
$sth
->fetchrow_array();
$sth
->finish();
if
(
$vars
[3] -
$count
< 100000) {
dbg(
"bayes: token expiration would expire too many tokens, aborting"
);
$deleted
= 0;
$newdelta
= 0;
}
else
{
$sql
= "DELETE from bayes_token
WHERE id = ?
AND atime < ?";
$rows
=
$self
->{_dbh}->
do
(
$sql
,
undef
,
$self
->{_userid},
$too_old
);
unless
(
defined
(
$rows
)) {
dbg(
"bayes: token_expiration: SQL error: "
.
$self
->{_dbh}->errstr());
$deleted
= 0;
$self
->{_dbh}->rollback();
goto
token_expiration_final;
}
$deleted
= (
$rows
eq
'0E0'
) ? 0 :
$rows
;
}
$sql
= "UPDATE bayes_vars SET token_count = token_count - ?,
last_expire = ?,
last_atime_delta = ?,
last_expire_reduce = ?,
oldest_token_age = (SELECT min(atime)
FROM bayes_token
WHERE id = ?)
WHERE id = ?";
$rows
=
$self
->{_dbh}->
do
(
$sql
,
undef
,
$deleted
,
time
(),
$newdelta
,
$deleted
,
$self
->{_userid},
$self
->{_userid});
unless
(
defined
(
$rows
)) {
dbg(
"bayes: token_expiration: SQL error: "
.
$self
->{_dbh}->errstr());
$self
->{_dbh}->rollback();
$deleted
= 0;
goto
token_expiration_final;
}
$self
->{_dbh}->commit();
token_expiration_final:
my
$kept
=
$vars
[3] -
$deleted
;
$num_hapaxes
=
$self
->_get_num_hapaxes()
if
(
$opts
->{verbose});
$num_lowfreq
=
$self
->_get_num_lowfreq()
if
(
$opts
->{verbose});
$self
->untie_db();
return
(
$kept
,
$deleted
,
$num_hapaxes
,
$num_lowfreq
);
}
sub
seen_put {
my
(
$self
,
$msgid
,
$flag
) =
@_
;
return
0
if
(!
$msgid
);
return
0
if
(!
$flag
);
return
0
unless
(
defined
(
$self
->{_dbh}));
my
$sql
= "INSERT INTO bayes_seen (id, msgid, flag)
VALUES (?,?,?)
ON DUPLICATE KEY UPDATE flag=VALUES(flag)";
my
$rows
=
$self
->{_dbh}->
do
(
$sql
,
undef
,
$self
->{_userid},
$msgid
,
$flag
);
unless
(
defined
(
$rows
)) {
dbg(
"bayes: seen_put: SQL error: "
.
$self
->{_dbh}->errstr());
$self
->{_dbh}->rollback();
return
0;
}
dbg(
"bayes: seen ($msgid) put"
);
$self
->{_dbh}->commit();
return
1;
}
sub
seen_delete {
my
(
$self
,
$msgid
) =
@_
;
return
0
if
(!
$msgid
);
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
);
unless
(
defined
(
$rows
)) {
dbg(
"bayes: seen_delete: SQL error: "
.
$self
->{_dbh}->errstr());
$self
->{_dbh}->rollback();
return
0;
}
$self
->{_dbh}->commit();
return
1;
}
sub
set_last_expire {
my
(
$self
,
$time
) =
@_
;
return
0
unless
(
defined
(
$time
));
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});
unless
(
defined
(
$rows
)) {
dbg(
"bayes: set_last_expire: SQL error: "
.
$self
->{_dbh}->errstr());
$self
->{_dbh}->rollback();
return
0;
}
$self
->{_dbh}->commit();
return
1;
}
sub
set_running_expire_tok {
my
(
$self
) =
@_
;
return
0
unless
(
defined
(
$self
->{_dbh}));
my
$sql
= "INSERT INTO bayes_expire (id,runtime) VALUES (?,?)
ON DUPLICATE KEY UPDATE runtime=VALUES(runtime)";
my
$time
=
time
();
my
$rows
=
$self
->{_dbh}->
do
(
$sql
,
undef
,
$self
->{_userid},
$time
);
unless
(
defined
(
$rows
)) {
dbg(
"bayes: set_running_expire_tok: SQL error: "
.
$self
->{_dbh}->errstr());
$self
->{_dbh}->rollback();
return
;
}
$self
->{_dbh}->commit();
return
$time
;
}
sub
remove_running_expire_tok {
my
(
$self
) =
@_
;
return
0
unless
(
defined
(
$self
->{_dbh}));
my
$sql
= "DELETE from bayes_expire
WHERE id = ?";
my
$rows
=
$self
->{_dbh}->
do
(
$sql
,
undef
,
$self
->{_userid});
unless
(
defined
(
$rows
)) {
dbg(
"bayes: remove_running_expire_tok: SQL error: "
.
$self
->{_dbh}->errstr());
$self
->{_dbh}->rollback();
return
0;
}
$self
->{_dbh}->commit();
return
1;
}
sub
tok_get {
my
(
$self
,
$token
) =
@_
;
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
);
unless
(
defined
(
$sth
)) {
dbg(
"bayes: tok_get: SQL error: "
.
$self
->{_dbh}->errstr());
$self
->{_dbh}->rollback();
return
(0,0,0);
}
$sth
->bind_param(1,
$self
->{_userid});
$sth
->bind_param(2,
$token
, DBI::SQL_BINARY);
my
$rc
=
$sth
->execute();
unless
(
$rc
) {
dbg(
"bayes: tok_get: SQL error: "
.
$self
->{_dbh}->errstr());
$self
->{_dbh}->rollback();
return
(0,0,0);
}
my
(
$spam_count
,
$ham_count
,
$atime
) =
$sth
->fetchrow_array();
$sth
->finish();
$spam_count
= 0
if
(!
$spam_count
||
$spam_count
< 0);
$ham_count
= 0
if
(!
$ham_count
||
$ham_count
< 0);
$atime
= 0
if
(!
$atime
);
return
(
$spam_count
,
$ham_count
,
$atime
)
}
sub
tok_get_all {
my
(
$self
,
@tokens
) =
@_
;
return
[]
unless
(
defined
(
$self
->{_dbh}));
my
$token_list_size
=
scalar
(
@tokens
);
dbg(
"bayes: tok_get_all: token count: $token_list_size"
);
my
@tok_results
;
my
$search_index
= 0;
my
$results_index
= 0;
my
$bunch_end
;
my
$token_select
=
$self
->_token_select_string();
my
$multi_sql
= "SELECT
$token_select
, spam_count, ham_count, atime
FROM bayes_token
WHERE id = ?
AND token IN ";
while
(
$token_list_size
>
$search_index
) {
my
$bunch_size
;
if
(
$token_list_size
-
$search_index
> 100) {
$bunch_size
= 100;
}
else
{
$bunch_size
=
$token_list_size
-
$search_index
;
}
while
(
$token_list_size
-
$search_index
>=
$bunch_size
) {
my
@tok
;
my
$in_str
=
'('
;
$bunch_end
=
$search_index
+
$bunch_size
;
for
( ;
$search_index
<
$bunch_end
;
$search_index
++) {
$in_str
.=
'?,'
;
push
(
@tok
,
$tokens
[
$search_index
]);
}
chop
$in_str
;
$in_str
.=
')'
;
my
$dynamic_sql
=
$multi_sql
.
$in_str
;
my
$sth
=
$self
->{_dbh}->prepare(
$dynamic_sql
);
unless
(
defined
(
$sth
)) {
dbg(
"bayes: tok_get_all: SQL error: "
.
$self
->{_dbh}->errstr());
$self
->{_dbh}->rollback();
return
[];
}
my
$idx
= 0;
$sth
->bind_param(++
$idx
,
$self
->{_userid});
$sth
->bind_param(++
$idx
,
$_
, DBI::SQL_BINARY)
foreach
(
@tok
);
my
$rc
=
$sth
->execute();
unless
(
$rc
) {
dbg(
"bayes: tok_get_all: SQL error: "
.
$self
->{_dbh}->errstr());
$self
->{_dbh}->rollback();
return
[];
}
my
$results
=
$sth
->fetchall_arrayref();
$sth
->finish();
foreach
my
$result
(@{
$results
}) {
$result
->[1] = 0
if
(!
$result
->[1] ||
$result
->[1] < 0);
$result
->[2] = 0
if
(!
$result
->[2] ||
$result
->[2] < 0);
$result
->[3] = 0
if
(!
$result
->[3]);
$tok_results
[
$results_index
++] =
$result
;
}
}
}
return
\
@tok_results
;
}
sub
nspam_nham_change {
my
(
$self
,
$num_spam
,
$num_ham
) =
@_
;
return
0
unless
(
defined
(
$self
->{_dbh}));
my
$sql
;
my
@bindings
;
if
(
$num_spam
!= 0 &&
$num_ham
!= 0) {
$sql
= "UPDATE bayes_vars
SET spam_count = spam_count + ?,
ham_count = ham_count + ?
WHERE id = ?";
@bindings
= (
$num_spam
,
$num_ham
,
$self
->{_userid});
}
elsif
(
$num_spam
!= 0) {
$sql
= "UPDATE bayes_vars
SET spam_count = spam_count + ?
WHERE id = ?";
@bindings
= (
$num_spam
,
$self
->{_userid});
}
elsif
(
$num_ham
!= 0) {
$sql
= "UPDATE bayes_vars
SET ham_count = ham_count + ?
WHERE id = ?";
@bindings
= (
$num_ham
,
$self
->{_userid});
}
else
{
dbg(
"bayes: nspam_nham_change: Called with no delta on spam or ham"
);
return
1;
}
my
$rows
=
$self
->{_dbh}->
do
(
$sql
,
undef
,
@bindings
);
unless
(
defined
(
$rows
)) {
dbg(
"bayes: nspam_nham_change: SQL error: "
.
$self
->{_dbh}->errstr());
$self
->{_dbh}->rollback();
return
0;
}
$self
->{_dbh}->commit();
return
1;
}
sub
tok_touch {
my
(
$self
,
$token
,
$atime
) =
@_
;
return
0
unless
(
defined
(
$self
->{_dbh}));
my
$sql
= "UPDATE bayes_token
SET atime = ?
WHERE id = ?
AND token = ?
AND atime < ?";
my
$sth
=
$self
->{_dbh}->prepare_cached(
$sql
);
unless
(
defined
(
$sth
)) {
dbg(
"bayes: tok_touch: SQL error: "
.
$self
->{_dbh}->errstr());
$self
->{_dbh}->rollback();
return
0;
}
$sth
->bind_param(1,
$atime
);
$sth
->bind_param(2,
$self
->{_userid});
$sth
->bind_param(3,
$token
, DBI::SQL_BINARY);
$sth
->bind_param(4,
$atime
);
my
$rows
=
$sth
->execute();
unless
(
$rows
) {
dbg(
"bayes: tok_touch: SQL error: "
.
$self
->{_dbh}->errstr());
$self
->{_dbh}->rollback();
return
0;
}
return
1
if
(
$rows
eq
'0E0'
);
$sql
= "UPDATE bayes_vars
SET newest_token_age = ?
WHERE id = ?
AND newest_token_age < ?";
$rows
=
$self
->{_dbh}->
do
(
$sql
,
undef
,
$atime
,
$self
->{_userid},
$atime
);
unless
(
defined
(
$rows
)) {
dbg(
"bayes: tok_touch: SQL error: "
.
$self
->{_dbh}->errstr());
$self
->{_dbh}->rollback();
return
0;
}
$self
->{_dbh}->commit();
return
1;
}
sub
tok_touch_all {
my
(
$self
,
$tokens
,
$atime
) =
@_
;
return
0
unless
(
defined
(
$self
->{_dbh}));
return
1
unless
(
scalar
(@{
$tokens
}));
my
$sql
=
"UPDATE bayes_token SET atime = ? WHERE id = ? AND token IN ("
;
foreach
(@{
$tokens
}) {
$sql
.=
"?,"
;
}
chop
(
$sql
);
$sql
.=
") AND atime < ?"
;
my
$sth
=
$self
->{_dbh}->prepare(
$sql
);
unless
(
defined
(
$sth
)) {
dbg(
"bayes: tok_touch_all: SQL error: "
.
$self
->{_dbh}->errstr());
$self
->{_dbh}->rollback();
return
[];
}
my
$idx
= 0;
$sth
->bind_param(++
$idx
,
$atime
);
$sth
->bind_param(++
$idx
,
$self
->{_userid});
$sth
->bind_param(++
$idx
,
$_
, DBI::SQL_BINARY)
foreach
(@{
$tokens
});
$sth
->bind_param(++
$idx
,
$atime
);
my
$rows
=
$sth
->execute();
unless
(
$rows
) {
dbg(
"bayes: tok_touch_all: SQL error: "
.
$self
->{_dbh}->errstr());
$self
->{_dbh}->rollback();
return
0;
}
return
1
if
(
$rows
eq
'0E0'
);
$sql
= "UPDATE bayes_vars
SET newest_token_age = ?
WHERE id = ?
AND newest_token_age < ?";
$rows
=
$self
->{_dbh}->
do
(
$sql
,
undef
,
$atime
,
$self
->{_userid},
$atime
);
unless
(
defined
(
$rows
)) {
dbg(
"bayes: tok_touch_all: SQL error: "
.
$self
->{_dbh}->errstr());
$self
->{_dbh}->rollback();
return
0;
}
$self
->{_dbh}->commit();
return
1;
}
sub
cleanup {
my
(
$self
) =
@_
;
return
1
unless
(
$self
->{needs_cleanup});
$self
->{needs_cleanup} = 0;
my
$sql
= "DELETE from bayes_token
WHERE id = ?
AND spam_count <= 0
AND ham_count <= 0";
my
$toks_deleted
=
$self
->{_dbh}->
do
(
$sql
,
undef
,
$self
->{_userid});
unless
(
defined
(
$toks_deleted
)) {
dbg(
"bayes: cleanup: SQL error: "
.
$self
->{_dbh}->errstr());
$self
->{_dbh}->rollback();
return
0;
}
return
1
if
(
$toks_deleted
eq
'0E0'
);
$sql
=
"UPDATE bayes_vars SET token_count = token_count - ? WHERE id = ?"
;
my
$rows
=
$self
->{_dbh}->
do
(
$sql
,
undef
,
$toks_deleted
,
$self
->{_userid});
unless
(
defined
(
$rows
)) {
dbg(
"bayes: cleanup: SQL error: "
.
$self
->{_dbh}->errstr());
$self
->{_dbh}->rollback();
return
0;
}
$self
->{_dbh}->commit();
return
1;
}
sub
clear_database {
my
(
$self
) =
@_
;
if
(
$self
->tie_db_readonly()) {
$self
->tie_db_writable();
}
else
{
return
1;
}
return
0
unless
(
defined
(
$self
->{_dbh}));
my
$rows
=
$self
->{_dbh}->
do
(
"DELETE FROM bayes_vars WHERE id = ?"
,
undef
,
$self
->{_userid});
unless
(
defined
(
$rows
)) {
dbg(
"bayes: SQL error removing user (bayes_vars) data: "
.
$self
->{_dbh}->errstr());
$self
->{_dbh}->rollback();
return
0;
}
$rows
=
$self
->{_dbh}->
do
(
"DELETE FROM bayes_seen WHERE id = ?"
,
undef
,
$self
->{_userid});
unless
(
defined
(
$rows
)) {
dbg(
"bayes: SQL error removing seen data: "
.
$self
->{_dbh}->errstr());
$self
->{_dbh}->rollback();
return
0;
}
$rows
=
$self
->{_dbh}->
do
(
"DELETE FROM bayes_token WHERE id = ?"
,
undef
,
$self
->{_userid});
unless
(
defined
(
$rows
)) {
dbg(
"bayes: SQL error removing token data: "
.
$self
->{_dbh}->errstr());
$self
->{_dbh}->rollback();
return
0;
}
$self
->{_dbh}->commit();
return
1;
}
sub
_connect_db {
my
(
$self
) =
@_
;
$self
->{_dbh} =
undef
;
my
$dbh
= DBI->
connect
(
$self
->{_dsn},
$self
->{_dbuser},
$self
->{_dbpass},
{
'PrintError'
=> 0,
'AutoCommit'
=> 0});
if
(!
$dbh
) {
dbg(
"bayes: unable to connect to database: "
.DBI->errstr());
return
0;
}
else
{
dbg(
"bayes: database connection established"
);
}
$self
->{_dbh} =
$dbh
;
return
1;
}
sub
_initialize_db {
my
(
$self
,
$create_entry_p
) =
@_
;
return
0
if
!
defined
$self
->{_dbh};
return
0
if
!
defined
$self
->{_username} ||
$self
->{_username} eq
''
;
if
(
$self
->{bayes}->{conf}->{bayes_sql_username_authorized}) {
my
$services
= {
'bayessql'
=> 0 };
$self
->{bayes}->{main}->call_plugins(
"services_allowed_for_username"
,
{
services
=>
$services
,
username
=>
$self
->{_username},
conf
=>
$self
->{bayes}->{conf},
});
unless
(
$services
->{bayessql}) {
dbg(
"bayes: username not allowed by services_allowed_for_username plugin call"
);
return
0;
}
}
my
$sqlselect
=
"SELECT id FROM bayes_vars WHERE username = ?"
;
my
$sthselect
=
$self
->{_dbh}->prepare_cached(
$sqlselect
);
unless
(
defined
(
$sthselect
)) {
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());
return
0;
}
my
(
$id
) =
$sthselect
->fetchrow_array();
if
(
$id
) {
$self
->{_userid} =
$id
;
dbg(
"bayes: Using userid: "
.
$self
->{_userid});
$sthselect
->finish();
return
1;
}
return
0
unless
(
$create_entry_p
);
my
$sqlinsert
=
"INSERT INTO bayes_vars (username) VALUES (?)"
;
my
$rows
=
$self
->{_dbh}->
do
(
$sqlinsert
,
undef
,
$self
->{_username});
unless
(
defined
(
$rows
)) {
dbg(
"bayes: _initialize_db: SQL error: "
.
$self
->{_dbh}->errstr());
$self
->{_dbh}->rollback();
return
0;
}
$id
=
$self
->{_dsn} =~ /^DBI:MariaDB/i ?
$self
->{_dbh}->{
'mariadb_insertid'
} :
$self
->{_dbh}->{
'mysql_insertid'
};
$self
->{_dbh}->commit();
if
(
$id
) {
$self
->{_userid} =
$id
;
dbg(
"bayes: using userid: "
.
$self
->{_userid});
return
1;
}
return
1;
}
sub
_put_token {
my
(
$self
,
$token
,
$spam_count
,
$ham_count
,
$atime
) =
@_
;
return
0
unless
(
defined
(
$self
->{_dbh}));
$spam_count
||= 0;
$ham_count
||= 0;
if
(
$spam_count
== 0 &&
$ham_count
== 0) {
return
1;
}
if
(
$spam_count
< 0 ||
$ham_count
< 0) {
$self
->{needs_cleanup} = 1;
my
$sql
= "UPDATE bayes_token SET spam_count = GREATEST(spam_count + ?, 0),
ham_count = GREATEST(ham_count + ?, 0)
WHERE id = ?
AND token = ?";
my
$sth
=
$self
->{_dbh}->prepare_cached(
$sql
);
unless
(
defined
(
$sth
)) {
dbg(
"bayes: _put_token: SQL error: "
.
$self
->{_dbh}->errstr());
$self
->{_dbh}->rollback();
return
0;
}
$sth
->bind_param(1,
$spam_count
);
$sth
->bind_param(2,
$ham_count
);
$sth
->bind_param(3,
$self
->{_userid});
$sth
->bind_param(4,
$token
, DBI::SQL_BINARY);
my
$rc
=
$sth
->execute();
unless
(
$rc
) {
dbg(
"bayes: _put_token: SQL error: "
.
$self
->{_dbh}->errstr());
$self
->{_dbh}->rollback();
return
0;
}
}
else
{
my
$sql
= "INSERT INTO bayes_token
(id, token, spam_count, ham_count, atime)
VALUES (?,?,?,?,?)
ON DUPLICATE KEY UPDATE spam_count = GREATEST(spam_count + ?, 0),
ham_count = GREATEST(ham_count + ?, 0),
atime = GREATEST(atime, ?)";
my
$sth
=
$self
->{_dbh}->prepare_cached(
$sql
);
unless
(
defined
(
$sth
)) {
dbg(
"bayes: _put_token: SQL error: "
.
$self
->{_dbh}->errstr());
$self
->{_dbh}->rollback();
return
0;
}
$sth
->bind_param(1,
$self
->{_userid});
$sth
->bind_param(2,
$token
, DBI::SQL_BINARY);
$sth
->bind_param(3,
$spam_count
);
$sth
->bind_param(4,
$ham_count
);
$sth
->bind_param(5,
$atime
);
$sth
->bind_param(6,
$spam_count
);
$sth
->bind_param(7,
$ham_count
);
$sth
->bind_param(8,
$atime
);
my
$rc
=
$sth
->execute();
unless
(
$rc
) {
dbg(
"bayes: _put_token: SQL error: "
.
$self
->{_dbh}->errstr());
$self
->{_dbh}->rollback();
return
0;
}
my
$num_rows
=
$rc
;
$sth
->finish();
if
(
$num_rows
== 1 ||
$num_rows
== 2 ||
$num_rows
== 3) {
my
$token_count_update
=
''
;
$token_count_update
=
"token_count = token_count + 1,"
if
$num_rows
== 1;
$sql
= "UPDATE bayes_vars SET
$token_count_update
newest_token_age = GREATEST(newest_token_age, ?),
oldest_token_age = LEAST(oldest_token_age, ?)
WHERE id = ?";
$sth
=
$self
->{_dbh}->prepare_cached(
$sql
);
unless
(
defined
(
$sth
)) {
dbg(
"bayes: _put_token: SQL error: "
.
$self
->{_dbh}->errstr());
$self
->{_dbh}->rollback();
return
0;
}
my
$rc
=
$sth
->execute(
$atime
,
$atime
,
$self
->{_userid});
unless
(
$rc
) {
dbg(
"bayes: _put_token: SQL error: "
.
$self
->{_dbh}->errstr());
$self
->{_dbh}->rollback();
return
0;
}
}
else
{
my
$token_displ
=
$token
;
$token_displ
=~ s/(.)/
sprintf
(
'%02x'
,
ord
($1))/egs;
dbg(
"bayes: _put_token: Updated an unexpected number of rows: %s, "
.
"id: %s, token (hex): %s"
,
$num_rows
,
$self
->{_userid},
$token_displ
);
$self
->{_dbh}->rollback();
return
0;
}
}
$self
->{_dbh}->commit();
return
1;
}
sub
_put_tokens {
my
(
$self
,
$tokens
,
$spam_count
,
$ham_count
,
$atime
) =
@_
;
return
0
unless
(
defined
(
$self
->{_dbh}));
$spam_count
||= 0;
$ham_count
||= 0;
if
(
$spam_count
== 0 &&
$ham_count
== 0) {
return
1;
}
if
(
$spam_count
< 0 ||
$ham_count
< 0) {
$self
->{needs_cleanup} = 1;
my
$sql
= "UPDATE bayes_token SET spam_count = GREATEST(spam_count + ?, 0),
ham_count = GREATEST(ham_count + ?, 0)
WHERE id = ?
AND token = ?";
my
$sth
=
$self
->{_dbh}->prepare_cached(
$sql
);
unless
(
defined
(
$sth
)) {
dbg(
"bayes: _put_tokens: SQL error: "
.
$self
->{_dbh}->errstr());
$self
->{_dbh}->rollback();
return
0;
}
$sth
->bind_param(1,
$spam_count
);
$sth
->bind_param(2,
$ham_count
);
$sth
->bind_param(3,
$self
->{_userid});
my
$error_p
= 0;
foreach
my
$token
(
keys
%{
$tokens
}) {
$sth
->bind_param(4,
$token
, DBI::SQL_BINARY);
my
$rc
=
$sth
->execute();
unless
(
$rc
) {
dbg(
"bayes: _put_tokens: SQL error: "
.
$self
->{_dbh}->errstr());
$error_p
= 1;
}
}
$sth
->finish();
if
(
$error_p
) {
$self
->{_dbh}->rollback();
return
0;
}
}
else
{
my
$sql
= "INSERT INTO bayes_token
(id, token, spam_count, ham_count, atime)
VALUES (?,?,?,?,?)
ON DUPLICATE KEY UPDATE spam_count = GREATEST(spam_count + ?, 0),
ham_count = GREATEST(ham_count + ?, 0),
atime = GREATEST(atime, ?)";
my
$sth
=
$self
->{_dbh}->prepare_cached(
$sql
);
unless
(
defined
(
$sth
)) {
dbg(
"bayes: _put_tokens: SQL error: "
.
$self
->{_dbh}->errstr());
$self
->{_dbh}->rollback();
return
0;
}
$sth
->bind_param(1,
$self
->{_userid});
$sth
->bind_param(3,
$spam_count
);
$sth
->bind_param(4,
$ham_count
);
$sth
->bind_param(5,
$atime
);
$sth
->bind_param(6,
$spam_count
);
$sth
->bind_param(7,
$ham_count
);
$sth
->bind_param(8,
$atime
);
my
$error_p
= 0;
my
$new_tokens
= 0;
my
$need_atime_update_p
= 0;
foreach
my
$token
(
keys
%{
$tokens
}) {
$sth
->bind_param(2,
$token
, DBI::SQL_BINARY);
my
$rc
=
$sth
->execute();
if
(!
$rc
) {
dbg(
"bayes: _put_tokens: SQL error: "
.
$self
->{_dbh}->errstr());
$error_p
= 1;
}
else
{
my
$num_rows
=
$rc
;
if
(
$num_rows
== 1) {
$new_tokens
++;
$need_atime_update_p
= 1;
}
elsif
(
$num_rows
== 2 ||
$num_rows
== 3) {
$need_atime_update_p
= 1;
}
else
{
my
$token_displ
=
$token
;
$token_displ
=~ s/(.)/
sprintf
(
'%02x'
,
ord
($1))/egs;
dbg(
"bayes: _put_tokens: Updated an unexpected number of rows: %s, "
.
"id: %s, token (hex): %s"
,
$num_rows
,
$self
->{_userid},
$token_displ
);
$error_p
= 1;
}
}
}
$sth
->finish();
if
(
$error_p
) {
$self
->{_dbh}->rollback();
return
0;
}
if
(
$need_atime_update_p
) {
my
$token_count_update
=
''
;
$token_count_update
=
"token_count = token_count + $new_tokens,"
if
(
$new_tokens
);
$sql
= "UPDATE bayes_vars SET
$token_count_update
newest_token_age = GREATEST(newest_token_age, ?),
oldest_token_age = LEAST(oldest_token_age, ?)
WHERE id = ?";
$sth
=
$self
->{_dbh}->prepare_cached(
$sql
);
unless
(
defined
(
$sth
)) {
dbg(
"bayes: _put_tokens: SQL error: "
.
$self
->{_dbh}->errstr());
$self
->{_dbh}->rollback();
return
0;
}
my
$rc
=
$sth
->execute(
$atime
,
$atime
,
$self
->{_userid});
unless
(
$rc
) {
dbg(
"bayes: _put_tokens: SQL error: "
.
$self
->{_dbh}->errstr());
$self
->{_dbh}->rollback();
return
0;
}
}
else
{
info(
"bayes: _put_tokens: no atime updates needed? Num of tokens: %d"
,
scalar
keys
%{
$tokens
});
}
}
$self
->{_dbh}->commit();
return
1;
}
sub
_token_select_string {
return
"RPAD(token, 5, ' ')"
;
}
sub
sa_die { Mail::SpamAssassin::sa_die(
@_
); }
1;