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 
101  if ( $batchSize < 1 ) {
102  throw new InvalidArgumentException( 'Batch size must be at least 1 row.' );
103  }
104  $this->db = $db;
105  $this->table = $table;
106  $this->primaryKey = (array)$primaryKey;
107  $this->fetchColumns = $this->primaryKey;
108  $this->orderBy = implode( ' ASC,', $this->primaryKey ) . ' ASC';
109  $this->batchSize = $batchSize;
110  }
111 
116  public function addConditions( array $conditions ) {
117  $this->conditions = array_merge( $this->conditions, $conditions );
118  }
119 
124  public function addOptions( array $options ) {
125  $this->options = array_merge( $this->options, $options );
126  }
127 
132  public function addJoinConditions( array $conditions ) {
133  $this->joinConditions = array_merge( $this->joinConditions, $conditions );
134  }
135 
140  public function setFetchColumns( array $columns ) {
141  // If it's not the all column selector merge in the primary keys we need
142  if ( count( $columns ) === 1 && reset( $columns ) === '*' ) {
143  $this->fetchColumns = $columns;
144  } else {
145  $this->fetchColumns = array_unique( array_merge(
146  $this->primaryKey,
147  $columns
148  ) );
149  }
150  }
151 
158  public function extractPrimaryKeys( $row ) {
159  $pk = [];
160  foreach ( $this->primaryKey as $alias => $column ) {
161  $name = is_numeric( $alias ) ? $column : $alias;
162  $pk[$name] = $row->{$name};
163  }
164  return $pk;
165  }
166 
170  public function current() {
171  return $this->current;
172  }
173 
177  public function key() {
178  return $this->key;
179  }
180 
184  public function rewind() {
185  $this->key = -1; // self::next() will turn this into 0
186  $this->current = [];
187  $this->next();
188  }
189 
193  public function valid() {
194  return (bool)$this->current;
195  }
196 
200  public function hasChildren() {
201  return $this->current && count( $this->current );
202  }
203 
207  public function getChildren() {
208  return new NotRecursiveIterator( new ArrayIterator( $this->current ) );
209  }
210 
214  public function next() {
215  $res = $this->db->select(
216  $this->table,
217  $this->fetchColumns,
218  $this->buildConditions(),
219  __METHOD__,
220  [
221  'LIMIT' => $this->batchSize,
222  'ORDER BY' => $this->orderBy,
223  ] + $this->options,
224  $this->joinConditions
225  );
226 
227  // The iterator is converted to an array because in addition to
228  // returning it in self::current() we need to use the end value
229  // in self::buildConditions()
230  $this->current = iterator_to_array( $res );
231  $this->key++;
232  }
233 
246  protected function buildConditions() {
247  if ( !$this->current ) {
248  return $this->conditions;
249  }
250 
251  $maxRow = end( $this->current );
252  $maximumValues = [];
253  foreach ( $this->primaryKey as $alias => $column ) {
254  $name = is_numeric( $alias ) ? $column : $alias;
255  $maximumValues[$column] = $this->db->addQuotes( $maxRow->{$name} );
256  }
257 
258  $pkConditions = [];
259  // For example: If we have 3 primary keys
260  // first run through will generate
261  // col1 = 4 AND col2 = 7 AND col3 > 1
262  // second run through will generate
263  // col1 = 4 AND col2 > 7
264  // and the final run through will generate
265  // col1 > 4
266  while ( $maximumValues ) {
267  $pkConditions[] = $this->buildGreaterThanCondition( $maximumValues );
268  array_pop( $maximumValues );
269  }
270 
271  $conditions = $this->conditions;
272  $conditions[] = sprintf( '( %s )', implode( ' ) OR ( ', $pkConditions ) );
273 
274  return $conditions;
275  }
276 
289  protected function buildGreaterThanCondition( array $quotedMaximumValues ) {
290  $keys = array_keys( $quotedMaximumValues );
291  $lastColumn = end( $keys );
292  $lastValue = array_pop( $quotedMaximumValues );
293  $conditions = [];
294  foreach ( $quotedMaximumValues as $column => $value ) {
295  $conditions[] = "$column = $value";
296  }
297  $conditions[] = "$lastColumn > $lastValue";
298 
299  return implode( ' AND ', $conditions );
300  }
301 }
BatchRowIterator\$db
IDatabase $db
The database to read from.
Definition: BatchRowIterator.php:38
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:140
BatchRowIterator\rewind
rewind()
Reset the iterator to the begining of the table.
Definition: BatchRowIterator.php:184
BatchRowIterator\valid
valid()
Definition: BatchRowIterator.php:193
BatchRowIterator\addConditions
addConditions(array $conditions)
Definition: BatchRowIterator.php:116
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:289
BatchRowIterator\extractPrimaryKeys
extractPrimaryKeys( $row)
Extracts the primary key(s) from a database row.
Definition: BatchRowIterator.php:158
$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:200
BatchRowIterator\next
next()
Fetch the next set of rows from the database.
Definition: BatchRowIterator.php:214
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:246
BatchRowIterator\addOptions
addOptions(array $options)
Definition: BatchRowIterator.php:124
NotRecursiveIterator
Definition: NotRecursiveIterator.php:27
BatchRowIterator\__construct
__construct(IDatabase $db, $table, $primaryKey, $batchSize)
Stable to call.
Definition: BatchRowIterator.php:100
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:177
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:132
BatchRowIterator\$fetchColumns
array $fetchColumns
List of column names to select from the table suitable for use with IDatabase::select()
Definition: BatchRowIterator.php:69
$keys
$keys
Definition: testCompression.php:72
BatchRowIterator\current
current()
Definition: BatchRowIterator.php:170
BatchRowIterator\getChildren
getChildren()
Definition: BatchRowIterator.php:207
BatchRowIterator\$options
array $options
Additional query options.
Definition: BatchRowIterator.php:89