diff --git a/framework/db/dao/Connection.php b/framework/db/dao/Connection.php index 37d567d..edea75b 100644 --- a/framework/db/dao/Connection.php +++ b/framework/db/dao/Connection.php @@ -451,11 +451,12 @@ class Connection extends \yii\base\ApplicationComponent } else { $driver = $this->getDriverName(); - if (isset($this->schemaMap[$driver])) - return $this->_schema = Yii::createComponent($this->schemaMap[$driver], $this); - else - throw new CDbException(Yii::t('yii', 'Connection does not support reading schema for {driver} database.', - array('{driver}' => $driver))); + if (isset($this->schemaMap[$driver])) { + return $this->_schema = \Yii::createComponent($this->schemaMap[$driver], $this); + } + else { + throw new Exception("Connection does not support reading schema for '$driver' database."); + } } } diff --git a/framework/db/dao/QueryBuilder.php b/framework/db/dao/QueryBuilder.php index a071593..6d97876 100644 --- a/framework/db/dao/QueryBuilder.php +++ b/framework/db/dao/QueryBuilder.php @@ -18,19 +18,32 @@ namespace yii\db\dao; */ class QueryBuilder extends \yii\base\Component { - private $_connection; - - public function __construct($connection) - { - $this->_connection = $connection; - } - /** - * @return CDbConnection the connection associated with this command + * @var array the abstract column types mapped to physical column types. */ - public function getConnection() + public $columnTypes = array( + 'pk' => 'int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY', + 'string' => 'varchar(255)', + 'text' => 'text', + 'integer' => 'int(11)', + 'float' => 'float', + 'decimal' => 'decimal', + 'datetime' => 'datetime', + 'timestamp' => 'timestamp', + 'time' => 'time', + 'date' => 'date', + 'binary' => 'blob', + 'boolean' => 'tinyint(1)', + 'money' => 'decimal(19,4)', + ); + + public $connection; + public $schema; + + public function __construct($schema) { - return $this->_connection; + $this->connection = $schema->getDbConnection(); + $this->schema = $schema; } public function build($query) @@ -69,14 +82,14 @@ class QueryBuilder extends \yii\base\Component public function createTable($table, $columns, $options = null) { $cols = array(); - foreach ($columns as $name => $type) - { - if (is_string($name)) - $cols[] = "\t" . $this->quoteColumnName($name) . ' ' . $this->getColumnType($type); + foreach ($columns as $name => $type) { + if (is_string($name)) { + $cols[] = "\t" . $this->schema->quoteColumnName($name) . ' ' . $this->schema->getColumnType($type); + } else $cols[] = "\t" . $type; } - $sql = "CREATE TABLE " . $this->quoteTableName($table) . " (\n" . implode(",\n", $cols) . "\n)"; + $sql = "CREATE TABLE " . $this->schema->quoteTableName($table) . " (\n" . implode(",\n", $cols) . "\n)"; return $options === null ? $sql : $sql . ' ' . $options; } @@ -253,6 +266,67 @@ class QueryBuilder extends \yii\base\Component return 'DROP INDEX ' . $this->quoteTableName($name) . ' ON ' . $this->quoteTableName($table); } + /** + * Resets the sequence value of a table's primary key. + * The sequence will be reset such that the primary key of the next new row inserted + * will have the specified value or 1. + * @param CDbTableSchema $table the table schema whose primary key sequence will be reset + * @param mixed $value the value for the primary key of the next new row inserted. If this is not set, + * the next new row's primary key will have a value 1. + */ + public function resetSequence($table, $value = null) + { + } + + /** + * Enables or disables integrity check. + * @param boolean $check whether to turn on or off the integrity check. + * @param string $schema the schema of the tables. Defaults to empty string, meaning the current or default schema. + */ + public function checkIntegrity($check = true, $schema = '') + { + } + + /** + * Converts an abstract column type into a physical column type. + * The conversion is done using the type map specified in {@link columnTypes}. + * These abstract column types are supported (using MySQL as example to explain the corresponding + * physical types): + * + * + * If the abstract type contains two or more parts separated by spaces (e.g. "string NOT NULL"), then only + * the first part will be converted, and the rest of the parts will be appended to the conversion result. + * For example, 'string NOT NULL' is converted to 'varchar(255) NOT NULL'. + * @param string $type abstract column type + * @return string physical column type. + */ + public function getColumnType($type) + { + if (isset($this->columnTypes[$type])) { + return $this->columnTypes[$type]; + } + elseif (($pos = strpos($type, ' ')) !== false) { + $t = substr($type, 0, $pos); + return (isset($this->columnTypes[$t]) ? $this->columnTypes[$t] : $t) . substr($type, $pos); + } + else { + return $type; + } + } + protected function buildSelect($query) { $select = $query->distinct ? 'SELECT DISTINCT' : 'SELECT'; @@ -275,10 +349,10 @@ class QueryBuilder extends \yii\base\Component } 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->connection->quoteSimpleColumnName($matches[2]); } else { - $columns[$i] = $this->_connection->quoteColumnName($column); + $columns[$i] = $this->connection->quoteColumnName($column); } } } @@ -299,10 +373,10 @@ class QueryBuilder extends \yii\base\Component foreach ($tables as $i => $table) { if (strpos($table, '(') === false) { if (preg_match('/^(.*?)(?i:\s+as\s+|\s+)(.*)$/i', $table, $matches)) { // with alias - $tables[$i] = $this->_connection->quoteTableName($matches[1]) . ' ' . $this->_connection->quoteTableName($matches[2]); + $tables[$i] = $this->connection->quoteTableName($matches[1]) . ' ' . $this->connection->quoteTableName($matches[2]); } else { - $tables[$i] = $this->_connection->quoteTableName($table); + $tables[$i] = $this->connection->quoteTableName($table); } } } @@ -326,10 +400,10 @@ class QueryBuilder extends \yii\base\Component $table = $join[1]; if (strpos($table,'(')===false) { if (preg_match('/^(.*?)(?i:\s+as\s+|\s+)(.*)$/', $table, $matches)) { // with alias - $table = $this->_connection->quoteTableName($matches[1]).' '.$this->_connection->quoteTableName($matches[2]); + $table = $this->connection->quoteTableName($matches[1]).' '.$this->connection->quoteTableName($matches[2]); } else { - $table = $this->_connection->quoteTableName($table); + $table = $this->connection->quoteTableName($table); } } $joins[$i] = strtoupper($join[0]) . ' ' . $table; @@ -371,7 +445,7 @@ class QueryBuilder extends \yii\base\Component $columns[$i] = (string)$column; } elseif (strpos($column, '(') === false) { - $columns[$i] = $this->_connection->quoteColumnName($column); + $columns[$i] = $this->connection->quoteColumnName($column); } } return 'GROUP BY ' . implode(', ', $columns); @@ -402,10 +476,10 @@ class QueryBuilder extends \yii\base\Component } elseif (strpos($column, '(') === false) { if (preg_match('/^(.*?)\s+(asc|desc)$/i', $column, $matches)) { - $columns[$i] = $this->_connection->quoteColumnName($matches[1]) . ' ' . strtoupper($matches[2]); + $columns[$i] = $this->connection->quoteColumnName($matches[1]) . ' ' . strtoupper($matches[2]); } else { - $columns[$i] = $this->_connection->quoteColumnName($column); + $columns[$i] = $this->connection->quoteColumnName($column); } } } @@ -435,7 +509,7 @@ class QueryBuilder extends \yii\base\Component } foreach ($unions as $i => $union) { if ($union instanceof Query) { - $unions[$i] = $union->getSql($this->_connection); + $unions[$i] = $union->getSql($this->connection); } } return "UNION (\n" . implode("\n) UNION (\n", $unions) . "\n)"; @@ -469,7 +543,7 @@ class QueryBuilder extends \yii\base\Component $column = $conditions[1]; if (strpos($column, '(') === false) { - $column = $this->_connection->quoteColumnName($column); + $column = $this->connection->quoteColumnName($column); } $values = $conditions[2]; @@ -483,7 +557,7 @@ class QueryBuilder extends \yii\base\Component } foreach ($values as $i => $value) { if (is_string($value)) { - $values[$i] = $this->_connection->quoteValue($value); + $values[$i] = $this->connection->quoteValue($value); } else { $values[$i] = (string)$value; @@ -506,7 +580,7 @@ class QueryBuilder extends \yii\base\Component } $expressions = array(); foreach ($values as $value) { - $expressions[] = $column . ' ' . $operator . ' ' . $this->_connection->quoteValue($value); + $expressions[] = $column . ' ' . $operator . ' ' . $this->connection->quoteValue($value); } return implode($andor, $expressions); } diff --git a/framework/db/dao/Schema.php b/framework/db/dao/Schema.php index 0ac1020..179a1b9 100644 --- a/framework/db/dao/Schema.php +++ b/framework/db/dao/Schema.php @@ -18,12 +18,6 @@ namespace yii\db\dao; */ abstract class Schema extends \yii\base\Component { - /** - * @var array the abstract column types mapped to physical column types. - * @since 1.1.6 - */ - public $columnTypes = array(); - private $_tableNames = array(); private $_tables = array(); private $_connection; @@ -49,55 +43,58 @@ abstract class Schema extends \yii\base\Component } /** - * @return CDbConnection database connection. The connection is active. + * @return Connection database connection. The connection is active. */ - public function getDbConnection() + public function getConnection() { return $this->_connection; } /** * Obtains the metadata for the named table. - * @param string $name table name + * @param string $name table name. The table name may contain schema name if any. Do not quote the table name. * @return CDbTableSchema table metadata. Null if the named table does not exist. */ - public function getTable($name) + public function getTableSchema($name) { - if (isset($this->_tables[$name])) + if (isset($this->_tables[$name])) { return $this->_tables[$name]; - else - { - if ($this->_connection->tablePrefix !== null && strpos($name, '{{') !== false) - $realName = preg_replace('/\{\{(.*?)\}\}/', $this->_connection->tablePrefix . '$1', $name); - else - $realName = $name; + } - // temporarily disable query caching - if ($this->_connection->queryCachingDuration > 0) - { - $qcDuration = $this->_connection->queryCachingDuration; - $this->_connection->queryCachingDuration = 0; - } + if (strpos($name, '{{') !== false) { + $realName = preg_replace('/\{\{(.*?)\}\}/', $this->_connection->tablePrefix . '$1', $name); + } + else { + $realName = $name; + } + + $db = $this->_connection; - if (!isset($this->_cacheExclude[$name]) && ($duration = $this->_connection->schemaCachingDuration) > 0 && $this->_connection->schemaCacheID !== false && ($cache = Yii::app()->getComponent($this->_connection->schemaCacheID)) !== null) - { - $key = 'yii:dbschema' . $this->_connection->connectionString . ':' . $this->_connection->username . ':' . $name; - if (($table = $cache->get($key)) === false) - { - $table = $this->loadTable($realName); - if ($table !== null) - $cache->set($key, $table, $duration); + // temporarily disable query caching + if ($db->queryCachingDuration >= 0) { + $qcDuration = $db->queryCachingDuration; + $db->queryCachingDuration = -1; + } + + if (!in_array($name, $db->schemaCachingExclude) && $db->schemaCachingDuration >= 0 && ($cache = \Yii::app()->getComponent($db->schemaCacheID)) !== null) { + $key = __CLASS__ . ":{$db->dsn}/{$db->username}/{$name}"; + if (($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->loadTable($realName); - - if (isset($qcDuration)) // re-enable query caching - $this->_connection->queryCachingDuration = $qcDuration; + $this->_tables[$name] = $table; + } + else { + $this->_tables[$name] = $table = $this->loadTableSchema($realName); + } - return $table; + if (isset($qcDuration)) { // re-enable query caching + $db->queryCachingDuration = $qcDuration; } + + return $table; } /** @@ -106,7 +103,6 @@ abstract class Schema extends \yii\base\Component * @return array the metadata for all tables in the database. * Each array element is an instance of {@link CDbTableSchema} (or its child class). * The array keys are table names. - * @since 1.0.2 */ public function getTables($schema = '') { @@ -124,7 +120,6 @@ abstract class Schema extends \yii\base\Component * @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. - * @since 1.0.2 */ public function getTableNames($schema = '') { @@ -151,15 +146,11 @@ abstract class Schema extends \yii\base\Component */ public function refresh() { - if (($duration = $this->_connection->schemaCachingDuration) > 0 && $this->_connection->schemaCacheID !== false && ($cache = Yii::app()->getComponent($this->_connection->schemaCacheID)) !== null) - { - foreach (array_keys($this->_tables) as $name) - { - if (!isset($this->_cacheExclude[$name])) - { - $key = 'yii:dbschema' . $this->_connection->connectionString . ':' . $this->_connection->username . ':' . $name; - $cache->delete($key); - } + $db = $this->_connection; + if ($db->schemaCachingDuration >= 0 && ($cache = \Yii::app()->getComponent($db->schemaCacheID)) !== null) { + foreach ($this->_tables as $name => $table) { + $key = __CLASS__ . ":{$db->dsn}/{$db->username}/{$name}"; + $cache->delete($key); } } $this->_tables = array(); @@ -190,7 +181,6 @@ abstract class Schema extends \yii\base\Component * A simple table name does not schema prefix. * @param string $name table name * @return string the properly quoted table name - * @since 1.1.6 */ public function quoteSimpleTableName($name) { @@ -255,36 +245,13 @@ abstract class Schema extends \yii\base\Component } /** - * Resets the sequence value of a table's primary key. - * The sequence will be reset such that the primary key of the next new row inserted - * will have the specified value or 1. - * @param CDbTableSchema $table the table schema whose primary key sequence will be reset - * @param mixed $value the value for the primary key of the next new row inserted. If this is not set, - * the next new row's primary key will have a value 1. - * @since 1.1 - */ - public function resetSequence($table, $value = null) - { - } - - /** - * Enables or disables integrity check. - * @param boolean $check whether to turn on or off the integrity check. - * @param string $schema the schema of the tables. Defaults to empty string, meaning the current or default schema. - * @since 1.1 - */ - public function checkIntegrity($check = true, $schema = '') - { - } - - /** * Creates a command builder for the database. * This method may be overridden by child classes to create a DBMS-specific command builder. * @return CDbCommandBuilder command builder instance */ - protected function createCommandBuilder() + protected function createQueryBuilder() { - return new CDbCommandBuilder($this); + return new QueryBuilder($this); } /** @@ -294,52 +261,10 @@ abstract class Schema extends \yii\base\Component * @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. - * @since 1.0.2 */ protected function findTableNames($schema = '') { - throw new CDbException(Yii::t('yii', '{class} does not support fetching all table names.', - array('{class}' => get_class($this)))); + throw new Exception(get_class($this) . 'does not support fetching all table names.'); } - /** - * Converts an abstract column type into a physical column type. - * The conversion is done using the type map specified in {@link columnTypes}. - * These abstract column types are supported (using MySQL as example to explain the corresponding - * physical types): - * - * - * If the abstract type contains two or more parts separated by spaces (e.g. "string NOT NULL"), then only - * the first part will be converted, and the rest of the parts will be appended to the conversion result. - * For example, 'string NOT NULL' is converted to 'varchar(255) NOT NULL'. - * @param string $type abstract column type - * @return string physical column type. - * @since 1.1.6 - */ - public function getColumnType($type) - { - if (isset($this->columnTypes[$type])) - return $this->columnTypes[$type]; - elseif (($pos = strpos($type, ' ')) !== false) - { - $t = substr($type, 0, $pos); - return (isset($this->columnTypes[$t]) ? $this->columnTypes[$t] : $t) . substr($type, $pos); - } - else - return $type; - } - }