我有一个CLR DLL(称为clr.dll
),我们之前已加载并使用它。我正在使用它加载大量XML文件(对此我们没有XSD,因此我们不能在SSIS中使用复杂的XML处理器,因为这需要XSD)。我正在将其加载到也称为CLR的数据库中。有关信息,CLR函数通过每个节点具有一行来将XML文件作为表返回。 CLR还具有一些测试功能
以前,这种方法有效,现在会产生错误。错误似乎是间歇性发生的,即使在服务器上执行的唯一操作是对CLR的调用,也会发生错误。我有一些测试函数,这些函数实际上不占用任何资源,并且也不起作用。也就是说,这不是资源问题。
CLR装有PERMISSION_SET = EXTERNAL_ACCESS
。数据库设置为TRUSTWORTHY ON
。看来,在SQL Server 2008 R2中,我们无法使用哈希码加载CLR,并且我的dll仍然超出了HASHBYTES
支持的8000个字符(由其他文章建议)。
在CLR中,这些函数属于名为UserDefinedFunctions
的类。
这是我用来设置CLR的脚本的简化版本。数据库具有正确的权限。总之,它:(1)删除CLR数据库中的所有功能,(2)删除并重新连接CLR,(3)创建链接到程序集的功能并适当地设置它们的权限。
USE CLR
GO
-- drop all functions
DECLARE @FunctionName NVARCHAR(400)
DECLARE @SQL NVARCHAR(MAX)
WHILE EXISTS (SELECT *
FROM sysobjects
WHERE xtype IN ('FS', 'FT'))
BEGIN
SET @FunctionName = (SELECT TOP 1 name
FROM sysobjects
WHERE xtype IN ('FS', 'FT'))
SET @SQL = 'DROP FUNCTION dbo.' + @FunctionName
EXEC sp_executesql @SQL
END
GO
-- belt and braces, this should be the case anyway
ALTER DATABASE CLR SET TRUSTWORTHY ON;
GO
-- drops and load the assembly, first time
IF EXISTS (SELECT *
FROM sys.assemblies
WHERE name = 'CLR')
DROP ASSEMBLY CLR
GO
CREATE ASSEMBLY CLR from 'e:\clr\clr.dll' WITH PERMISSION_SET = EXTERNAL_ACCESS;
GO
-- ======== Scalar-functions ================================================================
CREATE FUNCTION dbo.DateTimeToString(@dt DATETIME, @fmt NVARCHAR(MAX)) RETURNS NVARCHAR(MAX)
AS EXTERNAL NAME CLR.UserDefinedFunctions.DateTimeToString;
GO
CREATE FUNCTION dbo.FileExists(@Filename NVARCHAR(MAX)) RETURNS BIT
AS EXTERNAL NAME CLR.UserDefinedFunctions.FileExists;
GO
CREATE FUNCTION dbo.FileGetCreated(@Filename NVARCHAR(MAX)) RETURNS DATETIME
AS EXTERNAL NAME CLR.UserDefinedFunctions.FileGetCreated;
GO
CREATE FUNCTION dbo.FileGetModified(@Filename NVARCHAR(MAX)) RETURNS DATETIME
AS EXTERNAL NAME CLR.UserDefinedFunctions.FileGetModified;
GO
CREATE FUNCTION dbo.FileGetSize(@Filename NVARCHAR(MAX)) RETURNS BIGINT
AS EXTERNAL NAME CLR.UserDefinedFunctions.FileGetSize;
GO
CREATE FUNCTION Reflection(@Data NVARCHAR(MAX)) RETURNS NVARCHAR(MAX) -- test - returns the string passed to it
AS EXTERNAL NAME CLR.UserDefinedFunctions.Reflection;
GO
-- ======== Table-valued functions -- ReadTextFile
CREATE FUNCTION dbo.ReadTextFile(@Filename NVARCHAR(4000))
RETURNS
TABLE
(
LineIndex INT,
Data NVARCHAR(4000)
)
AS
EXTERNAL NAME CLR.UserDefinedFunctions.ReadTextFile
GO
-- ReadXmlDoc
CREATE FUNCTION dbo.ReadXmlDoc(@Filename NVARCHAR(4000))
RETURNS
TABLE
(
NodeIndex INT,
ParentIndex INT,
DepthIndex INT,
ChildCount INT,
SiblingIndex INT,
SiblingCount INT,
IsTerminalNode BIT,
Tag NVARCHAR(4000),
IndexPath NVARCHAR(4000),
SimplePath NVARCHAR(4000),
UniquePath NVARCHAR(4000),
ExtendedPath NVARCHAR(4000),
TextValue NVARCHAR(4000)
)
AS
EXTERNAL NAME CLR.UserDefinedFunctions.ReadXmlDoc
GO
-- SELECT and EXECUTE permissions are set here, but omitted for security reasons
然后,我调用名为Reflection
的最小测试函数,该函数仅返回传递给它的字符串:
SELECT CLR.dbo.Reflection('hello')
无论我调用哪个函数或在哪个数据库中执行,都会引发此错误:
Msg 10314,第16级,状态11,第1行
尝试加载程序集ID 65544时,Microsoft .NET Framework中发生错误。服务器可能资源不足,或者PERMISSION_SET = EXTERNAL_ACCESS或UNSAFE可能不信任该程序集。再次运行查询,或查看文档以查看如何解决程序集信任问题。有关此错误的更多信息:System.IO.FileLoadException:无法加载文件或程序集'clr,Version = 0.0.0.0,Culture = neutral,PublicKeyToken = null'或其依赖项之一。发生与安全性有关的错误。 (来自HRESULT的异常:0x8013150A) System.IO.FileLoadException:
在System.Reflection.Assembly._nLoad(AssemblyName文件名,字符串codeBase,证据assemblySecurity,程序集locationHint,StackCrawlMark&stackMark,布尔throwOnFileNotFound,布尔值用于自省)
在System.Reflection.Assembly.InternalLoad处(AssemblyName assemblyRef,证据assemblySecurity,StackCrawlMark和stackMark,布尔值用于自省)
在System.Reflection.Assembly.InternalLoad处(字符串assemblyString,证据assemblySecurity,StackCrawlMark&stackMark,布尔值用于自省)
在System.Reflection.Assembly.Load(String assemblyString)
正如我所说,这不是资源问题,也不是权限问题,它有时会发生。目前,我本周无法运行它。上周重新应用放置并创建脚本解决了该问题。
当我按照下面所罗门的建议执行此代码时:
SELECT name,
is_trustworthy_on,
is_db_chaining_on,
compatibility_level,
owner_sid,
collation_name
FROM sys.databases
WHERE name IN (N'CLR', N'Other_DB')
我得到:
name is_trustworthy_on is_db_chaining_on compatibility_level owner_sid collation_name
Other_DB 0 0 100 0x0105000000000005150000009530FDDAA5F3AE3A5859ABA1C5FB0000 Latin1_General_CI_AS
CLR 1 0 100 0x0105000000000005150000009530FDDAA5F3AE3A5859ABA17C2E0100 Latin1_General_CI_AS
添加了2019年10月2日。此外,查询1:
SELECT lgn.[name],
lgn.[type_desc],
lgn.[sid],
CASE lgn.[sid] WHEN SUSER_SID() THEN 1 ELSE 0 END AS [IsCurrent]
FROM sys.server_principals lgn
WHERE lgn.[sid] IN (0x0105000000000005150000009530FDDAA5F3AE3A5859ABA1C5FB0000, 0x0105000000000005150000009530FDDAA5F3AE3A5859ABA17C2E0100, SUSER_SID());
结果1:
name type_desc sid IsCurrent
GRP\clone WINDOWS_LOGIN 0x0105000000000005150000009530FDDAA5F3AE3A5859ABA1C5FB0000 0
GRP\mark WINDOWS_LOGIN 0x0105000000000005150000009530FDDAA5F3AE3A5859ABA1CD790200 1
查询2:
USE [CLR];
GO
SELECT DB_NAME() AS [DB], usr.[name], usr.[type_desc], usr.[sid], CASE usr.[sid] WHEN USER_SID() THEN 1 ELSE 0 END AS [IsCurrent]
FROM sys.database_principals usr
WHERE usr.[sid] IN (0x0105000000000005150000009530FDDAA5F3AE3A5859ABA1C5FB0000, 0x0105000000000005150000009530FDDAA5F3AE3A5859ABA17C2E0100, USER_SID())
OR usr.[name] = N'dbo';
结果2:
DB name type_desc sid IsCurrent
CLR dbo WINDOWS_USER 0x0105000000000005150000009530FDDAA5F3AE3A5859ABA17C2E0100 0
CLR GRP\mark WINDOWS_USER 0x0105000000000005150000009530FDDAA5F3AE3A5859ABA1CD790200 1
查询3:
USE [Other_DB];
GO
SELECT DB_NAME() AS [DB],
usr.[name],
usr.[type_desc],
usr.[sid],
CASE usr.[sid] WHEN USER_SID() THEN 1 ELSE 0 END AS [IsCurrent]
FROM sys.database_principals usr
WHERE usr.[sid] IN (0x0105000000000005150000009530FDDAA5F3AE3A5859ABA1C5FB0000, 0x0105000000000005150000009530FDDAA5F3AE3A5859ABA17C2E0100, USER_SID())
OR usr.[name] = N'dbo';
结果3:
DB name type_desc sid IsCurrent
Other_DB dbo WINDOWS_USER 0x0105000000000005150000009530FDDAA5F3AE3A5859ABA1C5FB0000 0
答案 0 :(得分:0)
所以我学到了以下内容:
Msg 10314, Level 16, State 11, Line 1
An error occurred in the Microsoft .NET Framework while trying to load assembly id 65544. The server may be running out of resources, or the assembly may not be trusted with PERMISSION_SET = EXTERNAL_ACCESS or UNSAFE. Run the query again, or check documentation to see how to solve the assembly trust issues. For more information about this error:
System.IO.FileLoadException: Could not load file or assembly 'clr, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. An error relating to security occurred. (Exception from HRESULT: 0x8013150A)
System.IO.FileLoadException:
at System.Reflection.Assembly._nLoad(AssemblyName fileName, String codeBase, Evidence assemblySecurity, Assembly locationHint, StackCrawlMark& stackMark, Boolean throwOnFileNotFound, Boolean forIntrospection)
at System.Reflection.Assembly.InternalLoad(AssemblyName assemblyRef, Evidence assemblySecurity, StackCrawlMark& stackMark, Boolean forIntrospection)
at System.Reflection.Assembly.InternalLoad(String assemblyString, Evidence assemblySecurity, StackCrawlMark& stackMark, Boolean forIntrospection)
at System.Reflection.Assembly.Load(String assemblyString)
答案 1 :(得分:0)
测试已确认此问题与包含SQLCLR程序集的数据库(在这种情况下为CLR
数据库)的所有者直接相关。最初,CLR
数据库是Windows(活动目录/ AD)帐户的所有者。将数据库所有者切换到sa
后,此行为停止发生。
虽然缩小了问题区域的范围(例如,程序集所在的数据库所有者),并且找到了解决方案(例如,将数据库所有者更改为sa
),但尚未导致该行为的确切情况集确定后,到目前为止,根据提供的信息,我无法重现此行为。
目前,我还可以肯定地说,包含Assembly的数据库(TRUSTWORTHY ON
DB)的CLR
设置是主要因素。我相信启用TRUSTWORTHY
后,设置为EXTERNAL_ACCESS
或UNSAFE
的程序集会进行额外的外部安全检查。 TRUSTWORTHY
仅在此处使用,因为大会尚未签署。我相信,如果TRUSTWORTHY
为OFF
(即被禁用),则数据库所有者将不是成为一个因素,并且在数据库中甚至无法实现此行为。第一名。
其他说明
在创建程序集并将SQL函数连接到CLR时,它不会加载CLR。
否,但这仅是因为在SQL Server启动时一直加载CLR并一直运行(除非您启用了“轻量级池” /“光纤模式”,并且不应该启用)。
此外,当您CREATE
或ALTER
某个程序集时,会创建一个应用程序域 ,因为需要验证该程序集。但是,程序集本身并未加载到App Domain中。
第一次执行CLR调用时,它将对照CLR数据库的SID检查正在执行查询的数据库的SID。
不。不检查“当前”数据库所有者的SID(好吧,除非正在使用跨数据库所有权链接,否则不这样做)。根据{{1}}中的owner_sid
记录检查包含程序集的数据库所有者的SID,以确保它们匹配,但启用sys.databases
时仅 。在这种情况下,如果所有者SID在TRUSTWORTHY
和sys.databases
之间不匹配(在包含Assembly的DB中),那么您将收到一条错误消息,指出这些SID必须匹配。
这两个SID可以相同也可以不同。它们可能有所不同的主要原因是,正在运行查询的数据库可能已从另一台服务器还原。
不完全是。所有者最初由创建(通过sys.database_principals
,附加或还原)数据库的登录名设置。但是,更改数据库的所有者很容易,因此我不认为一个数据库来自另一个服务器就是因为拥有另一个所有者。
如果他们不同意,则会发生此错误:
不。不需要这两个数据库的所有者相同。
关于主要问题的顶部代码块中所示的“删除所有功能”代码块:
CREATE DATABASE
/ sysobjects
/ dbo.sysobjects
,因为这是一个兼容性视图,该视图仅允许与为2005版之前的SQL Server版本编写的代码向后兼容( 9.0)。从SQL Server 2005开始,您应该使用sys.sysobjects
或sys.objects
而不是旧的sys.{anything}
视图。sys{anything}
的数据类型应该为@FunctionName
(这是sysname
的同义词),因为它几乎用于SQL Server中的所有实体名称。不要简单地寻找某种类型的对象,因为它不能非常可靠/稳定,因为它无法区分与不同程序集关联的T-SQL包装对象。您现在只有一个Assembly,但是当首选方法几乎不需要额外的努力时,它仍然是不必要的过于简单的方法。您应该从NVARCHAR(128)
系统目录视图开始。例如:
sys.assembly_modules
从该查询开始,很容易从SELECT obj.[name]
FROM sys.assembly_modules asmd
INNER JOIN sys.assemblies assm
ON assm.[assembly_id] = asmd.[assembly_id]
INNER JOIN sys.objects obj
ON obj.[object_id] = asmd.[object_id]
WHERE assm.[name] = N'{assembly_name_here}'
获取模式名称和/或使用obj.[schema_id]
语句在CASE
之间切换DROP
语句的对象类型,FUNCTION
,PROCEDURE
,具体取决于所需的内容。
我知道这只是一个部署脚本,但是仍然可以使用一个批处理/执行来删除所有功能,而不是使用缓慢的逐行循环:
TRIGGER
DECLARE @SQL NVARCHAR(MAX) = N'';
SELECT @SQL += N'DROP FUNCTION dbo.' + obj.[name] + N';'
+ NCHAR(0x0D) + NCHAR(0x0A) -- CR+LF
FROM sys.assembly_modules asmd
INNER JOIN sys.assemblies assm
ON assm.[assembly_id] = asmd.[assembly_id]
INNER JOIN sys.objects obj
ON obj.[object_id] = asmd.[object_id]
WHERE assm.[name] = N'{assembly_name_here}';
PRINT @SQL; -- DEBUG (else comment out and UNcomment line below)
--EXEC (@SQL);
。有关详细信息,请参见“ PLEASE, Please, please Stop Using Impersonation, TRUSTWORTHY, and Cross-DB Ownership Chaining”。而是用证书(即Module Signing)对程序集签名。请参阅:
SET TRUSTWORTHY ON
文字(也就是十六进制字节)而不是DLL上部署程序集文件系统,因为该外部DLL现在是脚本的依赖项,甚至可能与脚本中T-SQL包装对象的创建不同步。也很难对部署脚本进行版本控制和/或在系统之间进行传输。要将编译后的DLL轻松地转换为可部署的VARBINARY
文字,请参见我为此目的创建的开源工具:BinaryFormatter。