/** * Delete one or more revisions from the database * Do this inside a transaction * * @param array $id Array of revision id values * @param DatabaseBase $dbw DatabaseBase class (needs to be a master) */ private function deleteRevs($id, &$dbw) { if (!is_array($id)) { $id = array($id); } $dbw->delete('revision', array('rev_id' => $id), __METHOD__); }
/** * @param DatabaseBase $db * @return mixed */ public function doQuery($db) { $ids = array_map('intval', $this->ids); $queryInfo = array('tables' => array('revision', 'user'), 'fields' => array_merge(Revision::selectFields(), Revision::selectUserFields()), 'conds' => array('rev_page' => $this->title->getArticleID(), 'rev_id' => $ids), 'options' => array('ORDER BY' => 'rev_id DESC'), 'join_conds' => array('page' => Revision::pageJoinCond(), 'user' => Revision::userJoinCond())); ChangeTags::modifyDisplayQuery($queryInfo['tables'], $queryInfo['fields'], $queryInfo['conds'], $queryInfo['join_conds'], $queryInfo['options'], ''); return $db->select($queryInfo['tables'], $queryInfo['fields'], $queryInfo['conds'], __METHOD__, $queryInfo['options'], $queryInfo['join_conds']); }
/** * @param ResultWrapper $res * @param DatabaseBase $dbw * @return null|int */ function convertOptionBatch($res, $dbw) { $id = null; foreach ($res as $row) { $this->mConversionCount++; $insertRows = array(); foreach (explode("\n", $row->user_options) as $s) { $m = array(); if (!preg_match("/^(.[^=]*)=(.*)\$/", $s, $m)) { continue; } // MW < 1.16 would save even default values. Filter them out // here (as in User) to avoid adding many unnecessary rows. $defaultOption = User::getDefaultOption($m[1]); if (is_null($defaultOption) || $m[2] != $defaultOption) { $insertRows[] = array('up_user' => $row->user_id, 'up_property' => $m[1], 'up_value' => $m[2]); } } if (count($insertRows)) { $dbw->insert('user_properties', $insertRows, __METHOD__, array('IGNORE')); } $dbw->update('user', array('user_options' => ''), array('user_id' => $row->user_id), __METHOD__); $id = $row->user_id; } return $id; }
public function updateRevision($columnPrefix, DatabaseBase $dbw, $continue = null) { $rows = $dbw->select('flow_revision', array('rev_id', 'rev_type'), array('rev_id > ' . $dbw->addQuotes($continue), "{$columnPrefix}_id > 0", "{$columnPrefix}_ip IS NOT NULL"), __METHOD__, array('LIMIT' => $this->mBatchSize, 'ORDER BY' => 'rev_id')); $ids = $objs = array(); foreach ($rows as $row) { $id = UUID::create($row->rev_id); $type = self::$types[$row->rev_type]; $om = $this->storage->getStorage($type); $obj = $om->get($id); if ($obj) { $om->merge($obj); $ids[] = $row->rev_id; $objs[] = $obj; } else { $this->error(__METHOD__ . ": Failed loading {$type}: " . $id->getAlphadecimal()); } } if (!$ids) { return null; } $dbw->update('flow_revision', array("{$columnPrefix}_ip" => null), array('rev_id' => $ids), __METHOD__); foreach ($objs as $obj) { $this->storage->cachePurge($obj); } $this->completeCount += count($ids); return end($ids); }
public function refreshBatch(DatabaseBase $dbr, UUID $continue, $countableActions, UUID $stop) { $rows = $dbr->select('flow_revision', array('rev_id', 'rev_user_id'), array('rev_id > ' . $dbr->addQuotes($continue->getBinary()), 'rev_id <= ' . $dbr->addQuotes($stop->getBinary()), 'rev_user_id > 0', 'rev_user_wiki' => wfWikiID(), 'rev_change_type' => $countableActions), __METHOD__, array('ORDER BY' => 'rev_id ASC', 'LIMIT' => $this->mBatchSize)); // end of data if (!$rows || $rows->numRows() === 0) { return false; } foreach ($rows as $row) { // User::incEditCount only allows for edit count to be increased 1 // at a time. It'd be better to immediately be able to increase the // edit count by the exact number it should be increased with, but // I'd rather re-use existing code, especially in a run-once script, // where performance is not the most important thing ;) $user = User::newFromId($row->rev_user_id); $user->incEditCount(); // save updates so we can print them when the script is done running if (!isset($this->updates[$user->getId()])) { $this->updates[$user->getId()] = 0; } $this->updates[$user->getId()]++; // set value for next batch to continue at $continue = $row->rev_id; } return UUID::create($continue); }
/** * @see SMWDescription::getSQLCondition * * FIXME: store specific code should be in the store component * * @since 0.6 * * @param string $tableName * @param array $fieldNames * @param DatabaseBase $dbs * * @return boolean */ public function getSQLCondition($tableName, array $fieldNames, DatabaseBase $dbs) { $dataItem = $this->getDataItem(); // Only execute the query when the description's type is geographical coordinates, // the description is valid, and the near comparator is used. if ($dataItem instanceof SMWDIGeoCoord) { switch ($this->getComparator()) { case SMW_CMP_EQ: $comparator = '='; break; case SMW_CMP_LEQ: $comparator = '<='; break; case SMW_CMP_GEQ: $comparator = '>='; break; case SMW_CMP_NEQ: $comparator = '!='; break; default: return false; } $lat = $dbs->addQuotes($dataItem->getLatitude()); $lon = $dbs->addQuotes($dataItem->getLongitude()); $conditions = array(); $conditions[] = "{$tableName}.{$fieldNames['1']} {$comparator} {$lat}"; $conditions[] = "{$tableName}.{$fieldNames['2']} {$comparator} {$lon}"; return implode(' AND ', $conditions); } return false; }
function updateUserWikiCount(DatabaseBase $db, $userId, $wikiCount) { $res = $db->update("webmaster_user_accounts", array("wikis_number" => $wikiCount), array("user_id" => $userId)); if (!$res) { throw new Exception("Failed to update User id=" . $userId . " count = " . $wikiCount); } }
/** * @param DatabaseBase $db * @return mixed */ public function doQuery($db) { $ids = array_map('intval', $this->ids); $live = $db->select(array('revision', 'page', 'user'), array_merge(Revision::selectFields(), Revision::selectUserFields()), array('rev_page' => $this->title->getArticleID(), 'rev_id' => $ids), __METHOD__, array('ORDER BY' => 'rev_id DESC'), array('page' => Revision::pageJoinCond(), 'user' => Revision::userJoinCond())); if ($live->numRows() >= count($ids)) { // All requested revisions are live, keeps things simple! return $live; } // Check if any requested revisions are available fully deleted. $archived = $db->select(array('archive'), Revision::selectArchiveFields(), array('ar_rev_id' => $ids), __METHOD__, array('ORDER BY' => 'ar_rev_id DESC')); if ($archived->numRows() == 0) { return $live; } elseif ($live->numRows() == 0) { return $archived; } else { // Combine the two! Whee $rows = array(); foreach ($live as $row) { $rows[$row->rev_id] = $row; } foreach ($archived as $row) { $rows[$row->ar_rev_id] = $row; } krsort($rows); return new FakeResultWrapper(array_values($rows)); } }
/** * Lock the appropriate tables for the script * @param DatabaseBase $db * @param string $extraTable The name of any extra tables to lock (eg: text) */ private function lockTables($db, $extraTable = []) { $tbls = ['page', 'revision', 'redirect']; if ($extraTable) { $tbls = array_merge($tbls, $extraTable); } $db->lockTables([], $tbls, __METHOD__, false); }
/** * @param DatabaseBase $db * @return mixed */ public function doQuery($db) { $timestamps = array(); foreach ($this->ids as $id) { $timestamps[] = $db->timestamp($id); } return $db->select('archive', Revision::selectArchiveFields(), array('ar_namespace' => $this->title->getNamespace(), 'ar_title' => $this->title->getDBkey(), 'ar_timestamp' => $timestamps), __METHOD__, array('ORDER BY' => 'ar_timestamp DESC')); }
/** * @param DatabaseBase $db * @return mixed */ public function doQuery($db) { $archiveNames = array(); foreach ($this->ids as $timestamp) { $archiveNames[] = $timestamp . '!' . $this->title->getDBkey(); } return $db->select('oldimage', OldLocalFile::selectFields(), array('oi_name' => $this->title->getDBkey(), 'oi_archive_name' => $archiveNames), __METHOD__, array('ORDER BY' => 'oi_timestamp DESC')); }
/** * @param array $updates Array of arrays each containing two keys, 'primaryKey' * and 'changes'. primaryKey must contain a map of column names to values * sufficient to uniquely identify the row changes must contain a map of column * names to update values to apply to the row. */ public function write(array $updates) { $this->db->begin(); foreach ($updates as $update) { $this->db->update($this->table, $update['changes'], $update['primaryKey'], __METHOD__); } $this->db->commit(); wfWaitForSlaves(false, false, $this->clusterName); }
/** * @param DatabaseBase $db * @return mixed */ public function doQuery($db) { $ids = array_map('intval', $this->ids); $queryInfo = DatabaseLogEntry::getSelectQueryData(); $queryInfo['conds'] += array('log_id' => $ids); $queryInfo['options'] += array('ORDER BY' => 'log_id DESC'); ChangeTags::modifyDisplayQuery($queryInfo['tables'], $queryInfo['fields'], $queryInfo['conds'], $queryInfo['join_conds'], $queryInfo['options'], ''); return $db->select($queryInfo['tables'], $queryInfo['fields'], $queryInfo['conds'], __METHOD__, $queryInfo['options'], $queryInfo['join_conds']); }
/** * May throw a database error if, say, the server dies during query. * @param DatabaseBase $db * @param int $id The old_id * @return string */ private function doGetText($db, $id) { $id = intval($id); $row = $db->selectRow('text', ['old_text', 'old_flags'], ['old_id' => $id], __METHOD__); $text = Revision::getRevisionText($row); if ($text === false) { return false; } return $text; }
public static function buildPostInExpr(\DatabaseBase $db, array $arr) { $range = ''; foreach ($arr as $post) { if ($range) { $range .= ','; } $range .= $db->addQuotes($post->id->getBin()); } return ' IN(' . $range . ')'; }
/** * Get temp user accounts * * - check if these accounts are really temp ones * - do not remove accounts with password set (122 of them) * * @param DatabaseBase $db * @return int[] */ protected function getAccountsToRemove(DatabaseBase $db) { $res = $db->select(self::USER_TABLE, ['user_id', 'user_name'], ['user_name ' . $db->buildLike(self::TEMPUSER_PREFIX, $db->anyString()), 'user_password' => ''], __METHOD__); $users = []; while ($user = $res->fetchObject()) { // check if this is really a temp user: "******" + <user ID> if ($user->user_name === self::TEMPUSER_PREFIX . $user->user_id) { $users[] = intval($user->user_id); } else { $this->output(sprintf(" > skipped %s (#%d)\n", $user->user_name, $user->user_id)); } } return $users; }
/** * Cache page existence for performance * * @param DatabaseBase $db * @param ResultWrapper $res */ function preprocessResults($db, $res) { $batch = new LinkBatch(); foreach ($res as $row) { $batch->add($row->namespace, $row->title); $batch->addObj($this->getRedirectTarget($row)); } $batch->execute(); // Back to start for display if ($res->numRows() > 0) { // If there are no rows we get an error seeking. $db->dataSeek($res, 0); } }
/** * Perform a cleanup for a set of wikis * * @param DatabaseBase $db database handler * @param string $table name of table to clean up * @param string $wiki_id_column table column name to use when querying for wiki ID * @param Array $city_ids IDs of wikis to remove from the table */ private function doTableCleanup(DatabaseBase $db, $table, array $city_ids, $wiki_id_column = 'wiki_id') { $start = microtime(true); $db->delete($table, [$wiki_id_column => $city_ids], __METHOD__); $rows = $db->affectedRows(); // just in case MW decides to start a transaction automagically $db->commit(__METHOD__); Wikia\Logger\WikiaLogger::instance()->info(__METHOD__, ['table' => $table, 'cities' => join(', ', $city_ids), 'count' => count($city_ids), 'took' => round(microtime(true) - $start, 4), 'rows' => $rows]); $this->output(sprintf("%s: %s - removed %d rows\n", date('Y-m-d H:i:s'), $table, $rows)); // throttle delete queries if ($rows > 0) { sleep(5); } }
protected function buildUpdateCondition(DatabaseBase $dbw) { $rcNew = $dbw->addQuotes(RC_NEW); $rcSrcNew = $dbw->addQuotes(RecentChange::SRC_NEW); $rcEdit = $dbw->addQuotes(RC_EDIT); $rcSrcEdit = $dbw->addQuotes(RecentChange::SRC_EDIT); $rcLog = $dbw->addQuotes(RC_LOG); $rcSrcLog = $dbw->addQuotes(RecentChange::SRC_LOG); $rcExternal = $dbw->addQuotes(RC_EXTERNAL); $rcSrcExternal = $dbw->addQuotes(RecentChange::SRC_EXTERNAL); return "rc_source = CASE\n\t\t\t\t\tWHEN rc_type = {$rcNew} THEN {$rcSrcNew}\n\t\t\t\t\tWHEN rc_type = {$rcEdit} THEN {$rcSrcEdit}\n\t\t\t\t\tWHEN rc_type = {$rcLog} THEN {$rcSrcLog}\n\t\t\t\t\tWHEN rc_type = {$rcExternal} THEN {$rcSrcExternal}\n\t\t\t\t\tELSE ''\n\t\t\t\tEND"; }
function addMissingImage($filename, $fullpath) { global $wgContLang; $timestamp = $this->dbw->timestamp($this->getRepo()->getFileTimestamp($fullpath)); $altname = $wgContLang->checkTitleEncoding($filename); if ($altname != $filename) { if ($this->dryrun) { $filename = $altname; $this->output("Estimating transcoding... {$altname}\n"); } else { # @todo FIXME: create renameFile() $filename = $this->renameFile($filename); } } if ($filename == '') { $this->output("Empty filename for {$fullpath}\n"); return; } if (!$this->dryrun) { $file = wfLocalFile($filename); if (!$file->recordUpload('', '(recovered file, missing upload log entry)', '', '', '', false, $timestamp)) { $this->output("Error uploading file {$fullpath}\n"); return; } } $this->output($fullpath . "\n"); }
function copyExactMatch($srcTable, $dstTable, $copyPos) { $numRowsCopied = 0; $srcRes = $this->dbw->select($srcTable, '*', array('log_timestamp' => $copyPos), __METHOD__); $dstRes = $this->dbw->select($dstTable, '*', array('log_timestamp' => $copyPos), __METHOD__); if ($srcRes->numRows()) { $srcRow = $srcRes->fetchObject(); $srcFields = array_keys((array) $srcRow); $srcRes->seek(0); $dstRowsSeen = array(); # Make a hashtable of rows that already exist in the destination foreach ($dstRes as $dstRow) { $reducedDstRow = array(); foreach ($srcFields as $field) { $reducedDstRow[$field] = $dstRow->{$field}; } $hash = md5(serialize($reducedDstRow)); $dstRowsSeen[$hash] = true; } # Copy all the source rows that aren't already in the destination foreach ($srcRes as $srcRow) { $hash = md5(serialize((array) $srcRow)); if (!isset($dstRowsSeen[$hash])) { $this->dbw->insert($dstTable, (array) $srcRow, __METHOD__); $numRowsCopied++; } } } return $numRowsCopied; }
/** * Common function for databases that don't understand the MySQLish syntax of interwiki.sql. * * @return Status */ public function populateInterwikiTable() { $status = $this->getConnection(); if (!$status->isOK()) { return $status; } $this->db->selectDB($this->getVar('wgDBname')); if ($this->db->selectRow('interwiki', '*', array(), __METHOD__)) { $status->warning('config-install-interwiki-exists'); return $status; } global $IP; wfSuppressWarnings(); $rows = file("{$IP}/maintenance/interwiki.list", FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); wfRestoreWarnings(); $interwikis = array(); if (!$rows) { return Status::newFatal('config-install-interwiki-list'); } foreach ($rows as $row) { $row = preg_replace('/^\\s*([^#]*?)\\s*(#.*)?$/', '\\1', $row); // strip comments - whee if ($row == "") { continue; } $row .= "||"; $interwikis[] = array_combine(array('iw_prefix', 'iw_url', 'iw_local', 'iw_api', 'iw_wikiid'), explode('|', $row)); } $this->db->insert('interwiki', $interwikis, __METHOD__); return Status::newGood(); }
/** * Migrates user options from the user table blob to user_properties */ protected function doMigrateUserOptions() { if ( $this->db->tableExists( 'user_properties' ) ) { $cl = $this->maintenance->runChild( 'ConvertUserOptions', 'convertUserOptions.php' ); $cl->execute(); $this->output( "done.\n" ); } }
/** * Format and output report results using the given information plus * OutputPage * * @param OutputPage $out OutputPage to print to * @param Skin $skin User skin to use [unused] * @param DatabaseBase $dbr (read) connection to use * @param int $res Result pointer * @param int $num Number of available result rows * @param int $offset Paging offset */ protected function outputResults($out, $skin, $dbr, $res, $num, $offset) { if ($num > 0) { $gallery = new ImageGallery(); # $res might contain the whole 1,000 rows, so we read up to # $num [should update this to use a Pager] for ($i = 0; $i < $num && ($row = $dbr->fetchObject($res)); $i++) { $namespace = isset($row->namespace) ? $row->namespace : NS_FILE; $title = Title::makeTitleSafe($namespace, $row->title); if ($title instanceof Title && $title->getNamespace() == NS_FILE) { $gallery->add($title, $this->getCellHtml($row)); } } $out->addHTML($gallery->toHtml()); } }
/** * Uses the primary key list and the maximal result row from the * previous iteration to build an SQL condition sufficient for * selecting the next page of results. All except the final key use * `=` conditions while the final key uses a `>` condition * * Example output: * array( '( foo = 42 AND bar > 7 ) OR ( foo > 42 )' ) * * @return array The SQL conditions necessary to select the next set * of rows in the batched query */ protected function buildConditions() { if (!$this->current) { return $this->conditions; } $maxRow = end($this->current); $maximumValues = array(); foreach ($this->primaryKey as $column) { $maximumValues[$column] = $this->db->addQuotes($maxRow->{$column}); } $pkConditions = array(); // For example: If we have 3 primary keys // first run through will generate // col1 = 4 AND col2 = 7 AND col3 > 1 // second run through will generate // col1 = 4 AND col2 > 7 // and the final run through will generate // col1 > 4 while ($maximumValues) { $pkConditions[] = $this->buildGreaterThanCondition($maximumValues); array_pop($maximumValues); } $conditions = $this->conditions; $conditions[] = sprintf('( %s )', implode(' ) OR ( ', $pkConditions)); return $conditions; }
/** * @return DatabaseBase */ function getMasterDB() { if (!isset($this->dbConn)) { $this->dbConn = DatabaseBase::factory($this->dbType, array('host' => $this->dbServer, 'user' => $this->dbUser, 'password' => $this->dbPassword, 'dbname' => $this->dbName, 'flags' => $this->dbFlags, 'tablePrefix' => $this->tablePrefix)); } return $this->dbConn; }
/** * fixTemplate * * This code ensures that the version of the Template that was in existence * at the same time as the Memento gets loaded and displayed with the * Memento. * * @fixme make this compatible with parser cache * @param Title $title * @param Parser $parser * @param integer $id * * @return array containing the text, finalTitle, and deps */ public function fixTemplate(Title $title, Parser $parser, &$id) { // stopgap measure until we can find a better way // to work with parser cache $parser->disableCache(); $request = $parser->getUser()->getRequest(); if ($request->getHeader('ACCEPT-DATETIME')) { $requestDatetime = $request->getHeader('ACCEPT-DATETIME'); $mwMementoTimestamp = $this->parseRequestDateTime($requestDatetime); $firstRev = $title->getFirstRevision(); // if the template no longer exists, return gracefully if ($firstRev != null) { if ($firstRev->getTimestamp() < $mwMementoTimestamp) { $pgID = $title->getArticleID(); $this->db->begin(); $res = $this->db->selectRow('revision', array('rev_id'), array('rev_page' => $pgID, 'rev_timestamp <=' . $this->db->addQuotes($mwMementoTimestamp)), __METHOD__, array('ORDER BY' => 'rev_id DESC', 'LIMIT' => '1')); $id = $res->rev_id; } else { // if we get something prior to the first memento, just // go with the first one $id = $firstRev->getId(); } } } }
/** * Merge page histories * * @param integer $id The page_id * @param Title $newTitle The new title * @return bool */ private function mergePage($row, Title $newTitle) { $id = $row->page_id; // Construct the WikiPage object we will need later, while the // page_id still exists. Note that this cannot use makeTitleSafe(), // we are deliberately constructing an invalid title. $sourceTitle = Title::makeTitle($row->page_namespace, $row->page_title); $sourceTitle->resetArticleID($id); $wikiPage = new WikiPage($sourceTitle); $wikiPage->loadPageData('fromdbmaster'); $destId = $newTitle->getArticleId(); $this->beginTransaction($this->db, __METHOD__); $this->db->update('revision', array('rev_page' => $destId), array('rev_page' => $id), __METHOD__); $this->db->delete('page', array('page_id' => $id), __METHOD__); /* Call LinksDeletionUpdate to delete outgoing links from the old title, * and update category counts. * * Calling external code with a fake broken Title is a fairly dubious * idea. It's necessary because it's quite a lot of code to duplicate, * but that also makes it fragile since it would be easy for someone to * accidentally introduce an assumption of title validity to the code we * are calling. */ $update = new LinksDeletionUpdate($wikiPage); $update->doUpdate(); $this->commitTransaction($this->db, __METHOD__); return true; }
/** * @param DatabaseBase $dbw * @param string $table * @param string $column * @param string $wiki */ function doDeletes($dbw, $table, $column, $wiki) { if (!$dbw->tableExists($table)) { $this->error("Maintenance script cannot be run on this wiki as there is no {$table} table", 1); } $this->output("{$table}:\n"); $count = 0; do { $wikiQuoted = $dbw->addQuotes($wiki); $dbw->query("DELETE FROM {$table} WHERE {$column}={$wikiQuoted} LIMIT 500", __METHOD__); $affected = $dbw->affectedRows(); $count += $affected; $this->output("{$count}\n"); wfWaitForSlaves(); } while ($affected === 500); $this->output("{$count} {$table} rows deleted\n"); }
function rewind() { if ($this->numRows()) { $this->db->dataSeek($this, 0); } $this->pos = 0; $this->currentRow = null; }