SqlServerConnector.php 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  1. <?php
  2. namespace Illuminate\Database\Connectors;
  3. use Illuminate\Support\Arr;
  4. use PDO;
  5. class SqlServerConnector extends Connector implements ConnectorInterface
  6. {
  7. /**
  8. * The PDO connection options.
  9. *
  10. * @var array
  11. */
  12. protected $options = [
  13. PDO::ATTR_CASE => PDO::CASE_NATURAL,
  14. PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
  15. PDO::ATTR_ORACLE_NULLS => PDO::NULL_NATURAL,
  16. PDO::ATTR_STRINGIFY_FETCHES => false,
  17. ];
  18. /**
  19. * Establish a database connection.
  20. *
  21. * @param array $config
  22. * @return \PDO
  23. */
  24. public function connect(array $config)
  25. {
  26. $options = $this->getOptions($config);
  27. $connection = $this->createConnection($this->getDsn($config), $config, $options);
  28. $this->configureIsolationLevel($connection, $config);
  29. return $connection;
  30. }
  31. /**
  32. * Set the connection transaction isolation level.
  33. *
  34. * https://learn.microsoft.com/en-us/sql/t-sql/statements/set-transaction-isolation-level-transact-sql
  35. *
  36. * @param \PDO $connection
  37. * @param array $config
  38. * @return void
  39. */
  40. protected function configureIsolationLevel($connection, array $config)
  41. {
  42. if (! isset($config['isolation_level'])) {
  43. return;
  44. }
  45. $connection->prepare(
  46. "SET TRANSACTION ISOLATION LEVEL {$config['isolation_level']}"
  47. )->execute();
  48. }
  49. /**
  50. * Create a DSN string from a configuration.
  51. *
  52. * @param array $config
  53. * @return string
  54. */
  55. protected function getDsn(array $config)
  56. {
  57. // First we will create the basic DSN setup as well as the port if it is in
  58. // in the configuration options. This will give us the basic DSN we will
  59. // need to establish the PDO connections and return them back for use.
  60. if ($this->prefersOdbc($config)) {
  61. return $this->getOdbcDsn($config);
  62. }
  63. if (in_array('sqlsrv', $this->getAvailableDrivers())) {
  64. return $this->getSqlSrvDsn($config);
  65. } else {
  66. return $this->getDblibDsn($config);
  67. }
  68. }
  69. /**
  70. * Determine if the database configuration prefers ODBC.
  71. *
  72. * @param array $config
  73. * @return bool
  74. */
  75. protected function prefersOdbc(array $config)
  76. {
  77. return in_array('odbc', $this->getAvailableDrivers()) &&
  78. ($config['odbc'] ?? null) === true;
  79. }
  80. /**
  81. * Get the DSN string for a DbLib connection.
  82. *
  83. * @param array $config
  84. * @return string
  85. */
  86. protected function getDblibDsn(array $config)
  87. {
  88. return $this->buildConnectString('dblib', array_merge([
  89. 'host' => $this->buildHostString($config, ':'),
  90. 'dbname' => $config['database'],
  91. ], Arr::only($config, ['appname', 'charset', 'version'])));
  92. }
  93. /**
  94. * Get the DSN string for an ODBC connection.
  95. *
  96. * @param array $config
  97. * @return string
  98. */
  99. protected function getOdbcDsn(array $config)
  100. {
  101. return isset($config['odbc_datasource_name'])
  102. ? 'odbc:'.$config['odbc_datasource_name'] : '';
  103. }
  104. /**
  105. * Get the DSN string for a SqlSrv connection.
  106. *
  107. * @param array $config
  108. * @return string
  109. */
  110. protected function getSqlSrvDsn(array $config)
  111. {
  112. $arguments = [
  113. 'Server' => $this->buildHostString($config, ','),
  114. ];
  115. if (isset($config['database'])) {
  116. $arguments['Database'] = $config['database'];
  117. }
  118. if (isset($config['readonly'])) {
  119. $arguments['ApplicationIntent'] = 'ReadOnly';
  120. }
  121. if (isset($config['pooling']) && $config['pooling'] === false) {
  122. $arguments['ConnectionPooling'] = '0';
  123. }
  124. if (isset($config['appname'])) {
  125. $arguments['APP'] = $config['appname'];
  126. }
  127. if (isset($config['encrypt'])) {
  128. $arguments['Encrypt'] = $config['encrypt'];
  129. }
  130. if (isset($config['trust_server_certificate'])) {
  131. $arguments['TrustServerCertificate'] = $config['trust_server_certificate'];
  132. }
  133. if (isset($config['multiple_active_result_sets']) && $config['multiple_active_result_sets'] === false) {
  134. $arguments['MultipleActiveResultSets'] = 'false';
  135. }
  136. if (isset($config['transaction_isolation'])) {
  137. $arguments['TransactionIsolation'] = $config['transaction_isolation'];
  138. }
  139. if (isset($config['multi_subnet_failover'])) {
  140. $arguments['MultiSubnetFailover'] = $config['multi_subnet_failover'];
  141. }
  142. if (isset($config['column_encryption'])) {
  143. $arguments['ColumnEncryption'] = $config['column_encryption'];
  144. }
  145. if (isset($config['key_store_authentication'])) {
  146. $arguments['KeyStoreAuthentication'] = $config['key_store_authentication'];
  147. }
  148. if (isset($config['key_store_principal_id'])) {
  149. $arguments['KeyStorePrincipalId'] = $config['key_store_principal_id'];
  150. }
  151. if (isset($config['key_store_secret'])) {
  152. $arguments['KeyStoreSecret'] = $config['key_store_secret'];
  153. }
  154. if (isset($config['login_timeout'])) {
  155. $arguments['LoginTimeout'] = $config['login_timeout'];
  156. }
  157. if (isset($config['authentication'])) {
  158. $arguments['Authentication'] = $config['authentication'];
  159. }
  160. return $this->buildConnectString('sqlsrv', $arguments);
  161. }
  162. /**
  163. * Build a connection string from the given arguments.
  164. *
  165. * @param string $driver
  166. * @param array $arguments
  167. * @return string
  168. */
  169. protected function buildConnectString($driver, array $arguments)
  170. {
  171. return $driver.':'.implode(';', array_map(function ($key) use ($arguments) {
  172. return sprintf('%s=%s', $key, $arguments[$key]);
  173. }, array_keys($arguments)));
  174. }
  175. /**
  176. * Build a host string from the given configuration.
  177. *
  178. * @param array $config
  179. * @param string $separator
  180. * @return string
  181. */
  182. protected function buildHostString(array $config, $separator)
  183. {
  184. if (empty($config['port'])) {
  185. return $config['host'];
  186. }
  187. return $config['host'].$separator.$config['port'];
  188. }
  189. /**
  190. * Get the available PDO drivers.
  191. *
  192. * @return array
  193. */
  194. protected function getAvailableDrivers()
  195. {
  196. return PDO::getAvailableDrivers();
  197. }
  198. }