2017-12-18 14 views
0

MS SQL Server 2014 DB에서 공유 API를 구현하려고합니다. 이 아키텍처에서 스키마는 비슷한 구조를 가져야하고 dbo가 소유 한 공유 API를 사용해야하며 동시에 자신의 API를 노출해야합니다. 개체 이름을 지정하지 않고 서로를 호출하려면 EXECUTE AS USER 문이 현재 사용자의 특정 기본 스키마로 컨텍스트를 전환하는 데 사용됩니다. 저장된 프로 시저에서 사용자의 기본 스키마를 통해 액세스 할 때 SQL Server에서 사용자 전환시

문제

은 여기에서 : 사용 문맥 전환 즉각적인 액세스가 미세한 (예컨대 SELECT * from test_tbl; 하였다 EXECUTE AS USER) 동작, 저장 프로 시저의 기본 스키마를 통해 액세스 오류 Msg 208, Level 16, State 1 실패있다.

내 질문을 게시하기 전에 많은 실험과 테스트를 시도하고 MSDN, 웹 및 SQL 포럼에서 며칠 동안 아무런 단서를 찾지 않았습니다.

-- 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 

테스트 스크립트 : 재생

스크립트 (<MDF><LDF>는 appropritate 파일 경로로 대체 필요)

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   

.

+0

스키마 "test_schema"에서 "test_tbl"테이블을 만들었지 만 프로 시저에서 스키마를 지정하지 않았습니다. 어쨌든 당신이 스키마를 지정해야한다면 그것은 디폴트 스키마로 실행된다는 것을 압니다. –

+0

@SeanLange 공유 API 개념은 규정되지 않은 'test_tbl'을 통해 구현됩니다.이 테이블은 [MSDN] (https://msdn.microsoft.com/en-us/library/ms190387.aspx)에 따라 기본 스키마를 통해 액세스해야합니다.).그렇지 않으면 스키마 이름 종속성으로 인해 API가 공유로 제공 될 수 없습니다. 내 질문에 빛을 가져 오는 [SOF] (/ questions/27357352/sql-default-schema-resolution-in-stored-procedure)와 비슷한 또 다른 질문을 발견했습니다. –

답변

1

문제는 여기에서 : 사용 문맥에 즉시 액세스 스위칭 잘 작동하는 동안 (예 test_tbl로부터 SELECT * 다음 사용자로 EXECUTE) 에러 메시지 실패 , 저장 프로 시저의 기본 스키마를 통해 접근 208, 수준 16, 상태 1

여기서 문제는 SQL Server에서 정규화되지 않은 개체 이름을 확인하는 방법을 알 수 없다는 것입니다. 객체가 발견되지 않는 경우

당신은 일반 SQL을 실행하고 스키마를 지정하지 않고 객체를 사용

은, 첫 번째 사용자 default schema가 선택되어, dbo 스키마가 확인됩니다. 객체가 dbo에서도 발견되지 않으면 오류가 발생합니다.

저장 프로 시저의 경우와 다릅니다. schema가 지정되지 않으면 sp의 스키마가 먼저 검사되고 객체가 없으면 dbo 스키마가 검사되고 다시 찾지 못하면 오류가 발생합니다. User default schema은 저장 프로 시저의 경우 절대로 선택되지 않습니다

+0

따라서 여러 스키마에서 SP를 공유 할 수있는 방법이 있습니까? –

+0

모든 스키마에이 sp 복사본을 만드는 경우에만 – sepupic

+0

테이블을 변경할 수있는 경우 다른 방법을 사용할 수 있습니다. 정규화되지 않은 test_tbl 테이블 대신에 분할 뷰를 생성 할 수 있습니다. 이를 수행하려면 check_name 제약이있는 모든 테이블에 schema_name 열을 추가하십시오. 모든 "유사한"테이블을 모두 합집합으로 뷰를 생성하십시오. 그런 다음 뷰를 사용하기 전에 계산할 수있는 적절한 스키마로 test_table을 필터링하는 대신이 뷰를 사용하십시오. 쿼리에서 옵션 (재 컴파일)을 사용하면 스키마 이름을 포함하는 변수가 스니핑되고 올바른 테이블을 제외한 모든 테이블이 실행 계획에서 제외됩니다 – sepupic