sub _put_token()

in lib/Mail/SpamAssassin/BayesStore/SQL.pm [1842:2024]


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;
  }

  my ($existing_spam_count,
      $existing_ham_count,
      $existing_atime) = $self->tok_get($token);

  if (!$existing_atime) {

    # You can't create a new entry for a token with a negative count, so just return
    # if we are unable to find an entry.
    return 1 if ($spam_count < 0 || $ham_count < 0);

    my $sql = "INSERT INTO bayes_token
               (id, token, spam_count, ham_count, atime)
               VALUES (?,?,?,?,?)";

    my $sth = $self->{_dbh}->prepare_cached($sql);

    unless (defined($sth)) {
      dbg("bayes: _put_token: SQL error: ".$self->{_dbh}->errstr());
      return 0;
    }

    my $rc = $sth->execute($self->{_userid},
			   $token,
			   $spam_count,
			   $ham_count,
			   $atime);
    
    unless ($rc) {
      dbg("bayes: _put_token: SQL error: ".$self->{_dbh}->errstr());
      return 0;
    }

    $sth->finish();

    $sql = "UPDATE bayes_vars SET token_count = token_count + 1
             WHERE id = ?";

    my $rows = $self->{_dbh}->do($sql, undef, $self->{_userid});
    
    unless (defined($rows)) {
      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);

    unless (defined($rows)) {
      dbg("bayes: _put_token: SQL error: ".$self->{_dbh}->errstr());
      return 0;
    }

    if ($rows eq '0E0') {
      # no need to update oldest_token_age if we updated newest_token_age
      
      $sql = "UPDATE bayes_vars SET oldest_token_age = ?
               WHERE id = ? AND oldest_token_age > ?";

      $rows = $self->{_dbh}->do($sql, undef, $atime, $self->{_userid}, $atime);
      
      unless (defined($rows)) {
	dbg("bayes: _put_token: SQL error: ".$self->{_dbh}->errstr());
	return 0;
      }
    }
  }
  else {

    if ($spam_count < 0 || $ham_count < 0) {
      # we only need to cleanup when we subtract counts for a token and the
      # counts may have both reached 0
      # XXX - future optimization, since we have the existing spam/ham counts
      # we can make an educated guess on if the count would reach 0, for
      # instance, if we are decreasing spam_count but spam_count is currently
      # > 1000, then there is no possible way this update or any others that
      # might currently be happening could reduce that value to 0, so there
      # would be no need to set the needs_cleanup flag
      $self->{needs_cleanup} = 1;
    }

    my $update_atime_p = 1;
    my $updated_atime_p = 0;

    # if the existing atime is already >= the one we are going to set, then
    # don't bother
    $update_atime_p = 0 if ($existing_atime >= $atime);

    # These SQL statements include as part of the WHERE clause something like
    # "AND spam_count + ? >= 0" or "AND ham_count + ? >= 0".  This is to keep
    # the count from going negative.

    if ($spam_count) {
      my $sql;
      my @args;
      if ($update_atime_p) {
	$sql = "UPDATE bayes_token
                   SET spam_count = spam_count + ?,
                       atime = ?
                 WHERE id = ?
                   AND token = ?
                   AND spam_count + ? >= 0";
	@args = ($spam_count, $atime, $self->{_userid}, $token, $spam_count);
	$updated_atime_p = 1; # note the fact that we did do it
      }
      else {
	$sql = "UPDATE bayes_token
                   SET spam_count = spam_count + ?
                 WHERE id = ?
                   AND token = ?
                   AND spam_count + ? >= 0";
	@args = ($spam_count, $self->{_userid}, $token, $spam_count);
      }

      my $rows = $self->{_dbh}->do($sql, undef, @args);

      unless (defined($rows)) {
	dbg("bayes: _put_token: SQL error: ".$self->{_dbh}->errstr());
	return 0;
      }
    }

    if ($ham_count) {
      my $sql;
      my @args;
      if ($update_atime_p && !$updated_atime_p) {
	$sql = "UPDATE bayes_token
                   SET ham_count = ham_count + ?,
                       atime = ?
                 WHERE id = ?
                   AND token = ?
                   AND ham_count + ? >= 0";
	@args = ($ham_count, $atime, $self->{_userid}, $token, $ham_count);
	$updated_atime_p = 1; # note the fact that we did do it
      }
      else {
	$sql = "UPDATE bayes_token
                   SET ham_count = ham_count + ?
                 WHERE id = ?
                   AND token = ?
                   AND ham_count + ? >= 0";
	@args = ($ham_count, $self->{_userid}, $token, $ham_count);
      }

      my $rows = $self->{_dbh}->do($sql, undef, @args);

      unless (defined($rows)) {
	dbg("bayes: _put_token: SQL error: ".$self->{_dbh}->errstr());
	return 0;
      }
    }

    if ($updated_atime_p) {
      # we updated the atime, so we need to check and update bayes_vars
      # we only need to worry about newest_token_age since we would have
      # 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 < ?";

      my $rows = $self->{_dbh}->do($sql, undef, $atime, $self->{_userid}, $atime);

      unless (defined($rows)) {
	dbg("bayes: _put_token: SQL error: ".$self->{_dbh}->errstr());
	return 0;
      }
    }
  }

  return 1;
}