From e150b7fae7623722cc3f1f9f4bea3d759385bf2d Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Tue, 15 Jan 2013 20:16:27 -0500 Subject: [PATCH] renamed Driver to Schema. --- framework/db/Connection.php | 102 ++++++------ framework/db/Driver.php | 295 --------------------------------- framework/db/Schema.php | 322 ++++++++++++++++++++++++++++++++++++ framework/db/Transaction.php | 2 +- framework/db/mysql/Driver.php | 269 ------------------------------ framework/db/mysql/QueryBuilder.php | 30 ++-- framework/db/mysql/Schema.php | 269 ++++++++++++++++++++++++++++++ framework/db/sqlite/Driver.php | 203 ----------------------- framework/db/sqlite/Schema.php | 203 +++++++++++++++++++++++ 9 files changed, 856 insertions(+), 839 deletions(-) delete mode 100644 framework/db/Driver.php create mode 100644 framework/db/Schema.php delete mode 100644 framework/db/mysql/Driver.php create mode 100644 framework/db/mysql/Schema.php delete mode 100644 framework/db/sqlite/Driver.php create mode 100644 framework/db/sqlite/Schema.php diff --git a/framework/db/Connection.php b/framework/db/Connection.php index 13a9a77..1fceb1e 100644 --- a/framework/db/Connection.php +++ b/framework/db/Connection.php @@ -10,6 +10,7 @@ namespace yii\db; use yii\db\Exception; +use yii\base\BadConfigException; /** * Connection represents a connection to a database via [PDO](http://www.php.net/manual/en/ref.pdo.php). @@ -87,8 +88,8 @@ use yii\db\Exception; * ~~~ * * @property boolean $isActive Whether the DB connection is established. This property is read-only. - * @property Transaction $currentTransaction The currently active transaction. Null if no active transaction. - * @property Driver $driver The database driver for the current connection. + * @property Transaction $transaction The currently active transaction. Null if no active transaction. + * @property Schema $schema The database schema information 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. @@ -237,34 +238,34 @@ class Connection extends \yii\base\ApplicationComponent */ public $initSQLs; /** - * @var array mapping between PDO driver names and [[Driver]] classes. + * @var array mapping between PDO driver names and [[Schema]] classes. * The keys of the array are PDO driver names while the values the corresponding - * driver class name or configuration. Please refer to [[\Yii::createObject]] for + * schema class name or configuration. Please refer to [[\Yii::createObject()]] for * details on how to specify a configuration. * - * This property is mainly used by [[getDriver()]] when fetching the database schema information. + * This property is mainly used by [[getSchema()]] when fetching the database schema information. * You normally do not need to set this property unless you want to use your own - * [[Driver]] class to support DBMS that is not supported by Yii. - */ - public $driverMap = array( - 'pgsql' => 'yii\db\pgsql\Driver', // PostgreSQL - 'mysqli' => 'yii\db\mysql\Driver', // MySQL - 'mysql' => 'yii\db\mysql\Driver', // MySQL - 'sqlite' => 'yii\db\sqlite\Driver', // sqlite 3 - 'sqlite2' => 'yii\db\sqlite\Driver', // sqlite 2 - 'mssql' => 'yi\db\dao\mssql\Driver', // Mssql driver on windows hosts - 'dblib' => 'yii\db\mssql\Driver', // dblib drivers on linux (and maybe others os) hosts - 'sqlsrv' => 'yii\db\mssql\Driver', // Mssql - 'oci' => 'yii\db\oci\Driver', // Oracle driver + * [[Schema]] class to support DBMS that is not supported by Yii. + */ + public $schemaMap = array( + 'pgsql' => 'yii\db\pgsql\Schema', // PostgreSQL + 'mysqli' => 'yii\db\mysql\Schema', // MySQL + 'mysql' => 'yii\db\mysql\Schema', // MySQL + 'sqlite' => 'yii\db\sqlite\Schema', // sqlite 3 + 'sqlite2' => 'yii\db\sqlite\Schema', // sqlite 2 + 'mssql' => 'yi\db\dao\mssql\Schema', // Mssql driver on windows hosts + 'dblib' => 'yii\db\mssql\Schema', // dblib drivers on linux (and maybe others os) hosts + 'sqlsrv' => 'yii\db\mssql\Schema', // Mssql + 'oci' => 'yii\db\oci\Schema', // Oracle driver ); /** * @var Transaction the currently active transaction */ private $_transaction; /** - * @var Driver the database driver + * @var Schema the database schema */ - private $_driver; + private $_schema; /** * Closes the connection when this component is being serialized. @@ -330,7 +331,7 @@ class Connection extends \yii\base\ApplicationComponent { if ($this->pdo === null) { if (empty($this->dsn)) { - throw new Exception('Connection.dsn cannot be empty.'); + throw new BadConfigException('Connection.dsn cannot be empty.'); } try { \Yii::trace('Opening DB connection: ' . $this->dsn, __CLASS__); @@ -354,7 +355,7 @@ class Connection extends \yii\base\ApplicationComponent if ($this->pdo !== null) { \Yii::trace('Closing DB connection: ' . $this->dsn, __CLASS__); $this->pdo = null; - $this->_driver = null; + $this->_schema = null; $this->_transaction = null; } } @@ -372,7 +373,7 @@ class Connection extends \yii\base\ApplicationComponent if (($pos = strpos($this->dsn, ':')) !== false) { $driver = strtolower(substr($this->dsn, 0, $pos)); if ($driver === 'mssql' || $driver === 'dblib' || $driver === 'sqlsrv') { - $pdoClass = 'mssql\PDO'; + $pdoClass = 'yii\db\mssql\PDO'; } } return new $pdoClass($this->dsn, $this->username, $this->password, $this->attributes); @@ -421,13 +422,9 @@ class Connection extends \yii\base\ApplicationComponent * Returns the currently active transaction. * @return Transaction the currently active transaction. Null if no active transaction. */ - public function getCurrentTransaction() + public function getTransaction() { - if ($this->_transaction !== null && $this->_transaction->isActive) { - return $this->_transaction; - } else { - return null; - } + return $this->_transaction && $this->_transaction->isActive ? $this->_transaction : null; } /** @@ -445,19 +442,22 @@ class Connection extends \yii\base\ApplicationComponent } /** - * Returns the metadata information for the underlying database. - * @return Driver the metadata information for the underlying database. + * Returns the schema information for the database opened by this connection. + * @return Schema the schema information for the database opened by this connection. + * @throws BadConfigException if there is no support for the current driver type */ - public function getDriver() + public function getSchema() { - if ($this->_driver !== null) { - return $this->_driver; + if ($this->_schema !== null) { + return $this->_schema; } else { $driver = $this->getDriverName(); - if (isset($this->driverMap[$driver])) { - return $this->_driver = \Yii::createObject($this->driverMap[$driver], $this); + if (isset($this->schemaMap[$driver])) { + $this->_schema = \Yii::createObject($this->schemaMap[$driver]); + $this->_schema->connection = $this; + return $this->_schema; } else { - throw new Exception("Connection does not support reading metadata for '$driver' database."); + throw new BadConfigException("Connection does not support reading schema information for '$driver' DBMS."); } } } @@ -468,18 +468,18 @@ class Connection extends \yii\base\ApplicationComponent */ public function getQueryBuilder() { - return $this->getDriver()->getQueryBuilder(); + return $this->getSchema()->getQueryBuilder(); } /** - * 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. + * Obtains the schema information for the named table. + * @param string $name 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. + * @return TableSchema table schema information. Null if the named table does not exist. */ public function getTableSchema($name, $refresh = false) { - return $this->getDriver()->getTableSchema($name, $refresh); + return $this->getSchema()->getTableSchema($name, $refresh); } /** @@ -490,8 +490,7 @@ class Connection extends \yii\base\ApplicationComponent */ public function getLastInsertID($sequenceName = '') { - $this->open(); - return $this->pdo->lastInsertId($sequenceName); + return $this->getSchema()->getLastInsertID($sequenceName); } /** @@ -503,16 +502,7 @@ class Connection extends \yii\base\ApplicationComponent */ public function quoteValue($str) { - if (!is_string($str)) { - return $str; - } - - $this->open(); - if (($value = $this->pdo->quote($str)) !== false) { - return $value; - } else { // the driver doesn't support quote (e.g. oci) - return "'" . addcslashes(str_replace("'", "''", $str), "\000\n\r\\\032") . "'"; - } + return $this->getSchema()->quoteValue($str); } /** @@ -525,7 +515,7 @@ class Connection extends \yii\base\ApplicationComponent */ public function quoteTableName($name) { - return $this->getDriver()->quoteTableName($name); + return $this->getSchema()->quoteTableName($name); } /** @@ -538,7 +528,7 @@ class Connection extends \yii\base\ApplicationComponent */ public function quoteColumnName($name) { - return $this->getDriver()->quoteColumnName($name); + return $this->getSchema()->quoteColumnName($name); } /** @@ -587,7 +577,7 @@ class Connection extends \yii\base\ApplicationComponent * and the second element the total time spent in SQL execution. * @see \yii\logging\Logger::getProfiling() */ - public function getStats() + public function getExecutionSummary() { $logger = \Yii::getLogger(); $timings = $logger->getProfiling(array('yii\db\Command::query', 'yii\db\Command::execute')); diff --git a/framework/db/Driver.php b/framework/db/Driver.php deleted file mode 100644 index 46ea8fc..0000000 --- a/framework/db/Driver.php +++ /dev/null @@ -1,295 +0,0 @@ - - * @since 2.0 - */ -abstract class Driver extends \yii\base\Object -{ - /** - * The followings are the supported abstract column data types. - */ - const TYPE_PK = 'pk'; - const TYPE_STRING = 'string'; - const TYPE_TEXT = 'text'; - const TYPE_SMALLINT = 'smallint'; - const TYPE_INTEGER = 'integer'; - const TYPE_BIGINT = 'bigint'; - const TYPE_FLOAT = 'float'; - const TYPE_DECIMAL = 'decimal'; - const TYPE_DATETIME = 'datetime'; - const TYPE_TIMESTAMP = 'timestamp'; - const TYPE_TIME = 'time'; - const TYPE_DATE = 'date'; - const TYPE_BINARY = 'binary'; - const TYPE_BOOLEAN = 'boolean'; - const TYPE_MONEY = 'money'; - - /** - * @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 metadata (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. - * @param array $config name-value pairs that will be used to initialize the object properties - */ - public function __construct($connection, $config = array()) - { - $this->connection = $connection; - parent::__construct($config); - } - - /** - * 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 = $this->getRealTableName($name); - - /** @var $cache \yii\caching\Cache */ - if ($db->enableSchemaCache && ($cache = \Yii::$application->getComponent($db->schemaCacheID)) !== null && !in_array($name, $db->schemaCacheExclude, true)) { - $key = $this->getCacheKey($name); - if ($refresh || ($table = $cache->get($key)) === false) { - $table = $this->loadTableSchema($realName); - if ($table !== null) { - $cache->set($key, $table, $db->schemaCacheDuration); - } - } - $this->_tables[$name] = $table; - } else { - $this->_tables[$name] = $table = $this->loadTableSchema($realName); - } - - 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 all cached table schemas so that they can be re-created later - * to reflect the database schema change. - */ - public function refresh() - { - /** @var $cache \yii\caching\Cache */ - if ($this->connection->enableSchemaCache && ($cache = \Yii::$application->getComponent($this->connection->schemaCacheID)) !== null) { - foreach ($this->_tables as $name => $table) { - $cache->delete($this->getCacheKey($name)); - } - } - $this->_tables = array(); - } - - /** - * 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.'); - } - - /** - * Quotes a table name for use in a query. - * If the table name contains schema prefix, the prefix will also be properly quoted. - * If the table name is already quoted or contains special characters including '(', '[[' and '{{', - * then this method will do nothing. - * @param string $name table name - * @return string the properly quoted table name - * @see quoteSimpleTableName - */ - public function quoteTableName($name) - { - if (strpos($name, '(') !== false || strpos($name, '[[') !== false || strpos($name, '{{') !== false) { - return $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 column name for use in a query. - * If the column name contains prefix, the prefix will also be properly quoted. - * If the column name is already quoted or contains special characters including '(', '[[' and '{{', - * then this method will do nothing. - * @param string $name column name - * @return string the properly quoted column name - * @see quoteSimpleColumnName - */ - public function quoteColumnName($name) - { - if (strpos($name, '(') !== false || strpos($name, '[[') !== false || strpos($name, '{{') !== false) { - return $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 table name for use in a query. - * A simple table name should contain the table name only without any schema prefix. - * If the table name is already quoted, this method will do nothing. - * @param string $name table name - * @return string the properly quoted table name - */ - public function quoteSimpleTableName($name) - { - return strpos($name, "'") !== false ? $name : "'" . $name . "'"; - } - - /** - * Quotes a simple column name for use in a query. - * A simple column name should contain the column name only without any prefix. - * If the column name is already quoted or is the asterisk character '*', this method will do nothing. - * @param string $name column name - * @return string the properly quoted column name - */ - public function quoteSimpleColumnName($name) - { - return strpos($name, '"') !== false || $name === '*' ? $name : '"' . $name . '"'; - } - - /** - * Returns the real name of a table name. - * This method will strip off curly brackets from the given table name - * and replace the percentage character in the name with [[Connection::tablePrefix]]. - * @param string $name the table name to be converted - * @return string the real name of the given table name - */ - public function getRealTableName($name) - { - if ($this->connection->enableAutoQuoting && strpos($name, '{{') !== false) { - $name = preg_replace('/\\{\\{(.*?)\\}\\}/', '\1', $name); - return str_replace('%', $this->connection->tablePrefix, $name); - } else { - return $name; - } - } -} diff --git a/framework/db/Schema.php b/framework/db/Schema.php new file mode 100644 index 0000000..c8e32ed --- /dev/null +++ b/framework/db/Schema.php @@ -0,0 +1,322 @@ + + * @since 2.0 + */ +abstract class Schema extends \yii\base\Object +{ + /** + * The followings are the supported abstract column data types. + */ + const TYPE_PK = 'pk'; + const TYPE_STRING = 'string'; + const TYPE_TEXT = 'text'; + const TYPE_SMALLINT = 'smallint'; + const TYPE_INTEGER = 'integer'; + const TYPE_BIGINT = 'bigint'; + const TYPE_FLOAT = 'float'; + const TYPE_DECIMAL = 'decimal'; + const TYPE_DATETIME = 'datetime'; + const TYPE_TIMESTAMP = 'timestamp'; + const TYPE_TIME = 'time'; + const TYPE_DATE = 'date'; + const TYPE_BINARY = 'binary'; + const TYPE_BOOLEAN = 'boolean'; + const TYPE_MONEY = 'money'; + + /** + * @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 metadata (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); + + + /** + * 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 = $this->getRealTableName($name); + + /** @var $cache \yii\caching\Cache */ + if ($db->enableSchemaCache && ($cache = \Yii::$application->getComponent($db->schemaCacheID)) !== null && !in_array($name, $db->schemaCacheExclude, true)) { + $key = $this->getCacheKey($name); + if ($refresh || ($table = $cache->get($key)) === false) { + $table = $this->loadTableSchema($realName); + if ($table !== null) { + $cache->set($key, $table, $db->schemaCacheDuration); + } + } + $this->_tables[$name] = $table; + } else { + $this->_tables[$name] = $table = $this->loadTableSchema($realName); + } + + 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 all cached table schemas so that they can be re-created later + * to reflect the database schema change. + */ + public function refresh() + { + /** @var $cache \yii\caching\Cache */ + if ($this->connection->enableSchemaCache && ($cache = \Yii::$application->getComponent($this->connection->schemaCacheID)) !== null) { + foreach ($this->_tables as $name => $table) { + $cache->delete($this->getCacheKey($name)); + } + } + $this->_tables = array(); + } + + /** + * 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.'); + } + + /** + * 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 = '') + { + if ($this->connection->isActive) { + return $this->connection->pdo->lastInsertId($sequenceName); + } else { + throw new Exception('DB Connection is not active.'); + } + } + + + /** + * Quotes a string value for use in a query. + * Note that if the parameter is not a string, it will be returned without change. + * @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) + { + if (!is_string($str)) { + return $str; + } + + $this->connection->open(); + if (($value = $this->connection->pdo->quote($str)) !== false) { + return $value; + } else { // the driver doesn't support quote (e.g. oci) + return "'" . addcslashes(str_replace("'", "''", $str), "\000\n\r\\\032") . "'"; + } + } + + /** + * Quotes a table name for use in a query. + * If the table name contains schema prefix, the prefix will also be properly quoted. + * If the table name is already quoted or contains special characters including '(', '[[' and '{{', + * then this method will do nothing. + * @param string $name table name + * @return string the properly quoted table name + * @see quoteSimpleTableName + */ + public function quoteTableName($name) + { + if (strpos($name, '(') !== false || strpos($name, '[[') !== false || strpos($name, '{{') !== false) { + return $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 column name for use in a query. + * If the column name contains prefix, the prefix will also be properly quoted. + * If the column name is already quoted or contains special characters including '(', '[[' and '{{', + * then this method will do nothing. + * @param string $name column name + * @return string the properly quoted column name + * @see quoteSimpleColumnName + */ + public function quoteColumnName($name) + { + if (strpos($name, '(') !== false || strpos($name, '[[') !== false || strpos($name, '{{') !== false) { + return $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 table name for use in a query. + * A simple table name should contain the table name only without any schema prefix. + * If the table name is already quoted, this method will do nothing. + * @param string $name table name + * @return string the properly quoted table name + */ + public function quoteSimpleTableName($name) + { + return strpos($name, "'") !== false ? $name : "'" . $name . "'"; + } + + /** + * Quotes a simple column name for use in a query. + * A simple column name should contain the column name only without any prefix. + * If the column name is already quoted or is the asterisk character '*', this method will do nothing. + * @param string $name column name + * @return string the properly quoted column name + */ + public function quoteSimpleColumnName($name) + { + return strpos($name, '"') !== false || $name === '*' ? $name : '"' . $name . '"'; + } + + /** + * Returns the real name of a table name. + * This method will strip off curly brackets from the given table name + * and replace the percentage character in the name with [[Connection::tablePrefix]]. + * @param string $name the table name to be converted + * @return string the real name of the given table name + */ + public function getRealTableName($name) + { + if ($this->connection->enableAutoQuoting && strpos($name, '{{') !== false) { + $name = preg_replace('/\\{\\{(.*?)\\}\\}/', '\1', $name); + return str_replace('%', $this->connection->tablePrefix, $name); + } else { + return $name; + } + } +} diff --git a/framework/db/Transaction.php b/framework/db/Transaction.php index fc9867a..c1c7f0d 100644 --- a/framework/db/Transaction.php +++ b/framework/db/Transaction.php @@ -67,7 +67,7 @@ class Transaction extends \yii\base\Object { if (!$this->_active) { if ($this->connection === null) { - throw new BadConfigException('Transaction::connection must be set.'); + throw new BadConfigException('Transaction.connection must be set.'); } \Yii::trace('Starting transaction', __CLASS__); $this->connection->open(); diff --git a/framework/db/mysql/Driver.php b/framework/db/mysql/Driver.php deleted file mode 100644 index 6946ac5..0000000 --- a/framework/db/mysql/Driver.php +++ /dev/null @@ -1,269 +0,0 @@ - - * @since 2.0 - */ -class Driver extends \yii\db\Driver -{ - /** - * @var array mapping from physical column types (keys) to abstract column types (values) - */ - public $typeMap = array( - 'tinyint' => self::TYPE_SMALLINT, - 'bit' => self::TYPE_SMALLINT, - 'smallint' => self::TYPE_SMALLINT, - 'mediumint' => self::TYPE_INTEGER, - 'int' => self::TYPE_INTEGER, - 'integer' => self::TYPE_INTEGER, - 'bigint' => self::TYPE_BIGINT, - 'float' => self::TYPE_FLOAT, - 'double' => self::TYPE_FLOAT, - 'real' => self::TYPE_FLOAT, - 'decimal' => self::TYPE_DECIMAL, - 'numeric' => self::TYPE_DECIMAL, - 'tinytext' => self::TYPE_TEXT, - 'mediumtext' => self::TYPE_TEXT, - 'longtext' => self::TYPE_TEXT, - 'text' => self::TYPE_TEXT, - 'varchar' => self::TYPE_STRING, - 'string' => self::TYPE_STRING, - 'char' => self::TYPE_STRING, - 'datetime' => self::TYPE_DATETIME, - 'year' => self::TYPE_DATE, - 'date' => self::TYPE_DATE, - 'time' => self::TYPE_TIME, - 'timestamp' => self::TYPE_TIMESTAMP, - 'enum' => self::TYPE_STRING, - ); - - /** - * 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 . '`'; - } - - /** - * Creates a query builder for the MySQL database. - * @return QueryBuilder query builder instance - */ - public function createQueryBuilder() - { - return new QueryBuilder($this->connection); - } - - /** - * Loads the metadata for the specified table. - * @param string $name table name - * @return \yii\db\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; - } - } - - /** - * Resolves the table name and schema name (if any). - * @param \yii\db\TableSchema $table the table metadata object - * @param string $name the 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 ColumnSchema 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->dbType = $column['Type']; - $this->resolveColumnType($c); - $c->resolvePhpType(); - - $this->resolveColumnDefault($c, $column['Default']); - - return $c; - } - - /** - * Resolves the default value for the column. - * @param \yii\db\ColumnSchema $column the column metadata object - * @param string $value the default value fetched from database - */ - protected function resolveColumnDefault($column, $value) - { - if ($column->type !== 'timestamp' || $value !== 'CURRENT_TIMESTAMP') { - $column->defaultValue = $column->typecast($value); - } - } - - /** - * Resolves the abstract data type for the column. - * @param \yii\db\ColumnSchema $column the column metadata object - */ - public function resolveColumnType($column) - { - $column->type = self::TYPE_STRING; - $column->unsigned = strpos($column->dbType, 'unsigned') !== false; - - if (preg_match('/^(\w+)(?:\(([^\)]+)\))?/', $column->dbType, $matches)) { - $type = $matches[1]; - if (isset($this->typeMap[$type])) { - $column->type = $this->typeMap[$type]; - } - - if (!empty($matches[2])) { - if ($type === 'enum') { - $values = explode(',', $matches[2]); - foreach ($values as $i => $value) { - $values[$i] = trim($value, "'"); - } - $column->enumValues = $values; - } else { - $values = explode(',', $matches[2]); - $column->size = $column->precision = (int)$values[0]; - if (isset($values[1])) { - $column->scale = (int)$values[1]; - } - if ($column->size === 1 && ($type === 'tinyint' || $type === 'bit')) { - $column->type = 'boolean'; - } elseif ($type === 'bit') { - if ($column->size > 32) { - $column->type = 'bigint'; - } elseif ($column->size === 32) { - $column->type = 'integer'; - } - } - } - } - } - } - - /** - * Collects the metadata of table columns. - * @param \yii\db\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) { - $column = $this->createColumn($column); - $table->columns[$column->name] = $column; - if ($column->isPrimaryKey) { - $table->primaryKey[] = $column->name; - if ($column->autoIncrement) { - $table->sequenceName = ''; - } - } - } - return true; - } - - /** - * Collects the foreign key column details for the given table. - * @param \yii\db\TableSchema $table the table metadata - */ - protected function findConstraints($table) - { - $row = $this->connection->createCommand('SHOW CREATE TABLE ' . $table->quotedName)->queryRow(); - if (isset($row['Create Table'])) { - $sql = $row['Create Table']; - } else { - $row = array_values($row); - $sql = $row[1]; - } - - $regexp = '/FOREIGN KEY\s+\(([^\)]+)\)\s+REFERENCES\s+([^\(^\s]+)\s*\(([^\)]+)\)/mi'; - 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; - } - } - } - - /** - * 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(); - } - $sql = 'SHOW TABLES FROM ' . $this->quoteSimpleTableName($schema); - $names = $this->connection->createCommand($sql)->queryColumn(); - foreach ($names as $i => $name) { - $names[$i] = $schema . '.' . $name; - } - return $names; - } -} diff --git a/framework/db/mysql/QueryBuilder.php b/framework/db/mysql/QueryBuilder.php index b044f2c..cab1b14 100644 --- a/framework/db/mysql/QueryBuilder.php +++ b/framework/db/mysql/QueryBuilder.php @@ -23,21 +23,21 @@ class QueryBuilder extends \yii\db\QueryBuilder * @var array mapping from abstract column types (keys) to physical column types (values). */ public $typeMap = array( - Driver::TYPE_PK => 'int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY', - Driver::TYPE_STRING => 'varchar(255)', - Driver::TYPE_TEXT => 'text', - Driver::TYPE_SMALLINT => 'smallint(6)', - Driver::TYPE_INTEGER => 'int(11)', - Driver::TYPE_BIGINT => 'bigint(20)', - Driver::TYPE_FLOAT => 'float', - Driver::TYPE_DECIMAL => 'decimal', - Driver::TYPE_DATETIME => 'datetime', - Driver::TYPE_TIMESTAMP => 'timestamp', - Driver::TYPE_TIME => 'time', - Driver::TYPE_DATE => 'date', - Driver::TYPE_BINARY => 'blob', - Driver::TYPE_BOOLEAN => 'tinyint(1)', - Driver::TYPE_MONEY => 'decimal(19,4)', + Schema::TYPE_PK => 'int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY', + Schema::TYPE_STRING => 'varchar(255)', + Schema::TYPE_TEXT => 'text', + Schema::TYPE_SMALLINT => 'smallint(6)', + Schema::TYPE_INTEGER => 'int(11)', + Schema::TYPE_BIGINT => 'bigint(20)', + Schema::TYPE_FLOAT => 'float', + Schema::TYPE_DECIMAL => 'decimal', + Schema::TYPE_DATETIME => 'datetime', + Schema::TYPE_TIMESTAMP => 'timestamp', + Schema::TYPE_TIME => 'time', + Schema::TYPE_DATE => 'date', + Schema::TYPE_BINARY => 'blob', + Schema::TYPE_BOOLEAN => 'tinyint(1)', + Schema::TYPE_MONEY => 'decimal(19,4)', ); /** diff --git a/framework/db/mysql/Schema.php b/framework/db/mysql/Schema.php new file mode 100644 index 0000000..25c84fa --- /dev/null +++ b/framework/db/mysql/Schema.php @@ -0,0 +1,269 @@ + + * @since 2.0 + */ +class Schema extends \yii\db\Schema +{ + /** + * @var array mapping from physical column types (keys) to abstract column types (values) + */ + public $typeMap = array( + 'tinyint' => self::TYPE_SMALLINT, + 'bit' => self::TYPE_SMALLINT, + 'smallint' => self::TYPE_SMALLINT, + 'mediumint' => self::TYPE_INTEGER, + 'int' => self::TYPE_INTEGER, + 'integer' => self::TYPE_INTEGER, + 'bigint' => self::TYPE_BIGINT, + 'float' => self::TYPE_FLOAT, + 'double' => self::TYPE_FLOAT, + 'real' => self::TYPE_FLOAT, + 'decimal' => self::TYPE_DECIMAL, + 'numeric' => self::TYPE_DECIMAL, + 'tinytext' => self::TYPE_TEXT, + 'mediumtext' => self::TYPE_TEXT, + 'longtext' => self::TYPE_TEXT, + 'text' => self::TYPE_TEXT, + 'varchar' => self::TYPE_STRING, + 'string' => self::TYPE_STRING, + 'char' => self::TYPE_STRING, + 'datetime' => self::TYPE_DATETIME, + 'year' => self::TYPE_DATE, + 'date' => self::TYPE_DATE, + 'time' => self::TYPE_TIME, + 'timestamp' => self::TYPE_TIMESTAMP, + 'enum' => self::TYPE_STRING, + ); + + /** + * 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 . '`'; + } + + /** + * Creates a query builder for the MySQL database. + * @return QueryBuilder query builder instance + */ + public function createQueryBuilder() + { + return new QueryBuilder($this->connection); + } + + /** + * Loads the metadata for the specified table. + * @param string $name table name + * @return \yii\db\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; + } + } + + /** + * Resolves the table name and schema name (if any). + * @param \yii\db\TableSchema $table the table metadata object + * @param string $name the 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 ColumnSchema 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->dbType = $column['Type']; + $this->resolveColumnType($c); + $c->resolvePhpType(); + + $this->resolveColumnDefault($c, $column['Default']); + + return $c; + } + + /** + * Resolves the default value for the column. + * @param \yii\db\ColumnSchema $column the column metadata object + * @param string $value the default value fetched from database + */ + protected function resolveColumnDefault($column, $value) + { + if ($column->type !== 'timestamp' || $value !== 'CURRENT_TIMESTAMP') { + $column->defaultValue = $column->typecast($value); + } + } + + /** + * Resolves the abstract data type for the column. + * @param \yii\db\ColumnSchema $column the column metadata object + */ + public function resolveColumnType($column) + { + $column->type = self::TYPE_STRING; + $column->unsigned = strpos($column->dbType, 'unsigned') !== false; + + if (preg_match('/^(\w+)(?:\(([^\)]+)\))?/', $column->dbType, $matches)) { + $type = $matches[1]; + if (isset($this->typeMap[$type])) { + $column->type = $this->typeMap[$type]; + } + + if (!empty($matches[2])) { + if ($type === 'enum') { + $values = explode(',', $matches[2]); + foreach ($values as $i => $value) { + $values[$i] = trim($value, "'"); + } + $column->enumValues = $values; + } else { + $values = explode(',', $matches[2]); + $column->size = $column->precision = (int)$values[0]; + if (isset($values[1])) { + $column->scale = (int)$values[1]; + } + if ($column->size === 1 && ($type === 'tinyint' || $type === 'bit')) { + $column->type = 'boolean'; + } elseif ($type === 'bit') { + if ($column->size > 32) { + $column->type = 'bigint'; + } elseif ($column->size === 32) { + $column->type = 'integer'; + } + } + } + } + } + } + + /** + * Collects the metadata of table columns. + * @param \yii\db\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) { + $column = $this->createColumn($column); + $table->columns[$column->name] = $column; + if ($column->isPrimaryKey) { + $table->primaryKey[] = $column->name; + if ($column->autoIncrement) { + $table->sequenceName = ''; + } + } + } + return true; + } + + /** + * Collects the foreign key column details for the given table. + * @param \yii\db\TableSchema $table the table metadata + */ + protected function findConstraints($table) + { + $row = $this->connection->createCommand('SHOW CREATE TABLE ' . $table->quotedName)->queryRow(); + if (isset($row['Create Table'])) { + $sql = $row['Create Table']; + } else { + $row = array_values($row); + $sql = $row[1]; + } + + $regexp = '/FOREIGN KEY\s+\(([^\)]+)\)\s+REFERENCES\s+([^\(^\s]+)\s*\(([^\)]+)\)/mi'; + 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; + } + } + } + + /** + * 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(); + } + $sql = 'SHOW TABLES FROM ' . $this->quoteSimpleTableName($schema); + $names = $this->connection->createCommand($sql)->queryColumn(); + foreach ($names as $i => $name) { + $names[$i] = $schema . '.' . $name; + } + return $names; + } +} diff --git a/framework/db/sqlite/Driver.php b/framework/db/sqlite/Driver.php deleted file mode 100644 index 4854ff3..0000000 --- a/framework/db/sqlite/Driver.php +++ /dev/null @@ -1,203 +0,0 @@ - - * @since 2.0 - */ -class Driver extends \yii\db\Driver -{ - /** - * @var array mapping from physical column types (keys) to abstract column types (values) - */ - public $typeMap = array( - 'tinyint' => self::TYPE_SMALLINT, - 'bit' => self::TYPE_SMALLINT, - 'smallint' => self::TYPE_SMALLINT, - 'mediumint' => self::TYPE_INTEGER, - 'int' => self::TYPE_INTEGER, - 'integer' => self::TYPE_INTEGER, - 'bigint' => self::TYPE_BIGINT, - 'float' => self::TYPE_FLOAT, - 'double' => self::TYPE_FLOAT, - 'real' => self::TYPE_FLOAT, - 'decimal' => self::TYPE_DECIMAL, - 'numeric' => self::TYPE_DECIMAL, - 'tinytext' => self::TYPE_TEXT, - 'mediumtext' => self::TYPE_TEXT, - 'longtext' => self::TYPE_TEXT, - 'text' => self::TYPE_TEXT, - 'varchar' => self::TYPE_STRING, - 'string' => self::TYPE_STRING, - 'char' => self::TYPE_STRING, - 'datetime' => self::TYPE_DATETIME, - 'year' => self::TYPE_DATE, - 'date' => self::TYPE_DATE, - 'time' => self::TYPE_TIME, - 'timestamp' => self::TYPE_TIMESTAMP, - 'enum' => self::TYPE_STRING, - ); - - /** - * Creates a query builder for the MySQL 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. - * @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 = '') - { - $sql = "SELECT DISTINCT tbl_name FROM sqlite_master WHERE tbl_name<>'sqlite_sequence'"; - return $this->connection->createCommand($sql)->queryColumn(); - } - - /** - * Loads the metadata for the specified table. - * @param string $name table name - * @return \yii\db\TableSchema driver dependent table metadata. Null if the table does not exist. - */ - protected function loadTableSchema($name) - { - $table = new TableSchema; - $table->name = $name; - $table->quotedName = $this->quoteTableName($name); - - if ($this->findColumns($table)) { - $this->findConstraints($table); - return $table; - } - } - - /** - * Collects the table column metadata. - * @param \yii\db\TableSchema $table the table metadata - * @return boolean whether the table exists in the database - */ - protected function findColumns($table) - { - $sql = "PRAGMA table_info({$table->quotedName})"; - $columns = $this->connection->createCommand($sql)->queryAll(); - if (empty($columns)) { - return false; - } - - foreach ($columns as $column) { - $column = $this->createColumn($column); - $table->columns[$column->name] = $column; - if ($column->isPrimaryKey) { - $table->primaryKey[] = $column->name; - } - } - if (count($table->primaryKey) === 1 && !strncasecmp($table->columns[$table->primaryKey[0]]->dbType, 'int', 3)) { - $table->sequenceName = ''; - $table->columns[$table->primaryKey[0]]->autoIncrement = true; - } - - return true; - } - - /** - * Collects the foreign key column details for the given table. - * @param \yii\db\TableSchema $table the table metadata - */ - protected function findConstraints($table) - { - $sql = "PRAGMA foreign_key_list({$table->quotedName})"; - $keys = $this->connection->createCommand($sql)->queryAll(); - foreach ($keys as $key) { - $table->foreignKeys[] = array($key['table'], $key['from'] => $key['to']); - } - } - - /** - * Creates a table column. - * @param array $column column metadata - * @return ColumnSchema normalized column metadata - */ - protected function createColumn($column) - { - $c = new ColumnSchema; - $c->name = $column['name']; - $c->quotedName = $this->quoteSimpleColumnName($c->name); - $c->allowNull = !$column['notnull']; - $c->isPrimaryKey = $column['pk'] != 0; - - $c->dbType = $column['type']; - $this->resolveColumnType($c); - $c->resolvePhpType(); - - $this->resolveColumnDefault($c, $column['dflt_value']); - - return $c; - } - - /** - * Resolves the abstract data type for the column. - * @param \yii\db\ColumnSchema $column the column metadata object - */ - public function resolveColumnType($column) - { - $column->type = self::TYPE_STRING; - $column->unsigned = strpos($column->dbType, 'unsigned') !== false; - - if (preg_match('/^(\w+)(?:\(([^\)]+)\))?/', $column->dbType, $matches)) { - $type = $matches[1]; - if (isset($this->typeMap[$type])) { - $column->type = $this->typeMap[$type]; - } - - if (!empty($matches[2])) { - $values = explode(',', $matches[2]); - $column->size = $column->precision = (int)$values[0]; - if (isset($values[1])) { - $column->scale = (int)$values[1]; - } - if ($column->size === 1 && ($type === 'tinyint' || $type === 'bit')) { - $column->type = 'boolean'; - } elseif ($type === 'bit') { - if ($column->size > 32) { - $column->type = 'bigint'; - } elseif ($column->size === 32) { - $column->type = 'integer'; - } - } - } - } - } - - /** - * Resolves the default value for the column. - * @param \yii\db\ColumnSchema $column the column metadata object - * @param string $value the default value fetched from database - */ - protected function resolveColumnDefault($column, $value) - { - if ($column->type === 'string') { - $column->defaultValue = trim($value, "'\""); - } else { - $column->defaultValue = $column->typecast(strcasecmp($value, 'null') ? $value : null); - } - } -} diff --git a/framework/db/sqlite/Schema.php b/framework/db/sqlite/Schema.php new file mode 100644 index 0000000..adfabdc --- /dev/null +++ b/framework/db/sqlite/Schema.php @@ -0,0 +1,203 @@ + + * @since 2.0 + */ +class Schema extends \yii\db\Schema +{ + /** + * @var array mapping from physical column types (keys) to abstract column types (values) + */ + public $typeMap = array( + 'tinyint' => self::TYPE_SMALLINT, + 'bit' => self::TYPE_SMALLINT, + 'smallint' => self::TYPE_SMALLINT, + 'mediumint' => self::TYPE_INTEGER, + 'int' => self::TYPE_INTEGER, + 'integer' => self::TYPE_INTEGER, + 'bigint' => self::TYPE_BIGINT, + 'float' => self::TYPE_FLOAT, + 'double' => self::TYPE_FLOAT, + 'real' => self::TYPE_FLOAT, + 'decimal' => self::TYPE_DECIMAL, + 'numeric' => self::TYPE_DECIMAL, + 'tinytext' => self::TYPE_TEXT, + 'mediumtext' => self::TYPE_TEXT, + 'longtext' => self::TYPE_TEXT, + 'text' => self::TYPE_TEXT, + 'varchar' => self::TYPE_STRING, + 'string' => self::TYPE_STRING, + 'char' => self::TYPE_STRING, + 'datetime' => self::TYPE_DATETIME, + 'year' => self::TYPE_DATE, + 'date' => self::TYPE_DATE, + 'time' => self::TYPE_TIME, + 'timestamp' => self::TYPE_TIMESTAMP, + 'enum' => self::TYPE_STRING, + ); + + /** + * Creates a query builder for the MySQL 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. + * @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 = '') + { + $sql = "SELECT DISTINCT tbl_name FROM sqlite_master WHERE tbl_name<>'sqlite_sequence'"; + return $this->connection->createCommand($sql)->queryColumn(); + } + + /** + * Loads the metadata for the specified table. + * @param string $name table name + * @return \yii\db\TableSchema driver dependent table metadata. Null if the table does not exist. + */ + protected function loadTableSchema($name) + { + $table = new TableSchema; + $table->name = $name; + $table->quotedName = $this->quoteTableName($name); + + if ($this->findColumns($table)) { + $this->findConstraints($table); + return $table; + } + } + + /** + * Collects the table column metadata. + * @param \yii\db\TableSchema $table the table metadata + * @return boolean whether the table exists in the database + */ + protected function findColumns($table) + { + $sql = "PRAGMA table_info({$table->quotedName})"; + $columns = $this->connection->createCommand($sql)->queryAll(); + if (empty($columns)) { + return false; + } + + foreach ($columns as $column) { + $column = $this->createColumn($column); + $table->columns[$column->name] = $column; + if ($column->isPrimaryKey) { + $table->primaryKey[] = $column->name; + } + } + if (count($table->primaryKey) === 1 && !strncasecmp($table->columns[$table->primaryKey[0]]->dbType, 'int', 3)) { + $table->sequenceName = ''; + $table->columns[$table->primaryKey[0]]->autoIncrement = true; + } + + return true; + } + + /** + * Collects the foreign key column details for the given table. + * @param \yii\db\TableSchema $table the table metadata + */ + protected function findConstraints($table) + { + $sql = "PRAGMA foreign_key_list({$table->quotedName})"; + $keys = $this->connection->createCommand($sql)->queryAll(); + foreach ($keys as $key) { + $table->foreignKeys[] = array($key['table'], $key['from'] => $key['to']); + } + } + + /** + * Creates a table column. + * @param array $column column metadata + * @return ColumnSchema normalized column metadata + */ + protected function createColumn($column) + { + $c = new ColumnSchema; + $c->name = $column['name']; + $c->quotedName = $this->quoteSimpleColumnName($c->name); + $c->allowNull = !$column['notnull']; + $c->isPrimaryKey = $column['pk'] != 0; + + $c->dbType = $column['type']; + $this->resolveColumnType($c); + $c->resolvePhpType(); + + $this->resolveColumnDefault($c, $column['dflt_value']); + + return $c; + } + + /** + * Resolves the abstract data type for the column. + * @param \yii\db\ColumnSchema $column the column metadata object + */ + public function resolveColumnType($column) + { + $column->type = self::TYPE_STRING; + $column->unsigned = strpos($column->dbType, 'unsigned') !== false; + + if (preg_match('/^(\w+)(?:\(([^\)]+)\))?/', $column->dbType, $matches)) { + $type = $matches[1]; + if (isset($this->typeMap[$type])) { + $column->type = $this->typeMap[$type]; + } + + if (!empty($matches[2])) { + $values = explode(',', $matches[2]); + $column->size = $column->precision = (int)$values[0]; + if (isset($values[1])) { + $column->scale = (int)$values[1]; + } + if ($column->size === 1 && ($type === 'tinyint' || $type === 'bit')) { + $column->type = 'boolean'; + } elseif ($type === 'bit') { + if ($column->size > 32) { + $column->type = 'bigint'; + } elseif ($column->size === 32) { + $column->type = 'integer'; + } + } + } + } + } + + /** + * Resolves the default value for the column. + * @param \yii\db\ColumnSchema $column the column metadata object + * @param string $value the default value fetched from database + */ + protected function resolveColumnDefault($column, $value) + { + if ($column->type === 'string') { + $column->defaultValue = trim($value, "'\""); + } else { + $column->defaultValue = $column->typecast(strcasecmp($value, 'null') ? $value : null); + } + } +}