SQL Server 2016 CLR Stored Procedure Error : "A system assertion check has failed"

SQL Server 2016 CLR Stored Procedure Error : "A system assertion check has failed"

我正在尝试通过 C# 程序集 运行 本机代码 API(在 C++ .dll 中),以便我可以在 CLR 存储过程中使用某些 API 函数在 SQL 服务器中。我试图从 C++ dll 使用的函数访问来自数据历史记录的原始数据和 returns 非托管类型的数据。然后由 C# 程序集将结果编组并通过管道传输到 SQL 服务器。

我没有 C++ dll 的源代码,所以我真的不知道幕后到底发生了什么(它是第三方的)。但是,我可以毫无问题地在 C# 控制台应用程序中访问这些 API 函数(我依赖 https://lennilobel.wordpress.com/2014/01/29/calling-c-from-sql-clr-c-code/ 在 .NET 中包装 C++ dll)。我开发了一个工作的 C# 控制台应用程序,然后将它变成一个 Class 库,将 class 包装在“[Microsoft.SqlServer.Server.SqlProcedure]” 中并将程序集添加到所需的 SQL 数据库在不安全模式下。我还确保在 SQL 服务器中启用了 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,一切看起来都很好,所以这不是问题所在。我已经复制了 Leonard 的示例(来自上面的 link),这与我使用更简单的 C++ dll 进行的过程基本相同。该示例没有发生任何错误,因此我相信 SQL 服务器和 C++ API 之间存在对应用程序域的竞争。

我的问题

这是我正在尝试做的事情的预期问题吗?我不太了解 SQL 服务器在使用 CLR 存储过程时如何访问计算机内存和声明应用程序域,但似乎 SQL 服务器和 C++ API.

下面显示的是 C# 程序集的两个部分(从 C# harness 调用 C++ dll 和 class 要由存储过程访问)。

从 C++ 导入 C# DLL

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

it seems as if there is some harmful resource competition between SQL Server and the C++ API.

是的。您不应该真正使用来自 SQL CLR 的非托管 DLL。 CLR 托管代码是内存安全的,SQLCLR 旨在保护 SQL 服务器免受自定义托管代码引起的任何问题的影响。但是,如果您使用非托管代码,则没有任何安全保证,并且您可能(很可能)会崩溃 SQL 服务器执行此操作。

而是从短期客户端进程加载非托管 DLL,与 SQL 服务器进程分开,并且是短期的,以便在客户端进程终止。 SSIS 是托管和 运行 类似内容的简单方法。

Is this an expected issue for what I am attempting to do?

我不会说 "expected" 而是 "not unexpected" 或 "shouldn't be surprised by"。那个第 3 方库显然在隔离时做的事情很好,但在从 SQL 服务器的 CLR 主机启动时是不可接受的。 SQL 服务器的 CLR 主机受到如此严格的限制是有充分理由的。

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

务必将更新后的 SQLCLR 代码的 PERMISSION_SET 设置为 EXTERNAL_ACCESS,因为您不需要 UNSAFE :-),并且您仍然可以在您的查询批处理中获得相当即时的响应,而无需 shell 通过 xp_cmdshell 调用命令行或调用 SSIS 甚至安排作业来完成它。