Arresto anomalo del CLR su SQL Server 2014 (windows 2012R2)


12

Ho questo piccolo CLR che svolge una funzione RegEX su una stringa in colonne.

Durante l'esecuzione su SQL Server 2014 (12.0.2000) su Windows Server 2012R2 il processo si arresta in modo anomalo

Messaggio 0, livello 11, stato 0, riga 0 Si è verificato un errore grave nel comando corrente. I risultati, se presenti, devono essere eliminati.

e dà una discarica dello stack se lo faccio

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

ma quando lo faccio

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

restituisce le righe.

Funziona perfettamente sulla stessa build di SQL Server in esecuzione su Windows 8.1.

Qualche idea?

- Modifica È il più semplice possibile

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

Quindi, con piccole modifiche, questo funziona ora: la lezione principale in C # sembra essere la stessa di TSQL, attenzione alla conversione implicita dei dati.

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

Questo succede per tutti i modelli o solo per questo? Potrebbe essere un modello inefficiente (ad es. Backtracking eccessivo o catture non necessarie). Dovresti cercare di impostare la proprietà MatchTimeout (nuova in .NET Framework 4.5). Hai codificato tu stesso la funzione RegEx? In tal caso, stai usando metodi RegEx statici o di istanza? Il SqlFunctionmetodo è contrassegnato come IsDeterministic=true? L'assemblaggio è contrassegnato come SAFE?
Solomon Rutzky,

2
Quanto sono grandi questi tavoli? Inoltre, potresti verificare se il piano stimato per le dichiarazioni del problema ha un operatore parallelo? In caso affermativo, è possibile verificare se il problema si verifica senza parallelismo, ad esempio con un suggerimento MAXDOP = 1.
Amit Banerjee,

2
Il codice sembra corretto, tranne per l' [SqlFunction]attributo duplicato . È questo il codice esatto? Non penso che sarebbe compilare. La distinzione Framework versione 2.0 / 3.0 / 3.5 non è un problema poiché si utilizza 4.0 / 4.5 / 4.5.x / etc o qualsiasi altra cosa si trovi su quel server poiché ci si trova su SQL Server 2014 che è vincolato alla versione 4. di CLR server che mostra il problema a 32 bit? Quanta memoria ha rispetto agli altri server? E hai controllato i log di SQL Server subito dopo aver ricevuto quell'errore?
Solomon Rutzky,

2
La versione esatta di .NET non è correlata al problema, anche se sarebbe bello sapere se tutti i server sono su almeno 4.5 poiché ciò significherebbe che è possibile utilizzare la nuova MatchTimeoutproprietà. Ma non penso che questo sia davvero il problema se stai passando solo 5 caratteri al massimo. Si è possibile che questa macchina ha un corrotto installazione di .NET Framework, e che può essere riparato una volta trota attività di pesca hanno cessato ;-). Inoltre, [0-9].*è semplice ma anche inefficiente poiché corrisponde a tutti i caratteri, se presenti, dopo la prima cifra; usare solo [0-9]per un IsMatchè meglio.
Solomon Rutzky,

1
Perché sei passato DataAccessKinda Read? Questo rallenta e non stai facendo alcun accesso ai dati. Inoltre, mi rendo conto che sembra funzionare ora, ma sarei cauto nell'usare il ToString()metodo rispetto alla Valueproprietà in quanto non penso che ToString gestisca correttamente le codifiche o qualcosa del genere. Qual è l'impostazione delle regole di confronto dei database? Ovviamente, ho appena riletto uno dei tuoi commenti sopra e ho visto che la colonna è VARCHAR invece di NVARCHAR. Quel campo ha regole di confronto diverse rispetto al database?
Solomon Rutzky,

Risposte:


4

Il problema è un conflitto locale tra il sistema operativo Windows e SQL Server (in particolare il database in cui è caricato l'Assemblea). È possibile eseguire la query seguente per vedere su cosa sono entrambi impostati:

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

Se sono diversi, puoi sicuramente ottenere un comportamento "strano", come quello che stai vedendo. Il problema è che:

  • SqlStringinclude più del semplice testo: include le regole di confronto predefinite del database in cui esiste l'assembly. Le regole di confronto sono composte da due informazioni: le informazioni sulla locale (es. LCID) e le opzioni di confronto (es. SqlCompareOptions) che dettagliano la sensibilità a maiuscole, accenti, kana, larghezza o qualsiasi altra cosa (binaria e binaria2).
  • Le operazioni con stringhe in .NET, a meno che non venga specificata esplicitamente una locale, utilizzano le informazioni sulla locale del thread corrente, che è impostato in Windows (ovvero il sistema operativo / sistema operativo).

Il conflitto si verifica in genere quando si fa riferimento a un parametro SqlString senza utilizzare .Valueo in .ToString()modo da eseguire una conversione implicita SqlString. In tal caso, si verificherebbe un'eccezione che afferma che i LCID non corrispondono.

Apparentemente ci sono altri scenari, come l'esecuzione di confronti di stringhe (alcuni / tutti?), Anche quando si utilizza Regex come mostra questo caso (anche se finora non sono stato in grado di riprodurlo).

Alcune idee per le correzioni:

Ideale (le aspettative saranno sempre soddisfatte su come funzionano i confronti):

  • Modificare il LCID di Windows o SQL Server (lingua predefinita) in modo che entrambi corrispondano

Meno che ideale (il comportamento delle impostazioni internazionali di Windows potrebbe non essere le stesse regole per l'uguaglianza e l'ordinamento e quindi potrebbero esserci risultati imprevisti):

  • Utilizzare il .ToStringmetodo o la .Valueproprietà, che restituiscono entrambi la stringa senza il LCID di SQL Server in modo che tutte le operazioni utilizzino il LCID del sistema operativo.

Potrebbe aiutare:

  • Forse usare SqlCharsinvece di SqlStringcome non porta il LCID e le informazioni di confronto da SQL Server
  • Specifica che la cultura non ha importanza tramite StringComparison.InvariantCulture:
    • String.Compare(string, string, StringComparison.InvariantCulture) o String.Compare(string, string, StringComparison.InvariantCultureIgnoreCase)
    • Per Regex, specificare RegexOptions.CultureInvariant

1

Aggiornato ..

La localizzazione è diversa tra il motore SQL e il server della finestra come sottolinea @srutzky:

os_language_version SqlServerLCID
1033 1039

La seguente modifica al codice: l'impostazione dell'opzione RegexOptions.CultureInvariantaggira l'errore. Il codice invariato non arresta in modo anomalo SQL Server 2012 su Windows Server 2012R2 con le stesse impostazioni di lingua ma lo fa su 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);
 }

Potete per favore eseguire le seguenti operazioni sul server che è stato blocca: SELECT os_language_version, SERVERPROPERTY('LCID') AS 'SqlServerLCID' FROM sys.dm_os_windows_info;. È del tutto possibile che il problema fosse un conflitto nelle impostazioni della lingua. La tua soluzione potrebbe essere ancora il modo migliore di procedere, ma in genere non dovrebbe essere necessario utilizzare al ToString()posto della Valueproprietà su SqlStrings. Quindi sarebbe bello confermare la situazione.
Solomon Rutzky,

Ho pubblicato una risposta per chiarire, ma il problema non dovrebbe essere risolto impostando RegexOptions.CultureInvariantpoiché non si passa la Optionsvariabile Regex.IsMatch(sqldata, regex). La cosa che è cambiata tra il tuo codice originale e il nuovo codice funzionante è che sei passato dall'uso SqlString.Valuea SqlString.ToString(). Ho il sospetto che vedresti lo stesso comportamento fisso se passassi all'utilizzo SqlChars. Ma lo farei solo come test. L'approccio migliore consiste nel modificare il LCID di Windows o SQL Server in modo che corrisponda all'altro. Puoi anche rimuovere la variabile statica Opzioni.
Solomon Rutzky,

Ciao. Grazie per aver accettato la mia risposta :). Solo per citare, ho fatto ulteriori ricerche e, se ho capito cosa stavo vedendo, allora mentre ho ragione sul fatto che la causa principale è un LCID diverso tra il sistema operativo e SQL Server, non è, o non dovrebbe essere, correlato alla .Valueproprietà di a SqlStringche apparentemente restituisce lo stesso valore interno del .ToString()metodo. Sto ancora indagando e aggiornerò la mia risposta con qualunque cosa trovo :).
Solomon Rutzky,

Ho modificato la mia risposta alla luce di nuove informazioni. Non riesco a riprodurre questo scenario. Il codice nella domanda è davvero quello che stavi / stai usando? L'unica vera differenza tra loro è che quello che usa gli errori RegexOptions.IgnoreCasementre l'altro no. Ho creato un ambiente simile: Windows (8.0) usando LCID di 1033, SQL Server DB ha LCID di 1039, usando lo stesso RegEx che hai pubblicato, facendo COUNT(*)un VARCHARcampo riempito con GUID, usando un modello di '[0-3â].*', su una tabella con 10 milioni di righe. È SQL Server 2012, non 2014, anche se non credo che dovrebbe importare.
Solomon Rutzky,

1
Grazie per tutte le risposte Il codice nella domanda è quello che stavo usando. Ho avuto una regex davvero complicata, ma sono riuscito a bloccarla usando una molto semplice. La modifica delle impostazioni RegexOptions.CultureInvariant ha interrotto il comportamento
Spörri
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.