Completed
Push — master ( 8e44ae...abda0f )
by William
04:48
created

CreateStatement::parse()   F

Complexity

Conditions 60
Paths 1248

Size

Total Lines 291
Code Lines 167

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 159
CRAP Score 60.0008

Importance

Changes 0
Metric Value
cc 60
eloc 167
nc 1248
nop 2
dl 0
loc 291
ccs 159
cts 160
cp 0.9938
crap 60.0008
rs 0
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * `CREATE` statement.
4
 */
5
6
declare(strict_types=1);
7
8
namespace PhpMyAdmin\SqlParser\Statements;
9
10
use PhpMyAdmin\SqlParser\Components\ArrayObj;
11
use PhpMyAdmin\SqlParser\Components\CreateDefinition;
12
use PhpMyAdmin\SqlParser\Components\DataType;
13
use PhpMyAdmin\SqlParser\Components\Expression;
14
use PhpMyAdmin\SqlParser\Components\OptionsArray;
15
use PhpMyAdmin\SqlParser\Components\ParameterDefinition;
16
use PhpMyAdmin\SqlParser\Components\PartitionDefinition;
17
use PhpMyAdmin\SqlParser\Parser;
18
use PhpMyAdmin\SqlParser\Statement;
19
use PhpMyAdmin\SqlParser\Token;
20
use PhpMyAdmin\SqlParser\TokensList;
21
use function is_array;
22
use function trim;
23
24
/**
25
 * `CREATE` statement.
26
 */
27
class CreateStatement extends Statement
28
{
29
    /**
30
     * Options for `CREATE` statements.
31
     *
32
     * @var array
33
     */
34
    public static $OPTIONS = [
35
        // CREATE TABLE
36
        'TEMPORARY' => 1,
37
38
        // CREATE VIEW
39
        'OR REPLACE' => 2,
40
        'ALGORITHM' => [
41
            3,
42
            'var=',
43
        ],
44
        // `DEFINER` is also used for `CREATE FUNCTION / PROCEDURE`
45
        'DEFINER' => [
46
            4,
47
            'expr=',
48
        ],
49
        'SQL SECURITY' => [
50
            5,
51
            'var',
52
        ],
53
54
        'DATABASE' => 6,
55
        'EVENT' => 6,
56
        'FUNCTION' => 6,
57
        'INDEX' => 6,
58
        'UNIQUE INDEX' => 6,
59
        'FULLTEXT INDEX' => 6,
60
        'SPATIAL INDEX' => 6,
61
        'PROCEDURE' => 6,
62
        'SERVER' => 6,
63
        'TABLE' => 6,
64
        'TABLESPACE' => 6,
65
        'TRIGGER' => 6,
66
        'USER' => 6,
67
        'VIEW' => 6,
68
        'SCHEMA' => 6,
69
70
        // CREATE TABLE
71
        'IF NOT EXISTS' => 7,
72
    ];
73
74
    /**
75
     * All database options.
76
     *
77
     * @var array
78
     */
79
    public static $DB_OPTIONS = [
80
        'CHARACTER SET' => [
81
            1,
82
            'var=',
83
        ],
84
        'CHARSET' => [
85
            1,
86
            'var=',
87
        ],
88
        'DEFAULT CHARACTER SET' => [
89
            1,
90
            'var=',
91
        ],
92
        'DEFAULT CHARSET' => [
93
            1,
94
            'var=',
95
        ],
96
        'DEFAULT COLLATE' => [
97
            2,
98
            'var=',
99
        ],
100
        'COLLATE' => [
101
            2,
102
            'var=',
103
        ],
104
    ];
105
106
    /**
107
     * All table options.
108
     *
109
     * @var array
110
     */
111
    public static $TABLE_OPTIONS = [
112
        'ENGINE' => [
113
            1,
114
            'var=',
115
        ],
116
        'AUTO_INCREMENT' => [
117
            2,
118
            'var=',
119
        ],
120
        'AVG_ROW_LENGTH' => [
121
            3,
122
            'var',
123
        ],
124
        'CHARACTER SET' => [
125
            4,
126
            'var=',
127
        ],
128
        'CHARSET' => [
129
            4,
130
            'var=',
131
        ],
132
        'DEFAULT CHARACTER SET' => [
133
            4,
134
            'var=',
135
        ],
136
        'DEFAULT CHARSET' => [
137
            4,
138
            'var=',
139
        ],
140
        'CHECKSUM' => [
141
            5,
142
            'var',
143
        ],
144
        'DEFAULT COLLATE' => [
145
            6,
146
            'var=',
147
        ],
148
        'COLLATE' => [
149
            6,
150
            'var=',
151
        ],
152
        'COMMENT' => [
153
            7,
154
            'var=',
155
        ],
156
        'CONNECTION' => [
157
            8,
158
            'var',
159
        ],
160
        'DATA DIRECTORY' => [
161
            9,
162
            'var',
163
        ],
164
        'DELAY_KEY_WRITE' => [
165
            10,
166
            'var',
167
        ],
168
        'INDEX DIRECTORY' => [
169
            11,
170
            'var',
171
        ],
172
        'INSERT_METHOD' => [
173
            12,
174
            'var',
175
        ],
176
        'KEY_BLOCK_SIZE' => [
177
            13,
178
            'var',
179
        ],
180
        'MAX_ROWS' => [
181
            14,
182
            'var',
183
        ],
184
        'MIN_ROWS' => [
185
            15,
186
            'var',
187
        ],
188
        'PACK_KEYS' => [
189
            16,
190
            'var',
191
        ],
192
        'PASSWORD' => [
193
            17,
194
            'var',
195
        ],
196
        'ROW_FORMAT' => [
197
            18,
198
            'var',
199
        ],
200
        'TABLESPACE' => [
201
            19,
202
            'var',
203
        ],
204
        'STORAGE' => [
205
            20,
206
            'var',
207
        ],
208
        'UNION' => [
209
            21,
210
            'var',
211
        ],
212
    ];
213
214
    /**
215
     * All function options.
216
     *
217
     * @var array
218
     */
219
    public static $FUNC_OPTIONS = [
220
        'COMMENT' => [
221
            1,
222
            'var=',
223
        ],
224
        'LANGUAGE SQL' => 2,
225
        'DETERMINISTIC' => 3,
226
        'NOT DETERMINISTIC' => 3,
227
        'CONTAINS SQL' => 4,
228
        'NO SQL' => 4,
229
        'READS SQL DATA' => 4,
230
        'MODIFIES SQL DATA' => 4,
231
        'SQL SECURITY DEFINER' => [
232
            5,
233
            'var',
234
        ],
235
    ];
236
237
    /**
238
     * All trigger options.
239
     *
240
     * @var array
241
     */
242
    public static $TRIGGER_OPTIONS = [
243
        'BEFORE' => 1,
244
        'AFTER' => 1,
245
        'INSERT' => 2,
246
        'UPDATE' => 2,
247
        'DELETE' => 2,
248
    ];
249
250
    /**
251
     * The name of the entity that is created.
252
     *
253
     * Used by all `CREATE` statements.
254
     *
255
     * @var Expression
256
     */
257
    public $name;
258
259
    /**
260
     * The options of the entity (table, procedure, function, etc.).
261
     *
262
     * Used by `CREATE TABLE`, `CREATE FUNCTION` and `CREATE PROCEDURE`.
263
     *
264
     * @see static::$TABLE_OPTIONS
265
     * @see static::$FUNC_OPTIONS
266
     * @see static::$TRIGGER_OPTIONS
267
     *
268
     * @var OptionsArray
269
     */
270
    public $entityOptions;
271
272
    /**
273
     * If `CREATE TABLE`, a list of columns and keys.
274
     * If `CREATE VIEW`, a list of columns.
275
     *
276
     * Used by `CREATE TABLE` and `CREATE VIEW`.
277
     *
278
     * @var CreateDefinition[]|ArrayObj
279
     */
280
    public $fields;
281
282
    /**
283
     * If `CREATE TABLE ... SELECT`.
284
     * If `CREATE VIEW AS ` ... SELECT`.
285
     *
286
     * Used by `CREATE TABLE`, `CREATE VIEW`
287
     *
288
     * @var SelectStatement
289
     */
290
    public $select;
291
292
    /**
293
     * If `CREATE TABLE ... LIKE`.
294
     *
295
     * Used by `CREATE TABLE`
296
     *
297
     * @var Expression
298
     */
299
    public $like;
300
301
    /**
302
     * Expression used for partitioning.
303
     *
304
     * @var string
305
     */
306
    public $partitionBy;
307
308
    /**
309
     * The number of partitions.
310
     *
311
     * @var int
312
     */
313
    public $partitionsNum;
314
315
    /**
316
     * Expression used for subpartitioning.
317
     *
318
     * @var string
319
     */
320
    public $subpartitionBy;
321
322
    /**
323
     * The number of subpartitions.
324
     *
325
     * @var int
326
     */
327
    public $subpartitionsNum;
328
329
    /**
330
     * The partition of the new table.
331
     *
332
     * @var PartitionDefinition[]
333
     */
334
    public $partitions;
335
336
    /**
337
     * If `CREATE TRIGGER` the name of the table.
338
     *
339
     * Used by `CREATE TRIGGER`.
340
     *
341
     * @var Expression
342
     */
343
    public $table;
344
345
    /**
346
     * The return data type of this routine.
347
     *
348
     * Used by `CREATE FUNCTION`.
349
     *
350
     * @var DataType
351
     */
352
    public $return;
353
354
    /**
355
     * The parameters of this routine.
356
     *
357
     * Used by `CREATE FUNCTION` and `CREATE PROCEDURE`.
358
     *
359
     * @var ParameterDefinition[]
360
     */
361
    public $parameters;
362
363
    /**
364
     * The body of this function or procedure.
365
     * For views, it is the select statement that creates the view.
366
     * Used by `CREATE FUNCTION`, `CREATE PROCEDURE` and `CREATE VIEW`.
367
     *
368
     * @var Token[]|string
369
     */
370
    public $body = [];
371
372
    /**
373
     * @return string
374
     */
375 52
    public function build()
376
    {
377 52
        $fields = '';
378 52
        if (! empty($this->fields)) {
379 32
            if (is_array($this->fields)) {
380 28
                $fields = CreateDefinition::build($this->fields) . ' ';
381 4
            } elseif ($this->fields instanceof ArrayObj) {
0 ignored issues
show
introduced by
$this->fields is always a sub-type of PhpMyAdmin\SqlParser\Components\ArrayObj.
Loading history...
382 4
                $fields = ArrayObj::build($this->fields);
383
            }
384
        }
385
386 52
        if ($this->options->has('DATABASE') || $this->options->has('SCHEMA')) {
387
            return 'CREATE '
388 4
                . OptionsArray::build($this->options) . ' '
389 4
                . Expression::build($this->name) . ' '
390 4
                . OptionsArray::build($this->entityOptions);
391 48
        } elseif ($this->options->has('TABLE')) {
392 32
            if ($this->select !== null) {
393
                return 'CREATE '
394 4
                    . OptionsArray::build($this->options) . ' '
395 4
                    . Expression::build($this->name) . ' '
396 4
                    . $this->select->build();
397 28
            } elseif ($this->like !== null) {
398
                return 'CREATE '
399 4
                    . OptionsArray::build($this->options) . ' '
400 4
                    . Expression::build($this->name) . ' LIKE '
401 4
                    . Expression::build($this->like);
402
            } else {
403 28
                $partition = '';
404
405 28
                if (! empty($this->partitionBy)) {
406 12
                    $partition .= "\nPARTITION BY " . $this->partitionBy;
407
                }
408
409 28
                if (! empty($this->partitionsNum)) {
410 4
                    $partition .= "\nPARTITIONS " . $this->partitionsNum;
411
                }
412
413 28
                if (! empty($this->subpartitionBy)) {
414 8
                    $partition .= "\nSUBPARTITION BY " . $this->subpartitionBy;
415
                }
416
417 28
                if (! empty($this->subpartitionsNum)) {
418 4
                    $partition .= "\nSUBPARTITIONS " . $this->subpartitionsNum;
419
                }
420
421 28
                if (! empty($this->partitions)) {
422 12
                    $partition .= "\n" . PartitionDefinition::build($this->partitions);
423
                }
424
425
                return 'CREATE '
426 28
                    . OptionsArray::build($this->options) . ' '
427 28
                    . Expression::build($this->name) . ' '
428 28
                    . $fields
429 28
                    . OptionsArray::build($this->entityOptions)
430 28
                    . $partition;
431
            }
432 16
        } elseif ($this->options->has('VIEW')) {
433
            return 'CREATE '
434 4
                . OptionsArray::build($this->options) . ' '
435 4
                . Expression::build($this->name) . ' '
436 4
                . $fields . ' AS ' . ($this->select ? $this->select->build() : TokensList::build($this->body)) . ' '
437 4
                . OptionsArray::build($this->entityOptions);
438 12
        } elseif ($this->options->has('TRIGGER')) {
439
            return 'CREATE '
440 4
                . OptionsArray::build($this->options) . ' '
441 4
                . Expression::build($this->name) . ' '
442 4
                . OptionsArray::build($this->entityOptions) . ' '
443 4
                . 'ON ' . Expression::build($this->table) . ' '
444 4
                . 'FOR EACH ROW ' . TokensList::build($this->body);
445 8
        } elseif ($this->options->has('PROCEDURE')
446 8
            || $this->options->has('FUNCTION')
447
        ) {
448 4
            $tmp = '';
449 4
            if ($this->options->has('FUNCTION')) {
450 4
                $tmp = 'RETURNS ' . DataType::build($this->return);
451
            }
452
453
            return 'CREATE '
454 4
                . OptionsArray::build($this->options) . ' '
455 4
                . Expression::build($this->name) . ' '
456 4
                . ParameterDefinition::build($this->parameters) . ' '
457 4
                . $tmp . ' ' . TokensList::build($this->body);
458
        }
459
460
        return 'CREATE '
461 4
            . OptionsArray::build($this->options) . ' '
462 4
            . Expression::build($this->name) . ' '
463 4
            . TokensList::build($this->body);
464
    }
465
466
    /**
467
     * @param Parser     $parser the instance that requests parsing
468
     * @param TokensList $list   the list of tokens to be parsed
469
     */
470 256
    public function parse(Parser $parser, TokensList $list)
471
    {
472 256
        ++$list->idx; // Skipping `CREATE`.
473
474
        // Parsing options.
475 256
        $this->options = OptionsArray::parse($parser, $list, static::$OPTIONS);
476 256
        ++$list->idx; // Skipping last option.
477
478 256
        $isDatabase = $this->options->has('DATABASE') || $this->options->has('SCHEMA');
479 256
        $fieldName = $isDatabase ? 'database' : 'table';
480
481
        // Parsing the field name.
482 256
        $this->name = Expression::parse(
483 256
            $parser,
484 256
            $list,
485
            [
486 256
                'parseField' => $fieldName,
487
                'breakOnAlias' => true,
488
            ]
489
        );
490
491 256
        if (! isset($this->name) || ($this->name === '')) {
492 4
            $parser->error(
493 4
                'The name of the entity was expected.',
494 4
                $list->tokens[$list->idx]
495
            );
496
        } else {
497 252
            ++$list->idx; // Skipping field.
498
        }
499
500
        /**
501
         * Token parsed at this moment.
502
         *
503
         * @var Token
504
         */
505 256
        $token = $list->tokens[$list->idx];
506 256
        $nextidx = $list->idx + 1;
507 256
        while ($nextidx < $list->count && $list->tokens[$nextidx]->type === Token::TYPE_WHITESPACE) {
508 140
            ++$nextidx;
509
        }
510
511 256
        if ($isDatabase) {
512 20
            $this->entityOptions = OptionsArray::parse(
513 20
                $parser,
514 20
                $list,
515 20
                static::$DB_OPTIONS
516
            );
517 236
        } elseif ($this->options->has('TABLE')) {
518 148
            if (($token->type === Token::TYPE_KEYWORD)
519 148
             && ($token->keyword === 'SELECT')) {
520
                /* CREATE TABLE ... SELECT */
521 8
                $this->select = new SelectStatement($parser, $list);
522 140
            } elseif (($token->type === Token::TYPE_KEYWORD) && ($token->keyword === 'AS')
523 140
                && ($list->tokens[$nextidx]->type === Token::TYPE_KEYWORD)
524 140
                && ($list->tokens[$nextidx]->value === 'SELECT')) {
525
                /* CREATE TABLE ... AS SELECT */
526 4
                $list->idx = $nextidx;
527 4
                $this->select = new SelectStatement($parser, $list);
528 136
            } elseif ($token->type === Token::TYPE_KEYWORD
529 136
                && $token->keyword === 'LIKE') {
530
                /* CREATE TABLE `new_tbl` LIKE 'orig_tbl' */
531 12
                $list->idx = $nextidx;
532 12
                $this->like = Expression::parse(
533 12
                    $parser,
534 12
                    $list,
535
                    [
536 12
                        'parseField' => 'table',
537
                        'breakOnAlias' => true,
538
                    ]
539
                );
540
                // The 'LIKE' keyword was found, but no table_name was found next to it
541 12
                if ($this->like === null) {
542 4
                    $parser->error(
543 4
                        'A table name was expected.',
544 12
                        $list->tokens[$list->idx]
545
                    );
546
                }
547
            } else {
548 128
                $this->fields = CreateDefinition::parse($parser, $list);
549 128
                if (empty($this->fields)) {
550 12
                    $parser->error(
551 12
                        'At least one column definition was expected.',
552 12
                        $list->tokens[$list->idx]
553
                    );
554
                }
555
556 128
                ++$list->idx;
557
558 128
                $this->entityOptions = OptionsArray::parse(
559 128
                    $parser,
560 128
                    $list,
561 128
                    static::$TABLE_OPTIONS
562
                );
563
564
                /**
565
                 * The field that is being filled (`partitionBy` or
566
                 * `subpartitionBy`).
567
                 *
568
                 * @var string
569
                 */
570 128
                $field = null;
571
572
                /**
573
                 * The number of brackets. `false` means no bracket was found
574
                 * previously. At least one bracket is required to validate the
575
                 * expression.
576
                 *
577
                 * @var int|bool
578
                 */
579 128
                $brackets = false;
580
581
                /*
582
                 * Handles partitions.
583
                 */
584 148
                for (; $list->idx < $list->count; ++$list->idx) {
585
                    /**
586
                     * Token parsed at this moment.
587
                     *
588
                     * @var Token
589
                     */
590 128
                    $token = $list->tokens[$list->idx];
591
592
                    // End of statement.
593 128
                    if ($token->type === Token::TYPE_DELIMITER) {
594 96
                        break;
595
                    }
596
597
                    // Skipping comments.
598 128
                    if ($token->type === Token::TYPE_COMMENT) {
599 4
                        continue;
600
                    }
601
602 128
                    if (($token->type === Token::TYPE_KEYWORD) && ($token->keyword === 'PARTITION BY')) {
603 20
                        $field = 'partitionBy';
604 20
                        $brackets = false;
605 128
                    } elseif (($token->type === Token::TYPE_KEYWORD) && ($token->keyword === 'SUBPARTITION BY')) {
606 16
                        $field = 'subpartitionBy';
607 16
                        $brackets = false;
608 128
                    } elseif (($token->type === Token::TYPE_KEYWORD) && ($token->keyword === 'PARTITIONS')) {
609 8
                        $token = $list->getNextOfType(Token::TYPE_NUMBER);
610 8
                        --$list->idx; // `getNextOfType` also advances one position.
611 8
                        $this->partitionsNum = $token->value;
612 128
                    } elseif (($token->type === Token::TYPE_KEYWORD) && ($token->keyword === 'SUBPARTITIONS')) {
613 8
                        $token = $list->getNextOfType(Token::TYPE_NUMBER);
614 8
                        --$list->idx; // `getNextOfType` also advances one position.
615 8
                        $this->subpartitionsNum = $token->value;
616 128
                    } elseif (! empty($field)) {
617
                        /*
618
                         * Handling the content of `PARTITION BY` and `SUBPARTITION BY`.
619
                         */
620
621
                        // Counting brackets.
622 20
                        if ($token->type === Token::TYPE_OPERATOR) {
623 20
                            if ($token->value === '(') {
624
                                // This is used instead of `++$brackets` because,
625
                                // initially, `$brackets` is `false` cannot be
626
                                // incremented.
627 20
                                $brackets += 1;
628 20
                            } elseif ($token->value === ')') {
629 20
                                --$brackets;
630
                            }
631
                        }
632
633
                        // Building the expression used for partitioning.
634 20
                        $this->$field .= $token->type === Token::TYPE_WHITESPACE ? ' ' : $token->token;
635
636
                        // Last bracket was read, the expression ended.
637
                        // Comparing with `0` and not `false`, because `false` means
638
                        // that no bracket was found and at least one must is
639
                        // required.
640 20
                        if ($brackets === 0) {
641 20
                            $this->$field = trim($this->$field);
642 20
                            $field = null;
643
                        }
644 128
                    } elseif (($token->type === Token::TYPE_OPERATOR) && ($token->value === '(')) {
645 32
                        if (! empty($this->partitionBy)) {
646 20
                            $this->partitions = ArrayObj::parse(
0 ignored issues
show
Documentation Bug introduced by
It seems like PhpMyAdmin\SqlParser\Com...\PartitionDefinition')) of type PhpMyAdmin\SqlParser\Com...ser\Components\ArrayObj is incompatible with the declared type PhpMyAdmin\SqlParser\Com...s\PartitionDefinition[] of property $partitions.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
647 20
                                $parser,
648 20
                                $list,
649 20
                                ['type' => 'PhpMyAdmin\\SqlParser\\Components\\PartitionDefinition']
650
                            );
651
                        }
652
653 32
                        break;
654
                    }
655
                }
656
            }
657 88
        } elseif ($this->options->has('PROCEDURE')
658 88
            || $this->options->has('FUNCTION')
659
        ) {
660 44
            $this->parameters = ParameterDefinition::parse($parser, $list);
661 44
            if ($this->options->has('FUNCTION')) {
662 24
                $prev_token = $token;
663 24
                $token = $list->getNextOfType(Token::TYPE_KEYWORD);
664 24
                if ($token === null || $token->keyword !== 'RETURNS') {
665 12
                    $parser->error(
666 12
                        'A "RETURNS" keyword was expected.',
667 12
                        $token ?? $prev_token
668
                    );
669
                } else {
670 12
                    ++$list->idx;
671 12
                    $this->return = DataType::parse(
672 12
                        $parser,
673 6
                        $list
674
                    );
675
                }
676
            }
677
678 44
            ++$list->idx;
679
680 44
            $this->entityOptions = OptionsArray::parse(
681 44
                $parser,
682 44
                $list,
683 44
                static::$FUNC_OPTIONS
684
            );
685 44
            ++$list->idx;
686
687 44
            for (; $list->idx < $list->count; ++$list->idx) {
688 36
                $token = $list->tokens[$list->idx];
689 36
                $this->body[] = $token;
690
            }
691 44
        } elseif ($this->options->has('VIEW')) {
692 24
            $token = $list->getNext(); // Skipping whitespaces and comments.
693
694
            // Parsing columns list.
695 24
            if (($token->type === Token::TYPE_OPERATOR) && ($token->value === '(')) {
696 8
                --$list->idx; // getNext() also goes forward one field.
697 8
                $this->fields = ArrayObj::parse($parser, $list);
698 8
                ++$list->idx; // Skipping last token from the array.
699 8
                $list->getNext();
700
            }
701
702
            // Parsing the SELECT expression with and without the `AS` keyword
703 24
            if ($token->type === Token::TYPE_KEYWORD
704 24
                && $token->keyword === 'SELECT'
705
            ) {
706
                $this->select = new SelectStatement($parser, $list);
707 24
            } elseif ($token->type === Token::TYPE_KEYWORD
708 24
                && $token->keyword === 'AS'
709 24
                && $list->tokens[$nextidx]->type === Token::TYPE_KEYWORD
710 24
                && $list->tokens[$nextidx]->value === 'SELECT'
711
            ) {
712 16
                $list->idx = $nextidx;
713 16
                $this->select = new SelectStatement($parser, $list);
714
            } else {
715 24
                for (; $list->idx < $list->count; ++$list->idx) {
716 8
                    $token = $list->tokens[$list->idx];
717 8
                    if ($token->type === Token::TYPE_DELIMITER) {
718 8
                        break;
719
                    }
720 8
                    $this->body[] = $token;
721
                }
722
            }
723 20
        } elseif ($this->options->has('TRIGGER')) {
724
            // Parsing the time and the event.
725 4
            $this->entityOptions = OptionsArray::parse(
726 4
                $parser,
727 4
                $list,
728 4
                static::$TRIGGER_OPTIONS
729
            );
730 4
            ++$list->idx;
731
732 4
            $list->getNextOfTypeAndValue(Token::TYPE_KEYWORD, 'ON');
733 4
            ++$list->idx; // Skipping `ON`.
734
735
            // Parsing the name of the table.
736 4
            $this->table = Expression::parse(
737 4
                $parser,
738 4
                $list,
739
                [
740 4
                    'parseField' => 'table',
741
                    'breakOnAlias' => true,
742
                ]
743
            );
744 4
            ++$list->idx;
745
746 4
            $list->getNextOfTypeAndValue(Token::TYPE_KEYWORD, 'FOR EACH ROW');
747 4
            ++$list->idx; // Skipping `FOR EACH ROW`.
748
749 4
            for (; $list->idx < $list->count; ++$list->idx) {
750 4
                $token = $list->tokens[$list->idx];
751 4
                $this->body[] = $token;
752
            }
753
        } else {
754 16
            for (; $list->idx < $list->count; ++$list->idx) {
755 16
                $token = $list->tokens[$list->idx];
756 16
                if ($token->type === Token::TYPE_DELIMITER) {
757 16
                    break;
758
                }
759
760 4
                $this->body[] = $token;
761
            }
762
        }
763 256
    }
764
}
765