我们通过添加“_V1”、“_V2”等后缀来根据任何 ALTER 请求创建一个新的存储过程。 这样我们就可以保持应用程序和数据库之间的向后兼容性。
该标准使我们保留了许多旧版本的代码。 我们开始使用 ALTER SCHEMA [CanDel] TRANSFER 来仅保留存储过程的最后 4 个版本。 其他进程(作业)充当垃圾收集器 - 如果对象在“CanDel”模式中保留 3 个月,它将被删除。
但是,有时我们需要进行回滚并将其中一个存储过程传输回原始模式。
我们注意到旧的权限消失了。 我很想知道这个功能是设计使然还是被改变了。
我们使用 SQL Server 2022。
我添加了一个示例代码来重现。
SET ANSI_NULLS ON;
GO
SET QUOTED_IDENTIFIER ON;
GO
SET NOCOUNT ON;
GO
USE tempdb;
GO
/* Cleanup */
IF OBJECT_ID('dbo.USP_PermissionsTest') IS NOT NULL
BEGIN
DROP PROCEDURE dbo.USP_PermissionsTest;
END;
IF OBJECT_ID('CanDel.USP_PermissionsTest') IS NOT NULL
BEGIN
DROP PROCEDURE CanDel.USP_PermissionsTest;
END;
GO
/* Create test role */
IF USER_ID('UDR_Test') IS NULL
CREATE ROLE [UDR_Test];
GO
/* Create dummy stored procedure */
IF OBJECT_ID('dbo.USP_PermissionsTest') IS NULL
BEGIN
EXEC('CREATE PROCEDURE dbo.USP_PermissionsTest AS');
END;
GO
/* grant permission to the stored procedure */
GRANT EXECUTE ON dbo.USP_PermissionsTest TO [UDR_Test];
GO
/* check permissions */
SELECT USER_NAME(p.grantee_principal_id) AS principal_name,
dp.principal_id,
dp.type_desc AS principal_type_desc,
p.class_desc,
CASE p.class_desc
WHEN 'DATABASE' THEN DB_NAME()
WHEN 'SCHEMA' THEN SCHEMA_NAME(p.major_id)
WHEN 'OBJECT_OR_COLUMN' THEN OBJECT_NAME(p.major_id)
ELSE NULL
END AS object_name,
p.permission_name,
p.state_desc AS permission_state_desc
FROM sys.database_permissions p
INNER JOIN sys.database_principals dp ON p.grantee_principal_id = dp.principal_id
LEFT OUTER JOIN sys.all_objects o ON p.major_id = o.object_id
WHERE p.grantee_principal_id = USER_ID('UDR_Test')
AND o.object_id = OBJECT_ID('dbo.USP_PermissionsTest')
ORDER BY o.name;
GO
/* Create different schema to a destination */
IF SCHEMA_ID('CanDel') IS NULL
BEGIN
EXEC('CREATE SCHEMA [CanDel];');
END
GO
/* Transfer stored procedure to other schema */
ALTER SCHEMA [CanDel] TRANSFER dbo.USP_PermissionsTest;
GO
/* check permissions */
SELECT USER_NAME(p.grantee_principal_id) AS principal_name,
dp.principal_id,
dp.type_desc AS principal_type_desc,
p.class_desc,
CASE p.class_desc
WHEN 'DATABASE' THEN DB_NAME()
WHEN 'SCHEMA' THEN SCHEMA_NAME(p.major_id)
WHEN 'OBJECT_OR_COLUMN' THEN OBJECT_NAME(p.major_id)
ELSE NULL
END AS object_name,
p.permission_name,
p.state_desc AS permission_state_desc
FROM sys.database_permissions p
INNER JOIN sys.database_principals dp ON p.grantee_principal_id = dp.principal_id
LEFT OUTER JOIN sys.all_objects o ON p.major_id = o.object_id
WHERE p.grantee_principal_id = USER_ID('UDR_Test')
AND o.object_id = OBJECT_ID('CanDel.USP_PermissionsTest')
ORDER BY o.name;
GO
/* Cleanup */
IF OBJECT_ID('dbo.USP_PermissionsTest') IS NOT NULL
BEGIN
DROP PROCEDURE dbo.USP_PermissionsTest;
END;
IF OBJECT_ID('CanDel.USP_PermissionsTest') IS NOT NULL
BEGIN
DROP PROCEDURE CanDel.USP_PermissionsTest;
END;
GO
IF SCHEMA_ID('CanDel') IS NOT NULL
BEGIN
EXEC('DROP SCHEMA [CanDel];');
END
GO
我编写了一个带有权限的传输包装器
CREATE OR ALTER PROCEDURE dbo.USP_TransferToSchemaWithPermissions
@I_SourceSchema sysname = 'dbo',
@I_SourceObject sysname = 'USP_PermissionsTest',
@I_TargetSchema sysname = 'CanDel'
WITH EXECUTE AS OWNER
AS
BEGIN
SET NOCOUNT ON;
DECLARE @cmd NVARCHAR(MAX);
DECLARE @crlf VARCHAR(2) = CHAR(13) + CHAR(10);
SELECT @I_TargetSchema = REPLACE(REPLACE(@I_TargetSchema,']',''),'[','');
SELECT @cmd = CONCAT(N'USE [',ORIGINAL_DB_NAME(),'];',@crlf,
N'IF OBJECT_ID(@I_TargetSchema + ''.'' + @I_SourceObject) IS NULL',@crlf,
N'BEGIN',@crlf,
N' DECLARE @icmd NVARCHAR(MAX) = N'''';',@crlf,
N' IF SCHEMA_ID(''',@I_TargetSchema,''') IS NULL EXEC(''CREATE SCHEMA [',@I_TargetSchema,'];'');',@crlf,
N' SELECT @icmd += CONCAT(IIF(dp.class IN (1,3,6,24,25),''IF '' + CASE dp.class',@crlf,
N' WHEN 0 THEN ''''',@crlf,
N' WHEN 1 THEN',@crlf, --table or column subset on the table
N' CASE WHEN dp.major_id < 0 THEN ''OBJECT_ID(''''sys.'' + OBJECT_NAME(dp.major_id) + '''''')''',@crlf,
N' ELSE ''OBJECT_ID('''''' + (SELECT @I_TargetSchema + ''.'' + name FROM sys.objects WHERE object_id = dp.major_id)+ '''''')'' END',@crlf,
N' WHEN 3 THEN ''SCHEMA_ID('''''' + @I_TargetSchema + '''''')''',@crlf,
N' WHEN 6 THEN ''TYPE_ID('''''' + (SELECT name FROM sys.types WHERE user_type_id = dp.major_id) + '''''')''',@crlf,
N' WHEN 24 THEN ''KEY_ID('''''' + (SELECT name FROM sys.symmetric_keys WHERE symmetric_key_id = dp.major_id) + '''''')''',@crlf,
N' WHEN 25 THEN ''CERT_ID('''''' + (SELECT name FROM sys.certificates WHERE certificate_id = dp.major_id) + '''''')''',@crlf,
N' END COLLATE DATABASE_DEFAULT + '' IS NOT NULL '',''''),',@crlf,
N' CASE dp.state',@crlf,
N' WHEN ''D'' THEN ''DENY ''',@crlf,
N' WHEN ''G'' THEN ''GRANT ''',@crlf,
N' WHEN ''R'' THEN ''REVOKE ''',@crlf,
N' WHEN ''W'' THEN ''GRANT ''',@crlf,
N' END,dp.permission_name,'' '',',@crlf,
N' CASE dp.class',@crlf,
N' WHEN 0 THEN ''''',@crlf,
N' WHEN 1 THEN',@crlf, --table or column subset on the table
N' CASE WHEN dp.major_id < 0 THEN',@crlf,
N' + ''ON [sys].['' + OBJECT_NAME(dp.major_id) + ''] ''',@crlf,
N' ELSE',@crlf,
N' + ''ON ['' +',@crlf,
N' (SELECT @I_TargetSchema + ''].['' + name FROM sys.objects WHERE object_id = dp.major_id)',@crlf,
N' +',@crlf, -- optionally concatenate column names
N' CASE WHEN MAX(dp.minor_id) > 0 ',@crlf,
N' THEN ''] (['' + REPLACE(',@crlf,
N' (SELECT name + ''], ['' ',@crlf,
N' FROM sys.columns ',@crlf,
N' WHERE object_id = dp.major_id ',@crlf,
N' AND column_id IN (SELECT minor_id ',@crlf,
N' FROM sys.database_permissions ',@crlf,
N' WHERE major_id = dp.major_id',@crlf,
N' AND USER_NAME(grantee_principal_id) IN (USER_NAME(dp.grantee_principal_id))',@crlf,
N' )',@crlf,
N' FOR XML PATH('''')',@crlf,
N' )',@crlf, --replace final square bracket pair
N' + ''])'', '', []'', '''')',@crlf,
N' ELSE '']''',@crlf,
N' END + '' ''',@crlf,
N' END',@crlf);
SELECT @cmd += CONCAT(
N' END COLLATE DATABASE_DEFAULT,''TO ['',USER_NAME(dp.grantee_principal_id),'']'',',@crlf,
N' CASE dp.state WHEN ''W'' THEN '' WITH GRANT OPTION'' ELSE '''' END,'';'',@crlf)',@crlf,
N' FROM sys.database_permissions dp',@crlf,
N' WHERE dp.major_id = OBJECT_ID(@I_SourceSchema + ''.'' + @I_SourceObject)',@crlf,
N' GROUP BY dp.state, dp.major_id, dp.permission_name, dp.class,dp.grantee_principal_id;',@crlf,
N' ',@crlf,
N' ALTER SCHEMA [',@I_TargetSchema,'] TRANSFER [',@I_SourceSchema,'].[',@I_SourceObject,'];',@crlf,
N' EXEC sys.sp_executesql @icmd, N''@crlf VARCHAR(2),@I_SourceSchema sysname,@I_SourceObject sysname,@I_TargetSchema sysname'',',@crlf,
N' @crlf = @crlf,@I_SourceSchema = @I_SourceSchema, @I_SourceObject= @I_SourceObject, @I_TargetSchema = @I_TargetSchema;',@crlf,
N'END',@crlf);
EXEC sys.sp_executesql @cmd, N'@crlf VARCHAR(2),@I_SourceSchema sysname,@I_SourceObject sysname,@I_TargetSchema sysname',
@crlf = @crlf,@I_SourceSchema = @I_SourceSchema, @I_SourceObject= @I_SourceObject, @I_TargetSchema = @I_TargetSchema;
END
GO