diff --git a/ChangeLog b/ChangeLog
index ba7afa0875..eff0b13e05 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -5,6 +5,11 @@ phpMyAdmin - ChangeLog
- issue #17842 Change js.cookie.js to js.cookie.min.js
- issue #17632 Improve tab keypress to text fields on the login form
+5.2.2 (not yet released)
+- issue #17028 Fix total count of rows in not accurate
+- issue #17683 Fix total count of rows in not accurate
+- issue #17766 Allow to open in a new tab copy and edit row actions
+
5.2.1 (2023-02-07)
- issue #17522 Fix case where the routes cache file is invalid
- issue #17506 Fix error when configuring 2FA without XMLWriter or Imagick
diff --git a/js/src/drag_drop_import.js b/js/src/drag_drop_import.js
index e9d405b3d8..5cf8758c4d 100644
--- a/js/src/drag_drop_import.js
+++ b/js/src/drag_drop_import.js
@@ -134,7 +134,7 @@ var DragDropImport = {
var filename = $this.parent('span').attr('data-filename');
$('body').append('
' +
window.Messages.dropImportImportResultHeader + ' - ' +
- Functions.escapeHtml(filename) + 'x
' + value.message + '');
+ escapeHtml(filename) + 'x' + value.message + '');
$('.pma_drop_result').draggable(); // to make this dialog draggable
}
});
diff --git a/libraries/classes/Controllers/Table/ChangeController.php b/libraries/classes/Controllers/Table/ChangeController.php
index 0ee8d6e7c2..c3d617364c 100644
--- a/libraries/classes/Controllers/Table/ChangeController.php
+++ b/libraries/classes/Controllers/Table/ChangeController.php
@@ -7,6 +7,7 @@ namespace PhpMyAdmin\Controllers\Table;
use PhpMyAdmin\Config\PageSettings;
use PhpMyAdmin\ConfigStorage\Relation;
use PhpMyAdmin\Controllers\AbstractController;
+use PhpMyAdmin\Core;
use PhpMyAdmin\DbTableExists;
use PhpMyAdmin\Html\Generator;
use PhpMyAdmin\Http\ServerRequest;
@@ -82,6 +83,12 @@ class ChangeController extends AbstractController
DbTableExists::check($GLOBALS['db'], $GLOBALS['table']);
+ if (isset($_GET['where_clause'], $_GET['where_clause_signature'])) {
+ if (Core::checkSqlQuerySignature($_GET['where_clause'], $_GET['where_clause_signature'])) {
+ $where_clause = $_GET['where_clause'];
+ }
+ }
+
/**
* Determine whether Insert or Edit and set global variables
*/
diff --git a/libraries/classes/Controllers/Table/ReplaceController.php b/libraries/classes/Controllers/Table/ReplaceController.php
index 37da83c735..6dc5a4a511 100644
--- a/libraries/classes/Controllers/Table/ReplaceController.php
+++ b/libraries/classes/Controllers/Table/ReplaceController.php
@@ -290,7 +290,7 @@ final class ReplaceController extends AbstractController
$valueSets[] = implode(', ', $queryValues);
} else {
// build update query
- $clauseIsUnique = $_POST['clause_is_unique'] ?? '';// Should contain 0 or 1
+ $clauseIsUnique = $_POST['clause_is_unique'] ?? $_GET['clause_is_unique'] ?? '';// Should contain 0 or 1
$GLOBALS['query'][] = 'UPDATE ' . Util::backquote($GLOBALS['table'])
. ' SET ' . implode(', ', $queryValues)
. ' WHERE ' . $where_clause
diff --git a/libraries/classes/Display/Results.php b/libraries/classes/Display/Results.php
index cb37fa5732..103dfb6b27 100644
--- a/libraries/classes/Display/Results.php
+++ b/libraries/classes/Display/Results.php
@@ -2735,8 +2735,10 @@ class Results
'db' => $this->properties['db'],
'table' => $this->properties['table'],
'where_clause' => $whereClause,
+ 'where_clause_signature' => Core::signSqlQuery($whereClause),
'clause_is_unique' => $clauseIsUnique,
'sql_query' => $urlSqlQuery,
+ 'sql_signature' => Core::signSqlQuery($urlSqlQuery),
'goto' => Url::getFromRoute('/sql'),
];
diff --git a/libraries/classes/InsertEdit.php b/libraries/classes/InsertEdit.php
index aeee5f84bc..5917da306d 100644
--- a/libraries/classes/InsertEdit.php
+++ b/libraries/classes/InsertEdit.php
@@ -135,6 +135,13 @@ class InsertEdit
'err_url' => $errorUrl,
'sql_query' => $_POST['sql_query'] ?? '',
];
+
+ if ($formParams['sql_query'] === '' && isset($_GET['sql_query'], $_GET['sql_signature'])) {
+ if (Core::checkSqlQuerySignature($_GET['sql_query'], $_GET['sql_signature'])) {
+ $formParams['sql_query'] = $_GET['sql_query'];
+ }
+ }
+
if (isset($whereClauses)) {
foreach ($whereClauseArray as $keyId => $whereClause) {
$formParams['where_clause[' . $keyId . ']'] = trim($whereClause);
@@ -143,6 +150,8 @@ class InsertEdit
if (isset($_POST['clause_is_unique'])) {
$formParams['clause_is_unique'] = $_POST['clause_is_unique'];
+ } elseif (isset($_GET['clause_is_unique'])) {
+ $formParams['clause_is_unique'] = $_GET['clause_is_unique'];
}
return $formParams;
diff --git a/libraries/classes/Sql.php b/libraries/classes/Sql.php
index 951ed97e2d..ee4fb08385 100644
--- a/libraries/classes/Sql.php
+++ b/libraries/classes/Sql.php
@@ -15,6 +15,7 @@ use PhpMyAdmin\Html\Generator;
use PhpMyAdmin\Html\MySQLDocumentation;
use PhpMyAdmin\Query\Generator as QueryGenerator;
use PhpMyAdmin\Query\Utilities;
+use PhpMyAdmin\SqlParser\Components\Expression;
use PhpMyAdmin\SqlParser\Statements\AlterStatement;
use PhpMyAdmin\SqlParser\Statements\DropStatement;
use PhpMyAdmin\SqlParser\Statements\SelectStatement;
@@ -707,24 +708,24 @@ class Sql
}
} else {
$statement = $statementInfo->statement;
- $tokenList = $statementInfo->parser->list;
- $replaces = [
- // Remove ORDER BY to decrease unnecessary sorting time
- [
- 'ORDER BY',
- '',
- ],
- // Removes LIMIT clause that might have been added
- [
- 'LIMIT',
- '',
- ],
- ];
- $countQuery = 'SELECT COUNT(*) FROM (' . Query::replaceClauses(
- $statement,
- $tokenList,
- $replaces
- ) . ') as cnt';
+
+ // Remove ORDER BY to decrease unnecessary sorting time
+ if ($statementInfo->order !== false) {
+ $statement->order = null;
+ }
+
+ // Removes LIMIT clause that might have been added
+ if ($statementInfo->limit !== null) {
+ $statement->limit = null;
+ }
+
+ if ($statementInfo->isGroup === false && count($statement->expr) === 1) {
+ $statement->expr[0] = new Expression();
+ $statement->expr[0]->expr = '1';
+ }
+
+ $countQuery = 'SELECT COUNT(*) FROM (' . $statement->build() . ' ) as cnt';
+
$unlimNumRows = $this->dbi->fetchValue($countQuery);
if ($unlimNumRows === false) {
$unlimNumRows = 0;
diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon
index d151b26f6a..268ae07aa5 100644
--- a/phpstan-baseline.neon
+++ b/phpstan-baseline.neon
@@ -7960,6 +7960,21 @@ parameters:
count: 1
path: libraries/classes/Setup/Index.php
+ -
+ message: "#^Access to an undefined property PhpMyAdmin\\\\SqlParser\\\\Statement\\:\\:\\$expr\\.$#"
+ count: 2
+ path: libraries/classes/Sql.php
+
+ -
+ message: "#^Access to an undefined property PhpMyAdmin\\\\SqlParser\\\\Statement\\:\\:\\$limit\\.$#"
+ count: 1
+ path: libraries/classes/Sql.php
+
+ -
+ message: "#^Access to an undefined property PhpMyAdmin\\\\SqlParser\\\\Statement\\:\\:\\$order\\.$#"
+ count: 1
+ path: libraries/classes/Sql.php
+
-
message: "#^Method PhpMyAdmin\\\\Sql\\:\\:countQueryResults\\(\\) should return int\\|numeric\\-string but returns mixed\\.$#"
count: 1
@@ -8100,11 +8115,6 @@ parameters:
count: 2
path: libraries/classes/Sql.php
- -
- message: "#^Parameter \\#2 \\$list of static method PhpMyAdmin\\\\SqlParser\\\\Utils\\\\Query\\:\\:replaceClauses\\(\\) expects PhpMyAdmin\\\\SqlParser\\\\TokensList, PhpMyAdmin\\\\SqlParser\\\\TokensList\\|null given\\.$#"
- count: 1
- path: libraries/classes/Sql.php
-
-
message: "#^Parameter \\#2 \\$table of method PhpMyAdmin\\\\Sql\\:\\:handleSortOrder\\(\\) expects string, string\\|null given\\.$#"
count: 1
diff --git a/psalm-baseline.xml b/psalm-baseline.xml
index cbdadc63e6..4c2eb55626 100644
--- a/psalm-baseline.xml
+++ b/psalm-baseline.xml
@@ -2874,9 +2874,15 @@
$GLOBALS['biggest_max_file_size']
-
+
$GLOBALS['current_result']
+ $_GET['where_clause']
+ $_GET['where_clause_signature']
+
+ $_GET['where_clause']
+ $_GET['where_clause_signature']
+
$GLOBALS['text_dir']
$isUpload
@@ -2884,6 +2890,9 @@
$GLOBALS['biggest_max_file_size'] > 0
+
+ $where_clause
+
@@ -7594,14 +7603,18 @@
-
+
+ $_GET['sql_query']
+ $_GET['sql_signature']
$whereClause
$whereClause
$_POST['fields']['multi_edit']
-
+
+ $_GET['sql_query']
+ $_GET['sql_signature']
$whereClause
@@ -12175,6 +12188,11 @@
$row['#']
+
+
+ 0 !== --$sortableColCount
+
+
$config->getCookie('phpMyAdmin')
@@ -12276,7 +12294,7 @@
$unlimNumRows
-
+
$columns[$indexColumnName]['Extra']
$extraData['error']
$fieldInfoResult[0]['Type']
@@ -12285,6 +12303,7 @@
$oneResult['Duration']
$oneResult['Status']
$sortCol
+ $statement->expr
$unlimNumRows
$unlimNumRows
Message::sanitize($warning)
@@ -12311,9 +12330,10 @@
$oneResult['Status']
$statementInfo->statement->where[0]
-
+
$_SESSION['tmpval']['pos']
$_SESSION['tmpval']['possible_as_geometry']
+ $statement->expr[0]
$maxRows
@@ -12384,18 +12404,28 @@
$statement->altered[0]->field->column
-
+
$result
$row[0]
$statementInfo->parser->list
$statementInfo->parser->list
$statementInfo->parser->list
$table
- $tokenList
(bool) $GLOBALS['cfg']['ShowSQL']
+
+ $statementInfo->limit !== null
+
+
+ $statement->expr
+ $statement->limit
+ $statement->order
+
+
+ $statement->expr
+
diff --git a/test/classes/Command/TwigLintCommandTest.php b/test/classes/Command/TwigLintCommandTest.php
index b97d2cd703..a02ab14837 100644
--- a/test/classes/Command/TwigLintCommandTest.php
+++ b/test/classes/Command/TwigLintCommandTest.php
@@ -14,9 +14,9 @@ use function class_exists;
use function sort;
use const DIRECTORY_SEPARATOR;
-use const ROOT_PATH;
use const SORT_NATURAL;
use const SORT_REGULAR;
+use const TEST_PATH;
/**
* @covers \PhpMyAdmin\Command\TwigLintCommand
@@ -42,7 +42,7 @@ class TwigLintCommandTest extends AbstractTestCase
public function testGetTemplateContents(): void
{
$contents = $this->callFunction($this->command, TwigLintCommand::class, 'getTemplateContents', [
- ROOT_PATH . 'test/classes/_data/file_listing/subfolder/one.ini',
+ TEST_PATH . 'test/classes/_data/file_listing/subfolder/one.ini',
]);
$this->assertSame('key=value' . "\n", $contents);
@@ -50,7 +50,7 @@ class TwigLintCommandTest extends AbstractTestCase
public function testFindFiles(): void
{
- $path = ROOT_PATH . 'test/classes/_data/file_listing';
+ $path = TEST_PATH . 'test/classes/_data/file_listing';
$filesFound = $this->callFunction($this->command, TwigLintCommand::class, 'findFiles', [$path]);
// Sort results to avoid file system test specific failures
@@ -66,7 +66,7 @@ class TwigLintCommandTest extends AbstractTestCase
public function testGetFilesInfo(): void
{
- $path = ROOT_PATH . 'test/classes/_data/file_listing';
+ $path = TEST_PATH . 'test/classes/_data/file_listing';
$filesInfos = $this->callFunction($this->command, TwigLintCommand::class, 'getFilesInfo', [$path]);
// Sort results to avoid file system test specific failures
@@ -120,7 +120,7 @@ class TwigLintCommandTest extends AbstractTestCase
->willReturnOnConsecutiveCalls('{{ file }}', '{{ file }');
$filesFound = $this->callFunction($command, TwigLintCommand::class, 'getFilesInfo', [
- ROOT_PATH . 'test/classes/_data/file_listing',
+ TEST_PATH . 'test/classes/_data/file_listing',
]);
$this->assertEquals([
diff --git a/test/classes/InsertEditTest.php b/test/classes/InsertEditTest.php
index 162647a59f..c9abc7a111 100644
--- a/test/classes/InsertEditTest.php
+++ b/test/classes/InsertEditTest.php
@@ -153,6 +153,43 @@ class InsertEditTest extends AbstractTestCase
);
}
+ /**
+ * Test for getFormParametersForInsertForm
+ */
+ public function testGetFormParametersForInsertFormGet(): void
+ {
+ $where_clause = [
+ 'foo' => 'bar ',
+ '1' => ' test',
+ ];
+ $_GET['clause_is_unique'] = false;
+ $_GET['sql_query'] = 'SELECT a';
+ $_GET['sql_signature'] = Core::signSqlQuery($_GET['sql_query']);
+ $GLOBALS['goto'] = 'index.php';
+
+ $result = $this->insertEdit->getFormParametersForInsertForm(
+ 'dbname',
+ 'tablename',
+ [],
+ $where_clause,
+ 'localhost'
+ );
+
+ $this->assertEquals(
+ [
+ 'db' => 'dbname',
+ 'table' => 'tablename',
+ 'goto' => 'index.php',
+ 'err_url' => 'localhost',
+ 'sql_query' => 'SELECT a',
+ 'where_clause[foo]' => 'bar',
+ 'where_clause[1]' => 'test',
+ 'clause_is_unique' => false,
+ ],
+ $result
+ );
+ }
+
/**
* Test for getWhereClauseArray
*/
diff --git a/test/classes/SqlTest.php b/test/classes/SqlTest.php
index cbe24d7d47..c72be2e63c 100644
--- a/test/classes/SqlTest.php
+++ b/test/classes/SqlTest.php
@@ -371,9 +371,9 @@ class SqlTest extends AbstractTestCase
164056,
50,
false,
- 'SELECT COUNT(*) FROM (select * from game_auth_logs l join ('
+ 'SELECT COUNT(*) FROM (SELECT 1 FROM game_auth_logs AS `l` JOIN ('
. ' select al.user_id, max(al.id) as id from game_auth_logs al '
- . 'where al.successfull = 1 group by al.user_id ) last_log on last_log.id = l.id'
+ . 'where al.successfull = 1 group by al.user_id ) AS `last_log` ON last_log.id = l.id'
. ' ) as cnt',
],
'join on SELECT results with alias.*' => [
@@ -385,9 +385,9 @@ class SqlTest extends AbstractTestCase
267,
50,
false,
- 'SELECT COUNT(*) FROM (select l.* from game_auth_logs l join ('
+ 'SELECT COUNT(*) FROM (SELECT 1 FROM game_auth_logs AS `l` JOIN ('
. ' select al.user_id, max(al.id) as id from game_auth_logs al '
- . 'where al.successfull = 1 group by al.user_id ) last_log on last_log.id = l.id'
+ . 'where al.successfull = 1 group by al.user_id ) AS `last_log` ON last_log.id = l.id'
. ' ) as cnt',
],
[
diff --git a/test/classes/Stubs/DbiDummy.php b/test/classes/Stubs/DbiDummy.php
index 362993ec11..58d2140bbe 100644
--- a/test/classes/Stubs/DbiDummy.php
+++ b/test/classes/Stubs/DbiDummy.php
@@ -2742,17 +2742,17 @@ class DbiDummy implements DbiExtension
'result' => [['hostname', 'username', 'password']],
],
[
- 'query' => 'SELECT COUNT(*) FROM (SELECT * FROM company_users WHERE not_working_count != 0 ) as cnt',
+ 'query' => 'SELECT COUNT(*) FROM (SELECT 1 FROM company_users WHERE not_working_count != 0 ) as cnt',
'result' => false,
],
[
- 'query' => 'SELECT COUNT(*) FROM (SELECT * FROM company_users ) as cnt',
+ 'query' => 'SELECT COUNT(*) FROM (SELECT 1 FROM company_users ) as cnt',
'result' => [
[4],
],
],
[
- 'query' => 'SELECT COUNT(*) FROM (SELECT * FROM company_users WHERE working_count = 0 ) as cnt',
+ 'query' => 'SELECT COUNT(*) FROM (SELECT 1 FROM company_users WHERE working_count = 0 ) as cnt',
'result' => [
[15],
],
@@ -2765,8 +2765,8 @@ class DbiDummy implements DbiExtension
],
[
'query' => 'SELECT COUNT(*) FROM ('
- . 'SELECT *, 1, (SELECT COUNT(*) FROM tbl1) as c1, '
- . '(SELECT 1 FROM tbl2) as c2 FROM company_users WHERE subquery_case = 0 ) as cnt',
+ . 'SELECT *, 1, (SELECT COUNT(*) FROM tbl1) AS `c1`, '
+ . '(SELECT 1 FROM tbl2) AS `c2` FROM company_users WHERE subquery_case = 0 ) as cnt',
'result' => [
[42],
],
diff --git a/test/classes/ThemeTest.php b/test/classes/ThemeTest.php
index 10a371e3f4..35c8491811 100644
--- a/test/classes/ThemeTest.php
+++ b/test/classes/ThemeTest.php
@@ -65,7 +65,7 @@ class ThemeTest extends AbstractTestCase
*/
public function testCheckImgPathIncorrect(): void
{
- $this->object->setPath(ROOT_PATH . 'test/classes/_data/incorrect_theme');
+ $this->object->setPath(TEST_PATH . 'test/classes/_data/incorrect_theme');
$this->assertFalse(
$this->object->loadInfo(),
'Theme name is not properly set'