Skip to content

Commit 9b8fa13

Browse files
committed
Enable-/Disable-SqlDscDatabaseSnapshotIsolation: Add new commands
Add support for enabling and disabling snapshot isolation on SQL databases - Updated SqlServerDsc.strings.psd1 to include new strings for snapshot isolation commands. - Implemented Disable-SqlDscDatabaseSnapshotIsolation and Enable-SqlDscDatabaseSnapshotIsolation commands with appropriate logic. - Created integration tests for both enabling and disabling snapshot isolation, ensuring idempotency and error handling. - Added unit tests for the new commands, covering various scenarios including successful execution, error handling, and state verification. - Enhanced the SMO stub to support the SetSnapshotIsolation method for testing purposes.
1 parent 8ababf9 commit 9b8fa13

9 files changed

Lines changed: 1264 additions & 0 deletions

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2525

2626
### Added
2727

28+
- Added public command `Enable-SqlDscDatabaseSnapshotIsolation` to enable snapshot
29+
isolation for a database in a SQL Server Database Engine instance. This command
30+
uses the SMO `SetSnapshotIsolation()` method to enable row-versioning and snapshot
31+
isolation settings to optimize concurrency and consistency ([issue #2329](https://github.com/dsccommunity/SqlServerDsc/issues/2329)).
32+
- Added public command `Disable-SqlDscDatabaseSnapshotIsolation` to disable snapshot
33+
isolation for a database in a SQL Server Database Engine instance. This command
34+
uses the SMO `SetSnapshotIsolation()` method to disable row-versioning and snapshot
35+
isolation settings ([issue #2329](https://github.com/dsccommunity/SqlServerDsc/issues/2329)).
2836
- Added public command `New-SqlDscDatabaseSnapshot` to create database snapshots
2937
in a SQL Server Database Engine instance using SMO. This command provides an
3038
automated and DSC-friendly approach to snapshot management by leveraging
Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
<#
2+
.SYNOPSIS
3+
Disables snapshot isolation for a database in a SQL Server Database Engine instance.
4+
5+
.DESCRIPTION
6+
This command disables snapshot isolation for a database in a SQL Server Database Engine
7+
instance. Disabling snapshot isolation removes row-versioning and snapshot isolation
8+
settings for the database.
9+
10+
The command uses the SetSnapshotIsolation() method on the SMO Database object to disable
11+
row-versioning and snapshot isolation settings.
12+
13+
.PARAMETER ServerObject
14+
Specifies current server connection object.
15+
16+
.PARAMETER Name
17+
Specifies the name of the database to modify.
18+
19+
.PARAMETER DatabaseObject
20+
Specifies the database object to modify (from Get-SqlDscDatabase).
21+
22+
.PARAMETER Refresh
23+
Specifies that the **ServerObject**'s databases should be refreshed before
24+
trying to get the database object. This is helpful when databases could have been
25+
modified outside of the **ServerObject**, for example through T-SQL. But
26+
on instances with a large amount of databases it might be better to make
27+
sure the **ServerObject** is recent enough.
28+
29+
This parameter is only used when setting snapshot isolation using **ServerObject** and
30+
**Name** parameters.
31+
32+
.PARAMETER Force
33+
Specifies that snapshot isolation should be disabled without any confirmation.
34+
35+
.PARAMETER PassThru
36+
Specifies that the database object should be returned after modification.
37+
38+
.EXAMPLE
39+
$serverObject = Connect-SqlDscDatabaseEngine -InstanceName 'MyInstance'
40+
Disable-SqlDscDatabaseSnapshotIsolation -ServerObject $serverObject -Name 'MyDatabase'
41+
42+
Disables snapshot isolation for the database named **MyDatabase**.
43+
44+
.EXAMPLE
45+
$serverObject = Connect-SqlDscDatabaseEngine -InstanceName 'MyInstance'
46+
$databaseObject = $serverObject | Get-SqlDscDatabase -Name 'MyDatabase'
47+
Disable-SqlDscDatabaseSnapshotIsolation -DatabaseObject $databaseObject -Force
48+
49+
Disables snapshot isolation for the database using a database object without prompting for confirmation.
50+
51+
.EXAMPLE
52+
$serverObject = Connect-SqlDscDatabaseEngine -InstanceName 'MyInstance'
53+
Disable-SqlDscDatabaseSnapshotIsolation -ServerObject $serverObject -Name 'MyDatabase' -PassThru
54+
55+
Disables snapshot isolation and returns the updated database object.
56+
57+
.INPUTS
58+
Microsoft.SqlServer.Management.Smo.Database
59+
60+
The database object to modify (from Get-SqlDscDatabase).
61+
62+
.OUTPUTS
63+
None.
64+
65+
When PassThru is specified the output is [Microsoft.SqlServer.Management.Smo.Database].
66+
#>
67+
function Disable-SqlDscDatabaseSnapshotIsolation
68+
{
69+
[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('UseSyntacticallyCorrectExamples', '', Justification = 'Because the rule does not yet support parsing the code when a parameter type is not available. The ScriptAnalyzer rule UseSyntacticallyCorrectExamples will always error in the editor due to https://github.com/indented-automation/Indented.ScriptAnalyzerRules/issues.')]
70+
[OutputType()]
71+
[OutputType([Microsoft.SqlServer.Management.Smo.Database])]
72+
[CmdletBinding(DefaultParameterSetName = 'ServerObjectSet', SupportsShouldProcess = $true, ConfirmImpact = 'Medium')]
73+
param
74+
(
75+
[Parameter(ParameterSetName = 'ServerObjectSet', Mandatory = $true)]
76+
[Microsoft.SqlServer.Management.Smo.Server]
77+
$ServerObject,
78+
79+
[Parameter(ParameterSetName = 'ServerObjectSet', Mandatory = $true)]
80+
[ValidateNotNullOrEmpty()]
81+
[System.String]
82+
$Name,
83+
84+
[Parameter(ParameterSetName = 'ServerObjectSet')]
85+
[System.Management.Automation.SwitchParameter]
86+
$Refresh,
87+
88+
[Parameter(ParameterSetName = 'DatabaseObjectSet', Mandatory = $true, ValueFromPipeline = $true)]
89+
[Microsoft.SqlServer.Management.Smo.Database]
90+
$DatabaseObject,
91+
92+
[Parameter()]
93+
[System.Management.Automation.SwitchParameter]
94+
$Force,
95+
96+
[Parameter()]
97+
[System.Management.Automation.SwitchParameter]
98+
$PassThru
99+
)
100+
101+
begin
102+
{
103+
if ($Force.IsPresent -and -not $Confirm)
104+
{
105+
$ConfirmPreference = 'None'
106+
}
107+
}
108+
109+
process
110+
{
111+
# Get the database object based on the parameter set
112+
switch ($PSCmdlet.ParameterSetName)
113+
{
114+
'ServerObjectSet'
115+
{
116+
$previousErrorActionPreference = $ErrorActionPreference
117+
$ErrorActionPreference = 'Stop'
118+
119+
$sqlDatabaseObject = $ServerObject |
120+
Get-SqlDscDatabase -Name $Name -Refresh:$Refresh -ErrorAction 'Stop'
121+
122+
$ErrorActionPreference = $previousErrorActionPreference
123+
}
124+
125+
'DatabaseObjectSet'
126+
{
127+
$sqlDatabaseObject = $DatabaseObject
128+
}
129+
}
130+
131+
$verboseDescriptionMessage = $script:localizedData.DatabaseSnapshotIsolation_Disable_ShouldProcessVerboseDescription -f $sqlDatabaseObject.Name, $sqlDatabaseObject.Parent.InstanceName
132+
$verboseWarningMessage = $script:localizedData.DatabaseSnapshotIsolation_Disable_ShouldProcessVerboseWarning -f $sqlDatabaseObject.Name
133+
$captionMessage = $script:localizedData.DatabaseSnapshotIsolation_Disable_ShouldProcessCaption
134+
135+
if ($PSCmdlet.ShouldProcess($verboseDescriptionMessage, $verboseWarningMessage, $captionMessage))
136+
{
137+
# Check if snapshot isolation is already disabled (idempotence)
138+
if ($sqlDatabaseObject.SnapshotIsolationState -eq 'Disabled')
139+
{
140+
Write-Debug -Message ($script:localizedData.DatabaseSnapshotIsolation_AlreadyDisabled -f $sqlDatabaseObject.Name)
141+
}
142+
else
143+
{
144+
Write-Debug -Message ($script:localizedData.DatabaseSnapshotIsolation_Disabling -f $sqlDatabaseObject.Name)
145+
146+
try
147+
{
148+
$sqlDatabaseObject.SetSnapshotIsolation($false)
149+
}
150+
catch
151+
{
152+
$errorMessage = $script:localizedData.DatabaseSnapshotIsolation_DisableFailed -f $sqlDatabaseObject.Name
153+
154+
$PSCmdlet.ThrowTerminatingError(
155+
[System.Management.Automation.ErrorRecord]::new(
156+
[System.InvalidOperationException]::new($errorMessage, $_.Exception),
157+
'DSDSI0001', # cspell: disable-line
158+
[System.Management.Automation.ErrorCategory]::InvalidOperation,
159+
$sqlDatabaseObject
160+
)
161+
)
162+
}
163+
164+
Write-Debug -Message ($script:localizedData.DatabaseSnapshotIsolation_Disabled -f $sqlDatabaseObject.Name)
165+
}
166+
167+
<#
168+
Refresh the database object to get the updated SnapshotIsolationState property if:
169+
- PassThru is specified (user wants the updated object back)
170+
- Using DatabaseObject parameter set (user's object reference should be updated)
171+
172+
Refresh even if no change was made to ensure the object is up to date.
173+
#>
174+
if ($PassThru.IsPresent -or $PSCmdlet.ParameterSetName -eq 'DatabaseObjectSet')
175+
{
176+
$sqlDatabaseObject.Refresh()
177+
}
178+
179+
if ($PassThru.IsPresent)
180+
{
181+
return $sqlDatabaseObject
182+
}
183+
}
184+
}
185+
}
Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
<#
2+
.SYNOPSIS
3+
Enables snapshot isolation for a database in a SQL Server Database Engine instance.
4+
5+
.DESCRIPTION
6+
This command enables snapshot isolation for a database in a SQL Server Database Engine
7+
instance. Enabling snapshot isolation may require additional tempdb space and can affect
8+
transaction behavior.
9+
10+
The command uses the SetSnapshotIsolation() method on the SMO Database object to enable
11+
row-versioning and snapshot isolation settings to optimize concurrency and consistency.
12+
13+
.PARAMETER ServerObject
14+
Specifies current server connection object.
15+
16+
.PARAMETER Name
17+
Specifies the name of the database to modify.
18+
19+
.PARAMETER DatabaseObject
20+
Specifies the database object to modify (from Get-SqlDscDatabase).
21+
22+
.PARAMETER Refresh
23+
Specifies that the **ServerObject**'s databases should be refreshed before
24+
trying to get the database object. This is helpful when databases could have been
25+
modified outside of the **ServerObject**, for example through T-SQL. But
26+
on instances with a large amount of databases it might be better to make
27+
sure the **ServerObject** is recent enough.
28+
29+
This parameter is only used when setting snapshot isolation using **ServerObject** and
30+
**Name** parameters.
31+
32+
.PARAMETER Force
33+
Specifies that snapshot isolation should be enabled without any confirmation.
34+
35+
.PARAMETER PassThru
36+
Specifies that the database object should be returned after modification.
37+
38+
.EXAMPLE
39+
$serverObject = Connect-SqlDscDatabaseEngine -InstanceName 'MyInstance'
40+
Enable-SqlDscDatabaseSnapshotIsolation -ServerObject $serverObject -Name 'MyDatabase'
41+
42+
Enables snapshot isolation for the database named **MyDatabase**.
43+
44+
.EXAMPLE
45+
$serverObject = Connect-SqlDscDatabaseEngine -InstanceName 'MyInstance'
46+
$databaseObject = $serverObject | Get-SqlDscDatabase -Name 'MyDatabase'
47+
Enable-SqlDscDatabaseSnapshotIsolation -DatabaseObject $databaseObject -Force
48+
49+
Enables snapshot isolation for the database using a database object without prompting for confirmation.
50+
51+
.EXAMPLE
52+
$serverObject = Connect-SqlDscDatabaseEngine -InstanceName 'MyInstance'
53+
Enable-SqlDscDatabaseSnapshotIsolation -ServerObject $serverObject -Name 'MyDatabase' -PassThru
54+
55+
Enables snapshot isolation and returns the updated database object.
56+
57+
.INPUTS
58+
Microsoft.SqlServer.Management.Smo.Database
59+
60+
The database object to modify (from Get-SqlDscDatabase).
61+
62+
.OUTPUTS
63+
None.
64+
65+
When PassThru is specified the output is [Microsoft.SqlServer.Management.Smo.Database].
66+
#>
67+
function Enable-SqlDscDatabaseSnapshotIsolation
68+
{
69+
[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('UseSyntacticallyCorrectExamples', '', Justification = 'Because the rule does not yet support parsing the code when a parameter type is not available. The ScriptAnalyzer rule UseSyntacticallyCorrectExamples will always error in the editor due to https://github.com/indented-automation/Indented.ScriptAnalyzerRules/issues.')]
70+
[OutputType()]
71+
[OutputType([Microsoft.SqlServer.Management.Smo.Database])]
72+
[CmdletBinding(DefaultParameterSetName = 'ServerObjectSet', SupportsShouldProcess = $true, ConfirmImpact = 'Medium')]
73+
param
74+
(
75+
[Parameter(ParameterSetName = 'ServerObjectSet', Mandatory = $true)]
76+
[Microsoft.SqlServer.Management.Smo.Server]
77+
$ServerObject,
78+
79+
[Parameter(ParameterSetName = 'ServerObjectSet', Mandatory = $true)]
80+
[ValidateNotNullOrEmpty()]
81+
[System.String]
82+
$Name,
83+
84+
[Parameter(ParameterSetName = 'ServerObjectSet')]
85+
[System.Management.Automation.SwitchParameter]
86+
$Refresh,
87+
88+
[Parameter(ParameterSetName = 'DatabaseObjectSet', Mandatory = $true, ValueFromPipeline = $true)]
89+
[Microsoft.SqlServer.Management.Smo.Database]
90+
$DatabaseObject,
91+
92+
[Parameter()]
93+
[System.Management.Automation.SwitchParameter]
94+
$Force,
95+
96+
[Parameter()]
97+
[System.Management.Automation.SwitchParameter]
98+
$PassThru
99+
)
100+
101+
begin
102+
{
103+
if ($Force.IsPresent -and -not $Confirm)
104+
{
105+
$ConfirmPreference = 'None'
106+
}
107+
}
108+
109+
process
110+
{
111+
# Get the database object based on the parameter set
112+
switch ($PSCmdlet.ParameterSetName)
113+
{
114+
'ServerObjectSet'
115+
{
116+
$previousErrorActionPreference = $ErrorActionPreference
117+
$ErrorActionPreference = 'Stop'
118+
119+
$sqlDatabaseObject = $ServerObject |
120+
Get-SqlDscDatabase -Name $Name -Refresh:$Refresh -ErrorAction 'Stop'
121+
122+
$ErrorActionPreference = $previousErrorActionPreference
123+
}
124+
125+
'DatabaseObjectSet'
126+
{
127+
$sqlDatabaseObject = $DatabaseObject
128+
}
129+
}
130+
131+
$verboseDescriptionMessage = $script:localizedData.DatabaseSnapshotIsolation_Enable_ShouldProcessVerboseDescription -f $sqlDatabaseObject.Name, $sqlDatabaseObject.Parent.InstanceName
132+
$verboseWarningMessage = $script:localizedData.DatabaseSnapshotIsolation_Enable_ShouldProcessVerboseWarning -f $sqlDatabaseObject.Name
133+
$captionMessage = $script:localizedData.DatabaseSnapshotIsolation_Enable_ShouldProcessCaption
134+
135+
if ($PSCmdlet.ShouldProcess($verboseDescriptionMessage, $verboseWarningMessage, $captionMessage))
136+
{
137+
# Check if snapshot isolation is already enabled (idempotence)
138+
if ($sqlDatabaseObject.SnapshotIsolationState -eq 'Enabled')
139+
{
140+
Write-Debug -Message ($script:localizedData.DatabaseSnapshotIsolation_AlreadyEnabled -f $sqlDatabaseObject.Name)
141+
}
142+
else
143+
{
144+
Write-Debug -Message ($script:localizedData.DatabaseSnapshotIsolation_Enabling -f $sqlDatabaseObject.Name)
145+
146+
try
147+
{
148+
$sqlDatabaseObject.SetSnapshotIsolation($true)
149+
}
150+
catch
151+
{
152+
$errorMessage = $script:localizedData.DatabaseSnapshotIsolation_EnableFailed -f $sqlDatabaseObject.Name
153+
154+
$PSCmdlet.ThrowTerminatingError(
155+
[System.Management.Automation.ErrorRecord]::new(
156+
[System.InvalidOperationException]::new($errorMessage, $_.Exception),
157+
'ESDSI0001', # cspell: disable-line
158+
[System.Management.Automation.ErrorCategory]::InvalidOperation,
159+
$sqlDatabaseObject
160+
)
161+
)
162+
}
163+
164+
Write-Debug -Message ($script:localizedData.DatabaseSnapshotIsolation_Enabled -f $sqlDatabaseObject.Name)
165+
}
166+
167+
<#
168+
Refresh the database object to get the updated SnapshotIsolationState property if:
169+
- PassThru is specified (user wants the updated object back)
170+
- Using DatabaseObject parameter set (user's object reference should be updated)
171+
172+
Refresh even if no change was made to ensure the object is up to date.
173+
#>
174+
if ($PassThru.IsPresent -or $PSCmdlet.ParameterSetName -eq 'DatabaseObjectSet')
175+
{
176+
$sqlDatabaseObject.Refresh()
177+
}
178+
179+
if ($PassThru.IsPresent)
180+
{
181+
return $sqlDatabaseObject
182+
}
183+
}
184+
}
185+
}

0 commit comments

Comments
 (0)