[8.x] SqlServer Grammar: Bugfixes for hasTable and dropIfExists / support for using schema names in these functions #37280
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
This PR has 2 bugfixes and 1 enhancement:
Consider a database in SqlServer named
laravel
. By default, all tables created in this database will be created in thedbo
schema which is the default schema in SqlServer. When other schemas are created in the database (e.g. a schema calledtest_schema
), tables with the same name can be created in both schema's. So a valid example listing of tables would be:Current implementation of
Schema::hasTable()
will return:While the output of the function is correct, the underlying count here is 2 as both
jobs
tables are foundOk, now we really enter the subject of this PR here. Should this return true or false. SqlServer differs from other database engines such as MySQL / Postgres with the ability of using schemas (for security / seperation reasons) within the database.
Whether the output is correct here is determined by the question if the schema name is considered to be a part of the table name.
In Schema (
Schema::create('test_schema.test_table, ...)
), Eloquent (protected $table = 'test_schema.test_table';
) and Query Builder (DB::table('test_schema.test_table')
) the use of the schema name as part of the table name is valid and results in expected behaviour (CRUD on table in schema test_schema).Returning
false
in statements above is therefore not consistent.At first glance a correct output as there is indeed a table
users
. However it apparently has no notion of the schema it lives in. When a statement is run in SqlServer without specifying the schema, the interpreter will assume the defaullt schema (dbo
). So, when runningselect * from users
here, SqlServer will answer withinvalid object name 'users'
. Which is very confusing when using it like:Returning
true
here is therefore kind of strangeProposed implementation of
Schema::hasTable()
will return:As no schema is provided, the
object_id()
function will assume the default schema (dbo
) and finds the table.When schema name is provided, expected results are returned
The default schema does not have a table users, however
The
test_schema
schema does have one.Current implementation of
Schema::dropIfExists()
will return:SqlServer will drop the table jobs in the default schema
dbo
. Expected behaviour.However, how will we drop the jobs table in the
test_schema
schema ?No error here until we use it like:
So, the table was not dropped, instead considered to not exist.
Here, the
if
returns true, however thedrop
statement fails.. As the schema name is omitted, thedrop
statement will look for a table to drop in the default schema. Theif
check has no notion of schemas and finds the table in thetest_schema
schema.Proposed implementation of
Schema::dropIfExists()
will return:SqlServer will drop the table jobs in the default schema
dbo
. Expected behaviour.SqlServer will drop the table jobs in the schema
test_schema
. Expected behaviour.So, the table was indeed really dropped
No error, nothing is dropped as there is no
users
table in the default schema.Table is dropped
Impact for users
It is higly unlikely that current users of these functions will have to change their code. Currently both functions only work as expected when tables are used without specifying a schema name, which will continue to work with proposed implementation.
It does however enable the use of the schema name as part of the table name, consistent with other functions in available in the framework.
One thing to consider is the use of both a table prefix on the connection and specifying a schema name in these functions. The prefix is, well, prefixed to the
$table
parameter and will as such essentially prefix the schema name of the table. This is however consistent with the behaviour of the other parts of the framework where the$table
parameter is used.