Merge remote-tracking branch 'origin/master'

This commit is contained in:
Weblate 2015-06-17 08:27:54 +02:00
commit cd9d383cd1
2 changed files with 168 additions and 90 deletions

View File

@ -58,6 +58,7 @@ phpMyAdmin - ChangeLog
+ rfe #1594 Use plain English descriptors instead of script names for icon link destinations
+ rfe #1541 Disable foreign key checks for some operations
- bug #4957 "With selected" links doesn't work in table browse
- bug #4795 Query builder: missing joint for the intermediary table
4.4.10.0 (not yet released)
- bug #4950 Issues in database selection for replication

View File

@ -1304,23 +1304,23 @@ class PMA_DbQbe
/**
* Provides UNIQUE columns and INDEX columns present in criteria tables
*
* @param array $all_tables Tables involved in the search
* @param array $all_columns Columns involved in the search
* @param array $search_tables Tables involved in the search
* @param array $search_columns Columns involved in the search
* @param array $where_clause_columns Columns having criteria where clause
*
* @return array having UNIQUE and INDEX columns
*/
private function _getIndexes($all_tables, $all_columns,
private function _getIndexes($search_tables, $search_columns,
$where_clause_columns
) {
$unique_columns = array();
$index_columns = array();
foreach ($all_tables as $table) {
foreach ($search_tables as $table) {
$indexes = $GLOBALS['dbi']->getTableIndexes($this->_db, $table);
foreach ($indexes as $index) {
$column = $table . '.' . $index['Column_name'];
if (isset($all_columns[$column])) {
if (isset($search_columns[$column])) {
if ($index['Non_unique'] == 0) {
if (isset($where_clause_columns[$column])) {
$unique_columns[$column] = 'Y';
@ -1347,27 +1347,27 @@ class PMA_DbQbe
/**
* Provides UNIQUE columns and INDEX columns present in criteria tables
*
* @param array $all_tables Tables involved in the search
* @param array $all_columns Columns involved in the search
* @param array $search_tables Tables involved in the search
* @param array $search_columns Columns involved in the search
* @param array $where_clause_columns Columns having criteria where clause
*
* @return array having UNIQUE and INDEX columns
*/
private function _getLeftJoinColumnCandidates($all_tables, $all_columns,
private function _getLeftJoinColumnCandidates($search_tables, $search_columns,
$where_clause_columns
) {
$GLOBALS['dbi']->selectDb($this->_db);
// Get unique columns and index columns
$indexes = $this->_getIndexes(
$all_tables, $all_columns, $where_clause_columns
$search_tables, $search_columns, $where_clause_columns
);
$unique_columns = $indexes['unique'];
$index_columns = $indexes['index'];
list($candidate_columns, $needsort)
= $this->_getLeftJoinColumnCandidatesBest(
$all_tables, $where_clause_columns, $unique_columns, $index_columns
$search_tables, $where_clause_columns, $unique_columns, $index_columns
);
// If we came up with $unique_columns (very good) or $index_columns (still
@ -1403,14 +1403,14 @@ class PMA_DbQbe
/**
* Provides the main table to form the LEFT JOIN clause
*
* @param array $all_tables Tables involved in the search
* @param array $all_columns Columns involved in the search
* @param array $search_tables Tables involved in the search
* @param array $search_columns Columns involved in the search
* @param array $where_clause_columns Columns having criteria where clause
* @param array $where_clause_tables Tables having criteria where clause
*
* @return string table name
*/
private function _getMasterTable($all_tables, $all_columns,
private function _getMasterTable($search_tables, $search_columns,
$where_clause_columns, $where_clause_tables
) {
if (count($where_clause_tables) == 1) {
@ -1425,7 +1425,7 @@ class PMA_DbQbe
// because he is using one of his databases as pmadb,
// the last db selected is not always the one where we need to work)
$candidate_columns = $this->_getLeftJoinColumnCandidates(
$all_tables, $all_columns, $where_clause_columns
$search_tables, $search_columns, $where_clause_columns
);
// Generally, we need to display all the rows of foreign (referenced)
@ -1537,27 +1537,27 @@ class PMA_DbQbe
$from_clause = '';
if (isset($_POST['criteriaColumn']) && count($_POST['criteriaColumn']) > 0) {
// Initialize some variables
$all_tables = $all_columns = array();
$search_tables = $search_columns = array();
// We only start this if we have fields, otherwise it would be dumb
foreach ($_POST['criteriaColumn'] as $value) {
$parts = explode('.', $value);
if (! empty($parts[0]) && ! empty($parts[1])) {
$table = str_replace('`', '', $parts[0]);
$all_tables[$table] = $table;
$all_columns[] = $table . '.' . str_replace('`', '', $parts[1]);
$search_tables[$table] = $table;
$search_columns[] = $table . '.' . str_replace('`', '', $parts[1]);
}
} // end while
// Create LEFT JOINS out of Relations
$from_clause = $this->_getJoinForFromClause($all_tables, $all_columns);
$from_clause = $this->_getJoinForFromClause($search_tables, $search_columns);
// 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)
", ", array_map('PMA_Util::backquote', $search_tables)
);
}
} // end count($_POST['criteriaColumn']) > 0
@ -1568,50 +1568,19 @@ class PMA_DbQbe
/**
* Formulates the WHERE clause by JOINing tables
*
* @param array $allTables Tables involved in the search
* @param array $allColumns Columns involved in the search
* @param array $searchTables Tables involved in the search
* @param array $searchColumns Columns involved in the search
*
* @return string table name
*/
private function _getJoinForFromClause($allTables, $allColumns)
private function _getJoinForFromClause($searchTables, $searchColumns)
{
// $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']);
}
}
foreach ($searchTables as $oneTable) {
$this->_loadRelationsForTable($relations, $oneTable);
}
// Get tables and columns with valid where clauses
@ -1621,7 +1590,7 @@ class PMA_DbQbe
// Get master table
$master = $this->_getMasterTable(
$allTables, $allColumns,
$searchTables, $searchColumns,
$whereClauseColumns, $whereClauseTables
);
@ -1632,47 +1601,72 @@ class PMA_DbQbe
// Add master tables
$finalized[$master] = '';
}
// Fill the $finalized array with JOIN clauses for each table
$this->_fillJoinClauses($finalized, $relations, $searchTables);
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;
// JOIN clause
$join = '';
// 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;
// Tables that can not be combined with the table cluster
// which includes master table
$unfinalized = array_diff($searchTables, array_keys($finalized));
if (count($unfinalized) > 0) {
// We are done if all tables are in $finalized
if (count($finalized) == count($allTables)) {
break 3;
// We need to look for intermediary tables to JOIN unfinalized tables
// Heuristic to chose intermediary tables is to look for tables
// having relationships with unfinalized tables
foreach ($unfinalized as $oneTable) {
$references = PMA_getChildReferences($this->_db, $oneTable);
foreach ($references as $column => $columnReferences) {
foreach ($columnReferences as $reference) {
// Only from this schema
if ($reference['table_schema'] == $this->_db) {
$table = $reference['table_name'];
$this->_loadRelationsForTable($relations, $table);
// Make copies
$tempFinalized = $finalized;
$tempSearchTables = $searchTables;
$tempSearchTables[] = $table;
// Try joining with the added table
$this->_fillJoinClauses(
$tempFinalized, $relations, $tempSearchTables
);
$tempUnfinalized = array_diff(
$tempSearchTables, array_keys($tempFinalized)
);
// Take greedy approach.
// If the unfinalized count drops we keep the new table
// and switch temporary varibles with the original ones
if (count($tempUnfinalized) < count($unfinalized)) {
$finalized = $tempFinalized;
$searchTables = $tempSearchTables;
}
// We are done if no unfinalized tables anymore
if (count($tempUnfinalized) == 0) {
break 3;
}
}
}
}
}
// If no new tables were added during this iteration, break;
if (! $added) {
break;
$unfinalized = array_diff($searchTables, array_keys($finalized));
// If there are still unfinalized tables
if (count($unfinalized) > 0) {
// Add these tables as cartesian product before joined tables
$join .= implode(
', ', array_map('PMA_Util::backquote', $unfinalized)
);
}
}
// 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) {
@ -1692,6 +1686,89 @@ class PMA_DbQbe
return $join;
}
/**
* Loads relations for a given table into the $relations array
*
* @param array $relations array of relations
* @param string $oneTable the table
*
* @return void
*/
private function _loadRelationsForTable(&$relations, $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']);
}
}
}
/**
* Fills the $finalized arrays with JOIN clauses for each of the tables
*
* @param array $finalized JOIN clauses for each table
* @param array $relations Relations among tables
* @param array $searchTables Tables involved in the search
*
* @return void
*/
private function _fillJoinClauses(&$finalized, $relations, $searchTables)
{
while (true) {
$added = false;
foreach ($searchTables as $masterTable) {
$foreignData = $relations[$masterTable];
foreach ($foreignData as $foreignTable => $clause) {
if (! isset($finalized[$masterTable])
&& isset($finalized[$foreignTable])
) {
$finalized[$masterTable] = $clause;
$added = true;
} elseif (! isset($finalized[$foreignTable])
&& isset($finalized[$masterTable])
&& in_array($foreignTable, $searchTables)
) {
$finalized[$foreignTable] = $clause;
$added = true;
}
if ($added) {
// We are done if all tables are in $finalized
if (count($finalized) == count($searchTables)) {
return;
}
}
}
}
// If no new tables were added during this iteration, break;
if (! $added) {
return;
}
}
}
/**
* Provides the generated SQL query
*
@ -1859,7 +1936,7 @@ class PMA_DbQbe
/**
* Get best
*
* @param array $all_tables All tables
* @param array $search_tables Tables involved in the search
* @param array $where_clause_columns Columns with where clause
* @param array $unique_columns Unique columns
* @param array $index_columns Indexed columns
@ -1867,7 +1944,7 @@ class PMA_DbQbe
* @return array
*/
private function _getLeftJoinColumnCandidatesBest(
$all_tables, $where_clause_columns, $unique_columns, $index_columns
$search_tables, $where_clause_columns, $unique_columns, $index_columns
) {
// now we want to find the best.
if (isset($unique_columns) && count($unique_columns) > 0) {
@ -1883,7 +1960,7 @@ class PMA_DbQbe
$needsort = 0;
return array($candidate_columns, $needsort);
} else {
$candidate_columns = $all_tables;
$candidate_columns = $search_tables;
$needsort = 0;
return array($candidate_columns, $needsort);
}