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.