Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[10.x] Fix using schema names in SQL server #49929

Closed
wants to merge 13 commits into from
Closed
87 changes: 87 additions & 0 deletions src/Illuminate/Database/Concerns/UsesSchemaAwareTables.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
<?php

namespace Illuminate\Database\Concerns;

trait UsesSchemaAwareTables
{
/**
* Get the columns for a given table.
*
* @param string $table
* @return array
*/
public function getColumns($table)
{
[$database, $schema, $table] = $this->parseSchemaAndTable($table);

$table = $this->connection->getTablePrefix().$table;

$results = $this->connection->selectFromWriteConnection(
$this->grammar->compileColumns($database, $schema, $table)
);

return $this->connection->getPostProcessor()->processColumns($results);
}

/**
* Determine if the given table exists.
*
* @param string $table
* @return bool
*/
public function hasTable($table)
{
[, $schema, $table] = $this->parseSchemaAndTable($table);

$table = $this->connection->getTablePrefix().$table;

foreach ($this->getTables() as $value) {
if (strtolower($table) === strtolower($value['name']) &&
strtolower($schema) === strtolower($value['schema'])) {
return true;
}
}

return false;
}

/**
* Parse the database object reference and extract the database, schema, and table.
*
* @param string $reference
* @return array
*/
protected function parseSchemaAndTable($reference)
{
$parts = explode('.', $reference);

// In order to be fully backward compatibel with previous version where users
// may have used square brackets with the identifiers in SQLServer grammar
// e.g. "schema.[table1]". We shall trim the parts for their occurrence.
$parts = array_map(function ($part) {
return trim($part, '[]');
}, $parts);

$database = $this->connection->getConfig('database');

// If the reference contains a database name, we will use that instead of the
// default database name for the connection. This allows the database name
// to be specified in the query instead of at the full connection level.
if (count($parts) === 3) {
$database = $parts[0];
array_shift($parts);
}

// We will use the default schema unless the schema has been specified in the
// query. If the schema has been specified in the query then we can use it
// instead of using the Postgres configuration or SQL database default.
$schema = $this->getDefaultSchema();

if (count($parts) === 2) {
$schema = $parts[0];
array_shift($parts);
}

return [$database, $schema, $parts[0]];
}
}
39 changes: 23 additions & 16 deletions src/Illuminate/Database/Schema/Grammars/SqlServerGrammar.php
Original file line number Diff line number Diff line change
Expand Up @@ -145,26 +145,33 @@ public function compileColumnListing($table)
/**
* Compile the query to determine the columns.
*
* @param string $database
* @param string $schema
* @param string $table
* @return string
*/
public function compileColumns($table)
{
return sprintf(
'select col.name, type.name as type_name, '
.'col.max_length as length, col.precision as precision, col.scale as places, '
.'col.is_nullable as nullable, def.definition as [default], '
.'col.is_identity as autoincrement, col.collation_name as collation, '
.'cast(prop.value as nvarchar(max)) as comment '
.'from sys.columns as col '
.'join sys.types as type on col.user_type_id = type.user_type_id '
.'join sys.objects as obj on col.object_id = obj.object_id '
.'join sys.schemas as scm on obj.schema_id = scm.schema_id '
.'left join sys.default_constraints def on col.default_object_id = def.object_id and col.object_id = def.parent_object_id '
."left join sys.extended_properties as prop on obj.object_id = prop.major_id and col.column_id = prop.minor_id and prop.name = 'MS_Description' "
."where obj.type in ('U', 'V') and obj.name = %s and scm.name = SCHEMA_NAME() "
.'order by col.column_id',
public function compileColumns($database, $schema, $table)
{
$sql = <<<'SQL'
select col.name, type.name as type_name,
col.max_length as length, col.precision as precision, col.scale as places,
col.is_nullable as nullable, def.definition as [default],
col.is_identity as autoincrement, col.collation_name as collation,
cast(prop.value as nvarchar(max)) as comment
from sys.columns as col
join sys.types as type on col.user_type_id = type.user_type_id
join sys.objects as obj on col.object_id = obj.object_id
join sys.schemas as scm on obj.schema_id = scm.schema_id
left join sys.default_constraints def on col.default_object_id = def.object_id and col.object_id = def.parent_object_id
left join sys.extended_properties as prop on obj.object_id = prop.major_id and col.column_id = prop.minor_id and prop.name = 'MS_Description'
where obj.type in ('U', 'V')
and obj.[name] = %s
and scm.[name] = %s
SQL;

return sprintf($sql,
$this->quoteString($table),
$this->quoteString($schema),
);
}

Expand Down
70 changes: 6 additions & 64 deletions src/Illuminate/Database/Schema/PostgresBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@
namespace Illuminate\Database\Schema;

use Illuminate\Database\Concerns\ParsesSearchPath;
use Illuminate\Database\Concerns\UsesSchemaAwareTables;

class PostgresBuilder extends Builder
{
use ParsesSearchPath {
use ParsesSearchPath, UsesSchemaAwareTables {
parseSearchPath as baseParseSearchPath;
}

Expand Down Expand Up @@ -36,23 +37,6 @@ public function dropDatabaseIfExists($name)
);
}

/**
* Determine if the given table exists.
*
* @param string $table
* @return bool
*/
public function hasTable($table)
{
[$database, $schema, $table] = $this->parseSchemaAndTable($table);

$table = $this->connection->getTablePrefix().$table;

return count($this->connection->selectFromWriteConnection(
$this->grammar->compileTableExists(), [$database, $schema, $table]
)) > 0;
}

/**
* Get the user-defined types that belong to the database.
*
Expand Down Expand Up @@ -206,22 +190,13 @@ public function dropAllTypes()
}

/**
* Get the columns for a given table.
* Get the default schema for the connection.
*
* @param string $table
* @return array
* @return string
*/
public function getColumns($table)
public function getDefaultSchema()
{
[$database, $schema, $table] = $this->parseSchemaAndTable($table);

$table = $this->connection->getTablePrefix().$table;

$results = $this->connection->selectFromWriteConnection(
$this->grammar->compileColumns($database, $schema, $table)
);

return $this->connection->getPostProcessor()->processColumns($results);
return $this->getSchemas()[0];
}

/**
Expand Down Expand Up @@ -270,39 +245,6 @@ protected function getSchemas()
);
}

/**
* Parse the database object reference and extract the database, schema, and table.
*
* @param string $reference
* @return array
*/
protected function parseSchemaAndTable($reference)
{
$parts = explode('.', $reference);

$database = $this->connection->getConfig('database');

// If the reference contains a database name, we will use that instead of the
// default database name for the connection. This allows the database name
// to be specified in the query instead of at the full connection level.
if (count($parts) === 3) {
$database = $parts[0];
array_shift($parts);
}

// We will use the default schema unless the schema has been specified in the
// query. If the schema has been specified in the query then we can use it
// instead of a default schema configured in the connection search path.
$schema = $this->getSchemas()[0];

if (count($parts) === 2) {
$schema = $parts[0];
array_shift($parts);
}

return [$database, $schema, $parts[0]];
}

/**
* Parse the "search_path" configuration value into an array.
*
Expand Down
16 changes: 15 additions & 1 deletion src/Illuminate/Database/Schema/SqlServerBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,12 @@

namespace Illuminate\Database\Schema;

use Illuminate\Database\Concerns\UsesSchemaAwareTables;

class SqlServerBuilder extends Builder
{
use UsesSchemaAwareTables;

/**
* Create a database in the schema.
*
Expand Down Expand Up @@ -53,7 +57,7 @@ public function dropAllViews()
}

/**
* Drop all tables from the database.
* Get all tables from the database.
*
* @deprecated Will be removed in a future Laravel version.
*
Expand All @@ -79,4 +83,14 @@ public function getAllViews()
$this->grammar->compileGetAllViews()
);
}

/**
* Get the default schema for the connection.
*
* @return string
*/
public function getDefaultSchema()
{
return $this->connection->getConfig('default_schema') ?: 'dbo';
}
}
Loading
Loading