mirror of
https://github.com/yiisoft/yii2.git
synced 2025-11-03 13:58:55 +08:00
Fixes #17057: Fixed issues with table names that contain special characters or keywords in MSSQL
This commit is contained in:
committed by
Alexander Makarov
parent
fc29f5f843
commit
eb65dba796
@ -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
|
||||
|
||||
@ -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);
|
||||
}
|
||||
@ -493,6 +492,17 @@ abstract class Schema extends BaseObject
|
||||
return implode('.', $parts);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
@ -661,7 +671,7 @@ abstract class Schema extends BaseObject
|
||||
}
|
||||
$message = $e->getMessage() . "\nThe SQL being executed was: $rawSql";
|
||||
$errorInfo = $e instanceof \PDOException ? $e->errorInfo : null;
|
||||
return new $exceptionClass($message, $errorInfo, (int) $e->getCode(), $e);
|
||||
return new $exceptionClass($message, $errorInfo, (int)$e->getCode(), $e);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -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
|
||||
@ -126,6 +126,25 @@ class Schema extends \yii\db\Schema implements ConstraintFinderInterface
|
||||
return $resolvedName;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@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
|
||||
@ -223,8 +242,8 @@ SQL;
|
||||
$result = [];
|
||||
foreach ($indexes as $name => $index) {
|
||||
$result[] = new IndexConstraint([
|
||||
'isPrimary' => (bool) $index[0]['index_is_primary'],
|
||||
'isUnique' => (bool) $index[0]['index_is_unique'],
|
||||
'isPrimary' => (bool)$index[0]['index_is_primary'],
|
||||
'isUnique' => (bool)$index[0]['index_is_unique'],
|
||||
'name' => $name,
|
||||
'columnNames' => ArrayHelper::getColumn($index, 'column_name'),
|
||||
]);
|
||||
@ -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
|
||||
@ -349,9 +368,9 @@ SQL;
|
||||
}
|
||||
if (!empty($matches[2])) {
|
||||
$values = explode(',', $matches[2]);
|
||||
$column->size = $column->precision = (int) $values[0];
|
||||
$column->size = $column->precision = (int)$values[0];
|
||||
if (isset($values[1])) {
|
||||
$column->scale = (int) $values[1];
|
||||
$column->scale = (int)$values[1];
|
||||
}
|
||||
if ($column->size === 1 && ($type === 'tinyint' || $type === 'bit')) {
|
||||
$column->type = 'boolean';
|
||||
|
||||
@ -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]
|
||||
);
|
||||
|
||||
@ -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'],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@ -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`'],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user