Commit 8ca17bbf by Carsten Brandt

Merge pull request #847 from yiisoft/cubrid

[WIP] Cubrid db support
parents 95c26746 23b858a2
......@@ -2,8 +2,14 @@ Database basics
===============
Yii has a database access layer built on top of PHP's [PDO](http://www.php.net/manual/en/ref.pdo.php). It provides
uniform API and solves some inconsistencies between different DBMS. By default Yii supports MySQL, SQLite, PostgreSQL,
Oracle and MSSQL.
uniform API and solves some inconsistencies between different DBMS. By default Yii supports the following DBMS:
- [MySQL](http://www.mysql.com/)
- [SQLite](http://sqlite.org/)
- [PostgreSQL](http://www.postgresql.org/)
- [CUBRID](http://www.cubrid.org/) (version 9.1.0 and higher).
- Oracle
- MSSQL
Configuration
......@@ -22,6 +28,7 @@ return array(
'dsn' => 'mysql:host=localhost;dbname=mydatabase', // MySQL, MariaDB
//'dsn' => 'sqlite:/path/to/database/file', // SQLite
//'dsn' => 'pgsql:host=localhost;port=5432;dbname=mydatabase', // PostgreSQL
//'dsn' => 'cubrid:dbname=demodb;host=localhost;port=33000', // CUBRID
//'dsn' => 'sqlsrv:Server=localhost;Database=mydatabase', // MS SQL Server, sqlsrv driver
//'dsn' => 'dblib:host=localhost;dbname=mydatabase', // MS SQL Server, dblib driver
//'dsn' => 'mssql:host=localhost;dbname=mydatabase', // MS SQL Server, mssql driver
......@@ -34,8 +41,10 @@ return array(
// ...
);
```
Please refer to the [PHP manual](http://www.php.net/manual/en/function.PDO-construct.php) for more details
on the format of the DSN string.
After the component is configured you can access it using the following syntax:
After the connection component is configured you can access it using the following syntax:
```php
$connection = \Yii::$app->db;
......
......@@ -181,7 +181,7 @@ class Command extends \yii\base\Component
{
$this->prepare();
if ($dataType === null) {
$this->pdoStatement->bindParam($name, $value, $this->getPdoType($value));
$this->pdoStatement->bindParam($name, $value, $this->db->schema->getPdoType($value));
} elseif ($length === null) {
$this->pdoStatement->bindParam($name, $value, $dataType);
} elseif ($driverOptions === null) {
......@@ -208,7 +208,7 @@ class Command extends \yii\base\Component
{
$this->prepare();
if ($dataType === null) {
$this->pdoStatement->bindValue($name, $value, $this->getPdoType($value));
$this->pdoStatement->bindValue($name, $value, $this->db->schema->getPdoType($value));
} else {
$this->pdoStatement->bindValue($name, $value, $dataType);
}
......@@ -236,7 +236,7 @@ class Command extends \yii\base\Component
$type = $value[1];
$value = $value[0];
} else {
$type = $this->getPdoType($value);
$type = $this->db->schema->getPdoType($value);
}
$this->pdoStatement->bindValue($name, $value, $type);
$this->_params[$name] = $value;
......@@ -246,25 +246,6 @@ class Command extends \yii\base\Component
}
/**
* Determines the PDO type for the give PHP data value.
* @param mixed $data the data whose PDO type is to be determined
* @return integer the PDO type
* @see http://www.php.net/manual/en/pdo.constants.php
*/
private function getPdoType($data)
{
static $typeMap = array(
'boolean' => \PDO::PARAM_BOOL,
'integer' => \PDO::PARAM_INT,
'string' => \PDO::PARAM_STR,
'resource' => \PDO::PARAM_LOB,
'NULL' => \PDO::PARAM_NULL,
);
$type = gettype($data);
return isset($typeMap[$type]) ? $typeMap[$type] : \PDO::PARAM_STR;
}
/**
* Executes the SQL statement.
* This method should only be used for executing non-query SQL statement, such as `INSERT`, `DELETE`, `UPDATE` SQLs.
* No result set will be returned.
......@@ -472,7 +453,7 @@ class Command extends \yii\base\Component
* ))->execute();
* ~~~
*
* Not that the values in each row must match the corresponding column names.
* Note that the values in each row must match the corresponding column names.
*
* @param string $table the table that new rows will be inserted into.
* @param array $columns the column names
......
......@@ -201,7 +201,7 @@ class Connection extends Component
public $queryCache = 'cache';
/**
* @var string the charset used for database connection. The property is only used
* for MySQL and PostgreSQL databases. Defaults to null, meaning using default charset
* for MySQL, PostgreSQL and CUBRID databases. Defaults to null, meaning using default charset
* as specified by the database.
*
* Note that if you're using GBK or BIG5 then it's highly recommended to
......@@ -244,6 +244,7 @@ class Connection extends Component
'oci' => 'yii\db\oci\Schema', // Oracle driver
'mssql' => 'yii\db\mssql\Schema', // older MSSQL driver on MS Windows hosts
'dblib' => 'yii\db\mssql\Schema', // dblib drivers on GNU/Linux (and maybe other OSes) hosts
'cubrid' => 'yii\db\cubrid\Schema', // CUBRID
);
/**
* @var Transaction the currently active transaction
......@@ -361,7 +362,7 @@ class Connection extends Component
if ($this->emulatePrepare !== null && constant('PDO::ATTR_EMULATE_PREPARES')) {
$this->pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, $this->emulatePrepare);
}
if ($this->charset !== null && in_array($this->getDriverName(), array('pgsql', 'mysql', 'mysqli'))) {
if ($this->charset !== null && in_array($this->getDriverName(), array('pgsql', 'mysql', 'mysqli', 'cubrid'))) {
$this->pdo->exec('SET NAMES ' . $this->pdo->quote($this->charset));
}
$this->trigger(self::EVENT_AFTER_OPEN);
......
......@@ -134,7 +134,7 @@ class QueryBuilder extends \yii\base\Object
* ))->execute();
* ~~~
*
* Not that the values in each row must match the corresponding column names.
* Note that the values in each row must match the corresponding column names.
*
* @param string $table the table that new rows will be inserted into.
* @param array $columns the column names
......
......@@ -376,4 +376,23 @@ abstract class Schema extends Object
return 'string';
}
}
/**
* Determines the PDO type for the give PHP data value.
* @param mixed $data the data whose PDO type is to be determined
* @return integer the PDO type
* @see http://www.php.net/manual/en/pdo.constants.php
*/
public function getPdoType($data)
{
static $typeMap = array( // php type => PDO type
'boolean' => \PDO::PARAM_BOOL,
'integer' => \PDO::PARAM_INT,
'string' => \PDO::PARAM_STR,
'resource' => \PDO::PARAM_LOB,
'NULL' => \PDO::PARAM_NULL,
);
$type = gettype($data);
return isset($typeMap[$type]) ? $typeMap[$type] : \PDO::PARAM_STR;
}
}
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\db\cubrid;
use yii\base\InvalidParamException;
/**
* QueryBuilder is the query builder for CUBRID databases (version 9.1.x and higher).
*
* @author Carsten Brandt <mail@cebe.cc>
* @since 2.0
*/
class QueryBuilder extends \yii\db\QueryBuilder
{
/**
* @var array mapping from abstract column types (keys) to physical column types (values).
*/
public $typeMap = array(
Schema::TYPE_PK => 'int NOT NULL AUTO_INCREMENT PRIMARY KEY',
Schema::TYPE_STRING => 'varchar(255)',
Schema::TYPE_TEXT => 'varchar',
Schema::TYPE_SMALLINT => 'smallint',
Schema::TYPE_INTEGER => 'int',
Schema::TYPE_BIGINT => 'bigint',
Schema::TYPE_FLOAT => 'float(7)',
Schema::TYPE_DECIMAL => 'decimal(10,0)',
Schema::TYPE_DATETIME => 'datetime',
Schema::TYPE_TIMESTAMP => 'timestamp',
Schema::TYPE_TIME => 'time',
Schema::TYPE_DATE => 'date',
Schema::TYPE_BINARY => 'blob',
Schema::TYPE_BOOLEAN => 'smallint',
Schema::TYPE_MONEY => 'decimal(19,4)',
);
/**
* Creates a SQL statement for resetting the sequence value of a table's primary key.
* The sequence will be reset such that the primary key of the next new row inserted
* will have the specified value or 1.
* @param string $tableName the name of the table 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.
* @return string the SQL statement for resetting sequence
* @throws InvalidParamException if the table does not exist or there is no sequence associated with the table.
*/
public function resetSequence($tableName, $value = null)
{
$table = $this->db->getTableSchema($tableName);
if ($table !== null && $table->sequenceName !== null) {
$tableName = $this->db->quoteTableName($tableName);
if ($value === null) {
$key = reset($table->primaryKey);
$value = (int)$this->db->createCommand("SELECT MAX(`$key`) FROM " . $this->db->schema->quoteTableName($tableName))->queryScalar() + 1;
} else {
$value = (int)$value;
}
return "ALTER TABLE " . $this->db->schema->quoteTableName($tableName) . " AUTO_INCREMENT=$value;";
} elseif ($table === null) {
throw new InvalidParamException("Table not found: $tableName");
} else {
throw new InvalidParamException("There is not sequence associated with table '$tableName'.");
}
}
/**
* Generates a batch INSERT SQL statement.
* For example,
*
* ~~~
* $connection->createCommand()->batchInsert('tbl_user', array('name', 'age'), array(
* array('Tom', 30),
* array('Jane', 20),
* array('Linda', 25),
* ))->execute();
* ~~~
*
* Note that the values in each row must match the corresponding column names.
*
* @param string $table the table that new rows will be inserted into.
* @param array $columns the column names
* @param array $rows the rows to be batch inserted into the table
* @return string the batch INSERT SQL statement
*/
public function batchInsert($table, $columns, $rows)
{
if (($tableSchema = $this->db->getTableSchema($table)) !== null) {
$columnSchemas = $tableSchema->columns;
} else {
$columnSchemas = array();
}
foreach ($columns as $i => $name) {
$columns[$i] = $this->db->quoteColumnName($name);
}
$values = array();
foreach ($rows as $row) {
$vs = array();
foreach ($row as $i => $value) {
if (!is_array($value) && isset($columnSchemas[$columns[$i]])) {
$value = $columnSchemas[$columns[$i]]->typecast($value);
}
$vs[] = is_string($value) ? $this->db->quoteValue($value) : $value;
}
$values[] = '(' . implode(', ', $vs) . ')';
}
return 'INSERT INTO ' . $this->db->quoteTableName($table)
. ' (' . implode(', ', $columns) . ') VALUES ' . implode(', ', $values);
}
}
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\db\cubrid;
use yii\base\NotSupportedException;
use yii\db\Expression;
use yii\db\TableSchema;
use yii\db\ColumnSchema;
/**
* Schema is the class for retrieving metadata from a CUBRID database (version 9.1.x and higher).
*
* @author Carsten Brandt <mail@cebe.cc>
* @since 2.0
*/
class Schema extends \yii\db\Schema
{
/**
* @var array mapping from physical column types (keys) to abstract column types (values)
* Please refer to [CUBRID manual](http://www.cubrid.org/manual/91/en/sql/datatype.html) for
* details on data types.
*/
public $typeMap = array(
// Numeric data types
'short' => self::TYPE_SMALLINT,
'smallint' => self::TYPE_SMALLINT,
'int' => self::TYPE_INTEGER,
'integer' => self::TYPE_INTEGER,
'bigint' => self::TYPE_BIGINT,
'numeric' => self::TYPE_DECIMAL,
'decimal' => self::TYPE_DECIMAL,
'float' => self::TYPE_FLOAT,
'real' => self::TYPE_FLOAT,
'double' => self::TYPE_FLOAT,
'double precision' => self::TYPE_FLOAT,
'monetary' => self::TYPE_MONEY,
// Date/Time data types
'date' => self::TYPE_DATE,
'time' => self::TYPE_TIME,
'timestamp' => self::TYPE_TIMESTAMP,
'datetime' => self::TYPE_DATETIME,
// String data types
'char' => self::TYPE_STRING,
'varchar' => self::TYPE_STRING,
'char varying' => self::TYPE_STRING,
'nchar' => self::TYPE_STRING,
'nchar varying' => self::TYPE_STRING,
'string' => self::TYPE_STRING,
// BLOB/CLOB data types
'blob' => self::TYPE_BINARY,
'clob' => self::TYPE_BINARY,
// Bit string data types
'bit' => self::TYPE_STRING,
'bit varying' => self::TYPE_STRING,
// Collection data types (considered strings for now, may add support for them later)
'set' => self::TYPE_STRING,
'multiset' => self::TYPE_STRING,
'list' => self::TYPE_STRING,
'sequence' => self::TYPE_STRING,
'enum' => self::TYPE_STRING,
);
/**
* Quotes a table name for use in a query.
* A simple table name has no schema prefix.
* @param string $name table name
* @return string the properly quoted table name
*/
public function quoteSimpleTableName($name)
{
return strpos($name, "`") !== false ? $name : "`" . $name . "`";
}
/**
* Quotes a column name for use in a query.
* A simple column name has no prefix.
* @param string $name column name
* @return string the properly quoted column name
*/
public function quoteSimpleColumnName($name)
{
return strpos($name, '`') !== false || $name === '*' ? $name : '`' . $name . '`';
}
/**
* Quotes a string value for use in a query.
* Note that if the parameter is not a string, it will be returned without change.
* @param string $str string to be quoted
* @return string the properly quoted string
* @see http://www.php.net/manual/en/function.PDO-quote.php
*/
public function quoteValue($str)
{
if (!is_string($str)) {
return $str;
}
$this->db->open();
// workaround for broken PDO::quote() implementation in CUBRID 9.1.0 http://jira.cubrid.org/browse/APIS-658
if (version_compare($this->db->pdo->getAttribute(\PDO::ATTR_CLIENT_VERSION), '9.1.0', '<=')) {
return "'" . addcslashes(str_replace("'", "''", $str), "\000\n\r\\\032") . "'";
} else {
return $this->db->pdo->quote($str);
}
}
/**
* Creates a query builder for the CUBRID database.
* @return QueryBuilder query builder instance
*/
public function createQueryBuilder()
{
return new QueryBuilder($this->db);
}
/**
* Loads the metadata for the specified table.
* @param string $name table name
* @return TableSchema driver dependent table metadata. Null if the table does not exist.
*/
protected function loadTableSchema($name)
{
$this->db->open();
$tableInfo = $this->db->pdo->cubrid_schema(\PDO::CUBRID_SCH_TABLE, $name);
if (isset($tableInfo[0]['NAME'])) {
$table = new TableSchema();
$table->name = $tableInfo[0]['NAME'];
$sql = 'SHOW FULL COLUMNS FROM ' . $this->quoteSimpleTableName($table->name);
$columns = $this->db->createCommand($sql)->queryAll();
foreach ($columns as $info) {
$column = $this->loadColumnSchema($info);
$table->columns[$column->name] = $column;
if ($column->isPrimaryKey) {
$table->primaryKey[] = $column->name;
if ($column->autoIncrement) {
$table->sequenceName = '';
}
}
}
$foreignKeys = $this->db->pdo->cubrid_schema(\PDO::CUBRID_SCH_IMPORTED_KEYS, $table->name);
foreach($foreignKeys as $key) {
if (isset($table->foreignKeys[$key['FK_NAME']])) {
$table->foreignKeys[$key['FK_NAME']][$key['FKCOLUMN_NAME']] = $key['PKCOLUMN_NAME'];
} else {
$table->foreignKeys[$key['FK_NAME']] = array(
$key['PKTABLE_NAME'],
$key['FKCOLUMN_NAME'] => $key['PKCOLUMN_NAME']
);
}
}
$table->foreignKeys = array_values($table->foreignKeys);
return $table;
} else {
return null;
}
}
/**
* Loads the column information into a [[ColumnSchema]] object.
* @param array $info column information
* @return ColumnSchema the column schema object
*/
protected function loadColumnSchema($info)
{
$column = new ColumnSchema();
$column->name = $info['Field'];
$column->allowNull = $info['Null'] === 'YES';
$column->isPrimaryKey = strpos($info['Key'], 'PRI') !== false;
$column->autoIncrement = stripos($info['Extra'], 'auto_increment') !== false;
$column->dbType = strtolower($info['Type']);
$column->unsigned = strpos($column->dbType, 'unsigned') !== false;
$column->type = self::TYPE_STRING;
if (preg_match('/^([\w ]+)(?:\(([^\)]+)\))?/', $column->dbType, $matches)) {
$type = $matches[1];
if (isset($this->typeMap[$type])) {
$column->type = $this->typeMap[$type];
}
if (!empty($matches[2])) {
if ($type === 'enum') {
$values = explode(',', $matches[2]);
foreach ($values as $i => $value) {
$values[$i] = trim($value, "'");
}
$column->enumValues = $values;
} else {
$values = explode(',', $matches[2]);
$column->size = $column->precision = (int)$values[0];
if (isset($values[1])) {
$column->scale = (int)$values[1];
}
}
}
}
$column->phpType = $this->getColumnPhpType($column);
if ($column->type === 'timestamp' && $info['Default'] === 'CURRENT_TIMESTAMP' ||
$column->type === 'datetime' && $info['Default'] === 'SYS_DATETIME' ||
$column->type === 'date' && $info['Default'] === 'SYS_DATE' ||
$column->type === 'time' && $info['Default'] === 'SYS_TIME') {
$column->defaultValue = new Expression($info['Default']);
} else {
$column->defaultValue = $column->typecast($info['Default']);
}
return $column;
}
/**
* Returns all table names in the database.
* @param string $schema the schema of the tables. Defaults to empty string, meaning the current or default schema.
* @return array all table names in the database. The names have NO schema name prefix.
*/
protected function findTableNames($schema = '')
{
$this->db->open();
$tables = $this->db->pdo->cubrid_schema(\PDO::CUBRID_SCH_TABLE);
$tableNames = array();
foreach($tables as $table) {
// do not list system tables
if ($table['TYPE'] != 0) {
$tableNames[] = $table['NAME'];
}
}
return $tableNames;
}
/**
* Determines the PDO type for the give PHP data value.
* @param mixed $data the data whose PDO type is to be determined
* @return integer the PDO type
* @see http://www.php.net/manual/en/pdo.constants.php
*/
public function getPdoType($data)
{
static $typeMap = array(
'boolean' => \PDO::PARAM_INT, // CUBRID PDO does not support PARAM_BOOL
'integer' => \PDO::PARAM_INT,
'string' => \PDO::PARAM_STR,
'resource' => \PDO::PARAM_LOB,
'NULL' => \PDO::PARAM_NULL,
);
$type = gettype($data);
return isset($typeMap[$type]) ? $typeMap[$type] : \PDO::PARAM_STR;
}
}
......@@ -152,7 +152,7 @@ class QueryBuilder extends \yii\db\QueryBuilder
* ))->execute();
* ~~~
*
* Not that the values in each row must match the corresponding column names.
* Note that the values in each row must match the corresponding column names.
*
* @param string $table the table that new rows will be inserted into.
* @param array $columns the column names
......
<?php
return array(
'databases' => array(
'cubrid' => array(
'dsn' => 'cubrid:dbname=demodb;host=localhost;port=33000',
'username' => 'dba',
'password' => '',
'fixture' => __DIR__ . '/cubrid.sql',
),
'mysql' => array(
'dsn' => 'mysql:host=127.0.0.1;dbname=yiitest',
'username' => 'travis',
......
/**
* This is the database schema for testing CUBRID support of Yii DAO and Active Record.
* The database setup in config.php is required to perform then relevant tests:
*/
DROP TABLE IF EXISTS tbl_composite_fk;
DROP TABLE IF EXISTS tbl_order_item;
DROP TABLE IF EXISTS tbl_item;
DROP TABLE IF EXISTS tbl_order;
DROP TABLE IF EXISTS tbl_category;
DROP TABLE IF EXISTS tbl_customer;
DROP TABLE IF EXISTS tbl_type;
DROP TABLE IF EXISTS tbl_constraints;
CREATE TABLE `tbl_constraints`
(
`id` integer not null,
`field1` varchar(255)
);
CREATE TABLE `tbl_customer` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`email` varchar(128) NOT NULL,
`name` varchar(128) NOT NULL,
`address` string,
`status` int (11) DEFAULT 0,
PRIMARY KEY (`id`)
);
CREATE TABLE `tbl_category` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(128) NOT NULL,
PRIMARY KEY (`id`)
);
CREATE TABLE `tbl_item` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(128) NOT NULL,
`category_id` int(11) NOT NULL,
PRIMARY KEY (`id`),
CONSTRAINT `FK_item_category_id` FOREIGN KEY (`category_id`) REFERENCES `tbl_category` (`id`) ON DELETE CASCADE
);
CREATE TABLE `tbl_order` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`customer_id` int(11) NOT NULL,
`create_time` int(11) NOT NULL,
`total` decimal(10,0) NOT NULL,
PRIMARY KEY (`id`),
CONSTRAINT `FK_order_customer_id` FOREIGN KEY (`customer_id`) REFERENCES `tbl_customer` (`id`) ON DELETE CASCADE
);
CREATE TABLE `tbl_order_item` (
`order_id` int(11) NOT NULL,
`item_id` int(11) NOT NULL,
`quantity` int(11) NOT NULL,
`subtotal` decimal(10,0) NOT NULL,
PRIMARY KEY (`order_id`,`item_id`),
CONSTRAINT `FK_order_item_order_id` FOREIGN KEY (`order_id`) REFERENCES `tbl_order` (`id`) ON DELETE CASCADE,
CONSTRAINT `FK_order_item_item_id` FOREIGN KEY (`item_id`) REFERENCES `tbl_item` (`id`) ON DELETE CASCADE
);
CREATE TABLE `tbl_type` (
`int_col` int(11) NOT NULL,
`int_col2` int(11) DEFAULT '1',
`char_col` char(100) NOT NULL,
`char_col2` varchar(100) DEFAULT 'something',
`char_col3` string,
`enum_col` enum('a', 'b'),
`float_col` double NOT NULL,
`float_col2` double DEFAULT '1.23',
`blob_col` blob,
`numeric_col` decimal(5,2) DEFAULT '33.22',
`time` timestamp NOT NULL DEFAULT '2002-01-01 00:00:00',
`bool_col` smallint NOT NULL,
`bool_col2` smallint DEFAULT 1
);
CREATE TABLE `tbl_composite_fk` (
`id` int(11) NOT NULL,
`order_id` int(11) NOT NULL,
`item_id` int(11) NOT NULL,
PRIMARY KEY (`id`),
CONSTRAINT `FK_composite_fk_order_item` FOREIGN KEY (`order_id`,`item_id`) REFERENCES `tbl_order_item` (`order_id`,`item_id`) ON DELETE CASCADE
);
INSERT INTO tbl_customer (email, name, address, status) VALUES ('user1@example.com', 'user1', 'address1', 1);
INSERT INTO tbl_customer (email, name, address, status) VALUES ('user2@example.com', 'user2', 'address2', 1);
INSERT INTO tbl_customer (email, name, address, status) VALUES ('user3@example.com', 'user3', 'address3', 2);
INSERT INTO tbl_category (name) VALUES ('Books');
INSERT INTO tbl_category (name) VALUES ('Movies');
INSERT INTO tbl_item (name, category_id) VALUES ('Agile Web Application Development with Yii1.1 and PHP5', 1);
INSERT INTO tbl_item (name, category_id) VALUES ('Yii 1.1 Application Development Cookbook', 1);
INSERT INTO tbl_item (name, category_id) VALUES ('Ice Age', 2);
INSERT INTO tbl_item (name, category_id) VALUES ('Toy Story', 2);
INSERT INTO tbl_item (name, category_id) VALUES ('Cars', 2);
INSERT INTO tbl_order (customer_id, create_time, total) VALUES (1, 1325282384, 110.0);
INSERT INTO tbl_order (customer_id, create_time, total) VALUES (2, 1325334482, 33.0);
INSERT INTO tbl_order (customer_id, create_time, total) VALUES (2, 1325502201, 40.0);
INSERT INTO tbl_order_item (order_id, item_id, quantity, subtotal) VALUES (1, 1, 1, 30.0);
INSERT INTO tbl_order_item (order_id, item_id, quantity, subtotal) VALUES (1, 2, 2, 40.0);
INSERT INTO tbl_order_item (order_id, item_id, quantity, subtotal) VALUES (2, 4, 1, 10.0);
INSERT INTO tbl_order_item (order_id, item_id, quantity, subtotal) VALUES (2, 5, 1, 15.0);
INSERT INTO tbl_order_item (order_id, item_id, quantity, subtotal) VALUES (2, 3, 1, 8.0);
INSERT INTO tbl_order_item (order_id, item_id, quantity, subtotal) VALUES (3, 2, 1, 40.0);
......@@ -3,6 +3,7 @@
* The database setup in config.php is required to perform then relevant tests:
*/
DROP TABLE IF EXISTS tbl_composite_fk CASCADE;
DROP TABLE IF EXISTS tbl_order_item CASCADE;
DROP TABLE IF EXISTS tbl_item CASCADE;
DROP TABLE IF EXISTS tbl_order CASCADE;
......@@ -62,6 +63,14 @@ CREATE TABLE `tbl_order_item` (
CONSTRAINT `FK_order_item_item_id` FOREIGN KEY (`item_id`) REFERENCES `tbl_item` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `tbl_composite_fk` (
`id` int(11) NOT NULL,
`order_id` int(11) NOT NULL,
`item_id` int(11) NOT NULL,
PRIMARY KEY (`id`),
CONSTRAINT `FK_composite_fk_order_item` FOREIGN KEY (`order_id`,`item_id`) REFERENCES `tbl_order_item` (`order_id`,`item_id`) ON DELETE CASCADE
);
CREATE TABLE `tbl_type` (
`int_col` int(11) NOT NULL,
`int_col2` int(11) DEFAULT '1',
......
......@@ -3,6 +3,7 @@
* The database setup in config.php is required to perform then relevant tests:
*/
DROP TABLE IF EXISTS tbl_composite_fk;
DROP TABLE IF EXISTS tbl_order_item;
DROP TABLE IF EXISTS tbl_item;
DROP TABLE IF EXISTS tbl_order;
......@@ -48,6 +49,14 @@ CREATE TABLE tbl_order_item (
PRIMARY KEY (order_id, item_id)
);
CREATE TABLE `tbl_composite_fk` (
`id` int(11) NOT NULL,
`order_id` int(11) NOT NULL,
`item_id` int(11) NOT NULL,
PRIMARY KEY (`id`),
CONSTRAINT `FK_composite_fk_order_item` FOREIGN KEY (`order_id`,`item_id`) REFERENCES `tbl_order_item` (`order_id`,`item_id`) ON DELETE CASCADE
);
CREATE TABLE tbl_type (
int_col INTEGER NOT NULL,
int_col2 INTEGER DEFAULT '1',
......
......@@ -9,12 +9,6 @@ use yii\db\DataReader;
class CommandTest extends DatabaseTestCase
{
protected function setUp()
{
parent::setUp();
$this->mockApplication();
}
public function testConstruct()
{
$db = $this->getConnection(false);
......
......@@ -6,12 +6,6 @@ use yii\db\Connection;
class ConnectionTest extends DatabaseTestCase
{
protected function setUp()
{
parent::setUp();
$this->mockApplication();
}
public function testConstruct()
{
$connection = $this->getConnection(false);
......
<?php
namespace yiiunit\framework\db;
use yii\db\Connection;
use yiiunit\TestCase as TestCase;
abstract class DatabaseTestCase extends TestCase
{
protected $database;
protected $driverName = 'mysql';
/**
* @var Connection
*/
protected $db;
protected function setUp()
{
parent::setUp();
$this->mockApplication();
$databases = $this->getParam('databases');
$this->database = $databases[$this->driverName];
$pdo_database = 'pdo_'.$this->driverName;
......@@ -20,6 +23,15 @@ abstract class DatabaseTestCase extends TestCase
if (!extension_loaded('pdo') || !extension_loaded($pdo_database)) {
$this->markTestSkipped('pdo and pdo_'.$pdo_database.' extension are required.');
}
$this->mockApplication();
}
protected function tearDown()
{
if ($this->db) {
$this->db->close();
}
$this->destroyApplication();
}
/**
......
......@@ -8,15 +8,10 @@ use yii\db\mysql\QueryBuilder as MysqlQueryBuilder;
use yii\db\sqlite\QueryBuilder as SqliteQueryBuilder;
use yii\db\mssql\QueryBuilder as MssqlQueryBuilder;
use yii\db\pgsql\QueryBuilder as PgsqlQueryBuilder;
use yii\db\cubrid\QueryBuilder as CubridQueryBuilder;
class QueryBuilderTest extends DatabaseTestCase
{
protected function setUp()
{
parent::setUp();
$this->mockApplication();
}
/**
* @throws \Exception
* @return QueryBuilder
......@@ -32,6 +27,8 @@ class QueryBuilderTest extends DatabaseTestCase
return new MssqlQueryBuilder($this->getConnection());
case 'pgsql':
return new PgsqlQueryBuilder($this->getConnection());
case 'cubrid':
return new CubridQueryBuilder($this->getConnection());
}
throw new \Exception('Test is not implemented for ' . $this->driverName);
}
......
......@@ -9,12 +9,6 @@ use yii\db\DataReader;
class QueryTest extends DatabaseTestCase
{
protected function setUp()
{
parent::setUp();
$this->mockApplication();
}
public function testSelect()
{
// default
......
<?php
namespace yiiunit\framework\db;
use yii\caching\FileCache;
use yii\db\Schema;
class SchemaTest extends DatabaseTestCase
{
public function testGetPDOType()
{
$values = array(
array(null, \PDO::PARAM_NULL),
array('', \PDO::PARAM_STR),
array('hello', \PDO::PARAM_STR),
array(0, \PDO::PARAM_INT),
array(1, \PDO::PARAM_INT),
array(1337, \PDO::PARAM_INT),
array(true, \PDO::PARAM_BOOL),
array(false, \PDO::PARAM_BOOL),
array($fp=fopen(__FILE__, 'rb'), \PDO::PARAM_LOB),
);
$schema = $this->getConnection()->schema;
foreach($values as $value) {
$this->assertEquals($value[1], $schema->getPdoType($value[0]));
}
fclose($fp);
}
public function testFindTableNames()
{
/** @var Schema $schema */
$schema = $this->getConnection()->schema;
$tables = $schema->getTableNames();
$this->assertTrue(in_array('tbl_customer', $tables));
$this->assertTrue(in_array('tbl_category', $tables));
$this->assertTrue(in_array('tbl_item', $tables));
$this->assertTrue(in_array('tbl_order', $tables));
$this->assertTrue(in_array('tbl_order_item', $tables));
$this->assertTrue(in_array('tbl_type', $tables));
}
public function testGetTableSchemas()
{
/** @var Schema $schema */
$schema = $this->getConnection()->schema;
$tables = $schema->getTableSchemas();
$this->assertEquals(count($schema->getTableNames()), count($tables));
foreach($tables as $table) {
$this->assertInstanceOf('yii\db\TableSchema', $table);
}
}
public function testGetNonExistingTableSchema()
{
$this->assertNull($this->getConnection()->schema->getTableSchema('nonexisting_table'));
}
public function testSchemaCache()
{
/** @var Schema $schema */
$schema = $this->getConnection()->schema;
$schema->db->enableSchemaCache = true;
$schema->db->schemaCache = new FileCache();
$noCacheTable = $schema->getTableSchema('tbl_type', true);
$cachedTable = $schema->getTableSchema('tbl_type', true);
$this->assertEquals($noCacheTable, $cachedTable);
}
public function testCompositeFk()
{
/** @var Schema $schema */
$schema = $this->getConnection()->schema;
$table = $schema->getTableSchema('tbl_composite_fk');
$this->assertCount(1, $table->foreignKeys);
$this->assertTrue(isset($table->foreignKeys[0]));
$this->assertEquals('tbl_order_item', $table->foreignKeys[0][0]);
$this->assertEquals('order_id', $table->foreignKeys[0]['order_id']);
$this->assertEquals('item_id', $table->foreignKeys[0]['item_id']);
}
}
<?php
namespace yiiunit\framework\db\cubrid;
use yiiunit\framework\db\ActiveRecordTest;
class CubridActiveRecordTest extends ActiveRecordTest
{
public $driverName = 'cubrid';
}
<?php
namespace yiiunit\framework\db\cubrid;
use yiiunit\framework\db\CommandTest;
class CubridCommandTest extends CommandTest
{
public $driverName = 'cubrid';
public function testBindParamValue()
{
$db = $this->getConnection();
// bindParam
$sql = 'INSERT INTO tbl_customer(email, name, address) VALUES (:email, :name, :address)';
$command = $db->createCommand($sql);
$email = 'user4@example.com';
$name = 'user4';
$address = 'address4';
$command->bindParam(':email', $email);
$command->bindParam(':name', $name);
$command->bindParam(':address', $address);
$command->execute();
$sql = 'SELECT name FROM tbl_customer WHERE email=:email';
$command = $db->createCommand($sql);
$command->bindParam(':email', $email);
$this->assertEquals($name, $command->queryScalar());
$sql = "INSERT INTO tbl_type (int_col, char_col, char_col2, enum_col, float_col, blob_col, numeric_col, bool_col, bool_col2) VALUES (:int_col, '', :char_col, :enum_col, :float_col, CHAR_TO_BLOB(:blob_col), :numeric_col, :bool_col, :bool_col2)";
$command = $db->createCommand($sql);
$intCol = 123;
$charCol = 'abc';
$enumCol = 'a';
$floatCol = 1.23;
$blobCol = "\x10\x11\x12";
$numericCol = '1.23';
$boolCol = false;
$boolCol2 = true;
$command->bindParam(':int_col', $intCol);
$command->bindParam(':char_col', $charCol);
$command->bindParam(':enum_col', $enumCol);
$command->bindParam(':float_col', $floatCol);
$command->bindParam(':blob_col', $blobCol);
$command->bindParam(':numeric_col', $numericCol);
$command->bindParam(':bool_col', $boolCol);
$command->bindParam(':bool_col2', $boolCol2);
$this->assertEquals(1, $command->execute());
$sql = 'SELECT * FROM tbl_type';
$row = $db->createCommand($sql)->queryOne();
$this->assertEquals($intCol, $row['int_col']);
$this->assertEquals($enumCol, $row['enum_col']);
$this->assertEquals($charCol, $row['char_col2']);
$this->assertEquals($floatCol, $row['float_col']);
$this->assertEquals($blobCol, fread($row['blob_col'], 3));
$this->assertEquals($numericCol, $row['numeric_col']);
$this->assertEquals($boolCol, $row['bool_col']);
$this->assertEquals($boolCol2, $row['bool_col2']);
// bindValue
$sql = 'INSERT INTO tbl_customer(email, name, address) VALUES (:email, \'user5\', \'address5\')';
$command = $db->createCommand($sql);
$command->bindValue(':email', 'user5@example.com');
$command->execute();
$sql = 'SELECT email FROM tbl_customer WHERE name=:name';
$command = $db->createCommand($sql);
$command->bindValue(':name', 'user5');
$this->assertEquals('user5@example.com', $command->queryScalar());
}
}
<?php
namespace yiiunit\framework\db\cubrid;
use yiiunit\framework\db\ConnectionTest;
class CubridConnectionTest extends ConnectionTest
{
public $driverName = 'cubrid';
public function testQuoteValue()
{
$connection = $this->getConnection(false);
$this->assertEquals(123, $connection->quoteValue(123));
$this->assertEquals("'string'", $connection->quoteValue('string'));
$this->assertEquals("'It''s interesting'", $connection->quoteValue("It's interesting"));
}
}
<?php
namespace yiiunit\framework\db\cubrid;
use yii\base\NotSupportedException;
use yii\db\sqlite\Schema;
use yiiunit\framework\db\QueryBuilderTest;
class CubridQueryBuilderTest extends QueryBuilderTest
{
public $driverName = 'cubrid';
/**
* this is not used as a dataprovider for testGetColumnType to speed up the test
* when used as dataprovider every single line will cause a reconnect with the database which is not needed here
*/
public function columnTypes()
{
return array(
array(Schema::TYPE_PK, 'int NOT NULL AUTO_INCREMENT PRIMARY KEY'),
array(Schema::TYPE_PK . '(8)', 'int NOT NULL AUTO_INCREMENT PRIMARY KEY'),
array(Schema::TYPE_PK . ' CHECK (value > 5)', 'int NOT NULL AUTO_INCREMENT PRIMARY KEY CHECK (value > 5)'),
array(Schema::TYPE_PK . '(8) CHECK (value > 5)', 'int NOT NULL AUTO_INCREMENT PRIMARY KEY CHECK (value > 5)'),
array(Schema::TYPE_STRING, 'varchar(255)'),
array(Schema::TYPE_STRING . '(32)', 'varchar(32)'),
array(Schema::TYPE_STRING . ' CHECK (value LIKE "test%")', 'varchar(255) CHECK (value LIKE "test%")'),
array(Schema::TYPE_STRING . '(32) CHECK (value LIKE "test%")', 'varchar(32) CHECK (value LIKE "test%")'),
array(Schema::TYPE_STRING . ' NOT NULL', 'varchar(255) NOT NULL'),
array(Schema::TYPE_TEXT, 'varchar'),
array(Schema::TYPE_TEXT . '(255)', 'varchar'),
array(Schema::TYPE_TEXT . ' CHECK (value LIKE "test%")', 'varchar CHECK (value LIKE "test%")'),
array(Schema::TYPE_TEXT . '(255) CHECK (value LIKE "test%")', 'varchar CHECK (value LIKE "test%")'),
array(Schema::TYPE_TEXT . ' NOT NULL', 'varchar NOT NULL'),
array(Schema::TYPE_TEXT . '(255) NOT NULL', 'varchar NOT NULL'),
array(Schema::TYPE_SMALLINT, 'smallint'),
array(Schema::TYPE_SMALLINT . '(8)', 'smallint'),
array(Schema::TYPE_INTEGER, 'int'),
array(Schema::TYPE_INTEGER . '(8)', 'int'),
array(Schema::TYPE_INTEGER . ' CHECK (value > 5)', 'int CHECK (value > 5)'),
array(Schema::TYPE_INTEGER . '(8) CHECK (value > 5)', 'int CHECK (value > 5)'),
array(Schema::TYPE_INTEGER . ' NOT NULL', 'int NOT NULL'),
array(Schema::TYPE_BIGINT, 'bigint'),
array(Schema::TYPE_BIGINT . '(8)', 'bigint'),
array(Schema::TYPE_BIGINT . ' CHECK (value > 5)', 'bigint CHECK (value > 5)'),
array(Schema::TYPE_BIGINT . '(8) CHECK (value > 5)', 'bigint CHECK (value > 5)'),
array(Schema::TYPE_BIGINT . ' NOT NULL', 'bigint NOT NULL'),
array(Schema::TYPE_FLOAT, 'float(7)'),
array(Schema::TYPE_FLOAT . '(16)', 'float(16)'),
array(Schema::TYPE_FLOAT . ' CHECK (value > 5.6)', 'float(7) CHECK (value > 5.6)'),
array(Schema::TYPE_FLOAT . '(16) CHECK (value > 5.6)', 'float(16) CHECK (value > 5.6)'),
array(Schema::TYPE_FLOAT . ' NOT NULL', 'float(7) NOT NULL'),
array(Schema::TYPE_DECIMAL, 'decimal(10,0)'),
array(Schema::TYPE_DECIMAL . '(12,4)', 'decimal(12,4)'),
array(Schema::TYPE_DECIMAL . ' CHECK (value > 5.6)', 'decimal(10,0) CHECK (value > 5.6)'),
array(Schema::TYPE_DECIMAL . '(12,4) CHECK (value > 5.6)', 'decimal(12,4) CHECK (value > 5.6)'),
array(Schema::TYPE_DECIMAL . ' NOT NULL', 'decimal(10,0) NOT NULL'),
array(Schema::TYPE_DATETIME, 'datetime'),
array(Schema::TYPE_DATETIME . " CHECK(value BETWEEN '2011-01-01' AND '2013-01-01')", "datetime CHECK(value BETWEEN '2011-01-01' AND '2013-01-01')"),
array(Schema::TYPE_DATETIME . ' NOT NULL', 'datetime NOT NULL'),
array(Schema::TYPE_TIMESTAMP, 'timestamp'),
array(Schema::TYPE_TIMESTAMP . " CHECK(value BETWEEN '2011-01-01' AND '2013-01-01')", "timestamp CHECK(value BETWEEN '2011-01-01' AND '2013-01-01')"),
array(Schema::TYPE_TIMESTAMP . ' NOT NULL', 'timestamp NOT NULL'),
array(Schema::TYPE_TIME, 'time'),
array(Schema::TYPE_TIME . " CHECK(value BETWEEN '12:00:00' AND '13:01:01')", "time CHECK(value BETWEEN '12:00:00' AND '13:01:01')"),
array(Schema::TYPE_TIME . ' NOT NULL', 'time NOT NULL'),
array(Schema::TYPE_DATE, 'date'),
array(Schema::TYPE_DATE . " CHECK(value BETWEEN '2011-01-01' AND '2013-01-01')", "date CHECK(value BETWEEN '2011-01-01' AND '2013-01-01')"),
array(Schema::TYPE_DATE . ' NOT NULL', 'date NOT NULL'),
array(Schema::TYPE_BINARY, 'blob'),
array(Schema::TYPE_BOOLEAN, 'smallint'),
array(Schema::TYPE_BOOLEAN . ' NOT NULL DEFAULT 1', 'smallint NOT NULL DEFAULT 1'),
array(Schema::TYPE_MONEY, 'decimal(19,4)'),
array(Schema::TYPE_MONEY . '(16,2)', 'decimal(16,2)'),
array(Schema::TYPE_MONEY . ' CHECK (value > 0.0)', 'decimal(19,4) CHECK (value > 0.0)'),
array(Schema::TYPE_MONEY . '(16,2) CHECK (value > 0.0)', 'decimal(16,2) CHECK (value > 0.0)'),
array(Schema::TYPE_MONEY . ' NOT NULL', 'decimal(19,4) NOT NULL'),
);
}
}
<?php
namespace yiiunit\framework\db\cubrid;
use yiiunit\framework\db\QueryTest;
class CubridQueryTest extends QueryTest
{
public $driverName = 'cubrid';
}
<?php
namespace yiiunit\framework\db\cubrid;
use yiiunit\framework\db\SchemaTest;
class CubridSchemaTest extends SchemaTest
{
public $driverName = 'cubrid';
public function testGetPDOType()
{
$values = array(
null => \PDO::PARAM_NULL,
'' => \PDO::PARAM_STR,
'hello' => \PDO::PARAM_STR,
0 => \PDO::PARAM_INT,
1 => \PDO::PARAM_INT,
1337 => \PDO::PARAM_INT,
true => \PDO::PARAM_INT, // CUBRID PDO does not support PARAM_BOOL
false => \PDO::PARAM_INT, // CUBRID PDO does not support PARAM_BOOL
);
$schema = $this->getConnection()->schema;
foreach($values as $value => $type) {
$this->assertEquals($type, $schema->getPdoType($value));
}
$this->assertEquals(\PDO::PARAM_LOB, $schema->getPdoType($fp=fopen(__FILE__, 'rb')));
fclose($fp);
}
}
......@@ -6,9 +6,5 @@ use yiiunit\framework\db\ActiveRecordTest;
class MssqlActiveRecordTest extends ActiveRecordTest
{
protected function setUp()
{
$this->driverName = 'sqlsrv';
parent::setUp();
}
protected $driverName = 'sqlsrv';
}
......@@ -6,11 +6,7 @@ use yiiunit\framework\db\CommandTest;
class MssqlCommandTest extends CommandTest
{
public function setUp()
{
$this->driverName = 'sqlsrv';
parent::setUp();
}
protected $driverName = 'sqlsrv';
public function testAutoQuoting()
{
......
......@@ -6,11 +6,7 @@ use yiiunit\framework\db\ConnectionTest;
class MssqlConnectionTest extends ConnectionTest
{
public function setUp()
{
$this->driverName = 'sqlsrv';
parent::setUp();
}
protected $driverName = 'sqlsrv';
public function testQuoteValue()
{
......
......@@ -6,9 +6,5 @@ use yiiunit\framework\db\QueryTest;
class MssqlQueryTest extends QueryTest
{
public function setUp()
{
$this->driverName = 'sqlsrv';
parent::setUp();
}
protected $driverName = 'sqlsrv';
}
......@@ -6,9 +6,5 @@ use yiiunit\framework\db\ActiveRecordTest;
class PostgreSQLActiveRecordTest extends ActiveRecordTest
{
protected function setUp()
{
$this->driverName = 'pgsql';
parent::setUp();
}
protected $driverName = 'pgsql';
}
......@@ -5,11 +5,7 @@ use yiiunit\framework\db\ConnectionTest;
class PostgreSQLConnectionTest extends ConnectionTest
{
public function setUp()
{
$this->driverName = 'pgsql';
parent::setUp();
}
protected $driverName = 'pgsql';
public function testConnection()
{
......
......@@ -2,13 +2,11 @@
namespace yiiunit\framework\db\pgsql;
use yii\base\NotSupportedException;
use yii\db\pgsql\Schema;
use yiiunit\framework\db\QueryBuilderTest;
class PostgreSQLQueryBuilderTest extends QueryBuilderTest
{
public $driverName = 'pgsql';
public function columnTypes()
......
......@@ -5,9 +5,5 @@ use yiiunit\framework\db\ActiveRecordTest;
class SqliteActiveRecordTest extends ActiveRecordTest
{
protected function setUp()
{
$this->driverName = 'sqlite';
parent::setUp();
}
protected $driverName = 'sqlite';
}
......@@ -5,11 +5,7 @@ use yiiunit\framework\db\CommandTest;
class SqliteCommandTest extends CommandTest
{
protected function setUp()
{
$this->driverName = 'sqlite';
parent::setUp();
}
protected $driverName = 'sqlite';
public function testAutoQuoting()
{
......
......@@ -5,11 +5,7 @@ use yiiunit\framework\db\ConnectionTest;
class SqliteConnectionTest extends ConnectionTest
{
protected function setUp()
{
$this->driverName = 'sqlite';
parent::setUp();
}
protected $driverName = 'sqlite';
public function testConstruct()
{
......
......@@ -8,7 +8,7 @@ use yiiunit\framework\db\QueryBuilderTest;
class SqliteQueryBuilderTest extends QueryBuilderTest
{
public $driverName = 'sqlite';
protected $driverName = 'sqlite';
public function columnTypes()
{
......
......@@ -5,9 +5,5 @@ use yiiunit\framework\db\QueryTest;
class SqliteQueryTest extends QueryTest
{
protected function setUp()
{
$this->driverName = 'sqlite';
parent::setUp();
}
protected $driverName = 'sqlite';
}
<?php
namespace yiiunit\framework\db\sqlite;
use yiiunit\framework\db\SchemaTest;
class SqliteSchemaTest extends SchemaTest
{
protected $driverName = 'sqlite';
public function testCompositeFk()
{
$this->markTestSkipped('sqlite does not allow getting enough information about composite FK.');
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment