Skip to content

Commit 8972c0e

Browse files
committed
Merge branch 'QA_5_2'
Signed-off-by: Maurício Meneghini Fauth <mauricio@fauth.dev>
2 parents 7514e75 + 0301e48 commit 8972c0e

12 files changed

Lines changed: 288 additions & 267 deletions

File tree

ChangeLog

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ phpMyAdmin - ChangeLog
2626
- issue #16451 Increase password characters limit to 2000 during login
2727
- issue Fix "IS NULL" is shown for non-nullable columns on search page
2828
- issue #16199 Fix dragging of tables in designer
29+
- issue #17702 Fix performance issue when handling large number of tables in a single database
2930

3031
5.2.1 (2023-02-07)
3132
- issue #17522 Fix case where the routes cache file is invalid

libraries/classes/DatabaseInterface.php

Lines changed: 28 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -367,6 +367,16 @@ public function getTablesFull(
367367
}
368368

369369
$tables = [];
370+
$paging_applied = false;
371+
372+
if ($limitCount && is_array($table) && $sortBy === 'Name') {
373+
if ($sortOrder === 'DESC') {
374+
$table = array_reverse($table);
375+
}
376+
377+
$table = array_slice($table, $limitOffset, $limitCount);
378+
$paging_applied = true;
379+
}
370380

371381
if (! $GLOBALS['cfg']['Server']['DisableIS']) {
372382
$sqlWhereTable = '';
@@ -398,7 +408,7 @@ public function getTablesFull(
398408
// Sort the tables
399409
$sql .= ' ORDER BY ' . $sortBy . ' ' . $sortOrder;
400410

401-
if ($limitCount) {
411+
if ($limitCount && ! $paging_applied) {
402412
$sql .= ' LIMIT ' . $limitCount . ' OFFSET ' . $limitOffset;
403413
}
404414

@@ -467,17 +477,26 @@ static function ($a, $b) {
467477
$tables[$oneDatabaseName] = $oneDatabaseTables;
468478
}
469479
}
480+
481+
// on windows with lower_case_table_names = 1
482+
// MySQL returns
483+
// with SHOW DATABASES or information_schema.SCHEMATA: `Test`
484+
// but information_schema.TABLES gives `test`
485+
// see https://github.com/phpmyadmin/phpmyadmin/issues/8402
486+
$tables = $tables[$database]
487+
?? $tables[mb_strtolower($database)]
488+
?? [];
470489
}
471490

472491
// If permissions are wrong on even one database directory,
473492
// information_schema does not return any table info for any database
474493
// this is why we fall back to SHOW TABLE STATUS even for MySQL >= 50002
475494
if ($tables === []) {
476495
$sql = 'SHOW TABLE STATUS FROM ' . Util::backquote($database);
477-
if ($table || ($tableIsGroup === true) || $tableType) {
496+
if (($table !== '' && $table !== []) || ($tableIsGroup === true) || $tableType) {
478497
$sql .= ' WHERE';
479498
$needAnd = false;
480-
if ($table || ($tableIsGroup === true)) {
499+
if (($table !== '' && $table !== []) || ($tableIsGroup === true)) {
481500
if (is_array($table)) {
482501
$sql .= ' `Name` IN ('
483502
. implode(
@@ -566,28 +585,16 @@ static function ($a, $b) {
566585
unset($sortValues);
567586
}
568587

569-
if ($limitCount) {
570-
$eachTables = array_slice($eachTables, $limitOffset, $limitCount);
588+
if ($limitCount && ! $paging_applied) {
589+
$eachTables = array_slice($eachTables, $limitOffset, $limitCount, true);
571590
}
572591

573-
$tables[$database] = Compatibility::getISCompatForGetTablesFull($eachTables, $database);
574-
}
575-
576-
// cache table data
577-
// so Table does not require to issue SHOW TABLE STATUS again
578-
$this->cache->cacheTableData($tables, $table);
579-
580-
if (isset($tables[$database])) {
581-
return $tables[$database];
592+
$tables = Compatibility::getISCompatForGetTablesFull($eachTables, $database);
582593
}
583594

584-
if (isset($tables[mb_strtolower($database)])) {
585-
// on windows with lower_case_table_names = 1
586-
// MySQL returns
587-
// with SHOW DATABASES or information_schema.SCHEMATA: `Test`
588-
// but information_schema.TABLES gives `test`
589-
// see https://github.com/phpmyadmin/phpmyadmin/issues/8402
590-
return $tables[mb_strtolower($database)];
595+
if ($tables !== []) {
596+
// cache table data, so Table does not require to issue SHOW TABLE STATUS again
597+
$this->cache->cacheTableData($database, $tables);
591598
}
592599

593600
return $tables;

libraries/classes/Export.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -674,6 +674,13 @@ public function exportDatabase(
674674

675675
$views = [];
676676

677+
if ($tables !== []) {
678+
// Prefetch table information to improve performance.
679+
// Table status will get saved in Query Cache,
680+
// and all instantiations of Table below should be much faster.
681+
$this->dbi->getTablesFull($db->getName(), $tables);
682+
}
683+
677684
foreach ($tables as $table) {
678685
$tableObject = new Table($table, $db->getName(), $this->dbi);
679686
// if this is a view, collect it for later;

libraries/classes/Query/Cache.php

Lines changed: 13 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -15,37 +15,27 @@
1515
*/
1616
class Cache
1717
{
18-
/** @var array Table data cache */
18+
/** @var array[] Table data cache */
1919
private array $tableCache = [];
2020

2121
/**
2222
* Caches table data so Table does not require to issue
2323
* SHOW TABLE STATUS again
2424
*
25-
* @param array $tables information for tables of some databases
26-
* @param string|bool $table table name
25+
* @param mixed[][] $tables information for tables of some databases
2726
*/
28-
public function cacheTableData(array $tables, string|bool $table): void
27+
public function cacheTableData(string $database, array $tables): void
2928
{
30-
// Note: I don't see why we would need array_merge_recursive() here,
31-
// as it creates double entries for the same table (for example a double
32-
// entry for Comment when changing the storage engine in Operations)
33-
// Note 2: Instead of array_merge(), simply use the + operator because
34-
// array_merge() renumbers numeric keys starting with 0, therefore
35-
// we would lose a db name that consists only of numbers
36-
37-
foreach ($tables as $one_database => $tablesInDatabase) {
38-
if (isset($this->tableCache[$one_database])) {
39-
// the + operator does not do the intended effect
40-
// when the cache for one table already exists
41-
if ($table && isset($this->tableCache[$one_database][$table])) {
42-
unset($this->tableCache[$one_database][$table]);
43-
}
44-
45-
$this->tableCache[$one_database] += $tablesInDatabase;
46-
} else {
47-
$this->tableCache[$one_database] = $tablesInDatabase;
48-
}
29+
// Note: This function must not use array_merge because numerical indices must be preserved.
30+
// When an entry already exists for the database in cache, we merge the incoming data with existing data.
31+
// The union operator appends elements from right to left unless they exists on the left already.
32+
// Doing the union with incoming data on the left ensures that when we reread table status from DB,
33+
// we overwrite whatever was in cache with the new data.
34+
35+
if (isset($this->tableCache[$database])) {
36+
$this->tableCache[$database] = $tables + $this->tableCache[$database];
37+
} else {
38+
$this->tableCache[$database] = $tables;
4939
}
5040
}
5141

libraries/classes/Query/Compatibility.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
*/
2020
class Compatibility
2121
{
22+
/** @return mixed[][] */
2223
public static function getISCompatForGetTablesFull(array $eachTables, string $eachDatabase): array
2324
{
2425
foreach ($eachTables as $table_name => $_) {

libraries/classes/Tracking/Tracker.php

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@
88
namespace PhpMyAdmin\Tracking;
99

1010
use PhpMyAdmin\Cache;
11+
use PhpMyAdmin\ConfigStorage\Features\TrackingFeature;
1112
use PhpMyAdmin\ConfigStorage\Relation;
13+
use PhpMyAdmin\DatabaseInterface;
1214
use PhpMyAdmin\Dbal\Connection;
1315
use PhpMyAdmin\Plugins;
1416
use PhpMyAdmin\Plugins\Export\ExportSql;
@@ -820,6 +822,16 @@ public static function handleQuery(string $query): void
820822
return;
821823
}
822824

825+
$relation = new Relation($GLOBALS['dbi']);
826+
$trackingFeature = $relation->getRelationParameters()->trackingFeature;
827+
if ($trackingFeature === null) {
828+
return;
829+
}
830+
831+
if (! self::isAnyTrackingInProgress($GLOBALS['dbi'], $trackingFeature, $dbname)) {
832+
return;
833+
}
834+
823835
// Get some information about query
824836
$result = self::parseQuery($query);
825837

@@ -920,4 +932,19 @@ public static function handleQuery(string $query): void
920932

921933
$GLOBALS['dbi']->queryAsControlUser($sqlQuery);
922934
}
935+
936+
private static function isAnyTrackingInProgress(
937+
DatabaseInterface $dbi,
938+
TrackingFeature $trackingFeature,
939+
string $dbname,
940+
): bool {
941+
$sqlQuery = sprintf(
942+
'/*NOTRACK*/ SELECT 1 FROM %s.%s WHERE tracking_active = 1 AND db_name = %s LIMIT 1',
943+
Util::backquote($trackingFeature->database),
944+
Util::backquote($trackingFeature->tracking),
945+
"'" . $dbi->escapeString($dbname, Connection::TYPE_CONTROL) . "'",
946+
);
947+
948+
return $dbi->queryAsControlUser($sqlQuery)->fetchValue() !== false;
949+
}
923950
}

libraries/classes/Util.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1999,7 +1999,7 @@ public static function getDbInfo(ServerRequest $request, string $db, bool $isRes
19991999
// We must use union operator here instead of array_merge to preserve numerical keys
20002000
$tables = $groupTable + $GLOBALS['dbi']->getTablesFull(
20012001
$db,
2002-
$groupWithSeparator !== false ? $groupWithSeparator : '',
2002+
$groupWithSeparator !== false ? $groupWithSeparator : $tables,
20032003
$groupWithSeparator !== false,
20042004
$limitOffset,
20052005
$limitCount,

phpstan-baseline.neon

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3445,11 +3445,6 @@ parameters:
34453445
count: 2
34463446
path: libraries/classes/DatabaseInterface.php
34473447

3448-
-
3449-
message: "#^Parameter \\#2 \\$table of method PhpMyAdmin\\\\Query\\\\Cache\\:\\:cacheTableData\\(\\) expects bool\\|string, array\\|string given\\.$#"
3450-
count: 1
3451-
path: libraries/classes/DatabaseInterface.php
3452-
34533448
-
34543449
message: "#^Parameter \\#2 \\$tableName of static method PhpMyAdmin\\\\StorageEngine\\:\\:getMroongaLengths\\(\\) expects string, \\(int\\|string\\) given\\.$#"
34553450
count: 1
@@ -7115,11 +7110,6 @@ parameters:
71157110
count: 1
71167111
path: libraries/classes/Query/Cache.php
71177112

7118-
-
7119-
message: "#^Method PhpMyAdmin\\\\Query\\\\Cache\\:\\:cacheTableData\\(\\) has parameter \\$tables with no value type specified in iterable type array\\.$#"
7120-
count: 1
7121-
path: libraries/classes/Query/Cache.php
7122-
71237113
-
71247114
message: "#^Method PhpMyAdmin\\\\Query\\\\Cache\\:\\:getCache\\(\\) return type has no value type specified in iterable type array\\.$#"
71257115
count: 1
@@ -7150,11 +7140,6 @@ parameters:
71507140
count: 1
71517141
path: libraries/classes/Query/Compatibility.php
71527142

7153-
-
7154-
message: "#^Method PhpMyAdmin\\\\Query\\\\Compatibility\\:\\:getISCompatForGetTablesFull\\(\\) return type has no value type specified in iterable type array\\.$#"
7155-
count: 1
7156-
path: libraries/classes/Query/Compatibility.php
7157-
71587143
-
71597144
message: "#^Method PhpMyAdmin\\\\Query\\\\Utilities\\:\\:usortComparisonCallback\\(\\) has parameter \\$a with no value type specified in iterable type array\\.$#"
71607145
count: 1

psalm-baseline.xml

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6213,9 +6213,6 @@
62136213
<NullableReturnStatement>
62146214
<code>$user</code>
62156215
</NullableReturnStatement>
6216-
<PossiblyInvalidArgument>
6217-
<code>$table</code>
6218-
</PossiblyInvalidArgument>
62196216
<PossiblyInvalidArrayOffset>
62206217
<code>$row[$value]</code>
62216218
</PossiblyInvalidArrayOffset>
@@ -11972,9 +11969,6 @@
1197211969
</MixedReturnTypeCoercion>
1197311970
</file>
1197411971
<file src="libraries/classes/Query/Cache.php">
11975-
<MixedArrayAccess>
11976-
<code><![CDATA[$this->tableCache[$one_database][$table]]]></code>
11977-
</MixedArrayAccess>
1197811972
<MixedArrayOffset>
1197911973
<code>$loc[$key]</code>
1198011974
<code>$loc[$key]</code>
@@ -11984,11 +11978,7 @@
1198411978
<code>$key</code>
1198511979
<code>$loc</code>
1198611980
<code>$loc[array_shift($contentPath)]</code>
11987-
<code>$tablesInDatabase</code>
1198811981
</MixedAssignment>
11989-
<MixedOperand>
11990-
<code><![CDATA[$this->tableCache[$one_database]]]></code>
11991-
</MixedOperand>
1199211982
<PossiblyUnusedMethod>
1199311983
<code>getCache</code>
1199411984
</PossiblyUnusedMethod>
@@ -12046,6 +12036,10 @@
1204612036
<code>$colCollation</code>
1204712037
<code>$colType</code>
1204812038
</MixedAssignment>
12039+
<MixedReturnTypeCoercion>
12040+
<code>$eachTables</code>
12041+
<code>mixed[][]</code>
12042+
</MixedReturnTypeCoercion>
1204912043
</file>
1205012044
<file src="libraries/classes/Query/Utilities.php">
1205112045
<MixedArrayAssignment>
@@ -13665,6 +13659,7 @@
1366513659
<code>escapeString</code>
1366613660
<code>escapeString</code>
1366713661
<code>escapeString</code>
13662+
<code>escapeString</code>
1366813663
</DeprecatedMethod>
1366913664
<InvalidArrayOffset>
1367013665
<code><![CDATA[$GLOBALS['export_type']]]></code>

templates/home/index.twig

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@
8888
</div>
8989
{% endif %}
9090

91+
{% if available_languages is not empty or has_theme_manager %}
9192
<div class="card mt-4">
9293
<div class="card-header">
9394
{% trans 'Appearance settings' %}
@@ -168,6 +169,7 @@
168169
{% endif %}
169170
</ul>
170171
</div>
172+
{% endif %}
171173
</div>
172174

173175
<div class="col-lg-5 col-12">

0 commit comments

Comments
 (0)