/** * Populates the search index with content from all pages */ protected function populateSearchIndex() { $res = $this->db->select('page', 'MAX(page_id) AS count'); $s = $this->db->fetchObject($res); $count = $s->count; $this->output("Rebuilding index fields for {$count} pages...\n"); $n = 0; $fields = array_merge(Revision::selectPageFields(), Revision::selectFields(), Revision::selectTextFields()); while ($n < $count) { if ($n) { $this->output($n . "\n"); } $end = $n + self::RTI_CHUNK_SIZE - 1; $res = $this->db->select(['page', 'revision', 'text'], $fields, ["page_id BETWEEN {$n} AND {$end}", 'page_latest = rev_id', 'rev_text_id = old_id'], __METHOD__); foreach ($res as $s) { try { $title = Title::makeTitle($s->page_namespace, $s->page_title); $rev = new Revision($s); $content = $rev->getContent(); $u = new SearchUpdate($s->page_id, $title, $content); $u->doUpdate(); } catch (MWContentSerializationException $ex) { $this->output("Failed to deserialize content of revision {$s->rev_id} of page " . "`" . $title->getPrefixedDBkey() . "`!\n"); } } $n += self::RTI_CHUNK_SIZE; } }
/** * @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)); } }
/** * Get all global images from a certain page */ public function getLinksFromPage($id) { $res = $this->db->select('globalimagelinks', 'gil_to', array('gil_wiki' => $this->interwiki, 'gil_page' => $id), __METHOD__); $images = array(); foreach ($res as $row) { $images[] = $row->gil_to; } return $images; }
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); }
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); }
/** * @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']); }
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; }
/** * @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 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']); }
/** * Find pages in main and talk namespaces that have a prefix of the new * namespace so we know titles that will need migrating * * @param int $ns Destination namespace id * @param string $name Prefix that is being made a namespace * @param array $options Associative array of validated command-line options * * @return ResultWrapper */ private function getTargetList($ns, $name, $options) { if ($options['move-talk'] && MWNamespace::isSubject($ns)) { $checkNamespaces = array(NS_MAIN, NS_TALK); } else { $checkNamespaces = NS_MAIN; } return $this->db->select('page', array('page_id', 'page_title', 'page_namespace'), array('page_namespace' => $checkNamespaces, 'page_title' . $this->db->buildLike("{$name}:", $this->db->anyString())), __METHOD__); }
/** * Fetch the next set of rows from the database. */ public function next() { $res = $this->db->select($this->table, $this->fetchColumns, $this->buildConditions(), __METHOD__, array('LIMIT' => $this->batchSize, 'ORDER BY' => $this->orderBy), $this->joinConditions); // The iterator is converted to an array because in addition to // returning it in self::current() we need to use the end value // in self::buildConditions() $this->current = iterator_to_array($res); $this->key++; }
/** * Get the interwiki list * * @todo Needs to respect interwiki cache! * @return Array */ private function getInterwikiList() { $result = $this->db->select('interwiki', array('iw_prefix')); $prefixes = array(); foreach ($result as $row) { $prefixes[] = $row->iw_prefix; } return $prefixes; }
/** * Find pages in mainspace that have a prefix of the new namespace * so we know titles that will need migrating * * @param int $ns Namespace id (id for new namespace?) * @param string $name Prefix that is being made a namespace * * @return array */ private function getConflicts($ns, $name) { $titleSql = "TRIM(LEADING {$this->db->addQuotes("{$name}:")} FROM page_title)"; if ($ns == 0) { // An interwiki; try an alternate encoding with '-' for ':' $titleSql = $this->db->buildConcat(array($this->db->addQuotes("{$name}-"), $titleSql)); } return iterator_to_array($this->db->select('page', array('id' => 'page_id', 'oldtitle' => 'page_title', 'namespace' => $this->db->addQuotes($ns) . ' + page_namespace', 'title' => $titleSql, 'oldnamespace' => 'page_namespace'), array('page_namespace' => array(0, 1), 'page_title' . $this->db->buildLike("{$name}:", $this->db->anyString())), __METHOD__)); }
/** * @return array */ public function getGroups() { # @todo FIXME: Untested. $groups = array(); $res = $this->mDb->select('user_groups', 'ug_group', array('ug_user' => $this->mRow->user_id), __METHOD__); foreach ($res as $row) { $groups[] = $row->ug_group; } return $groups; }
/** * Populates the search index with content from all pages */ protected function populateSearchIndex() { $res = $this->db->select('page', 'MAX(page_id) AS count'); $s = $this->db->fetchObject($res); $count = $s->count; $this->output("Rebuilding index fields for {$count} pages...\n"); $n = 0; while ($n < $count) { if ($n) { $this->output($n . "\n"); } $end = $n + self::RTI_CHUNK_SIZE - 1; $res = $this->db->select(array('page', 'revision', 'text'), array('page_id', 'page_namespace', 'page_title', 'old_flags', 'old_text'), array("page_id BETWEEN {$n} AND {$end}", 'page_latest = rev_id', 'rev_text_id = old_id'), __METHOD__); foreach ($res as $s) { $revtext = Revision::getRevisionText($s); $u = new SearchUpdate($s->page_id, $s->page_title, $revtext); $u->doUpdate(); } $n += self::RTI_CHUNK_SIZE; } }
/** * Get the links from a foreign database * @return Array crafted to look like an API response */ function getLinksFromDB($param) { global $wgInterlanguageExtensionDB; if (!$this->foreignDbr) { $this->foreignDbr = wfGetDB(DB_SLAVE, array(), $wgInterlanguageExtensionDB); } list($dbKey, $namespace) = $this->getKeyNS($param); $a = array('query' => array('pages' => array())); $res = $this->foreignDbr->select('page', array('page_id', 'page_is_redirect'), array('page_title' => $dbKey, 'page_namespace' => $namespace), __FUNCTION__); $res = $res->fetchObject(); if ($res === false) { // There is no such article on the central wiki $a['query']['pages'][-1] = array('missing' => ""); } else { if ($res->page_is_redirect) { $res = $this->foreignDbr->select(array('redirect', 'page'), 'page_id', array('rd_title = page_title', 'rd_from' => $res->page_id), __FUNCTION__); $res = $res->fetchObject(); } $a['query']['pages'][0] = array('langlinks' => $this->readLinksFromDB($this->foreignDbr, $res->page_id)); } return $a; }
/** * 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; }
/** * @see PropertyStatisticsStore::getUsageCounts * * @since 1.9 * * @param array $propertyIds * * @return array */ public function getUsageCounts(array $propertyIds) { if ($propertyIds === array()) { return array(); } $propertyStatistics = $this->dbConnection->select($this->dbConnection->tablename($this->table), array('usage_count', 'p_id'), array('p_id' => $propertyIds), __METHOD__); $usageCounts = array(); foreach ($propertyStatistics as $propertyStatistic) { assert(ctype_digit($propertyStatistic->p_id)); assert(ctype_digit($propertyStatistic->usage_count)); $usageCounts[(int) $propertyStatistic->p_id] = (int) $propertyStatistic->usage_count; } return $usageCounts; }
public function execute() { $user = $this->getOption('user', 'Maintenance script'); $this->dbr = wfGetDB(DB_MASTER); $campaigns = $this->dbr->select('uw_campaigns', '*'); if (!$campaigns->numRows()) { $this->output("Nothing to migrate.\n"); return; } foreach ($campaigns as $campaign) { $oldConfig = $this->getConfigFromDB($campaign->campaign_id); $newConfig = $this->getConfigForJSON($campaign, $oldConfig); $title = Title::makeTitleSafe(NS_CAMPAIGN, $campaign->campaign_name); $page = Wikipage::factory($title); $content = new CampaignContent(json_encode($newConfig)); $page->doEditContent($content, "Migrating from old campaign tables", 0, false, User::newFromName($user)); $this->output("Migrated {$campaign->campaign_name}\n"); } }
function tryInsertWiki(DatabaseBase $db, $wikiId) { if (is_string($wikiId)) { $w = WikiFactory::UrlToID($wikiId); if ($w == null) { throw new Exception("Can't resolve " . $wikiId); } $wikiId = $w; } $res = $db->select("webmaster_sitemaps", array('wiki_id'), array("wiki_id" => $wikiId), __METHOD__); if ($res->fetchRow()) { return false; } //echo "insert: " . $wikiId . "\n"; if (!$db->insert("webmaster_sitemaps", array("wiki_id" => $wikiId, "user_id" => null))) { throw new Exception("can't insert wiki id = " . $wikiId); } return true; }
/** * Scans a "parent layer" of the articles/categories in $this->next */ function scan_next_layer() { wfProfileIn(__METHOD__); # Find all parents of the article currently in $this->next $layer = array(); $res = $this->dbr->select('categorylinks', '*', array('cl_from' => $this->next), __METHOD__ . '-1'); foreach ($res as $o) { $k = $o->cl_to; # Update parent tree if (!isset($this->parents[$o->cl_from])) { $this->parents[$o->cl_from] = array(); } $this->parents[$o->cl_from][$k] = $o; # Ignore those we already have if (in_array($k, $this->deadend)) { continue; } if (isset($this->name2id[$k])) { continue; } # Hey, new category! $layer[$k] = $k; } $this->next = array(); # Find the IDs of all category pages in $layer, if they exist if (count($layer) > 0) { $res = $this->dbr->select('page', array('page_id', 'page_title'), array('page_namespace' => NS_CATEGORY, 'page_title' => $layer), __METHOD__ . '-2'); foreach ($res as $o) { $id = $o->page_id; $name = $o->page_title; $this->name2id[$name] = $id; $this->next[] = $id; unset($layer[$name]); } } # Mark dead ends foreach ($layer as $v) { $this->deadend[$v] = $v; } wfProfileOut(__METHOD__); }
public function updateRevision(DatabaseBase $dbw, $continue = null) { $rows = $dbw->select('flow_revision', array('rev_id', 'rev_user_id', 'rev_user_text', 'rev_mod_user_id', 'rev_mod_user_text', 'rev_edit_user_id', 'rev_edit_user_text'), array('rev_id > ' . $dbw->addQuotes($continue), $dbw->makeList(array('rev_user_id' => 0, 'rev_mod_user_id' => 0, 'rev_edit_user_id' => 0), LIST_OR)), __METHOD__, array('LIMIT' => $this->mBatchSize, 'ORDER BY' => 'rev_id')); $continue = null; foreach ($rows as $row) { $continue = $row->rev_id; $updates = array(); if ($row->rev_user_id == 0) { $updates['rev_user_ip'] = $row->rev_user_text; } if ($row->rev_mod_user_id == 0) { $updates['rev_mod_user_ip'] = $row->rev_mod_user_text; } if ($row->rev_edit_user_id == 0) { $updates['rev_edit_user_ip'] = $row->rev_edit_user_text; } if ($updates) { $dbw->update('flow_revision', $updates, array('rev_id' => $row->rev_id), __METHOD__); } } return $continue; }
private static function __getResults() { global $wgLang; wfProfileIn(__METHOD__); /* main query */ $aResult = array(); $aFields = array('/* BLOGS */ rev_page as page_id', 'page_namespace', 'page_title', 'min(rev_timestamp) as create_timestamp', 'unix_timestamp(rev_timestamp) as timestamp', 'rev_timestamp', 'min(rev_id) as rev_id', 'rev_user'); $res = self::$dbr->select(array_map(array(self::$dbr, 'tableName'), self::$aTables), $aFields, self::$aWhere, __METHOD__, self::__makeDBOrder()); while ($oRow = self::$dbr->fetchObject($res)) { if (class_exists('ArticleCommentList')) { $oComments = ArticleCommentList::newFromText($oRow->page_title, $oRow->page_namespace); $iCount = $oComments ? $oComments->getCountAllNested() : 0; } else { $iCount = 0; } /* username */ $oTitle = Title::newFromText($oRow->page_title, $oRow->page_namespace); $sUsername = ""; if (!$oTitle instanceof Title) { continue; } $username = BlogArticle::getOwner($oTitle); $oRevision = Revision::newFromTitle($oTitle); $aResult[$oRow->page_id] = array("page" => $oRow->page_id, "namespace" => $oRow->page_namespace, "title" => $oRow->page_title, "page_touched" => !is_null($oRevision) ? $oRevision->getTimestamp() : $oTitle->getTouched(), "rev_timestamp" => $oRow->rev_timestamp, "timestamp" => $oRow->timestamp, "username" => isset($username) ? $username : "", "text" => self::__getRevisionText($oRow->page_id, $oRevision), "revision" => $oRow->rev_id, "comments" => $iCount, "votes" => '', "props" => BlogArticle::getProps($oRow->page_id)); // Sort by comment count for popular blog posts module if (isset(self::$aOptions['order']) && self::$aOptions['order'] == 'page_id') { uasort($aResult, array("BlogTemplateClass", "__sortByCommentCount")); } // We may need to query for 50 results but display 5 if (isset(self::$aOptions['displaycount']) && self::$aOptions['displaycount'] != self::$aOptions['count']) { $aResult = array_slice($aResult, 0, self::$aOptions['displaycount']); } } // macbre: change for Oasis to add avatars and comments / likes data wfRunHooks('BlogTemplateGetResults', array(&$aResult)); self::$dbr->freeResult($res); wfProfileOut(__METHOD__); return $aResult; }
/** * Add the old versions of the image to the batch * @return array List of archive names from old versions */ function addOlds() { $archiveBase = 'archive'; $this->olds = array(); $this->oldCount = 0; $archiveNames = array(); $result = $this->db->select('oldimage', array('oi_archive_name', 'oi_deleted'), array('oi_name' => $this->oldName), __METHOD__); foreach ($result as $row) { $archiveNames[] = $row->oi_archive_name; $oldName = $row->oi_archive_name; $bits = explode('!', $oldName, 2); if (count($bits) != 2) { wfDebug("Old file name missing !: '{$oldName}' \n"); continue; } list($timestamp, $filename) = $bits; if ($this->oldName != $filename) { wfDebug("Old file name doesn't match: '{$oldName}' \n"); continue; } $this->oldCount++; // Do we want to add those to oldCount? if ($row->oi_deleted & File::DELETED_FILE) { continue; } $this->olds[] = array("{$archiveBase}/{$this->oldHash}{$oldName}", "{$archiveBase}/{$this->newHash}{$timestamp}!{$this->newName}"); } return $archiveNames; }
/** * Recursive helper for canCreateObjectsForWebUser(). * @param DatabaseBase $conn * @param int $targetMember Role ID of the member to look for * @param int $group Role ID of the group to look for * @param int $maxDepth Maximum recursive search depth * @return bool */ protected function isRoleMember($conn, $targetMember, $group, $maxDepth) { if ($targetMember === $group) { // A role is always a member of itself return true; } // Get all members of the given group $res = $conn->select('"pg_catalog"."pg_auth_members"', array('member'), array('roleid' => $group), __METHOD__); foreach ($res as $row) { if ($row->member == $targetMember) { // Found target member return true; } // Recursively search each member of the group to see if the target // is a member of it, up to the given maximum depth. if ($maxDepth > 0) { if ($this->isRoleMember($conn, $targetMember, $row->member, $maxDepth - 1)) { // Found member of member return true; } } } return false; }
/** * Count the number of items on a user's watchlist * * @param DatabaseBase $dbr A database connection * @return int */ protected function countItems($dbr) { # Fetch the raw count $rows = $dbr->select('watchlist', array('count' => 'COUNT(*)'), array('wl_user' => $this->getUser()->getId()), __METHOD__); $row = $dbr->fetchObject($rows); $count = $row->count; return floor($count / 2); }
/** * @param DatabaseBase $db * @return mixed */ public function doQuery($db) { $conds = array('rev_page' => $this->title->getArticleID()); if ($this->ids !== null) { $conds['rev_id'] = array_map('intval', $this->ids); } return $db->select(array('revision', 'page', 'user'), array_merge(Revision::selectFields(), Revision::selectUserFields()), $conds, __METHOD__, array('ORDER BY' => 'rev_id DESC'), array('page' => Revision::pageJoinCond(), 'user' => Revision::userJoinCond())); }
/** * Get the current data stored for the given ID in the given database * table. The result is an array of updates, formatted like the one of * the table insertion arrays created by preparePropertyTableInserts(). * * @note Tables without IDs as subject are not supported. They will * hopefully vanish soon anyway. * * @since 1.8 * @param integer $sid * @param SMWSQLStore3Table $tableDeclaration * @param DatabaseBase $dbr used for reading only * @return array */ protected function getCurrentPropertyTableContents($sid, SMWSQLStore3Table $propertyTable, DatabaseBase $dbr) { if (!$propertyTable->usesIdSubject()) { // does not occur, but let's be strict throw new InvalidArgumentException('Operation not supported for tables without subject IDs.'); } $contents = array(); $results = $dbr->select($propertyTable->getName(), '*', array('s_id' => $sid), __METHOD__); foreach ($results as $result) { $rowArray = (array) $result; $rowKey = self::makeDatabaseRowKey($rowArray); $contents[$rowKey] = $rowArray; } $dbr->freeResult($results); return $contents; }
/** * This function should *not* be called outside of JobQueueDB * * @param DatabaseBase $dbw * @param array $jobs * @param int $flags * @param string $method * @return boolean * @throws type */ public function doBatchPushInternal(DatabaseBase $dbw, array $jobs, $flags, $method) { if (!count($jobs)) { return true; } $rowSet = array(); // (sha1 => job) map for jobs that are de-duplicated $rowList = array(); // list of jobs for jobs that are are not de-duplicated foreach ($jobs as $job) { $row = $this->insertFields($job); if ($job->ignoreDuplicates()) { $rowSet[$row['job_sha1']] = $row; } else { $rowList[] = $row; } } if ($flags & self::QOS_ATOMIC) { $dbw->begin($method); // wrap all the job additions in one transaction } try { // Strip out any duplicate jobs that are already in the queue... if (count($rowSet)) { $res = $dbw->select('job', 'job_sha1', array('job_sha1' => array_keys($rowSet), 'job_token' => ''), $method); foreach ($res as $row) { wfDebug("Job with hash '{$row->job_sha1}' is a duplicate.\n"); unset($rowSet[$row->job_sha1]); // already enqueued } } // Build the full list of job rows to insert $rows = array_merge($rowList, array_values($rowSet)); // Insert the job rows in chunks to avoid slave lag... foreach (array_chunk($rows, 50) as $rowBatch) { $dbw->insert('job', $rowBatch, $method); } JobQueue::incrStats('job-insert', $this->type, count($rows)); JobQueue::incrStats('job-insert-duplicate', $this->type, count($rowSet) + count($rowList) - count($rows)); } catch (DBError $e) { if ($flags & self::QOS_ATOMIC) { $dbw->rollback($method); } throw $e; } if ($flags & self::QOS_ATOMIC) { $dbw->commit($method); } $this->cache->set($this->getCacheKey('empty'), 'false', JobQueueDB::CACHE_TTL_LONG); return true; }