Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support table and column comments #286

Draft
wants to merge 7 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 85 additions & 6 deletions src/Schema.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@
use function strncasecmp;
use function strtolower;

use const PREG_SET_ORDER;

/**
* Implements the SQLite Server specific schema, supporting SQLite 3.3.0 or higher.
*
Expand Down Expand Up @@ -155,8 +157,11 @@ protected function loadTableSchema(string $name): TableSchemaInterface|null

$table->name($name);
$table->fullName($name);
$table->createSql($this->getCreateTableSql($name));
$this->findTableComment($table);

if ($this->findColumns($table)) {
$this->findComments($table);
$this->findConstraints($table);

return $table;
Expand Down Expand Up @@ -294,12 +299,7 @@ protected function loadTableUniques(string $tableName): array
*/
protected function loadTableChecks(string $tableName): array
{
$sql = $this->db->createCommand(
'SELECT `sql` FROM `sqlite_master` WHERE name = :tableName',
[':tableName' => $tableName],
)->queryScalar();

$sql = ($sql === false || $sql === null) ? '' : (string) $sql;
$sql = $this->getCreateTableSql($tableName);

$code = (new SqlTokenizer($sql))->tokenize();
$pattern = (new SqlTokenizer('any CREATE any TABLE any()'))->tokenize();
Expand Down Expand Up @@ -741,6 +741,85 @@ protected function getCacheTag(): string
return md5(serialize(array_merge([self::class], $this->generateCacheKey())));
}

/**
* @throws Exception
* @throws InvalidConfigException
* @throws Throwable
*/
private function findTableComment(TableSchemaInterface $tableSchema): void
{
$sql = $tableSchema->getCreateSql() ?? '';

if (preg_match('#^[^(]+?((?:\s*--[^\n]*|\s*/\*.*?\*/)+)\s*\(#', $sql, $matches) === 1) {
$comment = $this->filterComment($matches[1]);
$tableSchema->comment($comment);
}
}

/**
* @throws Exception
* @throws InvalidConfigException
* @throws Throwable
*/
private function findComments(TableSchemaInterface $tableSchema): void
{
$sql = $tableSchema->getCreateSql() ?? '';

if (!preg_match('#^(?:[^(]*--[^\n]*|[^(]*/\*.*?\*/)*[^(]*\((.*)\)[^)]*$#s', $sql, $matches)) {
return;
}

$columnsDefinition = $matches[1];

$identifierPattern = '(?:([`"])([^`"]+)\1|\[([^\]]+)\]|([a-zA-Z_]\w*))';
$notCommaPattern = "(?:[^,]|\([^()]+\)|'[^']+')*?";
$commentPattern = '(?:\s*--[^\n]*|\s*/\*.*?\*/)';

$pattern = "#$identifierPattern\s*$notCommaPattern,?($commentPattern+)#";

if (preg_match_all($pattern, $columnsDefinition, $matches, PREG_SET_ORDER)) {
foreach ($matches as $match) {
$columnName = ($match[2] ?: $match[3]) ?: $match[4];
$comment = $this->filterComment($match[5] ?: $match[6]);

$tableSchema->getColumn($columnName)?->comment($comment);
}
}
}

private function filterComment(string $comment): string
{
preg_match_all('#--([^\n]*)|/\*(.*?)\*/#', $comment, $matches, PREG_SET_ORDER);

$lines = [];

foreach ($matches as $match) {
$lines[] = trim($match[1] ?: $match[2]);
}

return implode("\n", $lines);
}

/**
* Returns the `CREATE TABLE` SQL string.
*
* @param string $name The table name.
*
* @throws Exception
* @throws InvalidConfigException
* @throws Throwable
* @return string The `CREATE TABLE` SQL string.
*/
private function getCreateTableSql(string $name): string
{
$sql = $this->db->createCommand(
'SELECT `sql` FROM `sqlite_master` WHERE `name` = :tableName',
[':tableName' => $name],
)->queryScalar();

return (string)$sql;
}

/**
* @throws Throwable
*/
Expand Down
20 changes: 20 additions & 0 deletions tests/SchemaTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -359,4 +359,24 @@ public function testNotConnectionPDO(): void

$schema->refreshTableSchema('customer');
}

public function testTableComment(): void
{
$db = $this->getConnection(true);
$schema = $db->getSchema();
$table = $schema->getTableSchema('comment');

$this->assertSame("Table comment\nsecond line\nthird line", $table->getComment());
}

public function testColumnComments(): void
{
$db = $this->getConnection(true);
$schema = $db->getSchema();
$table = $schema->getTableSchema('comment');

$this->assertSame('primary key', $table->getColumn('id')->getComment());
$this->assertSame('USD', $table->getColumn('price')->getComment());
$this->assertSame("Column comment\nsecond line\nthird line", $table->getColumn('name')->getComment());
}
}
12 changes: 12 additions & 0 deletions tests/Support/Fixture/sqlite.sql
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ DROP TABLE IF EXISTS "negative_default_values";
DROP TABLE IF EXISTS "animal";
DROP TABLE IF EXISTS "default_pk";
DROP TABLE IF EXISTS "notauto_pk";
DROP TABLE IF EXISTS "comment";
DROP VIEW IF EXISTS "animal_view";
DROP TABLE IF EXISTS "T_constraints_4";
DROP TABLE IF EXISTS "T_constraints_3";
Expand Down Expand Up @@ -173,6 +174,17 @@ CREATE TABLE "timestamp_default" (
timestamp_text TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
); -- STRICT

CREATE TABLE `comment` -- Table comment
-- second line
/* third line */
(
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL, -- primary key
price decimal(10,2), -- USD
`name` varchar(100) DEFAULT 'Pan, Peter' -- Column comment
-- second line
/* third line */
);

CREATE VIEW "animal_view" AS SELECT * FROM "animal";

INSERT INTO "animal" ("type") VALUES ('yiiunit\data\ar\Cat');
Expand Down
Loading