diff --git a/framework/yii/db/Connection.php b/framework/yii/db/Connection.php index e14eeb7..014bbaa 100644 --- a/framework/yii/db/Connection.php +++ b/framework/yii/db/Connection.php @@ -352,6 +352,8 @@ class Connection extends Component $driver = strtolower(substr($this->dsn, 0, $pos)); if ($driver === 'mssql' || $driver === 'dblib' || $driver === 'sqlsrv') { $pdoClass = 'yii\db\mssql\PDO'; + } else if ($driver === 'pgsql') { + $pdoClass = 'yii\db\pgsql\PDO'; } } return new $pdoClass($this->dsn, $this->username, $this->password, $this->attributes); diff --git a/framework/yii/db/pgsql/PDO.php b/framework/yii/db/pgsql/PDO.php new file mode 100644 index 0000000..3ff9d01 --- /dev/null +++ b/framework/yii/db/pgsql/PDO.php @@ -0,0 +1,61 @@ + + * @since 2.0 + */ +class PDO extends \PDO { + + /** + * 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]; + } + } + parent::__construct($dsn, $username, $passwd, $options); + if (!is_null($searchPath)) { + $this->setSchemaSearchPath($searchPath); + } + } + + /** + * 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); + } + +} diff --git a/framework/yii/db/pgsql/Schema.php b/framework/yii/db/pgsql/Schema.php index 4ccf33a..32044ee 100644 --- a/framework/yii/db/pgsql/Schema.php +++ b/framework/yii/db/pgsql/Schema.php @@ -1,4 +1,5 @@ * @since 2.0 */ -class Schema extends \yii\db\Schema -{ +class Schema extends \yii\db\Schema { + + /** + * 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]; + } + } + + /** + * 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) - { + public function loadTableSchema($name) { $table = new TableSchema(); $this->resolveTableNames($table, $name); $this->findPrimaryKeys($table); @@ -32,5 +57,6 @@ class Schema extends \yii\db\Schema $this->findForeignKeys($table); return $table; } - } + } + } \ No newline at end of file diff --git a/tests/unit/data/config.php b/tests/unit/data/config.php index cb612d4..330a464 100644 --- a/tests/unit/data/config.php +++ b/tests/unit/data/config.php @@ -21,9 +21,12 @@ return array( 'fixture' => __DIR__ . '/mssql.sql', ), 'pgsql' => array( - 'dsn' => 'pgsql:host=localhost;dbname=yiitest;port=5432', + 'dsn' => 'pgsql:host=localhost;dbname=yiitest;port=5432;', 'username' => 'postgres', 'password' => 'postgres', + 'attributes' => array( + 'search_path' => 'master,hello' + ), 'fixture' => __DIR__ . '/postgres.sql', ) ) diff --git a/tests/unit/framework/db/DatabaseTestCase.php b/tests/unit/framework/db/DatabaseTestCase.php index 429d961..7ce9bec 100644 --- a/tests/unit/framework/db/DatabaseTestCase.php +++ b/tests/unit/framework/db/DatabaseTestCase.php @@ -37,6 +37,9 @@ abstract class DatabaseTestCase extends TestCase $db->username = $this->database['username']; $db->password = $this->database['password']; } + if (isset($this->database['attributes'])) { + $db->attributes = $this->database['attributes']; + } if ($open) { $db->open(); $lines = explode(';', file_get_contents($this->database['fixture'])); diff --git a/tests/unit/framework/db/pgsql/PostgreSQLConnectionTest.php b/tests/unit/framework/db/pgsql/PostgreSQLConnectionTest.php index 64ceb05..678b197 100644 --- a/tests/unit/framework/db/pgsql/PostgreSQLConnectionTest.php +++ b/tests/unit/framework/db/pgsql/PostgreSQLConnectionTest.php @@ -4,15 +4,48 @@ 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); - } +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)')); + } + + }