From 358d2fd1f1dca6bfb16fb415e76bb9b0b68f0499 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20=C4=8Ciha=C5=99?= Date: Tue, 13 Jun 2017 16:57:26 +0200 Subject: [PATCH] Rewrite export alias defining MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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ř --- ChangeLog | 1 + ajax.php | 34 ++++ js/export.js | 196 +++++++++++++++++---- js/messages.php | 4 + libraries/display_export.lib.php | 174 +++++++----------- templates/export/alias_add.phtml | 54 ++++++ templates/export/alias_item.phtml | 10 ++ test/libraries/PMA_display_export_test.php | 34 +--- themes/original/css/common.css.php | 17 +- themes/pmahomme/css/common.css.php | 17 +- 10 files changed, 332 insertions(+), 209 deletions(-) create mode 100644 ajax.php create mode 100644 templates/export/alias_add.phtml create mode 100644 templates/export/alias_item.phtml diff --git a/ChangeLog b/ChangeLog index cd57c48228..97cdec7fba 100644 --- a/ChangeLog +++ b/ChangeLog @@ -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 diff --git a/ajax.php b/ajax.php new file mode 100644 index 0000000000..da69d976c7 --- /dev/null +++ b/ajax.php @@ -0,0 +1,34 @@ +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!')); +} diff --git a/js/export.js b/js/export.js index e4d51d41c8..2803800c25 100644 --- a/js/export.js +++ b/js/export.js @@ -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.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.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.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.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.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(''); + }); }); diff --git a/js/messages.php b/js/messages.php index bd807543b1..a7f4c8547b 100644 --- a/js/messages.php +++ b/js/messages.php @@ -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" */ diff --git a/libraries/display_export.lib.php b/libraries/display_export.lib.php index bc80a65078..36f2d77b15 100644 --- a/libraries/display_export.lib.php +++ b/libraries/display_export.lib.php @@ -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 = ''; + + $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 .= '' . $template->render(array( + 'type' => '', 'name' => '', 'field' => 'aliases_new', 'value' => '' + )) . ''; + + return $result . '
' + . __('Defined aliases') + . '
'; +} + +/** + * 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 = '
'; - $db_html = ''; - $db_html .= ''; - $table_html .= ''; - $table_html .= ''; - $table_html .= ''; - $col_html .= ''; - $col_html .= '' - . ''; - 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 .= ''; - $col_html .= ''; - $col_html .= ''; - $col_html .= ''; - } - $col_html .= '
' . __('Old column name') . '' . __('New column name') . '
'; - } - $table_html .= ''; - $table_html .= $table_input_html . '
' . $col_html . '
'; - } - $db_html .= ''; - $html .= $db_html; - $html .= $db_input_html . '
'; - $html .= $table_html; + $html .= PMA_getHtmlForCurrentAlias(); + $html .= Template::get('export/alias_add')->render(); $html .= '
'; return $html; diff --git a/templates/export/alias_add.phtml b/templates/export/alias_add.phtml new file mode 100644 index 0000000000..3f29fa9467 --- /dev/null +++ b/templates/export/alias_add.phtml @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + +
+ + + + + + + +
+ + + + + + + +
diff --git a/templates/export/alias_item.phtml b/templates/export/alias_item.phtml new file mode 100644 index 0000000000..e0da9cce58 --- /dev/null +++ b/templates/export/alias_item.phtml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/test/libraries/PMA_display_export_test.php b/test/libraries/PMA_display_export_test.php index c3c2d7ca84..bad7ca286d 100644 --- a/test/libraries/PMA_display_export_test.php +++ b/test/libraries/PMA_display_export_test.php @@ -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_<b>table', - $html - ); - $this->assertContains( - 'col<2', - $html - ); - $this->assertContains( - 'co"l1', - $html - ); - $this->assertContains( - '
', - $html - ); - $name_attr = 'aliases[test\'_db][tables][test_<b>table][alias]'; - $id_attr = mb_substr(md5($name_attr), 0, 12); - - $this->assertContains( - '', - $html - ); + $this->assertContains('