diff --git a/ChangeLog b/ChangeLog
index 4f1bcdb10f..0f3759dd9c 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -54,6 +54,7 @@ phpMyAdmin - ChangeLog
+ rfe #1634 Don't group tables in tree if the result has only one group
- bug #4946 When hide table structure actions is false, action should be in a row
+ rfe #726 Batch changing the collation of each column in a table
+- bug #4510 QBE generates wrong query
+ rfe #1594 Use plain English descriptors instead of script names for icon link destinations
+ rfe #1541 Disable foreign key checks for some operations
diff --git a/libraries/DBQbe.class.php b/libraries/DBQbe.class.php
index 3873817920..8efe43b66c 100644
--- a/libraries/DBQbe.class.php
+++ b/libraries/DBQbe.class.php
@@ -128,6 +128,13 @@ class PMA_DbQbe
* @var array
*/
private $_curField;
+ /**
+ * Current alias
+ *
+ * @access private
+ * @var array
+ */
+ private $_curAlias;
/**
* Current criteria Sort options
*
@@ -135,6 +142,13 @@ class PMA_DbQbe
* @var array
*/
private $_curSort;
+ /**
+ * Current criteria sort order
+ *
+ * @access private
+ * @var array
+ */
+ private $_curSortOrder;
/**
* Current criteria Show options
*
@@ -398,6 +412,50 @@ class PMA_DbQbe
return $html_output;
}
+ /**
+ * Provides select options list containing sort order
+ *
+ * @param integer $columnNumber Column Number (0,1,2) or more
+ * @param integer $sortOrder Sort order
+ *
+ * @return string HTML for select options
+ */
+ private function _getSortOrderSelectCell($columnNumber, $sortOrder)
+ {
+ $totalColumnCount = $this->_getNewColumnCount();
+ $html_output = '
';
+ $html_output .= '';
+ $html_output .= ' | ';
+ return $html_output;
+ }
+
+ /**
+ * Returns the new column count after adding and removing columns as instructed
+ *
+ * @return int new column count
+ */
+ private function _getNewColumnCount()
+ {
+ $totalColumnCount = $this->_criteria_column_count;
+ if (! empty($this->_criteriaColumnInsert)) {
+ $totalColumnCount += count($this->_criteriaColumnInsert);
+ }
+ if (! empty($this->_criteriaColumnDelete)) {
+ $totalColumnCount -= count($this->_criteriaColumnDelete);
+ }
+ return $totalColumnCount;
+ }
+
/**
* Provides search form's row containing column select options
*
@@ -444,6 +502,59 @@ class PMA_DbQbe
return $html_output;
}
+ /**
+ * Provides search form's row containing column aliases
+ *
+ * @return string HTML for search table's row
+ */
+ private function _getColumnAliasRow()
+ {
+ $html_output = '';
+ $html_output .= '| ' . __('Alias:') . ' | ';
+ $new_column_count = 0;
+
+ for (
+ $colInd = 0;
+ $colInd < $this->_criteria_column_count;
+ $colInd++
+ ) {
+ if (! empty($this->_criteriaColumnInsert)
+ && isset($this->_criteriaColumnInsert[$colInd])
+ && $this->_criteriaColumnInsert[$colInd] == 'on'
+ ) {
+ $html_output .= '';
+ $html_output .= '';
+ $html_output .= ' | ';
+ $new_column_count++;
+ } // end if
+
+ if (! empty($this->_criteriaColumnDelete)
+ && isset($this->_criteriaColumnDelete[$colInd])
+ && $this->_criteriaColumnDelete[$colInd] == 'on'
+ ) {
+ continue;
+ }
+
+ $tmp_alias = '';
+ if (! empty($_REQUEST['criteriaAlias'][$colInd])) {
+ $tmp_alias
+ = $this->_curAlias[$new_column_count]
+ = $_REQUEST['criteriaAlias'][$colInd];
+ }// end if
+
+ $html_output .= '';
+ $html_output .= '';
+ $html_output .= ' | ';
+ $new_column_count++;
+ } // end for
+ $html_output .= '
';
+ return $html_output;
+ }
+
/**
* Provides search form's row containing sort(ASC/DESC) select options
*
@@ -482,26 +593,23 @@ class PMA_DbQbe
) {
$_REQUEST['criteriaSort'][$colInd] = '';
} //end if
- // Set asc_selected
- if (isset($_REQUEST['criteriaSort'][$colInd])
- && $_REQUEST['criteriaSort'][$colInd] == 'ASC'
- ) {
+
+ $asc_selected = ''; $desc_selected = '';
+ if (isset($_REQUEST['criteriaSort'][$colInd])) {
$this->_curSort[$new_column_count]
= $_REQUEST['criteriaSort'][$colInd];
- $asc_selected = ' selected="selected"';
+ // Set asc_selected
+ if ($_REQUEST['criteriaSort'][$colInd] == 'ASC') {
+ $asc_selected = ' selected="selected"';
+ } // end if
+ // Set desc selected
+ if ($_REQUEST['criteriaSort'][$colInd] == 'DESC') {
+ $desc_selected = ' selected="selected"';
+ } // end if
} else {
- $asc_selected = '';
- } // end if
- // Set desc selected
- if (isset($_REQUEST['criteriaSort'][$colInd])
- && $_REQUEST['criteriaSort'][$colInd] == 'DESC'
- ) {
- $this->_curSort[$new_column_count]
- = $_REQUEST['criteriaSort'][$colInd];
- $desc_selected = ' selected="selected"';
- } else {
- $desc_selected = '';
- } // end if
+ $this->_curSort[$new_column_count] = '';
+ }
+
$html_output .= $this->_getSortSelectCell(
$new_column_count, $asc_selected, $desc_selected
);
@@ -511,6 +619,55 @@ class PMA_DbQbe
return $html_output;
}
+ /**
+ * Provides search form's row containing sort order
+ *
+ * @return string HTML for search table's row
+ */
+ private function _getSortOrder()
+ {
+ $html_output = '';
+ $html_output .= '| ' . __('Sort order:') . ' | ';
+ $new_column_count = 0;
+
+ for (
+ $colInd = 0;
+ $colInd < $this->_criteria_column_count;
+ $colInd++
+ ) {
+ if (! empty($this->_criteriaColumnInsert)
+ && isset($this->_criteriaColumnInsert[$colInd])
+ && $this->_criteriaColumnInsert[$colInd] == 'on'
+ ) {
+ $html_output .= $this->_getSortOrderSelectCell(
+ $new_column_count, null
+ );
+ $new_column_count++;
+ } // end if
+
+ if (! empty($this->_criteriaColumnDelete)
+ && isset($this->_criteriaColumnDelete[$colInd])
+ && $this->_criteriaColumnDelete[$colInd] == 'on'
+ ) {
+ continue;
+ }
+
+ $sortOrder = null;
+ if (! empty($_REQUEST['criteriaSortOrder'][$colInd])) {
+ $sortOrder
+ = $this->_curSortOrder[$new_column_count]
+ = $_REQUEST['criteriaSortOrder'][$colInd];
+ }
+
+ $html_output .= $this->_getSortOrderSelectCell(
+ $new_column_count, $sortOrder
+ );
+ $new_column_count++;
+ } // end for
+ $html_output .= '
';
+ return $html_output;
+ }
+
/**
* Provides search form's row containing SHOW checkboxes
*
@@ -708,20 +865,24 @@ class PMA_DbQbe
*
* @param integer $column_number Column Number (0,1,2) or more
* @param array $selected Selected criteria column name
+ * @param bool $last_column Whether this is the last column
*
* @return string HTML for modification cell
*/
- private function _getAndOrColCell($column_number, $selected = null)
- {
+ private function _getAndOrColCell(
+ $column_number, $selected = null, $last_column = false
+ ) {
$html_output = '';
- $html_output .= '' . __('Or:') . '';
- $html_output .= '';
- $html_output .= ' ' . __('And:') . '';
- $html_output .= '';
+ if (! $last_column) {
+ $html_output .= '' . __('Or:') . '';
+ $html_output .= '';
+ $html_output .= ' ' . __('And:') . '';
+ $html_output .= '';
+ }
$html_output .= ' ' . __('Ins');
$html_output .= '';
@@ -779,7 +940,8 @@ class PMA_DbQbe
}
$html_output .= $this->_getAndOrColCell(
$new_column_count,
- $checked_options
+ $checked_options,
+ ($column_index + 1 == $this->_criteria_column_count)
);
$new_column_count++;
} // end for
@@ -978,7 +1140,12 @@ class PMA_DbQbe
&& isset($this->_curShow[$column_index])
&& $this->_curShow[$column_index] == 'on'
) {
- $select_clauses[] = $this->_curField[$column_index];
+ $select = $this->_curField[$column_index];
+ if (! empty($this->_curAlias[$column_index])) {
+ $select .= " AS "
+ . PMA_Util::backquote($this->_curAlias[$column_index]);
+ }
+ $select_clauses[] = $select;
}
} // end for
if ($select_clauses) {
@@ -1092,6 +1259,18 @@ class PMA_DbQbe
$orderby_clause = '';
$orderby_clauses = array();
+ // Create copy of instance variables
+ $field = $this->_curField;
+ $sort = $this->_curSort;
+ $sortOrder = $this->_curSortOrder;
+ if ($sortOrder
+ && count($sortOrder) == count($sort)
+ && count($sortOrder) == count($field)
+ ) {
+ // Sort all three arrays based on sort order
+ array_multisort($sortOrder, $sort, $field);
+ }
+
for (
$column_index = 0;
$column_index < $this->_criteria_column_count;
@@ -1100,19 +1279,19 @@ class PMA_DbQbe
// if all columns are chosen with * selector,
// then sorting isn't available
// Fix for Bug #570698
- if (empty($this->_curField[$column_index])
- && empty($this->_curSort[$column_index])
+ if (empty($field[$column_index])
+ && empty($sort[$column_index])
) {
continue;
}
- if (/*overload*/mb_substr($this->_curField[$column_index], -2) == '.*') {
+ if (/*overload*/mb_substr($field[$column_index], -2) == '.*') {
continue;
}
- if (! empty($this->_curSort[$column_index])) {
- $orderby_clauses[] = $this->_curField[$column_index] . ' '
- . $this->_curSort[$column_index];
+ if (! empty($sort[$column_index])) {
+ $orderby_clauses[] = $field[$column_index] . ' '
+ . $sort[$column_index];
}
} // end for
if ($orderby_clauses) {
@@ -1371,30 +1550,139 @@ class PMA_DbQbe
} // end while
// Create LEFT JOINS out of Relations
- if (count($all_tables) > 0) {
- // Get tables and columns with valid where clauses
- $valid_where_clauses = $this->_getWhereClauseTablesAndColumns();
- $where_clause_tables = $valid_where_clauses['where_clause_tables'];
- $where_clause_columns = $valid_where_clauses['where_clause_columns'];
- // Get master table
- $master = $this->_getMasterTable(
- $all_tables, $all_columns,
- $where_clause_columns, $where_clause_tables
- );
- $from_clause = PMA_Util::backquote($master)
- . PMA_getRelatives($all_tables, $master);
+ $from_clause = $this->_getJoinForFromClause($all_tables, $all_columns);
- } // end if (count($all_tables) > 0)
+ // In case relations are not defined, just generate the FROM clause
+ // from the list of tables, however we don't generate any JOIN
+ if (empty($from_clause)) {
+ // Create cartesian product
+ $from_clause = implode(", ", array_map('PMA_Util::backquote', $all_tables));
+ }
} // end count($_POST['criteriaColumn']) > 0
- // In case relations are not defined, just generate the FROM clause
- // from the list of tables, however we don't generate any JOIN
- if (empty($from_clause) && isset($all_tables)) {
- $from_clause = implode(', ', $all_tables);
- }
return $from_clause;
}
+ /**
+ * Formulates the WHERE clause by JOINing tables
+ *
+ * @param array $allTables Tables involved in the search
+ * @param array $allColumns Columns involved in the search
+ *
+ * @return string table name
+ */
+ private function _getJoinForFromClause($allTables, $allColumns) {
+
+ // $relations[master_table][foreign_table] => clause
+ $relations = array();
+
+ // Fill $relations with inter table relationship data
+ foreach ($allTables as $oneTable) {
+ $relations[$oneTable] = array();
+
+ $foreigners = PMA_getForeigners($GLOBALS['db'], $oneTable);
+ foreach ($foreigners as $field => $foreigner) {
+ // Foreign keys data
+ if ($field == 'foreign_keys_data') {
+ foreach ($foreigner as $oneKey) {
+ $clauses = array();
+ // There may be multiple column relations
+ foreach ($oneKey['index_list'] as $index => $oneField) {
+ $clauses[] = PMA_Util::backquote($oneTable) . "."
+ . PMA_Util::backquote($oneField) . " = "
+ . PMA_Util::backquote($oneKey['ref_table_name']) . "."
+ . PMA_Util::backquote($oneKey['ref_index_list'][$index]);
+ }
+ // Combine multiple column relations with AND
+ $relations[$oneTable][$oneKey['ref_table_name']]
+ = implode(" AND ", $clauses);
+ }
+ } else { // Internal relations
+ $relations[$oneTable][$foreigner['foreign_table']]
+ = PMA_Util::backquote($oneTable) . "."
+ . PMA_Util::backquote($field) . " = "
+ . PMA_Util::backquote($foreigner['foreign_table']) . "."
+ . PMA_Util::backquote($foreigner['foreign_field']);
+ }
+ }
+ }
+
+ // Get tables and columns with valid where clauses
+ $validWhereClauses = $this->_getWhereClauseTablesAndColumns();
+ $whereClauseTables = $validWhereClauses['where_clause_tables'];
+ $whereClauseColumns = $validWhereClauses['where_clause_columns'];
+
+ // Get master table
+ $master = $this->_getMasterTable(
+ $allTables, $allColumns,
+ $whereClauseColumns, $whereClauseTables
+ );
+
+ // Will incldue master tables and all tables that can be combined into
+ // a cluster by their relation
+ $finalized = array();
+ if (mb_strlen($master) > 0) {
+ // Add master tables
+ $finalized[$master] = '';
+ }
+
+ while (true) {
+ $added = false;
+ foreach ($relations as $masterTable => $foreignData) {
+ foreach ($foreignData as $foreignTable => $clause) {
+ if (! isset($finalized[$masterTable])
+ && isset($finalized[$foreignTable])
+ ) {
+ $finalized[$masterTable] = $clause;
+ $added = true;
+
+ // We are done if all tables are in $finalized
+ if (count($finalized) == count($allTables)) {
+ break 3;
+ }
+ } else if (! isset($finalized[$foreignTable])
+ && isset($finalized[$masterTable])
+ && in_array($foreignTable, $allTables)
+ ) {
+ $finalized[$foreignTable] = $clause;
+ $added = true;
+
+ // We are done if all tables are in $finalized
+ if (count($finalized) == count($allTables)) {
+ break 3;
+ }
+ }
+ }
+ }
+ // If no new tables were added during this iteration, break;
+ if (! $added) {
+ break;
+ }
+ }
+
+ // Tables that can not be combined with the table cluster
+ // that includes master table
+ $unfinalized = array_diff($allTables, array_keys($finalized));
+ // Add these tables as cartesian product before joined tables
+ $join = implode(', ', array_map('PMA_Util::backquote', $unfinalized));
+
+ $first = true;
+ // Add joined tables
+ foreach ($finalized as $table => $clause) {
+ if ($first) {
+ if (! empty($join)) {
+ $join .= ", ";
+ }
+ $join .= PMA_Util::backquote($table);
+ $first = false;
+ } else {
+ $join .= "\n LEFT JOIN " . PMA_Util::backquote($table) . " ON " . $clause;
+ }
+ }
+
+ return $join;
+ }
+
/**
* Provides the generated SQL query
*
@@ -1434,8 +1722,10 @@ class PMA_DbQbe
$html_output .= '';
// Get table's elements
$html_output .= $this->_getColumnNamesRow();
- $html_output .= $this->_getSortRow();
+ $html_output .= $this->_getColumnAliasRow();
$html_output .= $this->_getShowRow();
+ $html_output .= $this->_getSortRow();
+ $html_output .= $this->_getSortOrder();
$html_output .= $this->_getCriteriaInputboxRow();
$html_output .= $this->_getInsDelAndOrCriteriaRows();
$html_output .= $this->_getModifyColumnsRow();
diff --git a/libraries/relation.lib.php b/libraries/relation.lib.php
index e10f4a18ad..d160ee1cfe 100644
--- a/libraries/relation.lib.php
+++ b/libraries/relation.lib.php
@@ -1364,130 +1364,6 @@ function PMA_getForeignData(
return $foreignData;
} // end of 'PMA_getForeignData()' function
-/**
- * Finds all related tables
- *
- * @param array $all_tables All the involved tables
- * @param string $master The master table to form the LEFT JOIN clause
- *
- * @return string LEFT JOIN
- * @access private
- */
-function PMA_getRelatives($all_tables, $master)
-{
- $fromclause = '';
- $left_joins = array();
- $emerg = '';
- $ignore_internal_relations = false;
-
- // The list of tables that we still couldn't connect
- $remaining_tables = $all_tables;
- unset($remaining_tables[$master]);
- // The list of already connected tables
- $known_tables = array();
- $known_tables[$master] = $master;
- $run = 0;
- while ($GLOBALS['cfgRelation']['relwork'] && count($remaining_tables) > 0) {
- // Whether to go from master to foreign or vice versa
- if ($run % 2 == 0) {
- $from = 'master';
- $to = 'foreign';
- } else {
- $from = 'foreign';
- $to = 'master';
- }
- $in_know = '(\'' . implode('\', \'', $known_tables) . '\')';
- $in_left = '(\'' . implode('\', \'', $remaining_tables) . '\')';
- $rel_query = 'SELECT *'
- . ' FROM ' . PMA_Util::backquote($GLOBALS['cfgRelation']['db'])
- . '.' . PMA_Util::backquote($GLOBALS['cfgRelation']['relation'])
- . ' WHERE ' . $from . '_db = \''
- . PMA_Util::sqlAddSlashes($GLOBALS['db']) . '\''
- . ' AND ' . $to . '_db = \''
- . PMA_Util::sqlAddSlashes($GLOBALS['db']) . '\''
- . ' AND ' . $from . '_table IN ' . $in_know
- . ' AND ' . $to . '_table IN ' . $in_left;
- $relations = @$GLOBALS['dbi']->query($rel_query, $GLOBALS['controllink']);
- while ($row = $GLOBALS['dbi']->fetchAssoc($relations)) {
- $found_table = $row[$to . '_table'];
- if (isset($remaining_tables[$found_table])) {
- $left_join_with = PMA_Util::backquote($GLOBALS['db']) . '.'
- . PMA_Util::backquote($row[$to . '_table']);
- $on_condition = PMA_Util::backquote($row[$from . '_table']) . '.'
- . PMA_Util::backquote($row[$from . '_field']) . ' = '
- . PMA_Util::backquote($row[$to . '_table']) . '.'
- . PMA_Util::backquote($row[$to . '_field']);
-
- $left_joins[$left_join_with] = array(
- 'left_join_with' => $left_join_with,
- 'on_condition' => array($on_condition)
- );
-
- $known_tables[$found_table] = $found_table;
- unset($remaining_tables[$found_table]);
- }
- } // end while
- $run++;
- if ($run > 5) {
- foreach ($remaining_tables as $table) {
- $emerg .= ', ' . PMA_Util::backquote($table);
- unset($remaining_tables[$table]);
- }
- }
- } // end while
-
- // Generate 'LEFT JOIN's for InnoDB foreign keys.
- $remaining_tables = $all_tables;
- foreach ($remaining_tables as $one_table) {
- $foreigners = PMA_getForeigners($GLOBALS['db'], $one_table, '', 'foreign');
- foreach ($foreigners['foreign_keys_data'] as $one_key) {
- if (in_array($one_key['ref_table_name'], $all_tables)
- && ! isset($one_key['ref_db_name'])
- && ($one_key['ref_table_name'] == $master
- || $one_key['ref_table_name'] == $one_table)
- ) {
- $left_join_with = PMA_Util::backquote($GLOBALS['db']) . '.'
- . PMA_Util::backquote($one_table);
- if (! isset($left_joins[$left_join_with])) {
- $left_joins[$left_join_with] = array(
- 'left_join_with' =>$left_join_with,
- 'on_condition' => array()
- );
- }
-
- foreach ($one_key['ref_index_list'] as $key => $one_column) {
- $on_condition = PMA_Util::backquote($one_key['ref_table_name'])
- . '.' . PMA_Util::backquote($one_column) . ' = '
- . PMA_Util::backquote($one_table) . '.'
- . PMA_Util::backquote($one_key['index_list'][$key]);
-
- if (! in_array($on_condition, $left_joins[$left_join_with]['on_condition'])) {
- $left_joins[$left_join_with]['on_condition'][]
- = $on_condition;
- }
- }
- $ignore_internal_relations = true;
- unset($remaining_tables[$one_table]);
- }
- }
- }
-
- if ($ignore_internal_relations) {
- $emerg = '';
- }
-
- // Build the 'FROM' clause.
- foreach ($left_joins as $one_join) {
- $fromclause .= "\n" . ' LEFT JOIN '
- . $one_join['left_join_with']
- . ' ON '
- . implode(' AND ', $one_join['on_condition']);
- }
-
- $fromclause = $emerg . $fromclause;
- return $fromclause;
-} // end of the "PMA_getRelatives()" function
-
/**
* Rename a field in relation tables
*
diff --git a/test/classes/PMA_DBQbe_test.php b/test/classes/PMA_DBQbe_test.php
index c84f337c6e..c93e8fc906 100644
--- a/test/classes/PMA_DBQbe_test.php
+++ b/test/classes/PMA_DBQbe_test.php
@@ -272,10 +272,7 @@ class PMA_DBQbe_Test extends PHPUnit_Framework_TestCase
. ' Ins DelOr:'
- . ' And: Ins | Ins Del'
. ' | ',
$this->_callProtectedFunction(
|