Connection.php 20.8 KB
Newer Older
w  
Qiang Xue committed
1 2 3 4 5 6 7 8 9
<?php
/**
 * Connection class file
 *
 * @link http://www.yiiframework.com/
 * @copyright Copyright &copy; 2008-2012 Yii Software LLC
 * @license http://www.yiiframework.com/license/
 */

w  
Qiang Xue committed
10 11
namespace yii\db\dao;

w  
Qiang Xue committed
12 13
use yii\db\Exception;

w  
Qiang Xue committed
14
/**
w  
Qiang Xue committed
15
 * Connection represents a connection to a database via [PDO](http://www.php.net/manual/en/ref.pdo.php).
w  
Qiang Xue committed
16
 *
w  
Qiang Xue committed
17 18 19
 * Connection works together with [[Command]], [[DataReader]] and [[Transaction]]
 * to provide data access to various DBMS in a common set of APIs. They are a thin wrapper
 * of the [[PDO PHP extension]](http://www.php.net/manual/en/ref.pdo.php).
w  
Qiang Xue committed
20
 *
w  
Qiang Xue committed
21 22
 * To establish a DB connection, set [[dsn]], [[username]] and [[password]], and then
 * call [[open]] or set [[active]] to be true.
w  
Qiang Xue committed
23 24
 *
 * The following example shows how to create a Connection instance and establish
w  
Qiang Xue committed
25
 * the DB connection:
w  
Qiang Xue committed
26
 *
w  
Qiang Xue committed
27
 * ~~~
Qiang Xue committed
28 29 30 31 32
 * $connection = \yii\db\dao\Connection::newInstance(array(
 * 	'dsn' => $dsn,
 * 	'username' => $username,
 * 	'password' => $password,
 * ));
Qiang Xue committed
33
 * $connection->active = true;  // same as: $connection->open();
w  
Qiang Xue committed
34 35
 * ~~~
 *
Qiang Xue committed
36
 * After the DB connection is established, one can execute SQL statements like the following:
w  
Qiang Xue committed
37 38 39
 *
 * ~~~
 * $command = $connection->createCommand('SELECT * FROM tbl_post');
Qiang Xue committed
40 41 42
 * $posts = $command->queryAll();
 * $command = $connection->createCommand('UPDATE tbl_post SET status=1');
 * $command->execute();
w  
Qiang Xue committed
43 44
 * ~~~
 *
Qiang Xue committed
45 46 47
 * One can also do prepared SQL execution and bind parameters to the prepared SQL.
 * When the parameters are coming from user input, you should use this approach
 * to prevent SQL injection attacks. The following is an example:
w  
Qiang Xue committed
48
 *
w  
Qiang Xue committed
49 50 51 52 53
 * ~~~
 * $command = $connection->createCommand('SELECT * FROM tbl_post WHERE id=:id');
 * $command->bindValue(':id', $_GET['id']);
 * $post = $command->query();
 * ~~~
w  
Qiang Xue committed
54
 *
Qiang Xue committed
55 56 57 58
 * For more information about how to perform various DB queries, please refer to [[Command]].
 *
 * If the underlying DBMS supports transactions, you can perform transactional SQL queries
 * like the following:
w  
Qiang Xue committed
59
 *
w  
Qiang Xue committed
60 61 62
 * ~~~
 * $transaction = $connection->beginTransaction();
 * try {
Qiang Xue committed
63 64 65 66
 *	 $connection->createCommand($sql1)->execute();
 *	 $connection->createCommand($sql2)->execute();
 *	 // ... executing other SQL statements ...
 *	 $transaction->commit();
Qiang Xue committed
67
 * } catch(Exception $e) {
Qiang Xue committed
68
 *	 $transaction->rollBack();
w  
Qiang Xue committed
69
 * }
w  
Qiang Xue committed
70
 * ~~~
w  
Qiang Xue committed
71
 *
Qiang Xue committed
72 73
 * Connection is often used as an [[\yii\base\ApplicationComponent|application component]] and configured in the application
 * configuration like the following:
w  
Qiang Xue committed
74 75
 *
 * ~~~
w  
Qiang Xue committed
76
 * array(
Qiang Xue committed
77 78 79 80 81 82 83 84 85
 *	 'components' => array(
 *		 'db' => array(
 *			 'class' => '\yii\db\dao\Connection',
 *			 'dsn' => 'mysql:host=127.0.0.1;dbname=demo',
 *			 'username' => 'root',
 *			 'password' => '',
 *			 'charset' => 'utf8',
 *		 ),
 *	 ),
w  
Qiang Xue committed
86
 * )
w  
Qiang Xue committed
87
 * ~~~
w  
Qiang Xue committed
88
 *
Qiang Xue committed
89 90
 * @property boolean $active Whether the DB connection is established.
 * @property Transaction $currentTransaction The currently active transaction. Null if no active transaction.
Qiang Xue committed
91
 * @property Driver $driver The database driver for the current connection.
Qiang Xue committed
92 93 94 95 96 97
 * @property QueryBuilder $queryBuilder The query builder.
 * @property string $lastInsertID The row ID of the last row inserted, or the last value retrieved from the sequence object.
 * @property string $driverName Name of the DB driver currently being used.
 * @property string $clientVersion The version information of the DB driver.
 * @property array $stats The statistical results of SQL executions.
 *
w  
Qiang Xue committed
98 99 100 101 102 103
 * @author Qiang Xue <qiang.xue@gmail.com>
 * @since 2.0
 */
class Connection extends \yii\base\ApplicationComponent
{
	/**
w  
Qiang Xue committed
104 105 106
	 * @var string the Data Source Name, or DSN, contains the information required to connect to the database.
	 * Please refer to the [PHP manual](http://www.php.net/manual/en/function.PDO-construct.php) on
	 * the format of the DSN string.
Qiang Xue committed
107
	 * @see charset
w  
Qiang Xue committed
108 109 110 111 112 113 114 115 116 117
	 */
	public $dsn;
	/**
	 * @var string the username for establishing DB connection. Defaults to empty string.
	 */
	public $username = '';
	/**
	 * @var string the password for establishing DB connection. Defaults to empty string.
	 */
	public $password = '';
w  
Qiang Xue committed
118 119 120 121 122 123 124 125 126 127 128 129 130 131
	/**
	 * @var array PDO attributes (name=>value) that should be set when calling [[open]]
	 * to establish a DB connection. Please refer to the
	 * [PHP manual](http://www.php.net/manual/en/function.PDO-setAttribute.php) for
	 * details about available attributes.
	 */
	public $attributes;
	/**
	 * @var \PDO the PHP PDO instance associated with this DB connection.
	 * This property is mainly managed by [[open]] and [[close]] methods.
	 * When a DB connection is active, this property will represent a PDO instance;
	 * otherwise, it will be null.
	 */
	public $pdo;
w  
Qiang Xue committed
132 133
	/**
	 * @var integer number of seconds that table metadata can remain valid in cache.
w  
Qiang Xue committed
134 135 136 137 138
	 * Defaults to -1, meaning schema caching is disabled.
	 * Use 0 to indicate that the cached data will never expire.
	 *
	 * Note that in order to enable schema caching, a valid cache component as specified
	 * by [[schemaCacheID]] must be enabled.
w  
Qiang Xue committed
139
	 * @see schemaCachingExclude
w  
Qiang Xue committed
140
	 * @see schemaCacheID
w  
Qiang Xue committed
141
	 */
w  
Qiang Xue committed
142
	public $schemaCachingDuration = -1;
w  
Qiang Xue committed
143 144
	/**
	 * @var array list of tables whose metadata should NOT be cached. Defaults to empty array.
Qiang Xue committed
145
	 * The table names may contain schema prefix, if any. Do not quote the table names.
w  
Qiang Xue committed
146 147 148 149 150
	 * @see schemaCachingDuration
	 */
	public $schemaCachingExclude = array();
	/**
	 * @var string the ID of the cache application component that is used to cache the table metadata.
w  
Qiang Xue committed
151 152
	 * Defaults to 'cache'.
	 * @see schemaCachingDuration
w  
Qiang Xue committed
153 154 155 156
	 */
	public $schemaCacheID = 'cache';
	/**
	 * @var integer number of seconds that query results can remain valid in cache.
w  
Qiang Xue committed
157 158
	 * Defaults to -1, meaning query caching is disabled.
	 * Use 0 to indicate that the cached data will never expire.
w  
Qiang Xue committed
159
	 *
w  
Qiang Xue committed
160 161
	 * Note that in order to enable query caching, a valid cache component as specified
	 * by [[queryCacheID]] must be enabled.
w  
Qiang Xue committed
162
	 *
w  
Qiang Xue committed
163 164
	 * The method [[cache()]] is provided as a convenient way of setting this property
	 * and [[queryCachingDependency]] on the fly.
w  
Qiang Xue committed
165 166 167 168 169
	 *
	 * @see cache
	 * @see queryCachingDependency
	 * @see queryCacheID
	 */
w  
Qiang Xue committed
170
	public $queryCachingDuration = -1;
w  
Qiang Xue committed
171
	/**
Qiang Xue committed
172
	 * @var \yii\caching\Dependency the dependency that will be used when saving query results into cache.
w  
Qiang Xue committed
173
	 * Defaults to null, meaning no dependency.
w  
Qiang Xue committed
174 175 176 177
	 * @see queryCachingDuration
	 */
	public $queryCachingDependency;
	/**
w  
Qiang Xue committed
178 179
	 * @var integer the number of SQL statements that need to be cached when they are executed next.
	 * Defaults to 0, meaning the query result of the next SQL statement will NOT be cached.
w  
Qiang Xue committed
180 181
	 * Note that each time after executing a SQL statement (whether executed on DB server or fetched from
	 * query cache), this property will be reduced by 1 until 0.
w  
Qiang Xue committed
182
	 * @see queryCachingDuration
w  
Qiang Xue committed
183 184 185 186
	 */
	public $queryCachingCount = 0;
	/**
	 * @var string the ID of the cache application component that is used for query caching.
w  
Qiang Xue committed
187 188
	 * Defaults to 'cache'.
	 * @see queryCachingDuration
w  
Qiang Xue committed
189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204
	 */
	public $queryCacheID = 'cache';
	/**
	 * @var string the charset used for database connection. The property is only used
	 * for MySQL and PostgreSQL databases. Defaults to null, meaning using default charset
	 * as specified by the database.
	 *
	 * Note that if you're using GBK or BIG5 then it's highly recommended to
	 * update to PHP 5.3.6+ and to specify charset via DSN like
	 * 'mysql:dbname=mydatabase;host=127.0.0.1;charset=GBK;'.
	 */
	public $charset;
	/**
	 * @var boolean whether to turn on prepare emulation. Defaults to false, meaning PDO
	 * will use the native prepare support if available. For some databases (such as MySQL),
	 * this may need to be set true so that PDO can emulate the prepare support to bypass
Qiang Xue committed
205 206
	 * the buggy native prepare support.
	 * The default value is null, which means the PDO ATTR_EMULATE_PREPARES value will not be changed.
w  
Qiang Xue committed
207 208 209
	 */
	public $emulatePrepare;
	/**
w  
Qiang Xue committed
210
	 * @var boolean whether to enable profiling for the SQL statements being executed.
w  
Qiang Xue committed
211 212
	 * Defaults to false. This should be mainly enabled and used during development
	 * to find out the bottleneck of SQL executions.
w  
Qiang Xue committed
213
	 * @see getStats
w  
Qiang Xue committed
214 215 216
	 */
	public $enableProfiling = false;
	/**
Qiang Xue committed
217 218
	 * @var string the default prefix for table names. Defaults to null, meaning not using table prefix.
	 * By setting this property, any token like '{{TableName}}' in [[Command::sql]] will
w  
Qiang Xue committed
219 220
	 * be replaced with 'prefixTableName', where 'prefix' refers to this property value.
	 * For example, '{{post}}' becomes 'tbl_post', if 'tbl_' is set as the table prefix.
Qiang Xue committed
221 222 223
	 *
	 * Note that if you set this property to be an empty string, then '{{post}}' will be replaced
	 * with 'post'.
w  
Qiang Xue committed
224 225 226
	 */
	public $tablePrefix;
	/**
Qiang Xue committed
227
	 * @var array a list of SQL statements that should be executed right after the DB connection is established.
w  
Qiang Xue committed
228 229 230
	 */
	public $initSQLs;
	/**
Qiang Xue committed
231
	 * @var array mapping between PDO driver names and [[Driver]] classes.
w  
Qiang Xue committed
232
	 * The keys of the array are PDO driver names while the values the corresponding
Qiang Xue committed
233
	 * driver class name or configuration. Please refer to [[\Yii::createObject]] for
w  
Qiang Xue committed
234 235
	 * details on how to specify a configuration.
	 *
Qiang Xue committed
236
	 * This property is mainly used by [[getDriver()]] when fetching the database schema information.
Qiang Xue committed
237
	 * You normally do not need to set this property unless you want to use your own
Qiang Xue committed
238 239 240 241 242 243 244 245 246 247 248 249
	 * [[Driver]] class to support DBMS that is not supported by Yii.
	 */
	public $driverMap = array(
		'pgsql' => '\yii\db\dao\pgsql\Driver', // PostgreSQL
		'mysqli' => '\yii\db\dao\mysql\Driver', // MySQL
		'mysql' => '\yii\db\dao\mysql\Driver', // MySQL
		'sqlite' => '\yii\db\dao\sqlite\Driver', // sqlite 3
		'sqlite2' => '\yii\db\dao\sqlite\Driver', // sqlite 2
		'mssql' => '\yii\db\dao\mssql\Driver', // Mssql driver on windows hosts
		'dblib' => '\yii\db\dao\mssql\Driver', // dblib drivers on linux (and maybe others os) hosts
		'sqlsrv' => '\yii\db\dao\mssql\Driver', // Mssql
		'oci' => '\yii\db\dao\oci\Driver', // Oracle driver
w  
Qiang Xue committed
250
	);
Qiang Xue committed
251 252 253
	/**
	 * @var Transaction the currently active transaction
	 */
w  
Qiang Xue committed
254
	private $_transaction;
Qiang Xue committed
255
	/**
Qiang Xue committed
256
	 * @var Driver the database driver
Qiang Xue committed
257
	 */
Qiang Xue committed
258
	private $_driver;
w  
Qiang Xue committed
259 260

	/**
w  
Qiang Xue committed
261
	 * Closes the connection when this component is being serialized.
w  
Qiang Xue committed
262 263 264 265 266 267 268 269 270 271 272 273 274 275 276
	 * @return array
	 */
	public function __sleep()
	{
		$this->close();
		return array_keys(get_object_vars($this));
	}

	/**
	 * Returns a list of available PDO drivers.
	 * @return array list of available PDO drivers
	 * @see http://www.php.net/manual/en/function.PDO-getAvailableDrivers.php
	 */
	public static function getAvailableDrivers()
	{
w  
Qiang Xue committed
277
		return \PDO::getAvailableDrivers();
w  
Qiang Xue committed
278 279 280
	}

	/**
w  
Qiang Xue committed
281
	 * Returns a value indicating whether the DB connection is established.
w  
Qiang Xue committed
282 283 284 285
	 * @return boolean whether the DB connection is established
	 */
	public function getActive()
	{
w  
Qiang Xue committed
286
		return $this->pdo !== null;
w  
Qiang Xue committed
287 288 289
	}

	/**
w  
Qiang Xue committed
290 291 292
	 * Opens or closes the DB connection.
	 * @param boolean $value whether to open or close the DB connection
	 * @throws Exception if there is any error when establishing the connection
w  
Qiang Xue committed
293 294 295
	 */
	public function setActive($value)
	{
w  
Qiang Xue committed
296
		$value ? $this->open() : $this->close();
w  
Qiang Xue committed
297 298 299 300
	}

	/**
	 * Sets the parameters about query caching.
w  
Qiang Xue committed
301 302 303
	 * This method is provided as a shortcut to setting three properties that are related
	 * with query caching: [[queryCachingDuration]], [[queryCachingDependency]] and
	 * [[queryCachingCount]].
w  
Qiang Xue committed
304
	 * @param integer $duration the number of seconds that query results may remain valid in cache.
w  
Qiang Xue committed
305
	 * See [[queryCachingDuration]] for more details.
Qiang Xue committed
306
	 * @param \yii\caching\Dependency $dependency the dependency for the cached query result.
w  
Qiang Xue committed
307
	 * See [[queryCachingDependency]] for more details.
Qiang Xue committed
308
	 * @param integer $queryCount the number of SQL queries that need to be cached after calling this method.
w  
Qiang Xue committed
309
	 * See [[queryCachingCount]] for more details.
w  
Qiang Xue committed
310 311
	 * @return Connection the connection instance itself.
	 */
w  
Qiang Xue committed
312
	public function cache($duration = 300, $dependency = null, $queryCount = 1)
w  
Qiang Xue committed
313 314 315 316 317 318 319 320
	{
		$this->queryCachingDuration = $duration;
		$this->queryCachingDependency = $dependency;
		$this->queryCachingCount = $queryCount;
		return $this;
	}

	/**
w  
Qiang Xue committed
321 322 323
	 * Establishes a DB connection.
	 * It does nothing if a DB connection has already been established.
	 * @throws Exception if connection fails
w  
Qiang Xue committed
324
	 */
w  
Qiang Xue committed
325
	public function open()
w  
Qiang Xue committed
326
	{
w  
Qiang Xue committed
327 328 329 330 331
		if ($this->pdo === null) {
			if (empty($this->dsn)) {
				throw new Exception('Connection.dsn cannot be empty.');
			}
			try {
Qiang Xue committed
332
				\Yii::trace('Opening DB connection: ' . $this->dsn, __CLASS__);
w  
Qiang Xue committed
333 334
				$this->pdo = $this->createPdoInstance();
				$this->initConnection($this->pdo);
w  
Qiang Xue committed
335
			}
w  
Qiang Xue committed
336
			catch (\PDOException $e) {
Qiang Xue committed
337 338 339
				\Yii::error("Failed to open DB connection ({$this->dsn}): " . $e->getMessage(), __CLASS__);
				$message = YII_DEBUG ? 'Failed to open DB connection: ' . $e->getMessage() : 'Failed to open DB connection.';
				throw new Exception($message, (int)$e->getCode(), $e->errorInfo);
w  
Qiang Xue committed
340 341 342 343 344 345 346 347
			}
		}
	}

	/**
	 * Closes the currently active DB connection.
	 * It does nothing if the connection is already closed.
	 */
w  
Qiang Xue committed
348
	public function close()
w  
Qiang Xue committed
349
	{
w  
Qiang Xue committed
350
		if ($this->pdo !== null) {
Qiang Xue committed
351
			\Yii::trace('Closing DB connection: ' . $this->dsn, __CLASS__);
w  
Qiang Xue committed
352
			$this->pdo = null;
Qiang Xue committed
353
			$this->_driver = null;
Qiang Xue committed
354
			$this->_transaction = null;
w  
Qiang Xue committed
355
		}
w  
Qiang Xue committed
356 357 358 359
	}

	/**
	 * Creates the PDO instance.
w  
Qiang Xue committed
360
	 * This method is called by [[open]] to establish a DB connection.
Qiang Xue committed
361 362
	 * The default implementation will create a PHP PDO instance.
	 * You may override this method if the default PDO needs to be adapted for certain DBMS.
w  
Qiang Xue committed
363
	 * @return \PDO the pdo instance
w  
Qiang Xue committed
364 365 366
	 */
	protected function createPdoInstance()
	{
w  
Qiang Xue committed
367 368 369 370
		$pdoClass = '\PDO';
		if (($pos = strpos($this->dsn, ':')) !== false) {
			$driver = strtolower(substr($this->dsn, 0, $pos));
			if ($driver === 'mssql' || $driver === 'dblib' || $driver === 'sqlsrv') {
Qiang Xue committed
371
				$pdoClass = 'mssql\PDO';
w  
Qiang Xue committed
372
			}
w  
Qiang Xue committed
373
		}
w  
Qiang Xue committed
374
		return new $pdoClass($this->dsn, $this->username, $this->password, $this->attributes);
w  
Qiang Xue committed
375 376 377
	}

	/**
w  
Qiang Xue committed
378 379
	 * Initializes the DB connection.
	 * This method is invoked right after the DB connection is established.
Qiang Xue committed
380
	 * The default implementation sets the database [[charset]] and executes SQLs specified
w  
Qiang Xue committed
381
	 * in [[initSQLs]].
w  
Qiang Xue committed
382
	 */
w  
Qiang Xue committed
383
	protected function initConnection()
w  
Qiang Xue committed
384
	{
w  
Qiang Xue committed
385 386 387
		$this->pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
		if ($this->emulatePrepare !== null && constant('\PDO::ATTR_EMULATE_PREPARES')) {
			$this->pdo->setAttribute(\PDO::ATTR_EMULATE_PREPARES, $this->emulatePrepare);
w  
Qiang Xue committed
388
		}
Qiang Xue committed
389 390
		if ($this->charset !== null && in_array($this->getDriverName(), array('pgsql', 'mysql', 'mysqli'))) {
			$this->pdo->exec('SET NAMES ' . $this->pdo->quote($this->charset));
w  
Qiang Xue committed
391 392 393 394 395
		}
		if (!empty($this->initSQLs)) {
			foreach ($this->initSQLs as $sql) {
				$this->pdo->exec($sql);
			}
w  
Qiang Xue committed
396 397 398 399 400
		}
	}

	/**
	 * Creates a command for execution.
Qiang Xue committed
401 402
	 * @param string $sql the SQL statement to be executed
	 * @param array $params the parameters to be bound to the SQL statement
w  
Qiang Xue committed
403
	 * @return Command the DB command
w  
Qiang Xue committed
404
	 */
Qiang Xue committed
405
	public function createCommand($sql = null, $params = array())
w  
Qiang Xue committed
406
	{
w  
Qiang Xue committed
407
		$this->open();
Qiang Xue committed
408
		return new Command($this, $sql, $params);
w  
Qiang Xue committed
409 410 411 412
	}

	/**
	 * Returns the currently active transaction.
w  
Qiang Xue committed
413
	 * @return Transaction the currently active transaction. Null if no active transaction.
w  
Qiang Xue committed
414 415 416
	 */
	public function getCurrentTransaction()
	{
Qiang Xue committed
417
		if ($this->_transaction !== null && $this->_transaction->active) {
w  
Qiang Xue committed
418
			return $this->_transaction;
Qiang Xue committed
419
		} else {
Qiang Xue committed
420
			return null;
w  
Qiang Xue committed
421 422 423 424 425
		}
	}

	/**
	 * Starts a transaction.
Qiang Xue committed
426
	 * @return Transaction the transaction initiated
w  
Qiang Xue committed
427 428 429
	 */
	public function beginTransaction()
	{
w  
Qiang Xue committed
430 431 432 433
		\Yii::trace('Starting transaction', __CLASS__);
		$this->open();
		$this->pdo->beginTransaction();
		return $this->_transaction = new Transaction($this);
w  
Qiang Xue committed
434 435 436
	}

	/**
Qiang Xue committed
437
	 * Returns the metadata information for the underlying database.
Qiang Xue committed
438
	 * @return Driver the metadata information for the underlying database.
w  
Qiang Xue committed
439
	 */
Qiang Xue committed
440
	public function getDriver()
w  
Qiang Xue committed
441
	{
Qiang Xue committed
442 443
		if ($this->_driver !== null) {
			return $this->_driver;
Qiang Xue committed
444
		} else {
w  
Qiang Xue committed
445
			$driver = $this->getDriverName();
Qiang Xue committed
446 447
			if (isset($this->driverMap[$driver])) {
				return $this->_driver = \Yii::createObject($this->driverMap[$driver], $this);
Qiang Xue committed
448
			} else {
Qiang Xue committed
449
				throw new Exception("Connection does not support reading metadata for '$driver' database.");
w  
Qiang Xue committed
450
			}
w  
Qiang Xue committed
451 452 453
		}
	}

Qiang Xue committed
454 455 456 457
	/**
	 * Returns the query builder for the current DB connection.
	 * @return QueryBuilder the query builder for the current DB connection.
	 */
w  
Qiang Xue committed
458 459
	public function getQueryBuilder()
	{
Qiang Xue committed
460
		return $this->getDriver()->getQueryBuilder();
w  
Qiang Xue committed
461 462
	}

w  
Qiang Xue committed
463 464 465 466 467 468 469 470
	/**
	 * Returns the ID of the last inserted row or sequence value.
	 * @param string $sequenceName name of the sequence object (required by some DBMS)
	 * @return string the row ID of the last row inserted, or the last value retrieved from the sequence object
	 * @see http://www.php.net/manual/en/function.PDO-lastInsertId.php
	 */
	public function getLastInsertID($sequenceName = '')
	{
w  
Qiang Xue committed
471 472
		$this->open();
		return $this->pdo->lastInsertId($sequenceName);
w  
Qiang Xue committed
473 474 475 476
	}

	/**
	 * Quotes a string value for use in a query.
Qiang Xue committed
477
	 * Note that if the parameter is not a string, it will be returned without change.
w  
Qiang Xue committed
478 479 480 481 482 483
	 * @param string $str string to be quoted
	 * @return string the properly quoted string
	 * @see http://www.php.net/manual/en/function.PDO-quote.php
	 */
	public function quoteValue($str)
	{
w  
Qiang Xue committed
484
		if (!is_string($str)) {
w  
Qiang Xue committed
485
			return $str;
w  
Qiang Xue committed
486
		}
w  
Qiang Xue committed
487

w  
Qiang Xue committed
488 489
		$this->open();
		if (($value = $this->pdo->quote($str)) !== false) {
w  
Qiang Xue committed
490
			return $value;
Qiang Xue committed
491
		} else { // the driver doesn't support quote (e.g. oci)
w  
Qiang Xue committed
492
			return "'" . addcslashes(str_replace("'", "''", $str), "\000\n\r\\\032") . "'";
w  
Qiang Xue committed
493
		}
w  
Qiang Xue committed
494 495 496 497 498 499
	}

	/**
	 * Quotes a table name for use in a query.
	 * If the table name contains schema prefix, the prefix will also be properly quoted.
	 * @param string $name table name
Qiang Xue committed
500
	 * @param boolean $simple if this is true, then the method will assume $name is a table name without schema prefix.
w  
Qiang Xue committed
501 502
	 * @return string the properly quoted table name
	 */
w  
Qiang Xue committed
503
	public function quoteTableName($name, $simple = false)
w  
Qiang Xue committed
504
	{
Qiang Xue committed
505
		return $simple ? $this->getDriver()->quoteSimpleTableName($name) : $this->getDriver()->quoteTableName($name);
w  
Qiang Xue committed
506 507 508 509
	}

	/**
	 * Quotes a column name for use in a query.
Qiang Xue committed
510
	 * If the column name contains table prefix, the prefix will also be properly quoted.
w  
Qiang Xue committed
511
	 * @param string $name column name
Qiang Xue committed
512
	 * @param boolean $simple if this is true, then the method will assume $name is a column name without table prefix.
w  
Qiang Xue committed
513 514
	 * @return string the properly quoted column name
	 */
w  
Qiang Xue committed
515
	public function quoteColumnName($name, $simple = false)
w  
Qiang Xue committed
516
	{
Qiang Xue committed
517
		return $simple ? $this->getDriver()->quoteSimpleColumnName($name) : $this->getDriver()->quoteColumnName($name);
w  
Qiang Xue committed
518 519
	}

Qiang Xue committed
520 521 522 523 524 525 526 527 528 529 530 531 532
	/**
	 * Prefixes table names in a SQL statement with [[tablePrefix]].
	 * By calling this method, tokens like '{{TableName}}' in the given SQL statement will
	 * be replaced with 'prefixTableName', where 'prefix' refers to [[tablePrefix]].
	 * Note that if [[tablePrefix]] is null, this method will do nothing.
	 * @param string $sql the SQL statement whose table names need to be prefixed with [[tablePrefix]].
	 * @return string the expanded SQL statement
	 * @see tablePrefix
	 */
	public function expandTablePrefix($sql)
	{
		if ($this->tablePrefix !== null && strpos($sql, '{{') !== false) {
			return preg_replace('/{{(.*?)}}/', $this->tablePrefix . '\1', $sql);
Qiang Xue committed
533
		} else {
Qiang Xue committed
534 535 536 537
			return $sql;
		}
	}

w  
Qiang Xue committed
538
	/**
w  
Qiang Xue committed
539 540
	 * Determines the PDO type for the give PHP data type.
	 * @param string $type The PHP type (obtained by `gettype()` call).
w  
Qiang Xue committed
541
	 * @return integer the corresponding PDO type
w  
Qiang Xue committed
542
	 * @see http://www.php.net/manual/en/pdo.constants.php
w  
Qiang Xue committed
543 544 545
	 */
	public function getPdoType($type)
	{
w  
Qiang Xue committed
546
		static $typeMap = array(
w  
Qiang Xue committed
547 548 549 550
			'boolean' => \PDO::PARAM_BOOL,
			'integer' => \PDO::PARAM_INT,
			'string' => \PDO::PARAM_STR,
			'NULL' => \PDO::PARAM_NULL,
w  
Qiang Xue committed
551
		);
w  
Qiang Xue committed
552
		return isset($typeMap[$type]) ? $typeMap[$type] : \PDO::PARAM_STR;
w  
Qiang Xue committed
553 554 555
	}

	/**
w  
Qiang Xue committed
556
	 * Returns the name of the DB driver for the current [[dsn]].
w  
Qiang Xue committed
557 558 559 560
	 * @return string name of the DB driver
	 */
	public function getDriverName()
	{
w  
Qiang Xue committed
561 562
		if (($pos = strpos($this->dsn, ':')) !== false) {
			return strtolower(substr($this->dsn, 0, $pos));
Qiang Xue committed
563
		} else {
w  
Qiang Xue committed
564
			return strtolower($this->getAttribute(\PDO::ATTR_DRIVER_NAME));
w  
Qiang Xue committed
565
		}
w  
Qiang Xue committed
566 567 568 569 570 571 572 573 574 575
	}

	/**
	 * Obtains a specific DB connection attribute information.
	 * @param integer $name the attribute to be queried
	 * @return mixed the corresponding attribute information
	 * @see http://www.php.net/manual/en/function.PDO-getAttribute.php
	 */
	public function getAttribute($name)
	{
w  
Qiang Xue committed
576 577
		$this->open();
		return $this->pdo->getAttribute($name);
w  
Qiang Xue committed
578 579 580 581 582 583 584 585 586 587
	}

	/**
	 * Sets an attribute on the database connection.
	 * @param integer $name the attribute to be set
	 * @param mixed $value the attribute value
	 * @see http://www.php.net/manual/en/function.PDO-setAttribute.php
	 */
	public function setAttribute($name, $value)
	{
w  
Qiang Xue committed
588 589
		$this->open();
		$this->pdo->setAttribute($name, $value);
w  
Qiang Xue committed
590 591 592 593 594 595
	}

	/**
	 * Returns the statistical results of SQL executions.
	 * The results returned include the number of SQL statements executed and
	 * the total time spent.
w  
Qiang Xue committed
596
	 * In order to use this method, [[enableProfiling]] has to be set true.
w  
Qiang Xue committed
597 598
	 * @return array the first element indicates the number of SQL statements executed,
	 * and the second element the total time spent in SQL execution.
Qiang Xue committed
599
	 * @see \yii\logging\Logger::getProfiling()
w  
Qiang Xue committed
600 601 602
	 */
	public function getStats()
	{
w  
Qiang Xue committed
603
		$logger = \Yii::getLogger();
w  
Qiang Xue committed
604
		$timings = $logger->getProfiling(array('yii\db\dao\Command::query', 'yii\db\dao\Command::execute'));
w  
Qiang Xue committed
605
		$count = count($timings);
w  
Qiang Xue committed
606 607 608 609
		$time = 0;
		foreach ($timings as $timing) {
			$time += $timing[1];
		}
w  
Qiang Xue committed
610 611 612
		return array($count, $time);
	}
}