Connection.php 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236
  1. <?php
  2. declare(strict_types=1);
  3. /**
  4. * This file is part of Hyperf.
  5. *
  6. * @link https://www.hyperf.io
  7. * @document https://hyperf.wiki
  8. * @contact group@hyperf.io
  9. * @license https://github.com/hyperf/hyperf/blob/master/LICENSE
  10. */
  11. namespace Hyperf\Database;
  12. use Closure;
  13. use DateTimeInterface;
  14. use Doctrine\DBAL\Connection as DoctrineConnection;
  15. use Doctrine\DBAL\Schema\AbstractSchemaManager;
  16. use Doctrine\DBAL\Schema\Column;
  17. use Exception;
  18. use Generator;
  19. use Hyperf\Collection\Arr;
  20. use Hyperf\Contracts\Events\Dispatcher;
  21. use Hyperf\Database\Events\QueryExecuted;
  22. use Hyperf\Database\Exception\InvalidArgumentException;
  23. use Hyperf\Database\Exception\QueryException;
  24. use Hyperf\Database\Query\Builder;
  25. use Hyperf\Database\Query\Builder as QueryBuilder;
  26. use Hyperf\Database\Query\Expression;
  27. use Hyperf\Database\Query\Grammars\Grammar as QueryGrammar;
  28. use Hyperf\Database\Query\Processors\Processor;
  29. use Hyperf\Database\Schema\Builder as SchemaBuilder;
  30. use Hyperf\Database\Schema\Grammars\Grammar as SchemaGrammar;
  31. use LogicException;
  32. use PDO;
  33. use PDOStatement;
  34. use Psr\EventDispatcher\EventDispatcherInterface;
  35. use RuntimeException;
  36. use Throwable;
  37. class Connection implements ConnectionInterface
  38. {
  39. use DetectsDeadlocks;
  40. use DetectsLostConnections;
  41. use Concerns\ManagesTransactions;
  42. /**
  43. * The active PDO connection.
  44. *
  45. * @var Closure|PDO
  46. */
  47. protected mixed $pdo;
  48. /**
  49. * The active PDO connection used for reads.
  50. *
  51. * @var Closure|PDO
  52. */
  53. protected mixed $readPdo = null;
  54. /**
  55. * The name of the connected database.
  56. */
  57. protected string $database;
  58. /**
  59. * The table prefix for the connection.
  60. */
  61. protected string $tablePrefix = '';
  62. /**
  63. * The database connection configuration options.
  64. */
  65. protected array $config = [];
  66. /**
  67. * The reconnector instance for the connection.
  68. *
  69. * @var callable
  70. */
  71. protected mixed $reconnector = null;
  72. /**
  73. * The query grammar implementation.
  74. */
  75. protected QueryGrammar $queryGrammar;
  76. /**
  77. * The schema grammar implementation.
  78. */
  79. protected ?SchemaGrammar $schemaGrammar = null;
  80. /**
  81. * The query post processor implementation.
  82. */
  83. protected Processor $postProcessor;
  84. /**
  85. * The event dispatcher instance.
  86. */
  87. protected ?EventDispatcherInterface $events = null;
  88. /**
  89. * The default fetch mode of the connection.
  90. */
  91. protected int $fetchMode = PDO::FETCH_OBJ;
  92. /**
  93. * The number of active transactions.
  94. */
  95. protected int $transactions = 0;
  96. /**
  97. * Indicates if changes have been made to the database.
  98. */
  99. protected bool $recordsModified = false;
  100. /**
  101. * All the queries run against the connection.
  102. */
  103. protected array $queryLog = [];
  104. /**
  105. * Indicates whether queries are being logged.
  106. */
  107. protected bool $loggingQueries = false;
  108. /**
  109. * Indicates if the connection is in a "dry run".
  110. */
  111. protected bool $pretending = false;
  112. /**
  113. * The instance of Doctrine connection.
  114. *
  115. * @var DoctrineConnection
  116. */
  117. protected mixed $doctrineConnection = null;
  118. /**
  119. * The connection resolvers.
  120. * @var Closure[]
  121. */
  122. protected static array $resolvers = [];
  123. /**
  124. * All the callbacks that should be invoked before a query is executed.
  125. *
  126. * @var Closure[]
  127. */
  128. protected static array $beforeExecutingCallbacks = [];
  129. /**
  130. * Error count for executing SQL.
  131. */
  132. protected int $errorCount = 0;
  133. /**
  134. * Create a new database connection instance.
  135. *
  136. * @param Closure|PDO $pdo
  137. */
  138. public function __construct(mixed $pdo, string $database = '', string $tablePrefix = '', array $config = [])
  139. {
  140. $this->pdo = $pdo;
  141. // First we will setup the default properties. We keep track of the DB
  142. // name we are connected to since it is needed when some reflective
  143. // type commands are run such as checking whether a table exists.
  144. $this->database = $database;
  145. $this->tablePrefix = $tablePrefix;
  146. $this->config = $config;
  147. // We need to initialize a query grammar and the query post processors
  148. // which are both very important parts of the database abstractions
  149. // so we initialize these to their default values while starting.
  150. $this->useDefaultQueryGrammar();
  151. $this->useDefaultPostProcessor();
  152. }
  153. /**
  154. * Set the query grammar to the default implementation.
  155. */
  156. public function useDefaultQueryGrammar(): void
  157. {
  158. $this->queryGrammar = $this->getDefaultQueryGrammar();
  159. }
  160. /**
  161. * Set the schema grammar to the default implementation.
  162. */
  163. public function useDefaultSchemaGrammar(): void
  164. {
  165. $this->schemaGrammar = $this->getDefaultSchemaGrammar();
  166. }
  167. /**
  168. * Set the query post processor to the default implementation.
  169. */
  170. public function useDefaultPostProcessor(): void
  171. {
  172. $this->postProcessor = $this->getDefaultPostProcessor();
  173. }
  174. /**
  175. * Get a schema builder instance for the connection.
  176. */
  177. public function getSchemaBuilder(): SchemaBuilder
  178. {
  179. if (is_null($this->schemaGrammar)) {
  180. $this->useDefaultSchemaGrammar();
  181. }
  182. return new SchemaBuilder($this);
  183. }
  184. /**
  185. * Begin a fluent query against a database table.
  186. * @param Expression|string $table
  187. */
  188. public function table($table): Builder
  189. {
  190. return $this->query()->from($table);
  191. }
  192. /**
  193. * Get a new query builder instance.
  194. */
  195. public function query(): QueryBuilder
  196. {
  197. return new QueryBuilder(
  198. $this,
  199. $this->getQueryGrammar(),
  200. $this->getPostProcessor()
  201. );
  202. }
  203. /**
  204. * Run a select statement and return a single result.
  205. */
  206. public function selectOne(string $query, array $bindings = [], bool $useReadPdo = true)
  207. {
  208. $records = $this->select($query, $bindings, $useReadPdo);
  209. return array_shift($records);
  210. }
  211. /**
  212. * Run a select statement against the database.
  213. */
  214. public function selectFromWriteConnection(string $query, array $bindings = []): array
  215. {
  216. return $this->select($query, $bindings, false);
  217. }
  218. /**
  219. * Run a select statement against the database.
  220. */
  221. public function select(string $query, array $bindings = [], bool $useReadPdo = true): array
  222. {
  223. return $this->run($query, $bindings, function ($query, $bindings) use ($useReadPdo) {
  224. if ($this->pretending()) {
  225. return [];
  226. }
  227. // For select statements, we'll simply execute the query and return an array
  228. // of the database result set. Each element in the array will be a single
  229. // row from the database table, and will either be an array or objects.
  230. $statement = $this->prepared($this->getPdoForSelect($useReadPdo)
  231. ->prepare($query));
  232. $this->bindValues($statement, $this->prepareBindings($bindings));
  233. $statement->execute();
  234. return $statement->fetchAll();
  235. });
  236. }
  237. /**
  238. * Run a select statement against the database and returns a generator.
  239. */
  240. public function cursor(string $query, array $bindings = [], bool $useReadPdo = true): Generator
  241. {
  242. $statement = $this->run($query, $bindings, function ($query, $bindings) use ($useReadPdo) {
  243. if ($this->pretending()) {
  244. return [];
  245. }
  246. // First we will create a statement for the query. Then, we will set the fetch
  247. // mode and prepare the bindings for the query. Once that's done we will be
  248. // ready to execute the query against the database and return the cursor.
  249. $statement = $this->prepared($this->getPdoForSelect($useReadPdo)
  250. ->prepare($query));
  251. $this->bindValues(
  252. $statement,
  253. $this->prepareBindings($bindings)
  254. );
  255. // Next, we'll execute the query against the database and return the statement
  256. // so we can return the cursor. The cursor will use a PHP generator to give
  257. // back one row at a time without using a bunch of memory to render them.
  258. $statement->execute();
  259. return $statement;
  260. });
  261. while ($record = $statement->fetch()) {
  262. yield $record;
  263. }
  264. }
  265. /**
  266. * Run an insert statement against the database.
  267. */
  268. public function insert(string $query, array $bindings = []): bool
  269. {
  270. return $this->statement($query, $bindings);
  271. }
  272. /**
  273. * Run an update statement against the database.
  274. */
  275. public function update(string $query, array $bindings = []): int
  276. {
  277. return $this->affectingStatement($query, $bindings);
  278. }
  279. /**
  280. * Run a delete statement against the database.
  281. */
  282. public function delete(string $query, array $bindings = []): int
  283. {
  284. return $this->affectingStatement($query, $bindings);
  285. }
  286. /**
  287. * Execute an SQL statement and return the boolean result.
  288. */
  289. public function statement(string $query, array $bindings = []): bool
  290. {
  291. return $this->run($query, $bindings, function ($query, $bindings) {
  292. if ($this->pretending()) {
  293. return true;
  294. }
  295. $statement = $this->getPdo()->prepare($query);
  296. $this->bindValues($statement, $this->prepareBindings($bindings));
  297. $this->recordsHaveBeenModified();
  298. return $statement->execute();
  299. });
  300. }
  301. /**
  302. * Run an SQL statement and get the number of rows affected.
  303. */
  304. public function affectingStatement(string $query, array $bindings = []): int
  305. {
  306. return $this->run($query, $bindings, function ($query, $bindings) {
  307. if ($this->pretending()) {
  308. return 0;
  309. }
  310. // For update or delete statements, we want to get the number of rows affected
  311. // by the statement and return that back to the developer. We'll first need
  312. // to execute the statement and then we'll use PDO to fetch the affected.
  313. $statement = $this->getPdo()->prepare($query);
  314. $this->bindValues($statement, $this->prepareBindings($bindings));
  315. $statement->execute();
  316. $this->recordsHaveBeenModified(
  317. ($count = $statement->rowCount()) > 0
  318. );
  319. return $count;
  320. });
  321. }
  322. /**
  323. * Run a raw, unprepared query against the PDO connection.
  324. */
  325. public function unprepared(string $query): bool
  326. {
  327. return $this->run($query, [], function ($query) {
  328. if ($this->pretending()) {
  329. return true;
  330. }
  331. $this->recordsHaveBeenModified(
  332. $change = $this->getPdo()->exec($query) !== false
  333. );
  334. return $change;
  335. });
  336. }
  337. /**
  338. * Execute the given callback in "dry run" mode.
  339. */
  340. public function pretend(Closure $callback): array
  341. {
  342. return $this->withFreshQueryLog(function () use ($callback) {
  343. $this->pretending = true;
  344. // Basically to make the database connection "pretend", we will just return
  345. // the default values for all the query methods, then we will return an
  346. // array of queries that were "executed" within the Closure callback.
  347. $callback($this);
  348. $this->pretending = false;
  349. return $this->queryLog;
  350. });
  351. }
  352. /**
  353. * Bind values to their parameters in the given statement.
  354. */
  355. public function bindValues(PDOStatement $statement, array $bindings): void
  356. {
  357. foreach ($bindings as $key => $value) {
  358. $statement->bindValue(
  359. is_string($key) ? $key : $key + 1,
  360. $value,
  361. is_int($value) ? PDO::PARAM_INT : PDO::PARAM_STR
  362. );
  363. }
  364. }
  365. /**
  366. * Prepare the query bindings for execution.
  367. */
  368. public function prepareBindings(array $bindings): array
  369. {
  370. $grammar = $this->getQueryGrammar();
  371. foreach ($bindings as $key => $value) {
  372. // We need to transform all instances of DateTimeInterface into the actual
  373. // date string. Each query grammar maintains its own date string format
  374. // so we'll just ask the grammar for the format to get from the date.
  375. if ($value instanceof DateTimeInterface) {
  376. $bindings[$key] = $value->format($grammar->getDateFormat());
  377. } elseif (is_bool($value)) {
  378. $bindings[$key] = (int) $value;
  379. }
  380. }
  381. return $bindings;
  382. }
  383. /**
  384. * Log a query in the connection's query log.
  385. * @param null|array|int|Throwable $result
  386. */
  387. public function logQuery(string $query, array $bindings, ?float $time = null, $result = null)
  388. {
  389. $this->event(new QueryExecuted($query, $bindings, $time, $this, $result));
  390. if ($this->loggingQueries) {
  391. $this->queryLog[] = compact('query', 'bindings', 'time');
  392. }
  393. }
  394. /**
  395. * Reconnect to the database.
  396. *
  397. * @throws LogicException
  398. */
  399. public function reconnect()
  400. {
  401. if (is_callable($this->reconnector)) {
  402. return call_user_func($this->reconnector, $this);
  403. }
  404. throw new LogicException('Lost connection and no reconnector available.');
  405. }
  406. /**
  407. * Disconnect from the underlying PDO connection.
  408. */
  409. public function disconnect()
  410. {
  411. $this->setPdo(null)->setReadPdo(null);
  412. }
  413. /**
  414. * Register a hook to be run just before a database query is executed.
  415. */
  416. public static function beforeExecuting(Closure $callback): void
  417. {
  418. static::$beforeExecutingCallbacks[] = $callback;
  419. }
  420. /**
  421. * Clear all hooks which will be run before a database query.
  422. */
  423. public static function clearBeforeExecutingCallbacks(): void
  424. {
  425. static::$beforeExecutingCallbacks = [];
  426. }
  427. /**
  428. * Register a database query listener with the connection.
  429. */
  430. public function listen(Closure $callback)
  431. {
  432. // FIXME: Dynamic register query event.
  433. $this->events?->listen(QueryExecuted::class, $callback);
  434. }
  435. /**
  436. * Get a new raw query expression.
  437. * @param mixed $value
  438. */
  439. public function raw($value): Expression
  440. {
  441. return new Expression($value);
  442. }
  443. /**
  444. * Escape a value for safe SQL embedding.
  445. *
  446. * @param null|bool|float|int|string $value
  447. */
  448. public function escape(mixed $value, bool $binary = false): string
  449. {
  450. if ($value === null) {
  451. return 'null';
  452. }
  453. if ($binary) {
  454. return $this->escapeBinary($value);
  455. }
  456. if (is_int($value) || is_float($value)) {
  457. return (string) $value;
  458. }
  459. if (is_bool($value)) {
  460. return $this->escapeBool($value);
  461. }
  462. if (str_contains($value, "\00")) {
  463. throw new RuntimeException('Strings with null bytes cannot be escaped. Use the binary escape option.');
  464. }
  465. if (preg_match('//u', $value) === false) {
  466. throw new RuntimeException('Strings with invalid UTF-8 byte sequences cannot be escaped.');
  467. }
  468. return $this->escapeString($value);
  469. }
  470. /**
  471. * Indicate if any records have been modified.
  472. */
  473. public function recordsHaveBeenModified(bool $value = true)
  474. {
  475. if (! $this->recordsModified) {
  476. $this->recordsModified = $value;
  477. }
  478. }
  479. /**
  480. * Reset $recordsModified property to false.
  481. */
  482. public function resetRecordsModified(): void
  483. {
  484. $this->recordsModified = false;
  485. }
  486. /**
  487. * Is Doctrine available?
  488. */
  489. public function isDoctrineAvailable(): bool
  490. {
  491. return class_exists('Doctrine\DBAL\Connection');
  492. }
  493. /**
  494. * Get a Doctrine Schema Column instance.
  495. *
  496. * @param string $table
  497. * @param string $column
  498. * @return Column
  499. */
  500. public function getDoctrineColumn($table, $column)
  501. {
  502. $schema = $this->getDoctrineSchemaManager();
  503. return $schema->listTableDetails($table)->getColumn($column);
  504. }
  505. /**
  506. * Get the Doctrine DBAL schema manager for the connection.
  507. *
  508. * @return AbstractSchemaManager
  509. */
  510. public function getDoctrineSchemaManager()
  511. {
  512. $connection = $this->getDoctrineConnection();
  513. return $this->getDoctrineDriver()->getSchemaManager(
  514. $connection,
  515. $connection->getDatabasePlatform()
  516. );
  517. }
  518. /**
  519. * Get the Doctrine DBAL database connection instance.
  520. *
  521. * @return DoctrineConnection
  522. */
  523. public function getDoctrineConnection()
  524. {
  525. if (is_null($this->doctrineConnection)) {
  526. $driver = $this->getDoctrineDriver();
  527. $this->doctrineConnection = new DoctrineConnection([
  528. 'pdo' => $this->getPdo(),
  529. 'dbname' => $this->getConfig('database'),
  530. 'driver' => null,
  531. ], $driver);
  532. }
  533. return $this->doctrineConnection;
  534. }
  535. /**
  536. * Get the current PDO connection.
  537. *
  538. * @return PDO
  539. */
  540. public function getPdo()
  541. {
  542. if ($this->pdo instanceof Closure) {
  543. return $this->pdo = call_user_func($this->pdo);
  544. }
  545. return $this->pdo;
  546. }
  547. /**
  548. * Get the current PDO connection used for reading.
  549. *
  550. * @return PDO
  551. */
  552. public function getReadPdo()
  553. {
  554. if ($this->transactions > 0) {
  555. return $this->getPdo();
  556. }
  557. if ($this->recordsModified && $this->getConfig('sticky')) {
  558. return $this->getPdo();
  559. }
  560. if ($this->readPdo instanceof Closure) {
  561. return $this->readPdo = call_user_func($this->readPdo);
  562. }
  563. return $this->readPdo ?: $this->getPdo();
  564. }
  565. /**
  566. * Set the PDO connection.
  567. *
  568. * @param null|Closure|PDO $pdo
  569. * @return $this
  570. */
  571. public function setPdo($pdo)
  572. {
  573. $this->transactions = 0;
  574. try {
  575. $this->pdo = $pdo;
  576. } catch (Exception) {
  577. }
  578. return $this;
  579. }
  580. /**
  581. * Set the PDO connection used for reading.
  582. *
  583. * @param null|Closure|PDO $pdo
  584. * @return $this
  585. */
  586. public function setReadPdo($pdo)
  587. {
  588. try {
  589. $this->readPdo = $pdo;
  590. } catch (Exception) {
  591. }
  592. return $this;
  593. }
  594. /**
  595. * Set the reconnect instance on the connection.
  596. */
  597. public function setReconnector(callable $reconnector): static
  598. {
  599. $this->reconnector = $reconnector;
  600. return $this;
  601. }
  602. /**
  603. * Get the database connection name.
  604. *
  605. * @return null|string
  606. */
  607. public function getName()
  608. {
  609. return $this->getConfig('name');
  610. }
  611. /**
  612. * Get an option from the configuration options.
  613. *
  614. * @param null|string $option
  615. */
  616. public function getConfig($option = null)
  617. {
  618. return Arr::get($this->config, $option);
  619. }
  620. /**
  621. * Get the PDO driver name.
  622. *
  623. * @return string
  624. */
  625. public function getDriverName()
  626. {
  627. return $this->getConfig('driver');
  628. }
  629. /**
  630. * Get the query grammar used by the connection.
  631. *
  632. * @return QueryGrammar
  633. */
  634. public function getQueryGrammar()
  635. {
  636. return $this->queryGrammar;
  637. }
  638. /**
  639. * Set the query grammar used by the connection.
  640. *
  641. * @return $this
  642. */
  643. public function setQueryGrammar(QueryGrammar $grammar)
  644. {
  645. $this->queryGrammar = $grammar;
  646. return $this;
  647. }
  648. /**
  649. * Get the schema grammar used by the connection.
  650. */
  651. public function getSchemaGrammar(): SchemaGrammar
  652. {
  653. if (is_null($this->schemaGrammar)) {
  654. $this->useDefaultSchemaGrammar();
  655. }
  656. return $this->schemaGrammar;
  657. }
  658. /**
  659. * Set the schema grammar used by the connection.
  660. *
  661. * @return $this
  662. */
  663. public function setSchemaGrammar(SchemaGrammar $grammar)
  664. {
  665. $this->schemaGrammar = $grammar;
  666. return $this;
  667. }
  668. /**
  669. * Get the query post processor used by the connection.
  670. */
  671. public function getPostProcessor(): Processor
  672. {
  673. return $this->postProcessor;
  674. }
  675. /**
  676. * Set the query post processor used by the connection.
  677. */
  678. public function setPostProcessor(Processor $processor): static
  679. {
  680. $this->postProcessor = $processor;
  681. return $this;
  682. }
  683. /**
  684. * Get the event dispatcher used by the connection.
  685. *
  686. * @return Dispatcher
  687. */
  688. public function getEventDispatcher()
  689. {
  690. return $this->events;
  691. }
  692. /**
  693. * Set the event dispatcher instance on the connection.
  694. *
  695. * @return $this
  696. */
  697. public function setEventDispatcher(EventDispatcherInterface $events)
  698. {
  699. $this->events = $events;
  700. return $this;
  701. }
  702. /**
  703. * Unset the event dispatcher for this connection.
  704. */
  705. public function unsetEventDispatcher()
  706. {
  707. $this->events = null;
  708. }
  709. /**
  710. * Determine if the connection in a "dry run".
  711. *
  712. * @return bool
  713. */
  714. public function pretending()
  715. {
  716. return $this->pretending === true;
  717. }
  718. /**
  719. * Get the connection query log.
  720. *
  721. * @return array
  722. */
  723. public function getQueryLog()
  724. {
  725. return $this->queryLog;
  726. }
  727. /**
  728. * Get the connection query log with embedded bindings.
  729. *
  730. * @return array
  731. */
  732. public function getRawQueryLog()
  733. {
  734. return array_map(fn (array $log) => [
  735. 'raw_query' => $this->queryGrammar->substituteBindingsIntoRawSql(
  736. $log['query'],
  737. array_map(fn ($value) => $this->escape($value), $this->prepareBindings($log['bindings']))
  738. ),
  739. 'time' => $log['time'],
  740. ], $this->getQueryLog());
  741. }
  742. /**
  743. * Clear the query log.
  744. */
  745. public function flushQueryLog()
  746. {
  747. $this->queryLog = [];
  748. }
  749. /**
  750. * Enable the query log on the connection.
  751. */
  752. public function enableQueryLog()
  753. {
  754. $this->loggingQueries = true;
  755. }
  756. /**
  757. * Disable the query log on the connection.
  758. */
  759. public function disableQueryLog()
  760. {
  761. $this->loggingQueries = false;
  762. }
  763. /**
  764. * Determine whether we're logging queries.
  765. *
  766. * @return bool
  767. */
  768. public function logging()
  769. {
  770. return $this->loggingQueries;
  771. }
  772. /**
  773. * Get the name of the connected database.
  774. *
  775. * @return string
  776. */
  777. public function getDatabaseName()
  778. {
  779. return $this->database;
  780. }
  781. /**
  782. * Set the name of the connected database.
  783. *
  784. * @param string $database
  785. * @return $this
  786. */
  787. public function setDatabaseName($database)
  788. {
  789. $this->database = $database;
  790. return $this;
  791. }
  792. /**
  793. * Get the table prefix for the connection.
  794. */
  795. public function getTablePrefix(): string
  796. {
  797. return $this->tablePrefix;
  798. }
  799. /**
  800. * Set the table prefix in use by the connection.
  801. */
  802. public function setTablePrefix(string $prefix): static
  803. {
  804. $this->tablePrefix = $prefix;
  805. $this->getQueryGrammar()->setTablePrefix($prefix);
  806. return $this;
  807. }
  808. /**
  809. * Set the table prefix and return the grammar.
  810. */
  811. public function withTablePrefix(Grammar $grammar): Grammar
  812. {
  813. $grammar->setTablePrefix($this->tablePrefix);
  814. return $grammar;
  815. }
  816. /**
  817. * Register a connection resolver.
  818. */
  819. public static function resolverFor(string $driver, Closure $callback)
  820. {
  821. static::$resolvers[$driver] = $callback;
  822. }
  823. /**
  824. * Get the connection resolver for the given driver.
  825. */
  826. public static function getResolver(string $driver): ?Closure
  827. {
  828. return static::$resolvers[$driver] ?? null;
  829. }
  830. public function getErrorCount(): int
  831. {
  832. return $this->errorCount;
  833. }
  834. /**
  835. * Escape a string value for safe SQL embedding.
  836. */
  837. protected function escapeString(string $value): string
  838. {
  839. return $this->getPdo()->quote($value);
  840. }
  841. /**
  842. * Escape a boolean value for safe SQL embedding.
  843. */
  844. protected function escapeBool(bool $value): string
  845. {
  846. return $value ? '1' : '0';
  847. }
  848. /**
  849. * Escape a binary value for safe SQL embedding.
  850. */
  851. protected function escapeBinary(mixed $value): string
  852. {
  853. throw new RuntimeException('The database connection does not support escaping binary values.');
  854. }
  855. /**
  856. * Get the default query grammar instance.
  857. */
  858. protected function getDefaultQueryGrammar(): QueryGrammar
  859. {
  860. return new QueryGrammar();
  861. }
  862. /**
  863. * Get the default schema grammar instance.
  864. */
  865. protected function getDefaultSchemaGrammar(): SchemaGrammar
  866. {
  867. throw new InvalidArgumentException("Don't has the default grammar.");
  868. }
  869. /**
  870. * Get the default post processor instance.
  871. */
  872. protected function getDefaultPostProcessor(): Processor
  873. {
  874. return new Processor();
  875. }
  876. /**
  877. * Configure the PDO prepared statement.
  878. *
  879. * @return PDOStatement
  880. */
  881. protected function prepared(PDOStatement $statement)
  882. {
  883. $statement->setFetchMode($this->fetchMode);
  884. $this->event(new Events\StatementPrepared(
  885. $this,
  886. $statement
  887. ));
  888. return $statement;
  889. }
  890. /**
  891. * Get the PDO connection to use for a select query.
  892. *
  893. * @param bool $useReadPdo
  894. * @return PDO
  895. */
  896. protected function getPdoForSelect($useReadPdo = true)
  897. {
  898. return $useReadPdo ? $this->getReadPdo() : $this->getPdo();
  899. }
  900. /**
  901. * Execute the given callback in "dry run" mode.
  902. *
  903. * @param Closure $callback
  904. * @return array
  905. */
  906. protected function withFreshQueryLog($callback)
  907. {
  908. $loggingQueries = $this->loggingQueries;
  909. // First we will back up the value of the logging queries property and then
  910. // we'll be ready to run callbacks. This query log will also get cleared
  911. // so we will have a new log of all the queries that are executed now.
  912. $this->enableQueryLog();
  913. $this->queryLog = [];
  914. // Now we'll execute this callback and capture the result. Once it has been
  915. // executed we will restore the value of query logging and give back the
  916. // value of the callback so the original callers can have the results.
  917. $result = $callback();
  918. $this->loggingQueries = $loggingQueries;
  919. return $result;
  920. }
  921. /**
  922. * Run a SQL statement and log its execution context.
  923. *
  924. * @throws QueryException
  925. */
  926. protected function run(string $query, array $bindings, Closure $callback)
  927. {
  928. foreach (static::$beforeExecutingCallbacks as $beforeExecutingCallback) {
  929. $beforeExecutingCallback($query, $bindings, $this);
  930. }
  931. $this->reconnectIfMissingConnection();
  932. $start = microtime(true);
  933. // Here we will run this query. If an exception occurs we'll determine if it was
  934. // caused by a connection that has been lost. If that is the cause, we'll try
  935. // to re-establish connection and re-run the query with a fresh connection.
  936. try {
  937. $result = $this->runQueryCallback($query, $bindings, $callback);
  938. } catch (QueryException $e) {
  939. $result = $this->handleQueryException(
  940. $e,
  941. $query,
  942. $bindings,
  943. $callback
  944. );
  945. }
  946. // Once we have run the query we will calculate the time that it took to run and
  947. // then log the query, bindings, result and execution time so we will report them on
  948. // the event that the developer needs them. We'll log time in milliseconds.
  949. $this->logQuery(
  950. $query,
  951. $bindings,
  952. $this->getElapsedTime($start),
  953. $result
  954. );
  955. return $result;
  956. }
  957. /**
  958. * Run a SQL statement.
  959. *
  960. * @throws QueryException
  961. */
  962. protected function runQueryCallback(string $query, array $bindings, Closure $callback)
  963. {
  964. // To execute the statement, we'll simply call the callback, which will actually
  965. // run the SQL against the PDO connection. Then we can calculate the time it
  966. // took to execute and log the query SQL, bindings and time in our memory.
  967. try {
  968. $result = $callback($query, $bindings);
  969. }
  970. // If an exception occurs when attempting to run a query, we'll format the error
  971. // message to include the bindings with SQL, which will make this exception a
  972. // lot more helpful to the developer instead of just the database's errors.
  973. catch (Exception $e) {
  974. ++$this->errorCount;
  975. throw new QueryException(
  976. $query,
  977. $this->prepareBindings($bindings),
  978. $e
  979. );
  980. } catch (Throwable $throwable) {
  981. ++$this->errorCount;
  982. throw $throwable;
  983. }
  984. return $result;
  985. }
  986. /**
  987. * Get the elapsed time since a given starting point.
  988. */
  989. protected function getElapsedTime(float $start): float
  990. {
  991. return round((microtime(true) - $start) * 1000, 2);
  992. }
  993. /**
  994. * Handle a query exception.
  995. *
  996. * @throws Exception
  997. */
  998. protected function handleQueryException(QueryException $e, string $query, array $bindings, Closure $callback)
  999. {
  1000. if ($this->transactions >= 1) {
  1001. throw $e;
  1002. }
  1003. return $this->tryAgainIfCausedByLostConnection(
  1004. $e,
  1005. $query,
  1006. $bindings,
  1007. $callback
  1008. );
  1009. }
  1010. /**
  1011. * Handle a query exception that occurred during query execution.
  1012. *
  1013. * @throws QueryException
  1014. */
  1015. protected function tryAgainIfCausedByLostConnection(QueryException $e, string $query, array $bindings, Closure $callback)
  1016. {
  1017. if ($this->causedByLostConnection($e->getPrevious())) {
  1018. $this->reconnect();
  1019. return $this->runQueryCallback($query, $bindings, $callback);
  1020. }
  1021. throw $e;
  1022. }
  1023. /**
  1024. * Reconnect to the database if a PDO connection is missing.
  1025. */
  1026. protected function reconnectIfMissingConnection()
  1027. {
  1028. if (is_null($this->pdo)) {
  1029. $this->reconnect();
  1030. }
  1031. }
  1032. /**
  1033. * Fire an event for this connection.
  1034. *
  1035. * @param string $event
  1036. * @return null|array
  1037. */
  1038. protected function fireConnectionEvent($event)
  1039. {
  1040. return match ($event) {
  1041. 'beganTransaction' => $this->event(new Events\TransactionBeginning($this)),
  1042. 'committed' => $this->event(new Events\TransactionCommitted($this)),
  1043. 'rollingBack' => $this->event(new Events\TransactionRolledBack($this)),
  1044. };
  1045. }
  1046. /**
  1047. * Fire the given event if possible.
  1048. * @param object $event
  1049. * @return object
  1050. */
  1051. protected function event($event)
  1052. {
  1053. return $this->events?->dispatch($event);
  1054. }
  1055. }