1
|
|
|
<?php |
2
|
|
|
/* vim: set expandtab sw=4 ts=4 sts=4: */ |
3
|
|
|
/** |
4
|
|
|
* Holds the PhpMyAdmin\Controllers\Table\StructureController |
5
|
|
|
* |
6
|
|
|
* @package PhpMyAdmin\Controllers |
7
|
|
|
*/ |
8
|
|
|
declare(strict_types=1); |
9
|
|
|
|
10
|
|
|
namespace PhpMyAdmin\Controllers\Table; |
11
|
|
|
|
12
|
|
|
use PhpMyAdmin\CentralColumns; |
13
|
|
|
use PhpMyAdmin\CheckUserPrivileges; |
14
|
|
|
use PhpMyAdmin\Config\PageSettings; |
15
|
|
|
use PhpMyAdmin\Controllers\TableController; |
16
|
|
|
use PhpMyAdmin\Core; |
17
|
|
|
use PhpMyAdmin\CreateAddField; |
18
|
|
|
use PhpMyAdmin\Index; |
19
|
|
|
use PhpMyAdmin\Message; |
20
|
|
|
use PhpMyAdmin\ParseAnalyze; |
21
|
|
|
use PhpMyAdmin\Partition; |
22
|
|
|
use PhpMyAdmin\Relation; |
23
|
|
|
use PhpMyAdmin\Sql; |
24
|
|
|
use PhpMyAdmin\SqlParser\Context; |
25
|
|
|
use PhpMyAdmin\SqlParser\Parser; |
26
|
|
|
use PhpMyAdmin\SqlParser\Statements\CreateStatement; |
27
|
|
|
use PhpMyAdmin\Table; |
28
|
|
|
use PhpMyAdmin\Template; |
29
|
|
|
use PhpMyAdmin\Tracker; |
30
|
|
|
use PhpMyAdmin\Transformations; |
31
|
|
|
use PhpMyAdmin\Url; |
32
|
|
|
use PhpMyAdmin\Util; |
33
|
|
|
|
34
|
|
|
/** |
35
|
|
|
* Handles table structure logic |
36
|
|
|
* |
37
|
|
|
* @package PhpMyAdmin\Controllers |
38
|
|
|
*/ |
39
|
|
|
class StructureController extends TableController |
40
|
|
|
{ |
41
|
|
|
/** |
42
|
|
|
* @var Table The table object |
43
|
|
|
*/ |
44
|
|
|
protected $table_obj; |
45
|
|
|
/** |
46
|
|
|
* @var string The URL query string |
47
|
|
|
*/ |
48
|
|
|
protected $_url_query; |
49
|
|
|
/** |
50
|
|
|
* @var bool DB is information_schema |
51
|
|
|
*/ |
52
|
|
|
protected $_db_is_system_schema; |
53
|
|
|
/** |
54
|
|
|
* @var bool Table is a view |
55
|
|
|
*/ |
56
|
|
|
protected $_tbl_is_view; |
57
|
|
|
/** |
58
|
|
|
* @var string Table storage engine |
59
|
|
|
*/ |
60
|
|
|
protected $_tbl_storage_engine; |
61
|
|
|
/** |
62
|
|
|
* @var int Number of rows |
63
|
|
|
*/ |
64
|
|
|
protected $_table_info_num_rows; |
65
|
|
|
/** |
66
|
|
|
* @var string Table collation |
67
|
|
|
*/ |
68
|
|
|
protected $_tbl_collation; |
69
|
|
|
/** |
70
|
|
|
* @var array Show table info |
71
|
|
|
*/ |
72
|
|
|
protected $_showtable; |
73
|
|
|
|
74
|
|
|
/** |
75
|
|
|
* @var CreateAddField |
76
|
|
|
*/ |
77
|
|
|
private $createAddField; |
78
|
|
|
|
79
|
|
|
/** |
80
|
|
|
* @var Relation |
81
|
|
|
*/ |
82
|
|
|
private $relation; |
83
|
|
|
|
84
|
|
|
/** |
85
|
|
|
* @var Transformations |
86
|
|
|
*/ |
87
|
|
|
private $transformations; |
88
|
|
|
|
89
|
|
|
/** |
90
|
|
|
* StructureController constructor |
91
|
|
|
* |
92
|
|
|
* @param \PhpMyAdmin\Response $response Response object |
93
|
|
|
* @param \PhpMyAdmin\DatabaseInterface $dbi DatabaseInterface object |
94
|
|
|
* @param string $db Database name |
95
|
|
|
* @param string $table Table name |
96
|
|
|
* @param string $type Indicate the db_structure or tbl_structure |
97
|
|
|
* @param int $num_tables Number of tables |
98
|
|
|
* @param int $pos Current position in the list |
99
|
|
|
* @param bool $db_is_system_schema DB is information_schema |
100
|
|
|
* @param int $total_num_tables Number of tables |
101
|
|
|
* @param array $tables Tables in the DB |
102
|
|
|
* @param bool $is_show_stats Whether stats show or not |
103
|
|
|
* @param bool $tbl_is_view Table is a view |
104
|
|
|
* @param string $tbl_storage_engine Table storage engine |
105
|
|
|
* @param int $table_info_num_rows Number of rows |
106
|
|
|
* @param string $tbl_collation Table collation |
107
|
|
|
* @param array $showtable Show table info |
108
|
|
|
*/ |
109
|
|
|
public function __construct( |
110
|
|
|
$response, |
111
|
|
|
$dbi, |
112
|
|
|
$db, |
113
|
|
|
$table, |
114
|
|
|
$type, |
|
|
|
|
115
|
|
|
$num_tables, |
|
|
|
|
116
|
|
|
$pos, |
|
|
|
|
117
|
|
|
$db_is_system_schema, |
118
|
|
|
$total_num_tables, |
|
|
|
|
119
|
|
|
$tables, |
|
|
|
|
120
|
|
|
$is_show_stats, |
|
|
|
|
121
|
|
|
$tbl_is_view, |
122
|
|
|
$tbl_storage_engine, |
123
|
|
|
$table_info_num_rows, |
124
|
|
|
$tbl_collation, |
125
|
|
|
$showtable |
126
|
|
|
) { |
127
|
|
|
parent::__construct($response, $dbi, $db, $table); |
128
|
|
|
|
129
|
|
|
$this->_db_is_system_schema = $db_is_system_schema; |
130
|
|
|
$this->_url_query = Url::getCommonRaw(['db' => $db, 'table' => $table]); |
131
|
|
|
$this->_tbl_is_view = $tbl_is_view; |
132
|
|
|
$this->_tbl_storage_engine = $tbl_storage_engine; |
133
|
|
|
$this->_table_info_num_rows = $table_info_num_rows; |
134
|
|
|
$this->_tbl_collation = $tbl_collation; |
135
|
|
|
$this->_showtable = $showtable; |
136
|
|
|
$this->table_obj = $this->dbi->getTable($this->db, $this->table); |
137
|
|
|
|
138
|
|
|
$this->createAddField = new CreateAddField($dbi); |
139
|
|
|
$this->relation = new Relation($dbi); |
140
|
|
|
$this->transformations = new Transformations(); |
141
|
|
|
} |
142
|
|
|
|
143
|
|
|
/** |
144
|
|
|
* Index action |
145
|
|
|
* |
146
|
|
|
* @return void |
147
|
|
|
*/ |
148
|
|
|
public function indexAction() |
149
|
|
|
{ |
150
|
|
|
PageSettings::showGroup('TableStructure'); |
151
|
|
|
|
152
|
|
|
$checkUserPrivileges = new CheckUserPrivileges($this->dbi); |
153
|
|
|
$checkUserPrivileges->getPrivileges(); |
154
|
|
|
|
155
|
|
|
$this->response->getHeader()->getScripts()->addFiles( |
156
|
|
|
[ |
157
|
|
|
'tbl_structure.js', |
158
|
|
|
'indexes.js', |
159
|
|
|
] |
160
|
|
|
); |
161
|
|
|
|
162
|
|
|
/** |
163
|
|
|
* Handle column moving |
164
|
|
|
*/ |
165
|
|
|
if (isset($_POST['move_columns']) |
166
|
|
|
&& is_array($_POST['move_columns']) |
167
|
|
|
&& $this->response->isAjax() |
168
|
|
|
) { |
169
|
|
|
$this->moveColumns(); |
170
|
|
|
return; |
171
|
|
|
} |
172
|
|
|
|
173
|
|
|
/** |
174
|
|
|
* handle MySQL reserved words columns check |
175
|
|
|
*/ |
176
|
|
|
if (isset($_POST['reserved_word_check'])) { |
177
|
|
|
if ($GLOBALS['cfg']['ReservedWordDisableWarning'] === false) { |
178
|
|
|
$columns_names = $_POST['field_name']; |
179
|
|
|
$reserved_keywords_names = []; |
180
|
|
|
foreach ($columns_names as $column) { |
181
|
|
|
if (Context::isKeyword(trim($column), true)) { |
182
|
|
|
$reserved_keywords_names[] = trim($column); |
183
|
|
|
} |
184
|
|
|
} |
185
|
|
|
if (Context::isKeyword(trim($this->table), true)) { |
186
|
|
|
$reserved_keywords_names[] = trim($this->table); |
187
|
|
|
} |
188
|
|
|
if (count($reserved_keywords_names) == 0) { |
189
|
|
|
$this->response->setRequestStatus(false); |
190
|
|
|
} |
191
|
|
|
$this->response->addJSON( |
192
|
|
|
'message', |
193
|
|
|
sprintf( |
194
|
|
|
_ngettext( |
195
|
|
|
'The name \'%s\' is a MySQL reserved keyword.', |
196
|
|
|
'The names \'%s\' are MySQL reserved keywords.', |
197
|
|
|
count($reserved_keywords_names) |
198
|
|
|
), |
199
|
|
|
implode(',', $reserved_keywords_names) |
200
|
|
|
) |
201
|
|
|
); |
202
|
|
|
} else { |
203
|
|
|
$this->response->setRequestStatus(false); |
204
|
|
|
} |
205
|
|
|
return; |
206
|
|
|
} |
207
|
|
|
/** |
208
|
|
|
* A click on Change has been made for one column |
209
|
|
|
*/ |
210
|
|
|
if (isset($_GET['change_column'])) { |
211
|
|
|
$this->displayHtmlForColumnChange(null, 'tbl_structure.php'); |
212
|
|
|
return; |
213
|
|
|
} |
214
|
|
|
|
215
|
|
|
/** |
216
|
|
|
* Adding or editing partitioning of the table |
217
|
|
|
*/ |
218
|
|
|
if (isset($_POST['edit_partitioning']) |
219
|
|
|
&& ! isset($_POST['save_partitioning']) |
220
|
|
|
) { |
221
|
|
|
$this->displayHtmlForPartitionChange(); |
222
|
|
|
return; |
223
|
|
|
} |
224
|
|
|
|
225
|
|
|
/** |
226
|
|
|
* handle multiple field commands if required |
227
|
|
|
* |
228
|
|
|
* submit_mult_*_x comes from IE if <input type="img" ...> is used |
229
|
|
|
*/ |
230
|
|
|
$submit_mult = $this->getMultipleFieldCommandType(); |
231
|
|
|
|
232
|
|
|
if (! empty($submit_mult)) { |
233
|
|
|
if (isset($_POST['selected_fld'])) { |
234
|
|
|
if ($submit_mult == 'browse') { |
235
|
|
|
// browsing the table displaying only selected columns |
236
|
|
|
$this->displayTableBrowseForSelectedColumns( |
237
|
|
|
$GLOBALS['goto'], |
238
|
|
|
$GLOBALS['pmaThemeImage'] |
239
|
|
|
); |
240
|
|
|
} else { |
241
|
|
|
// handle multiple field commands |
242
|
|
|
// handle confirmation of deleting multiple columns |
243
|
|
|
$action = 'tbl_structure.php'; |
244
|
|
|
$GLOBALS['selected'] = $_POST['selected_fld']; |
245
|
|
|
list( |
246
|
|
|
$what_ret, $query_type_ret, $is_unset_submit_mult, |
247
|
|
|
$mult_btn_ret, $centralColsError |
248
|
|
|
) |
249
|
|
|
= $this->getDataForSubmitMult( |
250
|
|
|
$submit_mult, |
251
|
|
|
$_POST['selected_fld'], |
252
|
|
|
$action |
253
|
|
|
); |
254
|
|
|
//update the existing variables |
255
|
|
|
// todo: refactor mult_submits.inc.php such as |
256
|
|
|
// below globals are not needed anymore |
257
|
|
|
if (isset($what_ret)) { |
258
|
|
|
$GLOBALS['what'] = $what_ret; |
259
|
|
|
global $what; |
260
|
|
|
} |
261
|
|
|
if (isset($query_type_ret)) { |
262
|
|
|
$GLOBALS['query_type'] = $query_type_ret; |
263
|
|
|
global $query_type; |
264
|
|
|
} |
265
|
|
|
if ($is_unset_submit_mult) { |
266
|
|
|
unset($submit_mult); |
267
|
|
|
} |
268
|
|
|
if (isset($mult_btn_ret)) { |
269
|
|
|
$GLOBALS['mult_btn'] = $mult_btn_ret; |
270
|
|
|
global $mult_btn; |
271
|
|
|
} |
272
|
|
|
include ROOT_PATH . 'libraries/mult_submits.inc.php'; |
273
|
|
|
/** |
274
|
|
|
* if $submit_mult == 'change', execution will have stopped |
275
|
|
|
* at this point |
276
|
|
|
*/ |
277
|
|
|
if (empty($message)) { |
|
|
|
|
278
|
|
|
$message = Message::success(); |
279
|
|
|
} |
280
|
|
|
$this->response->addHTML( |
281
|
|
|
Util::getMessage($message, $sql_query) |
|
|
|
|
282
|
|
|
); |
283
|
|
|
} |
284
|
|
|
} else { |
285
|
|
|
$this->response->setRequestStatus(false); |
286
|
|
|
$this->response->addJSON('message', __('No column selected.')); |
287
|
|
|
} |
288
|
|
|
} |
289
|
|
|
|
290
|
|
|
/** |
291
|
|
|
* Modifications have been submitted -> updates the table |
292
|
|
|
*/ |
293
|
|
|
if (isset($_POST['do_save_data'])) { |
294
|
|
|
$regenerate = $this->updateColumns(); |
295
|
|
|
if (! $regenerate) { |
296
|
|
|
// continue to show the table's structure |
297
|
|
|
unset($_POST['selected']); |
298
|
|
|
} |
299
|
|
|
} |
300
|
|
|
|
301
|
|
|
/** |
302
|
|
|
* Modifications to the partitioning have been submitted -> updates the table |
303
|
|
|
*/ |
304
|
|
|
if (isset($_POST['save_partitioning'])) { |
305
|
|
|
$this->updatePartitioning(); |
306
|
|
|
} |
307
|
|
|
|
308
|
|
|
/** |
309
|
|
|
* Adding indexes |
310
|
|
|
*/ |
311
|
|
|
if (isset($_POST['add_key']) |
312
|
|
|
|| isset($_POST['partition_maintenance']) |
313
|
|
|
) { |
314
|
|
|
//todo: set some variables for sql.php include, to be eliminated |
315
|
|
|
//after refactoring sql.php |
316
|
|
|
$db = $this->db; |
317
|
|
|
$table = $this->table; |
318
|
|
|
$sql_query = $GLOBALS['sql_query']; |
319
|
|
|
$cfg = $GLOBALS['cfg']; |
320
|
|
|
$pmaThemeImage = $GLOBALS['pmaThemeImage']; |
321
|
|
|
include ROOT_PATH . 'sql.php'; |
322
|
|
|
$GLOBALS['reload'] = true; |
323
|
|
|
} |
324
|
|
|
|
325
|
|
|
/** |
326
|
|
|
* Gets the relation settings |
327
|
|
|
*/ |
328
|
|
|
$cfgRelation = $this->relation->getRelationsParam(); |
329
|
|
|
|
330
|
|
|
/** |
331
|
|
|
* Runs common work |
332
|
|
|
*/ |
333
|
|
|
// set db, table references, for require_once that follows |
334
|
|
|
// got to be eliminated in long run |
335
|
|
|
$db = &$this->db; |
336
|
|
|
$table = &$this->table; |
337
|
|
|
$url_params = []; |
338
|
|
|
include_once ROOT_PATH . 'libraries/tbl_common.inc.php'; |
339
|
|
|
$this->_db_is_system_schema = $db_is_system_schema; |
|
|
|
|
340
|
|
|
$this->_url_query = Url::getCommonRaw([ |
341
|
|
|
'db' => $db, |
342
|
|
|
'table' => $table, |
343
|
|
|
'goto' => 'tbl_structure.php', |
344
|
|
|
'back' => 'tbl_structure.php', |
345
|
|
|
]); |
346
|
|
|
/* The url_params array is initialized in above include */ |
347
|
|
|
$url_params['goto'] = 'tbl_structure.php'; |
348
|
|
|
$url_params['back'] = 'tbl_structure.php'; |
349
|
|
|
|
350
|
|
|
// 2. Gets table keys and retains them |
351
|
|
|
// @todo should be: $server->db($db)->table($table)->primary() |
352
|
|
|
$primary = Index::getPrimary($this->table, $this->db); |
353
|
|
|
$columns_with_index = $this->dbi |
354
|
|
|
->getTable($this->db, $this->table) |
355
|
|
|
->getColumnsWithIndex( |
356
|
|
|
Index::UNIQUE | Index::INDEX | Index::SPATIAL |
357
|
|
|
| Index::FULLTEXT |
358
|
|
|
); |
359
|
|
|
$columns_with_unique_index = $this->dbi |
360
|
|
|
->getTable($this->db, $this->table) |
361
|
|
|
->getColumnsWithIndex(Index::UNIQUE); |
362
|
|
|
|
363
|
|
|
// 3. Get fields |
364
|
|
|
$fields = (array) $this->dbi->getColumns( |
365
|
|
|
$this->db, |
366
|
|
|
$this->table, |
367
|
|
|
null, |
368
|
|
|
true |
369
|
|
|
); |
370
|
|
|
|
371
|
|
|
//display table structure |
372
|
|
|
$this->response->addHTML( |
373
|
|
|
$this->displayStructure( |
374
|
|
|
$cfgRelation, |
375
|
|
|
$columns_with_unique_index, |
376
|
|
|
$url_params, |
377
|
|
|
$primary, |
378
|
|
|
$fields, |
379
|
|
|
$columns_with_index |
380
|
|
|
) |
381
|
|
|
); |
382
|
|
|
} |
383
|
|
|
|
384
|
|
|
/** |
385
|
|
|
* Moves columns in the table's structure based on $_REQUEST |
386
|
|
|
* |
387
|
|
|
* @return void |
388
|
|
|
*/ |
389
|
|
|
protected function moveColumns() |
390
|
|
|
{ |
391
|
|
|
$this->dbi->selectDb($this->db); |
392
|
|
|
|
393
|
|
|
/* |
394
|
|
|
* load the definitions for all columns |
395
|
|
|
*/ |
396
|
|
|
$columns = $this->dbi->getColumnsFull($this->db, $this->table); |
397
|
|
|
$column_names = array_keys($columns); |
398
|
|
|
$changes = []; |
399
|
|
|
|
400
|
|
|
// move columns from first to last |
401
|
|
|
for ($i = 0, $l = count($_POST['move_columns']); $i < $l; $i++) { |
402
|
|
|
$column = $_POST['move_columns'][$i]; |
403
|
|
|
// is this column already correctly placed? |
404
|
|
|
if ($column_names[$i] == $column) { |
405
|
|
|
continue; |
406
|
|
|
} |
407
|
|
|
|
408
|
|
|
// it is not, let's move it to index $i |
409
|
|
|
$data = $columns[$column]; |
410
|
|
|
$extracted_columnspec = Util::extractColumnSpec($data['Type']); |
411
|
|
|
if (isset($data['Extra']) |
412
|
|
|
&& $data['Extra'] == 'on update CURRENT_TIMESTAMP' |
413
|
|
|
) { |
414
|
|
|
$extracted_columnspec['attribute'] = $data['Extra']; |
415
|
|
|
unset($data['Extra']); |
416
|
|
|
} |
417
|
|
|
$current_timestamp = ($data['Type'] == 'timestamp' |
|
|
|
|
418
|
|
|
|| $data['Type'] == 'datetime') |
419
|
|
|
&& ($data['Default'] == 'CURRENT_TIMESTAMP' |
420
|
|
|
|| $data['Default'] == 'current_timestamp()'); |
421
|
|
|
|
422
|
|
|
if ($data['Null'] === 'YES' && $data['Default'] === null) { |
423
|
|
|
$default_type = 'NULL'; |
424
|
|
|
} elseif ($current_timestamp) { |
425
|
|
|
$default_type = 'CURRENT_TIMESTAMP'; |
426
|
|
|
} elseif ($data['Default'] === null) { |
427
|
|
|
$default_type = 'NONE'; |
428
|
|
|
} else { |
429
|
|
|
$default_type = 'USER_DEFINED'; |
430
|
|
|
} |
431
|
|
|
|
432
|
|
|
$virtual = [ |
433
|
|
|
'VIRTUAL', |
434
|
|
|
'PERSISTENT', |
435
|
|
|
'VIRTUAL GENERATED', |
436
|
|
|
'STORED GENERATED', |
437
|
|
|
]; |
438
|
|
|
$data['Virtuality'] = ''; |
439
|
|
|
$data['Expression'] = ''; |
440
|
|
|
if (isset($data['Extra']) && in_array($data['Extra'], $virtual)) { |
441
|
|
|
$data['Virtuality'] = str_replace(' GENERATED', '', $data['Extra']); |
442
|
|
|
$expressions = $this->table->getColumnGenerationExpression($column); |
443
|
|
|
$data['Expression'] = $expressions[$column]; |
444
|
|
|
} |
445
|
|
|
|
446
|
|
|
$changes[] = 'CHANGE ' . Table::generateAlter( |
447
|
|
|
$column, |
448
|
|
|
$column, |
449
|
|
|
mb_strtoupper($extracted_columnspec['type']), |
450
|
|
|
$extracted_columnspec['spec_in_brackets'], |
451
|
|
|
$extracted_columnspec['attribute'], |
452
|
|
|
isset($data['Collation']) ? $data['Collation'] : '', |
453
|
|
|
$data['Null'] === 'YES' ? 'NULL' : 'NOT NULL', |
454
|
|
|
$default_type, |
455
|
|
|
$current_timestamp ? '' : $data['Default'], |
456
|
|
|
isset($data['Extra']) && $data['Extra'] !== '' ? $data['Extra'] |
|
|
|
|
457
|
|
|
: false, |
458
|
|
|
isset($data['COLUMN_COMMENT']) && $data['COLUMN_COMMENT'] !== '' |
|
|
|
|
459
|
|
|
? $data['COLUMN_COMMENT'] : false, |
460
|
|
|
$data['Virtuality'], |
461
|
|
|
$data['Expression'], |
462
|
|
|
$i === 0 ? '-first' : $column_names[$i - 1] |
463
|
|
|
); |
464
|
|
|
// update current column_names array, first delete old position |
465
|
|
|
for ($j = 0, $ll = count($column_names); $j < $ll; $j++) { |
466
|
|
|
if ($column_names[$j] == $column) { |
467
|
|
|
unset($column_names[$j]); |
468
|
|
|
} |
469
|
|
|
} |
470
|
|
|
// insert moved column |
471
|
|
|
array_splice($column_names, $i, 0, $column); |
472
|
|
|
} |
473
|
|
|
if (empty($changes) && ! isset($_REQUEST['preview_sql'])) { // should never happen |
474
|
|
|
$this->response->setRequestStatus(false); |
475
|
|
|
return; |
476
|
|
|
} |
477
|
|
|
// query for moving the columns |
478
|
|
|
$sql_query = sprintf( |
479
|
|
|
'ALTER TABLE %s %s', |
480
|
|
|
Util::backquote($this->table), |
481
|
|
|
implode(', ', $changes) |
482
|
|
|
); |
483
|
|
|
|
484
|
|
|
if (isset($_REQUEST['preview_sql'])) { // preview sql |
485
|
|
|
$this->response->addJSON( |
486
|
|
|
'sql_data', |
487
|
|
|
$this->template->render('preview_sql', [ |
488
|
|
|
'query_data' => $sql_query, |
489
|
|
|
]) |
490
|
|
|
); |
491
|
|
|
} else { // move column |
492
|
|
|
$this->dbi->tryQuery($sql_query); |
493
|
|
|
$tmp_error = $this->dbi->getError(); |
494
|
|
|
if ($tmp_error) { |
495
|
|
|
$this->response->setRequestStatus(false); |
496
|
|
|
$this->response->addJSON('message', Message::error($tmp_error)); |
|
|
|
|
497
|
|
|
} else { |
498
|
|
|
$message = Message::success( |
499
|
|
|
__('The columns have been moved successfully.') |
500
|
|
|
); |
501
|
|
|
$this->response->addJSON('message', $message); |
502
|
|
|
$this->response->addJSON('columns', $column_names); |
503
|
|
|
} |
504
|
|
|
} |
505
|
|
|
} |
506
|
|
|
|
507
|
|
|
/** |
508
|
|
|
* Displays HTML for changing one or more columns |
509
|
|
|
* |
510
|
|
|
* @param array $selected the selected columns |
511
|
|
|
* @param string $action target script to call |
512
|
|
|
* |
513
|
|
|
* @return void |
514
|
|
|
* |
515
|
|
|
*/ |
516
|
|
|
protected function displayHtmlForColumnChange($selected, $action) |
517
|
|
|
{ |
518
|
|
|
// $selected comes from mult_submits.inc.php |
519
|
|
|
if (empty($selected)) { |
520
|
|
|
$selected[] = $_REQUEST['field']; |
521
|
|
|
$selected_cnt = 1; |
522
|
|
|
} else { // from a multiple submit |
523
|
|
|
$selected_cnt = count($selected); |
524
|
|
|
} |
525
|
|
|
|
526
|
|
|
/** |
527
|
|
|
* @todo optimize in case of multiple fields to modify |
528
|
|
|
*/ |
529
|
|
|
$fields_meta = []; |
530
|
|
|
for ($i = 0; $i < $selected_cnt; $i++) { |
531
|
|
|
$value = $this->dbi->getColumns( |
532
|
|
|
$this->db, |
533
|
|
|
$this->table, |
534
|
|
|
$selected[$i], |
535
|
|
|
true |
536
|
|
|
); |
537
|
|
|
if (count($value) == 0) { |
538
|
|
|
$message = Message::error( |
539
|
|
|
__('Failed to get description of column %s!') |
540
|
|
|
); |
541
|
|
|
$message->addParam($selected[$i]); |
542
|
|
|
$this->response->addHTML($message); |
543
|
|
|
} else { |
544
|
|
|
$fields_meta[] = $value; |
545
|
|
|
} |
546
|
|
|
} |
547
|
|
|
$num_fields = count($fields_meta); |
548
|
|
|
// set these globals because tbl_columns_definition_form.inc.php |
549
|
|
|
// verifies them |
550
|
|
|
// @todo: refactor tbl_columns_definition_form.inc.php so that it uses |
551
|
|
|
// protected function params |
552
|
|
|
$GLOBALS['action'] = $action; |
553
|
|
|
$GLOBALS['num_fields'] = $num_fields; |
554
|
|
|
|
555
|
|
|
/** |
556
|
|
|
* Form for changing properties. |
557
|
|
|
*/ |
558
|
|
|
$checkUserPrivileges = new CheckUserPrivileges($this->dbi); |
559
|
|
|
$checkUserPrivileges->getPrivileges(); |
560
|
|
|
|
561
|
|
|
include ROOT_PATH . 'libraries/tbl_columns_definition_form.inc.php'; |
562
|
|
|
} |
563
|
|
|
|
564
|
|
|
/** |
565
|
|
|
* Displays HTML for partition change |
566
|
|
|
* |
567
|
|
|
* @return void |
568
|
|
|
*/ |
569
|
|
|
protected function displayHtmlForPartitionChange() |
570
|
|
|
{ |
571
|
|
|
$partitionDetails = null; |
572
|
|
|
if (! isset($_POST['partition_by'])) { |
573
|
|
|
$partitionDetails = $this->_extractPartitionDetails(); |
574
|
|
|
} |
575
|
|
|
|
576
|
|
|
include ROOT_PATH . 'libraries/tbl_partition_definition.inc.php'; |
577
|
|
|
$this->response->addHTML( |
578
|
|
|
$this->template->render('table/structure/partition_definition_form', [ |
579
|
|
|
'db' => $this->db, |
580
|
|
|
'table' => $this->table, |
581
|
|
|
'partition_details' => $partitionDetails, |
582
|
|
|
]) |
583
|
|
|
); |
584
|
|
|
} |
585
|
|
|
|
586
|
|
|
/** |
587
|
|
|
* Extracts partition details from CREATE TABLE statement |
588
|
|
|
* |
589
|
|
|
* @return array[] array of partition details |
590
|
|
|
*/ |
591
|
|
|
private function _extractPartitionDetails() |
592
|
|
|
{ |
593
|
|
|
$createTable = (new Table($this->table, $this->db))->showCreate(); |
594
|
|
|
if (! $createTable) { |
595
|
|
|
return null; |
596
|
|
|
} |
597
|
|
|
|
598
|
|
|
$parser = new Parser($createTable); |
599
|
|
|
/** |
600
|
|
|
* @var CreateStatement $stmt |
601
|
|
|
*/ |
602
|
|
|
$stmt = $parser->statements[0]; |
603
|
|
|
|
604
|
|
|
$partitionDetails = []; |
605
|
|
|
|
606
|
|
|
$partitionDetails['partition_by'] = ''; |
607
|
|
|
$partitionDetails['partition_expr'] = ''; |
608
|
|
|
$partitionDetails['partition_count'] = ''; |
609
|
|
|
|
610
|
|
|
if (! empty($stmt->partitionBy)) { |
611
|
|
|
$openPos = strpos($stmt->partitionBy, "("); |
612
|
|
|
$closePos = strrpos($stmt->partitionBy, ")"); |
613
|
|
|
|
614
|
|
|
$partitionDetails['partition_by'] |
615
|
|
|
= trim(substr($stmt->partitionBy, 0, $openPos)); |
616
|
|
|
$partitionDetails['partition_expr'] |
617
|
|
|
= trim(substr($stmt->partitionBy, $openPos + 1, $closePos - ($openPos + 1))); |
618
|
|
|
if (isset($stmt->partitionsNum)) { |
619
|
|
|
$count = $stmt->partitionsNum; |
620
|
|
|
} else { |
621
|
|
|
$count = count($stmt->partitions); |
622
|
|
|
} |
623
|
|
|
$partitionDetails['partition_count'] = $count; |
624
|
|
|
} |
625
|
|
|
|
626
|
|
|
$partitionDetails['subpartition_by'] = ''; |
627
|
|
|
$partitionDetails['subpartition_expr'] = ''; |
628
|
|
|
$partitionDetails['subpartition_count'] = ''; |
629
|
|
|
|
630
|
|
|
if (! empty($stmt->subpartitionBy)) { |
631
|
|
|
$openPos = strpos($stmt->subpartitionBy, "("); |
632
|
|
|
$closePos = strrpos($stmt->subpartitionBy, ")"); |
633
|
|
|
|
634
|
|
|
$partitionDetails['subpartition_by'] |
635
|
|
|
= trim(substr($stmt->subpartitionBy, 0, $openPos)); |
636
|
|
|
$partitionDetails['subpartition_expr'] |
637
|
|
|
= trim(substr($stmt->subpartitionBy, $openPos + 1, $closePos - ($openPos + 1))); |
638
|
|
|
if (isset($stmt->subpartitionsNum)) { |
639
|
|
|
$count = $stmt->subpartitionsNum; |
640
|
|
|
} else { |
641
|
|
|
$count = count($stmt->partitions[0]->subpartitions); |
642
|
|
|
} |
643
|
|
|
$partitionDetails['subpartition_count'] = $count; |
644
|
|
|
} |
645
|
|
|
|
646
|
|
|
// Only LIST and RANGE type parameters allow subpartitioning |
647
|
|
|
$partitionDetails['can_have_subpartitions'] |
648
|
|
|
= $partitionDetails['partition_count'] > 1 |
649
|
|
|
&& ($partitionDetails['partition_by'] == 'RANGE' |
650
|
|
|
|| $partitionDetails['partition_by'] == 'RANGE COLUMNS' |
651
|
|
|
|| $partitionDetails['partition_by'] == 'LIST' |
652
|
|
|
|| $partitionDetails['partition_by'] == 'LIST COLUMNS'); |
653
|
|
|
|
654
|
|
|
// Values are specified only for LIST and RANGE type partitions |
655
|
|
|
$partitionDetails['value_enabled'] = isset($partitionDetails['partition_by']) |
656
|
|
|
&& ($partitionDetails['partition_by'] == 'RANGE' |
657
|
|
|
|| $partitionDetails['partition_by'] == 'RANGE COLUMNS' |
658
|
|
|
|| $partitionDetails['partition_by'] == 'LIST' |
659
|
|
|
|| $partitionDetails['partition_by'] == 'LIST COLUMNS'); |
660
|
|
|
|
661
|
|
|
$partitionDetails['partitions'] = []; |
662
|
|
|
|
663
|
|
|
for ($i = 0, $iMax = (int) $partitionDetails['partition_count']; $i < $iMax; $i++) { |
664
|
|
|
if (! isset($stmt->partitions[$i])) { |
665
|
|
|
$partitionDetails['partitions'][$i] = [ |
666
|
|
|
'name' => 'p' . $i, |
667
|
|
|
'value_type' => '', |
668
|
|
|
'value' => '', |
669
|
|
|
'engine' => '', |
670
|
|
|
'comment' => '', |
671
|
|
|
'data_directory' => '', |
672
|
|
|
'index_directory' => '', |
673
|
|
|
'max_rows' => '', |
674
|
|
|
'min_rows' => '', |
675
|
|
|
'tablespace' => '', |
676
|
|
|
'node_group' => '', |
677
|
|
|
]; |
678
|
|
|
} else { |
679
|
|
|
$p = $stmt->partitions[$i]; |
680
|
|
|
$type = $p->type; |
681
|
|
|
$expr = trim((string) $p->expr, '()'); |
682
|
|
|
if ($expr == 'MAXVALUE') { |
683
|
|
|
$type .= ' MAXVALUE'; |
684
|
|
|
$expr = ''; |
685
|
|
|
} |
686
|
|
|
$partitionDetails['partitions'][$i] = [ |
687
|
|
|
'name' => $p->name, |
688
|
|
|
'value_type' => $type, |
689
|
|
|
'value' => $expr, |
690
|
|
|
'engine' => $p->options->has('ENGINE', true), |
691
|
|
|
'comment' => trim((string) $p->options->has('COMMENT', true), "'"), |
692
|
|
|
'data_directory' => trim((string) $p->options->has('DATA DIRECTORY', true), "'"), |
693
|
|
|
'index_directory' => trim((string) $p->options->has('INDEX_DIRECTORY', true), "'"), |
694
|
|
|
'max_rows' => $p->options->has('MAX_ROWS', true), |
695
|
|
|
'min_rows' => $p->options->has('MIN_ROWS', true), |
696
|
|
|
'tablespace' => $p->options->has('TABLESPACE', true), |
697
|
|
|
'node_group' => $p->options->has('NODEGROUP', true), |
698
|
|
|
]; |
699
|
|
|
} |
700
|
|
|
|
701
|
|
|
$partition =& $partitionDetails['partitions'][$i]; |
702
|
|
|
$partition['prefix'] = 'partitions[' . $i . ']'; |
703
|
|
|
|
704
|
|
|
if ($partitionDetails['subpartition_count'] > 1) { |
705
|
|
|
$partition['subpartition_count'] = $partitionDetails['subpartition_count']; |
706
|
|
|
$partition['subpartitions'] = []; |
707
|
|
|
|
708
|
|
|
for ($j = 0, $jMax = (int) $partitionDetails['subpartition_count']; $j < $jMax; $j++) { |
709
|
|
|
if (! isset($stmt->partitions[$i]->subpartitions[$j])) { |
710
|
|
|
$partition['subpartitions'][$j] = [ |
711
|
|
|
'name' => $partition['name'] . '_s' . $j, |
712
|
|
|
'engine' => '', |
713
|
|
|
'comment' => '', |
714
|
|
|
'data_directory' => '', |
715
|
|
|
'index_directory' => '', |
716
|
|
|
'max_rows' => '', |
717
|
|
|
'min_rows' => '', |
718
|
|
|
'tablespace' => '', |
719
|
|
|
'node_group' => '', |
720
|
|
|
]; |
721
|
|
|
} else { |
722
|
|
|
$sp = $stmt->partitions[$i]->subpartitions[$j]; |
723
|
|
|
$partition['subpartitions'][$j] = [ |
724
|
|
|
'name' => $sp->name, |
725
|
|
|
'engine' => $sp->options->has('ENGINE', true), |
726
|
|
|
'comment' => trim($sp->options->has('COMMENT', true), "'"), |
727
|
|
|
'data_directory' => trim($sp->options->has('DATA DIRECTORY', true), "'"), |
728
|
|
|
'index_directory' => trim($sp->options->has('INDEX_DIRECTORY', true), "'"), |
729
|
|
|
'max_rows' => $sp->options->has('MAX_ROWS', true), |
730
|
|
|
'min_rows' => $sp->options->has('MIN_ROWS', true), |
731
|
|
|
'tablespace' => $sp->options->has('TABLESPACE', true), |
732
|
|
|
'node_group' => $sp->options->has('NODEGROUP', true), |
733
|
|
|
]; |
734
|
|
|
} |
735
|
|
|
|
736
|
|
|
$subpartition =& $partition['subpartitions'][$j]; |
737
|
|
|
$subpartition['prefix'] = 'partitions[' . $i . ']' |
738
|
|
|
. '[subpartitions][' . $j . ']'; |
739
|
|
|
} |
740
|
|
|
} |
741
|
|
|
} |
742
|
|
|
|
743
|
|
|
return $partitionDetails; |
744
|
|
|
} |
745
|
|
|
|
746
|
|
|
/** |
747
|
|
|
* Update the table's partitioning based on $_REQUEST |
748
|
|
|
* |
749
|
|
|
* @return void |
750
|
|
|
*/ |
751
|
|
|
protected function updatePartitioning() |
752
|
|
|
{ |
753
|
|
|
$sql_query = "ALTER TABLE " . Util::backquote($this->table) . " " |
754
|
|
|
. $this->createAddField->getPartitionsDefinition(); |
755
|
|
|
|
756
|
|
|
// Execute alter query |
757
|
|
|
$result = $this->dbi->tryQuery($sql_query); |
758
|
|
|
|
759
|
|
|
if ($result !== false) { |
760
|
|
|
$message = Message::success( |
761
|
|
|
__('Table %1$s has been altered successfully.') |
762
|
|
|
); |
763
|
|
|
$message->addParam($this->table); |
764
|
|
|
$this->response->addHTML( |
765
|
|
|
Util::getMessage($message, $sql_query, 'success') |
766
|
|
|
); |
767
|
|
|
} else { |
768
|
|
|
$this->response->setRequestStatus(false); |
769
|
|
|
$this->response->addJSON( |
770
|
|
|
'message', |
771
|
|
|
Message::rawError( |
772
|
|
|
__('Query error') . ':<br>' . $this->dbi->getError() |
773
|
|
|
) |
774
|
|
|
); |
775
|
|
|
} |
776
|
|
|
} |
777
|
|
|
|
778
|
|
|
/** |
779
|
|
|
* Function to get the type of command for multiple field handling |
780
|
|
|
* |
781
|
|
|
* @return string |
782
|
|
|
*/ |
783
|
|
|
protected function getMultipleFieldCommandType() |
784
|
|
|
{ |
785
|
|
|
$types = [ |
786
|
|
|
'change', |
787
|
|
|
'drop', |
788
|
|
|
'primary', |
789
|
|
|
'index', |
790
|
|
|
'unique', |
791
|
|
|
'spatial', |
792
|
|
|
'fulltext', |
793
|
|
|
'browse', |
794
|
|
|
]; |
795
|
|
|
|
796
|
|
|
foreach ($types as $type) { |
797
|
|
|
if (isset($_POST['submit_mult_' . $type . '_x'])) { |
798
|
|
|
return $type; |
799
|
|
|
} |
800
|
|
|
} |
801
|
|
|
|
802
|
|
|
if (isset($_POST['submit_mult'])) { |
803
|
|
|
return $_POST['submit_mult']; |
804
|
|
|
} elseif (isset($_POST['mult_btn']) |
805
|
|
|
&& $_POST['mult_btn'] == __('Yes') |
806
|
|
|
) { |
807
|
|
|
if (isset($_POST['selected'])) { |
808
|
|
|
$_POST['selected_fld'] = $_POST['selected']; |
809
|
|
|
} |
810
|
|
|
return 'row_delete'; |
811
|
|
|
} |
812
|
|
|
|
813
|
|
|
return null; |
814
|
|
|
} |
815
|
|
|
|
816
|
|
|
/** |
817
|
|
|
* Function to display table browse for selected columns |
818
|
|
|
* |
819
|
|
|
* @param string $goto goto page url |
820
|
|
|
* @param string $pmaThemeImage URI of the pma theme image |
821
|
|
|
* |
822
|
|
|
* @return void |
823
|
|
|
*/ |
824
|
|
|
protected function displayTableBrowseForSelectedColumns($goto, $pmaThemeImage) |
825
|
|
|
{ |
826
|
|
|
$GLOBALS['active_page'] = 'sql.php'; |
827
|
|
|
$fields = []; |
828
|
|
|
foreach ($_POST['selected_fld'] as $sval) { |
829
|
|
|
$fields[] = Util::backquote($sval); |
830
|
|
|
} |
831
|
|
|
$sql_query = sprintf( |
832
|
|
|
'SELECT %s FROM %s.%s', |
833
|
|
|
implode(', ', $fields), |
834
|
|
|
Util::backquote($this->db), |
835
|
|
|
Util::backquote($this->table) |
836
|
|
|
); |
837
|
|
|
|
838
|
|
|
// Parse and analyze the query |
839
|
|
|
$db = &$this->db; |
840
|
|
|
list( |
841
|
|
|
$analyzed_sql_results, |
842
|
|
|
$db, |
843
|
|
|
) = ParseAnalyze::sqlQuery($sql_query, $db); |
844
|
|
|
// @todo: possibly refactor |
845
|
|
|
extract($analyzed_sql_results); |
846
|
|
|
|
847
|
|
|
$sql = new Sql(); |
848
|
|
|
$this->response->addHTML( |
849
|
|
|
$sql->executeQueryAndGetQueryResponse( |
850
|
|
|
isset($analyzed_sql_results) ? $analyzed_sql_results : '', |
|
|
|
|
851
|
|
|
false, // is_gotofile |
852
|
|
|
$this->db, // db |
853
|
|
|
$this->table, // table |
854
|
|
|
null, // find_real_end |
855
|
|
|
null, // sql_query_for_bookmark |
856
|
|
|
null, // extra_data |
857
|
|
|
null, // message_to_show |
858
|
|
|
null, // message |
859
|
|
|
null, // sql_data |
860
|
|
|
$goto, // goto |
861
|
|
|
$pmaThemeImage, // pmaThemeImage |
862
|
|
|
null, // disp_query |
863
|
|
|
null, // disp_message |
864
|
|
|
null, // query_type |
865
|
|
|
$sql_query, // sql_query |
866
|
|
|
null, // selectedTables |
867
|
|
|
null // complete_query |
868
|
|
|
) |
869
|
|
|
); |
870
|
|
|
} |
871
|
|
|
|
872
|
|
|
/** |
873
|
|
|
* Update the table's structure based on $_REQUEST |
874
|
|
|
* |
875
|
|
|
* @return boolean true if error occurred |
876
|
|
|
* |
877
|
|
|
*/ |
878
|
|
|
protected function updateColumns() |
879
|
|
|
{ |
880
|
|
|
$err_url = 'tbl_structure.php' . Url::getCommon( |
881
|
|
|
[ |
882
|
|
|
'db' => $this->db, |
883
|
|
|
'table' => $this->table, |
884
|
|
|
] |
885
|
|
|
); |
886
|
|
|
$regenerate = false; |
887
|
|
|
$field_cnt = count($_POST['field_name']); |
888
|
|
|
$changes = []; |
889
|
|
|
$adjust_privileges = []; |
890
|
|
|
$columns_with_index = $this->dbi |
891
|
|
|
->getTable($this->db, $this->table) |
892
|
|
|
->getColumnsWithIndex( |
893
|
|
|
Index::PRIMARY | Index::UNIQUE |
894
|
|
|
); |
895
|
|
|
for ($i = 0; $i < $field_cnt; $i++) { |
896
|
|
|
if (! $this->columnNeedsAlterTable($i)) { |
897
|
|
|
continue; |
898
|
|
|
} |
899
|
|
|
|
900
|
|
|
$changes[] = 'CHANGE ' . Table::generateAlter( |
901
|
|
|
Util::getValueByKey($_POST, "field_orig.${i}", ''), |
902
|
|
|
$_POST['field_name'][$i], |
903
|
|
|
$_POST['field_type'][$i], |
904
|
|
|
$_POST['field_length'][$i], |
905
|
|
|
$_POST['field_attribute'][$i], |
906
|
|
|
Util::getValueByKey($_POST, "field_collation.${i}", ''), |
907
|
|
|
Util::getValueByKey($_POST, "field_null.${i}", 'NOT NULL'), |
908
|
|
|
$_POST['field_default_type'][$i], |
909
|
|
|
$_POST['field_default_value'][$i], |
910
|
|
|
Util::getValueByKey($_POST, "field_extra.${i}", false), |
|
|
|
|
911
|
|
|
Util::getValueByKey($_POST, "field_comments.${i}", ''), |
912
|
|
|
Util::getValueByKey($_POST, "field_virtuality.${i}", ''), |
913
|
|
|
Util::getValueByKey($_POST, "field_expression.${i}", ''), |
914
|
|
|
Util::getValueByKey($_POST, "field_move_to.${i}", ''), |
915
|
|
|
$columns_with_index |
916
|
|
|
); |
917
|
|
|
|
918
|
|
|
// find the remembered sort expression |
919
|
|
|
$sorted_col = $this->table_obj->getUiProp( |
920
|
|
|
Table::PROP_SORTED_COLUMN |
921
|
|
|
); |
922
|
|
|
// if the old column name is part of the remembered sort expression |
923
|
|
|
if (mb_strpos( |
924
|
|
|
(string) $sorted_col, |
925
|
|
|
Util::backquote($_POST['field_orig'][$i]) |
|
|
|
|
926
|
|
|
) !== false) { |
927
|
|
|
// delete the whole remembered sort expression |
928
|
|
|
$this->table_obj->removeUiProp(Table::PROP_SORTED_COLUMN); |
929
|
|
|
} |
930
|
|
|
|
931
|
|
|
if (isset($_POST['field_adjust_privileges'][$i]) |
932
|
|
|
&& ! empty($_POST['field_adjust_privileges'][$i]) |
933
|
|
|
&& $_POST['field_orig'][$i] != $_POST['field_name'][$i] |
934
|
|
|
) { |
935
|
|
|
$adjust_privileges[$_POST['field_orig'][$i]] |
936
|
|
|
= $_POST['field_name'][$i]; |
937
|
|
|
} |
938
|
|
|
} // end for |
939
|
|
|
|
940
|
|
|
if (count($changes) > 0 || isset($_POST['preview_sql'])) { |
941
|
|
|
// Builds the primary keys statements and updates the table |
942
|
|
|
$key_query = ''; |
943
|
|
|
/** |
944
|
|
|
* this is a little bit more complex |
945
|
|
|
* |
946
|
|
|
* @todo if someone selects A_I when altering a column we need to check: |
947
|
|
|
* - no other column with A_I |
948
|
|
|
* - the column has an index, if not create one |
949
|
|
|
* |
950
|
|
|
*/ |
951
|
|
|
|
952
|
|
|
// To allow replication, we first select the db to use |
953
|
|
|
// and then run queries on this db. |
954
|
|
|
if (! $this->dbi->selectDb($this->db)) { |
955
|
|
|
Util::mysqlDie( |
956
|
|
|
$this->dbi->getError(), |
957
|
|
|
'USE ' . Util::backquote($this->db) . ';', |
958
|
|
|
false, |
959
|
|
|
$err_url |
960
|
|
|
); |
961
|
|
|
} |
962
|
|
|
$sql_query = 'ALTER TABLE ' . Util::backquote($this->table) . ' '; |
963
|
|
|
$sql_query .= implode(', ', $changes) . $key_query; |
964
|
|
|
$sql_query .= ';'; |
965
|
|
|
|
966
|
|
|
// If there is a request for SQL previewing. |
967
|
|
|
if (isset($_POST['preview_sql'])) { |
968
|
|
|
Core::previewSQL(count($changes) > 0 ? $sql_query : ''); |
969
|
|
|
} |
970
|
|
|
|
971
|
|
|
$columns_with_index = $this->dbi |
972
|
|
|
->getTable($this->db, $this->table) |
973
|
|
|
->getColumnsWithIndex( |
974
|
|
|
Index::PRIMARY | Index::UNIQUE | Index::INDEX |
975
|
|
|
| Index::SPATIAL | Index::FULLTEXT |
976
|
|
|
); |
977
|
|
|
|
978
|
|
|
$changedToBlob = []; |
979
|
|
|
// While changing the Column Collation |
980
|
|
|
// First change to BLOB |
981
|
|
|
for ($i = 0; $i < $field_cnt; $i++) { |
982
|
|
|
if (isset($_POST['field_collation'][$i]) |
983
|
|
|
&& isset($_POST['field_collation_orig'][$i]) |
984
|
|
|
&& $_POST['field_collation'][$i] !== $_POST['field_collation_orig'][$i] |
985
|
|
|
&& ! in_array($_POST['field_orig'][$i], $columns_with_index) |
986
|
|
|
) { |
987
|
|
|
$secondary_query = 'ALTER TABLE ' . Util::backquote( |
988
|
|
|
$this->table |
989
|
|
|
) |
990
|
|
|
. ' CHANGE ' . Util::backquote( |
|
|
|
|
991
|
|
|
$_POST['field_orig'][$i] |
992
|
|
|
) |
993
|
|
|
. ' ' . Util::backquote($_POST['field_orig'][$i]) |
994
|
|
|
. ' BLOB;'; |
995
|
|
|
$this->dbi->query($secondary_query); |
996
|
|
|
$changedToBlob[$i] = true; |
997
|
|
|
} else { |
998
|
|
|
$changedToBlob[$i] = false; |
999
|
|
|
} |
1000
|
|
|
} |
1001
|
|
|
|
1002
|
|
|
// Then make the requested changes |
1003
|
|
|
$result = $this->dbi->tryQuery($sql_query); |
1004
|
|
|
|
1005
|
|
|
if ($result !== false) { |
1006
|
|
|
$changed_privileges = $this->adjustColumnPrivileges( |
1007
|
|
|
$adjust_privileges |
1008
|
|
|
); |
1009
|
|
|
|
1010
|
|
|
if ($changed_privileges) { |
1011
|
|
|
$message = Message::success( |
1012
|
|
|
__( |
1013
|
|
|
'Table %1$s has been altered successfully. Privileges ' . |
1014
|
|
|
'have been adjusted.' |
1015
|
|
|
) |
1016
|
|
|
); |
1017
|
|
|
} else { |
1018
|
|
|
$message = Message::success( |
1019
|
|
|
__('Table %1$s has been altered successfully.') |
1020
|
|
|
); |
1021
|
|
|
} |
1022
|
|
|
$message->addParam($this->table); |
1023
|
|
|
|
1024
|
|
|
$this->response->addHTML( |
1025
|
|
|
Util::getMessage($message, $sql_query, 'success') |
1026
|
|
|
); |
1027
|
|
|
} else { |
1028
|
|
|
// An error happened while inserting/updating a table definition |
1029
|
|
|
|
1030
|
|
|
// Save the Original Error |
1031
|
|
|
$orig_error = $this->dbi->getError(); |
1032
|
|
|
$changes_revert = []; |
1033
|
|
|
|
1034
|
|
|
// Change back to Original Collation and data type |
1035
|
|
|
for ($i = 0; $i < $field_cnt; $i++) { |
1036
|
|
|
if ($changedToBlob[$i]) { |
1037
|
|
|
$changes_revert[] = 'CHANGE ' . Table::generateAlter( |
1038
|
|
|
Util::getValueByKey($_POST, "field_orig.${i}", ''), |
1039
|
|
|
$_POST['field_name'][$i], |
1040
|
|
|
$_POST['field_type_orig'][$i], |
1041
|
|
|
$_POST['field_length_orig'][$i], |
1042
|
|
|
$_POST['field_attribute_orig'][$i], |
1043
|
|
|
Util::getValueByKey($_POST, "field_collation_orig.${i}", ''), |
1044
|
|
|
Util::getValueByKey($_POST, "field_null_orig.${i}", 'NOT NULL'), |
1045
|
|
|
$_POST['field_default_type_orig'][$i], |
1046
|
|
|
$_POST['field_default_value_orig'][$i], |
1047
|
|
|
Util::getValueByKey($_POST, "field_extra_orig.${i}", false), |
1048
|
|
|
Util::getValueByKey($_POST, "field_comments_orig.${i}", ''), |
1049
|
|
|
Util::getValueByKey($_POST, "field_virtuality_orig.${i}", ''), |
1050
|
|
|
Util::getValueByKey($_POST, "field_expression_orig.${i}", ''), |
1051
|
|
|
Util::getValueByKey($_POST, "field_move_to_orig.${i}", '') |
1052
|
|
|
); |
1053
|
|
|
} |
1054
|
|
|
} |
1055
|
|
|
|
1056
|
|
|
$revert_query = 'ALTER TABLE ' . Util::backquote($this->table) |
1057
|
|
|
. ' '; |
1058
|
|
|
$revert_query .= implode(', ', $changes_revert) . ''; |
1059
|
|
|
$revert_query .= ';'; |
1060
|
|
|
|
1061
|
|
|
// Column reverted back to original |
1062
|
|
|
$this->dbi->query($revert_query); |
1063
|
|
|
|
1064
|
|
|
$this->response->setRequestStatus(false); |
1065
|
|
|
$this->response->addJSON( |
1066
|
|
|
'message', |
1067
|
|
|
Message::rawError( |
1068
|
|
|
__('Query error') . ':<br>' . $orig_error |
1069
|
|
|
) |
1070
|
|
|
); |
1071
|
|
|
$regenerate = true; |
1072
|
|
|
} |
1073
|
|
|
} |
1074
|
|
|
|
1075
|
|
|
// update field names in relation |
1076
|
|
|
if (isset($_POST['field_orig']) && is_array($_POST['field_orig'])) { |
1077
|
|
|
foreach ($_POST['field_orig'] as $fieldindex => $fieldcontent) { |
1078
|
|
|
if ($_POST['field_name'][$fieldindex] != $fieldcontent) { |
1079
|
|
|
$this->relation->renameField( |
1080
|
|
|
$this->db, |
1081
|
|
|
$this->table, |
1082
|
|
|
$fieldcontent, |
1083
|
|
|
$_POST['field_name'][$fieldindex] |
1084
|
|
|
); |
1085
|
|
|
} |
1086
|
|
|
} |
1087
|
|
|
} |
1088
|
|
|
|
1089
|
|
|
// update mime types |
1090
|
|
|
if (isset($_POST['field_mimetype']) |
1091
|
|
|
&& is_array($_POST['field_mimetype']) |
1092
|
|
|
&& $GLOBALS['cfg']['BrowseMIME'] |
1093
|
|
|
) { |
1094
|
|
|
foreach ($_POST['field_mimetype'] as $fieldindex => $mimetype) { |
1095
|
|
|
if (isset($_POST['field_name'][$fieldindex]) |
1096
|
|
|
&& strlen($_POST['field_name'][$fieldindex]) > 0 |
1097
|
|
|
) { |
1098
|
|
|
$this->transformations->setMime( |
1099
|
|
|
$this->db, |
1100
|
|
|
$this->table, |
1101
|
|
|
$_POST['field_name'][$fieldindex], |
1102
|
|
|
$mimetype, |
1103
|
|
|
$_POST['field_transformation'][$fieldindex], |
1104
|
|
|
$_POST['field_transformation_options'][$fieldindex], |
1105
|
|
|
$_POST['field_input_transformation'][$fieldindex], |
1106
|
|
|
$_POST['field_input_transformation_options'][$fieldindex] |
1107
|
|
|
); |
1108
|
|
|
} |
1109
|
|
|
} |
1110
|
|
|
} |
1111
|
|
|
return $regenerate; |
1112
|
|
|
} |
1113
|
|
|
|
1114
|
|
|
/** |
1115
|
|
|
* Adjusts the Privileges for all the columns whose names have changed |
1116
|
|
|
* |
1117
|
|
|
* @param array $adjust_privileges assoc array of old col names mapped to new |
1118
|
|
|
* cols |
1119
|
|
|
* |
1120
|
|
|
* @return boolean boolean whether at least one column privileges |
1121
|
|
|
* adjusted |
1122
|
|
|
*/ |
1123
|
|
|
protected function adjustColumnPrivileges(array $adjust_privileges) |
1124
|
|
|
{ |
1125
|
|
|
$changed = false; |
1126
|
|
|
|
1127
|
|
|
if (Util::getValueByKey($GLOBALS, 'col_priv', false) |
1128
|
|
|
&& Util::getValueByKey($GLOBALS, 'is_reload_priv', false) |
1129
|
|
|
) { |
1130
|
|
|
$this->dbi->selectDb('mysql'); |
1131
|
|
|
|
1132
|
|
|
// For Column specific privileges |
1133
|
|
|
foreach ($adjust_privileges as $oldCol => $newCol) { |
1134
|
|
|
$this->dbi->query( |
1135
|
|
|
sprintf( |
1136
|
|
|
'UPDATE %s SET Column_name = "%s" |
1137
|
|
|
WHERE Db = "%s" |
1138
|
|
|
AND Table_name = "%s" |
1139
|
|
|
AND Column_name = "%s";', |
1140
|
|
|
Util::backquote('columns_priv'), |
1141
|
|
|
$newCol, |
1142
|
|
|
$this->db, |
1143
|
|
|
$this->table, |
1144
|
|
|
$oldCol |
1145
|
|
|
) |
1146
|
|
|
); |
1147
|
|
|
|
1148
|
|
|
// i.e. if atleast one column privileges adjusted |
1149
|
|
|
$changed = true; |
1150
|
|
|
} |
1151
|
|
|
|
1152
|
|
|
if ($changed) { |
1153
|
|
|
// Finally FLUSH the new privileges |
1154
|
|
|
$this->dbi->query("FLUSH PRIVILEGES;"); |
1155
|
|
|
} |
1156
|
|
|
} |
1157
|
|
|
|
1158
|
|
|
return $changed; |
1159
|
|
|
} |
1160
|
|
|
|
1161
|
|
|
/** |
1162
|
|
|
* Verifies if some elements of a column have changed |
1163
|
|
|
* |
1164
|
|
|
* @param integer $i column index in the request |
1165
|
|
|
* |
1166
|
|
|
* @return boolean true if we need to generate ALTER TABLE |
1167
|
|
|
* |
1168
|
|
|
*/ |
1169
|
|
|
protected function columnNeedsAlterTable($i) |
1170
|
|
|
{ |
1171
|
|
|
// these two fields are checkboxes so might not be part of the |
1172
|
|
|
// request; therefore we define them to avoid notices below |
1173
|
|
|
if (! isset($_POST['field_null'][$i])) { |
1174
|
|
|
$_POST['field_null'][$i] = 'NO'; |
1175
|
|
|
} |
1176
|
|
|
if (! isset($_POST['field_extra'][$i])) { |
1177
|
|
|
$_POST['field_extra'][$i] = ''; |
1178
|
|
|
} |
1179
|
|
|
|
1180
|
|
|
// field_name does not follow the convention (corresponds to field_orig) |
1181
|
|
|
if ($_POST['field_name'][$i] != $_POST['field_orig'][$i]) { |
1182
|
|
|
return true; |
1183
|
|
|
} |
1184
|
|
|
|
1185
|
|
|
$fields = [ |
1186
|
|
|
'field_attribute', |
1187
|
|
|
'field_collation', |
1188
|
|
|
'field_comments', |
1189
|
|
|
'field_default_value', |
1190
|
|
|
'field_default_type', |
1191
|
|
|
'field_extra', |
1192
|
|
|
'field_length', |
1193
|
|
|
'field_null', |
1194
|
|
|
'field_type', |
1195
|
|
|
]; |
1196
|
|
|
foreach ($fields as $field) { |
1197
|
|
|
if ($_POST[$field][$i] != $_POST[$field . '_orig'][$i]) { |
1198
|
|
|
return true; |
1199
|
|
|
} |
1200
|
|
|
} |
1201
|
|
|
return ! empty($_POST['field_move_to'][$i]); |
1202
|
|
|
} |
1203
|
|
|
|
1204
|
|
|
/** |
1205
|
|
|
* Displays the table structure ('show table' works correct since 3.23.03) |
1206
|
|
|
* |
1207
|
|
|
* @param array $cfgRelation current relation parameters |
1208
|
|
|
* @param array $columns_with_unique_index Columns with unique index |
1209
|
|
|
* @param mixed $url_params Contains an associative |
1210
|
|
|
* array with url params |
1211
|
|
|
* @param Index|false $primary_index primary index or false if |
1212
|
|
|
* no one exists |
1213
|
|
|
* @param array $fields Fields |
1214
|
|
|
* @param array $columns_with_index Columns with index |
1215
|
|
|
* |
1216
|
|
|
* @return string |
1217
|
|
|
*/ |
1218
|
|
|
protected function displayStructure( |
1219
|
|
|
array $cfgRelation, |
1220
|
|
|
array $columns_with_unique_index, |
1221
|
|
|
$url_params, |
1222
|
|
|
$primary_index, |
1223
|
|
|
array $fields, |
1224
|
|
|
array $columns_with_index |
1225
|
|
|
) { |
1226
|
|
|
// prepare comments |
1227
|
|
|
$comments_map = []; |
1228
|
|
|
$mime_map = []; |
1229
|
|
|
|
1230
|
|
|
if ($GLOBALS['cfg']['ShowPropertyComments']) { |
1231
|
|
|
$comments_map = $this->relation->getComments($this->db, $this->table); |
1232
|
|
|
if ($cfgRelation['mimework'] && $GLOBALS['cfg']['BrowseMIME']) { |
1233
|
|
|
$mime_map = $this->transformations->getMime($this->db, $this->table, true); |
1234
|
|
|
} |
1235
|
|
|
} |
1236
|
|
|
$centralColumns = new CentralColumns($this->dbi); |
1237
|
|
|
$central_list = $centralColumns->getFromTable( |
1238
|
|
|
$this->db, |
1239
|
|
|
$this->table |
1240
|
|
|
); |
1241
|
|
|
$columns_list = []; |
|
|
|
|
1242
|
|
|
|
1243
|
|
|
$titles = [ |
1244
|
|
|
'Change' => Util::getIcon('b_edit', __('Change')), |
1245
|
|
|
'Drop' => Util::getIcon('b_drop', __('Drop')), |
1246
|
|
|
'NoDrop' => Util::getIcon('b_drop', __('Drop')), |
1247
|
|
|
'Primary' => Util::getIcon('b_primary', __('Primary')), |
1248
|
|
|
'Index' => Util::getIcon('b_index', __('Index')), |
1249
|
|
|
'Unique' => Util::getIcon('b_unique', __('Unique')), |
1250
|
|
|
'Spatial' => Util::getIcon('b_spatial', __('Spatial')), |
1251
|
|
|
'IdxFulltext' => Util::getIcon('b_ftext', __('Fulltext')), |
1252
|
|
|
'NoPrimary' => Util::getIcon('bd_primary', __('Primary')), |
1253
|
|
|
'NoIndex' => Util::getIcon('bd_index', __('Index')), |
1254
|
|
|
'NoUnique' => Util::getIcon('bd_unique', __('Unique')), |
1255
|
|
|
'NoSpatial' => Util::getIcon('bd_spatial', __('Spatial')), |
1256
|
|
|
'NoIdxFulltext' => Util::getIcon('bd_ftext', __('Fulltext')), |
1257
|
|
|
'DistinctValues' => Util::getIcon('b_browse', __('Distinct values')), |
1258
|
|
|
]; |
1259
|
|
|
|
1260
|
|
|
/** |
1261
|
|
|
* Work on the table |
1262
|
|
|
*/ |
1263
|
|
|
if ($this->_tbl_is_view && ! $this->_db_is_system_schema) { |
1264
|
|
|
$item = $this->dbi->fetchSingleRow( |
1265
|
|
|
sprintf( |
1266
|
|
|
"SELECT `VIEW_DEFINITION`, `CHECK_OPTION`, `DEFINER`, |
1267
|
|
|
`SECURITY_TYPE` |
1268
|
|
|
FROM `INFORMATION_SCHEMA`.`VIEWS` |
1269
|
|
|
WHERE TABLE_SCHEMA='%s' |
1270
|
|
|
AND TABLE_NAME='%s';", |
1271
|
|
|
$this->dbi->escapeString($this->db), |
1272
|
|
|
$this->dbi->escapeString($this->table) |
1273
|
|
|
) |
1274
|
|
|
); |
1275
|
|
|
|
1276
|
|
|
$createView = $this->dbi->getTable($this->db, $this->table) |
1277
|
|
|
->showCreate(); |
1278
|
|
|
// get algorithm from $createView of the form |
1279
|
|
|
// CREATE ALGORITHM=<ALGORITHM> DE... |
1280
|
|
|
$parts = explode(" ", substr($createView, 17)); |
|
|
|
|
1281
|
|
|
$item['ALGORITHM'] = $parts[0]; |
1282
|
|
|
|
1283
|
|
|
$view = [ |
1284
|
|
|
'operation' => 'alter', |
1285
|
|
|
'definer' => $item['DEFINER'], |
1286
|
|
|
'sql_security' => $item['SECURITY_TYPE'], |
1287
|
|
|
'name' => $this->table, |
1288
|
|
|
'as' => $item['VIEW_DEFINITION'], |
1289
|
|
|
'with' => $item['CHECK_OPTION'], |
1290
|
|
|
'algorithm' => $item['ALGORITHM'], |
1291
|
|
|
]; |
1292
|
|
|
|
1293
|
|
|
$edit_view_url = 'view_create.php' |
1294
|
|
|
. Url::getCommon($url_params) . '&' |
1295
|
|
|
. implode( |
1296
|
|
|
'&', |
1297
|
|
|
array_map( |
1298
|
|
|
function ($key, $val) { |
1299
|
|
|
return 'view[' . urlencode($key) . ']=' . urlencode( |
1300
|
|
|
$val |
1301
|
|
|
); |
1302
|
|
|
}, |
1303
|
|
|
array_keys($view), |
1304
|
|
|
$view |
1305
|
|
|
) |
1306
|
|
|
); |
1307
|
|
|
} |
1308
|
|
|
|
1309
|
|
|
/** |
1310
|
|
|
* Displays Space usage and row statistics |
1311
|
|
|
*/ |
1312
|
|
|
// BEGIN - Calc Table Space |
1313
|
|
|
// Get valid statistics whatever is the table type |
1314
|
|
|
if ($GLOBALS['cfg']['ShowStats']) { |
1315
|
|
|
//get table stats in HTML format |
1316
|
|
|
$tablestats = $this->getTableStats(); |
1317
|
|
|
//returning the response in JSON format to be used by Ajax |
1318
|
|
|
$this->response->addJSON('tableStat', $tablestats); |
1319
|
|
|
} |
1320
|
|
|
// END - Calc Table Space |
1321
|
|
|
|
1322
|
|
|
$hideStructureActions = false; |
1323
|
|
|
if ($GLOBALS['cfg']['HideStructureActions'] === true) { |
1324
|
|
|
$hideStructureActions = true; |
1325
|
|
|
} |
1326
|
|
|
|
1327
|
|
|
// logic removed from Template |
1328
|
|
|
$rownum = 0; |
1329
|
|
|
$columns_list = []; |
1330
|
|
|
$attributes = []; |
1331
|
|
|
$displayed_fields = []; |
1332
|
|
|
$row_comments = []; |
1333
|
|
|
$extracted_columnspecs = []; |
1334
|
|
|
foreach ($fields as &$field) { |
1335
|
|
|
$rownum += 1; |
1336
|
|
|
$columns_list[] = $field['Field']; |
1337
|
|
|
|
1338
|
|
|
$extracted_columnspecs[$rownum] = Util::extractColumnSpec($field['Type']); |
1339
|
|
|
$attributes[$rownum] = $extracted_columnspecs[$rownum]['attribute']; |
1340
|
|
|
if (strpos($field['Extra'], 'on update CURRENT_TIMESTAMP') !== false) { |
1341
|
|
|
$attributes[$rownum] = 'on update CURRENT_TIMESTAMP'; |
1342
|
|
|
} |
1343
|
|
|
|
1344
|
|
|
$displayed_fields[$rownum] = new \stdClass(); |
1345
|
|
|
$displayed_fields[$rownum]->text = $field['Field']; |
1346
|
|
|
$displayed_fields[$rownum]->icon = ""; |
1347
|
|
|
$row_comments[$rownum] = ''; |
1348
|
|
|
|
1349
|
|
|
if (isset($comments_map[$field['Field']])) { |
1350
|
|
|
$displayed_fields[$rownum]->comment = $comments_map[$field['Field']]; |
1351
|
|
|
$row_comments[$rownum] = $comments_map[$field['Field']]; |
1352
|
|
|
} |
1353
|
|
|
|
1354
|
|
|
if ($primary_index && $primary_index->hasColumn($field['Field'])) { |
1355
|
|
|
$displayed_fields[$rownum]->icon .= |
1356
|
|
|
Util::getImage('b_primary', __('Primary')); |
1357
|
|
|
} |
1358
|
|
|
|
1359
|
|
|
if (in_array($field['Field'], $columns_with_index)) { |
1360
|
|
|
$displayed_fields[$rownum]->icon .= |
1361
|
|
|
Util::getImage('bd_primary', __('Index')); |
1362
|
|
|
} |
1363
|
|
|
} |
1364
|
|
|
|
1365
|
|
|
$engine = $this->table_obj->getStorageEngine(); |
1366
|
|
|
return $this->template->render('table/structure/display_structure', [ |
1367
|
|
|
'url_params' => [ |
1368
|
|
|
'db' => $this->db, |
1369
|
|
|
'table' => $this->table, |
1370
|
|
|
], |
1371
|
|
|
'is_foreign_key_supported' => Util::isForeignKeySupported($engine), |
1372
|
|
|
'displayIndexesHtml' => Index::getHtmlForDisplayIndexes(), |
1373
|
|
|
'cfg_relation' => $this->relation->getRelationsParam(), |
1374
|
|
|
'hide_structure_actions' => $hideStructureActions, |
1375
|
|
|
'db' => $this->db, |
1376
|
|
|
'table' => $this->table, |
1377
|
|
|
'db_is_system_schema' => $this->_db_is_system_schema, |
1378
|
|
|
'tbl_is_view' => $this->_tbl_is_view, |
1379
|
|
|
'mime_map' => $mime_map, |
1380
|
|
|
'url_query' => $this->_url_query, |
1381
|
|
|
'titles' => $titles, |
1382
|
|
|
'tbl_storage_engine' => $this->_tbl_storage_engine, |
1383
|
|
|
'primary' => $primary_index, |
1384
|
|
|
'columns_with_unique_index' => $columns_with_unique_index, |
1385
|
|
|
'edit_view_url' => isset($edit_view_url) ? $edit_view_url : null, |
1386
|
|
|
'columns_list' => $columns_list, |
1387
|
|
|
'table_stats' => isset($tablestats) ? $tablestats : null, |
1388
|
|
|
'fields' => $fields, |
1389
|
|
|
'extracted_columnspecs' => $extracted_columnspecs, |
1390
|
|
|
'columns_with_index' => $columns_with_index, |
1391
|
|
|
'central_list' => $central_list, |
1392
|
|
|
'comments_map' => $comments_map, |
1393
|
|
|
'browse_mime' => $GLOBALS['cfg']['BrowseMIME'], |
1394
|
|
|
'show_column_comments' => $GLOBALS['cfg']['ShowColumnComments'], |
1395
|
|
|
'show_stats' => $GLOBALS['cfg']['ShowStats'], |
1396
|
|
|
'relation_commwork' => $GLOBALS['cfgRelation']['commwork'], |
1397
|
|
|
'relation_mimework' => $GLOBALS['cfgRelation']['mimework'], |
1398
|
|
|
'central_columns_work' => $GLOBALS['cfgRelation']['centralcolumnswork'], |
1399
|
|
|
'mysql_int_version' => $this->dbi->getVersion(), |
1400
|
|
|
'pma_theme_image' => $GLOBALS['pmaThemeImage'], |
1401
|
|
|
'text_dir' => $GLOBALS['text_dir'], |
1402
|
|
|
'is_active' => Tracker::isActive(), |
1403
|
|
|
'have_partitioning' => Partition::havePartitioning(), |
1404
|
|
|
'partitions' => Partition::getPartitions($this->db, $this->table), |
1405
|
|
|
'partition_names' => Partition::getPartitionNames($this->db, $this->table), |
1406
|
|
|
'default_sliders_state' => $GLOBALS['cfg']['InitialSlidersState'], |
1407
|
|
|
'attributes' => $attributes, |
1408
|
|
|
'displayed_fields' => $displayed_fields, |
1409
|
|
|
'row_comments' => $row_comments, |
1410
|
|
|
]); |
1411
|
|
|
} |
1412
|
|
|
|
1413
|
|
|
/** |
1414
|
|
|
* Get HTML snippet for display table statistics |
1415
|
|
|
* |
1416
|
|
|
* @return string |
1417
|
|
|
*/ |
1418
|
|
|
protected function getTableStats() |
1419
|
|
|
{ |
1420
|
|
|
if (empty($this->_showtable)) { |
1421
|
|
|
$this->_showtable = $this->dbi->getTable( |
|
|
|
|
1422
|
|
|
$this->db, |
1423
|
|
|
$this->table |
1424
|
|
|
)->getStatusInfo(null, true); |
1425
|
|
|
} |
1426
|
|
|
|
1427
|
|
|
if (empty($this->_showtable['Data_length'])) { |
1428
|
|
|
$this->_showtable['Data_length'] = 0; |
1429
|
|
|
} |
1430
|
|
|
if (empty($this->_showtable['Index_length'])) { |
1431
|
|
|
$this->_showtable['Index_length'] = 0; |
1432
|
|
|
} |
1433
|
|
|
|
1434
|
|
|
$is_innodb = (isset($this->_showtable['Type']) |
1435
|
|
|
&& $this->_showtable['Type'] == 'InnoDB'); |
1436
|
|
|
|
1437
|
|
|
$mergetable = $this->table_obj->isMerge(); |
1438
|
|
|
|
1439
|
|
|
// this is to display for example 261.2 MiB instead of 268k KiB |
1440
|
|
|
$max_digits = 3; |
1441
|
|
|
$decimals = 1; |
1442
|
|
|
list($data_size, $data_unit) = Util::formatByteDown( |
1443
|
|
|
$this->_showtable['Data_length'], |
1444
|
|
|
$max_digits, |
1445
|
|
|
$decimals |
1446
|
|
|
); |
1447
|
|
|
if ($mergetable == false) { |
|
|
|
|
1448
|
|
|
list($index_size, $index_unit) = Util::formatByteDown( |
1449
|
|
|
$this->_showtable['Index_length'], |
1450
|
|
|
$max_digits, |
1451
|
|
|
$decimals |
1452
|
|
|
); |
1453
|
|
|
} |
1454
|
|
|
// InnoDB returns a huge value in Data_free, do not use it |
1455
|
|
|
if (! $is_innodb && isset($this->_showtable['Data_free']) |
1456
|
|
|
&& $this->_showtable['Data_free'] > 0 |
1457
|
|
|
) { |
1458
|
|
|
list($free_size, $free_unit) = Util::formatByteDown( |
1459
|
|
|
$this->_showtable['Data_free'], |
1460
|
|
|
$max_digits, |
1461
|
|
|
$decimals |
1462
|
|
|
); |
1463
|
|
|
list($effect_size, $effect_unit) = Util::formatByteDown( |
1464
|
|
|
$this->_showtable['Data_length'] |
1465
|
|
|
+ $this->_showtable['Index_length'] |
1466
|
|
|
- $this->_showtable['Data_free'], |
1467
|
|
|
$max_digits, |
1468
|
|
|
$decimals |
1469
|
|
|
); |
1470
|
|
|
} else { |
1471
|
|
|
list($effect_size, $effect_unit) = Util::formatByteDown( |
1472
|
|
|
$this->_showtable['Data_length'] |
1473
|
|
|
+ $this->_showtable['Index_length'], |
1474
|
|
|
$max_digits, |
1475
|
|
|
$decimals |
1476
|
|
|
); |
1477
|
|
|
} |
1478
|
|
|
list($tot_size, $tot_unit) = Util::formatByteDown( |
1479
|
|
|
$this->_showtable['Data_length'] + $this->_showtable['Index_length'], |
1480
|
|
|
$max_digits, |
1481
|
|
|
$decimals |
1482
|
|
|
); |
1483
|
|
|
if ($this->_table_info_num_rows > 0) { |
1484
|
|
|
list($avg_size, $avg_unit) = Util::formatByteDown( |
1485
|
|
|
($this->_showtable['Data_length'] |
1486
|
|
|
+ $this->_showtable['Index_length']) |
1487
|
|
|
/ $this->_showtable['Rows'], |
1488
|
|
|
6, |
1489
|
|
|
1 |
1490
|
|
|
); |
1491
|
|
|
} else { |
1492
|
|
|
$avg_size = $avg_unit = ''; |
1493
|
|
|
} |
1494
|
|
|
|
1495
|
|
|
$engine = $this->dbi->getTable($this->db, $this->table)->getStorageEngine(); |
1496
|
|
|
return $this->template->render('table/structure/display_table_stats', [ |
1497
|
|
|
'url_params' => [ |
1498
|
|
|
'db' => $GLOBALS['db'], |
1499
|
|
|
'table' => $GLOBALS['table'], |
1500
|
|
|
], |
1501
|
|
|
'is_foreign_key_supported' => Util::isForeignKeySupported($engine), |
1502
|
|
|
'cfg_relation' => $this->relation->getRelationsParam(), |
1503
|
|
|
'showtable' => $this->_showtable, |
1504
|
|
|
'table_info_num_rows' => $this->_table_info_num_rows, |
1505
|
|
|
'tbl_is_view' => $this->_tbl_is_view, |
1506
|
|
|
'db_is_system_schema' => $this->_db_is_system_schema, |
1507
|
|
|
'tbl_storage_engine' => $this->_tbl_storage_engine, |
1508
|
|
|
'url_query' => $this->_url_query, |
1509
|
|
|
'tbl_collation' => $this->_tbl_collation, |
1510
|
|
|
'is_innodb' => $is_innodb, |
1511
|
|
|
'mergetable' => $mergetable, |
1512
|
|
|
'avg_size' => isset($avg_size) ? $avg_size : null, |
1513
|
|
|
'avg_unit' => isset($avg_unit) ? $avg_unit : null, |
1514
|
|
|
'data_size' => $data_size, |
1515
|
|
|
'data_unit' => $data_unit, |
1516
|
|
|
'index_size' => isset($index_size) ? $index_size : null, |
1517
|
|
|
'index_unit' => isset($index_unit) ? $index_unit : null, |
1518
|
|
|
'free_size' => isset($free_size) ? $free_size : null, |
1519
|
|
|
'free_unit' => isset($free_unit) ? $free_unit : null, |
1520
|
|
|
'effect_size' => $effect_size, |
1521
|
|
|
'effect_unit' => $effect_unit, |
1522
|
|
|
'tot_size' => $tot_size, |
1523
|
|
|
'tot_unit' => $tot_unit, |
1524
|
|
|
'table' => $GLOBALS['table'], |
1525
|
|
|
]); |
1526
|
|
|
} |
1527
|
|
|
|
1528
|
|
|
/** |
1529
|
|
|
* Gets table primary key |
1530
|
|
|
* |
1531
|
|
|
* @return string |
1532
|
|
|
*/ |
1533
|
|
|
protected function getKeyForTablePrimary() |
1534
|
|
|
{ |
1535
|
|
|
$this->dbi->selectDb($this->db); |
1536
|
|
|
$result = $this->dbi->query( |
1537
|
|
|
'SHOW KEYS FROM ' . Util::backquote($this->table) . ';' |
1538
|
|
|
); |
1539
|
|
|
$primary = ''; |
1540
|
|
|
while ($row = $this->dbi->fetchAssoc($result)) { |
|
|
|
|
1541
|
|
|
// Backups the list of primary keys |
1542
|
|
|
if ($row['Key_name'] == 'PRIMARY') { |
1543
|
|
|
$primary .= $row['Column_name'] . ', '; |
1544
|
|
|
} |
1545
|
|
|
} // end while |
1546
|
|
|
$this->dbi->freeResult($result); |
|
|
|
|
1547
|
|
|
|
1548
|
|
|
return $primary; |
1549
|
|
|
} |
1550
|
|
|
|
1551
|
|
|
/** |
1552
|
|
|
* Get List of information for Submit Mult |
1553
|
|
|
* |
1554
|
|
|
* @param string $submit_mult mult_submit type |
1555
|
|
|
* @param array $selected the selected columns |
1556
|
|
|
* @param string $action action type |
1557
|
|
|
* |
1558
|
|
|
* @return array |
1559
|
|
|
*/ |
1560
|
|
|
protected function getDataForSubmitMult($submit_mult, $selected, $action) |
1561
|
|
|
{ |
1562
|
|
|
$centralColumns = new CentralColumns($this->dbi); |
1563
|
|
|
$what = null; |
1564
|
|
|
$query_type = null; |
1565
|
|
|
$is_unset_submit_mult = false; |
1566
|
|
|
$mult_btn = null; |
1567
|
|
|
$centralColsError = null; |
1568
|
|
|
switch ($submit_mult) { |
1569
|
|
|
case 'drop': |
1570
|
|
|
$what = 'drop_fld'; |
1571
|
|
|
break; |
1572
|
|
|
case 'primary': |
1573
|
|
|
// Gets table primary key |
1574
|
|
|
$primary = $this->getKeyForTablePrimary(); |
1575
|
|
|
if (empty($primary)) { |
1576
|
|
|
// no primary key, so we can safely create new |
1577
|
|
|
$is_unset_submit_mult = true; |
1578
|
|
|
$query_type = 'primary_fld'; |
1579
|
|
|
$mult_btn = __('Yes'); |
1580
|
|
|
} else { |
1581
|
|
|
// primary key exists, so lets as user |
1582
|
|
|
$what = 'primary_fld'; |
1583
|
|
|
} |
1584
|
|
|
break; |
1585
|
|
|
case 'index': |
1586
|
|
|
$is_unset_submit_mult = true; |
1587
|
|
|
$query_type = 'index_fld'; |
1588
|
|
|
$mult_btn = __('Yes'); |
1589
|
|
|
break; |
1590
|
|
|
case 'unique': |
1591
|
|
|
$is_unset_submit_mult = true; |
1592
|
|
|
$query_type = 'unique_fld'; |
1593
|
|
|
$mult_btn = __('Yes'); |
1594
|
|
|
break; |
1595
|
|
|
case 'spatial': |
1596
|
|
|
$is_unset_submit_mult = true; |
1597
|
|
|
$query_type = 'spatial_fld'; |
1598
|
|
|
$mult_btn = __('Yes'); |
1599
|
|
|
break; |
1600
|
|
|
case 'ftext': |
1601
|
|
|
$is_unset_submit_mult = true; |
1602
|
|
|
$query_type = 'fulltext_fld'; |
1603
|
|
|
$mult_btn = __('Yes'); |
1604
|
|
|
break; |
1605
|
|
|
case 'add_to_central_columns': |
1606
|
|
|
$centralColsError = $centralColumns->syncUniqueColumns( |
1607
|
|
|
$selected, |
1608
|
|
|
false |
1609
|
|
|
); |
1610
|
|
|
break; |
1611
|
|
|
case 'remove_from_central_columns': |
1612
|
|
|
$centralColsError = $centralColumns->deleteColumnsFromList( |
1613
|
|
|
$selected, |
1614
|
|
|
false |
1615
|
|
|
); |
1616
|
|
|
break; |
1617
|
|
|
case 'change': |
1618
|
|
|
$this->displayHtmlForColumnChange($selected, $action); |
1619
|
|
|
// execution stops here but PhpMyAdmin\Response correctly finishes |
1620
|
|
|
// the rendering |
1621
|
|
|
exit; |
|
|
|
|
1622
|
|
|
case 'browse': |
1623
|
|
|
// this should already be handled by tbl_structure.php |
1624
|
|
|
} |
1625
|
|
|
|
1626
|
|
|
return [ |
1627
|
|
|
$what, |
1628
|
|
|
$query_type, |
1629
|
|
|
$is_unset_submit_mult, |
1630
|
|
|
$mult_btn, |
1631
|
|
|
$centralColsError, |
1632
|
|
|
]; |
1633
|
|
|
} |
1634
|
|
|
} |
1635
|
|
|
|
This check looks for parameters that have been defined for a function or method, but which are not used in the method body.