182 # NB: the offset is quoted, not validated. It is treated as an
183 # arbitrary string to support the widest variety of index types. Be
184 # careful outputting it into HTML!
185 $this->mOffset = $this->mRequest->getText(
'offset' );
187 # Use consistent behavior for the limit options
188 $this->mDefaultLimit = $this->
getUser()->getIntOption(
'rclimit' );
189 if ( !$this->mLimit ) {
191 list( $this->mLimit, ) = $this->mRequest
192 ->getLimitOffsetForUser( $this->
getUser() );
195 $this->mIsBackwards = ( $this->mRequest->getVal(
'dir' ) ==
'prev' );
196 # Let the subclass set the DB here; otherwise use a replica DB for the current wiki
201 $order = $this->mRequest->getVal(
'order' );
203 if ( is_array( $index ) && isset( $index[$order] ) ) {
204 $this->mOrderType = $order;
205 $this->mIndexField = $index[$order];
206 $this->mExtraSortFields = isset( $extraSort[$order] )
207 ? (array)$extraSort[$order]
209 } elseif ( is_array( $index ) ) {
210 # First element is the default
211 $this->mIndexField = reset( $index );
212 $this->mOrderType = key( $index );
213 $this->mExtraSortFields = isset( $extraSort[$this->mOrderType] )
214 ? (array)$extraSort[$this->mOrderType]
217 # $index is not an array
218 $this->mOrderType =
null;
219 $this->mIndexField = $index;
220 $isSortAssociative = array_values( $extraSort ) !== $extraSort;
221 if ( $isSortAssociative ) {
222 $this->mExtraSortFields = isset( $extraSort[$index] )
223 ? (array)$extraSort[$index]
226 $this->mExtraSortFields = (array)$extraSort;
230 if ( !isset( $this->mDefaultDirection ) ) {
232 $this->mDefaultDirection = is_array( $dir )
257 ? self::QUERY_ASCENDING
261 # Plus an extra row so that we can tell the "next" link should be shown
262 $queryLimit = $this->mLimit + 1;
264 if ( $this->mOffset ==
'' ) {
273 $isFirst = !$this->
reallyDoQuery( $this->mOffset, 1, $oppositeOrder )->numRows();
274 $this->mIncludeOffset = $oldIncludeOffset;
284 $this->mQueryDone =
true;
287 $this->mResult->rewind();
368 $numRows =
$res->numRows();
372 $this->mPastTheEndIndex = [];
373 $this->mPastTheEndRow =
null;
376 $indexColumns = array_map(
function ( $v ) {
378 $parts = explode(
'.', $v );
379 return end( $parts );
380 }, (array)$this->mIndexField );
382 $row =
$res->fetchRow();
383 foreach ( $indexColumns as $indexColumn ) {
384 $firstIndex[] = $row[$indexColumn];
387 # Discard the extra result row if there is one
388 if ( $numRows > $this->mLimit && $numRows > 1 ) {
389 $res->seek( $numRows - 1 );
390 $this->mPastTheEndRow =
$res->fetchObject();
391 foreach ( $indexColumns as $indexColumn ) {
392 $this->mPastTheEndIndex[] = $this->mPastTheEndRow->$indexColumn;
394 $res->seek( $numRows - 2 );
395 $row =
$res->fetchRow();
396 foreach ( $indexColumns as $indexColumn ) {
397 $lastIndex[] = $row[$indexColumn];
400 $this->mPastTheEndRow =
null;
401 $res->seek( $numRows - 1 );
402 $row =
$res->fetchRow();
403 foreach ( $indexColumns as $indexColumn ) {
404 $lastIndex[] = $row[$indexColumn];
409 if ( $this->mIsBackwards ) {
410 $this->mIsFirst = ( $numRows < $limit );
411 $this->mIsLast = $isFirst;
412 $this->mLastShown = $firstIndex;
413 $this->mFirstShown = $lastIndex;
415 $this->mIsFirst = $isFirst;
416 $this->mIsLast = ( $numRows < $limit );
417 $this->mLastShown = $lastIndex;
418 $this->mFirstShown = $firstIndex;
446 list( $tables, $fields, $conds, $fname, $options, $join_conds ) =
449 return $this->mDb->select( $tables, $fields, $conds, $fname, $options, $join_conds );
467 $tables = $info[
'tables'];
468 $fields = $info[
'fields'];
469 $conds = $info[
'conds'] ?? [];
470 $options = $info[
'options'] ?? [];
471 $join_conds = $info[
'join_conds'] ?? [];
472 $indexColumns = (array)$this->mIndexField;
473 $sortColumns = array_merge( $indexColumns, $this->mExtraSortFields );
475 if ( $order === self::QUERY_ASCENDING ) {
476 $options[
'ORDER BY'] = $sortColumns;
477 $operator = $this->mIncludeOffset ?
'>=' :
'>';
480 foreach ( $sortColumns as $col ) {
481 $orderBy[] = $col .
' DESC';
483 $options[
'ORDER BY'] = $orderBy;
484 $operator = $this->mIncludeOffset ?
'<=' :
'<';
487 $offsets = explode(
'|', $offset, count( $indexColumns ) );
495 $options[
'LIMIT'] = intval( $limit );
496 return [ $tables, $fields, $conds, $fname, $options, $join_conds ];
538 for ( $i = 1; $i <= count( $offsets ); $i++ ) {
540 array_slice( $offsets, 0, $i ),
541 array_slice( $columns, 0, $i ),
545 return $this->mDb->makeList( $innerConds, IDatabase::LIST_OR );
559 while ( count( $offsets ) > 1 ) {
560 $conds[] = $columns[0] .
'=' . $this->mDb->addQuotes( $offsets[0] );
561 array_shift( $columns );
562 array_shift( $offsets );
564 $conds[] = $columns[0] . $operator . $this->mDb->addQuotes( $offsets[0] );
565 return $this->mDb->makeList( $conds, IDatabase::LIST_AND );
587 if ( !$this->mQueryDone ) {
591 if ( $this->mResult->numRows() ) {
592 # Do any special query batches before display
596 # Don't use any extra rows returned by the query
597 $numRows = min( $this->mResult->numRows(), $this->mLimit );
601 if ( $this->mIsBackwards ) {
602 for ( $i = $numRows - 1; $i >= 0; $i-- ) {
603 $this->mResult->seek( $i );
604 $row = $this->mResult->fetchObject();
608 $this->mResult->seek( 0 );
609 for ( $i = 0; $i < $numRows; $i++ ) {
610 $row = $this->mResult->fetchObject();
713 if ( !isset( $this->mDefaultQuery ) ) {
714 $this->mDefaultQuery = $this->
getRequest()->getQueryValues();
715 unset( $this->mDefaultQuery[
'title'] );
716 unset( $this->mDefaultQuery[
'dir'] );
717 unset( $this->mDefaultQuery[
'offset'] );
718 unset( $this->mDefaultQuery[
'limit'] );
719 unset( $this->mDefaultQuery[
'order'] );
720 unset( $this->mDefaultQuery[
'month'] );
721 unset( $this->mDefaultQuery[
'year'] );
746 if ( !$this->mQueryDone ) {
750 # Don't announce the limit everywhere if it's the default
751 $urlLimit = $this->mLimit == $this->mDefaultLimit ? null :
$this->mLimit;
753 if ( $this->mIsFirst ) {
759 'offset' => implode(
'|', (array)$this->mFirstShown ),
762 $first = [
'limit' => $urlLimit ];
764 if ( $this->mIsLast ) {
768 $next = [
'offset' => implode(
'|', (array)$this->mLastShown ),
'limit' => $urlLimit ];
769 $last = [
'dir' =>
'prev',
'limit' => $urlLimit ];