Update website

This commit is contained in:
Guilhem Lavaux 2024-11-19 08:02:04 +01:00
parent 4413528994
commit 1d90fbf296
6865 changed files with 1091082 additions and 0 deletions

View file

@ -0,0 +1,42 @@
<?php
namespace Doctrine\DBAL;
final class ArrayParameterType
{
/**
* Represents an array of ints to be expanded by Doctrine SQL parsing.
*/
public const INTEGER = ParameterType::INTEGER + Connection::ARRAY_PARAM_OFFSET;
/**
* Represents an array of strings to be expanded by Doctrine SQL parsing.
*/
public const STRING = ParameterType::STRING + Connection::ARRAY_PARAM_OFFSET;
/**
* Represents an array of ascii strings to be expanded by Doctrine SQL parsing.
*/
public const ASCII = ParameterType::ASCII + Connection::ARRAY_PARAM_OFFSET;
/**
* Represents an array of ascii strings to be expanded by Doctrine SQL parsing.
*/
public const BINARY = ParameterType::BINARY + Connection::ARRAY_PARAM_OFFSET;
/**
* @internal
*
* @psalm-param self::* $type
*
* @psalm-return ParameterType::INTEGER|ParameterType::STRING|ParameterType::ASCII|ParameterType::BINARY
*/
public static function toElementParameterType(int $type): int
{
return $type - Connection::ARRAY_PARAM_OFFSET;
}
private function __construct()
{
}
}

View file

@ -0,0 +1,10 @@
<?php
namespace Doctrine\DBAL\ArrayParameters;
use Throwable;
/** @internal */
interface Exception extends Throwable
{
}

View file

@ -0,0 +1,19 @@
<?php
namespace Doctrine\DBAL\ArrayParameters\Exception;
use Doctrine\DBAL\ArrayParameters\Exception;
use LogicException;
use function sprintf;
/** @psalm-immutable */
class MissingNamedParameter extends LogicException implements Exception
{
public static function new(string $name): self
{
return new self(
sprintf('Named parameter "%s" does not have a bound value.', $name),
);
}
}

View file

@ -0,0 +1,23 @@
<?php
namespace Doctrine\DBAL\ArrayParameters\Exception;
use Doctrine\DBAL\ArrayParameters\Exception;
use LogicException;
use function sprintf;
/**
* @internal
*
* @psalm-immutable
*/
class MissingPositionalParameter extends LogicException implements Exception
{
public static function new(int $index): self
{
return new self(
sprintf('Positional parameter at index %d does not have a bound value.', $index),
);
}
}

View file

@ -0,0 +1,116 @@
<?php
namespace Doctrine\DBAL\Cache;
use Doctrine\DBAL\Driver\FetchUtils;
use Doctrine\DBAL\Driver\Result;
use function array_values;
use function count;
use function reset;
/** @internal The class is internal to the caching layer implementation. */
final class ArrayResult implements Result
{
/** @var list<array<string, mixed>> */
private array $data;
private int $columnCount = 0;
private int $num = 0;
/** @param list<array<string, mixed>> $data */
public function __construct(array $data)
{
$this->data = $data;
if (count($data) === 0) {
return;
}
$this->columnCount = count($data[0]);
}
/**
* {@inheritDoc}
*/
public function fetchNumeric()
{
$row = $this->fetch();
if ($row === false) {
return false;
}
return array_values($row);
}
/**
* {@inheritDoc}
*/
public function fetchAssociative()
{
return $this->fetch();
}
/**
* {@inheritDoc}
*/
public function fetchOne()
{
$row = $this->fetch();
if ($row === false) {
return false;
}
return reset($row);
}
/**
* {@inheritDoc}
*/
public function fetchAllNumeric(): array
{
return FetchUtils::fetchAllNumeric($this);
}
/**
* {@inheritDoc}
*/
public function fetchAllAssociative(): array
{
return FetchUtils::fetchAllAssociative($this);
}
/**
* {@inheritDoc}
*/
public function fetchFirstColumn(): array
{
return FetchUtils::fetchFirstColumn($this);
}
public function rowCount(): int
{
return count($this->data);
}
public function columnCount(): int
{
return $this->columnCount;
}
public function free(): void
{
$this->data = [];
}
/** @return array<string, mixed>|false */
private function fetch()
{
if (! isset($this->data[$this->num])) {
return false;
}
return $this->data[$this->num++];
}
}

View file

@ -0,0 +1,21 @@
<?php
namespace Doctrine\DBAL\Cache;
use Doctrine\DBAL\Exception;
/** @psalm-immutable */
class CacheException extends Exception
{
/** @return CacheException */
public static function noCacheKey()
{
return new self('No cache key was set.');
}
/** @return CacheException */
public static function noResultDriverConfigured()
{
return new self('Trying to cache a query but no result driver is configured.');
}
}

View file

@ -0,0 +1,176 @@
<?php
namespace Doctrine\DBAL\Cache;
use Doctrine\Common\Cache\Cache;
use Doctrine\Common\Cache\Psr6\CacheAdapter;
use Doctrine\Common\Cache\Psr6\DoctrineProvider;
use Doctrine\DBAL\Types\Type;
use Doctrine\Deprecations\Deprecation;
use Psr\Cache\CacheItemPoolInterface;
use TypeError;
use function get_class;
use function hash;
use function serialize;
use function sha1;
use function sprintf;
/**
* Query Cache Profile handles the data relevant for query caching.
*
* It is a value object, setter methods return NEW instances.
*/
class QueryCacheProfile
{
private ?CacheItemPoolInterface $resultCache = null;
/** @var int */
private $lifetime;
/** @var string|null */
private $cacheKey;
/**
* @param int $lifetime
* @param string|null $cacheKey
* @param CacheItemPoolInterface|Cache|null $resultCache
*/
public function __construct($lifetime = 0, $cacheKey = null, ?object $resultCache = null)
{
$this->lifetime = $lifetime;
$this->cacheKey = $cacheKey;
if ($resultCache instanceof CacheItemPoolInterface) {
$this->resultCache = $resultCache;
} elseif ($resultCache instanceof Cache) {
Deprecation::trigger(
'doctrine/dbal',
'https://github.com/doctrine/dbal/pull/4620',
'Passing an instance of %s to %s as $resultCache is deprecated. Pass an instance of %s instead.',
Cache::class,
__METHOD__,
CacheItemPoolInterface::class,
);
$this->resultCache = CacheAdapter::wrap($resultCache);
} elseif ($resultCache !== null) {
throw new TypeError(sprintf(
'$resultCache: Expected either null or an instance of %s or %s, got %s.',
CacheItemPoolInterface::class,
Cache::class,
get_class($resultCache),
));
}
}
public function getResultCache(): ?CacheItemPoolInterface
{
return $this->resultCache;
}
/**
* @deprecated Use {@see getResultCache()} instead.
*
* @return Cache|null
*/
public function getResultCacheDriver()
{
Deprecation::trigger(
'doctrine/dbal',
'https://github.com/doctrine/dbal/pull/4620',
'%s is deprecated, call getResultCache() instead.',
__METHOD__,
);
return $this->resultCache !== null ? DoctrineProvider::wrap($this->resultCache) : null;
}
/** @return int */
public function getLifetime()
{
return $this->lifetime;
}
/**
* @return string
*
* @throws CacheException
*/
public function getCacheKey()
{
if ($this->cacheKey === null) {
throw CacheException::noCacheKey();
}
return $this->cacheKey;
}
/**
* Generates the real cache key from query, params, types and connection parameters.
*
* @param string $sql
* @param list<mixed>|array<string, mixed> $params
* @param array<int, Type|int|string|null>|array<string, Type|int|string|null> $types
* @param array<string, mixed> $connectionParams
*
* @return array{string, string}
*/
public function generateCacheKeys($sql, $params, $types, array $connectionParams = [])
{
if (isset($connectionParams['password'])) {
unset($connectionParams['password']);
}
$realCacheKey = 'query=' . $sql .
'&params=' . serialize($params) .
'&types=' . serialize($types) .
'&connectionParams=' . hash('sha256', serialize($connectionParams));
// should the key be automatically generated using the inputs or is the cache key set?
$cacheKey = $this->cacheKey ?? sha1($realCacheKey);
return [$cacheKey, $realCacheKey];
}
public function setResultCache(CacheItemPoolInterface $cache): QueryCacheProfile
{
return new QueryCacheProfile($this->lifetime, $this->cacheKey, $cache);
}
/**
* @deprecated Use {@see setResultCache()} instead.
*
* @return QueryCacheProfile
*/
public function setResultCacheDriver(Cache $cache)
{
Deprecation::trigger(
'doctrine/dbal',
'https://github.com/doctrine/dbal/pull/4620',
'%s is deprecated, call setResultCache() instead.',
__METHOD__,
);
return new QueryCacheProfile($this->lifetime, $this->cacheKey, CacheAdapter::wrap($cache));
}
/**
* @param string|null $cacheKey
*
* @return QueryCacheProfile
*/
public function setCacheKey($cacheKey)
{
return new QueryCacheProfile($this->lifetime, $cacheKey, $this->resultCache);
}
/**
* @param int $lifetime
*
* @return QueryCacheProfile
*/
public function setLifetime($lifetime)
{
return new QueryCacheProfile($lifetime, $this->cacheKey, $this->resultCache);
}
}

28
vendor/doctrine/dbal/src/ColumnCase.php vendored Normal file
View file

@ -0,0 +1,28 @@
<?php
namespace Doctrine\DBAL;
/**
* Contains portable column case conversions.
*/
final class ColumnCase
{
/**
* Convert column names to upper case.
*/
public const UPPER = 1;
/**
* Convert column names to lower case.
*/
public const LOWER = 2;
/**
* This class cannot be instantiated.
*
* @codeCoverageIgnore
*/
private function __construct()
{
}
}

View file

@ -0,0 +1,265 @@
<?php
namespace Doctrine\DBAL;
use Doctrine\Common\Cache\Cache;
use Doctrine\Common\Cache\Psr6\CacheAdapter;
use Doctrine\Common\Cache\Psr6\DoctrineProvider;
use Doctrine\DBAL\Driver\Middleware;
use Doctrine\DBAL\Logging\SQLLogger;
use Doctrine\DBAL\Schema\SchemaManagerFactory;
use Doctrine\Deprecations\Deprecation;
use Psr\Cache\CacheItemPoolInterface;
use function func_num_args;
/**
* Configuration container for the Doctrine DBAL.
*/
class Configuration
{
/** @var Middleware[] */
private array $middlewares = [];
/**
* The SQL logger in use. If null, SQL logging is disabled.
*
* @var SQLLogger|null
*/
protected $sqlLogger;
/**
* The cache driver implementation that is used for query result caching.
*/
private ?CacheItemPoolInterface $resultCache = null;
/**
* The cache driver implementation that is used for query result caching.
*
* @deprecated Use {@see $resultCache} instead.
*
* @var Cache|null
*/
protected $resultCacheImpl;
/**
* The callable to use to filter schema assets.
*
* @var callable|null
*/
protected $schemaAssetsFilter;
/**
* The default auto-commit mode for connections.
*
* @var bool
*/
protected $autoCommit = true;
/**
* Whether type comments should be disabled to provide the same DB schema than
* will be obtained with DBAL 4.x. This is useful when relying only on the
* platform-aware schema comparison (which does not need those type comments)
* rather than the deprecated legacy tooling.
*/
private bool $disableTypeComments = false;
private ?SchemaManagerFactory $schemaManagerFactory = null;
public function __construct()
{
$this->schemaAssetsFilter = static function (): bool {
return true;
};
}
/**
* Sets the SQL logger to use. Defaults to NULL which means SQL logging is disabled.
*
* @deprecated Use {@see setMiddlewares()} and {@see \Doctrine\DBAL\Logging\Middleware} instead.
*/
public function setSQLLogger(?SQLLogger $logger = null): void
{
Deprecation::trigger(
'doctrine/dbal',
'https://github.com/doctrine/dbal/pull/4967',
'%s is deprecated, use setMiddlewares() and Logging\\Middleware instead.',
__METHOD__,
);
$this->sqlLogger = $logger;
}
/**
* Gets the SQL logger that is used.
*
* @deprecated
*/
public function getSQLLogger(): ?SQLLogger
{
Deprecation::triggerIfCalledFromOutside(
'doctrine/dbal',
'https://github.com/doctrine/dbal/pull/4967',
'%s is deprecated.',
__METHOD__,
);
return $this->sqlLogger;
}
/**
* Gets the cache driver implementation that is used for query result caching.
*/
public function getResultCache(): ?CacheItemPoolInterface
{
return $this->resultCache;
}
/**
* Gets the cache driver implementation that is used for query result caching.
*
* @deprecated Use {@see getResultCache()} instead.
*/
public function getResultCacheImpl(): ?Cache
{
Deprecation::trigger(
'doctrine/dbal',
'https://github.com/doctrine/dbal/pull/4620',
'%s is deprecated, call getResultCache() instead.',
__METHOD__,
);
return $this->resultCacheImpl;
}
/**
* Sets the cache driver implementation that is used for query result caching.
*/
public function setResultCache(CacheItemPoolInterface $cache): void
{
$this->resultCacheImpl = DoctrineProvider::wrap($cache);
$this->resultCache = $cache;
}
/**
* Sets the cache driver implementation that is used for query result caching.
*
* @deprecated Use {@see setResultCache()} instead.
*/
public function setResultCacheImpl(Cache $cacheImpl): void
{
Deprecation::trigger(
'doctrine/dbal',
'https://github.com/doctrine/dbal/pull/4620',
'%s is deprecated, call setResultCache() instead.',
__METHOD__,
);
$this->resultCacheImpl = $cacheImpl;
$this->resultCache = CacheAdapter::wrap($cacheImpl);
}
/**
* Sets the callable to use to filter schema assets.
*/
public function setSchemaAssetsFilter(?callable $callable = null): void
{
if (func_num_args() < 1) {
Deprecation::trigger(
'doctrine/dbal',
'https://github.com/doctrine/dbal/pull/5483',
'Not passing an argument to %s is deprecated.',
__METHOD__,
);
} elseif ($callable === null) {
Deprecation::trigger(
'doctrine/dbal',
'https://github.com/doctrine/dbal/pull/5483',
'Using NULL as a schema asset filter is deprecated.'
. ' Use a callable that always returns true instead.',
);
}
$this->schemaAssetsFilter = $callable;
}
/**
* Returns the callable to use to filter schema assets.
*/
public function getSchemaAssetsFilter(): ?callable
{
return $this->schemaAssetsFilter;
}
/**
* Sets the default auto-commit mode for connections.
*
* If a connection is in auto-commit mode, then all its SQL statements will be executed and committed as individual
* transactions. Otherwise, its SQL statements are grouped into transactions that are terminated by a call to either
* the method commit or the method rollback. By default, new connections are in auto-commit mode.
*
* @see getAutoCommit
*
* @param bool $autoCommit True to enable auto-commit mode; false to disable it
*/
public function setAutoCommit(bool $autoCommit): void
{
$this->autoCommit = $autoCommit;
}
/**
* Returns the default auto-commit mode for connections.
*
* @see setAutoCommit
*
* @return bool True if auto-commit mode is enabled by default for connections, false otherwise.
*/
public function getAutoCommit(): bool
{
return $this->autoCommit;
}
/**
* @param Middleware[] $middlewares
*
* @return $this
*/
public function setMiddlewares(array $middlewares): self
{
$this->middlewares = $middlewares;
return $this;
}
/** @return Middleware[] */
public function getMiddlewares(): array
{
return $this->middlewares;
}
public function getSchemaManagerFactory(): ?SchemaManagerFactory
{
return $this->schemaManagerFactory;
}
/** @return $this */
public function setSchemaManagerFactory(SchemaManagerFactory $schemaManagerFactory): self
{
$this->schemaManagerFactory = $schemaManagerFactory;
return $this;
}
public function getDisableTypeComments(): bool
{
return $this->disableTypeComments;
}
/** @return $this */
public function setDisableTypeComments(bool $disableTypeComments): self
{
$this->disableTypeComments = $disableTypeComments;
return $this;
}
}

2001
vendor/doctrine/dbal/src/Connection.php vendored Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,31 @@
<?php
namespace Doctrine\DBAL;
/** @psalm-immutable */
class ConnectionException extends Exception
{
/** @return ConnectionException */
public static function commitFailedRollbackOnly()
{
return new self('Transaction commit failed because the transaction has been marked for rollback only.');
}
/** @return ConnectionException */
public static function noActiveTransaction()
{
return new self('There is no active transaction.');
}
/** @return ConnectionException */
public static function savepointsNotSupported()
{
return new self('Savepoints are not supported by this driver.');
}
/** @return ConnectionException */
public static function mayNotAlterNestedTransactionWithSavepointsInTransaction()
{
return new self('May not alter the nested transaction with savepoints behavior while a transaction is open.');
}
}

View file

@ -0,0 +1,374 @@
<?php
namespace Doctrine\DBAL\Connections;
use Doctrine\Common\EventManager;
use Doctrine\DBAL\Configuration;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Driver;
use Doctrine\DBAL\Driver\Connection as DriverConnection;
use Doctrine\DBAL\Driver\Exception as DriverException;
use Doctrine\DBAL\DriverManager;
use Doctrine\DBAL\Event\ConnectionEventArgs;
use Doctrine\DBAL\Events;
use Doctrine\DBAL\Exception;
use Doctrine\DBAL\Statement;
use Doctrine\Deprecations\Deprecation;
use InvalidArgumentException;
use SensitiveParameter;
use function array_rand;
use function count;
/**
* Primary-Replica Connection
*
* Connection can be used with primary-replica setups.
*
* Important for the understanding of this connection should be how and when
* it picks the replica or primary.
*
* 1. Replica if primary was never picked before and ONLY if 'getWrappedConnection'
* or 'executeQuery' is used.
* 2. Primary picked when 'executeStatement', 'insert', 'delete', 'update', 'createSavepoint',
* 'releaseSavepoint', 'beginTransaction', 'rollback', 'commit' or 'prepare' is called.
* 3. If Primary was picked once during the lifetime of the connection it will always get picked afterwards.
* 4. One replica connection is randomly picked ONCE during a request.
*
* ATTENTION: You can write to the replica with this connection if you execute a write query without
* opening up a transaction. For example:
*
* $conn = DriverManager::getConnection(...);
* $conn->executeQuery("DELETE FROM table");
*
* Be aware that Connection#executeQuery is a method specifically for READ
* operations only.
*
* Use Connection#executeStatement for any SQL statement that changes/updates
* state in the database (UPDATE, INSERT, DELETE or DDL statements).
*
* This connection is limited to replica operations using the
* Connection#executeQuery operation only, because it wouldn't be compatible
* with the ORM or SchemaManager code otherwise. Both use all the other
* operations in a context where writes could happen to a replica, which makes
* this restricted approach necessary.
*
* You can manually connect to the primary at any time by calling:
*
* $conn->ensureConnectedToPrimary();
*
* Instantiation through the DriverManager looks like:
*
* @psalm-import-type Params from DriverManager
* @example
*
* $conn = DriverManager::getConnection(array(
* 'wrapperClass' => 'Doctrine\DBAL\Connections\PrimaryReadReplicaConnection',
* 'driver' => 'pdo_mysql',
* 'primary' => array('user' => '', 'password' => '', 'host' => '', 'dbname' => ''),
* 'replica' => array(
* array('user' => 'replica1', 'password' => '', 'host' => '', 'dbname' => ''),
* array('user' => 'replica2', 'password' => '', 'host' => '', 'dbname' => ''),
* )
* ));
*
* You can also pass 'driverOptions' and any other documented option to each of this drivers
* to pass additional information.
*/
class PrimaryReadReplicaConnection extends Connection
{
/**
* Primary and Replica connection (one of the randomly picked replicas).
*
* @var DriverConnection[]|null[]
*/
protected $connections = ['primary' => null, 'replica' => null];
/**
* You can keep the replica connection and then switch back to it
* during the request if you know what you are doing.
*
* @var bool
*/
protected $keepReplica = false;
/**
* Creates Primary Replica Connection.
*
* @internal The connection can be only instantiated by the driver manager.
*
* @param array<string,mixed> $params
* @psalm-param Params $params
*
* @throws Exception
* @throws InvalidArgumentException
*/
public function __construct(
array $params,
Driver $driver,
?Configuration $config = null,
?EventManager $eventManager = null
) {
if (! isset($params['replica'], $params['primary'])) {
throw new InvalidArgumentException('primary or replica configuration missing');
}
if (count($params['replica']) === 0) {
throw new InvalidArgumentException('You have to configure at least one replica.');
}
if (isset($params['driver'])) {
$params['primary']['driver'] = $params['driver'];
foreach ($params['replica'] as $replicaKey => $replica) {
$params['replica'][$replicaKey]['driver'] = $params['driver'];
}
}
$this->keepReplica = (bool) ($params['keepReplica'] ?? false);
parent::__construct($params, $driver, $config, $eventManager);
}
/**
* Checks if the connection is currently towards the primary or not.
*/
public function isConnectedToPrimary(): bool
{
return $this->_conn !== null && $this->_conn === $this->connections['primary'];
}
/**
* @param string|null $connectionName
*
* @return bool
*/
public function connect($connectionName = null)
{
if ($connectionName !== null) {
throw new InvalidArgumentException(
'Passing a connection name as first argument is not supported anymore.'
. ' Use ensureConnectedToPrimary()/ensureConnectedToReplica() instead.',
);
}
return $this->performConnect();
}
protected function performConnect(?string $connectionName = null): bool
{
$requestedConnectionChange = ($connectionName !== null);
$connectionName = $connectionName ?? 'replica';
if ($connectionName !== 'replica' && $connectionName !== 'primary') {
throw new InvalidArgumentException('Invalid option to connect(), only primary or replica allowed.');
}
// If we have a connection open, and this is not an explicit connection
// change request, then abort right here, because we are already done.
// This prevents writes to the replica in case of "keepReplica" option enabled.
if ($this->_conn !== null && ! $requestedConnectionChange) {
return false;
}
$forcePrimaryAsReplica = false;
if ($this->getTransactionNestingLevel() > 0) {
$connectionName = 'primary';
$forcePrimaryAsReplica = true;
}
if (isset($this->connections[$connectionName])) {
$this->_conn = $this->connections[$connectionName];
if ($forcePrimaryAsReplica && ! $this->keepReplica) {
$this->connections['replica'] = $this->_conn;
}
return false;
}
if ($connectionName === 'primary') {
$this->connections['primary'] = $this->_conn = $this->connectTo($connectionName);
// Set replica connection to primary to avoid invalid reads
if (! $this->keepReplica) {
$this->connections['replica'] = $this->connections['primary'];
}
} else {
$this->connections['replica'] = $this->_conn = $this->connectTo($connectionName);
}
if ($this->_eventManager->hasListeners(Events::postConnect)) {
Deprecation::trigger(
'doctrine/dbal',
'https://github.com/doctrine/dbal/issues/5784',
'Subscribing to %s events is deprecated. Implement a middleware instead.',
Events::postConnect,
);
$eventArgs = new ConnectionEventArgs($this);
$this->_eventManager->dispatchEvent(Events::postConnect, $eventArgs);
}
return true;
}
/**
* Connects to the primary node of the database cluster.
*
* All following statements after this will be executed against the primary node.
*/
public function ensureConnectedToPrimary(): bool
{
return $this->performConnect('primary');
}
/**
* Connects to a replica node of the database cluster.
*
* All following statements after this will be executed against the replica node,
* unless the keepReplica option is set to false and a primary connection
* was already opened.
*/
public function ensureConnectedToReplica(): bool
{
return $this->performConnect('replica');
}
/**
* Connects to a specific connection.
*
* @param string $connectionName
*
* @return DriverConnection
*
* @throws Exception
*/
protected function connectTo($connectionName)
{
$params = $this->getParams();
$connectionParams = $this->chooseConnectionConfiguration($connectionName, $params);
try {
return $this->_driver->connect($connectionParams);
} catch (DriverException $e) {
throw $this->convertException($e);
}
}
/**
* @param string $connectionName
* @param mixed[] $params
*
* @return mixed
*/
protected function chooseConnectionConfiguration(
$connectionName,
#[SensitiveParameter]
$params
) {
if ($connectionName === 'primary') {
return $params['primary'];
}
$config = $params['replica'][array_rand($params['replica'])];
if (! isset($config['charset']) && isset($params['primary']['charset'])) {
$config['charset'] = $params['primary']['charset'];
}
return $config;
}
/**
* {@inheritDoc}
*/
public function executeStatement($sql, array $params = [], array $types = [])
{
$this->ensureConnectedToPrimary();
return parent::executeStatement($sql, $params, $types);
}
/**
* {@inheritDoc}
*/
public function beginTransaction()
{
$this->ensureConnectedToPrimary();
return parent::beginTransaction();
}
/**
* {@inheritDoc}
*/
public function commit()
{
$this->ensureConnectedToPrimary();
return parent::commit();
}
/**
* {@inheritDoc}
*/
public function rollBack()
{
$this->ensureConnectedToPrimary();
return parent::rollBack();
}
/**
* {@inheritDoc}
*/
public function close()
{
unset($this->connections['primary'], $this->connections['replica']);
parent::close();
$this->_conn = null;
$this->connections = ['primary' => null, 'replica' => null];
}
/**
* {@inheritDoc}
*/
public function createSavepoint($savepoint)
{
$this->ensureConnectedToPrimary();
parent::createSavepoint($savepoint);
}
/**
* {@inheritDoc}
*/
public function releaseSavepoint($savepoint)
{
$this->ensureConnectedToPrimary();
parent::releaseSavepoint($savepoint);
}
/**
* {@inheritDoc}
*/
public function rollbackSavepoint($savepoint)
{
$this->ensureConnectedToPrimary();
parent::rollbackSavepoint($savepoint);
}
public function prepare(string $sql): Statement
{
$this->ensureConnectedToPrimary();
return parent::prepare($sql);
}
}

57
vendor/doctrine/dbal/src/Driver.php vendored Normal file
View file

@ -0,0 +1,57 @@
<?php
namespace Doctrine\DBAL;
use Doctrine\DBAL\Driver\API\ExceptionConverter;
use Doctrine\DBAL\Driver\Connection as DriverConnection;
use Doctrine\DBAL\Driver\Exception;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Schema\AbstractSchemaManager;
use SensitiveParameter;
/**
* Driver interface.
* Interface that all DBAL drivers must implement.
*
* @psalm-import-type Params from DriverManager
*/
interface Driver
{
/**
* Attempts to create a connection with the database.
*
* @param array<string, mixed> $params All connection parameters.
* @psalm-param Params $params All connection parameters.
*
* @return DriverConnection The database connection.
*
* @throws Exception
*/
public function connect(
#[SensitiveParameter]
array $params
);
/**
* Gets the DatabasePlatform instance that provides all the metadata about
* the platform this driver connects to.
*
* @return AbstractPlatform The database platform.
*/
public function getDatabasePlatform();
/**
* Gets the SchemaManager that can be used to inspect and change the underlying
* database schema of the platform this driver connects to.
*
* @deprecated Use {@link AbstractPlatform::createSchemaManager()} instead.
*
* @return AbstractSchemaManager
*/
public function getSchemaManager(Connection $conn, AbstractPlatform $platform);
/**
* Gets the ExceptionConverter that can be used to convert driver-level exceptions into DBAL exceptions.
*/
public function getExceptionConverter(): ExceptionConverter;
}

View file

@ -0,0 +1,25 @@
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\API;
use Doctrine\DBAL\Driver\Exception;
use Doctrine\DBAL\Exception\DriverException;
use Doctrine\DBAL\Query;
interface ExceptionConverter
{
/**
* Converts a given driver-level exception into a DBAL-level driver exception.
*
* Implementors should use the vendor-specific error code and SQLSTATE of the exception
* and instantiate the most appropriate specialized {@see DriverException} subclass.
*
* @param Exception $exception The driver exception to convert.
* @param Query|null $query The SQL query that triggered the exception, if any.
*
* @return DriverException An instance of {@see DriverException} or one of its subclasses.
*/
public function convert(Exception $exception, ?Query $query): DriverException;
}

View file

@ -0,0 +1,65 @@
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\API\IBMDB2;
use Doctrine\DBAL\Driver\API\ExceptionConverter as ExceptionConverterInterface;
use Doctrine\DBAL\Driver\Exception;
use Doctrine\DBAL\Exception\ConnectionException;
use Doctrine\DBAL\Exception\DriverException;
use Doctrine\DBAL\Exception\ForeignKeyConstraintViolationException;
use Doctrine\DBAL\Exception\InvalidFieldNameException;
use Doctrine\DBAL\Exception\NonUniqueFieldNameException;
use Doctrine\DBAL\Exception\NotNullConstraintViolationException;
use Doctrine\DBAL\Exception\SyntaxErrorException;
use Doctrine\DBAL\Exception\TableExistsException;
use Doctrine\DBAL\Exception\TableNotFoundException;
use Doctrine\DBAL\Exception\UniqueConstraintViolationException;
use Doctrine\DBAL\Query;
/**
* @internal
*
* @link https://www.ibm.com/docs/en/db2/11.5?topic=messages-sql
*/
final class ExceptionConverter implements ExceptionConverterInterface
{
public function convert(Exception $exception, ?Query $query): DriverException
{
switch ($exception->getCode()) {
case -104:
return new SyntaxErrorException($exception, $query);
case -203:
return new NonUniqueFieldNameException($exception, $query);
case -204:
return new TableNotFoundException($exception, $query);
case -206:
return new InvalidFieldNameException($exception, $query);
case -407:
return new NotNullConstraintViolationException($exception, $query);
case -530:
case -531:
case -532:
case -20356:
return new ForeignKeyConstraintViolationException($exception, $query);
case -601:
return new TableExistsException($exception, $query);
case -803:
return new UniqueConstraintViolationException($exception, $query);
case -1336:
case -30082:
return new ConnectionException($exception, $query);
}
return new DriverException($exception, $query);
}
}

View file

@ -0,0 +1,120 @@
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\API\MySQL;
use Doctrine\DBAL\Driver\API\ExceptionConverter as ExceptionConverterInterface;
use Doctrine\DBAL\Driver\Exception;
use Doctrine\DBAL\Exception\ConnectionException;
use Doctrine\DBAL\Exception\ConnectionLost;
use Doctrine\DBAL\Exception\DatabaseDoesNotExist;
use Doctrine\DBAL\Exception\DeadlockException;
use Doctrine\DBAL\Exception\DriverException;
use Doctrine\DBAL\Exception\ForeignKeyConstraintViolationException;
use Doctrine\DBAL\Exception\InvalidFieldNameException;
use Doctrine\DBAL\Exception\LockWaitTimeoutException;
use Doctrine\DBAL\Exception\NonUniqueFieldNameException;
use Doctrine\DBAL\Exception\NotNullConstraintViolationException;
use Doctrine\DBAL\Exception\SyntaxErrorException;
use Doctrine\DBAL\Exception\TableExistsException;
use Doctrine\DBAL\Exception\TableNotFoundException;
use Doctrine\DBAL\Exception\UniqueConstraintViolationException;
use Doctrine\DBAL\Query;
/** @internal */
final class ExceptionConverter implements ExceptionConverterInterface
{
/**
* @link https://dev.mysql.com/doc/mysql-errors/8.0/en/client-error-reference.html
* @link https://dev.mysql.com/doc/mysql-errors/8.0/en/server-error-reference.html
*/
public function convert(Exception $exception, ?Query $query): DriverException
{
switch ($exception->getCode()) {
case 1008:
return new DatabaseDoesNotExist($exception, $query);
case 1213:
return new DeadlockException($exception, $query);
case 1205:
return new LockWaitTimeoutException($exception, $query);
case 1050:
return new TableExistsException($exception, $query);
case 1051:
case 1146:
return new TableNotFoundException($exception, $query);
case 1216:
case 1217:
case 1451:
case 1452:
case 1701:
return new ForeignKeyConstraintViolationException($exception, $query);
case 1062:
case 1557:
case 1569:
case 1586:
return new UniqueConstraintViolationException($exception, $query);
case 1054:
case 1166:
case 1611:
return new InvalidFieldNameException($exception, $query);
case 1052:
case 1060:
case 1110:
return new NonUniqueFieldNameException($exception, $query);
case 1064:
case 1149:
case 1287:
case 1341:
case 1342:
case 1343:
case 1344:
case 1382:
case 1479:
case 1541:
case 1554:
case 1626:
return new SyntaxErrorException($exception, $query);
case 1044:
case 1045:
case 1046:
case 1049:
case 1095:
case 1142:
case 1143:
case 1227:
case 1370:
case 1429:
case 2002:
case 2005:
case 2054:
return new ConnectionException($exception, $query);
case 2006:
case 4031:
return new ConnectionLost($exception, $query);
case 1048:
case 1121:
case 1138:
case 1171:
case 1252:
case 1263:
case 1364:
case 1566:
return new NotNullConstraintViolationException($exception, $query);
}
return new DriverException($exception, $query);
}
}

View file

@ -0,0 +1,74 @@
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\API\OCI;
use Doctrine\DBAL\Driver\API\ExceptionConverter as ExceptionConverterInterface;
use Doctrine\DBAL\Driver\Exception;
use Doctrine\DBAL\Exception\ConnectionException;
use Doctrine\DBAL\Exception\DatabaseDoesNotExist;
use Doctrine\DBAL\Exception\DatabaseObjectNotFoundException;
use Doctrine\DBAL\Exception\DriverException;
use Doctrine\DBAL\Exception\ForeignKeyConstraintViolationException;
use Doctrine\DBAL\Exception\InvalidFieldNameException;
use Doctrine\DBAL\Exception\NonUniqueFieldNameException;
use Doctrine\DBAL\Exception\NotNullConstraintViolationException;
use Doctrine\DBAL\Exception\SyntaxErrorException;
use Doctrine\DBAL\Exception\TableExistsException;
use Doctrine\DBAL\Exception\TableNotFoundException;
use Doctrine\DBAL\Exception\UniqueConstraintViolationException;
use Doctrine\DBAL\Query;
/** @internal */
final class ExceptionConverter implements ExceptionConverterInterface
{
/** @link http://www.dba-oracle.com/t_error_code_list.htm */
public function convert(Exception $exception, ?Query $query): DriverException
{
switch ($exception->getCode()) {
case 1:
case 2299:
case 38911:
return new UniqueConstraintViolationException($exception, $query);
case 904:
return new InvalidFieldNameException($exception, $query);
case 918:
case 960:
return new NonUniqueFieldNameException($exception, $query);
case 923:
return new SyntaxErrorException($exception, $query);
case 942:
return new TableNotFoundException($exception, $query);
case 955:
return new TableExistsException($exception, $query);
case 1017:
case 12545:
return new ConnectionException($exception, $query);
case 1400:
return new NotNullConstraintViolationException($exception, $query);
case 1918:
return new DatabaseDoesNotExist($exception, $query);
case 2289:
case 2443:
case 4080:
return new DatabaseObjectNotFoundException($exception, $query);
case 2266:
case 2291:
case 2292:
return new ForeignKeyConstraintViolationException($exception, $query);
}
return new DriverException($exception, $query);
}
}

View file

@ -0,0 +1,89 @@
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\API\PostgreSQL;
use Doctrine\DBAL\Driver\API\ExceptionConverter as ExceptionConverterInterface;
use Doctrine\DBAL\Driver\Exception;
use Doctrine\DBAL\Exception\ConnectionException;
use Doctrine\DBAL\Exception\DatabaseDoesNotExist;
use Doctrine\DBAL\Exception\DeadlockException;
use Doctrine\DBAL\Exception\DriverException;
use Doctrine\DBAL\Exception\ForeignKeyConstraintViolationException;
use Doctrine\DBAL\Exception\InvalidFieldNameException;
use Doctrine\DBAL\Exception\NonUniqueFieldNameException;
use Doctrine\DBAL\Exception\NotNullConstraintViolationException;
use Doctrine\DBAL\Exception\SchemaDoesNotExist;
use Doctrine\DBAL\Exception\SyntaxErrorException;
use Doctrine\DBAL\Exception\TableExistsException;
use Doctrine\DBAL\Exception\TableNotFoundException;
use Doctrine\DBAL\Exception\UniqueConstraintViolationException;
use Doctrine\DBAL\Query;
use function strpos;
/** @internal */
final class ExceptionConverter implements ExceptionConverterInterface
{
/** @link http://www.postgresql.org/docs/9.4/static/errcodes-appendix.html */
public function convert(Exception $exception, ?Query $query): DriverException
{
switch ($exception->getSQLState()) {
case '40001':
case '40P01':
return new DeadlockException($exception, $query);
case '0A000':
// Foreign key constraint violations during a TRUNCATE operation
// are considered "feature not supported" in PostgreSQL.
if (strpos($exception->getMessage(), 'truncate') !== false) {
return new ForeignKeyConstraintViolationException($exception, $query);
}
break;
case '23502':
return new NotNullConstraintViolationException($exception, $query);
case '23503':
return new ForeignKeyConstraintViolationException($exception, $query);
case '23505':
return new UniqueConstraintViolationException($exception, $query);
case '3D000':
return new DatabaseDoesNotExist($exception, $query);
case '3F000':
return new SchemaDoesNotExist($exception, $query);
case '42601':
return new SyntaxErrorException($exception, $query);
case '42702':
return new NonUniqueFieldNameException($exception, $query);
case '42703':
return new InvalidFieldNameException($exception, $query);
case '42P01':
return new TableNotFoundException($exception, $query);
case '42P07':
return new TableExistsException($exception, $query);
case '08006':
return new ConnectionException($exception, $query);
}
// Prior to fixing https://bugs.php.net/bug.php?id=64705 (PHP 7.4.10),
// in some cases (mainly connection errors) the PDO exception wouldn't provide a SQLSTATE via its code.
// We have to match against the SQLSTATE in the error message in these cases.
if ($exception->getCode() === 7 && strpos($exception->getMessage(), 'SQLSTATE[08006]') !== false) {
return new ConnectionException($exception, $query);
}
return new DriverException($exception, $query);
}
}

View file

@ -0,0 +1,69 @@
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\API\SQLSrv;
use Doctrine\DBAL\Driver\API\ExceptionConverter as ExceptionConverterInterface;
use Doctrine\DBAL\Driver\Exception;
use Doctrine\DBAL\Exception\ConnectionException;
use Doctrine\DBAL\Exception\DatabaseObjectNotFoundException;
use Doctrine\DBAL\Exception\DriverException;
use Doctrine\DBAL\Exception\ForeignKeyConstraintViolationException;
use Doctrine\DBAL\Exception\InvalidFieldNameException;
use Doctrine\DBAL\Exception\NonUniqueFieldNameException;
use Doctrine\DBAL\Exception\NotNullConstraintViolationException;
use Doctrine\DBAL\Exception\SyntaxErrorException;
use Doctrine\DBAL\Exception\TableExistsException;
use Doctrine\DBAL\Exception\TableNotFoundException;
use Doctrine\DBAL\Exception\UniqueConstraintViolationException;
use Doctrine\DBAL\Query;
/**
* @internal
*
* @link https://docs.microsoft.com/en-us/sql/relational-databases/errors-events/database-engine-events-and-errors
*/
final class ExceptionConverter implements ExceptionConverterInterface
{
public function convert(Exception $exception, ?Query $query): DriverException
{
switch ($exception->getCode()) {
case 102:
return new SyntaxErrorException($exception, $query);
case 207:
return new InvalidFieldNameException($exception, $query);
case 208:
return new TableNotFoundException($exception, $query);
case 209:
return new NonUniqueFieldNameException($exception, $query);
case 515:
return new NotNullConstraintViolationException($exception, $query);
case 547:
case 4712:
return new ForeignKeyConstraintViolationException($exception, $query);
case 2601:
case 2627:
return new UniqueConstraintViolationException($exception, $query);
case 2714:
return new TableExistsException($exception, $query);
case 3701:
case 15151:
return new DatabaseObjectNotFoundException($exception, $query);
case 11001:
case 18456:
return new ConnectionException($exception, $query);
}
return new DriverException($exception, $query);
}
}

View file

@ -0,0 +1,85 @@
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\API\SQLite;
use Doctrine\DBAL\Driver\API\ExceptionConverter as ExceptionConverterInterface;
use Doctrine\DBAL\Driver\Exception;
use Doctrine\DBAL\Exception\ConnectionException;
use Doctrine\DBAL\Exception\DriverException;
use Doctrine\DBAL\Exception\ForeignKeyConstraintViolationException;
use Doctrine\DBAL\Exception\InvalidFieldNameException;
use Doctrine\DBAL\Exception\LockWaitTimeoutException;
use Doctrine\DBAL\Exception\NonUniqueFieldNameException;
use Doctrine\DBAL\Exception\NotNullConstraintViolationException;
use Doctrine\DBAL\Exception\ReadOnlyException;
use Doctrine\DBAL\Exception\SyntaxErrorException;
use Doctrine\DBAL\Exception\TableExistsException;
use Doctrine\DBAL\Exception\TableNotFoundException;
use Doctrine\DBAL\Exception\UniqueConstraintViolationException;
use Doctrine\DBAL\Query;
use function strpos;
/** @internal */
final class ExceptionConverter implements ExceptionConverterInterface
{
/** @link http://www.sqlite.org/c3ref/c_abort.html */
public function convert(Exception $exception, ?Query $query): DriverException
{
if (strpos($exception->getMessage(), 'database is locked') !== false) {
return new LockWaitTimeoutException($exception, $query);
}
if (
strpos($exception->getMessage(), 'must be unique') !== false ||
strpos($exception->getMessage(), 'is not unique') !== false ||
strpos($exception->getMessage(), 'are not unique') !== false ||
strpos($exception->getMessage(), 'UNIQUE constraint failed') !== false
) {
return new UniqueConstraintViolationException($exception, $query);
}
if (
strpos($exception->getMessage(), 'may not be NULL') !== false ||
strpos($exception->getMessage(), 'NOT NULL constraint failed') !== false
) {
return new NotNullConstraintViolationException($exception, $query);
}
if (strpos($exception->getMessage(), 'no such table:') !== false) {
return new TableNotFoundException($exception, $query);
}
if (strpos($exception->getMessage(), 'already exists') !== false) {
return new TableExistsException($exception, $query);
}
if (strpos($exception->getMessage(), 'has no column named') !== false) {
return new InvalidFieldNameException($exception, $query);
}
if (strpos($exception->getMessage(), 'ambiguous column name') !== false) {
return new NonUniqueFieldNameException($exception, $query);
}
if (strpos($exception->getMessage(), 'syntax error') !== false) {
return new SyntaxErrorException($exception, $query);
}
if (strpos($exception->getMessage(), 'attempt to write a readonly database') !== false) {
return new ReadOnlyException($exception, $query);
}
if (strpos($exception->getMessage(), 'unable to open database file') !== false) {
return new ConnectionException($exception, $query);
}
if (strpos($exception->getMessage(), 'FOREIGN KEY constraint failed') !== false) {
return new ForeignKeyConstraintViolationException($exception, $query);
}
return new DriverException($exception, $query);
}
}

View file

@ -0,0 +1,80 @@
<?php
namespace Doctrine\DBAL\Driver\API\SQLite;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Platforms\SqlitePlatform;
use Doctrine\Deprecations\Deprecation;
use function array_merge;
use function strpos;
/**
* User-defined SQLite functions.
*
* @internal
*/
final class UserDefinedFunctions
{
private const DEFAULT_FUNCTIONS = [
'sqrt' => ['callback' => [SqlitePlatform::class, 'udfSqrt'], 'numArgs' => 1],
'mod' => ['callback' => [SqlitePlatform::class, 'udfMod'], 'numArgs' => 2],
'locate' => ['callback' => [SqlitePlatform::class, 'udfLocate'], 'numArgs' => -1],
];
/**
* @param callable(string, callable, int): bool $callback
* @param array<string, array{callback: callable, numArgs: int}> $additionalFunctions
*/
public static function register(callable $callback, array $additionalFunctions = []): void
{
$userDefinedFunctions = array_merge(self::DEFAULT_FUNCTIONS, $additionalFunctions);
foreach ($userDefinedFunctions as $function => $data) {
$callback($function, $data['callback'], $data['numArgs']);
}
}
/**
* User-defined function that implements MOD().
*
* @param int $a
* @param int $b
*/
public static function mod($a, $b): int
{
return $a % $b;
}
/**
* User-defined function that implements LOCATE().
*
* @param string $str
* @param string $substr
* @param int $offset
*/
public static function locate($str, $substr, $offset = 0): int
{
Deprecation::trigger(
'doctrine/dbal',
'https://github.com/doctrine/dbal/pull/5749',
'Relying on DBAL\'s emulated LOCATE() function is deprecated. '
. 'Use INSTR() or %s::getLocateExpression() instead.',
AbstractPlatform::class,
);
// SQL's LOCATE function works on 1-based positions, while PHP's strpos works on 0-based positions.
// So we have to make them compatible if an offset is given.
if ($offset > 0) {
$offset -= 1;
}
$pos = strpos($str, $substr, $offset);
if ($pos !== false) {
return $pos + 1;
}
return 0;
}
}

View file

@ -0,0 +1,100 @@
<?php
namespace Doctrine\DBAL\Driver;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Driver\API\ExceptionConverter as ExceptionConverterInterface;
use Doctrine\DBAL\Driver\API\IBMDB2\ExceptionConverter;
use Doctrine\DBAL\Exception as DBALException;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Platforms\DB2111Platform;
use Doctrine\DBAL\Platforms\DB2Platform;
use Doctrine\DBAL\Schema\DB2SchemaManager;
use Doctrine\DBAL\VersionAwarePlatformDriver;
use Doctrine\Deprecations\Deprecation;
use function assert;
use function preg_match;
use function version_compare;
/**
* Abstract base implementation of the {@see Driver} interface for IBM DB2 based drivers.
*/
abstract class AbstractDB2Driver implements VersionAwarePlatformDriver
{
/**
* {@inheritDoc}
*/
public function getDatabasePlatform()
{
return new DB2Platform();
}
/**
* {@inheritDoc}
*
* @deprecated Use {@link DB2Platform::createSchemaManager()} instead.
*/
public function getSchemaManager(Connection $conn, AbstractPlatform $platform)
{
Deprecation::triggerIfCalledFromOutside(
'doctrine/dbal',
'https://github.com/doctrine/dbal/pull/5458',
'AbstractDB2Driver::getSchemaManager() is deprecated.'
. ' Use DB2Platform::createSchemaManager() instead.',
);
assert($platform instanceof DB2Platform);
return new DB2SchemaManager($conn, $platform);
}
public function getExceptionConverter(): ExceptionConverterInterface
{
return new ExceptionConverter();
}
/**
* {@inheritDoc}
*/
public function createDatabasePlatformForVersion($version)
{
if (version_compare($this->getVersionNumber($version), '11.1', '>=')) {
return new DB2111Platform();
}
Deprecation::trigger(
'doctrine/dbal',
'https://github.com/doctrine/dbal/pull/5156',
'IBM DB2 < 11.1 support is deprecated and will be removed in DBAL 4.'
. ' Consider upgrading to IBM DB2 11.1 or later.',
);
return $this->getDatabasePlatform();
}
/**
* Detects IBM DB2 server version
*
* @param string $versionString Version string as returned by IBM DB2 server, i.e. 'DB2/LINUXX8664 11.5.8.0'
*
* @throws DBALException
*/
private function getVersionNumber(string $versionString): string
{
if (
preg_match(
'/^(?:[^\s]+\s)?(?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)/i',
$versionString,
$versionParts,
) !== 1
) {
throw DBALException::invalidPlatformVersionSpecified(
$versionString,
'^(?:[^\s]+\s)?<major_version>.<minor_version>.<patch_version>',
);
}
return $versionParts['major'] . '.' . $versionParts['minor'] . '.' . $versionParts['patch'];
}
}

View file

@ -0,0 +1,44 @@
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver;
use Exception as BaseException;
use Throwable;
/**
* Base implementation of the {@see Exception} interface.
*
* @internal
*
* @psalm-immutable
*/
abstract class AbstractException extends BaseException implements Exception
{
/**
* The SQLSTATE of the driver.
*/
private ?string $sqlState = null;
/**
* @param string $message The driver error message.
* @param string|null $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 Throwable|null $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;
}
}

View file

@ -0,0 +1,231 @@
<?php
namespace Doctrine\DBAL\Driver;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Driver\API\ExceptionConverter;
use Doctrine\DBAL\Driver\API\MySQL;
use Doctrine\DBAL\Exception;
use Doctrine\DBAL\Platforms\AbstractMySQLPlatform;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Platforms\MariaDb1010Platform;
use Doctrine\DBAL\Platforms\MariaDb1027Platform;
use Doctrine\DBAL\Platforms\MariaDb1043Platform;
use Doctrine\DBAL\Platforms\MariaDb1052Platform;
use Doctrine\DBAL\Platforms\MariaDb1060Platform;
use Doctrine\DBAL\Platforms\MySQL57Platform;
use Doctrine\DBAL\Platforms\MySQL80Platform;
use Doctrine\DBAL\Platforms\MySQL84Platform;
use Doctrine\DBAL\Platforms\MySQLPlatform;
use Doctrine\DBAL\Schema\MySQLSchemaManager;
use Doctrine\DBAL\VersionAwarePlatformDriver;
use Doctrine\Deprecations\Deprecation;
use function assert;
use function preg_match;
use function stripos;
use function version_compare;
/**
* Abstract base implementation of the {@see Driver} interface for MySQL based drivers.
*/
abstract class AbstractMySQLDriver implements VersionAwarePlatformDriver
{
/**
* {@inheritDoc}
*
* @throws Exception
*/
public function createDatabasePlatformForVersion($version)
{
$mariadb = stripos($version, 'mariadb') !== false;
if ($mariadb) {
$mariaDbVersion = $this->getMariaDbMysqlVersionNumber($version);
if (version_compare($mariaDbVersion, '10.10.0', '>=')) {
return new MariaDb1010Platform();
}
if (version_compare($mariaDbVersion, '10.6.0', '>=')) {
return new MariaDb1060Platform();
}
if (version_compare($mariaDbVersion, '10.5.2', '>=')) {
return new MariaDb1052Platform();
}
if (version_compare($mariaDbVersion, '10.4.3', '>=')) {
return new MariaDb1043Platform();
}
Deprecation::trigger(
'doctrine/dbal',
'https://github.com/doctrine/dbal/pull/6110',
'Support for MariaDB < 10.4 is deprecated and will be removed in DBAL 4.'
. ' Consider upgrading to a more recent version of MariaDB.',
);
if (version_compare($mariaDbVersion, '10.2.7', '>=')) {
return new MariaDb1027Platform();
}
} else {
$oracleMysqlVersion = $this->getOracleMysqlVersionNumber($version);
if (version_compare($oracleMysqlVersion, '8.4.0', '>=')) {
if (! version_compare($version, '8.4.0', '>=')) {
Deprecation::trigger(
'doctrine/orm',
'https://github.com/doctrine/dbal/pull/5779',
'Version detection logic for MySQL will change in DBAL 4. '
. 'Please specify the version as the server reports it, e.g. "8.4.0" instead of "8.4".',
);
}
return new MySQL84Platform();
}
if (version_compare($oracleMysqlVersion, '8', '>=')) {
if (! version_compare($version, '8.0.0', '>=')) {
Deprecation::trigger(
'doctrine/orm',
'https://github.com/doctrine/dbal/pull/5779',
'Version detection logic for MySQL will change in DBAL 4. '
. 'Please specify the version as the server reports it, e.g. "8.0.31" instead of "8".',
);
}
return new MySQL80Platform();
}
if (version_compare($oracleMysqlVersion, '5.7.9', '>=')) {
if (! version_compare($version, '5.7.9', '>=')) {
Deprecation::trigger(
'doctrine/orm',
'https://github.com/doctrine/dbal/pull/5779',
'Version detection logic for MySQL will change in DBAL 4. '
. 'Please specify the version as the server reports it, e.g. "5.7.40" instead of "5.7".',
);
}
return new MySQL57Platform();
}
Deprecation::trigger(
'doctrine/dbal',
'https://github.com/doctrine/dbal/pull/5072',
'MySQL 5.6 support is deprecated and will be removed in DBAL 4.'
. ' Consider upgrading to MySQL 5.7 or later.',
);
}
return $this->getDatabasePlatform();
}
/**
* Get a normalized 'version number' from the server string
* returned by Oracle MySQL servers.
*
* @param string $versionString Version string returned by the driver, i.e. '5.7.10'
*
* @throws Exception
*/
private function getOracleMysqlVersionNumber(string $versionString): string
{
if (
preg_match(
'/^(?P<major>\d+)(?:\.(?P<minor>\d+)(?:\.(?P<patch>\d+))?)?/',
$versionString,
$versionParts,
) !== 1
) {
throw Exception::invalidPlatformVersionSpecified(
$versionString,
'<major_version>.<minor_version>.<patch_version>',
);
}
$majorVersion = $versionParts['major'];
$minorVersion = $versionParts['minor'] ?? 0;
$patchVersion = $versionParts['patch'] ?? null;
if ($majorVersion === '5' && $minorVersion === '7') {
$patchVersion ??= '9';
} else {
$patchVersion ??= '0';
}
return $majorVersion . '.' . $minorVersion . '.' . $patchVersion;
}
/**
* Detect MariaDB server version, including hack for some mariadb distributions
* that starts with the prefix '5.5.5-'
*
* @param string $versionString Version string as returned by mariadb server, i.e. '5.5.5-Mariadb-10.0.8-xenial'
*
* @throws Exception
*/
private function getMariaDbMysqlVersionNumber(string $versionString): string
{
if (stripos($versionString, 'MariaDB') === 0) {
Deprecation::trigger(
'doctrine/orm',
'https://github.com/doctrine/dbal/pull/5779',
'Version detection logic for MySQL will change in DBAL 4. '
. 'Please specify the version as the server reports it, '
. 'e.g. "10.9.3-MariaDB" instead of "mariadb-10.9".',
);
}
if (
preg_match(
'/^(?:5\.5\.5-)?(mariadb-)?(?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)/i',
$versionString,
$versionParts,
) !== 1
) {
throw Exception::invalidPlatformVersionSpecified(
$versionString,
'^(?:5\.5\.5-)?(mariadb-)?<major_version>.<minor_version>.<patch_version>',
);
}
return $versionParts['major'] . '.' . $versionParts['minor'] . '.' . $versionParts['patch'];
}
/**
* {@inheritDoc}
*
* @return AbstractMySQLPlatform
*/
public function getDatabasePlatform()
{
return new MySQLPlatform();
}
/**
* {@inheritDoc}
*
* @deprecated Use {@link AbstractMySQLPlatform::createSchemaManager()} instead.
*
* @return MySQLSchemaManager
*/
public function getSchemaManager(Connection $conn, AbstractPlatform $platform)
{
Deprecation::triggerIfCalledFromOutside(
'doctrine/dbal',
'https://github.com/doctrine/dbal/pull/5458',
'AbstractMySQLDriver::getSchemaManager() is deprecated.'
. ' Use MySQLPlatform::createSchemaManager() instead.',
);
assert($platform instanceof AbstractMySQLPlatform);
return new MySQLSchemaManager($conn, $platform);
}
public function getExceptionConverter(): ExceptionConverter
{
return new MySQL\ExceptionConverter();
}
}

View file

@ -0,0 +1,65 @@
<?php
namespace Doctrine\DBAL\Driver;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Driver;
use Doctrine\DBAL\Driver\AbstractOracleDriver\EasyConnectString;
use Doctrine\DBAL\Driver\API\ExceptionConverter;
use Doctrine\DBAL\Driver\API\OCI;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Platforms\OraclePlatform;
use Doctrine\DBAL\Schema\OracleSchemaManager;
use Doctrine\Deprecations\Deprecation;
use function assert;
/**
* Abstract base implementation of the {@see Driver} interface for Oracle based drivers.
*/
abstract class AbstractOracleDriver implements Driver
{
/**
* {@inheritDoc}
*/
public function getDatabasePlatform()
{
return new OraclePlatform();
}
/**
* {@inheritDoc}
*
* @deprecated Use {@link OraclePlatform::createSchemaManager()} instead.
*/
public function getSchemaManager(Connection $conn, AbstractPlatform $platform)
{
Deprecation::triggerIfCalledFromOutside(
'doctrine/dbal',
'https://github.com/doctrine/dbal/pull/5458',
'AbstractOracleDriver::getSchemaManager() is deprecated.'
. ' Use OraclePlatform::createSchemaManager() instead.',
);
assert($platform instanceof OraclePlatform);
return new OracleSchemaManager($conn, $platform);
}
public function getExceptionConverter(): ExceptionConverter
{
return new OCI\ExceptionConverter();
}
/**
* Returns an appropriate Easy Connect String for the given parameters.
*
* @param array<string, mixed> $params The connection parameters to return the Easy Connect String for.
*
* @return string
*/
protected function getEasyConnectString(array $params)
{
return (string) EasyConnectString::fromConnectionParameters($params);
}
}

View file

@ -0,0 +1,116 @@
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\AbstractOracleDriver;
use function implode;
use function is_array;
use function sprintf;
/**
* Represents an Oracle Easy Connect string
*
* @link https://docs.oracle.com/database/121/NETAG/naming.htm
*/
final class EasyConnectString
{
private string $string;
private function __construct(string $string)
{
$this->string = $string;
}
public function __toString(): string
{
return $this->string;
}
/**
* Creates the object from an array representation
*
* @param mixed[] $params
*/
public static function fromArray(array $params): self
{
return new self(self::renderParams($params));
}
/**
* Creates the object from the given DBAL connection parameters.
*
* @param mixed[] $params
*/
public static function fromConnectionParameters(array $params): self
{
if (isset($params['connectstring'])) {
return new self($params['connectstring']);
}
if (! isset($params['host'])) {
return new self($params['dbname'] ?? '');
}
$connectData = [];
if (isset($params['servicename']) || isset($params['dbname'])) {
$serviceKey = 'SID';
if (isset($params['service'])) {
$serviceKey = 'SERVICE_NAME';
}
$serviceName = $params['servicename'] ?? $params['dbname'];
$connectData[$serviceKey] = $serviceName;
}
if (isset($params['instancename'])) {
$connectData['INSTANCE_NAME'] = $params['instancename'];
}
if (! empty($params['pooled'])) {
$connectData['SERVER'] = 'POOLED';
}
return self::fromArray([
'DESCRIPTION' => [
'ADDRESS' => [
'PROTOCOL' => 'TCP',
'HOST' => $params['host'],
'PORT' => $params['port'] ?? 1521,
],
'CONNECT_DATA' => $connectData,
],
]);
}
/** @param mixed[] $params */
private static function renderParams(array $params): string
{
$chunks = [];
foreach ($params as $key => $value) {
$string = self::renderValue($value);
if ($string === '') {
continue;
}
$chunks[] = sprintf('(%s=%s)', $key, $string);
}
return implode('', $chunks);
}
/** @param mixed $value */
private static function renderValue($value): string
{
if (is_array($value)) {
return self::renderParams($value);
}
return (string) $value;
}
}

View file

@ -0,0 +1,93 @@
<?php
namespace Doctrine\DBAL\Driver;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Driver\API\ExceptionConverter;
use Doctrine\DBAL\Driver\API\PostgreSQL;
use Doctrine\DBAL\Exception;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Platforms\PostgreSQL100Platform;
use Doctrine\DBAL\Platforms\PostgreSQL120Platform;
use Doctrine\DBAL\Platforms\PostgreSQL94Platform;
use Doctrine\DBAL\Platforms\PostgreSQLPlatform;
use Doctrine\DBAL\Schema\PostgreSQLSchemaManager;
use Doctrine\DBAL\VersionAwarePlatformDriver;
use Doctrine\Deprecations\Deprecation;
use function assert;
use function preg_match;
use function version_compare;
/**
* Abstract base implementation of the {@see Driver} interface for PostgreSQL based drivers.
*/
abstract class AbstractPostgreSQLDriver implements VersionAwarePlatformDriver
{
/**
* {@inheritDoc}
*/
public function createDatabasePlatformForVersion($version)
{
if (preg_match('/^(?P<major>\d+)(?:\.(?P<minor>\d+)(?:\.(?P<patch>\d+))?)?/', $version, $versionParts) !== 1) {
throw Exception::invalidPlatformVersionSpecified(
$version,
'<major_version>.<minor_version>.<patch_version>',
);
}
$majorVersion = $versionParts['major'];
$minorVersion = $versionParts['minor'] ?? 0;
$patchVersion = $versionParts['patch'] ?? 0;
$version = $majorVersion . '.' . $minorVersion . '.' . $patchVersion;
if (version_compare($version, '12.0', '>=')) {
return new PostgreSQL120Platform();
}
if (version_compare($version, '10.0', '>=')) {
return new PostgreSQL100Platform();
}
Deprecation::trigger(
'doctrine/dbal',
'https://github.com/doctrine/dbal/pull/5060',
'PostgreSQL 9 support is deprecated and will be removed in DBAL 4.'
. ' Consider upgrading to Postgres 10 or later.',
);
return new PostgreSQL94Platform();
}
/**
* {@inheritDoc}
*/
public function getDatabasePlatform()
{
return new PostgreSQL94Platform();
}
/**
* {@inheritDoc}
*
* @deprecated Use {@link PostgreSQLPlatform::createSchemaManager()} instead.
*/
public function getSchemaManager(Connection $conn, AbstractPlatform $platform)
{
Deprecation::triggerIfCalledFromOutside(
'doctrine/dbal',
'https://github.com/doctrine/dbal/pull/5458',
'AbstractPostgreSQLDriver::getSchemaManager() is deprecated.'
. ' Use PostgreSQLPlatform::createSchemaManager() instead.',
);
assert($platform instanceof PostgreSQLPlatform);
return new PostgreSQLSchemaManager($conn, $platform);
}
public function getExceptionConverter(): ExceptionConverter
{
return new PostgreSQL\ExceptionConverter();
}
}

View file

@ -0,0 +1,53 @@
<?php
namespace Doctrine\DBAL\Driver;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Driver;
use Doctrine\DBAL\Driver\API\ExceptionConverter as ExceptionConverterInterface;
use Doctrine\DBAL\Driver\API\SQLSrv\ExceptionConverter;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Platforms\SQLServer2012Platform;
use Doctrine\DBAL\Platforms\SQLServerPlatform;
use Doctrine\DBAL\Schema\SQLServerSchemaManager;
use Doctrine\Deprecations\Deprecation;
use function assert;
/**
* Abstract base implementation of the {@see Driver} interface for Microsoft SQL Server based drivers.
*/
abstract class AbstractSQLServerDriver implements Driver
{
/**
* {@inheritDoc}
*/
public function getDatabasePlatform()
{
return new SQLServer2012Platform();
}
/**
* {@inheritDoc}
*
* @deprecated Use {@link SQLServerPlatform::createSchemaManager()} instead.
*/
public function getSchemaManager(Connection $conn, AbstractPlatform $platform)
{
Deprecation::triggerIfCalledFromOutside(
'doctrine/dbal',
'https://github.com/doctrine/dbal/pull/5458',
'AbstractSQLServerDriver::getSchemaManager() is deprecated.'
. ' Use SQLServerPlatform::createSchemaManager() instead.',
);
assert($platform instanceof SQLServerPlatform);
return new SQLServerSchemaManager($conn, $platform);
}
public function getExceptionConverter(): ExceptionConverterInterface
{
return new ExceptionConverter();
}
}

View file

@ -0,0 +1,20 @@
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\AbstractSQLServerDriver\Exception;
use Doctrine\DBAL\Driver\AbstractException;
/**
* @internal
*
* @psalm-immutable
*/
final class PortWithoutHost extends AbstractException
{
public static function new(): self
{
return new self('Connection port specified without the host');
}
}

View file

@ -0,0 +1,52 @@
<?php
namespace Doctrine\DBAL\Driver;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Driver;
use Doctrine\DBAL\Driver\API\ExceptionConverter;
use Doctrine\DBAL\Driver\API\SQLite;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Platforms\SqlitePlatform;
use Doctrine\DBAL\Schema\SqliteSchemaManager;
use Doctrine\Deprecations\Deprecation;
use function assert;
/**
* Abstract base implementation of the {@see Doctrine\DBAL\Driver} interface for SQLite based drivers.
*/
abstract class AbstractSQLiteDriver implements Driver
{
/**
* {@inheritDoc}
*/
public function getDatabasePlatform()
{
return new SqlitePlatform();
}
/**
* {@inheritDoc}
*
* @deprecated Use {@link SqlitePlatform::createSchemaManager()} instead.
*/
public function getSchemaManager(Connection $conn, AbstractPlatform $platform)
{
Deprecation::triggerIfCalledFromOutside(
'doctrine/dbal',
'https://github.com/doctrine/dbal/pull/5458',
'AbstractSQLiteDriver::getSchemaManager() is deprecated.'
. ' Use SqlitePlatform::createSchemaManager() instead.',
);
assert($platform instanceof SqlitePlatform);
return new SqliteSchemaManager($conn, $platform);
}
public function getExceptionConverter(): ExceptionConverter
{
return new SQLite\ExceptionConverter();
}
}

View file

@ -0,0 +1,31 @@
<?php
namespace Doctrine\DBAL\Driver\AbstractSQLiteDriver\Middleware;
use Doctrine\DBAL\Driver;
use Doctrine\DBAL\Driver\Connection;
use Doctrine\DBAL\Driver\Middleware;
use Doctrine\DBAL\Driver\Middleware\AbstractDriverMiddleware;
use SensitiveParameter;
class EnableForeignKeys implements Middleware
{
public function wrap(Driver $driver): Driver
{
return new class ($driver) extends AbstractDriverMiddleware {
/**
* {@inheritDoc}
*/
public function connect(
#[SensitiveParameter]
array $params
): Connection {
$connection = parent::connect($params);
$connection->exec('PRAGMA foreign_keys=ON');
return $connection;
}
};
}
}

View file

@ -0,0 +1,86 @@
<?php
namespace Doctrine\DBAL\Driver;
use Doctrine\DBAL\ParameterType;
/**
* Connection interface.
* Driver connections must implement this interface.
*
* @method resource|object getNativeConnection()
*/
interface Connection
{
/**
* Prepares a statement for execution and returns a Statement object.
*
* @throws Exception
*/
public function prepare(string $sql): Statement;
/**
* Executes an SQL statement, returning a result set as a Statement object.
*
* @throws Exception
*/
public function query(string $sql): Result;
/**
* Quotes a string for use in a query.
*
* The usage of this method is discouraged. Use prepared statements
* or {@see AbstractPlatform::quoteStringLiteral()} instead.
*
* @param mixed $value
* @param int $type
*
* @return mixed
*/
public function quote($value, $type = ParameterType::STRING);
/**
* Executes an SQL statement and return the number of affected rows.
*
* @throws Exception
*/
public function exec(string $sql): int;
/**
* Returns the ID of the last inserted row or sequence value.
*
* @param string|null $name
*
* @return string|int|false
*
* @throws Exception
*/
public function lastInsertId($name = null);
/**
* Initiates a transaction.
*
* @return bool TRUE on success or FALSE on failure.
*
* @throws Exception
*/
public function beginTransaction();
/**
* Commits a transaction.
*
* @return bool TRUE on success or FALSE on failure.
*
* @throws Exception
*/
public function commit();
/**
* Rolls back the current transaction, as initiated by beginTransaction().
*
* @return bool TRUE on success or FALSE on failure.
*
* @throws Exception
*/
public function rollBack();
}

View file

@ -0,0 +1,20 @@
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver;
use Throwable;
/** @psalm-immutable */
interface Exception extends Throwable
{
/**
* Returns the SQLSTATE the driver was in at the time the error occurred.
*
* Returns null if the driver does not provide a SQLSTATE for the error occurred.
*
* @return string|null
*/
public function getSQLState();
}

View file

@ -0,0 +1,23 @@
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\Exception;
use Doctrine\DBAL\Driver\AbstractException;
use function sprintf;
/**
* @internal
*
* @psalm-immutable
*/
final class UnknownParameterType extends AbstractException
{
/** @param mixed $type */
public static function new($type): self
{
return new self(sprintf('Unknown parameter type, %d given.', $type));
}
}

View file

@ -0,0 +1,73 @@
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver;
/** @internal */
final class FetchUtils
{
/**
* @return mixed|false
*
* @throws Exception
*/
public static function fetchOne(Result $result)
{
$row = $result->fetchNumeric();
if ($row === false) {
return false;
}
return $row[0];
}
/**
* @return list<list<mixed>>
*
* @throws Exception
*/
public static function fetchAllNumeric(Result $result): array
{
$rows = [];
while (($row = $result->fetchNumeric()) !== false) {
$rows[] = $row;
}
return $rows;
}
/**
* @return list<array<string,mixed>>
*
* @throws Exception
*/
public static function fetchAllAssociative(Result $result): array
{
$rows = [];
while (($row = $result->fetchAssociative()) !== false) {
$rows[] = $row;
}
return $rows;
}
/**
* @return list<mixed>
*
* @throws Exception
*/
public static function fetchFirstColumn(Result $result): array
{
$rows = [];
while (($row = $result->fetchOne()) !== false) {
$rows[] = $row;
}
return $rows;
}
}

View file

@ -0,0 +1,141 @@
<?php
namespace Doctrine\DBAL\Driver\IBMDB2;
use Doctrine\DBAL\Driver\IBMDB2\Exception\ConnectionError;
use Doctrine\DBAL\Driver\IBMDB2\Exception\PrepareFailed;
use Doctrine\DBAL\Driver\IBMDB2\Exception\StatementError;
use Doctrine\DBAL\Driver\Result as ResultInterface;
use Doctrine\DBAL\Driver\ServerInfoAwareConnection;
use Doctrine\DBAL\Driver\Statement as DriverStatement;
use Doctrine\DBAL\ParameterType;
use Doctrine\Deprecations\Deprecation;
use stdClass;
use function assert;
use function db2_autocommit;
use function db2_commit;
use function db2_escape_string;
use function db2_exec;
use function db2_last_insert_id;
use function db2_num_rows;
use function db2_prepare;
use function db2_rollback;
use function db2_server_info;
use function error_get_last;
use const DB2_AUTOCOMMIT_OFF;
use const DB2_AUTOCOMMIT_ON;
final class Connection implements ServerInfoAwareConnection
{
/** @var resource */
private $connection;
/**
* @internal The connection can be only instantiated by its driver.
*
* @param resource $connection
*/
public function __construct($connection)
{
$this->connection = $connection;
}
/**
* {@inheritDoc}
*/
public function getServerVersion()
{
$serverInfo = db2_server_info($this->connection);
assert($serverInfo instanceof stdClass);
return $serverInfo->DBMS_VER;
}
public function prepare(string $sql): DriverStatement
{
$stmt = @db2_prepare($this->connection, $sql);
if ($stmt === false) {
throw PrepareFailed::new(error_get_last());
}
return new Statement($stmt);
}
public function query(string $sql): ResultInterface
{
return $this->prepare($sql)->execute();
}
/**
* {@inheritDoc}
*/
public function quote($value, $type = ParameterType::STRING)
{
$value = db2_escape_string($value);
if ($type === ParameterType::INTEGER) {
return $value;
}
return "'" . $value . "'";
}
public function exec(string $sql): int
{
$stmt = @db2_exec($this->connection, $sql);
if ($stmt === false) {
throw StatementError::new();
}
return db2_num_rows($stmt);
}
/**
* {@inheritDoc}
*/
public function lastInsertId($name = null)
{
if ($name !== null) {
Deprecation::triggerIfCalledFromOutside(
'doctrine/dbal',
'https://github.com/doctrine/dbal/issues/4687',
'The usage of Connection::lastInsertId() with a sequence name is deprecated.',
);
}
return db2_last_insert_id($this->connection) ?? false;
}
public function beginTransaction(): bool
{
return db2_autocommit($this->connection, DB2_AUTOCOMMIT_OFF);
}
public function commit(): bool
{
if (! db2_commit($this->connection)) {
throw ConnectionError::new($this->connection);
}
return db2_autocommit($this->connection, DB2_AUTOCOMMIT_ON);
}
public function rollBack(): bool
{
if (! db2_rollback($this->connection)) {
throw ConnectionError::new($this->connection);
}
return db2_autocommit($this->connection, DB2_AUTOCOMMIT_ON);
}
/** @return resource */
public function getNativeConnection()
{
return $this->connection;
}
}

View file

@ -0,0 +1,84 @@
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\IBMDB2;
use SensitiveParameter;
use function implode;
use function sprintf;
use function strpos;
/**
* IBM DB2 DSN
*/
final class DataSourceName
{
private string $string;
private function __construct(
#[SensitiveParameter]
string $string
) {
$this->string = $string;
}
public function toString(): string
{
return $this->string;
}
/**
* Creates the object from an array representation
*
* @param array<string,mixed> $params
*/
public static function fromArray(
#[SensitiveParameter]
array $params
): self {
$chunks = [];
foreach ($params as $key => $value) {
$chunks[] = sprintf('%s=%s', $key, $value);
}
return new self(implode(';', $chunks));
}
/**
* Creates the object from the given DBAL connection parameters.
*
* @param array<string,mixed> $params
*/
public static function fromConnectionParameters(
#[SensitiveParameter]
array $params
): self {
if (isset($params['dbname']) && strpos($params['dbname'], '=') !== false) {
return new self($params['dbname']);
}
$dsnParams = [];
foreach (
[
'host' => 'HOSTNAME',
'port' => 'PORT',
'protocol' => 'PROTOCOL',
'dbname' => 'DATABASE',
'user' => 'UID',
'password' => 'PWD',
] as $dbalParam => $dsnParam
) {
if (! isset($params[$dbalParam])) {
continue;
}
$dsnParams[$dsnParam] = $params[$dbalParam];
}
return self::fromArray($dsnParams);
}
}

View file

@ -0,0 +1,41 @@
<?php
namespace Doctrine\DBAL\Driver\IBMDB2;
use Doctrine\DBAL\Driver\AbstractDB2Driver;
use Doctrine\DBAL\Driver\IBMDB2\Exception\ConnectionFailed;
use SensitiveParameter;
use function db2_connect;
use function db2_pconnect;
final class Driver extends AbstractDB2Driver
{
/**
* {@inheritDoc}
*
* @return Connection
*/
public function connect(
#[SensitiveParameter]
array $params
) {
$dataSourceName = DataSourceName::fromConnectionParameters($params)->toString();
$username = $params['user'] ?? '';
$password = $params['password'] ?? '';
$driverOptions = $params['driverOptions'] ?? [];
if (! empty($params['persistent'])) {
$connection = db2_pconnect($dataSourceName, $username, $password, $driverOptions);
} else {
$connection = db2_connect($dataSourceName, $username, $password, $driverOptions);
}
if ($connection === false) {
throw ConnectionFailed::new();
}
return new Connection($connection);
}
}

View file

@ -0,0 +1,27 @@
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\IBMDB2\Exception;
use Doctrine\DBAL\Driver\AbstractException;
/**
* @internal
*
* @psalm-immutable
*/
final class CannotCopyStreamToStream extends AbstractException
{
/** @psalm-param array{message: string}|null $error */
public static function new(?array $error): self
{
$message = 'Could not copy source stream to temporary file';
if ($error !== null) {
$message .= ': ' . $error['message'];
}
return new self($message);
}
}

View file

@ -0,0 +1,27 @@
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\IBMDB2\Exception;
use Doctrine\DBAL\Driver\AbstractException;
/**
* @internal
*
* @psalm-immutable
*/
final class CannotCreateTemporaryFile extends AbstractException
{
/** @psalm-param array{message: string}|null $error */
public static function new(?array $error): self
{
$message = 'Could not create temporary file';
if ($error !== null) {
$message .= ': ' . $error['message'];
}
return new self($message);
}
}

View file

@ -0,0 +1,29 @@
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\IBMDB2\Exception;
use Doctrine\DBAL\Driver\AbstractException;
use function db2_conn_error;
use function db2_conn_errormsg;
/**
* @internal
*
* @psalm-immutable
*/
final class ConnectionError extends AbstractException
{
/** @param resource $connection */
public static function new($connection): self
{
$message = db2_conn_errormsg($connection);
$sqlState = db2_conn_error($connection);
return Factory::create($message, static function (int $code) use ($message, $sqlState): self {
return new self($message, $sqlState, $code);
});
}
}

View file

@ -0,0 +1,28 @@
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\IBMDB2\Exception;
use Doctrine\DBAL\Driver\AbstractException;
use function db2_conn_error;
use function db2_conn_errormsg;
/**
* @internal
*
* @psalm-immutable
*/
final class ConnectionFailed extends AbstractException
{
public static function new(): self
{
$message = db2_conn_errormsg();
$sqlState = db2_conn_error();
return Factory::create($message, static function (int $code) use ($message, $sqlState): self {
return new self($message, $sqlState, $code);
});
}
}

View file

@ -0,0 +1,35 @@
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\IBMDB2\Exception;
use Doctrine\DBAL\Driver\AbstractException;
use function preg_match;
/**
* @internal
*
* @psalm-immutable
*/
final class Factory
{
/**
* @param callable(int): T $constructor
*
* @return T
*
* @template T of AbstractException
*/
public static function create(string $message, callable $constructor): AbstractException
{
$code = 0;
if (preg_match('/ SQL(\d+)N /', $message, $matches) === 1) {
$code = -(int) $matches[1];
}
return $constructor($code);
}
}

View file

@ -0,0 +1,25 @@
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\IBMDB2\Exception;
use Doctrine\DBAL\Driver\AbstractException;
/**
* @internal
*
* @psalm-immutable
*/
final class PrepareFailed extends AbstractException
{
/** @psalm-param array{message: string}|null $error */
public static function new(?array $error): self
{
if ($error === null) {
return new self('Unknown error');
}
return new self($error['message']);
}
}

View file

@ -0,0 +1,34 @@
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\IBMDB2\Exception;
use Doctrine\DBAL\Driver\AbstractException;
use function db2_stmt_error;
use function db2_stmt_errormsg;
/**
* @internal
*
* @psalm-immutable
*/
final class StatementError extends AbstractException
{
/** @param resource|null $statement */
public static function new($statement = null): self
{
if ($statement !== null) {
$message = db2_stmt_errormsg($statement);
$sqlState = db2_stmt_error($statement);
} else {
$message = db2_stmt_errormsg();
$sqlState = db2_stmt_error();
}
return Factory::create($message, static function (int $code) use ($message, $sqlState): self {
return new self($message, $sqlState, $code);
});
}
}

View file

@ -0,0 +1,113 @@
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\IBMDB2;
use Doctrine\DBAL\Driver\FetchUtils;
use Doctrine\DBAL\Driver\IBMDB2\Exception\StatementError;
use Doctrine\DBAL\Driver\Result as ResultInterface;
use function db2_fetch_array;
use function db2_fetch_assoc;
use function db2_free_result;
use function db2_num_fields;
use function db2_num_rows;
use function db2_stmt_error;
final class Result implements ResultInterface
{
/** @var resource */
private $statement;
/**
* @internal The result can be only instantiated by its driver connection or statement.
*
* @param resource $statement
*/
public function __construct($statement)
{
$this->statement = $statement;
}
/**
* {@inheritDoc}
*/
public function fetchNumeric()
{
$row = @db2_fetch_array($this->statement);
if ($row === false && db2_stmt_error($this->statement) !== '02000') {
throw StatementError::new($this->statement);
}
return $row;
}
/**
* {@inheritDoc}
*/
public function fetchAssociative()
{
$row = @db2_fetch_assoc($this->statement);
if ($row === false && db2_stmt_error($this->statement) !== '02000') {
throw StatementError::new($this->statement);
}
return $row;
}
/**
* {@inheritDoc}
*/
public function fetchOne()
{
return FetchUtils::fetchOne($this);
}
/**
* {@inheritDoc}
*/
public function fetchAllNumeric(): array
{
return FetchUtils::fetchAllNumeric($this);
}
/**
* {@inheritDoc}
*/
public function fetchAllAssociative(): array
{
return FetchUtils::fetchAllAssociative($this);
}
/**
* {@inheritDoc}
*/
public function fetchFirstColumn(): array
{
return FetchUtils::fetchFirstColumn($this);
}
public function rowCount(): int
{
return @db2_num_rows($this->statement);
}
public function columnCount(): int
{
$count = db2_num_fields($this->statement);
if ($count !== false) {
return $count;
}
return 0;
}
public function free(): void
{
db2_free_result($this->statement);
}
}

View file

@ -0,0 +1,220 @@
<?php
namespace Doctrine\DBAL\Driver\IBMDB2;
use Doctrine\DBAL\Driver\Exception;
use Doctrine\DBAL\Driver\IBMDB2\Exception\CannotCopyStreamToStream;
use Doctrine\DBAL\Driver\IBMDB2\Exception\CannotCreateTemporaryFile;
use Doctrine\DBAL\Driver\IBMDB2\Exception\StatementError;
use Doctrine\DBAL\Driver\Result as ResultInterface;
use Doctrine\DBAL\Driver\Statement as StatementInterface;
use Doctrine\DBAL\ParameterType;
use Doctrine\Deprecations\Deprecation;
use function assert;
use function db2_bind_param;
use function db2_execute;
use function error_get_last;
use function fclose;
use function func_num_args;
use function is_int;
use function is_resource;
use function stream_copy_to_stream;
use function stream_get_meta_data;
use function tmpfile;
use const DB2_BINARY;
use const DB2_CHAR;
use const DB2_LONG;
use const DB2_PARAM_FILE;
use const DB2_PARAM_IN;
final class Statement implements StatementInterface
{
/** @var resource */
private $stmt;
/** @var mixed[] */
private array $parameters = [];
/**
* Map of LOB parameter positions to the tuples containing reference to the variable bound to the driver statement
* and the temporary file handle bound to the underlying statement
*
* @var array<int,string|resource|null>
*/
private array $lobs = [];
/**
* @internal The statement can be only instantiated by its driver connection.
*
* @param resource $stmt
*/
public function __construct($stmt)
{
$this->stmt = $stmt;
}
/**
* {@inheritDoc}
*/
public function bindValue($param, $value, $type = ParameterType::STRING): bool
{
assert(is_int($param));
if (func_num_args() < 3) {
Deprecation::trigger(
'doctrine/dbal',
'https://github.com/doctrine/dbal/pull/5558',
'Not passing $type to Statement::bindValue() is deprecated.'
. ' Pass the type corresponding to the parameter being bound.',
);
}
return $this->bindParam($param, $value, $type);
}
/**
* {@inheritDoc}
*
* @deprecated Use {@see bindValue()} instead.
*/
public function bindParam($param, &$variable, $type = ParameterType::STRING, $length = null): bool
{
Deprecation::trigger(
'doctrine/dbal',
'https://github.com/doctrine/dbal/pull/5563',
'%s is deprecated. Use bindValue() instead.',
__METHOD__,
);
assert(is_int($param));
if (func_num_args() < 3) {
Deprecation::trigger(
'doctrine/dbal',
'https://github.com/doctrine/dbal/pull/5558',
'Not passing $type to Statement::bindParam() is deprecated.'
. ' Pass the type corresponding to the parameter being bound.',
);
}
switch ($type) {
case ParameterType::INTEGER:
$this->bind($param, $variable, DB2_PARAM_IN, DB2_LONG);
break;
case ParameterType::LARGE_OBJECT:
$this->lobs[$param] = &$variable;
break;
default:
$this->bind($param, $variable, DB2_PARAM_IN, DB2_CHAR);
break;
}
return true;
}
/**
* @param int $position Parameter position
* @param mixed $variable
*
* @throws Exception
*/
private function bind($position, &$variable, int $parameterType, int $dataType): void
{
$this->parameters[$position] =& $variable;
if (! db2_bind_param($this->stmt, $position, '', $parameterType, $dataType)) {
throw StatementError::new($this->stmt);
}
}
/**
* {@inheritDoc}
*/
public function execute($params = null): ResultInterface
{
if ($params !== null) {
Deprecation::trigger(
'doctrine/dbal',
'https://github.com/doctrine/dbal/pull/5556',
'Passing $params to Statement::execute() is deprecated. Bind parameters using'
. ' Statement::bindParam() or Statement::bindValue() instead.',
);
}
$handles = $this->bindLobs();
$result = @db2_execute($this->stmt, $params ?? $this->parameters);
foreach ($handles as $handle) {
fclose($handle);
}
$this->lobs = [];
if ($result === false) {
throw StatementError::new($this->stmt);
}
return new Result($this->stmt);
}
/**
* @return list<resource>
*
* @throws Exception
*/
private function bindLobs(): array
{
$handles = [];
foreach ($this->lobs as $param => $value) {
if (is_resource($value)) {
$handle = $handles[] = $this->createTemporaryFile();
$path = stream_get_meta_data($handle)['uri'];
$this->copyStreamToStream($value, $handle);
$this->bind($param, $path, DB2_PARAM_FILE, DB2_BINARY);
} else {
$this->bind($param, $value, DB2_PARAM_IN, DB2_CHAR);
}
unset($value);
}
return $handles;
}
/**
* @return resource
*
* @throws Exception
*/
private function createTemporaryFile()
{
$handle = @tmpfile();
if ($handle === false) {
throw CannotCreateTemporaryFile::new(error_get_last());
}
return $handle;
}
/**
* @param resource $source
* @param resource $target
*
* @throws Exception
*/
private function copyStreamToStream($source, $target): void
{
if (@stream_copy_to_stream($source, $target) === false) {
throw CannotCopyStreamToStream::new(error_get_last());
}
}
}

View file

@ -0,0 +1,12 @@
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver;
use Doctrine\DBAL\Driver;
interface Middleware
{
public function wrap(Driver $driver): Driver;
}

View file

@ -0,0 +1,113 @@
<?php
namespace Doctrine\DBAL\Driver\Middleware;
use Doctrine\DBAL\Driver\Connection;
use Doctrine\DBAL\Driver\Result;
use Doctrine\DBAL\Driver\ServerInfoAwareConnection;
use Doctrine\DBAL\Driver\Statement;
use Doctrine\DBAL\ParameterType;
use Doctrine\Deprecations\Deprecation;
use LogicException;
use function get_class;
use function method_exists;
use function sprintf;
abstract class AbstractConnectionMiddleware implements ServerInfoAwareConnection
{
private Connection $wrappedConnection;
public function __construct(Connection $wrappedConnection)
{
$this->wrappedConnection = $wrappedConnection;
}
public function prepare(string $sql): Statement
{
return $this->wrappedConnection->prepare($sql);
}
public function query(string $sql): Result
{
return $this->wrappedConnection->query($sql);
}
/**
* {@inheritDoc}
*/
public function quote($value, $type = ParameterType::STRING)
{
return $this->wrappedConnection->quote($value, $type);
}
public function exec(string $sql): int
{
return $this->wrappedConnection->exec($sql);
}
/**
* {@inheritDoc}
*/
public function lastInsertId($name = null)
{
if ($name !== null) {
Deprecation::triggerIfCalledFromOutside(
'doctrine/dbal',
'https://github.com/doctrine/dbal/issues/4687',
'The usage of Connection::lastInsertId() with a sequence name is deprecated.',
);
}
return $this->wrappedConnection->lastInsertId($name);
}
/**
* {@inheritDoc}
*/
public function beginTransaction()
{
return $this->wrappedConnection->beginTransaction();
}
/**
* {@inheritDoc}
*/
public function commit()
{
return $this->wrappedConnection->commit();
}
/**
* {@inheritDoc}
*/
public function rollBack()
{
return $this->wrappedConnection->rollBack();
}
/**
* {@inheritDoc}
*/
public function getServerVersion()
{
if (! $this->wrappedConnection instanceof ServerInfoAwareConnection) {
throw new LogicException('The underlying connection is not a ServerInfoAwareConnection');
}
return $this->wrappedConnection->getServerVersion();
}
/** @return resource|object */
public function getNativeConnection()
{
if (! method_exists($this->wrappedConnection, 'getNativeConnection')) {
throw new LogicException(sprintf(
'The driver connection %s does not support accessing the native connection.',
get_class($this->wrappedConnection),
));
}
return $this->wrappedConnection->getNativeConnection();
}
}

View file

@ -0,0 +1,73 @@
<?php
namespace Doctrine\DBAL\Driver\Middleware;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Driver;
use Doctrine\DBAL\Driver\API\ExceptionConverter;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\VersionAwarePlatformDriver;
use Doctrine\Deprecations\Deprecation;
use SensitiveParameter;
abstract class AbstractDriverMiddleware implements VersionAwarePlatformDriver
{
private Driver $wrappedDriver;
public function __construct(Driver $wrappedDriver)
{
$this->wrappedDriver = $wrappedDriver;
}
/**
* {@inheritDoc}
*/
public function connect(
#[SensitiveParameter]
array $params
) {
return $this->wrappedDriver->connect($params);
}
/**
* {@inheritDoc}
*/
public function getDatabasePlatform()
{
return $this->wrappedDriver->getDatabasePlatform();
}
/**
* {@inheritDoc}
*
* @deprecated Use {@link AbstractPlatform::createSchemaManager()} instead.
*/
public function getSchemaManager(Connection $conn, AbstractPlatform $platform)
{
Deprecation::triggerIfCalledFromOutside(
'doctrine/dbal',
'https://github.com/doctrine/dbal/pull/5458',
'AbstractDriverMiddleware::getSchemaManager() is deprecated.'
. ' Use AbstractPlatform::createSchemaManager() instead.',
);
return $this->wrappedDriver->getSchemaManager($conn, $platform);
}
public function getExceptionConverter(): ExceptionConverter
{
return $this->wrappedDriver->getExceptionConverter();
}
/**
* {@inheritDoc}
*/
public function createDatabasePlatformForVersion($version)
{
if ($this->wrappedDriver instanceof VersionAwarePlatformDriver) {
return $this->wrappedDriver->createDatabasePlatformForVersion($version);
}
return $this->wrappedDriver->getDatabasePlatform();
}
}

View file

@ -0,0 +1,78 @@
<?php
namespace Doctrine\DBAL\Driver\Middleware;
use Doctrine\DBAL\Driver\Result;
abstract class AbstractResultMiddleware implements Result
{
private Result $wrappedResult;
public function __construct(Result $result)
{
$this->wrappedResult = $result;
}
/**
* {@inheritDoc}
*/
public function fetchNumeric()
{
return $this->wrappedResult->fetchNumeric();
}
/**
* {@inheritDoc}
*/
public function fetchAssociative()
{
return $this->wrappedResult->fetchAssociative();
}
/**
* {@inheritDoc}
*/
public function fetchOne()
{
return $this->wrappedResult->fetchOne();
}
/**
* {@inheritDoc}
*/
public function fetchAllNumeric(): array
{
return $this->wrappedResult->fetchAllNumeric();
}
/**
* {@inheritDoc}
*/
public function fetchAllAssociative(): array
{
return $this->wrappedResult->fetchAllAssociative();
}
/**
* {@inheritDoc}
*/
public function fetchFirstColumn(): array
{
return $this->wrappedResult->fetchFirstColumn();
}
public function rowCount(): int
{
return $this->wrappedResult->rowCount();
}
public function columnCount(): int
{
return $this->wrappedResult->columnCount();
}
public function free(): void
{
$this->wrappedResult->free();
}
}

View file

@ -0,0 +1,71 @@
<?php
namespace Doctrine\DBAL\Driver\Middleware;
use Doctrine\DBAL\Driver\Result;
use Doctrine\DBAL\Driver\Statement;
use Doctrine\DBAL\ParameterType;
use Doctrine\Deprecations\Deprecation;
use function func_num_args;
abstract class AbstractStatementMiddleware implements Statement
{
private Statement $wrappedStatement;
public function __construct(Statement $wrappedStatement)
{
$this->wrappedStatement = $wrappedStatement;
}
/**
* {@inheritDoc}
*/
public function bindValue($param, $value, $type = ParameterType::STRING)
{
if (func_num_args() < 3) {
Deprecation::trigger(
'doctrine/dbal',
'https://github.com/doctrine/dbal/pull/5558',
'Not passing $type to Statement::bindValue() is deprecated.'
. ' Pass the type corresponding to the parameter being bound.',
);
}
return $this->wrappedStatement->bindValue($param, $value, $type);
}
/**
* {@inheritDoc}
*
* @deprecated Use {@see bindValue()} instead.
*/
public function bindParam($param, &$variable, $type = ParameterType::STRING, $length = null)
{
Deprecation::trigger(
'doctrine/dbal',
'https://github.com/doctrine/dbal/pull/5563',
'%s is deprecated. Use bindValue() instead.',
__METHOD__,
);
if (func_num_args() < 3) {
Deprecation::trigger(
'doctrine/dbal',
'https://github.com/doctrine/dbal/pull/5558',
'Not passing $type to Statement::bindParam() is deprecated.'
. ' Pass the type corresponding to the parameter being bound.',
);
}
return $this->wrappedStatement->bindParam($param, $variable, $type, $length);
}
/**
* {@inheritDoc}
*/
public function execute($params = null): Result
{
return $this->wrappedStatement->execute($params);
}
}

View file

@ -0,0 +1,141 @@
<?php
namespace Doctrine\DBAL\Driver\Mysqli;
use Doctrine\DBAL\Driver\Mysqli\Exception\ConnectionError;
use Doctrine\DBAL\Driver\Result as ResultInterface;
use Doctrine\DBAL\Driver\ServerInfoAwareConnection;
use Doctrine\DBAL\Driver\Statement as DriverStatement;
use Doctrine\DBAL\ParameterType;
use Doctrine\Deprecations\Deprecation;
use mysqli;
use mysqli_sql_exception;
final class Connection implements ServerInfoAwareConnection
{
/**
* Name of the option to set connection flags
*/
public const OPTION_FLAGS = 'flags';
private mysqli $connection;
/** @internal The connection can be only instantiated by its driver. */
public function __construct(mysqli $connection)
{
$this->connection = $connection;
}
/**
* Retrieves mysqli native resource handle.
*
* Could be used if part of your application is not using DBAL.
*
* @deprecated Call {@see getNativeConnection()} instead.
*/
public function getWrappedResourceHandle(): mysqli
{
Deprecation::trigger(
'doctrine/dbal',
'https://github.com/doctrine/dbal/pull/5037',
'%s is deprecated, call getNativeConnection() instead.',
__METHOD__,
);
return $this->getNativeConnection();
}
public function getServerVersion(): string
{
return $this->connection->get_server_info();
}
public function prepare(string $sql): DriverStatement
{
try {
$stmt = $this->connection->prepare($sql);
} catch (mysqli_sql_exception $e) {
throw ConnectionError::upcast($e);
}
if ($stmt === false) {
throw ConnectionError::new($this->connection);
}
return new Statement($stmt);
}
public function query(string $sql): ResultInterface
{
return $this->prepare($sql)->execute();
}
/**
* {@inheritDoc}
*/
public function quote($value, $type = ParameterType::STRING)
{
return "'" . $this->connection->escape_string($value) . "'";
}
public function exec(string $sql): int
{
try {
$result = $this->connection->query($sql);
} catch (mysqli_sql_exception $e) {
throw ConnectionError::upcast($e);
}
if ($result === false) {
throw ConnectionError::new($this->connection);
}
return $this->connection->affected_rows;
}
/**
* {@inheritDoc}
*/
public function lastInsertId($name = null)
{
if ($name !== null) {
Deprecation::triggerIfCalledFromOutside(
'doctrine/dbal',
'https://github.com/doctrine/dbal/issues/4687',
'The usage of Connection::lastInsertId() with a sequence name is deprecated.',
);
}
return $this->connection->insert_id;
}
public function beginTransaction(): bool
{
$this->connection->begin_transaction();
return true;
}
public function commit(): bool
{
try {
return $this->connection->commit();
} catch (mysqli_sql_exception $e) {
return false;
}
}
public function rollBack(): bool
{
try {
return $this->connection->rollback();
} catch (mysqli_sql_exception $e) {
return false;
}
}
public function getNativeConnection(): mysqli
{
return $this->connection;
}
}

View file

@ -0,0 +1,117 @@
<?php
namespace Doctrine\DBAL\Driver\Mysqli;
use Doctrine\DBAL\Driver\AbstractMySQLDriver;
use Doctrine\DBAL\Driver\Mysqli\Exception\ConnectionFailed;
use Doctrine\DBAL\Driver\Mysqli\Exception\HostRequired;
use Doctrine\DBAL\Driver\Mysqli\Initializer\Charset;
use Doctrine\DBAL\Driver\Mysqli\Initializer\Options;
use Doctrine\DBAL\Driver\Mysqli\Initializer\Secure;
use Generator;
use mysqli;
use mysqli_sql_exception;
use SensitiveParameter;
final class Driver extends AbstractMySQLDriver
{
/**
* {@inheritDoc}
*
* @return Connection
*/
public function connect(
#[SensitiveParameter]
array $params
) {
if (! empty($params['persistent'])) {
if (! isset($params['host'])) {
throw HostRequired::forPersistentConnection();
}
$host = 'p:' . $params['host'];
} else {
$host = $params['host'] ?? null;
}
$connection = new mysqli();
foreach ($this->compilePreInitializers($params) as $initializer) {
$initializer->initialize($connection);
}
try {
$success = @$connection->real_connect(
$host,
$params['user'] ?? null,
$params['password'] ?? null,
$params['dbname'] ?? null,
$params['port'] ?? null,
$params['unix_socket'] ?? null,
$params['driverOptions'][Connection::OPTION_FLAGS] ?? 0,
);
} catch (mysqli_sql_exception $e) {
throw ConnectionFailed::upcast($e);
}
if (! $success) {
throw ConnectionFailed::new($connection);
}
foreach ($this->compilePostInitializers($params) as $initializer) {
$initializer->initialize($connection);
}
return new Connection($connection);
}
/**
* @param array<string, mixed> $params
*
* @return Generator<int, Initializer>
*/
private function compilePreInitializers(
#[SensitiveParameter]
array $params
): Generator {
unset($params['driverOptions'][Connection::OPTION_FLAGS]);
if (isset($params['driverOptions']) && $params['driverOptions'] !== []) {
yield new Options($params['driverOptions']);
}
if (
! isset($params['ssl_key']) &&
! isset($params['ssl_cert']) &&
! isset($params['ssl_ca']) &&
! isset($params['ssl_capath']) &&
! isset($params['ssl_cipher'])
) {
return;
}
yield new Secure(
$params['ssl_key'] ?? '',
$params['ssl_cert'] ?? '',
$params['ssl_ca'] ?? '',
$params['ssl_capath'] ?? '',
$params['ssl_cipher'] ?? '',
);
}
/**
* @param array<string, mixed> $params
*
* @return Generator<int, Initializer>
*/
private function compilePostInitializers(
#[SensitiveParameter]
array $params
): Generator {
if (! isset($params['charset'])) {
return;
}
yield new Charset($params['charset']);
}
}

View file

@ -0,0 +1,31 @@
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\Mysqli\Exception;
use Doctrine\DBAL\Driver\AbstractException;
use mysqli;
use mysqli_sql_exception;
use ReflectionProperty;
/**
* @internal
*
* @psalm-immutable
*/
final class ConnectionError extends AbstractException
{
public static function new(mysqli $connection): self
{
return new self($connection->error, $connection->sqlstate, $connection->errno);
}
public static function upcast(mysqli_sql_exception $exception): self
{
$p = new ReflectionProperty(mysqli_sql_exception::class, 'sqlstate');
$p->setAccessible(true);
return new self($exception->getMessage(), $p->getValue($exception), (int) $exception->getCode(), $exception);
}
}

View file

@ -0,0 +1,36 @@
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\Mysqli\Exception;
use Doctrine\DBAL\Driver\AbstractException;
use mysqli;
use mysqli_sql_exception;
use ReflectionProperty;
use function assert;
/**
* @internal
*
* @psalm-immutable
*/
final class ConnectionFailed extends AbstractException
{
public static function new(mysqli $connection): self
{
$error = $connection->connect_error;
assert($error !== null);
return new self($error, 'HY000', $connection->connect_errno);
}
public static function upcast(mysqli_sql_exception $exception): self
{
$p = new ReflectionProperty(mysqli_sql_exception::class, 'sqlstate');
$p->setAccessible(true);
return new self($exception->getMessage(), $p->getValue($exception), (int) $exception->getCode(), $exception);
}
}

View file

@ -0,0 +1,22 @@
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\Mysqli\Exception;
use Doctrine\DBAL\Driver\AbstractException;
use function sprintf;
/**
* @internal
*
* @psalm-immutable
*/
final class FailedReadingStreamOffset extends AbstractException
{
public static function new(int $parameter): self
{
return new self(sprintf('Failed reading the stream resource for parameter #%d.', $parameter));
}
}

View file

@ -0,0 +1,20 @@
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\Mysqli\Exception;
use Doctrine\DBAL\Driver\AbstractException;
/**
* @internal
*
* @psalm-immutable
*/
final class HostRequired extends AbstractException
{
public static function forPersistentConnection(): self
{
return new self('The "host" parameter is required for a persistent connection');
}
}

View file

@ -0,0 +1,42 @@
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\Mysqli\Exception;
use Doctrine\DBAL\Driver\AbstractException;
use mysqli;
use mysqli_sql_exception;
use ReflectionProperty;
use function sprintf;
/**
* @internal
*
* @psalm-immutable
*/
final class InvalidCharset extends AbstractException
{
public static function fromCharset(mysqli $connection, string $charset): self
{
return new self(
sprintf('Failed to set charset "%s": %s', $charset, $connection->error),
$connection->sqlstate,
$connection->errno,
);
}
public static function upcast(mysqli_sql_exception $exception, string $charset): self
{
$p = new ReflectionProperty(mysqli_sql_exception::class, 'sqlstate');
$p->setAccessible(true);
return new self(
sprintf('Failed to set charset "%s": %s', $charset, $exception->getMessage()),
$p->getValue($exception),
(int) $exception->getCode(),
$exception,
);
}
}

View file

@ -0,0 +1,25 @@
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\Mysqli\Exception;
use Doctrine\DBAL\Driver\AbstractException;
use function sprintf;
/**
* @internal
*
* @psalm-immutable
*/
final class InvalidOption extends AbstractException
{
/** @param mixed $value */
public static function fromOption(int $option, $value): self
{
return new self(
sprintf('Failed to set option %d with value "%s"', $option, $value),
);
}
}

View file

@ -0,0 +1,24 @@
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\Mysqli\Exception;
use Doctrine\DBAL\Driver\AbstractException;
use function sprintf;
/**
* @internal
*
* @psalm-immutable
*/
final class NonStreamResourceUsedAsLargeObject extends AbstractException
{
public static function new(int $parameter): self
{
return new self(
sprintf('The resource passed as a LARGE_OBJECT parameter #%d must be of type "stream"', $parameter),
);
}
}

View file

@ -0,0 +1,31 @@
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\Mysqli\Exception;
use Doctrine\DBAL\Driver\AbstractException;
use mysqli_sql_exception;
use mysqli_stmt;
use ReflectionProperty;
/**
* @internal
*
* @psalm-immutable
*/
final class StatementError extends AbstractException
{
public static function new(mysqli_stmt $statement): self
{
return new self($statement->error, $statement->sqlstate, $statement->errno);
}
public static function upcast(mysqli_sql_exception $exception): self
{
$p = new ReflectionProperty(mysqli_sql_exception::class, 'sqlstate');
$p->setAccessible(true);
return new self($exception->getMessage(), $p->getValue($exception), (int) $exception->getCode(), $exception);
}
}

View file

@ -0,0 +1,14 @@
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\Mysqli;
use Doctrine\DBAL\Driver\Exception;
use mysqli;
interface Initializer
{
/** @throws Exception */
public function initialize(mysqli $connection): void;
}

View file

@ -0,0 +1,35 @@
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\Mysqli\Initializer;
use Doctrine\DBAL\Driver\Mysqli\Exception\InvalidCharset;
use Doctrine\DBAL\Driver\Mysqli\Initializer;
use mysqli;
use mysqli_sql_exception;
final class Charset implements Initializer
{
private string $charset;
public function __construct(string $charset)
{
$this->charset = $charset;
}
public function initialize(mysqli $connection): void
{
try {
$success = $connection->set_charset($this->charset);
} catch (mysqli_sql_exception $e) {
throw InvalidCharset::upcast($e, $this->charset);
}
if ($success) {
return;
}
throw InvalidCharset::fromCharset($connection, $this->charset);
}
}

View file

@ -0,0 +1,32 @@
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\Mysqli\Initializer;
use Doctrine\DBAL\Driver\Mysqli\Exception\InvalidOption;
use Doctrine\DBAL\Driver\Mysqli\Initializer;
use mysqli;
use function mysqli_options;
final class Options implements Initializer
{
/** @var array<int,mixed> */
private array $options;
/** @param array<int,mixed> $options */
public function __construct(array $options)
{
$this->options = $options;
}
public function initialize(mysqli $connection): void
{
foreach ($this->options as $option => $value) {
if (! mysqli_options($connection, $option, $value)) {
throw InvalidOption::fromOption($option, $value);
}
}
}
}

View file

@ -0,0 +1,38 @@
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\Mysqli\Initializer;
use Doctrine\DBAL\Driver\Mysqli\Initializer;
use mysqli;
use SensitiveParameter;
final class Secure implements Initializer
{
private string $key;
private string $cert;
private string $ca;
private string $capath;
private string $cipher;
public function __construct(
#[SensitiveParameter]
string $key,
string $cert,
string $ca,
string $capath,
string $cipher
) {
$this->key = $key;
$this->cert = $cert;
$this->ca = $ca;
$this->capath = $capath;
$this->cipher = $cipher;
}
public function initialize(mysqli $connection): void
{
$connection->ssl_set($this->key, $this->cert, $this->ca, $this->capath, $this->cipher);
}
}

View file

@ -0,0 +1,179 @@
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\Mysqli;
use Doctrine\DBAL\Driver\Exception;
use Doctrine\DBAL\Driver\FetchUtils;
use Doctrine\DBAL\Driver\Mysqli\Exception\StatementError;
use Doctrine\DBAL\Driver\Result as ResultInterface;
use mysqli_sql_exception;
use mysqli_stmt;
use function array_column;
use function array_combine;
use function array_fill;
use function count;
final class Result implements ResultInterface
{
private mysqli_stmt $statement;
/**
* Whether the statement result has columns. The property should be used only after the result metadata
* has been fetched ({@see $metadataFetched}). Otherwise, the property value is undetermined.
*/
private bool $hasColumns = false;
/**
* Mapping of statement result column indexes to their names. The property should be used only
* if the statement result has columns ({@see $hasColumns}). Otherwise, the property value is undetermined.
*
* @var array<int,string>
*/
private array $columnNames = [];
/** @var mixed[] */
private array $boundValues = [];
/**
* @internal The result can be only instantiated by its driver connection or statement.
*
* @throws Exception
*/
public function __construct(mysqli_stmt $statement)
{
$this->statement = $statement;
$meta = $statement->result_metadata();
if ($meta === false) {
return;
}
$this->hasColumns = true;
$this->columnNames = array_column($meta->fetch_fields(), 'name');
$meta->free();
// Store result of every execution which has it. Otherwise it will be impossible
// to execute a new statement in case if the previous one has non-fetched rows
// @link http://dev.mysql.com/doc/refman/5.7/en/commands-out-of-sync.html
$this->statement->store_result();
// Bind row values _after_ storing the result. Otherwise, if mysqli is compiled with libmysql,
// it will have to allocate as much memory as it may be needed for the given column type
// (e.g. for a LONGBLOB column it's 4 gigabytes)
// @link https://bugs.php.net/bug.php?id=51386#1270673122
//
// Make sure that the values are bound after each execution. Otherwise, if free() has been
// previously called on the result, the values are unbound making the statement unusable.
//
// It's also important that row values are bound after _each_ call to store_result(). Otherwise,
// if mysqli is compiled with libmysql, subsequently fetched string values will get truncated
// to the length of the ones fetched during the previous execution.
$this->boundValues = array_fill(0, count($this->columnNames), null);
// The following is necessary as PHP cannot handle references to properties properly
$refs = &$this->boundValues;
if (! $this->statement->bind_result(...$refs)) {
throw StatementError::new($this->statement);
}
}
/**
* {@inheritDoc}
*/
public function fetchNumeric()
{
try {
$ret = $this->statement->fetch();
} catch (mysqli_sql_exception $e) {
throw StatementError::upcast($e);
}
if ($ret === false) {
throw StatementError::new($this->statement);
}
if ($ret === null) {
return false;
}
$values = [];
foreach ($this->boundValues as $v) {
$values[] = $v;
}
return $values;
}
/**
* {@inheritDoc}
*/
public function fetchAssociative()
{
$values = $this->fetchNumeric();
if ($values === false) {
return false;
}
return array_combine($this->columnNames, $values);
}
/**
* {@inheritDoc}
*/
public function fetchOne()
{
return FetchUtils::fetchOne($this);
}
/**
* {@inheritDoc}
*/
public function fetchAllNumeric(): array
{
return FetchUtils::fetchAllNumeric($this);
}
/**
* {@inheritDoc}
*/
public function fetchAllAssociative(): array
{
return FetchUtils::fetchAllAssociative($this);
}
/**
* {@inheritDoc}
*/
public function fetchFirstColumn(): array
{
return FetchUtils::fetchFirstColumn($this);
}
public function rowCount(): int
{
if ($this->hasColumns) {
return $this->statement->num_rows;
}
return $this->statement->affected_rows;
}
public function columnCount(): int
{
return $this->statement->field_count;
}
public function free(): void
{
$this->statement->free_result();
}
}

View file

@ -0,0 +1,239 @@
<?php
namespace Doctrine\DBAL\Driver\Mysqli;
use Doctrine\DBAL\Driver\Exception;
use Doctrine\DBAL\Driver\Exception\UnknownParameterType;
use Doctrine\DBAL\Driver\Mysqli\Exception\FailedReadingStreamOffset;
use Doctrine\DBAL\Driver\Mysqli\Exception\NonStreamResourceUsedAsLargeObject;
use Doctrine\DBAL\Driver\Mysqli\Exception\StatementError;
use Doctrine\DBAL\Driver\Result as ResultInterface;
use Doctrine\DBAL\Driver\Statement as StatementInterface;
use Doctrine\DBAL\ParameterType;
use Doctrine\Deprecations\Deprecation;
use mysqli_sql_exception;
use mysqli_stmt;
use function array_fill;
use function assert;
use function count;
use function feof;
use function fread;
use function func_num_args;
use function get_resource_type;
use function is_int;
use function is_resource;
use function str_repeat;
final class Statement implements StatementInterface
{
private const PARAM_TYPE_MAP = [
ParameterType::ASCII => 's',
ParameterType::STRING => 's',
ParameterType::BINARY => 's',
ParameterType::BOOLEAN => 'i',
ParameterType::NULL => 's',
ParameterType::INTEGER => 'i',
ParameterType::LARGE_OBJECT => 'b',
];
private mysqli_stmt $stmt;
/** @var mixed[] */
private array $boundValues;
private string $types;
/**
* Contains ref values for bindValue().
*
* @var mixed[]
*/
private array $values = [];
/** @internal The statement can be only instantiated by its driver connection. */
public function __construct(mysqli_stmt $stmt)
{
$this->stmt = $stmt;
$paramCount = $this->stmt->param_count;
$this->types = str_repeat('s', $paramCount);
$this->boundValues = array_fill(1, $paramCount, null);
}
/**
* @deprecated Use {@see bindValue()} instead.
*
* {@inheritDoc}
*
* @psalm-assert ParameterType::* $type
*/
public function bindParam($param, &$variable, $type = ParameterType::STRING, $length = null): bool
{
Deprecation::trigger(
'doctrine/dbal',
'https://github.com/doctrine/dbal/pull/5563',
'%s is deprecated. Use bindValue() instead.',
__METHOD__,
);
assert(is_int($param));
if (func_num_args() < 3) {
Deprecation::trigger(
'doctrine/dbal',
'https://github.com/doctrine/dbal/pull/5558',
'Not passing $type to Statement::bindParam() is deprecated.'
. ' Pass the type corresponding to the parameter being bound.',
);
}
if (! isset(self::PARAM_TYPE_MAP[$type])) {
throw UnknownParameterType::new($type);
}
$this->boundValues[$param] =& $variable;
$this->types[$param - 1] = self::PARAM_TYPE_MAP[$type];
return true;
}
/**
* {@inheritDoc}
*
* @psalm-assert ParameterType::* $type
*/
public function bindValue($param, $value, $type = ParameterType::STRING): bool
{
assert(is_int($param));
if (func_num_args() < 3) {
Deprecation::trigger(
'doctrine/dbal',
'https://github.com/doctrine/dbal/pull/5558',
'Not passing $type to Statement::bindValue() is deprecated.'
. ' Pass the type corresponding to the parameter being bound.',
);
}
if (! isset(self::PARAM_TYPE_MAP[$type])) {
throw UnknownParameterType::new($type);
}
$this->values[$param] = $value;
$this->boundValues[$param] =& $this->values[$param];
$this->types[$param - 1] = self::PARAM_TYPE_MAP[$type];
return true;
}
/**
* {@inheritDoc}
*/
public function execute($params = null): ResultInterface
{
if ($params !== null) {
Deprecation::trigger(
'doctrine/dbal',
'https://github.com/doctrine/dbal/pull/5556',
'Passing $params to Statement::execute() is deprecated. Bind parameters using'
. ' Statement::bindParam() or Statement::bindValue() instead.',
);
}
if ($params !== null && count($params) > 0) {
if (! $this->bindUntypedValues($params)) {
throw StatementError::new($this->stmt);
}
} elseif (count($this->boundValues) > 0) {
$this->bindTypedParameters();
}
try {
$result = $this->stmt->execute();
} catch (mysqli_sql_exception $e) {
throw StatementError::upcast($e);
}
if (! $result) {
throw StatementError::new($this->stmt);
}
return new Result($this->stmt);
}
/**
* Binds parameters with known types previously bound to the statement
*
* @throws Exception
*/
private function bindTypedParameters(): void
{
$streams = $values = [];
$types = $this->types;
foreach ($this->boundValues as $parameter => $value) {
assert(is_int($parameter));
if (! isset($types[$parameter - 1])) {
$types[$parameter - 1] = self::PARAM_TYPE_MAP[ParameterType::STRING];
}
if ($types[$parameter - 1] === self::PARAM_TYPE_MAP[ParameterType::LARGE_OBJECT]) {
if (is_resource($value)) {
if (get_resource_type($value) !== 'stream') {
throw NonStreamResourceUsedAsLargeObject::new($parameter);
}
$streams[$parameter] = $value;
$values[$parameter] = null;
continue;
}
$types[$parameter - 1] = self::PARAM_TYPE_MAP[ParameterType::STRING];
}
$values[$parameter] = $value;
}
if (! $this->stmt->bind_param($types, ...$values)) {
throw StatementError::new($this->stmt);
}
$this->sendLongData($streams);
}
/**
* Handle $this->_longData after regular query parameters have been bound
*
* @param array<int, resource> $streams
*
* @throws Exception
*/
private function sendLongData(array $streams): void
{
foreach ($streams as $paramNr => $stream) {
while (! feof($stream)) {
$chunk = fread($stream, 8192);
if ($chunk === false) {
throw FailedReadingStreamOffset::new($paramNr);
}
if (! $this->stmt->send_long_data($paramNr - 1, $chunk)) {
throw StatementError::new($this->stmt);
}
}
}
}
/**
* Binds a array of values to bound parameters.
*
* @param mixed[] $values
*/
private function bindUntypedValues(array $values): bool
{
return $this->stmt->bind_param(str_repeat('s', count($values)), ...$values);
}
}

View file

@ -0,0 +1,170 @@
<?php
namespace Doctrine\DBAL\Driver\OCI8;
use Doctrine\DBAL\Driver\Exception;
use Doctrine\DBAL\Driver\OCI8\Exception\Error;
use Doctrine\DBAL\Driver\OCI8\Exception\SequenceDoesNotExist;
use Doctrine\DBAL\Driver\Result as ResultInterface;
use Doctrine\DBAL\Driver\ServerInfoAwareConnection;
use Doctrine\DBAL\Driver\Statement as DriverStatement;
use Doctrine\DBAL\ParameterType;
use Doctrine\DBAL\SQL\Parser;
use Doctrine\Deprecations\Deprecation;
use function addcslashes;
use function assert;
use function is_float;
use function is_int;
use function is_resource;
use function oci_commit;
use function oci_parse;
use function oci_rollback;
use function oci_server_version;
use function preg_match;
use function str_replace;
final class Connection implements ServerInfoAwareConnection
{
/** @var resource */
private $connection;
private Parser $parser;
private ExecutionMode $executionMode;
/**
* @internal The connection can be only instantiated by its driver.
*
* @param resource $connection
*/
public function __construct($connection)
{
$this->connection = $connection;
$this->parser = new Parser(false);
$this->executionMode = new ExecutionMode();
}
public function getServerVersion(): string
{
$version = oci_server_version($this->connection);
if ($version === false) {
throw Error::new($this->connection);
}
$result = preg_match('/\s+(\d+\.\d+\.\d+\.\d+\.\d+)\s+/', $version, $matches);
assert($result === 1);
return $matches[1];
}
/** @throws Parser\Exception */
public function prepare(string $sql): DriverStatement
{
$visitor = new ConvertPositionalToNamedPlaceholders();
$this->parser->parse($sql, $visitor);
$statement = oci_parse($this->connection, $visitor->getSQL());
assert(is_resource($statement));
return new Statement($this->connection, $statement, $visitor->getParameterMap(), $this->executionMode);
}
/**
* @throws Exception
* @throws Parser\Exception
*/
public function query(string $sql): ResultInterface
{
return $this->prepare($sql)->execute();
}
/**
* {@inheritDoc}
*/
public function quote($value, $type = ParameterType::STRING)
{
if (is_int($value) || is_float($value)) {
return $value;
}
$value = str_replace("'", "''", $value);
return "'" . addcslashes($value, "\000\n\r\\\032") . "'";
}
/**
* @throws Exception
* @throws Parser\Exception
*/
public function exec(string $sql): int
{
return $this->prepare($sql)->execute()->rowCount();
}
/**
* {@inheritDoc}
*
* @param string|null $name
*
* @return int|false
*
* @throws Parser\Exception
*/
public function lastInsertId($name = null)
{
if ($name === null) {
return false;
}
Deprecation::triggerIfCalledFromOutside(
'doctrine/dbal',
'https://github.com/doctrine/dbal/issues/4687',
'The usage of Connection::lastInsertId() with a sequence name is deprecated.',
);
$result = $this->query('SELECT ' . $name . '.CURRVAL FROM DUAL')->fetchOne();
if ($result === false) {
throw SequenceDoesNotExist::new();
}
return (int) $result;
}
public function beginTransaction(): bool
{
$this->executionMode->disableAutoCommit();
return true;
}
public function commit(): bool
{
if (! oci_commit($this->connection)) {
throw Error::new($this->connection);
}
$this->executionMode->enableAutoCommit();
return true;
}
public function rollBack(): bool
{
if (! oci_rollback($this->connection)) {
throw Error::new($this->connection);
}
$this->executionMode->enableAutoCommit();
return true;
}
/** @return resource */
public function getNativeConnection()
{
return $this->connection;
}
}

View file

@ -0,0 +1,56 @@
<?php
namespace Doctrine\DBAL\Driver\OCI8;
use Doctrine\DBAL\SQL\Parser\Visitor;
use function count;
use function implode;
/**
* Converts positional (?) into named placeholders (:param<num>).
*
* Oracle does not support positional parameters, hence this method converts all
* positional parameters into artificially named parameters.
*
* @internal This class is not covered by the backward compatibility promise
*/
final class ConvertPositionalToNamedPlaceholders implements Visitor
{
/** @var list<string> */
private array $buffer = [];
/** @var array<int,string> */
private array $parameterMap = [];
public function acceptOther(string $sql): void
{
$this->buffer[] = $sql;
}
public function acceptPositionalParameter(string $sql): void
{
$position = count($this->parameterMap) + 1;
$param = ':param' . $position;
$this->parameterMap[$position] = $param;
$this->buffer[] = $param;
}
public function acceptNamedParameter(string $sql): void
{
$this->buffer[] = $sql;
}
public function getSQL(): string
{
return implode('', $this->buffer);
}
/** @return array<int,string> */
public function getParameterMap(): array
{
return $this->parameterMap;
}
}

View file

@ -0,0 +1,58 @@
<?php
namespace Doctrine\DBAL\Driver\OCI8;
use Doctrine\DBAL\Driver\AbstractOracleDriver;
use Doctrine\DBAL\Driver\OCI8\Exception\ConnectionFailed;
use Doctrine\DBAL\Driver\OCI8\Exception\InvalidConfiguration;
use SensitiveParameter;
use function oci_connect;
use function oci_new_connect;
use function oci_pconnect;
use const OCI_NO_AUTO_COMMIT;
/**
* A Doctrine DBAL driver for the Oracle OCI8 PHP extensions.
*/
final class Driver extends AbstractOracleDriver
{
/**
* {@inheritDoc}
*
* @return Connection
*/
public function connect(
#[SensitiveParameter]
array $params
) {
$username = $params['user'] ?? '';
$password = $params['password'] ?? '';
$charset = $params['charset'] ?? '';
$sessionMode = $params['sessionMode'] ?? OCI_NO_AUTO_COMMIT;
$connectionString = $this->getEasyConnectString($params);
$persistent = ! empty($params['persistent']);
$exclusive = ! empty($params['driverOptions']['exclusive']);
if ($persistent && $exclusive) {
throw InvalidConfiguration::forPersistentAndExclusive();
}
if ($persistent) {
$connection = @oci_pconnect($username, $password, $connectionString, $charset, $sessionMode);
} elseif ($exclusive) {
$connection = @oci_new_connect($username, $password, $connectionString, $charset, $sessionMode);
} else {
$connection = @oci_connect($username, $password, $connectionString, $charset, $sessionMode);
}
if ($connection === false) {
throw ConnectionFailed::new();
}
return new Connection($connection);
}
}

View file

@ -0,0 +1,26 @@
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\OCI8\Exception;
use Doctrine\DBAL\Driver\AbstractException;
use function assert;
use function oci_error;
/**
* @internal
*
* @psalm-immutable
*/
final class ConnectionFailed extends AbstractException
{
public static function new(): self
{
$error = oci_error();
assert($error !== false);
return new self($error['message'], null, $error['code']);
}
}

View file

@ -0,0 +1,27 @@
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\OCI8\Exception;
use Doctrine\DBAL\Driver\AbstractException;
use function assert;
use function oci_error;
/**
* @internal
*
* @psalm-immutable
*/
final class Error extends AbstractException
{
/** @param resource $resource */
public static function new($resource): self
{
$error = oci_error($resource);
assert($error !== false);
return new self($error['message'], null, $error['code']);
}
}

View file

@ -0,0 +1,20 @@
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\OCI8\Exception;
use Doctrine\DBAL\Driver\AbstractException;
/**
* @internal
*
* @psalm-immutable
*/
final class InvalidConfiguration extends AbstractException
{
public static function forPersistentAndExclusive(): self
{
return new self('The "persistent" parameter and the "exclusive" driver option are mutually exclusive');
}
}

View file

@ -0,0 +1,27 @@
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\OCI8\Exception;
use Doctrine\DBAL\Driver\AbstractException;
use function sprintf;
/**
* @internal
*
* @psalm-immutable
*/
final class NonTerminatedStringLiteral extends AbstractException
{
public static function new(int $offset): self
{
return new self(
sprintf(
'The statement contains non-terminated string literal starting at offset %d.',
$offset,
),
);
}
}

View file

@ -0,0 +1,20 @@
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\OCI8\Exception;
use Doctrine\DBAL\Driver\AbstractException;
/**
* @internal
*
* @psalm-immutable
*/
final class SequenceDoesNotExist extends AbstractException
{
public static function new(): self
{
return new self('lastInsertId failed: Query was executed but no result was returned.');
}
}

View file

@ -0,0 +1,24 @@
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\OCI8\Exception;
use Doctrine\DBAL\Driver\AbstractException;
use function sprintf;
/**
* @internal
*
* @psalm-immutable
*/
final class UnknownParameterIndex extends AbstractException
{
public static function new(int $index): self
{
return new self(
sprintf('Could not find variable mapping with index %d, in the SQL statement', $index),
);
}
}

View file

@ -0,0 +1,30 @@
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\OCI8;
/**
* Encapsulates the execution mode that is shared between the connection and its statements.
*
* @internal This class is not covered by the backward compatibility promise
*/
final class ExecutionMode
{
private bool $isAutoCommitEnabled = true;
public function enableAutoCommit(): void
{
$this->isAutoCommitEnabled = true;
}
public function disableAutoCommit(): void
{
$this->isAutoCommitEnabled = false;
}
public function isAutoCommitEnabled(): bool
{
return $this->isAutoCommitEnabled;
}
}

View file

@ -0,0 +1,38 @@
<?php
namespace Doctrine\DBAL\Driver\OCI8\Middleware;
use Doctrine\DBAL\Driver;
use Doctrine\DBAL\Driver\Connection;
use Doctrine\DBAL\Driver\Middleware;
use Doctrine\DBAL\Driver\Middleware\AbstractDriverMiddleware;
use SensitiveParameter;
class InitializeSession implements Middleware
{
public function wrap(Driver $driver): Driver
{
return new class ($driver) extends AbstractDriverMiddleware {
/**
* {@inheritDoc}
*/
public function connect(
#[SensitiveParameter]
array $params
): Connection {
$connection = parent::connect($params);
$connection->exec(
'ALTER SESSION SET'
. " NLS_DATE_FORMAT = 'YYYY-MM-DD HH24:MI:SS'"
. " NLS_TIME_FORMAT = 'HH24:MI:SS'"
. " NLS_TIMESTAMP_FORMAT = 'YYYY-MM-DD HH24:MI:SS'"
. " NLS_TIMESTAMP_TZ_FORMAT = 'YYYY-MM-DD HH24:MI:SS TZH:TZM'"
. " NLS_NUMERIC_CHARACTERS = '.,'",
);
return $connection;
}
};
}
}

View file

@ -0,0 +1,145 @@
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\OCI8;
use Doctrine\DBAL\Driver\Exception;
use Doctrine\DBAL\Driver\FetchUtils;
use Doctrine\DBAL\Driver\OCI8\Exception\Error;
use Doctrine\DBAL\Driver\Result as ResultInterface;
use function oci_cancel;
use function oci_error;
use function oci_fetch_all;
use function oci_fetch_array;
use function oci_num_fields;
use function oci_num_rows;
use const OCI_ASSOC;
use const OCI_FETCHSTATEMENT_BY_COLUMN;
use const OCI_FETCHSTATEMENT_BY_ROW;
use const OCI_NUM;
use const OCI_RETURN_LOBS;
use const OCI_RETURN_NULLS;
final class Result implements ResultInterface
{
/** @var resource */
private $statement;
/**
* @internal The result can be only instantiated by its driver connection or statement.
*
* @param resource $statement
*/
public function __construct($statement)
{
$this->statement = $statement;
}
/**
* {@inheritDoc}
*/
public function fetchNumeric()
{
return $this->fetch(OCI_NUM);
}
/**
* {@inheritDoc}
*/
public function fetchAssociative()
{
return $this->fetch(OCI_ASSOC);
}
/**
* {@inheritDoc}
*/
public function fetchOne()
{
return FetchUtils::fetchOne($this);
}
/**
* {@inheritDoc}
*/
public function fetchAllNumeric(): array
{
return $this->fetchAll(OCI_NUM, OCI_FETCHSTATEMENT_BY_ROW);
}
/**
* {@inheritDoc}
*/
public function fetchAllAssociative(): array
{
return $this->fetchAll(OCI_ASSOC, OCI_FETCHSTATEMENT_BY_ROW);
}
/**
* {@inheritDoc}
*/
public function fetchFirstColumn(): array
{
return $this->fetchAll(OCI_NUM, OCI_FETCHSTATEMENT_BY_COLUMN)[0];
}
public function rowCount(): int
{
$count = oci_num_rows($this->statement);
if ($count !== false) {
return $count;
}
return 0;
}
public function columnCount(): int
{
$count = oci_num_fields($this->statement);
if ($count !== false) {
return $count;
}
return 0;
}
public function free(): void
{
oci_cancel($this->statement);
}
/**
* @return mixed|false
*
* @throws Exception
*/
private function fetch(int $mode)
{
$result = oci_fetch_array($this->statement, $mode | OCI_RETURN_NULLS | OCI_RETURN_LOBS);
if ($result === false && oci_error($this->statement) !== false) {
throw Error::new($this->statement);
}
return $result;
}
/** @return array<mixed> */
private function fetchAll(int $mode, int $fetchStructure): array
{
oci_fetch_all(
$this->statement,
$result,
0,
-1,
$mode | OCI_RETURN_NULLS | $fetchStructure | OCI_RETURN_LOBS,
);
return $result;
}
}

View file

@ -0,0 +1,174 @@
<?php
namespace Doctrine\DBAL\Driver\OCI8;
use Doctrine\DBAL\Driver\OCI8\Exception\Error;
use Doctrine\DBAL\Driver\OCI8\Exception\UnknownParameterIndex;
use Doctrine\DBAL\Driver\Result as ResultInterface;
use Doctrine\DBAL\Driver\Statement as StatementInterface;
use Doctrine\DBAL\ParameterType;
use Doctrine\Deprecations\Deprecation;
use function func_num_args;
use function is_int;
use function oci_bind_by_name;
use function oci_execute;
use function oci_new_descriptor;
use const OCI_B_BIN;
use const OCI_B_BLOB;
use const OCI_COMMIT_ON_SUCCESS;
use const OCI_D_LOB;
use const OCI_NO_AUTO_COMMIT;
use const OCI_TEMP_BLOB;
use const SQLT_CHR;
final class Statement implements StatementInterface
{
/** @var resource */
private $connection;
/** @var resource */
private $statement;
/** @var array<int,string> */
private array $parameterMap;
private ExecutionMode $executionMode;
/**
* @internal The statement can be only instantiated by its driver connection.
*
* @param resource $connection
* @param resource $statement
* @param array<int,string> $parameterMap
*/
public function __construct($connection, $statement, array $parameterMap, ExecutionMode $executionMode)
{
$this->connection = $connection;
$this->statement = $statement;
$this->parameterMap = $parameterMap;
$this->executionMode = $executionMode;
}
/**
* {@inheritDoc}
*/
public function bindValue($param, $value, $type = ParameterType::STRING): bool
{
if (func_num_args() < 3) {
Deprecation::trigger(
'doctrine/dbal',
'https://github.com/doctrine/dbal/pull/5558',
'Not passing $type to Statement::bindValue() is deprecated.'
. ' Pass the type corresponding to the parameter being bound.',
);
}
return $this->bindParam($param, $value, $type);
}
/**
* {@inheritDoc}
*
* @deprecated Use {@see bindValue()} instead.
*/
public function bindParam($param, &$variable, $type = ParameterType::STRING, $length = null): bool
{
Deprecation::trigger(
'doctrine/dbal',
'https://github.com/doctrine/dbal/pull/5563',
'%s is deprecated. Use bindValue() instead.',
__METHOD__,
);
if (func_num_args() < 3) {
Deprecation::trigger(
'doctrine/dbal',
'https://github.com/doctrine/dbal/pull/5558',
'Not passing $type to Statement::bindParam() is deprecated.'
. ' Pass the type corresponding to the parameter being bound.',
);
}
if (is_int($param)) {
if (! isset($this->parameterMap[$param])) {
throw UnknownParameterIndex::new($param);
}
$param = $this->parameterMap[$param];
}
if ($type === ParameterType::LARGE_OBJECT) {
if ($variable !== null) {
$lob = oci_new_descriptor($this->connection, OCI_D_LOB);
$lob->writeTemporary($variable, OCI_TEMP_BLOB);
$variable =& $lob;
} else {
$type = ParameterType::STRING;
}
}
return oci_bind_by_name(
$this->statement,
$param,
$variable,
$length ?? -1,
$this->convertParameterType($type),
);
}
/**
* Converts DBAL parameter type to oci8 parameter type
*/
private function convertParameterType(int $type): int
{
switch ($type) {
case ParameterType::BINARY:
return OCI_B_BIN;
case ParameterType::LARGE_OBJECT:
return OCI_B_BLOB;
default:
return SQLT_CHR;
}
}
/**
* {@inheritDoc}
*/
public function execute($params = null): ResultInterface
{
if ($params !== null) {
Deprecation::trigger(
'doctrine/dbal',
'https://github.com/doctrine/dbal/pull/5556',
'Passing $params to Statement::execute() is deprecated. Bind parameters using'
. ' Statement::bindParam() or Statement::bindValue() instead.',
);
foreach ($params as $key => $val) {
if (is_int($key)) {
$this->bindValue($key + 1, $val, ParameterType::STRING);
} else {
$this->bindValue($key, $val, ParameterType::STRING);
}
}
}
if ($this->executionMode->isAutoCommitEnabled()) {
$mode = OCI_COMMIT_ON_SUCCESS;
} else {
$mode = OCI_NO_AUTO_COMMIT;
}
$ret = @oci_execute($this->statement, $mode);
if (! $ret) {
throw Error::new($this->statement);
}
return new Result($this->statement);
}
}

View file

@ -0,0 +1,158 @@
<?php
namespace Doctrine\DBAL\Driver\PDO;
use Doctrine\DBAL\Driver\Exception\UnknownParameterType;
use Doctrine\DBAL\Driver\PDO\PDOException as DriverPDOException;
use Doctrine\DBAL\Driver\Result as ResultInterface;
use Doctrine\DBAL\Driver\ServerInfoAwareConnection;
use Doctrine\DBAL\Driver\Statement as StatementInterface;
use Doctrine\DBAL\ParameterType;
use Doctrine\Deprecations\Deprecation;
use PDO;
use PDOException;
use PDOStatement;
use function assert;
final class Connection implements ServerInfoAwareConnection
{
private PDO $connection;
/** @internal The connection can be only instantiated by its driver. */
public function __construct(PDO $connection)
{
$connection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$this->connection = $connection;
}
public function exec(string $sql): int
{
try {
$result = $this->connection->exec($sql);
assert($result !== false);
return $result;
} catch (PDOException $exception) {
throw Exception::new($exception);
}
}
/**
* {@inheritDoc}
*/
public function getServerVersion()
{
return $this->connection->getAttribute(PDO::ATTR_SERVER_VERSION);
}
/**
* {@inheritDoc}
*
* @return Statement
*/
public function prepare(string $sql): StatementInterface
{
try {
$stmt = $this->connection->prepare($sql);
assert($stmt instanceof PDOStatement);
return new Statement($stmt);
} catch (PDOException $exception) {
throw Exception::new($exception);
}
}
public function query(string $sql): ResultInterface
{
try {
$stmt = $this->connection->query($sql);
assert($stmt instanceof PDOStatement);
return new Result($stmt);
} catch (PDOException $exception) {
throw Exception::new($exception);
}
}
/**
* {@inheritDoc}
*
* @throws UnknownParameterType
*
* @psalm-assert ParameterType::* $type
*/
public function quote($value, $type = ParameterType::STRING)
{
return $this->connection->quote($value, ParameterTypeMap::convertParamType($type));
}
/**
* {@inheritDoc}
*/
public function lastInsertId($name = null)
{
try {
if ($name === null) {
return $this->connection->lastInsertId();
}
Deprecation::triggerIfCalledFromOutside(
'doctrine/dbal',
'https://github.com/doctrine/dbal/issues/4687',
'The usage of Connection::lastInsertId() with a sequence name is deprecated.',
);
return $this->connection->lastInsertId($name);
} catch (PDOException $exception) {
throw Exception::new($exception);
}
}
public function beginTransaction(): bool
{
try {
return $this->connection->beginTransaction();
} catch (PDOException $exception) {
throw DriverPDOException::new($exception);
}
}
public function commit(): bool
{
try {
return $this->connection->commit();
} catch (PDOException $exception) {
throw DriverPDOException::new($exception);
}
}
public function rollBack(): bool
{
try {
return $this->connection->rollBack();
} catch (PDOException $exception) {
throw DriverPDOException::new($exception);
}
}
public function getNativeConnection(): PDO
{
return $this->connection;
}
/** @deprecated Call {@see getNativeConnection()} instead. */
public function getWrappedConnection(): PDO
{
Deprecation::triggerIfCalledFromOutside(
'doctrine/dbal',
'https://github.com/doctrine/dbal/pull/5037',
'%s is deprecated, call getNativeConnection() instead.',
__METHOD__,
);
return $this->getNativeConnection();
}
}

View file

@ -0,0 +1,30 @@
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\PDO;
use Doctrine\DBAL\Driver\AbstractException;
use PDOException;
/**
* @internal
*
* @psalm-immutable
*/
final class Exception extends AbstractException
{
public static function new(PDOException $exception): self
{
if ($exception->errorInfo !== null) {
[$sqlState, $code] = $exception->errorInfo;
$code ??= 0;
} else {
$code = $exception->getCode();
$sqlState = null;
}
return new self($exception->getMessage(), $sqlState, $code, $exception);
}
}

View file

@ -0,0 +1,76 @@
<?php
namespace Doctrine\DBAL\Driver\PDO\MySQL;
use Doctrine\DBAL\Driver\AbstractMySQLDriver;
use Doctrine\DBAL\Driver\PDO\Connection;
use Doctrine\DBAL\Driver\PDO\Exception;
use PDO;
use PDOException;
use SensitiveParameter;
final class Driver extends AbstractMySQLDriver
{
/**
* {@inheritDoc}
*
* @return Connection
*/
public function connect(
#[SensitiveParameter]
array $params
) {
$driverOptions = $params['driverOptions'] ?? [];
if (! empty($params['persistent'])) {
$driverOptions[PDO::ATTR_PERSISTENT] = true;
}
$safeParams = $params;
unset($safeParams['password'], $safeParams['url']);
try {
$pdo = new PDO(
$this->constructPdoDsn($safeParams),
$params['user'] ?? '',
$params['password'] ?? '',
$driverOptions,
);
} catch (PDOException $exception) {
throw Exception::new($exception);
}
return new Connection($pdo);
}
/**
* Constructs the MySQL PDO DSN.
*
* @param mixed[] $params
*/
private function constructPdoDsn(array $params): string
{
$dsn = 'mysql:';
if (isset($params['host']) && $params['host'] !== '') {
$dsn .= 'host=' . $params['host'] . ';';
}
if (isset($params['port'])) {
$dsn .= 'port=' . $params['port'] . ';';
}
if (isset($params['dbname'])) {
$dsn .= 'dbname=' . $params['dbname'] . ';';
}
if (isset($params['unix_socket'])) {
$dsn .= 'unix_socket=' . $params['unix_socket'] . ';';
}
if (isset($params['charset'])) {
$dsn .= 'charset=' . $params['charset'] . ';';
}
return $dsn;
}
}

View file

@ -0,0 +1,61 @@
<?php
namespace Doctrine\DBAL\Driver\PDO\OCI;
use Doctrine\DBAL\Driver\AbstractOracleDriver;
use Doctrine\DBAL\Driver\PDO\Connection;
use Doctrine\DBAL\Driver\PDO\Exception;
use PDO;
use PDOException;
use SensitiveParameter;
final class Driver extends AbstractOracleDriver
{
/**
* {@inheritDoc}
*
* @return Connection
*/
public function connect(
#[SensitiveParameter]
array $params
) {
$driverOptions = $params['driverOptions'] ?? [];
if (! empty($params['persistent'])) {
$driverOptions[PDO::ATTR_PERSISTENT] = true;
}
$safeParams = $params;
unset($safeParams['password'], $safeParams['url']);
try {
$pdo = new PDO(
$this->constructPdoDsn($params),
$params['user'] ?? '',
$params['password'] ?? '',
$driverOptions,
);
} catch (PDOException $exception) {
throw Exception::new($exception);
}
return new Connection($pdo);
}
/**
* Constructs the Oracle PDO DSN.
*
* @param mixed[] $params
*/
private function constructPdoDsn(array $params): string
{
$dsn = 'oci:dbname=' . $this->getEasyConnectString($params);
if (isset($params['charset'])) {
$dsn .= ';charset=' . $params['charset'];
}
return $dsn;
}
}

View file

@ -0,0 +1,33 @@
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\PDO;
use Doctrine\DBAL\Driver\Exception as DriverException;
/**
* @internal
*
* @psalm-immutable
*/
final class PDOException extends \PDOException implements DriverException
{
private ?string $sqlState = null;
public static function new(\PDOException $previous): self
{
$exception = new self($previous->message, 0, $previous);
$exception->errorInfo = $previous->errorInfo;
$exception->code = $previous->code;
$exception->sqlState = $previous->errorInfo[0] ?? null;
return $exception;
}
public function getSQLState(): ?string
{
return $this->sqlState;
}
}

View file

@ -0,0 +1,49 @@
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\PDO;
use Doctrine\DBAL\Driver\Exception\UnknownParameterType;
use Doctrine\DBAL\ParameterType;
use PDO;
/** @internal */
final class ParameterTypeMap
{
private const PARAM_TYPE_MAP = [
ParameterType::NULL => PDO::PARAM_NULL,
ParameterType::INTEGER => PDO::PARAM_INT,
ParameterType::STRING => PDO::PARAM_STR,
ParameterType::ASCII => PDO::PARAM_STR,
ParameterType::BINARY => PDO::PARAM_LOB,
ParameterType::LARGE_OBJECT => PDO::PARAM_LOB,
ParameterType::BOOLEAN => PDO::PARAM_BOOL,
];
/**
* Converts DBAL parameter type to PDO parameter type
*
* @psalm-return PDO::PARAM_*
*
* @throws UnknownParameterType
*
* @psalm-assert ParameterType::* $type
*/
public static function convertParamType(int $type): int
{
if (! isset(self::PARAM_TYPE_MAP[$type])) {
throw UnknownParameterType::new($type);
}
return self::PARAM_TYPE_MAP[$type];
}
private function __construct()
{
}
private function __clone()
{
}
}

View file

@ -0,0 +1,135 @@
<?php
namespace Doctrine\DBAL\Driver\PDO\PgSQL;
use Doctrine\DBAL\Driver\AbstractPostgreSQLDriver;
use Doctrine\DBAL\Driver\PDO\Connection;
use Doctrine\DBAL\Driver\PDO\Exception;
use Doctrine\Deprecations\Deprecation;
use PDO;
use PDOException;
use SensitiveParameter;
final class Driver extends AbstractPostgreSQLDriver
{
/**
* {@inheritDoc}
*
* @return Connection
*/
public function connect(
#[SensitiveParameter]
array $params
) {
$driverOptions = $params['driverOptions'] ?? [];
if (! empty($params['persistent'])) {
$driverOptions[PDO::ATTR_PERSISTENT] = true;
}
$safeParams = $params;
unset($safeParams['password'], $safeParams['url']);
try {
$pdo = new PDO(
$this->constructPdoDsn($safeParams),
$params['user'] ?? '',
$params['password'] ?? '',
$driverOptions,
);
} catch (PDOException $exception) {
throw Exception::new($exception);
}
if (
! isset($driverOptions[PDO::PGSQL_ATTR_DISABLE_PREPARES])
|| $driverOptions[PDO::PGSQL_ATTR_DISABLE_PREPARES] === true
) {
$pdo->setAttribute(PDO::PGSQL_ATTR_DISABLE_PREPARES, true);
}
$connection = new Connection($pdo);
/* defining client_encoding via SET NAMES to avoid inconsistent DSN support
* - passing client_encoding via the 'options' param breaks pgbouncer support
*/
if (isset($params['charset'])) {
$connection->exec('SET NAMES \'' . $params['charset'] . '\'');
}
return $connection;
}
/**
* Constructs the Postgres PDO DSN.
*
* @param array<string, mixed> $params
*/
private function constructPdoDsn(array $params): string
{
$dsn = 'pgsql:';
if (isset($params['host']) && $params['host'] !== '') {
$dsn .= 'host=' . $params['host'] . ';';
}
if (isset($params['port']) && $params['port'] !== '') {
$dsn .= 'port=' . $params['port'] . ';';
}
if (isset($params['dbname'])) {
$dsn .= 'dbname=' . $params['dbname'] . ';';
} elseif (isset($params['default_dbname'])) {
Deprecation::trigger(
'doctrine/dbal',
'https://github.com/doctrine/dbal/pull/5705',
'The "default_dbname" connection parameter is deprecated. Use "dbname" instead.',
);
$dsn .= 'dbname=' . $params['default_dbname'] . ';';
} else {
if (isset($params['user']) && $params['user'] !== 'postgres') {
Deprecation::trigger(
'doctrine/dbal',
'https://github.com/doctrine/dbal/pull/5705',
'Relying on the DBAL connecting to the "postgres" database by default is deprecated.'
. ' Unless you want to have the server determine the default database for the connection,'
. ' specify the database name explicitly.',
);
}
// Used for temporary connections to allow operations like dropping the database currently connected to.
$dsn .= 'dbname=postgres;';
}
if (isset($params['sslmode'])) {
$dsn .= 'sslmode=' . $params['sslmode'] . ';';
}
if (isset($params['sslrootcert'])) {
$dsn .= 'sslrootcert=' . $params['sslrootcert'] . ';';
}
if (isset($params['sslcert'])) {
$dsn .= 'sslcert=' . $params['sslcert'] . ';';
}
if (isset($params['sslkey'])) {
$dsn .= 'sslkey=' . $params['sslkey'] . ';';
}
if (isset($params['sslcrl'])) {
$dsn .= 'sslcrl=' . $params['sslcrl'] . ';';
}
if (isset($params['application_name'])) {
$dsn .= 'application_name=' . $params['application_name'] . ';';
}
if (isset($params['gssencmode'])) {
$dsn .= 'gssencmode=' . $params['gssencmode'] . ';';
}
return $dsn;
}
}

View file

@ -0,0 +1,124 @@
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\PDO;
use Doctrine\DBAL\Driver\Result as ResultInterface;
use PDO;
use PDOException;
use PDOStatement;
final class Result implements ResultInterface
{
private PDOStatement $statement;
/** @internal The result can be only instantiated by its driver connection or statement. */
public function __construct(PDOStatement $statement)
{
$this->statement = $statement;
}
/**
* {@inheritDoc}
*/
public function fetchNumeric()
{
return $this->fetch(PDO::FETCH_NUM);
}
/**
* {@inheritDoc}
*/
public function fetchAssociative()
{
return $this->fetch(PDO::FETCH_ASSOC);
}
/**
* {@inheritDoc}
*/
public function fetchOne()
{
return $this->fetch(PDO::FETCH_COLUMN);
}
/**
* {@inheritDoc}
*/
public function fetchAllNumeric(): array
{
return $this->fetchAll(PDO::FETCH_NUM);
}
/**
* {@inheritDoc}
*/
public function fetchAllAssociative(): array
{
return $this->fetchAll(PDO::FETCH_ASSOC);
}
/**
* {@inheritDoc}
*/
public function fetchFirstColumn(): array
{
return $this->fetchAll(PDO::FETCH_COLUMN);
}
public function rowCount(): int
{
try {
return $this->statement->rowCount();
} catch (PDOException $exception) {
throw Exception::new($exception);
}
}
public function columnCount(): int
{
try {
return $this->statement->columnCount();
} catch (PDOException $exception) {
throw Exception::new($exception);
}
}
public function free(): void
{
$this->statement->closeCursor();
}
/**
* @psalm-param PDO::FETCH_* $mode
*
* @return mixed
*
* @throws Exception
*/
private function fetch(int $mode)
{
try {
return $this->statement->fetch($mode);
} catch (PDOException $exception) {
throw Exception::new($exception);
}
}
/**
* @psalm-param PDO::FETCH_* $mode
*
* @return list<mixed>
*
* @throws Exception
*/
private function fetchAll(int $mode): array
{
try {
return $this->statement->fetchAll($mode);
} catch (PDOException $exception) {
throw Exception::new($exception);
}
}
}

View file

@ -0,0 +1,70 @@
<?php
namespace Doctrine\DBAL\Driver\PDO\SQLSrv;
use Doctrine\DBAL\Driver\Middleware\AbstractConnectionMiddleware;
use Doctrine\DBAL\Driver\PDO\Connection as PDOConnection;
use Doctrine\DBAL\Driver\Statement as StatementInterface;
use Doctrine\Deprecations\Deprecation;
use PDO;
final class Connection extends AbstractConnectionMiddleware
{
private PDOConnection $connection;
public function __construct(PDOConnection $connection)
{
parent::__construct($connection);
$this->connection = $connection;
}
public function prepare(string $sql): StatementInterface
{
return new Statement(
$this->connection->prepare($sql),
);
}
/**
* {@inheritDoc}
*/
public function lastInsertId($name = null)
{
if ($name === null) {
return parent::lastInsertId($name);
}
Deprecation::triggerIfCalledFromOutside(
'doctrine/dbal',
'https://github.com/doctrine/dbal/issues/4687',
'The usage of Connection::lastInsertId() with a sequence name is deprecated.',
);
$statement = $this->prepare(
'SELECT CONVERT(VARCHAR(MAX), current_value) FROM sys.sequences WHERE name = ?',
);
$statement->bindValue(1, $name);
return $statement->execute()
->fetchOne();
}
public function getNativeConnection(): PDO
{
return $this->connection->getNativeConnection();
}
/** @deprecated Call {@see getNativeConnection()} instead. */
public function getWrappedConnection(): PDO
{
Deprecation::trigger(
'doctrine/dbal',
'https://github.com/doctrine/dbal/pull/5037',
'%s is deprecated, call getNativeConnection() instead.',
__METHOD__,
);
return $this->connection->getWrappedConnection();
}
}

View file

@ -0,0 +1,108 @@
<?php
namespace Doctrine\DBAL\Driver\PDO\SQLSrv;
use Doctrine\DBAL\Driver\AbstractSQLServerDriver;
use Doctrine\DBAL\Driver\AbstractSQLServerDriver\Exception\PortWithoutHost;
use Doctrine\DBAL\Driver\Exception;
use Doctrine\DBAL\Driver\PDO\Connection as PDOConnection;
use Doctrine\DBAL\Driver\PDO\Exception as PDOException;
use PDO;
use SensitiveParameter;
use function is_int;
use function sprintf;
final class Driver extends AbstractSQLServerDriver
{
/**
* {@inheritDoc}
*
* @return Connection
*/
public function connect(
#[SensitiveParameter]
array $params
) {
$driverOptions = $dsnOptions = [];
if (isset($params['driverOptions'])) {
foreach ($params['driverOptions'] as $option => $value) {
if (is_int($option)) {
$driverOptions[$option] = $value;
} else {
$dsnOptions[$option] = $value;
}
}
}
if (! empty($params['persistent'])) {
$driverOptions[PDO::ATTR_PERSISTENT] = true;
}
$safeParams = $params;
unset($safeParams['password'], $safeParams['url']);
try {
$pdo = new PDO(
$this->constructDsn($safeParams, $dsnOptions),
$params['user'] ?? '',
$params['password'] ?? '',
$driverOptions,
);
} catch (\PDOException $exception) {
throw PDOException::new($exception);
}
return new Connection(new PDOConnection($pdo));
}
/**
* Constructs the Sqlsrv PDO DSN.
*
* @param mixed[] $params
* @param string[] $connectionOptions
*
* @throws Exception
*/
private function constructDsn(array $params, array $connectionOptions): string
{
$dsn = 'sqlsrv:server=';
if (isset($params['host'])) {
$dsn .= $params['host'];
if (isset($params['port'])) {
$dsn .= ',' . $params['port'];
}
} elseif (isset($params['port'])) {
throw PortWithoutHost::new();
}
if (isset($params['dbname'])) {
$connectionOptions['Database'] = $params['dbname'];
}
if (isset($params['MultipleActiveResultSets'])) {
$connectionOptions['MultipleActiveResultSets'] = $params['MultipleActiveResultSets'] ? 'true' : 'false';
}
return $dsn . $this->getConnectionOptionsDsn($connectionOptions);
}
/**
* Converts a connection options array to the DSN
*
* @param string[] $connectionOptions
*/
private function getConnectionOptionsDsn(array $connectionOptions): string
{
$connectionOptionsDsn = '';
foreach ($connectionOptions as $paramName => $paramValue) {
$connectionOptionsDsn .= sprintf(';%s=%s', $paramName, $paramValue);
}
return $connectionOptionsDsn;
}
}

View file

@ -0,0 +1,109 @@
<?php
namespace Doctrine\DBAL\Driver\PDO\SQLSrv;
use Doctrine\DBAL\Driver\Exception\UnknownParameterType;
use Doctrine\DBAL\Driver\Middleware\AbstractStatementMiddleware;
use Doctrine\DBAL\Driver\PDO\Statement as PDOStatement;
use Doctrine\DBAL\ParameterType;
use Doctrine\Deprecations\Deprecation;
use PDO;
use function func_num_args;
final class Statement extends AbstractStatementMiddleware
{
private PDOStatement $statement;
/** @internal The statement can be only instantiated by its driver connection. */
public function __construct(PDOStatement $statement)
{
parent::__construct($statement);
$this->statement = $statement;
}
/**
* {@inheritDoc}
*
* @deprecated Use {@see bindValue()} instead.
*
* @param string|int $param
* @param mixed $variable
* @param int $type
* @param int|null $length
* @param mixed $driverOptions The usage of the argument is deprecated.
*
* @throws UnknownParameterType
*
* @psalm-assert ParameterType::* $type
*/
public function bindParam(
$param,
&$variable,
$type = ParameterType::STRING,
$length = null,
$driverOptions = null
): bool {
Deprecation::trigger(
'doctrine/dbal',
'https://github.com/doctrine/dbal/pull/5563',
'%s is deprecated. Use bindValue() instead.',
__METHOD__,
);
if (func_num_args() < 3) {
Deprecation::trigger(
'doctrine/dbal',
'https://github.com/doctrine/dbal/pull/5558',
'Not passing $type to Statement::bindParam() is deprecated.'
. ' Pass the type corresponding to the parameter being bound.',
);
}
if (func_num_args() > 4) {
Deprecation::triggerIfCalledFromOutside(
'doctrine/dbal',
'https://github.com/doctrine/dbal/issues/4533',
'The $driverOptions argument of Statement::bindParam() is deprecated.',
);
}
switch ($type) {
case ParameterType::LARGE_OBJECT:
case ParameterType::BINARY:
$driverOptions ??= PDO::SQLSRV_ENCODING_BINARY;
break;
case ParameterType::ASCII:
$type = ParameterType::STRING;
$length = 0;
$driverOptions = PDO::SQLSRV_ENCODING_SYSTEM;
break;
}
return $this->statement->bindParam($param, $variable, $type, $length ?? 0, $driverOptions);
}
/**
* @throws UnknownParameterType
*
* {@inheritDoc}
*
* @psalm-assert ParameterType::* $type
*/
public function bindValue($param, $value, $type = ParameterType::STRING): bool
{
if (func_num_args() < 3) {
Deprecation::trigger(
'doctrine/dbal',
'https://github.com/doctrine/dbal/pull/5558',
'Not passing $type to Statement::bindValue() is deprecated.'
. ' Pass the type corresponding to the parameter being bound.',
);
}
return $this->bindParam($param, $value, $type);
}
}

View file

@ -0,0 +1,77 @@
<?php
namespace Doctrine\DBAL\Driver\PDO\SQLite;
use Doctrine\DBAL\Driver\AbstractSQLiteDriver;
use Doctrine\DBAL\Driver\API\SQLite\UserDefinedFunctions;
use Doctrine\DBAL\Driver\PDO\Connection;
use Doctrine\DBAL\Driver\PDO\Exception;
use Doctrine\Deprecations\Deprecation;
use PDO;
use PDOException;
use SensitiveParameter;
use function array_intersect_key;
final class Driver extends AbstractSQLiteDriver
{
/**
* {@inheritDoc}
*
* @return Connection
*/
public function connect(
#[SensitiveParameter]
array $params
) {
$driverOptions = $params['driverOptions'] ?? [];
$userDefinedFunctions = [];
if (isset($driverOptions['userDefinedFunctions'])) {
Deprecation::trigger(
'doctrine/dbal',
'https://github.com/doctrine/dbal/pull/5742',
'The SQLite-specific driver option "userDefinedFunctions" is deprecated.'
. ' Register function directly on the native connection instead.',
);
$userDefinedFunctions = $driverOptions['userDefinedFunctions'];
unset($driverOptions['userDefinedFunctions']);
}
try {
$pdo = new PDO(
$this->constructPdoDsn(array_intersect_key($params, ['path' => true, 'memory' => true])),
$params['user'] ?? '',
$params['password'] ?? '',
$driverOptions,
);
} catch (PDOException $exception) {
throw Exception::new($exception);
}
UserDefinedFunctions::register(
[$pdo, 'sqliteCreateFunction'],
$userDefinedFunctions,
);
return new Connection($pdo);
}
/**
* Constructs the Sqlite PDO DSN.
*
* @param array<string, mixed> $params
*/
private function constructPdoDsn(array $params): string
{
$dsn = 'sqlite:';
if (isset($params['path'])) {
$dsn .= $params['path'];
} elseif (isset($params['memory'])) {
$dsn .= ':memory:';
}
return $dsn;
}
}

View file

@ -0,0 +1,137 @@
<?php
namespace Doctrine\DBAL\Driver\PDO;
use Doctrine\DBAL\Driver\Exception\UnknownParameterType;
use Doctrine\DBAL\Driver\Result as ResultInterface;
use Doctrine\DBAL\Driver\Statement as StatementInterface;
use Doctrine\DBAL\ParameterType;
use Doctrine\Deprecations\Deprecation;
use PDOException;
use PDOStatement;
use function array_slice;
use function func_get_args;
use function func_num_args;
final class Statement implements StatementInterface
{
private PDOStatement $stmt;
/** @internal The statement can be only instantiated by its driver connection. */
public function __construct(PDOStatement $stmt)
{
$this->stmt = $stmt;
}
/**
* {@inheritDoc}
*
* @throws UnknownParameterType
*
* @psalm-assert ParameterType::* $type
*/
public function bindValue($param, $value, $type = ParameterType::STRING)
{
if (func_num_args() < 3) {
Deprecation::trigger(
'doctrine/dbal',
'https://github.com/doctrine/dbal/pull/5558',
'Not passing $type to Statement::bindValue() is deprecated.'
. ' Pass the type corresponding to the parameter being bound.',
);
}
$pdoType = ParameterTypeMap::convertParamType($type);
try {
return $this->stmt->bindValue($param, $value, $pdoType);
} catch (PDOException $exception) {
throw Exception::new($exception);
}
}
/**
* {@inheritDoc}
*
* @deprecated Use {@see bindValue()} instead.
*
* @param mixed $param
* @param mixed $variable
* @param int $type
* @param int|null $length
* @param mixed $driverOptions The usage of the argument is deprecated.
*
* @throws UnknownParameterType
*
* @psalm-assert ParameterType::* $type
*/
public function bindParam(
$param,
&$variable,
$type = ParameterType::STRING,
$length = null,
$driverOptions = null
): bool {
Deprecation::trigger(
'doctrine/dbal',
'https://github.com/doctrine/dbal/pull/5563',
'%s is deprecated. Use bindValue() instead.',
__METHOD__,
);
if (func_num_args() < 3) {
Deprecation::trigger(
'doctrine/dbal',
'https://github.com/doctrine/dbal/pull/5558',
'Not passing $type to Statement::bindParam() is deprecated.'
. ' Pass the type corresponding to the parameter being bound.',
);
}
if (func_num_args() > 4) {
Deprecation::triggerIfCalledFromOutside(
'doctrine/dbal',
'https://github.com/doctrine/dbal/issues/4533',
'The $driverOptions argument of Statement::bindParam() is deprecated.',
);
}
$pdoType = ParameterTypeMap::convertParamType($type);
try {
return $this->stmt->bindParam(
$param,
$variable,
$pdoType,
$length ?? 0,
...array_slice(func_get_args(), 4),
);
} catch (PDOException $exception) {
throw Exception::new($exception);
}
}
/**
* {@inheritDoc}
*/
public function execute($params = null): ResultInterface
{
if ($params !== null) {
Deprecation::trigger(
'doctrine/dbal',
'https://github.com/doctrine/dbal/pull/5556',
'Passing $params to Statement::execute() is deprecated. Bind parameters using'
. ' Statement::bindParam() or Statement::bindValue() instead.',
);
}
try {
$this->stmt->execute($params);
} catch (PDOException $exception) {
throw Exception::new($exception);
}
return new Result($this->stmt);
}
}

View file

@ -0,0 +1,161 @@
<?php
namespace Doctrine\DBAL\Driver\PgSQL;
use Doctrine\DBAL\Driver\ServerInfoAwareConnection;
use Doctrine\DBAL\ParameterType;
use Doctrine\DBAL\SQL\Parser;
use Doctrine\Deprecations\Deprecation;
use PgSql\Connection as PgSqlConnection;
use TypeError;
use function assert;
use function get_class;
use function gettype;
use function is_object;
use function is_resource;
use function pg_close;
use function pg_escape_bytea;
use function pg_escape_literal;
use function pg_get_result;
use function pg_last_error;
use function pg_result_error;
use function pg_send_prepare;
use function pg_send_query;
use function pg_version;
use function sprintf;
use function uniqid;
final class Connection implements ServerInfoAwareConnection
{
/** @var PgSqlConnection|resource */
private $connection;
private Parser $parser;
/** @param PgSqlConnection|resource $connection */
public function __construct($connection)
{
if (! is_resource($connection) && ! $connection instanceof PgSqlConnection) {
throw new TypeError(sprintf(
'Expected connection to be a resource or an instance of %s, got %s.',
PgSqlConnection::class,
is_object($connection) ? get_class($connection) : gettype($connection),
));
}
$this->connection = $connection;
$this->parser = new Parser(false);
}
public function __destruct()
{
if (! isset($this->connection)) {
return;
}
@pg_close($this->connection);
}
public function prepare(string $sql): Statement
{
$visitor = new ConvertParameters();
$this->parser->parse($sql, $visitor);
$statementName = uniqid('dbal', true);
if (@pg_send_prepare($this->connection, $statementName, $visitor->getSQL()) !== true) {
throw new Exception(pg_last_error($this->connection));
}
$result = @pg_get_result($this->connection);
assert($result !== false);
if ((bool) pg_result_error($result)) {
throw Exception::fromResult($result);
}
return new Statement($this->connection, $statementName, $visitor->getParameterMap());
}
public function query(string $sql): Result
{
if (@pg_send_query($this->connection, $sql) !== true) {
throw new Exception(pg_last_error($this->connection));
}
$result = @pg_get_result($this->connection);
assert($result !== false);
if ((bool) pg_result_error($result)) {
throw Exception::fromResult($result);
}
return new Result($result);
}
/** {@inheritDoc} */
public function quote($value, $type = ParameterType::STRING)
{
if ($type === ParameterType::BINARY || $type === ParameterType::LARGE_OBJECT) {
return sprintf("'%s'", pg_escape_bytea($this->connection, $value));
}
return pg_escape_literal($this->connection, $value);
}
public function exec(string $sql): int
{
return $this->query($sql)->rowCount();
}
/** {@inheritDoc} */
public function lastInsertId($name = null)
{
if ($name !== null) {
Deprecation::triggerIfCalledFromOutside(
'doctrine/dbal',
'https://github.com/doctrine/dbal/issues/4687',
'The usage of Connection::lastInsertId() with a sequence name is deprecated.',
);
return $this->query(sprintf('SELECT CURRVAL(%s)', $this->quote($name)))->fetchOne();
}
return $this->query('SELECT LASTVAL()')->fetchOne();
}
/** @return true */
public function beginTransaction(): bool
{
$this->exec('BEGIN');
return true;
}
/** @return true */
public function commit(): bool
{
$this->exec('COMMIT');
return true;
}
/** @return true */
public function rollBack(): bool
{
$this->exec('ROLLBACK');
return true;
}
public function getServerVersion(): string
{
return (string) pg_version($this->connection)['server'];
}
/** @return PgSqlConnection|resource */
public function getNativeConnection()
{
return $this->connection;
}
}

View file

@ -0,0 +1,49 @@
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\PgSQL;
use Doctrine\DBAL\SQL\Parser\Visitor;
use function count;
use function implode;
final class ConvertParameters implements Visitor
{
/** @var list<string> */
private array $buffer = [];
/** @var array<array-key, int> */
private array $parameterMap = [];
public function acceptPositionalParameter(string $sql): void
{
$position = count($this->parameterMap) + 1;
$this->parameterMap[$position] = $position;
$this->buffer[] = '$' . $position;
}
public function acceptNamedParameter(string $sql): void
{
$position = count($this->parameterMap) + 1;
$this->parameterMap[$sql] = $position;
$this->buffer[] = '$' . $position;
}
public function acceptOther(string $sql): void
{
$this->buffer[] = $sql;
}
public function getSQL(): string
{
return implode('', $this->buffer);
}
/** @return array<array-key, int> */
public function getParameterMap(): array
{
return $this->parameterMap;
}
}

View file

@ -0,0 +1,86 @@
<?php
namespace Doctrine\DBAL\Driver\PgSQL;
use Doctrine\DBAL\Driver\AbstractPostgreSQLDriver;
use ErrorException;
use SensitiveParameter;
use function addslashes;
use function array_filter;
use function array_keys;
use function array_map;
use function array_slice;
use function array_values;
use function func_get_args;
use function implode;
use function pg_connect;
use function restore_error_handler;
use function set_error_handler;
use function sprintf;
use const PGSQL_CONNECT_FORCE_NEW;
final class Driver extends AbstractPostgreSQLDriver
{
/** {@inheritDoc} */
public function connect(
#[SensitiveParameter]
array $params
): Connection {
set_error_handler(
static function (int $severity, string $message) {
throw new ErrorException($message, 0, $severity, ...array_slice(func_get_args(), 2, 2));
},
);
try {
$connection = pg_connect($this->constructConnectionString($params), PGSQL_CONNECT_FORCE_NEW);
} catch (ErrorException $e) {
throw new Exception($e->getMessage(), '08006', 0, $e);
} finally {
restore_error_handler();
}
if ($connection === false) {
throw new Exception('Unable to connect to Postgres server.');
}
$driverConnection = new Connection($connection);
if (isset($params['application_name'])) {
$driverConnection->exec('SET application_name = ' . $driverConnection->quote($params['application_name']));
}
return $driverConnection;
}
/**
* Constructs the Postgres connection string
*
* @param array<string, mixed> $params
*/
private function constructConnectionString(
#[SensitiveParameter]
array $params
): string {
$components = array_filter(
[
'host' => $params['host'] ?? null,
'port' => $params['port'] ?? null,
'dbname' => $params['dbname'] ?? 'postgres',
'user' => $params['user'] ?? null,
'password' => $params['password'] ?? null,
'sslmode' => $params['sslmode'] ?? null,
'gssencmode' => $params['gssencmode'] ?? null,
],
static fn ($value) => $value !== '' && $value !== null,
);
return implode(' ', array_map(
static fn ($value, string $key) => sprintf("%s='%s'", $key, addslashes($value)),
array_values($components),
array_keys($components),
));
}
}

View file

@ -0,0 +1,30 @@
<?php
namespace Doctrine\DBAL\Driver\PgSQL;
use Doctrine\DBAL\Driver\AbstractException;
use PgSql\Result as PgSqlResult;
use function pg_result_error_field;
use const PGSQL_DIAG_MESSAGE_PRIMARY;
use const PGSQL_DIAG_SQLSTATE;
/**
* @internal
*
* @psalm-immutable
*/
final class Exception extends AbstractException
{
/** @param PgSqlResult|resource $result */
public static function fromResult($result): self
{
$sqlstate = pg_result_error_field($result, PGSQL_DIAG_SQLSTATE);
if ($sqlstate === false) {
$sqlstate = null;
}
return new self((string) pg_result_error_field($result, PGSQL_DIAG_MESSAGE_PRIMARY), $sqlstate);
}
}

View file

@ -0,0 +1,29 @@
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\PgSQL\Exception;
use Doctrine\DBAL\Driver\Exception;
use UnexpectedValueException;
use function sprintf;
/** @psalm-immutable */
final class UnexpectedValue extends UnexpectedValueException implements Exception
{
public static function new(string $value, string $type): self
{
return new self(sprintf(
'Unexpected value "%s" of type "%s" returned by Postgres',
$value,
$type,
));
}
/** @return null */
public function getSQLState()
{
return null;
}
}

View file

@ -0,0 +1,18 @@
<?php
namespace Doctrine\DBAL\Driver\PgSQL\Exception;
use Doctrine\DBAL\Driver\AbstractException;
use function sprintf;
/** @psalm-immutable */
final class UnknownParameter extends AbstractException
{
public static function new(string $param): self
{
return new self(
sprintf('Could not find parameter %s in the SQL statement', $param),
);
}
}

Some files were not shown because too many files have changed in this diff Show more