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 
55  protected $conditions = [];
56 
60  protected $joinConditions = [];
61 
66  protected $fetchColumns;
67 
71  protected $orderBy;
72 
76  private $current = [];
77 
81  private $key;
82 
86  protected $options = [];
87 
96  if ( $batchSize < 1 ) {
97  throw new InvalidArgumentException( 'Batch size must be at least 1 row.' );
98  }
99  $this->db = $db;
100  $this->table = $table;
101  $this->primaryKey = (array)$primaryKey;
102  $this->fetchColumns = $this->primaryKey;
103  $this->orderBy = implode( ' ASC,', $this->primaryKey ) . ' ASC';
104  $this->batchSize = $batchSize;
105  }
106 
111  public function addConditions( array $conditions ) {
112  $this->conditions = array_merge( $this->conditions, $conditions );
113  }
114 
119  public function addOptions( array $options ) {
120  $this->options = array_merge( $this->options, $options );
121  }
122 
127  public function addJoinConditions( array $conditions ) {
128  $this->joinConditions = array_merge( $this->joinConditions, $conditions );
129  }
130 
135  public function setFetchColumns( array $columns ) {
136  // If it's not the all column selector merge in the primary keys we need
137  if ( count( $columns ) === 1 && reset( $columns ) === '*' ) {
138  $this->fetchColumns = $columns;
139  } else {
140  $this->fetchColumns = array_unique( array_merge(
141  $this->primaryKey,
142  $columns
143  ) );
144  }
145  }
146 
153  public function extractPrimaryKeys( $row ) {
154  $pk = [];
155  foreach ( $this->primaryKey as $alias => $column ) {
156  $name = is_numeric( $alias ) ? $column : $alias;
157  $pk[$name] = $row->{$name};
158  }
159  return $pk;
160  }
161 
165  public function current() {
166  return $this->current;
167  }
168 
172  public function key() {
173  return $this->key;
174  }
175 
179  public function rewind() {
180  $this->key = -1; // self::next() will turn this into 0
181  $this->current = [];
182  $this->next();
183  }
184 
188  public function valid() {
189  return (bool)$this->current;
190  }
191 
195  public function hasChildren() {
196  return $this->current && count( $this->current );
197  }
198 
202  public function getChildren() {
203  return new NotRecursiveIterator( new ArrayIterator( $this->current ) );
204  }
205 
209  public function next() {
210  $res = $this->db->select(
211  $this->table,
212  $this->fetchColumns,
213  $this->buildConditions(),
214  __METHOD__,
215  [
216  'LIMIT' => $this->batchSize,
217  'ORDER BY' => $this->orderBy,
218  ] + $this->options,
219  $this->joinConditions
220  );
221 
222  // The iterator is converted to an array because in addition to
223  // returning it in self::current() we need to use the end value
224  // in self::buildConditions()
225  $this->current = iterator_to_array( $res );
226  $this->key++;
227  }
228 
241  protected function buildConditions() {
242  if ( !$this->current ) {
243  return $this->conditions;
244  }
245 
246  $maxRow = end( $this->current );
247  $maximumValues = [];
248  foreach ( $this->primaryKey as $alias => $column ) {
249  $name = is_numeric( $alias ) ? $column : $alias;
250  $maximumValues[$column] = $this->db->addQuotes( $maxRow->{$name} );
251  }
252 
253  $pkConditions = [];
254  // For example: If we have 3 primary keys
255  // first run through will generate
256  // col1 = 4 AND col2 = 7 AND col3 > 1
257  // second run through will generate
258  // col1 = 4 AND col2 > 7
259  // and the final run through will generate
260  // col1 > 4
261  while ( $maximumValues ) {
262  $pkConditions[] = $this->buildGreaterThanCondition( $maximumValues );
263  array_pop( $maximumValues );
264  }
265 
267  $conditions[] = sprintf( '( %s )', implode( ' ) OR ( ', $pkConditions ) );
268 
269  return $conditions;
270  }
271 
284  protected function buildGreaterThanCondition( array $quotedMaximumValues ) {
285  $keys = array_keys( $quotedMaximumValues );
286  $lastColumn = end( $keys );
287  $lastValue = array_pop( $quotedMaximumValues );
288  $conditions = [];
289  foreach ( $quotedMaximumValues as $column => $value ) {
290  $conditions[] = "$column = $value";
291  }
292  $conditions[] = "$lastColumn > $lastValue";
293 
294  return implode( ' AND ', $conditions );
295  }
296 }
setFetchColumns(array $columns)
int $key
key 0-indexed number of pages fetched since self::reset()
extractPrimaryKeys( $row)
Extracts the primary key(s) from a database row.
array $options
Additional query options.
next()
Fetch the next set of rows from the database.
buildConditions()
Uses the primary key list and the maximal result row from the previous iteration to build an SQL cond...
addJoinConditions(array $conditions)
rewind()
Reset the iterator to the begining of the table.
Basic database interface for live and lazy-loaded relation database handles.
Definition: IDatabase.php:38
__construct(IDatabase $db, $table, $primaryKey, $batchSize)
addOptions(array $options)
buildGreaterThanCondition(array $quotedMaximumValues)
Given an array of column names and their maximum value generate an SQL condition where all keys excep...
addConditions(array $conditions)