Skip to content

Commit

Permalink
[11.x] Non-default schema names (#50019)
Browse files Browse the repository at this point in the history
* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* fix rename table on sqlsrv

* wip

* wip

* fix rename column and rename index on sqlsrv

* fix drop primary on pgsql

* wip

* wip

* wip

* wip

* wip

* wip

* enable prefix tests

* wip

* wip

* fix prefix auto-increment starting value on pgsql

* use blueprint prefix on sqlite

* use wrapTable where ever possible

* wip

* wip

* fix index name with schema name+prefix

* fix schema name+prefix

* wip

* fix wrapping table

* fix auto-increment starting value on pgsql

* wip

* fix tests
  • Loading branch information
hafezdivandari committed Feb 12, 2024
1 parent a5f53e5 commit 2555473
Show file tree
Hide file tree
Showing 9 changed files with 505 additions and 40 deletions.
52 changes: 37 additions & 15 deletions src/Illuminate/Database/Grammar.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,21 +43,38 @@ public function wrapArray(array $values)
*/
public function wrapTable($table)
{
if (! $this->isExpression($table)) {
return $this->wrap($this->tablePrefix.$table, true);
if ($this->isExpression($table)) {
return $this->getValue($table);
}

return $this->getValue($table);
// If the table being wrapped has an alias we'll need to separate the pieces
// so we can prefix the table and then wrap each of the segments on their
// own and then join these both back together using the "as" connector.
if (stripos($table, ' as ') !== false) {
return $this->wrapAliasedTable($table);
}

// If the table being wrapped has a custom schema name specified, we need to
// prefix the last segment as the table name then wrap each segment alone
// and eventually join them both back together using the dot connector.
if (str_contains($table, '.')) {
$table = substr_replace($table, '.'.$this->tablePrefix, strrpos($table, '.'), 1);

return collect(explode('.', $table))
->map($this->wrapValue(...))
->implode('.');
}

return $this->wrapValue($this->tablePrefix.$table);
}

/**
* Wrap a value in keyword identifiers.
*
* @param \Illuminate\Contracts\Database\Query\Expression|string $value
* @param bool $prefixAlias
* @return string
*/
public function wrap($value, $prefixAlias = false)
public function wrap($value)
{
if ($this->isExpression($value)) {
return $this->getValue($value);
Expand All @@ -67,7 +84,7 @@ public function wrap($value, $prefixAlias = false)
// the pieces so we can wrap each of the segments of the expression on its
// own, and then join these both back together using the "as" connector.
if (stripos($value, ' as ') !== false) {
return $this->wrapAliasedValue($value, $prefixAlias);
return $this->wrapAliasedValue($value);
}

// If the given value is a JSON selector we will wrap it differently than a
Expand All @@ -84,23 +101,28 @@ public function wrap($value, $prefixAlias = false)
* Wrap a value that has an alias.
*
* @param string $value
* @param bool $prefixAlias
* @return string
*/
protected function wrapAliasedValue($value, $prefixAlias = false)
protected function wrapAliasedValue($value)
{
$segments = preg_split('/\s+as\s+/i', $value);

// If we are wrapping a table we need to prefix the alias with the table prefix
// as well in order to generate proper syntax. If this is a column of course
// no prefix is necessary. The condition will be true when from wrapTable.
if ($prefixAlias) {
$segments[1] = $this->tablePrefix.$segments[1];
}

return $this->wrap($segments[0]).' as '.$this->wrapValue($segments[1]);
}

/**
* Wrap a table that has an alias.
*
* @param string $value
* @return string
*/
protected function wrapAliasedTable($value)
{
$segments = preg_split('/\s+as\s+/i', $value);

return $this->wrapTable($segments[0]).' as '.$this->wrapValue($this->tablePrefix.$segments[1]);
}

/**
* Wrap the given value segments.
*
Expand Down
6 changes: 5 additions & 1 deletion src/Illuminate/Database/Schema/Blueprint.php
Original file line number Diff line number Diff line change
Expand Up @@ -1599,7 +1599,11 @@ protected function dropIndexCommand($command, $type, $index)
*/
protected function createIndexName($type, array $columns)
{
$index = strtolower($this->prefix.$this->table.'_'.implode('_', $columns).'_'.$type);
$table = str_contains($this->table, '.')
? substr_replace($this->table, '.'.$this->prefix, strrpos($this->table, '.'), 1)
: $this->prefix.$this->table;

$index = strtolower($table.'_'.implode('_', $columns).'_'.$type);

return str_replace(['-', '.'], '_', $index);
}
Expand Down
7 changes: 5 additions & 2 deletions src/Illuminate/Database/Schema/Grammars/PostgresGrammar.php
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,9 @@ public function compileAutoIncrementStartingValues(Blueprint $blueprint, Fluent
{
if ($command->column->autoIncrement
&& $value = $command->column->get('startingValue', $command->column->get('from'))) {
return 'alter sequence '.$blueprint->getTable().'_'.$command->column->name.'_seq restart with '.$value;
$table = last(explode('.', $blueprint->getTable()));

return 'alter sequence '.$blueprint->getPrefix().$table.'_'.$command->column->name.'_seq restart with '.$value;
}
}

Expand Down Expand Up @@ -483,7 +485,8 @@ public function compileDropColumn(Blueprint $blueprint, Fluent $command)
*/
public function compileDropPrimary(Blueprint $blueprint, Fluent $command)
{
$index = $this->wrap("{$blueprint->getPrefix()}{$blueprint->getTable()}_pkey");
$table = last(explode('.', $blueprint->getTable()));
$index = $this->wrap("{$blueprint->getPrefix()}{$table}_pkey");

return 'alter table '.$this->wrapTable($blueprint)." drop constraint {$index}";
}
Expand Down
4 changes: 2 additions & 2 deletions src/Illuminate/Database/Schema/Grammars/SQLiteGrammar.php
Original file line number Diff line number Diff line change
Expand Up @@ -295,8 +295,8 @@ public function compileChange(Blueprint $blueprint, Fluent $command, Connection
fn ($index) => $this->{'compile'.ucfirst($index->name)}($blueprint, $index)
)->all();

$tempTable = $this->wrap('__temp__'.$this->getTablePrefix().$table);
$table = $this->wrap($this->getTablePrefix().$table);
$tempTable = $this->wrap('__temp__'.$blueprint->getPrefix().$table);
$table = $this->wrapTable($blueprint);
$columnNames = implode(', ', $columnNames);

$foreignKeyConstraintsEnabled = $connection->scalar('pragma foreign_keys');
Expand Down
19 changes: 10 additions & 9 deletions src/Illuminate/Database/Schema/Grammars/SqlServerGrammar.php
Original file line number Diff line number Diff line change
Expand Up @@ -215,8 +215,8 @@ public function compileAdd(Blueprint $blueprint, Fluent $command)
*/
public function compileRenameColumn(Blueprint $blueprint, Fluent $command, Connection $connection)
{
return sprintf("sp_rename '%s', %s, 'COLUMN'",
$this->wrap($blueprint->getTable().'.'.$command->from),
return sprintf("sp_rename %s, %s, N'COLUMN'",
$this->quoteString($this->wrapTable($blueprint).'.'.$this->wrap($command->from)),
$this->wrap($command->to)
);
}
Expand Down Expand Up @@ -358,7 +358,7 @@ public function compileDrop(Blueprint $blueprint, Fluent $command)
public function compileDropIfExists(Blueprint $blueprint, Fluent $command)
{
return sprintf('if object_id(%s, \'U\') is not null drop table %s',
$this->quoteString($this->getTablePrefix().$blueprint->getTable()),
$this->quoteString($this->wrapTable($blueprint)),
$this->wrapTable($blueprint)
);
}
Expand Down Expand Up @@ -403,7 +403,7 @@ public function compileDropDefaultConstraint(Blueprint $blueprint, Fluent $comma
: "'".implode("','", $command->columns)."'";

$table = $this->wrapTable($blueprint);
$tableName = $this->quoteString($this->getTablePrefix().$blueprint->getTable());
$tableName = $this->quoteString($this->wrapTable($blueprint));

$sql = "DECLARE @sql NVARCHAR(MAX) = '';";
$sql .= "SELECT @sql += 'ALTER TABLE $table DROP CONSTRAINT ' + OBJECT_NAME([default_object_id]) + ';' ";
Expand Down Expand Up @@ -491,9 +491,10 @@ public function compileDropForeign(Blueprint $blueprint, Fluent $command)
*/
public function compileRename(Blueprint $blueprint, Fluent $command)
{
$from = $this->wrapTable($blueprint);

return "sp_rename {$from}, ".$this->wrapTable($command->to);
return sprintf('sp_rename %s, %s',
$this->quoteString($this->wrapTable($blueprint)),
$this->wrapTable($command->to)
);
}

/**
Expand All @@ -505,8 +506,8 @@ public function compileRename(Blueprint $blueprint, Fluent $command)
*/
public function compileRenameIndex(Blueprint $blueprint, Fluent $command)
{
return sprintf("sp_rename N'%s', %s, N'INDEX'",
$this->wrap($blueprint->getTable().'.'.$command->from),
return sprintf("sp_rename %s, %s, N'INDEX'",
$this->quoteString($this->wrapTable($blueprint).'.'.$this->wrap($command->from)),
$this->wrap($command->to)
);
}
Expand Down
2 changes: 1 addition & 1 deletion tests/Database/DatabaseSchemaBlueprintTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ public function testRenameColumn()
$this->assertEquals(['alter table "users" rename column "foo" to "bar"'], $blueprint->toSql($connection, new SQLiteGrammar));

$blueprint = clone $base;
$this->assertEquals(['sp_rename \'"users"."foo"\', "bar", \'COLUMN\''], $blueprint->toSql($connection, new SqlServerGrammar));
$this->assertEquals(['sp_rename N\'"users"."foo"\', "bar", N\'COLUMN\''], $blueprint->toSql($connection, new SqlServerGrammar));
}

public function testNativeRenameColumnOnMysql57()
Expand Down
10 changes: 5 additions & 5 deletions tests/Database/DatabaseSqlServerSchemaGrammarTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -83,14 +83,14 @@ public function testDropTableIfExists()
$statements = $blueprint->toSql($this->getConnection(), $this->getGrammar());

$this->assertCount(1, $statements);
$this->assertSame('if object_id(N\'users\', \'U\') is not null drop table "users"', $statements[0]);
$this->assertSame('if object_id(N\'"users"\', \'U\') is not null drop table "users"', $statements[0]);

$blueprint = new Blueprint('users');
$blueprint->dropIfExists();
$statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()->setTablePrefix('prefix_'));

$this->assertCount(1, $statements);
$this->assertSame('if object_id(N\'prefix_users\', \'U\') is not null drop table "prefix_users"', $statements[0]);
$this->assertSame('if object_id(N\'"prefix_users"\', \'U\') is not null drop table "prefix_users"', $statements[0]);
}

public function testDropColumn()
Expand Down Expand Up @@ -124,7 +124,7 @@ public function testDropColumnDropsCreatesSqlToDropDefaultConstraints()
$statements = $blueprint->toSql($this->getConnection(), $this->getGrammar());

$this->assertCount(1, $statements);
$this->assertSame("DECLARE @sql NVARCHAR(MAX) = '';SELECT @sql += 'ALTER TABLE \"foo\" DROP CONSTRAINT ' + OBJECT_NAME([default_object_id]) + ';' FROM sys.columns WHERE [object_id] = OBJECT_ID(N'foo') AND [name] in ('bar') AND [default_object_id] <> 0;EXEC(@sql);alter table \"foo\" drop column \"bar\"", $statements[0]);
$this->assertSame("DECLARE @sql NVARCHAR(MAX) = '';SELECT @sql += 'ALTER TABLE \"foo\" DROP CONSTRAINT ' + OBJECT_NAME([default_object_id]) + ';' FROM sys.columns WHERE [object_id] = OBJECT_ID(N'\"foo\"') AND [name] in ('bar') AND [default_object_id] <> 0;EXEC(@sql);alter table \"foo\" drop column \"bar\"", $statements[0]);
}

public function testDropPrimary()
Expand Down Expand Up @@ -185,7 +185,7 @@ public function testDropConstrainedForeignId()

$this->assertCount(2, $statements);
$this->assertSame('alter table "users" drop constraint "users_foo_foreign"', $statements[0]);
$this->assertSame('DECLARE @sql NVARCHAR(MAX) = \'\';SELECT @sql += \'ALTER TABLE "users" DROP CONSTRAINT \' + OBJECT_NAME([default_object_id]) + \';\' FROM sys.columns WHERE [object_id] = OBJECT_ID(N\'users\') AND [name] in (\'foo\') AND [default_object_id] <> 0;EXEC(@sql);alter table "users" drop column "foo"', $statements[1]);
$this->assertSame('DECLARE @sql NVARCHAR(MAX) = \'\';SELECT @sql += \'ALTER TABLE "users" DROP CONSTRAINT \' + OBJECT_NAME([default_object_id]) + \';\' FROM sys.columns WHERE [object_id] = OBJECT_ID(N\'"users"\') AND [name] in (\'foo\') AND [default_object_id] <> 0;EXEC(@sql);alter table "users" drop column "foo"', $statements[1]);
}

public function testDropTimestamps()
Expand Down Expand Up @@ -226,7 +226,7 @@ public function testRenameTable()
$statements = $blueprint->toSql($this->getConnection(), $this->getGrammar());

$this->assertCount(1, $statements);
$this->assertSame('sp_rename "users", "foo"', $statements[0]);
$this->assertSame('sp_rename N\'"users"\', "foo"', $statements[0]);
}

public function testRenameIndex()
Expand Down
10 changes: 5 additions & 5 deletions tests/Integration/Database/DatabaseSchemaBlueprintTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ public function testNativeColumnModifyingOnSqlServer()
});

$this->assertEquals([
"DECLARE @sql NVARCHAR(MAX) = '';SELECT @sql += 'ALTER TABLE \"users\" DROP CONSTRAINT ' + OBJECT_NAME([default_object_id]) + ';' FROM sys.columns WHERE [object_id] = OBJECT_ID(N'users') AND [name] in ('added_at') AND [default_object_id] <> 0;EXEC(@sql)",
"DECLARE @sql NVARCHAR(MAX) = '';SELECT @sql += 'ALTER TABLE \"users\" DROP CONSTRAINT ' + OBJECT_NAME([default_object_id]) + ';' FROM sys.columns WHERE [object_id] = OBJECT_ID(N'\"users\"') AND [name] in ('added_at') AND [default_object_id] <> 0;EXEC(@sql)",
'alter table "users" alter column "added_at" datetime2(4) not null',
'alter table "users" add default CURRENT_TIMESTAMP for "added_at"',
], $blueprint->toSql($connection, new SqlServerGrammar));
Expand All @@ -200,7 +200,7 @@ public function testNativeColumnModifyingOnSqlServer()
});

$this->assertEquals([
"DECLARE @sql NVARCHAR(MAX) = '';SELECT @sql += 'ALTER TABLE \"users\" DROP CONSTRAINT ' + OBJECT_NAME([default_object_id]) + ';' FROM sys.columns WHERE [object_id] = OBJECT_ID(N'users') AND [name] in ('name') AND [default_object_id] <> 0;EXEC(@sql)",
"DECLARE @sql NVARCHAR(MAX) = '';SELECT @sql += 'ALTER TABLE \"users\" DROP CONSTRAINT ' + OBJECT_NAME([default_object_id]) + ';' FROM sys.columns WHERE [object_id] = OBJECT_ID(N'\"users\"') AND [name] in ('name') AND [default_object_id] <> 0;EXEC(@sql)",
'alter table "users" alter column "name" nchar(40) collate unicode null',
'alter table "users" add default \'easy\' for "name"',
], $blueprint->toSql($connection, new SqlServerGrammar));
Expand All @@ -210,7 +210,7 @@ public function testNativeColumnModifyingOnSqlServer()
});

$this->assertEquals([
"DECLARE @sql NVARCHAR(MAX) = '';SELECT @sql += 'ALTER TABLE \"users\" DROP CONSTRAINT ' + OBJECT_NAME([default_object_id]) + ';' FROM sys.columns WHERE [object_id] = OBJECT_ID(N'users') AND [name] in ('foo') AND [default_object_id] <> 0;EXEC(@sql)",
"DECLARE @sql NVARCHAR(MAX) = '';SELECT @sql += 'ALTER TABLE \"users\" DROP CONSTRAINT ' + OBJECT_NAME([default_object_id]) + ';' FROM sys.columns WHERE [object_id] = OBJECT_ID(N'\"users\"') AND [name] in ('foo') AND [default_object_id] <> 0;EXEC(@sql)",
'alter table "users" alter column "foo" int not null',
], $blueprint->toSql($connection, new SqlServerGrammar));
}
Expand Down Expand Up @@ -448,7 +448,7 @@ public function testAddUniqueIndexWithoutNameWorks()
$queries = $blueprintSqlServer->toSql(DB::connection(), new SqlServerGrammar);

$expected = [
"DECLARE @sql NVARCHAR(MAX) = '';SELECT @sql += 'ALTER TABLE \"users\" DROP CONSTRAINT ' + OBJECT_NAME([default_object_id]) + ';' FROM sys.columns WHERE [object_id] = OBJECT_ID(N'users') AND [name] in ('name') AND [default_object_id] <> 0;EXEC(@sql)",
"DECLARE @sql NVARCHAR(MAX) = '';SELECT @sql += 'ALTER TABLE \"users\" DROP CONSTRAINT ' + OBJECT_NAME([default_object_id]) + ';' FROM sys.columns WHERE [object_id] = OBJECT_ID(N'\"users\"') AND [name] in ('name') AND [default_object_id] <> 0;EXEC(@sql)",
'alter table "users" alter column "name" nvarchar(255) null',
'create unique index "users_name_unique" on "users" ("name")',
];
Expand Down Expand Up @@ -512,7 +512,7 @@ public function testAddUniqueIndexWithNameWorks()
$queries = $blueprintSqlServer->toSql(DB::connection(), new SqlServerGrammar);

$expected = [
"DECLARE @sql NVARCHAR(MAX) = '';SELECT @sql += 'ALTER TABLE \"users\" DROP CONSTRAINT ' + OBJECT_NAME([default_object_id]) + ';' FROM sys.columns WHERE [object_id] = OBJECT_ID(N'users') AND [name] in ('name') AND [default_object_id] <> 0;EXEC(@sql)",
"DECLARE @sql NVARCHAR(MAX) = '';SELECT @sql += 'ALTER TABLE \"users\" DROP CONSTRAINT ' + OBJECT_NAME([default_object_id]) + ';' FROM sys.columns WHERE [object_id] = OBJECT_ID(N'\"users\"') AND [name] in ('name') AND [default_object_id] <> 0;EXEC(@sql)",
'alter table "users" alter column "name" int null',
'create unique index "index1" on "users" ("name")',
];
Expand Down

0 comments on commit 2555473

Please sign in to comment.