vendor/doctrine/dbal/lib/Doctrine/DBAL/Connection.php line 1276

Open in your IDE?
  1. <?php
  2. namespace Doctrine\DBAL;
  3. use Closure;
  4. use Doctrine\Common\EventManager;
  5. use Doctrine\DBAL\Cache\ArrayStatement;
  6. use Doctrine\DBAL\Cache\CacheException;
  7. use Doctrine\DBAL\Cache\QueryCacheProfile;
  8. use Doctrine\DBAL\Cache\ResultCacheStatement;
  9. use Doctrine\DBAL\Driver\Connection as DriverConnection;
  10. use Doctrine\DBAL\Driver\PingableConnection;
  11. use Doctrine\DBAL\Driver\ResultStatement;
  12. use Doctrine\DBAL\Driver\ServerInfoAwareConnection;
  13. use Doctrine\DBAL\Exception\ConnectionLost;
  14. use Doctrine\DBAL\Exception\InvalidArgumentException;
  15. use Doctrine\DBAL\Exception\NoKeyValue;
  16. use Doctrine\DBAL\Platforms\AbstractPlatform;
  17. use Doctrine\DBAL\Query\Expression\ExpressionBuilder;
  18. use Doctrine\DBAL\Query\QueryBuilder;
  19. use Doctrine\DBAL\Schema\AbstractSchemaManager;
  20. use Doctrine\DBAL\Types\Type;
  21. use Doctrine\Deprecations\Deprecation;
  22. use Throwable;
  23. use Traversable;
  24. use function array_key_exists;
  25. use function array_shift;
  26. use function assert;
  27. use function func_get_args;
  28. use function implode;
  29. use function is_int;
  30. use function is_string;
  31. use function key;
  32. /**
  33.  * A wrapper around a Doctrine\DBAL\Driver\Connection that adds features like
  34.  * events, transaction isolation levels, configuration, emulated transaction nesting,
  35.  * lazy connecting and more.
  36.  *
  37.  * @psalm-import-type Params from DriverManager
  38.  */
  39. class Connection implements DriverConnection
  40. {
  41.     /**
  42.      * Constant for transaction isolation level READ UNCOMMITTED.
  43.      *
  44.      * @deprecated Use TransactionIsolationLevel::READ_UNCOMMITTED.
  45.      */
  46.     public const TRANSACTION_READ_UNCOMMITTED TransactionIsolationLevel::READ_UNCOMMITTED;
  47.     /**
  48.      * Constant for transaction isolation level READ COMMITTED.
  49.      *
  50.      * @deprecated Use TransactionIsolationLevel::READ_COMMITTED.
  51.      */
  52.     public const TRANSACTION_READ_COMMITTED TransactionIsolationLevel::READ_COMMITTED;
  53.     /**
  54.      * Constant for transaction isolation level REPEATABLE READ.
  55.      *
  56.      * @deprecated Use TransactionIsolationLevel::REPEATABLE_READ.
  57.      */
  58.     public const TRANSACTION_REPEATABLE_READ TransactionIsolationLevel::REPEATABLE_READ;
  59.     /**
  60.      * Constant for transaction isolation level SERIALIZABLE.
  61.      *
  62.      * @deprecated Use TransactionIsolationLevel::SERIALIZABLE.
  63.      */
  64.     public const TRANSACTION_SERIALIZABLE TransactionIsolationLevel::SERIALIZABLE;
  65.     /**
  66.      * Represents an array of ints to be expanded by Doctrine SQL parsing.
  67.      */
  68.     public const PARAM_INT_ARRAY ParameterType::INTEGER self::ARRAY_PARAM_OFFSET;
  69.     /**
  70.      * Represents an array of strings to be expanded by Doctrine SQL parsing.
  71.      */
  72.     public const PARAM_STR_ARRAY ParameterType::STRING self::ARRAY_PARAM_OFFSET;
  73.     /**
  74.      * Offset by which PARAM_* constants are detected as arrays of the param type.
  75.      */
  76.     public const ARRAY_PARAM_OFFSET 100;
  77.     /**
  78.      * The wrapped driver connection.
  79.      *
  80.      * @var \Doctrine\DBAL\Driver\Connection|null
  81.      */
  82.     protected $_conn;
  83.     /** @var Configuration */
  84.     protected $_config;
  85.     /** @var EventManager */
  86.     protected $_eventManager;
  87.     /** @var ExpressionBuilder */
  88.     protected $_expr;
  89.     /**
  90.      * The current auto-commit mode of this connection.
  91.      *
  92.      * @var bool
  93.      */
  94.     private $autoCommit true;
  95.     /**
  96.      * The transaction nesting level.
  97.      *
  98.      * @var int
  99.      */
  100.     private $transactionNestingLevel 0;
  101.     /**
  102.      * The currently active transaction isolation level or NULL before it has been determined.
  103.      *
  104.      * @var int|null
  105.      */
  106.     private $transactionIsolationLevel;
  107.     /**
  108.      * If nested transactions should use savepoints.
  109.      *
  110.      * @var bool
  111.      */
  112.     private $nestTransactionsWithSavepoints false;
  113.     /**
  114.      * The parameters used during creation of the Connection instance.
  115.      *
  116.      * @var array<string,mixed>
  117.      * @phpstan-var array<string,mixed>
  118.      * @psalm-var Params
  119.      */
  120.     private $params;
  121.     /**
  122.      * The database platform object used by the connection or NULL before it's initialized.
  123.      *
  124.      * @var AbstractPlatform|null
  125.      */
  126.     private $platform;
  127.     /**
  128.      * The schema manager.
  129.      *
  130.      * @var AbstractSchemaManager|null
  131.      */
  132.     protected $_schemaManager;
  133.     /**
  134.      * The used DBAL driver.
  135.      *
  136.      * @var Driver
  137.      */
  138.     protected $_driver;
  139.     /**
  140.      * Flag that indicates whether the current transaction is marked for rollback only.
  141.      *
  142.      * @var bool
  143.      */
  144.     private $isRollbackOnly false;
  145.     /** @var int */
  146.     protected $defaultFetchMode FetchMode::ASSOCIATIVE;
  147.     /**
  148.      * Initializes a new instance of the Connection class.
  149.      *
  150.      * @internal The connection can be only instantiated by the driver manager.
  151.      *
  152.      * @param array<string,mixed> $params       The connection parameters.
  153.      * @param Driver              $driver       The driver to use.
  154.      * @param Configuration|null  $config       The configuration, optional.
  155.      * @param EventManager|null   $eventManager The event manager, optional.
  156.      * @psalm-param Params $params
  157.      * @phpstan-param array<string,mixed> $params
  158.      *
  159.      * @throws Exception
  160.      */
  161.     public function __construct(
  162.         array $params,
  163.         Driver $driver,
  164.         ?Configuration $config null,
  165.         ?EventManager $eventManager null
  166.     ) {
  167.         $this->_driver $driver;
  168.         $this->params  $params;
  169.         if (isset($params['pdo'])) {
  170.             $this->_conn $params['pdo'];
  171.             unset($this->params['pdo']);
  172.         }
  173.         if (isset($params['platform'])) {
  174.             if (! $params['platform'] instanceof Platforms\AbstractPlatform) {
  175.                 throw Exception::invalidPlatformType($params['platform']);
  176.             }
  177.             $this->platform $params['platform'];
  178.         }
  179.         // Create default config and event manager if none given
  180.         if (! $config) {
  181.             $config = new Configuration();
  182.         }
  183.         if (! $eventManager) {
  184.             $eventManager = new EventManager();
  185.         }
  186.         $this->_config       $config;
  187.         $this->_eventManager $eventManager;
  188.         $this->_expr = new Query\Expression\ExpressionBuilder($this);
  189.         $this->autoCommit $config->getAutoCommit();
  190.     }
  191.     /**
  192.      * Gets the parameters used during instantiation.
  193.      *
  194.      * @internal
  195.      *
  196.      * @return array<string,mixed>
  197.      * @psalm-return Params
  198.      * @phpstan-return array<string,mixed>
  199.      */
  200.     public function getParams()
  201.     {
  202.         return $this->params;
  203.     }
  204.     /**
  205.      * Gets the name of the database this Connection is connected to.
  206.      *
  207.      * @return string
  208.      */
  209.     public function getDatabase()
  210.     {
  211.         return $this->_driver->getDatabase($this);
  212.     }
  213.     /**
  214.      * Gets the hostname of the currently connected database.
  215.      *
  216.      * @deprecated
  217.      *
  218.      * @return string|null
  219.      */
  220.     public function getHost()
  221.     {
  222.         Deprecation::trigger(
  223.             'doctrine/dbal',
  224.             'https://github.com/doctrine/dbal/issues/3580',
  225.             'Connection::getHost() is deprecated, get the database server host from application config ' .
  226.             'or as a last resort from internal Connection::getParams() API.'
  227.         );
  228.         return $this->params['host'] ?? null;
  229.     }
  230.     /**
  231.      * Gets the port of the currently connected database.
  232.      *
  233.      * @deprecated
  234.      *
  235.      * @return mixed
  236.      */
  237.     public function getPort()
  238.     {
  239.         Deprecation::trigger(
  240.             'doctrine/dbal',
  241.             'https://github.com/doctrine/dbal/issues/3580',
  242.             'Connection::getPort() is deprecated, get the database server port from application config ' .
  243.             'or as a last resort from internal Connection::getParams() API.'
  244.         );
  245.         return $this->params['port'] ?? null;
  246.     }
  247.     /**
  248.      * Gets the username used by this connection.
  249.      *
  250.      * @deprecated
  251.      *
  252.      * @return string|null
  253.      */
  254.     public function getUsername()
  255.     {
  256.         Deprecation::trigger(
  257.             'doctrine/dbal',
  258.             'https://github.com/doctrine/dbal/issues/3580',
  259.             'Connection::getUsername() is deprecated, get the username from application config ' .
  260.             'or as a last resort from internal Connection::getParams() API.'
  261.         );
  262.         return $this->params['user'] ?? null;
  263.     }
  264.     /**
  265.      * Gets the password used by this connection.
  266.      *
  267.      * @deprecated
  268.      *
  269.      * @return string|null
  270.      */
  271.     public function getPassword()
  272.     {
  273.         Deprecation::trigger(
  274.             'doctrine/dbal',
  275.             'https://github.com/doctrine/dbal/issues/3580',
  276.             'Connection::getPassword() is deprecated, get the password from application config ' .
  277.             'or as a last resort from internal Connection::getParams() API.'
  278.         );
  279.         return $this->params['password'] ?? null;
  280.     }
  281.     /**
  282.      * Gets the DBAL driver instance.
  283.      *
  284.      * @return Driver
  285.      */
  286.     public function getDriver()
  287.     {
  288.         return $this->_driver;
  289.     }
  290.     /**
  291.      * Gets the Configuration used by the Connection.
  292.      *
  293.      * @return Configuration
  294.      */
  295.     public function getConfiguration()
  296.     {
  297.         return $this->_config;
  298.     }
  299.     /**
  300.      * Gets the EventManager used by the Connection.
  301.      *
  302.      * @return EventManager
  303.      */
  304.     public function getEventManager()
  305.     {
  306.         return $this->_eventManager;
  307.     }
  308.     /**
  309.      * Gets the DatabasePlatform for the connection.
  310.      *
  311.      * @return AbstractPlatform
  312.      *
  313.      * @throws Exception
  314.      */
  315.     public function getDatabasePlatform()
  316.     {
  317.         if ($this->platform === null) {
  318.             $this->platform $this->detectDatabasePlatform();
  319.             $this->platform->setEventManager($this->_eventManager);
  320.         }
  321.         return $this->platform;
  322.     }
  323.     /**
  324.      * Gets the ExpressionBuilder for the connection.
  325.      *
  326.      * @return ExpressionBuilder
  327.      */
  328.     public function getExpressionBuilder()
  329.     {
  330.         return $this->_expr;
  331.     }
  332.     /**
  333.      * Establishes the connection with the database.
  334.      *
  335.      * @return bool TRUE if the connection was successfully established, FALSE if
  336.      *              the connection is already open.
  337.      */
  338.     public function connect()
  339.     {
  340.         if ($this->_conn !== null) {
  341.             return false;
  342.         }
  343.         $driverOptions $this->params['driverOptions'] ?? [];
  344.         $user          $this->params['user'] ?? null;
  345.         $password      $this->params['password'] ?? null;
  346.         $this->_conn $this->_driver->connect($this->params$user$password$driverOptions);
  347.         $this->transactionNestingLevel 0;
  348.         if ($this->autoCommit === false) {
  349.             $this->beginTransaction();
  350.         }
  351.         if ($this->_eventManager->hasListeners(Events::postConnect)) {
  352.             $eventArgs = new Event\ConnectionEventArgs($this);
  353.             $this->_eventManager->dispatchEvent(Events::postConnect$eventArgs);
  354.         }
  355.         return true;
  356.     }
  357.     /**
  358.      * Detects and sets the database platform.
  359.      *
  360.      * Evaluates custom platform class and version in order to set the correct platform.
  361.      *
  362.      * @throws Exception If an invalid platform was specified for this connection.
  363.      */
  364.     private function detectDatabasePlatform(): AbstractPlatform
  365.     {
  366.         $version $this->getDatabasePlatformVersion();
  367.         if ($version !== null) {
  368.             assert($this->_driver instanceof VersionAwarePlatformDriver);
  369.             return $this->_driver->createDatabasePlatformForVersion($version);
  370.         }
  371.         return $this->_driver->getDatabasePlatform();
  372.     }
  373.     /**
  374.      * Returns the version of the related platform if applicable.
  375.      *
  376.      * Returns null if either the driver is not capable to create version
  377.      * specific platform instances, no explicit server version was specified
  378.      * or the underlying driver connection cannot determine the platform
  379.      * version without having to query it (performance reasons).
  380.      *
  381.      * @return string|null
  382.      *
  383.      * @throws Throwable
  384.      */
  385.     private function getDatabasePlatformVersion()
  386.     {
  387.         // Driver does not support version specific platforms.
  388.         if (! $this->_driver instanceof VersionAwarePlatformDriver) {
  389.             return null;
  390.         }
  391.         // Explicit platform version requested (supersedes auto-detection).
  392.         if (isset($this->params['serverVersion'])) {
  393.             return $this->params['serverVersion'];
  394.         }
  395.         // If not connected, we need to connect now to determine the platform version.
  396.         if ($this->_conn === null) {
  397.             try {
  398.                 $this->connect();
  399.             } catch (Throwable $originalException) {
  400.                 if (empty($this->params['dbname'])) {
  401.                     throw $originalException;
  402.                 }
  403.                 // The database to connect to might not yet exist.
  404.                 // Retry detection without database name connection parameter.
  405.                 $params $this->params;
  406.                 unset($this->params['dbname']);
  407.                 try {
  408.                     $this->connect();
  409.                 } catch (Throwable $fallbackException) {
  410.                     // Either the platform does not support database-less connections
  411.                     // or something else went wrong.
  412.                     throw $originalException;
  413.                 } finally {
  414.                     $this->params $params;
  415.                 }
  416.                 $serverVersion $this->getServerVersion();
  417.                 // Close "temporary" connection to allow connecting to the real database again.
  418.                 $this->close();
  419.                 return $serverVersion;
  420.             }
  421.         }
  422.         return $this->getServerVersion();
  423.     }
  424.     /**
  425.      * Returns the database server version if the underlying driver supports it.
  426.      *
  427.      * @return string|null
  428.      */
  429.     private function getServerVersion()
  430.     {
  431.         $connection $this->getWrappedConnection();
  432.         // Automatic platform version detection.
  433.         if ($connection instanceof ServerInfoAwareConnection && ! $connection->requiresQueryForServerVersion()) {
  434.             return $connection->getServerVersion();
  435.         }
  436.         // Unable to detect platform version.
  437.         return null;
  438.     }
  439.     /**
  440.      * Returns the current auto-commit mode for this connection.
  441.      *
  442.      * @see    setAutoCommit
  443.      *
  444.      * @return bool True if auto-commit mode is currently enabled for this connection, false otherwise.
  445.      */
  446.     public function isAutoCommit()
  447.     {
  448.         return $this->autoCommit === true;
  449.     }
  450.     /**
  451.      * Sets auto-commit mode for this connection.
  452.      *
  453.      * If a connection is in auto-commit mode, then all its SQL statements will be executed and committed as individual
  454.      * transactions. Otherwise, its SQL statements are grouped into transactions that are terminated by a call to either
  455.      * the method commit or the method rollback. By default, new connections are in auto-commit mode.
  456.      *
  457.      * NOTE: If this method is called during a transaction and the auto-commit mode is changed, the transaction is
  458.      * committed. If this method is called and the auto-commit mode is not changed, the call is a no-op.
  459.      *
  460.      * @see   isAutoCommit
  461.      *
  462.      * @param bool $autoCommit True to enable auto-commit mode; false to disable it.
  463.      *
  464.      * @return void
  465.      */
  466.     public function setAutoCommit($autoCommit)
  467.     {
  468.         $autoCommit = (bool) $autoCommit;
  469.         // Mode not changed, no-op.
  470.         if ($autoCommit === $this->autoCommit) {
  471.             return;
  472.         }
  473.         $this->autoCommit $autoCommit;
  474.         // Commit all currently active transactions if any when switching auto-commit mode.
  475.         if ($this->_conn === null || $this->transactionNestingLevel === 0) {
  476.             return;
  477.         }
  478.         $this->commitAll();
  479.     }
  480.     /**
  481.      * Sets the fetch mode.
  482.      *
  483.      * @deprecated Use one of the fetch- or iterate-related methods.
  484.      *
  485.      * @param int $fetchMode
  486.      *
  487.      * @return void
  488.      */
  489.     public function setFetchMode($fetchMode)
  490.     {
  491.         Deprecation::trigger(
  492.             'doctrine/dbal',
  493.             'https://github.com/doctrine/dbal/pull/4019',
  494.             'Default Fetch Mode configuration is deprecated, use explicit Connection::fetch*() APIs instead.'
  495.         );
  496.         $this->defaultFetchMode $fetchMode;
  497.     }
  498.     /**
  499.      * Prepares and executes an SQL query and returns the first row of the result
  500.      * as an associative array.
  501.      *
  502.      * @deprecated Use fetchAssociative()
  503.      *
  504.      * @param string                                                               $sql    SQL query
  505.      * @param array<int, mixed>|array<string, mixed>                               $params Query parameters
  506.      * @param array<int, int|string|Type|null>|array<string, int|string|Type|null> $types  Parameter types
  507.      *
  508.      * @return array<string, mixed>|false False is returned if no rows are found.
  509.      *
  510.      * @throws Exception
  511.      */
  512.     public function fetchAssoc($sql, array $params = [], array $types = [])
  513.     {
  514.         Deprecation::trigger(
  515.             'doctrine/dbal',
  516.             'https://github.com/doctrine/dbal/pull/4019',
  517.             'Connection::fetchAssoc() is deprecated, use Connection::fetchAssociative() API instead.'
  518.         );
  519.         return $this->executeQuery($sql$params$types)->fetch(FetchMode::ASSOCIATIVE);
  520.     }
  521.     /**
  522.      * Prepares and executes an SQL query and returns the first row of the result
  523.      * as a numerically indexed array.
  524.      *
  525.      * @deprecated Use fetchNumeric()
  526.      *
  527.      * @param string                                                               $sql    SQL query
  528.      * @param array<int, mixed>|array<string, mixed>                               $params Query parameters
  529.      * @param array<int, int|string|Type|null>|array<string, int|string|Type|null> $types  Parameter types
  530.      *
  531.      * @return array<int, mixed>|false False is returned if no rows are found.
  532.      */
  533.     public function fetchArray($sql, array $params = [], array $types = [])
  534.     {
  535.         Deprecation::trigger(
  536.             'doctrine/dbal',
  537.             'https://github.com/doctrine/dbal/pull/4019',
  538.             'Connection::fetchArray() is deprecated, use Connection::fetchNumeric() API instead.'
  539.         );
  540.         return $this->executeQuery($sql$params$types)->fetch(FetchMode::NUMERIC);
  541.     }
  542.     /**
  543.      * Prepares and executes an SQL query and returns the value of a single column
  544.      * of the first row of the result.
  545.      *
  546.      * @deprecated Use fetchOne() instead.
  547.      *
  548.      * @param string                                                               $sql    SQL query
  549.      * @param array<int, mixed>|array<string, mixed>                               $params Query parameters
  550.      * @param int                                                                  $column 0-indexed column number
  551.      * @param array<int, int|string|Type|null>|array<string, int|string|Type|null> $types  Parameter types
  552.      *
  553.      * @return mixed|false False is returned if no rows are found.
  554.      *
  555.      * @throws Exception
  556.      */
  557.     public function fetchColumn($sql, array $params = [], $column 0, array $types = [])
  558.     {
  559.         Deprecation::trigger(
  560.             'doctrine/dbal',
  561.             'https://github.com/doctrine/dbal/pull/4019',
  562.             'Connection::fetchColumn() is deprecated, use Connection::fetchOne() API instead.'
  563.         );
  564.         return $this->executeQuery($sql$params$types)->fetchColumn($column);
  565.     }
  566.     /**
  567.      * Prepares and executes an SQL query and returns the first row of the result
  568.      * as an associative array.
  569.      *
  570.      * @param string                                                               $query  SQL query
  571.      * @param array<int, mixed>|array<string, mixed>                               $params Query parameters
  572.      * @param array<int, int|string|Type|null>|array<string, int|string|Type|null> $types  Parameter types
  573.      *
  574.      * @return array<string, mixed>|false False is returned if no rows are found.
  575.      *
  576.      * @throws Exception
  577.      */
  578.     public function fetchAssociative(string $query, array $params = [], array $types = [])
  579.     {
  580.         try {
  581.             $stmt $this->ensureForwardCompatibilityStatement(
  582.                 $this->executeQuery($query$params$types)
  583.             );
  584.             return $stmt->fetchAssociative();
  585.         } catch (Throwable $e) {
  586.             $this->handleExceptionDuringQuery($e$query$params$types);
  587.         }
  588.     }
  589.     /**
  590.      * Prepares and executes an SQL query and returns the first row of the result
  591.      * as a numerically indexed array.
  592.      *
  593.      * @param string                                                               $query  SQL query
  594.      * @param array<int, mixed>|array<string, mixed>                               $params Query parameters
  595.      * @param array<int, int|string|Type|null>|array<string, int|string|Type|null> $types  Parameter types
  596.      *
  597.      * @return array<int, mixed>|false False is returned if no rows are found.
  598.      *
  599.      * @throws Exception
  600.      */
  601.     public function fetchNumeric(string $query, array $params = [], array $types = [])
  602.     {
  603.         try {
  604.             $stmt $this->ensureForwardCompatibilityStatement(
  605.                 $this->executeQuery($query$params$types)
  606.             );
  607.             return $stmt->fetchNumeric();
  608.         } catch (Throwable $e) {
  609.             $this->handleExceptionDuringQuery($e$query$params$types);
  610.         }
  611.     }
  612.     /**
  613.      * Prepares and executes an SQL query and returns the value of a single column
  614.      * of the first row of the result.
  615.      *
  616.      * @param string                                                               $query  SQL query
  617.      * @param array<int, mixed>|array<string, mixed>                               $params Query parameters
  618.      * @param array<int, int|string|Type|null>|array<string, int|string|Type|null> $types  Parameter types
  619.      *
  620.      * @return mixed|false False is returned if no rows are found.
  621.      *
  622.      * @throws Exception
  623.      */
  624.     public function fetchOne(string $query, array $params = [], array $types = [])
  625.     {
  626.         try {
  627.             $stmt $this->ensureForwardCompatibilityStatement(
  628.                 $this->executeQuery($query$params$types)
  629.             );
  630.             return $stmt->fetchOne();
  631.         } catch (Throwable $e) {
  632.             $this->handleExceptionDuringQuery($e$query$params$types);
  633.         }
  634.     }
  635.     /**
  636.      * Whether an actual connection to the database is established.
  637.      *
  638.      * @return bool
  639.      */
  640.     public function isConnected()
  641.     {
  642.         return $this->_conn !== null;
  643.     }
  644.     /**
  645.      * Checks whether a transaction is currently active.
  646.      *
  647.      * @return bool TRUE if a transaction is currently active, FALSE otherwise.
  648.      */
  649.     public function isTransactionActive()
  650.     {
  651.         return $this->transactionNestingLevel 0;
  652.     }
  653.     /**
  654.      * Adds condition based on the criteria to the query components
  655.      *
  656.      * @param array<string,mixed> $criteria   Map of key columns to their values
  657.      * @param string[]            $columns    Column names
  658.      * @param mixed[]             $values     Column values
  659.      * @param string[]            $conditions Key conditions
  660.      *
  661.      * @throws Exception
  662.      */
  663.     private function addCriteriaCondition(
  664.         array $criteria,
  665.         array &$columns,
  666.         array &$values,
  667.         array &$conditions
  668.     ): void {
  669.         $platform $this->getDatabasePlatform();
  670.         foreach ($criteria as $columnName => $value) {
  671.             if ($value === null) {
  672.                 $conditions[] = $platform->getIsNullExpression($columnName);
  673.                 continue;
  674.             }
  675.             $columns[]    = $columnName;
  676.             $values[]     = $value;
  677.             $conditions[] = $columnName ' = ?';
  678.         }
  679.     }
  680.     /**
  681.      * Executes an SQL DELETE statement on a table.
  682.      *
  683.      * Table expression and columns are not escaped and are not safe for user-input.
  684.      *
  685.      * @param string                                                               $table    Table name
  686.      * @param array<string, mixed>                                                 $criteria Deletion criteria
  687.      * @param array<int, int|string|Type|null>|array<string, int|string|Type|null> $types    Parameter types
  688.      *
  689.      * @return int The number of affected rows.
  690.      *
  691.      * @throws Exception
  692.      */
  693.     public function delete($table, array $criteria, array $types = [])
  694.     {
  695.         if (empty($criteria)) {
  696.             throw InvalidArgumentException::fromEmptyCriteria();
  697.         }
  698.         $columns $values $conditions = [];
  699.         $this->addCriteriaCondition($criteria$columns$values$conditions);
  700.         return $this->executeStatement(
  701.             'DELETE FROM ' $table ' WHERE ' implode(' AND '$conditions),
  702.             $values,
  703.             is_string(key($types)) ? $this->extractTypeValues($columns$types) : $types
  704.         );
  705.     }
  706.     /**
  707.      * Closes the connection.
  708.      *
  709.      * @return void
  710.      */
  711.     public function close()
  712.     {
  713.         $this->_conn null;
  714.     }
  715.     /**
  716.      * Sets the transaction isolation level.
  717.      *
  718.      * @param int $level The level to set.
  719.      *
  720.      * @return int
  721.      */
  722.     public function setTransactionIsolation($level)
  723.     {
  724.         $this->transactionIsolationLevel $level;
  725.         return $this->executeStatement($this->getDatabasePlatform()->getSetTransactionIsolationSQL($level));
  726.     }
  727.     /**
  728.      * Gets the currently active transaction isolation level.
  729.      *
  730.      * @return int The current transaction isolation level.
  731.      */
  732.     public function getTransactionIsolation()
  733.     {
  734.         if ($this->transactionIsolationLevel === null) {
  735.             $this->transactionIsolationLevel $this->getDatabasePlatform()->getDefaultTransactionIsolationLevel();
  736.         }
  737.         return $this->transactionIsolationLevel;
  738.     }
  739.     /**
  740.      * Executes an SQL UPDATE statement on a table.
  741.      *
  742.      * Table expression and columns are not escaped and are not safe for user-input.
  743.      *
  744.      * @param string                                                               $table    Table name
  745.      * @param array<string, mixed>                                                 $data     Column-value pairs
  746.      * @param array<string, mixed>                                                 $criteria Update criteria
  747.      * @param array<int, int|string|Type|null>|array<string, int|string|Type|null> $types    Parameter types
  748.      *
  749.      * @return int The number of affected rows.
  750.      *
  751.      * @throws Exception
  752.      */
  753.     public function update($table, array $data, array $criteria, array $types = [])
  754.     {
  755.         $columns $values $conditions $set = [];
  756.         foreach ($data as $columnName => $value) {
  757.             $columns[] = $columnName;
  758.             $values[]  = $value;
  759.             $set[]     = $columnName ' = ?';
  760.         }
  761.         $this->addCriteriaCondition($criteria$columns$values$conditions);
  762.         if (is_string(key($types))) {
  763.             $types $this->extractTypeValues($columns$types);
  764.         }
  765.         $sql 'UPDATE ' $table ' SET ' implode(', '$set)
  766.                 . ' WHERE ' implode(' AND '$conditions);
  767.         return $this->executeStatement($sql$values$types);
  768.     }
  769.     /**
  770.      * Inserts a table row with specified data.
  771.      *
  772.      * Table expression and columns are not escaped and are not safe for user-input.
  773.      *
  774.      * @param string                                                               $table Table name
  775.      * @param array<string, mixed>                                                 $data  Column-value pairs
  776.      * @param array<int, int|string|Type|null>|array<string, int|string|Type|null> $types Parameter types
  777.      *
  778.      * @return int The number of affected rows.
  779.      *
  780.      * @throws Exception
  781.      */
  782.     public function insert($table, array $data, array $types = [])
  783.     {
  784.         if (empty($data)) {
  785.             return $this->executeStatement('INSERT INTO ' $table ' () VALUES ()');
  786.         }
  787.         $columns = [];
  788.         $values  = [];
  789.         $set     = [];
  790.         foreach ($data as $columnName => $value) {
  791.             $columns[] = $columnName;
  792.             $values[]  = $value;
  793.             $set[]     = '?';
  794.         }
  795.         return $this->executeStatement(
  796.             'INSERT INTO ' $table ' (' implode(', '$columns) . ')' .
  797.             ' VALUES (' implode(', '$set) . ')',
  798.             $values,
  799.             is_string(key($types)) ? $this->extractTypeValues($columns$types) : $types
  800.         );
  801.     }
  802.     /**
  803.      * Extract ordered type list from an ordered column list and type map.
  804.      *
  805.      * @param array<int, string>                                                   $columnList
  806.      * @param array<int, int|string|Type|null>|array<string, int|string|Type|null> $types
  807.      *
  808.      * @return array<int, int|string|Type|null>|array<string, int|string|Type|null>
  809.      */
  810.     private function extractTypeValues(array $columnList, array $types)
  811.     {
  812.         $typeValues = [];
  813.         foreach ($columnList as $columnIndex => $columnName) {
  814.             $typeValues[] = $types[$columnName] ?? ParameterType::STRING;
  815.         }
  816.         return $typeValues;
  817.     }
  818.     /**
  819.      * Quotes a string so it can be safely used as a table or column name, even if
  820.      * it is a reserved name.
  821.      *
  822.      * Delimiting style depends on the underlying database platform that is being used.
  823.      *
  824.      * NOTE: Just because you CAN use quoted identifiers does not mean
  825.      * you SHOULD use them. In general, they end up causing way more
  826.      * problems than they solve.
  827.      *
  828.      * @param string $str The name to be quoted.
  829.      *
  830.      * @return string The quoted name.
  831.      */
  832.     public function quoteIdentifier($str)
  833.     {
  834.         return $this->getDatabasePlatform()->quoteIdentifier($str);
  835.     }
  836.     /**
  837.      * {@inheritDoc}
  838.      *
  839.      * @param mixed                $value
  840.      * @param int|string|Type|null $type
  841.      */
  842.     public function quote($value$type ParameterType::STRING)
  843.     {
  844.         $connection $this->getWrappedConnection();
  845.         [$value$bindingType] = $this->getBindingInfo($value$type);
  846.         return $connection->quote($value$bindingType);
  847.     }
  848.     /**
  849.      * Prepares and executes an SQL query and returns the result as an associative array.
  850.      *
  851.      * @deprecated Use fetchAllAssociative()
  852.      *
  853.      * @param string         $sql    The SQL query.
  854.      * @param mixed[]        $params The query parameters.
  855.      * @param int[]|string[] $types  The query parameter types.
  856.      *
  857.      * @return mixed[]
  858.      */
  859.     public function fetchAll($sql, array $params = [], $types = [])
  860.     {
  861.         return $this->executeQuery($sql$params$types)->fetchAll();
  862.     }
  863.     /**
  864.      * Prepares and executes an SQL query and returns the result as an array of numeric arrays.
  865.      *
  866.      * @param string                                                               $query  SQL query
  867.      * @param array<int, mixed>|array<string, mixed>                               $params Query parameters
  868.      * @param array<int, int|string|Type|null>|array<string, int|string|Type|null> $types  Parameter types
  869.      *
  870.      * @return array<int,array<int,mixed>>
  871.      *
  872.      * @throws Exception
  873.      */
  874.     public function fetchAllNumeric(string $query, array $params = [], array $types = []): array
  875.     {
  876.         try {
  877.             $stmt $this->ensureForwardCompatibilityStatement(
  878.                 $this->executeQuery($query$params$types)
  879.             );
  880.             return $stmt->fetchAllNumeric();
  881.         } catch (Throwable $e) {
  882.             $this->handleExceptionDuringQuery($e$query$params$types);
  883.         }
  884.     }
  885.     /**
  886.      * Prepares and executes an SQL query and returns the result as an array of associative arrays.
  887.      *
  888.      * @param string                                                               $query  SQL query
  889.      * @param array<int, mixed>|array<string, mixed>                               $params Query parameters
  890.      * @param array<int, int|string|Type|null>|array<string, int|string|Type|null> $types  Parameter types
  891.      *
  892.      * @return array<int,array<string,mixed>>
  893.      *
  894.      * @throws Exception
  895.      */
  896.     public function fetchAllAssociative(string $query, array $params = [], array $types = []): array
  897.     {
  898.         try {
  899.             $stmt $this->ensureForwardCompatibilityStatement(
  900.                 $this->executeQuery($query$params$types)
  901.             );
  902.             return $stmt->fetchAllAssociative();
  903.         } catch (Throwable $e) {
  904.             $this->handleExceptionDuringQuery($e$query$params$types);
  905.         }
  906.     }
  907.     /**
  908.      * Prepares and executes an SQL query and returns the result as an associative array with the keys
  909.      * mapped to the first column and the values mapped to the second column.
  910.      *
  911.      * @param string                                           $query  SQL query
  912.      * @param array<int, mixed>|array<string, mixed>           $params Query parameters
  913.      * @param array<int, int|string>|array<string, int|string> $types  Parameter types
  914.      *
  915.      * @return array<mixed,mixed>
  916.      *
  917.      * @throws Exception
  918.      */
  919.     public function fetchAllKeyValue(string $query, array $params = [], array $types = []): array
  920.     {
  921.         $stmt $this->executeQuery($query$params$types);
  922.         $this->ensureHasKeyValue($stmt);
  923.         $data = [];
  924.         foreach ($stmt->fetchAll(FetchMode::NUMERIC) as [$key$value]) {
  925.             $data[$key] = $value;
  926.         }
  927.         return $data;
  928.     }
  929.     /**
  930.      * Prepares and executes an SQL query and returns the result as an associative array with the keys mapped
  931.      * to the first column and the values being an associative array representing the rest of the columns
  932.      * and their values.
  933.      *
  934.      * @param string                                           $query  SQL query
  935.      * @param array<int, mixed>|array<string, mixed>           $params Query parameters
  936.      * @param array<int, int|string>|array<string, int|string> $types  Parameter types
  937.      *
  938.      * @return array<mixed,array<string,mixed>>
  939.      *
  940.      * @throws Exception
  941.      */
  942.     public function fetchAllAssociativeIndexed(string $query, array $params = [], array $types = []): array
  943.     {
  944.         $stmt $this->executeQuery($query$params$types);
  945.         $data = [];
  946.         foreach ($stmt->fetchAll(FetchMode::ASSOCIATIVE) as $row) {
  947.             $data[array_shift($row)] = $row;
  948.         }
  949.         return $data;
  950.     }
  951.     /**
  952.      * Prepares and executes an SQL query and returns the result as an array of the first column values.
  953.      *
  954.      * @param string                                                               $query  SQL query
  955.      * @param array<int, mixed>|array<string, mixed>                               $params Query parameters
  956.      * @param array<int, int|string|Type|null>|array<string, int|string|Type|null> $types  Parameter types
  957.      *
  958.      * @return array<int,mixed>
  959.      *
  960.      * @throws Exception
  961.      */
  962.     public function fetchFirstColumn(string $query, array $params = [], array $types = []): array
  963.     {
  964.         try {
  965.             $stmt $this->ensureForwardCompatibilityStatement(
  966.                 $this->executeQuery($query$params$types)
  967.             );
  968.             return $stmt->fetchFirstColumn();
  969.         } catch (Throwable $e) {
  970.             $this->handleExceptionDuringQuery($e$query$params$types);
  971.         }
  972.     }
  973.     /**
  974.      * Prepares and executes an SQL query and returns the result as an iterator over rows represented as numeric arrays.
  975.      *
  976.      * @param string                                                               $query  SQL query
  977.      * @param array<int, mixed>|array<string, mixed>                               $params Query parameters
  978.      * @param array<int, int|string|Type|null>|array<string, int|string|Type|null> $types  Parameter types
  979.      *
  980.      * @return Traversable<int,array<int,mixed>>
  981.      *
  982.      * @throws Exception
  983.      */
  984.     public function iterateNumeric(string $query, array $params = [], array $types = []): Traversable
  985.     {
  986.         try {
  987.             $stmt $this->ensureForwardCompatibilityStatement(
  988.                 $this->executeQuery($query$params$types)
  989.             );
  990.             yield from $stmt->iterateNumeric();
  991.         } catch (Throwable $e) {
  992.             $this->handleExceptionDuringQuery($e$query$params$types);
  993.         }
  994.     }
  995.     /**
  996.      * Prepares and executes an SQL query and returns the result as an iterator over rows represented
  997.      * as associative arrays.
  998.      *
  999.      * @param string                                                               $query  SQL query
  1000.      * @param array<int, mixed>|array<string, mixed>                               $params Query parameters
  1001.      * @param array<int, int|string|Type|null>|array<string, int|string|Type|null> $types  Parameter types
  1002.      *
  1003.      * @return Traversable<int,array<string,mixed>>
  1004.      *
  1005.      * @throws Exception
  1006.      */
  1007.     public function iterateAssociative(string $query, array $params = [], array $types = []): Traversable
  1008.     {
  1009.         try {
  1010.             $stmt $this->ensureForwardCompatibilityStatement(
  1011.                 $this->executeQuery($query$params$types)
  1012.             );
  1013.             yield from $stmt->iterateAssociative();
  1014.         } catch (Throwable $e) {
  1015.             $this->handleExceptionDuringQuery($e$query$params$types);
  1016.         }
  1017.     }
  1018.     /**
  1019.      * Prepares and executes an SQL query and returns the result as an iterator with the keys
  1020.      * mapped to the first column and the values mapped to the second column.
  1021.      *
  1022.      * @param string                                           $query  SQL query
  1023.      * @param array<int, mixed>|array<string, mixed>           $params Query parameters
  1024.      * @param array<int, int|string>|array<string, int|string> $types  Parameter types
  1025.      *
  1026.      * @return Traversable<mixed,mixed>
  1027.      *
  1028.      * @throws Exception
  1029.      */
  1030.     public function iterateKeyValue(string $query, array $params = [], array $types = []): Traversable
  1031.     {
  1032.         $stmt $this->executeQuery($query$params$types);
  1033.         $this->ensureHasKeyValue($stmt);
  1034.         while (($row $stmt->fetch(FetchMode::NUMERIC)) !== false) {
  1035.             yield $row[0] => $row[1];
  1036.         }
  1037.     }
  1038.     /**
  1039.      * Prepares and executes an SQL query and returns the result as an iterator with the keys mapped
  1040.      * to the first column and the values being an associative array representing the rest of the columns
  1041.      * and their values.
  1042.      *
  1043.      * @param string                                           $query  SQL query
  1044.      * @param array<int, mixed>|array<string, mixed>           $params Query parameters
  1045.      * @param array<int, int|string>|array<string, int|string> $types  Parameter types
  1046.      *
  1047.      * @return Traversable<mixed,array<string,mixed>>
  1048.      *
  1049.      * @throws Exception
  1050.      */
  1051.     public function iterateAssociativeIndexed(string $query, array $params = [], array $types = []): Traversable
  1052.     {
  1053.         $stmt $this->executeQuery($query$params$types);
  1054.         while (($row $stmt->fetch(FetchMode::ASSOCIATIVE)) !== false) {
  1055.             yield array_shift($row) => $row;
  1056.         }
  1057.     }
  1058.     /**
  1059.      * Prepares and executes an SQL query and returns the result as an iterator over the first column values.
  1060.      *
  1061.      * @param string                                                               $query  SQL query
  1062.      * @param array<int, mixed>|array<string, mixed>                               $params Query parameters
  1063.      * @param array<int, int|string|Type|null>|array<string, int|string|Type|null> $types  Parameter types
  1064.      *
  1065.      * @return Traversable<int,mixed>
  1066.      *
  1067.      * @throws Exception
  1068.      */
  1069.     public function iterateColumn(string $query, array $params = [], array $types = []): Traversable
  1070.     {
  1071.         try {
  1072.             $stmt $this->ensureForwardCompatibilityStatement(
  1073.                 $this->executeQuery($query$params$types)
  1074.             );
  1075.             yield from $stmt->iterateColumn();
  1076.         } catch (Throwable $e) {
  1077.             $this->handleExceptionDuringQuery($e$query$params$types);
  1078.         }
  1079.     }
  1080.     /**
  1081.      * Prepares an SQL statement.
  1082.      *
  1083.      * @param string $sql The SQL statement to prepare.
  1084.      *
  1085.      * @return Statement The prepared statement.
  1086.      *
  1087.      * @throws Exception
  1088.      */
  1089.     public function prepare($sql)
  1090.     {
  1091.         try {
  1092.             $stmt = new Statement($sql$this);
  1093.         } catch (Throwable $e) {
  1094.             $this->handleExceptionDuringQuery($e$sql);
  1095.         }
  1096.         $stmt->setFetchMode($this->defaultFetchMode);
  1097.         return $stmt;
  1098.     }
  1099.     /**
  1100.      * Executes an, optionally parametrized, SQL query.
  1101.      *
  1102.      * If the query is parametrized, a prepared statement is used.
  1103.      * If an SQLLogger is configured, the execution is logged.
  1104.      *
  1105.      * @param string                                                               $sql    SQL query
  1106.      * @param array<int, mixed>|array<string, mixed>                               $params Query parameters
  1107.      * @param array<int, int|string|Type|null>|array<string, int|string|Type|null> $types  Parameter types
  1108.      *
  1109.      * @return ForwardCompatibility\DriverStatement|ForwardCompatibility\DriverResultStatement
  1110.      *
  1111.      * The executed statement or the cached result statement if a query cache profile is used
  1112.      *
  1113.      * @throws Exception
  1114.      */
  1115.     public function executeQuery($sql, array $params = [], $types = [], ?QueryCacheProfile $qcp null)
  1116.     {
  1117.         if ($qcp !== null) {
  1118.             return $this->executeCacheQuery($sql$params$types$qcp);
  1119.         }
  1120.         $connection $this->getWrappedConnection();
  1121.         $logger $this->_config->getSQLLogger();
  1122.         if ($logger) {
  1123.             $logger->startQuery($sql$params$types);
  1124.         }
  1125.         try {
  1126.             if ($params) {
  1127.                 [$sql$params$types] = SQLParserUtils::expandListParameters($sql$params$types);
  1128.                 $stmt $connection->prepare($sql);
  1129.                 if ($types) {
  1130.                     $this->_bindTypedValues($stmt$params$types);
  1131.                     $stmt->execute();
  1132.                 } else {
  1133.                     $stmt->execute($params);
  1134.                 }
  1135.             } else {
  1136.                 $stmt $connection->query($sql);
  1137.             }
  1138.         } catch (Throwable $e) {
  1139.             $this->handleExceptionDuringQuery(
  1140.                 $e,
  1141.                 $sql,
  1142.                 $params,
  1143.                 $types
  1144.             );
  1145.         }
  1146.         $stmt->setFetchMode($this->defaultFetchMode);
  1147.         if ($logger) {
  1148.             $logger->stopQuery();
  1149.         }
  1150.         return $this->ensureForwardCompatibilityStatement($stmt);
  1151.     }
  1152.     /**
  1153.      * Executes a caching query.
  1154.      *
  1155.      * @param string                                                               $sql    SQL query
  1156.      * @param array<int, mixed>|array<string, mixed>                               $params Query parameters
  1157.      * @param array<int, int|string|Type|null>|array<string, int|string|Type|null> $types  Parameter types
  1158.      *
  1159.      * @return ForwardCompatibility\DriverResultStatement
  1160.      *
  1161.      * @throws CacheException
  1162.      */
  1163.     public function executeCacheQuery($sql$params$typesQueryCacheProfile $qcp)
  1164.     {
  1165.         $resultCache $qcp->getResultCacheDriver() ?? $this->_config->getResultCacheImpl();
  1166.         if ($resultCache === null) {
  1167.             throw CacheException::noResultDriverConfigured();
  1168.         }
  1169.         $connectionParams $this->params;
  1170.         unset($connectionParams['platform']);
  1171.         [$cacheKey$realKey] = $qcp->generateCacheKeys($sql$params$types$connectionParams);
  1172.         // fetch the row pointers entry
  1173.         $data $resultCache->fetch($cacheKey);
  1174.         if ($data !== false) {
  1175.             // is the real key part of this row pointers map or is the cache only pointing to other cache keys?
  1176.             if (isset($data[$realKey])) {
  1177.                 $stmt = new ArrayStatement($data[$realKey]);
  1178.             } elseif (array_key_exists($realKey$data)) {
  1179.                 $stmt = new ArrayStatement([]);
  1180.             }
  1181.         }
  1182.         if (! isset($stmt)) {
  1183.             $stmt = new ResultCacheStatement(
  1184.                 $this->executeQuery($sql$params$types),
  1185.                 $resultCache,
  1186.                 $cacheKey,
  1187.                 $realKey,
  1188.                 $qcp->getLifetime()
  1189.             );
  1190.         }
  1191.         $stmt->setFetchMode($this->defaultFetchMode);
  1192.         return $this->ensureForwardCompatibilityStatement($stmt);
  1193.     }
  1194.     /**
  1195.      * @return ForwardCompatibility\Result
  1196.      */
  1197.     private function ensureForwardCompatibilityStatement(ResultStatement $stmt)
  1198.     {
  1199.         return ForwardCompatibility\Result::ensure($stmt);
  1200.     }
  1201.     /**
  1202.      * Executes an, optionally parametrized, SQL query and returns the result,
  1203.      * applying a given projection/transformation function on each row of the result.
  1204.      *
  1205.      * @deprecated
  1206.      *
  1207.      * @param string  $sql      The SQL query to execute.
  1208.      * @param mixed[] $params   The parameters, if any.
  1209.      * @param Closure $function The transformation function that is applied on each row.
  1210.      *                           The function receives a single parameter, an array, that
  1211.      *                           represents a row of the result set.
  1212.      *
  1213.      * @return mixed[] The projected result of the query.
  1214.      */
  1215.     public function project($sql, array $paramsClosure $function)
  1216.     {
  1217.         Deprecation::trigger(
  1218.             'doctrine/dbal',
  1219.             'https://github.com/doctrine/dbal/pull/3823',
  1220.             'Connection::project() is deprecated without replacement, implement data projections in your own code.'
  1221.         );
  1222.         $result = [];
  1223.         $stmt   $this->executeQuery($sql$params);
  1224.         while ($row $stmt->fetch()) {
  1225.             $result[] = $function($row);
  1226.         }
  1227.         $stmt->closeCursor();
  1228.         return $result;
  1229.     }
  1230.     /**
  1231.      * Executes an SQL statement, returning a result set as a Statement object.
  1232.      *
  1233.      * @deprecated Use {@link executeQuery()} instead.
  1234.      *
  1235.      * @return \Doctrine\DBAL\Driver\Statement
  1236.      *
  1237.      * @throws Exception
  1238.      */
  1239.     public function query()
  1240.     {
  1241.         Deprecation::trigger(
  1242.             'doctrine/dbal',
  1243.             'https://github.com/doctrine/dbal/pull/4163',
  1244.             'Connection::query() is deprecated, use Connection::executeQuery() instead.'
  1245.         );
  1246.         $connection $this->getWrappedConnection();
  1247.         $args func_get_args();
  1248.         $logger $this->_config->getSQLLogger();
  1249.         if ($logger) {
  1250.             $logger->startQuery($args[0]);
  1251.         }
  1252.         try {
  1253.             $statement $connection->query(...$args);
  1254.         } catch (Throwable $e) {
  1255.             $this->handleExceptionDuringQuery($e$args[0]);
  1256.         }
  1257.         $statement->setFetchMode($this->defaultFetchMode);
  1258.         if ($logger) {
  1259.             $logger->stopQuery();
  1260.         }
  1261.         return $statement;
  1262.     }
  1263.     /**
  1264.      * Executes an SQL INSERT/UPDATE/DELETE query with the given parameters
  1265.      * and returns the number of affected rows.
  1266.      *
  1267.      * This method supports PDO binding types as well as DBAL mapping types.
  1268.      *
  1269.      * @deprecated Use {@link executeStatement()} instead.
  1270.      *
  1271.      * @param string                                                               $sql    SQL statement
  1272.      * @param array<int, mixed>|array<string, mixed>                               $params Statement parameters
  1273.      * @param array<int, int|string|Type|null>|array<string, int|string|Type|null> $types  Parameter types
  1274.      *
  1275.      * @return int The number of affected rows.
  1276.      *
  1277.      * @throws Exception
  1278.      */
  1279.     public function executeUpdate($sql, array $params = [], array $types = [])
  1280.     {
  1281.         Deprecation::trigger(
  1282.             'doctrine/dbal',
  1283.             'https://github.com/doctrine/dbal/pull/4163',
  1284.             'Connection::executeUpdate() is deprecated, use Connection::executeStatement() instead.'
  1285.         );
  1286.         return $this->executeStatement($sql$params$types);
  1287.     }
  1288.     /**
  1289.      * Executes an SQL statement with the given parameters and returns the number of affected rows.
  1290.      *
  1291.      * Could be used for:
  1292.      *  - DML statements: INSERT, UPDATE, DELETE, etc.
  1293.      *  - DDL statements: CREATE, DROP, ALTER, etc.
  1294.      *  - DCL statements: GRANT, REVOKE, etc.
  1295.      *  - Session control statements: ALTER SESSION, SET, DECLARE, etc.
  1296.      *  - Other statements that don't yield a row set.
  1297.      *
  1298.      * This method supports PDO binding types as well as DBAL mapping types.
  1299.      *
  1300.      * @param string                                                               $sql    SQL statement
  1301.      * @param array<int, mixed>|array<string, mixed>                               $params Statement parameters
  1302.      * @param array<int, int|string|Type|null>|array<string, int|string|Type|null> $types  Parameter types
  1303.      *
  1304.      * @return int The number of affected rows.
  1305.      *
  1306.      * @throws Exception
  1307.      */
  1308.     public function executeStatement($sql, array $params = [], array $types = [])
  1309.     {
  1310.         $connection $this->getWrappedConnection();
  1311.         $logger $this->_config->getSQLLogger();
  1312.         if ($logger) {
  1313.             $logger->startQuery($sql$params$types);
  1314.         }
  1315.         try {
  1316.             if ($params) {
  1317.                 [$sql$params$types] = SQLParserUtils::expandListParameters($sql$params$types);
  1318.                 $stmt $connection->prepare($sql);
  1319.                 if ($types) {
  1320.                     $this->_bindTypedValues($stmt$params$types);
  1321.                     $stmt->execute();
  1322.                 } else {
  1323.                     $stmt->execute($params);
  1324.                 }
  1325.                 $result $stmt->rowCount();
  1326.             } else {
  1327.                 $result $connection->exec($sql);
  1328.             }
  1329.         } catch (Throwable $e) {
  1330.             $this->handleExceptionDuringQuery(
  1331.                 $e,
  1332.                 $sql,
  1333.                 $params,
  1334.                 $types
  1335.             );
  1336.         }
  1337.         if ($logger) {
  1338.             $logger->stopQuery();
  1339.         }
  1340.         return $result;
  1341.     }
  1342.     /**
  1343.      * Executes an SQL statement and return the number of affected rows.
  1344.      *
  1345.      * @deprecated Use {@link executeStatement()} instead.
  1346.      *
  1347.      * @param string $sql
  1348.      *
  1349.      * @return int The number of affected rows.
  1350.      *
  1351.      * @throws Exception
  1352.      */
  1353.     public function exec($sql)
  1354.     {
  1355.         Deprecation::trigger(
  1356.             'doctrine/dbal',
  1357.             'https://github.com/doctrine/dbal/pull/4163',
  1358.             'Connection::exec() is deprecated, use Connection::executeStatement() instead.'
  1359.         );
  1360.         $connection $this->getWrappedConnection();
  1361.         $logger $this->_config->getSQLLogger();
  1362.         if ($logger) {
  1363.             $logger->startQuery($sql);
  1364.         }
  1365.         try {
  1366.             $result $connection->exec($sql);
  1367.         } catch (Throwable $e) {
  1368.             $this->handleExceptionDuringQuery($e$sql);
  1369.         }
  1370.         if ($logger) {
  1371.             $logger->stopQuery();
  1372.         }
  1373.         return $result;
  1374.     }
  1375.     /**
  1376.      * Returns the current transaction nesting level.
  1377.      *
  1378.      * @return int The nesting level. A value of 0 means there's no active transaction.
  1379.      */
  1380.     public function getTransactionNestingLevel()
  1381.     {
  1382.         return $this->transactionNestingLevel;
  1383.     }
  1384.     /**
  1385.      * Fetches the SQLSTATE associated with the last database operation.
  1386.      *
  1387.      * @deprecated The error information is available via exceptions.
  1388.      *
  1389.      * @return string|null The last error code.
  1390.      */
  1391.     public function errorCode()
  1392.     {
  1393.         Deprecation::trigger(
  1394.             'doctrine/dbal',
  1395.             'https://github.com/doctrine/dbal/pull/3507',
  1396.             'Connection::errorCode() is deprecated, use getCode() or getSQLState() on Exception instead.'
  1397.         );
  1398.         return $this->getWrappedConnection()->errorCode();
  1399.     }
  1400.     /**
  1401.      * {@inheritDoc}
  1402.      *
  1403.      * @deprecated The error information is available via exceptions.
  1404.      */
  1405.     public function errorInfo()
  1406.     {
  1407.         Deprecation::trigger(
  1408.             'doctrine/dbal',
  1409.             'https://github.com/doctrine/dbal/pull/3507',
  1410.             'Connection::errorInfo() is deprecated, use getCode() or getSQLState() on Exception instead.'
  1411.         );
  1412.         return $this->getWrappedConnection()->errorInfo();
  1413.     }
  1414.     /**
  1415.      * Returns the ID of the last inserted row, or the last value from a sequence object,
  1416.      * depending on the underlying driver.
  1417.      *
  1418.      * Note: This method may not return a meaningful or consistent result across different drivers,
  1419.      * because the underlying database may not even support the notion of AUTO_INCREMENT/IDENTITY
  1420.      * columns or sequences.
  1421.      *
  1422.      * @param string|null $name Name of the sequence object from which the ID should be returned.
  1423.      *
  1424.      * @return string A string representation of the last inserted ID.
  1425.      */
  1426.     public function lastInsertId($name null)
  1427.     {
  1428.         return $this->getWrappedConnection()->lastInsertId($name);
  1429.     }
  1430.     /**
  1431.      * Executes a function in a transaction.
  1432.      *
  1433.      * The function gets passed this Connection instance as an (optional) parameter.
  1434.      *
  1435.      * If an exception occurs during execution of the function or transaction commit,
  1436.      * the transaction is rolled back and the exception re-thrown.
  1437.      *
  1438.      * @param Closure $func The function to execute transactionally.
  1439.      *
  1440.      * @return mixed The value returned by $func
  1441.      *
  1442.      * @throws Throwable
  1443.      */
  1444.     public function transactional(Closure $func)
  1445.     {
  1446.         $this->beginTransaction();
  1447.         try {
  1448.             $res $func($this);
  1449.             $this->commit();
  1450.             return $res;
  1451.         } catch (Throwable $e) {
  1452.             $this->rollBack();
  1453.             throw $e;
  1454.         }
  1455.     }
  1456.     /**
  1457.      * Sets if nested transactions should use savepoints.
  1458.      *
  1459.      * @param bool $nestTransactionsWithSavepoints
  1460.      *
  1461.      * @return void
  1462.      *
  1463.      * @throws ConnectionException
  1464.      */
  1465.     public function setNestTransactionsWithSavepoints($nestTransactionsWithSavepoints)
  1466.     {
  1467.         if ($this->transactionNestingLevel 0) {
  1468.             throw ConnectionException::mayNotAlterNestedTransactionWithSavepointsInTransaction();
  1469.         }
  1470.         if (! $this->getDatabasePlatform()->supportsSavepoints()) {
  1471.             throw ConnectionException::savepointsNotSupported();
  1472.         }
  1473.         $this->nestTransactionsWithSavepoints = (bool) $nestTransactionsWithSavepoints;
  1474.     }
  1475.     /**
  1476.      * Gets if nested transactions should use savepoints.
  1477.      *
  1478.      * @return bool
  1479.      */
  1480.     public function getNestTransactionsWithSavepoints()
  1481.     {
  1482.         return $this->nestTransactionsWithSavepoints;
  1483.     }
  1484.     /**
  1485.      * Returns the savepoint name to use for nested transactions are false if they are not supported
  1486.      * "savepointFormat" parameter is not set
  1487.      *
  1488.      * @return mixed A string with the savepoint name or false.
  1489.      */
  1490.     protected function _getNestedTransactionSavePointName()
  1491.     {
  1492.         return 'DOCTRINE2_SAVEPOINT_' $this->transactionNestingLevel;
  1493.     }
  1494.     /**
  1495.      * {@inheritDoc}
  1496.      */
  1497.     public function beginTransaction()
  1498.     {
  1499.         $connection $this->getWrappedConnection();
  1500.         ++$this->transactionNestingLevel;
  1501.         $logger $this->_config->getSQLLogger();
  1502.         if ($this->transactionNestingLevel === 1) {
  1503.             if ($logger) {
  1504.                 $logger->startQuery('"START TRANSACTION"');
  1505.             }
  1506.             $connection->beginTransaction();
  1507.             if ($logger) {
  1508.                 $logger->stopQuery();
  1509.             }
  1510.         } elseif ($this->nestTransactionsWithSavepoints) {
  1511.             if ($logger) {
  1512.                 $logger->startQuery('"SAVEPOINT"');
  1513.             }
  1514.             $this->createSavepoint($this->_getNestedTransactionSavePointName());
  1515.             if ($logger) {
  1516.                 $logger->stopQuery();
  1517.             }
  1518.         }
  1519.         return true;
  1520.     }
  1521.     /**
  1522.      * {@inheritDoc}
  1523.      *
  1524.      * @throws ConnectionException If the commit failed due to no active transaction or
  1525.      *                                            because the transaction was marked for rollback only.
  1526.      */
  1527.     public function commit()
  1528.     {
  1529.         if ($this->transactionNestingLevel === 0) {
  1530.             throw ConnectionException::noActiveTransaction();
  1531.         }
  1532.         if ($this->isRollbackOnly) {
  1533.             throw ConnectionException::commitFailedRollbackOnly();
  1534.         }
  1535.         $result true;
  1536.         $connection $this->getWrappedConnection();
  1537.         $logger $this->_config->getSQLLogger();
  1538.         if ($this->transactionNestingLevel === 1) {
  1539.             if ($logger) {
  1540.                 $logger->startQuery('"COMMIT"');
  1541.             }
  1542.             $result $connection->commit();
  1543.             if ($logger) {
  1544.                 $logger->stopQuery();
  1545.             }
  1546.         } elseif ($this->nestTransactionsWithSavepoints) {
  1547.             if ($logger) {
  1548.                 $logger->startQuery('"RELEASE SAVEPOINT"');
  1549.             }
  1550.             $this->releaseSavepoint($this->_getNestedTransactionSavePointName());
  1551.             if ($logger) {
  1552.                 $logger->stopQuery();
  1553.             }
  1554.         }
  1555.         --$this->transactionNestingLevel;
  1556.         if ($this->autoCommit !== false || $this->transactionNestingLevel !== 0) {
  1557.             return $result;
  1558.         }
  1559.         $this->beginTransaction();
  1560.         return $result;
  1561.     }
  1562.     /**
  1563.      * Commits all current nesting transactions.
  1564.      */
  1565.     private function commitAll(): void
  1566.     {
  1567.         while ($this->transactionNestingLevel !== 0) {
  1568.             if ($this->autoCommit === false && $this->transactionNestingLevel === 1) {
  1569.                 // When in no auto-commit mode, the last nesting commit immediately starts a new transaction.
  1570.                 // Therefore we need to do the final commit here and then leave to avoid an infinite loop.
  1571.                 $this->commit();
  1572.                 return;
  1573.             }
  1574.             $this->commit();
  1575.         }
  1576.     }
  1577.     /**
  1578.      * Cancels any database changes done during the current transaction.
  1579.      *
  1580.      * @return bool
  1581.      *
  1582.      * @throws ConnectionException If the rollback operation failed.
  1583.      */
  1584.     public function rollBack()
  1585.     {
  1586.         if ($this->transactionNestingLevel === 0) {
  1587.             throw ConnectionException::noActiveTransaction();
  1588.         }
  1589.         $connection $this->getWrappedConnection();
  1590.         $logger $this->_config->getSQLLogger();
  1591.         if ($this->transactionNestingLevel === 1) {
  1592.             if ($logger) {
  1593.                 $logger->startQuery('"ROLLBACK"');
  1594.             }
  1595.             $this->transactionNestingLevel 0;
  1596.             $connection->rollBack();
  1597.             $this->isRollbackOnly false;
  1598.             if ($logger) {
  1599.                 $logger->stopQuery();
  1600.             }
  1601.             if ($this->autoCommit === false) {
  1602.                 $this->beginTransaction();
  1603.             }
  1604.         } elseif ($this->nestTransactionsWithSavepoints) {
  1605.             if ($logger) {
  1606.                 $logger->startQuery('"ROLLBACK TO SAVEPOINT"');
  1607.             }
  1608.             $this->rollbackSavepoint($this->_getNestedTransactionSavePointName());
  1609.             --$this->transactionNestingLevel;
  1610.             if ($logger) {
  1611.                 $logger->stopQuery();
  1612.             }
  1613.         } else {
  1614.             $this->isRollbackOnly true;
  1615.             --$this->transactionNestingLevel;
  1616.         }
  1617.         return true;
  1618.     }
  1619.     /**
  1620.      * Creates a new savepoint.
  1621.      *
  1622.      * @param string $savepoint The name of the savepoint to create.
  1623.      *
  1624.      * @return void
  1625.      *
  1626.      * @throws ConnectionException
  1627.      */
  1628.     public function createSavepoint($savepoint)
  1629.     {
  1630.         $platform $this->getDatabasePlatform();
  1631.         if (! $platform->supportsSavepoints()) {
  1632.             throw ConnectionException::savepointsNotSupported();
  1633.         }
  1634.         $this->getWrappedConnection()->exec($platform->createSavePoint($savepoint));
  1635.     }
  1636.     /**
  1637.      * Releases the given savepoint.
  1638.      *
  1639.      * @param string $savepoint The name of the savepoint to release.
  1640.      *
  1641.      * @return void
  1642.      *
  1643.      * @throws ConnectionException
  1644.      */
  1645.     public function releaseSavepoint($savepoint)
  1646.     {
  1647.         $platform $this->getDatabasePlatform();
  1648.         if (! $platform->supportsSavepoints()) {
  1649.             throw ConnectionException::savepointsNotSupported();
  1650.         }
  1651.         if (! $platform->supportsReleaseSavepoints()) {
  1652.             return;
  1653.         }
  1654.         $this->getWrappedConnection()->exec($platform->releaseSavePoint($savepoint));
  1655.     }
  1656.     /**
  1657.      * Rolls back to the given savepoint.
  1658.      *
  1659.      * @param string $savepoint The name of the savepoint to rollback to.
  1660.      *
  1661.      * @return void
  1662.      *
  1663.      * @throws ConnectionException
  1664.      */
  1665.     public function rollbackSavepoint($savepoint)
  1666.     {
  1667.         $platform $this->getDatabasePlatform();
  1668.         if (! $platform->supportsSavepoints()) {
  1669.             throw ConnectionException::savepointsNotSupported();
  1670.         }
  1671.         $this->getWrappedConnection()->exec($platform->rollbackSavePoint($savepoint));
  1672.     }
  1673.     /**
  1674.      * Gets the wrapped driver connection.
  1675.      *
  1676.      * @return DriverConnection
  1677.      */
  1678.     public function getWrappedConnection()
  1679.     {
  1680.         $this->connect();
  1681.         assert($this->_conn !== null);
  1682.         return $this->_conn;
  1683.     }
  1684.     /**
  1685.      * Gets the SchemaManager that can be used to inspect or change the
  1686.      * database schema through the connection.
  1687.      *
  1688.      * @return AbstractSchemaManager
  1689.      */
  1690.     public function getSchemaManager()
  1691.     {
  1692.         if ($this->_schemaManager === null) {
  1693.             $this->_schemaManager $this->_driver->getSchemaManager($this);
  1694.         }
  1695.         return $this->_schemaManager;
  1696.     }
  1697.     /**
  1698.      * Marks the current transaction so that the only possible
  1699.      * outcome for the transaction to be rolled back.
  1700.      *
  1701.      * @return void
  1702.      *
  1703.      * @throws ConnectionException If no transaction is active.
  1704.      */
  1705.     public function setRollbackOnly()
  1706.     {
  1707.         if ($this->transactionNestingLevel === 0) {
  1708.             throw ConnectionException::noActiveTransaction();
  1709.         }
  1710.         $this->isRollbackOnly true;
  1711.     }
  1712.     /**
  1713.      * Checks whether the current transaction is marked for rollback only.
  1714.      *
  1715.      * @return bool
  1716.      *
  1717.      * @throws ConnectionException If no transaction is active.
  1718.      */
  1719.     public function isRollbackOnly()
  1720.     {
  1721.         if ($this->transactionNestingLevel === 0) {
  1722.             throw ConnectionException::noActiveTransaction();
  1723.         }
  1724.         return $this->isRollbackOnly;
  1725.     }
  1726.     /**
  1727.      * Converts a given value to its database representation according to the conversion
  1728.      * rules of a specific DBAL mapping type.
  1729.      *
  1730.      * @param mixed  $value The value to convert.
  1731.      * @param string $type  The name of the DBAL mapping type.
  1732.      *
  1733.      * @return mixed The converted value.
  1734.      */
  1735.     public function convertToDatabaseValue($value$type)
  1736.     {
  1737.         return Type::getType($type)->convertToDatabaseValue($value$this->getDatabasePlatform());
  1738.     }
  1739.     /**
  1740.      * Converts a given value to its PHP representation according to the conversion
  1741.      * rules of a specific DBAL mapping type.
  1742.      *
  1743.      * @param mixed  $value The value to convert.
  1744.      * @param string $type  The name of the DBAL mapping type.
  1745.      *
  1746.      * @return mixed The converted type.
  1747.      */
  1748.     public function convertToPHPValue($value$type)
  1749.     {
  1750.         return Type::getType($type)->convertToPHPValue($value$this->getDatabasePlatform());
  1751.     }
  1752.     /**
  1753.      * Binds a set of parameters, some or all of which are typed with a PDO binding type
  1754.      * or DBAL mapping type, to a given statement.
  1755.      *
  1756.      * @internal Duck-typing used on the $stmt parameter to support driver statements as well as
  1757.      *           raw PDOStatement instances.
  1758.      *
  1759.      * @param \Doctrine\DBAL\Driver\Statement                                      $stmt   Prepared statement
  1760.      * @param array<int, mixed>|array<string, mixed>                               $params Statement parameters
  1761.      * @param array<int, int|string|Type|null>|array<string, int|string|Type|null> $types  Parameter types
  1762.      *
  1763.      * @return void
  1764.      */
  1765.     private function _bindTypedValues($stmt, array $params, array $types)
  1766.     {
  1767.         // Check whether parameters are positional or named. Mixing is not allowed, just like in PDO.
  1768.         if (is_int(key($params))) {
  1769.             // Positional parameters
  1770.             $typeOffset array_key_exists(0$types) ? -0;
  1771.             $bindIndex  1;
  1772.             foreach ($params as $value) {
  1773.                 $typeIndex $bindIndex $typeOffset;
  1774.                 if (isset($types[$typeIndex])) {
  1775.                     $type                  $types[$typeIndex];
  1776.                     [$value$bindingType] = $this->getBindingInfo($value$type);
  1777.                     $stmt->bindValue($bindIndex$value$bindingType);
  1778.                 } else {
  1779.                     $stmt->bindValue($bindIndex$value);
  1780.                 }
  1781.                 ++$bindIndex;
  1782.             }
  1783.         } else {
  1784.             // Named parameters
  1785.             foreach ($params as $name => $value) {
  1786.                 if (isset($types[$name])) {
  1787.                     $type                  $types[$name];
  1788.                     [$value$bindingType] = $this->getBindingInfo($value$type);
  1789.                     $stmt->bindValue($name$value$bindingType);
  1790.                 } else {
  1791.                     $stmt->bindValue($name$value);
  1792.                 }
  1793.             }
  1794.         }
  1795.     }
  1796.     /**
  1797.      * Gets the binding type of a given type. The given type can be a PDO or DBAL mapping type.
  1798.      *
  1799.      * @param mixed                $value The value to bind.
  1800.      * @param int|string|Type|null $type  The type to bind (PDO or DBAL).
  1801.      *
  1802.      * @return mixed[] [0] => the (escaped) value, [1] => the binding type.
  1803.      */
  1804.     private function getBindingInfo($value$type)
  1805.     {
  1806.         if (is_string($type)) {
  1807.             $type Type::getType($type);
  1808.         }
  1809.         if ($type instanceof Type) {
  1810.             $value       $type->convertToDatabaseValue($value$this->getDatabasePlatform());
  1811.             $bindingType $type->getBindingType();
  1812.         } else {
  1813.             $bindingType $type;
  1814.         }
  1815.         return [$value$bindingType];
  1816.     }
  1817.     /**
  1818.      * Resolves the parameters to a format which can be displayed.
  1819.      *
  1820.      * @internal This is a purely internal method. If you rely on this method, you are advised to
  1821.      *           copy/paste the code as this method may change, or be removed without prior notice.
  1822.      *
  1823.      * @param array<int, mixed>|array<string, mixed>                               $params Query parameters
  1824.      * @param array<int, int|string|Type|null>|array<string, int|string|Type|null> $types  Parameter types
  1825.      *
  1826.      * @return array<int, int|string|Type|null>|array<string, int|string|Type|null>
  1827.      */
  1828.     public function resolveParams(array $params, array $types)
  1829.     {
  1830.         $resolvedParams = [];
  1831.         // Check whether parameters are positional or named. Mixing is not allowed, just like in PDO.
  1832.         if (is_int(key($params))) {
  1833.             // Positional parameters
  1834.             $typeOffset array_key_exists(0$types) ? -0;
  1835.             $bindIndex  1;
  1836.             foreach ($params as $value) {
  1837.                 $typeIndex $bindIndex $typeOffset;
  1838.                 if (isset($types[$typeIndex])) {
  1839.                     $type                       $types[$typeIndex];
  1840.                     [$value]                    = $this->getBindingInfo($value$type);
  1841.                     $resolvedParams[$bindIndex] = $value;
  1842.                 } else {
  1843.                     $resolvedParams[$bindIndex] = $value;
  1844.                 }
  1845.                 ++$bindIndex;
  1846.             }
  1847.         } else {
  1848.             // Named parameters
  1849.             foreach ($params as $name => $value) {
  1850.                 if (isset($types[$name])) {
  1851.                     $type                  $types[$name];
  1852.                     [$value]               = $this->getBindingInfo($value$type);
  1853.                     $resolvedParams[$name] = $value;
  1854.                 } else {
  1855.                     $resolvedParams[$name] = $value;
  1856.                 }
  1857.             }
  1858.         }
  1859.         return $resolvedParams;
  1860.     }
  1861.     /**
  1862.      * Creates a new instance of a SQL query builder.
  1863.      *
  1864.      * @return QueryBuilder
  1865.      */
  1866.     public function createQueryBuilder()
  1867.     {
  1868.         return new Query\QueryBuilder($this);
  1869.     }
  1870.     /**
  1871.      * Ping the server
  1872.      *
  1873.      * When the server is not available the method returns FALSE.
  1874.      * It is responsibility of the developer to handle this case
  1875.      * and abort the request or reconnect manually:
  1876.      *
  1877.      * @deprecated
  1878.      *
  1879.      * @return bool
  1880.      *
  1881.      * @example
  1882.      *
  1883.      *   if ($conn->ping() === false) {
  1884.      *      $conn->close();
  1885.      *      $conn->connect();
  1886.      *   }
  1887.      *
  1888.      * It is undefined if the underlying driver attempts to reconnect
  1889.      * or disconnect when the connection is not available anymore
  1890.      * as long it returns TRUE when a reconnect succeeded and
  1891.      * FALSE when the connection was dropped.
  1892.      */
  1893.     public function ping()
  1894.     {
  1895.         Deprecation::trigger(
  1896.             'doctrine/dbal',
  1897.             'https://github.com/doctrine/dbal/pull/4119',
  1898.             'Retry and reconnecting lost connections now happens automatically, ping() will be removed in DBAL 3.'
  1899.         );
  1900.         $connection $this->getWrappedConnection();
  1901.         if ($connection instanceof PingableConnection) {
  1902.             return $connection->ping();
  1903.         }
  1904.         try {
  1905.             $this->query($this->getDatabasePlatform()->getDummySelectSQL());
  1906.             return true;
  1907.         } catch (DBALException $e) {
  1908.             return false;
  1909.         }
  1910.     }
  1911.     /**
  1912.      * @internal
  1913.      *
  1914.      * @param array<int, mixed>|array<string, mixed>                               $params
  1915.      * @param array<int, int|string|Type|null>|array<string, int|string|Type|null> $types
  1916.      *
  1917.      * @psalm-return never-return
  1918.      *
  1919.      * @throws Exception
  1920.      */
  1921.     public function handleExceptionDuringQuery(Throwable $estring $sql, array $params = [], array $types = []): void
  1922.     {
  1923.         $this->throw(
  1924.             Exception::driverExceptionDuringQuery(
  1925.                 $this->_driver,
  1926.                 $e,
  1927.                 $sql,
  1928.                 $this->resolveParams($params$types)
  1929.             )
  1930.         );
  1931.     }
  1932.     /**
  1933.      * @internal
  1934.      *
  1935.      * @psalm-return never-return
  1936.      *
  1937.      * @throws Exception
  1938.      */
  1939.     public function handleDriverException(Throwable $e): void
  1940.     {
  1941.         $this->throw(
  1942.             Exception::driverException(
  1943.                 $this->_driver,
  1944.                 $e
  1945.             )
  1946.         );
  1947.     }
  1948.     /**
  1949.      * @internal
  1950.      *
  1951.      * @psalm-return never-return
  1952.      *
  1953.      * @throws Exception
  1954.      */
  1955.     private function throw(Exception $e): void
  1956.     {
  1957.         if ($e instanceof ConnectionLost) {
  1958.             $this->close();
  1959.         }
  1960.         throw $e;
  1961.     }
  1962.     private function ensureHasKeyValue(ResultStatement $stmt): void
  1963.     {
  1964.         $columnCount $stmt->columnCount();
  1965.         if ($columnCount 2) {
  1966.             throw NoKeyValue::fromColumnCount($columnCount);
  1967.         }
  1968.     }
  1969. }