Commit de7797c5 authored by Benjamin Eberlei's avatar Benjamin Eberlei

Merge pull request #282 from deeky666/add-sql-server-charset-collation-support

Add column collation support for SQL Server
parents 0b355ce2 22bd7481
......@@ -409,7 +409,25 @@ class SQLServerPlatform extends AbstractPlatform
*/
public function getListTableColumnsSQL($table, $database = null)
{
return "exec sp_columns @table_name = '" . $table . "'";
return "SELECT col.name,
type.name AS type,
col.max_length AS length,
~col.is_nullable AS notnull,
def.definition AS [default],
col.scale,
col.precision,
col.is_identity AS autoincrement,
col.collation_name AS collation
FROM sys.columns AS col
JOIN sys.types AS type
ON col.user_type_id = type.user_type_id
JOIN sys.objects AS obj
ON col.object_id = obj.object_id
LEFT JOIN sys.default_constraints def
ON col.default_object_id = def.object_id
AND col.object_id = def.parent_object_id
WHERE obj.type = 'U'
AND obj.name = '$table'";
}
/**
......@@ -957,4 +975,42 @@ class SQLServerPlatform extends AbstractPlatform
return " DEFAULT '" . $field['default'] . "'";
}
/**
* {@inheritdoc}
*/
public function getColumnCollationDeclarationSQL($collation)
{
return 'COLLATE ' . $collation;
}
/**
* {@inheritdoc}
*
* Modifies column declaration order as it differs in Microsoft SQL Server.
*/
public function getColumnDeclarationSQL($name, array $field)
{
if (isset($field['columnDefinition'])) {
$columnDef = $this->getCustomTypeDeclarationSQL($field);
} else {
$default = $this->getDefaultValueDeclarationSQL($field);
$collation = (isset($field['collate']) && $field['collate']) ?
' ' . $this->getColumnCollationDeclarationSQL($field['collate']) : '';
$notnull = (isset($field['notnull']) && $field['notnull']) ? ' NOT NULL' : '';
$unique = (isset($field['unique']) && $field['unique']) ?
' ' . $this->getUniqueFieldDeclarationSQL() : '';
$check = (isset($field['check']) && $field['check']) ?
' ' . $field['check'] : '';
$typeDecl = $field['type']->getSqlDeclaration($field, $this);
$columnDef = $typeDecl . $collation . $default . $notnull . $unique . $check;
}
return $name . ' ' . $columnDef;
}
}
......@@ -22,6 +22,7 @@ namespace Doctrine\DBAL\Schema;
use Doctrine\DBAL\Events;
use Doctrine\DBAL\Event\SchemaIndexDefinitionEventArgs;
use Doctrine\DBAL\Driver\SQLSrv\SQLSrvException;
use Doctrine\DBAL\Types\Type;
/**
* SQL Server Schema Manager
......@@ -44,69 +45,68 @@ class SQLServerSchemaManager extends AbstractSchemaManager
}
/**
* @override
* {@inheritdoc}
*/
protected function _getPortableTableColumnDefinition($tableColumn)
{
$dbType = strtolower($tableColumn['TYPE_NAME']);
$autoincrement = false;
if (stripos($dbType, 'identity')) {
$dbType = trim(str_ireplace('identity', '', $dbType));
$autoincrement = true;
}
$type = array();
$unsigned = $fixed = null;
$dbType = $tableColumn['type'];
$fixed = null;
$length = (int) $tableColumn['length'];
$default = $tableColumn['default'];
if (!isset($tableColumn['name'])) {
$tableColumn['name'] = '';
}
$default = $tableColumn['COLUMN_DEF'];
while ($default != ($default2 = preg_replace("/^\((.*)\)$/", '$1', $default))) {
$default = trim($default2, "'");
}
$length = (int) $tableColumn['LENGTH'];
switch ($dbType) {
case 'nchar':
case 'nvarchar':
case 'ntext':
// Unicode data requires 2 bytes per character
$length = $length / 2;
break;
case 'varchar':
// TEXT type is returned as VARCHAR(MAX) with a length of -1
if ($length == -1) {
$dbType = 'text';
}
break;
}
$type = $this->_platform->getDoctrineTypeMapping($dbType);
switch ($type) {
case 'char':
if ($tableColumn['LENGTH'] == '1') {
$type = 'boolean';
if (preg_match('/^(is|has)/', $tableColumn['name'])) {
$type = array_reverse($type);
}
}
$fixed = true;
break;
case 'text':
$fixed = false;
break;
}
switch ($dbType) {
case 'nchar':
case 'nvarchar':
case 'ntext':
// Unicode data requires 2 bytes per character
$length = $length / 2;
break;
}
$options = array(
'length' => ($length == 0 || !in_array($type, array('text', 'string'))) ? null : $length,
'unsigned' => (bool) $unsigned,
'unsigned' => false,
'fixed' => (bool) $fixed,
'default' => $default !== 'NULL' ? $default : null,
'notnull' => (bool) ($tableColumn['IS_NULLABLE'] != 'YES'),
'scale' => $tableColumn['SCALE'],
'precision' => $tableColumn['PRECISION'],
'autoincrement' => $autoincrement,
'notnull' => (bool) $tableColumn['notnull'],
'scale' => $tableColumn['scale'],
'precision' => $tableColumn['precision'],
'autoincrement' => (bool) $tableColumn['autoincrement'],
);
return new Column($tableColumn['COLUMN_NAME'], \Doctrine\DBAL\Types\Type::getType($type), $options);
$platformOptions = array(
'collate' => $tableColumn['collation'] == 'NULL' ? null : $tableColumn['collation']
);
$column = new Column($tableColumn['name'], Type::getType($type), $options);
$column->setPlatformOptions($platformOptions);
return $column;
}
/**
......
......@@ -34,4 +34,22 @@ class SQLServerSchemaManagerTest extends SchemaManagerFunctionalTestCase
$columns = $this->_sm->listTableColumns('sqlsrv_drop_column');
$this->assertEquals(1, count($columns));
}
public function testCollationCharset()
{
$table = new \Doctrine\DBAL\Schema\Table($tableName = 'test_collation_charset');
$column = $table->addColumn($columnName = 'test', 'string');
$this->_sm->dropAndCreateTable($table);
$columns = $this->_sm->listTableColumns($tableName);
$this->assertTrue($columns[$columnName]->hasPlatformOption('collate')); // SQL Server should report a default collation on the column
$column->setPlatformOption('collate', $collation = 'Icelandic_CS_AS');
$this->_sm->dropAndCreateTable($table);
$columns = $this->_sm->listTableColumns($tableName);
$this->assertEquals($collation, $columns[$columnName]->getPlatformOption('collate'));
}
}
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