Passed
Push — master ( 090da9...b8c26e )
by Maurício
08:32 queued 13s
created

testGetDetailedProfilingStatsWithoutData()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 4
c 1
b 0
f 0
nc 1
nop 0
dl 0
loc 6
rs 10
1
<?php
2
3
declare(strict_types=1);
4
5
namespace PhpMyAdmin\Tests;
6
7
use PhpMyAdmin\ConfigStorage\Relation;
8
use PhpMyAdmin\ConfigStorage\RelationCleanup;
9
use PhpMyAdmin\DatabaseInterface;
0 ignored issues
show
Bug introduced by
The type PhpMyAdmin\DatabaseInterface was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
10
use PhpMyAdmin\Operations;
11
use PhpMyAdmin\ParseAnalyze;
12
use PhpMyAdmin\Sql;
13
use PhpMyAdmin\Template;
14
use PhpMyAdmin\Tests\Stubs\DbiDummy;
15
use PhpMyAdmin\Transformations;
16
use ReflectionMethod;
17
use stdClass;
18
19
use const MYSQLI_TYPE_SHORT;
20
use const MYSQLI_TYPE_TIMESTAMP;
21
use const MYSQLI_TYPE_VAR_STRING;
22
23
/** @covers \PhpMyAdmin\Sql */
24
class SqlTest extends AbstractTestCase
25
{
26
    protected DatabaseInterface $dbi;
27
28
    protected DbiDummy $dummyDbi;
29
30
    private Sql $sql;
31
32
    /**
33
     * Setup for test cases
34
     */
35
    protected function setUp(): void
36
    {
37
        parent::setUp();
38
39
        parent::setLanguage();
40
41
        parent::setTheme();
42
43
        $this->dummyDbi = $this->createDbiDummy();
44
        $this->dbi = $this->createDatabaseInterface($this->dummyDbi);
45
        $GLOBALS['dbi'] = $this->dbi;
46
        $GLOBALS['server'] = 1;
47
        $GLOBALS['db'] = 'db';
48
        $GLOBALS['table'] = 'table';
49
        $GLOBALS['cfg']['AllowThirdPartyFraming'] = false;
50
        $GLOBALS['cfg']['SendErrorReports'] = 'ask';
51
        $GLOBALS['cfg']['ServerDefault'] = 1;
52
        $GLOBALS['cfg']['DefaultTabDatabase'] = 'structure';
53
        $GLOBALS['cfg']['DefaultTabTable'] = 'browse';
54
        $GLOBALS['cfg']['ShowDatabasesNavigationAsTree'] = true;
55
        $GLOBALS['cfg']['NavigationTreeDefaultTabTable'] = 'structure';
56
        $GLOBALS['cfg']['NavigationTreeDefaultTabTable2'] = '';
57
        $GLOBALS['cfg']['LimitChars'] = 50;
58
        $GLOBALS['cfg']['Confirm'] = true;
59
        $GLOBALS['cfg']['LoginCookieValidity'] = 1440;
60
        $GLOBALS['cfg']['enable_drag_drop_import'] = true;
61
        $GLOBALS['showtable'] = null;
62
63
        $relation = new Relation($GLOBALS['dbi']);
64
        $this->sql = new Sql(
65
            $GLOBALS['dbi'],
66
            $relation,
67
            new RelationCleanup($GLOBALS['dbi'], $relation),
68
            new Operations($GLOBALS['dbi'], $relation),
69
            new Transformations(),
70
            new Template(),
71
        );
72
    }
73
74
    /**
75
     * Test for getSqlWithLimitClause
76
     */
77
    public function testGetSqlWithLimitClause(): void
78
    {
79
        // Test environment.
80
        $GLOBALS['_SESSION']['tmpval']['pos'] = 1;
81
        $GLOBALS['_SESSION']['tmpval']['max_rows'] = 2;
82
83
        $this->assertEquals('SELECT * FROM test LIMIT 1, 2 ', $this->callFunction(
84
            $this->sql,
85
            Sql::class,
86
            'getSqlWithLimitClause',
87
            [ParseAnalyze::sqlQuery('SELECT * FROM test LIMIT 0, 10', $GLOBALS['db'])[0]],
88
        ));
89
    }
90
91
    /**
92
     * Test for isRememberSortingOrder
93
     */
94
    public function testIsRememberSortingOrder(): void
95
    {
96
        // Test environment.
97
        $GLOBALS['cfg']['RememberSorting'] = true;
98
99
        $this->assertTrue(
100
            $this->callFunction($this->sql, Sql::class, 'isRememberSortingOrder', [
101
                ParseAnalyze::sqlQuery('SELECT * FROM tbl', $GLOBALS['db'])[0],
102
            ]),
103
        );
104
105
        $this->assertFalse(
106
            $this->callFunction($this->sql, Sql::class, 'isRememberSortingOrder', [
107
                ParseAnalyze::sqlQuery('SELECT col FROM tbl', $GLOBALS['db'])[0],
108
            ]),
109
        );
110
111
        $this->assertFalse(
112
            $this->callFunction($this->sql, Sql::class, 'isRememberSortingOrder', [
113
                ParseAnalyze::sqlQuery('SELECT 1', $GLOBALS['db'])[0],
114
            ]),
115
        );
116
117
        $this->assertFalse(
118
            $this->callFunction($this->sql, Sql::class, 'isRememberSortingOrder', [
119
                ParseAnalyze::sqlQuery('SELECT col1, col2 FROM tbl', $GLOBALS['db'])[0],
120
            ]),
121
        );
122
123
        $this->assertFalse(
124
            $this->callFunction($this->sql, Sql::class, 'isRememberSortingOrder', [
125
                ParseAnalyze::sqlQuery('SELECT COUNT(*) from tbl', $GLOBALS['db'])[0],
126
            ]),
127
        );
128
    }
129
130
    /**
131
     * Test for isAppendLimitClause
132
     */
133
    public function testIsAppendLimitClause(): void
134
    {
135
        // Test environment.
136
        $GLOBALS['_SESSION']['tmpval']['max_rows'] = 10;
137
138
        $this->assertTrue(
139
            $this->callFunction($this->sql, Sql::class, 'isAppendLimitClause', [
140
                ParseAnalyze::sqlQuery('SELECT * FROM tbl', $GLOBALS['db'])[0],
141
            ]),
142
        );
143
144
        $this->assertFalse(
145
            $this->callFunction($this->sql, Sql::class, 'isAppendLimitClause', [
146
                ParseAnalyze::sqlQuery('SELECT * from tbl LIMIT 0, 10', $GLOBALS['db'])[0],
147
            ]),
148
        );
149
    }
150
151
    public function testIsJustBrowsing(): void
152
    {
153
        // Test environment.
154
        $GLOBALS['_SESSION']['tmpval']['max_rows'] = 10;
155
156
        $this->assertTrue(Sql::isJustBrowsing(
157
            ParseAnalyze::sqlQuery('SELECT * FROM db.tbl', $GLOBALS['db'])[0],
158
            null,
159
        ));
160
161
        $this->assertTrue(Sql::isJustBrowsing(
162
            ParseAnalyze::sqlQuery('SELECT * FROM tbl WHERE 1', $GLOBALS['db'])[0],
163
            null,
164
        ));
165
166
        $this->assertFalse(Sql::isJustBrowsing(
167
            ParseAnalyze::sqlQuery('SELECT * from tbl1, tbl2 LIMIT 0, 10', $GLOBALS['db'])[0],
168
            null,
169
        ));
170
    }
171
172
    /**
173
     * Test for isDeleteTransformationInfo
174
     */
175
    public function testIsDeleteTransformationInfo(): void
176
    {
177
        $this->assertTrue(
178
            $this->callFunction($this->sql, Sql::class, 'isDeleteTransformationInfo', [
179
                ParseAnalyze::sqlQuery('ALTER TABLE tbl DROP COLUMN col', $GLOBALS['db'])[0],
180
            ]),
181
        );
182
183
        $this->assertTrue(
184
            $this->callFunction($this->sql, Sql::class, 'isDeleteTransformationInfo', [
185
                ParseAnalyze::sqlQuery('DROP TABLE tbl', $GLOBALS['db'])[0],
186
            ]),
187
        );
188
189
        $this->assertFalse(
190
            $this->callFunction($this->sql, Sql::class, 'isDeleteTransformationInfo', [
191
                ParseAnalyze::sqlQuery('SELECT * from tbl', $GLOBALS['db'])[0],
192
            ]),
193
        );
194
    }
195
196
    /**
197
     * Test for hasNoRightsToDropDatabase
198
     */
199
    public function testHasNoRightsToDropDatabase(): void
200
    {
201
        $this->assertTrue(
202
            $this->sql->hasNoRightsToDropDatabase(
203
                ParseAnalyze::sqlQuery('DROP DATABASE db', $GLOBALS['db'])[0],
204
                false,
205
                false,
206
            ),
207
        );
208
209
        $this->assertFalse(
210
            $this->sql->hasNoRightsToDropDatabase(
211
                ParseAnalyze::sqlQuery('DROP TABLE tbl', $GLOBALS['db'])[0],
212
                false,
213
                false,
214
            ),
215
        );
216
217
        $this->assertFalse(
218
            $this->sql->hasNoRightsToDropDatabase(
219
                ParseAnalyze::sqlQuery('SELECT * from tbl', $GLOBALS['db'])[0],
220
                false,
221
                false,
222
            ),
223
        );
224
    }
225
226
    /**
227
     * Should return false if all columns are not from the same table
228
     */
229
    public function testWithMultipleTables(): void
230
    {
231
        $col1 = new stdClass();
232
        $col1->table = 'table1';
233
        $col2 = new stdClass();
234
        $col2->table = 'table1';
235
        $col3 = new stdClass();
236
        $col3->table = 'table3';
237
238
        $fieldsMeta = [$col1, $col2, $col3];
239
        $this->assertFalse(
240
            $this->callFunction($this->sql, Sql::class, 'resultSetHasJustOneTable', [$fieldsMeta]),
241
        );
242
243
        // should not matter on where the odd column occurs
244
        $fieldsMeta = [$col2, $col3, $col1];
245
        $this->assertFalse(
246
            $this->callFunction($this->sql, Sql::class, 'resultSetHasJustOneTable', [$fieldsMeta]),
247
        );
248
249
        $fieldsMeta = [$col3, $col1, $col2];
250
        $this->assertFalse(
251
            $this->callFunction($this->sql, Sql::class, 'resultSetHasJustOneTable', [$fieldsMeta]),
252
        );
253
    }
254
255
    /**
256
     * Should return true if all the columns are from the same table
257
     */
258
    public function testWithSameTable(): void
259
    {
260
        $col1 = new stdClass();
261
        $col1->table = 'table1';
262
        $col2 = new stdClass();
263
        $col2->table = 'table1';
264
        $col3 = new stdClass();
265
        $col3->table = 'table1';
266
        $fieldsMeta = [$col1, $col2, $col3];
267
268
        $this->assertTrue(
269
            $this->callFunction($this->sql, Sql::class, 'resultSetHasJustOneTable', [$fieldsMeta]),
270
        );
271
    }
272
273
    /**
274
     * Should return true even if function columns (table is '') occur when others
275
     * are from the same table.
276
     */
277
    public function testWithFunctionColumns(): void
278
    {
279
        $col1 = new stdClass();
280
        $col1->table = 'table1';
281
        $col2 = new stdClass();
282
        $col2->table = '';
283
        $col3 = new stdClass();
284
        $col3->table = 'table1';
285
286
        $fieldsMeta = [$col1, $col2, $col3];
287
        $this->assertTrue(
288
            $this->callFunction($this->sql, Sql::class, 'resultSetHasJustOneTable', [$fieldsMeta]),
289
        );
290
291
        // should not matter on where the function column occurs
292
        $fieldsMeta = [$col2, $col3, $col1];
293
        $this->assertTrue(
294
            $this->callFunction($this->sql, Sql::class, 'resultSetHasJustOneTable', [$fieldsMeta]),
295
        );
296
297
        $fieldsMeta = [$col3, $col1, $col2];
298
        $this->assertTrue(
299
            $this->callFunction($this->sql, Sql::class, 'resultSetHasJustOneTable', [$fieldsMeta]),
300
        );
301
    }
302
303
    /**
304
     * We can not say all the columns are from the same table if all the columns
305
     * are function columns (table is '')
306
     */
307
    public function testWithOnlyFunctionColumns(): void
308
    {
309
        $col1 = new stdClass();
310
        $col1->table = '';
311
        $col2 = new stdClass();
312
        $col2->table = '';
313
        $col3 = new stdClass();
314
        $col3->table = '';
315
        $fieldsMeta = [$col1, $col2, $col3];
316
317
        $this->assertFalse(
318
            $this->callFunction($this->sql, Sql::class, 'resultSetHasJustOneTable', [$fieldsMeta]),
319
        );
320
    }
321
322
    /** @return mixed[][] */
323
    public static function dataProviderCountQueryResults(): array
324
    {
325
        // sql query
326
        // session tmpval
327
        // num rows
328
        // result
329
        // just browsing
330
        return [
331
            'join on SELECT results with *' => [
332
                // -- Showing rows 0 - 49 (164056 total, 0 in query, Query took 0.1498 seconds.)
333
                'select * from game_auth_logs l join ('
334
                    . ' select al.user_id, max(al.id) as id from game_auth_logs al '
335
                    . 'where al.successfull = 1 group by al.user_id ) last_log on last_log.id = l.id;',
336
                ['max_rows' => 50, 'pos' => 0],
337
                164056,
338
                50,
339
                false,
340
                'SELECT COUNT(*) FROM (SELECT 1 FROM game_auth_logs AS `l` JOIN ('
341
                    . ' select al.user_id, max(al.id) as id from game_auth_logs al '
342
                    . 'where al.successfull = 1 group by al.user_id ) AS `last_log` ON last_log.id = l.id'
343
                    . ' ) as cnt',
344
            ],
345
            'join on SELECT results with alias.*' => [
346
                // -- Showing rows 0 - 24 (267 total, Query took 0.1533 seconds.)
347
                'select l.* from game_auth_logs l join ('
348
                    . ' select al.user_id, max(al.id) as id from game_auth_logs al '
349
                    . 'where al.successfull = 1 group by al.user_id ) last_log on last_log.id = l.id;',
350
                ['max_rows' => 50, 'pos' => 0],
351
                267,
352
                50,
353
                false,
354
                'SELECT COUNT(*) FROM (SELECT 1 FROM game_auth_logs AS `l` JOIN ('
355
                    . ' select al.user_id, max(al.id) as id from game_auth_logs al '
356
                    . 'where al.successfull = 1 group by al.user_id ) AS `last_log` ON last_log.id = l.id'
357
                    . ' ) as cnt',
358
            ],
359
            ['SELECT * FROM company_users WHERE id != 0 LIMIT 0, 10', ['max_rows' => 250], -1, -1],
360
            ['SELECT * FROM company_users WHERE id != 0', ['max_rows' => 250, 'pos' => -1], -1, -2],
361
            ['SELECT * FROM company_users WHERE id != 0', ['max_rows' => 250, 'pos' => -1], -1, -2],
362
            ['SELECT * FROM company_users WHERE id != 0', ['max_rows' => 250, 'pos' => 250], -1, 249],
363
            ['SELECT * FROM company_users WHERE id != 0', ['max_rows' => 250, 'pos' => 4], 2, 6],
364
            ['SELECT * FROM company_users WHERE id != 0', ['max_rows' => 'all', 'pos' => 4], 2, 2],
365
            [null, [], 2, 0],
366
            ['SELECT * FROM company_users LIMIT 1,4', ['max_rows' => 10, 'pos' => 4], 20, 20],
367
            ['SELECT * FROM company_users', ['max_rows' => 10, 'pos' => 4], 20, 4],
368
            ['SELECT * FROM company_users WHERE not_working_count != 0', ['max_rows' => 10, 'pos' => 4], 20, 0],
369
            ['SELECT * FROM company_users WHERE working_count = 0', ['max_rows' => 10, 'pos' => 4], 20, 15],
370
            ['UPDATE company_users SET a=1 WHERE working_count = 0', ['max_rows' => 10, 'pos' => 4], 20, 20],
371
            ['UPDATE company_users SET a=1 WHERE working_count = 0', ['max_rows' => 'all', 'pos' => 4], 20, 20],
372
            ['UPDATE company_users SET a=1 WHERE working_count = 0', ['max_rows' => 15], 20, 20],
373
            ['SELECT * FROM company_users WHERE id != 0', ['max_rows' => 250, 'pos' => 4], 2, 6, true],
374
            [
375
                'SELECT *, (SELECT COUNT(*) FROM tbl1) as c1, (SELECT 1 FROM tbl2) as c2 '
376
                . 'FROM company_users WHERE id != 0',
377
                ['max_rows' => 250, 'pos' => 4],
378
                2,
379
                6,
380
                true,
381
            ],
382
            ['SELECT * FROM company_users', ['max_rows' => 10, 'pos' => 4], 20, 18, true],
383
            [
384
                'SELECT *, 1, (SELECT COUNT(*) FROM tbl1) as c1, '
385
                . '(SELECT 1 FROM tbl2) as c2 FROM company_users WHERE subquery_case = 0',
386
                ['max_rows' => 10, 'pos' => 4],
387
                20,
388
                42,
389
390
            ],
391
            [
392
                'SELECT ( as c2 FROM company_users WHERE working_count = 0',// Invalid query
393
                ['max_rows' => 10],
394
                20,
395
                20,
396
397
            ],
398
        ];
399
    }
400
401
    /**
402
     * @param mixed[] $sessionTmpVal
403
     *
404
     * @dataProvider dataProviderCountQueryResults
405
     */
406
    public function testCountQueryResults(
407
        string|null $sqlQuery,
408
        array $sessionTmpVal,
409
        int $numRows,
410
        int $expectedNumRows,
411
        bool $justBrowsing = false,
412
        string|null $expectedCountQuery = null,
413
    ): void {
414
        if ($justBrowsing) {
415
            $GLOBALS['cfg']['Server']['DisableIS'] = true;
416
        }
417
418
        $_SESSION['tmpval'] = $sessionTmpVal;
419
420
        if ($expectedCountQuery !== null) {
421
            $this->dummyDbi->addResult(
422
                $expectedCountQuery,
423
                [[$expectedNumRows]],
424
                [],
425
                [],
426
            );
427
        }
428
429
        $result = $this->callFunction(
430
            $this->sql,
431
            Sql::class,
432
            'countQueryResults',
433
            [
434
                $numRows,
435
                $justBrowsing,
436
                'my_dataset',// db
437
                'company_users',// table
438
                ParseAnalyze::sqlQuery($sqlQuery ?? '', $GLOBALS['db'])[0],
439
            ],
440
        );
441
        $this->assertEquals($expectedNumRows, $result);
442
        $this->dummyDbi->assertAllQueriesConsumed();
443
    }
444
445
    public function testExecuteQueryAndSendQueryResponse(): void
446
    {
447
        $this->dummyDbi->addSelectDb('sakila');
448
        $this->dummyDbi->addResult(
449
            'SELECT * FROM `sakila`.`country` LIMIT 0, 3;',
450
            [
451
                ['1', 'Afghanistan', '2006-02-15 04:44:00'],
452
                ['2', 'Algeria', '2006-02-15 04:44:00'],
453
                ['3', 'American Samoa', '2006-02-15 04:44:00'],
454
            ],
455
            ['country_id', 'country', 'last_update'],
456
            [
457
                FieldHelper::fromArray(['type' => MYSQLI_TYPE_SHORT, 'length' => 5]),
458
                FieldHelper::fromArray(['type' => MYSQLI_TYPE_VAR_STRING, 'length' => 200]),
459
                FieldHelper::fromArray(['type' => MYSQLI_TYPE_TIMESTAMP, 'length' => 19]),
460
            ],
461
        );
462
        $this->dummyDbi->addResult(
463
            'SHOW TABLE STATUS FROM `sakila` WHERE `Name` LIKE \'country%\'',
464
            [
465
                [
466
                    'country',
467
                    'InnoDB',
468
                    '10',
469
                    'Dynamic',
470
                    '109',
471
                    '150',
472
                    '16384',
473
                    '0',
474
                    '0',
475
                    '0',
476
                    '110',
477
                    '2011-12-13 14:15:16',
478
                    null,
479
                    null,
480
                    'utf8mb4_general_ci',
481
                    null,
482
                    '',
483
                    '',
484
                    '0',
485
                    'N',
486
                ],
487
            ],
488
            [
489
                'Name',
490
                'Engine',
491
                'Version',
492
                'Row_format',
493
                'Rows',
494
                'Avg_row_length',
495
                'Data_length',
496
                'Max_data_length',
497
                'Index_length',
498
                'Data_free',
499
                'Auto_increment',
500
                'Create_time',
501
                'Update_time',
502
                'Check_time',
503
                'Collation',
504
                'Checksum',
505
                'Create_options',
506
                'Comment',
507
                'Max_index_length',
508
                'Temporary',
509
            ],
510
        );
511
        $this->dummyDbi->addResult(
512
            'SHOW CREATE TABLE `sakila`.`country`',
513
            [
514
                [
515
                    'country',
516
                    'CREATE TABLE `country` (' . "\n"
517
                    . '  `country_id` smallint(5) unsigned NOT NULL AUTO_INCREMENT,' . "\n"
518
                    . '  `country` varchar(50) NOT NULL,' . "\n"
519
                    . '  `last_update` timestamp NOT NULL DEFAULT current_timestamp()'
520
                    . ' ON UPDATE current_timestamp(),' . "\n"
521
                    . '  PRIMARY KEY (`country_id`)' . "\n"
522
                    . ') ENGINE=InnoDB AUTO_INCREMENT=110 DEFAULT CHARSET=utf8mb4',
523
                ],
524
            ],
525
            ['Table', 'Create Table'],
526
        );
527
        $this->dummyDbi->addResult('SELECT COUNT(*) FROM `sakila`.`country`', [['109']]);
528
        $this->dummyDbi->addResult(
529
            'SHOW FULL COLUMNS FROM `sakila`.`country`',
530
            [
531
                [
532
                    'country_id',
533
                    'smallint(5) unsigned',
534
                    null,
535
                    'NO',
536
                    'PRI',
537
                    null,
538
                    'auto_increment',
539
                    'select,insert,update,references',
540
                    '',
541
                ],
542
                [
543
                    'country',
544
                    'varchar(50)',
545
                    'utf8mb4_general_ci',
546
                    'NO',
547
                    '',
548
                    null,
549
                    '',
550
                    'select,insert,update,references',
551
                    '',
552
                ],
553
                [
554
                    'last_update',
555
                    'timestamp',
556
                    null,
557
                    'NO',
558
                    '',
559
                    'current_timestamp()',
560
                    'on update current_timestamp()',
561
                    'select,insert,update,references',
562
                    '',
563
                ],
564
            ],
565
            ['Field', 'Type', 'Collation', 'Null', 'Key', 'Default', 'Extra', 'Privileges', 'Comment'],
566
        );
567
        $this->dummyDbi->addResult(
568
            'SHOW COLUMNS FROM `sakila`.`country`',
569
            [
570
                ['country_id', 'smallint(5) unsigned', 'NO', 'PRI', null, 'auto_increment'],
571
                ['country', 'varchar(50)', 'NO', '', null, ''],
572
                ['last_update', 'timestamp', 'NO', '', 'current_timestamp()', 'on update current_timestamp()'],
573
            ],
574
            ['Field', 'Type', 'Null', 'Key', 'Default', 'Extra'],
575
        );
576
        $this->dummyDbi->addResult(
577
            'SHOW INDEXES FROM `sakila`.`country`',
578
            [['country', '0', 'PRIMARY', 'country_id']],
579
            ['Table', 'Non_unique', 'Key_name', 'Column_name'],
580
        );
581
        $this->dummyDbi->addResult(
582
            'SELECT 1 FROM information_schema.VIEWS'
583
            . ' WHERE TABLE_SCHEMA = \'sakila\' AND TABLE_NAME = \'country\' AND IS_UPDATABLE = \'YES\'',
584
            [],
585
        );
586
        $_SESSION['sql_from_query_box'] = true;
587
        $GLOBALS['db'] = 'sakila';
588
        $GLOBALS['table'] = 'country';
589
        $GLOBALS['sql_query'] = 'SELECT * FROM `sakila`.`country` LIMIT 0, 3;';
590
        $GLOBALS['cfg']['Server']['DisableIS'] = true;
591
        $GLOBALS['cfg']['Server']['user'] = 'user';
592
        $actual = $this->sql->executeQueryAndSendQueryResponse(
593
            null,
594
            false,
595
            'sakila',
596
            'different_table',
597
            null,
598
            null,
599
            null,
600
            null,
601
            null,
602
            'index.php?route=/sql',
603
            null,
604
            null,
605
            'SELECT * FROM `sakila`.`country` LIMIT 0, 3;',
606
            null,
607
        );
608
        $this->assertStringContainsString('Showing rows 0 -  2 (3 total', $actual);
609
        $this->assertStringContainsString('SELECT * FROM `sakila`.`country` LIMIT 0, 3;', $actual);
610
        $this->assertStringContainsString('Afghanistan', $actual);
611
        $this->assertStringContainsString('Algeria', $actual);
612
        $this->assertStringContainsString('American Samoa', $actual);
613
        $this->assertStringContainsString('data-type="int"', $actual);
614
        $this->assertStringContainsString('data-type="string"', $actual);
615
        $this->assertStringContainsString('data-type="timestamp"', $actual);
616
    }
617
618
    public function testGetDetailedProfilingStatsWithoutData(): void
619
    {
620
        $method = new ReflectionMethod($this->sql, 'getDetailedProfilingStats');
621
        $this->assertSame(
622
            ['total_time' => 0, 'states' => [], 'chart' => [], 'profile' => []],
623
            $method->invoke($this->sql, []),
624
        );
625
    }
626
627
    public function testGetDetailedProfilingStatsWithData(): void
628
    {
629
        $method = new ReflectionMethod($this->sql, 'getDetailedProfilingStats');
630
        $profiling = [
631
            ['Status' => 'Starting', 'Duration' => '0.000017'],
632
            ['Status' => 'checking permissions', 'Duration' => '0.000003'],
633
            ['Status' => 'Opening tables', 'Duration' => '0.000152'],
634
            ['Status' => 'After opening tables', 'Duration' => '0.000004'],
635
            ['Status' => 'System lock', 'Duration' => '0.000002'],
636
            ['Status' => 'table lock', 'Duration' => '0.000003'],
637
            ['Status' => 'Opening tables', 'Duration' => '0.000008'],
638
            ['Status' => 'After opening tables', 'Duration' => '0.000002'],
639
            ['Status' => 'System lock', 'Duration' => '0.000002'],
640
            ['Status' => 'table lock', 'Duration' => '0.000012'],
641
            ['Status' => 'Unlocking tables', 'Duration' => '0.000003'],
642
            ['Status' => 'closing tables', 'Duration' => '0.000005'],
643
            ['Status' => 'init', 'Duration' => '0.000007'],
644
            ['Status' => 'Optimizing', 'Duration' => '0.000004'],
645
            ['Status' => 'Statistics', 'Duration' => '0.000006'],
646
            ['Status' => 'Preparing', 'Duration' => '0.000006'],
647
            ['Status' => 'Executing', 'Duration' => '0.000002'],
648
            ['Status' => 'Sending data', 'Duration' => '0.000029'],
649
            ['Status' => 'End of update loop', 'Duration' => '0.000003'],
650
            ['Status' => 'Query end', 'Duration' => '0.000002'],
651
            ['Status' => 'Commit', 'Duration' => '0.000002'],
652
            ['Status' => 'closing tables', 'Duration' => '0.000002'],
653
            ['Status' => 'Unlocking tables', 'Duration' => '0.000001'],
654
            ['Status' => 'closing tables', 'Duration' => '0.000002'],
655
            ['Status' => 'Starting cleanup', 'Duration' => '0.000002'],
656
            ['Status' => 'Freeing items', 'Duration' => '0.000002'],
657
            ['Status' => 'Updating status', 'Duration' => '0.000007'],
658
            ['Status' => 'Reset for next command', 'Duration' => '0.000009'],
659
        ];
660
        $expected = [
661
            'total_time' => 0.000299,
662
            'states' => [
663
                'Starting' => ['total_time' => '0.000017', 'calls' => 1],
664
                'Checking Permissions' => ['total_time' => '0.000003', 'calls' => 1],
665
                'Opening Tables' => ['total_time' => 0.00016, 'calls' => 2],
666
                'After Opening Tables' => ['total_time' => 6.0E-6, 'calls' => 2],
667
                'System Lock' => ['total_time' => 4.0E-6, 'calls' => 2],
668
                'Table Lock' => ['total_time' => 1.5E-5, 'calls' => 2],
669
                'Unlocking Tables' => ['total_time' => 4.0E-6, 'calls' => 2],
670
                'Closing Tables' => ['total_time' => 9.0E-6, 'calls' => 3],
671
                'Init' => ['total_time' => '0.000007', 'calls' => 1],
672
                'Optimizing' => ['total_time' => '0.000004', 'calls' => 1],
673
                'Statistics' => ['total_time' => '0.000006', 'calls' => 1],
674
                'Preparing' => ['total_time' => '0.000006', 'calls' => 1],
675
                'Executing' => ['total_time' => '0.000002', 'calls' => 1],
676
                'Sending Data' => ['total_time' => '0.000029', 'calls' => 1],
677
                'End Of Update Loop' => ['total_time' => '0.000003', 'calls' => 1],
678
                'Query End' => ['total_time' => '0.000002', 'calls' => 1],
679
                'Commit' => ['total_time' => '0.000002', 'calls' => 1],
680
                'Starting Cleanup' => ['total_time' => '0.000002', 'calls' => 1],
681
                'Freeing Items' => ['total_time' => '0.000002', 'calls' => 1],
682
                'Updating Status' => ['total_time' => '0.000007', 'calls' => 1],
683
                'Reset For Next Command' => ['total_time' => '0.000009', 'calls' => 1],
684
            ],
685
            'chart' => [
686
                'Starting' => '0.000017',
687
                'Checking Permissions' => '0.000003',
688
                'Opening Tables' => 0.00016,
689
                'After Opening Tables' => 6.0E-6,
690
                'System Lock' => 4.0E-6,
691
                'Table Lock' => 1.5E-5,
692
                'Unlocking Tables' => 4.0E-6,
693
                'Closing Tables' => 9.0E-6,
694
                'Init' => '0.000007',
695
                'Optimizing' => '0.000004',
696
                'Statistics' => '0.000006',
697
                'Preparing' => '0.000006',
698
                'Executing' => '0.000002',
699
                'Sending Data' => '0.000029',
700
                'End Of Update Loop' => '0.000003',
701
                'Query End' => '0.000002',
702
                'Commit' => '0.000002',
703
                'Starting Cleanup' => '0.000002',
704
                'Freeing Items' => '0.000002',
705
                'Updating Status' => '0.000007',
706
                'Reset For Next Command' => '0.000009',
707
            ],
708
            'profile' => [
709
                ['status' => 'Starting', 'duration' => '17 µ', 'duration_raw' => '0.000017'],
710
                ['status' => 'Checking Permissions', 'duration' => '3 µ', 'duration_raw' => '0.000003'],
711
                ['status' => 'Opening Tables', 'duration' => '152 µ', 'duration_raw' => '0.000152'],
712
                ['status' => 'After Opening Tables', 'duration' => '4 µ', 'duration_raw' => '0.000004'],
713
                ['status' => 'System Lock', 'duration' => '2 µ', 'duration_raw' => '0.000002'],
714
                ['status' => 'Table Lock', 'duration' => '3 µ', 'duration_raw' => '0.000003'],
715
                ['status' => 'Opening Tables', 'duration' => '8 µ', 'duration_raw' => '0.000008'],
716
                ['status' => 'After Opening Tables', 'duration' => '2 µ', 'duration_raw' => '0.000002'],
717
                ['status' => 'System Lock', 'duration' => '2 µ', 'duration_raw' => '0.000002'],
718
                ['status' => 'Table Lock', 'duration' => '12 µ', 'duration_raw' => '0.000012'],
719
                ['status' => 'Unlocking Tables', 'duration' => '3 µ', 'duration_raw' => '0.000003'],
720
                ['status' => 'Closing Tables', 'duration' => '5 µ', 'duration_raw' => '0.000005'],
721
                ['status' => 'Init', 'duration' => '7 µ', 'duration_raw' => '0.000007'],
722
                ['status' => 'Optimizing', 'duration' => '4 µ', 'duration_raw' => '0.000004'],
723
                ['status' => 'Statistics', 'duration' => '6 µ', 'duration_raw' => '0.000006'],
724
                ['status' => 'Preparing', 'duration' => '6 µ', 'duration_raw' => '0.000006'],
725
                ['status' => 'Executing', 'duration' => '2 µ', 'duration_raw' => '0.000002'],
726
                ['status' => 'Sending Data', 'duration' => '29 µ', 'duration_raw' => '0.000029'],
727
                ['status' => 'End Of Update Loop', 'duration' => '3 µ', 'duration_raw' => '0.000003'],
728
                ['status' => 'Query End', 'duration' => '2 µ', 'duration_raw' => '0.000002'],
729
                ['status' => 'Commit', 'duration' => '2 µ', 'duration_raw' => '0.000002'],
730
                ['status' => 'Closing Tables', 'duration' => '2 µ', 'duration_raw' => '0.000002'],
731
                ['status' => 'Unlocking Tables', 'duration' => '1 µ', 'duration_raw' => '0.000001'],
732
                ['status' => 'Closing Tables', 'duration' => '2 µ', 'duration_raw' => '0.000002'],
733
                ['status' => 'Starting Cleanup', 'duration' => '2 µ', 'duration_raw' => '0.000002'],
734
                ['status' => 'Freeing Items', 'duration' => '2 µ', 'duration_raw' => '0.000002'],
735
                ['status' => 'Updating Status', 'duration' => '7 µ', 'duration_raw' => '0.000007'],
736
                ['status' => 'Reset For Next Command', 'duration' => '9 µ', 'duration_raw' => '0.000009'],
737
            ],
738
        ];
739
        $this->assertSame($expected, $method->invoke($this->sql, $profiling));
740
    }
741
}
742