1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
declare(strict_types=1); |
4
|
|
|
|
5
|
|
|
namespace PhpMyAdmin\Database; |
6
|
|
|
|
7
|
|
|
use PhpMyAdmin\Charsets; |
8
|
|
|
use PhpMyAdmin\ColumnFull; |
9
|
|
|
use PhpMyAdmin\Config; |
10
|
|
|
use PhpMyAdmin\ConfigStorage\Relation; |
11
|
|
|
use PhpMyAdmin\DatabaseInterface; |
12
|
|
|
use PhpMyAdmin\Dbal\ConnectionType; |
13
|
|
|
use PhpMyAdmin\Identifiers\DatabaseName; |
|
|
|
|
14
|
|
|
use PhpMyAdmin\Message; |
15
|
|
|
use PhpMyAdmin\Template; |
16
|
|
|
use PhpMyAdmin\Util; |
17
|
|
|
|
18
|
|
|
use function __; |
19
|
|
|
use function array_column; |
20
|
|
|
use function array_diff; |
21
|
|
|
use function array_map; |
22
|
|
|
use function array_merge; |
23
|
|
|
use function array_unique; |
24
|
|
|
use function array_values; |
25
|
|
|
use function bin2hex; |
26
|
|
|
use function ceil; |
27
|
|
|
use function count; |
28
|
|
|
use function explode; |
29
|
|
|
use function htmlspecialchars; |
30
|
|
|
use function implode; |
31
|
|
|
use function in_array; |
32
|
|
|
use function is_array; |
33
|
|
|
use function is_bool; |
34
|
|
|
use function mb_strtoupper; |
35
|
|
|
use function sprintf; |
36
|
|
|
use function trim; |
37
|
|
|
|
38
|
|
|
class CentralColumns |
39
|
|
|
{ |
40
|
|
|
/** |
41
|
|
|
* Current user |
42
|
|
|
*/ |
43
|
|
|
private string $user; |
44
|
|
|
|
45
|
|
|
/** |
46
|
|
|
* Number of rows displayed when browsing a result set |
47
|
|
|
*/ |
48
|
|
|
private int $maxRows; |
49
|
|
|
|
50
|
|
|
/** |
51
|
|
|
* Which editor should be used for CHAR/VARCHAR fields |
52
|
|
|
*/ |
53
|
|
|
private string $charEditing; |
54
|
|
|
|
55
|
|
|
/** |
56
|
|
|
* Disable use of INFORMATION_SCHEMA |
57
|
|
|
*/ |
58
|
|
|
private bool $disableIs; |
59
|
|
|
|
60
|
|
|
private Relation $relation; |
61
|
|
|
|
62
|
|
|
public Template $template; |
63
|
|
|
|
64
|
48 |
|
public function __construct(private DatabaseInterface $dbi) |
65
|
|
|
{ |
66
|
48 |
|
$config = Config::getInstance(); |
|
|
|
|
67
|
48 |
|
$this->user = $config->selectedServer['user']; |
68
|
48 |
|
$this->maxRows = $config->settings['MaxRows']; |
69
|
48 |
|
$this->charEditing = $config->settings['CharEditing']; |
70
|
48 |
|
$this->disableIs = $config->selectedServer['DisableIS']; |
71
|
|
|
|
72
|
48 |
|
$this->relation = new Relation($this->dbi); |
73
|
48 |
|
$this->template = new Template(); |
74
|
|
|
} |
75
|
|
|
|
76
|
|
|
/** |
77
|
|
|
* Returns the configuration storage settings for central columns or false if no storage is available |
78
|
|
|
* |
79
|
|
|
* @return array{user:string, db: string, table:string}|false |
80
|
|
|
*/ |
81
|
48 |
|
public function getParams(): array|false |
82
|
|
|
{ |
83
|
48 |
|
$centralColumnsFeature = $this->relation->getRelationParameters()->centralColumnsFeature; |
84
|
48 |
|
if ($centralColumnsFeature === null) { |
85
|
|
|
return false; |
86
|
|
|
} |
87
|
|
|
|
88
|
48 |
|
return [ |
89
|
48 |
|
'user' => $this->user, |
90
|
48 |
|
'db' => $centralColumnsFeature->database->getName(), |
91
|
48 |
|
'table' => $centralColumnsFeature->centralColumns->getName(), |
92
|
48 |
|
]; |
93
|
|
|
} |
94
|
|
|
|
95
|
|
|
/** |
96
|
|
|
* get $num columns of given database from central columns list |
97
|
|
|
* starting at offset $from |
98
|
|
|
* |
99
|
|
|
* @param string $db selected database |
100
|
|
|
* @param int $from starting offset of first result |
101
|
|
|
* @param int $num maximum number of results to return |
102
|
|
|
* |
103
|
|
|
* @return mixed[] list of $num columns present in central columns list |
104
|
|
|
* starting at offset $from for the given database |
105
|
|
|
*/ |
106
|
4 |
|
public function getColumnsList(string $db, int $from = 0, int $num = 25): array |
107
|
|
|
{ |
108
|
4 |
|
$cfgCentralColumns = $this->getParams(); |
109
|
4 |
|
if (! is_array($cfgCentralColumns)) { |
110
|
|
|
return []; |
111
|
|
|
} |
112
|
|
|
|
113
|
4 |
|
$pmadb = $cfgCentralColumns['db']; |
114
|
4 |
|
$centralListTable = $cfgCentralColumns['table']; |
115
|
|
|
//get current values of $db from central column list |
116
|
4 |
|
if ($num === 0) { |
117
|
|
|
$query = 'SELECT * FROM ' . Util::backquote($pmadb) . '.' . Util::backquote($centralListTable) . ' ' |
118
|
|
|
. 'WHERE db_name = ' . $this->dbi->quoteString($db, ConnectionType::ControlUser) . ';'; |
119
|
|
|
} else { |
120
|
4 |
|
$query = 'SELECT * FROM ' . Util::backquote($pmadb) . '.' . Util::backquote($centralListTable) . ' ' |
121
|
4 |
|
. 'WHERE db_name = ' . $this->dbi->quoteString($db, ConnectionType::ControlUser) . ' ' |
122
|
4 |
|
. 'LIMIT ' . $from . ', ' . $num . ';'; |
123
|
|
|
} |
124
|
|
|
|
125
|
4 |
|
$hasList = $this->dbi->fetchResult($query, null, null, ConnectionType::ControlUser); |
126
|
4 |
|
$this->handleColumnExtra($hasList); |
127
|
|
|
|
128
|
4 |
|
return $hasList; |
129
|
|
|
} |
130
|
|
|
|
131
|
|
|
/** |
132
|
|
|
* Get the number of columns present in central list for given db |
133
|
|
|
* |
134
|
|
|
* @param string $db current database |
135
|
|
|
* |
136
|
|
|
* @return int number of columns in central list of columns for $db |
137
|
|
|
*/ |
138
|
4 |
|
public function getCount(string $db): int |
139
|
|
|
{ |
140
|
4 |
|
$cfgCentralColumns = $this->getParams(); |
141
|
4 |
|
if (! is_array($cfgCentralColumns)) { |
142
|
|
|
return 0; |
143
|
|
|
} |
144
|
|
|
|
145
|
4 |
|
$pmadb = $cfgCentralColumns['db']; |
146
|
4 |
|
$centralListTable = $cfgCentralColumns['table']; |
147
|
4 |
|
$query = 'SELECT count(db_name) FROM ' |
148
|
4 |
|
. Util::backquote($pmadb) . '.' . Util::backquote($centralListTable) . ' ' |
149
|
4 |
|
. 'WHERE db_name = ' . $this->dbi->quoteString($db, ConnectionType::ControlUser) . ';'; |
150
|
4 |
|
$res = $this->dbi->fetchResult($query, null, null, ConnectionType::ControlUser); |
151
|
4 |
|
if (isset($res[0])) { |
152
|
4 |
|
return (int) $res[0]; |
153
|
|
|
} |
154
|
|
|
|
155
|
|
|
return 0; |
156
|
|
|
} |
157
|
|
|
|
158
|
|
|
/** |
159
|
|
|
* return the existing columns in central list among the given list of columns |
160
|
|
|
* |
161
|
|
|
* @param string $db the selected database |
162
|
|
|
* @param string[] $cols list of given columns |
163
|
|
|
* |
164
|
|
|
* @return string[] list of columns in central columns among given set of columns |
165
|
|
|
*/ |
166
|
8 |
|
public function findExistingColNames( |
167
|
|
|
string $db, |
168
|
|
|
array $cols, |
169
|
|
|
): array { |
170
|
8 |
|
$cfgCentralColumns = $this->getParams(); |
171
|
8 |
|
if (! is_array($cfgCentralColumns)) { |
172
|
|
|
return []; |
173
|
|
|
} |
174
|
|
|
|
175
|
8 |
|
$cols = $this->getWhereInColumns($cols); |
176
|
|
|
|
177
|
8 |
|
$pmadb = $cfgCentralColumns['db']; |
178
|
8 |
|
$centralListTable = $cfgCentralColumns['table']; |
179
|
8 |
|
$query = 'SELECT col_name FROM ' |
180
|
8 |
|
. Util::backquote($pmadb) . '.' . Util::backquote($centralListTable) . ' WHERE db_name = ' |
181
|
8 |
|
. $this->dbi->quoteString($db, ConnectionType::ControlUser) . ' AND col_name IN (' . $cols . ');'; |
182
|
|
|
|
183
|
8 |
|
return $this->dbi->fetchResult($query, null, null, ConnectionType::ControlUser); |
|
|
|
|
184
|
|
|
} |
185
|
|
|
|
186
|
|
|
/** |
187
|
|
|
* return the existing columns in central list among the given list of columns |
188
|
|
|
* |
189
|
|
|
* @param string $db the selected database |
190
|
|
|
* @param string[] $cols list of given columns |
191
|
|
|
* |
192
|
|
|
* @return (string|null)[][] list of columns in central columns among given set of columns |
193
|
|
|
*/ |
194
|
12 |
|
private function findExistingColumns( |
195
|
|
|
string $db, |
196
|
|
|
array $cols, |
197
|
|
|
): array { |
198
|
12 |
|
$cfgCentralColumns = $this->getParams(); |
199
|
12 |
|
if (! is_array($cfgCentralColumns)) { |
200
|
|
|
return []; |
201
|
|
|
} |
202
|
|
|
|
203
|
12 |
|
$cols = $this->getWhereInColumns($cols); |
204
|
|
|
|
205
|
12 |
|
$pmadb = $cfgCentralColumns['db']; |
206
|
12 |
|
$centralListTable = $cfgCentralColumns['table']; |
207
|
12 |
|
$query = 'SELECT * FROM ' |
208
|
12 |
|
. Util::backquote($pmadb) . '.' . Util::backquote($centralListTable) . ' WHERE db_name = ' |
209
|
12 |
|
. $this->dbi->quoteString($db, ConnectionType::ControlUser) . ' AND col_name IN (' . $cols . ');'; |
210
|
12 |
|
$hasList = $this->dbi->fetchResult($query, null, null, ConnectionType::ControlUser); |
211
|
12 |
|
$this->handleColumnExtra($hasList); |
212
|
|
|
|
213
|
12 |
|
return $hasList; |
214
|
|
|
} |
215
|
|
|
|
216
|
|
|
/** |
217
|
|
|
* build the insert query for central columns list given PMA storage |
218
|
|
|
* db, central_columns table, column name and corresponding definition to be added |
219
|
|
|
* |
220
|
|
|
* @param ColumnFull $def list of attributes of the column being added |
221
|
|
|
* @param string $db PMA configuration storage database name |
222
|
|
|
* @param string $centralListTable central columns configuration storage table name |
223
|
|
|
* |
224
|
|
|
* @return string query string to insert the given column |
225
|
|
|
* with definition into central list |
226
|
|
|
*/ |
227
|
8 |
|
private function getInsertQuery( |
228
|
|
|
ColumnFull $def, |
229
|
|
|
string $db, |
230
|
|
|
string $centralListDb, |
231
|
|
|
string $centralListTable, |
232
|
|
|
): string { |
233
|
8 |
|
$extractedColumnSpec = Util::extractColumnSpec($def->type); |
234
|
8 |
|
$attribute = trim($extractedColumnSpec['attribute']); |
235
|
8 |
|
$type = $extractedColumnSpec['type']; |
236
|
8 |
|
$length = $extractedColumnSpec['spec_in_brackets']; |
237
|
|
|
|
238
|
8 |
|
$collation = $def->collation ?? ''; |
239
|
8 |
|
$isNull = $def->isNull ? '1' : '0'; |
240
|
8 |
|
$extra = $def->extra; |
241
|
8 |
|
$default = $def->default ?? ''; |
242
|
|
|
|
243
|
8 |
|
return 'INSERT INTO ' |
244
|
8 |
|
. Util::backquote($centralListDb) . '.' . Util::backquote($centralListTable) . ' ' |
245
|
8 |
|
. 'VALUES ( ' . $this->dbi->quoteString($db, ConnectionType::ControlUser) . ' ,' |
246
|
8 |
|
. $this->dbi->quoteString($def->field, ConnectionType::ControlUser) . ',' |
247
|
8 |
|
. $this->dbi->quoteString($type, ConnectionType::ControlUser) . ',' |
248
|
8 |
|
. $this->dbi->quoteString((string) $length, ConnectionType::ControlUser) . ',' |
249
|
8 |
|
. $this->dbi->quoteString($collation, ConnectionType::ControlUser) . ',' |
250
|
8 |
|
. $this->dbi->quoteString($isNull, ConnectionType::ControlUser) . ',' |
251
|
8 |
|
. '\'' . implode(',', [$extra, $attribute]) |
252
|
8 |
|
. '\',' . $this->dbi->quoteString($default, ConnectionType::ControlUser) . ');'; |
253
|
|
|
} |
254
|
|
|
|
255
|
|
|
/** |
256
|
|
|
* If $isTable is true then unique columns from given tables as $field_select |
257
|
|
|
* are added to central list otherwise the $field_select is considered as |
258
|
|
|
* list of columns and these columns are added to central list if not already added |
259
|
|
|
* |
260
|
|
|
* @param string[] $fieldSelect if $isTable is true selected tables list otherwise selected columns list |
261
|
|
|
* @param bool $isTable if passed array is of tables or columns |
262
|
|
|
* @param string $containingTable if $isTable is false, then table name to which columns belong |
263
|
|
|
* |
264
|
|
|
* @return true|Message |
265
|
|
|
*/ |
266
|
4 |
|
public function syncUniqueColumns( |
267
|
|
|
DatabaseName $databaseName, |
268
|
|
|
array $fieldSelect, |
269
|
|
|
bool $isTable = true, |
270
|
|
|
string $containingTable = '', |
271
|
|
|
): bool|Message { |
272
|
4 |
|
$cfgCentralColumns = $this->getParams(); |
273
|
4 |
|
if (! is_array($cfgCentralColumns)) { |
274
|
|
|
return $this->getStorageNotReadyMessage(); |
275
|
|
|
} |
276
|
|
|
|
277
|
4 |
|
$existingCols = []; |
278
|
4 |
|
$insQuery = []; |
279
|
4 |
|
if ($isTable) { |
280
|
4 |
|
$cols = []; |
281
|
4 |
|
$fields = []; |
282
|
4 |
|
foreach ($fieldSelect as $table) { |
283
|
4 |
|
$fields[$table] = $this->dbi->getColumns($databaseName->getName(), $table, true); |
284
|
4 |
|
$cols = array_merge($cols, array_column($fields[$table], 'field')); |
285
|
|
|
} |
286
|
|
|
|
287
|
4 |
|
$hasList = $this->findExistingColNames($databaseName->getName(), $cols); |
288
|
4 |
|
foreach ($fieldSelect as $table) { |
289
|
4 |
|
foreach ($fields[$table] as $def) { |
290
|
4 |
|
$field = $def->field; |
291
|
4 |
|
if (! in_array($field, $hasList, true)) { |
292
|
4 |
|
$hasList[] = $field; |
293
|
4 |
|
$insQuery[] = $this->getInsertQuery( |
294
|
4 |
|
$def, |
295
|
4 |
|
$databaseName->getName(), |
296
|
4 |
|
$cfgCentralColumns['db'], |
297
|
4 |
|
$cfgCentralColumns['table'], |
298
|
4 |
|
); |
299
|
|
|
} else { |
300
|
|
|
$existingCols[] = "'" . $field . "'"; |
301
|
|
|
} |
302
|
|
|
} |
303
|
|
|
} |
304
|
|
|
} else { |
305
|
|
|
$hasList = $this->findExistingColNames($databaseName->getName(), $fieldSelect); |
306
|
|
|
foreach ($fieldSelect as $column) { |
307
|
|
|
if (! in_array($column, $hasList, true)) { |
308
|
|
|
$hasList[] = $column; |
309
|
|
|
$field = $this->dbi->getColumn($databaseName->getName(), $containingTable, $column, true); |
310
|
|
|
$insQuery[] = $this->getInsertQuery( |
311
|
|
|
$field, |
|
|
|
|
312
|
|
|
$databaseName->getName(), |
313
|
|
|
$cfgCentralColumns['db'], |
314
|
|
|
$cfgCentralColumns['table'], |
315
|
|
|
); |
316
|
|
|
} else { |
317
|
|
|
$existingCols[] = "'" . $column . "'"; |
318
|
|
|
} |
319
|
|
|
} |
320
|
|
|
} |
321
|
|
|
|
322
|
4 |
|
$message = true; |
323
|
4 |
|
if ($existingCols !== []) { |
324
|
|
|
$existingCols = implode(',', array_unique($existingCols)); |
325
|
|
|
$message = Message::notice( |
326
|
|
|
sprintf( |
327
|
|
|
__( |
328
|
|
|
'Could not add %1$s as they already exist in central list!', |
329
|
|
|
), |
330
|
|
|
htmlspecialchars($existingCols), |
331
|
|
|
), |
332
|
|
|
); |
333
|
|
|
$message->addMessage( |
334
|
|
|
Message::notice( |
335
|
|
|
'Please remove them first from central list if you want to update above columns', |
336
|
|
|
), |
337
|
|
|
); |
338
|
|
|
} |
339
|
|
|
|
340
|
4 |
|
foreach ($insQuery as $query) { |
341
|
4 |
|
if (! $this->dbi->tryQuery($query, ConnectionType::ControlUser)) { |
342
|
|
|
$message = Message::error(__('Could not add columns!')); |
343
|
|
|
$message->addMessage( |
344
|
|
|
Message::rawError($this->dbi->getError(ConnectionType::ControlUser)), |
345
|
|
|
); |
346
|
|
|
|
347
|
|
|
return $message; |
348
|
|
|
} |
349
|
|
|
} |
350
|
|
|
|
351
|
4 |
|
return $message; |
352
|
|
|
} |
353
|
|
|
|
354
|
|
|
/** |
355
|
|
|
* if $isTable is true it removes all columns of given tables as $field_select from |
356
|
|
|
* central columns list otherwise $field_select is columns list and it removes |
357
|
|
|
* given columns if present in central list |
358
|
|
|
* |
359
|
|
|
* @param string $database Database name |
360
|
|
|
* @param string[] $fieldSelect if $isTable selected list of tables otherwise |
361
|
|
|
* selected list of columns to remove from central list |
362
|
|
|
* @param bool $isTable if passed array is of tables or columns |
363
|
|
|
* |
364
|
|
|
* @return true|Message |
365
|
|
|
*/ |
366
|
|
|
public function deleteColumnsFromList( |
367
|
|
|
string $database, |
368
|
|
|
array $fieldSelect, |
369
|
|
|
bool $isTable = true, |
370
|
|
|
): bool|Message { |
371
|
|
|
$cfgCentralColumns = $this->getParams(); |
372
|
|
|
if (! is_array($cfgCentralColumns)) { |
373
|
|
|
return $this->getStorageNotReadyMessage(); |
374
|
|
|
} |
375
|
|
|
|
376
|
|
|
$pmadb = $cfgCentralColumns['db']; |
377
|
|
|
$centralListTable = $cfgCentralColumns['table']; |
378
|
|
|
$colNotExist = []; |
379
|
|
|
if ($isTable) { |
380
|
|
|
$fields = []; |
381
|
|
|
$cols = []; |
382
|
|
|
foreach ($fieldSelect as $table) { |
383
|
|
|
$fields[$table] = $this->dbi->getColumnNames($database, $table); |
384
|
|
|
$cols = array_merge($cols, $fields[$table]); |
385
|
|
|
} |
386
|
|
|
|
387
|
|
|
$hasList = $this->findExistingColNames($database, $cols); |
388
|
|
|
foreach ($fieldSelect as $table) { |
389
|
|
|
foreach ($fields[$table] as $column) { |
390
|
|
|
if (in_array($column, $hasList, true)) { |
391
|
|
|
continue; |
392
|
|
|
} |
393
|
|
|
|
394
|
|
|
$colNotExist[] = "'" . $column . "'"; |
395
|
|
|
} |
396
|
|
|
} |
397
|
|
|
} else { |
398
|
|
|
$cols = $fieldSelect; |
399
|
|
|
$hasList = $this->findExistingColNames($database, $cols); |
400
|
|
|
foreach ($fieldSelect as $column) { |
401
|
|
|
if (in_array($column, $hasList, true)) { |
402
|
|
|
continue; |
403
|
|
|
} |
404
|
|
|
|
405
|
|
|
$colNotExist[] = "'" . $column . "'"; |
406
|
|
|
} |
407
|
|
|
} |
408
|
|
|
|
409
|
|
|
$message = true; |
410
|
|
|
if ($colNotExist !== []) { |
411
|
|
|
$colNotExist = implode(',', array_unique($colNotExist)); |
412
|
|
|
$message = Message::notice( |
413
|
|
|
sprintf( |
414
|
|
|
__( |
415
|
|
|
'Couldn\'t remove Column(s) %1$s as they don\'t exist in central columns list!', |
416
|
|
|
), |
417
|
|
|
htmlspecialchars($colNotExist), |
418
|
|
|
), |
419
|
|
|
); |
420
|
|
|
} |
421
|
|
|
|
422
|
|
|
$cols = $this->getWhereInColumns($cols); |
423
|
|
|
$query = 'DELETE FROM ' |
424
|
|
|
. Util::backquote($pmadb) . '.' . Util::backquote($centralListTable) . ' WHERE db_name = ' |
425
|
|
|
. $this->dbi->quoteString($database, ConnectionType::ControlUser) . ' AND col_name IN (' . $cols . ');'; |
426
|
|
|
|
427
|
|
|
if (! $this->dbi->tryQuery($query, ConnectionType::ControlUser)) { |
428
|
|
|
$message = Message::error(__('Could not remove columns!')); |
429
|
|
|
$message->addHtml('<br>' . htmlspecialchars($cols) . '<br>'); |
430
|
|
|
$message->addMessage( |
431
|
|
|
Message::rawError($this->dbi->getError(ConnectionType::ControlUser)), |
432
|
|
|
); |
433
|
|
|
} |
434
|
|
|
|
435
|
|
|
return $message; |
436
|
|
|
} |
437
|
|
|
|
438
|
|
|
/** |
439
|
|
|
* Make the columns of given tables consistent with central list of columns. |
440
|
|
|
* Updates only those columns which are not being referenced. |
441
|
|
|
* |
442
|
|
|
* @param string $db current database |
443
|
|
|
* @param string[] $selectedTables list of selected tables. |
444
|
|
|
* |
445
|
|
|
* @return true|Message |
446
|
|
|
*/ |
447
|
4 |
|
public function makeConsistentWithList( |
448
|
|
|
string $db, |
449
|
|
|
array $selectedTables, |
450
|
|
|
): bool|Message { |
451
|
4 |
|
$message = true; |
452
|
4 |
|
$this->dbi->selectDb($db); |
453
|
4 |
|
foreach ($selectedTables as $table) { |
454
|
4 |
|
$query = 'ALTER TABLE ' . Util::backquote($table); |
455
|
4 |
|
$hasList = $this->findExistingColumns($db, $this->dbi->getColumnNames($db, $table)); |
456
|
4 |
|
foreach ($hasList as $column) { |
457
|
4 |
|
$columnStatus = $this->relation->checkChildForeignReferences($db, $table, $column['col_name']); |
458
|
|
|
//column definition can only be changed if |
459
|
|
|
//it is not referenced by another column |
460
|
4 |
|
if (! $columnStatus['isEditable']) { |
461
|
|
|
continue; |
462
|
|
|
} |
463
|
|
|
|
464
|
4 |
|
$query .= ' MODIFY ' . Util::backquote($column['col_name']) . ' ' . $column['col_type']; |
465
|
4 |
|
if ($column['col_length']) { |
466
|
4 |
|
$query .= '(' . $column['col_length'] . ')'; |
467
|
|
|
} |
468
|
|
|
|
469
|
4 |
|
$query .= ' ' . $column['col_attribute']; |
470
|
4 |
|
if ($column['col_isNull']) { |
471
|
4 |
|
$query .= ' NULL'; |
472
|
|
|
} else { |
473
|
4 |
|
$query .= ' NOT NULL'; |
474
|
|
|
} |
475
|
|
|
|
476
|
4 |
|
$query .= ' ' . $column['col_extra']; |
477
|
4 |
|
if ($column['col_default']) { |
478
|
|
|
if ( |
479
|
4 |
|
$column['col_default'] !== 'CURRENT_TIMESTAMP' |
480
|
4 |
|
&& $column['col_default'] !== 'current_timestamp()' |
481
|
|
|
) { |
482
|
4 |
|
$query .= ' DEFAULT ' . $this->dbi->quoteString( |
483
|
4 |
|
$column['col_default'], |
484
|
4 |
|
ConnectionType::ControlUser, |
485
|
4 |
|
); |
486
|
|
|
} else { |
487
|
4 |
|
$query .= ' DEFAULT ' . $column['col_default']; |
488
|
|
|
} |
489
|
|
|
} |
490
|
|
|
|
491
|
4 |
|
$query .= ','; |
492
|
|
|
} |
493
|
|
|
|
494
|
4 |
|
$query = trim($query, ' ,') . ';'; |
495
|
4 |
|
if ($this->dbi->tryQuery($query)) { |
496
|
4 |
|
continue; |
497
|
|
|
} |
498
|
|
|
|
499
|
|
|
if ($message === true) { |
500
|
|
|
$message = Message::error($this->dbi->getError()); |
501
|
|
|
} else { |
502
|
|
|
$message->addText($this->dbi->getError(), '<br>'); |
503
|
|
|
} |
504
|
|
|
} |
505
|
|
|
|
506
|
4 |
|
return $message; |
507
|
|
|
} |
508
|
|
|
|
509
|
|
|
/** |
510
|
|
|
* update a column in central columns list if a edit is requested |
511
|
|
|
* |
512
|
|
|
* @param string $db current database |
513
|
|
|
* @param string $origColName original column name before edit |
514
|
|
|
* @param string $colName new column name |
515
|
|
|
* @param string $colType new column type |
516
|
|
|
* @param string $colAttribute new column attribute |
517
|
|
|
* @param string $colLength new column length |
518
|
|
|
* @param bool $colIsNull value 1 if new column isNull is true, 0 otherwise |
519
|
|
|
* @param string $collation new column collation |
520
|
|
|
* @param string $colExtra new column extra property |
521
|
|
|
* @param string $colDefault new column default value |
522
|
|
|
* |
523
|
|
|
* @return true|Message |
524
|
|
|
*/ |
525
|
8 |
|
public function updateOneColumn( |
526
|
|
|
string $db, |
527
|
|
|
string $origColName, |
528
|
|
|
string $colName, |
529
|
|
|
string $colType, |
530
|
|
|
string $colAttribute, |
531
|
|
|
string $colLength, |
532
|
|
|
bool $colIsNull, |
533
|
|
|
string $collation, |
534
|
|
|
string $colExtra, |
535
|
|
|
string $colDefault, |
536
|
|
|
): bool|Message { |
537
|
8 |
|
$cfgCentralColumns = $this->getParams(); |
538
|
8 |
|
if (! is_array($cfgCentralColumns)) { |
539
|
|
|
return $this->getStorageNotReadyMessage(); |
540
|
|
|
} |
541
|
|
|
|
542
|
8 |
|
$centralListDb = $cfgCentralColumns['db']; |
543
|
8 |
|
$centralTable = $cfgCentralColumns['table']; |
544
|
8 |
|
if ($origColName === '') { |
545
|
4 |
|
$def = new ColumnFull( |
546
|
4 |
|
$colName, |
547
|
4 |
|
$colType . ($colLength !== '' ? '(' . $colLength . ')' : ''), |
548
|
4 |
|
$collation, |
549
|
4 |
|
$colIsNull, |
550
|
4 |
|
'', |
551
|
4 |
|
$colDefault, |
552
|
4 |
|
$colExtra, |
553
|
4 |
|
'', |
554
|
4 |
|
'', |
555
|
4 |
|
); |
556
|
4 |
|
$query = $this->getInsertQuery($def, $db, $centralListDb, $centralTable); |
557
|
|
|
} else { |
558
|
8 |
|
$query = 'UPDATE ' . Util::backquote($centralListDb) . '.' . Util::backquote($centralTable) |
559
|
8 |
|
. ' SET col_type = ' . $this->dbi->quoteString($colType, ConnectionType::ControlUser) |
560
|
8 |
|
. ', col_name = ' . $this->dbi->quoteString($colName, ConnectionType::ControlUser) |
561
|
8 |
|
. ', col_length = ' . $this->dbi->quoteString($colLength, ConnectionType::ControlUser) |
562
|
8 |
|
. ', col_isNull = ' . $colIsNull |
563
|
8 |
|
. ', col_collation = ' . $this->dbi->quoteString($collation, ConnectionType::ControlUser) |
564
|
8 |
|
. ', col_extra = \'' |
565
|
8 |
|
. implode(',', [$colExtra, $colAttribute]) . '\'' |
566
|
8 |
|
. ', col_default = ' . $this->dbi->quoteString($colDefault, ConnectionType::ControlUser) |
567
|
8 |
|
. ' WHERE db_name = ' . $this->dbi->quoteString($db, ConnectionType::ControlUser) |
568
|
8 |
|
. ' AND col_name = ' . $this->dbi->quoteString($origColName, ConnectionType::ControlUser); |
569
|
|
|
} |
570
|
|
|
|
571
|
8 |
|
if (! $this->dbi->tryQuery($query, ConnectionType::ControlUser)) { |
572
|
|
|
return Message::error($this->dbi->getError(ConnectionType::ControlUser)); |
573
|
|
|
} |
574
|
|
|
|
575
|
8 |
|
return true; |
576
|
|
|
} |
577
|
|
|
|
578
|
|
|
/** |
579
|
|
|
* Update Multiple column in central columns list if a change is requested |
580
|
|
|
* |
581
|
|
|
* @param mixed[] $params Request parameters |
582
|
|
|
* |
583
|
|
|
* @return true|Message |
584
|
|
|
*/ |
585
|
4 |
|
public function updateMultipleColumn(array $params): bool|Message |
586
|
|
|
{ |
587
|
4 |
|
$columnDefault = $params['field_default_type']; |
588
|
4 |
|
$columnIsNull = []; |
589
|
4 |
|
$columnExtra = []; |
590
|
4 |
|
$numberCentralFields = count($params['orig_col_name']); |
591
|
4 |
|
for ($i = 0; $i < $numberCentralFields; $i++) { |
592
|
4 |
|
$columnIsNull[$i] = isset($params['field_null'][$i]); |
593
|
4 |
|
$columnExtra[$i] = $params['col_extra'][$i] ?? ''; |
594
|
|
|
|
595
|
4 |
|
if ($columnDefault[$i] === 'NONE') { |
596
|
|
|
$columnDefault[$i] = ''; |
597
|
4 |
|
} elseif ($columnDefault[$i] === 'USER_DEFINED') { |
598
|
|
|
$columnDefault[$i] = $params['field_default_value'][$i]; |
599
|
|
|
} |
600
|
|
|
|
601
|
4 |
|
$message = $this->updateOneColumn( |
602
|
4 |
|
$params['db'], |
603
|
4 |
|
$params['orig_col_name'][$i], |
604
|
4 |
|
$params['field_name'][$i], |
605
|
4 |
|
$params['field_type'][$i], |
606
|
4 |
|
$params['field_attribute'][$i], |
607
|
4 |
|
$params['field_length'][$i], |
608
|
4 |
|
$columnIsNull[$i], |
609
|
4 |
|
$params['field_collation'][$i], |
610
|
4 |
|
$columnExtra[$i], |
611
|
4 |
|
$columnDefault[$i], |
612
|
4 |
|
); |
613
|
4 |
|
if (! is_bool($message)) { |
614
|
|
|
return $message; |
615
|
|
|
} |
616
|
|
|
} |
617
|
|
|
|
618
|
4 |
|
return true; |
619
|
|
|
} |
620
|
|
|
|
621
|
|
|
/** |
622
|
|
|
* build html for editing a row in central columns table |
623
|
|
|
* |
624
|
|
|
* @param (string|null)[] $row array contains complete information of a particular row of central list table |
625
|
|
|
* @param int $rowNum position the row in the table |
626
|
|
|
* |
627
|
|
|
* @return string html of a particular row in the central columns table. |
628
|
|
|
*/ |
629
|
4 |
|
private function getHtmlForEditTableRow(array $row, int $rowNum): string |
630
|
|
|
{ |
631
|
4 |
|
$meta = []; |
632
|
4 |
|
if (! isset($row['col_default']) || $row['col_default'] == '') { |
633
|
|
|
$meta['DefaultType'] = 'NONE'; |
634
|
4 |
|
} elseif ($row['col_default'] === 'CURRENT_TIMESTAMP' || $row['col_default'] === 'current_timestamp()') { |
635
|
4 |
|
$meta['DefaultType'] = 'CURRENT_TIMESTAMP'; |
636
|
4 |
|
} elseif ($row['col_default'] === 'NULL') { |
637
|
|
|
$meta['DefaultType'] = $row['col_default']; |
638
|
|
|
} else { |
639
|
4 |
|
$meta['DefaultType'] = 'USER_DEFINED'; |
640
|
4 |
|
$meta['DefaultValue'] = $row['col_default']; |
641
|
|
|
} |
642
|
|
|
|
643
|
4 |
|
$defaultValue = ''; |
644
|
4 |
|
$typeUpper = mb_strtoupper((string) $row['col_type']); |
645
|
|
|
|
646
|
|
|
// For a TIMESTAMP, do not show the string "CURRENT_TIMESTAMP" as a default value |
647
|
4 |
|
if (isset($meta['DefaultValue'])) { |
648
|
4 |
|
$defaultValue = $meta['DefaultValue']; |
649
|
|
|
|
650
|
4 |
|
if ($typeUpper === 'BIT') { |
651
|
|
|
$defaultValue = Util::convertBitDefaultValue($meta['DefaultValue']); |
652
|
4 |
|
} elseif ($typeUpper === 'BINARY' || $typeUpper === 'VARBINARY') { |
653
|
|
|
$defaultValue = bin2hex($meta['DefaultValue']); |
654
|
|
|
} |
655
|
|
|
} |
656
|
|
|
|
657
|
4 |
|
$charsets = Charsets::getCharsets($this->dbi, $this->disableIs); |
658
|
4 |
|
$collations = Charsets::getCollations($this->dbi, $this->disableIs); |
659
|
|
|
|
660
|
4 |
|
return $this->template->render('database/central_columns/edit_table_row', [ |
661
|
4 |
|
'row_num' => $rowNum, |
662
|
4 |
|
'row' => $row, |
663
|
4 |
|
'max_rows' => $this->maxRows, |
664
|
4 |
|
'meta' => $meta, |
665
|
4 |
|
'default_value' => $defaultValue, |
666
|
4 |
|
'char_editing' => $this->charEditing, |
667
|
4 |
|
'charsets' => $charsets, |
668
|
4 |
|
'collations' => $collations, |
669
|
4 |
|
'attribute_types' => $this->dbi->types->getAttributes(), |
670
|
4 |
|
]); |
671
|
|
|
} |
672
|
|
|
|
673
|
|
|
/** |
674
|
|
|
* get the list of columns in given database excluding |
675
|
|
|
* the columns present in current table |
676
|
|
|
* |
677
|
|
|
* @param string $db selected database |
678
|
|
|
* @param string $table current table name |
679
|
|
|
* |
680
|
|
|
* @return mixed[] encoded list of columns present in central list for the given database |
681
|
|
|
*/ |
682
|
8 |
|
public function getListRaw(string $db, string $table): array |
683
|
|
|
{ |
684
|
8 |
|
$cfgCentralColumns = $this->getParams(); |
685
|
8 |
|
if (! is_array($cfgCentralColumns)) { |
686
|
|
|
return []; |
687
|
|
|
} |
688
|
|
|
|
689
|
8 |
|
$pmadb = $cfgCentralColumns['db']; |
690
|
8 |
|
$centralTable = $cfgCentralColumns['table']; |
691
|
8 |
|
if ($table === '') { |
692
|
4 |
|
$query = 'SELECT * FROM ' . Util::backquote($pmadb) . '.' . Util::backquote($centralTable) . ' ' |
693
|
4 |
|
. 'WHERE db_name = ' . $this->dbi->quoteString($db, ConnectionType::ControlUser) . ';'; |
694
|
|
|
} else { |
695
|
4 |
|
$columns = $this->dbi->getColumnNames($db, $table); |
696
|
4 |
|
$query = 'SELECT * FROM ' . Util::backquote($pmadb) . '.' . Util::backquote($centralTable) . ' ' |
697
|
4 |
|
. 'WHERE db_name = ' . $this->dbi->quoteString($db, ConnectionType::ControlUser); |
698
|
4 |
|
if ($columns !== []) { |
699
|
4 |
|
$query .= ' AND col_name NOT IN (' . $this->getWhereInColumns($columns) . ')'; |
700
|
|
|
} |
701
|
|
|
|
702
|
4 |
|
$query .= ';'; |
703
|
|
|
} |
704
|
|
|
|
705
|
8 |
|
$columnsList = $this->dbi->fetchResult($query, null, null, ConnectionType::ControlUser); |
706
|
8 |
|
$this->handleColumnExtra($columnsList); |
707
|
|
|
|
708
|
8 |
|
return $columnsList; |
709
|
|
|
} |
710
|
|
|
|
711
|
|
|
/** |
712
|
|
|
* Column `col_extra` is used to store both extra and attributes for a column. |
713
|
|
|
* This method separates them. |
714
|
|
|
* |
715
|
|
|
* @param mixed[] $columnsList columns list |
716
|
|
|
*/ |
717
|
24 |
|
private function handleColumnExtra(array &$columnsList): void |
718
|
|
|
{ |
719
|
24 |
|
foreach ($columnsList as &$row) { |
720
|
24 |
|
$vals = explode(',', $row['col_extra']); |
721
|
|
|
|
722
|
24 |
|
if (in_array('BINARY', $vals, true)) { |
723
|
24 |
|
$row['col_attribute'] = 'BINARY'; |
724
|
20 |
|
} elseif (in_array('UNSIGNED', $vals, true)) { |
725
|
20 |
|
$row['col_attribute'] = 'UNSIGNED'; |
726
|
20 |
|
} elseif (in_array('UNSIGNED ZEROFILL', $vals, true)) { |
727
|
|
|
$row['col_attribute'] = 'UNSIGNED ZEROFILL'; |
728
|
20 |
|
} elseif (in_array('on update CURRENT_TIMESTAMP', $vals, true)) { |
729
|
20 |
|
$row['col_attribute'] = 'on update CURRENT_TIMESTAMP'; |
730
|
|
|
} else { |
731
|
|
|
$row['col_attribute'] = ''; |
732
|
|
|
} |
733
|
|
|
|
734
|
24 |
|
$row['col_extra'] = in_array('auto_increment', $vals, true) ? 'auto_increment' : ''; |
735
|
|
|
} |
736
|
|
|
} |
737
|
|
|
|
738
|
|
|
/** |
739
|
|
|
* Get HTML for editing page central columns |
740
|
|
|
* |
741
|
|
|
* @param string[] $selectedFld Array containing the selected fields |
742
|
|
|
* @param string $selectedDb String containing the name of database |
743
|
|
|
* |
744
|
|
|
* @return string HTML for complete editing page for central columns |
745
|
|
|
*/ |
746
|
4 |
|
public function getHtmlForEditingPage(array $selectedFld, string $selectedDb): string |
747
|
|
|
{ |
748
|
4 |
|
$html = ''; |
749
|
4 |
|
$listDetailCols = $this->findExistingColumns($selectedDb, $selectedFld); |
750
|
4 |
|
$rowNum = 0; |
751
|
4 |
|
foreach ($listDetailCols as $row) { |
752
|
4 |
|
$tableHtmlRow = $this->getHtmlForEditTableRow($row, $rowNum); |
753
|
4 |
|
$html .= $tableHtmlRow; |
754
|
4 |
|
$rowNum++; |
755
|
|
|
} |
756
|
|
|
|
757
|
4 |
|
return $html; |
758
|
|
|
} |
759
|
|
|
|
760
|
|
|
/** |
761
|
|
|
* get number of columns of given database from central columns list |
762
|
|
|
* starting at offset $from |
763
|
|
|
* |
764
|
|
|
* @param string $db selected database |
765
|
|
|
* @param int $from starting offset of first result |
766
|
|
|
* @param int $num maximum number of results to return |
767
|
|
|
* |
768
|
|
|
* @return int count of $num columns present in central columns list |
769
|
|
|
* starting at offset $from for the given database |
770
|
|
|
*/ |
771
|
|
|
public function getColumnsCount(string $db, int $from = 0, int $num = 25): int |
772
|
|
|
{ |
773
|
|
|
$cfgCentralColumns = $this->getParams(); |
774
|
|
|
if (! is_array($cfgCentralColumns)) { |
775
|
|
|
return 0; |
776
|
|
|
} |
777
|
|
|
|
778
|
|
|
$pmadb = $cfgCentralColumns['db']; |
779
|
|
|
$centralListTable = $cfgCentralColumns['table']; |
780
|
|
|
//get current values of $db from central column list |
781
|
|
|
$query = 'SELECT COUNT(db_name) FROM ' . Util::backquote($pmadb) . '.' . Util::backquote($centralListTable) |
782
|
|
|
. ' WHERE db_name = ' . $this->dbi->quoteString($db, ConnectionType::ControlUser) |
783
|
|
|
. ($num === 0 ? '' : 'LIMIT ' . $from . ', ' . $num) . ';'; |
784
|
|
|
$result = $this->dbi->fetchResult($query, null, null, ConnectionType::ControlUser); |
785
|
|
|
|
786
|
|
|
if (isset($result[0])) { |
787
|
|
|
return (int) $result[0]; |
788
|
|
|
} |
789
|
|
|
|
790
|
|
|
return -1; |
791
|
|
|
} |
792
|
|
|
|
793
|
|
|
/** @return string[] */ |
794
|
4 |
|
public function getColumnsNotInCentralList(string $db, string $table): array |
795
|
|
|
{ |
796
|
4 |
|
$existingColumns = $this->findExistingColNames($db, $this->dbi->getColumnNames($db, $table)); |
797
|
4 |
|
$columnNames = $this->dbi->getColumnNames($db, $table); |
798
|
|
|
|
799
|
|
|
// returns a list of column names less the ones from $existingColumns |
800
|
4 |
|
return array_values(array_diff($columnNames, $existingColumns)); |
801
|
|
|
} |
802
|
|
|
|
803
|
|
|
/** |
804
|
|
|
* Adding a new user defined column to central list |
805
|
|
|
* |
806
|
|
|
* @param string $db current database |
807
|
|
|
* @param int $totalRows number of rows in central columns |
808
|
|
|
* @param int $pos offset of first result with complete result set |
809
|
|
|
* @param string $textDir table footer arrow direction |
810
|
|
|
* |
811
|
|
|
* @return mixed[] |
812
|
|
|
*/ |
813
|
|
|
public function getTemplateVariablesForMain( |
814
|
|
|
string $db, |
815
|
|
|
int $totalRows, |
816
|
|
|
int $pos, |
817
|
|
|
string $textDir, |
818
|
|
|
): array { |
819
|
|
|
$maxRows = $this->maxRows; |
820
|
|
|
$attributeTypes = $this->dbi->types->getAttributes(); |
821
|
|
|
|
822
|
|
|
$tnPageNow = ($pos / $this->maxRows) + 1; |
823
|
|
|
$tnNbTotalPage = (int) ceil($totalRows / $this->maxRows); |
824
|
|
|
$tnPageSelector = $tnNbTotalPage > 1 ? Util::pageselector( |
825
|
|
|
'pos', |
826
|
|
|
$this->maxRows, |
827
|
|
|
$tnPageNow, |
828
|
|
|
$tnNbTotalPage, |
829
|
|
|
) : ''; |
830
|
|
|
$tables = $this->dbi->getTables($db); |
831
|
|
|
$rowsList = $this->getColumnsList($db, $pos, $maxRows); |
832
|
|
|
|
833
|
|
|
$defaultValues = []; |
834
|
|
|
$rowsMeta = []; |
835
|
|
|
$typesUpper = []; |
836
|
|
|
$rowNum = 0; |
837
|
|
|
foreach ($rowsList as $row) { |
838
|
|
|
$rowsMeta[$rowNum] = []; |
839
|
|
|
if (! isset($row['col_default']) || $row['col_default'] == '') { |
840
|
|
|
$rowsMeta[$rowNum]['DefaultType'] = 'NONE'; |
841
|
|
|
} elseif ($row['col_default'] === 'CURRENT_TIMESTAMP' || $row['col_default'] === 'current_timestamp()') { |
842
|
|
|
$rowsMeta[$rowNum]['DefaultType'] = 'CURRENT_TIMESTAMP'; |
843
|
|
|
} elseif ($row['col_default'] === 'NULL') { |
844
|
|
|
$rowsMeta[$rowNum]['DefaultType'] = $row['col_default']; |
845
|
|
|
} else { |
846
|
|
|
$rowsMeta[$rowNum]['DefaultType'] = 'USER_DEFINED'; |
847
|
|
|
$rowsMeta[$rowNum]['DefaultValue'] = $row['col_default']; |
848
|
|
|
} |
849
|
|
|
|
850
|
|
|
$typesUpper[$rowNum] = mb_strtoupper((string) $row['col_type']); |
851
|
|
|
|
852
|
|
|
// For a TIMESTAMP, do not show the string "CURRENT_TIMESTAMP" as a default value |
853
|
|
|
$defaultValues[$rowNum] = ''; |
854
|
|
|
if (isset($rowsMeta[$rowNum]['DefaultValue'])) { |
855
|
|
|
$defaultValues[$rowNum] = $rowsMeta[$rowNum]['DefaultValue']; |
856
|
|
|
|
857
|
|
|
if ($typesUpper[$rowNum] === 'BIT') { |
858
|
|
|
$defaultValues[$rowNum] = Util::convertBitDefaultValue($rowsMeta[$rowNum]['DefaultValue']); |
859
|
|
|
} elseif ($typesUpper[$rowNum] === 'BINARY' || $typesUpper[$rowNum] === 'VARBINARY') { |
860
|
|
|
$defaultValues[$rowNum] = bin2hex($rowsMeta[$rowNum]['DefaultValue']); |
861
|
|
|
} |
862
|
|
|
} |
863
|
|
|
|
864
|
|
|
$rowNum++; |
865
|
|
|
} |
866
|
|
|
|
867
|
|
|
$charsets = Charsets::getCharsets($this->dbi, $this->disableIs); |
868
|
|
|
$collations = Charsets::getCollations($this->dbi, $this->disableIs); |
869
|
|
|
$charsetsList = []; |
870
|
|
|
foreach ($charsets as $charset) { |
871
|
|
|
$collationsList = []; |
872
|
|
|
foreach ($collations[$charset->getName()] as $collation) { |
873
|
|
|
$collationsList[] = ['name' => $collation->getName(), 'description' => $collation->getDescription()]; |
874
|
|
|
} |
875
|
|
|
|
876
|
|
|
$charsetsList[] = [ |
877
|
|
|
'name' => $charset->getName(), |
878
|
|
|
'description' => $charset->getDescription(), |
879
|
|
|
'collations' => $collationsList, |
880
|
|
|
]; |
881
|
|
|
} |
882
|
|
|
|
883
|
|
|
return [ |
884
|
|
|
'db' => $db, |
885
|
|
|
'total_rows' => $totalRows, |
886
|
|
|
'max_rows' => $maxRows, |
887
|
|
|
'pos' => $pos, |
888
|
|
|
'char_editing' => $this->charEditing, |
889
|
|
|
'attribute_types' => $attributeTypes, |
890
|
|
|
'tn_nbTotalPage' => $tnNbTotalPage, |
891
|
|
|
'tn_page_selector' => $tnPageSelector, |
892
|
|
|
'tables' => $tables, |
893
|
|
|
'rows_list' => $rowsList, |
894
|
|
|
'rows_meta' => $rowsMeta, |
895
|
|
|
'default_values' => $defaultValues, |
896
|
|
|
'types_upper' => $typesUpper, |
897
|
|
|
'text_dir' => $textDir, |
898
|
|
|
'charsets' => $charsetsList, |
899
|
|
|
]; |
900
|
|
|
} |
901
|
|
|
|
902
|
|
|
/** @param string[] $columns */ |
903
|
24 |
|
private function getWhereInColumns(array $columns): string |
904
|
|
|
{ |
905
|
24 |
|
return implode(',', array_map( |
906
|
24 |
|
fn (string $string): string => $this->dbi->quoteString($string, ConnectionType::ControlUser), |
907
|
24 |
|
$columns, |
908
|
24 |
|
)); |
909
|
|
|
} |
910
|
|
|
|
911
|
|
|
private function getStorageNotReadyMessage(): Message |
912
|
|
|
{ |
913
|
|
|
return Message::error( |
914
|
|
|
__('The configuration storage is not ready for the central list of columns feature.'), |
915
|
|
|
); |
916
|
|
|
} |
917
|
|
|
} |
918
|
|
|
|
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