我正在尝试在MS SQL Server 2014 DB中实现共享API。在该体系结构中,模式应该具有类似的结构并使用dbo拥有的共享API,同时公开自己的API。为了在不限定对象名称的情况下相互调用,EXECUTE AS USER
语句用于上下文切换到当前用户的某个默认模式。
问题在于:虽然使用用户上下文切换的即时访问工作正常(例如EXECUTE AS USER
后跟SELECT * from test_tbl;
),但存储过程中通过默认模式的访问失败,错误为Msg 208, Level 16, State 1
。
在发布我的问题之前,我尝试了很多实验和测试,并在几天内搜索了MSDN,Web和SQL论坛的任何线索,但没有运气。
用于复制的脚本(<MDF>
和<LDF>
需要使用适当的文件路径替换):
-- DB creation
CREATE DATABASE [test_sql]
CONTAINMENT = NONE
ON PRIMARY
( NAME = N'test_sql', FILENAME = N'<MDF>' , SIZE = 5120KB , FILEGROWTH = 1024KB )
LOG ON
( NAME = N'test_sql_log', FILENAME = N'<LDF>' , SIZE = 2048KB , FILEGROWTH = 10%)
COLLATE Cyrillic_General_CI_AS
GO
ALTER DATABASE [test_sql] SET COMPATIBILITY_LEVEL = 120
GO
ALTER DATABASE [test_sql] SET ANSI_NULL_DEFAULT OFF
GO
ALTER DATABASE [test_sql] SET ANSI_NULLS OFF
GO
ALTER DATABASE [test_sql] SET ANSI_PADDING OFF
GO
ALTER DATABASE [test_sql] SET ANSI_WARNINGS OFF
GO
ALTER DATABASE [test_sql] SET ARITHABORT OFF
GO
ALTER DATABASE [test_sql] SET AUTO_CLOSE OFF
GO
ALTER DATABASE [test_sql] SET AUTO_SHRINK OFF
GO
ALTER DATABASE [test_sql] SET AUTO_CREATE_STATISTICS ON
GO
ALTER DATABASE [test_sql] SET AUTO_UPDATE_STATISTICS ON
GO
ALTER DATABASE [test_sql] SET CURSOR_CLOSE_ON_COMMIT OFF
GO
ALTER DATABASE [test_sql] SET CURSOR_DEFAULT GLOBAL
GO
ALTER DATABASE [test_sql] SET CONCAT_NULL_YIELDS_NULL OFF
GO
ALTER DATABASE [test_sql] SET NUMERIC_ROUNDABORT OFF
GO
ALTER DATABASE [test_sql] SET QUOTED_IDENTIFIER OFF
GO
ALTER DATABASE [test_sql] SET RECURSIVE_TRIGGERS OFF
GO
ALTER DATABASE [test_sql] SET DISABLE_BROKER
GO
ALTER DATABASE [test_sql] SET AUTO_UPDATE_STATISTICS_ASYNC OFF
GO
ALTER DATABASE [test_sql] SET DATE_CORRELATION_OPTIMIZATION OFF
GO
ALTER DATABASE [test_sql] SET PARAMETERIZATION SIMPLE
GO
ALTER DATABASE [test_sql] SET READ_COMMITTED_SNAPSHOT OFF
GO
ALTER DATABASE [test_sql] SET READ_WRITE
GO
ALTER DATABASE [test_sql] SET RECOVERY FULL
GO
ALTER DATABASE [test_sql] SET MULTI_USER
GO
ALTER DATABASE [test_sql] SET PAGE_VERIFY CHECKSUM
GO
ALTER DATABASE [test_sql] SET TARGET_RECOVERY_TIME = 0 SECONDS
GO
ALTER DATABASE [test_sql] SET DELAYED_DURABILITY = DISABLED
GO
USE [test_sql]
GO
IF NOT EXISTS (SELECT name FROM sys.filegroups WHERE is_default=1 AND name = N'PRIMARY') ALTER DATABASE [test_sql] MODIFY FILEGROUP [PRIMARY] DEFAULT
GO
-- Srv login, DB user and schema creation
CREATE LOGIN [test_usr_login] WITH PASSWORD=N'test_usr_login', DEFAULT_DATABASE=[test_sql], DEFAULT_LANGUAGE=[us_english], CHECK_EXPIRATION=OFF, CHECK_POLICY=OFF
GO
CREATE USER [test_usr] FOR LOGIN [test_usr_login] WITH DEFAULT_SCHEMA=[test_schema]
GO
CREATE SCHEMA [test_schema] AUTHORIZATION [test_usr]
GO
-- Table and stored proc creation
IF OBJECT_id("[test_schema].[test_tbl]", "U") IS NOT NULL
DROP TABLE [test_schema].[test_tbl];
GO
CREATE TABLE [test_schema].[test_tbl](
[tc] [nchar](10) NULL
) ON [PRIMARY]
GO
IF OBJECT_id("[dbo].[TA]", "P") IS NOT NULL
DROP PROCEDURE [dbo].[TA];
GO
CREATE PROCEDURE [dbo].[TA] AS BEGIN
SET NOCOUNT ON;
SELECT * FROM
(VALUES
('CURRENT_USER', CURRENT_USER),
('SCHEMA_NAME', SCHEMA_NAME()),
('have_UNqualified_select', cast(HAS_PERMS_BY_NAME("[test_tbl]", "OBJECT", "SELECT") as nchar(10))),
('have_qualified_select', cast(HAS_PERMS_BY_NAME("[test_schema].[test_tbl]", "OBJECT", "SELECT") as nchar(10)))
) AS tmptbl([key], val); -- select permissions fro [test_tbl] of the current user
SELECT tc as qualified_tc FROM [test_schema].[test_tbl]; -- qualified select
SELECT tc as UNqualified_tc from [test_tbl]; -- unqualified select fails with Msg 208
END
GO
GRANT EXECUTE ON [dbo].[TA] TO [test_usr]
GO
测试脚本:
USE [test_sql]
GO
DECLARE @return_value int
execute as login = N'test_usr_login'; -- even when logged in with test_usr_logn, Msg 208 occurs
EXEC @return_value = [dbo].[TA]
revert
SELECT 'Return Value' = @return_value
GO
输出消息:
消息208,级别16,状态1,过程TA,行14无效的对象名称'test_tbl'。
(1排受影响)
输出结果:
key val
CURRENT_USER test_usr
SCHEMA_NAME test_schema
have_UNqualified_select 1
have_qualified_select 1
我很感激任何能够为所述问题的解决方案带来光明的人。
问题出在这里:虽然使用用户上下文切换的立即访问工作正常(例如EXECUTE AS USER后跟来自test_tbl的SELECT *;),存储过程中通过默认模式的访问失败,错误消息为208,级别16,状态1。
这里的问题是您不知道SQL Server如何解析非限定对象名称。
当您执行普通sql并使用对象而不指定其架构时,将检查第一个用户default schema
,如果找不到对象,则检查dbo
架构。如果甚至在dbo中找不到该对象,则会引发错误。
它与存储过程有所不同。如果未指定schema,则首先检查sp的模式,如果未找到object,则检查dbo
模式,如果再次找不到,则引发错误。在存储过程中从不检查User default schema