Blocage de CLR sur SQL Server 2014 (Windows 2012R2)

12

J'ai ce petit CLR qui fait une fonction RegEX sur une chaîne de colonnes.

Lors de l'exécution sur SQL Server 2014 (12.0.2000) sur Windows Server 2012R2, le processus se bloque avec

Msg 0, niveau 11, état 0, ligne 0 Une erreur grave s'est produite sur la commande actuelle. Le cas échéant, les résultats doivent être ignorés.

et donne un vidage de la pile si je fais

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

mais quand je fais

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

il renvoie les lignes.

Fonctionne parfaitement sur la même version de SQL Server exécutée sur Windows 8.1.

Des idées?

- Modifier C'est aussi simple que possible

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

Donc, par petits changements, cela fonctionne maintenant: la leçon principale en C # semble être la même que dans TSQL, méfiez-vous de la conversion implicite des données.

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
la source
Est-ce que cela se produit pour tous les modèles ou seulement celui-ci? Ce pourrait être un modèle inefficace (c'est-à-dire un retour en arrière excessif ou des captures inutiles). Vous devez examiner la définition de la propriété MatchTimeout (nouvelle dans .NET Framework 4.5). Avez-vous codé la fonction RegEx vous-même? Si oui, utilisez-vous des méthodes RegEx statiques ou d'instance? La SqlFunctionméthode est-elle marquée comme IsDeterministic=true? L'assemblage est-il marqué comme SAFE?
Solomon Rutzky
2
Quelle est la taille de ces tables? De plus, pourriez-vous vérifier si le plan estimé pour les énoncés de problème a un opérateur parallèle? Si oui, pourriez-vous vérifier si le problème se produit sans parallélisme, c'est-à-dire avec un indice MAXDOP = 1.
Amit Banerjee
2
Le code semble correct, à l'exception de l' [SqlFunction]attribut en double . Est-ce le code exact? Je ne pense pas que cela se compilerait. La distinction Framework version 2.0 / 3.0 / 3.5 n'est pas un problème car vous utilisez 4.0 / 4.5 / 4.5.x / etc ou tout ce qui se trouve sur ce serveur puisque vous êtes sur SQL Server 2014 qui est lié à CLR version 4. Est-ce que serveur montrant le problème 32 bits? Combien de mémoire possède-t-il par rapport aux autres serveurs? Et avez-vous vérifié les journaux SQL Server juste après avoir obtenu cette erreur?
Solomon Rutzky
2
La version exacte de .NET n'est pas liée au problème, mais ce serait bien de savoir si tous les serveurs sont sur au moins 4.5 car cela signifierait que vous pouvez utiliser la nouvelle MatchTimeoutpropriété. Mais je ne pense pas que ce soit vraiment le problème non plus si vous ne passez que 5 caractères max. Il est possible que cette machine ait une installation corrompue du .NET Framework, et qu'elle puisse être réparée une fois que les activités de pêche à la truite ont cessé ;-). En outre, [0-9].*est simple mais aussi inefficace car il correspond à tous les caractères, le cas échéant, après le premier chiffre; utiliser juste [0-9]pour un IsMatchest mieux.
Solomon Rutzky
1
Pourquoi avez-vous changé DataAccessKindpour Read? Cela ralentit simplement et vous ne faites aucun accès aux données. De plus, je me rends compte que cela semble fonctionner maintenant, mais je serais prudent avec l'utilisation de la ToString()méthode par opposition à la Valuepropriété car je ne pense pas que ToString gère correctement les encodages, ou quelque chose comme ça. À quoi sert le classement de vos bases de données? Bien sûr, je viens de relire l'un de vos commentaires ci-dessus et de voir que la colonne est VARCHAR au lieu de NVARCHAR. Ce champ a-t-il un classement différent de celui de la base de données?
Solomon Rutzky

Réponses:

4

Le problème est un conflit de paramètres régionaux entre le système d'exploitation Windows et SQL Server (en particulier la base de données où l'assembly est chargé). Vous pouvez exécuter la requête suivante pour voir à quoi ils sont tous deux définis:

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

S'ils sont différents, vous pouvez certainement obtenir un comportement "étrange", comme ce que vous voyez. Le problème est que:

  • SqlStringinclut plus que le texte lui-même: il inclut le classement par défaut de la base de données dans laquelle l'assembly existe. Le classement est composé de deux informations: les informations locales (c.-à-d. LCID) et les options de comparaison (c.-à-d. SqlCompareOptions) qui détaillent la sensibilité à la casse, aux accents, au kana, à la largeur ou à tout (binaire et binaire2).
  • Les opérations de chaîne dans .NET, sauf indication explicite de paramètres régionaux, utilisez les informations de paramètres régionaux du thread actuel, qui sont définies dans Windows (c'est-à-dire le système d'exploitation / OS).

Le conflit se produit généralement lors du référencement d'un paramètre SqlString sans utiliser .Valueou .ToString()tel qu'il effectue une conversion implicite vers SqlString. Dans ce cas, cela provoquerait une exception indiquant que les LCID ne correspondent pas.

Il existe apparemment d'autres scénarios, tels que des comparaisons de chaînes (certaines / toutes?), Y compris lors de l'utilisation de Regex comme le montre ce cas (bien que jusqu'à présent je n'ai pas été en mesure de reproduire cela).

Quelques idées de correctifs:

Idéal (les attentes seront toujours satisfaites quant au fonctionnement des comparaisons):

  • Modifiez le LCID Windows ou SQL Server (langue par défaut) afin que les deux correspondent

Moins qu'idéal (le comportement des paramètres régionaux Windows peut ne pas être les mêmes règles d'égalité et de tri et il peut donc y avoir des résultats inattendus):

  • Utilisez la .ToStringméthode ou la .Valuepropriété, qui renvoient toutes les deux la chaîne sans le LCID SQL Server afin que les opérations utilisent toutes le LCID du système d'exploitation.

Pourrait aider:

  • Peut-être utiliser SqlCharsau lieu de SqlStringcar il n'apporte pas le LCID et les informations de classement de SQL Server
  • Spécifiez que la culture n'a pas d'importance via StringComparison.InvariantCulture:
    • String.Compare(string, string, StringComparison.InvariantCulture) ou String.Compare(string, string, StringComparison.InvariantCultureIgnoreCase)
    • Pour Regex, spécifiez RegexOptions.CultureInvariant
Solomon Rutzky
la source
1

Mis à jour..

La localisation est différente entre le moteur SQL et le serveur de fenêtres comme le souligne @srutzky:

os_language_version SqlServerLCID
1033 1039

La modification suivante du code - la définition de l'option RegexOptions.CultureInvariantcontourne l'erreur. Le code inchangé ne plantera pas SQL Server 2012 sur Windows Server 2012R2 avec les mêmes paramètres de langue mais le fait sur 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
la source
Pouvez - vous s'il vous plaît exécutez les commandes suivantes sur le serveur qui plantait: SELECT os_language_version, SERVERPROPERTY('LCID') AS 'SqlServerLCID' FROM sys.dm_os_windows_info;. Il est fort possible que le problème soit un conflit dans les paramètres de langue. Votre solution est peut-être encore la meilleure solution, mais en général, il ne devrait pas être nécessaire d'utiliser à la ToString()place de la Valuepropriété du par SqlString. Ce serait donc bien de confirmer la situation.
Solomon Rutzky
J'ai posté une réponse pour clarifier, mais le problème ne devrait pas être résolu en définissant RegexOptions.CultureInvariantpuisque vous ne transmettez pas la Optionsvariable à Regex.IsMatch(sqldata, regex). La chose qui a changé entre votre code d'origine et le nouveau code de travail est que vous êtes passé de SqlString.Valueà SqlString.ToString(). Je soupçonne que vous verriez le même comportement fixe si vous passiez à l'utilisation SqlChars. Mais je ferais juste cela comme un test. La meilleure approche consiste à modifier le LCID de Windows ou SQL Server pour correspondre à l'autre. Vous pouvez également supprimer la variable statique Options.
Solomon Rutzky
Salut. Merci d'avoir accepté ma réponse :). Juste pour mentionner, j'ai fait des recherches plus approfondies et, si j'ai compris ce que je voyais, alors que j'ai raison de dire que la cause racine est un LCID différent entre le système d'exploitation et SQL Server, il n'est pas, ou ne devrait pas être, lié à la .Valuepropriété d'un SqlStringas qui renvoie apparemment la même valeur interne que la .ToString()méthode. J'étudie toujours et mettrai à jour ma réponse avec tout ce que je trouverai :).
Solomon Rutzky du
J'ai ajusté ma réponse à la lumière de nouvelles informations. Je ne peux pas reproduire ce scénario. Le code de la question est-il vraiment ce que vous utilisiez / utilisiez? La seule vraie différence entre eux est que celui que les erreurs utilisent RegexOptions.IgnoreCasetandis que l'autre ne le fait pas. J'ai mis en place un environnement similaire: Windows (8.0) en utilisant LCID de 1033, SQL Server DB a LCID de 1039, en utilisant le même RegEx que vous avez publié, en faisant un COUNT(*)sur un VARCHARchamp rempli de GUID, en utilisant un modèle de '[0-3â].*', sur une table avec 10 millions de lignes. C'est SQL Server 2012, pas 2014, bien que je ne pense pas que cela devrait avoir d'importance.
Solomon Rutzky
1
Merci pour toutes les réponses. Le code dans la question est ce que j'utilisais. J'ai eu un regex vraiment compliqué mais j'ai réussi à planter cela en utilisant un très simple. La modification des paramètres RegexOptions.CultureInvariant a arrêté le comportement
Spörri