| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271 |
- <?php
- namespace Doctrine\DBAL;
- use Closure;
- use Doctrine\Common\EventManager;
- use Doctrine\DBAL\Cache\ArrayStatement;
- use Doctrine\DBAL\Cache\CacheException;
- use Doctrine\DBAL\Cache\QueryCacheProfile;
- use Doctrine\DBAL\Cache\ResultCacheStatement;
- use Doctrine\DBAL\Driver\Connection as DriverConnection;
- use Doctrine\DBAL\Driver\PDO\Statement as PDODriverStatement;
- use Doctrine\DBAL\Driver\PingableConnection;
- use Doctrine\DBAL\Driver\ResultStatement;
- use Doctrine\DBAL\Driver\ServerInfoAwareConnection;
- use Doctrine\DBAL\Exception\ConnectionLost;
- use Doctrine\DBAL\Exception\InvalidArgumentException;
- use Doctrine\DBAL\Exception\NoKeyValue;
- use Doctrine\DBAL\Platforms\AbstractPlatform;
- use Doctrine\DBAL\Query\Expression\ExpressionBuilder;
- use Doctrine\DBAL\Query\QueryBuilder;
- use Doctrine\DBAL\Schema\AbstractSchemaManager;
- use Doctrine\DBAL\Types\Type;
- use Doctrine\Deprecations\Deprecation;
- use PDO;
- use Throwable;
- use Traversable;
- use function array_key_exists;
- use function array_shift;
- use function assert;
- use function func_get_args;
- use function implode;
- use function is_int;
- use function is_string;
- use function key;
- /**
- * A wrapper around a Doctrine\DBAL\Driver\Connection that adds features like
- * events, transaction isolation levels, configuration, emulated transaction nesting,
- * lazy connecting and more.
- *
- * @psalm-import-type Params from DriverManager
- */
- class Connection implements DriverConnection
- {
- /**
- * Constant for transaction isolation level READ UNCOMMITTED.
- *
- * @deprecated Use TransactionIsolationLevel::READ_UNCOMMITTED.
- */
- public const TRANSACTION_READ_UNCOMMITTED = TransactionIsolationLevel::READ_UNCOMMITTED;
- /**
- * Constant for transaction isolation level READ COMMITTED.
- *
- * @deprecated Use TransactionIsolationLevel::READ_COMMITTED.
- */
- public const TRANSACTION_READ_COMMITTED = TransactionIsolationLevel::READ_COMMITTED;
- /**
- * Constant for transaction isolation level REPEATABLE READ.
- *
- * @deprecated Use TransactionIsolationLevel::REPEATABLE_READ.
- */
- public const TRANSACTION_REPEATABLE_READ = TransactionIsolationLevel::REPEATABLE_READ;
- /**
- * Constant for transaction isolation level SERIALIZABLE.
- *
- * @deprecated Use TransactionIsolationLevel::SERIALIZABLE.
- */
- public const TRANSACTION_SERIALIZABLE = TransactionIsolationLevel::SERIALIZABLE;
- /**
- * Represents an array of ints to be expanded by Doctrine SQL parsing.
- */
- public const PARAM_INT_ARRAY = ParameterType::INTEGER + self::ARRAY_PARAM_OFFSET;
- /**
- * Represents an array of strings to be expanded by Doctrine SQL parsing.
- */
- public const PARAM_STR_ARRAY = ParameterType::STRING + self::ARRAY_PARAM_OFFSET;
- /**
- * Offset by which PARAM_* constants are detected as arrays of the param type.
- */
- public const ARRAY_PARAM_OFFSET = 100;
- /**
- * The wrapped driver connection.
- *
- * @var \Doctrine\DBAL\Driver\Connection|null
- */
- protected $_conn;
- /** @var Configuration */
- protected $_config;
- /** @var EventManager */
- protected $_eventManager;
- /** @var ExpressionBuilder */
- protected $_expr;
- /**
- * The current auto-commit mode of this connection.
- *
- * @var bool
- */
- private $autoCommit = true;
- /**
- * The transaction nesting level.
- *
- * @var int
- */
- private $transactionNestingLevel = 0;
- /**
- * The currently active transaction isolation level or NULL before it has been determined.
- *
- * @var int|null
- */
- private $transactionIsolationLevel;
- /**
- * If nested transactions should use savepoints.
- *
- * @var bool
- */
- private $nestTransactionsWithSavepoints = false;
- /**
- * The parameters used during creation of the Connection instance.
- *
- * @var array<string,mixed>
- * @phpstan-var array<string,mixed>
- * @psalm-var Params
- */
- private $params;
- /**
- * The database platform object used by the connection or NULL before it's initialized.
- *
- * @var AbstractPlatform|null
- */
- private $platform;
- /**
- * The schema manager.
- *
- * @var AbstractSchemaManager|null
- */
- protected $_schemaManager;
- /**
- * The used DBAL driver.
- *
- * @var Driver
- */
- protected $_driver;
- /**
- * Flag that indicates whether the current transaction is marked for rollback only.
- *
- * @var bool
- */
- private $isRollbackOnly = false;
- /** @var int */
- protected $defaultFetchMode = FetchMode::ASSOCIATIVE;
- /**
- * Initializes a new instance of the Connection class.
- *
- * @internal The connection can be only instantiated by the driver manager.
- *
- * @param array<string,mixed> $params The connection parameters.
- * @param Driver $driver The driver to use.
- * @param Configuration|null $config The configuration, optional.
- * @param EventManager|null $eventManager The event manager, optional.
- * @psalm-param Params $params
- * @phpstan-param array<string,mixed> $params
- *
- * @throws Exception
- */
- public function __construct(
- array $params,
- Driver $driver,
- ?Configuration $config = null,
- ?EventManager $eventManager = null
- ) {
- $this->_driver = $driver;
- $this->params = $params;
- if (isset($params['pdo'])) {
- Deprecation::trigger(
- 'doctrine/dbal',
- 'https://github.com/doctrine/dbal/pull/3554',
- 'Passing a user provided PDO instance directly to Doctrine is deprecated.'
- );
- if (! $params['pdo'] instanceof PDO) {
- throw Exception::invalidPdoInstance();
- }
- $this->_conn = $params['pdo'];
- $this->_conn->setAttribute(PDO::ATTR_STATEMENT_CLASS, [PDODriverStatement::class, []]);
- unset($this->params['pdo']);
- }
- if (isset($params['platform'])) {
- if (! $params['platform'] instanceof Platforms\AbstractPlatform) {
- throw Exception::invalidPlatformType($params['platform']);
- }
- $this->platform = $params['platform'];
- }
- // Create default config and event manager if none given
- if (! $config) {
- $config = new Configuration();
- }
- if (! $eventManager) {
- $eventManager = new EventManager();
- }
- $this->_config = $config;
- $this->_eventManager = $eventManager;
- $this->_expr = new Query\Expression\ExpressionBuilder($this);
- $this->autoCommit = $config->getAutoCommit();
- }
- /**
- * Gets the parameters used during instantiation.
- *
- * @internal
- *
- * @return array<string,mixed>
- * @psalm-return Params
- * @phpstan-return array<string,mixed>
- */
- public function getParams()
- {
- return $this->params;
- }
- /**
- * Gets the name of the database this Connection is connected to.
- *
- * @return string
- */
- public function getDatabase()
- {
- return $this->_driver->getDatabase($this);
- }
- /**
- * Gets the hostname of the currently connected database.
- *
- * @deprecated
- *
- * @return string|null
- */
- public function getHost()
- {
- Deprecation::trigger(
- 'doctrine/dbal',
- 'https://github.com/doctrine/dbal/issues/3580',
- 'Connection::getHost() is deprecated, get the database server host from application config ' .
- 'or as a last resort from internal Connection::getParams() API.'
- );
- return $this->params['host'] ?? null;
- }
- /**
- * Gets the port of the currently connected database.
- *
- * @deprecated
- *
- * @return mixed
- */
- public function getPort()
- {
- Deprecation::trigger(
- 'doctrine/dbal',
- 'https://github.com/doctrine/dbal/issues/3580',
- 'Connection::getPort() is deprecated, get the database server port from application config ' .
- 'or as a last resort from internal Connection::getParams() API.'
- );
- return $this->params['port'] ?? null;
- }
- /**
- * Gets the username used by this connection.
- *
- * @deprecated
- *
- * @return string|null
- */
- public function getUsername()
- {
- Deprecation::trigger(
- 'doctrine/dbal',
- 'https://github.com/doctrine/dbal/issues/3580',
- 'Connection::getUsername() is deprecated, get the username from application config ' .
- 'or as a last resort from internal Connection::getParams() API.'
- );
- return $this->params['user'] ?? null;
- }
- /**
- * Gets the password used by this connection.
- *
- * @deprecated
- *
- * @return string|null
- */
- public function getPassword()
- {
- Deprecation::trigger(
- 'doctrine/dbal',
- 'https://github.com/doctrine/dbal/issues/3580',
- 'Connection::getPassword() is deprecated, get the password from application config ' .
- 'or as a last resort from internal Connection::getParams() API.'
- );
- return $this->params['password'] ?? null;
- }
- /**
- * Gets the DBAL driver instance.
- *
- * @return Driver
- */
- public function getDriver()
- {
- return $this->_driver;
- }
- /**
- * Gets the Configuration used by the Connection.
- *
- * @return Configuration
- */
- public function getConfiguration()
- {
- return $this->_config;
- }
- /**
- * Gets the EventManager used by the Connection.
- *
- * @return EventManager
- */
- public function getEventManager()
- {
- return $this->_eventManager;
- }
- /**
- * Gets the DatabasePlatform for the connection.
- *
- * @return AbstractPlatform
- *
- * @throws Exception
- */
- public function getDatabasePlatform()
- {
- if ($this->platform === null) {
- $this->platform = $this->detectDatabasePlatform();
- $this->platform->setEventManager($this->_eventManager);
- }
- return $this->platform;
- }
- /**
- * Gets the ExpressionBuilder for the connection.
- *
- * @return ExpressionBuilder
- */
- public function getExpressionBuilder()
- {
- return $this->_expr;
- }
- /**
- * Establishes the connection with the database.
- *
- * @return bool TRUE if the connection was successfully established, FALSE if
- * the connection is already open.
- */
- public function connect()
- {
- if ($this->_conn !== null) {
- return false;
- }
- $driverOptions = $this->params['driverOptions'] ?? [];
- $user = $this->params['user'] ?? null;
- $password = $this->params['password'] ?? null;
- $this->_conn = $this->_driver->connect($this->params, $user, $password, $driverOptions);
- $this->transactionNestingLevel = 0;
- if ($this->autoCommit === false) {
- $this->beginTransaction();
- }
- if ($this->_eventManager->hasListeners(Events::postConnect)) {
- $eventArgs = new Event\ConnectionEventArgs($this);
- $this->_eventManager->dispatchEvent(Events::postConnect, $eventArgs);
- }
- return true;
- }
- /**
- * Detects and sets the database platform.
- *
- * Evaluates custom platform class and version in order to set the correct platform.
- *
- * @throws Exception If an invalid platform was specified for this connection.
- */
- private function detectDatabasePlatform(): AbstractPlatform
- {
- $version = $this->getDatabasePlatformVersion();
- if ($version !== null) {
- assert($this->_driver instanceof VersionAwarePlatformDriver);
- return $this->_driver->createDatabasePlatformForVersion($version);
- }
- return $this->_driver->getDatabasePlatform();
- }
- /**
- * Returns the version of the related platform if applicable.
- *
- * Returns null if either the driver is not capable to create version
- * specific platform instances, no explicit server version was specified
- * or the underlying driver connection cannot determine the platform
- * version without having to query it (performance reasons).
- *
- * @return string|null
- *
- * @throws Throwable
- */
- private function getDatabasePlatformVersion()
- {
- // Driver does not support version specific platforms.
- if (! $this->_driver instanceof VersionAwarePlatformDriver) {
- return null;
- }
- // Explicit platform version requested (supersedes auto-detection).
- if (isset($this->params['serverVersion'])) {
- return $this->params['serverVersion'];
- }
- // If not connected, we need to connect now to determine the platform version.
- if ($this->_conn === null) {
- try {
- $this->connect();
- } catch (Throwable $originalException) {
- if (empty($this->params['dbname'])) {
- throw $originalException;
- }
- // The database to connect to might not yet exist.
- // Retry detection without database name connection parameter.
- $params = $this->params;
- unset($this->params['dbname']);
- try {
- $this->connect();
- } catch (Throwable $fallbackException) {
- // Either the platform does not support database-less connections
- // or something else went wrong.
- throw $originalException;
- } finally {
- $this->params = $params;
- }
- $serverVersion = $this->getServerVersion();
- // Close "temporary" connection to allow connecting to the real database again.
- $this->close();
- return $serverVersion;
- }
- }
- return $this->getServerVersion();
- }
- /**
- * Returns the database server version if the underlying driver supports it.
- *
- * @return string|null
- */
- private function getServerVersion()
- {
- $connection = $this->getWrappedConnection();
- // Automatic platform version detection.
- if ($connection instanceof ServerInfoAwareConnection && ! $connection->requiresQueryForServerVersion()) {
- return $connection->getServerVersion();
- }
- // Unable to detect platform version.
- return null;
- }
- /**
- * Returns the current auto-commit mode for this connection.
- *
- * @see setAutoCommit
- *
- * @return bool True if auto-commit mode is currently enabled for this connection, false otherwise.
- */
- public function isAutoCommit()
- {
- return $this->autoCommit === true;
- }
- /**
- * Sets auto-commit mode for this connection.
- *
- * 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.
- *
- * NOTE: If this method is called during a transaction and the auto-commit mode is changed, the transaction is
- * committed. If this method is called and the auto-commit mode is not changed, the call is a no-op.
- *
- * @see isAutoCommit
- *
- * @param bool $autoCommit True to enable auto-commit mode; false to disable it.
- *
- * @return void
- */
- public function setAutoCommit($autoCommit)
- {
- $autoCommit = (bool) $autoCommit;
- // Mode not changed, no-op.
- if ($autoCommit === $this->autoCommit) {
- return;
- }
- $this->autoCommit = $autoCommit;
- // Commit all currently active transactions if any when switching auto-commit mode.
- if ($this->_conn === null || $this->transactionNestingLevel === 0) {
- return;
- }
- $this->commitAll();
- }
- /**
- * Sets the fetch mode.
- *
- * @deprecated Use one of the fetch- or iterate-related methods.
- *
- * @param int $fetchMode
- *
- * @return void
- */
- public function setFetchMode($fetchMode)
- {
- Deprecation::trigger(
- 'doctrine/dbal',
- 'https://github.com/doctrine/dbal/pull/4019',
- 'Default Fetch Mode configuration is deprecated, use explicit Connection::fetch*() APIs instead.'
- );
- $this->defaultFetchMode = $fetchMode;
- }
- /**
- * Prepares and executes an SQL query and returns the first row of the result
- * as an associative array.
- *
- * @deprecated Use fetchAssociative()
- *
- * @param string $sql SQL query
- * @param array<int, mixed>|array<string, mixed> $params Query parameters
- * @param array<int, int|string|Type|null>|array<string, int|string|Type|null> $types Parameter types
- *
- * @return array<string, mixed>|false False is returned if no rows are found.
- *
- * @throws Exception
- */
- public function fetchAssoc($sql, array $params = [], array $types = [])
- {
- Deprecation::trigger(
- 'doctrine/dbal',
- 'https://github.com/doctrine/dbal/pull/4019',
- 'Connection::fetchAssoc() is deprecated, use Connection::fetchAssociative() API instead.'
- );
- return $this->executeQuery($sql, $params, $types)->fetch(FetchMode::ASSOCIATIVE);
- }
- /**
- * Prepares and executes an SQL query and returns the first row of the result
- * as a numerically indexed array.
- *
- * @deprecated Use fetchNumeric()
- *
- * @param string $sql SQL query
- * @param array<int, mixed>|array<string, mixed> $params Query parameters
- * @param array<int, int|string|Type|null>|array<string, int|string|Type|null> $types Parameter types
- *
- * @return array<int, mixed>|false False is returned if no rows are found.
- */
- public function fetchArray($sql, array $params = [], array $types = [])
- {
- Deprecation::trigger(
- 'doctrine/dbal',
- 'https://github.com/doctrine/dbal/pull/4019',
- 'Connection::fetchArray() is deprecated, use Connection::fetchNumeric() API instead.'
- );
- return $this->executeQuery($sql, $params, $types)->fetch(FetchMode::NUMERIC);
- }
- /**
- * Prepares and executes an SQL query and returns the value of a single column
- * of the first row of the result.
- *
- * @deprecated Use fetchOne() instead.
- *
- * @param string $sql SQL query
- * @param array<int, mixed>|array<string, mixed> $params Query parameters
- * @param int $column 0-indexed column number
- * @param array<int, int|string|Type|null>|array<string, int|string|Type|null> $types Parameter types
- *
- * @return mixed|false False is returned if no rows are found.
- *
- * @throws Exception
- */
- public function fetchColumn($sql, array $params = [], $column = 0, array $types = [])
- {
- Deprecation::trigger(
- 'doctrine/dbal',
- 'https://github.com/doctrine/dbal/pull/4019',
- 'Connection::fetchColumn() is deprecated, use Connection::fetchOne() API instead.'
- );
- return $this->executeQuery($sql, $params, $types)->fetchColumn($column);
- }
- /**
- * Prepares and executes an SQL query and returns the first row of the result
- * as an associative array.
- *
- * @param string $query SQL query
- * @param array<int, mixed>|array<string, mixed> $params Query parameters
- * @param array<int, int|string|Type|null>|array<string, int|string|Type|null> $types Parameter types
- *
- * @return array<string, mixed>|false False is returned if no rows are found.
- *
- * @throws Exception
- */
- public function fetchAssociative(string $query, array $params = [], array $types = [])
- {
- try {
- $stmt = $this->ensureForwardCompatibilityStatement(
- $this->executeQuery($query, $params, $types)
- );
- return $stmt->fetchAssociative();
- } catch (Throwable $e) {
- $this->handleExceptionDuringQuery($e, $query, $params, $types);
- }
- }
- /**
- * Prepares and executes an SQL query and returns the first row of the result
- * as a numerically indexed array.
- *
- * @param string $query SQL query
- * @param array<int, mixed>|array<string, mixed> $params Query parameters
- * @param array<int, int|string|Type|null>|array<string, int|string|Type|null> $types Parameter types
- *
- * @return array<int, mixed>|false False is returned if no rows are found.
- *
- * @throws Exception
- */
- public function fetchNumeric(string $query, array $params = [], array $types = [])
- {
- try {
- $stmt = $this->ensureForwardCompatibilityStatement(
- $this->executeQuery($query, $params, $types)
- );
- return $stmt->fetchNumeric();
- } catch (Throwable $e) {
- $this->handleExceptionDuringQuery($e, $query, $params, $types);
- }
- }
- /**
- * Prepares and executes an SQL query and returns the value of a single column
- * of the first row of the result.
- *
- * @param string $query SQL query
- * @param array<int, mixed>|array<string, mixed> $params Query parameters
- * @param array<int, int|string|Type|null>|array<string, int|string|Type|null> $types Parameter types
- *
- * @return mixed|false False is returned if no rows are found.
- *
- * @throws Exception
- */
- public function fetchOne(string $query, array $params = [], array $types = [])
- {
- try {
- $stmt = $this->ensureForwardCompatibilityStatement(
- $this->executeQuery($query, $params, $types)
- );
- return $stmt->fetchOne();
- } catch (Throwable $e) {
- $this->handleExceptionDuringQuery($e, $query, $params, $types);
- }
- }
- /**
- * Whether an actual connection to the database is established.
- *
- * @return bool
- */
- public function isConnected()
- {
- return $this->_conn !== null;
- }
- /**
- * Checks whether a transaction is currently active.
- *
- * @return bool TRUE if a transaction is currently active, FALSE otherwise.
- */
- public function isTransactionActive()
- {
- return $this->transactionNestingLevel > 0;
- }
- /**
- * Adds condition based on the criteria to the query components
- *
- * @param array<string,mixed> $criteria Map of key columns to their values
- * @param string[] $columns Column names
- * @param mixed[] $values Column values
- * @param string[] $conditions Key conditions
- *
- * @throws Exception
- */
- private function addCriteriaCondition(
- array $criteria,
- array &$columns,
- array &$values,
- array &$conditions
- ): void {
- $platform = $this->getDatabasePlatform();
- foreach ($criteria as $columnName => $value) {
- if ($value === null) {
- $conditions[] = $platform->getIsNullExpression($columnName);
- continue;
- }
- $columns[] = $columnName;
- $values[] = $value;
- $conditions[] = $columnName . ' = ?';
- }
- }
- /**
- * Executes an SQL DELETE statement on a table.
- *
- * Table expression and columns are not escaped and are not safe for user-input.
- *
- * @param string $table Table name
- * @param array<string, mixed> $criteria Deletion criteria
- * @param array<int, int|string|Type|null>|array<string, int|string|Type|null> $types Parameter types
- *
- * @return int The number of affected rows.
- *
- * @throws Exception
- */
- public function delete($table, array $criteria, array $types = [])
- {
- if (empty($criteria)) {
- throw InvalidArgumentException::fromEmptyCriteria();
- }
- $columns = $values = $conditions = [];
- $this->addCriteriaCondition($criteria, $columns, $values, $conditions);
- return $this->executeStatement(
- 'DELETE FROM ' . $table . ' WHERE ' . implode(' AND ', $conditions),
- $values,
- is_string(key($types)) ? $this->extractTypeValues($columns, $types) : $types
- );
- }
- /**
- * Closes the connection.
- *
- * @return void
- */
- public function close()
- {
- $this->_conn = null;
- }
- /**
- * Sets the transaction isolation level.
- *
- * @param int $level The level to set.
- *
- * @return int
- */
- public function setTransactionIsolation($level)
- {
- $this->transactionIsolationLevel = $level;
- return $this->executeStatement($this->getDatabasePlatform()->getSetTransactionIsolationSQL($level));
- }
- /**
- * Gets the currently active transaction isolation level.
- *
- * @return int The current transaction isolation level.
- */
- public function getTransactionIsolation()
- {
- if ($this->transactionIsolationLevel === null) {
- $this->transactionIsolationLevel = $this->getDatabasePlatform()->getDefaultTransactionIsolationLevel();
- }
- return $this->transactionIsolationLevel;
- }
- /**
- * Executes an SQL UPDATE statement on a table.
- *
- * Table expression and columns are not escaped and are not safe for user-input.
- *
- * @param string $table Table name
- * @param array<string, mixed> $data Column-value pairs
- * @param array<string, mixed> $criteria Update criteria
- * @param array<int, int|string|Type|null>|array<string, int|string|Type|null> $types Parameter types
- *
- * @return int The number of affected rows.
- *
- * @throws Exception
- */
- public function update($table, array $data, array $criteria, array $types = [])
- {
- $columns = $values = $conditions = $set = [];
- foreach ($data as $columnName => $value) {
- $columns[] = $columnName;
- $values[] = $value;
- $set[] = $columnName . ' = ?';
- }
- $this->addCriteriaCondition($criteria, $columns, $values, $conditions);
- if (is_string(key($types))) {
- $types = $this->extractTypeValues($columns, $types);
- }
- $sql = 'UPDATE ' . $table . ' SET ' . implode(', ', $set)
- . ' WHERE ' . implode(' AND ', $conditions);
- return $this->executeStatement($sql, $values, $types);
- }
- /**
- * Inserts a table row with specified data.
- *
- * Table expression and columns are not escaped and are not safe for user-input.
- *
- * @param string $table Table name
- * @param array<string, mixed> $data Column-value pairs
- * @param array<int, int|string|Type|null>|array<string, int|string|Type|null> $types Parameter types
- *
- * @return int The number of affected rows.
- *
- * @throws Exception
- */
- public function insert($table, array $data, array $types = [])
- {
- if (empty($data)) {
- return $this->executeStatement('INSERT INTO ' . $table . ' () VALUES ()');
- }
- $columns = [];
- $values = [];
- $set = [];
- foreach ($data as $columnName => $value) {
- $columns[] = $columnName;
- $values[] = $value;
- $set[] = '?';
- }
- return $this->executeStatement(
- 'INSERT INTO ' . $table . ' (' . implode(', ', $columns) . ')' .
- ' VALUES (' . implode(', ', $set) . ')',
- $values,
- is_string(key($types)) ? $this->extractTypeValues($columns, $types) : $types
- );
- }
- /**
- * Extract ordered type list from an ordered column list and type map.
- *
- * @param array<int, string> $columnList
- * @param array<int, int|string|Type|null>|array<string, int|string|Type|null> $types
- *
- * @return array<int, int|string|Type|null>|array<string, int|string|Type|null>
- */
- private function extractTypeValues(array $columnList, array $types)
- {
- $typeValues = [];
- foreach ($columnList as $columnIndex => $columnName) {
- $typeValues[] = $types[$columnName] ?? ParameterType::STRING;
- }
- return $typeValues;
- }
- /**
- * Quotes a string so it can be safely used as a table or column name, even if
- * it is a reserved name.
- *
- * Delimiting style depends on the underlying database platform that is being used.
- *
- * NOTE: Just because you CAN use quoted identifiers does not mean
- * you SHOULD use them. In general, they end up causing way more
- * problems than they solve.
- *
- * @param string $str The name to be quoted.
- *
- * @return string The quoted name.
- */
- public function quoteIdentifier($str)
- {
- return $this->getDatabasePlatform()->quoteIdentifier($str);
- }
- /**
- * {@inheritDoc}
- *
- * @param mixed $value
- * @param int|string|Type|null $type
- */
- public function quote($value, $type = ParameterType::STRING)
- {
- $connection = $this->getWrappedConnection();
- [$value, $bindingType] = $this->getBindingInfo($value, $type);
- return $connection->quote($value, $bindingType);
- }
- /**
- * Prepares and executes an SQL query and returns the result as an associative array.
- *
- * @deprecated Use fetchAllAssociative()
- *
- * @param string $sql The SQL query.
- * @param mixed[] $params The query parameters.
- * @param int[]|string[] $types The query parameter types.
- *
- * @return mixed[]
- */
- public function fetchAll($sql, array $params = [], $types = [])
- {
- return $this->executeQuery($sql, $params, $types)->fetchAll();
- }
- /**
- * Prepares and executes an SQL query and returns the result as an array of numeric arrays.
- *
- * @param string $query SQL query
- * @param array<int, mixed>|array<string, mixed> $params Query parameters
- * @param array<int, int|string|Type|null>|array<string, int|string|Type|null> $types Parameter types
- *
- * @return array<int,array<int,mixed>>
- *
- * @throws Exception
- */
- public function fetchAllNumeric(string $query, array $params = [], array $types = []): array
- {
- try {
- $stmt = $this->ensureForwardCompatibilityStatement(
- $this->executeQuery($query, $params, $types)
- );
- return $stmt->fetchAllNumeric();
- } catch (Throwable $e) {
- $this->handleExceptionDuringQuery($e, $query, $params, $types);
- }
- }
- /**
- * Prepares and executes an SQL query and returns the result as an array of associative arrays.
- *
- * @param string $query SQL query
- * @param array<int, mixed>|array<string, mixed> $params Query parameters
- * @param array<int, int|string|Type|null>|array<string, int|string|Type|null> $types Parameter types
- *
- * @return array<int,array<string,mixed>>
- *
- * @throws Exception
- */
- public function fetchAllAssociative(string $query, array $params = [], array $types = []): array
- {
- try {
- $stmt = $this->ensureForwardCompatibilityStatement(
- $this->executeQuery($query, $params, $types)
- );
- return $stmt->fetchAllAssociative();
- } catch (Throwable $e) {
- $this->handleExceptionDuringQuery($e, $query, $params, $types);
- }
- }
- /**
- * Prepares and executes an SQL query and returns the result as an associative array with the keys
- * mapped to the first column and the values mapped to the second column.
- *
- * @param string $query SQL query
- * @param array<int, mixed>|array<string, mixed> $params Query parameters
- * @param array<int, int|string>|array<string, int|string> $types Parameter types
- *
- * @return array<mixed,mixed>
- *
- * @throws Exception
- */
- public function fetchAllKeyValue(string $query, array $params = [], array $types = []): array
- {
- $stmt = $this->executeQuery($query, $params, $types);
- $this->ensureHasKeyValue($stmt);
- $data = [];
- foreach ($stmt->fetchAll(FetchMode::NUMERIC) as [$key, $value]) {
- $data[$key] = $value;
- }
- return $data;
- }
- /**
- * Prepares and executes an SQL query and returns the result as an associative array with the keys mapped
- * to the first column and the values being an associative array representing the rest of the columns
- * and their values.
- *
- * @param string $query SQL query
- * @param array<int, mixed>|array<string, mixed> $params Query parameters
- * @param array<int, int|string>|array<string, int|string> $types Parameter types
- *
- * @return array<mixed,array<string,mixed>>
- *
- * @throws Exception
- */
- public function fetchAllAssociativeIndexed(string $query, array $params = [], array $types = []): array
- {
- $stmt = $this->executeQuery($query, $params, $types);
- $data = [];
- foreach ($stmt->fetchAll(FetchMode::ASSOCIATIVE) as $row) {
- $data[array_shift($row)] = $row;
- }
- return $data;
- }
- /**
- * Prepares and executes an SQL query and returns the result as an array of the first column values.
- *
- * @param string $query SQL query
- * @param array<int, mixed>|array<string, mixed> $params Query parameters
- * @param array<int, int|string|Type|null>|array<string, int|string|Type|null> $types Parameter types
- *
- * @return array<int,mixed>
- *
- * @throws Exception
- */
- public function fetchFirstColumn(string $query, array $params = [], array $types = []): array
- {
- try {
- $stmt = $this->ensureForwardCompatibilityStatement(
- $this->executeQuery($query, $params, $types)
- );
- return $stmt->fetchFirstColumn();
- } catch (Throwable $e) {
- $this->handleExceptionDuringQuery($e, $query, $params, $types);
- }
- }
- /**
- * Prepares and executes an SQL query and returns the result as an iterator over rows represented as numeric arrays.
- *
- * @param string $query SQL query
- * @param array<int, mixed>|array<string, mixed> $params Query parameters
- * @param array<int, int|string|Type|null>|array<string, int|string|Type|null> $types Parameter types
- *
- * @return Traversable<int,array<int,mixed>>
- *
- * @throws Exception
- */
- public function iterateNumeric(string $query, array $params = [], array $types = []): Traversable
- {
- try {
- $stmt = $this->ensureForwardCompatibilityStatement(
- $this->executeQuery($query, $params, $types)
- );
- yield from $stmt->iterateNumeric();
- } catch (Throwable $e) {
- $this->handleExceptionDuringQuery($e, $query, $params, $types);
- }
- }
- /**
- * Prepares and executes an SQL query and returns the result as an iterator over rows represented
- * as associative arrays.
- *
- * @param string $query SQL query
- * @param array<int, mixed>|array<string, mixed> $params Query parameters
- * @param array<int, int|string|Type|null>|array<string, int|string|Type|null> $types Parameter types
- *
- * @return Traversable<int,array<string,mixed>>
- *
- * @throws Exception
- */
- public function iterateAssociative(string $query, array $params = [], array $types = []): Traversable
- {
- try {
- $stmt = $this->ensureForwardCompatibilityStatement(
- $this->executeQuery($query, $params, $types)
- );
- yield from $stmt->iterateAssociative();
- } catch (Throwable $e) {
- $this->handleExceptionDuringQuery($e, $query, $params, $types);
- }
- }
- /**
- * Prepares and executes an SQL query and returns the result as an iterator with the keys
- * mapped to the first column and the values mapped to the second column.
- *
- * @param string $query SQL query
- * @param array<int, mixed>|array<string, mixed> $params Query parameters
- * @param array<int, int|string>|array<string, int|string> $types Parameter types
- *
- * @return Traversable<mixed,mixed>
- *
- * @throws Exception
- */
- public function iterateKeyValue(string $query, array $params = [], array $types = []): Traversable
- {
- $stmt = $this->executeQuery($query, $params, $types);
- $this->ensureHasKeyValue($stmt);
- while (($row = $stmt->fetch(FetchMode::NUMERIC)) !== false) {
- yield $row[0] => $row[1];
- }
- }
- /**
- * Prepares and executes an SQL query and returns the result as an iterator with the keys mapped
- * to the first column and the values being an associative array representing the rest of the columns
- * and their values.
- *
- * @param string $query SQL query
- * @param array<int, mixed>|array<string, mixed> $params Query parameters
- * @param array<int, int|string>|array<string, int|string> $types Parameter types
- *
- * @return Traversable<mixed,array<string,mixed>>
- *
- * @throws Exception
- */
- public function iterateAssociativeIndexed(string $query, array $params = [], array $types = []): Traversable
- {
- $stmt = $this->executeQuery($query, $params, $types);
- while (($row = $stmt->fetch(FetchMode::ASSOCIATIVE)) !== false) {
- yield array_shift($row) => $row;
- }
- }
- /**
- * Prepares and executes an SQL query and returns the result as an iterator over the first column values.
- *
- * @param string $query SQL query
- * @param array<int, mixed>|array<string, mixed> $params Query parameters
- * @param array<int, int|string|Type|null>|array<string, int|string|Type|null> $types Parameter types
- *
- * @return Traversable<int,mixed>
- *
- * @throws Exception
- */
- public function iterateColumn(string $query, array $params = [], array $types = []): Traversable
- {
- try {
- $stmt = $this->ensureForwardCompatibilityStatement(
- $this->executeQuery($query, $params, $types)
- );
- yield from $stmt->iterateColumn();
- } catch (Throwable $e) {
- $this->handleExceptionDuringQuery($e, $query, $params, $types);
- }
- }
- /**
- * Prepares an SQL statement.
- *
- * @param string $sql The SQL statement to prepare.
- *
- * @return Statement The prepared statement.
- *
- * @throws Exception
- */
- public function prepare($sql)
- {
- try {
- $stmt = new Statement($sql, $this);
- } catch (Throwable $e) {
- $this->handleExceptionDuringQuery($e, $sql);
- }
- $stmt->setFetchMode($this->defaultFetchMode);
- return $stmt;
- }
- /**
- * Executes an, optionally parametrized, SQL query.
- *
- * If the query is parametrized, a prepared statement is used.
- * If an SQLLogger is configured, the execution is logged.
- *
- * @param string $sql SQL query
- * @param array<int, mixed>|array<string, mixed> $params Query parameters
- * @param array<int, int|string|Type|null>|array<string, int|string|Type|null> $types Parameter types
- *
- * @return ForwardCompatibility\DriverStatement|ForwardCompatibility\DriverResultStatement
- *
- * The executed statement or the cached result statement if a query cache profile is used
- *
- * @throws Exception
- */
- public function executeQuery($sql, array $params = [], $types = [], ?QueryCacheProfile $qcp = null)
- {
- if ($qcp !== null) {
- return $this->executeCacheQuery($sql, $params, $types, $qcp);
- }
- $connection = $this->getWrappedConnection();
- $logger = $this->_config->getSQLLogger();
- if ($logger) {
- $logger->startQuery($sql, $params, $types);
- }
- try {
- if ($params) {
- [$sql, $params, $types] = SQLParserUtils::expandListParameters($sql, $params, $types);
- $stmt = $connection->prepare($sql);
- if ($types) {
- $this->_bindTypedValues($stmt, $params, $types);
- $stmt->execute();
- } else {
- $stmt->execute($params);
- }
- } else {
- $stmt = $connection->query($sql);
- }
- } catch (Throwable $e) {
- $this->handleExceptionDuringQuery(
- $e,
- $sql,
- $params,
- $types
- );
- }
- $stmt->setFetchMode($this->defaultFetchMode);
- if ($logger) {
- $logger->stopQuery();
- }
- return $this->ensureForwardCompatibilityStatement($stmt);
- }
- /**
- * Executes a caching query.
- *
- * @param string $sql SQL query
- * @param array<int, mixed>|array<string, mixed> $params Query parameters
- * @param array<int, int|string|Type|null>|array<string, int|string|Type|null> $types Parameter types
- *
- * @return ForwardCompatibility\DriverResultStatement
- *
- * @throws CacheException
- */
- public function executeCacheQuery($sql, $params, $types, QueryCacheProfile $qcp)
- {
- $resultCache = $qcp->getResultCacheDriver() ?? $this->_config->getResultCacheImpl();
- if ($resultCache === null) {
- throw CacheException::noResultDriverConfigured();
- }
- $connectionParams = $this->params;
- unset($connectionParams['platform']);
- [$cacheKey, $realKey] = $qcp->generateCacheKeys($sql, $params, $types, $connectionParams);
- // fetch the row pointers entry
- $data = $resultCache->fetch($cacheKey);
- if ($data !== false) {
- // is the real key part of this row pointers map or is the cache only pointing to other cache keys?
- if (isset($data[$realKey])) {
- $stmt = new ArrayStatement($data[$realKey]);
- } elseif (array_key_exists($realKey, $data)) {
- $stmt = new ArrayStatement([]);
- }
- }
- if (! isset($stmt)) {
- $stmt = new ResultCacheStatement(
- $this->executeQuery($sql, $params, $types),
- $resultCache,
- $cacheKey,
- $realKey,
- $qcp->getLifetime()
- );
- }
- $stmt->setFetchMode($this->defaultFetchMode);
- return $this->ensureForwardCompatibilityStatement($stmt);
- }
- /**
- * @return ForwardCompatibility\Result
- */
- private function ensureForwardCompatibilityStatement(ResultStatement $stmt)
- {
- return ForwardCompatibility\Result::ensure($stmt);
- }
- /**
- * Executes an, optionally parametrized, SQL query and returns the result,
- * applying a given projection/transformation function on each row of the result.
- *
- * @deprecated
- *
- * @param string $sql The SQL query to execute.
- * @param mixed[] $params The parameters, if any.
- * @param Closure $function The transformation function that is applied on each row.
- * The function receives a single parameter, an array, that
- * represents a row of the result set.
- *
- * @return mixed[] The projected result of the query.
- */
- public function project($sql, array $params, Closure $function)
- {
- Deprecation::trigger(
- 'doctrine/dbal',
- 'https://github.com/doctrine/dbal/pull/3823',
- 'Connection::project() is deprecated without replacement, implement data projections in your own code.'
- );
- $result = [];
- $stmt = $this->executeQuery($sql, $params);
- while ($row = $stmt->fetch()) {
- $result[] = $function($row);
- }
- $stmt->closeCursor();
- return $result;
- }
- /**
- * Executes an SQL statement, returning a result set as a Statement object.
- *
- * @deprecated Use {@link executeQuery()} instead.
- *
- * @return \Doctrine\DBAL\Driver\Statement
- *
- * @throws Exception
- */
- public function query()
- {
- Deprecation::trigger(
- 'doctrine/dbal',
- 'https://github.com/doctrine/dbal/pull/4163',
- 'Connection::query() is deprecated, use Connection::executeQuery() instead.'
- );
- $connection = $this->getWrappedConnection();
- $args = func_get_args();
- $logger = $this->_config->getSQLLogger();
- if ($logger) {
- $logger->startQuery($args[0]);
- }
- try {
- $statement = $connection->query(...$args);
- } catch (Throwable $e) {
- $this->handleExceptionDuringQuery($e, $args[0]);
- }
- $statement->setFetchMode($this->defaultFetchMode);
- if ($logger) {
- $logger->stopQuery();
- }
- return $statement;
- }
- /**
- * Executes an SQL INSERT/UPDATE/DELETE query with the given parameters
- * and returns the number of affected rows.
- *
- * This method supports PDO binding types as well as DBAL mapping types.
- *
- * @deprecated Use {@link executeStatement()} instead.
- *
- * @param string $sql SQL statement
- * @param array<int, mixed>|array<string, mixed> $params Statement parameters
- * @param array<int, int|string|Type|null>|array<string, int|string|Type|null> $types Parameter types
- *
- * @return int The number of affected rows.
- *
- * @throws Exception
- */
- public function executeUpdate($sql, array $params = [], array $types = [])
- {
- Deprecation::trigger(
- 'doctrine/dbal',
- 'https://github.com/doctrine/dbal/pull/4163',
- 'Connection::executeUpdate() is deprecated, use Connection::executeStatement() instead.'
- );
- return $this->executeStatement($sql, $params, $types);
- }
- /**
- * Executes an SQL statement with the given parameters and returns the number of affected rows.
- *
- * Could be used for:
- * - DML statements: INSERT, UPDATE, DELETE, etc.
- * - DDL statements: CREATE, DROP, ALTER, etc.
- * - DCL statements: GRANT, REVOKE, etc.
- * - Session control statements: ALTER SESSION, SET, DECLARE, etc.
- * - Other statements that don't yield a row set.
- *
- * This method supports PDO binding types as well as DBAL mapping types.
- *
- * @param string $sql SQL statement
- * @param array<int, mixed>|array<string, mixed> $params Statement parameters
- * @param array<int, int|string|Type|null>|array<string, int|string|Type|null> $types Parameter types
- *
- * @return int The number of affected rows.
- *
- * @throws Exception
- */
- public function executeStatement($sql, array $params = [], array $types = [])
- {
- $connection = $this->getWrappedConnection();
- $logger = $this->_config->getSQLLogger();
- if ($logger) {
- $logger->startQuery($sql, $params, $types);
- }
- try {
- if ($params) {
- [$sql, $params, $types] = SQLParserUtils::expandListParameters($sql, $params, $types);
- $stmt = $connection->prepare($sql);
- if ($types) {
- $this->_bindTypedValues($stmt, $params, $types);
- $stmt->execute();
- } else {
- $stmt->execute($params);
- }
- $result = $stmt->rowCount();
- } else {
- $result = $connection->exec($sql);
- }
- } catch (Throwable $e) {
- $this->handleExceptionDuringQuery(
- $e,
- $sql,
- $params,
- $types
- );
- }
- if ($logger) {
- $logger->stopQuery();
- }
- return $result;
- }
- /**
- * Executes an SQL statement and return the number of affected rows.
- *
- * @deprecated Use {@link executeStatement()} instead.
- *
- * @param string $sql
- *
- * @return int The number of affected rows.
- *
- * @throws Exception
- */
- public function exec($sql)
- {
- Deprecation::trigger(
- 'doctrine/dbal',
- 'https://github.com/doctrine/dbal/pull/4163',
- 'Connection::exec() is deprecated, use Connection::executeStatement() instead.'
- );
- $connection = $this->getWrappedConnection();
- $logger = $this->_config->getSQLLogger();
- if ($logger) {
- $logger->startQuery($sql);
- }
- try {
- $result = $connection->exec($sql);
- } catch (Throwable $e) {
- $this->handleExceptionDuringQuery($e, $sql);
- }
- if ($logger) {
- $logger->stopQuery();
- }
- return $result;
- }
- /**
- * Returns the current transaction nesting level.
- *
- * @return int The nesting level. A value of 0 means there's no active transaction.
- */
- public function getTransactionNestingLevel()
- {
- return $this->transactionNestingLevel;
- }
- /**
- * Fetches the SQLSTATE associated with the last database operation.
- *
- * @deprecated The error information is available via exceptions.
- *
- * @return string|null The last error code.
- */
- public function errorCode()
- {
- Deprecation::trigger(
- 'doctrine/dbal',
- 'https://github.com/doctrine/dbal/pull/3507',
- 'Connection::errorCode() is deprecated, use getCode() or getSQLState() on Exception instead.'
- );
- return $this->getWrappedConnection()->errorCode();
- }
- /**
- * {@inheritDoc}
- *
- * @deprecated The error information is available via exceptions.
- */
- public function errorInfo()
- {
- Deprecation::trigger(
- 'doctrine/dbal',
- 'https://github.com/doctrine/dbal/pull/3507',
- 'Connection::errorInfo() is deprecated, use getCode() or getSQLState() on Exception instead.'
- );
- return $this->getWrappedConnection()->errorInfo();
- }
- /**
- * Returns the ID of the last inserted row, or the last value from a sequence object,
- * depending on the underlying driver.
- *
- * Note: This method may not return a meaningful or consistent result across different drivers,
- * because the underlying database may not even support the notion of AUTO_INCREMENT/IDENTITY
- * columns or sequences.
- *
- * @param string|null $name Name of the sequence object from which the ID should be returned.
- *
- * @return string|int|false A string representation of the last inserted ID.
- */
- public function lastInsertId($name = null)
- {
- return $this->getWrappedConnection()->lastInsertId($name);
- }
- /**
- * Executes a function in a transaction.
- *
- * The function gets passed this Connection instance as an (optional) parameter.
- *
- * If an exception occurs during execution of the function or transaction commit,
- * the transaction is rolled back and the exception re-thrown.
- *
- * @param Closure $func The function to execute transactionally.
- *
- * @return mixed The value returned by $func
- *
- * @throws Throwable
- */
- public function transactional(Closure $func)
- {
- $this->beginTransaction();
- try {
- $res = $func($this);
- $this->commit();
- return $res;
- } catch (Throwable $e) {
- $this->rollBack();
- throw $e;
- }
- }
- /**
- * Sets if nested transactions should use savepoints.
- *
- * @param bool $nestTransactionsWithSavepoints
- *
- * @return void
- *
- * @throws ConnectionException
- */
- public function setNestTransactionsWithSavepoints($nestTransactionsWithSavepoints)
- {
- if ($this->transactionNestingLevel > 0) {
- throw ConnectionException::mayNotAlterNestedTransactionWithSavepointsInTransaction();
- }
- if (! $this->getDatabasePlatform()->supportsSavepoints()) {
- throw ConnectionException::savepointsNotSupported();
- }
- $this->nestTransactionsWithSavepoints = (bool) $nestTransactionsWithSavepoints;
- }
- /**
- * Gets if nested transactions should use savepoints.
- *
- * @return bool
- */
- public function getNestTransactionsWithSavepoints()
- {
- return $this->nestTransactionsWithSavepoints;
- }
- /**
- * Returns the savepoint name to use for nested transactions are false if they are not supported
- * "savepointFormat" parameter is not set
- *
- * @return mixed A string with the savepoint name or false.
- */
- protected function _getNestedTransactionSavePointName()
- {
- return 'DOCTRINE2_SAVEPOINT_' . $this->transactionNestingLevel;
- }
- /**
- * {@inheritDoc}
- */
- public function beginTransaction()
- {
- $connection = $this->getWrappedConnection();
- ++$this->transactionNestingLevel;
- $logger = $this->_config->getSQLLogger();
- if ($this->transactionNestingLevel === 1) {
- if ($logger) {
- $logger->startQuery('"START TRANSACTION"');
- }
- $connection->beginTransaction();
- if ($logger) {
- $logger->stopQuery();
- }
- } elseif ($this->nestTransactionsWithSavepoints) {
- if ($logger) {
- $logger->startQuery('"SAVEPOINT"');
- }
- $this->createSavepoint($this->_getNestedTransactionSavePointName());
- if ($logger) {
- $logger->stopQuery();
- }
- }
- return true;
- }
- /**
- * {@inheritDoc}
- *
- * @throws ConnectionException If the commit failed due to no active transaction or
- * because the transaction was marked for rollback only.
- */
- public function commit()
- {
- if ($this->transactionNestingLevel === 0) {
- throw ConnectionException::noActiveTransaction();
- }
- if ($this->isRollbackOnly) {
- throw ConnectionException::commitFailedRollbackOnly();
- }
- $result = true;
- $connection = $this->getWrappedConnection();
- $logger = $this->_config->getSQLLogger();
- if ($this->transactionNestingLevel === 1) {
- if ($logger) {
- $logger->startQuery('"COMMIT"');
- }
- $result = $connection->commit();
- if ($logger) {
- $logger->stopQuery();
- }
- } elseif ($this->nestTransactionsWithSavepoints) {
- if ($logger) {
- $logger->startQuery('"RELEASE SAVEPOINT"');
- }
- $this->releaseSavepoint($this->_getNestedTransactionSavePointName());
- if ($logger) {
- $logger->stopQuery();
- }
- }
- --$this->transactionNestingLevel;
- if ($this->autoCommit !== false || $this->transactionNestingLevel !== 0) {
- return $result;
- }
- $this->beginTransaction();
- return $result;
- }
- /**
- * Commits all current nesting transactions.
- */
- private function commitAll(): void
- {
- while ($this->transactionNestingLevel !== 0) {
- if ($this->autoCommit === false && $this->transactionNestingLevel === 1) {
- // When in no auto-commit mode, the last nesting commit immediately starts a new transaction.
- // Therefore we need to do the final commit here and then leave to avoid an infinite loop.
- $this->commit();
- return;
- }
- $this->commit();
- }
- }
- /**
- * Cancels any database changes done during the current transaction.
- *
- * @return bool
- *
- * @throws ConnectionException If the rollback operation failed.
- */
- public function rollBack()
- {
- if ($this->transactionNestingLevel === 0) {
- throw ConnectionException::noActiveTransaction();
- }
- $connection = $this->getWrappedConnection();
- $logger = $this->_config->getSQLLogger();
- if ($this->transactionNestingLevel === 1) {
- if ($logger) {
- $logger->startQuery('"ROLLBACK"');
- }
- $this->transactionNestingLevel = 0;
- $connection->rollBack();
- $this->isRollbackOnly = false;
- if ($logger) {
- $logger->stopQuery();
- }
- if ($this->autoCommit === false) {
- $this->beginTransaction();
- }
- } elseif ($this->nestTransactionsWithSavepoints) {
- if ($logger) {
- $logger->startQuery('"ROLLBACK TO SAVEPOINT"');
- }
- $this->rollbackSavepoint($this->_getNestedTransactionSavePointName());
- --$this->transactionNestingLevel;
- if ($logger) {
- $logger->stopQuery();
- }
- } else {
- $this->isRollbackOnly = true;
- --$this->transactionNestingLevel;
- }
- return true;
- }
- /**
- * Creates a new savepoint.
- *
- * @param string $savepoint The name of the savepoint to create.
- *
- * @return void
- *
- * @throws ConnectionException
- */
- public function createSavepoint($savepoint)
- {
- $platform = $this->getDatabasePlatform();
- if (! $platform->supportsSavepoints()) {
- throw ConnectionException::savepointsNotSupported();
- }
- $this->getWrappedConnection()->exec($platform->createSavePoint($savepoint));
- }
- /**
- * Releases the given savepoint.
- *
- * @param string $savepoint The name of the savepoint to release.
- *
- * @return void
- *
- * @throws ConnectionException
- */
- public function releaseSavepoint($savepoint)
- {
- $platform = $this->getDatabasePlatform();
- if (! $platform->supportsSavepoints()) {
- throw ConnectionException::savepointsNotSupported();
- }
- if (! $platform->supportsReleaseSavepoints()) {
- return;
- }
- $this->getWrappedConnection()->exec($platform->releaseSavePoint($savepoint));
- }
- /**
- * Rolls back to the given savepoint.
- *
- * @param string $savepoint The name of the savepoint to rollback to.
- *
- * @return void
- *
- * @throws ConnectionException
- */
- public function rollbackSavepoint($savepoint)
- {
- $platform = $this->getDatabasePlatform();
- if (! $platform->supportsSavepoints()) {
- throw ConnectionException::savepointsNotSupported();
- }
- $this->getWrappedConnection()->exec($platform->rollbackSavePoint($savepoint));
- }
- /**
- * Gets the wrapped driver connection.
- *
- * @return DriverConnection
- */
- public function getWrappedConnection()
- {
- $this->connect();
- assert($this->_conn !== null);
- return $this->_conn;
- }
- /**
- * Gets the SchemaManager that can be used to inspect or change the
- * database schema through the connection.
- *
- * @return AbstractSchemaManager
- */
- public function getSchemaManager()
- {
- if ($this->_schemaManager === null) {
- $this->_schemaManager = $this->_driver->getSchemaManager($this);
- }
- return $this->_schemaManager;
- }
- /**
- * Marks the current transaction so that the only possible
- * outcome for the transaction to be rolled back.
- *
- * @return void
- *
- * @throws ConnectionException If no transaction is active.
- */
- public function setRollbackOnly()
- {
- if ($this->transactionNestingLevel === 0) {
- throw ConnectionException::noActiveTransaction();
- }
- $this->isRollbackOnly = true;
- }
- /**
- * Checks whether the current transaction is marked for rollback only.
- *
- * @return bool
- *
- * @throws ConnectionException If no transaction is active.
- */
- public function isRollbackOnly()
- {
- if ($this->transactionNestingLevel === 0) {
- throw ConnectionException::noActiveTransaction();
- }
- return $this->isRollbackOnly;
- }
- /**
- * Converts a given value to its database representation according to the conversion
- * rules of a specific DBAL mapping type.
- *
- * @param mixed $value The value to convert.
- * @param string $type The name of the DBAL mapping type.
- *
- * @return mixed The converted value.
- */
- public function convertToDatabaseValue($value, $type)
- {
- return Type::getType($type)->convertToDatabaseValue($value, $this->getDatabasePlatform());
- }
- /**
- * Converts a given value to its PHP representation according to the conversion
- * rules of a specific DBAL mapping type.
- *
- * @param mixed $value The value to convert.
- * @param string $type The name of the DBAL mapping type.
- *
- * @return mixed The converted type.
- */
- public function convertToPHPValue($value, $type)
- {
- return Type::getType($type)->convertToPHPValue($value, $this->getDatabasePlatform());
- }
- /**
- * Binds a set of parameters, some or all of which are typed with a PDO binding type
- * or DBAL mapping type, to a given statement.
- *
- * @internal Duck-typing used on the $stmt parameter to support driver statements as well as
- * raw PDOStatement instances.
- *
- * @param \Doctrine\DBAL\Driver\Statement $stmt Prepared statement
- * @param array<int, mixed>|array<string, mixed> $params Statement parameters
- * @param array<int, int|string|Type|null>|array<string, int|string|Type|null> $types Parameter types
- *
- * @return void
- */
- private function _bindTypedValues($stmt, array $params, array $types)
- {
- // Check whether parameters are positional or named. Mixing is not allowed, just like in PDO.
- if (is_int(key($params))) {
- // Positional parameters
- $typeOffset = array_key_exists(0, $types) ? -1 : 0;
- $bindIndex = 1;
- foreach ($params as $value) {
- $typeIndex = $bindIndex + $typeOffset;
- if (isset($types[$typeIndex])) {
- $type = $types[$typeIndex];
- [$value, $bindingType] = $this->getBindingInfo($value, $type);
- $stmt->bindValue($bindIndex, $value, $bindingType);
- } else {
- $stmt->bindValue($bindIndex, $value);
- }
- ++$bindIndex;
- }
- } else {
- // Named parameters
- foreach ($params as $name => $value) {
- if (isset($types[$name])) {
- $type = $types[$name];
- [$value, $bindingType] = $this->getBindingInfo($value, $type);
- $stmt->bindValue($name, $value, $bindingType);
- } else {
- $stmt->bindValue($name, $value);
- }
- }
- }
- }
- /**
- * Gets the binding type of a given type. The given type can be a PDO or DBAL mapping type.
- *
- * @param mixed $value The value to bind.
- * @param int|string|Type|null $type The type to bind (PDO or DBAL).
- *
- * @return array{mixed, int} [0] => the (escaped) value, [1] => the binding type.
- */
- private function getBindingInfo($value, $type): array
- {
- if (is_string($type)) {
- $type = Type::getType($type);
- }
- if ($type instanceof Type) {
- $value = $type->convertToDatabaseValue($value, $this->getDatabasePlatform());
- $bindingType = $type->getBindingType();
- } else {
- $bindingType = $type ?? ParameterType::STRING;
- }
- return [$value, $bindingType];
- }
- /**
- * Resolves the parameters to a format which can be displayed.
- *
- * @internal This is a purely internal method. If you rely on this method, you are advised to
- * copy/paste the code as this method may change, or be removed without prior notice.
- *
- * @param array<int, mixed>|array<string, mixed> $params Query parameters
- * @param array<int, int|string|Type|null>|array<string, int|string|Type|null> $types Parameter types
- *
- * @return array<int, int|string|Type|null>|array<string, int|string|Type|null>
- */
- public function resolveParams(array $params, array $types)
- {
- $resolvedParams = [];
- // Check whether parameters are positional or named. Mixing is not allowed, just like in PDO.
- if (is_int(key($params))) {
- // Positional parameters
- $typeOffset = array_key_exists(0, $types) ? -1 : 0;
- $bindIndex = 1;
- foreach ($params as $value) {
- $typeIndex = $bindIndex + $typeOffset;
- if (isset($types[$typeIndex])) {
- $type = $types[$typeIndex];
- [$value] = $this->getBindingInfo($value, $type);
- $resolvedParams[$bindIndex] = $value;
- } else {
- $resolvedParams[$bindIndex] = $value;
- }
- ++$bindIndex;
- }
- } else {
- // Named parameters
- foreach ($params as $name => $value) {
- if (isset($types[$name])) {
- $type = $types[$name];
- [$value] = $this->getBindingInfo($value, $type);
- $resolvedParams[$name] = $value;
- } else {
- $resolvedParams[$name] = $value;
- }
- }
- }
- return $resolvedParams;
- }
- /**
- * Creates a new instance of a SQL query builder.
- *
- * @return QueryBuilder
- */
- public function createQueryBuilder()
- {
- return new Query\QueryBuilder($this);
- }
- /**
- * Ping the server
- *
- * When the server is not available the method returns FALSE.
- * It is responsibility of the developer to handle this case
- * and abort the request or reconnect manually:
- *
- * @deprecated
- *
- * @return bool
- *
- * @example
- *
- * if ($conn->ping() === false) {
- * $conn->close();
- * $conn->connect();
- * }
- *
- * It is undefined if the underlying driver attempts to reconnect
- * or disconnect when the connection is not available anymore
- * as long it returns TRUE when a reconnect succeeded and
- * FALSE when the connection was dropped.
- */
- public function ping()
- {
- Deprecation::trigger(
- 'doctrine/dbal',
- 'https://github.com/doctrine/dbal/pull/4119',
- 'Retry and reconnecting lost connections now happens automatically, ping() will be removed in DBAL 3.'
- );
- $connection = $this->getWrappedConnection();
- if ($connection instanceof PingableConnection) {
- return $connection->ping();
- }
- try {
- $this->query($this->getDatabasePlatform()->getDummySelectSQL());
- return true;
- } catch (DBALException $e) {
- return false;
- }
- }
- /**
- * @internal
- *
- * @param array<int, mixed>|array<string, mixed> $params
- * @param array<int, int|string|Type|null>|array<string, int|string|Type|null> $types
- *
- * @psalm-return never-return
- *
- * @throws Exception
- */
- public function handleExceptionDuringQuery(Throwable $e, string $sql, array $params = [], array $types = []): void
- {
- $this->throw(
- Exception::driverExceptionDuringQuery(
- $this->_driver,
- $e,
- $sql,
- $this->resolveParams($params, $types)
- )
- );
- }
- /**
- * @internal
- *
- * @psalm-return never-return
- *
- * @throws Exception
- */
- public function handleDriverException(Throwable $e): void
- {
- $this->throw(
- Exception::driverException(
- $this->_driver,
- $e
- )
- );
- }
- /**
- * @internal
- *
- * @psalm-return never-return
- *
- * @throws Exception
- */
- private function throw(Exception $e): void
- {
- if ($e instanceof ConnectionLost) {
- $this->close();
- }
- throw $e;
- }
- private function ensureHasKeyValue(ResultStatement $stmt): void
- {
- $columnCount = $stmt->columnCount();
- if ($columnCount < 2) {
- throw NoKeyValue::fromColumnCount($columnCount);
- }
- }
- }
|