Сбой CLR на SQL Server 2014 (Windows 2012R2)

12

У меня есть этот маленький CLR, который выполняет функцию RegEX для строки в столбцах.

При запуске на SQL Server 2014 (12.0.2000) в Windows Server 2012R2 процесс завершается с

Сообщение 0, уровень 11, состояние 0, строка 0 Произошла серьезная ошибка в текущей команде. Результаты, если таковые имеются, должны быть отброшены.

и дает дамп стека, если я делаю

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

но когда я сделаю

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

он возвращает строки.

Отлично работает на той же сборке SQL Server, работающей на Windows 8.1.

Есть идеи?

- Редактировать Это так просто, как может быть

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);
 }
Spörri
источник
Это происходит для всех моделей или только для этого? Это может быть неэффективный шаблон (например, чрезмерный возврат или ненужные захваты). Вы должны посмотреть на установку свойства MatchTimeout (новое в .NET Framework 4.5). Вы сами кодировали функцию RegEx? Если да, то используете ли вы статические или экземплярные методы RegEx? Является ли SqlFunctionметод помечен как IsDeterministic=true? Помечена ли сборка как SAFE?
Соломон Руцкий
2
Насколько велики эти таблицы? Кроме того, не могли бы вы проверить, есть ли в предполагаемом плане для постановки задачи параллельный оператор? Если да, не могли бы вы проверить, если проблема возникает без параллелизма, то есть с подсказкой MAXDOP = 1.
Амит Банерджи
2
Код выглядит нормально, за исключением [SqlFunction]атрибута duplicate . Это точный код? Я не думаю, что это скомпилируется. Различие в версии 2.0 / 3.0 / 3.5 платформы не является проблемой, поскольку вы используете 4.0 / 4.5 / 4.5.x / etc или что-то еще на этом сервере, так как вы используете SQL Server 2014, который привязан к CLR версии 4. Является ли сервер показывает проблему 32-бит? Сколько памяти у него по сравнению с другими серверами? И вы проверили журналы SQL Server сразу после получения этой ошибки?
Соломон Руцки
2
Точная версия .NET не связана с этой проблемой, хотя было бы неплохо узнать, все ли серверы работают как минимум на 4.5, поскольку это может означать, что вы можете использовать новое MatchTimeoutсвойство. Но я не думаю, что это действительно проблема, если вы проходите не более 5 символов. Это является возможным , что это одна машина имеет поврежденную установку в .NET Framework, и которые могут быть восстановлены , как только форель промысловая деятельность перестала ;-). Кроме того, [0-9].*это просто, но также неэффективно, поскольку оно соответствует всем символам, если они есть, после первой цифры; лучше использовать только [0-9]для IsMatch.
Соломон Руцки
1
Почему ты изменился DataAccessKindна Read? Это только замедляет его, и вы не делаете никакого доступа к данным. Кроме того, я понимаю, что он, кажется, работает сейчас, но я буду осторожен с использованием ToString()метода, а не со Valueсвойством, так как не думаю, что ToString правильно обрабатывает кодировки или что-то в этом роде. Как настроено сопоставление баз данных? Конечно, я просто перечитал один из ваших комментариев выше и увидел, что столбец VARCHAR вместо NVARCHAR. Это поле имеет другое сопоставление, чем база данных?
Соломон Руцкий,

Ответы:

4

Проблема заключается в конфликте локалей между ОС Windows и 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) и опций сравнения (например, SqlCompareOptions), которые детализируют чувствительность к регистру, акцентам, кане, ширине или всему (двоичному и двоичному2).
  • Строковые операции в .NET, если явно не указан языковой стандарт, используют информацию о текущем потоке, заданном в Windows (т. Е. В операционной системе / ОС).

Конфликт обычно возникает при обращении к параметру SqlString без использования .Valueили так .ToString(), что он выполняет неявное преобразование в SqlString. В этом случае это вызвало бы исключение, говорящее, что идентификаторы LCID не совпадают.

Очевидно, есть другие сценарии, такие как выполнение (некоторых / всех?) Сравнений строк, в том числе при использовании Regex, как показывает этот случай (хотя до сих пор я не смог воспроизвести это).

Некоторые идеи для исправлений:

Идеально (ожидания всегда будут оправдываться относительно того, как работают сравнения):

  • Измените LCID Windows или SQL Server (язык по умолчанию), чтобы оба соответствовали

Меньше, чем идеал (поведение языка Windows может не совпадать с правилами равенства и сортировки, и поэтому могут быть неожиданные результаты):

  • Используйте .ToStringметод или .Valueсвойство, которые оба возвращают строку без LCID SQL Server, поэтому все операции будут использовать ОС LCID.

Может помочь:

  • Может быть, использовать SqlCharsвместо, SqlStringпоскольку он не приносит LCID и информацию о сопоставлении с SQL Server
  • Укажите, что культура не имеет значения через StringComparison.InvariantCulture:
    • String.Compare(string, string, StringComparison.InvariantCulture) или String.Compare(string, string, StringComparison.InvariantCultureIgnoreCase)
    • Для Regex укажите RegexOptions.CultureInvariant
Соломон Руцкий
источник
1

Обновленный ..

Локализация отличается между SQL Engine и оконным сервером, как указывает @srutzky:

os_language_version SqlServerLCID
1033 1039

Следующее изменение в коде - установка опции RegexOptions.CultureInvariantобходит ошибку. Неизмененный код не приведет к сбою SQL Server 2012 в Windows Server 2012R2 с теми же настройками языка, но и в 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
источник
Можете ли вы выполнить следующие действия на сервере, на котором был сбой: SELECT os_language_version, SERVERPROPERTY('LCID') AS 'SqlServerLCID' FROM sys.dm_os_windows_info;. Вполне возможно, что проблема была в конфликте языковых настроек. Ваше решение все еще может быть лучшим путем, но обычно не нужно использовать ToString()вместо Valueсвойства на SqlStrings. Так что было бы просто неплохо подтвердить ситуацию.
Соломон Руцкий,
Я опубликовал ответ, чтобы прояснить ситуацию, но проблему не нужно решать настройкой, RegexOptions.CultureInvariantтак как вы не передаете Optionsпеременную в Regex.IsMatch(sqldata, regex). То, что изменилось между вашим исходным кодом и новым рабочим кодом, это то, что вы перешли от использования SqlString.Valueк SqlString.ToString(). Я подозреваю, что вы бы увидели такое же фиксированное поведение, если бы вы переключились на использование SqlChars. Но я бы просто сделал это в качестве теста. Наилучшим подходом является изменение LCID Windows или SQL Server для соответствия другому. Вы также можете удалить статическую переменную Options.
Соломон Руцки
Всем привет. Спасибо, что приняли мой ответ :). Просто упомяну, что я провел дальнейшие исследования и, если понял, что я вижу, тогда, хотя я прав в том, что основной причиной является различный LCID между ОС и SQL Server, он не связан или не должен быть связан со .Valueсвойством a SqlStringas, по-видимому, возвращает то же внутреннее значение, что и .ToString()метод. Я все еще расследую и обновлю свой ответ всем, что найду :).
Соломон Руцки
Я скорректировал свой ответ в свете новой информации. Я не могу воспроизвести этот сценарий. Код в Вопросе действительно то, что вы использовали / используете? Единственная реальная разница между ними заключается в том, что тот, который использует ошибки, RegexOptions.IgnoreCaseа другой нет. Я создал подобную среду: Windows (8.0) с помощью LCID 1033, SQL Server DB имеет LCID из 1039, используя один и тот же RegEx , что вы в курсе, делать COUNT(*)на VARCHARполе , заполненном GUIDs, используя образец '[0-3â].*', на столе с 10 миллионами строк. Это SQL Server 2012, а не 2014, хотя я не думаю, что это должно иметь значение.
Соломон Руцкий
1
Спасибо за ответы на все вопросы. Код в вопросе - то, что я использовал. У меня было действительно сложное регулярное выражение, но мне удалось это сделать с помощью очень простого. Изменение настроек RegexOptions.CultureInvariant остановило поведение
Spörri