Awaria CLR na SQL Server 2014 (Windows 2012R2)

12

Mam ten mały CLR, który wykonuje funkcję RegEX na ciąg znaków w kolumnach.

Podczas uruchamiania w programie SQL Server 2014 (12.0.2000) w systemie Windows Server 2012R2 proces ulega awarii

Wiadomość 0, poziom 11, stan 0, wiersz 0 Wystąpił poważny błąd w bieżącym poleceniu. Ewentualne wyniki należy odrzucić.

i daje zrzut stosu, jeśli to zrobię

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

ale kiedy robię

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

zwraca wiersze.

Działa doskonale na tej samej kompilacji SQL Server działającej w systemie Windows 8.1.

Jakieś pomysły?

- Edycja To jest tak proste, jak to tylko możliwe

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

Tak więc po drobnych zmianach działa to teraz: główna lekcja w języku C # wydaje się być taka sama jak w TSQL, strzeż się niejawnej konwersji danych.

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);
 }
Spörri
źródło
Czy dzieje się tak w przypadku wszystkich wzorów, czy tylko tego? Może to być nieefektywny wzorzec (tj. Nadmierne cofanie się lub niepotrzebne przechwytywanie). Powinieneś przyjrzeć się ustawianiu właściwości MatchTimeout (nowości w .NET Framework 4.5). Czy sam kodowałeś funkcję RegEx? Jeśli tak, to czy używasz metod statycznych lub instancyjnych RegEx? Czy SqlFunctionmetoda jest oznaczona jako IsDeterministic=true? Czy zespół jest oznaczony jako SAFE?
Solomon Rutzky
2
Jak duże są te stoły? Czy możesz również sprawdzić, czy szacowany plan instrukcji problemów ma operatora równoległego? Jeśli tak, czy możesz sprawdzić, czy problem występuje bez równoległości, tj. Z podpowiedź MAXDOP = 1.
Amit Banerjee
2
Kod wygląda dobrze, z wyjątkiem duplikatu [SqlFunction]atrybutu. Czy to dokładnie kod? Nie sądzę, żeby to się skompilowało. Rozróżnienie Framework w wersji 2.0 / 3.0 / 3.5 nie stanowi problemu, ponieważ używasz wersji 4.0 / 4.5 / 4.5.x / etc lub cokolwiek, co znajduje się na tym serwerze, ponieważ korzystasz z SQL Server 2014, który jest powiązany z wersją 4. CLR serwer pokazuje problem w wersji 32-bitowej? Ile pamięci ma w porównaniu do innych serwerów? I czy sprawdziłeś dzienniki SQL Server zaraz po otrzymaniu tego błędu?
Solomon Rutzky
2
Dokładna wersja .NET nie jest związana z tym problemem, choć dobrze byłoby wiedzieć, czy wszystkie serwery mają co najmniej 4.5, ponieważ oznaczałoby to, że możesz użyć nowej MatchTimeoutwłaściwości. Ale nie sądzę, że to jest tak naprawdę problem, jeśli podajesz maksymalnie 5 znaków. To jest możliwe, że jedna maszyna ma uszkodzone zainstalować .NET Framework, i które mogą być naprawione raz pstrąg działalność połowowa przestały ;-). Ponadto [0-9].*jest prosty, ale również nieefektywny, ponieważ pasuje do wszystkich znaków, jeśli występują, po pierwszej cyfrze; używanie tylko [0-9]dla IsMatchjest lepsze.
Solomon Rutzky
1
Dlaczego zmieniłeś się DataAccessKindna Read? To tylko spowalnia i nie masz dostępu do danych. Zdaję sobie również sprawę, że wydaje się, że teraz działa, ale byłbym ostrożny z użyciem tej ToString()metody Valuezamiast właściwości, ponieważ nie sądzę, aby ToString poprawnie obsługiwał kodowanie lub coś w tym rodzaju. Jakie jest ustawienie sortowania baz danych? Oczywiście, po prostu ponownie przeczytałem jeden z twoich komentarzy powyżej i widzę, że kolumna to VARCHAR zamiast NVARCHAR. Czy to pole ma inne zestawienie niż baza danych?
Solomon Rutzky

Odpowiedzi:

4

Problemem jest konflikt ustawień regionalnych między systemem operacyjnym Windows a SQL Server (w szczególności bazą danych, w której zestaw jest ładowany). Możesz uruchomić następujące zapytanie, aby zobaczyć, na co oba są ustawione:

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

Jeśli są różne, na pewno możesz uzyskać pewne „dziwne” zachowanie, takie jak to, co widzisz. Problem polega na tym, że:

  • SqlStringzawiera więcej niż sam tekst: obejmuje domyślne sortowanie bazy danych, w której istnieje zestaw. Zestawienie składa się z dwóch części informacji: informacji o lokalizacji (tj. LCID) oraz opcji porównania (tj. SqlCompareOptions), które wyszczególniają wrażliwość na wielkość liter, akcenty, kana, szerokość lub wszystko (binarne i binarne2).
  • Operacje na łańcuchach w .NET, chyba że podano jawnie ustawienia narodowe, użyj informacji o ustawieniach regionalnych bieżącego wątku, które są ustawione w Windows (tj. System operacyjny / system operacyjny).

Konflikt zwykle występuje, gdy odwołuje się do parametru SqlString bez użycia .Valuelub w .ToString()taki sposób, że dokonuje niejawnej konwersji na SqlString. W takim przypadku spowodowałoby to wyjątek stwierdzający, że LCID nie są zgodne.

Są najwyraźniej inne scenariusze, takie jak przeprowadzanie porównań ciągów (niektóre / wszystkie?), W tym przy użyciu Regex, jak pokazuje ten przypadek (chociaż do tej pory nie byłem w stanie tego odtworzyć).

Kilka pomysłów na poprawki:

Idealny (zawsze będą spełnione oczekiwania dotyczące działania porównań):

  • Zmień LCID systemu Windows lub SQL Server (język domyślny), aby oba były zgodne

Mniej niż idealny (zachowanie ustawień regionalnych Windows może nie być tymi samymi regułami dla równości i sortowania, więc mogą wystąpić nieoczekiwane wyniki):

  • Użyj .ToStringmetody lub .Valuewłaściwości, które zwracają ciąg bez identyfikatora LCID programu SQL Server, aby wszystkie operacje korzystały z identyfikatora LCID systemu operacyjnego.

Może pomóc:

  • Może używać SqlCharszamiast, SqlStringponieważ nie przynosi LCID i informacji o sortowaniu z SQL Server
  • Określ, że kultura nie ma znaczenia poprzez StringComparison.InvariantCulture:
    • String.Compare(string, string, StringComparison.InvariantCulture) lub String.Compare(string, string, StringComparison.InvariantCultureIgnoreCase)
    • W przypadku Regex podaj RegexOptions.CultureInvariant
Solomon Rutzky
źródło
1

Zaktualizowano ..

Lokalizacja różni się między silnikiem SQL a serwerem okien, jak wskazuje @srutzky:

os_language_version SqlServerLCID
1033 1039

Następująca zmiana w kodzie - ustawienie opcji RegexOptions.CultureInvariantomija błąd. Niezmieniony kod nie spowoduje awarii programu SQL Server 2012 w systemie Windows Server 2012R2 z tymi samymi ustawieniami języka, ale dzieje się tak w przypadku programu 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);
 }
Spörri
źródło
Czy możesz uruchomić następujące na serwerze, który został awarii: SELECT os_language_version, SERVERPROPERTY('LCID') AS 'SqlServerLCID' FROM sys.dm_os_windows_info;. Całkiem możliwe, że problemem był konflikt w ustawieniach językowych. Twoje rozwiązanie może nadal być najlepszym rozwiązaniem, ale generalnie nie powinno być potrzeby używania ToString()zamiast Valuewłaściwości na SqlStrings. Byłoby więc miło potwierdzić sytuację.
Solomon Rutzky
Wysłałem odpowiedź, aby wyjaśnić, ale problem nie powinien zostać rozwiązany przez ustawienie, RegexOptions.CultureInvariantponieważ nie przekazujesz Optionszmiennej Regex.IsMatch(sqldata, regex). Rzecz, która zmieniła się pomiędzy oryginalnym kodem i nowego kodeksu pracy, to poszedł z użyciem SqlString.Valuedo SqlString.ToString(). Podejrzewam, że zobaczyłbyś to samo stałe zachowanie, gdybyś przestawił się na używanie SqlChars. Ale zrobiłbym to tylko jako test. Najlepszym rozwiązaniem jest zmiana identyfikatora LCID systemu Windows lub SQL Server, aby pasował do drugiego. Możesz także usunąć zmienną statyczną Opcje.
Solomon Rutzky
Cześć. Dziękuję za zaakceptowanie mojej odpowiedzi :). Wystarczy wspomnieć, że przeprowadziłem dalsze badania i, jeśli zrozumiałem, co widzę, to mimo że mam rację co do tego, że podstawową przyczyną jest inny identyfikator LCID między systemem operacyjnym a programem SQL Server, nie jest on lub nie powinien być związany z .Valuewłaściwością , SqlStringponieważ najwyraźniej zwraca tę samą wartość wewnętrzną co .ToString()metoda. Nadal badam i zaktualizuję swoją odpowiedź tym, co znajdę :).
Solomon Rutzky
Dostosowałem odpowiedź w świetle nowych informacji. Nie mogę odtworzyć tego scenariusza. Czy kod w pytaniu naprawdę jest tym, czego używasz / używasz? Jedyną prawdziwą różnicą między nimi jest to, że ten używa błędów, RegexOptions.IgnoreCasea drugi nie. Skonfigurowałem podobne środowisko: Windows (8.0) przy użyciu LCID 1033, SQL Server DB ma LCID 1039, przy użyciu tego samego RegEx, który napisałeś, robiąc COUNT(*)na VARCHARpolu wypełnionym GUID, używając wzorca '[0-3â].*', na stole z 10 milionami rzędów. To SQL Server 2012, a nie 2014, choć nie uważam, że to powinno mieć znaczenie.
Solomon Rutzky
1
Dziękuję za wszystkie odpowiedzi. Kod w pytaniu jest tym, czego używałem. Miałem naprawdę skomplikowany regex, ale udało mi się go zawiesić za pomocą bardzo prostego. Zmiana ustawień RegexOptions.CultureInvariant zatrzymała zachowanie
Spörri