microsoft / sqlmanagementobjects

Sql Management Objects, an API for scripting and managing SQL Server and Azure SQL Database
Other
128 stars 21 forks source link

Using .Databases with a misspelled database name changes database name in SMO #115

Open andreasjordan opened 1 year ago

andreasjordan commented 1 year ago

This bug was found using dbatools: https://github.com/dataplat/dbatools/issues/8598

There are two ways to select a single database from the list of databases from a Microsoft.SqlServer.Management.Smo.Server object: $server.Databases | Where-Object Name -EQ <dbname> and $server['<dbname>'].

The first one works without problems. But the second one changes the casing of the name from the original one to the one used to query the database.

Here is the code to show that:

Import-Module -Name dbatools

# correct:
$server = [Microsoft.SqlServer.Management.Smo.Server]::new('sql01')
$server.Databases | Where-Object Name -EQ Master | Select-Object -ExpandProperty Name
$server.Databases['Master'] | Select-Object -ExpandProperty Name

# wrong:
$server = [Microsoft.SqlServer.Management.Smo.Server]::new('sql01')
$server.Databases['Master'] | Select-Object -ExpandProperty Name
$server.Databases | Where-Object Name -EQ Master | Select-Object -ExpandProperty Name

And here is a screenshot:

image

Yes, I'm using not the current version - but I don't have the current version at hand. But I think it will take you only minutes to test this in your test environment. If it is fixed with the current version: Sorry to bother you.

potatoqualitee commented 1 year ago

In this case, "misspelled" is related to case-sensitivity. master is correct, Master is not -- but our collations are all case-insensitive so it should not matter.

shueybubbles commented 1 year ago

I am curious - if you call database.Refresh() after getting it from the collection, does the Name property then return the correct name?

andreasjordan commented 1 year ago

Just tested; $server.Databases.Refresh() has no effect. Only $server.Databases.ResetCollection() followed by $server.Databases.Refresh() returns the correct name.

shueybubbles commented 1 year ago

This issue is related to #119 - When indexing the Databases collection with a string, SMO constructs the Database object using the app-provided name then attempts to connect and populate its properties. The Name property is not overridden with the name that comes back from the query. It's even worse for Azure DB because SMO has to try to connect directly to the user DB to see if it exists and the resulting SqlException is not properly handled.