/** * 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 = []; foreach ($this->primaryKey as $column) { $maximumValues[$column] = $this->db->addQuotes($maxRow->{$column}); } $pkConditions = []; // 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; }
/** * Invalidate the cache of a list of pages from a single namespace. * This is intended for use by subclasses. * * @param IDatabase $dbw * @param int $namespace Namespace number * @param array $dbkeys */ public static function invalidatePages(IDatabase $dbw, $namespace, array $dbkeys) { if ($dbkeys === []) { return; } $dbw->onTransactionIdle(function () use($dbw, $namespace, $dbkeys) { $services = MediaWikiServices::getInstance(); $lbFactory = $services->getDBLoadBalancerFactory(); // Determine which pages need to be updated. // This is necessary to prevent the job queue from smashing the DB with // large numbers of concurrent invalidations of the same page. $now = $dbw->timestamp(); $ids = $dbw->selectFieldValues('page', 'page_id', ['page_namespace' => $namespace, 'page_title' => $dbkeys, 'page_touched < ' . $dbw->addQuotes($now)], __METHOD__); if (!$ids) { return; } $batchSize = $services->getMainConfig()->get('UpdateRowsPerQuery'); $ticket = $lbFactory->getEmptyTransactionTicket(__METHOD__); foreach (array_chunk($ids, $batchSize) as $idBatch) { $dbw->update('page', ['page_touched' => $now], ['page_id' => $idBatch, 'page_touched < ' . $dbw->addQuotes($now)], __METHOD__); $lbFactory->commitAndWaitForReplication(__METHOD__, $ticket); } }, __METHOD__); }
/** * SQL clause to skip forbidden log types for this user * * @param IDatabase $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; }
/** * Build a SQL expression for a closed interval (i.e. BETWEEN). * * By specifying a null $start or $end, it is also possible to create * half-bounded or unbounded intervals using this function. * * @param IDatabase $db Database connection * @param string $var Field name * @param mixed $start First value to include or null * @param mixed $end Last value to include or null */ private static function intervalCond(IDatabase $db, $var, $start, $end) { if ($start === null && $end === null) { return "{$var} IS NOT NULL"; } elseif ($end === null) { return "{$var} >= {$db->addQuotes($start)}"; } elseif ($start === null) { return "{$var} <= {$db->addQuotes($end)}"; } else { return "{$var} BETWEEN {$db->addQuotes($start)} AND {$db->addQuotes($end)}"; } }
/** * Generates condition for the query used in a batch count visiting watchers. * * @param IDatabase $db * @param array $targetsWithVisitThresholds array of pairs (LinkTarget, last visit threshold) * @return string */ private function getVisitingWatchersCondition(IDatabase $db, array $targetsWithVisitThresholds) { $missingTargets = []; $namespaceConds = []; foreach ($targetsWithVisitThresholds as list($target, $threshold)) { if ($threshold === null) { $missingTargets[] = $target; continue; } /* @var LinkTarget $target */ $namespaceConds[$target->getNamespace()][] = $db->makeList(['wl_title = ' . $db->addQuotes($target->getDBkey()), $db->makeList(['wl_notificationtimestamp >= ' . $db->addQuotes($db->timestamp($threshold)), 'wl_notificationtimestamp IS NULL'], LIST_OR)], LIST_AND); } $conds = []; foreach ($namespaceConds as $namespace => $pageConds) { $conds[] = $db->makeList(['wl_namespace = ' . $namespace, '(' . $db->makeList($pageConds, LIST_OR) . ')'], LIST_AND); } if ($missingTargets) { $lb = new LinkBatch($missingTargets); $conds[] = $lb->constructSet('wl', $db); } return $db->makeList($conds, LIST_OR); }
/** * Creates a query condition part for getting only items before or after the given link target * (while ordering using $sort mode) * * @param IDatabase $db * @param LinkTarget $target * @param string $op comparison operator to use in the conditions * @return string */ private function getFromUntilTargetConds(IDatabase $db, LinkTarget $target, $op) { return $db->makeList(["wl_namespace {$op} " . $target->getNamespace(), $db->makeList(['wl_namespace = ' . $target->getNamespace(), "wl_title {$op}= " . $db->addQuotes($target->getDBkey())], LIST_AND)], LIST_OR); }