AbstractMySQLDriver.php 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  1. <?php
  2. namespace Doctrine\DBAL\Driver;
  3. use Doctrine\DBAL\Connection;
  4. use Doctrine\DBAL\Driver;
  5. use Doctrine\DBAL\Driver\DriverException as DeprecatedDriverException;
  6. use Doctrine\DBAL\Exception;
  7. use Doctrine\DBAL\Exception\ConnectionException;
  8. use Doctrine\DBAL\Exception\ConnectionLost;
  9. use Doctrine\DBAL\Exception\DeadlockException;
  10. use Doctrine\DBAL\Exception\DriverException;
  11. use Doctrine\DBAL\Exception\ForeignKeyConstraintViolationException;
  12. use Doctrine\DBAL\Exception\InvalidFieldNameException;
  13. use Doctrine\DBAL\Exception\LockWaitTimeoutException;
  14. use Doctrine\DBAL\Exception\NonUniqueFieldNameException;
  15. use Doctrine\DBAL\Exception\NotNullConstraintViolationException;
  16. use Doctrine\DBAL\Exception\SyntaxErrorException;
  17. use Doctrine\DBAL\Exception\TableExistsException;
  18. use Doctrine\DBAL\Exception\TableNotFoundException;
  19. use Doctrine\DBAL\Exception\UniqueConstraintViolationException;
  20. use Doctrine\DBAL\Platforms\MariaDb1027Platform;
  21. use Doctrine\DBAL\Platforms\MySQL57Platform;
  22. use Doctrine\DBAL\Platforms\MySQL80Platform;
  23. use Doctrine\DBAL\Platforms\MySqlPlatform;
  24. use Doctrine\DBAL\Schema\MySqlSchemaManager;
  25. use Doctrine\DBAL\VersionAwarePlatformDriver;
  26. use function assert;
  27. use function preg_match;
  28. use function stripos;
  29. use function version_compare;
  30. /**
  31. * Abstract base implementation of the {@link Driver} interface for MySQL based drivers.
  32. */
  33. abstract class AbstractMySQLDriver implements Driver, ExceptionConverterDriver, VersionAwarePlatformDriver
  34. {
  35. /**
  36. * {@inheritdoc}
  37. *
  38. * @deprecated
  39. *
  40. * @link https://dev.mysql.com/doc/mysql-errors/8.0/en/client-error-reference.html
  41. * @link https://dev.mysql.com/doc/mysql-errors/8.0/en/server-error-reference.html
  42. */
  43. public function convertException($message, DeprecatedDriverException $exception)
  44. {
  45. switch ($exception->getErrorCode()) {
  46. case '1213':
  47. return new DeadlockException($message, $exception);
  48. case '1205':
  49. return new LockWaitTimeoutException($message, $exception);
  50. case '1050':
  51. return new TableExistsException($message, $exception);
  52. case '1051':
  53. case '1146':
  54. return new TableNotFoundException($message, $exception);
  55. case '1216':
  56. case '1217':
  57. case '1451':
  58. case '1452':
  59. case '1701':
  60. return new ForeignKeyConstraintViolationException($message, $exception);
  61. case '1062':
  62. case '1557':
  63. case '1569':
  64. case '1586':
  65. return new UniqueConstraintViolationException($message, $exception);
  66. case '1054':
  67. case '1166':
  68. case '1611':
  69. return new InvalidFieldNameException($message, $exception);
  70. case '1052':
  71. case '1060':
  72. case '1110':
  73. return new NonUniqueFieldNameException($message, $exception);
  74. case '1064':
  75. case '1149':
  76. case '1287':
  77. case '1341':
  78. case '1342':
  79. case '1343':
  80. case '1344':
  81. case '1382':
  82. case '1479':
  83. case '1541':
  84. case '1554':
  85. case '1626':
  86. return new SyntaxErrorException($message, $exception);
  87. case '1044':
  88. case '1045':
  89. case '1046':
  90. case '1049':
  91. case '1095':
  92. case '1142':
  93. case '1143':
  94. case '1227':
  95. case '1370':
  96. case '1429':
  97. case '2002':
  98. case '2005':
  99. return new ConnectionException($message, $exception);
  100. case '2006':
  101. return new ConnectionLost($message, $exception);
  102. case '1048':
  103. case '1121':
  104. case '1138':
  105. case '1171':
  106. case '1252':
  107. case '1263':
  108. case '1364':
  109. case '1566':
  110. return new NotNullConstraintViolationException($message, $exception);
  111. }
  112. return new DriverException($message, $exception);
  113. }
  114. /**
  115. * {@inheritdoc}
  116. *
  117. * @throws Exception
  118. */
  119. public function createDatabasePlatformForVersion($version)
  120. {
  121. $mariadb = stripos($version, 'mariadb') !== false;
  122. if ($mariadb && version_compare($this->getMariaDbMysqlVersionNumber($version), '10.2.7', '>=')) {
  123. return new MariaDb1027Platform();
  124. }
  125. if (! $mariadb) {
  126. $oracleMysqlVersion = $this->getOracleMysqlVersionNumber($version);
  127. if (version_compare($oracleMysqlVersion, '8', '>=')) {
  128. return new MySQL80Platform();
  129. }
  130. if (version_compare($oracleMysqlVersion, '5.7.9', '>=')) {
  131. return new MySQL57Platform();
  132. }
  133. }
  134. return $this->getDatabasePlatform();
  135. }
  136. /**
  137. * Get a normalized 'version number' from the server string
  138. * returned by Oracle MySQL servers.
  139. *
  140. * @param string $versionString Version string returned by the driver, i.e. '5.7.10'
  141. *
  142. * @throws Exception
  143. */
  144. private function getOracleMysqlVersionNumber(string $versionString): string
  145. {
  146. if (
  147. ! preg_match(
  148. '/^(?P<major>\d+)(?:\.(?P<minor>\d+)(?:\.(?P<patch>\d+))?)?/',
  149. $versionString,
  150. $versionParts
  151. )
  152. ) {
  153. throw Exception::invalidPlatformVersionSpecified(
  154. $versionString,
  155. '<major_version>.<minor_version>.<patch_version>'
  156. );
  157. }
  158. $majorVersion = $versionParts['major'];
  159. $minorVersion = $versionParts['minor'] ?? 0;
  160. $patchVersion = $versionParts['patch'] ?? null;
  161. if ($majorVersion === '5' && $minorVersion === '7' && $patchVersion === null) {
  162. $patchVersion = '9';
  163. }
  164. return $majorVersion . '.' . $minorVersion . '.' . $patchVersion;
  165. }
  166. /**
  167. * Detect MariaDB server version, including hack for some mariadb distributions
  168. * that starts with the prefix '5.5.5-'
  169. *
  170. * @param string $versionString Version string as returned by mariadb server, i.e. '5.5.5-Mariadb-10.0.8-xenial'
  171. *
  172. * @throws Exception
  173. */
  174. private function getMariaDbMysqlVersionNumber(string $versionString): string
  175. {
  176. if (
  177. ! preg_match(
  178. '/^(?:5\.5\.5-)?(mariadb-)?(?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)/i',
  179. $versionString,
  180. $versionParts
  181. )
  182. ) {
  183. throw Exception::invalidPlatformVersionSpecified(
  184. $versionString,
  185. '^(?:5\.5\.5-)?(mariadb-)?<major_version>.<minor_version>.<patch_version>'
  186. );
  187. }
  188. return $versionParts['major'] . '.' . $versionParts['minor'] . '.' . $versionParts['patch'];
  189. }
  190. /**
  191. * {@inheritdoc}
  192. *
  193. * @deprecated Use Connection::getDatabase() instead.
  194. */
  195. public function getDatabase(Connection $conn)
  196. {
  197. $params = $conn->getParams();
  198. if (isset($params['dbname'])) {
  199. return $params['dbname'];
  200. }
  201. $database = $conn->query('SELECT DATABASE()')->fetchColumn();
  202. assert($database !== false);
  203. return $database;
  204. }
  205. /**
  206. * {@inheritdoc}
  207. *
  208. * @return MySqlPlatform
  209. */
  210. public function getDatabasePlatform()
  211. {
  212. return new MySqlPlatform();
  213. }
  214. /**
  215. * {@inheritdoc}
  216. *
  217. * @return MySqlSchemaManager
  218. */
  219. public function getSchemaManager(Connection $conn)
  220. {
  221. return new MySqlSchemaManager($conn);
  222. }
  223. }