Completed
Push — master ( 09acfe...9d8ae9 )
by Michal
07:25
created

CreateStatement   C

Complexity

Total Complexity 69

Size/Duplication

Total Lines 569
Duplicated Lines 3.87 %

Coupling/Cohesion

Components 1
Dependencies 12

Test Coverage

Coverage 100%

Importance

Changes 15
Bugs 6 Features 1
Metric Value
wmc 69
c 15
b 6
f 1
lcom 1
cbo 12
dl 22
loc 569
ccs 225
cts 225
cp 1
rs 5.5752

2 Methods

Rating   Name   Duplication   Size   Complexity  
D build() 0 76 18
F parse() 22 253 51

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like CreateStatement often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use CreateStatement, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/**
4
 * `CREATE` statement.
5
 *
6
 * @package    SqlParser
7
 * @subpackage Statements
8
 */
9
namespace SqlParser\Statements;
10
11
use SqlParser\Parser;
12
use SqlParser\Statement;
13
use SqlParser\Token;
14
use SqlParser\TokensList;
15
use SqlParser\Components\ArrayObj;
16
use SqlParser\Components\DataType;
17
use SqlParser\Components\CreateDefinition;
18
use SqlParser\Components\PartitionDefinition;
19
use SqlParser\Components\Expression;
20
use SqlParser\Components\OptionsArray;
21
use SqlParser\Components\ParameterDefinition;
22
use SqlParser\Statements\SelectStatement;
23
24
/**
25
 * `CREATE` statement.
26
 *
27
 * @category   Statements
28
 * @package    SqlParser
29
 * @subpackage Statements
30
 * @author     Dan Ungureanu <[email protected]>
31
 * @license    http://opensource.org/licenses/GPL-2.0 GNU Public License
32
 */
33
class CreateStatement extends Statement
34
{
35
36
    /**
37
     * Options for `CREATE` statements.
38
     *
39
     * @var array
40
     */
41
    public static $OPTIONS = array(
42
43
        // CREATE TABLE
44
        'TEMPORARY'                     => 1,
45
46
        // CREATE VIEW
47
        'OR REPLACE'                    => array(2, 'var='),
48
        'ALGORITHM'                     => array(3, 'var='),
49
        // `DEFINER` is also used for `CREATE FUNCTION / PROCEDURE`
50
        'DEFINER'                       => array(4, 'var='),
51
        'SQL SECURITY'                  => array(5, 'var'),
52
53
        'DATABASE'                      => 6,
54
        'EVENT'                         => 6,
55
        'FUNCTION'                      => 6,
56
        'INDEX'                         => 6,
57
        'UNIQUE INDEX'                  => 6,
58
        'FULLTEXT INDEX'                => 6,
59
        'SPATIAL INDEX'                 => 6,
60
        'PROCEDURE'                     => 6,
61
        'SERVER'                        => 6,
62
        'TABLE'                         => 6,
63
        'TABLESPACE'                    => 6,
64
        'TRIGGER'                       => 6,
65
        'USER'                          => 6,
66
        'VIEW'                          => 6,
67
68
        // CREATE TABLE
69
        'IF NOT EXISTS'                 => 7,
70
    );
71
72
    /**
73
     * All database options.
74
     *
75
     * @var array
76
     */
77
    public static $DB_OPTIONS = array(
78
        'CHARACTER SET'                 => array(1, 'var='),
79
        'CHARSET'                       => array(1, 'var='),
80
        'DEFAULT CHARACTER SET'         => array(1, 'var='),
81
        'DEFAULT CHARSET'               => array(1, 'var='),
82
        'DEFAULT COLLATE'               => array(2, 'var='),
83
        'COLLATE'                       => array(2, 'var='),
84
    );
85
86
    /**
87
     * All table options.
88
     *
89
     * @var array
90
     */
91
    public static $TABLE_OPTIONS = array(
92
        'ENGINE'                        => array(1, 'var='),
93
        'AUTO_INCREMENT'                => array(2, 'var='),
94
        'AVG_ROW_LENGTH'                => array(3, 'var'),
95
        'CHARACTER SET'                 => array(4, 'var='),
96
        'CHARSET'                       => array(4, 'var='),
97
        'DEFAULT CHARACTER SET'         => array(4, 'var='),
98
        'DEFAULT CHARSET'               => array(4, 'var='),
99
        'CHECKSUM'                      => array(5, 'var'),
100
        'DEFAULT COLLATE'               => array(6, 'var='),
101
        'COLLATE'                       => array(6, 'var='),
102
        'COMMENT'                       => array(7, 'var='),
103
        'CONNECTION'                    => array(8, 'var'),
104
        'DATA DIRECTORY'                => array(9, 'var'),
105
        'DELAY_KEY_WRITE'               => array(10, 'var'),
106
        'INDEX DIRECTORY'               => array(11, 'var'),
107
        'INSERT_METHOD'                 => array(12, 'var'),
108
        'KEY_BLOCK_SIZE'                => array(13, 'var'),
109
        'MAX_ROWS'                      => array(14, 'var'),
110
        'MIN_ROWS'                      => array(15, 'var'),
111
        'PACK_KEYS'                     => array(16, 'var'),
112
        'PASSWORD'                      => array(17, 'var'),
113
        'ROW_FORMAT'                    => array(18, 'var'),
114
        'TABLESPACE'                    => array(19, 'var'),
115
        'STORAGE'                       => array(20, 'var'),
116
        'UNION'                         => array(21, 'var'),
117
    );
118
119
    /**
120
     * All function options.
121
     *
122
     * @var array
123
     */
124
    public static $FUNC_OPTIONS = array(
125
        'COMMENT'                       => array(1, 'var='),
126
        'LANGUAGE SQL'                  => 2,
127
        'DETERMINISTIC'                 => 3,
128
        'NOT DETERMINISTIC'             => 3,
129
        'CONTAINS SQL'                  => 4,
130
        'NO SQL'                        => 4,
131
        'READS SQL DATA'                => 4,
132
        'MODIFIES SQL DATA'             => 4,
133
        'SQL SECURITY DEFINER'          => array(5, 'var'),
134
    );
135
136
    /**
137
     * All trigger options.
138
     *
139
     * @var array
140
     */
141
    public static $TRIGGER_OPTIONS = array(
142
        'BEFORE'                        => 1,
143
        'AFTER'                         => 1,
144
        'INSERT'                        => 2,
145
        'UPDATE'                        => 2,
146
        'DELETE'                        => 2,
147
    );
148
149
    /**
150
     * The name of the entity that is created.
151
     *
152
     * Used by all `CREATE` statements.
153
     *
154
     * @var Expression
155
     */
156
    public $name;
157
158
    /**
159
     * The options of the entity (table, procedure, function, etc.).
160
     *
161
     * Used by `CREATE TABLE`, `CREATE FUNCTION` and `CREATE PROCEDURE`.
162
     *
163
     * @var OptionsArray
164
     *
165
     * @see static::$TABLE_OPTIONS
166
     * @see static::$FUNC_OPTIONS
167
     * @see static::$TRIGGER_OPTIONS
168
     */
169
    public $entityOptions;
170
171
    /**
172
     * If `CREATE TABLE`, a list of columns and keys.
173
     * If `CREATE VIEW`, a list of columns.
174
     *
175
     * Used by `CREATE TABLE` and `CREATE VIEW`.
176
     *
177
     * @var CreateDefinition[]|ArrayObj
178
     */
179
    public $fields;
180
181
    /**
182
     * If `CREATE TABLE ... SELECT`
183
     *
184
     * Used by `CREATE TABLE`
185
     *
186
     * @var SelectStatement
187
     */
188
    public $select;
189
190
    /**
191
     * Expression used for partitioning.
192
     *
193
     * @var string
194
     */
195
    public $partitionBy;
196
197
    /**
198
     * The number of partitions.
199
     *
200
     * @var int
201
     */
202
    public $partitionsNum;
203
204
    /**
205
     * Expression used for subpartitioning.
206
     *
207
     * @var string
208
     */
209
    public $subpartitionBy;
210
211
    /**
212
     * The number of subpartitions.
213
     *
214
     * @var int
215
     */
216
    public $subpartitionsNum;
217
218
    /**
219
     * The partition of the new table.
220
     *
221
     * @var PartitionDefinition[]
222
     */
223
    public $partitions;
224
225
    /**
226
     * If `CREATE TRIGGER` the name of the table.
227
     *
228
     * Used by `CREATE TRIGGER`.
229
     *
230
     * @var Expression
231
     */
232
    public $table;
233
234
    /**
235
     * The return data type of this routine.
236
     *
237
     * Used by `CREATE FUNCTION`.
238
     *
239
     * @var DataType
240
     */
241
    public $return;
242
243
    /**
244
     * The parameters of this routine.
245
     *
246
     * Used by `CREATE FUNCTION` and `CREATE PROCEDURE`.
247
     *
248
     * @var ParameterDefinition[]
249
     */
250
    public $parameters;
251
252
    /**
253
     * The body of this function or procedure. For views, it is the select
254
     * statement that gets the
255
     *
256
     * Used by `CREATE FUNCTION`, `CREATE PROCEDURE` and `CREATE VIEW`.
257
     *
258
     * @var Token[]|string
259
     */
260
    public $body = array();
261
262
    /**
263
     * @return string
264
     */
265 10
    public function build()
266
    {
267 10
        $fields = '';
268 10
        if (!empty($this->fields)) {
269 5
            if (is_array($this->fields)) {
270 4
                $fields = CreateDefinition::build($this->fields) . ' ';
271 5
            } elseif ($this->fields instanceof ArrayObj) {
272 1
                $fields = ArrayObj::build($this->fields);
273 1
            }
274 5
        }
275 10
        if ($this->options->has('DATABASE')) {
276
            return 'CREATE '
277 1
                . OptionsArray::build($this->options) . ' '
278 1
                . Expression::build($this->name) . ' '
279 1
                . OptionsArray::build($this->entityOptions);
280 9
        } elseif ($this->options->has('TABLE') && !is_null($this->select)) {
281
            return 'CREATE '
282 1
                . OptionsArray::build($this->options) . ' '
283 1
                . Expression::build($this->name) . ' '
284 1
                . $this->select->build();
285 8
        } elseif ($this->options->has('TABLE')) {
286 4
            $partition = '';
287
288 4
            if (!empty($this->partitionBy)) {
289 1
                $partition .= "\nPARTITION BY " . $this->partitionBy;
290 1
            }
291 4
            if (!empty($this->partitionsNum)) {
292 1
                $partition .= "\nPARTITIONS " . $this->partitionsNum;
293 1
            }
294 4
            if (!empty($this->subpartitionBy)) {
295 1
                $partition .= "\nSUBPARTITION BY " . $this->subpartitionBy;
296 1
            }
297 4
            if (!empty($this->subpartitionsNum)) {
298 1
                $partition .= "\nSUBPARTITIONS " . $this->subpartitionsNum;
299 1
            }
300 4
            if (!empty($this->partitions)) {
301 1
                $partition .= "\n" . PartitionDefinition::build($this->partitions);
302 1
            }
303
304
            return 'CREATE '
305 4
                . OptionsArray::build($this->options) . ' '
306 4
                . Expression::build($this->name) . ' '
307 4
                . $fields
308 4
                . OptionsArray::build($this->entityOptions)
309 4
                . $partition;
310 4
        } elseif ($this->options->has('VIEW')) {
311
            return 'CREATE '
312 1
                . OptionsArray::build($this->options) . ' '
313 1
                . Expression::build($this->name) . ' '
314 1
                . $fields . ' AS ' . TokensList::build($this->body) . ' '
315 1
                . OptionsArray::build($this->entityOptions);
316 3
        } elseif ($this->options->has('TRIGGER')) {
317
            return 'CREATE '
318 1
                . OptionsArray::build($this->options) . ' '
319 1
                . Expression::build($this->name) . ' '
320 1
                . OptionsArray::build($this->entityOptions) . ' '
321 1
                . 'ON ' . Expression::build($this->table) . ' '
322 1
                . 'FOR EACH ROW ' . TokensList::build($this->body);
323 2
        } elseif (($this->options->has('PROCEDURE'))
324 2
            || ($this->options->has('FUNCTION'))
325 2
        ) {
326 1
            $tmp = '';
327 1
            if ($this->options->has('FUNCTION')) {
328 1
                $tmp = 'RETURNS ' . DataType::build($this->return);
329 1
            }
330
            return 'CREATE '
331 1
                . OptionsArray::build($this->options) . ' '
332 1
                . Expression::build($this->name) . ' '
333 1
                . ParameterDefinition::build($this->parameters) . ' '
334 1
                . $tmp . ' ' . TokensList::build($this->body);
335
        }
336
        return 'CREATE '
337 1
            . OptionsArray::build($this->options) . ' '
338 1
            . Expression::build($this->name) . ' '
339 1
            . TokensList::build($this->body);
340
    }
341
342
    /**
343
     * @param Parser     $parser The instance that requests parsing.
344
     * @param TokensList $list   The list of tokens to be parsed.
345
     *
346
     * @return void
347
     */
348 42
    public function parse(Parser $parser, TokensList $list)
349
    {
350 42
        ++$list->idx; // Skipping `CREATE`.
351
352
        // Parsing options.
353 42
        $this->options = OptionsArray::parse($parser, $list, static::$OPTIONS);
354 42
        ++$list->idx; // Skipping last option.
355
356
        // Parsing the field name.
357 42
        $this->name = Expression::parse(
358 42
            $parser,
359 42
            $list,
360
            array(
361 42
                'parseField' => 'table',
362 42
                'breakOnAlias' => true,
363
            )
364 42
        );
365
366 42
        if ((!isset($this->name)) || ($this->name === '')) {
367 1
            $parser->error(
368 1
                __('The name of the entity was expected.'),
369 1
                $list->tokens[$list->idx]
370 1
            );
371 1
        } else {
372 41
            ++$list->idx; // Skipping field.
373
        }
374
375
        /**
376
         * Token parsed at this moment.
377
         *
378
         * @var Token $token
379
         */
380 42
        $token = $list->tokens[$list->idx];
381 42
        $nextidx = $list->idx + 1;
382 42
        while ($nextidx < $list->count && $list->tokens[$nextidx]->type == Token::TYPE_WHITESPACE) {
383 22
            $nextidx++;
384 22
        }
385
386 42
        if ($this->options->has('DATABASE')) {
387 1
            $this->entityOptions = OptionsArray::parse(
388 1
                $parser,
389 1
                $list,
390
                static::$DB_OPTIONS
391 1
            );
392 42
        } elseif ($this->options->has('TABLE') && ($token->type == Token::TYPE_KEYWORD) && ($token->value == 'SELECT')) {
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 121 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
393
            /* CREATE TABLE ... SELECT */
394 2
            $this->select = new SelectStatement($parser, $list);
395 2
        } elseif (
396 39
                $this->options->has('TABLE') &&
397 39
                ($token->type == Token::TYPE_KEYWORD) && ($token->value == 'AS') &&
398 39
                ($list->tokens[$nextidx]->type == Token::TYPE_KEYWORD) && ($list->tokens[$nextidx]->value == 'SELECT')
399 39
            ) {
400
            /* CREATE TABLE ... AS SELECT */
401 1
            $list->idx = $nextidx;
402 1
            $this->select = new SelectStatement($parser, $list);
403 39
        } elseif ($this->options->has('TABLE')) {
404 20
            $this->fields = CreateDefinition::parse($parser, $list);
405 20
            if (empty($this->fields)) {
406 1
                $parser->error(
407 1
                    __('At least one column definition was expected.'),
408 1
                    $list->tokens[$list->idx]
409 1
                );
410 1
            }
411 20
            ++$list->idx;
412
413 20
            $this->entityOptions = OptionsArray::parse(
414 20
                $parser,
415 20
                $list,
416
                static::$TABLE_OPTIONS
417 20
            );
418
419
            /**
420
             * The field that is being filled (`partitionBy` or
421
             * `subpartitionBy`).
422
             *
423
             * @var string $field
424
             */
425 20
            $field = null;
426
427
            /**
428
             * The number of brackets. `false` means no bracket was found
429
             * previously. At least one bracket is required to validate the
430
             * expression.
431
             *
432
             * @var int|bool $brackets
433
             */
434 20
            $brackets = false;
435
436
            /*
437
             * Handles partitions.
438
             */
439 20
            for (; $list->idx < $list->count; ++$list->idx) {
440
                /**
441
                 * Token parsed at this moment.
442
                 *
443
                 * @var Token $token
444
                 */
445 20
                $token = $list->tokens[$list->idx];
446
447
                // End of statement.
448 20
                if ($token->type === Token::TYPE_DELIMITER) {
449 17
                    break;
450
                }
451
452
                // Skipping comments.
453 20
                if ($token->type === Token::TYPE_COMMENT) {
454 1
                    continue;
455
                }
456
457 20
                if (($token->type === Token::TYPE_KEYWORD) && ($token->value === 'PARTITION BY')) {
458 2
                    $field = 'partitionBy';
459 2
                    $brackets = false;
460 20
                } elseif (($token->type === Token::TYPE_KEYWORD) && ($token->value === 'SUBPARTITION BY')) {
461 2
                    $field = 'subpartitionBy';
462 2
                    $brackets = false;
463 20
                } elseif (($token->type === Token::TYPE_KEYWORD) && ($token->value === 'PARTITIONS')) {
464 2
                    $token = $list->getNextOfType(Token::TYPE_NUMBER);
465 2
                    --$list->idx; // `getNextOfType` also advances one position.
466 2
                    $this->partitionsNum = $token->value;
0 ignored issues
show
Documentation Bug introduced by
The property $partitionsNum was declared of type integer, but $token->value is of type string. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
467 20
                } elseif (($token->type === Token::TYPE_KEYWORD) && ($token->value === 'SUBPARTITIONS')) {
468 2
                    $token = $list->getNextOfType(Token::TYPE_NUMBER);
469 2
                    --$list->idx; // `getNextOfType` also advances one position.
470 2
                    $this->subpartitionsNum = $token->value;
0 ignored issues
show
Documentation Bug introduced by
The property $subpartitionsNum was declared of type integer, but $token->value is of type string. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
471 20
                } elseif (!empty($field)) {
472
                    /*
473
                     * Handling the content of `PARTITION BY` and `SUBPARTITION BY`.
474
                     */
475
476
                    // Counting brackets.
477 2 View Code Duplication
                    if (($token->type === Token::TYPE_OPERATOR) && ($token->value === '(')) {
478
                        // This is used instead of `++$brackets` because,
479
                        // initially, `$brackets` is `false` cannot be
480
                        // incremented.
481 2
                        $brackets = $brackets + 1;
482 2
                    } elseif (($token->type === Token::TYPE_OPERATOR) && ($token->value === ')')) {
483 2
                        --$brackets;
484 2
                    }
485
486
                    // Building the expression used for partitioning.
487 2
                    $this->$field .= ($token->type === Token::TYPE_WHITESPACE) ? ' ' : $token->token;
488
489
                    // Last bracket was read, the expression ended.
490
                    // Comparing with `0` and not `false`, because `false` means
491
                    // that no bracket was found and at least one must is
492
                    // required.
493 2
                    if ($brackets === 0) {
494 2
                        $this->$field = trim($this->$field);
495 2
                        $field = null;
496 2
                    }
497 20
                } elseif (($token->type === Token::TYPE_OPERATOR) && ($token->value === '(')) {
498 3
                    if (!empty($this->partitionBy)) {
499 2
                        $this->partitions = ArrayObj::parse(
0 ignored issues
show
Documentation Bug introduced by
It seems like \SqlParser\Components\Ar...\PartitionDefinition')) can also be of type object<SqlParser\Components\ArrayObj>. However, the property $partitions is declared as type array<integer,object<Sql...s\PartitionDefinition>>. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
500 2
                            $parser,
501 2
                            $list,
502
                            array(
503
                                'type' => 'SqlParser\\Components\\PartitionDefinition'
504 2
                            )
505 2
                        );
506 2
                    }
507 3
                    break;
508
                }
509 20
            }
510 38
        } elseif (($this->options->has('PROCEDURE'))
511 13
            || ($this->options->has('FUNCTION'))
512 18
        ) {
513 10
            $this->parameters = ParameterDefinition::parse($parser, $list);
514 10
            if ($this->options->has('FUNCTION')) {
515 5
                $token = $list->getNextOfType(Token::TYPE_KEYWORD);
516 5
                if ($token->value !== 'RETURNS') {
517 2
                    $parser->error(
518 2
                        __('A "RETURNS" keyword was expected.'),
519
                        $token
520 2
                    );
521 2
                } else {
522 3
                    ++$list->idx;
523 3
                    $this->return = DataType::parse(
524 3
                        $parser,
525
                        $list
526 3
                    );
527
                }
528 5
            }
529 10
            ++$list->idx;
530
531 10
            $this->entityOptions = OptionsArray::parse(
532 10
                $parser,
533 10
                $list,
534
                static::$FUNC_OPTIONS
535 10
            );
536 10
            ++$list->idx;
537
538 10
            for (; $list->idx < $list->count; ++$list->idx) {
539 9
                $token = $list->tokens[$list->idx];
540 9
                $this->body[] = $token;
541 9
            }
542 18
        } elseif ($this->options->has('VIEW')) {
543 3
            $token = $list->getNext(); // Skipping whitespaces and comments.
544
545
            // Parsing columns list.
546 3
            if (($token->type === Token::TYPE_OPERATOR) && ($token->value === '(')) {
547 2
                --$list->idx; // getNext() also goes forward one field.
548 2
                $this->fields = ArrayObj::parse($parser, $list);
549 2
                ++$list->idx; // Skipping last token from the array.
550 2
                $list->getNext();
551 2
            }
552
553
            // Parsing the `AS` keyword.
554 3 View Code Duplication
            for (; $list->idx < $list->count; ++$list->idx) {
555 3
                $token = $list->tokens[$list->idx];
556 3
                if ($token->type === Token::TYPE_DELIMITER) {
557 3
                    break;
558
                }
559 3
                $this->body[] = $token;
560 3
            }
561 8
        } elseif ($this->options->has('TRIGGER')) {
562
            // Parsing the time and the event.
563 1
            $this->entityOptions = OptionsArray::parse(
564 1
                $parser,
565 1
                $list,
566
                static::$TRIGGER_OPTIONS
567 1
            );
568 1
            ++$list->idx;
569
570 1
            $list->getNextOfTypeAndValue(Token::TYPE_KEYWORD, 'ON');
571 1
            ++$list->idx; // Skipping `ON`.
572
573
            // Parsing the name of the table.
574 1
            $this->table = Expression::parse(
575 1
                $parser,
576 1
                $list,
577
                array(
578 1
                    'parseField' => 'table',
579 1
                    'breakOnAlias' => true,
580
                )
581 1
            );
582 1
            ++$list->idx;
583
584 1
            $list->getNextOfTypeAndValue(Token::TYPE_KEYWORD, 'FOR EACH ROW');
585 1
            ++$list->idx; // Skipping `FOR EACH ROW`.
586
587 1
            for (; $list->idx < $list->count; ++$list->idx) {
588 1
                $token = $list->tokens[$list->idx];
589 1
                $this->body[] = $token;
590 1
            }
591 1
        } else {
592 4 View Code Duplication
            for (; $list->idx < $list->count; ++$list->idx) {
593 4
                $token = $list->tokens[$list->idx];
594 4
                if ($token->type === Token::TYPE_DELIMITER) {
595 4
                    break;
596
                }
597 1
                $this->body[] = $token;
598 1
            }
599
        }
600 42
    }
601
}
602