vendor/doctrine/dbal/src/Connection.php line 1700

Open in your IDE?
  1. <?php
  2. namespace Doctrine\DBAL;
  3. use Closure;
  4. use Doctrine\Common\EventManager;
  5. use Doctrine\DBAL\Cache\ArrayResult;
  6. use Doctrine\DBAL\Cache\CacheException;
  7. use Doctrine\DBAL\Cache\QueryCacheProfile;
  8. use Doctrine\DBAL\Driver\API\ExceptionConverter;
  9. use Doctrine\DBAL\Driver\Connection as DriverConnection;
  10. use Doctrine\DBAL\Driver\ServerInfoAwareConnection;
  11. use Doctrine\DBAL\Driver\Statement as DriverStatement;
  12. use Doctrine\DBAL\Event\TransactionBeginEventArgs;
  13. use Doctrine\DBAL\Event\TransactionCommitEventArgs;
  14. use Doctrine\DBAL\Event\TransactionRollBackEventArgs;
  15. use Doctrine\DBAL\Exception\ConnectionLost;
  16. use Doctrine\DBAL\Exception\DriverException;
  17. use Doctrine\DBAL\Exception\InvalidArgumentException;
  18. use Doctrine\DBAL\Platforms\AbstractPlatform;
  19. use Doctrine\DBAL\Query\Expression\ExpressionBuilder;
  20. use Doctrine\DBAL\Query\QueryBuilder;
  21. use Doctrine\DBAL\Schema\AbstractSchemaManager;
  22. use Doctrine\DBAL\SQL\Parser;
  23. use Doctrine\DBAL\Types\Type;
  24. use Doctrine\Deprecations\Deprecation;
  25. use Throwable;
  26. use Traversable;
  27. use function assert;
  28. use function count;
  29. use function get_class;
  30. use function implode;
  31. use function is_int;
  32. use function is_string;
  33. use function key;
  34. /**
  35.  * A database abstraction-level connection that implements features like events, transaction isolation levels,
  36.  * configuration, emulated transaction nesting, lazy connecting and more.
  37.  *
  38.  * @psalm-import-type Params from DriverManager
  39.  */
  40. class Connection
  41. {
  42.     /**
  43.      * Represents an array of ints to be expanded by Doctrine SQL parsing.
  44.      */
  45.     public const PARAM_INT_ARRAY ParameterType::INTEGER self::ARRAY_PARAM_OFFSET;
  46.     /**
  47.      * Represents an array of strings to be expanded by Doctrine SQL parsing.
  48.      */
  49.     public const PARAM_STR_ARRAY ParameterType::STRING self::ARRAY_PARAM_OFFSET;
  50.     /**
  51.      * Offset by which PARAM_* constants are detected as arrays of the param type.
  52.      */
  53.     public const ARRAY_PARAM_OFFSET 100;
  54.     /**
  55.      * The wrapped driver connection.
  56.      *
  57.      * @var \Doctrine\DBAL\Driver\Connection|null
  58.      */
  59.     protected $_conn;
  60.     /** @var Configuration */
  61.     protected $_config;
  62.     /** @var EventManager */
  63.     protected $_eventManager;
  64.     /**
  65.      * @deprecated Use {@see createExpressionBuilder()} instead.
  66.      *
  67.      * @var ExpressionBuilder
  68.      */
  69.     protected $_expr;
  70.     /**
  71.      * The current auto-commit mode of this connection.
  72.      *
  73.      * @var bool
  74.      */
  75.     private $autoCommit true;
  76.     /**
  77.      * The transaction nesting level.
  78.      *
  79.      * @var int
  80.      */
  81.     private $transactionNestingLevel 0;
  82.     /**
  83.      * The currently active transaction isolation level or NULL before it has been determined.
  84.      *
  85.      * @var int|null
  86.      */
  87.     private $transactionIsolationLevel;
  88.     /**
  89.      * If nested transactions should use savepoints.
  90.      *
  91.      * @var bool
  92.      */
  93.     private $nestTransactionsWithSavepoints false;
  94.     /**
  95.      * The parameters used during creation of the Connection instance.
  96.      *
  97.      * @var array<string,mixed>
  98.      * @phpstan-var array<string,mixed>
  99.      * @psalm-var Params
  100.      */
  101.     private $params;
  102.     /**
  103.      * The database platform object used by the connection or NULL before it's initialized.
  104.      *
  105.      * @var AbstractPlatform|null
  106.      */
  107.     private $platform;
  108.     /** @var ExceptionConverter|null */
  109.     private $exceptionConverter;
  110.     /** @var Parser|null */
  111.     private $parser;
  112.     /**
  113.      * The schema manager.
  114.      *
  115.      * @deprecated Use {@see createSchemaManager()} instead.
  116.      *
  117.      * @var AbstractSchemaManager|null
  118.      */
  119.     protected $_schemaManager;
  120.     /**
  121.      * The used DBAL driver.
  122.      *
  123.      * @var Driver
  124.      */
  125.     protected $_driver;
  126.     /**
  127.      * Flag that indicates whether the current transaction is marked for rollback only.
  128.      *
  129.      * @var bool
  130.      */
  131.     private $isRollbackOnly false;
  132.     /**
  133.      * Initializes a new instance of the Connection class.
  134.      *
  135.      * @internal The connection can be only instantiated by the driver manager.
  136.      *
  137.      * @param array<string,mixed> $params       The connection parameters.
  138.      * @param Driver              $driver       The driver to use.
  139.      * @param Configuration|null  $config       The configuration, optional.
  140.      * @param EventManager|null   $eventManager The event manager, optional.
  141.      * @psalm-param Params $params
  142.      * @phpstan-param array<string,mixed> $params
  143.      *
  144.      * @throws Exception
  145.      */
  146.     public function __construct(
  147.         array $params,
  148.         Driver $driver,
  149.         ?Configuration $config null,
  150.         ?EventManager $eventManager null
  151.     ) {
  152.         $this->_driver $driver;
  153.         $this->params  $params;
  154.         if (isset($params['platform'])) {
  155.             if (! $params['platform'] instanceof Platforms\AbstractPlatform) {
  156.                 throw Exception::invalidPlatformType($params['platform']);
  157.             }
  158.             $this->platform $params['platform'];
  159.         }
  160.         // Create default config and event manager if none given
  161.         if ($config === null) {
  162.             $config = new Configuration();
  163.         }
  164.         if ($eventManager === null) {
  165.             $eventManager = new EventManager();
  166.         }
  167.         $this->_config       $config;
  168.         $this->_eventManager $eventManager;
  169.         $this->_expr $this->createExpressionBuilder();
  170.         $this->autoCommit $config->getAutoCommit();
  171.     }
  172.     /**
  173.      * Gets the parameters used during instantiation.
  174.      *
  175.      * @internal
  176.      *
  177.      * @return array<string,mixed>
  178.      * @psalm-return Params
  179.      * @phpstan-return array<string,mixed>
  180.      */
  181.     public function getParams()
  182.     {
  183.         return $this->params;
  184.     }
  185.     /**
  186.      * Gets the name of the currently selected database.
  187.      *
  188.      * @return string|null The name of the database or NULL if a database is not selected.
  189.      *                     The platforms which don't support the concept of a database (e.g. embedded databases)
  190.      *                     must always return a string as an indicator of an implicitly selected database.
  191.      *
  192.      * @throws Exception
  193.      */
  194.     public function getDatabase()
  195.     {
  196.         $platform $this->getDatabasePlatform();
  197.         $query    $platform->getDummySelectSQL($platform->getCurrentDatabaseExpression());
  198.         $database $this->fetchOne($query);
  199.         assert(is_string($database) || $database === null);
  200.         return $database;
  201.     }
  202.     /**
  203.      * Gets the DBAL driver instance.
  204.      *
  205.      * @return Driver
  206.      */
  207.     public function getDriver()
  208.     {
  209.         return $this->_driver;
  210.     }
  211.     /**
  212.      * Gets the Configuration used by the Connection.
  213.      *
  214.      * @return Configuration
  215.      */
  216.     public function getConfiguration()
  217.     {
  218.         return $this->_config;
  219.     }
  220.     /**
  221.      * Gets the EventManager used by the Connection.
  222.      *
  223.      * @return EventManager
  224.      */
  225.     public function getEventManager()
  226.     {
  227.         return $this->_eventManager;
  228.     }
  229.     /**
  230.      * Gets the DatabasePlatform for the connection.
  231.      *
  232.      * @return AbstractPlatform
  233.      *
  234.      * @throws Exception
  235.      */
  236.     public function getDatabasePlatform()
  237.     {
  238.         if ($this->platform === null) {
  239.             $this->platform $this->detectDatabasePlatform();
  240.             $this->platform->setEventManager($this->_eventManager);
  241.         }
  242.         return $this->platform;
  243.     }
  244.     /**
  245.      * Creates an expression builder for the connection.
  246.      */
  247.     public function createExpressionBuilder(): ExpressionBuilder
  248.     {
  249.         return new ExpressionBuilder($this);
  250.     }
  251.     /**
  252.      * Gets the ExpressionBuilder for the connection.
  253.      *
  254.      * @deprecated Use {@see createExpressionBuilder()} instead.
  255.      *
  256.      * @return ExpressionBuilder
  257.      */
  258.     public function getExpressionBuilder()
  259.     {
  260.         Deprecation::triggerIfCalledFromOutside(
  261.             'doctrine/dbal',
  262.             'https://github.com/doctrine/dbal/issues/4515',
  263.             'Connection::getExpressionBuilder() is deprecated,'
  264.                 ' use Connection::createExpressionBuilder() instead.'
  265.         );
  266.         return $this->_expr;
  267.     }
  268.     /**
  269.      * Establishes the connection with the database.
  270.      *
  271.      * @return bool TRUE if the connection was successfully established, FALSE if
  272.      *              the connection is already open.
  273.      *
  274.      * @throws Exception
  275.      */
  276.     public function connect()
  277.     {
  278.         if ($this->_conn !== null) {
  279.             return false;
  280.         }
  281.         try {
  282.             $this->_conn $this->_driver->connect($this->params);
  283.         } catch (Driver\Exception $e) {
  284.             throw $this->convertException($e);
  285.         }
  286.         if ($this->autoCommit === false) {
  287.             $this->beginTransaction();
  288.         }
  289.         if ($this->_eventManager->hasListeners(Events::postConnect)) {
  290.             $eventArgs = new Event\ConnectionEventArgs($this);
  291.             $this->_eventManager->dispatchEvent(Events::postConnect$eventArgs);
  292.         }
  293.         return true;
  294.     }
  295.     /**
  296.      * Detects and sets the database platform.
  297.      *
  298.      * Evaluates custom platform class and version in order to set the correct platform.
  299.      *
  300.      * @throws Exception If an invalid platform was specified for this connection.
  301.      */
  302.     private function detectDatabasePlatform(): AbstractPlatform
  303.     {
  304.         $version $this->getDatabasePlatformVersion();
  305.         if ($version !== null) {
  306.             assert($this->_driver instanceof VersionAwarePlatformDriver);
  307.             return $this->_driver->createDatabasePlatformForVersion($version);
  308.         }
  309.         return $this->_driver->getDatabasePlatform();
  310.     }
  311.     /**
  312.      * Returns the version of the related platform if applicable.
  313.      *
  314.      * Returns null if either the driver is not capable to create version
  315.      * specific platform instances, no explicit server version was specified
  316.      * or the underlying driver connection cannot determine the platform
  317.      * version without having to query it (performance reasons).
  318.      *
  319.      * @return string|null
  320.      *
  321.      * @throws Throwable
  322.      */
  323.     private function getDatabasePlatformVersion()
  324.     {
  325.         // Driver does not support version specific platforms.
  326.         if (! $this->_driver instanceof VersionAwarePlatformDriver) {
  327.             return null;
  328.         }
  329.         // Explicit platform version requested (supersedes auto-detection).
  330.         if (isset($this->params['serverVersion'])) {
  331.             return $this->params['serverVersion'];
  332.         }
  333.         // If not connected, we need to connect now to determine the platform version.
  334.         if ($this->_conn === null) {
  335.             try {
  336.                 $this->connect();
  337.             } catch (Exception $originalException) {
  338.                 if (! isset($this->params['dbname'])) {
  339.                     throw $originalException;
  340.                 }
  341.                 // The database to connect to might not yet exist.
  342.                 // Retry detection without database name connection parameter.
  343.                 $params $this->params;
  344.                 unset($this->params['dbname']);
  345.                 try {
  346.                     $this->connect();
  347.                 } catch (Exception $fallbackException) {
  348.                     // Either the platform does not support database-less connections
  349.                     // or something else went wrong.
  350.                     throw $originalException;
  351.                 } finally {
  352.                     $this->params $params;
  353.                 }
  354.                 $serverVersion $this->getServerVersion();
  355.                 // Close "temporary" connection to allow connecting to the real database again.
  356.                 $this->close();
  357.                 return $serverVersion;
  358.             }
  359.         }
  360.         return $this->getServerVersion();
  361.     }
  362.     /**
  363.      * Returns the database server version if the underlying driver supports it.
  364.      *
  365.      * @return string|null
  366.      *
  367.      * @throws Exception
  368.      */
  369.     private function getServerVersion()
  370.     {
  371.         $connection $this->getWrappedConnection();
  372.         // Automatic platform version detection.
  373.         if ($connection instanceof ServerInfoAwareConnection) {
  374.             try {
  375.                 return $connection->getServerVersion();
  376.             } catch (Driver\Exception $e) {
  377.                 throw $this->convertException($e);
  378.             }
  379.         }
  380.         Deprecation::trigger(
  381.             'doctrine/dbal',
  382.             'https://github.com/doctrine/dbal/pulls/4750',
  383.             'Not implementing the ServerInfoAwareConnection interface in %s is deprecated',
  384.             get_class($connection)
  385.         );
  386.         // Unable to detect platform version.
  387.         return null;
  388.     }
  389.     /**
  390.      * Returns the current auto-commit mode for this connection.
  391.      *
  392.      * @see    setAutoCommit
  393.      *
  394.      * @return bool True if auto-commit mode is currently enabled for this connection, false otherwise.
  395.      */
  396.     public function isAutoCommit()
  397.     {
  398.         return $this->autoCommit === true;
  399.     }
  400.     /**
  401.      * Sets auto-commit mode for this connection.
  402.      *
  403.      * If a connection is in auto-commit mode, then all its SQL statements will be executed and committed as individual
  404.      * transactions. Otherwise, its SQL statements are grouped into transactions that are terminated by a call to either
  405.      * the method commit or the method rollback. By default, new connections are in auto-commit mode.
  406.      *
  407.      * NOTE: If this method is called during a transaction and the auto-commit mode is changed, the transaction is
  408.      * committed. If this method is called and the auto-commit mode is not changed, the call is a no-op.
  409.      *
  410.      * @see   isAutoCommit
  411.      *
  412.      * @param bool $autoCommit True to enable auto-commit mode; false to disable it.
  413.      *
  414.      * @return void
  415.      */
  416.     public function setAutoCommit($autoCommit)
  417.     {
  418.         $autoCommit = (bool) $autoCommit;
  419.         // Mode not changed, no-op.
  420.         if ($autoCommit === $this->autoCommit) {
  421.             return;
  422.         }
  423.         $this->autoCommit $autoCommit;
  424.         // Commit all currently active transactions if any when switching auto-commit mode.
  425.         if ($this->_conn === null || $this->transactionNestingLevel === 0) {
  426.             return;
  427.         }
  428.         $this->commitAll();
  429.     }
  430.     /**
  431.      * Prepares and executes an SQL query and returns the first row of the result
  432.      * as an associative array.
  433.      *
  434.      * @param string                                                               $query  SQL query
  435.      * @param list<mixed>|array<string, mixed>                                     $params Query parameters
  436.      * @param array<int, int|string|Type|null>|array<string, int|string|Type|null> $types  Parameter types
  437.      *
  438.      * @return array<string, mixed>|false False is returned if no rows are found.
  439.      *
  440.      * @throws Exception
  441.      */
  442.     public function fetchAssociative(string $query, array $params = [], array $types = [])
  443.     {
  444.         return $this->executeQuery($query$params$types)->fetchAssociative();
  445.     }
  446.     /**
  447.      * Prepares and executes an SQL query and returns the first row of the result
  448.      * as a numerically indexed array.
  449.      *
  450.      * @param string                                                               $query  SQL query
  451.      * @param list<mixed>|array<string, mixed>                                     $params Query parameters
  452.      * @param array<int, int|string|Type|null>|array<string, int|string|Type|null> $types  Parameter types
  453.      *
  454.      * @return list< mixed>|false False is returned if no rows are found.
  455.      *
  456.      * @throws Exception
  457.      */
  458.     public function fetchNumeric(string $query, array $params = [], array $types = [])
  459.     {
  460.         return $this->executeQuery($query$params$types)->fetchNumeric();
  461.     }
  462.     /**
  463.      * Prepares and executes an SQL query and returns the value of a single column
  464.      * of the first row of the result.
  465.      *
  466.      * @param string                                                               $query  SQL query
  467.      * @param list<mixed>|array<string, mixed>                                     $params Query parameters
  468.      * @param array<int, int|string|Type|null>|array<string, int|string|Type|null> $types  Parameter types
  469.      *
  470.      * @return mixed|false False is returned if no rows are found.
  471.      *
  472.      * @throws Exception
  473.      */
  474.     public function fetchOne(string $query, array $params = [], array $types = [])
  475.     {
  476.         return $this->executeQuery($query$params$types)->fetchOne();
  477.     }
  478.     /**
  479.      * Whether an actual connection to the database is established.
  480.      *
  481.      * @return bool
  482.      */
  483.     public function isConnected()
  484.     {
  485.         return $this->_conn !== null;
  486.     }
  487.     /**
  488.      * Checks whether a transaction is currently active.
  489.      *
  490.      * @return bool TRUE if a transaction is currently active, FALSE otherwise.
  491.      */
  492.     public function isTransactionActive()
  493.     {
  494.         return $this->transactionNestingLevel 0;
  495.     }
  496.     /**
  497.      * Adds condition based on the criteria to the query components
  498.      *
  499.      * @param array<string,mixed> $criteria   Map of key columns to their values
  500.      * @param string[]            $columns    Column names
  501.      * @param mixed[]             $values     Column values
  502.      * @param string[]            $conditions Key conditions
  503.      *
  504.      * @throws Exception
  505.      */
  506.     private function addCriteriaCondition(
  507.         array $criteria,
  508.         array &$columns,
  509.         array &$values,
  510.         array &$conditions
  511.     ): void {
  512.         $platform $this->getDatabasePlatform();
  513.         foreach ($criteria as $columnName => $value) {
  514.             if ($value === null) {
  515.                 $conditions[] = $platform->getIsNullExpression($columnName);
  516.                 continue;
  517.             }
  518.             $columns[]    = $columnName;
  519.             $values[]     = $value;
  520.             $conditions[] = $columnName ' = ?';
  521.         }
  522.     }
  523.     /**
  524.      * Executes an SQL DELETE statement on a table.
  525.      *
  526.      * Table expression and columns are not escaped and are not safe for user-input.
  527.      *
  528.      * @param string                                                               $table    Table name
  529.      * @param array<string, mixed>                                                 $criteria Deletion criteria
  530.      * @param array<int, int|string|Type|null>|array<string, int|string|Type|null> $types    Parameter types
  531.      *
  532.      * @return int The number of affected rows.
  533.      *
  534.      * @throws Exception
  535.      */
  536.     public function delete($table, array $criteria, array $types = [])
  537.     {
  538.         if (count($criteria) === 0) {
  539.             throw InvalidArgumentException::fromEmptyCriteria();
  540.         }
  541.         $columns $values $conditions = [];
  542.         $this->addCriteriaCondition($criteria$columns$values$conditions);
  543.         return $this->executeStatement(
  544.             'DELETE FROM ' $table ' WHERE ' implode(' AND '$conditions),
  545.             $values,
  546.             is_string(key($types)) ? $this->extractTypeValues($columns$types) : $types
  547.         );
  548.     }
  549.     /**
  550.      * Closes the connection.
  551.      *
  552.      * @return void
  553.      */
  554.     public function close()
  555.     {
  556.         $this->_conn                   null;
  557.         $this->transactionNestingLevel 0;
  558.     }
  559.     /**
  560.      * Sets the transaction isolation level.
  561.      *
  562.      * @param int $level The level to set.
  563.      *
  564.      * @return int
  565.      *
  566.      * @throws Exception
  567.      */
  568.     public function setTransactionIsolation($level)
  569.     {
  570.         $this->transactionIsolationLevel $level;
  571.         return $this->executeStatement($this->getDatabasePlatform()->getSetTransactionIsolationSQL($level));
  572.     }
  573.     /**
  574.      * Gets the currently active transaction isolation level.
  575.      *
  576.      * @return int The current transaction isolation level.
  577.      *
  578.      * @throws Exception
  579.      */
  580.     public function getTransactionIsolation()
  581.     {
  582.         if ($this->transactionIsolationLevel === null) {
  583.             $this->transactionIsolationLevel $this->getDatabasePlatform()->getDefaultTransactionIsolationLevel();
  584.         }
  585.         return $this->transactionIsolationLevel;
  586.     }
  587.     /**
  588.      * Executes an SQL UPDATE statement on a table.
  589.      *
  590.      * Table expression and columns are not escaped and are not safe for user-input.
  591.      *
  592.      * @param string                                                               $table    Table name
  593.      * @param array<string, mixed>                                                 $data     Column-value pairs
  594.      * @param array<string, mixed>                                                 $criteria Update criteria
  595.      * @param array<int, int|string|Type|null>|array<string, int|string|Type|null> $types    Parameter types
  596.      *
  597.      * @return int The number of affected rows.
  598.      *
  599.      * @throws Exception
  600.      */
  601.     public function update($table, array $data, array $criteria, array $types = [])
  602.     {
  603.         $columns $values $conditions $set = [];
  604.         foreach ($data as $columnName => $value) {
  605.             $columns[] = $columnName;
  606.             $values[]  = $value;
  607.             $set[]     = $columnName ' = ?';
  608.         }
  609.         $this->addCriteriaCondition($criteria$columns$values$conditions);
  610.         if (is_string(key($types))) {
  611.             $types $this->extractTypeValues($columns$types);
  612.         }
  613.         $sql 'UPDATE ' $table ' SET ' implode(', '$set)
  614.                 . ' WHERE ' implode(' AND '$conditions);
  615.         return $this->executeStatement($sql$values$types);
  616.     }
  617.     /**
  618.      * Inserts a table row with specified data.
  619.      *
  620.      * Table expression and columns are not escaped and are not safe for user-input.
  621.      *
  622.      * @param string                                                               $table Table name
  623.      * @param array<string, mixed>                                                 $data  Column-value pairs
  624.      * @param array<int, int|string|Type|null>|array<string, int|string|Type|null> $types Parameter types
  625.      *
  626.      * @return int The number of affected rows.
  627.      *
  628.      * @throws Exception
  629.      */
  630.     public function insert($table, array $data, array $types = [])
  631.     {
  632.         if (count($data) === 0) {
  633.             return $this->executeStatement('INSERT INTO ' $table ' () VALUES ()');
  634.         }
  635.         $columns = [];
  636.         $values  = [];
  637.         $set     = [];
  638.         foreach ($data as $columnName => $value) {
  639.             $columns[] = $columnName;
  640.             $values[]  = $value;
  641.             $set[]     = '?';
  642.         }
  643.         return $this->executeStatement(
  644.             'INSERT INTO ' $table ' (' implode(', '$columns) . ')' .
  645.             ' VALUES (' implode(', '$set) . ')',
  646.             $values,
  647.             is_string(key($types)) ? $this->extractTypeValues($columns$types) : $types
  648.         );
  649.     }
  650.     /**
  651.      * Extract ordered type list from an ordered column list and type map.
  652.      *
  653.      * @param array<int, string>                                                   $columnList
  654.      * @param array<int, int|string|Type|null>|array<string, int|string|Type|null> $types
  655.      *
  656.      * @return array<int, int|string|Type|null>|array<string, int|string|Type|null>
  657.      */
  658.     private function extractTypeValues(array $columnList, array $types): array
  659.     {
  660.         $typeValues = [];
  661.         foreach ($columnList as $columnName) {
  662.             $typeValues[] = $types[$columnName] ?? ParameterType::STRING;
  663.         }
  664.         return $typeValues;
  665.     }
  666.     /**
  667.      * Quotes a string so it can be safely used as a table or column name, even if
  668.      * it is a reserved name.
  669.      *
  670.      * Delimiting style depends on the underlying database platform that is being used.
  671.      *
  672.      * NOTE: Just because you CAN use quoted identifiers does not mean
  673.      * you SHOULD use them. In general, they end up causing way more
  674.      * problems than they solve.
  675.      *
  676.      * @param string $str The name to be quoted.
  677.      *
  678.      * @return string The quoted name.
  679.      */
  680.     public function quoteIdentifier($str)
  681.     {
  682.         return $this->getDatabasePlatform()->quoteIdentifier($str);
  683.     }
  684.     /**
  685.      * The usage of this method is discouraged. Use prepared statements
  686.      * or {@see AbstractPlatform::quoteStringLiteral()} instead.
  687.      *
  688.      * @param mixed                $value
  689.      * @param int|string|Type|null $type
  690.      *
  691.      * @return mixed
  692.      */
  693.     public function quote($value$type ParameterType::STRING)
  694.     {
  695.         $connection $this->getWrappedConnection();
  696.         [$value$bindingType] = $this->getBindingInfo($value$type);
  697.         return $connection->quote($value$bindingType);
  698.     }
  699.     /**
  700.      * Prepares and executes an SQL query and returns the result as an array of numeric arrays.
  701.      *
  702.      * @param string                                                               $query  SQL query
  703.      * @param list<mixed>|array<string, mixed>                                     $params Query parameters
  704.      * @param array<int, int|string|Type|null>|array<string, int|string|Type|null> $types  Parameter types
  705.      *
  706.      * @return list<list<mixed>>
  707.      *
  708.      * @throws Exception
  709.      */
  710.     public function fetchAllNumeric(string $query, array $params = [], array $types = []): array
  711.     {
  712.         return $this->executeQuery($query$params$types)->fetchAllNumeric();
  713.     }
  714.     /**
  715.      * Prepares and executes an SQL query and returns the result as an array of associative arrays.
  716.      *
  717.      * @param string                                                               $query  SQL query
  718.      * @param list<mixed>|array<string, mixed>                                     $params Query parameters
  719.      * @param array<int, int|string|Type|null>|array<string, int|string|Type|null> $types  Parameter types
  720.      *
  721.      * @return list<array<string,mixed>>
  722.      *
  723.      * @throws Exception
  724.      */
  725.     public function fetchAllAssociative(string $query, array $params = [], array $types = []): array
  726.     {
  727.         return $this->executeQuery($query$params$types)->fetchAllAssociative();
  728.     }
  729.     /**
  730.      * Prepares and executes an SQL query and returns the result as an associative array with the keys
  731.      * mapped to the first column and the values mapped to the second column.
  732.      *
  733.      * @param string                                                               $query  SQL query
  734.      * @param list<mixed>|array<string, mixed>                                     $params Query parameters
  735.      * @param array<int, int|string|Type|null>|array<string, int|string|Type|null> $types  Parameter types
  736.      *
  737.      * @return array<mixed,mixed>
  738.      *
  739.      * @throws Exception
  740.      */
  741.     public function fetchAllKeyValue(string $query, array $params = [], array $types = []): array
  742.     {
  743.         return $this->executeQuery($query$params$types)->fetchAllKeyValue();
  744.     }
  745.     /**
  746.      * Prepares and executes an SQL query and returns the result as an associative array with the keys mapped
  747.      * to the first column and the values being an associative array representing the rest of the columns
  748.      * and their values.
  749.      *
  750.      * @param string                                                               $query  SQL query
  751.      * @param list<mixed>|array<string, mixed>                                     $params Query parameters
  752.      * @param array<int, int|string|Type|null>|array<string, int|string|Type|null> $types  Parameter types
  753.      *
  754.      * @return array<mixed,array<string,mixed>>
  755.      *
  756.      * @throws Exception
  757.      */
  758.     public function fetchAllAssociativeIndexed(string $query, array $params = [], array $types = []): array
  759.     {
  760.         return $this->executeQuery($query$params$types)->fetchAllAssociativeIndexed();
  761.     }
  762.     /**
  763.      * Prepares and executes an SQL query and returns the result as an array of the first column values.
  764.      *
  765.      * @param string                                                               $query  SQL query
  766.      * @param list<mixed>|array<string, mixed>                                     $params Query parameters
  767.      * @param array<int, int|string|Type|null>|array<string, int|string|Type|null> $types  Parameter types
  768.      *
  769.      * @return list<mixed>
  770.      *
  771.      * @throws Exception
  772.      */
  773.     public function fetchFirstColumn(string $query, array $params = [], array $types = []): array
  774.     {
  775.         return $this->executeQuery($query$params$types)->fetchFirstColumn();
  776.     }
  777.     /**
  778.      * Prepares and executes an SQL query and returns the result as an iterator over rows represented as numeric arrays.
  779.      *
  780.      * @param string                                                               $query  SQL query
  781.      * @param list<mixed>|array<string, mixed>                                     $params Query parameters
  782.      * @param array<int, int|string|Type|null>|array<string, int|string|Type|null> $types  Parameter types
  783.      *
  784.      * @return Traversable<int,list<mixed>>
  785.      *
  786.      * @throws Exception
  787.      */
  788.     public function iterateNumeric(string $query, array $params = [], array $types = []): Traversable
  789.     {
  790.         return $this->executeQuery($query$params$types)->iterateNumeric();
  791.     }
  792.     /**
  793.      * Prepares and executes an SQL query and returns the result as an iterator over rows represented
  794.      * as associative arrays.
  795.      *
  796.      * @param string                                                               $query  SQL query
  797.      * @param list<mixed>|array<string, mixed>                                     $params Query parameters
  798.      * @param array<int, int|string|Type|null>|array<string, int|string|Type|null> $types  Parameter types
  799.      *
  800.      * @return Traversable<int,array<string,mixed>>
  801.      *
  802.      * @throws Exception
  803.      */
  804.     public function iterateAssociative(string $query, array $params = [], array $types = []): Traversable
  805.     {
  806.         return $this->executeQuery($query$params$types)->iterateAssociative();
  807.     }
  808.     /**
  809.      * Prepares and executes an SQL query and returns the result as an iterator with the keys
  810.      * mapped to the first column and the values mapped to the second column.
  811.      *
  812.      * @param string                                                               $query  SQL query
  813.      * @param list<mixed>|array<string, mixed>                                     $params Query parameters
  814.      * @param array<int, int|string|Type|null>|array<string, int|string|Type|null> $types  Parameter types
  815.      *
  816.      * @return Traversable<mixed,mixed>
  817.      *
  818.      * @throws Exception
  819.      */
  820.     public function iterateKeyValue(string $query, array $params = [], array $types = []): Traversable
  821.     {
  822.         return $this->executeQuery($query$params$types)->iterateKeyValue();
  823.     }
  824.     /**
  825.      * Prepares and executes an SQL query and returns the result as an iterator with the keys mapped
  826.      * to the first column and the values being an associative array representing the rest of the columns
  827.      * and their values.
  828.      *
  829.      * @param string                                           $query  SQL query
  830.      * @param list<mixed>|array<string, mixed>                 $params Query parameters
  831.      * @param array<int, int|string>|array<string, int|string> $types  Parameter types
  832.      *
  833.      * @return Traversable<mixed,array<string,mixed>>
  834.      *
  835.      * @throws Exception
  836.      */
  837.     public function iterateAssociativeIndexed(string $query, array $params = [], array $types = []): Traversable
  838.     {
  839.         return $this->executeQuery($query$params$types)->iterateAssociativeIndexed();
  840.     }
  841.     /**
  842.      * Prepares and executes an SQL query and returns the result as an iterator over the first column values.
  843.      *
  844.      * @param string                                                               $query  SQL query
  845.      * @param list<mixed>|array<string, mixed>                                     $params Query parameters
  846.      * @param array<int, int|string|Type|null>|array<string, int|string|Type|null> $types  Parameter types
  847.      *
  848.      * @return Traversable<int,mixed>
  849.      *
  850.      * @throws Exception
  851.      */
  852.     public function iterateColumn(string $query, array $params = [], array $types = []): Traversable
  853.     {
  854.         return $this->executeQuery($query$params$types)->iterateColumn();
  855.     }
  856.     /**
  857.      * Prepares an SQL statement.
  858.      *
  859.      * @param string $sql The SQL statement to prepare.
  860.      *
  861.      * @throws Exception
  862.      */
  863.     public function prepare(string $sql): Statement
  864.     {
  865.         $connection $this->getWrappedConnection();
  866.         try {
  867.             $statement $connection->prepare($sql);
  868.         } catch (Driver\Exception $e) {
  869.             throw $this->convertExceptionDuringQuery($e$sql);
  870.         }
  871.         return new Statement($this$statement$sql);
  872.     }
  873.     /**
  874.      * Executes an, optionally parametrized, SQL query.
  875.      *
  876.      * If the query is parametrized, a prepared statement is used.
  877.      * If an SQLLogger is configured, the execution is logged.
  878.      *
  879.      * @param string                                                               $sql    SQL query
  880.      * @param list<mixed>|array<string, mixed>                                     $params Query parameters
  881.      * @param array<int, int|string|Type|null>|array<string, int|string|Type|null> $types  Parameter types
  882.      *
  883.      * @throws Exception
  884.      */
  885.     public function executeQuery(
  886.         string $sql,
  887.         array $params = [],
  888.         $types = [],
  889.         ?QueryCacheProfile $qcp null
  890.     ): Result {
  891.         if ($qcp !== null) {
  892.             return $this->executeCacheQuery($sql$params$types$qcp);
  893.         }
  894.         $connection $this->getWrappedConnection();
  895.         $logger $this->_config->getSQLLogger();
  896.         if ($logger !== null) {
  897.             $logger->startQuery($sql$params$types);
  898.         }
  899.         try {
  900.             if (count($params) > 0) {
  901.                 if ($this->needsArrayParameterConversion($params$types)) {
  902.                     [$sql$params$types] = $this->expandArrayParameters($sql$params$types);
  903.                 }
  904.                 $stmt $connection->prepare($sql);
  905.                 if (count($types) > 0) {
  906.                     $this->_bindTypedValues($stmt$params$types);
  907.                     $result $stmt->execute();
  908.                 } else {
  909.                     $result $stmt->execute($params);
  910.                 }
  911.             } else {
  912.                 $result $connection->query($sql);
  913.             }
  914.             return new Result($result$this);
  915.         } catch (Driver\Exception $e) {
  916.             throw $this->convertExceptionDuringQuery($e$sql$params$types);
  917.         } finally {
  918.             if ($logger !== null) {
  919.                 $logger->stopQuery();
  920.             }
  921.         }
  922.     }
  923.     /**
  924.      * Executes a caching query.
  925.      *
  926.      * @param string                                                               $sql    SQL query
  927.      * @param list<mixed>|array<string, mixed>                                     $params Query parameters
  928.      * @param array<int, int|string|Type|null>|array<string, int|string|Type|null> $types  Parameter types
  929.      *
  930.      * @throws CacheException
  931.      * @throws Exception
  932.      */
  933.     public function executeCacheQuery($sql$params$typesQueryCacheProfile $qcp): Result
  934.     {
  935.         $resultCache $qcp->getResultCache() ?? $this->_config->getResultCache();
  936.         if ($resultCache === null) {
  937.             throw CacheException::noResultDriverConfigured();
  938.         }
  939.         $connectionParams $this->params;
  940.         unset($connectionParams['platform']);
  941.         [$cacheKey$realKey] = $qcp->generateCacheKeys($sql$params$types$connectionParams);
  942.         $item $resultCache->getItem($cacheKey);
  943.         if ($item->isHit()) {
  944.             $value $item->get();
  945.             if (isset($value[$realKey])) {
  946.                 return new Result(new ArrayResult($value[$realKey]), $this);
  947.             }
  948.         } else {
  949.             $value = [];
  950.         }
  951.         $data $this->fetchAllAssociative($sql$params$types);
  952.         $value[$realKey] = $data;
  953.         $item->set($value);
  954.         $lifetime $qcp->getLifetime();
  955.         if ($lifetime 0) {
  956.             $item->expiresAfter($lifetime);
  957.         }
  958.         $resultCache->save($item);
  959.         return new Result(new ArrayResult($data), $this);
  960.     }
  961.     /**
  962.      * Executes an SQL statement with the given parameters and returns the number of affected rows.
  963.      *
  964.      * Could be used for:
  965.      *  - DML statements: INSERT, UPDATE, DELETE, etc.
  966.      *  - DDL statements: CREATE, DROP, ALTER, etc.
  967.      *  - DCL statements: GRANT, REVOKE, etc.
  968.      *  - Session control statements: ALTER SESSION, SET, DECLARE, etc.
  969.      *  - Other statements that don't yield a row set.
  970.      *
  971.      * This method supports PDO binding types as well as DBAL mapping types.
  972.      *
  973.      * @param string                                                               $sql    SQL statement
  974.      * @param list<mixed>|array<string, mixed>                                     $params Statement parameters
  975.      * @param array<int, int|string|Type|null>|array<string, int|string|Type|null> $types  Parameter types
  976.      *
  977.      * @return int The number of affected rows.
  978.      *
  979.      * @throws Exception
  980.      */
  981.     public function executeStatement($sql, array $params = [], array $types = [])
  982.     {
  983.         $connection $this->getWrappedConnection();
  984.         $logger $this->_config->getSQLLogger();
  985.         if ($logger !== null) {
  986.             $logger->startQuery($sql$params$types);
  987.         }
  988.         try {
  989.             if (count($params) > 0) {
  990.                 if ($this->needsArrayParameterConversion($params$types)) {
  991.                     [$sql$params$types] = $this->expandArrayParameters($sql$params$types);
  992.                 }
  993.                 $stmt $connection->prepare($sql);
  994.                 if (count($types) > 0) {
  995.                     $this->_bindTypedValues($stmt$params$types);
  996.                     $result $stmt->execute();
  997.                 } else {
  998.                     $result $stmt->execute($params);
  999.                 }
  1000.                 return $result->rowCount();
  1001.             }
  1002.             return $connection->exec($sql);
  1003.         } catch (Driver\Exception $e) {
  1004.             throw $this->convertExceptionDuringQuery($e$sql$params$types);
  1005.         } finally {
  1006.             if ($logger !== null) {
  1007.                 $logger->stopQuery();
  1008.             }
  1009.         }
  1010.     }
  1011.     /**
  1012.      * Returns the current transaction nesting level.
  1013.      *
  1014.      * @return int The nesting level. A value of 0 means there's no active transaction.
  1015.      */
  1016.     public function getTransactionNestingLevel()
  1017.     {
  1018.         return $this->transactionNestingLevel;
  1019.     }
  1020.     /**
  1021.      * Returns the ID of the last inserted row, or the last value from a sequence object,
  1022.      * depending on the underlying driver.
  1023.      *
  1024.      * Note: This method may not return a meaningful or consistent result across different drivers,
  1025.      * because the underlying database may not even support the notion of AUTO_INCREMENT/IDENTITY
  1026.      * columns or sequences.
  1027.      *
  1028.      * @param string|null $name Name of the sequence object from which the ID should be returned.
  1029.      *
  1030.      * @return string|int|false A string representation of the last inserted ID.
  1031.      *
  1032.      * @throws Exception
  1033.      */
  1034.     public function lastInsertId($name null)
  1035.     {
  1036.         if ($name !== null) {
  1037.             Deprecation::trigger(
  1038.                 'doctrine/dbal',
  1039.                 'https://github.com/doctrine/dbal/issues/4687',
  1040.                 'The usage of Connection::lastInsertId() with a sequence name is deprecated.'
  1041.             );
  1042.         }
  1043.         try {
  1044.             return $this->getWrappedConnection()->lastInsertId($name);
  1045.         } catch (Driver\Exception $e) {
  1046.             throw $this->convertException($e);
  1047.         }
  1048.     }
  1049.     /**
  1050.      * Executes a function in a transaction.
  1051.      *
  1052.      * The function gets passed this Connection instance as an (optional) parameter.
  1053.      *
  1054.      * If an exception occurs during execution of the function or transaction commit,
  1055.      * the transaction is rolled back and the exception re-thrown.
  1056.      *
  1057.      * @param Closure $func The function to execute transactionally.
  1058.      *
  1059.      * @return mixed The value returned by $func
  1060.      *
  1061.      * @throws Throwable
  1062.      */
  1063.     public function transactional(Closure $func)
  1064.     {
  1065.         $this->beginTransaction();
  1066.         try {
  1067.             $res $func($this);
  1068.             $this->commit();
  1069.             return $res;
  1070.         } catch (Throwable $e) {
  1071.             $this->rollBack();
  1072.             throw $e;
  1073.         }
  1074.     }
  1075.     /**
  1076.      * Sets if nested transactions should use savepoints.
  1077.      *
  1078.      * @param bool $nestTransactionsWithSavepoints
  1079.      *
  1080.      * @return void
  1081.      *
  1082.      * @throws Exception
  1083.      */
  1084.     public function setNestTransactionsWithSavepoints($nestTransactionsWithSavepoints)
  1085.     {
  1086.         if ($this->transactionNestingLevel 0) {
  1087.             throw ConnectionException::mayNotAlterNestedTransactionWithSavepointsInTransaction();
  1088.         }
  1089.         if (! $this->getDatabasePlatform()->supportsSavepoints()) {
  1090.             throw ConnectionException::savepointsNotSupported();
  1091.         }
  1092.         $this->nestTransactionsWithSavepoints = (bool) $nestTransactionsWithSavepoints;
  1093.     }
  1094.     /**
  1095.      * Gets if nested transactions should use savepoints.
  1096.      *
  1097.      * @return bool
  1098.      */
  1099.     public function getNestTransactionsWithSavepoints()
  1100.     {
  1101.         return $this->nestTransactionsWithSavepoints;
  1102.     }
  1103.     /**
  1104.      * Returns the savepoint name to use for nested transactions.
  1105.      *
  1106.      * @return string
  1107.      */
  1108.     protected function _getNestedTransactionSavePointName()
  1109.     {
  1110.         return 'DOCTRINE2_SAVEPOINT_' $this->transactionNestingLevel;
  1111.     }
  1112.     /**
  1113.      * @return bool
  1114.      *
  1115.      * @throws Exception
  1116.      */
  1117.     public function beginTransaction()
  1118.     {
  1119.         $connection $this->getWrappedConnection();
  1120.         ++$this->transactionNestingLevel;
  1121.         $logger $this->_config->getSQLLogger();
  1122.         if ($this->transactionNestingLevel === 1) {
  1123.             if ($logger !== null) {
  1124.                 $logger->startQuery('"START TRANSACTION"');
  1125.             }
  1126.             $connection->beginTransaction();
  1127.             if ($logger !== null) {
  1128.                 $logger->stopQuery();
  1129.             }
  1130.         } elseif ($this->nestTransactionsWithSavepoints) {
  1131.             if ($logger !== null) {
  1132.                 $logger->startQuery('"SAVEPOINT"');
  1133.             }
  1134.             $this->createSavepoint($this->_getNestedTransactionSavePointName());
  1135.             if ($logger !== null) {
  1136.                 $logger->stopQuery();
  1137.             }
  1138.         }
  1139.         $this->getEventManager()->dispatchEvent(Events::onTransactionBegin, new TransactionBeginEventArgs($this));
  1140.         return true;
  1141.     }
  1142.     /**
  1143.      * @return bool
  1144.      *
  1145.      * @throws Exception
  1146.      */
  1147.     public function commit()
  1148.     {
  1149.         if ($this->transactionNestingLevel === 0) {
  1150.             throw ConnectionException::noActiveTransaction();
  1151.         }
  1152.         if ($this->isRollbackOnly) {
  1153.             throw ConnectionException::commitFailedRollbackOnly();
  1154.         }
  1155.         $result true;
  1156.         $connection $this->getWrappedConnection();
  1157.         $logger $this->_config->getSQLLogger();
  1158.         if ($this->transactionNestingLevel === 1) {
  1159.             if ($logger !== null) {
  1160.                 $logger->startQuery('"COMMIT"');
  1161.             }
  1162.             $result $connection->commit();
  1163.             if ($logger !== null) {
  1164.                 $logger->stopQuery();
  1165.             }
  1166.         } elseif ($this->nestTransactionsWithSavepoints) {
  1167.             if ($logger !== null) {
  1168.                 $logger->startQuery('"RELEASE SAVEPOINT"');
  1169.             }
  1170.             $this->releaseSavepoint($this->_getNestedTransactionSavePointName());
  1171.             if ($logger !== null) {
  1172.                 $logger->stopQuery();
  1173.             }
  1174.         }
  1175.         --$this->transactionNestingLevel;
  1176.         $this->getEventManager()->dispatchEvent(Events::onTransactionCommit, new TransactionCommitEventArgs($this));
  1177.         if ($this->autoCommit !== false || $this->transactionNestingLevel !== 0) {
  1178.             return $result;
  1179.         }
  1180.         $this->beginTransaction();
  1181.         return $result;
  1182.     }
  1183.     /**
  1184.      * Commits all current nesting transactions.
  1185.      *
  1186.      * @throws Exception
  1187.      */
  1188.     private function commitAll(): void
  1189.     {
  1190.         while ($this->transactionNestingLevel !== 0) {
  1191.             if ($this->autoCommit === false && $this->transactionNestingLevel === 1) {
  1192.                 // When in no auto-commit mode, the last nesting commit immediately starts a new transaction.
  1193.                 // Therefore we need to do the final commit here and then leave to avoid an infinite loop.
  1194.                 $this->commit();
  1195.                 return;
  1196.             }
  1197.             $this->commit();
  1198.         }
  1199.     }
  1200.     /**
  1201.      * Cancels any database changes done during the current transaction.
  1202.      *
  1203.      * @return bool
  1204.      *
  1205.      * @throws Exception
  1206.      */
  1207.     public function rollBack()
  1208.     {
  1209.         if ($this->transactionNestingLevel === 0) {
  1210.             throw ConnectionException::noActiveTransaction();
  1211.         }
  1212.         $connection $this->getWrappedConnection();
  1213.         $logger $this->_config->getSQLLogger();
  1214.         if ($this->transactionNestingLevel === 1) {
  1215.             if ($logger !== null) {
  1216.                 $logger->startQuery('"ROLLBACK"');
  1217.             }
  1218.             $this->transactionNestingLevel 0;
  1219.             $connection->rollBack();
  1220.             $this->isRollbackOnly false;
  1221.             if ($logger !== null) {
  1222.                 $logger->stopQuery();
  1223.             }
  1224.             if ($this->autoCommit === false) {
  1225.                 $this->beginTransaction();
  1226.             }
  1227.         } elseif ($this->nestTransactionsWithSavepoints) {
  1228.             if ($logger !== null) {
  1229.                 $logger->startQuery('"ROLLBACK TO SAVEPOINT"');
  1230.             }
  1231.             $this->rollbackSavepoint($this->_getNestedTransactionSavePointName());
  1232.             --$this->transactionNestingLevel;
  1233.             if ($logger !== null) {
  1234.                 $logger->stopQuery();
  1235.             }
  1236.         } else {
  1237.             $this->isRollbackOnly true;
  1238.             --$this->transactionNestingLevel;
  1239.         }
  1240.         $this->getEventManager()->dispatchEvent(Events::onTransactionRollBack, new TransactionRollBackEventArgs($this));
  1241.         return true;
  1242.     }
  1243.     /**
  1244.      * Creates a new savepoint.
  1245.      *
  1246.      * @param string $savepoint The name of the savepoint to create.
  1247.      *
  1248.      * @return void
  1249.      *
  1250.      * @throws Exception
  1251.      */
  1252.     public function createSavepoint($savepoint)
  1253.     {
  1254.         $platform $this->getDatabasePlatform();
  1255.         if (! $platform->supportsSavepoints()) {
  1256.             throw ConnectionException::savepointsNotSupported();
  1257.         }
  1258.         $this->executeStatement($platform->createSavePoint($savepoint));
  1259.     }
  1260.     /**
  1261.      * Releases the given savepoint.
  1262.      *
  1263.      * @param string $savepoint The name of the savepoint to release.
  1264.      *
  1265.      * @return void
  1266.      *
  1267.      * @throws Exception
  1268.      */
  1269.     public function releaseSavepoint($savepoint)
  1270.     {
  1271.         $platform $this->getDatabasePlatform();
  1272.         if (! $platform->supportsSavepoints()) {
  1273.             throw ConnectionException::savepointsNotSupported();
  1274.         }
  1275.         if (! $platform->supportsReleaseSavepoints()) {
  1276.             return;
  1277.         }
  1278.         $this->executeStatement($platform->releaseSavePoint($savepoint));
  1279.     }
  1280.     /**
  1281.      * Rolls back to the given savepoint.
  1282.      *
  1283.      * @param string $savepoint The name of the savepoint to rollback to.
  1284.      *
  1285.      * @return void
  1286.      *
  1287.      * @throws Exception
  1288.      */
  1289.     public function rollbackSavepoint($savepoint)
  1290.     {
  1291.         $platform $this->getDatabasePlatform();
  1292.         if (! $platform->supportsSavepoints()) {
  1293.             throw ConnectionException::savepointsNotSupported();
  1294.         }
  1295.         $this->executeStatement($platform->rollbackSavePoint($savepoint));
  1296.     }
  1297.     /**
  1298.      * Gets the wrapped driver connection.
  1299.      *
  1300.      * @return DriverConnection
  1301.      *
  1302.      * @throws Exception
  1303.      */
  1304.     public function getWrappedConnection()
  1305.     {
  1306.         $this->connect();
  1307.         assert($this->_conn !== null);
  1308.         return $this->_conn;
  1309.     }
  1310.     /**
  1311.      * Creates a SchemaManager that can be used to inspect or change the
  1312.      * database schema through the connection.
  1313.      *
  1314.      * @throws Exception
  1315.      */
  1316.     public function createSchemaManager(): AbstractSchemaManager
  1317.     {
  1318.         return $this->_driver->getSchemaManager(
  1319.             $this,
  1320.             $this->getDatabasePlatform()
  1321.         );
  1322.     }
  1323.     /**
  1324.      * Gets the SchemaManager that can be used to inspect or change the
  1325.      * database schema through the connection.
  1326.      *
  1327.      * @deprecated Use {@see createSchemaManager()} instead.
  1328.      *
  1329.      * @return AbstractSchemaManager
  1330.      *
  1331.      * @throws Exception
  1332.      */
  1333.     public function getSchemaManager()
  1334.     {
  1335.         Deprecation::triggerIfCalledFromOutside(
  1336.             'doctrine/dbal',
  1337.             'https://github.com/doctrine/dbal/issues/4515',
  1338.             'Connection::getSchemaManager() is deprecated, use Connection::createSchemaManager() instead.'
  1339.         );
  1340.         if ($this->_schemaManager === null) {
  1341.             $this->_schemaManager $this->createSchemaManager();
  1342.         }
  1343.         return $this->_schemaManager;
  1344.     }
  1345.     /**
  1346.      * Marks the current transaction so that the only possible
  1347.      * outcome for the transaction to be rolled back.
  1348.      *
  1349.      * @return void
  1350.      *
  1351.      * @throws ConnectionException If no transaction is active.
  1352.      */
  1353.     public function setRollbackOnly()
  1354.     {
  1355.         if ($this->transactionNestingLevel === 0) {
  1356.             throw ConnectionException::noActiveTransaction();
  1357.         }
  1358.         $this->isRollbackOnly true;
  1359.     }
  1360.     /**
  1361.      * Checks whether the current transaction is marked for rollback only.
  1362.      *
  1363.      * @return bool
  1364.      *
  1365.      * @throws ConnectionException If no transaction is active.
  1366.      */
  1367.     public function isRollbackOnly()
  1368.     {
  1369.         if ($this->transactionNestingLevel === 0) {
  1370.             throw ConnectionException::noActiveTransaction();
  1371.         }
  1372.         return $this->isRollbackOnly;
  1373.     }
  1374.     /**
  1375.      * Converts a given value to its database representation according to the conversion
  1376.      * rules of a specific DBAL mapping type.
  1377.      *
  1378.      * @param mixed  $value The value to convert.
  1379.      * @param string $type  The name of the DBAL mapping type.
  1380.      *
  1381.      * @return mixed The converted value.
  1382.      *
  1383.      * @throws Exception
  1384.      */
  1385.     public function convertToDatabaseValue($value$type)
  1386.     {
  1387.         return Type::getType($type)->convertToDatabaseValue($value$this->getDatabasePlatform());
  1388.     }
  1389.     /**
  1390.      * Converts a given value to its PHP representation according to the conversion
  1391.      * rules of a specific DBAL mapping type.
  1392.      *
  1393.      * @param mixed  $value The value to convert.
  1394.      * @param string $type  The name of the DBAL mapping type.
  1395.      *
  1396.      * @return mixed The converted type.
  1397.      *
  1398.      * @throws Exception
  1399.      */
  1400.     public function convertToPHPValue($value$type)
  1401.     {
  1402.         return Type::getType($type)->convertToPHPValue($value$this->getDatabasePlatform());
  1403.     }
  1404.     /**
  1405.      * Binds a set of parameters, some or all of which are typed with a PDO binding type
  1406.      * or DBAL mapping type, to a given statement.
  1407.      *
  1408.      * @param DriverStatement                                                      $stmt   Prepared statement
  1409.      * @param list<mixed>|array<string, mixed>                                     $params Statement parameters
  1410.      * @param array<int, int|string|Type|null>|array<string, int|string|Type|null> $types  Parameter types
  1411.      *
  1412.      * @throws Exception
  1413.      */
  1414.     private function _bindTypedValues(DriverStatement $stmt, array $params, array $types): void
  1415.     {
  1416.         // Check whether parameters are positional or named. Mixing is not allowed.
  1417.         if (is_int(key($params))) {
  1418.             $bindIndex 1;
  1419.             foreach ($params as $key => $value) {
  1420.                 if (isset($types[$key])) {
  1421.                     $type                  $types[$key];
  1422.                     [$value$bindingType] = $this->getBindingInfo($value$type);
  1423.                     $stmt->bindValue($bindIndex$value$bindingType);
  1424.                 } else {
  1425.                     $stmt->bindValue($bindIndex$value);
  1426.                 }
  1427.                 ++$bindIndex;
  1428.             }
  1429.         } else {
  1430.             // Named parameters
  1431.             foreach ($params as $name => $value) {
  1432.                 if (isset($types[$name])) {
  1433.                     $type                  $types[$name];
  1434.                     [$value$bindingType] = $this->getBindingInfo($value$type);
  1435.                     $stmt->bindValue($name$value$bindingType);
  1436.                 } else {
  1437.                     $stmt->bindValue($name$value);
  1438.                 }
  1439.             }
  1440.         }
  1441.     }
  1442.     /**
  1443.      * Gets the binding type of a given type.
  1444.      *
  1445.      * @param mixed                $value The value to bind.
  1446.      * @param int|string|Type|null $type  The type to bind (PDO or DBAL).
  1447.      *
  1448.      * @return array{mixed, int} [0] => the (escaped) value, [1] => the binding type.
  1449.      *
  1450.      * @throws Exception
  1451.      */
  1452.     private function getBindingInfo($value$type): array
  1453.     {
  1454.         if (is_string($type)) {
  1455.             $type Type::getType($type);
  1456.         }
  1457.         if ($type instanceof Type) {
  1458.             $value       $type->convertToDatabaseValue($value$this->getDatabasePlatform());
  1459.             $bindingType $type->getBindingType();
  1460.         } else {
  1461.             $bindingType $type ?? ParameterType::STRING;
  1462.         }
  1463.         return [$value$bindingType];
  1464.     }
  1465.     /**
  1466.      * Creates a new instance of a SQL query builder.
  1467.      *
  1468.      * @return QueryBuilder
  1469.      */
  1470.     public function createQueryBuilder()
  1471.     {
  1472.         return new Query\QueryBuilder($this);
  1473.     }
  1474.     /**
  1475.      * @internal
  1476.      *
  1477.      * @param list<mixed>|array<string, mixed>                                     $params
  1478.      * @param array<int, int|string|Type|null>|array<string, int|string|Type|null> $types
  1479.      */
  1480.     final public function convertExceptionDuringQuery(
  1481.         Driver\Exception $e,
  1482.         string $sql,
  1483.         array $params = [],
  1484.         array $types = []
  1485.     ): DriverException {
  1486.         return $this->handleDriverException($e, new Query($sql$params$types));
  1487.     }
  1488.     /**
  1489.      * @internal
  1490.      */
  1491.     final public function convertException(Driver\Exception $e): DriverException
  1492.     {
  1493.         return $this->handleDriverException($enull);
  1494.     }
  1495.     /**
  1496.      * @param array<int, mixed>|array<string, mixed>                               $params
  1497.      * @param array<int, int|string|Type|null>|array<string, int|string|Type|null> $types
  1498.      *
  1499.      * @return array{string, list<mixed>, array<int,Type|int|string|null>}
  1500.      */
  1501.     private function expandArrayParameters(string $sql, array $params, array $types): array
  1502.     {
  1503.         if ($this->parser === null) {
  1504.             $this->parser $this->getDatabasePlatform()->createSQLParser();
  1505.         }
  1506.         $visitor = new ExpandArrayParameters($params$types);
  1507.         $this->parser->parse($sql$visitor);
  1508.         return [
  1509.             $visitor->getSQL(),
  1510.             $visitor->getParameters(),
  1511.             $visitor->getTypes(),
  1512.         ];
  1513.     }
  1514.     /**
  1515.      * @param array<int, mixed>|array<string, mixed>                               $params
  1516.      * @param array<int, int|string|Type|null>|array<string, int|string|Type|null> $types
  1517.      */
  1518.     private function needsArrayParameterConversion(array $params, array $types): bool
  1519.     {
  1520.         if (is_string(key($params))) {
  1521.             return true;
  1522.         }
  1523.         foreach ($types as $type) {
  1524.             if ($type === self::PARAM_INT_ARRAY || $type === self::PARAM_STR_ARRAY) {
  1525.                 return true;
  1526.             }
  1527.         }
  1528.         return false;
  1529.     }
  1530.     private function handleDriverException(
  1531.         Driver\Exception $driverException,
  1532.         ?Query $query
  1533.     ): DriverException {
  1534.         if ($this->exceptionConverter === null) {
  1535.             $this->exceptionConverter $this->_driver->getExceptionConverter();
  1536.         }
  1537.         $exception $this->exceptionConverter->convert($driverException$query);
  1538.         if ($exception instanceof ConnectionLost) {
  1539.             $this->close();
  1540.         }
  1541.         return $exception;
  1542.     }
  1543.     /**
  1544.      * BC layer for a wide-spread use-case of old DBAL APIs
  1545.      *
  1546.      * @deprecated This API is deprecated and will be removed after 2022
  1547.      *
  1548.      * @param array<mixed>           $params The query parameters
  1549.      * @param array<int|string|null> $types  The parameter types
  1550.      */
  1551.     public function executeUpdate(string $sql, array $params = [], array $types = []): int
  1552.     {
  1553.         return $this->executeStatement($sql$params$types);
  1554.     }
  1555.     /**
  1556.      * BC layer for a wide-spread use-case of old DBAL APIs
  1557.      *
  1558.      * @deprecated This API is deprecated and will be removed after 2022
  1559.      */
  1560.     public function query(string $sql): Result
  1561.     {
  1562.         return $this->executeQuery($sql);
  1563.     }
  1564.     /**
  1565.      * BC layer for a wide-spread use-case of old DBAL APIs
  1566.      *
  1567.      * @deprecated This API is deprecated and will be removed after 2022
  1568.      */
  1569.     public function exec(string $sql): int
  1570.     {
  1571.         return $this->executeStatement($sql);
  1572.     }
  1573. }