Database context - Put the database back in the Query and Invoke script methods - #10579
Open
andreasjordan wants to merge 1 commit into
Open
Database context - Put the database back in the Query and Invoke script methods#10579andreasjordan wants to merge 1 commit into
andreasjordan wants to merge 1 commit into
Conversation
…pt methods The Query and Invoke script methods of Server and Database do not run on a private connection. The execution manager of an SMO database is the connection context of the parent server, which belongs to the caller, so these methods issued a USE and never switched back. Every command using them handed the connection back pointing at a different database, and everything the caller ran afterwards silently executed in the wrong one. All four methods now remember ConnectionContext.CurrentDatabase and put it back in a finally, so a failing query restores it too. The Server pair needs the same treatment of its own, because Server.Query and Server.Invoke call $this.Databases[$Database].ExecuteWithResults() directly and never go through the Database methods. Restoring rather than running on a copied connection is deliberate. A copy works, but it is a different session: it cannot see the temp tables or SET options of the caller, and it opens a connection per call. Restoring keeps the session, and costs one round trip only when the database actually moved. The database the caller was on is restored, not master. A connection sitting in msdb is returned to msdb - restoring to master would have passed every other test and still been wrong. This covers the script methods only. The direct SMO calls of #10555, and SMO's own Create() and Drop(), are untouched and still leak - Invoke-DbaDbUpgrade in #10556 is one of those. (do *) Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
3 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
Type of Change
Purpose
Step one of #10555: the mechanism, on its own.
The
QueryandInvokescript methods ofServerandDatabaseinxml/dbatools.Types.ps1xmldo not run on a private connection. The execution manager of an SMO database is the connection context of the parent server, which belongs to the caller, so they issue aUSEand never switch back:These four methods are reached from roughly 68 database-scoped
.Query()/.Invoke()call sites plus 45 two-argument$server.Query($sql, $db)calls across 28 files, so this one file is the cheapest place in the module to fix it.Approach
Each method remembers
ConnectionContext.CurrentDatabaseand puts it back in afinally, so a query that throws restores the context as well.The
Serverpair needs its own copy of that.Server.QueryandServer.Invokecall$this.Databases[$Database].ExecuteWithResults(...)directly and never go through theDatabasemethods, so fixingDatabase.Queryalone does not reach them. That is four edits, not two - the issue body assumed otherwise.Restoring, not copying.
ConnectionContext.Copy().GetDatabaseConnection($name)also works, and was the other candidate, but it is a different session:A copy cannot see the temp tables or
SEToptions of the caller and opens a connection per call, which would be a silent behaviour change for any command that builds session state and then queries through the wrapper. Restoring keeps the session and costs one round trip, and only when the database actually moved.The caller's database is restored, not master. A connection sitting in
msdbis returned tomsdb. Restoring to master would have passed every other test and still been wrong - and it matters, because the Agent commands move the context tomsdbrather thanmaster.The database name is escaped for the
USE, so a database containing]in its name is handled.Commands to test
Tests
tests\InModule.TypeExtensions.Tests.ps1, following the existingInModule.*naming for test files that are not the test of a single command. 10 tests onInstanceSingle:AllTablesstill returns every tablemsdbis returned tomsdb, not to masterAll 10 pass. Against
development7 of them fail; the 3 that pass are the correctness assertions, which are there to catch the fix breaking something rather than to prove the bug.Because this reaches every command that uses the wrappers, 15 further test files of wrapper-using commands were run on top:
Find-DbaSimilarTable,Get-DbaCpuRingBuffer,Get-DbaDatabase,Get-DbaDbFeatureUsage,Get-DbaDbFile,Get-DbaDbSnapshot,Get-DbaDbVirtualLogFile,Get-DbaHelpIndex,Get-DbaInstanceInstallDate,Get-DbaModule,Get-DbaSchemaChangeHistory,Install-DbaWhoIsActive,Invoke-DbaDbClone,New-DbaLinkedServer,Set-DbaDbFileGrowth. 104 tests, no failures.What this does not fix
Only the script methods. The other two sources in #10555 are untouched and still leak:
$db.ExecuteNonQuery(...)/$db.ExecuteWithResults(...)call sites, which are SMO's own methods and cannot be shadowedCreate()andDrop()of server-level objectsInvoke-DbaDbUpgrade(#10556) is in the first of those groups. Verified against a database forced to compatibility level 100 so the upgrade really ran - it went to 150 and the connection was still left in the upgraded database.🤖 Generated with Claude Code