PostgresConnector.php 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  1. <?php
  2. namespace Illuminate\Database\Connectors;
  3. use Illuminate\Database\Concerns\ParsesSearchPath;
  4. use PDO;
  5. class PostgresConnector extends Connector implements ConnectorInterface
  6. {
  7. use ParsesSearchPath;
  8. /**
  9. * The default PDO connection options.
  10. *
  11. * @var array
  12. */
  13. protected $options = [
  14. PDO::ATTR_CASE => PDO::CASE_NATURAL,
  15. PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
  16. PDO::ATTR_ORACLE_NULLS => PDO::NULL_NATURAL,
  17. PDO::ATTR_STRINGIFY_FETCHES => false,
  18. ];
  19. /**
  20. * Establish a database connection.
  21. *
  22. * @param array $config
  23. * @return \PDO
  24. */
  25. public function connect(array $config)
  26. {
  27. // First we'll create the basic DSN and connection instance connecting to the
  28. // using the configuration option specified by the developer. We will also
  29. // set the default character set on the connections to UTF-8 by default.
  30. $connection = $this->createConnection(
  31. $this->getDsn($config), $config, $this->getOptions($config)
  32. );
  33. $this->configureIsolationLevel($connection, $config);
  34. $this->configureEncoding($connection, $config);
  35. // Next, we will check to see if a timezone has been specified in this config
  36. // and if it has we will issue a statement to modify the timezone with the
  37. // database. Setting this DB timezone is an optional configuration item.
  38. $this->configureTimezone($connection, $config);
  39. $this->configureSearchPath($connection, $config);
  40. // Postgres allows an application_name to be set by the user and this name is
  41. // used to when monitoring the application with pg_stat_activity. So we'll
  42. // determine if the option has been specified and run a statement if so.
  43. $this->configureApplicationName($connection, $config);
  44. $this->configureSynchronousCommit($connection, $config);
  45. return $connection;
  46. }
  47. /**
  48. * Set the connection transaction isolation level.
  49. *
  50. * @param \PDO $connection
  51. * @param array $config
  52. * @return void
  53. */
  54. protected function configureIsolationLevel($connection, array $config)
  55. {
  56. if (isset($config['isolation_level'])) {
  57. $connection->prepare("set session characteristics as transaction isolation level {$config['isolation_level']}")->execute();
  58. }
  59. }
  60. /**
  61. * Set the connection character set and collation.
  62. *
  63. * @param \PDO $connection
  64. * @param array $config
  65. * @return void
  66. */
  67. protected function configureEncoding($connection, $config)
  68. {
  69. if (! isset($config['charset'])) {
  70. return;
  71. }
  72. $connection->prepare("set names '{$config['charset']}'")->execute();
  73. }
  74. /**
  75. * Set the timezone on the connection.
  76. *
  77. * @param \PDO $connection
  78. * @param array $config
  79. * @return void
  80. */
  81. protected function configureTimezone($connection, array $config)
  82. {
  83. if (isset($config['timezone'])) {
  84. $timezone = $config['timezone'];
  85. $connection->prepare("set time zone '{$timezone}'")->execute();
  86. }
  87. }
  88. /**
  89. * Set the "search_path" on the database connection.
  90. *
  91. * @param \PDO $connection
  92. * @param array $config
  93. * @return void
  94. */
  95. protected function configureSearchPath($connection, $config)
  96. {
  97. if (isset($config['search_path']) || isset($config['schema'])) {
  98. $searchPath = $this->quoteSearchPath(
  99. $this->parseSearchPath($config['search_path'] ?? $config['schema'])
  100. );
  101. $connection->prepare("set search_path to {$searchPath}")->execute();
  102. }
  103. }
  104. /**
  105. * Format the search path for the DSN.
  106. *
  107. * @param array $searchPath
  108. * @return string
  109. */
  110. protected function quoteSearchPath($searchPath)
  111. {
  112. return count($searchPath) === 1 ? '"'.$searchPath[0].'"' : '"'.implode('", "', $searchPath).'"';
  113. }
  114. /**
  115. * Set the application name on the connection.
  116. *
  117. * @param \PDO $connection
  118. * @param array $config
  119. * @return void
  120. */
  121. protected function configureApplicationName($connection, $config)
  122. {
  123. if (isset($config['application_name'])) {
  124. $applicationName = $config['application_name'];
  125. $connection->prepare("set application_name to '$applicationName'")->execute();
  126. }
  127. }
  128. /**
  129. * Create a DSN string from a configuration.
  130. *
  131. * @param array $config
  132. * @return string
  133. */
  134. protected function getDsn(array $config)
  135. {
  136. // First we will create the basic DSN setup as well as the port if it is in
  137. // in the configuration options. This will give us the basic DSN we will
  138. // need to establish the PDO connections and return them back for use.
  139. extract($config, EXTR_SKIP);
  140. $host = isset($host) ? "host={$host};" : '';
  141. // Sometimes - users may need to connect to a database that has a different
  142. // name than the database used for "information_schema" queries. This is
  143. // typically the case if using "pgbouncer" type software when pooling.
  144. $database = $connect_via_database ?? $database;
  145. $port = $connect_via_port ?? $port ?? null;
  146. $dsn = "pgsql:{$host}dbname='{$database}'";
  147. // If a port was specified, we will add it to this Postgres DSN connections
  148. // format. Once we have done that we are ready to return this connection
  149. // string back out for usage, as this has been fully constructed here.
  150. if (! is_null($port)) {
  151. $dsn .= ";port={$port}";
  152. }
  153. return $this->addSslOptions($dsn, $config);
  154. }
  155. /**
  156. * Add the SSL options to the DSN.
  157. *
  158. * @param string $dsn
  159. * @param array $config
  160. * @return string
  161. */
  162. protected function addSslOptions($dsn, array $config)
  163. {
  164. foreach (['sslmode', 'sslcert', 'sslkey', 'sslrootcert'] as $option) {
  165. if (isset($config[$option])) {
  166. $dsn .= ";{$option}={$config[$option]}";
  167. }
  168. }
  169. return $dsn;
  170. }
  171. /**
  172. * Configure the synchronous_commit setting.
  173. *
  174. * @param \PDO $connection
  175. * @param array $config
  176. * @return void
  177. */
  178. protected function configureSynchronousCommit($connection, array $config)
  179. {
  180. if (! isset($config['synchronous_commit'])) {
  181. return;
  182. }
  183. $connection->prepare("set synchronous_commit to '{$config['synchronous_commit']}'")->execute();
  184. }
  185. }