117private $loadBalancer =
null;
120private $databaseProvider =
null;
123private $linkBatchFactory =
null;
140 [ SpecialAncientPages::class,
'Ancientpages' ],
141 [ SpecialBrokenRedirects::class,
'BrokenRedirects' ],
142 [ SpecialDeadendPages::class,
'Deadendpages' ],
143 [ SpecialDoubleRedirects::class,
'DoubleRedirects' ],
144 [ SpecialListDuplicatedFiles::class,
'ListDuplicatedFiles' ],
145 [ SpecialLinkSearch::class,
'LinkSearch' ],
146 [ SpecialListRedirects::class,
'Listredirects' ],
147 [ SpecialLonelyPages::class,
'Lonelypages' ],
148 [ SpecialLongPages::class,
'Longpages' ],
149 [ SpecialMediaStatistics::class,
'MediaStatistics', SpecialMediaStatistics::MAX_LIMIT ],
150 [ SpecialMIMESearch::class,
'MIMEsearch' ],
151 [ SpecialMostCategories::class,
'Mostcategories' ],
152 [ SpecialMostImages::class,
'Mostimages' ],
153 [ SpecialMostInterwikis::class,
'Mostinterwikis' ],
154 [ SpecialMostLinkedCategories::class,
'Mostlinkedcategories' ],
155 [ SpecialMostLinkedTemplates::class,
'Mostlinkedtemplates' ],
156 [ SpecialMostLinked::class,
'Mostlinked' ],
157 [ SpecialMostRevisions::class,
'Mostrevisions' ],
158 [ SpecialFewestRevisions::class,
'Fewestrevisions' ],
159 [ SpecialShortPages::class,
'Shortpages' ],
160 [ SpecialUncategorizedCategories::class,
'Uncategorizedcategories' ],
161 [ SpecialUncategorizedPages::class,
'Uncategorizedpages' ],
162 [ SpecialUncategorizedImages::class,
'Uncategorizedimages' ],
163 [ SpecialUncategorizedTemplates::class,
'Uncategorizedtemplates' ],
164 [ SpecialUnusedCategories::class,
'Unusedcategories' ],
165 [ SpecialUnusedImages::class,
'Unusedimages' ],
166 [ SpecialWantedCategories::class,
'Wantedcategories' ],
167 [ SpecialWantedFiles::class,
'Wantedfiles' ],
168 [ SpecialWantedPages::class,
'Wantedpages' ],
169 [ SpecialWantedTemplates::class,
'Wantedtemplates' ],
170 [ SpecialUnwatchedPages::class,
'Unwatchedpages' ],
171 [ SpecialUnusedTemplates::class,
'Unusedtemplates' ],
172 [ SpecialWithoutInterwiki::class,
'Withoutinterwiki' ],
185 $this->linkBatchFactory = $linkBatchFactory;
193if ( $this->linkBatchFactory === null ) {
194// Fallback if not provided 195// TODO Change to wfWarn in a future release 198return $this->linkBatchFactory;
209if ( !is_array( $disableQueryPageUpdate ) ) {
214foreach ( $disableQueryPageUpdate as $name => $runMode ) {
215if ( is_int( $name ) ) {
216// The run mode may be omitted 217 $pages[$runMode] =
'disabled';
219 $pages[$name] = $runMode;
231 $this->listoutput = $bool;
382 $this->getOutput()->addWikiMsg(
'specialpage-empty' );
407publicfunctionrecache( $limit, $unused =
true ) {
408if ( !$this->isCacheable() ) {
412 $fname = static::class .
'::recache';
413 $dbw = $this->getDatabaseProvider()->getPrimaryDatabase();
416 $res = $this->reallyDoQuery( $limit,
false );
419 $num = $res->numRows();
422foreach ( $res as $i => $row ) {
423if ( isset( $row->value ) ) {
424if ( $this->usesTimestamps() ) {
427 $value = intval( $row->value );
// T16414 434'qc_type' => $this->getName(),
435'qc_namespace' => $row->namespace,
436'qc_title' => $row->title,
441 $dbw->doAtomicSection(
444// Clear out any old cached data 446 ->deleteFrom(
'querycache' )
447 ->where( [
'qc_type' => $this->getName() ] )
448 ->caller( $fname )->execute();
449// Update the querycache_info record for the page 451 ->insertInto(
'querycache_info' )
452 ->row( [
'qci_type' => $this->getName(),
'qci_timestamp' => $dbw->
timestamp() ] )
453 ->onDuplicateKeyUpdate()
454 ->uniqueIndexFields( [
'qci_type' ] )
455 ->set( [
'qci_timestamp' => $dbw->
timestamp() ] )
456 ->caller( $fname )->execute();
459// Save results into the querycache table on the primary DB 460if ( count( $vals ) ) {
461foreach ( array_chunk( $vals, 500 ) as $chunk ) {
463 ->insertInto(
'querycache' )
465 ->caller( $fname )->execute();
479return $this->getDBLoadBalancer()
480 ->getConnection( ILoadBalancer::DB_REPLICA,
'vslow' );
490if ( $this->isCached() ) {
491 $dbw = $this->getDatabaseProvider()->getPrimaryDatabase();
492 $dbw->newDeleteQueryBuilder()
493 ->deleteFrom(
'querycache' )
495'qc_type' => $this->getName(),
496'qc_namespace' => $title->getNamespace(),
497'qc_title' => $title->getDBkey(),
499 ->caller( __METHOD__ )->execute();
509 $fname = static::class .
'::' . __FUNCTION__;
510 $dbw = $this->getDatabaseProvider()->getPrimaryDatabase();
511 $dbw->newDeleteQueryBuilder()
512 ->deleteFrom(
'querycache' )
513 ->where( [
'qc_type' => $this->getName() ] )
514 ->caller( $fname )->execute();
515 $dbw->newDeleteQueryBuilder()
516 ->deleteFrom(
'querycachetwo' )
517 ->where( [
'qcc_type' => $this->getName() ] )
518 ->caller( $fname )->execute();
519 $dbw->newDeleteQueryBuilder()
520 ->deleteFrom(
'querycache_info' )
521 ->where( [
'qci_type' => $this->getName() ] )
522 ->caller( $fname )->execute();
534 $fname = static::class .
'::reallyDoQuery';
535 $dbr = $this->getRecacheDB();
536 $query = $this->getQueryInfo();
537 $order = $this->getOrderFields();
539if ( $this->sortDescending() ) {
540foreach ( $order as &$field ) {
545 $tables = isset( $query[
'tables'] ) ? (array)$query[
'tables'] : [];
546 $fields = isset( $query[
'fields'] ) ? (array)$query[
'fields'] : [];
547 $conds = isset( $query[
'conds'] ) ? (array)$query[
'conds'] : [];
548 $options = isset( $query[
'options'] ) ? (array)$query[
'options'] : [];
549 $join_conds = isset( $query[
'join_conds'] ) ? (array)$query[
'join_conds'] : [];
551 $queryBuilder = $dbr->newSelectQueryBuilder()
556 ->options( $options )
557 ->joinConds( $join_conds );
559 $queryBuilder->orderBy( $order );
562if ( $limit !==
false ) {
563 $queryBuilder->limit( intval( $limit ) );
566if ( $offset !==
false ) {
567 $queryBuilder->offset( intval( $offset ) );
570return $queryBuilder->fetchResultSet();
579publicfunctiondoQuery( $offset =
false, $limit =
false ) {
580if ( $this->isCached() && $this->isCacheable() ) {
581return $this->fetchFromCache( $limit, $offset );
583return $this->reallyDoQuery( $limit, $offset );
597 $dbr = $this->getDatabaseProvider()->getReplicaDatabase();
598 $queryBuilder = $dbr->newSelectQueryBuilder()
599 ->select( [
'qc_type',
'namespace' =>
'qc_namespace',
'title' =>
'qc_title',
'value' =>
'qc_value' ] )
600 ->from(
'querycache' )
601 ->where( [
'qc_type' => $this->getName() ] );
603if ( $limit !==
false ) {
604 $queryBuilder->limit( intval( $limit ) );
607if ( $offset !==
false ) {
608 $queryBuilder->offset( intval( $offset ) );
611 $order = $this->getCacheOrderFields();
612if ( $this->sortDescending() ) {
613 $queryBuilder->orderBy( $order, SelectQueryBuilder::SORT_DESC );
615 $queryBuilder->orderBy( $order );
618return $queryBuilder->caller( __METHOD__ )->fetchResultSet();
636if ( $this->cachedTimestamp ===
null ) {
637 $dbr = $this->getDatabaseProvider()->getReplicaDatabase();
638 $fname = static::class .
'::getCachedTimestamp';
639 $this->cachedTimestamp = $dbr->newSelectQueryBuilder()
640 ->select(
'qci_timestamp' )
641 ->from(
'querycache_info' )
642 ->where( [
'qci_type' => $this->getName() ] )
643 ->caller( $fname )->fetchField();
645return $this->cachedTimestamp;
661 [ $limit, $offset ] = $this->getRequest()
662 ->getLimitOffsetForUser( $this->getUser() );
664 $maxResults = $this->getMaxResults();
665// Can't display more than max results on a page 666 $limit = min( $limit, $maxResults );
667// Can't skip over more than the end of $maxResults 668 $offset = min( $offset, $maxResults + 1 );
670return [ $limit, $offset ];
682 $maxResults = $this->getMaxResults();
684 $limit = min( $uiLimit + 1, $maxResults - $uiOffset );
685return max( $limit, 0 );
702// Max of 10000, unless we store more than 10000 in query cache. 713 $this->checkPermissions();
716 $this->outputHeader();
718 $out = $this->getOutput();
720if ( $this->isCached() && !$this->isCacheable() ) {
721 $out->addWikiMsg(
'querypage-disabled' );
725 $out->setSyndicated( $this->isSyndicated() );
727if ( $this->limit == 0 && $this->offset == 0 ) {
728 [ $this->limit, $this->offset ] = $this->getLimitOffset();
730 $dbLimit = $this->getDBLimit( $this->limit, $this->offset );
731// @todo Use doQuery() 732if ( !$this->isCached() ) {
733// select one extra row for navigation 734 $res = $this->reallyDoQuery( $dbLimit, $this->offset );
736// Get the cached result, select one extra row for navigation 737 $res = $this->fetchFromCache( $dbLimit, $this->offset );
738if ( !$this->listoutput ) {
739// Fetch the timestamp of this update 740 $ts = $this->getCachedTimestamp();
741 $lang = $this->getLanguage();
742 $maxResults = $lang->formatNum( $this->getConfig()->
get(
746 $user = $this->getUser();
747 $updated = $lang->userTimeAndDate( $ts, $user );
748 $updateddate = $lang->userDate( $ts, $user );
749 $updatedtime = $lang->userTime( $ts, $user );
750 $out->addMeta(
'Data-Cache-Time', $ts );
751 $out->addJsConfigVars(
'dataCacheTime', $ts );
752 $out->addWikiMsg(
'perfcachedts', $updated, $updateddate, $updatedtime, $maxResults );
754 $out->addWikiMsg(
'perfcached', $maxResults );
757// If updates on this page have been disabled, let the user know 758// that the data set won't be refreshed for now 759 $disabledQueryPages = self::getDisabledQueryPages( $this->getConfig() );
760if ( isset( $disabledQueryPages[$this->getName()] ) ) {
761 $runMode = $disabledQueryPages[$this->getName()];
762if ( $runMode ===
'disabled' ) {
764"<div class=\"mw-querypage-no-updates\">\n$1\n</div>",
765'querypage-no-updates' 768// Messages used here: querypage-updates-periodical 770"<div class=\"mw-querypage-updates-" . $runMode .
"\">\n$1\n</div>",
771'querypage-updates-' . $runMode
778 $this->numRows = $res->numRows();
780 $dbr = $this->getRecacheDB();
781 $this->preprocessResults( $dbr, $res );
783 $out->addHTML( Html::openElement(
'div', [
'class' =>
'mw-spcontent' ] ) );
785// Top header and navigation 786if ( $this->shownavigation ) {
787 $out->addHTML( $this->getPageHeader() );
788if ( $this->numRows > 0 ) {
789 $out->addHTML( $this->msg(
'showingresultsinrange' )->numParams(
790 min( $this->numRows, $this->limit ),
// do not show the one extra row, if exist 791 $this->offset + 1, ( min( $this->numRows, $this->limit ) + $this->offset ) )->parseAsBlock() );
792// Disable the "next" link when we reach the end 794 && ( $this->offset + $this->limit >= $this->getMaxResults() );
795 $atEnd = ( $this->numRows <= $this->limit ) || $miserMaxResults;
796 $paging = $this->buildPrevNextNavigation( $this->offset,
797 $this->limit, $this->linkParameters(), $atEnd, $par );
798 $out->addHTML(
'<p>' . $paging .
'</p>' );
800// No results to show, so don't bother with "showing X of Y" etc. 801// -- just let the user know and give up now 802 $this->showEmptyText();
803 $out->addHTML( Html::closeElement(
'div' ) );
808// The actual results; specialist subclasses will want to handle this 809// with more than a straight list, so we hand them the info, plus 810// an OutputPage, and let them get on with it 811 $this->outputResults( $out,
813 $dbr,
// Should use IResultWrapper for this 815 min( $this->numRows, $this->limit ),
// do not format the one extra row, if exist 818// Repeat the paging links at the bottom 819if ( $this->shownavigation ) {
820// @phan-suppress-next-line PhanPossiblyUndeclaredVariable paging is set when used here 821 $out->addHTML(
'<p>' . $paging .
'</p>' );
824 $out->addHTML( Html::closeElement(
'div' ) );
843if ( !$this->listoutput ) {
844 $html[] = $this->openList( $offset );
847// $res might contain the whole 1,000 rows, so we read up to 848// $num [should update this to use a Pager] 849// phpcs:ignore Generic.CodeAnalysis.AssignmentInCondition.Found 850for ( $i = 0; $i < $num && $row = $res->fetchObject(); $i++ ) {
851 $line = $this->formatResult( $skin, $row );
853 $html[] = $this->listoutput
855 :
"<li>{$line}</li>\n";
859if ( !$this->listoutput ) {
860 $html[] = $this->closeList();
863 $html = $this->listoutput
864 ? $this->getContentLanguage()->listToText( $html )
865 : implode(
'', $html );
867 $out->addHTML( $html );
876return"\n<ol start='" . ( $offset + 1 ) .
"' class='special'>\n";
912 $batch = $this->getLinkBatchFactory()->newLinkBatch()->setCaller( __METHOD__ );
913foreach ( $res as $row ) {
914 $batch->add( $ns ?? (
int)$row->namespace, $row->title );
927 $this->loadBalancer = $loadBalancer;
936if ( $this->loadBalancer === null ) {
937// Fallback if not provided 938// TODO Change to wfWarn in a future release 941return $this->loadBalancer;
949 $this->databaseProvider = $databaseProvider;
957if ( $this->databaseProvider === null ) {
958 $this->databaseProvider = MediaWikiServices::getInstance()->getConnectionProvider();
960return $this->databaseProvider;
965class_alias( QueryPage::class,
'QueryPage' );
wfTimestamp( $outputtype=TS_UNIX, $ts=0)
Get a timestamp string in one of various formats.
if(!defined('MW_SETUP_CALLBACK'))
This class provides an implementation of the core hook interfaces, forwarding hook calls to HookConta...
This class is a collection of static functions that serve two purposes:
A class containing constants representing the names of configuration variables.
const DisableQueryPages
Name constant for the DisableQueryPages setting, for use with Config::get()
const QueryCacheLimit
Name constant for the QueryCacheLimit setting, for use with Config::get()
const MiserMode
Name constant for the MiserMode setting, for use with Config::get()
const DisableQueryPageUpdate
Name constant for the DisableQueryPageUpdate setting, for use with Config::get()
Service locator for MediaWiki core services.
static getInstance()
Returns the global default instance of the top level service locator.
This is one of the Core classes and should be read at least once by any new developers.
The base class for all skins.
This is a class for doing query pages; since they're almost all the same, we factor out some of the f...
getPageHeader()
The content returned by this function will be output before any result.
linkParameters()
If using extra form wheely-dealies, return a set of parameters here as an associative array.
bool $shownavigation
Whether to show prev/next links.
bool $listoutput
Whether or not we want plain listoutput rather than an ordered list.
getOrderFields()
Subclasses return an array of fields to order by here.
preprocessResults( $db, $res)
Do any necessary preprocessing of the result object.
setDatabaseProvider(IConnectionProvider $databaseProvider)
getLimitOffset()
Returns limit and offset, as returned by $this->getRequest()->getLimitOffsetForUser().
int $offset
The offset and limit in use, as passed to the query() function.
getDBLimit( $uiLimit, $uiOffset)
What is limit to fetch from DB.
isCached()
Whether or not the output of the page in question is retrieved from the database cache.
execute( $par)
This is the actual workhorse.
setListoutput( $bool)
A mutator for $this->listoutput;.
executeLBFromResultWrapper(IResultWrapper $res, $ns=null)
Creates a new LinkBatch object, adds all pages from the passed result wrapper (MUST include title and...
getMaxResults()
Get max number of results we can return in miser mode.
isExpensive()
Should this query page only be updated offline on large wikis?
static getPages()
Get a list of query page classes and their associated special pages, for periodic updates.
fetchFromCache( $limit, $offset=false)
Fetch the query results from the query cache.
setDBLoadBalancer(ILoadBalancer $loadBalancer)
string null false $cachedTimestamp
deleteAllCachedData()
Remove all cached value This is needed when the page is no longer using the cache.
showEmptyText()
Outputs some kind of an informative message (via OutputPage) to let the user know that the query retu...
doQuery( $offset=false, $limit=false)
Somewhat deprecated, you probably want to be using execute()
outputResults( $out, $skin, $dbr, $res, $num, $offset)
Format and output report results using the given information plus OutputPage.
getRecacheDB()
Get a DB connection to be used for slow recache queries.
static getDisabledQueryPages(Config $config)
Get a list of disabled query pages and their run mode.
sortDescending()
Override to sort by increasing values.
int $numRows
The number of rows returned by the query.
recache( $limit, $unused=true)
Clear the cache and save new results.
usesTimestamps()
Does this query return timestamps rather than integers in its 'value' field? If true,...
isSyndicated()
Sometimes we don't want to build rss / atom feeds.
getQueryInfo()
Subclasses return an SQL query here, formatted as an array with the following keys: tables => Table(s...
setLinkBatchFactory(LinkBatchFactory $linkBatchFactory)
formatResult( $skin, $result)
Formats the results of the query for display.
getCacheOrderFields()
Return the order fields for fetchFromCache.
isCacheable()
Is the output of this query cacheable? Non-cacheable expensive pages will be disabled in miser mode a...
reallyDoQuery( $limit, $offset=false)
Run the query and return the result.
Parent class for all special pages.
Implements Special:Ancientpages.
List of redirects to non-existent pages.
List of pages that contain no links to other pages.
List of redirects to another redirecting page.
List articles with the fewest revisions.
Special:LinkSearch to search the external-links table.
List all files where the current version is a duplicate of the current version of another file.
Lists all the redirecting pages on the wiki.
List of articles with no article linking to them, thus being lonely.
Implements Special:Longpages.
Search the database for files of the requested MIME type, comparing this with the 'img_major_mime' an...
Implements Special:MediaStatistics.
List of pages that have the highest category count.
List of the most used images.
List of pages that have the highest interwiki count.
List of categories with the most pages in them.
List of templates with a large number of transclusion links, i.e.
List of pages ordered by the number of pages linking to them.
Implements Special:Mostrevisions.
List of the shortest pages in the database.
List of uncategorized category pages.
List of file pages which haven't been categorised.
List of pages without any category.
List of all uncategorised pages in the Template namespace.
Implements Special:Unusedcategories.
Lists of unused templates.
List of pages that are not on anyone's watchlist.
List of the most wanted categories.
List of the most linked non-existent files.
List of the most-linked pages that do not exist.
List of the most wanted templates.
List of pages without any language links.
Database error base class.
Build SELECT queries with a fluent interface.
Interface for configuration instances.
get( $name)
Get a configuration variable such as "Sitename" or "UploadMaintenance.".
Represents the target of a wiki link.
Provide primary and replica IDatabase connections.
Interface to a relational database.
newDeleteQueryBuilder()
Get an DeleteQueryBuilder bound to this connection.
newInsertQueryBuilder()
Get an InsertQueryBuilder bound to this connection.
This class is a delegate to ILBFactory for a given database cluster.
A database connection without write operations.
Result wrapper for grabbing data queried from an IDatabase object.
numRows()
Get the number of rows in a result object.
seek( $pos)
Change the position of the cursor in a result object.
timestamp( $ts=0)
Convert a timestamp in one of the formats accepted by ConvertibleTimestamp to the format used for ins...