From 33dfef377f126e20a016bb3feb397c384bdd9910 Mon Sep 17 00:00:00 2001 From: Chirayu Chiripal Date: Mon, 2 Jun 2014 18:28:56 +0530 Subject: [PATCH] RFE-759 Use aliases in SQL export for tables and columns Signed-off-by: Chirayu Chiripal --- ChangeLog | 1 + export.php | 3 +- libraries/export.lib.php | 9 +- libraries/plugins/export/ExportSql.class.php | 395 +++++++++++++++--- libraries/sqlparser.data.php | 5 + libraries/sqlparser.lib.php | 117 +++++- tbl_row_action.php | 18 +- .../plugin/export/PMA_ExportSql_test.php | 247 ++++++++++- test/libraries/PMA_SQL_parser_test.php | 138 +++++- 9 files changed, 815 insertions(+), 118 deletions(-) diff --git a/ChangeLog b/ChangeLog index 65cda74a45..cdc54f5fa3 100644 --- a/ChangeLog +++ b/ChangeLog @@ -13,6 +13,7 @@ phpMyAdmin - ChangeLog + rfe #1527 Charts for data in format + rfe Allow saving query charts as images + rfe #1145 Preview SQL instead of executing it ++ rfe #759 Use aliases in SQL export for tables and columns 4.2.4.0 (not yet released) diff --git a/export.php b/export.php index 248633f2c0..7403adcb80 100644 --- a/export.php +++ b/export.php @@ -391,7 +391,8 @@ 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 + $allrows, $limit_to, $limit_from, $sql_query, + PMA_SQP_getAliasesFromQuery($sql_query, $db) ); } if (! $export_plugin->exportFooter()) { diff --git a/libraries/export.lib.php b/libraries/export.lib.php index e8f6cfc83b..41d2315977 100644 --- a/libraries/export.lib.php +++ b/libraries/export.lib.php @@ -646,13 +646,14 @@ function PMA_exportDatabase( * @param string $limit_to upper limit * @param string $limit_from starting limit * @param string $sql_query query for which exporting is requested + * @param array $aliases Alias information for db/table/column * * @return void */ function 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 + $allrows, $limit_to, $limit_from, $sql_query, $aliases ) { if (! $export_plugin->exportDBHeader($db)) { return; @@ -691,7 +692,7 @@ function PMA_exportTable( if (! $export_plugin->exportStructure( $db, $table, $crlf, $err_url, 'create_table', $export_type, - $do_relation, $do_comments, $do_mime, $do_dates + $do_relation, $do_comments, $do_mime, $do_dates, $aliases )) { return; } @@ -719,7 +720,7 @@ function PMA_exportTable( . '.' . PMA_Util::backquote($table) . $add_query; } if (! $export_plugin->exportData( - $db, $table, $crlf, $err_url, $local_query + $db, $table, $crlf, $err_url, $local_query, $aliases )) { return; } @@ -732,7 +733,7 @@ function PMA_exportTable( if (! $export_plugin->exportStructure( $db, $table, $crlf, $err_url, 'triggers', $export_type, - $do_relation, $do_comments, $do_mime, $do_dates + $do_relation, $do_comments, $do_mime, $do_dates, $aliases )) { return; } diff --git a/libraries/plugins/export/ExportSql.class.php b/libraries/plugins/export/ExportSql.class.php index 9216fddfbc..14d56e5f98 100644 --- a/libraries/plugins/export/ExportSql.class.php +++ b/libraries/plugins/export/ExportSql.class.php @@ -729,14 +729,18 @@ class ExportSql extends ExportPlugin /** * Outputs CREATE DATABASE statement * - * @param string $db Database name + * @param string $db Database name + * @param string $db_alias Aliases of db * * @return bool Whether it succeeded */ - public function exportDBCreate($db) + public function exportDBCreate($db, $db_alias = '') { global $crlf; + if (empty($db_alias)) { + $db_alias = $db; + } if (isset($GLOBALS['sql_compatibility'])) { $compat = $GLOBALS['sql_compatibility']; } else { @@ -746,7 +750,7 @@ class ExportSql extends ExportPlugin if (! PMA_exportOutputHandler( 'DROP DATABASE ' . (isset($GLOBALS['sql_backquotes']) - ? PMA_Util::backquoteCompat($db, $compat) : $db) + ? PMA_Util::backquoteCompat($db_alias, $compat) : $db_alias) . ';' . $crlf )) { return false; @@ -755,8 +759,8 @@ class ExportSql extends ExportPlugin if (isset($GLOBALS['sql_create_database'])) { $create_query = 'CREATE DATABASE IF NOT EXISTS ' . (isset($GLOBALS['sql_backquotes']) - ? PMA_Util::backquoteCompat($db, $compat) : $db); - $collation = PMA_getDbCollation($db); + ? PMA_Util::backquoteCompat($db_alias, $compat) : $db_alias); + $collation = PMA_getDbCollation($db_alias); if (PMA_DRIZZLE) { $create_query .= ' COLLATE ' . $collation; } else { @@ -778,11 +782,11 @@ class ExportSql extends ExportPlugin || PMA_DRIZZLE) ) { $result = PMA_exportOutputHandler( - 'USE ' . PMA_Util::backquoteCompat($db, $compat) + 'USE ' . PMA_Util::backquoteCompat($db_alias, $compat) . ';' . $crlf ); } else { - $result = PMA_exportOutputHandler('USE ' . $db . ';' . $crlf); + $result = PMA_exportOutputHandler('USE ' . $db_alias . ';' . $crlf); } return $result; } else { @@ -793,12 +797,16 @@ class ExportSql extends ExportPlugin /** * Outputs database header * - * @param string $db Database name + * @param string $db Database name + * @param string $db_alias Alias of db * * @return bool Whether it succeeded */ - public function exportDBHeader($db) + public function exportDBHeader($db, $db_alias = '') { + if (empty($db_alias)) { + $db_alias = $db; + } if (isset($GLOBALS['sql_compatibility'])) { $compat = $GLOBALS['sql_compatibility']; } else { @@ -808,8 +816,8 @@ class ExportSql extends ExportPlugin . $this->_exportComment( __('Database:') . ' ' . (isset($GLOBALS['sql_backquotes']) - ? PMA_Util::backquoteCompat($db, $compat) - : '\'' . $db . '\'') + ? PMA_Util::backquoteCompat($db_alias, $compat) + : '\'' . $db_alias . '\'') ) . $this->_exportComment(); return PMA_exportOutputHandler($head); @@ -1007,11 +1015,12 @@ class ExportSql extends ExportPlugin * of error * @param bool $show_dates whether to include creation/ * update/check dates - * @param bool $add_semicolon whether to add semicolon and + * @param bool $add_semicolon whether to add semicolon and * end-of-line at the end * @param bool $view whether we're handling a view * @param bool $update_indexes_increments whether we need to update - * two global variables + * two global variables + * @param array $aliases Aliases of db/table/columns * * @return string resulting schema */ @@ -1023,12 +1032,17 @@ class ExportSql extends ExportPlugin $show_dates = false, $add_semicolon = true, $view = false, - $update_indexes_increments = true + $update_indexes_increments = true, + $aliases = array() ) { global $sql_drop_table, $sql_backquotes, $sql_constraints, $sql_constraints_query, $sql_indexes, $sql_indexes_query, $sql_auto_increments,$sql_drop_foreign_keys; + $db_alias = $db; + $table_alias = $table; + $this->initAlias($aliases, $db_alias, $table_alias); + $schema_create = ''; $auto_increment = ''; $new_crlf = $crlf; @@ -1124,7 +1138,7 @@ class ExportSql extends ExportPlugin // no need to generate a DROP VIEW here, it was done earlier if (! empty($sql_drop_table) && ! PMA_Table::isView($db, $table)) { $schema_create .= 'DROP TABLE IF EXISTS ' - . PMA_Util::backquote($table, $sql_backquotes) . ';' + . PMA_Util::backquote($table_alias, $sql_backquotes) . ';' . $crlf; } @@ -1162,7 +1176,6 @@ class ExportSql extends ExportPlugin if ($result != false && ($row = $GLOBALS['dbi']->fetchRow($result))) { $create_query = $row[1]; unset($row); - // Convert end of line chars to one that we want (note that MySQL // doesn't return query it will accept in all cases) if (strpos($create_query, "(\r\n ")) { @@ -1187,7 +1200,10 @@ class ExportSql extends ExportPlugin $create_query ); } - + // substitute aliases in create query + $create_query = $this->replaceWithAliases( + $create_query, $aliases, $db, $table + ); // Should we use IF NOT EXISTS? if (isset($GLOBALS['sql_if_not_exists'])) { $create_query = preg_replace( @@ -1244,19 +1260,19 @@ class ExportSql extends ExportPlugin . $this->_exportComment( __('Constraints for table') . ' ' - . PMA_Util::backquoteCompat($table, $compat) + . PMA_Util::backquoteCompat($table_alias, $compat) ) . $this->_exportComment(); } $sql_constraints_query .= 'ALTER TABLE ' - . PMA_Util::backquoteCompat($table, $compat) + . PMA_Util::backquoteCompat($table_alias, $compat) . $crlf; $sql_constraints .= 'ALTER TABLE ' - . PMA_Util::backquoteCompat($table, $compat) + . PMA_Util::backquoteCompat($table_alias, $compat) . $crlf; $sql_drop_foreign_keys .= 'ALTER TABLE ' - . PMA_Util::backquoteCompat($db, $compat) . '.' - . PMA_Util::backquoteCompat($table, $compat) + . PMA_Util::backquoteCompat($db_alias, $compat) . '.' + . PMA_Util::backquoteCompat($table_alias, $compat) . $crlf; } //if there are indexes @@ -1288,16 +1304,16 @@ class ExportSql extends ExportPlugin . $this->_exportComment( __('Indexes for table') . ' ' - . PMA_Util::backquoteCompat($table, $compat) + . PMA_Util::backquoteCompat($table_alias, $compat) ) . $this->_exportComment(); } $sql_indexes_query .= 'ALTER TABLE ' - . PMA_Util::backquoteCompat($table, $compat) + . PMA_Util::backquoteCompat($table_alias, $compat) . $crlf; $sql_indexes .= 'ALTER TABLE ' - . PMA_Util::backquoteCompat($table, $compat) + . PMA_Util::backquoteCompat($table_alias, $compat) . $crlf; } if ($update_indexes_increments && preg_match( @@ -1324,12 +1340,12 @@ class ExportSql extends ExportPlugin . $this->_exportComment( __('AUTO_INCREMENT for table') . ' ' - . PMA_Util::backquoteCompat($table, $compat) + . PMA_Util::backquoteCompat($table_alias, $compat) ) . $this->_exportComment(); } $sql_auto_increments .= 'ALTER TABLE ' - . PMA_Util::backquoteCompat($table, $compat) + . PMA_Util::backquoteCompat($table_alias, $compat) . $crlf; } @@ -1500,6 +1516,7 @@ class ExportSql extends ExportPlugin * @param string $crlf end of line sequence * @param bool $do_relation whether to include relation comments * @param bool $do_mime whether to include mime comments + * @param array $aliases Aliases of db/table/columns * * @return string resulting comments */ @@ -1508,10 +1525,15 @@ class ExportSql extends ExportPlugin $table, $crlf, $do_relation = false, - $do_mime = false + $do_mime = false, + $aliases = array() ) { global $cfgRelation, $sql_backquotes; + $db_alias = $db; + $table_alias = $table; + $this->initAlias($aliases, $db_alias, $table_alias); + $schema_create = ''; // Check if we can use Relations @@ -1565,14 +1587,18 @@ class ExportSql extends ExportPlugin . $this->_exportComment() . $this->_exportComment( __('RELATIONS FOR TABLE') . ' ' - . PMA_Util::backquote($table, $sql_backquotes) + . PMA_Util::backquote($table_alias, $sql_backquotes) . ':' ); foreach ($res_rel as $rel_field => $rel) { + $rel_field_alias = !empty( + $aliases[$db]['tables'][$table]['columns'][$rel_field] + ) ? $aliases[$db]['tables'][$table]['columns'][$rel_field] + : $rel_field; $schema_create .= $this->_exportComment( ' ' - . PMA_Util::backquote($rel_field, $sql_backquotes) + . PMA_Util::backquote($rel_field_alias, $sql_backquotes) ) . $this->_exportComment( ' ' @@ -1613,6 +1639,7 @@ class ExportSql extends ExportPlugin * parameter * @param bool $mime whether to include mime comments * @param bool $dates whether to include creation/update/check dates + * @param array $aliases Aliases of db/table/columns * * @return bool Whether it succeeded */ @@ -1626,8 +1653,12 @@ class ExportSql extends ExportPlugin $relation = false, $comments = false, $mime = false, - $dates = false + $dates = false, + $aliases = array() ) { + $db_alias = $db; + $table_alias = $table; + $this->initAlias($aliases, $db_alias, $table_alias); if (isset($GLOBALS['sql_compatibility'])) { $compat = $GLOBALS['sql_compatibility']; } else { @@ -1635,8 +1666,8 @@ class ExportSql extends ExportPlugin } $formatted_table_name = (isset($GLOBALS['sql_backquotes'])) - ? PMA_Util::backquoteCompat($table, $compat) - : '\'' . $table . '\''; + ? PMA_Util::backquoteCompat($table_alias, $compat) + : '\'' . $table_alias . '\''; $dump = $this->_possibleCRLF() . $this->_exportComment(str_repeat('-', 56)) . $this->_possibleCRLF() @@ -1648,12 +1679,18 @@ class ExportSql extends ExportPlugin __('Table structure for table') . ' ' . $formatted_table_name ); $dump .= $this->_exportComment(); - $dump .= $this->getTableDef($db, $table, $crlf, $error_url, $dates); - $dump .= $this->_getTableComments($db, $table, $crlf, $relation, $mime); + $dump .= $this->getTableDef( + $db, $table, $crlf, $error_url, $dates, + true, false, true, $aliases + ); + $dump .= $this->_getTableComments( + $db, $table, $crlf, $relation, $mime, $aliases + ); break; case 'triggers': $dump = ''; - $triggers = $GLOBALS['dbi']->getTriggers($db, $table); + $delimiter = '$$'; + $triggers = $GLOBALS['dbi']->getTriggers($db, $table, $delimiter); if ($triggers) { $dump .= $this->_possibleCRLF() . $this->_exportComment() @@ -1661,13 +1698,14 @@ class ExportSql extends ExportPlugin __('Triggers') . ' ' . $formatted_table_name ) . $this->_exportComment(); - $delimiter = '//'; foreach ($triggers as $trigger) { if (! empty($GLOBALS['sql_drop_table'])) { $dump .= $trigger['drop'] . ';' . $crlf; } $dump .= 'DELIMITER ' . $delimiter . $crlf; - $dump .= $trigger['create']; + $dump .= $this->replaceWithAliases( + $trigger['create'], $aliases, $db, $table + ); $dump .= 'DELIMITER ;' . $crlf; } } @@ -1733,13 +1771,19 @@ class ExportSql extends ExportPlugin * @param string $crlf the end of line sequence * @param string $error_url the url to go back in case of error * @param string $sql_query SQL query for obtaining data + * @param array $aliases Aliases of db/table/columns * * @return bool Whether it succeeded */ - public function exportData($db, $table, $crlf, $error_url, $sql_query) - { + public function exportData( + $db, $table, $crlf, $error_url, $sql_query, $aliases = array() + ) { global $current_row, $sql_backquotes; + $db_alias = $db; + $table_alias = $table; + $this->initAlias($aliases, $db_alias, $table_alias); + if (isset($GLOBALS['sql_compatibility'])) { $compat = $GLOBALS['sql_compatibility']; } else { @@ -1747,8 +1791,8 @@ class ExportSql extends ExportPlugin } $formatted_table_name = (isset($GLOBALS['sql_backquotes'])) - ? PMA_Util::backquoteCompat($table, $compat) - : '\'' . $table . '\''; + ? PMA_Util::backquoteCompat($table_alias, $compat) + : '\'' . $table_alias . '\''; // Do not export data for a VIEW, unless asked to export the view as a table // (For a VIEW, this is called only when exporting a single VIEW) @@ -1768,11 +1812,6 @@ class ExportSql extends ExportPlugin return true; } - // analyze the query to get the true column names, not the aliases - // (this fixes an undefined index, also if Complete inserts - // are used, we did not get the true column name in case of aliases) - $analyzed_sql = PMA_SQP_analyze(PMA_SQP_parse($sql_query)); - $result = $GLOBALS['dbi']->tryQuery( $sql_query, null, PMA_DatabaseInterface::QUERY_UNBUFFERED ); @@ -1798,19 +1837,11 @@ class ExportSql extends ExportPlugin $field_set = array(); for ($j = 0; $j < $fields_cnt; $j++) { - if (isset($analyzed_sql[0]['select_expr'][$j]['column'])) { - $field_set[$j] = PMA_Util::backquoteCompat( - $analyzed_sql[0]['select_expr'][$j]['column'], - $compat, - $sql_backquotes - ); - } else { - $field_set[$j] = PMA_Util::backquoteCompat( - $fields_meta[$j]->name, - $compat, - $sql_backquotes - ); - } + $field_set[$j] = PMA_Util::backquoteCompat( + $fields_meta[$j]->name, + $compat, + $sql_backquotes + ); } if (isset($GLOBALS['sql_type']) @@ -1823,7 +1854,7 @@ class ExportSql extends ExportPlugin } // avoid EOL blank $schema_insert .= PMA_Util::backquoteCompat( - $table, + $table_alias, $compat, $sql_backquotes ) . ' SET'; @@ -1858,7 +1889,7 @@ class ExportSql extends ExportPlugin ) { $truncate = 'TRUNCATE TABLE ' . PMA_Util::backquoteCompat( - $table, + $table_alias, $compat, $sql_backquotes ) . ";"; @@ -1881,7 +1912,7 @@ class ExportSql extends ExportPlugin $fields = implode(', ', $field_set); $schema_insert = $sql_command . $insert_delayed . ' INTO ' . PMA_Util::backquoteCompat( - $table, + $table_alias, $compat, $sql_backquotes ) @@ -1890,7 +1921,7 @@ class ExportSql extends ExportPlugin } else { $schema_insert = $sql_command . $insert_delayed . ' INTO ' . PMA_Util::backquoteCompat( - $table, + $table_alias, $compat, $sql_backquotes ) @@ -1936,7 +1967,7 @@ class ExportSql extends ExportPlugin if (! PMA_exportOutputHandler( 'SET IDENTITY_INSERT ' . PMA_Util::backquoteCompat( - $table, + $table_alias, $compat ) . ' ON ;' . $crlf @@ -2079,7 +2110,7 @@ class ExportSql extends ExportPlugin ) { $outputSucceeded = PMA_exportOutputHandler( $crlf . 'SET IDENTITY_INSERT ' - . PMA_Util::backquoteCompat($table, $compat) + . PMA_Util::backquoteCompat($table_alias, $compat) . ' OFF;' . $crlf ); if (! $outputSucceeded) { @@ -2197,4 +2228,236 @@ class ExportSql extends ExportPlugin return $create_query; } + + /** + * replaces db/table/column names with their aliases + * + * @param string $sql_query SQL query in which aliases are to be substituted + * @param array $aliases Alias information for db/table/column + * @param string $db the database name + * @param string $table the tablename + * + * @return string query replaced with aliases + */ + public function replaceWithAliases($sql_query, $aliases, $db, $table) + { + // Return original sql query if no aliases are provided. + if (!is_array($aliases) || empty($aliases) || empty($sql_query)) { + return $sql_query; + } + $supported_query_types = array( + 'CREATE' => true, + ); + $supported_query_ons = array( + 'TABLE' => true, + 'TRIGGER' => true + ); + $identifier_types = array( + 'alpha_identifier', + 'quote_backtick' + ); + $query_type = ''; + $query_on = ''; + // Adjustment value for each pos value + // of token after replacement + $offset = 0; + $open_braces = 0; + $in_create_table_fields = false; + // flag to force end query parsing + $query_end = false; + // Convert all line feeds to Unix style + $sql_query = str_replace("\r\n", "\n", $sql_query); + $sql_query = str_replace("\r", "\n", $sql_query); + $tokens = PMA_SQP_parse($sql_query); + $ref_seen = false; + $ref_table_seen = false; + $old_table = $table; + $on_seen = false; + $size = $tokens['len']; + for ($i = 0; $i < $size && !$query_end; $i++) { + $type = $tokens[$i]['type']; + $data = $tokens[$i]['data']; + $d_unq = PMA_Util::unQuote($data); + $d_upper = strtoupper($d_unq); + $pos = $tokens[$i]['pos'] + $offset; + if ($type === 'alpha_reservedWord') { + if ($query_type === '' + && !empty($supported_query_types[$d_upper]) + ) { + $query_type = $d_upper; + } elseif ($query_on === '' + && !empty($supported_query_ons[$d_upper]) + ) { + $query_on = $d_upper; + } + } + // CREATE TABLE - Alias replacement + if ($query_type === 'CREATE' && $query_on === 'TABLE') { + // replace create table name + if (!$in_create_table_fields + && in_array($type, $identifier_types) + && isset($aliases[$db]['tables'][$table]['alias']) + ) { + $sql_query = $this->substituteAlias( + $sql_query, $data, + $aliases[$db]['tables'][$table]['alias'], + $pos, $offset + ); + } elseif ($type === 'punct_bracket_open_round') { + // CREATE TABLE fields started + if (!$in_create_table_fields) { + $in_create_table_fields = true; + } + $open_braces++; + } elseif ($type === 'punct_bracket_close_round') { + // end our parsing after last ) + // no columns appear after that + if ($in_create_table_fields && $open_braces === 0) { + $query_end = true; + } + // End of Foreign key reference + if ($ref_seen) { + $ref_seen = $ref_table_seen = false; + $table = $old_table; + } + $open_braces--; + // handles Foreign key references + } elseif ($type === 'alpha_reservedWord' + && $d_upper === 'REFERENCES' + ) { + $ref_seen = true; + } elseif (in_array($type, $identifier_types) + && $ref_seen === true && !$ref_table_seen + ) { + $table = $d_unq; + $ref_table_seen = true; + // Replace column names + } elseif (in_array($type, $identifier_types) + && isset($aliases[$db]['tables'][$table]['columns'][$d_unq]) + ) { + $sql_query = $this->substituteAlias( + $sql_query, $data, + $aliases[$db]['tables'][$table]['columns'][$d_unq], + $pos, $offset + ); + } + // CREATE TRIGGER - Alias replacement + } elseif ($query_type === 'CREATE' && $query_on === 'TRIGGER') { + // Skip till 'ON' in encountered + if (!$on_seen && $type === 'alpha_reservedWord' + && $d_upper === 'ON' + ) { + $on_seen = true; + } elseif ($on_seen && in_array($type, $identifier_types)) { + if (!$ref_table_seen + && isset($aliases[$db]['tables'][$d_unq]['alias']) + ) { + $ref_table_seen = true; + $sql_query = $this->substituteAlias( + $sql_query, $data, + $aliases[$db]['tables'][$d_unq]['alias'], + $pos, $offset + ); + } else { + // search for identifiers + $alias = $this->getAlias($aliases, $d_unq); + if (!empty($alias)) { + $sql_query = $this->substituteAlias( + $sql_query, $data, $alias, $pos, $offset + ); + } + } + } + } + } + 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; + } + + /** + * substitutes alias in query at given position + * Note: pos is the value from PMA_SQP_parse() + offset + * + * @param string $sql_query the SQL query + * @param string $data the data to be replaced + * @param string $alias the replacement + * @param string $pos the position of alias + * @param string &$offset the change in pos occured after substitution + * + * @return string replaced query with alias + */ + public function substituteAlias($sql_query, $data, $alias, $pos, &$offset = null) + { + if (!empty($GLOBALS['sql_backquotes'])) { + $alias = PMA_Util::backquote($alias); + } + $alias_len = $GLOBALS['PMA_String']->strlen($alias); + $data_len = $GLOBALS['PMA_String']->strlen($data); + if (isset($offset)) { + $offset += ($alias_len - $data_len); + } + $sql_query = substr_replace( + $sql_query, $alias, $pos - $data_len, $data_len + ); + return $sql_query; + } + + /** + * initialize aliases + * + * @param array $aliases Alias information for db/table/column + * @param string &$db the database + * @param string &$table the table + * + * @return nothing + */ + public function initAlias($aliases, &$db, &$table = null) + { + if (isset($aliases[$db]['tables'][$table]['alias'])) { + $table = $aliases[$db]['tables'][$table]['alias']; + } + if (isset($aliases[$db]['alias'])) { + $db = $aliases[$db]['alias']; + } + } + + /** + * recursively search for alias of a identifier. + * + * @param array $aliases Alias information for db/table/column + * @param string $id the identifier to be searched + * + * @return string alias of the identifier if found or '' + */ + public function getAlias($aliases, $id) + { + // search each database + foreach ($aliases as $db_key => $db) { + // check if id is database and has alias + if ($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'])) { + return $table['alias']; + } + // search each of its columns + foreach ($table['columns'] as $col_key => $col) { + // check if id is column + if ($col_key === $id) { + return $col; + } + } + } + } + return ''; + } } diff --git a/libraries/sqlparser.data.php b/libraries/sqlparser.data.php index f688e642f5..c70849ebe3 100644 --- a/libraries/sqlparser.data.php +++ b/libraries/sqlparser.data.php @@ -391,6 +391,7 @@ $PMA_SQPdata_reserved_word = array ( 'AUTO_INCREMENT', 'AVG_ROW_LENGTH', 'BACKUP', + 'BEFORE', 'BEGIN', 'BETWEEN', 'BINLOG', @@ -441,7 +442,9 @@ $PMA_SQPdata_reserved_word = array ( 'DUMPFILE', 'DUPLICATE', 'DYNAMIC', + 'EACH', 'ELSE', + 'ELSEIF', 'ENCLOSED', 'END', 'ENGINE', @@ -451,6 +454,7 @@ $PMA_SQPdata_reserved_word = array ( 'EVENTS', 'EXECUTE', 'EXISTS', + 'EXIT', 'EXPLAIN', 'EXTENDED', 'FALSE', @@ -651,6 +655,7 @@ $PMA_SQPdata_reserved_word = array ( 'TO', 'TRAILING', 'TRANSACTIONAL', // 5.1 ? + 'TRIGGER', 'TRUE', 'TRUNCATE', 'TYPE', diff --git a/libraries/sqlparser.lib.php b/libraries/sqlparser.lib.php index 34958928c2..5e348dd1a6 100644 --- a/libraries/sqlparser.lib.php +++ b/libraries/sqlparser.lib.php @@ -54,10 +54,6 @@ if (! isset($mysql_charsets)) { /** * Stores parsed elemented of query to array. * - * Currently we don't need the $pos (token position in query) - * for other purposes than LIMIT clause verification, - * so many calls to this function do not include the 4th parameter - * * @param array &$arr Array to store element * @param string $type Type of element * @param string $data Data (text) of element @@ -303,10 +299,10 @@ function PMA_SQP_parse($sql) $previous_was_quote = $this_was_quote; $this_was_quote = false; - if (($c == "\n")) { + if (($c === "\n")) { $this_was_space = true; $count2++; - PMA_SQP_arrayAdd($sql_array, 'white_newline', '', $arraysize); + PMA_SQP_arrayAdd($sql_array, 'white_newline', "\n", $arraysize, $count2); continue; } @@ -325,7 +321,8 @@ function PMA_SQP_parse($sql) if (($c == '#') || (($count2 + 1 < $len) && ($c == '/') && ($next_c == '*')) || (($count2 + 2 == $len) && ($c == '-') && ($next_c == '-')) - || (($count2 + 2 < $len) && ($c == '-') && ($next_c == '-') && (($GLOBALS['PMA_String']->substr($sql, $count2 + 2, 1) <= ' '))) + || (($count2 + 2 < $len) && ($c == '-') && ($next_c == '-') + && (($GLOBALS['PMA_String']->substr($sql, $count2 + 2, 1) <= ' '))) ) { $count2++; $pos = 0; @@ -351,7 +348,9 @@ function PMA_SQP_parse($sql) $str = $GLOBALS['PMA_String']->substr( $sql, $count1, $count2 - $count1 ); - PMA_SQP_arrayAdd($sql_array, 'comment_' . $type, $str, $arraysize); + PMA_SQP_arrayAdd( + $sql_array, 'comment_' . $type, $str, $arraysize, $count2 + ); continue; } // end if @@ -430,7 +429,9 @@ function PMA_SQP_parse($sql) continue; } elseif (($pos + 1 < $len) && ($GLOBALS['PMA_String']->substr($sql, $pos, 1) == $quotetype) - && ($GLOBALS['PMA_String']->substr($sql, $pos + 1, 1) == $quotetype) + && ($GLOBALS['PMA_String']->substr( + $sql, $pos + 1, 1 + ) == $quotetype) ) { $pos = $pos + 2; continue; @@ -459,7 +460,7 @@ function PMA_SQP_parse($sql) break; } // end switch $data = $GLOBALS['PMA_String']->substr($sql, $count1, $count2 - $count1); - PMA_SQP_arrayAdd($sql_array, $type, $data, $arraysize); + PMA_SQP_arrayAdd($sql_array, $type, $data, $arraysize, $count2); continue; } @@ -483,7 +484,7 @@ function PMA_SQP_parse($sql) } $type = 'punct_bracket_' . $type_type . '_' . $type_style; - PMA_SQP_arrayAdd($sql_array, $type, $c, $arraysize); + PMA_SQP_arrayAdd($sql_array, $type, $c, $arraysize, $count2); continue; } @@ -507,8 +508,10 @@ function PMA_SQP_parse($sql) if ($GLOBALS['PMA_String']->isSqlIdentifier($c, false) || $c == '@' || ($c == '.' - && $GLOBALS['PMA_String']->isDigit($GLOBALS['PMA_String']->substr($sql, $count2 + 1, 1)) - && ($previous_was_space || $previous_was_bracket || $previous_was_listsep)) + && $GLOBALS['PMA_String']->isDigit( + $GLOBALS['PMA_String']->substr($sql, $count2 + 1, 1) + ) && ($previous_was_space || $previous_was_bracket + || $previous_was_listsep)) ) { /* DEBUG echo $GLOBALS['PMA_String']->substr($sql, $count2); @@ -564,7 +567,12 @@ function PMA_SQP_parse($sql) unset($pos); } - while (($count2 < $len) && $GLOBALS['PMA_String']->isSqlIdentifier($GLOBALS['PMA_String']->substr($sql, $count2, 1), ($is_sql_variable || $is_digit))) { + while (($count2 < $len) + && $GLOBALS['PMA_String']->isSqlIdentifier( + $GLOBALS['PMA_String']->substr($sql, $count2, 1), + ($is_sql_variable || $is_digit) + ) + ) { $c2 = $GLOBALS['PMA_String']->substr($sql, $count2, 1); if ($is_sql_variable && ($c2 == '.')) { $count2++; @@ -640,7 +648,13 @@ function PMA_SQP_parse($sql) // Checks for punct if ($GLOBALS['PMA_String']->strpos($allpunct_list, $c) !== false) { - while (($count2 < $len) && $GLOBALS['PMA_String']->strpos($allpunct_list, $GLOBALS['PMA_String']->substr($sql, $count2, 1)) !== false) { + while (($count2 < $len) + && $GLOBALS['PMA_String']->strpos( + $allpunct_list, $GLOBALS['PMA_String']->substr( + $sql, $count2, 1 + ) + ) !== false + ) { $count2++; } $l = $count2 - $count1; @@ -677,13 +691,15 @@ function PMA_SQP_parse($sql) break; } PMA_SQP_arrayAdd( - $sql_array, 'punct' . $t_suffix, $punct_data, $arraysize + $sql_array, 'punct' . $t_suffix, $punct_data, $arraysize, $count2 ); } elseif ($punct_data == $GLOBALS['sql_delimiter'] || isset($allpunct_list_pair[$punct_data]) ) { // Ok, we have one of the valid combined punct expressions - PMA_SQP_arrayAdd($sql_array, 'punct', $punct_data, $arraysize); + PMA_SQP_arrayAdd( + $sql_array, 'punct', $punct_data, $arraysize, $count2 + ); } else { // Bad luck, lets split it up more $first = $punct_data[0]; @@ -694,7 +710,11 @@ function PMA_SQP_parse($sql) ) { $count2 = $count1 + 1; $punct_data = $first; - } elseif (($last2 == '/*') || (($last2 == '--') && ($count2 == $len || $GLOBALS['PMA_String']->substr($sql, $count2, 1) <= ' '))) { + } elseif (($last2 == '/*') + || (($last2 == '--') + && ($count2 == $len + || $GLOBALS['PMA_String']->substr($sql, $count2, 1) <= ' ')) + ) { $count2 -= 2; $punct_data = $GLOBALS['PMA_String']->substr( $sql, $count1, $count2 - $count1 @@ -716,7 +736,9 @@ function PMA_SQP_parse($sql) PMA_SQP_throwError($debugstr, $sql); return $sql_array; } - PMA_SQP_arrayAdd($sql_array, 'punct', $punct_data, $arraysize); + PMA_SQP_arrayAdd( + $sql_array, 'punct', $punct_data, $arraysize, $count2 + ); continue; } // end if... elseif... else continue; @@ -1302,7 +1324,8 @@ function PMA_SQP_analyze($arr) if ($subresult['querytype'] == 'SELECT' && ! $in_group_concat - && ! ($seen_subquery && $arr[$i - 1]['type'] == 'punct_bracket_close_round') + && ! ($seen_subquery + && $arr[$i - 1]['type'] == 'punct_bracket_close_round') ) { if (!$seen_from) { if ($previous_was_identifier && isset($chain)) { @@ -2833,7 +2856,6 @@ function PMA_SQP_isKeyWord($column) return in_array(strtoupper($column), $PMA_SQPdata_forbidden_word); } - /** * Get Parser Data Map from sqlparser.data.php * @@ -2850,6 +2872,7 @@ function PMA_SQP_getParserDataMap() 'PMA_SQPdata_column_type' => $PMA_SQPdata_column_type, ); } + /** * Get Parser analyze Map from parse_analyze_inc.php * @@ -2864,4 +2887,56 @@ function PMA_SQP_getParserAnalyzeMap($sql_query, $db) return $analyzed_sql_results; } +/** + * Get Aliases from select query + * Note: only useful for select query on single table. + * + * @param string $select_query The Select SQL Query + * @param string $db Current DB + * + * @return Array alias information from select query + */ +function PMA_SQP_getAliasesFromQuery($select_query, $db) +{ + if (empty($select_query) || empty($db)) { + return array(); + } + $analyzed_sql = PMA_SQP_analyze(PMA_SQP_parse($select_query)); + $aliases = array( + $db => array( + 'alias' => null, + 'tables' => array() + ) + ); + foreach ($analyzed_sql[0]['table_ref'] as $table) { + $t_db = !empty($table['db']) ? $table['db'] : $db; + if (!isset($aliases[$t_db])) { + $aliases[$t_db] = array( + 'alias' => null, + 'tables' => array() + ); + } + $aliases[$t_db]['tables'][$table['table_true_name']] = array( + 'alias' => !empty($table['table_alias']) + ? $table['table_alias'] : null, + 'columns' => array() + ); + } + foreach ($analyzed_sql[0]['select_expr'] as $cols) { + if (!empty($cols['alias'])) { + $t_db = !empty($cols['db']) ? $cols['db'] : $db; + if (!empty($cols['table_true_name'])) { + $aliases[$t_db]['tables'][$cols['table_true_name']] + ['columns'][$cols['column']] = $cols['alias']; + } else { + foreach ($aliases[$t_db]['tables'] as $key => $table) { + $aliases[$t_db]['tables'][$key] + ['columns'][$cols['column']] = $cols['alias']; + } + } + } + } + return $aliases; +} + ?> diff --git a/tbl_row_action.php b/tbl_row_action.php index b09e70e01a..51a1889e38 100644 --- a/tbl_row_action.php +++ b/tbl_row_action.php @@ -59,10 +59,13 @@ if (!empty($submit_mult)) { // indicating WHERE clause. Then we build the array which is used // for the tbl_change.php script. $where_clause = array(); - foreach ($_REQUEST['rows_to_delete'] as $i => $i_where_clause) { - $where_clause[] = urldecode($i_where_clause); + if (isset($_REQUEST['rows_to_delete']) + && is_array($_REQUEST['rows_to_delete']) + ) { + foreach ($_REQUEST['rows_to_delete'] as $i => $i_where_clause) { + $where_clause[] = urldecode($i_where_clause); + } } - $active_page = 'tbl_change.php'; include 'tbl_change.php'; break; @@ -76,10 +79,13 @@ if (!empty($submit_mult)) { // indicating WHERE clause. Then we build the array which is used // for the tbl_change.php script. $where_clause = array(); - foreach ($_REQUEST['rows_to_delete'] as $i => $i_where_clause) { - $where_clause[] = urldecode($i_where_clause); + if (isset($_REQUEST['rows_to_delete']) + && is_array($_REQUEST['rows_to_delete']) + ) { + foreach ($_REQUEST['rows_to_delete'] as $i => $i_where_clause) { + $where_clause[] = urldecode($i_where_clause); + } } - $active_page = 'tbl_export.php'; include 'tbl_export.php'; break; diff --git a/test/classes/plugin/export/PMA_ExportSql_test.php b/test/classes/plugin/export/PMA_ExportSql_test.php index 59b8055e89..b57153bc23 100644 --- a/test/classes/plugin/export/PMA_ExportSql_test.php +++ b/test/classes/plugin/export/PMA_ExportSql_test.php @@ -1704,7 +1704,7 @@ class PMA_ExportSql_Test extends PHPUnit_Framework_TestCase ); $this->assertContains( - "foo;\nDELIMITER //\nbarDELIMITER ;\n", + "foo;\nDELIMITER $$\nbarDELIMITER ;\n", $result ); @@ -1896,7 +1896,7 @@ class PMA_ExportSql_Test extends PHPUnit_Framework_TestCase ); $this->assertContains( - 'INSERT DELAYED IGNORE INTO "table" ("a", ' . + 'INSERT DELAYED IGNORE INTO "table" ("name", ' . '"name", "name", "name", ' . '"name") VALUES', $result @@ -2001,7 +2001,7 @@ class PMA_ExportSql_Test extends PHPUnit_Framework_TestCase $result = ob_get_clean(); $this->assertContains( - 'UPDATE IGNORE "table" SET "a" = NULL,' . + 'UPDATE IGNORE "table" SET "name" = NULL,' . '"name" = NULL WHERE CONCAT(`tbl`.`pma`) IS NULL;', $result ); @@ -2133,5 +2133,246 @@ class PMA_ExportSql_Test extends PHPUnit_Framework_TestCase $result ); } + + /** + * Test for ExportSql::initAlias + * + * @return void + */ + public function testInitAlias() + { + $aliases = array( + 'a' => array( + 'alias' => 'aliastest', + 'tables' => array( + 'foo' => array( + 'alias' => 'qwerty' + ), + 'bar' => array( + 'alias' => 'f' + ) + ) + ) + ); + $db = 'a'; + $table = null; + + $this->object->initAlias($aliases, $db, $table); + $this->assertEquals('aliastest', $db); + $this->assertNull($table); + + $db = 'foo'; + $table = 'qwerty'; + + $this->object->initAlias($aliases, $db, $table); + $this->assertEquals('foo', $db); + $this->assertEquals('qwerty', $table); + + $db = 'a'; + $table = 'foo'; + + $this->object->initAlias($aliases, $db, $table); + $this->assertEquals('aliastest', $db); + $this->assertEquals('qwerty', $table); + } + + /** + * Test for ExportSql::getAlias + * + * @return void + */ + public function testGetAlias() + { + $aliases = array( + 'a' => array( + 'alias' => 'aliastest', + 'tables' => array( + 'foo' => array( + 'alias' => 'qwerty', + 'columns' => array( + 'baz' => 'p', + 'pqr' => 'pphymdain' + ) + ), + 'bar' => array( + 'alias' => 'f', + 'columns' => array( + 'xy' => 'n' + ) + ) + ) + ) + ); + + $this->assertEquals( + 'f', $this->object->getAlias($aliases, 'bar') + ); + + $this->assertEquals( + 'aliastest', $this->object->getAlias($aliases, 'a') + ); + + $this->assertEquals( + 'pphymdain', $this->object->getAlias($aliases, 'pqr') + ); + + $this->assertEquals( + '', $this->object->getAlias($aliases, 'abc') + ); + } + + /** + * Test for ExportSql::substituteAlias + * + * @return void + */ + public function testSubstituteAlias() + { + $GLOBALS['sql_backquotes'] = '`'; + $sql_query = 'CREATE TABLE `data` ( xyz int )'; + $data = '`data`'; + $alias = 'sample'; + $pos = 19; + $offset = 0; + $result =$this->object->substituteAlias( + $sql_query, $data, $alias, $pos, $offset + ); + + $this->assertEquals( + 'CREATE TABLE `sample` ( xyz int )', + $result + ); + $this->assertEquals(2, $offset); + + $GLOBALS['sql_backquotes'] = false; + $result =$this->object->substituteAlias( + $sql_query, $data, $alias, $pos + ); + + $this->assertEquals( + 'CREATE TABLE sample ( xyz int )', + $result + ); + + $GLOBALS['sql_backquotes'] = '`'; + $sql_query = 'CREATE TABLE `sample` ( qwerty int )'; + $data = 'qwerty'; + $alias = 'f'; + $offset = 2; + $pos = 28 + 2; + $result =$this->object->substituteAlias( + $sql_query, $data, $alias, $pos, $offset + ); + + $this->assertEquals( + 'CREATE TABLE `sample` ( `f` int )', + $result + ); + $this->assertEquals(-1, $offset); + } + + /** + * Test for ExportSql::replaceWithAlias + * + * @return void + */ + public function testReplaceWithAlias() + { + $aliases = array( + 'a' => array( + 'alias' => 'aliastest', + 'tables' => array( + 'foo' => array( + 'alias' => 'bartest', + 'columns' => array( + 'baz' => 'p', + 'pqr' => 'pphymdain' + ) + ), + 'bar' => array( + 'alias' => 'f', + 'columns' => array( + 'xy' => 'n' + ) + ) + ) + ) + ); + + $GLOBALS['sql_backquotes'] = '`'; + $db = 'a'; + $table = 'foo'; + $sql_query = "CREATE TABLE IF NOT EXISTS foo (" + . "baz tinyint(3) unsigned NOT NULL COMMENT 'Primary Key'," + . "xyz varchar(255) COLLATE latin1_general_ci NOT NULL " + . "COMMENT 'xyz'," + . "pqr varchar(10) COLLATE latin1_general_ci NOT NULL " + . "COMMENT 'pqr'," + . "CONSTRAINT fk_om_dept FOREIGN KEY (baz) " + . "REFERENCES dept_master (baz)," + . ") ENGINE=InnoDB DEFAULT CHARSET=latin1 COLLATE=" + . "latin1_general_ci COMMENT='List' AUTO_INCREMENT=5"; + $result = $this->object->replaceWithAliases( + $sql_query, $aliases, $db, $table + ); + + $this->assertEquals( + "CREATE TABLE IF NOT EXISTS `bartest` (" + . "`p` tinyint(3) unsigned NOT NULL COMMENT 'Primary Key'," + . "xyz varchar(255) COLLATE latin1_general_ci NOT NULL " + . "COMMENT 'xyz'," + . "`pphymdain` varchar(10) COLLATE latin1_general_ci NOT NULL " + . "COMMENT 'pqr'," + . "CONSTRAINT fk_om_dept FOREIGN KEY (`p`) " + . "REFERENCES dept_master (baz)," + . ") ENGINE=InnoDB DEFAULT CHARSET=latin1 COLLATE=" + . "latin1_general_ci COMMENT='List' AUTO_INCREMENT=5", + $result + ); + + $result = $this->object->replaceWithAliases($sql_query, array(), '', ''); + + $this->assertEquals( + "CREATE TABLE IF NOT EXISTS foo (" + . "baz tinyint(3) unsigned NOT NULL COMMENT 'Primary Key'," + . "xyz varchar(255) COLLATE latin1_general_ci NOT NULL " + . "COMMENT 'xyz'," + . "pqr varchar(10) COLLATE latin1_general_ci NOT NULL " + . "COMMENT 'pqr'," + . "CONSTRAINT fk_om_dept FOREIGN KEY (baz) " + . "REFERENCES dept_master (baz)," + . ") ENGINE=InnoDB DEFAULT CHARSET=latin1 COLLATE=" + . "latin1_general_ci COMMENT='List' AUTO_INCREMENT=5", + $result + ); + + $GLOBALS['sql_backquotes'] = null; + $table = 'bar'; + $sql_query = "CREATE TRIGGER `BEFORE_bar_INSERT` " + . "BEFORE INSERT ON `bar`\r\n" + . "FOR EACH ROW BEGIN\r\n" + . "SET @cnt=(SELECT count(*) FROM bar WHERE " + . "xy=NEW.xy AND id=NEW.id AND " + . "abc=NEW.xy LIMIT 1);\r\n" + . "IF @cnt<>0 THEN\n" + . "SET NEW.xy=1;\r\n" + . "END IF;\nEND\n$$"; + $result = $this->object->replaceWithAliases( + $sql_query, $aliases, $db, $table + ); + + $this->assertEquals( + "CREATE TRIGGER `BEFORE_bar_INSERT` " + . "BEFORE INSERT ON f\n" + . "FOR EACH ROW BEGIN\n" + . "SET @cnt=(SELECT count(*) FROM f WHERE " + . "n=NEW.n AND id=NEW.id AND " + . "abc=NEW.n LIMIT 1);\n" + . "IF @cnt<>0 THEN\n" + . "SET NEW.n=1;\n" + . "END IF;\nEND\n$$", + $result + ); + } } ?> diff --git a/test/libraries/PMA_SQL_parser_test.php b/test/libraries/PMA_SQL_parser_test.php index b86f16a635..6f433cbbf0 100644 --- a/test/libraries/PMA_SQL_parser_test.php +++ b/test/libraries/PMA_SQL_parser_test.php @@ -153,7 +153,7 @@ class PMA_SQLParser_Test extends PHPUnit_Framework_TestCase 2 => array( 'type' => 'punct_queryend', 'data' => ';', - 'pos' => 0, + 'pos' => 9, ), 'len' => 3, ) @@ -171,7 +171,7 @@ class PMA_SQLParser_Test extends PHPUnit_Framework_TestCase 1 => array( 'type' => 'punct', 'data' => '*', - 'pos' => 0, + 'pos' => 8, ), 2 => array( 'type' => 'alpha_reservedWord', @@ -188,7 +188,7 @@ class PMA_SQLParser_Test extends PHPUnit_Framework_TestCase 4 => array( 'type' => 'punct_queryend', 'data' => ';', - 'pos' => 0, + 'pos' => 18, ), 'len' => 5, ) @@ -206,7 +206,7 @@ class PMA_SQLParser_Test extends PHPUnit_Framework_TestCase 1 => array( 'type' => 'punct', 'data' => '*', - 'pos' => 0, + 'pos' => 8, ), 2 => array( 'type' => 'alpha_reservedWord', @@ -217,12 +217,12 @@ class PMA_SQLParser_Test extends PHPUnit_Framework_TestCase 3 => array( 'type' => 'quote_backtick', 'data' => '`aaa`', - 'pos' => 0, + 'pos' => 19, ), 4 => array( 'type' => 'punct_queryend', 'data' => ';', - 'pos' => 0, + 'pos' => 20, ), 'len' => 5, ) @@ -240,7 +240,7 @@ class PMA_SQLParser_Test extends PHPUnit_Framework_TestCase 1 => array( 'type' => 'punct', 'data' => '*', - 'pos' => 0, + 'pos' => 8, ), 2 => array( 'type' => 'alpha_reservedWord', @@ -251,21 +251,26 @@ class PMA_SQLParser_Test extends PHPUnit_Framework_TestCase 3 => array( 'type' => 'quote_backtick', 'data' => '`aaa`', - 'pos' => 0, + 'pos' => 19, ), 4 => array( 'type' => 'punct_queryend', 'data' => ';', - 'pos' => 0, + 'pos' => 20, ), 'len' => 5, ), - '
Automatically appended backtick to the end of query!
' + '
Automatically appended ' + . 'backtick to the end of query!
' ), array( - 'SELECT * FROM `a_table` tbla INNER JOIN b_table` tblb ON tblb.id = tbla.id WHERE tblb.field1 != tbla.field1`;', + 'SELECT * FROM `a_table` tbla INNER JOIN b_table` tblb ON ' + . 'tblb.id = tbla.id WHERE tblb.field1 != tbla.field1`;', array( - 'raw' => 'SELECT * FROM `a_table` tbla INNER JOIN b_table` tblb ON tblb.id = tbla.id WHERE tblb.field1 != tbla.field1`;', + 'raw' => 'SELECT * FROM `a_table` tbla INNER JOIN ' + . 'b_table` tblb ON tblb.id = tbla.id WHERE ' + . 'tblb.field1 != tbla.field1`;', 0 => array( 'type' => 'alpha_reservedWord', 'data' => 'SELECT', @@ -275,7 +280,7 @@ class PMA_SQLParser_Test extends PHPUnit_Framework_TestCase 1 => array( 'type' => 'punct', 'data' => '*', - 'pos' => 0, + 'pos' => 8, ), 2 => array( 'type' => 'alpha_reservedWord', @@ -286,7 +291,7 @@ class PMA_SQLParser_Test extends PHPUnit_Framework_TestCase 3 => array( 'type' => 'quote_backtick', 'data' => '`a_table`', - 'pos' => 0, + 'pos' => 23, ), 4 => array( 'type' => 'alpha_identifier', @@ -314,18 +319,117 @@ class PMA_SQLParser_Test extends PHPUnit_Framework_TestCase ), 8 => array( 'type' => 'quote_backtick', - 'data' => '` tblb ON tblb.id = tbla.id WHERE tblb.field1 != tbla.field1`', - 'pos' => 0, + 'data' => '` tblb ON tblb.id = tbla.id WHERE ' + . 'tblb.field1 != tbla.field1`', + 'pos' => 108, ), 9 => array( 'type' => 'punct_queryend', 'data' => ';', - 'pos' => 0, + 'pos' => 109, ), 'len' => 10, ) ), ); } + + /** + * Data provider for testPMA_SQP_getAliasesFromQuery + * + * @return array with test data + */ + public function aliasDataProvider() + { + return array( + array( + 'select i.name as `n`,abcdef gh from qwerty i', + 'mydb', + array( + 'mydb' => array( + 'alias' => null, + 'tables' => array( + 'qwerty' => array( + 'alias' => 'i', + 'columns' => array( + 'name' => 'n', + 'abcdef' => 'gh' + ) + ) + ) + ) + ) + ), + array( + 'select film_id id,title from film', + 'sakila', + array( + 'sakila' => array( + 'alias' => null, + 'tables' => array( + 'film' => array( + 'alias' => null, + 'columns' => array( + 'film_id' => 'id' + ) + ) + ) + ) + ) + ), + array( + 'select `sakila`.`A`.`actor_id` as aid,`F`.`film_id` `fid`,' + . 'last_update updated from `sakila`.actor A join `film_actor` as ' + . '`F` on F.actor_id = A.`actor_id`', + 'sakila', + array( + 'sakila' => array( + 'alias' => null, + 'tables' => array( + 'film_actor' => array( + 'alias' => 'F', + 'columns' => array( + 'film_id' => 'fid', + 'last_update' => 'updated' + ) + ), + 'actor' => array( + 'alias'=> 'A', + 'columns' => array( + 'actor_id' => 'aid', + 'last_update' => 'updated' + ) + ) + ) + ) + ) + ), + array( + '', + '', + array() + ) + ); + } + + /** + * Testing of PMA_SQP_getAliasesFromQuery. + * + * @param string $select_query The Select SQL Query + * @param string $db Current DB + * @param array $expected Expected parse result + * + * @return void + * + * @dataProvider aliasDataProvider + * @group medium + */ + public function testPMA_SQP_getAliasesFromQuery($select_query, $db, $expected) + { + $this->assertEquals( + $expected, + PMA_SQP_getAliasesFromQuery($select_query, $db) + ); + } } ?>