SQL Server 2016 CLR 存储过程错误:"A system assertion check has failed"



我正在尝试通过C#assembly运行本机代码API(在C .dll中),以便我可以在SQL Server中的CLR存储过程中使用某些API函数。我试图从C DLL使用的功能从数据历史学家访问原始数据,并以非托管类型返回数据。然后将其保留到C#汇编到元帅,并将管道结果输送到SQL Server。

我没有C DLL的源代码,所以我真的不知道在引擎盖下方发生了什么(这是第三方)。但是,我可以在没有问题的情况下访问C#控制台应用程序中的这些API函数(我依靠https://lennilobel.wordpress.com/2014/01/29/calling-calling-calling-calling-calling-calling-calling-calling-calling-calling-calling-calling-clrom-sql-clr-clr-clr-clr-clr-clr-clr-clr-clr-clr-code/-将C DLL包装在.NET中)。我开发了一个工作的C#控制台应用程序,然后将其变成一个类库,将类包装在" [Microsoft.sqlserver.server.sqlprocedure]中",并将汇编添加到不安全模式的所需SQL数据库中。我还确保在SQL Server中启用了CLR,并且在我使用的数据库中关闭了TrustWorthy。

但是,当我尝试调用使用C#汇编的存储过程时,我会收到以下问题。

Location:    AppDomain.cpp:2705
Expression:  hr != E_POINTER
SPID:        66
Process ID:  3584
Msg 3624, Level 20, State 1, Procedure sp_direct_proficy_api, Line 0 [Batch Start Line 2]
A system assertion check has failed. Check the SQL Server error log for details. Typically, an assertion failure is caused by a software bug or data corruption. To check for database corruption, consider running DBCC CHECKDB. If you agreed to send dumps to Microsoft during setup, a mini dump will be sent to Microsoft. An update might be available from Microsoft in the latest Service Pack or in a Hotfix from Technical Support.
Msg 596, Level 21, State 1, Line 2
Cannot continue the execution because the session is in the kill state.
Msg 0, Level 20, State 0, Line 2
A severe error occurred on the current command.  The results, if any, should be discarded.

我已经对系统断言检查进行了一些Google搜索,并发现它们通常是由于数据库损坏而发生的。我已经运行了DBCC CheckDB,一切看起来都不错,所以这不是问题。我已经复制了伦纳德的示例(从上面的链接中),这与我使用更简单的C DLL所进行的过程基本相同。该示例没有发生任何错误,因此我认为SQL Server和C API之间存在一些APPDOAN的竞争。

我的问题

这是我试图做的事情的预期问题吗?我对SQL Server在使用CLR存储过程时如何访问计算机内存的访问并声称AppDomain并不了解很多,但是似乎SQL Server和C API之间存在一些有害的资源竞争。

如下所示是C#组件的两个部分(从C#线束到C DLL的调用,以及将通过存储过程访问的类)。

C#DLL从C 导入

public class IHUAPI
{
const string DLLNAME = "IHUAPI.dll";
static class IHU64
{
    [DllImport(DLLNAME, CallingConvention = CallingConvention.StdCall, EntryPoint = "ihuConnect@16")]
    public static extern ihuErrorCode ihuConnect(string server, string username, string password, out int serverhandle);
    [DllImport(DLLNAME, CallingConvention = CallingConvention.StdCall, EntryPoint = "ihuReadRawDataByTime")]
    public static extern ihuErrorCode ihuReadRawDataByTime(int serverhandle, string tagname, ref IHU_TIMESTAMP start, ref IHU_TIMESTAMP end, out int numberOfSamples, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 4, ArraySubType = UnmanagedType.LPStruct)] out IHU_DATA_SAMPLE[] samples);
}
public static ihuErrorCode ihuConnect(string server, string username, string password, out int serverhandle)
{
        return IHU64.ihuConnect(server, username, password, out serverhandle);
}
public static ihuErrorCode ihuReadRawDataByTime(int serverhandle, string tagname, IHU_TIMESTAMP start, IHU_TIMESTAMP end, out IHU_DATA_SAMPLE[] samples)
{
        int numberOfSamples;
        return IHU64.ihuReadRawDataByTime(serverhandle, tagname, ref start, ref end, out numberOfSamples, out samples);
}
}

C#存储过程中使用的汇编访问C API

[Microsoft.SqlServer.Server.SqlProcedure]
public static void API_Query(string tagname, DateTime start_date, DateTime end_date)
{
    int handle;
    ihuErrorCode result;
    result = IHUAPI.ihuConnect("houmseosprf007", "", "", out handle);
    IHU_DATA_SAMPLE[] values;
    IHU_TIMESTAMP start = new IHU_TIMESTAMP(start_date);
    IHU_TIMESTAMP end = new IHU_TIMESTAMP(end_date);
    ihuErrorCode result_api = IHUAPI.ihuReadRawDataByTime(handle, tagname, start, end, out values);
    SqlMetaData[] md = new SqlMetaData[3];
    md[0] = new SqlMetaData("tagname", SqlDbType.Text);
    md[1] = new SqlMetaData("return_value", SqlDbType.NVarChar, 50);
    md[2] = new SqlMetaData("timestamp", SqlDbType.DateTime);
    SqlDataRecord row = new SqlDataRecord(md);
    SqlContext.Pipe.SendResultsStart(row);
    DateTime p;
    string p2;
    for (int i = 1; i < (values == null ? 0 : values.Length); i++)
    {
        using (IHU_DATA_SAMPLE sample = values[i])
        {
            if (sample.ValueDataType != ihuDataType.Array)
            {
                p = sample.TimeStamp.ToDateTime();
                p2 = sample.ValueObject.ToString();
                row.SetValue(0, tagname);
                row.SetValue(1, p2);
                row.SetValue(2, p);
            }
            else
            {
                p = sample.TimeStamp.ToDateTime();
                ihuArrayValue aValue = (ihuArrayValue)Marshal.PtrToStructure(sample.Value.ArrayPtr, typeof(ihuArrayValue));
                p2 = aValue.GetArrayValue.ToString();
                row.SetValue(0, tagname);
                row.SetValue(1, p2);
                row.SetValue(2, p);

            }
        }
        SqlContext.Pipe.SendResultsRow(row);
    }
    SqlContext.Pipe.SendResultsEnd();
}

这是我尝试做的事情的预期问题吗?

我不会说"期望"那么多,因为"不是 un 期望"或"不应该对"。该第三方库显然在隔离时做的事情很好,但是当它从SQL Server的CLR主机中启动时,它是不可接受的。SQL Server的CLR主机受到高度限制是有充分理由的。

因此,您应该做的是主机托管这个第三方C 库和您的原始(以及工作)C#包装器作为在托管您要连接到的服务" IHU"服务的服务器上运行的Web服务。然后,对于您的SQLCLR代码,请使用HttpWebRequestHttpWebResponse调用该Web服务。在SendResultsRow()环中解析响应XML/JSON。

请确保将更新的SQLCLR代码的许可证设置为EXTERNAL_ACCESS,因为您不需要UNSAFE :-),并且您在查询批次中仍然会得到相当瞬时的响应,而无需打电话给命令 - 通过xp_cmdshell或调用SSI,甚至安排工作来完成。

似乎SQL Server和C API之间似乎存在一些有害的资源竞争。

是。您不应该真正使用SQL CLR的非管理DLL。CLR托管代码是内存安全的,SQLCLR旨在保护SQL Server免受由自定义托管代码引起的任何问题。但是,如果您使用不受管理的代码,则没有任何安全保证,并且可能(可能)您可能会崩溃SQL Server这样做。

取而代之的是,从短寿命的客户端进程(与SQL Server进程分开)和短期寿命,以使第三方DLL的任何内存问题都可以在客户端流程终止时清除。SSIS是托管和运行类似内容的简单方法。

相关内容

最新更新