SQL Server 2014에서 CLR 충돌 (Windows 2012R2)


12

열의 문자열에서 RegEX 함수를 수행하는이 작은 CLR이 있습니다.

Windows Server 2012R2의 SQL Server 2014 (12.0.2000)에서 실행할 때 프로세스가 충돌합니다

메시지 0, 수준 11, 상태 0, 줄 0 현재 명령에서 심각한 오류가 발생했습니다. 결과가 있으면 버려야합니다.

내가 할 경우 스택 덤프를 제공합니다

select count (*) from table where (CLRREGEX,'Regex')

하지만 내가 할 때

select * from table where (CLRREGEX,'Regex') 

행을 반환합니다.

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);
    }
}

따라서 약간의 변경만으로도 현재 작동합니다. 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);
 }

이것은 모든 패턴 또는이 패턴에서만 발생합니까? 비효율적 인 패턴 일 수 있습니다 (예 : 과도한 역 추적 또는 불필요한 캡처). MatchTimeout 속성 을 설정해야 합니다 (.NET Framework 4.5의 새로운 기능). RegEx 함수를 직접 코딩 했습니까? 그렇다면 정적 또는 인스턴스 RegEx 메서드를 사용하고 있습니까? 는 IS SqlFunction방법으로 표시 IsDeterministic=true? 어셈블리가로 표시되어 SAFE있습니까?
Solomon Rutzky

2
이 테이블이 얼마나 큽니까? 또한 문제점 설명에 대한 예상 계획에 병렬 연산자가 있는지 확인할 수 있습니까? 그렇다면 병렬 처리없이 (예 : MAXDOP = 1 힌트) 문제가 발생하는지 확인할 수 있습니다.
Banerjee를

2
중복 [SqlFunction]속성을 제외하고 코드가 잘 보입니다 . 그게 정확한 코드입니까? 나는 그것이 컴파일 될 것이라고 생각하지 않습니다. CLR 버전 4에 바인딩 된 SQL Server 2014를 사용하기 때문에 4.0 / 4.5 / 4.5.x / etc 또는 그 서버에있는 모든 것을 사용할 때 Framework 버전 2.0 / 3.0 / 3.5 구별은 문제가되지 않습니다. 서버에 문제가 32 비트로 표시됩니까? 다른 서버와 비교하여 얼마나 많은 메모리가 있습니까? 오류가 발생한 직후에 SQL Server 로그를 확인 했습니까?
Solomon Rutzky

2
.NET의 정확한 버전은 문제와 관련이 없지만 모든 서버가 4.5 이상인지 확인하는 것이 좋으므로 새 MatchTimeout속성을 사용할 수 있습니다 . 그러나 5 자 이하로 전달하는 경우 실제로 이것이 문제라고 생각하지 않습니다. 이다 이 하나의 기계가의 .NET Framework 설치 손상이 것이 가능하고, 송어 낚시 활동이 ;-) 중단 한 후 그 수리 할 수있다. 또한 [0-9].*첫 번째 자리 뒤의 모든 문자와 일치하므로 간단하지만 비효율적입니다. 그냥 사용 [0-9]하는 IsMatch것이 좋습니다.
Solomon Rutzky

1
당신은 왜 변경 않았다 DataAccessKind하려면 Read? 그것은 속도를 늦추고 데이터 액세스를하지 않습니다. 또한, 나는 그것이 현재 작동하고있는 것처럼 보이지만 ToString이 인코딩을 올바르게 처리하지 않는다고 생각하는 것처럼 속성과 ToString()반대 되는 방법 을 사용하는 데 신중해야 Value합니다. 데이터베이스 데이터 정렬은 무엇으로 설정되어 있습니까? 물론 위의 의견 중 하나를 다시 읽고 열이 NVARCHAR 대신 VARCHAR임을 알 수 있습니다. 해당 필드가 데이터베이스와 다른 데이터 정렬을 가지고 있습니까?
Solomon Rutzky

답변:


4

문제는 Windows OS와 SQL Server (특히 어셈블리가로드 된 데이터베이스) 간의 로캘 충돌입니다. 다음 쿼리를 실행하여 둘 다 무엇을 설정했는지 확인할 수 있습니다.

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

그들이 다르다면, 당신이보고있는 것과 같은 "홀수"행동을 확실히 얻을 수 있습니다. 문제는 다음과 같습니다.

  • SqlString텍스트 자체 만 포함합니다. 여기에는 어셈블리가 존재하는 데이터베이스의 기본 데이터 정렬이 포함됩니다. 데이터 정렬은 로캘 정보 (예 : LCID)와 대 / 소문자, 악센트, 가나, 너비 또는 모든 항목 (이진 및 이진 2)에 대한 민감도를 자세히 나타내는 비교 옵션 (예 : SqlCompareOptions)의 두 가지 정보로 구성됩니다.
  • .NET의 문자열 작업은 명시 적으로 로캘이 지정되지 않은 경우 Windows (예 : 운영 체제 / OS)에 설정된 현재 스레드의 로캘 정보를 사용합니다.

충돌은 일반적으로 .Value또는 .ToString()로 암시 적으로 변환하지 않도록 SqlString 매개 변수를 참조 할 때 발생합니다 SqlString. 이 경우 LCID가 일치하지 않는다는 예외가 발생합니다.

이 경우에 표시된 것처럼 Regex를 사용할 때를 포함하여 (일부 / 모두?) 문자열 비교 수행과 같은 다른 시나리오가 있습니다 (지금까지 이것을 재현 할 수는 없었습니다).

수정에 대한 몇 가지 아이디어 :

이상적 (비교 방법에 대한 기대는 항상 충족 됨) :

  • Windows 또는 SQL Server LCID (기본 언어)를 모두 일치하도록 변경하십시오.

이상적이지 않음 (Windows 로케일의 동작은 동일 및 정렬에 대해 동일한 규칙이 아니므 로 예상치 못한 결과가 발생할 있음) :

  • SQL Server LCID없이 문자열을 반환 하는 .ToString메서드 나 .Value속성을 사용하면 작업이 모두 OS LCID를 사용하게됩니다.

도움이 될 수 있습니다 :

  • SQL Server에서 LCID 및 데이터 정렬 정보를 가져 오지 않으므로 SqlChars대신 사용하십시오.SqlString
  • 다음을 통해 문화가 중요하지 않도록 지정하십시오 StringComparison.InvariantCulture.
    • String.Compare(string, string, StringComparison.InvariantCulture) 또는 String.Compare(string, string, StringComparison.InvariantCultureIgnoreCase)
    • 정규식에 대해 다음을 지정하십시오. RegexOptions.CultureInvariant

1

업데이트 ..

@srutzky가 지적한 것처럼 SQL 엔진과 창 서버의 지역화는 다릅니다.

os_language_version SqlServerLCID
1033 1039

다음 코드 변경-옵션을 설정 RegexOptions.CultureInvariant하면 오류가 발생합니다. 변경되지 않은 코드는 동일한 언어 설정을 사용하는 Windows Server 2012R2에서 SQL Server 2012와 충돌하지 않지만 SQL Server 2014에서는 충돌합니다.

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)]
    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);
 }

충돌 한 서버에서 다음을 실행할 수 있습니까? SELECT os_language_version, SERVERPROPERTY('LCID') AS 'SqlServerLCID' FROM sys.dm_os_windows_info;. 언어 설정에서 문제가 발생했을 가능성이 있습니다. 귀하의 솔루션이 여전히 최선의 방법 일지 모르지만 일반적으로 ToString()Value속성 대신 사용할 필요는 없습니다 SqlString. 따라서 상황을 확인하는 것이 좋을 것입니다.
Solomon Rutzky 2016 년

명확히하기 위해 답변을 게시했지만 변수를에 전달하지 않기 때문에 설정 하여 문제를 해결 해서는 안됩니다 . 원래 코드와 새로운 작동 코드 사이에서 변경된 것은를 사용 하는 것 입니다. 를 사용하도록 전환하면 동일한 고정 동작이 표시 될 것 입니다. 그러나 나는 그것을 시험으로 할 것입니다. 가장 좋은 방법은 Windows 또는 SQL Server의 LCID를 다른 것과 일치하도록 변경하는 것입니다. Options 정적 변수를 제거 할 수도 있습니다. RegexOptions.CultureInvariantOptionsRegex.IsMatch(sqldata, regex)SqlString.ValueSqlString.ToString()SqlChars
Solomon Rutzky 2016 년

안녕. 내 답변을 수락 해 주셔서 감사합니다 :). 언급 한 바에 따르면, 추가 조사를 수행 한 결과 내가 본 내용을 이해 한 경우 OS와 SQL Server간에 다른 LCID가되는 근본 원인에 대해서는 정확하지만 .Value속성 과 관련이 없거나 관련되어서는 안됩니다 는 메서드 SqlString와 동일한 내부 값을 반환합니다 .ToString(). 나는 여전히 조사 중이며 내가 찾은 것으로 내 대답을 업데이트 할 것입니다 :).
Solomon Rutzky

새로운 정보에 비추어 답변을 조정했습니다. 이 시나리오를 재현 할 수 없습니다. 질문의 코드가 실제로 사용중인 것입니까? 그들 사이의 유일한 차이점은 오류가 사용 RegexOptions.IgnoreCase하는 반면 다른 것은 사용 하지 않는 것입니다. 내가 비슷한 환경을 설정 한 : 윈도우 (8.0) 1033 LCID를 사용하여, SQL 서버 DB는 당신이 일을 게시하는 것과 같은 정규식을 사용하여, 1039 LCID가 있습니다 COUNT(*)A의 VARCHAR패턴을 사용하여 GUID를 가득 필드 '[0-3â].*'테이블에, 천만 행. 그것은 중요하지 않다고 생각하지만 SQL Server 2012는 2014가 아닙니다.
Solomon Rutzky

1
모든 답변에 감사드립니다. 질문의 코드는 내가 사용하고있는 것입니다. 나는 정말 복잡한 정규 표현식을 가지고 있었지만 매우 간단한 것을 사용 하여이 문제를 해결할 수있었습니다. RegexOptions.CultureInvariant 설정을 변경하면 동작이 중지되었습니다
Spörri
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.