I recently installed SQL Server 2017 Express and localdb (general availablity). While attempting to install the tSQLt framework I've discovered a new security feature implemented in SQL Server 2017: the "clr strict security" option. This new security feature seems to prevent the creation of the tSQLtCLR assembly.
The SQL error message states:
CREATE or ALTER ASSEMBLY for assembly 'tSQLtCLR' with the SAFE or
EXTERNAL_ACCESS option failed because the 'clr strict security' option
of sp_configure is set to 1. Microsoft recommends that you sign the
assembly with a certificate or asymmetric key that has a corresponding
login with UNSAFE ASSEMBLY permission. Alternatively, you can trust
the assembly using sp_add_trusted_assembly.
I've read Microsoft's technical documentation related to the sp_add_trusted_assembly procedure, but it seems to assume that you were able to successfully create the assembly. How would one code the tSQLtCLR assembly to be listed as "trusted" if you can't get it created in the first place?
SQL Server 2017 introduces a new server-level configuration option named "CLR strict security", and it is enabled by default. This option requires that ALL Assemblies, even SAFE ones, be signed with a certificate or strong name key, and that the Certificate or Asymmetric Key used to do that signing is loaded into [master], and has a Login created from it, and that Login has been granted the UNSAFE ASSEMBLY permission.
Due to SAFE Assemblies now needing to have the signature-based Login in place before being loaded via CREATE ASSEMBLY, it is no longer possible to have an empty, signed Assembly that gets loaded into [master] via CREATE ASSEMBLY ... FROM 0x... WITH PERMISSION_SET = SAFE;.
Now, there are only two ways to create objects usable to set up SQLCLR security from a VARBINARY literal or variable (i.e. not from an external file):
CREATE ASSEMBLY ... FROM 0x...;
CREATE CERTIFICATE ... FROM BINARY = 0x...;
Option #1 is no longer an option, at least not by itself. Option 2 is fine, but was never preferred due Certificates not being fully integrated into the Visual Studio / MSBuild build process.
Fortunately, there are two ways to fix this as discussed in the following two blog posts of mine:
SQLCLR vs. SQL Server 2017, Part 2: “CLR strict security” – Solution 1 — more steps than Part 3, Solution 2 (below), but a good fit for existing projects as it requires almost no changes to the existing solution or even deployment process (and in fact, this is effectively the route that I went for my SQL# project as all it did was add 3 simple steps to the beginning of the installation script)
SQLCLR vs. SQL Server 2017, Part 3: “CLR strict security” – Solution 2
HOWEVER,
that just answers the question of "why" you are in the situation that you are currently in. To fix that situation, assuming that you likely aren't going to update the tSQLt build process to include a Certificate, then you can do a simple one-time fix of:
ALTER DATABASE [master] SET TRUSTWORTHY ON;
EXEC tSQLt.InstallExternalAccessKey;
EXEC master.sys.sp_executesql N'GRANT UNSAFE ASSEMBLY TO [tSQLtExternalAccessKey];';
ALTER DATABASE [master] SET TRUSTWORTHY OFF;
The GRANT UNSAFE ASSEMBLY is there due to the tSQLt.InstallExternalAccessKey Stored Procedure only granting EXTERNAL ACCESS ASSEMBLY to the Login, which used to be fine, but now is not enough.
Of course, you won't be able to load the tSQLt Assemblies until those 4 steps are done, so if the process is to load everything first and that is failing, then you will need to do:
EXEC sp_configure 'clr strict security', 0; RECONFIGURE;
-- Install tSQLt ...
EXEC tSQLt.InstallExternalAccessKey;
EXEC master.sys.sp_executesql N'GRANT UNSAFE ASSEMBLY TO [tSQLtExternalAccessKey];';
EXEC sp_configure 'clr strict security', 1; RECONFIGURE;
I created an issue in the tSQLt GitHub repository with the steps require to incorporate the ideal fix into the source files: https://github.com/tSQLt-org/tSQLt/issues/25
PLEASE NOTE
that none of these possible solutions includes using the new "Trusted Assemblies" feature. That feature should never, ever be used by anyone for any reason (outside of sheer curiosity and testing). The reasons for avoiding it are detailed in several blog posts (currently 3 and more on the way) starting with:
SQLCLR vs. SQL Server 2017, Part 4: “Trusted Assemblies” – The Disappointment
The tSQLt assembly is signed already. For now, you can create the assembly in master, create a certificate from it, drop the assembly again and then take the necessary steps with that certificate.
I'm working on getting the required step to install tSQLt on 2017 automated.
This worked for me:
declare
#hash binary(64)
,#description nvarchar(4000)
select
#hash = HASHBYTES('SHA2_512', af.content)
, #description = a.clr_name
FROM sys.assemblies a
JOIN sys.assembly_files af
ON a.assembly_id = af.assembly_id
WHERE a.is_user_defined = 1
and a.name = 'tSQLtCLR'
EXEC sys.sp_add_trusted_assembly
#hash
,#description
You can use this script on master and then try to run tSQLt framework script query:
USE master
GO
IF OBJECT_ID('tempdb..#Private_GetAssemblyKeyBytes') IS NOT NULL DROP PROCEDURE #Private_GetAssemblyKeyBytes;
GO
CREATE PROCEDURE #Private_GetAssemblyKeyBytes
#AssemblyKeyBytes VARBINARY(MAX) = NULL OUTPUT,
#AssemblyKeyThumbPrint VARBINARY(MAX) = NULL OUTPUT
AS
SELECT #AssemblyKeyBytes =
0x4D5A90000300000004000000FFFF0000B800000000000000400000000000000000000000000000000000000000000000000000000000000000000000800000000E1FBA0E00B409CD21B8014CCD21546869732070726F6772616D2063616E6E6F742062+
0x652072756E20696E20444F53206D6F64652E0D0D0A2400000000000000504500004C010300BB91FD5F0000000000000000E00022200B013000000A0000000600000000000042280000002000000040000000000010002000000002000004000000000000+
0x0004000000000000000080000000020000919C0000030040850000100000100000000010000010000000000000100000000000000000000000F02700004F00000000400000A003000000000000000000000000000000000000006000000C000000B82600+
0x001C0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000080000000000000000000000082000004800000000000000000000002E746578740000004808000000200000000A0000000200+
0x00000000000000000000000000200000602E72737263000000A00300000040000000040000000C0000000000000000000000000000400000402E72656C6F6300000C00000000600000000200000010000000000000000000000000000040000042000000+
0x000000000000000000000000002428000000000000480000000200050058200000E005000009000000000000000000000000000000382600008000000000000000000000000000000000000000000000000000000000000000000000001E02280F00000A+
0x2A42534A4201000100000000000C00000076322E302E35303732370000000005006C000000A8010000237E0000140200002C02000023537472696E67730000000040040000040000002355530044040000100000002347554944000000540400008C0100+
0x0023426C6F620000000000000002000001471400000900000000FA013300160000010000001000000002000000010000000F0000000E0000000100000001000000000078010100000000000600ED00DE0106005A01DE0106002100AC010F00FE01000006+
0x00490094010600D00094010600B100940106004101940106000D01940106002601940106007900940106003500BF0106001300BF0106009400940106006000940106000D028D010000000001000000000001000100000010002502140241000100010050+
0x20000000008618A601060001000900A60101001100A60106001900A6010A002900A60110003100A60110003900A60110004100A60110004900A60110005100A60110005900A60110006100A60115006900A60110007100A60110007900A60110008100A6+
0x0106002E000B00C5002E001300CE002E001B00ED002E002300F6002E002B000C012E0033000C012E003B000C012E00430012012E004B001D012E0053000C012E005B000C012E00630035012E006B005F012E0073006C0104800000010000000000000001+
0x0000002300140200000200000000000000000000001A000A000000000000000000003C4D6F64756C653E006D73636F726C696200477569644174747269627574650044656275676761626C6541747472696275746500436F6D56697369626C6541747472+
0x696275746500417373656D626C795469746C6541747472696275746500417373656D626C794B65794E616D6541747472696275746500417373656D626C7954726164656D61726B41747472696275746500417373656D626C7946696C6556657273696F6E+
0x41747472696275746500417373656D626C79436F6E66696775726174696F6E41747472696275746500417373656D626C794465736372697074696F6E41747472696275746500436F6D70696C6174696F6E52656C61786174696F6E734174747269627574+
0x6500417373656D626C7950726F6475637441747472696275746500417373656D626C79436F7079726967687441747472696275746500417373656D626C79436F6D70616E794174747269627574650052756E74696D65436F6D7061746962696C69747941+
0x7474726962757465007453514C74417373656D626C794B65792E646C6C0053797374656D0053797374656D2E5265666C656374696F6E002E63746F720053797374656D2E446961676E6F73746963730053797374656D2E52756E74696D652E496E746572+
0x6F7053657276696365730053797374656D2E52756E74696D652E436F6D70696C6572536572766963657300446562756767696E674D6F646573004F626A656374007453514C74417373656D626C794B657900656D7074790000000000009C98AF3349A76E+
0x4AA6D306AB985BCF8E00042001010803200001052001011111042001010E042001010208B77A5C561934E08980A00024000004800000940000000602000000240000525341310004000001000100B9AF416AD8DFEDEC08A5652FA257F1242BF4ED60EF5A+
0x7B84A429604D62C919C5663A9C7710A7C5DF9953B69EC89FCE85D71E051140B273F4C9BF890A2BC19C48F22D7B1F1D739F90EEBC5729555F7F8B63ED088BBB083B336F7E38B92D44CFE1C842F09632B85114772FF2122BC638C78D497C4E88C2D656C166+
0x050D6E1EF3940801000800000000001E01000100540216577261704E6F6E457863657074696F6E5468726F777301080100020000000000150100107453514C74417373656D626C794B657900000501000000000A0100057453514C74000017010012436F+
0x7079726967687420C2A920203230313900002901002430333536303035622D373166642D346466332D383530322D32376336613630366539653800000C010007312E302E302E3000001D0100187453514C745F4F6666696369616C5369676E696E674B65+
0x7900000000E73936ABF16E67A4775423AE67F541BFE077E40098D90CEE90D33A631CE7C62B60228445A1C9B8A698B13317C65FD100A55706470458F0CE8C8E8CF2F30B457604C6E5F07A6862B1B45AB75E470E9F5D20E7596164413276227809F5BA14FC+
0x8A08DEEB63878D480DAE9D11907249CE1C6F58C9E69E925F071F552DEE436ADA0400000000BB91FD5F00000000020000001C010000D4260000D408000052534453D827CA8B9C7A5A4597EBB065786433D601000000443A5C615C315C735C7453514C7443+
0x4C525C7453514C74417373656D626C794B65795C6F626A5C437275697365436F6E74726F6C5C7453514C74417373656D626C794B65792E706462000000000000000000000000000000000000000000000000000000000000000000000000000000000000+
0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000+
0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001828000000000000000000003228000000200000000000000000000000000000000000000000000024280000000000000000000000005F+
0x436F72446C6C4D61696E006D73636F7265652E646C6C0000000000FF250020001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000+
0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000+
0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000+
0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000+
0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001001000000018000080000000+
0x00000000000000000000000100010000003000008000000000000000000000000000000100000000004800000058400000440300000000000000000000440334000000560053005F00560045005200530049004F004E005F0049004E0046004F00000000+
0x00BD04EFFE00000100000001000000000000000100000000003F000000000000000400000002000000000000000000000000000000440000000100560061007200460069006C00650049006E0066006F00000000002400040000005400720061006E0073+
0x006C006100740069006F006E00000000000000B004A4020000010053007400720069006E006700460069006C00650049006E0066006F0000008002000001003000300030003000300034006200300000001A000100010043006F006D006D0065006E0074+
0x00730000000000000022000100010043006F006D00700061006E0079004E0061006D00650000000000000000004A0011000100460069006C0065004400650073006300720069007000740069006F006E00000000007400530051004C0074004100730073+
0x0065006D0062006C0079004B006500790000000000300008000100460069006C006500560065007200730069006F006E000000000031002E0030002E0030002E00300000004A001500010049006E007400650072006E0061006C004E0061006D00650000+
0x007400530051004C00740041007300730065006D0062006C0079004B00650079002E0064006C006C00000000004800120001004C006500670061006C0043006F007000790072006900670068007400000043006F00700079007200690067006800740020+
0x00A90020002000320030003100390000002A00010001004C006500670061006C00540072006100640065006D00610072006B00730000000000000000005200150001004F0072006900670069006E0061006C00460069006C0065006E0061006D00650000+
0x007400530051004C00740041007300730065006D0062006C0079004B00650079002E0064006C006C00000000002C0006000100500072006F0064007500630074004E0061006D006500000000007400530051004C0074000000340008000100500072006F+
0x006400750063007400560065007200730069006F006E00000031002E0030002E0030002E003000000038000800010041007300730065006D0062006C0079002000560065007200730069006F006E00000031002E0030002E0030002E0030000000000000+
0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000+
0x000C0000004438000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000+
0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000+
0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000+
0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000+
0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000+
0x000000000000000000
,#AssemblyKeyThumbPrint = 0xE8FFF6F136D7B53E ;
GO
IF OBJECT_ID('tempdb..#Private_EnableCLR') IS NOT NULL DROP PROCEDURE #Private_EnableCLR;
GO
CREATE PROCEDURE #Private_EnableCLR
AS
BEGIN
EXEC master.sys.sp_configure #configname='clr enabled', #configvalue = 1;
RECONFIGURE;
END;
GO
IF OBJECT_ID('tempdb..#Private_GetSQLProductMajorVersion') IS NOT NULL DROP PROCEDURE #Private_GetSQLProductMajorVersion;
GO
CREATE PROCEDURE #Private_GetSQLProductMajorVersion
AS
RETURN CAST(PARSENAME(CAST(SERVERPROPERTY('ProductVersion') AS NVARCHAR(MAX)),4) AS INT);
GO
DECLARE #Version INT;
EXEC #Version = #Private_GetSQLProductMajorVersion;
PRINT #Version;
GO
IF OBJECT_ID('tempdb..#RemoveAssemblyKey') IS NOT NULL DROP PROCEDURE #RemoveAssemblyKey;
GO
CREATE PROCEDURE #RemoveAssemblyKey
AS
BEGIN
IF(NOT EXISTS(SELECT * FROM sys.fn_my_permissions(NULL,'server') AS FMP WHERE FMP.permission_name = 'CONTROL SERVER'))
BEGIN
RAISERROR('Only principals with CONTROL SERVER permission can execute this procedure.',16,10);
RETURN -1;
END;
DECLARE #master_sys_sp_executesql NVARCHAR(MAX); SET #master_sys_sp_executesql = 'master.sys.sp_executesql';
DECLARE #ProductMajorVersion INT;
EXEC #ProductMajorVersion = #Private_GetSQLProductMajorVersion;
IF SUSER_ID('tSQLtAssemblyKey') IS NOT NULL DROP LOGIN tSQLtAssemblyKey;
EXEC #master_sys_sp_executesql N'IF ASYMKEY_ID(''tSQLtAssemblyKey'') IS NOT NULL DROP ASYMMETRIC KEY tSQLtAssemblyKey;';
EXEC #master_sys_sp_executesql N'IF EXISTS(SELECT * FROM sys.assemblies WHERE name = ''tSQLtAssemblyKey'') DROP ASSEMBLY tSQLtAssemblyKey;';
DECLARE #cmd NVARCHAR(MAX);
IF(#ProductMajorVersion>=14)
BEGIN
DECLARE #TrustedHash NVARCHAR(MAX);
DECLARE #AssemblyKeyBytes VARBINARY(MAX);
EXEC #Private_GetAssemblyKeyBytes #AssemblyKeyBytes = #AssemblyKeyBytes OUT;
SELECT #TrustedHash = CONVERT(NVARCHAR(MAX),HASHBYTES('SHA2_512',#AssemblyKeyBytes),1);
SELECT #cmd =
'IF EXISTS(SELECT 1 FROM sys.trusted_assemblies WHERE hash = ' + #TrustedHash +' AND description = ''tSQLt Ephemeral'')'+
'EXEC sys.sp_drop_trusted_assembly #hash = ' + #TrustedHash + ';';
EXEC master.sys.sp_executesql #cmd;
END;
END;
GO
IF OBJECT_ID('tempdb..#InstallAssemblyKey') IS NOT NULL DROP PROCEDURE #InstallAssemblyKey;
GO
CREATE PROCEDURE #InstallAssemblyKey
AS
BEGIN
IF(NOT EXISTS(SELECT * FROM sys.fn_my_permissions(NULL,'server') AS FMP WHERE FMP.permission_name = 'CONTROL SERVER'))
BEGIN
RAISERROR('Only principals with CONTROL SERVER permission can execute this procedure.',16,10);
RETURN -1;
END;
DECLARE #cmd NVARCHAR(MAX);
DECLARE #cmd2 NVARCHAR(MAX);
DECLARE #master_sys_sp_executesql NVARCHAR(MAX); SET #master_sys_sp_executesql = 'master.sys.sp_executesql';
DECLARE #ProductMajorVersion INT;
EXEC #ProductMajorVersion = #Private_GetSQLProductMajorVersion;
DECLARE #AssemblyKeyBytes VARBINARY(MAX),
#AssemblyKeyThumbPrint VARBINARY(MAX);
EXEC #Private_GetAssemblyKeyBytes #AssemblyKeyBytes OUT, #AssemblyKeyThumbPrint OUT;
SET #cmd = 'IF EXISTS(SELECT * FROM sys.assemblies WHERE name = ''tSQLtAssemblyKey'') DROP ASSEMBLY tSQLtAssemblyKey;';
EXEC #master_sys_sp_executesql #cmd;
SET #cmd2 = 'SELECT #cmd = ''DROP ASSEMBLY ''+QUOTENAME(A.name)+'';'''+
' FROM master.sys.assemblies AS A'+
' WHERE A.clr_name LIKE ''tsqltassemblykey, %'';';
EXEC sys.sp_executesql #cmd2,N'#cmd NVARCHAR(MAX) OUTPUT',#cmd OUT;
EXEC #master_sys_sp_executesql #cmd;
DECLARE #Hash VARBINARY(64) = NULL;
IF(#ProductMajorVersion>=14)
BEGIN
SELECT #Hash = HASHBYTES('SHA2_512',#AssemblyKeyBytes);
SELECT #cmd =
'IF NOT EXISTS (SELECT * FROM sys.trusted_assemblies WHERE [hash] = #Hash)'+
'BEGIN'+
' EXEC sys.sp_add_trusted_assembly #hash = #Hash, #description = N''tSQLt Ephemeral'';'+
'END ELSE BEGIN'+
' SELECT #Hash = NULL FROM sys.trusted_assemblies WHERE [hash] = #Hash AND description <> ''tSQLt Ephemeral'';'+
'END;';
EXEC #master_sys_sp_executesql #cmd, N'#Hash VARBINARY(64) OUTPUT',#Hash OUT;
END;
SELECT #cmd =
'CREATE ASSEMBLY tSQLtAssemblyKey AUTHORIZATION dbo FROM ' +
CONVERT(NVARCHAR(MAX),#AssemblyKeyBytes,1) +
' WITH PERMISSION_SET = SAFE;'
EXEC #master_sys_sp_executesql #cmd;
IF SUSER_ID('tSQLtAssemblyKey') IS NOT NULL DROP LOGIN tSQLtAssemblyKey;
SET #cmd = N'IF ASYMKEY_ID(''tSQLtAssemblyKey'') IS NOT NULL DROP ASYMMETRIC KEY tSQLtAssemblyKey;';
EXEC #master_sys_sp_executesql #cmd;
SET #cmd2 = 'SELECT #cmd = ISNULL(''DROP LOGIN ''+QUOTENAME(SP.name)+'';'','''')+''DROP ASYMMETRIC KEY '' + QUOTENAME(AK.name) + '';'''+
' FROM master.sys.asymmetric_keys AS AK'+
' LEFT JOIN master.sys.server_principals AS SP'+
' ON AK.sid = SP.sid'+
' WHERE AK.thumbprint = #AssemblyKeyThumbPrint;';
EXEC sys.sp_executesql #cmd2,N'#cmd NVARCHAR(MAX) OUTPUT, #AssemblyKeyThumbPrint VARBINARY(MAX)',#cmd OUT, #AssemblyKeyThumbPrint;
EXEC #master_sys_sp_executesql #cmd;
SET #cmd = 'CREATE ASYMMETRIC KEY tSQLtAssemblyKey FROM ASSEMBLY tSQLtAssemblyKey;';
EXEC #master_sys_sp_executesql #cmd;
SET #cmd = 'CREATE LOGIN tSQLtAssemblyKey FROM ASYMMETRIC KEY tSQLtAssemblyKey;';
EXEC #master_sys_sp_executesql #cmd;
SET #cmd = 'DROP ASSEMBLY tSQLtAssemblyKey;';
EXEC #master_sys_sp_executesql #cmd;
IF(#Hash IS NOT NULL)
BEGIN
SELECT #cmd = 'EXEC sys.sp_drop_trusted_assembly #hash = #Hash;';
EXEC #master_sys_sp_executesql #cmd, N'#Hash VARBINARY(64)',#Hash;
END;
IF(#ProductMajorVersion>=14)
BEGIN
SET #cmd = 'GRANT UNSAFE ASSEMBLY TO tSQLtAssemblyKey;';
END
ELSE
BEGIN
SET #cmd = 'GRANT EXTERNAL ACCESS ASSEMBLY TO tSQLtAssemblyKey;';
END;
EXEC #master_sys_sp_executesql #cmd;
END;
GO
IF OBJECT_ID('tempdb..#PrepareServer') IS NOT NULL DROP PROCEDURE #PrepareServer;
GO
CREATE PROCEDURE #PrepareServer
AS
BEGIN
EXEC #Private_EnableCLR;
EXEC #InstallAssemblyKey;
END;
GO
EXEC #PrepareServer;
GO
I was tested this code, on SQL-Server 2016-2017-2019, worked well.
Related
I am trying to create a stored procedure that would be generic. I am trying to alter a database and set the recovery mode to either simple or full. It would accept database name and mode as parameter.
The SQL query executes in the context of the master database and alters the database specified. I am trying to incorporate it via Execute SQL task in SSIS. I need the stored procedure to reside in the database that is going to perform the operation on. Not sure how that is going to work. USE database keyword is not allowed in the stored procedure...
The original query works fine but I am facing an issue while trying to execute the stored procedure in the database.It says 'RECOVERY' is not a recognized SET option.
Original query:
use master
ALTER DATABASE XYZ
SET RECOVERY FULL
Stored procedure:
USE XYZ
GO
CREATE PROCEDURE DatabaseRecoveryMode
(#mode varchar(10),
#database varchar(50))
AS
BEGIN
ALTER DATABASE #database
SET RECOVERY #mode
END
The ALTER DATABASE documentation shows the recovery model is a keyword, not a variable. You'll need to construct and execute a dynamic SQL statement for this.
CREATE PROCEDURE dbo.DatabaseRecoveryMode
(
#mode nvarchar(11),
#database sysname
)
AS
IF #mode NOT IN(N'SIMPLE', N'BULK_LOGGED', N'FULL')
BEGIN
RAISERROR('Recovery model must be SIMPLE, BULK_LOGGED, OR FULL', 16, 1);
RETURN 1;
END;
DECLARE #SQL nvarchar(MAX) = N'ALTER DATABASE '
+ QUOTENAME(#database)
+ N' SET RECOVERY '+ #mode + N';';
EXECUTE(#SQL);
GO
You need to use dynamic SQL
USE XYZ
GO
Create Procedure DatabaseRecoveryMode
(
#mode varchar(10),
#database varchar(50)
)
AS
begin
DECLARE #SQL NVARCHAR(MAX)
DECLARE #db NVARCHAR(60), #Use NVARCHAR(100)
SET #db = N'master'
SET #Use = N'Use ' + #db
SET #SQL = #Use + N' ALTER DATABASE '+ #database + N' SET RECOVERY ' + #mode ;
--SELECT #SQL
EXEC sys.sp_executesql #SQL ;
end
GO
Is there a way to add a WHERE clause within the sp_addrolemember script so that I don't have to create the stored procedure in every single database?
For example in my stored procedure:
BEGIN
DECLARE #SQL NVARCHAR(MAX)
DECLARE #GrantSql INT
EXEC #GrantSql = sp_addrolemember 'db_owner', #LoginName WHERE DatabaseName = 'DBName'
IF #GrantSQL = 0
BEGIN
INSERT INTO TableName....
END
END
In documentation:
Adds a database user, database role, Windows login, or Windows group
to a database role in the CURRENT database
But you can try:
EXEC #GrantSql = DBName..sp_addrolemember 'db_owner', #LoginName
If DBName is a parameter, you should use parametrized dynamic sql
DECLARE #Sql NVARCHAR(MAX) =
N'EXEC #GrantSql = ' + QUOTENAME(#DBName) + '..sp_addrolemember #RoleName, #LoginName'
EXEC sp_executesql #Sql, N'#RoleName NVARCHAR(MAX), #LoginName NVARCHAR(255)', #RoleName, #LoginName
P.S.
Here is a good article about dynamic sql:
http://www.sommarskog.se/dynamic_sql.html
I think everyone who are about to write dynamic sql MUST read it
I am writing scripts to generate stored procedures within a database whose current schema notation will be unknown (think shared hosting).
I have decided to use dynamic SQL within the stored procedures so that the web application can pass the database schema based on a user defined setting to the SQL Server in order for it to fire properly.
When I started writing the stored procedures, I noticed that dynamic SQL opens up a whole SQL injection problem I would not normally have so I re-wrote the procedure to combat this. However even though SQL allows me to run the script to generate the stored procedure, each time I try to run the test stored procedure, I get a syntax error
Incorrect syntax near the keyword 'WHERE'
I believe this is to do with the parameter for the schema but I am at a loss as to why this is not working? I am entering the value dbo for the schema.
/*
Name : usp_GetTestTicker
Description : returns test ticker
*/
if not exists (select * from dbo.sysobjects
where id = object_id(N'usp_GetTestTicker')
and OBJECTPROPERTY(id, N'IsProcedure') = 1)
BEGIN
DECLARE #sql as nvarchar(150)
SET #sql = 'CREATE procedure usp_GetTestTicker AS'
EXEC(#sql)
END
GO
ALTER PROCEDURE usp_GetTestTicker
#schema VARCHAR(25),
#TickerItemId INT
AS
SET NOCOUNT ON
BEGIN
DECLARE #sql_cmd NVARCHAR(MAX)
DECLARE #sql_params NVARCHAR(MAX)
SET #sql_cmd = N'SELECT * FROM #schema.TickerItem WHERE TickerItemId = #TickerItemId'
SET #sql_params = N'#schema VARCHAR(25), #TickerItemId INT'
EXEC sp_executesql #sql_cmd, #sql_params, #schema, #TickerItemId
END
GO
To prevent SQL injection, you will need to validate the schema against the sys.schemas table, e.g.
ALTER PROCEDURE usp_GetTestTicker
#schema NVARCHAR(25),
#TickerItemId INT
AS
BEGIN
SET NOCOUNT ON
IF NOT EXISTS (SELECT * FROM sys.schemas WHERE name = #schema)
BEGIN
-- throw an error here. Your web code will have to handle the error and report an invalid schema
END
ELSE
BEGIN
DECLARE #sql_cmd NVARCHAR(MAX), #sql_params NVARCHAR(MAX)
SET #sql_cmd = N'SELECT * FROM ' + #schema + '.TickerItem WHERE TickerItemId = #TickerItemId'
SET #sql_params = N'#TickerItemId INT'
EXEC sp_executesql #sql_cmd, #sql_params, #TickerItemId
END
END
I am using below code to switch db context to master and create procedure and setup start up script.
BEGIN TRY
DECLARE #dbName NVARCHAR(100)
SET #dbName = DB_NAME()
USE MASTER
IF NOT EXISTS (
SELECT name
FROM sys.objects
WHERE object_id = OBJECT_ID('spSetTrustWorthyOn')
)
EXEC (
'CREATE PROCEDURE spSetTrustWorthyOn
AS
BEGIN
ALTER DATABASE [' + #dbName + '] SET TRUSTWORTHY ON
END'
)
EXECUTE sp_procoption 'spSetTrustWorthyOn'
,'startup'
,'ON'
END TRY
BEGIN CATCH
END CATCH
GO
Now Issue is when I want to switch back to existing database.I could not find any way to go back to my original database.
I also can not hard code the database as this is dynamic query and we have multiple databases.
Any help will be much appreciated.
Thanks
Instead of a USE statement for the master database, qualify the catalog views and use EXEC sp_executesql statement with the master database qualified. This will avoid changing the database context in the outer script.
DECLARE
#dbName sysname = DB_NAME()
,#sql nvarchar(MAX);
BEGIN TRY
IF NOT EXISTS (
SELECT *
FROM master.sys.objects
WHERE object_id = OBJECT_ID(N'spSetTrustWorthyOn')
)
BEGIN
SET #sql = N'CREATE PROCEDURE spSetTrustWorthyOn
AS
BEGIN
ALTER DATABASE ' + QUOTENAME(#dbName) + ' SET TRUSTWORTHY ON;
END;';
EXECUTE master..sp_executesql #sql;
EXECUTE sp_procoption
'spSetTrustWorthyOn'
,'startup'
,'ON';
END;
END TRY
BEGIN CATCH
THROW;
END CATCH;
GO
My SQL scripts are generated from templates. They contain the IP address. There can be a lot of them and the operator can by mistake execute the incorrect script. How the script can check if it was launched on the correct machine? (Otherwise, I would like to print the message and exit.)
Is it possible at all?
Thanks,
Petr
Updated Answer
Here's a script that I found from http://weblogs.sqlteam.com/peterl/archive/2008/07/16/How-to-get-IP-address.aspx, with some modifications to better suit your needs:
DECLARE #TargetIpAddress varchar(15);
SET #TargetIpAddress = '127.0.0.1'; --<== The IP address of the server you want.
DECLARE #Interfaces TABLE
(
RowID int IDENTITY(0, 1)
,Interface char(38)
,IP varchar(15)
);
INSERT #Interfaces ( Interface )
EXEC master..xp_regenumkeys N'HKEY_LOCAL_MACHINE',
N'System\CurrentControlSet\Services\TcpIP\Parameters\Interfaces';
DECLARE #RowID int
,#IP varchar(15)
,#Key nvarchar(200);
SELECT #RowID = MAX(RowID)
FROM #Interfaces;
WHILE #RowID >= 0
BEGIN
SELECT #Key = N'System\CurrentControlSet\Services\TcpIP\Parameters\Interfaces\' + Interface
FROM #Interfaces
WHERE RowID = #RowID;
EXEC master..xp_regread N'HKEY_LOCAL_MACHINE', #Key, N'DhcpIPAddress', #IP OUTPUT;
IF #IP <> '0.0.0.0'
UPDATE #Interfaces
SET IP = #IP
WHERE RowID = #RowID;
SET #RowID = #RowID - 1;
END;
IF NOT EXISTS (SELECT IP FROM #Interfaces WHERE IP = #TargetIpAddress)
BEGIN
DECLARE #ErrorMessage varchar(2000);
SET #ErrorMessage = 'This is not the correct server. This server does not have an IP address of %s.';
SET #TargetIpAddress = ISNULL(#TargetIpAddress, 'NULL');
RAISERROR(#ErrorMessage, 16, 1, #TargetIpAddress);
END
-- The rest of the script...
Original Answer
It seems like using the Server\Instance name instead of the IP address would be easier to work with, and less likely to break if the server(s) were assigned different IP addresses at some later date.
-- You can get the instance name like this:
SELECT ##SERVERNAME +'\'+ ##SERVICENAME AS 'Instance';
-- Although, you might prefer this instead:
SELECT CAST(SERVERPROPERTY('MachineName') AS nvarchar(128))
+COALESCE('\'+CAST(SERVERPROPERTY('InstanceName') AS nvarchar(128)), '');
-- NetBIOS name of the local computer on which the instance of SQL Server
-- is currently running.
-- If the instance of SQL Server is in a failover cluster and you want to obtain
-- the name of the failover clustered instance, use the MachineName property.
SELECT SERVERPROPERTY('ComputerNamePhysicalNetBIOS');
You can find detailed information about the SERVERPROPERTY function at MSDN: SERVERPROPERTY (Transact-SQL). Although, this function doesn't provide any way to obtain the IP address of the server/instance - there is no built-in function that provides this information.
Create Procedure P_GetIPAddresses (#IPS varchar(4000) out)
as
begin
Select #IPS=''
Create TABLE #temp (Line varchar(200))
Insert #temp exec master..xp_cmdshell 'ipconfig'
Select #IPS = #IPS + Coalesce(RTRIM(Replace(SubString(Line,1,CharIndex(':',line)-1) ,'.','')) + SubString(Line,CharIndex(':',line) ,200),'')
from #temp
where upper (Line) like '%ADRESS%'
--SELECT * from #tmp --DEBUG
DROP TABLE #temp
end
Usage if xp_cmdshell is disabled, parsing Adresses is up to you ...
EXEC sp_configure 'show advanced options', 1
GO
-- To update the currently configured value for advanced options.
RECONFIGURE
GO
-- To enable the feature.
EXEC sp_configure 'xp_cmdshell', 1
GO
-- To update the currently configured value for this feature.
RECONFIGURE
GO
Declare #IPS Varchar(4000)
exec P_GetIPAddresses #IPS out
Select #IPS
EXEC sp_configure 'show advanced options', 1
GO
-- To update the currently configured value for advanced options.
RECONFIGURE
GO
-- To enable the feature.
EXEC sp_configure 'xp_cmdshell', 0
GO
-- To update the currently configured value for this feature.
RECONFIGURE
GO
I could provide a xp_GetIP.dll, yet only tested with SQL-Server 2005 32-bit
which you could register via
EXEC sp_addextendedproc xp_GetIP, 'C:\temp\xpGetIP.dll'
Call would be
Declare #IP varchar(100)
exec xp_GetIP #IP output
print #IP
output only : 192.168.69.69
EDIT:
Working to on SQL-Server 2008 R2 64-bit als 64bit DLL
Sorry for reposting, but annoyed of allways missing oportunities of accessing systemadata inside SQL-Server I decided to write a Extended Stored Procedure as Bridge to WMI.
Download http://bummisoft.de/download/XP_WMI.zip
the call would be:
exec xp_wmiv3 'Select * from Win32_NetworkAdapterConfiguration where IPEnabled=TRUE'
the DLL is available for 32bit and 64bit SQLServers and free for non comercial use.
Installation:
EXEC sp_addextendedproc xp_wmiv3, '<Your Path>\XP_WMIV3_DLL.dll'
eg: EXEC sp_addextendedproc xp_wmiv3, 'C:\DLLs\XP_WMIV3_DLL.dll'
Uninstallation
EXEC sp_dropextendedproc xp_wmiv3
Usage
e.g.
exec xp_wmiv3 'SELECT * FROM Win32_Volume'
exec xp_wmiv3 'SELECT * FROM CIM_Userdevice where Name like "%HID%"'
create table #tmp(
Domain varchar(255),
Name varchar(255),Sid varchar(255)
)
insert into #tmp
exec xp_wmiV2 'SELECT Domain, SID, Name FROM Win32_UserAccount where Status = "OK"'
select * from #tmp
drop table #tmp
Currently tested under SQL-Server 2005 and SQL-Server 2008.