Menghancurkan CLR di SQL Server 2014 (windows 2012R2)

12

Saya punya CLR kecil ini yang melakukan fungsi RegEX pada string di kolom.

Saat berjalan pada SQL Server 2014 (12.0.2000) pada Windows Server 2012R2 proses macet

Msg 0, Level 11, Negara 0, Baris 0 Terjadi kesalahan parah pada perintah saat ini. Hasilnya, jika ada, harus dibuang.

dan memberikan stack stack jika saya melakukannya

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

tetapi ketika saya melakukannya

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

mengembalikan baris.

Bekerja dengan sempurna pada build SQL Server yang sama yang berjalan pada Windows 8.1.

Ada ide?

- Sunting Ini sesederhana mungkin

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

Jadi dengan sedikit perubahan ini berfungsi sekarang: Pelajaran utama dalam C # tampaknya sama seperti di TSQL Waspadalah terhadap konversi data implisit.

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
sumber
Apakah ini terjadi untuk semua pola atau hanya yang ini? Ini bisa jadi merupakan pola yang tidak efisien (mis. Backtracking berlebihan atau Capture yang tidak perlu). Anda harus melihat ke pengaturan properti MatchTimeout (baru di .NET Framework 4.5). Apakah Anda mengkode fungsi RegEx sendiri? Jika demikian, apakah Anda menggunakan metode RegEx statis atau instan? Apakah SqlFunctionmetode ini ditandai IsDeterministic=true? Apakah majelis ditandai sebagai SAFE?
Solomon Rutzky
2
Seberapa besar tabel ini? Juga, dapatkah Anda memeriksa apakah rencana perkiraan untuk laporan masalah memiliki operator paralel? Jika ya, dapatkah Anda memeriksa apakah masalah terjadi tanpa paralelisme yaitu dengan petunjuk MAXDOP = 1.
Amit Banerjee
2
Kode terlihat baik-baik saja, kecuali untuk [SqlFunction]atribut duplikat . Apakah itu kode yang tepat? Saya tidak berpikir itu akan dikompilasi. Perbedaan Framework versi 2.0 / 3.0 / 3.5 adalah non-masalah karena Anda menggunakan 4.0 / 4.5 / 4.5.x / etc atau apa pun yang ada di server itu karena Anda berada di SQL Server 2014 yang terikat ke CLR versi 4. Apakah server menunjukkan masalah 32-bit? Berapa banyak memori yang dibandingkan dengan server lain? Dan sudahkah Anda memeriksa log SQL Server setelah mendapatkan kesalahan itu?
Solomon Rutzky
2
Versi tepat dari .NET tidak terkait dengan masalah, meskipun alangkah baiknya jika semua server ada di setidaknya 4,5 karena itu berarti Anda dapat menggunakan MatchTimeoutproperti baru . Tapi saya tidak berpikir itu benar-benar masalah baik jika Anda hanya melewati 5 karakter maks. Hal ini mungkin bahwa mesin satu ini memiliki rusak menginstal .NET Framework, dan yang dapat diperbaiki sekali trout kegiatan penangkapan ikan telah berhenti ;-). Juga, [0-9].*sederhana tetapi juga tidak efisien karena cocok dengan semua karakter, jika ada, setelah digit pertama; menggunakan hanya [0-9]untuk yang IsMatchlebih baik.
Solomon Rutzky
1
Mengapa Anda mengubah DataAccessKindke Read? Itu hanya memperlambatnya dan Anda tidak melakukan akses data apa pun. Juga, saya menyadari bahwa ini tampaknya berfungsi sekarang, tetapi saya akan berhati-hati dengan menggunakan ToString()metode yang bertentangan dengan Valueproperti karena saya tidak berpikir ToString menangani pengodean dengan benar, atau sesuatu seperti itu. Di mana pengaturan pengumpulan basis data Anda? Tentu saja, saya baru saja membaca kembali salah satu komentar Anda di atas dan melihat bahwa kolomnya adalah VARCHAR, bukan NVARCHAR. Apakah bidang itu memiliki susunan yang berbeda dari basis data?
Solomon Rutzky

Jawaban:

4

Masalahnya adalah konflik lokal antara OS Windows dan SQL Server (khususnya database tempat Majelis dimuat). Anda dapat menjalankan kueri berikut untuk melihat keduanya diatur:

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

Jika mereka berbeda maka Anda pasti bisa mendapatkan perilaku "aneh", seperti apa yang Anda lihat. Masalahnya adalah:

  • SqlStringtermasuk lebih dari sekedar teks itu sendiri: itu termasuk susunan default dari database di mana majelis ada. Susunan terdiri dari dua bagian informasi: info lokal (yaitu LCID), dan opsi perbandingan (yaitu SqlCompareOptions) yang merinci sensitivitas terhadap huruf besar-kecil, aksen, kana, lebar, atau semuanya (biner dan biner2).
  • Operasi string di .NET, kecuali secara eksplisit diberikan lokal, gunakan info lokal dari utas saat ini, yang diatur dalam Windows (yaitu Sistem Operasi / OS).

Konflik biasanya terjadi ketika mereferensikan parameter SqlString tanpa menggunakan .Valueatau .ToString()sedemikian rupa sehingga ia melakukan konversi implisit SqlString. Dalam hal itu akan menyebabkan pengecualian yang mengatakan bahwa LCID tidak cocok.

Tampaknya ada skenario lain, seperti melakukan perbandingan string (sebagian / semua?), Termasuk ketika menggunakan Regex seperti yang ditunjukkan oleh kasus ini (meskipun sejauh ini saya belum dapat mereproduksi ini).

Beberapa ide untuk perbaikan:

Ideal (harapan akan selalu dipenuhi tentang bagaimana perbandingan bekerja):

  • Ubah baik Windows atau SQL Server LCID (bahasa default) sehingga keduanya cocok

Kurang dari ideal (perilaku lokal Windows mungkin bukan aturan yang sama untuk kesetaraan dan penyortiran sehingga mungkin ada hasil yang tidak terduga):

  • Gunakan .ToStringmetode atau .Valueproperti, yang keduanya mengembalikan string tanpa SQL Server LCID sehingga semua operasi akan menggunakan OS LCID.

Mungkin membantu:

  • Mungkin menggunakan SqlCharsalih-alih SqlStringkarena tidak membawa informasi LCID dan pemeriksaan dari SQL Server
  • Tentukan bahwa budaya tidak penting melalui StringComparison.InvariantCulture:
    • String.Compare(string, string, StringComparison.InvariantCulture) atau String.Compare(string, string, StringComparison.InvariantCultureIgnoreCase)
    • Untuk Regex, tentukan RegexOptions.CultureInvariant
Solomon Rutzky
sumber
1

Diperbarui ..

Lokalisasi berbeda antara SQL Engine dan jendela Server seperti yang ditunjukkan @srutzky:

os_language_version SqlServerLCID
1033 1039

Perubahan berikut pada kode - pengaturan opsi RegexOptions.CultureInvariantmengatasi kesalahan. Kode tidak berubah tidak akan crash SQL Server 2012 pada Windows Server 2012R2 dengan pengaturan bahasa yang sama tetapi melakukannya pada 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
sumber
Bisakah Anda jalankan berikut pada server yang menerjang: SELECT os_language_version, SERVERPROPERTY('LCID') AS 'SqlServerLCID' FROM sys.dm_os_windows_info;. Sangat mungkin bahwa masalahnya adalah konflik dalam pengaturan bahasa. Solusi Anda mungkin masih merupakan cara terbaik untuk pergi, tetapi secara umum tidak seharusnya ada kebutuhan untuk menggunakan ToString()alih-alih Valueproperti di SqlStrings. Jadi akan lebih baik untuk mengkonfirmasi situasinya.
Solomon Rutzky
Saya memposting jawaban untuk mengklarifikasi, tetapi masalah tidak boleh diselesaikan dengan menetapkan RegexOptions.CultureInvariantkarena Anda tidak meneruskan Optionsvariabel ke dalamnya Regex.IsMatch(sqldata, regex). Hal yang berubah antara kode asli Anda dan kode baru yang berfungsi adalah Anda beralih dari menggunakan SqlString.Valueke SqlString.ToString(). Saya menduga Anda akan melihat perilaku tetap yang sama jika Anda beralih menggunakan SqlChars. Tapi saya hanya akan melakukan itu sebagai ujian. Pendekatan terbaik adalah mengubah LCID Windows atau SQL Server agar sesuai dengan yang lain. Anda juga dapat menghapus variabel statis Opsi.
Solomon Rutzky
Hai yang disana. Terima kasih telah menerima jawaban saya :). Sebagai tambahan, saya melakukan penelitian lebih lanjut dan, jika mengerti apa yang saya lihat, maka sementara saya benar tentang penyebab root menjadi LCID yang berbeda antara OS dan SQL Server, itu tidak, atau tidak seharusnya, terkait dengan .Valueproperti dari SqlStringyang ternyata mengembalikan nilai internal yang sama dengan .ToString()metode. Saya masih menyelidiki dan akan memperbarui jawaban saya dengan apa pun yang saya temukan :).
Solomon Rutzky
Saya menyesuaikan jawaban saya berdasarkan info baru. Saya tidak dapat mereproduksi skenario ini. Apakah kode dalam Pertanyaan benar-benar apa yang Anda / gunakan? Satu-satunya perbedaan nyata di antara mereka adalah bahwa salah satu yang menggunakan kesalahan RegexOptions.IgnoreCasesementara yang lain tidak. Saya telah menyiapkan lingkungan yang serupa: Windows (8.0) menggunakan LCID 1033, SQL Server DB memiliki LCID 1039, menggunakan RegEx yang sama yang Anda posting, melakukan COUNT(*)pada VARCHARbidang yang diisi dengan GUID, menggunakan pola '[0-3â].*', di atas meja dengan 10 juta baris. Ini adalah SQL Server 2012, bukan 2014, meskipun saya pikir itu tidak masalah.
Solomon Rutzky
1
Terima kasih atas semua jawabannya. Kode dalam pertanyaan adalah apa yang saya gunakan. Saya memiliki regex yang benar-benar rumit tetapi berhasil menabrak ini menggunakan yang sangat sederhana. Mengubah pengaturan RegexOptions.CultureInvariant menghentikan perilaku
Spörri