Skip to content

Fixes invalid T-SQL column comment for quoted table names.#4284

Closed
metaturso wants to merge 1 commit intodoctrine:2.12.xfrom
metaturso:fix/tsql-comment-column
Closed

Fixes invalid T-SQL column comment for quoted table names.#4284
metaturso wants to merge 1 commit intodoctrine:2.12.xfrom
metaturso:fix/tsql-comment-column

Conversation

@metaturso
Copy link
Copy Markdown

Q A
Type bug
BC Break no
Fixed issues #4283

Summary

This merge request fixes the issue whereby a quoted table or schema name was getting converted into a string literal, resulting in a T-SQL statement referencing an invalid table name like N'[table_name]'.

The issues happened whenever the entity table name contained Quoted Reserved Words, for example:

/**
 * @ORM\Table(name="`the-model-table`")
 */
class TheModel
{
}

The issue is fixed within the SQLServer2012Platform base class by inserting the schema or table names as T-SQL identifiers when generating the call to sp_addextendedproperty for quoted names.

In other words, because Doctrine converts a quoted name like `table-name` into a valid T-SQL identifier like [table-name] we needn't wrap the identifier into a string literal before passing it to getAddExtendedPropertySQL.

@greg0ire
Copy link
Copy Markdown
Member

I think you should target 2.11.x

@metaturso metaturso changed the base branch from master to 2.11.x September 22, 2020 15:35
@metaturso
Copy link
Copy Markdown
Author

I rebased my branch onto 2.11.x. (git rebase --onto 2.11.x master fix/tsql-comment-column) and it's possible that this didn't play nice with AppVeyor's mergeability checks.

I'm happy to open a fresh MR targeting the desired branch if you prefer.

There's a few places left in SQLServerPlatform that need the same fix (e.g. callers of get*ExtendedPropertySQL methods.) I'll write tests and fix for those too.

How would you feel about a method which initialises both variables at once, e.g. [$schemaSQL, $tableSQL] = $this->doodad($tableName)?

@greg0ire
Copy link
Copy Markdown
Member

I'm happy to open a fresh MR targeting the desired branch if you prefer.

Please don't, it's great the way it is 👍

I will close and reopen, it might help with the checks.

@greg0ire greg0ire closed this Sep 22, 2020
@greg0ire greg0ire reopened this Sep 22, 2020
Comment on lines +29 to +35
$createTableSQL = $this->getCreateTableSQL('table_name');

self::assertEquals(
"EXEC sp_addextendedproperty N'MS_Description', N'(DC2Type:array)', "
. "N'SCHEMA', 'dbo', N'TABLE', 'table_name', N'COLUMN', column_name",
$createTableSQL
);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
$createTableSQL = $this->getCreateTableSQL('table_name');
self::assertEquals(
"EXEC sp_addextendedproperty N'MS_Description', N'(DC2Type:array)', "
. "N'SCHEMA', 'dbo', N'TABLE', 'table_name', N'COLUMN', column_name",
$createTableSQL
);
self::assertEquals(
"EXEC sp_addextendedproperty N'MS_Description', N'(DC2Type:array)', "
. "N'SCHEMA', 'dbo', N'TABLE', 'table_name', N'COLUMN', column_name",
$this->getCreateTableSQL('table_name')
);

Comment on lines +40 to +47
$createTableSQL = $this->getCreateTableSQL('`quoted-table-name`');

self::assertEquals(
"EXEC sp_addextendedproperty N'MS_Description', N'(DC2Type:array)', "
. "N'SCHEMA', 'dbo', N'TABLE', [quoted-table-name], N'COLUMN', column_name",
$createTableSQL
);
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
$createTableSQL = $this->getCreateTableSQL('`quoted-table-name`');
self::assertEquals(
"EXEC sp_addextendedproperty N'MS_Description', N'(DC2Type:array)', "
. "N'SCHEMA', 'dbo', N'TABLE', [quoted-table-name], N'COLUMN', column_name",
$createTableSQL
);
}
self::assertEquals(
"EXEC sp_addextendedproperty N'MS_Description', N'(DC2Type:array)', "
. "N'SCHEMA', 'dbo', N'TABLE', [quoted-table-name], N'COLUMN', column_name",
$this->getCreateTableSQL('`quoted-table-name`')
);
}

Comment on lines +51 to +57
$createTableSQL = $this->getCreateTableSQL('`custom.quoted-table-name`');

self::assertEquals(
"EXEC sp_addextendedproperty N'MS_Description', N'(DC2Type:array)', "
. "N'SCHEMA', [custom], N'TABLE', [quoted-table-name], N'COLUMN', column_name",
$createTableSQL
);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
$createTableSQL = $this->getCreateTableSQL('`custom.quoted-table-name`');
self::assertEquals(
"EXEC sp_addextendedproperty N'MS_Description', N'(DC2Type:array)', "
. "N'SCHEMA', [custom], N'TABLE', [quoted-table-name], N'COLUMN', column_name",
$createTableSQL
);
self::assertEquals(
"EXEC sp_addextendedproperty N'MS_Description', N'(DC2Type:array)', "
. "N'SCHEMA', [custom], N'TABLE', [quoted-table-name], N'COLUMN', column_name",
$this->getCreateTableSQL('`custom.quoted-table-name`')
);

. "N'SCHEMA', [custom], N'TABLE', [quoted-table-name], N'COLUMN', column_name",
$createTableSQL
);
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it make sense to use a data provider instead? All 3 tests have the same logic, it would be great to be able to see just what changes in one place.


public function getCreateTableSQL(string $quotedName): string
{
$driver = $this->createMock(Driver::class);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since you perform no expectations, this is a stub:

Suggested change
$driver = $this->createMock(Driver::class);
$driver = $this->createStub(Driver::class);

@morozov
Copy link
Copy Markdown
Member

morozov commented Oct 4, 2020

This change requires a functional test that runs the updated SQL queries by a real database, not making assertions against the generated SQL and using mocks.

@metaturso
Copy link
Copy Markdown
Author

Couldn't agree more!

The proposed unit test is good enough to show the problem exists not prove that it's fixed. In that sense, it helped expose the fact that the ALTER and DROP column/table cases are also bugged.

I won't get another chance to work on this probably until next weekend, when I plan to begin writing the new integration test against a real SQL Server instance before attempting a fix. I'll try to push the new version of the unit failing unit test sooner than that to arouse discussion about a clean fix.

In the meantime, could you let me know your requirements or preferences for this new test? For example, could you point me to an existing test that I can or should use as reference for level of abstraction, structure, assertion style, etc., just to get an idea?

On a related note, is the current Travis CI pipeline already configured to run SQL Server tests?

Thanks in advance for your time.

@morozov
Copy link
Copy Markdown
Member

morozov commented Oct 5, 2020

In the meantime, could you let me know your requirements or preferences for this new test? For example, could you point me to an existing test that I can or should use as reference for level of abstraction, structure, assertion style, etc., just to get an idea?

As far as I understand the problem, you'll need to create a table with a name that has to be quoted. Look for the tests that call dropAndCreateTable() under tests/Doctrine/Tests/DBAL/Functional.

Please implement it in a platform-agnostic way (e.g. see DefaultValueTest for example). Even if it fails on certain platforms, we can mark it incomplete there which would effectively document newly discovered bugs.

On a related note, is the current Travis CI pipeline already configured to run SQL Server tests?

Yes, see the Travis and AppVeyor builds.

$tableSQL = $this->quoteStringLiteral($tableName);
}

if (strpos($tableName, '[') === false) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should preg_replace('~^\[|\]$~s', '', $tableName) instead and quote as literal as we would normally do.

String literal seeems to be the official way to escape: https://docs.microsoft.com/en-us/sql/relational-databases/system-stored-procedures/sp-addextendedproperty-transact-sql?view=sql-server-ver15#b-adding-an-extended-property-to-a-column-in-a-table

Copy link
Copy Markdown
Author

@metaturso metaturso Oct 21, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@metaturso please see #4360 , I think we need to fix in a little more abstract way - please provide feedback, if you want to change anything or help with the tests, please send a PR directly to my branch. Michael

@mvorisek thanks for stepping in with a patch for this, chap. Your fix in PR #4360 is reasonably self-contained and I'm happy to ditch my changes in favour of yours.

There's still something about the amount of (un)escaping that's done at multiple levels that doesn't completely sit right with me, but fixing the data-flow is a much more pervasive change that's likely to break B/C or third-party code so I can't offer a better alternative.

I didn't get a chance to sit down and write the promised integration test. I'll commit and push the fixed version of the unit test, which runs against 2.11.x version of the codebase, during my lunch break. Feel free to steal this from my PR if that's of any use to you.

Should you decide not to use my test, please don't forget to cover for #4283 by adding test cases or fixture using i) table names with a custom schema name and ii) containing special characters or reserved keywords to your pull request.

@mvorisek
Copy link
Copy Markdown
Contributor

@metaturso please see #4360 , I think we need to fix in a little more abstract way - please provide feedback, if you want to change anything or help with the tests, please send a PR directly to my branch. Michael

@metaturso
Copy link
Copy Markdown
Author

I've applied all suggestions to the unit test and updated it to cover the add, update and drop Extended Property cases, @morozov and @greg0ire thanks for your feedback and suggestions.

All changes to the SQL Server platform class have been removed from this branch because @mvorisek has already raised a comprehensive fix to the original issue (thanks for that.) The failing unit test has been left here for his convenience.

I reckon this PR can now be closed in favour of #4360.

@mvorisek
Copy link
Copy Markdown
Contributor

@metaturso please submit the tests as PR into https://github.com/mvorisek/dbal/tree/fix_mssql_comment_with_escaped_column branch, so your work can be integrated into #4360

@metaturso
Copy link
Copy Markdown
Author

Will do shortly. Close this PR at your discretion.

@morozov morozov changed the base branch from 2.11.x to 2.12.x November 29, 2020 21:38
@morozov morozov closed this Apr 21, 2021
@morozov morozov deleted the branch doctrine:2.12.x April 21, 2021 16:26
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Jul 22, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants