8 changed files with 471 additions and 149 deletions
			
			
		| @ -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,284 @@ | ||||
| <?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. | ||||
| 	 * @var string  | ||||
| 	 */ | ||||
| 	public $defaultSchema = 'public'; | ||||
| 
 | ||||
| 	/** | ||||
| 	 * @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 = $this->defaultSchema; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	/** | ||||
| 	 * 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); | ||||
| 
 | ||||
| 		//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} | ||||
| SQL; | ||||
| 
 | ||||
| 		$constraints = $this->db->createCommand($sql)->queryAll(); | ||||
| 		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; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	/** | ||||
| 	 * 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) { | ||||
| 		$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} | ||||
| 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. | ||||
|  * 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. | ||||
|  * To test this feature, you need to create a database named 'yiitest' on 'localhost' | ||||
|  * and create an account 'postgres/postgres' 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 | ||||
| DROP TABLE IF EXISTS tbl_order_item CASCADE; | ||||
| DROP TABLE IF EXISTS tbl_item CASCADE; | ||||
| DROP TABLE IF EXISTS tbl_order CASCADE; | ||||
| DROP TABLE IF EXISTS tbl_category CASCADE; | ||||
| DROP TABLE IF EXISTS tbl_customer CASCADE; | ||||
| 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'); | ||||
| INSERT INTO test.users (username, password, email) VALUES ('user2','pass2','email2'); | ||||
| INSERT INTO test.users (username, password, email) VALUES ('user3','pass3','email3'); | ||||
| comment on column public.tbl_customer.email is 'someone@example.com'; | ||||
| 
 | ||||
| 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 | ||||
| CREATE TABLE tbl_category ( | ||||
|   id serial not null primary key, | ||||
|   name varchar(128) NOT NULL | ||||
| ); | ||||
| 
 | ||||
| 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 | ||||
| CREATE TABLE tbl_item ( | ||||
|   id serial not null primary key, | ||||
|   name varchar(128) NOT NULL, | ||||
|   category_id integer NOT NULL references tbl_category(id) on UPDATE CASCADE on DELETE CASCADE | ||||
| ); | ||||
| 
 | ||||
| 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 | ||||
| CREATE TABLE tbl_order ( | ||||
|   id serial not null primary key, | ||||
|   customer_id integer NOT NULL references tbl_customer(id) on UPDATE CASCADE on DELETE CASCADE, | ||||
|   create_time integer NOT NULL, | ||||
|   total decimal(10,0) NOT NULL | ||||
| ); | ||||
| 
 | ||||
| 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 | ||||
| CREATE TABLE tbl_order_item ( | ||||
|   order_id integer NOT NULL references tbl_order(id) on UPDATE CASCADE on DELETE CASCADE, | ||||
|   item_id integer NOT NULL references tbl_item(id) on UPDATE CASCADE on DELETE CASCADE, | ||||
|   quantity integer NOT NULL, | ||||
|   subtotal decimal(10,0) NOT NULL, | ||||
|   PRIMARY KEY (order_id,item_id) | ||||
| ); | ||||
| 
 | ||||
| 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) | ||||
| CREATE TABLE tbl_type ( | ||||
|   int_col integer NOT NULL, | ||||
|   int_col2 integer DEFAULT '1', | ||||
|   char_col char(100) NOT NULL, | ||||
|   char_col2 varchar(100) DEFAULT 'something', | ||||
|   char_col3 text, | ||||
|   float_col double precision NOT NULL, | ||||
|   float_col2 double precision DEFAULT '1.23', | ||||
|   blob_col bytea, | ||||
|   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' | ||||
| ); | ||||
| 
 | ||||
| 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 | ||||
| ); | ||||
| 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); | ||||
|  | ||||
| @ -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