diff --git a/public/Get-DbaRandomizedValue.ps1 b/public/Get-DbaRandomizedValue.ps1 index 104b959ab92..5b13fa805f8 100644 --- a/public/Get-DbaRandomizedValue.ps1 +++ b/public/Get-DbaRandomizedValue.ps1 @@ -214,31 +214,40 @@ function Get-DbaRandomizedValue { $supportedDataTypes = 'bigint', 'bit', 'bool', 'char', 'date', 'datetime', 'datetime2', 'decimal', 'int', 'float', 'guid', 'money', 'numeric', 'nchar', 'ntext', 'nvarchar', 'real', 'smalldatetime', 'smallint', 'text', 'time', 'tinyint', 'uniqueidentifier', 'userdefineddatatype', 'varchar' # Check the variables + # None of these validation stops may use -Continue: the begin block has no enclosing loop, so + # the continue would escape the command and eat an iteration of whatever loop the caller runs + # in - and the data masking and generation commands call this in a loop per row. if (-not $DataType -and -not $RandomizerType -and -not $RandomizerSubType) { - Stop-Function -Message "Please use one of the variables i.e. -DataType, -RandomizerType or -RandomizerSubType" -Continue + Stop-Function -Message "Please use one of the variables i.e. -DataType, -RandomizerType or -RandomizerSubType" + return } elseif ($DataType -and ($RandomizerType -or $RandomizerSubType)) { - Stop-Function -Message "You cannot use -DataType with -RandomizerType or -RandomizerSubType" -Continue + Stop-Function -Message "You cannot use -DataType with -RandomizerType or -RandomizerSubType" + return } elseif (-not $RandomizerSubType -and $RandomizerType) { - Stop-Function -Message "Please enter a sub type" -Continue + Stop-Function -Message "Please enter a sub type" + return } elseif (-not $RandomizerType -and $RandomizerSubType) { $RandomizerType = $uniqueSubType } if ($DataType -and $DataType.ToLowerInvariant() -notin $supportedDataTypes) { - Stop-Function -Message "Unsupported sql data type" -Continue -Target $DataType + Stop-Function -Message "Unsupported sql data type" -Target $DataType + return } # Check the bogus type if ($RandomizerType) { if ($RandomizerType -notin $script:uniquerandomizertypes) { - Stop-Function -Message "Invalid randomizer type" -Continue -Target $RandomizerType + Stop-Function -Message "Invalid randomizer type" -Target $RandomizerType + return } } # Check the sub type if ($RandomizerSubType) { if ($RandomizerSubType -notin $script:uniquerandomizersubtype) { - Stop-Function -Message "Invalid randomizer sub type" -Continue -Target $RandomizerSubType + Stop-Function -Message "Invalid randomizer sub type" -Target $RandomizerSubType + return } # The type and the subtype used to be checked against two independent lists, so Name/ZipCode passed @@ -247,21 +256,25 @@ function Get-DbaRandomizedValue { $randomizerCombination = $script:randomizerTypes.Group | Where-Object { $_.Type -eq $RandomizerType -and $_.SubType -eq $RandomizerSubType } | Select-Object -First 1 if (-not $randomizerCombination) { - Stop-Function -Message "Randomizer type $RandomizerType has no sub type $RandomizerSubType, run Get-DbaRandomizedType to list the valid combinations" -Continue -Target $RandomizerSubType + Stop-Function -Message "Randomizer type $RandomizerType has no sub type $RandomizerSubType, run Get-DbaRandomizedType to list the valid combinations" -Target $RandomizerSubType + return } # Some combinations need input that cannot be made up. The randomizer types say which parameter # that is, so the list stays the single place that knows. if ($randomizerCombination.RequiredParameter -eq "Value" -and -not $Value) { - Stop-Function -Message "Value cannot be empty when using sub type $RandomizerSubType" -Continue -Target $RandomizerSubType + Stop-Function -Message "Value cannot be empty when using sub type $RandomizerSubType" -Target $RandomizerSubType + return } if ($randomizerCombination.RequiredParameter -eq "Format" -and -not $Format) { - Stop-Function -Message "Format cannot be empty when using sub type $RandomizerSubType, use something like ###-###" -Continue -Target $RandomizerSubType + Stop-Function -Message "Format cannot be empty when using sub type $RandomizerSubType, use something like ###-###" -Target $RandomizerSubType + return } if ($randomizerCombination.RequiredParameter -eq "StaticValue") { - Stop-Function -Message "Randomizer type $RandomizerType does not generate a value, it marks a value that the configuration supplies" -Continue -Target $RandomizerType + Stop-Function -Message "Randomizer type $RandomizerType does not generate a value, it marks a value that the configuration supplies" -Target $RandomizerType + return } } diff --git a/tests/Get-DbaRandomizedValue.Tests.ps1 b/tests/Get-DbaRandomizedValue.Tests.ps1 index 75535201dd9..f5470ce02bb 100644 --- a/tests/Get-DbaRandomizedValue.Tests.ps1 +++ b/tests/Get-DbaRandomizedValue.Tests.ps1 @@ -83,6 +83,19 @@ Describe $CommandName -Tag IntegrationTests { It "Accepts the same sub type under the type it belongs to" { Get-DbaRandomizedValue -RandomizerType Address -RandomizerSubType ZipCode | Should -Not -BeNullOrEmpty } + + It "Warns without eating an iteration of the caller's loop" { + # The validation guards used to run Stop-Function -Continue in the begin block, where no + # loop encloses it - the continue escaped the command and consumed an iteration of this + # very loop, so the counter fell short (#10638). + $loopCount = 0 + foreach ($i in 1..3) { + $null = Get-DbaRandomizedValue -WarningAction SilentlyContinue + $loopCount++ + } + $loopCount | Should -Be 3 + $WarnVar | Should -BeLike "*Please use one of the variables*" + } } Context "Every randomizer type" { @@ -96,10 +109,12 @@ Describe $CommandName -Tag IntegrationTests { foreach ($randomizerType in (Get-DbaRandomizedType)) { $typeKey = "$($randomizerType.Type)/$($randomizerType.SubType)" - # Stop-Function -Continue from the begin block skips the rest of this iteration, so this entry - # is written first and only overwritten when the call comes back. Landing on it means a message - # was given. A Stop-Function inside the switch is swallowed by the switch instead, and those - # calls do come back, so the warning is checked as well. + # Landing on "message" means the call warned instead of producing a value - the guard in + # the begin block returns nothing and sets the warning, and a Stop-Function inside the + # switch is swallowed by the switch. Both come back, so the warning is checked below; the + # pre-written entry stays as a safety net. (Before #10638 the begin block guards escaped + # the command via a loop-less -Continue and ate this very foreach iteration, which is why + # the entry is written first.) $typeResults[$typeKey] = "message" $typeWarning = $null