Passed
Push — master ( 94652f...ff593e )
by William
03:47
created

CreateStatementTest::testBuilderView()   B

Complexity

Conditions 1
Paths 1

Size

Total Lines 79
Code Lines 57

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 1
eloc 57
nc 1
nop 0
dl 0
loc 79
rs 8.9381
c 2
b 0
f 0

How to fix   Long Method   

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
declare(strict_types=1);
4
5
namespace PhpMyAdmin\SqlParser\Tests\Builder;
6
7
use PhpMyAdmin\SqlParser\Components\CreateDefinition;
8
use PhpMyAdmin\SqlParser\Components\DataType;
9
use PhpMyAdmin\SqlParser\Components\Expression;
10
use PhpMyAdmin\SqlParser\Components\Key;
11
use PhpMyAdmin\SqlParser\Components\OptionsArray;
12
use PhpMyAdmin\SqlParser\Components\ParameterDefinition;
13
use PhpMyAdmin\SqlParser\Parser;
14
use PhpMyAdmin\SqlParser\Statements\CreateStatement;
15
use PhpMyAdmin\SqlParser\Tests\TestCase;
16
use PhpMyAdmin\SqlParser\TokensList;
17
18
class CreateStatementTest extends TestCase
19
{
20
    public function testBuilder(): void
21
    {
22
        $parser = new Parser('CREATE USER "jeffrey"@"localhost" IDENTIFIED BY "mypass"');
23
        $stmt = $parser->statements[0];
24
        $this->assertEquals(
25
            'CREATE USER "jeffrey"@"localhost" IDENTIFIED BY "mypass"',
26
            $stmt->build()
27
        );
28
    }
29
30
    public function testBuilderDatabase(): void
31
    {
32
        // CREATE DATABASE ...
33
        $parser = new Parser(
34
            'CREATE DATABASE `mydb` ' .
35
            'DEFAULT CHARACTER SET = utf8 DEFAULT COLLATE = utf8_general_ci'
36
        );
37
        $stmt = $parser->statements[0];
38
39
        $this->assertEquals(
40
            'CREATE DATABASE `mydb` ' .
41
            'DEFAULT CHARACTER SET=utf8 DEFAULT COLLATE=utf8_general_ci',
42
            $stmt->build()
43
        );
44
45
        // CREATE SCHEMA ...
46
        $parser = new Parser('CREATE SCHEMA `mydb` DEFAULT CHARACTER SET = utf8 DEFAULT COLLATE = utf8_general_ci');
47
        $stmt = $parser->statements[0];
48
49
        $this->assertEquals(
50
            'CREATE SCHEMA `mydb` ' .
51
            'DEFAULT CHARACTER SET=utf8 DEFAULT COLLATE=utf8_general_ci',
52
            $stmt->build()
53
        );
54
    }
55
56
    public function testBuilderDefaultInt(): void
57
    {
58
        $parser = new Parser(
59
            'CREATE TABLE IF NOT EXISTS t1 (' .
60
            " c1 int(11) NOT NULL DEFAULT '0' COMMENT 'xxx'" .
61
            ') ENGINE=MyISAM'
62
        );
63
        $stmt = $parser->statements[0];
64
65
        $this->assertEquals(
66
            "CREATE TABLE IF NOT EXISTS t1 (\n" .
67
            "  `c1` int(11) NOT NULL DEFAULT '0' COMMENT 'xxx'\n" .
68
            ') ENGINE=MyISAM',
69
            $stmt->build()
70
        );
71
    }
72
73
    public function testBuilderCollate(): void
74
    {
75
        $parser = new Parser(
76
            'CREATE TABLE IF NOT EXISTS t1 (' .
77
            " c1 varchar(11) NOT NULL DEFAULT '0' COLLATE 'utf8_czech_ci' COMMENT 'xxx'" .
78
            ') ENGINE=MyISAM'
79
        );
80
        $stmt = $parser->statements[0];
81
82
        $this->assertEquals(
83
            "CREATE TABLE IF NOT EXISTS t1 (\n" .
84
            "  `c1` varchar(11) NOT NULL DEFAULT '0' COLLATE 'utf8_czech_ci' COMMENT 'xxx'\n" .
85
            ') ENGINE=MyISAM',
86
            $stmt->build()
87
        );
88
    }
89
90
    public function testBuilderDefaultComment(): void
91
    {
92
        $parser = new Parser(
93
            'CREATE TABLE `wp_audio` (' .
94
            " `somedata` int(11) DEFAULT NULL COMMENT 'ma data', " .
95
            " `someinfo` int(11) DEFAULT NULL COMMENT 'ma info' " .
96
            ' )'
97
        );
98
        $stmt = $parser->statements[0];
99
100
        $this->assertEquals(
101
            "CREATE TABLE `wp_audio` (\n" .
102
            "  `somedata` int(11) DEFAULT NULL COMMENT 'ma data',\n" .
103
            "  `someinfo` int(11) DEFAULT NULL COMMENT 'ma info'\n" .
104
            ') ',
105
            $stmt->build()
106
        );
107
    }
108
109
    public function testBuilderTable(): void
110
    {
111
        /* Assertion 1 */
112
        $stmt = new CreateStatement();
113
114
        $stmt->name = new Expression('', 'test', '');
115
        $stmt->options = new OptionsArray(['TABLE']);
116
        $stmt->fields = [
117
            new CreateDefinition(
118
                'id',
119
                new OptionsArray(['NOT NULL', 'AUTO_INCREMENT']),
120
                new DataType('INT', [11], new OptionsArray(['UNSIGNED']))
121
            ),
122
            new CreateDefinition(
123
                '',
124
                null,
125
                new Key('', [['name' => 'id']], 'PRIMARY KEY')
126
            ),
127
        ];
128
129
        $this->assertEquals(
130
            "CREATE TABLE `test` (\n" .
131
            "  `id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT,\n" .
132
            "  PRIMARY KEY (`id`)\n" .
133
            ') ',
134
            $stmt->build()
135
        );
136
137
        /* Assertion 2 */
138
        $query =
139
            "CREATE TABLE `jos_core_acl_aro` (\n" .
140
            "  `id` int(11) NOT NULL,\n" .
141
            "  `section_value` varchar(240) NOT NULL DEFAULT '0',\n" .
142
            "  `value` varchar(240) NOT NULL DEFAULT '',\n" .
143
            "  `order_value` int(11) NOT NULL DEFAULT '0',\n" .
144
            "  `name` varchar(255) NOT NULL DEFAULT '',\n" .
145
            "  `hidden` int(11) NOT NULL DEFAULT '0',\n" .
146
            "  PRIMARY KEY (`id`),\n" .
147
            "  UNIQUE KEY `jos_section_value_value_aro` (`section_value`(100),`value`(15)) USING BTREE,\n" .
148
            "  KEY `jos_gacl_hidden_aro` (`hidden`)\n" .
149
            ') ENGINE=InnoDB DEFAULT CHARSET=latin1';
150
        $parser = new Parser($query);
151
        $this->assertEquals($query, $parser->statements[0]->build());
152
153
        /* Assertion 3 */
154
        $query = 'CREATE TABLE `table_copy` LIKE `old_table`';
155
        $parser = new Parser($query);
156
        $this->assertEquals($query, $parser->statements[0]->build());
157
158
        /* Assertion 4 */
159
        $query =
160
            "CREATE TABLE `aa` (\n" .
161
            "  `id` int(11) NOT NULL,\n" .
162
            "  `rTime` timestamp(3) NOT NULL DEFAULT '0000-00-00 00:00:00.000' ON UPDATE CURRENT_TIMESTAMP(3),\n" .
163
            "  PRIMARY KEY (`id`)\n" .
164
            ') ENGINE=InnoDB DEFAULT CHARSET=latin1';
165
        $parser = new Parser($query);
166
        $this->assertEquals($query, $parser->statements[0]->build());
167
168
        /* Assertion 5 */
169
        $parser = new Parser(
170
            'CREATE table table_name WITH' .
171
            ' cte (col1) AS ( SELECT 1 UNION ALL SELECT 2 )' .
172
            ' SELECT col1 FROM cte'
173
        );
174
        $stmt = $parser->statements[0];
175
176
        $this->assertEquals(
177
            'CREATE TABLE table_name WITH' .
178
            ' cte(col1) AS (SELECT 1 UNION ALL SELECT 2)' .
179
            ' SELECT col1 FROM cte',
180
            $stmt->build()
181
        );
182
    }
183
184
    public function testBuilderPartitions(): void
185
    {
186
        /* Assertion 1 */
187
        $query = 'CREATE TABLE ts (' . "\n"
188
            . '  `id` int,' . "\n"
189
            . '  `purchased` date' . "\n"
190
            . ') ' . "\n"
191
            . 'PARTITION BY RANGE(YEAR(purchased))' . "\n"
192
            . 'PARTITIONS 3' . "\n"
193
            . 'SUBPARTITION BY HASH(TO_DAYS(purchased))' . "\n"
194
            . 'SUBPARTITIONS 2' . "\n"
195
            . '(' . "\n"
196
            . 'PARTITION p0 VALUES LESS THAN (1990)  (' . "\n"
197
            . 'SUBPARTITION s0,' . "\n"
198
            . 'SUBPARTITION s1' . "\n"
199
            . '),' . "\n"
200
            . 'PARTITION p1 VALUES LESS THAN (2000)  (' . "\n"
201
            . 'SUBPARTITION s2,' . "\n"
202
            . 'SUBPARTITION s3' . "\n"
203
            . '),' . "\n"
204
            . 'PARTITION p2 VALUES LESS THAN MAXVALUE  (' . "\n"
205
            . 'SUBPARTITION s4,' . "\n"
206
            . 'SUBPARTITION s5' . "\n"
207
            . ')' . "\n"
208
            . ')';
209
        $parser = new Parser($query);
210
        $this->assertEquals($query, $parser->statements[0]->build());
211
212
        /* Assertion 2 */
213
        $query = 'CREATE TABLE `pma_test` (' . "\n"
214
            . '  `test_id` int(32) NOT NULL,' . "\n"
215
            . '  `time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP' . "\n"
216
            . ') ENGINE=InnoDB DEFAULT CHARSET=utf8' . "\n"
217
            . 'PARTITION BY RANGE (test_id)' . "\n"
218
            . '(' . "\n"
219
            . 'PARTITION p0 VALUES LESS THAN (250000) ENGINE=InnoDB,' . "\n"
220
            . 'PARTITION p1 VALUES LESS THAN (500000) ENGINE=InnoDB,' . "\n"
221
            . 'PARTITION p2 VALUES LESS THAN (750000) ENGINE=InnoDB,' . "\n"
222
            . 'PARTITION p3 VALUES LESS THAN (1000000) ENGINE=InnoDB,' . "\n"
223
            . 'PARTITION p4 VALUES LESS THAN (1250000) ENGINE=InnoDB,' . "\n"
224
            . 'PARTITION p5 VALUES LESS THAN (1500000) ENGINE=InnoDB,' . "\n"
225
            . 'PARTITION p6 VALUES LESS THAN MAXVALUE ENGINE=InnoDB' . "\n"
226
            . ')';
227
        $parser = new Parser($query);
228
        $this->assertEquals($query, $parser->statements[0]->build());
229
    }
230
231
    /**
232
     * @return string[][]
233
     */
234
    public function partitionQueriesProvider(): array
235
    {
236
        return [
237
            [
238
                'subparts' => <<<EOT
239
CREATE TABLE `ts` (
240
  `id` int(11) DEFAULT NULL,
241
  `purchased` date DEFAULT NULL
242
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
243
PARTITION BY RANGE (YEAR(purchased))
244
SUBPARTITION BY HASH (TO_DAYS(purchased))
245
(
246
PARTITION p0 VALUES LESS THAN (1990)  (
247
SUBPARTITION s0 ENGINE=InnoDB,
248
SUBPARTITION s1 ENGINE=InnoDB
249
),
250
PARTITION p1 VALUES LESS THAN (2000)  (
251
SUBPARTITION s2 ENGINE=InnoDB,
252
SUBPARTITION s3 ENGINE=InnoDB
253
),
254
PARTITION p2 VALUES LESS THAN MAXVALUE  (
255
SUBPARTITION s4 ENGINE=InnoDB,
256
SUBPARTITION s5 ENGINE=InnoDB
257
)
258
)
259
EOT
260
            ,
261
            ],
262
            [
263
                'parts' => <<<EOT
264
CREATE TABLE ptest (
265
  `event_date` date NOT NULL
266
) ENGINE=InnoDB DEFAULT CHARSET=latin1 ROW_FORMAT=DYNAMIC
267
PARTITION BY HASH (TO_DAYS(event_date))
268
(
269
PARTITION p0 ENGINE=InnoDB,
270
PARTITION p1 ENGINE=InnoDB,
271
PARTITION p2 ENGINE=InnoDB,
272
PARTITION p3 ENGINE=InnoDB,
273
PARTITION p4 ENGINE=InnoDB
274
)
275
EOT
276
            ,
277
            ],
278
        ];
279
    }
280
281
    /**
282
     * @dataProvider partitionQueriesProvider
283
     */
284
    public function testBuilderPartitionsEngine(string $query): void
285
    {
286
        $parser = new Parser($query);
287
        $stmt = $parser->statements[0];
288
289
        $this->assertEquals($query, $stmt->build());
290
    }
291
292
    public function testBuilderView(): void
293
    {
294
        $parser = new Parser(
295
            'CREATE VIEW myView (vid, vfirstname) AS ' .
296
            'SELECT id, first_name FROM employee WHERE id = 1'
297
        );
298
        $stmt = $parser->statements[0];
299
300
        $this->assertEquals(
301
            'CREATE VIEW myView (vid, vfirstname) AS  ' .
302
            'SELECT id, first_name FROM employee WHERE id = 1 ',
303
            $stmt->build()
304
        );
305
306
        $parser = new Parser(
307
            'CREATE OR REPLACE VIEW myView (vid, vfirstname) AS ' .
308
            'SELECT id, first_name FROM employee WHERE id = 1'
309
        );
310
        $stmt = $parser->statements[0];
311
312
        $this->assertEquals(
313
            'CREATE OR REPLACE VIEW myView (vid, vfirstname) AS  ' .
314
            'SELECT id, first_name FROM employee WHERE id = 1 ',
315
            $stmt->build()
316
        );
317
318
        // Assert the builder can build wrong syntax select expressions
319
        $parser = new Parser(
320
            'CREATE OR REPLACE VIEW myView (vid, vfirstname) AS ' .
321
            'SELECT id, first_name, FROMzz employee WHERE id = 1'
322
        );
323
        $stmt = $parser->statements[0];
324
        $this->assertEquals(
325
            'CREATE OR REPLACE VIEW myView (vid, vfirstname) AS  ' .
326
            'SELECT id, first_name, FROMzz employee WHERE id = 1 ',
327
            $stmt->build()
328
        );
329
330
        $parser = new Parser(
331
            'CREATE OR REPLACE VIEW myView (vid, vfirstname) AS ' .
332
            'SELECT id, first_name, FROMzz employee WHERE id = 1 ' .
333
            'UNION ' .
334
            'SELECT id, first_name, FROMzz employee WHERE id = 2 '
335
        );
336
        $stmt = $parser->statements[0];
337
338
        $this->assertEquals(
339
            'CREATE OR REPLACE VIEW myView (vid, vfirstname) AS  ' .
340
            'SELECT id, first_name, FROMzz employee WHERE id = 1 ' .
341
            'UNION ' .
342
            'SELECT id, first_name, FROMzz employee WHERE id = 2  ',
343
            $stmt->build()
344
        );
345
346
        $parser = new Parser('CREATE VIEW `view_programlevelpartner`  AS SELECT `p`.`country_id`'
347
        . 'AS `country_id` FROM `program_level_partner` `p` ORDER BY `p`.`id` asc');
348
        $stmt = $parser->statements[0];
349
        $this->assertEquals(
350
            'CREATE VIEW `view_programlevelpartner`  AS SELECT `p`.`country_id`'
351
            . ' AS `country_id` FROM `program_level_partner` AS `p` ORDER BY `p`.`id` ASC ',
352
            $stmt->build()
353
        );
354
355
        $parser = new Parser('CREATE VIEW `view_zg_bycountry`  AS '
356
        . 'SELECT `d`.`zg_id` FROM `view_zg_value` AS `d` GROUP BY `d`.`ind_id`;');
357
        $stmt = $parser->statements[0];
358
        $this->assertEquals(
359
            'CREATE VIEW `view_zg_bycountry`  AS '
360
            . 'SELECT `d`.`zg_id` FROM `view_zg_value` AS `d` GROUP BY `d`.`ind_id` ',
361
            $stmt->build()
362
        );
363
364
        $parser = new Parser('CREATE view view_name AS WITH  aa(col1)'
365
        . ' AS ( SELECT 1 UNION ALL SELECT 2 ) SELECT col1 FROM cte AS `d` ');
366
        $stmt = $parser->statements[0];
367
        $this->assertEquals(
368
            'CREATE view view_name  AS WITH aa(col1)'
369
            . ' AS (SELECT 1 UNION ALL SELECT 2) SELECT col1 FROM cte AS `d`  ',
370
            $stmt->build()
371
        );
372
    }
373
374
    public function testBuilderViewComplex(): void
375
    {
376
        $parser = new Parser(
377
            'CREATE VIEW withclause AS' . "\n"
378
            . "\n"
379
            . 'WITH cte AS (' . "\n"
380
                . 'SELECT p.name, p.shape' . "\n"
381
                . 'FROM gis_all as p' . "\n"
382
            . ')' . "\n"
383
            . "\n"
384
            . 'SELECT cte.*' . "\n"
385
            . 'FROM cte' . "\n"
386
            . 'CROSS JOIN gis_all;'
387
        );
388
        $stmt = $parser->statements[0];
389
390
        $this->assertEquals(
391
            'CREATE VIEW withclause  AS '
392
            . 'WITH cte AS ('
393
                . 'SELECT p.name, p.shape '
394
                . 'FROM gis_all AS `p`'
395
            . ') '
396
            . 'SELECT cte.* '
397
            . 'FROM cte '
398
            . 'CROSS JOIN gis_all ',
399
            $stmt->build()
400
        );
401
        $parser = new Parser(
402
            'CREATE VIEW withclause2 AS' . "\n"
403
            . "\n"
404
            . 'WITH cte AS (' . "\n"
405
                . "\t" . 'SELECT p.name, p.shape' . "\n"
406
                . "\t" . 'FROM gis_all as p' . "\n"
407
            . '), cte2 AS (' . "\n"
408
                . "\t" . 'SELECT p.name as n2, p.shape as sh2' . "\n"
409
                . "\t" . 'FROM gis_all as p' . "\n"
410
            . ')' . "\n"
411
            . "\n"
412
            . 'SELECT cte.*,cte2.*' . "\n"
413
            . 'FROM cte,cte2' . "\n"
414
            . 'CROSS JOIN gis_all;'
415
        );
416
        $stmt = $parser->statements[0];
417
418
        $this->assertEquals(
419
            'CREATE VIEW withclause2  AS '
420
            . 'WITH cte AS ('
421
                . 'SELECT p.name, p.shape'
422
                . ' FROM gis_all AS `p`'
423
            . '), cte2 AS ('
424
                . 'SELECT p.name AS `n2`, p.shape AS `sh2`'
425
                . ' FROM gis_all AS `p`'
426
            . ')'
427
            . ' SELECT cte.*, cte2.* '
428
            . 'FROM cte, cte2'
429
            . ' CROSS JOIN gis_all ',
430
            $stmt->build()
431
        );
432
    }
433
434
    public function testBuilderCreateProcedure(): void
435
    {
436
        $parser = new Parser(
437
            'CREATE DEFINER=`root`@`%`'
438
            . ' PROCEDURE `test2`(IN `_var` INT) NOT DETERMINISTIC NO SQL'
439
            . ' SQL SECURITY INVOKER NO SQL SQL SECURITY INVOKER SELECT _var'
440
        );
441
442
        /** @var CreateStatement $stmt */
443
        $stmt = $parser->statements[0];
444
445
        $this->assertSame(
446
            'CREATE DEFINER=`root`@`%`'
447
            . ' PROCEDURE `test2` (IN `_var` INT)  NOT DETERMINISTIC NO SQL'
448
            . ' SQL SECURITY INVOKER NO SQL SQL SECURITY INVOKER SELECT _var',
449
            $stmt->build()
450
        );
451
452
        $this->assertFalse($stmt->entityOptions->isEmpty());
0 ignored issues
show
Bug introduced by
The method isEmpty() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

452
        $this->assertFalse($stmt->entityOptions->/** @scrutinizer ignore-call */ isEmpty());

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
453
        $this->assertFalse($stmt->options->isEmpty());
0 ignored issues
show
Bug introduced by
The method isEmpty() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

453
        $this->assertFalse($stmt->options->/** @scrutinizer ignore-call */ isEmpty());

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
454
        $this->assertSame(
455
            'DEFINER=`root`@`%` PROCEDURE',
456
            $stmt->options->__toString()
457
        );
458
459
        $this->assertSame(
460
            '`test2`',
461
            $stmt->name->__toString()
462
        );
463
464
        $this->assertSame(
465
            '(IN `_var` INT)',
466
            ParameterDefinition::build($stmt->parameters)
467
        );
468
469
        $this->assertSame(
470
            'NOT DETERMINISTIC NO SQL SQL SECURITY INVOKER NO SQL SQL SECURITY INVOKER',
471
            $stmt->entityOptions->__toString()
472
        );
473
474
        $this->assertSame(
475
            'SELECT _var',
476
            TokensList::build($stmt->body)
477
        );
478
    }
479
480
    public function testBuilderCreateFunction(): void
481
    {
482
        $parser = new Parser(
483
            'CREATE DEFINER=`root`@`localhost`'
484
            . ' FUNCTION `inventory_in_stock`(`p_inventory_id` INT) RETURNS tinyint(1)'
485
            . ' READS SQL DATA'
486
            . ' COMMENT \'My best function written by a friend\'\'s friend\''
487
            . ' BEGIN' . "\n"
488
            . '    DECLARE v_rentals INT;' . "\n"
489
            . '    DECLARE v_out     INT;' . "\n"
490
            . "\n"
491
            . '    ' . "\n"
492
            . '    ' . "\n"
493
            . "\n"
494
            . '    SELECT COUNT(*) INTO v_rentals' . "\n"
495
            . '    FROM rental' . "\n"
496
            . '    WHERE inventory_id = p_inventory_id;' . "\n"
497
            . "\n"
498
            . '    IF v_rentals = 0 THEN' . "\n"
499
            . '      RETURN TRUE;' . "\n"
500
            . '    END IF;' . "\n"
501
            . "\n"
502
            . '    SELECT COUNT(rental_id) INTO v_out' . "\n"
503
            . '    FROM inventory LEFT JOIN rental USING(inventory_id)' . "\n"
504
            . '    WHERE inventory.inventory_id = p_inventory_id' . "\n"
505
            . '    AND rental.return_date IS NULL;' . "\n"
506
            . "\n"
507
            . '    IF v_out > 0 THEN' . "\n"
508
            . '      RETURN FALSE;' . "\n"
509
            . '    ELSE' . "\n"
510
            . '      RETURN TRUE;' . "\n"
511
            . '    END IF;' . "\n"
512
            . 'END'
513
        );
514
515
        /** @var CreateStatement $stmt */
516
        $stmt = $parser->statements[0];
517
518
        $this->assertSame(
519
            'CREATE DEFINER=`root`@`localhost`'
520
            . ' FUNCTION `inventory_in_stock` (`p_inventory_id` INT) RETURNS TINYINT(1)'
521
            . ' READS SQL DATA'
522
            . ' COMMENT \'My best function written by a friend\'\'s friend\''
523
            . ' BEGIN' . "\n"
524
            . '    DECLARE v_rentals INT;' . "\n"
525
            . '    DECLARE v_out     INT;' . "\n"
526
            . "\n"
527
            . '    ' . "\n"
528
            . '    ' . "\n"
529
            . "\n"
530
            . '    SELECT COUNT(*) INTO v_rentals' . "\n"
531
            . '    FROM rental' . "\n"
532
            . '    WHERE inventory_id = p_inventory_id;' . "\n"
533
            . "\n"
534
            . '    IF v_rentals = 0 THEN' . "\n"
535
            . '      RETURN TRUE;' . "\n"
536
            . '    END IF;' . "\n"
537
            . "\n"
538
            . '    SELECT COUNT(rental_id) INTO v_out' . "\n"
539
            . '    FROM inventory LEFT JOIN rental USING(inventory_id)' . "\n"
540
            . '    WHERE inventory.inventory_id = p_inventory_id' . "\n"
541
            . '    AND rental.return_date IS NULL;' . "\n"
542
            . "\n"
543
            . '    IF v_out > 0 THEN' . "\n"
544
            . '      RETURN FALSE;' . "\n"
545
            . '    ELSE' . "\n"
546
            . '      RETURN TRUE;' . "\n"
547
            . '    END IF;' . "\n"
548
            . 'END',
549
            $stmt->build()
550
        );
551
552
        $this->assertFalse($stmt->entityOptions->isEmpty());
553
        $this->assertFalse($stmt->options->isEmpty());
554
555
        $this->assertSame(
556
            'DEFINER=`root`@`localhost` FUNCTION',
557
            $stmt->options->__toString()
558
        );
559
560
        $this->assertSame(
561
            '`inventory_in_stock`',
562
            $stmt->name->__toString()
563
        );
564
565
        $this->assertSame(
566
            '(`p_inventory_id` INT)',
567
            ParameterDefinition::build($stmt->parameters)
568
        );
569
570
        $this->assertSame(
571
            'READS SQL DATA COMMENT \'My best function written by a friend\'\'s friend\'',
572
            $stmt->entityOptions->__toString()
573
        );
574
575
        $this->assertSame(
576
            'BEGIN' . "\n"
577
            . '    DECLARE v_rentals INT;' . "\n"
578
            . '    DECLARE v_out     INT;' . "\n"
579
            . "\n"
580
            . '    ' . "\n"
581
            . '    ' . "\n"
582
            . "\n"
583
            . '    SELECT COUNT(*) INTO v_rentals' . "\n"
584
            . '    FROM rental' . "\n"
585
            . '    WHERE inventory_id = p_inventory_id;' . "\n"
586
            . "\n"
587
            . '    IF v_rentals = 0 THEN' . "\n"
588
            . '      RETURN TRUE;' . "\n"
589
            . '    END IF;' . "\n"
590
            . "\n"
591
            . '    SELECT COUNT(rental_id) INTO v_out' . "\n"
592
            . '    FROM inventory LEFT JOIN rental USING(inventory_id)' . "\n"
593
            . '    WHERE inventory.inventory_id = p_inventory_id' . "\n"
594
            . '    AND rental.return_date IS NULL;' . "\n"
595
            . "\n"
596
            . '    IF v_out > 0 THEN' . "\n"
597
            . '      RETURN FALSE;' . "\n"
598
            . '    ELSE' . "\n"
599
            . '      RETURN TRUE;' . "\n"
600
            . '    END IF;' . "\n"
601
            . 'END',
602
            TokensList::build($stmt->body)
603
        );
604
    }
605
606
    public function testBuilderTrigger(): void
607
    {
608
        $stmt = new CreateStatement();
609
610
        $stmt->options = new OptionsArray(['TRIGGER']);
611
        $stmt->name = new Expression('ins_sum');
612
        $stmt->entityOptions = new OptionsArray(['BEFORE', 'INSERT']);
613
        $stmt->table = new Expression('account');
614
        $stmt->body = 'SET @sum = @sum + NEW.amount';
615
616
        $this->assertEquals(
617
            'CREATE TRIGGER ins_sum BEFORE INSERT ON account ' .
618
            'FOR EACH ROW SET @sum = @sum + NEW.amount',
619
            $stmt->build()
620
        );
621
    }
622
623
    public function testBuilderRoutine(): void
624
    {
625
        $parser = new Parser(
626
            'CREATE FUNCTION test (IN `i` INT) RETURNS VARCHAR ' .
627
            'BEGIN ' .
628
            'DECLARE name VARCHAR DEFAULT ""; ' .
629
            'SELECT name INTO name FROM employees WHERE id = i; ' .
630
            'RETURN name; ' .
631
            'END'
632
        );
633
        $stmt = $parser->statements[0];
634
635
        $this->assertEquals(
636
            'CREATE FUNCTION test (IN `i` INT) RETURNS VARCHAR ' .
637
            ' BEGIN ' .
638
            'DECLARE name VARCHAR DEFAULT ""; ' .
639
            'SELECT name INTO name FROM employees WHERE id = i; ' .
640
            'RETURN name; ' .
641
            'END',
642
            $stmt->build()
643
        );
644
    }
645
646
    public function testBuildSelect(): void
647
    {
648
        $parser = new Parser('CREATE TABLE new_tbl SELECT * FROM orig_tbl');
649
        $this->assertEquals(
650
            'CREATE TABLE new_tbl SELECT * FROM orig_tbl',
651
            $parser->statements[0]->build()
652
        );
653
    }
654
655
    public function testBuildCreateTableSortedIndex(): void
656
    {
657
        $parser = new Parser(
658
            <<<'SQL'
659
CREATE TABLE `entries` (
660
    `id` int(11) NOT NULL AUTO_INCREMENT,
661
    `fk_ug_id` int(11) DEFAULT NULL,
662
    `amount` decimal(10,2) DEFAULT NULL,
663
    PRIMARY KEY (`id`),
664
    KEY `entries__ug` (`fk_ug_id` DESC),
665
    KEY `entries__ug2` (`fk_ug_id` ASC),
666
    KEY `33` (`id` ASC, `fk_ug_id` DESC)
667
) /*!50100 TABLESPACE `innodb_system` */ ENGINE=InnoDB AUTO_INCREMENT=4465 DEFAULT CHARSET=utf8
668
SQL
669
        );
670
671
        /** @var CreateStatement $stmt */
672
        $stmt = $parser->statements[0];
673
674
        $tableBody = <<<'SQL'
675
(
676
  `id` int(11) NOT NULL AUTO_INCREMENT,
677
  `fk_ug_id` int(11) DEFAULT NULL,
678
  `amount` decimal(10,2) DEFAULT NULL,
679
  PRIMARY KEY (`id`),
680
  KEY `entries__ug` (`fk_ug_id` DESC),
681
  KEY `entries__ug2` (`fk_ug_id` ASC),
682
  KEY `33` (`id` ASC,`fk_ug_id` DESC)
683
)
684
SQL;
685
686
        $this->assertEquals(
687
            $tableBody,
688
            CreateDefinition::build($stmt->fields)
0 ignored issues
show
Bug introduced by
It seems like $stmt->fields can also be of type PhpMyAdmin\SqlParser\Components\ArrayObj; however, parameter $component of PhpMyAdmin\SqlParser\Com...eateDefinition::build() does only seem to accept PhpMyAdmin\SqlParser\Com...onents\CreateDefinition, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

688
            CreateDefinition::build(/** @scrutinizer ignore-type */ $stmt->fields)
Loading history...
689
        );
690
691
        $this->assertEquals(
692
            'CREATE TABLE `entries` '
693
            . $tableBody
694
            . ' ENGINE=InnoDB AUTO_INCREMENT=4465 DEFAULT CHARSET=utf8 TABLESPACE `innodb_system`',
695
            $stmt->build()
696
        );
697
    }
698
699
    public function testBuildCreateTableComplexIndexes(): void
700
    {
701
        // phpcs:disable Generic.Files.LineLength.TooLong
702
        $parser = new Parser(
703
            <<<'SQL'
704
CREATE TABLE `page_rebuild_control` (
705
    `proc_row_number` int DEFAULT NULL,
706
    `place_id` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
707
    `place_name` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
708
    `place_type` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
709
    `waterway_id` varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
710
    `cache_updated` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
711
    `place_active` varchar(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
712
    `alias_type` int NOT NULL DEFAULT '0',
713
    `status` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,
714
    `time_taken` float DEFAULT NULL,
715
    PRIMARY KEY (`place_id`,`place_type`) USING BTREE,
716
    KEY `place_type_idx` (`place_type`(10)),
717
    KEY `cached_time_idx` (`cache_updated`),
718
    KEY `active_idx` (`place_active`),
719
    KEY `status_idx` (`status`),
720
    KEY `waterway_idx` (`waterway_id`),
721
    KEY `time_taken_idx` (`time_taken`),
722
    KEY `updated_tz_ind3` (
723
        -- my expression
724
		(convert_tz(`cache_updated`,_utf8mb4'GMT',_utf8mb4'GB'))
725
    ) COMMENT 'foo\'s',
726
    KEY `updated_tz_ind_two_indexes_commented` (
727
		-- first expression
728
		(
729
			convert_tz(`cache_updated`,_utf8mb4'GMT',_utf8mb4'GB')
730
		)
731
		,
732
		-- second expression
733
		(
734
			convert_tz(`cache_updated`,_utf8mb4'GMT',_utf8mb4'FR')
735
		)
736
	)
737
	-- and now some options
738
	COMMENT 'haha, this is a complex and indented case',
739
    KEY `alias_type_idx` (`alias_type`),
740
    KEY `updated_tz_ind2` ((convert_tz(`cache_updated`,_utf8mb4'GMT',_utf8mb4'GB'))) COMMENT 'foo\'s',
741
    KEY `updated_tz_ind_two_indexes` ((convert_tz(`cache_updated`,_utf8mb4'GMT',_utf8mb4'GB')), (convert_tz(`cache_updated`,_utf8mb4'GMT',_utf8mb4'FR'))) COMMENT 'bar\'s',
742
    KEY `updated_tz_ind` ((convert_tz(`cache_updated`,_utf8mb4'GMT',_utf8mb4'GB')))
743
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
744
SQL
745
        );
746
        // phpcs:enable
747
748
        /** @var CreateStatement $stmt */
749
        $stmt = $parser->statements[0];
750
751
        // phpcs:disable Generic.Files.LineLength.TooLong
752
        $tableBody = <<<'SQL'
753
(
754
  `proc_row_number` int DEFAULT NULL,
755
  `place_id` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
756
  `place_name` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
757
  `place_type` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
758
  `waterway_id` varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
759
  `cache_updated` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
760
  `place_active` varchar(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
761
  `alias_type` int NOT NULL DEFAULT '0',
762
  `status` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,
763
  `time_taken` float DEFAULT NULL,
764
  PRIMARY KEY (`place_id`,`place_type`) USING BTREE,
765
  KEY `place_type_idx` (`place_type`(10)),
766
  KEY `cached_time_idx` (`cache_updated`),
767
  KEY `active_idx` (`place_active`),
768
  KEY `status_idx` (`status`),
769
  KEY `waterway_idx` (`waterway_id`),
770
  KEY `time_taken_idx` (`time_taken`),
771
  KEY `updated_tz_ind3` ((convert_tz(`cache_updated`,_utf8mb4'GMT',_utf8mb4'GB'))) COMMENT 'foo\'s',
772
  KEY `updated_tz_ind_two_indexes_commented` ((
773
			convert_tz(`cache_updated`,_utf8mb4'GMT',_utf8mb4'GB')
774
		), (
775
			convert_tz(`cache_updated`,_utf8mb4'GMT',_utf8mb4'FR')
776
		)) COMMENT 'haha, this is a complex and indented case',
777
  KEY `alias_type_idx` (`alias_type`),
778
  KEY `updated_tz_ind2` ((convert_tz(`cache_updated`,_utf8mb4'GMT',_utf8mb4'GB'))) COMMENT 'foo\'s',
779
  KEY `updated_tz_ind_two_indexes` ((convert_tz(`cache_updated`,_utf8mb4'GMT',_utf8mb4'GB')), (convert_tz(`cache_updated`,_utf8mb4'GMT',_utf8mb4'FR'))) COMMENT 'bar\'s',
780
  KEY `updated_tz_ind` ((convert_tz(`cache_updated`,_utf8mb4'GMT',_utf8mb4'GB')))
781
)
782
SQL;
783
        // phpcs:enable
784
785
        $this->assertEquals(
786
            $tableBody,
787
            CreateDefinition::build($stmt->fields)
0 ignored issues
show
Bug introduced by
It seems like $stmt->fields can also be of type PhpMyAdmin\SqlParser\Components\ArrayObj; however, parameter $component of PhpMyAdmin\SqlParser\Com...eateDefinition::build() does only seem to accept PhpMyAdmin\SqlParser\Com...onents\CreateDefinition, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

787
            CreateDefinition::build(/** @scrutinizer ignore-type */ $stmt->fields)
Loading history...
788
        );
789
790
        $this->assertEquals(
791
            'CREATE TABLE `page_rebuild_control` '
792
            . $tableBody
793
            . ' ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci',
794
            $stmt->build()
795
        );
796
    }
797
}
798