Update website
This commit is contained in:
parent
bb4b0f9be8
commit
011b183e28
4263 changed files with 3014 additions and 720369 deletions
|
@ -1,25 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Controllers\Table;
|
||||
|
||||
use PhpMyAdmin\Controllers\AbstractController as Controller;
|
||||
use PhpMyAdmin\ResponseRenderer;
|
||||
use PhpMyAdmin\Template;
|
||||
|
||||
abstract class AbstractController extends Controller
|
||||
{
|
||||
/** @var string */
|
||||
protected $db;
|
||||
|
||||
/** @var string */
|
||||
protected $table;
|
||||
|
||||
public function __construct(ResponseRenderer $response, Template $template, string $db, string $table)
|
||||
{
|
||||
parent::__construct($response, $template);
|
||||
$this->db = $db;
|
||||
$this->table = $table;
|
||||
}
|
||||
}
|
|
@ -1,197 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Controllers\Table;
|
||||
|
||||
use PhpMyAdmin\Config;
|
||||
use PhpMyAdmin\ConfigStorage\Relation;
|
||||
use PhpMyAdmin\Core;
|
||||
use PhpMyAdmin\CreateAddField;
|
||||
use PhpMyAdmin\DatabaseInterface;
|
||||
use PhpMyAdmin\DbTableExists;
|
||||
use PhpMyAdmin\Html\Generator;
|
||||
use PhpMyAdmin\Message;
|
||||
use PhpMyAdmin\ResponseRenderer;
|
||||
use PhpMyAdmin\Table\ColumnsDefinition;
|
||||
use PhpMyAdmin\Template;
|
||||
use PhpMyAdmin\Transformations;
|
||||
use PhpMyAdmin\Url;
|
||||
use PhpMyAdmin\Util;
|
||||
|
||||
use function __;
|
||||
use function intval;
|
||||
use function is_array;
|
||||
use function min;
|
||||
use function strlen;
|
||||
|
||||
/**
|
||||
* Displays add field form and handles it.
|
||||
*/
|
||||
class AddFieldController extends AbstractController
|
||||
{
|
||||
/** @var Transformations */
|
||||
private $transformations;
|
||||
|
||||
/** @var Config */
|
||||
private $config;
|
||||
|
||||
/** @var Relation */
|
||||
private $relation;
|
||||
|
||||
/** @var DatabaseInterface */
|
||||
private $dbi;
|
||||
|
||||
public function __construct(
|
||||
ResponseRenderer $response,
|
||||
Template $template,
|
||||
string $db,
|
||||
string $table,
|
||||
Transformations $transformations,
|
||||
Config $config,
|
||||
Relation $relation,
|
||||
DatabaseInterface $dbi
|
||||
) {
|
||||
parent::__construct($response, $template, $db, $table);
|
||||
$this->transformations = $transformations;
|
||||
$this->config = $config;
|
||||
$this->relation = $relation;
|
||||
$this->dbi = $dbi;
|
||||
}
|
||||
|
||||
public function __invoke(): void
|
||||
{
|
||||
global $errorUrl, $message, $action, $active_page, $sql_query;
|
||||
global $num_fields, $regenerate, $result, $db, $table;
|
||||
|
||||
$this->addScriptFiles(['table/structure.js']);
|
||||
|
||||
// Check parameters
|
||||
Util::checkParameters(['db', 'table']);
|
||||
|
||||
$cfg = $this->config->settings;
|
||||
|
||||
/**
|
||||
* Defines the url to return to in case of error in a sql statement
|
||||
*/
|
||||
$errorUrl = Url::getFromRoute('/table/sql', [
|
||||
'db' => $db,
|
||||
'table' => $table,
|
||||
]);
|
||||
|
||||
// check number of fields to be created
|
||||
if (isset($_POST['submit_num_fields'])) {
|
||||
if (isset($_POST['orig_after_field'])) {
|
||||
$_POST['after_field'] = $_POST['orig_after_field'];
|
||||
}
|
||||
|
||||
if (isset($_POST['orig_field_where'])) {
|
||||
$_POST['field_where'] = $_POST['orig_field_where'];
|
||||
}
|
||||
|
||||
$num_fields = min(
|
||||
intval($_POST['orig_num_fields']) + intval($_POST['added_fields']),
|
||||
4096
|
||||
);
|
||||
$regenerate = true;
|
||||
} elseif (isset($_POST['num_fields']) && intval($_POST['num_fields']) > 0) {
|
||||
$num_fields = min(4096, intval($_POST['num_fields']));
|
||||
} else {
|
||||
$num_fields = 1;
|
||||
}
|
||||
|
||||
if (isset($_POST['do_save_data'])) {
|
||||
// avoid an incorrect calling of PMA_updateColumns() via
|
||||
// /table/structure below
|
||||
unset($_POST['do_save_data']);
|
||||
|
||||
$createAddField = new CreateAddField($this->dbi);
|
||||
|
||||
$sql_query = $createAddField->getColumnCreationQuery($table);
|
||||
|
||||
// If there is a request for SQL previewing.
|
||||
if (isset($_POST['preview_sql'])) {
|
||||
Core::previewSQL($sql_query);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$result = $createAddField->tryColumnCreationQuery($db, $sql_query, $errorUrl);
|
||||
|
||||
if ($result !== true) {
|
||||
$error_message_html = Generator::mysqlDie('', '', false, $errorUrl, false);
|
||||
$this->response->addHTML($error_message_html ?? '');
|
||||
$this->response->setRequestStatus(false);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Update comment table for mime types [MIME]
|
||||
if (isset($_POST['field_mimetype']) && is_array($_POST['field_mimetype']) && $cfg['BrowseMIME']) {
|
||||
foreach ($_POST['field_mimetype'] as $fieldindex => $mimetype) {
|
||||
if (! isset($_POST['field_name'][$fieldindex]) || strlen($_POST['field_name'][$fieldindex]) <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->transformations->setMime(
|
||||
$db,
|
||||
$table,
|
||||
$_POST['field_name'][$fieldindex],
|
||||
$mimetype,
|
||||
$_POST['field_transformation'][$fieldindex],
|
||||
$_POST['field_transformation_options'][$fieldindex],
|
||||
$_POST['field_input_transformation'][$fieldindex],
|
||||
$_POST['field_input_transformation_options'][$fieldindex]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Go back to the structure sub-page
|
||||
$message = Message::success(
|
||||
__('Table %1$s has been altered successfully.')
|
||||
);
|
||||
$message->addParam($table);
|
||||
$this->response->addJSON(
|
||||
'message',
|
||||
Generator::getMessage($message, $sql_query, 'success')
|
||||
);
|
||||
|
||||
// Give an URL to call and use to appends the structure after the success message
|
||||
$this->response->addJSON(
|
||||
'structure_refresh_route',
|
||||
Url::getFromRoute('/table/structure', [
|
||||
'db' => $db,
|
||||
'table' => $table,
|
||||
'ajax_request' => '1',
|
||||
])
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$url_params = ['db' => $db, 'table' => $table];
|
||||
$errorUrl = Util::getScriptNameForOption($cfg['DefaultTabTable'], 'table');
|
||||
$errorUrl .= Url::getCommon($url_params, '&');
|
||||
|
||||
DbTableExists::check();
|
||||
|
||||
$active_page = Url::getFromRoute('/table/structure');
|
||||
/**
|
||||
* Display the form
|
||||
*/
|
||||
$action = Url::getFromRoute('/table/add-field');
|
||||
|
||||
$this->addScriptFiles(['vendor/jquery/jquery.uitablefilter.js', 'indexes.js']);
|
||||
|
||||
$templateData = ColumnsDefinition::displayForm(
|
||||
$this->transformations,
|
||||
$this->relation,
|
||||
$this->dbi,
|
||||
$action,
|
||||
$num_fields,
|
||||
$regenerate
|
||||
);
|
||||
|
||||
$this->render('columns_definitions/column_definitions_form', $templateData);
|
||||
}
|
||||
}
|
|
@ -1,280 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Controllers\Table;
|
||||
|
||||
use PhpMyAdmin\Config\PageSettings;
|
||||
use PhpMyAdmin\ConfigStorage\Relation;
|
||||
use PhpMyAdmin\DbTableExists;
|
||||
use PhpMyAdmin\Html\Generator;
|
||||
use PhpMyAdmin\InsertEdit;
|
||||
use PhpMyAdmin\ResponseRenderer;
|
||||
use PhpMyAdmin\Template;
|
||||
use PhpMyAdmin\Url;
|
||||
|
||||
use function __;
|
||||
use function array_fill;
|
||||
use function count;
|
||||
use function is_array;
|
||||
use function str_contains;
|
||||
use function strlen;
|
||||
use function strpos;
|
||||
|
||||
/**
|
||||
* Displays form for editing and inserting new table rows.
|
||||
*/
|
||||
class ChangeController extends AbstractController
|
||||
{
|
||||
/** @var InsertEdit */
|
||||
private $insertEdit;
|
||||
|
||||
/** @var Relation */
|
||||
private $relation;
|
||||
|
||||
public function __construct(
|
||||
ResponseRenderer $response,
|
||||
Template $template,
|
||||
string $db,
|
||||
string $table,
|
||||
InsertEdit $insertEdit,
|
||||
Relation $relation
|
||||
) {
|
||||
parent::__construct($response, $template, $db, $table);
|
||||
$this->insertEdit = $insertEdit;
|
||||
$this->relation = $relation;
|
||||
}
|
||||
|
||||
public function __invoke(): void
|
||||
{
|
||||
global $cfg, $db, $table, $text_dir, $disp_message, $urlParams;
|
||||
global $errorUrl, $where_clause, $unsaved_values, $insert_mode, $where_clause_array, $where_clauses;
|
||||
global $result, $rows, $found_unique_key, $after_insert, $comments_map, $table_columns;
|
||||
global $chg_evt_handler, $timestamp_seen, $columns_cnt, $tabindex;
|
||||
global $tabindex_for_value, $o_rows, $biggest_max_file_size, $has_blob_field;
|
||||
global $jsvkey, $vkey, $current_result, $repopulate, $checked;
|
||||
|
||||
$pageSettings = new PageSettings('Edit');
|
||||
$this->response->addHTML($pageSettings->getErrorHTML());
|
||||
$this->response->addHTML($pageSettings->getHTML());
|
||||
|
||||
DbTableExists::check();
|
||||
|
||||
/**
|
||||
* Determine whether Insert or Edit and set global variables
|
||||
*/
|
||||
[
|
||||
$insert_mode,
|
||||
$where_clause,
|
||||
$where_clause_array,
|
||||
$where_clauses,
|
||||
$result,
|
||||
$rows,
|
||||
$found_unique_key,
|
||||
$after_insert,
|
||||
] = $this->insertEdit->determineInsertOrEdit($where_clause ?? null, $db, $table);
|
||||
// Increase number of rows if unsaved rows are more
|
||||
if (! empty($unsaved_values) && count($rows) < count($unsaved_values)) {
|
||||
$rows = array_fill(0, count($unsaved_values), false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines the url to return to in case of error in a sql statement
|
||||
* (at this point, $GLOBALS['goto'] will be set but could be empty)
|
||||
*/
|
||||
if (empty($GLOBALS['goto'])) {
|
||||
if (strlen($table) > 0) {
|
||||
// avoid a problem (see bug #2202709)
|
||||
$GLOBALS['goto'] = Url::getFromRoute('/table/sql');
|
||||
} else {
|
||||
$GLOBALS['goto'] = Url::getFromRoute('/database/sql');
|
||||
}
|
||||
}
|
||||
|
||||
$urlParams = [
|
||||
'db' => $db,
|
||||
'sql_query' => $_POST['sql_query'] ?? '',
|
||||
];
|
||||
|
||||
if (strpos($GLOBALS['goto'] ?? '', 'index.php?route=/table') === 0) {
|
||||
$urlParams['table'] = $table;
|
||||
}
|
||||
|
||||
$errorUrl = $GLOBALS['goto'] . Url::getCommon(
|
||||
$urlParams,
|
||||
! str_contains($GLOBALS['goto'], '?') ? '?' : '&'
|
||||
);
|
||||
unset($urlParams);
|
||||
|
||||
$comments_map = $this->insertEdit->getCommentsMap($db, $table);
|
||||
|
||||
/**
|
||||
* START REGULAR OUTPUT
|
||||
*/
|
||||
|
||||
$this->addScriptFiles([
|
||||
'makegrid.js',
|
||||
'sql.js',
|
||||
'table/change.js',
|
||||
'vendor/jquery/additional-methods.js',
|
||||
'gis_data_editor.js',
|
||||
]);
|
||||
|
||||
/**
|
||||
* Displays the query submitted and its result
|
||||
*
|
||||
* $disp_message come from /table/replace
|
||||
*/
|
||||
if (! empty($disp_message)) {
|
||||
$this->response->addHTML(Generator::getMessage($disp_message, null));
|
||||
}
|
||||
|
||||
$table_columns = $this->insertEdit->getTableColumns($db, $table);
|
||||
|
||||
// retrieve keys into foreign fields, if any
|
||||
$foreigners = $this->relation->getForeigners($db, $table);
|
||||
|
||||
// Retrieve form parameters for insert/edit form
|
||||
$_form_params = $this->insertEdit->getFormParametersForInsertForm(
|
||||
$db,
|
||||
$table,
|
||||
$where_clauses,
|
||||
$where_clause_array,
|
||||
$errorUrl
|
||||
);
|
||||
|
||||
/**
|
||||
* Displays the form
|
||||
*/
|
||||
// autocomplete feature of IE kills the "onchange" event handler and it
|
||||
// must be replaced by the "onpropertychange" one in this case
|
||||
$chg_evt_handler = 'onchange';
|
||||
// Had to put the URI because when hosted on an https server,
|
||||
// some browsers send wrongly this form to the http server.
|
||||
|
||||
$html_output = '';
|
||||
// Set if we passed the first timestamp field
|
||||
$timestamp_seen = false;
|
||||
$columns_cnt = count($table_columns);
|
||||
|
||||
$tabindex = 0;
|
||||
$tabindex_for_value = 0;
|
||||
$o_rows = 0;
|
||||
$biggest_max_file_size = 0;
|
||||
|
||||
$urlParams['db'] = $db;
|
||||
$urlParams['table'] = $table;
|
||||
$urlParams = $this->insertEdit->urlParamsInEditMode($urlParams, $where_clause_array);
|
||||
|
||||
$has_blob_field = false;
|
||||
foreach ($table_columns as $column) {
|
||||
if ($this->insertEdit->isColumn($column, ['blob', 'tinyblob', 'mediumblob', 'longblob'])) {
|
||||
$has_blob_field = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//Insert/Edit form
|
||||
//If table has blob fields we have to disable ajax.
|
||||
$isUpload = $GLOBALS['config']->get('enable_upload');
|
||||
$html_output .= $this->insertEdit->getHtmlForInsertEditFormHeader($has_blob_field, $isUpload);
|
||||
|
||||
$html_output .= Url::getHiddenInputs($_form_params);
|
||||
|
||||
// user can toggle the display of Function column and column types
|
||||
// (currently does not work for multi-edits)
|
||||
if (! $cfg['ShowFunctionFields'] || ! $cfg['ShowFieldTypesInDataEditView']) {
|
||||
$html_output .= __('Show');
|
||||
}
|
||||
|
||||
if (! $cfg['ShowFunctionFields']) {
|
||||
$html_output .= $this->insertEdit->showTypeOrFunction('function', $urlParams, false);
|
||||
}
|
||||
|
||||
if (! $cfg['ShowFieldTypesInDataEditView']) {
|
||||
$html_output .= $this->insertEdit->showTypeOrFunction('type', $urlParams, false);
|
||||
}
|
||||
|
||||
$GLOBALS['plugin_scripts'] = [];
|
||||
foreach ($rows as $row_id => $current_row) {
|
||||
if (empty($current_row)) {
|
||||
$current_row = [];
|
||||
}
|
||||
|
||||
$jsvkey = $row_id;
|
||||
$vkey = '[multi_edit][' . $jsvkey . ']';
|
||||
|
||||
$current_result = (isset($result) && is_array($result) && isset($result[$row_id])
|
||||
? $result[$row_id]
|
||||
: $result);
|
||||
$repopulate = [];
|
||||
$checked = true;
|
||||
if (isset($unsaved_values[$row_id])) {
|
||||
$repopulate = $unsaved_values[$row_id];
|
||||
$checked = false;
|
||||
}
|
||||
|
||||
if ($insert_mode && $row_id > 0) {
|
||||
$html_output .= $this->insertEdit->getHtmlForIgnoreOption($row_id, $checked);
|
||||
}
|
||||
|
||||
$html_output .= $this->insertEdit->getHtmlForInsertEditRow(
|
||||
$urlParams,
|
||||
$table_columns,
|
||||
$comments_map,
|
||||
$timestamp_seen,
|
||||
$current_result,
|
||||
$chg_evt_handler,
|
||||
$jsvkey,
|
||||
$vkey,
|
||||
$insert_mode,
|
||||
$current_row,
|
||||
$o_rows,
|
||||
$tabindex,
|
||||
$columns_cnt,
|
||||
$isUpload,
|
||||
$foreigners,
|
||||
$tabindex_for_value,
|
||||
$table,
|
||||
$db,
|
||||
$row_id,
|
||||
$biggest_max_file_size,
|
||||
$text_dir,
|
||||
$repopulate,
|
||||
$where_clause_array
|
||||
);
|
||||
}
|
||||
|
||||
$this->addScriptFiles($GLOBALS['plugin_scripts']);
|
||||
|
||||
unset($unsaved_values, $checked, $repopulate, $GLOBALS['plugin_scripts']);
|
||||
|
||||
if (! isset($after_insert)) {
|
||||
$after_insert = 'back';
|
||||
}
|
||||
|
||||
$isNumeric = InsertEdit::isWhereClauseNumeric($where_clause);
|
||||
$html_output .= $this->template->render('table/insert/actions_panel', [
|
||||
'where_clause' => $where_clause,
|
||||
'after_insert' => $after_insert,
|
||||
'found_unique_key' => $found_unique_key,
|
||||
'is_numeric' => $isNumeric,
|
||||
]);
|
||||
|
||||
if ($biggest_max_file_size > 0) {
|
||||
$html_output .= '<input type="hidden" name="MAX_FILE_SIZE" value="' . $biggest_max_file_size . '">' . "\n";
|
||||
}
|
||||
|
||||
$html_output .= '</form>';
|
||||
|
||||
$html_output .= $this->insertEdit->getHtmlForGisEditor();
|
||||
// end Insert/Edit form
|
||||
|
||||
if ($insert_mode) {
|
||||
//Continue insertion form
|
||||
$html_output .= $this->insertEdit->getContinueInsertionForm($table, $db, $where_clause_array, $errorUrl);
|
||||
}
|
||||
|
||||
$this->response->addHTML($html_output);
|
||||
}
|
||||
}
|
|
@ -1,56 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Controllers\Table;
|
||||
|
||||
use PhpMyAdmin\ResponseRenderer;
|
||||
use PhpMyAdmin\Template;
|
||||
use PhpMyAdmin\Url;
|
||||
|
||||
use function __;
|
||||
use function is_array;
|
||||
|
||||
final class ChangeRowsController extends AbstractController
|
||||
{
|
||||
/** @var ChangeController */
|
||||
private $changeController;
|
||||
|
||||
public function __construct(
|
||||
ResponseRenderer $response,
|
||||
Template $template,
|
||||
string $db,
|
||||
string $table,
|
||||
ChangeController $changeController
|
||||
) {
|
||||
parent::__construct($response, $template, $db, $table);
|
||||
$this->changeController = $changeController;
|
||||
}
|
||||
|
||||
public function __invoke(): void
|
||||
{
|
||||
global $active_page, $where_clause;
|
||||
|
||||
if (isset($_POST['goto']) && (! isset($_POST['rows_to_delete']) || ! is_array($_POST['rows_to_delete']))) {
|
||||
$this->response->setRequestStatus(false);
|
||||
$this->response->addJSON('message', __('No row selected.'));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// As we got the rows to be edited from the
|
||||
// 'rows_to_delete' checkbox, we use the index of it as the
|
||||
// indicating WHERE clause. Then we build the array which is used
|
||||
// for the /table/change script.
|
||||
$where_clause = [];
|
||||
if (isset($_POST['rows_to_delete']) && is_array($_POST['rows_to_delete'])) {
|
||||
foreach ($_POST['rows_to_delete'] as $i_where_clause) {
|
||||
$where_clause[] = $i_where_clause;
|
||||
}
|
||||
}
|
||||
|
||||
$active_page = Url::getFromRoute('/table/change');
|
||||
|
||||
($this->changeController)();
|
||||
}
|
||||
}
|
|
@ -1,229 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Controllers\Table;
|
||||
|
||||
use PhpMyAdmin\DatabaseInterface;
|
||||
use PhpMyAdmin\DbTableExists;
|
||||
use PhpMyAdmin\FieldMetadata;
|
||||
use PhpMyAdmin\Html\Generator;
|
||||
use PhpMyAdmin\Message;
|
||||
use PhpMyAdmin\ResponseRenderer;
|
||||
use PhpMyAdmin\SqlParser\Components\Limit;
|
||||
use PhpMyAdmin\SqlParser\Parser;
|
||||
use PhpMyAdmin\SqlParser\Statements\SelectStatement;
|
||||
use PhpMyAdmin\Template;
|
||||
use PhpMyAdmin\Url;
|
||||
use PhpMyAdmin\Util;
|
||||
|
||||
use function __;
|
||||
use function array_keys;
|
||||
use function htmlspecialchars;
|
||||
use function json_encode;
|
||||
use function min;
|
||||
use function strlen;
|
||||
|
||||
/**
|
||||
* Handles creation of the chart.
|
||||
*/
|
||||
class ChartController extends AbstractController
|
||||
{
|
||||
/** @var DatabaseInterface */
|
||||
private $dbi;
|
||||
|
||||
public function __construct(
|
||||
ResponseRenderer $response,
|
||||
Template $template,
|
||||
string $db,
|
||||
string $table,
|
||||
DatabaseInterface $dbi
|
||||
) {
|
||||
parent::__construct($response, $template, $db, $table);
|
||||
$this->dbi = $dbi;
|
||||
}
|
||||
|
||||
public function __invoke(): void
|
||||
{
|
||||
global $db, $table, $cfg, $sql_query, $errorUrl;
|
||||
|
||||
if (isset($_REQUEST['pos'], $_REQUEST['session_max_rows']) && $this->response->isAjax()) {
|
||||
$this->ajax();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Throw error if no sql query is set
|
||||
if (! isset($sql_query) || $sql_query == '') {
|
||||
$this->response->setRequestStatus(false);
|
||||
$this->response->addHTML(
|
||||
Message::error(__('No SQL query was set to fetch data.'))->getDisplay()
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->addScriptFiles([
|
||||
'chart.js',
|
||||
'table/chart.js',
|
||||
'vendor/jqplot/jquery.jqplot.js',
|
||||
'vendor/jqplot/plugins/jqplot.barRenderer.js',
|
||||
'vendor/jqplot/plugins/jqplot.canvasAxisLabelRenderer.js',
|
||||
'vendor/jqplot/plugins/jqplot.canvasTextRenderer.js',
|
||||
'vendor/jqplot/plugins/jqplot.categoryAxisRenderer.js',
|
||||
'vendor/jqplot/plugins/jqplot.dateAxisRenderer.js',
|
||||
'vendor/jqplot/plugins/jqplot.pointLabels.js',
|
||||
'vendor/jqplot/plugins/jqplot.pieRenderer.js',
|
||||
'vendor/jqplot/plugins/jqplot.enhancedPieLegendRenderer.js',
|
||||
'vendor/jqplot/plugins/jqplot.highlighter.js',
|
||||
]);
|
||||
|
||||
$url_params = [];
|
||||
|
||||
/**
|
||||
* Runs common work
|
||||
*/
|
||||
if (strlen($table) > 0) {
|
||||
Util::checkParameters(['db', 'table']);
|
||||
|
||||
$url_params = ['db' => $db, 'table' => $table];
|
||||
$errorUrl = Util::getScriptNameForOption($cfg['DefaultTabTable'], 'table');
|
||||
$errorUrl .= Url::getCommon($url_params, '&');
|
||||
|
||||
DbTableExists::check();
|
||||
|
||||
$url_params['goto'] = Util::getScriptNameForOption($cfg['DefaultTabTable'], 'table');
|
||||
$url_params['back'] = Url::getFromRoute('/table/sql');
|
||||
$this->dbi->selectDb($db);
|
||||
} elseif (strlen($db) > 0) {
|
||||
$url_params['goto'] = Util::getScriptNameForOption($cfg['DefaultTabDatabase'], 'database');
|
||||
$url_params['back'] = Url::getFromRoute('/sql');
|
||||
|
||||
Util::checkParameters(['db']);
|
||||
|
||||
$errorUrl = Util::getScriptNameForOption($cfg['DefaultTabDatabase'], 'database');
|
||||
$errorUrl .= Url::getCommon(['db' => $db], '&');
|
||||
|
||||
if (! $this->hasDatabase()) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
$url_params['goto'] = Util::getScriptNameForOption($cfg['DefaultTabServer'], 'server');
|
||||
$url_params['back'] = Url::getFromRoute('/sql');
|
||||
$errorUrl = Url::getFromRoute('/');
|
||||
|
||||
if ($this->dbi->isSuperUser()) {
|
||||
$this->dbi->selectDb('mysql');
|
||||
}
|
||||
}
|
||||
|
||||
$result = $this->dbi->tryQuery($sql_query);
|
||||
$fields_meta = $row = [];
|
||||
if ($result !== false) {
|
||||
$fields_meta = $this->dbi->getFieldsMeta($result);
|
||||
$row = $result->fetchAssoc();
|
||||
}
|
||||
|
||||
$keys = array_keys($row);
|
||||
$numericColumnFound = false;
|
||||
foreach (array_keys($keys) as $idx) {
|
||||
if (
|
||||
isset($fields_meta[$idx]) && (
|
||||
$fields_meta[$idx]->isType(FieldMetadata::TYPE_INT)
|
||||
|| $fields_meta[$idx]->isType(FieldMetadata::TYPE_REAL)
|
||||
)
|
||||
) {
|
||||
$numericColumnFound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (! $numericColumnFound) {
|
||||
$this->response->setRequestStatus(false);
|
||||
$this->response->addJSON(
|
||||
'message',
|
||||
__('No numeric columns present in the table to plot.')
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$url_params['db'] = $db;
|
||||
$url_params['reload'] = 1;
|
||||
|
||||
$startAndNumberOfRowsFieldset = Generator::getStartAndNumberOfRowsFieldsetData($sql_query);
|
||||
|
||||
/**
|
||||
* Displays the page
|
||||
*/
|
||||
$this->render('table/chart/tbl_chart', [
|
||||
'url_params' => $url_params,
|
||||
'keys' => $keys,
|
||||
'fields_meta' => $fields_meta,
|
||||
'table_has_a_numeric_column' => $numericColumnFound,
|
||||
'start_and_number_of_rows_fieldset' => $startAndNumberOfRowsFieldset,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle ajax request
|
||||
*/
|
||||
public function ajax(): void
|
||||
{
|
||||
global $db, $table, $sql_query, $urlParams, $errorUrl, $cfg;
|
||||
|
||||
if (strlen($table) > 0 && strlen($db) > 0) {
|
||||
Util::checkParameters(['db', 'table']);
|
||||
|
||||
$urlParams = ['db' => $db, 'table' => $table];
|
||||
$errorUrl = Util::getScriptNameForOption($cfg['DefaultTabTable'], 'table');
|
||||
$errorUrl .= Url::getCommon($urlParams, '&');
|
||||
|
||||
DbTableExists::check();
|
||||
}
|
||||
|
||||
$parser = new Parser($sql_query);
|
||||
/**
|
||||
* @var SelectStatement $statement
|
||||
*/
|
||||
$statement = $parser->statements[0];
|
||||
if (empty($statement->limit)) {
|
||||
$statement->limit = new Limit($_REQUEST['session_max_rows'], $_REQUEST['pos']);
|
||||
} else {
|
||||
$start = $statement->limit->offset + $_REQUEST['pos'];
|
||||
$rows = min($_REQUEST['session_max_rows'], $statement->limit->rowCount - $_REQUEST['pos']);
|
||||
$statement->limit = new Limit($rows, $start);
|
||||
}
|
||||
|
||||
$sql_with_limit = $statement->build();
|
||||
|
||||
$result = $this->dbi->tryQuery($sql_with_limit);
|
||||
$data = [];
|
||||
if ($result !== false) {
|
||||
$data = $result->fetchAllAssoc();
|
||||
}
|
||||
|
||||
if ($data === []) {
|
||||
$this->response->setRequestStatus(false);
|
||||
$this->response->addJSON('message', __('No data to display'));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$sanitized_data = [];
|
||||
|
||||
foreach ($data as $data_row) {
|
||||
$tmp_row = [];
|
||||
foreach ($data_row as $data_column => $data_value) {
|
||||
$escaped_value = $data_value === null ? null : htmlspecialchars($data_value);
|
||||
$tmp_row[htmlspecialchars($data_column)] = $escaped_value;
|
||||
}
|
||||
|
||||
$sanitized_data[] = $tmp_row;
|
||||
}
|
||||
|
||||
$this->response->setRequestStatus(true);
|
||||
$this->response->addJSON('message', null);
|
||||
$this->response->addJSON('chartData', json_encode($sanitized_data));
|
||||
}
|
||||
}
|
|
@ -1,175 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Controllers\Table;
|
||||
|
||||
use PhpMyAdmin\Config;
|
||||
use PhpMyAdmin\ConfigStorage\Relation;
|
||||
use PhpMyAdmin\Core;
|
||||
use PhpMyAdmin\CreateAddField;
|
||||
use PhpMyAdmin\DatabaseInterface;
|
||||
use PhpMyAdmin\Html\Generator;
|
||||
use PhpMyAdmin\ResponseRenderer;
|
||||
use PhpMyAdmin\Table\ColumnsDefinition;
|
||||
use PhpMyAdmin\Template;
|
||||
use PhpMyAdmin\Transformations;
|
||||
use PhpMyAdmin\Url;
|
||||
use PhpMyAdmin\Util;
|
||||
|
||||
use function __;
|
||||
use function htmlspecialchars;
|
||||
use function is_array;
|
||||
use function mb_strtolower;
|
||||
use function sprintf;
|
||||
use function strlen;
|
||||
|
||||
/**
|
||||
* Displays table create form and handles it.
|
||||
*/
|
||||
class CreateController extends AbstractController
|
||||
{
|
||||
/** @var Transformations */
|
||||
private $transformations;
|
||||
|
||||
/** @var Config */
|
||||
private $config;
|
||||
|
||||
/** @var Relation */
|
||||
private $relation;
|
||||
|
||||
/** @var DatabaseInterface */
|
||||
private $dbi;
|
||||
|
||||
public function __construct(
|
||||
ResponseRenderer $response,
|
||||
Template $template,
|
||||
string $db,
|
||||
string $table,
|
||||
Transformations $transformations,
|
||||
Config $config,
|
||||
Relation $relation,
|
||||
DatabaseInterface $dbi
|
||||
) {
|
||||
parent::__construct($response, $template, $db, $table);
|
||||
$this->transformations = $transformations;
|
||||
$this->config = $config;
|
||||
$this->relation = $relation;
|
||||
$this->dbi = $dbi;
|
||||
}
|
||||
|
||||
public function __invoke(): void
|
||||
{
|
||||
global $num_fields, $action, $sql_query, $result, $db, $table;
|
||||
|
||||
Util::checkParameters(['db']);
|
||||
|
||||
$cfg = $this->config->settings;
|
||||
|
||||
/* Check if database name is empty */
|
||||
if (strlen($db) === 0) {
|
||||
Generator::mysqlDie(
|
||||
__('The database name is empty!'),
|
||||
'',
|
||||
false,
|
||||
'index.php'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects the database to work with
|
||||
*/
|
||||
if (! $this->dbi->selectDb($db)) {
|
||||
Generator::mysqlDie(
|
||||
sprintf(__('\'%s\' database does not exist.'), htmlspecialchars($db)),
|
||||
'',
|
||||
false,
|
||||
'index.php'
|
||||
);
|
||||
}
|
||||
|
||||
if ($this->dbi->getColumns($db, $table)) {
|
||||
// table exists already
|
||||
Generator::mysqlDie(
|
||||
sprintf(__('Table %s already exists!'), htmlspecialchars($table)),
|
||||
'',
|
||||
false,
|
||||
Url::getFromRoute('/database/structure', ['db' => $db])
|
||||
);
|
||||
}
|
||||
|
||||
$createAddField = new CreateAddField($this->dbi);
|
||||
|
||||
$num_fields = $createAddField->getNumberOfFieldsFromRequest();
|
||||
|
||||
$action = Url::getFromRoute('/table/create');
|
||||
|
||||
/**
|
||||
* The form used to define the structure of the table has been submitted
|
||||
*/
|
||||
if (isset($_POST['do_save_data'])) {
|
||||
// lower_case_table_names=1 `DB` becomes `db`
|
||||
if ($this->dbi->getLowerCaseNames() === '1') {
|
||||
$db = mb_strtolower($db);
|
||||
$table = mb_strtolower($table);
|
||||
}
|
||||
|
||||
$sql_query = $createAddField->getTableCreationQuery($db, $table);
|
||||
|
||||
// If there is a request for SQL previewing.
|
||||
if (isset($_POST['preview_sql'])) {
|
||||
Core::previewSQL($sql_query);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Executes the query
|
||||
$result = $this->dbi->tryQuery($sql_query);
|
||||
|
||||
if ($result) {
|
||||
// Update comment table for mime types [MIME]
|
||||
if (isset($_POST['field_mimetype']) && is_array($_POST['field_mimetype']) && $cfg['BrowseMIME']) {
|
||||
foreach ($_POST['field_mimetype'] as $fieldindex => $mimetype) {
|
||||
if (
|
||||
! isset($_POST['field_name'][$fieldindex])
|
||||
|| strlen($_POST['field_name'][$fieldindex]) <= 0
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->transformations->setMime(
|
||||
$db,
|
||||
$table,
|
||||
$_POST['field_name'][$fieldindex],
|
||||
$mimetype,
|
||||
$_POST['field_transformation'][$fieldindex],
|
||||
$_POST['field_transformation_options'][$fieldindex],
|
||||
$_POST['field_input_transformation'][$fieldindex],
|
||||
$_POST['field_input_transformation_options'][$fieldindex]
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$this->response->setRequestStatus(false);
|
||||
$this->response->addJSON('message', $this->dbi->getError());
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Do not display the table in the header since it hasn't been created yet
|
||||
$this->response->getHeader()->getMenu()->setTable('');
|
||||
|
||||
$this->addScriptFiles(['vendor/jquery/jquery.uitablefilter.js', 'indexes.js']);
|
||||
|
||||
$templateData = ColumnsDefinition::displayForm(
|
||||
$this->transformations,
|
||||
$this->relation,
|
||||
$this->dbi,
|
||||
$action,
|
||||
$num_fields
|
||||
);
|
||||
|
||||
$this->render('columns_definitions/column_definitions_form', $templateData);
|
||||
}
|
||||
}
|
|
@ -1,46 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Controllers\Table;
|
||||
|
||||
use PhpMyAdmin\DbTableExists;
|
||||
use PhpMyAdmin\Url;
|
||||
use PhpMyAdmin\Util;
|
||||
use PhpMyAdmin\Utils\ForeignKey;
|
||||
|
||||
use function __;
|
||||
use function is_array;
|
||||
|
||||
final class DeleteConfirmController extends AbstractController
|
||||
{
|
||||
public function __invoke(): void
|
||||
{
|
||||
global $db, $table, $sql_query, $urlParams, $errorUrl, $cfg;
|
||||
|
||||
$selected = $_POST['rows_to_delete'] ?? null;
|
||||
|
||||
if (! isset($selected) || ! is_array($selected)) {
|
||||
$this->response->setRequestStatus(false);
|
||||
$this->response->addJSON('message', __('No row selected.'));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Util::checkParameters(['db', 'table']);
|
||||
|
||||
$urlParams = ['db' => $db, 'table' => $table];
|
||||
$errorUrl = Util::getScriptNameForOption($cfg['DefaultTabTable'], 'table');
|
||||
$errorUrl .= Url::getCommon($urlParams, '&');
|
||||
|
||||
DbTableExists::check();
|
||||
|
||||
$this->render('table/delete/confirm', [
|
||||
'db' => $db,
|
||||
'table' => $table,
|
||||
'selected' => $selected,
|
||||
'sql_query' => $sql_query,
|
||||
'is_foreign_key_check' => ForeignKey::isCheckEnabled(),
|
||||
]);
|
||||
}
|
||||
}
|
|
@ -1,107 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Controllers\Table;
|
||||
|
||||
use PhpMyAdmin\ConfigStorage\Relation;
|
||||
use PhpMyAdmin\ConfigStorage\RelationCleanup;
|
||||
use PhpMyAdmin\DatabaseInterface;
|
||||
use PhpMyAdmin\Operations;
|
||||
use PhpMyAdmin\ResponseRenderer;
|
||||
use PhpMyAdmin\Sql;
|
||||
use PhpMyAdmin\Template;
|
||||
use PhpMyAdmin\Transformations;
|
||||
use PhpMyAdmin\Url;
|
||||
use PhpMyAdmin\Util;
|
||||
use PhpMyAdmin\Utils\ForeignKey;
|
||||
|
||||
use function __;
|
||||
use function sprintf;
|
||||
|
||||
final class DeleteRowsController extends AbstractController
|
||||
{
|
||||
/** @var DatabaseInterface */
|
||||
private $dbi;
|
||||
|
||||
public function __construct(
|
||||
ResponseRenderer $response,
|
||||
Template $template,
|
||||
string $db,
|
||||
string $table,
|
||||
DatabaseInterface $dbi
|
||||
) {
|
||||
parent::__construct($response, $template, $db, $table);
|
||||
$this->dbi = $dbi;
|
||||
}
|
||||
|
||||
public function __invoke(): void
|
||||
{
|
||||
global $db, $goto, $sql_query, $table, $disp_message, $disp_query, $active_page;
|
||||
|
||||
$mult_btn = $_POST['mult_btn'] ?? '';
|
||||
$original_sql_query = $_POST['original_sql_query'] ?? '';
|
||||
$selected = $_POST['selected'] ?? [];
|
||||
|
||||
$relation = new Relation($this->dbi);
|
||||
$sql = new Sql(
|
||||
$this->dbi,
|
||||
$relation,
|
||||
new RelationCleanup($this->dbi, $relation),
|
||||
new Operations($this->dbi, $relation),
|
||||
new Transformations(),
|
||||
$this->template
|
||||
);
|
||||
|
||||
if ($mult_btn === __('Yes')) {
|
||||
$default_fk_check_value = ForeignKey::handleDisableCheckInit();
|
||||
$sql_query = '';
|
||||
|
||||
foreach ($selected as $row) {
|
||||
$query = sprintf(
|
||||
'DELETE FROM %s WHERE %s LIMIT 1;',
|
||||
Util::backquote($table),
|
||||
$row
|
||||
);
|
||||
$sql_query .= $query . "\n";
|
||||
$this->dbi->selectDb($db);
|
||||
$this->dbi->query($query);
|
||||
}
|
||||
|
||||
if (! empty($_REQUEST['pos'])) {
|
||||
$_REQUEST['pos'] = $sql->calculatePosForLastPage($db, $table, $_REQUEST['pos']);
|
||||
}
|
||||
|
||||
ForeignKey::handleDisableCheckCleanup($default_fk_check_value);
|
||||
|
||||
$disp_message = __('Your SQL query has been executed successfully.');
|
||||
$disp_query = $sql_query;
|
||||
}
|
||||
|
||||
$_url_params = $GLOBALS['urlParams'];
|
||||
$_url_params['goto'] = Url::getFromRoute('/table/sql');
|
||||
|
||||
if (isset($original_sql_query)) {
|
||||
$sql_query = $original_sql_query;
|
||||
}
|
||||
|
||||
$active_page = Url::getFromRoute('/sql');
|
||||
|
||||
$this->response->addHTML($sql->executeQueryAndSendQueryResponse(
|
||||
null,
|
||||
false,
|
||||
$db,
|
||||
$table,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
$goto,
|
||||
null,
|
||||
null,
|
||||
$sql_query,
|
||||
null
|
||||
));
|
||||
}
|
||||
}
|
|
@ -1,42 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Controllers\Table;
|
||||
|
||||
use PhpMyAdmin\DbTableExists;
|
||||
use PhpMyAdmin\Url;
|
||||
use PhpMyAdmin\Util;
|
||||
|
||||
use function __;
|
||||
|
||||
final class DropColumnConfirmationController extends AbstractController
|
||||
{
|
||||
public function __invoke(): void
|
||||
{
|
||||
global $urlParams, $errorUrl, $cfg;
|
||||
|
||||
$selected = $_POST['selected_fld'] ?? null;
|
||||
|
||||
if (empty($selected)) {
|
||||
$this->response->setRequestStatus(false);
|
||||
$this->response->addJSON('message', __('No column selected.'));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Util::checkParameters(['db', 'table']);
|
||||
|
||||
$urlParams = ['db' => $this->db, 'table' => $this->table];
|
||||
$errorUrl = Util::getScriptNameForOption($cfg['DefaultTabTable'], 'table');
|
||||
$errorUrl .= Url::getCommon($urlParams, '&');
|
||||
|
||||
DbTableExists::check();
|
||||
|
||||
$this->render('table/structure/drop_confirm', [
|
||||
'db' => $this->db,
|
||||
'table' => $this->table,
|
||||
'fields' => $selected,
|
||||
]);
|
||||
}
|
||||
}
|
|
@ -1,91 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Controllers\Table;
|
||||
|
||||
use PhpMyAdmin\ConfigStorage\RelationCleanup;
|
||||
use PhpMyAdmin\DatabaseInterface;
|
||||
use PhpMyAdmin\FlashMessages;
|
||||
use PhpMyAdmin\Message;
|
||||
use PhpMyAdmin\ResponseRenderer;
|
||||
use PhpMyAdmin\Template;
|
||||
use PhpMyAdmin\Util;
|
||||
|
||||
use function __;
|
||||
use function _ngettext;
|
||||
use function count;
|
||||
|
||||
final class DropColumnController extends AbstractController
|
||||
{
|
||||
/** @var DatabaseInterface */
|
||||
private $dbi;
|
||||
|
||||
/** @var FlashMessages */
|
||||
private $flash;
|
||||
|
||||
/** @var RelationCleanup */
|
||||
private $relationCleanup;
|
||||
|
||||
public function __construct(
|
||||
ResponseRenderer $response,
|
||||
Template $template,
|
||||
string $db,
|
||||
string $table,
|
||||
DatabaseInterface $dbi,
|
||||
FlashMessages $flash,
|
||||
RelationCleanup $relationCleanup
|
||||
) {
|
||||
parent::__construct($response, $template, $db, $table);
|
||||
$this->dbi = $dbi;
|
||||
$this->flash = $flash;
|
||||
$this->relationCleanup = $relationCleanup;
|
||||
}
|
||||
|
||||
public function __invoke(): void
|
||||
{
|
||||
$selected = $_POST['selected'] ?? [];
|
||||
|
||||
if (empty($selected)) {
|
||||
$this->response->setRequestStatus(false);
|
||||
$this->response->addJSON('message', __('No column selected.'));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$selectedCount = count($selected);
|
||||
if (($_POST['mult_btn'] ?? '') === __('Yes')) {
|
||||
$i = 1;
|
||||
$statement = 'ALTER TABLE ' . Util::backquote($this->table);
|
||||
|
||||
foreach ($selected as $field) {
|
||||
$this->relationCleanup->column($this->db, $this->table, $field);
|
||||
$statement .= ' DROP ' . Util::backquote($field);
|
||||
$statement .= $i++ === $selectedCount ? ';' : ',';
|
||||
}
|
||||
|
||||
$this->dbi->selectDb($this->db);
|
||||
$result = $this->dbi->tryQuery($statement);
|
||||
|
||||
if (! $result) {
|
||||
$message = Message::error($this->dbi->getError());
|
||||
}
|
||||
} else {
|
||||
$message = Message::success(__('No change'));
|
||||
}
|
||||
|
||||
if (empty($message)) {
|
||||
$message = Message::success(
|
||||
_ngettext(
|
||||
'%1$d column has been dropped successfully.',
|
||||
'%1$d columns have been dropped successfully.',
|
||||
$selectedCount
|
||||
)
|
||||
);
|
||||
$message->addParam($selectedCount);
|
||||
}
|
||||
|
||||
$this->flash->addMessage($message->isError() ? 'danger' : 'success', $message->getMessage());
|
||||
$this->redirect('/table/structure', ['db' => $this->db, 'table' => $this->table]);
|
||||
}
|
||||
}
|
|
@ -1,132 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Controllers\Table;
|
||||
|
||||
use PhpMyAdmin\Config\PageSettings;
|
||||
use PhpMyAdmin\Export\Options;
|
||||
use PhpMyAdmin\Message;
|
||||
use PhpMyAdmin\Plugins;
|
||||
use PhpMyAdmin\ResponseRenderer;
|
||||
use PhpMyAdmin\SqlParser\Parser;
|
||||
use PhpMyAdmin\SqlParser\Statements\SelectStatement;
|
||||
use PhpMyAdmin\SqlParser\Utils\Query;
|
||||
use PhpMyAdmin\Template;
|
||||
use PhpMyAdmin\Url;
|
||||
use PhpMyAdmin\Util;
|
||||
|
||||
use function __;
|
||||
use function array_merge;
|
||||
use function implode;
|
||||
use function is_array;
|
||||
|
||||
class ExportController extends AbstractController
|
||||
{
|
||||
/** @var Options */
|
||||
private $export;
|
||||
|
||||
public function __construct(
|
||||
ResponseRenderer $response,
|
||||
Template $template,
|
||||
string $db,
|
||||
string $table,
|
||||
Options $export
|
||||
) {
|
||||
parent::__construct($response, $template, $db, $table);
|
||||
$this->export = $export;
|
||||
}
|
||||
|
||||
public function __invoke(): void
|
||||
{
|
||||
global $db, $urlParams, $table, $replaces, $cfg, $errorUrl;
|
||||
global $sql_query, $where_clause, $num_tables, $unlim_num_rows;
|
||||
|
||||
$pageSettings = new PageSettings('Export');
|
||||
$pageSettingsErrorHtml = $pageSettings->getErrorHTML();
|
||||
$pageSettingsHtml = $pageSettings->getHTML();
|
||||
|
||||
$this->addScriptFiles(['export.js']);
|
||||
|
||||
Util::checkParameters(['db', 'table']);
|
||||
|
||||
$urlParams = ['db' => $db, 'table' => $table];
|
||||
$errorUrl = Util::getScriptNameForOption($cfg['DefaultTabTable'], 'table');
|
||||
$errorUrl .= Url::getCommon($urlParams, '&');
|
||||
|
||||
$urlParams['goto'] = Url::getFromRoute('/table/export');
|
||||
$urlParams['back'] = Url::getFromRoute('/table/export');
|
||||
|
||||
// When we have some query, we need to remove LIMIT from that and possibly
|
||||
// generate WHERE clause (if we are asked to export specific rows)
|
||||
|
||||
if (! empty($sql_query)) {
|
||||
$parser = new Parser($sql_query);
|
||||
|
||||
if (! empty($parser->statements[0]) && ($parser->statements[0] instanceof SelectStatement)) {
|
||||
// Checking if the WHERE clause has to be replaced.
|
||||
if (! empty($where_clause) && is_array($where_clause)) {
|
||||
$replaces[] = [
|
||||
'WHERE',
|
||||
'WHERE (' . implode(') OR (', $where_clause) . ')',
|
||||
];
|
||||
}
|
||||
|
||||
// Preparing to remove the LIMIT clause.
|
||||
$replaces[] = [
|
||||
'LIMIT',
|
||||
'',
|
||||
];
|
||||
|
||||
// Replacing the clauses.
|
||||
$sql_query = Query::replaceClauses($parser->statements[0], $parser->list, $replaces);
|
||||
}
|
||||
}
|
||||
|
||||
if (! isset($sql_query)) {
|
||||
$sql_query = '';
|
||||
}
|
||||
|
||||
if (! isset($num_tables)) {
|
||||
$num_tables = 0;
|
||||
}
|
||||
|
||||
if (! isset($unlim_num_rows)) {
|
||||
$unlim_num_rows = 0;
|
||||
}
|
||||
|
||||
$GLOBALS['single_table'] = $_POST['single_table'] ?? $_GET['single_table'] ?? $GLOBALS['single_table'] ?? null;
|
||||
|
||||
$exportList = Plugins::getExport('table', isset($GLOBALS['single_table']));
|
||||
|
||||
if (empty($exportList)) {
|
||||
$this->response->addHTML(Message::error(
|
||||
__('Could not load export plugins, please check your installation!')
|
||||
)->getDisplay());
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$exportType = 'table';
|
||||
$isReturnBackFromRawExport = isset($_POST['export_type']) && $_POST['export_type'] === 'raw';
|
||||
if (isset($_POST['raw_query']) || $isReturnBackFromRawExport) {
|
||||
$exportType = 'raw';
|
||||
}
|
||||
|
||||
$options = $this->export->getOptions(
|
||||
$exportType,
|
||||
$db,
|
||||
$table,
|
||||
$sql_query,
|
||||
$num_tables,
|
||||
$unlim_num_rows,
|
||||
$exportList
|
||||
);
|
||||
|
||||
$this->render('table/export/index', array_merge($options, [
|
||||
'export_type' => $exportType,
|
||||
'page_settings_error_html' => $pageSettingsErrorHtml,
|
||||
'page_settings_html' => $pageSettingsHtml,
|
||||
]));
|
||||
}
|
||||
}
|
|
@ -1,59 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Controllers\Table;
|
||||
|
||||
use PhpMyAdmin\ResponseRenderer;
|
||||
use PhpMyAdmin\Template;
|
||||
use PhpMyAdmin\Url;
|
||||
|
||||
use function __;
|
||||
use function is_array;
|
||||
|
||||
final class ExportRowsController extends AbstractController
|
||||
{
|
||||
/** @var ExportController */
|
||||
private $exportController;
|
||||
|
||||
public function __construct(
|
||||
ResponseRenderer $response,
|
||||
Template $template,
|
||||
string $db,
|
||||
string $table,
|
||||
ExportController $exportController
|
||||
) {
|
||||
parent::__construct($response, $template, $db, $table);
|
||||
$this->exportController = $exportController;
|
||||
}
|
||||
|
||||
public function __invoke(): void
|
||||
{
|
||||
global $active_page, $single_table, $where_clause;
|
||||
|
||||
if (isset($_POST['goto']) && (! isset($_POST['rows_to_delete']) || ! is_array($_POST['rows_to_delete']))) {
|
||||
$this->response->setRequestStatus(false);
|
||||
$this->response->addJSON('message', __('No row selected.'));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Needed to allow SQL export
|
||||
$single_table = true;
|
||||
|
||||
// As we got the rows to be exported from the
|
||||
// 'rows_to_delete' checkbox, we use the index of it as the
|
||||
// indicating WHERE clause. Then we build the array which is used
|
||||
// for the /table/change script.
|
||||
$where_clause = [];
|
||||
if (isset($_POST['rows_to_delete']) && is_array($_POST['rows_to_delete'])) {
|
||||
foreach ($_POST['rows_to_delete'] as $i_where_clause) {
|
||||
$where_clause[] = $i_where_clause;
|
||||
}
|
||||
}
|
||||
|
||||
$active_page = Url::getFromRoute('/table/export');
|
||||
|
||||
($this->exportController)();
|
||||
}
|
||||
}
|
|
@ -1,367 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Controllers\Table;
|
||||
|
||||
use PhpMyAdmin\DatabaseInterface;
|
||||
use PhpMyAdmin\DbTableExists;
|
||||
use PhpMyAdmin\Html\Generator;
|
||||
use PhpMyAdmin\ResponseRenderer;
|
||||
use PhpMyAdmin\Template;
|
||||
use PhpMyAdmin\Url;
|
||||
use PhpMyAdmin\Util;
|
||||
|
||||
use function __;
|
||||
use function array_key_exists;
|
||||
use function count;
|
||||
use function is_array;
|
||||
use function mb_strtolower;
|
||||
use function preg_match;
|
||||
use function preg_replace;
|
||||
use function str_contains;
|
||||
use function str_ireplace;
|
||||
use function str_replace;
|
||||
use function strncasecmp;
|
||||
|
||||
/**
|
||||
* Handles find and replace tab.
|
||||
*
|
||||
* Displays find and replace form, allows previewing and do the replacing.
|
||||
*/
|
||||
class FindReplaceController extends AbstractController
|
||||
{
|
||||
/** @var array */
|
||||
private $columnNames;
|
||||
|
||||
/** @var array */
|
||||
private $columnTypes;
|
||||
|
||||
/** @var string */
|
||||
private $connectionCharSet;
|
||||
|
||||
/** @var DatabaseInterface */
|
||||
private $dbi;
|
||||
|
||||
public function __construct(
|
||||
ResponseRenderer $response,
|
||||
Template $template,
|
||||
string $db,
|
||||
string $table,
|
||||
DatabaseInterface $dbi
|
||||
) {
|
||||
parent::__construct($response, $template, $db, $table);
|
||||
$this->dbi = $dbi;
|
||||
|
||||
$this->columnNames = [];
|
||||
$this->columnTypes = [];
|
||||
$this->loadTableInfo();
|
||||
$this->connectionCharSet = (string) $this->dbi->fetchValue('SELECT @@character_set_connection');
|
||||
}
|
||||
|
||||
public function __invoke(): void
|
||||
{
|
||||
global $db, $table, $urlParams, $cfg, $errorUrl;
|
||||
|
||||
Util::checkParameters(['db', 'table']);
|
||||
|
||||
$urlParams = ['db' => $db, 'table' => $table];
|
||||
$errorUrl = Util::getScriptNameForOption($cfg['DefaultTabTable'], 'table');
|
||||
$errorUrl .= Url::getCommon($urlParams, '&');
|
||||
|
||||
DbTableExists::check();
|
||||
|
||||
if (isset($_POST['find'])) {
|
||||
$this->findAction();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->addScriptFiles(['table/find_replace.js']);
|
||||
|
||||
if (isset($_POST['replace'])) {
|
||||
$this->replaceAction();
|
||||
}
|
||||
|
||||
// Displays the find and replace form
|
||||
$this->displaySelectionFormAction();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all the columns of a table along with their types.
|
||||
*/
|
||||
private function loadTableInfo(): void
|
||||
{
|
||||
// Gets the list and number of columns
|
||||
$columns = $this->dbi->getColumns($this->db, $this->table, true);
|
||||
|
||||
foreach ($columns as $row) {
|
||||
// set column name
|
||||
$this->columnNames[] = $row['Field'];
|
||||
|
||||
$type = (string) $row['Type'];
|
||||
// reformat mysql query output
|
||||
if (strncasecmp($type, 'set', 3) == 0 || strncasecmp($type, 'enum', 4) == 0) {
|
||||
$type = str_replace(',', ', ', $type);
|
||||
} else {
|
||||
// strip the "BINARY" attribute, except if we find "BINARY(" because
|
||||
// this would be a BINARY or VARBINARY column type
|
||||
if (! preg_match('@BINARY[\(]@i', $type)) {
|
||||
$type = str_ireplace('BINARY', '', $type);
|
||||
}
|
||||
|
||||
$type = str_ireplace('ZEROFILL', '', $type);
|
||||
$type = str_ireplace('UNSIGNED', '', $type);
|
||||
$type = mb_strtolower($type);
|
||||
}
|
||||
|
||||
if (empty($type)) {
|
||||
$type = ' ';
|
||||
}
|
||||
|
||||
$this->columnTypes[] = $type;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Display selection form action
|
||||
*/
|
||||
public function displaySelectionFormAction(): void
|
||||
{
|
||||
global $goto;
|
||||
|
||||
if (! isset($goto)) {
|
||||
$goto = Util::getScriptNameForOption($GLOBALS['cfg']['DefaultTabTable'], 'table');
|
||||
}
|
||||
|
||||
$column_names = $this->columnNames;
|
||||
$column_types = $this->columnTypes;
|
||||
$types = [];
|
||||
$num_cols = count($column_names);
|
||||
for ($i = 0; $i < $num_cols; $i++) {
|
||||
$types[$column_names[$i]] = preg_replace('@\\(.*@s', '', $column_types[$i]);
|
||||
}
|
||||
|
||||
$this->render('table/find_replace/index', [
|
||||
'db' => $this->db,
|
||||
'table' => $this->table,
|
||||
'goto' => $goto,
|
||||
'column_names' => $column_names,
|
||||
'types' => $types,
|
||||
'sql_types' => $this->dbi->types,
|
||||
]);
|
||||
}
|
||||
|
||||
public function findAction(): void
|
||||
{
|
||||
$useRegex = array_key_exists('useRegex', $_POST)
|
||||
&& $_POST['useRegex'] === 'on';
|
||||
|
||||
$preview = $this->getReplacePreview(
|
||||
$_POST['columnIndex'],
|
||||
$_POST['find'],
|
||||
$_POST['replaceWith'],
|
||||
$useRegex,
|
||||
$this->connectionCharSet
|
||||
);
|
||||
$this->response->addJSON('preview', $preview);
|
||||
}
|
||||
|
||||
public function replaceAction(): void
|
||||
{
|
||||
$this->replace(
|
||||
$_POST['columnIndex'],
|
||||
$_POST['findString'],
|
||||
$_POST['replaceWith'],
|
||||
$_POST['useRegex'],
|
||||
$this->connectionCharSet
|
||||
);
|
||||
$this->response->addHTML(
|
||||
Generator::getMessage(
|
||||
__('Your SQL query has been executed successfully.'),
|
||||
null,
|
||||
'success'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns HTML for previewing strings found and their replacements
|
||||
*
|
||||
* @param int $columnIndex index of the column
|
||||
* @param string $find string to find in the column
|
||||
* @param string $replaceWith string to replace with
|
||||
* @param bool $useRegex to use Regex replace or not
|
||||
* @param string $charSet character set of the connection
|
||||
*
|
||||
* @return string HTML for previewing strings found and their replacements
|
||||
*/
|
||||
public function getReplacePreview(
|
||||
$columnIndex,
|
||||
$find,
|
||||
$replaceWith,
|
||||
$useRegex,
|
||||
$charSet
|
||||
) {
|
||||
$column = $this->columnNames[$columnIndex];
|
||||
if ($useRegex) {
|
||||
$result = $this->getRegexReplaceRows($columnIndex, $find, $replaceWith, $charSet);
|
||||
} else {
|
||||
$sql_query = 'SELECT '
|
||||
. Util::backquote($column) . ','
|
||||
. ' REPLACE('
|
||||
. Util::backquote($column) . ", '" . $find . "', '"
|
||||
. $replaceWith
|
||||
. "'),"
|
||||
. ' COUNT(*)'
|
||||
. ' FROM ' . Util::backquote($this->db)
|
||||
. '.' . Util::backquote($this->table)
|
||||
. ' WHERE ' . Util::backquote($column)
|
||||
. " LIKE '%" . $find . "%' COLLATE " . $charSet . '_bin'; // here we
|
||||
// change the collation of the 2nd operand to a case sensitive
|
||||
// binary collation to make sure that the comparison
|
||||
// is case sensitive
|
||||
$sql_query .= ' GROUP BY ' . Util::backquote($column)
|
||||
. ' ORDER BY ' . Util::backquote($column) . ' ASC';
|
||||
|
||||
$result = $this->dbi->fetchResult($sql_query, 0);
|
||||
}
|
||||
|
||||
return $this->template->render('table/find_replace/replace_preview', [
|
||||
'db' => $this->db,
|
||||
'table' => $this->table,
|
||||
'column_index' => $columnIndex,
|
||||
'find' => $find,
|
||||
'replace_with' => $replaceWith,
|
||||
'use_regex' => $useRegex,
|
||||
'result' => $result,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds and returns Regex pattern and their replacements
|
||||
*
|
||||
* @param int $columnIndex index of the column
|
||||
* @param string $find string to find in the column
|
||||
* @param string $replaceWith string to replace with
|
||||
* @param string $charSet character set of the connection
|
||||
*
|
||||
* @return array|bool Array containing original values, replaced values and count
|
||||
*/
|
||||
private function getRegexReplaceRows(
|
||||
$columnIndex,
|
||||
$find,
|
||||
$replaceWith,
|
||||
$charSet
|
||||
) {
|
||||
$column = $this->columnNames[$columnIndex];
|
||||
$sql_query = 'SELECT '
|
||||
. Util::backquote($column) . ','
|
||||
. ' 1,' // to add an extra column that will have replaced value
|
||||
. ' COUNT(*)'
|
||||
. ' FROM ' . Util::backquote($this->db)
|
||||
. '.' . Util::backquote($this->table)
|
||||
. ' WHERE ' . Util::backquote($column)
|
||||
. " RLIKE '" . $this->dbi->escapeString($find) . "' COLLATE "
|
||||
. $charSet . '_bin'; // here we
|
||||
// change the collation of the 2nd operand to a case sensitive
|
||||
// binary collation to make sure that the comparison is case sensitive
|
||||
$sql_query .= ' GROUP BY ' . Util::backquote($column)
|
||||
. ' ORDER BY ' . Util::backquote($column) . ' ASC';
|
||||
|
||||
$result = $this->dbi->fetchResult($sql_query, 0);
|
||||
|
||||
/* Iterate over possible delimiters to get one */
|
||||
$delimiters = [
|
||||
'/',
|
||||
'@',
|
||||
'#',
|
||||
'~',
|
||||
'!',
|
||||
'$',
|
||||
'%',
|
||||
'^',
|
||||
'&',
|
||||
'_',
|
||||
];
|
||||
$found = false;
|
||||
for ($i = 0, $l = count($delimiters); $i < $l; $i++) {
|
||||
if (! str_contains($find, $delimiters[$i])) {
|
||||
$found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (! $found) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$find = $delimiters[$i] . $find . $delimiters[$i];
|
||||
foreach ($result as $index => $row) {
|
||||
$result[$index][1] = preg_replace($find, $replaceWith, $row[0]);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces a given string in a column with a give replacement
|
||||
*
|
||||
* @param int $columnIndex index of the column
|
||||
* @param string $find string to find in the column
|
||||
* @param string $replaceWith string to replace with
|
||||
* @param bool $useRegex to use Regex replace or not
|
||||
* @param string $charSet character set of the connection
|
||||
*/
|
||||
public function replace(
|
||||
$columnIndex,
|
||||
$find,
|
||||
$replaceWith,
|
||||
$useRegex,
|
||||
$charSet
|
||||
): void {
|
||||
$column = $this->columnNames[$columnIndex];
|
||||
if ($useRegex) {
|
||||
$toReplace = $this->getRegexReplaceRows($columnIndex, $find, $replaceWith, $charSet);
|
||||
$sql_query = 'UPDATE ' . Util::backquote($this->table)
|
||||
. ' SET ' . Util::backquote($column);
|
||||
|
||||
if (is_array($toReplace)) {
|
||||
if (count($toReplace) > 0) {
|
||||
$sql_query .= ' = CASE';
|
||||
foreach ($toReplace as $row) {
|
||||
$sql_query .= "\n WHEN " . Util::backquote($column)
|
||||
. " = '" . $this->dbi->escapeString($row[0])
|
||||
. "' THEN '" . $this->dbi->escapeString($row[1]) . "'";
|
||||
}
|
||||
|
||||
$sql_query .= ' END';
|
||||
} else {
|
||||
$sql_query .= ' = ' . Util::backquote($column);
|
||||
}
|
||||
}
|
||||
|
||||
$sql_query .= ' WHERE ' . Util::backquote($column)
|
||||
. " RLIKE '" . $this->dbi->escapeString($find) . "' COLLATE "
|
||||
. $charSet . '_bin'; // here we
|
||||
// change the collation of the 2nd operand to a case sensitive
|
||||
// binary collation to make sure that the comparison
|
||||
// is case sensitive
|
||||
} else {
|
||||
$sql_query = 'UPDATE ' . Util::backquote($this->table)
|
||||
. ' SET ' . Util::backquote($column) . ' ='
|
||||
. ' REPLACE('
|
||||
. Util::backquote($column) . ", '" . $find . "', '"
|
||||
. $replaceWith
|
||||
. "')"
|
||||
. ' WHERE ' . Util::backquote($column)
|
||||
. " LIKE '%" . $find . "%' COLLATE " . $charSet . '_bin'; // here we
|
||||
// change the collation of the 2nd operand to a case sensitive
|
||||
// binary collation to make sure that the comparison
|
||||
// is case sensitive
|
||||
}
|
||||
|
||||
$this->dbi->query($sql_query);
|
||||
$GLOBALS['sql_query'] = $sql_query;
|
||||
}
|
||||
}
|
|
@ -1,103 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Controllers\Table;
|
||||
|
||||
use PhpMyAdmin\Core;
|
||||
use PhpMyAdmin\DatabaseInterface;
|
||||
use PhpMyAdmin\Html\Generator;
|
||||
use PhpMyAdmin\Mime;
|
||||
use PhpMyAdmin\ResponseRenderer;
|
||||
use PhpMyAdmin\Template;
|
||||
use PhpMyAdmin\Util;
|
||||
|
||||
use function __;
|
||||
use function htmlspecialchars;
|
||||
use function ini_set;
|
||||
use function sprintf;
|
||||
use function strlen;
|
||||
|
||||
/**
|
||||
* Provides download to a given field defined in parameters.
|
||||
*/
|
||||
class GetFieldController extends AbstractController
|
||||
{
|
||||
/** @var DatabaseInterface */
|
||||
private $dbi;
|
||||
|
||||
public function __construct(
|
||||
ResponseRenderer $response,
|
||||
Template $template,
|
||||
string $db,
|
||||
string $table,
|
||||
DatabaseInterface $dbi
|
||||
) {
|
||||
parent::__construct($response, $template, $db, $table);
|
||||
$this->dbi = $dbi;
|
||||
}
|
||||
|
||||
public function __invoke(): void
|
||||
{
|
||||
global $db, $table;
|
||||
|
||||
$this->response->disable();
|
||||
|
||||
/* Check parameters */
|
||||
Util::checkParameters([
|
||||
'db',
|
||||
'table',
|
||||
]);
|
||||
|
||||
/* Select database */
|
||||
if (! $this->dbi->selectDb($db)) {
|
||||
Generator::mysqlDie(
|
||||
sprintf(__('\'%s\' database does not exist.'), htmlspecialchars($db)),
|
||||
'',
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
/* Check if table exists */
|
||||
if (! $this->dbi->getColumns($db, $table)) {
|
||||
Generator::mysqlDie(__('Invalid table name'));
|
||||
}
|
||||
|
||||
if (
|
||||
! isset($_GET['where_clause'])
|
||||
|| ! isset($_GET['where_clause_sign'])
|
||||
|| ! Core::checkSqlQuerySignature($_GET['where_clause'], $_GET['where_clause_sign'])
|
||||
) {
|
||||
/* l10n: In case a SQL query did not pass a security check */
|
||||
Core::fatalError(__('There is an issue with your request.'));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* Grab data */
|
||||
$sql = 'SELECT ' . Util::backquote($_GET['transform_key'])
|
||||
. ' FROM ' . Util::backquote($table)
|
||||
. ' WHERE ' . $_GET['where_clause'] . ';';
|
||||
$result = $this->dbi->fetchValue($sql);
|
||||
|
||||
/* Check return code */
|
||||
if ($result === false) {
|
||||
Generator::mysqlDie(
|
||||
__('MySQL returned an empty result set (i.e. zero rows).'),
|
||||
$sql
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* Avoid corrupting data */
|
||||
ini_set('url_rewriter.tags', '');
|
||||
|
||||
Core::downloadHeader(
|
||||
$table . '-' . $_GET['transform_key'] . '.bin',
|
||||
Mime::detect($result),
|
||||
strlen($result)
|
||||
);
|
||||
echo $result;
|
||||
}
|
||||
}
|
|
@ -1,203 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Controllers\Table;
|
||||
|
||||
use PhpMyAdmin\Core;
|
||||
use PhpMyAdmin\DatabaseInterface;
|
||||
use PhpMyAdmin\Gis\GisVisualization;
|
||||
use PhpMyAdmin\Html\Generator;
|
||||
use PhpMyAdmin\Message;
|
||||
use PhpMyAdmin\ResponseRenderer;
|
||||
use PhpMyAdmin\Template;
|
||||
use PhpMyAdmin\Url;
|
||||
use PhpMyAdmin\Util;
|
||||
|
||||
use function __;
|
||||
use function array_merge;
|
||||
use function is_array;
|
||||
|
||||
/**
|
||||
* Handles creation of the GIS visualizations.
|
||||
*/
|
||||
final class GisVisualizationController extends AbstractController
|
||||
{
|
||||
/** @var GisVisualization */
|
||||
private $visualization;
|
||||
|
||||
/** @var DatabaseInterface */
|
||||
private $dbi;
|
||||
|
||||
public function __construct(
|
||||
ResponseRenderer $response,
|
||||
Template $template,
|
||||
string $db,
|
||||
string $table,
|
||||
DatabaseInterface $dbi
|
||||
) {
|
||||
parent::__construct($response, $template, $db, $table);
|
||||
$this->dbi = $dbi;
|
||||
}
|
||||
|
||||
public function __invoke(): void
|
||||
{
|
||||
global $cfg, $urlParams, $db, $errorUrl;
|
||||
|
||||
Util::checkParameters(['db']);
|
||||
|
||||
$errorUrl = Util::getScriptNameForOption($cfg['DefaultTabDatabase'], 'database');
|
||||
$errorUrl .= Url::getCommon(['db' => $db], '&');
|
||||
|
||||
if (! $this->hasDatabase()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// SQL query for retrieving GIS data
|
||||
$sqlQuery = '';
|
||||
if (isset($_GET['sql_query'], $_GET['sql_signature'])) {
|
||||
if (Core::checkSqlQuerySignature($_GET['sql_query'], $_GET['sql_signature'])) {
|
||||
$sqlQuery = $_GET['sql_query'];
|
||||
}
|
||||
} elseif (isset($_POST['sql_query'])) {
|
||||
$sqlQuery = $_POST['sql_query'];
|
||||
}
|
||||
|
||||
// Throw error if no sql query is set
|
||||
if ($sqlQuery == '') {
|
||||
$this->response->setRequestStatus(false);
|
||||
$this->response->addHTML(
|
||||
Message::error(__('No SQL query was set to fetch data.'))->getDisplay()
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Execute the query and return the result
|
||||
$result = $this->dbi->tryQuery($sqlQuery);
|
||||
// Get the meta data of results
|
||||
$meta = [];
|
||||
if ($result !== false) {
|
||||
$meta = $this->dbi->getFieldsMeta($result);
|
||||
}
|
||||
|
||||
// Find the candidate fields for label column and spatial column
|
||||
$labelCandidates = [];
|
||||
$spatialCandidates = [];
|
||||
foreach ($meta as $column_meta) {
|
||||
if ($column_meta->isMappedTypeGeometry) {
|
||||
$spatialCandidates[] = $column_meta->name;
|
||||
} else {
|
||||
$labelCandidates[] = $column_meta->name;
|
||||
}
|
||||
}
|
||||
|
||||
// Get settings if any posted
|
||||
$visualizationSettings = [];
|
||||
// Download as PNG/SVG/PDF use _GET and the normal form uses _POST
|
||||
if (isset($_POST['visualizationSettings']) && is_array($_POST['visualizationSettings'])) {
|
||||
$visualizationSettings = $_POST['visualizationSettings'];
|
||||
} elseif (isset($_GET['visualizationSettings']) && is_array($_GET['visualizationSettings'])) {
|
||||
$visualizationSettings = $_GET['visualizationSettings'];
|
||||
}
|
||||
|
||||
// Check mysql version
|
||||
$visualizationSettings['mysqlVersion'] = $this->dbi->getVersion();
|
||||
$visualizationSettings['isMariaDB'] = $this->dbi->isMariaDB();
|
||||
|
||||
if (! isset($visualizationSettings['labelColumn']) && isset($labelCandidates[0])) {
|
||||
$visualizationSettings['labelColumn'] = '';
|
||||
}
|
||||
|
||||
// If spatial column is not set, use first geometric column as spatial column
|
||||
if (! isset($visualizationSettings['spatialColumn'])) {
|
||||
$visualizationSettings['spatialColumn'] = $spatialCandidates[0];
|
||||
}
|
||||
|
||||
// Download as PNG/SVG/PDF use _GET and the normal form uses _POST
|
||||
// Convert geometric columns from bytes to text.
|
||||
$pos = (int) ($_POST['pos'] ?? $_GET['pos'] ?? $_SESSION['tmpval']['pos']);
|
||||
if (isset($_POST['session_max_rows']) || isset($_GET['session_max_rows'])) {
|
||||
$rows = (int) ($_POST['session_max_rows'] ?? $_GET['session_max_rows']);
|
||||
} else {
|
||||
if ($_SESSION['tmpval']['max_rows'] !== 'all') {
|
||||
$rows = (int) $_SESSION['tmpval']['max_rows'];
|
||||
} else {
|
||||
$rows = (int) $GLOBALS['cfg']['MaxRows'];
|
||||
}
|
||||
}
|
||||
|
||||
$this->visualization = GisVisualization::get($sqlQuery, $visualizationSettings, $rows, $pos);
|
||||
|
||||
if (isset($_GET['saveToFile'])) {
|
||||
$this->saveToFile($visualizationSettings['spatialColumn'], $_GET['fileFormat']);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->addScriptFiles(['vendor/openlayers/OpenLayers.js', 'table/gis_visualization.js']);
|
||||
|
||||
// If all the rows contain SRID, use OpenStreetMaps on the initial loading.
|
||||
if (! isset($_POST['displayVisualization'])) {
|
||||
if ($this->visualization->hasSrid()) {
|
||||
$visualizationSettings['choice'] = 'useBaseLayer';
|
||||
} else {
|
||||
unset($visualizationSettings['choice']);
|
||||
}
|
||||
}
|
||||
|
||||
$this->visualization->setUserSpecifiedSettings($visualizationSettings);
|
||||
if ($visualizationSettings != null) {
|
||||
foreach ($this->visualization->getSettings() as $setting => $val) {
|
||||
if (isset($visualizationSettings[$setting])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$visualizationSettings[$setting] = $val;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays the page
|
||||
*/
|
||||
$urlParams['goto'] = Util::getScriptNameForOption($cfg['DefaultTabDatabase'], 'database');
|
||||
$urlParams['back'] = Url::getFromRoute('/sql');
|
||||
$urlParams['sql_query'] = $sqlQuery;
|
||||
$urlParams['sql_signature'] = Core::signSqlQuery($sqlQuery);
|
||||
$downloadUrl = Url::getFromRoute('/table/gis-visualization', array_merge(
|
||||
$urlParams,
|
||||
[
|
||||
'saveToFile' => true,
|
||||
'session_max_rows' => $rows,
|
||||
'pos' => $pos,
|
||||
'visualizationSettings[spatialColumn]' => $visualizationSettings['spatialColumn'],
|
||||
'visualizationSettings[labelColumn]' => $visualizationSettings['labelColumn'] ?? null,
|
||||
]
|
||||
));
|
||||
|
||||
$startAndNumberOfRowsFieldset = Generator::getStartAndNumberOfRowsFieldsetData($sqlQuery);
|
||||
|
||||
$html = $this->template->render('table/gis_visualization/gis_visualization', [
|
||||
'url_params' => $urlParams,
|
||||
'download_url' => $downloadUrl,
|
||||
'label_candidates' => $labelCandidates,
|
||||
'spatial_candidates' => $spatialCandidates,
|
||||
'visualization_settings' => $visualizationSettings,
|
||||
'start_and_number_of_rows_fieldset' => $startAndNumberOfRowsFieldset,
|
||||
'visualization' => $this->visualization->toImage('svg'),
|
||||
'draw_ol' => $this->visualization->asOl(),
|
||||
]);
|
||||
|
||||
$this->response->addHTML($html);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $filename File name
|
||||
* @param string $format Save format
|
||||
*/
|
||||
private function saveToFile(string $filename, string $format): void
|
||||
{
|
||||
$this->response->disable();
|
||||
$this->visualization->toFile($filename, $format);
|
||||
}
|
||||
}
|
|
@ -1,132 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Controllers\Table;
|
||||
|
||||
use PhpMyAdmin\Charsets;
|
||||
use PhpMyAdmin\Config\PageSettings;
|
||||
use PhpMyAdmin\DatabaseInterface;
|
||||
use PhpMyAdmin\DbTableExists;
|
||||
use PhpMyAdmin\Encoding;
|
||||
use PhpMyAdmin\Import;
|
||||
use PhpMyAdmin\Import\Ajax;
|
||||
use PhpMyAdmin\Message;
|
||||
use PhpMyAdmin\Plugins;
|
||||
use PhpMyAdmin\ResponseRenderer;
|
||||
use PhpMyAdmin\Template;
|
||||
use PhpMyAdmin\Url;
|
||||
use PhpMyAdmin\Util;
|
||||
use PhpMyAdmin\Utils\ForeignKey;
|
||||
|
||||
use function __;
|
||||
use function intval;
|
||||
use function is_numeric;
|
||||
|
||||
final class ImportController extends AbstractController
|
||||
{
|
||||
/** @var DatabaseInterface */
|
||||
private $dbi;
|
||||
|
||||
public function __construct(
|
||||
ResponseRenderer $response,
|
||||
Template $template,
|
||||
string $db,
|
||||
string $table,
|
||||
DatabaseInterface $dbi
|
||||
) {
|
||||
parent::__construct($response, $template, $db, $table);
|
||||
$this->dbi = $dbi;
|
||||
}
|
||||
|
||||
public function __invoke(): void
|
||||
{
|
||||
global $db, $table, $urlParams, $SESSION_KEY, $cfg, $errorUrl;
|
||||
|
||||
$pageSettings = new PageSettings('Import');
|
||||
$pageSettingsErrorHtml = $pageSettings->getErrorHTML();
|
||||
$pageSettingsHtml = $pageSettings->getHTML();
|
||||
|
||||
$this->addScriptFiles(['import.js']);
|
||||
|
||||
Util::checkParameters(['db', 'table']);
|
||||
|
||||
$urlParams = ['db' => $db, 'table' => $table];
|
||||
$errorUrl = Util::getScriptNameForOption($cfg['DefaultTabTable'], 'table');
|
||||
$errorUrl .= Url::getCommon($urlParams, '&');
|
||||
|
||||
DbTableExists::check();
|
||||
|
||||
$urlParams['goto'] = Url::getFromRoute('/table/import');
|
||||
$urlParams['back'] = Url::getFromRoute('/table/import');
|
||||
|
||||
[$SESSION_KEY, $uploadId] = Ajax::uploadProgressSetup();
|
||||
|
||||
$importList = Plugins::getImport('table');
|
||||
|
||||
if (empty($importList)) {
|
||||
$this->response->addHTML(Message::error(__(
|
||||
'Could not load import plugins, please check your installation!'
|
||||
))->getDisplay());
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$offset = null;
|
||||
if (isset($_REQUEST['offset']) && is_numeric($_REQUEST['offset'])) {
|
||||
$offset = intval($_REQUEST['offset']);
|
||||
}
|
||||
|
||||
$timeoutPassed = $_REQUEST['timeout_passed'] ?? null;
|
||||
$localImportFile = $_REQUEST['local_import_file'] ?? null;
|
||||
$compressions = Import::getCompressions();
|
||||
|
||||
$charsets = Charsets::getCharsets($this->dbi, $cfg['Server']['DisableIS']);
|
||||
|
||||
$idKey = $_SESSION[$SESSION_KEY]['handler']::getIdKey();
|
||||
$hiddenInputs = [
|
||||
$idKey => $uploadId,
|
||||
'import_type' => 'table',
|
||||
'db' => $db,
|
||||
'table' => $table,
|
||||
];
|
||||
|
||||
$default = isset($_GET['format']) ? (string) $_GET['format'] : Plugins::getDefault('Import', 'format');
|
||||
$choice = Plugins::getChoice($importList, $default);
|
||||
$options = Plugins::getOptions('Import', $importList);
|
||||
$skipQueriesDefault = Plugins::getDefault('Import', 'skip_queries');
|
||||
$isAllowInterruptChecked = Plugins::checkboxCheck('Import', 'allow_interrupt');
|
||||
$maxUploadSize = (int) $GLOBALS['config']->get('max_upload_size');
|
||||
|
||||
$this->render('table/import/index', [
|
||||
'page_settings_error_html' => $pageSettingsErrorHtml,
|
||||
'page_settings_html' => $pageSettingsHtml,
|
||||
'upload_id' => $uploadId,
|
||||
'handler' => $_SESSION[$SESSION_KEY]['handler'],
|
||||
'hidden_inputs' => $hiddenInputs,
|
||||
'db' => $db,
|
||||
'table' => $table,
|
||||
'max_upload_size' => $maxUploadSize,
|
||||
'formatted_maximum_upload_size' => Util::getFormattedMaximumUploadSize($maxUploadSize),
|
||||
'plugins_choice' => $choice,
|
||||
'options' => $options,
|
||||
'skip_queries_default' => $skipQueriesDefault,
|
||||
'is_allow_interrupt_checked' => $isAllowInterruptChecked,
|
||||
'local_import_file' => $localImportFile,
|
||||
'is_upload' => $GLOBALS['config']->get('enable_upload'),
|
||||
'upload_dir' => $cfg['UploadDir'] ?? null,
|
||||
'timeout_passed_global' => $GLOBALS['timeout_passed'] ?? null,
|
||||
'compressions' => $compressions,
|
||||
'is_encoding_supported' => Encoding::isSupported(),
|
||||
'encodings' => Encoding::listEncodings(),
|
||||
'import_charset' => $cfg['Import']['charset'] ?? null,
|
||||
'timeout_passed' => $timeoutPassed,
|
||||
'offset' => $offset,
|
||||
'can_convert_kanji' => Encoding::canConvertKanji(),
|
||||
'charsets' => $charsets,
|
||||
'is_foreign_key_check' => ForeignKey::isCheckEnabled(),
|
||||
'user_upload_dir' => Util::userDir((string) ($cfg['UploadDir'] ?? '')),
|
||||
'local_files' => Import::getLocalFiles($importList),
|
||||
]);
|
||||
}
|
||||
}
|
|
@ -1,100 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Controllers\Table;
|
||||
|
||||
use PhpMyAdmin\DatabaseInterface;
|
||||
use PhpMyAdmin\DbTableExists;
|
||||
use PhpMyAdmin\Index;
|
||||
use PhpMyAdmin\ResponseRenderer;
|
||||
use PhpMyAdmin\Table\Indexes;
|
||||
use PhpMyAdmin\Template;
|
||||
use PhpMyAdmin\Url;
|
||||
use PhpMyAdmin\Util;
|
||||
|
||||
use function is_array;
|
||||
|
||||
final class IndexRenameController extends AbstractController
|
||||
{
|
||||
/** @var DatabaseInterface */
|
||||
private $dbi;
|
||||
|
||||
/** @var Indexes */
|
||||
private $indexes;
|
||||
|
||||
public function __construct(
|
||||
ResponseRenderer $response,
|
||||
Template $template,
|
||||
string $db,
|
||||
string $table,
|
||||
DatabaseInterface $dbi,
|
||||
Indexes $indexes
|
||||
) {
|
||||
parent::__construct($response, $template, $db, $table);
|
||||
$this->dbi = $dbi;
|
||||
$this->indexes = $indexes;
|
||||
}
|
||||
|
||||
public function __invoke(): void
|
||||
{
|
||||
global $db, $table, $urlParams, $cfg, $errorUrl;
|
||||
|
||||
if (! isset($_POST['create_edit_table'])) {
|
||||
Util::checkParameters(['db', 'table']);
|
||||
|
||||
$urlParams = ['db' => $db, 'table' => $table];
|
||||
$errorUrl = Util::getScriptNameForOption($cfg['DefaultTabTable'], 'table');
|
||||
$errorUrl .= Url::getCommon($urlParams, '&');
|
||||
|
||||
DbTableExists::check();
|
||||
}
|
||||
|
||||
if (isset($_POST['index'])) {
|
||||
if (is_array($_POST['index'])) {
|
||||
// coming already from form
|
||||
$index = new Index($_POST['index']);
|
||||
} else {
|
||||
$index = $this->dbi->getTable($this->db, $this->table)->getIndex($_POST['index']);
|
||||
}
|
||||
} else {
|
||||
$index = new Index();
|
||||
}
|
||||
|
||||
if (isset($_POST['do_save_data'])) {
|
||||
$this->indexes->doSaveData($index, true, $this->db, $this->table);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->displayRenameForm($index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the rename form to rename an index
|
||||
*
|
||||
* @param Index $index An Index instance.
|
||||
*/
|
||||
private function displayRenameForm(Index $index): void
|
||||
{
|
||||
$this->dbi->selectDb($GLOBALS['db']);
|
||||
|
||||
$formParams = [
|
||||
'db' => $this->db,
|
||||
'table' => $this->table,
|
||||
];
|
||||
|
||||
if (isset($_POST['old_index'])) {
|
||||
$formParams['old_index'] = $_POST['old_index'];
|
||||
} elseif (isset($_POST['index'])) {
|
||||
$formParams['old_index'] = $_POST['index'];
|
||||
}
|
||||
|
||||
$this->addScriptFiles(['indexes.js']);
|
||||
|
||||
$this->render('table/index_rename_form', [
|
||||
'index' => $index,
|
||||
'form_params' => $formParams,
|
||||
]);
|
||||
}
|
||||
}
|
|
@ -1,153 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Controllers\Table;
|
||||
|
||||
use PhpMyAdmin\DatabaseInterface;
|
||||
use PhpMyAdmin\DbTableExists;
|
||||
use PhpMyAdmin\Index;
|
||||
use PhpMyAdmin\ResponseRenderer;
|
||||
use PhpMyAdmin\Table\Indexes;
|
||||
use PhpMyAdmin\Template;
|
||||
use PhpMyAdmin\Url;
|
||||
use PhpMyAdmin\Util;
|
||||
|
||||
use function count;
|
||||
use function is_array;
|
||||
use function is_numeric;
|
||||
use function json_decode;
|
||||
use function min;
|
||||
|
||||
/**
|
||||
* Displays index edit/creation form and handles it.
|
||||
*/
|
||||
class IndexesController extends AbstractController
|
||||
{
|
||||
/** @var DatabaseInterface */
|
||||
private $dbi;
|
||||
|
||||
/** @var Indexes */
|
||||
private $indexes;
|
||||
|
||||
public function __construct(
|
||||
ResponseRenderer $response,
|
||||
Template $template,
|
||||
string $db,
|
||||
string $table,
|
||||
DatabaseInterface $dbi,
|
||||
Indexes $indexes
|
||||
) {
|
||||
parent::__construct($response, $template, $db, $table);
|
||||
$this->dbi = $dbi;
|
||||
$this->indexes = $indexes;
|
||||
}
|
||||
|
||||
public function __invoke(): void
|
||||
{
|
||||
global $db, $table, $urlParams, $cfg, $errorUrl;
|
||||
|
||||
if (! isset($_POST['create_edit_table'])) {
|
||||
Util::checkParameters(['db', 'table']);
|
||||
|
||||
$urlParams = ['db' => $db, 'table' => $table];
|
||||
$errorUrl = Util::getScriptNameForOption($cfg['DefaultTabTable'], 'table');
|
||||
$errorUrl .= Url::getCommon($urlParams, '&');
|
||||
|
||||
DbTableExists::check();
|
||||
}
|
||||
|
||||
if (isset($_POST['index'])) {
|
||||
if (is_array($_POST['index'])) {
|
||||
// coming already from form
|
||||
$index = new Index($_POST['index']);
|
||||
} else {
|
||||
$index = $this->dbi->getTable($this->db, $this->table)->getIndex($_POST['index']);
|
||||
}
|
||||
} else {
|
||||
$index = new Index();
|
||||
}
|
||||
|
||||
if (isset($_POST['do_save_data'])) {
|
||||
$this->indexes->doSaveData($index, false, $this->db, $this->table);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->displayForm($index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the form to edit/create an index
|
||||
*
|
||||
* @param Index $index An Index instance.
|
||||
*/
|
||||
private function displayForm(Index $index): void
|
||||
{
|
||||
$this->dbi->selectDb($GLOBALS['db']);
|
||||
$add_fields = 0;
|
||||
if (isset($_POST['index']) && is_array($_POST['index'])) {
|
||||
// coming already from form
|
||||
if (isset($_POST['index']['columns']['names'])) {
|
||||
$add_fields = count($_POST['index']['columns']['names'])
|
||||
- $index->getColumnCount();
|
||||
}
|
||||
|
||||
if (isset($_POST['add_fields'])) {
|
||||
$add_fields += $_POST['added_fields'];
|
||||
}
|
||||
} elseif (isset($_POST['create_index'])) {
|
||||
/**
|
||||
* In most cases, an index may consist of up to 16 columns, so add an initial limit.
|
||||
* More columns could be added later if necessary.
|
||||
*
|
||||
* @see https://dev.mysql.com/doc/refman/5.6/en/multiple-column-indexes.html "up to 16 columns"
|
||||
* @see https://mariadb.com/kb/en/innodb-limitations/#limitations-on-schema "maximum of 16 columns"
|
||||
* @see https://mariadb.com/kb/en/myisam-overview/#myisam-features "Maximum of 32 columns per index"
|
||||
*/
|
||||
$add_fields = 1;
|
||||
if (is_numeric($_POST['added_fields']) && $_POST['added_fields'] >= 2) {
|
||||
$add_fields = min((int) $_POST['added_fields'], 16);
|
||||
}
|
||||
}
|
||||
|
||||
// Get fields and stores their name/type
|
||||
if (isset($_POST['create_edit_table'])) {
|
||||
$fields = json_decode($_POST['columns'], true);
|
||||
$index_params = [
|
||||
'Non_unique' => $_POST['index']['Index_choice'] === 'UNIQUE'
|
||||
? '0' : '1',
|
||||
];
|
||||
$index->set($index_params);
|
||||
$add_fields = count($fields);
|
||||
} else {
|
||||
$fields = $this->dbi->getTable($this->db, $this->table)
|
||||
->getNameAndTypeOfTheColumns();
|
||||
}
|
||||
|
||||
$form_params = [
|
||||
'db' => $this->db,
|
||||
'table' => $this->table,
|
||||
];
|
||||
|
||||
if (isset($_POST['create_index'])) {
|
||||
$form_params['create_index'] = 1;
|
||||
} elseif (isset($_POST['old_index'])) {
|
||||
$form_params['old_index'] = $_POST['old_index'];
|
||||
} elseif (isset($_POST['index'])) {
|
||||
$form_params['old_index'] = $_POST['index'];
|
||||
}
|
||||
|
||||
$this->addScriptFiles(['indexes.js']);
|
||||
|
||||
$this->render('table/index_form', [
|
||||
'fields' => $fields,
|
||||
'index' => $index,
|
||||
'form_params' => $form_params,
|
||||
'add_fields' => $add_fields,
|
||||
'create_edit_table' => isset($_POST['create_edit_table']),
|
||||
'default_sliders_state' => $GLOBALS['cfg']['InitialSlidersState'],
|
||||
'is_from_nav' => isset($_POST['is_from_nav']),
|
||||
]);
|
||||
}
|
||||
}
|
|
@ -1,93 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Controllers\Table\Maintenance;
|
||||
|
||||
use PhpMyAdmin\Config;
|
||||
use PhpMyAdmin\Controllers\Table\AbstractController;
|
||||
use PhpMyAdmin\Dbal\DatabaseName;
|
||||
use PhpMyAdmin\Dbal\TableName;
|
||||
use PhpMyAdmin\Html\Generator;
|
||||
use PhpMyAdmin\Http\ServerRequest;
|
||||
use PhpMyAdmin\Message;
|
||||
use PhpMyAdmin\ResponseRenderer;
|
||||
use PhpMyAdmin\Table\Maintenance;
|
||||
use PhpMyAdmin\Template;
|
||||
use Webmozart\Assert\Assert;
|
||||
use Webmozart\Assert\InvalidArgumentException;
|
||||
|
||||
use function __;
|
||||
use function count;
|
||||
|
||||
final class AnalyzeController extends AbstractController
|
||||
{
|
||||
/** @var Maintenance */
|
||||
private $model;
|
||||
|
||||
/** @var Config */
|
||||
private $config;
|
||||
|
||||
public function __construct(
|
||||
ResponseRenderer $response,
|
||||
Template $template,
|
||||
string $db,
|
||||
string $table,
|
||||
Maintenance $model,
|
||||
Config $config
|
||||
) {
|
||||
parent::__construct($response, $template, $db, $table);
|
||||
$this->model = $model;
|
||||
$this->config = $config;
|
||||
}
|
||||
|
||||
public function __invoke(ServerRequest $request): void
|
||||
{
|
||||
$selectedTablesParam = $request->getParsedBodyParam('selected_tbl');
|
||||
|
||||
try {
|
||||
Assert::isArray($selectedTablesParam);
|
||||
Assert::notEmpty($selectedTablesParam);
|
||||
Assert::allStringNotEmpty($selectedTablesParam);
|
||||
} catch (InvalidArgumentException $exception) {
|
||||
$this->response->setRequestStatus(false);
|
||||
$this->response->addJSON('message', __('No table selected.'));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
$database = DatabaseName::fromValue($request->getParam('db'));
|
||||
$selectedTables = [];
|
||||
foreach ($selectedTablesParam as $table) {
|
||||
$selectedTables[] = TableName::fromValue($table);
|
||||
}
|
||||
} catch (InvalidArgumentException $exception) {
|
||||
$message = Message::error($exception->getMessage());
|
||||
$this->response->setRequestStatus(false);
|
||||
$this->response->addJSON('message', $message->getDisplay());
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->config->get('DisableMultiTableMaintenance') && count($selectedTables) > 1) {
|
||||
$this->response->setRequestStatus(false);
|
||||
$this->response->addJSON('message', __('Maintenance operations on multiple tables are disabled.'));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
[$rows, $query] = $this->model->getAnalyzeTableRows($database, $selectedTables);
|
||||
|
||||
$message = Generator::getMessage(
|
||||
__('Your SQL query has been executed successfully.'),
|
||||
$query,
|
||||
'success'
|
||||
);
|
||||
|
||||
$this->render('table/maintenance/analyze', [
|
||||
'message' => $message,
|
||||
'rows' => $rows,
|
||||
]);
|
||||
}
|
||||
}
|
|
@ -1,96 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Controllers\Table\Maintenance;
|
||||
|
||||
use PhpMyAdmin\Config;
|
||||
use PhpMyAdmin\Controllers\Table\AbstractController;
|
||||
use PhpMyAdmin\Dbal\DatabaseName;
|
||||
use PhpMyAdmin\Dbal\TableName;
|
||||
use PhpMyAdmin\Html\Generator;
|
||||
use PhpMyAdmin\Http\ServerRequest;
|
||||
use PhpMyAdmin\Message;
|
||||
use PhpMyAdmin\ResponseRenderer;
|
||||
use PhpMyAdmin\Table\Maintenance;
|
||||
use PhpMyAdmin\Template;
|
||||
use Webmozart\Assert\Assert;
|
||||
use Webmozart\Assert\InvalidArgumentException;
|
||||
|
||||
use function __;
|
||||
use function count;
|
||||
|
||||
final class CheckController extends AbstractController
|
||||
{
|
||||
/** @var Maintenance */
|
||||
private $model;
|
||||
|
||||
/** @var Config */
|
||||
private $config;
|
||||
|
||||
public function __construct(
|
||||
ResponseRenderer $response,
|
||||
Template $template,
|
||||
string $db,
|
||||
string $table,
|
||||
Maintenance $model,
|
||||
Config $config
|
||||
) {
|
||||
parent::__construct($response, $template, $db, $table);
|
||||
$this->model = $model;
|
||||
$this->config = $config;
|
||||
}
|
||||
|
||||
public function __invoke(ServerRequest $request): void
|
||||
{
|
||||
$selectedTablesParam = $request->getParsedBodyParam('selected_tbl');
|
||||
|
||||
try {
|
||||
Assert::isArray($selectedTablesParam);
|
||||
Assert::notEmpty($selectedTablesParam);
|
||||
Assert::allStringNotEmpty($selectedTablesParam);
|
||||
} catch (InvalidArgumentException $exception) {
|
||||
$this->response->setRequestStatus(false);
|
||||
$this->response->addJSON('message', __('No table selected.'));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
$database = DatabaseName::fromValue($request->getParam('db'));
|
||||
$selectedTables = [];
|
||||
foreach ($selectedTablesParam as $table) {
|
||||
$selectedTables[] = TableName::fromValue($table);
|
||||
}
|
||||
} catch (InvalidArgumentException $exception) {
|
||||
$message = Message::error($exception->getMessage());
|
||||
$this->response->setRequestStatus(false);
|
||||
$this->response->addJSON('message', $message->getDisplay());
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->config->get('DisableMultiTableMaintenance') && count($selectedTables) > 1) {
|
||||
$this->response->setRequestStatus(false);
|
||||
$this->response->addJSON('message', __('Maintenance operations on multiple tables are disabled.'));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
[$rows, $query] = $this->model->getCheckTableRows($database, $selectedTables);
|
||||
|
||||
$message = Generator::getMessage(
|
||||
__('Your SQL query has been executed successfully.'),
|
||||
$query,
|
||||
'success'
|
||||
);
|
||||
|
||||
$indexesProblems = $this->model->getIndexesProblems($database, $selectedTables);
|
||||
|
||||
$this->render('table/maintenance/check', [
|
||||
'message' => $message,
|
||||
'rows' => $rows,
|
||||
'indexes_problems' => $indexesProblems,
|
||||
]);
|
||||
}
|
||||
}
|
|
@ -1,94 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Controllers\Table\Maintenance;
|
||||
|
||||
use PhpMyAdmin\Config;
|
||||
use PhpMyAdmin\Controllers\Table\AbstractController;
|
||||
use PhpMyAdmin\Dbal\DatabaseName;
|
||||
use PhpMyAdmin\Dbal\TableName;
|
||||
use PhpMyAdmin\Html\Generator;
|
||||
use PhpMyAdmin\Http\ServerRequest;
|
||||
use PhpMyAdmin\Message;
|
||||
use PhpMyAdmin\ResponseRenderer;
|
||||
use PhpMyAdmin\Table\Maintenance;
|
||||
use PhpMyAdmin\Template;
|
||||
use Webmozart\Assert\Assert;
|
||||
use Webmozart\Assert\InvalidArgumentException;
|
||||
|
||||
use function __;
|
||||
use function count;
|
||||
|
||||
final class ChecksumController extends AbstractController
|
||||
{
|
||||
/** @var Maintenance */
|
||||
private $model;
|
||||
|
||||
/** @var Config */
|
||||
private $config;
|
||||
|
||||
public function __construct(
|
||||
ResponseRenderer $response,
|
||||
Template $template,
|
||||
string $db,
|
||||
string $table,
|
||||
Maintenance $model,
|
||||
Config $config
|
||||
) {
|
||||
parent::__construct($response, $template, $db, $table);
|
||||
$this->model = $model;
|
||||
$this->config = $config;
|
||||
}
|
||||
|
||||
public function __invoke(ServerRequest $request): void
|
||||
{
|
||||
$selectedTablesParam = $request->getParsedBodyParam('selected_tbl');
|
||||
|
||||
try {
|
||||
Assert::isArray($selectedTablesParam);
|
||||
Assert::notEmpty($selectedTablesParam);
|
||||
Assert::allStringNotEmpty($selectedTablesParam);
|
||||
} catch (InvalidArgumentException $exception) {
|
||||
$this->response->setRequestStatus(false);
|
||||
$this->response->addJSON('message', __('No table selected.'));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
$database = DatabaseName::fromValue($request->getParam('db'));
|
||||
$selectedTables = [];
|
||||
foreach ($selectedTablesParam as $table) {
|
||||
$selectedTables[] = TableName::fromValue($table);
|
||||
}
|
||||
} catch (InvalidArgumentException $exception) {
|
||||
$message = Message::error($exception->getMessage());
|
||||
$this->response->setRequestStatus(false);
|
||||
$this->response->addJSON('message', $message->getDisplay());
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->config->get('DisableMultiTableMaintenance') && count($selectedTables) > 1) {
|
||||
$this->response->setRequestStatus(false);
|
||||
$this->response->addJSON('message', __('Maintenance operations on multiple tables are disabled.'));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
[$rows, $query, $warnings] = $this->model->getChecksumTableRows($database, $selectedTables);
|
||||
|
||||
$message = Generator::getMessage(
|
||||
__('Your SQL query has been executed successfully.'),
|
||||
$query,
|
||||
'success'
|
||||
);
|
||||
|
||||
$this->render('table/maintenance/checksum', [
|
||||
'message' => $message,
|
||||
'rows' => $rows,
|
||||
'warnings' => $warnings,
|
||||
]);
|
||||
}
|
||||
}
|
|
@ -1,93 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Controllers\Table\Maintenance;
|
||||
|
||||
use PhpMyAdmin\Config;
|
||||
use PhpMyAdmin\Controllers\Table\AbstractController;
|
||||
use PhpMyAdmin\Dbal\DatabaseName;
|
||||
use PhpMyAdmin\Dbal\TableName;
|
||||
use PhpMyAdmin\Html\Generator;
|
||||
use PhpMyAdmin\Http\ServerRequest;
|
||||
use PhpMyAdmin\Message;
|
||||
use PhpMyAdmin\ResponseRenderer;
|
||||
use PhpMyAdmin\Table\Maintenance;
|
||||
use PhpMyAdmin\Template;
|
||||
use Webmozart\Assert\Assert;
|
||||
use Webmozart\Assert\InvalidArgumentException;
|
||||
|
||||
use function __;
|
||||
use function count;
|
||||
|
||||
final class OptimizeController extends AbstractController
|
||||
{
|
||||
/** @var Maintenance */
|
||||
private $model;
|
||||
|
||||
/** @var Config */
|
||||
private $config;
|
||||
|
||||
public function __construct(
|
||||
ResponseRenderer $response,
|
||||
Template $template,
|
||||
string $db,
|
||||
string $table,
|
||||
Maintenance $model,
|
||||
Config $config
|
||||
) {
|
||||
parent::__construct($response, $template, $db, $table);
|
||||
$this->model = $model;
|
||||
$this->config = $config;
|
||||
}
|
||||
|
||||
public function __invoke(ServerRequest $request): void
|
||||
{
|
||||
$selectedTablesParam = $request->getParsedBodyParam('selected_tbl');
|
||||
|
||||
try {
|
||||
Assert::isArray($selectedTablesParam);
|
||||
Assert::notEmpty($selectedTablesParam);
|
||||
Assert::allStringNotEmpty($selectedTablesParam);
|
||||
} catch (InvalidArgumentException $exception) {
|
||||
$this->response->setRequestStatus(false);
|
||||
$this->response->addJSON('message', __('No table selected.'));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
$database = DatabaseName::fromValue($request->getParam('db'));
|
||||
$selectedTables = [];
|
||||
foreach ($selectedTablesParam as $table) {
|
||||
$selectedTables[] = TableName::fromValue($table);
|
||||
}
|
||||
} catch (InvalidArgumentException $exception) {
|
||||
$message = Message::error($exception->getMessage());
|
||||
$this->response->setRequestStatus(false);
|
||||
$this->response->addJSON('message', $message->getDisplay());
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->config->get('DisableMultiTableMaintenance') && count($selectedTables) > 1) {
|
||||
$this->response->setRequestStatus(false);
|
||||
$this->response->addJSON('message', __('Maintenance operations on multiple tables are disabled.'));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
[$rows, $query] = $this->model->getOptimizeTableRows($database, $selectedTables);
|
||||
|
||||
$message = Generator::getMessage(
|
||||
__('Your SQL query has been executed successfully.'),
|
||||
$query,
|
||||
'success'
|
||||
);
|
||||
|
||||
$this->render('table/maintenance/optimize', [
|
||||
'message' => $message,
|
||||
'rows' => $rows,
|
||||
]);
|
||||
}
|
||||
}
|
|
@ -1,93 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Controllers\Table\Maintenance;
|
||||
|
||||
use PhpMyAdmin\Config;
|
||||
use PhpMyAdmin\Controllers\Table\AbstractController;
|
||||
use PhpMyAdmin\Dbal\DatabaseName;
|
||||
use PhpMyAdmin\Dbal\TableName;
|
||||
use PhpMyAdmin\Html\Generator;
|
||||
use PhpMyAdmin\Http\ServerRequest;
|
||||
use PhpMyAdmin\Message;
|
||||
use PhpMyAdmin\ResponseRenderer;
|
||||
use PhpMyAdmin\Table\Maintenance;
|
||||
use PhpMyAdmin\Template;
|
||||
use Webmozart\Assert\Assert;
|
||||
use Webmozart\Assert\InvalidArgumentException;
|
||||
|
||||
use function __;
|
||||
use function count;
|
||||
|
||||
final class RepairController extends AbstractController
|
||||
{
|
||||
/** @var Maintenance */
|
||||
private $model;
|
||||
|
||||
/** @var Config */
|
||||
private $config;
|
||||
|
||||
public function __construct(
|
||||
ResponseRenderer $response,
|
||||
Template $template,
|
||||
string $db,
|
||||
string $table,
|
||||
Maintenance $model,
|
||||
Config $config
|
||||
) {
|
||||
parent::__construct($response, $template, $db, $table);
|
||||
$this->model = $model;
|
||||
$this->config = $config;
|
||||
}
|
||||
|
||||
public function __invoke(ServerRequest $request): void
|
||||
{
|
||||
$selectedTablesParam = $request->getParsedBodyParam('selected_tbl');
|
||||
|
||||
try {
|
||||
Assert::isArray($selectedTablesParam);
|
||||
Assert::notEmpty($selectedTablesParam);
|
||||
Assert::allStringNotEmpty($selectedTablesParam);
|
||||
} catch (InvalidArgumentException $exception) {
|
||||
$this->response->setRequestStatus(false);
|
||||
$this->response->addJSON('message', __('No table selected.'));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
$database = DatabaseName::fromValue($request->getParam('db'));
|
||||
$selectedTables = [];
|
||||
foreach ($selectedTablesParam as $table) {
|
||||
$selectedTables[] = TableName::fromValue($table);
|
||||
}
|
||||
} catch (InvalidArgumentException $exception) {
|
||||
$message = Message::error($exception->getMessage());
|
||||
$this->response->setRequestStatus(false);
|
||||
$this->response->addJSON('message', $message->getDisplay());
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->config->get('DisableMultiTableMaintenance') && count($selectedTables) > 1) {
|
||||
$this->response->setRequestStatus(false);
|
||||
$this->response->addJSON('message', __('Maintenance operations on multiple tables are disabled.'));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
[$rows, $query] = $this->model->getRepairTableRows($database, $selectedTables);
|
||||
|
||||
$message = Generator::getMessage(
|
||||
__('Your SQL query has been executed successfully.'),
|
||||
$query,
|
||||
'success'
|
||||
);
|
||||
|
||||
$this->render('table/maintenance/repair', [
|
||||
'message' => $message,
|
||||
'rows' => $rows,
|
||||
]);
|
||||
}
|
||||
}
|
|
@ -1,499 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Controllers\Table;
|
||||
|
||||
use PhpMyAdmin\Charsets;
|
||||
use PhpMyAdmin\CheckUserPrivileges;
|
||||
use PhpMyAdmin\ConfigStorage\Relation;
|
||||
use PhpMyAdmin\DatabaseInterface;
|
||||
use PhpMyAdmin\DbTableExists;
|
||||
use PhpMyAdmin\Html\Generator;
|
||||
use PhpMyAdmin\Index;
|
||||
use PhpMyAdmin\Message;
|
||||
use PhpMyAdmin\Operations;
|
||||
use PhpMyAdmin\Partitioning\Partition;
|
||||
use PhpMyAdmin\Query\Generator as QueryGenerator;
|
||||
use PhpMyAdmin\Query\Utilities;
|
||||
use PhpMyAdmin\ResponseRenderer;
|
||||
use PhpMyAdmin\StorageEngine;
|
||||
use PhpMyAdmin\Template;
|
||||
use PhpMyAdmin\Url;
|
||||
use PhpMyAdmin\Util;
|
||||
|
||||
use function __;
|
||||
use function count;
|
||||
use function implode;
|
||||
use function mb_strstr;
|
||||
use function mb_strtolower;
|
||||
use function mb_strtoupper;
|
||||
use function preg_replace;
|
||||
use function strlen;
|
||||
use function urldecode;
|
||||
|
||||
class OperationsController extends AbstractController
|
||||
{
|
||||
/** @var Operations */
|
||||
private $operations;
|
||||
|
||||
/** @var CheckUserPrivileges */
|
||||
private $checkUserPrivileges;
|
||||
|
||||
/** @var Relation */
|
||||
private $relation;
|
||||
|
||||
/** @var DatabaseInterface */
|
||||
private $dbi;
|
||||
|
||||
public function __construct(
|
||||
ResponseRenderer $response,
|
||||
Template $template,
|
||||
string $db,
|
||||
string $table,
|
||||
Operations $operations,
|
||||
CheckUserPrivileges $checkUserPrivileges,
|
||||
Relation $relation,
|
||||
DatabaseInterface $dbi
|
||||
) {
|
||||
parent::__construct($response, $template, $db, $table);
|
||||
$this->operations = $operations;
|
||||
$this->checkUserPrivileges = $checkUserPrivileges;
|
||||
$this->relation = $relation;
|
||||
$this->dbi = $dbi;
|
||||
}
|
||||
|
||||
public function __invoke(): void
|
||||
{
|
||||
global $urlParams, $reread_info, $tbl_is_view, $tbl_storage_engine;
|
||||
global $show_comment, $tbl_collation, $table_info_num_rows, $row_format, $auto_increment, $create_options;
|
||||
global $table_alters, $warning_messages, $lowerCaseNames, $db, $table, $reload, $result;
|
||||
global $new_tbl_storage_engine, $sql_query, $message_to_show, $columns, $hideOrderTable, $indexes;
|
||||
global $notNull, $comment, $errorUrl, $cfg;
|
||||
|
||||
$this->checkUserPrivileges->getPrivileges();
|
||||
|
||||
// lower_case_table_names=1 `DB` becomes `db`
|
||||
$lowerCaseNames = $this->dbi->getLowerCaseNames() === '1';
|
||||
|
||||
if ($lowerCaseNames) {
|
||||
$table = mb_strtolower($table);
|
||||
}
|
||||
|
||||
$pma_table = $this->dbi->getTable($db, $table);
|
||||
|
||||
$this->addScriptFiles(['table/operations.js']);
|
||||
|
||||
Util::checkParameters(['db', 'table']);
|
||||
|
||||
$isSystemSchema = Utilities::isSystemSchema($db);
|
||||
$urlParams = ['db' => $db, 'table' => $table];
|
||||
$errorUrl = Util::getScriptNameForOption($cfg['DefaultTabTable'], 'table');
|
||||
$errorUrl .= Url::getCommon($urlParams, '&');
|
||||
|
||||
DbTableExists::check();
|
||||
|
||||
$urlParams['goto'] = $urlParams['back'] = Url::getFromRoute('/table/operations');
|
||||
|
||||
$relationParameters = $this->relation->getRelationParameters();
|
||||
|
||||
/**
|
||||
* Reselect current db (needed in some cases probably due to the calling of {@link Relation})
|
||||
*/
|
||||
$this->dbi->selectDb($db);
|
||||
|
||||
$reread_info = $pma_table->getStatusInfo(null, false);
|
||||
$GLOBALS['showtable'] = $pma_table->getStatusInfo(null, (isset($reread_info) && $reread_info));
|
||||
if ($pma_table->isView()) {
|
||||
$tbl_is_view = true;
|
||||
$tbl_storage_engine = __('View');
|
||||
$show_comment = null;
|
||||
} else {
|
||||
$tbl_is_view = false;
|
||||
$tbl_storage_engine = $pma_table->getStorageEngine();
|
||||
$show_comment = $pma_table->getComment();
|
||||
}
|
||||
|
||||
$tbl_collation = $pma_table->getCollation();
|
||||
$table_info_num_rows = $pma_table->getNumRows();
|
||||
$row_format = $pma_table->getRowFormat();
|
||||
$auto_increment = $pma_table->getAutoIncrement();
|
||||
$create_options = $pma_table->getCreateOptions();
|
||||
|
||||
// set initial value of these variables, based on the current table engine
|
||||
if ($pma_table->isEngine('ARIA')) {
|
||||
// the value for transactional can be implicit
|
||||
// (no create option found, in this case it means 1)
|
||||
// or explicit (option found with a value of 0 or 1)
|
||||
// ($create_options['transactional'] may have been set by Table class,
|
||||
// from the $create_options)
|
||||
$create_options['transactional'] = ($create_options['transactional'] ?? '') == '0'
|
||||
? '0'
|
||||
: '1';
|
||||
$create_options['page_checksum'] = $create_options['page_checksum'] ?? '';
|
||||
}
|
||||
|
||||
$pma_table = $this->dbi->getTable($db, $table);
|
||||
$reread_info = false;
|
||||
$table_alters = [];
|
||||
|
||||
/**
|
||||
* If the table has to be moved to some other database
|
||||
*/
|
||||
if (isset($_POST['submit_move']) || isset($_POST['submit_copy'])) {
|
||||
$message = $this->operations->moveOrCopyTable($db, $table);
|
||||
|
||||
if (! $this->response->isAjax()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->response->addJSON('message', $message);
|
||||
|
||||
if ($message->isSuccess()) {
|
||||
if (isset($_POST['submit_move'], $_POST['target_db'])) {
|
||||
$db = $_POST['target_db'];// Used in Header::getJsParams()
|
||||
}
|
||||
|
||||
$this->response->addJSON('db', $db);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->response->setRequestStatus(false);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates table comment, type and options if required
|
||||
*/
|
||||
if (isset($_POST['submitoptions'])) {
|
||||
$_message = '';
|
||||
$warning_messages = [];
|
||||
|
||||
if (isset($_POST['new_name'])) {
|
||||
// lower_case_table_names=1 `DB` becomes `db`
|
||||
if ($lowerCaseNames) {
|
||||
$_POST['new_name'] = mb_strtolower($_POST['new_name']);
|
||||
}
|
||||
|
||||
// Get original names before rename operation
|
||||
$oldTable = $pma_table->getName();
|
||||
$oldDb = $pma_table->getDbName();
|
||||
|
||||
if ($pma_table->rename($_POST['new_name'])) {
|
||||
if (isset($_POST['adjust_privileges']) && ! empty($_POST['adjust_privileges'])) {
|
||||
$this->operations->adjustPrivilegesRenameOrMoveTable(
|
||||
$oldDb,
|
||||
$oldTable,
|
||||
$_POST['db'],
|
||||
$_POST['new_name']
|
||||
);
|
||||
}
|
||||
|
||||
// Reselect the original DB
|
||||
$db = $oldDb;
|
||||
$this->dbi->selectDb($oldDb);
|
||||
$_message .= $pma_table->getLastMessage();
|
||||
$result = true;
|
||||
$table = $pma_table->getName();
|
||||
$reread_info = true;
|
||||
$reload = true;
|
||||
} else {
|
||||
$_message .= $pma_table->getLastError();
|
||||
$result = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
! empty($_POST['new_tbl_storage_engine'])
|
||||
&& mb_strtoupper($_POST['new_tbl_storage_engine']) !== $tbl_storage_engine
|
||||
) {
|
||||
$new_tbl_storage_engine = mb_strtoupper($_POST['new_tbl_storage_engine']);
|
||||
|
||||
if ($pma_table->isEngine('ARIA')) {
|
||||
$create_options['transactional'] = ($create_options['transactional'] ?? '') == '0'
|
||||
? '0'
|
||||
: '1';
|
||||
$create_options['page_checksum'] = $create_options['page_checksum'] ?? '';
|
||||
}
|
||||
} else {
|
||||
$new_tbl_storage_engine = '';
|
||||
}
|
||||
|
||||
$row_format = $create_options['row_format'] ?? $pma_table->getRowFormat();
|
||||
|
||||
$table_alters = $this->operations->getTableAltersArray(
|
||||
$pma_table,
|
||||
$create_options['pack_keys'],
|
||||
(empty($create_options['checksum']) ? '0' : '1'),
|
||||
($create_options['page_checksum'] ?? ''),
|
||||
(empty($create_options['delay_key_write']) ? '0' : '1'),
|
||||
$row_format,
|
||||
$new_tbl_storage_engine,
|
||||
(isset($create_options['transactional']) && $create_options['transactional'] == '0' ? '0' : '1'),
|
||||
$tbl_collation
|
||||
);
|
||||
|
||||
if (count($table_alters) > 0) {
|
||||
$sql_query = 'ALTER TABLE '
|
||||
. Util::backquote($table);
|
||||
$sql_query .= "\r\n" . implode("\r\n", $table_alters);
|
||||
$sql_query .= ';';
|
||||
$result = (bool) $this->dbi->query($sql_query);
|
||||
$reread_info = true;
|
||||
unset($table_alters);
|
||||
$warning_messages = $this->operations->getWarningMessagesArray();
|
||||
}
|
||||
|
||||
if (! empty($_POST['tbl_collation']) && ! empty($_POST['change_all_collations'])) {
|
||||
$this->operations->changeAllColumnsCollation($db, $table, $_POST['tbl_collation']);
|
||||
}
|
||||
|
||||
if (isset($_POST['tbl_collation']) && empty($_POST['tbl_collation'])) {
|
||||
if ($this->response->isAjax()) {
|
||||
$this->response->setRequestStatus(false);
|
||||
$this->response->addJSON(
|
||||
'message',
|
||||
Message::error(__('No collation provided.'))
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reordering the table has been requested by the user
|
||||
*/
|
||||
if (isset($_POST['submitorderby']) && ! empty($_POST['order_field'])) {
|
||||
$sql_query = QueryGenerator::getQueryForReorderingTable(
|
||||
$table,
|
||||
urldecode($_POST['order_field']),
|
||||
$_POST['order_order'] ?? null
|
||||
);
|
||||
$result = $this->dbi->query($sql_query);
|
||||
}
|
||||
|
||||
/**
|
||||
* A partition operation has been requested by the user
|
||||
*/
|
||||
if (isset($_POST['submit_partition']) && ! empty($_POST['partition_operation'])) {
|
||||
$sql_query = QueryGenerator::getQueryForPartitioningTable(
|
||||
$table,
|
||||
$_POST['partition_operation'],
|
||||
$_POST['partition_name']
|
||||
);
|
||||
$result = $this->dbi->query($sql_query);
|
||||
}
|
||||
|
||||
if ($reread_info) {
|
||||
// to avoid showing the old value (for example the AUTO_INCREMENT) after
|
||||
// a change, clear the cache
|
||||
$this->dbi->getCache()->clearTableCache();
|
||||
$this->dbi->selectDb($db);
|
||||
$GLOBALS['showtable'] = $pma_table->getStatusInfo(null, true);
|
||||
if ($pma_table->isView()) {
|
||||
$tbl_is_view = true;
|
||||
$tbl_storage_engine = __('View');
|
||||
$show_comment = null;
|
||||
} else {
|
||||
$tbl_is_view = false;
|
||||
$tbl_storage_engine = $pma_table->getStorageEngine();
|
||||
$show_comment = $pma_table->getComment();
|
||||
}
|
||||
|
||||
$tbl_collation = $pma_table->getCollation();
|
||||
$table_info_num_rows = $pma_table->getNumRows();
|
||||
$row_format = $pma_table->getRowFormat();
|
||||
$auto_increment = $pma_table->getAutoIncrement();
|
||||
$create_options = $pma_table->getCreateOptions();
|
||||
}
|
||||
|
||||
unset($reread_info);
|
||||
|
||||
if (isset($result) && empty($message_to_show)) {
|
||||
if (empty($_message)) {
|
||||
if (empty($sql_query)) {
|
||||
$_message = Message::success(__('No change'));
|
||||
} else {
|
||||
$_message = $result
|
||||
? Message::success()
|
||||
: Message::error();
|
||||
}
|
||||
|
||||
if ($this->response->isAjax()) {
|
||||
$this->response->setRequestStatus($_message->isSuccess());
|
||||
$this->response->addJSON('message', $_message);
|
||||
if (! empty($sql_query)) {
|
||||
$this->response->addJSON(
|
||||
'sql_query',
|
||||
Generator::getMessage('', $sql_query)
|
||||
);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
$_message = $result
|
||||
? Message::success($_message)
|
||||
: Message::error($_message);
|
||||
}
|
||||
|
||||
if (! empty($warning_messages)) {
|
||||
$_message = new Message();
|
||||
$_message->addMessagesString($warning_messages);
|
||||
$_message->isError(true);
|
||||
if ($this->response->isAjax()) {
|
||||
$this->response->setRequestStatus(false);
|
||||
$this->response->addJSON('message', $_message);
|
||||
if (! empty($sql_query)) {
|
||||
$this->response->addJSON(
|
||||
'sql_query',
|
||||
Generator::getMessage('', $sql_query)
|
||||
);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
unset($warning_messages);
|
||||
}
|
||||
|
||||
if (empty($sql_query)) {
|
||||
$this->response->addHTML(
|
||||
$_message->getDisplay()
|
||||
);
|
||||
} else {
|
||||
$this->response->addHTML(
|
||||
Generator::getMessage($_message, $sql_query)
|
||||
);
|
||||
}
|
||||
|
||||
unset($_message);
|
||||
}
|
||||
|
||||
$urlParams['goto'] = $urlParams['back'] = Url::getFromRoute('/table/operations');
|
||||
|
||||
$columns = $this->dbi->getColumns($db, $table);
|
||||
|
||||
$hideOrderTable = false;
|
||||
// `ALTER TABLE ORDER BY` does not make sense for InnoDB tables that contain
|
||||
// a user-defined clustered index (PRIMARY KEY or NOT NULL UNIQUE index).
|
||||
// InnoDB always orders table rows according to such an index if one is present.
|
||||
if ($tbl_storage_engine === 'INNODB') {
|
||||
$indexes = Index::getFromTable($table, $db);
|
||||
foreach ($indexes as $name => $idx) {
|
||||
if ($name === 'PRIMARY') {
|
||||
$hideOrderTable = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if ($idx->getNonUnique()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$notNull = true;
|
||||
foreach ($idx->getColumns() as $column) {
|
||||
if ($column->getNull()) {
|
||||
$notNull = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($notNull) {
|
||||
$hideOrderTable = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$comment = '';
|
||||
if (mb_strstr((string) $show_comment, '; InnoDB free') === false) {
|
||||
if (mb_strstr((string) $show_comment, 'InnoDB free') === false) {
|
||||
// only user entered comment
|
||||
$comment = (string) $show_comment;
|
||||
} else {
|
||||
// here we have just InnoDB generated part
|
||||
$comment = '';
|
||||
}
|
||||
} else {
|
||||
// remove InnoDB comment from end, just the minimal part (*? is non greedy)
|
||||
$comment = preg_replace('@; InnoDB free:.*?$@', '', (string) $show_comment);
|
||||
}
|
||||
|
||||
$storageEngines = StorageEngine::getArray();
|
||||
|
||||
$charsets = Charsets::getCharsets($this->dbi, $GLOBALS['cfg']['Server']['DisableIS']);
|
||||
$collations = Charsets::getCollations($this->dbi, $GLOBALS['cfg']['Server']['DisableIS']);
|
||||
|
||||
$hasPackKeys = isset($create_options['pack_keys'])
|
||||
&& $pma_table->isEngine(['MYISAM', 'ARIA', 'ISAM']);
|
||||
$hasChecksumAndDelayKeyWrite = $pma_table->isEngine(['MYISAM', 'ARIA']);
|
||||
$hasTransactionalAndPageChecksum = $pma_table->isEngine('ARIA');
|
||||
$hasAutoIncrement = strlen((string) $auto_increment) > 0
|
||||
&& $pma_table->isEngine(['MYISAM', 'ARIA', 'INNODB', 'PBXT', 'ROCKSDB']);
|
||||
|
||||
$possibleRowFormats = $this->operations->getPossibleRowFormat();
|
||||
|
||||
$databaseList = [];
|
||||
if (count($GLOBALS['dblist']->databases) <= $GLOBALS['cfg']['MaxDbList']) {
|
||||
$databaseList = $GLOBALS['dblist']->databases->getList();
|
||||
}
|
||||
|
||||
$hasForeignKeys = ! empty($this->relation->getForeigners($db, $table, '', 'foreign'));
|
||||
$hasPrivileges = $GLOBALS['table_priv'] && $GLOBALS['col_priv'] && $GLOBALS['is_reload_priv'];
|
||||
$switchToNew = isset($_SESSION['pma_switch_to_new']) && $_SESSION['pma_switch_to_new'];
|
||||
|
||||
$partitions = [];
|
||||
$partitionsChoices = [];
|
||||
|
||||
if (Partition::havePartitioning()) {
|
||||
$partitionNames = Partition::getPartitionNames($db, $table);
|
||||
if (isset($partitionNames[0])) {
|
||||
$partitions = $partitionNames;
|
||||
$partitionsChoices = $this->operations->getPartitionMaintenanceChoices();
|
||||
}
|
||||
}
|
||||
|
||||
$foreigners = $this->operations->getForeignersForReferentialIntegrityCheck(
|
||||
$urlParams,
|
||||
$relationParameters->relationFeature !== null
|
||||
);
|
||||
|
||||
$this->render('table/operations/index', [
|
||||
'db' => $db,
|
||||
'table' => $table,
|
||||
'url_params' => $urlParams,
|
||||
'columns' => $columns,
|
||||
'hide_order_table' => $hideOrderTable,
|
||||
'table_comment' => $comment,
|
||||
'storage_engine' => $tbl_storage_engine,
|
||||
'storage_engines' => $storageEngines,
|
||||
'charsets' => $charsets,
|
||||
'collations' => $collations,
|
||||
'tbl_collation' => $tbl_collation,
|
||||
'row_formats' => $possibleRowFormats[$tbl_storage_engine] ?? [],
|
||||
'row_format_current' => $GLOBALS['showtable']['Row_format'],
|
||||
'has_auto_increment' => $hasAutoIncrement,
|
||||
'auto_increment' => $auto_increment,
|
||||
'has_pack_keys' => $hasPackKeys,
|
||||
'pack_keys' => $create_options['pack_keys'] ?? '',
|
||||
'has_transactional_and_page_checksum' => $hasTransactionalAndPageChecksum,
|
||||
'has_checksum_and_delay_key_write' => $hasChecksumAndDelayKeyWrite,
|
||||
'delay_key_write' => empty($create_options['delay_key_write']) ? '0' : '1',
|
||||
'transactional' => ($create_options['transactional'] ?? '') == '0' ? '0' : '1',
|
||||
'page_checksum' => $create_options['page_checksum'] ?? '',
|
||||
'checksum' => empty($create_options['checksum']) ? '0' : '1',
|
||||
'database_list' => $databaseList,
|
||||
'has_foreign_keys' => $hasForeignKeys,
|
||||
'has_privileges' => $hasPrivileges,
|
||||
'switch_to_new' => $switchToNew,
|
||||
'is_system_schema' => $isSystemSchema,
|
||||
'is_view' => $tbl_is_view,
|
||||
'partitions' => $partitions,
|
||||
'partitions_choices' => $partitionsChoices,
|
||||
'foreigners' => $foreigners,
|
||||
]);
|
||||
}
|
||||
}
|
|
@ -1,66 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Controllers\Table\Partition;
|
||||
|
||||
use PhpMyAdmin\Controllers\Table\AbstractController;
|
||||
use PhpMyAdmin\Dbal\DatabaseName;
|
||||
use PhpMyAdmin\Dbal\TableName;
|
||||
use PhpMyAdmin\Html\Generator;
|
||||
use PhpMyAdmin\Http\ServerRequest;
|
||||
use PhpMyAdmin\Message;
|
||||
use PhpMyAdmin\Partitioning\Maintenance;
|
||||
use PhpMyAdmin\ResponseRenderer;
|
||||
use PhpMyAdmin\Template;
|
||||
use Webmozart\Assert\Assert;
|
||||
use Webmozart\Assert\InvalidArgumentException;
|
||||
|
||||
use function __;
|
||||
|
||||
final class AnalyzeController extends AbstractController
|
||||
{
|
||||
/** @var Maintenance */
|
||||
private $model;
|
||||
|
||||
public function __construct(
|
||||
ResponseRenderer $response,
|
||||
Template $template,
|
||||
string $db,
|
||||
string $table,
|
||||
Maintenance $maintenance
|
||||
) {
|
||||
parent::__construct($response, $template, $db, $table);
|
||||
$this->model = $maintenance;
|
||||
}
|
||||
|
||||
public function __invoke(ServerRequest $request): void
|
||||
{
|
||||
$partitionName = $request->getParsedBodyParam('partition_name');
|
||||
|
||||
try {
|
||||
Assert::stringNotEmpty($partitionName);
|
||||
$database = DatabaseName::fromValue($request->getParam('db'));
|
||||
$table = TableName::fromValue($request->getParam('table'));
|
||||
} catch (InvalidArgumentException $exception) {
|
||||
$message = Message::error($exception->getMessage());
|
||||
$this->response->addHTML($message->getDisplay());
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
[$rows, $query] = $this->model->analyze($database, $table, $partitionName);
|
||||
|
||||
$message = Generator::getMessage(
|
||||
__('Your SQL query has been executed successfully.'),
|
||||
$query,
|
||||
'success'
|
||||
);
|
||||
|
||||
$this->render('table/partition/analyze', [
|
||||
'partition_name' => $partitionName,
|
||||
'message' => $message,
|
||||
'rows' => $rows,
|
||||
]);
|
||||
}
|
||||
}
|
|
@ -1,66 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Controllers\Table\Partition;
|
||||
|
||||
use PhpMyAdmin\Controllers\Table\AbstractController;
|
||||
use PhpMyAdmin\Dbal\DatabaseName;
|
||||
use PhpMyAdmin\Dbal\TableName;
|
||||
use PhpMyAdmin\Html\Generator;
|
||||
use PhpMyAdmin\Http\ServerRequest;
|
||||
use PhpMyAdmin\Message;
|
||||
use PhpMyAdmin\Partitioning\Maintenance;
|
||||
use PhpMyAdmin\ResponseRenderer;
|
||||
use PhpMyAdmin\Template;
|
||||
use Webmozart\Assert\Assert;
|
||||
use Webmozart\Assert\InvalidArgumentException;
|
||||
|
||||
use function __;
|
||||
|
||||
final class CheckController extends AbstractController
|
||||
{
|
||||
/** @var Maintenance */
|
||||
private $model;
|
||||
|
||||
public function __construct(
|
||||
ResponseRenderer $response,
|
||||
Template $template,
|
||||
string $db,
|
||||
string $table,
|
||||
Maintenance $maintenance
|
||||
) {
|
||||
parent::__construct($response, $template, $db, $table);
|
||||
$this->model = $maintenance;
|
||||
}
|
||||
|
||||
public function __invoke(ServerRequest $request): void
|
||||
{
|
||||
$partitionName = $request->getParsedBodyParam('partition_name');
|
||||
|
||||
try {
|
||||
Assert::stringNotEmpty($partitionName);
|
||||
$database = DatabaseName::fromValue($request->getParam('db'));
|
||||
$table = TableName::fromValue($request->getParam('table'));
|
||||
} catch (InvalidArgumentException $exception) {
|
||||
$message = Message::error($exception->getMessage());
|
||||
$this->response->addHTML($message->getDisplay());
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
[$rows, $query] = $this->model->check($database, $table, $partitionName);
|
||||
|
||||
$message = Generator::getMessage(
|
||||
__('Your SQL query has been executed successfully.'),
|
||||
$query,
|
||||
'success'
|
||||
);
|
||||
|
||||
$this->render('table/partition/check', [
|
||||
'partition_name' => $partitionName,
|
||||
'message' => $message,
|
||||
'rows' => $rows,
|
||||
]);
|
||||
}
|
||||
}
|
|
@ -1,73 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Controllers\Table\Partition;
|
||||
|
||||
use PhpMyAdmin\Controllers\Table\AbstractController;
|
||||
use PhpMyAdmin\Dbal\DatabaseName;
|
||||
use PhpMyAdmin\Dbal\TableName;
|
||||
use PhpMyAdmin\Html\Generator;
|
||||
use PhpMyAdmin\Http\ServerRequest;
|
||||
use PhpMyAdmin\Message;
|
||||
use PhpMyAdmin\Partitioning\Maintenance;
|
||||
use PhpMyAdmin\ResponseRenderer;
|
||||
use PhpMyAdmin\Template;
|
||||
use Webmozart\Assert\Assert;
|
||||
use Webmozart\Assert\InvalidArgumentException;
|
||||
|
||||
use function __;
|
||||
|
||||
final class DropController extends AbstractController
|
||||
{
|
||||
/** @var Maintenance */
|
||||
private $model;
|
||||
|
||||
public function __construct(
|
||||
ResponseRenderer $response,
|
||||
Template $template,
|
||||
string $db,
|
||||
string $table,
|
||||
Maintenance $maintenance
|
||||
) {
|
||||
parent::__construct($response, $template, $db, $table);
|
||||
$this->model = $maintenance;
|
||||
}
|
||||
|
||||
public function __invoke(ServerRequest $request): void
|
||||
{
|
||||
$partitionName = $request->getParsedBodyParam('partition_name');
|
||||
|
||||
try {
|
||||
Assert::stringNotEmpty($partitionName);
|
||||
$database = DatabaseName::fromValue($request->getParam('db'));
|
||||
$table = TableName::fromValue($request->getParam('table'));
|
||||
} catch (InvalidArgumentException $exception) {
|
||||
$message = Message::error($exception->getMessage());
|
||||
$this->response->addHTML($message->getDisplay());
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
[$result, $query] = $this->model->drop($database, $table, $partitionName);
|
||||
|
||||
if ($result) {
|
||||
$message = Generator::getMessage(
|
||||
__('Your SQL query has been executed successfully.'),
|
||||
$query,
|
||||
'success'
|
||||
);
|
||||
} else {
|
||||
$message = Generator::getMessage(
|
||||
__('Error'),
|
||||
$query,
|
||||
'error'
|
||||
);
|
||||
}
|
||||
|
||||
$this->render('table/partition/drop', [
|
||||
'partition_name' => $partitionName,
|
||||
'message' => $message,
|
||||
]);
|
||||
}
|
||||
}
|
|
@ -1,66 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Controllers\Table\Partition;
|
||||
|
||||
use PhpMyAdmin\Controllers\Table\AbstractController;
|
||||
use PhpMyAdmin\Dbal\DatabaseName;
|
||||
use PhpMyAdmin\Dbal\TableName;
|
||||
use PhpMyAdmin\Html\Generator;
|
||||
use PhpMyAdmin\Http\ServerRequest;
|
||||
use PhpMyAdmin\Message;
|
||||
use PhpMyAdmin\Partitioning\Maintenance;
|
||||
use PhpMyAdmin\ResponseRenderer;
|
||||
use PhpMyAdmin\Template;
|
||||
use Webmozart\Assert\Assert;
|
||||
use Webmozart\Assert\InvalidArgumentException;
|
||||
|
||||
use function __;
|
||||
|
||||
final class OptimizeController extends AbstractController
|
||||
{
|
||||
/** @var Maintenance */
|
||||
private $model;
|
||||
|
||||
public function __construct(
|
||||
ResponseRenderer $response,
|
||||
Template $template,
|
||||
string $db,
|
||||
string $table,
|
||||
Maintenance $maintenance
|
||||
) {
|
||||
parent::__construct($response, $template, $db, $table);
|
||||
$this->model = $maintenance;
|
||||
}
|
||||
|
||||
public function __invoke(ServerRequest $request): void
|
||||
{
|
||||
$partitionName = $request->getParsedBodyParam('partition_name');
|
||||
|
||||
try {
|
||||
Assert::stringNotEmpty($partitionName);
|
||||
$database = DatabaseName::fromValue($request->getParam('db'));
|
||||
$table = TableName::fromValue($request->getParam('table'));
|
||||
} catch (InvalidArgumentException $exception) {
|
||||
$message = Message::error($exception->getMessage());
|
||||
$this->response->addHTML($message->getDisplay());
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
[$rows, $query] = $this->model->optimize($database, $table, $partitionName);
|
||||
|
||||
$message = Generator::getMessage(
|
||||
__('Your SQL query has been executed successfully.'),
|
||||
$query,
|
||||
'success'
|
||||
);
|
||||
|
||||
$this->render('table/partition/optimize', [
|
||||
'partition_name' => $partitionName,
|
||||
'message' => $message,
|
||||
'rows' => $rows,
|
||||
]);
|
||||
}
|
||||
}
|
|
@ -1,73 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Controllers\Table\Partition;
|
||||
|
||||
use PhpMyAdmin\Controllers\Table\AbstractController;
|
||||
use PhpMyAdmin\Dbal\DatabaseName;
|
||||
use PhpMyAdmin\Dbal\TableName;
|
||||
use PhpMyAdmin\Html\Generator;
|
||||
use PhpMyAdmin\Http\ServerRequest;
|
||||
use PhpMyAdmin\Message;
|
||||
use PhpMyAdmin\Partitioning\Maintenance;
|
||||
use PhpMyAdmin\ResponseRenderer;
|
||||
use PhpMyAdmin\Template;
|
||||
use Webmozart\Assert\Assert;
|
||||
use Webmozart\Assert\InvalidArgumentException;
|
||||
|
||||
use function __;
|
||||
|
||||
final class RebuildController extends AbstractController
|
||||
{
|
||||
/** @var Maintenance */
|
||||
private $model;
|
||||
|
||||
public function __construct(
|
||||
ResponseRenderer $response,
|
||||
Template $template,
|
||||
string $db,
|
||||
string $table,
|
||||
Maintenance $maintenance
|
||||
) {
|
||||
parent::__construct($response, $template, $db, $table);
|
||||
$this->model = $maintenance;
|
||||
}
|
||||
|
||||
public function __invoke(ServerRequest $request): void
|
||||
{
|
||||
$partitionName = $request->getParsedBodyParam('partition_name');
|
||||
|
||||
try {
|
||||
Assert::stringNotEmpty($partitionName);
|
||||
$database = DatabaseName::fromValue($request->getParam('db'));
|
||||
$table = TableName::fromValue($request->getParam('table'));
|
||||
} catch (InvalidArgumentException $exception) {
|
||||
$message = Message::error($exception->getMessage());
|
||||
$this->response->addHTML($message->getDisplay());
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
[$result, $query] = $this->model->rebuild($database, $table, $partitionName);
|
||||
|
||||
if ($result) {
|
||||
$message = Generator::getMessage(
|
||||
__('Your SQL query has been executed successfully.'),
|
||||
$query,
|
||||
'success'
|
||||
);
|
||||
} else {
|
||||
$message = Generator::getMessage(
|
||||
__('Error'),
|
||||
$query,
|
||||
'error'
|
||||
);
|
||||
}
|
||||
|
||||
$this->render('table/partition/rebuild', [
|
||||
'partition_name' => $partitionName,
|
||||
'message' => $message,
|
||||
]);
|
||||
}
|
||||
}
|
|
@ -1,66 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Controllers\Table\Partition;
|
||||
|
||||
use PhpMyAdmin\Controllers\Table\AbstractController;
|
||||
use PhpMyAdmin\Dbal\DatabaseName;
|
||||
use PhpMyAdmin\Dbal\TableName;
|
||||
use PhpMyAdmin\Html\Generator;
|
||||
use PhpMyAdmin\Http\ServerRequest;
|
||||
use PhpMyAdmin\Message;
|
||||
use PhpMyAdmin\Partitioning\Maintenance;
|
||||
use PhpMyAdmin\ResponseRenderer;
|
||||
use PhpMyAdmin\Template;
|
||||
use Webmozart\Assert\Assert;
|
||||
use Webmozart\Assert\InvalidArgumentException;
|
||||
|
||||
use function __;
|
||||
|
||||
final class RepairController extends AbstractController
|
||||
{
|
||||
/** @var Maintenance */
|
||||
private $model;
|
||||
|
||||
public function __construct(
|
||||
ResponseRenderer $response,
|
||||
Template $template,
|
||||
string $db,
|
||||
string $table,
|
||||
Maintenance $maintenance
|
||||
) {
|
||||
parent::__construct($response, $template, $db, $table);
|
||||
$this->model = $maintenance;
|
||||
}
|
||||
|
||||
public function __invoke(ServerRequest $request): void
|
||||
{
|
||||
$partitionName = $request->getParsedBodyParam('partition_name');
|
||||
|
||||
try {
|
||||
Assert::stringNotEmpty($partitionName);
|
||||
$database = DatabaseName::fromValue($request->getParam('db'));
|
||||
$table = TableName::fromValue($request->getParam('table'));
|
||||
} catch (InvalidArgumentException $exception) {
|
||||
$message = Message::error($exception->getMessage());
|
||||
$this->response->addHTML($message->getDisplay());
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
[$rows, $query] = $this->model->repair($database, $table, $partitionName);
|
||||
|
||||
$message = Generator::getMessage(
|
||||
__('Your SQL query has been executed successfully.'),
|
||||
$query,
|
||||
'success'
|
||||
);
|
||||
|
||||
$this->render('table/partition/repair', [
|
||||
'partition_name' => $partitionName,
|
||||
'message' => $message,
|
||||
'rows' => $rows,
|
||||
]);
|
||||
}
|
||||
}
|
|
@ -1,73 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Controllers\Table\Partition;
|
||||
|
||||
use PhpMyAdmin\Controllers\Table\AbstractController;
|
||||
use PhpMyAdmin\Dbal\DatabaseName;
|
||||
use PhpMyAdmin\Dbal\TableName;
|
||||
use PhpMyAdmin\Html\Generator;
|
||||
use PhpMyAdmin\Http\ServerRequest;
|
||||
use PhpMyAdmin\Message;
|
||||
use PhpMyAdmin\Partitioning\Maintenance;
|
||||
use PhpMyAdmin\ResponseRenderer;
|
||||
use PhpMyAdmin\Template;
|
||||
use Webmozart\Assert\Assert;
|
||||
use Webmozart\Assert\InvalidArgumentException;
|
||||
|
||||
use function __;
|
||||
|
||||
final class TruncateController extends AbstractController
|
||||
{
|
||||
/** @var Maintenance */
|
||||
private $model;
|
||||
|
||||
public function __construct(
|
||||
ResponseRenderer $response,
|
||||
Template $template,
|
||||
string $db,
|
||||
string $table,
|
||||
Maintenance $maintenance
|
||||
) {
|
||||
parent::__construct($response, $template, $db, $table);
|
||||
$this->model = $maintenance;
|
||||
}
|
||||
|
||||
public function __invoke(ServerRequest $request): void
|
||||
{
|
||||
$partitionName = $request->getParsedBodyParam('partition_name');
|
||||
|
||||
try {
|
||||
Assert::stringNotEmpty($partitionName);
|
||||
$database = DatabaseName::fromValue($request->getParam('db'));
|
||||
$table = TableName::fromValue($request->getParam('table'));
|
||||
} catch (InvalidArgumentException $exception) {
|
||||
$message = Message::error($exception->getMessage());
|
||||
$this->response->addHTML($message->getDisplay());
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
[$result, $query] = $this->model->truncate($database, $table, $partitionName);
|
||||
|
||||
if ($result) {
|
||||
$message = Generator::getMessage(
|
||||
__('Your SQL query has been executed successfully.'),
|
||||
$query,
|
||||
'success'
|
||||
);
|
||||
} else {
|
||||
$message = Generator::getMessage(
|
||||
__('Error'),
|
||||
$query,
|
||||
'error'
|
||||
);
|
||||
}
|
||||
|
||||
$this->render('table/partition/truncate', [
|
||||
'partition_name' => $partitionName,
|
||||
'message' => $message,
|
||||
]);
|
||||
}
|
||||
}
|
|
@ -1,75 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
* Controller for table privileges
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Controllers\Table;
|
||||
|
||||
use PhpMyAdmin\DatabaseInterface;
|
||||
use PhpMyAdmin\ResponseRenderer;
|
||||
use PhpMyAdmin\Server\Privileges;
|
||||
use PhpMyAdmin\Template;
|
||||
use PhpMyAdmin\Util;
|
||||
|
||||
use function mb_strtolower;
|
||||
|
||||
/**
|
||||
* Controller for table privileges
|
||||
*/
|
||||
class PrivilegesController extends AbstractController
|
||||
{
|
||||
/** @var Privileges */
|
||||
private $privileges;
|
||||
|
||||
/** @var DatabaseInterface */
|
||||
private $dbi;
|
||||
|
||||
public function __construct(
|
||||
ResponseRenderer $response,
|
||||
Template $template,
|
||||
string $db,
|
||||
string $table,
|
||||
Privileges $privileges,
|
||||
DatabaseInterface $dbi
|
||||
) {
|
||||
parent::__construct($response, $template, $db, $table);
|
||||
$this->privileges = $privileges;
|
||||
$this->dbi = $dbi;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $params Request parameters
|
||||
* @psalm-param array{checkprivsdb: string, checkprivstable: string} $params
|
||||
*/
|
||||
public function __invoke(array $params): string
|
||||
{
|
||||
global $cfg, $text_dir;
|
||||
|
||||
$scriptName = Util::getScriptNameForOption($cfg['DefaultTabTable'], 'table');
|
||||
|
||||
$db = $params['checkprivsdb'];
|
||||
$table = $params['checkprivstable'];
|
||||
if ($this->dbi->getLowerCaseNames() === '1') {
|
||||
$db = mb_strtolower($params['checkprivsdb']);
|
||||
$table = mb_strtolower($params['checkprivstable']);
|
||||
}
|
||||
|
||||
$privileges = [];
|
||||
if ($this->dbi->isSuperUser()) {
|
||||
$privileges = $this->privileges->getAllPrivileges($db, $table);
|
||||
}
|
||||
|
||||
return $this->template->render('table/privileges/index', [
|
||||
'db' => $db,
|
||||
'table' => $table,
|
||||
'is_superuser' => $this->dbi->isSuperUser(),
|
||||
'table_url' => $scriptName,
|
||||
'text_dir' => $text_dir,
|
||||
'is_createuser' => $this->dbi->isCreateUser(),
|
||||
'is_grantuser' => $this->dbi->isGrantUser(),
|
||||
'privileges' => $privileges,
|
||||
]);
|
||||
}
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Controllers\Table;
|
||||
|
||||
use PhpMyAdmin\Controllers\Sql\SqlController;
|
||||
use PhpMyAdmin\RecentFavoriteTable;
|
||||
|
||||
/**
|
||||
* Browse recent and favorite tables chosen from navigation.
|
||||
*/
|
||||
class RecentFavoriteController extends AbstractController
|
||||
{
|
||||
public function __invoke(): void
|
||||
{
|
||||
global $containerBuilder;
|
||||
|
||||
RecentFavoriteTable::getInstance('recent')->removeIfInvalid($_REQUEST['db'], $_REQUEST['table']);
|
||||
|
||||
RecentFavoriteTable::getInstance('favorite')->removeIfInvalid($_REQUEST['db'], $_REQUEST['table']);
|
||||
|
||||
/** @var SqlController $controller */
|
||||
$controller = $containerBuilder->get(SqlController::class);
|
||||
$controller();
|
||||
}
|
||||
}
|
|
@ -1,347 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Controllers\Table;
|
||||
|
||||
use PhpMyAdmin\ConfigStorage\Features\DisplayFeature;
|
||||
use PhpMyAdmin\ConfigStorage\Features\RelationFeature;
|
||||
use PhpMyAdmin\ConfigStorage\Relation;
|
||||
use PhpMyAdmin\Core;
|
||||
use PhpMyAdmin\DatabaseInterface;
|
||||
use PhpMyAdmin\Html\Generator;
|
||||
use PhpMyAdmin\Index;
|
||||
use PhpMyAdmin\ResponseRenderer;
|
||||
use PhpMyAdmin\Table;
|
||||
use PhpMyAdmin\Template;
|
||||
use PhpMyAdmin\Util;
|
||||
use PhpMyAdmin\Utils\ForeignKey;
|
||||
|
||||
use function __;
|
||||
use function array_key_exists;
|
||||
use function array_keys;
|
||||
use function array_values;
|
||||
use function mb_strtoupper;
|
||||
use function md5;
|
||||
use function strtoupper;
|
||||
use function uksort;
|
||||
use function usort;
|
||||
|
||||
/**
|
||||
* Display table relations for viewing and editing.
|
||||
*
|
||||
* Includes phpMyAdmin relations and InnoDB relations.
|
||||
*/
|
||||
final class RelationController extends AbstractController
|
||||
{
|
||||
/** @var Relation */
|
||||
private $relation;
|
||||
|
||||
/** @var DatabaseInterface */
|
||||
private $dbi;
|
||||
|
||||
public function __construct(
|
||||
ResponseRenderer $response,
|
||||
Template $template,
|
||||
string $db,
|
||||
string $table,
|
||||
Relation $relation,
|
||||
DatabaseInterface $dbi
|
||||
) {
|
||||
parent::__construct($response, $template, $db, $table);
|
||||
$this->relation = $relation;
|
||||
$this->dbi = $dbi;
|
||||
}
|
||||
|
||||
/**
|
||||
* Index
|
||||
*/
|
||||
public function __invoke(): void
|
||||
{
|
||||
global $route;
|
||||
|
||||
$options = [
|
||||
'CASCADE' => 'CASCADE',
|
||||
'SET_NULL' => 'SET NULL',
|
||||
'NO_ACTION' => 'NO ACTION',
|
||||
'RESTRICT' => 'RESTRICT',
|
||||
];
|
||||
|
||||
$table = $this->dbi->getTable($this->db, $this->table);
|
||||
$storageEngine = mb_strtoupper((string) $table->getStatusInfo('Engine'));
|
||||
|
||||
$relationParameters = $this->relation->getRelationParameters();
|
||||
|
||||
$relations = [];
|
||||
if ($relationParameters->relationFeature !== null) {
|
||||
$relations = $this->relation->getForeigners($this->db, $this->table, '', 'internal');
|
||||
}
|
||||
|
||||
$relationsForeign = [];
|
||||
if (ForeignKey::isSupported($storageEngine)) {
|
||||
$relationsForeign = $this->relation->getForeigners($this->db, $this->table, '', 'foreign');
|
||||
}
|
||||
|
||||
// Send table of column names to populate corresponding dropdowns depending
|
||||
// on the current selection
|
||||
if (isset($_POST['getDropdownValues']) && $_POST['getDropdownValues'] === 'true') {
|
||||
// if both db and table are selected
|
||||
if (isset($_POST['foreignTable'])) {
|
||||
$this->getDropdownValueForTable();
|
||||
} else { // if only the db is selected
|
||||
$this->getDropdownValueForDatabase($storageEngine);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->addScriptFiles(['table/relation.js', 'indexes.js']);
|
||||
|
||||
// Set the database
|
||||
$this->dbi->selectDb($this->db);
|
||||
|
||||
// updates for Internal relations
|
||||
if (isset($_POST['destination_db']) && $relationParameters->relationFeature !== null) {
|
||||
$this->updateForInternalRelation($table, $relationParameters->relationFeature, $relations);
|
||||
}
|
||||
|
||||
// updates for foreign keys
|
||||
$this->updateForForeignKeys($table, $options, $relationsForeign);
|
||||
|
||||
// Updates for display field
|
||||
if ($relationParameters->displayFeature !== null && isset($_POST['display_field'])) {
|
||||
$this->updateForDisplayField($table, $relationParameters->displayFeature);
|
||||
}
|
||||
|
||||
// If we did an update, refresh our data
|
||||
if (isset($_POST['destination_db']) && $relationParameters->relationFeature !== null) {
|
||||
$relations = $this->relation->getForeigners($this->db, $this->table, '', 'internal');
|
||||
}
|
||||
|
||||
if (isset($_POST['destination_foreign_db']) && ForeignKey::isSupported($storageEngine)) {
|
||||
$relationsForeign = $this->relation->getForeigners($this->db, $this->table, '', 'foreign');
|
||||
}
|
||||
|
||||
/**
|
||||
* Dialog
|
||||
*/
|
||||
// Now find out the columns of our $table
|
||||
// need to use DatabaseInterface::QUERY_BUFFERED with $this->dbi->numRows()
|
||||
// in mysqli
|
||||
$columns = $this->dbi->getColumns($this->db, $this->table);
|
||||
|
||||
$column_array = [];
|
||||
$column_hash_array = [];
|
||||
$column_array[''] = '';
|
||||
foreach ($columns as $column) {
|
||||
if (strtoupper($storageEngine) !== 'INNODB' && empty($column['Key'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$column_array[$column['Field']] = $column['Field'];
|
||||
$column_hash_array[$column['Field']] = md5($column['Field']);
|
||||
}
|
||||
|
||||
if ($GLOBALS['cfg']['NaturalOrder']) {
|
||||
uksort($column_array, 'strnatcasecmp');
|
||||
}
|
||||
|
||||
// common form
|
||||
$engine = $this->dbi->getTable($this->db, $this->table)->getStorageEngine();
|
||||
$this->render('table/relation/common_form', [
|
||||
'is_foreign_key_supported' => ForeignKey::isSupported($engine),
|
||||
'db' => $this->db,
|
||||
'table' => $this->table,
|
||||
'relation_parameters' => $relationParameters,
|
||||
'tbl_storage_engine' => $storageEngine,
|
||||
'existrel' => $relations,
|
||||
'existrel_foreign' => array_key_exists('foreign_keys_data', $relationsForeign)
|
||||
? $relationsForeign['foreign_keys_data']
|
||||
: [],
|
||||
'options_array' => $options,
|
||||
'column_array' => $column_array,
|
||||
'column_hash_array' => $column_hash_array,
|
||||
'save_row' => array_values($columns),
|
||||
'url_params' => $GLOBALS['urlParams'],
|
||||
'databases' => $GLOBALS['dblist']->databases,
|
||||
'dbi' => $this->dbi,
|
||||
'default_sliders_state' => $GLOBALS['cfg']['InitialSlidersState'],
|
||||
'route' => $route,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update for display field
|
||||
*/
|
||||
private function updateForDisplayField(Table $table, DisplayFeature $displayFeature): void
|
||||
{
|
||||
$table->updateDisplayField($_POST['display_field'], $displayFeature);
|
||||
|
||||
$this->response->addHTML(
|
||||
Generator::getMessage(
|
||||
__('Display column was successfully updated.'),
|
||||
'',
|
||||
'success'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update for FK
|
||||
*
|
||||
* @param Table $table Table
|
||||
* @param array $options Options
|
||||
* @param array $relationsForeign External relations
|
||||
*/
|
||||
private function updateForForeignKeys(Table $table, array $options, array $relationsForeign): void
|
||||
{
|
||||
$multi_edit_columns_name = $_POST['foreign_key_fields_name'] ?? null;
|
||||
$preview_sql_data = '';
|
||||
$seen_error = false;
|
||||
|
||||
// (for now, one index name only; we keep the definitions if the
|
||||
// foreign db is not the same)
|
||||
if (
|
||||
isset($_POST['destination_foreign_db'], $_POST['destination_foreign_table'])
|
||||
&& isset($_POST['destination_foreign_column'])
|
||||
) {
|
||||
[
|
||||
$html,
|
||||
$preview_sql_data,
|
||||
$display_query,
|
||||
$seen_error,
|
||||
] = $table->updateForeignKeys(
|
||||
$_POST['destination_foreign_db'],
|
||||
$multi_edit_columns_name,
|
||||
$_POST['destination_foreign_table'],
|
||||
$_POST['destination_foreign_column'],
|
||||
$options,
|
||||
$this->table,
|
||||
array_key_exists('foreign_keys_data', $relationsForeign)
|
||||
? $relationsForeign['foreign_keys_data']
|
||||
: []
|
||||
);
|
||||
$this->response->addHTML($html);
|
||||
}
|
||||
|
||||
// If there is a request for SQL previewing.
|
||||
if (isset($_POST['preview_sql'])) {
|
||||
Core::previewSQL($preview_sql_data);
|
||||
|
||||
exit;
|
||||
}
|
||||
|
||||
if (empty($display_query) || $seen_error) {
|
||||
return;
|
||||
}
|
||||
|
||||
$GLOBALS['display_query'] = $display_query;
|
||||
$this->response->addHTML(
|
||||
Generator::getMessage(
|
||||
__('Your SQL query has been executed successfully.'),
|
||||
null,
|
||||
'success'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update for internal relation
|
||||
*
|
||||
* @param array $relations Relations
|
||||
*/
|
||||
private function updateForInternalRelation(
|
||||
Table $table,
|
||||
RelationFeature $relationFeature,
|
||||
array $relations
|
||||
): void {
|
||||
$multi_edit_columns_name = $_POST['fields_name'] ?? null;
|
||||
|
||||
if (
|
||||
! $table->updateInternalRelations(
|
||||
$multi_edit_columns_name,
|
||||
$_POST['destination_db'],
|
||||
$_POST['destination_table'],
|
||||
$_POST['destination_column'],
|
||||
$relationFeature,
|
||||
$relations
|
||||
)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->response->addHTML(
|
||||
Generator::getMessage(
|
||||
__('Internal relationships were successfully updated.'),
|
||||
'',
|
||||
'success'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send table columns for foreign table dropdown
|
||||
*/
|
||||
public function getDropdownValueForTable(): void
|
||||
{
|
||||
$foreignTable = $_POST['foreignTable'];
|
||||
$table_obj = $this->dbi->getTable($_POST['foreignDb'], $foreignTable);
|
||||
// Since views do not have keys defined on them provide the full list of
|
||||
// columns
|
||||
if ($table_obj->isView()) {
|
||||
$columnList = $table_obj->getColumns(false, false);
|
||||
} else {
|
||||
$columnList = $table_obj->getIndexedColumns(false, false);
|
||||
}
|
||||
|
||||
if ($GLOBALS['cfg']['NaturalOrder']) {
|
||||
usort($columnList, 'strnatcasecmp');
|
||||
}
|
||||
|
||||
$this->response->addJSON('columns', $columnList);
|
||||
|
||||
// @todo should be: $server->db($db)->table($table)->primary()
|
||||
$primary = Index::getPrimary($foreignTable, $_POST['foreignDb']);
|
||||
if ($primary === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->response->addJSON('primary', array_keys($primary->getColumns()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Send database selection values for dropdown
|
||||
*
|
||||
* @param string $storageEngine Storage engine.
|
||||
*/
|
||||
public function getDropdownValueForDatabase(string $storageEngine): void
|
||||
{
|
||||
$tables = [];
|
||||
$foreign = isset($_POST['foreign']) && $_POST['foreign'] === 'true';
|
||||
|
||||
if ($foreign) {
|
||||
$query = 'SHOW TABLE STATUS FROM '
|
||||
. Util::backquote($_POST['foreignDb']);
|
||||
$tables_rs = $this->dbi->query($query);
|
||||
|
||||
foreach ($tables_rs as $row) {
|
||||
if (! isset($row['Engine']) || mb_strtoupper($row['Engine']) != $storageEngine) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$tables[] = $row['Name'];
|
||||
}
|
||||
} else {
|
||||
$query = 'SHOW TABLES FROM '
|
||||
. Util::backquote($_POST['foreignDb']);
|
||||
$tables_rs = $this->dbi->query($query);
|
||||
$tables = $tables_rs->fetchAllColumn();
|
||||
}
|
||||
|
||||
if ($GLOBALS['cfg']['NaturalOrder']) {
|
||||
usort($tables, 'strnatcasecmp');
|
||||
}
|
||||
|
||||
$this->response->addJSON('tables', $tables);
|
||||
}
|
||||
}
|
|
@ -1,669 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Controllers\Table;
|
||||
|
||||
use PhpMyAdmin\ConfigStorage\Relation;
|
||||
use PhpMyAdmin\Controllers\Database\SqlController as DatabaseSqlController;
|
||||
use PhpMyAdmin\Controllers\Sql\SqlController;
|
||||
use PhpMyAdmin\Controllers\Table\SqlController as TableSqlController;
|
||||
use PhpMyAdmin\Core;
|
||||
use PhpMyAdmin\DatabaseInterface;
|
||||
use PhpMyAdmin\File;
|
||||
use PhpMyAdmin\Html\Generator;
|
||||
use PhpMyAdmin\InsertEdit;
|
||||
use PhpMyAdmin\Message;
|
||||
use PhpMyAdmin\Plugins\IOTransformationsPlugin;
|
||||
use PhpMyAdmin\ResponseRenderer;
|
||||
use PhpMyAdmin\Table;
|
||||
use PhpMyAdmin\Template;
|
||||
use PhpMyAdmin\Transformations;
|
||||
use PhpMyAdmin\Util;
|
||||
|
||||
use function __;
|
||||
use function array_keys;
|
||||
use function array_values;
|
||||
use function class_exists;
|
||||
use function count;
|
||||
use function implode;
|
||||
use function in_array;
|
||||
use function is_file;
|
||||
use function is_numeric;
|
||||
use function method_exists;
|
||||
use function parse_str;
|
||||
use function sprintf;
|
||||
|
||||
/**
|
||||
* Manipulation of table data like inserting, replacing and updating.
|
||||
*/
|
||||
final class ReplaceController extends AbstractController
|
||||
{
|
||||
/** @var InsertEdit */
|
||||
private $insertEdit;
|
||||
|
||||
/** @var Transformations */
|
||||
private $transformations;
|
||||
|
||||
/** @var Relation */
|
||||
private $relation;
|
||||
|
||||
/** @var DatabaseInterface */
|
||||
private $dbi;
|
||||
|
||||
public function __construct(
|
||||
ResponseRenderer $response,
|
||||
Template $template,
|
||||
string $db,
|
||||
string $table,
|
||||
InsertEdit $insertEdit,
|
||||
Transformations $transformations,
|
||||
Relation $relation,
|
||||
DatabaseInterface $dbi
|
||||
) {
|
||||
parent::__construct($response, $template, $db, $table);
|
||||
$this->insertEdit = $insertEdit;
|
||||
$this->transformations = $transformations;
|
||||
$this->relation = $relation;
|
||||
$this->dbi = $dbi;
|
||||
}
|
||||
|
||||
public function __invoke(): void
|
||||
{
|
||||
global $containerBuilder, $db, $table, $urlParams, $message;
|
||||
global $errorUrl, $mime_map, $unsaved_values, $active_page, $disp_query, $disp_message;
|
||||
global $goto_include, $loop_array, $using_key, $is_insert, $is_insertignore, $query;
|
||||
global $value_sets, $func_no_param, $func_optional_param, $gis_from_text_functions, $gis_from_wkb_functions;
|
||||
global $query_fields, $insert_errors, $row_skipped, $query_values;
|
||||
global $total_affected_rows, $last_messages, $warning_messages, $error_messages, $return_to_sql_query;
|
||||
|
||||
Util::checkParameters(['db', 'table', 'goto']);
|
||||
|
||||
$this->dbi->selectDb($db);
|
||||
|
||||
/**
|
||||
* Initializes some variables
|
||||
*/
|
||||
$goto_include = false;
|
||||
|
||||
$this->addScriptFiles(['makegrid.js', 'sql.js', 'indexes.js', 'gis_data_editor.js']);
|
||||
|
||||
$insertRows = $_POST['insert_rows'] ?? null;
|
||||
if (is_numeric($insertRows) && $insertRows != $GLOBALS['cfg']['InsertRows']) {
|
||||
// check whether insert row mode, if so include /table/change
|
||||
$this->addScriptFiles([
|
||||
'vendor/jquery/additional-methods.js',
|
||||
'table/change.js',
|
||||
]);
|
||||
$GLOBALS['cfg']['InsertRows'] = $_POST['insert_rows'];
|
||||
/** @var ChangeController $controller */
|
||||
$controller = $containerBuilder->get(ChangeController::class);
|
||||
$controller();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$after_insert_actions = [
|
||||
'new_insert',
|
||||
'same_insert',
|
||||
'edit_next',
|
||||
];
|
||||
if (isset($_POST['after_insert']) && in_array($_POST['after_insert'], $after_insert_actions)) {
|
||||
$urlParams['after_insert'] = $_POST['after_insert'];
|
||||
if (isset($_POST['where_clause'])) {
|
||||
foreach ($_POST['where_clause'] as $one_where_clause) {
|
||||
if ($_POST['after_insert'] === 'same_insert') {
|
||||
$urlParams['where_clause'][] = $one_where_clause;
|
||||
} elseif ($_POST['after_insert'] === 'edit_next') {
|
||||
$this->insertEdit->setSessionForEditNext($one_where_clause);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//get $goto_include for different cases
|
||||
$goto_include = $this->insertEdit->getGotoInclude($goto_include);
|
||||
|
||||
// Defines the url to return in case of failure of the query
|
||||
$errorUrl = $this->insertEdit->getErrorUrl($urlParams);
|
||||
|
||||
/**
|
||||
* Prepares the update/insert of a row
|
||||
*/
|
||||
[
|
||||
$loop_array,
|
||||
$using_key,
|
||||
$is_insert,
|
||||
$is_insertignore,
|
||||
] = $this->insertEdit->getParamsForUpdateOrInsert();
|
||||
|
||||
$query = [];
|
||||
$value_sets = [];
|
||||
$func_no_param = [
|
||||
'CONNECTION_ID',
|
||||
'CURRENT_USER',
|
||||
'CURDATE',
|
||||
'CURTIME',
|
||||
'CURRENT_DATE',
|
||||
'CURRENT_TIME',
|
||||
'DATABASE',
|
||||
'LAST_INSERT_ID',
|
||||
'NOW',
|
||||
'PI',
|
||||
'RAND',
|
||||
'SYSDATE',
|
||||
'UNIX_TIMESTAMP',
|
||||
'USER',
|
||||
'UTC_DATE',
|
||||
'UTC_TIME',
|
||||
'UTC_TIMESTAMP',
|
||||
'UUID',
|
||||
'UUID_SHORT',
|
||||
'VERSION',
|
||||
];
|
||||
$func_optional_param = [
|
||||
'RAND',
|
||||
'UNIX_TIMESTAMP',
|
||||
];
|
||||
|
||||
$gis_from_text_functions = [
|
||||
'GeomFromText',
|
||||
'GeomCollFromText',
|
||||
'LineFromText',
|
||||
'MLineFromText',
|
||||
'PointFromText',
|
||||
'MPointFromText',
|
||||
'PolyFromText',
|
||||
'MPolyFromText',
|
||||
];
|
||||
$gis_from_wkb_functions = [
|
||||
'GeomFromWKB',
|
||||
'GeomCollFromWKB',
|
||||
'LineFromWKB',
|
||||
'MLineFromWKB',
|
||||
'PointFromWKB',
|
||||
'MPointFromWKB',
|
||||
'PolyFromWKB',
|
||||
'MPolyFromWKB',
|
||||
];
|
||||
if ($this->dbi->getVersion() >= 50600) {
|
||||
$gis_from_text_functions = [
|
||||
'ST_GeomFromText',
|
||||
'ST_GeomCollFromText',
|
||||
'ST_LineFromText',
|
||||
'ST_MLineFromText',
|
||||
'ST_PointFromText',
|
||||
'ST_MPointFromText',
|
||||
'ST_PolyFromText',
|
||||
'ST_MPolyFromText',
|
||||
];
|
||||
$gis_from_wkb_functions = [
|
||||
'ST_GeomFromWKB',
|
||||
'ST_GeomCollFromWKB',
|
||||
'ST_LineFromWKB',
|
||||
'ST_MLineFromWKB',
|
||||
'ST_PointFromWKB',
|
||||
'ST_MPointFromWKB',
|
||||
'ST_PolyFromWKB',
|
||||
'ST_MPolyFromWKB',
|
||||
];
|
||||
}
|
||||
|
||||
$mime_map = $this->transformations->getMime($db, $table);
|
||||
if ($mime_map === null) {
|
||||
$mime_map = [];
|
||||
}
|
||||
|
||||
$query_fields = [];
|
||||
$insert_errors = [];
|
||||
$row_skipped = false;
|
||||
$unsaved_values = [];
|
||||
foreach ($loop_array as $rownumber => $where_clause) {
|
||||
// skip fields to be ignored
|
||||
if (! $using_key && isset($_POST['insert_ignore_' . $where_clause])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Defines the SET part of the sql query
|
||||
$query_values = [];
|
||||
|
||||
// Map multi-edit keys to single-level arrays, dependent on how we got the fields
|
||||
$multi_edit_columns = $_POST['fields']['multi_edit'][$rownumber] ?? [];
|
||||
$multi_edit_columns_name = $_POST['fields_name']['multi_edit'][$rownumber] ?? [];
|
||||
$multi_edit_columns_prev = $_POST['fields_prev']['multi_edit'][$rownumber] ?? null;
|
||||
$multi_edit_funcs = $_POST['funcs']['multi_edit'][$rownumber] ?? null;
|
||||
$multi_edit_salt = $_POST['salt']['multi_edit'][$rownumber] ?? null;
|
||||
$multi_edit_columns_type = $_POST['fields_type']['multi_edit'][$rownumber] ?? null;
|
||||
$multi_edit_columns_null = $_POST['fields_null']['multi_edit'][$rownumber] ?? null;
|
||||
$multi_edit_columns_null_prev = $_POST['fields_null_prev']['multi_edit'][$rownumber] ?? null;
|
||||
$multi_edit_auto_increment = $_POST['auto_increment']['multi_edit'][$rownumber] ?? null;
|
||||
$multi_edit_virtual = $_POST['virtual']['multi_edit'][$rownumber] ?? null;
|
||||
|
||||
// When a select field is nullified, it's not present in $_POST
|
||||
// so initialize it; this way, the foreach($multi_edit_columns) will process it
|
||||
foreach (array_keys($multi_edit_columns_name) as $key) {
|
||||
if (isset($multi_edit_columns[$key])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$multi_edit_columns[$key] = '';
|
||||
}
|
||||
|
||||
// Iterate in the order of $multi_edit_columns_name,
|
||||
// not $multi_edit_columns, to avoid problems
|
||||
// when inserting multiple entries
|
||||
$insert_fail = false;
|
||||
foreach ($multi_edit_columns_name as $key => $column_name) {
|
||||
$current_value = $multi_edit_columns[$key];
|
||||
// Note: $key is an md5 of the fieldname. The actual fieldname is
|
||||
// available in $multi_edit_columns_name[$key]
|
||||
|
||||
$file_to_insert = new File();
|
||||
$file_to_insert->checkTblChangeForm((string) $key, (string) $rownumber);
|
||||
|
||||
$possibly_uploaded_val = $file_to_insert->getContent();
|
||||
if ($possibly_uploaded_val !== false) {
|
||||
$current_value = $possibly_uploaded_val;
|
||||
}
|
||||
|
||||
// Apply Input Transformation if defined
|
||||
if (! empty($mime_map[$column_name]) && ! empty($mime_map[$column_name]['input_transformation'])) {
|
||||
$filename = 'libraries/classes/Plugins/Transformations/'
|
||||
. $mime_map[$column_name]['input_transformation'];
|
||||
if (is_file(ROOT_PATH . $filename)) {
|
||||
$classname = $this->transformations->getClassName($filename);
|
||||
if (class_exists($classname)) {
|
||||
/** @var IOTransformationsPlugin $transformation_plugin */
|
||||
$transformation_plugin = new $classname();
|
||||
$transformation_options = $this->transformations->getOptions(
|
||||
$mime_map[$column_name]['input_transformation_options']
|
||||
);
|
||||
$current_value = $transformation_plugin->applyTransformation(
|
||||
$current_value,
|
||||
$transformation_options
|
||||
);
|
||||
// check if transformation was successful or not
|
||||
// and accordingly set error messages & insert_fail
|
||||
if (
|
||||
method_exists($transformation_plugin, 'isSuccess')
|
||||
&& ! $transformation_plugin->isSuccess()
|
||||
) {
|
||||
$insert_fail = true;
|
||||
$row_skipped = true;
|
||||
$insert_errors[] = sprintf(
|
||||
__('Row: %1$s, Column: %2$s, Error: %3$s'),
|
||||
$rownumber,
|
||||
$column_name,
|
||||
$transformation_plugin->getError()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($file_to_insert->isError()) {
|
||||
$insert_errors[] = $file_to_insert->getError();
|
||||
}
|
||||
|
||||
// delete $file_to_insert temporary variable
|
||||
$file_to_insert->cleanUp();
|
||||
|
||||
if (empty($multi_edit_funcs[$key])) {
|
||||
$current_value_as_an_array = $this->insertEdit->getCurrentValueForDifferentTypes(
|
||||
$possibly_uploaded_val,
|
||||
$key,
|
||||
$multi_edit_columns_type,
|
||||
$current_value,
|
||||
$multi_edit_auto_increment,
|
||||
$rownumber,
|
||||
$multi_edit_columns_name,
|
||||
$multi_edit_columns_null,
|
||||
$multi_edit_columns_null_prev,
|
||||
$is_insert,
|
||||
$using_key,
|
||||
$where_clause,
|
||||
$table,
|
||||
$multi_edit_funcs
|
||||
);
|
||||
} else {
|
||||
$current_value_as_an_array = $this->insertEdit->getCurrentValueAsAnArrayForMultipleEdit(
|
||||
$multi_edit_funcs,
|
||||
$multi_edit_salt,
|
||||
$gis_from_text_functions,
|
||||
$current_value,
|
||||
$gis_from_wkb_functions,
|
||||
$func_optional_param,
|
||||
$func_no_param,
|
||||
$key
|
||||
);
|
||||
}
|
||||
|
||||
if (! isset($multi_edit_virtual, $multi_edit_virtual[$key])) {
|
||||
[
|
||||
$query_values,
|
||||
$query_fields,
|
||||
] = $this->insertEdit->getQueryValuesForInsertAndUpdateInMultipleEdit(
|
||||
$multi_edit_columns_name,
|
||||
$multi_edit_columns_null,
|
||||
$current_value,
|
||||
$multi_edit_columns_prev,
|
||||
$multi_edit_funcs,
|
||||
$is_insert,
|
||||
$query_values,
|
||||
$query_fields,
|
||||
$current_value_as_an_array,
|
||||
$value_sets,
|
||||
$key,
|
||||
$multi_edit_columns_null_prev
|
||||
);
|
||||
}
|
||||
|
||||
if (! isset($multi_edit_columns_null[$key])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$multi_edit_columns[$key] = null;
|
||||
}
|
||||
|
||||
// temporarily store rows not inserted
|
||||
// so that they can be populated again.
|
||||
if ($insert_fail) {
|
||||
$unsaved_values[$rownumber] = $multi_edit_columns;
|
||||
}
|
||||
|
||||
if ($insert_fail || count($query_values) <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($is_insert) {
|
||||
$value_sets[] = implode(', ', $query_values);
|
||||
} else {
|
||||
// build update query
|
||||
$clauseIsUnique = $_POST['clause_is_unique'] ?? '';// Should contain 0 or 1
|
||||
$query[] = 'UPDATE ' . Util::backquote($table)
|
||||
. ' SET ' . implode(', ', $query_values)
|
||||
. ' WHERE ' . $where_clause
|
||||
. ($clauseIsUnique ? '' : ' LIMIT 1');
|
||||
}
|
||||
}
|
||||
|
||||
unset(
|
||||
$multi_edit_columns_name,
|
||||
$multi_edit_columns_prev,
|
||||
$multi_edit_funcs,
|
||||
$multi_edit_columns_type,
|
||||
$multi_edit_columns_null,
|
||||
$func_no_param,
|
||||
$multi_edit_auto_increment,
|
||||
$current_value_as_an_array,
|
||||
$key,
|
||||
$current_value,
|
||||
$loop_array,
|
||||
$where_clause,
|
||||
$using_key,
|
||||
$multi_edit_columns_null_prev,
|
||||
$insert_fail
|
||||
);
|
||||
|
||||
// Builds the sql query
|
||||
if ($is_insert && count($value_sets) > 0) {
|
||||
$query = $this->insertEdit->buildSqlQuery($is_insertignore, $query_fields, $value_sets);
|
||||
} elseif (empty($query) && ! isset($_POST['preview_sql']) && ! $row_skipped) {
|
||||
// No change -> move back to the calling script
|
||||
//
|
||||
// Note: logic passes here for inline edit
|
||||
$message = Message::success(__('No change'));
|
||||
// Avoid infinite recursion
|
||||
if ($goto_include === '/table/replace') {
|
||||
$goto_include = '/table/change';
|
||||
}
|
||||
|
||||
$active_page = $goto_include;
|
||||
|
||||
if ($goto_include === '/sql') {
|
||||
/** @var SqlController $controller */
|
||||
$controller = $containerBuilder->get(SqlController::class);
|
||||
$controller();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ($goto_include === '/database/sql') {
|
||||
/** @var DatabaseSqlController $controller */
|
||||
$controller = $containerBuilder->get(DatabaseSqlController::class);
|
||||
$controller();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ($goto_include === '/table/change') {
|
||||
/** @var ChangeController $controller */
|
||||
$controller = $containerBuilder->get(ChangeController::class);
|
||||
$controller();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ($goto_include === '/table/sql') {
|
||||
/** @var TableSqlController $controller */
|
||||
$controller = $containerBuilder->get(TableSqlController::class);
|
||||
$controller();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/** @psalm-suppress UnresolvableInclude */
|
||||
include ROOT_PATH . Core::securePath($goto_include);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
unset($multi_edit_columns, $is_insertignore);
|
||||
|
||||
// If there is a request for SQL previewing.
|
||||
if (isset($_POST['preview_sql'])) {
|
||||
Core::previewSQL($query);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the sql query and get the result, then move back to the calling
|
||||
* page
|
||||
*/
|
||||
[
|
||||
$urlParams,
|
||||
$total_affected_rows,
|
||||
$last_messages,
|
||||
$warning_messages,
|
||||
$error_messages,
|
||||
$return_to_sql_query,
|
||||
] = $this->insertEdit->executeSqlQuery($urlParams, $query);
|
||||
|
||||
if ($is_insert && (count($value_sets) > 0 || $row_skipped)) {
|
||||
$message = Message::getMessageForInsertedRows($total_affected_rows);
|
||||
$unsaved_values = array_values($unsaved_values);
|
||||
} else {
|
||||
$message = Message::getMessageForAffectedRows($total_affected_rows);
|
||||
}
|
||||
|
||||
if ($row_skipped) {
|
||||
$goto_include = '/table/change';
|
||||
$message->addMessagesString($insert_errors, '<br>');
|
||||
$message->isError(true);
|
||||
}
|
||||
|
||||
$message->addMessages($last_messages, '<br>');
|
||||
|
||||
if (! empty($warning_messages)) {
|
||||
$message->addMessagesString($warning_messages, '<br>');
|
||||
$message->isError(true);
|
||||
}
|
||||
|
||||
if (! empty($error_messages)) {
|
||||
$message->addMessagesString($error_messages);
|
||||
$message->isError(true);
|
||||
}
|
||||
|
||||
unset(
|
||||
$error_messages,
|
||||
$warning_messages,
|
||||
$total_affected_rows,
|
||||
$last_messages,
|
||||
$row_skipped,
|
||||
$insert_errors
|
||||
);
|
||||
|
||||
/**
|
||||
* The following section only applies to grid editing.
|
||||
* However, verifying isAjax() is not enough to ensure we are coming from
|
||||
* grid editing. If we are coming from the Edit or Copy link in Browse mode,
|
||||
* ajax_page_request is present in the POST parameters.
|
||||
*/
|
||||
if ($this->response->isAjax() && ! isset($_POST['ajax_page_request'])) {
|
||||
/**
|
||||
* If we are in grid editing, we need to process the relational and
|
||||
* transformed fields, if they were edited. After that, output the correct
|
||||
* link/transformed value and exit
|
||||
*/
|
||||
if (isset($_POST['rel_fields_list']) && $_POST['rel_fields_list'] != '') {
|
||||
$map = $this->relation->getForeigners($db, $table, '', 'both');
|
||||
|
||||
/** @var array<int,array> $relation_fields */
|
||||
$relation_fields = [];
|
||||
parse_str($_POST['rel_fields_list'], $relation_fields);
|
||||
|
||||
// loop for each relation cell
|
||||
foreach ($relation_fields as $cell_index => $curr_rel_field) {
|
||||
foreach ($curr_rel_field as $relation_field => $relation_field_value) {
|
||||
$where_comparison = "='" . $relation_field_value . "'";
|
||||
$dispval = $this->insertEdit->getDisplayValueForForeignTableColumn(
|
||||
$where_comparison,
|
||||
$map,
|
||||
$relation_field
|
||||
);
|
||||
|
||||
$extra_data['relations'][$cell_index] = $this->insertEdit->getLinkForRelationalDisplayField(
|
||||
$map,
|
||||
$relation_field,
|
||||
$where_comparison,
|
||||
$dispval,
|
||||
$relation_field_value
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($_POST['do_transformations']) && $_POST['do_transformations'] == true) {
|
||||
$edited_values = [];
|
||||
parse_str($_POST['transform_fields_list'], $edited_values);
|
||||
|
||||
if (! isset($extra_data)) {
|
||||
$extra_data = [];
|
||||
}
|
||||
|
||||
$transformation_types = [
|
||||
'input_transformation',
|
||||
'transformation',
|
||||
];
|
||||
foreach ($mime_map as $transformation) {
|
||||
$column_name = $transformation['column_name'];
|
||||
foreach ($transformation_types as $type) {
|
||||
$file = Core::securePath($transformation[$type]);
|
||||
$extra_data = $this->insertEdit->transformEditedValues(
|
||||
$db,
|
||||
$table,
|
||||
$transformation,
|
||||
$edited_values,
|
||||
$file,
|
||||
$column_name,
|
||||
$extra_data,
|
||||
$type
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Need to check the inline edited value can be truncated by MySQL
|
||||
// without informing while saving
|
||||
$column_name = $_POST['fields_name']['multi_edit'][0][0];
|
||||
|
||||
$this->insertEdit->verifyWhetherValueCanBeTruncatedAndAppendExtraData(
|
||||
$db,
|
||||
$table,
|
||||
$column_name,
|
||||
$extra_data
|
||||
);
|
||||
|
||||
/**Get the total row count of the table*/
|
||||
$_table = new Table($_POST['table'], $_POST['db']);
|
||||
$extra_data['row_count'] = $_table->countRecords();
|
||||
|
||||
$extra_data['sql_query'] = Generator::getMessage($message, $GLOBALS['display_query']);
|
||||
|
||||
$this->response->setRequestStatus($message->isSuccess());
|
||||
$this->response->addJSON('message', $message);
|
||||
$this->response->addJSON($extra_data);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (! empty($return_to_sql_query)) {
|
||||
$disp_query = $GLOBALS['sql_query'];
|
||||
$disp_message = $message;
|
||||
unset($message);
|
||||
$GLOBALS['sql_query'] = $return_to_sql_query;
|
||||
}
|
||||
|
||||
$this->addScriptFiles(['vendor/jquery/additional-methods.js', 'table/change.js']);
|
||||
|
||||
$active_page = $goto_include;
|
||||
|
||||
/**
|
||||
* If user asked for "and then Insert another new row" we have to remove
|
||||
* WHERE clause information so that /table/change does not go back
|
||||
* to the current record
|
||||
*/
|
||||
if (isset($_POST['after_insert']) && $_POST['after_insert'] === 'new_insert') {
|
||||
unset($_POST['where_clause']);
|
||||
}
|
||||
|
||||
if ($goto_include === '/sql') {
|
||||
/** @var SqlController $controller */
|
||||
$controller = $containerBuilder->get(SqlController::class);
|
||||
$controller();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ($goto_include === '/database/sql') {
|
||||
/** @var DatabaseSqlController $controller */
|
||||
$controller = $containerBuilder->get(DatabaseSqlController::class);
|
||||
$controller();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ($goto_include === '/table/change') {
|
||||
/** @var ChangeController $controller */
|
||||
$controller = $containerBuilder->get(ChangeController::class);
|
||||
$controller();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ($goto_include === '/table/sql') {
|
||||
/** @var TableSqlController $controller */
|
||||
$controller = $containerBuilder->get(TableSqlController::class);
|
||||
$controller();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load target page.
|
||||
*/
|
||||
/** @psalm-suppress UnresolvableInclude */
|
||||
require ROOT_PATH . Core::securePath($goto_include);
|
||||
}
|
||||
}
|
|
@ -1,405 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Controllers\Table;
|
||||
|
||||
use PhpMyAdmin\ConfigStorage\Relation;
|
||||
use PhpMyAdmin\ConfigStorage\RelationCleanup;
|
||||
use PhpMyAdmin\Core;
|
||||
use PhpMyAdmin\DatabaseInterface;
|
||||
use PhpMyAdmin\DbTableExists;
|
||||
use PhpMyAdmin\Operations;
|
||||
use PhpMyAdmin\ResponseRenderer;
|
||||
use PhpMyAdmin\Sql;
|
||||
use PhpMyAdmin\Table\Search;
|
||||
use PhpMyAdmin\Template;
|
||||
use PhpMyAdmin\Transformations;
|
||||
use PhpMyAdmin\Url;
|
||||
use PhpMyAdmin\Util;
|
||||
use PhpMyAdmin\Utils\Gis;
|
||||
|
||||
use function in_array;
|
||||
use function intval;
|
||||
use function mb_strtolower;
|
||||
use function md5;
|
||||
use function preg_match;
|
||||
use function preg_replace;
|
||||
use function str_ireplace;
|
||||
use function str_replace;
|
||||
use function strncasecmp;
|
||||
use function strtoupper;
|
||||
|
||||
/**
|
||||
* Handles table search tab.
|
||||
*
|
||||
* Display table search form, create SQL query from form data
|
||||
* and call Sql::executeQueryAndSendQueryResponse() to execute it.
|
||||
*/
|
||||
class SearchController extends AbstractController
|
||||
{
|
||||
/**
|
||||
* Names of columns
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $columnNames;
|
||||
/**
|
||||
* Types of columns
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $columnTypes;
|
||||
/**
|
||||
* Types of columns without any replacement
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $originalColumnTypes;
|
||||
/**
|
||||
* Collations of columns
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $columnCollations;
|
||||
/**
|
||||
* Null Flags of columns
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $columnNullFlags;
|
||||
/**
|
||||
* Whether a geometry column is present
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
private $geomColumnFlag;
|
||||
/**
|
||||
* Foreign Keys
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $foreigners;
|
||||
|
||||
/** @var Search */
|
||||
private $search;
|
||||
|
||||
/** @var Relation */
|
||||
private $relation;
|
||||
|
||||
/** @var DatabaseInterface */
|
||||
private $dbi;
|
||||
|
||||
public function __construct(
|
||||
ResponseRenderer $response,
|
||||
Template $template,
|
||||
string $db,
|
||||
string $table,
|
||||
Search $search,
|
||||
Relation $relation,
|
||||
DatabaseInterface $dbi
|
||||
) {
|
||||
parent::__construct($response, $template, $db, $table);
|
||||
$this->search = $search;
|
||||
$this->relation = $relation;
|
||||
$this->dbi = $dbi;
|
||||
|
||||
$this->columnNames = [];
|
||||
$this->columnTypes = [];
|
||||
$this->originalColumnTypes = [];
|
||||
$this->columnCollations = [];
|
||||
$this->columnNullFlags = [];
|
||||
$this->geomColumnFlag = false;
|
||||
$this->foreigners = [];
|
||||
$this->loadTableInfo();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all the columns of a table along with their types, collations
|
||||
* and whether null or not.
|
||||
*/
|
||||
private function loadTableInfo(): void
|
||||
{
|
||||
// Gets the list and number of columns
|
||||
$columns = $this->dbi->getColumns($this->db, $this->table, true);
|
||||
// Get details about the geometry functions
|
||||
$geom_types = Gis::getDataTypes();
|
||||
|
||||
foreach ($columns as $row) {
|
||||
// set column name
|
||||
$this->columnNames[] = $row['Field'];
|
||||
|
||||
$type = (string) $row['Type'];
|
||||
// before any replacement
|
||||
$this->originalColumnTypes[] = mb_strtolower($type);
|
||||
// check whether table contains geometric columns
|
||||
if (in_array($type, $geom_types)) {
|
||||
$this->geomColumnFlag = true;
|
||||
}
|
||||
|
||||
// reformat mysql query output
|
||||
if (strncasecmp($type, 'set', 3) == 0 || strncasecmp($type, 'enum', 4) == 0) {
|
||||
$type = str_replace(',', ', ', $type);
|
||||
} else {
|
||||
// strip the "BINARY" attribute, except if we find "BINARY(" because
|
||||
// this would be a BINARY or VARBINARY column type
|
||||
if (! preg_match('@BINARY[\(]@i', $type)) {
|
||||
$type = str_ireplace('BINARY', '', $type);
|
||||
}
|
||||
|
||||
$type = str_ireplace('ZEROFILL', '', $type);
|
||||
$type = str_ireplace('UNSIGNED', '', $type);
|
||||
$type = mb_strtolower($type);
|
||||
}
|
||||
|
||||
if (empty($type)) {
|
||||
$type = ' ';
|
||||
}
|
||||
|
||||
$this->columnTypes[] = $type;
|
||||
$this->columnNullFlags[] = $row['Null'];
|
||||
$this->columnCollations[] = ! empty($row['Collation']) && $row['Collation'] !== 'NULL'
|
||||
? $row['Collation']
|
||||
: '';
|
||||
}
|
||||
|
||||
// Retrieve foreign keys
|
||||
$this->foreigners = $this->relation->getForeigners($this->db, $this->table);
|
||||
}
|
||||
|
||||
/**
|
||||
* Index action
|
||||
*/
|
||||
public function __invoke(): void
|
||||
{
|
||||
global $db, $table, $urlParams, $cfg, $errorUrl;
|
||||
|
||||
Util::checkParameters(['db', 'table']);
|
||||
|
||||
$urlParams = ['db' => $db, 'table' => $table];
|
||||
$errorUrl = Util::getScriptNameForOption($cfg['DefaultTabTable'], 'table');
|
||||
$errorUrl .= Url::getCommon($urlParams, '&');
|
||||
|
||||
DbTableExists::check();
|
||||
|
||||
$this->addScriptFiles([
|
||||
'makegrid.js',
|
||||
'sql.js',
|
||||
'table/select.js',
|
||||
'table/change.js',
|
||||
'vendor/jquery/jquery.uitablefilter.js',
|
||||
'gis_data_editor.js',
|
||||
]);
|
||||
|
||||
if (isset($_POST['range_search'])) {
|
||||
$this->rangeSearchAction();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* No selection criteria received -> display the selection form
|
||||
*/
|
||||
if (! isset($_POST['columnsToDisplay']) && ! isset($_POST['displayAllColumns'])) {
|
||||
$this->displaySelectionFormAction();
|
||||
} else {
|
||||
$this->doSelectionAction();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get data row action
|
||||
*/
|
||||
public function getDataRowAction(): void
|
||||
{
|
||||
if (! Core::checkSqlQuerySignature($_POST['where_clause'], $_POST['where_clause_sign'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$extra_data = [];
|
||||
$row_info_query = 'SELECT * FROM ' . Util::backquote($_POST['db']) . '.'
|
||||
. Util::backquote($_POST['table']) . ' WHERE ' . $_POST['where_clause'];
|
||||
$result = $this->dbi->query($row_info_query . ';');
|
||||
$fields_meta = $this->dbi->getFieldsMeta($result);
|
||||
while ($row = $result->fetchAssoc()) {
|
||||
// for bit fields we need to convert them to printable form
|
||||
$i = 0;
|
||||
foreach ($row as $col => $val) {
|
||||
if (isset($fields_meta[$i]) && $fields_meta[$i]->isMappedTypeBit) {
|
||||
$row[$col] = Util::printableBitValue((int) $val, (int) $fields_meta[$i]->length);
|
||||
}
|
||||
|
||||
$i++;
|
||||
}
|
||||
|
||||
$extra_data['row_info'] = $row;
|
||||
}
|
||||
|
||||
$this->response->addJSON($extra_data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Do selection action
|
||||
*/
|
||||
public function doSelectionAction(): void
|
||||
{
|
||||
/**
|
||||
* Selection criteria have been submitted -> do the work
|
||||
*/
|
||||
$sql_query = $this->search->buildSqlQuery();
|
||||
|
||||
/**
|
||||
* Add this to ensure following procedures included running correctly.
|
||||
*/
|
||||
$sql = new Sql(
|
||||
$this->dbi,
|
||||
$this->relation,
|
||||
new RelationCleanup($this->dbi, $this->relation),
|
||||
new Operations($this->dbi, $this->relation),
|
||||
new Transformations(),
|
||||
$this->template
|
||||
);
|
||||
|
||||
$this->response->addHTML($sql->executeQueryAndSendQueryResponse(
|
||||
null, // analyzed_sql_results
|
||||
false, // is_gotofile
|
||||
$this->db, // db
|
||||
$this->table, // table
|
||||
null, // find_real_end
|
||||
null, // sql_query_for_bookmark
|
||||
null, // extra_data
|
||||
null, // message_to_show
|
||||
null, // sql_data
|
||||
$GLOBALS['goto'], // goto
|
||||
null, // disp_query
|
||||
null, // disp_message
|
||||
$sql_query, // sql_query
|
||||
null // complete_query
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Display selection form action
|
||||
*/
|
||||
public function displaySelectionFormAction(): void
|
||||
{
|
||||
global $goto, $cfg;
|
||||
|
||||
if (! isset($goto)) {
|
||||
$goto = Util::getScriptNameForOption($cfg['DefaultTabTable'], 'table');
|
||||
}
|
||||
|
||||
$this->render('table/search/index', [
|
||||
'db' => $this->db,
|
||||
'table' => $this->table,
|
||||
'goto' => $goto,
|
||||
'self' => $this,
|
||||
'geom_column_flag' => $this->geomColumnFlag,
|
||||
'column_names' => $this->columnNames,
|
||||
'column_types' => $this->columnTypes,
|
||||
'column_collations' => $this->columnCollations,
|
||||
'default_sliders_state' => $cfg['InitialSlidersState'],
|
||||
'max_rows' => intval($cfg['MaxRows']),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Range search action
|
||||
*/
|
||||
public function rangeSearchAction(): void
|
||||
{
|
||||
$min_max = $this->getColumnMinMax($_POST['column']);
|
||||
$this->response->addJSON('column_data', $min_max);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds minimum and maximum value of a given column.
|
||||
*
|
||||
* @param string $column Column name
|
||||
*
|
||||
* @return array|null
|
||||
*/
|
||||
public function getColumnMinMax($column): ?array
|
||||
{
|
||||
$sql_query = 'SELECT MIN(' . Util::backquote($column) . ') AS `min`, '
|
||||
. 'MAX(' . Util::backquote($column) . ') AS `max` '
|
||||
. 'FROM ' . Util::backquote($this->db) . '.'
|
||||
. Util::backquote($this->table);
|
||||
|
||||
return $this->dbi->fetchSingleRow($sql_query);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a column's type, collation, operators list, and criteria value
|
||||
* to display in table search form
|
||||
*
|
||||
* @param int $search_index Row number in table search form
|
||||
* @param int $column_index Column index in ColumnNames array
|
||||
*
|
||||
* @return array Array containing column's properties
|
||||
*/
|
||||
public function getColumnProperties($search_index, $column_index)
|
||||
{
|
||||
$selected_operator = ($_POST['criteriaColumnOperators'][$search_index] ?? '');
|
||||
$entered_value = ($_POST['criteriaValues'] ?? '');
|
||||
//Gets column's type and collation
|
||||
$type = $this->columnTypes[$column_index];
|
||||
$collation = $this->columnCollations[$column_index];
|
||||
$cleanType = preg_replace('@\(.*@s', '', $type);
|
||||
//Gets column's comparison operators depending on column type
|
||||
$typeOperators = $this->dbi->types->getTypeOperatorsHtml(
|
||||
$cleanType,
|
||||
$this->columnNullFlags[$column_index],
|
||||
$selected_operator
|
||||
);
|
||||
$func = $this->template->render('table/search/column_comparison_operators', [
|
||||
'search_index' => $search_index,
|
||||
'type_operators' => $typeOperators,
|
||||
]);
|
||||
//Gets link to browse foreign data(if any) and criteria inputbox
|
||||
$foreignData = $this->relation->getForeignData(
|
||||
$this->foreigners,
|
||||
$this->columnNames[$column_index],
|
||||
false,
|
||||
'',
|
||||
''
|
||||
);
|
||||
$htmlAttributes = '';
|
||||
if (in_array($cleanType, $this->dbi->types->getIntegerTypes())) {
|
||||
$extractedColumnspec = Util::extractColumnSpec($this->originalColumnTypes[$column_index]);
|
||||
$is_unsigned = $extractedColumnspec['unsigned'];
|
||||
$minMaxValues = $this->dbi->types->getIntegerRange($cleanType, ! $is_unsigned);
|
||||
$htmlAttributes = 'data-min="' . $minMaxValues[0] . '" '
|
||||
. 'data-max="' . $minMaxValues[1] . '"';
|
||||
}
|
||||
|
||||
$htmlAttributes .= ' onfocus="return '
|
||||
. 'verifyAfterSearchFieldChange(' . $search_index . ', \'#tbl_search_form\')"';
|
||||
|
||||
$value = $this->template->render('table/search/input_box', [
|
||||
'str' => '',
|
||||
'column_type' => (string) $type,
|
||||
'column_data_type' => strtoupper($cleanType),
|
||||
'html_attributes' => $htmlAttributes,
|
||||
'column_id' => 'fieldID_',
|
||||
'in_zoom_search_edit' => false,
|
||||
'foreigners' => $this->foreigners,
|
||||
'column_name' => $this->columnNames[$column_index],
|
||||
'column_name_hash' => md5($this->columnNames[$column_index]),
|
||||
'foreign_data' => $foreignData,
|
||||
'table' => $this->table,
|
||||
'column_index' => $search_index,
|
||||
'foreign_max_limit' => $GLOBALS['cfg']['ForeignKeyMaxLimit'],
|
||||
'criteria_values' => $entered_value,
|
||||
'db' => $this->db,
|
||||
'in_fbs' => true,
|
||||
]);
|
||||
|
||||
return [
|
||||
'type' => $type,
|
||||
'collation' => $collation,
|
||||
'func' => $func,
|
||||
'value' => $value,
|
||||
];
|
||||
}
|
||||
}
|
|
@ -1,71 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Controllers\Table;
|
||||
|
||||
use PhpMyAdmin\Config\PageSettings;
|
||||
use PhpMyAdmin\DbTableExists;
|
||||
use PhpMyAdmin\ResponseRenderer;
|
||||
use PhpMyAdmin\SqlQueryForm;
|
||||
use PhpMyAdmin\Template;
|
||||
use PhpMyAdmin\Url;
|
||||
use PhpMyAdmin\Util;
|
||||
|
||||
use function htmlspecialchars;
|
||||
|
||||
/**
|
||||
* Table SQL executor
|
||||
*/
|
||||
final class SqlController extends AbstractController
|
||||
{
|
||||
/** @var SqlQueryForm */
|
||||
private $sqlQueryForm;
|
||||
|
||||
public function __construct(
|
||||
ResponseRenderer $response,
|
||||
Template $template,
|
||||
string $db,
|
||||
string $table,
|
||||
SqlQueryForm $sqlQueryForm
|
||||
) {
|
||||
parent::__construct($response, $template, $db, $table);
|
||||
$this->sqlQueryForm = $sqlQueryForm;
|
||||
}
|
||||
|
||||
public function __invoke(): void
|
||||
{
|
||||
global $errorUrl, $goto, $back, $db, $table, $cfg;
|
||||
|
||||
$this->addScriptFiles(['makegrid.js', 'vendor/jquery/jquery.uitablefilter.js', 'sql.js']);
|
||||
|
||||
$pageSettings = new PageSettings('Sql');
|
||||
$this->response->addHTML($pageSettings->getErrorHTML());
|
||||
$this->response->addHTML($pageSettings->getHTML());
|
||||
|
||||
Util::checkParameters(['db', 'table']);
|
||||
|
||||
$url_params = ['db' => $db, 'table' => $table];
|
||||
$errorUrl = Util::getScriptNameForOption($cfg['DefaultTabTable'], 'table');
|
||||
$errorUrl .= Url::getCommon($url_params, '&');
|
||||
|
||||
DbTableExists::check();
|
||||
|
||||
/**
|
||||
* After a syntax error, we return to this script
|
||||
* with the typed query in the textarea.
|
||||
*/
|
||||
$goto = Url::getFromRoute('/table/sql');
|
||||
$back = Url::getFromRoute('/table/sql');
|
||||
|
||||
$this->response->addHTML($this->sqlQueryForm->getHtml(
|
||||
$db,
|
||||
$table,
|
||||
$_GET['sql_query'] ?? true,
|
||||
false,
|
||||
isset($_POST['delimiter'])
|
||||
? htmlspecialchars($_POST['delimiter'])
|
||||
: ';'
|
||||
));
|
||||
}
|
||||
}
|
|
@ -1,74 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Controllers\Table\Structure;
|
||||
|
||||
use PhpMyAdmin\Controllers\Table\AbstractController;
|
||||
use PhpMyAdmin\Controllers\Table\StructureController;
|
||||
use PhpMyAdmin\DatabaseInterface;
|
||||
use PhpMyAdmin\Message;
|
||||
use PhpMyAdmin\ResponseRenderer;
|
||||
use PhpMyAdmin\Template;
|
||||
use PhpMyAdmin\Util;
|
||||
|
||||
use function __;
|
||||
use function count;
|
||||
|
||||
final class AddIndexController extends AbstractController
|
||||
{
|
||||
/** @var DatabaseInterface */
|
||||
private $dbi;
|
||||
|
||||
/** @var StructureController */
|
||||
private $structureController;
|
||||
|
||||
public function __construct(
|
||||
ResponseRenderer $response,
|
||||
Template $template,
|
||||
string $db,
|
||||
string $table,
|
||||
DatabaseInterface $dbi,
|
||||
StructureController $structureController
|
||||
) {
|
||||
parent::__construct($response, $template, $db, $table);
|
||||
$this->dbi = $dbi;
|
||||
$this->structureController = $structureController;
|
||||
}
|
||||
|
||||
public function __invoke(): void
|
||||
{
|
||||
global $sql_query, $db, $table, $message;
|
||||
|
||||
$selected = $_POST['selected_fld'] ?? [];
|
||||
|
||||
if (empty($selected)) {
|
||||
$this->response->setRequestStatus(false);
|
||||
$this->response->addJSON('message', __('No column selected.'));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$i = 1;
|
||||
$selectedCount = count($selected);
|
||||
$sql_query = 'ALTER TABLE ' . Util::backquote($table) . ' ADD INDEX(';
|
||||
|
||||
foreach ($selected as $field) {
|
||||
$sql_query .= Util::backquote($field);
|
||||
$sql_query .= $i++ === $selectedCount ? ');' : ', ';
|
||||
}
|
||||
|
||||
$this->dbi->selectDb($db);
|
||||
$result = $this->dbi->tryQuery($sql_query);
|
||||
|
||||
if (! $result) {
|
||||
$message = Message::error($this->dbi->getError());
|
||||
}
|
||||
|
||||
if (empty($message)) {
|
||||
$message = Message::success();
|
||||
}
|
||||
|
||||
($this->structureController)();
|
||||
}
|
||||
}
|
|
@ -1,44 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Controllers\Table\Structure;
|
||||
|
||||
use PhpMyAdmin\Controllers\Sql\SqlController;
|
||||
use PhpMyAdmin\Controllers\Table\AbstractController;
|
||||
use PhpMyAdmin\Controllers\Table\StructureController;
|
||||
use PhpMyAdmin\ResponseRenderer;
|
||||
use PhpMyAdmin\Template;
|
||||
|
||||
final class AddKeyController extends AbstractController
|
||||
{
|
||||
/** @var SqlController */
|
||||
private $sqlController;
|
||||
|
||||
/** @var StructureController */
|
||||
private $structureController;
|
||||
|
||||
public function __construct(
|
||||
ResponseRenderer $response,
|
||||
Template $template,
|
||||
string $db,
|
||||
string $table,
|
||||
SqlController $sqlController,
|
||||
StructureController $structureController
|
||||
) {
|
||||
parent::__construct($response, $template, $db, $table);
|
||||
$this->sqlController = $sqlController;
|
||||
$this->structureController = $structureController;
|
||||
}
|
||||
|
||||
public function __invoke(): void
|
||||
{
|
||||
global $reload;
|
||||
|
||||
($this->sqlController)();
|
||||
|
||||
$reload = true;
|
||||
|
||||
($this->structureController)();
|
||||
}
|
||||
}
|
|
@ -1,84 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Controllers\Table\Structure;
|
||||
|
||||
use PhpMyAdmin\Controllers\Table\AbstractController;
|
||||
use PhpMyAdmin\ParseAnalyze;
|
||||
use PhpMyAdmin\ResponseRenderer;
|
||||
use PhpMyAdmin\Sql;
|
||||
use PhpMyAdmin\Template;
|
||||
use PhpMyAdmin\Url;
|
||||
use PhpMyAdmin\Util;
|
||||
|
||||
use function __;
|
||||
use function implode;
|
||||
use function sprintf;
|
||||
|
||||
final class BrowseController extends AbstractController
|
||||
{
|
||||
/** @var Sql */
|
||||
private $sql;
|
||||
|
||||
public function __construct(ResponseRenderer $response, Template $template, string $db, string $table, Sql $sql)
|
||||
{
|
||||
parent::__construct($response, $template, $db, $table);
|
||||
$this->sql = $sql;
|
||||
}
|
||||
|
||||
public function __invoke(): void
|
||||
{
|
||||
if (empty($_POST['selected_fld'])) {
|
||||
$this->response->setRequestStatus(false);
|
||||
$this->response->addJSON('message', __('No column selected.'));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->displayTableBrowseForSelectedColumns($GLOBALS['goto']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Function to display table browse for selected columns
|
||||
*
|
||||
* @param string $goto goto page url
|
||||
*/
|
||||
private function displayTableBrowseForSelectedColumns($goto): void
|
||||
{
|
||||
$GLOBALS['active_page'] = Url::getFromRoute('/sql');
|
||||
$fields = [];
|
||||
foreach ($_POST['selected_fld'] as $sval) {
|
||||
$fields[] = Util::backquote($sval);
|
||||
}
|
||||
|
||||
$sql_query = sprintf(
|
||||
'SELECT %s FROM %s.%s',
|
||||
implode(', ', $fields),
|
||||
Util::backquote($this->db),
|
||||
Util::backquote($this->table)
|
||||
);
|
||||
|
||||
// Parse and analyze the query
|
||||
[$analyzed_sql_results, $this->db] = ParseAnalyze::sqlQuery($sql_query, $this->db);
|
||||
|
||||
$this->response->addHTML(
|
||||
$this->sql->executeQueryAndGetQueryResponse(
|
||||
$analyzed_sql_results ?? '',
|
||||
false, // is_gotofile
|
||||
$this->db, // db
|
||||
$this->table, // table
|
||||
null, // find_real_end
|
||||
null, // sql_query_for_bookmark
|
||||
null, // extra_data
|
||||
null, // message_to_show
|
||||
null, // sql_data
|
||||
$goto, // goto
|
||||
null, // disp_query
|
||||
null, // disp_message
|
||||
$sql_query, // sql_query
|
||||
null // complete_query
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,62 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Controllers\Table\Structure;
|
||||
|
||||
use PhpMyAdmin\Controllers\Table\AbstractController;
|
||||
use PhpMyAdmin\Controllers\Table\StructureController;
|
||||
use PhpMyAdmin\Database\CentralColumns;
|
||||
use PhpMyAdmin\Message;
|
||||
use PhpMyAdmin\ResponseRenderer;
|
||||
use PhpMyAdmin\Template;
|
||||
|
||||
use function __;
|
||||
|
||||
final class CentralColumnsAddController extends AbstractController
|
||||
{
|
||||
/** @var CentralColumns */
|
||||
private $centralColumns;
|
||||
|
||||
/** @var StructureController */
|
||||
private $structureController;
|
||||
|
||||
public function __construct(
|
||||
ResponseRenderer $response,
|
||||
Template $template,
|
||||
string $db,
|
||||
string $table,
|
||||
CentralColumns $centralColumns,
|
||||
StructureController $structureController
|
||||
) {
|
||||
parent::__construct($response, $template, $db, $table);
|
||||
$this->centralColumns = $centralColumns;
|
||||
$this->structureController = $structureController;
|
||||
}
|
||||
|
||||
public function __invoke(): void
|
||||
{
|
||||
global $message;
|
||||
|
||||
$selected = $_POST['selected_fld'] ?? [];
|
||||
|
||||
if (empty($selected)) {
|
||||
$this->response->setRequestStatus(false);
|
||||
$this->response->addJSON('message', __('No column selected.'));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$centralColsError = $this->centralColumns->syncUniqueColumns($selected, false);
|
||||
|
||||
if ($centralColsError instanceof Message) {
|
||||
$message = $centralColsError;
|
||||
}
|
||||
|
||||
if (empty($message)) {
|
||||
$message = Message::success();
|
||||
}
|
||||
|
||||
($this->structureController)();
|
||||
}
|
||||
}
|
|
@ -1,62 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Controllers\Table\Structure;
|
||||
|
||||
use PhpMyAdmin\Controllers\Table\AbstractController;
|
||||
use PhpMyAdmin\Controllers\Table\StructureController;
|
||||
use PhpMyAdmin\Database\CentralColumns;
|
||||
use PhpMyAdmin\Message;
|
||||
use PhpMyAdmin\ResponseRenderer;
|
||||
use PhpMyAdmin\Template;
|
||||
|
||||
use function __;
|
||||
|
||||
final class CentralColumnsRemoveController extends AbstractController
|
||||
{
|
||||
/** @var CentralColumns */
|
||||
private $centralColumns;
|
||||
|
||||
/** @var StructureController */
|
||||
private $structureController;
|
||||
|
||||
public function __construct(
|
||||
ResponseRenderer $response,
|
||||
Template $template,
|
||||
string $db,
|
||||
string $table,
|
||||
CentralColumns $centralColumns,
|
||||
StructureController $structureController
|
||||
) {
|
||||
parent::__construct($response, $template, $db, $table);
|
||||
$this->centralColumns = $centralColumns;
|
||||
$this->structureController = $structureController;
|
||||
}
|
||||
|
||||
public function __invoke(): void
|
||||
{
|
||||
global $db, $message;
|
||||
|
||||
$selected = $_POST['selected_fld'] ?? [];
|
||||
|
||||
if (empty($selected)) {
|
||||
$this->response->setRequestStatus(false);
|
||||
$this->response->addJSON('message', __('No column selected.'));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$centralColsError = $this->centralColumns->deleteColumnsFromList($db, $selected, false);
|
||||
|
||||
if ($centralColsError instanceof Message) {
|
||||
$message = $centralColsError;
|
||||
}
|
||||
|
||||
if (empty($message)) {
|
||||
$message = Message::success();
|
||||
}
|
||||
|
||||
($this->structureController)();
|
||||
}
|
||||
}
|
|
@ -1,125 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Controllers\Table\Structure;
|
||||
|
||||
use PhpMyAdmin\CheckUserPrivileges;
|
||||
use PhpMyAdmin\ConfigStorage\Relation;
|
||||
use PhpMyAdmin\Controllers\Table\AbstractController;
|
||||
use PhpMyAdmin\DatabaseInterface;
|
||||
use PhpMyAdmin\Message;
|
||||
use PhpMyAdmin\ResponseRenderer;
|
||||
use PhpMyAdmin\Table\ColumnsDefinition;
|
||||
use PhpMyAdmin\Template;
|
||||
use PhpMyAdmin\Transformations;
|
||||
use PhpMyAdmin\Url;
|
||||
|
||||
use function __;
|
||||
use function count;
|
||||
|
||||
final class ChangeController extends AbstractController
|
||||
{
|
||||
/** @var Relation */
|
||||
private $relation;
|
||||
|
||||
/** @var Transformations */
|
||||
private $transformations;
|
||||
|
||||
/** @var DatabaseInterface */
|
||||
private $dbi;
|
||||
|
||||
public function __construct(
|
||||
ResponseRenderer $response,
|
||||
Template $template,
|
||||
string $db,
|
||||
string $table,
|
||||
Relation $relation,
|
||||
Transformations $transformations,
|
||||
DatabaseInterface $dbi
|
||||
) {
|
||||
parent::__construct($response, $template, $db, $table);
|
||||
$this->relation = $relation;
|
||||
$this->transformations = $transformations;
|
||||
$this->dbi = $dbi;
|
||||
}
|
||||
|
||||
public function __invoke(): void
|
||||
{
|
||||
if (isset($_GET['change_column'])) {
|
||||
$this->displayHtmlForColumnChange(null);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$selected = $_POST['selected_fld'] ?? [];
|
||||
|
||||
if (empty($selected)) {
|
||||
$this->response->setRequestStatus(false);
|
||||
$this->response->addJSON('message', __('No column selected.'));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->displayHtmlForColumnChange($selected);
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays HTML for changing one or more columns
|
||||
*
|
||||
* @param array|null $selected the selected columns
|
||||
*/
|
||||
private function displayHtmlForColumnChange(?array $selected): void
|
||||
{
|
||||
global $action, $num_fields;
|
||||
|
||||
if (empty($selected)) {
|
||||
$selected[] = $_REQUEST['field'];
|
||||
$selected_cnt = 1;
|
||||
} else { // from a multiple submit
|
||||
$selected_cnt = count($selected);
|
||||
}
|
||||
|
||||
/**
|
||||
* @todo optimize in case of multiple fields to modify
|
||||
*/
|
||||
$fields_meta = [];
|
||||
for ($i = 0; $i < $selected_cnt; $i++) {
|
||||
$value = $this->dbi->getColumn($this->db, $this->table, $selected[$i], true);
|
||||
if (count($value) === 0) {
|
||||
$message = Message::error(
|
||||
__('Failed to get description of column %s!')
|
||||
);
|
||||
$message->addParam($selected[$i]);
|
||||
$this->response->addHTML($message->getDisplay());
|
||||
} else {
|
||||
$fields_meta[] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
$num_fields = count($fields_meta);
|
||||
|
||||
$action = Url::getFromRoute('/table/structure/save');
|
||||
|
||||
/**
|
||||
* Form for changing properties.
|
||||
*/
|
||||
$checkUserPrivileges = new CheckUserPrivileges($this->dbi);
|
||||
$checkUserPrivileges->getPrivileges();
|
||||
|
||||
$this->addScriptFiles(['vendor/jquery/jquery.uitablefilter.js', 'indexes.js']);
|
||||
|
||||
$templateData = ColumnsDefinition::displayForm(
|
||||
$this->transformations,
|
||||
$this->relation,
|
||||
$this->dbi,
|
||||
$action,
|
||||
$num_fields,
|
||||
null,
|
||||
$selected,
|
||||
$fields_meta
|
||||
);
|
||||
|
||||
$this->render('columns_definitions/column_definitions_form', $templateData);
|
||||
}
|
||||
}
|
|
@ -1,74 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Controllers\Table\Structure;
|
||||
|
||||
use PhpMyAdmin\Controllers\Table\AbstractController;
|
||||
use PhpMyAdmin\Controllers\Table\StructureController;
|
||||
use PhpMyAdmin\DatabaseInterface;
|
||||
use PhpMyAdmin\Message;
|
||||
use PhpMyAdmin\ResponseRenderer;
|
||||
use PhpMyAdmin\Template;
|
||||
use PhpMyAdmin\Util;
|
||||
|
||||
use function __;
|
||||
use function count;
|
||||
|
||||
final class FulltextController extends AbstractController
|
||||
{
|
||||
/** @var DatabaseInterface */
|
||||
private $dbi;
|
||||
|
||||
/** @var StructureController */
|
||||
private $structureController;
|
||||
|
||||
public function __construct(
|
||||
ResponseRenderer $response,
|
||||
Template $template,
|
||||
string $db,
|
||||
string $table,
|
||||
DatabaseInterface $dbi,
|
||||
StructureController $structureController
|
||||
) {
|
||||
parent::__construct($response, $template, $db, $table);
|
||||
$this->dbi = $dbi;
|
||||
$this->structureController = $structureController;
|
||||
}
|
||||
|
||||
public function __invoke(): void
|
||||
{
|
||||
global $sql_query, $db, $table, $message;
|
||||
|
||||
$selected = $_POST['selected_fld'] ?? [];
|
||||
|
||||
if (empty($selected)) {
|
||||
$this->response->setRequestStatus(false);
|
||||
$this->response->addJSON('message', __('No column selected.'));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$i = 1;
|
||||
$selectedCount = count($selected);
|
||||
$sql_query = 'ALTER TABLE ' . Util::backquote($table) . ' ADD FULLTEXT(';
|
||||
|
||||
foreach ($selected as $field) {
|
||||
$sql_query .= Util::backquote($field);
|
||||
$sql_query .= $i++ === $selectedCount ? ');' : ', ';
|
||||
}
|
||||
|
||||
$this->dbi->selectDb($db);
|
||||
$result = $this->dbi->tryQuery($sql_query);
|
||||
|
||||
if (! $result) {
|
||||
$message = Message::error($this->dbi->getError());
|
||||
}
|
||||
|
||||
if (empty($message)) {
|
||||
$message = Message::success();
|
||||
}
|
||||
|
||||
($this->structureController)();
|
||||
}
|
||||
}
|
|
@ -1,183 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Controllers\Table\Structure;
|
||||
|
||||
use PhpMyAdmin\Controllers\Table\AbstractController;
|
||||
use PhpMyAdmin\DatabaseInterface;
|
||||
use PhpMyAdmin\Message;
|
||||
use PhpMyAdmin\ResponseRenderer;
|
||||
use PhpMyAdmin\Table;
|
||||
use PhpMyAdmin\Template;
|
||||
use PhpMyAdmin\Util;
|
||||
|
||||
use function __;
|
||||
use function array_keys;
|
||||
use function array_splice;
|
||||
use function count;
|
||||
use function implode;
|
||||
use function in_array;
|
||||
use function is_array;
|
||||
use function mb_strtoupper;
|
||||
use function sprintf;
|
||||
use function str_replace;
|
||||
|
||||
final class MoveColumnsController extends AbstractController
|
||||
{
|
||||
/** @var Table The table object */
|
||||
private $tableObj;
|
||||
|
||||
/** @var DatabaseInterface */
|
||||
private $dbi;
|
||||
|
||||
public function __construct(
|
||||
ResponseRenderer $response,
|
||||
Template $template,
|
||||
string $db,
|
||||
string $table,
|
||||
DatabaseInterface $dbi
|
||||
) {
|
||||
parent::__construct($response, $template, $db, $table);
|
||||
$this->dbi = $dbi;
|
||||
$this->tableObj = $this->dbi->getTable($this->db, $this->table);
|
||||
}
|
||||
|
||||
public function __invoke(): void
|
||||
{
|
||||
if (! isset($_POST['move_columns']) || ! is_array($_POST['move_columns']) || ! $this->response->isAjax()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->dbi->selectDb($this->db);
|
||||
|
||||
/**
|
||||
* load the definitions for all columns
|
||||
*/
|
||||
$columns = $this->dbi->getColumnsFull($this->db, $this->table);
|
||||
$column_names = array_keys($columns);
|
||||
$changes = [];
|
||||
|
||||
// @see https://mariadb.com/kb/en/library/changes-improvements-in-mariadb-102/#information-schema
|
||||
$usesLiteralNull = $this->dbi->isMariaDB() && $this->dbi->getVersion() >= 100200;
|
||||
$defaultNullValue = $usesLiteralNull ? 'NULL' : null;
|
||||
// move columns from first to last
|
||||
for ($i = 0, $l = count($_POST['move_columns']); $i < $l; $i++) {
|
||||
$column = $_POST['move_columns'][$i];
|
||||
// is this column already correctly placed?
|
||||
if ($column_names[$i] == $column) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// it is not, let's move it to index $i
|
||||
$data = $columns[$column];
|
||||
$extracted_columnspec = Util::extractColumnSpec($data['Type']);
|
||||
if (isset($data['Extra']) && $data['Extra'] === 'on update CURRENT_TIMESTAMP') {
|
||||
$extracted_columnspec['attribute'] = $data['Extra'];
|
||||
unset($data['Extra']);
|
||||
}
|
||||
|
||||
$timeType = $data['Type'] === 'timestamp' || $data['Type'] === 'datetime';
|
||||
$timeDefault = $data['Default'] === 'CURRENT_TIMESTAMP' || $data['Default'] === 'current_timestamp()';
|
||||
$current_timestamp = $timeType && $timeDefault;
|
||||
|
||||
$uuidType = $data['Type'] === 'uuid';
|
||||
$uuidDefault = $data['Default'] === 'UUID' || $data['Default'] === 'uuid()';
|
||||
$uuid = $uuidType && $uuidDefault;
|
||||
|
||||
// @see https://mariadb.com/kb/en/library/information-schema-columns-table/#examples
|
||||
if ($data['Null'] === 'YES' && in_array($data['Default'], [$defaultNullValue, null])) {
|
||||
$default_type = 'NULL';
|
||||
} elseif ($current_timestamp) {
|
||||
$default_type = 'CURRENT_TIMESTAMP';
|
||||
} elseif ($uuid) {
|
||||
$default_type = 'UUID';
|
||||
} elseif ($data['Default'] === null) {
|
||||
$default_type = 'NONE';
|
||||
} else {
|
||||
$default_type = 'USER_DEFINED';
|
||||
}
|
||||
|
||||
$virtual = [
|
||||
'VIRTUAL',
|
||||
'PERSISTENT',
|
||||
'VIRTUAL GENERATED',
|
||||
'STORED GENERATED',
|
||||
];
|
||||
$data['Virtuality'] = '';
|
||||
$data['Expression'] = '';
|
||||
if (isset($data['Extra']) && in_array($data['Extra'], $virtual)) {
|
||||
$data['Virtuality'] = str_replace(' GENERATED', '', $data['Extra']);
|
||||
$expressions = $this->tableObj->getColumnGenerationExpression($column);
|
||||
$data['Expression'] = is_array($expressions) ? $expressions[$column] : null;
|
||||
}
|
||||
|
||||
$changes[] = 'CHANGE ' . Table::generateAlter(
|
||||
$column,
|
||||
$column,
|
||||
mb_strtoupper($extracted_columnspec['type']),
|
||||
$extracted_columnspec['spec_in_brackets'],
|
||||
$extracted_columnspec['attribute'],
|
||||
$data['Collation'] ?? '',
|
||||
$data['Null'] === 'YES' ? 'YES' : 'NO',
|
||||
$default_type,
|
||||
$current_timestamp ? '' : $data['Default'],
|
||||
isset($data['Extra']) && $data['Extra'] !== '' ? $data['Extra']
|
||||
: false,
|
||||
isset($data['COLUMN_COMMENT']) && $data['COLUMN_COMMENT'] !== ''
|
||||
? $data['COLUMN_COMMENT'] : false,
|
||||
$data['Virtuality'],
|
||||
$data['Expression'],
|
||||
$i === 0 ? '-first' : $column_names[$i - 1]
|
||||
);
|
||||
// update current column_names array, first delete old position
|
||||
for ($j = 0, $ll = count($column_names); $j < $ll; $j++) {
|
||||
if ($column_names[$j] != $column) {
|
||||
continue;
|
||||
}
|
||||
|
||||
unset($column_names[$j]);
|
||||
}
|
||||
|
||||
// insert moved column
|
||||
array_splice($column_names, $i, 0, $column);
|
||||
}
|
||||
|
||||
if (empty($changes) && ! isset($_REQUEST['preview_sql'])) { // should never happen
|
||||
$this->response->setRequestStatus(false);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// query for moving the columns
|
||||
$sql_query = sprintf(
|
||||
'ALTER TABLE %s %s',
|
||||
Util::backquote($this->table),
|
||||
implode(', ', $changes)
|
||||
);
|
||||
|
||||
if (isset($_REQUEST['preview_sql'])) { // preview sql
|
||||
$this->response->addJSON(
|
||||
'sql_data',
|
||||
$this->template->render('preview_sql', ['query_data' => $sql_query])
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->dbi->tryQuery($sql_query);
|
||||
$tmp_error = $this->dbi->getError();
|
||||
if ($tmp_error !== '') {
|
||||
$this->response->setRequestStatus(false);
|
||||
$this->response->addJSON('message', Message::error($tmp_error));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$message = Message::success(
|
||||
__('The columns have been moved successfully.')
|
||||
);
|
||||
$this->response->addJSON('message', $message);
|
||||
$this->response->addJSON('columns', $column_names);
|
||||
}
|
||||
}
|
|
@ -1,282 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Controllers\Table\Structure;
|
||||
|
||||
use PhpMyAdmin\Config\PageSettings;
|
||||
use PhpMyAdmin\Controllers\Table\AbstractController;
|
||||
use PhpMyAdmin\Controllers\Table\StructureController;
|
||||
use PhpMyAdmin\CreateAddField;
|
||||
use PhpMyAdmin\DatabaseInterface;
|
||||
use PhpMyAdmin\Html\Generator;
|
||||
use PhpMyAdmin\Message;
|
||||
use PhpMyAdmin\Partitioning\TablePartitionDefinition;
|
||||
use PhpMyAdmin\ResponseRenderer;
|
||||
use PhpMyAdmin\SqlParser\Parser;
|
||||
use PhpMyAdmin\SqlParser\Statements\CreateStatement;
|
||||
use PhpMyAdmin\StorageEngine;
|
||||
use PhpMyAdmin\Table;
|
||||
use PhpMyAdmin\Template;
|
||||
use PhpMyAdmin\Util;
|
||||
|
||||
use function __;
|
||||
use function count;
|
||||
use function strpos;
|
||||
use function strrpos;
|
||||
use function substr;
|
||||
use function trim;
|
||||
|
||||
final class PartitioningController extends AbstractController
|
||||
{
|
||||
/** @var DatabaseInterface */
|
||||
private $dbi;
|
||||
|
||||
/** @var CreateAddField */
|
||||
private $createAddField;
|
||||
|
||||
/** @var StructureController */
|
||||
private $structureController;
|
||||
|
||||
public function __construct(
|
||||
ResponseRenderer $response,
|
||||
Template $template,
|
||||
string $db,
|
||||
string $table,
|
||||
DatabaseInterface $dbi,
|
||||
CreateAddField $createAddField,
|
||||
StructureController $structureController
|
||||
) {
|
||||
parent::__construct($response, $template, $db, $table);
|
||||
$this->dbi = $dbi;
|
||||
$this->createAddField = $createAddField;
|
||||
$this->structureController = $structureController;
|
||||
}
|
||||
|
||||
public function __invoke(): void
|
||||
{
|
||||
if (isset($_POST['save_partitioning'])) {
|
||||
$this->dbi->selectDb($this->db);
|
||||
$this->updatePartitioning();
|
||||
($this->structureController)();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$pageSettings = new PageSettings('TableStructure');
|
||||
$this->response->addHTML($pageSettings->getErrorHTML());
|
||||
$this->response->addHTML($pageSettings->getHTML());
|
||||
|
||||
$this->addScriptFiles(['table/structure.js', 'indexes.js']);
|
||||
|
||||
$partitionDetails = null;
|
||||
if (! isset($_POST['partition_by'])) {
|
||||
$partitionDetails = $this->extractPartitionDetails();
|
||||
}
|
||||
|
||||
$storageEngines = StorageEngine::getArray();
|
||||
|
||||
$partitionDetails = TablePartitionDefinition::getDetails($partitionDetails);
|
||||
$this->render('table/structure/partition_definition_form', [
|
||||
'db' => $this->db,
|
||||
'table' => $this->table,
|
||||
'partition_details' => $partitionDetails,
|
||||
'storage_engines' => $storageEngines,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts partition details from CREATE TABLE statement
|
||||
*
|
||||
* @return array<string, array<int, array<string, mixed>>|bool|int|string>|null array of partition details
|
||||
*/
|
||||
private function extractPartitionDetails(): ?array
|
||||
{
|
||||
$createTable = (new Table($this->table, $this->db))->showCreate();
|
||||
if (! $createTable) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$parser = new Parser($createTable);
|
||||
/**
|
||||
* @var CreateStatement $stmt
|
||||
*/
|
||||
$stmt = $parser->statements[0];
|
||||
|
||||
$partitionDetails = [];
|
||||
|
||||
$partitionDetails['partition_by'] = '';
|
||||
$partitionDetails['partition_expr'] = '';
|
||||
$partitionDetails['partition_count'] = 0;
|
||||
|
||||
if (! empty($stmt->partitionBy)) {
|
||||
$openPos = strpos($stmt->partitionBy, '(');
|
||||
$closePos = strrpos($stmt->partitionBy, ')');
|
||||
|
||||
if ($openPos !== false && $closePos !== false) {
|
||||
$partitionDetails['partition_by'] = trim(substr($stmt->partitionBy, 0, $openPos));
|
||||
$partitionDetails['partition_expr'] = trim(substr(
|
||||
$stmt->partitionBy,
|
||||
$openPos + 1,
|
||||
$closePos - ($openPos + 1)
|
||||
));
|
||||
|
||||
$count = $stmt->partitionsNum ?? count($stmt->partitions);
|
||||
|
||||
$partitionDetails['partition_count'] = $count;
|
||||
}
|
||||
}
|
||||
|
||||
$partitionDetails['subpartition_by'] = '';
|
||||
$partitionDetails['subpartition_expr'] = '';
|
||||
$partitionDetails['subpartition_count'] = 0;
|
||||
|
||||
if (! empty($stmt->subpartitionBy)) {
|
||||
$openPos = strpos($stmt->subpartitionBy, '(');
|
||||
$closePos = strrpos($stmt->subpartitionBy, ')');
|
||||
|
||||
if ($openPos !== false && $closePos !== false) {
|
||||
$partitionDetails['subpartition_by'] = trim(substr($stmt->subpartitionBy, 0, $openPos));
|
||||
$partitionDetails['subpartition_expr'] = trim(substr(
|
||||
$stmt->subpartitionBy,
|
||||
$openPos + 1,
|
||||
$closePos - ($openPos + 1)
|
||||
));
|
||||
|
||||
$count = $stmt->subpartitionsNum ?? count($stmt->partitions[0]->subpartitions);
|
||||
|
||||
$partitionDetails['subpartition_count'] = $count;
|
||||
}
|
||||
}
|
||||
|
||||
// Only LIST and RANGE type parameters allow subpartitioning
|
||||
$partitionDetails['can_have_subpartitions'] = $partitionDetails['partition_count'] > 1
|
||||
&& ($partitionDetails['partition_by'] === 'RANGE'
|
||||
|| $partitionDetails['partition_by'] === 'RANGE COLUMNS'
|
||||
|| $partitionDetails['partition_by'] === 'LIST'
|
||||
|| $partitionDetails['partition_by'] === 'LIST COLUMNS');
|
||||
|
||||
// Values are specified only for LIST and RANGE type partitions
|
||||
$partitionDetails['value_enabled'] = isset($partitionDetails['partition_by'])
|
||||
&& ($partitionDetails['partition_by'] === 'RANGE'
|
||||
|| $partitionDetails['partition_by'] === 'RANGE COLUMNS'
|
||||
|| $partitionDetails['partition_by'] === 'LIST'
|
||||
|| $partitionDetails['partition_by'] === 'LIST COLUMNS');
|
||||
|
||||
$partitionDetails['partitions'] = [];
|
||||
|
||||
for ($i = 0, $iMax = $partitionDetails['partition_count']; $i < $iMax; $i++) {
|
||||
if (! isset($stmt->partitions[$i])) {
|
||||
$partitionDetails['partitions'][$i] = [
|
||||
'name' => 'p' . $i,
|
||||
'value_type' => '',
|
||||
'value' => '',
|
||||
'engine' => '',
|
||||
'comment' => '',
|
||||
'data_directory' => '',
|
||||
'index_directory' => '',
|
||||
'max_rows' => '',
|
||||
'min_rows' => '',
|
||||
'tablespace' => '',
|
||||
'node_group' => '',
|
||||
];
|
||||
} else {
|
||||
$p = $stmt->partitions[$i];
|
||||
$type = $p->type;
|
||||
$expr = trim((string) $p->expr, '()');
|
||||
if ($expr === 'MAXVALUE') {
|
||||
$type .= ' MAXVALUE';
|
||||
$expr = '';
|
||||
}
|
||||
|
||||
$partitionDetails['partitions'][$i] = [
|
||||
'name' => $p->name,
|
||||
'value_type' => $type,
|
||||
'value' => $expr,
|
||||
'engine' => $p->options->has('ENGINE', true),
|
||||
'comment' => trim((string) $p->options->has('COMMENT', true), "'"),
|
||||
'data_directory' => trim((string) $p->options->has('DATA DIRECTORY', true), "'"),
|
||||
'index_directory' => trim((string) $p->options->has('INDEX_DIRECTORY', true), "'"),
|
||||
'max_rows' => $p->options->has('MAX_ROWS', true),
|
||||
'min_rows' => $p->options->has('MIN_ROWS', true),
|
||||
'tablespace' => $p->options->has('TABLESPACE', true),
|
||||
'node_group' => $p->options->has('NODEGROUP', true),
|
||||
];
|
||||
}
|
||||
|
||||
$partition =& $partitionDetails['partitions'][$i];
|
||||
$partition['prefix'] = 'partitions[' . $i . ']';
|
||||
|
||||
if ($partitionDetails['subpartition_count'] <= 1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$partition['subpartition_count'] = $partitionDetails['subpartition_count'];
|
||||
$partition['subpartitions'] = [];
|
||||
|
||||
for ($j = 0, $jMax = $partitionDetails['subpartition_count']; $j < $jMax; $j++) {
|
||||
if (! isset($stmt->partitions[$i]->subpartitions[$j])) {
|
||||
$partition['subpartitions'][$j] = [
|
||||
'name' => $partition['name'] . '_s' . $j,
|
||||
'engine' => '',
|
||||
'comment' => '',
|
||||
'data_directory' => '',
|
||||
'index_directory' => '',
|
||||
'max_rows' => '',
|
||||
'min_rows' => '',
|
||||
'tablespace' => '',
|
||||
'node_group' => '',
|
||||
];
|
||||
} else {
|
||||
$sp = $stmt->partitions[$i]->subpartitions[$j];
|
||||
$partition['subpartitions'][$j] = [
|
||||
'name' => $sp->name,
|
||||
'engine' => $sp->options->has('ENGINE', true),
|
||||
'comment' => trim((string) $sp->options->has('COMMENT', true), "'"),
|
||||
'data_directory' => trim((string) $sp->options->has('DATA DIRECTORY', true), "'"),
|
||||
'index_directory' => trim((string) $sp->options->has('INDEX_DIRECTORY', true), "'"),
|
||||
'max_rows' => $sp->options->has('MAX_ROWS', true),
|
||||
'min_rows' => $sp->options->has('MIN_ROWS', true),
|
||||
'tablespace' => $sp->options->has('TABLESPACE', true),
|
||||
'node_group' => $sp->options->has('NODEGROUP', true),
|
||||
];
|
||||
}
|
||||
|
||||
$subpartition =& $partition['subpartitions'][$j];
|
||||
$subpartition['prefix'] = 'partitions[' . $i . ']'
|
||||
. '[subpartitions][' . $j . ']';
|
||||
}
|
||||
}
|
||||
|
||||
return $partitionDetails;
|
||||
}
|
||||
|
||||
private function updatePartitioning(): void
|
||||
{
|
||||
$sql_query = 'ALTER TABLE ' . Util::backquote($this->table) . ' '
|
||||
. $this->createAddField->getPartitionsDefinition();
|
||||
|
||||
// Execute alter query
|
||||
$result = $this->dbi->tryQuery($sql_query);
|
||||
|
||||
if ($result === false) {
|
||||
$this->response->setRequestStatus(false);
|
||||
$this->response->addJSON(
|
||||
'message',
|
||||
Message::rawError(
|
||||
__('Query error') . ':<br>' . $this->dbi->getError()
|
||||
)
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$message = Message::success(
|
||||
__('Table %1$s has been altered successfully.')
|
||||
);
|
||||
$message->addParam($this->table);
|
||||
$this->response->addHTML(
|
||||
Generator::getMessage($message, $sql_query, 'success')
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,135 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Controllers\Table\Structure;
|
||||
|
||||
use PhpMyAdmin\Controllers\Table\AbstractController;
|
||||
use PhpMyAdmin\Controllers\Table\StructureController;
|
||||
use PhpMyAdmin\DatabaseInterface;
|
||||
use PhpMyAdmin\DbTableExists;
|
||||
use PhpMyAdmin\Message;
|
||||
use PhpMyAdmin\ResponseRenderer;
|
||||
use PhpMyAdmin\Template;
|
||||
use PhpMyAdmin\Url;
|
||||
use PhpMyAdmin\Util;
|
||||
|
||||
use function __;
|
||||
use function count;
|
||||
|
||||
final class PrimaryController extends AbstractController
|
||||
{
|
||||
/** @var DatabaseInterface */
|
||||
private $dbi;
|
||||
|
||||
/** @var StructureController */
|
||||
private $structureController;
|
||||
|
||||
public function __construct(
|
||||
ResponseRenderer $response,
|
||||
Template $template,
|
||||
string $db,
|
||||
string $table,
|
||||
DatabaseInterface $dbi,
|
||||
StructureController $structureController
|
||||
) {
|
||||
parent::__construct($response, $template, $db, $table);
|
||||
$this->dbi = $dbi;
|
||||
$this->structureController = $structureController;
|
||||
}
|
||||
|
||||
public function __invoke(): void
|
||||
{
|
||||
global $db, $table, $message, $sql_query, $urlParams, $errorUrl, $cfg;
|
||||
|
||||
$selected = $_POST['selected'] ?? [];
|
||||
$selected_fld = $_POST['selected_fld'] ?? [];
|
||||
|
||||
if (empty($selected) && empty($selected_fld)) {
|
||||
$this->response->setRequestStatus(false);
|
||||
$this->response->addJSON('message', __('No column selected.'));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$primary = $this->getKeyForTablePrimary();
|
||||
if (empty($primary) && ! empty($selected_fld)) {
|
||||
// no primary key, so we can safely create new
|
||||
$mult_btn = __('Yes');
|
||||
$selected = $selected_fld;
|
||||
}
|
||||
|
||||
$mult_btn = $_POST['mult_btn'] ?? $mult_btn ?? '';
|
||||
|
||||
if (! empty($selected_fld) && ! empty($primary)) {
|
||||
Util::checkParameters(['db', 'table']);
|
||||
|
||||
$urlParams = ['db' => $db, 'table' => $table];
|
||||
$errorUrl = Util::getScriptNameForOption($cfg['DefaultTabTable'], 'table');
|
||||
$errorUrl .= Url::getCommon($urlParams, '&');
|
||||
|
||||
DbTableExists::check();
|
||||
|
||||
$this->render('table/structure/primary', [
|
||||
'db' => $db,
|
||||
'table' => $table,
|
||||
'selected' => $selected_fld,
|
||||
]);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ($mult_btn === __('Yes')) {
|
||||
$sql_query = 'ALTER TABLE ' . Util::backquote($table);
|
||||
if (! empty($primary)) {
|
||||
$sql_query .= ' DROP PRIMARY KEY,';
|
||||
}
|
||||
|
||||
$sql_query .= ' ADD PRIMARY KEY(';
|
||||
|
||||
$i = 1;
|
||||
$selectedCount = count($selected);
|
||||
foreach ($selected as $field) {
|
||||
$sql_query .= Util::backquote($field);
|
||||
$sql_query .= $i++ === $selectedCount ? ');' : ', ';
|
||||
}
|
||||
|
||||
$this->dbi->selectDb($db);
|
||||
$result = $this->dbi->tryQuery($sql_query);
|
||||
|
||||
if (! $result) {
|
||||
$message = Message::error($this->dbi->getError());
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($message)) {
|
||||
$message = Message::success();
|
||||
}
|
||||
|
||||
($this->structureController)();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets table primary key
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function getKeyForTablePrimary()
|
||||
{
|
||||
$this->dbi->selectDb($this->db);
|
||||
$result = $this->dbi->query(
|
||||
'SHOW KEYS FROM ' . Util::backquote($this->table) . ';'
|
||||
);
|
||||
$primary = '';
|
||||
foreach ($result as $row) {
|
||||
// Backups the list of primary keys
|
||||
if ($row['Key_name'] !== 'PRIMARY') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$primary .= $row['Column_name'] . ', ';
|
||||
}
|
||||
|
||||
return $primary;
|
||||
}
|
||||
}
|
|
@ -1,56 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Controllers\Table\Structure;
|
||||
|
||||
use PhpMyAdmin\Controllers\Table\AbstractController;
|
||||
use PhpMyAdmin\SqlParser\Context;
|
||||
|
||||
use function _ngettext;
|
||||
use function count;
|
||||
use function implode;
|
||||
use function sprintf;
|
||||
use function trim;
|
||||
|
||||
final class ReservedWordCheckController extends AbstractController
|
||||
{
|
||||
public function __invoke(): void
|
||||
{
|
||||
if ($GLOBALS['cfg']['ReservedWordDisableWarning'] !== false) {
|
||||
$this->response->setRequestStatus(false);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$columns_names = $_POST['field_name'];
|
||||
$reserved_keywords_names = [];
|
||||
foreach ($columns_names as $column) {
|
||||
if (! Context::isKeyword(trim($column), true)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$reserved_keywords_names[] = trim($column);
|
||||
}
|
||||
|
||||
if (Context::isKeyword(trim($this->table), true)) {
|
||||
$reserved_keywords_names[] = trim($this->table);
|
||||
}
|
||||
|
||||
if (count($reserved_keywords_names) === 0) {
|
||||
$this->response->setRequestStatus(false);
|
||||
}
|
||||
|
||||
$this->response->addJSON(
|
||||
'message',
|
||||
sprintf(
|
||||
_ngettext(
|
||||
'The name \'%s\' is a MySQL reserved keyword.',
|
||||
'The names \'%s\' are MySQL reserved keywords.',
|
||||
count($reserved_keywords_names)
|
||||
),
|
||||
implode(',', $reserved_keywords_names)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,402 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Controllers\Table\Structure;
|
||||
|
||||
use PhpMyAdmin\ConfigStorage\Relation;
|
||||
use PhpMyAdmin\Controllers\Table\AbstractController;
|
||||
use PhpMyAdmin\Controllers\Table\StructureController;
|
||||
use PhpMyAdmin\Core;
|
||||
use PhpMyAdmin\DatabaseInterface;
|
||||
use PhpMyAdmin\Html\Generator;
|
||||
use PhpMyAdmin\Index;
|
||||
use PhpMyAdmin\Message;
|
||||
use PhpMyAdmin\ResponseRenderer;
|
||||
use PhpMyAdmin\Table;
|
||||
use PhpMyAdmin\Template;
|
||||
use PhpMyAdmin\Transformations;
|
||||
use PhpMyAdmin\Url;
|
||||
use PhpMyAdmin\Util;
|
||||
|
||||
use function __;
|
||||
use function count;
|
||||
use function implode;
|
||||
use function in_array;
|
||||
use function is_array;
|
||||
use function mb_strpos;
|
||||
use function sprintf;
|
||||
use function strlen;
|
||||
|
||||
final class SaveController extends AbstractController
|
||||
{
|
||||
/** @var Table The table object */
|
||||
private $tableObj;
|
||||
|
||||
/** @var Relation */
|
||||
private $relation;
|
||||
|
||||
/** @var Transformations */
|
||||
private $transformations;
|
||||
|
||||
/** @var DatabaseInterface */
|
||||
private $dbi;
|
||||
|
||||
/** @var StructureController */
|
||||
private $structureController;
|
||||
|
||||
public function __construct(
|
||||
ResponseRenderer $response,
|
||||
Template $template,
|
||||
string $db,
|
||||
string $table,
|
||||
Relation $relation,
|
||||
Transformations $transformations,
|
||||
DatabaseInterface $dbi,
|
||||
StructureController $structureController
|
||||
) {
|
||||
parent::__construct($response, $template, $db, $table);
|
||||
$this->relation = $relation;
|
||||
$this->transformations = $transformations;
|
||||
$this->dbi = $dbi;
|
||||
$this->structureController = $structureController;
|
||||
|
||||
$this->tableObj = $this->dbi->getTable($this->db, $this->table);
|
||||
}
|
||||
|
||||
public function __invoke(): void
|
||||
{
|
||||
$regenerate = $this->updateColumns();
|
||||
if (! $regenerate) {
|
||||
// continue to show the table's structure
|
||||
unset($_POST['selected']);
|
||||
}
|
||||
|
||||
($this->structureController)();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the table's structure based on $_REQUEST
|
||||
*
|
||||
* @return bool true if error occurred
|
||||
*/
|
||||
private function updateColumns(): bool
|
||||
{
|
||||
$err_url = Url::getFromRoute('/table/structure', [
|
||||
'db' => $this->db,
|
||||
'table' => $this->table,
|
||||
]);
|
||||
$regenerate = false;
|
||||
$field_cnt = count($_POST['field_name'] ?? []);
|
||||
$changes = [];
|
||||
$adjust_privileges = [];
|
||||
$columns_with_index = $this->dbi
|
||||
->getTable($this->db, $this->table)
|
||||
->getColumnsWithIndex(Index::PRIMARY | Index::UNIQUE);
|
||||
for ($i = 0; $i < $field_cnt; $i++) {
|
||||
if (! $this->columnNeedsAlterTable($i)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$changes[] = 'CHANGE ' . Table::generateAlter(
|
||||
Util::getValueByKey($_POST, 'field_orig.' . $i, ''),
|
||||
$_POST['field_name'][$i],
|
||||
$_POST['field_type'][$i],
|
||||
$_POST['field_length'][$i],
|
||||
$_POST['field_attribute'][$i],
|
||||
Util::getValueByKey($_POST, 'field_collation.' . $i, ''),
|
||||
Util::getValueByKey($_POST, 'field_null.' . $i, 'NO'),
|
||||
$_POST['field_default_type'][$i],
|
||||
$_POST['field_default_value'][$i],
|
||||
Util::getValueByKey($_POST, 'field_extra.' . $i, false),
|
||||
Util::getValueByKey($_POST, 'field_comments.' . $i, ''),
|
||||
Util::getValueByKey($_POST, 'field_virtuality.' . $i, ''),
|
||||
Util::getValueByKey($_POST, 'field_expression.' . $i, ''),
|
||||
Util::getValueByKey($_POST, 'field_move_to.' . $i, ''),
|
||||
$columns_with_index
|
||||
);
|
||||
|
||||
// find the remembered sort expression
|
||||
$sorted_col = $this->tableObj->getUiProp(Table::PROP_SORTED_COLUMN);
|
||||
// if the old column name is part of the remembered sort expression
|
||||
if (mb_strpos((string) $sorted_col, Util::backquote($_POST['field_orig'][$i])) !== false) {
|
||||
// delete the whole remembered sort expression
|
||||
$this->tableObj->removeUiProp(Table::PROP_SORTED_COLUMN);
|
||||
}
|
||||
|
||||
if (
|
||||
! isset($_POST['field_adjust_privileges'][$i])
|
||||
|| empty($_POST['field_adjust_privileges'][$i])
|
||||
|| $_POST['field_orig'][$i] == $_POST['field_name'][$i]
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$adjust_privileges[$_POST['field_orig'][$i]] = $_POST['field_name'][$i];
|
||||
}
|
||||
|
||||
if (count($changes) > 0 || isset($_POST['preview_sql'])) {
|
||||
// Builds the primary keys statements and updates the table
|
||||
$key_query = '';
|
||||
/**
|
||||
* this is a little bit more complex
|
||||
*
|
||||
* @todo if someone selects A_I when altering a column we need to check:
|
||||
* - no other column with A_I
|
||||
* - the column has an index, if not create one
|
||||
*/
|
||||
|
||||
// To allow replication, we first select the db to use
|
||||
// and then run queries on this db.
|
||||
if (! $this->dbi->selectDb($this->db)) {
|
||||
Generator::mysqlDie(
|
||||
$this->dbi->getError(),
|
||||
'USE ' . Util::backquote($this->db) . ';',
|
||||
false,
|
||||
$err_url
|
||||
);
|
||||
}
|
||||
|
||||
$sql_query = 'ALTER TABLE ' . Util::backquote($this->table) . ' ';
|
||||
$sql_query .= implode(', ', $changes) . $key_query;
|
||||
if (isset($_POST['online_transaction'])) {
|
||||
$sql_query .= ', ALGORITHM=INPLACE, LOCK=NONE';
|
||||
}
|
||||
|
||||
$sql_query .= ';';
|
||||
|
||||
// If there is a request for SQL previewing.
|
||||
if (isset($_POST['preview_sql'])) {
|
||||
Core::previewSQL(count($changes) > 0 ? $sql_query : '');
|
||||
|
||||
exit;
|
||||
}
|
||||
|
||||
$columns_with_index = $this->dbi
|
||||
->getTable($this->db, $this->table)
|
||||
->getColumnsWithIndex(Index::PRIMARY | Index::UNIQUE | Index::INDEX | Index::SPATIAL | Index::FULLTEXT);
|
||||
|
||||
$changedToBlob = [];
|
||||
// While changing the Column Collation
|
||||
// First change to BLOB
|
||||
for ($i = 0; $i < $field_cnt; $i++) {
|
||||
if (
|
||||
isset($_POST['field_collation'][$i], $_POST['field_collation_orig'][$i])
|
||||
&& $_POST['field_collation'][$i] !== $_POST['field_collation_orig'][$i]
|
||||
&& ! in_array($_POST['field_orig'][$i], $columns_with_index)
|
||||
) {
|
||||
$secondary_query = 'ALTER TABLE ' . Util::backquote($this->table)
|
||||
. ' CHANGE ' . Util::backquote($_POST['field_orig'][$i])
|
||||
. ' ' . Util::backquote($_POST['field_orig'][$i])
|
||||
. ' BLOB';
|
||||
|
||||
if (isset($_POST['field_virtuality'][$i], $_POST['field_expression'][$i])) {
|
||||
if ($_POST['field_virtuality'][$i]) {
|
||||
$secondary_query .= ' AS (' . $_POST['field_expression'][$i] . ') '
|
||||
. $_POST['field_virtuality'][$i];
|
||||
}
|
||||
}
|
||||
|
||||
$secondary_query .= ';';
|
||||
|
||||
$this->dbi->query($secondary_query);
|
||||
$changedToBlob[$i] = true;
|
||||
} else {
|
||||
$changedToBlob[$i] = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Then make the requested changes
|
||||
$result = $this->dbi->tryQuery($sql_query);
|
||||
|
||||
if ($result !== false) {
|
||||
$changed_privileges = $this->adjustColumnPrivileges($adjust_privileges);
|
||||
|
||||
if ($changed_privileges) {
|
||||
$message = Message::success(
|
||||
__(
|
||||
'Table %1$s has been altered successfully. Privileges have been adjusted.'
|
||||
)
|
||||
);
|
||||
} else {
|
||||
$message = Message::success(
|
||||
__('Table %1$s has been altered successfully.')
|
||||
);
|
||||
}
|
||||
|
||||
$message->addParam($this->table);
|
||||
|
||||
$this->response->addHTML(
|
||||
Generator::getMessage($message, $sql_query, 'success')
|
||||
);
|
||||
} else {
|
||||
// An error happened while inserting/updating a table definition
|
||||
|
||||
// Save the Original Error
|
||||
$orig_error = $this->dbi->getError();
|
||||
$changes_revert = [];
|
||||
|
||||
// Change back to Original Collation and data type
|
||||
for ($i = 0; $i < $field_cnt; $i++) {
|
||||
if (! $changedToBlob[$i]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$changes_revert[] = 'CHANGE ' . Table::generateAlter(
|
||||
Util::getValueByKey($_POST, 'field_orig.' . $i, ''),
|
||||
$_POST['field_name'][$i],
|
||||
$_POST['field_type_orig'][$i],
|
||||
$_POST['field_length_orig'][$i],
|
||||
$_POST['field_attribute_orig'][$i],
|
||||
Util::getValueByKey($_POST, 'field_collation_orig.' . $i, ''),
|
||||
Util::getValueByKey($_POST, 'field_null_orig.' . $i, 'NO'),
|
||||
$_POST['field_default_type_orig'][$i],
|
||||
$_POST['field_default_value_orig'][$i],
|
||||
Util::getValueByKey($_POST, 'field_extra_orig.' . $i, false),
|
||||
Util::getValueByKey($_POST, 'field_comments_orig.' . $i, ''),
|
||||
Util::getValueByKey($_POST, 'field_virtuality_orig.' . $i, ''),
|
||||
Util::getValueByKey($_POST, 'field_expression_orig.' . $i, ''),
|
||||
Util::getValueByKey($_POST, 'field_move_to_orig.' . $i, '')
|
||||
);
|
||||
}
|
||||
|
||||
$revert_query = 'ALTER TABLE ' . Util::backquote($this->table)
|
||||
. ' ';
|
||||
$revert_query .= implode(', ', $changes_revert) . '';
|
||||
$revert_query .= ';';
|
||||
|
||||
// Column reverted back to original
|
||||
$this->dbi->query($revert_query);
|
||||
|
||||
$this->response->setRequestStatus(false);
|
||||
$message = Message::rawError(
|
||||
__('Query error') . ':<br>' . $orig_error
|
||||
);
|
||||
$this->response->addHTML(
|
||||
Generator::getMessage($message, $sql_query, 'error')
|
||||
);
|
||||
$regenerate = true;
|
||||
}
|
||||
}
|
||||
|
||||
// update field names in relation
|
||||
if (isset($_POST['field_orig']) && is_array($_POST['field_orig'])) {
|
||||
foreach ($_POST['field_orig'] as $fieldindex => $fieldcontent) {
|
||||
if ($_POST['field_name'][$fieldindex] == $fieldcontent) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->relation->renameField($this->db, $this->table, $fieldcontent, $_POST['field_name'][$fieldindex]);
|
||||
}
|
||||
}
|
||||
|
||||
// update mime types
|
||||
if (isset($_POST['field_mimetype']) && is_array($_POST['field_mimetype']) && $GLOBALS['cfg']['BrowseMIME']) {
|
||||
foreach ($_POST['field_mimetype'] as $fieldindex => $mimetype) {
|
||||
if (! isset($_POST['field_name'][$fieldindex]) || strlen($_POST['field_name'][$fieldindex]) <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->transformations->setMime(
|
||||
$this->db,
|
||||
$this->table,
|
||||
$_POST['field_name'][$fieldindex],
|
||||
$mimetype,
|
||||
$_POST['field_transformation'][$fieldindex],
|
||||
$_POST['field_transformation_options'][$fieldindex],
|
||||
$_POST['field_input_transformation'][$fieldindex],
|
||||
$_POST['field_input_transformation_options'][$fieldindex]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $regenerate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies if some elements of a column have changed
|
||||
*
|
||||
* @param int $i column index in the request
|
||||
*/
|
||||
private function columnNeedsAlterTable($i): bool
|
||||
{
|
||||
// these two fields are checkboxes so might not be part of the
|
||||
// request; therefore we define them to avoid notices below
|
||||
if (! isset($_POST['field_null'][$i])) {
|
||||
$_POST['field_null'][$i] = 'NO';
|
||||
}
|
||||
|
||||
if (! isset($_POST['field_extra'][$i])) {
|
||||
$_POST['field_extra'][$i] = '';
|
||||
}
|
||||
|
||||
// field_name does not follow the convention (corresponds to field_orig)
|
||||
if ($_POST['field_name'][$i] != $_POST['field_orig'][$i]) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$fields = [
|
||||
'field_attribute',
|
||||
'field_collation',
|
||||
'field_comments',
|
||||
'field_default_value',
|
||||
'field_default_type',
|
||||
'field_extra',
|
||||
'field_length',
|
||||
'field_null',
|
||||
'field_type',
|
||||
];
|
||||
foreach ($fields as $field) {
|
||||
if ($_POST[$field][$i] != $_POST[$field . '_orig'][$i]) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return ! empty($_POST['field_move_to'][$i]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adjusts the Privileges for all the columns whose names have changed
|
||||
*
|
||||
* @param array $adjust_privileges assoc array of old col names mapped to new
|
||||
* cols
|
||||
*/
|
||||
private function adjustColumnPrivileges(array $adjust_privileges): bool
|
||||
{
|
||||
$changed = false;
|
||||
|
||||
if (
|
||||
Util::getValueByKey($GLOBALS, 'col_priv', false)
|
||||
&& Util::getValueByKey($GLOBALS, 'is_reload_priv', false)
|
||||
) {
|
||||
$this->dbi->selectDb('mysql');
|
||||
|
||||
// For Column specific privileges
|
||||
foreach ($adjust_privileges as $oldCol => $newCol) {
|
||||
$this->dbi->query(
|
||||
sprintf(
|
||||
'UPDATE %s SET Column_name = "%s"
|
||||
WHERE Db = "%s"
|
||||
AND Table_name = "%s"
|
||||
AND Column_name = "%s";',
|
||||
Util::backquote('columns_priv'),
|
||||
$newCol,
|
||||
$this->db,
|
||||
$this->table,
|
||||
$oldCol
|
||||
)
|
||||
);
|
||||
|
||||
// i.e. if atleast one column privileges adjusted
|
||||
$changed = true;
|
||||
}
|
||||
|
||||
if ($changed) {
|
||||
// Finally FLUSH the new privileges
|
||||
$this->dbi->query('FLUSH PRIVILEGES;');
|
||||
}
|
||||
}
|
||||
|
||||
return $changed;
|
||||
}
|
||||
}
|
|
@ -1,74 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Controllers\Table\Structure;
|
||||
|
||||
use PhpMyAdmin\Controllers\Table\AbstractController;
|
||||
use PhpMyAdmin\Controllers\Table\StructureController;
|
||||
use PhpMyAdmin\DatabaseInterface;
|
||||
use PhpMyAdmin\Message;
|
||||
use PhpMyAdmin\ResponseRenderer;
|
||||
use PhpMyAdmin\Template;
|
||||
use PhpMyAdmin\Util;
|
||||
|
||||
use function __;
|
||||
use function count;
|
||||
|
||||
final class SpatialController extends AbstractController
|
||||
{
|
||||
/** @var DatabaseInterface */
|
||||
private $dbi;
|
||||
|
||||
/** @var StructureController */
|
||||
private $structureController;
|
||||
|
||||
public function __construct(
|
||||
ResponseRenderer $response,
|
||||
Template $template,
|
||||
string $db,
|
||||
string $table,
|
||||
DatabaseInterface $dbi,
|
||||
StructureController $structureController
|
||||
) {
|
||||
parent::__construct($response, $template, $db, $table);
|
||||
$this->dbi = $dbi;
|
||||
$this->structureController = $structureController;
|
||||
}
|
||||
|
||||
public function __invoke(): void
|
||||
{
|
||||
global $sql_query, $db, $table, $message;
|
||||
|
||||
$selected = $_POST['selected_fld'] ?? [];
|
||||
|
||||
if (empty($selected)) {
|
||||
$this->response->setRequestStatus(false);
|
||||
$this->response->addJSON('message', __('No column selected.'));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$i = 1;
|
||||
$selectedCount = count($selected);
|
||||
$sql_query = 'ALTER TABLE ' . Util::backquote($table) . ' ADD SPATIAL(';
|
||||
|
||||
foreach ($selected as $field) {
|
||||
$sql_query .= Util::backquote($field);
|
||||
$sql_query .= $i++ === $selectedCount ? ');' : ', ';
|
||||
}
|
||||
|
||||
$this->dbi->selectDb($db);
|
||||
$result = $this->dbi->tryQuery($sql_query);
|
||||
|
||||
if (! $result) {
|
||||
$message = Message::error($this->dbi->getError());
|
||||
}
|
||||
|
||||
if (empty($message)) {
|
||||
$message = Message::success();
|
||||
}
|
||||
|
||||
($this->structureController)();
|
||||
}
|
||||
}
|
|
@ -1,74 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Controllers\Table\Structure;
|
||||
|
||||
use PhpMyAdmin\Controllers\Table\AbstractController;
|
||||
use PhpMyAdmin\Controllers\Table\StructureController;
|
||||
use PhpMyAdmin\DatabaseInterface;
|
||||
use PhpMyAdmin\Message;
|
||||
use PhpMyAdmin\ResponseRenderer;
|
||||
use PhpMyAdmin\Template;
|
||||
use PhpMyAdmin\Util;
|
||||
|
||||
use function __;
|
||||
use function count;
|
||||
|
||||
final class UniqueController extends AbstractController
|
||||
{
|
||||
/** @var DatabaseInterface */
|
||||
private $dbi;
|
||||
|
||||
/** @var StructureController */
|
||||
private $structureController;
|
||||
|
||||
public function __construct(
|
||||
ResponseRenderer $response,
|
||||
Template $template,
|
||||
string $db,
|
||||
string $table,
|
||||
DatabaseInterface $dbi,
|
||||
StructureController $structureController
|
||||
) {
|
||||
parent::__construct($response, $template, $db, $table);
|
||||
$this->dbi = $dbi;
|
||||
$this->structureController = $structureController;
|
||||
}
|
||||
|
||||
public function __invoke(): void
|
||||
{
|
||||
global $sql_query, $db, $table, $message;
|
||||
|
||||
$selected = $_POST['selected_fld'] ?? [];
|
||||
|
||||
if (empty($selected)) {
|
||||
$this->response->setRequestStatus(false);
|
||||
$this->response->addJSON('message', __('No column selected.'));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$i = 1;
|
||||
$selectedCount = count($selected);
|
||||
$sql_query = 'ALTER TABLE ' . Util::backquote($table) . ' ADD UNIQUE(';
|
||||
|
||||
foreach ($selected as $field) {
|
||||
$sql_query .= Util::backquote($field);
|
||||
$sql_query .= $i++ === $selectedCount ? ');' : ', ';
|
||||
}
|
||||
|
||||
$this->dbi->selectDb($db);
|
||||
$result = $this->dbi->tryQuery($sql_query);
|
||||
|
||||
if (! $result) {
|
||||
$message = Message::error($this->dbi->getError());
|
||||
}
|
||||
|
||||
if (empty($message)) {
|
||||
$message = Message::success();
|
||||
}
|
||||
|
||||
($this->structureController)();
|
||||
}
|
||||
}
|
|
@ -1,404 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Controllers\Table;
|
||||
|
||||
use PhpMyAdmin\Charsets;
|
||||
use PhpMyAdmin\CheckUserPrivileges;
|
||||
use PhpMyAdmin\Config\PageSettings;
|
||||
use PhpMyAdmin\ConfigStorage\Relation;
|
||||
use PhpMyAdmin\ConfigStorage\RelationCleanup;
|
||||
use PhpMyAdmin\ConfigStorage\RelationParameters;
|
||||
use PhpMyAdmin\CreateAddField;
|
||||
use PhpMyAdmin\Database\CentralColumns;
|
||||
use PhpMyAdmin\DatabaseInterface;
|
||||
use PhpMyAdmin\DbTableExists;
|
||||
use PhpMyAdmin\Engines\Innodb;
|
||||
use PhpMyAdmin\FlashMessages;
|
||||
use PhpMyAdmin\Html\Generator;
|
||||
use PhpMyAdmin\Index;
|
||||
use PhpMyAdmin\Partitioning\Partition;
|
||||
use PhpMyAdmin\Query\Utilities;
|
||||
use PhpMyAdmin\ResponseRenderer;
|
||||
use PhpMyAdmin\StorageEngine;
|
||||
use PhpMyAdmin\Table;
|
||||
use PhpMyAdmin\Template;
|
||||
use PhpMyAdmin\Tracker;
|
||||
use PhpMyAdmin\Transformations;
|
||||
use PhpMyAdmin\Url;
|
||||
use PhpMyAdmin\Util;
|
||||
use PhpMyAdmin\Utils\ForeignKey;
|
||||
use stdClass;
|
||||
|
||||
use function __;
|
||||
use function in_array;
|
||||
use function is_string;
|
||||
use function str_contains;
|
||||
|
||||
/**
|
||||
* Displays table structure infos like columns, indexes, size, rows
|
||||
* and allows manipulation of indexes and columns.
|
||||
*/
|
||||
class StructureController extends AbstractController
|
||||
{
|
||||
/** @var Table The table object */
|
||||
protected $tableObj;
|
||||
|
||||
/** @var CreateAddField */
|
||||
private $createAddField;
|
||||
|
||||
/** @var Relation */
|
||||
private $relation;
|
||||
|
||||
/** @var Transformations */
|
||||
private $transformations;
|
||||
|
||||
/** @var RelationCleanup */
|
||||
private $relationCleanup;
|
||||
|
||||
/** @var DatabaseInterface */
|
||||
private $dbi;
|
||||
|
||||
/** @var FlashMessages */
|
||||
private $flash;
|
||||
|
||||
public function __construct(
|
||||
ResponseRenderer $response,
|
||||
Template $template,
|
||||
string $db,
|
||||
string $table,
|
||||
Relation $relation,
|
||||
Transformations $transformations,
|
||||
CreateAddField $createAddField,
|
||||
RelationCleanup $relationCleanup,
|
||||
DatabaseInterface $dbi,
|
||||
FlashMessages $flash
|
||||
) {
|
||||
parent::__construct($response, $template, $db, $table);
|
||||
$this->createAddField = $createAddField;
|
||||
$this->relation = $relation;
|
||||
$this->transformations = $transformations;
|
||||
$this->relationCleanup = $relationCleanup;
|
||||
$this->dbi = $dbi;
|
||||
$this->flash = $flash;
|
||||
|
||||
$this->tableObj = $this->dbi->getTable($this->db, $this->table);
|
||||
}
|
||||
|
||||
public function __invoke(): void
|
||||
{
|
||||
global $reread_info, $showtable, $db, $table, $cfg, $errorUrl;
|
||||
global $tbl_is_view, $tbl_storage_engine, $tbl_collation, $table_info_num_rows;
|
||||
|
||||
$this->dbi->selectDb($this->db);
|
||||
$reread_info = $this->tableObj->getStatusInfo(null, true);
|
||||
$showtable = $this->tableObj->getStatusInfo(null, (isset($reread_info) && $reread_info));
|
||||
|
||||
if ($this->tableObj->isView()) {
|
||||
$tbl_is_view = true;
|
||||
$tbl_storage_engine = __('View');
|
||||
} else {
|
||||
$tbl_is_view = false;
|
||||
$tbl_storage_engine = $this->tableObj->getStorageEngine();
|
||||
}
|
||||
|
||||
$tbl_collation = $this->tableObj->getCollation();
|
||||
$table_info_num_rows = $this->tableObj->getNumRows();
|
||||
|
||||
$pageSettings = new PageSettings('TableStructure');
|
||||
$this->response->addHTML($pageSettings->getErrorHTML());
|
||||
$this->response->addHTML($pageSettings->getHTML());
|
||||
|
||||
$checkUserPrivileges = new CheckUserPrivileges($this->dbi);
|
||||
$checkUserPrivileges->getPrivileges();
|
||||
|
||||
$this->addScriptFiles(['table/structure.js', 'indexes.js']);
|
||||
|
||||
$relationParameters = $this->relation->getRelationParameters();
|
||||
|
||||
Util::checkParameters(['db', 'table']);
|
||||
|
||||
$isSystemSchema = Utilities::isSystemSchema($db);
|
||||
$url_params = ['db' => $db, 'table' => $table];
|
||||
$errorUrl = Util::getScriptNameForOption($cfg['DefaultTabTable'], 'table');
|
||||
$errorUrl .= Url::getCommon($url_params, '&');
|
||||
|
||||
DbTableExists::check();
|
||||
|
||||
$primary = Index::getPrimary($this->table, $this->db);
|
||||
$columns_with_index = $this->dbi
|
||||
->getTable($this->db, $this->table)
|
||||
->getColumnsWithIndex(Index::UNIQUE | Index::INDEX | Index::SPATIAL | Index::FULLTEXT);
|
||||
$columns_with_unique_index = $this->dbi
|
||||
->getTable($this->db, $this->table)
|
||||
->getColumnsWithIndex(Index::UNIQUE);
|
||||
|
||||
$fields = $this->dbi->getColumns($this->db, $this->table, true);
|
||||
|
||||
$this->response->addHTML($this->displayStructure(
|
||||
$relationParameters,
|
||||
$columns_with_unique_index,
|
||||
$primary,
|
||||
$fields,
|
||||
$columns_with_index,
|
||||
$isSystemSchema
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays the table structure ('show table' works correct since 3.23.03)
|
||||
*
|
||||
* @param array $columns_with_unique_index Columns with unique index
|
||||
* @param Index|false $primary_index primary index or false if no one exists
|
||||
* @param array $fields Fields
|
||||
* @param array $columns_with_index Columns with index
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function displayStructure(
|
||||
RelationParameters $relationParameters,
|
||||
array $columns_with_unique_index,
|
||||
$primary_index,
|
||||
array $fields,
|
||||
array $columns_with_index,
|
||||
bool $isSystemSchema
|
||||
) {
|
||||
global $route, $tbl_is_view, $tbl_storage_engine;
|
||||
|
||||
// prepare comments
|
||||
$comments_map = [];
|
||||
$mime_map = [];
|
||||
|
||||
if ($GLOBALS['cfg']['ShowPropertyComments']) {
|
||||
$comments_map = $this->relation->getComments($this->db, $this->table);
|
||||
if ($relationParameters->browserTransformationFeature !== null && $GLOBALS['cfg']['BrowseMIME']) {
|
||||
$mime_map = $this->transformations->getMime($this->db, $this->table, true);
|
||||
}
|
||||
}
|
||||
|
||||
$centralColumns = new CentralColumns($this->dbi);
|
||||
$central_list = $centralColumns->getFromTable($this->db, $this->table);
|
||||
|
||||
/**
|
||||
* Displays Space usage and row statistics
|
||||
*/
|
||||
// BEGIN - Calc Table Space
|
||||
// Get valid statistics whatever is the table type
|
||||
if ($GLOBALS['cfg']['ShowStats']) {
|
||||
//get table stats in HTML format
|
||||
$tablestats = $this->getTableStats($isSystemSchema);
|
||||
//returning the response in JSON format to be used by Ajax
|
||||
$this->response->addJSON('tableStat', $tablestats);
|
||||
}
|
||||
|
||||
// END - Calc Table Space
|
||||
|
||||
// logic removed from Template
|
||||
$rownum = 0;
|
||||
$columns_list = [];
|
||||
$attributes = [];
|
||||
$displayed_fields = [];
|
||||
$row_comments = [];
|
||||
$extracted_columnspecs = [];
|
||||
$collations = [];
|
||||
foreach ($fields as &$field) {
|
||||
++$rownum;
|
||||
$columns_list[] = $field['Field'];
|
||||
|
||||
$extracted_columnspecs[$rownum] = Util::extractColumnSpec($field['Type']);
|
||||
$attributes[$rownum] = $extracted_columnspecs[$rownum]['attribute'];
|
||||
if (str_contains($field['Extra'], 'on update CURRENT_TIMESTAMP')) {
|
||||
$attributes[$rownum] = 'on update CURRENT_TIMESTAMP';
|
||||
}
|
||||
|
||||
$displayed_fields[$rownum] = new stdClass();
|
||||
$displayed_fields[$rownum]->text = $field['Field'];
|
||||
$displayed_fields[$rownum]->icon = '';
|
||||
$row_comments[$rownum] = '';
|
||||
|
||||
if (isset($comments_map[$field['Field']])) {
|
||||
$displayed_fields[$rownum]->comment = $comments_map[$field['Field']];
|
||||
$row_comments[$rownum] = $comments_map[$field['Field']];
|
||||
}
|
||||
|
||||
if ($primary_index && $primary_index->hasColumn($field['Field'])) {
|
||||
$displayed_fields[$rownum]->icon .= Generator::getImage('b_primary', __('Primary'));
|
||||
}
|
||||
|
||||
if (in_array($field['Field'], $columns_with_index)) {
|
||||
$displayed_fields[$rownum]->icon .= Generator::getImage('bd_primary', __('Index'));
|
||||
}
|
||||
|
||||
$collation = Charsets::findCollationByName(
|
||||
$this->dbi,
|
||||
$GLOBALS['cfg']['Server']['DisableIS'],
|
||||
$field['Collation'] ?? ''
|
||||
);
|
||||
if ($collation === null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$collations[$collation->getName()] = [
|
||||
'name' => $collation->getName(),
|
||||
'description' => $collation->getDescription(),
|
||||
];
|
||||
}
|
||||
|
||||
$engine = $this->tableObj->getStorageEngine();
|
||||
|
||||
return $this->template->render('table/structure/display_structure', [
|
||||
'collations' => $collations,
|
||||
'is_foreign_key_supported' => ForeignKey::isSupported($engine),
|
||||
'indexes' => Index::getFromTable($this->table, $this->db),
|
||||
'indexes_duplicates' => Index::findDuplicates($this->table, $this->db),
|
||||
'relation_parameters' => $relationParameters,
|
||||
'hide_structure_actions' => $GLOBALS['cfg']['HideStructureActions'] === true,
|
||||
'db' => $this->db,
|
||||
'table' => $this->table,
|
||||
'db_is_system_schema' => $isSystemSchema,
|
||||
'tbl_is_view' => $tbl_is_view,
|
||||
'mime_map' => $mime_map,
|
||||
'tbl_storage_engine' => $tbl_storage_engine,
|
||||
'primary' => $primary_index,
|
||||
'columns_with_unique_index' => $columns_with_unique_index,
|
||||
'columns_list' => $columns_list,
|
||||
'table_stats' => $tablestats ?? null,
|
||||
'fields' => $fields,
|
||||
'extracted_columnspecs' => $extracted_columnspecs,
|
||||
'columns_with_index' => $columns_with_index,
|
||||
'central_list' => $central_list,
|
||||
'comments_map' => $comments_map,
|
||||
'browse_mime' => $GLOBALS['cfg']['BrowseMIME'],
|
||||
'show_column_comments' => $GLOBALS['cfg']['ShowColumnComments'],
|
||||
'show_stats' => $GLOBALS['cfg']['ShowStats'],
|
||||
'mysql_int_version' => $this->dbi->getVersion(),
|
||||
'is_mariadb' => $this->dbi->isMariaDB(),
|
||||
'text_dir' => $GLOBALS['text_dir'],
|
||||
'is_active' => Tracker::isActive(),
|
||||
'have_partitioning' => Partition::havePartitioning(),
|
||||
'partitions' => Partition::getPartitions($this->db, $this->table),
|
||||
'partition_names' => Partition::getPartitionNames($this->db, $this->table),
|
||||
'default_sliders_state' => $GLOBALS['cfg']['InitialSlidersState'],
|
||||
'attributes' => $attributes,
|
||||
'displayed_fields' => $displayed_fields,
|
||||
'row_comments' => $row_comments,
|
||||
'route' => $route,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get HTML snippet for display table statistics
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getTableStats(bool $isSystemSchema)
|
||||
{
|
||||
global $showtable, $tbl_is_view;
|
||||
global $tbl_storage_engine, $table_info_num_rows, $tbl_collation;
|
||||
|
||||
if (empty($showtable)) {
|
||||
$showtable = $this->dbi->getTable($this->db, $this->table)->getStatusInfo(null, true);
|
||||
}
|
||||
|
||||
if (is_string($showtable)) {
|
||||
$showtable = [];
|
||||
}
|
||||
|
||||
if (empty($showtable['Data_length'])) {
|
||||
$showtable['Data_length'] = 0;
|
||||
}
|
||||
|
||||
if (empty($showtable['Index_length'])) {
|
||||
$showtable['Index_length'] = 0;
|
||||
}
|
||||
|
||||
$is_innodb = (isset($showtable['Type'])
|
||||
&& $showtable['Type'] === 'InnoDB');
|
||||
|
||||
$mergetable = $this->tableObj->isMerge();
|
||||
|
||||
// this is to display for example 261.2 MiB instead of 268k KiB
|
||||
$max_digits = 3;
|
||||
$decimals = 1;
|
||||
[$data_size, $data_unit] = Util::formatByteDown($showtable['Data_length'], $max_digits, $decimals);
|
||||
if ($mergetable === false) {
|
||||
[$index_size, $index_unit] = Util::formatByteDown($showtable['Index_length'], $max_digits, $decimals);
|
||||
}
|
||||
|
||||
if (isset($showtable['Data_free'])) {
|
||||
[$free_size, $free_unit] = Util::formatByteDown($showtable['Data_free'], $max_digits, $decimals);
|
||||
[$effect_size, $effect_unit] = Util::formatByteDown(
|
||||
$showtable['Data_length']
|
||||
+ $showtable['Index_length']
|
||||
- $showtable['Data_free'],
|
||||
$max_digits,
|
||||
$decimals
|
||||
);
|
||||
} else {
|
||||
[$effect_size, $effect_unit] = Util::formatByteDown(
|
||||
$showtable['Data_length']
|
||||
+ $showtable['Index_length'],
|
||||
$max_digits,
|
||||
$decimals
|
||||
);
|
||||
}
|
||||
|
||||
[$tot_size, $tot_unit] = Util::formatByteDown(
|
||||
$showtable['Data_length'] + $showtable['Index_length'],
|
||||
$max_digits,
|
||||
$decimals
|
||||
);
|
||||
|
||||
$avg_size = '';
|
||||
$avg_unit = '';
|
||||
if ($table_info_num_rows > 0) {
|
||||
[$avg_size, $avg_unit] = Util::formatByteDown(
|
||||
($showtable['Data_length']
|
||||
+ $showtable['Index_length'])
|
||||
/ $showtable['Rows'],
|
||||
6,
|
||||
1
|
||||
);
|
||||
}
|
||||
|
||||
/** @var Innodb $innodbEnginePlugin */
|
||||
$innodbEnginePlugin = StorageEngine::getEngine('Innodb');
|
||||
$innodb_file_per_table = $innodbEnginePlugin->supportsFilePerTable();
|
||||
|
||||
$tableCollation = [];
|
||||
$collation = Charsets::findCollationByName($this->dbi, $GLOBALS['cfg']['Server']['DisableIS'], $tbl_collation);
|
||||
if ($collation !== null) {
|
||||
$tableCollation = [
|
||||
'name' => $collation->getName(),
|
||||
'description' => $collation->getDescription(),
|
||||
];
|
||||
}
|
||||
|
||||
return $this->template->render('table/structure/display_table_stats', [
|
||||
'db' => $GLOBALS['db'],
|
||||
'table' => $GLOBALS['table'],
|
||||
'showtable' => $showtable,
|
||||
'table_info_num_rows' => $table_info_num_rows,
|
||||
'tbl_is_view' => $tbl_is_view,
|
||||
'db_is_system_schema' => $isSystemSchema,
|
||||
'tbl_storage_engine' => $tbl_storage_engine,
|
||||
'table_collation' => $tableCollation,
|
||||
'is_innodb' => $is_innodb,
|
||||
'mergetable' => $mergetable,
|
||||
'avg_size' => $avg_size ?? null,
|
||||
'avg_unit' => $avg_unit ?? null,
|
||||
'data_size' => $data_size,
|
||||
'data_unit' => $data_unit,
|
||||
'index_size' => $index_size ?? null,
|
||||
'index_unit' => $index_unit ?? null,
|
||||
'innodb_file_per_table' => $innodb_file_per_table,
|
||||
'free_size' => $free_size ?? null,
|
||||
'free_unit' => $free_unit ?? null,
|
||||
'effect_size' => $effect_size,
|
||||
'effect_unit' => $effect_unit,
|
||||
'tot_size' => $tot_size,
|
||||
'tot_unit' => $tot_unit,
|
||||
]);
|
||||
}
|
||||
}
|
|
@ -1,224 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Controllers\Table;
|
||||
|
||||
use PhpMyAdmin\DbTableExists;
|
||||
use PhpMyAdmin\Message;
|
||||
use PhpMyAdmin\ResponseRenderer;
|
||||
use PhpMyAdmin\Template;
|
||||
use PhpMyAdmin\Tracker;
|
||||
use PhpMyAdmin\Tracking;
|
||||
use PhpMyAdmin\Url;
|
||||
use PhpMyAdmin\Util;
|
||||
|
||||
use function __;
|
||||
use function array_map;
|
||||
use function define;
|
||||
use function explode;
|
||||
use function htmlspecialchars;
|
||||
use function sprintf;
|
||||
use function strtotime;
|
||||
|
||||
final class TrackingController extends AbstractController
|
||||
{
|
||||
/** @var Tracking */
|
||||
private $tracking;
|
||||
|
||||
public function __construct(
|
||||
ResponseRenderer $response,
|
||||
Template $template,
|
||||
string $db,
|
||||
string $table,
|
||||
Tracking $tracking
|
||||
) {
|
||||
parent::__construct($response, $template, $db, $table);
|
||||
$this->tracking = $tracking;
|
||||
}
|
||||
|
||||
public function __invoke(): void
|
||||
{
|
||||
global $text_dir, $urlParams, $msg, $errorUrl;
|
||||
global $data, $entries, $filter_ts_from, $filter_ts_to, $filter_users, $selection_schema;
|
||||
global $selection_data, $selection_both, $db, $table, $cfg;
|
||||
|
||||
$this->addScriptFiles(['vendor/jquery/jquery.tablesorter.js', 'table/tracking.js']);
|
||||
|
||||
define('TABLE_MAY_BE_ABSENT', true);
|
||||
|
||||
Util::checkParameters(['db', 'table']);
|
||||
|
||||
$urlParams = ['db' => $db, 'table' => $table];
|
||||
$errorUrl = Util::getScriptNameForOption($cfg['DefaultTabTable'], 'table');
|
||||
$errorUrl .= Url::getCommon($urlParams, '&');
|
||||
|
||||
DbTableExists::check();
|
||||
|
||||
$activeMessage = '';
|
||||
if (
|
||||
Tracker::isActive()
|
||||
&& Tracker::isTracked($GLOBALS['db'], $GLOBALS['table'])
|
||||
&& ! (isset($_POST['toggle_activation'])
|
||||
&& $_POST['toggle_activation'] === 'deactivate_now')
|
||||
&& ! (isset($_POST['report_export'])
|
||||
&& $_POST['export_type'] === 'sqldumpfile')
|
||||
) {
|
||||
$msg = Message::notice(
|
||||
sprintf(
|
||||
__('Tracking of %s is activated.'),
|
||||
htmlspecialchars($GLOBALS['db'] . '.' . $GLOBALS['table'])
|
||||
)
|
||||
);
|
||||
$activeMessage = $msg->getDisplay();
|
||||
}
|
||||
|
||||
$urlParams['goto'] = Url::getFromRoute('/table/tracking');
|
||||
$urlParams['back'] = Url::getFromRoute('/table/tracking');
|
||||
|
||||
$data = [];
|
||||
$entries = [];
|
||||
$filter_ts_from = null;
|
||||
$filter_ts_to = null;
|
||||
$filter_users = [];
|
||||
$selection_schema = false;
|
||||
$selection_data = false;
|
||||
$selection_both = false;
|
||||
|
||||
// Init vars for tracking report
|
||||
if (isset($_POST['report']) || isset($_POST['report_export'])) {
|
||||
$data = Tracker::getTrackedData($GLOBALS['db'], $GLOBALS['table'], $_POST['version']);
|
||||
|
||||
if (! isset($_POST['logtype'])) {
|
||||
$_POST['logtype'] = 'schema_and_data';
|
||||
}
|
||||
|
||||
if ($_POST['logtype'] === 'schema') {
|
||||
$selection_schema = true;
|
||||
} elseif ($_POST['logtype'] === 'data') {
|
||||
$selection_data = true;
|
||||
} else {
|
||||
$selection_both = true;
|
||||
}
|
||||
|
||||
if (! isset($_POST['date_from'])) {
|
||||
$_POST['date_from'] = $data['date_from'];
|
||||
}
|
||||
|
||||
if (! isset($_POST['date_to'])) {
|
||||
$_POST['date_to'] = $data['date_to'];
|
||||
}
|
||||
|
||||
if (! isset($_POST['users'])) {
|
||||
$_POST['users'] = '*';
|
||||
}
|
||||
|
||||
$filter_ts_from = strtotime($_POST['date_from']);
|
||||
$filter_ts_to = strtotime($_POST['date_to']);
|
||||
$filter_users = array_map('trim', explode(',', $_POST['users']));
|
||||
}
|
||||
|
||||
// Prepare export
|
||||
if (isset($_POST['report_export'])) {
|
||||
$entries = $this->tracking->getEntries($data, (int) $filter_ts_from, (int) $filter_ts_to, $filter_users);
|
||||
}
|
||||
|
||||
// Export as file download
|
||||
if (isset($_POST['report_export']) && $_POST['export_type'] === 'sqldumpfile') {
|
||||
$this->tracking->exportAsFileDownload($entries);
|
||||
}
|
||||
|
||||
$actionMessage = '';
|
||||
if (isset($_POST['submit_mult'])) {
|
||||
if (! empty($_POST['selected_versions'])) {
|
||||
if ($_POST['submit_mult'] === 'delete_version') {
|
||||
foreach ($_POST['selected_versions'] as $version) {
|
||||
$this->tracking->deleteTrackingVersion($db, $table, $version);
|
||||
}
|
||||
|
||||
$actionMessage = Message::success(
|
||||
__('Tracking versions deleted successfully.')
|
||||
)->getDisplay();
|
||||
}
|
||||
} else {
|
||||
$actionMessage = Message::notice(
|
||||
__('No versions selected.')
|
||||
)->getDisplay();
|
||||
}
|
||||
}
|
||||
|
||||
$deleteVersion = '';
|
||||
if (isset($_POST['submit_delete_version'])) {
|
||||
$deleteVersion = $this->tracking->deleteTrackingVersion($db, $table, $_POST['version']);
|
||||
}
|
||||
|
||||
$createVersion = '';
|
||||
if (isset($_POST['submit_create_version'])) {
|
||||
$createVersion = $this->tracking->createTrackingVersion($db, $table);
|
||||
}
|
||||
|
||||
$deactivateTracking = '';
|
||||
if (isset($_POST['toggle_activation']) && $_POST['toggle_activation'] === 'deactivate_now') {
|
||||
$deactivateTracking = $this->tracking->changeTracking($db, $table, 'deactivate');
|
||||
}
|
||||
|
||||
$activateTracking = '';
|
||||
if (isset($_POST['toggle_activation']) && $_POST['toggle_activation'] === 'activate_now') {
|
||||
$activateTracking = $this->tracking->changeTracking($db, $table, 'activate');
|
||||
}
|
||||
|
||||
// Export as SQL execution
|
||||
$message = '';
|
||||
if (isset($_POST['report_export']) && $_POST['export_type'] === 'execution') {
|
||||
$this->tracking->exportAsSqlExecution($entries);
|
||||
$msg = Message::success(__('SQL statements executed.'));
|
||||
$message = $msg->getDisplay();
|
||||
}
|
||||
|
||||
$sqlDump = '';
|
||||
if (isset($_POST['report_export']) && $_POST['export_type'] === 'sqldump') {
|
||||
$sqlDump = $this->tracking->exportAsSqlDump($db, $table, $entries);
|
||||
}
|
||||
|
||||
$schemaSnapshot = '';
|
||||
if (isset($_POST['snapshot'])) {
|
||||
$schemaSnapshot = $this->tracking->getHtmlForSchemaSnapshot($urlParams);
|
||||
}
|
||||
|
||||
$trackingReportRows = '';
|
||||
if (isset($_POST['report']) && (isset($_POST['delete_ddlog']) || isset($_POST['delete_dmlog']))) {
|
||||
$trackingReportRows = $this->tracking->deleteTrackingReportRows($db, $table, $data);
|
||||
}
|
||||
|
||||
$trackingReport = '';
|
||||
if (isset($_POST['report']) || isset($_POST['report_export'])) {
|
||||
$trackingReport = $this->tracking->getHtmlForTrackingReport(
|
||||
$data,
|
||||
$urlParams,
|
||||
$selection_schema,
|
||||
$selection_data,
|
||||
$selection_both,
|
||||
(int) $filter_ts_to,
|
||||
(int) $filter_ts_from,
|
||||
$filter_users
|
||||
);
|
||||
}
|
||||
|
||||
$main = $this->tracking->getHtmlForMainPage($db, $table, $urlParams, $text_dir);
|
||||
|
||||
$this->render('table/tracking/index', [
|
||||
'active_message' => $activeMessage,
|
||||
'action_message' => $actionMessage,
|
||||
'delete_version' => $deleteVersion,
|
||||
'create_version' => $createVersion,
|
||||
'deactivate_tracking' => $deactivateTracking,
|
||||
'activate_tracking' => $activateTracking,
|
||||
'message' => $message,
|
||||
'sql_dump' => $sqlDump,
|
||||
'schema_snapshot' => $schemaSnapshot,
|
||||
'tracking_report_rows' => $trackingReportRows,
|
||||
'tracking_report' => $trackingReport,
|
||||
'main' => $main,
|
||||
]);
|
||||
}
|
||||
}
|
|
@ -1,92 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Controllers\Table;
|
||||
|
||||
use PhpMyAdmin\Database\Triggers;
|
||||
use PhpMyAdmin\DatabaseInterface;
|
||||
use PhpMyAdmin\DbTableExists;
|
||||
use PhpMyAdmin\ResponseRenderer;
|
||||
use PhpMyAdmin\Template;
|
||||
use PhpMyAdmin\Url;
|
||||
use PhpMyAdmin\Util;
|
||||
|
||||
use function in_array;
|
||||
use function strlen;
|
||||
|
||||
/**
|
||||
* Triggers management.
|
||||
*/
|
||||
class TriggersController extends AbstractController
|
||||
{
|
||||
/** @var DatabaseInterface */
|
||||
private $dbi;
|
||||
|
||||
public function __construct(
|
||||
ResponseRenderer $response,
|
||||
Template $template,
|
||||
string $db,
|
||||
string $table,
|
||||
DatabaseInterface $dbi
|
||||
) {
|
||||
parent::__construct($response, $template, $db, $table);
|
||||
$this->dbi = $dbi;
|
||||
}
|
||||
|
||||
public function __invoke(): void
|
||||
{
|
||||
global $db, $table, $tables, $num_tables, $total_num_tables, $sub_part;
|
||||
global $tooltip_truename, $tooltip_aliasname, $pos;
|
||||
global $errors, $urlParams, $errorUrl, $cfg;
|
||||
|
||||
$this->addScriptFiles(['database/triggers.js']);
|
||||
|
||||
if (! $this->response->isAjax()) {
|
||||
/**
|
||||
* Displays the header and tabs
|
||||
*/
|
||||
if (! empty($table) && in_array($table, $this->dbi->getTables($db))) {
|
||||
Util::checkParameters(['db', 'table']);
|
||||
|
||||
$urlParams = ['db' => $db, 'table' => $table];
|
||||
$errorUrl = Util::getScriptNameForOption($cfg['DefaultTabTable'], 'table');
|
||||
$errorUrl .= Url::getCommon($urlParams, '&');
|
||||
|
||||
DbTableExists::check();
|
||||
} else {
|
||||
$table = '';
|
||||
|
||||
Util::checkParameters(['db']);
|
||||
|
||||
$errorUrl = Util::getScriptNameForOption($cfg['DefaultTabDatabase'], 'database');
|
||||
$errorUrl .= Url::getCommon(['db' => $db], '&');
|
||||
|
||||
if (! $this->hasDatabase()) {
|
||||
return;
|
||||
}
|
||||
|
||||
[
|
||||
$tables,
|
||||
$num_tables,
|
||||
$total_num_tables,
|
||||
$sub_part,,,
|
||||
$tooltip_truename,
|
||||
$tooltip_aliasname,
|
||||
$pos,
|
||||
] = Util::getDbInfo($db, $sub_part ?? '');
|
||||
}
|
||||
} elseif (strlen($db) > 0) {
|
||||
$this->dbi->selectDb($db);
|
||||
}
|
||||
|
||||
/**
|
||||
* Keep a list of errors that occurred while
|
||||
* processing an 'Add' or 'Edit' operation.
|
||||
*/
|
||||
$errors = [];
|
||||
|
||||
$triggers = new Triggers($this->dbi, $this->template, $this->response);
|
||||
$triggers->main();
|
||||
}
|
||||
}
|
|
@ -1,467 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Controllers\Table;
|
||||
|
||||
use PhpMyAdmin\ConfigStorage\Relation;
|
||||
use PhpMyAdmin\Core;
|
||||
use PhpMyAdmin\DatabaseInterface;
|
||||
use PhpMyAdmin\DbTableExists;
|
||||
use PhpMyAdmin\ResponseRenderer;
|
||||
use PhpMyAdmin\Table\Search;
|
||||
use PhpMyAdmin\Template;
|
||||
use PhpMyAdmin\Url;
|
||||
use PhpMyAdmin\Util;
|
||||
use PhpMyAdmin\Utils\Gis;
|
||||
|
||||
use function array_search;
|
||||
use function array_values;
|
||||
use function count;
|
||||
use function htmlspecialchars;
|
||||
use function in_array;
|
||||
use function intval;
|
||||
use function is_numeric;
|
||||
use function json_encode;
|
||||
use function mb_strtolower;
|
||||
use function md5;
|
||||
use function preg_match;
|
||||
use function preg_replace;
|
||||
use function str_ireplace;
|
||||
use function str_replace;
|
||||
use function strncasecmp;
|
||||
use function strtoupper;
|
||||
|
||||
/**
|
||||
* Handles table zoom search tab.
|
||||
*
|
||||
* Display table zoom search form, create SQL queries from form data.
|
||||
*/
|
||||
class ZoomSearchController extends AbstractController
|
||||
{
|
||||
/** @var Search */
|
||||
private $search;
|
||||
|
||||
/** @var Relation */
|
||||
private $relation;
|
||||
|
||||
/** @var array */
|
||||
private $columnNames;
|
||||
|
||||
/** @var array */
|
||||
private $columnTypes;
|
||||
|
||||
/** @var array */
|
||||
private $originalColumnTypes;
|
||||
|
||||
/** @var array */
|
||||
private $columnCollations;
|
||||
|
||||
/** @var array */
|
||||
private $columnNullFlags;
|
||||
|
||||
/** @var bool Whether a geometry column is present */
|
||||
private $geomColumnFlag;
|
||||
|
||||
/** @var array Foreign keys */
|
||||
private $foreigners;
|
||||
|
||||
/** @var DatabaseInterface */
|
||||
private $dbi;
|
||||
|
||||
public function __construct(
|
||||
ResponseRenderer $response,
|
||||
Template $template,
|
||||
string $db,
|
||||
string $table,
|
||||
Search $search,
|
||||
Relation $relation,
|
||||
DatabaseInterface $dbi
|
||||
) {
|
||||
parent::__construct($response, $template, $db, $table);
|
||||
$this->search = $search;
|
||||
$this->relation = $relation;
|
||||
$this->dbi = $dbi;
|
||||
|
||||
$this->columnNames = [];
|
||||
$this->columnTypes = [];
|
||||
$this->originalColumnTypes = [];
|
||||
$this->columnCollations = [];
|
||||
$this->columnNullFlags = [];
|
||||
$this->geomColumnFlag = false;
|
||||
$this->foreigners = [];
|
||||
$this->loadTableInfo();
|
||||
}
|
||||
|
||||
public function __invoke(): void
|
||||
{
|
||||
global $goto, $db, $table, $urlParams, $cfg, $errorUrl;
|
||||
|
||||
Util::checkParameters(['db', 'table']);
|
||||
|
||||
$urlParams = ['db' => $db, 'table' => $table];
|
||||
$errorUrl = Util::getScriptNameForOption($cfg['DefaultTabTable'], 'table');
|
||||
$errorUrl .= Url::getCommon($urlParams, '&');
|
||||
|
||||
DbTableExists::check();
|
||||
|
||||
$this->addScriptFiles([
|
||||
'makegrid.js',
|
||||
'sql.js',
|
||||
'vendor/jqplot/jquery.jqplot.js',
|
||||
'vendor/jqplot/plugins/jqplot.canvasTextRenderer.js',
|
||||
'vendor/jqplot/plugins/jqplot.canvasAxisLabelRenderer.js',
|
||||
'vendor/jqplot/plugins/jqplot.dateAxisRenderer.js',
|
||||
'vendor/jqplot/plugins/jqplot.highlighter.js',
|
||||
'vendor/jqplot/plugins/jqplot.cursor.js',
|
||||
'table/zoom_plot_jqplot.js',
|
||||
'table/change.js',
|
||||
]);
|
||||
|
||||
/**
|
||||
* Handle AJAX request for data row on point select
|
||||
*/
|
||||
if (isset($_POST['get_data_row']) && $_POST['get_data_row'] == true) {
|
||||
$this->getDataRowAction();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle AJAX request for changing field information
|
||||
* (value,collation,operators,field values) in input form
|
||||
*/
|
||||
if (isset($_POST['change_tbl_info']) && $_POST['change_tbl_info'] == true) {
|
||||
$this->changeTableInfoAction();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
//Set default datalabel if not selected
|
||||
if (! isset($_POST['zoom_submit']) || $_POST['dataLabel'] == '') {
|
||||
$dataLabel = $this->relation->getDisplayField($this->db, $this->table);
|
||||
} else {
|
||||
$dataLabel = $_POST['dataLabel'];
|
||||
}
|
||||
|
||||
// Displays the zoom search form
|
||||
$this->displaySelectionFormAction($dataLabel);
|
||||
|
||||
/**
|
||||
* Handle the input criteria and generate the query result
|
||||
* Form for displaying query results
|
||||
*/
|
||||
if (
|
||||
! isset($_POST['zoom_submit'])
|
||||
|| $_POST['criteriaColumnNames'][0] === 'pma_null'
|
||||
|| $_POST['criteriaColumnNames'][1] === 'pma_null'
|
||||
|| $_POST['criteriaColumnNames'][0] == $_POST['criteriaColumnNames'][1]
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (! isset($goto)) {
|
||||
$goto = Util::getScriptNameForOption($GLOBALS['cfg']['DefaultTabTable'], 'table');
|
||||
}
|
||||
|
||||
$this->zoomSubmitAction($dataLabel, $goto);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all the columns of a table along with their types, collations
|
||||
* and whether null or not.
|
||||
*/
|
||||
private function loadTableInfo(): void
|
||||
{
|
||||
// Gets the list and number of columns
|
||||
$columns = $this->dbi->getColumns($this->db, $this->table, true);
|
||||
// Get details about the geometry functions
|
||||
$geom_types = Gis::getDataTypes();
|
||||
|
||||
foreach ($columns as $row) {
|
||||
// set column name
|
||||
$this->columnNames[] = $row['Field'];
|
||||
|
||||
$type = (string) $row['Type'];
|
||||
// before any replacement
|
||||
$this->originalColumnTypes[] = mb_strtolower($type);
|
||||
// check whether table contains geometric columns
|
||||
if (in_array($type, $geom_types)) {
|
||||
$this->geomColumnFlag = true;
|
||||
}
|
||||
|
||||
// reformat mysql query output
|
||||
if (strncasecmp($type, 'set', 3) == 0 || strncasecmp($type, 'enum', 4) == 0) {
|
||||
$type = str_replace(',', ', ', $type);
|
||||
} else {
|
||||
// strip the "BINARY" attribute, except if we find "BINARY(" because
|
||||
// this would be a BINARY or VARBINARY column type
|
||||
if (! preg_match('@BINARY[\(]@i', $type)) {
|
||||
$type = str_ireplace('BINARY', '', $type);
|
||||
}
|
||||
|
||||
$type = str_ireplace('ZEROFILL', '', $type);
|
||||
$type = str_ireplace('UNSIGNED', '', $type);
|
||||
$type = mb_strtolower($type);
|
||||
}
|
||||
|
||||
if (empty($type)) {
|
||||
$type = ' ';
|
||||
}
|
||||
|
||||
$this->columnTypes[] = $type;
|
||||
$this->columnNullFlags[] = $row['Null'];
|
||||
$this->columnCollations[] = ! empty($row['Collation']) && $row['Collation'] !== 'NULL'
|
||||
? $row['Collation']
|
||||
: '';
|
||||
}
|
||||
|
||||
// Retrieve foreign keys
|
||||
$this->foreigners = $this->relation->getForeigners($this->db, $this->table);
|
||||
}
|
||||
|
||||
/**
|
||||
* Display selection form action
|
||||
*
|
||||
* @param string $dataLabel Data label
|
||||
*/
|
||||
public function displaySelectionFormAction($dataLabel = null): void
|
||||
{
|
||||
global $goto;
|
||||
|
||||
if (! isset($goto)) {
|
||||
$goto = Util::getScriptNameForOption($GLOBALS['cfg']['DefaultTabTable'], 'table');
|
||||
}
|
||||
|
||||
$column_names = $this->columnNames;
|
||||
$criteria_column_names = $_POST['criteriaColumnNames'] ?? null;
|
||||
$keys = [];
|
||||
for ($i = 0; $i < 4; $i++) {
|
||||
if (! isset($criteria_column_names[$i])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($criteria_column_names[$i] === 'pma_null') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$keys[$criteria_column_names[$i]] = array_search($criteria_column_names[$i], $column_names);
|
||||
}
|
||||
|
||||
$this->render('table/zoom_search/index', [
|
||||
'db' => $this->db,
|
||||
'table' => $this->table,
|
||||
'goto' => $goto,
|
||||
'self' => $this,
|
||||
'geom_column_flag' => $this->geomColumnFlag,
|
||||
'column_names' => $column_names,
|
||||
'data_label' => $dataLabel,
|
||||
'keys' => $keys,
|
||||
'criteria_column_names' => $criteria_column_names,
|
||||
'criteria_column_types' => $_POST['criteriaColumnTypes'] ?? null,
|
||||
'max_plot_limit' => ! empty($_POST['maxPlotLimit'])
|
||||
? intval($_POST['maxPlotLimit'])
|
||||
: intval($GLOBALS['cfg']['maxRowPlotLimit']),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get data row action
|
||||
*/
|
||||
public function getDataRowAction(): void
|
||||
{
|
||||
if (! Core::checkSqlQuerySignature($_POST['where_clause'], $_POST['where_clause_sign'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$extra_data = [];
|
||||
$row_info_query = 'SELECT * FROM ' . Util::backquote($_POST['db']) . '.'
|
||||
. Util::backquote($_POST['table']) . ' WHERE ' . $_POST['where_clause'];
|
||||
$result = $this->dbi->query($row_info_query . ';');
|
||||
$fields_meta = $this->dbi->getFieldsMeta($result);
|
||||
while ($row = $result->fetchAssoc()) {
|
||||
// for bit fields we need to convert them to printable form
|
||||
$i = 0;
|
||||
foreach ($row as $col => $val) {
|
||||
if ($fields_meta[$i]->isMappedTypeBit) {
|
||||
$row[$col] = Util::printableBitValue((int) $val, (int) $fields_meta[$i]->length);
|
||||
}
|
||||
|
||||
$i++;
|
||||
}
|
||||
|
||||
$extra_data['row_info'] = $row;
|
||||
}
|
||||
|
||||
$this->response->addJSON($extra_data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Change table info action
|
||||
*/
|
||||
public function changeTableInfoAction(): void
|
||||
{
|
||||
$field = $_POST['field'];
|
||||
if ($field === 'pma_null') {
|
||||
$this->response->addJSON('field_type', '');
|
||||
$this->response->addJSON('field_collation', '');
|
||||
$this->response->addJSON('field_operators', '');
|
||||
$this->response->addJSON('field_value', '');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$key = array_search($field, $this->columnNames);
|
||||
$search_index = (isset($_POST['it']) && is_numeric($_POST['it'])
|
||||
? intval($_POST['it']) : 0);
|
||||
|
||||
$properties = $this->getColumnProperties($search_index, $key);
|
||||
$this->response->addJSON(
|
||||
'field_type',
|
||||
htmlspecialchars($properties['type'])
|
||||
);
|
||||
$this->response->addJSON('field_collation', $properties['collation']);
|
||||
$this->response->addJSON('field_operators', $properties['func']);
|
||||
$this->response->addJSON('field_value', $properties['value']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Zoom submit action
|
||||
*
|
||||
* @param string $dataLabel Data label
|
||||
* @param string $goto Goto
|
||||
*/
|
||||
public function zoomSubmitAction($dataLabel, $goto): void
|
||||
{
|
||||
//Query generation part
|
||||
$sql_query = $this->search->buildSqlQuery();
|
||||
$sql_query .= ' LIMIT ' . $_POST['maxPlotLimit'];
|
||||
|
||||
//Query execution part
|
||||
$result = $this->dbi->query($sql_query . ';');
|
||||
$fields_meta = $this->dbi->getFieldsMeta($result);
|
||||
$data = [];
|
||||
while ($row = $result->fetchAssoc()) {
|
||||
//Need a row with indexes as 0,1,2 for the getUniqueCondition
|
||||
// hence using a temporary array
|
||||
$tmpRow = array_values($row);
|
||||
|
||||
//Get unique condition on each row (will be needed for row update)
|
||||
$uniqueCondition = Util::getUniqueCondition(
|
||||
count($this->columnNames),
|
||||
$fields_meta,
|
||||
$tmpRow,
|
||||
true
|
||||
);
|
||||
//Append it to row array as where_clause
|
||||
$row['where_clause'] = $uniqueCondition[0];
|
||||
$row['where_clause_sign'] = Core::signSqlQuery($uniqueCondition[0]);
|
||||
|
||||
$tmpData = [
|
||||
$_POST['criteriaColumnNames'][0] => $row[$_POST['criteriaColumnNames'][0]],
|
||||
$_POST['criteriaColumnNames'][1] => $row[$_POST['criteriaColumnNames'][1]],
|
||||
'where_clause' => $uniqueCondition[0],
|
||||
'where_clause_sign' => Core::signSqlQuery($uniqueCondition[0]),
|
||||
];
|
||||
$tmpData[$dataLabel] = $dataLabel ? $row[$dataLabel] : '';
|
||||
$data[] = $tmpData;
|
||||
}
|
||||
|
||||
unset($tmpData);
|
||||
|
||||
$column_names_hashes = [];
|
||||
|
||||
foreach ($this->columnNames as $columnName) {
|
||||
$column_names_hashes[$columnName] = md5($columnName);
|
||||
}
|
||||
|
||||
$this->render('table/zoom_search/result_form', [
|
||||
'db' => $this->db,
|
||||
'table' => $this->table,
|
||||
'column_names' => $this->columnNames,
|
||||
'column_names_hashes' => $column_names_hashes,
|
||||
'foreigners' => $this->foreigners,
|
||||
'column_null_flags' => $this->columnNullFlags,
|
||||
'column_types' => $this->columnTypes,
|
||||
'goto' => $goto,
|
||||
'data' => $data,
|
||||
'data_json' => json_encode($data),
|
||||
'zoom_submit' => isset($_POST['zoom_submit']),
|
||||
'foreign_max_limit' => $GLOBALS['cfg']['ForeignKeyMaxLimit'],
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a column's type, collation, operators list, and criteria value
|
||||
* to display in table search form
|
||||
*
|
||||
* @param int $search_index Row number in table search form
|
||||
* @param int $column_index Column index in ColumnNames array
|
||||
*
|
||||
* @return array Array containing column's properties
|
||||
*/
|
||||
public function getColumnProperties($search_index, $column_index)
|
||||
{
|
||||
$selected_operator = ($_POST['criteriaColumnOperators'][$search_index] ?? '');
|
||||
$entered_value = ($_POST['criteriaValues'] ?? '');
|
||||
//Gets column's type and collation
|
||||
$type = $this->columnTypes[$column_index];
|
||||
$collation = $this->columnCollations[$column_index];
|
||||
$cleanType = preg_replace('@\(.*@s', '', $type);
|
||||
//Gets column's comparison operators depending on column type
|
||||
$typeOperators = $this->dbi->types->getTypeOperatorsHtml(
|
||||
$cleanType,
|
||||
$this->columnNullFlags[$column_index],
|
||||
$selected_operator
|
||||
);
|
||||
$func = $this->template->render('table/search/column_comparison_operators', [
|
||||
'search_index' => $search_index,
|
||||
'type_operators' => $typeOperators,
|
||||
]);
|
||||
//Gets link to browse foreign data(if any) and criteria inputbox
|
||||
$foreignData = $this->relation->getForeignData(
|
||||
$this->foreigners,
|
||||
$this->columnNames[$column_index],
|
||||
false,
|
||||
'',
|
||||
''
|
||||
);
|
||||
$htmlAttributes = '';
|
||||
if (in_array($cleanType, $this->dbi->types->getIntegerTypes())) {
|
||||
$extractedColumnspec = Util::extractColumnSpec($this->originalColumnTypes[$column_index]);
|
||||
$is_unsigned = $extractedColumnspec['unsigned'];
|
||||
$minMaxValues = $this->dbi->types->getIntegerRange($cleanType, ! $is_unsigned);
|
||||
$htmlAttributes = 'data-min="' . $minMaxValues[0] . '" '
|
||||
. 'data-max="' . $minMaxValues[1] . '"';
|
||||
}
|
||||
|
||||
$htmlAttributes .= ' onfocus="return '
|
||||
. 'verifyAfterSearchFieldChange(' . $search_index . ', \'#zoom_search_form\')"';
|
||||
|
||||
$value = $this->template->render('table/search/input_box', [
|
||||
'str' => '',
|
||||
'column_type' => (string) $type,
|
||||
'column_data_type' => strtoupper($cleanType),
|
||||
'html_attributes' => $htmlAttributes,
|
||||
'column_id' => 'fieldID_',
|
||||
'in_zoom_search_edit' => false,
|
||||
'foreigners' => $this->foreigners,
|
||||
'column_name' => $this->columnNames[$column_index],
|
||||
'column_name_hash' => md5($this->columnNames[$column_index]),
|
||||
'foreign_data' => $foreignData,
|
||||
'table' => $this->table,
|
||||
'column_index' => $search_index,
|
||||
'foreign_max_limit' => $GLOBALS['cfg']['ForeignKeyMaxLimit'],
|
||||
'criteria_values' => $entered_value,
|
||||
'db' => $this->db,
|
||||
'in_fbs' => true,
|
||||
]);
|
||||
|
||||
return [
|
||||
'type' => $type,
|
||||
'collation' => $collation,
|
||||
'func' => $func,
|
||||
'value' => $value,
|
||||
];
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue