From a69fb4827409786203ee6f4befc5aa4009d13ef8 Mon Sep 17 00:00:00 2001 From: Aaron Schulz Date: Wed, 13 May 2009 22:03:32 +0000 Subject: [PATCH] [schema change] Use new log_search table to replace ugly code from r48839. Maintenance script added to populate the table with revisiondelete log items. --- includes/LogEventsList.php | 21 ++++-- includes/LogPage.php | 18 ++++- includes/specials/SpecialRevisiondelete.php | 80 ++++++++++++--------- maintenance/populateLogSearch.php | 67 +++++++++++++++++ maintenance/tables.sql | 12 ++++ maintenance/updaters.inc | 1 + 6 files changed, 158 insertions(+), 41 deletions(-) create mode 100644 maintenance/populateLogSearch.php diff --git a/includes/LogEventsList.php b/includes/LogEventsList.php index 7a6f3f808a..1617fa47aa 100644 --- a/includes/LogEventsList.php +++ b/includes/LogEventsList.php @@ -651,22 +651,29 @@ class LogPager extends ReverseChronologicalPager { } public function getQueryInfo() { + $tables = array( 'logging', 'user' ); $this->mConds[] = 'user_id = log_user'; + $index = array(); + # Add log_search table if there are conditions on it + if( array_key_exists('ls_field',$this->mConds) ) { + $tables[] = 'log_search'; + $index = array( 'log_search' => 'PRIMARY', 'logging' => 'PRIMARY' ); # Don't use the wrong logging index - if( $this->title || $this->pattern || $this->user ) { - $index = array( 'USE INDEX' => array( 'logging' => array('page_time','user_time') ) ); + } else if( $this->title || $this->pattern || $this->user ) { + $index = array( 'logging' => array('page_time','user_time') ); } else if( $this->types ) { - $index = array( 'USE INDEX' => array( 'logging' => 'type_time' ) ); + $index = array( 'logging' => 'type_time' ); } else { - $index = array( 'USE INDEX' => array( 'logging' => 'times' ) ); + $index = array( 'logging' => 'times' ); } $info = array( - 'tables' => array( 'logging', 'user' ), + 'tables' => $tables, 'fields' => array( 'log_type', 'log_action', 'log_user', 'log_namespace', 'log_title', 'log_params', 'log_comment', 'log_id', 'log_deleted', 'log_timestamp', 'user_name', 'user_editcount' ), 'conds' => $this->mConds, - 'options' => $index, - 'join_conds' => array( 'user' => array( 'INNER JOIN', 'user_id=log_user' ) ), + 'options' => array( 'USE INDEX' => $index ), + 'join_conds' => array( 'user' => array( 'INNER JOIN', 'user_id=log_user' ), + 'log_search' => array( 'INNER JOIN', 'ls_log_id=log_id' ) ), ); ChangeTags::modifyDisplayQuery( $info['tables'], $info['fields'], $info['conds'], diff --git a/includes/LogPage.php b/includes/LogPage.php index 39869cb504..bb4cecb1b2 100644 --- a/includes/LogPage.php +++ b/includes/LogPage.php @@ -91,7 +91,7 @@ class LogPage { $this->type, $this->action, $this->target, $this->comment, $this->params, $newId ); $rc->notifyRC2UDP(); } - return true; + return $newId; } /** @@ -373,6 +373,22 @@ class LogPage { return $this->saveContent(); } + + /** + * Add relations to log_search table + * @static + */ + public function addRelations( $field, $values, $logid ) { + if( empty($values) ) + return false; // nothing + $data = array(); + foreach( $values as $value ) { + $data[] = array('ls_field' => $field,'ls_value' => $value,'ls_log_id' => $logid); + } + $dbw = wfGetDB( DB_MASTER ); + $dbw->insert( 'log_search', $data, __METHOD__, 'IGNORE' ); + return true; + } /** * Create a blob from a parameter array diff --git a/includes/specials/SpecialRevisiondelete.php b/includes/specials/SpecialRevisiondelete.php index 5e276757f7..6bd0b1d3d3 100644 --- a/includes/specials/SpecialRevisiondelete.php +++ b/includes/specials/SpecialRevisiondelete.php @@ -85,16 +85,15 @@ class SpecialRevisionDelete extends UnlistedSpecialPage { $this->showImages(); } else if( $this->deleteKey == 'logid' ) { $this->showLogItems(); - return; // no logs for now } - list($qc,$lim) = $this->getLogQueryCond(); + $qc = $this->getLogQueryCond(); # Show relevant lines from the deletion log $wgOut->addHTML( "

" . htmlspecialchars( LogPage::logName( 'delete' ) ) . "

\n" ); - LogEventsList::showLogExtract( $wgOut, 'delete', $this->page->getPrefixedText(), '', $lim, $qc ); + LogEventsList::showLogExtract( $wgOut, 'delete', $this->page->getPrefixedText(), '', 25, $qc ); # Show relevant lines from the suppression log if( $wgUser->isAllowed( 'suppressionlog' ) ) { $wgOut->addHTML( "

" . htmlspecialchars( LogPage::logName( 'suppress' ) ) . "

\n" ); - LogEventsList::showLogExtract( $wgOut, 'suppress', $this->page->getPrefixedText(), '', $lim, $qc ); + LogEventsList::showLogExtract( $wgOut, 'suppress', $this->page->getPrefixedText(), '', 25, $qc ); } } @@ -121,9 +120,7 @@ class SpecialRevisionDelete extends UnlistedSpecialPage { } private function getLogQueryCond() { - $ids = $safeIds = array(); - $limit = 25; // default - $conds = array( 'log_action' => 'revision' ); // revision delete logs + $logAction = 'revision'; switch( $this->deleteKey ) { case 'oldid': $ids = $this->oldids; @@ -137,25 +134,19 @@ class SpecialRevisionDelete extends UnlistedSpecialPage { case 'fileid': $ids = $this->fileids; break; + case 'logid': + $ids = $this->logids; + $logAction = 'event'; + break; default: // bad type? - return array($conds,$limit); - } - // Just get the whole log if there are a lot if items - if( count($ids) > $limit ) - return array($conds,$limit); - // Digit chars only - foreach( $ids as $id ) { - if( preg_match( '/^\d+$/', $id, $m ) ) { - $safeIds[] = $m[0]; - } - } - // Format is - if( count($safeIds) ) { - $conds[] = "log_params RLIKE '^{$this->deleteKey}.*(^|\n|,)(".implode('|',$safeIds).")(,|\n|$)'"; - } else { - $conds = array('1=0'); - } - return array($conds,$limit); + return array(); + } + // Revision delete logs for these item + $conds = array( 'log_action' => $logAction ); + $conds['ls_field'] = RevisionDeleter::getRelationType( $this->deleteKey ); + $conds['ls_value'] = $ids; + $conds[] = 'log_id = ls_log_id'; + return $conds; } private function secureOperation() { @@ -824,7 +815,7 @@ class SpecialRevisionDelete extends UnlistedSpecialPage { private function failure() { global $wgOut; - $wgOut->setPagetitle( wfMsg( 'actioncomplete' ) ); + $wgOut->setPagetitle( wfMsg( 'actionfailed' ) ); $wrap = '$1'; if( $this->deleteKey == 'logid' ) { $this->showLogItems(); @@ -1540,18 +1531,41 @@ class RevisionDeleter { $param, $items = array() ) { // Put things hidden from sysops in the oversight log - $logtype = ( ($nbitfield | $obitfield) & Revision::DELETED_RESTRICTED ) ? + $logType = ( ($nbitfield | $obitfield) & Revision::DELETED_RESTRICTED ) ? 'suppress' : 'delete'; - $log = new LogPage( $logtype ); + // Log deletions show with a difference action message + $logAction = ( $param == 'logid' ) ? 'event' : 'revision'; + // Track what items changed here $itemCSV = implode(',',$items); - + // Add params for effected page and ids if( $param == 'logid' ) { $params = array( $itemCSV, "ofield={$obitfield}", "nfield={$nbitfield}" ); - $log->addEntry( 'event', $title, $comment, $params ); } else { - // Add params for effected page and ids $params = array( $param, $itemCSV, "ofield={$obitfield}", "nfield={$nbitfield}" ); - $log->addEntry( 'revision', $title, $comment, $params ); } + // Actually add the deletion log entry + $log = new LogPage( $logType ); + $logid = $log->addEntry( $logAction, $title, $comment, $params ); + // Allow for easy searching of deletion log items for revision/log items + $log->addRelations( self::getRelationType($param), $items, $logid ); } -} + + // Get DB field name for URL param... + // Future code for other things may also track + // other types of revision-specific changes. + public static function getRelationType( $param ) { + switch( $param ) { + case 'oldid': + return 'rev_id'; + case 'artimestamp': + return 'rev_timestamp'; + case 'oldimage': + return 'oi_timestamp'; + case 'fileid': + return 'file_id'; + case 'logid': + return 'log_id'; + } + throw new MWException( "Bad log URL param type!" ); + } +} \ No newline at end of file diff --git a/maintenance/populateLogSearch.php b/maintenance/populateLogSearch.php new file mode 100644 index 0000000000..88127bfe7d --- /dev/null +++ b/maintenance/populateLogSearch.php @@ -0,0 +1,67 @@ +tableExists( 'log_search' ) ) { + echo "log_search does not exist\n"; + exit( 1 ); +} + +migrate_log_params( $db ); + +function migrate_log_params( $db ) { + $start = $db->selectField( 'logging', 'MIN(log_id)', false, __FUNCTION__ ); + if( !$start ) { + die("Nothing to do.\n"); + } + $end = $db->selectField( 'logging', 'MAX(log_id)', false, __FUNCTION__ ); + + # Do remaining chunk + $end += BATCH_SIZE - 1; + $blockStart = $start; + $blockEnd = $start + BATCH_SIZE - 1; + while( $blockEnd <= $end ) { + echo "...doing log_id from $blockStart to $blockEnd\n"; + $cond = "log_id BETWEEN $blockStart AND $blockEnd"; + $res = $db->select( 'logging', '*', $cond, __FUNCTION__ ); + $batch = array(); + while( $row = $db->fetchObject( $res ) ) { + // RevisionDelete logs - revisions + if( LogEventsList::typeAction( $row, array('delete','suppress'), 'revision' ) ) { + $params = LogPage::extractParams( $row->log_params ); + // Param format: [ ] + if( count($params) >= 2 ) { + $field = RevisionDeleter::getRelationType($params[0]); + $items = explode(',',$params[1]); + $log = new LogPage( $row->log_type ); + $log->addRelations( $field, $items, $row->log_id ); + } + // RevisionDelete logs - log events + } else if( LogEventsList::typeAction( $row, array('delete','suppress'), 'event' ) ) { + $params = LogPage::extractParams( $row->log_params ); + // Param format: [ ] + if( count($params) >= 1 ) { + $items = explode(',',$params[0]); + $log = new LogPage( $row->log_type ); + $log->addRelations( 'log_id', $items, $row->log_id ); + } + } + } + $blockStart += BATCH_SIZE - 1; + $blockEnd += BATCH_SIZE - 1; + wfWaitForSlaves( 5 ); + } + echo "...Done!\n"; +} diff --git a/maintenance/tables.sql b/maintenance/tables.sql index 80ff0aacf1..101a17784f 100644 --- a/maintenance/tables.sql +++ b/maintenance/tables.sql @@ -1124,6 +1124,18 @@ CREATE INDEX /*i*/page_time ON /*_*/logging (log_namespace, log_title, log_times CREATE INDEX /*i*/times ON /*_*/logging (log_timestamp); +CREATE TABLE /*_*/log_search ( + -- The type of ID (rev ID, log ID, rev timestamp, username) + ls_field varbinary(32) NOT NULL, + -- The value of the ID + ls_value varchar(255) NOT NULL, + -- Key to log_id + ls_log_id int unsigned NOT NULL default 0, + PRIMARY KEY (ls_field,ls_value,ls_log_id) +); +CREATE INDEX /*i*/ls_log_id ON /*_*/log_search (ls_log_id); + + CREATE TABLE /*_*/trackbacks ( tb_id int PRIMARY KEY AUTO_INCREMENT, tb_page int REFERENCES /*_*/page(page_id) ON DELETE CASCADE, diff --git a/maintenance/updaters.inc b/maintenance/updaters.inc index b404e9f0a5..69db8bf194 100644 --- a/maintenance/updaters.inc +++ b/maintenance/updaters.inc @@ -156,6 +156,7 @@ $wgUpdates = array( array( 'add_table', 'tag_summary', 'patch-change_tag.sql' ), array( 'add_table', 'valid_tag', 'patch-change_tag.sql' ), array( 'add_table', 'user_properties', 'patch-user_properties.sql' ), + array( 'add_table', 'log_search', 'patch-log_search.sql' ), ), 'sqlite' => array( -- 2.20.1