MediaWiki REL1_39
SelectQueryBuilder.php
Go to the documentation of this file.
1<?php
2
3namespace Wikimedia\Rdbms;
4
14
16 public const SORT_ASC = 'ASC';
17
19 public const SORT_DESC = 'DESC';
20
24 private $fields = [];
25
29 private $conds = [];
30
34 private $caller = __CLASS__;
35
39 protected $options = [];
40
44 private $nextAutoAlias = 1;
45
47 protected $db;
48
54 public function __construct( IDatabase $db ) {
55 $this->db = $db;
56 }
57
65 public function connection( IDatabase $db ) {
66 if ( $this->db->getType() !== $db->getType() ) {
67 throw new \InvalidArgumentException( __METHOD__ .
68 ' cannot switch to a database of a different type.' );
69 }
70 $this->db = $db;
71 return $this;
72 }
73
93 public function queryInfo( $info ) {
94 if ( isset( $info['tables'] ) ) {
95 $this->rawTables( $info['tables'] );
96 }
97 if ( isset( $info['fields'] ) ) {
98 $this->fields( $info['fields'] );
99 }
100 if ( isset( $info['conds'] ) ) {
101 $this->where( $info['conds'] );
102 }
103 if ( isset( $info['options'] ) ) {
104 $this->options( (array)$info['options'] );
105 }
106 if ( isset( $info['join_conds'] ) ) {
107 $this->joinConds( (array)$info['join_conds'] );
108 }
109 if ( isset( $info['joins'] ) ) {
110 $this->joinConds( (array)$info['joins'] );
111 }
112 return $this;
113 }
114
127 public function rawTables( $tables ) {
128 if ( is_array( $tables ) ) {
129 $this->tables = array_merge( $this->tables, $tables );
130 } elseif ( is_string( $tables ) ) {
131 $this->tables[] = $tables;
132 } else {
133 throw new \InvalidArgumentException( __METHOD__ .
134 ': $tables must be a string or array' );
135 }
136 return $this;
137 }
138
144 public function newSubquery() {
145 return new self( $this->db );
146 }
147
155 public function from( $table, $alias = null ) {
156 return $this->table( $table, $alias );
157 }
158
165 public function tables( $tables ) {
166 foreach ( $tables as $alias => $table ) {
167 if ( is_string( $alias ) ) {
168 $this->table( $table, $alias );
169 } else {
170 $this->table( $table );
171 }
172 }
173 return $this;
174 }
175
186 public function fields( $fields ) {
187 if ( is_array( $fields ) ) {
188 $this->fields = array_merge( $this->fields, $fields );
189 } else {
190 $this->fields[] = $fields;
191 }
192 return $this;
193 }
194
201 public function select( $fields ) {
202 return $this->fields( $fields );
203 }
204
213 public function field( $field, $alias = null ) {
214 if ( $alias === null ) {
215 $this->fields[] = $field;
216 } else {
217 $this->fields[$alias] = $field;
218 }
219 return $this;
220 }
221
258 public function where( $conds ) {
259 if ( is_array( $conds ) ) {
260 foreach ( $conds as $key => $cond ) {
261 if ( is_int( $key ) ) {
262 $this->conds[] = $cond;
263 } elseif ( isset( $this->conds[$key] ) ) {
264 // @phan-suppress-previous-line PhanTypeMismatchDimFetch
265 // T288882
266 $this->conds[] = $this->db->makeList(
267 [ $key => $cond ], IDatabase::LIST_AND );
268 } else {
269 $this->conds[$key] = $cond;
270 }
271 }
272 } else {
273 $this->conds[] = $conds;
274 }
275 return $this;
276 }
277
284 public function andWhere( $conds ) {
285 return $this->where( $conds );
286 }
287
294 public function conds( $conds ) {
295 return $this->where( $conds );
296 }
297
306 public function joinConds( array $joinConds ) {
307 $this->joinConds = array_merge( $this->joinConds, $joinConds );
308 return $this;
309 }
310
316 protected function getAutoAlias() {
317 return 'sqb' . ( $this->nextAutoAlias++ );
318 }
319
326 public function newJoinGroup() {
327 return new JoinGroup( $this->getAutoAlias() );
328 }
329
340 public function offset( $offset ) {
341 $this->options['OFFSET'] = $offset;
342 return $this;
343 }
344
355 public function limit( $limit ) {
356 $this->options['LIMIT'] = $limit;
357 return $this;
358 }
359
367 public function lockInShareMode() {
368 $this->options[] = 'LOCK IN SHARE MODE';
369 return $this;
370 }
371
379 public function forUpdate() {
380 $this->options[] = 'FOR UPDATE';
381 return $this;
382 }
383
389 public function distinct() {
390 $this->options[] = 'DISTINCT';
391 return $this;
392 }
393
400 public function setMaxExecutionTime( int $time ) {
401 $this->options['MAX_EXECUTION_TIME'] = $time;
402 return $this;
403 }
404
414 public function groupBy( $group ) {
415 $this->mergeOption( 'GROUP BY', $group );
416 return $this;
417 }
418
430 public function having( $having ) {
431 $this->mergeOption( 'HAVING', $having );
432 return $this;
433 }
434
445 public function orderBy( $fields, $direction = null ) {
446 if ( $direction === null ) {
447 $this->mergeOption( 'ORDER BY', $fields );
448 } elseif ( is_array( $fields ) ) {
449 $fieldsWithDirection = [];
450 foreach ( $fields as $field ) {
451 $fieldsWithDirection[] = "$field $direction";
452 }
453 $this->mergeOption( 'ORDER BY', $fieldsWithDirection );
454 } else {
455 $this->mergeOption( 'ORDER BY', "$fields $direction" );
456 }
457 return $this;
458 }
459
466 private function mergeOption( $name, $newArrayOrValue ) {
467 $value = isset( $this->options[$name] )
468 ? (array)$this->options[$name] : [];
469 if ( is_array( $newArrayOrValue ) ) {
470 $value = array_merge( $value, $newArrayOrValue );
471 } else {
472 $value[] = $newArrayOrValue;
473 }
474 $this->options[$name] = $value;
475 }
476
489 public function useIndex( $index ) {
490 $this->setIndexHint( 'USE INDEX', $index );
491 return $this;
492 }
493
506 public function ignoreIndex( $index ) {
507 $this->setIndexHint( 'IGNORE INDEX', $index );
508 return $this;
509 }
510
517 private function setIndexHint( $type, $value ) {
518 if ( !isset( $this->options[$type] ) ) {
519 $this->options[$type] = [];
520 } elseif ( !is_array( $this->options[$type] ) ) {
521 throw new \UnexpectedValueException(
522 __METHOD__ . ": The $type option cannot be appended to " .
523 'because it is not an array. This may have been caused by a prior ' .
524 'call to option() or options().' );
525 }
526 if ( is_array( $value ) ) {
527 $this->options[$type] = array_merge( $this->options[$type], $value );
528 } elseif ( $this->lastAlias === null ) {
529 throw new \UnexpectedValueException(
530 __METHOD__ . ': Cannot append index value since there is no' .
531 'prior table' );
532 } else {
533 $this->options[$type][$this->lastAlias] = $value;
534 }
535 }
536
542 public function explain() {
543 $this->options['EXPLAIN'] = true;
544 return $this;
545 }
546
552 public function straightJoinOption() {
553 $this->options[] = 'STRAIGHT_JOIN';
554 return $this;
555 }
556
562 public function bigResult() {
563 $this->options[] = 'SQL_BIG_RESULT';
564 return $this;
565 }
566
572 public function bufferResult() {
573 $this->options[] = 'SQL_BUFFER_RESULT';
574 return $this;
575 }
576
582 public function smallResult() {
583 $this->options[] = 'SQL_SMALL_RESULT';
584 return $this;
585 }
586
592 public function calcFoundRows() {
593 $this->options[] = 'SQL_CALC_FOUND_ROWS';
594 return $this;
595 }
596
605 public function option( $name, $value = null ) {
606 if ( $value === null ) {
607 $this->options[] = $name;
608 } else {
609 $this->options[$name] = $value;
610 }
611 return $this;
612 }
613
621 public function options( array $options ) {
622 $this->options = array_merge( $this->options, $options );
623 return $this;
624 }
625
632 public function caller( $fname ) {
633 $this->caller = $fname;
634 return $this;
635 }
636
642 public function fetchResultSet() {
643 return $this->db->select( $this->tables, $this->fields, $this->conds, $this->caller,
644 $this->options, $this->joinConds );
645 }
646
654 public function fetchField() {
655 if ( count( $this->fields ) !== 1 ) {
656 throw new \UnexpectedValueException(
657 __METHOD__ . ' expects the query to have only one field' );
658 }
659 $field = reset( $this->fields );
660 return $this->db->selectField( $this->tables, $field, $this->conds, $this->caller,
661 $this->options, $this->joinConds );
662 }
663
671 public function fetchFieldValues() {
672 if ( count( $this->fields ) !== 1 ) {
673 throw new \UnexpectedValueException(
674 __METHOD__ . ' expects the query to have only one field' );
675 }
676 $field = reset( $this->fields );
677 return $this->db->selectFieldValues( $this->tables, $field, $this->conds, $this->caller,
678 $this->options, $this->joinConds );
679 }
680
687 public function fetchRow() {
688 return $this->db->selectRow( $this->tables, $this->fields, $this->conds, $this->caller,
689 $this->options, $this->joinConds );
690 }
691
699 public function fetchRowCount() {
700 return $this->db->selectRowCount( $this->tables, $this->getRowCountVar(), $this->conds,
701 $this->caller, $this->options, $this->joinConds );
702 }
703
714 public function estimateRowCount() {
715 return $this->db->estimateRowCount( $this->tables, $this->getRowCountVar(), $this->conds,
716 $this->caller, $this->options, $this->joinConds );
717 }
718
725 private function getRowCountVar() {
726 if ( count( $this->fields ) === 0 ) {
727 return '*';
728 } elseif ( count( $this->fields ) === 1 ) {
729 return reset( $this->fields );
730 } else {
731 throw new \UnexpectedValueException(
732 __METHOD__ . ' expects the query to have at most one field' );
733 }
734 }
735
741 public function lockForUpdate() {
742 return $this->db->lockForUpdate( $this->tables, $this->conds, $this->caller,
743 $this->options, $this->joinConds );
744 }
745
757 public function buildGroupConcatField( $delim ) {
758 if ( count( $this->fields ) !== 1 ) {
759 throw new \UnexpectedValueException(
760 __METHOD__ . ' expects the query to have only one field' );
761 }
762 $field = reset( $this->fields );
763 return $this->db->buildGroupConcatField( $delim, $this->tables, $field,
764 $this->conds, $this->joinConds );
765 }
766
772 public function getSQL() {
773 return $this->db->selectSQLText( $this->tables, $this->fields, $this->conds, $this->caller,
774 $this->options, $this->joinConds );
775 }
776
791 public function getQueryInfo( $joinsName = 'join_conds' ) {
792 $info = [
793 'tables' => $this->tables,
794 'fields' => $this->fields,
795 'conds' => $this->conds,
796 'options' => $this->options,
797 ];
798 $info[ $joinsName ] = $this->joinConds;
799 return $info;
800 }
801}
A class for code shared between SelectQueryBuilder and JoinGroup.
table( $table, $alias=null)
Add a single table or a single parenthesized group.
An object representing a parenthesized group of tables and their join types and conditions.
Definition JoinGroup.php:9
Note that none of the methods in this class are stable to override.
estimateRowCount()
Estimate the number of rows in dataset.
getQueryInfo( $joinsName='join_conds')
Get an associative array describing the query in terms of its raw parameters to Database::select().
distinct()
Enable the DISTINCT option.
straightJoinOption()
Enable the STRAIGHT_JOIN query option.
groupBy( $group)
Add a GROUP BY clause.
newSubquery()
Get an empty SelectQueryBuilder which can be used to build a subquery of this query.
lockForUpdate()
Run the SELECT query with the FOR UPDATE option.
useIndex( $index)
Set a USE INDEX option.
connection(IDatabase $db)
Change the IDatabase object the query builder is bound to.
rawTables( $tables)
Given a table or table array as might be passed to Database::select(), append it to the existing tabl...
conds( $conds)
Add conditions to the query.
limit( $limit)
Set the query limit.
andWhere( $conds)
Add conditions to the query.
fetchField()
Run the constructed SELECT query, and return a single field extracted from the first result row.
array $options
The options to be passed to IDatabase::select()
fetchFieldValues()
Run the constructed SELECT query, and extract a single field from each result row,...
calcFoundRows()
Enable the SQL_CALC_FOUND_ROWS option.
newJoinGroup()
Create a parenthesized group of joins which can be added to the object like a table.
fetchResultSet()
Run the constructed SELECT query and return all results.
lockInShareMode()
Enable the LOCK IN SHARE MODE option.
bufferResult()
Enable the SQL_BUFFER_RESULT option.
queryInfo( $info)
Set the query parameters to the given values, appending to the values which were already set.
options(array $options)
Manually set multiple options in the $options array to be passed to IDatabase::select().
forUpdate()
Enable the FOR UPDATE option.
option( $name, $value=null)
Manually set an option in the $options array to be passed to IDatabase::select()
buildGroupConcatField( $delim)
Build a GROUP_CONCAT or equivalent statement for a query.
select( $fields)
Add a field or an array of fields to the query.
caller( $fname)
Set the method name to be included in an SQL comment.
tables( $tables)
Add multiple tables.
ignoreIndex( $index)
Set the IGNORE INDEX option.
having( $having)
Add a HAVING clause.
from( $table, $alias=null)
Add a single table to the SELECT query.
setMaxExecutionTime(int $time)
Set MAX_EXECUTION_TIME for queries.
field( $field, $alias=null)
Add a single field to the query, optionally with an alias.
orderBy( $fields, $direction=null)
Set the ORDER BY clause.
smallResult()
Enable the SQL_SMALL_RESULT option.
explain()
Make the query be an EXPLAIN SELECT query instead of a SELECT query.
bigResult()
Enable the SQL_BIG_RESULT option.
fetchRowCount()
Run the SELECT query, and return the number of results.
fetchRow()
Run the constructed SELECT query, and return the first result row.
joinConds(array $joinConds)
Manually append to the $join_conds array which will be passed to IDatabase::select().
fields( $fields)
Add a field or an array of fields to the query.
getAutoAlias()
Get a table alias which is unique to this SelectQueryBuilder.
where( $conds)
Add conditions to the query.
getSQL()
Get the SQL query string which would be used by fetchResultSet().
Basic database interface for live and lazy-loaded relation database handles.
Definition IDatabase.php:39
getType()
Get the RDBMS type of the server (e.g.