【发布时间】:2016-05-17 22:56:32
【问题描述】:
我编写了以下SQL CLR 函数以散列大于8000 字节的字符串值(T-SQL 内置HASHBYTES 函数的输入值限制):
[SqlFunction(DataAccess = DataAccessKind.None, IsDeterministic = true)]
public static SqlBinary HashBytes(SqlString algorithm, SqlString value)
{
HashAlgorithm algorithmType = HashAlgorithm.Create(algorithm.Value);
if (algorithmType == null || value.IsNull)
{
return new SqlBinary();
}
else
{
byte[] bytes = Encoding.UTF8.GetBytes(value.Value);
return new SqlBinary(algorithmType.ComputeHash(bytes));
}
}
它适用于拉丁字符串。例如,以下哈希是相同的:
SELECT dbo.fn_Utils_GetHashBytes ('MD5', 'test'); -- 0x098F6BCD4621D373CADE4E832627B4F6
SELECT HASHBYTES ('MD5', 'test'); -- 0x098F6BCD4621D373CADE4E832627B4F6
问题在于它不适用于西里尔字符串。例如:
SELECT dbo.fn_Utils_GetHashBytes ('MD5 ', N'даровете на влъхвите') -- NULL
SELECT HashBytes ('MD5 ',N'даровете на влъхвите') -- 0x838B1B625A6074B2BE55CDB7FCEA2832
SELECT dbo.fn_Utils_GetHashBytes ('SHA256', N'даровете на влъхвите') -- 0xA1D65374A0B954F8291E00BC3DD9DF655D8A4A6BF127CFB15BBE794D2A098844
SELECT HashBytes ('SHA2_256',N'даровете на влъхвите') -- 0x375F6993E0ECE1864336E565C8E14848F2A4BAFCF60BC0C8F5636101DD15B25A
我得到了NULL 的MD5,尽管如果代码作为控制台应用程序执行,它会返回值。谁能告诉我我做错了什么?
另外,我从here 得到了这个函数,其中一个 cmets 说:
小心 CLR SP 参数被静默截断为 8000 字节 - 我必须用 [SqlFacet(MaxSize = -1)] 标记参数,否则第 8000 个之后的字节将被忽略!
但我已经对此进行了测试,并且工作正常。例如,如果我生成一个 8000 字节字符串的散列和相同字符串加一个符号的第二个散列,我得到的散列是不同的。
DECLARE @A VARCHAR(MAX) = '8000 bytes string...'
DECLARE @B VARCHAR(MAX) = @A + '1'
SELECT LEN(@A), LEN(@B)
SELECT IIF(dbo.fn_Utils_GetHashBytes ('MD5', @A + '1') = dbo.fn_Utils_GetHashBytes ('MD5', @B), 1, 0) -- 0
我应该担心这个吗?
【问题讨论】:
-
关于 NULL - 你在 MD5 字符串后面有一个额外的空格,所以没有创建哈希算法。删除应该可以解决它。
-
关于不同的哈希值:内置
HashBytes在您的情况下对 unicode 字符串(即 utf-16)进行操作。另一方面,您的函数在计算哈希之前接受 utf-16 输入并将其转换为 utf-8。输入不同,哈希值也不同。 -
不。在拉丁文情况下,这两个函数都采用 ASCII 输入(没有
N前缀),对于 ASCII 字符串,utf-8 表示是相同的,因此哈希值相等。