mirror of
https://gitee.com/hyperf/hyperf.git
synced 2024-11-29 18:27:44 +08:00
Fixed bug that database-pgsql does not support migration. (#5417)
This commit is contained in:
parent
f980943bf1
commit
aeaf500517
@ -14,6 +14,7 @@
|
|||||||
## Fixed
|
## Fixed
|
||||||
|
|
||||||
- [#5405](https://github.com/hyperf/hyperf/pull/5405) Fixed get local ip error when IPv6 exists.
|
- [#5405](https://github.com/hyperf/hyperf/pull/5405) Fixed get local ip error when IPv6 exists.
|
||||||
|
- [#5417](https://github.com/hyperf/hyperf/pull/5417) Fixed bug that database-pgsql does not support migration.
|
||||||
|
|
||||||
## Optimized
|
## Optimized
|
||||||
|
|
||||||
|
@ -30,6 +30,7 @@
|
|||||||
"psr/simple-cache": "^1.0|^2.0|^3.0"
|
"psr/simple-cache": "^1.0|^2.0|^3.0"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
|
"doctrine/dbal": "^3.6",
|
||||||
"doctrine/inflector": "^2.0",
|
"doctrine/inflector": "^2.0",
|
||||||
"doctrine/instantiator": "^1.0",
|
"doctrine/instantiator": "^1.0",
|
||||||
"egulias/email-validator": "^3.0",
|
"egulias/email-validator": "^3.0",
|
||||||
|
141
src/database-pgsql/src/DBAL/Connection.php
Normal file
141
src/database-pgsql/src/DBAL/Connection.php
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
/**
|
||||||
|
* This file is part of Hyperf.
|
||||||
|
*
|
||||||
|
* @link https://www.hyperf.io
|
||||||
|
* @document https://hyperf.wiki
|
||||||
|
* @contact group@hyperf.io
|
||||||
|
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
|
||||||
|
*/
|
||||||
|
namespace Hyperf\Database\PgSQL\DBAL;
|
||||||
|
|
||||||
|
use Doctrine\DBAL\Driver\Result as ResultInterface;
|
||||||
|
use Doctrine\DBAL\Driver\Statement as StatementInterface;
|
||||||
|
use Doctrine\DBAL\ParameterType;
|
||||||
|
use Swoole\Coroutine\PostgreSQL;
|
||||||
|
use Swoole\Coroutine\PostgreSQLStatement;
|
||||||
|
|
||||||
|
class Connection implements \Doctrine\DBAL\Driver\Connection
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Create a new PDO connection instance.
|
||||||
|
*/
|
||||||
|
public function __construct(private PostgreSQL $connection)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute an SQL statement.
|
||||||
|
*/
|
||||||
|
public function exec(string $sql): int
|
||||||
|
{
|
||||||
|
$stmt = $this->connection->query($sql);
|
||||||
|
|
||||||
|
\assert($stmt instanceof PostgreSQLStatement);
|
||||||
|
|
||||||
|
return $stmt->affectedRows();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepare a new SQL statement.
|
||||||
|
*/
|
||||||
|
public function prepare(string $sql): StatementInterface
|
||||||
|
{
|
||||||
|
$stmt = $this->connection->prepare($sql);
|
||||||
|
|
||||||
|
\assert($stmt instanceof PostgreSQLStatement);
|
||||||
|
|
||||||
|
return new Statement($stmt);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute a new query against the connection.
|
||||||
|
*/
|
||||||
|
public function query(string $sql): ResultInterface
|
||||||
|
{
|
||||||
|
$stmt = $this->connection->query($sql);
|
||||||
|
|
||||||
|
\assert($stmt instanceof PostgreSQLStatement);
|
||||||
|
|
||||||
|
return new Result($stmt);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the last insert ID.
|
||||||
|
*
|
||||||
|
* @param null|string $name
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function lastInsertId($name = null)
|
||||||
|
{
|
||||||
|
if ($name !== null) {
|
||||||
|
return $this->query(sprintf('SELECT CURRVAL(%s)', $this->quote($name)))->fetchOne();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->query('SELECT LASTVAL()')->fetchOne();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Begin a new database transaction.
|
||||||
|
*/
|
||||||
|
public function beginTransaction(): bool
|
||||||
|
{
|
||||||
|
$this->exec('BEGIN');
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Commit a database transaction.
|
||||||
|
*/
|
||||||
|
public function commit(): bool
|
||||||
|
{
|
||||||
|
$this->exec('COMMIT');
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Roll back a database transaction.
|
||||||
|
*/
|
||||||
|
public function rollBack(): bool
|
||||||
|
{
|
||||||
|
$this->exec('ROLLBACK');
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wrap quotes around the given input.
|
||||||
|
*
|
||||||
|
* @param string $input
|
||||||
|
* @param string $type
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function quote($input, $type = ParameterType::STRING)
|
||||||
|
{
|
||||||
|
return $this->connection->escapeLiteral($input);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the server version for the connection.
|
||||||
|
*/
|
||||||
|
public function getServerVersion(): string
|
||||||
|
{
|
||||||
|
$result = $this->query('SHOW server_version');
|
||||||
|
|
||||||
|
$serverVersion = $result->fetchOne();
|
||||||
|
if ($version = strstr($serverVersion, ' ', true)) {
|
||||||
|
return $version;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $serverVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getNativeConnection(): PostgreSQL
|
||||||
|
{
|
||||||
|
return $this->connection;
|
||||||
|
}
|
||||||
|
}
|
45
src/database-pgsql/src/DBAL/Exception.php
Normal file
45
src/database-pgsql/src/DBAL/Exception.php
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
/**
|
||||||
|
* This file is part of Hyperf.
|
||||||
|
*
|
||||||
|
* @link https://www.hyperf.io
|
||||||
|
* @document https://hyperf.wiki
|
||||||
|
* @contact group@hyperf.io
|
||||||
|
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
|
||||||
|
*/
|
||||||
|
namespace Hyperf\Database\PgSQL\DBAL;
|
||||||
|
|
||||||
|
use Doctrine\DBAL\Driver\Exception as DriverExceptionInterface;
|
||||||
|
use Exception as BaseException;
|
||||||
|
use Throwable;
|
||||||
|
|
||||||
|
class Exception extends BaseException implements DriverExceptionInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The SQLSTATE of the driver.
|
||||||
|
*/
|
||||||
|
private ?string $sqlState;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $message the driver error message
|
||||||
|
* @param null|string $sqlState the SQLSTATE the driver is in at the time the error occurred, if any
|
||||||
|
* @param int $code the driver specific error code if any
|
||||||
|
* @param null|Throwable $previous the previous throwable used for the exception chaining
|
||||||
|
*/
|
||||||
|
public function __construct($message, $sqlState = null, $code = 0, ?Throwable $previous = null)
|
||||||
|
{
|
||||||
|
parent::__construct($message, $code, $previous);
|
||||||
|
|
||||||
|
$this->sqlState = $sqlState;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getSQLState()
|
||||||
|
{
|
||||||
|
return $this->sqlState;
|
||||||
|
}
|
||||||
|
}
|
@ -12,9 +12,20 @@ declare(strict_types=1);
|
|||||||
namespace Hyperf\Database\PgSQL\DBAL;
|
namespace Hyperf\Database\PgSQL\DBAL;
|
||||||
|
|
||||||
use Doctrine\DBAL\Driver\AbstractPostgreSQLDriver;
|
use Doctrine\DBAL\Driver\AbstractPostgreSQLDriver;
|
||||||
use Hyperf\Database\DBAL\Concerns\ConnectsToDatabase;
|
use InvalidArgumentException;
|
||||||
|
use Swoole\Coroutine\PostgreSQL;
|
||||||
|
|
||||||
class PostgresDriver extends AbstractPostgreSQLDriver
|
class PostgresDriver extends AbstractPostgreSQLDriver
|
||||||
{
|
{
|
||||||
use ConnectsToDatabase;
|
/**
|
||||||
|
* Create a new database connection.
|
||||||
|
*/
|
||||||
|
public function connect(array $params)
|
||||||
|
{
|
||||||
|
if (! isset($params['pdo']) || ! $params['pdo'] instanceof PostgreSQL) {
|
||||||
|
throw new InvalidArgumentException('The "pdo" property must be required.');
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Connection($params['pdo']);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
82
src/database-pgsql/src/DBAL/Result.php
Normal file
82
src/database-pgsql/src/DBAL/Result.php
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
/**
|
||||||
|
* This file is part of Hyperf.
|
||||||
|
*
|
||||||
|
* @link https://www.hyperf.io
|
||||||
|
* @document https://hyperf.wiki
|
||||||
|
* @contact group@hyperf.io
|
||||||
|
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
|
||||||
|
*/
|
||||||
|
namespace Hyperf\Database\PgSQL\DBAL;
|
||||||
|
|
||||||
|
use Doctrine\DBAL\Driver\Result as ResultInterface;
|
||||||
|
use Swoole\Coroutine\PostgreSQLStatement;
|
||||||
|
|
||||||
|
final class Result implements ResultInterface
|
||||||
|
{
|
||||||
|
public function __construct(private PostgreSQLStatement $result)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/** {@inheritdoc} */
|
||||||
|
public function fetchNumeric()
|
||||||
|
{
|
||||||
|
return $this->result->fetchArray(result_type: SW_PGSQL_NUM);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** {@inheritdoc} */
|
||||||
|
public function fetchAssociative()
|
||||||
|
{
|
||||||
|
return $this->result->fetchAssoc();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** {@inheritdoc} */
|
||||||
|
public function fetchOne()
|
||||||
|
{
|
||||||
|
$row = $this->fetchNumeric();
|
||||||
|
if ($row === false) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $row[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** {@inheritdoc} */
|
||||||
|
public function fetchAllNumeric(): array
|
||||||
|
{
|
||||||
|
return $this->result->fetchAll(SW_PGSQL_NUM);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** {@inheritdoc} */
|
||||||
|
public function fetchAllAssociative(): array
|
||||||
|
{
|
||||||
|
return $this->result->fetchAll(SW_PGSQL_ASSOC);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** {@inheritdoc} */
|
||||||
|
public function fetchFirstColumn(): array
|
||||||
|
{
|
||||||
|
$resultSet = $this->result->fetchAll(SW_PGSQL_NUM);
|
||||||
|
if ($resultSet === false) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return array_map(fn ($row) => $row[0], $resultSet);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function rowCount(): int
|
||||||
|
{
|
||||||
|
return (int) $this->result->affectedRows();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function columnCount(): int
|
||||||
|
{
|
||||||
|
return (int) $this->result->fieldCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function free(): void
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
63
src/database-pgsql/src/DBAL/Statement.php
Normal file
63
src/database-pgsql/src/DBAL/Statement.php
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
/**
|
||||||
|
* This file is part of Hyperf.
|
||||||
|
*
|
||||||
|
* @link https://www.hyperf.io
|
||||||
|
* @document https://hyperf.wiki
|
||||||
|
* @contact group@hyperf.io
|
||||||
|
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
|
||||||
|
*/
|
||||||
|
namespace Hyperf\Database\PgSQL\DBAL;
|
||||||
|
|
||||||
|
use Doctrine\DBAL\Driver\Statement as StatementInterface;
|
||||||
|
use Doctrine\DBAL\ParameterType;
|
||||||
|
use Swoole\Coroutine\PostgreSQLStatement;
|
||||||
|
|
||||||
|
use function ksort;
|
||||||
|
|
||||||
|
final class Statement implements StatementInterface
|
||||||
|
{
|
||||||
|
private array $parameters = [];
|
||||||
|
|
||||||
|
public function __construct(private PostgreSQLStatement $stmt)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/** {@inheritdoc} */
|
||||||
|
public function bindValue($param, $value, $type = ParameterType::STRING): bool
|
||||||
|
{
|
||||||
|
$this->parameters[$param] = $value;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** {@inheritdoc} */
|
||||||
|
public function bindParam($param, &$variable, $type = ParameterType::STRING, $length = null): bool
|
||||||
|
{
|
||||||
|
$this->parameters[$param] = &$variable;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** {@inheritdoc} */
|
||||||
|
public function execute($params = null): Result
|
||||||
|
{
|
||||||
|
if (! empty($params)) {
|
||||||
|
foreach ($params as $param => $value) {
|
||||||
|
if (is_int($param)) {
|
||||||
|
$this->bindValue($param + 1, $value, ParameterType::STRING);
|
||||||
|
} else {
|
||||||
|
$this->bindValue($param, $value, ParameterType::STRING);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ksort($this->parameters);
|
||||||
|
|
||||||
|
if (! $this->stmt->execute($this->parameters)) {
|
||||||
|
throw new Exception($this->stmt->error ?? 'Execute failed.');
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Result($this->stmt);
|
||||||
|
}
|
||||||
|
}
|
@ -13,14 +13,22 @@ namespace HyperfTest\Database\PgSQL\Cases;
|
|||||||
|
|
||||||
use Exception;
|
use Exception;
|
||||||
use Hyperf\Database\Connection;
|
use Hyperf\Database\Connection;
|
||||||
|
use Hyperf\Database\ConnectionResolver;
|
||||||
|
use Hyperf\Database\ConnectionResolverInterface;
|
||||||
use Hyperf\Database\Connectors\ConnectionFactory;
|
use Hyperf\Database\Connectors\ConnectionFactory;
|
||||||
use Hyperf\Database\Exception\QueryException;
|
use Hyperf\Database\Exception\QueryException;
|
||||||
|
use Hyperf\Database\Migrations\DatabaseMigrationRepository;
|
||||||
|
use Hyperf\Database\Migrations\Migrator;
|
||||||
use Hyperf\Database\PgSQL\Connectors\PostgresSqlSwooleExtConnector;
|
use Hyperf\Database\PgSQL\Connectors\PostgresSqlSwooleExtConnector;
|
||||||
use Hyperf\Database\PgSQL\PostgreSqlSwooleExtConnection;
|
use Hyperf\Database\PgSQL\PostgreSqlSwooleExtConnection;
|
||||||
use Hyperf\Database\Query\Builder;
|
use Hyperf\Database\Query\Builder;
|
||||||
|
use Hyperf\Database\Schema\Schema;
|
||||||
|
use Hyperf\Utils\ApplicationContext;
|
||||||
|
use Hyperf\Utils\Filesystem\Filesystem;
|
||||||
use Mockery;
|
use Mockery;
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
use Psr\Container\ContainerInterface;
|
use Psr\Container\ContainerInterface;
|
||||||
|
use Symfony\Component\Console\Style\OutputStyle;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @internal
|
* @internal
|
||||||
@ -28,28 +36,24 @@ use Psr\Container\ContainerInterface;
|
|||||||
*/
|
*/
|
||||||
class PostgreSqlSwooleExtConnectionTest extends TestCase
|
class PostgreSqlSwooleExtConnectionTest extends TestCase
|
||||||
{
|
{
|
||||||
protected ConnectionFactory $connectionFactory;
|
protected Migrator $migrator;
|
||||||
|
|
||||||
public function setUp(): void
|
public function setUp(): void
|
||||||
{
|
|
||||||
$container = Mockery::mock(ContainerInterface::class);
|
|
||||||
$container->shouldReceive('has')->andReturn(true);
|
|
||||||
$container->shouldReceive('get')->with('db.connector.pgsql-swoole')->andReturn(new PostgresSqlSwooleExtConnector());
|
|
||||||
|
|
||||||
$this->connectionFactory = new ConnectionFactory($container);
|
|
||||||
|
|
||||||
Connection::resolverFor('pgsql-swoole', static function ($connection, $database, $prefix, $config) {
|
|
||||||
return new PostgreSqlSwooleExtConnection($connection, $database, $prefix, $config);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testSelectMethodDuplicateKeyValueException()
|
|
||||||
{
|
{
|
||||||
if (SWOOLE_MAJOR_VERSION < 5) {
|
if (SWOOLE_MAJOR_VERSION < 5) {
|
||||||
$this->markTestSkipped('PostgreSql requires Swoole version >= 5.0.0');
|
$this->markTestSkipped('PostgreSql requires Swoole version >= 5.0.0');
|
||||||
}
|
}
|
||||||
|
|
||||||
$connection = $this->connectionFactory->make([
|
$container = Mockery::mock(ContainerInterface::class);
|
||||||
|
$container->shouldReceive('has')->andReturn(true);
|
||||||
|
$container->shouldReceive('get')->with('db.connector.pgsql-swoole')->andReturn(new PostgresSqlSwooleExtConnector());
|
||||||
|
$connector = new ConnectionFactory($container);
|
||||||
|
|
||||||
|
Connection::resolverFor('pgsql-swoole', static function ($connection, $database, $prefix, $config) {
|
||||||
|
return new PostgreSqlSwooleExtConnection($connection, $database, $prefix, $config);
|
||||||
|
});
|
||||||
|
|
||||||
|
$connection = $connector->make([
|
||||||
'driver' => 'pgsql-swoole',
|
'driver' => 'pgsql-swoole',
|
||||||
'host' => '127.0.0.1',
|
'host' => '127.0.0.1',
|
||||||
'port' => 5432,
|
'port' => 5432,
|
||||||
@ -58,6 +62,32 @@ class PostgreSqlSwooleExtConnectionTest extends TestCase
|
|||||||
'password' => 'postgres',
|
'password' => 'postgres',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
$resolver = new ConnectionResolver(['default' => $connection]);
|
||||||
|
|
||||||
|
$container->shouldReceive('get')->with(ConnectionResolverInterface::class)->andReturn($resolver);
|
||||||
|
|
||||||
|
ApplicationContext::setContainer($container);
|
||||||
|
|
||||||
|
$this->migrator = new Migrator(
|
||||||
|
$repository = new DatabaseMigrationRepository($resolver, 'migrations'),
|
||||||
|
$resolver,
|
||||||
|
new Filesystem()
|
||||||
|
);
|
||||||
|
|
||||||
|
$output = Mockery::mock(OutputStyle::class);
|
||||||
|
$output->shouldReceive('writeln');
|
||||||
|
|
||||||
|
$this->migrator->setOutput($output);
|
||||||
|
|
||||||
|
if (! $repository->repositoryExists()) {
|
||||||
|
$repository->createRepository();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSelectMethodDuplicateKeyValueException()
|
||||||
|
{
|
||||||
|
$connection = ApplicationContext::getContainer()->get(ConnectionResolverInterface::class)->connection();
|
||||||
|
|
||||||
$builder = new Builder($connection);
|
$builder = new Builder($connection);
|
||||||
|
|
||||||
$this->expectException(QueryException::class);
|
$this->expectException(QueryException::class);
|
||||||
@ -73,18 +103,7 @@ class PostgreSqlSwooleExtConnectionTest extends TestCase
|
|||||||
|
|
||||||
public function testAffectingStatementWithWrongSql()
|
public function testAffectingStatementWithWrongSql()
|
||||||
{
|
{
|
||||||
if (SWOOLE_MAJOR_VERSION < 5) {
|
$connection = ApplicationContext::getContainer()->get(ConnectionResolverInterface::class)->connection();
|
||||||
$this->markTestSkipped('PostgreSql requires Swoole version >= 5.0.0');
|
|
||||||
}
|
|
||||||
|
|
||||||
$connection = $this->connectionFactory->make([
|
|
||||||
'driver' => 'pgsql-swoole',
|
|
||||||
'host' => '127.0.0.1',
|
|
||||||
'port' => 5432,
|
|
||||||
'database' => 'postgres',
|
|
||||||
'username' => 'postgres',
|
|
||||||
'password' => 'postgres',
|
|
||||||
]);
|
|
||||||
|
|
||||||
$this->expectException(QueryException::class);
|
$this->expectException(QueryException::class);
|
||||||
|
|
||||||
@ -93,11 +112,9 @@ class PostgreSqlSwooleExtConnectionTest extends TestCase
|
|||||||
|
|
||||||
public function testCreateConnectionTimedOut()
|
public function testCreateConnectionTimedOut()
|
||||||
{
|
{
|
||||||
if (SWOOLE_MAJOR_VERSION < 5) {
|
$factory = new ConnectionFactory(ApplicationContext::getContainer());
|
||||||
$this->markTestSkipped('PostgreSql requires Swoole version >= 5.0.0');
|
|
||||||
}
|
|
||||||
|
|
||||||
$connection = $this->connectionFactory->make([
|
$connection = $factory->make([
|
||||||
'driver' => 'pgsql-swoole',
|
'driver' => 'pgsql-swoole',
|
||||||
'host' => 'non-existent-host.internal',
|
'host' => 'non-existent-host.internal',
|
||||||
'port' => 5432,
|
'port' => 5432,
|
||||||
@ -111,4 +128,32 @@ class PostgreSqlSwooleExtConnectionTest extends TestCase
|
|||||||
|
|
||||||
$connection->affectingStatement('UPDATE xx SET x = 1 WHERE id = 1');
|
$connection->affectingStatement('UPDATE xx SET x = 1 WHERE id = 1');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testCreateTableForMigration()
|
||||||
|
{
|
||||||
|
$queryCommentSQL = "select a.attname,
|
||||||
|
d.description
|
||||||
|
from pg_class c,
|
||||||
|
pg_attribute a,
|
||||||
|
pg_type t,
|
||||||
|
pg_description d
|
||||||
|
where c.relname = 'password_resets_for_pgsql'
|
||||||
|
and a.attnum > 0
|
||||||
|
and a.attrelid = c.oid
|
||||||
|
and a.atttypid = t.oid
|
||||||
|
and d.objoid = a.attrelid
|
||||||
|
and d.objsubid = a.attnum";
|
||||||
|
|
||||||
|
$schema = new Schema();
|
||||||
|
|
||||||
|
$this->migrator->rollback([__DIR__ . '/../migrations/two']);
|
||||||
|
$this->migrator->rollback([__DIR__ . '/../migrations/one']);
|
||||||
|
|
||||||
|
$this->migrator->run([__DIR__ . '/../migrations/one']);
|
||||||
|
$this->assertTrue($schema->hasTable('password_resets_for_pgsql'));
|
||||||
|
$this->assertSame('', $schema->connection()->selectOne($queryCommentSQL)['description'] ?? '');
|
||||||
|
|
||||||
|
$this->migrator->run([__DIR__ . '/../migrations/two']);
|
||||||
|
$this->assertSame('邮箱', $schema->connection()->selectOne($queryCommentSQL)['description'] ?? '');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
153
src/database-pgsql/tests/DBAL/ConnectionTest.php
Normal file
153
src/database-pgsql/tests/DBAL/ConnectionTest.php
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
/**
|
||||||
|
* This file is part of Hyperf.
|
||||||
|
*
|
||||||
|
* @link https://www.hyperf.io
|
||||||
|
* @document https://hyperf.wiki
|
||||||
|
* @contact group@hyperf.io
|
||||||
|
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
|
||||||
|
*/
|
||||||
|
namespace HyperfTest\Database\PgSQL\DBAL;
|
||||||
|
|
||||||
|
use Hyperf\Database\PgSQL\DBAL\Connection;
|
||||||
|
use Hyperf\Database\PgSQL\DBAL\Result;
|
||||||
|
use Hyperf\Database\PgSQL\DBAL\Statement;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
use Swoole\Coroutine\PostgreSQL;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
* @coversNothing
|
||||||
|
*/
|
||||||
|
class ConnectionTest extends TestCase
|
||||||
|
{
|
||||||
|
protected Connection $connection;
|
||||||
|
|
||||||
|
public function setUp(): void
|
||||||
|
{
|
||||||
|
if (SWOOLE_MAJOR_VERSION < 5) {
|
||||||
|
$this->markTestSkipped('PostgreSql requires Swoole version >= 5.0.0');
|
||||||
|
}
|
||||||
|
|
||||||
|
$pgsql = new PostgreSQL();
|
||||||
|
$connected = $pgsql->connect('host=127.0.0.1 port=5432 dbname=postgres user=postgres password=postgres');
|
||||||
|
if (! $connected) {
|
||||||
|
$this->fail('Failed to connect to PostgreSQL server.');
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->connection = new Connection($pgsql);
|
||||||
|
|
||||||
|
$pgsql->query('CREATE TABLE IF NOT EXISTS connection_tests_for_hyperf( id SERIAL constraint id primary key, val1 varchar(255) not null, val2 varchar(255) not null);');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function tearDown(): void
|
||||||
|
{
|
||||||
|
$this->connection->exec('DROP TABLE IF EXISTS connection_tests_for_hyperf');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testExec()
|
||||||
|
{
|
||||||
|
$sql = sprintf('insert into "connection_tests_for_hyperf" ("val1", "val2") values (\'%s\', \'%s\');', uniqid(), uniqid());
|
||||||
|
$this->assertGreaterThan(0, $this->connection->exec($sql));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testPrepare()
|
||||||
|
{
|
||||||
|
$stmt = $this->connection->prepare('select * from "connection_tests_for_hyperf" where "val1" = $1');
|
||||||
|
$this->assertInstanceOf(Statement::class, $stmt);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testQuery()
|
||||||
|
{
|
||||||
|
$this->insertOneRow();
|
||||||
|
$result = $this->connection->query('select * from "connection_tests_for_hyperf"');
|
||||||
|
$this->assertInstanceOf(Result::class, $result);
|
||||||
|
$this->assertGreaterThan(0, $result->rowCount());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testLastInsertId()
|
||||||
|
{
|
||||||
|
$this->insertOneRow();
|
||||||
|
$one = $this->connection->lastInsertId();
|
||||||
|
$this->assertIsNumeric($one);
|
||||||
|
$this->assertGreaterThan(0, $one);
|
||||||
|
|
||||||
|
$this->insertOneRow();
|
||||||
|
$two = $this->connection->lastInsertId();
|
||||||
|
$this->assertIsNumeric($two);
|
||||||
|
$this->assertGreaterThan($one, $two);
|
||||||
|
|
||||||
|
$this->assertEquals($two, $this->connection->lastInsertId('connection_tests_for_hyperf_id_seq'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testTransactions()
|
||||||
|
{
|
||||||
|
$count = $this->getRowCount();
|
||||||
|
|
||||||
|
$this->connection->beginTransaction();
|
||||||
|
$this->insertOneRow();
|
||||||
|
$this->connection->rollBack();
|
||||||
|
|
||||||
|
$this->assertEquals($count, $this->getRowCount());
|
||||||
|
|
||||||
|
$this->connection->beginTransaction();
|
||||||
|
$this->insertOneRow();
|
||||||
|
$this->connection->commit();
|
||||||
|
|
||||||
|
$this->assertEquals($count + 1, $this->getRowCount());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testQuote()
|
||||||
|
{
|
||||||
|
$this->assertSame("'hyperf'", $this->connection->quote('hyperf'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetServerVersion()
|
||||||
|
{
|
||||||
|
$this->assertMatchesRegularExpression('/^(?P<major>\d+)(?:\.(?P<minor>\d+)(?:\.(?P<patch>\d+))?)?/', $this->connection->getServerVersion());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetNativeConnection()
|
||||||
|
{
|
||||||
|
$this->assertInstanceOf(PostgreSQL::class, $this->connection->getNativeConnection());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testResult()
|
||||||
|
{
|
||||||
|
$insertResult = $this->connection
|
||||||
|
->prepare('insert into "connection_tests_for_hyperf" ("val1", "val2") values ($1, $2)')
|
||||||
|
->execute([$val1 = uniqid(), $val2 = uniqid()]);
|
||||||
|
|
||||||
|
$this->assertEquals(1, $insertResult->rowCount());
|
||||||
|
|
||||||
|
$result = $this->connection
|
||||||
|
->prepare('select val1, val2, id from "connection_tests_for_hyperf" where "val1" = $1')
|
||||||
|
->execute([$val1]);
|
||||||
|
|
||||||
|
$row = $result->fetchNumeric();
|
||||||
|
$id = $row[2];
|
||||||
|
|
||||||
|
$this->assertSame([$val1, $val2, $id], $row);
|
||||||
|
$this->assertSame(['val1' => $val1, 'val2' => $val2, 'id' => $id], $result->fetchAssociative());
|
||||||
|
$this->assertSame($val1, $result->fetchOne());
|
||||||
|
$this->assertSame([[$val1, $val2, $id]], $result->fetchAllNumeric());
|
||||||
|
$this->assertSame([['val1' => $val1, 'val2' => $val2, 'id' => $id]], $result->fetchAllAssociative());
|
||||||
|
$this->assertSame([$val1], $result->fetchFirstColumn());
|
||||||
|
$this->assertEquals(1, $result->rowCount());
|
||||||
|
$this->assertEquals(3, $result->columnCount());
|
||||||
|
}
|
||||||
|
|
||||||
|
private function insertOneRow()
|
||||||
|
{
|
||||||
|
$stmt = $this->connection->prepare('insert into "connection_tests_for_hyperf" ("val1", "val2") values ($1, $2)');
|
||||||
|
$stmt->execute([uniqid(), uniqid()]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getRowCount()
|
||||||
|
{
|
||||||
|
$result = $this->connection->query('select count(*) from "connection_tests_for_hyperf"');
|
||||||
|
return $result->fetchOne();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,37 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
/**
|
||||||
|
* This file is part of Hyperf.
|
||||||
|
*
|
||||||
|
* @link https://www.hyperf.io
|
||||||
|
* @document https://hyperf.wiki
|
||||||
|
* @contact group@hyperf.io
|
||||||
|
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
|
||||||
|
*/
|
||||||
|
use Hyperf\Database\Migrations\Migration;
|
||||||
|
use Hyperf\Database\Schema\Blueprint;
|
||||||
|
use Hyperf\Database\Schema\Schema;
|
||||||
|
|
||||||
|
class CreatePasswordResetsForPgsqlTable extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
Schema::create('password_resets_for_pgsql', function (Blueprint $table) {
|
||||||
|
$table->string('email')->index();
|
||||||
|
$table->string('token')->index();
|
||||||
|
$table->timestamp('created_at');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
Schema::dropIfExists('password_resets_for_pgsql');
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,39 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
/**
|
||||||
|
* This file is part of Hyperf.
|
||||||
|
*
|
||||||
|
* @link https://www.hyperf.io
|
||||||
|
* @document https://hyperf.wiki
|
||||||
|
* @contact group@hyperf.io
|
||||||
|
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
|
||||||
|
*/
|
||||||
|
use Hyperf\Database\Migrations\Migration;
|
||||||
|
use Hyperf\Database\Schema\Blueprint;
|
||||||
|
use Hyperf\Database\Schema\Schema;
|
||||||
|
|
||||||
|
class AlterPasswordResetsForPgsqlEmailColumn extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
Schema::table('password_resets_for_pgsql', function (Blueprint $table) {
|
||||||
|
$table->string('email')->comment('邮箱')->change();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
if (Schema::hasTable('password_resets_for_pgsql')) {
|
||||||
|
Schema::table('password_resets_for_pgsql', function (Blueprint $table) {
|
||||||
|
$table->string('email')->index()->change();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user