Збій 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);
 }

Чи трапляється це для всіх моделей чи лише цього? Це може бути неефективна модель (тобто надмірне зволікання або непотрібне захоплення). Вам слід поглянути на налаштування властивості MatchTimeout (нове в .NET Framework 4.5). Ви самостійно кодували функцію RegEx? Якщо так, чи використовуєте ви статичні чи екземплярні RegEx методи? Чи SqlFunctionпозначений метод як IsDeterministic=true? Чи позначено збірку як SAFE?
Соломон Руцький

2
Наскільки великі ці таблиці? Також, чи можете ви перевірити, чи є в розрахунковому плані заяв про проблеми паралельний оператор? Якщо так, чи можете ви перевірити, чи проблема виникає без паралелізму, тобто з підказкою MAXDOP = 1.
Аміт Банерджі

2
Код виглядає добре, за винятком дублюючого [SqlFunction]атрибута. Це точний код? Я не думаю, що це складеться. Відмінність версії Framework 2.0 / 3.0 / 3.5 не є проблемою, оскільки ви використовуєте 4.0 / 4.5 / 4.5.x / тощо або що інше на цьому сервері, оскільки ви перебуваєте на 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, як це показує цей випадок (хоча поки що я не зміг це відтворити).

Деякі ідеї для виправлень:

Ідеально (очікування завжди будуть виправдані щодо того, як працюють порівняння):

  • Змініть або Windows, або SQL Server LCID (мова за замовчуванням), щоб обидва збігалися

Менш ідеального (поведінка локалі 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 і вікном Server, як вказує @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);
 }

Чи можете ви виконати наступні дії на сервері, на якому був збій: 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, щоб відповідати іншому. Ви також можете видалити Параметри статичної змінної.
Соломон Руцький

Привіт там. Дякуємо, що прийняли мою відповідь :). Зазначимо, я робив подальші дослідження, і якщо я зрозумів, що я бачу, то, хоча я маю рацію щодо першопричини, що відрізняється LCID між ОС і SQL Server, це не має або не повинно бути пов'язано з .Valueвластивістю в а , SqlStringяк , по- видимому повертає той же внутрішнє значення в якості .ToString()методу. Я все ще розслідую і оновлю свою відповідь тим, що знайду :).
Соломон Руцький

Я коригував свою відповідь у світлі нової інформації. Я не можу відтворити цей сценарій. Чи справді код у Питання те, що ви використовували / використовуєте? Єдина реальна відмінність між ними полягає в тому, що той, який помилки використовує, RegexOptions.IgnoreCaseа інший - ні. Я створив подібне середовище: Windows (8.0) за допомогою LCID 1033, DB SQL Server має LCID 1039, використовуючи той самий RegEx, який ви розмістили, роблячи COUNT(*)на VARCHARполі, заповненому GUID, використовуючи шаблон '[0-3â].*', на столі з 10 мільйонами рядків. Це SQL Server 2012, а не 2014 рік, хоча я не думаю, що це має мати значення.
Соломон Руцький

1
Дякую за всі відповіді. Код у питанні - що я використовував. У мене був дуже складний регулярний вираз, але мені вдалося розбити це за допомогою дуже простого. Зміна параметрів RegexOptions.CultureInvariant припинила поведінку
Spörri
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.