MediaWiki REL1_39
QueryPage.php
Go to the documentation of this file.
1<?php
32
42abstract class QueryPage extends SpecialPage {
44 protected $listoutput = false;
45
47 protected $offset = 0;
48
50 protected $limit = 0;
51
59 protected $numRows;
60
64 protected $cachedTimestamp = null;
65
69 protected $shownavigation = true;
70
72 private $loadBalancer = null;
73
75 private $linkBatchFactory = null;
76
87 public static function getPages() {
88 static $qp = null;
89
90 if ( $qp === null ) {
91 $qp = [
92 [ SpecialAncientPages::class, 'Ancientpages' ],
93 [ SpecialBrokenRedirects::class, 'BrokenRedirects' ],
94 [ SpecialDeadendPages::class, 'Deadendpages' ],
95 [ SpecialDoubleRedirects::class, 'DoubleRedirects' ],
96 [ SpecialListDuplicatedFiles::class, 'ListDuplicatedFiles' ],
97 [ SpecialLinkSearch::class, 'LinkSearch' ],
98 [ SpecialListRedirects::class, 'Listredirects' ],
99 [ SpecialLonelyPages::class, 'Lonelypages' ],
100 [ SpecialLongPages::class, 'Longpages' ],
101 [ SpecialMediaStatistics::class, 'MediaStatistics', SpecialMediaStatistics::MAX_LIMIT ],
102 [ SpecialMIMESearch::class, 'MIMEsearch' ],
103 [ SpecialMostCategories::class, 'Mostcategories' ],
104 [ MostimagesPage::class, 'Mostimages' ],
105 [ SpecialMostInterwikis::class, 'Mostinterwikis' ],
106 [ SpecialMostLinkedCategories::class, 'Mostlinkedcategories' ],
107 [ SpecialMostLinkedTemplates::class, 'Mostlinkedtemplates' ],
108 [ SpecialMostLinked::class, 'Mostlinked' ],
109 [ SpecialMostRevisions::class, 'Mostrevisions' ],
110 [ SpecialFewestRevisions::class, 'Fewestrevisions' ],
111 [ SpecialShortPages::class, 'Shortpages' ],
112 [ SpecialUncategorizedCategories::class, 'Uncategorizedcategories' ],
113 [ SpecialUncategorizedPages::class, 'Uncategorizedpages' ],
114 [ SpecialUncategorizedImages::class, 'Uncategorizedimages' ],
115 [ SpecialUncategorizedTemplates::class, 'Uncategorizedtemplates' ],
116 [ SpecialUnusedCategories::class, 'Unusedcategories' ],
117 [ SpecialUnusedImages::class, 'Unusedimages' ],
118 [ SpecialWantedCategories::class, 'Wantedcategories' ],
119 [ WantedFilesPage::class, 'Wantedfiles' ],
120 [ WantedPagesPage::class, 'Wantedpages' ],
121 [ SpecialWantedTemplates::class, 'Wantedtemplates' ],
122 [ SpecialUnwatchedPages::class, 'Unwatchedpages' ],
123 [ SpecialUnusedTemplates::class, 'Unusedtemplates' ],
124 [ SpecialWithoutInterwiki::class, 'Withoutinterwiki' ],
125 ];
126 Hooks::runner()->onWgQueryPages( $qp );
127 }
128
129 return $qp;
130 }
131
136 final protected function setLinkBatchFactory( LinkBatchFactory $linkBatchFactory ) {
137 $this->linkBatchFactory = $linkBatchFactory;
138 }
139
144 final protected function getLinkBatchFactory(): LinkBatchFactory {
145 if ( $this->linkBatchFactory === null ) {
146 // Fallback if not provided
147 // TODO Change to wfWarn in a future release
148 $this->linkBatchFactory = MediaWikiServices::getInstance()->getLinkBatchFactory();
149 }
150 return $this->linkBatchFactory;
151 }
152
158 public static function getDisabledQueryPages( Config $config ) {
159 $disableQueryPageUpdate = $config->get( MainConfigNames::DisableQueryPageUpdate );
160
161 if ( !is_array( $disableQueryPageUpdate ) ) {
162 return [];
163 }
164
165 $pages = [];
166 foreach ( $disableQueryPageUpdate as $name => $runMode ) {
167 if ( is_int( $name ) ) {
168 // The run mode may be omitted
169 $pages[$runMode] = 'disabled';
170 } else {
171 $pages[$name] = $runMode;
172 }
173 }
174 return $pages;
175 }
176
182 protected function setListoutput( $bool ) {
183 $this->listoutput = $bool;
184 }
185
213 public function getQueryInfo() {
214 // @phan-suppress-next-line PhanTypeMismatchReturnProbablyReal null needed for b/c checks
215 return null;
216 }
217
225 protected function getSQL() {
226 wfDeprecated( __METHOD__, '1.39' );
227 throw new MWException( "Bug in a QueryPage: doesn't implement getQueryInfo() nor "
228 . "getQuery() properly" );
229 }
230
239 protected function getOrderFields() {
240 return [ 'value' ];
241 }
242
254 public function usesTimestamps() {
255 return false;
256 }
257
264 protected function sortDescending() {
265 return true;
266 }
267
288 public function isExpensive() {
289 return $this->getConfig()->get( MainConfigNames::DisableQueryPages );
290 }
291
300 public function isCacheable() {
301 return true;
302 }
303
311 public function isCached() {
312 return $this->isExpensive() && $this->getConfig()->get( MainConfigNames::MiserMode );
313 }
314
321 public function isSyndicated() {
322 return true;
323 }
324
334 abstract protected function formatResult( $skin, $result );
335
342 protected function getPageHeader() {
343 return '';
344 }
345
353 protected function showEmptyText() {
354 $this->getOutput()->addWikiMsg( 'specialpage-empty' );
355 }
356
365 protected function linkParameters() {
366 return [];
367 }
368
379 public function recache( $limit, $ignoreErrors = true ) {
380 if ( !$this->isCacheable() ) {
381 return 0;
382 }
383
384 $fname = static::class . '::recache';
385 $dbw = $this->getDBLoadBalancer()->getConnectionRef( ILoadBalancer::DB_PRIMARY );
386
387 try {
388 // Do query
389 $res = $this->reallyDoQuery( $limit, false );
390 $num = false;
391 if ( $res ) {
392 $num = $res->numRows();
393 // Fetch results
394 $vals = [];
395 foreach ( $res as $i => $row ) {
396 if ( isset( $row->value ) ) {
397 if ( $this->usesTimestamps() ) {
398 $value = (int)wfTimestamp( TS_UNIX, $row->value );
399 } else {
400 $value = intval( $row->value ); // T16414
401 }
402 } else {
403 $value = $i;
404 }
405
406 $vals[] = [
407 'qc_type' => $this->getName(),
408 'qc_namespace' => $row->namespace,
409 'qc_title' => $row->title,
410 'qc_value' => $value
411 ];
412 }
413
414 $dbw->doAtomicSection(
415 __METHOD__,
416 function ( IDatabase $dbw, $fname ) use ( $vals ) {
417 // Clear out any old cached data
418 $dbw->delete( 'querycache',
419 [ 'qc_type' => $this->getName() ],
420 $fname
421 );
422 // Save results into the querycache table on the primary DB
423 if ( count( $vals ) ) {
424 $dbw->insert( 'querycache', $vals, $fname );
425 }
426 // Update the querycache_info record for the page
427 $dbw->upsert(
428 'querycache_info',
429 [
430 'qci_type' => $this->getName(),
431 'qci_timestamp' => $dbw->timestamp(),
432 ],
433 'qci_type',
434 [
435 'qci_timestamp' => $dbw->timestamp(),
436 ],
437 $fname
438 );
439 }
440 );
441 }
442 } catch ( DBError $e ) {
443 if ( !$ignoreErrors ) {
444 throw $e; // report query error
445 }
446 $num = false; // set result to false to indicate error
447 }
448
449 return $num;
450 }
451
457 protected function getRecacheDB() {
458 return $this->getDBLoadBalancer()
459 ->getConnectionRef( ILoadBalancer::DB_REPLICA, [ $this->getName(), 'QueryPage::recache', 'vslow' ] );
460 }
461
468 public function delete( LinkTarget $title ) {
469 if ( $this->isCached() ) {
470 $dbw = $this->getDBLoadBalancer()->getConnectionRef( ILoadBalancer::DB_PRIMARY );
471 $dbw->delete( 'querycache', [
472 'qc_type' => $this->getName(),
473 'qc_namespace' => $title->getNamespace(),
474 'qc_title' => $title->getDBkey(),
475 ], __METHOD__ );
476 }
477 }
478
484 public function deleteAllCachedData() {
485 $fname = static::class . '::' . __FUNCTION__;
486 $dbw = $this->getDBLoadBalancer()->getConnectionRef( ILoadBalancer::DB_PRIMARY );
487 $dbw->delete( 'querycache',
488 [ 'qc_type' => $this->getName() ],
489 $fname
490 );
491 $dbw->delete( 'querycachetwo',
492 [ 'qcc_type' => $this->getName() ],
493 $fname
494 );
495 $dbw->delete( 'querycache_info',
496 [ 'qci_type' => $this->getName() ],
497 $fname
498 );
499 }
500
509 public function reallyDoQuery( $limit, $offset = false ) {
510 $fname = static::class . '::reallyDoQuery';
511 $dbr = $this->getRecacheDB();
512 $query = $this->getQueryInfo();
513 $order = $this->getOrderFields();
514
515 if ( $this->sortDescending() ) {
516 foreach ( $order as &$field ) {
517 $field .= ' DESC';
518 }
519 }
520
521 if ( is_array( $query ) ) {
522 $tables = isset( $query['tables'] ) ? (array)$query['tables'] : [];
523 $fields = isset( $query['fields'] ) ? (array)$query['fields'] : [];
524 $conds = isset( $query['conds'] ) ? (array)$query['conds'] : [];
525 $options = isset( $query['options'] ) ? (array)$query['options'] : [];
526 $join_conds = isset( $query['join_conds'] ) ? (array)$query['join_conds'] : [];
527
528 if ( $order ) {
529 $options['ORDER BY'] = $order;
530 }
531
532 if ( $limit !== false ) {
533 $options['LIMIT'] = intval( $limit );
534 }
535
536 if ( $offset !== false ) {
537 $options['OFFSET'] = intval( $offset );
538 }
539
540 $res = $dbr->select( $tables, $fields, $conds, $fname,
541 $options, $join_conds
542 );
543 } else {
544 // Old-fashioned raw SQL style, deprecated
545 MWDebug::detectDeprecatedOverride(
546 $this,
547 __CLASS__,
548 'getSQL',
549 '1.39'
550 );
551 $sql = $this->getSQL();
552 $sql .= ' ORDER BY ' . implode( ', ', $order );
553 $sql = $dbr->limitResult( $sql, $limit, $offset );
554 $res = $dbr->query( $sql, $fname );
555 }
556
557 return $res;
558 }
559
566 public function doQuery( $offset = false, $limit = false ) {
567 if ( $this->isCached() && $this->isCacheable() ) {
568 return $this->fetchFromCache( $limit, $offset );
569 } else {
570 return $this->reallyDoQuery( $limit, $offset );
571 }
572 }
573
583 public function fetchFromCache( $limit, $offset = false ) {
584 $dbr = $this->getDBLoadBalancer()->getConnectionRef( ILoadBalancer::DB_REPLICA );
585 $options = [];
586
587 if ( $limit !== false ) {
588 $options['LIMIT'] = intval( $limit );
589 }
590
591 if ( $offset !== false ) {
592 $options['OFFSET'] = intval( $offset );
593 }
594
595 $order = $this->getCacheOrderFields();
596 if ( $this->sortDescending() ) {
597 foreach ( $order as &$field ) {
598 $field .= " DESC";
599 }
600 }
601 if ( $order ) {
602 $options['ORDER BY'] = $order;
603 }
604
605 return $dbr->select( 'querycache',
606 [ 'qc_type',
607 'namespace' => 'qc_namespace',
608 'title' => 'qc_title',
609 'value' => 'qc_value' ],
610 [ 'qc_type' => $this->getName() ],
611 __METHOD__,
612 $options
613 );
614 }
615
623 protected function getCacheOrderFields() {
624 return [ 'value' ];
625 }
626
630 public function getCachedTimestamp() {
631 if ( $this->cachedTimestamp === null ) {
632 $dbr = $this->getDBLoadBalancer()->getConnectionRef( ILoadBalancer::DB_REPLICA );
633 $fname = static::class . '::getCachedTimestamp';
634 $this->cachedTimestamp = $dbr->selectField( 'querycache_info', 'qci_timestamp',
635 [ 'qci_type' => $this->getName() ], $fname );
636 }
637 return $this->cachedTimestamp;
638 }
639
652 protected function getLimitOffset() {
653 list( $limit, $offset ) = $this->getRequest()
654 ->getLimitOffsetForUser( $this->getUser() );
655 if ( $this->getConfig()->get( MainConfigNames::MiserMode ) ) {
656 $maxResults = $this->getMaxResults();
657 // Can't display more than max results on a page
658 $limit = min( $limit, $maxResults );
659 // Can't skip over more than the end of $maxResults
660 $offset = min( $offset, $maxResults + 1 );
661 }
662 return [ $limit, $offset ];
663 }
664
673 protected function getDBLimit( $uiLimit, $uiOffset ) {
674 $maxResults = $this->getMaxResults();
675 if ( $this->getConfig()->get( MainConfigNames::MiserMode ) ) {
676 $limit = min( $uiLimit + 1, $maxResults - $uiOffset );
677 return max( $limit, 0 );
678 } else {
679 return $uiLimit + 1;
680 }
681 }
682
693 protected function getMaxResults() {
694 // Max of 10000, unless we store more than 10000 in query cache.
695 return max( $this->getConfig()->get( MainConfigNames::QueryCacheLimit ), 10000 );
696 }
697
704 public function execute( $par ) {
705 $this->checkPermissions();
706
707 $this->setHeaders();
708 $this->outputHeader();
709
710 $out = $this->getOutput();
711
712 if ( $this->isCached() && !$this->isCacheable() ) {
713 $out->addWikiMsg( 'querypage-disabled' );
714 return;
715 }
716
717 $out->setSyndicated( $this->isSyndicated() );
718
719 if ( $this->limit == 0 && $this->offset == 0 ) {
720 list( $this->limit, $this->offset ) = $this->getLimitOffset();
721 }
722 $dbLimit = $this->getDBLimit( $this->limit, $this->offset );
723 // @todo Use doQuery()
724 if ( !$this->isCached() ) {
725 // select one extra row for navigation
726 $res = $this->reallyDoQuery( $dbLimit, $this->offset );
727 } else {
728 // Get the cached result, select one extra row for navigation
729 $res = $this->fetchFromCache( $dbLimit, $this->offset );
730 if ( !$this->listoutput ) {
731 // Fetch the timestamp of this update
732 $ts = $this->getCachedTimestamp();
733 $lang = $this->getLanguage();
734 $maxResults = $lang->formatNum( $this->getConfig()->get(
735 MainConfigNames::QueryCacheLimit ) );
736
737 if ( $ts ) {
738 $user = $this->getUser();
739 $updated = $lang->userTimeAndDate( $ts, $user );
740 $updateddate = $lang->userDate( $ts, $user );
741 $updatedtime = $lang->userTime( $ts, $user );
742 $out->addMeta( 'Data-Cache-Time', $ts );
743 $out->addJsConfigVars( 'dataCacheTime', $ts );
744 $out->addWikiMsg( 'perfcachedts', $updated, $updateddate, $updatedtime, $maxResults );
745 } else {
746 $out->addWikiMsg( 'perfcached', $maxResults );
747 }
748
749 // If updates on this page have been disabled, let the user know
750 // that the data set won't be refreshed for now
751 $disabledQueryPages = self::getDisabledQueryPages( $this->getConfig() );
752 if ( isset( $disabledQueryPages[$this->getName()] ) ) {
753 $runMode = $disabledQueryPages[$this->getName()];
754 if ( $runMode === 'disabled' ) {
755 $out->wrapWikiMsg(
756 "<div class=\"mw-querypage-no-updates\">\n$1\n</div>",
757 'querypage-no-updates'
758 );
759 } else {
760 // Messages used here: querypage-updates-periodical
761 $out->wrapWikiMsg(
762 "<div class=\"mw-querypage-updates-" . $runMode . "\">\n$1\n</div>",
763 'querypage-updates-' . $runMode
764 );
765 }
766 }
767 }
768 }
769
770 $this->numRows = $res->numRows();
771
772 $dbr = $this->getRecacheDB();
773 $this->preprocessResults( $dbr, $res );
774
775 $out->addHTML( Xml::openElement( 'div', [ 'class' => 'mw-spcontent' ] ) );
776
777 // Top header and navigation
778 if ( $this->shownavigation ) {
779 $out->addHTML( $this->getPageHeader() );
780 if ( $this->numRows > 0 ) {
781 $out->addHTML( $this->msg( 'showingresultsinrange' )->numParams(
782 min( $this->numRows, $this->limit ), // do not show the one extra row, if exist
783 $this->offset + 1, ( min( $this->numRows, $this->limit ) + $this->offset ) )->parseAsBlock() );
784 // Disable the "next" link when we reach the end
785 $miserMaxResults = $this->getConfig()->get( MainConfigNames::MiserMode )
786 && ( $this->offset + $this->limit >= $this->getMaxResults() );
787 $atEnd = ( $this->numRows <= $this->limit ) || $miserMaxResults;
788 $paging = $this->buildPrevNextNavigation( $this->offset,
789 $this->limit, $this->linkParameters(), $atEnd, $par );
790 $out->addHTML( '<p>' . $paging . '</p>' );
791 } else {
792 // No results to show, so don't bother with "showing X of Y" etc.
793 // -- just let the user know and give up now
794 $this->showEmptyText();
795 $out->addHTML( Xml::closeElement( 'div' ) );
796 return;
797 }
798 }
799
800 // The actual results; specialist subclasses will want to handle this
801 // with more than a straight list, so we hand them the info, plus
802 // an OutputPage, and let them get on with it
803 $this->outputResults( $out,
804 $this->getSkin(),
805 $dbr, // Should use IResultWrapper for this
806 $res,
807 min( $this->numRows, $this->limit ), // do not format the one extra row, if exist
808 $this->offset );
809
810 // Repeat the paging links at the bottom
811 if ( $this->shownavigation ) {
812 // @phan-suppress-next-line PhanPossiblyUndeclaredVariable paging is set when used here
813 $out->addHTML( '<p>' . $paging . '</p>' );
814 }
815
816 $out->addHTML( Xml::closeElement( 'div' ) );
817 }
818
832 protected function outputResults( $out, $skin, $dbr, $res, $num, $offset ) {
833 if ( $num > 0 ) {
834 $html = [];
835 if ( !$this->listoutput ) {
836 $html[] = $this->openList( $offset );
837 }
838
839 // $res might contain the whole 1,000 rows, so we read up to
840 // $num [should update this to use a Pager]
841 for ( $i = 0; $i < $num && $row = $res->fetchObject(); $i++ ) {
842 $line = $this->formatResult( $skin, $row );
843 if ( $line ) {
844 $html[] = $this->listoutput
845 ? $line
846 : "<li>{$line}</li>\n";
847 }
848 }
849
850 if ( !$this->listoutput ) {
851 $html[] = $this->closeList();
852 }
853
854 $html = $this->listoutput
855 ? $this->getContentLanguage()->listToText( $html )
856 : implode( '', $html );
857
858 $out->addHTML( $html );
859 }
860 }
861
866 protected function openList( $offset ) {
867 return "\n<ol start='" . ( $offset + 1 ) . "' class='special'>\n";
868 }
869
873 protected function closeList() {
874 return "</ol>\n";
875 }
876
883 protected function preprocessResults( $db, $res ) {
884 }
885
898 protected function executeLBFromResultWrapper( IResultWrapper $res, $ns = null ) {
899 if ( !$res->numRows() ) {
900 return;
901 }
902
903 $batch = $this->getLinkBatchFactory()->newLinkBatch();
904 foreach ( $res as $row ) {
905 $batch->add( $ns ?? (int)$row->namespace, $row->title );
906 }
907 $batch->execute();
908
909 $res->seek( 0 );
910 }
911
916 final protected function setDBLoadBalancer( ILoadBalancer $loadBalancer ) {
917 $this->loadBalancer = $loadBalancer;
918 }
919
924 final protected function getDBLoadBalancer(): ILoadBalancer {
925 if ( $this->loadBalancer === null ) {
926 // Fallback if not provided
927 // TODO Change to wfWarn in a future release
928 $this->loadBalancer = MediaWikiServices::getInstance()->getDBLoadBalancer();
929 }
930 return $this->loadBalancer;
931 }
932}
getUser()
wfTimestamp( $outputtype=TS_UNIX, $ts=0)
Get a timestamp string in one of various formats.
wfDeprecated( $function, $version=false, $component=false, $callerOffset=2)
Logs a warning that a deprecated feature was used.
if(!defined('MW_SETUP_CALLBACK'))
The persistent session ID (if any) loaded at startup.
Definition WebStart.php:82
MediaWiki exception.
A class containing constants representing the names of configuration variables.
Service locator for MediaWiki core services.
This is a class for doing query pages; since they're almost all the same, we factor out some of the f...
Definition QueryPage.php:42
isExpensive()
Should this query page only be updated offline on large wikis?
linkParameters()
If using extra form wheely-dealies, return a set of parameters here as an associative array.
getMaxResults()
Get max number of results we can return in miser mode.
doQuery( $offset=false, $limit=false)
Somewhat deprecated, you probably want to be using execute()
executeLBFromResultWrapper(IResultWrapper $res, $ns=null)
Creates a new LinkBatch object, adds all pages from the passed result wrapper (MUST include title and...
setListoutput( $bool)
A mutator for $this->listoutput;.
static getDisabledQueryPages(Config $config)
Get a list of query pages disabled and with it's run mode.
recache( $limit, $ignoreErrors=true)
Clear the cache and save new results.
setDBLoadBalancer(ILoadBalancer $loadBalancer)
fetchFromCache( $limit, $offset=false)
Fetch the query results from the query cache.
int $offset
The offset and limit in use, as passed to the query() function.
Definition QueryPage.php:47
outputResults( $out, $skin, $dbr, $res, $num, $offset)
Format and output report results using the given information plus OutputPage.
isCached()
Whether or not the output of the page in question is retrieved from the database cache.
sortDescending()
Override to sort by increasing values.
deleteAllCachedData()
Remove all cached value This is needed when the page is no longer using the cache.
formatResult( $skin, $result)
Formats the results of the query for display.
isSyndicated()
Sometimes we don't want to build rss / atom feeds.
getCachedTimestamp()
static getPages()
Get a list of query page classes and their associated special pages, for periodic updates.
Definition QueryPage.php:87
bool $shownavigation
Whether to show prev/next links.
Definition QueryPage.php:69
reallyDoQuery( $limit, $offset=false)
Run the query and return the result.
isCacheable()
Is the output of this query cacheable? Non-cacheable expensive pages will be disabled in miser mode a...
getOrderFields()
Subclasses return an array of fields to order by here.
getLinkBatchFactory()
showEmptyText()
Outputs some kind of an informative message (via OutputPage) to let the user know that the query retu...
usesTimestamps()
Does this query return timestamps rather than integers in its 'value' field? If true,...
getCacheOrderFields()
Return the order fields for fetchFromCache.
getQueryInfo()
Subclasses return an SQL query here, formatted as an array with the following keys: tables => Table(s...
openList( $offset)
getDBLimit( $uiLimit, $uiOffset)
What is limit to fetch from DB.
int $numRows
The number of rows returned by the query.
Definition QueryPage.php:59
preprocessResults( $db, $res)
Do any necessary preprocessing of the result object.
setLinkBatchFactory(LinkBatchFactory $linkBatchFactory)
getSQL()
For back-compat, subclasses may return a raw SQL query here, as a string.
getPageHeader()
The content returned by this function will be output before any result.
bool $listoutput
Whether or not we want plain listoutput rather than an ordered list.
Definition QueryPage.php:44
execute( $par)
This is the actual workhorse.
string null false $cachedTimestamp
Definition QueryPage.php:64
getDBLoadBalancer()
getLimitOffset()
Returns limit and offset, as returned by $this->getRequest()->getLimitOffsetForUser().
getRecacheDB()
Get a DB connection to be used for slow recache queries.
Parent class for all special pages.
Database error base class.
Definition DBError.php:31
Interface for configuration instances.
Definition Config.php:30
get( $name)
Get a configuration variable such as "Sitename" or "UploadMaintenance.".
Basic database interface for live and lazy-loaded relation database handles.
Definition IDatabase.php:39
delete( $table, $conds, $fname=__METHOD__)
Delete all rows in a table that match a condition.
upsert( $table, array $rows, $uniqueKeys, array $set, $fname=__METHOD__)
Upsert row(s) into a table, in the provided order, while updating conflicting rows.
insert( $table, $rows, $fname=__METHOD__, $options=[])
Insert row(s) into a table, in the provided order.
Create and track the database connections and transactions for a given database cluster.
Result wrapper for grabbing data queried from an IDatabase object.
timestamp( $ts=0)
Convert a timestamp in one of the formats accepted by ConvertibleTimestamp to the format used for ins...
$line
Definition mcc.php:119
if(!isset( $args[0])) $lang