Skip to content

Database context - Run server level statements on the connection - #10580

Open
andreasjordan wants to merge 1 commit into
developmentfrom
fix-database-context-server-level
Open

Database context - Run server level statements on the connection#10580
andreasjordan wants to merge 1 commit into
developmentfrom
fix-database-context-server-level

Conversation

@andreasjordan

Copy link
Copy Markdown
Collaborator

Type of Change

Purpose

Step two of #10555, and the half that needs no new mechanism, so it is independent of #10579.

Ten call sites went through a database object only because they needed somewhere to run a statement. The execution manager of an SMO database is the connection context of the parent server, so each of them issued a USE and left the connection of the caller in master or msdb. None of the statements needed a database context at all.

The clearest example is Export-DbaLogin.ps1:391-393, where the primary path was already right and only the fallback was not:

$hashedPass = $server.ConnectionContext.ExecuteScalar($sql)          # fine
} catch {
    $hashedPassDt = $server.Databases['master'].ExecuteWithResults($sql)   # same query, leaks

Approach

Each statement now runs on the connection itself, so there is no context to put back:

Command Statement Why no database is needed
Export-DbaLogin, New-DbaLogin, Get-LoginPasswordHash password hash from sys.sql_logins / sys.server_principals server level catalog views; the primary path already used the connection
Get-DbaDbDetachedFileInfo fn_helpcollations() available in every database
Get-OfflineSqlFileStructure SERVERPROPERTY(...) does not depend on the current database
Set-DbaTempDbConfig ALTER DATABASE tempdb ... does not depend on the current database
Remove-DbaAgentJob sp_delete_job named in full as msdb.dbo.sp_delete_job

Only the last one changes a statement. Verified that the three part name works and leaves the context alone:

context before : master
3-part sp_delete_job: worked
context after  : master
job still there: False

Also verified that ConnectionContext.ExecuteNonQuery takes the string array Set-DbaTempDbConfig hands it, which is the one signature difference between the two receivers.

The help of Connect-DbaInstance recommended the pattern this removes:

To execute SQL commands directly: $server.ConnectionContext.ExecuteReader($sql) or $server.Databases['master'].ExecuteNonQuery($sql)

It now points at the connection context and says why.

Tests

Regression contexts for the two sites that a test can actually reach, both asserting on a -NonPooledConnection, because SMO reopens a pooled one at its default database and hides the leak:

  • Remove-DbaAgentJob still removes the job and leaves the connection where it was
  • Get-DbaDbDetachedFileInfo still reads the file and resolves the collation, and leaves a connection that was on msdb on msdb

The other sites have no reachable test, and it is worth being explicit about why rather than adding a test that proves nothing:

  • the three password hash sites are catch fallbacks for ExecuteScalar, which does not fail on a supported SQL Server, so the changed line does not run
  • Get-OfflineSqlFileStructure has no callers anywhere in the module; the change is correct but the function is dead code
  • Set-DbaTempDbConfig reaches its changed line only by really reconfiguring tempdb. The existing Set-DbaTempDbConfig tests do execute that path, so it is exercised, but a context assertion cannot pass yet - see below.

Test files run: Export-DbaLogin (20), New-DbaLogin (15), Get-DbaDbDetachedFileInfo (9), Remove-DbaAgentJob (12 + 1 skipped), Sync-DbaLoginPassword (9), all passing.

Set-DbaTempDbConfig is only half fixed by this

It also reads tempdb through $server.Databases['tempdb'].Query(...) in three places, which is the script method fix in #10579. Measured on this branch, the connection still ends up in tempdb from those calls even when the command stops before the line changed here:

context before : msdb
command failed: Current tempdb ... is not suitable to be reconfigured ...
context after  : tempdb

So the command is free of the leak only once both changes are in, and a context test for it belongs with the second one.

馃 Generated with Claude Code

Ten call sites went through a database object only because they needed somewhere
to run a statement. The execution manager of an SMO database is the connection
context of the parent server, so each of them issued a USE and left the
connection of the caller in master or msdb. None of the statements needed a
database context in the first place.

They now run on the connection itself:

- Export-DbaLogin, New-DbaLogin, Get-LoginPasswordHash read a password hash from
  sys.sql_logins or sys.server_principals. In all three the primary path already
  used ConnectionContext.ExecuteScalar and only the fallback went through master.
- Get-DbaDbDetachedFileInfo resolves a collation with fn_helpcollations, which is
  available in every database.
- Get-OfflineSqlFileStructure reads SERVERPROPERTY.
- Set-DbaTempDbConfig executes ALTER DATABASE tempdb statements.
- Remove-DbaAgentJob called sp_delete_job in msdb. The procedure is now named in
  full as msdb.dbo.sp_delete_job, so the connection does not have to go there.

The help of Connect-DbaInstance recommended the pattern this removes, so it now
points at the connection context and says why.

This is the part of #10555 that needs no new mechanism, so it is separate from
the script method fix in #10579. Set-DbaTempDbConfig also reads tempdb through
$server.Databases['tempdb'].Query(), which is that other fix; the command is only
free of the leak once both are in.

(do Export-DbaLogin, New-DbaLogin, Get-DbaDbDetachedFileInfo, Set-DbaTempDbConfig, Remove-DbaAgentJob, Sync-DbaLoginPassword, Connect-DbaInstance)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Database-scoped SMO calls silently change the current database of the shared connection

1 participant