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; |
|
|
|
|
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
|
|
|
|
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:For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths