Falhando no CLR no SQL Server 2014 (windows 2012R2)

12

Eu tenho esse pequeno CLR que faz uma função RegEX em uma seqüência de caracteres em colunas.

Ao executar no SQL Server 2014 (12.0.2000) no Windows Server 2012R2, o processo falha com

Msg 0, Nível 11, Estado 0, Linha 0 Ocorreu um erro grave no comando atual. Os resultados, se existirem, deveriam ser descartados.

e dá um despejo de pilha se eu fizer

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

mas quando eu faço

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

retorna as linhas.

Funciona perfeitamente na mesma versão do SQL Server em execução no Windows 8.1.

Alguma ideia?

- Edit É o mais simples possível

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

Então, com pequenas mudanças, isso funciona agora: A principal lição em C # parece ser a mesma que em TSQL, cuidado com a conversão implícita de dados.

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
fonte
Isso acontece para todos os padrões ou apenas para este? Pode ser um padrão ineficiente (ou seja, retorno excessivo ou capturas desnecessárias). Você deve definir a propriedade MatchTimeout (nova no .NET Framework 4.5). Você mesmo codificou a função RegEx? Se sim, você está usando métodos RegEx estáticos ou de instância? O SqlFunctionmétodo está marcado como IsDeterministic=true? A montagem está marcada como SAFE?
Solomon Rutzky,
2
Quão grandes são essas tabelas? Além disso, você pode verificar se o plano estimado para as declarações do problema possui um operador paralelo? Se sim, você pode verificar se o problema ocorre sem paralelismo, ou seja, com uma dica MAXDOP = 1.
Amit Banerjee
2
O código parece bom, exceto pelo [SqlFunction]atributo duplicado . Esse é o código exato? Eu não acho que isso seria compilado. A distinção do Framework versão 2.0 / 3.0 / 3.5 não é um problema, pois você está usando 4.0 / 4.5 / 4.5.x / etc ou o que estiver nesse servidor, pois você está no SQL Server 2014, que está vinculado à versão 4. do CLR. servidor mostrando o problema de 32 bits? Quanta memória ela tem em comparação com os outros servidores? E você verificou os logs do SQL Server logo após receber esse erro?
Solomon Rutzky
2
A versão exata do .NET não está relacionada ao problema, embora seja bom saber se todos os servidores estão com pelo menos 4,5, pois isso significa que você pode usar a nova MatchTimeoutpropriedade. Mas também não acho que esse seja o problema se você está passando apenas 5 caracteres no máximo. Ele é possível que esta máquina tem um corrompido instalação do .NET Framework, e que pode ser reparado, uma vez truta actividades de pesca deixaram ;-). Além disso, [0-9].*é simples, mas também ineficiente, pois corresponde a todos os caracteres, se houver, após o primeiro dígito; usando apenas [0-9]para um IsMatché melhor.
Solomon Rutzky,
1
Por que você mudou DataAccessKindpara Read? Isso diminui a velocidade e você não está acessando dados. Além disso, percebo que parece estar funcionando agora, mas eu seria cauteloso ao usar o ToString()método em oposição à Valuepropriedade, pois não acho que o ToString lida com codificações corretamente ou algo assim. Como está definido o agrupamento de bancos de dados? Obviamente, acabei de reler um dos seus comentários acima e vejo que a coluna é VARCHAR em vez de NVARCHAR. Esse campo tem um agrupamento diferente do banco de dados?
Solomon Rutzky

Respostas:

4

O problema é um conflito de localidade entre o sistema operacional Windows e o SQL Server (especificamente o banco de dados em que o assembly está carregado). Você pode executar a seguinte consulta para ver o que eles estão definidos para:

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

Se eles são diferentes, você pode definitivamente ter algum comportamento "estranho", como o que está vendo. A questão é que:

  • SqlStringinclui mais do que apenas o próprio texto: inclui o agrupamento padrão do banco de dados no qual o assembly existe. O agrupamento é composto por duas informações: as informações da localidade (por exemplo, LCID) e as opções de comparação (por exemplo, SqlCompareOptions) que detalham a sensibilidade a maiúsculas, minúsculas, kana, largura ou tudo o mais (binário e binário2).
  • As operações de sequência no .NET, a menos que seja explicitamente fornecido um código de idioma, use as informações de código de idioma do encadeamento atual, definido no Windows (ou seja, o Sistema operacional / SO).

O conflito geralmente ocorre ao fazer referência a um parâmetro SqlString sem usar .Value ou para .ToString()que ele faça uma conversão implícita SqlString. Nesse caso, causaria uma exceção dizendo que os LCIDs não correspondem.

Aparentemente, existem outros cenários, como a comparação (algumas / todas?) De cadeias, inclusive ao usar o Regex, como mostra este caso (embora até agora eu não tenha sido capaz de reproduzir isso).

Algumas idéias para correções:

Ideal (sempre serão atendidas as expectativas em relação ao funcionamento das comparações):

  • Altere o LCID do Windows ou do SQL Server (idioma padrão) para que ambos correspondam

Inferior ao ideal (o comportamento da localidade do Windows pode não ser as mesmas regras para igualdade e classificação e, portanto, poderia haver resultados inesperados):

  • Use o .ToStringmétodo ou a .Valuepropriedade, que retornará a sequência sem o LCID do SQL Server, para que todas as operações utilizem o LCID do sistema operacional.

Pode ajudar:

  • Talvez use em SqlCharsvez deSqlString não trazer as informações de LCID e agrupamento do SQL Server
  • Especifique que a cultura não importa via StringComparison.InvariantCulture :
    • String.Compare(string, string, StringComparison.InvariantCulture) ou String.Compare(string, string, StringComparison.InvariantCultureIgnoreCase)
    • Para Regex, especifique RegexOptions.CultureInvariant
Solomon Rutzky
fonte
1

Atualizada..

A localização é diferente entre o Mecanismo SQL e a janela Servidor, como @srutzky aponta:

os_language_version SqlServerLCID
1033 1039

A seguinte alteração no código - definir a opção RegexOptions.CultureInvariantcontorna o erro. O código inalterado não trava o SQL Server 2012 no Windows Server 2012R2 com as mesmas configurações de idioma, mas o faz no 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
fonte
Você pode por favor execute o seguinte no servidor que foi deixando de funcionar: SELECT os_language_version, SERVERPROPERTY('LCID') AS 'SqlServerLCID' FROM sys.dm_os_windows_info;. É bem possível que o problema tenha sido um conflito nas configurações de idioma. Sua solução ainda pode ser o melhor caminho a percorrer, mas geralmente não deve ser necessário usar em ToString()vez da Valuepropriedade SqlStrings. Seria bom confirmar a situação.
Solomon Rutzky
Postei uma resposta para esclarecer, mas o problema não deve ser resolvido pela configuração, RegexOptions.CultureInvariantpois você não passa a Optionsvariável Regex.IsMatch(sqldata, regex). O que mudou entre o código original e o novo código funcional é que você passou de SqlString.Valuepara SqlString.ToString(). Eu suspeito que você veria o mesmo comportamento fixo se você passasse a usar SqlChars. Mas eu faria isso como um teste. A melhor abordagem é alterar o LCID do Windows ou do SQL Server para corresponder ao outro. Você também pode remover a variável estática Options.
Solomon Rutzky
Olá. Obrigado por aceitar minha resposta :). Apenas para mencionar, fiz uma pesquisa mais aprofundada e, se entendi o que estava vendo, enquanto estou certo sobre a causa raiz ser um LCID diferente entre o SO e o SQL Server, ele não está, ou não deveria estar, relacionado à .Valuepropriedade de a SqlStringque aparentemente retorna o mesmo valor interno que o .ToString()método. Ainda estou investigando e atualizarei minha resposta com o que encontrar :).
Solomon Rutzky
Eu ajustei minha resposta à luz de novas informações. Não consigo reproduzir esse cenário. O código da pergunta é realmente o que você estava / está usando? A única diferença real entre eles é que aquele que os erros usam RegexOptions.IgnoreCaseenquanto o outro não. Eu configurei um ambiente semelhante: Windows (8.0) usando LCID de 1033, o SQL Server DB tem LCID de 1039, usando o mesmo RegEx que você postou, fazendo um COUNT(*)em um VARCHARcampo cheio de GUIDs, usando um padrão de '[0-3â].*', em uma tabela com 10 milhões de linhas. É o SQL Server 2012, não 2014, embora eu não ache que isso deva importar.
Solomon Rutzky
1
Obrigado por todas as respostas. O código na pergunta é o que eu estava usando. Eu tinha um regex muito complicado, mas consegui travar isso usando um muito simples. Alterar as configurações RegexOptions.CultureInvariant parou o comportamento
Spörri