1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260 |
- <?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\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 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.
- *
- * @throws Exception
- *
- * @phpstan-param array<string,mixed> $params
- * @psalm-param Params $params
- */
- public function __construct(
- array $params,
- Driver $driver,
- ?Configuration $config = null,
- ?EventManager $eventManager = null
- ) {
- $this->_driver = $driver;
- $this->params = $params;
- if (isset($params['pdo'])) {
- $this->_conn = $params['pdo'];
- 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>
- *
- * @phpstan-return array<string,mixed>
- * @psalm-return Params
- */
- 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 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 mixed[] [0] => the (escaped) value, [1] => the binding type.
- */
- private function getBindingInfo($value, $type)
- {
- if (is_string($type)) {
- $type = Type::getType($type);
- }
- if ($type instanceof Type) {
- $value = $type->convertToDatabaseValue($value, $this->getDatabasePlatform());
- $bindingType = $type->getBindingType();
- } else {
- $bindingType = $type;
- }
- 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
- *
- * @throws Exception
- *
- * @psalm-return never-return
- */
- 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
- *
- * @throws Exception
- *
- * @psalm-return never-return
- */
- public function handleDriverException(Throwable $e): void
- {
- $this->throw(
- Exception::driverException(
- $this->_driver,
- $e
- )
- );
- }
- /**
- * @internal
- *
- * @throws Exception
- *
- * @psalm-return never-return
- */
- 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);
- }
- }
- }
|