Commit 62c2a8b9 authored by David Négrier's avatar David Négrier

Adding support for table level comments in all DBMS

parent 29486f06
......@@ -28,6 +28,7 @@ example shows:
$myTable->addColumn("username", "string", array("length" => 32));
$myTable->setPrimaryKey(array("id"));
$myTable->addUniqueIndex(array("username"));
$myTable->setComment('Some comment');
$schema->createSequence("my_table_seq");
$myForeign = $schema->createTable("my_foreign");
......
......@@ -1607,6 +1607,9 @@ abstract class AbstractPlatform
$sql = $this->_getCreateTableSQL($tableName, $columns, $options);
if ($this->supportsCommentOnStatement()) {
if ($table->hasOption('comment')) {
$sql[] = $this->getCommentOnTableSQL($tableName, $table->getOption('comment'));
}
foreach ($table->getColumns() as $column) {
$comment = $this->getColumnComment($column);
......@@ -1621,6 +1624,17 @@ abstract class AbstractPlatform
return array_merge($sql, $columnSql);
}
protected function getCommentOnTableSQL(string $tableName, ?string $comment) : string
{
$tableName = new Identifier($tableName);
return sprintf(
'COMMENT ON TABLE %s IS %s',
$tableName->getQuotedName($this),
$this->quoteStringLiteral((string) $comment)
);
}
/**
* @param string $tableName
* @param string $columnName
......
......@@ -900,4 +900,17 @@ class DB2Platform extends AbstractPlatform
{
return Keywords\DB2Keywords::class;
}
public function getListTableCommentsSQL(string $table) : string
{
return sprintf(
<<<'SQL'
SELECT REMARKS
FROM SYSIBM.SYSTABLES
WHERE NAME = UPPER( %s )
SQL
,
$this->quoteStringLiteral($table)
);
}
}
......@@ -519,9 +519,7 @@ SQL
// Comment
if (isset($options['comment'])) {
$comment = trim($options['comment'], " '");
$tableOptions[] = sprintf('COMMENT = %s ', $this->quoteStringLiteral($comment));
$tableOptions[] = sprintf('COMMENT = %s ', $this->quoteStringLiteral($options['comment']));
}
// Row format
......
......@@ -1182,4 +1182,25 @@ SQL
{
return 'BLOB';
}
public function getListTableCommentsSQL(string $table, ?string $database = null) : string
{
$tableCommentsName = 'user_tab_comments';
$ownerCondition = '';
if ($database !== null && $database !== '/') {
$tableCommentsName = 'all_tab_comments';
$ownerCondition = ' AND owner = ' . $this->quoteStringLiteral($this->normalizeIdentifier($database)->getName());
}
return sprintf(
<<<'SQL'
SELECT comments FROM %s WHERE table_name = %s%s
SQL
,
$tableCommentsName,
$this->quoteStringLiteral($this->normalizeIdentifier($table)->getName()),
$ownerCondition
);
}
}
......@@ -1252,4 +1252,19 @@ SQL
{
return $columnDiff->fromColumn ? $this->getColumnComment($columnDiff->fromColumn) : null;
}
public function getListTableMetadataSQL(string $table, ?string $schema = null) : string
{
if ($schema !== null) {
$table = $schema . '.' . $table;
}
return sprintf(
<<<'SQL'
SELECT obj_description(%s::regclass) AS table_comment;
SQL
,
$this->quoteStringLiteral($table)
);
}
}
......@@ -247,6 +247,11 @@ SQL
$defaultConstraintsSql = [];
$commentsSql = [];
$tableComment = $options['comment'] ?? null;
if ($tableComment !== null) {
$commentsSql[] = $this->getCommentOnTableSQL($tableName, $tableComment);
}
// @todo does other code breaks because of this?
// force primary keys to be not null
foreach ($columns as &$column) {
......@@ -1656,4 +1661,35 @@ SQL
return strtoupper(dechex(crc32($identifier->getName())));
}
protected function getCommentOnTableSQL(string $tableName, ?string $comment) : string
{
return sprintf(
<<<'SQL'
EXEC sys.sp_addextendedproperty @name=N'MS_Description',
@value=N%s, @level0type=N'SCHEMA', @level0name=N'dbo',
@level1type=N'TABLE', @level1name=N%s
SQL
,
$this->quoteStringLiteral((string) $comment),
$this->quoteStringLiteral($tableName)
);
}
public function getListTableMetadataSQL(string $table) : string
{
return sprintf(
<<<'SQL'
SELECT
p.value AS [table_comment]
FROM
sys.tables AS tbl
INNER JOIN sys.extended_properties AS p ON p.major_id=tbl.object_id AND p.minor_id=0 AND p.class=1
WHERE
(tbl.name=N%s and SCHEMA_NAME(tbl.schema_id)=N'dbo' and p.name=N'MS_Description')
SQL
,
$this->quoteStringLiteral($table)
);
}
}
......@@ -23,6 +23,7 @@ use function str_replace;
use function strlen;
use function strpos;
use function strtolower;
use function trim;
/**
* The SqlitePlatform class describes the specifics and dialects of the SQLite
......@@ -332,7 +333,14 @@ class SqlitePlatform extends AbstractPlatform
}
}
$query = ['CREATE TABLE ' . $name . ' (' . $queryFields . ')'];
$tableComment = '';
if (isset($options['comment'])) {
$comment = trim($options['comment'], " '");
$tableComment = $this->getInlineTableCommentSQL($comment);
}
$query = ['CREATE TABLE ' . $name . ' ' . $tableComment . '(' . $queryFields . ')'];
if (isset($options['alter']) && $options['alter'] === true) {
return $query;
......@@ -605,6 +613,11 @@ class SqlitePlatform extends AbstractPlatform
return '--' . str_replace("\n", "\n--", $comment) . "\n";
}
private function getInlineTableCommentSQL(string $comment) : string
{
return $this->getInlineColumnCommentSQL($comment);
}
/**
* {@inheritDoc}
*/
......
......@@ -2,6 +2,7 @@
namespace Doctrine\DBAL\Schema;
use Doctrine\DBAL\Platforms\DB2Platform;
use Doctrine\DBAL\Types\Type;
use const CASE_LOWER;
use function array_change_key_case;
......@@ -209,4 +210,18 @@ class DB2SchemaManager extends AbstractSchemaManager
return new View($view['name'], $sql);
}
public function listTableDetails($tableName) : Table
{
$table = parent::listTableDetails($tableName);
/** @var DB2Platform $platform */
$platform = $this->_platform;
$sql = $platform->getListTableCommentsSQL($tableName);
$tableOptions = $this->_conn->fetchAssoc($sql);
$table->addOption('comment', $tableOptions['REMARKS']);
return $table;
}
}
......@@ -385,4 +385,18 @@ SQL;
);
}
}
public function listTableDetails($tableName) : Table
{
$table = parent::listTableDetails($tableName);
/** @var OraclePlatform $platform */
$platform = $this->_platform;
$sql = $platform->getListTableCommentsSQL($tableName);
$tableOptions = $this->_conn->fetchAssoc($sql);
$table->addOption('comment', $tableOptions['COMMENTS']);
return $table;
}
}
......@@ -489,4 +489,19 @@ class PostgreSqlSchemaManager extends AbstractSchemaManager
return str_replace("''", "'", $default);
}
public function listTableDetails($tableName) : Table
{
$table = parent::listTableDetails($tableName);
/** @var PostgreSqlPlatform $platform */
$platform = $this->_platform;
$sql = $platform->getListTableMetadataSQL($tableName);
$tableOptions = $this->_conn->fetchAssoc($sql);
$table->addOption('comment', $tableOptions['table_comment']);
return $table;
}
}
......@@ -4,6 +4,7 @@ namespace Doctrine\DBAL\Schema;
use Doctrine\DBAL\DBALException;
use Doctrine\DBAL\Driver\DriverException;
use Doctrine\DBAL\Platforms\SQLServerPlatform;
use Doctrine\DBAL\Types\Type;
use PDOException;
use Throwable;
......@@ -325,4 +326,22 @@ class SQLServerSchemaManager extends AbstractSchemaManager
)
);
}
/**
* @param string $tableName
*/
public function listTableDetails($tableName) : Table
{
$table = parent::listTableDetails($tableName);
/** @var SQLServerPlatform $platform */
$platform = $this->_platform;
$sql = $platform->getListTableMetadataSQL($tableName);
$tableOptions = $this->_conn->fetchAssoc($sql);
$table->addOption('comment', $tableOptions['table_comment']);
return $table;
}
}
......@@ -470,6 +470,25 @@ class SqliteSchemaManager extends AbstractSchemaManager
return $match[1];
}
private function parseTableCommentFromSQL(string $table, string $sql) : ?string
{
$pattern = '/\s* # Allow whitespace characters at start of line
CREATE\sTABLE # Match "CREATE TABLE"
(?:\W"' . preg_quote($this->_platform->quoteSingleIdentifier($table), '/') . '"\W|\W' . preg_quote($table, '/')
. '\W) # Match table name (quoted and unquoted)
( # Start capture
(?:\s*--[^\n]*\n?)+ # Capture anything that starts with whitespaces followed by -- until the end of the line(s)
)/ix';
if (preg_match($pattern, $sql, $match) !== 1) {
return null;
}
$comment = preg_replace('{^\s*--}m', '', rtrim($match[1], "\n"));
return $comment === '' ? null : $comment;
}
private function parseColumnCommentFromSQL(string $column, string $sql) : ?string
{
$pattern = '{[\s(,](?:\W' . preg_quote($this->_platform->quoteSingleIdentifier($column)) . '\W|\W' . preg_quote($column)
......@@ -503,4 +522,22 @@ SQL
[$table]
) ?: null;
}
/**
* @param string $tableName
*/
public function listTableDetails($tableName) : Table
{
$table = parent::listTableDetails($tableName);
$tableCreateSql = $this->getCreateTableSQL($tableName) ?? '';
$comment = $this->parseTableCommentFromSQL($tableName, $tableCreateSql);
if ($comment !== null) {
$table->addOption('comment', $comment);
}
return $table;
}
}
......@@ -840,4 +840,17 @@ class Table extends AbstractAsset
return $this->trimQuotes(strtolower($identifier));
}
public function setComment(?string $comment) : self
{
// For keeping backward compatibility with MySQL in previous releases, table comments are stored as options.
$this->addOption('comment', $comment);
return $this;
}
public function getComment() : ?string
{
return $this->_options['comment'] ?? null;
}
}
......@@ -61,4 +61,9 @@ class SQLAnywhereSchemaManagerTest extends SchemaManagerFunctionalTestCase
self::assertArrayHasKey('test', $columns);
self::assertTrue($columns['test']->getFixed());
}
public function testCommentInTable() : void
{
self::markTestSkipped('Table level comments are not supported on SQLAnywhere');
}
}
......@@ -1594,4 +1594,15 @@ abstract class SchemaManagerFunctionalTestCase extends DbalFunctionalTestCase
$onlineTable = $this->schemaManager->listTableDetails('test_partial_column_index');
self::assertEquals($expected, $onlineTable->getIndexes());
}
public function testCommentInTable() : void
{
$table = new Table('table_with_comment');
$table->addColumn('id', 'integer');
$table->setComment('Foo with control characters \'\\');
$this->schemaManager->dropAndCreateTable($table);
$table = $this->schemaManager->listTableDetails('table_with_comment');
self::assertSame('Foo with control characters \'\\', $table->getComment());
}
}
......@@ -4,6 +4,7 @@ namespace Doctrine\Tests\DBAL\Platforms;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Platforms\PostgreSqlPlatform;
use Doctrine\DBAL\Schema\Table;
class PostgreSqlPlatformTest extends AbstractPostgreSqlPlatformTestCase
{
......@@ -16,4 +17,19 @@ class PostgreSqlPlatformTest extends AbstractPostgreSqlPlatformTestCase
{
self::assertTrue($this->platform->supportsPartialIndexes());
}
public function testGetCreateTableSQLWithColumnCollation() : void
{
$table = new Table('foo');
$table->addColumn('id', 'string');
$table->addOption('comment', 'foo');
self::assertSame(
[
'CREATE TABLE foo (id VARCHAR(255) NOT NULL)',
"COMMENT ON TABLE foo IS 'foo'",
],
$this->platform->getCreateTableSQL($table),
'Comments are added to table.'
);
}
}
......@@ -885,4 +885,13 @@ class TableTest extends DbalTestCase
['"FOO"'],
];
}
public function testTableComment() : void
{
$table = new Table('bar');
self::assertNull($table->getComment());
$table->setComment('foo');
self::assertEquals('foo', $table->getComment());
}
}
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