Rewrite export alias defining

The old code did load all colums for all tables in all databases in
export. This can easily explode for server wide exports or exports on
database with lot of tables.

It has been rewritten in a way to load required data on demand from
server. This will make aliases defining slightly slower, but I think
it's acceptable price for making the export work on larger databases.

Fixes #13008

Signed-off-by: Michal Čihař <michal@cihar.com>
This commit is contained in:
Michal Čihař 2017-06-13 16:57:26 +02:00
parent 97e604adb7
commit 358d2fd1f1
10 changed files with 332 additions and 209 deletions

View File

@ -13,6 +13,7 @@ phpMyAdmin - ChangeLog
- issue #13193 Improved documentation on user settings
- issue #13092 Gracefully handle early fatal errors in AJAX requests
- issue #13327 Fixed Incorrect NavigationTreeEnableExpansion default value in the documentation
- issue #13008 Fixed export of database with a lot of tables
4.7.1 (2017-05-25)
- issue #13132 Always execute tracking queries as controluser

34
ajax.php Normal file
View File

@ -0,0 +1,34 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Generic AJAX endpoint for getting information about database
*
* @package PhpMyAdmin
*/
use PMA\libraries\Response;
use PMA\libraries\Util;
require_once 'libraries/common.inc.php';
$response = Response::getInstance();
if (empty($_POST['type'])) {
PMA_fatalError(__('Bad type!'));
}
switch ($_POST['type']) {
case 'list-databases':
$response->addJSON('databases', $GLOBALS['dblist']->databases);
break;
case 'list-tables':
Util::checkParameters(array('db'));
$response->addJSON('tables', $GLOBALS['dbi']->getTables($_REQUEST['db']));
break;
case 'list-columns':
Util::checkParameters(array('db', 'table'));
$response->addJSON('columns', $GLOBALS['dbi']->getColumnNames($_REQUEST['db'], $_REQUEST['table']));
break;
default:
PMA_fatalError(__('Bad type!'));
}

View File

@ -225,8 +225,10 @@ AJAX.registerTeardown('export.js', function () {
$("input[type='radio'][name='quick_or_custom']").unbind('change');
$("input[type='radio'][name='allrows']").unbind('change');
$('#btn_alias_config').off('click');
$('#db_alias_select').off('change');
$('.table_alias_select').off('change');
$('.alias_remove').off('click');
$('#db_alias_button').off('click');
$('#table_alias_button').off('click');
$('#column_alias_button').off('click');
$('input[name="table_select[]"]').off('change');
$('input[name="table_structure[]"]').off('change');
$('input[name="table_data[]"]').off('change');
@ -764,23 +766,10 @@ function aliasSelectHandler(event) {
function createAliasModal(event) {
event.preventDefault();
var dlgButtons = {};
dlgButtons[PMA_messages.strResetAll] = function() {
$(this).find('input[type="text"]').val('');
};
dlgButtons[PMA_messages.strReset] = function() {
$(this).find('input[type="text"]:visible').val('');
};
dlgButtons[PMA_messages.strSaveAndClose] = function() {
$(this).dialog("close");
// do not fillup form submission with empty values
$.each($(this).find('input[type="text"]'), function (i, e) {
if ($(e).val().trim().length == 0) {
$(e).prop('disabled', true);
}
});
$('#alias_modal').parent().appendTo($('form[name="dump"]'));
};
$('#alias_modal').find('input[type="text"]').prop('disabled', false);
$('#alias_modal').dialog({
width: Math.min($(window).width() - 100, 700),
maxHeight: $(window).height(),
@ -789,24 +778,79 @@ function createAliasModal(event) {
buttons: dlgButtons,
create: function() {
$(this).css('maxHeight', $(window).height() - 150);
$('.alias-dialog .ui-dialog-titlebar-close').remove();
var db = PMA_commonParams.get('db');
if (db) {
var option = $('<option></option>');
option.text(db);
option.attr('value', db);
$('#db_alias_select').append(option).val(db).change();
} else {
var params = {
ajax_request : true,
token : PMA_commonParams.get('token'),
server : PMA_commonParams.get('server'),
type: 'list-databases'
};
$.post('ajax.php', params, function (response) {
if (response.success === true) {
$.each(response.databases, function (idx, value) {
var option = $('<option></option>');
option.text(value);
option.attr('value', value);
$('#db_alias_select').append(option);
});
} else {
PMA_ajaxShowMessage(response.error, false);
}
});
}
},
close: function() {
var isEmpty = true;
$(this).find('input[type="text"]').each(function() {
// trim input fields on close
$(this).val($(this).val().trim());
// check if non empty field present
// trim empty input fields on close
if ($(this).val()) {
isEmpty = false;
} else {
$(this).parents('tr').remove();
}
});
// Toggle checkbox based on aliases
$('input#btn_alias_config').prop('checked', !isEmpty);
},
position: { my: "center top", at: "center top", of: window }
});
// Call change event of .table_alias_select
$('.table_alias_select:visible').trigger('change');
}
function aliasToggleRow(elm) {
var inputs = elm.parents('tr').find('input,button');
if (elm.val()) {
inputs.attr('disabled', false);
} else {
inputs.attr('disabled', true);
}
}
function addAlias(type, name, field, value) {
if (value === '') {
return;
}
var row = $('#alias_data tfoot tr').clone();
row.find('th').text(type);
row.find('td:first').text(name);
row.find('input').attr('name', field);
row.find('input').val(value);
row.find('.alias_remove').on('click', function () {
$(this).parents('tr').remove();
});
var matching = $('#alias_data [name="' + $.escapeSelector(field) + '"]');
if (matching.length > 0) {
matching.parents('tr').remove();
}
$('#alias_data tbody').append(row);
}
AJAX.registerOnload('export.js', function () {
@ -846,18 +890,100 @@ AJAX.registerOnload('export.js', function () {
// Open Alias Modal Dialog on click
$('#btn_alias_config').on('click', createAliasModal);
// Database alias select on change event
$('#db_alias_select').on(
'change',
{sel: 'span', type: '_tables'},
aliasSelectHandler
);
// Table alias select on change event
$('.table_alias_select').on(
'change',
{sel: 'table', type: '_cols'},
aliasSelectHandler
);
$('.alias_remove').on('click', function () {
$(this).parents('tr').remove();
});
$('#db_alias_select').on('change', function () {
aliasToggleRow($(this));
var db = $(this).val();
var table = PMA_commonParams.get('table');
if (table) {
var option = $('<option></option>');
option.text(table);
option.attr('value', table);
$('#table_alias_select').append(option).val(table).change();
} else {
var params = {
ajax_request : true,
token : PMA_commonParams.get('token'),
server : PMA_commonParams.get('server'),
db : $(this).val(),
type: 'list-tables'
};
$.post('ajax.php', params, function (response) {
if (response.success === true) {
$.each(response.tables, function (idx, value) {
var option = $('<option></option>');
option.text(value);
option.attr('value', value);
$('#table_alias_select').append(option);
});
} else {
PMA_ajaxShowMessage(response.error, false);
}
});
}
});
$('#table_alias_select').on('change', function () {
aliasToggleRow($(this));
var params = {
ajax_request : true,
token : PMA_commonParams.get('token'),
server : PMA_commonParams.get('server'),
db : $('#db_alias_select').val(),
table: $(this).val(),
type: 'list-columns'
};
$.post('ajax.php', params, function (response) {
if (response.success === true) {
$.each(response.columns, function (idx, value) {
var option = $('<option></option>');
option.text(value);
option.attr('value', value);
$('#column_alias_select').append(option);
});
} else {
PMA_ajaxShowMessage(response.error, false);
}
});
});
$('#column_alias_select').on('change', function () {
aliasToggleRow($(this));
});
$('#db_alias_button').on('click', function (e) {
e.preventDefault();
var db = $('#db_alias_select').val();
addAlias(
PMA_messages.strAliasDatabase,
db,
'aliases[' + db + '][alias]',
$('#db_alias_name').val()
);
$('#db_alias_name').val('');
});
$('#table_alias_button').on('click', function (e) {
e.preventDefault();
var db = $('#db_alias_select').val();
var table = $('#table_alias_select').val();
addAlias(
PMA_messages.strAliasTable,
db + '.' + table,
'aliases[' + db + '][tables][' + table + '][alias]',
$('#table_alias_name').val()
);
$('#table_alias_name').val('');
});
$('#column_alias_button').on('click', function (e) {
e.preventDefault();
var db = $('#db_alias_select').val();
var table = $('#table_alias_select').val();
var column = $('#column_alias_select').val();
addAlias(
PMA_messages.strAliasColumn,
db + '.' + table + '.' + column,
'aliases[' + db + '][tables][' + table + '][colums][' + column + ']',
$('#column_alias_name').val()
);
$('#column_alias_name').val('');
});
});

View File

@ -295,6 +295,10 @@ $js_messages['strProfilingResults'] = __('Profiling results');
$js_messages['strTable'] = _pgettext('Display format', 'Table');
$js_messages['strChart'] = __('Chart');
$js_messages['strAliasDatabase'] = _pgettext('Alias', 'Database');
$js_messages['strAliasTable'] = _pgettext('Alias', 'Table');
$js_messages['strAliasColumn'] = _pgettext('Alias', 'Column');
/* l10n: A collection of available filters */
$js_messages['strFiltersForLogTable'] = __('Log table filter options');
/* l10n: Filter as in "Start Filtering" */

View File

@ -11,6 +11,7 @@ use PMA\libraries\Message;
use PMA\libraries\plugins\ExportPlugin;
use PMA\libraries\Response;
use PMA\libraries\Table;
use PMA\libraries\Template;
use PMA\libraries\URL;
/**
@ -877,25 +878,76 @@ function PMA_getHtmlForExportOptions(
$html .= PMA_getHtmlForExportOptionsQuickExport();
}
$html .= PMA_getHtmlForAliasModalDialog($db, $table);
$html .= PMA_getHtmlForAliasModalDialog();
$html .= PMA_getHtmlForExportOptionsOutput($export_type);
$html .= PMA_getHtmlForExportOptionsFormat($export_list);
return $html;
}
/**
* Prints Html For Alias Modal Dialog
*
* @param String $db Selected DB
* @param String $table Selected Table
* Generate Html For currently defined aliases
*
* @return string
*/
function PMA_getHtmlForAliasModalDialog($db = '', $table = '')
function PMA_getHtmlForCurrentAlias()
{
$result = '<table id="alias_data"><thead><tr><th colspan="4">'
. __('Defined aliases')
. '</th></tr></thead><tbody>';
$template = Template::get('export/alias_item');
if (isset($_SESSION['tmpval']['aliases'])) {
$aliases = $_SESSION['tmpval']['aliases'];
foreach ($_SESSION['tmpval']['aliases'] as $db => $db_data) {
if (isset($db_data['alias'])) {
$result .= $template->render(array(
'type' => _pgettext('Alias', 'Database'),
'name' => $db,
'field' => 'aliases[' . $db . '][alias]',
'value' => $db_data['alias'],
));
}
if (! isset($db_data['tables'])) {
continue;
}
foreach ($db_data['tables'] as $table => $table_data) {
if (isset($table_data['alias'])) {
$result .= $template->render(array(
'type' => _pgettext('Alias', 'Table'),
'name' => $db . '.' . $table,
'field' => 'aliases[' . $db . '][tables][' . $table . '][alias]',
'value' => $table_data['alias'],
));
}
if (! isset($table_data['columns'])) {
continue;
}
foreach ($table_data['columns'] as $column => $column_name) {
$result .= $template->render(array(
'type' => _pgettext('Alias', 'Column'),
'name' => $db . '.' . $table . '.'. $column,
'field' => 'aliases[' . $db . '][tables][' . $table . '][colums][' . $column . ']',
'value' => $column_name,
));
}
}
}
}
// Empty row for javascript manipulations
$result .= '</tbody><tfoot class="hide">' . $template->render(array(
'type' => '', 'name' => '', 'field' => 'aliases_new', 'value' => ''
)) . '</tfoot>';
return $result . '</table>';
}
/**
* Generate Html For Alias Modal Dialog
*
* @return string
*/
function PMA_getHtmlForAliasModalDialog()
{
// In case of server export, the following list of
// databases are not shown in the list.
$dbs_not_allowed = array(
@ -906,114 +958,10 @@ function PMA_getHtmlForAliasModalDialog($db = '', $table = '')
// Fetch Columns info
// Server export does not have db set.
$title = __('Rename exported databases/tables/columns');
if (empty($db)) {
$databases = $GLOBALS['dbi']->getColumnsFull(
null, null, null, $GLOBALS['userlink']
);
foreach ($dbs_not_allowed as $db) {
unset($databases[$db]);
}
// Database export does not have table set.
} elseif (empty($table)) {
$tables = $GLOBALS['dbi']->getColumnsFull(
$db, null, null, $GLOBALS['userlink']
);
$databases = array($db => $tables);
// Table export
} else {
$columns = $GLOBALS['dbi']->getColumnsFull(
$db, $table, null, $GLOBALS['userlink']
);
$databases = array(
$db => array(
$table => $columns
)
);
}
$html = '<div id="alias_modal" class="hide" title="' . $title . '">';
$db_html = '<label class="col-2">' . __('Select database') . ': '
. '</label><select id="db_alias_select">';
$table_html = '<label class="col-2">' . __('Select table') . ': </label>';
$first_db = true;
$table_input_html = $db_input_html = '';
foreach ($databases as $db => $tables) {
$val = '';
if (!empty($aliases[$db]['alias'])) {
$val = htmlspecialchars($aliases[$db]['alias']);
}
$db = htmlspecialchars($db);
$name_attr = 'aliases[' . $db . '][alias]';
$id_attr = substr(md5($name_attr), 0, 12);
$class = 'hide';
if ($first_db) {
$first_db = false;
$class = '';
$db_input_html = '<label class="col-2" for="' . $id_attr . '">'
. __('New database name') . ': </label>';
}
$db_input_html .= '<input type="text" name="' . $name_attr . '" '
. 'placeholder="' . $db . ' alias" class="' . $class . '" '
. 'id="' . $id_attr . '" value="' . $val . '" disabled="disabled"/>';
$db_html .= '<option value="' . $id_attr . '">' . $db . '</option>';
$table_html .= '<span id="' . $id_attr . '_tables" class="' . $class . '">';
$table_html .= '<select id="' . $id_attr . '_tables_select" '
. 'class="table_alias_select">';
$first_tbl = true;
$col_html = '';
foreach ($tables as $table => $columns) {
$val = '';
if (!empty($aliases[$db]['tables'][$table]['alias'])) {
$val = htmlspecialchars($aliases[$db]['tables'][$table]['alias']);
}
$table = htmlspecialchars($table);
$name_attr = 'aliases[' . $db . '][tables][' . $table . '][alias]';
$id_attr = substr(md5($name_attr), 0, 12);
$class = 'hide';
if ($first_tbl) {
$first_tbl = false;
$class = '';
$table_input_html = '<label class="col-2" for="' . $id_attr . '">'
. __('New table name') . ': </label>';
}
$table_input_html .= '<input type="text" value="' . $val . '" '
. 'name="' . $name_attr . '" id="' . $id_attr . '" '
. 'placeholder="' . $table . ' alias" class="' . $class . '" '
. 'disabled="disabled"/>';
$table_html .= '<option value="' . $id_attr . '">'
. $table . '</option>';
$col_html .= '<table id="' . $id_attr . '_cols" class="'
. $class . '" width="100%">';
$col_html .= '<thead><tr><th>' . __('Old column name') . '</th>'
. '<th>' . __('New column name') . '</th></tr></thead><tbody>';
foreach ($columns as $column => $col_def) {
$val = '';
if (!empty($aliases[$db]['tables'][$table]['columns'][$column])) {
$val = htmlspecialchars(
$aliases[$db]['tables'][$table]['columns'][$column]
);
}
$column = htmlspecialchars($column);
$name_attr = 'aliases[' . $db . '][tables][' . $table
. '][columns][' . $column . ']';
$id_attr = substr(md5($name_attr), 0, 12);
$col_html .= '<tr>';
$col_html .= '<th><label for="' . $id_attr . '">' . $column
. '</label></th>';
$col_html .= '<td><dummy_inp type="text" name="' . $name_attr . '" '
. 'id="' . $id_attr . '" placeholder="'
. $column . ' alias" value="' . $val . '"></dummy_inp></td>';
$col_html .= '</tr>';
}
$col_html .= '</tbody></table>';
}
$table_html .= '</select>';
$table_html .= $table_input_html . '<hr/>' . $col_html . '</span>';
}
$db_html .= '</select>';
$html .= $db_html;
$html .= $db_input_html . '<hr/>';
$html .= $table_html;
$html .= PMA_getHtmlForCurrentAlias();
$html .= Template::get('export/alias_add')->render();
$html .= '</div>';
return $html;

View File

@ -0,0 +1,54 @@
<table>
<thead>
<tr>
<th colspan="4"><?= __('Define new aliases') ?></th>
</tr>
</thead>
<tr>
<td>
<label><?= __('Select database:') ?></label>
</td>
<td>
<select id="db_alias_select"><option value=""></option></select>
</td>
<td>
<input id="db_alias_name" placeholder="<?= __('New database name') ?>" disabled="1" />
</td>
<td>
<button id="db_alias_button" class="ui-button ui-corner-all ui-widget" disabled="1"><?= __('Add') ?></button>
</td>
</tr>
<tr>
<td>
<label><?= __('Select table:') ?></label>
</td>
<td>
<select id="table_alias_select"><option value=""></option></select>
</td>
<td>
<input id="table_alias_name" placeholder="<?= __('New table name') ?>" disabled="1" />
</td>
<td>
<button id="table_alias_button" class="ui-button ui-corner-all ui-widget" disabled="1"><?= __('Add') ?></button>
</td>
</tr>
<tr>
<td>
<label><?= __('Select column:') ?></label>
</td>
<td>
<select id="column_alias_select"><option value=""></option></select>
</td>
<td>
<input id="column_alias_name" placeholder="<?= __('New column name') ?>" disabled="1" />
</td>
<td>
<button id="column_alias_button" class="ui-button ui-corner-all ui-widget" disabled="1"><?= __('Add') ?></button>
</td>
</tr>
</table>

View File

@ -0,0 +1,10 @@
<tr>
<th><?= htmlspecialchars($type) ?></th>
<td><?= htmlspecialchars($name) ?></td>
<td>
<input name="<?= htmlspecialchars($field) ?>" value="<?= htmlspecialchars($value) ?>" required="true" type="text" />
</td>
<td>
<button class="alias_remove ui-button ui-corner-all ui-widget"><?= __('Remove') ?></button>
</td>
</tr>

View File

@ -235,10 +235,6 @@ class PMA_DisplayExport_Test extends PHPUnit_Framework_TestCase
'New table name',
$html
);
$this->assertContains(
'test_column',
$html
);
//validate 6: PMA_getHtmlForExportOptionsOutput
$this->assertContains(
@ -297,35 +293,7 @@ class PMA_DisplayExport_Test extends PHPUnit_Framework_TestCase
. 'Rename exported databases/tables/columns">',
$html
);
$this->assertContains(
'test\'_db',
$html
);
$this->assertContains(
'test_&lt;b&gt;table',
$html
);
$this->assertContains(
'col&lt;2',
$html
);
$this->assertContains(
'co&quot;l1',
$html
);
$this->assertContains(
'<hr/>',
$html
);
$name_attr = 'aliases[test\'_db][tables][test_&lt;b&gt;table][alias]';
$id_attr = mb_substr(md5($name_attr), 0, 12);
$this->assertContains(
'<input type="text" value="" name="' . $name_attr . '" '
. 'id="' . $id_attr . '" placeholder="'
. 'test_&lt;b&gt;table alias" class="" disabled="disabled"/>',
$html
);
$this->assertContains('<button class="alias_remove', $html);
}
}

View File

@ -259,7 +259,7 @@ button.mult_submit {
/* odd items 1,3,5,7,... */
table tr:nth-child(odd) {
background: <?php echo $GLOBALS['cfg']['BgOne']; ?>;
background: <?php echo $GLOBALS['cfg']['BgOne']; ?>;
}
/* even items 2,4,6,8,... */
@ -2679,19 +2679,8 @@ table.show_create td {
border-right: 1px solid #bbb;
}
#alias_modal table th {
vertical-align: middle;
padding-left: 1em;
}
#alias_modal label.col-2 {
min-width: 20%;
display: inline-block;
}
#alias_modal select {
width: 25%;
margin-right: 2em;
#alias_modal table {
width: 100%;
}
#alias_modal label {

View File

@ -542,7 +542,7 @@ button.mult_submit {
table tbody:first-of-type tr:nth-child(odd),
table tbody:first-of-type tr:nth-child(odd) th {
background: #fff;
}
}
/* even items 2,4,6,8,... */
table tbody:first-of-type tr:nth-child(even),
@ -2979,19 +2979,8 @@ table.show_create td {
border-right: 1px solid #bbb;
}
#alias_modal table th {
vertical-align: middle;
padding-left: 1em;
}
#alias_modal label.col-2 {
min-width: 20%;
display: inline-block;
}
#alias_modal select {
width: 25%;
margin-right: 2em;
#alias_modal table {
width: 100%;
}
#alias_modal label {