Commit ebecc098 by Qiang Xue

DB refactoring.

parent 9958a580
<?php
/**
* NotSupportedException class file.
*
* @link http://www.yiiframework.com/
* @copyright Copyright &copy; 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\base;
/**
* NotSupportedException represents an exception caused by accessing features that are not supported.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
class NotSupportedException extends \Exception
{
}
......@@ -22,15 +22,11 @@ class ColumnSchema extends \yii\base\Component
*/
public $name;
/**
* @var string the quoted name of this column.
*/
public $quotedName;
/**
* @var boolean whether this column can be null.
*/
public $allowNull;
/**
* @var string logical type of this column. Possible logic types include:
* @var string abstract type of this column. Possible abstract types include:
* string, text, boolean, smallint, integer, bigint, float, decimal, datetime,
* timestamp, time, date, binary, and money.
*/
......@@ -77,31 +73,11 @@ class ColumnSchema extends \yii\base\Component
* when [[type]] is `smallint`, `integer` or `bigint`.
*/
public $unsigned;
/**
* Extracts the PHP type from DB type.
* @var string comment of this column. Not all DBMS support this.
*/
public function resolvePhpType()
{
static $typeMap = array( // logical type => php type
'smallint' => 'integer',
'integer' => 'integer',
'bigint' => 'integer',
'boolean' => 'boolean',
'float' => 'double',
);
if (isset($typeMap[$this->type])) {
if ($this->type === 'bigint') {
$this->phpType = PHP_INT_SIZE == 8 && !$this->unsigned ? 'integer' : 'string';
} elseif ($this->type === 'integer') {
$this->phpType = PHP_INT_SIZE == 4 && $this->unsigned ? 'string' : 'integer';
} else {
$this->phpType = $typeMap[$this->type];
}
} else {
$this->phpType = 'string';
}
}
public $comment;
/**
* Converts the input value according to [[phpType]].
......
......@@ -9,7 +9,8 @@
namespace yii\db;
use yii\db\Exception;
use yii\base\NotSupportedException;
use yii\base\BadCallException;
/**
* Schema is the base class for concrete DBMS-specific schema classes.
......@@ -113,15 +114,20 @@ abstract class Schema extends \yii\base\Object
/**
* 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.
* @param string $schema the schema of the tables. Defaults to empty string, meaning the current or default schema name.
* @param boolean $refresh whether to fetch the latest available table schemas. If this is false,
* cached data may be returned if available.
* @return array the metadata for all tables in the database.
* Each array element is an instance of [[TableSchema]] (or its child class).
* Each array element is an instance of [[TableSchema]] or its child class.
*/
public function getTableSchemas($schema = '')
public function getTableSchemas($schema = '', $refresh = false)
{
$tables = array();
foreach ($this->getTableNames($schema) as $name) {
if (($table = $this->getTableSchema($name)) !== null) {
foreach ($this->getTableNames($schema, $refresh) as $name) {
if ($schema !== '') {
$name = $schema . '.' . $name;
}
if (($table = $this->getTableSchema($name, $refresh)) !== null) {
$tables[] = $table;
}
}
......@@ -130,7 +136,7 @@ abstract class Schema extends \yii\base\Object
/**
* 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.
* @param string $schema the schema of the tables. Defaults to empty string, meaning the current or default schema name.
* 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.
......@@ -168,6 +174,7 @@ abstract class Schema extends \yii\base\Object
$cache->delete($this->getCacheKey($name));
}
}
$this->_tableNames = array();
$this->_tables = array();
}
......@@ -186,18 +193,19 @@ abstract class Schema extends \yii\base\Object
* 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.
* @return array all table names in the database. The names have NO the schema name prefix.
* @throws NotSupportedException if this method is called
*/
protected function findTableNames($schema = '')
{
throw new Exception(get_class($this) . ' does not support fetching all table names.');
throw new NotSupportedException(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
* @throws BadCallException if the DB connection is not active
* @see http://www.php.net/manual/en/function.PDO-lastInsertId.php
*/
public function getLastInsertID($sequenceName = '')
......@@ -205,11 +213,10 @@ abstract class Schema extends \yii\base\Object
if ($this->connection->isActive) {
return $this->connection->pdo->lastInsertId($sequenceName);
} else {
throw new Exception('DB Connection is not active.');
throw new BadCallException('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.
......@@ -319,4 +326,31 @@ abstract class Schema extends \yii\base\Object
return $name;
}
}
/**
* Extracts the PHP type from abstract DB type.
* @param string $type abstract DB type
* @return string PHP type name
*/
protected function getColumnPhpType($type)
{
static $typeMap = array( // abstract type => php type
'smallint' => 'integer',
'integer' => 'integer',
'bigint' => 'integer',
'boolean' => 'boolean',
'float' => 'double',
);
if (isset($typeMap[$type])) {
if ($type === 'bigint') {
return PHP_INT_SIZE == 8 && !$this->unsigned ? 'integer' : 'string';
} elseif ($type === 'integer') {
return PHP_INT_SIZE == 4 && $this->unsigned ? 'string' : 'integer';
} else {
return $typeMap[$this->type];
}
} else {
return 'string';
}
}
}
......@@ -9,7 +9,7 @@
namespace yii\db;
use yii\db\Exception;
use yii\base\BadParamException;
/**
* TableSchema represents the metadata of a database table.
......@@ -36,10 +36,6 @@ class TableSchema extends \yii\base\Object
*/
public $name;
/**
* @var string quoted name of this table. This will include [[schemaName]] if it is not empty.
*/
public $quotedName;
/**
* @var string[] primary keys of this table.
*/
public $primaryKey = array();
......@@ -63,6 +59,10 @@ class TableSchema extends \yii\base\Object
* @var ColumnSchema[] column metadata of this table. Each array element is a [[ColumnSchema]] object, indexed by column names.
*/
public $columns = array();
/**
* @var string comment of this table
*/
public $comment;
/**
* Gets the named column metadata.
......@@ -87,7 +87,7 @@ class TableSchema extends \yii\base\Object
/**
* Manually specifies the primary key for this table.
* @param string|array $keys the primary key (can be composite)
* @throws \yii\db\Exception if the specified key cannot be found in the table.
* @throws BadParamException if the specified key cannot be found in the table.
*/
public function fixPrimaryKey($keys)
{
......@@ -102,7 +102,7 @@ class TableSchema extends \yii\base\Object
if (isset($this->columns[$key])) {
$this->columns[$key]->isPrimaryKey = true;
} else {
throw new Exception("Primary key '$key' cannot be found in table '{$this->name}'.");
throw new BadParamException("Primary key '$key' cannot be found in table '{$this->name}'.");
}
}
}
......
......@@ -85,7 +85,7 @@ class Schema extends \yii\db\Schema
/**
* 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.
* @return TableSchema driver dependent table metadata. Null if the table does not exist.
*/
protected function loadTableSchema($name)
{
......@@ -95,6 +95,8 @@ class Schema extends \yii\db\Schema
if ($this->findColumns($table)) {
$this->findConstraints($table);
return $table;
} else {
return null;
}
}
......@@ -109,64 +111,34 @@ class Schema extends \yii\db\Schema
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
* Loads the column information into a [[ColumnSchema]] object.
* @param array $info column information
* @return ColumnSchema the column schema object
*/
protected function createColumn($column)
protected function loadColumn($info)
{
$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;
}
$column = new ColumnSchema;
/**
* 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);
}
}
$column->name = $info['Field'];
$column->allowNull = $info['Null'] === 'YES';
$column->isPrimaryKey = strpos($info['Key'], 'PRI') !== false;
$column->autoIncrement = stripos($info['Extra'], 'auto_increment') !== false;
/**
* 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->dbType = $info['Type'];
$column->unsigned = strpos($column->dbType, 'unsigned') !== false;
$column->type = self::TYPE_STRING;
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]);
......@@ -192,23 +164,31 @@ class Schema extends \yii\db\Schema
}
}
}
$column->phpType = $this->getColumnPhpType($column->type);
if ($column->type !== 'timestamp' || $info['Default'] !== 'CURRENT_TIMESTAMP') {
$column->defaultValue = $column->typecast($info['Default']);
}
return $column;
}
/**
* Collects the metadata of table columns.
* @param \yii\db\TableSchema $table the table metadata
* @param TableSchema $table the table metadata
* @return boolean whether the table exists in the database
*/
protected function findColumns($table)
{
$sql = 'SHOW COLUMNS FROM ' . $table->quotedName;
$sql = 'SHOW COLUMNS FROM ' . $this->quoteSimpleTableName($table->name);
try {
$columns = $this->connection->createCommand($sql)->queryAll();
} catch (\Exception $e) {
return false;
}
foreach ($columns as $column) {
$column = $this->createColumn($column);
foreach ($columns as $info) {
$column = $this->loadColumn($info);
$table->columns[$column->name] = $column;
if ($column->isPrimaryKey) {
$table->primaryKey[] = $column->name;
......@@ -222,11 +202,11 @@ class Schema extends \yii\db\Schema
/**
* Collects the foreign key column details for the given table.
* @param \yii\db\TableSchema $table the table metadata
* @param TableSchema $table the table metadata
*/
protected function findConstraints($table)
{
$row = $this->connection->createCommand('SHOW CREATE TABLE ' . $table->quotedName)->queryRow();
$row = $this->connection->createCommand('SHOW CREATE TABLE ' . $this->quoteSimpleTableName($table->name))->queryRow();
if (isset($row['Create Table'])) {
$sql = $row['Create Table'];
} else {
......@@ -250,20 +230,17 @@ class Schema extends \yii\db\Schema
/**
* 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.
* @return array all table names in the database. The names have NO the schema name prefix.
*/
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;
$sql = 'SHOW TABLES';
if ($schema !== '') {
$sql .= ' FROM ' . $this->quoteSimpleTableName($schema);
}
return $names;
return $this->connection->createCommand($sql)->queryColumn();
}
}
......@@ -82,7 +82,6 @@ class Schema extends \yii\db\Schema
{
$table = new TableSchema;
$table->name = $name;
$table->quotedName = $this->quoteTableName($name);
if ($this->findColumns($table)) {
$this->findConstraints($table);
......@@ -97,7 +96,7 @@ class Schema extends \yii\db\Schema
*/
protected function findColumns($table)
{
$sql = "PRAGMA table_info({$table->quotedName})";
$sql = "PRAGMA table_info(" . $this->quoteSimpleTableName($table->name) . ')';
$columns = $this->connection->createCommand($sql)->queryAll();
if (empty($columns)) {
return false;
......@@ -124,7 +123,7 @@ class Schema extends \yii\db\Schema
*/
protected function findConstraints($table)
{
$sql = "PRAGMA foreign_key_list({$table->quotedName})";
$sql = "PRAGMA foreign_key_list(" . $this->quoteSimpleTableName($table->name) . ')';
$keys = $this->connection->createCommand($sql)->queryAll();
foreach ($keys as $key) {
$table->foreignKeys[] = array($key['table'], $key['from'] => $key['to']);
......@@ -140,13 +139,12 @@ class Schema extends \yii\db\Schema
{
$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();
$c->phpType = $this->getColumnPhpType($this->type);
$this->resolveColumnDefault($c, $column['dflt_value']);
......
......@@ -18,11 +18,6 @@ namespace yii\validators;
class UniqueValidator extends Validator
{
/**
* @var boolean whether the comparison is case sensitive. Defaults to true.
* Note, by setting it to false, you are assuming the attribute type is string.
*/
public $caseSensitive = true;
/**
* @var boolean whether the attribute value can be null or empty. Defaults to true,
* meaning that if the attribute is empty, it is considered valid.
*/
......@@ -76,8 +71,7 @@ class UniqueValidator extends Validator
}
$query = $className::find();
$query->where($this->caseSensitive ? "{$column->quotedName}=:value" : "LOWER({$column->quotedName})=LOWER(:value)");
$query->params(array(':value' => $value));
$query->where(array($column->name => $value));
if ($object->getIsNewRecord()) {
// if current $object isn't in the database yet then it's OK just
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment