Merge pull request #19487 from kamil-tekiela/Improvements-to-DatabaseInterface
Improvements to database interface
This commit is contained in:
commit
37018dbd5c
@ -6684,7 +6684,7 @@ parameters:
|
||||
-
|
||||
message: '#^Only booleans are allowed in a negated boolean, PhpMyAdmin\\Dbal\\ResultInterface\|false given\.$#'
|
||||
identifier: booleanNot.exprNotBoolean
|
||||
count: 3
|
||||
count: 2
|
||||
path: src/Dbal/DatabaseInterface.php
|
||||
|
||||
-
|
||||
@ -10170,7 +10170,7 @@ parameters:
|
||||
Use dependency injection instead\.$#
|
||||
'''
|
||||
identifier: staticMethod.deprecated
|
||||
count: 7
|
||||
count: 8
|
||||
path: src/Navigation/Nodes/Node.php
|
||||
|
||||
-
|
||||
|
||||
@ -5993,6 +5993,7 @@
|
||||
<code><![CDATA[DatabaseInterface::getInstance()]]></code>
|
||||
<code><![CDATA[DatabaseInterface::getInstance()]]></code>
|
||||
<code><![CDATA[DatabaseInterface::getInstance()]]></code>
|
||||
<code><![CDATA[DatabaseInterface::getInstance()]]></code>
|
||||
</DeprecatedMethod>
|
||||
<MixedArgument>
|
||||
<code><![CDATA[$db]]></code>
|
||||
|
||||
@ -1711,25 +1711,6 @@ class DatabaseInterface
|
||||
return $this->extension->getError($this->connections[$connectionType->value]);
|
||||
}
|
||||
|
||||
/**
|
||||
* returns the number of rows returned by last query
|
||||
* used with tryQuery as it accepts false
|
||||
*
|
||||
* @param string $query query to run
|
||||
*
|
||||
* @psalm-return int|numeric-string
|
||||
*/
|
||||
public function queryAndGetNumRows(string $query): string|int
|
||||
{
|
||||
$result = $this->tryQuery($query);
|
||||
|
||||
if (! $result) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return $result->numRows();
|
||||
}
|
||||
|
||||
/**
|
||||
* returns last inserted auto_increment id for given $link
|
||||
*/
|
||||
@ -1970,14 +1951,13 @@ class DatabaseInterface
|
||||
$this->isPercona = stripos($this->versionComment, 'percona') !== false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare an SQL statement for execution.
|
||||
*
|
||||
* @param string $query The query, as a string.
|
||||
*/
|
||||
public function prepare(string $query, ConnectionType $connectionType = ConnectionType::User): Statement|null
|
||||
{
|
||||
return $this->extension->prepare($this->connections[$connectionType->value], $query);
|
||||
/** @param list<string> $params */
|
||||
public function executeQuery(
|
||||
string $query,
|
||||
array $params,
|
||||
ConnectionType $connectionType = ConnectionType::User,
|
||||
): ResultInterface|null {
|
||||
return $this->extension->executeQuery($this->connections[$connectionType->value], $query, $params);
|
||||
}
|
||||
|
||||
public function getDatabaseList(): ListDatabase
|
||||
|
||||
@ -91,11 +91,11 @@ interface DbiExtension
|
||||
public function escapeString(Connection $connection, string $string): string;
|
||||
|
||||
/**
|
||||
* Prepare an SQL statement for execution.
|
||||
* Execute a prepared statement and return the result.
|
||||
*
|
||||
* @param string $query The query, as a string.
|
||||
* @param list<string> $params
|
||||
*/
|
||||
public function prepare(Connection $connection, string $query): Statement|null;
|
||||
public function executeQuery(Connection $connection, string $query, array $params): ResultInterface|null;
|
||||
|
||||
/**
|
||||
* Returns the number of warnings from the last query.
|
||||
|
||||
@ -284,20 +284,21 @@ class DbiMysqli implements DbiExtension
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare an SQL statement for execution.
|
||||
* Execute a prepared statement and return the result.
|
||||
*
|
||||
* @param string $query The query, as a string.
|
||||
* @param list<string> $params
|
||||
*/
|
||||
public function prepare(Connection $connection, string $query): Statement|null
|
||||
public function executeQuery(Connection $connection, string $query, array $params): MysqliResult|null
|
||||
{
|
||||
/** @var mysqli $mysqli */
|
||||
$mysqli = $connection->connection;
|
||||
$statement = $mysqli->prepare($query);
|
||||
if ($statement === false) {
|
||||
$result = $mysqli->execute_query($query, $params);
|
||||
|
||||
if ($result === false) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new MysqliStatement($statement);
|
||||
return new MysqliResult($result);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -1,39 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Dbal;
|
||||
|
||||
use mysqli_stmt;
|
||||
|
||||
use function count;
|
||||
|
||||
final class MysqliStatement implements Statement
|
||||
{
|
||||
public function __construct(private mysqli_stmt $statement)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes a prepared statement.
|
||||
*
|
||||
* @param list<string> $params
|
||||
*/
|
||||
public function execute(array $params): bool
|
||||
{
|
||||
$paramCount = $this->statement->param_count;
|
||||
if (count($params) !== $paramCount) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->statement->execute($params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a result set from a prepared statement.
|
||||
*/
|
||||
public function getResult(): ResultInterface
|
||||
{
|
||||
return new MysqliResult($this->statement->get_result());
|
||||
}
|
||||
}
|
||||
@ -1,20 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Dbal;
|
||||
|
||||
interface Statement
|
||||
{
|
||||
/**
|
||||
* Executes a prepared statement.
|
||||
*
|
||||
* @param list<string> $params
|
||||
*/
|
||||
public function execute(array $params): bool;
|
||||
|
||||
/**
|
||||
* Gets a result set from a prepared statement.
|
||||
*/
|
||||
public function getResult(): ResultInterface;
|
||||
}
|
||||
@ -379,13 +379,13 @@ class Node
|
||||
$query = 'SHOW DATABASES ';
|
||||
$query .= $this->getWhereClause('Database', $searchClause);
|
||||
|
||||
return (int) $dbi->queryAndGetNumRows($query);
|
||||
return $this->queryAndGetNumRows($query);
|
||||
}
|
||||
|
||||
$retval = 0;
|
||||
foreach ($this->getDatabasesToSearch($userPrivileges, $searchClause) as $db) {
|
||||
$query = 'SHOW DATABASES LIKE ' . $dbi->quoteString($db);
|
||||
$retval += (int) $dbi->queryAndGetNumRows($query);
|
||||
$retval += $this->queryAndGetNumRows($query);
|
||||
}
|
||||
|
||||
return $retval;
|
||||
@ -831,4 +831,20 @@ class Node
|
||||
|
||||
return $retval;
|
||||
}
|
||||
|
||||
/**
|
||||
* returns the number of rows returned by last query
|
||||
* used with tryQuery as it accepts false
|
||||
*/
|
||||
protected function queryAndGetNumRows(string $query): int
|
||||
{
|
||||
$dbi = DatabaseInterface::getInstance();
|
||||
$result = $dbi->tryQuery($query);
|
||||
|
||||
if ($result === false) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (int) $result->numRows();
|
||||
}
|
||||
}
|
||||
|
||||
@ -94,7 +94,7 @@ class NodeTable extends NodeDatabaseChild
|
||||
$db = Util::backquote($db);
|
||||
$table = Util::backquote($table);
|
||||
$query = 'SHOW COLUMNS FROM ' . $table . ' FROM ' . $db;
|
||||
$retval = (int) $dbi->queryAndGetNumRows($query);
|
||||
$retval = $this->queryAndGetNumRows($query);
|
||||
}
|
||||
|
||||
break;
|
||||
@ -102,7 +102,7 @@ class NodeTable extends NodeDatabaseChild
|
||||
$db = Util::backquote($db);
|
||||
$table = Util::backquote($table);
|
||||
$query = 'SHOW INDEXES FROM ' . $table . ' FROM ' . $db;
|
||||
$retval = (int) $dbi->queryAndGetNumRows($query);
|
||||
$retval = $this->queryAndGetNumRows($query);
|
||||
break;
|
||||
case 'triggers':
|
||||
if (! $this->config->selectedServer['DisableIS']) {
|
||||
@ -116,7 +116,7 @@ class NodeTable extends NodeDatabaseChild
|
||||
} else {
|
||||
$db = Util::backquote($db);
|
||||
$query = 'SHOW TRIGGERS FROM ' . $db . ' WHERE `Table` = ' . $dbi->quoteString($table);
|
||||
$retval = (int) $dbi->queryAndGetNumRows($query);
|
||||
$retval = $this->queryAndGetNumRows($query);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
@ -1196,12 +1196,12 @@ class Privileges
|
||||
NOT (`Table_priv` = \'\' AND Column_priv = \'\')
|
||||
ORDER BY `User` ASC, `Host` ASC, `Db` ASC, `Table_priv` ASC;
|
||||
';
|
||||
$statement = $this->dbi->prepare($query);
|
||||
if ($statement === null || ! $statement->execute([$db->getName(), $table->getName()])) {
|
||||
$result = $this->dbi->executeQuery($query, [$db->getName(), $table->getName()]);
|
||||
if ($result === null) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return $statement->getResult()->fetchAllAssoc();
|
||||
return $result->fetchAllAssoc();
|
||||
}
|
||||
|
||||
/** @return array<int, array<string|null>> */
|
||||
@ -3171,12 +3171,11 @@ class Privileges
|
||||
private function getUserPrivileges(string $user, string $host, bool $hasAccountLocking): array|null
|
||||
{
|
||||
$query = 'SELECT * FROM `mysql`.`user` WHERE `User` = ? AND `Host` = ?;';
|
||||
$statement = $this->dbi->prepare($query);
|
||||
if ($statement === null || ! $statement->execute([$user, $host])) {
|
||||
$result = $this->dbi->executeQuery($query, [$user, $host]);
|
||||
if ($result === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$result = $statement->getResult();
|
||||
/** @var array<string, string|null>|null $userPrivileges */
|
||||
$userPrivileges = $result->fetchAssoc();
|
||||
if ($userPrivileges === []) {
|
||||
@ -3190,12 +3189,11 @@ class Privileges
|
||||
$userPrivileges['account_locked'] = 'N';
|
||||
|
||||
$query = 'SELECT * FROM `mysql`.`global_priv` WHERE `User` = ? AND `Host` = ?;';
|
||||
$statement = $this->dbi->prepare($query);
|
||||
if ($statement === null || ! $statement->execute([$user, $host])) {
|
||||
$result = $this->dbi->executeQuery($query, [$user, $host]);
|
||||
if ($result === null) {
|
||||
return $userPrivileges;
|
||||
}
|
||||
|
||||
$result = $statement->getResult();
|
||||
/** @var array<string, string|null>|null $globalPrivileges */
|
||||
$globalPrivileges = $result->fetchAssoc();
|
||||
if ($globalPrivileges === []) {
|
||||
|
||||
@ -13,7 +13,6 @@ use PhpMyAdmin\Dbal\ConnectionType;
|
||||
use PhpMyAdmin\Dbal\DatabaseInterface;
|
||||
use PhpMyAdmin\Dbal\DbiExtension;
|
||||
use PhpMyAdmin\Dbal\ResultInterface;
|
||||
use PhpMyAdmin\Dbal\Statement;
|
||||
use PhpMyAdmin\I18n\LanguageManager;
|
||||
use PhpMyAdmin\Index;
|
||||
use PhpMyAdmin\Query\Utilities;
|
||||
@ -758,17 +757,17 @@ class DatabaseInterfaceTest extends AbstractTestCase
|
||||
$dummyDbi->assertAllQueriesConsumed();
|
||||
}
|
||||
|
||||
public function testPrepare(): void
|
||||
public function testExecuteQuery(): void
|
||||
{
|
||||
$query = 'SELECT * FROM `mysql`.`user` WHERE `User` = ? AND `Host` = ?;';
|
||||
$stmtStub = self::createStub(Statement::class);
|
||||
$resultStub = self::createStub(ResultInterface::class);
|
||||
$dummyDbi = $this->createMock(DbiExtension::class);
|
||||
$dummyDbi->expects(self::once())->method('prepare')
|
||||
->with(self::isType('object'), self::equalTo($query))
|
||||
->willReturn($stmtStub);
|
||||
$dummyDbi->expects(self::once())->method('executeQuery')
|
||||
->with(self::isType('object'), self::equalTo($query), self::equalTo(['root', 'localhost']))
|
||||
->willReturn($resultStub);
|
||||
$dbi = $this->createDatabaseInterface($dummyDbi);
|
||||
$stmt = $dbi->prepare($query, ConnectionType::ControlUser);
|
||||
self::assertSame($stmtStub, $stmt);
|
||||
$stmt = $dbi->executeQuery($query, ['root', 'localhost'], ConnectionType::ControlUser);
|
||||
self::assertSame($resultStub, $stmt);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -1,25 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Tests\Dbal;
|
||||
|
||||
use mysqli_stmt;
|
||||
use PhpMyAdmin\Dbal\MysqliResult;
|
||||
use PhpMyAdmin\Dbal\MysqliStatement;
|
||||
use PHPUnit\Framework\Attributes\CoversClass;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
#[CoversClass(MysqliStatement::class)]
|
||||
#[CoversClass(MysqliResult::class)]
|
||||
class MysqliStatementTest extends TestCase
|
||||
{
|
||||
public function testGetResult(): void
|
||||
{
|
||||
$mysqliStmt = self::createMock(mysqli_stmt::class);
|
||||
$mysqliStmt->expects(self::once())->method('get_result')->willReturn(false);
|
||||
$statement = new MysqliStatement($mysqliStmt);
|
||||
$result = $statement->getResult();
|
||||
self::assertInstanceOf(MysqliResult::class, $result);
|
||||
}
|
||||
}
|
||||
@ -12,7 +12,6 @@ use PhpMyAdmin\Current;
|
||||
use PhpMyAdmin\Dbal\ConnectionType;
|
||||
use PhpMyAdmin\Dbal\DatabaseInterface;
|
||||
use PhpMyAdmin\Dbal\ResultInterface;
|
||||
use PhpMyAdmin\Dbal\Statement;
|
||||
use PhpMyAdmin\Html\Generator;
|
||||
use PhpMyAdmin\Http\Factory\ServerRequestFactory;
|
||||
use PhpMyAdmin\Message;
|
||||
@ -1896,18 +1895,15 @@ class PrivilegesTest extends AbstractTestCase
|
||||
public function testGetUserPrivileges(): void
|
||||
{
|
||||
$mysqliResultStub = $this->createMock(ResultInterface::class);
|
||||
$mysqliStmtStub = $this->createMock(Statement::class);
|
||||
$mysqliStmtStub->expects(self::exactly(2))->method('execute')->willReturn(true);
|
||||
$mysqliStmtStub->expects(self::exactly(2))->method('getResult')->willReturn($mysqliResultStub);
|
||||
|
||||
$dbi = $this->createMock(DatabaseInterface::class);
|
||||
$dbi->expects(self::once())->method('isMariaDB')->willReturn(true);
|
||||
|
||||
$userQuery = 'SELECT * FROM `mysql`.`user` WHERE `User` = ? AND `Host` = ?;';
|
||||
$globalPrivQuery = 'SELECT * FROM `mysql`.`global_priv` WHERE `User` = ? AND `Host` = ?;';
|
||||
$dbi->expects(self::exactly(2))->method('prepare')->willReturnMap([
|
||||
[$userQuery, ConnectionType::User, $mysqliStmtStub],
|
||||
[$globalPrivQuery, ConnectionType::User, $mysqliStmtStub],
|
||||
$dbi->expects(self::exactly(2))->method('executeQuery')->willReturnMap([
|
||||
[$userQuery, ['test.user', 'test.host'], ConnectionType::User, $mysqliResultStub],
|
||||
[$globalPrivQuery,['test.user', 'test.host'], ConnectionType::User, $mysqliResultStub],
|
||||
]);
|
||||
|
||||
$mysqliResultStub->expects(self::exactly(2))
|
||||
|
||||
@ -15,7 +15,6 @@ use PhpMyAdmin\Config\Settings\Server;
|
||||
use PhpMyAdmin\Dbal\Connection;
|
||||
use PhpMyAdmin\Dbal\DbiExtension;
|
||||
use PhpMyAdmin\Dbal\ResultInterface;
|
||||
use PhpMyAdmin\Dbal\Statement;
|
||||
use PhpMyAdmin\FieldMetadata;
|
||||
use PhpMyAdmin\Identifiers\DatabaseName;
|
||||
use PhpMyAdmin\Tests\FieldHelper;
|
||||
@ -314,7 +313,12 @@ class DbiDummy implements DbiExtension
|
||||
$this->dummyQueries = [];
|
||||
}
|
||||
|
||||
public function prepare(Connection $connection, string $query): Statement|null
|
||||
/**
|
||||
* Execute a prepared statement and return the result.
|
||||
*
|
||||
* @param list<string> $params
|
||||
*/
|
||||
public function executeQuery(Connection $connection, string $query, array $params): ResultInterface|null
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user