SQL Server 2014 (windows 2012R2) 上的 CLR 崩溃

Spö*_*rri 12 sql-server sql-clr sql-server-2014

我有这个小型 CLR,它对列中的字符串执行 RegEX 函数。

在 Windows Server 2012R2 上的 SQL Server 2014 (12.0.2000) 上运行时,进程崩溃

Msg 0, Level 11, State 0, Line 0 当前命令发生严重错误。结果,如果有的话,应该被丢弃。

如果我这样做,并给出堆栈转储

select count (*) from table where (CLRREGEX,'Regex')
Run Code Online (Sandbox Code Playgroud)

但是当我这样做的时候

select * from table where (CLRREGEX,'Regex') 
Run Code Online (Sandbox Code Playgroud)

它返回行。

在 Windows 8.1 上运行的相同 SQL Server 版本上完美运行。

有任何想法吗?

-- 编辑就这么简单

using System;
using System.Collections.Generic;
using System.Text;
using System.Data.SqlTypes;           //SqlString, SqlInt32, SqlBoolean
using System.Text.RegularExpressions; //Match, Regex
using Microsoft.SqlServer.Server;     //SqlFunctionAttribute
public partial class UserDefinedFunctions
{
    public static readonly RegexOptions Options = RegexOptions.IgnorePatternWhitespace | RegexOptions.Multiline;
    [SqlFunction]
    [Microsoft.SqlServer.Server.SqlFunction(IsDeterministic = true, IsPrecise = true)]
    public static SqlBoolean RegExMatch(SqlString input, SqlString pattern)
    {
        if (input.IsNull || pattern.IsNull) //nulls dont qualify for a match
            return SqlBoolean.False;
    return Regex.IsMatch(input.Value, pattern.Value, RegexOptions.IgnoreCase);
    }
}
Run Code Online (Sandbox Code Playgroud)

因此,现在可以通过一些小的更改来实现:C# 中的主要课程似乎与 TSQL 中的相同,请注意隐式数据转换。

using System;
using System.Text;
using System.Data.SqlTypes;           //SqlString, SqlInt32, SqlBoolean
using System.Text.RegularExpressions; //Match, Regex
using Microsoft.SqlServer.Server;     //SqlFunctionAttribute
public partial class UserDefinedFunctions
{
public static readonly RegexOptions Options = RegexOptions.IgnorePatternWhitespace | RegexOptions.Singleline | RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.CultureInvariant;

    [Microsoft.SqlServer.Server.SqlFunction(IsDeterministic = true, IsPrecise = true, DataAccess = DataAccessKind.Read)]
    public static SqlBoolean RegExMatch(SqlString input, SqlString pattern)
{
    if (input.IsNull || pattern.IsNull) //nulls dont qualify for a match
        return SqlBoolean.False;
    string sqldata = input.ToString();
    string regex = pattern.ToString();
    return Regex.IsMatch(sqldata, regex);
 }
Run Code Online (Sandbox Code Playgroud)

Sol*_*zky 4

该问题是 Windows 操作系统和 SQL Server(特别是加载程序集的数据库)之间的区域设置冲突。您可以运行以下查询来查看它们的设置:

SELECT os_language_version,
       DATABASEPROPERTYEX(N'{name of DB where Assembly exists}', 'LCID') AS 'DatabaseLCID'
FROM   sys.dm_os_windows_info;
Run Code Online (Sandbox Code Playgroud)

如果它们不同,那么您肯定会得到一些“奇怪”的行为,例如您所看到的。问题在于:

  • SqlString不仅仅包括文本本身:它还包括程序集所在数据库的默认排序规则。排序规则由两条信息组成:区域设置信息(即 LCID)和比较选项(即 SqlCompareOptions),后者详细说明了对大小写、重音、假名、宽度或所有内容(二进制和二进制 2)的敏感性。
  • .NET 中的字符串操作,除非显式给出区域设置,否则使用当前线程的区域设置信息,该信息在 Windows(即操作系统/OS)中设置。

.Value当引用 SqlString 参数而不使用或.ToString()使其隐式转换为 时,通常会发生冲突SqlString。在这种情况下,会导致异常,指出 LCID 不匹配。

显然还有其他场景,例如执行(部分/全部?)字符串比较,包括使用正则表达式时,如本例所示(尽管到目前为止我还无法重现这一点)。

一些修复的想法:

理想(关于比较如何进行的期望将始终得到满足):

  • 更改 Windows 或 SQL Server LCID(默认语言)以使两者匹配

不太理想(Windows 区域设置的行为可能与相等和排序的规则不同,因此可能会出现意外结果):

  • 使用.ToString方法或.Value属性,它们都返回不带 SQL Server LCID 的字符串,因此所有操作都将使用操作系统 LCID。

可能有帮助:

  • 也许使用SqlChars而不是SqlString因为它不会带来来自 SQL Server 的 LCID 和排序规则信息
  • 通过以下方式指定文化并不重要StringComparison.InvariantCulture
    • String.Compare(string, string, StringComparison.InvariantCulture)或者String.Compare(string, string, StringComparison.InvariantCultureIgnoreCase)
    • 对于正则表达式,请指定RegexOptions.CultureInvariant