/** * @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; }
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 testAddQuotesStringQuote() { $check = "'string''s cause trouble'"; if ($this->db->getType() === 'mysql') { $check = "'string\\'s cause trouble'"; } $this->assertEquals($check, $this->db->addQuotes("string's cause trouble")); }
/** * @see PropertyStatisticsStore::addToUsageCount * * @since 1.9 * * @param integer $propertyId * @param integer $value * * @return boolean Success indicator * @throws MWException */ public function addToUsageCount($propertyId, $value) { if (!is_int($value)) { throw new MWException('The value to add must be an integer'); } if (!is_int($propertyId) || $propertyId <= 0) { throw new MWException('The property id to add must be a positive integer'); } if ($value == 0) { return true; } return $this->dbConnection->update($this->table, array('usage_count = usage_count ' . ($value > 0 ? '+ ' : '- ') . $this->dbConnection->addQuotes(abs($value))), array('p_id' => $propertyId), __METHOD__); }
/** * Check and repair the destination fields in a link table * @param string $table The link table name * @param string $fieldPrefix The field prefix in the link table * @param int $ns Destination namespace id * @param string $name * @param array $options Associative array of validated command-line options * @param array $extraConds Extra conditions for the SQL query */ private function checkLinkTable($table, $fieldPrefix, $ns, $name, $options, $extraConds = array()) { $batchConds = array(); $fromField = "{$fieldPrefix}_from"; $namespaceField = "{$fieldPrefix}_namespace"; $titleField = "{$fieldPrefix}_title"; $batchSize = 500; while (true) { $res = $this->db->select($table, array($fromField, $namespaceField, $titleField), array_merge($batchConds, $extraConds, array($namespaceField => 0, $titleField . $this->db->buildLike("{$name}:", $this->db->anyString()))), __METHOD__, array('ORDER BY' => array($titleField, $fromField), 'LIMIT' => $batchSize)); if ($res->numRows() == 0) { break; } foreach ($res as $row) { $logTitle = "from={$row->{$fromField}} ns={$row->{$namespaceField}} " . "dbk={$row->{$titleField}}"; $destTitle = $this->getDestinationTitle($ns, $name, $row->{$namespaceField}, $row->{$titleField}, $options); $this->totalLinks++; if (!$destTitle) { $this->output("{$table} {$logTitle} *** INVALID\n"); continue; } $this->resolvableLinks++; if (!$options['fix']) { $this->output("{$table} {$logTitle} -> " . $destTitle->getPrefixedDBkey() . " DRY RUN\n"); continue; } $this->db->update($table, array($namespaceField => $destTitle->getNamespace(), $titleField => $destTitle->getDBkey()), array($namespaceField => 0, $titleField => $row->{$titleField}, $fromField => $row->{$fromField}), __METHOD__, array('IGNORE')); $this->output("{$table} {$logTitle} -> " . $destTitle->getPrefixedDBkey() . "\n"); } $encLastTitle = $this->db->addQuotes($row->{$titleField}); $encLastFrom = $this->db->addQuotes($row->{$fromField}); $batchConds = array("{$titleField} > {$encLastTitle} " . "OR ({$titleField} = {$encLastTitle} AND {$fromField} > {$encLastFrom})"); wfWaitForSlaves(); } }
/** * 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; }
/** * 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(); } } } }
/** * Find pages in mainspace that have a prefix of the new namespace * so we know titles that will need migrating * @param $ns int Namespace id (id for new namespace?) * @param $name String Prefix that is being made a namespace */ private function getConflicts( $ns, $name ) { $page = 'page'; $table = $this->db->tableName( $page ); $prefix = $this->db->strencode( $name ); $encNamespace = $this->db->addQuotes( $ns ); $titleSql = "TRIM(LEADING '$prefix:' FROM {$page}_title)"; if( $ns == 0 ) { // An interwiki; try an alternate encoding with '-' for ':' $titleSql = $this->db->buildConcat( array( "'$prefix-'", $titleSql ) ); } $sql = "SELECT {$page}_id AS id, {$page}_title AS oldtitle, $encNamespace + {$page}_namespace AS namespace, $titleSql AS title, {$page}_namespace AS oldnamespace FROM {$table} WHERE ( {$page}_namespace=0 OR {$page}_namespace=1 ) AND {$page}_title " . $this->db->buildLike( $name . ':', $this->db->anyString() ); $result = $this->db->query( $sql, __METHOD__ ); $set = array(); foreach( $result as $row ) { $set[] = $row; } return $set; }
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); }
private static function __getCategories($aParamValues, &$parser) { wfProfileIn(__METHOD__); self::$aCategoryNames = $aParamValues; $aPages = array(); if (!empty($aParamValues)) { # RT 26917 $aParamValues = array_map("strip_tags", array_map(array("self", "__parseCategories"), $aParamValues, array($parser))); // set timestamp option, if set $timestampLimit = BLOGS_TIMESTAMP; if (!empty(self::$aOptions['timestamp'])) { $timestampLimit = self::$aOptions['timestamp']; } /* set max length of group concat query */ self::$dbr->query('SET group_concat_max_len = ' . GROUP_CONCAT, __METHOD__); /* run query */ $res = self::$dbr->select(array(self::$dbr->tableName('page'), self::$dbr->tableName('categorylinks')), array("cl_to", "GROUP_CONCAT(DISTINCT cl_from SEPARATOR ',') AS cl_page"), array("page_namespace" => NS_BLOG_ARTICLE, "page_id = cl_from", "cl_to in (" . self::$dbr->makeList($aParamValues) . ")", "page_touched >= " . self::$dbr->addQuotes($timestampLimit)), __METHOD__, array('GROUP BY' => 'cl_to')); while ($oRow = self::$dbr->fetchObject($res)) { // BugId:49408 // Since GROUP_CONCAT respects group_concat_max_len arbitrarily, // sometimes we end up with a comma or a truncated item, which // we don't want. if (GROUP_CONCAT == strlen($oRow->cl_page)) { $aPages[] = preg_replace('/,\\d+,?$/', '', $oRow->cl_page); } else { $aPages[] = $oRow->cl_page; } } self::$dbr->freeResult($res); } wfProfileOut(__METHOD__); return $aPages; }
/** * 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__)); }
/** * @param DatabaseBase $db * @param string $table * @param string $field * @return null|PostgresField */ static function fromText($db, $table, $field) { $q = <<<SQL SELECT attnotnull, attlen, conname AS conname, atthasdef, adsrc, COALESCE(condeferred, 'f') AS deferred, COALESCE(condeferrable, 'f') AS deferrable, CASE WHEN typname = 'int2' THEN 'smallint' WHEN typname = 'int4' THEN 'integer' WHEN typname = 'int8' THEN 'bigint' WHEN typname = 'bpchar' THEN 'char' ELSE typname END AS typname FROM pg_class c JOIN pg_namespace n ON (n.oid = c.relnamespace) JOIN pg_attribute a ON (a.attrelid = c.oid) JOIN pg_type t ON (t.oid = a.atttypid) LEFT JOIN pg_constraint o ON (o.conrelid = c.oid AND a.attnum = ANY(o.conkey) AND o.contype = 'f') LEFT JOIN pg_attrdef d on c.oid=d.adrelid and a.attnum=d.adnum WHERE relkind = 'r' AND nspname=%s AND relname=%s AND attname=%s; SQL; $table = $db->tableName($table, 'raw'); $res = $db->query(sprintf($q, $db->addQuotes($db->getCoreSchema()), $db->addQuotes($table), $db->addQuotes($field))); $row = $db->fetchObject($res); if (!$row) { return null; } $n = new PostgresField(); $n->type = $row->typname; $n->nullable = $row->attnotnull == 'f'; $n->name = $field; $n->tablename = $table; $n->max_length = $row->attlen; $n->deferrable = $row->deferrable == 't'; $n->deferred = $row->deferred == 't'; $n->conname = $row->conname; $n->has_default = $row->atthasdef === 't'; $n->default = $row->adsrc; return $n; }
protected function doCollationUpdate() { global $wgCategoryCollation; if ($this->db->selectField('categorylinks', 'COUNT(*)', 'cl_collation != ' . $this->db->addQuotes($wgCategoryCollation), __METHOD__) == 0) { $this->output("...collations up-to-date.\n"); return; } $task = new UpdateCollation(); $task->execute(); }
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"; }
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 . ')'; }
/** * @param array $array array to sanitize * @return array */ protected function sqlSanitizeArray($array) { if ($array == null) { return null; } $resultArray = []; foreach ($array as $i => $v) { $resultArray[] = $this->databaseConnection->addQuotes($v); } return $resultArray; }
/** * Update CategoryLinks collation */ protected function doCollationUpdate() { global $wgCategoryCollation; if ($this->db->selectField('categorylinks', 'COUNT(*)', 'cl_collation != ' . $this->db->addQuotes($wgCategoryCollation), __METHOD__) == 0) { $this->output("...collations up-to-date.\n"); return; } $this->output("Updating category collations..."); $task = $this->maintenance->runChild('UpdateCollation'); $task->execute(); $this->output("...done.\n"); }
/** * @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"); }
/** * Copy all rows from $srcTable to $dstTable */ function sync($srcTable, $dstTable) { $batchSize = 1000; $minTs = $this->dbw->selectField($srcTable, 'MIN(log_timestamp)', false, __METHOD__); $minTsUnix = wfTimestamp(TS_UNIX, $minTs); $numRowsCopied = 0; while (true) { $maxTs = $this->dbw->selectField($srcTable, 'MAX(log_timestamp)', false, __METHOD__); $copyPos = $this->dbw->selectField($dstTable, 'MAX(log_timestamp)', false, __METHOD__); $maxTsUnix = wfTimestamp(TS_UNIX, $maxTs); $copyPosUnix = wfTimestamp(TS_UNIX, $copyPos); if ($copyPos === null) { $percent = 0; } else { $percent = ($copyPosUnix - $minTsUnix) / ($maxTsUnix - $minTsUnix) * 100; } printf("%s %.2f%%\n", $copyPos, $percent); # Handle all entries with timestamp equal to $copyPos if ($copyPos !== null) { $numRowsCopied += $this->copyExactMatch($srcTable, $dstTable, $copyPos); } # Now copy a batch of rows if ($copyPos === null) { $conds = false; } else { $conds = array('log_timestamp > ' . $this->dbw->addQuotes($copyPos)); } $srcRes = $this->dbw->select($srcTable, '*', $conds, __METHOD__, array('LIMIT' => $batchSize, 'ORDER BY' => 'log_timestamp')); if (!$srcRes->numRows()) { # All done break; } $batch = array(); foreach ($srcRes as $srcRow) { $batch[] = (array) $srcRow; } $this->dbw->insert($dstTable, $batch, __METHOD__); $numRowsCopied += count($batch); wfWaitForSlaves(); } echo "Copied {$numRowsCopied} rows\n"; }
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; }
/** * @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 { // https://bugzilla.wikimedia.org/show_bug.cgi?id=52868 //$dbw->delete( // $table, // array( $column => $wiki ), // __METHOD__, // array( 'LIMIT' => 500 ), //); $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"); }
/** * Create some initial DB entries for important built-in properties. Having the DB contents predefined * allows us to safe DB calls when certain data is needed. At the same time, the entries in the DB * make sure that DB-based functions work as with all other properties. */ protected function setupPredefinedProperties($verbose, DatabaseBase $db) { global $wgDBtype; $this->reportProgress("Setting up internal property indices ...\n", $verbose); // Check if we already have this structure $borderiw = $db->selectField(SMWSQLStore3::ID_TABLE, 'smw_iw', 'smw_id=' . $db->addQuotes(\SMWSql3SmwIds::FXD_PROP_BORDER_ID)); if ($borderiw != SMW_SQL3_SMWBORDERIW) { $this->reportProgress(" ... allocating space for internal properties ...\n", $verbose); $this->store->smwIds->moveSMWPageID(\SMWSql3SmwIds::FXD_PROP_BORDER_ID); // make sure position 50 is empty $db->insert(SMWSQLStore3::ID_TABLE, array('smw_id' => \SMWSql3SmwIds::FXD_PROP_BORDER_ID, 'smw_title' => '', 'smw_namespace' => 0, 'smw_iw' => SMW_SQL3_SMWBORDERIW, 'smw_subobject' => '', 'smw_sortkey' => ''), 'SMW::setup'); // put dummy "border element" on index 50 $this->reportProgress(' ', $verbose); for ($i = 0; $i < \SMWSql3SmwIds::FXD_PROP_BORDER_ID; $i++) { // make way for built-in ids $this->store->smwIds->moveSMWPageID($i); $this->reportProgress('.', $verbose); } $this->reportProgress(" done.\n", $verbose); } else { $this->reportProgress(" ... space for internal properties already allocated.\n", $verbose); } // now write actual properties; do that each time, it is cheap enough and we can update sortkeys by current language $this->reportProgress(" ... writing entries for internal properties ...", $verbose); foreach (SMWSql3SmwIds::$special_ids as $prop => $id) { $p = new SMWDIProperty($prop); $db->replace(SMWSQLStore3::ID_TABLE, array('smw_id'), array('smw_id' => $id, 'smw_title' => $p->getKey(), 'smw_namespace' => SMW_NS_PROPERTY, 'smw_iw' => $this->store->smwIds->getPropertyInterwiki($p), 'smw_subobject' => '', 'smw_sortkey' => $p->getLabel()), 'SMW::setup'); } $this->reportProgress(" done.\n", $verbose); if ($wgDBtype == 'postgres') { $sequenceIndex = SMWSQLStore3::ID_TABLE . '_smw_id_seq'; $this->reportProgress(" ... updating {$sequenceIndex} sequence accordingly.\n", $verbose); $max = $db->selectField(SMWSQLStore3::ID_TABLE, 'max(smw_id)', array(), __METHOD__); $max += 1; $db->query("ALTER SEQUENCE {$sequenceIndex} RESTART WITH {$max}", __METHOD__); } $this->reportProgress("Internal properties initialized successfully.\n", $verbose); }
/** * Update the query cache as needed * * @param DatabaseBase $dbw * @param int $days How many days user must be idle before he is considered inactive * @param int $window Maximum time range of new data to scan (in seconds) * @return int|bool UNIX timestamp the cache is now up-to-date as of (false on error) */ protected static function doQueryCacheUpdate(DatabaseBase $dbw, $days, $window) { $lockKey = wfWikiID() . '-activeusers'; if (!$dbw->lock($lockKey, __METHOD__, 1)) { return false; // exclusive update (avoids duplicate entries) } $now = time(); $cTime = $dbw->selectField('querycache_info', 'qci_timestamp', array('qci_type' => 'activeusers')); $cTimeUnix = $cTime ? wfTimestamp(TS_UNIX, $cTime) : 1; // Pick the date range to fetch from. This is normally from the last // update to till the present time, but has a limited window for sanity. // If the window is limited, multiple runs are need to fully populate it. $sTimestamp = max($cTimeUnix, $now - $days * 86400); $eTimestamp = min($sTimestamp + $window, $now); // Get all the users active since the last update $res = $dbw->select(array('recentchanges'), array('rc_user_text', 'lastedittime' => 'MAX(rc_timestamp)'), array('rc_user > 0', 'rc_type != ' . $dbw->addQuotes(RC_EXTERNAL), 'rc_log_type IS NULL OR rc_log_type != ' . $dbw->addQuotes('newusers'), 'rc_timestamp >= ' . $dbw->addQuotes($dbw->timestamp($sTimestamp)), 'rc_timestamp <= ' . $dbw->addQuotes($dbw->timestamp($eTimestamp))), __METHOD__, array('GROUP BY' => array('rc_user_text'), 'ORDER BY' => 'NULL')); $names = array(); foreach ($res as $row) { $names[$row->rc_user_text] = $row->lastedittime; } // Rotate out users that have not edited in too long (according to old data set) $dbw->delete('querycachetwo', array('qcc_type' => 'activeusers', 'qcc_value < ' . $dbw->addQuotes($now - $days * 86400)), __METHOD__); // Find which of the recently active users are already accounted for if (count($names)) { $res = $dbw->select('querycachetwo', array('user_name' => 'qcc_title'), array('qcc_type' => 'activeusers', 'qcc_namespace' => NS_USER, 'qcc_title' => array_keys($names)), __METHOD__); foreach ($res as $row) { unset($names[$row->user_name]); } } // Insert the users that need to be added to the list (which their last edit time if (count($names)) { $newRows = array(); foreach ($names as $name => $lastEditTime) { $newRows[] = array('qcc_type' => 'activeusers', 'qcc_namespace' => NS_USER, 'qcc_title' => $name, 'qcc_value' => wfTimestamp(TS_UNIX, $lastEditTime), 'qcc_namespacetwo' => 0, 'qcc_titletwo' => ''); } foreach (array_chunk($newRows, 500) as $rowBatch) { $dbw->insert('querycachetwo', $rowBatch, __METHOD__); if (!$dbw->trxLevel()) { wfWaitForSlaves(); } } } // Touch the data freshness timestamp $dbw->replace('querycache_info', array('qci_type'), array('qci_type' => 'activeusers', 'qci_timestamp' => $dbw->timestamp($eTimestamp)), __METHOD__); $dbw->unlock($lockKey, __METHOD__); return $eTimestamp; }
/** * @param DatabaseBase $db * @param string $ip * @param string|bool $xfor * @return mixed array/false conditions */ public static function getIpConds($db, $ip, $xfor = false) { $type = $xfor ? 'xff' : 'ip'; // IPv4 CIDR, 16-32 bits $matches = array(); if (preg_match('#^(\\d+\\.\\d+\\.\\d+\\.\\d+)/(\\d+)$#', $ip, $matches)) { if ($matches[2] < 16 || $matches[2] > 32) { return false; // invalid } list($start, $end) = IP::parseRange($ip); return array('cuc_' . $type . '_hex BETWEEN ' . $db->addQuotes($start) . ' AND ' . $db->addQuotes($end)); } elseif (preg_match('#^\\w{1,4}:\\w{1,4}:\\w{1,4}:\\w{1,4}:\\w{1,4}:\\w{1,4}:\\w{1,4}:\\w{1,4}/(\\d+)$#', $ip, $matches)) { // IPv6 CIDR, 64-128 bits if ($matches[1] < 64 || $matches[1] > 128) { return false; // invalid } list($start, $end) = IP::parseRange($ip); return array('cuc_' . $type . '_hex BETWEEN ' . $db->addQuotes($start) . ' AND ' . $db->addQuotes($end)); } elseif (preg_match('#^(\\d+)\\.(\\d+)\\.(\\d+)\\.(\\d+)$#', $ip)) { // 32 bit IPv4 $ip_hex = IP::toHex($ip); return array('cuc_' . $type . '_hex' => $ip_hex); } elseif (preg_match('#^\\w{1,4}:\\w{1,4}:\\w{1,4}:\\w{1,4}:\\w{1,4}:\\w{1,4}:\\w{1,4}:\\w{1,4}$#', $ip)) { // 128 bit IPv6 $ip_hex = IP::toHex($ip); return array('cuc_' . $type . '_hex' => $ip_hex); } // throw away this query, incomplete IP, these don't get through the entry point anyway return false; // invalid }
/** * @param string $s * @return string */ public function addQuotes($s) { if ($s instanceof MssqlBlob) { return $s->fetch(); } elseif ($s instanceof Blob) { // this shouldn't really ever be called, but it's here if needed // (and will quite possibly make the SQL error out) $blob = new MssqlBlob($s->fetch()); return $blob->fetch(); } else { if (is_bool($s)) { $s = $s ? 1 : 0; } return parent::addQuotes($s); } }
protected function populateRevisionOrArchive(DatabaseBase $dbw, $table, $ns) { $prefix = $table === 'archive' ? 'ar' : 'rev'; $model_column = "{$prefix}_content_model"; $format_column = "{$prefix}_content_format"; $key = "{$prefix}_id"; if ($table === 'archive') { $selectTables = 'archive'; $fields = array('ar_namespace', 'ar_title'); $join_conds = array(); $where = $ns === 'all' ? array() : array('ar_namespace' => $ns); } else { // revision $selectTables = array('revision', 'page'); $fields = array('page_title', 'page_namespace'); $join_conds = array('page' => array('INNER JOIN', 'rev_page=page_id')); $where = $ns === 'all' ? array() : array('page_namespace' => $ns); } $toSave = array(); $lastId = 0; do { $rows = $dbw->select($selectTables, array_merge($fields, array($model_column, $format_column, $key)), array($model_column => null, "{$key} > " . $dbw->addQuotes($lastId)) + $where, __METHOD__, array('LIMIT' => $this->mBatchSize, 'ORDER BY' => "{$key} ASC"), $join_conds); $this->output("Fetched {$rows->numRows()} rows.\n"); foreach ($rows as $row) { if ($table === 'archive') { $title = Title::makeTitle($row->ar_namespace, $row->ar_title); } else { $title = Title::newFromRow($row); } $lastId = $row->{$key}; try { $handler = ContentHandler::getForTitle($title); } catch (MWException $e) { $this->error("Invalid content model for {$title}"); continue; } $defaultModel = $handler->getModelID(); $defaultFormat = $handler->getDefaultFormat(); $dbModel = $row->{$model_column}; $dbFormat = $row->{$format_column}; $id = $row->{$key}; if ($dbModel === null && $dbFormat === null) { // Set the defaults $toSave[$defaultModel][] = $row->{$key}; } else { // $dbModel === null, $dbFormat set. if ($dbFormat === $defaultFormat) { $toSave[$defaultModel][] = $row->{$key}; } else { // non-default format, just update now $this->output("Updating model to match format for {$table} {$id} of {$title}... "); $dbw->update($table, array($model_column => $defaultModel), array($key => $id), __METHOD__); wfWaitForSlaves(); $this->output("done.\n"); continue; } } if (count($toSave[$defaultModel]) >= $this->mBatchSize) { $this->updateRevisionOrArchiveRows($dbw, $toSave[$defaultModel], $defaultModel, $table); unset($toSave[$defaultModel]); } } } while ($rows->numRows() >= $this->mBatchSize); foreach ($toSave as $model => $ids) { $this->updateRevisionOrArchiveRows($dbw, $ids, $model, $table); } }
/** * Change the usage count for the property of the given ID by the given * value. The method does nothing if the count is 0. * * @since 1.8 * @param integer $propertyId * @param integer $value * @param DatabaseBase $dbw used for writing * @return boolean success indicator */ protected function addToPropertyUsageCount($propertyId, $value, DatabaseBase $dbw) { if ($value == 0) { return true; } return $dbw->update(SMWSQLStore3::PROPERTY_STATISTICS_TABLE, array('usage_count = usage_count + ' . $dbw->addQuotes($value)), array('p_id' => $propertyId), __METHOD__); }
function addQuotes($s) { if ($s instanceof Blob) { return "'" . $s->fetch($s) . "'"; } else { return parent::addQuotes($s); } }
/** * SQL clause to skip forbidden log types for this user * * @param DatabaseBase $db * @param string $audience Public/user * @param User $user User to check, or null to use $wgUser * @return string|bool String on success, false on failure. */ public static function getExcludeClause($db, $audience = 'public', User $user = null) { global $wgLogRestrictions; if ($audience != 'public' && $user === null) { global $wgUser; $user = $wgUser; } // Reset the array, clears extra "where" clauses when $par is used $hiddenLogs = array(); // Don't show private logs to unprivileged users foreach ($wgLogRestrictions as $logType => $right) { if ($audience == 'public' || !$user->isAllowed($right)) { $hiddenLogs[] = $logType; } } if (count($hiddenLogs) == 1) { return 'log_type != ' . $db->addQuotes($hiddenLogs[0]); } elseif ($hiddenLogs) { return 'log_type NOT IN (' . $db->makeList($hiddenLogs) . ')'; } return false; }
/** * Return an SQL expression selecting rows which sort above the given row, * assuming an ordering of cl_from, cl_to * @param stdClass $row * @param DatabaseBase $dbw * @return string */ function getBatchCondition($row, $dbw) { $fields = array('cl_from', 'cl_to'); $first = true; $cond = false; $prefix = false; foreach ($fields as $field) { $encValue = $dbw->addQuotes($row->{$field}); $inequality = "{$field} > {$encValue}"; $equality = "{$field} = {$encValue}"; if ($first) { $cond = $inequality; $prefix = $equality; $first = false; } else { $cond .= " OR ({$prefix} AND {$inequality})"; $prefix .= " AND {$equality}"; } } return $cond; }