MediaWiki REL1_37
SelectQueryBuilder.php
Go to the documentation of this file.
1<?php
2
3namespace Wikimedia\Rdbms;
4
12
14 public const SORT_ASC = 'ASC';
15
17 public const SORT_DESC = 'DESC';
18
22 private $fields = [];
23
27 private $conds = [];
28
32 private $caller = __CLASS__;
33
37 protected $options = [];
38
42 private $nextAutoAlias = 1;
43
45 protected $db;
46
52 public function __construct( IDatabase $db ) {
53 $this->db = $db;
54 }
55
63 public function connection( IDatabase $db ) {
64 if ( $this->db->getType() !== $db->getType() ) {
65 throw new \InvalidArgumentException( __METHOD__ .
66 ' cannot switch to a database of a different type.' );
67 }
68 $this->db = $db;
69 return $this;
70 }
71
91 public function queryInfo( $info ) {
92 if ( isset( $info['tables'] ) ) {
93 $this->rawTables( $info['tables'] );
94 }
95 if ( isset( $info['fields'] ) ) {
96 $this->fields( $info['fields'] );
97 }
98 if ( isset( $info['conds'] ) ) {
99 $this->where( $info['conds'] );
100 }
101 if ( isset( $info['options'] ) ) {
102 $this->options( (array)$info['options'] );
103 }
104 if ( isset( $info['join_conds'] ) ) {
105 $this->joinConds( (array)$info['join_conds'] );
106 }
107 if ( isset( $info['joins'] ) ) {
108 $this->joinConds( (array)$info['joins'] );
109 }
110 return $this;
111 }
112
125 public function rawTables( $tables ) {
126 if ( is_array( $tables ) ) {
127 $this->tables = array_merge( $this->tables, $tables );
128 } elseif ( is_string( $tables ) ) {
129 $this->tables[] = $tables;
130 } else {
131 throw new \InvalidArgumentException( __METHOD__ .
132 ': $tables must be a string or array' );
133 }
134 return $this;
135 }
136
142 public function newSubquery() {
143 return new self( $this->db );
144 }
145
153 public function from( $table, $alias = null ) {
154 return $this->table( $table, $alias );
155 }
156
163 public function tables( $tables ) {
164 foreach ( $tables as $alias => $table ) {
165 if ( is_string( $alias ) ) {
166 $this->table( $table, $alias );
167 } else {
168 $this->table( $table );
169 }
170 }
171 return $this;
172 }
173
184 public function fields( $fields ) {
185 if ( is_array( $fields ) ) {
186 $this->fields = array_merge( $this->fields, $fields );
187 } else {
188 $this->fields[] = $fields;
189 }
190 return $this;
191 }
192
199 public function select( $fields ) {
200 return $this->fields( $fields );
201 }
202
211 public function field( $field, $alias = null ) {
212 if ( $alias === null ) {
213 $this->fields[] = $field;
214 } else {
215 $this->fields[$alias] = $field;
216 }
217 return $this;
218 }
219
256 public function where( $conds ) {
257 if ( is_array( $conds ) ) {
258 foreach ( $conds as $key => $cond ) {
259 if ( is_int( $key ) ) {
260 $this->conds[] = $cond;
261 } elseif ( isset( $this->conds[$key] ) ) {
262 // T288882
263 $this->conds[] = $this->db->makeList(
264 [ $key => $cond ], IDatabase::LIST_AND );
265 } else {
266 $this->conds[$key] = $cond;
267 }
268 }
269 } else {
270 $this->conds[] = $conds;
271 }
272 return $this;
273 }
274
281 public function andWhere( $conds ) {
282 return $this->where( $conds );
283 }
284
291 public function conds( $conds ) {
292 return $this->where( $conds );
293 }
294
303 public function joinConds( array $joinConds ) {
304 $this->joinConds = array_merge( $this->joinConds, $joinConds );
305 return $this;
306 }
307
313 protected function getAutoAlias() {
314 return 'sqb' . ( $this->nextAutoAlias++ );
315 }
316
323 public function newJoinGroup() {
324 return new JoinGroup( $this->getAutoAlias() );
325 }
326
337 public function offset( $offset ) {
338 $this->options['OFFSET'] = $offset;
339 return $this;
340 }
341
352 public function limit( $limit ) {
353 $this->options['LIMIT'] = $limit;
354 return $this;
355 }
356
364 public function lockInShareMode() {
365 $this->options[] = 'LOCK IN SHARE MODE';
366 return $this;
367 }
368
376 public function forUpdate() {
377 $this->options[] = 'FOR UPDATE';
378 return $this;
379 }
380
386 public function distinct() {
387 $this->options[] = 'DISTINCT';
388 return $this;
389 }
390
397 public function setMaxExecutionTime( int $time ) {
398 $this->options['MAX_EXECUTION_TIME'] = $time;
399 return $this;
400 }
401
411 public function groupBy( $group ) {
412 $this->mergeOption( 'GROUP BY', $group );
413 return $this;
414 }
415
427 public function having( $having ) {
428 $this->mergeOption( 'HAVING', $having );
429 return $this;
430 }
431
442 public function orderBy( $fields, $direction = null ) {
443 if ( $direction === null ) {
444 $this->mergeOption( 'ORDER BY', $fields );
445 } elseif ( is_array( $fields ) ) {
446 $fieldsWithDirection = [];
447 foreach ( $fields as $field ) {
448 $fieldsWithDirection[] = "$field $direction";
449 }
450 $this->mergeOption( 'ORDER BY', $fieldsWithDirection );
451 } else {
452 $this->mergeOption( 'ORDER BY', "$fields $direction" );
453 }
454 return $this;
455 }
456
463 private function mergeOption( $name, $newArrayOrValue ) {
464 $value = isset( $this->options[$name] )
465 ? (array)$this->options[$name] : [];
466 if ( is_array( $newArrayOrValue ) ) {
467 $value = array_merge( $value, $newArrayOrValue );
468 } else {
469 $value[] = $newArrayOrValue;
470 }
471 $this->options[$name] = $value;
472 }
473
486 public function useIndex( $index ) {
487 $this->setIndexHint( 'USE INDEX', $index );
488 return $this;
489 }
490
503 public function ignoreIndex( $index ) {
504 $this->setIndexHint( 'IGNORE INDEX', $index );
505 return $this;
506 }
507
514 private function setIndexHint( $type, $value ) {
515 if ( !isset( $this->options[$type] ) ) {
516 $this->options[$type] = [];
517 } elseif ( !is_array( $this->options[$type] ) ) {
518 throw new \UnexpectedValueException(
519 __METHOD__ . ": The $type option cannot be appended to " .
520 'because it is not an array. This may have been caused by a prior ' .
521 'call to option() or options().' );
522 }
523 if ( is_array( $value ) ) {
524 $this->options[$type] = array_merge( $this->options[$type], $value );
525 } elseif ( $this->lastAlias === null ) {
526 throw new \UnexpectedValueException(
527 __METHOD__ . ': Cannot append index value since there is no' .
528 'prior table' );
529 } else {
530 $this->options[$type][$this->lastAlias] = $value;
531 }
532 }
533
539 public function explain() {
540 $this->options['EXPLAIN'] = true;
541 return $this;
542 }
543
549 public function straightJoin() {
550 $this->options[] = 'STRAIGHT_JOIN';
551 return $this;
552 }
553
559 public function bigResult() {
560 $this->options[] = 'SQL_BIG_RESULT';
561 return $this;
562 }
563
569 public function bufferResult() {
570 $this->options[] = 'SQL_BUFFER_RESULT';
571 return $this;
572 }
573
579 public function smallResult() {
580 $this->options[] = 'SQL_SMALL_RESULT';
581 return $this;
582 }
583
589 public function calcFoundRows() {
590 $this->options[] = 'SQL_CALC_FOUND_ROWS';
591 return $this;
592 }
593
602 public function option( $name, $value = null ) {
603 if ( $value === null ) {
604 $this->options[] = $name;
605 } else {
606 $this->options[$name] = $value;
607 }
608 return $this;
609 }
610
618 public function options( array $options ) {
619 $this->options = array_merge( $this->options, $options );
620 return $this;
621 }
622
629 public function caller( $fname ) {
630 $this->caller = $fname;
631 return $this;
632 }
633
639 public function fetchResultSet() {
640 return $this->db->select( $this->tables, $this->fields, $this->conds, $this->caller,
641 $this->options, $this->joinConds );
642 }
643
651 public function fetchField() {
652 if ( count( $this->fields ) !== 1 ) {
653 throw new \UnexpectedValueException(
654 __METHOD__ . ' expects the query to have only one field' );
655 }
656 $field = reset( $this->fields );
657 return $this->db->selectField( $this->tables, $field, $this->conds, $this->caller,
658 $this->options, $this->joinConds );
659 }
660
668 public function fetchFieldValues() {
669 if ( count( $this->fields ) !== 1 ) {
670 throw new \UnexpectedValueException(
671 __METHOD__ . ' expects the query to have only one field' );
672 }
673 $field = reset( $this->fields );
674 return $this->db->selectFieldValues( $this->tables, $field, $this->conds, $this->caller,
675 $this->options, $this->joinConds );
676 }
677
684 public function fetchRow() {
685 return $this->db->selectRow( $this->tables, $this->fields, $this->conds, $this->caller,
686 $this->options, $this->joinConds );
687 }
688
696 public function fetchRowCount() {
697 return $this->db->selectRowCount( $this->tables, $this->getRowCountVar(), $this->conds,
698 $this->caller, $this->options, $this->joinConds );
699 }
700
711 public function estimateRowCount() {
712 return $this->db->estimateRowCount( $this->tables, $this->getRowCountVar(), $this->conds,
713 $this->caller, $this->options, $this->joinConds );
714 }
715
722 private function getRowCountVar() {
723 if ( count( $this->fields ) === 0 ) {
724 return '*';
725 } elseif ( count( $this->fields ) === 1 ) {
726 return reset( $this->fields );
727 } else {
728 throw new \UnexpectedValueException(
729 __METHOD__ . ' expects the query to have at most one field' );
730 }
731 }
732
738 public function lockForUpdate() {
739 return $this->db->lockForUpdate( $this->tables, $this->conds, $this->caller,
740 $this->options, $this->joinConds );
741 }
742
754 public function buildGroupConcatField( $delim ) {
755 if ( count( $this->fields ) !== 1 ) {
756 throw new \UnexpectedValueException(
757 __METHOD__ . ' expects the query to have only one field' );
758 }
759 $field = reset( $this->fields );
760 return $this->db->buildGroupConcatField( $delim, $this->tables, $field,
761 $this->conds, $this->joinConds );
762 }
763
769 public function getSQL() {
770 return $this->db->selectSQLText( $this->tables, $this->fields, $this->conds, $this->caller,
771 $this->options, $this->joinConds );
772 }
773
788 public function getQueryInfo( $joinsName = 'join_conds' ) {
789 $info = [
790 'tables' => $this->tables,
791 'fields' => $this->fields,
792 'conds' => $this->conds,
793 'options' => $this->options,
794 ];
795 $info[ $joinsName ] = $this->joinConds;
796 return $info;
797 }
798}
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
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().
int $nextAutoAlias
An integer used to assign automatic aliases to tables and groups.
distinct()
Enable the DISTINCT option.
array $conds
The conditions to be passed to IDatabase::select()
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.
setIndexHint( $type, $value)
Private helper for methods that set index hints.
array $options
The options to be passed to IDatabase::select()
straightJoin()
Enable the STRAIGHT_JOIN option.
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.
array $fields
The fields to be passed to IDatabase::select()
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.
string $caller
The caller (function name) to be passed to IDatabase::select()
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().
mergeOption( $name, $newArrayOrValue)
Add a value to an option which may be not set or a string or array.
fields( $fields)
Add a field or an array of fields to the query.
getRowCountVar()
Private helper which extracts a field suitable for row counting from the fields array.
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:38
getType()
Get the RDBMS type of the server (e.g.