MediaWiki  master
BatchRowIterator.php
Go to the documentation of this file.
1 <?php
2 
4 
33 class BatchRowIterator implements RecursiveIterator {
34 
38  protected $db;
39 
43  protected $table;
44 
48  protected $primaryKey;
49 
53  protected $batchSize;
54 
58  protected $conditions = [];
59 
63  protected $joinConditions = [];
64 
69  protected $fetchColumns;
70 
74  protected $orderBy;
75 
79  private $current = [];
80 
84  private $key;
85 
89  protected $options = [];
90 
94  protected $caller;
95 
106  if ( $batchSize < 1 ) {
107  throw new InvalidArgumentException( 'Batch size must be at least 1 row.' );
108  }
109  $this->db = $db;
110  $this->table = $table;
111  $this->primaryKey = (array)$primaryKey;
112  $this->fetchColumns = $this->primaryKey;
113  $this->orderBy = implode( ' ASC,', $this->primaryKey ) . ' ASC';
114  $this->batchSize = $batchSize;
115  }
116 
121  public function addConditions( array $conditions ) {
122  $this->conditions = array_merge( $this->conditions, $conditions );
123  }
124 
129  public function addOptions( array $options ) {
130  $this->options = array_merge( $this->options, $options );
131  }
132 
137  public function addJoinConditions( array $conditions ) {
138  $this->joinConditions = array_merge( $this->joinConditions, $conditions );
139  }
140 
145  public function setFetchColumns( array $columns ) {
146  // If it's not the all column selector merge in the primary keys we need
147  if ( count( $columns ) === 1 && reset( $columns ) === '*' ) {
148  $this->fetchColumns = $columns;
149  } else {
150  $this->fetchColumns = array_unique( array_merge(
151  $this->primaryKey,
152  $columns
153  ) );
154  }
155  }
156 
165  public function setCaller( $caller ) {
166  $this->caller = $caller;
167 
168  return $this;
169  }
170 
177  public function extractPrimaryKeys( $row ) {
178  $pk = [];
179  foreach ( $this->primaryKey as $alias => $column ) {
180  $name = is_numeric( $alias ) ? $column : $alias;
181  $pk[$name] = $row->{$name};
182  }
183  return $pk;
184  }
185 
189  public function current() {
190  return $this->current;
191  }
192 
196  public function key() {
197  return $this->key;
198  }
199 
203  public function rewind() {
204  $this->key = -1; // self::next() will turn this into 0
205  $this->current = [];
206  $this->next();
207  }
208 
212  public function valid() {
213  return (bool)$this->current;
214  }
215 
219  public function hasChildren() {
220  return $this->current && count( $this->current );
221  }
222 
226  public function getChildren() {
227  return new NotRecursiveIterator( new ArrayIterator( $this->current ) );
228  }
229 
233  public function next() {
234  $caller = __METHOD__;
235  if ( (string)$this->caller !== '' ) {
236  $caller .= " (for {$this->caller})";
237  }
238 
239  $res = $this->db->select(
240  $this->table,
241  $this->fetchColumns,
242  $this->buildConditions(),
243  $caller,
244  [
245  'LIMIT' => $this->batchSize,
246  'ORDER BY' => $this->orderBy,
247  ] + $this->options,
248  $this->joinConditions
249  );
250 
251  // The iterator is converted to an array because in addition to
252  // returning it in self::current() we need to use the end value
253  // in self::buildConditions()
254  $this->current = iterator_to_array( $res );
255  $this->key++;
256  }
257 
270  protected function buildConditions() {
271  if ( !$this->current ) {
272  return $this->conditions;
273  }
274 
275  $maxRow = end( $this->current );
276  $maximumValues = [];
277  foreach ( $this->primaryKey as $alias => $column ) {
278  $name = is_numeric( $alias ) ? $column : $alias;
279  $maximumValues[$column] = $this->db->addQuotes( $maxRow->{$name} );
280  }
281 
282  $pkConditions = [];
283  // For example: If we have 3 primary keys
284  // first run through will generate
285  // col1 = 4 AND col2 = 7 AND col3 > 1
286  // second run through will generate
287  // col1 = 4 AND col2 > 7
288  // and the final run through will generate
289  // col1 > 4
290  while ( $maximumValues ) {
291  $pkConditions[] = $this->buildGreaterThanCondition( $maximumValues );
292  array_pop( $maximumValues );
293  }
294 
295  $conditions = $this->conditions;
296  $conditions[] = sprintf( '( %s )', implode( ' ) OR ( ', $pkConditions ) );
297 
298  return $conditions;
299  }
300 
313  protected function buildGreaterThanCondition( array $quotedMaximumValues ) {
314  $keys = array_keys( $quotedMaximumValues );
315  $lastColumn = end( $keys );
316  $lastValue = array_pop( $quotedMaximumValues );
317  $conditions = [];
318  foreach ( $quotedMaximumValues as $column => $value ) {
319  $conditions[] = "$column = $value";
320  }
321  $conditions[] = "$lastColumn > $lastValue";
322 
323  return implode( ' AND ', $conditions );
324  }
325 }
BatchRowIterator\$db
IDatabase $db
The database to read from.
Definition: BatchRowIterator.php:38
BatchRowIterator\$caller
$caller
For debugging which method is using this class.
Definition: BatchRowIterator.php:94
BatchRowIterator\$batchSize
int $batchSize
The number of rows to fetch per iteration.
Definition: BatchRowIterator.php:53
BatchRowIterator\$joinConditions
array $joinConditions
Definition: BatchRowIterator.php:63
BatchRowIterator\$orderBy
string $orderBy
SQL Order by condition generated from $this->primaryKey.
Definition: BatchRowIterator.php:74
BatchRowIterator\setFetchColumns
setFetchColumns(array $columns)
Definition: BatchRowIterator.php:145
BatchRowIterator\rewind
rewind()
Reset the iterator to the begining of the table.
Definition: BatchRowIterator.php:203
BatchRowIterator\valid
valid()
Definition: BatchRowIterator.php:212
BatchRowIterator\addConditions
addConditions(array $conditions)
Definition: BatchRowIterator.php:121
BatchRowIterator
Allows iterating a large number of rows in batches transparently.
Definition: BatchRowIterator.php:33
BatchRowIterator\buildGreaterThanCondition
buildGreaterThanCondition(array $quotedMaximumValues)
Given an array of column names and their maximum value generate an SQL condition where all keys excep...
Definition: BatchRowIterator.php:313
BatchRowIterator\extractPrimaryKeys
extractPrimaryKeys( $row)
Extracts the primary key(s) from a database row.
Definition: BatchRowIterator.php:177
$res
$res
Definition: testCompression.php:57
BatchRowIterator\$current
array $current
The current iterator value.
Definition: BatchRowIterator.php:79
Wikimedia\Rdbms\IDatabase
Basic database interface for live and lazy-loaded relation database handles.
Definition: IDatabase.php:38
BatchRowIterator\hasChildren
hasChildren()
Definition: BatchRowIterator.php:219
BatchRowIterator\next
next()
Fetch the next set of rows from the database.
Definition: BatchRowIterator.php:233
BatchRowIterator\$primaryKey
array $primaryKey
The name of the primary key(s)
Definition: BatchRowIterator.php:48
BatchRowIterator\buildConditions
buildConditions()
Uses the primary key list and the maximal result row from the previous iteration to build an SQL cond...
Definition: BatchRowIterator.php:270
BatchRowIterator\addOptions
addOptions(array $options)
Definition: BatchRowIterator.php:129
NotRecursiveIterator
Definition: NotRecursiveIterator.php:27
BatchRowIterator\__construct
__construct(IDatabase $db, $table, $primaryKey, $batchSize)
Stable to call.
Definition: BatchRowIterator.php:105
BatchRowIterator\$table
string array $table
The name or names of the table to read from.
Definition: BatchRowIterator.php:43
BatchRowIterator\key
key()
Definition: BatchRowIterator.php:196
BatchRowIterator\$key
int $key
0-indexed number of pages fetched since self::reset()
Definition: BatchRowIterator.php:84
BatchRowIterator\$conditions
array $conditions
Array of strings containing SQL conditions to add to the query.
Definition: BatchRowIterator.php:58
BatchRowIterator\addJoinConditions
addJoinConditions(array $conditions)
Definition: BatchRowIterator.php:137
BatchRowIterator\$fetchColumns
array $fetchColumns
List of column names to select from the table suitable for use with IDatabase::select()
Definition: BatchRowIterator.php:69
BatchRowIterator\setCaller
setCaller( $caller)
Use ->setCaller( METHOD ) to indicate which code is using this class.
Definition: BatchRowIterator.php:165
$keys
$keys
Definition: testCompression.php:72
BatchRowIterator\current
current()
Definition: BatchRowIterator.php:189
BatchRowIterator\getChildren
getChildren()
Definition: BatchRowIterator.php:226
BatchRowIterator\$options
array $options
Additional query options.
Definition: BatchRowIterator.php:89