From 879994252dcc28a5ab5688defd5ec2772f0be2f2 Mon Sep 17 00:00:00 2001 From: Alexander Makarov Date: Thu, 25 Aug 2011 02:01:52 +0400 Subject: [PATCH 01/10] minor typo fixes --- framework/base/Model.php | 4 ++-- framework/base/ModelBehavior.php | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/framework/base/Model.php b/framework/base/Model.php index 918a9df..a75b694 100644 --- a/framework/base/Model.php +++ b/framework/base/Model.php @@ -419,7 +419,7 @@ class Model extends Component implements Initable, \IteratorAggregate, \ArrayAcc public function getAttributeLabel($attribute) { $labels = $this->attributeLabels(); - return isset($labels[$attribute]) ? $labels[$attribute] : $this->generateAttributeLabel($attribute) + return isset($labels[$attribute]) ? $labels[$attribute] : $this->generateAttributeLabel($attribute); } /** @@ -590,7 +590,7 @@ class Model extends Component implements Initable, \IteratorAggregate, \ArrayAcc public function onUnsafeAttribute($name, $value) { if (YII_DEBUG) { - \Yii::warning(sprintf('Failed to set unsafe attribute "%s" in "%s".', $name, get_class($this)); + \Yii::warning(sprintf('Failed to set unsafe attribute "%s" in "%s".', $name, get_class($this))); } } diff --git a/framework/base/ModelBehavior.php b/framework/base/ModelBehavior.php index f8fa23b..c97c820 100644 --- a/framework/base/ModelBehavior.php +++ b/framework/base/ModelBehavior.php @@ -42,7 +42,7 @@ class ModelBehavior extends Behavior /** * Responds to [[Model::onAfterConstruct]] event. - * Overrides this method if you want to handle the corresponding event of the [[owner]]. + * Override this method if you want to handle the corresponding event of the [[owner]]. * @param Event $event event parameter */ public function afterConstruct($event) @@ -51,7 +51,7 @@ class ModelBehavior extends Behavior /** * Responds to [[Model::onBeforeValidate]] event. - * Overrides this method if you want to handle the corresponding event of the [[owner]]. + * Override this method if you want to handle the corresponding event of the [[owner]]. * You may set the [[ValidationEvent::isValid|isValid]] property of the event parameter * to be false to cancel the validation process. * @param ValidationEvent $event event parameter @@ -62,7 +62,7 @@ class ModelBehavior extends Behavior /** * Responds to [[Model::onAfterValidate]] event. - * Overrides this method if you want to handle the corresponding event of the [[owner]]. + * Override this method if you want to handle the corresponding event of the [[owner]]. * @param Event $event event parameter */ public function afterValidate($event) From 01adc984f89ff8b3dd595c236136a638036204d2 Mon Sep 17 00:00:00 2001 From: Alexander Makarov Date: Thu, 25 Aug 2011 02:04:29 +0400 Subject: [PATCH 02/10] Git ignore rules for PhpStom, Netbeans, Windows thumbnail cache --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ebfa4c2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/.idea/ +/nbproject/ +Thumbs.db \ No newline at end of file From 3e18a7a6a0bb46b43b8d1e934d8dd666ef153fdf Mon Sep 17 00:00:00 2001 From: Alexander Makarov Date: Thu, 25 Aug 2011 20:08:03 +0400 Subject: [PATCH 03/10] added zend studio to gitignore --- .gitignore | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index ebfa4c2..89fc2a8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,13 @@ -/.idea/ -/nbproject/ +# phpstorm project files +.idea + +# netbeans project files +nbproject + +# zend studio for eclipse project files +.buildpath +.project +.settings + +# windows thumbnail cache Thumbs.db \ No newline at end of file From fbf243d6fa228c967f915d66928a5907615e2201 Mon Sep 17 00:00:00 2001 From: Alexander Makarov Date: Tue, 30 Aug 2011 01:12:29 +0400 Subject: [PATCH 04/10] more todo --- todo.txt | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/todo.txt b/todo.txt index 80946ca..b50cb50 100644 --- a/todo.txt +++ b/todo.txt @@ -25,14 +25,21 @@ - db * DAO * schema + * write a guide on creating own schema definitions * AR - * document-based - * key-value-based + * saving related records + * collection support for results + * document-based (should allow storage-specific methods additionally to generic ones) + * mongodb + * key-value-based (should allow storage-specific methods additionally to generic ones) + * redis + * memcachedb - i18n * consider using PHP built-in support and data * message translations, choice format * formatting: number and date * parsing?? + * make dates/date patterns uniform application-wide including JUI, formats etc. - helpers * array * image @@ -42,3 +49,10 @@ * get/setFlash() should be moved to session component * support optional parameter in URL patterns - gii + * move generation API out of gii, provide yiic commands to use it. Use same templates for gii/yiic. + * i18n variant of templates +- markup and HTML helpers + * use HTML5 instead of XHTML +- assets + * ability to manage scripts order (store these in a vector?) + * http://ryanbigg.com/guides/asset_pipeline.html, use content hash instead of mtime + directory hash. From 92fe7e47a1106da0aa635c185417cfe2ca7f31df Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Wed, 31 Aug 2011 17:41:56 -0400 Subject: [PATCH 05/10] w --- framework/db/dao/ColumnSchema.php | 93 ++++------- framework/db/dao/QueryBuilder.php | 10 +- framework/db/dao/Schema.php | 107 ++++--------- framework/db/dao/TableSchema.php | 12 +- framework/db/dao/mysql/ColumnSchema.php | 111 +++++++++---- framework/db/dao/mysql/QueryBuilder.php | 93 +++++++++++ framework/db/dao/mysql/Schema.php | 276 ++++++++------------------------ framework/db/dao/mysql/TableSchema.php | 24 --- 8 files changed, 321 insertions(+), 405 deletions(-) create mode 100644 framework/db/dao/mysql/QueryBuilder.php delete mode 100644 framework/db/dao/mysql/TableSchema.php diff --git a/framework/db/dao/ColumnSchema.php b/framework/db/dao/ColumnSchema.php index 3b4e413..dd7256a 100644 --- a/framework/db/dao/ColumnSchema.php +++ b/framework/db/dao/ColumnSchema.php @@ -8,13 +8,15 @@ * @license http://www.yiiframework.com/license/ */ +namespace yii\db\dao; + /** * ColumnSchema class describes the column meta data of a database table. * * @author Qiang Xue * @since 2.0 */ -class ColumnSchema extends CComponent +class ColumnSchema extends \yii\base\Component { /** * @var string name of this column (without quotes). @@ -30,7 +32,7 @@ class ColumnSchema extends CComponent public $allowNull; /** * @var string logical type of this column. Possible logic types include: - * string, text, boolean, integer, float, decimal, datetime, timestamp, time, date, binary + * string, text, boolean, smallint, integer, bigint, float, decimal, datetime, timestamp, time, date, binary */ public $type; /** @@ -47,6 +49,10 @@ class ColumnSchema extends CComponent */ public $defaultValue; /** + * @var array enumerable values + */ + public $enumValues; + /** * @var integer size of the column. */ public $size; @@ -63,69 +69,37 @@ class ColumnSchema extends CComponent */ public $isPrimaryKey; /** - * @var boolean whether this column is a foreign key - */ - public $isForeignKey; - /** * @var boolean whether this column is auto-incremental */ public $autoIncrement = false; - - /** - * Initializes the column with its DB type and default value. - * This sets up the column's PHP type, size, precision, scale as well as default value. - * @param string $dbType the column's DB type - * @param mixed $defaultValue the default value + * @var boolean whether this column is unsigned. This is only meaningful + * when [[type]] is `integer` or `bigint`. */ - public function init($dbType, $defaultValue) - { - $this->dbType = $dbType; - $this->extractType($dbType); - $this->extractLimit($dbType); - if ($defaultValue !== null) - $this->extractDefault($defaultValue); - } + public $unsigned; /** * Extracts the PHP type from DB type. - * @param string $dbType DB type - */ - protected function extractType($dbType) - { - if (stripos($dbType, 'int') !== false && stripos($dbType, 'unsigned int') === false) - $this->type = 'integer'; - elseif (stripos($dbType, 'bool') !== false) - $this->type = 'boolean'; - elseif (preg_match('/(real|floa|doub)/i', $dbType)) - $this->type = 'double'; - else - $this->type = 'string'; - } - - /** - * Extracts size, precision and scale information from column's DB type. - * @param string $dbType the column's DB type */ - protected function extractLimit($dbType) + protected function getPhpType() { - if (strpos($dbType, '(') && preg_match('/\((.*)\)/', $dbType, $matches)) - { - $values = explode(',', $matches[1]); - $this->size = $this->precision = (int)$values[0]; - if (isset($values[1])) - $this->scale = (int)$values[1]; + 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') { + return PHP_INT_SIZE == 8 && !$this->unsigned ? 'integer' : 'string'; + } + elseif ($this->type === 'integer') { + return PHP_INT_SIZE == 4 && $this->unsigned ? 'string' : 'integer'; + } + return $typeMap[$this->type]; } - } - - /** - * Extracts the default value for the column. - * The value is typecasted to correct PHP type. - * @param mixed $defaultValue the default value obtained from metadata - */ - protected function extractDefault($defaultValue) - { - $this->defaultValue = $this->typecast($defaultValue); + return 'string'; } /** @@ -135,17 +109,14 @@ class ColumnSchema extends CComponent */ public function typecast($value) { - if (gettype($value) === $this->type || $value === null || $value instanceof CDbExpression) + if ($value === null || gettype($value) === $this->phpType || $value instanceof Expression) { return $value; - if ($value === '') - return $this->type === 'string' ? '' : null; - switch ($this->type) - { + } + switch ($this->phpType) { case 'string': return (string)$value; case 'integer': return (integer)$value; case 'boolean': return (boolean)$value; - case 'double': - default: return $value; } + return $value; } } diff --git a/framework/db/dao/QueryBuilder.php b/framework/db/dao/QueryBuilder.php index 6d97876..c32a429 100644 --- a/framework/db/dao/QueryBuilder.php +++ b/framework/db/dao/QueryBuilder.php @@ -151,7 +151,7 @@ class QueryBuilder extends \yii\base\Component public function dropColumn($table, $column) { return "ALTER TABLE " . $this->quoteTableName($table) - . " DROP COLUMN " . $this->quoteColumnName($column); + . " DROP COLUMN " . $this->quoteSimpleColumnName($column); } /** @@ -165,8 +165,8 @@ class QueryBuilder extends \yii\base\Component public function renameColumn($table, $name, $newName) { return "ALTER TABLE " . $this->quoteTableName($table) - . " RENAME COLUMN " . $this->quoteColumnName($name) - . " TO " . $this->quoteColumnName($newName); + . " RENAME COLUMN " . $this->quoteSimpleColumnName($name) + . " TO " . $this->quoteSimpleColumnName($newName); } /** @@ -181,8 +181,8 @@ class QueryBuilder extends \yii\base\Component public function alterColumn($table, $column, $type) { return 'ALTER TABLE ' . $this->quoteTableName($table) . ' CHANGE ' - . $this->quoteColumnName($column) . ' ' - . $this->quoteColumnName($column) . ' ' + . $this->quoteSimpleColumnName($column) . ' ' + . $this->quoteSimpleColumnName($column) . ' ' . $this->getColumnType($type); } diff --git a/framework/db/dao/Schema.php b/framework/db/dao/Schema.php index 179a1b9..a831a31 100644 --- a/framework/db/dao/Schema.php +++ b/framework/db/dao/Schema.php @@ -18,18 +18,18 @@ namespace yii\db\dao; */ abstract class Schema extends \yii\base\Component { + public $connection; + private $_tableNames = array(); private $_tables = array(); - private $_connection; private $_builder; - private $_cacheExclude = array(); /** * Loads the metadata for the specified table. * @param string $name table name - * @return CDbTableSchema driver dependent table metadata, null if the table does not exist. + * @return TableSchema driver dependent table metadata, null if the table does not exist. */ - abstract protected function loadTable($name); + abstract protected function loadTableSchema($name); /** * Constructor. @@ -37,17 +37,7 @@ abstract class Schema extends \yii\base\Component */ public function __construct($connection) { - $this->_connection = $connection; - foreach ($connection->schemaCachingExclude as $name) - $this->_cacheExclude[$name] = true; - } - - /** - * @return Connection database connection. The connection is active. - */ - public function getConnection() - { - return $this->_connection; + $this->connection = $connection; } /** @@ -62,13 +52,13 @@ abstract class Schema extends \yii\base\Component } if (strpos($name, '{{') !== false) { - $realName = preg_replace('/\{\{(.*?)\}\}/', $this->_connection->tablePrefix . '$1', $name); + $realName = preg_replace('/\{\{(.*?)\}\}/', $this->connection->tablePrefix . '$1', $name); } else { $realName = $name; } - - $db = $this->_connection; + + $db = $this->connection; // temporarily disable query caching if ($db->queryCachingDuration >= 0) { @@ -77,7 +67,7 @@ abstract class Schema extends \yii\base\Component } if (!in_array($name, $db->schemaCachingExclude) && $db->schemaCachingDuration >= 0 && ($cache = \Yii::app()->getComponent($db->schemaCacheID)) !== null) { - $key = __CLASS__ . ":{$db->dsn}/{$db->username}/{$name}"; + $key = __CLASS__ . "/{$db->dsn}/{$db->username}/{$name}"; if (($table = $cache->get($key)) === false) { $table = $this->loadTableSchema($realName); if ($table !== null) { @@ -102,15 +92,14 @@ 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. * @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. */ - public function getTables($schema = '') + public function getTableSchemas($schema = '') { $tables = array(); - foreach ($this->getTableNames($schema) as $name) - { - if (($table = $this->getTable($name)) !== null) - $tables[$name] = $table; + foreach ($this->getTableNames($schema) as $name) { + if (($table = $this->getTableSchema($name)) !== null) { + $tables[] = $table; + } } return $tables; } @@ -123,20 +112,21 @@ abstract class Schema extends \yii\base\Component */ public function getTableNames($schema = '') { - if (!isset($this->_tableNames[$schema])) + if (!isset($this->_tableNames[$schema])) { $this->_tableNames[$schema] = $this->findTableNames($schema); + } return $this->_tableNames[$schema]; } /** - * @return CDbCommandBuilder the SQL command builder for this connection. + * @return QueryBuilder the query builder for this connection. */ - public function getCommandBuilder() + public function getQueryBuilder() { - if ($this->_builder !== null) - return $this->_builder; - else - return $this->_builder = $this->createCommandBuilder(); + if ($this->_builder === null) { + $this->_builder = $this->createQueryBuilder(); + } + return $this->_builder; } /** @@ -146,7 +136,7 @@ abstract class Schema extends \yii\base\Component */ public function refresh() { - $db = $this->_connection; + $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}"; @@ -155,7 +145,6 @@ abstract class Schema extends \yii\base\Component } $this->_tables = array(); $this->_tableNames = array(); - $this->_builder = null; } /** @@ -167,11 +156,13 @@ abstract class Schema extends \yii\base\Component */ public function quoteTableName($name) { - if (strpos($name, '.') === false) + if (strpos($name, '.') === false) { return $this->quoteSimpleTableName($name); + } $parts = explode('.', $name); - foreach ($parts as $i => $part) + foreach ($parts as $i => $part) { $parts[$i] = $this->quoteSimpleTableName($part); + } return implode('.', $parts); } @@ -184,7 +175,7 @@ abstract class Schema extends \yii\base\Component */ public function quoteSimpleTableName($name) { - return "'" . $name . "'"; + return strpos($name, "'") !== false ? $name : "'" . $name . "'"; } /** @@ -196,14 +187,13 @@ abstract class Schema extends \yii\base\Component */ public function quoteColumnName($name) { - if (($pos = strrpos($name, '.')) !== false) - { + if (($pos = strrpos($name, '.')) !== false) { $prefix = $this->quoteTableName(substr($name, 0, $pos)) . '.'; $name = substr($name, $pos + 1); } else $prefix = ''; - return $prefix . ($name === '*' ? $name : $this->quoteSimpleColumnName($name)); + return $prefix . $this->quoteSimpleColumnName($name); } /** @@ -211,45 +201,18 @@ abstract class Schema extends \yii\base\Component * A simple column name does not contain prefix. * @param string $name column name * @return string the properly quoted column name - * @since 1.1.6 */ public function quoteSimpleColumnName($name) { - return '"' . $name . '"'; - } - - /** - * Compares two table names. - * The table names can be either quoted or unquoted. This method - * will consider both cases. - * @param string $name1 table name 1 - * @param string $name2 table name 2 - * @return boolean whether the two table names refer to the same table. - */ - public function compareTableNames($name1, $name2) - { - $name1 = str_replace(array('"', '`', "'"), '', $name1); - $name2 = str_replace(array('"', '`', "'"), '', $name2); - if (($pos = strrpos($name1, '.')) !== false) - $name1 = substr($name1, $pos + 1); - if (($pos = strrpos($name2, '.')) !== false) - $name2 = substr($name2, $pos + 1); - if ($this->_connection->tablePrefix !== null) - { - if (strpos($name1, '{') !== false) - $name1 = $this->_connection->tablePrefix . str_replace(array('{', '}'), '', $name1); - if (strpos($name2, '{') !== false) - $name2 = $this->_connection->tablePrefix . str_replace(array('{', '}'), '', $name2); - } - return $name1 === $name2; + return strpos($name, '"') !== false || $name === '*' ? $name : '"' . $name . '"'; } /** - * 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 + * 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 */ - protected function createQueryBuilder() + public function createQueryBuilder() { return new QueryBuilder($this); } diff --git a/framework/db/dao/TableSchema.php b/framework/db/dao/TableSchema.php index be97032..a7bdae7 100644 --- a/framework/db/dao/TableSchema.php +++ b/framework/db/dao/TableSchema.php @@ -39,7 +39,7 @@ class TableSchema extends \yii\base\Component */ public $name; /** - * @var string quoted name of this table. This may include [[schemaName]] if it is not empty. + * @var string quoted name of this table. This will include [[schemaName]] if it is not empty. */ public $quotedName; /** @@ -51,7 +51,15 @@ class TableSchema extends \yii\base\Component */ public $sequenceName; /** - * @var array foreign keys of this table. The array is indexed by column name. Each value is an array of foreign table name and foreign column name. + * @var array foreign keys of this table. Each array element is of the following structure: + * + * ~~~ + * array( + * 'ForeignTableName', + * 'fk1' => 'pk1', // pk1 is in foreign table + * 'fk2' => 'pk2', // if composite foreign key + * ) + * ~~~ */ public $foreignKeys = array(); /** diff --git a/framework/db/dao/mysql/ColumnSchema.php b/framework/db/dao/mysql/ColumnSchema.php index 671dd12..f9f2b61 100644 --- a/framework/db/dao/mysql/ColumnSchema.php +++ b/framework/db/dao/mysql/ColumnSchema.php @@ -8,6 +8,8 @@ * @license http://www.yiiframework.com/license/ */ +namespace yii\db\dao\mysql; + /** * ColumnSchema class describes the column meta data of a MySQL table. * @@ -20,18 +22,76 @@ class ColumnSchema extends \yii\db\dao\ColumnSchema * Extracts the PHP type from DB type. * @param string $dbType DB type */ - protected function extractType($dbType) + public function initTypes($dbType) { - if (strncmp($dbType, 'enum', 4) === 0) - $this->type = 'string'; - elseif (strpos($dbType, 'float') !== false || strpos($dbType, 'double') !== false) - $this->type = 'double'; - elseif (strpos($dbType, 'bool') !== false) - $this->type = 'boolean'; - elseif (strpos($dbType, 'int') === 0 && strpos($dbType, 'unsigned') === false || preg_match('/(bit|tinyint|smallint|mediumint)/', $dbType)) - $this->type = 'integer'; - else - $this->type = 'string'; + static $typeMap = array( // dbType => type + 'tinyint' => 'smallint', + 'bit' => 'smallint', + 'smallint' => 'smallint', + 'mediumint' => 'integer', + 'int' => 'integer', + 'integer' => 'integer', + 'bigint' => 'bigint', + 'float' => 'float', + 'double' => 'float', + 'real' => 'float', + 'decimal' => 'decimal', + 'numeric' => 'decimal', + 'tinytext' => 'text', + 'mediumtext' => 'text', + 'longtext' => 'text', + 'text' => 'text', + 'varchar' => 'string', + 'string' => 'string', + 'char' => 'string', + 'datetime' => 'datetime', + 'year' => 'date', + 'date' => 'date', + 'time' => 'time', + 'timestamp' => 'timestamp', + 'enum' => 'enum', + ); + + $this->dbType = $dbType; + $this->type = 'string'; + $this->unsigned = strpos($this->dbType, 'unsigned') !== false; + + if (preg_match('/^(\w+)(?:\(([^\)]+)\))?/', $this->dbType, $matches)) { + $type = $matches[1]; + if (isset($typeMap[$type])) { + $this->type = $typeMap[$type]; + } + + if (!empty($matches[2])) { + if ($this->type === 'enum') { + $values = explode(',', $matches[2]); + foreach ($values as $i => $value) { + $values[$i] = trim($value, "'"); + } + $this->enumValues = $values; + } + else { + $values = explode(',', $matches[2]); + $this->size = $this->precision = (int)$values[0]; + if (isset($values[1])) { + $this->scale = (int)$values[1]; + } + if ($this->size === 1 && ($type === 'tinyint' || $type === 'bit')) { + $this->type = 'boolean'; + } + elseif ($type === 'bit') { + if ($this->size > 32) { + $this->type = 'bigint'; + } + elseif ($this->size === 32) { + $this->type = 'integer'; + } + } + } + } + } + + $this->phpType = $this->getPhpType(); } /** @@ -39,32 +99,13 @@ class ColumnSchema extends \yii\db\dao\ColumnSchema * The value is typecasted to correct PHP type. * @param mixed $defaultValue the default value obtained from metadata */ - protected function extractDefault($defaultValue) + public function initDefaultValue($defaultValue) { - if ($this->dbType === 'timestamp' && $defaultValue === 'CURRENT_TIMESTAMP') + if ($this->type === 'timestamp' && $defaultValue === 'CURRENT_TIMESTAMP') { $this->defaultValue = null; - else - parent::extractDefault($defaultValue); - } - - /** - * Extracts size, precision and scale information from column's DB type. - * @param string $dbType the column's DB type - */ - protected function extractLimit($dbType) - { - if (strncmp($dbType, 'enum', 4) === 0 && preg_match('/\((.*)\)/', $dbType, $matches)) - { - $values = explode(',', $matches[1]); - $size = 0; - foreach ($values as $value) - { - if (($n = strlen($value)) > $size) - $size = $n; - } - $this->size = $this->precision = $size-2; } - else - parent::extractLimit($dbType); + else { + $this->defaultValue = $this->typecast($defaultValue); + } } } \ No newline at end of file diff --git a/framework/db/dao/mysql/QueryBuilder.php b/framework/db/dao/mysql/QueryBuilder.php new file mode 100644 index 0000000..9ec5842 --- /dev/null +++ b/framework/db/dao/mysql/QueryBuilder.php @@ -0,0 +1,93 @@ + + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2012 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +namespace yii\db\dao\mysql; + +/** + * QueryBuilder builds a SQL statement based on the specification given as a [[Query]] object. + * + * @author Qiang Xue + * @since 2.0 + */ +class QueryBuilder extends \yii\db\dao\QueryBuilder +{ + /** + * @var array the abstract column types mapped to physical column types. + */ + public $columnTypes = array( + 'pk' => 'int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY', + 'string' => 'varchar(255)', + 'text' => 'text', + 'smallint' => 'smallint', + 'integer' => 'int(11)', + 'bigint'=> 'bigint', + 'boolean' => 'tinyint(1)', + 'float' => 'float', + 'decimal' => 'decimal', + 'money' => 'decimal(19,4)', + 'datetime' => 'datetime', + 'timestamp' => 'timestamp', + 'time' => 'time', + 'date' => 'date', + 'binary' => 'blob', + ); + + /** + * 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 $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. + * @since 1.1.6 + */ + public function renameColumn($table, $name, $newName) + { + $db = $this->getDbConnection(); + $row = $db->createCommand('SHOW CREATE TABLE ' . $db->quoteTableName($table))->queryRow(); + if ($row === false) + throw new CDbException(Yii::t('yii', 'Unable to find "{column}" in table "{table}".', array('{column}' => $name, '{table}' => $table))); + if (isset($row['Create Table'])) + $sql = $row['Create Table']; + else + { + $row = array_values($row); + $sql = $row[1]; + } + if (preg_match_all('/^\s*`(.*?)`\s+(.*?),?$/m', $sql, $matches)) + { + foreach ($matches[1] as $i => $c) + { + if ($c === $name) + { + return "ALTER TABLE " . $db->quoteTableName($table) + . " CHANGE " . $db->quoteColumnName($name) + . ' ' . $db->quoteColumnName($newName) . ' ' . $matches[2][$i]; + } + } + } + + // try to give back a SQL anyway + return "ALTER TABLE " . $db->quoteTableName($table) + . " CHANGE " . $db->quoteColumnName($name) . ' ' . $newName; + } + + /** + * 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. + * @since 1.1.6 + */ + public function dropForeignKey($name, $table) + { + return 'ALTER TABLE ' . $this->quoteTableName($table) + . ' DROP FOREIGN KEY ' . $this->quoteColumnName($name); + } +} diff --git a/framework/db/dao/mysql/Schema.php b/framework/db/dao/mysql/Schema.php index 751ca5c..a11ad40 100644 --- a/framework/db/dao/mysql/Schema.php +++ b/framework/db/dao/mysql/Schema.php @@ -1,53 +1,32 @@ * @link http://www.yiiframework.com/ - * @copyright Copyright © 2008-2011 Yii Software LLC + * @copyright Copyright © 2008-2012 Yii Software LLC * @license http://www.yiiframework.com/license/ */ +namespace yii\db\dao\mysql; + /** - * CMysqlSchema is the class for retrieving metadata information from a MySQL database (version 4.1.x and 5.x). + * Schema is the class for retrieving metadata information from a MySQL database (version 4.1.x and 5.x). * * @author Qiang Xue - * @version $Id: CMysqlSchema.php 3304 2011-06-23 14:53:50Z qiang.xue $ - * @package system.db.schema.mysql - * @since 1.0 + * @since 2.0 */ -class CMysqlSchema extends CDbSchema +class Schema extends \yii\db\dao\Schema { /** - * @var array the abstract column types mapped to physical column types. - * @since 1.1.6 - */ - 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)', - ); - - /** * Quotes a table name for use in a query. * A simple table name does not schema prefix. * @param string $name table name * @return string the properly quoted table name - * @since 1.1.6 */ public function quoteSimpleTableName($name) { - return '`' . $name . '`'; + return strpos($name, "`") !== false ? $name : "`" . $name . "`"; } /** @@ -55,75 +34,26 @@ class CMysqlSchema extends CDbSchema * A simple column name does not contain prefix. * @param string $name column name * @return string the properly quoted column name - * @since 1.1.6 */ public function quoteSimpleColumnName($name) { - return '`' . $name . '`'; - } - - /** - * Compares two table names. - * The table names can be either quoted or unquoted. This method - * will consider both cases. - * @param string $name1 table name 1 - * @param string $name2 table name 2 - * @return boolean whether the two table names refer to the same table. - */ - public function compareTableNames($name1, $name2) - { - return parent::compareTableNames(strtolower($name1), strtolower($name2)); - } - - /** - * 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) - { - if ($table->sequenceName !== null) - { - if ($value === null) - $value = $this->getDbConnection()->createCommand("SELECT MAX(` {$table->primaryKey}`) FROM {$table->rawName}")->queryScalar() + 1; - else - $value = (int)$value; - $this->getDbConnection()->createCommand("ALTER TABLE {$table->rawName} AUTO_INCREMENT=$value")->execute(); - } - } - - /** - * 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 = '') - { - $this->getDbConnection()->createCommand('SET FOREIGN_KEY_CHECKS=' . ($check ? 1 : 0))->execute(); + return strpos($name, '`') !== false || $name === '*' ? $name : '`' . $name . '`'; } /** * Loads the metadata for the specified table. * @param string $name table name - * @return CMysqlTableSchema 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 loadTable($name) + protected function loadTableSchema($name) { - $table = new CMysqlTableSchema; + $table = new TableSchema; $this->resolveTableNames($table, $name); - if ($this->findColumns($table)) - { + if ($this->findColumns($table)) { $this->findConstraints($table); return $table; } - else - return null; } /** @@ -134,52 +64,15 @@ class CMysqlSchema extends CDbSchema protected function resolveTableNames($table, $name) { $parts = explode('.', str_replace('`', '', $name)); - if (isset($parts[1])) - { + if (isset($parts[1])) { $table->schemaName = $parts[0]; $table->name = $parts[1]; - $table->rawName = $this->quoteTableName($table->schemaName) . '.' . $this->quoteTableName($table->name); + $table->quotedName = $this->quoteSimpleTableName($table->schemaName) . '.' . $this->quoteSimpleTableName($table->name); } - else - { + else { $table->name = $parts[0]; - $table->rawName = $this->quoteTableName($table->name); - } - } - - /** - * Collects the table column metadata. - * @param CMysqlTableSchema $table the table metadata - * @return boolean whether the table exists in the database - */ - protected function findColumns($table) - { - $sql = 'SHOW COLUMNS FROM ' . $table->rawName; - try - { - $columns = $this->getDbConnection()->createCommand($sql)->queryAll(); - } - catch(Exception $e) - { - return false; - } - foreach ($columns as $column) - { - $c = $this->createColumn($column); - $table->columns[$c->name] = $c; - if ($c->isPrimaryKey) - { - if ($table->primaryKey === null) - $table->primaryKey = $c->name; - elseif (is_string($table->primaryKey)) - $table->primaryKey = array($table->primaryKey, $c->name); - else - $table->primaryKey[] = $c->name; - if ($c->autoIncrement) - $table->sequenceName = ''; - } + $table->quotedName = $this->quoteSimpleTableName($table->name); } - return true; } /** @@ -189,27 +82,51 @@ class CMysqlSchema extends CDbSchema */ protected function createColumn($column) { - $c = new CMysqlColumnSchema; + $c = new ColumnSchema; + $c->name = $column['Field']; - $c->rawName = $this->quoteColumnName($c->name); + $c->quotedName = $this->quoteSimpleColumnName($c->name); $c->allowNull = $column['Null'] === 'YES'; $c->isPrimaryKey = strpos($column['Key'], 'PRI') !== false; - $c->isForeignKey = false; - $c->init($column['Type'], $column['Default']); - $c->autoIncrement = strpos(strtolower($column['Extra']), 'auto_increment') !== false; + $c->autoIncrement = stripos($column['Extra'], 'auto_increment') !== false; + $c->initTypes($column['Type']); + $c->initDefaultValue($column['Default']); return $c; } /** - * @return float server version. + * Collects the table column metadata. + * @param CMysqlTableSchema $table the table metadata + * @return boolean whether the table exists in the database */ - protected function getServerVersion() + protected function findColumns($table) { - $version = $this->getDbConnection()->getAttribute(PDO::ATTR_SERVER_VERSION); - $digits = array(); - preg_match('/(\d+)\.(\d+)\.(\d+)/', $version, $digits); - return floatval($digits[1] . '.' . $digits[2] . $digits[3]); + $sql = 'SHOW COLUMNS FROM ' . $table->quotedName; + try { + $columns = $this->connection->createCommand($sql)->queryAll(); + } + catch(\Exception $e) { + return false; + } + foreach ($columns as $column) { + $table->columns[$c->name] = $c = $this->createColumn($column); + if ($c->isPrimaryKey) { + if ($table->primaryKey === null) { + $table->primaryKey = $c->name; + } + elseif (is_string($table->primaryKey)) { + $table->primaryKey = array($table->primaryKey, $c->name); + } + else { + $table->primaryKey[] = $c->name; + } + if ($c->autoIncrement) { + $table->sequenceName = ''; + } + } + } + return true; } /** @@ -218,23 +135,21 @@ class CMysqlSchema extends CDbSchema */ protected function findConstraints($table) { - $row = $this->getDbConnection()->createCommand('SHOW CREATE TABLE ' . $table->rawName)->queryRow(); + $row = $this->connection->createCommand('SHOW CREATE TABLE ' . $table->quotedName)->queryRow(); $matches = array(); $regexp = '/FOREIGN KEY\s+\(([^\)]+)\)\s+REFERENCES\s+([^\(^\s]+)\s*\(([^\)]+)\)/mi'; - foreach ($row as $sql) - { - if (preg_match_all($regexp, $sql, $matches, PREG_SET_ORDER)) + foreach ($row as $sql) { + if (preg_match_all($regexp, $sql, $matches, PREG_SET_ORDER)) { + foreach ($matches as $match) { + $fks = array_map('trim', explode(',', str_replace('`', '', $match[1]))); + $pks = array_map('trim', explode(',', str_replace('`', '', $match[3]))); + $constraint = array(str_replace('`', '', $match[2])), + foreach ($fks as $k => $name) { + $constraint[$name] = $pks[$k]; + } + $table->foreignKeys[] = $constraint; + } break; - } - foreach ($matches as $match) - { - $keys = array_map('trim', explode(',', str_replace('`', '', $match[1]))); - $fks = array_map('trim', explode(',', str_replace('`', '', $match[3]))); - foreach ($keys as $k => $name) - { - $table->foreignKeys[$name] = array(str_replace('`', '', $match[2]), $fks[$k]); - if (isset($table->columns[$name])) - $table->columns[$name]->isForeignKey = true; } } } @@ -244,67 +159,16 @@ class CMysqlSchema extends CDbSchema * @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 = '') { - if ($schema === '') - return $this->getDbConnection()->createCommand('SHOW TABLES')->queryColumn(); - $names = $this->getDbConnection()->createCommand('SHOW TABLES FROM ' . $this->quoteTableName($schema))->queryColumn(); - foreach ($names as &$name) - $name = $schema . '.' . $name; - return $names; - } - - /** - * 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 $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. - * @since 1.1.6 - */ - public function renameColumn($table, $name, $newName) - { - $db = $this->getDbConnection(); - $row = $db->createCommand('SHOW CREATE TABLE ' . $db->quoteTableName($table))->queryRow(); - if ($row === false) - throw new CDbException(Yii::t('yii', 'Unable to find "{column}" in table "{table}".', array('{column}' => $name, '{table}' => $table))); - if (isset($row['Create Table'])) - $sql = $row['Create Table']; - else - { - $row = array_values($row); - $sql = $row[1]; + if ($schema === '') { + return $this->connection->createCommand('SHOW TABLES')->queryColumn(); } - if (preg_match_all('/^\s*`(.*?)`\s+(.*?),?$/m', $sql, $matches)) - { - foreach ($matches[1] as $i => $c) - { - if ($c === $name) - { - return "ALTER TABLE " . $db->quoteTableName($table) - . " CHANGE " . $db->quoteColumnName($name) - . ' ' . $db->quoteColumnName($newName) . ' ' . $matches[2][$i]; - } - } + $names = $this->connection->createCommand('SHOW TABLES FROM ' . $this->quoteSimpleTableName($schema))->queryColumn(); + foreach ($names as &$name) { + $name = $schema . '.' . $name; } - - // try to give back a SQL anyway - return "ALTER TABLE " . $db->quoteTableName($table) - . " CHANGE " . $db->quoteColumnName($name) . ' ' . $newName; - } - - /** - * 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. - * @since 1.1.6 - */ - public function dropForeignKey($name, $table) - { - return 'ALTER TABLE ' . $this->quoteTableName($table) - . ' DROP FOREIGN KEY ' . $this->quoteColumnName($name); + return $names; } } diff --git a/framework/db/dao/mysql/TableSchema.php b/framework/db/dao/mysql/TableSchema.php deleted file mode 100644 index 42dcb51..0000000 --- a/framework/db/dao/mysql/TableSchema.php +++ /dev/null @@ -1,24 +0,0 @@ - - * @link http://www.yiiframework.com/ - * @copyright Copyright © 2008-2012 Yii Software LLC - * @license http://www.yiiframework.com/license/ - */ - -/** - * TableSchema represents the metadata for a MySQL table. - * - * @author Qiang Xue - * @since 2.0 - */ -class TableSchema extends CDbTableSchema -{ - /** - * @var string name of the schema (database) that this table belongs to. - * Defaults to null, meaning no schema (or the current database). - */ - public $schemaName; -} From 4f134c97a2e739b0782d921061d5aa1ed3d5171b Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Thu, 1 Sep 2011 22:04:46 -0400 Subject: [PATCH 06/10] w --- framework/db/dao/Command.php | 824 ++++++++------------------------------ framework/db/dao/Connection.php | 5 + framework/db/dao/DataReader.php | 2 + framework/db/dao/Query.php | 167 +------- framework/db/dao/QueryBuilder.php | 155 +++++-- 5 files changed, 306 insertions(+), 847 deletions(-) diff --git a/framework/db/dao/Command.php b/framework/db/dao/Command.php index ecb10fb..3fa762f 100644 --- a/framework/db/dao/Command.php +++ b/framework/db/dao/Command.php @@ -50,12 +50,19 @@ class Command extends \yii\base\Component */ public $params = array(); - private $_connection; - private $_text; - private $_statement; + public $connection; + public $query; + public $pdoStatement; + + private $_sql; private $_paramLog = array(); - private $_query; - private $_fetchMode = array(PDO::FETCH_ASSOC); + /** + * Set the default fetch mode for this statement + * @param mixed $mode fetch mode + * @return Command + * @see http://www.php.net/manual/en/function.PDOStatement-setFetchMode.php + */ + public $fetchMode = \PDO::FETCH_ASSOC; /** * Constructor. @@ -80,38 +87,19 @@ class Command extends \yii\base\Component */ public function __construct($connection, $query = null) { - $this->_connection = $connection; - if (is_array($query)) - { - foreach ($query as $name => $value) - $this->$name = $value; + $this->connection = $connection; + if (is_object($query)) { + $this->query = $query; + } + else { + $this->query = new Query; + if (is_array($this->query)) { + $this->query->fromArray($this->query); + } + else { + $this->_sql = $query; + } } - else - $this->setText($query); - } - - /** - * Set the statement to null when serializing. - * @return array - */ - public function __sleep() - { - $this->_statement = null; - return array_keys(get_object_vars($this)); - } - - /** - * Set the default fetch mode for this statement - * @param mixed $mode fetch mode - * @return Command - * @see http://www.php.net/manual/en/function.PDOStatement-setFetchMode.php - * @since 1.1.7 - */ - public function setFetchMode($mode) - { - $params = func_get_args(); - $this->_fetchMode = $params; - return $this; } /** @@ -124,9 +112,9 @@ class Command extends \yii\base\Component */ public function reset() { - $this->_text = null; - $this->_query = null; - $this->_statement = null; + $this->_sql = null; + $this->query = new Query; + $this->pdoStatement = null; $this->_paramLog = array(); $this->params = array(); return $this; @@ -135,11 +123,12 @@ class Command extends \yii\base\Component /** * @return string the SQL statement to be executed */ - public function getText() + public function getSql() { - if ($this->_text == '' && !empty($this->_query)) - $this->setText($this->buildQuery($this->_query)); - return $this->_text; + if ($this->_sql == '' && is_object($this->query)) { + $this->_sql = $this->query->getSql($this->connection); + } + return $this->_sql; } /** @@ -148,34 +137,19 @@ class Command extends \yii\base\Component * @param string $value the SQL statement to be executed * @return Command this command instance */ - public function setText($value) + public function setSql($value) { - if ($this->_connection->tablePrefix !== null && $value != '') - $this->_text = preg_replace('/{{(.*?)}}/', $this->_connection->tablePrefix . '\1', $value); - else - $this->_text = $value; + if ($this->connection->tablePrefix !== null && strpos($value, '{') !== false) { + $this->_sql = preg_replace('/{{(.*?)}}/', $this->connection->tablePrefix . '\1', $value); + } + else { + $this->_sql = $value; + } $this->cancel(); return $this; } /** - * @return CDbConnection the connection associated with this command - */ - public function getConnection() - { - return $this->_connection; - } - - /** - * @return PDOStatement the underlying PDOStatement for this command - * It could be null if the statement is not prepared yet. - */ - public function getPdoStatement() - { - return $this->_statement; - } - - /** * Prepares the SQL statement to be executed. * For complex SQL statement that is to be executed multiple times, * this may improve performance. @@ -184,18 +158,15 @@ class Command extends \yii\base\Component */ public function prepare() { - if ($this->_statement == null) - { - try - { - $this->_statement = $this->getConnection()->getPdoInstance()->prepare($this->getText()); + if ($this->pdoStatement == null) { + try { + $this->pdoStatement = $this->connection->pdo->prepare($this->getSql()); $this->_paramLog = array(); } - catch(Exception $e) - { - Yii::log('Error in preparing SQL: ' . $this->getText(), CLogger::LEVEL_ERROR, 'system.db.Command'); - $errorInfo = $e instanceof PDOException ? $e->errorInfo : null; - throw new CDbException(Yii::t('yii', 'Command failed to prepare the SQL statement: {error}', + catch(Exception $e) { + Yii::log('Error in preparing SQL: ' . $this->getSql(), CLogger::LEVEL_ERROR, 'system.db.Command'); + $errorInfo = $e instanceof \PDOException ? $e->errorInfo : null; + throw new Exception('Unable to prepare the SQL statement: {error}', array('{error}' => $e->getMessage())), (int)$e->getCode(), $errorInfo); } } @@ -206,7 +177,7 @@ class Command extends \yii\base\Component */ public function cancel() { - $this->_statement = null; + $this->pdoStatement = null; } /** @@ -226,13 +197,13 @@ class Command extends \yii\base\Component { $this->prepare(); if ($dataType === null) - $this->_statement->bindParam($name, $value, $this->_connection->getPdoType(gettype($value))); + $this->pdoStatement->bindParam($name, $value, $this->connection->getPdoType(gettype($value))); elseif ($length === null) - $this->_statement->bindParam($name, $value, $dataType); + $this->pdoStatement->bindParam($name, $value, $dataType); elseif ($driverOptions === null) - $this->_statement->bindParam($name, $value, $dataType, $length); + $this->pdoStatement->bindParam($name, $value, $dataType, $length); else - $this->_statement->bindParam($name, $value, $dataType, $length, $driverOptions); + $this->pdoStatement->bindParam($name, $value, $dataType, $length, $driverOptions); $this->_paramLog[$name] =& $value; return $this; } @@ -252,9 +223,9 @@ class Command extends \yii\base\Component { $this->prepare(); if ($dataType === null) - $this->_statement->bindValue($name, $value, $this->_connection->getPdoType(gettype($value))); + $this->pdoStatement->bindValue($name, $value, $this->connection->getPdoType(gettype($value))); else - $this->_statement->bindValue($name, $value, $dataType); + $this->pdoStatement->bindValue($name, $value, $dataType); $this->_paramLog[$name] = $value; return $this; } @@ -274,7 +245,7 @@ class Command extends \yii\base\Component $this->prepare(); foreach ($values as $name => $value) { - $this->_statement->bindValue($name, $value, $this->_connection->getPdoType(gettype($value))); + $this->pdoStatement->bindValue($name, $value, $this->connection->getPdoType(gettype($value))); $this->_paramLog[$name] = $value; } return $this; @@ -295,7 +266,7 @@ class Command extends \yii\base\Component */ public function execute($params = array()) { - if ($this->_connection->enableParamLogging && ($pars = array_merge($this->_paramLog, $params)) !== array()) + if ($this->connection->enableParamLogging && ($pars = array_merge($this->_paramLog, $params)) !== array()) { $p = array(); foreach ($pars as $name => $value) @@ -304,34 +275,34 @@ class Command extends \yii\base\Component } else $par = ''; - Yii::trace('Executing SQL: ' . $this->getText() . $par, 'system.db.Command'); + Yii::trace('Executing SQL: ' . $this->getSql() . $par, 'system.db.Command'); try { - if ($this->_connection->enableProfiling) - Yii::beginProfile('system.db.Command.execute(' . $this->getText() . ')', 'system.db.Command.execute'); + if ($this->connection->enableProfiling) + Yii::beginProfile('system.db.Command.execute(' . $this->getSql() . ')', 'system.db.Command.execute'); $this->prepare(); if ($params === array()) - $this->_statement->execute(); + $this->pdoStatement->execute(); else - $this->_statement->execute($params); - $n = $this->_statement->rowCount(); + $this->pdoStatement->execute($params); + $n = $this->pdoStatement->rowCount(); - if ($this->_connection->enableProfiling) - Yii::endProfile('system.db.Command.execute(' . $this->getText() . ')', 'system.db.Command.execute'); + if ($this->connection->enableProfiling) + Yii::endProfile('system.db.Command.execute(' . $this->getSql() . ')', 'system.db.Command.execute'); return $n; } catch(Exception $e) { - if ($this->_connection->enableProfiling) - Yii::endProfile('system.db.Command.execute(' . $this->getText() . ')', 'system.db.Command.execute'); + if ($this->connection->enableProfiling) + Yii::endProfile('system.db.Command.execute(' . $this->getSql() . ')', 'system.db.Command.execute'); $errorInfo = $e instanceof PDOException ? $e->errorInfo : null; $message = $e->getMessage(); Yii::log(Yii::t('yii', 'Command::execute() failed: {error}. The SQL statement executed was: {sql}.', - array('{error}' => $message, '{sql}' => $this->getText() . $par)), CLogger::LEVEL_ERROR, 'system.db.Command'); + array('{error}' => $message, '{sql}' => $this->getSql() . $par)), CLogger::LEVEL_ERROR, 'system.db.Command'); if (YII_DEBUG) - $message .= '. The SQL statement executed was: ' . $this->getText() . $par; + $message .= '. The SQL statement executed was: ' . $this->getSql() . $par; throw new CDbException(Yii::t('yii', 'Command failed to execute the SQL statement: {error}', array('{error}' => $message)), (int)$e->getCode(), $errorInfo); } @@ -351,7 +322,7 @@ class Command extends \yii\base\Component */ public function query($params = array()) { - return $this->queryInternal('', 0, $params); + return $this->queryInternal('', $params); } /** @@ -368,9 +339,9 @@ class Command extends \yii\base\Component * An empty array is returned if the query results in nothing. * @throws CException execution failed */ - public function queryAll($fetchAssociative = true, $params = array()) + public function queryAll($params = array(), $fetchMode = null) { - return $this->queryInternal('fetchAll', $fetchAssociative ? $this->_fetchMode : PDO::FETCH_NUM, $params); + return $this->queryInternal('fetchAll', $params, $fetchMode); } /** @@ -387,9 +358,9 @@ class Command extends \yii\base\Component * @return mixed the first row (in terms of an array) of the query result, false if no result. * @throws CException execution failed */ - public function queryRow($fetchAssociative = true, $params = array()) + public function queryRow($params = array(), $fetchMode = null) { - return $this->queryInternal('fetch', $fetchAssociative ? $this->_fetchMode : PDO::FETCH_NUM, $params); + return $this->queryInternal('fetch', $params, $fetchMode); } /** @@ -407,11 +378,13 @@ class Command extends \yii\base\Component */ public function queryScalar($params = array()) { - $result = $this->queryInternal('fetchColumn', 0, $params); - if (is_resource($result) && get_resource_type($result) === 'stream') + $result = $this->queryInternal('fetchColumn', $params); + if (is_resource($result) && get_resource_type($result) === 'stream') { return stream_get_contents($result); - else + } + else { return $result; + } } /** @@ -429,7 +402,7 @@ class Command extends \yii\base\Component */ public function queryColumn($params = array()) { - return $this->queryInternal('fetchAll', PDO::FETCH_COLUMN, $params); + return $this->queryInternal('fetchAll', $params, \PDO::FETCH_COLUMN); } /** @@ -443,11 +416,11 @@ class Command extends \yii\base\Component * This parameter has been available since version 1.0.10. * @return mixed the method execution result */ - private function queryInternal($method, $mode, $params = array()) + private function queryInternal($method, $params, $fetchMode = null) { $params = array_merge($this->params, $params); - if ($this->_connection->enableParamLogging && ($pars = array_merge($this->_paramLog, $params)) !== array()) + if ($this->connection->enableParamLogging && ($pars = array_merge($this->_paramLog, $params)) !== array()) { $p = array(); foreach ($pars as $name => $value) @@ -457,16 +430,16 @@ class Command extends \yii\base\Component else $par = ''; - Yii::trace('Querying SQL: ' . $this->getText() . $par, 'system.db.Command'); + Yii::trace('Querying SQL: ' . $this->getSql() . $par, 'system.db.Command'); - if ($this->_connection->queryCachingCount > 0 && $method !== '' - && $this->_connection->queryCachingDuration > 0 - && $this->_connection->queryCacheID !== false - && ($cache = Yii::app()->getComponent($this->_connection->queryCacheID)) !== null) + if ($this->connection->queryCachingCount > 0 && $method !== '' + && $this->connection->queryCachingDuration > 0 + && $this->connection->queryCacheID !== false + && ($cache = Yii::app()->getComponent($this->connection->queryCacheID)) !== null) { - $this->_connection->queryCachingCount--; - $cacheKey = 'yii:dbquery' . $this->_connection->connectionString . ':' . $this->_connection->username; - $cacheKey .= ':' . $this->getText() . ':' . serialize(array_merge($this->_paramLog, $params)); + $this->connection->queryCachingCount--; + $cacheKey = 'yii:dbquery' . $this->connection->connectionString . ':' . $this->connection->username; + $cacheKey .= ':' . $this->getSql() . ':' . serialize(array_merge($this->_paramLog, $params)); if (($result = $cache->get($cacheKey)) !== false) { Yii::trace('Query result found in cache', 'system.db.Command'); @@ -476,93 +449,50 @@ class Command extends \yii\base\Component try { - if ($this->_connection->enableProfiling) - Yii::beginProfile('system.db.Command.query(' . $this->getText() . $par . ')', 'system.db.Command.query'); + if ($this->connection->enableProfiling) + Yii::beginProfile('system.db.Command.query(' . $this->getSql() . $par . ')', 'system.db.Command.query'); $this->prepare(); if ($params === array()) - $this->_statement->execute(); + $this->pdoStatement->execute(); else - $this->_statement->execute($params); + $this->pdoStatement->execute($params); if ($method === '') - $result = new CDbDataReader($this); + $result = new DataReader($this); else { - $mode = (array)$mode; - $result = call_user_func_array(array($this->_statement, $method), $mode); - $this->_statement->closeCursor(); + if ($fetchMode === null) { + $fetchMode = $this->fetchMode; + } + $result = call_user_func_array(array($this->pdoStatement, $method), (array)$fetchMode); + $this->pdoStatement->closeCursor(); } - if ($this->_connection->enableProfiling) - Yii::endProfile('system.db.Command.query(' . $this->getText() . $par . ')', 'system.db.Command.query'); + if ($this->connection->enableProfiling) + Yii::endProfile('system.db.Command.query(' . $this->getSql() . $par . ')', 'system.db.Command.query'); if (isset($cache, $cacheKey)) - $cache->set($cacheKey, $result, $this->_connection->queryCachingDuration, $this->_connection->queryCachingDependency); + $cache->set($cacheKey, $result, $this->connection->queryCachingDuration, $this->connection->queryCachingDependency); return $result; } catch(Exception $e) { - if ($this->_connection->enableProfiling) - Yii::endProfile('system.db.Command.query(' . $this->getText() . $par . ')', 'system.db.Command.query'); + if ($this->connection->enableProfiling) + Yii::endProfile('system.db.Command.query(' . $this->getSql() . $par . ')', 'system.db.Command.query'); $errorInfo = $e instanceof PDOException ? $e->errorInfo : null; $message = $e->getMessage(); Yii::log(Yii::t('yii', 'Command::{method}() failed: {error}. The SQL statement executed was: {sql}.', - array('{method}' => $method, '{error}' => $message, '{sql}' => $this->getText() . $par)), CLogger::LEVEL_ERROR, 'system.db.Command'); + array('{method}' => $method, '{error}' => $message, '{sql}' => $this->getSql() . $par)), CLogger::LEVEL_ERROR, 'system.db.Command'); if (YII_DEBUG) - $message .= '. The SQL statement executed was: ' . $this->getText() . $par; + $message .= '. The SQL statement executed was: ' . $this->getSql() . $par; throw new CDbException(Yii::t('yii', 'Command failed to execute the SQL statement: {error}', array('{error}' => $message)), (int)$e->getCode(), $errorInfo); } } /** - * Builds a SQL SELECT statement from the given query specification. - * @param array $query the query specification in name-value pairs. The following - * query options are supported: {@link select}, {@link distinct}, {@link from}, - * {@link where}, {@link join}, {@link group}, {@link having}, {@link order}, - * {@link limit}, {@link offset} and {@link union}. - * @return string the SQL statement - * @since 1.1.6 - */ - public function buildQuery($query) - { - $sql = isset($query['distinct']) && $query['distinct'] ? 'SELECT DISTINCT' : 'SELECT'; - $sql .= ' ' . (isset($query['select']) ? $query['select'] : '*'); - - if (isset($query['from'])) - $sql .= "\nFROM " . $query['from']; - else - throw new CDbException(Yii::t('yii', 'The DB query must contain the "from" portion.')); - - if (isset($query['join'])) - $sql .= "\n" . (is_array($query['join']) ? implode("\n", $query['join']) : $query['join']); - - if (isset($query['where'])) - $sql .= "\nWHERE " . $query['where']; - - if (isset($query['group'])) - $sql .= "\nGROUP BY " . $query['group']; - - if (isset($query['having'])) - $sql .= "\nHAVING " . $query['having']; - - if (isset($query['order'])) - $sql .= "\nORDER BY " . $query['order']; - - $limit = isset($query['limit']) ? (int)$query['limit'] : -1; - $offset = isset($query['offset']) ? (int)$query['offset'] : -1; - if ($limit >= 0 || $offset > 0) - $sql = $this->_connection->getCommandBuilder()->applyLimit($sql, $limit, $offset); - - if (isset($query['union'])) - $sql .= "\nUNION (\n" . (is_array($query['union']) ? implode("\n) UNION (\n", $query['union']) : $query['union']) . ')'; - - return $sql; - } - - /** * Sets the SELECT part of the query. * @param mixed $columns the columns to be selected. Defaults to '*', meaning all columns. * Columns can be specified in either a string (e.g. "id, name") or an array (e.g. array('id', 'name')). @@ -576,84 +506,22 @@ class Command extends \yii\base\Component */ public function select($columns = '*', $option = '') { - if (is_string($columns) && strpos($columns, '(') !== false) - $this->_query['select'] = $columns; - else - { - if (!is_array($columns)) - $columns = preg_split('/\s*,\s*/', trim($columns), -1, PREG_SPLIT_NO_EMPTY); - - foreach ($columns as $i => $column) - { - if (is_object($column)) - $columns[$i] = (string)$column; - elseif (strpos($column, '(') === false) - { - if (preg_match('/^(.*?)(?i:\s+as\s+|\s+)(.*)$/', $column, $matches)) - $columns[$i] = $this->_connection->quoteColumnName($matches[1]) . ' AS ' . $this->_connection->quoteColumnName($matches[2]); - else - $columns[$i] = $this->_connection->quoteColumnName($column); - } - } - $this->_query['select'] = implode(', ', $columns); - } - if ($option != '') - $this->_query['select'] = $option . ' ' . $this->_query['select']; + $this->query->select = $columns; + $this->query->selectOption = $option; return $this; } /** - * Returns the SELECT part in the query. - * @return string the SELECT part (without 'SELECT') in the query. - * @since 1.1.6 - */ - public function getSelect() - { - return isset($this->_query['select']) ? $this->_query['select'] : ''; - } - - /** - * Sets the SELECT part in the query. - * @param mixed $value the data to be selected. Please refer to {@link select()} for details - * on how to specify this parameter. - * @since 1.1.6 - */ - public function setSelect($value) - { - $this->select($value); - } - - /** * Sets the SELECT part of the query with the DISTINCT flag turned on. * This is the same as {@link select} except that the DISTINCT flag is turned on. * @param mixed $columns the columns to be selected. See {@link select} for more details. * @return Command the command object itself * @since 1.1.6 */ - public function selectDistinct($columns = '*') + public function selectDistinct($columns = '*', $option = '') { - $this->_query['distinct'] = true; - return $this->select($columns); - } - - /** - * Returns a value indicating whether SELECT DISTINCT should be used. - * @return boolean a value indicating whether SELECT DISTINCT should be used. - * @since 1.1.6 - */ - public function getDistinct() - { - return isset($this->_query['distinct']) ? $this->_query['distinct'] : false; - } - - /** - * Sets a value indicating whether SELECT DISTINCT should be used. - * @param boolean $value a value indicating whether SELECT DISTINCT should be used. - * @since 1.1.6 - */ - public function setDistinct($value) - { - $this->_query['distinct'] = $value; + $this->query->distinct = true; + return $this->select($columns, $option); } /** @@ -668,49 +536,11 @@ class Command extends \yii\base\Component */ public function from($tables) { - if (is_string($tables) && strpos($tables, '(') !== false) - $this->_query['from'] = $tables; - else - { - if (!is_array($tables)) - $tables = preg_split('/\s*,\s*/', trim($tables), -1, PREG_SPLIT_NO_EMPTY); - foreach ($tables as $i => $table) - { - if (strpos($table, '(') === false) - { - if (preg_match('/^(.*?)(?i:\s+as\s+|\s+)(.*)$/', $table, $matches)) // with alias - $tables[$i] = $this->_connection->quoteTableName($matches[1]) . ' ' . $this->_connection->quoteTableName($matches[2]); - else - $tables[$i] = $this->_connection->quoteTableName($table); - } - } - $this->_query['from'] = implode(', ', $tables); - } + $this->query->from = $tables; return $this; } /** - * Returns the FROM part in the query. - * @return string the FROM part (without 'FROM' ) in the query. - * @since 1.1.6 - */ - public function getFrom() - { - return isset($this->_query['from']) ? $this->_query['from'] : ''; - } - - /** - * Sets the FROM part in the query. - * @param mixed $value the tables to be selected from. Please refer to {@link from()} for details - * on how to specify this parameter. - * @since 1.1.6 - */ - public function setFrom($value) - { - $this->from($value); - } - - /** * Sets the WHERE part of the query. * * The method requires a $conditions parameter, and optionally a $params parameter @@ -750,34 +580,12 @@ class Command extends \yii\base\Component */ public function where($conditions, $params = array()) { - $this->_query['where'] = $this->processConditions($conditions); - foreach ($params as $name => $value) - $this->params[$name] = $value; + $this->query->where = $conditions; + $this->query->addParams($params); return $this; } /** - * Returns the WHERE part in the query. - * @return string the WHERE part (without 'WHERE' ) in the query. - * @since 1.1.6 - */ - public function getWhere() - { - return isset($this->_query['where']) ? $this->_query['where'] : ''; - } - - /** - * Sets the WHERE part in the query. - * @param mixed $value the where part. Please refer to {@link where()} for details - * on how to specify this parameter. - * @since 1.1.6 - */ - public function setWhere($value) - { - $this->where($value); - } - - /** * Appends an INNER JOIN part to the query. * @param string $table the table to be joined. * Table name can contain schema prefix (e.g. 'public.tbl_user') and/or table alias (e.g. 'tbl_user u'). @@ -791,31 +599,7 @@ class Command extends \yii\base\Component */ public function join($table, $conditions, $params = array()) { - return $this->joinInternal('join', $table, $conditions, $params); - } - - /** - * Returns the join part in the query. - * @return mixed the join part in the query. This can be an array representing - * multiple join fragments, or a string representing a single jojin fragment. - * Each join fragment will contain the proper join operator (e.g. LEFT JOIN). - * @since 1.1.6 - */ - public function getJoin() - { - return isset($this->_query['join']) ? $this->_query['join'] : ''; - } - - /** - * Sets the join part in the query. - * @param mixed $value the join part in the query. This can be either a string or - * an array representing multiple join parts in the query. Each part must contain - * the proper join operator (e.g. 'LEFT JOIN tbl_profile ON tbl_user.id=tbl_profile.id') - * @since 1.1.6 - */ - public function setJoin($value) - { - $this->_query['join'] = $value; + return $this->joinInternal('JOIN', $table, $conditions, $params); } /** @@ -832,7 +616,7 @@ class Command extends \yii\base\Component */ public function leftJoin($table, $conditions, $params = array()) { - return $this->joinInternal('left join', $table, $conditions, $params); + return $this->joinInternal('LEFT JOIN', $table, $conditions, $params); } /** @@ -849,7 +633,7 @@ class Command extends \yii\base\Component */ public function rightJoin($table, $conditions, $params = array()) { - return $this->joinInternal('right join', $table, $conditions, $params); + return $this->joinInternal('RIGHT JOIN', $table, $conditions, $params); } /** @@ -864,7 +648,7 @@ class Command extends \yii\base\Component */ public function crossJoin($table) { - return $this->joinInternal('cross join', $table); + return $this->joinInternal('CROSS JOIN', $table); } /** @@ -879,7 +663,7 @@ class Command extends \yii\base\Component */ public function naturalJoin($table) { - return $this->joinInternal('natural join', $table); + return $this->joinInternal('NATURAL JOIN', $table); } /** @@ -891,48 +675,13 @@ class Command extends \yii\base\Component * @return Command the command object itself * @since 1.1.6 */ - public function group($columns) + public function groupBy($columns) { - if (is_string($columns) && strpos($columns, '(') !== false) - $this->_query['group'] = $columns; - else - { - if (!is_array($columns)) - $columns = preg_split('/\s*,\s*/', trim($columns), -1, PREG_SPLIT_NO_EMPTY); - foreach ($columns as $i => $column) - { - if (is_object($column)) - $columns[$i] = (string)$column; - elseif (strpos($column, '(') === false) - $columns[$i] = $this->_connection->quoteColumnName($column); - } - $this->_query['group'] = implode(', ', $columns); - } + $this->query->groupBy = $columns; return $this; } /** - * Returns the GROUP BY part in the query. - * @return string the GROUP BY part (without 'GROUP BY' ) in the query. - * @since 1.1.6 - */ - public function getGroup() - { - return isset($this->_query['group']) ? $this->_query['group'] : ''; - } - - /** - * Sets the GROUP BY part in the query. - * @param mixed $value the GROUP BY part. Please refer to {@link group()} for details - * on how to specify this parameter. - * @since 1.1.6 - */ - public function setGroup($value) - { - $this->group($value); - } - - /** * Sets the HAVING part of the query. * @param mixed $conditions the conditions to be put after HAVING. * Please refer to {@link where} on how to specify conditions. @@ -942,34 +691,12 @@ class Command extends \yii\base\Component */ public function having($conditions, $params = array()) { - $this->_query['having'] = $this->processConditions($conditions); - foreach ($params as $name => $value) - $this->params[$name] = $value; + $this->query->having = $conditions; + $this->query->addParams($params); return $this; } /** - * Returns the HAVING part in the query. - * @return string the HAVING part (without 'HAVING' ) in the query. - * @since 1.1.6 - */ - public function getHaving() - { - return isset($this->_query['having']) ? $this->_query['having'] : ''; - } - - /** - * Sets the HAVING part in the query. - * @param mixed $value the HAVING part. Please refer to {@link having()} for details - * on how to specify this parameter. - * @since 1.1.6 - */ - public function setHaving($value) - { - $this->having($value); - } - - /** * Sets the ORDER BY part of the query. * @param mixed $columns the columns (and the directions) to be ordered by. * Columns can be specified in either a string (e.g. "id ASC, name DESC") or an array (e.g. array('id ASC', 'name DESC')). @@ -978,89 +705,26 @@ class Command extends \yii\base\Component * @return Command the command object itself * @since 1.1.6 */ - public function order($columns) + public function orderBy($columns) { - if (is_string($columns) && strpos($columns, '(') !== false) - $this->_query['order'] = $columns; - else - { - if (!is_array($columns)) - $columns = preg_split('/\s*,\s*/', trim($columns), -1, PREG_SPLIT_NO_EMPTY); - foreach ($columns as $i => $column) - { - if (is_object($column)) - $columns[$i] = (string)$column; - elseif (strpos($column, '(') === false) - { - if (preg_match('/^(.*?)\s+(asc|desc)$/i', $column, $matches)) - $columns[$i] = $this->_connection->quoteColumnName($matches[1]) . ' ' . strtoupper($matches[2]); - else - $columns[$i] = $this->_connection->quoteColumnName($column); - } - } - $this->_query['order'] = implode(', ', $columns); - } + $this->query->orderBy = $columns; return $this; } /** - * Returns the ORDER BY part in the query. - * @return string the ORDER BY part (without 'ORDER BY' ) in the query. - * @since 1.1.6 - */ - public function getOrder() - { - return isset($this->_query['order']) ? $this->_query['order'] : ''; - } - - /** - * Sets the ORDER BY part in the query. - * @param mixed $value the ORDER BY part. Please refer to {@link order()} for details - * on how to specify this parameter. - * @since 1.1.6 - */ - public function setOrder($value) - { - $this->order($value); - } - - /** * Sets the LIMIT part of the query. * @param integer $limit the limit * @param integer $offset the offset * @return Command the command object itself * @since 1.1.6 */ - public function limit($limit, $offset = null) + public function limit($limit) { - $this->_query['limit'] = (int)$limit; - if ($offset !== null) - $this->offset($offset); + $this->query->limit = $limit; return $this; } /** - * Returns the LIMIT part in the query. - * @return string the LIMIT part (without 'LIMIT' ) in the query. - * @since 1.1.6 - */ - public function getLimit() - { - return isset($this->_query['limit']) ? $this->_query['limit'] : -1; - } - - /** - * Sets the LIMIT part in the query. - * @param integer $value the LIMIT part. Please refer to {@link limit()} for details - * on how to specify this parameter. - * @since 1.1.6 - */ - public function setLimit($value) - { - $this->limit($value); - } - - /** * Sets the OFFSET part of the query. * @param integer $offset the offset * @return Command the command object itself @@ -1068,32 +732,11 @@ class Command extends \yii\base\Component */ public function offset($offset) { - $this->_query['offset'] = (int)$offset; + $this->query->offset = $offset; return $this; } /** - * Returns the OFFSET part in the query. - * @return string the OFFSET part (without 'OFFSET' ) in the query. - * @since 1.1.6 - */ - public function getOffset() - { - return isset($this->_query['offset']) ? $this->_query['offset'] : -1; - } - - /** - * Sets the OFFSET part in the query. - * @param integer $value the OFFSET part. Please refer to {@link offset()} for details - * on how to specify this parameter. - * @since 1.1.6 - */ - public function setOffset($value) - { - $this->offset($value); - } - - /** * Appends a SQL statement using UNION operator. * @param string $sql the SQL statement to be appended using UNION * @return Command the command object itself @@ -1101,34 +744,8 @@ class Command extends \yii\base\Component */ public function union($sql) { - if (isset($this->_query['union']) && is_string($this->_query['union'])) - $this->_query['union'] = array($this->_query['union']); - - $this->_query['union'][] = $sql; - - return $this; - } - - /** - * Returns the UNION part in the query. - * @return mixed the UNION part (without 'UNION' ) in the query. - * This can be either a string or an array representing multiple union parts. - * @since 1.1.6 - */ - public function getUnion() - { - return isset($this->_query['union']) ? $this->_query['union'] : ''; - } - - /** - * Sets the UNION part in the query. - * @param mixed $value the UNION part. This can be either a string or an array - * representing multiple SQL statements to be unioned together. - * @since 1.1.6 - */ - public function setUnion($value) - { - $this->_query['union'] = $value; + $this->query->union[] = $sql; + return $this->query; } /** @@ -1141,28 +758,8 @@ class Command extends \yii\base\Component */ public function insert($table, $columns) { - $params = array(); - $names = array(); - $placeholders = array(); - foreach ($columns as $name => $value) - { - $names[] = $this->_connection->quoteColumnName($name); - if ($value instanceof CDbExpression) - { - $placeholders[] = $value->expression; - foreach ($value->params as $n => $v) - $params[$n] = $v; - } - else - { - $placeholders[] = ':' . $name; - $params[':' . $name] = $value; - } - } - $sql = 'INSERT INTO ' . $this->_connection->quoteTableName($table) - . ' (' . implode(', ', $names) . ') VALUES (' - . implode(', ', $placeholders) . ')'; - return $this->setText($sql)->execute($params); + $sql = $this->connection->getQueryBuilder()->insert($table, $columns, $params); + return $this->setSql($sql)->execute($params); } /** @@ -1178,25 +775,8 @@ class Command extends \yii\base\Component */ public function update($table, $columns, $conditions = '', $params = array()) { - $lines = array(); - foreach ($columns as $name => $value) - { - if ($value instanceof CDbExpression) - { - $lines[] = $this->_connection->quoteColumnName($name) . '=' . $value->expression; - foreach ($value->params as $n => $v) - $params[$n] = $v; - } - else - { - $lines[] = $this->_connection->quoteColumnName($name) . '=:' . $name; - $params[':' . $name] = $value; - } - } - $sql = 'UPDATE ' . $this->_connection->quoteTableName($table) . ' SET ' . implode(', ', $lines); - if (($where = $this->processConditions($conditions)) != '') - $sql .= ' WHERE ' . $where; - return $this->setText($sql)->execute($params); + $sql = $this->connection->getQueryBuilder()->update($table, $columns, $conditions, $params); + return $this->setSql($sql)->execute($params); } /** @@ -1210,10 +790,8 @@ class Command extends \yii\base\Component */ public function delete($table, $conditions = '', $params = array()) { - $sql = 'DELETE FROM ' . $this->_connection->quoteTableName($table); - if (($where = $this->processConditions($conditions)) != '') - $sql .= ' WHERE ' . $where; - return $this->setText($sql)->execute($params); + $sql = $this->connection->getQueryBuilder()->delete($table, $conditions); + return $this->setSql($sql)->execute($params); } /** @@ -1235,7 +813,8 @@ class Command extends \yii\base\Component */ public function createTable($table, $columns, $options = null) { - return $this->setText($this->getConnection()->getSchema()->createTable($table, $columns, $options))->execute(); + $sql = $this->connection->getQueryBuilder()->createTable($table, $columns, $options); + return $this->setSql($sql)->execute(); } /** @@ -1247,7 +826,8 @@ class Command extends \yii\base\Component */ public function renameTable($table, $newName) { - return $this->setText($this->getConnection()->getSchema()->renameTable($table, $newName))->execute(); + $sql = $this->connection->getQueryBuilder()->renameTable($table, $newName); + return $this->setSql($sql)->execute(); } /** @@ -1258,7 +838,8 @@ class Command extends \yii\base\Component */ public function dropTable($table) { - return $this->setText($this->getConnection()->getSchema()->dropTable($table))->execute(); + $sql = $this->connection->getQueryBuilder()->dropTable($table); + return $this->setSql($sql)->execute(); } /** @@ -1269,11 +850,8 @@ class Command extends \yii\base\Component */ public function truncateTable($table) { - $schema = $this->getConnection()->getSchema(); - $n = $this->setText($schema->truncateTable($table))->execute(); - if (strncasecmp($this->getConnection()->getDriverName(), 'sqlite', 6) === 0) - $schema->resetSequence($schema->getTable($table)); - return $n; + $sql = $this->connection->getQueryBuilder()->truncateTable($table); + return $this->setSql($sql)->execute(); } /** @@ -1288,7 +866,8 @@ class Command extends \yii\base\Component */ public function addColumn($table, $column, $type) { - return $this->setText($this->getConnection()->getSchema()->addColumn($table, $column, $type))->execute(); + $sql = $this->connection->getQueryBuilder()->addColumn($table, $column, $type); + return $this->setSql($sql)->execute(); } /** @@ -1300,7 +879,8 @@ class Command extends \yii\base\Component */ public function dropColumn($table, $column) { - return $this->setText($this->getConnection()->getSchema()->dropColumn($table, $column))->execute(); + $sql = $this->connection->getQueryBuilder()->dropColumn($table, $column); + return $this->setSql($sql)->execute(); } /** @@ -1313,7 +893,8 @@ class Command extends \yii\base\Component */ public function renameColumn($table, $name, $newName) { - return $this->setText($this->getConnection()->getSchema()->renameColumn($table, $name, $newName))->execute(); + $sql = $this->connection->getQueryBuilder()->renameColumn($table, $name, $newName); + return $this->setSql($sql)->execute(); } /** @@ -1328,7 +909,8 @@ class Command extends \yii\base\Component */ public function alterColumn($table, $column, $type) { - return $this->setText($this->getConnection()->getSchema()->alterColumn($table, $column, $type))->execute(); + $sql = $this->connection->getQueryBuilder()->alterColumn($table, $column, $type); + return $this->setSql($sql)->execute(); } /** @@ -1346,7 +928,8 @@ class Command extends \yii\base\Component */ public function addForeignKey($name, $table, $columns, $refTable, $refColumns, $delete = null, $update = null) { - return $this->setText($this->getConnection()->getSchema()->addForeignKey($name, $table, $columns, $refTable, $refColumns, $delete, $update))->execute(); + $sql = $this->connection->getQueryBuilder()->addForeignKey($name, $table, $columns, $refTable, $refColumns, $delete, $update); + return $this->setSql($sql)->execute(); } /** @@ -1358,7 +941,8 @@ class Command extends \yii\base\Component */ public function dropForeignKey($name, $table) { - return $this->setText($this->getConnection()->getSchema()->dropForeignKey($name, $table))->execute(); + $sql = $this->connection->getQueryBuilder()->dropForeignKey($name, $table); + return $this->setSql($sql)->execute(); } /** @@ -1373,7 +957,8 @@ class Command extends \yii\base\Component */ public function createIndex($name, $table, $column, $unique = false) { - return $this->setText($this->getConnection()->getSchema()->createIndex($name, $table, $column, $unique))->execute(); + $sql = $this->connection->getQueryBuilder()->createIndex($name, $table, $column, $unique); + return $this->setSql($sql)->execute(); } /** @@ -1385,78 +970,8 @@ class Command extends \yii\base\Component */ public function dropIndex($name, $table) { - return $this->setText($this->getConnection()->getSchema()->dropIndex($name, $table))->execute(); - } - - /** - * Generates the condition string that will be put in the WHERE part - * @param mixed $conditions the conditions that will be put in the WHERE part. - * @return string the condition string to put in the WHERE part - */ - private function processConditions($conditions) - { - if (!is_array($conditions)) - return $conditions; - elseif ($conditions === array()) - return ''; - $n = count($conditions); - $operator = strtoupper($conditions[0]); - if ($operator === 'OR' || $operator === 'AND') - { - $parts = array(); - for ($i = 1;$i < $n;++$i) - { - $condition = $this->processConditions($conditions[$i]); - if ($condition !== '') - $parts[] = '(' . $condition . ')'; - } - return $parts === array() ? '' : implode(' ' . $operator . ' ', $parts); - } - - if (!isset($conditions[1], $conditions[2])) - return ''; - - $column = $conditions[1]; - if (strpos($column, '(') === false) - $column = $this->_connection->quoteColumnName($column); - - $values = $conditions[2]; - if (!is_array($values)) - $values = array($values); - - if ($operator === 'IN' || $operator === 'NOT IN') - { - if ($values === array()) - return $operator === 'IN' ? '0=1' : ''; - foreach ($values as $i => $value) - { - if (is_string($value)) - $values[$i] = $this->_connection->quoteValue($value); - else - $values[$i] = (string)$value; - } - return $column . ' ' . $operator . ' (' . implode(', ', $values) . ')'; - } - - if ($operator === 'LIKE' || $operator === 'NOT LIKE' || $operator === 'OR LIKE' || $operator === 'OR NOT LIKE') - { - if ($values === array()) - return $operator === 'LIKE' || $operator === 'OR LIKE' ? '0=1' : ''; - - if ($operator === 'LIKE' || $operator === 'NOT LIKE') - $andor = ' AND '; - else - { - $andor = ' OR '; - $operator = $operator === 'OR LIKE' ? 'LIKE' : 'NOT LIKE'; - } - $expressions = array(); - foreach ($values as $value) - $expressions[] = $column . ' ' . $operator . ' ' . $this->_connection->quoteValue($value); - return implode($andor, $expressions); - } - - throw new CDbException(Yii::t('yii', 'Unknown operator "{operator}".', array('{operator}' => $operator))); + $sql = $this->connection->getQueryBuilder()->dropIndex($name, $table); + return $this->setSql($sql)->execute(); } /** @@ -1474,25 +989,8 @@ class Command extends \yii\base\Component */ private function joinInternal($type, $table, $conditions = '', $params = array()) { - 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]); - else - $table = $this->_connection->quoteTableName($table); - } - - $conditions = $this->processConditions($conditions); - if ($conditions != '') - $conditions = ' ON ' . $conditions; - - if (isset($this->_query['join']) && is_string($this->_query['join'])) - $this->_query['join'] = array($this->_query['join']); - - $this->_query['join'][] = strtoupper($type) . ' ' . $table . $conditions; - - foreach ($params as $name => $value) - $this->params[$name] = $value; + $this->query->join[] = array($type, $table, $conditions); + $this->query->addParams($params); return $this; } } diff --git a/framework/db/dao/Connection.php b/framework/db/dao/Connection.php index edea75b..2d4b8a7 100644 --- a/framework/db/dao/Connection.php +++ b/framework/db/dao/Connection.php @@ -460,6 +460,11 @@ class Connection extends \yii\base\ApplicationComponent } } + public function getQueryBuilder() + { + return $this->getSchema()->getQueryBuilder(); + } + /** * Returns the ID of the last inserted row or sequence value. * @param string $sequenceName name of the sequence object (required by some DBMS) diff --git a/framework/db/dao/DataReader.php b/framework/db/dao/DataReader.php index 663f526..d2228d8 100644 --- a/framework/db/dao/DataReader.php +++ b/framework/db/dao/DataReader.php @@ -8,6 +8,8 @@ * @license http://www.yiiframework.com/license/ */ +namespace yii\db\dao; + /** * DataReader represents a forward-only stream of rows from a query result set. * diff --git a/framework/db/dao/Query.php b/framework/db/dao/Query.php index 691dbda..cf96ad1 100644 --- a/framework/db/dao/Query.php +++ b/framework/db/dao/Query.php @@ -16,7 +16,7 @@ namespace yii\db\dao; * @author Qiang Xue * @since 2.0 */ -class Query extends CComponent +class Query extends \yii\base\Component { /** * @var mixed the columns being selected. This refers to the SELECT clause in an SQL @@ -72,7 +72,7 @@ class Query extends CComponent * @var array list of query parameter values indexed by parameter placeholders. * For example, array(':name'=>'Dan', ':age'=>31). */ - public $params; + public $params = array(); public $union; @@ -82,6 +82,18 @@ class Query extends CComponent return $connection->getQueryBuilder()->build($this); } + public function addParams($params) + { + foreach ($params as $name => $value) { + if (is_integer($name)) { + $this->params[] = $value; + } + else { + $this->params[$name] = $value; + } + } + } + /** * Appends a condition to the existing {@link condition}. * The new condition and the existing condition will be concatenated via the specified operator @@ -361,157 +373,6 @@ class Query extends CComponent } /** - * Merges with another criteria. - * In general, the merging makes the resulting criteria more restrictive. - * For example, if both criterias have conditions, they will be 'AND' together. - * Also, the criteria passed as the parameter takes precedence in case - * two options cannot be merged (e.g. LIMIT, OFFSET). - * @param Query $criteria the criteria to be merged with. - * @param boolean $useAnd whether to use 'AND' to merge condition and having options. - * If false, 'OR' will be used instead. Defaults to 'AND'. This parameter has been - * available since version 1.0.6. - * @since 1.0.5 - */ - public function mergeWith($criteria, $useAnd = true) - { - $and = $useAnd ? 'AND' : 'OR'; - if (is_array($criteria)) - $criteria = new self($criteria); - if ($this->select !== $criteria->select) - { - if ($this->select === '*') - $this->select = $criteria->select; - elseif ($criteria->select !== '*') - { - $select1 = is_string($this->select) ? preg_split('/\s*,\s*/', trim($this->select), -1, PREG_SPLIT_NO_EMPTY) : $this->select; - $select2 = is_string($criteria->select) ? preg_split('/\s*,\s*/', trim($criteria->select), -1, PREG_SPLIT_NO_EMPTY) : $criteria->select; - $this->select = array_merge($select1, array_diff($select2, $select1)); - } - } - - if ($this->condition !== $criteria->condition) - { - if ($this->condition === '') - $this->condition = $criteria->condition; - elseif ($criteria->condition !== '') - $this->condition = "( {$this->condition}) $and ( {$criteria->condition})"; - } - - if ($this->params !== $criteria->params) - $this->params = array_merge($this->params, $criteria->params); - - if ($criteria->limit > 0) - $this->limit = $criteria->limit; - - if ($criteria->offset >= 0) - $this->offset = $criteria->offset; - - if ($criteria->alias !== null) - $this->alias = $criteria->alias; - - if ($this->order !== $criteria->order) - { - if ($this->order === '') - $this->order = $criteria->order; - elseif ($criteria->order !== '') - $this->order = $criteria->order . ', ' . $this->order; - } - - if ($this->group !== $criteria->group) - { - if ($this->group === '') - $this->group = $criteria->group; - elseif ($criteria->group !== '') - $this->group .= ', ' . $criteria->group; - } - - if ($this->join !== $criteria->join) - { - if ($this->join === '') - $this->join = $criteria->join; - elseif ($criteria->join !== '') - $this->join .= ' ' . $criteria->join; - } - - if ($this->having !== $criteria->having) - { - if ($this->having === '') - $this->having = $criteria->having; - elseif ($criteria->having !== '') - $this->having = "( {$this->having}) $and ( {$criteria->having})"; - } - - if ($criteria->distinct > 0) - $this->distinct = $criteria->distinct; - - if ($criteria->together !== null) - $this->together = $criteria->together; - - if ($criteria->index !== null) - $this->index = $criteria->index; - - if (empty($this->scopes)) - $this->scopes = $criteria->scopes; - elseif (!empty($criteria->scopes)) - { - $scopes1 = (array)$this->scopes; - $scopes2 = (array)$criteria->scopes; - foreach ($scopes1 as $k => $v) - { - if (is_integer($k)) - $scopes[] = $v; - elseif (isset($scopes2[$k])) - $scopes[] = array($k => $v); - else - $scopes[$k] = $v; - } - foreach ($scopes2 as $k => $v) - { - if (is_integer($k)) - $scopes[] = $v; - elseif (isset($scopes1[$k])) - $scopes[] = array($k => $v); - else - $scopes[$k] = $v; - } - $this->scopes = $scopes; - } - - if (empty($this->with)) - $this->with = $criteria->with; - elseif (!empty($criteria->with)) - { - $this->with = (array)$this->with; - foreach ((array)$criteria->with as $k => $v) - { - if (is_integer($k)) - $this->with[] = $v; - elseif (isset($this->with[$k])) - { - $excludes = array(); - foreach (array('joinType', 'on') as $opt) - { - if (isset($this->with[$k][$opt])) - $excludes[$opt] = $this->with[$k][$opt]; - if (isset($v[$opt])) - $excludes[$opt] = ($opt === 'on' && isset($excludes[$opt]) && $v[$opt] !== $excludes[$opt]) ? - "($excludes[$opt]) AND $v[$opt]" : $v[$opt]; - unset($this->with[$k][$opt]); - unset($v[$opt]); - } - $this->with[$k] = new self($this->with[$k]); - $this->with[$k]->mergeWith($v, $useAnd); - $this->with[$k] = $this->with[$k]->toArray(); - if (count($excludes) !== 0) - $this->with[$k] = CMap::mergeArray($this->with[$k], $excludes); - } - else - $this->with[$k] = $v; - } - } - } - - /** * @return array the array representation of the criteria * @since 1.0.6 */ diff --git a/framework/db/dao/QueryBuilder.php b/framework/db/dao/QueryBuilder.php index c32a429..f26118b 100644 --- a/framework/db/dao/QueryBuilder.php +++ b/framework/db/dao/QueryBuilder.php @@ -42,7 +42,7 @@ class QueryBuilder extends \yii\base\Component public function __construct($schema) { - $this->connection = $schema->getDbConnection(); + $this->connection = $schema->connection; $this->schema = $schema; } @@ -59,8 +59,98 @@ class QueryBuilder extends \yii\base\Component $this->buildOrderBy($query), $this->buildLimit($query), ); + $sql = implode("\n", array_filter($clauses)); + if ($this->connection->tablePrefix !== null && strpos($sql, '{') !== false) { + $sql = preg_replace('/{{(.*?)}}/', $this->connection->tablePrefix . '\1', $sql); + } + return $sql; + } + + /** + * Creates and executes an INSERT SQL statement. + * The method will properly escape the column names, and bind the values to be inserted. + * @param string $table the table that new rows will be inserted into. + * @param array $columns the column data (name=>value) to be inserted into the table. + * @return integer number of rows affected by the execution. + * @since 1.1.6 + */ + public function insert($table, $columns, &$params = array()) + { + $names = array(); + $placeholders = array(); + $count = 0; + foreach ($columns as $name => $value) { + $names[] = $this->schema->quoteColumnName($name); + if ($value instanceof Expression) { + $placeholders[] = $value->expression; + foreach ($value->params as $n => $v) { + $params[$n] = $v; + } + } + else { + $placeholders[] = ':p' . $count; + $params[':p' . $count] = $value; + $count++; + } + } + + return 'INSERT INTO ' . $this->schema->quoteTableName($table) + . ' (' . implode(', ', $names) . ') VALUES (' + . implode(', ', $placeholders) . ')'; + } + + /** + * Creates and executes an UPDATE SQL statement. + * The method will properly escape the column names and bind the values to be updated. + * @param string $table the table to be updated. + * @param array $columns the column data (name=>value) to be updated. + * @param mixed $conditions the conditions that will be put in the WHERE part. Please + * refer to {@link where} on how to specify conditions. + * @param array $params the parameters to be bound to the query. + * @return integer number of rows affected by the execution. + * @since 1.1.6 + */ + public function update($table, $columns, $conditions = '', &$params = array()) + { + $lines = array(); + $count = 0; + foreach ($columns as $name => $value) { + if ($value instanceof Expression) { + $lines[] = $this->schema->quoteSimpleColumnName($name) . '=' . $value->expression; + foreach ($value->params as $n => $v) { + $params[$n] = $v; + } + } + else { + $lines[] = $this->schema->quoteSimpleColumnName($name) . '=:p' . $count; + $params[':p' . $count] = $value; + $count++; + } + } + $sql = 'UPDATE ' . $this->schema->quoteTableName($table) . ' SET ' . implode(', ', $lines); + if (($where = $this->buildCondition($conditions)) != '') { + $sql .= ' WHERE ' . $where; + } + + return $sql; + } - return implode("\n", array_filter($clauses)); + /** + * Creates and executes a DELETE SQL statement. + * @param string $table the table where the data will be deleted from. + * @param mixed $conditions the conditions that will be put in the WHERE part. Please + * refer to {@link where} on how to specify conditions. + * @param array $params the parameters to be bound to the query. + * @return integer number of rows affected by the execution. + * @since 1.1.6 + */ + public function delete($table, $conditions = '') + { + $sql = 'DELETE FROM ' . $this->schema->quoteTableName($table); + if (($where = $this->buildCondition($conditions)) != '') { + $sql .= ' WHERE ' . $where; + } + return $sql; } /** @@ -101,7 +191,7 @@ class QueryBuilder extends \yii\base\Component */ public function renameTable($table, $newName) { - return 'RENAME TABLE ' . $this->quoteTableName($table) . ' TO ' . $this->quoteTableName($newName); + return 'RENAME TABLE ' . $this->schema->quoteTableName($table) . ' TO ' . $this->schema->quoteTableName($newName); } /** @@ -111,7 +201,7 @@ class QueryBuilder extends \yii\base\Component */ public function dropTable($table) { - return "DROP TABLE " . $this->quoteTableName($table); + return "DROP TABLE " . $this->schema->quoteTableName($table); } /** @@ -121,7 +211,7 @@ class QueryBuilder extends \yii\base\Component */ public function truncateTable($table) { - return "TRUNCATE TABLE " . $this->quoteTableName($table); + return "TRUNCATE TABLE " . $this->schema->quoteTableName($table); } /** @@ -136,8 +226,8 @@ class QueryBuilder extends \yii\base\Component */ public function addColumn($table, $column, $type) { - return 'ALTER TABLE ' . $this->quoteTableName($table) - . ' ADD ' . $this->quoteColumnName($column) . ' ' + return 'ALTER TABLE ' . $this->schema->quoteTableName($table) + . ' ADD ' . $this->schema->quoteColumnName($column) . ' ' . $this->getColumnType($type); } @@ -150,8 +240,8 @@ class QueryBuilder extends \yii\base\Component */ public function dropColumn($table, $column) { - return "ALTER TABLE " . $this->quoteTableName($table) - . " DROP COLUMN " . $this->quoteSimpleColumnName($column); + return "ALTER TABLE " . $this->schema->quoteTableName($table) + . " DROP COLUMN " . $this->schema->quoteSimpleColumnName($column); } /** @@ -164,9 +254,9 @@ class QueryBuilder extends \yii\base\Component */ public function renameColumn($table, $name, $newName) { - return "ALTER TABLE " . $this->quoteTableName($table) - . " RENAME COLUMN " . $this->quoteSimpleColumnName($name) - . " TO " . $this->quoteSimpleColumnName($newName); + return "ALTER TABLE " . $this->schema->quoteTableName($table) + . " RENAME COLUMN " . $this->schema->quoteSimpleColumnName($name) + . " TO " . $this->schema->quoteSimpleColumnName($newName); } /** @@ -180,9 +270,9 @@ class QueryBuilder extends \yii\base\Component */ public function alterColumn($table, $column, $type) { - return 'ALTER TABLE ' . $this->quoteTableName($table) . ' CHANGE ' - . $this->quoteSimpleColumnName($column) . ' ' - . $this->quoteSimpleColumnName($column) . ' ' + return 'ALTER TABLE ' . $this->schema->quoteTableName($table) . ' CHANGE ' + . $this->schema->quoteSimpleColumnName($column) . ' ' + . $this->schema->quoteSimpleColumnName($column) . ' ' . $this->getColumnType($type); } @@ -202,14 +292,14 @@ class QueryBuilder extends \yii\base\Component { $columns = preg_split('/\s*,\s*/', $columns, -1, PREG_SPLIT_NO_EMPTY); foreach ($columns as $i => $col) - $columns[$i] = $this->quoteColumnName($col); + $columns[$i] = $this->schema->quoteColumnName($col); $refColumns = preg_split('/\s*,\s*/', $refColumns, -1, PREG_SPLIT_NO_EMPTY); foreach ($refColumns as $i => $col) - $refColumns[$i] = $this->quoteColumnName($col); - $sql = 'ALTER TABLE ' . $this->quoteTableName($table) - . ' ADD CONSTRAINT ' . $this->quoteColumnName($name) + $refColumns[$i] = $this->schema->quoteColumnName($col); + $sql = 'ALTER TABLE ' . $this->schema->quoteTableName($table) + . ' ADD CONSTRAINT ' . $this->schema->quoteColumnName($name) . ' FOREIGN KEY (' . implode(', ', $columns) . ')' - . ' REFERENCES ' . $this->quoteTableName($refTable) + . ' REFERENCES ' . $this->schema->quoteTableName($refTable) . ' (' . implode(', ', $refColumns) . ')'; if ($delete !== null) $sql .= ' ON DELETE ' . $delete; @@ -226,8 +316,8 @@ class QueryBuilder extends \yii\base\Component */ public function dropForeignKey($name, $table) { - return 'ALTER TABLE ' . $this->quoteTableName($table) - . ' DROP CONSTRAINT ' . $this->quoteColumnName($name); + return 'ALTER TABLE ' . $this->schema->quoteTableName($table) + . ' DROP CONSTRAINT ' . $this->schema->quoteColumnName($name); } /** @@ -248,11 +338,11 @@ class QueryBuilder extends \yii\base\Component if (strpos($col, '(') !== false) $cols[] = $col; else - $cols[] = $this->quoteColumnName($col); + $cols[] = $this->schema->quoteColumnName($col); } return ($unique ? 'CREATE UNIQUE INDEX ' : 'CREATE INDEX ') - . $this->quoteTableName($name) . ' ON ' - . $this->quoteTableName($table) . ' (' . implode(', ', $cols) . ')'; + . $this->schema->quoteTableName($name) . ' ON ' + . $this->schema->quoteTableName($table) . ' (' . implode(', ', $cols) . ')'; } /** @@ -263,7 +353,7 @@ class QueryBuilder extends \yii\base\Component */ public function dropIndex($name, $table) { - return 'DROP INDEX ' . $this->quoteTableName($name) . ' ON ' . $this->quoteTableName($table); + return 'DROP INDEX ' . $this->schema->quoteTableName($name) . ' ON ' . $this->schema->quoteTableName($table); } /** @@ -330,6 +420,9 @@ class QueryBuilder extends \yii\base\Component protected function buildSelect($query) { $select = $query->distinct ? 'SELECT DISTINCT' : 'SELECT'; + if ($query->selectOption != '') { + $select .= ' ' . $query->selectOption; + } $columns = $query->select; if (empty($columns)) { @@ -408,7 +501,7 @@ class QueryBuilder extends \yii\base\Component } $joins[$i] = strtoupper($join[0]) . ' ' . $table; if (isset($join[2])) { // join condition - $condition = $this->processCondition($join[2]); + $condition = $this->buildCondition($join[2]); $joins[$i] .= ' ON ' . $condition; } } @@ -423,7 +516,7 @@ class QueryBuilder extends \yii\base\Component protected function buildWhere($query) { - $where = $this->processConditions($query->where); + $where = $this->buildCondition($query->where); return empty($where) ? '' : 'WHERE ' . $where; } @@ -453,7 +546,7 @@ class QueryBuilder extends \yii\base\Component protected function buildHaving($query) { - $having = $this->processConditions($query->having); + $having = $this->buildCondition($query->having); return empty($having) ? '' : 'HAVING ' . $having; } @@ -515,7 +608,7 @@ class QueryBuilder extends \yii\base\Component return "UNION (\n" . implode("\n) UNION (\n", $unions) . "\n)"; } - protected function processCondition($conditions) + protected function buildCondition($conditions) { if (!is_array($conditions)) { return $conditions; @@ -529,7 +622,7 @@ class QueryBuilder extends \yii\base\Component if ($operator === 'OR' || $operator === 'AND') { $parts = array(); for ($i = 1; $i < $n; ++$i) { - $condition = $this->processCondition($conditions[$i]); + $condition = $this->buildCondition($conditions[$i]); if ($condition !== '') { $parts[] = '(' . $condition . ')'; } From b09b2da7d53c54027aaffcba1aeac533e7b4b2e7 Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Fri, 2 Sep 2011 00:17:45 -0400 Subject: [PATCH 07/10] w --- framework/YiiBase.php | 4 ++-- framework/db/dao/Command.php | 35 +++++++++++++++++------------------ framework/logging/Logger.php | 6 +++--- 3 files changed, 22 insertions(+), 23 deletions(-) diff --git a/framework/YiiBase.php b/framework/YiiBase.php index 6db3c9c..4a44e9d 100644 --- a/framework/YiiBase.php +++ b/framework/YiiBase.php @@ -397,9 +397,9 @@ class YiiBase * @param string $message the message to be logged. * @param string $category the category of the message. */ - public function warn($message, $category = 'application') + public function warning($message, $category = 'application') { - self::getLogger()->warn($message, $category); + self::getLogger()->warning($message, $category); } /** diff --git a/framework/db/dao/Command.php b/framework/db/dao/Command.php index 3fa762f..f1efef1 100644 --- a/framework/db/dao/Command.php +++ b/framework/db/dao/Command.php @@ -32,7 +32,7 @@ namespace yii\db\dao; * Starting from version 1.1.6, Command can also be used as a query builder * that builds a SQL statement from code fragments. For example, *
- * $user = Yii::app()->db->createCommand()
+ * $user = \Yii::app()->db->createCommand()
  *     ->select('username, password')
  *     ->from('tbl_user')
  *     ->where('id=:id', array(':id'=>1))
@@ -46,7 +46,6 @@ class Command extends \yii\base\Component
 {
 	/**
 	 * @var array the parameters (name=>value) to be bound to the current query.
-	 * @since 1.1.6
 	 */
 	public $params = array();
 
@@ -163,8 +162,8 @@ class Command extends \yii\base\Component
 				$this->pdoStatement = $this->connection->pdo->prepare($this->getSql());
 				$this->_paramLog = array();
 			}
-			catch(Exception $e) {
-				Yii::log('Error in preparing SQL: ' . $this->getSql(), CLogger::LEVEL_ERROR, 'system.db.Command');
+			catch(\Exception $e) {
+				\Yii::log('Error in preparing SQL: ' . $this->getSql(), CLogger::LEVEL_ERROR, 'system.db.Command');
                 $errorInfo = $e instanceof \PDOException ? $e->errorInfo : null;
 				throw new Exception('Unable to prepare the SQL statement: {error}',
 					array('{error}' => $e->getMessage())), (int)$e->getCode(), $errorInfo);
@@ -275,11 +274,11 @@ class Command extends \yii\base\Component
 		}
 		else
 			$par = '';
-		Yii::trace('Executing SQL: ' . $this->getSql() . $par, 'system.db.Command');
+		\Yii::trace('Executing SQL: ' . $this->getSql() . $par, 'system.db.Command');
 		try
 		{
 			if ($this->connection->enableProfiling)
-				Yii::beginProfile('system.db.Command.execute(' . $this->getSql() . ')', 'system.db.Command.execute');
+				\Yii::beginProfile('system.db.Command.execute(' . $this->getSql() . ')', 'system.db.Command.execute');
 
 			$this->prepare();
 			if ($params === array())
@@ -289,21 +288,21 @@ class Command extends \yii\base\Component
 			$n = $this->pdoStatement->rowCount();
 
 			if ($this->connection->enableProfiling)
-				Yii::endProfile('system.db.Command.execute(' . $this->getSql() . ')', 'system.db.Command.execute');
+				\Yii::endProfile('system.db.Command.execute(' . $this->getSql() . ')', 'system.db.Command.execute');
 
 			return $n;
 		}
 		catch(Exception $e)
 		{
 			if ($this->connection->enableProfiling)
-				Yii::endProfile('system.db.Command.execute(' . $this->getSql() . ')', 'system.db.Command.execute');
+				\Yii::endProfile('system.db.Command.execute(' . $this->getSql() . ')', 'system.db.Command.execute');
             $errorInfo = $e instanceof PDOException ? $e->errorInfo : null;
             $message = $e->getMessage();
-			Yii::log(Yii::t('yii', 'Command::execute() failed: {error}. The SQL statement executed was: {sql}.',
+			\Yii::log(\Yii::t('yii', 'Command::execute() failed: {error}. The SQL statement executed was: {sql}.',
 				array('{error}' => $message, '{sql}' => $this->getSql() . $par)), CLogger::LEVEL_ERROR, 'system.db.Command');
             if (YII_DEBUG)
             	$message .= '. The SQL statement executed was: ' . $this->getSql() . $par;
-			throw new CDbException(Yii::t('yii', 'Command failed to execute the SQL statement: {error}',
+			throw new CDbException(\Yii::t('yii', 'Command failed to execute the SQL statement: {error}',
 				array('{error}' => $message)), (int)$e->getCode(), $errorInfo);
 		}
 	}
@@ -430,19 +429,19 @@ class Command extends \yii\base\Component
 		else
 			$par = '';
 
-		Yii::trace('Querying SQL: ' . $this->getSql() . $par, 'system.db.Command');
+		\Yii::trace('Querying SQL: ' . $this->getSql() . $par, 'system.db.Command');
 
 		if ($this->connection->queryCachingCount > 0 && $method !== ''
 				&& $this->connection->queryCachingDuration > 0
 				&& $this->connection->queryCacheID !== false
-				&& ($cache = Yii::app()->getComponent($this->connection->queryCacheID)) !== null)
+				&& ($cache = \Yii::app()->getComponent($this->connection->queryCacheID)) !== null)
 		{
 			$this->connection->queryCachingCount--;
 			$cacheKey = 'yii:dbquery' . $this->connection->connectionString . ':' . $this->connection->username;
 			$cacheKey .= ':' . $this->getSql() . ':' . serialize(array_merge($this->_paramLog, $params));
 			if (($result = $cache->get($cacheKey)) !== false)
 			{
-				Yii::trace('Query result found in cache', 'system.db.Command');
+				\Yii::trace('Query result found in cache', 'system.db.Command');
 				return $result;
 			}
 		}
@@ -450,7 +449,7 @@ class Command extends \yii\base\Component
 		try
 		{
 			if ($this->connection->enableProfiling)
-				Yii::beginProfile('system.db.Command.query(' . $this->getSql() . $par . ')', 'system.db.Command.query');
+				\Yii::beginProfile('system.db.Command.query(' . $this->getSql() . $par . ')', 'system.db.Command.query');
 
 			$this->prepare();
 			if ($params === array())
@@ -470,7 +469,7 @@ class Command extends \yii\base\Component
 			}
 
 			if ($this->connection->enableProfiling)
-				Yii::endProfile('system.db.Command.query(' . $this->getSql() . $par . ')', 'system.db.Command.query');
+				\Yii::endProfile('system.db.Command.query(' . $this->getSql() . $par . ')', 'system.db.Command.query');
 
 			if (isset($cache, $cacheKey))
 				$cache->set($cacheKey, $result, $this->connection->queryCachingDuration, $this->connection->queryCachingDependency);
@@ -480,14 +479,14 @@ class Command extends \yii\base\Component
 		catch(Exception $e)
 		{
 			if ($this->connection->enableProfiling)
-				Yii::endProfile('system.db.Command.query(' . $this->getSql() . $par . ')', 'system.db.Command.query');
+				\Yii::endProfile('system.db.Command.query(' . $this->getSql() . $par . ')', 'system.db.Command.query');
             $errorInfo = $e instanceof PDOException ? $e->errorInfo : null;
             $message = $e->getMessage();
-			Yii::log(Yii::t('yii', 'Command::{method}() failed: {error}. The SQL statement executed was: {sql}.',
+			\Yii::log(\Yii::t('yii', 'Command::{method}() failed: {error}. The SQL statement executed was: {sql}.',
 				array('{method}' => $method, '{error}' => $message, '{sql}' => $this->getSql() . $par)), CLogger::LEVEL_ERROR, 'system.db.Command');
             if (YII_DEBUG)
             	$message .= '. The SQL statement executed was: ' . $this->getSql() . $par;
-			throw new CDbException(Yii::t('yii', 'Command failed to execute the SQL statement: {error}',
+			throw new CDbException(\Yii::t('yii', 'Command failed to execute the SQL statement: {error}',
 				array('{error}' => $message)), (int)$e->getCode(), $errorInfo);
 		}
 	}
diff --git a/framework/logging/Logger.php b/framework/logging/Logger.php
index 056a72e..88305c5 100644
--- a/framework/logging/Logger.php
+++ b/framework/logging/Logger.php
@@ -23,7 +23,7 @@ namespace yii\logging;
 class Logger extends \yii\base\Component
 {
 	const LEVEL_TRACE = 'trace';
-	const LEVEL_WARN = 'warn';
+	const LEVEL_WARNING = 'warning';
 	const LEVEL_ERROR = 'error';
 	const LEVEL_INFO = 'info';
 	const LEVEL_PROFILE = 'profile';
@@ -90,7 +90,7 @@ class Logger extends \yii\base\Component
 	 * @param string $message the message to be logged.
 	 * @param string $category the category of the message.
 	 */
-	public function warn($message, $category = 'application')
+	public function warning($message, $category = 'application')
 	{
 		$this->log($message, self::LEVEL_TRACE, $category);
 	}
@@ -136,7 +136,7 @@ class Logger extends \yii\base\Component
 	 * call stack information about application code will be appended to the message.
 	 * @param string $message the message to be logged.
 	 * @param string $level the level of the message. This must be one of the following:
-	 * 'trace', 'info', 'warn', 'error', 'profile'.
+	 * 'trace', 'info', 'warning', 'error', 'profile'.
 	 * @param string $category the category of the message.
 	 */
 	public function log($message, $level, $category)

From 6980dc55b2f40d05e7fe7849b572719504eef5de Mon Sep 17 00:00:00 2001
From: Qiang Xue 
Date: Fri, 2 Sep 2011 00:29:21 -0400
Subject: [PATCH 08/10] w

---
 framework/db/dao/QueryBuilder.php       | 10 +++++-----
 framework/db/dao/mysql/QueryBuilder.php | 32 +++++++++++++-------------------
 2 files changed, 18 insertions(+), 24 deletions(-)

diff --git a/framework/db/dao/QueryBuilder.php b/framework/db/dao/QueryBuilder.php
index f26118b..fbbc05e 100644
--- a/framework/db/dao/QueryBuilder.php
+++ b/framework/db/dao/QueryBuilder.php
@@ -21,7 +21,7 @@ class QueryBuilder extends \yii\base\Component
 	/**
 	 * @var array the abstract column types mapped to physical column types.
 	 */
-    public $columnTypes = array(
+    public $typeMap = array(
         'pk' => 'int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY',
         'string' => 'varchar(255)',
         'text' => 'text',
@@ -379,7 +379,7 @@ class QueryBuilder extends \yii\base\Component
 
 	/**
 	 * Converts an abstract column type into a physical column type.
-	 * The conversion is done using the type map specified in {@link columnTypes}.
+	 * The conversion is done using the type map specified in {@link typeMap}.
 	 * These abstract column types are supported (using MySQL as example to explain the corresponding
 	 * physical types):
 	 * 
    @@ -405,12 +405,12 @@ class QueryBuilder extends \yii\base\Component */ public function getColumnType($type) { - if (isset($this->columnTypes[$type])) { - return $this->columnTypes[$type]; + if (isset($this->typeMap[$type])) { + return $this->typeMap[$type]; } elseif (($pos = strpos($type, ' ')) !== false) { $t = substr($type, 0, $pos); - return (isset($this->columnTypes[$t]) ? $this->columnTypes[$t] : $t) . substr($type, $pos); + return (isset($this->typeMap[$t]) ? $this->typeMap[$t] : $t) . substr($type, $pos); } else { return $type; diff --git a/framework/db/dao/mysql/QueryBuilder.php b/framework/db/dao/mysql/QueryBuilder.php index 9ec5842..497f457 100644 --- a/framework/db/dao/mysql/QueryBuilder.php +++ b/framework/db/dao/mysql/QueryBuilder.php @@ -49,33 +49,27 @@ class QueryBuilder extends \yii\db\dao\QueryBuilder */ public function renameColumn($table, $name, $newName) { - $db = $this->getDbConnection(); - $row = $db->createCommand('SHOW CREATE TABLE ' . $db->quoteTableName($table))->queryRow(); + $quotedTable = $this->schema->quoteTableName($table); + $row = $this->connection->createCommand('SHOW CREATE TABLE ' . $quotedTable)->queryRow(); if ($row === false) throw new CDbException(Yii::t('yii', 'Unable to find "{column}" in table "{table}".', array('{column}' => $name, '{table}' => $table))); - if (isset($row['Create Table'])) + if (isset($row['Create Table'])) { $sql = $row['Create Table']; - else - { + } + else { $row = array_values($row); $sql = $row[1]; } - if (preg_match_all('/^\s*`(.*?)`\s+(.*?),?$/m', $sql, $matches)) - { - foreach ($matches[1] as $i => $c) - { - if ($c === $name) - { - return "ALTER TABLE " . $db->quoteTableName($table) - . " CHANGE " . $db->quoteColumnName($name) - . ' ' . $db->quoteColumnName($newName) . ' ' . $matches[2][$i]; + if (preg_match_all('/^\s*`(.*?)`\s+(.*?),?$/m', $sql, $matches)) { + foreach ($matches[1] as $i => $c) { + if ($c === $name) { + return "ALTER TABLE $quotedTable CHANGE " . $this->schema->quoteColumnName($name) + . ' ' . $this->schema->quoteColumnName($newName) . ' ' . $matches[2][$i]; } } } - // try to give back a SQL anyway - return "ALTER TABLE " . $db->quoteTableName($table) - . " CHANGE " . $db->quoteColumnName($name) . ' ' . $newName; + return "ALTER TABLE $quotedTable CHANGE " . $this->schema->quoteColumnName($name) . ' ' . $newName; } /** @@ -87,7 +81,7 @@ class QueryBuilder extends \yii\db\dao\QueryBuilder */ public function dropForeignKey($name, $table) { - return 'ALTER TABLE ' . $this->quoteTableName($table) - . ' DROP FOREIGN KEY ' . $this->quoteColumnName($name); + return 'ALTER TABLE ' . $this->schema->quoteTableName($table) + . ' DROP FOREIGN KEY ' . $this->schema->quoteColumnName($name); } } From 5f2f574381f3323f9a28a8eb6dd3aad7b2a4b3f0 Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Fri, 2 Sep 2011 23:47:18 -0400 Subject: [PATCH 09/10] w --- framework/db/Exception.php | 12 +- framework/db/dao/Command.php | 2 + framework/db/dao/Connection.php | 20 +- framework/db/dao/DataReader.php | 2 + framework/db/dao/QueryBuilder.php | 2 + framework/db/dao/Schema.php | 2 + framework/db/dao/Transaction.php | 2 + tests/unit/TestCase.php | 14 ++ tests/unit/bootstrap.php | 1 + tests/unit/data/config.php | 9 + tests/unit/data/mssql.sql | 306 +++++++++++++++++++++++++ tests/unit/data/mysql.sql | 150 ++++++++++++ tests/unit/data/postgres.sql | 165 +++++++++++++ tests/unit/data/sqlite.sql | 262 +++++++++++++++++++++ tests/unit/framework/base/ComponentTest.php | 2 +- tests/unit/framework/base/DictionaryTest.php | 20 +- tests/unit/framework/base/VectorTest.php | 18 +- tests/unit/framework/db/dao/ConnectionTest.php | 88 +++++++ 18 files changed, 1044 insertions(+), 33 deletions(-) create mode 100644 tests/unit/TestCase.php create mode 100644 tests/unit/data/config.php create mode 100644 tests/unit/data/mssql.sql create mode 100644 tests/unit/data/mysql.sql create mode 100644 tests/unit/data/postgres.sql create mode 100644 tests/unit/data/sqlite.sql create mode 100644 tests/unit/framework/db/dao/ConnectionTest.php diff --git a/framework/db/Exception.php b/framework/db/Exception.php index 242361f..17c5965 100644 --- a/framework/db/Exception.php +++ b/framework/db/Exception.php @@ -1,22 +1,22 @@ * @link http://www.yiiframework.com/ - * @copyright Copyright © 2008-2011 Yii Software LLC + * @copyright Copyright © 2008-2012 Yii Software LLC * @license http://www.yiiframework.com/license/ */ +namespace yii\db; + /** * CDbException represents an exception that is caused by some DB-related operations. * * @author Qiang Xue - * @version $Id: CDbException.php 2799 2011-01-01 19:31:13Z qiang.xue $ - * @package system.db - * @since 1.0 + * @since 2.0 */ -class CDbException extends CException +class Exception extends \yii\base\Exception { /** * @var mixed the error info provided by a PDO exception. This is the same as returned diff --git a/framework/db/dao/Command.php b/framework/db/dao/Command.php index f1efef1..5df6965 100644 --- a/framework/db/dao/Command.php +++ b/framework/db/dao/Command.php @@ -10,6 +10,8 @@ namespace yii\db\dao; +use yii\db\Exception; + /** * Command represents a SQL statement to be executed against a database. * diff --git a/framework/db/dao/Connection.php b/framework/db/dao/Connection.php index 2d4b8a7..b3e6965 100644 --- a/framework/db/dao/Connection.php +++ b/framework/db/dao/Connection.php @@ -10,6 +10,8 @@ namespace yii\db\dao; +use yii\db\Exception; + /** * Connection represents a connection to a database via [PDO](http://www.php.net/manual/en/ref.pdo.php). * @@ -275,7 +277,7 @@ class Connection extends \yii\base\ApplicationComponent */ public static function getAvailableDrivers() { - return PDO::getAvailableDrivers(); + return \PDO::getAvailableDrivers(); } /** @@ -485,7 +487,7 @@ class Connection extends \yii\base\ApplicationComponent */ public function quoteValue($str) { - if (is_int($str) || is_float($str) || is_bool($str)) { + if (!is_string($str)) { return $str; } @@ -504,9 +506,9 @@ class Connection extends \yii\base\ApplicationComponent * @param string $name table name * @return string the properly quoted table name */ - public function quoteTableName($name) + public function quoteTableName($name, $simple = false) { - return $this->getSchema()->quoteTableName($name); + return $simple ? $this->getSchema()->quoteSimpleTableName($name) : $this->getSchema()->quoteTableName($name); } /** @@ -515,9 +517,9 @@ class Connection extends \yii\base\ApplicationComponent * @param string $name column name * @return string the properly quoted column name */ - public function quoteColumnName($name) + public function quoteColumnName($name, $simple = false) { - return $this->getSchema()->quoteColumnName($name); + return $simple ? $this->getSchema()->quoteColumnName($name) : $this->getSchema()->quoteSimpleColumnName($name); } /** @@ -528,13 +530,13 @@ class Connection extends \yii\base\ApplicationComponent */ public function getPdoType($type) { - static $map = array( + static $typeMap = array( 'boolean' => \PDO::PARAM_BOOL, 'integer' => \PDO::PARAM_INT, 'string' => \PDO::PARAM_STR, 'NULL' => \PDO::PARAM_NULL, ); - return isset($map[$type]) ? $map[$type] : PDO::PARAM_STR; + return isset($typeMap[$type]) ? $typeMap[$type] : \PDO::PARAM_STR; } /** @@ -547,7 +549,7 @@ class Connection extends \yii\base\ApplicationComponent return strtolower(substr($this->dsn, 0, $pos)); } else { - return $this->getAttribute(\PDO::ATTR_DRIVER_NAME); + return strtolower($this->getAttribute(\PDO::ATTR_DRIVER_NAME)); } } diff --git a/framework/db/dao/DataReader.php b/framework/db/dao/DataReader.php index d2228d8..858b74e 100644 --- a/framework/db/dao/DataReader.php +++ b/framework/db/dao/DataReader.php @@ -10,6 +10,8 @@ namespace yii\db\dao; +use yii\db\Exception; + /** * DataReader represents a forward-only stream of rows from a query result set. * diff --git a/framework/db/dao/QueryBuilder.php b/framework/db/dao/QueryBuilder.php index fbbc05e..c769828 100644 --- a/framework/db/dao/QueryBuilder.php +++ b/framework/db/dao/QueryBuilder.php @@ -10,6 +10,8 @@ namespace yii\db\dao; +use yii\db\Exception; + /** * QueryBuilder builds a SQL statement based on the specification given as a [[Query]] object. * diff --git a/framework/db/dao/Schema.php b/framework/db/dao/Schema.php index a831a31..5632e86 100644 --- a/framework/db/dao/Schema.php +++ b/framework/db/dao/Schema.php @@ -10,6 +10,8 @@ namespace yii\db\dao; +use yii\db\Exception; + /** * Schema is the base class for retrieving metadata information. * diff --git a/framework/db/dao/Transaction.php b/framework/db/dao/Transaction.php index b9fd97e..87e4d89 100644 --- a/framework/db/dao/Transaction.php +++ b/framework/db/dao/Transaction.php @@ -10,6 +10,8 @@ namespace yii\db\dao; +use yii\db\Exception; + /** * Transaction represents a DB transaction. * diff --git a/tests/unit/TestCase.php b/tests/unit/TestCase.php new file mode 100644 index 0000000..c7826c8 --- /dev/null +++ b/tests/unit/TestCase.php @@ -0,0 +1,14 @@ +params === null) { + $this->params = require(__DIR__ . '/data/config.php'); + } + return isset($this->params[$name]) ? $this->params[$name] : null; + } +} \ No newline at end of file diff --git a/tests/unit/bootstrap.php b/tests/unit/bootstrap.php index daba4c9..5a8aeaa 100644 --- a/tests/unit/bootstrap.php +++ b/tests/unit/bootstrap.php @@ -7,3 +7,4 @@ $_SERVER['SCRIPT_NAME'] = '/' . __DIR__; $_SERVER['SCRIPT_FILENAME'] = __FILE__; require_once(__DIR__ . '/../../framework/yii.php'); +require_once(__DIR__ . '/TestCase.php'); \ No newline at end of file diff --git a/tests/unit/data/config.php b/tests/unit/data/config.php new file mode 100644 index 0000000..865edc6 --- /dev/null +++ b/tests/unit/data/config.php @@ -0,0 +1,9 @@ + array( + 'dsn' => 'mysql:host=127.0.0.1;dbname=yiitest', + 'username' => 'root', + 'password' => '', + ), +); \ No newline at end of file diff --git a/tests/unit/data/mssql.sql b/tests/unit/data/mssql.sql new file mode 100644 index 0000000..38967b2 --- /dev/null +++ b/tests/unit/data/mssql.sql @@ -0,0 +1,306 @@ +SET ANSI_NULLS ON +GO +SET QUOTED_IDENTIFIER ON +GO +IF NOT EXISTS (SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N'[dbo].[categories]') AND OBJECTPROPERTY(id, N'IsUserTable') = 1) +BEGIN +CREATE TABLE [dbo].[categories]( + [id] [int] IDENTITY(1,1) NOT NULL, + [name] [varchar](128) NOT NULL, + [parent_id] [int] NULL, + CONSTRAINT [PK_categories] PRIMARY KEY CLUSTERED +( + [id] ASC +) ON [PRIMARY] +) ON [PRIMARY] +END +GO +SET ANSI_NULLS ON +GO +SET QUOTED_IDENTIFIER ON +GO +IF NOT EXISTS (SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N'[dbo].[orders]') AND OBJECTPROPERTY(id, N'IsUserTable') = 1) +BEGIN +CREATE TABLE [dbo].[orders]( + [key1] [int] NOT NULL, + [key2] [int] NOT NULL, + [name] [varchar](128) NOT NULL, + CONSTRAINT [PK_orders] PRIMARY KEY CLUSTERED +( + [key1] ASC, + [key2] ASC +) ON [PRIMARY] +) ON [PRIMARY] +END +GO +SET ANSI_NULLS ON +GO +SET QUOTED_IDENTIFIER ON +GO +IF NOT EXISTS (SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N'[dbo].[types]') AND OBJECTPROPERTY(id, N'IsUserTable') = 1) +BEGIN +CREATE TABLE [dbo].[types]( + [int_col] [int] NOT NULL, + [int_col2] [int] NULL CONSTRAINT [DF_types_int_col2] DEFAULT (1), + [char_col] [char](100) NOT NULL, + [char_col2] [varchar](100) NULL CONSTRAINT [DF_types_char_col2] DEFAULT ('something'), + [char_col3] [text] NULL, + [float_col] [real] NOT NULL, + [float_col2] [float] NULL CONSTRAINT [DF_types_float_col2] DEFAULT (1.23), + [blob_col] [image] NULL, + [numeric_col] [numeric](5, 2) NULL CONSTRAINT [DF_types_numeric_col] DEFAULT (33.22), + [time] [datetime] NULL CONSTRAINT [DF_types_time] DEFAULT ('2002-01-01 00:00:00'), + [bool_col] [bit] NOT NULL, + [bool_col2] [bit] NOT NULL CONSTRAINT [DF_types_bool_col2] DEFAULT (1) +) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY] +END +GO +SET ANSI_NULLS ON +GO +SET QUOTED_IDENTIFIER ON +GO +IF NOT EXISTS (SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N'[dbo].[users]') AND OBJECTPROPERTY(id, N'IsUserTable') = 1) +BEGIN +CREATE TABLE [dbo].[users]( + [id] [int] IDENTITY(1,1) NOT NULL, + [username] [varchar](128) NOT NULL, + [password] [varchar](128) NOT NULL, + [email] [varchar](128) NOT NULL, + CONSTRAINT [PK_users] PRIMARY KEY CLUSTERED +( + [id] ASC +) ON [PRIMARY] +) ON [PRIMARY] +END +GO +SET ANSI_NULLS ON +GO +SET QUOTED_IDENTIFIER ON +GO +IF NOT EXISTS (SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N'[dbo].[post_category]') AND OBJECTPROPERTY(id, N'IsUserTable') = 1) +BEGIN +CREATE TABLE [dbo].[post_category]( + [category_id] [int] NOT NULL, + [post_id] [int] NOT NULL, + CONSTRAINT [PK_post_category] PRIMARY KEY CLUSTERED +( + [category_id] ASC, + [post_id] ASC +) ON [PRIMARY] +) ON [PRIMARY] +END +GO +SET ANSI_NULLS ON +GO +SET QUOTED_IDENTIFIER ON +GO +IF NOT EXISTS (SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N'[dbo].[items]') AND OBJECTPROPERTY(id, N'IsUserTable') = 1) +BEGIN +CREATE TABLE [dbo].[items]( + [id] [int] IDENTITY(1,1) NOT NULL, + [name] [varchar](128) NULL, + [col1] [int] NOT NULL, + [col2] [int] NOT NULL, + CONSTRAINT [PK_items] PRIMARY KEY CLUSTERED +( + [id] ASC +) ON [PRIMARY] +) ON [PRIMARY] +END +GO +SET ANSI_NULLS ON +GO +SET QUOTED_IDENTIFIER ON +GO +IF NOT EXISTS (SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N'[dbo].[comments]') AND OBJECTPROPERTY(id, N'IsUserTable') = 1) +BEGIN +CREATE TABLE [dbo].[comments]( + [id] [int] IDENTITY(1,1) NOT NULL, + [content] [text] NOT NULL, + [post_id] [int] NOT NULL, + [author_id] [int] NOT NULL, + CONSTRAINT [PK_comments] PRIMARY KEY CLUSTERED +( + [id] ASC +) ON [PRIMARY] +) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY] +END +GO +SET ANSI_NULLS ON +GO +SET QUOTED_IDENTIFIER ON +GO +IF NOT EXISTS (SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N'[dbo].[posts]') AND OBJECTPROPERTY(id, N'IsUserTable') = 1) +BEGIN +CREATE TABLE [dbo].[posts]( + [id] [int] IDENTITY(1,1) NOT NULL, + [title] [varchar](128) NOT NULL, + [create_time] [datetime] NOT NULL, + [author_id] [int] NOT NULL, + [content] [text] NULL, + CONSTRAINT [PK_posts] PRIMARY KEY CLUSTERED +( + [id] ASC +) ON [PRIMARY] +) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY] +END +GO +SET ANSI_NULLS ON +GO +SET QUOTED_IDENTIFIER ON +GO +IF NOT EXISTS (SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N'[dbo].[profiles]') AND OBJECTPROPERTY(id, N'IsUserTable') = 1) +BEGIN +CREATE TABLE [dbo].[profiles]( + [id] [int] IDENTITY(1,1) NOT NULL, + [first_name] [varchar](128) NOT NULL, + [last_name] [varchar](128) NOT NULL, + [user_id] [int] NOT NULL, + CONSTRAINT [PK_profiles] PRIMARY KEY CLUSTERED +( + [id] ASC +) ON [PRIMARY] +) ON [PRIMARY] +END +GO +IF NOT EXISTS (SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N'[dbo].[FK_categories_categories]') AND type = 'F') +ALTER TABLE [dbo].[categories] WITH CHECK ADD CONSTRAINT [FK_categories_categories] FOREIGN KEY([parent_id]) +REFERENCES [dbo].[categories] ([id]) +GO +IF NOT EXISTS (SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N'[dbo].[FK_post_category_categories]') AND type = 'F') +ALTER TABLE [dbo].[post_category] WITH CHECK ADD CONSTRAINT [FK_post_category_categories] FOREIGN KEY([category_id]) +REFERENCES [dbo].[categories] ([id]) +ON DELETE CASCADE +GO +IF NOT EXISTS (SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N'[dbo].[FK_post_category_posts]') AND type = 'F') +ALTER TABLE [dbo].[post_category] WITH NOCHECK ADD CONSTRAINT [FK_post_category_posts] FOREIGN KEY([post_id]) +REFERENCES [dbo].[posts] ([id]) +ON DELETE CASCADE +GO +ALTER TABLE [dbo].[post_category] CHECK CONSTRAINT [FK_post_category_posts] +GO +IF NOT EXISTS (SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N'[dbo].[FK_items_orders]') AND type = 'F') +ALTER TABLE [dbo].[items] WITH CHECK ADD CONSTRAINT [FK_items_orders] FOREIGN KEY([col1], [col2]) +REFERENCES [dbo].[orders] ([key1], [key2]) +ON DELETE CASCADE +GO +IF NOT EXISTS (SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N'[dbo].[FK_comments_users]') AND type = 'F') +ALTER TABLE [dbo].[comments] WITH NOCHECK ADD CONSTRAINT [FK_comments_users] FOREIGN KEY([author_id]) +REFERENCES [dbo].[users] ([id]) +ON DELETE CASCADE +GO +ALTER TABLE [dbo].[comments] CHECK CONSTRAINT [FK_comments_users] +GO +IF NOT EXISTS (SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N'[dbo].[FK_post_comment]') AND type = 'F') +ALTER TABLE [dbo].[comments] WITH NOCHECK ADD CONSTRAINT [FK_post_comment] FOREIGN KEY([post_id]) +REFERENCES [dbo].[posts] ([id]) +ON DELETE CASCADE +GO +ALTER TABLE [dbo].[comments] CHECK CONSTRAINT [FK_post_comment] +GO +IF NOT EXISTS (SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N'[dbo].[FK_posts_users]') AND type = 'F') +ALTER TABLE [dbo].[posts] WITH NOCHECK ADD CONSTRAINT [FK_posts_users] FOREIGN KEY([author_id]) +REFERENCES [dbo].[users] ([id]) +GO +ALTER TABLE [dbo].[posts] CHECK CONSTRAINT [FK_posts_users] +GO +IF NOT EXISTS (SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N'[dbo].[FK_profile_user]') AND type = 'F') +ALTER TABLE [dbo].[profiles] WITH NOCHECK ADD CONSTRAINT [FK_profile_user] FOREIGN KEY([user_id]) +REFERENCES [dbo].[users] ([id]) +ON DELETE CASCADE +GO +ALTER TABLE [dbo].[profiles] CHECK CONSTRAINT [FK_profile_user] + +INSERT INTO users (username, password, email) VALUES ('user1','pass1','email1') +GO +INSERT INTO users (username, password, email) VALUES ('user2','pass2','email2') +GO +INSERT INTO users (username, password, email) VALUES ('user3','pass3','email3') +GO + +INSERT INTO profiles (first_name, last_name, user_id) VALUES ('first 1','last 1',1) +GO +INSERT INTO profiles (first_name, last_name, user_id) VALUES ('first 2','last 2',2) +GO + +INSERT INTO posts (title, create_time, author_id, content) VALUES ('post 1','2000-01-01',1,'content 1') +GO +INSERT INTO posts (title, create_time, author_id, content) VALUES ('post 2','2000-01-02',2,'content 2') +GO +INSERT INTO posts (title, create_time, author_id, content) VALUES ('post 3','2000-01-03',2,'content 3') +GO +INSERT INTO posts (title, create_time, author_id, content) VALUES ('post 4','2000-01-04',2,'content 4') +GO +INSERT INTO posts (title, create_time, author_id, content) VALUES ('post 5','2000-01-05',3,'content 5') +GO + +INSERT INTO comments (content, post_id, author_id) VALUES ('comment 1',1, 2) +GO +INSERT INTO comments (content, post_id, author_id) VALUES ('comment 2',1, 2) +GO +INSERT INTO comments (content, post_id, author_id) VALUES ('comment 3',1, 2) +GO +INSERT INTO comments (content, post_id, author_id) VALUES ('comment 4',2, 2) +GO +INSERT INTO comments (content, post_id, author_id) VALUES ('comment 5',2, 2) +GO +INSERT INTO comments (content, post_id, author_id) VALUES ('comment 6',3, 2) +GO +INSERT INTO comments (content, post_id, author_id) VALUES ('comment 7',3, 2) +GO +INSERT INTO comments (content, post_id, author_id) VALUES ('comment 8',3, 2) +GO +INSERT INTO comments (content, post_id, author_id) VALUES ('comment 9',3, 2) +GO +INSERT INTO comments (content, post_id, author_id) VALUES ('comment 10',5, 3) +GO + +INSERT INTO categories (name, parent_id) VALUES ('cat 1',NULL) +GO +INSERT INTO categories (name, parent_id) VALUES ('cat 2',NULL) +GO +INSERT INTO categories (name, parent_id) VALUES ('cat 3',NULL) +GO +INSERT INTO categories (name, parent_id) VALUES ('cat 4',1) +GO +INSERT INTO categories (name, parent_id) VALUES ('cat 5',1) +GO +INSERT INTO categories (name, parent_id) VALUES ('cat 6',5) +GO +INSERT INTO categories (name, parent_id) VALUES ('cat 7',5) +GO + +INSERT INTO post_category (category_id, post_id) VALUES (1,1) +GO +INSERT INTO post_category (category_id, post_id) VALUES (2,1) +GO +INSERT INTO post_category (category_id, post_id) VALUES (3,1) +GO +INSERT INTO post_category (category_id, post_id) VALUES (4,2) +GO +INSERT INTO post_category (category_id, post_id) VALUES (1,2) +GO +INSERT INTO post_category (category_id, post_id) VALUES (1,3) +GO + + +INSERT INTO orders (key1,key2,name) VALUES (1,2,'order 12') +GO +INSERT INTO orders (key1,key2,name) VALUES (1,3,'order 13') +GO +INSERT INTO orders (key1,key2,name) VALUES (2,1,'order 21') +GO +INSERT INTO orders (key1,key2,name) VALUES (2,2,'order 22') +GO + + +INSERT INTO items (name,col1,col2) VALUES ('item 1',1,2) +GO +INSERT INTO items (name,col1,col2) VALUES ('item 2',1,2) +GO +INSERT INTO items (name,col1,col2) VALUES ('item 3',1,3) +GO +INSERT INTO items (name,col1,col2) VALUES ('item 4',2,2) +GO +INSERT INTO items (name,col1,col2) VALUES ('item 5',2,2) +GO diff --git a/tests/unit/data/mysql.sql b/tests/unit/data/mysql.sql new file mode 100644 index 0000000..057b1ad --- /dev/null +++ b/tests/unit/data/mysql.sql @@ -0,0 +1,150 @@ +/** + * This is the database schema for testing MySQL support of yii Active Record. + * To test this feature, you need to create a database named 'yii' on 'localhost' + * and create an account 'test/test' which owns this test database. + */ + +CREATE TABLE users +( + id INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT, + username VARCHAR(128) NOT NULL, + password VARCHAR(128) NOT NULL, + email VARCHAR(128) NOT NULL +) TYPE=INNODB; + +INSERT INTO users (username, password, email) VALUES ('user1','pass1','email1'); +INSERT INTO users (username, password, email) VALUES ('user2','pass2','email2'); +INSERT INTO users (username, password, email) VALUES ('user3','pass3','email3'); + +CREATE TABLE profiles +( + id INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT, + first_name VARCHAR(128) NOT NULL, + last_name VARCHAR(128) NOT NULL, + user_id INTEGER NOT NULL, + CONSTRAINT FK_profile_user FOREIGN KEY (user_id) + REFERENCES users (id) ON DELETE CASCADE ON UPDATE RESTRICT +) TYPE=INNODB; + +INSERT INTO profiles (first_name, last_name, user_id) VALUES ('first 1','last 1',1); +INSERT INTO profiles (first_name, last_name, user_id) VALUES ('first 2','last 2',2); + +CREATE TABLE posts +( + id INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT, + title VARCHAR(128) NOT NULL, + create_time TIMESTAMP NOT NULL, + author_id INTEGER NOT NULL, + content TEXT, + CONSTRAINT FK_post_author FOREIGN KEY (author_id) + REFERENCES users (id) ON DELETE CASCADE ON UPDATE RESTRICT +) TYPE=INNODB; + +INSERT INTO posts (title, create_time, author_id, content) VALUES ('post 1','2000-01-01',1,'content 1'); +INSERT INTO posts (title, create_time, author_id, content) VALUES ('post 2','2000-01-02',2,'content 2'); +INSERT INTO posts (title, create_time, author_id, content) VALUES ('post 3','2000-01-03',2,'content 3'); +INSERT INTO posts (title, create_time, author_id, content) VALUES ('post 4','2000-01-04',2,'content 4'); +INSERT INTO posts (title, create_time, author_id, content) VALUES ('post 5','2000-01-05',3,'content 5'); + +CREATE TABLE comments +( + id INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT, + content TEXT NOT NULL, + post_id INTEGER NOT NULL, + author_id INTEGER NOT NULL, + CONSTRAINT FK_post_comment FOREIGN KEY (post_id) + REFERENCES posts (id) ON DELETE CASCADE ON UPDATE RESTRICT, + CONSTRAINT FK_user_comment FOREIGN KEY (author_id) + REFERENCES users (id) ON DELETE CASCADE ON UPDATE RESTRICT +) TYPE=INNODB; + +INSERT INTO comments (content, post_id, author_id) VALUES ('comment 1',1, 2); +INSERT INTO comments (content, post_id, author_id) VALUES ('comment 2',1, 2); +INSERT INTO comments (content, post_id, author_id) VALUES ('comment 3',1, 2); +INSERT INTO comments (content, post_id, author_id) VALUES ('comment 4',2, 2); +INSERT INTO comments (content, post_id, author_id) VALUES ('comment 5',2, 2); +INSERT INTO comments (content, post_id, author_id) VALUES ('comment 6',3, 2); +INSERT INTO comments (content, post_id, author_id) VALUES ('comment 7',3, 2); +INSERT INTO comments (content, post_id, author_id) VALUES ('comment 8',3, 2); +INSERT INTO comments (content, post_id, author_id) VALUES ('comment 9',3, 2); +INSERT INTO comments (content, post_id, author_id) VALUES ('comment 10',5, 3); + +CREATE TABLE categories +( + id INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT, + name VARCHAR(128) NOT NULL, + parent_id INTEGER, + CONSTRAINT FK_category_category FOREIGN KEY (parent_id) + REFERENCES categories (id) ON DELETE CASCADE ON UPDATE RESTRICT +) TYPE=INNODB; + +INSERT INTO categories (name, parent_id) VALUES ('cat 1',NULL); +INSERT INTO categories (name, parent_id) VALUES ('cat 2',NULL); +INSERT INTO categories (name, parent_id) VALUES ('cat 3',NULL); +INSERT INTO categories (name, parent_id) VALUES ('cat 4',1); +INSERT INTO categories (name, parent_id) VALUES ('cat 5',1); +INSERT INTO categories (name, parent_id) VALUES ('cat 6',5); +INSERT INTO categories (name, parent_id) VALUES ('cat 7',5); + +CREATE TABLE post_category +( + category_id INTEGER NOT NULL, + post_id INTEGER NOT NULL, + PRIMARY KEY (category_id, post_id), + CONSTRAINT FK_post_category_post FOREIGN KEY (post_id) + REFERENCES posts (id) ON DELETE CASCADE ON UPDATE RESTRICT, + CONSTRAINT FK_post_category_category FOREIGN KEY (category_id) + REFERENCES categories (id) ON DELETE CASCADE ON UPDATE RESTRICT +) TYPE=INNODB; + +INSERT INTO post_category (category_id, post_id) VALUES (1,1); +INSERT INTO post_category (category_id, post_id) VALUES (2,1); +INSERT INTO post_category (category_id, post_id) VALUES (3,1); +INSERT INTO post_category (category_id, post_id) VALUES (4,2); +INSERT INTO post_category (category_id, post_id) VALUES (1,2); +INSERT INTO post_category (category_id, post_id) VALUES (1,3); + +CREATE TABLE orders +( + key1 INTEGER NOT NULL, + key2 INTEGER NOT NULL, + name VARCHAR(128), + PRIMARY KEY (key1, key2) +) TYPE=INNODB; + +INSERT INTO orders (key1,key2,name) VALUES (1,2,'order 12'); +INSERT INTO orders (key1,key2,name) VALUES (1,3,'order 13'); +INSERT INTO orders (key1,key2,name) VALUES (2,1,'order 21'); +INSERT INTO orders (key1,key2,name) VALUES (2,2,'order 22'); + +CREATE TABLE items +( + id INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT, + name VARCHAR(128), + col1 INTEGER NOT NULL, + col2 INTEGER NOT NULL, + CONSTRAINT FK_order_item FOREIGN KEY (col1,col2) + REFERENCES orders (key1,key2) ON DELETE CASCADE ON UPDATE RESTRICT +) TYPE=INNODB; + +INSERT INTO items (name,col1,col2) VALUES ('item 1',1,2); +INSERT INTO items (name,col1,col2) VALUES ('item 2',1,2); +INSERT INTO items (name,col1,col2) VALUES ('item 3',1,3); +INSERT INTO items (name,col1,col2) VALUES ('item 4',2,2); +INSERT INTO items (name,col1,col2) VALUES ('item 5',2,2); + +CREATE TABLE types +( + int_col INT NOT NULL, + int_col2 INTEGER DEFAULT 1, + char_col CHAR(100) NOT NULL, + char_col2 VARCHAR(100) DEFAULT 'something', + char_col3 TEXT, + float_col REAL(4,3) NOT NULL, + float_col2 DOUBLE DEFAULT 1.23, + blob_col BLOB, + numeric_col NUMERIC(5,2) DEFAULT 33.22, + time TIMESTAMP DEFAULT '2002-01-01', + bool_col BOOL NOT NULL, + bool_col2 BOOLEAN DEFAULT 1 +) TYPE=INNODB; \ No newline at end of file diff --git a/tests/unit/data/postgres.sql b/tests/unit/data/postgres.sql new file mode 100644 index 0000000..e46b284 --- /dev/null +++ b/tests/unit/data/postgres.sql @@ -0,0 +1,165 @@ +/** + * This is the database schema for testing PostgreSQL support of yii Active Record. + * To test this feature, you need to create a database named 'yii' on 'localhost' + * and create an account 'test/test' which owns this test database. + */ +CREATE SCHEMA test; + +CREATE TABLE test.users +( + id SERIAL NOT NULL PRIMARY KEY, + username VARCHAR(128) NOT NULL, + password VARCHAR(128) NOT NULL, + email VARCHAR(128) NOT NULL +); + +INSERT INTO test.users (username, password, email) VALUES ('user1','pass1','email1'); +INSERT INTO test.users (username, password, email) VALUES ('user2','pass2','email2'); +INSERT INTO test.users (username, password, email) VALUES ('user3','pass3','email3'); + +CREATE TABLE test.user_friends +( + id INTEGER NOT NULL, + friend INTEGER NOT NULL, + PRIMARY KEY (id, friend), + CONSTRAINT FK_user_id FOREIGN KEY (id) + REFERENCES test.users (id) ON DELETE CASCADE ON UPDATE RESTRICT, + CONSTRAINT FK_friend_id FOREIGN KEY (friend) + REFERENCES test.users (id) ON DELETE CASCADE ON UPDATE RESTRICT +); + +INSERT INTO test.user_friends VALUES (1,2); +INSERT INTO test.user_friends VALUES (1,3); +INSERT INTO test.user_friends VALUES (2,3); + +CREATE TABLE test.profiles +( + id SERIAL NOT NULL PRIMARY KEY, + first_name VARCHAR(128) NOT NULL, + last_name VARCHAR(128) NOT NULL, + user_id INTEGER NOT NULL, + CONSTRAINT FK_profile_user FOREIGN KEY (user_id) + REFERENCES test.users (id) ON DELETE CASCADE ON UPDATE RESTRICT +); + +INSERT INTO test.profiles (first_name, last_name, user_id) VALUES ('first 1','last 1',1); +INSERT INTO test.profiles (first_name, last_name, user_id) VALUES ('first 2','last 2',2); + +CREATE TABLE test.posts +( + id SERIAL NOT NULL PRIMARY KEY, + title VARCHAR(128) NOT NULL, + create_time TIMESTAMP NOT NULL, + author_id INTEGER NOT NULL, + content TEXT, + CONSTRAINT FK_post_author FOREIGN KEY (author_id) + REFERENCES test.users (id) ON DELETE CASCADE ON UPDATE RESTRICT +); + +INSERT INTO test.posts (title, create_time, author_id, content) VALUES ('post 1',TIMESTAMP '2004-10-19 10:23:54',1,'content 1'); +INSERT INTO test.posts (title, create_time, author_id, content) VALUES ('post 2',TIMESTAMP '2004-10-19 10:23:54',2,'content 2'); +INSERT INTO test.posts (title, create_time, author_id, content) VALUES ('post 3',TIMESTAMP '2004-10-19 10:23:54',2,'content 3'); +INSERT INTO test.posts (title, create_time, author_id, content) VALUES ('post 4',TIMESTAMP '2004-10-19 10:23:54',2,'content 4'); +INSERT INTO test.posts (title, create_time, author_id, content) VALUES ('post 5',TIMESTAMP '2004-10-19 10:23:54',3,'content 5'); + +CREATE TABLE test.comments +( + id SERIAL NOT NULL PRIMARY KEY, + content TEXT NOT NULL, + post_id INTEGER NOT NULL, + author_id INTEGER NOT NULL, + CONSTRAINT FK_post_comment FOREIGN KEY (post_id) + REFERENCES test.posts (id) ON DELETE CASCADE ON UPDATE RESTRICT, + CONSTRAINT FK_user_comment FOREIGN KEY (author_id) + REFERENCES test.users (id) ON DELETE CASCADE ON UPDATE RESTRICT +); + +INSERT INTO test.comments (content, post_id, author_id) VALUES ('comment 1',1, 2); +INSERT INTO test.comments (content, post_id, author_id) VALUES ('comment 2',1, 2); +INSERT INTO test.comments (content, post_id, author_id) VALUES ('comment 3',1, 2); +INSERT INTO test.comments (content, post_id, author_id) VALUES ('comment 4',2, 2); +INSERT INTO test.comments (content, post_id, author_id) VALUES ('comment 5',2, 2); +INSERT INTO test.comments (content, post_id, author_id) VALUES ('comment 6',3, 2); +INSERT INTO test.comments (content, post_id, author_id) VALUES ('comment 7',3, 2); +INSERT INTO test.comments (content, post_id, author_id) VALUES ('comment 8',3, 2); +INSERT INTO test.comments (content, post_id, author_id) VALUES ('comment 9',3, 2); +INSERT INTO test.comments (content, post_id, author_id) VALUES ('comment 10',5, 3); + +CREATE TABLE test.categories +( + id SERIAL NOT NULL PRIMARY KEY, + name VARCHAR(128) NOT NULL, + parent_id INTEGER, + CONSTRAINT FK_category_category FOREIGN KEY (parent_id) + REFERENCES test.categories (id) ON DELETE CASCADE ON UPDATE RESTRICT +); + +INSERT INTO test.categories (name, parent_id) VALUES ('cat 1',NULL); +INSERT INTO test.categories (name, parent_id) VALUES ('cat 2',NULL); +INSERT INTO test.categories (name, parent_id) VALUES ('cat 3',NULL); +INSERT INTO test.categories (name, parent_id) VALUES ('cat 4',1); +INSERT INTO test.categories (name, parent_id) VALUES ('cat 5',1); +INSERT INTO test.categories (name, parent_id) VALUES ('cat 6',5); +INSERT INTO test.categories (name, parent_id) VALUES ('cat 7',5); + +CREATE TABLE test.post_category +( + category_id INTEGER NOT NULL, + post_id INTEGER NOT NULL, + PRIMARY KEY (category_id, post_id), + CONSTRAINT FK_post_category_post FOREIGN KEY (post_id) + REFERENCES test.posts (id) ON DELETE CASCADE ON UPDATE RESTRICT, + CONSTRAINT FK_post_category_category FOREIGN KEY (category_id) + REFERENCES test.categories (id) ON DELETE CASCADE ON UPDATE RESTRICT +); + +INSERT INTO test.post_category (category_id, post_id) VALUES (1,1); +INSERT INTO test.post_category (category_id, post_id) VALUES (2,1); +INSERT INTO test.post_category (category_id, post_id) VALUES (3,1); +INSERT INTO test.post_category (category_id, post_id) VALUES (4,2); +INSERT INTO test.post_category (category_id, post_id) VALUES (1,2); +INSERT INTO test.post_category (category_id, post_id) VALUES (1,3); + +CREATE TABLE test.orders +( + key1 INTEGER NOT NULL, + key2 INTEGER NOT NULL, + name VARCHAR(128), + PRIMARY KEY (key1, key2) +); + +INSERT INTO test.orders (key1,key2,name) VALUES (1,2,'order 12'); +INSERT INTO test.orders (key1,key2,name) VALUES (1,3,'order 13'); +INSERT INTO test.orders (key1,key2,name) VALUES (2,1,'order 21'); +INSERT INTO test.orders (key1,key2,name) VALUES (2,2,'order 22'); + +CREATE TABLE test.items +( + id SERIAL NOT NULL PRIMARY KEY, + name VARCHAR(128), + col1 INTEGER NOT NULL, + col2 INTEGER NOT NULL, + CONSTRAINT FK_order_item FOREIGN KEY (col1,col2) + REFERENCES test.orders (key1,key2) ON DELETE CASCADE ON UPDATE RESTRICT +); + +INSERT INTO test.items (name,col1,col2) VALUES ('item 1',1,2); +INSERT INTO test.items (name,col1,col2) VALUES ('item 2',1,2); +INSERT INTO test.items (name,col1,col2) VALUES ('item 3',1,3); +INSERT INTO test.items (name,col1,col2) VALUES ('item 4',2,2); +INSERT INTO test.items (name,col1,col2) VALUES ('item 5',2,2); + +CREATE TABLE public.yii_types +( + int_col INT NOT NULL, + int_col2 INTEGER DEFAULT 1, + char_col CHAR(100) NOT NULL, + char_col2 VARCHAR(100) DEFAULT 'something', + char_col3 TEXT, + numeric_col NUMERIC(4,3) NOT NULL, + real_col REAL DEFAULT 1.23, + blob_col BYTEA, + time TIMESTAMP, + bool_col BOOL NOT NULL, + bool_col2 BOOLEAN DEFAULT TRUE +); \ No newline at end of file diff --git a/tests/unit/data/sqlite.sql b/tests/unit/data/sqlite.sql new file mode 100644 index 0000000..affeca9 --- /dev/null +++ b/tests/unit/data/sqlite.sql @@ -0,0 +1,262 @@ +CREATE TABLE users +( + id INTEGER NOT NULL PRIMARY KEY, + username VARCHAR(128) NOT NULL, + password VARCHAR(128) NOT NULL, + email VARCHAR(128) NOT NULL +); + +INSERT INTO users(id,username,password,email) VALUES (1,'user1','pass1','email1'); +INSERT INTO users(id,username,password,email) VALUES (2,'user2','pass2','email2'); +INSERT INTO users(id,username,password,email) VALUES (3,'user3','pass3','email3'); +INSERT INTO users(id,username,password,email) VALUES (4,'user4','pass4','email4'); + +CREATE TABLE groups +( + id INTEGER NOT NULL PRIMARY KEY, + name VARCHAR(128) NOT NULL +); + +INSERT INTO groups(id,name) VALUES (1,'group1'); +INSERT INTO groups(id,name) VALUES (2,'group2'); +INSERT INTO groups(id,name) VALUES (3,'group3'); +INSERT INTO groups(id,name) VALUES (4,'group4'); +INSERT INTO groups(id,name) VALUES (5,'group5'); +INSERT INTO groups(id,name) VALUES (6,'group6'); + +CREATE TABLE groups_descriptions +( + group_id INTEGER NOT NULL PRIMARY KEY, + name VARCHAR(128) NOT NULL +); + +INSERT INTO groups_descriptions(group_id,name) VALUES (1,'room1'); +INSERT INTO groups_descriptions(group_id,name) VALUES (2,'room2'); +INSERT INTO groups_descriptions(group_id,name) VALUES (3,'room3'); +INSERT INTO groups_descriptions(group_id,name) VALUES (4,'room4'); + +CREATE TABLE roles +( + user_id INTEGER NOT NULL, + group_id INTEGER NOT NULL, + name VARCHAR(128) NOT NULL, + PRIMARY KEY(user_id,group_id) +); + +INSERT INTO roles(user_id,group_id,name) VALUES (1,1,'dev'); +INSERT INTO roles(user_id,group_id,name) VALUES (1,2,'user'); +INSERT INTO roles(user_id,group_id,name) VALUES (2,1,'dev'); +INSERT INTO roles(user_id,group_id,name) VALUES (2,3,'user'); + +CREATE TABLE mentorships +( + teacher_id INTEGER NOT NULL, + student_id INTEGER NOT NULL, + progress VARCHAR(128) NOT NULL, + PRIMARY KEY(teacher_id,student_id) +); + +INSERT INTO mentorships(teacher_id,student_id,progress) VALUES (1,3,'good'); +INSERT INTO mentorships(teacher_id,student_id,progress) VALUES (2,4,'average'); + +CREATE TABLE profiles +( + id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + first_name VARCHAR(128) NOT NULL, + last_name VARCHAR(128) NOT NULL, + user_id INTEGER NOT NULL, + CONSTRAINT FK_profile_user FOREIGN KEY (user_id) + REFERENCES users (id) ON DELETE CASCADE ON UPDATE RESTRICT +); + +INSERT INTO profiles (first_name, last_name, user_id) VALUES ('first 1','last 1',1); +INSERT INTO profiles (first_name, last_name, user_id) VALUES ('first 2','last 2',2); + +CREATE TABLE posts +( + id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + title VARCHAR(128) NOT NULL, + create_time TIMESTAMP NOT NULL, + author_id INTEGER NOT NULL, + content TEXT, + CONSTRAINT FK_post_author FOREIGN KEY (author_id) + REFERENCES users (id) ON DELETE CASCADE ON UPDATE RESTRICT +); + +INSERT INTO posts (title, create_time, author_id, content) VALUES ('post 1',100000,1,'content 1'); +INSERT INTO posts (title, create_time, author_id, content) VALUES ('post 2',100001,2,'content 2'); +INSERT INTO posts (title, create_time, author_id, content) VALUES ('post 3',100002,2,'content 3'); +INSERT INTO posts (title, create_time, author_id, content) VALUES ('post 4',100003,2,'content 4'); +INSERT INTO posts (title, create_time, author_id, content) VALUES ('post 5',100004,3,'content 5'); + + +CREATE TABLE posts_nofk +( + id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + title VARCHAR(128) NOT NULL, + create_time TIMESTAMP NOT NULL, + author_id INTEGER NOT NULL, + content TEXT +); + +INSERT INTO posts_nofk (title, create_time, author_id, content) VALUES ('post 1',100000,1,'content 1'); +INSERT INTO posts_nofk (title, create_time, author_id, content) VALUES ('post 2',100001,2,'content 2'); +INSERT INTO posts_nofk (title, create_time, author_id, content) VALUES ('post 3',100002,2,'content 3'); +INSERT INTO posts_nofk (title, create_time, author_id, content) VALUES ('post 4',100003,2,'content 4'); +INSERT INTO posts_nofk (title, create_time, author_id, content) VALUES ('post 5',100004,3,'content 5'); + + +CREATE TABLE comments +( + id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + content TEXT NOT NULL, + post_id INTEGER NOT NULL, + author_id INTEGER NOT NULL, + CONSTRAINT FK_post_comment FOREIGN KEY (post_id) + REFERENCES posts (id) ON DELETE CASCADE ON UPDATE RESTRICT, + CONSTRAINT FK_user_comment FOREIGN KEY (author_id) + REFERENCES users (id) ON DELETE CASCADE ON UPDATE RESTRICT +); + +INSERT INTO comments (content, post_id, author_id) VALUES ('comment 1',1, 2); +INSERT INTO comments (content, post_id, author_id) VALUES ('comment 2',1, 2); +INSERT INTO comments (content, post_id, author_id) VALUES ('comment 3',1, 2); +INSERT INTO comments (content, post_id, author_id) VALUES ('comment 4',2, 2); +INSERT INTO comments (content, post_id, author_id) VALUES ('comment 5',2, 2); +INSERT INTO comments (content, post_id, author_id) VALUES ('comment 6',3, 2); +INSERT INTO comments (content, post_id, author_id) VALUES ('comment 7',3, 2); +INSERT INTO comments (content, post_id, author_id) VALUES ('comment 8',3, 2); +INSERT INTO comments (content, post_id, author_id) VALUES ('comment 9',3, 2); +INSERT INTO comments (content, post_id, author_id) VALUES ('comment 10',5, 3); + +CREATE TABLE categories +( + id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + name VARCHAR(128) NOT NULL, + parent_id INTEGER, + CONSTRAINT FK_category_category FOREIGN KEY (parent_id) + REFERENCES categories (id) ON DELETE CASCADE ON UPDATE RESTRICT +); + +INSERT INTO categories (name, parent_id) VALUES ('cat 1',NULL); +INSERT INTO categories (name, parent_id) VALUES ('cat 2',NULL); +INSERT INTO categories (name, parent_id) VALUES ('cat 3',NULL); +INSERT INTO categories (name, parent_id) VALUES ('cat 4',1); +INSERT INTO categories (name, parent_id) VALUES ('cat 5',1); +INSERT INTO categories (name, parent_id) VALUES ('cat 6',5); +INSERT INTO categories (name, parent_id) VALUES ('cat 7',5); + +CREATE TABLE post_category +( + category_id INTEGER NOT NULL, + post_id INTEGER NOT NULL, + PRIMARY KEY (category_id, post_id), + CONSTRAINT FK_post_category_post FOREIGN KEY (post_id) + REFERENCES posts (id) ON DELETE CASCADE ON UPDATE RESTRICT, + CONSTRAINT FK_post_category_category FOREIGN KEY (category_id) + REFERENCES categories (id) ON DELETE CASCADE ON UPDATE RESTRICT +); + +INSERT INTO post_category (category_id, post_id) VALUES (1,1); +INSERT INTO post_category (category_id, post_id) VALUES (2,1); +INSERT INTO post_category (category_id, post_id) VALUES (3,1); +INSERT INTO post_category (category_id, post_id) VALUES (4,2); +INSERT INTO post_category (category_id, post_id) VALUES (1,2); +INSERT INTO post_category (category_id, post_id) VALUES (1,3); + +CREATE TABLE orders +( + key1 INTEGER NOT NULL, + key2 INTEGER NOT NULL, + name VARCHAR(128), + PRIMARY KEY (key1, key2) +); + +INSERT INTO orders (key1,key2,name) VALUES (1,2,'order 12'); +INSERT INTO orders (key1,key2,name) VALUES (1,3,'order 13'); +INSERT INTO orders (key1,key2,name) VALUES (2,1,'order 21'); +INSERT INTO orders (key1,key2,name) VALUES (2,2,'order 22'); + +CREATE TABLE items +( + id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + name VARCHAR(128), + col1 INTEGER NOT NULL, + col2 INTEGER NOT NULL, + CONSTRAINT FK_order_item FOREIGN KEY (col1,col2) + REFERENCES orders (key1,key2) ON DELETE CASCADE ON UPDATE RESTRICT +); + +INSERT INTO items (name,col1,col2) VALUES ('item 1',1,2); +INSERT INTO items (name,col1,col2) VALUES ('item 2',1,2); +INSERT INTO items (name,col1,col2) VALUES ('item 3',1,3); +INSERT INTO items (name,col1,col2) VALUES ('item 4',2,2); +INSERT INTO items (name,col1,col2) VALUES ('item 5',2,2); + +CREATE TABLE types +( + int_col INT NOT NULL, + int_col2 INTEGER DEFAULT 1, + char_col CHAR(100) NOT NULL, + char_col2 VARCHAR(100) DEFAULT 'something', + char_col3 TEXT, + float_col REAL(4,3) NOT NULL, + float_col2 DOUBLE DEFAULT 1.23, + blob_col BLOB, + numeric_col NUMERIC(5,2) DEFAULT 33.22, + time TIMESTAMP DEFAULT 123, + bool_col BOOL NOT NULL, + bool_col2 BOOLEAN DEFAULT 1, + null_col INTEGER DEFAULT NULL +); + +CREATE TABLE Content +( + id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + class VARCHAR(128), + parentID INTEGER NOT NULL, + ownerID INTEGER NOT NULL, + title VARCHAR(100), + CONSTRAINT FK_content_user FOREIGN KEY (ownerID) + REFERENCES users (id) ON DELETE CASCADE ON UPDATE RESTRICT + CONSTRAINT FK_content_parent FOREIGN KEY (parentID) + REFERENCES Content (id) ON DELETE CASCADE ON UPDATE RESTRICT +); + +INSERT INTO Content (class,parentID,ownerID,title) VALUES ('Article',-1,1,'article 1'); +INSERT INTO Content (class,parentID,ownerID,title) VALUES ('Article',-1,2,'article 2'); +INSERT INTO Content (class,parentID,ownerID,title) VALUES ('Comment',1,1,'comment 1'); +INSERT INTO Content (class,parentID,ownerID,title) VALUES ('Article',-1,2,'article 3'); +INSERT INTO Content (class,parentID,ownerID,title) VALUES ('Comment',4,2,'comment 2'); +INSERT INTO Content (class,parentID,ownerID,title) VALUES ('Comment',4,1,'comment 3'); + +CREATE TABLE Article +( + id INTEGER NOT NULL PRIMARY KEY, + authorID INTEGER NOT NULL, + body TEXT, + CONSTRAINT FK_article_content FOREIGN KEY (id) + REFERENCES Content (id) ON DELETE CASCADE ON UPDATE RESTRICT + CONSTRAINT FK_article_author FOREIGN KEY (authorID) + REFERENCES users (id) ON DELETE CASCADE ON UPDATE RESTRICT +); + +INSERT INTO Article (id,authorID,body) VALUES (1,1,'content for article 1'); +INSERT INTO Article (id,authorID,body) VALUES (2,2,'content for article 2'); +INSERT INTO Article (id,authorID,body) VALUES (4,1,'content for article 3'); + +CREATE TABLE Comment +( + id INTEGER NOT NULL PRIMARY KEY, + authorID INTEGER NOT NULL, + body TEXT, + CONSTRAINT FK_comment_content FOREIGN KEY (id) + REFERENCES Content (id) ON DELETE CASCADE ON UPDATE RESTRICT + CONSTRAINT FK_article_author FOREIGN KEY (authorID) + REFERENCES users (id) ON DELETE CASCADE ON UPDATE RESTRICT +); + +INSERT INTO Comment (id,authorID,body) VALUES (3,1,'content for comment 1'); +INSERT INTO Comment (id,authorID,body) VALUES (5,1,'content for comment 2'); +INSERT INTO Comment (id,authorID,body) VALUES (6,1,'content for comment 3'); + diff --git a/tests/unit/framework/base/ComponentTest.php b/tests/unit/framework/base/ComponentTest.php index 53bc35d..6327b15 100644 --- a/tests/unit/framework/base/ComponentTest.php +++ b/tests/unit/framework/base/ComponentTest.php @@ -11,7 +11,7 @@ function globalEventHandler2($event) $event->handled=true; } -class ComponentTest extends \yii\test\TestCase +class ComponentTest extends TestCase { protected $component; diff --git a/tests/unit/framework/base/DictionaryTest.php b/tests/unit/framework/base/DictionaryTest.php index de3f7de..a33e643 100644 --- a/tests/unit/framework/base/DictionaryTest.php +++ b/tests/unit/framework/base/DictionaryTest.php @@ -1,18 +1,20 @@ dictionary=new \yii\base\Dictionary; + $this->dictionary=new Dictionary; $this->item1=new MapItem; $this->item2=new MapItem; $this->item3=new MapItem; @@ -31,9 +33,9 @@ class DictionaryTest extends \yii\test\TestCase public function testConstruct() { $a=array(1,2,'key3'=>3); - $dictionary=new \yii\base\Dictionary($a); + $dictionary=new Dictionary($a); $this->assertEquals(3,$dictionary->getCount()); - $dictionary2=new \yii\base\Dictionary($this->dictionary); + $dictionary2=new Dictionary($this->dictionary); $this->assertEquals(2,$dictionary2->getCount()); } @@ -97,8 +99,8 @@ class DictionaryTest extends \yii\test\TestCase $a=array('a'=>'v1','v2',array('2'),'c'=>array('3','c'=>'a')); $b=array('v22','a'=>'v11',array('2'),'c'=>array('c'=>'3','a')); $c=array('a'=>'v11','v2',array('2'),'c'=>array('3','c'=>'3','a'),'v22',array('2')); - $dictionary=new \yii\base\Dictionary($a); - $dictionary2=new \yii\base\Dictionary($b); + $dictionary=new Dictionary($a); + $dictionary2=new Dictionary($b); $dictionary->mergeWith($dictionary2); $this->assertTrue($dictionary->toArray()===$c); @@ -112,7 +114,7 @@ class DictionaryTest extends \yii\test\TestCase } public function testRecursiveMergeWithTraversable(){ - $dictionary = new \yii\base\Dictionary(); + $dictionary = new Dictionary(); $obj = new ArrayObject(array( 'k1' => $this->item1, 'k2' => $this->item2, @@ -176,13 +178,13 @@ class DictionaryTest extends \yii\test\TestCase public function testToArray() { - $dictionary = new \yii\base\Dictionary(array('key' => 'value')); + $dictionary = new Dictionary(array('key' => 'value')); $this->assertEquals(array('key' => 'value'), $dictionary->toArray()); } public function testIteratorCurrent() { - $dictionary = new \yii\base\Dictionary(array('key1' => 'value1', 'key2' => 'value2')); + $dictionary = new Dictionary(array('key1' => 'value1', 'key2' => 'value2')); $val = $dictionary->getIterator()->current(); $this->assertEquals('value1', $val); } diff --git a/tests/unit/framework/base/VectorTest.php b/tests/unit/framework/base/VectorTest.php index 65901d6..b8bf1a5 100644 --- a/tests/unit/framework/base/VectorTest.php +++ b/tests/unit/framework/base/VectorTest.php @@ -1,18 +1,20 @@ vector=new \yii\base\Vector; + $this->vector=new Vector; $this->item1=new ListItem; $this->item2=new ListItem; $this->item3=new ListItem; @@ -31,9 +33,9 @@ class VectorTest extends \yii\test\TestCase public function testConstruct() { $a=array(1,2,3); - $vector=new \yii\base\Vector($a); + $vector=new Vector($a); $this->assertEquals(3,$vector->getCount()); - $vector2=new \yii\base\Vector($this->vector); + $vector2=new Vector($this->vector); $this->assertEquals(2,$vector2->getCount()); } @@ -163,28 +165,28 @@ class VectorTest extends \yii\test\TestCase public function testOffsetSetAdd() { - $vector = new \yii\base\Vector(array(1, 2, 3)); + $vector = new Vector(array(1, 2, 3)); $vector->offsetSet(null, 4); $this->assertEquals(array(1, 2, 3, 4), $vector->toArray()); } public function testOffsetSetReplace() { - $vector = new \yii\base\Vector(array(1, 2, 3)); + $vector = new Vector(array(1, 2, 3)); $vector->offsetSet(1, 4); $this->assertEquals(array(1, 4, 3), $vector->toArray()); } public function testOffsetUnset() { - $vector = new \yii\base\Vector(array(1, 2, 3)); + $vector = new Vector(array(1, 2, 3)); $vector->offsetUnset(1); $this->assertEquals(array(1, 3), $vector->toArray()); } public function testIteratorCurrent() { - $vector = new \yii\base\Vector(array('value1', 'value2')); + $vector = new Vector(array('value1', 'value2')); $val = $vector->getIterator()->current(); $this->assertEquals('value1', $val); } diff --git a/tests/unit/framework/db/dao/ConnectionTest.php b/tests/unit/framework/db/dao/ConnectionTest.php new file mode 100644 index 0000000..d3dbbf8 --- /dev/null +++ b/tests/unit/framework/db/dao/ConnectionTest.php @@ -0,0 +1,88 @@ +markTestSkipped('PDO and MySQL extensions are required.'); + } + + function testConstruct() + { + $params = $this->getParam('mysql'); + $connection = new Connection($params['dsn'], $params['username'], $params['password']); + $this->assertEquals($params['dsn'], $connection->dsn); + $this->assertEquals($params['username'], $connection->username); + $this->assertEquals($params['password'], $connection->password); + } + + function testOpenClose() + { + $params = $this->getParam('mysql'); + $connection = new Connection($params['dsn'], $params['username'], $params['password']); + $this->assertFalse($connection->active); + $this->assertEquals(null, $connection->pdo); + + $connection->open(); + $this->assertTrue($connection->active); + $this->assertTrue($connection->pdo instanceof PDO); + + $connection->close(); + $this->assertFalse($connection->active); + $this->assertEquals(null, $connection->pdo); + + $connection = new Connection('unknown::memory:'); + $this->setExpectedException('yii\db\Exception'); + $connection->open(); + } + + /* + function testCreateCommand() + { + $sql='SELECT * FROM posts'; + $this->connection->active=true; + $this->connection->pdoInstance->exec(file_get_contents(dirname(__FILE__).'/data/sqlite.sql')); + $command=$this->connection->createCommand($sql); + $this->assertTrue($command instanceof CDbCommand); + } + + function testLastInsertID() + { + $this->connection->active=true; + $this->connection->pdoInstance->exec(file_get_contents(dirname(__FILE__).'/data/sqlite.sql')); + $sql='INSERT INTO posts(title,create_time,author_id) VALUES(\'test post\',11000,1)'; + $this->connection->createCommand($sql)->execute(); + $this->assertEquals($this->connection->lastInsertID,6); + } + + function testQuoteValue() + { + $this->connection->active=true; + $this->connection->pdoInstance->exec(file_get_contents(dirname(__FILE__).'/data/sqlite.sql')); + $str="this is 'my' name"; + $expectedStr="'this is ''my'' name'"; + $this->assertEquals($expectedStr,$this->connection->quoteValue($str)); + } + + function testColumnNameCase() + { + $this->connection->active=true; + $this->connection->pdoInstance->exec(file_get_contents(dirname(__FILE__).'/data/sqlite.sql')); + $this->assertEquals(PDO::CASE_NATURAL,$this->connection->ColumnCase); + $this->connection->columnCase=PDO::CASE_LOWER; + $this->assertEquals(PDO::CASE_LOWER,$this->connection->ColumnCase); + } + + function testNullConversion() + { + $this->connection->active=true; + $this->connection->pdoInstance->exec(file_get_contents(dirname(__FILE__).'/data/sqlite.sql')); + $this->assertEquals(PDO::NULL_NATURAL,$this->connection->NullConversion); + $this->connection->nullConversion=PDO::NULL_EMPTY_STRING; + $this->assertEquals(PDO::NULL_EMPTY_STRING,$this->connection->NullConversion); + } + */ +}