1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* @link http://www.yiiframework.com/ |
4
|
|
|
* @copyright Copyright (c) 2008 Yii Software LLC |
5
|
|
|
* @license http://www.yiiframework.com/license/ |
6
|
|
|
*/ |
7
|
|
|
|
8
|
|
|
namespace yii\db; |
9
|
|
|
|
10
|
|
|
use PDO; |
11
|
|
|
use Yii; |
12
|
|
|
use yii\base\Component; |
13
|
|
|
use yii\base\InvalidConfigException; |
14
|
|
|
use yii\base\NotSupportedException; |
15
|
|
|
use yii\caching\CacheInterface; |
16
|
|
|
|
17
|
|
|
/** |
18
|
|
|
* Connection represents a connection to a database via [PDO](https://secure.php.net/manual/en/book.pdo.php). |
19
|
|
|
* |
20
|
|
|
* Connection works together with [[Command]], [[DataReader]] and [[Transaction]] |
21
|
|
|
* to provide data access to various DBMS in a common set of APIs. They are a thin wrapper |
22
|
|
|
* of the [PDO PHP extension](https://secure.php.net/manual/en/book.pdo.php). |
23
|
|
|
* |
24
|
|
|
* Connection supports database replication and read-write splitting. In particular, a Connection component |
25
|
|
|
* can be configured with multiple [[primaries]] and [[replicas]]. It will do load balancing and failover by choosing |
26
|
|
|
* appropriate servers. It will also automatically direct read operations to the replicas and write operations to |
27
|
|
|
* the primary connections. |
28
|
|
|
* |
29
|
|
|
* To establish a DB connection, set [[dsn]], [[username]] and [[password]], and then |
30
|
|
|
* call [[open()]] to connect to the database server. The current state of the connection can be checked using [[$isActive]]. |
31
|
|
|
* |
32
|
|
|
* The following example shows how to create a Connection instance and establish |
33
|
|
|
* the DB connection: |
34
|
|
|
* |
35
|
|
|
* ```php |
36
|
|
|
* $connection = new \yii\db\Connection([ |
37
|
|
|
* 'dsn' => $dsn, |
38
|
|
|
* 'username' => $username, |
39
|
|
|
* 'password' => $password, |
40
|
|
|
* ]); |
41
|
|
|
* $connection->open(); |
42
|
|
|
* ``` |
43
|
|
|
* |
44
|
|
|
* After the DB connection is established, one can execute SQL statements like the following: |
45
|
|
|
* |
46
|
|
|
* ```php |
47
|
|
|
* $command = $connection->createCommand('SELECT * FROM post'); |
48
|
|
|
* $posts = $command->queryAll(); |
49
|
|
|
* $command = $connection->createCommand('UPDATE post SET status=1'); |
50
|
|
|
* $command->execute(); |
51
|
|
|
* ``` |
52
|
|
|
* |
53
|
|
|
* One can also do prepared SQL execution and bind parameters to the prepared SQL. |
54
|
|
|
* When the parameters are coming from user input, you should use this approach |
55
|
|
|
* to prevent SQL injection attacks. The following is an example: |
56
|
|
|
* |
57
|
|
|
* ```php |
58
|
|
|
* $command = $connection->createCommand('SELECT * FROM post WHERE id=:id'); |
59
|
|
|
* $command->bindValue(':id', $_GET['id']); |
60
|
|
|
* $post = $command->query(); |
61
|
|
|
* ``` |
62
|
|
|
* |
63
|
|
|
* For more information about how to perform various DB queries, please refer to [[Command]]. |
64
|
|
|
* |
65
|
|
|
* If the underlying DBMS supports transactions, you can perform transactional SQL queries |
66
|
|
|
* like the following: |
67
|
|
|
* |
68
|
|
|
* ```php |
69
|
|
|
* $transaction = $connection->beginTransaction(); |
70
|
|
|
* try { |
71
|
|
|
* $connection->createCommand($sql1)->execute(); |
72
|
|
|
* $connection->createCommand($sql2)->execute(); |
73
|
|
|
* // ... executing other SQL statements ... |
74
|
|
|
* $transaction->commit(); |
75
|
|
|
* } catch (Exception $e) { |
76
|
|
|
* $transaction->rollBack(); |
77
|
|
|
* } |
78
|
|
|
* ``` |
79
|
|
|
* |
80
|
|
|
* You also can use shortcut for the above like the following: |
81
|
|
|
* |
82
|
|
|
* ```php |
83
|
|
|
* $connection->transaction(function () { |
84
|
|
|
* $order = new Order($customer); |
85
|
|
|
* $order->save(); |
86
|
|
|
* $order->addItems($items); |
87
|
|
|
* }); |
88
|
|
|
* ``` |
89
|
|
|
* |
90
|
|
|
* If needed you can pass transaction isolation level as a second parameter: |
91
|
|
|
* |
92
|
|
|
* ```php |
93
|
|
|
* $connection->transaction(function (Connection $db) { |
94
|
|
|
* //return $db->... |
95
|
|
|
* }, Transaction::READ_UNCOMMITTED); |
96
|
|
|
* ``` |
97
|
|
|
* |
98
|
|
|
* Connection is often used as an application component and configured in the application |
99
|
|
|
* configuration like the following: |
100
|
|
|
* |
101
|
|
|
* ```php |
102
|
|
|
* 'components' => [ |
103
|
|
|
* 'db' => [ |
104
|
|
|
* 'class' => '\yii\db\Connection', |
105
|
|
|
* 'dsn' => 'mysql:host=127.0.0.1;dbname=demo', |
106
|
|
|
* 'username' => 'root', |
107
|
|
|
* 'password' => '', |
108
|
|
|
* 'charset' => 'utf8', |
109
|
|
|
* ], |
110
|
|
|
* ], |
111
|
|
|
* ``` |
112
|
|
|
* |
113
|
|
|
* @property string $driverName Name of the DB driver. |
114
|
|
|
* @property bool $isActive Whether the DB connection is established. This property is read-only. |
115
|
|
|
* @property string $lastInsertID The row ID of the last row inserted, or the last value retrieved from the |
116
|
|
|
* sequence object. This property is read-only. |
117
|
|
|
* @property bool $enableReplicas whether to enable read/write splitting by using [[replicas]] to read data. |
118
|
|
|
* Note that if [[replicas]] is empty, read/write splitting will NOT be enabled no matter what value this property takes. |
119
|
|
|
* @property array $replicas list of replica connection configurations. Each configuration is used to create a replica DB connection. |
120
|
|
|
* When [[enableReplicas]] is true, one of these configurations will be chosen and used to create a DB connection |
121
|
|
|
* for performing read queries only. |
122
|
|
|
* @property array $replicaConfig the configuration that should be merged with every replica configuration listed in [[replicas]]. |
123
|
|
|
* For example, |
124
|
|
|
* |
125
|
|
|
* ```php |
126
|
|
|
* [ |
127
|
|
|
* 'username' => 'replica', |
128
|
|
|
* 'password' => 'replica', |
129
|
|
|
* 'attributes' => [ |
130
|
|
|
* // use a smaller connection timeout |
131
|
|
|
* PDO::ATTR_TIMEOUT => 10, |
132
|
|
|
* ], |
133
|
|
|
* ] |
134
|
|
|
* ``` |
135
|
|
|
* @property array $primaries list of primary connection configurations. Each configuration is used to create a primary DB connection. |
136
|
|
|
* When [[open()]] is called, one of these configurations will be chosen and used to create a DB connection |
137
|
|
|
* which will be used by this object. |
138
|
|
|
* Note that when this property is not empty, the connection setting (e.g. `dsn`, `username`) of this object will |
139
|
|
|
* be ignored. |
140
|
|
|
* @property array $primaryConfig the configuration that should be merged with every primary configuration listed in [[primaries]]. |
141
|
|
|
* For example, |
142
|
|
|
* |
143
|
|
|
* ```php |
144
|
|
|
* [ |
145
|
|
|
* 'username' => 'primary', |
146
|
|
|
* 'password' => 'primary', |
147
|
|
|
* 'attributes' => [ |
148
|
|
|
* // use a smaller connection timeout |
149
|
|
|
* PDO::ATTR_TIMEOUT => 10, |
150
|
|
|
* ], |
151
|
|
|
* ] |
152
|
|
|
* ``` |
153
|
|
|
* @property bool $shufflePrimaries whether to shuffle [[primaries]] before getting one. |
154
|
|
|
* @property-read Connection|null $primary The currently active primary connection. `null` is returned if no primary |
155
|
|
|
* connection is available. This property is read-only. |
156
|
|
|
* @property-read PDO $primaryPdo The PDO instance for the currently active primary connection. This property is |
157
|
|
|
* read-only. |
158
|
|
|
* @property QueryBuilder $queryBuilder The query builder for the current DB connection. Note that the type of |
159
|
|
|
* this property differs in getter and setter. See [[getQueryBuilder()]] and [[setQueryBuilder()]] for details. |
160
|
|
|
* @property Schema $schema The schema information for the database opened by this connection. This property |
161
|
|
|
* is read-only. |
162
|
|
|
* @property string $serverVersion Server version as a string. This property is read-only. |
163
|
|
|
* @property-read Connection $replica The currently active replica connection. This property is read-only. |
164
|
|
|
* @property-read PDO $replicaPdo The PDO instance for the currently active replica connection. This property |
165
|
|
|
* is read-only. |
166
|
|
|
* @property Transaction|null $transaction The currently active transaction. Null if no active transaction. |
167
|
|
|
* This property is read-only. |
168
|
|
|
* |
169
|
|
|
* @author Qiang Xue <[email protected]> |
170
|
|
|
* @since 2.0 |
171
|
|
|
*/ |
172
|
|
|
class Connection extends Component |
173
|
|
|
{ |
174
|
|
|
/** |
175
|
|
|
* @event yii\base\Event an event that is triggered after a DB connection is established |
176
|
|
|
*/ |
177
|
|
|
const EVENT_AFTER_OPEN = 'afterOpen'; |
178
|
|
|
/** |
179
|
|
|
* @event yii\base\Event an event that is triggered right before a top-level transaction is started |
180
|
|
|
*/ |
181
|
|
|
const EVENT_BEGIN_TRANSACTION = 'beginTransaction'; |
182
|
|
|
/** |
183
|
|
|
* @event yii\base\Event an event that is triggered right after a top-level transaction is committed |
184
|
|
|
*/ |
185
|
|
|
const EVENT_COMMIT_TRANSACTION = 'commitTransaction'; |
186
|
|
|
/** |
187
|
|
|
* @event yii\base\Event an event that is triggered right after a top-level transaction is rolled back |
188
|
|
|
*/ |
189
|
|
|
const EVENT_ROLLBACK_TRANSACTION = 'rollbackTransaction'; |
190
|
|
|
|
191
|
|
|
/** |
192
|
|
|
* @var string the Data Source Name, or DSN, contains the information required to connect to the database. |
193
|
|
|
* Please refer to the [PHP manual](https://secure.php.net/manual/en/pdo.construct.php) on |
194
|
|
|
* the format of the DSN string. |
195
|
|
|
* |
196
|
|
|
* For [SQLite](https://secure.php.net/manual/en/ref.pdo-sqlite.connection.php) you may use a [path alias](guide:concept-aliases) |
197
|
|
|
* for specifying the database path, e.g. `sqlite:@app/data/db.sql`. |
198
|
|
|
* |
199
|
|
|
* @see charset |
200
|
|
|
*/ |
201
|
|
|
public $dsn; |
202
|
|
|
/** |
203
|
|
|
* @var string the username for establishing DB connection. Defaults to `null` meaning no username to use. |
204
|
|
|
*/ |
205
|
|
|
public $username; |
206
|
|
|
/** |
207
|
|
|
* @var string the password for establishing DB connection. Defaults to `null` meaning no password to use. |
208
|
|
|
*/ |
209
|
|
|
public $password; |
210
|
|
|
/** |
211
|
|
|
* @var array PDO attributes (name => value) that should be set when calling [[open()]] |
212
|
|
|
* to establish a DB connection. Please refer to the |
213
|
|
|
* [PHP manual](https://secure.php.net/manual/en/pdo.setattribute.php) for |
214
|
|
|
* details about available attributes. |
215
|
|
|
*/ |
216
|
|
|
public $attributes; |
217
|
|
|
/** |
218
|
|
|
* @var PDO the PHP PDO instance associated with this DB connection. |
219
|
|
|
* This property is mainly managed by [[open()]] and [[close()]] methods. |
220
|
|
|
* When a DB connection is active, this property will represent a PDO instance; |
221
|
|
|
* otherwise, it will be null. |
222
|
|
|
* @see pdoClass |
223
|
|
|
*/ |
224
|
|
|
public $pdo; |
225
|
|
|
/** |
226
|
|
|
* @var bool whether to enable schema caching. |
227
|
|
|
* Note that in order to enable truly schema caching, a valid cache component as specified |
228
|
|
|
* by [[schemaCache]] must be enabled and [[enableSchemaCache]] must be set true. |
229
|
|
|
* @see schemaCacheDuration |
230
|
|
|
* @see schemaCacheExclude |
231
|
|
|
* @see schemaCache |
232
|
|
|
*/ |
233
|
|
|
public $enableSchemaCache = false; |
234
|
|
|
/** |
235
|
|
|
* @var int number of seconds that table metadata can remain valid in cache. |
236
|
|
|
* Use 0 to indicate that the cached data will never expire. |
237
|
|
|
* @see enableSchemaCache |
238
|
|
|
*/ |
239
|
|
|
public $schemaCacheDuration = 3600; |
240
|
|
|
/** |
241
|
|
|
* @var array list of tables whose metadata should NOT be cached. Defaults to empty array. |
242
|
|
|
* The table names may contain schema prefix, if any. Do not quote the table names. |
243
|
|
|
* @see enableSchemaCache |
244
|
|
|
*/ |
245
|
|
|
public $schemaCacheExclude = []; |
246
|
|
|
/** |
247
|
|
|
* @var CacheInterface|string the cache object or the ID of the cache application component that |
248
|
|
|
* is used to cache the table metadata. |
249
|
|
|
* @see enableSchemaCache |
250
|
|
|
*/ |
251
|
|
|
public $schemaCache = 'cache'; |
252
|
|
|
/** |
253
|
|
|
* @var bool whether to enable query caching. |
254
|
|
|
* Note that in order to enable query caching, a valid cache component as specified |
255
|
|
|
* by [[queryCache]] must be enabled and [[enableQueryCache]] must be set true. |
256
|
|
|
* Also, only the results of the queries enclosed within [[cache()]] will be cached. |
257
|
|
|
* @see queryCache |
258
|
|
|
* @see cache() |
259
|
|
|
* @see noCache() |
260
|
|
|
*/ |
261
|
|
|
public $enableQueryCache = true; |
262
|
|
|
/** |
263
|
|
|
* @var int the default number of seconds that query results can remain valid in cache. |
264
|
|
|
* Defaults to 3600, meaning 3600 seconds, or one hour. Use 0 to indicate that the cached data will never expire. |
265
|
|
|
* The value of this property will be used when [[cache()]] is called without a cache duration. |
266
|
|
|
* @see enableQueryCache |
267
|
|
|
* @see cache() |
268
|
|
|
*/ |
269
|
|
|
public $queryCacheDuration = 3600; |
270
|
|
|
/** |
271
|
|
|
* @var CacheInterface|string the cache object or the ID of the cache application component |
272
|
|
|
* that is used for query caching. |
273
|
|
|
* @see enableQueryCache |
274
|
|
|
*/ |
275
|
|
|
public $queryCache = 'cache'; |
276
|
|
|
/** |
277
|
|
|
* @var string the charset used for database connection. The property is only used |
278
|
|
|
* for MySQL, PostgreSQL and CUBRID databases. Defaults to null, meaning using default charset |
279
|
|
|
* as configured by the database. |
280
|
|
|
* |
281
|
|
|
* For Oracle Database, the charset must be specified in the [[dsn]], for example for UTF-8 by appending `;charset=UTF-8` |
282
|
|
|
* to the DSN string. |
283
|
|
|
* |
284
|
|
|
* The same applies for if you're using GBK or BIG5 charset with MySQL, then it's highly recommended to |
285
|
|
|
* specify charset via [[dsn]] like `'mysql:dbname=mydatabase;host=127.0.0.1;charset=GBK;'`. |
286
|
|
|
*/ |
287
|
|
|
public $charset; |
288
|
|
|
/** |
289
|
|
|
* @var bool whether to turn on prepare emulation. Defaults to false, meaning PDO |
290
|
|
|
* will use the native prepare support if available. For some databases (such as MySQL), |
291
|
|
|
* this may need to be set true so that PDO can emulate the prepare support to bypass |
292
|
|
|
* the buggy native prepare support. |
293
|
|
|
* The default value is null, which means the PDO ATTR_EMULATE_PREPARES value will not be changed. |
294
|
|
|
*/ |
295
|
|
|
public $emulatePrepare; |
296
|
|
|
/** |
297
|
|
|
* @var string the common prefix or suffix for table names. If a table name is given |
298
|
|
|
* as `{{%TableName}}`, then the percentage character `%` will be replaced with this |
299
|
|
|
* property value. For example, `{{%post}}` becomes `{{tbl_post}}`. |
300
|
|
|
*/ |
301
|
|
|
public $tablePrefix = ''; |
302
|
|
|
/** |
303
|
|
|
* @var array mapping between PDO driver names and [[Schema]] classes. |
304
|
|
|
* The keys of the array are PDO driver names while the values are either the corresponding |
305
|
|
|
* schema class names or configurations. Please refer to [[Yii::createObject()]] for |
306
|
|
|
* details on how to specify a configuration. |
307
|
|
|
* |
308
|
|
|
* This property is mainly used by [[getSchema()]] when fetching the database schema information. |
309
|
|
|
* You normally do not need to set this property unless you want to use your own |
310
|
|
|
* [[Schema]] class to support DBMS that is not supported by Yii. |
311
|
|
|
*/ |
312
|
|
|
public $schemaMap = [ |
313
|
|
|
'pgsql' => 'yii\db\pgsql\Schema', // PostgreSQL |
314
|
|
|
'mysqli' => 'yii\db\mysql\Schema', // MySQL |
315
|
|
|
'mysql' => 'yii\db\mysql\Schema', // MySQL |
316
|
|
|
'sqlite' => 'yii\db\sqlite\Schema', // sqlite 3 |
317
|
|
|
'sqlite2' => 'yii\db\sqlite\Schema', // sqlite 2 |
318
|
|
|
'sqlsrv' => 'yii\db\mssql\Schema', // newer MSSQL driver on MS Windows hosts |
319
|
|
|
'oci' => 'yii\db\oci\Schema', // Oracle driver |
320
|
|
|
'mssql' => 'yii\db\mssql\Schema', // older MSSQL driver on MS Windows hosts |
321
|
|
|
'dblib' => 'yii\db\mssql\Schema', // dblib drivers on GNU/Linux (and maybe other OSes) hosts |
322
|
|
|
'cubrid' => 'yii\db\cubrid\Schema', // CUBRID |
323
|
|
|
]; |
324
|
|
|
/** |
325
|
|
|
* @var string Custom PDO wrapper class. If not set, it will use [[PDO]] or [[\yii\db\mssql\PDO]] when MSSQL is used. |
326
|
|
|
* @see pdo |
327
|
|
|
*/ |
328
|
|
|
public $pdoClass; |
329
|
|
|
/** |
330
|
|
|
* @var string the class used to create new database [[Command]] objects. If you want to extend the [[Command]] class, |
331
|
|
|
* you may configure this property to use your extended version of the class. |
332
|
|
|
* Since version 2.0.14 [[$commandMap]] is used if this property is set to its default value. |
333
|
|
|
* @see createCommand |
334
|
|
|
* @since 2.0.7 |
335
|
|
|
* @deprecated since 2.0.14. Use [[$commandMap]] for precise configuration. |
336
|
|
|
*/ |
337
|
|
|
public $commandClass = 'yii\db\Command'; |
338
|
|
|
/** |
339
|
|
|
* @var array mapping between PDO driver names and [[Command]] classes. |
340
|
|
|
* The keys of the array are PDO driver names while the values are either the corresponding |
341
|
|
|
* command class names or configurations. Please refer to [[Yii::createObject()]] for |
342
|
|
|
* details on how to specify a configuration. |
343
|
|
|
* |
344
|
|
|
* This property is mainly used by [[createCommand()]] to create new database [[Command]] objects. |
345
|
|
|
* You normally do not need to set this property unless you want to use your own |
346
|
|
|
* [[Command]] class or support DBMS that is not supported by Yii. |
347
|
|
|
* @since 2.0.14 |
348
|
|
|
*/ |
349
|
|
|
public $commandMap = [ |
350
|
|
|
'pgsql' => 'yii\db\Command', // PostgreSQL |
351
|
|
|
'mysqli' => 'yii\db\Command', // MySQL |
352
|
|
|
'mysql' => 'yii\db\Command', // MySQL |
353
|
|
|
'sqlite' => 'yii\db\sqlite\Command', // sqlite 3 |
354
|
|
|
'sqlite2' => 'yii\db\sqlite\Command', // sqlite 2 |
355
|
|
|
'sqlsrv' => 'yii\db\Command', // newer MSSQL driver on MS Windows hosts |
356
|
|
|
'oci' => 'yii\db\oci\Command', // Oracle driver |
357
|
|
|
'mssql' => 'yii\db\Command', // older MSSQL driver on MS Windows hosts |
358
|
|
|
'dblib' => 'yii\db\Command', // dblib drivers on GNU/Linux (and maybe other OSes) hosts |
359
|
|
|
'cubrid' => 'yii\db\Command', // CUBRID |
360
|
|
|
]; |
361
|
|
|
/** |
362
|
|
|
* @var bool whether to enable [savepoint](http://en.wikipedia.org/wiki/Savepoint). |
363
|
|
|
* Note that if the underlying DBMS does not support savepoint, setting this property to be true will have no effect. |
364
|
|
|
*/ |
365
|
|
|
public $enableSavepoint = true; |
366
|
|
|
/** |
367
|
|
|
* @var CacheInterface|string|false the cache object or the ID of the cache application component that is used to store |
368
|
|
|
* the health status of the DB servers specified in [[primaries]] and [[replicas]]. |
369
|
|
|
* This is used only when read/write splitting is enabled or [[primaries]] is not empty. |
370
|
|
|
* Set boolean `false` to disabled server status caching. |
371
|
|
|
* @see openFromPoolSequentially() for details about the failover behavior. |
372
|
|
|
* @see serverRetryInterval |
373
|
|
|
*/ |
374
|
|
|
public $serverStatusCache = 'cache'; |
375
|
|
|
/** |
376
|
|
|
* @var int the retry interval in seconds for dead servers listed in [[primaries]] and [[replicas]]. |
377
|
|
|
* This is used together with [[serverStatusCache]]. |
378
|
|
|
*/ |
379
|
|
|
public $serverRetryInterval = 600; |
380
|
|
|
/** |
381
|
|
|
* @var bool whether to enable read/write splitting by using [[replicas]] to read data. |
382
|
|
|
* Note that if [[replicas]] is empty, read/write splitting will NOT be enabled no matter what value this property takes. |
383
|
|
|
* @deprecated since 2.0.36. Use [[enableReplicas]] instead. |
384
|
|
|
*/ |
385
|
|
|
public $enableSlaves = true; |
386
|
|
|
/** |
387
|
|
|
* Returns the value of [[enableReplicas]]. |
388
|
|
|
* @return bool |
389
|
|
|
* @since 2.0.36 |
390
|
|
|
* @internal |
391
|
|
|
*/ |
392
|
|
|
public function getEnableReplicas() |
393
|
|
|
{ |
394
|
|
|
return $this->enableSlaves; |
|
|
|
|
395
|
|
|
} |
396
|
|
|
/** |
397
|
|
|
* Sets the value of [[enableReplicas]]. |
398
|
|
|
* @param bool $value |
399
|
|
|
* @since 2.0.36 |
400
|
|
|
* @internal |
401
|
|
|
*/ |
402
|
|
|
public function setEnableReplicas($value) |
403
|
|
|
{ |
404
|
|
|
$this->enableSlaves = $value; |
|
|
|
|
405
|
|
|
} |
406
|
|
|
/** |
407
|
|
|
* @var array list of replica connection configurations. Each configuration is used to create a replica DB connection. |
408
|
|
|
* When [[enableReplicas]] is true, one of these configurations will be chosen and used to create a DB connection |
409
|
|
|
* for performing read queries only. |
410
|
|
|
* @see enableSlaves |
411
|
|
|
* @see slaveConfig |
412
|
|
|
* @deprecated since 2.0.36. Use [[replicas]] instead. |
413
|
|
|
*/ |
414
|
|
|
public $slaves = []; |
415
|
|
|
/** |
416
|
|
|
* Returns the value of [[replicas]]. |
417
|
|
|
* @return array |
418
|
|
|
* @since 2.0.36 |
419
|
|
|
* @internal |
420
|
|
|
*/ |
421
|
|
|
public function getReplicas() |
422
|
|
|
{ |
423
|
|
|
return $this->slaves; |
|
|
|
|
424
|
|
|
} |
425
|
|
|
/** |
426
|
|
|
* Sets the value of [[replicas]]. |
427
|
|
|
* @param array $value |
428
|
|
|
* @since 2.0.36 |
429
|
|
|
* @internal |
430
|
|
|
*/ |
431
|
10 |
|
public function setReplicas($value) |
432
|
|
|
{ |
433
|
10 |
|
$this->slaves = $value; |
|
|
|
|
434
|
10 |
|
} |
435
|
|
|
/** |
436
|
|
|
* @var array the configuration that should be merged with every replica configuration listed in [[replicas]]. |
437
|
|
|
* For example, |
438
|
|
|
* |
439
|
|
|
* ```php |
440
|
|
|
* [ |
441
|
|
|
* 'username' => 'replica', |
442
|
|
|
* 'password' => 'replica', |
443
|
|
|
* 'attributes' => [ |
444
|
|
|
* // use a smaller connection timeout |
445
|
|
|
* PDO::ATTR_TIMEOUT => 10, |
446
|
|
|
* ], |
447
|
|
|
* ] |
448
|
|
|
* ``` |
449
|
|
|
* |
450
|
|
|
* @deprecated since 2.0.36. Use [[replicaConfig]] instead. |
451
|
|
|
*/ |
452
|
|
|
public $slaveConfig = []; |
453
|
|
|
/** |
454
|
|
|
* Returns the value of [[replicaConfig]]. |
455
|
|
|
* @return array |
456
|
|
|
* @since 2.0.36 |
457
|
|
|
* @internal |
458
|
|
|
*/ |
459
|
|
|
public function getReplicaConfig() |
460
|
|
|
{ |
461
|
|
|
return $this->slaveConfig; |
|
|
|
|
462
|
|
|
} |
463
|
|
|
/** |
464
|
|
|
* Sets the value of [[replicaConfig]]. |
465
|
|
|
* @param array $value |
466
|
|
|
* @since 2.0.36 |
467
|
|
|
* @internal |
468
|
|
|
*/ |
469
|
|
|
public function setReplicaConfig($value) |
470
|
|
|
{ |
471
|
|
|
$this->slaveConfig = $value; |
|
|
|
|
472
|
|
|
} |
473
|
|
|
/** |
474
|
|
|
* @var array list of primary connection configurations. Each configuration is used to create a primary DB connection. |
475
|
|
|
* When [[open()]] is called, one of these configurations will be chosen and used to create a DB connection |
476
|
|
|
* which will be used by this object. |
477
|
|
|
* Note that when this property is not empty, the connection setting (e.g. `dsn`, `username`) of this object will |
478
|
|
|
* be ignored. |
479
|
|
|
* @see masterConfig |
480
|
|
|
* @see shuffleMasters |
481
|
|
|
* @deprecated since 2.0.36. Use [[primaries]] instead. |
482
|
|
|
*/ |
483
|
|
|
public $masters = []; |
484
|
|
|
/** |
485
|
|
|
* Returns the value of [[primaries]]. |
486
|
|
|
* @return array |
487
|
|
|
* @since 2.0.36 |
488
|
|
|
* @internal |
489
|
|
|
*/ |
490
|
|
|
public function getPrimaries() |
491
|
|
|
{ |
492
|
|
|
return $this->masters; |
|
|
|
|
493
|
|
|
} |
494
|
|
|
/** |
495
|
|
|
* Sets the value of [[primaries]]. |
496
|
|
|
* @param array $value |
497
|
|
|
* @since 2.0.36 |
498
|
|
|
* @internal |
499
|
|
|
*/ |
500
|
4 |
|
public function setPrimaries($value) |
501
|
|
|
{ |
502
|
4 |
|
$this->masters = $value; |
|
|
|
|
503
|
4 |
|
} |
504
|
|
|
/** |
505
|
|
|
* @var array the configuration that should be merged with every primary configuration listed in [[primaries]]. |
506
|
|
|
* For example, |
507
|
|
|
* |
508
|
|
|
* ```php |
509
|
|
|
* [ |
510
|
|
|
* 'username' => 'primary', |
511
|
|
|
* 'password' => 'primary', |
512
|
|
|
* 'attributes' => [ |
513
|
|
|
* // use a smaller connection timeout |
514
|
|
|
* PDO::ATTR_TIMEOUT => 10, |
515
|
|
|
* ], |
516
|
|
|
* ] |
517
|
|
|
* ``` |
518
|
|
|
* |
519
|
|
|
* @deprecated since 2.0.36. Use [[primaryConfig]] instead. |
520
|
|
|
*/ |
521
|
|
|
public $masterConfig = []; |
522
|
|
|
/** |
523
|
|
|
* Returns the value of [[primaryConfig]]. |
524
|
|
|
* @return array |
525
|
|
|
* @since 2.0.36 |
526
|
|
|
* @internal |
527
|
|
|
*/ |
528
|
|
|
public function getPrimaryConfig() |
529
|
|
|
{ |
530
|
|
|
return $this->masterConfig; |
|
|
|
|
531
|
|
|
} |
532
|
|
|
/** |
533
|
|
|
* Sets the value of [[primaryConfig]]. |
534
|
|
|
* @param array $value |
535
|
|
|
* @since 2.0.36 |
536
|
|
|
* @internal |
537
|
|
|
*/ |
538
|
|
|
public function setPrimaryConfig($value) |
539
|
|
|
{ |
540
|
|
|
$this->masterConfig = $value; |
|
|
|
|
541
|
|
|
} |
542
|
|
|
/** |
543
|
|
|
* @var bool whether to shuffle [[primaries]] before getting one. |
544
|
|
|
* @since 2.0.11 |
545
|
|
|
* @see masters |
546
|
|
|
* @deprecated since 2.0.36. Use [[shufflePrimaries]] instead. |
547
|
|
|
*/ |
548
|
|
|
public $shuffleMasters = true; |
549
|
|
|
/** |
550
|
|
|
* Returns the value of [[shufflePrimaries]]. |
551
|
|
|
* @return bool |
552
|
|
|
* @since 2.0.36 |
553
|
|
|
* @internal |
554
|
|
|
*/ |
555
|
|
|
public function getShufflePrimaries() |
556
|
|
|
{ |
557
|
|
|
return $this->shuffleMasters; |
|
|
|
|
558
|
|
|
} |
559
|
|
|
/** |
560
|
|
|
* Sets the value of [[shufflePrimaries]]. |
561
|
|
|
* @param bool $value |
562
|
|
|
* @since 2.0.36 |
563
|
|
|
* @internal |
564
|
|
|
*/ |
565
|
|
|
public function setShufflePrimaries($value) |
566
|
|
|
{ |
567
|
|
|
$this->shuffleMasters = $value; |
|
|
|
|
568
|
|
|
} |
569
|
|
|
/** |
570
|
|
|
* @var bool whether to enable logging of database queries. Defaults to true. |
571
|
|
|
* You may want to disable this option in a production environment to gain performance |
572
|
|
|
* if you do not need the information being logged. |
573
|
|
|
* @since 2.0.12 |
574
|
|
|
* @see enableProfiling |
575
|
|
|
*/ |
576
|
|
|
public $enableLogging = true; |
577
|
|
|
/** |
578
|
|
|
* @var bool whether to enable profiling of opening database connection and database queries. Defaults to true. |
579
|
|
|
* You may want to disable this option in a production environment to gain performance |
580
|
|
|
* if you do not need the information being logged. |
581
|
|
|
* @since 2.0.12 |
582
|
|
|
* @see enableLogging |
583
|
|
|
*/ |
584
|
|
|
public $enableProfiling = true; |
585
|
|
|
|
586
|
|
|
/** |
587
|
|
|
* @var Transaction the currently active transaction |
588
|
|
|
*/ |
589
|
|
|
private $_transaction; |
590
|
|
|
/** |
591
|
|
|
* @var Schema the database schema |
592
|
|
|
*/ |
593
|
|
|
private $_schema; |
594
|
|
|
/** |
595
|
|
|
* @var string driver name |
596
|
|
|
*/ |
597
|
|
|
private $_driverName; |
598
|
|
|
/** |
599
|
|
|
* @var Connection|false the currently active primary connection |
600
|
|
|
*/ |
601
|
|
|
private $_primary = false; |
602
|
|
|
/** |
603
|
|
|
* @var Connection|false the currently active replica connection |
604
|
|
|
*/ |
605
|
|
|
private $_replica = false; |
606
|
|
|
/** |
607
|
|
|
* @var array query cache parameters for the [[cache()]] calls |
608
|
|
|
*/ |
609
|
|
|
private $_queryCacheInfo = []; |
610
|
|
|
/** |
611
|
|
|
* @var string[] quoted table name cache for [[quoteTableName()]] calls |
612
|
|
|
*/ |
613
|
|
|
private $_quotedTableNames; |
614
|
|
|
/** |
615
|
|
|
* @var string[] quoted column name cache for [[quoteColumnName()]] calls |
616
|
|
|
*/ |
617
|
|
|
private $_quotedColumnNames; |
618
|
|
|
|
619
|
|
|
|
620
|
|
|
/** |
621
|
|
|
* Returns a value indicating whether the DB connection is established. |
622
|
|
|
* @return bool whether the DB connection is established |
623
|
|
|
*/ |
624
|
332 |
|
public function getIsActive() |
625
|
|
|
{ |
626
|
332 |
|
return $this->pdo !== null; |
627
|
|
|
} |
628
|
|
|
|
629
|
|
|
/** |
630
|
|
|
* Uses query cache for the queries performed with the callable. |
631
|
|
|
* |
632
|
|
|
* When query caching is enabled ([[enableQueryCache]] is true and [[queryCache]] refers to a valid cache), |
633
|
|
|
* queries performed within the callable will be cached and their results will be fetched from cache if available. |
634
|
|
|
* For example, |
635
|
|
|
* |
636
|
|
|
* ```php |
637
|
|
|
* // The customer will be fetched from cache if available. |
638
|
|
|
* // If not, the query will be made against DB and cached for use next time. |
639
|
|
|
* $customer = $db->cache(function (Connection $db) { |
640
|
|
|
* return $db->createCommand('SELECT * FROM customer WHERE id=1')->queryOne(); |
641
|
|
|
* }); |
642
|
|
|
* ``` |
643
|
|
|
* |
644
|
|
|
* Note that query cache is only meaningful for queries that return results. For queries performed with |
645
|
|
|
* [[Command::execute()]], query cache will not be used. |
646
|
|
|
* |
647
|
|
|
* @param callable $callable a PHP callable that contains DB queries which will make use of query cache. |
648
|
|
|
* The signature of the callable is `function (Connection $db)`. |
649
|
|
|
* @param int $duration the number of seconds that query results can remain valid in the cache. If this is |
650
|
|
|
* not set, the value of [[queryCacheDuration]] will be used instead. |
651
|
|
|
* Use 0 to indicate that the cached data will never expire. |
652
|
|
|
* @param \yii\caching\Dependency $dependency the cache dependency associated with the cached query results. |
653
|
|
|
* @return mixed the return result of the callable |
654
|
|
|
* @throws \Exception|\Throwable if there is any exception during query |
655
|
|
|
* @see enableQueryCache |
656
|
|
|
* @see queryCache |
657
|
|
|
* @see noCache() |
658
|
|
|
*/ |
659
|
6 |
|
public function cache(callable $callable, $duration = null, $dependency = null) |
660
|
|
|
{ |
661
|
6 |
|
$this->_queryCacheInfo[] = [$duration === null ? $this->queryCacheDuration : $duration, $dependency]; |
662
|
|
|
try { |
663
|
6 |
|
$result = call_user_func($callable, $this); |
664
|
6 |
|
array_pop($this->_queryCacheInfo); |
665
|
6 |
|
return $result; |
666
|
|
|
} catch (\Exception $e) { |
667
|
|
|
array_pop($this->_queryCacheInfo); |
668
|
|
|
throw $e; |
669
|
|
|
} catch (\Throwable $e) { |
670
|
|
|
array_pop($this->_queryCacheInfo); |
671
|
|
|
throw $e; |
672
|
|
|
} |
673
|
|
|
} |
674
|
|
|
|
675
|
|
|
/** |
676
|
|
|
* Disables query cache temporarily. |
677
|
|
|
* |
678
|
|
|
* Queries performed within the callable will not use query cache at all. For example, |
679
|
|
|
* |
680
|
|
|
* ```php |
681
|
|
|
* $db->cache(function (Connection $db) { |
682
|
|
|
* |
683
|
|
|
* // ... queries that use query cache ... |
684
|
|
|
* |
685
|
|
|
* return $db->noCache(function (Connection $db) { |
686
|
|
|
* // this query will not use query cache |
687
|
|
|
* return $db->createCommand('SELECT * FROM customer WHERE id=1')->queryOne(); |
688
|
|
|
* }); |
689
|
|
|
* }); |
690
|
|
|
* ``` |
691
|
|
|
* |
692
|
|
|
* @param callable $callable a PHP callable that contains DB queries which should not use query cache. |
693
|
|
|
* The signature of the callable is `function (Connection $db)`. |
694
|
|
|
* @return mixed the return result of the callable |
695
|
|
|
* @throws \Exception|\Throwable if there is any exception during query |
696
|
|
|
* @see enableQueryCache |
697
|
|
|
* @see queryCache |
698
|
|
|
* @see cache() |
699
|
|
|
*/ |
700
|
40 |
|
public function noCache(callable $callable) |
701
|
|
|
{ |
702
|
40 |
|
$this->_queryCacheInfo[] = false; |
703
|
|
|
try { |
704
|
40 |
|
$result = call_user_func($callable, $this); |
705
|
40 |
|
array_pop($this->_queryCacheInfo); |
706
|
40 |
|
return $result; |
707
|
4 |
|
} catch (\Exception $e) { |
708
|
4 |
|
array_pop($this->_queryCacheInfo); |
709
|
4 |
|
throw $e; |
710
|
|
|
} catch (\Throwable $e) { |
711
|
|
|
array_pop($this->_queryCacheInfo); |
712
|
|
|
throw $e; |
713
|
|
|
} |
714
|
|
|
} |
715
|
|
|
|
716
|
|
|
/** |
717
|
|
|
* Returns the current query cache information. |
718
|
|
|
* This method is used internally by [[Command]]. |
719
|
|
|
* @param int $duration the preferred caching duration. If null, it will be ignored. |
720
|
|
|
* @param \yii\caching\Dependency $dependency the preferred caching dependency. If null, it will be ignored. |
721
|
|
|
* @return array the current query cache information, or null if query cache is not enabled. |
722
|
|
|
* @internal |
723
|
|
|
*/ |
724
|
1505 |
|
public function getQueryCacheInfo($duration, $dependency) |
725
|
|
|
{ |
726
|
1505 |
|
if (!$this->enableQueryCache) { |
727
|
44 |
|
return null; |
728
|
|
|
} |
729
|
|
|
|
730
|
1503 |
|
$info = end($this->_queryCacheInfo); |
731
|
1503 |
|
if (is_array($info)) { |
732
|
6 |
|
if ($duration === null) { |
|
|
|
|
733
|
6 |
|
$duration = $info[0]; |
734
|
|
|
} |
735
|
6 |
|
if ($dependency === null) { |
736
|
6 |
|
$dependency = $info[1]; |
737
|
|
|
} |
738
|
|
|
} |
739
|
|
|
|
740
|
1503 |
|
if ($duration === 0 || $duration > 0) { |
741
|
6 |
|
if (is_string($this->queryCache) && Yii::$app) { |
742
|
|
|
$cache = Yii::$app->get($this->queryCache, false); |
743
|
|
|
} else { |
744
|
6 |
|
$cache = $this->queryCache; |
745
|
|
|
} |
746
|
6 |
|
if ($cache instanceof CacheInterface) { |
747
|
6 |
|
return [$cache, $duration, $dependency]; |
748
|
|
|
} |
749
|
|
|
} |
750
|
|
|
|
751
|
1503 |
|
return null; |
752
|
|
|
} |
753
|
|
|
|
754
|
|
|
/** |
755
|
|
|
* Establishes a DB connection. |
756
|
|
|
* It does nothing if a DB connection has already been established. |
757
|
|
|
* @throws Exception if connection fails |
758
|
|
|
*/ |
759
|
2034 |
|
public function open() |
760
|
|
|
{ |
761
|
2034 |
|
if ($this->pdo !== null) { |
762
|
1615 |
|
return; |
763
|
|
|
} |
764
|
|
|
|
765
|
1979 |
|
if (!empty($this->masters)) { |
|
|
|
|
766
|
9 |
|
$db = $this->getMaster(); |
|
|
|
|
767
|
9 |
|
if ($db !== null) { |
768
|
9 |
|
$this->pdo = $db->pdo; |
769
|
9 |
|
return; |
770
|
|
|
} |
771
|
|
|
|
772
|
8 |
|
throw new InvalidConfigException('None of the primary DB servers are available.'); |
773
|
|
|
} |
774
|
|
|
|
775
|
1979 |
|
if (empty($this->dsn)) { |
776
|
|
|
throw new InvalidConfigException('Connection::dsn cannot be empty.'); |
777
|
|
|
} |
778
|
|
|
|
779
|
1979 |
|
$token = 'Opening DB connection: ' . $this->dsn; |
780
|
1979 |
|
$enableProfiling = $this->enableProfiling; |
781
|
|
|
try { |
782
|
1979 |
|
if ($this->enableLogging) { |
783
|
1979 |
|
Yii::info($token, __METHOD__); |
784
|
|
|
} |
785
|
|
|
|
786
|
1979 |
|
if ($enableProfiling) { |
787
|
1979 |
|
Yii::beginProfile($token, __METHOD__); |
788
|
|
|
} |
789
|
|
|
|
790
|
1979 |
|
$this->pdo = $this->createPdoInstance(); |
791
|
1979 |
|
$this->initConnection(); |
792
|
|
|
|
793
|
1979 |
|
if ($enableProfiling) { |
794
|
1979 |
|
Yii::endProfile($token, __METHOD__); |
795
|
|
|
} |
796
|
12 |
|
} catch (\PDOException $e) { |
797
|
12 |
|
if ($enableProfiling) { |
798
|
12 |
|
Yii::endProfile($token, __METHOD__); |
799
|
|
|
} |
800
|
|
|
|
801
|
12 |
|
throw new Exception($e->getMessage(), $e->errorInfo, (int) $e->getCode(), $e); |
802
|
|
|
} |
803
|
1979 |
|
} |
804
|
|
|
|
805
|
|
|
/** |
806
|
|
|
* Closes the currently active DB connection. |
807
|
|
|
* It does nothing if the connection is already closed. |
808
|
|
|
*/ |
809
|
2131 |
|
public function close() |
810
|
|
|
{ |
811
|
2131 |
|
if ($this->_primary) { |
812
|
8 |
|
if ($this->pdo === $this->_primary->pdo) { |
813
|
8 |
|
$this->pdo = null; |
814
|
|
|
} |
815
|
|
|
|
816
|
8 |
|
$this->_primary->close(); |
817
|
8 |
|
$this->_primary = false; |
818
|
|
|
} |
819
|
|
|
|
820
|
2131 |
|
if ($this->pdo !== null) { |
821
|
1784 |
|
Yii::debug('Closing DB connection: ' . $this->dsn, __METHOD__); |
822
|
1784 |
|
$this->pdo = null; |
823
|
|
|
} |
824
|
|
|
|
825
|
2131 |
|
if ($this->_replica) { |
826
|
4 |
|
$this->_replica->close(); |
827
|
4 |
|
$this->_replica = false; |
828
|
|
|
} |
829
|
|
|
|
830
|
2131 |
|
$this->_schema = null; |
831
|
2131 |
|
$this->_transaction = null; |
832
|
2131 |
|
$this->_driverName = null; |
833
|
2131 |
|
$this->_queryCacheInfo = []; |
834
|
2131 |
|
$this->_quotedTableNames = null; |
835
|
2131 |
|
$this->_quotedColumnNames = null; |
836
|
2131 |
|
} |
837
|
|
|
|
838
|
|
|
/** |
839
|
|
|
* Creates the PDO instance. |
840
|
|
|
* This method is called by [[open]] to establish a DB connection. |
841
|
|
|
* The default implementation will create a PHP PDO instance. |
842
|
|
|
* You may override this method if the default PDO needs to be adapted for certain DBMS. |
843
|
|
|
* @return PDO the pdo instance |
844
|
|
|
*/ |
845
|
1979 |
|
protected function createPdoInstance() |
846
|
|
|
{ |
847
|
1979 |
|
$pdoClass = $this->pdoClass; |
848
|
1979 |
|
if ($pdoClass === null) { |
|
|
|
|
849
|
1979 |
|
$pdoClass = 'PDO'; |
850
|
1979 |
|
if ($this->_driverName !== null) { |
851
|
239 |
|
$driver = $this->_driverName; |
852
|
1745 |
|
} elseif (($pos = strpos($this->dsn, ':')) !== false) { |
853
|
1745 |
|
$driver = strtolower(substr($this->dsn, 0, $pos)); |
854
|
|
|
} |
855
|
1979 |
|
if (isset($driver)) { |
856
|
1979 |
|
if ($driver === 'mssql' || $driver === 'dblib') { |
857
|
|
|
$pdoClass = 'yii\db\mssql\PDO'; |
858
|
1979 |
|
} elseif ($driver === 'sqlsrv') { |
|
|
|
|
859
|
|
|
$pdoClass = 'yii\db\mssql\SqlsrvPDO'; |
860
|
|
|
} |
861
|
|
|
} |
862
|
|
|
} |
863
|
|
|
|
864
|
1979 |
|
$dsn = $this->dsn; |
865
|
1979 |
|
if (strncmp('sqlite:@', $dsn, 8) === 0) { |
866
|
1 |
|
$dsn = 'sqlite:' . Yii::getAlias(substr($dsn, 7)); |
867
|
|
|
} |
868
|
|
|
|
869
|
1979 |
|
return new $pdoClass($dsn, $this->username, $this->password, $this->attributes); |
870
|
|
|
} |
871
|
|
|
|
872
|
|
|
/** |
873
|
|
|
* Initializes the DB connection. |
874
|
|
|
* This method is invoked right after the DB connection is established. |
875
|
|
|
* The default implementation turns on `PDO::ATTR_EMULATE_PREPARES` |
876
|
|
|
* if [[emulatePrepare]] is true, and sets the database [[charset]] if it is not empty. |
877
|
|
|
* It then triggers an [[EVENT_AFTER_OPEN]] event. |
878
|
|
|
*/ |
879
|
1979 |
|
protected function initConnection() |
880
|
|
|
{ |
881
|
1979 |
|
$this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); |
882
|
1979 |
|
if ($this->emulatePrepare !== null && constant('PDO::ATTR_EMULATE_PREPARES')) { |
883
|
|
|
if ($this->driverName !== 'sqlsrv') { |
884
|
|
|
$this->pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, $this->emulatePrepare); |
885
|
|
|
} |
886
|
|
|
} |
887
|
1979 |
|
if (in_array($this->getDriverName(), ['mssql', 'dblib'], true)) { |
888
|
|
|
$this->pdo->exec('SET ANSI_NULL_DFLT_ON ON'); |
889
|
|
|
} |
890
|
1979 |
|
if ($this->charset !== null && in_array($this->getDriverName(), ['pgsql', 'mysql', 'mysqli', 'cubrid'], true)) { |
891
|
|
|
$this->pdo->exec('SET NAMES ' . $this->pdo->quote($this->charset)); |
892
|
|
|
} |
893
|
1979 |
|
$this->trigger(self::EVENT_AFTER_OPEN); |
894
|
1979 |
|
} |
895
|
|
|
|
896
|
|
|
/** |
897
|
|
|
* Creates a command for execution. |
898
|
|
|
* @param string $sql the SQL statement to be executed |
899
|
|
|
* @param array $params the parameters to be bound to the SQL statement |
900
|
|
|
* @return Command the DB command |
901
|
|
|
*/ |
902
|
1596 |
|
public function createCommand($sql = null, $params = []) |
903
|
|
|
{ |
904
|
1596 |
|
$driver = $this->getDriverName(); |
905
|
1596 |
|
$config = ['class' => 'yii\db\Command']; |
906
|
1596 |
|
if ($this->commandClass !== $config['class']) { |
|
|
|
|
907
|
|
|
$config['class'] = $this->commandClass; |
|
|
|
|
908
|
1596 |
|
} elseif (isset($this->commandMap[$driver])) { |
909
|
1596 |
|
$config = !is_array($this->commandMap[$driver]) ? ['class' => $this->commandMap[$driver]] : $this->commandMap[$driver]; |
910
|
|
|
} |
911
|
1596 |
|
$config['db'] = $this; |
912
|
1596 |
|
$config['sql'] = $sql; |
913
|
|
|
/** @var Command $command */ |
914
|
1596 |
|
$command = Yii::createObject($config); |
915
|
1596 |
|
return $command->bindValues($params); |
916
|
|
|
} |
917
|
|
|
|
918
|
|
|
/** |
919
|
|
|
* Returns the currently active transaction. |
920
|
|
|
* @return Transaction|null the currently active transaction. Null if no active transaction. |
921
|
|
|
*/ |
922
|
1565 |
|
public function getTransaction() |
923
|
|
|
{ |
924
|
1565 |
|
return $this->_transaction && $this->_transaction->getIsActive() ? $this->_transaction : null; |
925
|
|
|
} |
926
|
|
|
|
927
|
|
|
/** |
928
|
|
|
* Starts a transaction. |
929
|
|
|
* @param string|null $isolationLevel The isolation level to use for this transaction. |
930
|
|
|
* See [[Transaction::begin()]] for details. |
931
|
|
|
* @return Transaction the transaction initiated |
932
|
|
|
*/ |
933
|
39 |
|
public function beginTransaction($isolationLevel = null) |
934
|
|
|
{ |
935
|
39 |
|
$this->open(); |
936
|
|
|
|
937
|
39 |
|
if (($transaction = $this->getTransaction()) === null) { |
938
|
39 |
|
$transaction = $this->_transaction = new Transaction(['db' => $this]); |
939
|
|
|
} |
940
|
39 |
|
$transaction->begin($isolationLevel); |
941
|
|
|
|
942
|
39 |
|
return $transaction; |
943
|
|
|
} |
944
|
|
|
|
945
|
|
|
/** |
946
|
|
|
* Executes callback provided in a transaction. |
947
|
|
|
* |
948
|
|
|
* @param callable $callback a valid PHP callback that performs the job. Accepts connection instance as parameter. |
949
|
|
|
* @param string|null $isolationLevel The isolation level to use for this transaction. |
950
|
|
|
* See [[Transaction::begin()]] for details. |
951
|
|
|
* @throws \Exception|\Throwable if there is any exception during query. In this case the transaction will be rolled back. |
952
|
|
|
* @return mixed result of callback function |
953
|
|
|
*/ |
954
|
23 |
|
public function transaction(callable $callback, $isolationLevel = null) |
955
|
|
|
{ |
956
|
23 |
|
$transaction = $this->beginTransaction($isolationLevel); |
957
|
23 |
|
$level = $transaction->level; |
958
|
|
|
|
959
|
|
|
try { |
960
|
23 |
|
$result = call_user_func($callback, $this); |
961
|
15 |
|
if ($transaction->isActive && $transaction->level === $level) { |
962
|
15 |
|
$transaction->commit(); |
963
|
|
|
} |
964
|
8 |
|
} catch (\Exception $e) { |
965
|
8 |
|
$this->rollbackTransactionOnLevel($transaction, $level); |
966
|
8 |
|
throw $e; |
967
|
|
|
} catch (\Throwable $e) { |
968
|
|
|
$this->rollbackTransactionOnLevel($transaction, $level); |
969
|
|
|
throw $e; |
970
|
|
|
} |
971
|
|
|
|
972
|
15 |
|
return $result; |
973
|
|
|
} |
974
|
|
|
|
975
|
|
|
/** |
976
|
|
|
* Rolls back given [[Transaction]] object if it's still active and level match. |
977
|
|
|
* In some cases rollback can fail, so this method is fail safe. Exception thrown |
978
|
|
|
* from rollback will be caught and just logged with [[\Yii::error()]]. |
979
|
|
|
* @param Transaction $transaction Transaction object given from [[beginTransaction()]]. |
980
|
|
|
* @param int $level Transaction level just after [[beginTransaction()]] call. |
981
|
|
|
*/ |
982
|
8 |
|
private function rollbackTransactionOnLevel($transaction, $level) |
983
|
|
|
{ |
984
|
8 |
|
if ($transaction->isActive && $transaction->level === $level) { |
985
|
|
|
// https://github.com/yiisoft/yii2/pull/13347 |
986
|
|
|
try { |
987
|
8 |
|
$transaction->rollBack(); |
988
|
|
|
} catch (\Exception $e) { |
989
|
|
|
\Yii::error($e, __METHOD__); |
990
|
|
|
// hide this exception to be able to continue throwing original exception outside |
991
|
|
|
} |
992
|
|
|
} |
993
|
8 |
|
} |
994
|
|
|
|
995
|
|
|
/** |
996
|
|
|
* Returns the schema information for the database opened by this connection. |
997
|
|
|
* @return Schema the schema information for the database opened by this connection. |
998
|
|
|
* @throws NotSupportedException if there is no support for the current driver type |
999
|
|
|
*/ |
1000
|
2065 |
|
public function getSchema() |
1001
|
|
|
{ |
1002
|
2065 |
|
if ($this->_schema !== null) { |
1003
|
1687 |
|
return $this->_schema; |
1004
|
|
|
} |
1005
|
|
|
|
1006
|
2010 |
|
$driver = $this->getDriverName(); |
1007
|
2010 |
|
if (isset($this->schemaMap[$driver])) { |
1008
|
2010 |
|
$config = !is_array($this->schemaMap[$driver]) ? ['class' => $this->schemaMap[$driver]] : $this->schemaMap[$driver]; |
1009
|
2010 |
|
$config['db'] = $this; |
1010
|
|
|
|
1011
|
2010 |
|
return $this->_schema = Yii::createObject($config); |
1012
|
|
|
} |
1013
|
|
|
|
1014
|
|
|
throw new NotSupportedException("Connection does not support reading schema information for '$driver' DBMS."); |
1015
|
|
|
} |
1016
|
|
|
|
1017
|
|
|
/** |
1018
|
|
|
* Returns the query builder for the current DB connection. |
1019
|
|
|
* @return QueryBuilder the query builder for the current DB connection. |
1020
|
|
|
*/ |
1021
|
1100 |
|
public function getQueryBuilder() |
1022
|
|
|
{ |
1023
|
1100 |
|
return $this->getSchema()->getQueryBuilder(); |
1024
|
|
|
} |
1025
|
|
|
|
1026
|
|
|
/** |
1027
|
|
|
* Can be used to set [[QueryBuilder]] configuration via Connection configuration array. |
1028
|
|
|
* |
1029
|
|
|
* @param array $value the [[QueryBuilder]] properties to be configured. |
1030
|
|
|
* @since 2.0.14 |
1031
|
|
|
*/ |
1032
|
|
|
public function setQueryBuilder($value) |
1033
|
|
|
{ |
1034
|
|
|
Yii::configure($this->getQueryBuilder(), $value); |
1035
|
|
|
} |
1036
|
|
|
|
1037
|
|
|
/** |
1038
|
|
|
* Obtains the schema information for the named table. |
1039
|
|
|
* @param string $name table name. |
1040
|
|
|
* @param bool $refresh whether to reload the table schema even if it is found in the cache. |
1041
|
|
|
* @return TableSchema table schema information. Null if the named table does not exist. |
1042
|
|
|
*/ |
1043
|
242 |
|
public function getTableSchema($name, $refresh = false) |
1044
|
|
|
{ |
1045
|
242 |
|
return $this->getSchema()->getTableSchema($name, $refresh); |
1046
|
|
|
} |
1047
|
|
|
|
1048
|
|
|
/** |
1049
|
|
|
* Returns the ID of the last inserted row or sequence value. |
1050
|
|
|
* @param string $sequenceName name of the sequence object (required by some DBMS) |
1051
|
|
|
* @return string the row ID of the last row inserted, or the last value retrieved from the sequence object |
1052
|
|
|
* @see https://secure.php.net/manual/en/pdo.lastinsertid.php |
1053
|
|
|
*/ |
1054
|
6 |
|
public function getLastInsertID($sequenceName = '') |
1055
|
|
|
{ |
1056
|
6 |
|
return $this->getSchema()->getLastInsertID($sequenceName); |
1057
|
|
|
} |
1058
|
|
|
|
1059
|
|
|
/** |
1060
|
|
|
* Quotes a string value for use in a query. |
1061
|
|
|
* Note that if the parameter is not a string, it will be returned without change. |
1062
|
|
|
* @param string $value string to be quoted |
1063
|
|
|
* @return string the properly quoted string |
1064
|
|
|
* @see https://secure.php.net/manual/en/pdo.quote.php |
1065
|
|
|
*/ |
1066
|
1075 |
|
public function quoteValue($value) |
1067
|
|
|
{ |
1068
|
1075 |
|
return $this->getSchema()->quoteValue($value); |
1069
|
|
|
} |
1070
|
|
|
|
1071
|
|
|
/** |
1072
|
|
|
* Quotes a table name for use in a query. |
1073
|
|
|
* If the table name contains schema prefix, the prefix will also be properly quoted. |
1074
|
|
|
* If the table name is already quoted or contains special characters including '(', '[[' and '{{', |
1075
|
|
|
* then this method will do nothing. |
1076
|
|
|
* @param string $name table name |
1077
|
|
|
* @return string the properly quoted table name |
1078
|
|
|
*/ |
1079
|
1361 |
|
public function quoteTableName($name) |
1080
|
|
|
{ |
1081
|
1361 |
|
if (isset($this->_quotedTableNames[$name])) { |
1082
|
1002 |
|
return $this->_quotedTableNames[$name]; |
1083
|
|
|
} |
1084
|
1295 |
|
return $this->_quotedTableNames[$name] = $this->getSchema()->quoteTableName($name); |
1085
|
|
|
} |
1086
|
|
|
|
1087
|
|
|
/** |
1088
|
|
|
* Quotes a column name for use in a query. |
1089
|
|
|
* If the column name contains prefix, the prefix will also be properly quoted. |
1090
|
|
|
* If the column name is already quoted or contains special characters including '(', '[[' and '{{', |
1091
|
|
|
* then this method will do nothing. |
1092
|
|
|
* @param string $name column name |
1093
|
|
|
* @return string the properly quoted column name |
1094
|
|
|
*/ |
1095
|
1416 |
|
public function quoteColumnName($name) |
1096
|
|
|
{ |
1097
|
1416 |
|
if (isset($this->_quotedColumnNames[$name])) { |
1098
|
875 |
|
return $this->_quotedColumnNames[$name]; |
1099
|
|
|
} |
1100
|
1361 |
|
return $this->_quotedColumnNames[$name] = $this->getSchema()->quoteColumnName($name); |
1101
|
|
|
} |
1102
|
|
|
|
1103
|
|
|
/** |
1104
|
|
|
* Processes a SQL statement by quoting table and column names that are enclosed within double brackets. |
1105
|
|
|
* Tokens enclosed within double curly brackets are treated as table names, while |
1106
|
|
|
* tokens enclosed within double square brackets are column names. They will be quoted accordingly. |
1107
|
|
|
* Also, the percentage character "%" at the beginning or ending of a table name will be replaced |
1108
|
|
|
* with [[tablePrefix]]. |
1109
|
|
|
* @param string $sql the SQL to be quoted |
1110
|
|
|
* @return string the quoted SQL |
1111
|
|
|
*/ |
1112
|
1640 |
|
public function quoteSql($sql) |
1113
|
|
|
{ |
1114
|
1640 |
|
return preg_replace_callback( |
1115
|
1640 |
|
'/(\\{\\{(%?[\w\-\. ]+%?)\\}\\}|\\[\\[([\w\-\. ]+)\\]\\])/', |
1116
|
1640 |
|
function ($matches) { |
1117
|
817 |
|
if (isset($matches[3])) { |
1118
|
601 |
|
return $this->quoteColumnName($matches[3]); |
1119
|
|
|
} |
1120
|
|
|
|
1121
|
713 |
|
return str_replace('%', $this->tablePrefix, $this->quoteTableName($matches[2])); |
1122
|
1640 |
|
}, |
1123
|
1640 |
|
$sql |
1124
|
|
|
); |
1125
|
|
|
} |
1126
|
|
|
|
1127
|
|
|
/** |
1128
|
|
|
* Returns the name of the DB driver. Based on the the current [[dsn]], in case it was not set explicitly |
1129
|
|
|
* by an end user. |
1130
|
|
|
* @return string name of the DB driver |
1131
|
|
|
*/ |
1132
|
2298 |
|
public function getDriverName() |
1133
|
|
|
{ |
1134
|
2298 |
|
if ($this->_driverName === null) { |
1135
|
2229 |
|
if (($pos = strpos($this->dsn, ':')) !== false) { |
1136
|
2229 |
|
$this->_driverName = strtolower(substr($this->dsn, 0, $pos)); |
1137
|
|
|
} else { |
1138
|
|
|
$this->_driverName = strtolower($this->getSlavePdo()->getAttribute(PDO::ATTR_DRIVER_NAME)); |
|
|
|
|
1139
|
|
|
} |
1140
|
|
|
} |
1141
|
|
|
|
1142
|
2298 |
|
return $this->_driverName; |
1143
|
|
|
} |
1144
|
|
|
|
1145
|
|
|
/** |
1146
|
|
|
* Changes the current driver name. |
1147
|
|
|
* @param string $driverName name of the DB driver |
1148
|
|
|
*/ |
1149
|
|
|
public function setDriverName($driverName) |
1150
|
|
|
{ |
1151
|
|
|
$this->_driverName = strtolower($driverName); |
1152
|
|
|
} |
1153
|
|
|
|
1154
|
|
|
/** |
1155
|
|
|
* Returns a server version as a string comparable by [[\version_compare()]]. |
1156
|
|
|
* @return string server version as a string. |
1157
|
|
|
* @since 2.0.14 |
1158
|
|
|
*/ |
1159
|
411 |
|
public function getServerVersion() |
1160
|
|
|
{ |
1161
|
411 |
|
return $this->getSchema()->getServerVersion(); |
1162
|
|
|
} |
1163
|
|
|
|
1164
|
|
|
/** |
1165
|
|
|
* Returns the PDO instance for the currently active replica connection. |
1166
|
|
|
* When [[enableReplicas]] is true, one of the replicas will be used for read queries, and its PDO instance |
1167
|
|
|
* will be returned by this method. |
1168
|
|
|
* @param bool $fallbackToPrimary whether to return the primary PDO if no replica connections are available. |
1169
|
|
|
* @return PDO|null the PDO instance for the currently active replica connection. `null` is returned if no |
1170
|
|
|
* replica connections are available and `$fallbackToPrimary` is false. |
1171
|
|
|
* @since 2.0.36 |
1172
|
|
|
*/ |
1173
|
1748 |
|
public function getReplicaPdo($fallbackToPrimary = true) |
1174
|
|
|
{ |
1175
|
1748 |
|
$db = $this->getSlave(false); |
|
|
|
|
1176
|
1748 |
|
if ($db === null) { |
1177
|
1744 |
|
return $fallbackToPrimary ? $this->getMasterPdo() : null; |
|
|
|
|
1178
|
|
|
} |
1179
|
|
|
|
1180
|
5 |
|
return $db->pdo; |
1181
|
|
|
} |
1182
|
|
|
|
1183
|
|
|
/** |
1184
|
|
|
* Returns the PDO instance for the currently active replica connection. |
1185
|
|
|
* When [[enableReplicas]] is true, one of the replicas will be used for read queries, and its PDO instance |
1186
|
|
|
* will be returned by this method. |
1187
|
|
|
* @param bool $fallbackToPrimary whether to return the primary PDO if no replica connections are available. |
1188
|
|
|
* @return PDO|null the PDO instance for the currently active replica connection. `null` is returned if no |
1189
|
|
|
* replica connections are available and `$fallbackToPrimary` is false. |
1190
|
|
|
* @deprecated since 2.0.36. Use [[getReplicaPdo()]] instead. |
1191
|
|
|
*/ |
1192
|
|
|
public function getSlavePdo($fallbackToPrimary = true) |
1193
|
|
|
{ |
1194
|
|
|
return $this->getReplicaPdo($fallbackToPrimary); |
1195
|
|
|
} |
1196
|
|
|
|
1197
|
|
|
/** |
1198
|
|
|
* Returns the PDO instance for the currently active primary connection. |
1199
|
|
|
* This method will open the primary DB connection and then return [[pdo]]. |
1200
|
|
|
* @return PDO the PDO instance for the currently active primary connection. |
1201
|
|
|
* @since 2.0.36 |
1202
|
|
|
*/ |
1203
|
1778 |
|
public function getPrimaryPdo() |
1204
|
|
|
{ |
1205
|
1778 |
|
$this->open(); |
1206
|
1778 |
|
return $this->pdo; |
1207
|
|
|
} |
1208
|
|
|
|
1209
|
|
|
/** |
1210
|
|
|
* Returns the PDO instance for the currently active primary connection. |
1211
|
|
|
* This method will open the primary DB connection and then return [[pdo]]. |
1212
|
|
|
* @return PDO the PDO instance for the currently active primary connection. |
1213
|
|
|
* @deprecated since 2.0.36. Use [[getPrimaryPdo()]] instead. |
1214
|
|
|
*/ |
1215
|
|
|
public function getMasterPdo() |
1216
|
|
|
{ |
1217
|
|
|
return $this->getPrimaryPdo(); |
1218
|
|
|
} |
1219
|
|
|
|
1220
|
|
|
/** |
1221
|
|
|
* Returns the currently active replica connection. |
1222
|
|
|
* If this method is called for the first time, it will try to open a replica connection when [[enableReplicas]] |
1223
|
|
|
* is true. |
1224
|
|
|
* @param bool $fallbackToPrimary whether to return the primary connection if no replica connections are |
1225
|
|
|
* available. |
1226
|
|
|
* @return Connection|null the currently active replica connection. `null` is returned if no replica connections |
1227
|
|
|
* are available and `$fallbackToPrimary` is false. |
1228
|
|
|
* @since 2.0.36 |
1229
|
|
|
*/ |
1230
|
1750 |
|
public function getReplica($fallbackToPrimary = true) |
1231
|
|
|
{ |
1232
|
1750 |
|
if (!$this->enableSlaves) { |
|
|
|
|
1233
|
209 |
|
return $fallbackToPrimary ? $this : null; |
1234
|
|
|
} |
1235
|
|
|
|
1236
|
1651 |
|
if ($this->_replica === false) { |
1237
|
1651 |
|
$this->_replica = $this->openFromPool($this->slaves, $this->slaveConfig); |
|
|
|
|
1238
|
|
|
} |
1239
|
|
|
|
1240
|
1645 |
|
return $this->_replica === null && $fallbackToPrimary ? $this : $this->_replica; |
|
|
|
|
1241
|
|
|
} |
1242
|
|
|
|
1243
|
|
|
/** |
1244
|
|
|
* Returns the currently active replica connection. |
1245
|
|
|
* If this method is called for the first time, it will try to open a replica connection when [[enableReplicas]] |
1246
|
|
|
* is true. |
1247
|
|
|
* @param bool $fallbackToPrimary whether to return the primary connection if no replica connections are |
1248
|
|
|
* available. |
1249
|
|
|
* @return Connection|null the currently active replica connection. `null` is returned if no replica connections |
1250
|
|
|
* are available and `$fallbackToPrimary` is false. |
1251
|
|
|
* @deprecated since 2.0.36. Use [[getReplica()]] instead. |
1252
|
|
|
*/ |
1253
|
|
|
public function getSlave($fallbackToPrimary = true) |
1254
|
|
|
{ |
1255
|
|
|
return $this->getReplica($fallbackToPrimary); |
1256
|
|
|
} |
1257
|
|
|
|
1258
|
|
|
|
1259
|
|
|
/** |
1260
|
|
|
* Returns the currently active primary connection. |
1261
|
|
|
* If this method is called for the first time, it will try to open a primary connection. |
1262
|
|
|
* @return Connection|null the currently active primary connection. `null` is returned if no primary connection |
1263
|
|
|
* is available. |
1264
|
|
|
* @since 2.0.36 |
1265
|
|
|
*/ |
1266
|
11 |
|
public function getPrimary() |
1267
|
|
|
{ |
1268
|
11 |
|
if ($this->_primary === false) { |
1269
|
11 |
|
$this->_primary = $this->shuffleMasters |
|
|
|
|
1270
|
2 |
|
? $this->openFromPool($this->masters, $this->masterConfig) |
|
|
|
|
1271
|
9 |
|
: $this->openFromPoolSequentially($this->masters, $this->masterConfig); |
|
|
|
|
1272
|
|
|
} |
1273
|
|
|
|
1274
|
11 |
|
return $this->_primary; |
|
|
|
|
1275
|
|
|
} |
1276
|
|
|
|
1277
|
|
|
/** |
1278
|
|
|
* Returns the currently active primary connection. |
1279
|
|
|
* If this method is called for the first time, it will try to open a primary connection. |
1280
|
|
|
* @return Connection|null the currently active primary connection. `null` is returned if no primary connection |
1281
|
|
|
* is available. |
1282
|
|
|
* @since 2.0.11 |
1283
|
|
|
* @deprecated since 2.0.36. Use [[getPrimary()]] instead. |
1284
|
|
|
*/ |
1285
|
|
|
public function getMaster() |
1286
|
|
|
{ |
1287
|
|
|
return $this->getPrimary(); |
1288
|
|
|
} |
1289
|
|
|
|
1290
|
|
|
/** |
1291
|
|
|
* Executes the provided callback by using the primary connection. |
1292
|
|
|
* |
1293
|
|
|
* This method is provided so that you can temporarily force using the primary connection to perform |
1294
|
|
|
* DB operations even if they are read queries. For example, |
1295
|
|
|
* |
1296
|
|
|
* ```php |
1297
|
|
|
* $result = $db->usePrimary(function ($db) { |
1298
|
|
|
* return $db->createCommand('SELECT * FROM user LIMIT 1')->queryOne(); |
1299
|
|
|
* }); |
1300
|
|
|
* ``` |
1301
|
|
|
* |
1302
|
|
|
* @param callable $callback a PHP callable to be executed by this method. Its signature is |
1303
|
|
|
* `function (Connection $db)`. Its return value will be returned by this method. |
1304
|
|
|
* @return mixed the return value of the callback |
1305
|
|
|
* @throws \Exception|\Throwable if there is any exception thrown from the callback |
1306
|
|
|
* @since 2.0.36 |
1307
|
|
|
*/ |
1308
|
100 |
|
public function usePrimary(callable $callback) |
1309
|
|
|
{ |
1310
|
100 |
|
if ($this->enableSlaves) { |
|
|
|
|
1311
|
100 |
|
$this->enableSlaves = false; |
|
|
|
|
1312
|
|
|
try { |
1313
|
100 |
|
$result = call_user_func($callback, $this); |
1314
|
4 |
|
} catch (\Exception $e) { |
1315
|
4 |
|
$this->enableSlaves = true; |
|
|
|
|
1316
|
4 |
|
throw $e; |
1317
|
|
|
} catch (\Throwable $e) { |
1318
|
|
|
$this->enableSlaves = true; |
|
|
|
|
1319
|
|
|
throw $e; |
1320
|
|
|
} |
1321
|
|
|
// TODO: use "finally" keyword when miminum required PHP version is >= 5.5 |
1322
|
96 |
|
$this->enableSlaves = true; |
|
|
|
|
1323
|
|
|
} else { |
1324
|
|
|
$result = call_user_func($callback, $this); |
1325
|
|
|
} |
1326
|
|
|
|
1327
|
96 |
|
return $result; |
1328
|
|
|
} |
1329
|
|
|
|
1330
|
|
|
/** |
1331
|
|
|
* Executes the provided callback by using the primary connection. |
1332
|
|
|
* |
1333
|
|
|
* This method is provided so that you can temporarily force using the primary connection to perform |
1334
|
|
|
* DB operations even if they are read queries. For example, |
1335
|
|
|
* |
1336
|
|
|
* ```php |
1337
|
|
|
* $result = $db->usePrimary(function ($db) { |
1338
|
|
|
* return $db->createCommand('SELECT * FROM user LIMIT 1')->queryOne(); |
1339
|
|
|
* }); |
1340
|
|
|
* ``` |
1341
|
|
|
* |
1342
|
|
|
* @param callable $callback a PHP callable to be executed by this method. Its signature is |
1343
|
|
|
* `function (Connection $db)`. Its return value will be returned by this method. |
1344
|
|
|
* @return mixed the return value of the callback |
1345
|
|
|
* @throws \Exception|\Throwable if there is any exception thrown from the callback |
1346
|
|
|
* @deprecated since 2.0.36. Use [[usePrimary()]] instead. |
1347
|
|
|
*/ |
1348
|
|
|
public function useMaster(callable $callback) |
1349
|
|
|
{ |
1350
|
|
|
return $this->usePrimary($callback); |
1351
|
|
|
} |
1352
|
|
|
|
1353
|
|
|
/** |
1354
|
|
|
* Opens the connection to a server in the pool. |
1355
|
|
|
* |
1356
|
|
|
* This method implements load balancing and failover among the given list of the servers. |
1357
|
|
|
* Connections will be tried in random order. |
1358
|
|
|
* For details about the failover behavior, see [[openFromPoolSequentially]]. |
1359
|
|
|
* |
1360
|
|
|
* @param array $pool the list of connection configurations in the server pool |
1361
|
|
|
* @param array $sharedConfig the configuration common to those given in `$pool`. |
1362
|
|
|
* @return Connection the opened DB connection, or `null` if no server is available |
1363
|
|
|
* @throws InvalidConfigException if a configuration does not specify "dsn" |
1364
|
|
|
* @see openFromPoolSequentially |
1365
|
|
|
*/ |
1366
|
1651 |
|
protected function openFromPool(array $pool, array $sharedConfig) |
1367
|
|
|
{ |
1368
|
1651 |
|
shuffle($pool); |
1369
|
1651 |
|
return $this->openFromPoolSequentially($pool, $sharedConfig); |
1370
|
|
|
} |
1371
|
|
|
|
1372
|
|
|
/** |
1373
|
|
|
* Opens the connection to a server in the pool. |
1374
|
|
|
* |
1375
|
|
|
* This method implements failover among the given list of servers. |
1376
|
|
|
* Connections will be tried in sequential order. The first successful connection will return. |
1377
|
|
|
* |
1378
|
|
|
* If [[serverStatusCache]] is configured, this method will cache information about |
1379
|
|
|
* unreachable servers and does not try to connect to these for the time configured in [[serverRetryInterval]]. |
1380
|
|
|
* This helps to keep the application stable when some servers are unavailable. Avoiding |
1381
|
|
|
* connection attempts to unavailable servers saves time when the connection attempts fail due to timeout. |
1382
|
|
|
* |
1383
|
|
|
* If none of the servers are available the status cache is ignored and connection attempts are made to all |
1384
|
|
|
* servers (Since version 2.0.35). This is to avoid downtime when all servers are unavailable for a short time. |
1385
|
|
|
* After a successful connection attempt the server is marked as avaiable again. |
1386
|
|
|
* |
1387
|
|
|
* @param array $pool the list of connection configurations in the server pool |
1388
|
|
|
* @param array $sharedConfig the configuration common to those given in `$pool`. |
1389
|
|
|
* @return Connection the opened DB connection, or `null` if no server is available |
1390
|
|
|
* @throws InvalidConfigException if a configuration does not specify "dsn" |
1391
|
|
|
* @since 2.0.11 |
1392
|
|
|
* @see openFromPool |
1393
|
|
|
* @see serverStatusCache |
1394
|
|
|
*/ |
1395
|
1659 |
|
protected function openFromPoolSequentially(array $pool, array $sharedConfig) |
1396
|
|
|
{ |
1397
|
1659 |
|
if (empty($pool)) { |
1398
|
1639 |
|
return null; |
1399
|
|
|
} |
1400
|
|
|
|
1401
|
21 |
|
if (!isset($sharedConfig['class'])) { |
1402
|
21 |
|
$sharedConfig['class'] = get_class($this); |
1403
|
|
|
} |
1404
|
|
|
|
1405
|
21 |
|
$cache = is_string($this->serverStatusCache) ? Yii::$app->get($this->serverStatusCache, false) : $this->serverStatusCache; |
1406
|
|
|
|
1407
|
21 |
|
foreach ($pool as $i => $config) { |
1408
|
21 |
|
$pool[$i] = $config = array_merge($sharedConfig, $config); |
1409
|
21 |
|
if (empty($config['dsn'])) { |
1410
|
6 |
|
throw new InvalidConfigException('The "dsn" option must be specified.'); |
1411
|
|
|
} |
1412
|
|
|
|
1413
|
15 |
|
$key = [__METHOD__, $config['dsn']]; |
1414
|
15 |
|
if ($cache instanceof CacheInterface && $cache->get($key)) { |
1415
|
|
|
// should not try this dead server now |
1416
|
|
|
continue; |
1417
|
|
|
} |
1418
|
|
|
|
1419
|
|
|
/* @var $db Connection */ |
1420
|
15 |
|
$db = Yii::createObject($config); |
1421
|
|
|
|
1422
|
|
|
try { |
1423
|
15 |
|
$db->open(); |
1424
|
15 |
|
return $db; |
1425
|
8 |
|
} catch (\Exception $e) { |
1426
|
8 |
|
Yii::warning("Connection ({$config['dsn']}) failed: " . $e->getMessage(), __METHOD__); |
1427
|
8 |
|
if ($cache instanceof CacheInterface) { |
1428
|
|
|
// mark this server as dead and only retry it after the specified interval |
1429
|
4 |
|
$cache->set($key, 1, $this->serverRetryInterval); |
1430
|
|
|
} |
1431
|
|
|
// exclude server from retry below |
1432
|
8 |
|
unset($pool[$i]); |
1433
|
|
|
} |
1434
|
|
|
} |
1435
|
|
|
|
1436
|
8 |
|
if ($cache instanceof CacheInterface) { |
1437
|
|
|
// if server status cache is enabled and no server is available |
1438
|
|
|
// ignore the cache and try to connect anyway |
1439
|
|
|
// $pool now only contains servers we did not already try in the loop above |
1440
|
4 |
|
foreach ($pool as $config) { |
1441
|
|
|
|
1442
|
|
|
/* @var $db Connection */ |
1443
|
|
|
$db = Yii::createObject($config); |
1444
|
|
|
try { |
1445
|
|
|
$db->open(); |
1446
|
|
|
} catch (\Exception $e) { |
1447
|
|
|
Yii::warning("Connection ({$config['dsn']}) failed: " . $e->getMessage(), __METHOD__); |
1448
|
|
|
continue; |
1449
|
|
|
} |
1450
|
|
|
|
1451
|
|
|
// mark this server as available again after successful connection |
1452
|
|
|
$cache->delete([__METHOD__, $config['dsn']]); |
1453
|
|
|
|
1454
|
|
|
return $db; |
1455
|
|
|
} |
1456
|
|
|
} |
1457
|
|
|
|
1458
|
8 |
|
return null; |
1459
|
|
|
} |
1460
|
|
|
|
1461
|
|
|
/** |
1462
|
|
|
* Close the connection before serializing. |
1463
|
|
|
* @return array |
1464
|
|
|
*/ |
1465
|
19 |
|
public function __sleep() |
1466
|
|
|
{ |
1467
|
19 |
|
$fields = (array) $this; |
1468
|
|
|
|
1469
|
19 |
|
unset($fields['pdo']); |
1470
|
19 |
|
unset($fields["\000" . __CLASS__ . "\000" . '_primary']); |
1471
|
19 |
|
unset($fields["\000" . __CLASS__ . "\000" . '_replica']); |
1472
|
19 |
|
unset($fields["\000" . __CLASS__ . "\000" . '_transaction']); |
1473
|
19 |
|
unset($fields["\000" . __CLASS__ . "\000" . '_schema']); |
1474
|
|
|
|
1475
|
19 |
|
return array_keys($fields); |
1476
|
|
|
} |
1477
|
|
|
|
1478
|
|
|
/** |
1479
|
|
|
* Reset the connection after cloning. |
1480
|
|
|
*/ |
1481
|
7 |
|
public function __clone() |
1482
|
|
|
{ |
1483
|
7 |
|
parent::__clone(); |
1484
|
|
|
|
1485
|
7 |
|
$this->_primary = false; |
1486
|
7 |
|
$this->_replica = false; |
1487
|
7 |
|
$this->_schema = null; |
1488
|
7 |
|
$this->_transaction = null; |
1489
|
7 |
|
if (strncmp($this->dsn, 'sqlite::memory:', 15) !== 0) { |
1490
|
|
|
// reset PDO connection, unless its sqlite in-memory, which can only have one connection |
1491
|
6 |
|
$this->pdo = null; |
1492
|
|
|
} |
1493
|
7 |
|
} |
1494
|
|
|
} |
1495
|
|
|
|
This property has been deprecated. The supplier of the class has supplied an explanatory message.
The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.