Absturz der CLR unter SQL Server 2014 (Windows 2012R2)

12

Ich habe diese kleine CLR, die eine RegEX-Funktion für eine Zeichenfolge in Spalten ausführt.

Bei der Ausführung unter SQL Server 2014 (12.0.2000) unter Windows Server 2012R2 stürzt der Prozess mit ab

Meldung 0, Ebene 11, Status 0, Zeile 0 Beim aktuellen Befehl ist ein schwerwiegender Fehler aufgetreten. Die Ergebnisse sollten, falls vorhanden, verworfen werden.

und gibt einen Stapelspeicherauszug, wenn ich tue

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

Aber wenn ich das mache

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

es gibt die Zeilen zurück.

Funktioniert perfekt mit demselben SQL Server-Build, der unter Windows 8.1 ausgeführt wird.

Irgendwelche Ideen?

- Bearbeiten Es ist so einfach wie es nur geht

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

Durch kleine Änderungen funktioniert dies jetzt: Die Hauptstunde in C # scheint dieselbe zu sein wie in TSQL. Achten Sie auf implizite Datenkonvertierung.

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
quelle
Passiert das für alle Muster oder nur für dieses? Dies kann ein ineffizientes Muster sein (z. B. übermäßiges Zurückverfolgen oder unnötige Erfassung). Sie sollten sich die Einstellung der MatchTimeout- Eigenschaft ansehen (neu in .NET Framework 4.5). Haben Sie die RegEx-Funktion selbst codiert? Wenn ja, verwenden Sie statische oder Instanz-RegEx-Methoden? Ist die SqlFunctionMethode als markiert IsDeterministic=true? Ist die Baugruppe markiert als SAFE?
Solomon Rutzky,
2
Wie groß sind diese Tische? Können Sie auch überprüfen, ob der geschätzte Plan für die Problembeschreibungen einen Paralleloperator hat? Wenn ja, können Sie überprüfen, ob das Problem ohne Parallelität auftritt, dh mit einem MAXDOP = 1-Hinweis.
Amit Banerjee
2
Der Code sieht gut aus, mit Ausnahme des doppelten [SqlFunction]Attributs. Ist das der genaue Code? Ich denke nicht, dass das kompilieren würde. Die Unterscheidung zwischen Framework Version 2.0 / 3.0 / 3.5 ist kein Problem, da Sie 4.0 / 4.5 / 4.5.x / etc verwenden oder was auch immer sich auf diesem Server befindet, da Sie sich in SQL Server 2014 befinden, das an CLR Version 4 gebunden ist Server zeigt das Problem 32-Bit? Wie viel Speicher hat es im Vergleich zu den anderen Servern? Haben Sie die SQL Server-Protokolle überprüft, nachdem Sie diesen Fehler erhalten haben?
Solomon Rutzky
2
Die genaue Version von .NET hängt nicht mit dem Problem zusammen. Es wäre jedoch gut zu wissen, ob alle Server mindestens auf 4.5 laufen, da Sie dann die neue MatchTimeoutEigenschaft verwenden können. Aber ich denke nicht, dass das wirklich das Problem ist, auch wenn Sie nur maximal 5 Zeichen eingeben. Es ist möglich, dass auf diesem Computer eine beschädigte Installation von .NET Framework vorhanden ist, die repariert werden kann, sobald die Forellenfischerei eingestellt wurde ;-). Auch [0-9].*ist einfach, aber auch ineffizient, da es allen Zeichen nach der ersten Ziffer entspricht, falls vorhanden; mit nur [0-9]für eine IsMatchist besser.
Solomon Rutzky
1
Warum haben Sie möglicherweise DataAccessKindzu Read? Das verlangsamt es nur und Sie machen keinen Datenzugriff. Außerdem ist mir klar, dass es jetzt zu funktionieren scheint, aber ich würde vorsichtig sein, wenn ich die ToString()Methode Valueanstelle der Eigenschaft verwende, da ich denke, dass ToString Kodierungen oder ähnliches nicht richtig handhabt. Auf welche Sortierung ist Ihre Datenbank eingestellt? Natürlich habe ich einen Ihrer Kommentare oben noch einmal gelesen und festgestellt, dass die Spalte VARCHAR anstelle von NVARCHAR ist. Hat dieses Feld eine andere Sortierung als die Datenbank?
Solomon Rutzky

Antworten:

4

Das Problem ist ein Gebietsschemakonflikt zwischen dem Windows-Betriebssystem und SQL Server (insbesondere der Datenbank, in die die Assembly geladen wird). Sie können die folgende Abfrage ausführen, um festzustellen, auf welche Werte beide festgelegt sind:

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

Wenn sie sich unterscheiden, können Sie auf jeden Fall ein merkwürdiges Verhalten feststellen, wie z. B. das, was Sie sehen. Das Problem ist, dass:

  • SqlStringenthält mehr als nur den Text selbst: Er enthält die Standardkollatierung der Datenbank, in der die Assembly vorhanden ist. Die Kollatierung besteht aus zwei Informationen: den Informationen zum Gebietsschema (z. B. LCID) und den Vergleichsoptionen (z. B. SqlCompareOptions), die die Empfindlichkeit für Groß- und Kleinschreibung, Akzente, Kana, Breite oder alles (binär und binär2) angeben.
  • Zeichenfolgenvorgänge in .NET verwenden, sofern nicht ausdrücklich ein Gebietsschema angegeben ist, die Gebietsschemainformationen des aktuellen Threads, der in Windows (dh dem Betriebssystem / Betriebssystem) festgelegt ist.

Der Konflikt tritt normalerweise auf, wenn auf einen SqlString-Parameter verwiesen wird, ohne dass .Valueoder .ToString()eine implizite Konvertierung in ausgeführt wird SqlString. In diesem Fall würde eine Ausnahme ausgelöst, die besagt, dass die LCIDs nicht übereinstimmen.

Es gibt anscheinend andere Szenarien, wie das Durchführen (einiger / aller?) Zeichenfolgenvergleiche, einschließlich der Verwendung von Regex, wie dieser Fall zeigt (obwohl ich dies bisher nicht reproduzieren konnte).

Einige Ideen für Korrekturen:

Ideal (Erwartungen bezüglich der Funktionsweise der Vergleiche werden immer erfüllt):

  • Ändern Sie entweder die Windows- oder die SQL Server-LCID (Standardsprache), sodass beide übereinstimmen

Weniger als ideal (das Verhalten des Windows - Gebietsschemas möglicherweise nicht die gleichen Regeln für die Gleichstellung und das Sortieren und so es könnte unerwartete Ergebnisse):

  • Verwenden Sie die .ToStringMethode oder .ValueEigenschaft, die beide die Zeichenfolge ohne die SQL Server-LCID zurückgeben, sodass alle Vorgänge die Betriebssystem-LCID verwenden.

Könnte helfen:

  • Verwenden Sie SqlCharsstattdessen möglicherweise, SqlStringda die LCID- und Sortierungsinformationen von SQL Server nicht mitgebracht werden
  • Festlegen, dass Kultur keine Rolle spielt über StringComparison.InvariantCulture:
    • String.Compare(string, string, StringComparison.InvariantCulture) oder String.Compare(string, string, StringComparison.InvariantCultureIgnoreCase)
    • Geben Sie für Regex an RegexOptions.CultureInvariant
Solomon Rutzky
quelle
1

Aktualisiert..

Die Lokalisierung unterscheidet sich zwischen der SQL Engine und dem Windows Server, wie @srutzky hervorhebt:

os_language_version SqlServerLCID
1033 1039

Die folgende Änderung am Code - Setzen der Option RegexOptions.CultureInvariantumgeht den Fehler. Der unveränderte Code führt unter Windows Server 2012R2 nicht zu einem Absturz von SQL Server 2012 mit denselben Spracheinstellungen, jedoch unter 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
quelle
Können Sie bitte die folgende auf dem Server ausführen, der stürzt: SELECT os_language_version, SERVERPROPERTY('LCID') AS 'SqlServerLCID' FROM sys.dm_os_windows_info;. Es ist durchaus möglich, dass das Problem ein Konflikt in den Spracheinstellungen war. Ihre Lösung ist zwar immer noch der beste Weg, aber im Allgemeinen sollte keine Verwendung ToString()anstelle der ValueEigenschaft auf SqlStrings erforderlich sein . Es wäre also schön, die Situation zu bestätigen.
Solomon Rutzky,
Ich habe eine Antwort gepostet, um dies zu klären, aber das Problem sollte nicht durch Einstellen gelöst werden, RegexOptions.CultureInvariantda Sie die OptionsVariable nicht übergeben Regex.IsMatch(sqldata, regex). Das, was sich zwischen Ihrem ursprünglichen Code und dem neuen, funktionierenden Code geändert hat, ist, dass Sie von der Verwendung SqlString.Valuezu gegangen sind SqlString.ToString(). Ich vermute, Sie würden dasselbe Verhalten feststellen, wenn Sie auf die Verwendung umsteigen würden SqlChars. Aber ich würde das nur als Test machen. Der beste Ansatz besteht darin, die LCID von Windows oder SQL Server so zu ändern, dass sie mit der anderen übereinstimmt. Sie können auch die statische Variable Options entfernen.
Solomon Rutzky
Hallo. Vielen Dank für die Annahme meiner Antwort :). Um nur zu erwähnen, ich habe weitere Nachforschungen angestellt und, wenn ich verstanden habe, was ich gesehen habe, habe ich zwar Recht, dass die Grundursache eine andere LCID zwischen dem Betriebssystem und SQL Server ist, sie hat aber nichts mit der .ValueEigenschaft zu tun oder sollte es auch nicht sein von a SqlStringas gibt anscheinend denselben internen Wert wie die .ToString()Methode zurück. Ich recherchiere noch und werde meine Antwort mit allem, was ich finde, aktualisieren :).
Solomon Rutzky,
Ich habe meine Antwort an neue Informationen angepasst. Ich kann dieses Szenario nicht reproduzieren. Ist der Code in der Frage wirklich das, was Sie verwendet haben / verwenden? Der einzige wirkliche Unterschied besteht darin, dass der eine Fehler verwendet, RegexOptions.IgnoreCasewährend der andere dies nicht tut. Ich habe eine ähnliche Umgebung eingerichtet: Windows (8.0) verwendet eine LCID von 1033, SQL Server DB eine LCID von 1039 und verwendet dieselbe RegEx, die Sie veröffentlicht haben. Dabei wird COUNT(*)in einem VARCHARmit GUIDs gefüllten Feld ein Muster von '[0-3â].*'für eine Tabelle verwendet mit 10 Millionen Zeilen. Es ist SQL Server 2012, nicht 2014, obwohl ich nicht denke, dass das wichtig sein sollte.
Solomon Rutzky
1
Danke für alle Antworten. Der Code in der Frage ist, was ich verwendet habe. Ich hatte einen wirklich komplizierten regulären Ausdruck, aber es gelang mir, diesen mit einem sehr einfachen zum Absturz zu bringen. Durch Ändern der RegexOptions.CultureInvariant-Einstellungen wurde das Verhalten gestoppt
Spörri