Connection.php 29 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025
  1. <?php
  2. /**
  3. * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
  4. * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
  5. *
  6. * Licensed under The MIT License
  7. * For full copyright and license information, please see the LICENSE.txt
  8. * Redistributions of files must retain the above copyright notice.
  9. *
  10. * @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
  11. * @link https://cakephp.org CakePHP(tm) Project
  12. * @since 3.0.0
  13. * @license https://opensource.org/licenses/mit-license.php MIT License
  14. */
  15. namespace Cake\Database;
  16. use Cake\Core\App;
  17. use Cake\Core\Retry\CommandRetry;
  18. use Cake\Database\Exception\MissingConnectionException;
  19. use Cake\Database\Exception\MissingDriverException;
  20. use Cake\Database\Exception\MissingExtensionException;
  21. use Cake\Database\Exception\NestedTransactionRollbackException;
  22. use Cake\Database\Log\LoggedQuery;
  23. use Cake\Database\Log\LoggingStatement;
  24. use Cake\Database\Log\QueryLogger;
  25. use Cake\Database\Retry\ReconnectStrategy;
  26. use Cake\Database\Schema\CachedCollection;
  27. use Cake\Database\Schema\Collection as SchemaCollection;
  28. use Cake\Datasource\ConnectionInterface;
  29. use Cake\Log\Log;
  30. use Exception;
  31. use Throwable;
  32. /**
  33. * Represents a connection with a database server.
  34. */
  35. class Connection implements ConnectionInterface
  36. {
  37. use TypeConverterTrait;
  38. /**
  39. * Contains the configuration params for this connection.
  40. *
  41. * @var array
  42. */
  43. protected $_config;
  44. /**
  45. * Driver object, responsible for creating the real connection
  46. * and provide specific SQL dialect.
  47. *
  48. * @var \Cake\Database\Driver
  49. */
  50. protected $_driver;
  51. /**
  52. * Contains how many nested transactions have been started.
  53. *
  54. * @var int
  55. */
  56. protected $_transactionLevel = 0;
  57. /**
  58. * Whether a transaction is active in this connection.
  59. *
  60. * @var bool
  61. */
  62. protected $_transactionStarted = false;
  63. /**
  64. * Whether this connection can and should use savepoints for nested
  65. * transactions.
  66. *
  67. * @var bool
  68. */
  69. protected $_useSavePoints = false;
  70. /**
  71. * Whether to log queries generated during this connection.
  72. *
  73. * @var bool
  74. */
  75. protected $_logQueries = false;
  76. /**
  77. * Logger object instance.
  78. *
  79. * @var \Cake\Database\Log\QueryLogger|null
  80. */
  81. protected $_logger;
  82. /**
  83. * The schema collection object
  84. *
  85. * @var \Cake\Database\Schema\Collection|null
  86. */
  87. protected $_schemaCollection;
  88. /**
  89. * NestedTransactionRollbackException object instance, will be stored if
  90. * the rollback method is called in some nested transaction.
  91. *
  92. * @var \Cake\Database\Exception\NestedTransactionRollbackException|null
  93. */
  94. protected $nestedTransactionRollbackException;
  95. /**
  96. * Constructor.
  97. *
  98. * ### Available options:
  99. * - `driver` Sort name or FCQN for driver.
  100. * - `log` Boolean indicating whether to use query logging.
  101. * - `name` Connection name.
  102. * - `cacheMetaData` Boolean indicating whether metadata (datasource schemas) should be cached.
  103. * If set to a string it will be used as the name of cache config to use.
  104. * - `cacheKeyPrefix` Custom prefix to use when generation cache keys. Defaults to connection name.
  105. *
  106. * @param array $config Configuration array.
  107. */
  108. public function __construct($config)
  109. {
  110. $this->_config = $config;
  111. $driver = '';
  112. if (!empty($config['driver'])) {
  113. $driver = $config['driver'];
  114. }
  115. $this->setDriver($driver, $config);
  116. if (!empty($config['log'])) {
  117. $this->enableQueryLogging($config['log']);
  118. }
  119. }
  120. /**
  121. * Destructor
  122. *
  123. * Disconnects the driver to release the connection.
  124. */
  125. public function __destruct()
  126. {
  127. if ($this->_transactionStarted && class_exists('Cake\Log\Log')) {
  128. Log::warning('The connection is going to be closed but there is an active transaction.');
  129. }
  130. }
  131. /**
  132. * {@inheritDoc}
  133. */
  134. public function config()
  135. {
  136. return $this->_config;
  137. }
  138. /**
  139. * {@inheritDoc}
  140. */
  141. public function configName()
  142. {
  143. if (empty($this->_config['name'])) {
  144. return '';
  145. }
  146. return $this->_config['name'];
  147. }
  148. /**
  149. * Sets the driver instance. If a string is passed it will be treated
  150. * as a class name and will be instantiated.
  151. *
  152. * @param \Cake\Database\Driver|string $driver The driver instance to use.
  153. * @param array $config Config for a new driver.
  154. * @throws \Cake\Database\Exception\MissingDriverException When a driver class is missing.
  155. * @throws \Cake\Database\Exception\MissingExtensionException When a driver's PHP extension is missing.
  156. * @return $this
  157. */
  158. public function setDriver($driver, $config = [])
  159. {
  160. if (is_string($driver)) {
  161. $className = App::className($driver, 'Database/Driver');
  162. if (!$className || !class_exists($className)) {
  163. throw new MissingDriverException(['driver' => $driver]);
  164. }
  165. $driver = new $className($config);
  166. }
  167. if (!$driver->enabled()) {
  168. throw new MissingExtensionException(['driver' => get_class($driver)]);
  169. }
  170. $this->_driver = $driver;
  171. return $this;
  172. }
  173. /**
  174. * Get the retry wrapper object that is allows recovery from server disconnects
  175. * while performing certain database actions, such as executing a query.
  176. *
  177. * @return \Cake\Core\Retry\CommandRetry The retry wrapper
  178. */
  179. public function getDisconnectRetry()
  180. {
  181. return new CommandRetry(new ReconnectStrategy($this));
  182. }
  183. /**
  184. * Gets the driver instance.
  185. *
  186. * @return \Cake\Database\Driver
  187. */
  188. public function getDriver()
  189. {
  190. return $this->_driver;
  191. }
  192. /**
  193. * Sets the driver instance. If a string is passed it will be treated
  194. * as a class name and will be instantiated.
  195. *
  196. * If no params are passed it will return the current driver instance.
  197. *
  198. * @deprecated 3.4.0 Use setDriver()/getDriver() instead.
  199. * @param \Cake\Database\Driver|string|null $driver The driver instance to use.
  200. * @param array $config Either config for a new driver or null.
  201. * @throws \Cake\Database\Exception\MissingDriverException When a driver class is missing.
  202. * @throws \Cake\Database\Exception\MissingExtensionException When a driver's PHP extension is missing.
  203. * @return \Cake\Database\Driver
  204. */
  205. public function driver($driver = null, $config = [])
  206. {
  207. deprecationWarning('Connection::driver() is deprecated. Use Connection::setDriver()/getDriver() instead.');
  208. if ($driver !== null) {
  209. $this->setDriver($driver, $config);
  210. }
  211. return $this->getDriver();
  212. }
  213. /**
  214. * Connects to the configured database.
  215. *
  216. * @throws \Cake\Database\Exception\MissingConnectionException if credentials are invalid.
  217. * @return bool true, if the connection was already established or the attempt was successful.
  218. */
  219. public function connect()
  220. {
  221. try {
  222. return $this->_driver->connect();
  223. } catch (Exception $e) {
  224. throw new MissingConnectionException(['reason' => $e->getMessage()], null, $e);
  225. }
  226. }
  227. /**
  228. * Disconnects from database server.
  229. *
  230. * @return void
  231. */
  232. public function disconnect()
  233. {
  234. $this->_driver->disconnect();
  235. }
  236. /**
  237. * Returns whether connection to database server was already established.
  238. *
  239. * @return bool
  240. */
  241. public function isConnected()
  242. {
  243. return $this->_driver->isConnected();
  244. }
  245. /**
  246. * Prepares a SQL statement to be executed.
  247. *
  248. * @param string|\Cake\Database\Query $sql The SQL to convert into a prepared statement.
  249. * @return \Cake\Database\StatementInterface
  250. */
  251. public function prepare($sql)
  252. {
  253. return $this->getDisconnectRetry()->run(function () use ($sql) {
  254. $statement = $this->_driver->prepare($sql);
  255. if ($this->_logQueries) {
  256. $statement = $this->_newLogger($statement);
  257. }
  258. return $statement;
  259. });
  260. }
  261. /**
  262. * Executes a query using $params for interpolating values and $types as a hint for each
  263. * those params.
  264. *
  265. * @param string $query SQL to be executed and interpolated with $params
  266. * @param array $params list or associative array of params to be interpolated in $query as values
  267. * @param array $types list or associative array of types to be used for casting values in query
  268. * @return \Cake\Database\StatementInterface executed statement
  269. */
  270. public function execute($query, array $params = [], array $types = [])
  271. {
  272. return $this->getDisconnectRetry()->run(function () use ($query, $params, $types) {
  273. if (!empty($params)) {
  274. $statement = $this->prepare($query);
  275. $statement->bind($params, $types);
  276. $statement->execute();
  277. } else {
  278. $statement = $this->query($query);
  279. }
  280. return $statement;
  281. });
  282. }
  283. /**
  284. * Compiles a Query object into a SQL string according to the dialect for this
  285. * connection's driver
  286. *
  287. * @param \Cake\Database\Query $query The query to be compiled
  288. * @param \Cake\Database\ValueBinder $generator The placeholder generator to use
  289. * @return string
  290. */
  291. public function compileQuery(Query $query, ValueBinder $generator)
  292. {
  293. return $this->getDriver()->compileQuery($query, $generator)[1];
  294. }
  295. /**
  296. * Executes the provided query after compiling it for the specific driver
  297. * dialect and returns the executed Statement object.
  298. *
  299. * @param \Cake\Database\Query $query The query to be executed
  300. * @return \Cake\Database\StatementInterface executed statement
  301. */
  302. public function run(Query $query)
  303. {
  304. return $this->getDisconnectRetry()->run(function () use ($query) {
  305. $statement = $this->prepare($query);
  306. $query->getValueBinder()->attachTo($statement);
  307. $statement->execute();
  308. return $statement;
  309. });
  310. }
  311. /**
  312. * Executes a SQL statement and returns the Statement object as result.
  313. *
  314. * @param string $sql The SQL query to execute.
  315. * @return \Cake\Database\StatementInterface
  316. */
  317. public function query($sql)
  318. {
  319. return $this->getDisconnectRetry()->run(function () use ($sql) {
  320. $statement = $this->prepare($sql);
  321. $statement->execute();
  322. return $statement;
  323. });
  324. }
  325. /**
  326. * Create a new Query instance for this connection.
  327. *
  328. * @return \Cake\Database\Query
  329. */
  330. public function newQuery()
  331. {
  332. return new Query($this);
  333. }
  334. /**
  335. * Sets a Schema\Collection object for this connection.
  336. *
  337. * @param \Cake\Database\Schema\Collection $collection The schema collection object
  338. * @return $this
  339. */
  340. public function setSchemaCollection(SchemaCollection $collection)
  341. {
  342. $this->_schemaCollection = $collection;
  343. return $this;
  344. }
  345. /**
  346. * Gets a Schema\Collection object for this connection.
  347. *
  348. * @return \Cake\Database\Schema\Collection
  349. */
  350. public function getSchemaCollection()
  351. {
  352. if ($this->_schemaCollection !== null) {
  353. return $this->_schemaCollection;
  354. }
  355. if (!empty($this->_config['cacheMetadata'])) {
  356. return $this->_schemaCollection = new CachedCollection($this, $this->_config['cacheMetadata']);
  357. }
  358. return $this->_schemaCollection = new SchemaCollection($this);
  359. }
  360. /**
  361. * Gets or sets a Schema\Collection object for this connection.
  362. *
  363. * @deprecated 3.4.0 Use setSchemaCollection()/getSchemaCollection()
  364. * @param \Cake\Database\Schema\Collection|null $collection The schema collection object
  365. * @return \Cake\Database\Schema\Collection
  366. */
  367. public function schemaCollection(SchemaCollection $collection = null)
  368. {
  369. deprecationWarning(
  370. 'Connection::schemaCollection() is deprecated. ' .
  371. 'Use Connection::setSchemaCollection()/getSchemaCollection() instead.'
  372. );
  373. if ($collection !== null) {
  374. $this->setSchemaCollection($collection);
  375. }
  376. return $this->getSchemaCollection();
  377. }
  378. /**
  379. * Executes an INSERT query on the specified table.
  380. *
  381. * @param string $table the table to insert values in
  382. * @param array $data values to be inserted
  383. * @param array $types list of associative array containing the types to be used for casting
  384. * @return \Cake\Database\StatementInterface
  385. */
  386. public function insert($table, array $data, array $types = [])
  387. {
  388. return $this->getDisconnectRetry()->run(function () use ($table, $data, $types) {
  389. $columns = array_keys($data);
  390. return $this->newQuery()->insert($columns, $types)
  391. ->into($table)
  392. ->values($data)
  393. ->execute();
  394. });
  395. }
  396. /**
  397. * Executes an UPDATE statement on the specified table.
  398. *
  399. * @param string $table the table to update rows from
  400. * @param array $data values to be updated
  401. * @param array $conditions conditions to be set for update statement
  402. * @param array $types list of associative array containing the types to be used for casting
  403. * @return \Cake\Database\StatementInterface
  404. */
  405. public function update($table, array $data, array $conditions = [], $types = [])
  406. {
  407. return $this->getDisconnectRetry()->run(function () use ($table, $data, $conditions, $types) {
  408. return $this->newQuery()->update($table)
  409. ->set($data, $types)
  410. ->where($conditions, $types)
  411. ->execute();
  412. });
  413. }
  414. /**
  415. * Executes a DELETE statement on the specified table.
  416. *
  417. * @param string $table the table to delete rows from
  418. * @param array $conditions conditions to be set for delete statement
  419. * @param array $types list of associative array containing the types to be used for casting
  420. * @return \Cake\Database\StatementInterface
  421. */
  422. public function delete($table, $conditions = [], $types = [])
  423. {
  424. return $this->getDisconnectRetry()->run(function () use ($table, $conditions, $types) {
  425. return $this->newQuery()->delete($table)
  426. ->where($conditions, $types)
  427. ->execute();
  428. });
  429. }
  430. /**
  431. * Starts a new transaction.
  432. *
  433. * @return void
  434. */
  435. public function begin()
  436. {
  437. if (!$this->_transactionStarted) {
  438. if ($this->_logQueries) {
  439. $this->log('BEGIN');
  440. }
  441. $this->getDisconnectRetry()->run(function () {
  442. $this->_driver->beginTransaction();
  443. });
  444. $this->_transactionLevel = 0;
  445. $this->_transactionStarted = true;
  446. $this->nestedTransactionRollbackException = null;
  447. return;
  448. }
  449. $this->_transactionLevel++;
  450. if ($this->isSavePointsEnabled()) {
  451. $this->createSavePoint((string)$this->_transactionLevel);
  452. }
  453. }
  454. /**
  455. * Commits current transaction.
  456. *
  457. * @return bool true on success, false otherwise
  458. */
  459. public function commit()
  460. {
  461. if (!$this->_transactionStarted) {
  462. return false;
  463. }
  464. if ($this->_transactionLevel === 0) {
  465. if ($this->wasNestedTransactionRolledback()) {
  466. $e = $this->nestedTransactionRollbackException;
  467. $this->nestedTransactionRollbackException = null;
  468. throw $e;
  469. }
  470. $this->_transactionStarted = false;
  471. $this->nestedTransactionRollbackException = null;
  472. if ($this->_logQueries) {
  473. $this->log('COMMIT');
  474. }
  475. return $this->_driver->commitTransaction();
  476. }
  477. if ($this->isSavePointsEnabled()) {
  478. $this->releaseSavePoint((string)$this->_transactionLevel);
  479. }
  480. $this->_transactionLevel--;
  481. return true;
  482. }
  483. /**
  484. * Rollback current transaction.
  485. *
  486. * @param bool|null $toBeginning Whether or not the transaction should be rolled back to the
  487. * beginning of it. Defaults to false if using savepoints, or true if not.
  488. * @return bool
  489. */
  490. public function rollback($toBeginning = null)
  491. {
  492. if (!$this->_transactionStarted) {
  493. return false;
  494. }
  495. $useSavePoint = $this->isSavePointsEnabled();
  496. if ($toBeginning === null) {
  497. $toBeginning = !$useSavePoint;
  498. }
  499. if ($this->_transactionLevel === 0 || $toBeginning) {
  500. $this->_transactionLevel = 0;
  501. $this->_transactionStarted = false;
  502. $this->nestedTransactionRollbackException = null;
  503. if ($this->_logQueries) {
  504. $this->log('ROLLBACK');
  505. }
  506. $this->_driver->rollbackTransaction();
  507. return true;
  508. }
  509. $savePoint = $this->_transactionLevel--;
  510. if ($useSavePoint) {
  511. $this->rollbackSavepoint($savePoint);
  512. } elseif ($this->nestedTransactionRollbackException === null) {
  513. $this->nestedTransactionRollbackException = new NestedTransactionRollbackException();
  514. }
  515. return true;
  516. }
  517. /**
  518. * Enables/disables the usage of savepoints, enables only if driver the allows it.
  519. *
  520. * If you are trying to enable this feature, make sure you check the return value of this
  521. * function to verify it was enabled successfully.
  522. *
  523. * ### Example:
  524. *
  525. * `$connection->enableSavePoints(true)` Returns true if drivers supports save points, false otherwise
  526. * `$connection->enableSavePoints(false)` Disables usage of savepoints and returns false
  527. *
  528. * @param bool $enable Whether or not save points should be used.
  529. * @return $this
  530. */
  531. public function enableSavePoints($enable)
  532. {
  533. if ($enable === false) {
  534. $this->_useSavePoints = false;
  535. } else {
  536. $this->_useSavePoints = $this->_driver->supportsSavePoints();
  537. }
  538. return $this;
  539. }
  540. /**
  541. * Disables the usage of savepoints.
  542. *
  543. * @return $this
  544. */
  545. public function disableSavePoints()
  546. {
  547. $this->_useSavePoints = false;
  548. return $this;
  549. }
  550. /**
  551. * Returns whether this connection is using savepoints for nested transactions
  552. *
  553. * @return bool true if enabled, false otherwise
  554. */
  555. public function isSavePointsEnabled()
  556. {
  557. return $this->_useSavePoints;
  558. }
  559. /**
  560. * Returns whether this connection is using savepoints for nested transactions
  561. * If a boolean is passed as argument it will enable/disable the usage of savepoints
  562. * only if driver the allows it.
  563. *
  564. * If you are trying to enable this feature, make sure you check the return value of this
  565. * function to verify it was enabled successfully.
  566. *
  567. * ### Example:
  568. *
  569. * `$connection->useSavePoints(true)` Returns true if drivers supports save points, false otherwise
  570. * `$connection->useSavePoints(false)` Disables usage of savepoints and returns false
  571. * `$connection->useSavePoints()` Returns current status
  572. *
  573. * @deprecated 3.4.0 Use enableSavePoints()/isSavePointsEnabled() instead.
  574. * @param bool|null $enable Whether or not save points should be used.
  575. * @return bool true if enabled, false otherwise
  576. */
  577. public function useSavePoints($enable = null)
  578. {
  579. deprecationWarning(
  580. 'Connection::useSavePoints() is deprecated. ' .
  581. 'Use Connection::enableSavePoints()/isSavePointsEnabled() instead.'
  582. );
  583. if ($enable !== null) {
  584. $this->enableSavePoints($enable);
  585. }
  586. return $this->isSavePointsEnabled();
  587. }
  588. /**
  589. * Creates a new save point for nested transactions.
  590. *
  591. * @param string|int $name The save point name.
  592. * @return void
  593. */
  594. public function createSavePoint($name)
  595. {
  596. $this->execute($this->_driver->savePointSQL($name))->closeCursor();
  597. }
  598. /**
  599. * Releases a save point by its name.
  600. *
  601. * @param string|int $name The save point name.
  602. * @return void
  603. */
  604. public function releaseSavePoint($name)
  605. {
  606. $this->execute($this->_driver->releaseSavePointSQL($name))->closeCursor();
  607. }
  608. /**
  609. * Rollback a save point by its name.
  610. *
  611. * @param string|int $name The save point name.
  612. * @return void
  613. */
  614. public function rollbackSavepoint($name)
  615. {
  616. $this->execute($this->_driver->rollbackSavePointSQL($name))->closeCursor();
  617. }
  618. /**
  619. * Run driver specific SQL to disable foreign key checks.
  620. *
  621. * @return void
  622. */
  623. public function disableForeignKeys()
  624. {
  625. $this->getDisconnectRetry()->run(function () {
  626. $this->execute($this->_driver->disableForeignKeySQL())->closeCursor();
  627. });
  628. }
  629. /**
  630. * Run driver specific SQL to enable foreign key checks.
  631. *
  632. * @return void
  633. */
  634. public function enableForeignKeys()
  635. {
  636. $this->getDisconnectRetry()->run(function () {
  637. $this->execute($this->_driver->enableForeignKeySQL())->closeCursor();
  638. });
  639. }
  640. /**
  641. * Returns whether the driver supports adding or dropping constraints
  642. * to already created tables.
  643. *
  644. * @return bool true if driver supports dynamic constraints
  645. */
  646. public function supportsDynamicConstraints()
  647. {
  648. return $this->_driver->supportsDynamicConstraints();
  649. }
  650. /**
  651. * {@inheritDoc}
  652. *
  653. * ### Example:
  654. *
  655. * ```
  656. * $connection->transactional(function ($connection) {
  657. * $connection->newQuery()->delete('users')->execute();
  658. * });
  659. * ```
  660. */
  661. public function transactional(callable $transaction)
  662. {
  663. $this->begin();
  664. try {
  665. $result = $transaction($this);
  666. } catch (Throwable $e) {
  667. $this->rollback(false);
  668. throw $e;
  669. } catch (Exception $e) {
  670. $this->rollback(false);
  671. throw $e;
  672. }
  673. if ($result === false) {
  674. $this->rollback(false);
  675. return false;
  676. }
  677. try {
  678. $this->commit();
  679. } catch (NestedTransactionRollbackException $e) {
  680. $this->rollback(false);
  681. throw $e;
  682. }
  683. return $result;
  684. }
  685. /**
  686. * Returns whether some nested transaction has been already rolled back.
  687. *
  688. * @return bool
  689. */
  690. protected function wasNestedTransactionRolledback()
  691. {
  692. return $this->nestedTransactionRollbackException instanceof NestedTransactionRollbackException;
  693. }
  694. /**
  695. * {@inheritDoc}
  696. *
  697. * ### Example:
  698. *
  699. * ```
  700. * $connection->disableConstraints(function ($connection) {
  701. * $connection->newQuery()->delete('users')->execute();
  702. * });
  703. * ```
  704. */
  705. public function disableConstraints(callable $operation)
  706. {
  707. return $this->getDisconnectRetry()->run(function () use ($operation) {
  708. $this->disableForeignKeys();
  709. try {
  710. $result = $operation($this);
  711. } catch (Exception $e) {
  712. $this->enableForeignKeys();
  713. throw $e;
  714. }
  715. $this->enableForeignKeys();
  716. return $result;
  717. });
  718. }
  719. /**
  720. * Checks if a transaction is running.
  721. *
  722. * @return bool True if a transaction is running else false.
  723. */
  724. public function inTransaction()
  725. {
  726. return $this->_transactionStarted;
  727. }
  728. /**
  729. * Quotes value to be used safely in database query.
  730. *
  731. * @param mixed $value The value to quote.
  732. * @param string|null $type Type to be used for determining kind of quoting to perform
  733. * @return string Quoted value
  734. */
  735. public function quote($value, $type = null)
  736. {
  737. list($value, $type) = $this->cast($value, $type);
  738. return $this->_driver->quote($value, $type);
  739. }
  740. /**
  741. * Checks if the driver supports quoting.
  742. *
  743. * @return bool
  744. */
  745. public function supportsQuoting()
  746. {
  747. return $this->_driver->supportsQuoting();
  748. }
  749. /**
  750. * Quotes a database identifier (a column name, table name, etc..) to
  751. * be used safely in queries without the risk of using reserved words.
  752. *
  753. * @param string $identifier The identifier to quote.
  754. * @return string
  755. */
  756. public function quoteIdentifier($identifier)
  757. {
  758. return $this->_driver->quoteIdentifier($identifier);
  759. }
  760. /**
  761. * Enables or disables metadata caching for this connection
  762. *
  763. * Changing this setting will not modify existing schema collections objects.
  764. *
  765. * @param bool|string $cache Either boolean false to disable metadata caching, or
  766. * true to use `_cake_model_` or the name of the cache config to use.
  767. * @return void
  768. */
  769. public function cacheMetadata($cache)
  770. {
  771. $this->_schemaCollection = null;
  772. $this->_config['cacheMetadata'] = $cache;
  773. }
  774. /**
  775. * {@inheritDoc}
  776. *
  777. * @deprecated 3.7.0 Use enableQueryLogging() and isQueryLoggingEnabled() instead.
  778. */
  779. public function logQueries($enable = null)
  780. {
  781. deprecationWarning(
  782. 'Connection::logQueries() is deprecated. ' .
  783. 'Use enableQueryLogging() and isQueryLoggingEnabled() instead.'
  784. );
  785. if ($enable === null) {
  786. return $this->_logQueries;
  787. }
  788. $this->_logQueries = $enable;
  789. }
  790. /**
  791. * Enable/disable query logging
  792. *
  793. * @param bool $value Enable/disable query logging
  794. * @return $this
  795. */
  796. public function enableQueryLogging($value)
  797. {
  798. $this->_logQueries = (bool)$value;
  799. return $this;
  800. }
  801. /**
  802. * Disable query logging
  803. *
  804. * @return $this
  805. */
  806. public function disableQueryLogging()
  807. {
  808. $this->_logQueries = false;
  809. return $this;
  810. }
  811. /**
  812. * Check if query logging is enabled.
  813. *
  814. * @return bool
  815. */
  816. public function isQueryLoggingEnabled()
  817. {
  818. return $this->_logQueries;
  819. }
  820. /**
  821. * {@inheritDoc}
  822. *
  823. * @deprecated 3.5.0 Use getLogger() and setLogger() instead.
  824. */
  825. public function logger($instance = null)
  826. {
  827. deprecationWarning(
  828. 'Connection::logger() is deprecated. ' .
  829. 'Use Connection::setLogger()/getLogger() instead.'
  830. );
  831. if ($instance === null) {
  832. return $this->getLogger();
  833. }
  834. $this->setLogger($instance);
  835. }
  836. /**
  837. * Sets a logger
  838. *
  839. * @param \Cake\Database\Log\QueryLogger $logger Logger object
  840. * @return $this
  841. */
  842. public function setLogger($logger)
  843. {
  844. $this->_logger = $logger;
  845. return $this;
  846. }
  847. /**
  848. * Gets the logger object
  849. *
  850. * @return \Cake\Database\Log\QueryLogger logger instance
  851. */
  852. public function getLogger()
  853. {
  854. if ($this->_logger === null) {
  855. $this->_logger = new QueryLogger();
  856. }
  857. return $this->_logger;
  858. }
  859. /**
  860. * Logs a Query string using the configured logger object.
  861. *
  862. * @param string $sql string to be logged
  863. * @return void
  864. */
  865. public function log($sql)
  866. {
  867. $query = new LoggedQuery();
  868. $query->query = $sql;
  869. $this->getLogger()->log($query);
  870. }
  871. /**
  872. * Returns a new statement object that will log the activity
  873. * for the passed original statement instance.
  874. *
  875. * @param \Cake\Database\StatementInterface $statement the instance to be decorated
  876. * @return \Cake\Database\Log\LoggingStatement
  877. */
  878. protected function _newLogger(StatementInterface $statement)
  879. {
  880. $log = new LoggingStatement($statement, $this->_driver);
  881. $log->setLogger($this->getLogger());
  882. return $log;
  883. }
  884. /**
  885. * Returns an array that can be used to describe the internal state of this
  886. * object.
  887. *
  888. * @return array
  889. */
  890. public function __debugInfo()
  891. {
  892. $secrets = [
  893. 'password' => '*****',
  894. 'username' => '*****',
  895. 'host' => '*****',
  896. 'database' => '*****',
  897. 'port' => '*****',
  898. ];
  899. $replace = array_intersect_key($secrets, $this->_config);
  900. $config = $replace + $this->_config;
  901. return [
  902. 'config' => $config,
  903. 'driver' => $this->_driver,
  904. 'transactionLevel' => $this->_transactionLevel,
  905. 'transactionStarted' => $this->_transactionStarted,
  906. 'useSavePoints' => $this->_useSavePoints,
  907. 'logQueries' => $this->_logQueries,
  908. 'logger' => $this->_logger,
  909. ];
  910. }
  911. }