diff --git a/src/Quoter.php b/src/Quoter.php index 3ff2f1952..cc07dc7aa 100644 --- a/src/Quoter.php +++ b/src/Quoter.php @@ -5,13 +5,12 @@ namespace Yiisoft\Db\Mssql; use Yiisoft\Db\Schema\Quoter as BaseQuoter; -use Yiisoft\Db\Schema\QuoterInterface; use function preg_match; use function preg_match_all; use function str_replace; -final class Quoter extends BaseQuoter implements QuoterInterface +final class Quoter extends BaseQuoter { /** * @psalm-param string[] $columnQuoteCharacter @@ -25,24 +24,24 @@ public function __construct( parent::__construct($columnQuoteCharacter, $tableQuoteCharacter, $tablePrefix); } - public function quoteColumnName(string $name): string - { - if (preg_match('/^\[.*]$/', $name)) { - return $name; - } - - return parent::quoteColumnName($name); - } - - protected function getTableNameParts(string $name): array + public function getTableNameParts(string $name): array { $parts = [$name]; preg_match_all('/([^.\[\]]+)|\[([^\[\]]+)]/', $name, $matches); if (isset($matches[0]) && !empty($matches[0])) { - $parts = $matches[0]; + $parts = array_slice($matches[0], -4, 4, true); } return str_replace(['[', ']'], '', $parts); } + + public function quoteColumnName(string $name): string + { + if (preg_match('/^\[.*]$/', $name)) { + return $name; + } + + return parent::quoteColumnName($name); + } } diff --git a/src/Schema.php b/src/Schema.php index 592d57380..e1430081d 100644 --- a/src/Schema.php +++ b/src/Schema.php @@ -6,7 +6,6 @@ use Throwable; use Yiisoft\Arrays\ArrayHelper; -use Yiisoft\Db\Cache\SchemaCache; use Yiisoft\Db\Connection\ConnectionInterface; use Yiisoft\Db\Constraint\CheckConstraint; use Yiisoft\Db\Constraint\Constraint; @@ -18,19 +17,16 @@ use Yiisoft\Db\Schema\ColumnSchemaBuilder; use Yiisoft\Db\Schema\ColumnSchemaInterface; use Yiisoft\Db\Schema\Schema as AbstractSchema; +use Yiisoft\Db\Schema\TableNameInterface; use Yiisoft\Db\Schema\TableSchemaInterface; use function array_change_key_case; use function array_map; -use function count; use function explode; use function is_array; use function md5; use function preg_match; -use function preg_match_all; -use function preg_replace; use function serialize; -use function str_contains; use function str_replace; use function strcasecmp; use function stripos; @@ -128,88 +124,26 @@ final class Schema extends AbstractSchema 'table' => self::TYPE_STRING, ]; - public function __construct(private ConnectionInterface $db, SchemaCache $schemaCache) - { - parent::__construct($schemaCache); - } - /** * Resolves the table name and schema name (if any). * - * @param string $name the table name. + * @param TableNameInterface $name the table name. * * @return TableSchemaInterface resolved table, schema, etc. names. * - * @todo Review this method and see if it can be simplified @darkdef. - * also see case `wrongBehaviour` in \Yiisoft\Db\TestSupport\TestCommandTrait::batchInsertSqlProviderTrait + * @todo also see case `wrongBehaviour` in \Yiisoft\Db\TestSupport\TestCommandTrait::batchInsertSqlProviderTrait */ - protected function resolveTableName(string $name): TableSchemaInterface + protected function resolveTableName(TableNameInterface $name): TableSchemaInterface { $resolvedName = new TableSchema(); - $parts = $this->getTableNameParts($name); - $partCount = count($parts); - - if ($partCount === 4) { - /** server name, catalog name, schema name and table name passed - not coverage tests */ - $resolvedName->catalogName($parts[1]); - $resolvedName->schemaName($parts[2]); - $resolvedName->name($parts[3]); - $resolvedName->fullName( - (string) $resolvedName->getCatalogName() . '.' . - (string) $resolvedName->getSchemaName() . '.' . - $resolvedName->getName() - ); - } elseif ($partCount === 3) { - /** catalog name, schema name and table name passed - not coverage tests */ - $resolvedName->catalogName($parts[0]); - $resolvedName->schemaName($parts[1]); - $resolvedName->name($parts[2]); - $resolvedName->fullName( - (string) $resolvedName->getCatalogName() . '.' . - (string) $resolvedName->getSchemaName() . '.' . - $resolvedName->getName() - ); - } elseif ($partCount === 2) { - /** only schema name and table name passed - not coverage tests */ - $resolvedName->schemaName($parts[0]); - $resolvedName->name($parts[1]); - $resolvedName->fullName( - ( - $resolvedName->getSchemaName() !== $this->defaultSchema - ? (string) $resolvedName->getSchemaName() . '.' : '' - ) . $resolvedName->getName() - ); - } else { - /** only table name passed */ - $resolvedName->schemaName($this->defaultSchema); - $resolvedName->name($parts[0]); - $resolvedName->fullName($resolvedName->getName()); - } - return $resolvedName; - } + $resolvedName->serverName($name->getServerName()); + $resolvedName->catalogName($name->getCatalogName()); + $resolvedName->schemaName($name->getSchemaName() ?? $this->defaultSchema); + $resolvedName->name($name->getTableName()); + $resolvedName->fullName((string) $name); - /** - * Splits full table name into parts. - * - * @param string $name - * - * @return array - * - * @psalm-return string[] - */ - protected function getTableNameParts(string $name): array - { - $parts = [$name]; - - preg_match_all('/([^.\[\]]+)|\[([^\[\]]+)]/', $name, $matches); - - if (isset($matches[0]) && !empty($matches[0])) { - $parts = $matches[0]; - } - - /** @psalm-var string[] */ - return str_replace(['[', ']'], '', $parts); + return $resolvedName; } /** @@ -270,13 +204,13 @@ protected function findTableNames(string $schema = ''): array /** * Loads the metadata for the specified table. * - * @param string $name table name. + * @param TableNameInterface $name table name. * * @throws Exception|InvalidConfigException|Throwable * * @return TableSchemaInterface|null DBMS-dependent table metadata, `null` if the table does not exist. */ - protected function loadTableSchema(string $name): ?TableSchemaInterface + protected function loadTableSchema(TableNameInterface $name): ?TableSchemaInterface { $table = $this->resolveTableName($name); $this->findPrimaryKeys($table); @@ -292,13 +226,13 @@ protected function loadTableSchema(string $name): ?TableSchemaInterface /** * Loads a primary key for the given table. * - * @param string $tableName table name. + * @param TableNameInterface $tableName table name. * * @throws Exception|InvalidConfigException|Throwable * * @return Constraint|null The primary key for the given table, `null` if the table has no primary key. */ - protected function loadTablePrimaryKey(string $tableName): ?Constraint + protected function loadTablePrimaryKey(TableNameInterface $tableName): ?Constraint { /** @var mixed */ $tablePrimaryKey = $this->loadTableConstraints($tableName, self::PRIMARY_KEY); @@ -308,13 +242,13 @@ protected function loadTablePrimaryKey(string $tableName): ?Constraint /** * Loads all foreign keys for the given table. * - * @param string $tableName table name. + * @param TableNameInterface $tableName table name. * * @throws Exception|InvalidConfigException|Throwable * * @return array The foreign keys for the given table. */ - protected function loadTableForeignKeys(string $tableName): array + protected function loadTableForeignKeys(TableNameInterface $tableName): array { /** @var mixed */ $tableForeingKeys = $this->loadTableConstraints($tableName, self::FOREIGN_KEYS); @@ -324,13 +258,13 @@ protected function loadTableForeignKeys(string $tableName): array /** * Loads all indexes for the given table. * - * @param string $tableName table name. + * @param TableNameInterface $tableName table name. * * @throws Exception|InvalidConfigException|Throwable * * @return array indexes for the given table. */ - protected function loadTableIndexes(string $tableName): array + protected function loadTableIndexes(TableNameInterface $tableName): array { $sql = <<resolveTableName($tableName); + // @todo check fullName or name???? $indexes = $this->db->createCommand($sql, [':fullName' => $resolvedName->getFullName()])->queryAll(); /** @psalm-var array[] $indexes */ @@ -379,13 +314,13 @@ protected function loadTableIndexes(string $tableName): array /** * Loads all unique constraints for the given table. * - * @param string $tableName table name. + * @param TableNameInterface $tableName table name. * * @throws Exception|InvalidConfigException|Throwable * * @return array The unique constraints for the given table. */ - protected function loadTableUniques(string $tableName): array + protected function loadTableUniques(TableNameInterface $tableName): array { /** @var mixed */ $tableUniques = $this->loadTableConstraints($tableName, self::UNIQUES); @@ -395,13 +330,13 @@ protected function loadTableUniques(string $tableName): array /** * Loads all check constraints for the given table. * - * @param string $tableName table name. + * @param TableNameInterface $tableName table name. * * @throws Exception|InvalidConfigException|Throwable * * @return array The check constraints for the given table. */ - protected function loadTableChecks(string $tableName): array + protected function loadTableChecks(TableNameInterface $tableName): array { /** @var mixed */ $tableCheck = $this->loadTableConstraints($tableName, self::CHECKS); @@ -411,13 +346,13 @@ protected function loadTableChecks(string $tableName): array /** * Loads all default value constraints for the given table. * - * @param string $tableName table name. + * @param TableNameInterface $tableName table name. * * @throws Exception|InvalidConfigException|Throwable * * @return array The default value constraints for the given table. */ - protected function loadTableDefaultValues(string $tableName): array + protected function loadTableDefaultValues(TableNameInterface $tableName): array { /** @var mixed */ $tableDefault = $this->loadTableConstraints($tableName, self::DEFAULTS); @@ -766,7 +701,7 @@ public function findUniqueIndexes(TableSchemaInterface $table): array /** * Loads multiple types of constraints and returns the specified ones. * - * @param string $tableName table name. + * @param TableNameInterface $tableName table name. * @param string $returnType return type: * - primaryKey * - foreignKeys @@ -778,7 +713,7 @@ public function findUniqueIndexes(TableSchemaInterface $table): array * * @return mixed constraints. */ - private function loadTableConstraints(string $tableName, string $returnType): mixed + private function loadTableConstraints(TableNameInterface $tableName, string $returnType): mixed { $sql = <<db->getTablePrefix(), $name); + if (!str_contains($name, '{{')) { + return $name; } - return $name; + $name = preg_replace('/{{(.*?)}}/', '\1', $name); + return str_replace('%', $this->db->getTablePrefix(), $name); } /** @@ -934,7 +872,7 @@ public function getRawTableName(string $name): string */ protected function getCacheKey(string $name): array { - return array_merge([__CLASS__], $this->db->getCacheKey(), [$this->getRawTableName($name)]); + return array_merge([__CLASS__], $this->db->getCacheKey(), [$name]); } /** diff --git a/tests/Provider/SchemaProvider.php b/tests/Provider/SchemaProvider.php index dcaa8e189..c4b29b85f 100644 --- a/tests/Provider/SchemaProvider.php +++ b/tests/Provider/SchemaProvider.php @@ -5,6 +5,7 @@ namespace Yiisoft\Db\Mssql\Tests\Provider; use Yiisoft\Db\Mssql\Tests\TestCase; +use Yiisoft\Db\Schema\TableName; final class SchemaProvider extends TestCase { @@ -29,6 +30,7 @@ public function getTableSchemaDataProvider(): array ['dbo.profile', 'profile'], ['profile', 'profile'], ['dbo.[table.with.special.characters]', 'table.with.special.characters'], + [new TableName('table.with.special.characters', 'dbo'), 'table.with.special.characters'], ]; } } diff --git a/tests/SchemaTest.php b/tests/SchemaTest.php index ea5f676c8..9e0fe4480 100644 --- a/tests/SchemaTest.php +++ b/tests/SchemaTest.php @@ -8,6 +8,7 @@ use Yiisoft\Db\Constraint\DefaultValueConstraint; use Yiisoft\Db\Exception\NotSupportedException; use Yiisoft\Db\Mssql\Schema; +use Yiisoft\Db\Schema\TableNameInterface; use Yiisoft\Db\Schema\TableSchemaInterface; use Yiisoft\Db\TestSupport\AnyValue; use Yiisoft\Db\TestSupport\TestSchemaTrait; @@ -147,10 +148,10 @@ public function testGetTableNames(array $pdoAttributes): void /** * @dataProvider \Yiisoft\Db\Mssql\Tests\Provider\SchemaProvider::getTableSchemaDataProvider * - * @param string $name + * @param string|TableNameInterface $name * @param string $expectedName */ - public function testGetTableSchema(string $name, string $expectedName): void + public function testGetTableSchema(string|TableNameInterface $name, string $expectedName): void { $tableSchema = $this->getConnection()->getSchema()->getTableSchema($name); $this->assertInstanceOf(TableSchemaInterface::class, $tableSchema);