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

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

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

w  
Qiang Xue committed
15
/**
w  
Qiang Xue committed
16
 * Connection represents a connection to a database via [PDO](http://www.php.net/manual/en/ref.pdo.php).
w  
Qiang Xue committed
17
 *
w  
Qiang Xue committed
18 19 20
 * 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
21
 *
w  
Qiang Xue committed
22 23
 * To establish a DB connection, set [[dsn]], [[username]] and [[password]], and then
 * call [[open]] or set [[active]] to be true.
w  
Qiang Xue committed
24 25
 *
 * The following example shows how to create a Connection instance and establish
w  
Qiang Xue committed
26
 * the DB connection:
w  
Qiang Xue committed
27
 *
w  
Qiang Xue committed
28
 * ~~~
Qiang Xue committed
29
 * $connection = \yii\db\dao\Connection::newInstance($dsn, $username, $password);
Qiang Xue committed
30
 * $connection->active = true;  // same as: $connection->open();
w  
Qiang Xue committed
31 32
 * ~~~
 *
Qiang Xue committed
33
 * After the DB connection is established, one can execute SQL statements like the following:
w  
Qiang Xue committed
34 35 36
 *
 * ~~~
 * $command = $connection->createCommand('SELECT * FROM tbl_post');
Qiang Xue committed
37 38 39
 * $posts = $command->queryAll();
 * $command = $connection->createCommand('UPDATE tbl_post SET status=1');
 * $command->execute();
w  
Qiang Xue committed
40 41
 * ~~~
 *
Qiang Xue committed
42 43 44
 * 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
45
 *
w  
Qiang Xue committed
46 47 48 49 50
 * ~~~
 * $command = $connection->createCommand('SELECT * FROM tbl_post WHERE id=:id');
 * $command->bindValue(':id', $_GET['id']);
 * $post = $command->query();
 * ~~~
w  
Qiang Xue committed
51
 *
Qiang Xue committed
52 53 54 55
 * 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
56
 *
w  
Qiang Xue committed
57 58 59
 * ~~~
 * $transaction = $connection->beginTransaction();
 * try {
Qiang Xue committed
60 61 62 63
 *	 $connection->createCommand($sql1)->execute();
 *	 $connection->createCommand($sql2)->execute();
 *	 // ... executing other SQL statements ...
 *	 $transaction->commit();
Qiang Xue committed
64
 * } catch(Exception $e) {
Qiang Xue committed
65
 *	 $transaction->rollBack();
w  
Qiang Xue committed
66
 * }
w  
Qiang Xue committed
67
 * ~~~
w  
Qiang Xue committed
68
 *
Qiang Xue committed
69 70
 * Connection is often used as an [[\yii\base\ApplicationComponent|application component]] and configured in the application
 * configuration like the following:
w  
Qiang Xue committed
71 72
 *
 * ~~~
w  
Qiang Xue committed
73
 * array(
Qiang Xue committed
74 75 76 77 78 79 80 81 82
 *	 '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
83
 * )
w  
Qiang Xue committed
84
 * ~~~
w  
Qiang Xue committed
85
 *
Qiang Xue committed
86 87
 * @property boolean $active Whether the DB connection is established.
 * @property Transaction $currentTransaction The currently active transaction. Null if no active transaction.
Qiang Xue committed
88
 * @property Driver $driver The database driver for the current connection.
Qiang Xue committed
89 90 91 92 93 94
 * @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
95 96 97 98 99 100
 * @author Qiang Xue <qiang.xue@gmail.com>
 * @since 2.0
 */
class Connection extends \yii\base\ApplicationComponent
{
	/**
w  
Qiang Xue committed
101 102 103
	 * @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
104
	 * @see charset
w  
Qiang Xue committed
105 106 107 108 109 110 111 112 113 114
	 */
	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
115 116 117 118 119 120 121 122 123 124 125 126 127 128
	/**
	 * @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
129 130
	/**
	 * @var integer number of seconds that table metadata can remain valid in cache.
w  
Qiang Xue committed
131 132 133 134 135
	 * 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
136
	 * @see schemaCachingExclude
w  
Qiang Xue committed
137
	 * @see schemaCacheID
w  
Qiang Xue committed
138
	 */
w  
Qiang Xue committed
139
	public $schemaCachingDuration = -1;
w  
Qiang Xue committed
140 141
	/**
	 * @var array list of tables whose metadata should NOT be cached. Defaults to empty array.
Qiang Xue committed
142
	 * The table names may contain schema prefix, if any. Do not quote the table names.
w  
Qiang Xue committed
143 144 145 146 147
	 * @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
148 149
	 * Defaults to 'cache'.
	 * @see schemaCachingDuration
w  
Qiang Xue committed
150 151 152 153
	 */
	public $schemaCacheID = 'cache';
	/**
	 * @var integer number of seconds that query results can remain valid in cache.
w  
Qiang Xue committed
154 155
	 * Defaults to -1, meaning query caching is disabled.
	 * Use 0 to indicate that the cached data will never expire.
w  
Qiang Xue committed
156
	 *
w  
Qiang Xue committed
157 158
	 * Note that in order to enable query caching, a valid cache component as specified
	 * by [[queryCacheID]] must be enabled.
w  
Qiang Xue committed
159
	 *
w  
Qiang Xue committed
160 161
	 * The method [[cache()]] is provided as a convenient way of setting this property
	 * and [[queryCachingDependency]] on the fly.
w  
Qiang Xue committed
162 163 164 165 166
	 *
	 * @see cache
	 * @see queryCachingDependency
	 * @see queryCacheID
	 */
w  
Qiang Xue committed
167
	public $queryCachingDuration = -1;
w  
Qiang Xue committed
168
	/**
Qiang Xue committed
169
	 * @var \yii\caching\Dependency the dependency that will be used when saving query results into cache.
w  
Qiang Xue committed
170
	 * Defaults to null, meaning no dependency.
w  
Qiang Xue committed
171 172 173 174
	 * @see queryCachingDuration
	 */
	public $queryCachingDependency;
	/**
w  
Qiang Xue committed
175 176
	 * @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
177 178
	 * 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
179
	 * @see queryCachingDuration
w  
Qiang Xue committed
180 181 182 183
	 */
	public $queryCachingCount = 0;
	/**
	 * @var string the ID of the cache application component that is used for query caching.
w  
Qiang Xue committed
184 185
	 * Defaults to 'cache'.
	 * @see queryCachingDuration
w  
Qiang Xue committed
186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201
	 */
	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
202 203
	 * 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
204 205 206
	 */
	public $emulatePrepare;
	/**
w  
Qiang Xue committed
207
	 * @var boolean whether to enable profiling for the SQL statements being executed.
w  
Qiang Xue committed
208 209
	 * Defaults to false. This should be mainly enabled and used during development
	 * to find out the bottleneck of SQL executions.
w  
Qiang Xue committed
210
	 * @see getStats
w  
Qiang Xue committed
211 212 213
	 */
	public $enableProfiling = false;
	/**
Qiang Xue committed
214 215
	 * @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
216 217
	 * 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
218 219 220
	 *
	 * Note that if you set this property to be an empty string, then '{{post}}' will be replaced
	 * with 'post'.
w  
Qiang Xue committed
221 222 223
	 */
	public $tablePrefix;
	/**
Qiang Xue committed
224
	 * @var array a list of SQL statements that should be executed right after the DB connection is established.
w  
Qiang Xue committed
225 226 227
	 */
	public $initSQLs;
	/**
Qiang Xue committed
228
	 * @var array mapping between PDO driver names and [[Driver]] classes.
w  
Qiang Xue committed
229
	 * The keys of the array are PDO driver names while the values the corresponding
Qiang Xue committed
230
	 * driver class name or configuration. Please refer to [[\Yii::createObject]] for
w  
Qiang Xue committed
231 232
	 * details on how to specify a configuration.
	 *
Qiang Xue committed
233
	 * This property is mainly used by [[getDriver()]] when fetching the database schema information.
Qiang Xue committed
234
	 * You normally do not need to set this property unless you want to use your own
Qiang Xue committed
235 236 237 238 239 240 241 242 243 244 245 246
	 * [[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
247
	);
Qiang Xue committed
248 249 250
	/**
	 * @var Transaction the currently active transaction
	 */
w  
Qiang Xue committed
251
	private $_transaction;
Qiang Xue committed
252
	/**
Qiang Xue committed
253
	 * @var Driver the database driver
Qiang Xue committed
254
	 */
Qiang Xue committed
255
	private $_driver;
w  
Qiang Xue committed
256 257 258 259

	/**
	 * Constructor.
	 * Note, the DB connection is not established when this connection
w  
Qiang Xue committed
260
	 * instance is created. You may set [[active]] to be true or call [[open]]
w  
Qiang Xue committed
261
	 * to establish the connection.
w  
Qiang Xue committed
262 263 264 265
	 * @param string $dsn the Data Source Name, or DSN, contains the information
	 * required to connect to the database.
	 * @param string $username the user name for the DSN string.
	 * @param string $password the password for the DSN string.
w  
Qiang Xue committed
266 267 268 269
	 * @see http://www.php.net/manual/en/function.PDO-construct.php
	 */
	public function __construct($dsn = '', $username = '', $password = '')
	{
w  
Qiang Xue committed
270
		$this->dsn = $dsn;
w  
Qiang Xue committed
271 272 273 274 275
		$this->username = $username;
		$this->password = $password;
	}

	/**
w  
Qiang Xue committed
276
	 * Closes the connection when this component is being serialized.
w  
Qiang Xue committed
277 278 279 280 281 282 283 284 285 286 287 288 289 290 291
	 * @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
292
		return \PDO::getAvailableDrivers();
w  
Qiang Xue committed
293 294 295
	}

	/**
w  
Qiang Xue committed
296
	 * Returns a value indicating whether the DB connection is established.
w  
Qiang Xue committed
297 298 299 300
	 * @return boolean whether the DB connection is established
	 */
	public function getActive()
	{
w  
Qiang Xue committed
301
		return $this->pdo !== null;
w  
Qiang Xue committed
302 303 304
	}

	/**
w  
Qiang Xue committed
305 306 307
	 * 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
308 309 310
	 */
	public function setActive($value)
	{
w  
Qiang Xue committed
311
		$value ? $this->open() : $this->close();
w  
Qiang Xue committed
312 313 314 315
	}

	/**
	 * Sets the parameters about query caching.
w  
Qiang Xue committed
316 317 318
	 * 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
319
	 * @param integer $duration the number of seconds that query results may remain valid in cache.
w  
Qiang Xue committed
320
	 * See [[queryCachingDuration]] for more details.
Qiang Xue committed
321
	 * @param \yii\caching\Dependency $dependency the dependency for the cached query result.
w  
Qiang Xue committed
322
	 * See [[queryCachingDependency]] for more details.
Qiang Xue committed
323
	 * @param integer $queryCount the number of SQL queries that need to be cached after calling this method.
w  
Qiang Xue committed
324
	 * See [[queryCachingCount]] for more details.
w  
Qiang Xue committed
325 326
	 * @return Connection the connection instance itself.
	 */
w  
Qiang Xue committed
327
	public function cache($duration = 300, $dependency = null, $queryCount = 1)
w  
Qiang Xue committed
328 329 330 331 332 333 334 335
	{
		$this->queryCachingDuration = $duration;
		$this->queryCachingDependency = $dependency;
		$this->queryCachingCount = $queryCount;
		return $this;
	}

	/**
w  
Qiang Xue committed
336 337 338
	 * Establishes a DB connection.
	 * It does nothing if a DB connection has already been established.
	 * @throws Exception if connection fails
w  
Qiang Xue committed
339
	 */
w  
Qiang Xue committed
340
	public function open()
w  
Qiang Xue committed
341
	{
w  
Qiang Xue committed
342 343 344 345 346
		if ($this->pdo === null) {
			if (empty($this->dsn)) {
				throw new Exception('Connection.dsn cannot be empty.');
			}
			try {
Qiang Xue committed
347
				\Yii::trace('Opening DB connection: ' . $this->dsn, __CLASS__);
w  
Qiang Xue committed
348 349
				$this->pdo = $this->createPdoInstance();
				$this->initConnection($this->pdo);
w  
Qiang Xue committed
350
			}
w  
Qiang Xue committed
351
			catch (\PDOException $e) {
Qiang Xue committed
352 353 354
				\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
355 356 357 358 359 360 361 362
			}
		}
	}

	/**
	 * Closes the currently active DB connection.
	 * It does nothing if the connection is already closed.
	 */
w  
Qiang Xue committed
363
	public function close()
w  
Qiang Xue committed
364
	{
w  
Qiang Xue committed
365
		if ($this->pdo !== null) {
Qiang Xue committed
366
			\Yii::trace('Closing DB connection: ' . $this->dsn, __CLASS__);
w  
Qiang Xue committed
367
			$this->pdo = null;
Qiang Xue committed
368
			$this->_driver = null;
Qiang Xue committed
369
			$this->_transaction = null;
w  
Qiang Xue committed
370
		}
w  
Qiang Xue committed
371 372 373 374
	}

	/**
	 * Creates the PDO instance.
w  
Qiang Xue committed
375
	 * This method is called by [[open]] to establish a DB connection.
Qiang Xue committed
376 377
	 * 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
378
	 * @return \PDO the pdo instance
w  
Qiang Xue committed
379 380 381
	 */
	protected function createPdoInstance()
	{
w  
Qiang Xue committed
382 383 384 385
		$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
386
				$pdoClass = 'mssql\PDO';
w  
Qiang Xue committed
387
			}
w  
Qiang Xue committed
388
		}
w  
Qiang Xue committed
389
		return new $pdoClass($this->dsn, $this->username, $this->password, $this->attributes);
w  
Qiang Xue committed
390 391 392
	}

	/**
w  
Qiang Xue committed
393 394
	 * Initializes the DB connection.
	 * This method is invoked right after the DB connection is established.
Qiang Xue committed
395
	 * The default implementation sets the database [[charset]] and executes SQLs specified
w  
Qiang Xue committed
396
	 * in [[initSQLs]].
w  
Qiang Xue committed
397
	 */
w  
Qiang Xue committed
398
	protected function initConnection()
w  
Qiang Xue committed
399
	{
w  
Qiang Xue committed
400 401 402
		$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
403
		}
Qiang Xue committed
404 405
		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
406 407 408 409 410
		}
		if (!empty($this->initSQLs)) {
			foreach ($this->initSQLs as $sql) {
				$this->pdo->exec($sql);
			}
w  
Qiang Xue committed
411 412 413 414 415
		}
	}

	/**
	 * Creates a command for execution.
Qiang Xue committed
416
	 * @param string|array|Query $query the DB query to be executed. This can be:
Qiang Xue committed
417 418 419 420
	 *
	 * - a string representing the SQL statement to be executed
	 * - a [[Query]] object representing the SQL query
	 * - an array that will be used to initialize [[Query]]
w  
Qiang Xue committed
421
	 * @return Command the DB command
w  
Qiang Xue committed
422 423 424
	 */
	public function createCommand($query = null)
	{
w  
Qiang Xue committed
425 426
		$this->open();
		return new Command($this, $query);
w  
Qiang Xue committed
427 428 429 430
	}

	/**
	 * Returns the currently active transaction.
w  
Qiang Xue committed
431
	 * @return Transaction the currently active transaction. Null if no active transaction.
w  
Qiang Xue committed
432 433 434
	 */
	public function getCurrentTransaction()
	{
Qiang Xue committed
435
		if ($this->_transaction !== null && $this->_transaction->active) {
w  
Qiang Xue committed
436
			return $this->_transaction;
Qiang Xue committed
437
		} else {
Qiang Xue committed
438
			return null;
w  
Qiang Xue committed
439 440 441 442 443
		}
	}

	/**
	 * Starts a transaction.
Qiang Xue committed
444
	 * @return Transaction the transaction initiated
w  
Qiang Xue committed
445 446 447
	 */
	public function beginTransaction()
	{
w  
Qiang Xue committed
448 449 450 451
		\Yii::trace('Starting transaction', __CLASS__);
		$this->open();
		$this->pdo->beginTransaction();
		return $this->_transaction = new Transaction($this);
w  
Qiang Xue committed
452 453 454
	}

	/**
Qiang Xue committed
455
	 * Returns the metadata information for the underlying database.
Qiang Xue committed
456
	 * @return Driver the metadata information for the underlying database.
w  
Qiang Xue committed
457
	 */
Qiang Xue committed
458
	public function getDriver()
w  
Qiang Xue committed
459
	{
Qiang Xue committed
460 461
		if ($this->_driver !== null) {
			return $this->_driver;
Qiang Xue committed
462
		} else {
w  
Qiang Xue committed
463
			$driver = $this->getDriverName();
Qiang Xue committed
464 465
			if (isset($this->driverMap[$driver])) {
				return $this->_driver = \Yii::createObject($this->driverMap[$driver], $this);
Qiang Xue committed
466
			} else {
Qiang Xue committed
467
				throw new Exception("Connection does not support reading metadata for '$driver' database.");
w  
Qiang Xue committed
468
			}
w  
Qiang Xue committed
469 470 471
		}
	}

Qiang Xue committed
472 473 474 475
	/**
	 * Returns the query builder for the current DB connection.
	 * @return QueryBuilder the query builder for the current DB connection.
	 */
w  
Qiang Xue committed
476 477
	public function getQueryBuilder()
	{
Qiang Xue committed
478
		return $this->getDriver()->getQueryBuilder();
w  
Qiang Xue committed
479 480
	}

w  
Qiang Xue committed
481 482 483 484 485 486 487 488
	/**
	 * 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
489 490
		$this->open();
		return $this->pdo->lastInsertId($sequenceName);
w  
Qiang Xue committed
491 492 493 494
	}

	/**
	 * Quotes a string value for use in a query.
Qiang Xue committed
495
	 * Note that if the parameter is not a string, it will be returned without change.
w  
Qiang Xue committed
496 497 498 499 500 501
	 * @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
502
		if (!is_string($str)) {
w  
Qiang Xue committed
503
			return $str;
w  
Qiang Xue committed
504
		}
w  
Qiang Xue committed
505

w  
Qiang Xue committed
506 507
		$this->open();
		if (($value = $this->pdo->quote($str)) !== false) {
w  
Qiang Xue committed
508
			return $value;
Qiang Xue committed
509
		} else { // the driver doesn't support quote (e.g. oci)
w  
Qiang Xue committed
510
			return "'" . addcslashes(str_replace("'", "''", $str), "\000\n\r\\\032") . "'";
w  
Qiang Xue committed
511
		}
w  
Qiang Xue committed
512 513 514 515 516 517
	}

	/**
	 * 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
518
	 * @param boolean $simple if this is true, then the method will assume $name is a table name without schema prefix.
w  
Qiang Xue committed
519 520
	 * @return string the properly quoted table name
	 */
w  
Qiang Xue committed
521
	public function quoteTableName($name, $simple = false)
w  
Qiang Xue committed
522
	{
Qiang Xue committed
523
		return $simple ? $this->getDriver()->quoteSimpleTableName($name) : $this->getDriver()->quoteTableName($name);
w  
Qiang Xue committed
524 525 526 527
	}

	/**
	 * Quotes a column name for use in a query.
Qiang Xue committed
528
	 * If the column name contains table prefix, the prefix will also be properly quoted.
w  
Qiang Xue committed
529
	 * @param string $name column name
Qiang Xue committed
530
	 * @param boolean $simple if this is true, then the method will assume $name is a column name without table prefix.
w  
Qiang Xue committed
531 532
	 * @return string the properly quoted column name
	 */
w  
Qiang Xue committed
533
	public function quoteColumnName($name, $simple = false)
w  
Qiang Xue committed
534
	{
Qiang Xue committed
535
		return $simple ? $this->getDriver()->quoteSimpleColumnName($name) : $this->getDriver()->quoteColumnName($name);
w  
Qiang Xue committed
536 537
	}

Qiang Xue committed
538 539 540 541 542 543 544 545 546 547 548 549 550
	/**
	 * 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
551
		} else {
Qiang Xue committed
552 553 554 555
			return $sql;
		}
	}

w  
Qiang Xue committed
556
	/**
w  
Qiang Xue committed
557 558
	 * Determines the PDO type for the give PHP data type.
	 * @param string $type The PHP type (obtained by `gettype()` call).
w  
Qiang Xue committed
559
	 * @return integer the corresponding PDO type
w  
Qiang Xue committed
560
	 * @see http://www.php.net/manual/en/pdo.constants.php
w  
Qiang Xue committed
561 562 563
	 */
	public function getPdoType($type)
	{
w  
Qiang Xue committed
564
		static $typeMap = array(
w  
Qiang Xue committed
565 566 567 568
			'boolean' => \PDO::PARAM_BOOL,
			'integer' => \PDO::PARAM_INT,
			'string' => \PDO::PARAM_STR,
			'NULL' => \PDO::PARAM_NULL,
w  
Qiang Xue committed
569
		);
w  
Qiang Xue committed
570
		return isset($typeMap[$type]) ? $typeMap[$type] : \PDO::PARAM_STR;
w  
Qiang Xue committed
571 572 573
	}

	/**
w  
Qiang Xue committed
574
	 * Returns the name of the DB driver for the current [[dsn]].
w  
Qiang Xue committed
575 576 577 578
	 * @return string name of the DB driver
	 */
	public function getDriverName()
	{
w  
Qiang Xue committed
579 580
		if (($pos = strpos($this->dsn, ':')) !== false) {
			return strtolower(substr($this->dsn, 0, $pos));
Qiang Xue committed
581
		} else {
w  
Qiang Xue committed
582
			return strtolower($this->getAttribute(\PDO::ATTR_DRIVER_NAME));
w  
Qiang Xue committed
583
		}
w  
Qiang Xue committed
584 585 586 587 588 589 590 591 592 593
	}

	/**
	 * 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
594 595
		$this->open();
		return $this->pdo->getAttribute($name);
w  
Qiang Xue committed
596 597 598 599 600 601 602 603 604 605
	}

	/**
	 * 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
606 607
		$this->open();
		$this->pdo->setAttribute($name, $value);
w  
Qiang Xue committed
608 609 610 611 612 613
	}

	/**
	 * 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
614
	 * In order to use this method, [[enableProfiling]] has to be set true.
w  
Qiang Xue committed
615 616
	 * @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
617
	 * @see \yii\logging\Logger::getProfiling()
w  
Qiang Xue committed
618 619 620
	 */
	public function getStats()
	{
w  
Qiang Xue committed
621
		$logger = \Yii::getLogger();
w  
Qiang Xue committed
622
		$timings = $logger->getProfiling(array('yii\db\dao\Command::query', 'yii\db\dao\Command::execute'));
w  
Qiang Xue committed
623
		$count = count($timings);
w  
Qiang Xue committed
624 625 626 627
		$time = 0;
		foreach ($timings as $timing) {
			$time += $timing[1];
		}
w  
Qiang Xue committed
628 629 630
		return array($count, $time);
	}
}