From 3ea7d2b52d8a48a9fdb2b59afb5c97f998a2ccd5 Mon Sep 17 00:00:00 2001 From: Chirayu Chiripal Date: Wed, 11 Jun 2014 22:44:55 +0530 Subject: [PATCH] rfe-755: export with table/column name changes Signed-off-by: Chirayu Chiripal --- ChangeLog | 1 + export.php | 25 +- js/export.js | 87 +++++++ libraries/display_export.lib.php | 125 ++++++++- libraries/export.lib.php | 124 ++++++--- libraries/plugins/export/ExportSql.class.php | 252 ++++++++++++++----- libraries/sqlparser.data.php | 4 + test/libraries/PMA_display_export_test.php | 116 ++++++++- test/libraries/PMA_export_test.php | 110 ++++++++ themes/original/css/common.css.php | 23 ++ themes/pmahomme/css/common.css.php | 23 ++ 11 files changed, 794 insertions(+), 96 deletions(-) create mode 100644 test/libraries/PMA_export_test.php diff --git a/ChangeLog b/ChangeLog index cf2ec854c6..e8f00a4d45 100644 --- a/ChangeLog +++ b/ChangeLog @@ -15,6 +15,7 @@ phpMyAdmin - ChangeLog + rfe #1145 Preview SQL instead of executing it + rfe #759 Use aliases in SQL export for tables and columns - bug #4450 Query is duplicated on Ctrl+Enter ++ rfe #755 Export with table/column name changes 4.2.4.0 (not yet released) - bug #4449 Mediawiki export does not produce table header row; also fix related PHP warnings diff --git a/export.php b/export.php index 215d494032..b1c80789bc 100644 --- a/export.php +++ b/export.php @@ -126,6 +126,7 @@ if (!defined('TESTSUITE')) { 'sql_hex_for_binary', 'sql_utc_time', 'sql_drop_database', + 'sql_views_as_tables', 'csv_separator', 'csv_enclosed', 'csv_escaped', @@ -147,7 +148,8 @@ if (!defined('TESTSUITE')) { 'latex_data_caption', 'latex_data_continued_caption', 'latex_data_label', - 'latex_null' + 'latex_null', + 'aliases' ); foreach ($post_params as $one_post_param) { @@ -241,6 +243,18 @@ if (!defined('TESTSUITE')) { PMA_fatalError(__('Bad parameters!')); } + // Merge SQL Query aliases with Export aliases from + // export page, Export page aliases are given more + // preference over SQL Query aliases. + if (!empty($_REQUEST['aliases'])) { + $aliases = PMA_mergeAliases( + PMA_SQP_getAliasesFromQuery($sql_query, $db), + $_REQUEST['aliases'] + ); + } else { + $aliases = PMA_SQP_getAliasesFromQuery($sql_query, $db); + } + /** * Increase time limit for script execution and initializes some variables */ @@ -370,12 +384,14 @@ if (!defined('TESTSUITE')) { } PMA_exportServer( $db_select, $whatStrucOrData, $export_plugin, $crlf, $err_url, - $export_type, $do_relation, $do_comments, $do_mime, $do_dates + $export_type, $do_relation, $do_comments, $do_mime, $do_dates, + $aliases ); } elseif ($export_type == 'database') { PMA_exportDatabase( $db, $tables, $whatStrucOrData, $export_plugin, $crlf, $err_url, - $export_type, $do_relation, $do_comments, $do_mime, $do_dates + $export_type, $do_relation, $do_comments, $do_mime, $do_dates, + $aliases ); } else { // We export just one table @@ -392,8 +408,7 @@ if (!defined('TESTSUITE')) { PMA_exportTable( $db, $table, $whatStrucOrData, $export_plugin, $crlf, $err_url, $export_type, $do_relation, $do_comments, $do_mime, $do_dates, - $allrows, $limit_to, $limit_from, $sql_query, - PMA_SQP_getAliasesFromQuery($sql_query, $db) + $allrows, $limit_to, $limit_from, $sql_query, $aliases ); } if (! $export_plugin->exportFooter()) { diff --git a/js/export.js b/js/export.js index 2f80e1518b..012d618069 100644 --- a/js/export.js +++ b/js/export.js @@ -42,6 +42,9 @@ AJAX.registerTeardown('export.js', function () { $("#plugins").unbind('change'); $("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'); }); AJAX.registerOnload('export.js', function () { @@ -246,6 +249,73 @@ function check_time_out(time_limit) }, time_limit * 1000); } + +/** + * Handler for Database/table alias select + * + * @param object event the event object + * + * @return void + */ +function aliasSelectHandler(event) { + var sel = event.data.sel; + var type = event.data.type; + var inputId = $(this).val(); + var $label = $(this).next('label'); + $('input#' + $label.attr('for')).addClass('hide'); + $('input#' + inputId).removeClass('hide'); + $label.attr('for', inputId); + //alert('#' + $label.attr('for')); + $('#alias_modal ' + sel + '[id$=' + type + ']:visible').addClass('hide'); + $('#alias_modal ' + sel + '#' + inputId + type).removeClass('hide'); + $("#alias_modal").dialog("option", "position", "center"); +} + +/** + * Handler for Alias dialog box + * + * @param object event the event object + * + * @return void + */ + function createAliasModal(event) { + event.preventDefault(); + $('#alias_modal').dialog({ + width: Math.min($(window).width() - 100, 700), + modal: true, + dialogClass: "alias-dialog", + buttons: { + 'Reset All': function() { + $(this).find('input[type="text"]').val(''); + }, + 'Reset': function() { + $(this).find('input[type="text"]:visible').val(''); + }, + 'Save & Close': function() { + $(this).dialog("close"); + $('#alias_modal').parent().appendTo($('form[name="dump"]')); + } + }, + create: function() { + $(this).css('maxHeight', $(window).height() - 150); + $('.alias-dialog .ui-dialog-titlebar-close').remove(); + }, + 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 + if ($(this).val()) { + isEmpty = false; + } + }); + $('input#btn_alias_config').attr('checked', !isEmpty); + }, + position: 'center' + }); + } + AJAX.registerOnload('export.js', function () { $("input[type='radio'][name='quick_or_custom']").change(toggle_quick_or_custom); @@ -278,4 +348,21 @@ AJAX.registerOnload('export.js', function () { disable_dump_some_rows_sub_options(); } }); + + // 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 + ); }); diff --git a/libraries/display_export.lib.php b/libraries/display_export.lib.php index 01dee658ae..9b48b886df 100644 --- a/libraries/display_export.lib.php +++ b/libraries/display_export.lib.php @@ -634,6 +634,10 @@ function PMA_getHtmlForExportOptionsOutput($export_type) $html = '
'; $html .= '

' . __('Output:') . '

'; $html .= '
    '; + $html .= '
  • '; + $html .= '
  • '; $html .= '
  • '; $html .= '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 .= '' + . ''; + $class = 'odd'; + foreach ($columns as $column => $col_def) { + $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 .= ''; + $class = $class === 'odd' ? 'even' : 'odd'; + } + $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 .= '
    '; + return $html; +} ?> diff --git a/libraries/export.lib.php b/libraries/export.lib.php index 41d2315977..cd7b0d7f2d 100644 --- a/libraries/export.lib.php +++ b/libraries/export.lib.php @@ -457,12 +457,14 @@ function PMA_getHtmlForDisplayedExportHeader($export_type, $db, $table) * @param string $do_comments whether to add comments * @param string $do_mime whether to add MIME info * @param string $do_dates whether to add dates + * @param array $aliases Alias information for db/table/column * * @return void */ function PMA_exportServer( $db_select, $whatStrucOrData, $export_plugin, $crlf, $err_url, - $export_type, $do_relation, $do_comments, $do_mime, $do_dates + $export_type, $do_relation, $do_comments, $do_mime, $do_dates, + $aliases ) { if (! empty($db_select)) { $tmp_select = implode($db_select, '|'); @@ -477,7 +479,7 @@ function PMA_exportServer( PMA_exportDatabase( $current_db, $tables, $whatStrucOrData, $export_plugin, $crlf, $err_url, $export_type, $do_relation, $do_comments, $do_mime, - $do_dates + $do_dates, $aliases ); } } // end foreach database @@ -497,17 +499,21 @@ function PMA_exportServer( * @param string $do_comments whether to add comments * @param string $do_mime whether to add MIME info * @param string $do_dates whether to add dates + * @param array $aliases Alias information for db/table/column * * @return void */ function PMA_exportDatabase( $db, $tables, $whatStrucOrData, $export_plugin, $crlf, $err_url, - $export_type, $do_relation, $do_comments, $do_mime, $do_dates + $export_type, $do_relation, $do_comments, $do_mime, $do_dates, + $aliases ) { - if (! $export_plugin->exportDBHeader($db)) { + $db_alias = !empty($aliases[$db]['alias']) + ? $aliases[$db]['alias'] : ''; + if (! $export_plugin->exportDBHeader($db, $db_alias)) { return; } - if (! $export_plugin->exportDBCreate($db)) { + if (! $export_plugin->exportDBCreate($db, $db_alias)) { return; } @@ -515,7 +521,7 @@ function PMA_exportDatabase( && strpos($GLOBALS['sql_structure_or_data'], 'structure') !== false && isset($GLOBALS['sql_procedure_function']) ) { - $export_plugin->exportRoutines($db); + $export_plugin->exportRoutines($db, $aliases); } $views = array(); @@ -537,9 +543,9 @@ function PMA_exportDatabase( if (isset($GLOBALS['sql_create_view'])) { if (! $export_plugin->exportStructure( - $db, $table, $crlf, $err_url, - 'stand_in', $export_type, - $do_relation, $do_comments, $do_mime, $do_dates + $db, $table, $crlf, $err_url, 'stand_in', + $export_type, $do_relation, $do_comments, + $do_mime, $do_dates, $aliases )) { break 1; } @@ -566,9 +572,9 @@ function PMA_exportDatabase( } if (! $export_plugin->exportStructure( - $db, $table, $crlf, $err_url, - 'create_table', $export_type, - $do_relation, $do_comments, $do_mime, $do_dates + $db, $table, $crlf, $err_url, 'create_table', + $export_type, $do_relation, $do_comments, + $do_mime, $do_dates, $aliases )) { break 1; } @@ -584,7 +590,7 @@ function PMA_exportDatabase( $local_query = 'SELECT * FROM ' . PMA_Util::backquote($db) . '.' . PMA_Util::backquote($table); if (! $export_plugin->exportData( - $db, $table, $crlf, $err_url, $local_query + $db, $table, $crlf, $err_url, $local_query, $aliases )) { break 1; } @@ -595,9 +601,9 @@ function PMA_exportDatabase( || $whatStrucOrData == 'structure_and_data') ) { if (! $export_plugin->exportStructure( - $db, $table, $crlf, $err_url, - 'triggers', $export_type, - $do_relation, $do_comments, $do_mime, $do_dates + $db, $table, $crlf, $err_url, 'triggers', + $export_type, $do_relation, $do_comments, + $do_mime, $do_dates, $aliases )) { break 1; } @@ -612,9 +618,9 @@ function PMA_exportDatabase( || $whatStrucOrData == 'structure_and_data' ) { if (! $export_plugin->exportStructure( - $db, $view, $crlf, $err_url, - 'create_view', $export_type, - $do_relation, $do_comments, $do_mime, $do_dates + $db, $view, $crlf, $err_url, 'create_view', + $export_type, $do_relation, $do_comments, + $do_mime, $do_dates, $aliases )) { break 1; } @@ -623,7 +629,7 @@ function PMA_exportDatabase( } - if (! $export_plugin->exportDBFooter($db)) { + if (! $export_plugin->exportDBFooter($db, $db_alias)) { return; } } @@ -655,7 +661,9 @@ function PMA_exportTable( $export_type, $do_relation, $do_comments, $do_mime, $do_dates, $allrows, $limit_to, $limit_from, $sql_query, $aliases ) { - if (! $export_plugin->exportDBHeader($db)) { + $db_alias = !empty($aliases[$db]['alias']) + ? $aliases[$db]['alias'] : ''; + if (! $export_plugin->exportDBHeader($db, $db_alias)) { return; } if (isset($allrows) @@ -679,9 +687,9 @@ function PMA_exportTable( if (isset($GLOBALS['sql_create_view'])) { if (! $export_plugin->exportStructure( - $db, $table, $crlf, $err_url, - 'create_view', $export_type, - $do_relation, $do_comments, $do_mime, $do_dates + $db, $table, $crlf, $err_url, 'create_view', + $export_type, $do_relation, $do_comments, + $do_mime, $do_dates, $aliases )) { return; } @@ -690,9 +698,9 @@ function PMA_exportTable( } else if (isset($GLOBALS['sql_create_table'])) { if (! $export_plugin->exportStructure( - $db, $table, $crlf, $err_url, - 'create_table', $export_type, - $do_relation, $do_comments, $do_mime, $do_dates, $aliases + $db, $table, $crlf, $err_url, 'create_table', + $export_type, $do_relation, $do_comments, + $do_mime, $do_dates, $aliases )) { return; } @@ -731,14 +739,14 @@ function PMA_exportTable( || $whatStrucOrData == 'structure_and_data') ) { if (! $export_plugin->exportStructure( - $db, $table, $crlf, $err_url, - 'triggers', $export_type, - $do_relation, $do_comments, $do_mime, $do_dates, $aliases + $db, $table, $crlf, $err_url, 'triggers', + $export_type, $do_relation, $do_comments, + $do_mime, $do_dates, $aliases )) { return; } } - if (! $export_plugin->exportDBFooter($db)) { + if (! $export_plugin->exportDBFooter($db, $db_alias)) { return; } } @@ -764,4 +772,58 @@ function PMA_showExportPage($export_type) } exit(); } + +/** + * Merge two alias arrays, if array1 and array2 have + * conflicting alias then array2 value is used if it + * is non empty otherwise array1 value. + * + * @param array $aliases1 first array of aliases + * @param array $aliases2 second array of aliases + * + * @return array resultant merged aliases info + */ +function PMA_mergeAliases($aliases1, $aliases2) +{ + // First do a recursive array merge + // on aliases arrays. + $aliases = array_merge_recursive($aliases1, $aliases2); + // Now, resolve conflicts in aliases, if any + foreach ($aliases as $db_name => $db) { + // If alias key is an array then + // it is a merge conflict. + if (isset($db['alias']) && is_array($db['alias'])) { + $val1 = $db['alias'][0]; + $val2 = $db['alias'][1]; + // Use aliases2 alias if non empty + $aliases[$db_name]['alias'] + = empty($val2) ? $val1 : $val2; + } + if (!isset($db['tables'])) { + continue; + } + foreach ($db['tables'] as $tbl_name => $tbl) { + if (isset($tbl['alias']) && is_array($tbl['alias'])) { + $val1 = $tbl['alias'][0]; + $val2 = $tbl['alias'][1]; + // Use aliases2 alias if non empty + $aliases[$db_name]['tables'][$tbl_name]['alias'] + = empty($val2) ? $val1 : $val2; + } + if (!isset($tbl['columns'])) { + continue; + } + foreach ($tbl['columns'] as $col => $col_as) { + if (isset($col_as) && is_array($col_as)) { + $val1 = $col_as[0]; + $val2 = $col_as[1]; + // Use aliases2 alias if non empty + $aliases[$db_name]['tables'][$tbl_name]['columns'][$col] + = empty($val2) ? $val1 : $val2; + } + }; + }; + } + return $aliases; +} ?> diff --git a/libraries/plugins/export/ExportSql.class.php b/libraries/plugins/export/ExportSql.class.php index 42a0ac0995..36b0125efd 100644 --- a/libraries/plugins/export/ExportSql.class.php +++ b/libraries/plugins/export/ExportSql.class.php @@ -475,14 +475,18 @@ class ExportSql extends ExportPlugin /** * Exports routines (procedures and functions) * - * @param string $db Database + * @param string $db Database + * @param array $aliases Aliases of db/table/columns * * @return bool Whether it succeeded */ - public function exportRoutines($db) + public function exportRoutines($db, $aliases = array()) { global $crlf; + $db_alias = $db; + $this->initAlias($aliases, $db_alias); + $text = ''; $delimiter = '$$'; @@ -500,17 +504,31 @@ class ExportSql extends ExportPlugin $this->_exportComment() . $this->_exportComment(__('Procedures')) . $this->_exportComment(); - + $used_alias = false; + $proc_query = ''; foreach ($procedure_names as $procedure_name) { if (! empty($GLOBALS['sql_drop_table'])) { - $text .= 'DROP PROCEDURE IF EXISTS ' + $proc_query .= 'DROP PROCEDURE IF EXISTS ' . PMA_Util::backquote($procedure_name) . $delimiter . $crlf; } - $text .= $GLOBALS['dbi'] - ->getDefinition($db, 'PROCEDURE', $procedure_name) - . $delimiter . $crlf . $crlf; + $create_query = $GLOBALS['dbi'] + ->getDefinition($db, 'PROCEDURE', $procedure_name); + $create_query = $this->replaceWithAliases( + $create_query, $aliases, $db, '', $flag + ); + // One warning per database + if ($flag) { + $used_alias = true; + } + $proc_query .= $create_query . $delimiter . $crlf . $crlf; } + if ($used_alias) { + $text .= $this->_exportComment(__('It appears your database uses procedures;')) + . $this->_exportComment(__('alias export may not work reliably in all cases.')) + . $this->_exportComment(); + } + $text .= $proc_query; } if ($function_names) { @@ -518,17 +536,31 @@ class ExportSql extends ExportPlugin $this->_exportComment() . $this->_exportComment(__('Functions')) . $this->_exportComment(); - + $used_alias = false; + $function_query = ''; foreach ($function_names as $function_name) { if (! empty($GLOBALS['sql_drop_table'])) { - $text .= 'DROP FUNCTION IF EXISTS ' + $function_query .= 'DROP FUNCTION IF EXISTS ' . PMA_Util::backquote($function_name) . $delimiter . $crlf; } - $text .= $GLOBALS['dbi'] - ->getDefinition($db, 'FUNCTION', $function_name) - . $delimiter . $crlf . $crlf; + $create_query = $GLOBALS['dbi'] + ->getDefinition($db, 'FUNCTION', $function_name); + $create_query = $this->replaceWithAliases( + $create_query, $aliases, $db, '', $flag + ); + // One warning per database + if ($flag) { + $used_alias = true; + } + $function_query .= $create_query . $delimiter . $crlf . $crlf; } + if ($used_alias) { + $text .= $this->_exportComment(__('It appears your database uses functions;')) + . $this->_exportComment(__('alias export may not work reliably in all cases.')) + . $this->_exportComment(); + } + $text .= $function_query; } if ($procedure_names || $function_names) { @@ -760,7 +792,7 @@ class ExportSql extends ExportPlugin $create_query = 'CREATE DATABASE IF NOT EXISTS ' . (isset($GLOBALS['sql_backquotes']) ? PMA_Util::backquoteCompat($db_alias, $compat) : $db_alias); - $collation = PMA_getDbCollation($db_alias); + $collation = PMA_getDbCollation($db); if (PMA_DRIZZLE) { $create_query .= ' COLLATE ' . $collation; } else { @@ -903,18 +935,22 @@ class ExportSql extends ExportPlugin /** * Returns a stand-in CREATE definition to resolve view dependencies * - * @param string $db the database name - * @param string $view the view name - * @param string $crlf the end of line sequence + * @param string $db the database name + * @param string $view the view name + * @param string $crlf the end of line sequence + * @param array $aliases Aliases of db/table/columns * * @return string resulting definition */ - public function getTableDefStandIn($db, $view, $crlf) + public function getTableDefStandIn($db, $view, $crlf, $aliases = array()) { + $db_alias = $db; + $view_alias = $view; + $this->initAlias($aliases, $db_alias, $view_alias); $create_query = ''; if (! empty($GLOBALS['sql_drop_table'])) { $create_query .= 'DROP VIEW IF EXISTS ' - . PMA_Util::backquote($view) + . PMA_Util::backquote($view_alias) . ';' . $crlf; } @@ -925,14 +961,18 @@ class ExportSql extends ExportPlugin ) { $create_query .= 'IF NOT EXISTS '; } - $create_query .= PMA_Util::backquote($view) . ' (' . $crlf; + $create_query .= PMA_Util::backquote($view_alias) . ' (' . $crlf; $tmp = array(); $columns = $GLOBALS['dbi']->getColumnsFull($db, $view); foreach ($columns as $column_name => $definition) { - $tmp[] = PMA_Util::backquote($column_name) . ' ' . + $col_alias = $column_name; + if (!empty($aliases[$db]['tables'][$view]['columns'][$col_alias])) { + $col_alias = $aliases[$db]['tables'][$view]['columns'][$col_alias]; + } + $tmp[] = PMA_Util::backquote($col_alias) . ' ' . $definition['Type'] . $crlf; } - $create_query .= implode(',', $tmp) . ');'; + $create_query .= implode(',', $tmp) . ');' . $crlf; return($create_query); } @@ -944,6 +984,7 @@ class ExportSql extends ExportPlugin * @param string $crlf the end of line sequence * @param bool $add_semicolon whether to add semicolon and end-of-line at * the end + * @param array $aliases Aliases of db/table/columns * * @return string resulting schema */ @@ -951,24 +992,32 @@ class ExportSql extends ExportPlugin $db, $view, $crlf, - $add_semicolon = true + $add_semicolon = true, + $aliases = array() ) { + $db_alias = $db; + $view_alias = $view; + $this->initAlias($aliases, $db_alias, $view_alias); $create_query = "CREATE TABLE"; if (isset($GLOBALS['sql_if_not_exists'])) { $create_query .= " IF NOT EXISTS "; } - $create_query .= PMA_Util::backquote($view) . "(" . $crlf; + $create_query .= PMA_Util::backquote($view_alias) . "(" . $crlf; $columns = $GLOBALS['dbi']->getColumns($db, $view, null, true); $firstCol = true; foreach ($columns as $column) { + $col_alias = $column['Field']; + if (!empty($aliases[$db]['tables'][$view]['columns'][$col_alias])) { + $col_alias = $aliases[$db]['tables'][$view]['columns'][$col_alias]; + } $extracted_columnspec = PMA_Util::extractColumnSpec($column['Type']); if (! $firstCol) { $create_query .= "," . $crlf; } - $create_query .= " " . PMA_Util::backquote($column['Field']); + $create_query .= " " . PMA_Util::backquote($col_alias); $create_query .= " " . $column['Type']; if ($extracted_columnspec['can_contain_collation'] && ! empty($column['Collation']) @@ -1173,6 +1222,7 @@ class ExportSql extends ExportPlugin return $this->_exportComment(__('in use') . '(' . $tmp_error . ')'); } + $warning = ''; if ($result != false && ($row = $GLOBALS['dbi']->fetchRow($result))) { $create_query = $row[1]; unset($row); @@ -1202,8 +1252,19 @@ class ExportSql extends ExportPlugin } // substitute aliases in create query $create_query = $this->replaceWithAliases( - $create_query, $aliases, $db, $table + $create_query, $aliases, $db, $table, $flag ); + // One warning per view + if ($flag && $view) { + $warning = $this->_exportComment() + . $this->_exportComment( + __('It appears your database uses views;') + ) + . $this->_exportComment( + __('alias export may not work reliably in all cases.') + ) + . $this->_exportComment(); + } // Should we use IF NOT EXISTS? if (isset($GLOBALS['sql_if_not_exists'])) { $create_query = preg_replace( @@ -1505,7 +1566,7 @@ class ExportSql extends ExportPlugin $schema_create .= ($compat != 'MSSQL') ? $auto_increment : ''; $GLOBALS['dbi']->freeResult($result); - return $schema_create . ($add_semicolon ? ';' . $crlf : ''); + return $warning . $schema_create . ($add_semicolon ? ';' . $crlf : ''); } // end of the 'getTableDef()' function /** @@ -1698,16 +1759,33 @@ class ExportSql extends ExportPlugin __('Triggers') . ' ' . $formatted_table_name ) . $this->_exportComment(); + $used_alias = false; + $trigger_query = ''; foreach ($triggers as $trigger) { if (! empty($GLOBALS['sql_drop_table'])) { - $dump .= $trigger['drop'] . ';' . $crlf; + $trigger_query .= $trigger['drop'] . ';' . $crlf; } - $dump .= 'DELIMITER ' . $delimiter . $crlf; - $dump .= $this->replaceWithAliases( - $trigger['create'], $aliases, $db, $table + + $trigger_query .= 'DELIMITER ' . $delimiter . $crlf; + $trigger_query .= $this->replaceWithAliases( + $trigger['create'], $aliases, $db, $table, $flag ); - $dump .= 'DELIMITER ;' . $crlf; + if ($flag) { + $used_alias = true; + } + $trigger_query .= 'DELIMITER ;' . $crlf; } + // One warning per table. + if ($used_alias) { + $dump .= $this->_exportComment( + __('It appears your table uses triggers;') + ) + . $this->_exportComment( + __('alias export may not work reliably in all cases.') + ) + . $this->_exportComment(); + } + $dump .= $trigger_query; } break; case 'create_view': @@ -1722,10 +1800,11 @@ class ExportSql extends ExportPlugin // delete the stand-in table previously created (if any) if ($export_type != 'table') { $dump .= 'DROP TABLE IF EXISTS ' - . PMA_Util::backquote($table) . ';' . $crlf; + . PMA_Util::backquote($table_alias) . ';' . $crlf; } $dump .= $this->getTableDef( - $db, $table, $crlf, $error_url, $dates, true, true + $db, $table, $crlf, $error_url, $dates, + true, true, true, $aliases ); } else { $dump .= @@ -1739,10 +1818,10 @@ class ExportSql extends ExportPlugin // delete the stand-in table previously created (if any) if ($export_type != 'table') { $dump .= 'DROP TABLE IF EXISTS ' - . PMA_Util::backquote($table) . ';' . $crlf; + . PMA_Util::backquote($table_alias) . ';' . $crlf; } $dump .= $this->_getTableDefForView( - $db, $table, $crlf, true + $db, $table, $crlf, true, $aliases ); } break; @@ -1753,7 +1832,7 @@ class ExportSql extends ExportPlugin ) . $this->_exportComment(); // export a stand-in definition to resolve view dependencies - $dump .= $this->getTableDefStandIn($db, $table, $crlf); + $dump .= $this->getTableDefStandIn($db, $table, $crlf, $aliases); } // end switch // this one is built by getTableDef() to use in table copy/move @@ -1837,8 +1916,12 @@ class ExportSql extends ExportPlugin $field_set = array(); for ($j = 0; $j < $fields_cnt; $j++) { + $col_as = $fields_meta[$j]->name; + if (!empty($aliases[$db]['tables'][$table]['columns'][$col_as])) { + $col_as = $aliases[$db]['tables'][$table]['columns'][$col_as]; + } $field_set[$j] = PMA_Util::backquoteCompat( - $fields_meta[$j]->name, + $col_as, $compat, $sql_backquotes ); @@ -2235,11 +2318,14 @@ class ExportSql extends ExportPlugin * @param array $aliases Alias information for db/table/column * @param string $db the database name * @param string $table the tablename + * @param string &$flag the flag denoting whether any replacement was done * * @return string query replaced with aliases */ - public function replaceWithAliases($sql_query, $aliases, $db, $table) - { + public function replaceWithAliases( + $sql_query, $aliases, $db, $table = '', &$flag = null + ) { + $flag = false; // Return original sql query if no aliases are provided. if (!is_array($aliases) || empty($aliases) || empty($sql_query)) { return $sql_query; @@ -2249,7 +2335,10 @@ class ExportSql extends ExportPlugin ); $supported_query_ons = array( 'TABLE' => true, - 'TRIGGER' => true + 'VIEW' => true, + 'TRIGGER' => true, + 'FUNCTION' => true, + 'PROCEDURE' => true ); $identifier_types = array( 'alpha_identifier', @@ -2276,8 +2365,15 @@ class ExportSql extends ExportPlugin for ($i = 0; $i < $size && !$query_end; $i++) { $type = $tokens[$i]['type']; $data = $tokens[$i]['data']; + $data_next = isset($tokens[$i+1]['data']) + ? $tokens[$i+1]['data'] : ''; + $data_prev = ($i > 0) ? $tokens[$i-1]['data'] : ''; $d_unq = PMA_Util::unQuote($data); + $d_unq_next = PMA_Util::unQuote($data_next); + $d_unq_prev = PMA_Util::unQuote($data_prev); $d_upper = strtoupper($d_unq); + $d_upper_next = strtoupper($d_unq_next); + $d_upper_prev = strtoupper($d_unq_prev); $pos = $tokens[$i]['pos'] + $offset; if ($type === 'alpha_reservedWord') { if ($query_type === '' @@ -2295,13 +2391,14 @@ class ExportSql extends ExportPlugin // replace create table name if (!$in_create_table_fields && in_array($type, $identifier_types) - && isset($aliases[$db]['tables'][$table]['alias']) + && !empty($aliases[$db]['tables'][$table]['alias']) ) { $sql_query = $this->substituteAlias( $sql_query, $data, $aliases[$db]['tables'][$table]['alias'], $pos, $offset ); + $flag = true; } elseif ($type === 'punct_bracket_open_round') { // CREATE TABLE fields started if (!$in_create_table_fields) { @@ -2330,15 +2427,24 @@ class ExportSql extends ExportPlugin ) { $table = $d_unq; $ref_table_seen = true; + if (!empty($aliases[$db]['tables'][$table]['alias'])) { + $sql_query = $this->substituteAlias( + $sql_query, $data, + $aliases[$db]['tables'][$table]['alias'], + $pos, $offset + ); + $flag = true; + } // Replace column names } elseif (in_array($type, $identifier_types) - && isset($aliases[$db]['tables'][$table]['columns'][$d_unq]) + && !empty($aliases[$db]['tables'][$table]['columns'][$d_unq]) ) { $sql_query = $this->substituteAlias( $sql_query, $data, $aliases[$db]['tables'][$table]['columns'][$d_unq], $pos, $offset ); + $flag = true; } // CREATE TRIGGER - Alias replacement } elseif ($query_type === 'CREATE' && $query_on === 'TRIGGER') { @@ -2349,7 +2455,7 @@ class ExportSql extends ExportPlugin $on_seen = true; } elseif ($on_seen && in_array($type, $identifier_types)) { if (!$ref_table_seen - && isset($aliases[$db]['tables'][$d_unq]['alias']) + && !empty($aliases[$db]['tables'][$d_unq]['alias']) ) { $ref_table_seen = true; $sql_query = $this->substituteAlias( @@ -2357,25 +2463,49 @@ class ExportSql extends ExportPlugin $aliases[$db]['tables'][$d_unq]['alias'], $pos, $offset ); + $flag = true; } else { - // search for identifiers + // search for identifier alias $alias = $this->getAlias($aliases, $d_unq); if (!empty($alias)) { $sql_query = $this->substituteAlias( $sql_query, $data, $alias, $pos, $offset ); + $flag = true; } } } + // CREATE PROCEDURE|FUNCTION|VIEW - Alias replacement + } elseif ($query_type === 'CREATE' + && ($query_on === 'FUNCTION' + || $query_on === 'PROCEDURE' + || $query_on === 'VIEW') + ) { + // LANGUAGE SQL | (READS|MODIFIES) SQL DATA + // characteristics are skipped + if ($type === 'alpha_identifier' + && (($d_upper === 'LANGUAGE' && $d_upper_next === 'SQL') + || ($d_upper === 'DATA' && $d_upper_prev === 'SQL')) + ) { + continue; + // No need to process further in case of VIEW + // when 'WITH' keyword has been detected + } elseif ($query_on === 'VIEW' + && $type === 'alpha_reservedWord' && $d_upper === 'WITH' + ) { + $query_end = true; + } elseif (in_array($type, $identifier_types)) { + // search for identifier alias + $alias = $this->getAlias($aliases, $d_unq); + if (!empty($alias)) { + $sql_query = $this->substituteAlias( + $sql_query, $data, $alias, $pos, $offset + ); + $flag = true; + }; + } } } - if ($query_type === 'CREATE' && $query_on === 'TRIGGER') { - $warning = $this->_exportComment() - . $this->_exportComment(__('It appears your table uses triggers;')) - . $this->_exportComment(__('alias export may not work reliably in all cases.')) - . $this->_exportComment(); - PMA_exportOutputHandler($warning); - } return $sql_query; } @@ -2418,10 +2548,10 @@ class ExportSql extends ExportPlugin */ public function initAlias($aliases, &$db, &$table = null) { - if (isset($aliases[$db]['tables'][$table]['alias'])) { + if (!empty($aliases[$db]['tables'][$table]['alias'])) { $table = $aliases[$db]['tables'][$table]['alias']; } - if (isset($aliases[$db]['alias'])) { + if (!empty($aliases[$db]['alias'])) { $db = $aliases[$db]['alias']; } } @@ -2431,27 +2561,35 @@ class ExportSql extends ExportPlugin * * @param array $aliases Alias information for db/table/column * @param string $id the identifier to be searched + * @param string $type db/tbl/col or any combination of them + * representing what to be searched * * @return string alias of the identifier if found or '' */ - public function getAlias($aliases, $id) + public function getAlias($aliases, $id, $type = 'dbtblcol') { // search each database foreach ($aliases as $db_key => $db) { // check if id is database and has alias - if ($db_key === $id && !empty($db['alias'])) { + if (stristr($type, 'db') !== false + && $db_key === $id && !empty($db['alias']) + ) { return $db['alias']; } // search each of its tables foreach ($db['tables'] as $table_key => $table) { // check if id is table and has alias - if ($table_key === $id && !empty($table['alias'])) { + if (stristr($type, 'tbl') !== false + && $table_key === $id && !empty($table['alias']) + ) { return $table['alias']; } // search each of its columns foreach ($table['columns'] as $col_key => $col) { // check if id is column - if ($col_key === $id) { + if (stristr($type, 'col') !== false + && $col_key === $id && !empty($col) + ) { return $col; } } diff --git a/libraries/sqlparser.data.php b/libraries/sqlparser.data.php index c70849ebe3..c1379864d6 100644 --- a/libraries/sqlparser.data.php +++ b/libraries/sqlparser.data.php @@ -494,6 +494,7 @@ $PMA_SQPdata_reserved_word = array ( 'INDEXES', 'INFILE', 'INNER', + 'INOUT', 'INSERT', 'INSERT_ID', 'INSERT_METHOD', @@ -541,6 +542,7 @@ $PMA_SQPdata_reserved_word = array ( 'MINUTE_SECOND', 'MIN_ROWS', 'MODE', + 'MODIFIES', 'MODIFY', 'MONTH', 'MRG_MYISAM', @@ -559,6 +561,7 @@ $PMA_SQPdata_reserved_word = array ( 'OPTIONALLY', 'OR', 'ORDER', + 'OUT', 'OUTER', 'OUTFILE', 'PACK_KEYS', @@ -581,6 +584,7 @@ $PMA_SQPdata_reserved_word = array ( 'RAID_TYPE', 'RANGE', // 5.1 'READ', + 'READS', 'READ_ONLY', // 5.1 'READ_WRITE', // 5.1 'REFERENCES', diff --git a/test/libraries/PMA_display_export_test.php b/test/libraries/PMA_display_export_test.php index 845efe211e..ed1eb0c9d1 100644 --- a/test/libraries/PMA_display_export_test.php +++ b/test/libraries/PMA_display_export_test.php @@ -143,6 +143,23 @@ class PMA_DisplayExport_Test extends PHPUnit_Framework_TestCase $single_table = "single_table"; PMA_Table::$cache[$db][$table]['ENGINE'] = "MERGE"; + $columns_info = array( + 'test_column1' => array( + 'COLUMN_NAME' => 'test_column1' + ), + 'test_column2' => array( + 'COLUMN_NAME' => 'test_column2' + ) + ); + $dbi = $this->getMockBuilder('PMA_DatabaseInterface') + ->disableOriginalConstructor() + ->getMock(); + + $dbi->expects($this->any())->method('getColumnsFull') + ->will($this->returnValue($columns_info)); + + $GLOBALS['dbi'] = $dbi; + /* Scan for plugins */ $export_list = PMA_getPlugins( "export", @@ -217,7 +234,34 @@ class PMA_DisplayExport_Test extends PHPUnit_Framework_TestCase $html ); - //validate 5: PMA_getHtmlForExportOptionsOutput + //validate 5: PMA_getHtmlForAliasModalDialog + $this->assertContains( + '
    ', + $html + ); + $this->assertContains( + 'Select database', + $html + ); + $this->assertContains( + 'Select table', + $html + ); + $this->assertContains( + 'New database name', + $html + ); + $this->assertContains( + 'New table name', + $html + ); + $this->assertContains( + 'test_column', + $html + ); + + //validate 6: PMA_getHtmlForExportOptionsOutput $this->assertContains( '
    ', $html @@ -227,7 +271,7 @@ class PMA_DisplayExport_Test extends PHPUnit_Framework_TestCase $html ); - //validate 6: PMA_getHtmlForExportOptionsFormat + //validate 7: PMA_getHtmlForExportOptionsFormat $this->assertContains( '
    ', $html @@ -237,4 +281,72 @@ class PMA_DisplayExport_Test extends PHPUnit_Framework_TestCase $html ); } + + /** + * Test for PMA_getHtmlForAliasModalDialog + * + * @return void + */ + public function testPMAGetHtmlForAliasModalDialog() + { + $columns_info = array( + 'test\'_db' => array( + 'test_table' => array( + 'co"l1' => array( + 'COLUMN_NAME' => 'co"l1' + ), + 'col<2' => array( + 'COLUMN_NAME' => 'col<2' + ) + ) + ) + ); + + $dbi = $this->getMockBuilder('PMA_DatabaseInterface') + ->disableOriginalConstructor() + ->getMock(); + + $dbi->expects($this->any())->method('getColumnsFull') + ->will($this->returnValue($columns_info)); + + $GLOBALS['dbi'] = $dbi; + + $html = PMA_getHtmlForAliasModalDialog(); + + $this->assertContains( + '
    ', + $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 = substr(md5($name_attr), 0, 12); + + $this->assertContains( + '', + $html + ); + } } diff --git a/test/libraries/PMA_export_test.php b/test/libraries/PMA_export_test.php new file mode 100644 index 0000000000..cf4e3dc770 --- /dev/null +++ b/test/libraries/PMA_export_test.php @@ -0,0 +1,110 @@ + array( + 'alias' => 'aliastest', + 'tables' => array( + 'foo' => array( + 'alias' => 'foobar', + 'columns' => array( + 'bar' => 'foo', + 'baz' => 'barbaz' + ) + ), + 'bar' => array( + 'alias' => 'foobaz', + 'columns' => array( + 'a' => 'a_alias', + 'b' => 'b' + ) + ) + ) + ) + ); + $aliases2 = array( + 'test_db' => array( + 'alias' => 'test', + 'tables' => array( + 'foo' => array( + 'columns' => array( + 'bar' => 'foobar' + ) + ), + 'baz' => array( + 'columns' => array( + 'a' => 'x' + ) + ) + ) + ) + ); + $expected = array( + 'test_db' => array( + 'alias' => 'test', + 'tables' => array( + 'foo' => array( + 'alias' => 'foobar', + 'columns' => array( + 'bar' => 'foobar', + 'baz' => 'barbaz' + ) + ), + 'bar' => array( + 'alias' => 'foobaz', + 'columns' => array( + 'a' => 'a_alias', + 'b' => 'b' + ) + ), + 'baz' => array( + 'columns' => array( + 'a' => 'x' + ) + ) + ) + ) + ); + $actual = PMA_mergeAliases($aliases1, $aliases2); + $this->assertEquals($expected, $actual); + } +} diff --git a/themes/original/css/common.css.php b/themes/original/css/common.css.php index 981df77fd5..d5d8490682 100644 --- a/themes/original/css/common.css.php +++ b/themes/original/css/common.css.php @@ -2670,3 +2670,26 @@ table.show_create { 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 label { + font-weight: bold; +} + +.ui-dialog { + position: fixed; +} diff --git a/themes/pmahomme/css/common.css.php b/themes/pmahomme/css/common.css.php index 27b77ec1e9..e10f2cb774 100644 --- a/themes/pmahomme/css/common.css.php +++ b/themes/pmahomme/css/common.css.php @@ -2966,3 +2966,26 @@ table.show_create { 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 label { + font-weight: bold; +} + +.ui-dialog { + position: fixed; +}