Browse Source

Fixes #17057: Fixed issues with table names that contain special characters or keywords in MSSQL

tags/2.0.22
Alexander Kartavenko 5 years ago committed by Alexander Makarov
parent
commit
eb65dba796
  1. 1
      framework/CHANGELOG.md
  2. 14
      framework/db/Schema.php
  3. 23
      framework/db/mssql/Schema.php
  4. 9
      tests/data/mssql.sql
  5. 50
      tests/framework/db/mssql/SchemaTest.php
  6. 25
      tests/framework/db/sqlite/SchemaTest.php

1
framework/CHANGELOG.md

@ -10,6 +10,7 @@ Yii Framework 2 Change Log
- Enh #17396: Added 'invoked by controller' to the debug log message when `\yii\base\Action` is used (alexkart)
- Bug #17325: Fixed "Cannot drop view" for MySQL while `migrate/fresh` (alexkart)
- Bug #17384: Fixed SQL error when passing `DISTINCT ON` queries (brandonkelly)
- Bug #17057: Fixed issues with table names that contain special characters or keywords in MSSQL (alexkart)
2.0.21 June 18, 2019

14
framework/db/Schema.php

@ -15,7 +15,6 @@ use yii\base\NotSupportedException;
use yii\caching\Cache;
use yii\caching\CacheInterface;
use yii\caching\TagDependency;
use yii\helpers\StringHelper;
/**
* Schema is the base class for concrete DBMS-specific schema classes.
@ -485,7 +484,7 @@ abstract class Schema extends BaseObject
if (strpos($name, '.') === false) {
return $this->quoteSimpleTableName($name);
}
$parts = explode('.', $name);
$parts = $this->getTableNameParts($name);
foreach ($parts as $i => $part) {
$parts[$i] = $this->quoteSimpleTableName($part);
}
@ -494,6 +493,17 @@ abstract class Schema extends BaseObject
}
/**
* Splits full table name into parts
* @param string $name
* @return array
* @since 2.0.22
*/
protected function getTableNameParts($name)
{
return explode('.', $name);
}
/**
* Quotes a column name for use in a query.
* If the column name contains prefix, the prefix will also be properly quoted.
* If the column name is already quoted or contains '(', '[[' or '{{',

23
framework/db/mssql/Schema.php

@ -98,7 +98,7 @@ class Schema extends \yii\db\Schema implements ConstraintFinderInterface
protected function resolveTableName($name)
{
$resolvedName = new TableSchema();
$parts = explode('.', str_replace(['[', ']'], '', $name));
$parts = $this->getTableNameParts($name);
$partCount = count($parts);
if ($partCount === 4) {
// server name, catalog name, schema name and table name passed
@ -127,6 +127,25 @@ class Schema extends \yii\db\Schema implements ConstraintFinderInterface
}
/**
* {@inheritDoc}
* @param string $name
* @return array
* @since 2.0.22
*/
protected function getTableNameParts($name)
{
$parts = [$name];
preg_match_all('/([^.\[\]]+)|\[([^\[\]]+)\]/', $name, $matches);
if (isset($matches[0]) && is_array($matches[0]) && !empty($matches[0])) {
$parts = $matches[0];
}
$parts = str_replace(['[', ']'], '', $parts);
return $parts;
}
/**
* {@inheritdoc}
* @see https://docs.microsoft.com/en-us/sql/relational-databases/system-catalog-views/sys-database-principals-transact-sql
*/
@ -297,7 +316,7 @@ SQL;
*/
protected function resolveTableNames($table, $name)
{
$parts = explode('.', str_replace(['[', ']'], '', $name));
$parts = $this->getTableNameParts($name);
$partCount = count($parts);
if ($partCount === 4) {
// server name, catalog name, schema name and table name passed

9
tests/data/mssql.sql

@ -21,6 +21,7 @@ IF OBJECT_ID('[T_constraints_3]', 'U') IS NOT NULL DROP TABLE [T_constraints_3];
IF OBJECT_ID('[T_constraints_2]', 'U') IS NOT NULL DROP TABLE [T_constraints_2];
IF OBJECT_ID('[T_constraints_1]', 'U') IS NOT NULL DROP TABLE [T_constraints_1];
IF OBJECT_ID('[T_upsert]', 'U') IS NOT NULL DROP TABLE [T_upsert];
IF OBJECT_ID('[table.with.special.characters]', 'U') IS NOT NULL DROP TABLE [table.with.special.characters];
CREATE TABLE [dbo].[profile] (
[id] [int] IDENTITY NOT NULL,
@ -89,7 +90,9 @@ CREATE TABLE [dbo].[order_item] (
[item_id] ASC
) ON [PRIMARY]
);CREATE TABLE [dbo].[order_item_with_null_fk] (
);
CREATE TABLE [dbo].[order_item_with_null_fk] (
[order_id] [int],
[item_id] [int],
[quantity] [int] NOT NULL,
@ -311,3 +314,7 @@ CREATE TABLE [T_upsert]
[profile_id] INT NULL,
UNIQUE ([email], [recovery_email])
);
CREATE TABLE [dbo].[table.with.special.characters] (
[id] [int]
);

50
tests/framework/db/mssql/SchemaTest.php

@ -83,4 +83,54 @@ class SchemaTest extends \yiiunit\framework\db\SchemaTest
}
}
}
/**
* @dataProvider quoteTableNameDataProvider
* @param $name
* @param $expectedName
* @throws \yii\base\NotSupportedException
*/
public function testQuoteTableName($name, $expectedName)
{
$schema = $this->getConnection()->getSchema();
$quotedName = $schema->quoteTableName($name);
$this->assertEquals($expectedName, $quotedName);
}
public function quoteTableNameDataProvider()
{
return [
['test', '[test]'],
['test.test', '[test].[test]'],
['test.test.test', '[test].[test].[test]'],
['[test]', '[test]'],
['[test].[test]', '[test].[test]'],
['test.[test.test]', '[test].[test.test]'],
['test.test.[test.test]', '[test].[test].[test.test]'],
['[test].[test.test]', '[test].[test.test]'],
];
}
/**
* @dataProvider getTableSchemaDataProvider
* @param $name
* @param $expectedName
* @throws \yii\base\NotSupportedException
*/
public function testGetTableSchema($name, $expectedName)
{
$schema = $this->getConnection()->getSchema();
$tableSchema = $schema->getTableSchema($name);
$this->assertEquals($expectedName, $tableSchema->name);
}
public function getTableSchemaDataProvider()
{
return [
['[dbo].[profile]', 'profile'],
['dbo.profile', 'profile'],
['profile', 'profile'],
['dbo.[table.with.special.characters]', 'table.with.special.characters'],
];
}
}

25
tests/framework/db/sqlite/SchemaTest.php

@ -82,4 +82,29 @@ class SchemaTest extends \yiiunit\framework\db\SchemaTest
return $result;
}
/**
* @dataProvider quoteTableNameDataProvider
* @param $name
* @param $expectedName
* @throws \yii\base\NotSupportedException
*/
public function testQuoteTableName($name, $expectedName)
{
$schema = $this->getConnection()->getSchema();
$quotedName = $schema->quoteTableName($name);
$this->assertEquals($expectedName, $quotedName);
}
public function quoteTableNameDataProvider()
{
return [
['test', '`test`'],
['test.test', '`test`.`test`'],
['test.test.test', '`test`.`test`.`test`'],
['`test`', '`test`'],
['`test`.`test`', '`test`.`test`'],
['test.`test`.test', '`test`.`test`.`test`'],
];
}
}

Loading…
Cancel
Save