Добро пожаловать в форум, Guest  >>   Войти | Регистрация | Поиск | Правила | В избранное | Подписаться
Все форумы / Microsoft SQL Server Новый топик    Ответить
 set xact_abort on не работает при raiserror?  [new]
GreenSunrise
Member

Откуда:
Сообщений: 12310
Если ошибка "нормальная", типа нарушения констрейнта, то транзакция откатывается целиком при включенном set xact_abort on.

if object_id('t2') is not null
    drop table t2
if object_id('t1') is not null
    drop table t1

create table t1 (id int primary key)
create table t2 (id int references t1(id))

set xact_abort on
go
begin tran
insert into t1 values (1)
insert into t2 values (2)
insert into t2 values (1)
commit
go
select * from t1
select * from t2
go
0 записей возвращают оба селекта.

Если ошибка пользовательская, выбрасываемая через raiserror, то транзакция не откатывается:
if object_id('t2') is not null
    drop table t2
if object_id('t1') is not null
    drop table t1

create table t1 (id int primary key)
create table t2 (id int references t1(id))

set xact_abort on
go
begin tran
insert into t1 values (1)
raiserror('some error', 16, 0)
insert into t2 values (1)
commit
go
select * from t1
select * from t2
go
В таблицах по одной записи.

Вот сообщение в первом случае:
Msg 547, Level 16, State 0, Line 3
The INSERT statement conflicted with the FOREIGN KEY constraint "FK__t2__id__4DB4832C". The conflict occurred in database "AdventureWorks", table "dbo.t1", column 'id'.

Вот сообщение во втором:
Msg 50000, Level 16, State 0, Line 3
some error

Северити одинаковый. Чем моя ошибка хуже системной? Почему транзакция во втором случае не откатилась?

Microsoft SQL Server 2005 - 9.00.4035.00 (X64) 
	Nov 24 2008 16:17:31 
	Copyright (c) 1988-2005 Microsoft Corporation
	Developer Edition (64-bit) on Windows NT 6.0 (Build 6001: Service Pack 1)
9 июн 09, 13:19    [7280547]     Ответить | Цитировать Сообщить модератору
 Re: set xact_abort on не работает при raiserror?  [new]
titanadm
Member

Откуда:
Сообщений: 16
Хуже, потому что Ваша. :)

[url=http://www.sommarskog.se/error-handling-I.html#XACT_ABORT
]Авторитет пишет[/url]:

When you issue SET XACT_ABORT ON, the very most of the statement-terminating errors instead become batch-aborting errors. Thus, if you don't want to litter your T-SQL code with checks on @@error, and if you are not interested in trying to recover from the error or invoke some error-logging routine in T-SQL, but you are content with execution being aborted on first error, then XACT_ABORT is for you.

Beware, though, that even when XACT_ABORT is ON, not all errors terminate the batch. Here are the exceptions I know of:

Errors you raise yourself with RAISERROR.
Compilation errors (which normally terminate the scope) do not terminate the batch.
Error 266, Transaction count after EXECUTE indicates that a COMMIT or ROLLBACK TRANSACTION statement is missing.
So at a minimum you still need to check @@error after the execution of a stored procedure or a block of dynamic SQL even if you use XACT_ABORT ON.
9 июн 09, 13:29    [7280602]     Ответить | Цитировать Сообщить модератору
 Re: set xact_abort on не работает при raiserror?  [new]
titanadm
Member

Откуда:
Сообщений: 16
Есть ещё try ... catch. Возможно, подойдет что-то вроде:

set xact_abort on
begin try
	begin tran
		print 'before error'
		raiserror('error',15,6)
		print 'after error'
	commit
end try
begin catch
	print 'in catch'
	print 'xact_state()='+cast(xact_state() as varchar(255))
	if xact_state() <> 0 
		rollback
	print 'xact_state()='+cast(xact_state() as varchar(255))
end catch
9 июн 09, 13:40    [7280669]     Ответить | Цитировать Сообщить модератору
 Re: set xact_abort on не работает при raiserror?  [new]
ChA
Member

Откуда: Москва
Сообщений: 10991
GreenSunrise
Северити одинаковый. Чем моя ошибка хуже системной? Почему транзакция во втором случае не откатилась?
Смутное воспоминание, что эта тема как-то уже обсуждалась. Полагаю, что RAISERROR предназначен только для отсылки сообщения клиенту, т.е., некий аналог PRINT-а. А дальше пусть клиент сам решает, что делать в случае такой ошибки.
С серверной стороны, если уж разработчик вызывает RAISERROR, то он же и должен писать код её обработки. В данном случае, это откат транзакции, хотя в общем случае это может быть что угодно. В случае же зависимости от SET XACT_ABORT может случится, что код обработки не будет вызван, так как сервер прервёт выполнение кода при состоянии в ON. Если надо прервать код после выполнения RAISERROR, то разработчик может выбрать любой из доступных способов, например, в простейших случаях, RETURN.
9 июн 09, 14:13    [7280914]     Ответить | Цитировать Сообщить модератору
 Re: set xact_abort on не работает при raiserror?  [new]
Crimean
Member

Откуда:
Сообщений: 13148
2 ChA
имхо все проще. xact_abort сделали, а обвязку путевую продумать - не стали, "невыгодно"
и это в стопервый раз наверное уже. не хотелось поднимать старую вонючку, но раз затронули, то
начну с триггеров, в которых нет "официального" средства выставить после себя @@error / @@rowcount / @@identity и т.п. и rollback в которых вообще снимает батч до 2005, а в 2005 обработать путево еще надо суметь.. и для чего в зоопарке все эти навороты - совершенно непонятно.. тупо код ради кода.. хотя наличие / отсутствие триггера на таблице по идее никто не должен замечать. instead of update - позор из позоров, 100% непродуманность, instead of insert вводится и тут же херит свежевведенную scope_identity() - даже не смешно, тихо херят по поведению свою же @@dbts и втихоря заменяют ее на "min_active_rowversion", режут функциональность tsequal - для чего?? и т.д. и т.п.
я к тому, что введя фичу с xact_abort закрыли чью-то мелкую хотелку и никто не подумал про увязку фичи с хранимками и прочим и прочим и прочим.. а так фича - да - оч удобная, сам пользую в ряде случаев. но, как обычно, "безыдейная", за что, собственно, ms и пинают все, кому не лень
9 июн 09, 14:24    [7281004]     Ответить | Цитировать Сообщить модератору
 Re: set xact_abort on не работает при raiserror?  [new]
ChA
Member

Откуда: Москва
Сообщений: 10991
Crimean
я к тому, что введя фичу с xact_abort закрыли чью-то мелкую хотелку и никто не подумал про увязку фичи с хранимками и прочим и прочим и прочим.. а так фича - да - оч удобная, сам пользую в ряде случаев. но, как обычно, "безыдейная", за что, собственно, ms и пинают все, кому не лень
Может и так, но некоторые из поднятых тобой вопросов, на мой взгляд, не так просты к реализации или логически оправданы, как может показатся на первый взгляд. Впрочем, эта тема скорее уже для флуда :) По любому, офтопик...
9 июн 09, 14:36    [7281119]     Ответить | Цитировать Сообщить модератору
 Re: set xact_abort on не работает при raiserror?  [new]
GreenSunrise
Member

Откуда:
Сообщений: 12310
Блин, как все херово... Была смутная надежда, что вышеописанная #опа - мой личный продолб, что ткнут мордой в грязь и покажут, как надо. Но увы, нет. Действительно, #опа.
9 июн 09, 15:05    [7281336]     Ответить | Цитировать Сообщить модератору
 Re: set xact_abort on не работает при raiserror?  [new]
Crimean
Member

Откуда:
Сообщений: 13148
GreenSunrise
Блин, как все херово... Была смутная надежда, что вышеописанная #опа - мой личный продолб, что ткнут мордой в грязь и покажут, как надо. Но увы, нет. Действительно, #опа.


ггггггг, вспомнить, как сделаны AppRole в 2000?


create procedure sp_setapprole
    @rolename   sysname,        -- name app role
    @password   sysname,		-- password for app role
	@encrypt	varchar(10)	= 'none'	-- Encryption style ('none' | 'odbc')
as
    -- SETUP RUNTIME OPTIONS / DECLARE VARIABLES --
	set nocount on

    -- DISALLOW USER TRANSACTION --
	set implicit_transactions off
	if (@@trancount > 0)
	begin
		raiserror(15002,-1,-1,'sp_setapprole')
		return (1)
	end

	-- CHECK PARAMETER
	if (@rolename IS NULL)
    begin
        raiserror(15431,-1,-1)
        return (1)
    end

	-- VALIDATE ENCRYPTION
	declare @encrStyle int
	select @encrStyle = case lower(@encrypt) when 'none' then 0 when 'odbc' then 1 else null end
	if @encrStyle is null
	begin
        raiserror(15600,-1,-1,'sp_setapprole')
        return (1)
	end

    -- SP MUST BE CALLED AT ADHOC LEVEL --
    if (@@nestlevel > 1)
    begin
        raiserror(15422,-1,-1)
        return (1)
    end

    -- ACTIVATE APPROLE (THIS IS ONLY VALID FROM THIS SP!) --
    setuser @rolename, @password, @encrStyle

    -- RETURN SUCCESS/FAILURE --
    if (@@error <> 0)
        return (1)

	raiserror(15494,-1,-1,@rolename)

    return (0) -- sp_setapprole

да, да, да именно как "setuser @rolename, @password, @encrStyle"!!! но - сделайте ЭТО из своей хранимки - получите "болт" :) я када ЭТО увидел, махнул рукой на попытки найти логику в T-SQL. нет ее там
9 июн 09, 16:22    [7281915]     Ответить | Цитировать Сообщить модератору
Все форумы / Microsoft SQL Server Ответить