Page Menu
Home
WickedGov Phorge
Search
Configure Global Search
Log In
Files
F5977514
SelectQueryBuilder.php
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
26 KB
Referenced Files
None
Subscribers
None
SelectQueryBuilder.php
View Options
<?php
namespace
Wikimedia\Rdbms
;
use
Wikimedia\Rdbms\Platform\ISQLPlatform
;
// Very long type annotations :(
// phpcs:disable Generic.Files.LineLength
/**
* Build SELECT queries with a fluent interface.
*
* Each query builder object must be used for a single database query only,
* and not be reused afterwards. To run multiple similar queries, you can
* create a query builder to set up most of your query, which you can use
* as a "template" to clone. You can then modify the cloned object for
* each individual query.
*
* Note that the methods in this class are not stable to override.
* This class may be extended to create query builders for specific database
* tables, such {@link \MediaWiki\Page\PageSelectQueryBuilder}, whilst still
* providing the same fluent interface for adding arbitrary additional
* conditions and such.
*
* @since 1.35
* @stable to extend
* @ingroup Database
*/
class
SelectQueryBuilder
extends
JoinGroupBase
{
/** sort the results in ascending order */
public
const
SORT_ASC
=
'ASC'
;
/** sort the results in descending order */
public
const
SORT_DESC
=
'DESC'
;
/**
* @var array The fields to be passed to IReadableDatabase::select()
*/
private
$fields
=
[];
/**
* @var array The conditions to be passed to IReadableDatabase::select()
*/
private
$conds
=
[];
/**
* @var string The caller (function name) to be passed to IReadableDatabase::select()
*/
private
$caller
=
__CLASS__
;
/**
* @var array The options to be passed to IReadableDatabase::select()
*/
protected
$options
=
[];
/**
* @var int An integer used to assign automatic aliases to tables and groups
*/
private
$nextAutoAlias
=
1
;
/**
* @var bool True if $this->caller has been set
*/
private
$isCallerOverridden
=
false
;
/** @var IReadableDatabase */
protected
IReadableDatabase
$db
;
/**
* Only for use in subclasses. To create a SelectQueryBuilder instance,
* use `$db->newSelectQueryBuilder()` instead.
*
* @param IReadableDatabase $db
*/
public
function
__construct
(
IReadableDatabase
$db
)
{
$this
->
db
=
$db
;
}
/**
* Change the IReadableDatabase object the query builder is bound to. The specified
* IReadableDatabase will subsequently be used to execute the query.
*
* @param IReadableDatabase $db
* @return $this
*/
public
function
connection
(
IReadableDatabase
$db
)
{
if
(
$this
->
db
->
getType
()
!==
$db
->
getType
()
)
{
throw
new
\InvalidArgumentException
(
__METHOD__
.
' cannot switch to a database of a different type.'
);
}
$this
->
db
=
$db
;
return
$this
;
}
/**
* Set the query parameters to the given values, appending to the values
* which were already set. This can be used to interface with legacy code.
* If a key is omitted, the previous value will be retained.
*
* The parameters must be formatted as required by IReadableDatabase::select. For
* example, JoinGroup cannot be used.
*
* @param array $info Associative array of query info, with keys:
* - tables: The raw array of tables to be passed to IReadableDatabase::select()
* - fields: The fields
* - conds: The conditions
* - options: The query options
* - join_conds: The join conditions
* - joins: Alias for join_conds. If both joins and join_conds are
* specified, the values will be merged.
* - caller: The caller signature
*
* @return $this
*/
public
function
queryInfo
(
$info
)
{
if
(
isset
(
$info
[
'tables'
]
)
)
{
$this
->
rawTables
(
$info
[
'tables'
]
);
}
if
(
isset
(
$info
[
'fields'
]
)
)
{
$this
->
fields
(
$info
[
'fields'
]
);
}
if
(
isset
(
$info
[
'conds'
]
)
)
{
$this
->
where
(
$info
[
'conds'
]
);
}
if
(
isset
(
$info
[
'options'
]
)
)
{
$this
->
options
(
(
array
)
$info
[
'options'
]
);
}
if
(
isset
(
$info
[
'join_conds'
]
)
)
{
$this
->
joinConds
(
(
array
)
$info
[
'join_conds'
]
);
}
if
(
isset
(
$info
[
'joins'
]
)
)
{
$this
->
joinConds
(
(
array
)
$info
[
'joins'
]
);
}
if
(
isset
(
$info
[
'caller'
]
)
)
{
$this
->
caller
(
$info
[
'caller'
]
);
}
return
$this
;
}
/**
* Given a table or table array as might be passed to IReadableDatabase::select(),
* append it to the existing tables, interpreting nested arrays as join
* groups.
*
* This can be used to interface with existing code that expresses join
* groups as nested arrays. In new code, join groups should generally
* be created with newJoinGroup(), which provides a fluent interface.
*
* @param string|array $tables Table references; see {@link IReadableDatabase::select}
* for details
* @return $this
*/
public
function
rawTables
(
$tables
)
{
if
(
is_array
(
$tables
)
)
{
$this
->
tables
=
array_merge
(
$this
->
tables
,
$tables
);
}
elseif
(
is_string
(
$tables
)
)
{
$this
->
tables
[]
=
$tables
;
}
else
{
throw
new
\InvalidArgumentException
(
__METHOD__
.
': $tables must be a string or array'
);
}
return
$this
;
}
/**
* Merge another query builder with this one. Append the other builder's
* tables, joins, fields, conditions and options to this one.
*
* @since 1.41
* @param SelectQueryBuilder $builder
* @return $this
*/
public
function
merge
(
SelectQueryBuilder
$builder
)
{
$this
->
rawTables
(
$builder
->
tables
);
$this
->
fields
(
$builder
->
fields
);
$this
->
where
(
$builder
->
conds
);
$this
->
options
(
$builder
->
options
);
$this
->
joinConds
(
$builder
->
joinConds
);
if
(
$builder
->
isCallerOverridden
)
{
$this
->
caller
(
$builder
->
caller
);
}
return
$this
;
}
/**
* Get an empty SelectQueryBuilder which can be used to build a subquery
* of this query.
* @return SelectQueryBuilder
*/
public
function
newSubquery
()
{
return
new
self
(
$this
->
db
);
}
/**
* Add a single table to the SELECT query. Alias for table().
*
* @param string|JoinGroup|SelectQueryBuilder $table Table reference; see {@link table}
* for details
* @param-taint $table exec_sql
* @param string|null $alias The table alias, or null for no alias
* @param-taint $alias exec_sql
* @return $this
*/
public
function
from
(
$table
,
$alias
=
null
)
{
return
$this
->
table
(
$table
,
$alias
);
}
/**
* Add multiple tables. It's recommended to use join() and leftJoin() instead in new code.
*
* @param string[] $tables Table references (string keys are aliases). See {@link table}
* for details.
* @param-taint $tables exec_sql
* @return $this
*/
public
function
tables
(
$tables
)
{
foreach
(
$tables
as
$alias
=>
$table
)
{
if
(
is_string
(
$alias
)
)
{
$this
->
table
(
$table
,
$alias
);
}
else
{
$this
->
table
(
$table
);
}
}
return
$this
;
}
/**
* Add a field or an array of fields to the query. Each field is an SQL
* fragment. If the array key is non-numeric, the key is taken to be an
* alias for the field.
*
* @see IReadableDatabase::select()
*
* @param string|string[] $fields
* @param-taint $fields exec_sql
* @return $this
*/
public
function
fields
(
$fields
)
{
if
(
is_array
(
$fields
)
)
{
$this
->
fields
=
array_merge
(
$this
->
fields
,
$fields
);
}
else
{
$this
->
fields
[]
=
$fields
;
}
return
$this
;
}
/**
* Add a field or an array of fields to the query. Alias for fields().
*
* @param string|string[] $fields
* @param-taint $fields exec_sql
* @return $this
*/
public
function
select
(
$fields
)
{
return
$this
->
fields
(
$fields
);
}
/**
* Add a single field to the query, optionally with an alias. The field is
* an SQL fragment. It is unsafe to pass user input to this function.
*
* @param string $field
* @param-taint $field exec_sql
* @param string|null $alias
* @param-taint $alias exec_sql
* @return $this
*/
public
function
field
(
$field
,
$alias
=
null
)
{
if
(
$alias
===
null
)
{
$this
->
fields
[]
=
$field
;
}
else
{
$this
->
fields
[
$alias
]
=
$field
;
}
return
$this
;
}
/**
* Remove all fields from the query.
*
* @return $this
*/
public
function
clearFields
()
{
$this
->
fields
=
[];
return
$this
;
}
/**
* Add conditions to the query. The supplied conditions will be appended
* to the existing conditions, separated by AND.
*
* @param string|IExpression|array<string,?scalar|non-empty-array<int,?scalar>|RawSQLValue>|array<int,string|IExpression> $conds
* @param-taint $conds exec_sql_numkey
*
* May be either a string containing a single condition, or an array of
* conditions. If an array is given, the conditions constructed from each
* element are combined with AND.
*
* Array elements may take one of two forms:
*
* - Elements with a numeric key are interpreted as raw SQL fragments.
* - Elements with a string key are interpreted as equality conditions,
* where the key is the field name.
* - If the value of such an array element is a scalar (such as a
* string), it will be treated as data and thus quoted appropriately.
* If it is null, an IS NULL clause will be added.
* - If the value is an array, an IN (...) clause will be constructed
* from its non-null elements, and an IS NULL clause will be added
* if null is present, such that the field may match any of the
* elements in the array. The non-null elements will be quoted.
*
* Note that expressions are often DBMS-dependent in their syntax.
* DBMS-independent wrappers are provided for constructing several types of
* expression commonly used in condition queries. See:
* - IReadableDatabase::buildLike()
* - IReadableDatabase::conditional()
*
* Untrusted user input is safe in the values of string keys, however untrusted
* input must not be used in the array key names or in the values of numeric keys.
* Escaping of untrusted input used in values of numeric keys should be done via
* IReadableDatabase::addQuotes()
*
* @return $this
*/
public
function
where
(
$conds
)
{
if
(
is_array
(
$conds
)
)
{
foreach
(
$conds
as
$key
=>
$cond
)
{
if
(
is_int
(
$key
)
)
{
$this
->
conds
[]
=
$cond
;
}
elseif
(
isset
(
$this
->
conds
[
$key
]
)
)
{
// @phan-suppress-previous-line PhanTypeMismatchDimFetch
// T288882
$this
->
conds
[]
=
$this
->
db
->
makeList
(
[
$key
=>
$cond
],
IReadableDatabase
::
LIST_AND
);
}
else
{
$this
->
conds
[
$key
]
=
$cond
;
}
}
}
else
{
$this
->
conds
[]
=
$conds
;
}
return
$this
;
}
/**
* Add conditions to the query. Alias for where().
*
* @param string|IExpression|array<string,?scalar|non-empty-array<int,?scalar>|RawSQLValue>|array<int,string|IExpression> $conds
* @param-taint $conds exec_sql_numkey
* @return $this
*/
public
function
andWhere
(
$conds
)
{
return
$this
->
where
(
$conds
);
}
/**
* Add conditions to the query. Alias for where().
*
* @param string|IExpression|array<string,?scalar|non-empty-array<int,?scalar>|RawSQLValue>|array<int,string|IExpression> $conds
* @param-taint $conds exec_sql_numkey
* @return $this
*/
public
function
conds
(
$conds
)
{
return
$this
->
where
(
$conds
);
}
/**
* Manually append to the $join_conds array which will be passed to
* IReadableDatabase::select(). This is not recommended for new code. Instead,
* join() and leftJoin() should be used.
*
* @param array $joinConds
* @return $this
*/
public
function
joinConds
(
array
$joinConds
)
{
$this
->
joinConds
=
array_merge
(
$this
->
joinConds
,
$joinConds
);
return
$this
;
}
/**
* Get a table alias which is unique to this SelectQueryBuilder
*
* @return string
*/
protected
function
getAutoAlias
()
{
return
'sqb'
.
(
$this
->
nextAutoAlias
++
);
}
/**
* Create a parenthesized group of joins which can be added to the object
* like a table. The group is initially empty.
*
* @return JoinGroup
*/
public
function
newJoinGroup
()
{
return
new
JoinGroup
(
$this
->
getAutoAlias
()
);
}
/**
* Set the offset. Skip this many rows at the start of the result set. Offset
* with limit() can theoretically be used for paging through a result set,
* but this is discouraged for performance reasons.
*
* If the query builder already has an offset, the old offset will be discarded.
*
* @param int $offset
* @return $this
*/
public
function
offset
(
$offset
)
{
$this
->
options
[
'OFFSET'
]
=
$offset
;
return
$this
;
}
/**
* Set the query limit. Return at most this many rows. The rows are sorted
* and then the first rows are taken until the limit is reached. Limit
* is applied to a result set after offset.
*
* If the query builder already has a limit, the old limit will be discarded.
*
* @param int $limit
* @return $this
*/
public
function
limit
(
$limit
)
{
$this
->
options
[
'LIMIT'
]
=
$limit
;
return
$this
;
}
/**
* Enable the LOCK IN SHARE MODE option. Lock the returned rows so that
* they can't be changed until the next COMMIT. Cannot be used with
* aggregate functions (COUNT, MAX, etc., but also DISTINCT).
*
* @return $this
*/
public
function
lockInShareMode
()
{
$this
->
options
[]
=
'LOCK IN SHARE MODE'
;
return
$this
;
}
/**
* Enable the FOR UPDATE option. Lock the returned rows so that
* they can't be changed until the next COMMIT. Cannot be used with
* aggregate functions (COUNT, MAX, etc., but also DISTINCT).
*
* @return $this
*/
public
function
forUpdate
()
{
$this
->
options
[]
=
'FOR UPDATE'
;
return
$this
;
}
/**
* Enable the DISTINCT option. Return only unique result rows.
*
* @return $this
*/
public
function
distinct
()
{
$this
->
options
[]
=
'DISTINCT'
;
return
$this
;
}
/**
* Set MAX_EXECUTION_TIME for queries.
*
* @param int $time maximum allowed time in milliseconds
* @return $this
*/
public
function
setMaxExecutionTime
(
int
$time
)
{
$this
->
options
[
'MAX_EXECUTION_TIME'
]
=
$time
;
return
$this
;
}
/**
* Add a GROUP BY clause. May be either an SQL fragment string naming a
* field or expression to group by, or an array of such SQL fragments.
*
* If there is an existing GROUP BY clause, the new one will be appended.
*
* @param string|string[] $group
* @param-taint $group exec_sql
* @return $this
*/
public
function
groupBy
(
$group
)
{
$this
->
mergeOption
(
'GROUP BY'
,
$group
);
return
$this
;
}
/**
* Add a HAVING clause. May be either a string containing a HAVING clause
* or an array of conditions building the HAVING clause. If an array is
* given, the conditions constructed from each element are combined with
* AND.
*
* If there is an existing HAVING clause, the new one will be appended.
*
* @param string|string[] $having
* @param-taint $having exec_sql_numkey
* @return $this
*/
public
function
having
(
$having
)
{
$this
->
mergeOption
(
'HAVING'
,
$having
);
return
$this
;
}
/**
* Set the ORDER BY clause. If it has already been set, append the
* additional fields to it.
*
* @param string[]|string $fields The field or list of fields to order by.
* @param-taint $fields exec_sql
* @param string|null $direction Sorting direction applied to all fields,
* self::SORT_ASC or self::SORT_DESC. If different fields need to be sorted in opposite
* directions, then this parameter must be omitted, and $fields must contain 'ASC' or 'DESC'
* after each field name.
* @param-taint $direction exec_sql
* @return $this
*/
public
function
orderBy
(
$fields
,
$direction
=
null
)
{
if
(
$direction
===
null
)
{
$this
->
mergeOption
(
'ORDER BY'
,
$fields
);
}
elseif
(
is_array
(
$fields
)
)
{
$fieldsWithDirection
=
[];
foreach
(
$fields
as
$field
)
{
$fieldsWithDirection
[]
=
"$field $direction"
;
}
$this
->
mergeOption
(
'ORDER BY'
,
$fieldsWithDirection
);
}
else
{
$this
->
mergeOption
(
'ORDER BY'
,
"$fields $direction"
);
}
return
$this
;
}
/**
* Add a value to an option which may be not set or a string or array.
*
* @param string $name
* @param string|string[] $newArrayOrValue
*/
private
function
mergeOption
(
$name
,
$newArrayOrValue
)
{
$value
=
isset
(
$this
->
options
[
$name
]
)
?
(
array
)
$this
->
options
[
$name
]
:
[];
if
(
is_array
(
$newArrayOrValue
)
)
{
$value
=
array_merge
(
$value
,
$newArrayOrValue
);
}
else
{
$value
[]
=
$newArrayOrValue
;
}
$this
->
options
[
$name
]
=
$value
;
}
/**
* Set a USE INDEX option.
*
* If a string is given, the index hint is applied to the most recently
* appended table or alias. If an array is given, it is assumed to be an
* associative array with the alias names in the keys and the indexes in
* the values, as in the USE INDEX option to IReadableDatabase::select(). The
* array will be merged with the existing value.
*
* @param string|string[] $index
* @param-taint $index exec_sql
* @return $this
*/
public
function
useIndex
(
$index
)
{
$this
->
setIndexHint
(
'USE INDEX'
,
$index
);
return
$this
;
}
/**
* Set the IGNORE INDEX option.
*
* If a string is given, the index hint is applied to the most recently
* appended table or alias. If an array is given, it is assumed to be an
* associative array with the alias names in the keys and the indexes in
* the values, as in the IGNORE INDEX option to IReadableDatabase::select(). The
* array will be merged with the existing value.
*
* @param string|string[] $index
* @param-taint $index exec_sql
* @return $this
*/
public
function
ignoreIndex
(
$index
)
{
$this
->
setIndexHint
(
'IGNORE INDEX'
,
$index
);
return
$this
;
}
/**
* Private helper for methods that set index hints.
*
* @param string $type
* @param string|string[] $value
*/
private
function
setIndexHint
(
$type
,
$value
)
{
if
(
!
isset
(
$this
->
options
[
$type
]
)
)
{
$this
->
options
[
$type
]
=
[];
}
elseif
(
!
is_array
(
$this
->
options
[
$type
]
)
)
{
throw
new
\UnexpectedValueException
(
__METHOD__
.
": The $type option cannot be appended to "
.
'because it is not an array. This may have been caused by a prior '
.
'call to option() or options().'
);
}
if
(
is_array
(
$value
)
)
{
$this
->
options
[
$type
]
=
array_merge
(
$this
->
options
[
$type
],
$value
);
}
elseif
(
$this
->
lastAlias
===
null
)
{
throw
new
\UnexpectedValueException
(
__METHOD__
.
': Cannot append index value since there is no'
.
'prior table'
);
}
else
{
$this
->
options
[
$type
][
$this
->
lastAlias
]
=
$value
;
}
}
/**
* Make the query be an EXPLAIN SELECT query instead of a SELECT query.
*
* @return $this
*/
public
function
explain
()
{
$this
->
options
[
'EXPLAIN'
]
=
true
;
return
$this
;
}
/**
* Enable the STRAIGHT_JOIN query option.
*
* @return $this
*/
public
function
straightJoinOption
()
{
$this
->
options
[]
=
'STRAIGHT_JOIN'
;
return
$this
;
}
/**
* Enable the SQL_BIG_RESULT option.
*
* @return $this
*/
public
function
bigResult
()
{
$this
->
options
[]
=
'SQL_BIG_RESULT'
;
return
$this
;
}
/**
* Enable the SQL_BUFFER_RESULT option.
*
* @return $this
*/
public
function
bufferResult
()
{
$this
->
options
[]
=
'SQL_BUFFER_RESULT'
;
return
$this
;
}
/**
* Enable the SQL_SMALL_RESULT option.
*
* @return $this
*/
public
function
smallResult
()
{
$this
->
options
[]
=
'SQL_SMALL_RESULT'
;
return
$this
;
}
/**
* Enable the SQL_CALC_FOUND_ROWS option.
*
* @return $this
*/
public
function
calcFoundRows
()
{
$this
->
options
[]
=
'SQL_CALC_FOUND_ROWS'
;
return
$this
;
}
/**
* Manually set an option in the $options array to be passed to
* IReadableDatabase::select()
*
* @param string $name The option name
* @param mixed $value The option value, or null for a boolean option
* @return $this
*/
public
function
option
(
$name
,
$value
=
null
)
{
if
(
$value
===
null
)
{
$this
->
options
[]
=
$name
;
}
else
{
$this
->
options
[
$name
]
=
$value
;
}
return
$this
;
}
/**
* Manually set multiple options in the $options array to be passed to
* IReadableDatabase::select().
*
* @param array $options
* @return $this
*/
public
function
options
(
array
$options
)
{
$this
->
options
=
array_merge
(
$this
->
options
,
$options
);
return
$this
;
}
/**
* @param int $recency Bitfield of IDBAccessObject::READ_* constants
* @return $this
*/
public
function
recency
(
$recency
)
{
if
(
(
$recency
&
IDBAccessObject
::
READ_EXCLUSIVE
)
==
IDBAccessObject
::
READ_EXCLUSIVE
)
{
$this
->
forUpdate
();
}
elseif
(
(
$recency
&
IDBAccessObject
::
READ_LOCKING
)
==
IDBAccessObject
::
READ_LOCKING
)
{
$this
->
lockInShareMode
();
}
return
$this
;
}
/**
* Set the method name to be included in an SQL comment.
*
* @param string $fname
* @param-taint $fname exec_sql
* @return $this
*/
public
function
caller
(
$fname
)
{
$this
->
caller
=
$fname
;
$this
->
isCallerOverridden
=
true
;
return
$this
;
}
/**
* get the method name of the caller, for use in sub classes
*
* @since 1.43
*/
final
protected
function
getCaller
():
string
{
return
$this
->
caller
;
}
/**
* Run the constructed SELECT query and return all results.
*
* @return IResultWrapper
* @return-taint tainted
*/
public
function
fetchResultSet
():
IResultWrapper
{
return
$this
->
db
->
select
(
$this
->
tables
,
$this
->
fields
,
$this
->
conds
,
$this
->
caller
,
$this
->
options
,
$this
->
joinConds
);
}
/**
* Run the constructed SELECT query, and return a single field extracted
* from the first result row. This may only be called when only one field
* has been added to the builder.
*
* @return mixed|false The value from the field, or false if nothing was found
* @return-taint tainted
*/
public
function
fetchField
()
{
if
(
count
(
$this
->
fields
)
!==
1
)
{
throw
new
\UnexpectedValueException
(
__METHOD__
.
' expects the query to have only one field'
);
}
$field
=
reset
(
$this
->
fields
);
return
$this
->
db
->
selectField
(
$this
->
tables
,
$field
,
$this
->
conds
,
$this
->
caller
,
$this
->
options
,
$this
->
joinConds
);
}
/**
* Run the constructed SELECT query, and extract a single field from each
* result row, returning an array containing all the values. This may only
* be called when only one field has been added to the builder.
*
* @return array
* @return-taint tainted
*/
public
function
fetchFieldValues
():
array
{
if
(
count
(
$this
->
fields
)
!==
1
)
{
throw
new
\UnexpectedValueException
(
__METHOD__
.
' expects the query to have only one field'
);
}
$field
=
reset
(
$this
->
fields
);
return
$this
->
db
->
selectFieldValues
(
$this
->
tables
,
$field
,
$this
->
conds
,
$this
->
caller
,
$this
->
options
,
$this
->
joinConds
);
}
/**
* Run the constructed SELECT query, and return the first result row. If
* there were no results, return false.
*
* @return \stdClass|false
* @return-taint tainted
*/
public
function
fetchRow
()
{
return
$this
->
db
->
selectRow
(
$this
->
tables
,
$this
->
fields
,
$this
->
conds
,
$this
->
caller
,
$this
->
options
,
$this
->
joinConds
);
}
/**
* Run the SELECT query, and return the number of results. This typically
* uses a subquery to discard the actual results on the server side, and
* is useful when counting rows with a limit.
*
* To count rows without a limit, it's more efficient to use a normal
* COUNT() expression, for example:
*
* $queryBuilder->select( 'COUNT(*)' )->from( 'page' )->fetchField()
*
* @return int
*/
public
function
fetchRowCount
():
int
{
return
$this
->
db
->
selectRowCount
(
$this
->
tables
,
$this
->
getRowCountVar
(),
$this
->
conds
,
$this
->
caller
,
$this
->
options
,
$this
->
joinConds
);
}
/**
* Estimate the number of rows in dataset
*
* MySQL allows you to estimate the number of rows that would be returned
* by a SELECT query, using EXPLAIN SELECT. The estimate is provided using
* index cardinality statistics, and is notoriously inaccurate, especially
* when large numbers of rows have recently been added or deleted.
*
* @return int
*/
public
function
estimateRowCount
():
int
{
return
$this
->
db
->
estimateRowCount
(
$this
->
tables
,
$this
->
getRowCountVar
(),
$this
->
conds
,
$this
->
caller
,
$this
->
options
,
$this
->
joinConds
);
}
/**
* Private helper which extracts a field suitable for row counting from the
* fields array
*
* @return string
*/
private
function
getRowCountVar
()
{
if
(
count
(
$this
->
fields
)
===
0
)
{
return
'*'
;
}
elseif
(
count
(
$this
->
fields
)
===
1
)
{
return
reset
(
$this
->
fields
);
}
else
{
throw
new
\UnexpectedValueException
(
__METHOD__
.
' expects the query to have at most one field'
);
}
}
/**
* Build a GROUP_CONCAT or equivalent statement for a query.
*
* This is useful for combining a field for several rows into a single string.
* NULL values will not appear in the output, duplicated values will appear,
* and the resulting delimiter-separated values have no defined sort order.
* Code using the results may need to use the PHP unique() or sort() methods.
*
* @param string $delim
* @return string
*/
public
function
buildGroupConcatField
(
$delim
)
{
if
(
count
(
$this
->
fields
)
!==
1
)
{
throw
new
\UnexpectedValueException
(
__METHOD__
.
' expects the query to have only one field'
);
}
$field
=
reset
(
$this
->
fields
);
return
$this
->
db
->
buildGroupConcatField
(
$delim
,
$this
->
tables
,
$field
,
$this
->
conds
,
$this
->
joinConds
);
}
/**
* Get the SQL query string which would be used by fetchResultSet().
*
* @return string
*/
public
function
getSQL
()
{
// Assume that whoever is calling this method is doing it to build a subquery
$caller
=
$this
->
isCallerOverridden
?
$this
->
caller
:
ISQLPlatform
::
CALLER_SUBQUERY
;
return
$this
->
db
->
selectSQLText
(
$this
->
tables
,
$this
->
fields
,
$this
->
conds
,
$caller
,
$this
->
options
,
$this
->
joinConds
);
}
/**
* Get an associative array describing the query in terms of its raw parameters to
* IReadableDatabase::select(). This can be used to interface with legacy code.
*
* @param string $joinsName The name of the join_conds key
* @return array The query info array, with keys:
* - tables: The table array
* - fields: The fields
* - conds: The conditions
* - options: The query options
* - join_conds: The join conditions. This can also be given a different
* name by passing a $joinsName parameter, since some legacy code uses
* the name "joins".
* - caller: The caller signature
*/
public
function
getQueryInfo
(
$joinsName
=
'join_conds'
)
{
$info
=
[
'tables'
=>
$this
->
tables
,
'fields'
=>
$this
->
fields
,
'conds'
=>
$this
->
conds
,
'options'
=>
$this
->
options
,
];
if
(
$this
->
caller
!==
__CLASS__
)
{
$info
[
'caller'
]
=
$this
->
caller
;
}
$info
[
$joinsName
]
=
$this
->
joinConds
;
return
$info
;
}
/**
* Execute the query, but throw away the results. This is intended for
* locking select queries. By calling this method, the caller is indicating
* that the query is only done to acquire locks on the selected rows. The
* field list is optional.
*
* Either forUpdate() or lockInShareMode() must be called before calling
* this method.
*
* @see self::forUpdate()
* @see self::lockInShareMode()
*
* @since 1.40
*/
public
function
acquireRowLocks
():
void
{
if
(
!
array_intersect
(
$this
->
options
,
[
'FOR UPDATE'
,
'LOCK IN SHARE MODE'
]
)
)
{
throw
new
\UnexpectedValueException
(
__METHOD__
.
' can only be called '
.
'after forUpdate() or lockInShareMode()'
);
}
$fields
=
$this
->
fields
?:
'1'
;
$this
->
db
->
select
(
$this
->
tables
,
$fields
,
$this
->
conds
,
$this
->
caller
,
$this
->
options
,
$this
->
joinConds
);
}
}
File Metadata
Details
Attached
Mime Type
text/x-php
Expires
Sat, Oct 3, 19:57 (3 d, 2 h ago)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
5d/5a/680919b480f2c1c012d29fa1856f
Default Alt Text
SelectQueryBuilder.php (26 KB)
Attached To
Mode
rMWPROD MediaWiki Production
Attached
Detach File
Event Timeline
Log In to Comment