diff --git a/ChangeLog b/ChangeLog index 55e38ce39..c97e98ff5 100644 --- a/ChangeLog +++ b/ChangeLog @@ -16,6 +16,7 @@ $HeadURL: https://phpmyadmin.svn.sourceforge.net/svnroot/phpmyadmin/trunk/phpMyA - bug #2872247 [interface] Failed opening required 'mysql_charsets.lib.php', thanks to CyberLeo Kitsana - cyberleo - bug [structure] "In use" table incorrectly reported as "view" - bug #2879909 [interface] Removed double htmlspecialchars when editing enum column +- bug #2868328 [relations] Adding foreign key when table name contains a dot 3.2.2.1 (2009-10-12) - [security] XSS and SQL injection, thanks to Herman van Rink diff --git a/libraries/Table.class.php b/libraries/Table.class.php index 7453d68d9..7523dabae 100644 --- a/libraries/Table.class.php +++ b/libraries/Table.class.php @@ -98,9 +98,9 @@ class PMA_Table * @param boolean whether to quote name with backticks `` * @return string table name */ - function getName($quoted = false) + function getName($backquoted = false) { - if ($quoted) { + if ($backquoted) { return PMA_backquote($this->name); } return $this->name; @@ -124,9 +124,9 @@ class PMA_Table * @param boolean whether to quote name with backticks `` * @return string database name for this table */ - function getDbName($quoted = false) + function getDbName($backquoted = false) { - if ($quoted) { + if ($backquoted) { return PMA_backquote($this->db_name); } return $this->db_name; @@ -137,9 +137,9 @@ class PMA_Table * * @param boolean whether to quote name with backticks `` */ - function getFullName($quoted = false) + function getFullName($backquoted = false) { - return $this->getDbName($quoted) . '.' . $this->getName($quoted); + return $this->getDbName($backquoted) . '.' . $this->getName($backquoted); } static public function isView($db = null, $table = null) @@ -1122,7 +1122,7 @@ class PMA_Table * @param boolean whether to quote name with backticks `` * @return array */ - public function getUniqueColumns($quoted = true) + public function getUniqueColumns($backquoted = true) { $sql = 'SHOW INDEX FROM ' . $this->getFullName(true) . ' WHERE Non_unique = 0'; $uniques = PMA_DBI_fetch_result($sql, array('Key_name', null), 'Column_name'); @@ -1132,7 +1132,7 @@ class PMA_Table if (count($index) > 1) { continue; } - $return[] = $this->getFullName($quoted) . '.' . ($quoted ? PMA_backquote($index[0]) : $index[0]); + $return[] = $this->getFullName($backquoted) . '.' . ($backquoted ? PMA_backquote($index[0]) : $index[0]); } return $return; @@ -1148,14 +1148,14 @@ class PMA_Table * @param boolean whether to quote name with backticks `` * @return array */ - public function getIndexedColumns($quoted = true) + public function getIndexedColumns($backquoted = true) { $sql = 'SHOW INDEX FROM ' . $this->getFullName(true) . ' WHERE Seq_in_index = 1'; $indexed = PMA_DBI_fetch_result($sql, 'Column_name', 'Column_name'); $return = array(); foreach ($indexed as $column) { - $return[] = $this->getFullName($quoted) . '.' . ($quoted ? PMA_backquote($column) : $column); + $return[] = $this->getFullName($backquoted) . '.' . ($backquoted ? PMA_backquote($column) : $column); } return $return; diff --git a/tbl_relation.php b/tbl_relation.php index 211c5d99d..ba886d1c8 100644 --- a/tbl_relation.php +++ b/tbl_relation.php @@ -5,7 +5,7 @@ * * includes phpMyAdmin relations and InnoDB relations * - * @todo fix name handling: currently names with dots (.) are not properly handled + * @todo fix name handling: currently names with dots (.) are not properly handled for internal relations (but foreign keys relations are correct) * @todo foreign key constraints require both fields being of equal type and size * @todo check foreign fields to be from same type and size, all other makes no sense * @todo add an link to create an index required for constraints, or an option to do automatically @@ -73,6 +73,36 @@ function PMA_generate_dropdown($dropdown_question, $select_name, $choices, $sele echo '' . "\n"; } +/** + * Split a string on backquote pairs + * + * @param string original string + * @return array containing the elements (and their surrounding backquotes) + * + * @access public + */ +function PMA_backquote_split($text) +{ + $elements = array(); + $final_pos = strlen($text) - 1; + $pos = 0; + while ($pos <= $final_pos) { + $first_backquote = strpos($text, '`', $pos); + $second_backquote = strpos($text, '`', $first_backquote + 1); + // after the second one, there might be another one which means + // this is an escaped backquote + if ($second_backquote < $final_pos && '`' == $text[$second_backquote + 1]) { + $second_backquote = strpos($text, '`', $second_backquote + 2); + } + if (false === $first_backquote || false === $second_backquote) { + break; + } + $elements[] = substr($text, $first_backquote, $second_backquote - $first_backquote + 1); + $pos = $second_backquote + 1; + } + return($elements); +} + /** * Gets the relation settings */ @@ -157,9 +187,7 @@ if (isset($_REQUEST['destination_foreign'])) { $master_field = $me_fields_name[$master_field_md5]; if (! empty($foreign_string)) { - $foreign_string = trim($foreign_string, '`'); - list($foreign_db, $foreign_table, $foreign_field) = - explode('.', $foreign_string); + list($foreign_db, $foreign_table, $foreign_field) = PMA_backquote_split($foreign_string); if (!isset($existrel_foreign[$master_field])) { // no key defined for this field @@ -173,9 +201,9 @@ if (isset($_REQUEST['destination_foreign'])) { . ' ADD FOREIGN KEY (' . PMA_backquote($master_field) . ')' . ' REFERENCES ' - . PMA_backquote($foreign_db) . '.' - . PMA_backquote($foreign_table) . '(' - . PMA_backquote($foreign_field) . ')'; + . $foreign_db . '.' + . $foreign_table . '(' + . $foreign_field . ')'; if (! empty($_REQUEST['on_delete'][$master_field_md5])) { $sql_query .= ' ON DELETE ' . $options_array[$_REQUEST['on_delete'][$master_field_md5]]; @@ -187,9 +215,11 @@ if (isset($_REQUEST['destination_foreign'])) { $display_query .= $sql_query . "\n"; // end repeated code - } elseif (($existrel_foreign[$master_field]['foreign_db'] . '.' .$existrel_foreign[$master_field]['foreign_table'] . '.' . $existrel_foreign[$master_field]['foreign_field'] != $foreign_string) - || ($_REQUEST['on_delete'][$master_field_md5] != (!empty($existrel_foreign[$master_field]['on_delete']) ? $existrel_foreign[$master_field]['on_delete'] : '')) - || ($_REQUEST['on_update'][$master_field_md5] != (!empty($existrel_foreign[$master_field]['on_update']) ? $existrel_foreign[$master_field]['on_update'] : '')) + } elseif (PMA_backquote($existrel_foreign[$master_field]['foreign_db']) != $foreign_db + || PMA_backquote($existrel_foreign[$master_field]['foreign_table']) != $foreign_table + || PMA_backquote($existrel_foreign[$master_field]['foreign_field']) != $foreign_field + || ($_REQUEST['on_delete'][$master_field_md5] != (!empty($existrel_foreign[$master_field]['on_delete']) ? $existrel_foreign[$master_field]['on_delete'] : '')) + || ($_REQUEST['on_update'][$master_field_md5] != (!empty($existrel_foreign[$master_field]['on_update']) ? $existrel_foreign[$master_field]['on_update'] : '')) ) { // another foreign key is already defined for this field // or @@ -209,9 +239,9 @@ if (isset($_REQUEST['destination_foreign'])) { . ' ADD FOREIGN KEY (' . PMA_backquote($master_field) . ')' . ' REFERENCES ' - . PMA_backquote($foreign_db) . '.' - . PMA_backquote($foreign_table) . '(' - . PMA_backquote($foreign_field) . ')'; + . $foreign_db . '.' + . $foreign_table . '(' + . $foreign_field . ')'; if (! empty($_REQUEST['on_delete'][$master_field_md5])) { $sql_query .= ' ON DELETE ' @@ -340,15 +370,15 @@ if ($cfgRelation['relwork'] || PMA_foreignkey_supported($tbl_type)) { $current_table = new PMA_Table($curr_table[0], $db); // explicitely ask for non-quoted list of indexed columns - $selectboxall = array_merge($selectboxall, $current_table->getUniqueColumns(false)); + $selectboxall = array_merge($selectboxall, $current_table->getUniqueColumns($backquoted = false)); // if foreign keys are supported, collect all keys from other // tables of the same engine if (PMA_foreignkey_supported($tbl_type) && isset($curr_table[1]) && strtoupper($curr_table[1]) == $tbl_type) { - // explicitely ask for non-quoted list of indexed columns - $selectboxall_foreign = array_merge($selectboxall_foreign, $current_table->getIndexedColumns(false)); + // need to obtain backquoted values to support dots inside values + $selectboxall_foreign = array_merge($selectboxall_foreign, $current_table->getIndexedColumns($backquoted = true)); } } // end while over tables } // end if @@ -443,9 +473,11 @@ if ($col_rs && PMA_DBI_num_rows($col_rs) > 0) {