diff --git a/framework/yii/db/pgsql/PDO.php b/framework/yii/db/pgsql/PDO.php index 3ff9d01..75c20a3 100644 --- a/framework/yii/db/pgsql/PDO.php +++ b/framework/yii/db/pgsql/PDO.php @@ -18,16 +18,33 @@ namespace yii\db\pgsql; */ class PDO extends \PDO { + const OPT_SEARCH_PATH = 'search_path'; + const OPT_DEFAULT_SCHEMA = 'default_schema'; + const DEFAULT_SCHEMA = 'public'; + + private $_currentDatabase = 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) && isset($options['search_path'])) { - $matches = null; - if (preg_match("/(\s?)+(\w)+((\s+)?,(\s+)?\w+)*/", $options['search_path'], $matches) === 1) { - $searchPath = $matches[0]; + 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 ($schema !== '') { + Schema::$DEFAULT_SCHEMA = $schema; + } + } + if (Schema::$DEFAULT_SCHEMA === null || Schema::$DEFAULT_SCHEMA === '') { + Schema::$DEFAULT_SCHEMA = self::DEFAULT_SCHEMA; } } parent::__construct($dsn, $username, $passwd, $options); @@ -37,6 +54,16 @@ class PDO extends \PDO { } /** + * 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" diff --git a/framework/yii/db/pgsql/Schema.php b/framework/yii/db/pgsql/Schema.php index 32044ee..f7329bd 100644 --- a/framework/yii/db/pgsql/Schema.php +++ b/framework/yii/db/pgsql/Schema.php @@ -12,7 +12,8 @@ use yii\db\TableSchema; use yii\db\ColumnSchema; /** - * Schema is the class for retrieving metadata from a PostgreSQL database (version 9.x and above). + * Schema is the class for retrieving metadata from a PostgreSQL database + * (version 9.x and above). * * @author Gevik Babakhani * @since 2.0 @@ -20,6 +21,85 @@ use yii\db\ColumnSchema; 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, + //'aclitem' => self::TYPE_STRING, + 'bit' => self::TYPE_STRING, + 'boolean' => self::TYPE_BOOLEAN, + 'box' => self::TYPE_STRING, + 'character' => self::TYPE_STRING, + 'bytea' => self::TYPE_BINARY, + 'char' => self::TYPE_STRING, + //'cid' => self::TYPE_STRING, + 'cidr' => self::TYPE_STRING, + 'circle' => self::TYPE_STRING, + 'date' => self::TYPE_DATE, + //'daterange' => self::TYPE_STRING, + 'real' => self::TYPE_FLOAT, + 'double precision' => self::TYPE_DECIMAL, + //'gtsvector' => self::TYPE_STRING, + 'inet' => self::TYPE_STRING, + 'smallint' => self::TYPE_SMALLINT, + 'integer' => self::TYPE_INTEGER, + //'int4range' => self::TYPE_STRING, //unknown + 'bigint' => self::TYPE_BIGINT, + //'int8range' => self::TYPE_STRING, // unknown + 'interval' => self::TYPE_STRING, + 'json' => self::TYPE_STRING, + 'line' => self::TYPE_STRING, + //'lseg' => 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, + //'pg_node_tree' => self::TYPE_STRING, + 'point' => self::TYPE_STRING, + 'polygon' => self::TYPE_STRING, + //'refcursor' => self::TYPE_STRING, + //'regclass' => self::TYPE_STRING, + //'regconfig' => self::TYPE_STRING, + //'regdictionary' => self::TYPE_STRING, + //'regoper' => self::TYPE_STRING, + //'regoperator' => self::TYPE_STRING, + //'regproc' => self::TYPE_STRING, + //'regprocedure' => self::TYPE_STRING, + //'regtype' => self::TYPE_STRING, + //'reltime' => self::TYPE_STRING, + //'smgr' => self::TYPE_STRING, + 'text' => self::TYPE_TEXT, + //'tid' => self::TYPE_STRING, + '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, + //'tinterval' => self::TYPE_STRING, + //'tsquery' => self::TYPE_STRING, + //'tsrange' => self::TYPE_STRING, + //'tstzrange' => self::TYPE_STRING, + //'tsvector' => self::TYPE_STRING, + //'txid_snapshot' => self::TYPE_STRING, + 'unknown' => self::TYPE_STRING, + 'uuid' => self::TYPE_STRING, + 'bit varying' => self::TYPE_STRING, + 'character varying' => self::TYPE_STRING, + //'xid' => self::TYPE_STRING, + 'xml' => self::TYPE_STRING + ); + + /** * Resolves the table name and schema name (if any). * @param TableSchema $table the table metadata object * @param string $name the table name @@ -32,6 +112,9 @@ class Schema extends \yii\db\Schema { } else { $table->name = $parts[0]; } + if ($table->schemaName === null) { + $table->schemaName = self::$DEFAULT_SCHEMA; + } } /** @@ -52,11 +135,85 @@ class Schema extends \yii\db\Schema { public function loadTableSchema($name) { $table = new TableSchema(); $this->resolveTableNames($table, $name); - $this->findPrimaryKeys($table); if ($this->findColumns($table)) { $this->findForeignKeys($table); return $table; } } + /** + * 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 = << 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); + if ($column->name == 'numbers') + print_r($column); + } + die(); + } + + /** + * 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->isPrimaryKey + $column->name = $info['column_name']; + //$column->phpType +//$column->precision +//$column->scale +//$column->size; +//$column->type +//$column->unsigned + return $column; + } + } \ No newline at end of file diff --git a/tests/unit/data/postgres.sql b/tests/unit/data/postgres.sql index ce75206..f158e58 100644 --- a/tests/unit/data/postgres.sql +++ b/tests/unit/data/postgres.sql @@ -11,14 +11,26 @@ DROP TABLE IF EXISTS tbl_category CASCADE; DROP TABLE IF EXISTS tbl_customer CASCADE; DROP TABLE IF EXISTS tbl_type CASCADE; +drop type if exists fullname cascade; +create type fullname as (firstname varchar,lastname varchar); + +drop type if exists mood cascade; +create type mood as enum ('sad','ok','happy',E'own\'s',E'\"quoted\"'); + + 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 + status integer DEFAULT 0, + fullname fullname, + mood mood, + numbers integer[] ); +comment on column public.tbl_customer.email is 'someone@example.com'; + CREATE TABLE tbl_category ( id serial not null primary key, name varchar(128) NOT NULL