From d75d932656add9bb21f0259dcff74bd4c637f8f8 Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Tue, 27 Dec 2011 15:29:33 -0500 Subject: [PATCH] ... --- framework/db/dao/Connection.php | 59 ++++--- framework/db/dao/Driver.php | 264 ++++++++++++++++++++++++++++++++ framework/db/dao/QueryBuilder.php | 72 ++++----- framework/db/dao/Schema.php | 264 -------------------------------- framework/db/dao/mysql/Driver.php | 173 +++++++++++++++++++++ framework/db/dao/mysql/QueryBuilder.php | 12 +- framework/db/dao/mysql/Schema.php | 173 --------------------- upgrade.md | 4 + 8 files changed, 512 insertions(+), 509 deletions(-) create mode 100644 framework/db/dao/Driver.php delete mode 100644 framework/db/dao/Schema.php create mode 100644 framework/db/dao/mysql/Driver.php delete mode 100644 framework/db/dao/mysql/Schema.php diff --git a/framework/db/dao/Connection.php b/framework/db/dao/Connection.php index cef4148..14f3953 100644 --- a/framework/db/dao/Connection.php +++ b/framework/db/dao/Connection.php @@ -85,7 +85,7 @@ use yii\db\Exception; * * @property boolean $active Whether the DB connection is established. * @property Transaction $currentTransaction The currently active transaction. Null if no active transaction. - * @property Schema $schema The database schema for the current connection. + * @property Driver $driver The database driver for the current connection. * @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. @@ -225,35 +225,34 @@ class Connection extends \yii\base\ApplicationComponent */ public $initSQLs; /** - * @var array mapping between PDO driver names and [[Schema]] classes. + * @var array mapping between PDO driver names and [[Driver]] classes. * The keys of the array are PDO driver names while the values the corresponding - * schema class name or configuration. Please refer to [[\Yii::createObject]] for + * driver class name or configuration. Please refer to [[\Yii::createObject]] for * details on how to specify a configuration. * - * This property is mainly used by [[getSchema]] when fetching the database schema information. + * This property is mainly used by [[getDriver()]] when fetching the database schema information. * You normally do not need to set this property unless you want to use your own - * [[Schema]] class to support DBMS that is not supported by Yii. - */ - public $schemaMap = array( - 'pgsql' => '\yii\db\dao\pgsql\Schema', // PostgreSQL - 'mysqli' => '\yii\db\dao\mysql\Schema', // MySQL - 'mysql' => '\yii\db\dao\mysql\Schema', // MySQL - 'sqlite' => '\yii\db\dao\sqlite\Schema', // sqlite 3 - 'sqlite2' => '\yii\db\dao\sqlite\Schema', // sqlite 2 - 'mssql' => '\yii\db\dao\mssql\Schema', // Mssql driver on windows hosts - 'dblib' => '\yii\db\dao\mssql\Schema', // dblib drivers on linux (and maybe others os) hosts - 'sqlsrv' => '\yii\db\dao\mssql\Schema', // Mssql - 'oci' => '\yii\db\dao\oci\Schema', // Oracle driver + * [[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 ); - /** * @var Transaction the currently active transaction */ private $_transaction; /** - * @var Schema the database schema + * @var Driver the database driver */ - private $_schema; + private $_driver; /** * Constructor. @@ -366,7 +365,7 @@ class Connection extends \yii\base\ApplicationComponent if ($this->pdo !== null) { \Yii::trace('Closing DB connection: ' . $this->dsn, __CLASS__); $this->pdo = null; - $this->_schema = null; + $this->_driver = null; $this->_transaction = null; } } @@ -454,18 +453,18 @@ class Connection extends \yii\base\ApplicationComponent /** * Returns the metadata information for the underlying database. - * @return Schema the metadata information for the underlying database. + * @return Driver the metadata information for the underlying database. */ - public function getSchema() + public function getDriver() { - if ($this->_schema !== null) { - return $this->_schema; + if ($this->_driver !== null) { + return $this->_driver; } else { $driver = $this->getDriverName(); - if (isset($this->schemaMap[$driver])) { - return $this->_schema = \Yii::createObject($this->schemaMap[$driver], $this); + if (isset($this->driverMap[$driver])) { + return $this->_driver = \Yii::createObject($this->driverMap[$driver], $this); } else { - throw new Exception("Connection does not support reading schema for '$driver' database."); + throw new Exception("Connection does not support reading meta data for '$driver' database."); } } } @@ -476,7 +475,7 @@ class Connection extends \yii\base\ApplicationComponent */ public function getQueryBuilder() { - return $this->getSchema()->getQueryBuilder(); + return $this->getDriver()->getQueryBuilder(); } /** @@ -521,7 +520,7 @@ class Connection extends \yii\base\ApplicationComponent */ public function quoteTableName($name, $simple = false) { - return $simple ? $this->getSchema()->quoteSimpleTableName($name) : $this->getSchema()->quoteTableName($name); + return $simple ? $this->getDriver()->quoteSimpleTableName($name) : $this->getDriver()->quoteTableName($name); } /** @@ -533,7 +532,7 @@ class Connection extends \yii\base\ApplicationComponent */ public function quoteColumnName($name, $simple = false) { - return $simple ? $this->getSchema()->quoteSimpleColumnName($name) : $this->getSchema()->quoteColumnName($name); + return $simple ? $this->getDriver()->quoteSimpleColumnName($name) : $this->getDriver()->quoteColumnName($name); } /** diff --git a/framework/db/dao/Driver.php b/framework/db/dao/Driver.php new file mode 100644 index 0000000..d31623e --- /dev/null +++ b/framework/db/dao/Driver.php @@ -0,0 +1,264 @@ + + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2012 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +namespace yii\db\dao; + +use yii\db\Exception; + +/** + * Driver is the base class for all database driver classes. + * + * Driver implements the DBMS-specific methods to support retrieving meta data of a database. + * + * @property QueryBuilder $queryBuilder the query builder for this connection. + * @property array $tableNames the names of all tables in this database. + * @property array $tableSchemas the meta data for all tables in this database. + * + * @author Qiang Xue + * @since 2.0 + */ +abstract class Driver extends \yii\base\Object +{ + /** + * @var Connection the database connection + */ + public $connection; + /** + * @var array list of ALL table names in the database + */ + private $_tableNames = array(); + /** + * @var array list of loaded table meta data (table name => TableSchema) + */ + private $_tables = array(); + /** + * @var QueryBuilder the query builder for this database + */ + private $_builder; + + /** + * Loads the metadata for the specified table. + * @param string $name table name + * @return TableSchema DBMS-dependent table metadata, null if the table does not exist. + */ + abstract protected function loadTableSchema($name); + + /** + * Constructor. + * @param Connection $connection database connection. + */ + public function __construct($connection) + { + $this->connection = $connection; + } + + /** + * Obtains the metadata for the named table. + * @param string $name table name. The table name may contain schema name if any. Do not quote the table name. + * @param boolean $refresh whether to reload the table schema even if it is found in the cache. + * @return TableSchema table metadata. Null if the named table does not exist. + */ + public function getTableSchema($name, $refresh = false) + { + if (isset($this->_tables[$name]) && !$refresh) { + return $this->_tables[$name]; + } + + $db = $this->connection; + + $realName = $db->expandTablePrefix($name); + + // temporarily disable query caching + if ($db->queryCachingDuration >= 0) { + $qcDuration = $db->queryCachingDuration; + $db->queryCachingDuration = -1; + } + + if (!in_array($name, $db->schemaCachingExclude, true) && $db->schemaCachingDuration >= 0 && ($cache = \Yii::$application->getComponent($db->schemaCacheID)) !== null) { + $key = $this->getCacheKey($name); + if ($refresh || ($table = $cache->get($key)) === false) { + $table = $this->loadTableSchema($realName); + if ($table !== null) { + $cache->set($key, $table, $db->schemaCachingDuration); + } + } + $this->_tables[$name] = $table; + } else { + $this->_tables[$name] = $table = $this->loadTableSchema($realName); + } + + if (isset($qcDuration)) { // re-enable query caching + $db->queryCachingDuration = $qcDuration; + } + + return $table; + } + + /** + * Returns the cache key for the specified table name. + * @param string $name the table name + * @return string the cache key + */ + public function getCacheKey($name) + { + return __CLASS__ . "/{$this->connection->dsn}/{$this->connection->username}/{$name}"; + } + + /** + * Returns the metadata for all tables in the database. + * @param string $schema the schema of the tables. Defaults to empty string, meaning the current or default schema. + * @return array the metadata for all tables in the database. + * Each array element is an instance of [[TableSchema]] (or its child class). + */ + public function getTableSchemas($schema = '') + { + $tables = array(); + foreach ($this->getTableNames($schema) as $name) { + if (($table = $this->getTableSchema($name)) !== null) { + $tables[] = $table; + } + } + return $tables; + } + + /** + * Returns all table names in the database. + * @param string $schema the schema of the tables. Defaults to empty string, meaning the current or default schema. + * If not empty, the returned table names will be prefixed with the schema name. + * @param boolean $refresh whether to fetch the latest available table names. If this is false, + * table names fetched previously (if available) will be returned. + * @return array all table names in the database. + */ + public function getTableNames($schema = '', $refresh = false) + { + if (!isset($this->_tableNames[$schema]) || $refresh) { + $this->_tableNames[$schema] = $this->findTableNames($schema); + } + return $this->_tableNames[$schema]; + } + + /** + * @return QueryBuilder the query builder for this connection. + */ + public function getQueryBuilder() + { + if ($this->_builder === null) { + $this->_builder = $this->createQueryBuilder(); + } + return $this->_builder; + } + + /** + * Refreshes the schema. + * This method cleans up the cached table schema and names + * so that they can be recreated to reflect the database schema change. + * @param string $tableName the name of the table that needs to be refreshed. + * If null, all currently loaded tables will be refreshed. + */ + public function refresh($tableName = null) + { + $db = $this->connection; + if ($db->schemaCachingDuration >= 0 && ($cache = \Yii::$application->getComponent($db->schemaCacheID)) !== null) { + if ($tableName === null) { + foreach ($this->_tables as $name => $table) { + $cache->delete($this->getCacheKey($name)); + } + $this->_tables = array(); + } else { + $cache->delete($this->getCacheKey($tableName)); + unset($this->_tables[$tableName]); + } + } + } + + /** + * 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 + * @return string the properly quoted table name + * @see quoteSimpleTableName + */ + public function quoteTableName($name) + { + if (strpos($name, '.') === false) { + return $this->quoteSimpleTableName($name); + } + $parts = explode('.', $name); + foreach ($parts as $i => $part) { + $parts[$i] = $this->quoteSimpleTableName($part); + } + return implode('.', $parts); + + } + + /** + * Quotes a simple table name for use in a query. + * A simple table name does not schema prefix. + * @param string $name table name + * @return string the properly quoted table name + */ + public function quoteSimpleTableName($name) + { + return strpos($name, "'") !== false ? $name : "'" . $name . "'"; + } + + /** + * Quotes a column name for use in a query. + * If the column name contains prefix, the prefix will also be properly quoted. + * @param string $name column name + * @return string the properly quoted column name + * @see quoteSimpleColumnName + */ + public function quoteColumnName($name) + { + if (($pos = strrpos($name, '.')) !== false) { + $prefix = $this->quoteTableName(substr($name, 0, $pos)) . '.'; + $name = substr($name, $pos + 1); + } else { + $prefix = ''; + } + return $prefix . $this->quoteSimpleColumnName($name); + } + + /** + * Quotes a simple column name for use in a query. + * A simple column name does not contain prefix. + * @param string $name column name + * @return string the properly quoted column name + */ + public function quoteSimpleColumnName($name) + { + return strpos($name, '"') !== false || $name === '*' ? $name : '"' . $name . '"'; + } + + /** + * Creates a query builder for the database. + * This method may be overridden by child classes to create a DBMS-specific query builder. + * @return QueryBuilder query builder instance + */ + public function createQueryBuilder() + { + return new QueryBuilder($this->connection); + } + + /** + * Returns all table names in the database. + * This method should be overridden by child classes in order to support this feature + * because the default implementation simply throws an exception. + * @param string $schema the schema of the tables. Defaults to empty string, meaning the current or default schema. + * If not empty, the returned table names will be prefixed with the schema name. + * @return array all table names in the database. + */ + protected function findTableNames($schema = '') + { + throw new Exception(get_class($this) . 'does not support fetching all table names.'); + } + +} diff --git a/framework/db/dao/QueryBuilder.php b/framework/db/dao/QueryBuilder.php index aa272ae..cf2598c 100644 --- a/framework/db/dao/QueryBuilder.php +++ b/framework/db/dao/QueryBuilder.php @@ -34,9 +34,9 @@ class QueryBuilder extends \yii\base\Object */ public $connection; /** - * @var Schema the database schema + * @var Driver the database driver */ - public $schema; + public $driver; /** * @var string the separator between different fragments of a SQL statement. * Defaults to an empty space. This is mainly used by [[build()]] when generating a SQL statement. @@ -54,7 +54,7 @@ class QueryBuilder extends \yii\base\Object public function __construct($connection) { $this->connection = $connection; - $this->schema = $connection->getSchema(); + $this->driver = $connection->getDriver(); } /** @@ -105,7 +105,7 @@ class QueryBuilder extends \yii\base\Object $placeholders = array(); $count = 0; foreach ($columns as $name => $value) { - $names[] = $this->schema->quoteColumnName($name); + $names[] = $this->driver->quoteColumnName($name); if ($value instanceof Expression) { $placeholders[] = $value->expression; foreach ($value->params as $n => $v) { @@ -121,7 +121,7 @@ class QueryBuilder extends \yii\base\Object $this->_query->addParams($params); } - return 'INSERT INTO ' . $this->schema->quoteTableName($table) + return 'INSERT INTO ' . $this->driver->quoteTableName($table) . ' (' . implode(', ', $names) . ') VALUES (' . implode(', ', $placeholders) . ')'; } @@ -143,12 +143,12 @@ class QueryBuilder extends \yii\base\Object $count = 0; foreach ($columns as $name => $value) { if ($value instanceof Expression) { - $lines[] = $this->schema->quoteSimpleColumnName($name) . '=' . $value->expression; + $lines[] = $this->driver->quoteSimpleColumnName($name) . '=' . $value->expression; foreach ($value->params as $n => $v) { $params[$n] = $v; } } else { - $lines[] = $this->schema->quoteSimpleColumnName($name) . '=:p' . $count; + $lines[] = $this->driver->quoteSimpleColumnName($name) . '=:p' . $count; $params[':p' . $count] = $value; $count++; } @@ -156,7 +156,7 @@ class QueryBuilder extends \yii\base\Object if ($this->_query instanceof Query) { $this->_query->addParams($params); } - $sql = 'UPDATE ' . $this->schema->quoteTableName($table) . ' SET ' . implode(', ', $lines); + $sql = 'UPDATE ' . $this->driver->quoteTableName($table) . ' SET ' . implode(', ', $lines); if (($where = $this->buildCondition($condition)) != '') { $sql .= ' WHERE ' . $where; } @@ -173,7 +173,7 @@ class QueryBuilder extends \yii\base\Object */ public function delete($table, $condition = '') { - $sql = 'DELETE FROM ' . $this->schema->quoteTableName($table); + $sql = 'DELETE FROM ' . $this->driver->quoteTableName($table); if (($where = $this->buildCondition($condition)) != '') { $sql .= ' WHERE ' . $where; } @@ -201,13 +201,13 @@ class QueryBuilder extends \yii\base\Object $cols = array(); foreach ($columns as $name => $type) { if (is_string($name)) { - $cols[] = "\t" . $this->schema->quoteColumnName($name) . ' ' . $this->schema->getColumnType($type); + $cols[] = "\t" . $this->driver->quoteColumnName($name) . ' ' . $this->driver->getColumnType($type); } else { $cols[] = "\t" . $type; } } - $sql = "CREATE TABLE " . $this->schema->quoteTableName($table) . " (\n" . implode(",\n", $cols) . "\n)"; + $sql = "CREATE TABLE " . $this->driver->quoteTableName($table) . " (\n" . implode(",\n", $cols) . "\n)"; return $options === null ? $sql : $sql . ' ' . $options; } @@ -219,7 +219,7 @@ class QueryBuilder extends \yii\base\Object */ public function renameTable($table, $newName) { - return 'RENAME TABLE ' . $this->schema->quoteTableName($table) . ' TO ' . $this->schema->quoteTableName($newName); + return 'RENAME TABLE ' . $this->driver->quoteTableName($table) . ' TO ' . $this->driver->quoteTableName($newName); } /** @@ -229,7 +229,7 @@ class QueryBuilder extends \yii\base\Object */ public function dropTable($table) { - return "DROP TABLE " . $this->schema->quoteTableName($table); + return "DROP TABLE " . $this->driver->quoteTableName($table); } /** @@ -239,7 +239,7 @@ class QueryBuilder extends \yii\base\Object */ public function truncateTable($table) { - return "TRUNCATE TABLE " . $this->schema->quoteTableName($table); + return "TRUNCATE TABLE " . $this->driver->quoteTableName($table); } /** @@ -253,8 +253,8 @@ class QueryBuilder extends \yii\base\Object */ public function addColumn($table, $column, $type) { - return 'ALTER TABLE ' . $this->schema->quoteTableName($table) - . ' ADD ' . $this->schema->quoteColumnName($column) . ' ' + return 'ALTER TABLE ' . $this->driver->quoteTableName($table) + . ' ADD ' . $this->driver->quoteColumnName($column) . ' ' . $this->getColumnType($type); } @@ -266,8 +266,8 @@ class QueryBuilder extends \yii\base\Object */ public function dropColumn($table, $column) { - return "ALTER TABLE " . $this->schema->quoteTableName($table) - . " DROP COLUMN " . $this->schema->quoteSimpleColumnName($column); + return "ALTER TABLE " . $this->driver->quoteTableName($table) + . " DROP COLUMN " . $this->driver->quoteSimpleColumnName($column); } /** @@ -279,9 +279,9 @@ class QueryBuilder extends \yii\base\Object */ public function renameColumn($table, $name, $newName) { - return "ALTER TABLE " . $this->schema->quoteTableName($table) - . " RENAME COLUMN " . $this->schema->quoteSimpleColumnName($name) - . " TO " . $this->schema->quoteSimpleColumnName($newName); + return "ALTER TABLE " . $this->driver->quoteTableName($table) + . " RENAME COLUMN " . $this->driver->quoteSimpleColumnName($name) + . " TO " . $this->driver->quoteSimpleColumnName($newName); } /** @@ -295,9 +295,9 @@ class QueryBuilder extends \yii\base\Object */ public function alterColumn($table, $column, $type) { - return 'ALTER TABLE ' . $this->schema->quoteTableName($table) . ' CHANGE ' - . $this->schema->quoteSimpleColumnName($column) . ' ' - . $this->schema->quoteSimpleColumnName($column) . ' ' + return 'ALTER TABLE ' . $this->driver->quoteTableName($table) . ' CHANGE ' + . $this->driver->quoteSimpleColumnName($column) . ' ' + . $this->driver->quoteSimpleColumnName($column) . ' ' . $this->getColumnType($type); } @@ -317,16 +317,16 @@ class QueryBuilder extends \yii\base\Object { $columns = preg_split('/\s*,\s*/', $columns, -1, PREG_SPLIT_NO_EMPTY); foreach ($columns as $i => $col) { - $columns[$i] = $this->schema->quoteColumnName($col); + $columns[$i] = $this->driver->quoteColumnName($col); } $refColumns = preg_split('/\s*,\s*/', $refColumns, -1, PREG_SPLIT_NO_EMPTY); foreach ($refColumns as $i => $col) { - $refColumns[$i] = $this->schema->quoteColumnName($col); + $refColumns[$i] = $this->driver->quoteColumnName($col); } - $sql = 'ALTER TABLE ' . $this->schema->quoteTableName($table) - . ' ADD CONSTRAINT ' . $this->schema->quoteColumnName($name) + $sql = 'ALTER TABLE ' . $this->driver->quoteTableName($table) + . ' ADD CONSTRAINT ' . $this->driver->quoteColumnName($name) . ' FOREIGN KEY (' . implode(', ', $columns) . ')' - . ' REFERENCES ' . $this->schema->quoteTableName($refTable) + . ' REFERENCES ' . $this->driver->quoteTableName($refTable) . ' (' . implode(', ', $refColumns) . ')'; if ($delete !== null) { $sql .= ' ON DELETE ' . $delete; @@ -345,8 +345,8 @@ class QueryBuilder extends \yii\base\Object */ public function dropForeignKey($name, $table) { - return 'ALTER TABLE ' . $this->schema->quoteTableName($table) - . ' DROP CONSTRAINT ' . $this->schema->quoteColumnName($name); + return 'ALTER TABLE ' . $this->driver->quoteTableName($table) + . ' DROP CONSTRAINT ' . $this->driver->quoteColumnName($name); } /** @@ -368,12 +368,12 @@ class QueryBuilder extends \yii\base\Object $cols[] = $col; } else { - $cols[] = $this->schema->quoteColumnName($col); + $cols[] = $this->driver->quoteColumnName($col); } } return ($unique ? 'CREATE UNIQUE INDEX ' : 'CREATE INDEX ') - . $this->schema->quoteTableName($name) . ' ON ' - . $this->schema->quoteTableName($table) . ' (' . implode(', ', $cols) . ')'; + . $this->driver->quoteTableName($name) . ' ON ' + . $this->driver->quoteTableName($table) . ' (' . implode(', ', $cols) . ')'; } /** @@ -384,7 +384,7 @@ class QueryBuilder extends \yii\base\Object */ public function dropIndex($name, $table) { - return 'DROP INDEX ' . $this->schema->quoteTableName($name) . ' ON ' . $this->schema->quoteTableName($table); + return 'DROP INDEX ' . $this->driver->quoteTableName($name) . ' ON ' . $this->driver->quoteTableName($table); } /** @@ -470,7 +470,7 @@ class QueryBuilder extends \yii\base\Object $columns[$i] = (string)$column; } elseif (strpos($column, '(') === false) { if (preg_match('/^(.*?)(?i:\s+as\s+|\s+)([\w\-\.])$/', $column, $matches)) { - $columns[$i] = $this->connection->quoteColumnName($matches[1]) . ' AS ' . $this->connection->quoteSimpleColumnName($matches[2]); + $columns[$i] = $this->connection->quoteColumnName($matches[1]) . ' AS ' . $this->driver->quoteSimpleColumnName($matches[2]); } else { $columns[$i] = $this->connection->quoteColumnName($column); } diff --git a/framework/db/dao/Schema.php b/framework/db/dao/Schema.php deleted file mode 100644 index 88a18ac..0000000 --- a/framework/db/dao/Schema.php +++ /dev/null @@ -1,264 +0,0 @@ - - * @link http://www.yiiframework.com/ - * @copyright Copyright © 2008-2012 Yii Software LLC - * @license http://www.yiiframework.com/license/ - */ - -namespace yii\db\dao; - -use yii\db\Exception; - -/** - * Schema represents the meta data of a database. - * - * Schema retrieves and maintains the meta data of database tables and columns. - * - * @property QueryBuilder $queryBuilder the query builder for this connection. - * @property array $tableNames the names of all tables in this database. - * @property array $tableSchemas the meta data for all tables in this database. - * - * @author Qiang Xue - * @since 2.0 - */ -abstract class Schema extends \yii\base\Object -{ - /** - * @var Connection the database connection - */ - public $connection; - /** - * @var array list of ALL table names in the database - */ - private $_tableNames = array(); - /** - * @var array list of loaded table meta data (table name => TableSchema) - */ - private $_tables = array(); - /** - * @var QueryBuilder the query builder for this database - */ - private $_builder; - - /** - * Loads the metadata for the specified table. - * @param string $name table name - * @return TableSchema DBMS-dependent table metadata, null if the table does not exist. - */ - abstract protected function loadTableSchema($name); - - /** - * Constructor. - * @param Connection $connection database connection. - */ - public function __construct($connection) - { - $this->connection = $connection; - } - - /** - * Obtains the metadata for the named table. - * @param string $name table name. The table name may contain schema name if any. Do not quote the table name. - * @param boolean $refresh whether to reload the table schema even if it is found in the cache. - * @return TableSchema table metadata. Null if the named table does not exist. - */ - public function getTableSchema($name, $refresh = false) - { - if (isset($this->_tables[$name]) && !$refresh) { - return $this->_tables[$name]; - } - - $db = $this->connection; - - $realName = $db->expandTablePrefix($name); - - // temporarily disable query caching - if ($db->queryCachingDuration >= 0) { - $qcDuration = $db->queryCachingDuration; - $db->queryCachingDuration = -1; - } - - if (!in_array($name, $db->schemaCachingExclude, true) && $db->schemaCachingDuration >= 0 && ($cache = \Yii::$application->getComponent($db->schemaCacheID)) !== null) { - $key = $this->getCacheKey($name); - if ($refresh || ($table = $cache->get($key)) === false) { - $table = $this->loadTableSchema($realName); - if ($table !== null) { - $cache->set($key, $table, $db->schemaCachingDuration); - } - } - $this->_tables[$name] = $table; - } else { - $this->_tables[$name] = $table = $this->loadTableSchema($realName); - } - - if (isset($qcDuration)) { // re-enable query caching - $db->queryCachingDuration = $qcDuration; - } - - return $table; - } - - /** - * Returns the cache key for the specified table name. - * @param string $name the table name - * @return string the cache key - */ - public function getCacheKey($name) - { - return __CLASS__ . "/{$this->connection->dsn}/{$this->connection->username}/{$name}"; - } - - /** - * Returns the metadata for all tables in the database. - * @param string $schema the schema of the tables. Defaults to empty string, meaning the current or default schema. - * @return array the metadata for all tables in the database. - * Each array element is an instance of [[TableSchema]] (or its child class). - */ - public function getTableSchemas($schema = '') - { - $tables = array(); - foreach ($this->getTableNames($schema) as $name) { - if (($table = $this->getTableSchema($name)) !== null) { - $tables[] = $table; - } - } - return $tables; - } - - /** - * Returns all table names in the database. - * @param string $schema the schema of the tables. Defaults to empty string, meaning the current or default schema. - * If not empty, the returned table names will be prefixed with the schema name. - * @param boolean $refresh whether to fetch the latest available table names. If this is false, - * table names fetched previously (if available) will be returned. - * @return array all table names in the database. - */ - public function getTableNames($schema = '', $refresh = false) - { - if (!isset($this->_tableNames[$schema]) || $refresh) { - $this->_tableNames[$schema] = $this->findTableNames($schema); - } - return $this->_tableNames[$schema]; - } - - /** - * @return QueryBuilder the query builder for this connection. - */ - public function getQueryBuilder() - { - if ($this->_builder === null) { - $this->_builder = $this->createQueryBuilder(); - } - return $this->_builder; - } - - /** - * Refreshes the schema. - * This method cleans up the cached table schema and names - * so that they can be recreated to reflect the database schema change. - * @param string $tableName the name of the table that needs to be refreshed. - * If null, all currently loaded tables will be refreshed. - */ - public function refresh($tableName = null) - { - $db = $this->connection; - if ($db->schemaCachingDuration >= 0 && ($cache = \Yii::$application->getComponent($db->schemaCacheID)) !== null) { - if ($tableName === null) { - foreach ($this->_tables as $name => $table) { - $cache->delete($this->getCacheKey($name)); - } - $this->_tables = array(); - } else { - $cache->delete($this->getCacheKey($tableName)); - unset($this->_tables[$tableName]); - } - } - } - - /** - * 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 - * @return string the properly quoted table name - * @see quoteSimpleTableName - */ - public function quoteTableName($name) - { - if (strpos($name, '.') === false) { - return $this->quoteSimpleTableName($name); - } - $parts = explode('.', $name); - foreach ($parts as $i => $part) { - $parts[$i] = $this->quoteSimpleTableName($part); - } - return implode('.', $parts); - - } - - /** - * Quotes a simple table name for use in a query. - * A simple table name does not schema prefix. - * @param string $name table name - * @return string the properly quoted table name - */ - public function quoteSimpleTableName($name) - { - return strpos($name, "'") !== false ? $name : "'" . $name . "'"; - } - - /** - * Quotes a column name for use in a query. - * If the column name contains prefix, the prefix will also be properly quoted. - * @param string $name column name - * @return string the properly quoted column name - * @see quoteSimpleColumnName - */ - public function quoteColumnName($name) - { - if (($pos = strrpos($name, '.')) !== false) { - $prefix = $this->quoteTableName(substr($name, 0, $pos)) . '.'; - $name = substr($name, $pos + 1); - } else { - $prefix = ''; - } - return $prefix . $this->quoteSimpleColumnName($name); - } - - /** - * Quotes a simple column name for use in a query. - * A simple column name does not contain prefix. - * @param string $name column name - * @return string the properly quoted column name - */ - public function quoteSimpleColumnName($name) - { - return strpos($name, '"') !== false || $name === '*' ? $name : '"' . $name . '"'; - } - - /** - * Creates a query builder for the database. - * This method may be overridden by child classes to create a DBMS-specific query builder. - * @return QueryBuilder query builder instance - */ - public function createQueryBuilder() - { - return new QueryBuilder($this->connection); - } - - /** - * Returns all table names in the database. - * This method should be overridden by child classes in order to support this feature - * because the default implementation simply throws an exception. - * @param string $schema the schema of the tables. Defaults to empty string, meaning the current or default schema. - * If not empty, the returned table names will be prefixed with the schema name. - * @return array all table names in the database. - */ - protected function findTableNames($schema = '') - { - throw new Exception(get_class($this) . 'does not support fetching all table names.'); - } - -} diff --git a/framework/db/dao/mysql/Driver.php b/framework/db/dao/mysql/Driver.php new file mode 100644 index 0000000..08f4f7f --- /dev/null +++ b/framework/db/dao/mysql/Driver.php @@ -0,0 +1,173 @@ + + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2012 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +namespace yii\db\dao\mysql; + +use yii\db\dao\TableSchema; + +/** + * Driver is the class for retrieving meta data from a MySQL database (version 4.1.x and 5.x). + * + * @author Qiang Xue + * @since 2.0 + */ +class Driver extends \yii\db\dao\Driver +{ + /** + * Quotes a table name for use in a query. + * A simple table name has no schema prefix. + * @param string $name table name + * @return string the properly quoted table name + */ + public function quoteSimpleTableName($name) + { + return strpos($name, "`") !== false ? $name : "`" . $name . "`"; + } + + /** + * Quotes a column name for use in a query. + * A simple column name has no prefix. + * @param string $name column name + * @return string the properly quoted column name + */ + public function quoteSimpleColumnName($name) + { + return strpos($name, '`') !== false || $name === '*' ? $name : '`' . $name . '`'; + } + + /** + * Loads the metadata for the specified table. + * @param string $name table name + * @return \yii\db\dao\TableSchema driver dependent table metadata. Null if the table does not exist. + */ + protected function loadTableSchema($name) + { + $table = new TableSchema; + $this->resolveTableNames($table, $name); + + if ($this->findColumns($table)) { + $this->findConstraints($table); + return $table; + } + } + + /** + * Generates various kinds of table names. + * @param \yii\db\dao\TableSchema $table the table instance + * @param string $name the unquoted table name + */ + protected function resolveTableNames($table, $name) + { + $parts = explode('.', str_replace('`', '', $name)); + if (isset($parts[1])) { + $table->schemaName = $parts[0]; + $table->name = $parts[1]; + $table->quotedName = $this->quoteSimpleTableName($table->schemaName) . '.' . $this->quoteSimpleTableName($table->name); + } else { + $table->name = $parts[0]; + $table->quotedName = $this->quoteSimpleTableName($table->name); + } + } + + /** + * Creates a table column. + * @param array $column column metadata + * @return CDbColumnSchema normalized column metadata + */ + protected function createColumn($column) + { + $c = new ColumnSchema; + + $c->name = $column['Field']; + $c->quotedName = $this->quoteSimpleColumnName($c->name); + $c->allowNull = $column['Null'] === 'YES'; + $c->isPrimaryKey = strpos($column['Key'], 'PRI') !== false; + $c->autoIncrement = stripos($column['Extra'], 'auto_increment') !== false; + $c->initTypes($column['Type']); + $c->initDefaultValue($column['Default']); + + return $c; + } + + /** + * Collects the table column metadata. + * @param \yii\db\dao\TableSchema $table the table metadata + * @return boolean whether the table exists in the database + */ + protected function findColumns($table) + { + $sql = 'SHOW COLUMNS FROM ' . $table->quotedName; + try { + $columns = $this->connection->createCommand($sql)->queryAll(); + } + catch(\Exception $e) { + return false; + } + foreach ($columns as $column) { + $table->columns[$c->name] = $c = $this->createColumn($column); + if ($c->isPrimaryKey) { + if ($table->primaryKey === null) { + $table->primaryKey = $c->name; + } elseif (is_string($table->primaryKey)) { + $table->primaryKey = array($table->primaryKey, $c->name); + } else { + $table->primaryKey[] = $c->name; + } + if ($c->autoIncrement) { + $table->sequenceName = ''; + } + } + } + return true; + } + + /** + * Collects the foreign key column details for the given table. + * @param \yii\db\dao\TableSchema $table the table metadata + */ + protected function findConstraints($table) + { + $row = $this->connection->createCommand('SHOW CREATE TABLE ' . $table->quotedName)->queryRow(); + $matches = array(); + $regexp = '/FOREIGN KEY\s+\(([^\)]+)\)\s+REFERENCES\s+([^\(^\s]+)\s*\(([^\)]+)\)/mi'; + foreach ($row as $sql) { + if (preg_match_all($regexp, $sql, $matches, PREG_SET_ORDER)) { + foreach ($matches as $match) { + $fks = array_map('trim', explode(',', str_replace('`', '', $match[1]))); + $pks = array_map('trim', explode(',', str_replace('`', '', $match[3]))); + $constraint = array(str_replace('`', '', $match[2])); + foreach ($fks as $k => $name) { + $constraint[$name] = $pks[$k]; + } + $table->foreignKeys[] = $constraint; + } + break; + } + } + } + + /** + * Returns all table names in the database. + * @param string $schema the schema of the tables. Defaults to empty string, meaning the current or default schema. + * If not empty, the returned table names will be prefixed with the schema name. + * @return array all table names in the database. + */ + protected function findTableNames($schema = '') + { + if ($schema === '') { + return $this->connection->createCommand('SHOW TABLES')->queryColumn(); + } + $names = $this->connection->createCommand('SHOW TABLES FROM ' . $this->quoteSimpleTableName($schema))->queryColumn(); + foreach ($names as &$name) { + $name = $schema . '.' . $name; + } + return $names; + } +} diff --git a/framework/db/dao/mysql/QueryBuilder.php b/framework/db/dao/mysql/QueryBuilder.php index 96ac877..100db2d 100644 --- a/framework/db/dao/mysql/QueryBuilder.php +++ b/framework/db/dao/mysql/QueryBuilder.php @@ -48,7 +48,7 @@ class QueryBuilder extends \yii\db\dao\QueryBuilder */ public function renameColumn($table, $name, $newName) { - $quotedTable = $this->schema->quoteTableName($table); + $quotedTable = $this->driver->quoteTableName($table); $row = $this->connection->createCommand('SHOW CREATE TABLE ' . $quotedTable)->queryRow(); if ($row === false) throw new CDbException(Yii::t('yii', 'Unable to find "{column}" in table "{table}".', array('{column}' => $name, '{table}' => $table))); @@ -61,13 +61,13 @@ class QueryBuilder extends \yii\db\dao\QueryBuilder if (preg_match_all('/^\s*`(.*?)`\s+(.*?),?$/m', $sql, $matches)) { foreach ($matches[1] as $i => $c) { if ($c === $name) { - return "ALTER TABLE $quotedTable CHANGE " . $this->schema->quoteColumnName($name) - . ' ' . $this->schema->quoteColumnName($newName) . ' ' . $matches[2][$i]; + return "ALTER TABLE $quotedTable CHANGE " . $this->driver->quoteColumnName($name) + . ' ' . $this->driver->quoteColumnName($newName) . ' ' . $matches[2][$i]; } } } // try to give back a SQL anyway - return "ALTER TABLE $quotedTable CHANGE " . $this->schema->quoteColumnName($name) . ' ' . $newName; + return "ALTER TABLE $quotedTable CHANGE " . $this->driver->quoteColumnName($name) . ' ' . $newName; } /** @@ -78,7 +78,7 @@ class QueryBuilder extends \yii\db\dao\QueryBuilder */ public function dropForeignKey($name, $table) { - return 'ALTER TABLE ' . $this->schema->quoteTableName($table) - . ' DROP FOREIGN KEY ' . $this->schema->quoteColumnName($name); + return 'ALTER TABLE ' . $this->driver->quoteTableName($table) + . ' DROP FOREIGN KEY ' . $this->driver->quoteColumnName($name); } } diff --git a/framework/db/dao/mysql/Schema.php b/framework/db/dao/mysql/Schema.php deleted file mode 100644 index 0f28e98..0000000 --- a/framework/db/dao/mysql/Schema.php +++ /dev/null @@ -1,173 +0,0 @@ - - * @link http://www.yiiframework.com/ - * @copyright Copyright © 2008-2012 Yii Software LLC - * @license http://www.yiiframework.com/license/ - */ - -namespace yii\db\dao\mysql; - -use yii\db\dao\TableSchema; - -/** - * Schema is the class for retrieving metadata information from a MySQL database (version 4.1.x and 5.x). - * - * @author Qiang Xue - * @since 2.0 - */ -class Schema extends \yii\db\dao\Schema -{ - /** - * Quotes a table name for use in a query. - * A simple table name does not schema prefix. - * @param string $name table name - * @return string the properly quoted table name - */ - public function quoteSimpleTableName($name) - { - return strpos($name, "`") !== false ? $name : "`" . $name . "`"; - } - - /** - * Quotes a column name for use in a query. - * A simple column name does not contain prefix. - * @param string $name column name - * @return string the properly quoted column name - */ - public function quoteSimpleColumnName($name) - { - return strpos($name, '`') !== false || $name === '*' ? $name : '`' . $name . '`'; - } - - /** - * Loads the metadata for the specified table. - * @param string $name table name - * @return \yii\db\dao\TableSchema driver dependent table metadata. Null if the table does not exist. - */ - protected function loadTableSchema($name) - { - $table = new TableSchema; - $this->resolveTableNames($table, $name); - - if ($this->findColumns($table)) { - $this->findConstraints($table); - return $table; - } - } - - /** - * Generates various kinds of table names. - * @param \yii\db\dao\TableSchema $table the table instance - * @param string $name the unquoted table name - */ - protected function resolveTableNames($table, $name) - { - $parts = explode('.', str_replace('`', '', $name)); - if (isset($parts[1])) { - $table->schemaName = $parts[0]; - $table->name = $parts[1]; - $table->quotedName = $this->quoteSimpleTableName($table->schemaName) . '.' . $this->quoteSimpleTableName($table->name); - } else { - $table->name = $parts[0]; - $table->quotedName = $this->quoteSimpleTableName($table->name); - } - } - - /** - * Creates a table column. - * @param array $column column metadata - * @return CDbColumnSchema normalized column metadata - */ - protected function createColumn($column) - { - $c = new ColumnSchema; - - $c->name = $column['Field']; - $c->quotedName = $this->quoteSimpleColumnName($c->name); - $c->allowNull = $column['Null'] === 'YES'; - $c->isPrimaryKey = strpos($column['Key'], 'PRI') !== false; - $c->autoIncrement = stripos($column['Extra'], 'auto_increment') !== false; - $c->initTypes($column['Type']); - $c->initDefaultValue($column['Default']); - - return $c; - } - - /** - * Collects the table column metadata. - * @param \yii\db\dao\TableSchema $table the table metadata - * @return boolean whether the table exists in the database - */ - protected function findColumns($table) - { - $sql = 'SHOW COLUMNS FROM ' . $table->quotedName; - try { - $columns = $this->connection->createCommand($sql)->queryAll(); - } - catch(\Exception $e) { - return false; - } - foreach ($columns as $column) { - $table->columns[$c->name] = $c = $this->createColumn($column); - if ($c->isPrimaryKey) { - if ($table->primaryKey === null) { - $table->primaryKey = $c->name; - } elseif (is_string($table->primaryKey)) { - $table->primaryKey = array($table->primaryKey, $c->name); - } else { - $table->primaryKey[] = $c->name; - } - if ($c->autoIncrement) { - $table->sequenceName = ''; - } - } - } - return true; - } - - /** - * Collects the foreign key column details for the given table. - * @param \yii\db\dao\TableSchema $table the table metadata - */ - protected function findConstraints($table) - { - $row = $this->connection->createCommand('SHOW CREATE TABLE ' . $table->quotedName)->queryRow(); - $matches = array(); - $regexp = '/FOREIGN KEY\s+\(([^\)]+)\)\s+REFERENCES\s+([^\(^\s]+)\s*\(([^\)]+)\)/mi'; - foreach ($row as $sql) { - if (preg_match_all($regexp, $sql, $matches, PREG_SET_ORDER)) { - foreach ($matches as $match) { - $fks = array_map('trim', explode(',', str_replace('`', '', $match[1]))); - $pks = array_map('trim', explode(',', str_replace('`', '', $match[3]))); - $constraint = array(str_replace('`', '', $match[2])); - foreach ($fks as $k => $name) { - $constraint[$name] = $pks[$k]; - } - $table->foreignKeys[] = $constraint; - } - break; - } - } - } - - /** - * Returns all table names in the database. - * @param string $schema the schema of the tables. Defaults to empty string, meaning the current or default schema. - * If not empty, the returned table names will be prefixed with the schema name. - * @return array all table names in the database. - */ - protected function findTableNames($schema = '') - { - if ($schema === '') { - return $this->connection->createCommand('SHOW TABLES')->queryColumn(); - } - $names = $this->connection->createCommand('SHOW TABLES FROM ' . $this->quoteSimpleTableName($schema))->queryColumn(); - foreach ($names as &$name) { - $name = $schema . '.' . $name; - } - return $names; - } -} diff --git a/upgrade.md b/upgrade.md index f93c4de..d38beba 100644 --- a/upgrade.md +++ b/upgrade.md @@ -24,6 +24,7 @@ Upgrading from v1.1.x --------------------- - All framework classes are now namespaced, and the name prefix `C` is removed. + - The format of path alias is changed to `@yii/base/Component`. In 1.x, this would be `system.base.CComponent`. See guide for more details. @@ -36,4 +37,7 @@ Upgrading from v1.1.x - `CFormModel` is removed. Please use `yii\base\Model` instead. +- `CDbCriteria` is replaced by `yii\db\dao\Query` which includes methods for + building a query. `CDbCommandBuilder` is replaced by `yii\db\dao\QueryBuilder` + which has cleaner and more complete support of query building capabilities.