Total Complexity | 182 |
Total Lines | 1593 |
Duplicated Lines | 0 % |
Changes | 0 |
Complex classes like StructureController often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use StructureController, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
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) |
||
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) |
||
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.