From 1295fbe2bcf02706b5ac8f3f30044daa24f28086 Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Tue, 27 Dec 2011 23:17:39 -0500 Subject: [PATCH] ... --- framework/db/dao/QueryBuilder.php | 6 +- framework/db/dao/mysql/Driver.php | 3 +- framework/db/dao/mysql/QueryBuilder.php | 6 +- framework/db/dao/sqlite/Driver.php | 210 +++++++++++++++++++++++++++++++ framework/db/dao/sqlite/QueryBuilder.php | 167 ++++++++++++++++++++++++ 5 files changed, 384 insertions(+), 8 deletions(-) create mode 100644 framework/db/dao/sqlite/Driver.php create mode 100644 framework/db/dao/sqlite/QueryBuilder.php diff --git a/framework/db/dao/QueryBuilder.php b/framework/db/dao/QueryBuilder.php index abdd2ce..c08ef53 100644 --- a/framework/db/dao/QueryBuilder.php +++ b/framework/db/dao/QueryBuilder.php @@ -309,14 +309,14 @@ class QueryBuilder extends \yii\base\Object /** * Builds a SQL statement for renaming a column. * @param string $table the table whose column is to be renamed. The name will be properly quoted by the method. - * @param string $name the old name of the column. The name will be properly quoted by the method. + * @param string $oldName the old name of the column. The name will be properly quoted by the method. * @param string $newName the new name of the column. The name will be properly quoted by the method. * @return string the SQL statement for renaming a DB column. */ - public function renameColumn($table, $name, $newName) + public function renameColumn($table, $oldName, $newName) { return "ALTER TABLE " . $this->driver->quoteTableName($table) - . " RENAME COLUMN " . $this->driver->quoteSimpleColumnName($name) + . " RENAME COLUMN " . $this->driver->quoteSimpleColumnName($oldName) . " TO " . $this->driver->quoteSimpleColumnName($newName); } diff --git a/framework/db/dao/mysql/Driver.php b/framework/db/dao/mysql/Driver.php index 3a77236..1c2e51b 100644 --- a/framework/db/dao/mysql/Driver.php +++ b/framework/db/dao/mysql/Driver.php @@ -75,8 +75,7 @@ class Driver extends \yii\db\dao\Driver } /** - * Creates a query builder for the database. - * This method may be overridden by child classes to create a DBMS-specific query builder. + * Creates a query builder for the MySQL database. * @return QueryBuilder query builder instance */ public function createQueryBuilder() diff --git a/framework/db/dao/mysql/QueryBuilder.php b/framework/db/dao/mysql/QueryBuilder.php index d1a46bf..8ab0455 100644 --- a/framework/db/dao/mysql/QueryBuilder.php +++ b/framework/db/dao/mysql/QueryBuilder.php @@ -13,7 +13,7 @@ namespace yii\db\dao\mysql; use yii\db\Exception; /** - * QueryBuilder builds a SQL statement based on the specification given as a [[Query]] object. + * QueryBuilder is the query builder for MySQL databases. * * @author Qiang Xue * @since 2.0 @@ -23,7 +23,7 @@ class QueryBuilder extends \yii\db\dao\QueryBuilder /** * @var array mapping from abstract column types (keys) to physical column types (values). */ - public $typeMap = array( + public $typeMap = array( Driver::TYPE_PK => 'int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY', Driver::TYPE_STRING => 'varchar(255)', Driver::TYPE_TEXT => 'text', @@ -39,7 +39,7 @@ class QueryBuilder extends \yii\db\dao\QueryBuilder Driver::TYPE_BINARY => 'blob', Driver::TYPE_BOOLEAN => 'tinyint(1)', Driver::TYPE_MONEY => 'decimal(19,4)', - ); + ); /** * Builds a SQL statement for renaming a column. diff --git a/framework/db/dao/sqlite/Driver.php b/framework/db/dao/sqlite/Driver.php new file mode 100644 index 0000000..7a0de23 --- /dev/null +++ b/framework/db/dao/sqlite/Driver.php @@ -0,0 +1,210 @@ + + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2012 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +namespace yii\db\dao\sqlite; + +use yii\db\dao\TableSchema; +use yii\db\dao\ColumnSchema; + +/** + * Driver is the class for retrieving metadata from a SQLite (2/3) database. + * + * @author Qiang Xue + * @since 2.0 + */ +class Driver extends \yii\db\dao\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\dao\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\dao\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) { + if ($table->primaryKey === null) { + $table->primaryKey = $column->name; + } elseif (is_string($table->primaryKey)) { + $table->primaryKey = array($table->primaryKey, $column->name); + } else { + $table->primaryKey[] = $column->name; + } + } + } + if (is_string($table->primaryKey) && !strncasecmp($table->columns[$table->primaryKey]->dbType, 'int', 3)) { + $table->sequenceName = ''; + $table->columns[$table->primaryKey]->autoIncrement = true; + } + + 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) + { + $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\dao\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\dao\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/dao/sqlite/QueryBuilder.php b/framework/db/dao/sqlite/QueryBuilder.php new file mode 100644 index 0000000..284e228 --- /dev/null +++ b/framework/db/dao/sqlite/QueryBuilder.php @@ -0,0 +1,167 @@ + + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2012 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +namespace yii\db\dao\sqlite; + +use yii\db\Exception; + +/** + * QueryBuilder is the query builder for MySQL databases. + * + * @author Qiang Xue + * @since 2.0 + */ +class QueryBuilder extends \yii\db\dao\QueryBuilder +{ + /** + * @var array mapping from abstract column types (keys) to physical column types (values). + */ + public $typeMap = array( + Driver::TYPE_PK => 'integer PRIMARY KEY AUTOINCREMENT NOT NULL', + Driver::TYPE_STRING => 'varchar(255)', + Driver::TYPE_TEXT => 'text', + Driver::TYPE_SMALLINT => 'smallint', + Driver::TYPE_INTEGER => 'integer', + Driver::TYPE_BIGINT => 'bigint', + 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)', + ); + + /** + * 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 string $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) + { + if ($table->sequenceName !== null) { + if ($value === null) { + $value = $this->connection->createCommand("SELECT MAX(`{$table->primaryKey}`) FROM {$table->quotedName}")->queryScalar(); + } + else { + $value = (int)$value - 1; + } + try { + // it's possible sqlite_sequence does not exist + $this->connection->createCommand("UPDATE sqlite_sequence SET seq='$value' WHERE name='{$table->name}'")->execute(); + } catch (Exception $e) { + } + } + } + + /** + * 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 = '') + { + // SQLite doesn't enforce integrity + } + + /** + * Builds a SQL statement for truncating a DB table. + * @param string $table the table to be truncated. The name will be properly quoted by the method. + * @return string the SQL statement for truncating a DB table. + */ + public function truncateTable($table) + { + return "DELETE FROM " . $this->driver->quoteTableName($table); + } + + /** + * Builds a SQL statement for dropping an index. + * @param string $name the name of the index to be dropped. The name will be properly quoted by the method. + * @param string $table the table whose index is to be dropped. The name will be properly quoted by the method. + * @return string the SQL statement for dropping an index. + */ + public function dropIndex($name, $table) + { + return 'DROP INDEX ' . $this->driver->quoteTableName($name); + } + + /** + * Builds a SQL statement for dropping a DB column. + * @param string $table the table whose column is to be dropped. The name will be properly quoted by the method. + * @param string $column the name of the column to be dropped. The name will be properly quoted by the method. + * @return string the SQL statement for dropping a DB column. + */ + public function dropColumn($table, $column) + { + throw new Exception('Dropping DB column is not supported by SQLite.'); + } + + /** + * Builds a SQL statement for renaming a column. + * @param string $table the table whose column is to be renamed. The name will be properly quoted by the method. + * @param string $oldName the old name of the column. The name will be properly quoted by the method. + * @param string $newName the new name of the column. The name will be properly quoted by the method. + * @return string the SQL statement for renaming a DB column. + */ + public function renameColumn($table, $oldName, $newName) + { + throw new Exception('Renaming a DB column is not supported by SQLite.'); + } + + /** + * Builds a SQL statement for adding a foreign key constraint to an existing table. + * The method will properly quote the table and column names. + * @param string $name the name of the foreign key constraint. + * @param string $table the table that the foreign key constraint will be added to. + * @param string|array $columns the name of the column to that the constraint will be added on. + * If there are multiple columns, separate them with commas or use an array to represent them. + * @param string $refTable the table that the foreign key references to. + * @param string|array $refColumns the name of the column that the foreign key references to. + * If there are multiple columns, separate them with commas or use an array to represent them. + * @param string $delete the ON DELETE option. Most DBMS support these options: RESTRICT, CASCADE, NO ACTION, SET DEFAULT, SET NULL + * @param string $update the ON UPDATE option. Most DBMS support these options: RESTRICT, CASCADE, NO ACTION, SET DEFAULT, SET NULL + * @return string the SQL statement for adding a foreign key constraint to an existing table. + */ + public function addForeignKey($name, $table, $columns, $refTable, $refColumns, $delete = null, $update = null) + { + throw new Exception('Adding a foreign key constraint to an existing table is not supported by SQLite.'); + } + + /** + * Builds a SQL statement for dropping a foreign key constraint. + * @param string $name the name of the foreign key constraint to be dropped. The name will be properly quoted by the method. + * @param string $table the table whose foreign is to be dropped. The name will be properly quoted by the method. + * @return string the SQL statement for dropping a foreign key constraint. + */ + public function dropForeignKey($name, $table) + { + throw new Exception('Dropping a foreign key constraint is not supported by SQLite.'); + } + + /** + * Builds a SQL statement for changing the definition of a column. + * @param string $table the table whose column is to be changed. The table name will be properly quoted by the method. + * @param string $column the name of the column to be changed. The name will be properly quoted by the method. + * @param string $type the new column type. The [[getColumnType()]] method will be invoked to convert abstract + * column type (if any) into the physical one. Anything that is not recognized as abstract type will be kept + * in the generated SQL. For example, 'string' will be turned into 'varchar(255)', while 'string not null' + * will become 'varchar(255) not null'. + * @return string the SQL statement for changing the definition of a column. + */ + public function alterColumn($table, $column, $type) + { + throw new Exception('Altering a DB column is not supported by SQLite.'); + } +}