Skip to content

Commit 67bc3b6

Browse files
Invoke-DbaQuery - Add -QuotedIdentifier parameter for filtered index compatibility (#10066)
1 parent c92affb commit 67bc3b6

4 files changed

Lines changed: 51 additions & 2 deletions

File tree

private/functions/Invoke-DbaAsync.ps1

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,10 @@ function Invoke-DbaAsync {
3737
.PARAMETER NoExec
3838
Use this switch to prepend SET NOEXEC ON and append SET NOEXEC OFF to each statement
3939
40+
.PARAMETER QuotedIdentifier
41+
Use this switch to prepend SET QUOTED_IDENTIFIER ON to each statement.
42+
Required for INSERT/UPDATE/DELETE on tables with filtered indexes, indexed views, computed columns, or XML indexes.
43+
4044
.PARAMETER EnableException
4145
By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
4246
@@ -74,6 +78,9 @@ function Invoke-DbaAsync {
7478
[switch]
7579
$NoExec,
7680

81+
[switch]
82+
$QuotedIdentifier,
83+
7784
[switch]$EnableException
7885
)
7986

@@ -184,8 +191,11 @@ function Invoke-DbaAsync {
184191
$Pieces = $Pieces | Where-Object { $_.Trim().Length -gt 0 }
185192
foreach ($piece in $Pieces) {
186193
$runningStatement = $piece
194+
if ($QuotedIdentifier) {
195+
$runningStatement = "SET QUOTED_IDENTIFIER ON; " + $runningStatement
196+
}
187197
if ($NoExec) {
188-
$runningStatement = "SET NOEXEC ON; " + $piece + " ;SET NOEXEC OFF;"
198+
$runningStatement = "SET NOEXEC ON; " + $runningStatement + " ;SET NOEXEC OFF;"
189199
}
190200
$cmd = New-Object Microsoft.Data.SqlClient.SqlCommand
191201
$cmd.CommandText = $runningStatement

public/Invoke-DbaQuery.ps1

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,10 @@ function Invoke-DbaQuery {
8080
Enables syntax and semantic validation without executing the actual statements. The SQL engine parses and compiles queries but doesn't run them.
8181
Use this to validate T-SQL syntax, check object references, and verify permissions before running potentially destructive scripts in production environments.
8282
83+
.PARAMETER QuotedIdentifier
84+
Prepends SET QUOTED_IDENTIFIER ON to each batch before execution. This is required for INSERT, UPDATE, and DELETE operations on tables with filtered indexes, indexed views, computed columns, or XML indexes.
85+
Use this when modifying tables with filtered indexes and experiencing silent failures, as SMO connections may have QUOTED_IDENTIFIER set to OFF depending on server/database configuration.
86+
8387
.PARAMETER AppendConnectionString
8488
Adds custom connection string parameters for specialized connection requirements like MultiSubnetFailover, encryption settings, or timeout values.
8589
Use this for Availability Group connections, Always Encrypted scenarios, or when you need connection properties not available through standard parameters. Authentication must still be handled via SqlInstance and SqlCredential.
@@ -184,6 +188,11 @@ function Invoke-DbaQuery {
184188
185189
Leverages your own parameters, giving you full power, mimicking Connect-DbaInstance's `-MultiSubnetFailover -ConnectTimeout 60`, to adhere to official guidelines to target FCI or AG listeners.
186190
See https://learn.microsoft.com/en-us/dotnet/framework/data/adonet/sql/sqlclient-support-for-high-availability-disaster-recovery#connecting-with-multisubnetfailover
191+
192+
.EXAMPLE
193+
PS C:\> Invoke-DbaQuery -SqlInstance server1 -Database tempdb -Query "INSERT INTO dbo.TableWithFilteredIndex (Id, Name) VALUES (1, 'Test')" -QuotedIdentifier
194+
195+
Executes an INSERT statement with QUOTED_IDENTIFIER set to ON. This is required when modifying tables that have filtered indexes, as SQL Server requires this setting for such operations.
187196
#>
188197
[CmdletBinding(DefaultParameterSetName = "Query")]
189198
param (
@@ -213,6 +222,7 @@ function Invoke-DbaQuery {
213222
[Microsoft.SqlServer.Management.Smo.Database[]]$InputObject,
214223
[switch]$ReadOnly,
215224
[switch]$NoExec,
225+
[switch]$QuotedIdentifier,
216226
[string]$AppendConnectionString,
217227
[switch]$EnableException
218228
)
@@ -254,6 +264,9 @@ function Invoke-DbaQuery {
254264
if (Test-Bound -ParameterName "NoExec") {
255265
$splatInvokeDbaSqlAsync["NoExec"] = $NoExec
256266
}
267+
if (Test-Bound -ParameterName "QuotedIdentifier") {
268+
$splatInvokeDbaSqlAsync["QuotedIdentifier"] = $QuotedIdentifier
269+
}
257270

258271
if (Test-Bound -ParameterName "File") {
259272
$files = @()

tests/Invoke-DbaQuery.Tests.ps1

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ Describe $CommandName -Tag UnitTests {
2727
"EnableException",
2828
"CommandType",
2929
"NoExec",
30+
"QuotedIdentifier",
3031
"AppendConnectionString"
3132
)
3233
Compare-Object -ReferenceObject $expectedParameters -DifferenceObject $hasParameters | Should -BeNullOrEmpty
@@ -358,6 +359,30 @@ SELECT 2
358359
{ Invoke-DbaQuery -SqlInstance $TestConfig.instance2 -Database tempdb -Query "SELEC p FROM c" -NoExec -EnableException } | Should -Throw "Incorrect syntax near 'SELEC'."
359360
}
360361

362+
It "supports QuotedIdentifier for filtered index compatibility" {
363+
# Create table with filtered index that requires QUOTED_IDENTIFIER ON
364+
$tableName = "dbatoolsci_TestFiltered_$(Get-Random)"
365+
$setupQuery = @"
366+
IF OBJECT_ID('tempdb.dbo.$tableName') IS NOT NULL DROP TABLE dbo.$tableName;
367+
CREATE TABLE dbo.$tableName (Id INT PRIMARY KEY, Name VARCHAR(100), IsDeleted BIT DEFAULT 0);
368+
CREATE INDEX IX_Filtered ON dbo.$tableName(Name) WHERE IsDeleted = 0;
369+
"@
370+
$null = Invoke-DbaQuery -SqlInstance $TestConfig.instance2 -Database tempdb -Query $setupQuery
371+
372+
# Insert with QuotedIdentifier should succeed
373+
$splatInsert = @{
374+
SqlInstance = $TestConfig.instance2
375+
Database = "tempdb"
376+
Query = "INSERT INTO dbo.$tableName (Id, Name) VALUES (1, 'Test'); SELECT COUNT(*) as cnt FROM dbo.$tableName;"
377+
QuotedIdentifier = $true
378+
}
379+
$result = Invoke-DbaQuery @splatInsert
380+
$result.cnt | Should -Be 1
381+
382+
# Cleanup
383+
$null = Invoke-DbaQuery -SqlInstance $TestConfig.instance2 -Database tempdb -Query "DROP TABLE dbo.$tableName"
384+
}
385+
361386
It "supports dropping temp objects (#8472)" {
362387
$sql = "CREATE PROC #DropStatistics
363388
AS

tests/pester.groups.ps1

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@ $TestsRunGroups = @{
2424
"appveyor_disabled" = @(
2525
'Backup-DbaDbCertificate',
2626
'Test-DbaDbLogShipStatus',
27-
'Invoke-DbaDbMirroring'
27+
'Invoke-DbaDbMirroring',
28+
'New-DbaEndpoint'
2829
)
2930
# do not run everywhere
3031
"disabled" = @()

0 commit comments

Comments
 (0)