1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
declare(strict_types=1); |
4
|
|
|
|
5
|
|
|
namespace PhpMyAdmin\Controllers\Database; |
6
|
|
|
|
7
|
|
|
use DateTimeImmutable; |
8
|
|
|
use PhpMyAdmin\Charsets; |
9
|
|
|
use PhpMyAdmin\Config; |
10
|
|
|
use PhpMyAdmin\Config\PageSettings; |
11
|
|
|
use PhpMyAdmin\ConfigStorage\Relation; |
12
|
|
|
use PhpMyAdmin\Controllers\InvocableController; |
13
|
|
|
use PhpMyAdmin\Current; |
14
|
|
|
use PhpMyAdmin\Dbal\DatabaseInterface; |
15
|
|
|
use PhpMyAdmin\DbTableExists; |
16
|
|
|
use PhpMyAdmin\Favorites\RecentFavoriteTable; |
17
|
|
|
use PhpMyAdmin\Favorites\RecentFavoriteTables; |
18
|
|
|
use PhpMyAdmin\Favorites\TableType; |
19
|
|
|
use PhpMyAdmin\Html\Generator; |
20
|
|
|
use PhpMyAdmin\Http\Response; |
21
|
|
|
use PhpMyAdmin\Http\ServerRequest; |
22
|
|
|
use PhpMyAdmin\Identifiers\DatabaseName; |
|
|
|
|
23
|
|
|
use PhpMyAdmin\Identifiers\TableName; |
|
|
|
|
24
|
|
|
use PhpMyAdmin\Message; |
25
|
|
|
use PhpMyAdmin\Query\Utilities; |
26
|
|
|
use PhpMyAdmin\Replication\Replication; |
27
|
|
|
use PhpMyAdmin\Replication\ReplicationInfo; |
28
|
|
|
use PhpMyAdmin\ResponseRenderer; |
29
|
|
|
use PhpMyAdmin\Sanitize; |
30
|
|
|
use PhpMyAdmin\StorageEngine; |
31
|
|
|
use PhpMyAdmin\Template; |
32
|
|
|
use PhpMyAdmin\Tracking\TrackedTable; |
|
|
|
|
33
|
|
|
use PhpMyAdmin\Tracking\Tracker; |
34
|
|
|
use PhpMyAdmin\Tracking\TrackingChecker; |
35
|
|
|
use PhpMyAdmin\Url; |
36
|
|
|
use PhpMyAdmin\Util; |
37
|
|
|
use Throwable; |
38
|
|
|
|
39
|
|
|
use function __; |
40
|
|
|
use function array_search; |
41
|
|
|
use function ceil; |
42
|
|
|
use function count; |
43
|
|
|
use function htmlspecialchars; |
44
|
|
|
use function implode; |
45
|
|
|
use function in_array; |
46
|
|
|
use function is_string; |
47
|
|
|
use function max; |
48
|
|
|
use function mb_substr; |
49
|
|
|
use function md5; |
50
|
|
|
use function preg_match; |
51
|
|
|
use function preg_quote; |
52
|
|
|
use function sprintf; |
53
|
|
|
use function str_replace; |
54
|
|
|
use function urlencode; |
55
|
|
|
|
56
|
|
|
/** |
57
|
|
|
* Handles database structure logic |
58
|
|
|
*/ |
59
|
|
|
final class StructureController implements InvocableController |
60
|
|
|
{ |
61
|
|
|
/** @var int Number of tables */ |
62
|
|
|
private int $numTables = 0; |
63
|
|
|
|
64
|
|
|
/** @var int Current position in the list */ |
65
|
|
|
private int $position = 0; |
66
|
|
|
|
67
|
|
|
/** @var bool DB is information_schema */ |
68
|
|
|
private bool $dbIsSystemSchema = false; |
69
|
|
|
|
70
|
|
|
/** @var int Number of tables */ |
71
|
|
|
private int $totalNumTables = 0; |
72
|
|
|
|
73
|
|
|
/** @var mixed[] Tables in the database */ |
74
|
|
|
private array $tables = []; |
75
|
|
|
|
76
|
|
|
/** @var bool whether stats show or not */ |
77
|
|
|
private bool $isShowStats = false; |
78
|
|
|
|
79
|
|
|
private ReplicationInfo $replicationInfo; |
80
|
|
|
|
81
|
20 |
|
public function __construct( |
82
|
|
|
private readonly ResponseRenderer $response, |
83
|
|
|
private readonly Template $template, |
84
|
|
|
private readonly Relation $relation, |
85
|
|
|
private readonly Replication $replication, |
86
|
|
|
private readonly DatabaseInterface $dbi, |
87
|
|
|
private readonly TrackingChecker $trackingChecker, |
88
|
|
|
private readonly PageSettings $pageSettings, |
89
|
|
|
private readonly DbTableExists $dbTableExists, |
90
|
|
|
private readonly Config $config, |
91
|
|
|
) { |
92
|
20 |
|
$this->replicationInfo = new ReplicationInfo($this->dbi); |
93
|
|
|
} |
94
|
|
|
|
95
|
|
|
/** |
96
|
|
|
* Retrieves database information for further use. |
97
|
|
|
*/ |
98
|
4 |
|
private function getDatabaseInfo(ServerRequest $request): void |
99
|
|
|
{ |
100
|
4 |
|
[$tables, $totalNumTables] = Util::getDbInfo($request, Current::$database); |
101
|
|
|
|
102
|
4 |
|
$this->tables = $tables; |
103
|
4 |
|
$this->numTables = count($tables); |
104
|
4 |
|
$this->position = Util::getTableListPosition($request, Current::$database); |
105
|
4 |
|
$this->totalNumTables = $totalNumTables; |
106
|
|
|
|
107
|
|
|
/** |
108
|
|
|
* whether to display extended stats |
109
|
|
|
*/ |
110
|
4 |
|
$this->isShowStats = $this->config->settings['ShowStats']; |
111
|
|
|
|
112
|
|
|
/** |
113
|
|
|
* whether selected db is information_schema |
114
|
|
|
*/ |
115
|
4 |
|
$this->dbIsSystemSchema = false; |
116
|
|
|
|
117
|
4 |
|
if (! Utilities::isSystemSchema(Current::$database)) { |
118
|
4 |
|
return; |
119
|
|
|
} |
120
|
|
|
|
121
|
|
|
$this->isShowStats = false; |
122
|
|
|
$this->dbIsSystemSchema = true; |
123
|
|
|
} |
124
|
|
|
|
125
|
|
|
public function __invoke(ServerRequest $request): Response |
126
|
|
|
{ |
127
|
|
|
$parameters = ['sort' => $_REQUEST['sort'] ?? null, 'sort_order' => $_REQUEST['sort_order'] ?? null]; |
128
|
|
|
|
129
|
|
|
if (Current::$database === '') { |
130
|
|
|
return $this->response->missingParameterError('db'); |
131
|
|
|
} |
132
|
|
|
|
133
|
|
|
$databaseName = DatabaseName::tryFrom($request->getParam('db')); |
134
|
|
|
if ($databaseName === null || ! $this->dbTableExists->selectDatabase($databaseName)) { |
135
|
|
|
if ($request->isAjax()) { |
136
|
|
|
$this->response->setRequestStatus(false); |
137
|
|
|
$this->response->addJSON('message', Message::error(__('No databases selected.'))); |
138
|
|
|
|
139
|
|
|
return $this->response->response(); |
140
|
|
|
} |
141
|
|
|
|
142
|
|
|
$this->response->redirectToRoute('/', ['reload' => true, 'message' => __('No databases selected.')]); |
143
|
|
|
|
144
|
|
|
return $this->response->response(); |
145
|
|
|
} |
146
|
|
|
|
147
|
|
|
$this->response->addScriptFiles(['database/structure.js', 'table/change.js']); |
148
|
|
|
|
149
|
|
|
// Gets the database structure |
150
|
|
|
$this->getDatabaseInfo($request); |
151
|
|
|
|
152
|
|
|
// Checks if there are any tables to be shown on current page. |
153
|
|
|
// If there are no tables, the user is redirected to the last page |
154
|
|
|
// having any. |
155
|
|
|
if ($this->totalNumTables > 0 && $this->position > $this->totalNumTables) { |
156
|
|
|
$this->response->redirectToRoute('/database/structure', [ |
157
|
|
|
'db' => Current::$database, |
158
|
|
|
'pos' => max(0, $this->totalNumTables - $this->config->settings['MaxTableList']), |
159
|
|
|
'reload' => 1, |
160
|
|
|
]); |
161
|
|
|
} |
162
|
|
|
|
163
|
|
|
$this->replicationInfo->load($request->getParsedBodyParamAsStringOrNull('primary_connection')); |
164
|
|
|
$replicaInfo = $this->replicationInfo->getReplicaInfo(); |
165
|
|
|
|
166
|
|
|
$this->pageSettings->init('DbStructure'); |
167
|
|
|
$this->response->addHTML($this->pageSettings->getErrorHTML()); |
168
|
|
|
$this->response->addHTML($this->pageSettings->getHTML()); |
169
|
|
|
|
170
|
|
|
if ($this->numTables > 0) { |
171
|
|
|
$urlParams = ['pos' => $this->position, 'db' => Current::$database]; |
172
|
|
|
if (isset($parameters['sort'])) { |
173
|
|
|
$urlParams['sort'] = $parameters['sort']; |
174
|
|
|
} |
175
|
|
|
|
176
|
|
|
if (isset($parameters['sort_order'])) { |
177
|
|
|
$urlParams['sort_order'] = $parameters['sort_order']; |
178
|
|
|
} |
179
|
|
|
|
180
|
|
|
$listNavigator = Generator::getListNavigator( |
181
|
|
|
$this->totalNumTables, |
182
|
|
|
$this->position, |
183
|
|
|
$urlParams, |
184
|
|
|
Url::getFromRoute('/database/structure'), |
185
|
|
|
'frame_content', |
186
|
|
|
$this->config->settings['MaxTableList'], |
187
|
|
|
); |
188
|
|
|
|
189
|
|
|
$tableList = $this->displayTableList($replicaInfo); |
190
|
|
|
} |
191
|
|
|
|
192
|
|
|
$createTable = ''; |
193
|
|
|
if (! $this->dbIsSystemSchema) { |
194
|
|
|
$createTable = $this->template->render('database/create_table', ['db' => Current::$database]); |
195
|
|
|
} |
196
|
|
|
|
197
|
|
|
$this->response->render('database/structure/index', [ |
198
|
|
|
'database' => Current::$database, |
199
|
|
|
'has_tables' => $this->numTables > 0, |
200
|
|
|
'list_navigator_html' => $listNavigator ?? '', |
201
|
|
|
'table_list_html' => $tableList ?? '', |
202
|
|
|
'is_system_schema' => $this->dbIsSystemSchema, |
203
|
|
|
'create_table_html' => $createTable, |
204
|
|
|
]); |
205
|
|
|
|
206
|
|
|
return $this->response->response(); |
207
|
|
|
} |
208
|
|
|
|
209
|
|
|
/** @param mixed[] $replicaInfo */ |
210
|
4 |
|
private function displayTableList(array $replicaInfo): string |
211
|
|
|
{ |
212
|
4 |
|
$html = ''; |
213
|
|
|
|
214
|
|
|
// filtering |
215
|
4 |
|
$html .= $this->template->render('filter', ['filter_value' => '']); |
216
|
|
|
|
217
|
4 |
|
$i = $sumEntries = 0; |
218
|
4 |
|
$overheadCheck = false; |
219
|
4 |
|
$createTimeAll = null; |
220
|
4 |
|
$updateTimeAll = null; |
221
|
4 |
|
$checkTimeAll = null; |
222
|
4 |
|
$numColumns = $this->config->settings['PropertiesNumColumns'] > 1 |
223
|
|
|
? ceil($this->numTables / $this->config->settings['PropertiesNumColumns']) + 1 |
224
|
4 |
|
: 0; |
225
|
4 |
|
$rowCount = 0; |
226
|
4 |
|
$sumSize = 0; |
227
|
4 |
|
$overheadSize = 0; |
228
|
|
|
|
229
|
4 |
|
$hiddenFields = []; |
230
|
4 |
|
$overallApproxRows = false; |
231
|
4 |
|
$structureTableRows = []; |
232
|
4 |
|
$trackedTables = $this->trackingChecker->getTrackedTables(Current::$database); |
233
|
4 |
|
$recentFavoriteTables = RecentFavoriteTables::getInstance(TableType::Favorite); |
234
|
|
|
/** @var mixed[] $currentTable */ |
235
|
4 |
|
foreach ($this->tables as $currentTable) { |
236
|
|
|
// Get valid statistics whatever is the table type |
237
|
|
|
|
238
|
4 |
|
$dropQuery = ''; |
239
|
4 |
|
$dropMessage = ''; |
240
|
4 |
|
$overhead = ''; |
241
|
4 |
|
$inputClass = ['checkall']; |
242
|
|
|
|
243
|
|
|
// Sets parameters for links |
244
|
4 |
|
$tableUrlParams = ['db' => Current::$database, 'table' => $currentTable['TABLE_NAME']]; |
245
|
|
|
// do not list the previous table's size info for a view |
246
|
|
|
|
247
|
4 |
|
[ |
248
|
4 |
|
$currentTable, |
249
|
4 |
|
$formattedSize, |
250
|
4 |
|
$unit, |
251
|
4 |
|
$formattedOverhead, |
252
|
4 |
|
$overheadUnit, |
253
|
4 |
|
$overheadSize, |
254
|
4 |
|
$tableIsView, |
255
|
4 |
|
$sumSize, |
256
|
4 |
|
] = $this->getStuffForEngineTypeTable($currentTable, $sumSize, $overheadSize); |
257
|
|
|
|
258
|
4 |
|
$curTable = $this->dbi |
259
|
4 |
|
->getTable(Current::$database, $currentTable['TABLE_NAME']); |
260
|
4 |
|
if (! $curTable->isMerge()) { |
261
|
4 |
|
$sumEntries += $currentTable['TABLE_ROWS']; |
262
|
|
|
} |
263
|
|
|
|
264
|
4 |
|
$collationDefinition = '---'; |
265
|
4 |
|
if (isset($currentTable['Collation'])) { |
266
|
|
|
$tableCollation = Charsets::findCollationByName( |
267
|
|
|
$this->dbi, |
268
|
|
|
$this->config->selectedServer['DisableIS'], |
269
|
|
|
$currentTable['Collation'], |
270
|
|
|
); |
271
|
|
|
if ($tableCollation !== null) { |
272
|
|
|
$collationDefinition = $this->template->render('database/structure/collation_definition', [ |
273
|
|
|
'valueTitle' => $tableCollation->getDescription(), |
274
|
|
|
'value' => $tableCollation->getName(), |
275
|
|
|
]); |
276
|
|
|
} |
277
|
|
|
} |
278
|
|
|
|
279
|
4 |
|
if ($this->isShowStats) { |
280
|
4 |
|
$overhead = '-'; |
281
|
4 |
|
if ($formattedOverhead != '') { |
282
|
4 |
|
$overhead = $this->template->render('database/structure/overhead', [ |
283
|
4 |
|
'table_url_params' => $tableUrlParams, |
284
|
4 |
|
'formatted_overhead' => $formattedOverhead, |
285
|
4 |
|
'overhead_unit' => $overheadUnit, |
286
|
4 |
|
]); |
287
|
4 |
|
$overheadCheck = true; |
288
|
4 |
|
$inputClass[] = 'tbl-overhead'; |
289
|
|
|
} |
290
|
|
|
} |
291
|
|
|
|
292
|
4 |
|
if ($this->config->settings['ShowDbStructureCharset']) { |
293
|
|
|
$charset = ''; |
294
|
|
|
if (isset($tableCollation)) { |
295
|
|
|
$charset = $tableCollation->getCharset(); |
296
|
|
|
} |
297
|
|
|
} |
298
|
|
|
|
299
|
4 |
|
$createTime = null; |
300
|
4 |
|
if ($this->config->settings['ShowDbStructureCreation'] && isset($currentTable['Create_time'])) { |
301
|
|
|
$createTime = $this->createDateTime($currentTable['Create_time']); |
302
|
|
|
if ($createTime !== null && ($createTimeAll === null || $createTime < $createTimeAll)) { |
303
|
|
|
$createTimeAll = $createTime; |
304
|
|
|
} |
305
|
|
|
} |
306
|
|
|
|
307
|
4 |
|
$updateTime = null; |
308
|
4 |
|
if ($this->config->settings['ShowDbStructureLastUpdate'] && isset($currentTable['Update_time'])) { |
309
|
|
|
$updateTime = $this->createDateTime($currentTable['Update_time']); |
310
|
|
|
if ($updateTime !== null && ($updateTimeAll === null || $updateTime < $updateTimeAll)) { |
311
|
|
|
$updateTimeAll = $updateTime; |
312
|
|
|
} |
313
|
|
|
} |
314
|
|
|
|
315
|
4 |
|
$checkTime = null; |
316
|
4 |
|
if ($this->config->settings['ShowDbStructureLastCheck'] && isset($currentTable['Check_time'])) { |
317
|
|
|
$checkTime = $this->createDateTime($currentTable['Check_time']); |
318
|
|
|
if ($checkTime !== null && ($checkTimeAll === null || $checkTime < $checkTimeAll)) { |
319
|
|
|
$checkTimeAll = $checkTime; |
320
|
|
|
} |
321
|
|
|
} |
322
|
|
|
|
323
|
4 |
|
$truename = $currentTable['TABLE_NAME']; |
324
|
|
|
|
325
|
4 |
|
$i++; |
326
|
|
|
|
327
|
4 |
|
$rowCount++; |
328
|
4 |
|
if ($tableIsView) { |
329
|
|
|
$hiddenFields[] = '<input type="hidden" name="views[]" value="' |
330
|
|
|
. htmlspecialchars($currentTable['TABLE_NAME']) . '">'; |
331
|
|
|
} |
332
|
|
|
|
333
|
|
|
/** |
334
|
|
|
* Always activate links for Browse, Search and Empty, even if |
335
|
|
|
* the icons are greyed, because |
336
|
|
|
* 1. for views, we don't know the number of rows at this point |
337
|
|
|
* 2. for tables, another source could have populated them since the |
338
|
|
|
* page was generated |
339
|
|
|
* |
340
|
|
|
* I could have used the PHP ternary conditional operator but I find |
341
|
|
|
* the code easier to read without this operator. |
342
|
|
|
*/ |
343
|
4 |
|
$mayHaveRows = $currentTable['TABLE_ROWS'] > 0 || $tableIsView; |
|
|
|
|
344
|
|
|
|
345
|
4 |
|
if (! $this->dbIsSystemSchema) { |
346
|
4 |
|
$dropQuery = sprintf( |
347
|
4 |
|
'DROP %s %s', |
348
|
4 |
|
$tableIsView ? 'VIEW' : 'TABLE', |
349
|
4 |
|
Util::backquote( |
350
|
4 |
|
$currentTable['TABLE_NAME'], |
351
|
4 |
|
), |
352
|
4 |
|
); |
353
|
4 |
|
$dropMessage = sprintf( |
354
|
4 |
|
($tableIsView ? __('View %s has been dropped.') : __('Table %s has been dropped.')), |
355
|
4 |
|
str_replace( |
356
|
4 |
|
' ', |
357
|
4 |
|
' ', |
358
|
4 |
|
htmlspecialchars($currentTable['TABLE_NAME']), |
359
|
4 |
|
), |
360
|
4 |
|
); |
361
|
|
|
} |
362
|
|
|
|
363
|
4 |
|
if ($numColumns > 0 && $this->numTables > $numColumns && ($rowCount % $numColumns) === 0) { |
364
|
|
|
$rowCount = 1; |
365
|
|
|
|
366
|
|
|
$html .= $this->template->render('database/structure/table_header', [ |
367
|
|
|
'db' => Current::$database, |
368
|
|
|
'db_is_system_schema' => $this->dbIsSystemSchema, |
369
|
|
|
'replication' => $replicaInfo['status'], |
370
|
|
|
'properties_num_columns' => $this->config->settings['PropertiesNumColumns'], |
371
|
|
|
'is_show_stats' => $this->isShowStats, |
372
|
|
|
'show_charset' => $this->config->settings['ShowDbStructureCharset'], |
373
|
|
|
'show_comment' => $this->config->settings['ShowDbStructureComment'], |
374
|
|
|
'show_creation' => $this->config->settings['ShowDbStructureCreation'], |
375
|
|
|
'show_last_update' => $this->config->settings['ShowDbStructureLastUpdate'], |
376
|
|
|
'show_last_check' => $this->config->settings['ShowDbStructureLastCheck'], |
377
|
|
|
'num_favorite_tables' => $this->config->settings['NumFavoriteTables'], |
378
|
|
|
'structure_table_rows' => $structureTableRows, |
379
|
|
|
]); |
380
|
|
|
$structureTableRows = []; |
381
|
|
|
} |
382
|
|
|
|
383
|
4 |
|
[$approxRows, $showSuperscript] = $this->isRowCountApproximated($currentTable, $tableIsView); |
384
|
|
|
|
385
|
4 |
|
[$do, $ignored] = $this->getReplicationStatus($replicaInfo, $truename); |
386
|
|
|
|
387
|
4 |
|
$structureTableRows[] = [ |
388
|
4 |
|
'table_name_hash' => md5($currentTable['TABLE_NAME']), |
389
|
4 |
|
'db_table_name_hash' => md5(Current::$database . '.' . $currentTable['TABLE_NAME']), |
390
|
4 |
|
'db' => Current::$database, |
391
|
4 |
|
'curr' => $i, |
392
|
4 |
|
'input_class' => implode(' ', $inputClass), |
393
|
4 |
|
'table_is_view' => $tableIsView, |
394
|
4 |
|
'current_table' => $currentTable, |
395
|
4 |
|
'may_have_rows' => $mayHaveRows, |
396
|
4 |
|
'browse_table_label_title' => htmlspecialchars($currentTable['TABLE_COMMENT']), |
397
|
4 |
|
'browse_table_label_truename' => $truename, |
398
|
4 |
|
'empty_table_sql_query' => 'TRUNCATE ' . Util::backquote($currentTable['TABLE_NAME']), |
399
|
4 |
|
'empty_table_message_to_show' => urlencode( |
400
|
4 |
|
sprintf( |
401
|
4 |
|
__('Table %s has been emptied.'), |
402
|
4 |
|
htmlspecialchars( |
403
|
4 |
|
$currentTable['TABLE_NAME'], |
404
|
4 |
|
), |
405
|
4 |
|
), |
406
|
4 |
|
), |
407
|
4 |
|
'tracking_icon' => $this->getTrackingIcon($truename, $trackedTables[$truename] ?? null), |
408
|
4 |
|
'server_replica_status' => $replicaInfo['status'], |
409
|
4 |
|
'table_url_params' => $tableUrlParams, |
410
|
4 |
|
'db_is_system_schema' => $this->dbIsSystemSchema, |
411
|
4 |
|
'drop_query' => $dropQuery, |
412
|
4 |
|
'drop_message' => $dropMessage, |
413
|
4 |
|
'collation' => $collationDefinition, |
414
|
4 |
|
'formatted_size' => $formattedSize, |
415
|
4 |
|
'unit' => $unit, |
416
|
4 |
|
'overhead' => $overhead, |
417
|
4 |
|
'create_time' => $createTime !== null ? Util::localisedDate($createTime) : '-', |
418
|
4 |
|
'update_time' => $updateTime !== null ? Util::localisedDate($updateTime) : '-', |
419
|
4 |
|
'check_time' => $checkTime !== null ? Util::localisedDate($checkTime) : '-', |
420
|
4 |
|
'charset' => $charset ?? '', |
421
|
4 |
|
'is_show_stats' => $this->isShowStats, |
422
|
4 |
|
'ignored' => $ignored, |
423
|
4 |
|
'do' => $do, |
424
|
4 |
|
'approx_rows' => $approxRows, |
425
|
4 |
|
'show_superscript' => $showSuperscript, |
426
|
4 |
|
'already_favorite' => $recentFavoriteTables->contains( |
427
|
4 |
|
new RecentFavoriteTable( |
428
|
4 |
|
DatabaseName::from(Current::$database), |
429
|
4 |
|
TableName::from($currentTable['TABLE_NAME']), |
430
|
4 |
|
), |
431
|
4 |
|
), |
432
|
4 |
|
'num_favorite_tables' => $this->config->settings['NumFavoriteTables'], |
433
|
4 |
|
'properties_num_columns' => $this->config->settings['PropertiesNumColumns'], |
434
|
4 |
|
'limit_chars' => $this->config->settings['LimitChars'], |
435
|
4 |
|
'show_charset' => $this->config->settings['ShowDbStructureCharset'], |
436
|
4 |
|
'show_comment' => $this->config->settings['ShowDbStructureComment'], |
437
|
4 |
|
'show_creation' => $this->config->settings['ShowDbStructureCreation'], |
438
|
4 |
|
'show_last_update' => $this->config->settings['ShowDbStructureLastUpdate'], |
439
|
4 |
|
'show_last_check' => $this->config->settings['ShowDbStructureLastCheck'], |
440
|
4 |
|
]; |
441
|
|
|
|
442
|
4 |
|
$overallApproxRows = $overallApproxRows || $approxRows; |
443
|
|
|
} |
444
|
|
|
|
445
|
4 |
|
$databaseCollation = []; |
446
|
4 |
|
$databaseCharset = ''; |
447
|
4 |
|
$collation = Charsets::findCollationByName( |
448
|
4 |
|
$this->dbi, |
449
|
4 |
|
$this->config->selectedServer['DisableIS'], |
450
|
4 |
|
$this->dbi->getDbCollation(Current::$database), |
451
|
4 |
|
); |
452
|
4 |
|
if ($collation !== null) { |
453
|
|
|
$databaseCollation = ['name' => $collation->getName(), 'description' => $collation->getDescription()]; |
454
|
|
|
$databaseCharset = $collation->getCharset(); |
455
|
|
|
} |
456
|
|
|
|
457
|
4 |
|
$relationParameters = $this->relation->getRelationParameters(); |
458
|
|
|
|
459
|
4 |
|
$defaultStorageEngine = ''; |
460
|
4 |
|
if ($this->config->settings['PropertiesNumColumns'] < 2) { |
461
|
|
|
// MySQL <= 5.5.2 |
462
|
4 |
|
$defaultStorageEngine = $this->dbi->fetchValue('SELECT @@storage_engine;'); |
463
|
4 |
|
if (! is_string($defaultStorageEngine) || $defaultStorageEngine === '') { |
464
|
|
|
// MySQL >= 5.5.3 |
465
|
4 |
|
$defaultStorageEngine = $this->dbi->fetchValue('SELECT @@default_storage_engine;'); |
466
|
|
|
} |
467
|
|
|
} |
468
|
|
|
|
469
|
4 |
|
return $html . $this->template->render('database/structure/table_header', [ |
470
|
4 |
|
'db' => Current::$database, |
471
|
4 |
|
'db_is_system_schema' => $this->dbIsSystemSchema, |
472
|
4 |
|
'replication' => $replicaInfo['status'], |
473
|
4 |
|
'properties_num_columns' => $this->config->settings['PropertiesNumColumns'], |
474
|
4 |
|
'is_show_stats' => $this->isShowStats, |
475
|
4 |
|
'show_charset' => $this->config->settings['ShowDbStructureCharset'], |
476
|
4 |
|
'show_comment' => $this->config->settings['ShowDbStructureComment'], |
477
|
4 |
|
'show_creation' => $this->config->settings['ShowDbStructureCreation'], |
478
|
4 |
|
'show_last_update' => $this->config->settings['ShowDbStructureLastUpdate'], |
479
|
4 |
|
'show_last_check' => $this->config->settings['ShowDbStructureLastCheck'], |
480
|
4 |
|
'num_favorite_tables' => $this->config->settings['NumFavoriteTables'], |
481
|
4 |
|
'structure_table_rows' => $structureTableRows, |
482
|
4 |
|
'body_for_table_summary' => [ |
483
|
4 |
|
'num_tables' => $this->numTables, |
484
|
4 |
|
'server_replica_status' => $replicaInfo['status'], |
485
|
4 |
|
'db_is_system_schema' => $this->dbIsSystemSchema, |
486
|
4 |
|
'sum_entries' => $sumEntries, |
487
|
4 |
|
'database_collation' => $databaseCollation, |
488
|
4 |
|
'is_show_stats' => $this->isShowStats, |
489
|
4 |
|
'database_charset' => $databaseCharset, |
490
|
4 |
|
'sum_size' => $sumSize, |
491
|
4 |
|
'overhead_size' => $overheadSize, |
492
|
4 |
|
'create_time_all' => $createTimeAll !== null ? Util::localisedDate($createTimeAll) : '-', |
493
|
4 |
|
'update_time_all' => $updateTimeAll !== null ? Util::localisedDate($updateTimeAll) : '-', |
494
|
4 |
|
'check_time_all' => $checkTimeAll !== null ? Util::localisedDate($checkTimeAll) : '-', |
495
|
4 |
|
'approx_rows' => $overallApproxRows, |
496
|
4 |
|
'num_favorite_tables' => $this->config->settings['NumFavoriteTables'], |
497
|
4 |
|
'db' => Current::$database, |
498
|
4 |
|
'properties_num_columns' => $this->config->settings['PropertiesNumColumns'], |
499
|
4 |
|
'default_storage_engine' => $defaultStorageEngine, |
500
|
4 |
|
'show_charset' => $this->config->settings['ShowDbStructureCharset'], |
501
|
4 |
|
'show_comment' => $this->config->settings['ShowDbStructureComment'], |
502
|
4 |
|
'show_creation' => $this->config->settings['ShowDbStructureCreation'], |
503
|
4 |
|
'show_last_update' => $this->config->settings['ShowDbStructureLastUpdate'], |
504
|
4 |
|
'show_last_check' => $this->config->settings['ShowDbStructureLastCheck'], |
505
|
4 |
|
], |
506
|
4 |
|
'check_all_tables' => [ |
507
|
4 |
|
'overhead_check' => $overheadCheck, |
508
|
4 |
|
'db_is_system_schema' => $this->dbIsSystemSchema, |
509
|
4 |
|
'hidden_fields' => $hiddenFields, |
510
|
4 |
|
'disable_multi_table' => $this->config->config->DisableMultiTableMaintenance, |
511
|
4 |
|
'central_columns_work' => $relationParameters->centralColumnsFeature !== null, |
512
|
4 |
|
], |
513
|
4 |
|
]); |
514
|
|
|
} |
515
|
|
|
|
516
|
|
|
/** |
517
|
|
|
* Returns the tracking icon if the table is tracked |
518
|
|
|
* |
519
|
|
|
* @return string HTML for tracking icon |
520
|
|
|
*/ |
521
|
4 |
|
private function getTrackingIcon(string $table, TrackedTable|null $trackedTable): string |
522
|
|
|
{ |
523
|
4 |
|
$trackingIcon = ''; |
524
|
4 |
|
if (Tracker::isActive() && $trackedTable !== null) { |
525
|
|
|
$trackingIcon = $this->template->render('database/structure/tracking_icon', [ |
526
|
|
|
'db' => Current::$database, |
527
|
|
|
'table' => $table, |
528
|
|
|
'is_tracked' => $trackedTable->active, |
529
|
|
|
]); |
530
|
|
|
} |
531
|
|
|
|
532
|
4 |
|
return $trackingIcon; |
533
|
|
|
} |
534
|
|
|
|
535
|
|
|
/** |
536
|
|
|
* Returns whether the row count is approximated |
537
|
|
|
* |
538
|
|
|
* @param mixed[] $currentTable array containing details about the table |
539
|
|
|
* @param bool $tableIsView whether the table is a view |
540
|
|
|
* |
541
|
|
|
* @return array{bool, string} |
|
|
|
|
542
|
|
|
*/ |
543
|
4 |
|
private function isRowCountApproximated( |
544
|
|
|
array $currentTable, |
545
|
|
|
bool $tableIsView, |
546
|
|
|
): array { |
547
|
4 |
|
$approxRows = false; |
548
|
4 |
|
$showSuperscript = ''; |
549
|
|
|
|
550
|
|
|
// there is a null value in the ENGINE |
551
|
|
|
// - when the table needs to be repaired, or |
552
|
|
|
// - when it's a view |
553
|
|
|
// so ensure that we'll display "in use" below for a table |
554
|
|
|
// that needs to be repaired |
555
|
4 |
|
if (isset($currentTable['TABLE_ROWS']) && ($currentTable['ENGINE'] != null || $tableIsView)) { |
556
|
|
|
// InnoDB/TokuDB table: we did not get an accurate row count |
557
|
4 |
|
$approxRows = ! $tableIsView |
558
|
4 |
|
&& in_array($currentTable['ENGINE'], ['CSV', 'InnoDB', 'TokuDB'], true) |
559
|
4 |
|
&& ! $currentTable['COUNTED']; |
560
|
|
|
|
561
|
4 |
|
if ($tableIsView && $currentTable['TABLE_ROWS'] >= $this->config->settings['MaxExactCountViews']) { |
562
|
|
|
$approxRows = true; |
563
|
|
|
$showSuperscript = Generator::showHint( |
564
|
|
|
Sanitize::convertBBCode( |
565
|
|
|
sprintf( |
566
|
|
|
__( |
567
|
|
|
'This view has at least this number of rows. Please refer to %sdocumentation%s.', |
568
|
|
|
), |
569
|
|
|
'[doc@cfg_MaxExactCountViews]', |
570
|
|
|
'[/doc]', |
571
|
|
|
), |
572
|
|
|
), |
573
|
|
|
); |
574
|
|
|
} |
575
|
|
|
} |
576
|
|
|
|
577
|
4 |
|
return [$approxRows, $showSuperscript]; |
578
|
|
|
} |
579
|
|
|
|
580
|
|
|
/** |
581
|
|
|
* Returns the replication status of the table. |
582
|
|
|
* |
583
|
|
|
* @param mixed[] $replicaInfo |
584
|
|
|
* @param string $table table name |
585
|
|
|
* |
586
|
|
|
* @return array{bool, bool} |
|
|
|
|
587
|
|
|
*/ |
588
|
4 |
|
private function getReplicationStatus(array $replicaInfo, string $table): array |
589
|
|
|
{ |
590
|
4 |
|
$do = $ignored = false; |
591
|
4 |
|
if ($replicaInfo['status']) { |
592
|
|
|
$nbServReplicaDoDb = count($replicaInfo['Do_DB']); |
593
|
|
|
$nbServReplicaIgnoreDb = count($replicaInfo['Ignore_DB']); |
594
|
|
|
$searchDoDBInTruename = array_search($table, $replicaInfo['Do_DB']); |
595
|
|
|
$searchDoDBInDB = array_search(Current::$database, $replicaInfo['Do_DB']); |
596
|
|
|
|
597
|
|
|
$do = (is_string($searchDoDBInTruename) && $searchDoDBInTruename !== '') |
598
|
|
|
|| (is_string($searchDoDBInDB) && $searchDoDBInDB !== '') |
599
|
|
|
|| ($nbServReplicaDoDb == 0 && $nbServReplicaIgnoreDb == 0) |
600
|
|
|
|| $this->hasTable($replicaInfo['Wild_Do_Table'], $table); |
601
|
|
|
|
602
|
|
|
$searchDb = array_search(Current::$database, $replicaInfo['Ignore_DB']); |
603
|
|
|
$searchTable = array_search($table, $replicaInfo['Ignore_Table']); |
604
|
|
|
$ignored = (is_string($searchTable) && $searchTable !== '') |
605
|
|
|
|| (is_string($searchDb) && $searchDb !== '') |
606
|
|
|
|| $this->hasTable($replicaInfo['Wild_Ignore_Table'], $table); |
607
|
|
|
} |
608
|
|
|
|
609
|
4 |
|
return [$do, $ignored]; |
610
|
|
|
} |
611
|
|
|
|
612
|
|
|
/** |
613
|
|
|
* Find table with truename |
614
|
|
|
* |
615
|
|
|
* @param mixed[] $db DB to look into |
616
|
|
|
* @param string $truename Table name |
617
|
|
|
*/ |
618
|
4 |
|
private function hasTable(array $db, string $truename): bool |
619
|
|
|
{ |
620
|
4 |
|
foreach ($db as $dbTable) { |
621
|
|
|
if ( |
622
|
4 |
|
Current::$database === $this->replication->extractDbOrTable($dbTable) |
623
|
4 |
|
&& preg_match( |
624
|
4 |
|
'@^' . |
625
|
4 |
|
preg_quote(mb_substr($this->replication->extractDbOrTable($dbTable, 'table'), 0, -1), '@') . '@', |
626
|
4 |
|
$truename, |
627
|
4 |
|
) === 1 |
628
|
|
|
) { |
629
|
4 |
|
return true; |
630
|
|
|
} |
631
|
|
|
} |
632
|
|
|
|
633
|
4 |
|
return false; |
634
|
|
|
} |
635
|
|
|
|
636
|
|
|
/** |
637
|
|
|
* Get the value set for ENGINE table, |
638
|
|
|
* |
639
|
|
|
* @internal param bool $table_is_view whether table is view or not |
640
|
|
|
* |
641
|
|
|
* @param mixed[] $currentTable current table |
642
|
|
|
* @param int $sumSize total table size |
643
|
|
|
* @param int $overheadSize overhead size |
644
|
|
|
* |
645
|
|
|
* @psalm-return list{mixed[], string, string, string, string, int, bool, int} |
646
|
|
|
*/ |
647
|
4 |
|
private function getStuffForEngineTypeTable( |
648
|
|
|
array $currentTable, |
649
|
|
|
int $sumSize, |
650
|
|
|
int $overheadSize, |
651
|
|
|
): array { |
652
|
4 |
|
$formattedSize = '-'; |
653
|
4 |
|
$unit = ''; |
654
|
4 |
|
$formattedOverhead = ''; |
655
|
4 |
|
$overheadUnit = ''; |
656
|
4 |
|
$tableIsView = false; |
657
|
|
|
|
658
|
4 |
|
switch ($currentTable['ENGINE']) { |
659
|
|
|
// MyISAM, ISAM or Heap table: Row count, data size and index size |
660
|
|
|
// are accurate; data size is accurate for ARCHIVE |
661
|
4 |
|
case 'MyISAM': |
662
|
4 |
|
case 'ISAM': |
663
|
4 |
|
case 'HEAP': |
664
|
4 |
|
case 'MEMORY': |
665
|
4 |
|
case 'ARCHIVE': |
666
|
4 |
|
case 'Aria': |
667
|
4 |
|
case 'Maria': |
668
|
4 |
|
[ |
669
|
4 |
|
$currentTable, |
670
|
4 |
|
$formattedSize, |
671
|
4 |
|
$unit, |
672
|
4 |
|
$formattedOverhead, |
673
|
4 |
|
$overheadUnit, |
674
|
4 |
|
$overheadSize, |
675
|
4 |
|
$sumSize, |
676
|
4 |
|
] = $this->getValuesForAriaTable( |
677
|
4 |
|
$currentTable, |
678
|
4 |
|
$sumSize, |
679
|
4 |
|
$overheadSize, |
680
|
4 |
|
$formattedSize, |
681
|
4 |
|
$unit, |
682
|
4 |
|
$formattedOverhead, |
683
|
4 |
|
$overheadUnit, |
684
|
4 |
|
); |
685
|
4 |
|
break; |
686
|
|
|
case 'InnoDB': |
687
|
|
|
case 'PBMS': |
688
|
|
|
case 'TokuDB': |
689
|
|
|
case 'ROCKSDB': |
690
|
|
|
// InnoDB table: Row count is not accurate but data and index sizes are. |
691
|
|
|
// PBMS table in Drizzle: TABLE_ROWS is taken from table cache, |
692
|
|
|
// so it may be unavailable |
693
|
|
|
[$currentTable, $formattedSize, $unit, $sumSize] = $this->getValuesForInnodbTable( |
694
|
|
|
$currentTable, |
695
|
|
|
$sumSize, |
696
|
|
|
); |
697
|
|
|
break; |
698
|
|
|
case 'CSV': |
699
|
|
|
[$currentTable, $formattedSize, $unit, $sumSize] = $this->getValuesForCsvTable($currentTable, $sumSize); |
700
|
|
|
break; |
701
|
|
|
// Mysql 5.0.x (and lower) uses MRG_MyISAM |
702
|
|
|
// and MySQL 5.1.x (and higher) uses MRG_MYISAM |
703
|
|
|
// Both are aliases for MERGE |
704
|
|
|
case 'MRG_MyISAM': |
705
|
|
|
case 'MRG_MYISAM': |
706
|
|
|
case 'MERGE': |
707
|
|
|
case 'BerkeleyDB': |
708
|
|
|
// Merge or BerkleyDB table: Only row count is accurate. |
709
|
|
|
if ($this->isShowStats) { |
710
|
|
|
$formattedSize = ' - '; |
711
|
|
|
} |
712
|
|
|
|
713
|
|
|
break; |
714
|
|
|
// for a view, the ENGINE is sometimes reported as null, |
715
|
|
|
// or on some servers it's reported as "SYSTEM VIEW" |
716
|
|
|
case null: |
717
|
|
|
case 'SYSTEM VIEW': |
718
|
|
|
// possibly a view, do nothing |
719
|
|
|
break; |
720
|
|
|
case 'Mroonga': |
721
|
|
|
// The idea is to show the size only if Mroonga is available, |
722
|
|
|
// in other case the old unknown message will appear |
723
|
|
|
if (StorageEngine::hasMroongaEngine()) { |
724
|
|
|
[$currentTable, $formattedSize, $unit, $sumSize] = $this->getValuesForMroongaTable( |
725
|
|
|
$currentTable, |
726
|
|
|
$sumSize, |
727
|
|
|
); |
728
|
|
|
break; |
729
|
|
|
} |
730
|
|
|
// no break, go to default case |
731
|
|
|
default: |
732
|
|
|
// Unknown table type. |
733
|
|
|
if ($this->isShowStats) { |
734
|
|
|
$formattedSize = __('unknown'); |
735
|
|
|
} |
736
|
|
|
} |
737
|
|
|
|
738
|
4 |
|
if ($currentTable['TABLE_TYPE'] === 'VIEW' || $currentTable['TABLE_TYPE'] === 'SYSTEM VIEW') { |
739
|
|
|
// countRecords() takes care of $cfg['MaxExactCountViews'] |
740
|
|
|
$currentTable['TABLE_ROWS'] = $this->dbi |
741
|
|
|
->getTable(Current::$database, $currentTable['TABLE_NAME']) |
742
|
|
|
->countRecords(true); |
743
|
|
|
$tableIsView = true; |
744
|
|
|
} |
745
|
|
|
|
746
|
4 |
|
return [ |
747
|
4 |
|
$currentTable, |
748
|
4 |
|
$formattedSize, |
749
|
4 |
|
$unit, |
750
|
4 |
|
$formattedOverhead, |
751
|
4 |
|
$overheadUnit, |
752
|
4 |
|
$overheadSize, |
753
|
4 |
|
$tableIsView, |
754
|
4 |
|
$sumSize, |
755
|
4 |
|
]; |
756
|
|
|
} |
757
|
|
|
|
758
|
|
|
/** |
759
|
|
|
* Get values for ARIA/MARIA tables |
760
|
|
|
* |
761
|
|
|
* @param mixed[] $currentTable current table |
762
|
|
|
* @param int $sumSize sum size |
763
|
|
|
* @param int $overheadSize overhead size |
764
|
|
|
* @param string $formattedSize formatted size |
765
|
|
|
* @param string $unit unit |
766
|
|
|
* @param string $formattedOverhead overhead formatted |
767
|
|
|
* @param string $overheadUnit overhead unit |
768
|
|
|
* |
769
|
|
|
* @return mixed[] |
770
|
|
|
*/ |
771
|
8 |
|
private function getValuesForAriaTable( |
772
|
|
|
array $currentTable, |
773
|
|
|
int $sumSize, |
774
|
|
|
int $overheadSize, |
775
|
|
|
string $formattedSize, |
776
|
|
|
string $unit, |
777
|
|
|
string $formattedOverhead, |
778
|
|
|
string $overheadUnit, |
779
|
|
|
): array { |
780
|
8 |
|
if ($this->dbIsSystemSchema) { |
781
|
4 |
|
$currentTable['Rows'] = $this->dbi |
782
|
4 |
|
->getTable(Current::$database, $currentTable['Name']) |
783
|
4 |
|
->countRecords(); |
784
|
|
|
} |
785
|
|
|
|
786
|
8 |
|
if ($this->isShowStats) { |
787
|
|
|
/** @var int $tblsize */ |
788
|
8 |
|
$tblsize = $currentTable['Data_length'] |
789
|
8 |
|
+ $currentTable['Index_length']; |
790
|
8 |
|
$sumSize += $tblsize; |
791
|
8 |
|
[$formattedSize, $unit] = Util::formatByteDown($tblsize, 3, $tblsize > 0 ? 1 : 0); |
792
|
8 |
|
if (isset($currentTable['Data_free']) && $currentTable['Data_free'] > 0) { |
793
|
8 |
|
[$formattedOverhead, $overheadUnit] = Util::formatByteDown($currentTable['Data_free'], 3, 1); |
794
|
8 |
|
$overheadSize += $currentTable['Data_free']; |
795
|
|
|
} |
796
|
|
|
} |
797
|
|
|
|
798
|
8 |
|
return [$currentTable, $formattedSize, $unit, $formattedOverhead, $overheadUnit, $overheadSize, $sumSize]; |
799
|
|
|
} |
800
|
|
|
|
801
|
|
|
/** |
802
|
|
|
* Get values for InnoDB table |
803
|
|
|
* |
804
|
|
|
* @param mixed[] $currentTable current table |
805
|
|
|
* @param int $sumSize sum size |
806
|
|
|
* |
807
|
|
|
* @return mixed[] |
808
|
|
|
*/ |
809
|
4 |
|
private function getValuesForInnodbTable( |
810
|
|
|
array $currentTable, |
811
|
|
|
int $sumSize, |
812
|
|
|
): array { |
813
|
4 |
|
$formattedSize = $unit = ''; |
814
|
|
|
|
815
|
|
|
if ( |
816
|
4 |
|
(in_array($currentTable['ENGINE'], ['InnoDB', 'TokuDB'], true) |
817
|
4 |
|
&& $currentTable['TABLE_ROWS'] < $this->config->settings['MaxExactCount']) |
818
|
4 |
|
|| ! isset($currentTable['TABLE_ROWS']) |
819
|
|
|
) { |
820
|
4 |
|
$currentTable['COUNTED'] = true; |
821
|
4 |
|
$currentTable['TABLE_ROWS'] = $this->dbi |
822
|
4 |
|
->getTable(Current::$database, $currentTable['TABLE_NAME']) |
823
|
4 |
|
->countRecords(true); |
824
|
|
|
} else { |
825
|
4 |
|
$currentTable['COUNTED'] = false; |
826
|
|
|
} |
827
|
|
|
|
828
|
4 |
|
if ($this->isShowStats) { |
829
|
|
|
/** @var int $tblsize */ |
830
|
4 |
|
$tblsize = $currentTable['Data_length'] |
831
|
4 |
|
+ $currentTable['Index_length']; |
832
|
4 |
|
$sumSize += $tblsize; |
833
|
4 |
|
[$formattedSize, $unit] = Util::formatByteDown($tblsize, 3, $tblsize > 0 ? 1 : 0); |
834
|
|
|
} |
835
|
|
|
|
836
|
4 |
|
return [$currentTable, $formattedSize, $unit, $sumSize]; |
837
|
|
|
} |
838
|
|
|
|
839
|
|
|
/** |
840
|
|
|
* Get values for CSV table |
841
|
|
|
* |
842
|
|
|
* https://bugs.mysql.com/bug.php?id=53929 |
843
|
|
|
* |
844
|
|
|
* @param mixed[] $currentTable |
845
|
|
|
* |
846
|
|
|
* @return mixed[] |
847
|
|
|
*/ |
848
|
|
|
private function getValuesForCsvTable(array $currentTable, int $sumSize): array |
849
|
|
|
{ |
850
|
|
|
$formattedSize = $unit = ''; |
851
|
|
|
|
852
|
|
|
if ($currentTable['ENGINE'] === 'CSV') { |
853
|
|
|
$currentTable['COUNTED'] = true; |
854
|
|
|
$currentTable['TABLE_ROWS'] = $this->dbi |
855
|
|
|
->getTable(Current::$database, $currentTable['TABLE_NAME']) |
856
|
|
|
->countRecords(true); |
857
|
|
|
} else { |
858
|
|
|
$currentTable['COUNTED'] = false; |
859
|
|
|
} |
860
|
|
|
|
861
|
|
|
if ($this->isShowStats) { |
862
|
|
|
// Only count columns that have double quotes |
863
|
|
|
$columnCount = (int) $this->dbi->fetchValue( |
864
|
|
|
'SELECT COUNT(COLUMN_NAME) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = ' |
865
|
|
|
. $this->dbi->quoteString(Current::$database) . ' AND TABLE_NAME = ' |
866
|
|
|
. $this->dbi->quoteString($currentTable['TABLE_NAME']) . ' AND NUMERIC_SCALE IS NULL;', |
867
|
|
|
); |
868
|
|
|
|
869
|
|
|
// Get column names |
870
|
|
|
$columnNames = $this->dbi->fetchValue( |
871
|
|
|
'SELECT GROUP_CONCAT(COLUMN_NAME) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = ' |
872
|
|
|
. $this->dbi->quoteString(Current::$database) . ' AND TABLE_NAME = ' |
873
|
|
|
. $this->dbi->quoteString($currentTable['TABLE_NAME']) . ';', |
874
|
|
|
); |
875
|
|
|
|
876
|
|
|
// 10Mb buffer for CONCAT_WS |
877
|
|
|
// not sure if is needed |
878
|
|
|
$this->dbi->query('SET SESSION group_concat_max_len = 10 * 1024 * 1024'); |
879
|
|
|
|
880
|
|
|
// Calculate data length |
881
|
|
|
$dataLength = (int) $this->dbi->fetchValue(' |
882
|
|
|
SELECT SUM(CHAR_LENGTH(REPLACE(REPLACE(REPLACE( |
883
|
|
|
CONCAT_WS(\',\', ' . $columnNames . '), |
|
|
|
|
884
|
|
|
UNHEX(\'0A\'), \'nn\'), UNHEX(\'22\'), \'nn\'), UNHEX(\'5C\'), \'nn\' |
885
|
|
|
))) FROM ' . Util::backquote(Current::$database) . '.' . Util::backquote($currentTable['TABLE_NAME'])); |
886
|
|
|
|
887
|
|
|
// Calculate quotes length |
888
|
|
|
$quotesLength = $currentTable['TABLE_ROWS'] * $columnCount * 2; |
889
|
|
|
|
890
|
|
|
/** @var int $tblsize */ |
891
|
|
|
$tblsize = $dataLength + $quotesLength + $currentTable['TABLE_ROWS']; |
892
|
|
|
|
893
|
|
|
$sumSize += $tblsize; |
894
|
|
|
[$formattedSize, $unit] = Util::formatByteDown($tblsize, 3, $tblsize > 0 ? 1 : 0); |
895
|
|
|
} |
896
|
|
|
|
897
|
|
|
return [$currentTable, $formattedSize, $unit, $sumSize]; |
898
|
|
|
} |
899
|
|
|
|
900
|
|
|
/** |
901
|
|
|
* Get values for Mroonga table |
902
|
|
|
* |
903
|
|
|
* @param mixed[] $currentTable current table |
904
|
|
|
* @param int $sumSize sum size |
905
|
|
|
* |
906
|
|
|
* @return mixed[] |
907
|
|
|
*/ |
908
|
4 |
|
private function getValuesForMroongaTable( |
909
|
|
|
array $currentTable, |
910
|
|
|
int $sumSize, |
911
|
|
|
): array { |
912
|
4 |
|
$formattedSize = ''; |
913
|
4 |
|
$unit = ''; |
914
|
|
|
|
915
|
4 |
|
if ($this->isShowStats) { |
916
|
|
|
/** @var int $tblsize */ |
917
|
4 |
|
$tblsize = $currentTable['Data_length'] + $currentTable['Index_length']; |
918
|
4 |
|
$sumSize += $tblsize; |
919
|
4 |
|
[$formattedSize, $unit] = Util::formatByteDown($tblsize, 3, $tblsize > 0 ? 1 : 0); |
920
|
|
|
} |
921
|
|
|
|
922
|
4 |
|
return [$currentTable, $formattedSize, $unit, $sumSize]; |
923
|
|
|
} |
924
|
|
|
|
925
|
|
|
private function createDateTime(mixed $dateTime): DateTimeImmutable|null |
926
|
|
|
{ |
927
|
|
|
if (! is_string($dateTime) || $dateTime === '') { |
928
|
|
|
return null; |
929
|
|
|
} |
930
|
|
|
|
931
|
|
|
try { |
932
|
|
|
return new DateTimeImmutable($dateTime); |
933
|
|
|
} catch (Throwable) { |
934
|
|
|
return null; |
935
|
|
|
} |
936
|
|
|
} |
937
|
|
|
} |
938
|
|
|
|
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