Perl ADO считает вывод на печать в хранимой процедуре ошибкой!
Прежде всего (в случае, если это важно), я использую Perl ActiveState (v5.8.7, построенный для многопоточности MSWin32-x86).
Я только что вышел из трехчасового сеанса отладки, пытаясь найти источник ошибки. Я обнаружил, что ошибки просто не было, но по какой-то причине объект подключения ADO получал Errors.Count
увеличивается с каждым напечатанным сообщением в выводе моей хранимой процедуры.
Рассмотрим следующий код Transact SQL:
CREATE PROCEDURE dbo.My_Sample() AS
BEGIN TRAN my_tran
-- Does something useful
if @@error <> 0 BEGIN
ROLLBACK TRAN my_tran
RAISERROR( 'SP My_Sample failed', 16, 1)
END ELSE BEGIN
COMMIT TRAN my_tran
PRINT 'SP My_Sample succeeded'
END
Теперь представьте, что Perl более или менее похож на:
sub execute_SQL {
# $conn is an already opened ADO connection object
# pointing to my SQL Server
# $sql is the T-SQL statement to be executed
my($conn, $sql) = @_;
$conn->Execute($sql);
my $error_collection = $conn->Errors();
my $ecount = $error_collection->Count;
if ($ecount == 0 ) { return 0; }
print "\n" . $ecount . " errors found\n";
print "Executed SQL Code:\n$sql\n\n";
print "Errors while executing:\n";
foreach my $error (in $error_collection){
print "Error: [" . $error->{Number} . "] " . $error->{Description} . "\n";
}
return 1;
}
Где-то еще, в основном коде Perl, я вызываю приведенную выше сабвуфер как:
execute_SQL( $conn, 'EXEC dbo.My_Sample' );
В конце концов я понял, что каждый оператор PRINT приводит к добавлению новой псевдо-ошибки в коллекцию ошибок ADO. Быстрое решение, которое я реализовал, состояло в том, чтобы превратить PRINT в SP в SELECT, чтобы обойти это.
Вопросы, которые я хотел бы задать:
- Это нормальное поведение?
- Есть ли способ избежать / обойти это?
2 ответа
Этого и следовало ожидать, так как это то, что делает ADO, а Win32::ADO довольно тонкий слой над ним.
ref: база знаний, обратите внимание, что операторы RAISERROR и PRINT возвращаются через коллекцию ошибок ADO
Хорошо, после долгих тестов и чтений я обнаружил, что это объясняется в статье BOLs "Использование PRINT" (мой акцент):
Оператор PRINT используется для возврата сообщений в приложения. PRINT принимает символьное или строковое выражение Unicode в качестве параметра и возвращает строку в виде сообщения в приложение. Сообщение возвращается как информационная ошибка приложениям, использующим пространство имен SQLClient или интерфейсы прикладного программирования (API) приложений ActiveX Data Objects (ADO), OLE DB и Open Database Connectivity (ODBC). SQLSTATE имеет значение 01000, собственная ошибка - 0, а строка сообщения об ошибке - строка символов, указанная в операторе PRINT. Строка возвращается в функцию обратного вызова обработчика сообщений в приложениях DB-Library.
Вооружившись этими знаниями, я адаптировал этот VB6 из этой статьи DevX, пока не получил это:
sub execute_SQL {
# $conn is an already opened ADO connection object
# pointing to my SQL Server
# $sql is the T-SQL statement to be executed
# Returns 0 if no error found, 1 otherwise
my($conn, $sql) = @_;
$conn->Execute($sql);
my $error_collection = $conn->Errors();
my $ecount = $error_collection->Count;
if ($ecount == 0 ) { return 0; }
my ($is_message, $real_error_found);
foreach my $error (in $error_collection){
$is_message = ($error->{SQLState} eq "01000" && $error->{NativeError}==0);
$real_error_found=1 unless $is_message;
if( $is_message) {
print "Message # " . $error->{Number}
. "\n Text: " . $error->{Description} ."\n";
} else {
print "Error # " . $error->{Number}
. "\n Description: " . $error->{Description}
. "\nSource: " . $error->{Source} . "\n";
}
}
print $message_to_print;
return $real_error_found;
}
Так что теперь мой сабвуфер Perl правильно сортирует реальные ошибки (исходящие от SQL Server через RaisError) и общее сообщение, выводимое через "PRINT".
Спасибо Ричарду Харрисону за его ответ, который привел меня на путь успеха.