Commit fdcae5d3 authored by Benjamin Eberlei's avatar Benjamin Eberlei

Merge branch 'sqlsrv'

parents 454d1e0b 2bfbde37
<?php
/*
* $Id$
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\DBAL\Driver\PDOMsSql;
/**
* The PDO-based MsSql driver.
*
* @since 2.0
*/
class Driver implements \Doctrine\DBAL\Driver
{
public function connect(array $params, $username = null, $password = null, array $driverOptions = array())
{
return new Connection(
$this->_constructPdoDsn($params),
$username,
$password,
$driverOptions
);
}
/**
* Constructs the MsSql PDO DSN.
*
* @return string The DSN.
*/
private function _constructPdoDsn(array $params)
{
// TODO: This might need to be revisted once we have access to a mssql server
$dsn = 'mssql:';
if (isset($params['host'])) {
$dsn .= 'host=' . $params['host'] . ';';
}
if (isset($params['port'])) {
$dsn .= 'port=' . $params['port'] . ';';
}
if (isset($params['dbname'])) {
$dsn .= 'dbname=' . $params['dbname'] . ';';
}
return $dsn;
}
public function getDatabasePlatform()
{
return new \Doctrine\DBAL\Platforms\MsSqlPlatform();
}
public function getSchemaManager(\Doctrine\DBAL\Connection $conn)
{
return new \Doctrine\DBAL\Schema\MsSqlSchemaManager($conn);
}
public function getName()
{
return 'pdo_mssql';
}
public function getDatabase(\Doctrine\DBAL\Connection $conn)
{
$params = $conn->getParams();
return $params['dbname'];
}
}
\ No newline at end of file
...@@ -19,42 +19,27 @@ ...@@ -19,42 +19,27 @@
* <http://www.doctrine-project.org>. * <http://www.doctrine-project.org>.
*/ */
namespace Doctrine\DBAL\Driver\PDOMsSql; namespace Doctrine\DBAL\Driver\PDOSqlsrv;
/** /**
* MsSql Connection implementation. * Sqlsrv Connection implementation.
* *
* @since 2.0 * @since 2.0
*/ */
class Connection extends \PDO implements \Doctrine\DBAL\Driver\Connection class Connection extends \Doctrine\DBAL\Driver\PDOConnection implements \Doctrine\DBAL\Driver\Connection
{ {
/** /**
* Performs the rollback.
*
* @override * @override
*/ */
public function rollback() public function quote($value, $type=\PDO::PARAM_STR)
{ {
$this->exec('ROLLBACK TRANSACTION'); $val = parent::quote($value, $type);
}
/** // Fix for a driver version terminating all values with null byte
* Performs the commit. if (strpos($val, "\0") !== false) {
* $val = substr($val, 0, -1);
* @override
*/
public function commit()
{
$this->exec('COMMIT TRANSACTION');
} }
/** return $val;
* Begins a database transaction.
*
* @override
*/
public function beginTransaction()
{
$this->exec('BEGIN TRANSACTION');
} }
} }
\ No newline at end of file
...@@ -30,7 +30,7 @@ class Driver implements \Doctrine\DBAL\Driver ...@@ -30,7 +30,7 @@ class Driver implements \Doctrine\DBAL\Driver
{ {
public function connect(array $params, $username = null, $password = null, array $driverOptions = array()) public function connect(array $params, $username = null, $password = null, array $driverOptions = array())
{ {
return new \Doctrine\DBAL\Driver\PDOConnection( return new Connection(
$this->_constructPdoDsn($params), $this->_constructPdoDsn($params),
$username, $username,
$password, $password,
......
...@@ -40,7 +40,6 @@ final class DriverManager ...@@ -40,7 +40,6 @@ final class DriverManager
'pdo_sqlite' => 'Doctrine\DBAL\Driver\PDOSqlite\Driver', 'pdo_sqlite' => 'Doctrine\DBAL\Driver\PDOSqlite\Driver',
'pdo_pgsql' => 'Doctrine\DBAL\Driver\PDOPgSql\Driver', 'pdo_pgsql' => 'Doctrine\DBAL\Driver\PDOPgSql\Driver',
'pdo_oci' => 'Doctrine\DBAL\Driver\PDOOracle\Driver', 'pdo_oci' => 'Doctrine\DBAL\Driver\PDOOracle\Driver',
'pdo_mssql' => 'Doctrine\DBAL\Driver\PDOMsSql\Driver',
'oci8' => 'Doctrine\DBAL\Driver\OCI8\Driver', 'oci8' => 'Doctrine\DBAL\Driver\OCI8\Driver',
'ibm_db2' => 'Doctrine\DBAL\Driver\IBMDB2\DB2Driver', 'ibm_db2' => 'Doctrine\DBAL\Driver\IBMDB2\DB2Driver',
'pdo_ibm' => 'Doctrine\DBAL\Driver\PDOIbm\Driver', 'pdo_ibm' => 'Doctrine\DBAL\Driver\PDOIbm\Driver',
...@@ -62,7 +61,7 @@ final class DriverManager ...@@ -62,7 +61,7 @@ final class DriverManager
* pdo_sqlite * pdo_sqlite
* pdo_pgsql * pdo_pgsql
* pdo_oracle * pdo_oracle
* pdo_mssql * pdo_sqlsrv
* *
* OR 'driverClass' that contains the full class name (with namespace) of the * OR 'driverClass' that contains the full class name (with namespace) of the
* driver class to instantiate. * driver class to instantiate.
......
...@@ -93,19 +93,15 @@ class MsSqlPlatform extends AbstractPlatform ...@@ -93,19 +93,15 @@ class MsSqlPlatform extends AbstractPlatform
*/ */
public function getDropDatabaseSQL($name) public function getDropDatabaseSQL($name)
{ {
// @todo do we really need to force drop? return 'DROP DATABASE ' . $name;
return 'ALTER DATABASE [' . $name . ']
SET SINGLE_USER
WITH ROLLBACK IMMEDIATE;
DROP DATABASE ' . $name . ';';
} }
/** /**
* @override * @override
*/ */
public function quoteIdentifier($str) public function supportsCreateDropDatabase()
{ {
return '[' . $str . ']'; return false;
} }
/** /**
...@@ -144,9 +140,9 @@ DROP DATABASE ' . $name . ';'; ...@@ -144,9 +140,9 @@ DROP DATABASE ' . $name . ';';
} }
return "IF EXISTS (SELECT * FROM sysobjects WHERE name = '$index') return "IF EXISTS (SELECT * FROM sysobjects WHERE name = '$index')
ALTER TABLE " . $this->quoteIdentifier($table) . " DROP CONSTRAINT " . $this->quoteIdentifier($index) . " ALTER TABLE " . $table . " DROP CONSTRAINT " . $index . "
ELSE ELSE
DROP INDEX " . $this->quoteIdentifier($index) . " ON " . $this->quoteIdentifier($table); DROP INDEX " . $index . " ON " . $table;
} }
} }
...@@ -155,27 +151,22 @@ DROP DATABASE ' . $name . ';'; ...@@ -155,27 +151,22 @@ DROP DATABASE ' . $name . ';';
*/ */
public function getCreateTableSQL(Table $table, $createFlags=self::CREATE_INDEXES) public function getCreateTableSQL(Table $table, $createFlags=self::CREATE_INDEXES)
{ {
$sql = parent::getCreateTableSQL($table, $createFlags); // Foreign keys cannot be identity columns at the same time
foreach ($table->getForeignKeys() AS $definition) {
$columns = $definition->getLocalColumns();
$primary = array(); if (count($columns) != 1)
continue;
foreach ($table->getIndexes() AS $index) { foreach ($table->getIndexes() AS $index) {
/* @var $index Index */ /* @var $index Index */
if ($index->isPrimary()) { if ($index->isPrimary() && in_array($columns[0], $index->getColumns())) {
$primary = $index->getColumns(); $table->getColumn($columns[0])->setAutoincrement(false);
}
}
if (count($primary) === 1) {
foreach ($table->getForeignKeys() AS $definition) {
$columns = $definition->getLocalColumns();
if (count($columns) === 1 && in_array($columns[0], $primary)) {
$sql[0] = str_replace(' IDENTITY', '', $sql[0]);
} }
} }
} }
return $sql; return parent::getCreateTableSQL($table, $createFlags);
} }
/** /**
...@@ -183,6 +174,14 @@ DROP DATABASE ' . $name . ';'; ...@@ -183,6 +174,14 @@ DROP DATABASE ' . $name . ';';
*/ */
protected function _getCreateTableSQL($tableName, array $columns, array $options = array()) protected function _getCreateTableSQL($tableName, array $columns, array $options = array())
{ {
// @todo does other code breaks because of this?
// foce primary keys to be not null
foreach ($columns as &$column) {
if (isset($column['primary']) && $column['primary']) {
$column['notnull'] = true;
}
}
$columnListSql = $this->getColumnDeclarationListSQL($columns); $columnListSql = $this->getColumnDeclarationListSQL($columns);
if (isset($options['uniqueConstraints']) && !empty($options['uniqueConstraints'])) { if (isset($options['uniqueConstraints']) && !empty($options['uniqueConstraints'])) {
...@@ -222,6 +221,53 @@ DROP DATABASE ' . $name . ';'; ...@@ -222,6 +221,53 @@ DROP DATABASE ' . $name . ';';
/** /**
* @override * @override
*/
public function getUniqueConstraintDeclarationSQL($name, Index $index)
{
$constraint = parent::getUniqueConstraintDeclarationSQL($name, $index);
$constraint = $this->_appendUniqueConstraintDefinition($constraint, $index);
return $constraint;
}
/**
* @override
*/
public function getCreateIndexSQL(Index $index, $table)
{
$constraint = parent::getCreateIndexSQL($index, $table);
if ($index->isUnique()) {
$constraint = $this->_appendUniqueConstraintDefinition($constraint, $index);
}
return $constraint;
}
/**
* Extend unique key constraint with required filters
*
* @param string $sql
* @param Index $index
* @return string
*/
private function _appendUniqueConstraintDefinition($sql, Index $index)
{
$fields = array();
foreach ($index->getColumns() as $field => $definition) {
if (!is_array($definition)) {
$field = $definition;
}
$fields[] = $field . ' IS NOT NULL';
}
return $sql . ' WHERE ' . implode(' AND ', $fields);
}
/**
* @override
*/ */
public function getAlterTableSQL(TableDiff $diff) public function getAlterTableSQL(TableDiff $diff)
{ {
...@@ -393,10 +439,9 @@ DROP DATABASE ' . $name . ';'; ...@@ -393,10 +439,9 @@ DROP DATABASE ' . $name . ';';
*/ */
public function getTrimExpression($str, $pos = self::TRIM_UNSPECIFIED, $char = false) public function getTrimExpression($str, $pos = self::TRIM_UNSPECIFIED, $char = false)
{ {
// @todo
$trimFn = ''; $trimFn = '';
$trimChar = ($char != false) ? (', ' . $char) : '';
if (!$char) {
if ($pos == self::TRIM_LEADING) { if ($pos == self::TRIM_LEADING) {
$trimFn = 'LTRIM'; $trimFn = 'LTRIM';
} else if ($pos == self::TRIM_TRAILING) { } else if ($pos == self::TRIM_TRAILING) {
...@@ -406,6 +451,26 @@ DROP DATABASE ' . $name . ';'; ...@@ -406,6 +451,26 @@ DROP DATABASE ' . $name . ';';
} }
return $trimFn . '(' . $str . ')'; return $trimFn . '(' . $str . ')';
} else {
/** Original query used to get those expressions
declare @c varchar(100) = 'xxxBarxxx', @trim_char char(1) = 'x';
declare @pat varchar(10) = '%[^' + @trim_char + ']%';
select @c as string
, @trim_char as trim_char
, stuff(@c, 1, patindex(@pat, @c) - 1, null) as trim_leading
, reverse(stuff(reverse(@c), 1, patindex(@pat, reverse(@c)) - 1, null)) as trim_trailing
, reverse(stuff(reverse(stuff(@c, 1, patindex(@pat, @c) - 1, null)), 1, patindex(@pat, reverse(stuff(@c, 1, patindex(@pat, @c) - 1, null))) - 1, null)) as trim_both;
*/
$pattern = "'%[^' + $char + ']%'";
if ($pos == self::TRIM_LEADING) {
return 'stuff(' . $str . ', 1, patindex(' . $pattern .', ' . $str . ') - 1, null)';
} else if ($pos == self::TRIM_TRAILING) {
return 'reverse(stuff(reverse(' . $str . '), 1, patindex(' . $pattern .', reverse(' . $str . ')) - 1, null))';
} else {
return 'reverse(stuff(reverse(stuff(' . $str . ', 1, patindex(' . $pattern .', ' . $str . ') - 1, null)), 1, patindex(' . $pattern .', reverse(stuff(' . $str . ', 1, patindex(' . $pattern .', ' . $str . ') - 1, null))) - 1, null))';
}
}
} }
/** /**
...@@ -650,6 +715,9 @@ DROP DATABASE ' . $name . ';'; ...@@ -650,6 +715,9 @@ DROP DATABASE ' . $name . ';';
return 'mssql'; return 'mssql';
} }
/**
* @override
*/
protected function initializeDoctrineTypeMappings() protected function initializeDoctrineTypeMappings()
{ {
$this->doctrineTypeMapping = array( $this->doctrineTypeMapping = array(
...@@ -716,4 +784,28 @@ DROP DATABASE ' . $name . ';'; ...@@ -716,4 +784,28 @@ DROP DATABASE ' . $name . ';';
{ {
return 'ROLLBACK TRANSACTION ' . $savepoint; return 'ROLLBACK TRANSACTION ' . $savepoint;
} }
/**
* @override
*/
public function appendLockHint($fromClause, $lockMode)
{
// @todo coorect
if ($lockMode == \Doctrine\DBAL\LockMode::PESSIMISTIC_READ) {
return $fromClause . ' WITH (tablockx)';
} else if ($lockMode == \Doctrine\DBAL\LockMode::PESSIMISTIC_WRITE) {
return $fromClause . ' WITH (tablockx)';
}
else {
return $fromClause;
}
}
/**
* @override
*/
public function getForUpdateSQL()
{
return ' ';
}
} }
...@@ -24,7 +24,7 @@ class MsSqlPlatformTest extends AbstractPlatformTestCase ...@@ -24,7 +24,7 @@ class MsSqlPlatformTest extends AbstractPlatformTestCase
{ {
return array( return array(
'CREATE TABLE test (foo NVARCHAR(255) DEFAULT NULL, bar NVARCHAR(255) DEFAULT NULL)', 'CREATE TABLE test (foo NVARCHAR(255) DEFAULT NULL, bar NVARCHAR(255) DEFAULT NULL)',
'CREATE UNIQUE INDEX test_foo_bar_uniq ON test (foo, bar)' 'CREATE UNIQUE INDEX test_foo_bar_uniq ON test (foo, bar) WHERE foo IS NOT NULL AND bar IS NOT NULL'
); );
} }
...@@ -67,12 +67,7 @@ class MsSqlPlatformTest extends AbstractPlatformTestCase ...@@ -67,12 +67,7 @@ class MsSqlPlatformTest extends AbstractPlatformTestCase
public function testGeneratesDDLSnippets() public function testGeneratesDDLSnippets()
{ {
$dropDatabaseExpectation = <<<DDB $dropDatabaseExpectation = 'DROP DATABASE foobar';
ALTER DATABASE [foobar]
SET SINGLE_USER
WITH ROLLBACK IMMEDIATE;
DROP DATABASE foobar;
DDB;
$this->assertEquals('SHOW DATABASES', $this->_platform->getShowDatabasesSQL()); $this->assertEquals('SHOW DATABASES', $this->_platform->getShowDatabasesSQL());
$this->assertEquals('CREATE DATABASE foobar', $this->_platform->getCreateDatabaseSQL('foobar')); $this->assertEquals('CREATE DATABASE foobar', $this->_platform->getCreateDatabaseSQL('foobar'));
...@@ -138,7 +133,7 @@ DDB; ...@@ -138,7 +133,7 @@ DDB;
public function getGenerateUniqueIndexSql() public function getGenerateUniqueIndexSql()
{ {
return 'CREATE UNIQUE INDEX index_name ON test (test, test2)'; return 'CREATE UNIQUE INDEX index_name ON test (test, test2) WHERE test IS NOT NULL AND test2 IS NOT NULL';
} }
public function getGenerateForeignKeySql() public function getGenerateForeignKeySql()
......
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