27 public const SORT_ASC =
'ASC';
30 public const SORT_DESC =
'DESC';
45 private $caller = __CLASS__;
55 private $nextAutoAlias = 1;
60 private $isCallerOverridden =
false;
83 if ( $this->db->getType() !==
$db->
getType() ) {
84 throw new \InvalidArgumentException( __METHOD__ .
85 ' cannot switch to a database of a different type.' );
112 if ( isset( $info[
'tables'] ) ) {
115 if ( isset( $info[
'fields'] ) ) {
116 $this->
fields( $info[
'fields'] );
118 if ( isset( $info[
'conds'] ) ) {
119 $this->
where( $info[
'conds'] );
121 if ( isset( $info[
'options'] ) ) {
122 $this->
options( (array)$info[
'options'] );
124 if ( isset( $info[
'join_conds'] ) ) {
125 $this->
joinConds( (array)$info[
'join_conds'] );
127 if ( isset( $info[
'joins'] ) ) {
128 $this->
joinConds( (array)$info[
'joins'] );
130 if ( isset( $info[
'caller'] ) ) {
131 $this->
caller( $info[
'caller'] );
151 } elseif ( is_string(
$tables ) ) {
154 throw new \InvalidArgumentException( __METHOD__ .
155 ': $tables must be a string or array' );
174 if ( $builder->isCallerOverridden ) {
198 public function from( $table, $alias =
null ) {
199 return $this->
table( $table, $alias );
210 foreach (
$tables as $alias => $table ) {
211 if ( is_string( $alias ) ) {
212 $this->
table( $table, $alias );
214 $this->
table( $table );
232 if ( is_array( $fields ) ) {
235 $this->
fields[] = $fields;
248 return $this->
fields( $fields );
261 public function field( $field, $alias =
null ) {
262 if ( $alias ===
null ) {
265 $this->
fields[$alias] = $field;
318 if ( is_array( $conds ) ) {
319 foreach ( $conds as $key => $cond ) {
320 if ( is_int( $key ) ) {
321 $this->
conds[] = $cond;
322 } elseif ( isset( $this->
conds[$key] ) ) {
325 $this->
conds[] = $this->db->makeList(
326 [ $key => $cond ], IReadableDatabase::LIST_AND );
328 $this->
conds[$key] = $cond;
332 $this->
conds[] = $conds;
345 return $this->
where( $conds );
356 return $this->
where( $conds );
378 return 'sqb' . ( $this->nextAutoAlias++ );
402 $this->
options[
'OFFSET'] = $offset;
417 $this->
options[
'LIMIT'] = $limit;
429 $this->
options[] =
'LOCK IN SHARE MODE';
441 $this->
options[] =
'FOR UPDATE';
462 $this->
options[
'MAX_EXECUTION_TIME'] = $time;
476 $this->mergeOption(
'GROUP BY', $group );
492 $this->mergeOption(
'HAVING', $having );
506 public function orderBy( $fields, $direction =
null ) {
507 if ( $direction ===
null ) {
508 $this->mergeOption(
'ORDER BY', $fields );
509 } elseif ( is_array( $fields ) ) {
510 $fieldsWithDirection = [];
511 foreach ( $fields as $field ) {
512 $fieldsWithDirection[] =
"$field $direction";
514 $this->mergeOption(
'ORDER BY', $fieldsWithDirection );
516 $this->mergeOption(
'ORDER BY',
"$fields $direction" );
527 private function mergeOption( $name, $newArrayOrValue ) {
528 $value = isset( $this->
options[$name] )
529 ? (array)$this->
options[$name] : [];
530 if ( is_array( $newArrayOrValue ) ) {
531 $value = array_merge( $value, $newArrayOrValue );
533 $value[] = $newArrayOrValue;
535 $this->
options[$name] = $value;
551 $this->setIndexHint(
'USE INDEX', $index );
568 $this->setIndexHint(
'IGNORE INDEX', $index );
578 private function setIndexHint( $type, $value ) {
579 if ( !isset( $this->
options[$type] ) ) {
581 } elseif ( !is_array( $this->
options[$type] ) ) {
582 throw new \UnexpectedValueException(
583 __METHOD__ .
": The $type option cannot be appended to " .
584 'because it is not an array. This may have been caused by a prior ' .
585 'call to option() or options().' );
587 if ( is_array( $value ) ) {
588 $this->
options[$type] = array_merge( $this->
options[$type], $value );
589 } elseif ( $this->lastAlias ===
null ) {
590 throw new \UnexpectedValueException(
591 __METHOD__ .
': Cannot append index value since there is no' .
604 $this->
options[
'EXPLAIN'] =
true;
614 $this->
options[] =
'STRAIGHT_JOIN';
624 $this->
options[] =
'SQL_BIG_RESULT';
634 $this->
options[] =
'SQL_BUFFER_RESULT';
644 $this->
options[] =
'SQL_SMALL_RESULT';
654 $this->
options[] =
'SQL_CALC_FOUND_ROWS';
666 public function option( $name, $value =
null ) {
667 if ( $value ===
null ) {
670 $this->
options[$name] = $value;
696 $this->isCallerOverridden =
true;
720 if ( count( $this->
fields ) !== 1 ) {
721 throw new \UnexpectedValueException(
722 __METHOD__ .
' expects the query to have only one field' );
724 $field = reset( $this->
fields );
725 return $this->db->selectField( $this->
tables, $field, $this->
conds, $this->
caller,
738 if ( count( $this->
fields ) !== 1 ) {
739 throw new \UnexpectedValueException(
740 __METHOD__ .
' expects the query to have only one field' );
742 $field = reset( $this->
fields );
743 return $this->db->selectFieldValues( $this->
tables, $field, $this->
conds, $this->
caller,
772 return $this->db->selectRowCount( $this->
tables, $this->getRowCountVar(), $this->
conds,
787 return $this->db->estimateRowCount( $this->
tables, $this->getRowCountVar(), $this->
conds,
797 private function getRowCountVar() {
798 if ( count( $this->
fields ) === 0 ) {
800 } elseif ( count( $this->
fields ) === 1 ) {
801 return reset( $this->
fields );
803 throw new \UnexpectedValueException(
804 __METHOD__ .
' expects the query to have at most one field' );
820 if ( count( $this->
fields ) !== 1 ) {
821 throw new \UnexpectedValueException(
822 __METHOD__ .
' expects the query to have only one field' );
824 $field = reset( $this->
fields );
825 return $this->db->buildGroupConcatField( $delim, $this->
tables, $field,
857 'fields' => $this->fields,
858 'conds' => $this->conds,
861 if ( $this->
caller !== __CLASS__ ) {
862 $info[
'caller'] = $this->caller;
883 if ( !array_intersect( $this->
options, [
'FOR UPDATE',
'LOCK IN SHARE MODE' ] ) ) {
884 throw new \UnexpectedValueException( __METHOD__ .
' can only be called ' .
885 'after forUpdate() or lockInShareMode()' );
887 $fields = $this->
fields ?:
'1';
if(!defined('MW_SETUP_CALLBACK'))