Я столкнулся с интересным поведением в SQL Server 2019 - похоже, этого не было в более ранних версиях.
Если в базе данных 1 я вызываю функцию в той же базе данных, которая вызывает функция в базе данных2, которая ВЫБИРАЕТ таблицу в базе данных2, я получаю: «В разрешении SELECT было отказано для объекта '{TableName}', базы данных '{DbName}', схемы 'dbo'.»
Если вместо этого , Я вызываю функцию в базе данных2 напрямую (без использования функции в базе данных1), запрос выполняется успешно.
Схема архитектуры
Мой вопрос: что за лог c стоит за этим? Я не понимаю, почему мне разрешено читать таблицу в другой базе данных без разрешения SELECT через функцию, но не , когда я вызываю эту функцию с помощью функции в моем текущая база данных! Это связано с функцией, предотвращающей передачу разрешений? В настоящий момент я предполагаю, что это запланированное изменение, но я не понимаю логики c, стоящей за ним.
Ниже приведен код, демонстрирующий поведение простым способом.
/*******************************************
SET UP
*******************************************/
CREATE DATABASE TestDb1
GO
CREATE DATABASE TestDb2
GO
CREATE LOGIN [TestLogin] WITH PASSWORD = '123456a.'
GO
--Create users in each database and add to roles.
USE TestDb1
CREATE USER [TestUser] FOR LOGIN [TestLogin]
CREATE ROLE Db1Role
ALTER ROLE Db1Role ADD MEMBER [TestUser]
USE TestDb2
CREATE USER [TestUser] FOR LOGIN [TestLogin]
CREATE ROLE Db2Role
ALTER ROLE Db2Role ADD MEMBER [TestUser]
--Create table in db1, but do no GRANTs on it.
USE TestDb1
CREATE TABLE dbo._testDb1Table (Col1 INT)
GO
--Create a function in db1, and GRANT EXECUTE.
CREATE FUNCTION dbo._TestDb1Function()
RETURNS INT
AS
BEGIN
DECLARE @Result INT = (SELECT TOP (1) Col1 FROM dbo._testDb1Table)
RETURN @Result
END
GO
GRANT EXECUTE ON dbo._TestDb1Function TO Db1Role
GO
--Create a function in db2, and GRANT EXECUTE.
USE TestDb2
GO
CREATE FUNCTION dbo._TestDb2Function()
RETURNS INT
AS
BEGIN
DECLARE @Result INT = (SELECT TestDb1.dbo._TestDb1Function())
RETURN @Result
END
GO
GRANT EXECUTE ON dbo._TestDb2Function TO Db2Role
GO
/*******************************************
TESTS
*******************************************/
USE TestDb2
--Querying TestDb1 by calling the TestDb2 function directly works.
EXECUTE AS LOGIN = 'TestLogin'
SELECT TestDb1.dbo._TestDb1Function()
REVERT
GO
--Querying TestDb2 through a scalar function in db2 doesn't work.
--The SELECT permission was denied on the object '_testDb1Table', database 'TestDb1', schema 'dbo'.
EXECUTE AS LOGIN = 'TestLogin'
SELECT dbo._TestDb2Function()
REVERT
GO
/*******************************************
TIDY UP
*******************************************/
USE [master]
DROP LOGIN [TestLogin]
DROP DATABASE TestDb1
DROP DATABASE TestDb2