我很懒惰,宁愿参数化一次(以获取名称的 object_id)并Invoke-SqlCmd
用于其余工作。
给定一系列旨在与任意服务器对象一起使用的函数,下面的测试是否足以确认 CmdletBinding 一个输入参数可以Int32
防止通过用户提供的方式进行注入$object_id
?
function foo {
[CmdletBinding()]Param(
[Int32]$object_id
)
$query = "select type_desc from sys.objects where object_id = $object_id;"
Invoke-Sqlcmd -ServerInstance "localhost" -Database "tempdb" -Query $query
}
foo 3
foo "0 union all select name from sys.syslogins where sid = 0x01"
foo $null
foo ([math]::Pow(2,31)+1)
foo @(1,2)
你可以,但这样做有问题。
首先,这仍然不是一个好主意,因为该模式不可扩展。它仅适用于某些数据类型。一旦你有了一个字符串参数,你就回到了注入的地方。这也意味着您必须关心某些数据类型(字节数组、GUID、日期时间)如何转换为字符串。
您还必须注意,空值参数将被静默修改
0
为非[int32]
可空类型。您必须指定[nullable[int32]]
以防止该行为。即便如此,由于转换为字符串的 null 值将导致空字符串,您会得到一个带有参数的更具描述性的错误。你宁愿调试这个:
或这个:
最后,如果您打算大量使用查询,系统可能更难重用查询计划。
一旦你习惯了,它真的不难指定:
[如果您不介意只输出 DataRows,则可以去掉最后一行的逗号。逗号强制函数输出整个 DataTable。如果没有逗号,您将获得 DataRows 流。]
它看起来很复杂,但你可以一遍又一遍地重复使用这个模式。我基本上是从我自己的脚本中复制上面的代码。
如果您关心的是代码部署,那么我认为这种方法仍然更好。上面的代码只需要 .Net Framework。事实上,该代码甚至可以在 PowerShell Core 6.0 上运行(当然,您必须调用
(foo <value>).Rows
以获得等效输出,因为 Core 尚未被教导如何在命令行显示数据表)。使用添加对正在安装的 PowerShell 模块Invoke-Sqlcmd
的依赖。SqlServer
当然,它是第一方模块,但默认情况下它仍未随 Windows 一起提供。你肯定会失去一些由其他人在没有封装的情况下完成的封装的好处Invoke-Sqlcmd
,但仍然需要权衡。