I've just run
Get-DbaDbQueryStoreOption -SqlInstance <MySQLServer> |
Where ActualState -ne DesiredState |
Select Database, ActualState, DesiredState
My intention was to get all rows from Get-DbaDbQueryStoreOption where ActualState and DesiredState were not identical. To my surprise, this returned rows where ActualState and DesiredState were identical.
Is my PowerShell somehow in error or is this a bug in Get-DbaDbQueryStoreOption?
I believe that this question makes sense even if you know nothing of the Get-DbaDbQueryStoreOption command from the dbatools module. However, the type of ActualState and DesiredState is Selected.Microsoft.SqlServer.Management.Smo.QueryStoreOptions.
Assuming that
DesiredStatetoo refers to a property of each input object, replaceWhere ActualState -ne DesiredStatewith:Your attempt to use simplified syntax cannot work, because the RHS of an operator such as
-ne, which binds to the-Valueparameter ofWhere-Object, must either be a literal, a variable reference or an expression, and none of them are able to refer to the current pipeline input object via the automatic$_variable, as the latter is only available inside script blocks.Due to argument-mode parsing,
DesiredStatein your attempt was interpreted as a bareword string literal, i.e. your attempt was equivalent toWhere ActualState -ne 'DesiredState'Thus, as shown above, a script block must be used, in which
$_can be used to refer to the current pipeline input object at will, which binds to the-FilterScriptparameter.The limitations of simplified syntax:
The price you pay for the syntactic convenience of the alternative, simplified syntax offered by the
Where-ObjectandForEach-Objectcmdlets is:You're limited to one operation (use of one operator, property reference or method call); e.g, you cannot combine multiple operations, such as with
-oror-and; for the latter, you must use a script block, inside of which no constraints apply.That one operation is limited to:
Where-Object:Comparing a property[1] of each pipeline input object to an external value (i.e., the comparison operand cannot also refer to the input object); e.g.:
ForEach-Object: Referencing a member of each pipeline input object, i.e. one of the following:Getting the value of a property.
Invoking a method with external values as arguments.
Simplified syntax uses argument-mode parsing, which implies:
Simple strings may optionally be passed as barewords (not enclosed in quotation marks) - though you're free to use PowerShell's usual string literals, as you must do in expression-mode parsing; e.g.:
Conversely, however, not all expressions are recognized as such in argument mode, and therefore situationally require enclosure in
(...), the grouping operator; e.g.:[1] Unfortunately, as of PowerShell (Core) 7.4.x you cannot compare the input objects as a whole, because the syntax requires specifying a property name. E.g. even though something like
1..10 | Where-Object -gt 5would be useful, it doesn't currently work. See GitHub issue #8357 for a feature request that would enable the latter.