MediaWiki  master
BatchRowIterator.php
Go to the documentation of this file.
1 <?php
2 
4 
29 class BatchRowIterator implements RecursiveIterator {
30 
34  protected $db;
35 
39  protected $table;
40 
44  protected $primaryKey;
45 
49  protected $batchSize;
50 
54  protected $conditions = [];
55 
59  protected $joinConditions = [];
60 
65  protected $fetchColumns;
66 
70  protected $orderBy;
71 
75  private $current = [];
76 
80  private $key;
81 
85  protected $options = [];
86 
95  if ( $batchSize < 1 ) {
96  throw new InvalidArgumentException( 'Batch size must be at least 1 row.' );
97  }
98  $this->db = $db;
99  $this->table = $table;
100  $this->primaryKey = (array)$primaryKey;
101  $this->fetchColumns = $this->primaryKey;
102  $this->orderBy = implode( ' ASC,', $this->primaryKey ) . ' ASC';
103  $this->batchSize = $batchSize;
104  }
105 
110  public function addConditions( array $conditions ) {
111  $this->conditions = array_merge( $this->conditions, $conditions );
112  }
113 
118  public function addOptions( array $options ) {
119  $this->options = array_merge( $this->options, $options );
120  }
121 
126  public function addJoinConditions( array $conditions ) {
127  $this->joinConditions = array_merge( $this->joinConditions, $conditions );
128  }
129 
134  public function setFetchColumns( array $columns ) {
135  // If it's not the all column selector merge in the primary keys we need
136  if ( count( $columns ) === 1 && reset( $columns ) === '*' ) {
137  $this->fetchColumns = $columns;
138  } else {
139  $this->fetchColumns = array_unique( array_merge(
140  $this->primaryKey,
141  $columns
142  ) );
143  }
144  }
145 
152  public function extractPrimaryKeys( $row ) {
153  $pk = [];
154  foreach ( $this->primaryKey as $alias => $column ) {
155  $name = is_numeric( $alias ) ? $column : $alias;
156  $pk[$name] = $row->{$name};
157  }
158  return $pk;
159  }
160 
164  public function current() {
165  return $this->current;
166  }
167 
171  public function key() {
172  return $this->key;
173  }
174 
178  public function rewind() {
179  $this->key = -1; // self::next() will turn this into 0
180  $this->current = [];
181  $this->next();
182  }
183 
187  public function valid() {
188  return (bool)$this->current;
189  }
190 
194  public function hasChildren() {
195  return $this->current && count( $this->current );
196  }
197 
201  public function getChildren() {
202  return new NotRecursiveIterator( new ArrayIterator( $this->current ) );
203  }
204 
208  public function next() {
209  $res = $this->db->select(
210  $this->table,
211  $this->fetchColumns,
212  $this->buildConditions(),
213  __METHOD__,
214  [
215  'LIMIT' => $this->batchSize,
216  'ORDER BY' => $this->orderBy,
217  ] + $this->options,
218  $this->joinConditions
219  );
220 
221  // The iterator is converted to an array because in addition to
222  // returning it in self::current() we need to use the end value
223  // in self::buildConditions()
224  $this->current = iterator_to_array( $res );
225  $this->key++;
226  }
227 
240  protected function buildConditions() {
241  if ( !$this->current ) {
242  return $this->conditions;
243  }
244 
245  $maxRow = end( $this->current );
246  $maximumValues = [];
247  foreach ( $this->primaryKey as $alias => $column ) {
248  $name = is_numeric( $alias ) ? $column : $alias;
249  $maximumValues[$column] = $this->db->addQuotes( $maxRow->{$name} );
250  }
251 
252  $pkConditions = [];
253  // For example: If we have 3 primary keys
254  // first run through will generate
255  // col1 = 4 AND col2 = 7 AND col3 > 1
256  // second run through will generate
257  // col1 = 4 AND col2 > 7
258  // and the final run through will generate
259  // col1 > 4
260  while ( $maximumValues ) {
261  $pkConditions[] = $this->buildGreaterThanCondition( $maximumValues );
262  array_pop( $maximumValues );
263  }
264 
265  $conditions = $this->conditions;
266  $conditions[] = sprintf( '( %s )', implode( ' ) OR ( ', $pkConditions ) );
267 
268  return $conditions;
269  }
270 
283  protected function buildGreaterThanCondition( array $quotedMaximumValues ) {
284  $keys = array_keys( $quotedMaximumValues );
285  $lastColumn = end( $keys );
286  $lastValue = array_pop( $quotedMaximumValues );
287  $conditions = [];
288  foreach ( $quotedMaximumValues as $column => $value ) {
289  $conditions[] = "$column = $value";
290  }
291  $conditions[] = "$lastColumn > $lastValue";
292 
293  return implode( ' AND ', $conditions );
294  }
295 }
BatchRowIterator\$db
IDatabase $db
The database to read from.
Definition: BatchRowIterator.php:34
BatchRowIterator\$batchSize
int $batchSize
The number of rows to fetch per iteration.
Definition: BatchRowIterator.php:49
BatchRowIterator\$joinConditions
array $joinConditions
Definition: BatchRowIterator.php:59
BatchRowIterator\$orderBy
string $orderBy
SQL Order by condition generated from $this->primaryKey.
Definition: BatchRowIterator.php:70
BatchRowIterator\setFetchColumns
setFetchColumns(array $columns)
Definition: BatchRowIterator.php:134
BatchRowIterator\rewind
rewind()
Reset the iterator to the begining of the table.
Definition: BatchRowIterator.php:178
BatchRowIterator\valid
valid()
Definition: BatchRowIterator.php:187
BatchRowIterator\addConditions
addConditions(array $conditions)
Definition: BatchRowIterator.php:110
BatchRowIterator
Definition: BatchRowIterator.php:29
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:283
BatchRowIterator\extractPrimaryKeys
extractPrimaryKeys( $row)
Extracts the primary key(s) from a database row.
Definition: BatchRowIterator.php:152
$res
$res
Definition: testCompression.php:57
BatchRowIterator\$current
array $current
The current iterator value.
Definition: BatchRowIterator.php:75
Wikimedia\Rdbms\IDatabase
Basic database interface for live and lazy-loaded relation database handles.
Definition: IDatabase.php:38
BatchRowIterator\hasChildren
hasChildren()
Definition: BatchRowIterator.php:194
BatchRowIterator\next
next()
Fetch the next set of rows from the database.
Definition: BatchRowIterator.php:208
BatchRowIterator\$primaryKey
array $primaryKey
The name of the primary key(s)
Definition: BatchRowIterator.php:44
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:240
BatchRowIterator\addOptions
addOptions(array $options)
Definition: BatchRowIterator.php:118
NotRecursiveIterator
Definition: NotRecursiveIterator.php:27
BatchRowIterator\__construct
__construct(IDatabase $db, $table, $primaryKey, $batchSize)
Definition: BatchRowIterator.php:94
BatchRowIterator\$table
string array $table
The name or names of the table to read from.
Definition: BatchRowIterator.php:39
BatchRowIterator\key
key()
Definition: BatchRowIterator.php:171
BatchRowIterator\$key
int $key
0-indexed number of pages fetched since self::reset()
Definition: BatchRowIterator.php:80
BatchRowIterator\$conditions
array $conditions
Array of strings containing SQL conditions to add to the query.
Definition: BatchRowIterator.php:54
BatchRowIterator\addJoinConditions
addJoinConditions(array $conditions)
Definition: BatchRowIterator.php:126
BatchRowIterator\$fetchColumns
array $fetchColumns
List of column names to select from the table suitable for use with IDatabase::select()
Definition: BatchRowIterator.php:65
$keys
$keys
Definition: testCompression.php:72
BatchRowIterator\current
current()
Definition: BatchRowIterator.php:164
BatchRowIterator\getChildren
getChildren()
Definition: BatchRowIterator.php:201
BatchRowIterator\$options
array $options
Additional query options.
Definition: BatchRowIterator.php:85