# This code is a part of Slash, and is released under the GPL. # Copyright 1997-2001 by Open Source Development Network. See README # and COPYING for more information, or see http://slashcode.com/. # $Id: MySQL.pm,v 1.2.2.114 2001/11/01 05:51:23 jamie Exp $ package Slash::DB::Static::MySQL; ##################################################################### # # Note, this is where all of the ugly red headed step children go. # This does not exist, these are not the methods you are looking for. # ##################################################################### use strict; use Slash::Utility; use URI (); use vars qw($VERSION); use base 'Slash::DB::MySQL'; ($VERSION) = ' $Revision: 1.2.2.114 $ ' =~ /\$Revision:\s+([^\s]+)/; # FRY: Hey, thinking hurts 'em! Maybe I can think of a way to use that. # SQL STATUS FUNCTIONS. ######################################################## sub sqlShowMasterStatus { my($self) = @_; #$self->sqlConnect(); my $stat = $self->{_dbh}->prepare("SHOW MASTER STATUS"); $stat->execute; my $statlist = []; push @{$statlist}, $_ while $_ = $stat->fetchrow_hashref; return $statlist; } ######################################################## sub sqlShowSlaveStatus { my($self) = @_; #$self->sqlConnect(); my $stat = $self->{_dbh}->prepare("SHOW SLAVE STATUS"); $stat->execute; my $statlist = []; push @{$statlist}, $_ while $_ = $stat->fetchrow_hashref; return $statlist; } ######################################################## # for slashd # This method is used in a pretty wasteful way sub getBackendStories { my($self, $section) = @_; my $select; $select .= "stories.sid, stories.title, time, dept, stories.uid,"; $select .= "alttext, image, stories.commentcount, stories.section as section,"; $select .= "story_text.introtext, story_text.bodytext,topics.tid as tid"; my $from = "stories, story_text, topics"; my $where; $where .= "stories.sid = story_text.sid"; $where .= " AND stories.tid=topics.tid"; $where .= " AND time < NOW()"; $where .= " AND stories.writestatus != 'delete'"; if ($section) { $where .= " AND stories.section=\"$section\" and displaystatus > -1"; } else { $where .= " AND displaystatus = 0"; } my $other = "ORDER BY time DESC LIMIT 10"; my $returnable = $self->sqlSelectAllHashrefArray($select, $from, $where, $other); return $returnable; } ######################################################## # This is only called if ssi is set # # Deprecated code as this is now handled in the tasks! # - Cliff 10/11/01 sub updateCommentTotals { my($self, $sid, $comments) = @_; my $hp = join ',', @{$comments->{0}{totals}}; $self->sqlUpdate("stories", { hitparade => $hp, writestatus => 'ok', commentcount => $comments->{0}{totals}[0] }, 'sid=' . $self->{_dbh}->quote($sid) ); } ######################################################## # For slashd # "LIMIT 10" chosen arbitrarily, actually could be LIMIT 5 but doesn't # hurt much to kick in some more in case the caller has been changed to # want more. sub getNewStoryTopic { my($self) = @_; my $sth = $self->sqlSelectMany( 'alttext, image, width, height, stories.tid as tid', 'stories, topics', "stories.tid=topics.tid AND displaystatus = 0 AND writestatus != 'delete' AND time < NOW()", 'ORDER BY time DESC LIMIT 10' ); return $sth; } ######################################################## # For dailystuff sub updateArchivedDiscussions { my($self) = @_; my $days_to_archive = getCurrentStatic('archive_delay'); # Close discussions. $self->sqlDo( "UPDATE discussions SET type='archived' WHERE to_days(now()) - to_days(ts) > $days_to_archive AND (type='open' OR type='dirty')" ); } ######################################################## # For dailystuff sub getArchiveList { my($self, $limit, $dir) = @_; $limit ||= 1; $dir = 'ASC' if $dir ne 'ASC' || $dir ne 'DESC'; my $days_to_archive = getCurrentStatic('archive_delay'); # Close associated story so that final archival .shtml is written # to disk. This is accomplished by the archive.pl task. my $returnable = $self->sqlSelectAll( 'sid, title, section', 'stories', "to_days(now()) - to_days(time) > $days_to_archive AND (writestatus='ok' OR writestatus='dirty')", "ORDER BY time $dir LIMIT $limit" ); return $returnable; } ######################################################## # For dailystuff sub deleteRecycledComments { my($self) = @_; my $days_to_archive = getCurrentStatic('archive_delay'); my $comments = $self->sqlSelectAll( 'cid, discussions.id', 'comments,discussions', "to_days(now()) - to_days(date) > $days_to_archive AND discussions.id = comments.sid AND discussions.type = 'recycle' AND comments.pid = 0" ); my $rtotal = 0; for my $comment (@$comments) { next if !$comment or ref($comment) ne 'ARRAY' or !@$comment; my $local_count = $self->_deleteThread($comment->[0]); $self->setDiscussionDelCount($comment->[1], $local_count); $rtotal += $local_count; } return $rtotal; } sub _deleteThread { my($self, $cid, $level, $comments_deleted) = @_; $level ||= 0; if (!$cid) { errorLog("_deleteThread called with no cid"); return 0; } my $count = 1; my @delList; $comments_deleted = \@delList if !$level; my $delkids = $self->getCommentChildren($cid); # Delete children of $cid. push @{$comments_deleted}, $cid; for (@{$delkids}) { my($cid) = @{$_}; push @{$comments_deleted}, $cid; $count += $self->_deleteThread($cid, $level+1, $comments_deleted); } # And now delete $cid. $count += $self->deleteComment($cid); return $count; } ######################################################## # For dailystuff # This just updates the counts for the day before # -Brian sub updateStoriesCounts { my($self) = @_; my $constants = getCurrentStatic(); my $counts = $self->sqlSelectAll( 'dat,count(*)', 'accesslog', "op='article' AND dat !='' AND to_days(now()) - to_days(ts) = 1", 'GROUP BY(dat)' ); for my $count (@$counts) { $self->sqlUpdate('stories', { -hits => "hits+$count->[1]" }, 'sid=' . $self->sqlQuote($count->[0]) ); } } ######################################################## # For dailystuff sub forgetCommentIPs { my($self) = @_; my $constants = getCurrentStatic(); # Forget the source IP information for comments older than a given # time. my $hours = $constants->{comments_forgetip_hours} || 720; my $hours1 = $hours-1; $hours1 = 0 if $hours1 < 0; # At what cid do we start scanning? my $mincid = $constants->{comments_forgetip_mincid}; if (!defined($mincid)) { $self->sqlInsert('vars', { name => 'comments_forgetip_mincid', value => '0', }); $mincid = 0; } # How many rows to do at once? We don't want to tie up the DB # for too long at one sitting. Find the first discussion posted # just after the time limit, and then the first comment in that # discussion. A discussion predates its comments, so this comment # is guaranteed to postdate the time limit, and finding it doesn't # require a table scan of comments, only of discussions. my $maxrows = $constants->{comments_forgetip_maxrows} || 10000; my $maxcid = 0; my $min_remember_sid = $self->sqlSelect("MIN(id)", "discussions", "ts > DATE_SUB(NOW(), INTERVAL $hours1 HOUR)"); if ($min_remember_sid) { $maxcid = $self->sqlSelect("MIN(cid)", "comments", "sid=$min_remember_sid"); } if ($maxcid < $mincid) { # Shouldn't happen, but just in case $maxcid = $mincid; } elsif ($mincid+$maxrows < $maxcid) { $maxcid = $mincid+$maxrows; } my $nextcid = $mincid; if ($maxcid > $mincid) { # Do the update. $self->sqlUpdate("comments", { ipid => '', subnetid => '' }, "cid BETWEEN $mincid AND $maxcid AND date < DATE_SUB(NOW(), INTERVAL $hours HOUR)" ); # How far did we go? $nextcid = $self->sqlSelect("MAX(cid)", "comments USE INDEX (primary)", # XXX ugly "cid BETWEEN $mincid AND $maxcid AND ipid = ''", ); $nextcid ||= $mincid; # The next forgetting can start here. $self->setVar('comments_forgetip_mincid', $nextcid); } return $nextcid - $mincid; } ######################################################## # For dailystuff sub deleteDaily { my($self) = @_; my $constants = getCurrentStatic(); $self->updateStoriesCounts(); # Now for some random stuff $self->sqlDo("DELETE from pollvoters"); $self->sqlDo("DELETE from moderatorlog WHERE to_days(now()) - to_days(ts) > $constants->{archive_delay}"); $self->sqlDo("DELETE from metamodlog WHERE to_days(now()) - to_days(ts) > $constants->{archive_delay}"); # Formkeys my $delete_time = time() - $constants->{'formkey_timeframe'}; $self->sqlDo("DELETE FROM formkeys WHERE ts < $delete_time"); # Note, on Slashdot, the next line locks the accesslog for several # minutes, up to 10 minutes if traffic has been heavy. $self->sqlDo("DELETE FROM accesslog WHERE date_add(ts,interval 48 hour) < now()"); } ######################################################## # For dailystuff sub countDaily { my($self) = @_; my %returnable; my $constants = getCurrentStatic(); ($returnable{'total'}) = $self->sqlSelect("count(*)", "accesslog", "to_days(now()) - to_days(ts)=1"); my $c = $self->sqlSelectMany("count(*)", "accesslog", "to_days(now()) - to_days(ts)=1 GROUP BY host_addr"); $returnable{'unique'} = $c->rows; $c->finish; $c = $self->sqlSelectMany("count(*)", "accesslog", "to_days(now()) - to_days(ts)=1 GROUP BY uid"); $returnable{'unique_users'} = $c->rows; $c->finish; $c = $self->sqlSelectMany("count(*)", "accesslog", "op='journal' AND to_days(now()) - to_days(ts)=1 GROUP BY op"); $returnable{'journals'} = $c->rows; $c->finish; # my($comments) = $self->sqlSelect("count(*)", "accesslog", # "to_days(now()) - to_days(ts)=1 AND op='comments'"); $c = $self->sqlSelectMany("dat,count(*)", "accesslog", "to_days(now()) - to_days(ts)=1 AND (op='index' OR dat='index') GROUP BY dat"); my(%indexes, %articles, %commentviews); while (my($sect, $cnt) = $c->fetchrow) { $indexes{$sect} = $cnt; } $c->finish; $c = $self->sqlSelectMany("dat,count(*),op", "accesslog", "to_days(now()) - to_days(ts)=1 AND op='article'", "GROUP BY dat"); while (my($sid, $cnt) = $c->fetchrow) { $articles{$sid} = $cnt; } $c->finish; # clean the key table # not used ... ? # $c = $self->sqlSelectMany("dat,count(*)", "accesslog", # "to_days(now()) - to_days(ts)=1 AND op='comments'", # "GROUP BY dat"); # while (my($sid, $cnt) = $c->fetchrow) { # $commentviews{$sid} = $cnt; # } # $c->finish; $returnable{'index'} = \%indexes; $returnable{'articles'} = \%articles; return \%returnable; } ######################################################## # For dailystuff sub updateStamps { my($self) = @_; my $columns = "uid"; my $tables = "accesslog"; my $where = "to_days(now())-to_days(ts)=1 AND uid > 0"; my $other = "GROUP BY uid"; my $E = $self->sqlSelectAll($columns, $tables, $where, $other); $self->sqlTransactionStart("LOCK TABLES users_info WRITE"); for (@{$E}) { my $uid = $_->[0]; $self->setUser($uid, {-lastaccess=>'now()'}); } $self->sqlTransactionFinish(); } ######################################################## # For dailystuff sub getDailyMail { my($self) = @_; my $columns = "stories.sid, stories.title, stories.section, users.nickname, stories.tid, stories.time, stories.dept, story_text.introtext, story_text.bodytext"; my $tables = "stories, story_text, users"; my $where = "time < NOW() AND TO_DAYS(NOW())-TO_DAYS(time)=1 "; $where .= "AND users.uid=stories.uid AND stories.sid=story_text.sid "; $where .= "AND stories.displaystatus=0 "; my $other = " ORDER BY stories.time DESC"; my $email = $self->sqlSelectAll($columns, $tables, $where, $other); return $email; } ######################################################## # For dailystuff sub getMailingList { my($self) = @_; my $columns = "realemail,nickname,users.uid"; my $tables = "users,users_comments,users_info"; my $where = "users.uid=users_comments.uid AND users.uid=users_info.uid AND maillist=1"; my $other = "order by realemail"; my $users = $self->sqlSelectAll($columns, $tables, $where, $other); return $users; } ######################################################## # For dailystuff # XXX Outdated, delete before tarball release - Jamie 2001/07/08 # If we go back to using this is may have issues -Brian #sub getOldStories { # my($self, $delay) = @_; # # my $columns = "sid,time,section,title"; # my $tables = "stories"; # my $where = "writestatus<5 AND writestatus >= 0 AND to_days(now()) - to_days(time) > $delay"; # # my $stories = $self->sqlSelectAll($columns, $tables, $where); # # return $stories; #} ######################################################## # For portald sub getTop10Comments { my($self) = @_; my $c = $self->sqlSelectMany("stories.sid, title, cid, subject, date, nickname, comments.points", "comments, stories, users", "comments.points >= 4 AND users.uid=comments.uid AND comments.sid=stories.discussion", "ORDER BY date DESC LIMIT 10"); my $comments = $c->fetchall_arrayref; $c->finish; formatDate($comments, 4, 4); return $comments; } ######################################################## # For portald sub randomBlock { my($self) = @_; my $c = $self->sqlSelectMany("bid,title,url,block", "blocks", "section='index' AND portal=1 AND ordernum < 0"); my $A = $c->fetchall_arrayref; $c->finish; my $R = $A->[rand @$A]; my($bid, $title, $url, $block) = @$R; $self->sqlUpdate("blocks", { title => "rand($title);", url => $url }, "bid='rand'"); return $block; } ######################################################## # For portald # ugly method name sub getAccesLogCountTodayAndYestarday { my($self) = @_; my $c = $self->sqlSelectMany("count(*), to_days(now()) - to_days(ts) as d", "accesslog", "", "GROUP by d order by d asc"); my($today) = $c->fetchrow; my($yesterday) = $c->fetchrow; $c->finish; return ($today, $yesterday); } ######################################################## # For portald sub getSitesRDF { my($self) = @_; my $columns = "bid,url,rdf,retrieve"; my $tables = "blocks"; my $where = "rdf != '' and retrieve=1"; my $other = ""; my $rdf = $self->sqlSelectAll($columns, $tables, $where, $other); return $rdf; } ######################################################## # For the sectionblock, which is generated by # tasks/refresh_sectionblocks.pl in the defaut theme. sub getSectionInfo { my($self) = @_; my $sections = $self->sqlSelectAllHashrefArray( 'section', "sections", "isolate=0 and (section != '' and section != 'articles') ORDER BY section" ); for (@{$sections}) { @{%{$_}}{qw(month day)} = $self->{_dbh}->selectrow_array(< -1 ORDER BY time DESC LIMIT 1 EOT $_->{count} = $self->{_dbh}->selectrow_array(< -1 EOT } return $sections; } ######################################################## # For moderatord sub tokens2points { my($self) = @_; my $constants = getCurrentStatic(); my @log; # rtbl my $cursor = $self->sqlSelectMany( 'uid', 'users_info', "tokens >= $constants->{maxtokens}" ); # For some reason, the underlying calls to setUser will not work # without the extra locks on users_acl or users_param. $self->sqlTransactionStart('LOCK TABLES users READ, users_acl READ, users_info WRITE, users_comments WRITE, users_param READ, users_prefs WRITE' ); # rtbl while (my($uid) = $cursor->fetchrow) { my $rtbl = $self->getUser($uid, 'rtbl') || 0; # rtbl push @log, Slash::getData( ($rtbl) ? 'moderatord_tokennotgrantmsg' : 'moderatord_tokengrantmsg', { uid => $uid } ); $self->setUser($uid, { -lastgranted => 'now()', -tokens => ($rtbl) ? '0' : "tokens*$constants->{token_retention}", -points => ($rtbl) ? '0' : ($constants->{maxtokens} / $constants->{tokensperpoint}), }); } $cursor->finish; $cursor = $self->sqlSelectMany('users.uid as uid', 'users,users_comments,users_info', "karma >= 0 AND points > $constants->{maxpoints} AND seclev < 100 AND users.uid=users_comments.uid AND users.uid=users_info.uid"); $self->sqlTransactionFinish(); $self->sqlTransactionStart("LOCK TABLES users_comments WRITE"); while (my($uid) = $cursor->fetchrow) { # No update of lastgranted here as this is just a limiter. # these points should expire as per normal. $self->sqlUpdate('users_comments', { points => $constants->{maxpoints}, }, "uid=$uid"); } $self->sqlTransactionFinish(); return \@log; } ######################################################## # For moderatord sub stirPool { my($self) = @_; my $stir = getCurrentStatic('stir'); my $cursor = $self->sqlSelectMany("points,users.uid as uid", "users,users_comments,users_info", "users.uid=users_comments.uid AND users.uid=users_info.uid AND seclev <= 1 AND points > 0 AND to_days(now())-to_days(lastgranted) > $stir"); my $revoked = 0; $self->sqlTransactionStart("LOCK TABLES users_info WRITE,users_comments WRITE"); while (my($p, $u) = $cursor->fetchrow) { $revoked += $p; $self->setUser($u, { points => '0', -lastgranted => 'now()', }); } $self->sqlTransactionFinish(); $cursor->finish; # We aren't using this for Slashdot, feel free to turn this on if you # wish to use it (the proper return value is: $revoked) return 0; } ######################################################## # For moderatord and some utils sub getLastUser { my($self) = @_; # Why users_info instead of users? - Cliff my($totalusers) = $self->sqlSelect("max(uid)", "users_info"); return $totalusers; } ######################################################## # For tailslash sub pagesServed { my($self) = @_; my $returnable = $self->sqlSelectAll("count(*),ts", "accesslog", "1=1", "GROUP BY ts ORDER BY ts ASC"); return $returnable; } ######################################################## # For tailslash sub maxAccessLog { my($self) = @_; my($returnable) = $self->sqlSelect("max(id)", "accesslog");; return $returnable; } ######################################################## # For tailslash sub getAccessLogInfo { my($self, $id) = @_; my $returnable = $self->sqlSelectAll("host_addr,uid,op,dat,ts,id", "accesslog", "id > $id", "ORDER BY ts DESC"); formatDate($returnable, 4, 4, '%H:%M'); return $returnable; } ######################################################## # For moderatord sub fetchEligibleModerators { my($self) = @_; my $constants = getCurrentStatic(); my $eligibleUsers = $self->getLastUser() * $constants->{m1_eligible_percentage}; # Should this list include authors if var[authors_unlimited] is # non-zero? my $returnable = $self->sqlSelectAll("users_info.uid,count(*) as c", "users_info,users_prefs, accesslog", "users_info.uid < $eligibleUsers AND users_info.uid=accesslog.uid AND users_info.uid=users_prefs.uid AND (op='article' or op='comments') AND willing=1 AND karma >= 0 GROUP BY users_info.uid HAVING c >= $constants->{m1_eligible_hitcount} ORDER BY c"); # Would it be better to NOT use the HAVING clause here and instead # iterate thru a statement handle and triggering on [c] as the old code # did? # # Another idea: # ALTER TABLE accesslog ADD INDEX uid (uid); # # Then SELECT ON/GROUP BY accesslog.uid? return $returnable; } ######################################################## # For moderatord sub updateTokens { my($self, $modlist) = @_; my $num_empty_uids = 0; for my $uid (@{$modlist}) { #if ($uid) { # ++$num_empty_uids; # next; #} $self->setUser($uid, { -tokens => "tokens+1", }); } if ($num_empty_uids) { errorLog("$num_empty_uids empty uids in updateTokens"); } # if ($num_empty_uids) { # errorLog("$num_empty_uids empty uids in updateTokens"); #} } ######################################################## # For dailyStuff # This should only be run once per day, if this isn't # true, the simple logic below, breaks. This can be # fixed by moving the by_days trigger to a date # based system as opposed to a counter-based one, # or even adding a date component to expiry checks, # which might be a better solution. sub checkUserExpiry { my($self) = @_; my($ret); # Subtract one from number of 'registered days left' for all users. $self->sqlUpdate( 'users_info', { -'expiry_days' => 'expiry_days-1' }, '1=1' ); # Now grab all UIDs that look to be expired, we explicitly exclude # authors from this search. $ret = $self->sqlSelectAll( 'distinct uid', 'users_info', 'expiry_days < 0 or expiry_comm < 0' ); # We only want the list of UIDs that aren't authors and have not already # expired. The extra perl code would be completely unavoidable if we had # subselects... *sigh* my(@returnable) = grep { my $user = $self->getUser($_->[0]); $_ = $_->[0]; !($user->{author} || ! $user->{registered}); } @{$ret}; return \@returnable; } ######################################################## # For moderation scripts. # This sub returns the moderatorlog IDs # that are eligible for consensus metamoderation. # sub getMetamodIDs { my($self) = @_; my($thresh, $num) = ( getCurrentStatic('m2_consensus'), getCurrentStatic('m2_batchsize') ); my $list = $self->sqlSelectAll( 'distinct moderatorlog.id', 'moderatorlog, metamodlog', "m2count >= $thresh AND moderatorlog.id=metamodlog.mmid AND cuid IS NOT NULL AND flag = 10", ($num) ? "LIMIT $num" : '' ); $_ = $_->[0] for @{$list}; return $list; } ######################################################## # For moderation scripts. # This sub returns the meta-moderation information # given the appropriate M2ID (primary # key into the metamodlog table). # sub getMetaModerations { my($self, $mmid) = @_; my $ret = $self->sqlSelectAllHashrefArray( '*', 'metamodlog', "mmid=$mmid" ); return $ret; } ######################################################## # For moderation scripts. # # sub updateM2Flag { my($self, $id, $val) = @_; $self->sqlUpdate('metamodlog', { -flag => $val, }, "id=$id"); } ######################################################## # For moderation scripts. # # Clears metamod flag for a given MMID. sub clearM2Flag { my($self, $id) = @_; # Note that we only update flags that are in the: # 10 - M2 Pending # state. $self->sqlUpdate('metamodlog', { -flag => '0', }, "flag=10 and mmid=$id"); } ######################################################## # For freshenup.pl,archive.pl # # sub getStoriesWithFlag { my($self, $writestatus, $order, $limit) = @_; my $sqlorder; $sqlorder = "ORDER BY time $order "; $sqlorder .= "LIMIT $limit" if $limit; # if writestatus is delete, we want ALL the candidates, # not just the ones that are displaying -- pudge my $displaywhere = $writestatus eq 'delete' ? '' : 'AND displaystatus > -1'; # Currently only used by two tasks and we do NOT want stories # that are marked as "Never Display". If this changes, # another method will be required. If such is created, I would # suggest getAllStoriesWithFlag() as the method name. We ALSO # don't want to mess with stories that haven't been displayed # yet! # # - Cliff 14-Oct-2001 my $returnable = $self->sqlSelectAll( "sid,title,section", "stories", "time < now() AND writestatus='$writestatus' $displaywhere", $sqlorder ); return $returnable; } ######################################################## # For tasks/spamarmor.pl # # Please note use of closure. This is not an error. # #{ #my($usr_block_size, $usr_start_point); # #sub iterateUsers { # my($self, $blocksize, $start) = @_; # # $start ||= 0; # # ($usr_block_size, $usr_start_point) = ($blocksize, $start) # if $blocksize && $blocksize != $usr_block_size; # $usr_start_point += $usr_block_size + 1 if !$blocksize; # # return $self->sqlSelectAllHashrefArray( # '*', # 'users', '', # "ORDER BY uid LIMIT $usr_start_point,$usr_block_size" # ); #} #} ######################################################## # For tasks/spamarmor.pl # # This returns a hashref of uid and realemail for 1/nth of the users # whose emaildisplay param is set to 1 (armored email addresses). # By default 1/7th, and which 1/7th determined by date. # # If emaildisplay is moved from users_param into the schema proper, # this code will have to be changed. # sub getTodayArmorList { my($self, $buckets, $which_bucket) = @_; # Defaults to 7 for weekly rotation. $buckets = 7 if !defined($buckets); $buckets =~ /(\d+)/; $buckets = $1; # Default to day of year. $which_bucket = (localtime)[7] if !defined($which_bucket); $which_bucket =~ /(\d+)/; $which_bucket = $1; $which_bucket %= $buckets; my $uid_aryref = $self->sqlSelectColArrayref( "uid", "users_param", "MOD(uid, $buckets) = $which_bucket AND name='emaildisplay' AND value=1", "ORDER BY uid" ); return { } if !@$uid_aryref; # nobody wants armor? skip next select my $uid_list = join(",", @$uid_aryref); return $self->sqlSelectAllHashref( "uid", "uid, realemail", "users", "uid IN ($uid_list)" ); } ######################################################## # freshen.pl sub deleteStoryAll { my($self, $sid) = @_; my $db_sid = $self->sqlQuote($sid); $self->sqlDo("DELETE FROM stories WHERE sid=$db_sid"); $self->sqlDo("DELETE FROM story_text WHERE sid=$db_sid"); my $discussion_id = $self->sqlSelect('id', 'discussions', "sid = $db_sid"); $self->deleteDiscussion($discussion_id) if $discussion_id; } ######################################################## # For tasks/author_cache.pl # GREATEST() is because of inconsistent schema were some values can # be NULL, which breaks MySQL -- pudge sub createAuthorCache { my($self) = @_; my $sql; $sql .= "REPLACE INTO authors_cache "; $sql .= "SELECT users.uid, nickname, GREATEST(fakeemail, ''), GREATEST(homepage, ''), 0, GREATEST(bio, ''), author "; $sql .= "FROM users, users_info "; $sql .= "WHERE users.author =1 "; $sql .= "AND users.uid=users_info.uid"; $self->sqlDo($sql); my $sql2; $sql2 .= "REPLACE INTO authors_cache "; $sql2 .= "SELECT users.uid, nickname, GREATEST(fakeemail, ''), GREATEST(homepage, ''), count(stories.uid), GREATEST(bio, ''), author "; $sql2 .= "FROM users, stories, users_info "; $sql2 .= "WHERE stories.uid=users.uid "; $sql2 .= "AND users.uid=users_info.uid GROUP BY stories.uid"; $self->sqlDo($sql2); } ######################################################## # For tasks/flush_formkeys.pl sub deleteOldFormkeys { my($self, $timeframe) = @_; $timeframe ||= 14400; my $nowtime = time(); $self->sqlDo("delete from formkeys where ts < ($nowtime - (2*".$timeframe."))"); } 1; __END__ =head1 NAME Slash::DB::Static::MySQL - MySQL Interface for Slash =head1 SYNOPSIS use Slash::DB::Static::MySQL; =head1 DESCRIPTION No documentation yet. Sue me. =head1 SEE ALSO Slash(3), Slash::DB(3). =cut