Форум: "Базы";
Текущий архив: 2007.12.23;
Скачать: [xml.tar.bz2];
ВнизКак правильно "гасить" MSSQL-ошибку с кодом 266 Найти похожие ветки
← →
Bless © (2007-08-17 14:35) [0]Как правильно отлавливать MSSQL-ошибку с кодом 266 (SeverityLevel=16) с текстом:
Transaction count after EXECUTE indicates that a COMMIT or ROLLBACK TRANSACTION statement is
missing. Previous count = %ld, current count = %ld
Мне нужно, чтоб в программе не выскакивало исключение с этой ошибкой. Где его правильнее всего гасить?
Заключать всякий ADOСommand.execute в try/except? Как-то некрасиво.
Мне кажется, что правильнее всего ловить ее где-то в событиях ADOConnection. Если я прав, то где именно?
Я пробовал в OnExecuteComplete так:
procedure TForm1.ADOConnection1ExecuteComplete
begin
if EventStatus=esErrorsOccured then
if error.NativeError = 266 then
EventStatus := esOK;
end;
но почему-то при error.Description = "Transaction count after EXECUTE indicates that ..." (т.е. это та самая ошибка), error.NativeError почему-то равно 0, а не 266, соответственно, ошибка не гасится. Почему? Или это исключение надо ловить в другом месте?
А вот если ловить исключения в try/except, обрамляющим AdoCommand.execute, то там как и ожидалось error.NativeError=266.
← →
Ega23 © (2007-08-17 15:37) [1]Я бы не ADOСommand.execute в try/except заключал, а сделал бы 2 функции (процедуры):
procedure ADOOpen(ads:ADODataSet; ct : string ="*");
begin
ads.Close;
if ct<>"*" then ads.CommandText := ct;
try
ads.Open;
except
..........
end;
end;
иprocedure ADOExec(adc:ADOCommand; ct : string ="*");
begin
if ct<>"*" then adc.CommandText := ct;
try
adc.Execute;
except
..........
end;
end;
и использовать их уже везде по проекту.
← →
Bless © (2007-08-17 18:11) [2]Можно и так, конечно. Только ведь нужно помнить о необходимости всегда вызывать эти обертки.
Поэтому, имхо, в ADOConnection это пихнуть красивше. Вот только непонятно почему NativeError=0. Хочу понять.
Хотя совсем правильно было бы переписать хранимки так, чтоб они не откатывали лишнего, если вызываются в контексте другой транзакции, тогда эта ошибка выскакивать не будет вооще. :)
← →
sniknik © (2007-08-18 09:13) [3]> NativeError почему-то равно 0
это потому, что при выполнении тебе приходит не один результат, а от нескольких связанных рекордсетов. первый в списке судя по всему выполнился нормально вот и 0.
дальше уже ADODataSet перебирает рекордсеты в поисках значимого (NextRecordset), т.е. отсекая впереди стоящие "пустышки". он и генерирует ексепт при найденном в одном из них коде ошибки. т.е. ADOConnection там уже не при чем.
если у тебя первыми идут не возвращающие данные команды (они тоже посылают рекордсеты "пустышки", только ради кода выполнения/ошибки) то можеш от них избавится поставить SET NOCOUNT ON в начале скрипта, в процедурах оно само собой должно быть... (как у вас нет? тогда мы ... не идем к вам :), тогда возможно(зависит от скрипта) первым придет именно тот у которого ошибка и твой код в ADOConnection1ExecuteComplete сработает.
BEGIN TRANSACTION тоже посылает код завершения/рекордсет,
т.е. если в скрипте например 2 команды
BEGIN TRANSACTION
SELECT * FROM ...
то SET NOCOUNT ON уберет первый пустой рекордсет и получишь при выполненом код сразу от второго
если же тоже две но
SELECT * FROM ...
SELECT * FROM ...
то в любом случае по очереди, и от второго только после "разбора".
← →
Bless © (2007-08-20 08:50) [4]
> sniknik © (18.08.07 09:13) [3]
Мне кажется, ты невнимательно прочел.
Ничего из сказанного тобой не объясняет, почему приerror.NativeError = 0
error.Description = "Transaction count after EXECUTE indicates that ..."
т.е. ошибке с кодом 0 соответствует текст ошибки с кодом 266?
P.S. SET NOCOUNT ON стоит, про рекордсеты-пустышки я тоже в курсе (от тебя, кстати, спасибо :).
← →
sniknik © (2007-08-20 09:09) [5]> при error.NativeError = 0 error.Description = "Transaction count after EXECUTE indicates that ..."
хм. действительно невнимательно, иначе бы это проверил... это стоит проверки. но теперь только после работы.
← →
Bless © (2007-08-20 14:16) [6]На всякий случай приведу простейший пример, иллюстрирующий проблему (вдруг пригодится).
Итак, есть хранимкаCREATE PROCEDURE [dbo].[vv]
AS
SET NOCOUNT ON
BEGIN TRAN
ROLLBACK TRAN
Есть проект с одной формой. На форме: один conn:TADOConnection с вбитой строкой подключения, один comm: TADOCommand, привязанный к conn и кнопка.
Обработчик OnClick для кнопки:procedure TForm1.Button1Click(Sender: TObject);
begin
cmd.CommandText := "begin tran";
cmd.Execute;
cmd.CommandText := "exec vv";
cmd.Execute;
cmd.CommandText := "rollback tran";
cmd.Execute;
end;
Обработчик OnExecuteComplete для conn:procedure TForm1.connExecuteComplete(Connection: TADOConnection;
RecordsAffected: Integer; const Error: Error;
var EventStatus: TEventStatus; const Command: _Command;
const Recordset: _Recordset);
begin
if error<>nil then
showmessage(IntToStr(error.NativeError) + " # " +
error.Description);
end;
У меня нажатие на кнопку приводит к MessageBox-у с текстом0 # Transaction count after EXECUTE indicates that a COMMIT or ROLLBACK TRANSACTION statement is missing. Previous count = 1, current count = 0.
← →
Bless © (2007-08-20 14:18) [7]
> один comm: TADOCommand
ошибся. Правильно "один cmd: TADOCommand"
← →
sniknik © (2007-08-20 16:31) [8]действительно 0... и ничего не поделаешь, событие с этим параметром идет от интерфейса, а сразу после события оно приобретает значение (и то и то из обьекта)...
похоже просто пару строк кода местами спутали, сначала событие инициируют, а после определяют одно последнее в объекте значение.
что можно сказать. жалуйся в мелкософт,... или пользуйся Error.Number.
← →
DiamondShark © (2007-08-21 13:37) [9]
> Bless © (20.08.07 14:16) [6]
А описание TSQL кто будет читать? Иван Фёдорович Крузенштерн?
ROLLBACK TRANSACTION without a savepoint_name or transaction_name rolls back to the beginning of the transaction. When nesting transactions, this same statement rolls back all inner transactions to the outermost BEGIN TRANSACTION statement. In both cases, ROLLBACK TRANSACTION decrements the @@TRANCOUNT system function to 0. ROLLBACK TRANSACTION savepoint_name does not decrement @@TRANCOUNT.
← →
Bless © (2007-08-21 15:45) [10]
> sniknik © (20.08.07 16:31) [8]
> что можно сказать. жалуйся в мелкософт,... или пользуйся
> Error.Number.
>
Жаль. А я надеялся, что ты скажешь, что у меня просто MDAC старый :)
> DiamondShark © (21.08.07 13:37) [9]
Это к чему?
← →
sniknik © (2007-08-21 16:01) [11]> Жаль. А я надеялся, что ты скажешь, что у меня просто MDAC старый :)
прописать Number вместо NativeError гораздо проще чем переставить MDAC, циферки там только другие, непонятнее будут, но на это есть комментарии...
← →
kaif © (2007-08-21 16:21) [12]Вообще-то просто так гасить ошибки "чтобы не выскакивали" не есть хорошо, ИМХО.
А по сабжу я обычно перехватываю ошибки в Application.OnException, анализирую там класс ошибки с помощью оператора is, привожу явно с помощью оператора as к этому классу, в котором обычно есть свойство ErrorCode типа Integer, если это ошибка сервера баз данных, анализирую этот код в операторе типа case и дальше уже предпринимаю какие-то действия - обычно сообщаю юзеру об ошибке в понятной для него форме или делаю дополнительные запросы для выяснения причин и сообщения пользователю в еще более понятной для него форме. То есть у меня в Application.OnException прописана куча ифов и касов, вызывающих процедуры для существенных или информативных ошибок и некий ELSE, выдающий все остальное в виде просто текста E.Message.
Такая централизация удобна тем, что там легко встроить логфайл и записывать в него протокол каких-тоо ошибок при работе с сервером.
← →
DiamondShark © (2007-08-21 16:46) [13]
> Bless © (21.08.07 15:45) [10]
> Это к чему?
Если начистоту, то это к тому, что писать заведомо бредовый SQL, а потом искать способ подавить ошибку -- это редкая форма мазохизма.
← →
Bless © (2007-08-22 12:23) [14]> Если начистоту, то это к тому, что писать заведомо бредовый SQL, а потом искать способ
> подавить ошибку -- это редкая форма мазохизма.
Эта ошибка сервера с точки зрения моей логики ошибкой не является, поэтому она не должна ни выскакивать, ни обрабатываться, просто игнорироваться.
Внешняя транзакция и должна быть откачена в случае облома, я знаю о таком поведении, оно учтено в вызывающих хранимках и пока нигде не стало ни проблемой, ни сюрпризом.
Может, и не правильно идеологически, что хранимка откачивает чужие транзакции, но я не вижу причин менять то, что нормально работает.
Тем более, что альтернатива, как я ее себе представляю, тоже не лишена недостатков.
Заведомо НЕбредовый SQL - это пихать везде, где есть BEGIN TRAN, SAVEPOINT-ы, я правильно понял?
Если так, то, насколько я могу судить, это потребует применения жестких правил именования SAVEPOINT-ов, исключающих дублирование имен, исполнение которых возлагается на людей, пишущих хранимки. В противном случае раскиданные грабли будут могут больно стукнуть по лбу. А людям как раз свойственна забывчивость.
Или я не прав?
Страницы: 1 вся ветка
Форум: "Базы";
Текущий архив: 2007.12.23;
Скачать: [xml.tar.bz2];
Память: 0.5 MB
Время: 0.062 c