Gevik Babakhani
12 years ago
10 changed files with 590 additions and 149 deletions
@ -0,0 +1,103 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
/** |
||||||
|
* @link http://www.yiiframework.com/ |
||||||
|
* @copyright Copyright (c) 2008 Yii Software LLC |
||||||
|
* @license http://www.yiiframework.com/license/ |
||||||
|
*/ |
||||||
|
|
||||||
|
namespace yii\db\pgsql; |
||||||
|
|
||||||
|
/** |
||||||
|
* This is an extension of the default PDO class for PostgreSQL drivers. |
||||||
|
* It provides additional low level functionality for setting database |
||||||
|
* configuration parameters. |
||||||
|
* |
||||||
|
* @author Gevik babakhani <gevikb@gmail.com> |
||||||
|
* @since 2.0 |
||||||
|
*/ |
||||||
|
class PDO extends \PDO |
||||||
|
{ |
||||||
|
|
||||||
|
const OPT_SEARCH_PATH = 'search_path'; |
||||||
|
const OPT_DEFAULT_SCHEMA = 'default_schema'; |
||||||
|
const DEFAULT_SCHEMA = 'public'; |
||||||
|
|
||||||
|
private $_currentDatabase; |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns value of the last inserted ID. |
||||||
|
* @param string|null $sequence the sequence name. Defaults to null. |
||||||
|
* @return integer last inserted ID value. |
||||||
|
*/ |
||||||
|
public function lastInsertId($sequence = null) { |
||||||
|
if ($sequence !== null) { |
||||||
|
$sequence = $this->quote($sequence); |
||||||
|
return $this->query("SELECT currval({$sequence})")->fetchColumn(); |
||||||
|
} else { |
||||||
|
return null; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Here we override the default PDO constructor in order to |
||||||
|
* find and set the default schema search path. |
||||||
|
*/ |
||||||
|
public function __construct($dsn, $username, $passwd, $options) { |
||||||
|
$searchPath = null; |
||||||
|
if (is_array($options)) { |
||||||
|
if (isset($options[self::OPT_SEARCH_PATH])) { |
||||||
|
$matches = null; |
||||||
|
if (preg_match("/(\s?)+(\w)+((\s+)?,(\s+)?\w+)*/", $options[self::OPT_SEARCH_PATH], $matches) === 1) { |
||||||
|
$searchPath = $matches[0]; |
||||||
|
} |
||||||
|
} |
||||||
|
if (isset($options[self::OPT_DEFAULT_SCHEMA])) { |
||||||
|
$schema = trim($options[self::OPT_DEFAULT_SCHEMA]); |
||||||
|
if (!empty($schema)) { |
||||||
|
Schema::$DEFAULT_SCHEMA = $schema; |
||||||
|
} |
||||||
|
} |
||||||
|
if (is_null(Schema::$DEFAULT_SCHEMA) || empty(Schema::$DEFAULT_SCHEMA)) { |
||||||
|
Schema::$DEFAULT_SCHEMA = self::DEFAULT_SCHEMA; |
||||||
|
} |
||||||
|
} |
||||||
|
parent::__construct($dsn, $username, $passwd, $options); |
||||||
|
if (!is_null($searchPath)) { |
||||||
|
$this->setSchemaSearchPath($searchPath); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the name of the current (connected) database |
||||||
|
* @return string |
||||||
|
*/ |
||||||
|
public function getCurrentDatabase() { |
||||||
|
if (is_null($this->_currentDatabase)) { |
||||||
|
return $this->query('select current_database()')->fetchColumn(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Sets the schema search path of the current users session. |
||||||
|
* The syntax of the path is a comma separated string with |
||||||
|
* your custom search path at the beginning and the "public" |
||||||
|
* schema at the end. |
||||||
|
* |
||||||
|
* This method automatically adds the "public" schema at the |
||||||
|
* end of the search path if it is not provied. |
||||||
|
* @param string custom schema search path. defaults to public |
||||||
|
*/ |
||||||
|
public function setSchemaSearchPath($searchPath = 'public') { |
||||||
|
$schemas = explode(',', str_replace(' ', '', $searchPath)); |
||||||
|
if (end($schemas) !== 'public') { |
||||||
|
$schemas[] = 'public'; |
||||||
|
} |
||||||
|
foreach ($schemas as $k => $item) { |
||||||
|
$schemas[$k] = '"' . str_replace(array('"', "'", ';'), '', $item) . '"'; |
||||||
|
} |
||||||
|
$path = implode(', ', $schemas); |
||||||
|
$this->exec('SET search_path TO ' . $path); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,41 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
/** |
||||||
|
* @link http://www.yiiframework.com/ |
||||||
|
* @copyright Copyright (c) 2008 Yii Software LLC |
||||||
|
* @license http://www.yiiframework.com/license/ |
||||||
|
*/ |
||||||
|
|
||||||
|
namespace yii\db\pgsql; |
||||||
|
|
||||||
|
/** |
||||||
|
* QueryBuilder is the query builder for PostgreSQL databases. |
||||||
|
* |
||||||
|
* @author Gevik Babakhani <gevikb@gmail.com> |
||||||
|
* @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 => 'serial not null primary key', |
||||||
|
Schema::TYPE_STRING => 'varchar', |
||||||
|
Schema::TYPE_TEXT => 'text', |
||||||
|
Schema::TYPE_SMALLINT => 'smallint', |
||||||
|
Schema::TYPE_INTEGER => 'integer', |
||||||
|
Schema::TYPE_BIGINT => 'bigint', |
||||||
|
Schema::TYPE_FLOAT => 'double precision', |
||||||
|
Schema::TYPE_DECIMAL => 'numeric', |
||||||
|
Schema::TYPE_DATETIME => 'timestamp', |
||||||
|
Schema::TYPE_TIMESTAMP => 'timestamp', |
||||||
|
Schema::TYPE_TIME => 'time', |
||||||
|
Schema::TYPE_DATE => 'date', |
||||||
|
Schema::TYPE_BINARY => 'bytea', |
||||||
|
Schema::TYPE_BOOLEAN => 'boolean', |
||||||
|
Schema::TYPE_MONEY => 'numeric(19,4)', |
||||||
|
); |
||||||
|
|
||||||
|
} |
@ -0,0 +1,294 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
/** |
||||||
|
* @link http://www.yiiframework.com/ |
||||||
|
* @copyright Copyright (c) 2008 Yii Software LLC |
||||||
|
* @license http://www.yiiframework.com/license/ |
||||||
|
*/ |
||||||
|
|
||||||
|
namespace yii\db\pgsql; |
||||||
|
|
||||||
|
use yii\db\TableSchema; |
||||||
|
use yii\db\ColumnSchema; |
||||||
|
|
||||||
|
/** |
||||||
|
* Schema is the class for retrieving metadata from a PostgreSQL database |
||||||
|
* (version 9.x and above). |
||||||
|
* |
||||||
|
* @author Gevik Babakhani <gevikb@gmail.com> |
||||||
|
* @since 2.0 |
||||||
|
*/ |
||||||
|
class Schema extends \yii\db\Schema |
||||||
|
{ |
||||||
|
|
||||||
|
/** |
||||||
|
* The default schema used for the current session. This value is |
||||||
|
* automatically set to "public" by the PDO driver. |
||||||
|
* @var string |
||||||
|
*/ |
||||||
|
public static $DEFAULT_SCHEMA; |
||||||
|
|
||||||
|
/** |
||||||
|
* @var array mapping from physical column types (keys) to abstract |
||||||
|
* column types (values) |
||||||
|
*/ |
||||||
|
public $typeMap = array( |
||||||
|
'abstime' => self::TYPE_TIMESTAMP, |
||||||
|
'bit' => self::TYPE_STRING, |
||||||
|
'boolean' => self::TYPE_BOOLEAN, |
||||||
|
'box' => self::TYPE_STRING, |
||||||
|
'character' => self::TYPE_STRING, |
||||||
|
'bytea' => self::TYPE_BINARY, |
||||||
|
'char' => self::TYPE_STRING, |
||||||
|
'cidr' => self::TYPE_STRING, |
||||||
|
'circle' => self::TYPE_STRING, |
||||||
|
'date' => self::TYPE_DATE, |
||||||
|
'real' => self::TYPE_FLOAT, |
||||||
|
'double precision' => self::TYPE_DECIMAL, |
||||||
|
'inet' => self::TYPE_STRING, |
||||||
|
'smallint' => self::TYPE_SMALLINT, |
||||||
|
'integer' => self::TYPE_INTEGER, |
||||||
|
'bigint' => self::TYPE_BIGINT, |
||||||
|
'interval' => self::TYPE_STRING, |
||||||
|
'json' => self::TYPE_STRING, |
||||||
|
'line' => self::TYPE_STRING, |
||||||
|
'macaddr' => self::TYPE_STRING, |
||||||
|
'money' => self::TYPE_MONEY, |
||||||
|
'name' => self::TYPE_STRING, |
||||||
|
'numeric' => self::TYPE_STRING, |
||||||
|
'numrange' => self::TYPE_DECIMAL, |
||||||
|
'oid' => self::TYPE_BIGINT, // should not be used. it's pg internal! |
||||||
|
'path' => self::TYPE_STRING, |
||||||
|
'point' => self::TYPE_STRING, |
||||||
|
'polygon' => self::TYPE_STRING, |
||||||
|
'text' => self::TYPE_TEXT, |
||||||
|
'time without time zone' => self::TYPE_TIME, |
||||||
|
'timestamp without time zone' => self::TYPE_TIMESTAMP, |
||||||
|
'timestamp with time zone' => self::TYPE_TIMESTAMP, |
||||||
|
'time with time zone' => self::TYPE_TIMESTAMP, |
||||||
|
'unknown' => self::TYPE_STRING, |
||||||
|
'uuid' => self::TYPE_STRING, |
||||||
|
'bit varying' => self::TYPE_STRING, |
||||||
|
'character varying' => self::TYPE_STRING, |
||||||
|
'xml' => self::TYPE_STRING |
||||||
|
); |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates a query builder for the MySQL database. |
||||||
|
* @return QueryBuilder query builder instance |
||||||
|
*/ |
||||||
|
public function createQueryBuilder() { |
||||||
|
return new QueryBuilder($this->db); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Resolves the table name and schema name (if any). |
||||||
|
* @param TableSchema $table the table metadata object |
||||||
|
* @param string $name the table name |
||||||
|
*/ |
||||||
|
protected function resolveTableNames($table, $name) { |
||||||
|
$parts = explode('.', str_replace('"', '', $name)); |
||||||
|
if (isset($parts[1])) { |
||||||
|
$table->schemaName = $parts[0]; |
||||||
|
$table->name = $parts[1]; |
||||||
|
} else { |
||||||
|
$table->name = $parts[0]; |
||||||
|
} |
||||||
|
if ($table->schemaName === null) { |
||||||
|
$table->schemaName = self::$DEFAULT_SCHEMA; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 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 . '"'; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Loads the metadata for the specified table. |
||||||
|
* @param string $name table name |
||||||
|
* @return TableSchema|null driver dependent table metadata. Null if the table does not exist. |
||||||
|
*/ |
||||||
|
public function loadTableSchema($name) { |
||||||
|
$table = new TableSchema(); |
||||||
|
$this->resolveTableNames($table, $name); |
||||||
|
if ($this->findColumns($table)) { |
||||||
|
$this->findConstraints($table); |
||||||
|
return $table; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Collects the foreign key column details for the given table. |
||||||
|
* @param TableSchema $table the table metadata |
||||||
|
*/ |
||||||
|
protected function findConstraints($table) { |
||||||
|
|
||||||
|
$tableName = $this->quoteValue($table->name); |
||||||
|
$tableSchema = $this->quoteValue($table->schemaName); |
||||||
|
$database = $this->quoteValue($this->db->pdo->getCurrentDatabase()); |
||||||
|
|
||||||
|
//We need to extract the constraints de hard way since: |
||||||
|
//http://www.postgresql.org/message-id/26677.1086673982@sss.pgh.pa.us |
||||||
|
|
||||||
|
$sql = <<<SQL |
||||||
|
select |
||||||
|
ct.conname as containst, |
||||||
|
c.relname as table_name, |
||||||
|
ns.nspname as table_schema, |
||||||
|
current_database() as table_catalog, |
||||||
|
(select string_agg(attname,',') attname from pg_attribute where attrelid=ct.conrelid and attnum = any(ct.conkey)) as columns, |
||||||
|
fc.relname as foreign_table_name, |
||||||
|
fns.nspname as foreign_table_schema, |
||||||
|
current_database() as foreign_table_catalog, |
||||||
|
(select string_agg(attname,',') attname from pg_attribute where attrelid=ct.confrelid and attnum = any(ct.confkey)) as foreign_columns |
||||||
|
from |
||||||
|
pg_constraint ct |
||||||
|
inner join pg_class c on c.oid=ct.conrelid |
||||||
|
inner join pg_namespace ns on c.relnamespace=ns.oid |
||||||
|
left join pg_class fc on fc.oid=ct.confrelid |
||||||
|
left join pg_namespace fns on fc.relnamespace=fns.oid |
||||||
|
|
||||||
|
where |
||||||
|
ct.contype='f' |
||||||
|
and c.relname={$tableName} |
||||||
|
and ns.nspname={$tableSchema} |
||||||
|
and current_database() = {$database} |
||||||
|
SQL; |
||||||
|
|
||||||
|
try { |
||||||
|
$constraints = $this->db->createCommand($sql)->queryAll(); |
||||||
|
} catch (\Exception $e) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
foreach ($constraints as $constraint) { |
||||||
|
$columns = explode(',', $constraint['columns']); |
||||||
|
$fcolumns = explode(',', $constraint['foreign_columns']); |
||||||
|
$citem = array($constraint['foreign_table_name']); |
||||||
|
foreach ($columns as $idx => $column) { |
||||||
|
$citem[] = array($fcolumns[$idx] => $column); |
||||||
|
} |
||||||
|
$table->foreignKeys[] = $citem; |
||||||
|
} |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Collects the metadata of table columns. |
||||||
|
* @param TableSchema $table the table metadata |
||||||
|
* @return boolean whether the table exists in the database |
||||||
|
*/ |
||||||
|
protected function findColumns($table) { |
||||||
|
$dbname = $this->db->quoteValue($this->db->pdo->getCurrentDatabase()); |
||||||
|
$tableName = $this->db->quoteValue($table->name); |
||||||
|
$schemaName = $this->db->quoteValue($table->schemaName); |
||||||
|
$sql = <<<SQL |
||||||
|
SELECT |
||||||
|
current_database() as table_catalog, |
||||||
|
d.nspname AS table_schema, |
||||||
|
c.relname AS table_name, |
||||||
|
a.attname AS column_name, |
||||||
|
t.typname AS data_type, |
||||||
|
a.attlen AS character_maximum_length, |
||||||
|
pg_catalog.col_description(c.oid, a.attnum) AS column_comment, |
||||||
|
a.atttypmod AS modifier, |
||||||
|
a.attnotnull = false AS is_nullable, |
||||||
|
CAST(pg_get_expr(ad.adbin, ad.adrelid) AS varchar) AS column_default, |
||||||
|
coalesce(pg_get_expr(ad.adbin, ad.adrelid) ~ 'nextval',false) AS is_autoinc, |
||||||
|
array_to_string((select array_agg(enumlabel) from pg_enum where enumtypid=a.atttypid)::varchar[],',') as enum_values, |
||||||
|
CASE atttypid |
||||||
|
WHEN 21 /*int2*/ THEN 16 |
||||||
|
WHEN 23 /*int4*/ THEN 32 |
||||||
|
WHEN 20 /*int8*/ THEN 64 |
||||||
|
WHEN 1700 /*numeric*/ THEN |
||||||
|
CASE WHEN atttypmod = -1 |
||||||
|
THEN null |
||||||
|
ELSE ((atttypmod - 4) >> 16) & 65535 |
||||||
|
END |
||||||
|
WHEN 700 /*float4*/ THEN 24 /*FLT_MANT_DIG*/ |
||||||
|
WHEN 701 /*float8*/ THEN 53 /*DBL_MANT_DIG*/ |
||||||
|
ELSE null |
||||||
|
END AS numeric_precision, |
||||||
|
CASE |
||||||
|
WHEN atttypid IN (21, 23, 20) THEN 0 |
||||||
|
WHEN atttypid IN (1700) THEN |
||||||
|
CASE |
||||||
|
WHEN atttypmod = -1 THEN null |
||||||
|
ELSE (atttypmod - 4) & 65535 |
||||||
|
END |
||||||
|
ELSE null |
||||||
|
END AS numeric_scale, |
||||||
|
CAST( |
||||||
|
information_schema._pg_char_max_length(information_schema._pg_truetypid(a, t), information_schema._pg_truetypmod(a, t)) |
||||||
|
AS numeric |
||||||
|
) AS size, |
||||||
|
a.attnum = any (ct.conkey) as is_pkey |
||||||
|
FROM |
||||||
|
pg_class c |
||||||
|
LEFT JOIN pg_attribute a ON a.attrelid = c.oid |
||||||
|
LEFT JOIN pg_attrdef ad ON a.attrelid = ad.adrelid AND a.attnum = ad.adnum |
||||||
|
LEFT JOIN pg_type t ON a.atttypid = t.oid |
||||||
|
LEFT JOIN pg_namespace d ON d.oid = c.relnamespace |
||||||
|
LEFT join pg_constraint ct on ct.conrelid=c.oid and ct.contype='p' |
||||||
|
WHERE |
||||||
|
a.attnum > 0 |
||||||
|
and c.relname = {$tableName} |
||||||
|
and d.nspname = {$schemaName} |
||||||
|
and current_database() = {$dbname} |
||||||
|
ORDER BY |
||||||
|
a.attnum; |
||||||
|
SQL; |
||||||
|
|
||||||
|
try { |
||||||
|
$columns = $this->db->createCommand($sql)->queryAll(); |
||||||
|
} catch (\Exception $e) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
foreach ($columns as $column) { |
||||||
|
$column = $this->loadColumnSchema($column); |
||||||
|
$table->columns[$column->name] = $column; |
||||||
|
if ($column->isPrimaryKey === true) { |
||||||
|
$table->primaryKey[] = $column->name; |
||||||
|
if ($table->sequenceName === null && preg_match("/nextval\('\w+'(::regclass)?\)/", $column->defaultValue) === 1) { |
||||||
|
$table->sequenceName = preg_replace(array('/nextval/', '/::/', '/regclass/', '/\'\)/', '/\(\'/'), '', $column->defaultValue); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 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->allowNull = $info['is_nullable']; |
||||||
|
$column->autoIncrement = $info['is_autoinc']; |
||||||
|
$column->comment = $info['column_comment']; |
||||||
|
$column->dbType = $info['data_type']; |
||||||
|
$column->defaultValue = $info['column_default']; |
||||||
|
$column->enumValues = explode(',', str_replace(array("''"), array("'"), $info['enum_values'])); |
||||||
|
$column->unsigned = false; // has no meanining in PG |
||||||
|
$column->isPrimaryKey = $info['is_pkey']; |
||||||
|
$column->name = $info['column_name']; |
||||||
|
$column->precision = $info['numeric_precision']; |
||||||
|
$column->scale = $info['numeric_scale']; |
||||||
|
$column->size = $info['size']; |
||||||
|
|
||||||
|
if (isset($this->typeMap[$column->dbType])) { |
||||||
|
$column->type = $this->typeMap[$column->dbType]; |
||||||
|
} else { |
||||||
|
$column->type = self::TYPE_STRING; |
||||||
|
} |
||||||
|
$column->phpType = $this->getColumnPhpType($column); |
||||||
|
return $column; |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -1,165 +1,87 @@ |
|||||||
/** |
/** |
||||||
* This is the database schema for testing PostgreSQL support of yii Active Record. |
* 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' |
* To test this feature, you need to create a database named 'yiitest' on 'localhost' |
||||||
* and create an account 'test/test' which owns this test database. |
* and create an account 'postgres/postgres' which owns this test database. |
||||||
*/ |
*/ |
||||||
CREATE SCHEMA test; |
|
||||||
|
|
||||||
CREATE TABLE test.users |
DROP TABLE IF EXISTS tbl_order_item CASCADE; |
||||||
( |
DROP TABLE IF EXISTS tbl_item CASCADE; |
||||||
id SERIAL NOT NULL PRIMARY KEY, |
DROP TABLE IF EXISTS tbl_order CASCADE; |
||||||
username VARCHAR(128) NOT NULL, |
DROP TABLE IF EXISTS tbl_category CASCADE; |
||||||
password VARCHAR(128) NOT NULL, |
DROP TABLE IF EXISTS tbl_customer CASCADE; |
||||||
email VARCHAR(128) NOT NULL |
DROP TABLE IF EXISTS tbl_type CASCADE; |
||||||
|
|
||||||
|
CREATE TABLE tbl_customer ( |
||||||
|
id serial not null primary key, |
||||||
|
email varchar(128) NOT NULL, |
||||||
|
name varchar(128) NOT NULL, |
||||||
|
address text, |
||||||
|
status integer DEFAULT 0 |
||||||
); |
); |
||||||
|
|
||||||
INSERT INTO test.users (username, password, email) VALUES ('user1','pass1','email1'); |
comment on column public.tbl_customer.email is 'someone@example.com'; |
||||||
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 |
CREATE TABLE tbl_category ( |
||||||
( |
id serial not null primary key, |
||||||
id INTEGER NOT NULL, |
name varchar(128) 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); |
CREATE TABLE tbl_item ( |
||||||
INSERT INTO test.user_friends VALUES (1,3); |
id serial not null primary key, |
||||||
INSERT INTO test.user_friends VALUES (2,3); |
name varchar(128) NOT NULL, |
||||||
|
category_id integer NOT NULL references tbl_category(id) on UPDATE CASCADE on DELETE CASCADE |
||||||
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); |
CREATE TABLE tbl_order ( |
||||||
INSERT INTO test.comments (content, post_id, author_id) VALUES ('comment 2',1, 2); |
id serial not null primary key, |
||||||
INSERT INTO test.comments (content, post_id, author_id) VALUES ('comment 3',1, 2); |
customer_id integer NOT NULL references tbl_customer(id) on UPDATE CASCADE on DELETE CASCADE, |
||||||
INSERT INTO test.comments (content, post_id, author_id) VALUES ('comment 4',2, 2); |
create_time integer NOT NULL, |
||||||
INSERT INTO test.comments (content, post_id, author_id) VALUES ('comment 5',2, 2); |
total decimal(10,0) NOT NULL |
||||||
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); |
CREATE TABLE tbl_order_item ( |
||||||
INSERT INTO test.categories (name, parent_id) VALUES ('cat 2',NULL); |
order_id integer NOT NULL references tbl_order(id) on UPDATE CASCADE on DELETE CASCADE, |
||||||
INSERT INTO test.categories (name, parent_id) VALUES ('cat 3',NULL); |
item_id integer NOT NULL references tbl_item(id) on UPDATE CASCADE on DELETE CASCADE, |
||||||
INSERT INTO test.categories (name, parent_id) VALUES ('cat 4',1); |
quantity integer NOT NULL, |
||||||
INSERT INTO test.categories (name, parent_id) VALUES ('cat 5',1); |
subtotal decimal(10,0) NOT NULL, |
||||||
INSERT INTO test.categories (name, parent_id) VALUES ('cat 6',5); |
PRIMARY KEY (order_id,item_id) |
||||||
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); |
CREATE TABLE tbl_type ( |
||||||
INSERT INTO test.post_category (category_id, post_id) VALUES (2,1); |
int_col integer NOT NULL, |
||||||
INSERT INTO test.post_category (category_id, post_id) VALUES (3,1); |
int_col2 integer DEFAULT '1', |
||||||
INSERT INTO test.post_category (category_id, post_id) VALUES (4,2); |
char_col char(100) NOT NULL, |
||||||
INSERT INTO test.post_category (category_id, post_id) VALUES (1,2); |
char_col2 varchar(100) DEFAULT 'something', |
||||||
INSERT INTO test.post_category (category_id, post_id) VALUES (1,3); |
char_col3 text, |
||||||
|
float_col double precision NOT NULL, |
||||||
CREATE TABLE test.orders |
float_col2 double precision DEFAULT '1.23', |
||||||
( |
blob_col bytea, |
||||||
key1 INTEGER NOT NULL, |
numeric_col decimal(5,2) DEFAULT '33.22', |
||||||
key2 INTEGER NOT NULL, |
time timestamp NOT NULL DEFAULT '2002-01-01 00:00:00', |
||||||
name VARCHAR(128), |
bool_col smallint NOT NULL, |
||||||
PRIMARY KEY (key1, key2) |
bool_col2 smallint DEFAULT '1' |
||||||
); |
); |
||||||
|
|
||||||
INSERT INTO test.orders (key1,key2,name) VALUES (1,2,'order 12'); |
INSERT INTO tbl_customer (email, name, address, status) VALUES ('user1@example.com', 'user1', 'address1', 1); |
||||||
INSERT INTO test.orders (key1,key2,name) VALUES (1,3,'order 13'); |
INSERT INTO tbl_customer (email, name, address, status) VALUES ('user2@example.com', 'user2', 'address2', 1); |
||||||
INSERT INTO test.orders (key1,key2,name) VALUES (2,1,'order 21'); |
INSERT INTO tbl_customer (email, name, address, status) VALUES ('user3@example.com', 'user3', 'address3', 2); |
||||||
INSERT INTO test.orders (key1,key2,name) VALUES (2,2,'order 22'); |
|
||||||
|
INSERT INTO tbl_category (name) VALUES ('Books'); |
||||||
CREATE TABLE test.items |
INSERT INTO tbl_category (name) VALUES ('Movies'); |
||||||
( |
|
||||||
id SERIAL NOT NULL PRIMARY KEY, |
INSERT INTO tbl_item (name, category_id) VALUES ('Agile Web Application Development with Yii1.1 and PHP5', 1); |
||||||
name VARCHAR(128), |
INSERT INTO tbl_item (name, category_id) VALUES ('Yii 1.1 Application Development Cookbook', 1); |
||||||
col1 INTEGER NOT NULL, |
INSERT INTO tbl_item (name, category_id) VALUES ('Ice Age', 2); |
||||||
col2 INTEGER NOT NULL, |
INSERT INTO tbl_item (name, category_id) VALUES ('Toy Story', 2); |
||||||
CONSTRAINT FK_order_item FOREIGN KEY (col1,col2) |
INSERT INTO tbl_item (name, category_id) VALUES ('Cars', 2); |
||||||
REFERENCES test.orders (key1,key2) ON DELETE CASCADE ON UPDATE RESTRICT |
|
||||||
); |
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 test.items (name,col1,col2) VALUES ('item 1',1,2); |
INSERT INTO tbl_order (customer_id, create_time, total) VALUES (2, 1325502201, 40.0); |
||||||
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 tbl_order_item (order_id, item_id, quantity, subtotal) VALUES (1, 1, 1, 30.0); |
||||||
INSERT INTO test.items (name,col1,col2) VALUES ('item 4',2,2); |
INSERT INTO tbl_order_item (order_id, item_id, quantity, subtotal) VALUES (1, 2, 2, 40.0); |
||||||
INSERT INTO test.items (name,col1,col2) VALUES ('item 5',2,2); |
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); |
||||||
CREATE TABLE public.yii_types |
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); |
||||||
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 |
|
||||||
); |
|
||||||
|
@ -0,0 +1,14 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
namespace yiiunit\framework\db\pgsql; |
||||||
|
|
||||||
|
use yiiunit\framework\db\ActiveRecordTest; |
||||||
|
|
||||||
|
class PostgreSQLActiveRecordTest extends ActiveRecordTest |
||||||
|
{ |
||||||
|
protected function setUp() |
||||||
|
{ |
||||||
|
$this->driverName = 'pgsql'; |
||||||
|
parent::setUp(); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,51 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
namespace yiiunit\framework\db\pgsql; |
||||||
|
|
||||||
|
use yiiunit\framework\db\ConnectionTest; |
||||||
|
|
||||||
|
class PostgreSQLConnectionTest extends ConnectionTest { |
||||||
|
|
||||||
|
public function setUp() { |
||||||
|
$this->driverName = 'pgsql'; |
||||||
|
parent::setUp(); |
||||||
|
} |
||||||
|
|
||||||
|
public function testConnection() { |
||||||
|
$connection = $this->getConnection(true); |
||||||
|
} |
||||||
|
|
||||||
|
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")); |
||||||
|
} |
||||||
|
|
||||||
|
function testQuoteTableName() |
||||||
|
{ |
||||||
|
$connection = $this->getConnection(false); |
||||||
|
$this->assertEquals('"table"', $connection->quoteTableName('table')); |
||||||
|
$this->assertEquals('"table"', $connection->quoteTableName('"table"')); |
||||||
|
$this->assertEquals('"schema"."table"', $connection->quoteTableName('schema.table')); |
||||||
|
$this->assertEquals('"schema"."table"', $connection->quoteTableName('schema."table"')); |
||||||
|
$this->assertEquals('"schema"."table"', $connection->quoteTableName('"schema"."table"')); |
||||||
|
$this->assertEquals('{{table}}', $connection->quoteTableName('{{table}}')); |
||||||
|
$this->assertEquals('(table)', $connection->quoteTableName('(table)')); |
||||||
|
} |
||||||
|
|
||||||
|
function testQuoteColumnName() |
||||||
|
{ |
||||||
|
$connection = $this->getConnection(false); |
||||||
|
$this->assertEquals('"column"', $connection->quoteColumnName('column')); |
||||||
|
$this->assertEquals('"column"', $connection->quoteColumnName('"column"')); |
||||||
|
$this->assertEquals('"table"."column"', $connection->quoteColumnName('table.column')); |
||||||
|
$this->assertEquals('"table"."column"', $connection->quoteColumnName('table."column"')); |
||||||
|
$this->assertEquals('"table"."column"', $connection->quoteColumnName('"table"."column"')); |
||||||
|
$this->assertEquals('[[column]]', $connection->quoteColumnName('[[column]]')); |
||||||
|
$this->assertEquals('{{column}}', $connection->quoteColumnName('{{column}}')); |
||||||
|
$this->assertEquals('(column)', $connection->quoteColumnName('(column)')); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
} |
Loading…
Reference in new issue